zcnpci/lua/entities/active_ragdoll_controller.lua

325 lines
8.3 KiB
Lua
Raw Normal View History

2026-05-25 04:57:41 +00:00
AddCSLuaFile()
ENT.Type = "anim"
ENT.Base = "base_anim"
ENT.AutomaticFrameAdvance = true
function ENT:SetupDataTables()
self:NetworkVar("Entity", 0, "Target")
end
function ENT:SetModule(mod)
self.Module = mod
end
function ENT:SetInitParams(...)
self.InitParams = {...}
end
local FORCE_SCALE = 3
local math_atan2 = math.atan2
local math_min = math.min
local math_max = math.max
local math_abs = math.abs
local phys_settings =
{
["ValveBiped.Bip01_Pelvis"] = {mass = 12.741364, inertia = Vector(0.80, 0.97, 0.96)},
["ValveBiped.Bip01_Spine2"] = {mass = 24.297474, inertia = Vector(2.15, 3.33, 3.12)},
["ValveBiped.Bip01_R_UpperArm"] = {mass = 3.529606, inertia = Vector(0.06, 0.28, 0.28)},
["ValveBiped.Bip01_L_UpperArm"] = {mass = 3.466939, inertia = Vector(0.06, 0.27, 0.27)},
["ValveBiped.Bip01_L_Forearm"] = {mass = 1.801132, inertia = Vector(0.02, 0.10, 0.10)},
["ValveBiped.Bip01_L_Hand"] = {mass = 1.075074, inertia = Vector(0.02, 0.02, 0.02)},
["ValveBiped.Bip01_R_Forearm"] = {mass = 1.781718, inertia = Vector(0.02, 0.10, 0.10)},
["ValveBiped.Bip01_R_Hand"] = {mass = 1.018670, inertia = Vector(0.02, 0.02, 0.02)},
["ValveBiped.Bip01_R_Thigh"] = {mass = 10.187500, inertia = Vector(0.35, 1.74, 1.76)},
["ValveBiped.Bip01_R_Calf"] = {mass = 4.996145, inertia = Vector(0.10, 0.63, 0.64)},
["ValveBiped.Bip01_Head1"] = {mass = 5.163157, inertia = Vector(0.19, 0.21, 0.27)},
["ValveBiped.Bip01_L_Thigh"] = {mass = 10.188610, inertia = Vector(0.35, 1.74, 1.76)},
["ValveBiped.Bip01_L_Calf"] = {mass = 4.995875, inertia = Vector(0.10, 0.63, 0.64)},
["ValveBiped.Bip01_L_Foot"] = {mass = 2.378366, inertia = Vector(0.05, 0.13, 0.13)},
["ValveBiped.Bip01_R_Foot"] = {mass = 2.378366, inertia = Vector(0.05, 0.13, 0.13)}
}
function ENT:PhysAlignAngles(phys, ang)
local avel = Vector(0, 0, 0)
local ang1 = phys:GetAngles()
local forward1 = ang1:Forward()
local forward2 = ang:Forward()
local fd = forward1:Dot(forward2)
local right1 = ang1:Right()
local right2 = ang:Right()
local rd = right1:Dot(right2)
local up1 = ang1:Up()
local up2 = ang:Up()
local ud = up1:Dot(up2)
local pitchvel = math.asin(forward1:Dot(up2)) * 180 / math.pi
local yawvel = math.asin(forward1:Dot(right2)) * 180 / math.pi
local rollvel = math.asin(right1:Dot(up2)) * 180 / math.pi
avel.y = avel.y + pitchvel
avel.z = avel.z + yawvel
avel.x = avel.x + rollvel
return avel
end
function ENT:SetMaxAngVel(val)
self.max_ang_vel = val
self.max_ang_vel_sqr = val * val
end
function ENT:SetBoneList(list)
self.bone_list = list
end
function ENT:Initialize()
if !self.Module then return end
if self.Module.Model then self:SetModel(self.Module.Model) end
2026-05-25 04:57:41 +00:00
for key, value in pairs(self.Module) do
if !self[key] then
self[key] = value
end
end
self:StartMotionController()
local target = self:GetTarget()
if !target._FixedSettings then
target._FixedSettings = true
for bone_name, info in pairs(phys_settings) do
local bone = target:LookupBone(bone_name)
if bone then
local phys_bone = target:TranslateBoneToPhysBone(bone)
local phys = target:GetPhysicsObjectNum(phys_bone)
if IsValid(phys) then
phys:SetInertia(info.inertia)
phys:SetMass(info.mass)
end
end
end
end
self.bone_translate = {}
self.bone_parent = {}
local is_match = false
for _, bone_name in pairs(self.Module.BoneList) do
local bone = target:LookupBone(bone_name)
if bone then
is_match = true
local phys_bone = target:TranslateBoneToPhysBone(bone)
local phys = target:GetPhysicsObjectNum(phys_bone)
if IsValid(phys) then
bone = target:TranslatePhysBoneToBone(phys_bone)
self.root_bone = self.root_bone or bone
self.root_phys_bone = self.root_phys_bone or phys_bone
self:AddToMotionController(phys)
self.bone_translate[bone] = self:LookupBone(bone_name)
local bone_parent = target:GetBoneParent(bone)
bone_parent = target:TranslateBoneToPhysBone(bone_parent)
bone_parent = target:TranslatePhysBoneToBone(bone_parent)
local bone_name_parent = target:GetBoneName(bone_parent)
self.bone_parent[bone] = bone_parent
self.bone_translate[bone_parent] = self:LookupBone(bone_name_parent)
end
end
end
if !is_match then
self:Remove()
return
end
self.bone_head = self:LookupBone("ValveBiped.Bip01_Head1")
if self.bone_head then
self.phys_bone_head = self:TranslatePhysBoneToBone(self.bone_head)
end
self.Created = CurTime()
target:DeleteOnRemove(self)
self:SetMaxAngVel(400) --default
if self.Module.Init then
if self.InitParams then
self.Module.Init(self, unpack(self.InitParams))
else
self.Module.Init(self)
end
end
if self.Module.PhysicsCollide then
self.PCCB = target:AddCallback("PhysicsCollide", function(ent, data)
self.Module.PhysicsCollide(self, ent, data)
end)
end
end
function ENT:Think()
if !self.Module then return end
if self.Module.Think then
self.Module.Think(self)
end
end
function ENT:OnRemove()
if !self.Module then return end
if self.Module.OnRemove then
self.Module.OnRemove(self)
end
local target = self:GetTarget()
if (self.PCCB and IsValid(target)) then
target:RemoveCallback("PhysicsCollide", self.PCCB)
end
end
function ENT:UpdateTransmitState()
return TRANSMIT_NEVER
--return TRANSMIT_PVS
end
function ENT:PhysicsSimulate(phys, dt)
local factor = 1
if self.Module.PhysicsSimulate then
local b, f = self.Module.PhysicsSimulate(self, phys, dt)
if b == false then
return
end
factor = f or factor
end
local target = self:GetTarget()
local phys_bone = phys:GetID()
if target.GS2IsDismembered then
--decapitatied?
if (self.phys_bone_head and target:GS2IsDismembered(self.phys_bone_head)) then
self:Remove()
return
end
local phys_bone = phys_bone
local dis = false
local bone = target:TranslatePhysBoneToBone(phys_bone)
repeat
phys_bone = target:TranslateBoneToPhysBone(bone)
if target:GS2IsDismembered(phys_bone) then
dis = true
break
end
bone = target:GetBoneParent(bone)
until (phys_bone == 1 or phys_bone == 0)
if (phys_bone == 0) then
dis = target:GS2IsDismembered(1)
if dis then
self.StartDie = self.StartDie or CurTime()
end
elseif (phys_bone != 1) then
dis = true
end
if dis then
self:RemoveFromMotionController(phys)
return
end
end
self:FrameAdvance()
local bone = target:TranslatePhysBoneToBone(phys_bone)
local bone_parent = self.bone_parent[bone]
local self_bone = self.bone_translate[bone]
local self_bone_parent = self.bone_translate[bone_parent]
if (self_bone and self_bone_parent) then
local _, bone_ang = self:GetBonePosition(self_bone)
local _, bone_ang_parent = self:GetBonePosition(self_bone_parent)
local _, lang = WorldToLocal(vector_origin, bone_ang, vector_origin, bone_ang_parent)
local _, target_ang = LocalToWorld(vector_origin, lang, target:GetBonePosition(bone_parent))
--TODO: clamp to ragdoll joint limits to avoid spazz
local ang_vel = self:PhysAlignAngles(phys, target_ang)
ang_vel:Mul(6 * factor)
ang_vel:Sub(phys:GetAngleVelocity())
if self.max_ang_vel then
local len_sqr = ang_vel:LengthSqr()
if (len_sqr > self.max_ang_vel_sqr) then
ang_vel:Normalize()
ang_vel:Mul(self.max_ang_vel)
end
end
phys:AddAngleVelocity(ang_vel)
--maybe this can work some day but that day is not today
--[[local phys_bone_parent = target:TranslateBoneToPhysBone(self_bone_parent)
local phys_parent = target:GetPhysicsObjectNum(phys_bone_parent)
local _, lang = WorldToLocal(vector_origin, bone_ang_parent, vector_origin, bone_ang)
local _, target_ang = LocalToWorld(vector_origin, lang, target:GetBonePosition(bone))
--TODO: clamp to ragdoll joint limits to avoid spazz
local ang_vel = self:PhysAlignAngles(phys_parent, target_ang)
ang_vel:Mul(20 * factor)
ang_vel:Sub(phys_parent:GetAngleVelocity())
if self.max_ang_vel then
local len_sqr = ang_vel:LengthSqr()
if (len_sqr > self.max_ang_vel_sqr) then
ang_vel:Normalize()
ang_vel:Mul(self.max_ang_vel)
end
end
phys_parent:AddAngleVelocity(ang_vel)]]
end
end