650 lines
24 KiB
Lua
650 lines
24 KiB
Lua
--[[-------------------------------------------------------------------------
|
||
A script for controlling the behavior of falling NPC body parts (e.g., legs).
|
||
Includes flinching effects and roll animation upon impact.
|
||
Version 2: Fixed GetGameData and HitNormal errors.
|
||
---------------------------------------------------------------------------]]
|
||
|
||
MODULE = {}
|
||
|
||
MODULE.Model = "models/police.mdl" -- The model to which the script is applied (likely not used directly in this code)
|
||
|
||
-- A list of bones that MAY be affected by physics or animation.
|
||
-- Only the leg bones from this list will be used for pulling.
|
||
MODULE.BoneList =
|
||
{
|
||
"ValveBiped.Bip01_Pelvis",
|
||
-- "ValveBiped.Bip01_Spine2",
|
||
-- "ValveBiped.Bip01_Head1",
|
||
-- "ValveBiped.Bip01_R_Upperarm",
|
||
-- "ValveBiped.Bip01_R_Forearm",
|
||
-- "ValveBiped.Bip01_R_Hand",
|
||
-- "ValveBiped.Bip01_L_Upperarm",
|
||
-- "ValveBiped.Bip01_L_Forearm",
|
||
-- "ValveBiped.Bip01_L_Hand",
|
||
"ValveBiped.Bip01_R_Thigh",
|
||
"ValveBiped.Bip01_R_Calf",
|
||
"ValveBiped.Bip01_R_Foot",
|
||
"ValveBiped.Bip01_L_Thigh",
|
||
"ValveBiped.Bip01_L_Calf",
|
||
"ValveBiped.Bip01_L_Foot"
|
||
}
|
||
|
||
-- A list of bones to which the jiggle effect WILL be applied.
|
||
-- Filtered from MODULE.BoneList to exclude the pelvis and other unwanted bones.
|
||
local twitchable_bone_names = {
|
||
"ValveBiped.Bip01_R_Thigh",
|
||
"ValveBiped.Bip01_R_Calf",
|
||
"ValveBiped.Bip01_R_Foot",
|
||
"ValveBiped.Bip01_L_Thigh",
|
||
"ValveBiped.Bip01_L_Calf",
|
||
"ValveBiped.Bip01_L_Foot"
|
||
}
|
||
|
||
local fakeup_bone_names = {
|
||
"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",]]
|
||
}
|
||
|
||
local fakeup_bone_down_names = {
|
||
"ValveBiped.Bip01_R_Forearm",
|
||
"ValveBiped.Bip01_L_Forearm",
|
||
"ValveBiped.Bip01_R_Thigh",
|
||
"ValveBiped.Bip01_L_Thigh",
|
||
}
|
||
|
||
-- Local Copies of Functions for Optimization
|
||
local math_Clamp = math.Clamp
|
||
local math_Rand = math.Rand
|
||
local math_random = math.random
|
||
local table_Random = table.Random -- We use `table.Random` to select a random element.
|
||
local IsValid = IsValid -- Local copy of IsValid
|
||
local CurTime = CurTime -- Local copy of CurTime
|
||
local timer_Create = timer.Create
|
||
local timer_Remove = timer.Remove
|
||
local VectorRand = VectorRand
|
||
|
||
--[[-------------------------------------------------------------------------
|
||
Convars (Settings)
|
||
---------------------------------------------------------------------------]]
|
||
-- Twitching
|
||
local cv_twitch_enabled = CreateConVar("zcnpci_falling_twitch_enabled", "1", {FCVAR_ARCHIVE, FCVAR_REPLICATED}, "Включить дергание для падающих ног")
|
||
local cv_twitch_interval_min = CreateConVar("zcnpci_falling_twitch_interval_min", "3", {FCVAR_ARCHIVE, FCVAR_REPLICATED}, "Минимальный интервал между дерганиями (сек)")
|
||
local cv_twitch_interval_max = CreateConVar("zcnpci_falling_twitch_interval_max", "6", {FCVAR_ARCHIVE, FCVAR_REPLICATED}, "Максимальный интервал между дерганиями (сек)")
|
||
local cv_twitch_force_min = CreateConVar("zcnpci_falling_twitch_min", "100", {FCVAR_ARCHIVE, FCVAR_REPLICATED}, "Мин. угловая скорость для дергания")
|
||
local cv_twitch_force_max = CreateConVar("zcnpci_falling_twitch_max", "250", {FCVAR_ARCHIVE, FCVAR_REPLICATED}, "Макс. угловая скорость для дергания")
|
||
|
||
-- Roll
|
||
local cv_anim_roll_enabled = CreateConVar("zcnpci_falling_anim_roll_enabled", "1", {FCVAR_ARCHIVE, FCVAR_REPLICATED}, "Включить анимацию при ударе о землю")
|
||
local cv_anim_roll_min_delay = CreateConVar("zcnpci_falling_anim_roll_min_delay", "0.5", {FCVAR_ARCHIVE, FCVAR_REPLICATED}, "Мин. задержка перед возможной анимацией после удара (сек)")
|
||
local cv_anim_roll_duration = CreateConVar("zcnpci_falling_anim_roll_duration", "3.5", {FCVAR_ARCHIVE, FCVAR_REPLICATED}, "Продолжительность принудительной анимации (сек)")
|
||
local cv_anim_roll_impact_threshold = CreateConVar("zcnpci_falling_anim_roll_impact_threshold", "300", {FCVAR_ARCHIVE, FCVAR_REPLICATED}, "Мин. скорость удара для запуска анимации")
|
||
local cv_anim_roll_playback_rate = CreateConVar("zcnpci_falling_anim_roll_playback_rate", "3.0", {FCVAR_ARCHIVE, FCVAR_REPLICATED}, "Скорость воспроизведения анимации")
|
||
|
||
-- Down time
|
||
local minimum_down_time = CreateConVar("zcnpci_down_time", "5", {FCVAR_ARCHIVE, FCVAR_REPLICATED}, "Down time!!!!!!!!!")
|
||
|
||
-- Unfaking
|
||
local can_unfake = CreateConVar("zcnpci_unfake_enabled", "1", {FCVAR_ARCHIVE, FCVAR_REPLICATED}, "Whether an NPC can unfake")
|
||
local unfake_time = CreateConVar("zcnpci_unfake_time", "1.5", {FCVAR_ARCHIVE, FCVAR_REPLICATED}, "Time it takes for an NPC to unfake")
|
||
|
||
--[[-------------------------------------------------------------------------
|
||
Tug function
|
||
Applies a random impulse to one of the leg bones.
|
||
---------------------------------------------------------------------------]]
|
||
function MODULE:DoTwitch()
|
||
if not IsValid(self) or not IsValid(self:GetTarget()) then return end
|
||
if not cv_twitch_enabled:GetBool() then return end
|
||
|
||
local phys = self:GetPhysicsObject()
|
||
if not IsValid(phys) then return end
|
||
|
||
if #twitchable_bone_names == 0 then return end
|
||
|
||
local random_bone_name = table_Random(twitchable_bone_names)
|
||
if not random_bone_name then return end
|
||
|
||
-- Get bone index by name
|
||
local bone_index = self:LookupBone(random_bone_name)
|
||
if not bone_index then
|
||
-- print("[Fedhoria Twitch] Bone index not found for name:", random_bone_name)
|
||
return
|
||
end
|
||
|
||
-- We retrieve the physical bone object by its index.
|
||
local phys_bone = self:GetPhysicsObjectNum(bone_index) -- We use GetPhysicsObjectNum to retrieve the physics object for a specific bone.
|
||
|
||
if IsValid(phys_bone) then
|
||
local force = math_Rand(cv_twitch_force_min:GetFloat(), cv_twitch_force_max:GetFloat())
|
||
local torque = VectorRand():GetNormal() * force
|
||
phys_bone:AddAngleVelocity(torque)
|
||
-- print("[Fedhoria Twitch] Applied torque", torque, "to bone", random_bone_name, "(index:", bone_index, ")")
|
||
-- else
|
||
-- print("[Fedhoria Twitch] Could not find a valid physics bone for index:", bone_index, "name:", random_bone_name)
|
||
end
|
||
|
||
self:ScheduleNextTwitch()
|
||
end
|
||
|
||
|
||
--[[-------------------------------------------------------------------------
|
||
Planning the next DoTwitch call
|
||
---------------------------------------------------------------------------]]
|
||
function MODULE:ScheduleNextTwitch()
|
||
if not IsValid(self) then return end
|
||
if not cv_twitch_enabled:GetBool() then return end
|
||
|
||
local timer_name = "Fedhoria_FallingLegs_Twitch_" .. self:EntIndex()
|
||
timer_Remove(timer_name)
|
||
|
||
local delay = math_Rand(cv_twitch_interval_min:GetFloat(), cv_twitch_interval_max:GetFloat())
|
||
|
||
timer_Create(timer_name, delay, 1, function()
|
||
if not IsValid(self) then return end
|
||
self:DoTwitch()
|
||
end)
|
||
end
|
||
|
||
--[[-------------------------------------------------------------------------
|
||
Starting the roll animation
|
||
---------------------------------------------------------------------------]]
|
||
function MODULE:StartAnimationRoll()
|
||
if not cv_anim_roll_enabled:GetBool() then return end
|
||
|
||
local target = self:GetTarget()
|
||
if not IsValid(target) then return end
|
||
|
||
local cur_time = CurTime()
|
||
self.AnimationRollEndTime = cur_time + cv_anim_roll_duration:GetFloat()
|
||
|
||
local seq = self:LookupSequence("Choked_Barnacle")
|
||
if seq and seq > 0 then
|
||
self:ResetSequence(seq)
|
||
self:SetPlaybackRate(cv_anim_roll_playback_rate:GetFloat())
|
||
else
|
||
self.AnimationRollEndTime = 0
|
||
return
|
||
end
|
||
|
||
--target.StartDie = nil
|
||
end
|
||
|
||
function MODULE:Init()
|
||
local seq = self:LookupSequence("Choked_Barnacle")
|
||
if seq then self:ResetSequence(seq) end
|
||
|
||
local target = self:GetTarget()
|
||
|
||
self:SetPlaybackRate(1)
|
||
self.LastCollideTime = 0
|
||
self.LastGroundCollideTime = 0
|
||
target.StartDie = nil
|
||
self.AnimationRollEndTime = 0
|
||
self.StopProcessing = false
|
||
self.LastThink = CurTime()
|
||
self.LastFakeUpCheck = CurTime()
|
||
|
||
self.bullseye = ents.Create("npc_bullseye")
|
||
self.bullseye:SetPos(target:GetPos())
|
||
self.bullseye:SetMoveType(MOVETYPE_OBSERVER)
|
||
self.bullseye:SetModel("models/editor/bullseye.mdl")
|
||
self.bullseye:SetHealth(99999)
|
||
self.bullseye:Spawn()
|
||
self.bullseye:Activate()
|
||
self.bullseye:SetNotSolid(true)
|
||
self.bullseye.npc_rag = target
|
||
|
||
self.dummy_entity = ents.Create(target.class_in_previous_life)
|
||
|
||
for i,ent in ipairs(ents.GetAll()) do
|
||
if !IsValid(ent) or !ent.IsNPC() then continue end
|
||
|
||
if ent:Disposition(self.dummy_entity) == D_HT then
|
||
ent:AddEntityRelationship(self.bullseye, D_HT, -1)
|
||
|
||
print("HATE. HATE. HATE")
|
||
end
|
||
end
|
||
|
||
-- Add damping to all bones.
|
||
local phys = self:GetPhysicsObject()
|
||
if IsValid(phys) then
|
||
local bone_count = phys:GetBoneCount()
|
||
for bone_index = 0, bone_count - 1 do
|
||
local phys_bone = phys:GetBone(bone_index)
|
||
if IsValid(phys_bone) then
|
||
phys_bone:SetDamping(0.8, 0.9) -- Linear and Angular Damping
|
||
end
|
||
end
|
||
end
|
||
|
||
-- запускаем систему дергания
|
||
self:ScheduleNextTwitch()
|
||
end
|
||
|
||
|
||
--[[-------------------------------------------------------------------------
|
||
Think-hook
|
||
---------------------------------------------------------------------------]]
|
||
|
||
function MODULE:Think()
|
||
--if (CurTime() - self.LastThink) < 1 then return end
|
||
print("thinking")
|
||
|
||
local target = self:GetTarget()
|
||
if !IsValid(target) then return end
|
||
|
||
self.LastThink = CurTime()
|
||
|
||
local phys = target:GetPhysicsObject()
|
||
|
||
if target.FakeUp then
|
||
print("fucking it")
|
||
local parent = self.FakeParent
|
||
if !IsValid(parent) then return end
|
||
|
||
--parent:FrameAdvance(FrameTime())
|
||
end
|
||
|
||
if !IsValid(phys) or !phys:IsAsleep() then return end
|
||
|
||
phys:Wake()
|
||
end
|
||
|
||
--[[-------------------------------------------------------------------------
|
||
Physics Collision Hook (FIXED)
|
||
---------------------------------------------------------------------------]]
|
||
function MODULE:PhysicsCollide(data, phys)
|
||
-- Checking the validity of collision data and the presence of HitNormal.
|
||
if not data or not data.HitNormal then
|
||
-- print("[Fedhoria Collide] Invalid collision data received.")
|
||
return
|
||
end
|
||
|
||
if data.HitEntity == self then return end
|
||
|
||
local cur_time = CurTime()
|
||
self.LastCollideTime = cur_time
|
||
|
||
-- Ground collision check (normal points upward)
|
||
if data.HitNormal.z > 0.7 then
|
||
local impact_speed = data.Speed
|
||
|
||
-- print("[Fedhoria Collide] Ground collision detected. Speed:", impact_speed)
|
||
|
||
if cv_anim_roll_enabled:GetBool() and
|
||
impact_speed > cv_anim_roll_impact_threshold:GetFloat() and
|
||
cur_time > self.AnimationRollEndTime and
|
||
cur_time > self.LastGroundCollideTime + cv_anim_roll_min_delay:GetFloat() then
|
||
self:StartAnimationRoll()
|
||
end
|
||
|
||
self.LastGroundCollideTime = cur_time
|
||
end
|
||
end
|
||
|
||
function MODULE:EntityTakeDamage(ent, dmginfo)
|
||
print("I TOOK DAMAGE")
|
||
end
|
||
|
||
|
||
--[[-------------------------------------------------------------------------
|
||
Physics Simulation Hook (FIXED)
|
||
---------------------------------------------------------------------------]]
|
||
function MODULE:PhysicsSimulate(phys, dt)
|
||
local cur_time = CurTime()
|
||
local target = self:GetTarget()
|
||
if not IsValid(target) then self:Remove(); self.bullseye:Remove(); return false end
|
||
|
||
if target.FakeUp then
|
||
print("fucking it")
|
||
local parent = self.FakeParent
|
||
|
||
if !IsValid(parent) then
|
||
self:Remove()
|
||
return false
|
||
end
|
||
|
||
if !self.ModelBoneList then
|
||
self.ModelBoneList = {}
|
||
|
||
local i = 0
|
||
|
||
while i < target:GetBoneCount() do
|
||
table.insert(self.ModelBoneList, target:GetBoneName(i))
|
||
i = i + 1
|
||
end
|
||
end
|
||
|
||
local animation_progress = (CurTime() - self.FakeUpStart) / (self.FakeUpEnd - self.FakeUpStart)
|
||
|
||
--[[util.TraceLine({
|
||
start = parent:GetPos() + Vector(0, 128, 0),
|
||
endpos = parent:GetPos(),
|
||
collisiongroup = COLLISION_GROUP_NPC
|
||
})]]
|
||
|
||
for i,v in pairs(fakeup_bone_down_names) do
|
||
local object = target:GetPhysicsObjectNum(target:TranslateBoneToPhysBone(target:LookupBone(v)))
|
||
|
||
object:SetMass(0.5)
|
||
end
|
||
|
||
for i,v in pairs(fakeup_bone_names) do
|
||
local object = target:GetPhysicsObjectNum(target:TranslateBoneToPhysBone(target:LookupBone(v)))
|
||
local parent_bone = parent:LookupBone(v)
|
||
|
||
if parent_bone == -1 then continue end
|
||
|
||
local parent_bone_matrix = parent:GetBoneMatrix(parent_bone)
|
||
local parent_bone_pos, parent_bone_angle = parent_bone_matrix:GetTranslation(), parent_bone_matrix:GetAngles()
|
||
parent_bone_angle.y = parent_bone_angle.y + 90
|
||
|
||
local shadow_data = {
|
||
secondstoarrive = 0.01,
|
||
pos = LerpVector(animation_progress, object:GetPos(), parent_bone_pos),
|
||
angle = LerpAngle(animation_progress, object:GetAngles(), parent_bone_angle),
|
||
maxspeed = 400,
|
||
maxangular = 2000,
|
||
maxspeeddamp = 60,
|
||
maxangularspeeddamp = 600,
|
||
}
|
||
|
||
-- Can't set position inside physics tick, crashes the game instantly.
|
||
-- Instead, send a shadow for it to follow (I think? I don't know GMod is kinda funky)
|
||
object:ComputeShadowControl(shadow_data)
|
||
|
||
object:Wake()
|
||
object:EnableGravity(false)
|
||
object:Wake()
|
||
|
||
i = i + 1
|
||
end
|
||
|
||
return false
|
||
end
|
||
--print((CurTime() - self.LastPhysProcess) < 0.05)
|
||
--if (CurTime() - self.LastPhysProcess) < (1 /) then return false end())
|
||
|
||
phys:Wake()
|
||
|
||
|
||
self.bullseye:SetPos(target:GetPos())
|
||
--self.bullseye:SetAngles(target:EyeAngles())
|
||
|
||
target.is_npc_corpse = true
|
||
|
||
if !target.StartDie then target.StartDie = cur_time end
|
||
|
||
-- Check for active animation
|
||
if cur_time < self.AnimationRollEndTime then
|
||
--target.StartDie = nil -- Resetting the "death" timer
|
||
return true -- We use standard physics.
|
||
end
|
||
|
||
-- Force multiplier
|
||
local f = target.organism.consciousness
|
||
|
||
local minimum_down_timer = 0
|
||
if target.StartDie then
|
||
minimum_down_timer = math_Clamp((cur_time - target.StartDie) / minimum_down_time:GetFloat(), 0, 1)
|
||
end
|
||
|
||
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 ((CurTime() - self.LastFakeUpCheck) >= 1.0) and (can_unfake:GetBool()) then
|
||
self.LastFakeUpCheck = CurTime()
|
||
|
||
-- This basically checks if the NPC will be in collision with anything when it spawns.
|
||
-- Side note: the mins and maxs values are slightly larger than the default npc_citizen
|
||
-- hitboxes, mostly because I assume that most humanoid NPCs will be around that size.
|
||
local tracehull = util.TraceHull({
|
||
start = target:GetPos(),
|
||
endpos = target:GetPos(),
|
||
mins = Vector(-20, -20, 0),
|
||
maxs = Vector(20, 20, 84),
|
||
mask = MASK_NPCSOLID,
|
||
filter = { target }
|
||
})
|
||
|
||
if (
|
||
(!tracehull.Hit) and
|
||
(minimum_down_timer >= 1.0) and
|
||
(target.class_in_previous_life != nil) and
|
||
!(target.organism.llegamputated or target.organism.rlegamputated or target.organism.larmamputated or target.organism.rarmamputated) and
|
||
(target.organism.pain <= 80) and
|
||
(target.organism.consciousness > 0.65) and
|
||
!target:IsOnFire()
|
||
) then
|
||
local ent = ents.Create(target.class_in_previous_life)
|
||
ent:SetPos(target:GetPos())
|
||
ent:SetModel(target:GetModel())
|
||
ent:SetMaterial(target:GetMaterial())
|
||
|
||
ent:SetSkin(target:GetSkin())
|
||
|
||
if target.citizentype then
|
||
ent:SetKeyValue("citizentype", target.citizentype)
|
||
end
|
||
|
||
for i = 0, target:GetNumBodyGroups() - 1 do
|
||
ent:SetBodygroup(i, target:GetBodygroup(i))
|
||
end
|
||
|
||
ent:Spawn()
|
||
|
||
ent:SetNotSolid(true)
|
||
ent:SetNPCState(NPC_STATE_NONE)
|
||
|
||
timer.Simple(0, function()
|
||
hg.organism.Add(ent)
|
||
table.Merge(ent.organism, target.organism)
|
||
|
||
ent.tourniquets = table.Copy(target.tourniquets)
|
||
ent.bandaged_limbs = table.Copy(target.bandaged_limbs)
|
||
|
||
ent.organism.alive = true
|
||
ent.organism.owner = ent
|
||
|
||
target.organism = nil
|
||
|
||
ent:CallOnRemove("organism", hg.organism.Remove, ent)
|
||
hg.send_bareinfo(ent.organism)
|
||
|
||
ent:SetSkin(target:GetSkin())
|
||
|
||
for i = 0, target:GetNumBodyGroups() - 1 do
|
||
ent:SetBodygroup(i, target:GetBodygroup(i))
|
||
end
|
||
end)
|
||
|
||
target.FakeUp = true
|
||
self.FakeParent = ent
|
||
self.FakeUpTime = unfake_time:GetFloat()
|
||
self.FakeUpEnd = CurTime() + self.FakeUpTime
|
||
self.FakeUpStart = CurTime()
|
||
|
||
ent:SetAngles(Angle(0, math.random(-180, 180), 0))
|
||
|
||
local phys = target:GetPhysicsObject()
|
||
phys:EnableGravity(false)
|
||
target:SetNotSolid(true)
|
||
target:SetMoveType(MOVETYPE_NONE)
|
||
|
||
ent:SetRenderMode(RENDERMODE_NONE)
|
||
|
||
timer.Simple(self.FakeUpTime, function()
|
||
ent:SetNotSolid(false)
|
||
ent:SetNPCState(NPC_STATE_IDLE)
|
||
|
||
ent:SetRenderMode(RENDERMODE_NORMAL)
|
||
|
||
target:Remove()
|
||
|
||
self:Remove()
|
||
end)
|
||
--[[]
|
||
local parent = self.FakeParent
|
||
|
||
if !self.ModelBoneList then
|
||
self.ModelBoneList = {}
|
||
|
||
local i = 0
|
||
|
||
while i < target:GetBoneCount() do
|
||
table.insert(self.ModelBoneList, target:GetBoneName(i))
|
||
i = i + 1
|
||
end
|
||
end
|
||
|
||
for i,v in pairs(self.ModelBoneList) do
|
||
print(v)
|
||
|
||
print("I'm here")
|
||
|
||
local bone = target:TranslateBoneToPhysBone(target:LookupBone(v))
|
||
local object = target:GetPhysicsObjectNum(bone)
|
||
if !IsValid(object) then i = i + 1; continue end
|
||
|
||
print("I'm getting to object")
|
||
|
||
local parent_bone = parent:TranslateBoneToPhysBone(parent:LookupBone(v))
|
||
if parent_bone == nil then i = i + 1; continue end
|
||
|
||
print("I'm getting to parent object")
|
||
|
||
object:Wake()
|
||
|
||
local shadow_data = {
|
||
secondstoarrive = 0.01,
|
||
pos = LerpVector(0.1, object:GetPos(), parent:GetBonePosition(parent_bone)),
|
||
angle = LerpAngle(0.1, object:GetAngles(), parent:GetBoneMatrix(parent_bone):GetAngles()),
|
||
maxspeed = 5000,
|
||
maxangular = 5000,
|
||
maxspeeddamp = 2000,
|
||
maxangularspeeddamp = 2000,
|
||
}
|
||
|
||
object:ComputeShadowControl(shadow_data)
|
||
|
||
--object:SetPos(LerpVector(0.2, object:GetPos(), parent:GetBonePosition(parent_bone)))
|
||
--object:SetAngles(LerpAngle(0.2, object:GetAngles(), parent:GetBoneMatrix(parent_bone):GetAngles()))
|
||
|
||
--object:EnableMotion(false)
|
||
|
||
--object:SetVelocity(Vector())
|
||
object:EnableGravity(false)
|
||
|
||
i = i + 1
|
||
end]]
|
||
|
||
self.bullseye:Remove()
|
||
return false
|
||
end
|
||
end
|
||
|
||
-- Getting up
|
||
-- Don't need to check for consciousness and the like because we've done it already above
|
||
|
||
|
||
local phys_bone_id = phys:GetID()
|
||
|
||
-- Main logic for the root bone
|
||
if (phys_bone_id == 0) then
|
||
-- Water Logic
|
||
if target:WaterLevel() > 0 then
|
||
self.AnimationRollEndTime = 0
|
||
local seq_choked = self:LookupSequence("Choked_Barnacle")
|
||
if seq_choked then self:ResetSequence(seq_choked) end
|
||
return false
|
||
end
|
||
|
||
-- Logic for fall animation
|
||
local vel = phys:GetVelocity()
|
||
local pbr = math_Clamp(vel.z / -600, 0.5, 1.5)
|
||
|
||
if self:GetSequence() ~= self:LookupSequence("Choked_Barnacle") or self:GetPlaybackRate() ~= pbr then
|
||
local seq_idle = self:LookupSequence("Choked_Barnacle")
|
||
if seq_idle then
|
||
self:ResetSequence(seq_idle)
|
||
self:SetPlaybackRate(pbr)
|
||
end
|
||
end
|
||
|
||
-- Fix: Safe vector handling
|
||
local pos = phys:GetPos()
|
||
if not pos then return true, f end -- If `pos` is invalid, do nothing.
|
||
|
||
self.last_pos = self.last_pos or pos
|
||
if not self.last_pos then self.last_pos = pos end
|
||
|
||
local offset_sqr = (pos - self.last_pos):LengthSqr()
|
||
self.last_pos = pos
|
||
|
||
if offset_sqr < (10*10 * dt*dt) then
|
||
target.StartDie = target.StartDie or cur_time
|
||
end
|
||
|
||
-- Physics correction after collision
|
||
local delta_collide = cur_time - self.LastCollideTime
|
||
if (delta_collide < 0.2) then
|
||
return true, 1 * f
|
||
elseif (delta_collide < 1.2) then
|
||
return true, (1 - (delta_collide - 0.2)) * f
|
||
end
|
||
|
||
return true, f
|
||
end
|
||
|
||
-- Logic for other bones
|
||
local delta_collide = cur_time - self.LastCollideTime
|
||
if (delta_collide < 0.2) then
|
||
return true, 1 * f
|
||
elseif (delta_collide < 1.2) then
|
||
return true, (1 - (delta_collide - 0.2)) * f
|
||
end
|
||
return true, f
|
||
end
|
||
|
||
--[[-------------------------------------------------------------------------
|
||
Entity Deletion Hook
|
||
---------------------------------------------------------------------------]]
|
||
function MODULE:OnRemove()
|
||
local timer_name = "Fedhoria_FallingLegs_Twitch_" .. self:EntIndex()
|
||
timer_Remove(timer_name)
|
||
|
||
if IsValid(self.bullseye) then self.bullseye:Remove() end
|
||
end
|
||
|
||
--[[-------------------------------------------------------------------------
|
||
Module registration (if it is part of a module system)
|
||
---------------------------------------------------------------------------]]
|
||
-- Registration Example:
|
||
-- falling_legs_manager:RegisterModule("FedhoriaFallingLegs", MODULE)
|
||
|