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 ) ;