NPCs should not unfake while moving... or shoot their friends even after they go unconscious... also first attempts at new unfaking animation

This commit is contained in:
toasterpanic 2026-06-03 22:55:35 -04:00
parent f6dbe81dbe
commit 4c4d59838a
7 changed files with 477 additions and 22 deletions

View file

@ -12,17 +12,19 @@ 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)
@ -30,11 +32,10 @@ local function RemoveRelationship(ent, me)
ent:SetEnemy(nil)
ent:ClearSchedule()
end
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)

View file

@ -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

92
lua/entities/v1.old Normal file
View file

@ -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

99
lua/entities/v2.old Normal file
View file

@ -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

94
lua/entities/v3.old Normal file
View file

@ -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

View file

@ -128,8 +128,13 @@ hook.Add("CreateEntityRagdoll", "zcnpci", function(ent, ragdoll)
end
if ent.npcfakeknockback then
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)

View file

@ -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
--[[-------------------------------------------------------------------------