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.