Kicking now downs enemies, optimizations, bug fixes, cleanups

This commit is contained in:
toasterpanic 2026-05-28 13:08:14 -04:00
parent b56cbd1d24
commit 48a650f948
5 changed files with 82 additions and 102 deletions

View file

@ -43,6 +43,13 @@ local function PopulateRagdollSBXToolMenu(pnl)
end end
local function PopulatePerformanceSBXToolMenu(pnl) local function PopulatePerformanceSBXToolMenu(pnl)
pnl:NumSlider("NPC ticks per second", "zcnpci_tps", 1, 66, 0)
pnl:ControlHelp("How many times NPCs should be processed per second. Will make them more responsive at the cost of performance.")
pnl:Help(" ")
pnl:Help(" ")
pnl:NumSlider("Activation range", "zcnpci_active_range", 0, 32768, 0) pnl:NumSlider("Activation range", "zcnpci_active_range", 0, 32768, 0)
pnl:ControlHelp("How close the ragdoll has to be to a player to live when downed by direct damage. Enemies downed by other causes are unaffected.") pnl:ControlHelp("How close the ragdoll has to be to a player to live when downed by direct damage. Enemies downed by other causes are unaffected.")
@ -56,6 +63,9 @@ local function PopulatePerformanceSBXToolMenu(pnl)
pnl:CheckBox("Enable automatic corpse removal", "zcnpci_enable_corpse_removal") pnl:CheckBox("Enable automatic corpse removal", "zcnpci_enable_corpse_removal")
pnl:ControlHelp("Enable or disable the removal of dead corpses.") pnl:ControlHelp("Enable or disable the removal of dead corpses.")
pnl:NumSlider("Corpse time between loops", "zcnpci_corpse_loop_time", 1, 66, 0)
pnl:ControlHelp("How long should we wait in between checks?")
pnl:NumSlider("Max corpses", "zcnpci_max_corpses", -1, 64, 0) pnl:NumSlider("Max corpses", "zcnpci_max_corpses", -1, 64, 0)
pnl:ControlHelp("The maximum amount of dead corpses that are allowed before one is removed. Set to -1 to disable") pnl:ControlHelp("The maximum amount of dead corpses that are allowed before one is removed. Set to -1 to disable")

View file

@ -3,8 +3,10 @@ include("zcnpci/modules.lua")
local enabled = CreateConVar("zcnpci_enabled", 1, bit.bor(FCVAR_ARCHIVE, FCVAR_REPLICATED)) local enabled = CreateConVar("zcnpci_enabled", 1, bit.bor(FCVAR_ARCHIVE, FCVAR_REPLICATED))
local active_range = CreateConVar("zcnpci_active_range", 32768, bit.bor(FCVAR_ARCHIVE, FCVAR_REPLICATED)) local active_range = CreateConVar("zcnpci_active_range", 32768, bit.bor(FCVAR_ARCHIVE, FCVAR_REPLICATED))
local always_active_on_player_kill = CreateConVar("zcnpci_always_active_on_player_kill", 1, bit.bor(FCVAR_ARCHIVE, FCVAR_REPLICATED)) local always_active_on_player_kill = CreateConVar("zcnpci_always_active_on_player_kill", 1, bit.bor(FCVAR_ARCHIVE, FCVAR_REPLICATED))
local ticks_per_second = CreateConVar("zcnpci_tps", 20, bit.bor(FCVAR_ARCHIVE, FCVAR_REPLICATED))
local remove_corpses = CreateConVar("zcnpci_enable_corpse_removal", 0, bit.bor(FCVAR_ARCHIVE, FCVAR_REPLICATED)) local remove_corpses = CreateConVar("zcnpci_enable_corpse_removal", 0, bit.bor(FCVAR_ARCHIVE, FCVAR_REPLICATED))
local corpse_loop_time = CreateConVar("zcnpci_corpse_loop_time", 2.0, bit.bor(FCVAR_ARCHIVE, FCVAR_REPLICATED))
local max_corpses = CreateConVar("zcnpci_max_corpses", 8, bit.bor(FCVAR_ARCHIVE, FCVAR_REPLICATED)) local max_corpses = CreateConVar("zcnpci_max_corpses", 8, 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_time = CreateConVar("zcnpci_max_corpse_time", 120, bit.bor(FCVAR_ARCHIVE, FCVAR_REPLICATED))
local min_corpse_distance = CreateConVar("zcnpci_min_corpse_distance", 500, bit.bor(FCVAR_ARCHIVE, FCVAR_REPLICATED)) local min_corpse_distance = CreateConVar("zcnpci_min_corpse_distance", 500, bit.bor(FCVAR_ARCHIVE, FCVAR_REPLICATED))
@ -17,7 +19,7 @@ local treat_near_death_as_dead = CreateConVar("zcnpci_treat_near_death_as_dead"
local allow_extended_base_npcs = CreateConVar("zcnpci_allow_extended_base_npcs", 1, bit.bor(FCVAR_ARCHIVE, FCVAR_REPLICATED)) local allow_extended_base_npcs = CreateConVar("zcnpci_allow_extended_base_npcs", 1, bit.bor(FCVAR_ARCHIVE, FCVAR_REPLICATED))
local allow_modded_npcs = CreateConVar("zcnpci_allow_modded_npcs", 0, bit.bor(FCVAR_ARCHIVE, FCVAR_REPLICATED)) local allow_modded_npcs = CreateConVar("zcnpci_allow_modded_npcs", 0, bit.bor(FCVAR_ARCHIVE, FCVAR_REPLICATED))
local auto_enable_humanoid_npcs = CreateConVar("zcnpci_auto_enable_humanoid_npcs", 1, bit.bor(FCVAR_ARCHIVE, FCVAR_REPLICATED)) local auto_enable_humanoid_npcs = CreateConVar("zcnpci_auto_enable_humanoid_npcs", 1, bit.bor(FCVAR_ARCHIVE, FCVAR_REPLICATED))
local modded_npc_whitelist = CreateConVar("zcnpci_modded_npc_whitelist", "NPC-classes-here!", bit.bor(FCVAR_ARCHIVE, FCVAR_REPLICATED)) local modded_npc_whitelist = CreateConVar("zcnpci_modded_npc_whitelist", "nb_example", bit.bor(FCVAR_ARCHIVE, FCVAR_REPLICATED))
-- The way I have to set up the npc whitelist client-side makes it so the host can't configure it without console commands, so we have to do this shit -- The way I have to set up the npc whitelist client-side makes it so the host can't configure it without console commands, so we have to do this shit
local set_modded_npc_whitelist = concommand.Add("zcnpci_set_modded_npc_whitelist", function(ply, cmd, args) local set_modded_npc_whitelist = concommand.Add("zcnpci_set_modded_npc_whitelist", function(ply, cmd, args)
@ -105,8 +107,6 @@ hook.Add("CreateEntityRagdoll", "zcnpci", function(ent, ragdoll)
hook.Run("RagdollDeath", ent, ragdoll) hook.Run("RagdollDeath", ent, ragdoll)
--table.Merge(zb.net.list[rag], zb.net.list[ent])
ragdoll.organism.owner = ragdoll ragdoll.organism.owner = ragdoll
ragdoll:CallOnRemove("organism", hg.organism.Remove, ragdoll) ragdoll:CallOnRemove("organism", hg.organism.Remove, ragdoll)
ragdoll.organism.owner.fullsend = true ragdoll.organism.owner.fullsend = true
@ -115,6 +115,10 @@ hook.Add("CreateEntityRagdoll", "zcnpci", function(ent, ragdoll)
ent.organism = nil ent.organism = nil
end end
if ent.npcfakeknockback then
ragdoll:GetPhysicsObject():SetVelocity(ent.npcfakeknockback)
end
timer.Simple(0, function() timer.Simple(0, function()
ragdoll.organism.alive = true ragdoll.organism.alive = true
@ -125,24 +129,55 @@ hook.Add("CreateEntityRagdoll", "zcnpci", function(ent, ragdoll)
end) end)
end) end)
hook.Add("EntityTakeDamage", "zcnpci", function(ent, dmginfo) hook.Add("HomigradDamage", "zcnpci", function(ent, dmginfo)
if !enabled:GetBool() then return end if !enabled:GetBool() or !IsValid(dmginfo) then return end
if (!ent:IsNPC() or dmginfo:GetDamage() < ent:Health()) then return end if !ent:IsNPC() then
if !ent.organism or !ent.StartDie then return end
last_dmgpos[ent] = dmginfo:GetDamagePosition() if dmginfo:IsDamageType(DMG_BULLET + DMG_BUCKSHOT + DMG_BLAST + DMG_CLUB + DMG_SLASH + DMG_GENERIC) then
-- Reset ragdoll get up timer -- don't want someone getting up mid-curbstomp
ent.StartDie = CurTime()
end
return
end
last_dmgpos[ent] = dmginfo:GetDamage()
last_attacker[ent] = dmginfo:GetAttacker() last_attacker[ent] = dmginfo:GetAttacker()
if dmginfo:IsDamageType(DMG_BULLET + DMG_BUCKSHOT + DMG_BLAST) then
if dmginfo:GetDamage() > 1.5 then
ent:TakeDamage(ent:Health())
end
elseif dmginfo:IsDamageType(DMG_CLUB + DMG_SLASH) then
-- If it's hands and has more than 7.5 damage, it's a kick probably
if IsValid(dmginfo:GetInflictor()) and (dmginfo:GetDamage() >= 7.5) and (
(dmginfo:GetInflictor():GetClass() == "weapon_hands_sh") or
(dmginfo:GetInflictor():GetClass() == "weapon_hg_coolhands")
) then
ent.neednpcfake = true
local attacker_angle = dmginfo:GetAttacker():EyeAngles()
local normal = attacker_angle:Forward(normal)
ent.npcfakeknockback = normal * dmginfo:GetDamage() * 135
end
end
end) end)
hook.Add("ScaleNPCDamage", "zcnpci", function(npc, hitgroup, dmginfo) hook.Add("ScaleNPCDamage", "zcnpci", function(npc, hitgroup, dmginfo)
if not IsValid(npc) then return end if !IsValid(npc) then return end
last_hitgroup[npc] = hitgroup last_hitgroup[npc] = hitgroup
end) end)
hook.Add("Think", "zcnpci", function() hook.Add("Tick", "zcnpci", function()
local do_corpse_loop = (CurTime() - last_corpse_tick) > 2.0 local do_corpse_loop = (CurTime() - last_corpse_tick) > corpse_loop_time:GetFloat()
local do_general_loop = (CurTime() - last_tick) > 0.1 local do_general_loop = (CurTime() - last_tick) > (1 / ticks_per_second:GetInt())
if do_general_loop then if do_general_loop then
last_tick = CurTime()
if do_corpse_loop then if do_corpse_loop then
last_corpse_tick = CurTime() last_corpse_tick = CurTime()
@ -160,7 +195,6 @@ hook.Add("Think", "zcnpci", function()
table.remove(corpses, 1) table.remove(corpses, 1)
end end
end end
for i, ent in pairs(ents.GetAll()) do for i, ent in pairs(ents.GetAll()) do
if !IsValid(ent) then continue end if !IsValid(ent) then continue end
if !ent.organism then continue end if !ent.organism then continue end
@ -174,6 +208,7 @@ hook.Add("Think", "zcnpci", function()
(ent.organism.consciousness <= 0.3) or (ent.organism.consciousness <= 0.3) or
(ent.organism.pain > 90) or (ent.organism.pain > 90) or
ent:IsOnFire() or ent:IsOnFire() or
ent.neednpcfake or
debug_ragdoll_all:GetBool() debug_ragdoll_all:GetBool()
) then ) then
local damage_info = DamageInfo() local damage_info = DamageInfo()
@ -281,45 +316,6 @@ end)
local once = true local once = true
--RagMod/TTT support
--[[hook.Add("OnEntityCreated", "Fedhoria", function(ent)
--If RagMod isn't installed remove this hook
if once then
once = nil
if (!RMA_Ragdolize and !CORPSE) then
hook.Remove("OnEntityCreated", "Fedhoria")
return
end
--these hooks fucks shit up
if RMA_Ragdolize then
hook.Remove( "PlayerDeath", "RM_PlayerDies")
hook.Add( "PostPlayerDeath", "RemoveRagdoll", function(ply)
if IsValid(ply.RM_Ragdoll) then
SafeRemoveEntity(ply:GetRagdollEntity())
ply:SpectateEntity(ply.RM_Ragdoll)
end
end)
end
end
if (!enabled:GetBool() or !players:GetBool() or !ent:IsRagdoll()) then return end
timer.Simple(0, function()
if !IsValid(ent) then return end
if CORPSE then
local ply = ent:GetDTEntity(CORPSE.dti.ENT_PLAYER)
if (IsValid(ply) and ply:IsPlayer()) then
fedhoria.StartModule(ent, "stumble_legs")
return
end
end
for _, ply in ipairs(player.GetAll()) do
if (ply.RM_IsRagdoll and ply.RM_Ragdoll == ent) then
fedhoria.StartModule(ent, "stumble_legs")
return
end
end
end)
end)]]
local PLAYER = FindMetaTable("Player") local PLAYER = FindMetaTable("Player")
local oldCreateRagdoll = PLAYER.CreateRagdoll local oldCreateRagdoll = PLAYER.CreateRagdoll
@ -362,36 +358,3 @@ local oldGetRagdollEntity = PLAYER.GetRagdollEntity
local function GetRagdollEntity(self) local function GetRagdollEntity(self)
return dolls[self] or NULL return dolls[self] or NULL
end end
--[[
if enabled:GetBool() then
PLAYER.CreateRagdoll = CreateRagdoll
PLAYER.GetRagdollEntity = GetRagdollEntity
end]]
--[[
cvars.AddChangeCallback("fedhoria_enabled", function(name, old, new)
if (new == "1") then
if players:GetBool() then
PLAYER.CreateRagdoll = CreateRagdoll
PLAYER.GetRagdollEntity = GetRagdollEntity
end
else
PLAYER.CreateRagdoll = oldCreateRagdoll
PLAYER.GetRagdollEntity = oldGetRagdollEntity
end
end)
cvars.AddChangeCallback("fedhoria_players", function(name, old, new)
if (new == "1") then
if enabled:GetBool() then
if (debug.getinfo(PLAYER.CreateRagdoll).short_src == "[C]") then
PLAYER.CreateRagdoll = CreateRagdoll
PLAYER.GetRagdollEntity = GetRagdollEntity
end
end
else
PLAYER.CreateRagdoll = oldCreateRagdoll
PLAYER.GetRagdollEntity = oldGetRagdollEntity
end
end)]]

View file

@ -75,6 +75,7 @@ function ENTITY:GetClosestPhysBone(pos)
end end
if !collides then return end if !collides then return end
if type(pos) != "Vector" then return end
local closest_bone local closest_bone
local dist = math.huge local dist = math.huge

View file

@ -149,16 +149,19 @@ function MODULE:StartAnimationRoll()
return return
end end
--self.StartDie = nil --target.StartDie = nil
end end
function MODULE:Init() function MODULE:Init()
local seq = self:LookupSequence("Choked_Barnacle") local seq = self:LookupSequence("Choked_Barnacle")
if seq then self:ResetSequence(seq) end if seq then self:ResetSequence(seq) end
local target = self:GetTarget()
self:SetPlaybackRate(1) self:SetPlaybackRate(1)
self.LastCollideTime = 0 self.LastCollideTime = 0
self.LastGroundCollideTime = 0 self.LastGroundCollideTime = 0
self.StartDie = nil target.StartDie = nil
self.AnimationRollEndTime = 0 self.AnimationRollEndTime = 0
self.StopProcessing = false self.StopProcessing = false
self.LastThink = CurTime() self.LastThink = CurTime()
@ -251,11 +254,11 @@ function MODULE:PhysicsSimulate(phys, dt)
target.is_npc_corpse = true target.is_npc_corpse = true
if !self.StartDie then self.StartDie = cur_time end if !target.StartDie then target.StartDie = cur_time end
-- Check for active animation -- Check for active animation
if cur_time < self.AnimationRollEndTime then if cur_time < self.AnimationRollEndTime then
--self.StartDie = nil -- Resetting the "death" timer --target.StartDie = nil -- Resetting the "death" timer
return true -- We use standard physics. return true -- We use standard physics.
end end
@ -263,8 +266,8 @@ function MODULE:PhysicsSimulate(phys, dt)
local f = target.organism.consciousness local f = target.organism.consciousness
local minimum_down_timer = 0 local minimum_down_timer = 0
if self.StartDie then if target.StartDie then
minimum_down_timer = math_Clamp((cur_time - self.StartDie) / minimum_down_time:GetFloat(), 0, 1) minimum_down_timer = math_Clamp((cur_time - target.StartDie) / minimum_down_time:GetFloat(), 0, 1)
end end
if !target.organism then if !target.organism then
@ -277,7 +280,7 @@ function MODULE:PhysicsSimulate(phys, dt)
self:Remove() self:Remove()
return false -- Cut the bullshit return false -- Cut the bullshit
elseif (target.organism.consciousness <= 0.5) or ((target.organism.lleg >= 0.85) and (target.organism.rleg >= 0.85)) then elseif (target.organism.consciousness <= 0.5) or ((target.organism.lleg >= 0.85) and (target.organism.rleg >= 0.85)) then
self.StartDie = cur_time target.StartDie = cur_time
return false return false
end end
@ -363,7 +366,7 @@ function MODULE:PhysicsSimulate(phys, dt)
self.last_pos = pos self.last_pos = pos
if offset_sqr < (10*10 * dt*dt) then if offset_sqr < (10*10 * dt*dt) then
self.StartDie = self.StartDie or cur_time target.StartDie = target.StartDie or cur_time
end end
-- Physics correction after collision -- Physics correction after collision

View file

@ -114,17 +114,20 @@ function MODULE:StartAnimationRoll()
return return
end end
self.StartDie = nil -- блять я заебался с этой хуйней target.StartDie = nil -- блять я заебался с этой хуйней
-- print("[Fedhoria AnimRoll] StartAnimationRoll initiated. Speed:", cv_anim_roll_playback_rate:GetFloat(), "Duration:", cv_anim_roll_duration:GetFloat(), "End Time:", self.AnimationRollEndTime) -- Debug -- print("[Fedhoria AnimRoll] StartAnimationRoll initiated. Speed:", cv_anim_roll_playback_rate:GetFloat(), "Duration:", cv_anim_roll_duration:GetFloat(), "End Time:", self.AnimationRollEndTime) -- Debug
end end
function MODULE:Init() function MODULE:Init()
local seq = self:LookupSequence("idleonfire") local seq = self:LookupSequence("idleonfire")
if seq then self:ResetSequence(seq) end if seq then self:ResetSequence(seq) end
local target = self:GetTarget()
self:SetPlaybackRate(1) self:SetPlaybackRate(1)
self.LastCollideTime = 0 self.LastCollideTime = 0
self.LastGroundCollideTime = 0 self.LastGroundCollideTime = 0
self.StartDie = nil target.StartDie = nil
self.AnimationRollEndTime = 0 self.AnimationRollEndTime = 0
-- идте нахуй с этой хуйней -- идте нахуй с этой хуйней
@ -172,7 +175,7 @@ function MODULE:PhysicsSimulate(phys, dt)
-- проверка на физику -- проверка на физику
if cur_time < self.AnimationRollEndTime then if cur_time < self.AnimationRollEndTime then
-- print("[Fedhoria Sim] In Animation Roll. Time left:", self.AnimationRollEndTime - cur_time) -- Debug -- print("[Fedhoria Sim] In Animation Roll. Time left:", self.AnimationRollEndTime - cur_time) -- Debug
-- self.StartDie = nil -- Сбрасываем таймер смерти -- target.StartDie = nil -- Сбрасываем таймер смерти
return true -- стандартная физика пошла нахуй, у меня по ней 2 return true -- стандартная физика пошла нахуй, у меня по ней 2
-- return false -- если физика нахуй идет -- return false -- если физика нахуй идет
@ -188,7 +191,7 @@ function MODULE:PhysicsSimulate(phys, dt)
local owner = target:GetOwningPlayer() local owner = target:GetOwningPlayer()
-- владелец идот нахуй -- владелец идот нахуй
f = (IsValid(owner) and owner:Alive()) and 1 or 0 f = (IsValid(owner) and owner:Alive()) and 1 or 0
self.StartDie = nil target.StartDie = nil
if f <= 0 then self.AnimationRollEndTime = 0 end if f <= 0 then self.AnimationRollEndTime = 0 end
end end
@ -241,10 +244,10 @@ 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 -- uменьшил порог неподвижности if (offset_sqr < (10*10 * dt*dt) and not (ragmod and ragmod:IsRagmodRagdoll(target))) then -- uменьшил порог неподвижности
self.StartDie = self.StartDie or cur_time -- идите нахуй target.StartDie = target.StartDie or cur_time -- идите нахуй
-- print("[Fedhoria Sim] Torso seems stationary. StartDie:", self.StartDie) -- Debug -- print("[Fedhoria Sim] Torso seems stationary. StartDie:", target.StartDie) -- Debug
else else
-- self.StartDie = nil -- нахуй таймер -- target.StartDie = nil -- нахуй таймер
-- print("[Fedhoria Sim] Torso moved. Resetting StartDie.") -- Debug -- print("[Fedhoria Sim] Torso moved. Resetting StartDie.") -- Debug
end end