Aller au contenu

Exemples de Scripts Prêts à l'Emploi

Voici 5 scripts Lua simples et commentés que vous pouvez copier, tester et adapter dans vos propres mods.


1. Soin & Armure sur une Touche (F4)

Restaure la santé et le gilet pare-balles du joueur au maximum lorsque la touche F4 est pressée.

-- Fichier : main.lua
local TOUCHE_SOIN = 0x73 -- Touche F4

Thread.Create(function()
    while true do
        if Input.IsKeyDown(TOUCHE_SOIN) then
            local playerPed = GetPlayerChar(GetPlayerId())

            SetCharHealth(playerPed, 200)   -- Vie max
            AddArmourToChar(playerPed, 100) -- Armure max

            Log.Print("Santé et armure restaurées !")
        end
        Thread.Wait(0)
    end
end)

2. Jamais Recherché par la Police (Never Wanted)

Bloque et réinitialise automatiquement le niveau d'étoiles de police à zéro.

-- Fichier : main.lua
Thread.Create(function()
    while true do
        local playerId = GetPlayerId()
        local playerPed = GetPlayerChar(playerId)

        -- Réinitialise les étoiles en continu
        ClearCharLastDamageEntity(playerPed)
        AlterWantedLevel(playerId, 0)
        ApplyWantedLevelChangeNow(playerId)
        Thread.Wait(0)
    end
end)

3. Téléportation de 10 Mètres vers le Haut (Touche Z)

Appuyez sur Z (0x5A) pour vous téléporter 10 mètres en l'air (très pratique pour se débloquer du décor).

-- Fichier : main.lua
local TOUCHE_TP = 0x5A -- Touche Z

Thread.Create(function()
    while true do
        if Input.IsKeyDown(TOUCHE_TP) then
            local playerPed = GetPlayerChar(GetPlayerId())

            -- Récupère la position : les paramètres de sortie reviennent en valeurs Lua
            local x, y, z = GetCharCoordinates(playerPed)

            -- Téléporte 10 mètres plus haut
            SetCharCoordinates(playerPed, x, y, z + 10.0)
            Log.Print("Téléportation effectuée !")
        end
        Thread.Wait(0)
    end
end)

4. Compteur de Vitesse & Boost Nitro (Touche Shift)

Affiche un compteur de vitesse en km/h à l'écran et applique une accélération avec la touche Shift :

-- Fichier : main.lua
local TOUCHE_NITRO = 0x10 -- Touche Maj / Shift

Thread.Create(function()
    while true do
        local playerPed = GetPlayerChar(GetPlayerId())

        -- Vérifie si le joueur est dans un véhicule
        if IsCharInAnyCar(playerPed) then
            local veh = GetCarCharIsUsing(playerPed)
            local speedMps = GetCarSpeed(veh)
            local speedKmh = speedMps * 3.6

            -- Dessine le fond et la vitesse
            Draw.Rect(20, 700, 200, 50, 0xAA000000)
            Draw.Text(30, 715, string.format("VITESSE : %3.0f KM/H", speedKmh), 0xFFFFFFFF)

            -- Boost Nitro en appuyant sur Shift
            if Input.IsKeyDown(TOUCHE_NITRO) then
                SetCarForwardSpeed(veh, speedMps * 1.3)
                Draw.Text(30, 735, ">> NITRO ACTIVE <<", 0xFFFF0000)
            end
        end
        Thread.Wait(0)
    end
end)

5. Activer / Désactiver l'Invincibilité (Godmode sur F3)

Bascule l'invincibilité ON / OFF à chaque pression de la touche F3 :

-- Fichier : main.lua
local TOUCHE_GODMODE = 0x72 -- Touche F3
local godmodeActif = false
local etaitAppuye = false

Thread.Create(function()
    while true do
        local estAppuye = Input.IsKeyDown(TOUCHE_GODMODE)

        -- Se déclenche uniquement au premier clic (pas au maintien prolongé)
        if estAppuye and not etaitAppuye then
            godmodeActif = not godmodeActif
            local playerPed = GetPlayerChar(GetPlayerId())

            SetCharInvincible(playerPed, godmodeActif)
            SetCharProofs(playerPed, godmodeActif, godmodeActif, godmodeActif, godmodeActif, godmodeActif)

            if godmodeActif then
                Console.MsgBox("God Mode : ACTIVÉ")
            else
                Console.MsgBox("God Mode : DÉSACTIVÉ")
            end
        end
        etaitAppuye = estAppuye
        Thread.Wait(0)
    end
end)

6. Attendre Entre Deux Tours de Boucle

Tous les exemples ci-dessus tournent à chaque image, c'est ce que veut dire Thread.Wait(0). Quand un mod n'en a pas besoin, attendez plus longtemps. Le thread dort, et ne coûte rien pendant ce temps.

-- Fichier : main.lua
-- Régénère 5 points de vie par seconde, jusqu'à 200
Thread.Create(function()
    while true do
        local ped = GetPlayerChar(GetPlayerId())
        local hp = GetCharHealth(ped)

        if hp > 0 and hp < 200 then
            SetCharHealth(ped, math.min(200, hp + 5))
        end

        Thread.Wait(1000) -- une seconde, sans figer le jeu
    end
end)

Pourquoi ne pas compter les images soi-même ?

Vous pourriez tenir un compteur ecoule = ecoule + dt et agir quand il dépasse une seconde, mais chaque mod réimplémente alors la même comptabilité fragile. Thread.Wait est le motif auquel les mods ScriptHookDotNet sont écrits, donc un portage garde sa forme.


7. Un Menu a l'Ecran (Input.SetMenuOpen)

Ouvre un petit menu sur F5, navigable aux flèches. Signaler au loader que le menu est ouvert est ce qui empêche le personnage de bouger et de tirer pendant la navigation.

-- Fichier : main.lua
local MENU_KEY = 0x74 -- F5
local items = { "Santé max", "Armure max", "Réparer le véhicule" }

local open, index = false, 1
local wasDown = {}

-- Détection de front : Input.IsKeyDown est un état, pas un événement. Sans ça,
-- chaque frame où la touche reste enfoncée compterait comme un nouvel appui.
local function pressed(vk)
    local down = Input.IsKeyDown(vk)
    local hit = down and not wasDown[vk]
    wasDown[vk] = down
    return hit
end

Thread.Create(function()
    while true do
        if pressed(MENU_KEY) then
            open = not open
            Input.SetMenuOpen(open) -- masque flèches/Entrée pour le JEU, pas pour nous
        end

        if not open then return end

        if pressed(0x26) then index = math.max(1, index - 1) end          -- Haut
        if pressed(0x28) then index = math.min(#items, index + 1) end     -- Bas

        if pressed(0x0D) then -- Entrée
            local ped = GetPlayerChar(GetPlayerId())
            if index == 1 then SetCharHealth(ped, 200)
            elseif index == 2 then AddArmourToChar(ped, 100)
            elseif index == 3 and IsCharInAnyCar(ped) then FixCar(GetCarCharIsUsing(ped)) end
        end

        local bg        = Color.RGBA(0, 0, 0, 75)
        local blanc     = Color.RGB(255, 255, 255)
        local surbrill  = Color.RGB(255, 200, 60)

        Draw.Rect(40, 200, 260, 30 + #items * 24, bg)
        Draw.Text(52, 208, "MON MENU", surbrill)

        for i, label in ipairs(items) do
            local préfixe = (i == index) and "> " or "  "
            Draw.Text(52, 232 + (i - 1) * 24, préfixe .. label,
                      (i == index) and surbrill or blanc)
        end
        Thread.Wait(0)
    end
end)
function onUnload()
    Input.SetMenuOpen(false) -- ne jamais laisser les entrées du jeu masquées
end

Toujours relâcher le drapeau du menu

Si le mod s'arrête avec Input.SetMenuOpen(true) encore actif, le joueur se retrouve incapable de bouger. Le remettre à false dans onUnload() n'est pas optionnel.


8. Support Manette (Gamepad.IsButtonDown)

Même principe qu'au clavier, avec les masques XInput bruts sur la manette 0.

-- Fichier : main.lua
local PAD_A     = 0x1000
local PAD_B     = 0x2000
local PAD_LEFT  = 0x0004
local PAD_RIGHT = 0x0008

Thread.Create(function()
    while true do
        if Gamepad.IsButtonDown(PAD_A) then
            SetCharHealth(GetPlayerChar(GetPlayerId()), 200)
        end
        Thread.Wait(0)
    end
end)
Bouton Masque Bouton Masque
Croix Haut 0x0001 A 0x1000
Croix Bas 0x0002 B 0x2000
Croix Gauche 0x0004 X 0x4000
Croix Droite 0x0008 Y 0x8000
Start 0x0010 Gâchette gauche (LB) 0x0100
Back 0x0020 Gâchette droite (RB) 0x0200
Stick gauche 0x0040 Stick droit 0x0080

Le jeu doit avoir le focus

Comme Input.IsKeyDown, la lecture manette renvoie false tant que la fenêtre de GTA IV n'est pas au premier plan.


9. Sauvegarder des Réglages à Côté du Mod (MOD_DIR)

MOD_DIR est le chemin absolu du dossier de votre mod. Construisez chaque chemin de fichier à partir de lui. Le répertoire de travail du process appartient au jeu, pas à votre mod.

-- Fichier : main.lua
local SETTINGS = MOD_DIR and (MOD_DIR .. "\\settings.txt")

local godmode = false

local function charger()
    if not SETTINGS then return end -- lancé depuis l'éditeur, pas de dossier
    local f = io.open(SETTINGS, "r")
    if not f then return end
    godmode = (f:read("l") == "1")
    f:close()
end

local function sauver()
    if not SETTINGS then return end
    local f = io.open(SETTINGS, "w")
    if not f then return end
    f:write(godmode and "1" or "0")
    f:close()
end

charger()
Log.Print("god mode restauré : " .. tostring(godmode))

Thread.Create(function()
    while true do
        if godmode then
            SetCharInvincible(GetPlayerChar(GetPlayerId()), true)
        end
        Thread.Wait(0)
    end
end)
function onUnload()
    sauver()
end

MOD_DIR vaut nil depuis l'éditeur

Un buffer exécuté directement depuis l'éditeur F6 n'a pas de dossier propre, donc MOD_DIR vaut nil. Prévoyez le cas, comme ci-dessus, sinon le mod plantera dès le premier test d'un extrait.


10. Appeler un Natif Hors de Portée de la Globale Typée

Environ 237 natifs n'ont aucune signature documentée, et une poignée d'autres sont enregistrés avec la mauvaise. Native.Call pousse les arguments tels quels, sans consulter la signature. C'est l'échappatoire pour quand la globale PascalCase se trompe.

-- Fichier : main.lua
-- Le nom passé à Native.* est toujours le nom canonique SNAKE_CASE.

-- Une sortie : GET_GAME_TIMER écrit son résultat par pointeur au lieu de le
-- retourner, il lui faut donc CallOut, pas Call.
local _, buf = Native.CallOut("GET_GAME_TIMER", 1)
if buf then
    Log.Print("game timer : " .. string.unpack("<i4", buf))
end

-- Plusieurs sorties d'un coup : 3 buffers contigus font un Vector3.
local ped = GetPlayerChar(GetPlayerId())
local _, bx, by, bz = Native.CallOut("GET_PED_BONE_POSITION", 3, ped, 0, 0.0, 0.0, 0.0)
if bx then
    Log.Print(string.format("os à %.2f %.2f %.2f",
        string.unpack("<f", bx), string.unpack("<f", by), string.unpack("<f", bz)))
end

Call n'est pas CallOut

Native.Call rend le buffer de retour du natif. Un natif sans valeur de retour, qui écrit par pointeur de sortie, et GTA IV en est plein, ne vous donnera rien d'utile par cette voie. Vérifiez d'abord le natif dans la base. Un paramètre out veut dire qu'il vous faut Native.CallOut.

Parcourez tous les natifs, typés ou non, dans la base de natifs.