Compare commits

..

No commits in common. "85eb62a495f8a7564bfd4ed0c430ac804b83ae73" and "a17730107aee267d7e81fed5f3279b61fcabcb8f" have entirely different histories.

4 changed files with 75 additions and 128 deletions

View file

@ -5,14 +5,8 @@ Adds better NPC integration for the Garry's Mod addon Z-City. While Z-City has a
- NPCs can now be alive while ragdolled, and can move around in that state. - NPCs can now be alive while ragdolled, and can move around in that state.
- NPCs can collapse due to unconsciousness or pain. - NPCs can collapse due to unconsciousness or pain.
- NPCs can get back up after being ragdolled. - NPCs can get back up after being ragdolled.
- Various performance settings are included, with the biggest one being automatic corpse removal.
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 plugin is based off of Kazarei's Euphoria, which in turn is a fork of Fedhoria by Rama.
## 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.
- NPCs currently instantly get up, with no animation. I plan on remedying this in the future.
## Incompatibilities ## Incompatibilities
@ -24,4 +18,4 @@ No guarantees any of this will become real.
- NPCs will back out of combat when injured. - NPCs will back out of combat when injured.
- Friendly NPCs can heal each other. - Friendly NPCs can heal each other.
- NPCs can heal themselves. - NPCs can heal themselves to a limited degree.

View file

@ -44,12 +44,6 @@ local function PopulatePerformanceSBXToolMenu(pnl)
pnl:CheckBox("Never remove corpses near the player", "zcnpci_disable_corpse_removal_near_player") 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:ControlHelp("If on, corpses under the variable above in distance 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: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.")
end end
if engine.ActiveGamemode() == "sandbox" then if engine.ActiveGamemode() == "sandbox" then

View file

@ -6,13 +6,9 @@ local always_active_on_player_kill = CreateConVar("zcnpci_always_active_on_play
local remove_corpses = CreateConVar("zcnpci_enable_corpse_removal", 0, bit.bor(FCVAR_ARCHIVE, FCVAR_REPLICATED)) local remove_corpses = CreateConVar("zcnpci_enable_corpse_removal", 0, bit.bor(FCVAR_ARCHIVE, FCVAR_REPLICATED))
local max_corpses = CreateConVar("zcnpci_max_corpses", 8, 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 max_corpse_time = CreateConVar("zcnpci_max_corpse_time", 30, bit.bor(FCVAR_ARCHIVE, FCVAR_REPLICATED))
local max_corpse_distance = CreateConVar("zcnpci_max_corpse_distance", 2500, bit.bor(FCVAR_ARCHIVE, FCVAR_REPLICATED)) local max_corpse_distance = CreateConVar("zcnpci_max_corpse_distance", 3000, 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 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_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 last_dmgpos = {} local last_dmgpos = {}
local last_hitgroup = {} local last_hitgroup = {}
@ -20,9 +16,6 @@ local last_attacker = {}
local corpses = {} local corpses = {}
local last_corpse_tick = CurTime()
local last_tick = CurTime()
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
@ -90,107 +83,78 @@ hook.Add("ScaleNPCDamage", "zcnpci", function(npc, hitgroup, dmginfo)
end) end)
hook.Add("Think", "zcnpci", function() hook.Add("Think", "zcnpci", function()
local do_corpse_loop = (CurTime() - last_corpse_tick) > 2.0 for i, ent in pairs(corpses) do
local do_general_loop = (CurTime() - last_tick) > 0.1 if !IsValid(ent) then
table.RemoveByValue(corpses, ent)
if do_general_loop then
if do_corpse_loop then
last_corpse_tick = CurTime()
for i, ent in pairs(corpses) do
if !IsValid(ent) then
table.RemoveByValue(corpses, ent)
end
end
if (max_corpses:GetFloat() != -1) and (#corpses > max_corpses:GetFloat()) then
if IsValid(corpses[1]) then
corpses[1]:Remove()
end
table.remove(corpses, 1)
end
end end
end
if (max_corpses:GetFloat() != -1) and (#corpses > max_corpses:GetFloat()) then
if IsValid(corpses[1]) then
corpses[1]:Remove()
end
table.remove(corpses, 1)
end
for i, ent in pairs(ents.GetAll()) do
if !IsValid(ent) then continue end
if !ent.organism then continue end
for i, ent in pairs(ents.GetAll()) do if ent:IsNPC() then
if !IsValid(ent) then continue end -- Knock them down if something is off
if !ent.organism then continue end if (
((ent.organism.lleg >= 1) and (ent.organism.rleg >= 1)) or
if ent:IsNPC() then ent.organism.llegamputated or
-- Knock them down if something is off ent.organism.rlegamputated or
if ( (ent.organism.consciousness <= 0.3) or
((ent.organism.lleg >= 1) and (ent.organism.rleg >= 1)) or (ent.organism.pain > 90)
ent.organism.llegamputated or ) then
ent.organism.rlegamputated or local damage_info = DamageInfo()
(ent.organism.consciousness <= 0.3) or damage_info:SetDamage(ent:Health())
(ent.organism.pain > 90) or damage_info:SetAttacker(ent)
ent:IsOnFire() or damage_info:SetDamageType( DMG_DIRECT )
debug_ragdoll_all:GetBool() damage_info:SetDamageForce(Vector(0, 0, 0))
) 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) ent:TakeDamageInfo(damage_info)
end
elseif !ent.organism.alive and remove_corpses then
-- Add to corpse list if not there already
if !table.HasValue(corpses, ent) then
table.insert(corpses, ent)
end
-- Get nearest player for future checks
local nearby_player
local players = player.GetAll()
for i, loop_player in pairs(players) do
local player_position = loop_player:GetPos()
local distance = ent:GetPos():Distance(player_position)
if (distance <= max_corpse_distance:GetFloat()) or (max_corpse_distance:GetFloat() == -1) then
nearby_player = loop_player
break
end end
elseif do_corpse_loop and remove_corpses and ent.is_npc_corpse then end
local clearable = false
if !ent.organism.alive then clearable = true end if nearby_player and IsValid(nearby_player) and no_corpse_removal_near_player:GetBool() then continue
if treat_crippled_as_dead:GetBool() and ( elseif (max_corpse_distance:GetFloat() != -1) and !nearby_player then
ent.organism.llegamputated or ent.organism.rlegamputated or ent.organism.larmamputated or ent.organism.rarmamputated or ent:Remove()
((ent.organism.lleg >= 1) and (ent.organism.rleg >= 1)) continue
) then clearable = true end end
if treat_near_death_as_dead:GetBool() and (
ent.organism.heartstop or
(ent.organism.brain > 0.6) or
!ent.organism.lungsfunction or
(ent.organism.blood < 3000) or
(ent.organism.pulse < 10)
) then clearable = true end
if !clearable then return end if max_corpse_time:GetFloat() != -1 then
if !ent.corpse_timestamp then ent.corpse_timestamp = CurTime()
-- Add to corpse list if not there already elseif (CurTime() - ent.corpse_timestamp) > max_corpse_time:GetFloat() then
if !table.HasValue(corpses, ent) then
table.insert(corpses, ent)
end
-- Get nearest player for future checks
local nearby_player
local players = player.GetAll()
for i, loop_player in pairs(players) do
local player_position = loop_player:GetPos()
local distance = ent:GetPos():Distance(player_position)
if (distance <= max_corpse_distance:GetFloat()) or (max_corpse_distance:GetFloat() == -1) then
nearby_player = loop_player
break
end
end
if nearby_player and IsValid(nearby_player) and no_corpse_removal_near_player:GetBool() then continue
elseif (max_corpse_distance:GetFloat() != -1) and !nearby_player then
ent:Remove() ent:Remove()
continue continue
end end
print((CurTime() - ent.corpse_timestamp) > max_corpse_time:GetFloat())
if max_corpse_time:GetFloat() != -1 then
if !ent.corpse_timestamp then ent.corpse_timestamp = CurTime()
elseif (CurTime() - ent.corpse_timestamp) > max_corpse_time:GetFloat() then
ent:Remove()
continue
end
print((CurTime() - ent.corpse_timestamp) > max_corpse_time:GetFloat())
end
end end
end end
end end
end) end)
--[[hook.Add("OnNPCKilled", "Fedhoria", function(ent, attacker, inflictor) --[[hook.Add("OnNPCKilled", "Fedhoria", function(ent, attacker, inflictor)

View file

@ -161,8 +161,6 @@ function MODULE:Init()
self.StartDie = nil self.StartDie = nil
self.AnimationRollEndTime = 0 self.AnimationRollEndTime = 0
self.StopProcessing = false self.StopProcessing = false
self.LastThink = CurTime()
self.LastPhysProcess = CurTime()
-- Add damping to all bones. -- Add damping to all bones.
local phys = self:GetPhysicsObject() local phys = self:GetPhysicsObject()
@ -186,15 +184,15 @@ end
---------------------------------------------------------------------------]] ---------------------------------------------------------------------------]]
function MODULE:Think() function MODULE:Think()
if (CurTime() - self.LastThink) < 1 then return end
if !IsValid(target) then return end if !IsValid(target) then return end
self.LastThink = CurTime() print("target is valid")
local phys = target:GetPhysicsObject() local phys = target:GetPhysicsObject()
if !IsValid(phys) or !phys:IsAsleep() then return end if !IsValid(phys) or !phys:IsAsleep() then return end
print("phys is valid")
phys:Wake() phys:Wake()
end end
@ -239,17 +237,13 @@ end
Physics Simulation Hook (FIXED) Physics Simulation Hook (FIXED)
---------------------------------------------------------------------------]] ---------------------------------------------------------------------------]]
function MODULE:PhysicsSimulate(phys, dt) function MODULE:PhysicsSimulate(phys, dt)
if self.StopProcessing then return false end phys:Wake()
--print((CurTime() - self.LastPhysProcess) < 0.05)
--if (CurTime() - self.LastPhysProcess) < (1 /) then return false end
self.LastPhysProcess = CurTime() if self.StopProcessing then return false end
local cur_time = CurTime() local cur_time = CurTime()
local target = self:GetTarget() local target = self:GetTarget()
if not IsValid(target) then self:Remove(); return false end if not IsValid(target) then self:Remove(); return false end
target.is_npc_corpse = true
if !self.StartDie then self.StartDie = cur_time end if !self.StartDie then self.StartDie = cur_time end
@ -287,8 +281,7 @@ function MODULE:PhysicsSimulate(phys, dt)
(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
(target.organism.pain <= 80) and (target.organism.pain <= 80)
!target:IsOnFire()
) then ) then
local ent = ents.Create(target.class_in_previous_life) local ent = ents.Create(target.class_in_previous_life)
ent:SetPos(target:GetPos()) ent:SetPos(target:GetPos())
@ -361,8 +354,10 @@ function MODULE:PhysicsSimulate(phys, dt)
local offset_sqr = (pos - self.last_pos):LengthSqr() local offset_sqr = (pos - self.last_pos):LengthSqr()
self.last_pos = pos self.last_pos = pos
if offset_sqr < (10*10 * dt*dt) then if (offset_sqr < (10*10 * dt*dt) and not (ragmod and ragmod:IsRagmodRagdoll(target))) then
self.StartDie = self.StartDie or cur_time self.StartDie = self.StartDie or cur_time
else
--self.StartDie = nil
end end
-- Physics correction after collision -- Physics correction after collision
@ -375,7 +370,7 @@ function MODULE:PhysicsSimulate(phys, dt)
return true, f return true, f
end end
-- Logic for other bones -- Logic for other bones
local delta_collide = cur_time - self.LastCollideTime local delta_collide = cur_time - self.LastCollideTime
if (delta_collide < 0.2) then if (delta_collide < 0.2) then