local entMeta = FindMetaTable("Entity") -- Maintains entities that are to be removed after disconnect local queuedForRemoval = {} --[[--------------------------------------------------------------------------- DarkRP hooks ---------------------------------------------------------------------------]] function GM:Initialize() self.Sandbox.Initialize(self) end function GM:playerBuyDoor(ply, ent) if ply:getJobTable().hobo then return false, DarkRP.getPhrase("door_hobo_unable") end return true end function GM:getDoorCost(ply, ent) return GAMEMODE.Config.doorcost ~= 0 and GAMEMODE.Config.doorcost or 30 end function GM:getVehicleCost(ply, ent) return GAMEMODE.Config.vehiclecost ~= 0 and GAMEMODE.Config.vehiclecost or 40 end local disallowedNames = {["ooc"] = true, ["shared"] = true, ["world"] = true, ["world prop"] = true} function GM:CanChangeRPName(ply, RPname) if disallowedNames[string.lower(RPname)] then return false, DarkRP.getPhrase("forbidden_name") end if not string.match(RPname, "^[a-zA-ZЀ-џ0-9 ]+$") then return false, DarkRP.getPhrase("illegal_characters") end local len = string.len(RPname) if len > 30 then return false, DarkRP.getPhrase("too_long") end if len < 3 then return false, DarkRP.getPhrase("too_short") end end function GM:canDemote(ply, target, reason) end function GM:canVote(ply, vote) end function GM:playerWalletChanged(ply, amount) end function GM:playerGetSalary(ply, amount) end function GM:DarkRPVarChanged(ply, var, oldvar, newvalue) end function GM:playerBoughtVehicle(ply, ent, cost) end function GM:playerBoughtDoor(ply, ent, cost) end function GM:canDropWeapon(ply, weapon) if not IsValid(weapon) then return false end local class = string.lower(weapon:GetClass()) if not GAMEMODE.Config.dropspawnedweapons then local jobTable = ply:getJobTable() if jobTable.weapons and table.HasValue(jobTable.weapons, class) then return false end end if self.Config.DisallowDrop[class] then return false end if not GAMEMODE.Config.restrictdrop then return true end for _, v in pairs(CustomShipments) do if v.entity ~= class then continue end return true end return false end function GM:DatabaseInitialized() DarkRP.initDatabase() end function GM:canSeeLogMessage(ply, message, colour) return true end function GM:canEarnNPCKillPay(ply, npc) return GAMEMODE.Config.npckillpay > 0 end function GM:calculateNPCKillPay(ply, npc) -- A NPC spawned by an addon might be worth more money than the default if npc.KillValue then return npc.KillValue end return GAMEMODE.Config.npckillpay end --[[--------------------------------------------------------- Gamemode functions ---------------------------------------------------------]] function GM:PlayerSpawnProp(ply, model) -- No prop spawning means no prop spawning. local allowed = GAMEMODE.Config.propspawning if not allowed then return false end if ply:isArrested() then return false end model = string.gsub(tostring(model), "\\", "/") model = string.gsub(tostring(model), "//", "/") local jobTable = ply:getJobTable() if jobTable.PlayerSpawnProp then jobTable.PlayerSpawnProp(ply, model) end return self.Sandbox.PlayerSpawnProp(self, ply, model) end function GM:PlayerSpawnedProp(ply, model, ent) self.Sandbox.PlayerSpawnedProp(self, ply, model, ent) ent.SID = ply.SID ent:CPPISetOwner(ply) local phys = ent:GetPhysicsObject() if IsValid(phys) then ent.RPOriginalMass = phys:GetMass() end if GAMEMODE.Config.proppaying then if ply:canAfford(GAMEMODE.Config.propcost) then DarkRP.notify(ply, 0, 4, DarkRP.getPhrase("deducted_money", DarkRP.formatMoney(GAMEMODE.Config.propcost))) ply:addMoney(-GAMEMODE.Config.propcost) else DarkRP.notify(ply, 1, 4, DarkRP.getPhrase("need_money", DarkRP.formatMoney(GAMEMODE.Config.propcost))) SafeRemoveEntity(ent) return false end end end local function checkAdminSpawn(ply, configVar, errorStr) local config = GAMEMODE.Config[configVar] if (config == true or config == 1) and ply:EntIndex() ~= 0 and not ply:IsAdmin() then DarkRP.notify(ply, 1, 5, DarkRP.getPhrase("need_admin", DarkRP.getPhrase(errorStr) or errorStr)) return false elseif config == 2 and ply:EntIndex() ~= 0 and not ply:IsSuperAdmin() then DarkRP.notify(ply, 1, 5, DarkRP.getPhrase("need_sadmin", DarkRP.getPhrase(errorStr) or errorStr)) return false elseif config == 3 and ply:EntIndex() ~= 0 then DarkRP.notify(ply, 1, 5, DarkRP.getPhrase("disabled", DarkRP.getPhrase(errorStr) or errorStr, DarkRP.getPhrase("see_settings"))) return false end return true end function GM:PlayerSpawnSENT(ply, class) return checkAdminSpawn(ply, "adminsents", "gm_spawnsent") and self.Sandbox.PlayerSpawnSENT(self, ply, class) and not ply:isArrested() end function GM:PlayerSpawnedSENT(ply, ent) self.Sandbox.PlayerSpawnedSENT(self, ply, ent) DarkRP.log(ply:Nick() .. " (" .. ply:SteamID() .. ") spawned SENT " .. ent:GetClass(), Color(255, 255, 0)) end local function canSpawnWeapon(ply) if (GAMEMODE.Config.adminweapons == 0 and ply:IsAdmin()) or (GAMEMODE.Config.adminweapons == 1 and ply:IsSuperAdmin()) or -- Can't use 2 to maintain compatibility (GAMEMODE.Config.adminweapons == 3) then return true end DarkRP.notify(ply, 1, 4, DarkRP.getPhrase("cant_spawn_weapons")) return false end function GM:PlayerSpawnSWEP(ply, class, info) return canSpawnWeapon(ply) and self.Sandbox.PlayerSpawnSWEP(self, ply, class, info) and not ply:isArrested() end function GM:PlayerGiveSWEP(ply, class, info) return canSpawnWeapon(ply) and self.Sandbox.PlayerGiveSWEP(self, ply, class, info) and not ply:isArrested() end function GM:PlayerSpawnEffect(ply, model) return self.Sandbox.PlayerSpawnEffect(self, ply, model) and not ply:isArrested() end function GM:PlayerSpawnVehicle(ply, model, class, info) return checkAdminSpawn(ply, "adminvehicles", "gm_spawnvehicle") and self.Sandbox.PlayerSpawnVehicle(self, ply, model, class, info) and not ply:isArrested() end function GM:PlayerSpawnedVehicle(ply, ent) self.Sandbox.PlayerSpawnedVehicle(self, ply, ent) local vehicleClass = ent.GetVehicleClass and " (" .. ent:GetVehicleClass() .. ")" or "" DarkRP.log(ply:Nick() .. " (" .. ply:SteamID() .. ") spawned Vehicle " .. ent:GetClass() .. vehicleClass, Color(255, 255, 0)) end function GM:PlayerSpawnNPC(ply, type, weapon) return checkAdminSpawn(ply, "adminnpcs", "gm_spawnnpc") and self.Sandbox.PlayerSpawnNPC(self, ply, type, weapon) and not ply:isArrested() end function GM:PlayerSpawnedNPC(ply, ent) self.Sandbox.PlayerSpawnedNPC(self, ply, ent) DarkRP.log(ply:Nick() .. " (" .. ply:SteamID() .. ") spawned NPC " .. ent:GetClass(), Color(255, 255, 0)) end function GM:PlayerSpawnRagdoll(ply, model) return self.Sandbox.PlayerSpawnRagdoll(self, ply, model) and not ply:isArrested() end function GM:PlayerSpawnedRagdoll(ply, model, ent) self.Sandbox.PlayerSpawnedRagdoll(self, ply, model, ent) ent.SID = ply.SID end function GM:EntityRemoved(ent) self.Sandbox.EntityRemoved(self, ent) if ent:IsVehicle() then local found = ent:CPPIGetOwner() if IsValid(found) then found.Vehicles = found.Vehicles or 1 found.Vehicles = found.Vehicles - 1 end end local owner = ent.Getowning_ent and ent:Getowning_ent() or Player(ent.SID or 0) if ent.DarkRPItem and IsValid(owner) and not ent.IsPocketing then owner:removeCustomEntity(ent.DarkRPItem) end if ent.isKeysOwnable and ent:isKeysOwnable() then ent:removeDoorData() end -- Quick workaround for the fact that we don't have a hook ordering system -- built into gmod. if self.DarkRPPostEntityRemoved then self.DarkRPPostEntityRemoved(self, ent) end end function GM:ShowSpare1(ply) local jobTable = ply:getJobTable() if jobTable.ShowSpare1 then return jobTable.ShowSpare1(ply) end end function GM:ShowSpare2(ply) local jobTable = ply:getJobTable() if jobTable.ShowSpare2 then return jobTable.ShowSpare2(ply) end end function GM:ShowTeam(ply) end function GM:ShowHelp(ply) end function GM:OnNPCKilled(victim, ent, weapon) -- If something killed the npc if not ent then return end if ent:IsVehicle() and ent:GetDriver():IsPlayer() then ent = ent:GetDriver() end -- If it wasn't a player directly, find out who owns the prop that did the killing if not ent:IsPlayer() then ent = Player(tonumber(ent.SID) or 0) end -- If we know by now who killed the NPC, pay them. if IsValid(ent) and hook.Call("canEarnNPCKillPay", GAMEMODE, ent, victim) then local amount = hook.Call("calculateNPCKillPay", GAMEMODE, ent, victim) ent:addMoney(amount) DarkRP.notify(ent, 0, 4, DarkRP.getPhrase("npc_killpay", DarkRP.formatMoney(amount))) end end function GM:KeyPress(ply, code) self.Sandbox.KeyPress(self, ply, code) end -- IsInRoom function to see if the player is in the same room. local roomTrResult = {} local roomTr = {output = roomTrResult} local function IsInRoom(listenerShootPos, talkerShootPos, talker) roomTr.start = talkerShootPos roomTr.endpos = listenerShootPos -- Listener needs not be ignored as that's the end of the trace roomTr.filter = talker roomTr.collisiongroup = COLLISION_GROUP_WORLD roomTr.mask = MASK_SOLID_BRUSHONLY util.TraceLine(roomTr) return not roomTrResult.HitWorld end local threed = GM.Config.voice3D local vrad = GM.Config.voiceradius local dynv = GM.Config.dynamicvoice local deadv = GM.Config.deadvoice local voiceDistance = GM.Config.voiceDistance * GM.Config.voiceDistance local DrpCanHear = {} -- Recreate DrpCanHear after Lua Refresh -- This prevents an indexing nil error in PlayerCanHearPlayersVoice for _, ply in ipairs(player.GetAll()) do DrpCanHear[ply] = {} end local gridSize = GM.Config.voiceDistance -- Grid cell size is equal to the size of the radius of player talking local floor = math.floor -- Caching floor as we will need to use it a lot -- Grid based position check local grid -- Translate player to grid coordinates. The first table maps players to x -- coordinates, the second table maps players to y coordinates. local plyToGrid = { {}, {} } -- Set DarkRP.voiceCheckTimeDelay before DarkRP is loaded to set the time -- between player voice radius checks. DarkRP.voiceCheckTimeDelay = DarkRP.voiceCheckTimeDelay or 0.3 timer.Create("DarkRPCanHearPlayersVoice", DarkRP.voiceCheckTimeDelay, 0, function() -- Voiceradius is off, everyone can hear everyone if not vrad then return end local players = player.GetHumans() -- Clear old values plyToGrid[1] = {} plyToGrid[2] = {} grid = {} local plyPos = {} local eyePos = {} -- Get the grid position of every player O(N) for _, ply in ipairs(players) do local pos = ply:GetPos() plyPos[ply] = pos eyePos[ply] = ply:EyePos() local x = floor(pos.x / gridSize) local y = floor(pos.y / gridSize) local row = grid[x] or {} local cell = row[y] or {} table.insert(cell, ply) row[y] = cell grid[x] = row plyToGrid[1][ply] = x plyToGrid[2][ply] = y DrpCanHear[ply] = {} -- Initialize output variable end -- Check all neighbouring cells for every player. -- We are only checking in 1 direction to avoid duplicate check of cells for _, ply1 in ipairs(players) do local gridX = plyToGrid[1][ply1] local gridY = plyToGrid[2][ply1] local ply1Pos = plyPos[ply1] local ply1EyePos = eyePos[ply1] for i = 0, 3 do local vOffset = 1 - ((i >= 3) and 1 or 0) local hOffset = -(i % 3-1) local x = gridX + hOffset local y = gridY + vOffset local row = grid[x] if not row then continue end local cell = row[y] if not cell then continue end for _, ply2 in ipairs(cell) do local canTalk = ply1Pos:DistToSqr(plyPos[ply2]) < voiceDistance and -- voiceradius is on and the two are within hearing distance (not dynv or IsInRoom(ply1EyePos, eyePos[ply2], ply2)) -- Dynamic voice is on and players are in the same room DrpCanHear[ply1][ply2] = canTalk and (deadv or ply2:Alive()) DrpCanHear[ply2][ply1] = canTalk and (deadv or ply1:Alive()) -- Take advantage of the symmetry end end end -- Doing a pass-through inside every cell to compute the interactions inside of the cells. -- Each grid check is O(N(N+1)/2) where N is the number of players inside the cell. for _, row in pairs(grid) do for _, cell in pairs(row) do local count = #cell for i = 1, count do local ply1 = cell[i] for j = i + 1, count do local ply2 = cell[j] local canTalk = plyPos[ply1]:DistToSqr(plyPos[ply2]) < voiceDistance and -- voiceradius is on and the two are within hearing distance (not dynv or IsInRoom(eyePos[ply1], eyePos[ply2], ply2)) -- Dynamic voice is on and players are in the same room DrpCanHear[ply1][ply2] = canTalk and (deadv or ply2:Alive()) DrpCanHear[ply2][ply1] = canTalk and (deadv or ply1:Alive()) -- Take advantage of the symmetry end end end end end) hook.Add("PlayerDisconnected", "DarkRPCanHear", function(ply) DrpCanHear[ply] = nil -- Clear to avoid memory leaks end) function GM:PlayerCanHearPlayersVoice(listener, talker) if not deadv and not talker:Alive() then return false end return not vrad or DrpCanHear[listener][talker] == true, threed end function GM:CanTool(ply, trace, mode) if not self.Sandbox.CanTool(self, ply, trace, mode) then return false end local ent = trace.Entity if IsValid(ent) then if ent.onlyremover then if mode == "remover" then return ply:IsAdmin() or ply:IsSuperAdmin() else return false end end if ent.nodupe and (mode == "weld" or mode == "weld_ez" or mode == "spawner" or mode == "duplicator" or mode == "adv_duplicator") then return false end if ent:IsVehicle() and mode == "nocollide" and not GAMEMODE.Config.allowvnocollide then return false end end return true end function GM:CanPlayerSuicide(ply) if ply.IsSleeping then DarkRP.notify(ply, 1, 4, DarkRP.getPhrase("unable", "suicide", "")) return false end if ply:isArrested() then DarkRP.notify(ply, 1, 4, DarkRP.getPhrase("unable", "suicide", "")) return false end if GAMEMODE.Config.wantedsuicide and ply:getDarkRPVar("wanted") then DarkRP.notify(ply, 1, 4, DarkRP.getPhrase("unable", "suicide", "")) return false end local jobTable = ply:getJobTable() if jobTable.CanPlayerSuicide then return jobTable.CanPlayerSuicide(ply) end return true end function GM:CanDrive(ply, ent) DarkRP.notify(ply, 1, 4, DarkRP.getPhrase("drive_disabled")) return false -- Disabled until people can't minge with it anymore end function GM:CanProperty(ply, property, ent) if self.Config.allowedProperties[property] and ent:CPPICanTool(ply, "remover") then return true end if property == "persist" and ply:IsSuperAdmin() then return true end DarkRP.notify(ply, 1, 4, DarkRP.getPhrase("property_disabled")) return false -- Disabled until antiminge measure is found end function GM:PlayerShouldTaunt(ply, actid) return GAMEMODE.Config.allowActs end function GM:DoPlayerDeath(ply, attacker, dmginfo, ...) local weapon = ply:GetActiveWeapon() local canDrop = hook.Call("canDropWeapon", self, ply, weapon) if (GAMEMODE.Config.dropweapondeath or ply.dropWeaponOnDeath) and weapon:IsValid() and canDrop then ply:dropDRPWeapon(weapon) end self.Sandbox.DoPlayerDeath(self, ply, attacker, dmginfo, ...) end function GM:PlayerDeath(ply, weapon, killer) local jobTable = ply:getJobTable() if jobTable.PlayerDeath then jobTable.PlayerDeath(ply, weapon, killer) end if GAMEMODE.Config.deathblack and not ply.blackScreen then ply.blackScreen = true SendUserMessage("blackScreen", ply, true) end if weapon:IsVehicle() and weapon:GetDriver():IsPlayer() then killer = weapon:GetDriver() end if GAMEMODE.Config.showdeaths then self.Sandbox.PlayerDeath(self, ply, weapon, killer) end ply:Extinguish() ply:ExitVehicle() if ply:isArrested() and not GAMEMODE.Config.respawninjail then -- If the player died in jail, make sure they can't respawn until their jail sentence is over -- NextSpawnTime is set to CurTime() on unarrest ply.NextSpawnTime = math.huge DarkRP.printMessageAll(HUD_PRINTCENTER, DarkRP.getPhrase("died_in_jail", ply:Nick())) DarkRP.notify(ply, 4, 4, DarkRP.getPhrase("dead_in_jail")) else -- Normal death, respawning. ply.NextSpawnTime = CurTime() + math.Clamp(GAMEMODE.Config.respawntime, 0, 10) end ply.DeathPos = ply:GetPos() if GAMEMODE.Config.dropmoneyondeath then local amount = GAMEMODE.Config.deathfee if not ply:canAfford(GAMEMODE.Config.deathfee) then amount = ply:getDarkRPVar("money") end if amount > 0 then ply:addMoney(-amount) DarkRP.createMoneyBag(ply:GetPos(), amount) end end if IsValid(ply) and (ply ~= killer or ply.Slayed) and not ply:isArrested() then if not GAMEMODE.Config.wantedrespawn then ply:setDarkRPVar("wanted", nil) end ply.DeathPos = nil ply.Slayed = false end ply.ConfiscatedWeapons = nil local KillerName = (killer:IsPlayer() and killer:Nick()) or tostring(killer) local WeaponName = IsValid(weapon) and ((weapon:IsPlayer() and weapon:GetActiveWeapon():IsValid() and weapon:GetActiveWeapon():GetClass()) or weapon:GetClass()) or "unknown" if killer == ply then KillerName = "Themself" WeaponName = "suicide trick" end DarkRP.log(ply:Nick() .. " was killed by " .. KillerName .. " with a " .. WeaponName, Color(255, 190, 0)) end local adminCopWeapons = { ["door_ram"] = true, ["arrest_stick"] = true, ["unarrest_stick"] = true, ["stunstick"] = true, ["weaponchecker"] = true, } function GM:PlayerCanPickupWeapon(ply, weapon) if ply:isArrested() then return false end if weapon.PlayerUse == false then return false end local weaponClass = weapon:GetClass() if ply:IsAdmin() and GAMEMODE.Config.AdminsCopWeapons and adminCopWeapons[weaponClass] then return true end local jobTable = ply:getJobTable() if jobTable.PlayerCanPickupWeapon then local val = jobTable.PlayerCanPickupWeapon(ply, weapon) return val == nil or val end if GAMEMODE.Config.license and not ply:getDarkRPVar("HasGunlicense") and not ply.RPLicenseSpawn then if GAMEMODE.NoLicense[string.lower(weaponClass)] or not weapon:IsWeapon() then return true end return false end return true end function GM:PlayerSetModel(ply) local jobTable = ply:getJobTable() -- Invalid job, return to Sandbox behaviour if not jobTable then return self.Sandbox.PlayerSetModel(ply) end if jobTable.PlayerSetModel then local model = jobTable.PlayerSetModel(ply) if model then ply:SetModel(model) return end end local EndModel = "" if GAMEMODE.Config.enforceplayermodel then if istable(jobTable.model) then local ChosenModel = string.lower(ply:getPreferredModel(ply:Team()) or "") local found for _, Models in pairs(jobTable.model) do if ChosenModel == string.lower(Models) then EndModel = Models found = true break end end if not found then EndModel = jobTable.model[math.random(#jobTable.model)] end else EndModel = jobTable.model end ply:SetModel(EndModel) else local cl_playermodel = ply:GetInfo("cl_playermodel") local modelname = player_manager.TranslatePlayerModel(cl_playermodel) ply:SetModel(ply:getPreferredModel(ply:Team()) or modelname) end self.Sandbox.PlayerSetModel(self, ply) ply:SetupHands() end local function initPlayer(ply) timer.Simple(5, function() if not IsValid(ply) then return end if GetGlobalBool("DarkRP_Lockdown") then SetGlobalBool("DarkRP_Lockdown", true) -- so new players who join know there's a lockdown, is this bug still there? end end) ply:initiateTax() ply:updateJob(team.GetName(GAMEMODE.DefaultTeam)) ply:setSelfDarkRPVar("salary", DarkRP.retrieveSalary(ply)) ply.LastJob = nil -- so players don't have to wait to get a job after joining ply.Ownedz = {} ply.LastLetterMade = CurTime() - 61 ply.LastVoteCop = CurTime() - 61 ply:SetTeam(GAMEMODE.DefaultTeam) ply.DarkRPInitialised = true -- Whether or not a player is being prevented from joining -- a specific team for a certain length of time if GAMEMODE.Config.restrictallteams then for i = 1, #RPExtraTeams do ply:teamBan(i, 0) end end end local function restoreReconnectedEnts(ply) local sid = ply:SteamID64() if not queuedForRemoval[sid] then return end timer.Remove("DarkRP_removeDisconnected_" .. sid) for _, e in pairs(queuedForRemoval[sid]) do if not IsValid(e) then continue end e.SID = ply.SID if e.Setowning_ent then e:Setowning_ent(ply) end -- Some entities (e.g. vehicles) have an SID, but do not have a DarkRPItem if e.DarkRPItem then ply:addCustomEntity(e.DarkRPItem) end end queuedForRemoval[sid] = nil end function GM:PlayerInitialSpawn(ply) self.Sandbox.PlayerInitialSpawn(self, ply) -- Initialize DrpCanHear for player (used for voice radius check) DrpCanHear[ply] = {} local sid = ply:SteamID() DarkRP.log(ply:Nick() .. " (" .. sid .. ") has joined the game", Color(0, 130, 255)) ply:setDarkRPVarsAttribute() ply:restorePlayerData() initPlayer(ply) ply.SID = ply:UserID() timer.Simple(1, function() if not IsValid(ply) then return end local group = GAMEMODE.Config.DefaultPlayerGroups[sid] if group then ply:SetUserGroup(group) end end) restoreReconnectedEnts(ply) end function GM:PlayerSelectSpawn(ply) local spawn = self.Sandbox.PlayerSelectSpawn(self, ply) local jobTable = ply:getJobTable() if jobTable.PlayerSelectSpawn then jobTable.PlayerSelectSpawn(ply, spawn) end local POS if spawn and spawn.GetPos then POS = spawn:GetPos() else POS = ply:GetPos() end local CustomSpawnPos = DarkRP.retrieveTeamSpawnPos(ply:Team()) if GAMEMODE.Config.customspawns and not ply:isArrested() and CustomSpawnPos and next(CustomSpawnPos) ~= nil then POS = CustomSpawnPos[math.random(1, #CustomSpawnPos)] end -- Spawn where died in certain cases if GAMEMODE.Config.strictsuicide and ply.DeathPos then POS = ply.DeathPos end if ply:isArrested() then POS = DarkRP.retrieveJailPos() or ply.DeathPos -- If we can't find a jail pos then we'll use where they died as a last resort end -- Make sure the player doesn't get stuck in something local _, hull = ply:GetHull() POS = DarkRP.findEmptyPos(POS, {ply}, 600, 30, hull) return spawn, POS end local oldPlyColor local function disableBabyGod(ply) if not IsValid(ply) or not ply.Babygod then return end ply.Babygod = nil ply:SetRenderMode(RENDERMODE_NORMAL) ply:GodDisable() -- Don't reinstate the SetColor function -- if there are still players who are babygodded local reinstateOldColor = true for _, p in ipairs(player.GetAll()) do reinstateOldColor = reinstateOldColor and p.Babygod == nil end if reinstateOldColor then entMeta.SetColor = oldPlyColor oldPlyColor = nil end ply:SetColor(ply.babyGodColor or color_white) ply.babyGodColor = nil end local function enableBabyGod(ply) timer.Remove(ply:EntIndex() .. "babygod") ply.Babygod = true ply:GodEnable() ply.babyGodColor = ply:GetColor() ply:SetRenderMode(RENDERMODE_TRANSALPHA) if not oldPlyColor then oldPlyColor = entMeta.SetColor entMeta.SetColor = function(p, c, ...) if not p.Babygod then return oldPlyColor(p, c, ...) end p.babyGodColor = c oldPlyColor(p, Color(c.r, c.g, c.b, 100)) end end ply:SetColor(ply.babyGodColor) timer.Create(ply:EntIndex() .. "babygod", GAMEMODE.Config.babygodtime or 0, 1, fp{disableBabyGod, ply}) end function GM:PlayerSpawn(ply) if not ply.DarkRPInitialised then DarkRP.errorNoHalt( string.format("DarkRP was unable to introduce player \"%s\" to the game. Expect further errors and shit generally being fucked!", IsValid(ply) and ply:Nick() or "unknown"), 1, { "This error most likely does not stand on its own, and previous serverside errors have a very good chance of telling you the cause.", "Note that errors from another addon could cause this. Specifically when they're thrown during 'PlayerInitialSpawn'.", "This error can also be caused by some other addon returning a value in 'PlayerInitialSpawn', though that is less likely.", "Errors in your DarkRP configuration (jobs, shipments, etc.) could also cause this. Earlier errors should tell you when this is the case." } ) end ply:CrosshairEnable() ply:UnSpectate() -- Kill any colormod if ply.blackScreen then ply.blackScreen = false SendUserMessage("blackScreen", ply, false) end if GAMEMODE.Config.babygod and not ply.IsSleeping and not ply.Babygod then enableBabyGod(ply) end ply.IsSleeping = false ply:Extinguish() for i = 0, 2 do local vm = ply:GetViewModel(i) if IsValid(vm) then vm:Extinguish() end end if ply.demotedWhileDead then ply.demotedWhileDead = nil local demoteTeam = hook.Call("demoteTeam", nil, ply) or GAMEMODE.DefaultTeam ply:changeTeam(demoteTeam, true) ply:setDarkRPVar("job", team.GetName(demoteTeam)) end local jobTable = ply:getJobTable() player_manager.SetPlayerClass(ply, jobTable.playerClass or "player_darkrp") ply:applyPlayerClassVars(true) player_manager.RunClass(ply, "Spawn") hook.Call("PlayerLoadout", self, ply) hook.Call("PlayerSetModel", self, ply) local ent, pos = hook.Call("PlayerSelectSpawn", self, ply) ply:SetPos(pos or ent:GetPos()) if jobTable.PlayerSpawn then jobTable.PlayerSpawn(ply) end DarkRP.log(ply:Nick() .. " (" .. ply:SteamID() .. ") spawned") end function GM:PlayerLoadout(ply) self.Sandbox.PlayerLoadout(self, ply) if ply:isArrested() then return end ply.RPLicenseSpawn = true timer.Simple(1, function() if not IsValid(ply) then return end ply.RPLicenseSpawn = false end) local jobTable = ply:getJobTable() for _, v in pairs(jobTable.weapons or {}) do ply:Give(v) end if jobTable.PlayerLoadout then local val = jobTable.PlayerLoadout(ply) if val == true then ply:SwitchToDefaultWeapon() return end end if jobTable.ammo then for k, v in pairs(jobTable.ammo) do ply:SetAmmo(v, k) end end for _, v in pairs(self.Config.DefaultWeapons) do ply:Give(v) end CAMI.PlayerHasAccess(ply, "DarkRP_GetAdminWeapons", function(access) if not access or not IsValid(ply) then return end for _, v in pairs(GAMEMODE.Config.AdminWeapons) do ply:Give(v) end if not GAMEMODE.Config.AdminsCopWeapons then return end ply:Give("door_ram") ply:Give("arrest_stick") ply:Give("unarrest_stick") ply:Give("stunstick") ply:Give("weaponchecker") end) ply:SwitchToDefaultWeapon() end --[[--------------------------------------------------------------------------- Remove with a delay if the player doesn't rejoin before the timer has run out ---------------------------------------------------------------------------]] local function removeDelayed(entList, ply) local removedelay = GAMEMODE.Config.entremovedelay if removedelay <= 0 then for _, e in pairs(entList) do SafeRemoveEntity(e) end return end local sid = ply:SteamID64() queuedForRemoval[sid] = entList timer.Create("DarkRP_removeDisconnected_" .. sid, removedelay, 1, function() for _, e in pairs(queuedForRemoval[sid] or {}) do SafeRemoveEntity(e) end queuedForRemoval[sid] = nil end) end -- Collect entities that are to be removed local function collectRemoveEntities(ply) if not GAMEMODE.Config.removeondisconnect then return {} end local collect = {} -- Get the classes of entities to remove local remClasses = {} for _, customEnt in pairs(DarkRPEntities) do remClasses[string.lower(customEnt.ent)] = true end local sid = ply.SID for _, v in ipairs(ents.GetAll()) do if v.SID ~= sid or not v:IsVehicle() and not remClasses[string.lower(v:GetClass() or "")] then continue end table.insert(collect, v) end if not ply:isMayor() then return collect end for _, ent in pairs(ply.lawboards or {}) do if not IsValid(ent) then continue end table.insert(collect, ent) end return collect end function GM:PlayerDisconnected(ply) self.Sandbox.PlayerDisconnected(self, ply) timer.Remove(ply:SteamID64() .. "jobtimer") timer.Remove(ply:SteamID64() .. "propertytax") local isMayor = ply:isMayor() local remList = collectRemoveEntities(ply) removeDelayed(remList, ply) DarkRP.destroyQuestionsWithEnt(ply) DarkRP.destroyVotesWithEnt(ply) if isMayor and GetGlobalBool("DarkRP_LockDown") then -- Stop the lockdown DarkRP.unLockdown(ply) end if isMayor and GAMEMODE.Config.shouldResetLaws then DarkRP.resetLaws() end if IsValid(ply.SleepRagdoll) then ply.SleepRagdoll:Remove() end ply:keysUnOwnAll() DarkRP.log(ply:Nick() .. " (" .. ply:SteamID() .. ") disconnected", Color(0, 130, 255)) local agenda = ply:getAgendaTable() -- Clear agenda if agenda and ply:Team() == agenda.Manager and team.NumPlayers(ply:Team()) <= 1 then agenda.text = nil for _, v in ipairs(player.GetAll()) do if v:getAgendaTable() ~= agenda then continue end v:setSelfDarkRPVar("agenda", agenda.text) end end local jobTable = ply:getJobTable() if jobTable.PlayerDisconnected then jobTable.PlayerDisconnected(ply) end end function GM:GetFallDamage(ply, flFallSpeed) if GetConVar("mp_falldamage"):GetBool() or GAMEMODE.Config.realisticfalldamage then if GAMEMODE.Config.falldamagedamper then return flFallSpeed / GAMEMODE.Config.falldamagedamper else return flFallSpeed / 15 end else if GAMEMODE.Config.falldamageamount then return GAMEMODE.Config.falldamageamount else return 10 end end end local function fuckQAC() local netRecs = {"Debug1", "Debug2", "checksaum", "gcontrol_vars", "control_vars", "QUACK_QUACK_MOTHER_FUCKER"} for _, v in ipairs(netRecs) do net.Receivers[v] = fn.Id end end function GM:InitPostEntity() self.InitPostEntityCalled = true local physData = physenv.GetPerformanceSettings() physData.MaxVelocity = 2000 physData.MaxAngularVelocity = 3636 physenv.SetPerformanceSettings(physData) -- Scriptenforcer enabled by default? Fuck you, not gonna happen. if not GAMEMODE.Config.disallowClientsideScripts then game.ConsoleCommand("sv_allowcslua 1\n") timer.Simple(1, fuckQAC) -- Also, fuck QAC which bans innocent people when allowcslua = 1 end game.ConsoleCommand("physgun_DampingFactor 0.9\n") game.ConsoleCommand("sv_sticktoground 0\n") game.ConsoleCommand("sv_airaccelerate 1000\n") -- sv_alltalk must be 0 -- Note, everyone will STILL hear everyone UNLESS GM.Config.voiceradius is set to true -- This will fix the GM.Config.voiceradius not working game.ConsoleCommand("sv_alltalk 0\n") if GAMEMODE.Config.unlockdoorsonstart then for _, v in ipairs(ents.GetAll()) do if not v:isDoor() then continue end v:Fire("unlock", "", 0) end end end timer.Simple(0.1, function() if not GAMEMODE.InitPostEntityCalled then GAMEMODE:InitPostEntity() end end) function GM:loadCustomDarkRPItems() -- Error when the default team isn't set if not GAMEMODE.DefaultTeam or not RPExtraTeams[GAMEMODE.DefaultTeam] then -- Re-set to first available team to hopefully prevent further errors. -- Because this error is more important than any that follow because of it. GAMEMODE.DefaultTeam = next(RPExtraTeams) local hints = { "This may happen when you disable the default citizen job. Make sure you update GAMEMODE.DefaultTeam to the new default team.", "GAMEMODE.DefaultTeam may be set to a job that does not exist anymore. Did you remove the job you had set to default?", "The error being in jobs.lua is a guess. This is usually right, but the problem might lie somewhere else." } -- Gotta be totally clear here local stack = "\tjobs.lua, settings.lua, disabled_defaults.lua or any of your other custom files." DarkRP.error("GAMEMODE.DefaultTeam is not set to an existing job.", 1, hints, "lua/darkrp_customthings/jobs.lua", -1, stack) end end function GM:PlayerLeaveVehicle(ply, vehicle) if GAMEMODE.Config.autovehiclelock and vehicle:isKeysOwnedBy(ply) then vehicle:keysLock() end self.Sandbox.PlayerLeaveVehicle(self, ply, vehicle) end local function ClearDecals() if GAMEMODE.Config.decalcleaner then for _, p in ipairs(player.GetAll()) do p:ConCommand("r_cleardecals") end end end timer.Create("RP_DecalCleaner", GM.Config.decaltimer, 0, ClearDecals) function GM:PlayerSpray() return not GAMEMODE.Config.allowsprays end function GM:GravGunOnPickedUp(ply, ent, ...) self.Sandbox.GravGunOnPickedUp(self, ply, ent, ...) -- Keeping track of who is holding an entity is done to make sure the entity -- cannot be pocketed. This is because holding an entity with the gravgun -- changes some properties. One such property is setting the mass to 1, -- causing the mass check of the pocket to always succeed. ent.DarkRPBeingGravGunHeldBy = ply end function GM:GravGunOnDropped(ply, ent, ...) self.Sandbox.GravGunOnDropped(self, ply, ent, ...) -- See comment at GravGunOnPickedUp ent.DarkRPBeingGravGunHeldBy = nil end