2015-06-26 21:02:55 +00:00
//------------------------------------------------------------------------------------------
// main.cs
// Main source file for the DXAI experimental AI enhancement project.
// https://github.com/Ragora/T2-DXAI.git
//
2015-10-10 20:08:43 +00:00
// Copyright (c) 2015 Robert MacGregor
2015-06-26 21:02:55 +00:00
// This software is licensed under the MIT license. Refer to LICENSE.txt for more information.
//------------------------------------------------------------------------------------------
2014-11-20 05:12:25 +00:00
2015-06-26 21:02:55 +00:00
exec ( " scripts/DXAI/objectives.cs " );
exec ( " scripts/DXAI/helpers.cs " );
exec ( " scripts/DXAI/config.cs " );
exec ( " scripts/DXAI/aicommander.cs " );
exec ( " scripts/DXAI/aiconnection.cs " );
2015-06-28 00:43:20 +00:00
exec ( " scripts/DXAI/priorityqueue.cs " );
exec ( " scripts/DXAI/cyclicset.cs " );
2015-10-11 07:02:19 +00:00
exec ( " scripts/DXAI/weaponProfiler.cs " );
2016-07-07 01:14:45 +00:00
exec ( " scripts/DXAI/loadouts.cs " );
2014-11-20 05:12:25 +00:00
2015-10-10 20:08:43 +00:00
//------------------------------------------------------------------------------------------
// Description: This cleanup function is called when the mission ends to clean up all
// active commanders and to delete them for the next mission which guarantees a blank
// slate.
//------------------------------------------------------------------------------------------
2014-11-20 05:12:25 +00:00
function DXAI :: cleanup ()
{
$DXAI :: System :: Setup = false ;
2016-07-07 01:14:45 +00:00
2014-11-20 05:12:25 +00:00
for ( % iteration = 1 ; % iteration < $DXAI :: ActiveCommanderCount + 1 ; % iteration ++ )
$DXAI :: ActiveCommander [ % iteration ] . delete ();
2016-07-07 01:14:45 +00:00
2014-11-20 05:12:25 +00:00
$DXAI :: ActiveCommanderCount = 0 ;
}
2015-10-10 20:08:43 +00:00
//------------------------------------------------------------------------------------------
// Description: This cleanup function is called when the mission starts to to instantiate
// and setup all AI commanders for the game.
// Param %numTeams: The number of teams to initialize for.
//
// TODO: Perhaps calculate %numTeams from the game object?
//------------------------------------------------------------------------------------------
2014-11-20 05:12:25 +00:00
function DXAI :: setup ( % numTeams )
{
// Mark the environment as invalidated for each new run so that our hooks
// can be verified
$DXAI :: System :: InvalidatedEnvironment = true ;
2016-07-07 01:14:45 +00:00
2014-11-20 05:12:25 +00:00
// Set our setup flag so that the execution hooks can behave correctly
$DXAI :: System :: Setup = true ;
2016-07-07 01:14:45 +00:00
2015-10-11 03:55:09 +00:00
// Create the AIGrenadeSet to hold known grenades.
new SimSet ( AIGrenadeSet );
2016-07-07 01:14:45 +00:00
2014-11-20 05:12:25 +00:00
for ( % iteration = 1 ; % iteration < % numTeams + 1 ; % iteration ++ )
{
% commander = new ScriptObject () { class = " AICommander " ; team = %iteration; };
% commander . setup ();
2016-07-07 01:14:45 +00:00
2014-11-20 05:12:25 +00:00
$DXAI :: ActiveCommander [ % iteration ] = % commander ;
2015-06-28 02:34:06 +00:00
% commander . loadObjectives ();
% commander . assignTasks ();
2014-11-20 05:12:25 +00:00
}
2016-07-07 01:14:45 +00:00
2014-11-20 05:12:25 +00:00
// And setup the default values
for ( % iteration = 0 ; % iteration < ClientGroup . getCount (); % iteration ++ )
{
% currentClient = ClientGroup . getObject ( % iteration );
2016-07-07 01:14:45 +00:00
2014-11-20 05:12:25 +00:00
% currentClient . viewDistance = $DXAI :: Bot :: DefaultViewDistance ;
% currentClient . fieldOfView = $DXAI :: Bot :: DefaultFieldOfView ;
}
2016-07-07 01:14:45 +00:00
2014-11-20 05:12:25 +00:00
$DXAI :: ActiveCommanderCount = % numTeams ;
}
2015-10-10 20:08:43 +00:00
//------------------------------------------------------------------------------------------
2016-07-07 01:14:45 +00:00
// Why: Due to the way the AI system must hook into some functions and the way game
2015-10-10 20:08:43 +00:00
// modes work, we must generate runtime overrides for some gamemode related functions. We
// can't simply hook DefaultGame functions base game modes will declare their own and so
// we'll need to hook those functions post-start as the game mode scripts are executed for
// each mission run.
// Description: This function is called once per update tick (roughly 32 milliseconds) to
// check that the hooks we need are actually active if the system detects that may be a
// necessity to do so. A runtime check is initiated at gamemode start and for each exec
// call made during runtime as any given exec can overwrite the hooks we required.
2016-07-07 01:14:45 +00:00
// If they were not overwritten, the function will return 11595 and do nothing else if the
2015-10-10 20:08:43 +00:00
// appropriate dummy parameters are passed in.
//
// TODO: Perhaps calculate %numTeams from the game object?
//------------------------------------------------------------------------------------------
2014-11-20 05:12:25 +00:00
function DXAI :: validateEnvironment ()
{
% gameModeName = $CurrentMissionType @ " Game " ;
2016-07-07 01:14:45 +00:00
% boundPayloadTemplate = " function " @ % gameModeName @ " ::<METHODNAME>() { return DefaultGame::<METHODNAME>( $DXAI ::System::RuntimeDummy); } " ;
% payloadTemplate = " function <METHODNAME>() { return <METHODNAME>( $DXAI ::System::RuntimeDummy); } " ;
2014-11-20 05:12:25 +00:00
if ( game . AIChooseGameObjective ( $DXAI :: System :: RuntimeDummy ) != 11595 )
{
error ( " DXAI: Function 'DefaultGame::AIChooseGameObjective' detected to be overwritten by the current gamemode. Correcting ... " );
2016-07-07 01:14:45 +00:00
eval ( strReplace ( % boundPayloadTemplate , " <METHODNAME> " , " AIChooseGameObjective " ));
2014-11-20 05:12:25 +00:00
// Make sure the patch took
if ( game . AIChooseGameObjective ( $DXAI :: System :: RuntimeDummy ) != 11595 )
error ( " DXAI: Failed to patch 'DefaultGame::AIChooseGameObjective'! DXAI may not function correctly. " );
}
2016-07-07 01:14:45 +00:00
if ( onAIRespawn ( $DXAI :: System :: RuntimeDummy ) != 11595 )
2014-11-20 05:12:25 +00:00
{
2016-07-07 01:14:45 +00:00
error ( " DXAI: Function 'onAIRespawn' detected to be overwritten by the current gamemode. Correcting ... " );
2015-06-25 01:03:57 +00:00
eval ( strReplace ( % payloadTemplate , " <METHODNAME> " , " onAIRespawn " ));
2016-07-07 01:14:45 +00:00
2014-11-20 05:12:25 +00:00
if ( game . onAIRespawn ( $DXAI :: System :: RuntimeDummy ) != 11595 )
2016-07-07 01:14:45 +00:00
error ( " DXAI: Failed to patch 'onAIRespawn'! DXAI may not function correctly. " );
2014-11-20 05:12:25 +00:00
}
2016-07-07 01:14:45 +00:00
2014-11-20 05:12:25 +00:00
$DXAI :: System :: InvalidatedEnvironment = false ;
}
2015-10-10 20:08:43 +00:00
//------------------------------------------------------------------------------------------
// Description: This update function is scheduled to be called roughly once every 32
// milliseconds which updates each active commander in the game as well as performs
// an environment validation if necessary.
//
// NOTE: This is called on its own scheduled tick, therefore it should not be called
// directly.
//------------------------------------------------------------------------------------------
2014-11-20 05:12:25 +00:00
function DXAI :: update ()
{
if ( isEventPending ( $DXAI :: updateHandle ))
cancel ( $DXAI :: updateHandle );
2016-07-07 01:14:45 +00:00
2015-06-28 00:43:20 +00:00
if ( ! isObject ( Game ))
return ;
2016-07-07 01:14:45 +00:00
2014-11-20 05:12:25 +00:00
// Check if the bound functions are overwritten by the current gamemode, or if something
// may have invalidated our hooks
if ( $DXAI :: System :: InvalidatedEnvironment && $DXAI :: System :: Setup )
DXAI :: validateEnvironment ();
2016-07-07 01:14:45 +00:00
2014-11-20 05:12:25 +00:00
for ( % iteration = 1 ; % iteration < $DXAI :: ActiveCommanderCount + 1 ; % iteration ++ )
$DXAI :: ActiveCommander [ % iteration ] . update ();
2016-07-07 01:14:45 +00:00
2014-11-20 05:12:25 +00:00
// Apparently we can't schedule a bound function otherwise
2015-06-28 02:34:06 +00:00
$DXAI :: updateHandle = schedule ( 32 , 0 , " eval " , " DXAI::update(); " );
2014-11-20 05:12:25 +00:00
}
function DXAI :: notifyPlayerDeath ( % killed , % killedBy )
{
for ( % iteration = 1 ; % iteration < $DXAI :: ActiveCommanderCount + 1 ; % iteration ++ )
$DXAI :: ActiveCommander [ % iteration ] . notifyPlayerDeath ( % killed , % killedBy );
}
2016-07-07 01:14:45 +00:00
function DXAI :: notifyFlagGrab ( % grabbedBy , % flagTeam )
{
$DXAI :: ActiveCommander [ % flagTeam ] . notifyFlagGrab ( % grabbedBy );
}
2015-10-10 20:08:43 +00:00
//------------------------------------------------------------------------------------------
// Description: There is a series of functions that the AI code can safely hook without
// worry of being overwritten implicitly such as the disconnect or exec functions. For
// those that can be, there is an environment validation that is performed to ensure that
// the necessary code will be called in response to the events we need to know about in
// this AI system.
//------------------------------------------------------------------------------------------
2014-11-20 05:12:25 +00:00
package DXAI_Hooks
{
2015-10-10 20:08:43 +00:00
//------------------------------------------------------------------------------------------
// Description: Called when the mission ends. We use this to perform any necessary cleanup
// operations between games.
//------------------------------------------------------------------------------------------
2014-11-20 05:12:25 +00:00
function DefaultGame :: gameOver ( % game )
{
parent :: gameOver ( % game );
2016-07-07 01:14:45 +00:00
2014-11-20 05:12:25 +00:00
DXAI :: cleanup ();
}
2016-07-07 01:14:45 +00:00
2015-10-10 20:08:43 +00:00
//------------------------------------------------------------------------------------------
// Description: Called when the mission starts. We use this to perform initialization and
// to start the update ticks.
//------------------------------------------------------------------------------------------
2014-11-20 05:12:25 +00:00
function DefaultGame :: startMatch ( % game )
{
parent :: startMatch ( % game );
2016-07-07 01:14:45 +00:00
2014-11-20 05:12:25 +00:00
DXAI :: setup ( % game . numTeams );
DXAI :: update ();
}
2016-07-07 01:14:45 +00:00
2015-10-10 20:08:43 +00:00
//------------------------------------------------------------------------------------------
// Description: We hook the disconnect function as a step to fix console spam from leaving
// a listen server due to the AI code continuing to run post-server shutdown in those
// cases.
//------------------------------------------------------------------------------------------
2015-06-25 01:03:57 +00:00
function disconnect ()
{
parent :: disconnect ();
2016-07-07 01:14:45 +00:00
2015-10-10 20:08:43 +00:00
DXAI :: cleanup ();
2015-06-25 01:03:57 +00:00
}
2016-07-07 01:14:45 +00:00
2015-10-10 20:08:43 +00:00
//------------------------------------------------------------------------------------------
// Description: In the game, bots can be made to change teams which means we need to hook
// this event so that commander affiliations can be properly updated.
//------------------------------------------------------------------------------------------
2014-11-20 05:12:25 +00:00
function DefaultGame :: AIChangeTeam ( % game , % client , % newTeam )
{
// Remove us from the old commander's control first
$DXAI :: ActiveCommander [ % client . team ] . removeBot ( % client );
2016-07-07 01:14:45 +00:00
2014-11-20 05:12:25 +00:00
parent :: AIChangeTeam ( % game , % client , % newTeam );
2016-07-07 01:14:45 +00:00
2014-11-20 05:12:25 +00:00
$DXAI :: ActiveCommander [ % newTeam ] . addBot ( % client );
}
2016-07-07 01:14:45 +00:00
2015-10-10 20:08:43 +00:00
//------------------------------------------------------------------------------------------
// Description: In the game, bots can be kicked like regular players so we hook this to
// ensure that commanders are properly notified of lesser bot counts.
//------------------------------------------------------------------------------------------
2014-11-20 05:12:25 +00:00
function AIConnection :: onAIDrop ( % client )
{
if ( isObject ( % client . commander ))
% client . commander . removeBot ( % client );
2016-07-07 01:14:45 +00:00
2014-11-20 05:12:25 +00:00
parent :: onAIDrop ( % client );
}
2016-07-07 01:14:45 +00:00
2014-11-20 05:12:25 +00:00
// Hooks for AI System notification
function DefaultGame :: onClientKilled ( % game , % clVictim , % clKiller , % damageType , % implement , % damageLocation )
{
parent :: onClientKilled ( % game , % clVictim , % clKiller , % damageType , % implement , % damageLocation );
2016-07-07 01:14:45 +00:00
2014-11-20 05:12:25 +00:00
DXAI :: notifyPlayerDeath ( % clVictim , % clKiller );
}
2016-07-07 01:14:45 +00:00
2014-11-20 05:12:25 +00:00
function DefaultGame :: onAIKilled ( % game , % clVictim , % clKiller , % damageType , % implement )
{
parent :: onAIKilled ( % game , % clVictim , % clKiller , % damageType , % implement );
2016-07-07 01:14:45 +00:00
2014-11-20 05:12:25 +00:00
DXAI :: notifyPlayerDeath ( % clVictim , % clKiller );
}
2016-07-07 01:14:45 +00:00
2015-10-10 20:08:43 +00:00
//------------------------------------------------------------------------------------------
// Description: We hook this function to implement some basic sound simulation for bots.
// This means that if something explodes, a bot will hear it and if the sound is close
// enough, they will shimmy away from the source using setDangerLocation.
//------------------------------------------------------------------------------------------
2014-11-20 05:12:25 +00:00
function ProjectileData :: onExplode ( % data , % proj , % pos , % mod )
{
parent :: onExplode ( % data , % proj , % pos , % mod );
2016-07-07 01:14:45 +00:00
2014-11-20 05:12:25 +00:00
// Look for any bots nearby
2015-06-25 01:03:57 +00:00
InitContainerRadiusSearch ( % pos , 100 , $TypeMasks :: PlayerObjectType );
2016-07-07 01:14:45 +00:00
2014-11-20 05:12:25 +00:00
while (( % targetObject = containerSearchNext ()) != 0 )
{
% currentDistance = containerSearchCurrRadDamageDist ();
2016-07-07 01:14:45 +00:00
2015-06-25 01:03:57 +00:00
if ( % currentDistance > 100 || !% targetObject . client . isAIControlled ())
2014-11-20 05:12:25 +00:00
continue ;
2016-07-07 01:14:45 +00:00
2014-11-20 05:12:25 +00:00
// Get the projectile team
% projectileTeam = - 1 ;
if ( isObject ( % proj . sourceObject ))
% projectileTeam = % proj . sourceObject . client . team ;
2016-07-07 01:14:45 +00:00
2014-11-20 05:12:25 +00:00
// Determine if we should run based on team & Team damage
% shouldRun = false ;
if ( isObject ( % proj . sourceObject ) && % projectileTeam == % targetObject . client . team && $TeamDamage )
% shouldRun = true ;
else if ( isObject ( % proj . sourceObject ) && % projectileTeam != % targetObject . client . team )
% shouldRun = true ;
2016-07-07 01:14:45 +00:00
2014-11-20 05:12:25 +00:00
// Determine if we 'heard' it. The sound distance seems to be roughly 55m or less and we check the maxDistance
// IIRC The 55m distance should scale with the min/max distances and volume but I'm not sure how those interact
% heardHit = false ;
% hitDistance = vectorDist ( % targetObject . getWorldBoxCenter (), % pos );
2016-07-07 01:14:45 +00:00
2015-06-25 01:03:57 +00:00
if ( % hitDistance <= 20 && % hitDistance <= % data . explosion . soundProfile . description . maxDistance )
2014-11-20 05:12:25 +00:00
% heardHit = true ;
2016-07-07 01:14:45 +00:00
2015-10-05 09:03:45 +00:00
// If the thing has any radius damage (and we heard it), run around a little bit if we need to, and look at it for a bit
2015-06-25 01:03:57 +00:00
if ( % data . indirectDamage != 0 && % heardHit )
2015-10-05 09:03:45 +00:00
{
2015-06-25 01:03:57 +00:00
% targetObject . client . schedule ( getRandom ( 250 , 400 ), " setDangerLocation " , % pos , 20 );
2015-10-05 09:03:45 +00:00
// TODO: Perhaps attempt to discern the direction of fire?
% targetObject . client . aimAt ( % pos );
}
2016-07-07 01:14:45 +00:00
2014-11-20 05:12:25 +00:00
// If we should care and it wasn't a teammate projectile, notify
if ( % shouldRun && % projectileTeam != % targetObject . client . team )
% targetObject . client . notifyProjectileImpact ( % data , % proj , % pos );
}
}
2016-07-07 01:14:45 +00:00
2015-10-10 20:08:43 +00:00
//------------------------------------------------------------------------------------------
2016-07-07 01:14:45 +00:00
// Description: This function is hooked so that we can try and guarantee that the DXAI
2015-10-10 20:08:43 +00:00
// gamemode hooks still exist in the runtime as game mode scripts are executed for each
// mission load.
//------------------------------------------------------------------------------------------
2015-06-26 17:31:55 +00:00
function CreateServer ( % mission , % missionType )
2016-07-07 01:14:45 +00:00
{
2015-06-26 17:31:55 +00:00
// Perform the default exec's
parent :: CreateServer ( % mission , % missionType );
2016-07-07 01:14:45 +00:00
2015-06-26 17:31:55 +00:00
// Ensure that the DXAI is active.
2015-06-26 21:02:55 +00:00
DXAI :: validateEnvironment ();
2016-07-07 01:14:45 +00:00
2015-10-11 07:02:19 +00:00
// Run our profiler here as well.
WeaponProfiler :: run ( false );
2015-06-26 17:31:55 +00:00
}
2015-10-11 03:55:09 +00:00
//------------------------------------------------------------------------------------------
// Description: The AIGrenadeThrown function is called to notify the AI code that a
// grenade has been added to the game sim which is how bots will evade any grenade that
// is merely within range of them. However, this is not the behavior we want. We want the
// bots to actually see the grenade before responding to it.
//------------------------------------------------------------------------------------------
function AIGrenadeThrown ( % projectile )
{
AIGrenadeSet . add ( % projectile );
}
2016-07-07 01:14:45 +00:00
2014-11-20 05:12:25 +00:00
// Make this do nothing so the bots don't ever get any objectives by default
function DefaultGame :: AIChooseGameObjective ( % game , % client ) { return 11595 ; }
2016-07-07 01:14:45 +00:00
function onAIRespawn ( % client )
2014-11-20 05:12:25 +00:00
{
2016-07-07 01:14:45 +00:00
if ( % client != $DXAI :: System :: RuntimeDummy )
parent :: onAIRespawn ( % client );
// Clear the tasks and assign the default tasks
// FIXME: Assign tasks on a per-gamemode basis correctly
% client . clearTasks ();
% client . addTask ( AIEnhancedEngageTarget );
% client . addTask ( AIEnhancedRearmTask );
% client . addTask ( AIEnhancedPathCorrectionTask );
% client . addTask ( AIEnhancedReturnFlagTask );
% client . addTask ( AIEnhancedFlagCaptureTask );
% client . addTask ( % client . primaryTask );
% client . hasFlag = false ;
2015-10-05 09:03:45 +00:00
% client . shouldRearm = true ;
2015-10-07 07:16:32 +00:00
% client . engageTargetLastPosition = " " ;
% client . engageTarget = - 1 ;
2016-07-07 01:14:45 +00:00
2014-11-20 05:12:25 +00:00
return 11595 ;
}
2016-07-07 01:14:45 +00:00
2014-11-20 05:12:25 +00:00
// We package hook the exec() and compile() functions to perform execution environment
// checking because these can easily load code that overwrites methods that are otherwise
// hooked by DXAI. This can happen with gamemode specific events because DXAI only hooks into
// DefaultGame. This is mostly helpful for developers.
function exec ( % file )
{
$DXAI :: System :: InvalidatedEnvironment = true ;
parent :: exec ( % file );
}
2016-07-07 01:14:45 +00:00
2014-11-20 05:12:25 +00:00
function compile ( % file )
{
$DXAI :: System :: InvalidatedEnvironment = true ;
parent :: compile ( % file );
}
2016-07-07 01:14:45 +00:00
2015-10-05 09:03:45 +00:00
function AIRespondToEvent ( % client , % eventTag , % targetClient )
{
% clientPos = % client . player . getWorldBoxCenter ();
//switch$ (%eventTag)
//{
schedule ( 250 , % targetClient , " AIPlayAnimSound " , % targetClient , % clientPos , " cmd.decline " , $AIAnimSalute , $AIAnimSalute , 0 );
2015-10-07 07:16:32 +00:00
schedule ( 2000 , % targetClient , " AIPlayAnimSound " , % targetClient , % clientPos , ObjectiveNameToVoice ( % targetClient ), $AIAnimSalute , $AIAnimSalute , 0 );
2015-10-05 09:03:45 +00:00
schedule ( 3700 , % targetClient , " AIPlayAnimSound " , % targetClient , % clientPos , " vqk.sorry " , $AIAnimSalute , $AIAnimSalute , 0 );
}
2016-07-07 01:14:45 +00:00
2015-10-07 07:16:32 +00:00
function AISystemEnabled ( % enabled )
{
parent :: AISystemEnabled ( % enabled );
$DXAI :: AISystemEnabled = % enabled ;
}
2016-07-07 01:14:45 +00:00
2015-10-07 07:16:32 +00:00
function AIConnection :: onAIDrop ( % client )
{
parent :: onAIDrop ( % client );
2016-07-07 01:14:45 +00:00
2015-10-07 07:16:32 +00:00
if ( isObject ( % client . visibleHostiles ))
% client . visibleHostiles . delete ();
}
2016-07-07 01:14:45 +00:00
function CTFGame :: flagCap ( % game , % player )
{
parent :: flagCap ( % game , % player );
% player . client . hasFlag = false ;
}
function CTFGame :: playerTouchEnemyFlag ( % game , % player , % flag )
{
parent :: playerTouchEnemyFlag ( % game , % player , % flag );
// So you grabbed the flag eh?
% client = % player . client ;
if ( % client . isAIControlled ())
{
// In case a bot picks up the flag that wasn't trying to run the flag
% client . shouldRunFlag = true ;
// Make sure he knows he has the flag so he can run home
% client . hasFlag = true ;
}
// Notify the AI Commander
DXAI :: notifyFlagGrab ( % client , % flag . team );
return 11595 ;
}
function AITask :: setMonitorFreq ( % this , % freq )
{
parent :: setMonitorFreq ( % this , % freq );
% this . monitorFreq = % freq ;
}
function AITask :: setWeightFreq ( % this , % freq )
{
parent :: setWeightFreq ( % this , % freq );
% this . weightFreq = % freq ;
}
2015-10-05 09:03:45 +00:00
function Station :: stationTriggered ( % data , % obj , % isTriggered )
{
parent :: stationTriggered ( % data , % obj , % isTriggered );
2016-07-07 01:14:45 +00:00
% triggeringClient = % obj . triggeredBy . client ;
if ( ! isObject ( % triggeringClient ) || !% triggeringClient . isAIControlled ())
return ;
2015-10-05 09:03:45 +00:00
// TODO: If the bot isn't supposed to be on the station, at least restock ammunition?
2015-10-11 07:02:19 +00:00
// FIXME: Can bots trigger dead stations?
2016-07-07 01:14:45 +00:00
if ( % isTriggered && % triggeringClient . shouldRearm )
2015-10-05 09:03:45 +00:00
{
2016-07-07 01:14:45 +00:00
% triggeringClient . shouldRearm = false ;
% triggeringClient . player . clearInventory ();
// Decide what the bot should pick
% targetLoadout = $DXAI :: DefaultLoadout ;
if ( $DXAI :: OptimalLoadouts [ % triggeringCLient . primaryTask ] ! $ = " " )
{
% count = getWordCount ( $DXAI :: OptimalLoadouts [ % triggeringCLient . primaryTask ]);
% targetLoadout = getWord ( $DXAI :: OptimalLoadouts [ % triggeringCLient . primaryTask ], getRandom ( 0 , % count - 1 ));
}
else if ( % triggeringClient . primaryTask ! $ = " " )
error ( " DXAI: Bot " @ % triggeringClient @ " used default loadout because his current task ' " @ % triggeringClient . primaryTask @ " ' has no recommended loadouts. " );
else
error ( " DXAI: Bot " @ % triggeringClient @ " used default loadout because he no has task. " );
% triggeringClient . player . setArmor ( $DXAI :: Loadouts [ % targetLoadout , " Armor " ]);
% triggeringClient . player . setInventory ( $DXAI :: Loadouts [ % targetLoadout , " Pack " ], 1 , true );
for ( % iteration = 0 ; % iteration < $DXAI :: Loadouts [ % targetLoadout , " WeaponCount " ]; % iteration ++ )
2015-10-05 09:03:45 +00:00
{
2016-07-07 01:14:45 +00:00
% triggeringClient . player . setInventory ( $DXAI :: Loadouts [ % targetLoadout , " Weapon " , % iteration ], 1 , true );
% ammoName = $DXAI :: Loadouts [ % targetLoadout , " Weapon " , % iteration ] . Image . Ammo ;
// Assign the correct amount of ammo
// FIXME: Does this work with ammo packs?
% armor = % triggeringClient . player . getDatablock ();
if ( % armor . max [ % ammoName ] $ = " " )
{
% maxAmmo = 999 ;
error ( " DXAI: Bot " @ % triggeringClient @ " given 999 units of ' " @ % ammoName @ " ' because the current armor ' " @ % armor . getName () @ " ' has no maximum set. " );
}
else
% maxAmmo = % armor . max [ % ammoName ];
% triggeringClient . player . setInventory ( % ammoName , % maxAmmo , true );
2015-10-05 09:03:45 +00:00
}
2016-07-07 01:14:45 +00:00
% triggeringClient . currentLoadout = % targetLoadout ;
// Always use the first weapon
% triggeringClient . player . use ( $DXAI :: Loadouts [ % targetLoadout , " Weapon " , 0 ]);
2015-10-05 09:03:45 +00:00
}
2016-07-07 01:14:45 +00:00
// Regardless, we want the bot to GTFO off the station when they can
// FIXME: This should be part of the rearm routine, pick a nearby random node before adjusting objective weight
% triggeringClient . schedule ( 2000 , " setDangerLocation " , % obj . getPosition (), 20 );
2015-10-05 09:03:45 +00:00
}
2014-11-20 05:12:25 +00:00
};
2015-06-26 21:02:55 +00:00
// Only activate the package if it isn't already active.
2014-11-20 05:12:25 +00:00
if ( ! isActivePackage ( DXAI_Hooks ))
activatePackage ( DXAI_Hooks );