Clear crippled and near-death enemies, various optimizations

This commit is contained in:
toasterpanic 2026-05-26 23:21:18 -04:00
parent 8da450edb3
commit 85eb62a495
3 changed files with 115 additions and 72 deletions

View file

@ -44,6 +44,12 @@ local function PopulatePerformanceSBXToolMenu(pnl)
pnl:CheckBox("Never remove corpses near the player", "zcnpci_disable_corpse_removal_near_player") pnl:CheckBox("Never remove corpses near the player", "zcnpci_disable_corpse_removal_near_player")
pnl:ControlHelp("If on, corpses under the variable above in distance will never be cleared.") pnl:ControlHelp("If on, corpses under the variable above in distance will never be cleared.")
pnl:CheckBox("Treat crippled NPCs as dead", "zcnpci_treat_crippled_as_dead")
pnl:ControlHelp("If on, corpses with amputations or both legs broken will be clearable.")
pnl:CheckBox("Treat near-death NPCs as dead", "zcnpci_treat_near_death_as_dead")
pnl:ControlHelp("If on, corpses near death (severe blood loss, comas, etc.) will be clearable.")
end end
if engine.ActiveGamemode() == "sandbox" then if engine.ActiveGamemode() == "sandbox" then

View file

@ -9,6 +9,10 @@ local max_corpses = CreateConVar("zcnpci_max_corpses", 8, bit.bor(FCVAR_ARC
local max_corpse_time = CreateConVar("zcnpci_max_corpse_time", 120, bit.bor(FCVAR_ARCHIVE, FCVAR_REPLICATED)) local max_corpse_time = CreateConVar("zcnpci_max_corpse_time", 120, bit.bor(FCVAR_ARCHIVE, FCVAR_REPLICATED))
local max_corpse_distance = CreateConVar("zcnpci_max_corpse_distance", 2500, bit.bor(FCVAR_ARCHIVE, FCVAR_REPLICATED)) local max_corpse_distance = CreateConVar("zcnpci_max_corpse_distance", 2500, bit.bor(FCVAR_ARCHIVE, FCVAR_REPLICATED))
local no_corpse_removal_near_player = CreateConVar("zcnpci_disable_corpse_removal_near_player", 0, bit.bor(FCVAR_ARCHIVE, FCVAR_REPLICATED)) local no_corpse_removal_near_player = CreateConVar("zcnpci_disable_corpse_removal_near_player", 0, bit.bor(FCVAR_ARCHIVE, FCVAR_REPLICATED))
local treat_crippled_as_dead = CreateConVar("zcnpci_treat_crippled_as_dead", 1, bit.bor(FCVAR_ARCHIVE, FCVAR_REPLICATED))
local treat_near_death_as_dead = CreateConVar("zcnpci_treat_near_death_as_dead", 1, bit.bor(FCVAR_ARCHIVE, FCVAR_REPLICATED))
local debug_ragdoll_all = CreateConVar("zcnpci_debug_ragdoll_all", 0, bit.bor(FCVAR_ARCHIVE, FCVAR_REPLICATED, FCVAR_PROTECTED))
local last_dmgpos = {} local last_dmgpos = {}
local last_hitgroup = {} local last_hitgroup = {}
@ -16,6 +20,9 @@ local last_attacker = {}
local corpses = {} local corpses = {}
local last_corpse_tick = CurTime()
local last_tick = CurTime()
hook.Add("CreateEntityRagdoll", "zcnpci", function(ent, ragdoll) hook.Add("CreateEntityRagdoll", "zcnpci", function(ent, ragdoll)
if !ent.organism then return end if !ent.organism then return end
@ -83,79 +90,107 @@ hook.Add("ScaleNPCDamage", "zcnpci", function(npc, hitgroup, dmginfo)
end) end)
hook.Add("Think", "zcnpci", function() hook.Add("Think", "zcnpci", function()
for i, ent in pairs(corpses) do local do_corpse_loop = (CurTime() - last_corpse_tick) > 2.0
if !IsValid(ent) then local do_general_loop = (CurTime() - last_tick) > 0.1
table.RemoveByValue(corpses, ent)
end
end
if (max_corpses:GetFloat() != -1) and (#corpses > max_corpses:GetFloat()) then if do_general_loop then
if IsValid(corpses[1]) then if do_corpse_loop then
corpses[1]:Remove() last_corpse_tick = CurTime()
end
table.remove(corpses, 1) for i, ent in pairs(corpses) do
end if !IsValid(ent) then
table.RemoveByValue(corpses, ent)
for i, ent in pairs(ents.GetAll()) do
if !IsValid(ent) then continue end
if !ent.organism then continue end
if ent:IsNPC() then
-- Knock them down if something is off
if (
((ent.organism.lleg >= 1) and (ent.organism.rleg >= 1)) or
ent.organism.llegamputated or
ent.organism.rlegamputated or
(ent.organism.consciousness <= 0.3) or
(ent.organism.pain > 90) or
ent:IsOnFire()
) then
local damage_info = DamageInfo()
damage_info:SetDamage(ent:Health())
damage_info:SetAttacker(ent)
damage_info:SetDamageType( DMG_DIRECT )
damage_info:SetDamageForce(Vector(0, 0, 0))
ent:TakeDamageInfo(damage_info)
end
elseif !ent.organism.alive and remove_corpses and ent.is_npc_corpse then
-- Add to corpse list if not there already
if !table.HasValue(corpses, ent) then
table.insert(corpses, ent)
end
-- Get nearest player for future checks
local nearby_player
local players = player.GetAll()
for i, loop_player in pairs(players) do
local player_position = loop_player:GetPos()
local distance = ent:GetPos():Distance(player_position)
if (distance <= max_corpse_distance:GetFloat()) or (max_corpse_distance:GetFloat() == -1) then
nearby_player = loop_player
break
end end
end end
if nearby_player and IsValid(nearby_player) and no_corpse_removal_near_player:GetBool() then continue if (max_corpses:GetFloat() != -1) and (#corpses > max_corpses:GetFloat()) then
elseif (max_corpse_distance:GetFloat() != -1) and !nearby_player then if IsValid(corpses[1]) then
ent:Remove() corpses[1]:Remove()
continue end
end
if max_corpse_time:GetFloat() != -1 then table.remove(corpses, 1)
if !ent.corpse_timestamp then ent.corpse_timestamp = CurTime() end
elseif (CurTime() - ent.corpse_timestamp) > max_corpse_time:GetFloat() then end
for i, ent in pairs(ents.GetAll()) do
if !IsValid(ent) then continue end
if !ent.organism then continue end
if ent:IsNPC() then
-- Knock them down if something is off
if (
((ent.organism.lleg >= 1) and (ent.organism.rleg >= 1)) or
ent.organism.llegamputated or
ent.organism.rlegamputated or
(ent.organism.consciousness <= 0.3) or
(ent.organism.pain > 90) or
ent:IsOnFire() or
debug_ragdoll_all:GetBool()
) then
local damage_info = DamageInfo()
damage_info:SetDamage(ent:Health())
damage_info:SetAttacker(ent)
damage_info:SetDamageType( DMG_DIRECT )
damage_info:SetDamageForce(Vector(0, 0, 0))
ent:TakeDamageInfo(damage_info)
end
elseif do_corpse_loop and remove_corpses and ent.is_npc_corpse then
local clearable = false
if !ent.organism.alive then clearable = true end
if treat_crippled_as_dead:GetBool() and (
ent.organism.llegamputated or ent.organism.rlegamputated or ent.organism.larmamputated or ent.organism.rarmamputated or
((ent.organism.lleg >= 1) and (ent.organism.rleg >= 1))
) then clearable = true end
if treat_near_death_as_dead:GetBool() and (
ent.organism.heartstop or
(ent.organism.brain > 0.6) or
!ent.organism.lungsfunction or
(ent.organism.blood < 3000) or
(ent.organism.pulse < 10)
) then clearable = true end
if !clearable then return end
-- Add to corpse list if not there already
if !table.HasValue(corpses, ent) then
table.insert(corpses, ent)
end
-- Get nearest player for future checks
local nearby_player
local players = player.GetAll()
for i, loop_player in pairs(players) do
local player_position = loop_player:GetPos()
local distance = ent:GetPos():Distance(player_position)
if (distance <= max_corpse_distance:GetFloat()) or (max_corpse_distance:GetFloat() == -1) then
nearby_player = loop_player
break
end
end
if nearby_player and IsValid(nearby_player) and no_corpse_removal_near_player:GetBool() then continue
elseif (max_corpse_distance:GetFloat() != -1) and !nearby_player then
ent:Remove() ent:Remove()
continue continue
end end
print((CurTime() - ent.corpse_timestamp) > max_corpse_time:GetFloat())
if max_corpse_time:GetFloat() != -1 then
if !ent.corpse_timestamp then ent.corpse_timestamp = CurTime()
elseif (CurTime() - ent.corpse_timestamp) > max_corpse_time:GetFloat() then
ent:Remove()
continue
end
print((CurTime() - ent.corpse_timestamp) > max_corpse_time:GetFloat())
end
end end
end end
end end
end) end)
--[[hook.Add("OnNPCKilled", "Fedhoria", function(ent, attacker, inflictor) --[[hook.Add("OnNPCKilled", "Fedhoria", function(ent, attacker, inflictor)

View file

@ -161,6 +161,8 @@ function MODULE:Init()
self.StartDie = nil self.StartDie = nil
self.AnimationRollEndTime = 0 self.AnimationRollEndTime = 0
self.StopProcessing = false self.StopProcessing = false
self.LastThink = CurTime()
self.LastPhysProcess = CurTime()
-- Add damping to all bones. -- Add damping to all bones.
local phys = self:GetPhysicsObject() local phys = self:GetPhysicsObject()
@ -184,14 +186,14 @@ end
---------------------------------------------------------------------------]] ---------------------------------------------------------------------------]]
function MODULE:Think() function MODULE:Think()
if (CurTime() - self.LastThink) < 1 then return end
if !IsValid(target) then return end if !IsValid(target) then return end
print("target is valid") self.LastThink = CurTime()
local phys = target:GetPhysicsObject() local phys = target:GetPhysicsObject()
if !IsValid(phys) or !phys:IsAsleep() then return end
print("phys is valid") if !IsValid(phys) or !phys:IsAsleep() then return end
phys:Wake() phys:Wake()
end end
@ -237,9 +239,11 @@ end
Physics Simulation Hook (FIXED) Physics Simulation Hook (FIXED)
---------------------------------------------------------------------------]] ---------------------------------------------------------------------------]]
function MODULE:PhysicsSimulate(phys, dt) function MODULE:PhysicsSimulate(phys, dt)
phys:Wake()
if self.StopProcessing then return false end if self.StopProcessing then return false end
--print((CurTime() - self.LastPhysProcess) < 0.05)
--if (CurTime() - self.LastPhysProcess) < (1 /) then return false end
self.LastPhysProcess = CurTime()
local cur_time = CurTime() local cur_time = CurTime()
local target = self:GetTarget() local target = self:GetTarget()
@ -357,10 +361,8 @@ function MODULE:PhysicsSimulate(phys, dt)
local offset_sqr = (pos - self.last_pos):LengthSqr() local offset_sqr = (pos - self.last_pos):LengthSqr()
self.last_pos = pos self.last_pos = pos
if (offset_sqr < (10*10 * dt*dt) and not (ragmod and ragmod:IsRagmodRagdoll(target))) then if offset_sqr < (10*10 * dt*dt) then
self.StartDie = self.StartDie or cur_time self.StartDie = self.StartDie or cur_time
else
--self.StartDie = nil
end end
-- Physics correction after collision -- Physics correction after collision