diff --git a/lua/autorun/client/zcnpci_menu.lua b/lua/autorun/client/zcnpci_menu.lua index bea33e1..32e904e 100644 --- a/lua/autorun/client/zcnpci_menu.lua +++ b/lua/autorun/client/zcnpci_menu.lua @@ -1,3 +1,16 @@ +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") + + pnl:CheckBox("NPCs shouldn't target unconscious players", "zcnpci_no_unconscious_targeting_players") + pnl:ControlHelp("This may or may not work.") +end + local function PopulateCustomNPCSBXToolMenu(pnl) local modded_npc_whitelist = CreateConVar("zcnpci_modded_npc_whitelist", "NPC-classes-here!", bit.bor(FCVAR_ARCHIVE, FCVAR_REPLICATED)) @@ -25,7 +38,7 @@ local function PopulateCustomNPCSBXToolMenu(pnl) 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.") + pnl:Help("A list of NPC classes, seperated by spaces or line breaks. Can only be set by a superadmin or server operator.") end local function PopulateRagdollSBXToolMenu(pnl) @@ -125,6 +138,11 @@ if engine.ActiveGamemode() == "sandbox" then PopulateRagdollSBXToolMenu(pnl) end) + spawnmenu.AddToolMenuOption("Utilities", "zcnpci", "AISettings", "AI", "", "", function(pnl) + pnl:ClearControls() + PopulateAISBXToolMenu(pnl) + end) + spawnmenu.AddToolMenuOption("Utilities", "zcnpci", "MainSettings", "Main", "", "", function(pnl) pnl:ClearControls() PopulateMainSBXToolMenu(pnl) diff --git a/lua/entities/npc_ragdoll_target.lua b/lua/entities/npc_ragdoll_target.lua index 5a69122..a0cd7d8 100644 --- a/lua/entities/npc_ragdoll_target.lua +++ b/lua/entities/npc_ragdoll_target.lua @@ -4,7 +4,12 @@ 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 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() then return end @@ -28,15 +33,23 @@ local function RemoveRelationship(ent, me) end end +function ENT:Classify() + return self.dummy_entity:Classify() +end + function ENT:Initialize() if SERVER then self:SetModel("models/maxofs2d/hover_basic.mdl") - self:SetCollisionGroup(COLLISION_GROUP_DEBRIS) + 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 @@ -52,18 +65,23 @@ function ENT:Think() local ragdoll = self.ragdoll_to_follow if !IsValid(ragdoll) then return end - self:SetPos(ragdoll:GetPos()) + 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 - local should_stop_targeting = ( + local should_stop_targeting = !zcnpci_no_unconscious_targeting:GetBool() or ( (ragdoll.organism.consciousness <= 0.4) or (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 diff --git a/lua/zcnpci/modules/falling_legs.lua b/lua/zcnpci/modules/falling_legs.lua index 03e9ade..6a5ed53 100644 --- a/lua/zcnpci/modules/falling_legs.lua +++ b/lua/zcnpci/modules/falling_legs.lua @@ -100,6 +100,9 @@ local minimum_down_time = CreateConVar("zcnpci_down_time", "5", {FCVAR_ARCHIVE, 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") +-- Targeting +local npc_targeting_enabled = CreateConVar("zcnpci_npc_targeting_enabled", "1", {FCVAR_ARCHIVE, FCVAR_REPLICATED}, "Should NPCs target downed NPCs") + --[[------------------------------------------------------------------------- Tug function Applies a random impulse to one of the leg bones.