mirror of
https://github.com/lifestorm/wnsrc.git
synced 2025-12-17 21:53:46 +03:00
247 lines
8.5 KiB
Lua
247 lines
8.5 KiB
Lua
|
|
--[[
|
||
|
|
| This file was obtained through the combined efforts
|
||
|
|
| of Madbluntz & Plymouth Antiquarian Society.
|
||
|
|
|
|
||
|
|
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||
|
|
| Maloy, DrPepper10 @ RIP, Atle!
|
||
|
|
|
|
||
|
|
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||
|
|
--]]
|
||
|
|
|
||
|
|
AddCSLuaFile("shared.lua")
|
||
|
|
include('shared.lua')
|
||
|
|
|
||
|
|
ENT.Antlion = {
|
||
|
|
"npc_vj_hla_antlion"
|
||
|
|
}
|
||
|
|
|
||
|
|
ENT.previousNodes = {} -- Leave this here, for some reason it doesn't work in the initialize
|
||
|
|
ENT.HordeSpawnRate = 0.19 -- The difference in time between each spawned Horde
|
||
|
|
ENT.SkipOnRemove = false
|
||
|
|
|
||
|
|
function ENT:Initialize()
|
||
|
|
local i = 0
|
||
|
|
for k, v in ipairs(ents.GetAll()) do
|
||
|
|
if v:GetClass() == "sent_vj_antlion_director" then
|
||
|
|
i = i + 1
|
||
|
|
if i > 1 then PrintMessage(HUD_PRINTTALK, "Only one A.I. Director can be present in the map.") self.SkipOnRemove = true self:Remove() return end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
if table.Count(VJ_ANT_NODEPOS) <= 0 then
|
||
|
|
error("No nodegraph detected! Play on a noded map to use the director. Nodes made using the nodegraph editor are undetectable as they're not actually nodes")
|
||
|
|
self:Remove()
|
||
|
|
return
|
||
|
|
end
|
||
|
|
self:SetCollisionGroup(COLLISION_GROUP_NONE)
|
||
|
|
self:SetMoveType(MOVETYPE_NONE)
|
||
|
|
self:SetSolid(SOLID_NONE)
|
||
|
|
self:SetPos(Vector(0,0,0))
|
||
|
|
self:SetNoDraw(true)
|
||
|
|
self:DrawShadow(false)
|
||
|
|
|
||
|
|
self.IsActivated = tobool(GetConVarNumber("vj_antlion_director_enabled"))
|
||
|
|
self.Antlion_SpawnDistance = GetConVarNumber("vj_antlion_director_spawnmax")
|
||
|
|
self.Antlion_SpawnDistanceClose = GetConVarNumber("vj_antlion_director_spawnmin")
|
||
|
|
self.Antlion_HordeChance = GetConVarNumber("vj_antlion_director_hordechance")
|
||
|
|
self.Antlion_HordeCooldownMin = GetConVarNumber("vj_antlion_director_hordecooldownmin")
|
||
|
|
self.Antlion_HordeCooldownMax = GetConVarNumber("vj_antlion_director_hordecooldownmax")
|
||
|
|
self.Antlion_MaxAntlions = GetConVarNumber("vj_antlion_director_max")
|
||
|
|
self.Antlion_MaxHordeSpawn = GetConVarNumber("vj_antlion_director_hordecount")
|
||
|
|
self.tbl_SpawnedNPCs = {}
|
||
|
|
self.tbl_NPCsWithEnemies = {}
|
||
|
|
self.NextAICheckTime = CurTime() +5
|
||
|
|
self.NextAntlionSpawnTime = CurTime() +1
|
||
|
|
self.NextHordeSpawnTime = CurTime() +math.Rand(self.Antlion_HordeCooldownMin,self.Antlion_HordeCooldownMax)
|
||
|
|
end
|
||
|
|
|
||
|
|
function ENT:OnRemove()
|
||
|
|
if self.SkipOnRemove == true then return end
|
||
|
|
for index,object in ipairs(self.tbl_SpawnedNPCs) do
|
||
|
|
if IsValid(object) then
|
||
|
|
object:Remove()
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
function ENT:Think()
|
||
|
|
self.IsActivated = tobool(GetConVarNumber("vj_antlion_director_enabled"))
|
||
|
|
if self.IsActivated then
|
||
|
|
self:ManageVars()
|
||
|
|
self:ManageAI()
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
function ENT:ManageVars()
|
||
|
|
self.Antlion_SpawnDistance = GetConVarNumber("vj_antlion_director_spawnmax")
|
||
|
|
self.Antlion_SpawnDistanceClose = GetConVarNumber("vj_antlion_director_spawnmin")
|
||
|
|
self.Antlion_HordeChance = GetConVarNumber("vj_antlion_director_hordechance")
|
||
|
|
self.Antlion_HordeCooldownMin = GetConVarNumber("vj_antlion_director_hordecooldownmin")
|
||
|
|
self.Antlion_HordeCooldownMax = GetConVarNumber("vj_antlion_director_hordecooldownmax")
|
||
|
|
self.Antlion_MaxAntlions = GetConVarNumber("vj_antlion_director_max")
|
||
|
|
self.Antlion_MaxHordeSpawn = GetConVarNumber("vj_antlion_director_hordecount")
|
||
|
|
self.AI_RefreshTime = GetConVarNumber("vj_l4d_director_refreshrate")
|
||
|
|
end
|
||
|
|
|
||
|
|
function ENT:CheckSurvivorDistance(ent)
|
||
|
|
local survivors = self:FindSurvivors(ent:GetPos())
|
||
|
|
local farthestSurvivor = survivors[table.Count(survivors)]
|
||
|
|
if ent:GetPos():Distance(farthestSurvivor:GetPos()) >= GetConVarNumber("vj_antlion_director_spawnmax") +750 && !ent:Visible(farthestSurvivor) then
|
||
|
|
ent:Remove()
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
function ENT:ManageAI()
|
||
|
|
-- Checks for inactive AI
|
||
|
|
if CurTime() > self.NextAICheckTime then
|
||
|
|
if #self.tbl_SpawnedNPCs <= 0 then return end
|
||
|
|
for _,v in ipairs(self.tbl_SpawnedNPCs) do
|
||
|
|
if IsValid(v) then
|
||
|
|
self:CheckSurvivorDistance(v)
|
||
|
|
if IsValid(v:GetEnemy()) && !table.HasValue(self.tbl_NPCsWithEnemies,v) then
|
||
|
|
table.insert(self.tbl_NPCsWithEnemies,v)
|
||
|
|
elseif !IsValid(v:GetEnemy()) then
|
||
|
|
if table.HasValue(self.tbl_NPCsWithEnemies,v) then
|
||
|
|
table.remove(self.tbl_NPCsWithEnemies,self.tbl_NPCsWithEnemies[v])
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
table.Empty(self.previousNodes) -- Reset the previously spawned-on nodes to refresh spawn positions
|
||
|
|
self.previousNodes = {}
|
||
|
|
self.NextAICheckTime = CurTime() +5
|
||
|
|
end
|
||
|
|
|
||
|
|
-- Spawns AI
|
||
|
|
if CurTime() > self.NextAntlionSpawnTime then
|
||
|
|
if table.Count(self.tbl_SpawnedNPCs) >= self.Antlion_MaxAntlions -self.Antlion_MaxHordeSpawn then return end -- Makes sure that we can at least spawn a Horde when it's time
|
||
|
|
self:SpawnAntlion(VJ_PICKRANDOMTABLE(self.Antlion),self:FindClearArea(false))
|
||
|
|
self.NextAntlionSpawnTime = CurTime() + math.Rand(GetConVarNumber("vj_antlion_director_delaymin"),GetConVarNumber("vj_antlion_director_delaymax"))
|
||
|
|
end
|
||
|
|
|
||
|
|
-- Spawns Hordes
|
||
|
|
if CurTime() > self.NextHordeSpawnTime && math.random(1,self.Antlion_HordeChance) == 1 then
|
||
|
|
for i = 1,self.Antlion_MaxHordeSpawn do
|
||
|
|
timer.Simple(self.HordeSpawnRate *i,function() -- Help with lag when spawning
|
||
|
|
if IsValid(self) then
|
||
|
|
self:SpawnAntlion(VJ_PICKRANDOMTABLE(self.Antlion),self:FindClearArea(true),true)
|
||
|
|
end
|
||
|
|
end)
|
||
|
|
end
|
||
|
|
self.NextHordeSpawnTime = CurTime() + math.Rand(self.Antlion_HordeCooldownMin,self.Antlion_HordeCooldownMax)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
function ENT:CheckNodeVisibility(pos,survivor)
|
||
|
|
local nodeCheck = ents.Create("prop_vj_animatable")
|
||
|
|
nodeCheck:SetModel("models/cpthazama/l4d1/common/common_patient_male01.mdl")
|
||
|
|
nodeCheck:SetPos(pos)
|
||
|
|
nodeCheck:SetCollisionGroup(COLLISION_GROUP_IN_VEHICLE)
|
||
|
|
nodeCheck:Spawn()
|
||
|
|
nodeCheck:Activate()
|
||
|
|
nodeCheck:SetNoDraw(true)
|
||
|
|
nodeCheck:DrawShadow(false)
|
||
|
|
timer.Simple(0.02,function()
|
||
|
|
if IsValid(nodeCheck) then
|
||
|
|
nodeCheck:Remove()
|
||
|
|
end
|
||
|
|
end)
|
||
|
|
return nodeCheck:Visible(survivor)
|
||
|
|
end
|
||
|
|
|
||
|
|
function ENT:ConfigureNodeTable(tbl)
|
||
|
|
local newTable = {}
|
||
|
|
local ply = self:PickRandomSurvivor()
|
||
|
|
for _,v in pairs(tbl) do
|
||
|
|
newTable[v] = v:Distance(ply:GetPos())
|
||
|
|
end
|
||
|
|
return table.SortByKey(newTable,true)
|
||
|
|
end
|
||
|
|
|
||
|
|
function ENT:FindClearArea(getClosest)
|
||
|
|
if table.Count(self.tbl_SpawnedNPCs) >= self.Antlion_MaxAntlions then return end
|
||
|
|
if getClosest then
|
||
|
|
if table.Count(VJ_ANT_NODEPOS) > 0 then
|
||
|
|
for _,i in ipairs(self:ConfigureNodeTable(VJ_ANT_NODEPOS)) do
|
||
|
|
local nodePos = i
|
||
|
|
local survivor = self:FindSurvivors(nodePos)[1]
|
||
|
|
if nodePos:Distance(survivor:GetPos()) <= self.Antlion_SpawnDistance && nodePos:Distance(survivor:GetPos()) > self.Antlion_SpawnDistanceClose then
|
||
|
|
if !self:CheckNodeVisibility(nodePos,survivor) then
|
||
|
|
table.insert(self.previousNodes,nodePos)
|
||
|
|
return nodePos
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
else
|
||
|
|
if table.Count(VJ_ANT_NODEPOS) > 0 then
|
||
|
|
for _,i in ipairs(self:ConfigureNodeTable(VJ_ANT_NODEPOS)) do
|
||
|
|
local nodePos = i
|
||
|
|
local survivor = self:FindSurvivors(nodePos)[1]
|
||
|
|
if self.previousNodes[nodePos] then return end
|
||
|
|
if nodePos:Distance(survivor:GetPos()) <= self.Antlion_SpawnDistance && nodePos:Distance(survivor:GetPos()) > self.Antlion_SpawnDistanceClose then
|
||
|
|
-- if !survivor:VisibleVec(nodePos) then
|
||
|
|
if !self:CheckNodeVisibility(nodePos,survivor) then
|
||
|
|
if table.HasValue(self.previousNodes,nodePos) then return end
|
||
|
|
table.insert(self.previousNodes,nodePos)
|
||
|
|
return nodePos
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
function ENT:PickRandomSurvivor()
|
||
|
|
local tbl = {}
|
||
|
|
for _,v in ipairs(player.GetAll()) do
|
||
|
|
table.insert(tbl,v)
|
||
|
|
end
|
||
|
|
return VJ_PICKRANDOMTABLE(tbl)
|
||
|
|
end
|
||
|
|
|
||
|
|
function ENT:FindSurvivors(vec)
|
||
|
|
local tb = {}
|
||
|
|
if vec then
|
||
|
|
for _,v in pairs(player.GetAll()) do
|
||
|
|
tb[v] = v:GetPos():Distance(vec)
|
||
|
|
end
|
||
|
|
return table.SortByKey(tb,true)
|
||
|
|
else
|
||
|
|
for _,v in pairs(player.GetAll()) do
|
||
|
|
table.insert(tb,v)
|
||
|
|
end
|
||
|
|
return tb
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
function ENT:SpawnAntlion(ent,pos,isHorde)
|
||
|
|
if pos == nil then /*MsgN("AI Director: Could not spawn Antlion, no position specified!")*/ return end
|
||
|
|
if table.Count(self.tbl_SpawnedNPCs) >= self.Antlion_MaxAntlions then return end
|
||
|
|
local Antlion = ents.Create(ent)
|
||
|
|
Antlion:SetPos(pos)
|
||
|
|
Antlion:Spawn()
|
||
|
|
Antlion:Activate()
|
||
|
|
table.insert(self.tbl_SpawnedNPCs,Antlion)
|
||
|
|
Antlion:VJ_ACT_PLAYACTIVITY("UnBurrow01",true,false,false)
|
||
|
|
if isHorde then
|
||
|
|
Antlion.FindEnemy_UseSphere = true
|
||
|
|
Antlion.FindEnemy_CanSeeThroughWalls = true
|
||
|
|
Antlion:DrawShadow(false)
|
||
|
|
timer.Simple(0.02,function()
|
||
|
|
if IsValid(Antlion) then
|
||
|
|
Antlion:DrawShadow(false)
|
||
|
|
end
|
||
|
|
end)
|
||
|
|
end
|
||
|
|
Antlion.AI_Director = self
|
||
|
|
function Antlion:CustomOnRemove()
|
||
|
|
if IsValid(self.AI_Director) then
|
||
|
|
table.remove(self.AI_Director.tbl_SpawnedNPCs,self.AI_Director.tbl_SpawnedNPCs[self])
|
||
|
|
if table.HasValue(self.AI_Director.tbl_NPCsWithEnemies,self) then table.remove(self.AI_Director.tbl_NPCsWithEnemies,self.AI_Director.tbl_NPCsWithEnemies[self]) end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
Antlion.EntitiesToNoCollide = {
|
||
|
|
"npc_vj_hla_antlion"
|
||
|
|
}
|
||
|
|
end
|