mirror of
https://github.com/lifestorm/wnsrc.git
synced 2025-12-17 13:53:45 +03:00
493 lines
13 KiB
Lua
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
|