283 lines
12 KiB
Lua
283 lines
12 KiB
Lua
|
|
--здесь был 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
|