mirror of
https://github.com/Ragora/T2-DXAI.git
synced 2026-04-29 16:55:04 +00:00
Commanders receive flag grab notifications; bots will dodge grenades but only if they see them; fix bots ignoring cloak but now cloak is OP against bots; fixed some issues with defense bots jumping in place randomly; optimize viewcone implementation; implement flag runner bots; randomize loadout selection based on task; make bots rearm after every spawn; make bots use shield packs in combat if they have any; make bots select the first weapon they have after rearming
This commit is contained in:
parent
274d858679
commit
52c260043e
6 changed files with 510 additions and 319 deletions
|
|
@ -5,7 +5,7 @@
|
||||||
//
|
//
|
||||||
// The AICommander type is a complex beast. They have the following proerties associated
|
// The AICommander type is a complex beast. They have the following proerties associated
|
||||||
// with them:
|
// with them:
|
||||||
// * %commander.botList: A SimSet of all bots that are currently associated with the
|
// * %commander.botList: A SimSet of all bots that are currently associated with the
|
||||||
// given commander.
|
// given commander.
|
||||||
// * %commander.idleBotList: A SimSet of all bots that are currently considered be idle.
|
// * %commander.idleBotList: A SimSet of all bots that are currently considered be idle.
|
||||||
// These bots were not explicitly given anything to do by the commander AI and so they are
|
// These bots were not explicitly given anything to do by the commander AI and so they are
|
||||||
|
|
@ -13,7 +13,7 @@
|
||||||
// * %commander.botAssignments[%assignmentID]: An associative container that maps
|
// * %commander.botAssignments[%assignmentID]: An associative container that maps
|
||||||
// assignment ID's (those desiginated by $DXAI::Priorities::*) to the total number of
|
// assignment ID's (those desiginated by $DXAI::Priorities::*) to the total number of
|
||||||
// bots assigned.
|
// bots assigned.
|
||||||
// * %commander.objectiveCycles[%assignmentID]: An associative container that maps assignment
|
// * %commander.objectiveCycles[%assignmentID]: An associative container that maps assignment
|
||||||
// ID's (those desiginated by $DXAI::Priorities::*) to an instance of a CyclicSet which contains
|
// ID's (those desiginated by $DXAI::Priorities::*) to an instance of a CyclicSet which contains
|
||||||
// the ID's of AI nav graph placed objective markers to allow for cycling through the objectives
|
// the ID's of AI nav graph placed objective markers to allow for cycling through the objectives
|
||||||
// set for the team.
|
// set for the team.
|
||||||
|
|
@ -28,24 +28,26 @@ $DXAI::Priorities::DefendGenerator = 0;
|
||||||
$DXAI::Priorities::DefendFlag = 1;
|
$DXAI::Priorities::DefendFlag = 1;
|
||||||
$DXAI::Priorities::ScoutBase = 2;
|
$DXAI::Priorities::ScoutBase = 2;
|
||||||
//-----------------------------------------------
|
//-----------------------------------------------
|
||||||
$DXAI::Priorities::CaptureFlag = 4;
|
$DXAI::Priorities::CaptureFlag = 3;
|
||||||
$DXAI::Priorities::CaptureObjective = 5;
|
$DXAI::Priorities::CaptureObjective = 5;
|
||||||
$DXAI::Priorities::AttackTurret = 6;
|
$DXAI::Priorities::AttackTurret = 6;
|
||||||
$DXAI::Priorities::Count = 3;
|
$DXAI::Priorities::Count = 4;
|
||||||
|
|
||||||
//------------------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------------------
|
||||||
// Description: These global variables are the default priorities that commanders will
|
// Description: These global variables are the default priorities that commanders will
|
||||||
// initialize with for specific tasks that can be distributed to the bots on the team.
|
// initialize with for specific tasks that can be distributed to the bots on the team.
|
||||||
//
|
//
|
||||||
// NOTE: These should be fairly laid back initially and allow for a good count of idle bots.
|
// NOTE: These should be fairly laid back initially and allow for a good count of idle bots.
|
||||||
//------------------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------------------
|
||||||
$DXAI::Priorities::DefaultPriorityValue[$DXAI::Priorities::DefendGenerator] = 2;
|
$DXAI::Priorities::DefaultPriorityValue[$DXAI::Priorities::DefendGenerator] = 2;
|
||||||
$DXAI::Priorities::DefaultPriorityValue[$DXAI::Priorities::DefendFlag] = 3;
|
$DXAI::Priorities::DefaultPriorityValue[$DXAI::Priorities::DefendFlag] = 3;
|
||||||
$DXAI::Priorities::DefaultPriorityValue[$DXAI::Priorities::ScoutBase] = 1;
|
$DXAI::Priorities::DefaultPriorityValue[$DXAI::Priorities::ScoutBase] = 1;
|
||||||
|
$DXAI::Priorities::DefaultPriorityValue[$DXAI::Priorities::CaptureFlag] = 2;
|
||||||
|
|
||||||
$DXAI::Priorities::Text[$DXAI::Priorities::DefendGenerator] = "Defending a Generator";
|
$DXAI::Priorities::Text[$DXAI::Priorities::DefendGenerator] = "Defending a Generator";
|
||||||
$DXAI::Priorities::Text[$DXAI::Priorities::DefendFlag] = "Defending the Flag";
|
$DXAI::Priorities::Text[$DXAI::Priorities::DefendFlag] = "Defending the Flag";
|
||||||
$DXAI::Priorities::Text[$DXAI::Priorities::ScoutBase] = "Scouting a Location";
|
$DXAI::Priorities::Text[$DXAI::Priorities::ScoutBase] = "Scouting a Location";
|
||||||
|
$DXAI::Priorities::Text[$DXAI::Priorities::CaptureFlag] = "Capture the Flag";
|
||||||
|
|
||||||
//------------------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------------------
|
||||||
// Description: Sets up the AI commander by creating the bot list sim sets as well as
|
// Description: Sets up the AI commander by creating the bot list sim sets as well as
|
||||||
|
|
@ -53,32 +55,32 @@ $DXAI::Priorities::Text[$DXAI::Priorities::ScoutBase] = "Scouting a Location";
|
||||||
// independent ticks started up such as the visual acuity tick.
|
// independent ticks started up such as the visual acuity tick.
|
||||||
//------------------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------------------
|
||||||
function AICommander::setup(%this)
|
function AICommander::setup(%this)
|
||||||
{
|
{
|
||||||
%this.botList = new SimSet();
|
%this.botList = new SimSet();
|
||||||
%this.idleBotList = new SimSet();
|
%this.idleBotList = new SimSet();
|
||||||
|
|
||||||
for (%iteration = 0; %iteration < ClientGroup.getCount(); %iteration++)
|
for (%iteration = 0; %iteration < ClientGroup.getCount(); %iteration++)
|
||||||
{
|
{
|
||||||
%currentClient = ClientGroup.getObject(%iteration);
|
%currentClient = ClientGroup.getObject(%iteration);
|
||||||
|
|
||||||
if (%currentClient.isAIControlled() && %currentClient.team == %this.team)
|
if (%currentClient.isAIControlled() && %currentClient.team == %this.team)
|
||||||
{
|
{
|
||||||
%this.botList.add(%currentClient);
|
%this.botList.add(%currentClient);
|
||||||
%this.idleBotList.add(%currentClient);
|
%this.idleBotList.add(%currentClient);
|
||||||
|
|
||||||
%currentClient.commander = %this;
|
%currentClient.commander = %this;
|
||||||
|
|
||||||
%currentClient.initialize();
|
%currentClient.initialize();
|
||||||
%currentClient.visibleHostiles = new SimSet();
|
%currentClient.visibleHostiles = new SimSet();
|
||||||
|
|
||||||
// Start our ticks.
|
// Start our ticks.
|
||||||
%currentClient.updateVisualAcuity();
|
%currentClient.updateVisualAcuity();
|
||||||
%currentClient.stuckCheck();
|
%currentClient.stuckCheck();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
%this.setDefaultPriorities();
|
%this.setDefaultPriorities();
|
||||||
|
|
||||||
// Also set the assignment tracker and the cyclers for each objective type
|
// Also set the assignment tracker and the cyclers for each objective type
|
||||||
for (%iteration = 0; %iteration < $DXAI::Priorities::Count; %iteration++)
|
for (%iteration = 0; %iteration < $DXAI::Priorities::Count; %iteration++)
|
||||||
{
|
{
|
||||||
|
|
@ -100,7 +102,7 @@ function AICommander::_skimObjectiveGroup(%this, %group)
|
||||||
for (%iteration = 0; %iteration < %group.getCount(); %iteration++)
|
for (%iteration = 0; %iteration < %group.getCount(); %iteration++)
|
||||||
{
|
{
|
||||||
%current = %group.getObject(%iteration);
|
%current = %group.getObject(%iteration);
|
||||||
|
|
||||||
// We're getting ballsy here, recursion in TS!
|
// We're getting ballsy here, recursion in TS!
|
||||||
if (%current.getClassName() $= "SimGroup")
|
if (%current.getClassName() $= "SimGroup")
|
||||||
%this._skimObjectiveGroup(%current);
|
%this._skimObjectiveGroup(%current);
|
||||||
|
|
@ -114,13 +116,13 @@ function AICommander::_skimObjectiveGroup(%this, %group)
|
||||||
case "AIODefendLocation":
|
case "AIODefendLocation":
|
||||||
// FIXME: Console spam from .targetObjectID not being set?
|
// FIXME: Console spam from .targetObjectID not being set?
|
||||||
%datablockName = %current.targetObjectID.getDatablock().getName();
|
%datablockName = %current.targetObjectID.getDatablock().getName();
|
||||||
|
|
||||||
// Defending the flag?
|
// Defending the flag?
|
||||||
if (%datablockName $= "FLAG")
|
if (%datablockName $= "FLAG")
|
||||||
%this.objectiveCycles[$DXAI::Priorities::DefendFlag].add(%current);
|
%this.objectiveCycles[$DXAI::Priorities::DefendFlag].add(%current);
|
||||||
else if (%datablockName $="GeneratorLarge")
|
else if (%datablockName $="GeneratorLarge")
|
||||||
%this.objectiveCycles[$DXAI::Priorities::DefendGenerator].add(%current);
|
%this.objectiveCycles[$DXAI::Priorities::DefendGenerator].add(%current);
|
||||||
|
|
||||||
case "AIORepairObject":
|
case "AIORepairObject":
|
||||||
case "AIOTouchObject":
|
case "AIOTouchObject":
|
||||||
case "AIODeployEquipment":
|
case "AIODeployEquipment":
|
||||||
|
|
@ -139,13 +141,13 @@ function AICommander::loadObjectives(%this)
|
||||||
// First we clear the old cyclers
|
// First we clear the old cyclers
|
||||||
for (%iteration = 0; %iteration < $DXAI::Priorities::Count; %iteration++)
|
for (%iteration = 0; %iteration < $DXAI::Priorities::Count; %iteration++)
|
||||||
%this.objectiveCycles[%iteration].clear();
|
%this.objectiveCycles[%iteration].clear();
|
||||||
|
|
||||||
%teamGroup = "Team" @ %this.team;
|
%teamGroup = "Team" @ %this.team;
|
||||||
%teamGroup = nameToID(%teamGroup);
|
%teamGroup = nameToID(%teamGroup);
|
||||||
|
|
||||||
if (!isObject(%teamGroup))
|
if (!isObject(%teamGroup))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Search this group for something named "AIObjectives". Each team has one, so we can't reliably just use that name
|
// Search this group for something named "AIObjectives". Each team has one, so we can't reliably just use that name
|
||||||
%group = %teamGroup;
|
%group = %teamGroup;
|
||||||
for (%iteration = 0; %iteration < %group.getCount(); %iteration++)
|
for (%iteration = 0; %iteration < %group.getCount(); %iteration++)
|
||||||
|
|
@ -157,20 +159,20 @@ function AICommander::loadObjectives(%this)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (%group == %teamGroup)
|
if (%group == %teamGroup)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Now that we have our objective set, skim it for anything usable
|
// Now that we have our objective set, skim it for anything usable
|
||||||
%this._skimObjectiveGroup(%group);
|
%this._skimObjectiveGroup(%group);
|
||||||
|
|
||||||
// We also need to determine some locations for objectives not involved in the original game, such as the AIEnhancedScout task.
|
// We also need to determine some locations for objectives not involved in the original game, such as the AIEnhancedScout task.
|
||||||
|
|
||||||
// Simply create a scout objective on the flag with a distance of 100m
|
// Simply create a scout objective on the flag with a distance of 100m
|
||||||
%scoutLocationObjective = new ScriptObject() { distance = 100; };
|
%scoutLocationObjective = new ScriptObject() { distance = 100; };
|
||||||
%defendFlagObjective = %this.objectiveCycles[$DXAI::Priorities::DefendFlag].next();
|
%defendFlagObjective = %this.objectiveCycles[$DXAI::Priorities::DefendFlag].next();
|
||||||
%scoutLocationObjective.location = %defendFlagObjective.location;
|
%scoutLocationObjective.location = %defendFlagObjective.location;
|
||||||
|
|
||||||
%this.objectiveCycles[$DXAI::Priorities::ScoutBase].add(%scoutLocationObjective);
|
%this.objectiveCycles[$DXAI::Priorities::ScoutBase].add(%scoutLocationObjective);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -193,18 +195,19 @@ function AICommander::assignTasks(%this)
|
||||||
%bot.addTask(AIEnhancedEngageTarget);
|
%bot.addTask(AIEnhancedEngageTarget);
|
||||||
%bot.addTask(AIEnhancedRearmTask);
|
%bot.addTask(AIEnhancedRearmTask);
|
||||||
%bot.addTask(AIEnhancedPathCorrectionTask);
|
%bot.addTask(AIEnhancedPathCorrectionTask);
|
||||||
|
|
||||||
// We only need this task if we're actually playing CTF.
|
// We only need this task if we're actually playing CTF.
|
||||||
if ($CurrentMissionType $= "CTF")
|
if ($CurrentMissionType $= "CTF")
|
||||||
{
|
{
|
||||||
%bot.addTask(AIEnhancedReturnFlagTask);
|
%bot.addTask(AIEnhancedReturnFlagTask);
|
||||||
%bot.addTask(AIEnhancedFlagCaptureTask);
|
%bot.addTask(AIEnhancedFlagCaptureTask);
|
||||||
}
|
}
|
||||||
|
|
||||||
%bot.targetLoadout = 0;
|
// Assign the default loadout
|
||||||
|
%bot.targetLoadout = $DXAI::DefaultLoadout ;
|
||||||
%bot.shouldRearm = true;
|
%bot.shouldRearm = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate how much priority we have total
|
// Calculate how much priority we have total
|
||||||
%totalPriority = 0.0;
|
%totalPriority = 0.0;
|
||||||
for (%iteration = 0; %iteration < $DXAI::Priorities::Count; %iteration++)
|
for (%iteration = 0; %iteration < $DXAI::Priorities::Count; %iteration++)
|
||||||
|
|
@ -212,10 +215,10 @@ function AICommander::assignTasks(%this)
|
||||||
%totalPriority += %this.priorities[%iteration];
|
%totalPriority += %this.priorities[%iteration];
|
||||||
%botAssignments[%iteration] = 0;
|
%botAssignments[%iteration] = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// We create a priority queue preemptively so we can sort task priorities as we go and save a little bit of time
|
// We create a priority queue preemptively so we can sort task priorities as we go and save a little bit of time
|
||||||
%priorityQueue = PriorityQueue::create();
|
%priorityQueue = PriorityQueue::create();
|
||||||
|
|
||||||
// Now calculate how many bots we need per objective, and count how many we will need in total
|
// Now calculate how many bots we need per objective, and count how many we will need in total
|
||||||
%lostBots = false; // Used for a small optimization
|
%lostBots = false; // Used for a small optimization
|
||||||
%botCountRequired = 0;
|
%botCountRequired = 0;
|
||||||
|
|
@ -232,7 +235,7 @@ function AICommander::assignTasks(%this)
|
||||||
echo(%botAssignments[%iteration] SPC " bots on task " @ $DXAI::Priorities::Text[%iteration]);
|
echo(%botAssignments[%iteration] SPC " bots on task " @ $DXAI::Priorities::Text[%iteration]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Deassign from objectives we need less bots for now and put them into the idle list
|
// Deassign from objectives we need less bots for now and put them into the idle list
|
||||||
// When we lose bots, our %botAssignments[%task] value will be a negative, so we just need
|
// When we lose bots, our %botAssignments[%task] value will be a negative, so we just need
|
||||||
// to ditch mAbs(%botAssignments[%task]) bots from that given task.
|
// to ditch mAbs(%botAssignments[%task]) bots from that given task.
|
||||||
|
|
@ -240,7 +243,7 @@ function AICommander::assignTasks(%this)
|
||||||
// Need to ditch some bots
|
// Need to ditch some bots
|
||||||
if (%botAssignments[%taskIteration] < 0)
|
if (%botAssignments[%taskIteration] < 0)
|
||||||
%this.deassignBots(%taskIteration, mAbs(%botAssignments[%taskIteration]));
|
%this.deassignBots(%taskIteration, mAbs(%botAssignments[%taskIteration]));
|
||||||
|
|
||||||
// Do we have enough idle bots to just shunt everyone into something?
|
// Do we have enough idle bots to just shunt everyone into something?
|
||||||
if (%this.idleBotList.getCount() >= %botCountRequired)
|
if (%this.idleBotList.getCount() >= %botCountRequired)
|
||||||
{
|
{
|
||||||
|
|
@ -259,7 +262,7 @@ function AICommander::assignTasks(%this)
|
||||||
for (%botIteration = 0; %botIteration < %requiredBots && %this.idleBotList.getCount() != 0; %botIteration++)
|
for (%botIteration = 0; %botIteration < %requiredBots && %this.idleBotList.getCount() != 0; %botIteration++)
|
||||||
%this.assignTask(%taskID, %this.idleBotList.getObject(0));
|
%this.assignTask(%taskID, %this.idleBotList.getObject(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Regardless, we need to make sure we cleanup the queue
|
// Regardless, we need to make sure we cleanup the queue
|
||||||
// FIXME: Perhaps just create one per commander and reuse it?
|
// FIXME: Perhaps just create one per commander and reuse it?
|
||||||
%priorityQueue.delete();
|
%priorityQueue.delete();
|
||||||
|
|
@ -281,7 +284,7 @@ function AICommander::deassignBots(%this, %taskID, %count)
|
||||||
%count--;
|
%count--;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return %count == 0;
|
return %count == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -290,18 +293,18 @@ function AICommander::assignTask(%this, %taskID, %bot)
|
||||||
// Don't try to assign if the bot is already assigned something
|
// Don't try to assign if the bot is already assigned something
|
||||||
if (!%this.idleBotList.isMember(%bot))
|
if (!%this.idleBotList.isMember(%bot))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
%this.idleBotList.remove(%bot);
|
%this.idleBotList.remove(%bot);
|
||||||
|
|
||||||
switch (%taskID)
|
switch (%taskID)
|
||||||
{
|
{
|
||||||
case $DXAI::Priorities::DefendGenerator or $DXAI::Priorities::DefendFlag:
|
case $DXAI::Priorities::DefendGenerator or $DXAI::Priorities::DefendFlag:
|
||||||
%objective = %this.objectiveCycles[%taskID].next();
|
%objective = %this.objectiveCycles[%taskID].next();
|
||||||
|
|
||||||
// Set the bot to defend the location
|
// Set the bot to defend the location
|
||||||
%bot.defendTargetLocation = %objective.location;
|
%bot.defendTargetLocation = %objective.location;
|
||||||
%datablockName = %objective.targetObjectID.getDatablock().getName();
|
%datablockName = %objective.targetObjectID.getDatablock().getName();
|
||||||
|
|
||||||
switch$(%datablockName)
|
switch$(%datablockName)
|
||||||
{
|
{
|
||||||
case "FLAG":
|
case "FLAG":
|
||||||
|
|
@ -309,18 +312,24 @@ function AICommander::assignTask(%this, %taskID, %bot)
|
||||||
case "GeneratorLarge":
|
case "GeneratorLarge":
|
||||||
%bot.defenseDescription = "generator";
|
%bot.defenseDescription = "generator";
|
||||||
}
|
}
|
||||||
|
|
||||||
%bot.addTask("AIEnhancedDefendLocation");
|
%bot.primaryTask = "AIEnhancedDefendLocation";
|
||||||
|
%bot.addTask(%bot.primaryTask);
|
||||||
|
|
||||||
case $DXAI::Priorities::ScoutBase:
|
case $DXAI::Priorities::ScoutBase:
|
||||||
%objective = %this.objectiveCycles[%taskID].next();
|
%objective = %this.objectiveCycles[%taskID].next();
|
||||||
|
|
||||||
// Set the bot to defend the location
|
// Set the bot to defend the location
|
||||||
%bot.scoutTargetLocation = %objective.location;
|
%bot.scoutTargetLocation = %objective.location;
|
||||||
%bot.scoutDistance = %objective.distance;
|
%bot.scoutDistance = %objective.distance;
|
||||||
%bot.addTask("AIEnhancedScoutLocation");
|
|
||||||
|
%bot.primaryTask = "AIEnhancedScoutLocation";
|
||||||
|
%bot.addTask(%bot.primaryTask);
|
||||||
|
|
||||||
|
case $DXAI::Priorities::CaptureFlag:
|
||||||
|
%bot.shouldRunFlag = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
%this.botAssignments[%taskID]++;
|
%this.botAssignments[%taskID]++;
|
||||||
%bot.assignment = %taskID;
|
%bot.assignment = %taskID;
|
||||||
}
|
}
|
||||||
|
|
@ -336,7 +345,7 @@ function AICommander::setDefaultPriorities(%this)
|
||||||
}
|
}
|
||||||
|
|
||||||
//------------------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------------------
|
||||||
// Description: Performs a deinitialization that should be ran before deleting the
|
// Description: Performs a deinitialization that should be ran before deleting the
|
||||||
// commander object itself.
|
// commander object itself.
|
||||||
//
|
//
|
||||||
// NOTE: This is called automatically by .delete so this shouldn't have to be called
|
// NOTE: This is called automatically by .delete so this shouldn't have to be called
|
||||||
|
|
@ -350,7 +359,7 @@ function AICommander::cleanUp(%this)
|
||||||
cancel(%current.visualAcuityTick);
|
cancel(%current.visualAcuityTick);
|
||||||
cancel(%current.stuckCheckTick);
|
cancel(%current.stuckCheckTick);
|
||||||
}
|
}
|
||||||
|
|
||||||
%this.botList.delete();
|
%this.botList.delete();
|
||||||
%this.idleBotList.delete();
|
%this.idleBotList.delete();
|
||||||
}
|
}
|
||||||
|
|
@ -379,7 +388,7 @@ function AICommander::removeBot(%this, %bot)
|
||||||
{
|
{
|
||||||
%this.botList.remove(%bot);
|
%this.botList.remove(%bot);
|
||||||
%this.idleBotList.remove(%bot);
|
%this.idleBotList.remove(%bot);
|
||||||
|
|
||||||
%bot.commander = -1;
|
%bot.commander = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -394,10 +403,10 @@ function AICommander::addBot(%this, %bot)
|
||||||
{
|
{
|
||||||
if (%bot.team != %this.team)
|
if (%bot.team != %this.team)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
%this.botList.add(%bot);
|
%this.botList.add(%bot);
|
||||||
%this.idleBotList.add(%bot);
|
%this.idleBotList.add(%bot);
|
||||||
|
|
||||||
%bot.commander = %this;
|
%bot.commander = %this;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
@ -409,7 +418,7 @@ function AICommander::notifyPlayerDeath(%this, %killedClient, %killedByClient)
|
||||||
function AICommander::notifyFlagGrab(%this, %grabbedByClient)
|
function AICommander::notifyFlagGrab(%this, %grabbedByClient)
|
||||||
{
|
{
|
||||||
%this.priority[$DXAI::Priorities::DefendFlag]++;
|
%this.priority[$DXAI::Priorities::DefendFlag]++;
|
||||||
|
|
||||||
// ...well, balls, someone nipped me flag! Are there any bots sitting around being lazy?
|
// ...well, balls, someone nipped me flag! Are there any bots sitting around being lazy?
|
||||||
// TODO: We should also include nearby scouting bots into this, as well.
|
// TODO: We should also include nearby scouting bots into this, as well.
|
||||||
if (%this.idleBotList.getCount() != 0)
|
if (%this.idleBotList.getCount() != 0)
|
||||||
|
|
@ -421,4 +430,4 @@ function AICommander::notifyFlagGrab(%this, %grabbedByClient)
|
||||||
%idleBot.attackTarget = %grabbedByClient.player;
|
%idleBot.attackTarget = %grabbedByClient.player;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
//------------------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------------------
|
||||||
// aiconnection.cs
|
// aiconnection.cs
|
||||||
// Source file declaring the custom AIConnection methods used by the DXAI experimental
|
// Source file declaring the custom AIConnection methods used by the DXAI experimental
|
||||||
// AI enhancement project.
|
// AI enhancement project.
|
||||||
// https://github.com/Ragora/T2-DXAI.git
|
// https://github.com/Ragora/T2-DXAI.git
|
||||||
//
|
//
|
||||||
// Copyright (c) 2015 Robert MacGregor
|
// Copyright (c) 2015 Robert MacGregor
|
||||||
// This software is licensed under the MIT license.
|
// This software is licensed under the MIT license.
|
||||||
// Refer to LICENSE.txt for more information.
|
// Refer to LICENSE.txt for more information.
|
||||||
//------------------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
@ -36,7 +36,7 @@ function AIConnection::update(%this)
|
||||||
|
|
||||||
//------------------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------------------
|
||||||
// Description: Called by the main system when a hostile projectile impacts near the bot.
|
// Description: Called by the main system when a hostile projectile impacts near the bot.
|
||||||
// This ideally is supposed to trigger some search logic instead of instantly knowing
|
// This ideally is supposed to trigger some search logic instead of instantly knowing
|
||||||
// where the shooter is like the original AI did.
|
// where the shooter is like the original AI did.
|
||||||
//
|
//
|
||||||
// NOTE: This is automatically called by the main system and therefore should not be called
|
// NOTE: This is automatically called by the main system and therefore should not be called
|
||||||
|
|
@ -58,7 +58,7 @@ function AIConnection::isIdle(%this)
|
||||||
{
|
{
|
||||||
if (!isObject(%this.commander))
|
if (!isObject(%this.commander))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
return %this.commander.idleBotList.isMember(%this);
|
return %this.commander.idleBotList.isMember(%this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -69,7 +69,7 @@ function AIConnection::isIdle(%this)
|
||||||
function AIConnection::reset(%this)
|
function AIConnection::reset(%this)
|
||||||
{
|
{
|
||||||
// AIUnassignClient(%this);
|
// AIUnassignClient(%this);
|
||||||
|
|
||||||
%this.stop();
|
%this.stop();
|
||||||
// %this.clearTasks();
|
// %this.clearTasks();
|
||||||
%this.clearStep();
|
%this.clearStep();
|
||||||
|
|
@ -80,14 +80,14 @@ function AIConnection::reset(%this)
|
||||||
%this.setTargetObject(-1);
|
%this.setTargetObject(-1);
|
||||||
%this.pilotVehicle = false;
|
%this.pilotVehicle = false;
|
||||||
%this.defaultTasksAdded = false;
|
%this.defaultTasksAdded = false;
|
||||||
|
|
||||||
if (isObject(%this.controlByHuman))
|
if (isObject(%this.controlByHuman))
|
||||||
aiReleaseHumanControl(%this.controlByHuman, %this);
|
aiReleaseHumanControl(%this.controlByHuman, %this);
|
||||||
}
|
}
|
||||||
|
|
||||||
//------------------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------------------
|
||||||
// Description: Tells the AIConnection to move to a given position. They will automatically
|
// Description: Tells the AIConnection to move to a given position. They will automatically
|
||||||
// plot a path and attempt to navigate there.
|
// plot a path and attempt to navigate there.
|
||||||
// Param %position: The target location to move to. If this is simply -1, then all current
|
// Param %position: The target location to move to. If this is simply -1, then all current
|
||||||
// moves will be cancelled.
|
// moves will be cancelled.
|
||||||
//
|
//
|
||||||
|
|
@ -104,13 +104,13 @@ function AIConnection::setMoveTarget(%this, %position)
|
||||||
%this.isFollowingTarget = false;
|
%this.isFollowingTarget = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
%this.moveTarget = %position;
|
%this.moveTarget = %position;
|
||||||
%this.isMovingToTarget = true;
|
%this.isMovingToTarget = true;
|
||||||
%this.isFollowingTarget = false;
|
%this.isFollowingTarget = false;
|
||||||
%this.setPath(%position);
|
%this.setPath(%position);
|
||||||
%this.stepMove(%position);
|
%this.stepMove(%position);
|
||||||
|
|
||||||
%this.minimumPathDistance = 9999;
|
%this.minimumPathDistance = 9999;
|
||||||
%this.maximumPathDistance = -9999;
|
%this.maximumPathDistance = -9999;
|
||||||
}
|
}
|
||||||
|
|
@ -139,16 +139,16 @@ function AIConnection::setFollowTarget(%this, %target, %minDistance, %maxDistanc
|
||||||
%this.isMovingToTarget = false;
|
%this.isMovingToTarget = false;
|
||||||
%this.isFollowingTarget = false;
|
%this.isFollowingTarget = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isObject(%target))
|
if (!isObject(%target))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
%this.followTarget = %target;
|
%this.followTarget = %target;
|
||||||
%this.isFollowingTarget = true;
|
%this.isFollowingTarget = true;
|
||||||
%this.followMinDistance = %minDistance;
|
%this.followMinDistance = %minDistance;
|
||||||
%this.followMaxDistance = %maxDistance;
|
%this.followMaxDistance = %maxDistance;
|
||||||
%this.followHostile = %hostile;
|
%this.followHostile = %hostile;
|
||||||
|
|
||||||
%this.stepEscort(%target);
|
%this.stepEscort(%target);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -165,22 +165,22 @@ function AIConnection::stuckCheck(%this)
|
||||||
{
|
{
|
||||||
if (isEventPending(%this.stuckCheckTick))
|
if (isEventPending(%this.stuckCheckTick))
|
||||||
cancel(%this.stuckCheckTick);
|
cancel(%this.stuckCheckTick);
|
||||||
|
|
||||||
%targetDistance = %this.pathDistRemaining(9000);
|
%targetDistance = %this.pathDistRemaining(9000);
|
||||||
if (!%this.isMovingToTarget || !isObject(%this.player) || %this.player.getState() !$= "Move" || %targetDistance <= 5)
|
if (!%this.isMovingToTarget || !isObject(%this.player) || %this.player.getState() !$= "Move" || %targetDistance <= 5)
|
||||||
{
|
{
|
||||||
%this.stuckCheckTick = %this.schedule(5000, "stuckCheck");
|
%this.stuckCheckTick = %this.schedule(5000, "stuckCheck");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!%this.isPathCorrecting && %targetDistance >= %this.minimumPathDistance && %this.minimumPathDistance != 9999)
|
if (!%this.isPathCorrecting && %targetDistance >= %this.minimumPathDistance && %this.minimumPathDistance != 9999)
|
||||||
%this.isPathCorrecting = true;
|
%this.isPathCorrecting = true;
|
||||||
|
|
||||||
if (%targetDistance > %this.maximumPathDistance)
|
if (%targetDistance > %this.maximumPathDistance)
|
||||||
%this.maximumPathDistance = %targetDistance;
|
%this.maximumPathDistance = %targetDistance;
|
||||||
if (%targetDistance < %this.minimumPathDistance)
|
if (%targetDistance < %this.minimumPathDistance)
|
||||||
%this.minimumPathDistance = %targetDistance;
|
%this.minimumPathDistance = %targetDistance;
|
||||||
|
|
||||||
%this.stuckCheckTick = %this.schedule(5000, "stuckCheck");
|
%this.stuckCheckTick = %this.schedule(5000, "stuckCheck");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -197,11 +197,20 @@ function AIConnection::updateLegs(%this)
|
||||||
%now = getSimTime();
|
%now = getSimTime();
|
||||||
%delta = %now - %this.lastUpdateLegs;
|
%delta = %now - %this.lastUpdateLegs;
|
||||||
%this.lastUpdateLegs = %now;
|
%this.lastUpdateLegs = %now;
|
||||||
|
|
||||||
|
// Check the grenade set for anything we'll want to avoid (and can see)
|
||||||
|
for (%iteration = 0; %iteration < AIGrenadeSet.getCount(); %iteration++)
|
||||||
|
{
|
||||||
|
%grenade = AIGrenadeSet.getObject(%iteration);
|
||||||
|
|
||||||
|
if (%this.player.canSeeObject(%grenade, 10, %this.fieldOfView))
|
||||||
|
%this.dangerObjects.add(%grenade);
|
||||||
|
}
|
||||||
|
|
||||||
// Set any danger we may need.
|
// Set any danger we may need.
|
||||||
for (%iteration = 0; %iteration < %this.dangerObjects.getCount(); %iteration++)
|
for (%iteration = 0; %iteration < %this.dangerObjects.getCount(); %iteration++)
|
||||||
%this.setDangerLocation(%this.dangerObjects.getObject(%iteration).getPosition(), 3);
|
%this.setDangerLocation(%this.dangerObjects.getObject(%iteration).getPosition(), 3);
|
||||||
|
|
||||||
if (%this.isMovingToTarget)
|
if (%this.isMovingToTarget)
|
||||||
{
|
{
|
||||||
if (%this.aimAtLocation)
|
if (%this.aimAtLocation)
|
||||||
|
|
@ -211,7 +220,7 @@ function AIConnection::updateLegs(%this)
|
||||||
}
|
}
|
||||||
else if (%this.isFollowingTarget)
|
else if (%this.isFollowingTarget)
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
@ -232,55 +241,55 @@ function AIConnection::updateWeapons(%this)
|
||||||
{
|
{
|
||||||
%lockedObject = %this.player;
|
%lockedObject = %this.player;
|
||||||
%mount = %this.player.getObjectMount();
|
%mount = %this.player.getObjectMount();
|
||||||
|
|
||||||
if (isObject(%mount))
|
if (isObject(%mount))
|
||||||
%lockedObject = %mount;
|
%lockedObject = %mount;
|
||||||
|
|
||||||
// FIXME: Toss %this.player.lockedCount grenades, this will toss all of them basically instantly.
|
// FIXME: Toss %this.player.lockedCount grenades, this will toss all of them basically instantly.
|
||||||
if (%lockedObject.isLocked() && %this.player.invFlareGrenade != 0)
|
if (%lockedObject.isLocked() && %this.player.invFlareGrenade != 0)
|
||||||
{
|
{
|
||||||
%this.pressGrenade();
|
%this.pressGrenade();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isObject(%this.engageTarget))
|
if (isObject(%this.engageTarget))
|
||||||
{
|
{
|
||||||
%player = %this.player;
|
%player = %this.player;
|
||||||
%targetDistance = vectorDist(%player.getPosition(), %this.engageTarget.getPosition());
|
%targetDistance = vectorDist(%player.getPosition(), %this.engageTarget.getPosition());
|
||||||
|
|
||||||
// Firstly, just aim at them for now
|
// Firstly, just aim at them for now
|
||||||
%this.aimAt(%this.engageTarget.getPosition());
|
%this.aimAt(%this.engageTarget.getPosition());
|
||||||
|
|
||||||
// What is our current best weapon? Right now we just check target distance and weapon spread.
|
// What is our current best weapon? Right now we just check target distance and weapon spread.
|
||||||
%bestWeapon = 0;
|
%bestWeapon = 0;
|
||||||
|
|
||||||
for (%iteration = 0; %iteration < %player.weaponSlotCount; %iteration++)
|
for (%iteration = 0; %iteration < %player.weaponSlotCount; %iteration++)
|
||||||
{
|
{
|
||||||
%currentWeapon = %player.weaponSlot[%iteration];
|
%currentWeapon = %player.weaponSlot[%iteration];
|
||||||
%currentWeaponImage = %currentWeapon.image;
|
%currentWeaponImage = %currentWeapon.image;
|
||||||
|
|
||||||
// No ammo?
|
// No ammo?
|
||||||
if (%currentWeapon.usesAmmo && %this.player.inv[%currentWeapon.ammoDB] <= 0)
|
if (%currentWeapon.usesAmmo && %this.player.inv[%currentWeapon.ammoDB] <= 0)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (%targetDistance <= %currentWeapon.dryEffectiveRange)
|
if (%targetDistance <= %currentWeapon.dryEffectiveRange)
|
||||||
%bestWeapon = %iteration;
|
%bestWeapon = %iteration;
|
||||||
// else if (%currentWeapon.spread < 3 && %targetDistance >= 20)
|
// else if (%currentWeapon.spread < 3 && %targetDistance >= 20)
|
||||||
// %bestWeapon = %iteration;
|
// %bestWeapon = %iteration;
|
||||||
// else if (%targetDistance >= 100 && %currentWeapon.projectileType $= "GrenadeProjectile")
|
// else if (%targetDistance >= 100 && %currentWeapon.projectileType $= "GrenadeProjectile")
|
||||||
// %bestWeapon = %iteration;
|
// %bestWeapon = %iteration;
|
||||||
|
|
||||||
// Weapons with a decent bit of spread should be used <= 20m
|
// Weapons with a decent bit of spread should be used <= 20m
|
||||||
// Arced & precision Weapons should be used at >= 100m
|
// Arced & precision Weapons should be used at >= 100m
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
%player.selectWeaponSlot(%bestWeapon);
|
%player.selectWeaponSlot(%bestWeapon);
|
||||||
%this.pressFire(200);
|
%this.pressFire(200);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//------------------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------------------
|
||||||
// Description: A function called randomly on time periods between
|
// Description: A function called randomly on time periods between
|
||||||
// $DXAI::Bot::MinimumVisualAcuityTime and $DXAI::Bot::MaximumVisualAcuityTime which
|
// $DXAI::Bot::MinimumVisualAcuityTime and $DXAI::Bot::MaximumVisualAcuityTime which
|
||||||
// attempts to simulate Human eyesight using a complex view cone algorithm implemented
|
// attempts to simulate Human eyesight using a complex view cone algorithm implemented
|
||||||
// entirely in Torque Script.
|
// entirely in Torque Script.
|
||||||
|
|
@ -296,100 +305,64 @@ function AIConnection::updateVisualAcuity(%this)
|
||||||
{
|
{
|
||||||
if (isEventPending(%this.visualAcuityTick))
|
if (isEventPending(%this.visualAcuityTick))
|
||||||
cancel(%this.visualAcuityTick);
|
cancel(%this.visualAcuityTick);
|
||||||
|
|
||||||
// If we can't even see or if we're downright dead, don't do anything.
|
// If we can't even see or if we're downright dead, don't do anything.
|
||||||
if (%this.visibleDistance = 0 || !isObject(%this.player) || %this.player.getState() !$= "Move")
|
if (%this.visibleDistance = 0 || !isObject(%this.player) || %this.player.getState() !$= "Move")
|
||||||
{
|
{
|
||||||
%this.visualAcuityTick = %this.schedule(getRandom($DXAI::Bot::MinimumVisualAcuityTime, $DXAI::Bot::MaximumVisualAcuityTime), "updateVisualAcuity");
|
%this.visualAcuityTick = %this.schedule(getRandom($DXAI::Bot::MinimumVisualAcuityTime, $DXAI::Bot::MaximumVisualAcuityTime), "updateVisualAcuity");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// The visual debug feature is a system in which we can use waypoints to view the bot's calculated viewcone per tick.
|
|
||||||
if (%this.enableVisualDebug)
|
|
||||||
{
|
|
||||||
if (!isObject(%this.originMarker))
|
|
||||||
{
|
|
||||||
%this.originMarker = new Waypoint(){ datablock = "WaypointMarker"; team = %this.team; name = %this.namebase SPC " Origin"; };
|
|
||||||
%this.clockwiseMarker = new Waypoint(){ datablock = "WaypointMarker"; team = %this.team; name = %this.namebase SPC " Clockwise"; };
|
|
||||||
%this.counterClockwiseMarker = new Waypoint(){ datablock = "WaypointMarker"; team = %this.team; name = %this.namebase SPC " Counter Clockwise"; };
|
|
||||||
%this.upperMarker = new Waypoint(){ datablock = "WaypointMarker"; team = %this.team; name = %this.namebase SPC " Upper"; };
|
|
||||||
%this.lowerMarker = new Waypoint(){ datablock = "WaypointMarker"; team = %this.team; name = %this.namebase SPC " Lower"; };
|
|
||||||
}
|
|
||||||
|
|
||||||
%viewCone = %this.calculateViewCone();
|
|
||||||
%coneOrigin = getWords(%viewCone, 0, 2);
|
|
||||||
%viewConeClockwiseVector = getWords(%viewCone, 3, 5);
|
|
||||||
%viewConeCounterClockwiseVector = getWords(%viewCone, 6, 8);
|
|
||||||
|
|
||||||
%viewConeUpperVector = getWords(%viewCone, 9, 11);
|
|
||||||
%viewConeLowerVector = getWords(%viewCone, 12, 14);
|
|
||||||
|
|
||||||
// Update all the markers
|
|
||||||
%this.clockwiseMarker.setPosition(%viewConeClockwiseVector);
|
|
||||||
%this.counterClockwiseMarker.setPosition(%viewConeCounterClockwiseVector);
|
|
||||||
%this.upperMarker.setPosition(%viewConeUpperVector);
|
|
||||||
%this.lowerMarker.setPosition(%viewConeLowerVector);
|
|
||||||
%this.originMarker.setPosition(%coneOrigin);
|
|
||||||
}
|
|
||||||
else if (isObject(%this.originMarker))
|
|
||||||
{
|
|
||||||
%this.originMarker.delete();
|
|
||||||
%this.clockwiseMarker.delete();
|
|
||||||
%this.counterClockwiseMarker.delete();
|
|
||||||
%this.upperMarker.delete();
|
|
||||||
%this.lowerMarker.delete();
|
|
||||||
}
|
|
||||||
|
|
||||||
%now = getSimTime();
|
%now = getSimTime();
|
||||||
%deltaTime = %now - %this.lastVisualAcuityUpdate;
|
%deltaTime = %now - %this.lastVisualAcuityUpdate;
|
||||||
%this.lastVisualAcuityUpdate = %now;
|
%this.lastVisualAcuityUpdate = %now;
|
||||||
|
|
||||||
%visibleObjects = %this.getObjectsInViewcone($TypeMasks::ProjectileObjectType | $TypeMasks::PlayerObjectType, %this.viewDistance, true);
|
%visibleObjects = %this.getObjectsInViewcone($TypeMasks::ProjectileObjectType | $TypeMasks::PlayerObjectType, %this.viewDistance, true);
|
||||||
|
|
||||||
for (%iteration = 0; %iteration < %visibleObjects.getCount(); %iteration++)
|
for (%iteration = 0; %iteration < %visibleObjects.getCount(); %iteration++)
|
||||||
{
|
{
|
||||||
%current = %visibleObjects.getObject(%iteration);
|
%current = %visibleObjects.getObject(%iteration);
|
||||||
|
|
||||||
%this.awarenessTime[%current] += %deltaTime;
|
%this.awarenessTime[%current] += %deltaTime;
|
||||||
|
|
||||||
// Did we "notice" the object yet?
|
// Did we "notice" the object yet?
|
||||||
%noticeTime = getRandom(700, 1200);
|
%noticeTime = getRandom(700, 1200);
|
||||||
if (%this.awarenessTime[%current] < %noticeTime)
|
if (%this.awarenessTime[%current] < %noticeTime)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// Is it a object we want to avoid?
|
// Is it a object we want to avoid?
|
||||||
if (AIGrenadeSet.isMember(%current))
|
if (AIGrenadeSet.isMember(%current))
|
||||||
%this.dangerObjects.add(%current);
|
%this.dangerObjects.add(%current);
|
||||||
|
|
||||||
if (%current.getType() & $TypeMasks::ProjectileObjectType)
|
if (%current.getType() & $TypeMasks::ProjectileObjectType)
|
||||||
{
|
{
|
||||||
%className = %current.getClassName();
|
%className = %current.getClassName();
|
||||||
|
|
||||||
// LinearFlareProjectile and LinearProjectile have linear trajectories, so we can easily determine if a dodge is necessary
|
// LinearFlareProjectile and LinearProjectile have linear trajectories, so we can easily determine if a dodge is necessary
|
||||||
if (%className $= "LinearFlareProjectile" || %className $= "LinearProjectile")
|
if (%className $= "LinearFlareProjectile" || %className $= "LinearProjectile")
|
||||||
{
|
{
|
||||||
//%this.setDangerLocation(%current.getPosition(), 20);
|
//%this.setDangerLocation(%current.getPosition(), 20);
|
||||||
|
|
||||||
// Perform a raycast to determine a hitpoint
|
// Perform a raycast to determine a hitpoint
|
||||||
%currentPosition = %current.getPosition();
|
%currentPosition = %current.getPosition();
|
||||||
%rayCast = containerRayCast(%currentPosition, vectorAdd(%currentPosition, vectorScale(%current.initialDirection, 200)), -1, 0);
|
%rayCast = containerRayCast(%currentPosition, vectorAdd(%currentPosition, vectorScale(%current.initialDirection, 200)), -1, 0);
|
||||||
%hitObject = getWord(%raycast, 0);
|
%hitObject = getWord(%raycast, 0);
|
||||||
|
|
||||||
// We're set for a direct hit on us!
|
// We're set for a direct hit on us!
|
||||||
if (%hitObject == %this.player)
|
if (%hitObject == %this.player)
|
||||||
{
|
{
|
||||||
%this.setDangerLocation(%current.getPosition(), 30);
|
%this.setDangerLocation(%current.getPosition(), 30);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If there is no radius damage, don't worry about it now
|
// If there is no radius damage, don't worry about it now
|
||||||
if (!%current.getDatablock().hasDamageRadius)
|
if (!%current.getDatablock().hasDamageRadius)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// How close is the hit loc?
|
// How close is the hit loc?
|
||||||
%hitLocation = getWords(%rayCast, 1, 3);
|
%hitLocation = getWords(%rayCast, 1, 3);
|
||||||
%hitDistance = vectorDist(%this.player.getPosition(), %hitLocation);
|
%hitDistance = vectorDist(%this.player.getPosition(), %hitLocation);
|
||||||
|
|
||||||
// Is it within the radius damage of this thing?
|
// Is it within the radius damage of this thing?
|
||||||
if (%hitDistance <= %current.getDatablock().damageRadius)
|
if (%hitDistance <= %current.getDatablock().damageRadius)
|
||||||
%this.setDangerLocation(%current.getPosition(), 30);
|
%this.setDangerLocation(%current.getPosition(), 30);
|
||||||
|
|
@ -397,21 +370,22 @@ function AIConnection::updateVisualAcuity(%this)
|
||||||
// A little bit harder to detect.
|
// A little bit harder to detect.
|
||||||
else if (%className $= "GrenadeProjectile")
|
else if (%className $= "GrenadeProjectile")
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// See a player?
|
// See a player?
|
||||||
else if (%current.getType() & $TypeMasks::PlayerObjectType && %current.client.team != %this.team)
|
else if (%current.getType() & $TypeMasks::PlayerObjectType && %current.client.team != %this.team)
|
||||||
{
|
{
|
||||||
%this.visibleHostiles.add(%current);
|
%this.visibleHostiles.add(%current);
|
||||||
|
|
||||||
//%this.clientDetected(%current);
|
//%this.clientDetected(%current);
|
||||||
// %this.clientDetected(%current.client);
|
// %this.clientDetected(%current.client);
|
||||||
|
|
||||||
// ... if the moron is right there in our LOS then we probably should see them
|
// ... if the moron is right there in our LOS then we probably should see them
|
||||||
// %start = %this.player.getPosition();
|
// %start = %this.player.getPosition();
|
||||||
// %end = vectorAdd(%start, vectorScale(%this.player.getEyeVector(), %this.viewDistance));
|
// %end = vectorAdd(%start, vectorScale(%this.player.getEyeVector(), %this.viewDistance));
|
||||||
|
|
||||||
// %rayCast = containerRayCast(%start, %end, -1, %this.player);
|
// %rayCast = containerRayCast(%start, %end, -1, %this.player);
|
||||||
// %hitObject = getWord(%raycast, 0);
|
// %hitObject = getWord(%raycast, 0);
|
||||||
|
|
||||||
// if (%hitObject == %current)
|
// if (%hitObject == %current)
|
||||||
|
|
@ -421,12 +395,12 @@ function AIConnection::updateVisualAcuity(%this)
|
||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now we run some logic on some things that we no longer can see.
|
// Now we run some logic on some things that we no longer can see.
|
||||||
for (%iteration = 0; %iteration < %this.visibleHostiles.getCount(); %iteration++)
|
for (%iteration = 0; %iteration < %this.visibleHostiles.getCount(); %iteration++)
|
||||||
{
|
{
|
||||||
%current = %this.visibleHostiles.getObject(%iteration);
|
%current = %this.visibleHostiles.getObject(%iteration);
|
||||||
|
|
||||||
if (%this.visibleHostiles.isMember(%current) && !%visibleObjects.isMember(%current))
|
if (%this.visibleHostiles.isMember(%current) && !%visibleObjects.isMember(%current))
|
||||||
{
|
{
|
||||||
%this.awarenessTime[%current] -= %deltaTime;
|
%this.awarenessTime[%current] -= %deltaTime;
|
||||||
|
|
@ -437,7 +411,7 @@ function AIConnection::updateVisualAcuity(%this)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
%visibleObjects.delete();
|
%visibleObjects.delete();
|
||||||
%this.visualAcuityTick = %this.schedule(getRandom($DXAI::Bot::MinimumVisualAcuityTime, $DXAI::Bot::MaximumVisualAcuityTime), "updateVisualAcuity");
|
%this.visualAcuityTick = %this.schedule(getRandom($DXAI::Bot::MinimumVisualAcuityTime, $DXAI::Bot::MaximumVisualAcuityTime), "updateVisualAcuity");
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -148,6 +148,71 @@ function GameConnection::getClosestInventory(%this)
|
||||||
return %closestInventory;
|
return %closestInventory;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function Player::getFacingAngle(%this)
|
||||||
|
{
|
||||||
|
%vector = vectorNormalize(%this.getMuzzleVector($WeaponSlot));
|
||||||
|
return mAtan(getWord(%vector, 1), getWord(%vector, 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
function Player::getViewConeIntersection(%this, %target, %maxDistance, %viewAngle)
|
||||||
|
{
|
||||||
|
%myPosition = %this.getPosition();
|
||||||
|
%targetPosition = %target.getPosition();
|
||||||
|
|
||||||
|
if (vectorDist(%myPosition, %targetPosition) > %maxDistance)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
%offset = vectorNormalize(vectorSub(%targetPosition, %myPosition));
|
||||||
|
%enemyAngle = mAtan(getWord(%offset, 1), getWord(%offset, 0));
|
||||||
|
%myAngle = %this.getFacingAngle();
|
||||||
|
|
||||||
|
%angle = %enemyAngle - %myAngle;
|
||||||
|
|
||||||
|
%viewMax = %viewAngle / 2;
|
||||||
|
%viewMin = -(%viewAngle / 2);
|
||||||
|
|
||||||
|
if (%angle >= %viewMin && %angle <= %viewMax)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function Player::getPackName(%this)
|
||||||
|
{
|
||||||
|
%item = %this.getMountedImage(2).item;
|
||||||
|
|
||||||
|
if (!isObject(%item))
|
||||||
|
return "";
|
||||||
|
|
||||||
|
return %item.getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
function Player::canSeeObject(%this, %target, %maxDistance, %viewAngle)
|
||||||
|
{
|
||||||
|
if (%target.isCloaked() || !%this.getViewConeIntersection(%target, %maxDistance, %viewAngle))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
%coneOrigin = %this.getPosition();
|
||||||
|
|
||||||
|
// Try to raycast the object
|
||||||
|
%rayCast = containerRayCast(%coneOrigin, %target.getWorldBoxCenter(), $TypeMasks::AllObjectType, %this);
|
||||||
|
%hitObject = getWord(%raycast, 0);
|
||||||
|
|
||||||
|
// Since the engine doesn't do raycasts against projectiles & items correctly, we just check if the bot
|
||||||
|
// hit -nothing- when doing the raycast rather than checking for a hit against the object
|
||||||
|
%workaroundTypes = $TypeMasks::ProjectileObjectType | $TypeMasks::ItemObjectType;
|
||||||
|
|
||||||
|
if (%hitObject == %target || (%target.getType() & %workaroundTypes && !isObject(%hitObject)))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function Precipitation::isCloaked(%this) { return false; }
|
||||||
|
function LinearProjectile::isCloaked(%this) { return false; }
|
||||||
|
function LinearFlareProjectile::isCloaked(%this) { return false; }
|
||||||
|
function GrenadeProjectile::isCloaked(%this) { return false; }
|
||||||
|
|
||||||
//------------------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------------------
|
||||||
// Description: Calculates a list of objects that can be seen by the given client using
|
// Description: Calculates a list of objects that can be seen by the given client using
|
||||||
// distance & field of view values passed in for evaluation.
|
// distance & field of view values passed in for evaluation.
|
||||||
|
|
@ -176,43 +241,22 @@ function GameConnection::getObjectsInViewcone(%this, %typeMask, %distance, %perf
|
||||||
if (%distance $= "")
|
if (%distance $= "")
|
||||||
%distance = %this.viewDistance;
|
%distance = %this.viewDistance;
|
||||||
|
|
||||||
%viewCone = %this.calculateViewCone(%distance);
|
|
||||||
|
|
||||||
// Extract the results: See TODO above ::calculateViewCone implementation
|
|
||||||
%coneOrigin = getWords(%viewCone, 0, 2);
|
|
||||||
%viewConeClockwiseVector = getWords(%viewCone, 3, 5);
|
|
||||||
%viewConeCounterClockwiseVector = getWords(%viewCone, 6, 8);
|
|
||||||
%viewConeUpperVector = getWords(%viewCone, 9, 11);
|
|
||||||
%viewConeLowerVector = getWords(%viewCone, 12, 14);
|
|
||||||
|
|
||||||
%result = new SimSet();
|
%result = new SimSet();
|
||||||
|
|
||||||
|
%coneOrigin = %this.player.getPosition();
|
||||||
|
|
||||||
// Doing a radius search should hopefully be faster than iterating over all objects in MissionCleanup.
|
// Doing a radius search should hopefully be faster than iterating over all objects in MissionCleanup.
|
||||||
// Even if the game did that internally it's definitely faster than doing it in TS
|
// Even if the game did that internally it's definitely faster than doing it in TS
|
||||||
InitContainerRadiusSearch(%coneOrigin, %distance, %typeMask);
|
InitContainerRadiusSearch(%coneOrigin, %distance, %typeMask);
|
||||||
while((%currentObject = containerSearchNext()) != 0)
|
while((%currentObject = containerSearchNext()) != 0)
|
||||||
{
|
{
|
||||||
if (%currentObject == %this || !isObject(%currentObject) || containerSearchCurrRadDamageDist() > %distance)
|
if (%currentObject == %this || !isObject(%currentObject) || containerSearchCurrRadDamageDist() > %distance || %currentObject.isCloaked())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// Check if the object is within both the horizontal and vertical triangles representing our view cone
|
// Check if the object is within both the horizontal and vertical triangles representing our view cone
|
||||||
if (%currentObject.getType() & %typeMask && pointInTriangle(%currentObject.getPosition(), %viewConeClockwiseVector, %viewConeCounterClockwiseVector, %coneOrigin) && pointInTriangle(%currentObject.getPosition(), %viewConeLowerVector, %viewConeUpperVector, %coneOrigin))
|
if (%currentObject.getType() & %typeMask && %this.player.getViewConeIntersection(%currentObject, %distance, %this.fieldOfView))
|
||||||
{
|
if (!%performLOSTest || %this.player.canSeeObject(%currentObject, %distance, %this.fieldOfView))
|
||||||
if (!%performLOSTest)
|
|
||||||
%result.add(%currentObject);
|
%result.add(%currentObject);
|
||||||
else
|
|
||||||
{
|
|
||||||
%rayCast = containerRayCast(%coneOrigin, %currentObject.getWorldBoxCenter(), $TypeMasks::AllObjectType, %this.player);
|
|
||||||
|
|
||||||
%hitObject = getWord(%raycast, 0);
|
|
||||||
|
|
||||||
// Since the engine doesn't do raycasts against projectiles & items correctly, we just check if the bot
|
|
||||||
// hit -nothing- when doing the raycast rather than checking for a hit against the object
|
|
||||||
%workaroundTypes = $TypeMasks::ProjectileObjectType | $TypeMasks::ItemObjectType;
|
|
||||||
if (%hitObject == %currentObject || (%currentObject.getType() & %workaroundTypes && !isObject(%hitObject)))
|
|
||||||
%result.add(%currentObject);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return %result;
|
return %result;
|
||||||
|
|
@ -237,7 +281,7 @@ function getRandomPosition(%position, %distance, %raycast)
|
||||||
if (!%raycast)
|
if (!%raycast)
|
||||||
return %result;
|
return %result;
|
||||||
|
|
||||||
%rayCast = containerRayCast(%position, %result, $TypeMasks::AllObjectType, 0);
|
%rayCast = containerRayCast(%position, %result, $TypeMasks::InteriorObjectType | $TypeMasks::StaticShapeObjectType, 0);
|
||||||
%result = getWords(%raycast, 1, 3);
|
%result = getWords(%raycast, 1, 3);
|
||||||
|
|
||||||
return %result;
|
return %result;
|
||||||
|
|
@ -257,6 +301,18 @@ function getRandomPositionOnTerrain(%position, %distance)
|
||||||
return setWord(%result, 2, getTerrainHeight(%result));
|
return setWord(%result, 2, getTerrainHeight(%result));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getRandomPositionInInterior(%position, %distance)
|
||||||
|
{
|
||||||
|
%firstPass = getRandomPosition(%position, %distance, true);
|
||||||
|
|
||||||
|
%rayCast = containerRayCast(%position, vectorAdd(%position, "0 0 -9000"), $TypeMasks::InteriorObjectType | $TypeMasks::StaticShapeObjectType, 0);
|
||||||
|
|
||||||
|
if (%rayCast == -1)
|
||||||
|
return %firstPass;
|
||||||
|
|
||||||
|
return getWords(%raycast, 1, 3);
|
||||||
|
}
|
||||||
|
|
||||||
//------------------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------------------
|
||||||
// Description: Multiplies two vectors together and returns the result.
|
// Description: Multiplies two vectors together and returns the result.
|
||||||
// Param %vec1: The first vector to multiply.
|
// Param %vec1: The first vector to multiply.
|
||||||
|
|
|
||||||
|
|
@ -9,23 +9,64 @@
|
||||||
//------------------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
$DXAI::Loadouts[0, "Name"] = "Light Scout";
|
$DXAI::Loadouts[0, "Name"] = "Light Scout";
|
||||||
$DXAI::Loadouts[0, "Weapon", 0] = ChainGun;
|
$DXAI::Loadouts[0, "Weapon", 0] = Chaingun;
|
||||||
$DXAI::Loadouts[0, "Weapon", 1] = Disc;
|
$DXAI::Loadouts[0, "Weapon", 1] = Disc;
|
||||||
$DXAI::Loadouts[0, "Weapon", 2] = GrenadeLauncher;
|
$DXAI::Loadouts[0, "Weapon", 2] = GrenadeLauncher;
|
||||||
$DXAI::Loadouts[0, "Pack"] = EnergyPack;
|
$DXAI::Loadouts[0, "Pack"] = EnergyPack;
|
||||||
$DXAI::Loadouts[0, "WeaponCount"] = 3;
|
$DXAI::Loadouts[0, "WeaponCount"] = 3;
|
||||||
$DXAI::Loadouts[0, "Armor"] = "Light";
|
$DXAI::Loadouts[0, "Armor"] = "Light";
|
||||||
|
|
||||||
$DXAI::Loadouts[1, "Name"] = "Defender";
|
$DXAI::Loadouts[1, "Name"] = "Light Defender";
|
||||||
$DXAI::Loadouts[1, "Weapon", 0] = ChainGun;
|
$DXAI::Loadouts[1, "Weapon", 0] = Blaster;
|
||||||
$DXAI::Loadouts[1, "Weapon", 1] = Disc;
|
$DXAI::Loadouts[1, "Weapon", 1] = Disc;
|
||||||
$DXAI::Loadouts[1, "Weapon", 2] = GrenadeLauncher;
|
$DXAI::Loadouts[1, "Weapon", 2] = GrenadeLauncher;
|
||||||
$DXAI::Loadouts[1, "Weapon", 3] = GrenadeLauncher;
|
$DXAI::Loadouts[1, "Pack"] = EnergyPack;
|
||||||
$DXAI::Loadouts[1, "Pack"] = AmmoPack;
|
$DXAI::Loadouts[1, "WeaponCount"] = 3;
|
||||||
$DXAI::Loadouts[1, "WeaponCount"] = 4;
|
$DXAI::Loadouts[1, "Armor"] = "Light";
|
||||||
$DXAI::Loadouts[1, "Armor"] = "Medium";
|
|
||||||
|
|
||||||
$DXAI::OptimalLoadouts["AIEnhancedDefendLocation"] = "1";
|
$DXAI::Loadouts[2, "Name"] = "Medium Defender";
|
||||||
|
$DXAI::Loadouts[2, "Weapon", 0] = ChainGun;
|
||||||
|
$DXAI::Loadouts[2, "Weapon", 1] = Disc;
|
||||||
|
$DXAI::Loadouts[2, "Weapon", 2] = GrenadeLauncher;
|
||||||
|
$DXAI::Loadouts[2, "Weapon", 3] = Plasma;
|
||||||
|
$DXAI::Loadouts[2, "Pack"] = AmmoPack;
|
||||||
|
$DXAI::Loadouts[2, "WeaponCount"] = 4;
|
||||||
|
$DXAI::Loadouts[2, "Armor"] = "Medium";
|
||||||
|
|
||||||
$DXAI::Loadouts::Count = 2;
|
$DXAI::Loadouts[3, "Name"] = "Heavy Defender";
|
||||||
$DXAI::Loadouts::Default = 0;
|
$DXAI::Loadouts[3, "Weapon", 0] = ChainGun;
|
||||||
|
$DXAI::Loadouts[3, "Weapon", 1] = Disc;
|
||||||
|
$DXAI::Loadouts[3, "Weapon", 2] = GrenadeLauncher;
|
||||||
|
$DXAI::Loadouts[3, "Weapon", 3] = Mortar;
|
||||||
|
$DXAI::Loadouts[3, "Weapon", 4] = Plasma;
|
||||||
|
$DXAI::Loadouts[3, "Pack"] = AmmoPack;
|
||||||
|
$DXAI::Loadouts[3, "WeaponCount"] = 5;
|
||||||
|
$DXAI::Loadouts[3, "Armor"] = "Heavy";
|
||||||
|
|
||||||
|
$DXAI::Loadouts[4, "Name"] = "Hardened Defender";
|
||||||
|
$DXAI::Loadouts[4, "Weapon", 0] = ChainGun;
|
||||||
|
$DXAI::Loadouts[4, "Weapon", 1] = Disc;
|
||||||
|
$DXAI::Loadouts[4, "Weapon", 2] = GrenadeLauncher;
|
||||||
|
$DXAI::Loadouts[4, "Weapon", 3] = Mortar;
|
||||||
|
$DXAI::Loadouts[4, "Weapon", 4] = Plasma;
|
||||||
|
$DXAI::Loadouts[4, "Pack"] = ShieldPack;
|
||||||
|
$DXAI::Loadouts[4, "WeaponCount"] = 5;
|
||||||
|
$DXAI::Loadouts[4, "Armor"] = "Heavy";
|
||||||
|
|
||||||
|
$DXAI::Loadouts[5, "Name"] = "Cloaked Scout";
|
||||||
|
$DXAI::Loadouts[5, "Weapon", 0] = Chaingun;
|
||||||
|
$DXAI::Loadouts[5, "Weapon", 1] = Disc;
|
||||||
|
$DXAI::Loadouts[5, "Weapon", 2] = GrenadeLauncher;
|
||||||
|
$DXAI::Loadouts[5, "Pack"] = CloakingPack;
|
||||||
|
$DXAI::Loadouts[5, "WeaponCount"] = 3;
|
||||||
|
$DXAI::Loadouts[5, "Armor"] = "Light";
|
||||||
|
|
||||||
|
$DXAI::OptimalLoadouts["AIEnhancedFlagCaptureTask"] = "0";
|
||||||
|
$DXAI::OptimalLoadouts["AIEnhancedScoutLocation"] = "0 5";
|
||||||
|
$DXAI::OptimalLoadouts["AIEnhancedDefendLocation"] = "2 3 4";
|
||||||
|
|
||||||
|
// A default loadout to use when the bot has no objective.
|
||||||
|
$DXAI::DefaultLoadout = 0;
|
||||||
|
|
||||||
|
$DXAI::Loadouts::Count = 6;
|
||||||
|
$DXAI::Loadouts::Default = 0;
|
||||||
|
|
|
||||||
|
|
@ -14,8 +14,8 @@ exec("scripts/DXAI/aicommander.cs");
|
||||||
exec("scripts/DXAI/aiconnection.cs");
|
exec("scripts/DXAI/aiconnection.cs");
|
||||||
exec("scripts/DXAI/priorityqueue.cs");
|
exec("scripts/DXAI/priorityqueue.cs");
|
||||||
exec("scripts/DXAI/cyclicset.cs");
|
exec("scripts/DXAI/cyclicset.cs");
|
||||||
exec("scripts/DXAI/loadouts.cs");
|
|
||||||
exec("scripts/DXAI/weaponProfiler.cs");
|
exec("scripts/DXAI/weaponProfiler.cs");
|
||||||
|
exec("scripts/DXAI/loadouts.cs");
|
||||||
|
|
||||||
//------------------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------------------
|
||||||
// Description: This cleanup function is called when the mission ends to clean up all
|
// Description: This cleanup function is called when the mission ends to clean up all
|
||||||
|
|
@ -25,10 +25,10 @@ exec("scripts/DXAI/weaponProfiler.cs");
|
||||||
function DXAI::cleanup()
|
function DXAI::cleanup()
|
||||||
{
|
{
|
||||||
$DXAI::System::Setup = false;
|
$DXAI::System::Setup = false;
|
||||||
|
|
||||||
for (%iteration = 1; %iteration < $DXAI::ActiveCommanderCount + 1; %iteration++)
|
for (%iteration = 1; %iteration < $DXAI::ActiveCommanderCount + 1; %iteration++)
|
||||||
$DXAI::ActiveCommander[%iteration].delete();
|
$DXAI::ActiveCommander[%iteration].delete();
|
||||||
|
|
||||||
$DXAI::ActiveCommanderCount = 0;
|
$DXAI::ActiveCommanderCount = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -44,37 +44,37 @@ function DXAI::setup(%numTeams)
|
||||||
// Mark the environment as invalidated for each new run so that our hooks
|
// Mark the environment as invalidated for each new run so that our hooks
|
||||||
// can be verified
|
// can be verified
|
||||||
$DXAI::System::InvalidatedEnvironment = true;
|
$DXAI::System::InvalidatedEnvironment = true;
|
||||||
|
|
||||||
// Set our setup flag so that the execution hooks can behave correctly
|
// Set our setup flag so that the execution hooks can behave correctly
|
||||||
$DXAI::System::Setup = true;
|
$DXAI::System::Setup = true;
|
||||||
|
|
||||||
// Create the AIGrenadeSet to hold known grenades.
|
// Create the AIGrenadeSet to hold known grenades.
|
||||||
new SimSet(AIGrenadeSet);
|
new SimSet(AIGrenadeSet);
|
||||||
|
|
||||||
for (%iteration = 1; %iteration < %numTeams + 1; %iteration++)
|
for (%iteration = 1; %iteration < %numTeams + 1; %iteration++)
|
||||||
{
|
{
|
||||||
%commander = new ScriptObject() { class = "AICommander"; team = %iteration; };
|
%commander = new ScriptObject() { class = "AICommander"; team = %iteration; };
|
||||||
%commander.setup();
|
%commander.setup();
|
||||||
|
|
||||||
$DXAI::ActiveCommander[%iteration] = %commander;
|
$DXAI::ActiveCommander[%iteration] = %commander;
|
||||||
%commander.loadObjectives();
|
%commander.loadObjectives();
|
||||||
%commander.assignTasks();
|
%commander.assignTasks();
|
||||||
}
|
}
|
||||||
|
|
||||||
// And setup the default values
|
// And setup the default values
|
||||||
for (%iteration = 0; %iteration < ClientGroup.getCount(); %iteration++)
|
for (%iteration = 0; %iteration < ClientGroup.getCount(); %iteration++)
|
||||||
{
|
{
|
||||||
%currentClient = ClientGroup.getObject(%iteration);
|
%currentClient = ClientGroup.getObject(%iteration);
|
||||||
|
|
||||||
%currentClient.viewDistance = $DXAI::Bot::DefaultViewDistance;
|
%currentClient.viewDistance = $DXAI::Bot::DefaultViewDistance;
|
||||||
%currentClient.fieldOfView = $DXAI::Bot::DefaultFieldOfView;
|
%currentClient.fieldOfView = $DXAI::Bot::DefaultFieldOfView;
|
||||||
}
|
}
|
||||||
|
|
||||||
$DXAI::ActiveCommanderCount = %numTeams;
|
$DXAI::ActiveCommanderCount = %numTeams;
|
||||||
}
|
}
|
||||||
|
|
||||||
//------------------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------------------
|
||||||
// Why: Due to the way the AI system must hook into some functions and the way game
|
// Why: Due to the way the AI system must hook into some functions and the way game
|
||||||
// modes work, we must generate runtime overrides for some gamemode related functions. We
|
// 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
|
// 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
|
// we'll need to hook those functions post-start as the game mode scripts are executed for
|
||||||
|
|
@ -83,7 +83,7 @@ function DXAI::setup(%numTeams)
|
||||||
// check that the hooks we need are actually active if the system detects that may be a
|
// 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
|
// 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.
|
// call made during runtime as any given exec can overwrite the hooks we required.
|
||||||
// If they were not overwritten, the function will return 11595 and do nothing else if the
|
// If they were not overwritten, the function will return 11595 and do nothing else if the
|
||||||
// appropriate dummy parameters are passed in.
|
// appropriate dummy parameters are passed in.
|
||||||
//
|
//
|
||||||
// TODO: Perhaps calculate %numTeams from the game object?
|
// TODO: Perhaps calculate %numTeams from the game object?
|
||||||
|
|
@ -91,29 +91,29 @@ function DXAI::setup(%numTeams)
|
||||||
function DXAI::validateEnvironment()
|
function DXAI::validateEnvironment()
|
||||||
{
|
{
|
||||||
%gameModeName = $CurrentMissionType @ "Game";
|
%gameModeName = $CurrentMissionType @ "Game";
|
||||||
|
|
||||||
%payloadTemplate = %payload = "function " @ %gameModeName @ "::<METHODNAME>() { return DefaultGame::<METHODNAME>($DXAI::System::RuntimeDummy); } ";
|
%boundPayloadTemplate = "function " @ %gameModeName @ "::<METHODNAME>() { return DefaultGame::<METHODNAME>($DXAI::System::RuntimeDummy); } ";
|
||||||
|
%payloadTemplate = "function <METHODNAME>() { return <METHODNAME>($DXAI::System::RuntimeDummy); } ";
|
||||||
if (game.AIChooseGameObjective($DXAI::System::RuntimeDummy) != 11595)
|
if (game.AIChooseGameObjective($DXAI::System::RuntimeDummy) != 11595)
|
||||||
{
|
{
|
||||||
error("DXAI: Function 'DefaultGame::AIChooseGameObjective' detected to be overwritten by the current gamemode. Correcting ...");
|
error("DXAI: Function 'DefaultGame::AIChooseGameObjective' detected to be overwritten by the current gamemode. Correcting ...");
|
||||||
|
|
||||||
eval(strReplace(%payloadTemplate, "<METHODNAME>", "AIChooseGameObjective"));
|
eval(strReplace(%boundPayloadTemplate, "<METHODNAME>", "AIChooseGameObjective"));
|
||||||
|
|
||||||
// Make sure the patch took
|
// Make sure the patch took
|
||||||
if (game.AIChooseGameObjective($DXAI::System::RuntimeDummy) != 11595)
|
if (game.AIChooseGameObjective($DXAI::System::RuntimeDummy) != 11595)
|
||||||
error("DXAI: Failed to patch 'DefaultGame::AIChooseGameObjective'! DXAI may not function correctly.");
|
error("DXAI: Failed to patch 'DefaultGame::AIChooseGameObjective'! DXAI may not function correctly.");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (game.onAIRespawn($DXAI::System::RuntimeDummy) != 11595)
|
if (onAIRespawn($DXAI::System::RuntimeDummy) != 11595)
|
||||||
{
|
{
|
||||||
error("DXAI: Function 'DefaultGame::onAIRespawn' detected to be overwritten by the current gamemode. Correcting ... ");
|
error("DXAI: Function 'onAIRespawn' detected to be overwritten by the current gamemode. Correcting ... ");
|
||||||
|
|
||||||
eval(strReplace(%payloadTemplate, "<METHODNAME>", "onAIRespawn"));
|
eval(strReplace(%payloadTemplate, "<METHODNAME>", "onAIRespawn"));
|
||||||
|
|
||||||
if (game.onAIRespawn($DXAI::System::RuntimeDummy) != 11595)
|
if (game.onAIRespawn($DXAI::System::RuntimeDummy) != 11595)
|
||||||
error("DXAI: Failed to patch 'DefaultGame::onAIRespawn'! DXAI may not function correctly.");
|
error("DXAI: Failed to patch 'onAIRespawn'! DXAI may not function correctly.");
|
||||||
}
|
}
|
||||||
|
|
||||||
$DXAI::System::InvalidatedEnvironment = false;
|
$DXAI::System::InvalidatedEnvironment = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -129,18 +129,18 @@ function DXAI::update()
|
||||||
{
|
{
|
||||||
if (isEventPending($DXAI::updateHandle))
|
if (isEventPending($DXAI::updateHandle))
|
||||||
cancel($DXAI::updateHandle);
|
cancel($DXAI::updateHandle);
|
||||||
|
|
||||||
if (!isObject(Game))
|
if (!isObject(Game))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Check if the bound functions are overwritten by the current gamemode, or if something
|
// Check if the bound functions are overwritten by the current gamemode, or if something
|
||||||
// may have invalidated our hooks
|
// may have invalidated our hooks
|
||||||
if ($DXAI::System::InvalidatedEnvironment && $DXAI::System::Setup)
|
if ($DXAI::System::InvalidatedEnvironment && $DXAI::System::Setup)
|
||||||
DXAI::validateEnvironment();
|
DXAI::validateEnvironment();
|
||||||
|
|
||||||
for (%iteration = 1; %iteration < $DXAI::ActiveCommanderCount + 1; %iteration++)
|
for (%iteration = 1; %iteration < $DXAI::ActiveCommanderCount + 1; %iteration++)
|
||||||
$DXAI::ActiveCommander[%iteration].update();
|
$DXAI::ActiveCommander[%iteration].update();
|
||||||
|
|
||||||
// Apparently we can't schedule a bound function otherwise
|
// Apparently we can't schedule a bound function otherwise
|
||||||
$DXAI::updateHandle = schedule(32, 0, "eval", "DXAI::update();");
|
$DXAI::updateHandle = schedule(32, 0, "eval", "DXAI::update();");
|
||||||
}
|
}
|
||||||
|
|
@ -151,6 +151,11 @@ function DXAI::notifyPlayerDeath(%killed, %killedBy)
|
||||||
$DXAI::ActiveCommander[%iteration].notifyPlayerDeath(%killed, %killedBy);
|
$DXAI::ActiveCommander[%iteration].notifyPlayerDeath(%killed, %killedBy);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function DXAI::notifyFlagGrab(%grabbedBy, %flagTeam)
|
||||||
|
{
|
||||||
|
$DXAI::ActiveCommander[%flagTeam].notifyFlagGrab(%grabbedBy);
|
||||||
|
}
|
||||||
|
|
||||||
//------------------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------------------
|
||||||
// Description: There is a series of functions that the AI code can safely hook without
|
// 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
|
// worry of being overwritten implicitly such as the disconnect or exec functions. For
|
||||||
|
|
@ -167,10 +172,10 @@ package DXAI_Hooks
|
||||||
function DefaultGame::gameOver(%game)
|
function DefaultGame::gameOver(%game)
|
||||||
{
|
{
|
||||||
parent::gameOver(%game);
|
parent::gameOver(%game);
|
||||||
|
|
||||||
DXAI::cleanup();
|
DXAI::cleanup();
|
||||||
}
|
}
|
||||||
|
|
||||||
//------------------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------------------
|
||||||
// Description: Called when the mission starts. We use this to perform initialization and
|
// Description: Called when the mission starts. We use this to perform initialization and
|
||||||
// to start the update ticks.
|
// to start the update ticks.
|
||||||
|
|
@ -178,11 +183,11 @@ package DXAI_Hooks
|
||||||
function DefaultGame::startMatch(%game)
|
function DefaultGame::startMatch(%game)
|
||||||
{
|
{
|
||||||
parent::startMatch(%game);
|
parent::startMatch(%game);
|
||||||
|
|
||||||
DXAI::setup(%game.numTeams);
|
DXAI::setup(%game.numTeams);
|
||||||
DXAI::update();
|
DXAI::update();
|
||||||
}
|
}
|
||||||
|
|
||||||
//------------------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------------------
|
||||||
// Description: We hook the disconnect function as a step to fix console spam from leaving
|
// 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
|
// a listen server due to the AI code continuing to run post-server shutdown in those
|
||||||
|
|
@ -191,10 +196,10 @@ package DXAI_Hooks
|
||||||
function disconnect()
|
function disconnect()
|
||||||
{
|
{
|
||||||
parent::disconnect();
|
parent::disconnect();
|
||||||
|
|
||||||
DXAI::cleanup();
|
DXAI::cleanup();
|
||||||
}
|
}
|
||||||
|
|
||||||
//------------------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------------------
|
||||||
// Description: In the game, bots can be made to change teams which means we need to hook
|
// 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.
|
// this event so that commander affiliations can be properly updated.
|
||||||
|
|
@ -203,12 +208,12 @@ package DXAI_Hooks
|
||||||
{
|
{
|
||||||
// Remove us from the old commander's control first
|
// Remove us from the old commander's control first
|
||||||
$DXAI::ActiveCommander[%client.team].removeBot(%client);
|
$DXAI::ActiveCommander[%client.team].removeBot(%client);
|
||||||
|
|
||||||
parent::AIChangeTeam(%game, %client, %newTeam);
|
parent::AIChangeTeam(%game, %client, %newTeam);
|
||||||
|
|
||||||
$DXAI::ActiveCommander[%newTeam].addBot(%client);
|
$DXAI::ActiveCommander[%newTeam].addBot(%client);
|
||||||
}
|
}
|
||||||
|
|
||||||
//------------------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------------------
|
||||||
// Description: In the game, bots can be kicked like regular players so we hook this to
|
// 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.
|
// ensure that commanders are properly notified of lesser bot counts.
|
||||||
|
|
@ -217,25 +222,25 @@ package DXAI_Hooks
|
||||||
{
|
{
|
||||||
if (isObject(%client.commander))
|
if (isObject(%client.commander))
|
||||||
%client.commander.removeBot(%client);
|
%client.commander.removeBot(%client);
|
||||||
|
|
||||||
parent::onAIDrop(%client);
|
parent::onAIDrop(%client);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hooks for AI System notification
|
// Hooks for AI System notification
|
||||||
function DefaultGame::onClientKilled(%game, %clVictim, %clKiller, %damageType, %implement, %damageLocation)
|
function DefaultGame::onClientKilled(%game, %clVictim, %clKiller, %damageType, %implement, %damageLocation)
|
||||||
{
|
{
|
||||||
parent::onClientKilled(%game, %clVictim, %clKiller, %damageType, %implement, %damageLocation);
|
parent::onClientKilled(%game, %clVictim, %clKiller, %damageType, %implement, %damageLocation);
|
||||||
|
|
||||||
DXAI::notifyPlayerDeath(%clVictim, %clKiller);
|
DXAI::notifyPlayerDeath(%clVictim, %clKiller);
|
||||||
}
|
}
|
||||||
|
|
||||||
function DefaultGame::onAIKilled(%game, %clVictim, %clKiller, %damageType, %implement)
|
function DefaultGame::onAIKilled(%game, %clVictim, %clKiller, %damageType, %implement)
|
||||||
{
|
{
|
||||||
parent::onAIKilled(%game, %clVictim, %clKiller, %damageType, %implement);
|
parent::onAIKilled(%game, %clVictim, %clKiller, %damageType, %implement);
|
||||||
|
|
||||||
DXAI::notifyPlayerDeath(%clVictim, %clKiller);
|
DXAI::notifyPlayerDeath(%clVictim, %clKiller);
|
||||||
}
|
}
|
||||||
|
|
||||||
//------------------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------------------
|
||||||
// Description: We hook this function to implement some basic sound simulation for bots.
|
// 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
|
// This means that if something explodes, a bot will hear it and if the sound is close
|
||||||
|
|
@ -244,37 +249,37 @@ package DXAI_Hooks
|
||||||
function ProjectileData::onExplode(%data, %proj, %pos, %mod)
|
function ProjectileData::onExplode(%data, %proj, %pos, %mod)
|
||||||
{
|
{
|
||||||
parent::onExplode(%data, %proj, %pos, %mod);
|
parent::onExplode(%data, %proj, %pos, %mod);
|
||||||
|
|
||||||
// Look for any bots nearby
|
// Look for any bots nearby
|
||||||
InitContainerRadiusSearch(%pos, 100, $TypeMasks::PlayerObjectType);
|
InitContainerRadiusSearch(%pos, 100, $TypeMasks::PlayerObjectType);
|
||||||
|
|
||||||
while ((%targetObject = containerSearchNext()) != 0)
|
while ((%targetObject = containerSearchNext()) != 0)
|
||||||
{
|
{
|
||||||
%currentDistance = containerSearchCurrRadDamageDist();
|
%currentDistance = containerSearchCurrRadDamageDist();
|
||||||
|
|
||||||
if (%currentDistance > 100 || !%targetObject.client.isAIControlled())
|
if (%currentDistance > 100 || !%targetObject.client.isAIControlled())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// Get the projectile team
|
// Get the projectile team
|
||||||
%projectileTeam = -1;
|
%projectileTeam = -1;
|
||||||
if (isObject(%proj.sourceObject))
|
if (isObject(%proj.sourceObject))
|
||||||
%projectileTeam = %proj.sourceObject.client.team;
|
%projectileTeam = %proj.sourceObject.client.team;
|
||||||
|
|
||||||
// Determine if we should run based on team & Team damage
|
// Determine if we should run based on team & Team damage
|
||||||
%shouldRun = false;
|
%shouldRun = false;
|
||||||
if (isObject(%proj.sourceObject) && %projectileTeam == %targetObject.client.team && $TeamDamage)
|
if (isObject(%proj.sourceObject) && %projectileTeam == %targetObject.client.team && $TeamDamage)
|
||||||
%shouldRun = true;
|
%shouldRun = true;
|
||||||
else if (isObject(%proj.sourceObject) && %projectileTeam != %targetObject.client.team)
|
else if (isObject(%proj.sourceObject) && %projectileTeam != %targetObject.client.team)
|
||||||
%shouldRun = true;
|
%shouldRun = true;
|
||||||
|
|
||||||
// Determine if we 'heard' it. The sound distance seems to be roughly 55m or less and we check the maxDistance
|
// 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
|
// IIRC The 55m distance should scale with the min/max distances and volume but I'm not sure how those interact
|
||||||
%heardHit = false;
|
%heardHit = false;
|
||||||
%hitDistance = vectorDist(%targetObject.getWorldBoxCenter(), %pos);
|
%hitDistance = vectorDist(%targetObject.getWorldBoxCenter(), %pos);
|
||||||
|
|
||||||
if (%hitDistance <= 20 && %hitDistance <= %data.explosion.soundProfile.description.maxDistance)
|
if (%hitDistance <= 20 && %hitDistance <= %data.explosion.soundProfile.description.maxDistance)
|
||||||
%heardHit = true;
|
%heardHit = true;
|
||||||
|
|
||||||
// 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
|
// 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
|
||||||
if (%data.indirectDamage != 0 && %heardHit)
|
if (%data.indirectDamage != 0 && %heardHit)
|
||||||
{
|
{
|
||||||
|
|
@ -282,26 +287,26 @@ package DXAI_Hooks
|
||||||
// TODO: Perhaps attempt to discern the direction of fire?
|
// TODO: Perhaps attempt to discern the direction of fire?
|
||||||
%targetObject.client.aimAt(%pos);
|
%targetObject.client.aimAt(%pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we should care and it wasn't a teammate projectile, notify
|
// If we should care and it wasn't a teammate projectile, notify
|
||||||
if (%shouldRun && %projectileTeam != %targetObject.client.team)
|
if (%shouldRun && %projectileTeam != %targetObject.client.team)
|
||||||
%targetObject.client.notifyProjectileImpact(%data, %proj, %pos);
|
%targetObject.client.notifyProjectileImpact(%data, %proj, %pos);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//------------------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------------------
|
||||||
// Description: This function is hooked so that we can try and guarantee that the DXAI
|
// Description: This function is hooked so that we can try and guarantee that the DXAI
|
||||||
// gamemode hooks still exist in the runtime as game mode scripts are executed for each
|
// gamemode hooks still exist in the runtime as game mode scripts are executed for each
|
||||||
// mission load.
|
// mission load.
|
||||||
//------------------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------------------
|
||||||
function CreateServer(%mission, %missionType)
|
function CreateServer(%mission, %missionType)
|
||||||
{
|
{
|
||||||
// Perform the default exec's
|
// Perform the default exec's
|
||||||
parent::CreateServer(%mission, %missionType);
|
parent::CreateServer(%mission, %missionType);
|
||||||
|
|
||||||
// Ensure that the DXAI is active.
|
// Ensure that the DXAI is active.
|
||||||
DXAI::validateEnvironment();
|
DXAI::validateEnvironment();
|
||||||
|
|
||||||
// Run our profiler here as well.
|
// Run our profiler here as well.
|
||||||
WeaponProfiler::run(false);
|
WeaponProfiler::run(false);
|
||||||
}
|
}
|
||||||
|
|
@ -316,24 +321,34 @@ package DXAI_Hooks
|
||||||
{
|
{
|
||||||
AIGrenadeSet.add(%projectile);
|
AIGrenadeSet.add(%projectile);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make this do nothing so the bots don't ever get any objectives by default
|
// Make this do nothing so the bots don't ever get any objectives by default
|
||||||
function DefaultGame::AIChooseGameObjective(%game, %client) { return 11595; }
|
function DefaultGame::AIChooseGameObjective(%game, %client) { return 11595; }
|
||||||
|
|
||||||
function DefaultGame::onAIRespawn(%game, %client)
|
function onAIRespawn(%client)
|
||||||
{
|
{
|
||||||
// Make sure the bot has no objectives
|
if (%client != $DXAI::System::RuntimeDummy)
|
||||||
// %client.reset();
|
parent::onAIRespawn(%client);
|
||||||
// %client.defaultTasksAdded = true;
|
|
||||||
|
// 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;
|
||||||
%client.shouldRearm = true;
|
%client.shouldRearm = true;
|
||||||
%client.targetLoadout = 1;
|
|
||||||
|
|
||||||
%client.engageTargetLastPosition = "";
|
%client.engageTargetLastPosition = "";
|
||||||
%client.engageTarget = -1;
|
%client.engageTarget = -1;
|
||||||
|
|
||||||
return 11595;
|
return 11595;
|
||||||
}
|
}
|
||||||
|
|
||||||
// We package hook the exec() and compile() functions to perform execution environment
|
// 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
|
// 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
|
// hooked by DXAI. This can happen with gamemode specific events because DXAI only hooks into
|
||||||
|
|
@ -343,13 +358,13 @@ package DXAI_Hooks
|
||||||
$DXAI::System::InvalidatedEnvironment = true;
|
$DXAI::System::InvalidatedEnvironment = true;
|
||||||
parent::exec(%file);
|
parent::exec(%file);
|
||||||
}
|
}
|
||||||
|
|
||||||
function compile(%file)
|
function compile(%file)
|
||||||
{
|
{
|
||||||
$DXAI::System::InvalidatedEnvironment = true;
|
$DXAI::System::InvalidatedEnvironment = true;
|
||||||
parent::compile(%file);
|
parent::compile(%file);
|
||||||
}
|
}
|
||||||
|
|
||||||
function AIRespondToEvent(%client, %eventTag, %targetClient)
|
function AIRespondToEvent(%client, %eventTag, %targetClient)
|
||||||
{
|
{
|
||||||
%clientPos = %client.player.getWorldBoxCenter();
|
%clientPos = %client.player.getWorldBoxCenter();
|
||||||
|
|
@ -359,45 +374,110 @@ package DXAI_Hooks
|
||||||
schedule(2000, %targetClient, "AIPlayAnimSound", %targetClient, %clientPos, ObjectiveNameToVoice(%targetClient), $AIAnimSalute, $AIAnimSalute, 0);
|
schedule(2000, %targetClient, "AIPlayAnimSound", %targetClient, %clientPos, ObjectiveNameToVoice(%targetClient), $AIAnimSalute, $AIAnimSalute, 0);
|
||||||
schedule(3700, %targetClient, "AIPlayAnimSound", %targetClient, %clientPos, "vqk.sorry", $AIAnimSalute, $AIAnimSalute, 0);
|
schedule(3700, %targetClient, "AIPlayAnimSound", %targetClient, %clientPos, "vqk.sorry", $AIAnimSalute, $AIAnimSalute, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
function AISystemEnabled(%enabled)
|
function AISystemEnabled(%enabled)
|
||||||
{
|
{
|
||||||
parent::AISystemEnabled(%enabled);
|
parent::AISystemEnabled(%enabled);
|
||||||
|
|
||||||
echo(%enabled);
|
|
||||||
$DXAI::AISystemEnabled = %enabled;
|
$DXAI::AISystemEnabled = %enabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
function AIConnection::onAIDrop(%client)
|
function AIConnection::onAIDrop(%client)
|
||||||
{
|
{
|
||||||
parent::onAIDrop(%client);
|
parent::onAIDrop(%client);
|
||||||
|
|
||||||
if (isObject(%client.visibleHostiles))
|
if (isObject(%client.visibleHostiles))
|
||||||
%client.visibleHostiles.delete();
|
%client.visibleHostiles.delete();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 Station::stationTriggered(%data, %obj, %isTriggered)
|
function Station::stationTriggered(%data, %obj, %isTriggered)
|
||||||
{
|
{
|
||||||
parent::stationTriggered(%data, %obj, %isTriggered);
|
parent::stationTriggered(%data, %obj, %isTriggered);
|
||||||
|
|
||||||
|
%triggeringClient = %obj.triggeredBy.client;
|
||||||
|
|
||||||
|
if (!isObject(%triggeringClient) || !%triggeringClient.isAIControlled())
|
||||||
|
return;
|
||||||
|
|
||||||
// TODO: If the bot isn't supposed to be on the station, at least restock ammunition?
|
// TODO: If the bot isn't supposed to be on the station, at least restock ammunition?
|
||||||
// FIXME: Can bots trigger dead stations?
|
// FIXME: Can bots trigger dead stations?
|
||||||
if (%isTriggered && %obj.triggeredBy.client.isAIControlled() && %obj.triggeredBy.client.shouldRearm)
|
if (%isTriggered && %triggeringClient.shouldRearm)
|
||||||
{
|
{
|
||||||
%bot = %obj.triggeredBy.client;
|
%triggeringClient.shouldRearm = false;
|
||||||
|
%triggeringClient.player.clearInventory();
|
||||||
%bot.shouldRearm = false;
|
|
||||||
%bot.player.clearInventory();
|
// Decide what the bot should pick
|
||||||
|
%targetLoadout = $DXAI::DefaultLoadout;
|
||||||
%bot.player.setArmor($DXAI::Loadouts[%bot.targetLoadout, "Armor"]);
|
|
||||||
%bot.player.setInventory($DXAI::Loadouts[%bot.targetLoadout, "Pack"], 1, true);
|
if ($DXAI::OptimalLoadouts[%triggeringCLient.primaryTask] !$= "")
|
||||||
|
|
||||||
for (%iteration = 0; %iteration < $DXAI::Loadouts[%bot.targetLoadout, "WeaponCount"]; %iteration++)
|
|
||||||
{
|
{
|
||||||
%bot.player.setInventory($DXAI::Loadouts[%bot.targetLoadout, "Weapon", %iteration], 1, true);
|
%count = getWordCount($DXAI::OptimalLoadouts[%triggeringCLient.primaryTask]);
|
||||||
%bot.player.setInventory($DXAI::Loadouts[%bot.targetLoadout, "Weapon", %iteration].Image.Ammo, 999, true); // TODO: Make it actually top out correctly!
|
%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++)
|
||||||
|
{
|
||||||
|
%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);
|
||||||
|
}
|
||||||
|
|
||||||
|
%triggeringClient.currentLoadout = %targetLoadout;
|
||||||
|
|
||||||
|
// Always use the first weapon
|
||||||
|
%triggeringClient.player.use($DXAI::Loadouts[%targetLoadout, "Weapon", 0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
//------------------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------------------
|
||||||
// main.cs
|
// main.cs
|
||||||
// Source file for the DXAI enhanced objective implementations.
|
// Source file for the DXAI enhanced objective implementations.
|
||||||
// https://github.com/Ragora/T2-DXAI.git
|
// https://github.com/Ragora/T2-DXAI.git
|
||||||
//
|
//
|
||||||
// Copyright (c) 2015 Robert MacGregor
|
// Copyright (c) 2015 Robert MacGregor
|
||||||
|
|
@ -12,14 +12,14 @@ $DXAI::Task::LowPriority = 100;
|
||||||
$DXAI::Task::MediumPriority = 200;
|
$DXAI::Task::MediumPriority = 200;
|
||||||
$DXAI::Task::HighPriority = 500;
|
$DXAI::Task::HighPriority = 500;
|
||||||
$DXAI::Task::VeryHighPriority = 1000;
|
$DXAI::Task::VeryHighPriority = 1000;
|
||||||
$DXAI::Task::ReservedPriority = 5000;
|
$DXAI::Task::ReservedPriority = 5000;
|
||||||
|
|
||||||
//------------------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------------------
|
||||||
// +Param %bot.escortTarget: The ID of the object to escort. This can be literally
|
// +Param %bot.escortTarget: The ID of the object to escort. This can be literally
|
||||||
// any object that exists in the game world.
|
// any object that exists in the game world.
|
||||||
// +Description: The AIEnhancedDefendLocation does exactly as the name implies. The
|
// +Description: The AIEnhancedDefendLocation does exactly as the name implies. The
|
||||||
// behavior a bot will exhibit with this code is that the bot will attempt to first to
|
// behavior a bot will exhibit with this code is that the bot will attempt to first to
|
||||||
// the location desiginated by %bot.defendLocation. Once the bot is in range, it will
|
// the location desiginated by %bot.defendLocation. Once the bot is in range, it will
|
||||||
// idly step about near the defense location, performing a sort of short range scouting.
|
// idly step about near the defense location, performing a sort of short range scouting.
|
||||||
// If the bot were to be knocked too far away, then this logic will simply start all over
|
// If the bot were to be knocked too far away, then this logic will simply start all over
|
||||||
// again.
|
// again.
|
||||||
|
|
@ -30,20 +30,20 @@ function AIEnhancedEscort::retire(%task, %client) { %client.isMoving = false; %c
|
||||||
function AIEnhancedEscort::weight(%task, %client) { %task.setWeight($DXAI::Task::MediumPriority); }
|
function AIEnhancedEscort::weight(%task, %client) { %task.setWeight($DXAI::Task::MediumPriority); }
|
||||||
|
|
||||||
function AIEnhancedEscort::monitor(%task, %client)
|
function AIEnhancedEscort::monitor(%task, %client)
|
||||||
{
|
{
|
||||||
// Is our escort object even a thing?
|
// Is our escort object even a thing?
|
||||||
if (!isObject(%client.escortTarget))
|
if (!isObject(%client.escortTarget))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
%escortLocation = %client.escortTarget.getPosition();
|
%escortLocation = %client.escortTarget.getPosition();
|
||||||
|
|
||||||
// Pick a location near the target
|
// Pick a location near the target
|
||||||
// FIXME: Only update randomly every so often, or perhaps update using the target's move direction & velocity?
|
// FIXME: Only update randomly every so often, or perhaps update using the target's move direction & velocity?
|
||||||
// TODO: Keep a minimum distance from the escort target, prevents crowding and accidental vehicular death.
|
// TODO: Keep a minimum distance from the escort target, prevents crowding and accidental vehicular death.
|
||||||
%client.isMoving = true;
|
%client.isMoving = true;
|
||||||
%client.manualAim = true;
|
%client.manualAim = true;
|
||||||
%client.aimLocation = %escortLocation;
|
%client.aimLocation = %escortLocation;
|
||||||
|
|
||||||
%client.setMoveTarget(getRandomPositionOnTerrain(%escortLocation, 40));
|
%client.setMoveTarget(getRandomPositionOnTerrain(%escortLocation, 40));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -52,7 +52,7 @@ function AIEnhancedEscort::monitor(%task, %client)
|
||||||
// must attempt to defend.
|
// must attempt to defend.
|
||||||
// +Description: The AIEnhancedDefendLocation does exactly as the name implies. The
|
// +Description: The AIEnhancedDefendLocation does exactly as the name implies. The
|
||||||
// behavior a bot will exhibit with this code is that the bot will attempt to first to
|
// behavior a bot will exhibit with this code is that the bot will attempt to first to
|
||||||
// the location desiginated by %bot.defendLocation. Once the bot is in range, it will
|
// the location desiginated by %bot.defendLocation. Once the bot is in range, it will
|
||||||
// idly step about near the defense location, performing a sort of short range scouting.
|
// idly step about near the defense location, performing a sort of short range scouting.
|
||||||
// If the bot were to be knocked too far away, then this logic will simply start all over
|
// If the bot were to be knocked too far away, then this logic will simply start all over
|
||||||
// again.
|
// again.
|
||||||
|
|
@ -63,7 +63,7 @@ function AIEnhancedDefendLocation::retire(%task, %client) { %client.isMoving = f
|
||||||
function AIEnhancedDefendLocation::weight(%task, %client) { %task.setWeight($DXAI::Task::MediumPriority); }
|
function AIEnhancedDefendLocation::weight(%task, %client) { %task.setWeight($DXAI::Task::MediumPriority); }
|
||||||
|
|
||||||
function AIEnhancedDefendLocation::monitor(%task, %client)
|
function AIEnhancedDefendLocation::monitor(%task, %client)
|
||||||
{
|
{
|
||||||
if (%client.getPathDistance(%client.defendTargetLocation) <= 40)
|
if (%client.getPathDistance(%client.defendTargetLocation) <= 40)
|
||||||
{
|
{
|
||||||
// Pick a random time to move to a nearby location
|
// Pick a random time to move to a nearby location
|
||||||
|
|
@ -72,19 +72,22 @@ function AIEnhancedDefendLocation::monitor(%task, %client)
|
||||||
%client.nextDefendRotation = getRandom(5000, 10000);
|
%client.nextDefendRotation = getRandom(5000, 10000);
|
||||||
%client.setMoveTarget(-1);
|
%client.setMoveTarget(-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we're near our random point, just don't move
|
// If we're near our random point, just don't move
|
||||||
if (%client.getPathDistance(%client.moveLocation) <= 10)
|
if (%client.getPathDistance(%client.moveLocation) <= 10)
|
||||||
%client.setMoveTarget(-1);
|
%client.setMoveTarget(-1);
|
||||||
|
|
||||||
%client.defendTime += 1024;
|
%client.defendTime += 1024;
|
||||||
if (%client.defendTime >= %client.nextDefendRotation)
|
if (%client.defendTime >= %client.nextDefendRotation)
|
||||||
{
|
{
|
||||||
%client.defendTime = 0;
|
%client.defendTime = 0;
|
||||||
%client.nextDefendRotation = getRandom(5000, 10000);
|
%client.nextDefendRotation = getRandom(5000, 10000);
|
||||||
|
|
||||||
// TODO: Replace with something that detects interiors as well
|
%nextPosition = NavGraph.nodeLoc(NavGraph.randNode(%client.player.getPosition(), 40, true, true));
|
||||||
%client.setMoveTarget(getRandomPositionOnTerrain(%client.defendTargetLocation, 40));
|
|
||||||
|
// If it isn't far enough, just pass on moving. This will help prevent bots jumping up in the air randomly.
|
||||||
|
if (vectorDist(%client.player.getPosition(), %nextPosition) > 5)
|
||||||
|
%client.setMoveTarget(%nextPosition);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
@ -114,16 +117,16 @@ function AIEnhancedScoutLocation::monitor(%task, %client)
|
||||||
{
|
{
|
||||||
if (%client.engageTarget)
|
if (%client.engageTarget)
|
||||||
return AIEnhancedScoutLocation::monitorEngage(%task, %client);
|
return AIEnhancedScoutLocation::monitorEngage(%task, %client);
|
||||||
|
|
||||||
// We can't really work without a NavGraph
|
// We can't really work without a NavGraph
|
||||||
if (!isObject(NavGraph))
|
if (!isObject(NavGraph))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// We just received the task, so find a node within distance of our scout location
|
// We just received the task, so find a node within distance of our scout location
|
||||||
if (%client.currentNode == -1)
|
if (%client.currentNode == -1)
|
||||||
{
|
{
|
||||||
%client.currentNode = NavGraph.randNode(%client.scoutTargetLocation, %client.scoutDistance, true, true);
|
%client.currentNode = NavGraph.randNode(%client.scoutTargetLocation, %client.scoutDistance, true, true);
|
||||||
|
|
||||||
if (%client.currentNode != -1)
|
if (%client.currentNode != -1)
|
||||||
%client.setMoveTarget(NavGraph.nodeLoc(%client.currentNode));
|
%client.setMoveTarget(NavGraph.nodeLoc(%client.currentNode));
|
||||||
}
|
}
|
||||||
|
|
@ -131,7 +134,7 @@ function AIEnhancedScoutLocation::monitor(%task, %client)
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
%pathDistance = %client.getPathDistance(%client.moveTarget);
|
%pathDistance = %client.getPathDistance(%client.moveTarget);
|
||||||
|
|
||||||
// Don't move if we're close enough to our next node
|
// Don't move if we're close enough to our next node
|
||||||
if (%pathDistance <= 40 && %client.isMovingToTarget)
|
if (%pathDistance <= 40 && %client.isMovingToTarget)
|
||||||
{
|
{
|
||||||
|
|
@ -146,7 +149,7 @@ function AIEnhancedScoutLocation::monitor(%task, %client)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
%client.scoutTime += 1024;
|
%client.scoutTime += 1024;
|
||||||
|
|
||||||
// Wait a little bit at each node
|
// Wait a little bit at each node
|
||||||
if (%client.scoutTime >= %client.nextScoutRotation)
|
if (%client.scoutTime >= %client.nextScoutRotation)
|
||||||
{
|
{
|
||||||
|
|
@ -155,12 +158,12 @@ function AIEnhancedScoutLocation::monitor(%task, %client)
|
||||||
|
|
||||||
// Pick a new node
|
// Pick a new node
|
||||||
%client.currentNode = NavGraph.randNode(%client.scoutTargetLocation, %client.scoutDistance, true, true);
|
%client.currentNode = NavGraph.randNode(%client.scoutTargetLocation, %client.scoutDistance, true, true);
|
||||||
|
|
||||||
// Ensure that we found a node.
|
// Ensure that we found a node.
|
||||||
if (%client.currentNode != -1)
|
if (%client.currentNode != -1)
|
||||||
%client.setMoveTarget(NavGraph.nodeLoc(%client.currentNode));
|
%client.setMoveTarget(NavGraph.nodeLoc(%client.currentNode));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function AIEnhancedScoutLocation::monitorEngage(%task, %client)
|
function AIEnhancedScoutLocation::monitorEngage(%task, %client)
|
||||||
|
|
@ -180,34 +183,62 @@ function AIEnhancedEngageTarget::initFromObjective(%task, %objective, %client) {
|
||||||
function AIEnhancedEngageTarget::assume(%task, %client) { %task.setMonitorFreq(1); }
|
function AIEnhancedEngageTarget::assume(%task, %client) { %task.setMonitorFreq(1); }
|
||||||
function AIEnhancedEngageTarget::retire(%task, %client) { }
|
function AIEnhancedEngageTarget::retire(%task, %client) { }
|
||||||
|
|
||||||
function AIEnhancedEngageTarget::weight(%task, %client)
|
function AIEnhancedEngageTarget::weight(%task, %client)
|
||||||
{
|
{
|
||||||
// Blow through seen targets
|
// Blow through seen targets
|
||||||
%chosenTarget = -1;
|
%chosenTarget = -1;
|
||||||
%chosenTargetDistance = 9999;
|
%chosenTargetDistance = 9999;
|
||||||
|
|
||||||
%botPosition = %client.player.getPosition();
|
%botPosition = %client.player.getPosition();
|
||||||
for (%iteration = 0; %iteration < %client.visibleHostiles.getCount(); %iteration++)
|
for (%iteration = 0; %iteration < %client.visibleHostiles.getCount(); %iteration++)
|
||||||
{
|
{
|
||||||
%current = %client.visibleHostiles.getObject(%iteration);
|
%current = %client.visibleHostiles.getObject(%iteration);
|
||||||
|
|
||||||
%targetDistance = vectorDist(%current.getPosition(), %botPosition);
|
%targetDistance = vectorDist(%current.getPosition(), %botPosition);
|
||||||
if (%targetDistance < %chosenTargetDistance)
|
|
||||||
|
// FIXME: We immediately forget about the asshole here
|
||||||
|
if (%targetDistance < %chosenTargetDistance && %client.player.canSeeObject(%current, %client.viewDistance, %client.fieldOfView))
|
||||||
{
|
{
|
||||||
%chosenTargetDistance = %targetDistance;
|
%chosenTargetDistance = %targetDistance;
|
||||||
%chosenTarget = %current;
|
%chosenTarget = %current;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
%client.engageTarget = %chosenTarget;
|
%client.engageTarget = %chosenTarget;
|
||||||
if (!isObject(%client.engageTarget) && %client.engageTargetLastPosition $= "")
|
if (!isObject(%client.engageTarget) && %client.engageTargetLastPosition $= "")
|
||||||
|
{
|
||||||
%task.setWeight($DXAI::Task::NoPriority);
|
%task.setWeight($DXAI::Task::NoPriority);
|
||||||
|
|
||||||
|
// Make sure we disable the pack
|
||||||
|
if (%client.player.getPackName() $= "ShieldPack")
|
||||||
|
{
|
||||||
|
%client.player.setImageTrigger(2, false);
|
||||||
|
%client.rechargingEnergy = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
%task.setWeight($DXAI::Task::VeryHighPriority);
|
%task.setWeight($DXAI::Task::VeryHighPriority);
|
||||||
|
|
||||||
|
// If we have a shield pack on, use it
|
||||||
|
if (%client.player.getPackName() $= "ShieldPack" && %client.player.getEnergyLevel() >= 30 && !%client.rechargingEnergy)
|
||||||
|
%client.player.setImageTrigger(2, true);
|
||||||
|
else if (%client.player.getPackName() $= "ShieldPack" && %client.player.getEnergyLevel() >= 70)
|
||||||
|
{
|
||||||
|
%client.player.setImageTrigger(2, true);
|
||||||
|
%client.rechargingEnergy = false;
|
||||||
|
}
|
||||||
|
else if (%client.player.getPackName() $= "ShieldPack")
|
||||||
|
{
|
||||||
|
// We ran out of energy, let the pack recharge for a bit
|
||||||
|
%client.player.setImageTrigger(2, false);
|
||||||
|
%client.rechargingEnergy = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function AIEnhancedEngageTarget::monitor(%task, %client)
|
function AIEnhancedEngageTarget::monitor(%task, %client)
|
||||||
{
|
{
|
||||||
if (isObject(%client.engageTarget))
|
if (isObject(%client.engageTarget))
|
||||||
{
|
{
|
||||||
if (%client.engageTarget.getState() !$= "Move")
|
if (%client.engageTarget.getState() !$= "Move")
|
||||||
|
|
@ -216,9 +247,9 @@ function AIEnhancedEngageTarget::monitor(%task, %client)
|
||||||
%client.engageTargetLastPosition = "";
|
%client.engageTargetLastPosition = "";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// %client.engageTargetLastPosition = %client.engageTarget.getWorldBoxCenter();
|
// %client.engageTargetLastPosition = %client.engageTarget.getWorldBoxCenter();
|
||||||
// %client.setMoveTarget(getRandomPositionOnTerrain(%client.engageTargetLastPosition, 40));
|
// %client.setMoveTarget(getRandomPositionOnTerrain(%client.engageTargetLastPosition, 40));
|
||||||
//%client.pressFire();
|
//%client.pressFire();
|
||||||
}
|
}
|
||||||
else if (%client.engageTargetLastPosition !$= "")
|
else if (%client.engageTargetLastPosition !$= "")
|
||||||
|
|
@ -244,21 +275,21 @@ function AIEnhancedRearmTask::initFromObjective(%task, %objective, %client) { }
|
||||||
function AIEnhancedRearmTask::assume(%task, %client) { %task.setMonitorFreq(32); }
|
function AIEnhancedRearmTask::assume(%task, %client) { %task.setMonitorFreq(32); }
|
||||||
function AIEnhancedRearmTask::retire(%task, %client) { }
|
function AIEnhancedRearmTask::retire(%task, %client) { }
|
||||||
|
|
||||||
function AIEnhancedRearmTask::weight(%task, %client)
|
function AIEnhancedRearmTask::weight(%task, %client)
|
||||||
{
|
{
|
||||||
if (%client.shouldRearm)
|
if (%client.shouldRearm)
|
||||||
%task.setWeight($DXAI::Task::HighPriority);
|
%task.setWeight($DXAI::Task::HighPriority);
|
||||||
else
|
else
|
||||||
%task.setWeight($DXAI::Task::NoPriority);
|
%task.setWeight($DXAI::Task::NoPriority);
|
||||||
|
|
||||||
%task.setMonitorFreq(getRandom(10, 32));
|
%task.setMonitorFreq(getRandom(10, 32));
|
||||||
}
|
}
|
||||||
|
|
||||||
function AIEnhancedRearmTask::monitor(%task, %client)
|
function AIEnhancedRearmTask::monitor(%task, %client)
|
||||||
{
|
{
|
||||||
if (!isObject(%client.rearmTarget))
|
if (!isObject(%client.rearmTarget))
|
||||||
%client.rearmTarget = %client.getClosestInventory();
|
%client.rearmTarget = %client.getClosestInventory();
|
||||||
|
|
||||||
if (isObject(%client.rearmTarget))
|
if (isObject(%client.rearmTarget))
|
||||||
{
|
{
|
||||||
// Politely wait if someone is already on it.
|
// Politely wait if someone is already on it.
|
||||||
|
|
@ -279,7 +310,7 @@ function AIEnhancedReturnFlagTask::initFromObjective(%task, %objective, %client)
|
||||||
function AIEnhancedReturnFlagTask::assume(%task, %client) { %task.setMonitorFreq(32); }
|
function AIEnhancedReturnFlagTask::assume(%task, %client) { %task.setMonitorFreq(32); }
|
||||||
function AIEnhancedReturnFlagTask::retire(%task, %client) { }
|
function AIEnhancedReturnFlagTask::retire(%task, %client) { }
|
||||||
|
|
||||||
function AIEnhancedReturnFlagTask::weight(%task, %client)
|
function AIEnhancedReturnFlagTask::weight(%task, %client)
|
||||||
{
|
{
|
||||||
%flag = nameToID("Team" @ %client.team @ "Flag1");
|
%flag = nameToID("Team" @ %client.team @ "Flag1");
|
||||||
if (!isObject(%flag) || %flag.isHome)
|
if (!isObject(%flag) || %flag.isHome)
|
||||||
|
|
@ -290,16 +321,16 @@ function AIEnhancedReturnFlagTask::weight(%task, %client)
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// TODO: For now, all the bots go after it! Make this check if the bot is range.
|
// TODO: For now, all the bots go after it! Make this check if the bot is range.
|
||||||
%task.setWeight($DXAI::Task::HighPriority);
|
%task.setWeight($DXAI::Task::HighPriority);
|
||||||
%client.returnFlagTarget = %flag;
|
%client.returnFlagTarget = %flag;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function AIEnhancedReturnFlagTask::monitor(%task, %client)
|
function AIEnhancedReturnFlagTask::monitor(%task, %client)
|
||||||
{
|
{
|
||||||
if (!isObject(%client.returnFlagTarget))
|
if (!isObject(%client.returnFlagTarget))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (isObject(%client.engageTarget) && %client.engageTarget.getState() $= "Move")
|
if (isObject(%client.engageTarget) && %client.engageTarget.getState() $= "Move")
|
||||||
AIEnhancedReturnFlagTask::monitorEngage(%task, %client);
|
AIEnhancedReturnFlagTask::monitorEngage(%task, %client);
|
||||||
else
|
else
|
||||||
|
|
@ -319,7 +350,7 @@ function AIEnhancedPathCorrectionTask::initFromObjective(%task, %objective, %cli
|
||||||
function AIEnhancedPathCorrectionTask::assume(%task, %client) { %task.setMonitorFreq(2); }
|
function AIEnhancedPathCorrectionTask::assume(%task, %client) { %task.setMonitorFreq(2); }
|
||||||
function AIEnhancedPathCorrectionTask::retire(%task, %client) { }
|
function AIEnhancedPathCorrectionTask::retire(%task, %client) { }
|
||||||
|
|
||||||
function AIEnhancedPathCorrectionTask::weight(%task, %client)
|
function AIEnhancedPathCorrectionTask::weight(%task, %client)
|
||||||
{
|
{
|
||||||
if (%client.isPathCorrecting)
|
if (%client.isPathCorrecting)
|
||||||
%task.setWeight($DXAI::Task::VeryHighPriority);
|
%task.setWeight($DXAI::Task::VeryHighPriority);
|
||||||
|
|
@ -328,7 +359,7 @@ function AIEnhancedPathCorrectionTask::weight(%task, %client)
|
||||||
}
|
}
|
||||||
|
|
||||||
function AIEnhancedPathCorrectionTask::monitor(%task, %client)
|
function AIEnhancedPathCorrectionTask::monitor(%task, %client)
|
||||||
{
|
{
|
||||||
if (%client.isPathCorrecting)
|
if (%client.isPathCorrecting)
|
||||||
{
|
{
|
||||||
if (%client.player.getEnergyPercent() >= 1)
|
if (%client.player.getEnergyPercent() >= 1)
|
||||||
|
|
@ -336,7 +367,7 @@ function AIEnhancedPathCorrectionTask::monitor(%task, %client)
|
||||||
else
|
else
|
||||||
%client.setMoveTarget(-1);
|
%client.setMoveTarget(-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
//------------------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
@ -347,14 +378,14 @@ function AIEnhancedFlagCaptureTask::initFromObjective(%task, %objective, %client
|
||||||
function AIEnhancedFlagCaptureTask::assume(%task, %client) { %task.setMonitorFreq(1); }
|
function AIEnhancedFlagCaptureTask::assume(%task, %client) { %task.setMonitorFreq(1); }
|
||||||
function AIEnhancedFlagCaptureTask::retire(%task, %client) { }
|
function AIEnhancedFlagCaptureTask::retire(%task, %client) { }
|
||||||
|
|
||||||
function AIEnhancedFlagCaptureTask::weight(%task, %client)
|
function AIEnhancedFlagCaptureTask::weight(%task, %client)
|
||||||
{
|
{
|
||||||
if (%client.shouldRunFlag)
|
if (%client.shouldRunFlag)
|
||||||
{
|
{
|
||||||
// First, is the enemy flag home?
|
// First, is the enemy flag home?
|
||||||
%enemyTeam = %client.team == 1 ? 2 : 1;
|
%enemyTeam = %client.team == 1 ? 2 : 1;
|
||||||
%enemyFlag = nameToID("Team" @ %enemyTeam @ "Flag1");
|
%enemyFlag = nameToID("Team" @ %enemyTeam @ "Flag1");
|
||||||
|
|
||||||
if (isObject(%enemyFlag) && %enemyFlag.isHome)
|
if (isObject(%enemyFlag) && %enemyFlag.isHome)
|
||||||
{
|
{
|
||||||
%client.targetCaptureFlag = %enemyFlag;
|
%client.targetCaptureFlag = %enemyFlag;
|
||||||
|
|
@ -366,11 +397,11 @@ function AIEnhancedFlagCaptureTask::weight(%task, %client)
|
||||||
}
|
}
|
||||||
|
|
||||||
function AIEnhancedFlagCaptureTask::monitor(%task, %client)
|
function AIEnhancedFlagCaptureTask::monitor(%task, %client)
|
||||||
{
|
{
|
||||||
if (!isObject(%client.targetCaptureFlag))
|
if (!isObject(%client.targetCaptureFlag) && !%client.hasFlag)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (%client.targetCaptureFlag.getObjectMount() != %client.player)
|
if (!%client.hasFlag)
|
||||||
%client.setMoveTarget(%client.targetCaptureFlag.getPosition());
|
%client.setMoveTarget(%client.targetCaptureFlag.getPosition());
|
||||||
else
|
else
|
||||||
%client.setMoveTarget(nameToID("Team" @ %client.team @ "Flag1").getPosition());
|
%client.setMoveTarget(nameToID("Team" @ %client.team @ "Flag1").getPosition());
|
||||||
|
|
@ -380,7 +411,7 @@ function AIEnhancedFlagCaptureTask::monitor(%task, %client)
|
||||||
function ObjectiveNameToVoice(%bot)
|
function ObjectiveNameToVoice(%bot)
|
||||||
{
|
{
|
||||||
%objective = %bot.getTaskName();
|
%objective = %bot.getTaskName();
|
||||||
|
|
||||||
%result = "avo.grunt";
|
%result = "avo.grunt";
|
||||||
switch$(%objective)
|
switch$(%objective)
|
||||||
{
|
{
|
||||||
|
|
@ -405,6 +436,6 @@ function ObjectiveNameToVoice(%bot)
|
||||||
case "AIEnhancedEscort":
|
case "AIEnhancedEscort":
|
||||||
%result = "slf.tsk.cover";
|
%result = "slf.tsk.cover";
|
||||||
}
|
}
|
||||||
|
|
||||||
return %result;
|
return %result;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue