mirror of
https://github.com/Ragora/T2-DXAI.git
synced 2026-07-15 03:24:36 +00:00
Make bots ignore their own fired projectiles so they don't try to dodge it; implement forced jetting; implement a basic engage routine that more or less emulates the base T2 AI's; fix magic numbers in timing code in some AI tasks
This commit is contained in:
parent
274d858679
commit
b9afd41b1c
6 changed files with 619 additions and 324 deletions
|
|
@ -28,10 +28,10 @@ $DXAI::Priorities::DefendGenerator = 0;
|
||||||
$DXAI::Priorities::DefendFlag = 1;
|
$DXAI::Priorities::DefendFlag = 1;
|
||||||
$DXAI::Priorities::ScoutBase = 2;
|
$DXAI::Priorities::ScoutBase = 2;
|
||||||
//-----------------------------------------------
|
//-----------------------------------------------
|
||||||
$DXAI::Priorities::CaptureFlag = 4;
|
$DXAI::Priorities::CaptureFlag = 3;
|
||||||
$DXAI::Priorities::CaptureObjective = 5;
|
$DXAI::Priorities::CaptureObjective = 5;
|
||||||
$DXAI::Priorities::AttackTurret = 6;
|
$DXAI::Priorities::AttackTurret = 6;
|
||||||
$DXAI::Priorities::Count = 3;
|
$DXAI::Priorities::Count = 4;
|
||||||
|
|
||||||
//------------------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------------------
|
||||||
// Description: These global variables are the default priorities that commanders will
|
// Description: These global variables are the default priorities that commanders will
|
||||||
|
|
@ -42,10 +42,12 @@ $DXAI::Priorities::Count = 3;
|
||||||
$DXAI::Priorities::DefaultPriorityValue[$DXAI::Priorities::DefendGenerator] = 2;
|
$DXAI::Priorities::DefaultPriorityValue[$DXAI::Priorities::DefendGenerator] = 2;
|
||||||
$DXAI::Priorities::DefaultPriorityValue[$DXAI::Priorities::DefendFlag] = 3;
|
$DXAI::Priorities::DefaultPriorityValue[$DXAI::Priorities::DefendFlag] = 3;
|
||||||
$DXAI::Priorities::DefaultPriorityValue[$DXAI::Priorities::ScoutBase] = 1;
|
$DXAI::Priorities::DefaultPriorityValue[$DXAI::Priorities::ScoutBase] = 1;
|
||||||
|
$DXAI::Priorities::DefaultPriorityValue[$DXAI::Priorities::CaptureFlag] = 2;
|
||||||
|
|
||||||
$DXAI::Priorities::Text[$DXAI::Priorities::DefendGenerator] = "Defending a Generator";
|
$DXAI::Priorities::Text[$DXAI::Priorities::DefendGenerator] = "Defending a Generator";
|
||||||
$DXAI::Priorities::Text[$DXAI::Priorities::DefendFlag] = "Defending the Flag";
|
$DXAI::Priorities::Text[$DXAI::Priorities::DefendFlag] = "Defending the Flag";
|
||||||
$DXAI::Priorities::Text[$DXAI::Priorities::ScoutBase] = "Scouting a Location";
|
$DXAI::Priorities::Text[$DXAI::Priorities::ScoutBase] = "Scouting a Location";
|
||||||
|
$DXAI::Priorities::Text[$DXAI::Priorities::CaptureFlag] = "Capture the Flag";
|
||||||
|
|
||||||
//------------------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------------------
|
||||||
// Description: Sets up the AI commander by creating the bot list sim sets as well as
|
// Description: Sets up the AI commander by creating the bot list sim sets as well as
|
||||||
|
|
@ -201,7 +203,8 @@ function AICommander::assignTasks(%this)
|
||||||
%bot.addTask(AIEnhancedFlagCaptureTask);
|
%bot.addTask(AIEnhancedFlagCaptureTask);
|
||||||
}
|
}
|
||||||
|
|
||||||
%bot.targetLoadout = 0;
|
// Assign the default loadout
|
||||||
|
%bot.targetLoadout = $DXAI::DefaultLoadout ;
|
||||||
%bot.shouldRearm = true;
|
%bot.shouldRearm = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -310,7 +313,8 @@ function AICommander::assignTask(%this, %taskID, %bot)
|
||||||
%bot.defenseDescription = "generator";
|
%bot.defenseDescription = "generator";
|
||||||
}
|
}
|
||||||
|
|
||||||
%bot.addTask("AIEnhancedDefendLocation");
|
%bot.primaryTask = "AIEnhancedDefendLocation";
|
||||||
|
%bot.addTask(%bot.primaryTask);
|
||||||
|
|
||||||
case $DXAI::Priorities::ScoutBase:
|
case $DXAI::Priorities::ScoutBase:
|
||||||
%objective = %this.objectiveCycles[%taskID].next();
|
%objective = %this.objectiveCycles[%taskID].next();
|
||||||
|
|
@ -318,7 +322,12 @@ function AICommander::assignTask(%this, %taskID, %bot)
|
||||||
// Set the bot to defend the location
|
// Set the bot to defend the location
|
||||||
%bot.scoutTargetLocation = %objective.location;
|
%bot.scoutTargetLocation = %objective.location;
|
||||||
%bot.scoutDistance = %objective.distance;
|
%bot.scoutDistance = %objective.distance;
|
||||||
%bot.addTask("AIEnhancedScoutLocation");
|
|
||||||
|
%bot.primaryTask = "AIEnhancedScoutLocation";
|
||||||
|
%bot.addTask(%bot.primaryTask);
|
||||||
|
|
||||||
|
case $DXAI::Priorities::CaptureFlag:
|
||||||
|
%bot.shouldRunFlag = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
%this.botAssignments[%taskID]++;
|
%this.botAssignments[%taskID]++;
|
||||||
|
|
|
||||||
|
|
@ -62,6 +62,17 @@ function AIConnection::isIdle(%this)
|
||||||
return %this.commander.idleBotList.isMember(%this);
|
return %this.commander.idleBotList.isMember(%this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function AIConnection::runJets(%this, %timeMS)
|
||||||
|
{
|
||||||
|
%this.shouldJet = true;
|
||||||
|
%this.schedule(%timeMS, "_cancelJets");
|
||||||
|
}
|
||||||
|
|
||||||
|
function AIConnection::_cancelJets(%this)
|
||||||
|
{
|
||||||
|
%this.shouldJet = false;
|
||||||
|
}
|
||||||
|
|
||||||
//------------------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------------------
|
||||||
// Description: Basically resets the entire state of the given AIConnection. It does not
|
// 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.
|
// unassign tasks, but it does reset the bot's current movement state.
|
||||||
|
|
@ -108,6 +119,7 @@ function AIConnection::setMoveTarget(%this, %position)
|
||||||
%this.moveTarget = %position;
|
%this.moveTarget = %position;
|
||||||
%this.isMovingToTarget = true;
|
%this.isMovingToTarget = true;
|
||||||
%this.isFollowingTarget = false;
|
%this.isFollowingTarget = false;
|
||||||
|
|
||||||
%this.setPath(%position);
|
%this.setPath(%position);
|
||||||
%this.stepMove(%position);
|
%this.stepMove(%position);
|
||||||
|
|
||||||
|
|
@ -198,10 +210,29 @@ function AIConnection::updateLegs(%this)
|
||||||
%delta = %now - %this.lastUpdateLegs;
|
%delta = %now - %this.lastUpdateLegs;
|
||||||
%this.lastUpdateLegs = %now;
|
%this.lastUpdateLegs = %now;
|
||||||
|
|
||||||
|
// Check the grenade set for anything we'll want to avoid (and can see)
|
||||||
|
for (%iteration = 0; %iteration < AIGrenadeSet.getCount(); %iteration++)
|
||||||
|
{
|
||||||
|
%grenade = AIGrenadeSet.getObject(%iteration);
|
||||||
|
|
||||||
|
if (%this.player.canSeeObject(%grenade, 10, %this.fieldOfView))
|
||||||
|
%this.dangerObjects.add(%grenade);
|
||||||
|
}
|
||||||
|
|
||||||
// Set any danger we may need.
|
// Set any danger we may need.
|
||||||
for (%iteration = 0; %iteration < %this.dangerObjects.getCount(); %iteration++)
|
for (%iteration = 0; %iteration < %this.dangerObjects.getCount(); %iteration++)
|
||||||
%this.setDangerLocation(%this.dangerObjects.getObject(%iteration).getPosition(), 3);
|
%this.setDangerLocation(%this.dangerObjects.getObject(%iteration).getPosition(), 3);
|
||||||
|
|
||||||
|
if (%this.shouldJet && !%this.player.isJetting())
|
||||||
|
{
|
||||||
|
%this.pressJump();
|
||||||
|
|
||||||
|
if (!isEventPending(%this.jetSchedule))
|
||||||
|
%this.jetSchedule = %this.schedule(128, "pressJet");
|
||||||
|
}
|
||||||
|
else if (%this.shouldJet)
|
||||||
|
%this.pressJet();
|
||||||
|
|
||||||
if (%this.isMovingToTarget)
|
if (%this.isMovingToTarget)
|
||||||
{
|
{
|
||||||
if (%this.aimAtLocation)
|
if (%this.aimAtLocation)
|
||||||
|
|
@ -220,6 +251,14 @@ function AIConnection::updateLegs(%this)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function AITask::getWeightFreq(%this) { return %this.weightFreq; }
|
||||||
|
|
||||||
|
function AITask::getMonitorFreq(%this) { return %this.monitorFreq; }
|
||||||
|
|
||||||
|
function AITask::getWeightTimeMS(%this) { return %this.getWeightFreq() * 32; }
|
||||||
|
|
||||||
|
function AITask::getMonitorTimeMS(%this) { return %this.getMonitorFreq() * 32; }
|
||||||
|
|
||||||
//------------------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------------------
|
||||||
// Description: A function called by the ::update function of the AIConnection that is
|
// 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 &
|
// called once every 32ms by the commander AI logic to update the bot's current aiming &
|
||||||
|
|
@ -304,42 +343,6 @@ function AIConnection::updateVisualAcuity(%this)
|
||||||
return;
|
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))
|
|
||||||
{
|
|
||||||
%this.originMarker = new Waypoint(){ datablock = "WaypointMarker"; team = %this.team; name = %this.namebase SPC " Origin"; };
|
|
||||||
%this.clockwiseMarker = new Waypoint(){ datablock = "WaypointMarker"; team = %this.team; name = %this.namebase SPC " Clockwise"; };
|
|
||||||
%this.counterClockwiseMarker = new Waypoint(){ datablock = "WaypointMarker"; team = %this.team; name = %this.namebase SPC " Counter Clockwise"; };
|
|
||||||
%this.upperMarker = new Waypoint(){ datablock = "WaypointMarker"; team = %this.team; name = %this.namebase SPC " Upper"; };
|
|
||||||
%this.lowerMarker = new Waypoint(){ datablock = "WaypointMarker"; team = %this.team; name = %this.namebase SPC " Lower"; };
|
|
||||||
}
|
|
||||||
|
|
||||||
%viewCone = %this.calculateViewCone();
|
|
||||||
%coneOrigin = getWords(%viewCone, 0, 2);
|
|
||||||
%viewConeClockwiseVector = getWords(%viewCone, 3, 5);
|
|
||||||
%viewConeCounterClockwiseVector = getWords(%viewCone, 6, 8);
|
|
||||||
|
|
||||||
%viewConeUpperVector = getWords(%viewCone, 9, 11);
|
|
||||||
%viewConeLowerVector = getWords(%viewCone, 12, 14);
|
|
||||||
|
|
||||||
// Update all the markers
|
|
||||||
%this.clockwiseMarker.setPosition(%viewConeClockwiseVector);
|
|
||||||
%this.counterClockwiseMarker.setPosition(%viewConeCounterClockwiseVector);
|
|
||||||
%this.upperMarker.setPosition(%viewConeUpperVector);
|
|
||||||
%this.lowerMarker.setPosition(%viewConeLowerVector);
|
|
||||||
%this.originMarker.setPosition(%coneOrigin);
|
|
||||||
}
|
|
||||||
else if (isObject(%this.originMarker))
|
|
||||||
{
|
|
||||||
%this.originMarker.delete();
|
|
||||||
%this.clockwiseMarker.delete();
|
|
||||||
%this.counterClockwiseMarker.delete();
|
|
||||||
%this.upperMarker.delete();
|
|
||||||
%this.lowerMarker.delete();
|
|
||||||
}
|
|
||||||
|
|
||||||
%now = getSimTime();
|
%now = getSimTime();
|
||||||
%deltaTime = %now - %this.lastVisualAcuityUpdate;
|
%deltaTime = %now - %this.lastVisualAcuityUpdate;
|
||||||
%this.lastVisualAcuityUpdate = %now;
|
%this.lastVisualAcuityUpdate = %now;
|
||||||
|
|
@ -361,7 +364,7 @@ function AIConnection::updateVisualAcuity(%this)
|
||||||
if (AIGrenadeSet.isMember(%current))
|
if (AIGrenadeSet.isMember(%current))
|
||||||
%this.dangerObjects.add(%current);
|
%this.dangerObjects.add(%current);
|
||||||
|
|
||||||
if (%current.getType() & $TypeMasks::ProjectileObjectType)
|
if (%current.getType() & $TypeMasks::ProjectileObjectType && %current.sourceObject != %this.player)
|
||||||
{
|
{
|
||||||
%className = %current.getClassName();
|
%className = %current.getClassName();
|
||||||
|
|
||||||
|
|
@ -404,6 +407,7 @@ function AIConnection::updateVisualAcuity(%this)
|
||||||
else if (%current.getType() & $TypeMasks::PlayerObjectType && %current.client.team != %this.team)
|
else if (%current.getType() & $TypeMasks::PlayerObjectType && %current.client.team != %this.team)
|
||||||
{
|
{
|
||||||
%this.visibleHostiles.add(%current);
|
%this.visibleHostiles.add(%current);
|
||||||
|
|
||||||
//%this.clientDetected(%current);
|
//%this.clientDetected(%current);
|
||||||
// %this.clientDetected(%current.client);
|
// %this.clientDetected(%current.client);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -148,6 +148,77 @@ function GameConnection::getClosestInventory(%this)
|
||||||
return %closestInventory;
|
return %closestInventory;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function Player::getFacingAngle(%this)
|
||||||
|
{
|
||||||
|
%vector = vectorNormalize(%this.getMuzzleVector($WeaponSlot));
|
||||||
|
return mAtan(getWord(%vector, 1), getWord(%vector, 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
function Player::getViewConeIntersection(%this, %target, %maxDistance, %viewAngle)
|
||||||
|
{
|
||||||
|
%myPosition = %this.getPosition();
|
||||||
|
%targetPosition = %target.getPosition();
|
||||||
|
|
||||||
|
if (vectorDist(%myPosition, %targetPosition) > %maxDistance)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
%offset = vectorNormalize(vectorSub(%targetPosition, %myPosition));
|
||||||
|
%enemyAngle = mAtan(getWord(%offset, 1), getWord(%offset, 0));
|
||||||
|
%myAngle = %this.getFacingAngle();
|
||||||
|
|
||||||
|
%angle = %enemyAngle - %myAngle;
|
||||||
|
|
||||||
|
%viewMax = %viewAngle / 2;
|
||||||
|
%viewMin = -(%viewAngle / 2);
|
||||||
|
|
||||||
|
if (%angle >= %viewMin && %angle <= %viewMax)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function Player::getPackName(%this)
|
||||||
|
{
|
||||||
|
%item = %this.getMountedImage(2).item;
|
||||||
|
|
||||||
|
if (!isObject(%item))
|
||||||
|
return "";
|
||||||
|
|
||||||
|
return %item.getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
function Player::canSeeObject(%this, %target, %maxDistance, %viewAngle)
|
||||||
|
{
|
||||||
|
if (%target.isCloaked() || !%this.getViewConeIntersection(%target, %maxDistance, %viewAngle))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
%coneOrigin = %this.getPosition();
|
||||||
|
|
||||||
|
// Try to raycast the object
|
||||||
|
%rayCast = containerRayCast(%coneOrigin, %target.getWorldBoxCenter(), $TypeMasks::AllObjectType, %this);
|
||||||
|
%hitObject = getWord(%raycast, 0);
|
||||||
|
|
||||||
|
// Since the engine doesn't do raycasts against projectiles & items correctly, we just check if the bot
|
||||||
|
// hit -nothing- when doing the raycast rather than checking for a hit against the object
|
||||||
|
%workaroundTypes = $TypeMasks::ProjectileObjectType | $TypeMasks::ItemObjectType;
|
||||||
|
|
||||||
|
if (%hitObject == %target || (%target.getType() & %workaroundTypes && !isObject(%hitObject)))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function Precipitation::isCloaked(%this) { return false; }
|
||||||
|
function LinearProjectile::isCloaked(%this) { return false; }
|
||||||
|
function LinearFlareProjectile::isCloaked(%this) { return false; }
|
||||||
|
function GrenadeProjectile::isCloaked(%this) { return false; }
|
||||||
|
function SniperProjectile::isCloaked(%this) { return false; }
|
||||||
|
|
||||||
|
function Player::getBackwardsVector(%this)
|
||||||
|
{
|
||||||
|
return vectorScale(%this.getForwardVector(), -1);
|
||||||
|
}
|
||||||
|
|
||||||
//------------------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------------------
|
||||||
// Description: Calculates a list of objects that can be seen by the given client using
|
// Description: Calculates a list of objects that can be seen by the given client using
|
||||||
// distance & field of view values passed in for evaluation.
|
// distance & field of view values passed in for evaluation.
|
||||||
|
|
@ -176,43 +247,22 @@ function GameConnection::getObjectsInViewcone(%this, %typeMask, %distance, %perf
|
||||||
if (%distance $= "")
|
if (%distance $= "")
|
||||||
%distance = %this.viewDistance;
|
%distance = %this.viewDistance;
|
||||||
|
|
||||||
%viewCone = %this.calculateViewCone(%distance);
|
|
||||||
|
|
||||||
// Extract the results: See TODO above ::calculateViewCone implementation
|
|
||||||
%coneOrigin = getWords(%viewCone, 0, 2);
|
|
||||||
%viewConeClockwiseVector = getWords(%viewCone, 3, 5);
|
|
||||||
%viewConeCounterClockwiseVector = getWords(%viewCone, 6, 8);
|
|
||||||
%viewConeUpperVector = getWords(%viewCone, 9, 11);
|
|
||||||
%viewConeLowerVector = getWords(%viewCone, 12, 14);
|
|
||||||
|
|
||||||
%result = new SimSet();
|
%result = new SimSet();
|
||||||
|
|
||||||
|
%coneOrigin = %this.player.getPosition();
|
||||||
|
|
||||||
// Doing a radius search should hopefully be faster than iterating over all objects in MissionCleanup.
|
// Doing a radius search should hopefully be faster than iterating over all objects in MissionCleanup.
|
||||||
// Even if the game did that internally it's definitely faster than doing it in TS
|
// Even if the game did that internally it's definitely faster than doing it in TS
|
||||||
InitContainerRadiusSearch(%coneOrigin, %distance, %typeMask);
|
InitContainerRadiusSearch(%coneOrigin, %distance, %typeMask);
|
||||||
while((%currentObject = containerSearchNext()) != 0)
|
while((%currentObject = containerSearchNext()) != 0)
|
||||||
{
|
{
|
||||||
if (%currentObject == %this || !isObject(%currentObject) || containerSearchCurrRadDamageDist() > %distance)
|
if (%currentObject == %this || !isObject(%currentObject) || containerSearchCurrRadDamageDist() > %distance || %currentObject.isCloaked())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// Check if the object is within both the horizontal and vertical triangles representing our view cone
|
// Check if the object is within both the horizontal and vertical triangles representing our view cone
|
||||||
if (%currentObject.getType() & %typeMask && pointInTriangle(%currentObject.getPosition(), %viewConeClockwiseVector, %viewConeCounterClockwiseVector, %coneOrigin) && pointInTriangle(%currentObject.getPosition(), %viewConeLowerVector, %viewConeUpperVector, %coneOrigin))
|
if (%currentObject.getType() & %typeMask && %this.player.getViewConeIntersection(%currentObject, %distance, %this.fieldOfView))
|
||||||
{
|
if (!%performLOSTest || %this.player.canSeeObject(%currentObject, %distance, %this.fieldOfView))
|
||||||
if (!%performLOSTest)
|
|
||||||
%result.add(%currentObject);
|
%result.add(%currentObject);
|
||||||
else
|
|
||||||
{
|
|
||||||
%rayCast = containerRayCast(%coneOrigin, %currentObject.getWorldBoxCenter(), $TypeMasks::AllObjectType, %this.player);
|
|
||||||
|
|
||||||
%hitObject = getWord(%raycast, 0);
|
|
||||||
|
|
||||||
// Since the engine doesn't do raycasts against projectiles & items correctly, we just check if the bot
|
|
||||||
// hit -nothing- when doing the raycast rather than checking for a hit against the object
|
|
||||||
%workaroundTypes = $TypeMasks::ProjectileObjectType | $TypeMasks::ItemObjectType;
|
|
||||||
if (%hitObject == %currentObject || (%currentObject.getType() & %workaroundTypes && !isObject(%hitObject)))
|
|
||||||
%result.add(%currentObject);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return %result;
|
return %result;
|
||||||
|
|
@ -237,7 +287,7 @@ function getRandomPosition(%position, %distance, %raycast)
|
||||||
if (!%raycast)
|
if (!%raycast)
|
||||||
return %result;
|
return %result;
|
||||||
|
|
||||||
%rayCast = containerRayCast(%position, %result, $TypeMasks::AllObjectType, 0);
|
%rayCast = containerRayCast(%position, %result, $TypeMasks::InteriorObjectType | $TypeMasks::StaticShapeObjectType, 0);
|
||||||
%result = getWords(%raycast, 1, 3);
|
%result = getWords(%raycast, 1, 3);
|
||||||
|
|
||||||
return %result;
|
return %result;
|
||||||
|
|
@ -257,6 +307,23 @@ function getRandomPositionOnTerrain(%position, %distance)
|
||||||
return setWord(%result, 2, getTerrainHeight(%result));
|
return setWord(%result, 2, getTerrainHeight(%result));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getRandomPositionInInterior(%position, %distance)
|
||||||
|
{
|
||||||
|
%firstPass = getRandomPosition(%position, %distance, true);
|
||||||
|
|
||||||
|
%rayCast = containerRayCast(%position, vectorAdd(%position, "0 0 -9000"), $TypeMasks::InteriorObjectType | $TypeMasks::StaticShapeObjectType, 0);
|
||||||
|
|
||||||
|
if (%rayCast == -1)
|
||||||
|
return %firstPass;
|
||||||
|
|
||||||
|
return getWords(%raycast, 1, 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getRandomFloat(%min, %max)
|
||||||
|
{
|
||||||
|
return %min + (getRandom() * (%max - %min));
|
||||||
|
}
|
||||||
|
|
||||||
//------------------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------------------
|
||||||
// Description: Multiplies two vectors together and returns the result.
|
// Description: Multiplies two vectors together and returns the result.
|
||||||
// Param %vec1: The first vector to multiply.
|
// Param %vec1: The first vector to multiply.
|
||||||
|
|
|
||||||
|
|
@ -9,23 +9,64 @@
|
||||||
//------------------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
$DXAI::Loadouts[0, "Name"] = "Light Scout";
|
$DXAI::Loadouts[0, "Name"] = "Light Scout";
|
||||||
$DXAI::Loadouts[0, "Weapon", 0] = ChainGun;
|
$DXAI::Loadouts[0, "Weapon", 0] = Chaingun;
|
||||||
$DXAI::Loadouts[0, "Weapon", 1] = Disc;
|
$DXAI::Loadouts[0, "Weapon", 1] = Disc;
|
||||||
$DXAI::Loadouts[0, "Weapon", 2] = GrenadeLauncher;
|
$DXAI::Loadouts[0, "Weapon", 2] = GrenadeLauncher;
|
||||||
$DXAI::Loadouts[0, "Pack"] = EnergyPack;
|
$DXAI::Loadouts[0, "Pack"] = EnergyPack;
|
||||||
$DXAI::Loadouts[0, "WeaponCount"] = 3;
|
$DXAI::Loadouts[0, "WeaponCount"] = 3;
|
||||||
$DXAI::Loadouts[0, "Armor"] = "Light";
|
$DXAI::Loadouts[0, "Armor"] = "Light";
|
||||||
|
|
||||||
$DXAI::Loadouts[1, "Name"] = "Defender";
|
$DXAI::Loadouts[1, "Name"] = "Light Defender";
|
||||||
$DXAI::Loadouts[1, "Weapon", 0] = ChainGun;
|
$DXAI::Loadouts[1, "Weapon", 0] = Blaster;
|
||||||
$DXAI::Loadouts[1, "Weapon", 1] = Disc;
|
$DXAI::Loadouts[1, "Weapon", 1] = Disc;
|
||||||
$DXAI::Loadouts[1, "Weapon", 2] = GrenadeLauncher;
|
$DXAI::Loadouts[1, "Weapon", 2] = GrenadeLauncher;
|
||||||
$DXAI::Loadouts[1, "Weapon", 3] = GrenadeLauncher;
|
$DXAI::Loadouts[1, "Pack"] = EnergyPack;
|
||||||
$DXAI::Loadouts[1, "Pack"] = AmmoPack;
|
$DXAI::Loadouts[1, "WeaponCount"] = 3;
|
||||||
$DXAI::Loadouts[1, "WeaponCount"] = 4;
|
$DXAI::Loadouts[1, "Armor"] = "Light";
|
||||||
$DXAI::Loadouts[1, "Armor"] = "Medium";
|
|
||||||
|
|
||||||
$DXAI::OptimalLoadouts["AIEnhancedDefendLocation"] = "1";
|
$DXAI::Loadouts[2, "Name"] = "Medium Defender";
|
||||||
|
$DXAI::Loadouts[2, "Weapon", 0] = ChainGun;
|
||||||
|
$DXAI::Loadouts[2, "Weapon", 1] = Disc;
|
||||||
|
$DXAI::Loadouts[2, "Weapon", 2] = GrenadeLauncher;
|
||||||
|
$DXAI::Loadouts[2, "Weapon", 3] = Plasma;
|
||||||
|
$DXAI::Loadouts[2, "Pack"] = AmmoPack;
|
||||||
|
$DXAI::Loadouts[2, "WeaponCount"] = 4;
|
||||||
|
$DXAI::Loadouts[2, "Armor"] = "Medium";
|
||||||
|
|
||||||
$DXAI::Loadouts::Count = 2;
|
$DXAI::Loadouts[3, "Name"] = "Heavy Defender";
|
||||||
|
$DXAI::Loadouts[3, "Weapon", 0] = ChainGun;
|
||||||
|
$DXAI::Loadouts[3, "Weapon", 1] = Disc;
|
||||||
|
$DXAI::Loadouts[3, "Weapon", 2] = GrenadeLauncher;
|
||||||
|
$DXAI::Loadouts[3, "Weapon", 3] = Mortar;
|
||||||
|
$DXAI::Loadouts[3, "Weapon", 4] = Plasma;
|
||||||
|
$DXAI::Loadouts[3, "Pack"] = AmmoPack;
|
||||||
|
$DXAI::Loadouts[3, "WeaponCount"] = 5;
|
||||||
|
$DXAI::Loadouts[3, "Armor"] = "Heavy";
|
||||||
|
|
||||||
|
$DXAI::Loadouts[4, "Name"] = "Hardened Defender";
|
||||||
|
$DXAI::Loadouts[4, "Weapon", 0] = ChainGun;
|
||||||
|
$DXAI::Loadouts[4, "Weapon", 1] = Disc;
|
||||||
|
$DXAI::Loadouts[4, "Weapon", 2] = GrenadeLauncher;
|
||||||
|
$DXAI::Loadouts[4, "Weapon", 3] = Mortar;
|
||||||
|
$DXAI::Loadouts[4, "Weapon", 4] = Plasma;
|
||||||
|
$DXAI::Loadouts[4, "Pack"] = ShieldPack;
|
||||||
|
$DXAI::Loadouts[4, "WeaponCount"] = 5;
|
||||||
|
$DXAI::Loadouts[4, "Armor"] = "Heavy";
|
||||||
|
|
||||||
|
$DXAI::Loadouts[5, "Name"] = "Cloaked Scout";
|
||||||
|
$DXAI::Loadouts[5, "Weapon", 0] = Chaingun;
|
||||||
|
$DXAI::Loadouts[5, "Weapon", 1] = Disc;
|
||||||
|
$DXAI::Loadouts[5, "Weapon", 2] = GrenadeLauncher;
|
||||||
|
$DXAI::Loadouts[5, "Pack"] = CloakingPack;
|
||||||
|
$DXAI::Loadouts[5, "WeaponCount"] = 3;
|
||||||
|
$DXAI::Loadouts[5, "Armor"] = "Light";
|
||||||
|
|
||||||
|
$DXAI::OptimalLoadouts["AIEnhancedFlagCaptureTask"] = "0";
|
||||||
|
$DXAI::OptimalLoadouts["AIEnhancedScoutLocation"] = "0 5";
|
||||||
|
$DXAI::OptimalLoadouts["AIEnhancedDefendLocation"] = "2 3 4";
|
||||||
|
|
||||||
|
// A default loadout to use when the bot has no objective.
|
||||||
|
$DXAI::DefaultLoadout = 0;
|
||||||
|
|
||||||
|
$DXAI::Loadouts::Count = 6;
|
||||||
$DXAI::Loadouts::Default = 0;
|
$DXAI::Loadouts::Default = 0;
|
||||||
|
|
@ -14,8 +14,8 @@ exec("scripts/DXAI/aicommander.cs");
|
||||||
exec("scripts/DXAI/aiconnection.cs");
|
exec("scripts/DXAI/aiconnection.cs");
|
||||||
exec("scripts/DXAI/priorityqueue.cs");
|
exec("scripts/DXAI/priorityqueue.cs");
|
||||||
exec("scripts/DXAI/cyclicset.cs");
|
exec("scripts/DXAI/cyclicset.cs");
|
||||||
exec("scripts/DXAI/loadouts.cs");
|
|
||||||
exec("scripts/DXAI/weaponProfiler.cs");
|
exec("scripts/DXAI/weaponProfiler.cs");
|
||||||
|
exec("scripts/DXAI/loadouts.cs");
|
||||||
|
|
||||||
//------------------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------------------
|
||||||
// Description: This cleanup function is called when the mission ends to clean up all
|
// Description: This cleanup function is called when the mission ends to clean up all
|
||||||
|
|
@ -92,26 +92,26 @@ function DXAI::validateEnvironment()
|
||||||
{
|
{
|
||||||
%gameModeName = $CurrentMissionType @ "Game";
|
%gameModeName = $CurrentMissionType @ "Game";
|
||||||
|
|
||||||
%payloadTemplate = %payload = "function " @ %gameModeName @ "::<METHODNAME>() { return DefaultGame::<METHODNAME>($DXAI::System::RuntimeDummy); } ";
|
%boundPayloadTemplate = "function " @ %gameModeName @ "::<METHODNAME>() { return DefaultGame::<METHODNAME>($DXAI::System::RuntimeDummy); } ";
|
||||||
|
%payloadTemplate = "function <METHODNAME>() { return <METHODNAME>($DXAI::System::RuntimeDummy); } ";
|
||||||
if (game.AIChooseGameObjective($DXAI::System::RuntimeDummy) != 11595)
|
if (game.AIChooseGameObjective($DXAI::System::RuntimeDummy) != 11595)
|
||||||
{
|
{
|
||||||
error("DXAI: Function 'DefaultGame::AIChooseGameObjective' detected to be overwritten by the current gamemode. Correcting ...");
|
error("DXAI: Function 'DefaultGame::AIChooseGameObjective' detected to be overwritten by the current gamemode. Correcting ...");
|
||||||
|
|
||||||
eval(strReplace(%payloadTemplate, "<METHODNAME>", "AIChooseGameObjective"));
|
eval(strReplace(%boundPayloadTemplate, "<METHODNAME>", "AIChooseGameObjective"));
|
||||||
|
|
||||||
// Make sure the patch took
|
// Make sure the patch took
|
||||||
if (game.AIChooseGameObjective($DXAI::System::RuntimeDummy) != 11595)
|
if (game.AIChooseGameObjective($DXAI::System::RuntimeDummy) != 11595)
|
||||||
error("DXAI: Failed to patch 'DefaultGame::AIChooseGameObjective'! DXAI may not function correctly.");
|
error("DXAI: Failed to patch 'DefaultGame::AIChooseGameObjective'! DXAI may not function correctly.");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (game.onAIRespawn($DXAI::System::RuntimeDummy) != 11595)
|
if (onAIRespawn($DXAI::System::RuntimeDummy) != 11595)
|
||||||
{
|
{
|
||||||
error("DXAI: Function 'DefaultGame::onAIRespawn' detected to be overwritten by the current gamemode. Correcting ... ");
|
error("DXAI: Function 'onAIRespawn' detected to be overwritten by the current gamemode. Correcting ... ");
|
||||||
|
|
||||||
eval(strReplace(%payloadTemplate, "<METHODNAME>", "onAIRespawn"));
|
eval(strReplace(%payloadTemplate, "<METHODNAME>", "onAIRespawn"));
|
||||||
|
|
||||||
if (game.onAIRespawn($DXAI::System::RuntimeDummy) != 11595)
|
if (game.onAIRespawn($DXAI::System::RuntimeDummy) != 11595)
|
||||||
error("DXAI: Failed to patch 'DefaultGame::onAIRespawn'! DXAI may not function correctly.");
|
error("DXAI: Failed to patch 'onAIRespawn'! DXAI may not function correctly.");
|
||||||
}
|
}
|
||||||
|
|
||||||
$DXAI::System::InvalidatedEnvironment = false;
|
$DXAI::System::InvalidatedEnvironment = false;
|
||||||
|
|
@ -151,6 +151,11 @@ function DXAI::notifyPlayerDeath(%killed, %killedBy)
|
||||||
$DXAI::ActiveCommander[%iteration].notifyPlayerDeath(%killed, %killedBy);
|
$DXAI::ActiveCommander[%iteration].notifyPlayerDeath(%killed, %killedBy);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function DXAI::notifyFlagGrab(%grabbedBy, %flagTeam)
|
||||||
|
{
|
||||||
|
$DXAI::ActiveCommander[%flagTeam].notifyFlagGrab(%grabbedBy);
|
||||||
|
}
|
||||||
|
|
||||||
//------------------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------------------
|
||||||
// Description: There is a series of functions that the AI code can safely hook without
|
// 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
|
// worry of being overwritten implicitly such as the disconnect or exec functions. For
|
||||||
|
|
@ -320,14 +325,24 @@ package DXAI_Hooks
|
||||||
// Make this do nothing so the bots don't ever get any objectives by default
|
// Make this do nothing so the bots don't ever get any objectives by default
|
||||||
function DefaultGame::AIChooseGameObjective(%game, %client) { return 11595; }
|
function DefaultGame::AIChooseGameObjective(%game, %client) { return 11595; }
|
||||||
|
|
||||||
function DefaultGame::onAIRespawn(%game, %client)
|
function onAIRespawn(%client)
|
||||||
{
|
{
|
||||||
// Make sure the bot has no objectives
|
if (%client != $DXAI::System::RuntimeDummy)
|
||||||
// %client.reset();
|
parent::onAIRespawn(%client);
|
||||||
// %client.defaultTasksAdded = true;
|
|
||||||
%client.shouldRearm = true;
|
|
||||||
%client.targetLoadout = 1;
|
|
||||||
|
|
||||||
|
// Clear the tasks and assign the default tasks
|
||||||
|
// FIXME: Assign tasks on a per-gamemode basis correctly
|
||||||
|
%client.clearTasks();
|
||||||
|
%client.addTask(AIEnhancedEngageTarget);
|
||||||
|
%client.addTask(AIEnhancedRearmTask);
|
||||||
|
%client.addTask(AIEnhancedPathCorrectionTask);
|
||||||
|
%client.addTask(AIEnhancedReturnFlagTask);
|
||||||
|
%client.addTask(AIEnhancedFlagCaptureTask);
|
||||||
|
|
||||||
|
%client.addTask(%client.primaryTask);
|
||||||
|
|
||||||
|
%client.hasFlag = false;
|
||||||
|
%client.shouldRearm = true;
|
||||||
%client.engageTargetLastPosition = "";
|
%client.engageTargetLastPosition = "";
|
||||||
%client.engageTarget = -1;
|
%client.engageTarget = -1;
|
||||||
|
|
||||||
|
|
@ -363,8 +378,6 @@ package DXAI_Hooks
|
||||||
function AISystemEnabled(%enabled)
|
function AISystemEnabled(%enabled)
|
||||||
{
|
{
|
||||||
parent::AISystemEnabled(%enabled);
|
parent::AISystemEnabled(%enabled);
|
||||||
|
|
||||||
echo(%enabled);
|
|
||||||
$DXAI::AISystemEnabled = %enabled;
|
$DXAI::AISystemEnabled = %enabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -376,28 +389,107 @@ package DXAI_Hooks
|
||||||
%client.visibleHostiles.delete();
|
%client.visibleHostiles.delete();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function CTFGame::flagCap(%game, %player)
|
||||||
|
{
|
||||||
|
parent::flagCap(%game, %player);
|
||||||
|
|
||||||
|
%player.client.hasFlag = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function CTFGame::playerTouchEnemyFlag(%game, %player, %flag)
|
||||||
|
{
|
||||||
|
parent::playerTouchEnemyFlag(%game, %player, %flag);
|
||||||
|
|
||||||
|
// So you grabbed the flag eh?
|
||||||
|
%client = %player.client;
|
||||||
|
|
||||||
|
if (%client.isAIControlled())
|
||||||
|
{
|
||||||
|
// In case a bot picks up the flag that wasn't trying to run the flag
|
||||||
|
%client.shouldRunFlag = true;
|
||||||
|
|
||||||
|
// Make sure he knows he has the flag so he can run home
|
||||||
|
%client.hasFlag = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Notify the AI Commander
|
||||||
|
DXAI::notifyFlagGrab(%client, %flag.team);
|
||||||
|
|
||||||
|
return 11595;
|
||||||
|
}
|
||||||
|
|
||||||
|
function AITask::setMonitorFreq(%this, %freq)
|
||||||
|
{
|
||||||
|
parent::setMonitorFreq(%this, %freq);
|
||||||
|
%this.monitorFreq = %freq;
|
||||||
|
}
|
||||||
|
|
||||||
|
function AITask::setWeightFreq(%this, %freq)
|
||||||
|
{
|
||||||
|
parent::setWeightFreq(%this, %freq);
|
||||||
|
%this.weightFreq = %freq;
|
||||||
|
}
|
||||||
|
|
||||||
function Station::stationTriggered(%data, %obj, %isTriggered)
|
function Station::stationTriggered(%data, %obj, %isTriggered)
|
||||||
{
|
{
|
||||||
parent::stationTriggered(%data, %obj, %isTriggered);
|
parent::stationTriggered(%data, %obj, %isTriggered);
|
||||||
|
|
||||||
|
%triggeringClient = %obj.triggeredBy.client;
|
||||||
|
|
||||||
|
if (!isObject(%triggeringClient) || !%triggeringClient.isAIControlled())
|
||||||
|
return;
|
||||||
|
|
||||||
// TODO: If the bot isn't supposed to be on the station, at least restock ammunition?
|
// TODO: If the bot isn't supposed to be on the station, at least restock ammunition?
|
||||||
// FIXME: Can bots trigger dead stations?
|
// FIXME: Can bots trigger dead stations?
|
||||||
if (%isTriggered && %obj.triggeredBy.client.isAIControlled() && %obj.triggeredBy.client.shouldRearm)
|
if (%isTriggered && %triggeringClient.shouldRearm)
|
||||||
{
|
{
|
||||||
%bot = %obj.triggeredBy.client;
|
%triggeringClient.shouldRearm = false;
|
||||||
|
%triggeringClient.player.clearInventory();
|
||||||
|
|
||||||
%bot.shouldRearm = false;
|
// Decide what the bot should pick
|
||||||
%bot.player.clearInventory();
|
%targetLoadout = $DXAI::DefaultLoadout;
|
||||||
|
|
||||||
%bot.player.setArmor($DXAI::Loadouts[%bot.targetLoadout, "Armor"]);
|
if ($DXAI::OptimalLoadouts[%triggeringCLient.primaryTask] !$= "")
|
||||||
%bot.player.setInventory($DXAI::Loadouts[%bot.targetLoadout, "Pack"], 1, true);
|
|
||||||
|
|
||||||
for (%iteration = 0; %iteration < $DXAI::Loadouts[%bot.targetLoadout, "WeaponCount"]; %iteration++)
|
|
||||||
{
|
{
|
||||||
%bot.player.setInventory($DXAI::Loadouts[%bot.targetLoadout, "Weapon", %iteration], 1, true);
|
%count = getWordCount($DXAI::OptimalLoadouts[%triggeringCLient.primaryTask]);
|
||||||
%bot.player.setInventory($DXAI::Loadouts[%bot.targetLoadout, "Weapon", %iteration].Image.Ammo, 999, true); // TODO: Make it actually top out correctly!
|
%targetLoadout = getWord($DXAI::OptimalLoadouts[%triggeringCLient.primaryTask], getRandom(0, %count - 1));
|
||||||
}
|
}
|
||||||
|
else if (%triggeringClient.primaryTask !$= "")
|
||||||
|
error("DXAI: Bot " @ %triggeringClient @ " used default loadout because his current task '" @ %triggeringClient.primaryTask @ "' has no recommended loadouts.");
|
||||||
|
else
|
||||||
|
error("DXAI: Bot " @ %triggeringClient @ " used default loadout because he no has task.");
|
||||||
|
|
||||||
|
%triggeringClient.player.setArmor($DXAI::Loadouts[%targetLoadout, "Armor"]);
|
||||||
|
%triggeringClient.player.setInventory($DXAI::Loadouts[%targetLoadout, "Pack"], 1, true);
|
||||||
|
|
||||||
|
for (%iteration = 0; %iteration < $DXAI::Loadouts[%targetLoadout, "WeaponCount"]; %iteration++)
|
||||||
|
{
|
||||||
|
%triggeringClient.player.setInventory($DXAI::Loadouts[%targetLoadout, "Weapon", %iteration], 1, true);
|
||||||
|
%ammoName = $DXAI::Loadouts[%targetLoadout, "Weapon", %iteration].Image.Ammo;
|
||||||
|
|
||||||
|
// Assign the correct amount of ammo
|
||||||
|
// FIXME: Does this work with ammo packs?
|
||||||
|
%armor = %triggeringClient.player.getDatablock();
|
||||||
|
if (%armor.max[%ammoName] $= "")
|
||||||
|
{
|
||||||
|
%maxAmmo = 999;
|
||||||
|
error("DXAI: Bot " @ %triggeringClient @ " given 999 units of '" @ %ammoName @ "' because the current armor '" @ %armor.getName() @ "' has no maximum set.");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
%maxAmmo = %armor.max[%ammoName];
|
||||||
|
|
||||||
|
%triggeringClient.player.setInventory(%ammoName, %maxAmmo, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
%triggeringClient.currentLoadout = %targetLoadout;
|
||||||
|
|
||||||
|
// Always use the first weapon
|
||||||
|
%triggeringClient.player.use($DXAI::Loadouts[%targetLoadout, "Weapon", 0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Regardless, we want the bot to GTFO off the station when they can
|
||||||
|
// FIXME: This should be part of the rearm routine, pick a nearby random node before adjusting objective weight
|
||||||
|
%triggeringClient.schedule(2000, "setDangerLocation", %obj.getPosition(), 20);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -77,14 +77,17 @@ function AIEnhancedDefendLocation::monitor(%task, %client)
|
||||||
if (%client.getPathDistance(%client.moveLocation) <= 10)
|
if (%client.getPathDistance(%client.moveLocation) <= 10)
|
||||||
%client.setMoveTarget(-1);
|
%client.setMoveTarget(-1);
|
||||||
|
|
||||||
%client.defendTime += 1024;
|
%client.defendTime += %task.getMonitorTimeMS();
|
||||||
if (%client.defendTime >= %client.nextDefendRotation)
|
if (%client.defendTime >= %client.nextDefendRotation)
|
||||||
{
|
{
|
||||||
%client.defendTime = 0;
|
%client.defendTime = 0;
|
||||||
%client.nextDefendRotation = getRandom(5000, 10000);
|
%client.nextDefendRotation = getRandom(5000, 10000);
|
||||||
|
|
||||||
// TODO: Replace with something that detects interiors as well
|
%nextPosition = NavGraph.nodeLoc(NavGraph.randNode(%client.player.getPosition(), 40, true, true));
|
||||||
%client.setMoveTarget(getRandomPositionOnTerrain(%client.defendTargetLocation, 40));
|
|
||||||
|
// If it isn't far enough, just pass on moving. This will help prevent bots jumping up in the air randomly.
|
||||||
|
if (vectorDist(%client.player.getPosition(), %nextPosition) > 5)
|
||||||
|
%client.setMoveTarget(%nextPosition);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
@ -137,7 +140,7 @@ function AIEnhancedScoutLocation::monitor(%task, %client)
|
||||||
{
|
{
|
||||||
%client.setMoveTarget(-1);
|
%client.setMoveTarget(-1);
|
||||||
%client.nextScoutRotation = getRandom(5000, 10000);
|
%client.nextScoutRotation = getRandom(5000, 10000);
|
||||||
%client.scoutTime += 1024;
|
%client.scoutTime += %task.getMonitorTimeMS();
|
||||||
}
|
}
|
||||||
else if(%client.getPathDistance(%client.moveTarget) > 40)
|
else if(%client.getPathDistance(%client.moveTarget) > 40)
|
||||||
{
|
{
|
||||||
|
|
@ -145,7 +148,7 @@ function AIEnhancedScoutLocation::monitor(%task, %client)
|
||||||
%client.scoutTime = 0;
|
%client.scoutTime = 0;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
%client.scoutTime += 1024;
|
%client.scoutTime += %task.getMonitorTimeMS();
|
||||||
|
|
||||||
// Wait a little bit at each node
|
// Wait a little bit at each node
|
||||||
if (%client.scoutTime >= %client.nextScoutRotation)
|
if (%client.scoutTime >= %client.nextScoutRotation)
|
||||||
|
|
@ -192,7 +195,9 @@ function AIEnhancedEngageTarget::weight(%task, %client)
|
||||||
%current = %client.visibleHostiles.getObject(%iteration);
|
%current = %client.visibleHostiles.getObject(%iteration);
|
||||||
|
|
||||||
%targetDistance = vectorDist(%current.getPosition(), %botPosition);
|
%targetDistance = vectorDist(%current.getPosition(), %botPosition);
|
||||||
if (%targetDistance < %chosenTargetDistance)
|
|
||||||
|
// FIXME: We immediately forget about the asshole here
|
||||||
|
if (%targetDistance < %chosenTargetDistance && %client.player.canSeeObject(%current, %client.viewDistance, %client.fieldOfView))
|
||||||
{
|
{
|
||||||
%chosenTargetDistance = %targetDistance;
|
%chosenTargetDistance = %targetDistance;
|
||||||
%chosenTarget = %current;
|
%chosenTarget = %current;
|
||||||
|
|
@ -201,9 +206,35 @@ function AIEnhancedEngageTarget::weight(%task, %client)
|
||||||
|
|
||||||
%client.engageTarget = %chosenTarget;
|
%client.engageTarget = %chosenTarget;
|
||||||
if (!isObject(%client.engageTarget) && %client.engageTargetLastPosition $= "")
|
if (!isObject(%client.engageTarget) && %client.engageTargetLastPosition $= "")
|
||||||
|
{
|
||||||
%task.setWeight($DXAI::Task::NoPriority);
|
%task.setWeight($DXAI::Task::NoPriority);
|
||||||
|
|
||||||
|
// Make sure we disable the pack
|
||||||
|
if (%client.player.getPackName() $= "ShieldPack")
|
||||||
|
{
|
||||||
|
%client.player.setImageTrigger(2, false);
|
||||||
|
%client.rechargingEnergy = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
%task.setWeight($DXAI::Task::VeryHighPriority);
|
%task.setWeight($DXAI::Task::VeryHighPriority);
|
||||||
|
|
||||||
|
// If we have a shield pack on, use it
|
||||||
|
if (%client.player.getPackName() $= "ShieldPack" && %client.player.getEnergyLevel() >= 30 && !%client.rechargingEnergy)
|
||||||
|
%client.player.setImageTrigger(2, true);
|
||||||
|
else if (%client.player.getPackName() $= "ShieldPack" && %client.player.getEnergyLevel() >= 70)
|
||||||
|
{
|
||||||
|
%client.player.setImageTrigger(2, true);
|
||||||
|
%client.rechargingEnergy = false;
|
||||||
|
}
|
||||||
|
else if (%client.player.getPackName() $= "ShieldPack")
|
||||||
|
{
|
||||||
|
// We ran out of energy, let the pack recharge for a bit
|
||||||
|
%client.player.setImageTrigger(2, false);
|
||||||
|
%client.rechargingEnergy = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function AIEnhancedEngageTarget::monitor(%task, %client)
|
function AIEnhancedEngageTarget::monitor(%task, %client)
|
||||||
|
|
@ -217,9 +248,60 @@ function AIEnhancedEngageTarget::monitor(%task, %client)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// %client.engageTargetLastPosition = %client.engageTarget.getWorldBoxCenter();
|
// Calculate the T between the target and this bot
|
||||||
// %client.setMoveTarget(getRandomPositionOnTerrain(%client.engageTargetLastPosition, 40));
|
%normal = vectorNormalize(vectorSub(%client.engageTarget.getWorldBoxCenter(), %client.player.getWorldBoxCenter()));
|
||||||
//%client.pressFire();
|
|
||||||
|
%forwardAngle = mAtan(getWord(%normal, 1), getWord(%normal, 0));
|
||||||
|
%horizontalMaxAngle = %forwardAngle + mDegToRad(90);
|
||||||
|
%horizontalMinAngle = %forwardAngle - mDegToRad(90);
|
||||||
|
|
||||||
|
%randomAngle = getRandomFloat(%horizontalMaxAngle, %horizontalMinAngle);
|
||||||
|
|
||||||
|
// FIXME: Maintain weapon distance
|
||||||
|
%minDist = 20;
|
||||||
|
%maxDist = 30;
|
||||||
|
|
||||||
|
%distance = getRandom(%minDist, %maxDist);
|
||||||
|
|
||||||
|
// Calculate a final point
|
||||||
|
%normal = mSin(%randomAngle) SPC mCos(%randomAngle);
|
||||||
|
|
||||||
|
%targetPoint = vectorAdd(%client.engageTarget.getWorldBoxCenter(), vectorScale(%normal, %distance));
|
||||||
|
%targetPoint = setWord(%targetPoint, 2, getTerrainHeight(%targetPoint));
|
||||||
|
|
||||||
|
%targetPoint = vectorAdd(%targetPoint, "0 0 2");
|
||||||
|
%client.engageTargetLastPosition = %client.engageTarget.getWorldBoxCenter();
|
||||||
|
|
||||||
|
if (%client.engageJetTiming $= "")
|
||||||
|
%client.engageJetTiming = getRandom(500, 10000);
|
||||||
|
else
|
||||||
|
%client.engageJetTime += %task.getMonitorTimeMS();
|
||||||
|
|
||||||
|
if (%client.engageJetTime >= %client.engageJetTiming && %client.player.getEnergyPercent() >= 0.5)
|
||||||
|
{
|
||||||
|
%client.combatJetTiming = getRandom(1000, 1500) + %client.engageJetTiming;
|
||||||
|
%client.runJets(%client.combatJetTiming);
|
||||||
|
|
||||||
|
%client.engageJetTiming = "";
|
||||||
|
%client.engageJetTime = 0;
|
||||||
|
|
||||||
|
%client.isCombatJetting = true;
|
||||||
|
|
||||||
|
%client.setMoveTarget(vectorScale(%client.engageTarget.getBackwardsVector(), 20));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (%client.isCombatJetting && %client.combatJetTime < %client.combatJetTiming && %client.player.getEnergyPercent() >= 0.2)
|
||||||
|
{
|
||||||
|
%client.combatJetTime += %task.getMonitorTimeMS();
|
||||||
|
%client.setMoveTarget(vectorScale(%client.engageTarget.getBackwardsVector(), 20));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
%client.setMoveTarget(%targetPoint);
|
||||||
|
|
||||||
|
%client.combatJetTime = 0;
|
||||||
|
%client.isCombatJetting = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (%client.engageTargetLastPosition !$= "")
|
else if (%client.engageTargetLastPosition !$= "")
|
||||||
{
|
{
|
||||||
|
|
@ -367,10 +449,10 @@ function AIEnhancedFlagCaptureTask::weight(%task, %client)
|
||||||
|
|
||||||
function AIEnhancedFlagCaptureTask::monitor(%task, %client)
|
function AIEnhancedFlagCaptureTask::monitor(%task, %client)
|
||||||
{
|
{
|
||||||
if (!isObject(%client.targetCaptureFlag))
|
if (!isObject(%client.targetCaptureFlag) && !%client.hasFlag)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (%client.targetCaptureFlag.getObjectMount() != %client.player)
|
if (!%client.hasFlag)
|
||||||
%client.setMoveTarget(%client.targetCaptureFlag.getPosition());
|
%client.setMoveTarget(%client.targetCaptureFlag.getPosition());
|
||||||
else
|
else
|
||||||
%client.setMoveTarget(nameToID("Team" @ %client.team @ "Flag1").getPosition());
|
%client.setMoveTarget(nameToID("Team" @ %client.team @ "Flag1").getPosition());
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue