diff --git a/scripts/DXAI/aicommander.cs b/scripts/DXAI/aicommander.cs index f217cd9..c5eb231 100644 --- a/scripts/DXAI/aicommander.cs +++ b/scripts/DXAI/aicommander.cs @@ -9,27 +9,23 @@ $DXAI::ActiveCommanderCount = 2; -$DXAI::Priorities::DefendGenerator = 1; -$DXAI::Priorities::DefendFlag = 2; -$DXAI::Priorities::ScoutBase = 3; +$DXAI::Priorities::DefendGenerator = 0; +$DXAI::Priorities::DefendFlag = 1; +$DXAI::Priorities::ScoutBase = 2; //----------------------------------------------- $DXAI::Priorities::CaptureFlag = 4; $DXAI::Priorities::CaptureObjective = 5; $DXAI::Priorities::AttackTurret = 6; $DXAI::Priorities::Count = 3; -// # of bots assigned = mCeil(((totalPriorityValues / priorityCounts) * priority) / priorityCounts) - -// totalPriorityValues = 3 + 2 -// priorityCounts = 2 -// Priority for Gen: 2 -// Priority for Flag: 3 -// flagBots = ((5.0 / 2.0) * 3.0) / 2.0 -// Gen bots = ((5.0 / 2.0) * 2.0) / 2.0 $DXAI::Priorities::DefaultPriorityValue[$DXAI::Priorities::DefendGenerator] = 2; $DXAI::Priorities::DefaultPriorityValue[$DXAI::Priorities::DefendFlag] = 3; $DXAI::Priorities::DefaultPriorityValue[$DXAI::Priorities::ScoutBase] = 1; +$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"; + function AICommander::setup(%this) { %this.botList = new SimSet(); @@ -50,9 +46,88 @@ function AICommander::setup(%this) %this.setDefaultPriorities(); - // Also set the assignment tracker + // Also set the assignment tracker and the cyclers for each objective type for (%iteration = 0; %iteration < $DXAI::Priorities::Count; %iteration++) + { %this.assignments[%iteration] = 0; + %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); } function AICommander::assignTasks(%this) @@ -76,11 +151,13 @@ function AICommander::assignTasks(%this) %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 - %priorityQueue.add(%iteration, %botAssignments[%iteration]); + { + %priorityQueue.add(%botAssignments[%iteration], %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 @@ -89,7 +166,7 @@ function AICommander::assignTasks(%this) for (%taskIteration = 0; %lostBots && %taskIteration < $DXAI::Priorities::Count; %taskiteration++) // Need to ditch some bots 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? if (%this.idleBotList.getCount() >= %botCountRequired) @@ -98,14 +175,14 @@ function AICommander::assignTasks(%this) for (%botIteration = 0; %botIteration < %botAssignments[%taskIteration]; %botIteration++) %this.assignTask(%taskIteration, %this.idleBotList.getObject(0)); } - // Okay, we don't have enough bots currently so we'll try to satisify the higher priority objectives first + // Okay, we don't have enough bots currently so we'll try to satisfy the higher priority objectives first else while (!%priorityQueue.isEmpty() && %this.idleBotList.getCount() != 0) { - %taskID = %priorityQueue.topKey(); - %requiredBots = %priorityQueue.topValue(); + %taskID = %priorityQueue.topValue(); + %requiredBots = %priorityQueue.topKey(); %priorityQueue.pop(); - + for (%botIteration = 0; %botIteration < %requiredBots && %this.idleBotList.getCount() != 0; %botIteration++) %this.assignTask(%taskID, %this.idleBotList.getObject(0)); } @@ -135,19 +212,27 @@ function AICommander::deassignBots(%this, %taskID, %count) function AICommander::assignTask(%this, %taskID, %bot) { // Don't try to assign if the bot is already assigned something - if (!%this.idleBotList.contains(%this)) + if (!%this.idleBotList.isMember(%bot)) return; - %this.idleBotList.remove(%this); + %this.idleBotList.remove(%bot); switch (%taskID) { - case $DXAI::Priorities::DefendGenerator: - break; - case $DXAI::Priorities::DefendFlag: - break; + 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"); + case $DXAI::Priorities::ScoutBase: - break; + %objective = %this.objectiveCycles[%taskID].next(); + + // Set the bot to defend the location + %bot.scoutLocation = %objective.location; + %bot.scoutDistance = %objective.distance; + %bot.addTask("AIEnhancedScoutLocation"); } %this.botAssignments[%taskID]++; diff --git a/scripts/DXAI/aiconnection.cs b/scripts/DXAI/aiconnection.cs index 1cb093a..e57dbeb 100644 --- a/scripts/DXAI/aiconnection.cs +++ b/scripts/DXAI/aiconnection.cs @@ -22,7 +22,7 @@ function AIConnection::initialize(%this, %aiClient) function AIConnection::update(%this) { - if (isObject(%this.player) && %this.player.getMoveState() $= "walk") + if (isObject(%this.player) && %this.player.getState() $= "Move") { %this.updateLegs(); %this.updateVisualAcuity(); diff --git a/scripts/DXAI/cyclicset.cs b/scripts/DXAI/cyclicset.cs new file mode 100644 index 0000000..453fcf9 --- /dev/null +++ b/scripts/DXAI/cyclicset.cs @@ -0,0 +1,54 @@ +//------------------------------------------------------------------------------------------ +// cyclicset.cs +// Main source file for the CyclicSet 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. +//------------------------------------------------------------------------------------------ + +function CyclicSet::add(%this, %item) +{ + %this.set.add(%item); +} + +function CyclicSet::delete(%this) +{ + %this.set.delete(); + ScriptObject::delete(%this); +} + +function CyclicSet::clear(%this) +{ + %this.index = 0; + %this.set.clear(); +} + +function CyclicSet::next(%this) +{ + if (%this.set.getCount() == 0) + return -1; + + %result = %this.set.getObject(%this.index); + + %this.index++; + %this.index %= %this.set.getCount(); + + return %result; +} + +function CyclicSet::randomize(%this) +{ + %this.index = getRandom(0, %this.set.getCount()); +} + +function CyclicSet::create(%name) +{ + %set = new SimSet(); + return new ScriptObject(%name) + { + index = 0; + class = "CyclicSet"; + set = %set; + }; +} \ No newline at end of file diff --git a/scripts/DXAI/helpers.cs b/scripts/DXAI/helpers.cs index bd9fe88..975566e 100644 --- a/scripts/DXAI/helpers.cs +++ b/scripts/DXAI/helpers.cs @@ -13,6 +13,14 @@ function sameSide(%p1, %p2, %a, %b) return false; } +function SimSet::contains(%this, %contained) +{ + for (%iteration = 0; %iteration < %this.getCount(); %iteration++) + if (%this.getObject(%iteration) == %contained) + return true; + return false; +} + function pointInTriangle(%point, %a, %b, %c) { if (sameSide(%point, %a, %b, %c) && sameSide(%point, %b, %a, %c) && sameSide(%point, %c, %a, %b)) diff --git a/scripts/DXAI/main.cs b/scripts/DXAI/main.cs index 170a8ae..c998207 100644 --- a/scripts/DXAI/main.cs +++ b/scripts/DXAI/main.cs @@ -12,6 +12,8 @@ exec("scripts/DXAI/helpers.cs"); exec("scripts/DXAI/config.cs"); exec("scripts/DXAI/aicommander.cs"); exec("scripts/DXAI/aiconnection.cs"); +exec("scripts/DXAI/priorityqueue.cs"); +exec("scripts/DXAI/cyclicset.cs"); // General DXAI API implementations function DXAI::cleanup() @@ -90,6 +92,9 @@ function DXAI::update() if (isEventPending($DXAI::updateHandle)) cancel($DXAI::updateHandle); + if (!isObject(Game)) + return; + // Check if the bound functions are overwritten by the current gamemode, or if something // may have invalidated our hooks if ($DXAI::System::InvalidatedEnvironment && $DXAI::System::Setup) diff --git a/scripts/DXAI/objectives.cs b/scripts/DXAI/objectives.cs index 48a6226..a997251 100644 --- a/scripts/DXAI/objectives.cs +++ b/scripts/DXAI/objectives.cs @@ -125,18 +125,21 @@ function AIEnhancedScoutLocation::monitor(%task, %client) // We're moving, or are near enough to our target else { + %pathDistance = %client.getPathDistance(%client.moveLocation); // Don't move if we're close enough to our next node - if (%client.getPathDistance(%client.moveLocation) <= 40 && %client.isMoving) + if (%pathDistance <= 40 && %client.isMoving) { %client.isMoving = false; %client.nextScoutRotation = getRandom(5000, 10000); %client.scoutTime += 32; } - else + else if(%client.getPathDistance(%client.moveLocation) > 40) { %client.isMoving = true; %client.scoutTime = 0; } + else + %client.scoutTime += 32; // Wait a little bit at each node if (%client.scoutTime >= %client.nextScoutRotation) diff --git a/scripts/DXAI/priorityqueue.cs b/scripts/DXAI/priorityqueue.cs index 377b007..e6efdf2 100644 --- a/scripts/DXAI/priorityqueue.cs +++ b/scripts/DXAI/priorityqueue.cs @@ -9,21 +9,34 @@ function PriorityQueue::add(%this, %key, %value) { + // If we already have a key, update it + if (%this.hasKey[%key]) + { + %this.values[%this.keyIndex[%key]] = %value; + return; + } + + %this.hasKey[%key] = true; + // Traverse the queue and discover our insertion point for (%iteration = 0; %iteration < %this.count; %iteration++) - if (%key >= %this.keys[%iteration]) + if (%key <= %this.keys[%iteration]) { + //%this.count++; %this._shift(%iteration, false); %this.values[%iteration] = %value; %this.keys[%iteration] = %key; %this.keyIndex[%key] = %iteration; - %this.hasKey[%key] = true; + %this.count++; + return; } // If we never made an insertion, just stick our key and value at the end %this.values[%this.count] = %value; - %this.keys[%this.count++] = %key; + %this.keys[%this.count] = %key; + %this.keyIndex[%key] = %this.count; + %this.count++; } function PriorityQueue::remove(%this, %key) @@ -42,17 +55,21 @@ function PriorityQueue::_shift(%this, %index, %isRemoval) { for (%iteration = %index; %iteration < %this.count; %iteration++) { - %this.values[%index] = %this.values[%index + 1]; - %this.keys[%index] = %this.keys[%index + 1; + %this.values[%iteration] = %this.values[%iteration + 1]; + %this.keys[%iteration] = %this.keys[%iteration + 1]; + + %this.keyIndex[%this.keys[%iteration]] = %iteration; } return; } - for (%iteration = %index; %iteration < %this.count; %iteration++) + for (%iteration = %this.count; %iteration >= %index; %iteration--) { - %this.values[%index + 1] = %this.values[%index]; - %this.keys[%index + 1] = %this.keys[%index]; + %this.values[%iteration] = %this.values[%iteration - 1]; + %this.keys[%iteration] = %this.keys[%iteration - 1]; + + %this.keyIndex[%this.keys[%iteration]] = %iteration - 1; } } @@ -68,6 +85,9 @@ function PriorityQueue::topKey(%this) function PriorityQueue::pop(%this) { + if (%this.count == 0) + return; + %this.hasKey[%this.keys[%this.count]] = false; %this.count--; } @@ -85,6 +105,12 @@ function Priorityqueue::isEmpty(%this) return %this.count == 0; } +function PriorityQueue::dump(%this) +{ + for (%iteration = 0; %iteration < %this.count; %iteration++) + echo(%iteration SPC %this.keys[%iteration] SPC "-> " @ %this.values[%iteration]); +} + function PriorityQueue::create(%name) { return new ScriptObject(%name)