From 4c4d59838a79a27b59787cc70a7fdf31d2a749c9 Mon Sep 17 00:00:00 2001 From: toasterpanic Date: Wed, 3 Jun 2026 22:55:35 -0400 Subject: [PATCH] NPCs should not unfake while moving... or shoot their friends even after they go unconscious... also first attempts at new unfaking animation --- lua/entities/npc_ragdoll_target.lua | 25 +++-- lua/entities/npc_ragdoll_unfaker.lua | 147 +++++++++++++++++++++++++++ lua/entities/v1.old | 92 +++++++++++++++++ lua/entities/v2.old | 99 ++++++++++++++++++ lua/entities/v3.old | 94 +++++++++++++++++ lua/zcnpci.lua | 19 ++-- lua/zcnpci/modules/falling_legs.lua | 23 ++++- 7 files changed, 477 insertions(+), 22 deletions(-) create mode 100644 lua/entities/npc_ragdoll_unfaker.lua create mode 100644 lua/entities/v1.old create mode 100644 lua/entities/v2.old create mode 100644 lua/entities/v3.old diff --git a/lua/entities/npc_ragdoll_target.lua b/lua/entities/npc_ragdoll_target.lua index 6a060ed..b4bf22d 100644 --- a/lua/entities/npc_ragdoll_target.lua +++ b/lua/entities/npc_ragdoll_target.lua @@ -12,29 +12,30 @@ local stop_targeting_when_unconscious = CreateConVar("zcnpci_no_unconscious_ta local vector_offset_to_shoot_at = Vector(0, 0, 8) local function UpdateRelationship(ent, me) - if !IsValid(ent) or !ent:IsNPC() then return end + if !IsValid(ent) or !ent:IsNPC() or (ent:GetClass() == "npc_ragdoll_target") then return end if ent:Disposition(me.dummy_entity) == D_HT then + print("Ragdoll target of class "..me.ragdoll_to_follow.class_in_previous_life.." is hated by a "..ent:GetClass()) ent:AddEntityRelationship(me, D_HT, -1) + else + ent:AddEntityRelationship(me, D_NU, -1) end end local function RemoveRelationship(ent, me) - if !IsValid(ent) or !ent:IsNPC() then return end + if !IsValid(ent) or !ent:IsNPC() or (ent:GetClass() == "npc_ragdoll_target") then return end - if ent:Disposition(me.dummy_entity) == D_HT then - ent:AddEntityRelationship(me, D_NU, -1) - ent:ClearEnemyMemory(me) + ent:AddEntityRelationship(me, D_NU, -1) + ent:ClearEnemyMemory(me) - if ent:GetEnemy() == me then - ent:SetEnemy(nil) - ent:ClearSchedule() - end + if ent:GetEnemy() == me then + ent:SetEnemy(nil) + ent:ClearSchedule() end end function ENT:Classify() - return self.dummy_entity:Classify() + return CLASS_NONE end function ENT:Initialize() @@ -63,7 +64,7 @@ end function ENT:Think() if SERVER then local ragdoll = self.ragdoll_to_follow - if !IsValid(ragdoll) then return end + if !IsValid(ragdoll) then self:Remove(); return end if should_follow_head:GetBool() then local head = ragdoll:GetPhysicsObjectNum(ragdoll:TranslateBoneToPhysBone(ragdoll:LookupBone("ValveBiped.Bip01_Head1"))) @@ -76,6 +77,8 @@ function ENT:Think() if !ragdoll.organism then return true end + if !ragdoll.organism.alive then self:Remove(); return end + local should_stop_targeting = !stop_targeting_when_unconscious:GetBool() or ( (ragdoll.organism.consciousness <= 0.4) or (ragdoll.organism.critical) diff --git a/lua/entities/npc_ragdoll_unfaker.lua b/lua/entities/npc_ragdoll_unfaker.lua new file mode 100644 index 0000000..af12317 --- /dev/null +++ b/lua/entities/npc_ragdoll_unfaker.lua @@ -0,0 +1,147 @@ +AddCSLuaFile() + +ENT.Type = "anim" +ENT.Base = "base_anim" +ENT.AutomaticFrameAdvance = true + + + +local bones_to_animate = { + "ValveBiped.Bip01_Pelvis", + "ValveBiped.Bip01_Spine2", + "ValveBiped.Bip01_Head1", + "ValveBiped.Bip01_R_Thigh", + "ValveBiped.Bip01_L_Thigh", + "ValveBiped.Bip01_L_Calf", + "ValveBiped.Bip01_R_Calf", + "ValveBiped.Bip01_R_Foot", + "ValveBiped.Bip01_L_Foot", + "ValveBiped.Bip01_R_Upperarm", + "ValveBiped.Bip01_L_Upperarm", + "ValveBiped.Bip01_R_Forearm", + "ValveBiped.Bip01_L_Forearm", + "ValveBiped.Bip01_R_Hand", + "ValveBiped.Bip01_L_Hand", +} + +function ENT:Initialize() + if SERVER then + print("I AM SERVERSIDE") + self:SetModel("models/dav0r/hoverball.mdl") + end + + if CLIENT then + print("I AM CLIENTSIDE") + timer.Simple(0.2, function() + + end) + end +end + +function ENT:Draw() + if CLIENT then + local old_ragdoll = self:GetNWEntity("parent") + local old_npc = self:GetNWEntity("parent_npc") + if IsValid(old_ragdoll) and IsValid(old_npc) then + if !self.ready_to_unfake then + self:SetModel(old_ragdoll:GetModel()) + + self.bone_list = {} + self.bone_list_end = {} + + self:SetupBones() + + local i = 0 + while i < self:GetBoneCount() do + local name = self:GetBoneName(i) + if name == "__INVALIDBONE__" then + i = i + 1 + continue + end + + table.insert(bones_to_animate, name) + + + local matrix = Matrix() + + local position, angle = old_ragdoll:GetBonePosition(i) + if position == old_ragdoll:GetPos() then + get_matrix = old_ragdoll:GetBoneMatrix(i) + if get_matrix then + position = get_matrix:GetTranslation() + end + end + matrix:Translate(position) + matrix:Rotate(angle) + + self.bone_list[name] = matrix + + local matrix = Matrix() + + local position, angle = old_npc:GetBonePosition(i) + if position == old_npc:GetPos() then + get_matrix = old_npc:GetBoneMatrix(i) + if get_matrix then + position = get_matrix:GetTranslation() + end + end + matrix:Translate(position) + matrix:Rotate(angle) + + + self.bone_list_end[name] = matrix + + i = i + 1 + end + + PrintTable(bones_to_animate) + + --[[for i,name in pairs(bones_to_animate) do + local bone_index = old_ragdoll:LookupBone(name) + if bone_index == nil then continue end + + self.bone_list[name] = Matrix(old_ragdoll:GetBoneMatrix(bone_index)) + end]] + + print("BONES:"..self:GetBoneCount()) + + self.ready_to_unfake = true + end + + self:SetPos(old_ragdoll:GetPos()) + + self:SetupBones() + + for name,matrix in pairs(self.bone_list) do + local bone_index = self:LookupBone(name) + if bone_index == -1 then + print(name.." does not exist, skipping...") + continue + end + + + --print(name) + --local bone_matrix = old_ragdoll:GetBoneMatrix(bone_index) + + if matrix:GetTranslation():DistToSqr(self:GetPos()) == 1 then continue end + + self:SetBoneMatrix(bone_index, matrix) + end + end + end + + self:DrawModel() +end + +function ENT:Think() + + return true +end + +function ENT:OnRemove() + if CLIENT then + --[[if self.anim_ragdoll then + self.anim_ragdoll:Remove() + end]] + end +end \ No newline at end of file diff --git a/lua/entities/v1.old b/lua/entities/v1.old new file mode 100644 index 0000000..6376c95 --- /dev/null +++ b/lua/entities/v1.old @@ -0,0 +1,92 @@ +AddCSLuaFile() + +ENT.Type = "anim" +ENT.Base = "base_anim" +ENT.AutomaticFrameAdvance = true + +local bones_to_animate = { + "ValveBiped.Bip01_Pelvis", + "ValveBiped.Bip01_Spine2", + "ValveBiped.Bip01_Head1", + "ValveBiped.Bip01_R_Thigh", + "ValveBiped.Bip01_L_Thigh", + "ValveBiped.Bip01_L_Calf", + "ValveBiped.Bip01_R_Calf", + "ValveBiped.Bip01_R_Foot", + "ValveBiped.Bip01_L_Foot", + "ValveBiped.Bip01_R_Upperarm", + "ValveBiped.Bip01_L_Upperarm", + "ValveBiped.Bip01_R_Forearm", + "ValveBiped.Bip01_L_Forearm", + "ValveBiped.Bip01_R_Hand", + "ValveBiped.Bip01_L_Hand", +} + +function ENT:Initialize() + if SERVER then + print("I AM SERVERSIDE") + self:SetModel("models/dav0r/hoverball.mdl") + end + + if CLIENT then + print("I AM CLIENTSIDE") + timer.Simple(0.2, function() + + end) + end +end + +function ENT:Draw() + if CLIENT then + local old_ragdoll = self:GetNWEntity("parent") + if IsValid(old_ragdoll) then + if !self.ready_to_unfake then + self:SetModel(old_ragdoll:GetModel()) + + self.bone_list = {} + + self:SetupBones() + + for i,name in pairs(bones_to_animate) do + local bone_index = old_ragdoll:LookupBone(name) + + self.bone_list[name] = old_ragdoll:GetBoneMatrix(bone_index) + end + + print("BONES:"..self:GetBoneCount()) + + self.ready_to_unfake = true + end + + self:SetPos(old_ragdoll:GetPos()) + + for name,matrix in pairs(self.bone_list) do + local bone_index = self:LookupBone(name) + if bone_index == -1 then + print(name.." does not exist, skipping...") + continue + end + + print(name) + --local bone_matrix = old_ragdoll:GetBoneMatrix(bone_index) + + self:SetBoneMatrix(bone_index, matrix) + end + end + end + + self:DrawModel() +end + +function ENT:Think() + + return true +end + +function ENT:OnRemove() + if CLIENT then + if self.anim_ragdoll then + self.anim_ragdoll:Remove() + end + end +end \ No newline at end of file diff --git a/lua/entities/v2.old b/lua/entities/v2.old new file mode 100644 index 0000000..163649b --- /dev/null +++ b/lua/entities/v2.old @@ -0,0 +1,99 @@ +AddCSLuaFile() + +ENT.Type = "anim" +ENT.Base = "base_anim" +ENT.AutomaticFrameAdvance = true + +local bones_to_animate = { + "ValveBiped.Bip01_Pelvis", + "ValveBiped.Bip01_Spine2", + "ValveBiped.Bip01_Head1", + "ValveBiped.Bip01_R_Thigh", + "ValveBiped.Bip01_L_Thigh", + "ValveBiped.Bip01_L_Calf", + "ValveBiped.Bip01_R_Calf", + "ValveBiped.Bip01_R_Foot", + "ValveBiped.Bip01_L_Foot", + "ValveBiped.Bip01_R_Upperarm", + "ValveBiped.Bip01_L_Upperarm", + "ValveBiped.Bip01_R_Forearm", + "ValveBiped.Bip01_L_Forearm", + "ValveBiped.Bip01_R_Hand", + "ValveBiped.Bip01_L_Hand", +} + +function ENT:Initialize() + if SERVER then + print("I AM SERVERSIDE") + self:SetModel("models/dav0r/hoverball.mdl") + end + + if CLIENT then + print("I AM CLIENTSIDE") + timer.Simple(0.2, function() + + end) + end +end + +function ENT:Draw() + if CLIENT then + local old_ragdoll = self:GetNWEntity("parent") + if IsValid(old_ragdoll) then + if !self.ready_to_unfake then + self:SetModel(old_ragdoll:GetModel()) + + self.bone_list = {} + self.bone_original_positions = {} + + self:SetupBones() + + for i,name in pairs(bones_to_animate) do + local bone_index = old_ragdoll:LookupBone(name) + + self.bone_list[name] = old_ragdoll:GetBoneMatrix(bone_index) + self.bone_original_positions[name] = self:GetBoneMatrix(bone_index) + end + + print("BONES:"..self:GetBoneCount()) + + self.ready_to_unfake = true + end + + self:SetPos(old_ragdoll:GetPos()) + + for name,matrix in pairs(self.bone_list) do + local bone_index = self:LookupBone(name) + if bone_index == -1 then + print(name.." does not exist, skipping...") + continue + end + + print(name) + --local bone_matrix = old_ragdoll:GetBoneMatrix(bone_index) + + local original_position = self.bone_original_positions[name]:GetTranslation() + local goal_position = matrix:GetTranslation() + + print((original_position - goal_position)) + + self:ManipulateBonePosition(bone_index, Vector(0, 0, 16)) + end + end + end + + self:DrawModel() +end + +function ENT:Think() + + return true +end + +function ENT:OnRemove() + if CLIENT then + if self.anim_ragdoll then + self.anim_ragdoll:Remove() + end + end +end \ No newline at end of file diff --git a/lua/entities/v3.old b/lua/entities/v3.old new file mode 100644 index 0000000..09a1eab --- /dev/null +++ b/lua/entities/v3.old @@ -0,0 +1,94 @@ +AddCSLuaFile() + +ENT.Type = "anim" +ENT.Base = "base_anim" +ENT.AutomaticFrameAdvance = true + +local bones_to_animate = { + "ValveBiped.Bip01_Pelvis", + "ValveBiped.Bip01_Spine2", + "ValveBiped.Bip01_Head1", + "ValveBiped.Bip01_R_Thigh", + "ValveBiped.Bip01_L_Thigh", + "ValveBiped.Bip01_L_Calf", + "ValveBiped.Bip01_R_Calf", + "ValveBiped.Bip01_R_Foot", + "ValveBiped.Bip01_L_Foot", + "ValveBiped.Bip01_R_Upperarm", + "ValveBiped.Bip01_L_Upperarm", + "ValveBiped.Bip01_R_Forearm", + "ValveBiped.Bip01_L_Forearm", + "ValveBiped.Bip01_R_Hand", + "ValveBiped.Bip01_L_Hand", +} + +function ENT:Initialize() + if SERVER then + print("I AM SERVERSIDE") + self:SetModel("models/dav0r/hoverball.mdl") + end + + if CLIENT then + print("I AM CLIENTSIDE") + timer.Simple(0.2, function() + + end) + end +end + +function ENT:Draw() + if CLIENT then + local old_ragdoll = self:GetNWEntity("parent") + if IsValid(old_ragdoll) then + if !self.ready_to_unfake then + self.ragdoll = ClientsideModel(old_ragdoll:GetModel()) + self.ragdoll:SetModel(old_ragdoll:GetModel()) + + self.bone_list = {} + + self.ragdoll:SetupBones() + + for i,name in pairs(bones_to_animate) do + local bone_index = old_ragdoll:LookupBone(name) + + self.bone_list[name] = old_ragdoll:GetBoneMatrix(bone_index) + end + + print("BONES:"..self.ragdoll:GetBoneCount()) + + self.ready_to_unfake = true + end + + self.ragdoll:SetPos(old_ragdoll:GetPos()) + + for name,matrix in pairs(self.bone_list) do + local bone_index = self.ragdoll:LookupBone(name) + if bone_index == -1 then + print(name.." does not exist, skipping...") + continue + end + + print(name) + --local bone_matrix = old_ragdoll:GetBoneMatrix(bone_index) + + self.ragdoll:SetBoneMatrix(bone_index, matrix) + end + end + end + + self:DrawModel() + self.ragdoll:DrawModel() +end + +function ENT:Think() + + return true +end + +function ENT:OnRemove() + if CLIENT then + if self.anim_ragdoll then + self.anim_ragdoll:Remove() + end + end +end diff --git a/lua/zcnpci.lua b/lua/zcnpci.lua index 3d5371f..c00a453 100644 --- a/lua/zcnpci.lua +++ b/lua/zcnpci.lua @@ -128,7 +128,12 @@ hook.Add("CreateEntityRagdoll", "zcnpci", function(ent, ragdoll) end if ent.npcfakeknockback then - ragdoll:GetPhysicsObject():SetVelocity(ent.npcfakeknockback) + if dmgpos and (phys_bone != -1) then + local phys = ragdoll:GetPhysicsObjectNum(phys_bone) + phys:SetVelocity(ent.npcfakeknockback) + else + ragdoll:GetPhysicsObject():SetVelocity(ent.npcfakeknockback) + end end local velocity = ragdoll:GetPhysicsObject():GetVelocity() @@ -172,20 +177,18 @@ hook.Add("HomigradDamage", "zcnpci", function(ent, dmginfo) if dmginfo:GetDamage() > 3 then table.insert(npcs_to_fake, ent) - local attacker_angle = dmginfo:GetAttacker():EyeAngles() + --local attacker_angle = dmginfo:GetAttacker():EyeAngles() - local normal = attacker_angle:Forward(normal) + --local normal = attacker_angle:Forward(normal) - ent.npcfakeknockback = normal * dmginfo:GetDamage() * 0 + --ent.npcfakeknockback = normal * 7 * 3 end elseif dmginfo:IsDamageType(DMG_CLUB + DMG_SLASH) then local attacker = dmginfo:GetAttacker() if !IsValid(attacker) then return end local fists = attacker:HasWeapon("weapon_hands_sh") - if !fists then fists = dmginfo:GetAttacker():HasWeapon("weapon_hg_coolhands") end - - local attacker = dmginfo:GetAttacker() + if !fists then fists = attacker:HasWeapon("weapon_hg_coolhands") end -- Kicks should knock NPCs down if IsValid(dmginfo:GetInflictor()) and fists and attacker.InLegKick and ((attacker.InLegKick + 0.1) > CurTime()) then @@ -196,6 +199,8 @@ hook.Add("HomigradDamage", "zcnpci", function(ent, dmginfo) local normal = attacker_angle:Forward(normal) ent.npcfakeknockback = normal * dmginfo:GetDamage() * 135 + + last_dmgpos[ent] = nil end end end) diff --git a/lua/zcnpci/modules/falling_legs.lua b/lua/zcnpci/modules/falling_legs.lua index e6fa720..4d26177 100644 --- a/lua/zcnpci/modules/falling_legs.lua +++ b/lua/zcnpci/modules/falling_legs.lua @@ -198,6 +198,7 @@ function MODULE:Init() self.StopProcessing = false self.LastThink = CurTime() self.LastFakeUpCheck = CurTime() + self.LastPosCheck = target:GetPos() self.bullseye = ents.Create("npc_ragdoll_target") self.bullseye:SetPos(target:GetPos()) @@ -296,6 +297,8 @@ end Physics Simulation Hook (FIXED) ---------------------------------------------------------------------------]] function MODULE:PhysicsSimulate(phys, dt) + if !SERVER then return end + local cur_time = CurTime() local target = self:GetTarget() if not IsValid(target) then self:Remove(); self.bullseye:Remove(); return false end @@ -388,20 +391,23 @@ function MODULE:PhysicsSimulate(phys, dt) if !target.organism then self:Remove() - self.bullseye:Remove() return false -- Cut the bullshit end if (!target.organism.alive) then -- If the NPC is dead, they probably aren't coming back; don't bother bringing them back to life self:Remove() - self.bullseye:Remove() return false -- Cut the bullshit elseif (target.organism.consciousness <= 0.5) or ((target.organism.lleg >= 0.85) and (target.organism.rleg >= 0.85)) then target.StartDie = cur_time return false end + if (self.LastPosCheck:DistToSqr(target:GetPos()) > (32 ^ 2)) then + self.LastPosCheck = target:GetPos() + target.StartDie = cur_time + end + if ((CurTime() - self.LastFakeUpCheck) >= 1.0) and (can_unfake:GetBool()) then self.LastFakeUpCheck = CurTime() @@ -466,6 +472,9 @@ function MODULE:PhysicsSimulate(phys, dt) for i = 0, target:GetNumBodyGroups() - 1 do ent:SetBodygroup(i, target:GetBodygroup(i)) end + + self.unfaker:Spawn() + self.unfaker:Activate() end) target.FakeUp = true @@ -481,6 +490,13 @@ function MODULE:PhysicsSimulate(phys, dt) target:SetNotSolid(true) target:SetMoveType(MOVETYPE_NONE) + self.unfaker = ents.Create("npc_ragdoll_unfaker") + self.unfaker:SetPos(target:GetPos()) + self.unfaker:SetNWEntity("parent", target) + self.unfaker:SetNWEntity("parent_npc", ent) + + + target:SetRenderMode(RENDERMODE_NONE) ent:SetRenderMode(RENDERMODE_NONE) timer.Simple(self.FakeUpTime, function() @@ -496,7 +512,6 @@ function MODULE:PhysicsSimulate(phys, dt) self:Remove() end) - self.bullseye:Remove() return false end end @@ -567,7 +582,7 @@ function MODULE:OnRemove() local timer_name = "Fedhoria_FallingLegs_Twitch_" .. self:EntIndex() timer_Remove(timer_name) - if IsValid(self.bullseye) then self.bullseye:Remove() end + if IsValid(self.unfaker) then self.unfaker:Remove() end end --[[-------------------------------------------------------------------------