From 167137f755f93d5d8d868ad75bd032649b55cfa1 Mon Sep 17 00:00:00 2001 From: Robert MacGregor Date: Sat, 10 Oct 2015 16:08:43 -0400 Subject: [PATCH] Added documentation --- LICENSE | 2 +- scripts/DXAI/aicommander.cs | 101 +++++++++++++++++++++++++++++++--- scripts/DXAI/aiconnection.cs | 2 +- scripts/DXAI/config.cs | 2 +- scripts/DXAI/cyclicset.cs | 2 +- scripts/DXAI/helpers.cs | 25 ++++++++- scripts/DXAI/loadouts.cs | 2 +- scripts/DXAI/main.cs | 85 ++++++++++++++++++++++++---- scripts/DXAI/objectives.cs | 11 +--- scripts/DXAI/priorityqueue.cs | 2 +- 10 files changed, 198 insertions(+), 36 deletions(-) diff --git a/LICENSE b/LICENSE index 9492ded..2dca643 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2014 Robert MacGregor +Copyright (c) 2015 Robert MacGregor Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/scripts/DXAI/aicommander.cs b/scripts/DXAI/aicommander.cs index f341f14..577a66f 100644 --- a/scripts/DXAI/aicommander.cs +++ b/scripts/DXAI/aicommander.cs @@ -3,7 +3,22 @@ // Source file for the DXAI commander AI implementation. // https://github.com/Ragora/T2-DXAI.git // -// Copyright (c) 2014 Robert MacGregor +// The AICommander type is a complex beast. They have the following proerties associated +// with them: +// * %commander.botList: A SimSet of all bots that are currently associated with the +// given commander. +// * %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 +// not doing anything particularly helpful. +// * %commander.botAssignments[%assignmentID]: An associative container that maps +// assignment ID's (those desiginated by $DXAI::Priorities::*) to the total number of +// bots assigned. +// * %commander.objectiveCycles[%assignmentID]: An associative container that maps assignment +// 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 +// set for the team. +// +// Copyright (c) 2015 Robert MacGregor // This software is licensed under the MIT license. Refer to LICENSE.txt for more information. //------------------------------------------------------------------------------------------ @@ -18,6 +33,12 @@ $DXAI::Priorities::CaptureObjective = 5; $DXAI::Priorities::AttackTurret = 6; $DXAI::Priorities::Count = 3; +//------------------------------------------------------------------------------------------ +// 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. +// +// 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::DefendFlag] = 3; $DXAI::Priorities::DefaultPriorityValue[$DXAI::Priorities::ScoutBase] = 1; @@ -26,6 +47,11 @@ $DXAI::Priorities::Text[$DXAI::Priorities::DefendGenerator] = "Defending a Gener $DXAI::Priorities::Text[$DXAI::Priorities::DefendFlag] = "Defending the Flag"; $DXAI::Priorities::Text[$DXAI::Priorities::ScoutBase] = "Scouting a Location"; +//------------------------------------------------------------------------------------------ +// Description: Sets up the AI commander by creating the bot list sim sets as well as +// claiming the bots that are currently on their team. Bots claimed will have all of their +// independent ticks started up such as the visual acuity tick. +//------------------------------------------------------------------------------------------ function AICommander::setup(%this) { %this.botList = new SimSet(); @@ -44,6 +70,8 @@ function AICommander::setup(%this) %currentClient.initialize(); %currentClient.visibleHostiles = new SimSet(); + + // Start our ticks. %currentClient.updateVisualAcuity(); %currentClient.stuckCheck(); } @@ -59,6 +87,14 @@ function AICommander::setup(%this) } } +//------------------------------------------------------------------------------------------ +// Description: Skims the objectives for the AI commander's team and pulls out any that +// we can use when assigning tasks to bots. This is done with some recursion down the line +// of nested SimGroup instances. When a usable objective is located, it is added to the +// cycler associated with the most appropriate task. +// Param %group: The group to recurse down. +// NOTE: This is an internal function and therefore should not be called directly. +//------------------------------------------------------------------------------------------ function AICommander::_skimObjectiveGroup(%this, %group) { for (%iteration = 0; %iteration < %group.getCount(); %iteration++) @@ -93,6 +129,11 @@ function AICommander::_skimObjectiveGroup(%this, %group) } } +//------------------------------------------------------------------------------------------ +// Description: Loads or reloads the objectives for the AI commander's team. It searches +// for the AIObjectives group associated with the team (all teams have one called that) +// and passes it off to _skimObjectGroup which does the actual objective processing. +//------------------------------------------------------------------------------------------ function AICommander::loadObjectives(%this) { // First we clear the old cyclers @@ -133,6 +174,16 @@ function AICommander::loadObjectives(%this) %this.objectiveCycles[$DXAI::Priorities::ScoutBase].add(%scoutLocationObjective); } +//------------------------------------------------------------------------------------------ +// Description: Distributes and assigns tasks to all bots under the jurisdiction of the +// AI commander according to current tasks priorities. +// +// TODO: Assign something to bots that are considered to be idle so they can stop sitting +// on top of the inventory stations like lazy bums. +// FIXME: This will be called when the commander wants to distribute tasks differently +// than what was previous set, and so blindly instructing bots to rearm isn't exactly +// the best choice in hindsight. +//------------------------------------------------------------------------------------------ function AICommander::assignTasks(%this) { // First, assign objectives that all bots should have @@ -214,6 +265,9 @@ function AICommander::assignTasks(%this) %priorityQueue.delete(); } +//------------------------------------------------------------------------------------------ +// Description: +//------------------------------------------------------------------------------------------ function AICommander::deassignBots(%this, %taskID, %count) { // TODO: More efficient removal? @@ -271,12 +325,23 @@ function AICommander::assignTask(%this, %taskID, %bot) %bot.assignment = %taskID; } +//------------------------------------------------------------------------------------------ +// Description: Helper function that is used to set the default task prioritizations on +// the commander object. +//------------------------------------------------------------------------------------------ function AICommander::setDefaultPriorities(%this) { for (%iteration = 0; %iteration < $DXAI::Priorities::Count; %iteration++) %this.priorities[%iteration] = $DXAI::Priorities::DefaultPriorityValue[%iteration]; } +//------------------------------------------------------------------------------------------ +// Description: Performs a deinitialization that should be ran before deleting the +// commander object itself. +// +// NOTE: This is called automatically by .delete so this shouldn't have to be called +// directly. +//------------------------------------------------------------------------------------------ function AICommander::cleanUp(%this) { for (%iteration = 0; %iteration < %this.botList.getCount(); %iteration++) @@ -285,17 +350,31 @@ function AICommander::cleanUp(%this) cancel(%current.visualAcuityTick); cancel(%current.stuckCheckTick); } - + %this.botList.delete(); %this.idleBotList.delete(); } +//------------------------------------------------------------------------------------------ +// Description: An overwritten delete function to perform proper cleanup before actually +// deleting the commander object. +//------------------------------------------------------------------------------------------ +function AICommander::delete(%this) +{ + %this.cleanUp(); + ScriptObject::delete(%this); +} + function AICommander::update(%this) { for (%iteration = 0; %iteration < %this.botList.getCount(); %iteration++) %this.botList.getObject(%iteration).update(); } +//------------------------------------------------------------------------------------------ +// Description: Removes the given bot from this AI commander's jurisdiction. +// Param %bot: The AIConnection to remove. +//------------------------------------------------------------------------------------------ function AICommander::removeBot(%this, %bot) { %this.botList.remove(%bot); @@ -304,15 +383,23 @@ function AICommander::removeBot(%this, %bot) %bot.commander = -1; } +//------------------------------------------------------------------------------------------ +// Description: Adds the given bot to the jurisdiction of this AI commander if the bot and +// commander work for the same team. +// Param %bot: The AIConnection to add. +// Return: A boolean representing whether or not the bot was successfully added. False is +// returned if the two are not on the same time at the time of adding. +//------------------------------------------------------------------------------------------ function AICommander::addBot(%this, %bot) { - if (!%this.botList.isMember(%bot)) - %this.botList.add(%bot); - - if (!%this.idleBotList.isMember(%bot)) - %this.idleBotList.add(%bot); + if (%bot.team != %this.team) + return false; + + %this.botList.add(%bot); + %this.idleBotList.add(%bot); %bot.commander = %this; + return true; } function AICommander::notifyPlayerDeath(%this, %killedClient, %killedByClient) diff --git a/scripts/DXAI/aiconnection.cs b/scripts/DXAI/aiconnection.cs index ad72e04..e8bf532 100644 --- a/scripts/DXAI/aiconnection.cs +++ b/scripts/DXAI/aiconnection.cs @@ -4,7 +4,7 @@ // AI enhancement project. // https://github.com/Ragora/T2-DXAI.git // -// Copyright (c) 2014 Robert MacGregor +// Copyright (c) 2015 Robert MacGregor // This software is licensed under the MIT license. // Refer to LICENSE.txt for more information. //------------------------------------------------------------------------------------------ diff --git a/scripts/DXAI/config.cs b/scripts/DXAI/config.cs index c437b96..0de475e 100644 --- a/scripts/DXAI/config.cs +++ b/scripts/DXAI/config.cs @@ -3,7 +3,7 @@ // Configuration file for the experimental DXAI system. // https://github.com/Ragora/T2-DXAI.git // -// Copyright (c) 2014 Robert MacGregor +// Copyright (c) 2015 Robert MacGregor // This software is licensed under the MIT license. // Refer to LICENSE.txt for more information. //------------------------------------------------------------------------------------------ diff --git a/scripts/DXAI/cyclicset.cs b/scripts/DXAI/cyclicset.cs index 6e8764b..6ae9a2a 100644 --- a/scripts/DXAI/cyclicset.cs +++ b/scripts/DXAI/cyclicset.cs @@ -4,7 +4,7 @@ // that is cycled through from start to finish before looping back to start. // https://github.com/Ragora/T2-DXAI.git // -// Copyright (c) 2014 Robert MacGregor +// Copyright (c) 2015 Robert MacGregor // This software is licensed under the MIT license. Refer to LICENSE.txt for more information. //------------------------------------------------------------------------------------------ diff --git a/scripts/DXAI/helpers.cs b/scripts/DXAI/helpers.cs index 180d029..b78b990 100644 --- a/scripts/DXAI/helpers.cs +++ b/scripts/DXAI/helpers.cs @@ -3,7 +3,7 @@ // Helper functions used in the experimental DXAI system. // https://github.com/Ragora/T2-DXAI.git // -// Copyright (c) 2014 Robert MacGregor +// Copyright (c) 2015 Robert MacGregor // This software is licensed under the MIT license. // Refer to LICENSE.txt for more information. //------------------------------------------------------------------------------------------ @@ -49,7 +49,9 @@ function pointInTriangle(%point, %a, %b, %c) // as the game's scripting environment for the gameplay is single threaded // and it probably does a hash to store the values. // FIXME: Mathematical optimizations, right now it's a hack because of no -// reliable way of getting a player's X facing? +// reliable way of getting a player's X facing? Also, the horizontal view cones may +// be all that's necessary. A player height check could be used to help alleviate +// computational complexity. //------------------------------------------------------------------------------------------ function GameConnection::calculateViewCone(%this, %distance) { @@ -243,6 +245,9 @@ function GameConnection::getObjectsInViewcone(%this, %typeMask, %distance, %perf // Description: Gets a random position somewhere within %distance of the given position. // Param %position: The position to generate a new position around. // Param %distance: The maximum distance the new position may be +// Param %raycast: A boolean representing whether or not a raycast should be made from +// %position to the randomly chosen location to stop on objects that may be in the way. +// This is useful for grabbing positions indoors. //------------------------------------------------------------------------------------------ function getRandomPosition(%position, %distance, %raycast) { @@ -261,13 +266,27 @@ function getRandomPosition(%position, %distance, %raycast) return %result; } +//------------------------------------------------------------------------------------------ +// Description: Gets a random position somewhere within %distance of the given position +// relative to the terrain object using getTerrainHeight. This is faster to use than +// getRandomPosition with the raycast setting if all that is necessary is generating a +// position relative to the terrain object. +// Param %position: The position to generate a new position around. +// Param %distance: The maximum distance the new position may be +//------------------------------------------------------------------------------------------ function getRandomPositionOnTerrain(%position, %distance) { %result = getRandomPosition(%position, %distance); return setWord(%result, 2, getTerrainHeight(%result)); } -function vectorMultiply(%vec1, %vec2) +//------------------------------------------------------------------------------------------ +// Description: Multiplies two vectors together and returns the result. +// Param %vec1: The first vector to multiply. +// Param %vec2: The second vector to multiply. +// Return: The product of the multiplication. +//------------------------------------------------------------------------------------------ +function vectorMult(%vec1, %vec2) { return (getWord(%vec1, 0) * getWord(%vec2, 0)) SPC (getWord(%vec1, 1) * getWord(%vec2, 1)) SPC diff --git a/scripts/DXAI/loadouts.cs b/scripts/DXAI/loadouts.cs index d0c4147..685bd91 100644 --- a/scripts/DXAI/loadouts.cs +++ b/scripts/DXAI/loadouts.cs @@ -4,7 +4,7 @@ // appropriate tasks. // https://github.com/Ragora/T2-DXAI.git // -// Copyright (c) 2014 Robert MacGregor +// Copyright (c) 2015 Robert MacGregor // This software is licensed under the MIT license. Refer to LICENSE.txt for more information. //------------------------------------------------------------------------------------------ diff --git a/scripts/DXAI/main.cs b/scripts/DXAI/main.cs index 7d97859..9026eb9 100644 --- a/scripts/DXAI/main.cs +++ b/scripts/DXAI/main.cs @@ -3,7 +3,7 @@ // Main source file for the DXAI experimental AI enhancement project. // https://github.com/Ragora/T2-DXAI.git // -// Copyright (c) 2014 Robert MacGregor +// Copyright (c) 2015 Robert MacGregor // This software is licensed under the MIT license. Refer to LICENSE.txt for more information. //------------------------------------------------------------------------------------------ @@ -16,20 +16,28 @@ exec("scripts/DXAI/priorityqueue.cs"); exec("scripts/DXAI/cyclicset.cs"); exec("scripts/DXAI/loadouts.cs"); -// General DXAI API implementations +//------------------------------------------------------------------------------------------ +// Description: This cleanup function is called when the mission ends to clean up all +// active commanders and to delete them for the next mission which guarantees a blank +// slate. +//------------------------------------------------------------------------------------------ function DXAI::cleanup() { $DXAI::System::Setup = false; for (%iteration = 1; %iteration < $DXAI::ActiveCommanderCount + 1; %iteration++) - { - $DXAI::ActiveCommander[%iteration].cleanup(); $DXAI::ActiveCommander[%iteration].delete(); - } $DXAI::ActiveCommanderCount = 0; } +//------------------------------------------------------------------------------------------ +// Description: This cleanup function is called when the mission starts to to instantiate +// and setup all AI commanders for the game. +// Param %numTeams: The number of teams to initialize for. +// +// TODO: Perhaps calculate %numTeams from the game object? +//------------------------------------------------------------------------------------------ function DXAI::setup(%numTeams) { // Mark the environment as invalidated for each new run so that our hooks @@ -61,6 +69,21 @@ function DXAI::setup(%numTeams) $DXAI::ActiveCommanderCount = %numTeams; } +//------------------------------------------------------------------------------------------ +// 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 +// can't simply hook DefaultGame functions base game modes will declare their own and so +// we'll need to hook those functions post-start as the game mode scripts are executed for +// each mission run. +// Description: This function is called once per update tick (roughly 32 milliseconds) to +// check that the hooks we need are actually active if the system detects that may be a +// necessity to do so. A runtime check is initiated at gamemode start and for each exec +// call made during runtime as any given exec can overwrite the hooks we required. +// If they were not overwritten, the function will return 11595 and do nothing else if the +// appropriate dummy parameters are passed in. +// +// TODO: Perhaps calculate %numTeams from the game object? +//------------------------------------------------------------------------------------------ function DXAI::validateEnvironment() { %gameModeName = $CurrentMissionType @ "Game"; @@ -90,6 +113,14 @@ function DXAI::validateEnvironment() $DXAI::System::InvalidatedEnvironment = false; } +//------------------------------------------------------------------------------------------ +// Description: This update function is scheduled to be called roughly once every 32 +// milliseconds which updates each active commander in the game as well as performs +// an environment validation if necessary. +// +// NOTE: This is called on its own scheduled tick, therefore it should not be called +// directly. +//------------------------------------------------------------------------------------------ function DXAI::update() { if (isEventPending($DXAI::updateHandle)) @@ -116,9 +147,19 @@ function DXAI::notifyPlayerDeath(%killed, %killedBy) $DXAI::ActiveCommander[%iteration].notifyPlayerDeath(%killed, %killedBy); } -// Hooks for the AI System +//------------------------------------------------------------------------------------------ +// Description: There is a series of functions that the AI code can safely hook without +// worry of being overwritten implicitly such as the disconnect or exec functions. For +// those that can be, there is an environment validation that is performed to ensure that +// the necessary code will be called in response to the events we need to know about in +// this AI system. +//------------------------------------------------------------------------------------------ package DXAI_Hooks { + //------------------------------------------------------------------------------------------ + // Description: Called when the mission ends. We use this to perform any necessary cleanup + // operations between games. + //------------------------------------------------------------------------------------------ function DefaultGame::gameOver(%game) { parent::gameOver(%game); @@ -126,6 +167,10 @@ package DXAI_Hooks DXAI::cleanup(); } + //------------------------------------------------------------------------------------------ + // Description: Called when the mission starts. We use this to perform initialization and + // to start the update ticks. + //------------------------------------------------------------------------------------------ function DefaultGame::startMatch(%game) { parent::startMatch(%game); @@ -134,14 +179,22 @@ package DXAI_Hooks DXAI::update(); } - // Listen server fix + //------------------------------------------------------------------------------------------ + // Description: We hook the disconnect function as a step to fix console spam from leaving + // a listen server due to the AI code continuing to run post-server shutdown in those + // cases. + //------------------------------------------------------------------------------------------ function disconnect() { parent::disconnect(); - DXAI::Cleanup(); + DXAI::cleanup(); } + //------------------------------------------------------------------------------------------ + // 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. + //------------------------------------------------------------------------------------------ function DefaultGame::AIChangeTeam(%game, %client, %newTeam) { // Remove us from the old commander's control first @@ -152,6 +205,10 @@ package DXAI_Hooks $DXAI::ActiveCommander[%newTeam].addBot(%client); } + //------------------------------------------------------------------------------------------ + // 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. + //------------------------------------------------------------------------------------------ function AIConnection::onAIDrop(%client) { if (isObject(%client.commander)) @@ -175,6 +232,11 @@ package DXAI_Hooks DXAI::notifyPlayerDeath(%clVictim, %clKiller); } + //------------------------------------------------------------------------------------------ + // Description: We hook this function to implement some basic sound simulation for bots. + // This means that if something explodes, a bot will hear it and if the sound is close + // enough, they will shimmy away from the source using setDangerLocation. + //------------------------------------------------------------------------------------------ function ProjectileData::onExplode(%data, %proj, %pos, %mod) { parent::onExplode(%data, %proj, %pos, %mod); @@ -223,8 +285,11 @@ 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. + //------------------------------------------------------------------------------------------ + // 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 + // mission load. + //------------------------------------------------------------------------------------------ function CreateServer(%mission, %missionType) { // Perform the default exec's diff --git a/scripts/DXAI/objectives.cs b/scripts/DXAI/objectives.cs index e5a6696..7580a3f 100644 --- a/scripts/DXAI/objectives.cs +++ b/scripts/DXAI/objectives.cs @@ -3,19 +3,10 @@ // Source file for the DXAI enhanced objective implementations. // https://github.com/Ragora/T2-DXAI.git // -// Copyright (c) 2014 Robert MacGregor +// Copyright (c) 2015 Robert MacGregor // This software is licensed under the MIT license. Refer to LICENSE.txt for more information. //------------------------------------------------------------------------------------------ -// Weights -// AIEnhancedFlagCaptureTask - $DXAI::Task::MediumPriority -// AIEnhancedEscort - $DXAI::Task::MediumPriority -// AIEnhancedDefendLocation - $DXAI::Task::MediumPriority -// AIEnhancedScoutLocation - $DXAI::Task::MediumPriority -// AIEnhancedEngageTarget - $DXAI::Task::VeryHighPriority -// AIEnhancedReturnFlagTask - $DXAI::Task::HighPriority -// AIEnhancedRearmTask - $DXAI::Task::HighPriority - $DXAI::Task::NoPriority = 0; $DXAI::Task::LowPriority = 100; $DXAI::Task::MediumPriority = 200; diff --git a/scripts/DXAI/priorityqueue.cs b/scripts/DXAI/priorityqueue.cs index c76ad56..bab8298 100644 --- a/scripts/DXAI/priorityqueue.cs +++ b/scripts/DXAI/priorityqueue.cs @@ -6,7 +6,7 @@ // FIXME: Make the keys regular priorities so more than one value can occupy the same // priority value. // -// Copyright (c) 2014 Robert MacGregor +// Copyright (c) 2015 Robert MacGregor // This software is licensed under the MIT license. Refer to LICENSE.txt for more information. //------------------------------------------------------------------------------------------