Files
wnsrc/lua/pac3/core/client/bones.lua
lifestorm 94063e4369 Upload
2024-08-04 22:55:00 +03:00

493 lines
13 KiB
Lua

--[[
| This file was obtained through the combined efforts
| of Madbluntz & Plymouth Antiquarian Society.
|
| Credits: lifestorm, Gregory Wayne Rossel JR.,
| Maloy, DrPepper10 @ RIP, Atle!
|
| Visit for more: https://plymouth.thetwilightzone.ru/
--]]
local pairs = pairs
local IsValid = IsValid
local unpack = unpack
local LocalToWorld = LocalToWorld
local math_sin = math.sin
local RealTime = RealTime
local Entity = Entity
local ipairs = ipairs
local NULL = NULL
local LerpVector = LerpVector
local LerpAngle = LerpAngle
local Angle = Angle
local Vector = Vector
local util_QuickTrace = util.QuickTrace
local pac = pac
local pac_isCameraAllowed = pac.CreateClientConVarFast("pac_enable_camera_as_bone", "1", true, "boolean")
pac.BoneNameReplacements =
{
{"Anim_Attachment", "attach"},
{"RH", "right hand"},
{"LH", "left hand"},
{"_L_", " left "},
{"_R_", " right "},
{"%p", " "},
{"ValveBiped", ""},
{"Bip01", ""},
{"Neck1", "neck"},
{"Head1", "head"},
{"Toe0", "toe"},
{"lowerarm", "lower arm"},
{"Bip", ""},
{" R", " right"},
{" L", " left"},
}
function pac.GetAllBones(ent)
ent = ent or NULL
local tbl = {}
if ent:IsValid() then
ent:InvalidateBoneCache()
pac.SetupBones(ent)
local count = ent:GetBoneCount() or 0
for bone = 0, count do
local name = ent:GetBoneName(bone)
local friendly = name
if name then
for _, value in pairs(pac.BoneNameReplacements) do
friendly = friendly:gsub(value[1], value[2])
end
friendly = friendly
:Trim()
:lower()
:gsub("(.-)(%d+)", "%1 %2")
local parent_i = ent:GetBoneParent(bone)
if parent_i == -1 then
parent_i = nil
end
tbl[friendly] =
{
friendly = friendly,
real = name,
bone = bone,
i = bone,
parent_i = parent_i,
}
end
end
local attachments = ent:GetAttachments()
if attachments then
for _, data in pairs(attachments) do
local parent_i = ent:GetParentAttachment(data.id)
if parent_i == -1 then
parent_i = nil
end
local friendly = data.name or "????"
friendly = friendly
:Trim()
:lower()
:gsub("(.-)(%d+)", "%1 %2")
if not tbl[friendly] then -- Some of bones CAN be attachments! So we defined them before already.
tbl[friendly] =
{
friendly = friendly,
real = data.name or "?????",
id = data.id,
i = data.id,
parent_i = parent_i,
is_attachment = true,
}
end
end
end
tbl.hitpos = {friendly = "hit position", is_special = true}
tbl.footstep = {friendly = "footsteps", is_special = true}
tbl.skirt = {friendly = "skirt", is_special = true}
tbl.skirt2 = {friendly = "skirt2", is_special = true}
tbl.hitpos_world_props = {friendly = "hitpos_world_props", is_special = true}
tbl.hitpos_world = {friendly = "hitpos_world", is_special = true}
tbl.hitpos_world_noang = {friendly = "hitpos_world_noang", is_special = true}
tbl.hitpos_ent_ang = {friendly = "hitpos_ent_ang", is_special = true}
tbl.hitpos_ent_ang_zero_pitch = {friendly = "hitpos_ent_ang_zero_pitch", is_special = true}
tbl.pos_ang = {friendly = "pos_ang", is_special = true}
tbl.pos_eyeang = {friendly = "pos_eyeang", is_special = true}
tbl.eyepos_eyeang = {friendly = "eyepos_eyeang", is_special = true}
tbl.eyepos_ang = {friendly = "eyepos_ang", is_special = true}
tbl.pos_noang = {friendly = "pos_noang", is_special = true}
tbl.camera = {friendly = "camera", is_special = true}
tbl.player_eyes = {friendly = "player_eyes", is_special = true}
tbl.physgun_beam_endpos = {friendly = "physgun_beam_endpos", is_special = true}
ent.pac_bone_count = count + 1
end
return tbl
end
function pac.GetModelBones(ent)
if not ent or not ent:IsValid() then return {} end
if not ent.pac_bones or ent:GetModel() ~= ent.pac_last_model then
ent:InvalidateBoneCache()
pac.SetupBones(ent)
if ent.pac_holdtypes then
ent.pac_holdtypes = {}
end
ent.pac_bones = pac.GetAllBones(ent)
ent.pac_last_model = ent:GetModel()
end
return ent.pac_bones
end
function pac.GetAllFlexes(ent)
local out = {}
if ent.GetFlexNum and ent:GetFlexNum() > 0 then
for i = 0, ent:GetFlexNum() - 1 do
local name = ent:GetFlexName(i)
out[name:lower()] = {i = i, name = name}
end
end
return out
end
function pac.GetFlexMap(ent)
if not ent or not ent:IsValid() then return {} end
if not ent.pac_flex_map or ent:GetModel() ~= ent.pac_last_model_flex then
ent.pac_flex_map = pac.GetAllFlexes(ent)
ent.pac_last_model_flex = ent:GetModel()
end
return ent.pac_flex_map
end
function pac.ResetBoneCache(ent)
if not IsValid(ent) then return end
ent.pac_last_model = nil
ent.pac_bones = nil
ent.pac_cached_child_bones = nil
ent.pac_flex_map = nil
if ent.pac_holdtypes then
ent.pac_holdtypes = {}
end
end
local UP = Vector(0,0,1):Angle()
local function GetBonePosition(ent, id)
local pos, ang = ent:GetBonePosition(id)
if not pos then return end
if ang.p ~= ang.p then ang.p = 0 end
if ang.y ~= ang.y then ang.y = 0 end
if ang.r ~= ang.r then ang.r = 0 end
if pos == ent:GetPos() then
local mat = ent:GetBoneMatrix(id)
if mat then
pos = mat:GetTranslation()
ang = mat:GetAngles()
end
end
if (ent:GetClass() == "viewmodel" or ent == pac.LocalHands) and
ent:GetOwner():IsPlayer() and ent:GetOwner():GetActiveWeapon().ViewModelFlip then
ang.r = -ang.r
end
return pos, ang
end
local angle_origin = Angle(0,0,0)
function pac.GetBonePosAng(ent, id, parent)
if not ent:IsValid() then return Vector(), Angle() end
if id == "physgun_beam_endpos" then
if ent.pac_drawphysgun_event then
local ply, wep, enabled, target, bone, hitpos = unpack(ent.pac_drawphysgun_event)
local endpos
if enabled then
if target:IsValid() then
if bone ~= 0 then
local wpos, wang = target:GetBonePosition(target:TranslatePhysBoneToBone(bone))
endpos = LocalToWorld(hitpos, Angle(), wpos, wang)
else
endpos = target:LocalToWorld(hitpos)
end
else
endpos = ply.pac_traceres and ply.pac_traceres.HitPos or util_QuickTrace(ply:EyePos(), ply:EyeAngles():Forward() * 16000, {ply, ply:GetParent()}).HitPos
end
end
return endpos, Angle()
end
elseif id == "camera" then
if pac_isCameraAllowed() then
return pac.EyePos, pac.EyeAng
else
return ent:EyePos(), ent:EyeAngles()
end
elseif id == "player_eyes" then
local oldEnt = ent -- Track reference to the original entity in case we aren't allowed to draw here
local ent = ent.pac_traceres and ent.pac_traceres.Entity or util_QuickTrace(ent:EyePos(), ent:EyeAngles():Forward() * 16000, {ent, ent:GetParent()}).Entity
local allowed = pac_isCameraAllowed()
if ent:IsValid() and (allowed or ent ~= pac.LocalPlayer) then -- Make sure we don't draw on viewer's screen if we aren't allowed to
return ent:EyePos(), ent:EyeAngles()
end
if allowed then
return pac.EyePos, pac.EyeAng
else
return oldEnt:EyePos(), oldEnt:EyeAngles()
end
elseif id == "pos_ang" then
return ent:GetPos(), ent:GetAngles()
elseif id == "pos_noang" then
return ent:GetPos(), angle_origin
elseif id == "pos_eyeang" then
return ent:GetPos(), ent:EyeAngles()
elseif id == "eyepos_eyeang" then
return ent:EyePos(), ent:EyeAngles()
elseif id == "eyepos_ang" then
return ent:EyePos(), ent:GetAngles()
elseif id == "hitpos_world_props" then
local res = util_QuickTrace(ent:EyePos(), ent:EyeAngles():Forward() * 16000, function(ent)
return ent:GetClass() == "prop_physics"
end)
return res.HitPos, res.HitNormal:Angle()
elseif id == "hitpos_world" then
local res = util_QuickTrace(ent:EyePos(), ent:EyeAngles():Forward() * 16000, function(ent)
return ent:IsWorld()
end)
return res.HitPos, res.HitNormal:Angle()
elseif id == "hitpos_world_noang" then
local res = util_QuickTrace(ent:EyePos(), ent:EyeAngles():Forward() * 16000, function(ent)
return ent:IsWorld()
end)
return res.HitPos, angle_origin
elseif id == "hitpos" or id == "hit position" then
if ent.pac_traceres then
return ent.pac_traceres.HitPos, ent.pac_traceres.HitNormal:Angle()
else
local res = util_QuickTrace(ent:EyePos(), ent:EyeAngles():Forward() * 16000, {ent, ent:GetOwner()})
return res.HitPos, res.HitNormal:Angle()
end
elseif id == "hitpos_ent_ang" then
if ent.pac_traceres then
return ent.pac_traceres.HitPos, ent:EyeAngles()
else
local res = util_QuickTrace(ent:EyePos(), ent:EyeAngles():Forward() * 16000, {ent, ent:GetOwner()})
return res.HitPos, ent:EyeAngles()
end
elseif id == "hitpos_ent_ang_zero_pitch" then
if ent.pac_traceres then
local ang = ent:EyeAngles()
ang.p = 0
return ent.pac_traceres.HitPos, ang
else
local res = util_QuickTrace(ent:EyePos(), ent:EyeAngles():Forward() * 16000, {ent, ent:GetOwner()})
return res.HitPos, ent:EyeAngles()
end
elseif id == "footstep" then
if ent.pac_last_footstep_pos then
return ent.pac_last_footstep_pos, UP
end
elseif id == "skirt" then
local apos, aang = pac.GetBonePosAng(ent, "left thigh", parent)
local bpos, bang = pac.GetBonePosAng(ent, "right thigh", parent)
return LerpVector(0.5, apos, bpos), LerpAngle(0.5, aang, bang)
elseif id == "skirt2" then
local apos, aang = pac.GetBonePosAng(ent, "left calf", parent)
local bpos, bang = pac.GetBonePosAng(ent, "right calf", parent)
return LerpVector(0.5, apos, bpos), LerpAngle(0.5, aang, bang)
end
local pos, ang
local bones = pac.GetModelBones(ent)
local data = bones[id]
if data and not data.is_special then
if data.is_attachment then
if parent and data.parent_i then
local posang = ent:GetAttachment(data.parent_i)
if not posang then
posang = ent:GetAttachment(data.id)
end
if posang then
pos, ang = posang.Pos, posang.Ang
end
else
local posang = ent:GetAttachment(data.id)
if posang then
pos, ang = posang.Pos, posang.Ang
end
end
else
if parent and data.parent_i then
pos, ang = GetBonePosition(ent, data.parent_i)
if not pos or not ang then
pos, ang = GetBonePosition(ent, data.bone)
end
else
pos, ang = GetBonePosition(ent, data.bone)
end
end
else
local bone_id = id and ent:LookupBone(id) or nil
if bone_id then
pos, ang = GetBonePosition(ent, bone_id)
end
end
if not pos then
pos = ent:GetPos()
end
if not ang then
if ent:IsPlayer() then
ang = ent:EyeAngles()
ang.p = 0
else
ang = ent:GetAngles()
end
end
return pos, ang
end
do -- bone manipulation for boneanimlib
local SCALE_RESET = Vector(1, 1, 1)
local ORIGIN_RESET = Vector(0, 0, 0)
local ANGLE_RESET = Angle(0, 0, 0)
local entmeta = FindMetaTable("Entity")
local ManipulateBoneScale = entmeta.ManipulateBoneScale
local ManipulateBonePosition = entmeta.ManipulateBonePosition
local ManipulateBoneAngles = entmeta.ManipulateBoneAngles
local ManipulateBoneJiggle = entmeta.ManipulateBoneJiggle
function pac.ResetBones(ent)
ent.pac_bones_once = true
local pac_boneanim = ent.pac_boneanim
local count = (ent:GetBoneCount() or 0) - 1
if pac_boneanim then
for i = 0, count do
ManipulateBoneScale(ent, i, SCALE_RESET)
ManipulateBonePosition(ent, i, pac_boneanim.positions[i] or ORIGIN_RESET)
ManipulateBoneAngles(ent, i, pac_boneanim.angles[i] or ANGLE_RESET)
ManipulateBoneJiggle(ent, i, 0)
end
else
for i = 0, count do
ManipulateBoneScale(ent, i, SCALE_RESET)
ManipulateBonePosition(ent, i, ORIGIN_RESET)
ManipulateBoneAngles(ent, i, ANGLE_RESET)
ManipulateBoneJiggle(ent, i, 0)
end
end
local i = ent.pac_bones_select_target
if i and count >= i then
ManipulateBoneScale(ent, i, ent:GetManipulateBoneScale(i) * (1 + math_sin(RealTime() * 4) * 0.1))
ent.pac_bones_select_target = nil
end
if ent.pac_touching_flexes then
local reset_scale = false
for id, time in pairs(ent.pac_touching_flexes) do
if not reset_scale then
ent:SetFlexScale(1)
reset_scale = true
end
if time < pac.RealTime then
ent:SetFlexWeight(id, 0)
else
if ent:GetFlexWeight(id) == 0 then
ent.pac_touching_flexes[id] = nil
end
end
end
end
hook.Call("PAC3ResetBones", nil, ent)
end
function pac.SetEntityBoneMatrix(ent, i, matrix)
pac.ManipulateBonePosition(ent, i, matrix:GetTranslation())
pac.ManipulateBoneAngles(ent, i, matrix:GetAngles())
end
function pac.ResetEntityBoneMatrix(ent, i)
pac.ManipulateBoneAngles(ent, i, angle_zero)
pac.ManipulateBonePosition(ent, i, vector_origin)
end
function pac.ManipulateBonePosition(ply, id, var)
ply.pac_boneanim = ply.pac_boneanim or {positions = {}, angles = {}}
ply.pac_boneanim.positions[id] = var
if not ply.pac_has_parts then
ply:ManipulateBonePosition(id, var)
end
end
function pac.ManipulateBoneAngles(ply, id, var)
ply.pac_boneanim = ply.pac_boneanim or {positions = {}, angles = {}}
ply.pac_boneanim.angles[id] = var
if not ply.pac_has_parts then
ply:ManipulateBoneAngles(id, var)
end
end
end
if Entity(1):IsPlayer() and not PAC_RESTART then
timer.Simple(0, function()
for _, v in ipairs(ents.GetAll()) do
pac.ResetBoneCache(v)
end
end)
end