This thread will be for anything related to the new NextBot system, I’ll keep the OP updated with useful information to help anyone who has questions or needs help.
Update 160 added bindings to the nextbot system, which is what CSS, TF2 and L4D bots use.
This allows us to do all kinds of stuff that we couldn’t do before with the old system. Including using coroutines thanks to garry.
[h2]Examples[/h2]
The code for the NextBot is here: link
And here is Garry’s test NPC:
[lua]AddCSLuaFile()
ENT.Base = “base_nextbot”
ENT.Spawnable = true
function ENT:Initialize()
–self:SetModel( “models/props_halloween/ghost_no_hat.mdl” );
–self:SetModel( “models/props_wasteland/controlroom_filecabinet002a.mdl” );
self:SetModel( “models/mossman.mdl” );
end
function ENT:BehaveAct()
end
function ENT:RunBehaviour()
while ( true ) do
– walk somewhere random
self:StartActivity( ACT_WALK ) – walk anims
self.loco:SetDesiredSpeed( 100 ) – walk speeds
self:MoveToPos( self:GetPos() + Vector( math.Rand( -1, 1 ), math.Rand( -1, 1 ), 0 ) * 200 ) – walk to a random place within about 200 units (yielding)
self:StartActivity( ACT_IDLE ) -- revert to idle activity
self:PlaySequenceAndWait( "idle_to_sit_ground" ) -- Sit on the floor
self:SetSequence( "sit_ground" ) -- Stay sitting
coroutine.wait( self:PlayScene( "scenes/eli_lab/mo_gowithalyx01.vcd" ) ) -- play a scene and wait for it to finish before progressing
self:PlaySequenceAndWait( "sit_ground_to_idle" ) -- Get up
-- find the furthest away hiding spot
local pos = self:FindSpot( "random", { type = 'hiding', radius = 5000 } )
-- if the position is valid
if ( pos ) then
self:StartActivity( ACT_RUN ) -- run anim
self.loco:SetDesiredSpeed( 200 ) -- run speed
self:PlayScene( "scenes/npc/female01/watchout.vcd" ) -- shout something while we run just for a laugh
self:MoveToPos( pos ) -- move to position (yielding)
self:PlaySequenceAndWait( "fear_reaction" ) -- play a fear animation
self:StartActivity( ACT_IDLE ) -- when we finished, go into the idle anim
else
-- some activity to signify that we didn't find shit
end
coroutine.yield()
end
end
– List the NPC as spawnable
list.Set( “NPC”, “npc_tf2_ghost”, { Name = “TF2 Ghost”,
Class = “npc_tf2_ghost”,
Category = “TF2”
})[/lua]
Here is a NPC that I made, it runs at the player and when its close enough it will jump at them and explode
[lua]AddCSLuaFile()
ENT.Base = “base_nextbot”
ENT.Spawnable = true
function ENT:Initialize()
self:SetModel( "models/weapons/ut2k4_parasite_mine.mdl" );
self:SetSkin(math.random(0,1))
self:SetHealth(50)
self.loco:SetDeathDropHeight(500) //default 200
self.loco:SetAcceleration(900) //default 400
self.loco:SetDeceleration(900) //default 400
self.loco:SetStepHeight(18) //default 18
self.loco:SetJumpHeight(50) //default 58
self.Isjumping = false
end
function ENT:Splode()
local Boom = ents.Create(“env_explosion”)
Boom:SetPos(self:GetPos())
Boom:SetKeyValue( “iMagnitude”, “90” )
// Boom:SetOwner(self.Entity:GetOwner())
Boom:SetOwner(self)
Boom:Spawn()
Boom:Fire(“Explode”,0,0)
Boom:Fire(“Kill”,0,0)
self:Remove()
end
–
– Name: NEXTBOT:BehaveUpdate
– Desc: Called to update the bot’s behaviour
– Arg1: number|interval|How long since the last update
– Ret1:
function ENT:BehaveUpdate( fInterval )
if ( !self.BehaveThread ) then return end
-- If you are not jumping yet and a player is close jump at them
if (!self.Isjumping) then
local ent = ents.FindInSphere( self:GetPos(), 120 )
for k,v in pairs( ent ) do
if v:IsPlayer() then
self.loco:FaceTowards( v:GetPos() )
self.loco:Jump( )
self.Isjumping = true
end
end
else -- If you are in the air and a player is really close explode
local ent = ents.FindInSphere( self:GetPos(), 50 )
for k,v in pairs( ent ) do
if v:IsPlayer() then
self:Splode()
end
end
end
local ok, message = coroutine.resume( self.BehaveThread )
if ( ok == false ) then
self.BehaveThread = nil
Msg( self, "error: ", message, "
" );
end
end
function ENT:RunBehaviour()
while ( true ) do
-- Find the player
pos = Entity(1):GetPos()
-- if the position is valid
if ( pos ) then
self:StartActivity( ACT_RUN ) -- run anim
self.loco:SetDesiredSpeed( 360 ) -- run speed
local opts = { lookahead = 300,
tolerance = 20,
draw = true,
maxage = 1,
repath = 0.1 }
self:MoveToPos( pos, opts ) -- move to position (yielding)
else
-- some activity to signify that we didn't find shit
self:StartActivity( ACT_RUN ) -- walk anims
self.loco:SetDesiredSpeed( 360 ) -- walk speeds
self:MoveToPos( self:GetPos() + Vector( math.Rand( -1, 1 ), math.Rand( -1, 1 ), 0 ) * 200 ) -- walk to a random place within about 200 units (yielding)
end
coroutine.yield()
end
end
function ENT:OnLandOnGround()
self.Isjumping = false
self:StartActivity( ACT_RUN )
end
function ENT:OnKilled( damageinfo )
-- If its killed by something other then it exploding then explode
if ( damageinfo:GetAttacker() != self ) then
self:Splode()
end
self:BecomeRagdoll( damageinfo )
end
–
– List the NPC as spawnable
list.Set( “NPC”, “UT2K4_SpiderMine”, { Name = “UT2K4 Spider Mine”,
Class = “UT2K4_SpiderMine”,
Category = “UT2K4”
})[/lua]
Here is it in action:
[h2]Useful links[/h2]
Gmod wiki
BotPath
Locomotion
NavArea
NextBot
NextBot Hooks
navmesh library
Valve wiki
Navigation Meshes
[h2]Useful information[/h2]
- CSS maps have navmeshes but you have to copy them from the gcf to be able to use them in GMod
- Locomotion is accessible through a NextBot entity’s ‘loco’ property/key.
- NextBot cvars begin with the nb_ prefix
[h2]NextBot Console Commands[/h2]
nb_allow_avoiding : 1 : , "sv", "cheat" :
nb_allow_climbing : 1 : , "sv", "cheat" :
nb_allow_gap_jumping : 1 : , "sv", "cheat" :
nb_blind : 0 : , "sv", "cheat" : Disable vision
nb_command : cmd : : Sends a command string to all bots
nb_debug : cmd : : Debug NextBots. Categories are: BEHAVIOR, LOOK_AT, PATH, ANIMATION, LOCOMOTION, VISION, HEARING, EVENTS, ERRORS.
nb_debug_climbing : 0 : , "sv", "cheat" :
nb_debug_filter : cmd : : Add items to the NextBot debug filter. Items can be entindexes or part of the indentifier of one or more bots.
nb_debug_history : 1 : , "sv", "cheat" : If true, each bot keeps a history of debug output in memory
nb_debug_known_entities : 0 : , "sv", "cheat" : Show the 'known entities' for the bot that is the current spectator target
nb_delete_all : cmd : : Delete all non-player NextBot entities.
nb_force_look_at : cmd : : Force selected bot to look at the local player's position
nb_goal_look_ahead_range : 50 : , "sv", "cheat" :
nb_head_aim_resettle_angle : 100 : , "sv", "cheat" : After rotating through this angle, the bot pauses to 'recenter' its virtual mouse on its virtual mousepad
nb_head_aim_resettle_time : 0 : , "sv", "cheat" : How long the bot pauses to 'recenter' its virtual mouse on its virtual mousepad
nb_head_aim_settle_duration : 0 : , "sv", "cheat" :
nb_head_aim_steady_max_rate : 100 : , "sv", "cheat" :
nb_ladder_align_range : 50 : , "sv", "cheat" :
nb_last_area_update_tolerance : 4 : , "sv", "cheat" : Distance a character needs to travel in order to invalidate cached area
nb_move_to_cursor : cmd : : Tell all NextBots to move to the cursor position
nb_path_draw_inc : 100 : , "sv", "cheat" :
nb_path_draw_segment_count : 100 : , "sv", "cheat" :
nb_path_segment_influence_radius : 100 : , "sv", "cheat" :
nb_player_crouch : 0 : , "sv", "cheat" : Force bots to crouch
nb_player_move : 1 : , "sv", "cheat" : Prevents bots from moving
nb_player_move_direct : 0 : , "sv" :
nb_player_stop : 0 : , "sv", "cheat" : Stop all NextBotPlayers from updating
nb_player_walk : 0 : , "sv", "cheat" : Force bots to walk
nb_saccade_speed : 1000 : , "sv", "cheat" :
nb_saccade_time : 0 : , "sv", "cheat" :
nb_select : cmd : : Select the bot you are aiming at for further debug operations.
nb_shadow_dist : 400 : , "cl" :
nb_speed_look_ahead_range : 150 : , "sv", "cheat" :
nb_stop : 0 : , "sv", "cheat", "rep" : Stop all NextBots
nb_update_debug : 0 : , "sv", "cheat" :
nb_update_framelimit : 15 : , "sv", "cheat" :
nb_update_frequency : 0 : , "sv", "cheat" :
nb_update_maxslide : 2 : , "sv", "cheat" :
nb_warp_selected_here : cmd : : Teleport the selected bot to your cursor position
I’ll make a better OP tonight when I have more time.