From f1e99d19a5aa6e5fa61518366235e3da09689d0b Mon Sep 17 00:00:00 2001 From: Alexander Pickering Date: Sat, 2 Jan 2016 22:10:06 -0500 Subject: Ai overhual now working --- gamemode/npcsystem/npcs/zombie.lua | 203 ++++++++++++++++++++++++++----------- 1 file changed, 142 insertions(+), 61 deletions(-) (limited to 'gamemode/npcsystem/npcs/zombie.lua') diff --git a/gamemode/npcsystem/npcs/zombie.lua b/gamemode/npcsystem/npcs/zombie.lua index d9232a7..7c598fd 100644 --- a/gamemode/npcsystem/npcs/zombie.lua +++ b/gamemode/npcsystem/npcs/zombie.lua @@ -1,13 +1,21 @@ -NPC.Name = "Bird" -NPC.Desc = "A flappy little guy" +NPC.Name = "Zombie" +NPC.Desc = "A fearsome monster!" NPC.Class = "Ambient" --Ambient, Agressive, Boss -NPC.Model = "models/pigeon.mdl" +NPC.Model = "models/Zombie/Classic.mdl" NPC.Icon = Material("wintersurvival2/hud/ws1_icons/icon_rock") -NPC.Social = "Pack" --Solo, Pack +NPC.Social = "Solo" --Solo, Pack + +NPC.Stats = { + ["Vitality"] = 100, + ["Speed"] = 50, + ["AwareDist"] = 1000, + ["Accel"] = 100, + ["Decel"] = 200, + ["Step"] = 20, --Step height + ["Hull"] = HULL_HUMAN +} -NPC.Vitality = 10 -NPC.Speed = 100 --Drops should be formated as [index]={["item name"], percent_drop} where percent_drop is a number from 0 to 100 NPC.Drops = { [0] = {"Meat",100},--Birds will drop at least 1 meat, and have a 50% chance of dropping 2 @@ -15,8 +23,70 @@ NPC.Drops = { [2] = {"Feather",50}, } ---Attacks should be formated as [i]={function (return bool) canattack(ply) = function doattack(ply)} -NPC.Attacks = nil +--Some npc's like birds have diffent names for their idle sequences +NPC.IdleSequences = { + [0] = "Idle", +} + +--Distance to be made aware of players + +--Attacks should be formated as [i]={function (return int dammage) canattack(ply) = function doattack(ply)} +--NPC will do the most dammage possible per attack +local checkmele = function(self, ply) + if(ply:GetPos():Distance(self:GetPos()) < 100) then return 20 end + return -1 +end + +local domele = function(self, ply) + self:StartActivity(ACT_MELEE_ATTACK1) + --Antlion has 6 attack animations + local attackanim = math.Round(math.Rand(1,6)) + coroutine.wait(0.75) + --If the player is still in front of us after the animation, they didn't dodge! apply dammage! + if(ply:GetPos():Distance(self:GetPos()) < 100) then + ply:TakeDamage(20) + end + --Finish up the animation + coroutine.wait(0.25) +end + +local checkrun = function(self, ply) return 0 end + +local dorun = function(self, ply) + local navarea = navmesh.GetNavArea(self:GetPos(), 100) + self.loco:SetDesiredSpeed( 50 ) + if navarea:IsValid() then + local moveop = {} + moveop.tolerance = 50 + moveop.repath = 2 + moveop.lookahead = 3 + moveop.draw = true + self:StartActivity(ACT_WALK) + self:MoveToPos(ply:GetPos(),moveop) + else + print("Could not find valid navmesh, suicideing to prevent server crash!") + self:BecomeRagdoll(DamageInfo()) + end +end + +NPC.Attacks = { + [1] = { --A mele attack + [checkmele] = domele + }, + + [2] = {--Move to the player + [checkrun] = dorun + + }, + + +} + +--Attack priority is a fucntion that takes a player, and returns an int describing it's prority to attack (higher = more important) NPC will always attack the player with the highest priority +function NPC:AttackPriority(ply) + local dist = ply:GetPos():Distance(self:GetPos()) + return self.Stats["AwareDist"] - dist +end --A function that takes a position and returns true if this is an acceptable place to spawn NPC.SpawnLocations = nil @@ -25,7 +95,7 @@ NPC.SpawnLocations = nil NPC.Target = nil --All enemies that this NPC is aware of -NPC.AwareEnemies = nil +NPC.AwareEnemies = {} --What to replace the ENT:BehaveAct with function NPC:Act() @@ -33,74 +103,85 @@ end --What to replace ENT:OnStuck with function NPC:Stuck() - + --If we're stuck, jump backwards end --What to replace ENT:RunBehaviour with +--[[ function NPC:Behave() + --[[ print("Going into antlion's custom behaviour") while ( true ) do - self:StartActivity( ACT_IDLE ) -- walk anims - self:PlaySequenceAndWait( "Idle01" ) -- Sit on the floor - --Check if there are any players nearby + --self:PlaySequenceAndWait( "Idle" ) + + --Main loop for ai + + --Update aware enemies local players = ents.FindByClass("Player") - local playernearby = false for k,v in pairs(players) do - local fardist = 800 - local closedist = 300 - local removedist = 2000 - local iscrouched = v:Crouching() + if(v:IsPigeon()) then continue end local dist = v:GetPos():Distance(self:GetPos()) - --Remove ourselves if there's noone nearby - if(dist > 2000) then - playernearby = true + if( dist < self.Stats["AwareDist"]) then + table.insert(self.AwareEnemies,v) end - --Keep flying away as long as we're being chased - while((dist < fardist and not iscrouched) or (dist < closedist)) do -- We found a player! - if(dist < 100) then -- We're in attack range! - local target = v; - local targetpos = v:GetPos() - self:StartActivity(ACT_MELEE_ATTACK1) - --Antlion has 6 attack animations - local attackanim = math.Round(math.Rand(1,6)) - self:PlaySequenceAndWait("attack" .. attackanim) - --If the player is still in front of us after the animation, they didn't dodge apply dammage! - if(v:GetPos():Distance(targetps) < 100) then - v:TakeDamage(20) - end - else - self:StartActivity(ACT_WALK) - self:PlaySequenceAndWait( "walk_all" ) - - --Find a position in roughly the oposite direction of the player - local tpos = self:GetPos() - local ppos = v:GetPos() - local direction = Vector(ppos.x - tpos.x, ppos.y - tpos.y, ppos.z - tpos.z) - direction:Normalize() - local addition = direction * 1000 - local topos = self:GetPos() + addition - - local navarea = navmesh.GetNavArea(self:GetPos(), 100) - - if navarea:IsValid() then - self:MoveToPos(topos) - else - self:BecomeRagdoll(DamageInfo()) - end - - --Check to see if we're being chased - iscrouched = v:Crouching() - dist = v:GetPos():Distance(self:GetPos()) - end + end + + --Find the enemy with the highest priority + local maxpriority = -1 + local maxprioritytarget = nil + for k,v in pairs(self.AwareEnemies) do + local priority = self:AttackPriority(v) + if(priority > maxpriority) then + maxpriority = priority + maxprioritytarget = v end end - if not playernearby then - self:BecomeRagdoll(DamageInfo()) + self.Target = maxprioritytarget + + --If we can't find anyone to attack, just stay idle + if(self.Target == nil) then + print("Couldn't find anyone to attack!") + --Play an idle sequence + local randanim = math.Round(math.Rand(0,#self.IdleSequences)) + self:PlaySequenceAndWait( self.IdleSequences[randanim] ) + self:StartActivity( ACT_IDLE ) + else + --We have a target to attack! + print("We found something to attack!") + --Find which attack will do the most dammage + local maxdammage = -1 + local maxdammagefunc = nil + for k,v in pairs(self.Attacks) do + local dammagefunc = nil + local attackfunc = nil + for i,j in pairs(v) do + dammagefunc = i + attackfunc = j + end + print("Using damagefunc:") + print(dammagefunc) + local dammage = dammagefunc(self, self.Target) + print("dammage is ") + print(dammage) + if(dammage > maxdammage) then + maxdammage = dammage + maxdammagefunc = attackfunc + end + end + + --Do that attack + if(maxdammagefunc) then + maxdammagefunc(self, self.Target) + else + print("It looked like we could attack, but we can't!") + end end + --coroutine.yield() end + coroutine.yield() end - +--]] --These are just here to tell the editors/develoeprs what functions are available.. dont un-comment them out, as this could affect all the items. /* function NPC:OnSpawn() -- cgit v1.2.3-70-g09d2