Fix coop item respawns and map rotation
This commit is contained in:
@@ -14,5 +14,6 @@ include( "dynamicmovement.lua" )
|
||||
include( "mapperstuff.lua" )
|
||||
include( "noheadcrabs.lua" )
|
||||
include( "shared.lua" )
|
||||
include( "maprotation.lua" )
|
||||
|
||||
resource.AddFile( "sound/weapons/universal/pap.wav" )
|
||||
|
||||
+33
-4
@@ -1,5 +1,4 @@
|
||||
function GetRandomLevel()
|
||||
local myTable = {
|
||||
COOP_LEVELS = {
|
||||
"syn_deadsimple",
|
||||
"Poison_Coop_RE1_Beta1",
|
||||
"oc_systems2B-2",
|
||||
@@ -402,6 +401,36 @@ function GetRandomLevel()
|
||||
"cl_coop_breakable",
|
||||
"cl_coop_breakable_b2",
|
||||
"ad_coop_mara_final"
|
||||
}
|
||||
return myTable[ math.random( #myTable ) ]
|
||||
}
|
||||
|
||||
local function GetInstalledCoopLevels()
|
||||
local installedMaps = {}
|
||||
local availableLevels = {}
|
||||
|
||||
for _, fileName in ipairs(file.Find("maps/*.bsp", "GAME")) do
|
||||
installedMaps[string.lower(string.StripExtension(fileName))] = true
|
||||
end
|
||||
|
||||
for _, levelName in ipairs(COOP_LEVELS) do
|
||||
local normalizedName = string.lower(levelName)
|
||||
if installedMaps[normalizedName] and not table.HasValue(availableLevels, normalizedName) then
|
||||
table.insert(availableLevels, normalizedName)
|
||||
end
|
||||
end
|
||||
|
||||
return availableLevels
|
||||
end
|
||||
|
||||
function GetRandomLevel(excludeLevel)
|
||||
local candidates = {}
|
||||
local excluded = string.lower(excludeLevel or game.GetMap() or "")
|
||||
|
||||
for _, levelName in ipairs(GetInstalledCoopLevels()) do
|
||||
if levelName ~= excluded then
|
||||
table.insert(candidates, levelName)
|
||||
end
|
||||
end
|
||||
|
||||
if #candidates == 0 then return nil end
|
||||
return candidates[math.random(#candidates)]
|
||||
end
|
||||
|
||||
@@ -23,6 +23,18 @@ function RemoveSpeedMods()
|
||||
end
|
||||
|
||||
function MapChange(mapname)
|
||||
mapname = string.lower(tostring(mapname or ""))
|
||||
if not file.Exists("maps/" .. mapname .. ".bsp", "GAME") then
|
||||
local fallback = GetRandomLevel()
|
||||
if not fallback then
|
||||
ErrorNoHalt("[custom_coop] Cannot change map: '" .. mapname .. "' is unavailable and no fallback is installed\n")
|
||||
return false
|
||||
end
|
||||
|
||||
ErrorNoHalt("[custom_coop] Map '" .. mapname .. "' is unavailable; using " .. fallback .. " instead\n")
|
||||
mapname = fallback
|
||||
end
|
||||
|
||||
file.Write("weptranslevel.txt",string.lower(mapname))
|
||||
file.Write("weptransplayers.txt","")
|
||||
file.Write("weptransweps.txt","")
|
||||
@@ -71,8 +83,7 @@ function MapChange(mapname)
|
||||
end
|
||||
end
|
||||
timer.Simple( 2, function() RunConsoleCommand("changelevel",string.lower(mapname)) end )
|
||||
timer.Simple( 4, function() PrintMessage( HUD_PRINTTALK,"Next map not found! Download the next map!" ) end )
|
||||
timer.Simple( 7, function() RunConsoleCommand("disconnect") end )
|
||||
return true
|
||||
end
|
||||
|
||||
function LoadWeapons()
|
||||
|
||||
@@ -0,0 +1,108 @@
|
||||
local changeDelay = CreateConVar(
|
||||
"gc_mapchangedelay",
|
||||
"8",
|
||||
FCVAR_ARCHIVE,
|
||||
"Seconds between map completion and loading the next coop map"
|
||||
)
|
||||
|
||||
local maxMapTime = CreateConVar(
|
||||
"gc_mapmaxtime",
|
||||
"3600",
|
||||
FCVAR_ARCHIVE,
|
||||
"Maximum map time in seconds before rotating; 0 disables the time limit"
|
||||
)
|
||||
|
||||
local changeQueued = false
|
||||
|
||||
local function MapExists(mapName)
|
||||
if not mapName or mapName == "" then return false end
|
||||
|
||||
local wanted = string.lower(string.StripExtension(mapName))
|
||||
for _, fileName in ipairs(file.Find("maps/*.bsp", "GAME")) do
|
||||
if string.lower(string.StripExtension(fileName)) == wanted then
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
local function SetFreshNextLevel()
|
||||
local nextLevel = GetConVar("nextlevel"):GetString()
|
||||
if nextLevel == "" or not MapExists(nextLevel) or string.lower(nextLevel) == string.lower(game.GetMap()) then
|
||||
nextLevel = NextRandomLevel()
|
||||
end
|
||||
return nextLevel
|
||||
end
|
||||
|
||||
function QueueCoopMapChange(reason, requestedMap)
|
||||
if changeQueued then return end
|
||||
|
||||
local destination = requestedMap and string.lower(requestedMap) or nil
|
||||
if not MapExists(destination) or destination == string.lower(game.GetMap()) then
|
||||
destination = SetFreshNextLevel()
|
||||
end
|
||||
|
||||
if not destination then
|
||||
ErrorNoHalt("[custom_coop] Map completed, but no installed destination map is available\n")
|
||||
return
|
||||
end
|
||||
|
||||
changeQueued = true
|
||||
local delay = math.max(changeDelay:GetFloat(), 0)
|
||||
PrintMessage(HUD_PRINTTALK, "Map complete! Loading " .. destination .. " in " .. math.ceil(delay) .. " seconds.")
|
||||
print("[custom_coop] Map change queued by " .. (reason or "unknown") .. ": " .. destination)
|
||||
|
||||
timer.Create("CustomCoopChangeCompletedMap", delay, 1, function()
|
||||
RunConsoleCommand("changelevel", destination)
|
||||
end)
|
||||
end
|
||||
|
||||
hook.Add("AcceptInput", "CustomCoopDetectMapCompletion", function(ent, inputName, _, _, value)
|
||||
if not IsValid(ent) then return end
|
||||
|
||||
local className = string.lower(ent:GetClass())
|
||||
local input = string.lower(inputName or "")
|
||||
|
||||
if className == "game_end" and input == "endgame" then
|
||||
QueueCoopMapChange("game_end")
|
||||
return true
|
||||
end
|
||||
|
||||
if className ~= "point_servercommand" or input ~= "command" then return end
|
||||
|
||||
local command = string.lower(string.Trim(tostring(value or "")))
|
||||
local requestedMap = string.match(command, "^changelevel%s+[%\"]?([%w_%-%[%]!]+)")
|
||||
or string.match(command, "^map%s+[%\"]?([%w_%-%[%]!]+)")
|
||||
|
||||
if requestedMap and (not MapExists(requestedMap) or string.lower(requestedMap) == string.lower(game.GetMap())) then
|
||||
QueueCoopMapChange("point_servercommand", requestedMap)
|
||||
return true
|
||||
end
|
||||
end)
|
||||
|
||||
-- Repair old trigger_changelevel/point_changelevel entities that refer to a map
|
||||
-- which is not mounted. Valid campaign destinations remain untouched.
|
||||
hook.Add("EntityKeyValue", "CustomCoopRepairChangelevelTargets", function(ent, key, value)
|
||||
local className = string.lower(ent:GetClass())
|
||||
if className ~= "trigger_changelevel" and className ~= "point_changelevel" then return end
|
||||
if string.lower(key or "") ~= "map" then return end
|
||||
if MapExists(value) and string.lower(value) ~= string.lower(game.GetMap()) then return end
|
||||
|
||||
local replacement = GetRandomLevel()
|
||||
if replacement then
|
||||
print("[custom_coop] Replacing unavailable changelevel target '" .. tostring(value) .. "' with " .. replacement)
|
||||
return replacement
|
||||
end
|
||||
end)
|
||||
|
||||
hook.Add("InitPostEntity", "CustomCoopStartMapTimeLimit", function()
|
||||
SetFreshNextLevel()
|
||||
|
||||
local limit = maxMapTime:GetFloat()
|
||||
if limit <= 0 then return end
|
||||
|
||||
timer.Create("CustomCoopMapTimeLimit", limit, 1, function()
|
||||
QueueCoopMapChange("time limit")
|
||||
end)
|
||||
end)
|
||||
+28
-19
@@ -7,29 +7,38 @@ DeriveGamemode("sandbox")
|
||||
|
||||
include( "levels.lua" )
|
||||
|
||||
function ChangeRandomLevel(ply,com,arg)
|
||||
local randomLevel = GetRandomLevel()
|
||||
print("Changing to random level " .. randomLevel)
|
||||
RunConsoleCommand( "changelevel", randomLevel )
|
||||
end
|
||||
|
||||
function NextRandomLevel(ply,com,arg)
|
||||
local randomLevel = GetRandomLevel()
|
||||
print("Setting nextlevel to random " .. randomLevel)
|
||||
RunConsoleCommand( "nextlevel", randomLevel )
|
||||
end
|
||||
|
||||
if (SERVER) then
|
||||
function ChangeRandomLevel()
|
||||
local randomLevel = GetRandomLevel()
|
||||
if not randomLevel then
|
||||
ErrorNoHalt("[custom_coop] No other installed coop map was found in levels.lua\n")
|
||||
return false
|
||||
end
|
||||
|
||||
print("[custom_coop] Changing to random level " .. randomLevel)
|
||||
RunConsoleCommand("changelevel", randomLevel)
|
||||
return true
|
||||
end
|
||||
|
||||
function NextRandomLevel()
|
||||
local randomLevel = GetRandomLevel()
|
||||
if not randomLevel then
|
||||
ErrorNoHalt("[custom_coop] Cannot set nextlevel: no other installed coop map was found\n")
|
||||
return nil
|
||||
end
|
||||
|
||||
print("[custom_coop] Setting nextlevel to " .. randomLevel)
|
||||
RunConsoleCommand("nextlevel", randomLevel)
|
||||
return randomLevel
|
||||
end
|
||||
|
||||
concommand.Add("randomlevel", ChangeRandomLevel)
|
||||
concommand.Add("randomnextlevel", NextRandomLevel)
|
||||
end
|
||||
|
||||
hook.Add( "Initialize", "SetupRandomLevels", function ()
|
||||
print("hatschi2")
|
||||
timer.Simple( 3600, ChangeRandomLevel )
|
||||
NextRandomLevel()
|
||||
timer.Simple( 0, NextRandomLevel )
|
||||
end)
|
||||
hook.Add("Initialize", "SetupRandomLevels", function()
|
||||
timer.Simple(0, NextRandomLevel)
|
||||
end)
|
||||
end
|
||||
|
||||
-- timer.Simple(
|
||||
-- 1,
|
||||
|
||||
+152
-170
@@ -1,191 +1,173 @@
|
||||
CreateConVar("gc_respawndistance", 75, SERVER_CAN_EXECUTE, "Item distance to map origin to trigger a respawn")
|
||||
local respawnDistance = CreateConVar(
|
||||
"gc_respawndistance",
|
||||
"75",
|
||||
FCVAR_ARCHIVE,
|
||||
"Distance a map item may move from its spawn before it is returned"
|
||||
)
|
||||
|
||||
hook.Add("PlayerSpawn", "SpawningInitializeAmmo", function(ply)
|
||||
timer.Simple(0, function()
|
||||
ply:ShouldDropWeapon(false)
|
||||
local respawnDelay = CreateConVar(
|
||||
"gc_itemrespawntime",
|
||||
"1",
|
||||
FCVAR_ARCHIVE,
|
||||
"Seconds before a collected or displaced map item respawns"
|
||||
)
|
||||
|
||||
local trackedItems = {}
|
||||
local suppressRespawns = false
|
||||
|
||||
local function IsRespawnable(ent)
|
||||
if not IsValid(ent) then return false end
|
||||
|
||||
local className = ent:GetClass()
|
||||
return string.StartWith(className, "item_") or string.StartWith(className, "weapon_")
|
||||
end
|
||||
|
||||
local function CaptureTemplate(ent)
|
||||
local bodygroups = {}
|
||||
for _, bodygroup in ipairs(ent:GetBodyGroups() or {}) do
|
||||
bodygroups[bodygroup.id] = ent:GetBodygroup(bodygroup.id)
|
||||
end
|
||||
|
||||
return {
|
||||
className = ent:GetClass(),
|
||||
position = ent:GetPos(),
|
||||
angles = ent:GetAngles(),
|
||||
model = ent:GetModel(),
|
||||
skin = ent:GetSkin(),
|
||||
material = ent:GetMaterial(),
|
||||
color = ent:GetColor(),
|
||||
collisionGroup = ent:GetCollisionGroup(),
|
||||
name = ent:GetName(),
|
||||
keyValues = ent:GetKeyValues(),
|
||||
bodygroups = bodygroups
|
||||
}
|
||||
end
|
||||
|
||||
local TrackItem
|
||||
local RespawnItem
|
||||
|
||||
local function ScheduleRespawn(template)
|
||||
if suppressRespawns then return end
|
||||
|
||||
timer.Simple(math.max(respawnDelay:GetFloat(), 0), function()
|
||||
if suppressRespawns then return end
|
||||
RespawnItem(template)
|
||||
end)
|
||||
end)
|
||||
end
|
||||
|
||||
hook.Add( "OnEntityCreated", "Respawn items", function( ent )
|
||||
if not ent:IsValid() then
|
||||
--print("Ignoring invalid item")
|
||||
return
|
||||
end
|
||||
TrackItem = function(ent, template)
|
||||
if not IsValid(ent) or trackedItems[ent] then return end
|
||||
|
||||
local entityClass = ent:GetClass()
|
||||
template = template or CaptureTemplate(ent)
|
||||
trackedItems[ent] = template
|
||||
|
||||
if not string.StartWith(entityClass, "item_") and not string.StartWith(entityClass, "weapon_") then
|
||||
--print("Ignoring not-item " .. entityClass)
|
||||
return
|
||||
end
|
||||
ent:CallOnRemove("CustomCoopRespawnItem", function(removed)
|
||||
local savedTemplate = trackedItems[removed]
|
||||
trackedItems[removed] = nil
|
||||
|
||||
timer.Simple(0, function()
|
||||
if not ent:IsValid() then
|
||||
-- print("Ignoring invalid item " .. entityClass .. " (from timer)")
|
||||
return
|
||||
if savedTemplate then
|
||||
ScheduleRespawn(savedTemplate)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
local entityClass = ent:GetClass()
|
||||
local entityPos = ent:GetPos()
|
||||
local entityAngles = ent:GetAngles()
|
||||
local entityModel = ent:GetModel()
|
||||
local entityCollisionGroup = ent:GetCollisionGroup()
|
||||
local entityCreatedByMap = ent:CreatedByMap()
|
||||
|
||||
ent:SetNWBool("blockPickup", true)
|
||||
ent:SetNWVector("respawnPos", entityPos)
|
||||
ent:SetNWAngle("respawnAngle", entityAngle)
|
||||
|
||||
ent:CallOnRemove("Respawn", function(ent)
|
||||
-- print("item " .. entityClass .. " removed!")
|
||||
|
||||
timer.Simple(1, function()
|
||||
if (entityCreatedByMap) then
|
||||
entityPos = entityPos + Vector(0, 0, 3)
|
||||
end
|
||||
|
||||
local ent = ents.Create(entityClass)
|
||||
ent:SetPos(entityPos)
|
||||
ent:SetAngles(entityAngles)
|
||||
ent:SetModel(entityModel)
|
||||
ent:SetCollisionGroup(entityCollisionGroup)
|
||||
ent:Spawn()
|
||||
ent:EmitSound("items/suitchargeok1.wav")
|
||||
|
||||
-- print("item " .. entityClass .. " recreated!")
|
||||
end)
|
||||
RespawnItem = function(template)
|
||||
local ent = ents.Create(template.className)
|
||||
if not IsValid(ent) then
|
||||
ErrorNoHalt("[custom_coop] Could not respawn " .. template.className .. "; retrying in 5 seconds\n")
|
||||
timer.Simple(5, function()
|
||||
if not suppressRespawns then RespawnItem(template) end
|
||||
end)
|
||||
return
|
||||
end
|
||||
|
||||
local callbackId = ent:AddCallback( "OnAngleChange", function( entity, newangle )
|
||||
local distance = entity:GetPos():Distance(entityPos)
|
||||
|
||||
if (distance < GetConVarNumber("gc_respawndistance")) then
|
||||
return
|
||||
end
|
||||
|
||||
local callbackId = entity:GetNWInt("callbackId")
|
||||
entity:RemoveCallback( "OnAngleChange", callbackId )
|
||||
|
||||
-- print("item " .. entityClass .. " distance: " .. distance .. " callbackId: " .. callbackId)
|
||||
|
||||
timer.Simple(1, function()
|
||||
if not entity:IsValid() then
|
||||
return
|
||||
end
|
||||
entity:Remove()
|
||||
end)
|
||||
end )
|
||||
|
||||
ent:SetNWInt("callbackId", callbackId)
|
||||
|
||||
-- print("Map item processed: " .. ent:GetClass() .. " pos: " .. entityPos.x .. "," .. entityPos.y .. "," .. entityPos.z)
|
||||
end)
|
||||
end )
|
||||
|
||||
hook.Add("PlayerCanPickupWeapon", "DontPickupIfAlreadyFull", function (ply, weapon)
|
||||
local className = weapon:GetClass()
|
||||
|
||||
for k, v in pairs(ply:GetWeapons()) do
|
||||
if (v:GetClass() == className) then
|
||||
local primaryAmmoType = weapon:GetPrimaryAmmoType()
|
||||
local secondaryAmmoType = weapon:GetSecondaryAmmoType()
|
||||
local primaryAmmoCount = ply:GetAmmoCount(primaryAmmoType)
|
||||
local secondaryAmmoCount = ply:GetAmmoCount(secondaryAmmoType)
|
||||
local vClip1 = v:Clip1()
|
||||
local vClip2 = v:Clip2()
|
||||
local vMaxClip1 = v:GetMaxClip1()
|
||||
local vMaxClip2 = v:GetMaxClip2()
|
||||
|
||||
if ply:KeyDown(IN_USE) then
|
||||
ply:ChatPrint(className .. ": vClip1:" .. vClip1 .. " maxClip1:" .. vMaxClip1 .. " vClip2:" .. vClip2.. " maxClip2:" .. vMaxClip2 ..
|
||||
" playerClip1:" .. primaryAmmoCount .. " playerClip2:" .. secondaryAmmoCount ..
|
||||
" wClip1:" .. weapon:Clip1() .. " wClip2:" .. weapon:Clip2())
|
||||
end
|
||||
|
||||
local blacklist = {
|
||||
"weapon_crowbar",
|
||||
"weapon_fists",
|
||||
"weapon_frag",
|
||||
"weapon_rpg"
|
||||
}
|
||||
|
||||
local whitelist = {
|
||||
"weapon_pistol",
|
||||
"weapon_357"
|
||||
}
|
||||
|
||||
if (table.HasValue(blacklist, className)) then
|
||||
local gave = false
|
||||
if (vMaxClip1 < 0 and primaryAmmoCount < 15) then
|
||||
local amountToGive = 15 - primaryAmmoCount
|
||||
ply:GiveAmmo(amountToGive, primaryAmmoType)
|
||||
--ply:ChatPrint(className .. " give1 " .. amountToGive .. " primary (" .. vClip1 .. "/" .. primaryAmmoCount .. "/" .. vMaxClip1 .. ")")
|
||||
gave = true
|
||||
end
|
||||
if (vMaxClip2 < 0 and secondaryAmmoCount < 15) then
|
||||
local amountToGive = 15 - secondaryAmmoCount
|
||||
ply:GiveAmmo(amountToGive, secondaryAmmoType)
|
||||
--ply:ChatPrint(className .. " give1 " .. amountToGive .. " secondary (" .. vClip2 .. "/" .. secondaryAmmoCount .. "/" .. vMaxClip2 .. ")")
|
||||
gave = true
|
||||
end
|
||||
if gave then
|
||||
return table.HasValue(whitelist, className)
|
||||
end
|
||||
end
|
||||
|
||||
if primaryAmmoCount >= vMaxClip1 and secondaryAmmoCount >= vMaxClip2 then
|
||||
return false
|
||||
else
|
||||
if primaryAmmoCount < vMaxClip1 then
|
||||
ply:GiveAmmo(math.min(weapon:Clip1(), vMaxClip1 - primaryAmmoCount), primaryAmmoType)
|
||||
--ply:ChatPrint(className .. " give2 " .. math.min(weapon:Clip1(), vMaxClip1 - primaryAmmoCount) .. " primary")
|
||||
end
|
||||
if secondaryAmmoCount < vMaxClip2 then
|
||||
ply:GiveAmmo(math.min(weapon:Clip2(), vMaxClip2 - secondaryAmmoCount), secondaryAmmoType)
|
||||
--ply:ChatPrint(className .. " give2 " .. math.min(weapon:Clip2(), vMaxClip2 - secondaryAmmoCount) .. " secondary")
|
||||
end
|
||||
|
||||
return table.HasValue(whitelist, className)
|
||||
end
|
||||
-- Restore mapper-provided properties before Spawn. Position, angles and model
|
||||
-- are restored explicitly because GetKeyValues does not always include them.
|
||||
for key, value in pairs(template.keyValues or {}) do
|
||||
if key ~= "classname" and key ~= "origin" and key ~= "angles" and key ~= "hammerid" then
|
||||
ent:SetKeyValue(key, tostring(value))
|
||||
end
|
||||
end
|
||||
|
||||
return true
|
||||
end)
|
||||
ent:SetPos(template.position + Vector(0, 0, 3))
|
||||
ent:SetAngles(template.angles)
|
||||
if template.model and template.model ~= "" then ent:SetModel(template.model) end
|
||||
if template.name and template.name ~= "" then ent:SetName(template.name) end
|
||||
ent:Spawn()
|
||||
ent:Activate()
|
||||
|
||||
hook.Add( "WeaponEquip", "RespawnOnCollected", function(weapon, ply)
|
||||
-- print("WeaponEquip " .. weapon:GetClass())
|
||||
if not IsValid(ent) then
|
||||
ScheduleRespawn(template)
|
||||
return
|
||||
end
|
||||
|
||||
local entityClass = weapon:GetClass()
|
||||
local entityPos = weapon:GetNWVector("respawnPos")
|
||||
local entityAngles = weapon:GetNWAngle("respawnAngle")
|
||||
local entityModel = weapon:GetModel()
|
||||
local entityCollisionGroup = weapon:GetCollisionGroup()
|
||||
local entityCreatedByMap = weapon:CreatedByMap()
|
||||
ent:SetSkin(template.skin or 0)
|
||||
ent:SetMaterial(template.material or "")
|
||||
ent:SetColor(template.color or color_white)
|
||||
ent:SetCollisionGroup(template.collisionGroup or COLLISION_GROUP_NONE)
|
||||
for id, value in pairs(template.bodygroups or {}) do
|
||||
ent:SetBodygroup(id, value)
|
||||
end
|
||||
|
||||
timer.Simple(1, function()
|
||||
if (entityCreatedByMap) then
|
||||
entityPos = entityPos + Vector(0, 0, 3)
|
||||
TrackItem(ent, template)
|
||||
ent:EmitSound("items/suitchargeok1.wav", 60, 100, 0.35)
|
||||
end
|
||||
|
||||
hook.Add("OnEntityCreated", "CustomCoopTrackMapItems", function(ent)
|
||||
-- OnEntityCreated fires before keyvalues and map-created state are ready.
|
||||
timer.Simple(0, function()
|
||||
if IsRespawnable(ent) and ent:CreatedByMap() then
|
||||
TrackItem(ent)
|
||||
end
|
||||
|
||||
local ent = ents.Create(entityClass)
|
||||
ent:SetPos(entityPos)
|
||||
ent:SetAngles(entityAngles)
|
||||
ent:SetModel(entityModel)
|
||||
ent:SetCollisionGroup(entityCollisionGroup)
|
||||
ent:Spawn()
|
||||
ent:EmitSound("items/suitchargeok1.wav")
|
||||
|
||||
-- print("item " .. entityClass .. " recreated!")
|
||||
end)
|
||||
|
||||
--if (weapon:GetClass() == "weapon_crowbar") then
|
||||
-- if (ply:HasWeapon("weapon_fists")) then
|
||||
-- ply:StripWeapon("weapon_fists")
|
||||
-- end
|
||||
--end
|
||||
end)
|
||||
|
||||
hook.Add("AllowPlayerPickup", "some_unique_name", function (ply, ent)
|
||||
return not ent:GetNWBool("blockPickup")
|
||||
hook.Add("WeaponEquip", "CustomCoopRespawnCollectedWeapon", function(weapon)
|
||||
local template = trackedItems[weapon]
|
||||
if not template then return end
|
||||
|
||||
-- A newly acquired weapon becomes the player's inventory entity and is not
|
||||
-- removed. Detach it and immediately start a new respawn cycle for its slot.
|
||||
trackedItems[weapon] = nil
|
||||
weapon:RemoveCallOnRemove("CustomCoopRespawnItem")
|
||||
ScheduleRespawn(template)
|
||||
end)
|
||||
|
||||
-- Prevent +use from dragging a map pickup away. Touch pickup still works, and
|
||||
-- duplicate weapons are left to the engine's EquipAmmo path so they correctly
|
||||
-- replenish reserve ammo and disappear only when ammo was actually accepted.
|
||||
hook.Add("AllowPlayerPickup", "CustomCoopProtectMapItems", function(_, ent)
|
||||
if trackedItems[ent] then return false end
|
||||
end)
|
||||
|
||||
timer.Create("CustomCoopReturnDisplacedItems", 1, 0, function()
|
||||
local maxDistance = math.max(respawnDistance:GetFloat(), 0)
|
||||
local maxDistanceSquared = maxDistance * maxDistance
|
||||
|
||||
for ent, template in pairs(trackedItems) do
|
||||
if not IsValid(ent) then
|
||||
trackedItems[ent] = nil
|
||||
elseif not IsValid(ent:GetParent()) and ent:GetPos():DistToSqr(template.position) > maxDistanceSquared then
|
||||
SafeRemoveEntityDelayed(ent, 0)
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
hook.Add("PlayerSpawn", "CustomCoopKeepWeaponsOnDeath", function(ply)
|
||||
timer.Simple(0, function()
|
||||
if IsValid(ply) then ply:ShouldDropWeapon(false) end
|
||||
end)
|
||||
end)
|
||||
|
||||
hook.Add("PreCleanupMap", "CustomCoopPauseItemRespawns", function()
|
||||
suppressRespawns = true
|
||||
trackedItems = {}
|
||||
end)
|
||||
|
||||
hook.Add("PostCleanupMap", "CustomCoopResumeItemRespawns", function()
|
||||
suppressRespawns = false
|
||||
end)
|
||||
|
||||
hook.Add("ShutDown", "CustomCoopStopItemRespawns", function()
|
||||
suppressRespawns = true
|
||||
end)
|
||||
|
||||
Reference in New Issue
Block a user