Compare commits
2 commits
cca4e204fd
...
eff80ee215
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
eff80ee215 | ||
|
|
af0c52bda5 |
10 changed files with 1932 additions and 0 deletions
19
README.md
19
README.md
|
|
@ -0,0 +1,19 @@
|
||||||
|
# Z-City NPC Integration
|
||||||
|
|
||||||
|
I made this in an angry fugue after desperately trying to improve my frames by creating a more optimized euphoria ragdoll mod. Now that I'm done with that I figured out how tio actually fix my frames, and it was not this addon. However, I have another issue: NPCs with Z-City suck. Like they tank hits like crazy. You could unload a mag on someone's leg and there would be a non-zero chance they would still be perfectly fine. I'd like to turn my optimized Fedhoria mod into a base for a Z-City NPC improvement mod.
|
||||||
|
|
||||||
|
Some ideas:
|
||||||
|
|
||||||
|
- If an enemy breaks a leg, slow them down.
|
||||||
|
- If an enemy breaks an arm, make their aim worse.
|
||||||
|
- Break both legs? Hope you like writhing on the ground for the rest of your life!
|
||||||
|
- Generally, enemies need to be closer to players in terms of fleshiness. Like a shot to the arm should knock them over, that shit hurts yknow.
|
||||||
|
- Enemies should target downed enemies of hostile factions.
|
||||||
|
|
||||||
|
Since theoretically the NPCs use the same health system that players use, these shouldn't be too hard to implement.
|
||||||
|
|
||||||
|
More optimistic ideas:
|
||||||
|
|
||||||
|
- NPCs will back out of combat when injured.
|
||||||
|
- Friendly NPCs can heal each other.
|
||||||
|
- NPCs can heal themselves to a limited degree.
|
||||||
48
autorun/client/fedh_menu.lua
Normal file
48
autorun/client/fedh_menu.lua
Normal file
|
|
@ -0,0 +1,48 @@
|
||||||
|
local function PopulateSBXToolMenu(pnl)
|
||||||
|
pnl:CheckBox("Enabled", "fedhoria_enabled")
|
||||||
|
pnl:ControlHelp("Enable or disable the addon.")
|
||||||
|
|
||||||
|
pnl:CheckBox("Players", "fedhoria_players")
|
||||||
|
pnl:ControlHelp("Enable or disable effect for players.")
|
||||||
|
|
||||||
|
pnl:CheckBox("NPCs", "fedhoria_npcs")
|
||||||
|
pnl:ControlHelp("Enable or disable effect for NPCs.")
|
||||||
|
|
||||||
|
pnl:Help(" ")
|
||||||
|
|
||||||
|
pnl:NumSlider("Stumble time", "fedhoria_stumble_time", 0, 10, 3)
|
||||||
|
pnl:ControlHelp("How long the ragdoll should stumble for.")
|
||||||
|
|
||||||
|
pnl:NumSlider("Die time", "fedhoria_dietime", 0, 10, 3)
|
||||||
|
pnl:ControlHelp("How long before the ragdoll dies after drowning/being still for too long.")
|
||||||
|
|
||||||
|
pnl:NumSlider("Die time variation", "fedhoria_dietime_variation", 0, 10, 2)
|
||||||
|
pnl:ControlHelp("A random number between 0 and the value you choose here will be added to the die timer.")
|
||||||
|
|
||||||
|
pnl:NumSlider("Wound grab chance", "fedhoria_woundgrab_chance", 0, 1, 3)
|
||||||
|
pnl:ControlHelp("The chance the ragdoll will grab it's wound when shot.")
|
||||||
|
|
||||||
|
pnl:NumSlider("Wound grab time", "fedhoria_woundgrab_time", 0, 10, 3)
|
||||||
|
pnl:ControlHelp("How long the ragdoll should hold its wound.")
|
||||||
|
|
||||||
|
pnl:Help(" ")
|
||||||
|
|
||||||
|
pnl:NumSlider("Activation range", "fedhoria_active_range", 0, 5000, 0)
|
||||||
|
pnl:ControlHelp("How close the ragdoll has to be to a player to have euphoria physics")
|
||||||
|
|
||||||
|
pnl:CheckBox("Always activate on player kill", "fedhoria_always_active_on_player_kill")
|
||||||
|
pnl:ControlHelp("If on, all player-killed ragdolls will be activated regardless of other factors.")
|
||||||
|
end
|
||||||
|
|
||||||
|
if engine.ActiveGamemode() == "sandbox" then
|
||||||
|
hook.Add("AddToolMenuCategories", "FedhoriaCategory", function()
|
||||||
|
spawnmenu.AddToolCategory("Utilities", "Fedhoria", "Fedhoria")
|
||||||
|
end)
|
||||||
|
|
||||||
|
hook.Add("PopulateToolMenu", "FedhoriaMenuSettings", function()
|
||||||
|
spawnmenu.AddToolMenuOption("Utilities", "Fedhoria", "FedhoriaSettings", "Settings", "", "", function(pnl)
|
||||||
|
pnl:ClearControls()
|
||||||
|
PopulateSBXToolMenu(pnl)
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
end
|
||||||
1
autorun/server/fedh_init.lua
Normal file
1
autorun/server/fedh_init.lua
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
include("fedhoria.lua")
|
||||||
325
entities/active_ragdoll_controller.lua
Normal file
325
entities/active_ragdoll_controller.lua
Normal file
|
|
@ -0,0 +1,325 @@
|
||||||
|
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
|
||||||
|
|
||||||
|
self:SetModel(self.Module.Model)
|
||||||
|
|
||||||
|
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
|
||||||
219
fedhoria.lua
Normal file
219
fedhoria.lua
Normal file
|
|
@ -0,0 +1,219 @@
|
||||||
|
include("fedhoria/modules.lua")
|
||||||
|
|
||||||
|
local enabled = CreateConVar("fedhoria_enabled", 1, bit.bor(FCVAR_ARCHIVE, FCVAR_REPLICATED))
|
||||||
|
local players = CreateConVar("fedhoria_players", 1, bit.bor(FCVAR_ARCHIVE, FCVAR_REPLICATED))
|
||||||
|
local npcs = CreateConVar("fedhoria_npcs", 1, bit.bor(FCVAR_ARCHIVE, FCVAR_REPLICATED))
|
||||||
|
local active_range = CreateConVar("fedhoria_active_range", 300, bit.bor(FCVAR_ARCHIVE, FCVAR_REPLICATED))
|
||||||
|
local always_active_on_player_kill = CreateConVar("fedhoria_always_active_on_player_kill", 1, bit.bor(FCVAR_ARCHIVE, FCVAR_REPLICATED))
|
||||||
|
|
||||||
|
local last_dmgpos = {}
|
||||||
|
local last_hitgroup = {}
|
||||||
|
local last_attacker = {}
|
||||||
|
|
||||||
|
hook.Add("CreateEntityRagdoll", "Fedhoria", function(ent, ragdoll)
|
||||||
|
if (!enabled:GetBool() or !npcs:GetBool()) then return end
|
||||||
|
local dmgpos = last_dmgpos[ent]
|
||||||
|
|
||||||
|
if last_hitgroup[ent] == HITGROUP_HEAD then return end
|
||||||
|
|
||||||
|
local active_by_default = false
|
||||||
|
|
||||||
|
if always_active_on_player_kill:GetBool() then
|
||||||
|
if last_attacker[ent] and last_attacker[ent]:IsPlayer() then active_by_default = true end
|
||||||
|
end
|
||||||
|
|
||||||
|
if !active_by_default then
|
||||||
|
local entity_position = ragdoll:GetPos()
|
||||||
|
local in_range = false
|
||||||
|
local players = player.GetAll()
|
||||||
|
|
||||||
|
for i, loop_player in pairs(players) do
|
||||||
|
local player_position = loop_player:GetPos()
|
||||||
|
|
||||||
|
local distance = entity_position:Distance(player_position)
|
||||||
|
|
||||||
|
if distance <= active_range:GetFloat() then
|
||||||
|
in_range = true
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if !in_range then return end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
print(dmgpos)
|
||||||
|
|
||||||
|
local phys_bone, lpos
|
||||||
|
|
||||||
|
if dmgpos then
|
||||||
|
phys_bone = ragdoll:GetClosestPhysBone(dmgpos)
|
||||||
|
if phys_bone then
|
||||||
|
local phys = ragdoll:GetPhysicsObjectNum(phys_bone)
|
||||||
|
lpos = phys:WorldToLocal(dmgpos)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
timer.Simple(0, function()
|
||||||
|
if !IsValid(ragdoll) then return end
|
||||||
|
|
||||||
|
fedhoria.StartModule(ragdoll, "stumble_legs", phys_bone, lpos)
|
||||||
|
last_dmgpos[ent] = nil
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
hook.Add("EntityTakeDamage", "Fedhoria", function(ent, dmginfo)
|
||||||
|
if (!enabled:GetBool() or !npcs:GetBool()) then return end
|
||||||
|
if (!ent:IsNPC() or dmginfo:GetDamage() < ent:Health()) then return end
|
||||||
|
|
||||||
|
last_dmgpos[ent] = dmginfo:GetDamagePosition()
|
||||||
|
last_attacker[ent] = dmginfo:GetAttacker()
|
||||||
|
end)
|
||||||
|
|
||||||
|
hook.Add("ScaleNPCDamage", "Fedhoria", function(npc, hitgroup, dmginfo)
|
||||||
|
if not IsValid(npc) then return end
|
||||||
|
last_hitgroup[npc] = hitgroup
|
||||||
|
print(last_hitgroup[npc] == HITGROUP_HEAD)
|
||||||
|
end)
|
||||||
|
|
||||||
|
--[[hook.Add("OnNPCKilled", "Fedhoria", function(ent, attacker, inflictor)
|
||||||
|
if (!enabled:GetBool() or !npcs:GetBool()) then return end
|
||||||
|
if (!ent:IsNPC() or dmginfo:GetDamage() < ent:Health()) then return end
|
||||||
|
|
||||||
|
last_dmgpos[ent] = dmginfo:GetDamagePosition()
|
||||||
|
end)]]
|
||||||
|
|
||||||
|
local once = true
|
||||||
|
|
||||||
|
--RagMod/TTT support
|
||||||
|
hook.Add("OnEntityCreated", "Fedhoria", function(ent)
|
||||||
|
--If RagMod isn't installed remove this hook
|
||||||
|
if once then
|
||||||
|
once = nil
|
||||||
|
if (!RMA_Ragdolize and !CORPSE) then
|
||||||
|
hook.Remove("OnEntityCreated", "Fedhoria")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
--these hooks fucks shit up
|
||||||
|
if RMA_Ragdolize then
|
||||||
|
hook.Remove( "PlayerDeath", "RM_PlayerDies")
|
||||||
|
hook.Add( "PostPlayerDeath", "RemoveRagdoll", function(ply)
|
||||||
|
if IsValid(ply.RM_Ragdoll) then
|
||||||
|
SafeRemoveEntity(ply:GetRagdollEntity())
|
||||||
|
ply:SpectateEntity(ply.RM_Ragdoll)
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if (!enabled:GetBool() or !players:GetBool() or !ent:IsRagdoll()) then return end
|
||||||
|
timer.Simple(0, function()
|
||||||
|
if !IsValid(ent) then return end
|
||||||
|
if CORPSE then
|
||||||
|
local ply = ent:GetDTEntity(CORPSE.dti.ENT_PLAYER)
|
||||||
|
if (IsValid(ply) and ply:IsPlayer()) then
|
||||||
|
fedhoria.StartModule(ent, "stumble_legs")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
end
|
||||||
|
for _, ply in ipairs(player.GetAll()) do
|
||||||
|
if (ply.RM_IsRagdoll and ply.RM_Ragdoll == ent) then
|
||||||
|
fedhoria.StartModule(ent, "stumble_legs")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
local PLAYER = FindMetaTable("Player")
|
||||||
|
|
||||||
|
local oldCreateRagdoll = PLAYER.CreateRagdoll
|
||||||
|
|
||||||
|
local dolls = {}
|
||||||
|
|
||||||
|
local function CreateRagdoll(self)
|
||||||
|
SafeRemoveEntity(dolls[self])
|
||||||
|
|
||||||
|
local ragdoll = ents.Create("prop_ragdoll")
|
||||||
|
ragdoll:SetModel(self:GetModel())
|
||||||
|
ragdoll:SetPos(self:GetPos())
|
||||||
|
ragdoll:SetAngles(self:GetAngles())
|
||||||
|
ragdoll:Spawn()
|
||||||
|
|
||||||
|
ragdoll:SetSkin(self:GetSkin())
|
||||||
|
|
||||||
|
for i = 0, self:GetNumBodyGroups() - 1 do
|
||||||
|
ragdoll:SetBodygroup(i, self:GetBodygroup(i))
|
||||||
|
end
|
||||||
|
|
||||||
|
for i = 0, ragdoll:GetPhysicsObjectCount()-1 do
|
||||||
|
local phys = ragdoll:GetPhysicsObjectNum(i)
|
||||||
|
local bone = ragdoll:TranslatePhysBoneToBone(i)
|
||||||
|
local matrix = self:GetBoneMatrix(bone)
|
||||||
|
local pos, ang = matrix:GetTranslation(), matrix:GetAngles()--self:GetBonePosition(bone)
|
||||||
|
phys:SetPos(pos)
|
||||||
|
phys:SetAngles(ang)
|
||||||
|
phys:SetVelocity(self:GetVelocity())
|
||||||
|
end
|
||||||
|
|
||||||
|
self:SpectateEntity(ragdoll)
|
||||||
|
self:Spectate(OBS_MODE_CHASE)
|
||||||
|
|
||||||
|
dolls[self] = ragdoll
|
||||||
|
end
|
||||||
|
|
||||||
|
local oldGetRagdollEntity = PLAYER.GetRagdollEntity
|
||||||
|
|
||||||
|
local function GetRagdollEntity(self)
|
||||||
|
return dolls[self] or NULL
|
||||||
|
end
|
||||||
|
|
||||||
|
if enabled:GetBool() then
|
||||||
|
PLAYER.CreateRagdoll = CreateRagdoll
|
||||||
|
PLAYER.GetRagdollEntity = GetRagdollEntity
|
||||||
|
end
|
||||||
|
|
||||||
|
cvars.AddChangeCallback("fedhoria_enabled", function(name, old, new)
|
||||||
|
if (new == "1") then
|
||||||
|
if players:GetBool() then
|
||||||
|
PLAYER.CreateRagdoll = CreateRagdoll
|
||||||
|
PLAYER.GetRagdollEntity = GetRagdollEntity
|
||||||
|
end
|
||||||
|
else
|
||||||
|
PLAYER.CreateRagdoll = oldCreateRagdoll
|
||||||
|
PLAYER.GetRagdollEntity = oldGetRagdollEntity
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
cvars.AddChangeCallback("fedhoria_players", function(name, old, new)
|
||||||
|
if (new == "1") then
|
||||||
|
if enabled:GetBool() then
|
||||||
|
if (debug.getinfo(PLAYER.CreateRagdoll).short_src == "[C]") then
|
||||||
|
PLAYER.CreateRagdoll = CreateRagdoll
|
||||||
|
PLAYER.GetRagdollEntity = GetRagdollEntity
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
PLAYER.CreateRagdoll = oldCreateRagdoll
|
||||||
|
PLAYER.GetRagdollEntity = oldGetRagdollEntity
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
hook.Add("PostPlayerDeath", "Fedhoria", function(ply)
|
||||||
|
if (!enabled:GetBool() or !players:GetBool()) then return end
|
||||||
|
timer.Simple(0, function()
|
||||||
|
if !IsValid(ply) then return end
|
||||||
|
local ragdoll = ply:GetRagdollEntity()
|
||||||
|
if (IsValid(ragdoll) and ragdoll:IsRagdoll()) then
|
||||||
|
fedhoria.StartModule(ragdoll, "stumble_legs")
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
--RagMod Reworked support
|
||||||
|
|
||||||
|
hook.Add("RM_RagdollReady", "Fedhoria", function(ragdoll)
|
||||||
|
if IsValid(ragdoll) then
|
||||||
|
fedhoria.StartModule(ragdoll, "stumble_legs")
|
||||||
|
end
|
||||||
|
end)
|
||||||
117
fedhoria/modules.lua
Normal file
117
fedhoria/modules.lua
Normal file
|
|
@ -0,0 +1,117 @@
|
||||||
|
fedhoria = {}
|
||||||
|
|
||||||
|
local modules = {}
|
||||||
|
|
||||||
|
function fedhoria.GetModule(name)
|
||||||
|
if modules[name] then
|
||||||
|
return modules[name]
|
||||||
|
end
|
||||||
|
local path = "fedhoria/modules/"..name..".lua"
|
||||||
|
if !file.Exists(path, "LUA") then
|
||||||
|
print("fedhoria.GetModule failed, couldn't find module '"..name.."'")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
local MODULE_old = MODULE
|
||||||
|
MODULE = {}
|
||||||
|
include(path)
|
||||||
|
modules[name] = MODULE
|
||||||
|
MODULE = MODULE_old
|
||||||
|
|
||||||
|
return modules[name]
|
||||||
|
end
|
||||||
|
|
||||||
|
function fedhoria.GetModuleList()
|
||||||
|
return modules
|
||||||
|
end
|
||||||
|
|
||||||
|
function fedhoria.StartModule(ent, name, ...)
|
||||||
|
if (!modules[name] and !fedhoria.GetModule(name)) then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
local contr = ents.Create("active_ragdoll_controller")
|
||||||
|
contr:SetTarget(ent)
|
||||||
|
contr:SetModule(modules[name])
|
||||||
|
contr:SetInitParams(...)
|
||||||
|
contr:Spawn()
|
||||||
|
|
||||||
|
return contr
|
||||||
|
end
|
||||||
|
|
||||||
|
--preload modules
|
||||||
|
for _, file_name in pairs(file.Find("fedhoria/modules/*.lua", "LUA")) do
|
||||||
|
fedhoria.GetModule(file_name:sub(1, -5))
|
||||||
|
end
|
||||||
|
|
||||||
|
local ENTITY = FindMetaTable("Entity")
|
||||||
|
|
||||||
|
--TODO: make this actually take mass into account
|
||||||
|
function ENTITY:GetMassCenter()
|
||||||
|
local center = Vector(0, 0, 0)
|
||||||
|
local mass = 0
|
||||||
|
local count = self:GetPhysicsObjectCount()
|
||||||
|
for phys_bone = 0, count - 1 do
|
||||||
|
local phys = self:GetPhysicsObjectNum(phys_bone)
|
||||||
|
local m = phys:GetMass()
|
||||||
|
mass = mass * m
|
||||||
|
--center:Add(phys:LocalToWorld(phys:GetMassCenter()) * m)
|
||||||
|
center:Add(phys:LocalToWorld(phys:GetMassCenter()))
|
||||||
|
end
|
||||||
|
--center:Div(mass)
|
||||||
|
center:Div(count)
|
||||||
|
return center
|
||||||
|
end
|
||||||
|
|
||||||
|
local PCOLLIDE_CACHE = {}
|
||||||
|
|
||||||
|
local vec_max = Vector(1, 1, 1) * 4
|
||||||
|
local vec_min = -vec_max
|
||||||
|
|
||||||
|
function ENTITY:GetClosestPhysBone(pos)
|
||||||
|
local mdl = self:GetModel()
|
||||||
|
local collides = PCOLLIDE_CACHE[mdl]
|
||||||
|
if !collides then
|
||||||
|
PCOLLIDE_CACHE[mdl] = CreatePhysCollidesFromModel(mdl)
|
||||||
|
collides = PCOLLIDE_CACHE[mdl]
|
||||||
|
end
|
||||||
|
|
||||||
|
if !collides then return end
|
||||||
|
|
||||||
|
local closest_bone
|
||||||
|
local dist = math.huge
|
||||||
|
|
||||||
|
for phys_bone = 0, self:GetPhysicsObjectCount() - 1 do
|
||||||
|
local phys = self:GetPhysicsObjectNum(phys_bone)
|
||||||
|
local collide = collides[phys_bone + 1]
|
||||||
|
if IsValid(collide) then
|
||||||
|
local phys_pos = phys:GetPos()
|
||||||
|
local phys_ang = phys:GetAngles()
|
||||||
|
local lpos = phys:WorldToLocal(pos)
|
||||||
|
local hitpos, _, d = collide:TraceBox(phys_pos, phys_ang, pos, pos, vec_min, vec_max)
|
||||||
|
if hitpos then
|
||||||
|
if (d < dist) then
|
||||||
|
dist = d
|
||||||
|
closest_bone = phys_bone
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return closest_bone
|
||||||
|
end
|
||||||
|
|
||||||
|
local PHYS = FindMetaTable("PhysObj")
|
||||||
|
|
||||||
|
function PHYS:GetID()
|
||||||
|
local ent = self:GetEntity()
|
||||||
|
for phys_bone = 0, ent:GetPhysicsObjectCount() - 1 do
|
||||||
|
if (ent:GetPhysicsObjectNum(phys_bone) == self) then
|
||||||
|
return phys_bone
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return -1
|
||||||
|
end
|
||||||
|
|
||||||
|
function PHYS:GetAABBCenter()
|
||||||
|
local min, max = self:GetAABB()
|
||||||
|
return (min + max) / 2
|
||||||
|
end
|
||||||
35
fedhoria/modules/example.lua
Normal file
35
fedhoria/modules/example.lua
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
MODULE.Model = "models/police.mdl"
|
||||||
|
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"
|
||||||
|
}
|
||||||
|
|
||||||
|
function MODULE:Init()
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
function MODULE:Think()
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
function MODULE:PhysicsCollide(ent, data)
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
function MODULE:PhysicsSimulate(phys, dt)
|
||||||
|
|
||||||
|
end
|
||||||
331
fedhoria/modules/falling_legs.lua
Normal file
331
fedhoria/modules/falling_legs.lua
Normal file
|
|
@ -0,0 +1,331 @@
|
||||||
|
--[[-------------------------------------------------------------------------
|
||||||
|
Скрипт для управления поведением падающих частей тела NPC (например, ног)
|
||||||
|
Включает эффекты дергания и анимацию переката при ударе.
|
||||||
|
Версия 2: Исправлены ошибки GetGameData и HitNormal.
|
||||||
|
---------------------------------------------------------------------------]]
|
||||||
|
|
||||||
|
MODULE = {}
|
||||||
|
|
||||||
|
MODULE.Model = "models/police.mdl" -- Модель, к которой применяется скрипт (вероятно, не используется напрямую в этом коде)
|
||||||
|
|
||||||
|
-- Список костей, которые МОГУТ быть затронуты физикой или анимацией.
|
||||||
|
-- Для дергания будут использоваться только кости ног из этого списка.
|
||||||
|
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"
|
||||||
|
}
|
||||||
|
|
||||||
|
-- Список костей, к которым БУДЕТ применяться эффект дергания.
|
||||||
|
-- Отфильтровано из MODULE.BoneList, чтобы исключить таз и другие нежелательные кости.
|
||||||
|
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 math_Clamp = math.Clamp
|
||||||
|
local math_Rand = math.Rand
|
||||||
|
local math_random = math.random
|
||||||
|
local table_Random = table.Random -- Используем table.Random для выбора случайного элемента
|
||||||
|
local IsValid = IsValid -- Локальная копия IsValid
|
||||||
|
local CurTime = CurTime -- Локальная копия CurTime
|
||||||
|
local timer_Create = timer.Create
|
||||||
|
local timer_Remove = timer.Remove
|
||||||
|
local VectorRand = VectorRand
|
||||||
|
|
||||||
|
--[[-------------------------------------------------------------------------
|
||||||
|
Конвары (Настройки)
|
||||||
|
---------------------------------------------------------------------------]]
|
||||||
|
-- Подергивание
|
||||||
|
local cv_twitch_enabled = CreateConVar("fedhoria_falling_twitch_enabled", "1", {FCVAR_ARCHIVE, FCVAR_REPLICATED}, "Включить дергание для падающих ног")
|
||||||
|
local cv_twitch_interval_min = CreateConVar("fedhoria_falling_twitch_interval_min", "3", {FCVAR_ARCHIVE, FCVAR_REPLICATED}, "Минимальный интервал между дерганиями (сек)")
|
||||||
|
local cv_twitch_interval_max = CreateConVar("fedhoria_falling_twitch_interval_max", "6", {FCVAR_ARCHIVE, FCVAR_REPLICATED}, "Максимальный интервал между дерганиями (сек)")
|
||||||
|
local cv_twitch_force_min = CreateConVar("fedhoria_falling_twitch_min", "100", {FCVAR_ARCHIVE, FCVAR_REPLICATED}, "Мин. угловая скорость для дергания")
|
||||||
|
local cv_twitch_force_max = CreateConVar("fedhoria_falling_twitch_max", "250", {FCVAR_ARCHIVE, FCVAR_REPLICATED}, "Макс. угловая скорость для дергания")
|
||||||
|
|
||||||
|
-- Перекат
|
||||||
|
local cv_anim_roll_enabled = CreateConVar("fedhoria_falling_anim_roll_enabled", "1", {FCVAR_ARCHIVE, FCVAR_REPLICATED}, "Включить анимацию при ударе о землю")
|
||||||
|
local cv_anim_roll_min_delay = CreateConVar("fedhoria_falling_anim_roll_min_delay", "0.5", {FCVAR_ARCHIVE, FCVAR_REPLICATED}, "Мин. задержка перед возможной анимацией после удара (сек)")
|
||||||
|
local cv_anim_roll_duration = CreateConVar("fedhoria_falling_anim_roll_duration", "3.5", {FCVAR_ARCHIVE, FCVAR_REPLICATED}, "Продолжительность принудительной анимации (сек)")
|
||||||
|
local cv_anim_roll_impact_threshold = CreateConVar("fedhoria_falling_anim_roll_impact_threshold", "300", {FCVAR_ARCHIVE, FCVAR_REPLICATED}, "Мин. скорость удара для запуска анимации")
|
||||||
|
local cv_anim_roll_playback_rate = CreateConVar("fedhoria_falling_anim_roll_playback_rate", "3.0", {FCVAR_ARCHIVE, FCVAR_REPLICATED}, "Скорость воспроизведения анимации")
|
||||||
|
|
||||||
|
-- Время жизни объекта после остановки
|
||||||
|
local die_time = CreateConVar("fedhoria_dietime", 5, {FCVAR_ARCHIVE, FCVAR_REPLICATED})
|
||||||
|
local die_time_variation = CreateConVar("fedhoria_dietime_variation", 3, {FCVAR_ARCHIVE, FCVAR_REPLICATED})
|
||||||
|
|
||||||
|
--[[-------------------------------------------------------------------------
|
||||||
|
Функция дергания
|
||||||
|
Применяет случайный импульс к одной из костей ног.
|
||||||
|
---------------------------------------------------------------------------]]
|
||||||
|
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
|
||||||
|
|
||||||
|
-- Получаем индекс кости по имени
|
||||||
|
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
|
||||||
|
|
||||||
|
-- Получаем физический объект кости по ее индексу
|
||||||
|
local phys_bone = self:GetPhysicsObjectNum(bone_index) -- Используем GetPhysicsObjectNum для получения физ. объекта конкретной кости
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
|
||||||
|
--[[-------------------------------------------------------------------------
|
||||||
|
Планирование следующего вызова DoTwitch
|
||||||
|
---------------------------------------------------------------------------]]
|
||||||
|
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
|
||||||
|
|
||||||
|
--[[-------------------------------------------------------------------------
|
||||||
|
Запуск анимации переката
|
||||||
|
---------------------------------------------------------------------------]]
|
||||||
|
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
|
||||||
|
|
||||||
|
--self.StartDie = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
function MODULE:Init()
|
||||||
|
local seq = self:LookupSequence("Choked_Barnacle")
|
||||||
|
if seq then self:ResetSequence(seq) end
|
||||||
|
self:SetPlaybackRate(1)
|
||||||
|
self.LastCollideTime = 0
|
||||||
|
self.LastGroundCollideTime = 0
|
||||||
|
self.StartDie = nil
|
||||||
|
self.AnimationRollEndTime = 0
|
||||||
|
|
||||||
|
-- Добавляем демпфирование для всех костей
|
||||||
|
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) -- Линейное и угловое демпфирование
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- запускаем систему дергания
|
||||||
|
self:ScheduleNextTwitch()
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--[[-------------------------------------------------------------------------
|
||||||
|
Think-хук
|
||||||
|
---------------------------------------------------------------------------]]
|
||||||
|
function MODULE:Think()
|
||||||
|
-- Место для дополнительной логики
|
||||||
|
end
|
||||||
|
|
||||||
|
--[[-------------------------------------------------------------------------
|
||||||
|
Хук столкновения физики (ИСПРАВЛЕН)
|
||||||
|
---------------------------------------------------------------------------]]
|
||||||
|
function MODULE:PhysicsCollide(data, phys)
|
||||||
|
-- Проверяем валидность данных столкновения и наличие 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
|
||||||
|
|
||||||
|
-- Проверка на столкновение с землей (нормаль смотрит вверх)
|
||||||
|
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:PhysicsSimulate(phys, dt)
|
||||||
|
local cur_time = CurTime()
|
||||||
|
local target = self:GetTarget()
|
||||||
|
if not IsValid(target) then self:Remove(); return false end
|
||||||
|
|
||||||
|
-- проверка на активную анимацию
|
||||||
|
if cur_time < self.AnimationRollEndTime then
|
||||||
|
--self.StartDie = nil -- сбрасываем таймер "смерти"
|
||||||
|
return true -- используем стандартную физику
|
||||||
|
end
|
||||||
|
|
||||||
|
-- логика для таймера исчезновения
|
||||||
|
local f = 1 -- множитель силы (по умолчанию 1)
|
||||||
|
if self.StartDie then
|
||||||
|
f = math_Clamp(1 - (cur_time - self.StartDie) / die_time:GetFloat(), 0, 1)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- поддержка для RagMod
|
||||||
|
if ragmod and ragmod:IsRagmodRagdoll(target) then
|
||||||
|
local owner = target:GetOwningPlayer()
|
||||||
|
f = (IsValid(owner) and owner:Alive()) and 1 or 0
|
||||||
|
--self.StartDie = nil
|
||||||
|
|
||||||
|
if f <= 0 then self.AnimationRollEndTime = 0 end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- удаление, если истекло время жизни
|
||||||
|
if (f <= 0) then
|
||||||
|
self:Remove()
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
local phys_bone_id = phys:GetID()
|
||||||
|
|
||||||
|
-- основная логика для корневой кости
|
||||||
|
if (phys_bone_id == 0) then
|
||||||
|
-- логика для воды
|
||||||
|
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
|
||||||
|
|
||||||
|
-- логика для анимации падения
|
||||||
|
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
|
||||||
|
|
||||||
|
-- фикс: безопасная работа с векторами
|
||||||
|
local pos = phys:GetPos()
|
||||||
|
if not pos then return true, f end -- если pos невалиден, ничего не делаем
|
||||||
|
|
||||||
|
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) and not (ragmod and ragmod:IsRagmodRagdoll(target))) then
|
||||||
|
self.StartDie = self.StartDie or cur_time
|
||||||
|
else
|
||||||
|
--self.StartDie = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
-- коррекция физики после столкновения
|
||||||
|
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
|
||||||
|
|
||||||
|
-- логика для других костей
|
||||||
|
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
|
||||||
|
|
||||||
|
--[[-------------------------------------------------------------------------
|
||||||
|
Хук удаления сущности
|
||||||
|
---------------------------------------------------------------------------]]
|
||||||
|
function MODULE:OnRemove()
|
||||||
|
local timer_name = "Fedhoria_FallingLegs_Twitch_" .. self:EntIndex()
|
||||||
|
timer_Remove(timer_name)
|
||||||
|
end
|
||||||
|
|
||||||
|
--[[-------------------------------------------------------------------------
|
||||||
|
Регистрация модуля (если это часть системы модулей)
|
||||||
|
---------------------------------------------------------------------------]]
|
||||||
|
-- Пример регистрации:
|
||||||
|
-- falling_legs_manager:RegisterModule("FedhoriaFallingLegs", MODULE)
|
||||||
|
|
||||||
283
fedhoria/modules/falling_torso.lua
Normal file
283
fedhoria/modules/falling_torso.lua
Normal file
|
|
@ -0,0 +1,283 @@
|
||||||
|
--здесь был BRAT.
|
||||||
|
|
||||||
|
MODULE = {}
|
||||||
|
|
||||||
|
MODULE.Model = "models/police.mdl"
|
||||||
|
MODULE.BoneList =
|
||||||
|
{
|
||||||
|
"ValveBiped.Bip01_Pelvis",
|
||||||
|
"ValveBiped.Bip01_Spine2",
|
||||||
|
"ValveBiped.Bip01_Head1",
|
||||||
|
"ValveBiped.Bip01_R_Upperarm",
|
||||||
|
"ValveBiped.Bip01_R_Forearm",
|
||||||
|
"ValveBiped.Bip01_L_Upperarm",
|
||||||
|
"ValveBiped.Bip01_L_Forearm",
|
||||||
|
}
|
||||||
|
|
||||||
|
local math_Clamp = math.Clamp
|
||||||
|
local math_Rand = math.Rand
|
||||||
|
local math_random = math.random
|
||||||
|
local table_HasValue = table.HasValue
|
||||||
|
|
||||||
|
local hand_offset = Vector(2, 0, 0)
|
||||||
|
|
||||||
|
--twtiching
|
||||||
|
local cv_twitch_enabled = CreateConVar("fedhoria_falling_twitch_enabled", "1", {FCVAR_ARCHIVE, FCVAR_REPLICATED}, "Включить дергание для падающих торсов")
|
||||||
|
local cv_twitch_interval_min = CreateConVar("fedhoria_falling_twitch_interval_min", "3", {FCVAR_ARCHIVE, FCVAR_REPLICATED}, "Минимальный интервал между дерганиями (сек)")
|
||||||
|
local cv_twitch_interval_max = CreateConVar("fedhoria_falling_twitch_interval_max", "6", {FCVAR_ARCHIVE, FCVAR_REPLICATED}, "Максимальный интервал между дерганиями (сек)")
|
||||||
|
local cv_twitch_force_min = CreateConVar("fedhoria_falling_twitch_min", "100", {FCVAR_ARCHIVE, FCVAR_REPLICATED}, "Мин. угловая скорость для дергания")
|
||||||
|
local cv_twitch_force_max = CreateConVar("fedhoria_falling_twitch_max", "250", {FCVAR_ARCHIVE, FCVAR_REPLICATED}, "Макс. угловая скорость для дергания")
|
||||||
|
local ignore_bone_indices_twitch = {0} -- игнор нахуй
|
||||||
|
|
||||||
|
--перекат ебаный
|
||||||
|
local cv_anim_roll_enabled = CreateConVar("fedhoria_falling_anim_roll_enabled", "1", {FCVAR_ARCHIVE, FCVAR_REPLICATED}, "Включить анимацию 'idleonfire' при ударе о землю")
|
||||||
|
local cv_anim_roll_min_delay = CreateConVar("fedhoria_falling_anim_roll_min_delay", "0.5", {FCVAR_ARCHIVE, FCVAR_REPLICATED}, "Мин. задержка перед возможной анимацией после удара (сек)")
|
||||||
|
local cv_anim_roll_duration = CreateConVar("fedhoria_falling_anim_roll_duration", "3.5", {FCVAR_ARCHIVE, FCVAR_REPLICATED}, "Продолжительность принудительной анимации 'idleonfire' (сек)")
|
||||||
|
local cv_anim_roll_impact_threshold = CreateConVar("fedhoria_falling_anim_roll_impact_threshold", "300", {FCVAR_ARCHIVE, FCVAR_REPLICATED}, "Мин. скорость удара для запуска анимации")
|
||||||
|
local cv_anim_roll_playback_rate = CreateConVar("fedhoria_falling_anim_roll_playback_rate", "3.0", {FCVAR_ARCHIVE, FCVAR_REPLICATED}, "Скорость воспроизведения анимации 'idleonfire'")
|
||||||
|
|
||||||
|
local die_time = CreateConVar("fedhoria_dietime", 5, {FCVAR_ARCHIVE, FCVAR_REPLICATED})
|
||||||
|
local die_time_variation = CreateConVar("fedhoria_dietime_variation", 3, {FCVAR_ARCHIVE, FCVAR_REPLICATED})
|
||||||
|
|
||||||
|
|
||||||
|
-- адно дергание
|
||||||
|
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
|
||||||
|
|
||||||
|
local bone_count = phys:GetBoneCount()
|
||||||
|
if bone_count <= 1 then return end -- нет костей для дергания (кроме, возможно, корневой)
|
||||||
|
|
||||||
|
local phys_bone = nil
|
||||||
|
local attempts = 0
|
||||||
|
local bone_index = -1 -- есл нету кости то иди нахуй
|
||||||
|
|
||||||
|
-- нахуй игнорируем кости, которые не нужны для дергания
|
||||||
|
repeat
|
||||||
|
bone_index = math_random(0, bone_count - 1)
|
||||||
|
if not table_HasValue(ignore_bone_indices_twitch, bone_index) then
|
||||||
|
phys_bone = phys:GetBone(bone_index)
|
||||||
|
end
|
||||||
|
attempts = attempts + 1
|
||||||
|
until IsValid(phys_bone) or attempts > 20 -- мало попытов но если не нашли то идем нахуй
|
||||||
|
|
||||||
|
|
||||||
|
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", bone_index)
|
||||||
|
-- else
|
||||||
|
-- print("[Fedhoria Twitch] Could not find a valid bone to twitch after", attempts, "attempts.")
|
||||||
|
end
|
||||||
|
|
||||||
|
-- следующее дергание
|
||||||
|
self:ScheduleNextTwitch()
|
||||||
|
end
|
||||||
|
|
||||||
|
-- следующая дрочка
|
||||||
|
function MODULE:ScheduleNextTwitch()
|
||||||
|
if not IsValid(self) then return end
|
||||||
|
if not cv_twitch_enabled:GetBool() then return end
|
||||||
|
|
||||||
|
local timer_name = "Fedhoria_FallingTorso_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)
|
||||||
|
-- print("[Fedhoria Twitch] Scheduled next twitch in", delay, "seconds.") -- ебаная отладка
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
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("idleonfire")
|
||||||
|
if seq then
|
||||||
|
self:ResetSequence(seq)
|
||||||
|
self:SetPlaybackRate(cv_anim_roll_playback_rate:GetFloat())
|
||||||
|
else
|
||||||
|
print("[Fedhoria AnimRoll ERROR] Sequence 'idleonfire' not found!")
|
||||||
|
self.AnimationRollEndTime = 0
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
self.StartDie = nil -- блять я заебался с этой хуйней
|
||||||
|
-- print("[Fedhoria AnimRoll] StartAnimationRoll initiated. Speed:", cv_anim_roll_playback_rate:GetFloat(), "Duration:", cv_anim_roll_duration:GetFloat(), "End Time:", self.AnimationRollEndTime) -- Debug
|
||||||
|
end
|
||||||
|
|
||||||
|
function MODULE:Init()
|
||||||
|
local seq = self:LookupSequence("idleonfire")
|
||||||
|
if seq then self:ResetSequence(seq) end
|
||||||
|
self:SetPlaybackRate(1)
|
||||||
|
self.LastCollideTime = 0
|
||||||
|
self.LastGroundCollideTime = 0
|
||||||
|
self.StartDie = nil
|
||||||
|
self.AnimationRollEndTime = 0
|
||||||
|
|
||||||
|
-- идте нахуй с этой хуйней
|
||||||
|
self:ScheduleNextTwitch()
|
||||||
|
end
|
||||||
|
|
||||||
|
function MODULE:Think()
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
function MODULE:PhysicsCollide(ent, data)
|
||||||
|
|
||||||
|
if data.HitEntity == self then return end
|
||||||
|
|
||||||
|
-- if data.HitEntity == self:GetTarget() then return end
|
||||||
|
|
||||||
|
local cur_time = CurTime()
|
||||||
|
self.LastCollideTime = cur_time
|
||||||
|
|
||||||
|
-- пизда, если это не земля
|
||||||
|
if (data.HitNormal.z > 0.7) then
|
||||||
|
local impact_speed = data.Speed
|
||||||
|
-- print("[Fedhoria Collide] Ground impact detected. Speed:", impact_speed, "Threshold:", cv_anim_roll_impact_threshold:GetFloat()) -- Debug
|
||||||
|
-- перекат ебаный
|
||||||
|
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 -- минимальная задержка перед идте нахуй
|
||||||
|
|
||||||
|
-- print("[Fedhoria AnimRoll] Ground Impact Threshold met. Speed:", impact_speed) -- Debug
|
||||||
|
self:StartAnimationRoll()
|
||||||
|
end
|
||||||
|
self.LastGroundCollideTime = cur_time
|
||||||
|
-- else -- Debug для других столкновений
|
||||||
|
-- print("[Fedhoria Collide] Non-ground collision. Normal Z:", data.HitNormal.z)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function MODULE:PhysicsSimulate(phys, dt)
|
||||||
|
local cur_time = CurTime()
|
||||||
|
local target = self:GetTarget()
|
||||||
|
if not IsValid(target) then self:Remove(); return false end -- если цели нет, то идем нахуй
|
||||||
|
|
||||||
|
-- проверка на физику
|
||||||
|
if cur_time < self.AnimationRollEndTime then
|
||||||
|
-- print("[Fedhoria Sim] In Animation Roll. Time left:", self.AnimationRollEndTime - cur_time) -- Debug
|
||||||
|
-- self.StartDie = nil -- Сбрасываем таймер смерти
|
||||||
|
|
||||||
|
return true -- стандартная физика пошла нахуй, у меня по ней 2
|
||||||
|
-- return false -- если физика нахуй идет
|
||||||
|
end
|
||||||
|
|
||||||
|
-- --- логика идет нахуй
|
||||||
|
|
||||||
|
-- логика для таймера
|
||||||
|
local f = 1 -- сила есть ума не надо (по умолчанию 1)
|
||||||
|
if self.StartDie then
|
||||||
|
f = math_Clamp(1 - (cur_time - self.StartDie) / die_time:GetFloat(), 0, 1)
|
||||||
|
end
|
||||||
|
|
||||||
|
print(f)
|
||||||
|
|
||||||
|
-- ебаная логика для regmod
|
||||||
|
if ragmod and ragmod:IsRagmodRagdoll(target) then
|
||||||
|
local owner = target:GetOwningPlayer()
|
||||||
|
-- владелец идот нахуй
|
||||||
|
f = (IsValid(owner) and owner:Alive()) and 1 or 0
|
||||||
|
self.StartDie = nil
|
||||||
|
|
||||||
|
if f <= 0 then self.AnimationRollEndTime = 0 end
|
||||||
|
end
|
||||||
|
|
||||||
|
--
|
||||||
|
if (f <= 0) then
|
||||||
|
-- print("[Fedhoria Sim] Removing entity (f <= 0)") -- Debug
|
||||||
|
self:Remove()
|
||||||
|
return false -- Прекращаем хуйню
|
||||||
|
end
|
||||||
|
|
||||||
|
local phys_bone_id = phys:GetID() -- айди кости
|
||||||
|
|
||||||
|
-- ыыыуыыыыыыыы
|
||||||
|
if (phys_bone_id == 0) then
|
||||||
|
-- логика для блядской воды (если торс попал в воду)
|
||||||
|
if target:WaterLevel() > 0 then
|
||||||
|
-- print("[Fedhoria Sim] Target in water.") -- Debug
|
||||||
|
self.AnimationRollEndTime = 0 -- нахуй анимация пошла
|
||||||
|
local seq_choked = self:LookupSequence("Choked_Barnacle")
|
||||||
|
if seq_choked then self:ResetSequence(seq_choked) end
|
||||||
|
-- возможн нужно добавить таймер на удаление или другую логику для воды
|
||||||
|
-- timer.Simple(0.5, function() if IsValid(self) then self:Remove() end end) -- пример опиздюливания через 5 сек
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
-- логика для анимации переката
|
||||||
|
local vel = phys:GetVelocity()
|
||||||
|
local pbr = math_Clamp(vel.z / -600, 0.5, 1.5)
|
||||||
|
-- падения и ебаная скорость
|
||||||
|
-- если мы не в.. а впрочем иди нахуй
|
||||||
|
if self:GetSequence() ~= self:LookupSequence("idleonfire") or self:GetPlaybackRate() ~= pbr then
|
||||||
|
local seq_idle = self:LookupSequence("idleonfire")
|
||||||
|
if seq_idle then
|
||||||
|
-- print("[Fedhoria Sim] Resetting sequence to idleonfire, pbr:", pbr) -- Debug
|
||||||
|
self:ResetSequence(seq_idle)
|
||||||
|
self:SetPlaybackRate(pbr)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- логика таймера ебаного умирания
|
||||||
|
local pos = phys:GetPos()
|
||||||
|
self.last_pos = self.last_pos or pos
|
||||||
|
local offset_sqr = (pos - self.last_pos):LengthSqr()
|
||||||
|
self.last_pos = pos
|
||||||
|
if (offset_sqr < (10*10 * dt*dt) and not (ragmod and ragmod:IsRagmodRagdoll(target))) then -- uменьшил порог неподвижности
|
||||||
|
self.StartDie = self.StartDie or cur_time -- идите нахуй
|
||||||
|
-- print("[Fedhoria Sim] Torso seems stationary. StartDie:", self.StartDie) -- Debug
|
||||||
|
else
|
||||||
|
-- self.StartDie = nil -- нахуй таймер
|
||||||
|
-- print("[Fedhoria Sim] Torso moved. Resetting StartDie.") -- Debug
|
||||||
|
end
|
||||||
|
|
||||||
|
--
|
||||||
|
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
|
||||||
|
|
||||||
|
-- --- отстали от жизни
|
||||||
|
-- затухание
|
||||||
|
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
|
||||||
|
|
||||||
|
|
||||||
|
function MODULE:OnRemove()
|
||||||
|
-- пошел нахуй таймер
|
||||||
|
local timer_name = "Fedhoria_FallingTorso_Twitch_" .. self:EntIndex()
|
||||||
|
timer.Remove(timer_name)
|
||||||
|
-- print("[Fedhoria] OnRemove called for", self:EntIndex(), "Timer removed:", timer_name) -- Debug
|
||||||
|
end
|
||||||
554
fedhoria/modules/stumble_legs.lua
Normal file
554
fedhoria/modules/stumble_legs.lua
Normal file
|
|
@ -0,0 +1,554 @@
|
||||||
|
MODULE.Model = "models/barney.mdl"
|
||||||
|
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"
|
||||||
|
}
|
||||||
|
|
||||||
|
local stumble_time = CreateConVar("fedhoria_stumble_time", 2, bit.bor(FCVAR_ARCHIVE, FCVAR_REPLICATED))
|
||||||
|
local grab_chance = CreateConVar("fedhoria_woundgrab_chance", 0.9, bit.bor(FCVAR_ARCHIVE, FCVAR_REPLICATED))
|
||||||
|
local grab_time = CreateConVar("fedhoria_woundgrab_time", 5, bit.bor(FCVAR_ARCHIVE, FCVAR_REPLICATED))
|
||||||
|
|
||||||
|
local math_atan2 = math.atan2
|
||||||
|
local math_pi = math.pi
|
||||||
|
local math_Clamp = math.Clamp
|
||||||
|
local math_max = math.max
|
||||||
|
local math_min = math.min
|
||||||
|
|
||||||
|
local constant = 10000
|
||||||
|
|
||||||
|
local function CreateSpring(phys1, phys2)
|
||||||
|
if (!IsValid(phys1) or !IsValid(phys2)) then return NULL end
|
||||||
|
local str_axis = tostring(phys2:LocalToWorld(phys2:GetAABBCenter()))
|
||||||
|
|
||||||
|
local const = ents.Create("phys_spring")
|
||||||
|
const:SetPos(phys1:LocalToWorld(phys1:GetAABBCenter()))
|
||||||
|
const:SetKeyValue("springaxis", str_axis)
|
||||||
|
const:SetKeyValue("constant", constant)
|
||||||
|
const:SetKeyValue("damping", 0.1)
|
||||||
|
const:SetKeyValue("relativedamping", 0.1)
|
||||||
|
const:SetPhysConstraintObjects(phys1, phys2)
|
||||||
|
const:Spawn()
|
||||||
|
const:Activate()
|
||||||
|
--const:Fire("SetSpringLength", 0, 0)
|
||||||
|
|
||||||
|
return const
|
||||||
|
end
|
||||||
|
|
||||||
|
local allowed_touch =
|
||||||
|
{
|
||||||
|
"ValveBiped.Bip01_R_Calf",
|
||||||
|
"ValveBiped.Bip01_R_Foot",
|
||||||
|
"ValveBiped.Bip01_L_Calf",
|
||||||
|
"ValveBiped.Bip01_L_Foot"
|
||||||
|
}
|
||||||
|
|
||||||
|
local hand_offset = Vector(2, 0, 0)
|
||||||
|
|
||||||
|
function MODULE:Init(phys_bone, lpos)
|
||||||
|
self.seq_walk = self:LookupSequence("walk_all")
|
||||||
|
self.seq_walk_speed = self:GetSequenceMoveDist(self.seq_walk) * self:SequenceDuration(self.seq_walk)
|
||||||
|
|
||||||
|
self.seq_run = self:LookupSequence("run_all")
|
||||||
|
self.seq_run_speed = self:GetSequenceMoveDist(self.seq_run) * self:SequenceDuration(self.seq_run)
|
||||||
|
|
||||||
|
self.seq_sprint = self:LookupSequence("sprint_all")
|
||||||
|
self.seq_sprint_speed = self:GetSequenceMoveDist(self.seq_sprint) * self:SequenceDuration(self.seq_sprint)
|
||||||
|
|
||||||
|
self:ResetSequence(self:LookupSequence("idle01"))
|
||||||
|
|
||||||
|
local target = self:GetTarget()
|
||||||
|
|
||||||
|
for _, bone_name in pairs(self.BoneList) do
|
||||||
|
local bone = self:LookupBone(bone_name)
|
||||||
|
if bone then
|
||||||
|
local phys_bone = target:TranslateBoneToPhysBone(bone)
|
||||||
|
local phys = target:GetPhysicsObjectNum(phys_bone)
|
||||||
|
--phys:AddGameFlag(FVPHYSICS_NO_SELF_COLLISIONS)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
self.bone_lthigh = self:LookupBone("ValveBiped.Bip01_L_Thigh")
|
||||||
|
self.bone_rthigh = self:LookupBone("ValveBiped.Bip01_R_Thigh")
|
||||||
|
|
||||||
|
if (self.bone_lthigh and self.bone_rthigh) then
|
||||||
|
self.phys_bone_lthigh = self:TranslateBoneToPhysBone(self.bone_lthigh)
|
||||||
|
self.phys_bone_rthigh = self:TranslateBoneToPhysBone(self.bone_rthigh)
|
||||||
|
end
|
||||||
|
|
||||||
|
self.bone_lcalf = self:LookupBone("ValveBiped.Bip01_L_Calf")
|
||||||
|
self.bone_rcalf = self:LookupBone("ValveBiped.Bip01_R_Calf")
|
||||||
|
|
||||||
|
if (self.bone_lcalf and self.bone_rcalf) then
|
||||||
|
self.phys_bone_lcalf = self:TranslateBoneToPhysBone(self.bone_lcalf)
|
||||||
|
self.phys_bone_rcalf = self:TranslateBoneToPhysBone(self.bone_rcalf)
|
||||||
|
end
|
||||||
|
|
||||||
|
self.bone_lfoot = self:LookupBone("ValveBiped.Bip01_L_Foot")
|
||||||
|
self.bone_rfoot = self:LookupBone("ValveBiped.Bip01_R_Foot")
|
||||||
|
|
||||||
|
if (self.bone_lfoot and self.bone_rfoot) then
|
||||||
|
self.phys_bone_lfoot = self:TranslateBoneToPhysBone(self.bone_lfoot)
|
||||||
|
self.phys_bone_rfoot = self:TranslateBoneToPhysBone(self.bone_rfoot)
|
||||||
|
end
|
||||||
|
|
||||||
|
self.bone_lhand = self:LookupBone("ValveBiped.Bip01_L_Hand")
|
||||||
|
self.bone_rhand = self:LookupBone("ValveBiped.Bip01_R_Hand")
|
||||||
|
|
||||||
|
if (self.bone_lhand and self.bone_rhand) then
|
||||||
|
self.phys_bone_lhand = self:TranslateBoneToPhysBone(self.bone_lhand)
|
||||||
|
self.phys_bone_rhand = self:TranslateBoneToPhysBone(self.bone_rhand)
|
||||||
|
end
|
||||||
|
|
||||||
|
self.phys_allowed_touch = {}
|
||||||
|
|
||||||
|
for _, bone_name in pairs(allowed_touch) do
|
||||||
|
local bone = self:LookupBone(bone_name)
|
||||||
|
if bone then
|
||||||
|
local phys_bone = self:TranslateBoneToPhysBone(bone)
|
||||||
|
self.phys_allowed_touch[phys_bone] = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
self.bone_pelvis = self:LookupBone("ValveBiped.Bip01_Pelvis")
|
||||||
|
self.bone_torso = self:LookupBone("ValveBiped.Bip01_Spine2")
|
||||||
|
|
||||||
|
if (self.bone_pelvis and self.bone_torso) then
|
||||||
|
self.phys_bone_pelvis = target:TranslateBoneToPhysBone(self.bone_pelvis)
|
||||||
|
self.phys_bone_torso = target:TranslateBoneToPhysBone(self.bone_torso)
|
||||||
|
|
||||||
|
--constraint.Weld(target, target, phys_bone_pelvis, phys_bone_torso)
|
||||||
|
end
|
||||||
|
|
||||||
|
local vel = target:GetVelocity():Length2D()
|
||||||
|
|
||||||
|
if phys_bone then
|
||||||
|
self.Springs = {}
|
||||||
|
--Grab wound
|
||||||
|
local phys = target:GetPhysicsObjectNum(phys_bone)
|
||||||
|
local dmgpos = phys:LocalToWorld(lpos)
|
||||||
|
if (IsValid(phys) and phys_bone != self.phys_bone_lhand and phys_bone != self.phys_bone_rhand) then
|
||||||
|
local phys_lhand = target:GetPhysicsObjectNum(self.phys_bone_lhand)
|
||||||
|
local phys_rhand = target:GetPhysicsObjectNum(self.phys_bone_rhand)
|
||||||
|
|
||||||
|
local str_axis = tostring(dmgpos)
|
||||||
|
|
||||||
|
if (IsValid(phys_lhand) and math.random() < grab_chance:GetFloat()) then
|
||||||
|
local const = ents.Create("phys_spring")
|
||||||
|
const:SetPos(phys_lhand:LocalToWorld(hand_offset))
|
||||||
|
const:SetKeyValue("springaxis", str_axis)
|
||||||
|
const:SetKeyValue("constant", 300)
|
||||||
|
const:SetKeyValue("damping", 0.1)
|
||||||
|
const:SetKeyValue("relativedamping", 0.1)
|
||||||
|
const:SetPhysConstraintObjects(phys_lhand, phys)
|
||||||
|
const:Spawn()
|
||||||
|
const:Activate()
|
||||||
|
const:Fire("SetSpringLength", 0, 0)
|
||||||
|
|
||||||
|
SafeRemoveEntityDelayed(const, grab_time:GetFloat())
|
||||||
|
|
||||||
|
table.insert(self.Springs, const)
|
||||||
|
end
|
||||||
|
|
||||||
|
if (IsValid(phys_rhand) and math.random() < grab_chance:GetFloat()) then
|
||||||
|
local const = ents.Create("phys_spring")
|
||||||
|
const:SetPos(phys_rhand:LocalToWorld(hand_offset))
|
||||||
|
const:SetKeyValue("springaxis", str_axis)
|
||||||
|
const:SetKeyValue("constant", 300)
|
||||||
|
const:SetKeyValue("damping", 0.1)
|
||||||
|
const:SetKeyValue("relativedamping", 0.1)
|
||||||
|
const:SetPhysConstraintObjects(phys_rhand, phys)
|
||||||
|
const:Spawn()
|
||||||
|
const:Activate()
|
||||||
|
const:Fire("SetSpringLength", 0, 0)
|
||||||
|
|
||||||
|
SafeRemoveEntityDelayed(const, grab_time:GetFloat())
|
||||||
|
|
||||||
|
table.insert(self.Springs, const)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--[[if (!target.GS2IsDismembered and vel < self.seq_walk_speed) then
|
||||||
|
--self:Remove()
|
||||||
|
self.Springs = {}
|
||||||
|
if (self.phys_bone_lthigh and self.phys_bone_lcalf) then
|
||||||
|
local phys_lthigh = target:GetPhysicsObjectNum(self.phys_bone_lthigh)
|
||||||
|
local phys_lcalf = target:GetPhysicsObjectNum(self.phys_bone_lcalf)
|
||||||
|
|
||||||
|
table.insert(self.Springs, CreateSpring(phys_lthigh, phys_lcalf))
|
||||||
|
end
|
||||||
|
|
||||||
|
if (self.phys_bone_rthigh and self.phys_bone_rcalf) then
|
||||||
|
local phys_rthigh = target:GetPhysicsObjectNum(self.phys_bone_rthigh)
|
||||||
|
local phys_rcalf = target:GetPhysicsObjectNum(self.phys_bone_rcalf)
|
||||||
|
|
||||||
|
table.insert(self.Springs, CreateSpring(phys_rthigh, phys_rcalf))
|
||||||
|
end
|
||||||
|
|
||||||
|
if (self.phys_bone_pelvis and self.phys_bone_torso) then
|
||||||
|
local phys_pelvis = target:GetPhysicsObjectNum(self.phys_bone_pelvis)
|
||||||
|
local phys_torso = target:GetPhysicsObjectNum(self.phys_bone_torso)
|
||||||
|
|
||||||
|
--CreateSpring(phys_pelvis, phys_torso)
|
||||||
|
end
|
||||||
|
|
||||||
|
if (self.phys_bone_pelvis and self.phys_bone_lthigh) then
|
||||||
|
local phys_pelvis = target:GetPhysicsObjectNum(self.phys_bone_pelvis)
|
||||||
|
local phys_lthigh = target:GetPhysicsObjectNum(self.phys_bone_lthigh)
|
||||||
|
|
||||||
|
--CreateSpring(phys_pelvis, phys_lthigh)
|
||||||
|
end
|
||||||
|
|
||||||
|
if (self.phys_bone_pelvis and self.phys_bone_rthigh) then
|
||||||
|
local phys_pelvis = target:GetPhysicsObjectNum(self.phys_bone_pelvis)
|
||||||
|
local phys_rthigh = target:GetPhysicsObjectNum(self.phys_bone_rthigh)
|
||||||
|
|
||||||
|
--CreateSpring(phys_pelvis, phys_rthigh)
|
||||||
|
end
|
||||||
|
end]]
|
||||||
|
end
|
||||||
|
|
||||||
|
local function DoFallingAnim(ent)
|
||||||
|
local mod1 = fedhoria.StartModule(ent, "falling_legs")
|
||||||
|
local mod2 = fedhoria.StartModule(ent, "falling_torso")
|
||||||
|
end
|
||||||
|
|
||||||
|
function MODULE:OnRemove()
|
||||||
|
local target = self:GetTarget()
|
||||||
|
if !IsValid(target) then return end
|
||||||
|
|
||||||
|
for _, bone_name in pairs(self.BoneList) 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:ClearGameFlag(FVPHYSICS_NO_SELF_COLLISIONS)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--[[if self.Springs then
|
||||||
|
for _, spring in pairs(self.Springs) do
|
||||||
|
SafeRemoveEntityDelayed(spring, 1)
|
||||||
|
end
|
||||||
|
end]]
|
||||||
|
|
||||||
|
DoFallingAnim(target)
|
||||||
|
end
|
||||||
|
|
||||||
|
function MODULE:Think()
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
function MODULE:PhysicsCollide(ent, data)
|
||||||
|
--if (data.HitEntity == ent) then return end
|
||||||
|
if (data.HitEntity != game.GetWorld()) then return end
|
||||||
|
if (CurTime() - self.Created < 0.1) then return end
|
||||||
|
|
||||||
|
local phys = data.PhysObject
|
||||||
|
local phys_bone = phys:GetID()
|
||||||
|
|
||||||
|
if !self.phys_allowed_touch[phys_bone] then
|
||||||
|
SafeRemoveEntityDelayed(self, 0)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local constant = 1
|
||||||
|
|
||||||
|
local tilt_ang = Angle(0, 0, 0)
|
||||||
|
|
||||||
|
local pelvis_offset = Vector(0, 0, 35)
|
||||||
|
local torso_offset = Vector(0, 0, 50)
|
||||||
|
|
||||||
|
local trace = {output={}}
|
||||||
|
local tr = trace.output
|
||||||
|
|
||||||
|
function MODULE:PhysicsSimulate(phys, dt)
|
||||||
|
local phys_bone = phys:GetID()
|
||||||
|
|
||||||
|
local target = self:GetTarget()
|
||||||
|
|
||||||
|
if (self.Springs and target:GetNWInt("GS2DisMask", 0) != 0) then
|
||||||
|
for _, spring in pairs(self.Springs) do
|
||||||
|
SafeRemoveEntity(spring)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local st = stumble_time:GetFloat()
|
||||||
|
|
||||||
|
if (st <= 0) then
|
||||||
|
self:Remove()
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
local f = 1 - (CurTime() - self.Created) / st
|
||||||
|
|
||||||
|
--RagMod Reworked support
|
||||||
|
if ragmod and ragmod:IsRagmodRagdoll(target) then
|
||||||
|
local owner = target:GetOwningPlayer()
|
||||||
|
if !IsValid(owner) or !owner:Alive() then
|
||||||
|
f = 0
|
||||||
|
else
|
||||||
|
f = 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if (f <= 0) then
|
||||||
|
self:Remove()
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
--helps reduce excessive twitching
|
||||||
|
if (phys:GetStress() > 100) then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
if (phys_bone == self.phys_bone_torso) then
|
||||||
|
local phys_pelvis = target:GetPhysicsObjectNum(self.phys_bone_pelvis or 0)
|
||||||
|
local vel = phys:GetVelocity()
|
||||||
|
vel.z = 0
|
||||||
|
vel:Normalize()
|
||||||
|
|
||||||
|
local ang = phys_pelvis:GetAngles()
|
||||||
|
ang.p = 0
|
||||||
|
ang.r = 0
|
||||||
|
|
||||||
|
local x = ang:Right():Dot(vel)
|
||||||
|
local y = ang:Forward():Dot(vel)
|
||||||
|
|
||||||
|
local yaw = 180 * math_atan2(y, x) / math_pi
|
||||||
|
|
||||||
|
if (yaw < 90 and yaw > -90) then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if (phys_bone == (self.phys_bone_pelvis or 0)) then
|
||||||
|
local phys_torso = target:GetPhysicsObjectNum(self.phys_bone_torso or 1)
|
||||||
|
--calculate animation settings here so its only done once per frame
|
||||||
|
local pos = phys:GetPos()
|
||||||
|
local ang = phys:GetAngles()
|
||||||
|
--local vel = phys:GetVelocity()
|
||||||
|
local vel = phys_torso:GetVelocity()
|
||||||
|
|
||||||
|
local forward = ang:Forward()
|
||||||
|
local right = ang:Right()
|
||||||
|
local up = ang:Up()
|
||||||
|
|
||||||
|
--try keeping balance a little bit better
|
||||||
|
tilt_ang.y = math_Clamp(90 + 180 * math_atan2(right.z, up.z) / math_pi, -50, 20) * f
|
||||||
|
|
||||||
|
--too much balance?
|
||||||
|
tilt_ang.p = -ang.p * f
|
||||||
|
|
||||||
|
self:ManipulateBoneAngles(self.bone_lthigh, tilt_ang)
|
||||||
|
self:ManipulateBoneAngles(self.bone_rthigh, tilt_ang)
|
||||||
|
|
||||||
|
local speed = vel:Length2D()
|
||||||
|
|
||||||
|
local slow = false
|
||||||
|
|
||||||
|
if (speed > self.seq_sprint_speed) then
|
||||||
|
self:ResetSequence(self.seq_sprint)
|
||||||
|
elseif (speed > self.seq_run_speed) then
|
||||||
|
self:ResetSequence(self.seq_run)
|
||||||
|
elseif (speed > self.seq_walk_speed) then
|
||||||
|
self:ResetSequence(self.seq_walk)
|
||||||
|
else
|
||||||
|
slow = true
|
||||||
|
end
|
||||||
|
|
||||||
|
if slow then
|
||||||
|
return false
|
||||||
|
--[[elseif (self.Springs) then
|
||||||
|
for _, spring in pairs(self.Springs) do
|
||||||
|
SafeRemoveEntity(spring)
|
||||||
|
end
|
||||||
|
self.Springs = nil]]
|
||||||
|
end
|
||||||
|
|
||||||
|
--self:ResetSequence(self.seq_sprint)
|
||||||
|
|
||||||
|
--local mass_center = target:GetMassCenter()
|
||||||
|
|
||||||
|
self.last_pos = self.last_pos or pos
|
||||||
|
|
||||||
|
local delta = (pos - self.last_pos) / dt
|
||||||
|
|
||||||
|
self.last_pos = pos
|
||||||
|
|
||||||
|
local pbr = vel:Length2D() / self:GetSequenceMoveDist(self:GetSequence())
|
||||||
|
--local pbr = vel:Dot(ang:Right()) / self:GetSequenceMoveDist(self:GetSequence())
|
||||||
|
|
||||||
|
--pbr = pbr / self:SequenceDuration()
|
||||||
|
|
||||||
|
vel.z = 0
|
||||||
|
vel:Normalize()
|
||||||
|
|
||||||
|
local ang = phys:GetAngles()
|
||||||
|
ang.p = 0
|
||||||
|
ang.r = 0
|
||||||
|
|
||||||
|
local x = ang:Right():Dot(vel)
|
||||||
|
local y = ang:Forward():Dot(vel)
|
||||||
|
|
||||||
|
self.pbr = math.min(3, Lerp(dt * constant, self.pbr or pbr, pbr))
|
||||||
|
|
||||||
|
local yaw = 180 * math_atan2(y, x) / math_pi
|
||||||
|
|
||||||
|
local m = 1
|
||||||
|
|
||||||
|
if (yaw > 90) then
|
||||||
|
m = -1.5
|
||||||
|
yaw = -90 + yaw - 90
|
||||||
|
self:ResetSequence(self.seq_sprint)
|
||||||
|
elseif (yaw < -90) then
|
||||||
|
m = -1.5
|
||||||
|
yaw = 90 - (yaw + 90)
|
||||||
|
self:ResetSequence(self.seq_sprint)
|
||||||
|
end
|
||||||
|
|
||||||
|
self.move_yaw = Lerp(dt * constant, self.move_yaw or yaw, yaw)
|
||||||
|
|
||||||
|
self:SetPoseParameter("move_yaw", self.move_yaw)
|
||||||
|
|
||||||
|
self:SetPlaybackRate(m * self.pbr * self:SequenceDuration())
|
||||||
|
|
||||||
|
trace.filter = target
|
||||||
|
trace.start = pos
|
||||||
|
trace.endpos = pos - torso_offset
|
||||||
|
|
||||||
|
util.TraceLine(trace)
|
||||||
|
|
||||||
|
if tr.Hit then
|
||||||
|
if (x < 0) then
|
||||||
|
ang = phys:GetAngles()
|
||||||
|
local a = math.max(0, -ang:Right().z)^2
|
||||||
|
|
||||||
|
if (a < 0.2) then
|
||||||
|
--self:Remove()
|
||||||
|
--return false
|
||||||
|
end
|
||||||
|
|
||||||
|
pos = phys_torso:GetPos()
|
||||||
|
|
||||||
|
local d = (pos - tr.HitPos):Length2D()
|
||||||
|
|
||||||
|
local div = self:GetSequenceMoveDist(self:GetSequence()) * self:SequenceDuration()
|
||||||
|
|
||||||
|
local m = math_max(0, 1 - (d / div))
|
||||||
|
|
||||||
|
vel = phys:GetVelocity()
|
||||||
|
vel.x = 0
|
||||||
|
vel.y = 0
|
||||||
|
|
||||||
|
m = m * a
|
||||||
|
|
||||||
|
vel.z = vel.z * m
|
||||||
|
|
||||||
|
phys:ApplyForceCenter(-vel * phys:GetMass() * f)
|
||||||
|
|
||||||
|
vel = phys_torso:GetVelocity()
|
||||||
|
vel.x = 0
|
||||||
|
vel.y = 0
|
||||||
|
|
||||||
|
vel.z = vel.z * m
|
||||||
|
|
||||||
|
phys_torso:ApplyForceCenter(-vel * phys_torso:GetMass() * f)
|
||||||
|
return false
|
||||||
|
else
|
||||||
|
local l2d = delta:Length2D()
|
||||||
|
|
||||||
|
local f = f * math_Clamp(l2d / self:GetSequenceMoveDist(self:GetSequence()), 0, 1)
|
||||||
|
|
||||||
|
local targetZ = tr.HitPos.z + torso_offset.z * f
|
||||||
|
|
||||||
|
local offsetZ = targetZ - phys_torso:GetPos().z
|
||||||
|
|
||||||
|
if (offsetZ > 0) then
|
||||||
|
local force = offsetZ^2 - phys_torso:GetVelocity().z
|
||||||
|
force = force * 0.5
|
||||||
|
force = math_min(force, 40)
|
||||||
|
phys_torso:ApplyForceCenter(Vector(0, 0, force) * phys_torso:GetMass() * f)
|
||||||
|
end
|
||||||
|
|
||||||
|
local targetZ = tr.HitPos.z + pelvis_offset.z * f
|
||||||
|
|
||||||
|
local offsetZ = targetZ - phys:GetPos().z
|
||||||
|
|
||||||
|
if (offsetZ > 0) then
|
||||||
|
local force = offsetZ^2 - phys:GetVelocity().z
|
||||||
|
force = force * 0.5
|
||||||
|
force = math_min(force, 40)
|
||||||
|
phys:ApplyForceCenter(Vector(0, 0, force) * phys:GetMass() * f)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
self:Remove()
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
if (self.phys_bone_lhand) then
|
||||||
|
--[[local phys_lhand = target:GetPhysicsObjectNum(self.phys_bone_lhand)
|
||||||
|
|
||||||
|
pos = phys_lhand:GetPos()
|
||||||
|
|
||||||
|
local pos2 = target:GetBonePosition(target:LookupBone("ValveBiped.Bip01_L_Upperarm"))
|
||||||
|
|
||||||
|
local offset = pos2 - pos
|
||||||
|
|
||||||
|
offset:Add(phys:GetVelocity() * dt)
|
||||||
|
|
||||||
|
offset.z = 0
|
||||||
|
|
||||||
|
offset = offset:GetNormal() * offset:Length()^2
|
||||||
|
|
||||||
|
offset:Sub(phys_lhand:GetVelocity())
|
||||||
|
|
||||||
|
phys_lhand:ApplyForceCenter(offset * phys_lhand:GetMass() * f)]]
|
||||||
|
end
|
||||||
|
|
||||||
|
if (self.phys_bone_rhand) then
|
||||||
|
--[[local phys_rhand = target:GetPhysicsObjectNum(self.phys_bone_rhand)
|
||||||
|
|
||||||
|
pos = phys_rhand:GetPos()
|
||||||
|
|
||||||
|
local pos2 = target:GetBonePosition(target:LookupBone("ValveBiped.Bip01_R_Upperarm"))
|
||||||
|
|
||||||
|
local offset = pos2 - pos
|
||||||
|
|
||||||
|
offset:Add(phys:GetVelocity() * dt)
|
||||||
|
|
||||||
|
offset.z = 0
|
||||||
|
|
||||||
|
offset = offset:GetNormal() * offset:Length()^2
|
||||||
|
|
||||||
|
offset:Sub(phys_rhand:GetVelocity())
|
||||||
|
|
||||||
|
phys_rhand:ApplyForceCenter(offset * phys_rhand:GetMass() * f)]]
|
||||||
|
end
|
||||||
|
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
--[[if self.Springs then
|
||||||
|
return false
|
||||||
|
end]]
|
||||||
|
|
||||||
|
return true, f
|
||||||
|
end
|
||||||
Loading…
Reference in a new issue