361 lines
9.5 KiB
Plaintext
361 lines
9.5 KiB
Plaintext
#include common_scripts\utility;
|
|
#include maps\mp\bots\_bots_strategy;
|
|
#include maps\mp\bots\_bots_ks;
|
|
#include maps\mp\bots\_bots_util;
|
|
|
|
//========================================================
|
|
// bot_killstreak_sentry
|
|
//========================================================
|
|
bot_killstreak_sentry( killstreak_info, killstreaks_array, can_use, targetType )
|
|
{
|
|
self endon( "bot_sentry_exited" );
|
|
self endon( "death" );
|
|
self endon( "disconnect" );
|
|
level endon( "game_ended" );
|
|
|
|
wait( RandomIntRange( 3, 5 ) );
|
|
|
|
while ( IsDefined( self.sentry_place_delay ) && GetTime() < self.sentry_place_delay )
|
|
{
|
|
wait 1;
|
|
}
|
|
|
|
if ( IsDefined( self.enemy ) && (self.enemy.health > 0) && (self BotCanSeeEntity( self.enemy )) )
|
|
return true;
|
|
|
|
targetPoint = self.origin;
|
|
if ( targetType != "hide_nonlethal" )
|
|
{
|
|
// Choose sentry targeting position
|
|
targetPoint = bot_sentry_choose_target( targetType );
|
|
|
|
if ( !IsDefined( targetPoint ) )
|
|
return true;
|
|
}
|
|
|
|
self bot_sentry_add_goal( killstreak_info, targetPoint, targetType, killstreaks_array );
|
|
|
|
while( self bot_has_tactical_goal("sentry_placement") )
|
|
{
|
|
wait(0.5); // Stay in this thread until the sentry gun is placed, otherwise bots might pick another killstreak instead
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bot_sentry_add_goal( killstreak_info, targetOrigin, targetType, killstreaks_array )
|
|
{
|
|
placement = self bot_sentry_choose_placement( killstreak_info, targetOrigin, targetType, killstreaks_array );
|
|
|
|
if ( IsDefined( placement ) )
|
|
{
|
|
self bot_abort_tactical_goal( "sentry_placement" );
|
|
|
|
extra_params = SpawnStruct();
|
|
extra_params.object = placement;
|
|
extra_params.script_goal_yaw = placement.yaw;
|
|
extra_params.script_goal_radius = 10;
|
|
extra_params.start_thread = ::bot_sentry_path_start;
|
|
extra_params.end_thread = ::bot_sentry_cancel;
|
|
extra_params.should_abort = ::bot_sentry_should_abort;
|
|
extra_params.action_thread = ::bot_sentry_activate;
|
|
|
|
self.placingItemStreakName = killstreak_info.streakName;
|
|
|
|
self bot_new_tactical_goal( "sentry_placement", placement.node.origin, 0, extra_params );
|
|
}
|
|
}
|
|
|
|
bot_sentry_should_abort( tactical_goal )
|
|
{
|
|
self endon( "death" );
|
|
self endon( "disconnect" );
|
|
level endon( "game_ended" );
|
|
|
|
if ( IsDefined( self.enemy ) && (self.enemy.health > 0) && (self BotCanSeeEntity( self.enemy )) )
|
|
return true;
|
|
|
|
// As long as we are actively doing a sentry placement, dont start a new one
|
|
self.sentry_place_delay = GetTime() + 1000;
|
|
|
|
return false;
|
|
}
|
|
|
|
bot_sentry_cancel_failsafe( )
|
|
{
|
|
self endon( "death" );
|
|
self endon( "disconnect" );
|
|
self endon( "bot_sentry_canceled" );
|
|
self endon( "bot_sentry_ensure_exit" );
|
|
|
|
level endon( "game_ended" );
|
|
|
|
while ( 1 )
|
|
{
|
|
if ( IsDefined( self.enemy ) && (self.enemy.health > 0) && (self BotCanSeeEntity( self.enemy )) )
|
|
self thread bot_sentry_cancel();
|
|
|
|
wait 0.05;
|
|
}
|
|
}
|
|
|
|
bot_sentry_path_start( tactical_goal )
|
|
{
|
|
self thread bot_sentry_path_thread( tactical_goal );
|
|
}
|
|
|
|
bot_sentry_path_thread( tactical_goal )
|
|
{
|
|
self endon( "stop_tactical_goal" );
|
|
self endon( "stop_goal_aborted_watch" );
|
|
self endon( "bot_sentry_canceled" );
|
|
self endon( "bot_sentry_exited" );
|
|
self endon( "death" );
|
|
self endon( "disconnect" );
|
|
|
|
level endon( "game_ended" );
|
|
|
|
// Switch to sentry when we are near goal
|
|
while ( IsDefined( tactical_goal.object ) && IsDefined( tactical_goal.object.weapon ) )
|
|
{
|
|
if ( Distance2D( self.origin, tactical_goal.object.node.origin ) < 400 )
|
|
{
|
|
self thread bot_force_stance_for_time( "stand", 5.0 );
|
|
self thread bot_sentry_cancel_failsafe();
|
|
self bot_switch_to_killstreak_weapon( tactical_goal.object.killstreak_info, tactical_goal.object.killstreaks_array, tactical_goal.object.weapon );
|
|
return;
|
|
}
|
|
wait 0.05;
|
|
}
|
|
}
|
|
|
|
bot_sentry_choose_target( targetType )
|
|
{
|
|
// Protect my current defending goal point if I have one
|
|
defend_center = self defend_valid_center();
|
|
if ( IsDefined(defend_center) )
|
|
return defend_center;
|
|
|
|
// Protect my current ambushing spot if I have one
|
|
if ( IsDefined( self.node_ambushing_from ) )
|
|
return self.node_ambushing_from.origin;
|
|
|
|
// Otherwise just return the highest traffic node around me
|
|
nodes = GetNodesInRadius( self.origin, 1000, 0, 512 );
|
|
nodes_to_select_from = 5;
|
|
if ( targetType != "turret" )
|
|
{
|
|
if ( self BotGetDifficultySetting("strategyLevel") == 1 )
|
|
nodes_to_select_from = 10;
|
|
else if ( self BotGetDifficultySetting("strategyLevel") == 0 )
|
|
nodes_to_select_from = 15;
|
|
}
|
|
|
|
if ( targetType == "turret_air" )
|
|
targetNode = self BotNodePick( nodes, nodes_to_select_from, "node_traffic", "ignore_no_sky" );
|
|
else
|
|
targetNode = self BotNodePick( nodes, nodes_to_select_from, "node_traffic" );
|
|
if ( IsDefined( targetNode ) )
|
|
return targetNode.origin;
|
|
}
|
|
|
|
bot_sentry_choose_placement( killstreak_info, targetOrigin, targetType, killstreaks_array )
|
|
{
|
|
placement = undefined;
|
|
|
|
nodes = GetNodesInRadius( targetOrigin, 1000, 0, 512 );
|
|
nodes_to_select_from = 5;
|
|
if ( targetType != "turret" )
|
|
{
|
|
if ( self BotGetDifficultySetting("strategyLevel") == 1 )
|
|
nodes_to_select_from = 10;
|
|
else if ( self BotGetDifficultySetting("strategyLevel") == 0 )
|
|
nodes_to_select_from = 15;
|
|
}
|
|
|
|
if ( targetType == "turret_air" )
|
|
placeNode = self BotNodePick( nodes, nodes_to_select_from, "node_sentry", targetOrigin, "ignore_no_sky" );
|
|
else if ( targetType == "trap" )
|
|
placeNode = self BotNodePick( nodes, nodes_to_select_from, "node_traffic" );
|
|
else if ( targetType == "hide_nonlethal" )
|
|
placeNode = self BotNodePick( nodes, nodes_to_select_from, "node_hide" );
|
|
else
|
|
placeNode = self BotNodePick( nodes, nodes_to_select_from, "node_sentry", targetOrigin );
|
|
|
|
if ( IsDefined( placeNode ) )
|
|
{
|
|
placement = SpawnStruct();
|
|
placement.node = placeNode;
|
|
if ( targetOrigin != placeNode.origin && targetType != "hide_nonlethal" )
|
|
placement.yaw = VectorToYaw(targetOrigin - placeNode.origin);
|
|
else
|
|
placement.yaw = undefined;
|
|
placement.weapon = killstreak_info.weapon;
|
|
placement.killstreak_info = killstreak_info;
|
|
placement.killstreaks_array = killstreaks_array;
|
|
}
|
|
|
|
return placement;
|
|
}
|
|
|
|
bot_sentry_carried_obj() // self = bot
|
|
{
|
|
if ( IsDefined( self.carriedsentry ) )
|
|
return self.carriedsentry;
|
|
|
|
if ( IsDefined( self.carriedIMS ) )
|
|
return self.carriedIMS;
|
|
|
|
if ( IsDefined( self.carriedItem ) )
|
|
return self.carriedItem;
|
|
}
|
|
|
|
bot_sentry_activate( tactical_goal )
|
|
{
|
|
result = false;
|
|
|
|
carried_obj = self bot_sentry_carried_obj();
|
|
|
|
// Place the sentry if it can be placed here, otherwise cancel out of carrying it around
|
|
if ( IsDefined( carried_obj ) )
|
|
{
|
|
abort = false;
|
|
|
|
if ( !carried_obj.canBePlaced )
|
|
{
|
|
// Bot cannot currently place the turret, move away from obstruction
|
|
time_to_try = 0.75;
|
|
start_time = GetTime();
|
|
|
|
placementYaw = self.angles[1];
|
|
if ( IsDefined( tactical_goal.object.yaw ) )
|
|
placementYaw = tactical_goal.object.yaw;
|
|
|
|
moveYaws = [];
|
|
moveYaws[0] = placementYaw + 180;
|
|
moveYaws[1] = placementYaw + 135;
|
|
moveYaws[2] = placementYaw - 135;
|
|
|
|
minDist = 1000;
|
|
foreach ( moveYaw in moveYaws )
|
|
{
|
|
hitPos = PlayerPhysicsTrace( tactical_goal.object.node.origin, tactical_goal.object.node.origin + AnglesToForward( (0, moveYaw + 180, 0) ) * 100 );
|
|
dist = Distance2D( hitpos, tactical_goal.object.node.origin );
|
|
if ( dist < minDist )
|
|
{
|
|
minDist = dist;
|
|
self BotSetScriptMove( moveYaw, time_to_try );
|
|
self BotLookAtPoint( tactical_goal.object.node.origin, time_to_try, "script_forced" );
|
|
}
|
|
}
|
|
|
|
while( !abort && IsDefined( carried_obj ) && !carried_obj.canBePlaced )
|
|
{
|
|
time_waited = float(GetTime() - start_time) / 1000.0;
|
|
if ( !carried_obj.canBePlaced && (time_waited > time_to_try) )
|
|
{
|
|
abort = true;
|
|
|
|
// wait a while before attempting another sentry placement
|
|
self.sentry_place_delay = GetTime() + 30000;
|
|
}
|
|
|
|
wait 0.05;
|
|
}
|
|
}
|
|
|
|
if ( IsDefined( carried_obj ) && carried_obj.canBePlaced )
|
|
{
|
|
self bot_send_place_notify();
|
|
result = true;
|
|
}
|
|
}
|
|
|
|
wait 0.25;
|
|
self bot_sentry_ensure_exit();
|
|
|
|
return result;
|
|
}
|
|
|
|
bot_send_place_notify()
|
|
{
|
|
self notify( "place_sentry" );
|
|
self notify( "place_ims" );
|
|
self notify( "placePlaceable" );
|
|
}
|
|
|
|
bot_send_cancel_notify()
|
|
{
|
|
self SwitchToWeapon( "none" );
|
|
self enableWeapons();
|
|
self enableWeaponSwitch();
|
|
self notify( "cancel_sentry" );
|
|
self notify( "cancel_ims" );
|
|
self notify( "cancelPlaceable" );
|
|
}
|
|
|
|
bot_sentry_cancel( tactical_goal )
|
|
{
|
|
// Cancel the sentry
|
|
self notify( "bot_sentry_canceled" );
|
|
|
|
self bot_send_cancel_notify();
|
|
|
|
self bot_sentry_ensure_exit();
|
|
}
|
|
|
|
bot_sentry_ensure_exit()
|
|
{
|
|
self notify( "bot_sentry_abort_goal_think" );
|
|
self notify( "bot_sentry_ensure_exit" );
|
|
|
|
self endon( "bot_sentry_ensure_exit" );
|
|
self endon( "death" );
|
|
self endon( "disconnect" );
|
|
level endon( "game_ended" );
|
|
|
|
self SwitchToWeapon( "none" );
|
|
self BotClearScriptGoal();
|
|
self BotSetStance( "none" );
|
|
self enableWeapons();
|
|
self enableWeaponSwitch();
|
|
wait 0.25;
|
|
|
|
attempts = 0;
|
|
while ( IsDefined( self bot_sentry_carried_obj() ) )
|
|
{
|
|
attempts++;
|
|
|
|
self bot_send_cancel_notify();
|
|
wait 0.25;
|
|
|
|
if ( attempts > 2 )
|
|
self bot_sentry_force_cancel();
|
|
}
|
|
|
|
self notify( "bot_sentry_exited" );
|
|
}
|
|
|
|
bot_sentry_force_cancel()
|
|
{
|
|
if ( IsDefined( self.carriedsentry ) )
|
|
self.carriedsentry maps\mp\killstreaks\_autosentry::sentry_setCancelled();
|
|
|
|
if ( IsDefined( self.carriedIMS ) )
|
|
self.carriedIMS maps\mp\killstreaks\_ims::ims_setCancelled();
|
|
|
|
if ( IsDefined( self.carriedItem ) )
|
|
self.carriedItem maps\mp\killstreaks\_placeable::onCancel( self.placingItemStreakName, false );
|
|
|
|
self.carriedsentry = undefined;
|
|
self.carriedIMS = undefined;
|
|
self.carriedItem = undefined;
|
|
|
|
self SwitchToWeapon( "none" );
|
|
self enableWeapons();
|
|
self enableWeaponSwitch();
|
|
}
|
|
|
|
|
|
|