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

386 lines
9.0 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 FrameTime = FrameTime
local BUILDER, PART = pac.PartTemplate("base")
local AnimStack
AnimStack = {
__index = {
push = function(self, part)
local stack = self.stack
if #stack == 0 then
-- Empty stack
table.insert(stack, part)
else
-- Stop the current animation if it's not self
local top = self:getTop()
if top ~= part then
if top then top:OnStackStop() end
-- Remove self from stack to move to end and also prevent things from breaking because table.RemoveByValue() only removes the first instance
table.RemoveByValue(stack, part)
table.insert(stack, part)
end
end
part:OnStackStart()
end,
pop = function(self, part)
part:OnStackStop()
local stack = self.stack
-- Remove self from animation stack
if table.RemoveByValue(stack, part) == #stack + 1 then
-- This was the current animation so play the next in the stack
local top = self:getTop()
if top then top:OnStackStart() end
end
end,
getTop = function(self)
local stack = self.stack
local top = stack[#stack]
-- Remove invalid parts
while top and not top:IsValid() do
table.remove(stack)
top = stack[#stack]
end
return top
end
},
__call = function(meta)
return setmetatable({
stack = {}
}, meta)
end,
get = function(ent)
local animStack = ent.pac_animation_stack
if not animStack then
animStack = AnimStack()
ent.pac_animation_stack = animStack
end
return animStack
end
}
setmetatable(AnimStack, AnimStack)
PART.ClassName = "animation"
PART.ThinkTime = 0
PART.Groups = {'entity', 'model', 'modifiers'}
PART.Icon = 'icon16/eye.png'
PART.frame = 0
BUILDER
:StartStorableVars()
:GetSet("Loop", true)
:GetSet("PingPongLoop", false)
:GetSet("SequenceName", "", {enums = function(part) local tbl = {} for k,v in pairs(part:GetSequenceList()) do tbl[v] = v end return tbl end})
:GetSet("Rate", 1, {editor_sensitivity = 0.1})
:GetSet("Offset", 0)
:GetSet("Min", 0)
:GetSet("Max", 1)
:GetSet("WeaponHoldType", "none", {enums = function(part) return part.ValidHoldTypes end})
:GetSet("OwnerCycle", false)
:GetSet("InvertFrames", false)
:GetSet("ResetOnHide", true)
:EndStorableVars()
local tonumber = tonumber
PART.ValidHoldTypes =
{
pistol = ACT_HL2MP_IDLE_PISTOL,
smg = ACT_HL2MP_IDLE_SMG1,
grenade = ACT_HL2MP_IDLE_GRENADE,
ar2 = ACT_HL2MP_IDLE_AR2,
shotgun = ACT_HL2MP_IDLE_SHOTGUN,
rpg = ACT_HL2MP_IDLE_RPG,
physgun = ACT_HL2MP_IDLE_PHYSGUN,
crossbow = ACT_HL2MP_IDLE_CROSSBOW,
melee = ACT_HL2MP_IDLE_MELEE,
slam = ACT_HL2MP_IDLE_SLAM,
normal = ACT_HL2MP_IDLE,
fist = ACT_HL2MP_IDLE_FIST,
melee2 = ACT_HL2MP_IDLE_MELEE2,
passive = ACT_HL2MP_IDLE_PASSIVE,
knife = ACT_HL2MP_IDLE_KNIFE,
duel = ACT_HL2MP_IDLE_DUEL,
camera = ACT_HL2MP_IDLE_CAMERA,
revolver = ACT_HL2MP_IDLE_REVOLVER,
zombie = ACT_HL2MP_IDLE_ZOMBIE,
magic = ACT_HL2MP_IDLE_MAGIC,
meleeangry = ACT_HL2MP_IDLE_MELEE_ANGRY,
angry = ACT_HL2MP_IDLE_ANGRY,
suitcase = ACT_HL2MP_IDLE_SUITCASE,
scared = ACT_HL2MP_IDLE_SCARED,
}
function PART:GetNiceName()
local str = self:GetSequenceName()
if str == "" and self:GetWeaponHoldType() ~= "none" then
str = self:GetWeaponHoldType()
end
return pac.PrettifyName(str)
end
function PART:GetSequenceList()
local ent = self:GetOwner()
if ent:IsValid() then
return ent:GetSequenceList()
end
return {"none"}
end
PART.GetSequenceNameList = PART.GetSequenceList
function PART:OnStackStop()
-- Move code from PART:OnHide() to here
local ent = self:GetOwner()
if ent:IsValid() then
if not self:GetResetOnHide() then
self.SequenceCycle = ent:GetCycle()
self.storeFrame = self.frame
else
self.SequenceCycle = nil
self.frame = 0
end
if ent.pac_animation_sequences then
ent.pac_animation_sequences[self] = nil
end
if ent.pac_animation_holdtypes then
ent.pac_animation_holdtypes[self] = nil
end
if not ent:IsPlayer() and self.prevSequence then
ent:ResetSequence(self.prevSequence)
self.prevSequence = nil
end
end
end
-- Stop animation and remove from animation stack
function PART:OnHide()
local ent = self:GetOwner()
if not ent:IsValid() then return end
AnimStack.get(ent):pop(self)
end
PART.random_seqname = ""
function PART:SetSequenceName(name)
self.SequenceName = name
self.random_seqname = table.Random(name:Split(";"))
if not self:IsHidden() then
self:OnShow()
end
end
function PART:OnStackStart()
-- Moved code from PART:OnShow() to here
self.PlayingSequenceFrom = RealTime()
local ent = self:GetOwner()
if ent:IsValid() then
self.prevSequence = ent:GetSequence()
self.random_seqname = table.Random(self.SequenceName:Split(";"))
if self.random_seqname ~= "" then
local seq = ent:LookupSequence(self.random_seqname) or 0
local count = ent:GetSequenceCount() or 0
if seq < 0 or seq > count or count < 0 then
return
end
ent.pac_animation_sequences = ent.pac_animation_sequences or {}
ent.pac_animation_sequences[self] = ent.pac_animation_sequences[self] or {}
local tbl = ent.pac_animation_sequences[self]
tbl.part = self
if seq ~= -1 then
tbl.seq = seq
else
seq = tonumber(self.random_seqname) or -1
if seq ~= -1 then
tbl.seq = seq
else
ent.pac_animation_sequences[self] = nil
end
end
if seq ~= -1 then
ent:ResetSequence(seq)
ent:SetSequence(seq)
if not self:GetResetOnHide() then
ent:ResetSequenceInfo()
for i = 1, 10 do
ent:FrameAdvance(1)
end
ent:ResetSequenceInfo()
end
end
elseif ent:IsPlayer() then
local t = self.WeaponHoldType
t = t:lower()
local index = self.ValidHoldTypes[t]
ent.pac_animation_holdtypes = ent.pac_animation_holdtypes or {}
if index == nil then
ent.pac_animation_holdtypes[self] = nil
else
local params = {}
params[ACT_MP_STAND_IDLE] = index + 0
params[ACT_MP_WALK] = index + 1
params[ACT_MP_RUN] = index + 2
params[ACT_MP_CROUCH_IDLE] = index + 3
params[ACT_MP_CROUCHWALK] = index + 4
params[ACT_MP_ATTACK_STAND_PRIMARYFIRE] = index + 5
params[ACT_MP_ATTACK_CROUCH_PRIMARYFIRE] = index + 5
params[ACT_MP_RELOAD_STAND] = index + 6
params[ACT_MP_RELOAD_CROUCH] = index + 7
params[ACT_MP_JUMP] = index + 8
params[ACT_RANGE_ATTACK1] = index + 9
params[ACT_MP_SWIM_IDLE] = index + 10
params[ACT_MP_SWIM] = index + 11
-- "normal" jump animation doesn't exist
if t == "normal" then
params[ACT_MP_JUMP] = ACT_HL2MP_JUMP_SLAM
end
-- these two aren't defined in ACTs for whatever reason
if t == "knife" or t == "melee2" then
params[ACT_MP_CROUCH_IDLE] = nil
end
params.part = self
ent.pac_animation_holdtypes[self] = params
end
end
if not self:GetResetOnHide() and self.SequenceCycle then
ent:SetCycle(self.SequenceCycle)
self.SequenceCycle = nil
if self.storeFrame then
self.frame = self.storeFrame
self.storeFrame = nil
end
end
end
end
-- Play animation and move to top of animation stack
function PART:OnShow()
local ent = self:GetOwner()
if not ent:IsValid() then return end
AnimStack.get(ent):push(self)
end
function PART:OnThink()
local ent = self:GetOwner()
if not ent:IsPlayer() then
self:OnUpdateAnimation(nil)
end
end
function PART:OnUpdateAnimation(ply)
if self:IsHiddenCached() then return end
local ent = self:GetOwner()
if not ent:IsValid() or not ent.pac_animation_stack or ent.pac_animation_stack.stack[#ent.pac_animation_stack.stack] ~= self then return end
-- from UpdateAnimation hook
if ply and ent ~= ply then return end
if not self.random_seqname then return end
local seq, duration = ent:LookupSequence(self.random_seqname)
local count = ent:GetSequenceCount() or 0
if seq < 0 or seq >= count then
-- It's an invalid sequence. Don't bother
return
end
if self.OwnerCycle then
local owner = self:GetRootPart():GetOwner()
if IsValid(owner) then
ent:SetCycle(owner:GetCycle())
end
return
end
local min = self.Min
local max = self.Max
local maxmin = max - min
if min == max then
local cycle = min
if pac.IsNumberValid(cycle) then
ent:SetCycle(self.InvertFrames and (1 - cycle) or cycle)
end
return
end
local rate = (duration == 0) and 0 or (self.Rate / duration / math.abs(maxmin) * FrameTime())
if self.PingPongLoop then
if self.Loop then
self.frame = (self.frame + rate) % 2
else
self.frame = math.max(math.min(self.frame + rate, 2), 0)
end
local cycle = min + math.abs(1 - (self.frame + 1 + self.Offset) % 2) * maxmin
if pac.IsNumberValid(cycle) then
ent:SetCycle(self.InvertFrames and (1 - cycle) or cycle)
end
else
if self.Loop then
self.frame = (self.frame + rate) % 2
else
self.frame = math.max(math.min(self.frame + rate, 1), 0)
end
local cycle = min + (self.frame + self.Offset) % 1 * maxmin
if pac.IsNumberValid(cycle) then
ent:SetCycle(self.InvertFrames and (1 - cycle) or cycle)
end
end
end
BUILDER:Register()