NextBot Contantly Follow Entity

Hello all,

I dived into learning about nextbot creation and found myself wanting to make a simple nextbot that follows the player around (like a companion). I then found myself stuck as I’ve gotten really confused about coroutines and my functions were not getting called repeatedly.
I found this code from here:



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 enemy's 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 )								-- This function moves the bot along the path
		
		if ( options.draw ) then path:Draw() end
		-- If we're stuck ) then call the HandleStuck function and abandon
		if ( self.loco:IsStuck() ) then
			self:HandleStuck()
			return "stuck"
		end

		coroutine.yield()

	end

	return "ok"

end


I tried to modify it but I had no luck with implementing it with the RunBehaviour as they did.

All I want to know is how can I get my nextbot to follow an entity constantly.

Thanks,
Computer600

PS: I do not want to use the GetEnemy or SetEnemy functions, if thats the only way then please tell me or suggest better ways, thanks again

where and when are you using this in your code?

I don’t recall where I found these, but I think they’re in the NextBot discussion thread… It is in the list of links on the wall of text I’m about to post ( at the bottom ).

nextbot_enemy.lua


AddCSLuaFile()
 
ENT.Base			= "base_nextbot"
 
function ENT:Initialize()


	self:SetModel( "models/Zombie/Classic.mdl" )
	self.Enemy=player.GetHumans()[1]
	self.Attacking=true
	self.Idling=false
	self:StartActivity( ACT_WALK )
	self.targetname=self:GetClass().."_"..tostring(self:EntIndex())
	self:SetKeyValue("targetname",self.targetname)
	--self:SetHealth(40)
	self.WalkSpeed=100
	self.NextAmble=0
	self:SetHealth( 250 )
end

function ENT:EnemyInRange()
	return self.Enemy:GetPos():Distance(self:GetPos())<=80
end

function ENT:OnStuck()
	
end

function ENT:OnInjured(dmginfo) 
	self.Idling=false
	print(dmginfo)
	--self.Enemy=dmginfo:GetAttacker()
end

function ENT:OnOtherKilled(dmginfo)
	self.Idling=false
	--self.Enemy=dmginfo:GetAttacker()
end

function ENT:OnLandOnGround() end

function ENT:ShouldChase(entity)
	return self.Attacking
end



function ENT:RunBehaviour()

	while ( true ) do
 
		
		if self.Idling then
			self.WalkSpeed=50
			
			if CurTime()>self.NextAmble then
				self:StartActivity(ACT_WALK)
				self:MoveToPos( self:GetPos() + Vector( math.Rand( -1, 1 ), math.Rand( -1, 1 ), 0 ) * 200 )
				
				self.NextAmble=CurTime()--+math.random(1,5)
			end
		elseif self.Attacking then
			self.WalkSpeed=100
		end
		self.loco:SetDesiredSpeed( self.WalkSpeed )
		if not self.Attacking then
			self.Idling=true
		else
			self.Idling=false
		end
		if IsValid(self.Enemy) and not self.Idling then
			if self:ShouldChase(self.Enemy) then
				self.Attacking=true
				local maxageScaled=math.Clamp(self.Enemy:GetPos():Distance(self:GetPos())/1000,0.1,3)
				self:MoveToPos(self.Enemy:GetPos(),{draw=false,maxage=maxageScaled})
				self:CheckDoorOpen()
				if self:EnemyInRange() then
					
					
					self:StartActivity(ACT_MELEE_ATTACK1)
					coroutine.wait(1)
					
					
					
					if self:EnemyInRange() then
					local dmginfo=DamageInfo()	
						dmginfo:SetDamagePosition(self:GetPos()+Vector(0,0,50))
						dmginfo:SetDamage(10) --damageless atm
						dmginfo:SetDamageType(DMG_CLUB)
						dmginfo:SetAttacker(self)
						self.Enemy:TakeDamageInfo(dmginfo)
						if self.Enemy:IsPlayer() then
							local moveAdd=Vector(0,0,200)
							if not self.Enemy:IsOnGround() then
								moveAdd=Vector(0,0,0)
							end
							self.Enemy:SetVelocity(moveAdd+((self.Enemy:GetPos()-self:GetPos()):GetNormal()*100))
						end
					end
					-- coroutine.wait(0.5) --For attack to finish
					self:StartActivity( ACT_WALK )
					
				end
			else
				self.Attacking=false
			end
		end
		
		
	
		coroutine.yield()
	end
	
end

function ENT:CheckDoorOpen()
	local offset=Vector(0,0,50)
	local doorTrace={
		start=self:GetPos()+offset,
		endpos=self:GetPos()+(self:EyeAngles():Forward()*70)+offset,
		filter=self,
		mask=MASK_NPCSOLID
	}
	local doorTraceRes=util.TraceLine(doorTrace)
	if IsValid(doorTraceRes.Entity) and doorTraceRes.Entity~=NULL then
		if string.match(doorTraceRes.Entity:GetClass(),"door") then
			--doorTraceRes.Entity:Fire("OpenAwayFrom",self.targetname)
			self:StartActivity(ACT_MELEE_ATTACK1)
			coroutine.wait(1)
				if IsValid(doorTraceRes.Entity) then
					local door=doorTraceRes.Entity
					door:SetNotSolid(true)
					door:DrawShadow(false)
					door:SetNoDraw(true)
					door:EmitSound("physics/wood/wood_box_impact_hard3.wav")
					door:Fire("Unlock", "", 0)
					
					
					local fakeDoor = CreateEntity("prop_physics")
	
					
					fakeDoor:SetAngles( door:GetAngles() )
					fakeDoor:SetModel( door:GetModel() )
					fakeDoor:SetSkin( door:GetSkin() )
					fakeDoor:SetPos( door:GetPos() )
					fakeDoor:Spawn()
					fakeDoor.IsFakeDoor=true

					
					
					
					fakeDoor:SetCollisionGroup(COLLISION_GROUP_WORLD)
							
				end
			coroutine.wait(0.5)
			self:StartActivity( ACT_WALK )
		end
	end
end
 
	

nextbot_passive.lua


AddCSLuaFile()
 
ENT.Base			= "base_nextbot"
 
function ENT:Initialize()

	self:SetModel( "models/Zombie/Classic.mdl" )
	self.Enemy = player.GetHumans( )[ 1 ]
	self.Attacking = true
	self.Idling = false
	self:StartActivity( ACT_WALK )
	self.targetname = self:GetClass( ) .. "_" .. tostring( self:EntIndex( ) )
	self:SetKeyValue( "targetname", self.targetname )
	--self:SetHealth( 40 )
	self.WalkSpeed = 100
	self.NextAmble = 0


end

-- function ENT:AcceptInput( Name, Activator, Caller )
	-- print( "NEXTBOT ACCEPTINPUT: ", Name, Activator, Caller )
-- end

-- function ENT:Use( ... )
	-- print( "NEXTBOT USE: ", ... )
-- end

function ENT:EnemyInRange( )
	return self.Enemy:GetPos( ):Distance( self:GetPos( ) ) <= 80
end

function ENT:OnStuck()
	
end

function ENT:OnInjured(dmginfo) 
	self.Idling=false
	print(dmginfo)
	--self.Enemy=dmginfo:GetAttacker()
end

function ENT:OnOtherKilled(dmginfo)
	self.Idling=false
	--self.Enemy=dmginfo:GetAttacker()
end

function ENT:OnLandOnGround() end

function ENT:ShouldChase(entity)
	return self.Attacking
end



function ENT:RunBehaviour()

	while ( true ) do
 
		
		if self.Idling then
			self.WalkSpeed=50
			
			if CurTime()>self.NextAmble then
				self:StartActivity(ACT_WALK)
				self:MoveToPos( self:GetPos() + Vector( math.Rand( -1, 1 ), math.Rand( -1, 1 ), 0 ) * 200 )
				
				self.NextAmble=CurTime()--+math.random(1,5)
			end
		elseif self.Attacking then
			self.WalkSpeed=100
		end
		self.loco:SetDesiredSpeed( self.WalkSpeed )
		if not self.Attacking then
			self.Idling=true
		else
			self.Idling=false
		end
		if IsValid(self.Enemy) and not self.Idling then
			if self:ShouldChase(self.Enemy) then
				self.Attacking=true
				local maxageScaled=math.Clamp(self.Enemy:GetPos():Distance(self:GetPos())/1000,0.1,3)
				self:MoveToPos(self.Enemy:GetPos(),{draw=false,maxage=maxageScaled})
				self:CheckDoorOpen()
				if self:EnemyInRange() then
					
					
					self:StartActivity(ACT_MELEE_ATTACK1)
					coroutine.wait(1)
					
					
					
					if self:EnemyInRange() then
					local dmginfo=DamageInfo()	
						dmginfo:SetDamagePosition(self:GetPos()+Vector(0,0,50))
						dmginfo:SetDamage(10) --damageless atm
						dmginfo:SetDamageType(DMG_CLUB)
						dmginfo:SetAttacker(self)
						self.Enemy:TakeDamageInfo(dmginfo)
						if self.Enemy:IsPlayer() then
							local moveAdd=Vector(0,0,200)
							if not self.Enemy:IsOnGround() then
								moveAdd=Vector(0,0,0)
							end
							self.Enemy:SetVelocity(moveAdd+((self.Enemy:GetPos()-self:GetPos()):GetNormal()*100))
						end
					end
					coroutine.wait(0.5) --For attack to finish
					self:StartActivity( ACT_WALK )
					
				end
			else
				self.Attacking=false
			end
		end
		
		
	
		coroutine.yield()
	end
	
end

function ENT:CheckDoorOpen()
	local offset=Vector(0,0,50)
	local doorTrace={
		start=self:GetPos()+offset,
		endpos=self:GetPos()+(self:EyeAngles():Forward()*70)+offset,
		filter=self,
		mask=MASK_NPCSOLID
	}
	local doorTraceRes=util.TraceLine(doorTrace)
	if IsValid(doorTraceRes.Entity) and doorTraceRes.Entity~=NULL then
		if string.match(doorTraceRes.Entity:GetClass(),"door") then
			--doorTraceRes.Entity:Fire("OpenAwayFrom",self.targetname)
			self:StartActivity(ACT_MELEE_ATTACK1)
			coroutine.wait(1)
				if IsValid(doorTraceRes.Entity) then
					local door=doorTraceRes.Entity
					door:SetNotSolid(true)
					door:DrawShadow(false)
					door:SetNoDraw(true)
					door:EmitSound("physics/wood/wood_box_impact_hard3.wav")
					door:Fire("Unlock", "", 0)
					
					
					local fakeDoor = CreateEntity("prop_physics")
	
					
					fakeDoor:SetAngles( door:GetAngles() )
					fakeDoor:SetModel( door:GetModel() )
					fakeDoor:SetSkin( door:GetSkin() )
					fakeDoor:SetPos( door:GetPos() )
					fakeDoor:Spawn()
					fakeDoor.IsFakeDoor=true

					
					
					
					fakeDoor:SetCollisionGroup(COLLISION_GROUP_WORLD)
							
				end
			coroutine.wait(0.5)
			self:StartActivity( ACT_WALK )
		end
	end
end
 
	

And links to helpful threads and more:

Hey, welcome to FacePunch.
I have written over 400 tutorials and completed “systems” in Lua for Garry’s Mod. I tutor and answer questions for free; feel free to add me on Steam if you need some guidance. This forum is for devs that need help working on things. Here are some resources to help you get started:

Generalized Lua Help ( Links to Wikis, Answers the question of “Where do I post a simple question or DarkRP Specific question”, links to other resources compiled by forum members )
https://dl.dropboxusercontent.com/u/26074909/tutoring/___welcome_docs/_welcome_general_lua_learning.lua.html

Useful Programs ( SteamCMD, Autosizer, Desktops, Process Explorer ) and Notepad++ Upgrades
https://dl.dropboxusercontent.com/u/26074909/tutoring/___welcome_docs/_welcome_useful_programs_and_notepadpp_upgrades.lua.html

AcecoolDev_Base Skeletonized Dev Base Game-Mode ( Never worry about Include or AddCSLuaFile ever again; comes with New Hooks, Console Commands, Meta-Table Objects, Helper Functions, Extended Functionality, and more! )
https://dl.dropboxusercontent.com/u/26074909/tutoring/___welcome_docs/_welcome_acecooldev_base_gamemode_info.lua.html