Nextbot randomly moving toward its enemy when it isn't supposed to
4 replies, posted
--Beware, long code incoming--
[code]AddCSLuaFile()
ENT.Base = "base_nextbot"
ENT.Spawnable = true
function ENT:Initialize()
local models = {
"models/humans/group03/male_01.mdl",
"models/humans/group03/male_02.mdl",
"models/humans/group03/male_03.mdl",
"models/humans/group03/male_04.mdl",
"models/humans/group03/male_05.mdl",
}
self:SetModel( table.Random( models ) )
self:SetHealth( 100 )
self.Running = false
self.lastthink = 0
self.LoseTargetDist = 2000 -- How far the enemy has to be before we lose them
self.SearchRadius = 2000 -- How far to search for enemies
end
----------------------------------------------------
-- ENT:Get/SetEnemy()
-- Simple functions used in keeping our enemy saved
----------------------------------------------------
function ENT:SetEnemy( ent )
self.Enemy = ent
end
function ENT:GetEnemy()
return self.Enemy
end
----------------------------------------------------
-- ENT:HaveEnemy()
-- Returns true if we have a enemy
----------------------------------------------------
function ENT:HaveEnemy()
-- If our current enemy is valid
if ( self:GetEnemy() and IsValid( self:GetEnemy() ) and self:GetEnemy():Alive() ) then
-- If the enemy is too far
if ( self:GetRangeTo( self:GetEnemy():GetPos() ) > self.LoseTargetDist ) then
-- If the enemy is lost then call FindEnemy() to look for a new one
-- FindEnemy() will return true if an enemy is found, making this function return true
return self:FindEnemy()
elseif self:GetRangeTo( self:GetEnemy():GetPos() ) <= 1000 then
if self.Running then
self.loco:SetDesiredSpeed( 0 )
self.loco:SetAcceleration( 0 )
self.loco:FaceTowards( self:GetEnemy():GetPos() )
self.Running = false --if we're running then make us not be running
end
self:ShootEnemy()
elseif self:GetRangeTo( self:GetEnemy():GetPos() ) > 1000 then
if self.Running == false or self.Running == nil then
self:StartActivity( ACT_RUN ) --if we arent running then make us run
self.loco:SetDesiredSpeed( 200 )
self.Running = true
end
-- If the enemy is dead( we have to check if its a player before we use Alive() )
elseif ( self:GetEnemy():IsPlayer() and !self:GetEnemy():Alive() ) then
return self:FindEnemy() -- Return false if the search finds nothing
end
-- The enemy is neither too far nor too dead so we can return true
return true
else
-- The enemy isn't valid so lets look for a new one
return self:FindEnemy()
end
end
function ENT:OnKilled( dmginfo )
hook.Call( "OnNPCKilled", GAMEMODE, self, dmginfo:GetAttacker(), dmginfo:GetInflictor() )
self:BecomeRagdoll( dmginfo )
self:EmitSound( "npc/ichthyosaur/attack_growl1.wav" )
end
----------------------------------------------------
-- ENT:FindEnemy()
-- Returns true and sets our enemy if we find one
----------------------------------------------------
function ENT:FindEnemy()
-- Search around us for entities
local _ents = ents.FindInSphere( self:GetPos(), self.SearchRadius )--ents.FindInCone( self:GetPos() + self:GetForward() * 10, self:GetForward(), 1000, 180 )
-- Here we loop through every entity the above search finds and see if it
--is what we want
for k, v in pairs( _ents ) do
if ( v:IsPlayer() and v:Health() > 0 ) then
self:SetEnemy( v )
return true
end
end
-- We found nothing so we will set our enemy as nil ( nothing ) and return false
self:SetEnemy( nil )
return false
end
function ENT:GiveWeapon(wep)
local wep = ents.Create(wep)
local pos = self:GetAttachment(self:LookupAttachment("anim_attachment_RH")).Pos
wep:SetOwner(self)
wep:SetPos(pos)
wep:Spawn()
wep.DontPickUp = true
wep:SetSolid(SOLID_NONE)
wep:SetParent(self)
wep:Fire("setparentattachment", "anim_attachment_RH")
wep:AddEffects(EF_BONEMERGE)
self.Wep = wep
end
function ENT:Think()
if not IsValid(self.Wep) then
self:GiveWeapon( "weapon_ar2" )
end
end
function ENT:MeleeAttack()
self:EmitSound( "Flesh.ImpactHard" )
self:PlaySequenceAndWait( "MeleeAttack01" )
self:GetEnemy():TakeDamage( 10 )
end
function ENT:ShootEnemy()
local bullet = {}
bullet.Num = 1
bullet.Src = self:GetForward()
bullet.Dir = self:GetEnemy():WorldSpaceCenter()
bullet.Spread = Vector(10,10,0)
bullet.Tracer = 1
bullet.TracerName = "Tracer"
bullet.Force = 1
bullet.Damage = math.random(1,3)
bullet.AmmoType = "ar2"
self:FireBullets( bullet )
self:PlaySequenceAndWait( "shoot_smg1" )
self:EmitSound( "Weapon_AR2.Single" )
self:MuzzleFlash()
end
----------------------------------------------------
-- ENT:RunBehaviour()
-- This is where the meat of our AI is
----------------------------------------------------
function ENT:RunBehaviour()
-- This function is called when the entity is first spawned. It acts as a giant loop that will run as long as the NPC exists
while ( true ) do
-- Lets use the above mentioned functions to see if we have/can find a enemy
if ( self:HaveEnemy() and self:GetEnemy() ) then
-- Now that we have an enemy, the code in this block will run
self.loco:FaceTowards( self:GetEnemy():GetPos() ) -- Face our enemy
--self:StartActivity( ACT_RUN ) -- Set the animation
--self.loco:SetDesiredSpeed( 300 ) -- Set the speed that we will be moving at. Don't worry, the animation will speed up/slow down to match
--self.loco:SetAcceleration( 500 ) -- We are going to run at the enemy quickly, so we want to accelerate really fast
self:ChaseEnemy() -- The new function like MoveToPos.
--self.loco:SetAcceleration( 600 ) -- Set this back to its default since we are done chasing the enemy
self:StartActivity( ACT_IDLE ) --We are done so go back to idle
-- Now once the above function is finished doing what it needs to do, the code will loop back to the start
-- unless you put stuff after the if statement. Then that will be run before it loops
else
-- Since we can't find an enemy, lets wander
-- Its the same code used in Garry's test bot
self:StartActivity( ACT_WALK ) -- Walk animation
self.loco:SetDesiredSpeed( 100 ) -- Walk speed
self:MoveToPos( self:GetPos() + Vector( math.Rand( -1, 1 ), math.Rand( -1, 1 ), 0 ) * 400 ) -- Walk to a random place within about 400 units ( yielding )
self:StartActivity( ACT_IDLE )
end
-- At this point in the code the bot has stopped chasing the player or finished walking to a random spot
-- Using this next function we are going to wait 2 seconds until we go ahead and repeat it
coroutine.wait( 0.10 )
end
end
----------------------------------------------------
-- ENT:ChaseEnemy()
-- Works similarly to Garry's MoveToPos function
-- except it will constantly follow the
-- position of the enemy until there no longer
-- is one.
----------------------------------------------------
function ENT:ChaseEnemy( options )
local options = options or {}
local path = Path( "Follow" )
path:SetMinLookAheadDistance( options.lookahead or 300 )
path:SetGoalTolerance( options.tolerance or 20 )
path:Compute( self, self:GetEnemy():GetPos() ) -- Compute the path towards the enemies position
if ( !path:IsValid() ) then return "failed" end
while ( path:IsValid() and self:HaveEnemy() ) do
if ( path:GetAge() > 0.1 ) then -- Since we are following the player we have to constantly remake the path
path:Compute( self, self:GetEnemy():GetPos() )-- Compute the path towards the enemy's position again
end
path:Update( self )
I would try Breaking your Chase Function When the enemy is within 1000 units
[CODE]
function ENT:ChaseEnemy( options )
local options = options or {}
local path = Path( "Follow" )
path:SetMinLookAheadDistance( options.lookahead or 300 )
path:SetGoalTolerance( options.tolerance or 20 )
path:Compute( self, self:GetEnemy():GetPos() )
if ( !path:IsValid() ) then return "failed" end
while ( path:IsValid() and self:HaveEnemy() ) do
if self:GetRangeTo(self:GetEnemy():GetPos()) <= 1000 then break end -- stop chasing my enemy if he's within 1000 units
if ( path:GetAge() > 0.1 ) then
path:Compute( self, self:GetEnemy():GetPos() )
end
path:Update( self )
if ( options.draw ) then path:Draw() end
if ( self.loco:IsStuck() ) then
self:HandleStuck()
return "stuck"
end
coroutine.yield()
end
return "ok"
end[/CODE]
also, you're bot's not gonna shoot anything if your shoot functions are inside HaveEnemy() unless you return them, either way, i'd put them out inside the Behavior Coroutine, or in Think(), depending on how your bot is supposed to function.
[QUOTE=MysticLlama;48067267]I would try Breaking your Chase Function When the enemy is within 1000 units
[CODE]
function ENT:ChaseEnemy( options )
local options = options or {}
local path = Path( "Follow" )
path:SetMinLookAheadDistance( options.lookahead or 300 )
path:SetGoalTolerance( options.tolerance or 20 )
path:Compute( self, self:GetEnemy():GetPos() )
if ( !path:IsValid() ) then return "failed" end
while ( path:IsValid() and self:HaveEnemy() ) do
if self:GetRangeTo(self:GetEnemy():GetPos()) <= 1000 then break end -- stop chasing my enemy if he's within 1000 units
if ( path:GetAge() > 0.1 ) then
path:Compute( self, self:GetEnemy():GetPos() )
end
path:Update( self )
if ( options.draw ) then path:Draw() end
if ( self.loco:IsStuck() ) then
self:HandleStuck()
return "stuck"
end
coroutine.yield()
end
return "ok"
end[/CODE]
also, you're bot's not gonna shoot anything if your shoot functions are inside HaveEnemy() unless you return them, either way, i'd put them out inside the Behavior Coroutine, or in Think(), depending on how your bot is supposed to function.[/QUOTE]
Nope. Having the shooting functions in HaveEnemy works perfectly fine.
[QUOTE=A Fghtr Pilot;48067656]Nope. Having the shooting functions in HaveEnemy works perfectly fine.[/QUOTE]
My mistake. Was looking at it differently.
But i tested your bot, and adding the Break to the chase function fixes the movement issue.
However, the bot now shifts constantly from its firing animation to its idle animation, so i would recommend changing that StartActivity() right after the self:ChaseEnemy() to play the "ACT_IDLE_SMG1" activity instead of "ACT_IDLE".
[QUOTE=MysticLlama;48072625]My mistake. Was looking at it differently.
But i tested your bot, and adding the Break to the chase function fixes the movement issue.
However, the bot now shifts constantly from its firing animation to its idle animation, so i would recommend changing that StartActivity() right after the self:ChaseEnemy() to play the "ACT_IDLE_SMG1" activity instead of "ACT_IDLE".[/QUOTE]
Also I fixed the issue in a different way too. Thanks for trying to help though
Sorry, you need to Log In to post a reply to this thread.