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 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.
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.
## 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.
This plugin is based off of Kazarei's Euphoria, which in turn is a fork of Fedhoria by Rama.
## Incompatibilities
@ -24,4 +18,4 @@ No guarantees any of this will become real.
- NPCs will back out of combat when injured.
- 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: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
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 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_distance = CreateConVar("zcnpci_max_corpse_distance", 2500, 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", 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 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_hitgroup = {}
@ -20,9 +16,6 @@ local last_attacker = {}
local corpses = {}
local last_corpse_tick = CurTime()
local last_tick = CurTime()
hook.Add("CreateEntityRagdoll", "zcnpci", function(ent, ragdoll)
if !ent.organism then return end
@ -90,107 +83,78 @@ hook.Add("ScaleNPCDamage", "zcnpci", function(npc, hitgroup, dmginfo)
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
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
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
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 !IsValid(ent) then continue end
if !ent.organism then continue end
if ent:IsNPC() then
-- Knock them down if something is off
if (
((ent.organism.lleg >= 1) and (ent.organism.rleg >= 1)) or
ent.organism.llegamputated or
ent.organism.rlegamputated or
(ent.organism.consciousness <= 0.3) or
(ent.organism.pain > 90) or
ent:IsOnFire() 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))
if ent:IsNPC() then
-- Knock them down if something is off
if (
((ent.organism.lleg >= 1) and (ent.organism.rleg >= 1)) or
ent.organism.llegamputated or
ent.organism.rlegamputated or
(ent.organism.consciousness <= 0.3) or
(ent.organism.pain > 90)
) 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
elseif do_corpse_loop and remove_corpses and ent.is_npc_corpse then
local clearable = false
end
if !ent.organism.alive then clearable = true end
if treat_crippled_as_dead:GetBool() and (
ent.organism.llegamputated or ent.organism.rlegamputated or ent.organism.larmamputated or ent.organism.rarmamputated or
((ent.organism.lleg >= 1) and (ent.organism.rleg >= 1))
) then clearable = true 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 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()
continue
end
if !clearable then return end
-- 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
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
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
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
print((CurTime() - ent.corpse_timestamp) > max_corpse_time:GetFloat())
end
end
end
end)
--[[hook.Add("OnNPCKilled", "Fedhoria", function(ent, attacker, inflictor)

View file

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