mirror of
https://github.com/Ragora/T2-DXAI.git
synced 2026-01-19 18:14:45 +00:00
A couple minor fixes and added some documentation
This commit is contained in:
parent
5afe51ec0d
commit
ea665f4095
|
|
@ -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))
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
Loading…
Reference in a new issue