Compare commits

..

No commits in common. "master" and "stop-tackling-people-you-cunts" have entirely different histories.

9 changed files with 108 additions and 703 deletions

View file

@ -1,23 +1,20 @@
# Z-City NPC Integration # Z-City NPC Integration
Adds better NPC integration for the Garry's Mod addon Z-City. Adds better NPC integration for the Garry's Mod addon Z-City. While Z-City has a convar that enables the custom health system on NPCs, it really isn't the best or most advanced thing in the world. This has irritated me since I first started using Z-City. Therefore, I've decided to take care of the problem myself with my own plugin.
While Z-City has a convar that enables the custom health system on NPCs, it really isn't the best or most bug-free thing in the world. This has irritated me since I first started using Z-City. Therefore, I've decided to take care of the problem myself with my own plugin.
- NPCs can be ragdolled like players can, and will wiggle around on the ground. They can get back up, too! - NPCs can be ragdolled like players can, and will wiggle around on the ground. They can get back up, too!
- It takes all elements of Z-City's health system into account, such as unconsciousness and pain. - It takes all elements of Z-City's health system into account, such as unconsciousness and pain.
- NPCs are responsive to damage from many sources -- bullets, fire, melee (especially kicks!) and more. - Various configurable settings are included, allowing you to tweak the mod to your liking.
- NPCs can also see and target downed NPCs, and will prioritize standing NPCs over them when needed.
- Everything is configurable, so you can modify and disable features as you like to create your preferred experience.
This mod is built to perform reasonably well, thus it includes optimizations, configurable performance settings, and an automatic corpse cleanup tool. This mod is built to perform reasonably well, thus it includes optimizations, configurable performance settings, and an automatic corpse remover.
This addon is based off of Kazarei's Euphoria, which in turn is a fork of Fedhoria by Rama. Credits to them for making the cool thing. This addon is based off of Kazarei's Euphoria, which in turn is a fork of Fedhoria by Rama. Credits to them for making the cool thing.
## Known issues ## Known issues
- Bandages, tourniquets, etc. that are on the ragdoll or NPC body will not be there when they get back up. Don't know how to fix this right now, though if anyone experienced with Z-City's codebase would know how to I would appreciate knowing. - Bandages, tourniquets, etc. that are on the ragdoll or NPC body will not be there when they get back up. Don't know how to fix this unfortunately.
- The NPC getting up animation is a little ugly. However, creating that animation was an adventure on levels that I would personally dislike re-experiencing. - NPCs currently instantly get up, with no animation. I plan on remedying this in the future.
- Enemy NPCs will not target living downed NPCs like how they do players. This will be fixed.
## Compatibility ## Compatibility
@ -31,23 +28,10 @@ It is, however, not compatbile with:
- Most mods that modify the behavior of death ragdolls (Reagdoll, Artagdoll, especially Fedhoria.) - Most mods that modify the behavior of death ragdolls (Reagdoll, Artagdoll, especially Fedhoria.)
- Mods that automatically remove death ragdolls (it will delete all ragdolled NPCs, even if they're still alive! Try the built-in automatic corpse removal instead.) - Mods that automatically remove death ragdolls (it will delete all ragdolled NPCs, even if they're still alive! Try the built-in automatic corpse removal instead.)
## Todo list ## Future plans
MUST BE DONE BEFORE RELEASE: No guarantees any of this will become real.
- Fire / falling / pain reaction (more rapid flailing) - NPCs will back out of combat when injured.
- NPCs should not unfake if being grabbed - Friendly NPCs can heal each other.
- NPCs can heal themselves.
Stuff to do:
- Posturing (like when you get hit) more similar to Z-City
- Facial expressions
- Generally improve stumbling (foot stepping)
- Stop NPCs from dropping their guns maybe?
Post release ideas:
- Localization support for other languages
- ReaSFX integration would probably be nice. I mean there's so many SFX packs to choose from, so I don't even have to pack it in
- To be entirely honest Fedhoria isn't the best addon out there for euphoria physics. Maybe rebase to another addon (or alternatively build a new one?)
- NPC targeting works, but works really weirdly (sometimes it just. keeps targeting corpses when it shouldn't.) It actually isn't THAT much of an issue because it doesn't feel totally outlandish but I would still love to fix it.

View file

@ -1,20 +1,8 @@
local function PopulateAISBXToolMenu(pnl)
pnl:CheckBox("Allow NPCs to target downed NPCs", "zcnpci_npc_targeting_enabled")
pnl:ControlHelp("If enabled, NPCs target downed hostile NPCs.")
pnl:CheckBox("NPCs should target head", "zcnpci_target_should_follow_head")
pnl:ControlHelp("Should NPCs target the player's head? This can result in more effective targeting.")
pnl:CheckBox("NPCs shouldn't target unconscious NPCs", "zcnpci_no_unconscious_targeting")
end
local function PopulateCustomNPCSBXToolMenu(pnl) local function PopulateCustomNPCSBXToolMenu(pnl)
local modded_npc_whitelist = CreateConVar("zcnpci_modded_npc_whitelist", "NPC-classes-here!", bit.bor(FCVAR_ARCHIVE, FCVAR_REPLICATED)) local modded_npc_whitelist = CreateConVar("zcnpci_modded_npc_whitelist", "NPC-classes-here!", bit.bor(FCVAR_ARCHIVE, FCVAR_REPLICATED))
pnl:Help("ZCNPCi has support for both modded NPCs, as well as base-game humanoid NPCs that Z-City never added. There is no guarantee that modded NPCs will work, but ones with similar skeletal structure to other base-game humanoid NPCs will generally work better.") pnl:Help("ZCNPCi has support for both modded NPCs, as well as base-game humanoid NPCs that Z-City never added. There is no guarantee that modded NPCs will work, but ones with similar skeletal structure to other base-game humanoid NPCs will generally work better.")
pnl:Help("As long as the NPC has a compatible skeleton, support will be provided for modded NPCs on a best-effort basis.")
pnl:CheckBox("Allow base-game extended NPCs", "zcnpci_allow_extended_base_npcs") pnl:CheckBox("Allow base-game extended NPCs", "zcnpci_allow_extended_base_npcs")
pnl:ControlHelp("Enable or disables applying custom health system to most other base-game humanoid NPCs, such as Kleiner.") pnl:ControlHelp("Enable or disables applying custom health system to most other base-game humanoid NPCs, such as Kleiner.")
@ -37,7 +25,7 @@ local function PopulateCustomNPCSBXToolMenu(pnl)
pnl:AddItem(text) pnl:AddItem(text)
pnl:Help("A list of NPC classes, seperated by spaces or line breaks. Can only be set by a superadmin or server operator.") pnl:Help("One NPC class per line. No spaces or other characters. Can only be set by a superadmin or server operator.")
end end
local function PopulateRagdollSBXToolMenu(pnl) local function PopulateRagdollSBXToolMenu(pnl)
@ -50,29 +38,8 @@ local function PopulateRagdollSBXToolMenu(pnl)
pnl:NumSlider("Wound grab time", "zcnpci_woundgrab_time", 0, 10, 3) pnl:NumSlider("Wound grab time", "zcnpci_woundgrab_time", 0, 10, 3)
pnl:ControlHelp("How long the ragdoll should hold its wound.") pnl:ControlHelp("How long the ragdoll should hold its wound.")
pnl:NumSlider("Writhing strength", "zcnpci_writhing_strength", 0, 5, 3)
pnl:ControlHelp("How hard should the ragdoll wriggle/writhe.")
pnl:NumSlider("Flailing speed multiplier", "zcnpci_flailing_playback_rate", 0, 12, 3)
pnl:ControlHelp("How fast or slow should the NPC flail by default.")
pnl:NumSlider("Flailing speed multiplier when panicking", "zcnpci_flailing_playback_rate", 0, 12, 3)
pnl:ControlHelp("How fast or slow should the NPC flail when panicking (on fire, falling, etc.).")
pnl:NumSlider("Death timer", "zcnpci_death_timer", -1, 600, 3)
pnl:ControlHelp("After an NPC is ragdolled for longer than this timer, they will die instantly. Set to -1 to disable.")
pnl:CheckBox("Allow getting up", "zcnpci_unfake_enabled")
pnl:ControlHelp("If enabled, NPCs will get back up if able.")
pnl:NumSlider("Get up time", "zcnpci_unfake_time", 0, 5, 3)
pnl:ControlHelp("How long the getting up animation lasts. Setting to 0 will disable the getting up animation.")
pnl:NumSlider("Minimum down time", "zcnpci_down_time", 0, 20, 3) pnl:NumSlider("Minimum down time", "zcnpci_down_time", 0, 20, 3)
pnl:ControlHelp("The minimum amount of time the ragdoll should be down before trying to get back up.") pnl:ControlHelp("The minimum amount of time the ragdoll should be down before trying to get back up.")
pnl:NumSlider("Movement sensitivity", "zcnpci_movement_sensitivity", -1, 256, 3)
pnl:ControlHelp("How far does the NPC have to move to reset the getting up timer. Set to -1 to disable.")
end end
local function PopulatePerformanceSBXToolMenu(pnl) local function PopulatePerformanceSBXToolMenu(pnl)
@ -152,11 +119,6 @@ if engine.ActiveGamemode() == "sandbox" then
PopulateRagdollSBXToolMenu(pnl) PopulateRagdollSBXToolMenu(pnl)
end) end)
spawnmenu.AddToolMenuOption("Utilities", "zcnpci", "AISettings", "AI", "", "", function(pnl)
pnl:ClearControls()
PopulateAISBXToolMenu(pnl)
end)
spawnmenu.AddToolMenuOption("Utilities", "zcnpci", "MainSettings", "Main", "", "", function(pnl) spawnmenu.AddToolMenuOption("Utilities", "zcnpci", "MainSettings", "Main", "", "", function(pnl)
pnl:ClearControls() pnl:ClearControls()
PopulateMainSBXToolMenu(pnl) PopulateMainSBXToolMenu(pnl)

View file

@ -97,7 +97,7 @@ end
function ENT:Initialize() function ENT:Initialize()
if !self.Module then return end if !self.Module then return end
if self.Module.Model then self:SetModel(self.Module.Model) end self:SetModel(self.Module.Model)
for key, value in pairs(self.Module) do for key, value in pairs(self.Module) do
if !self[key] then if !self[key] then

View file

@ -1,120 +0,0 @@
AddCSLuaFile()
ENT.Type = "ai"
ENT.Base = "base_ai"
ENT.AutomaticFrameAdvance = true
local debug_show_ragdoll_targets = CreateConVar("zcnpci_debug_show_ragdoll_targets", 0, bit.bor(FCVAR_ARCHIVE, FCVAR_REPLICATED))
local should_follow_head = CreateConVar("zcnpci_target_should_follow_head", 0, bit.bor(FCVAR_ARCHIVE, FCVAR_REPLICATED))
local stop_targeting_when_unconscious = CreateConVar("zcnpci_no_unconscious_targeting", 1, bit.bor(FCVAR_ARCHIVE, FCVAR_REPLICATED))
local vector_offset_to_shoot_at = Vector(0, 0, 8)
local function UpdateRelationship(ent, me)
if !IsValid(ent) or !ent:IsNPC() or (ent:GetClass() == "npc_ragdoll_target") then return end
if ent:Disposition(me.dummy_entity) == D_HT then
--print("Ragdoll target of class "..me.ragdoll_to_follow.class_in_previous_life.." is hated by a "..ent:GetClass())
ent:AddEntityRelationship(me, D_HT, -1)
else
ent:AddEntityRelationship(me, D_NU, -1)
end
end
local function RemoveRelationship(ent, me)
if !IsValid(ent) or !ent:IsNPC() or (ent:GetClass() == "npc_ragdoll_target") then return end
ent:AddEntityRelationship(me, D_NU, -1)
ent:ClearEnemyMemory(me)
if ent:GetEnemy() == me then
ent:SetEnemy(nil)
ent:ClearSchedule()
end
end
function ENT:Classify()
return CLASS_NONE
end
function ENT:Initialize()
if SERVER then
self:SetModel("models/maxofs2d/hover_basic.mdl")
self:SetCollisionGroup(COLLISION_GROUP_NONE)
self:SetHullType(HULL_TINY_CENTERED)
self:SetNoDraw(!debug_show_ragdoll_targets:GetBool())
self.dummy_entity = ents.Create(self.ragdoll_to_follow.class_in_previous_life)
self.last_stop_targeting = nil
-- Have to use this collision group otherwise ragdoll will obstruct visibility of target
self.ragdoll_to_follow:SetCollisionGroup(COLLISION_GROUP_DEBRIS)
hook.Add("OnEntityCreated", "zcnpci-"..self:GetCreationID(), function(new_entity)
if !IsValid(new_entity) or !new_entity:IsNPC() then return end
if !self.last_stop_targeting then
UpdateRelationship(new_entity, self)
end
end)
end
end
function ENT:Think()
if SERVER then
local ragdoll = self.ragdoll_to_follow
if !IsValid(ragdoll) then self:Remove(); return end
if should_follow_head:GetBool() then
local head = ragdoll:GetPhysicsObjectNum(ragdoll:TranslateBoneToPhysBone(ragdoll:LookupBone("ValveBiped.Bip01_Head1")))
self:SetPos(head:GetPos() + vector_offset_to_shoot_at)
else
self:SetPos(ragdoll:GetPos())
end
self:SetNoDraw(!debug_show_ragdoll_targets:GetBool())
if !ragdoll.organism then return true end
if !ragdoll.organism.alive then self:Remove(); return end
local should_stop_targeting = !stop_targeting_when_unconscious:GetBool() or (
(ragdoll.organism.consciousness <= 0.4) or
(ragdoll.organism.critical)
)
if (should_stop_targeting != self.last_stop_targeting) then
self.last_stop_targeting = should_stop_targeting
if should_stop_targeting then
self:SetColor(Color(255, 0, 0))
for i,ent in ipairs(ents.GetAll()) do
RemoveRelationship(ent, self)
end
else
self:SetColor(Color(0, 255, 0))
for i,ent in ipairs(ents.GetAll()) do
UpdateRelationship(ent, self)
end
end
end
self:NextThink(CurTime())
end
return true
end
function ENT:OnRemove()
if SERVER then
for i,ent in ipairs(ents.GetAll()) do
RemoveRelationship(ent, self)
end
self.dummy_entity:Remove()
hook.Remove("OnEntityCreated", "zcnpci-"..self:GetCreationID())
end
end

View file

@ -1,131 +0,0 @@
AddCSLuaFile()
ENT.Type = "anim"
ENT.Base = "base_anim"
ENT.AutomaticFrameAdvance = true
local bones_to_animate = {
"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",
}
-- Copy pasted directly from the GMOD wiki with small edits
local function SetRagdollPos(ragdoll, pos)
for i = 0, ragdoll:GetPhysicsObjectCount() - 1 do
local phys = ragdoll:GetPhysicsObjectNum(i)
local localPos = ragdoll:WorldToLocal( phys:GetPos() )
phys:SetPos(pos + localPos)
end
end
function ENT:Initialize()
if SERVER then
print("I AM SERVERSIDE")
self:SetModel("models/dav0r/hoverball.mdl")
end
if CLIENT then
print("I AM CLIENTSIDE")
self:SetNoDraw(true)
end
end
function ENT:Think()
if CLIENT then
--hook.Add("physics")
local old_ragdoll = self:GetNWEntity("parent")
local old_npc = self:GetNWEntity("parent_npc")
if IsValid(old_ragdoll) and IsValid(old_npc) then
local ragdoll = self.ragdoll
if !self.ready_to_animate then
self.ragdoll = ClientsideRagdoll(old_ragdoll:GetModel())
ragdoll = self.ragdoll
self.ragdoll:SetNoDraw(false)
self.ragdoll:DrawShadow(true)
SetRagdollPos(self.ragdoll, old_ragdoll:GetPos())
print(self.ragdoll)
print("SHOW UP YOU PIECE OF SHIT")
for i, name in pairs(bones_to_animate) do
local object = ragdoll:GetPhysicsObjectNum(ragdoll:TranslateBoneToPhysBone(ragdoll:LookupBone(name)))
local parent_object = old_ragdoll:GetBoneMatrix(old_ragdoll:LookupBone(name))
print(parent_object)
if parent_object == nil then continue end
object:SetPos(parent_object:GetTranslation())
object:SetAngles(parent_object:GetAngles())
end
self.ready_to_animate = true
end
local fake_start = self:GetNWFloat("fake_start")
local fake_end = self:GetNWFloat("fake_end")
local progress = (CurTime() - fake_start) / (fake_end - fake_start)
for i, name in pairs(bones_to_animate) do
local object = ragdoll:GetPhysicsObjectNum(ragdoll:TranslateBoneToPhysBone(ragdoll:LookupBone(name)))
local parent_bone = old_npc:LookupBone(name)
if parent_bone == -1 then continue end
local parent_bone_matrix = old_npc:GetBoneMatrix(parent_bone)
if !parent_bone_matrix then continue end
local parent_bone_pos, parent_bone_angle = parent_bone_matrix:GetTranslation(), parent_bone_matrix:GetAngles()
local old_bone_pos, old_bone_angle = object:GetPos(), object:GetAngles()
--parent_bone_angle.y = parent_bone_angle.y + 90
local shadow_data = {
secondstoarrive = 0.01,
pos = LerpVector(progress, old_bone_pos, parent_bone_pos),
angle = LerpAngle(progress, old_bone_angle, parent_bone_angle),
maxspeed = 400 * 4,
maxangular = 2000 * 4,
maxspeeddamp = 120 * 4,
maxangularspeeddamp = 600 * 4,
}
-- 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()
--i = i + 1
end
end
end
self:NextThink(CurTime() + 0.1)
return true
end
function ENT:OnRemove()
if CLIENT then
if self.ragdoll then
self.ragdoll:Remove()
end
end
end

View file

@ -19,9 +19,7 @@ local treat_near_death_as_dead = CreateConVar("zcnpci_treat_near_death_as_dead"
local allow_extended_base_npcs = CreateConVar("zcnpci_allow_extended_base_npcs", 1, bit.bor(FCVAR_ARCHIVE, FCVAR_REPLICATED)) local allow_extended_base_npcs = CreateConVar("zcnpci_allow_extended_base_npcs", 1, bit.bor(FCVAR_ARCHIVE, FCVAR_REPLICATED))
local allow_modded_npcs = CreateConVar("zcnpci_allow_modded_npcs", 0, bit.bor(FCVAR_ARCHIVE, FCVAR_REPLICATED)) local allow_modded_npcs = CreateConVar("zcnpci_allow_modded_npcs", 0, bit.bor(FCVAR_ARCHIVE, FCVAR_REPLICATED))
local auto_enable_humanoid_npcs = CreateConVar("zcnpci_auto_enable_humanoid_npcs", 1, bit.bor(FCVAR_ARCHIVE, FCVAR_REPLICATED)) local auto_enable_humanoid_npcs = CreateConVar("zcnpci_auto_enable_humanoid_npcs", 1, bit.bor(FCVAR_ARCHIVE, FCVAR_REPLICATED))
local modded_npc_whitelist = CreateConVar("zcnpci_modded_npc_whitelist", "npc_example_class", bit.bor(FCVAR_ARCHIVE, FCVAR_REPLICATED)) local modded_npc_whitelist = CreateConVar("zcnpci_modded_npc_whitelist", "nb_example", bit.bor(FCVAR_ARCHIVE, FCVAR_REPLICATED))
local debug_ragdoll_all = CreateConVar("zcnpci_debug_ragdoll_all", 0, bit.bor(FCVAR_ARCHIVE, FCVAR_REPLICATED))
-- The way I have to set up the npc whitelist client-side makes it so the host can't configure it without console commands, so we have to do this shit -- The way I have to set up the npc whitelist client-side makes it so the host can't configure it without console commands, so we have to do this shit
local set_modded_npc_whitelist = concommand.Add("zcnpci_set_modded_npc_whitelist", function(ply, cmd, args) local set_modded_npc_whitelist = concommand.Add("zcnpci_set_modded_npc_whitelist", function(ply, cmd, args)
@ -32,6 +30,8 @@ local set_modded_npc_whitelist = concommand.Add("zcnpci_set_modded_npc_whitelis
modded_npc_whitelist:SetString(args[1]) modded_npc_whitelist:SetString(args[1])
end) end)
local debug_ragdoll_all = CreateConVar("zcnpci_debug_ragdoll_all", 0, bit.bor(FCVAR_ARCHIVE, FCVAR_REPLICATED))
local last_dmgpos = {} local last_dmgpos = {}
local last_hitgroup = {} local last_hitgroup = {}
local last_attacker = {} local last_attacker = {}
@ -57,45 +57,6 @@ local corpses = {}
local last_corpse_tick = CurTime() local last_corpse_tick = CurTime()
local last_tick = CurTime() local last_tick = CurTime()
local hook_table = hook.GetTable()
local homigrad_damage_hook = hook_table.EntityTakeDamage["homigrad-damage"]
local homigrad_player_spawn_hook = hook_table.player_spawn["homigrad-spawn3"]
-- We need to override the default homigrad damage hook so it doesn't cause damage to ragdolls we are tryign to fake
hook.Add("EntityTakeDamage", "homigrad-damage", function(ent, dmginfo)
if ent:IsNPC() and ent.organism_no_damage then return false end
homigrad_damage_hook(ent, dmginfo)
end)
-- This disables player bullseye spawning, which fixes issues with combine targeting with ai_ignoreplayers
hook.Add("player_spawn", "homigrad-spawn3", function(data)
local ply = Player(data.userid)
if not IsValid(ply) then return end
ply.bull = {
IsValid = function() return true end,
Remove = function() return end
}
homigrad_player_spawn_hook(data)
end)
-- This is called right after the NPC bullseye is created for player ragdolls, which allows us to replace it with a dummy.
-- Fixes similar issues mentioned in previous comment
hook.Add("Ragdoll_Create", "zcnpci-override", function(ply, ragdoll)
if IsValid(ragdoll.bull) then
ragdoll.bull:Remove()
end
ragdoll.bull = {
IsValid = function() return true end,
SetPos = function() return true end,
Remove = function() return end
}
end)
hook.Add("CreateEntityRagdoll", "zcnpci", function(ent, ragdoll) hook.Add("CreateEntityRagdoll", "zcnpci", function(ent, ragdoll)
if !ent.organism then return end if !ent.organism then return end
@ -157,15 +118,8 @@ hook.Add("CreateEntityRagdoll", "zcnpci", function(ent, ragdoll)
end end
if ent.npcfakeknockback then if ent.npcfakeknockback then
if dmgpos and (phys_bone != -1) then
local phys = ragdoll:GetPhysicsObjectNum(phys_bone)
phys:SetVelocity(ent.npcfakeknockback)
else
ragdoll:GetPhysicsObject():SetVelocity(ent.npcfakeknockback) ragdoll:GetPhysicsObject():SetVelocity(ent.npcfakeknockback)
end end
end
local velocity = ragdoll:GetPhysicsObject():GetVelocity()
-- For some reason, citizen types such as rebels, medics, refugees, etc. use a keyvalue. -- For some reason, citizen types such as rebels, medics, refugees, etc. use a keyvalue.
-- That keyvalue does not transfer over to the ragdoll, despite it looking like the NPC, -- That keyvalue does not transfer over to the ragdoll, despite it looking like the NPC,
@ -173,19 +127,10 @@ hook.Add("CreateEntityRagdoll", "zcnpci", function(ent, ragdoll)
-- Why is it like this? Fuck if I know, I just know I don't wanna deal with it again. -- Why is it like this? Fuck if I know, I just know I don't wanna deal with it again.
ragdoll.citizentype = ent:GetInternalVariable("citizentype") ragdoll.citizentype = ent:GetInternalVariable("citizentype")
ragdoll.respawn_data = {
flags = ent:GetFlags(),
spawn_flags = ent:GetSpawnFlags()
}
ragdoll:AddCallback("PhysicsCollide", function(outEnt, data) hook.Run("Ragdoll Collide", ragdoll, data) end)
timer.Simple(0, function() timer.Simple(0, function()
if !IsValid(ragdoll) then return end
ragdoll.organism.alive = true ragdoll.organism.alive = true
ragdoll:SetRenderMode(RENDERMODE_NORMAL) if !IsValid(ragdoll) then return end
zcnpci.StartModule(ragdoll, "stumble_legs", phys_bone, lpos) zcnpci.StartModule(ragdoll, "stumble_legs", phys_bone, lpos)
last_dmgpos[ent] = nil last_dmgpos[ent] = nil
@ -194,7 +139,6 @@ end)
hook.Add("HomigradDamage", "zcnpci", function(ent, dmginfo) hook.Add("HomigradDamage", "zcnpci", function(ent, dmginfo)
if !enabled:GetBool() or !IsValid(dmginfo) then return end if !enabled:GetBool() or !IsValid(dmginfo) then return end
if !ent:IsNPC() then if !ent:IsNPC() then
if !ent.organism or !ent.StartDie then return end if !ent.organism or !ent.StartDie then return end
@ -206,33 +150,31 @@ hook.Add("HomigradDamage", "zcnpci", function(ent, dmginfo)
return return
end end
last_dmgpos[ent] = dmginfo:GetDamagePosition() last_dmgpos[ent] = dmginfo:GetDamage()
last_attacker[ent] = dmginfo:GetAttacker() last_attacker[ent] = dmginfo:GetAttacker()
if dmginfo:IsDamageType(DMG_BULLET + DMG_BUCKSHOT + DMG_BLAST) then if dmginfo:IsDamageType(DMG_BULLET + DMG_BUCKSHOT + DMG_BLAST) then
if dmginfo:GetDamage() > 3 then if dmginfo:GetDamage() > 1.5 then
table.insert(npcs_to_fake, ent) ent:TakeDamage(ent:Health())
end end
elseif dmginfo:IsDamageType(DMG_CLUB + DMG_SLASH) then elseif dmginfo:IsDamageType(DMG_CLUB + DMG_SLASH) then
local fists = dmginfo:GetAttacker():GetWeapon("weapon_hands_sh")
if !IsValid(fists) then fists = dmginfo:GetAttacker():GetWeapon("weapon_hg_coolhands") end
local attacker = dmginfo:GetAttacker() local attacker = dmginfo:GetAttacker()
if !IsValid(attacker) then return end
if attacker.HasWeapon == nil then return end print(fists.InLegKick)
local fists = attacker:HasWeapon("weapon_hands_sh")
if !fists then fists = attacker:HasWeapon("weapon_hg_coolhands") end
-- Kicks should knock NPCs down -- Kicks should knock NPCs down
if IsValid(dmginfo:GetInflictor()) and fists and attacker.InLegKick and ((attacker.InLegKick + 0.1) > CurTime()) then if IsValid(dmginfo:GetInflictor()) and IsValid(fists) and attacker.InLegKick and ((attacker.InLegKick + 0.1) > CurTime()) then
table.insert(npcs_to_fake, ent) table.insert(npcs_to_fake, ent)
print("ADD TO LIST!!!")
local attacker_angle = dmginfo:GetAttacker():EyeAngles() local attacker_angle = dmginfo:GetAttacker():EyeAngles()
local normal = attacker_angle:Forward(normal) local normal = attacker_angle:Forward(normal)
ent.npcfakeknockback = normal * dmginfo:GetDamage() * 135 ent.npcfakeknockback = normal * dmginfo:GetDamage() * 135
last_dmgpos[ent] = nil
end end
end end
end) end)
@ -346,21 +288,29 @@ hook.Add("Tick", "zcnpci", function()
end end
end end
-- NPC faking is in a seperate area because we want NPCs to fake as soon as they can PrintTable(npcs_to_fake)
for i,ent in pairs(npcs_to_fake) do
ent.organism_no_damage = true
-- NPC faking is in a seperate area because we want NPCs to ragdoll as soon as they can
for i,ent in pairs(npcs_to_fake) do
local damage_info = DamageInfo() local damage_info = DamageInfo()
damage_info:SetDamage(999999) damage_info:SetDamage(0)
damage_info:SetAttacker(ent) damage_info:SetAttacker(ent)
damage_info:SetDamageType( DMG_DIRECT )
damage_info:SetDamageForce(Vector(0, 0, 0)) damage_info:SetDamageForce(Vector(0, 0, 0))
ent:TakeDamageInfo(damage_info) ent:BecomeRagdoll(damage_info)
table.remove(npcs_to_fake, 1) table.remove(npcs_to_fake, 1)
end end
end) 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)]]
hook.Add("OnEntityCreated", "zcnpci", function(ent) hook.Add("OnEntityCreated", "zcnpci", function(ent)
if !IsValid(ent) then return end if !IsValid(ent) then return end

View file

@ -40,31 +40,6 @@ local twitchable_bone_names = {
"ValveBiped.Bip01_L_Foot" "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 Copies of Functions for Optimization
local math_Clamp = math.Clamp local math_Clamp = math.Clamp
local math_Rand = math.Rand local math_Rand = math.Rand
@ -94,24 +69,7 @@ local cv_anim_roll_impact_threshold = CreateConVar("zcnpci_falling_anim_roll_imp
local cv_anim_roll_playback_rate = CreateConVar("zcnpci_falling_anim_roll_playback_rate", "3.0", {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 -- Down time
local minimum_down_time = CreateConVar("zcnpci_down_time", "5", {FCVAR_ARCHIVE, FCVAR_REPLICATED}, "Down time!!!!!!!!!") local minimum_down_time = CreateConVar("zcnpci_down_time", "3", {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")
local movement_sensitivity = CreateConVar("zcnpci_movement_sensitivity", "40", {FCVAR_ARCHIVE, FCVAR_REPLICATED})
local death_timer = CreateConVar("zcnpci_death_timer", "-1", {FCVAR_ARCHIVE, FCVAR_REPLICATED})
-- Targeting
local npc_targeting_enabled = CreateConVar("zcnpci_npc_targeting_enabled", "1", {FCVAR_ARCHIVE, FCVAR_REPLICATED}, "Should NPCs target downed NPCs")
-- Flailing
local flailing_playback_rate = CreateConVar("zcnpci_flailing_playback_rate", "1.0", {FCVAR_ARCHIVE, FCVAR_REPLICATED})
local flailing_panic_multiplier = CreateConVar("zcnpci_flailing_panic_multiplier", "2.5", {FCVAR_ARCHIVE, FCVAR_REPLICATED})
local flailing_panic_airbone_check_distance = CreateConVar("zcnpci_flailing_panic_airbone_check_distance", "128", {FCVAR_ARCHIVE, FCVAR_REPLICATED})
-- Writhing
local writhing_strength = CreateConVar("zcnpci_writhing_strength", "1.0", {FCVAR_ARCHIVE, FCVAR_REPLICATED})
--[[------------------------------------------------------------------------- --[[-------------------------------------------------------------------------
Tug function Tug function
@ -207,15 +165,7 @@ function MODULE:Init()
self.AnimationRollEndTime = 0 self.AnimationRollEndTime = 0
self.StopProcessing = false self.StopProcessing = false
self.LastThink = CurTime() self.LastThink = CurTime()
self.LastFakeUpCheck = CurTime() self.LastPhysProcess = CurTime()
self.LastPosCheck = target:GetPos()
self.SpawnTimestamp = CurTime()
self.bullseye = ents.Create("npc_ragdoll_target")
self.bullseye:SetPos(target:GetPos())
self.bullseye.ragdoll_to_follow = target
self.bullseye:Spawn()
self.bullseye:Activate()
-- Add damping to all bones. -- Add damping to all bones.
local phys = self:GetPhysicsObject() local phys = self:GetPhysicsObject()
@ -239,29 +189,13 @@ end
---------------------------------------------------------------------------]] ---------------------------------------------------------------------------]]
function MODULE:Think() function MODULE:Think()
--if (CurTime() - self.LastThink) < 1 then return end if (CurTime() - self.LastThink) < 1 then return end
local target = self:GetTarget()
if !IsValid(target) then return end if !IsValid(target) then return end
self.LastThink = CurTime() self.LastThink = CurTime()
local phys = target:GetPhysicsObject() local phys = target:GetPhysicsObject()
if target.FakeUp then
local parent = self.FakeParent
if !IsValid(parent) then return end
if !self.ModelBoneList then return end
--[[for i,v in pairs(self.ModelBoneList) do
local object = target:GetPhysicsObjectNum(target:TranslateBoneToPhysBone(target:LookupBone(v)))
if !IsValid(object) then continue end
object:EnableCollisions(false)
end]]
end
if !IsValid(phys) or !phys:IsAsleep() then return end if !IsValid(phys) or !phys:IsAsleep() then return end
phys:Wake() phys:Wake()
@ -308,79 +242,15 @@ end
Physics Simulation Hook (FIXED) Physics Simulation Hook (FIXED)
---------------------------------------------------------------------------]] ---------------------------------------------------------------------------]]
function MODULE:PhysicsSimulate(phys, dt) function MODULE:PhysicsSimulate(phys, dt)
if !SERVER then return end if self.StopProcessing then return false end
--print((CurTime() - self.LastPhysProcess) < 0.05)
--if (CurTime() - self.LastPhysProcess) < (1 /) then return false end
self.LastPhysProcess = CurTime()
local cur_time = CurTime() local cur_time = CurTime()
local target = self:GetTarget() local target = self:GetTarget()
if not IsValid(target) then self:Remove(); self.bullseye:Remove(); return false end if not IsValid(target) then self:Remove(); return false end
if target.FakeUp then
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
local name = target:GetBoneName(i)
local object = target:LookupBone(name)
if object == -1 then i = i + 1; continue end
self.ModelBoneList[name] = target:GetBoneMatrix(object)
i = i + 1
end
end
local animation_progress = (CurTime() - self.FakeUpStart) / (self.FakeUpEnd - self.FakeUpStart)
for i, name in pairs(fakeup_bone_names) do
local object = target:GetPhysicsObjectNum(target:TranslateBoneToPhysBone(target:LookupBone(name)))
local parent_bone = parent:LookupBone(name)
if parent_bone == -1 then continue end
local parent_bone_matrix = parent:GetBoneMatrix(parent_bone)
if !parent_bone_matrix then continue end
local parent_bone_pos, parent_bone_angle = parent_bone_matrix:GetTranslation(), parent_bone_matrix:GetAngles()
local old_bone_pos, old_bone_angle = object:GetPos(), object:GetAngles()
--parent_bone_angle.y = parent_bone_angle.y + 90
local shadow_data = {
secondstoarrive = 0.01,
pos = LerpVector(animation_progress, old_bone_pos, parent_bone_pos),
angle = LerpAngle(animation_progress, old_bone_angle, 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()
--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:SetAngles(target:EyeAngles())
target.is_npc_corpse = true target.is_npc_corpse = true
@ -393,7 +263,7 @@ function MODULE:PhysicsSimulate(phys, dt)
end end
-- Force multiplier -- Force multiplier
local f = target.organism.consciousness * writhing_strength:GetFloat() local f = target.organism.consciousness
local minimum_down_timer = 0 local minimum_down_timer = 0
if target.StartDie then if target.StartDie then
@ -405,58 +275,18 @@ function MODULE:PhysicsSimulate(phys, dt)
return false -- Cut the bullshit return false -- Cut the bullshit
end end
if (death_timer:GetFloat() != -1) and ((CurTime() - self.SpawnTimestamp) > death_timer:GetFloat()) then
target.organism.alive = false
end
if (!target.organism.alive) then if (!target.organism.alive) then
-- If the NPC is dead, they probably aren't coming back; don't bother bringing them back to life -- If the NPC is dead, they probably aren't coming back; don't bother bringing them back to life
self:Remove() self:Remove()
return false -- Cut the bullshit return false -- Cut the bullshit
elseif ( elseif (target.organism.consciousness <= 0.5) or ((target.organism.lleg >= 0.85) and (target.organism.rleg >= 0.85)) then
(target.organism.consciousness <= 0.4) or
((target.organism.lleg >= 0.85) and (target.organism.rleg >= 0.85)) or
(target.organism.spine1 == 1) or
(target.organism.spine2 == 1) or
(target.organism.spine3 == 1)
) then
target.StartDie = cur_time target.StartDie = cur_time
return false return false
end end
if (self.LastPosCheck:DistToSqr(target:GetPos()) > (movement_sensitivity:GetFloat() ^ 2)) then -- Getting up
self.LastPosCheck = target:GetPos() -- Don't need to check for consciousness and the like because we've done it already above
target.StartDie = cur_time
end
local distance_check = util.TraceLine({
start = target:GetPos(),
endpos = target:GetPos() + Vector(0, 0, -128),
filter = {target},
mask = MASK_SOLID
}).Hit
if !distance_check then
target.StartDie = cur_time
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 ( if (
(!tracehull.Hit) and
(minimum_down_timer >= 1.0) and (minimum_down_timer >= 1.0) and
(target.class_in_previous_life != nil) 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.llegamputated or target.organism.rlegamputated or target.organism.larmamputated or target.organism.rarmamputated) and
@ -471,9 +301,6 @@ function MODULE:PhysicsSimulate(phys, dt)
ent:SetSkin(target:GetSkin()) ent:SetSkin(target:GetSkin())
ent:AddFlags(target.respawn_data.flags)
ent:AddSpawnFlags(target.respawn_data.spawn_flags)
if target.citizentype then if target.citizentype then
ent:SetKeyValue("citizentype", target.citizentype) ent:SetKeyValue("citizentype", target.citizentype)
end end
@ -484,9 +311,6 @@ function MODULE:PhysicsSimulate(phys, dt)
ent:Spawn() ent:Spawn()
ent:SetNotSolid(true)
ent:SetNPCState(NPC_STATE_NONE)
timer.Simple(0, function() timer.Simple(0, function()
hg.organism.Add(ent) hg.organism.Add(ent)
table.Merge(ent.organism, target.organism) table.Merge(ent.organism, target.organism)
@ -497,8 +321,6 @@ function MODULE:PhysicsSimulate(phys, dt)
ent.organism.alive = true ent.organism.alive = true
ent.organism.owner = ent ent.organism.owner = ent
target.organism = nil
ent:CallOnRemove("organism", hg.organism.Remove, ent) ent:CallOnRemove("organism", hg.organism.Remove, ent)
hg.send_bareinfo(ent.organism) hg.send_bareinfo(ent.organism)
@ -508,49 +330,14 @@ function MODULE:PhysicsSimulate(phys, dt)
ent:SetBodygroup(i, target:GetBodygroup(i)) ent:SetBodygroup(i, target:GetBodygroup(i))
end end
self.unfaker:Spawn() target:Remove()
self.unfaker:Activate()
end) end)
target.FakeUp = true self.StopProcessing = 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)
self.unfaker = ents.Create("npc_ragdoll_unfaker")
self.unfaker:SetPos(target:GetPos())
self.unfaker:SetNWEntity("parent", target)
self.unfaker:SetNWEntity("parent_npc", ent)
self.unfaker:SetNWFloat("fake_start", self.FakeUpStart)
self.unfaker:SetNWFloat("fake_end", self.FakeUpEnd)
target:SetRenderMode(RENDERMODE_NONE)
ent:SetRenderMode(RENDERMODE_NONE)
timer.Simple(self.FakeUpTime, function()
if !IsValid(ent) then return end
ent:SetNotSolid(false)
ent:SetNPCState(NPC_STATE_IDLE)
ent:SetRenderMode(RENDERMODE_NORMAL)
target:Remove()
self:Remove() self:Remove()
end)
return false return false
end end
end
local phys_bone_id = phys:GetID() local phys_bone_id = phys:GetID()
@ -566,17 +353,7 @@ function MODULE:PhysicsSimulate(phys, dt)
-- Logic for fall animation -- Logic for fall animation
local vel = phys:GetVelocity() local vel = phys:GetVelocity()
local pbr = math_Clamp(vel.z / -600, 0.5, 1.5) * flailing_playback_rate:GetFloat() local pbr = math_Clamp(vel.z / -600, 0.5, 1.5)
target.panicking = (
target:IsOnFire() or
(!distance_check) or
(target.organism.pain > 80)
)
if target.panicking then
pbr = pbr * flailing_panic_multiplier:GetFloat()
end
if self:GetSequence() ~= self:LookupSequence("Choked_Barnacle") or self:GetPlaybackRate() ~= pbr then if self:GetSequence() ~= self:LookupSequence("Choked_Barnacle") or self:GetPlaybackRate() ~= pbr then
local seq_idle = self:LookupSequence("Choked_Barnacle") local seq_idle = self:LookupSequence("Choked_Barnacle")
@ -627,8 +404,6 @@ end
function MODULE:OnRemove() function MODULE:OnRemove()
local timer_name = "Fedhoria_FallingLegs_Twitch_" .. self:EntIndex() local timer_name = "Fedhoria_FallingLegs_Twitch_" .. self:EntIndex()
timer_Remove(timer_name) timer_Remove(timer_name)
if IsValid(self.unfaker) then self.unfaker:Remove() end
end end
--[[------------------------------------------------------------------------- --[[-------------------------------------------------------------------------

View file

@ -35,12 +35,6 @@ local cv_anim_roll_duration = CreateConVar("zcnpci_falling_anim_roll_duration",
local cv_anim_roll_impact_threshold = CreateConVar("zcnpci_falling_anim_roll_impact_threshold", "300", {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}, "Скорость воспроизведения анимации") local cv_anim_roll_playback_rate = CreateConVar("zcnpci_falling_anim_roll_playback_rate", "3.0", {FCVAR_ARCHIVE, FCVAR_REPLICATED}, "Скорость воспроизведения анимации")
local flailing_playback_rate = CreateConVar("zcnpci_flailing_playback_rate", "1.0", {FCVAR_ARCHIVE, FCVAR_REPLICATED})
local flailing_panic_multiplier = CreateConVar("zcnpci_flailing_panic_multiplier", "2.5", {FCVAR_ARCHIVE, FCVAR_REPLICATED})
-- Writhing
local writhing_strength = CreateConVar("zcnpci_writhing_strength", "1.0", {FCVAR_ARCHIVE, FCVAR_REPLICATED})
-- Do it! -- Do it!
function MODULE:DoTwitch() function MODULE:DoTwitch()
@ -176,10 +170,7 @@ end
function MODULE:PhysicsSimulate(phys, dt) function MODULE:PhysicsSimulate(phys, dt)
local cur_time = CurTime() local cur_time = CurTime()
local target = self:GetTarget() local target = self:GetTarget()
if !IsValid(target) then self:Remove(); return false end -- если цели нет, то идем нахуй if not IsValid(target) then self:Remove(); return false end -- если цели нет, то идем нахуй
if !target.organism then self:Remove(); return false end
if target.FakeUp then self.AnimationRollEndTime = 0; return false end
-- проверка на физику -- проверка на физику
if cur_time < self.AnimationRollEndTime then if cur_time < self.AnimationRollEndTime then
@ -193,7 +184,7 @@ function MODULE:PhysicsSimulate(phys, dt)
-- --- логика идет нахуй -- --- логика идет нахуй
-- Force multiplier -- Force multiplier
local f = target.organism.consciousness * writhing_strength:GetFloat() local f = target.organism.consciousness
-- ебаная логика для regmod -- ебаная логика для regmod
if ragmod and ragmod:IsRagmodRagdoll(target) then if ragmod and ragmod:IsRagmodRagdoll(target) then
@ -214,11 +205,7 @@ function MODULE:PhysicsSimulate(phys, dt)
-- If the NPC is dead, they probably aren't coming back; don't bother bringing them back to life -- If the NPC is dead, they probably aren't coming back; don't bother bringing them back to life
self:Remove() self:Remove()
return false -- Cut the bullshit return false -- Cut the bullshit
elseif ( elseif (target.organism.consciousness <= 0.3) then
(target.organism.consciousness <= 0.4) or
(target.organism.spine2 == 1) or
(target.organism.spine3 == 1)
) then
return false return false
end end
@ -239,12 +226,7 @@ function MODULE:PhysicsSimulate(phys, dt)
-- логика для анимации переката -- логика для анимации переката
local vel = phys:GetVelocity() local vel = phys:GetVelocity()
local pbr = math_Clamp(vel.z / -600, 0.5, 1.5) * flailing_playback_rate:GetFloat() local pbr = math_Clamp(vel.z / -600, 0.5, 1.5)
if target.panicking then
pbr = pbr * flailing_panic_multiplier:GetFloat()
end
-- падения и ебаная скорость -- падения и ебаная скорость
-- если мы не в.. а впрочем иди нахуй -- если мы не в.. а впрочем иди нахуй
if self:GetSequence() ~= self:LookupSequence("idleonfire") or self:GetPlaybackRate() ~= pbr then if self:GetSequence() ~= self:LookupSequence("idleonfire") or self:GetPlaybackRate() ~= pbr then

View file

@ -279,8 +279,6 @@ local trace = {output={}}
local tr = trace.output local tr = trace.output
function MODULE:PhysicsSimulate(phys, dt) function MODULE:PhysicsSimulate(phys, dt)
if self.FakeUp then return end
local phys_bone = phys:GetID() local phys_bone = phys:GetID()
local target = self:GetTarget() local target = self:GetTarget()
@ -293,11 +291,6 @@ function MODULE:PhysicsSimulate(phys, dt)
local st = stumble_time:GetFloat() local st = stumble_time:GetFloat()
-- End stumbling if player is dead
if !target.organism or !target.organism.alive then
st = 0
end
if (st <= 0) then if (st <= 0) then
self:Remove() self:Remove()
return false return false
@ -305,6 +298,16 @@ function MODULE:PhysicsSimulate(phys, dt)
local f = 1 - (CurTime() - self.Created) / st 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 if (f <= 0) then
self:Remove() self:Remove()
return false return false