diff --git a/scripts/DXAI/aicommander.cs b/scripts/DXAI/aicommander.cs new file mode 100644 index 0000000..f217cd9 --- /dev/null +++ b/scripts/DXAI/aicommander.cs @@ -0,0 +1,213 @@ +//------------------------------------------------------------------------------------------ +// 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; + +$DXAI::Priorities::DefendGenerator = 1; +$DXAI::Priorities::DefendFlag = 2; +$DXAI::Priorities::ScoutBase = 3; +//----------------------------------------------- +$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; + +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(); + + // Also set the assignment tracker + for (%iteration = 0; %iteration < $DXAI::Priorities::Count; %iteration++) + %this.assignments[%iteration] = 0; +} + +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 + %priorityQueue.add(%iteration, %botAssignments[%iteration]); + } + + // 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) + %this.deassignBots(%taskIteration, mAbs(%botAssignments[%taskIteration]); + + // 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)); + } + // Okay, we don't have enough bots currently so we'll try to satisify the higher priority objectives first + else + while (!%priorityQueue.isEmpty() && %this.idleBotList.getCount() != 0) + { + %taskID = %priorityQueue.topKey(); + %requiredBots = %priorityQueue.topValue(); + %priorityQueue.pop(); + + 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 + if (!%this.idleBotList.contains(%this)) + return; + + %this.idleBotList.remove(%this); + + switch (%taskID) + { + case $DXAI::Priorities::DefendGenerator: + break; + case $DXAI::Priorities::DefendFlag: + break; + case $DXAI::Priorities::ScoutBase: + break; + } + + %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; + } + } +} \ No newline at end of file diff --git a/scripts/DXAI_Objectives.cs b/scripts/DXAI/aiconnection.cs similarity index 55% rename from scripts/DXAI_Objectives.cs rename to scripts/DXAI/aiconnection.cs index 16b953e..1cb093a 100644 --- a/scripts/DXAI_Objectives.cs +++ b/scripts/DXAI/aiconnection.cs @@ -1,12 +1,47 @@ -// DXAI_Objectives.cs -// Objectives for the AI system +//------------------------------------------------------------------------------------------ +// aiconnection.cs +// Source file declaring the custom AIConnection methods used by the DXAI experimental +// AI enhancement project. +// 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. +//------------------------------------------------------------------------------------------ -//---------------------------------------------------------------------- -// The AIVisualAcuity task is a complementary task for the AI grunt systems -// to perform better at recognizing things visually with reasonably -// Human perception capabilities. -// --------------------------------------------------------------------- +function AIConnection::initialize(%this, %aiClient) +{ + %this.fieldOfView = 3.14 / 2; // 90* View cone + %this.viewDistance = 300; + + if (!isObject(%aiClient)) + error("AIPlayer: Attempted to initialize with bad AI client connection!"); + + %this.client = %aiClient; +} + +function AIConnection::update(%this) +{ + if (isObject(%this.player) && %this.player.getMoveState() $= "walk") + { + %this.updateLegs(); + %this.updateVisualAcuity(); + } +} + +function AIConnection::notifyProjectileImpact(%this, %data, %proj, %position) +{ + if (!isObject(%proj.sourceObject) || %proj.sourceObject.client.team == %this.team) + return; +} + +function AIConnection::isIdle(%this) +{ + if (!isObject(%this.commander)) + return true; + + return %this.commander.idleBotList.isMember(%this); +} function AIConnection::updateLegs(%this) { @@ -17,6 +52,8 @@ function AIConnection::updateLegs(%this) if (%this.aimAtLocation) %this.aimAt(%this.moveLocation); + else if(%this.manualAim) + %this.aimAt(%this.aimLocation); } else { @@ -128,9 +165,7 @@ function AIConnection::updateVisualAcuity(%this) %rayCast = containerRayCast(%start, %end, -1, %this.player); %hitObject = getWord(%raycast, 0); - - // echo(%hitObject); - // echo(%current); + if (%hitObject == %current) { %this.clientDetected(%current); @@ -140,156 +175,4 @@ function AIConnection::updateVisualAcuity(%this) } %result.delete(); -} - -function AIConnection::enhancedLogicLoop(%this) -{ - cancel(%this.enhancedLogicHandle); - - if (isObject(%this.player)) - { - %this.updateVisualAcuity(); - %this.updateLegs(); - } - - %this.enhancedLogicHandle = %this.schedule(32, "enhancedLogicLoop"); -} - -//------------------------------------------------------------- -function AIEnhancedDefendLocation::initFromObjective(%task, %objective, %client) -{ - // Called to initialize from an objective object -} - -function AIEnhancedDefendLocation::assume(%task, %client) -{ - // Called when the bot starts the task - %task.setMonitorFreq(1); -} - -function AIEnhancedDefendLocation::retire(%task, %client) -{ - // Called when the bot stops the task -} - -function AIEnhancedDefendLocation::weight(%task, %client) -{ - %task.setWeight(1000); -} - -function AIEnhancedDefendLocation::monitor(%task, %client) -{ - // echo(%task.getMonitorFreq()); - if (%client.getPathDistance(%client.defendLocation) <= 40) - { - // Pick a random time to move to a nearby location - if (%client.defendTime == -1) - { - %client.nextDefendRotation = getRandom(5000, 10000); - %client.isMoving = false; - } - - // If we're near our random point, just don't move - if (%client.getPathDistance(%client.moveLocation) <= 10) - %client.isMoving = false; - - %client.defendTime += 32; - if (%client.defendTime >= %client.nextDefendRotation) - { - %client.defendTime = 0; - %client.nextDefendRotation = getRandom(5000, 10000); - - // TODO: Replace with something that detects interiors as well - %randomPosition = getRandomPosition(%client.defendLocation, 40); - %randomPosition = getWords(%randomPosition, 0, 1) SPC getTerrainHeight(%randomPosition); - - %client.moveLocation = %randomPosition; - %client.isMoving = true; - } - } - else - { - %client.defendTime = -1; - %client.moveLocation = %client.defendLocation; - %client.isMoving = true; - } -} - -//------------------------------------------------------------- -function AIEnhancedScoutLocation::initFromObjective(%task, %objective, %client) -{ - // Called to initialize from an objective object -} - -function AIEnhancedScoutLocation::assume(%task, %client) -{ - // Called when the bot starts the task - %task.setMonitorFreq(1); - - %client.currentNode = -1; -} - -function AIEnhancedScoutLocation::retire(%task, %client) -{ - // Called when the bot stops the task -} - -function AIEnhancedScoutLocation::weight(%task, %client) -{ - %task.setWeight(1000); -} - -function AIEnhancedScoutLocation::monitor(%task, %client) -{ - // We can't really work without a NavGraph - if (!isObject(NavGraph)) - return; - - // We just received the task, so find a node within distance of our scout location - if (%client.currentNode == -1) - { - %client.currentNode = NavGraph.randNode(%client.scoutLocation, %client.scoutDistance, true, true); - - if (%client.currentNode != -1) - { - %client.moveLocation = NavGraph.nodeLoc(%client.currentNode); - %client.isMoving = true; - } - } - // We're moving, or are near enough to our target - else - { - // Don't move if we're close enough to our next node - if (%client.getPathDistance(%client.moveLocation) <= 40) - { - %client.isMoving = false; - %client.nextScoutRotation = getRandom(5000, 10000); - %client.scoutTime += 32; - } - else - { - %client.isMoving = true; - %client.scoutTime += 0; - } - - // Wait a little bit at each node - if (%client.scoutTime >= %client.nextScoutRotation) - { - %client.scoutTime = 0; - %client.nextScoutRotation = getRandom(5000, 10000); - - // Pick a new node - %client.currentNode = NavGraph.randNode(%client.scoutLocation, %client.scoutDistance, true, true); - - // Ensure that we found a node. - if (%client.currentNode != -1) - { - %client.moveLocation = NavGraph.nodeLoc(%client.currentNode); - %client.isMoving = true; - } - } - } - -} - -//------------------------------------------------------------- \ No newline at end of file +} \ No newline at end of file diff --git a/scripts/DXAI_Config.cs b/scripts/DXAI/config.cs similarity index 93% rename from scripts/DXAI_Config.cs rename to scripts/DXAI/config.cs index bf25761..b158fe4 100644 --- a/scripts/DXAI_Config.cs +++ b/scripts/DXAI/config.cs @@ -1,4 +1,4 @@ -// DXAI_Config.cs +// config.cs // Configuration for the DXAI System // Copyright (c) 2014 Robert MacGregor diff --git a/scripts/DXAI_Helpers.cs b/scripts/DXAI/helpers.cs similarity index 97% rename from scripts/DXAI_Helpers.cs rename to scripts/DXAI/helpers.cs index 7ee827d..bd9fe88 100644 --- a/scripts/DXAI_Helpers.cs +++ b/scripts/DXAI/helpers.cs @@ -1,4 +1,4 @@ -// DXAI_Helpers.cs +// helpers.cs // Helper functions for the AI System // Copyright (c) 2014 Robert MacGregor @@ -163,6 +163,12 @@ function getRandomPosition(%position, %distance) return vectorAdd(%position, vectorScale(%direction, getRandom(0, %distance))); } +function getRandomPositionOnTerrain(%position, %distance) +{ + %result = getRandomPosition(%position, %distance); + return setWord(%result, 2, getTerrainHeight(%result)); +} + function vectorMultiply(%vec1, %vec2) { return (getWord(%vec1, 0) * getWord(%vec2, 0)) SPC diff --git a/scripts/DXAI_Main.cs b/scripts/DXAI/main.cs similarity index 77% rename from scripts/DXAI_Main.cs rename to scripts/DXAI/main.cs index 0f3d96a..170a8ae 100644 --- a/scripts/DXAI_Main.cs +++ b/scripts/DXAI/main.cs @@ -1,70 +1,17 @@ -// DXAI_Main.cs -// Experimental AI System for ProjectR3 +//------------------------------------------------------------------------------------------ +// main.cs +// Main source file for the DXAI experimental AI enhancement project. +// 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. +//------------------------------------------------------------------------------------------ -exec("scripts/Server/DXAI_Objectives.cs"); -exec("scripts/Server/DXAI_Helpers.cs"); -exec("scripts/Server/DXAI_Config.cs"); - -$DXAI::ActiveCommanderCount = 2; - -// AICommander -// This is a script object that exists for every team in a given -// gamemode and performs the coordination of bots in the game. - -function AICommander::notifyPlayerDeath(%this, %killed, %killedBy) -{ -} - -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.team == %this.team && %currentClient.isAIControlled()) - { - %this.botList.add(%currentClient); - %this.idleBotList.add(%currentClient); - - %currentClient.commander = %this; - } - } -} - -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::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(); -} +exec("scripts/DXAI/objectives.cs"); +exec("scripts/DXAI/helpers.cs"); +exec("scripts/DXAI/config.cs"); +exec("scripts/DXAI/aicommander.cs"); +exec("scripts/DXAI/aiconnection.cs"); // General DXAI API implementations function DXAI::cleanup() @@ -161,39 +108,6 @@ function DXAI::notifyPlayerDeath(%killed, %killedBy) $DXAI::ActiveCommander[%iteration].notifyPlayerDeath(%killed, %killedBy); } -// AIPlayer -// This is a script object that contains DXAI functionality on a per-soldier -// basis -function AIConnection::initialize(%this, %aiClient) -{ - %this.fieldOfView = 3.14 / 2; // 90* View cone - %this.viewDistance = 300; - - if (!isObject(%aiClient)) - error("AIPlayer: Attempted to initialize with bad AI client connection!"); - - %this.client = %aiClient; -} - -function AIConnection::update(%this) -{ -} - -function AIConnection::notifyProjectileImpact(%this, %data, %proj, %position) -{ - if (!isObject(%proj.sourceObject) || %proj.sourceObject.client.team == %this.team) - return; -} - -function AIConnection::isIdle(%this) -{ - if (!isObject(%this.commander)) - return true; - - return %this.commander.idleBotList.isMember(%this); -} - - // Hooks for the AI System package DXAI_Hooks { @@ -297,13 +211,15 @@ package DXAI_Hooks } } + // The CreateServer function is hooked so that we can try and guarantee that the DXAI gamemode hooks still + // exist in the runtime. function CreateServer(%mission, %missionType) { // Perform the default exec's parent::CreateServer(%mission, %missionType); // Ensure that the DXAI is active. - $DXAI::System::InvalidatedEnvironment = true; + DXAI::validateEnvironment(); } // Make this do nothing so the bots don't ever get any objectives by default @@ -335,5 +251,6 @@ package DXAI_Hooks } }; +// Only activate the package if it isn't already active. if (!isActivePackage(DXAI_Hooks)) activatePackage(DXAI_Hooks); diff --git a/scripts/DXAI/objectives.cs b/scripts/DXAI/objectives.cs new file mode 100644 index 0000000..48a6226 --- /dev/null +++ b/scripts/DXAI/objectives.cs @@ -0,0 +1,159 @@ +//------------------------------------------------------------------------------------------ +// main.cs +// Source file for the DXAI enhanced objective implementations. +// 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. +//------------------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------------------ +// +Param %bot.escortTarget: The ID of the object to escort. This can be literally +// any object that exists in the game world. +// +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 +// 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. +// If the bot were to be knocked too far away, then this logic will simply start all over +// again. +//------------------------------------------------------------------------------------------ +function AIEnhancedEscort::initFromObjective(%task, %objective, %client) { } +function AIEnhancedEscort::assume(%task, %client) { %task.setMonitorFreq(1); } +function AIEnhancedEscort::retire(%task, %client) { %client.isMoving = false; %client.manualAim = false; } +function AIEnhancedEscort::weight(%task, %client) { %task.setWeight(1000); } + +function AIEnhancedEscort::monitor(%task, %client) +{ + // Is our escort object even a thing? + if (!isObject(%client.escortTarget)) + return; + + %escortLocation = %client.escortTarget.getPosition(); + + // Pick a location near the target + // 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. + %client.isMoving = true; + %client.manualAim = true; + %client.aimLocation = %escortLocation; + + %client.moveLocation = getRandomPositionOnTerrain(%escortLocation, 40); +} + +//------------------------------------------------------------------------------------------ +// +Param %bot.defendLocation: The X Y Z coordinates of the location that this bot +// must attempt to defend. +// +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 +// 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. +// If the bot were to be knocked too far away, then this logic will simply start all over +// again. +//------------------------------------------------------------------------------------------ +function AIEnhancedDefendLocation::initFromObjective(%task, %objective, %client) { } +function AIEnhancedDefendLocation::assume(%task, %client) { %task.setMonitorFreq(1); } +function AIEnhancedDefendLocation::retire(%task, %client) { %client.isMoving = false; } +function AIEnhancedDefendLocation::weight(%task, %client) { %task.setWeight(1000); } + +function AIEnhancedDefendLocation::monitor(%task, %client) +{ + if (%client.getPathDistance(%client.defendLocation) <= 40) + { + // Pick a random time to move to a nearby location + if (%client.defendTime == -1) + { + %client.nextDefendRotation = getRandom(5000, 10000); + %client.isMoving = false; + } + + // If we're near our random point, just don't move + if (%client.getPathDistance(%client.moveLocation) <= 10) + %client.isMoving = false; + + %client.defendTime += 32; + if (%client.defendTime >= %client.nextDefendRotation) + { + %client.defendTime = 0; + %client.nextDefendRotation = getRandom(5000, 10000); + + // TODO: Replace with something that detects interiors as well + %client.moveLocation = getRandomPositionOnTerrain(%client.defendLocation, 40); + %client.isMoving = true; + } + } + else + { + %client.defendTime = -1; + %client.moveLocation = %client.defendLocation; + %client.isMoving = true; + } +} + +//------------------------------------------------------------------------------------------ +// +Param %bot.scoutLocation: The X Y Z coordinates of the location that this bot +// must attempt to scout. +// +Param %bot.scoutDistance: The maximum distance that this bot will attempt to scout +// out around %bot.scoutLocation. +// +Description: The AIEnhancedScoutLocation does exactly as the name implies. The +// behavior a bot will exhibit with this code is that the bot will pick random nodes from +// the navigation graph that is within %bot.scoutDistance of %bot.scoutLocation and head +// to that chosen node. This produces a bot that will wander around the given location, +// including into and through interiors & other noded obstructions. +//------------------------------------------------------------------------------------------ +function AIEnhancedScoutLocation::initFromObjective(%task, %objective, %client) { } +function AIEnhancedScoutLocation::assume(%task, %client) { %task.setMonitorFreq(1); %client.currentNode = -1; } +function AIEnhancedScoutLocation::retire(%task, %client) { } +function AIEnhancedScoutLocation::weight(%task, %client) { %task.setWeight(1000); } + +function AIEnhancedScoutLocation::monitor(%task, %client) +{ + // We can't really work without a NavGraph + if (!isObject(NavGraph)) + return; + + // We just received the task, so find a node within distance of our scout location + if (%client.currentNode == -1) + { + %client.currentNode = NavGraph.randNode(%client.scoutLocation, %client.scoutDistance, true, true); + + if (%client.currentNode != -1) + { + %client.moveLocation = NavGraph.nodeLoc(%client.currentNode); + %client.isMoving = true; + } + } + // We're moving, or are near enough to our target + else + { + // Don't move if we're close enough to our next node + if (%client.getPathDistance(%client.moveLocation) <= 40 && %client.isMoving) + { + %client.isMoving = false; + %client.nextScoutRotation = getRandom(5000, 10000); + %client.scoutTime += 32; + } + else + { + %client.isMoving = true; + %client.scoutTime = 0; + } + + // Wait a little bit at each node + if (%client.scoutTime >= %client.nextScoutRotation) + { + %client.scoutTime = 0; + %client.nextScoutRotation = getRandom(5000, 10000); + + // Pick a new node + %client.currentNode = NavGraph.randNode(%client.scoutLocation, %client.scoutDistance, true, true); + + // Ensure that we found a node. + if (%client.currentNode != -1) + { + %client.moveLocation = NavGraph.nodeLoc(%client.currentNode); + %client.isMoving = true; + } + } + } +} +//------------------------------------------------------------------------------------------ \ No newline at end of file diff --git a/scripts/DXAI/priorityqueue.cs b/scripts/DXAI/priorityqueue.cs new file mode 100644 index 0000000..377b007 --- /dev/null +++ b/scripts/DXAI/priorityqueue.cs @@ -0,0 +1,95 @@ +//------------------------------------------------------------------------------------------ +// priorityqueue.cs +// Source file for the priority queue 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 PriorityQueue::add(%this, %key, %value) +{ + // Traverse the queue and discover our insertion point + for (%iteration = 0; %iteration < %this.count; %iteration++) + if (%key >= %this.keys[%iteration]) + { + %this._shift(%iteration, false); + %this.values[%iteration] = %value; + %this.keys[%iteration] = %key; + %this.keyIndex[%key] = %iteration; + %this.hasKey[%key] = true; + 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; +} + +function PriorityQueue::remove(%this, %key) +{ + if (!%this.hasKey[%key]) + return; + + %this.hasKey[%key] = false; + %this._shift(%this.keyIndex[%key], true); + %this.count--; +} + +function PriorityQueue::_shift(%this, %index, %isRemoval) +{ + if (%isRemoval) + { + for (%iteration = %index; %iteration < %this.count; %iteration++) + { + %this.values[%index] = %this.values[%index + 1]; + %this.keys[%index] = %this.keys[%index + 1; + } + + return; + } + + for (%iteration = %index; %iteration < %this.count; %iteration++) + { + %this.values[%index + 1] = %this.values[%index]; + %this.keys[%index + 1] = %this.keys[%index]; + } +} + +function PriorityQueue::topValue(%this) +{ + return %this.values[%this.count - 1]; +} + +function PriorityQueue::topKey(%this) +{ + return %this.keys[%this.count - 1]; +} + +function PriorityQueue::pop(%this) +{ + %this.hasKey[%this.keys[%this.count]] = false; + %this.count--; +} + +function PriorityQueue::clear(%this) +{ + for (%iteration = 0; %iteration < %this.count; %iteration++) + %this.hasKey[%this.keys[%iteration]] = false; + + %this.count = 0; +} + +function Priorityqueue::isEmpty(%this) +{ + return %this.count == 0; +} + +function PriorityQueue::create(%name) +{ + return new ScriptObject(%name) + { + class = "PriorityQueue"; + count = 0; + }; +} \ No newline at end of file