iw6-scripts-dev/maps/mp/bots/_bots_personality.gsc
2024-12-11 11:28:08 +01:00

903 lines
26 KiB
Plaintext

// Personality functions for bots
#include common_scripts\utility;
#include maps\mp\_utility;
#include maps\mp\bots\_bots_util;
#include maps\mp\bots\_bots_loadout;
#include maps\mp\bots\_bots_strategy;
//=======================================================
// setup_personalities
//=======================================================
setup_personalities()
{
level.bot_personality = [];
level.bot_personality_list = [];
level.bot_personality["active"][0] = "default";
level.bot_personality["active"][1] = "run_and_gun";
level.bot_personality["active"][2] = "cqb";
level.bot_personality["stationary"][0] = "camper";
level.bot_personality_type = [];
foreach( index, personality_array in level.bot_personality )
{
foreach( personality in personality_array )
{
level.bot_personality_type[personality] = index;
level.bot_personality_list[level.bot_personality_list.size] = personality;
}
}
// Desired ratio of personalities
// Currently we would like 2 active bots (run and gun, cqb, etc) for every 1 camper-type bot
level.bot_personality_types_desired = [];
level.bot_personality_types_desired["active"] = 2;
level.bot_personality_types_desired["stationary"] = 1;
level.bot_pers_init = [];
level.bot_pers_init["default"] = ::init_personality_default;
level.bot_pers_init["camper"] = ::init_personality_camper;
level.bot_pers_update["default"] = ::update_personality_default;
level.bot_pers_update["camper"] = ::update_personality_camper;
}
//=======================================================
// assign_personality_functions
//=======================================================
bot_assign_personality_functions()
{
self.personality = self BotGetPersonality();
self.personality_init_function = level.bot_pers_init[self.personality];
if ( !IsDefined(self.personality_init_function) )
{
self.personality_init_function = level.bot_pers_init["default"];
}
// Call the init function now
self [[ self.personality_init_function ]]();
self.personality_update_function = level.bot_pers_update[self.personality];
if ( !IsDefined(self.personality_update_function) )
{
self.personality_update_function = level.bot_pers_update["default"];
}
}
//=======================================================
// bot_balance_personality
//=======================================================
bot_balance_personality()
{
if( IsDefined(self.personalityManuallySet) && self.personalityManuallySet )
{
return;
}
if ( bot_is_fireteam_mode() )
return;
Assert(level.bot_personality.size == level.bot_personality_types_desired.size);
persCounts = [];
persCountsByType = [];
foreach( personality_type, personality_array in level.bot_personality )
{
persCountsByType[personality_type] = 0;
foreach( personality in personality_array )
persCounts[personality] = 0;
}
// Count up the number of personalities on my team (not including myself), and also the types
foreach( bot in level.players )
{
if ( IsBot(bot) && IsDefined(bot.team) && (bot.team == self.team) && (bot != self) && IsDefined(bot.has_balanced_personality) )
{
personality = bot BotGetPersonality();
personality_type = level.bot_personality_type[personality];
persCounts[personality] = persCounts[personality] + 1;
persCountsByType[personality_type] = persCountsByType[personality_type] + 1;
}
}
// Determine which personality type is needed by looking at level.bot_personality_types_desired, which is the desired ratio of personalities
type_needed = undefined;
while( !IsDefined(type_needed) )
{
personality_types_desired_in_progress = level.bot_personality_types_desired;
while ( personality_types_desired_in_progress.size > 0 )
{
pers_type_picked = bot_get_string_index_for_integer( personality_types_desired_in_progress, RandomInt(personality_types_desired_in_progress.size) );
persCountsByType[pers_type_picked] -= level.bot_personality_types_desired[pers_type_picked];
if ( persCountsByType[pers_type_picked] < 0 )
{
type_needed = pers_type_picked;
break;
}
personality_types_desired_in_progress[pers_type_picked] = undefined;
}
}
// Now that we know which personality type is needed, figure out which personality is needed within that type
personality_needed = undefined;
least_common_personality = undefined;
least_common_personality_uses = 9999;
most_common_personality = undefined;
most_common_personality_uses = -9999;
randomized_personalities_needed = array_randomize(level.bot_personality[type_needed]);
foreach ( personality in randomized_personalities_needed )
{
if ( persCounts[personality] < least_common_personality_uses )
{
least_common_personality = personality;
least_common_personality_uses = persCounts[personality];
}
if ( persCounts[personality] > most_common_personality_uses )
{
most_common_personality = personality;
most_common_personality_uses = persCounts[personality];
}
}
if ( most_common_personality_uses - least_common_personality_uses >= 2 )
personality_needed = least_common_personality;
else
personality_needed = Random(level.bot_personality[type_needed]);
if ( self BotGetPersonality() != personality_needed )
self BotSetPersonality( personality_needed );
self.has_balanced_personality = true;
}
//=======================================================
// init_camper_data
//=======================================================
init_personality_camper()
{
clear_camper_data();
}
//=======================================================
// init_personality_default
//=======================================================
init_personality_default()
{
// clear camper data here too in case bot was changing personality from camper
clear_camper_data();
}
SCR_CONST_CAMPER_HUNT_TIME = 10000;
//=======================================================
// update_personality_camper
//=======================================================
update_personality_camper()
{
if ( should_select_new_ambush_point() && !self bot_is_defending() && !self bot_is_remote_or_linked() )
{
// If we took a detour to "hunt" then wait till that goal gets cleared before camping again
goalType = self BotGetScriptGoalType();
foundCampNode = false;
if ( !IsDefined(self.camper_time_started_hunting) )
self.camper_time_started_hunting = 0;
is_hunting = (goalType == "hunt");
time_to_stop_hunting = GetTime() > self.camper_time_started_hunting + SCR_CONST_CAMPER_HUNT_TIME;
if ( ( !is_hunting || time_to_stop_hunting ) && !self bot_out_of_ammo() ) // Don't look for a camp node if we're out of ammo
{
if ( !(self BotHasScriptGoal()) )
{
// If we dont currently have a script goal act like a run and gunner while we wait for a camping goal
self bot_random_path();
}
foundCampNode = self find_camp_node();
if ( !foundCampNode )
self.camper_time_started_hunting = GetTime();
}
if ( IsDefined( foundCampNode ) && foundCampNode )
{
self.ambush_entrances = self bot_queued_process( "bot_find_ambush_entrances", ::bot_find_ambush_entrances, self.node_ambushing_from, true );
// If applicable, plant a trap to cover our rear first
trap_item = self bot_get_ambush_trap_item( "trap_directional", "trap", "c4" );
if ( IsDefined( trap_item ) )
{
trapTime = GetTime();
self bot_set_ambush_trap( trap_item, self.ambush_entrances, self.node_ambushing_from, self.ambush_yaw );
trapTime = GetTime() - trapTime;
if ( trapTime > 0 && IsDefined( self.ambush_end ) && IsDefined( self.node_ambushing_from ) )
{
self.ambush_end += trapTime;
self.node_ambushing_from.bot_ambush_end = self.ambush_end + 10000;
}
}
if ( !(self bot_has_tactical_goal()) && !self bot_is_defending() && IsDefined(self.node_ambushing_from) )
{
// Go to the ambush point
self BotSetScriptGoalNode( self.node_ambushing_from, "camp", self.ambush_yaw );
self thread clear_script_goal_on( "bad_path", "node_relinquished", "out_of_ammo" );
self thread watch_out_of_ammo();
// When we get there add in the travel time to our camping timer
self thread bot_add_ambush_time_delayed( "clear_camper_data", "goal" );
// When we get there look toward each of the entrances periodically
self thread bot_watch_entrances_delayed( "clear_camper_data", "bot_add_ambush_time_delayed", self.ambush_entrances, self.ambush_yaw );
}
}
else
{
// Hunt until we can find a reasonable camping spot to use
if ( goalType == "camp" )
self BotClearScriptGoal();
update_personality_default();
}
}
}
//=======================================================
// update_personality_default
//=======================================================
update_personality_default()
{
script_goal = undefined;
has_script_goal = self BotHasScriptGoal();
if ( has_script_goal )
script_goal = self BotGetScriptGoal();
if ( !(self bot_has_tactical_goal()) && !self bot_is_remote_or_linked() )
{
distSq = undefined;
goalRadius = undefined;
if ( has_script_goal )
{
distSq = DistanceSquared(self.origin,script_goal);
goalRadius = self BotGetScriptGoalRadius();
goalRadiusDbl = goalRadius * 2;
if ( IsDefined( self.bot_memory_goal ) && distSq < goalRadiusDbl*goalRadiusDbl )
{
flagInvestigated = BotMemoryFlags( "investigated" );
BotFlagMemoryEvents( 0, GetTime() - self.bot_memory_goal_time, 1, self.bot_memory_goal, goalRadiusDbl, "kill", flagInvestigated, self );
BotFlagMemoryEvents( 0, GetTime() - self.bot_memory_goal_time, 1, self.bot_memory_goal, goalRadiusDbl, "death", flagInvestigated, self );
self.bot_memory_goal = undefined;
self.bot_memory_goal_time = undefined;
}
}
if ( !has_script_goal || (distSq < goalRadius * goalRadius) )
{
set_random_path = self bot_random_path();
// Sometimes plant a trap if we have applicable gear
if ( set_random_path && (RandomFloat( 100 ) < 25) )
{
trap_item = self bot_get_ambush_trap_item( "trap_directional", "trap" );
if ( IsDefined( trap_item ) )
{
ambush_point = self BotGetScriptGoal();
if ( IsDefined( ambush_point ) )
{
ambush_node = GetClosestNodeInSight( ambush_point );
if ( IsDefined( ambush_node ) )
{
ambush_entrances = self bot_queued_process( "bot_find_ambush_entrances", ::bot_find_ambush_entrances, ambush_node, false );
set_trap = self bot_set_ambush_trap( trap_item, ambush_entrances, ambush_node );
if( !IsDefined( set_trap ) || set_trap )
{
self BotClearScriptGoal();
set_random_path = self bot_random_path();
}
}
}
}
}
if ( set_random_path )
{
self thread clear_script_goal_on( "enemy", "bad_path", "goal", "node_relinquished", "search_end" );
}
}
}
}
//=======================================================
// clear_script_goal_on
//=======================================================
clear_script_goal_on( event1, event2, event3, event4, event5 )
{
self notify("clear_script_goal_on");
self endon ("clear_script_goal_on");
self endon( "death" );
self endon( "disconnect" );
self endon( "start_tactical_goal" );
goal_at_start = self BotGetScriptGoal();
keep_looping = true;
while( keep_looping )
{
result = self waittill_any_return( event1, event2, event3, event4, event5, "script_goal_changed" );
keep_looping = false;
should_clear_script_goal = true;
if ( result == "node_relinquished" || result == "goal" || result == "script_goal_changed" )
{
// For these notifies, we only clear the script goal if it was our original goal
if ( !self BotHasScriptGoal() )
{
should_clear_script_goal = false;
}
else
{
goal_at_end = self BotGetScriptGoal();
should_clear_script_goal = bot_vectors_are_equal(goal_at_start,goal_at_end);
}
}
if ( result == "enemy" && IsDefined(self.enemy) )
{
// Switched from one enemy to another, so don't clear script goal
should_clear_script_goal = false;
keep_looping = true; // Continue to wait for a better notify
}
if ( should_clear_script_goal )
self BotClearScriptGoal();
}
}
//=======================================================
// watch_out_of_ammo
//=======================================================
watch_out_of_ammo()
{
self notify("watch_out_of_ammo");
self endon ("watch_out_of_ammo");
self endon( "death" );
self endon( "disconnect" );
while(!self bot_out_of_ammo())
wait(0.5);
self notify("out_of_ammo");
}
//=======================================================
// bot_add_ambush_time_delayed
//=======================================================
bot_add_ambush_time_delayed( endEvent, waitFor )
{
self notify( "bot_add_ambush_time_delayed" );
self endon( "bot_add_ambush_time_delayed" );
self endon( "death" );
self endon( "disconnect" );
if ( IsDefined( endEvent ) )
self endon( endEvent );
self endon( "node_relinquished" );
self endon( "bad_path" );
// Add in how long we waited to the self.ambush_end time
startTime = GetTime();
if ( IsDefined( waitFor ) )
self waittill( waitFor );
if ( IsDefined( self.ambush_end ) && IsDefined( self.node_ambushing_from ) )
{
self.ambush_end += GetTime() - startTime;
self.node_ambushing_from.bot_ambush_end = self.ambush_end + 10000;
}
self notify( "bot_add_ambush_time_delayed" );
}
//=======================================================
// bot_watch_entrances_delayed
//=======================================================
bot_watch_entrances_delayed( endEvent, waitFor, entrances, yaw )
{
self notify( "bot_watch_entrances_delayed" );
if ( entrances.size > 0 )
{
self endon( "bot_watch_entrances_delayed" );
self endon( "death" );
self endon( "disconnect" );
self endon( endEvent );
self endon( "node_relinquished" );
self endon( "bad_path" );
if ( IsDefined( waitFor ) )
self waittill( waitFor );
self endon("path_enemy");
self childthread bot_watch_nodes( entrances, yaw, 0, self.ambush_end );
self childthread bot_monitor_watch_entrances_camp();
}
}
bot_monitor_watch_entrances_camp()
{
self notify( "bot_monitor_watch_entrances_camp" );
self endon( "bot_monitor_watch_entrances_camp" );
self notify( "bot_monitor_watch_entrances" );
self endon( "bot_monitor_watch_entrances" );
self endon( "disconnect" );
self endon( "death" );
while(!IsDefined(self.watch_nodes))
wait(0.05);
while( IsDefined( self.watch_nodes ) )
{
foreach( node in self.watch_nodes )
node.watch_node_chance[self.entity_number] = 1.0;
prioritize_watch_nodes_toward_enemies(0.5);
wait(RandomFloatRange(0.5,0.75));
}
}
//=======================================================
// bot_find_ambush_entrances
//=======================================================
bot_find_ambush_entrances( ambush_node, to_be_occupied )
{
self endon("disconnect");
// Get entrances and filter out any nodes that are too close or don't have exposure to the node in the crouching stance
useEntrances = [];
entrances = FindEntrances( ambush_node.origin );
AssertEx( entrances.size > 0, "Entrance points for node at location " + ambush_node.origin + " could not be calculated. Check pathgrid around that area" );
if ( IsDefined( entrances ) && entrances.size > 0 )
{
wait(0.05);
crouching = ( ambush_node.type != "Cover Stand" && ambush_node.type != "Conceal Stand" );
if ( crouching && to_be_occupied )
entrances = self BotNodeScoreMultiple( entrances, "node_exposure_vis", ambush_node.origin, "crouch" );
foreach ( node in entrances )
{
if ( DistanceSquared( self.origin, node.origin ) < (300 * 300) )
continue;
if ( crouching && to_be_occupied )
{
wait 0.05;
if ( !entrance_visible_from( node.origin, ambush_node.origin, "crouch" ) )
continue;
}
useEntrances[useEntrances.size] = node;
}
}
return useEntrances;
}
//=======================================================
// bot_filter_ambush_inuse
//=======================================================
bot_filter_ambush_inuse( nodes )
{
Assert(IsDefined(nodes));
resultNodes = [];
now = GetTime();
nodesSize = nodes.size;
for( i = 0; i < nodesSize; i++ )
{
node = nodes[i];
if ( !IsDefined( node.bot_ambush_end ) || (now > node.bot_ambush_end) )
resultNodes[resultNodes.size] = node;
}
return resultNodes;
}
//=======================================================
// bot_filter_ambush_vicinity
//=======================================================
bot_filter_ambush_vicinity( nodes, bot, radius )
{
resultNodes = [];
checkPoints = [];
radiusSq = (radius * radius);
if ( level.teamBased )
{
foreach( player in level.participants )
{
if ( !isReallyAlive( player ) )
continue;
if ( !IsDefined( player.team ) )
continue;
if ( (player.team == bot.team) && (player != bot) && IsDefined( player.node_ambushing_from ) )
checkPoints[checkPoints.size] = player.node_ambushing_from.origin;
}
}
checkpointsSize = checkPoints.size;
nodesSize = nodes.size;
for( i = 0; i < nodesSize; i++ )
{
tooClose = false;
node = nodes[i];
for ( j = 0; !tooClose && j < checkpointsSize; j++ )
{
distSq = DistanceSquared( checkPoints[j], node.origin );
tooClose = ( distSq < radiusSq );
}
if ( !tooClose )
resultNodes[resultNodes.size] = node;
}
return resultNodes;
}
//=======================================================
// clear_camper_data
//=======================================================
clear_camper_data()
{
self notify( "clear_camper_data" );
if ( IsDefined( self.node_ambushing_from ) && IsDefined( self.node_ambushing_from.bot_ambush_end ) )
self.node_ambushing_from.bot_ambush_end = undefined;
self.node_ambushing_from = undefined;
self.point_to_ambush = undefined;
self.ambush_yaw = undefined;
self.ambush_entrances = undefined;
self.ambush_duration = RandomIntRange( 20000, 30000 );
self.ambush_end = -1;
}
//=======================================================
// should_select_new_ambush_point
//======================================================
should_select_new_ambush_point()
{
if ( self bot_has_tactical_goal() )
return false;
if ( GetTime() > self.ambush_end )
return true;
if ( !self BotHasScriptGoal() )
return true;
return false;
}
//=======================================================
// find_camp_node
//=======================================================
find_camp_node( )
{
self notify( "find_camp_node" );
self endon( "find_camp_node" );
return self bot_queued_process( "find_camp_node_worker", ::find_camp_node_worker );
}
//=======================================================
// find_camp_node_worker
//=======================================================
find_camp_node_worker( )
{
self notify( "find_camp_node_worker" );
self endon( "find_camp_node_worker" );
self endon( "death" );
self endon( "disconnect" );
level endon( "game_ended" );
self clear_camper_data();
if ( level.zoneCount <= 0 )
return false;
myZone = GetZoneNearest( self.origin );
targetZone = undefined;
nextZone = undefined;
faceAngles = self.angles;
if ( IsDefined( myZone ) )
{
// Get nearest zone with predicted enemies but no allies
zoneEnemies = BotZoneNearestCount( myZone, self.team, -1, "enemy_predict", ">", 0, "ally", "<", 1 );
// Fallback to just nearest zone with enemies if that came back empty
if ( !IsDefined( zoneEnemies ) )
zoneEnemies = BotZoneNearestCount( myZone, self.team, -1, "enemy_predict", ">", 0 );
// If we have no idea where enemies are then pick the zone furthest from me
if ( !IsDefined( zoneEnemies ) )
{
furthestDist = -1;
furthestZone = -1;
for ( z = 0; z < level.zoneCount; z++ )
{
dist = Distance2DSquared( GetZoneOrigin( z ), self.origin );
if ( dist > furthestDist )
{
furthestDist = dist;
furthestZone = z;
}
}
Assert(furthestZone >= 0);
zoneEnemies = furthestZone;
}
Assert( IsDefined( zoneEnemies ) );
zonePath = GetZonePath( myZone, zoneEnemies );
if ( IsDefined( zonePath ) && (zonePath.size > 0) )
{
index = 0;
// Pick a point along the path adjacent to predicted enemies but no further than the midpoint
while ( index <= int(zonePath.size / 2) )
{
targetZone = zonePath[index];
nextZone = zonePath[int(min(index+1, zonePath.size - 1))];
if ( BotZoneGetCount( nextZone, self.team, "enemy_predict" ) != 0 )
break;
index++;
}
if ( IsDefined( targetZone ) && IsDefined( nextZone ) && targetZone != nextZone )
{
faceAngles = GetZoneOrigin( nextZone ) - GetZoneOrigin( targetZone );
faceAngles = VectorToAngles( faceAngles );
}
}
}
node_to_camp = undefined;
if ( IsDefined( targetZone ) )
{
keep_searching = true;
zone_steps = 1;
use_lenient_flag = false;
while( keep_searching )
{
// get set of nodes in the region we want to camp
nodes_to_select_from = GetZoneNodesByDist( targetZone, 800 * zone_steps, true );
if ( nodes_to_select_from.size > 1024 )
nodes_to_select_from = GetZoneNodes( targetZone, 0 );
wait 0.05;
// get direction we want to face in that region
randomRoll = RandomInt( 100 );
if ( randomRoll < 66 && randomRoll >= 33 )
faceAngles = (faceAngles[0], faceAngles[1] + 45, 0);
else if ( randomRoll < 33 )
faceAngles = (faceAngles[0], faceAngles[1] - 45, 0);
// Choose from only the BEST camp spots from within those nodes facing that direction
if ( nodes_to_select_from.size > 0 )
{
// Only want to pick from the best of the best
selectCount = int( min( max( 1, nodes_to_select_from.size * 0.15 ), 5 ) );
if ( use_lenient_flag )
nodes_to_select_from = self BotNodePickMultiple( nodes_to_select_from, selectCount, selectCount, "node_camp", AnglesToForward( faceAngles ), "lenient" );
else
nodes_to_select_from = self BotNodePickMultiple( nodes_to_select_from, selectCount, selectCount, "node_camp", AnglesToForward( faceAngles ) );
nodes_to_select_from = bot_filter_ambush_inuse( nodes_to_select_from );
if ( !IsDefined(self.can_camp_near_others) || !self.can_camp_near_others )
{
vicinity_radius = 800;
nodes_to_select_from = bot_filter_ambush_vicinity( nodes_to_select_from, self, vicinity_radius );
}
if ( nodes_to_select_from.size > 0 )
node_to_camp = random_weight_sorted( nodes_to_select_from );
}
if ( IsDefined(node_to_camp) )
{
keep_searching = false;
}
else
{
if ( IsDefined(self.camping_needs_fallback_camp_location) )
{
if ( zone_steps == 1 && !use_lenient_flag )
{
// First try 3 steps away instead of 1
zone_steps = 3;
}
else if ( zone_steps == 3 && !use_lenient_flag )
{
// 3 steps failed, so try using the lenient flag
use_lenient_flag = true;
}
else if ( zone_steps == 3 && use_lenient_flag )
{
// 3 zone steps AND the lenient flag didn't do it, so just bail
keep_searching = false;
}
}
else
{
keep_searching = false;
}
}
if ( keep_searching )
wait 0.05;
}
}
if ( !IsDefined( node_to_camp ) || !self BotNodeAvailable( node_to_camp ) )
return false;
self.node_ambushing_from = node_to_camp;
self.ambush_end = GetTime() + self.ambush_duration;
self.node_ambushing_from.bot_ambush_end = self.ambush_end;
self.ambush_yaw = faceAngles[1];
return true;
}
//=======================================================
// find_ambush_node
//=======================================================
find_ambush_node( optional_point_to_ambush, optional_ambush_radius )
{
self clear_camper_data();
if ( IsDefined(optional_point_to_ambush) )
{
self.point_to_ambush = optional_point_to_ambush;
}
else
{
// get all the high traffic nodes near the bot
node_to_ambush = undefined;
nodes_around_bot = GetNodesInRadius( self.origin, 5000, 0, 2000 );
if ( nodes_around_bot.size > 0 )
{
node_to_ambush = self BotNodePick( nodes_around_bot, nodes_around_bot.size * 0.25, "node_traffic" );
}
if ( IsDefined( node_to_ambush ) )
{
self.point_to_ambush = node_to_ambush.origin;
}
else
{
return false;
}
}
ambush_radius = 2000;
if ( IsDefined(optional_ambush_radius) )
ambush_radius = optional_ambush_radius;
nodes_around_ambush_point = GetNodesInRadius( self.point_to_ambush, ambush_radius, 0, 1000 );
ambush_node_trying = undefined;
Assert(IsDefined(nodes_around_ambush_point));
if ( nodes_around_ambush_point.size > 0 )
{
selectCount = int(max( 1, int( nodes_around_ambush_point.size * 0.15 ) ));
nodes_around_ambush_point = self BotNodePickMultiple( nodes_around_ambush_point, selectCount, selectCount, "node_ambush", self.point_to_ambush );
}
Assert(IsDefined(nodes_around_ambush_point));
nodes_around_ambush_point = bot_filter_ambush_inuse( nodes_around_ambush_point );
if ( nodes_around_ambush_point.size > 0 )
ambush_node_trying = random_weight_sorted( nodes_around_ambush_point );
if( !IsDefined( ambush_node_trying ) || !self BotNodeAvailable( ambush_node_trying ) )
return false;
self.node_ambushing_from = ambush_node_trying;
self.ambush_end = GetTime() + self.ambush_duration;
self.node_ambushing_from.bot_ambush_end = self.ambush_end;
node_to_ambush_point = VectorNormalize( self.point_to_ambush - self.node_ambushing_from.origin );
node_to_ambush_point_angles = VectorToAngles (node_to_ambush_point );
self.ambush_yaw = node_to_ambush_point_angles[1];
return true;
}
bot_random_path()
{
if ( self bot_is_remote_or_linked() )
return false;
random_path_func = level.bot_random_path_function[self.team];
return self [[random_path_func]]();
}
//=======================================================
// bot_random_path_default
//=======================================================
bot_random_path_default()
{
result = false;
chance_to_seek_out_killer = 50;
if ( self.personality == "camper" )
{
chance_to_seek_out_killer = 0;
}
goalPos = undefined;
if ( RandomInt(100) < chance_to_seek_out_killer )
{
goalPos = bot_recent_point_of_interest();
}
if ( !IsDefined( goalPos ) )
{
// Find a random place to go
randomNode = self BotFindNodeRandom();
if ( IsDefined( randomNode ) )
{
goalPos = randomNode.origin;
}
}
if ( IsDefined( goalPos ) )
{
result = self BotSetScriptGoal( goalPos, 128, "hunt" );
}
return result;
}
//=======================================================
// bot_setup_callback_class
//=======================================================
bot_setup_callback_class()
{
if ( self bot_setup_loadout_callback() )
return "callback";
else
return "class0";
}