2015-06-26 21:02:55 +00:00
|
|
|
//------------------------------------------------------------------------------------------
|
|
|
|
|
// aicommander.cs
|
|
|
|
|
// Source file for the DXAI commander AI implementation.
|
|
|
|
|
// https://github.com/Ragora/T2-DXAI.git
|
|
|
|
|
//
|
|
|
|
|
// Copyright (c) 2014 Robert MacGregor
|
|
|
|
|
// This software is licensed under the MIT license. Refer to LICENSE.txt for more information.
|
|
|
|
|
//------------------------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
$DXAI::ActiveCommanderCount = 2;
|
|
|
|
|
|
2015-06-28 00:43:20 +00:00
|
|
|
$DXAI::Priorities::DefendGenerator = 0;
|
|
|
|
|
$DXAI::Priorities::DefendFlag = 1;
|
|
|
|
|
$DXAI::Priorities::ScoutBase = 2;
|
2015-06-26 21:02:55 +00:00
|
|
|
//-----------------------------------------------
|
|
|
|
|
$DXAI::Priorities::CaptureFlag = 4;
|
|
|
|
|
$DXAI::Priorities::CaptureObjective = 5;
|
|
|
|
|
$DXAI::Priorities::AttackTurret = 6;
|
|
|
|
|
$DXAI::Priorities::Count = 3;
|
|
|
|
|
|
|
|
|
|
$DXAI::Priorities::DefaultPriorityValue[$DXAI::Priorities::DefendGenerator] = 2;
|
|
|
|
|
$DXAI::Priorities::DefaultPriorityValue[$DXAI::Priorities::DefendFlag] = 3;
|
|
|
|
|
$DXAI::Priorities::DefaultPriorityValue[$DXAI::Priorities::ScoutBase] = 1;
|
|
|
|
|
|
2015-06-28 00:43:20 +00:00
|
|
|
$DXAI::Priorities::Text[$DXAI::Priorities::DefendGenerator] = "Defending a Generator";
|
|
|
|
|
$DXAI::Priorities::Text[$DXAI::Priorities::DefendFlag] = "Defending the Flag";
|
|
|
|
|
$DXAI::Priorities::Text[$DXAI::Priorities::ScoutBase] = "Scouting a Location";
|
|
|
|
|
|
2015-06-26 21:02:55 +00:00
|
|
|
function AICommander::setup(%this)
|
|
|
|
|
{
|
|
|
|
|
%this.botList = new SimSet();
|
|
|
|
|
%this.idleBotList = new SimSet();
|
|
|
|
|
|
|
|
|
|
for (%iteration = 0; %iteration < ClientGroup.getCount(); %iteration++)
|
|
|
|
|
{
|
|
|
|
|
%currentClient = ClientGroup.getObject(%iteration);
|
|
|
|
|
|
|
|
|
|
if (%currentClient.isAIControlled() && %currentClient.team == %this.team)
|
|
|
|
|
{
|
|
|
|
|
%this.botList.add(%currentClient);
|
|
|
|
|
%this.idleBotList.add(%currentClient);
|
|
|
|
|
|
|
|
|
|
%currentClient.commander = %this;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
%this.setDefaultPriorities();
|
|
|
|
|
|
2015-06-28 00:43:20 +00:00
|
|
|
// Also set the assignment tracker and the cyclers for each objective type
|
2015-06-26 21:02:55 +00:00
|
|
|
for (%iteration = 0; %iteration < $DXAI::Priorities::Count; %iteration++)
|
2015-06-28 00:43:20 +00:00
|
|
|
{
|
2015-06-26 21:02:55 +00:00
|
|
|
%this.assignments[%iteration] = 0;
|
2015-06-28 00:43:20 +00:00
|
|
|
%this.objectiveCycles[%iteration] = CyclicSet::create();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function AICommander::_skimObjectiveGroup(%this, %group)
|
|
|
|
|
{
|
|
|
|
|
for (%iteration = 0; %iteration < %group.getCount(); %iteration++)
|
|
|
|
|
{
|
|
|
|
|
%current = %group.getObject(%iteration);
|
|
|
|
|
|
|
|
|
|
// We're getting ballsy here, recursion in TS!
|
|
|
|
|
if (%current.getClassName() $= "SimGroup")
|
|
|
|
|
%this._skimObjectiveGroup(%current);
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// Which objective type are we looking at?
|
|
|
|
|
switch$ (%current.getName())
|
|
|
|
|
{
|
|
|
|
|
case "AIOAttackObject":
|
|
|
|
|
case "AIOMortarObject":
|
|
|
|
|
case "AIODefendLocation":
|
|
|
|
|
// FIXME: Console spam from .targetObjectID not being set?
|
|
|
|
|
%datablockName = %current.targetObjectID.getDatablock().getName();
|
|
|
|
|
|
|
|
|
|
echo(%datablockName);
|
|
|
|
|
|
|
|
|
|
// Defending the flag?
|
|
|
|
|
if (%datablockName $= "FLAG")
|
|
|
|
|
%this.objectiveCycles[$DXAI::Priorities::DefendFlag].add(%current);
|
|
|
|
|
else if (%datablockName $="GeneratorLarge")
|
|
|
|
|
%this.objectiveCycles[$DXAI::Priorities::DefendGenerator].add(%current);
|
|
|
|
|
|
|
|
|
|
case "AIORepairObject":
|
|
|
|
|
case "AIOTouchObject":
|
|
|
|
|
case "AIODeployEquipment":
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function AICommander::loadObjectives(%this)
|
|
|
|
|
{
|
|
|
|
|
// First we clear the old cyclers
|
|
|
|
|
for (%iteration = 0; %iteration < $DXAI::Priorities::Count; %iteration++)
|
|
|
|
|
%this.objectiveCycles[%iteration].clear();
|
|
|
|
|
|
|
|
|
|
%teamGroup = "Team" @ %this.team;
|
|
|
|
|
%teamGroup = nameToID(%teamGroup);
|
|
|
|
|
|
|
|
|
|
if (!isObject(%teamGroup))
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
// Search this group for something named "AIObjectives". Each team has one, so we can't reliably just use that name
|
|
|
|
|
%group = %teamGroup;
|
|
|
|
|
for (%iteration = 0; %iteration < %group.getCount(); %iteration++)
|
|
|
|
|
{
|
|
|
|
|
%current = %group.getObject(%iteration);
|
|
|
|
|
if (%current.getClassName() $= "SimGroup" && %current.getName() $= "AIObjectives")
|
|
|
|
|
{
|
|
|
|
|
%group = %current;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (%group == %teamGroup)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
// Now that we have our objective set, skim it for anything usable
|
|
|
|
|
%this._skimObjectiveGroup(%group);
|
|
|
|
|
|
|
|
|
|
// 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
|
|
|
|
|
%scoutLocationObjective = new ScriptObject() { distance = 100; };
|
|
|
|
|
%defendFlagObjective = %this.objectiveCycles[$DXAI::Priorities::DefendFlag].next();
|
|
|
|
|
%scoutLocationObjective.location = %defendFlagObjective.location;
|
|
|
|
|
|
|
|
|
|
%this.objectiveCycles[$DXAI::Priorities::ScoutBase].add(%scoutLocationObjective);
|
2015-06-26 21:02:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function AICommander::assignTasks(%this)
|
|
|
|
|
{
|
|
|
|
|
// Calculate how much priority we have total, first
|
|
|
|
|
%totalPriority = 0.0;
|
|
|
|
|
for (%iteration = 0; %iteration < $DXAI::Priorities::Count; %iteration++)
|
|
|
|
|
{
|
|
|
|
|
%totalPriority += %this.priorities[%iteration];
|
|
|
|
|
%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
|
|
|
|
|
%priorityQueue = PriorityQueue::create();
|
|
|
|
|
|
|
|
|
|
// 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
|
|
|
|
|
%botCountRequired = 0;
|
|
|
|
|
for (%iteration = 0; %iteration < $DXAI::Priorities::Count; %iteration++)
|
|
|
|
|
{
|
|
|
|
|
%botAssignments[%iteration] = mCeil(((%totalPriority / $DXAI::Priorities::Count) * %this.priorities[%iteration]) / $DXAI::Priorities::Count);
|
|
|
|
|
%botAssignments[%iteration] -= %this.botAssignments[%iteration]; // If we already have bots doing this, then we don't need to replicate them
|
|
|
|
|
%botCountRequired += %botAssignments[%iteration];
|
|
|
|
|
if (%botAssignments[%iteration] < 0)
|
|
|
|
|
%lostBots = true;
|
|
|
|
|
else
|
2015-06-28 00:43:20 +00:00
|
|
|
{
|
|
|
|
|
%priorityQueue.add(%botAssignments[%iteration], %iteration);
|
|
|
|
|
echo(%botAssignments[%iteration] SPC " bots on task " @ $DXAI::Priorities::Text[%iteration]);
|
|
|
|
|
}
|
2015-06-26 21:02:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 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
|
|
|
|
|
// to ditch mAbs(%botAssignments[%task]) bots from that given task.
|
|
|
|
|
for (%taskIteration = 0; %lostBots && %taskIteration < $DXAI::Priorities::Count; %taskiteration++)
|
|
|
|
|
// Need to ditch some bots
|
|
|
|
|
if (%botAssignments[%taskIteration] < 0)
|
2015-06-28 00:43:20 +00:00
|
|
|
%this.deassignBots(%taskIteration, mAbs(%botAssignments[%taskIteration]));
|
2015-06-26 21:02:55 +00:00
|
|
|
|
|
|
|
|
// Do we have enough idle bots to just shunt everyone into something?
|
|
|
|
|
if (%this.idleBotList.getCount() >= %botCountRequired)
|
|
|
|
|
{
|
|
|
|
|
for (%taskIteration = 0; %taskIteration < $DXAI::Priorities::Count; %taskiteration++)
|
|
|
|
|
for (%botIteration = 0; %botIteration < %botAssignments[%taskIteration]; %botIteration++)
|
|
|
|
|
%this.assignTask(%taskIteration, %this.idleBotList.getObject(0));
|
|
|
|
|
}
|
2015-06-28 00:43:20 +00:00
|
|
|
// Okay, we don't have enough bots currently so we'll try to satisfy the higher priority objectives first
|
2015-06-26 21:02:55 +00:00
|
|
|
else
|
|
|
|
|
while (!%priorityQueue.isEmpty() && %this.idleBotList.getCount() != 0)
|
|
|
|
|
{
|
2015-06-28 00:43:20 +00:00
|
|
|
%taskID = %priorityQueue.topValue();
|
|
|
|
|
%requiredBots = %priorityQueue.topKey();
|
2015-06-26 21:02:55 +00:00
|
|
|
%priorityQueue.pop();
|
2015-06-28 00:43:20 +00:00
|
|
|
|
2015-06-26 21:02:55 +00:00
|
|
|
for (%botIteration = 0; %botIteration < %requiredBots && %this.idleBotList.getCount() != 0; %botIteration++)
|
|
|
|
|
%this.assignTask(%taskID, %this.idleBotList.getObject(0));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Regardless, we need to make sure we cleanup the queue
|
|
|
|
|
// FIXME: Perhaps just create one per commander and reuse it?
|
|
|
|
|
%priorityQueue.delete();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function AICommander::deassignBots(%this, %taskID, %count)
|
|
|
|
|
{
|
|
|
|
|
// TODO: More efficient removal?
|
|
|
|
|
for (%iteration = 0; %count > 0 && %iteration < %this.botList.getCount(); %iteration++)
|
|
|
|
|
{
|
|
|
|
|
%bot = %this.botList.getObject(%iteration);
|
|
|
|
|
if (%bot.assignment == %taskID)
|
|
|
|
|
{
|
|
|
|
|
%bot.clearTasks();
|
|
|
|
|
%this.idleBotList.add(%bot);
|
|
|
|
|
%count--;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return %count == 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function AICommander::assignTask(%this, %taskID, %bot)
|
|
|
|
|
{
|
|
|
|
|
// Don't try to assign if the bot is already assigned something
|
2015-06-28 00:43:20 +00:00
|
|
|
if (!%this.idleBotList.isMember(%bot))
|
2015-06-26 21:02:55 +00:00
|
|
|
return;
|
|
|
|
|
|
2015-06-28 00:43:20 +00:00
|
|
|
%this.idleBotList.remove(%bot);
|
2015-06-26 21:02:55 +00:00
|
|
|
|
|
|
|
|
switch (%taskID)
|
|
|
|
|
{
|
2015-06-28 00:43:20 +00:00
|
|
|
case $DXAI::Priorities::DefendGenerator or $DXAI::Priorities::DefendFlag:
|
|
|
|
|
%objective = %this.objectiveCycles[%taskID].next();
|
|
|
|
|
|
|
|
|
|
// Set the bot to defend the location
|
|
|
|
|
%bot.defendLocation = %objective.location;
|
|
|
|
|
%bot.addTask("AIEnhancedDefendLocation");
|
|
|
|
|
|
2015-06-26 21:02:55 +00:00
|
|
|
case $DXAI::Priorities::ScoutBase:
|
2015-06-28 00:43:20 +00:00
|
|
|
%objective = %this.objectiveCycles[%taskID].next();
|
|
|
|
|
|
|
|
|
|
// Set the bot to defend the location
|
|
|
|
|
%bot.scoutLocation = %objective.location;
|
|
|
|
|
%bot.scoutDistance = %objective.distance;
|
|
|
|
|
%bot.addTask("AIEnhancedScoutLocation");
|
2015-06-26 21:02:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
%this.botAssignments[%taskID]++;
|
|
|
|
|
%bot.assignment = %taskID;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function AICommander::setDefaultPriorities(%this)
|
|
|
|
|
{
|
|
|
|
|
for (%iteration = 0; %iteration < $DXAI::Priorities::Count; %iteration++)
|
|
|
|
|
%this.priorities[%iteration] = $DXAI::Priorities::DefaultPriorityValue[%iteration];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function AICommander::cleanUp(%this)
|
|
|
|
|
{
|
|
|
|
|
%this.botList.delete();
|
|
|
|
|
%this.idleBotList.delete();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function AICommander::update(%this)
|
|
|
|
|
{
|
|
|
|
|
for (%iteration = 0; %iteration < %this.botList.getCount(); %iteration++)
|
|
|
|
|
%this.botList.getObject(%iteration).update();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function AICommander::removeBot(%this, %bot)
|
|
|
|
|
{
|
|
|
|
|
%this.botList.remove(%bot);
|
|
|
|
|
%this.idleBotList.remove(%bot);
|
|
|
|
|
|
|
|
|
|
%bot.commander = -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function AICommander::addBot(%this, %bot)
|
|
|
|
|
{
|
|
|
|
|
if (!%this.botList.isMember(%bot))
|
|
|
|
|
%this.botList.add(%bot);
|
|
|
|
|
|
|
|
|
|
if (!%this.idleBotList.isMember(%bot))
|
|
|
|
|
%this.idleBotList.add(%bot);
|
|
|
|
|
|
|
|
|
|
%bot.commander = %this;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function AICommander::notifyPlayerDeath(%this, %killedClient, %killedByClient)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function AICommander::notifyFlagGrab(%this, %grabbedByClient)
|
|
|
|
|
{
|
|
|
|
|
%this.priority[$DXAI::Priorities::DefendFlag]++;
|
|
|
|
|
|
|
|
|
|
// ...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.
|
|
|
|
|
if (%this.idleBotList.getCount() != 0)
|
|
|
|
|
{
|
|
|
|
|
// Go full-force and try to kill that jerk!
|
|
|
|
|
for (%iteration = 0; %iteration < %this.idleBotList.getCount(); %iteration++)
|
|
|
|
|
{
|
|
|
|
|
%idleBot = %this.idleBotList.getObject(%iteration);
|
|
|
|
|
%idleBot.attackTarget = %grabbedByClient.player;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|