diff --git a/gamemode/shared.lua b/gamemode/shared.lua index 570244c..f5b790a 100644 --- a/gamemode/shared.lua +++ b/gamemode/shared.lua @@ -53,7 +53,7 @@ end -- end -- ) -function GM:IsSpawnpointSuitable(ply, spawnpointent, bMakeSuitable) +function GM:IsSpawnpointSuitable(ply, spawnpointent, bMakeSuitable) local Pos = spawnpointent:GetPos() -- Note that we're searching the default hull size here for a player in the way of our spawning. @@ -83,9 +83,90 @@ function GM:IsSpawnpointSuitable(ply, spawnpointent, bMakeSuitable) if (Blockers > 0) then return false end - return true -end - + return true +end + +if SERVER then + -- Preserve the inherited selector across script auto-refreshes so the + -- emergency fallback does not build a chain of older wrappers. + local DefaultPlayerSelectSpawn = GM.CustomCoopDefaultPlayerSelectSpawn or GM.PlayerSelectSpawn + GM.CustomCoopDefaultPlayerSelectSpawn = DefaultPlayerSelectSpawn + local spawnPriorities = { + info_player_deathmatch = 1, + info_player_coop = 1, + info_coop_spawn = 1, + gmod_player_start = 1, + info_player_rebel = 2, + info_player_start = 3, + info_player_combine = 4 + } + + -- Include the additional spawn classes registered by Garry's Mod, while + -- retaining a preference for the spawn types used by HL2DM coop maps. + for _, className in ipairs(scripted_ents.GetMember("gmod_player_start", "SpawnPointClasses") or {}) do + spawnPriorities[className] = spawnPriorities[className] or 2 + end + + local playerHullMins = Vector(-16, -16, 0) + local playerHullMaxs = Vector(16, 16, 72) + + local function BoxesIntersect(minsA, maxsA, minsB, maxsB) + return minsA.x <= maxsB.x and maxsA.x >= minsB.x + and minsA.y <= maxsB.y and maxsA.y >= minsB.y + and minsA.z <= maxsB.z and maxsA.z >= minsB.z + end + + local function IsSpawnInsideActiveHurt(spawnpoint) + local position = spawnpoint:GetPos() + local spawnMins = position + playerHullMins + local spawnMaxs = position + playerHullMaxs + + for _, hurt in ipairs(ents.FindByClass("trigger_hurt")) do + local disabled = hurt:GetInternalVariable("m_bDisabled") + local damage = tonumber(hurt:GetInternalVariable("damage") or hurt:GetKeyValues().damage) or 0 + if disabled ~= true and disabled ~= 1 and damage > 0 then + local hurtMins, hurtMaxs = hurt:WorldSpaceAABB() + if BoxesIntersect(spawnMins, spawnMaxs, hurtMins, hurtMaxs) then return true end + end + end + + return false + end + + function GM:PlayerSelectSpawn(ply, transition) + if transition then return end + + local bestPriority + local candidates = {} + for _, spawnpoint in ipairs(ents.GetAll()) do + local priority = spawnPriorities[spawnpoint:GetClass()] + if priority and spawnpoint:IsInWorld() and not IsSpawnInsideActiveHurt(spawnpoint) then + if spawnpoint:HasSpawnFlags(1) then priority = 0 end + if not bestPriority or priority < bestPriority then + bestPriority = priority + candidates = { spawnpoint } + elseif priority == bestPriority then + candidates[#candidates + 1] = spawnpoint + end + end + end + + if #candidates == 0 then + return DefaultPlayerSelectSpawn(self, ply, transition) + end + + local lastSpawnpoint = ply:GetVar("LastSpawnpoint") + if #candidates > 1 and IsValid(lastSpawnpoint) then + table.RemoveByValue(candidates, lastSpawnpoint) + end + + local chosen = candidates[math.random(#candidates)] + self.LastSpawnPoint = chosen + ply:SetVar("LastSpawnpoint", chosen) + return chosen + end +end + local hl2dmLoadout local automaticLoadout local BuildAutomaticLoadout