Gamemodes
Un gamemode est le mode de jeu d'une partie, écrit en Lua : ses règles (armes, noclip, dégâts, menu de spawn), ses équipes, ses manches et son HUD. Sandbox est lui-même un gamemode.
Gamemodes et addons
| Gamemode | Addon Lua | |
|---|---|---|
| Rôle | Les règles de la partie | Un ajout chargé dans toutes les parties |
| Combien par partie | Un seul, choisi en créant la partie | Tous les addons cochés dans Mes addons |
| Dossier | gamemodes/<nom>/ | addons/<nom>/lua/ (voir Structure d'un addon) |
| Fonctions | function GM:PlayerSpawn(ply)… | hook.Add("PlayerSpawn", "id", function(ply) … end) |
Quand tu crées une partie (Jouer → Créer une partie), tu choisis d'abord le gamemode, puis la carte. Tous les addons activés sont chargés en plus.
Créer un gamemode
- Créer → Code → Nouveau gamemode : choisis un nom et le gamemode dont il dérive (Sandbox par défaut).
- L'éditeur de code s'ouvre sur les fichiers créés.
- TESTER (F5) lance une partie solo avec ton gamemode. F2 pour revenir au code, F5 pour recharger.
Pendant la partie, enregistrer un fichier du gamemode le ré-exécute seul (autorefresh) avec GM = la table du gamemode en cours : tes function GM:… remplacent les anciennes, sans redémarrer la partie, puis le hook OnReloaded est appelé.
Les gamemodes locaux sont dans %APPDATA%\Godot\app_userdata\PhysiCore\gamemodes\.
Structure
mon_gamemode/
├── gamemode.json titre, description, gamemode de base
├── shared.lua serveur + client, chargé en premier (équipes, constantes)
├── shared/*.lua autres fichiers partagés
├── entities/*.lua entités scriptées (une par fichier, classe = nom du fichier)
├── weapons/*.lua armes Lua (SWEP, classe = nom du fichier), partagées
├── stools/*.lua outils de toolgun Lua (TOOL), partagés
├── init.lua serveur : les règles
├── server/*.lua autres fichiers serveur
├── cl_init.lua client : le HUD
└── client/*.lua autres fichiers client
Les fichiers de chaque dossier sont chargés par ordre alphabétique, après le fichier principal. Il n'y a pas de include() : range simplement tes fichiers dans ces dossiers. Les armes et outils sont décrits dans Armes et outils en Lua, les points et zones de map dans Points, zones et entités de map.
{
"title": "Chasse aux pièces",
"description": "Deux équipes ramassent des pièces en 2 minutes.",
"base": "sandbox",
"author": "Moi"
}
Héritage
Un gamemode dérive d'un autre (clé base) : il reprend toutes ses fonctions et ne redéfinit que ce qu'il change. Les fichiers du gamemode de base sont chargés avant les siens. Pour appeler la version de base d'une fonction :
function GM:PlayerSpawn(ply)
self.BaseClass.PlayerSpawn(self, ply) -- armes et noclip du gamemode de base
ply:SetHealth(50)
end
| Gamemode intégré | Comportement |
|---|---|
| Base (caché) | Rien n'est autorisé : pas d'armes, pas de noclip, pas de menu de spawn. Idéal pour un mode de jeu strict. |
| Sandbox (dérive de Base) | Toutes les armes (physgun, toolgun, pied-de-biche, pistolet, fusil à pompe, fusil d'assaut), noclip, menu de spawn avec l'onglet Armes. En partie solo, le physgun attrape aussi les PNJ. |
Leur code se lit dans Créer → Code (lecture seule) ; Dupliquer en fait une copie modifiable.
DeriveGamemode
Comme dans GMod, la base peut aussi se déclarer en Lua. Si gamemode.json n'a pas de clé base, le jeu cherche DeriveGamemode("nom") en début de ligne dans shared.lua, puis init.lua, puis cl_init.lua, et s'en sert comme base (fichiers de la base chargés avant). Sans l'un ni l'autre, la base est Sandbox. Quand elle existe, la clé base de gamemode.json reste prioritaire.
-- shared.lua d'un gamemode porté de GMod (gamemode.json sans "base")
DeriveGamemode("base")
GM.Name = "Mon mode strict"
À l'exécution, DeriveGamemode(nom) fait hériter GM de ce gamemode (GM.BaseClass, et les fonctions absentes sont lues dans la base). Le gamemode doit déjà être chargé, c'est-à-dire être la base de la partie : sinon une erreur est écrite dans la console.
Quand tous les fichiers du gamemode et des addons ont été exécutés, le jeu appelle le hook PostGamemodeLoaded, puis GM:Initialize, dans chaque royaume.
Les fonctions du gamemode
Chaque événement appelle d'abord les hook.Add des addons, puis la fonction GM:Événement du gamemode (sauf si un hook a renvoyé une valeur). Les règles principales :
| Fonction | Côté | Rôle |
|---|---|---|
GM:PlayerLoadout(ply) | serveur | Armes de départ : ply:Give("physgun"), ply:Give("pistol")… (voir Armes). |
GM:PlayerGiveWeapon(ply, arme) | serveur | true pour laisser prendre une arme dans le menu de spawn. |
GM:PlayerSetModel(ply, id) | serveur | true pour accepter le playermodel qu'un joueur choisit dans Apparence ("" = mannequin). Base et Sandbox acceptent. Voir Playermodels. |
GM:PlayerSpawnNPC(ply, classe) | serveur | true pour laisser faire apparaître un PNJ depuis le menu de spawn (Base : non, Sandbox : oui). Voir PNJ. |
GM:PlayerNoClip(ply) | serveur | true pour autoriser le noclip. |
GM:PlayerSpawnProp(ply, modèle) | serveur | false pour interdire le menu de spawn et les dupes. |
GM:PlayerShouldTakeDamage(ply, cause, attaquant) | serveur | false pour annuler des dégâts (chutes, tirs…). |
GM:PhysgunPickup(ply, ent) | serveur | false : le physgun ne l'attrape pas. ent est un prop, un véhicule ou un PNJ. Base refuse les PNJ ; Sandbox les accepte. En multijoueur, la protection des props a déjà refusé ce qui appartient aux autres. |
GM.PropProtection | partagé | false (dans shared.lua) désactive la protection des props pour ce gamemode, qui gère alors lui-même ce que chacun peut toucher (hooks PhysgunPickup, CanTool…). Pour l'ajuster sans la couper : hook ProtectionCanTouch. |
GM:HUDPaint() | client | Dessine ton HUD (voir Lua client). |
GM:HUDShouldDraw(nom) | client | false cache un élément du HUD du jeu. |
GM:SpawnMenuOpen() | client | false : la touche du menu de spawn ne fait rien. |
La liste complète est dans Hooks et fonctions du gamemode.
Classes de joueur : player_manager
Une classe de joueur regroupe les réglages d'un rôle (vitesses, vie, armes). Déclare-la dans un fichier partagé avec player_manager.RegisterClass(nom, table, base) (base conseillée : "player_default", qui existe déjà), puis donne-la côté serveur avec player_manager.SetPlayerClass(ply, nom) : elle s'applique à la prochaine apparition.
| Champ ou fonction de la classe | Rôle |
|---|---|
DisplayName | Nom affichable (le nom de la classe par défaut). |
WalkSpeed, RunSpeed, SlowWalkSpeed, JumpPower | Vitesses en m/s (comme ply:SetWalkSpeed… ; pas les unités de GMod). |
MaxHealth, StartHealth, StartArmor | Vie maximale, vie et armure à l'apparition. |
CLASS:Spawn() | À chaque apparition, après les réglages ci-dessus. |
CLASS:Loadout() | Armes de départ (appelée par GM:PlayerLoadout de Base). |
Dans ces fonctions, self.Player est le joueur. Le gamemode Base applique la classe dans GM:PlayerSpawn (player_manager.OnPlayerSpawn(ply)) et lance son Loadout dans GM:PlayerLoadout : un gamemode qui remplace ces fonctions doit les appeler lui-même (Sandbox remplace PlayerLoadout : le Loadout de la classe n'y est pas lancé). Aussi : player_manager.GetPlayerClass(ply) (des deux côtés, "" sans classe), player_manager.ClearPlayerClass(ply), player_manager.GetPlayerClasses(), et player_manager.RunClass(ply, "Fonction", …) pour appeler n'importe quelle fonction de sa classe.
-- shared.lua
local CLASS = {
DisplayName = "Policier",
WalkSpeed = 4,
RunSpeed = 8,
MaxHealth = 150,
StartHealth = 150,
StartArmor = 50,
}
function CLASS:Loadout()
self.Player:Give("hands")
self.Player:Give("pistol")
end
player_manager.RegisterClass("monmode_policier", CLASS, "player_default")
-- init.lua (le gamemode dérive de Base)
hook.Add("PlayerInitialSpawn", "monmode_classe", function(ply)
player_manager.SetPlayerClass(ply, "monmode_policier")
end)
Équipes, points et manches
team.SetUp(numéro, nom, couleur)dansshared.lua: le serveur et les clients connaissent les équipes. Le tableau des scores (Tab) les affiche.ply:SetTeam(n),ply:Team(),team.GetPlayers(n),team.AddScore(n, points).- Points et morts des joueurs :
ply:AddFrags(1),ply:Frags(),ply:Deaths()(les morts sont comptées automatiquement). - Pour une manche, garde l'état dans des variables réseau globales (
SetGlobalFloat…) : les clients peuvent afficher le chrono.
Exemple complet : chasse aux pièces
shared.lua
GM.Name = "Chasse aux pièces"
team.SetUp(1, "Rouges", Color(220, 60, 60))
team.SetUp(2, "Bleus", Color(60, 120, 230))
entities/piece.lua (voir Entités scriptées)
ENT.PrintName = "Pièce"
ENT.Model = "builtin:ball"
ENT.Physics = "static"
ENT.PhysgunDisabled = true
if SERVER then
function ENT:StartTouch(ply)
ply:AddFrags(1)
team.AddScore(ply:Team(), 1)
self:Remove()
end
end
init.lua
local DUREE = 120
-- Équipe la moins nombreuse.
function GM:PlayerInitialSpawn(ply)
if team.NumPlayers(1) <= team.NumPlayers(2) then
ply:SetTeam(1)
else
ply:SetTeam(2)
end
end
function GM:PlayerLoadout(ply) end -- pas d'armes
function GM:PlayerNoClip(ply) return false end
function GM:PlayerSpawnProp(ply, model) return false end
local function NouvelleManche()
game.CleanUpMap()
team.SetScore(1, 0)
team.SetScore(2, 0)
SetGlobalFloat("fin_manche", CurTime() + DUREE)
for i = 1, 20 do
local piece = ents.Create("piece")
piece:SetPos(Vector(math.random(-40, 40), 1, math.random(-40, 40)))
piece:Spawn()
end
end
function GM:Initialize()
timer.Simple(1, NouvelleManche)
end
function GM:Think(dt)
local fin = GetGlobalFloat("fin_manche", 0)
if fin > 0 and CurTime() >= fin then
SetGlobalFloat("fin_manche", 0)
local gagnant = team.GetScore(1) > team.GetScore(2) and 1 or 2
PrintMessage("Les " .. team.GetName(gagnant) .. " gagnent la manche !")
timer.Simple(5, NouvelleManche)
end
end
cl_init.lua
function GM:HUDPaint()
local reste = GetGlobalFloat("fin_manche", 0) - CurTime()
if reste > 0 then
draw.SimpleText(string.FormattedTime(reste), "Big", ScrW() / 2, 20, Color(255, 255, 255), TEXT_ALIGN_CENTER)
end
draw.SimpleText(team.GetName(1) .. " : " .. team.GetScore(1), "Bold", 20, 20, team.GetColor(1))
draw.SimpleText(team.GetName(2) .. " : " .. team.GetScore(2), "Bold", 20, 44, team.GetColor(2))
end
function GM:HUDShouldDraw(name)
return name ~= "Weapon" and name ~= "Crosshair"
end
Activer, partager
- Explorer → Mods → Mes addons → Gamemodes : décoche un gamemode pour qu'il ne soit plus proposé.
- Publier… (dans l'éditeur de code ou Mods → Publier) l'envoie sur le Workshop avec le tag
Gamemode. Un gamemode du Workshop auquel tu es abonné apparaît dans Créer une partie. - Les joueurs qui rejoignent ta partie n'ont pas besoin d'avoir ton gamemode : le serveur leur envoie ses scripts client.