Compare commits
No commits in common. "02557dacd1b94f351211b7428fa4f0c6463c5ea5" and "4c4d59838a79a27b59787cc70a7fdf31d2a749c9" have entirely different histories.
02557dacd1
...
4c4d59838a
8 changed files with 101 additions and 340 deletions
14
README.md
14
README.md
|
|
@ -1,16 +1,14 @@
|
|||
# Z-City NPC Integration
|
||||
|
||||
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 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.
|
||||
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 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.
|
||||
- NPCs are responsive to damage from many sources -- bullets, fire, melee (especially kicks!) and more.
|
||||
- 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.
|
||||
- Various configurable settings are included, allowing you to tweak the mod to your liking.
|
||||
|
||||
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.
|
||||
|
||||
|
|
@ -36,16 +34,16 @@ It is, however, not compatbile with:
|
|||
|
||||
MUST BE DONE BEFORE RELEASE:
|
||||
|
||||
- Spinal injuries
|
||||
- NPCs should not unfake when moved around
|
||||
- Fire / falling / pain reaction (more rapid flailing)
|
||||
- NPCs should not unfake if being grabbed
|
||||
- Fix combine targeting players when ai_ignoreplayers = 1. This is caused by an npc_bullseye that is always on the player. I don't know why it's always there but I don't like it. Will probably override it with my own npc_ragdoll_target entity.
|
||||
|
||||
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?
|
||||
- Stop NPCs from dropping their guns (that's not a good thing to do)
|
||||
|
||||
Post release ideas:
|
||||
|
||||
|
|
|
|||
|
|
@ -53,12 +53,6 @@ local function PopulateRagdollSBXToolMenu(pnl)
|
|||
pnl:NumSlider("Wound grab time", "zcnpci_woundgrab_time", 0, 10, 3)
|
||||
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. 1 is default.")
|
||||
|
||||
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.")
|
||||
|
||||
|
|
@ -67,9 +61,6 @@ local function PopulateRagdollSBXToolMenu(pnl)
|
|||
|
||||
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: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
|
||||
|
||||
local function PopulatePerformanceSBXToolMenu(pnl)
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ 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())
|
||||
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)
|
||||
|
|
@ -84,7 +84,7 @@ function ENT:Think()
|
|||
(ragdoll.organism.critical)
|
||||
)
|
||||
|
||||
if (should_stop_targeting != self.last_stop_targeting) then
|
||||
if true or (should_stop_targeting != self.last_stop_targeting) then
|
||||
self.last_stop_targeting = should_stop_targeting
|
||||
|
||||
if should_stop_targeting then
|
||||
|
|
|
|||
|
|
@ -4,6 +4,8 @@ ENT.Type = "anim"
|
|||
ENT.Base = "base_anim"
|
||||
ENT.AutomaticFrameAdvance = true
|
||||
|
||||
|
||||
|
||||
local bones_to_animate = {
|
||||
"ValveBiped.Bip01_Pelvis",
|
||||
"ValveBiped.Bip01_Spine2",
|
||||
|
|
@ -22,15 +24,6 @@ local bones_to_animate = {
|
|||
"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")
|
||||
|
|
@ -39,93 +32,116 @@ function ENT:Initialize()
|
|||
|
||||
if CLIENT then
|
||||
print("I AM CLIENTSIDE")
|
||||
self:SetNoDraw(true)
|
||||
timer.Simple(0.2, function()
|
||||
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
function ENT:Think()
|
||||
function ENT:Draw()
|
||||
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_unfake then
|
||||
self:SetModel(old_ragdoll:GetModel())
|
||||
|
||||
if !self.ready_to_animate then
|
||||
self.ragdoll = ClientsideRagdoll(old_ragdoll:GetModel())
|
||||
self.bone_list = {}
|
||||
self.bone_list_end = {}
|
||||
|
||||
self:SetupBones()
|
||||
|
||||
ragdoll = self.ragdoll
|
||||
local i = 0
|
||||
while i < self:GetBoneCount() do
|
||||
local name = self:GetBoneName(i)
|
||||
if name == "__INVALIDBONE__" then
|
||||
i = i + 1
|
||||
continue
|
||||
end
|
||||
|
||||
self.ragdoll:SetNoDraw(false)
|
||||
self.ragdoll:DrawShadow(true)
|
||||
table.insert(bones_to_animate, name)
|
||||
|
||||
SetRagdollPos(self.ragdoll, old_ragdoll:GetPos())
|
||||
|
||||
local matrix = Matrix()
|
||||
|
||||
local position, angle = old_ragdoll:GetBonePosition(i)
|
||||
if position == old_ragdoll:GetPos() then
|
||||
get_matrix = old_ragdoll:GetBoneMatrix(i)
|
||||
if get_matrix then
|
||||
position = get_matrix:GetTranslation()
|
||||
end
|
||||
end
|
||||
matrix:Translate(position)
|
||||
matrix:Rotate(angle)
|
||||
|
||||
print(self.ragdoll)
|
||||
print("SHOW UP YOU PIECE OF SHIT")
|
||||
self.bone_list[name] = matrix
|
||||
|
||||
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)
|
||||
local matrix = Matrix()
|
||||
|
||||
if parent_object == nil then continue end
|
||||
local position, angle = old_npc:GetBonePosition(i)
|
||||
if position == old_npc:GetPos() then
|
||||
get_matrix = old_npc:GetBoneMatrix(i)
|
||||
if get_matrix then
|
||||
position = get_matrix:GetTranslation()
|
||||
end
|
||||
end
|
||||
matrix:Translate(position)
|
||||
matrix:Rotate(angle)
|
||||
|
||||
|
||||
object:SetPos(parent_object:GetTranslation())
|
||||
object:SetAngles(parent_object:GetAngles())
|
||||
self.bone_list_end[name] = matrix
|
||||
|
||||
i = i + 1
|
||||
end
|
||||
|
||||
self.ready_to_animate = true
|
||||
PrintTable(bones_to_animate)
|
||||
|
||||
--[[for i,name in pairs(bones_to_animate) do
|
||||
local bone_index = old_ragdoll:LookupBone(name)
|
||||
if bone_index == nil then continue end
|
||||
|
||||
self.bone_list[name] = Matrix(old_ragdoll:GetBoneMatrix(bone_index))
|
||||
end]]
|
||||
|
||||
print("BONES:"..self:GetBoneCount())
|
||||
|
||||
self.ready_to_unfake = 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)
|
||||
self:SetPos(old_ragdoll:GetPos())
|
||||
|
||||
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)
|
||||
self:SetupBones()
|
||||
|
||||
if parent_bone == -1 then continue end
|
||||
for name,matrix in pairs(self.bone_list) do
|
||||
local bone_index = self:LookupBone(name)
|
||||
if bone_index == -1 then
|
||||
print(name.." does not exist, skipping...")
|
||||
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
|
||||
--print(name)
|
||||
--local bone_matrix = old_ragdoll:GetBoneMatrix(bone_index)
|
||||
|
||||
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,
|
||||
}
|
||||
if matrix:GetTranslation():DistToSqr(self:GetPos()) == 1 then continue end
|
||||
|
||||
-- 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
|
||||
self:SetBoneMatrix(bone_index, matrix)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
self:NextThink(CurTime() + 0.1)
|
||||
self:DrawModel()
|
||||
end
|
||||
|
||||
function ENT:Think()
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
function ENT:OnRemove()
|
||||
if CLIENT then
|
||||
if self.ragdoll then
|
||||
self.ragdoll:Remove()
|
||||
end
|
||||
--[[if self.anim_ragdoll then
|
||||
self.anim_ragdoll:Remove()
|
||||
end]]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -1,191 +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",
|
||||
}
|
||||
|
||||
local function GetBoneLocalOffsetMatrix(ent, bone_index)
|
||||
local parent = ent:GetBoneParent(bone_index)
|
||||
local parent_matrix = Matrix(ent:GetBoneMatrix(parent))
|
||||
parent_matrix:Invert()
|
||||
parent_matrix:Mul(ent:GetBoneMatrix(bone_index))
|
||||
|
||||
return parent_matrix
|
||||
end
|
||||
|
||||
local function GetGlobalMatrixFromLocal(ent, bone_index, local_matrix)
|
||||
local parent = ent:GetBoneParent(bone_index)
|
||||
local parent_matrix = Matrix(ent:GetBoneMatrix(parent))
|
||||
parent_matrix:Mul(local_matrix)
|
||||
|
||||
return parent_matrix
|
||||
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")
|
||||
timer.Simple(0.2, function()
|
||||
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
function ENT:Draw()
|
||||
if CLIENT then
|
||||
local old_ragdoll = self:GetNWEntity("parent")
|
||||
local old_npc = self:GetNWEntity("parent_npc")
|
||||
if IsValid(old_ragdoll) and IsValid(old_npc) then
|
||||
if !self.ready_to_unfake then
|
||||
self:SetModel(old_ragdoll:GetModel())
|
||||
|
||||
self.bone_list = {}
|
||||
self.bone_list_end = {}
|
||||
self.bone_list_end_offset = {}
|
||||
|
||||
self:SetupBones()
|
||||
|
||||
local i = 0
|
||||
while i < self:GetBoneCount() do
|
||||
local name = self:GetBoneName(i)
|
||||
if name == "__INVALIDBONE__" then
|
||||
i = i + 1
|
||||
continue
|
||||
end
|
||||
|
||||
table.insert(bones_to_animate, name)
|
||||
|
||||
local matrix = Matrix()
|
||||
|
||||
local position, angle = old_ragdoll:GetBonePosition(i)
|
||||
if position == old_ragdoll:GetPos() then
|
||||
get_matrix = old_ragdoll:GetBoneMatrix(i)
|
||||
if get_matrix then
|
||||
position = get_matrix:GetTranslation()
|
||||
end
|
||||
end
|
||||
matrix:Translate(position)
|
||||
matrix:Rotate(angle)
|
||||
|
||||
self.bone_list[name] = matrix
|
||||
|
||||
local matrix = Matrix()
|
||||
|
||||
local npc_bone_index = old_npc:LookupBone(name)
|
||||
|
||||
local position, angle = old_npc:GetBonePosition(npc_bone_index)
|
||||
if position == old_npc:GetPos() then
|
||||
print("DINGUS!!!! "..name)
|
||||
self.bone_list_end_offset[name] = GetBoneLocalOffsetMatrix(self, npc_bone_index)
|
||||
|
||||
get_matrix = old_npc:GetBoneMatrix(npc_bone_index)
|
||||
if get_matrix then
|
||||
position = get_matrix:GetTranslation()
|
||||
end
|
||||
else
|
||||
matrix:Translate(position)
|
||||
matrix:Rotate(angle)
|
||||
|
||||
|
||||
self.bone_list_end[name] = matrix
|
||||
end
|
||||
|
||||
|
||||
|
||||
i = i + 1
|
||||
end
|
||||
|
||||
PrintTable(bones_to_animate)
|
||||
|
||||
--[[for i,name in pairs(bones_to_animate) do
|
||||
local bone_index = old_ragdoll:LookupBone(name)
|
||||
if bone_index == nil then continue end
|
||||
|
||||
self.bone_list[name] = Matrix(old_ragdoll:GetBoneMatrix(bone_index))
|
||||
end]]
|
||||
|
||||
print("BONES:"..self:GetBoneCount())
|
||||
|
||||
self.ready_to_unfake = true
|
||||
end
|
||||
|
||||
self:SetPos(old_ragdoll:GetPos())
|
||||
|
||||
self:SetupBones()
|
||||
|
||||
local fake_start = self:GetNWFloat("fake_start")
|
||||
local fake_end = self:GetNWFloat("fake_end")
|
||||
|
||||
local progress = (CurTime() - fake_start) / (fake_end - fake_start)
|
||||
|
||||
for name,matrix in pairs(self.bone_list) do
|
||||
local bone_index = self:LookupBone(name)
|
||||
if bone_index == -1 then
|
||||
print(name.." does not exist, skipping...")
|
||||
continue
|
||||
end
|
||||
|
||||
local matrix_end = self.bone_list_end[name]
|
||||
|
||||
if matrix_end == nil then
|
||||
if (self.bone_list_end_offset[name] == nil) then
|
||||
print("OH FUCKKKKKKKK! No matrix end for "..name)
|
||||
continue
|
||||
end
|
||||
|
||||
matrix_end = GetGlobalMatrixFromLocal(self, bone_index, self.bone_list_end_offset[name])
|
||||
end
|
||||
|
||||
local final_matrix = Matrix()
|
||||
final_matrix:Translate(LerpVector(progress, matrix:GetTranslation(), matrix_end:GetTranslation()))
|
||||
final_matrix:Rotate(LerpAngle(progress, matrix:GetAngles(), matrix_end:GetAngles()))
|
||||
|
||||
--print(name)
|
||||
--local bone_matrix = old_ragdoll:GetBoneMatrix(bone_index)
|
||||
|
||||
if matrix:GetTranslation():DistToSqr(self:GetPos()) == 1 then continue end
|
||||
|
||||
self:SetBoneMatrix(bone_index, final_matrix)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
self:DrawModel()
|
||||
end
|
||||
|
||||
function ENT:Think()
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
function ENT:OnRemove()
|
||||
if CLIENT then
|
||||
--[[if self.anim_ragdoll then
|
||||
self.anim_ragdoll:Remove()
|
||||
end]]
|
||||
end
|
||||
end
|
||||
|
|
@ -57,45 +57,16 @@ local corpses = {}
|
|||
local last_corpse_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
|
||||
-- Don't particularly like doing this but you gotta do what you gotta do
|
||||
local homigrad_damage_hook = hook.GetTable().EntityTakeDamage["homigrad-damage"]
|
||||
|
||||
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)
|
||||
if !ent.organism then return end
|
||||
|
||||
|
|
@ -173,13 +144,6 @@ 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.
|
||||
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()
|
||||
if !IsValid(ragdoll) then return end
|
||||
|
||||
|
|
@ -212,13 +176,17 @@ hook.Add("HomigradDamage", "zcnpci", function(ent, dmginfo)
|
|||
if dmginfo:IsDamageType(DMG_BULLET + DMG_BUCKSHOT + DMG_BLAST) then
|
||||
if dmginfo:GetDamage() > 3 then
|
||||
table.insert(npcs_to_fake, ent)
|
||||
|
||||
--local attacker_angle = dmginfo:GetAttacker():EyeAngles()
|
||||
|
||||
--local normal = attacker_angle:Forward(normal)
|
||||
|
||||
--ent.npcfakeknockback = normal * 7 * 3
|
||||
end
|
||||
elseif dmginfo:IsDamageType(DMG_CLUB + DMG_SLASH) then
|
||||
local attacker = dmginfo:GetAttacker()
|
||||
if !IsValid(attacker) then return end
|
||||
|
||||
if attacker.HasWeapon == nil then return end
|
||||
|
||||
local fists = attacker:HasWeapon("weapon_hands_sh")
|
||||
if !fists then fists = attacker:HasWeapon("weapon_hg_coolhands") end
|
||||
|
||||
|
|
|
|||
|
|
@ -99,8 +99,6 @@ local minimum_down_time = CreateConVar("zcnpci_down_time", "5", {FCVAR_ARCHIVE,
|
|||
-- 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")
|
||||
|
|
@ -201,7 +199,6 @@ function MODULE:Init()
|
|||
self.LastThink = CurTime()
|
||||
self.LastFakeUpCheck = CurTime()
|
||||
self.LastPosCheck = target:GetPos()
|
||||
self.SpawnTimestamp = CurTime()
|
||||
|
||||
self.bullseye = ents.Create("npc_ragdoll_target")
|
||||
self.bullseye:SetPos(target:GetPos())
|
||||
|
|
@ -397,26 +394,16 @@ function MODULE:PhysicsSimulate(phys, dt)
|
|||
return false -- Cut the bullshit
|
||||
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 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.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
|
||||
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
|
||||
|
||||
if (self.LastPosCheck:DistToSqr(target:GetPos()) > (movement_sensitivity:GetFloat() ^ 2)) then
|
||||
if (self.LastPosCheck:DistToSqr(target:GetPos()) > (32 ^ 2)) then
|
||||
self.LastPosCheck = target:GetPos()
|
||||
target.StartDie = cur_time
|
||||
end
|
||||
|
|
@ -452,9 +439,6 @@ function MODULE:PhysicsSimulate(phys, dt)
|
|||
|
||||
ent:SetSkin(target:GetSkin())
|
||||
|
||||
ent:AddFlags(target.respawn_data.flags)
|
||||
ent:AddSpawnFlags(target.respawn_data.spawn_flags)
|
||||
|
||||
if target.citizentype then
|
||||
ent:SetKeyValue("citizentype", target.citizentype)
|
||||
end
|
||||
|
|
@ -510,8 +494,7 @@ function MODULE:PhysicsSimulate(phys, dt)
|
|||
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)
|
||||
|
|
|
|||
|
|
@ -208,11 +208,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
|
||||
self:Remove()
|
||||
return false -- Cut the bullshit
|
||||
elseif (
|
||||
(target.organism.consciousness <= 0.4) or
|
||||
(target.organism.spine2 == 1) or
|
||||
(target.organism.spine3 == 1)
|
||||
) then
|
||||
elseif (target.organism.consciousness <= 0.3) then
|
||||
return false
|
||||
end
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue