Compare commits

...

6 commits

6 changed files with 289 additions and 148 deletions

View file

@ -2,21 +2,31 @@
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.
- NPCs can now be alive while ragdolled, and can move around in that state.
- NPCs can collapse due to unconsciousness or pain.
- NPCs can get back up after being ragdolled.
- Various performance settings are included, with the biggest one being automatic corpse removal.
- 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.
- Various configurable settings are included, allowing you to tweak the mod to your liking.
This plugin 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 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.
## Known issues
- Kicking NPCs does not knock them down. This is not something I can easily fix -- I would have to override code in Z-City, which I would rather not do.
- 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.
- 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.
## Incompatibilities
## Compatibility
- Any mod that modifies the behavior of death ragdolls (Reagdoll, Artagdoll, Fedhoria)
This should be compatible with:
- The majority of NPC AI improvement mods
- Competently-programmed Z-City addons
It is, however, not compatbile with:
- 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.)
## Future plans

View file

@ -1,11 +1,34 @@
local function PopulateCustomNPCSBXToolMenu(pnl)
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: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:CheckBox("Allow modded NPCs", "zcnpci_allow_modded_npcs")
pnl:ControlHelp("Enable or disables applying custom health system to modded NPCs.")
pnl:CheckBox("Auto enable for all humanoid NPCs", "zcnpci_auto_enable_humanoid_npcs")
pnl:ControlHelp("If enabled, NPCs will automatically have the health system applied to them if they have a standard humanoid skeleton.")
pnl:Help("List of enabled modded NPC classes")
local text = vgui.Create("DTextEntry")
text:SetMultiline(true)
text:SetTall(200)
text:SetValue(modded_npc_whitelist:GetString())
text.OnChange = function (self)
RunConsoleCommand("zcnpci_set_modded_npc_whitelist", text:GetValue())
end
pnl:AddItem(text)
pnl:Help("One NPC class per line. No spaces or other characters. Can only be set by a superadmin or server operator.")
end
local function PopulateRagdollSBXToolMenu(pnl)
pnl:CheckBox("Enabled", "zcnpci_enabled")
pnl:ControlHelp("Enable or disable the addon.")
pnl:Help(" ")
pnl:Help(" ")
pnl:NumSlider("Stumble time", "zcnpci_stumble_time", 0, 10, 3)
pnl:ControlHelp("How long the ragdoll should try to stumble for.")
@ -20,8 +43,15 @@ local function PopulateRagdollSBXToolMenu(pnl)
end
local function PopulatePerformanceSBXToolMenu(pnl)
pnl:NumSlider("Activation range", "zcnpci_active_range", 0, 5000, 0)
pnl:ControlHelp("How close the ragdoll has to be to a player to be living when downed by damage. Enemies downed by other causes are unaffected.")
pnl:NumSlider("NPC ticks per second", "zcnpci_tps", 1, 66, 0)
pnl:ControlHelp("How many times NPCs should be processed per second. Will make them more responsive at the cost of performance.")
pnl:Help(" ")
pnl:Help(" ")
pnl:NumSlider("Activation range", "zcnpci_active_range", 0, 32768, 0)
pnl:ControlHelp("How close the ragdoll has to be to a player to live when downed by direct damage. Enemies downed by other causes are unaffected.")
pnl:CheckBox("Always activate on player kill", "zcnpci_always_active_on_player_kill")
pnl:ControlHelp("If on, all player-damaged ragdolls will be activated regardless of other factors.")
@ -33,6 +63,9 @@ local function PopulatePerformanceSBXToolMenu(pnl)
pnl:CheckBox("Enable automatic corpse removal", "zcnpci_enable_corpse_removal")
pnl:ControlHelp("Enable or disable the removal of dead corpses.")
pnl:NumSlider("Corpse time between loops", "zcnpci_corpse_loop_time", 1, 66, 0)
pnl:ControlHelp("How long should we wait in between checks?")
pnl:NumSlider("Max corpses", "zcnpci_max_corpses", -1, 64, 0)
pnl:ControlHelp("The maximum amount of dead corpses that are allowed before one is removed. Set to -1 to disable")
@ -42,30 +75,53 @@ local function PopulatePerformanceSBXToolMenu(pnl)
pnl:NumSlider("Max distance between player", "zcnpci_max_corpse_distance", -1, 32768, 2)
pnl:ControlHelp("If the corpse is further than this distance, it will be removed. Set to -1 to disable")
pnl:CheckBox("Never remove corpses near the player", "zcnpci_disable_corpse_removal_near_player")
pnl:ControlHelp("If on, corpses under the variable above in distance will never be cleared.")
pnl:NumSlider("Min distance between player", "zcnpci_min_corpse_distance", 0, 32768, 2)
pnl:ControlHelp("If the corpse is closer than this distance, it will never be cleared.")
pnl:CheckBox("Treat crippled NPCs as dead", "zcnpci_treat_crippled_as_dead")
pnl:ControlHelp("If on, corpses with amputations or both legs broken will be clearable.")
pnl:ControlHelp("If on, bodies with amputations or both legs broken will be clearable.")
pnl:CheckBox("Treat unconscious NPCs as dead", "zcnpci_treat_unconscious_as_dead")
pnl:ControlHelp("If on, bodies that are unconscious will be clearable.")
pnl:CheckBox("Treat near-death NPCs as dead", "zcnpci_treat_near_death_as_dead")
pnl:ControlHelp("If on, corpses near death (severe blood loss, comas, etc.) will be clearable.")
pnl:ControlHelp("If on, bodies near death (severe blood loss, comas, etc.) will be clearable.")
end
local function PopulateMainSBXToolMenu(pnl)
pnl:CheckBox("Enabled", "zcnpci_enabled")
pnl:ControlHelp("Enable or disable the addon.")
pnl:Help("This addon was developed by ToasterPanic.")
pnl:Help("It is based on Kazarei's Euphoria, which in turn is a fork of Fedhoria by Rama. Their work powers the ragdoll movement.")
end
if engine.ActiveGamemode() == "sandbox" then
hook.Add("AddToolMenuCategories", "ZCNPCICategory", function()
spawnmenu.AddToolCategory("Utilities", "zcnpci", "Z-City NPCi")
end)
hook.Add("PopulateToolMenu", "ZCNPCIMenuSettings", function()
spawnmenu.AddToolMenuOption("Utilities", "zcnpci", "RagdollSettings", "Ragdoll", "", "", function(pnl)
spawnmenu.AddToolMenuOption("Utilities", "zcnpci", "ModdedNPCSettings", "Custom NPCs", "", "", function(pnl)
pnl:ClearControls()
PopulateRagdollSBXToolMenu(pnl)
PopulateCustomNPCSBXToolMenu(pnl)
end)
spawnmenu.AddToolMenuOption("Utilities", "zcnpci", "PerformanceSettings", "Performance", "", "", function(pnl)
pnl:ClearControls()
PopulatePerformanceSBXToolMenu(pnl)
end)
spawnmenu.AddToolMenuOption("Utilities", "zcnpci", "RagdollSettings", "Ragdoll", "", "", function(pnl)
pnl:ClearControls()
PopulateRagdollSBXToolMenu(pnl)
end)
spawnmenu.AddToolMenuOption("Utilities", "zcnpci", "MainSettings", "Main", "", "", function(pnl)
pnl:ClearControls()
PopulateMainSBXToolMenu(pnl)
end)
end)
end

View file

@ -1,23 +1,57 @@
include("zcnpci/modules.lua")
local enabled = CreateConVar("zcnpci_enabled", 1, bit.bor(FCVAR_ARCHIVE, FCVAR_REPLICATED))
local active_range = CreateConVar("zcnpci_active_range", 3000, bit.bor(FCVAR_ARCHIVE, FCVAR_REPLICATED))
local active_range = CreateConVar("zcnpci_active_range", 32768, bit.bor(FCVAR_ARCHIVE, FCVAR_REPLICATED))
local always_active_on_player_kill = CreateConVar("zcnpci_always_active_on_player_kill", 1, bit.bor(FCVAR_ARCHIVE, FCVAR_REPLICATED))
local ticks_per_second = CreateConVar("zcnpci_tps", 20, bit.bor(FCVAR_ARCHIVE, FCVAR_REPLICATED))
local remove_corpses = CreateConVar("zcnpci_enable_corpse_removal", 0, bit.bor(FCVAR_ARCHIVE, FCVAR_REPLICATED))
local corpse_loop_time = CreateConVar("zcnpci_corpse_loop_time", 2.0, bit.bor(FCVAR_ARCHIVE, FCVAR_REPLICATED))
local max_corpses = CreateConVar("zcnpci_max_corpses", 8, bit.bor(FCVAR_ARCHIVE, FCVAR_REPLICATED))
local max_corpse_time = CreateConVar("zcnpci_max_corpse_time", 120, bit.bor(FCVAR_ARCHIVE, FCVAR_REPLICATED))
local min_corpse_distance = CreateConVar("zcnpci_min_corpse_distance", 500, bit.bor(FCVAR_ARCHIVE, FCVAR_REPLICATED))
local max_corpse_distance = CreateConVar("zcnpci_max_corpse_distance", 2500, bit.bor(FCVAR_ARCHIVE, FCVAR_REPLICATED))
local no_corpse_removal_near_player = CreateConVar("zcnpci_disable_corpse_removal_near_player", 0, bit.bor(FCVAR_ARCHIVE, FCVAR_REPLICATED))
local treat_crippled_as_dead = CreateConVar("zcnpci_treat_crippled_as_dead", 1, bit.bor(FCVAR_ARCHIVE, FCVAR_REPLICATED))
local treat_unconscious_as_dead = CreateConVar("zcnpci_treat_unconscious_as_dead", 1, bit.bor(FCVAR_ARCHIVE, FCVAR_REPLICATED))
local treat_near_death_as_dead = CreateConVar("zcnpci_treat_near_death_as_dead", 1, bit.bor(FCVAR_ARCHIVE, FCVAR_REPLICATED))
local debug_ragdoll_all = CreateConVar("zcnpci_debug_ragdoll_all", 0, bit.bor(FCVAR_ARCHIVE, FCVAR_REPLICATED, FCVAR_PROTECTED))
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 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", "nb_example", 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
local set_modded_npc_whitelist = concommand.Add("zcnpci_set_modded_npc_whitelist", function(ply, cmd, args)
if !IsValid(ply) or !args[1] then return end
if !ply:IsSuperAdmin() then return end
modded_npc_whitelist:SetString(args[1])
end)
local debug_ragdoll_all = CreateConVar("zcnpci_debug_ragdoll_all", 0, bit.bor(FCVAR_ARCHIVE, FCVAR_REPLICATED))
local last_dmgpos = {}
local last_hitgroup = {}
local last_attacker = {}
local npcs_to_fake = {}
-- These NPCs do not have organisms by default, despite being humanoid characters built into the game.
local base_npc_whitelist = {
"npc_kleiner",
"npc_breen",
"npc_barney",
"npc_alyx",
"npc_eli",
"npc_gman",
"npc_magnusson",
"npc_mossman",
"npc_odessa",
"npc_monk"
}
local corpses = {}
local last_corpse_tick = CurTime()
@ -66,6 +100,33 @@ hook.Add("CreateEntityRagdoll", "zcnpci", function(ent, ragdoll)
ragdoll.class_in_previous_life = ent:GetClass()
if !ragdoll.organism then
ragdoll.inventory = {}
ragdoll.inventory.Weapons = {}
hg.organism.Add(ragdoll)
table.Merge(ragdoll.organism, ent.organism)
hook.Run("RagdollDeath", ent, ragdoll)
ragdoll.organism.owner = ragdoll
ragdoll:CallOnRemove("organism", hg.organism.Remove, ragdoll)
ragdoll.organism.owner.fullsend = true
hg.send_bareinfo(ragdoll.organism)
ent.organism = nil
end
if ent.npcfakeknockback then
ragdoll:GetPhysicsObject():SetVelocity(ent.npcfakeknockback)
end
-- 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,
-- and you have to manually save it to the ragdoll to be able to make it work.
-- 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")
timer.Simple(0, function()
ragdoll.organism.alive = true
@ -76,24 +137,60 @@ hook.Add("CreateEntityRagdoll", "zcnpci", function(ent, ragdoll)
end)
end)
hook.Add("EntityTakeDamage", "zcnpci", function(ent, dmginfo)
if !enabled:GetBool() then return end
if (!ent:IsNPC() or dmginfo:GetDamage() < ent:Health()) then return end
hook.Add("HomigradDamage", "zcnpci", function(ent, dmginfo)
if !enabled:GetBool() or !IsValid(dmginfo) then return end
if !ent:IsNPC() then
if !ent.organism or !ent.StartDie then return end
last_dmgpos[ent] = dmginfo:GetDamagePosition()
if dmginfo:IsDamageType(DMG_BULLET + DMG_BUCKSHOT + DMG_BLAST + DMG_CLUB + DMG_SLASH + DMG_GENERIC) then
-- Reset ragdoll get up timer -- don't want someone getting up mid-curbstomp
ent.StartDie = CurTime()
end
return
end
last_dmgpos[ent] = dmginfo:GetDamage()
last_attacker[ent] = dmginfo:GetAttacker()
if dmginfo:IsDamageType(DMG_BULLET + DMG_BUCKSHOT + DMG_BLAST) then
if dmginfo:GetDamage() > 3 then
table.insert(npcs_to_fake, ent)
end
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()
print(fists.InLegKick)
-- Kicks should knock NPCs down
if IsValid(dmginfo:GetInflictor()) and IsValid(fists) and attacker.InLegKick and ((attacker.InLegKick + 0.1) > CurTime()) then
table.insert(npcs_to_fake, ent)
print("ADD TO LIST!!!")
local attacker_angle = dmginfo:GetAttacker():EyeAngles()
local normal = attacker_angle:Forward(normal)
ent.npcfakeknockback = normal * dmginfo:GetDamage() * 135
end
end
end)
hook.Add("ScaleNPCDamage", "zcnpci", function(npc, hitgroup, dmginfo)
if not IsValid(npc) then return end
if !IsValid(npc) then return end
last_hitgroup[npc] = hitgroup
end)
hook.Add("Think", "zcnpci", function()
local do_corpse_loop = (CurTime() - last_corpse_tick) > 2.0
local do_general_loop = (CurTime() - last_tick) > 0.1
hook.Add("Tick", "zcnpci", function()
local do_corpse_loop = (CurTime() - last_corpse_tick) > corpse_loop_time:GetFloat()
local do_general_loop = (CurTime() - last_tick) > (1 / ticks_per_second:GetInt())
if do_general_loop then
last_tick = CurTime()
if do_corpse_loop then
last_corpse_tick = CurTime()
@ -111,7 +208,6 @@ hook.Add("Think", "zcnpci", function()
table.remove(corpses, 1)
end
end
for i, ent in pairs(ents.GetAll()) do
if !IsValid(ent) then continue end
if !ent.organism then continue end
@ -125,15 +221,10 @@ hook.Add("Think", "zcnpci", function()
(ent.organism.consciousness <= 0.3) or
(ent.organism.pain > 90) or
ent:IsOnFire() or
debug_ragdoll_all:GetBool()
ent.neednpcfake or
debug_ragdoll_all:GetBool()
) then
local damage_info = DamageInfo()
damage_info:SetDamage(ent:Health())
damage_info:SetAttacker(ent)
damage_info:SetDamageType( DMG_DIRECT )
damage_info:SetDamageForce(Vector(0, 0, 0))
ent:TakeDamageInfo(damage_info)
table.insert(npcs_to_fake, ent)
end
elseif do_corpse_loop and remove_corpses and ent.is_npc_corpse then
local clearable = false
@ -150,8 +241,10 @@ hook.Add("Think", "zcnpci", function()
(ent.organism.blood < 3000) or
(ent.organism.pulse < 10)
) then clearable = true end
if treat_unconscious_as_dead:GetBool() and (ent.organism.consciousness <= 0.3)
then clearable = true end
if !clearable then return end
if !clearable then continue end
-- Add to corpse list if not there already
if !table.HasValue(corpses, ent) then
@ -160,6 +253,7 @@ hook.Add("Think", "zcnpci", function()
-- Get nearest player for future checks
local nearby_player
local player_too_close
local players = player.GetAll()
for i, loop_player in pairs(players) do
@ -169,11 +263,15 @@ hook.Add("Think", "zcnpci", function()
if (distance <= max_corpse_distance:GetFloat()) or (max_corpse_distance:GetFloat() == -1) then
nearby_player = loop_player
end
if (distance <= min_corpse_distance:GetFloat()) then
player_too_close = loop_player
break
end
end
if nearby_player and IsValid(nearby_player) and no_corpse_removal_near_player:GetBool() then continue
if player_too_close and IsValid(player_too_close) then continue
elseif (max_corpse_distance:GetFloat() != -1) and !nearby_player then
ent:Remove()
continue
@ -185,12 +283,25 @@ hook.Add("Think", "zcnpci", function()
ent:Remove()
continue
end
print((CurTime() - ent.corpse_timestamp) > max_corpse_time:GetFloat())
end
end
end
end
PrintTable(npcs_to_fake)
-- 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()
damage_info:SetDamage(0)
damage_info:SetAttacker(ent)
damage_info:SetDamageType( DMG_DIRECT )
damage_info:SetDamageForce(Vector(0, 0, 0))
ent:BecomeRagdoll(damage_info)
table.remove(npcs_to_fake, 1)
end
end)
--[[hook.Add("OnNPCKilled", "Fedhoria", function(ent, attacker, inflictor)
@ -200,46 +311,30 @@ end)
last_dmgpos[ent] = dmginfo:GetDamagePosition()
end)]]
local once = true
hook.Add("OnEntityCreated", "zcnpci", function(ent)
if !IsValid(ent) then return end
--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
local add_organism = false
if allow_modded_npcs:GetBool() then
local modded_npc_whitelist_string = modded_npc_whitelist:GetString()
modded_npc_whitelist_string = string.Replace(modded_npc_whitelist_string, "\n", " ")
local modded_npc_whitelist_table = string.Split(modded_npc_whitelist_string, " ")
if table.HasValue(modded_npc_whitelist_table, ent:GetClass()) then add_organism = true 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)]]
if allow_extended_base_npcs:GetBool() and table.HasValue(base_npc_whitelist, ent:GetClass()) then add_organism = true end
if add_organism then
hg.organism.Add(ent)
hg.organism.Clear(ent.organism)
ent.organism.fakePlayer = true
end
end)
local once = true
local PLAYER = FindMetaTable("Player")
@ -282,37 +377,4 @@ 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)]]
end

View file

@ -75,6 +75,7 @@ function ENTITY:GetClosestPhysBone(pos)
end
if !collides then return end
if type(pos) != "Vector" then return end
local closest_bone
local dist = math.huge

View file

@ -149,16 +149,19 @@ function MODULE:StartAnimationRoll()
return
end
--self.StartDie = nil
--target.StartDie = nil
end
function MODULE:Init()
local seq = self:LookupSequence("Choked_Barnacle")
if seq then self:ResetSequence(seq) end
local target = self:GetTarget()
self:SetPlaybackRate(1)
self.LastCollideTime = 0
self.LastGroundCollideTime = 0
self.StartDie = nil
target.StartDie = nil
self.AnimationRollEndTime = 0
self.StopProcessing = false
self.LastThink = CurTime()
@ -251,20 +254,20 @@ function MODULE:PhysicsSimulate(phys, dt)
target.is_npc_corpse = true
if !self.StartDie then self.StartDie = cur_time end
if !target.StartDie then target.StartDie = cur_time end
-- Check for active animation
if cur_time < self.AnimationRollEndTime then
--self.StartDie = nil -- Resetting the "death" timer
--target.StartDie = nil -- Resetting the "death" timer
return true -- We use standard physics.
end
-- Logic for the disappearance timer
local f = 1 -- Force multiplier (default: 1)
-- Force multiplier
local f = target.organism.consciousness
local minimum_down_timer = 0
if self.StartDie then
minimum_down_timer = math_Clamp((cur_time - self.StartDie) / minimum_down_time:GetFloat(), 0, 1)
if target.StartDie then
minimum_down_timer = math_Clamp((cur_time - target.StartDie) / minimum_down_time:GetFloat(), 0, 1)
end
if !target.organism then
@ -276,8 +279,8 @@ function MODULE:PhysicsSimulate(phys, dt)
-- If the NPC is dead, they probably aren't coming back; don't bother bringing them back to life
self:Remove()
return false -- Cut the bullshit
elseif (target.organism.consciousness <= 0.3) or ((target.organism.lleg >= 0.85) and (target.organism.rleg >= 0.85)) then
self.StartDie = cur_time
elseif (target.organism.consciousness <= 0.5) or ((target.organism.lleg >= 0.85) and (target.organism.rleg >= 0.85)) then
target.StartDie = cur_time
return false
end
@ -288,19 +291,22 @@ function MODULE:PhysicsSimulate(phys, dt)
(target.class_in_previous_life != nil) and
!(target.organism.llegamputated or target.organism.rlegamputated or target.organism.larmamputated or target.organism.rarmamputated) and
(target.organism.pain <= 80) and
(target.organism.consciousness > 0.65) and
!target:IsOnFire()
) then
local ent = ents.Create(target.class_in_previous_life)
ent:SetPos(target:GetPos())
ent:SetModel(target:GetModel())
ent:SetMaterial(target:GetMaterial())
if target:GetNumBodyGroups() > 0 then
local i = 1
while (i <= target:GetNumBodyGroups()) do
ent:SetBodygroup(i, target:GetBodygroup(i))
ent:SetSkin(target:GetSkin())
i = i + 1
end
if target.citizentype then
ent:SetKeyValue("citizentype", target.citizentype)
end
for i = 0, target:GetNumBodyGroups() - 1 do
ent:SetBodygroup(i, target:GetBodygroup(i))
end
ent:Spawn()
@ -318,6 +324,12 @@ function MODULE:PhysicsSimulate(phys, dt)
ent:CallOnRemove("organism", hg.organism.Remove, ent)
hg.send_bareinfo(ent.organism)
ent:SetSkin(target:GetSkin())
for i = 0, target:GetNumBodyGroups() - 1 do
ent:SetBodygroup(i, target:GetBodygroup(i))
end
target:Remove()
end)
@ -362,7 +374,7 @@ function MODULE:PhysicsSimulate(phys, dt)
self.last_pos = pos
if offset_sqr < (10*10 * dt*dt) then
self.StartDie = self.StartDie or cur_time
target.StartDie = target.StartDie or cur_time
end
-- Physics correction after collision
@ -375,7 +387,7 @@ function MODULE:PhysicsSimulate(phys, dt)
return true, f
end
-- Logic for other bones
local delta_collide = cur_time - self.LastCollideTime
if (delta_collide < 0.2) then

View file

@ -114,17 +114,20 @@ function MODULE:StartAnimationRoll()
return
end
self.StartDie = nil -- блять я заебался с этой хуйней
target.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
local target = self:GetTarget()
self:SetPlaybackRate(1)
self.LastCollideTime = 0
self.LastGroundCollideTime = 0
self.StartDie = nil
target.StartDie = nil
self.AnimationRollEndTime = 0
-- идте нахуй с этой хуйней
@ -172,7 +175,7 @@ function MODULE:PhysicsSimulate(phys, dt)
-- проверка на физику
if cur_time < self.AnimationRollEndTime then
-- print("[Fedhoria Sim] In Animation Roll. Time left:", self.AnimationRollEndTime - cur_time) -- Debug
-- self.StartDie = nil -- Сбрасываем таймер смерти
-- target.StartDie = nil -- Сбрасываем таймер смерти
return true -- стандартная физика пошла нахуй, у меня по ней 2
-- return false -- если физика нахуй идет
@ -180,18 +183,15 @@ function MODULE:PhysicsSimulate(phys, dt)
-- --- логика идет нахуй
-- логика для таймера
local f = 1 -- сила есть ума не надо (по умолчанию 1)
--[[if self.StartDie then
f = math_Clamp(1 - (cur_time - self.StartDie) / die_time:GetFloat(), 0, 1)
end]]
-- Force multiplier
local f = target.organism.consciousness
-- ебаная логика для 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
target.StartDie = nil
if f <= 0 then self.AnimationRollEndTime = 0 end
end
@ -244,10 +244,10 @@ function MODULE:PhysicsSimulate(phys, dt)
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
target.StartDie = target.StartDie or cur_time -- идите нахуй
-- print("[Fedhoria Sim] Torso seems stationary. StartDie:", target.StartDie) -- Debug
else
-- self.StartDie = nil -- нахуй таймер
-- target.StartDie = nil -- нахуй таймер
-- print("[Fedhoria Sim] Torso moved. Resetting StartDie.") -- Debug
end