Fix coop weapon pickup limits
This commit is contained in:
+97
-17
@@ -15,6 +15,30 @@ local respawnDelay = CreateConVar(
|
||||
local trackedItems = {}
|
||||
local suppressRespawns = false
|
||||
|
||||
-- Sandbox defaults this to 9999, overriding every Source ammo type's intended
|
||||
-- carry limit. Coop uses the original per-ammo limits instead.
|
||||
local maxAmmoOverride = GetConVar("gmod_maxammo")
|
||||
if maxAmmoOverride and maxAmmoOverride:GetInt() > 0 then
|
||||
RunConsoleCommand("gmod_maxammo", "0")
|
||||
end
|
||||
|
||||
local function ClampPlayerAmmo(ply)
|
||||
for ammoType = 1, 255 do
|
||||
local maximum = game.GetAmmoMax(ammoType)
|
||||
if maximum and maximum >= 0 and ply:GetAmmoCount(ammoType) > maximum then
|
||||
ply:SetAmmo(maximum, ammoType)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Also repairs reserves accumulated while the sandbox-wide 9999 override was
|
||||
-- active. Unknown and explicitly infinite ammo types are left unchanged.
|
||||
timer.Simple(0, function()
|
||||
for _, ply in ipairs(player.GetAll()) do
|
||||
if IsValid(ply) then ClampPlayerAmmo(ply) end
|
||||
end
|
||||
end)
|
||||
|
||||
-- These use the item_ prefix but are map machinery or containers rather than
|
||||
-- touch pickups. Ammo crates are already infinite; the others may be removed
|
||||
-- deliberately by Hammer I/O and must not be recreated as loose supplies.
|
||||
@@ -42,16 +66,18 @@ local itemAmmoTypes = {
|
||||
item_rpg_round = "RPG_Round"
|
||||
}
|
||||
|
||||
-- These weapons store their ammunition directly on the player rather than in
|
||||
-- a clip, so Clip1() cannot tell us how much a duplicate pickup is worth.
|
||||
local cliplessWeaponAmmo = {
|
||||
weapon_frag = 1,
|
||||
weapon_rpg = 1,
|
||||
weapon_slam = 1
|
||||
}
|
||||
|
||||
local function CanReceiveAmmo(ply, ammoType)
|
||||
if ammoType == nil or ammoType < 0 then return false end
|
||||
|
||||
local maximum
|
||||
local maxAmmoOverride = GetConVar("gmod_maxammo")
|
||||
if maxAmmoOverride and maxAmmoOverride:GetInt() > 0 then
|
||||
maximum = maxAmmoOverride:GetInt()
|
||||
else
|
||||
maximum = game.GetAmmoMax(ammoType)
|
||||
end
|
||||
local maximum = game.GetAmmoMax(ammoType)
|
||||
|
||||
-- Unknown/custom ammo types are left to their own pickup implementation.
|
||||
if maximum == nil or maximum < 0 then return true end
|
||||
@@ -74,6 +100,8 @@ local function CaptureTemplate(ent)
|
||||
bodygroups[bodygroup.id] = ent:GetBodygroup(bodygroup.id)
|
||||
end
|
||||
|
||||
local isWeapon = ent:IsWeapon()
|
||||
|
||||
return {
|
||||
className = ent:GetClass(),
|
||||
position = ent:GetPos(),
|
||||
@@ -85,7 +113,9 @@ local function CaptureTemplate(ent)
|
||||
collisionGroup = ent:GetCollisionGroup(),
|
||||
name = ent:GetName(),
|
||||
keyValues = ent:GetKeyValues(),
|
||||
bodygroups = bodygroups
|
||||
bodygroups = bodygroups,
|
||||
clip1 = isWeapon and ent:Clip1() or nil,
|
||||
clip2 = isWeapon and ent:Clip2() or nil
|
||||
}
|
||||
end
|
||||
|
||||
@@ -104,8 +134,11 @@ end
|
||||
TrackItem = function(ent, template)
|
||||
if not IsValid(ent) or trackedItems[ent] then return end
|
||||
|
||||
template = template or CaptureTemplate(ent)
|
||||
template = template or ent.CustomCoopRespawnTemplate or CaptureTemplate(ent)
|
||||
trackedItems[ent] = template
|
||||
-- Lua entity fields survive script auto-refresh. Keeping the template on
|
||||
-- the entity lets a fresh copy of this file recover its original spawn.
|
||||
ent.CustomCoopRespawnTemplate = template
|
||||
|
||||
ent:CallOnRemove("CustomCoopRespawnItem", function(removed)
|
||||
local savedTemplate = trackedItems[removed]
|
||||
@@ -147,6 +180,11 @@ RespawnItem = function(template)
|
||||
return
|
||||
end
|
||||
|
||||
-- Spawn normally fills these from the weapon script. Restore the captured
|
||||
-- values as well so mapper-specified pickup quantities survive recreation.
|
||||
if template.clip1 ~= nil then ent:SetClip1(template.clip1) end
|
||||
if template.clip2 ~= nil then ent:SetClip2(template.clip2) end
|
||||
|
||||
ent:SetSkin(template.skin or 0)
|
||||
ent:SetMaterial(template.material or "")
|
||||
ent:SetColor(template.color or color_white)
|
||||
@@ -168,6 +206,19 @@ hook.Add("OnEntityCreated", "CustomCoopTrackMapItems", function(ent)
|
||||
end)
|
||||
end)
|
||||
|
||||
local function TrackExistingItems()
|
||||
for _, ent in ipairs(ents.GetAll()) do
|
||||
if IsRespawnable(ent) and (ent:CreatedByMap() or ent.CustomCoopRespawnTemplate) then
|
||||
TrackItem(ent)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- InitPostEntity handles a normal map load; the zero-delay scan handles GMod's
|
||||
-- script auto-refresh, where existing entities do not fire OnEntityCreated.
|
||||
hook.Add("InitPostEntity", "CustomCoopTrackExistingMapItems", TrackExistingItems)
|
||||
timer.Simple(0, TrackExistingItems)
|
||||
|
||||
hook.Add("WeaponEquip", "CustomCoopRespawnCollectedWeapon", function(weapon)
|
||||
local template = trackedItems[weapon]
|
||||
if not template then return end
|
||||
@@ -179,17 +230,46 @@ hook.Add("WeaponEquip", "CustomCoopRespawnCollectedWeapon", function(weapon)
|
||||
ScheduleRespawn(template)
|
||||
end)
|
||||
|
||||
-- A duplicate weapon is removed through WEAPON:EquipAmmo even when the player
|
||||
-- cannot accept more ammo. Refusing only the useless attempt leaves the pickup
|
||||
-- available and silent until this player spends ammunition (or someone else
|
||||
-- who needs it touches it).
|
||||
-- Duplicate HL2 weapons do not transfer ammo consistently in GMod. Handle
|
||||
-- tracked map weapons here so useful ammo is transferred exactly once, while
|
||||
-- a full player leaves the pickup available and silent for someone else.
|
||||
hook.Add("PlayerCanPickupWeapon", "CustomCoopIgnoreFullDuplicateWeapons", function(ply, weapon)
|
||||
if not trackedItems[weapon] or not ply:HasWeapon(weapon:GetClass()) then return end
|
||||
if not ply:HasWeapon(weapon:GetClass()) then return end
|
||||
|
||||
if not CanReceiveAmmo(ply, weapon:GetPrimaryAmmoType())
|
||||
and not CanReceiveAmmo(ply, weapon:GetSecondaryAmmoType()) then
|
||||
return false
|
||||
local primaryAmmoType = weapon:GetPrimaryAmmoType()
|
||||
local secondaryAmmoType = weapon:GetSecondaryAmmoType()
|
||||
|
||||
-- Some map logic creates weapons dynamically, so they are not part of the
|
||||
-- map-entity tracker. A second copy of an ammo-less weapon is still always
|
||||
-- useless and should never produce another pickup event.
|
||||
if primaryAmmoType < 0 and secondaryAmmoType < 0 then return false end
|
||||
|
||||
if not trackedItems[weapon] then return end
|
||||
|
||||
local primaryAmount = math.max(weapon:Clip1(), 0)
|
||||
if primaryAmount == 0 then
|
||||
primaryAmount = cliplessWeaponAmmo[weapon:GetClass()] or 0
|
||||
end
|
||||
|
||||
local given = 0
|
||||
if primaryAmmoType >= 0 and primaryAmount > 0 then
|
||||
given = given + (ply:GiveAmmo(primaryAmount, primaryAmmoType) or 0)
|
||||
end
|
||||
|
||||
local secondaryAmount = math.max(weapon:Clip2(), 0)
|
||||
if secondaryAmmoType >= 0 and secondaryAmount > 0 then
|
||||
given = given + (ply:GiveAmmo(secondaryAmount, secondaryAmmoType) or 0)
|
||||
end
|
||||
|
||||
-- Handle the duplicate ourselves. The native path is inconsistent for
|
||||
-- HL2 weapons under GMod: it may drain or hide the entity without giving
|
||||
-- usable reserve ammo. Removing only after GiveAmmo succeeds also makes a
|
||||
-- full pickup silent and leaves it available to another player.
|
||||
if given > 0 then
|
||||
SafeRemoveEntityDelayed(weapon, 0)
|
||||
end
|
||||
|
||||
return false
|
||||
end)
|
||||
|
||||
-- Stock ammo items normally reject a full player themselves. Checking here as
|
||||
|
||||
Reference in New Issue
Block a user