A couple minor fixes and added some documentation

This commit is contained in:
Robert MacGregor 2015-10-07 17:23:47 -04:00
parent 5afe51ec0d
commit ea665f4095
5 changed files with 212 additions and 50 deletions

View file

@ -1,7 +1,7 @@
//------------------------------------------------------------------------------------------
// aiconnection.cs
// 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
//
// Copyright (c) 2014 Robert MacGregor
@ -9,12 +9,21 @@
// Refer to LICENSE.txt for more information.
//------------------------------------------------------------------------------------------
//------------------------------------------------------------------------------------------
// Description: This initializes some basic values on the given AIConnection object such
// as the fieldOfView and the viewDistance. It isn't supposed to do anything else.
//------------------------------------------------------------------------------------------
function AIConnection::initialize(%this)
{
%this.fieldOfView = 3.14 / 2; // 90* View cone
%this.viewDistance = 300;
%this.fieldOfView = $DXAI::Bot::DefaultFieldOfView;
%this.viewDistance = $DXAI::Bot::DefaultViewDistance;
}
//------------------------------------------------------------------------------------------
// Description: An update function that is called by the commander code itself once every
// 32 milliseconds. It is what controls the bot's legs (movement) as well as the aiming
// and firing logic.
//------------------------------------------------------------------------------------------
function AIConnection::update(%this)
{
if (isObject(%this.player) && %this.player.getState() $= "Move")
@ -24,12 +33,25 @@ function AIConnection::update(%this)
}
}
//------------------------------------------------------------------------------------------
// 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
// where the shooter is like the original AI did.
// NOTE: This is automatically called by the main system and therefore should not be called
// directly.
//------------------------------------------------------------------------------------------
function AIConnection::notifyProjectileImpact(%this, %data, %proj, %position)
{
if (!isObject(%proj.sourceObject) || %proj.sourceObject.client.team == %this.team)
return;
}
//------------------------------------------------------------------------------------------
// Description: Returns whether or not the given AIConnection is considered by be 'idle'.
// This is determined by checking whether or not the AIConnection is in their associated
// commander's idle bot list. If the AIConnection has no commander, then true is always
// returned.
//------------------------------------------------------------------------------------------
function AIConnection::isIdle(%this)
{
if (!isObject(%this.commander))
@ -38,6 +60,10 @@ function AIConnection::isIdle(%this)
return %this.commander.idleBotList.isMember(%this);
}
//------------------------------------------------------------------------------------------
// Description: Basically resets the entire state of the given AIConnection. It does not
// unassign tasks, but it does reset the bot's current movement state.
//------------------------------------------------------------------------------------------
function AIConnection::reset(%this)
{
// AIUnassignClient(%this);
@ -57,6 +83,15 @@ function AIConnection::reset(%this)
aiReleaseHumanControl(%this.controlByHuman, %this);
}
//------------------------------------------------------------------------------------------
// Description: Tells the AIConnection to move to a given position. They will automatically
// plot a path and attempt to navigate there.
// Param %position: The target location to move to. If this is simply -1, then all current
// moves will be cancelled.
// NOTE: This should only be called by the bot's current active task. If this is called
// outside of the AI task system, then the move order is very liable to be overwritten by
// the current running task in it's next monitor call.
//------------------------------------------------------------------------------------------
function AIConnection::setMoveTarget(%this, %position)
{
if (%position == -1)
@ -77,24 +112,50 @@ function AIConnection::setMoveTarget(%this, %position)
%this.maximumPathDistance = -9999;
}
//------------------------------------------------------------------------------------------
// Description: Tells the AIConnection to follow a given target object.
// Param %target: The ID of the target object to be following. If the target does not exist,
// nothing happens. If the target is -1, then all current moves will be cancelled.
// Param %minDistance: The minimum following distance that the bot should enforce.
// Param %maxDistance: The maximum following dinstance that the bot should enforce.
// Param %hostile: A boolean representing whether or not the bot should perform evasion
// while maintaining a follow distance between %minDistance and %maxDistance.
// NOTE: This should only be called by the bot's current active task. If this is called
// outside of the AI task system, then the move order is very liable to be overwritten by
// the current running task in it's next monitor call.
//------------------------------------------------------------------------------------------
function AIConnection::setFollowTarget(%this, %target, %minDistance, %maxDistance, %hostile)
{
if (!isObject(%target))
if (%target == -1)
{
%this.reset();
%this.isMovingToTarget = false;
%this.isFollowingTarget = false;
return;
}
if (!isObject(%target))
return;
%this.followTarget = %target;
%this.isFollowingTarget = true;
%this.followMinDistance = %minDistance;
%this.followMaxDistance = %maxDistance;
%this.followHostile = %hostile;
// TODO: Implement custom follow logic to respect %minDistance, %maxDistance and %hostile.
// Perhaps a specific combination of these values will trigger the default escort logic:
// A min distance of 10 or less, a max distance of 20 or less and not hostile?
%this.stepEscort(%target);
}
//------------------------------------------------------------------------------------------
// Description: A function that is used to determine whether or not the given AIConnection
// appears to be stuck somewhere. Currently, it works by tracking how far along the current
// path a given bot is once every 5 seconds. If there appears to have been no good progress
// between calls, then the bot is marked as stuck.
// NOTE: This is called automatically on its own scheduled tick and shouldn't be called
// directly.
//------------------------------------------------------------------------------------------
function AIConnection::stuckCheck(%this)
{
if (isEventPending(%this.stuckCheckTick))
@ -118,6 +179,13 @@ function AIConnection::stuckCheck(%this)
%this.stuckCheckTick = %this.schedule(5000, "stuckCheck");
}
//------------------------------------------------------------------------------------------
// Description: A function called by the ::update function of the AIConnection that is
// called once every 32ms by the commander AI logic to update the bot's current move
// logic.
// NOTE: This is automatically called by the commander AI and therefore should not be
// called directly.
//------------------------------------------------------------------------------------------
function AIConnection::updateLegs(%this)
{
%now = getSimTime();
@ -130,37 +198,6 @@ function AIConnection::updateLegs(%this)
%this.aimAt(%this.moveTarget);
else if(%this.manualAim)
%this.aimAt(%this.moveTarget);
%targetDistance = %this.pathDistRemaining(9000);
if (%targetDistance > %this.maximumPathDistance)
%this.maximumPathDistance = %targetDistance;
if (%targetDistance < %this.minimumPathDistance)
%this.minimumPathDistance = %targetDistance;
// Bots follow a set of lines drawn between nodes to slowly decrement the path distance,
// so bots that are stuck usually get their remaining distance stuck in some range of
// arbitrary values, so we monitor the minimum and maximum values over a period of 5 seconds
// Test...
%pathDistance = %this.getPathDistance(%this.moveTarget);
if(%pathDistance > 10 && %this.moveTravelTime < 10000)
%this.moveTravelTime += %delta;
else if (%pathDistance < 10)
%this.moveTravelTime = 0;
else if (%this.moveTravelTime >= 10000)
{
// We appear to be stuck, so pick a random nearby node and try to run to it
%this.moveTravelTime = 0;
%this.isPathCorrecting = true;
if (isObject(NavGraph))
{
%randomNode = NavGraph.randNode(%this.player.getPosition(), 200, true, true);
if (%randomNode != -1)
%this.setMoveTarget(NavGraph.nodeLoc(%randomNode));
}
}
}
else if (%this.isFollowingTarget)
{
@ -173,6 +210,13 @@ function AIConnection::updateLegs(%this)
}
}
//------------------------------------------------------------------------------------------
// Description: A function called by the ::update function of the AIConnection that is
// called once every 32ms by the commander AI logic to update the bot's current aiming &
// engagement logic.
// NOTE: This is automatically called by the commander AI and therefore should not be
// called directly.
//------------------------------------------------------------------------------------------
function AIConnection::updateWeapons(%this)
{
if (isObject(%this.engageTarget))
@ -195,17 +239,31 @@ function AIConnection::updateWeapons(%this)
}
}
//------------------------------------------------------------------------------------------
// Description: A function called randomly on time periods between
// $DXAI::Bot::MinimumVisualAcuityTime and $DXAI::Bot::MaximumVisualAcuityTime which
// attempts to simulate Human eyesight using a complex view cone algorithm implemented
// entirely in Torque Script.
// Param %bot.enableVisualDebug: A boolean assigned to an individual bot that is used to
// enable or disable the visual debug feature. This feature, when enabled, will draw the
// bot's view cone using waypoints placed at the individual points of the view cone and is
// updated once per tick of this function.
// NOTE: This is called automatically using its own scheduled ticks and therefore should
// not be called directly.
//------------------------------------------------------------------------------------------
function AIConnection::updateVisualAcuity(%this)
{
if (isEventPending(%this.visualAcuityTick))
cancel(%this.visualAcuityTick);
// 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")
{
%this.visualAcuityTick = %this.schedule(getRandom(230, 400), "updateVisualAcuity");
%this.visualAcuityTick = %this.schedule(getRandom($DXAI::Bot::MinimumVisualAcuityTime, $DXAI::Bot::MaximumVisualAcuityTime,), "updateVisualAcuity");
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))

View file

@ -8,11 +8,31 @@
// Refer to LICENSE.txt for more information.
//------------------------------------------------------------------------------------------
$DXAI::Commander::minimumFlagDefense = 1;
$DXAI::Commander::minimumGeneratorDefense = 1;
$DXAI::Commander::MinimumFlagDefense = 1;
$DXAI::Commander::MinimumGeneratorDefense = 1;
$DXAI::Bot::DefaultFieldOfView = 3.14159 / 2; // 90*
// This is the default view angle that bots will use. Probably
// shouldn't be changed much as I've never seen any mod ever that
// actually changed player FOV as part of its gameplay short of
// zooming.
$DXAI::Bot::DefaultFieldOfView = 3.14159 / 2;
// This is the default view distance that bots will be able to see for.
// This isn't necessarily the view distance they will use, as its more
// or less going to be deprecated in favor of one calculated from the
// map fog.
$DXAI::Bot::DefaultViewDistance = 300;
// This is the minimum scheduled time in milliseconds that the AI visual
// acuity code will run at. This is more of a setting to tweak performance
// as the associated visual acuity code will perform its own "perceptual"
// chekcs to ensure that bot reaction times are roughly equivalent to that
// of a Human.
$DXAI::Bot::MinimumVisualAcuityTime = 200;
// This is the maximum scheduled time in milliseconds that the AI visual
// acuity code will run at. This is more of a setting to tweak performance
// as the associated visual acuity code will perform its own "perceptual"
// chekcs to ensure that bot reaction times are roughly equivalent to that
// of a Human.
$DXAI::Bot::MaximumVisualAcuityTime = 400;

View file

@ -1,29 +1,45 @@
//------------------------------------------------------------------------------------------
// cyclicset.cs
// Main source file for the CyclicSet implementation.
// Main source file for the CyclicSet implementation. A CyclicSet is simply a set of objects
// 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
// This software is licensed under the MIT license. Refer to LICENSE.txt for more information.
//------------------------------------------------------------------------------------------
function CyclicSet::add(%this, %item)
//------------------------------------------------------------------------------------------
// Description: Adds an object to the cyclic set.
// Param %object: The object to be added to the cyclic set.
//------------------------------------------------------------------------------------------
function CyclicSet::add(%this, %object)
{
%this.set.add(%item);
%this.set.add(%object);
}
//------------------------------------------------------------------------------------------
// Description: An overrided implementation of the .delete() function that will properly
// cleanup the cyclic set before deleting itself proper.
//------------------------------------------------------------------------------------------
function CyclicSet::delete(%this)
{
%this.set.delete();
ScriptObject::delete(%this);
}
//------------------------------------------------------------------------------------------
// Description: Clears the cyclic set of all objects of which none are deleted.
//------------------------------------------------------------------------------------------
function CyclicSet::clear(%this)
{
%this.index = 0;
%this.set.clear();
}
//------------------------------------------------------------------------------------------
// Description: Gets the next object in the cyclic set.
// Return: The next object ID in the cyclic set.
//------------------------------------------------------------------------------------------
function CyclicSet::next(%this)
{
if (%this.set.getCount() == 0)
@ -37,18 +53,42 @@ function CyclicSet::next(%this)
return %result;
}
function CyclicSet::randomize(%this)
//------------------------------------------------------------------------------------------
// Description: Randomizes the index that the cyclic set is currently at.
//------------------------------------------------------------------------------------------
function CyclicSet::randomizeIndex(%this)
{
%this.index = getRandom(0, %this.set.getCount());
}
function CyclicSet::create(%name)
//------------------------------------------------------------------------------------------
// Description: Creates a new cyclic set with the given name.
// Param %name: The name to give to the new cyclic set.
// Param %container: If specified, the cyclic set will copy data contained in the given
// container into itself. This container be a SimGroup, a SimSet or another cyclic set.
// Return: The ID of the new cyclic set.
//
// Usage: %set = CyclicSet::create("MySet");
//------------------------------------------------------------------------------------------
function CyclicSet::create(%name, %container)
{
%set = new SimSet();
return new ScriptObject(%name)
%result = new ScriptObject(%name)
{
index = 0;
class = "CyclicSet";
set = %set;
};
if (isObject(%container))
{
if (%container.class $= "CyclicSet")
%container = %container.set;
if (%container.getClassName() $= "SimSet" || %container.getClassName() $= "SimGroup")
for (%iteration = 0; %iteration < %container.getCount(); %iteration++)
%result.set.add(%container.getObject(%iteration));
}
return %result;
}

View file

@ -379,7 +379,6 @@ function AIEnhancedFlagCaptureTask::monitor(%task, %client)
if (!isObject(%client.targetCaptureFlag))
return;
%client.isMovingToTarget = true;
if (%client.targetCaptureFlag.getObjectMount() != %client.player)
%client.setMoveTarget(%client.targetCaptureFlag.getPosition());
else

View file

@ -3,10 +3,21 @@
// Source file for the priority queue implementation.
// https://github.com/Ragora/T2-DXAI.git
//
// FIXME: Make the keys regular priorities so more than one value can occupy the same
// priority value.
//
// Copyright (c) 2014 Robert MacGregor
// This software is licensed under the MIT license. Refer to LICENSE.txt for more information.
//------------------------------------------------------------------------------------------
//------------------------------------------------------------------------------------------
// Description: Adds a new value to the priority queue.
// Param %key: The key (or priority) to map to %value. This must be a numeric value that
// can be compared using the relational operators.
// Param %value: The value to map. This can be arbitrary data or object ID's as Torque
// script treats object ID's and regular numerics as the same thing until you try to
// actually use them as an object.
//------------------------------------------------------------------------------------------
function PriorityQueue::add(%this, %key, %value)
{
// If we already have a key, update it
@ -39,16 +50,29 @@ function PriorityQueue::add(%this, %key, %value)
%this.count++;
}
//------------------------------------------------------------------------------------------
// Description: Removes a value from the priority queue with the given key (priority).
// Param %key: The key (priority) to remove from the priority queue.
// Return: A boolean representing whether or not anything was actually removed.
//------------------------------------------------------------------------------------------
function PriorityQueue::remove(%this, %key)
{
if (!%this.hasKey[%key])
return;
return false;
%this.hasKey[%key] = false;
%this._shift(%this.keyIndex[%key], true);
%this.count--;
return true;
}
//------------------------------------------------------------------------------------------
// Description: An internal function used by the priority queue to shift values around.
// Param %index: The index to start at.
// Param %isRemoval: A boolean representing whether or not this shift is supposed to be
// a removal.
// NOTE: This is an internal function and therefore should not be invoked directly.
//------------------------------------------------------------------------------------------
function PriorityQueue::_shift(%this, %index, %isRemoval)
{
if (%isRemoval)
@ -73,13 +97,32 @@ function PriorityQueue::_shift(%this, %index, %isRemoval)
}
}
//------------------------------------------------------------------------------------------
// Description: Returns the value in this priority queue with the current highest known
// priority.
// Return: The current value with the highest known priority. This returns -1 in the event
// that the priority queue is empty. However, this may be a valid value in whatever is
// using the priority queue so the ::isEmpty function should be used.
//------------------------------------------------------------------------------------------
function PriorityQueue::topValue(%this)
{
if (%this.count <= 0)
return -1;
return %this.values[%this.count - 1];
}
//------------------------------------------------------------------------------------------
// Description: Returns the highest key (priority)
// Return: The current value with the highest known key *priority. This returns -1 in the event
// that the priority queue is empty. However, this may be a valid value in whatever is
// using the priority queue so the ::isEmpty function should be used.
//------------------------------------------------------------------------------------------
function PriorityQueue::topKey(%this)
{
if (%this.count <= 0)
return -1;
return %this.keys[%this.count - 1];
}
@ -113,9 +156,11 @@ function PriorityQueue::dump(%this)
function PriorityQueue::create(%name)
{
return new ScriptObject(%name)
%result = new ScriptObject(%name)
{
class = "PriorityQueue";
count = 0;
};
return %result;
}