Physgun Wiki

Как добавить профессию

Профессии не регистрируются кодом напрямую: sh_registrator.lua при старте сканирует папку jobs/ и сам вызывает player_manager.RegisterPlayerClass для каждого найденного файла. Чтобы добавить профессию, достаточно создать один файл.

1. Файл профессии

gamemodes/darkrp/jobs/police.lua. ID профессии берётся из имени файла (police), сам файл выполняется через include с глобальной таблицей JOB, а не return:

JOB.DisplayName = "Полицейский" JOB.Description = "Следит за порядком в городе, задерживает нарушителей" JOB.Color = Color(23, 55, 181) JOB.Team = TEAM_GOVERNMENT JOB.MaxPlayers = 6 JOB.MaxHealth = 100 JOB.StartHealth = 100 JOB.StartArmor = 50 JOB.Salary = 750 JOB.Flags = { JOB_FLAG_CANT_BUY_DOOR, JOB_FLAG_NEED_VOTE } JOB.SWEPs = { "weapon_stunstick", "weapon_pistol" } JOB.Model = { "models/player/police.mdl", "models/player/police_fem.mdl" } function JOB:OnJoined(oldJob) self.Player:SellAllDoors() self.Player:LeaveAllDoors() end

JOB.Team обязателен и должен быть объявлен раньше в teams.lua. JOB.Model — таблица моделей, rp_player:SetModel выберет случайную при спавне.

Team, Model, MaxPlayers, Salary, DisplayName, Color, Flags и SWEPs реально применяются (см. таблицу ниже). Description, MaxHealth, StartHealth, StartArmor пока только хранятся на классе: rp_player:Spawn() не вызывает SetMaxHealth/SetHealth/SetArmor.

2. Выдаваемое оружие

JOB.SWEPs выдаётся при спавне в rp_player:Loadout вместе со стандартным набором (Default_SWEPs: физпушка, тулган, ключи):

function DEF_PLY:Loadout() for k, v in ipairs(self.Default_SWEPs) do self.Player:Give(v) end for k, v in ipairs(self.SWEPs) do self.Player:Give(v) end end

GM:PlayerLoadout перед этим полностью снимает оружие и патроны (StripWeapons/RemoveAllAmmo), так что предыдущий набор не накапливается при респавне или смене профессии.

3. Флаги

JOB.Flags — массив констант из sh_jobs.lua:

Флаг Значение Где используется
JOB_FLAG_CANT_BUY_DOOR 4 OnPlayerBuyDoor и OnPlayerAddDoorOwner: нельзя покупать двери и становиться совладельцем.
JOB_FLAG_NEED_VOTE 2 Команда /become (sv_jobs.lua): устройство на профессию проходит через голосование сервера.
JOB_FLAG_UNDISMISSABLE 1 Команда /demote: с такой профессии нельзя уволить.
JOB_FLAG_NONRP 3 Объявлен, но пока нигде не читается.
JOB_FLAG_DEMOTE_ON_DEATH 5 PLAYER:DemoteOnDeath: после смерти профессия сбрасывается на дефолтную.

4. Переопределение поведения геймода

Базовый класс rp_player (sh_jobs.lua) объявляет пустые методы-перехватчики, которые дёргает player_manager.RunClass из соответствующих GM-хуков до дефолтной логики геймода: CanBuyDoor/OnBoughtDoor, CanSellDoor/OnSoldDoor, OnRenamedDoor, CanAddDoorOwner/OnAddedDoorOwner, OnRemovedDoorOwner, CanOpenDoor, OnLeftDoor, CanDropMoney/OnDroppedMoney, CanDropDeathMoney/OnDroppedDeathMoney, CanTransferMoney/OnTransferedMoney, CheckBuildLimit, OnSpawnedProp, OnSalary, CanJoin, OnJoined. Профессия может переопределить любой из них прямо в своём файле:

-- jobs/police.lua function JOB:CanBuyDoor(door) return false -- полицейским двери покупать нельзя (дублирует JOB_FLAG_CANT_BUY_DOOR) end function JOB:CanJoin(ply) return ply:Money() >= 5000 -- вход в профессию только с деньгами end

Если метод вернул не-nil, это решение побеждает; вернул nil (или не определён) — используется дефолт геймода. Полный список хуков и их дефолтов — в разделах справочника «Двери», «Экономика» и «Профессии» в боковом меню.

5. Смена профессии игроком

Игрок меняет профессию командой /become <id> (sv_jobs.lua), которая ищет id среди player_manager.GetPlayerClasses(). Если у профессии флаг JOB_FLAG_NEED_VOTE — сервер голосует 20 секунд (Config.Defaults.JobVoteSeconds), иначе смена происходит сразу через PLAYER:SetJob. Между сменами действует кулдаун Config.Defaults.NextJobChange.

Уволить другого игрока может команда /demote <id> <причина> — тоже через голосование (Config.Defaults.DemoteVoteSeconds), недоступна для профессий с JOB_FLAG_UNDISMISSABLE и требует минимум Config.Defaults.MinPlayersToDemote игроков на сервере.

6. Двери для профессии

Привязка двери к профессии по её ID, один раз при настройке карты, через SetDoorJob:

hook.Add("InitPostEntity", "SetupPoliceDoors", function() local door = ents.FindByClass("prop_door_rotating")[1] if IsValid(door) then door:SetDoorJob("police") end end)

Игроки профессии police получат доступ к замку двери через CanBeOpenedBy без покупки: двери с назначенной профессией нельзя купить (см. PLAYER:BuyDoor).