mirror of
https://github.com/Ragora/T2-DXAI.git
synced 2026-01-19 18:14:45 +00:00
Added source
This commit is contained in:
parent
023a82b0ca
commit
bb2a77c4f8
9
scripts/DXAI_Config.cs
Normal file
9
scripts/DXAI_Config.cs
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
// DXAI_Config.cs
|
||||
// Configuration for the DXAI System
|
||||
// Copyright (c) 2014 Robert MacGregor
|
||||
|
||||
$DXAI::Commander::minimumFlagDefense = 1;
|
||||
$DXAI::Commander::minimumGeneratorDefense = 1;
|
||||
|
||||
$DXAI::Bot::DefaultFieldOfView = 3.14159 / 2; // 90*
|
||||
$DXAI::Bot::DefaultViewDistance = 300;
|
||||
167
scripts/DXAI_Helpers.cs
Normal file
167
scripts/DXAI_Helpers.cs
Normal file
|
|
@ -0,0 +1,167 @@
|
|||
// DXAI_Helpers.cs
|
||||
// Helper functions for the AI System
|
||||
// Copyright (c) 2014 Robert MacGregor
|
||||
|
||||
function sameSide(%p1, %p2, %a, %b)
|
||||
{
|
||||
%cp1 = vectorCross(vectorSub(%b, %a), vectorSub(%p1, %a));
|
||||
%cp2 = vectorCross(vectorSub(%b, %a), vectorSub(%p2, %a));
|
||||
|
||||
if (vectorDot(%cp1, %cp2) >= 0)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function pointInTriangle(%point, %a, %b, %c)
|
||||
{
|
||||
if (sameSide(%point, %a, %b, %c) && sameSide(%point, %b, %a, %c) && sameSide(%point, %c, %a, %b))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function AIConnection::reset(%this)
|
||||
{
|
||||
AIUnassignClient(%this);
|
||||
|
||||
%this.stop();
|
||||
%this.clearTasks();
|
||||
%this.clearStep();
|
||||
%this.lastDamageClient = -1;
|
||||
%this.lastDamageTurret = -1;
|
||||
%this.shouldEngage = -1;
|
||||
%this.setEngageTarget(-1);
|
||||
%this.setTargetObject(-1);
|
||||
%this.pilotVehicle = false;
|
||||
%this.defaultTasksAdded = false;
|
||||
|
||||
if (isObject(%this.controlByHuman))
|
||||
aiReleaseHumanControl(%this.controlByHuman, %this);
|
||||
}
|
||||
|
||||
// TODO: Return in a faster-to-read format: Could try as static GVar names
|
||||
// as the game's scripting environment for the gameplay is single threaded
|
||||
// and it probably does a hash to store the values.
|
||||
// TODO: Mathematical optimizations, right now it's a hack because of no
|
||||
// reliable way of getting a player's X facing?
|
||||
function GameConnection::calculateViewCone(%this, %distance)
|
||||
{
|
||||
//%xFacing = %this.player.getXFacing();
|
||||
%halfView = %this.fieldOfView / 2;
|
||||
%coneOrigin = %this.player.getMuzzlePoint($WeaponSlot);
|
||||
|
||||
%forwardVector = %this.player.getForwardVector();
|
||||
%sideVector = vectorCross("0 0 1", %forwardVector);
|
||||
|
||||
// Clockwise
|
||||
//%viewConeClockwise = %xFacing - %halfView;
|
||||
|
||||
// %viewConeClockwisePoint = mCos(%viewConeClockwise) SPC mSin(%viewConeClockwise) SPC "0";
|
||||
%viewConeClockwisePoint = mCos(-%halfView) SPC mSin(-%halfView) SPC "0";
|
||||
%viewConeClockwisePoint = vectorScale(%viewConeClockwisePoint, %this.viewDistance);
|
||||
//%viewConeClockwisePoint = vectorAdd(%viewConeClockwisePoint, %coneOrigin);
|
||||
|
||||
// Counter Clockwise
|
||||
//%viewConeCounterClockwise = %xFacing + %halfView;
|
||||
|
||||
//%viewConeCounterClockwisePoint = mCos(%viewConeCounterClockwise) SPC mSin(%viewConeCounterClockwise) SPC "0";
|
||||
%viewConeCounterClockwisePoint = mCos(%halfView) SPC mSin(%halfView) SPC "0";
|
||||
%viewConeCounterClockwisePoint = vectorScale(%viewConeCounterClockwisePoint, %this.viewDistance);
|
||||
//%viewConeCounterClockwisePoint = vectorAdd(%viewConeCounterClockwisePoint, %coneOrigin);
|
||||
|
||||
// Offsets
|
||||
%halfDistance = vectorDist(%viewConeCounterClockwisePoint, %viewConeClockwisePoint) / 2;
|
||||
|
||||
%viewConeCounterClockwisePoint = vectorScale(%sideVector, %halfDistance);
|
||||
%viewConeCounterClockwisePoint = vectorAdd(%coneOrigin, %viewConeCounterClockwisePoint);
|
||||
|
||||
%viewConeClockwisePoint = vectorScale(vectorScale(%sideVector, -1), %halfDistance);
|
||||
%viewConeClockwisePoint = vectorAdd(%coneOrigin, %viewConeClockwisePoint);
|
||||
|
||||
// Translate the upper and lower points
|
||||
%viewForwardPoint = vectorScale(%forwardVector, %this.viewDistance);
|
||||
|
||||
%viewConeUpperPoint = vectorAdd(vectorScale("0 0 1", %halfDistance), %viewForwardPoint);
|
||||
%viewConeUpperPoint = vectorAdd(%coneOrigin, %viewConeUpperPoint);
|
||||
|
||||
%viewConeLowerPoint = vectorAdd(vectorScale("0 0 -1", %halfDistance), %viewForwardPoint);
|
||||
%viewConeLowerPoint = vectorAdd(%coneOrigin, %viewConeLowerPoint);
|
||||
|
||||
// Now cast them forward
|
||||
%viewConeClockwisePoint = vectorAdd(%viewConeClockwisePoint, vectorScale(%this.player.getForwardVector(), %this.viewDistance));
|
||||
%viewConeCounterClockwisePoint = vectorAdd(%viewConeCounterClockwisePoint, vectorScale(%this.player.getForwardVector(), %this.viewDistance));
|
||||
|
||||
return %coneOrigin SPC %viewConeClockwisePoint SPC %viewConeCounterClockwisePoint SPC %viewConeUpperPoint SPC %viewConeLowerPoint;
|
||||
}
|
||||
|
||||
// View cone simulation function
|
||||
function GameConnection::getObjectsInViewcone(%this, %typeMask, %distance, %performLOSTest)
|
||||
{
|
||||
// FIXME: Radians
|
||||
if (%this.fieldOfView < 0 || %this.fieldOfView > 3.14)
|
||||
{
|
||||
%this.fieldOfView = $DXAPI::Bot::DefaultFieldOfView;
|
||||
error("DXAI: Bad field of view value! (" @ %this @ ".fieldOfView > 3.14 || " @ %this @ ".fieldOfView < 0)");
|
||||
}
|
||||
|
||||
if (%this.viewDistance <= 0)
|
||||
{
|
||||
%this.viewDistance = $DXAPI::Bot::DefaultViewDistance;
|
||||
error("DXAI: Bad view distance value! (" @ %this @ ".viewDistance <= 0)");
|
||||
}
|
||||
|
||||
if (%distance $= "")
|
||||
%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();
|
||||
|
||||
// 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
|
||||
InitContainerRadiusSearch(%coneOrigin, %distance, %typeMask);
|
||||
while((%currentObject = containerSearchNext()) != 0)
|
||||
{
|
||||
if (%currentObject == %this || !isObject(%currentObject) || containerSearchCurrRadDamageDist() > %distance)
|
||||
continue;
|
||||
|
||||
// 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 (!%performLOSTest)
|
||||
%result.add(%currentObject);
|
||||
else
|
||||
{
|
||||
%rayCast = containerRayCast(%coneOrigin, %currentObject.getWorldBoxCenter(), -1, 0);
|
||||
|
||||
%hitObject = getWord(%raycast, 0);
|
||||
|
||||
// Since the engine doesn't do raycasts against projectiles correctly, we just check if the bot
|
||||
// hit -nothing- when doing the raycast rather than checking for a hit against the object
|
||||
if (%hitObject == %currentObject || (%currentObject.getType() & $TypeMasks::ProjectileObjectType && !isObject(%hitObject)))
|
||||
%result.add(%currentObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return %result;
|
||||
}
|
||||
|
||||
// If the map editor was instantiated, this will prevent a little bit
|
||||
// of console warnings
|
||||
function Terraformer::getType(%this) { return 0; }
|
||||
|
||||
// Dummy ScriptObject methods to silence console warnings when testing the runtime
|
||||
// environment; this may not silence for all mods but it should help.
|
||||
$DXAI::System::RuntimeDummy = new ScriptObject(RuntimeDummy) { class = "RuntimeDummy"; };
|
||||
|
||||
function RuntimeDummy::addTask() { }
|
||||
function RuntimeDummy::reset() { }
|
||||
325
scripts/DXAI_Main.cs
Normal file
325
scripts/DXAI_Main.cs
Normal file
|
|
@ -0,0 +1,325 @@
|
|||
// DXAI_Main.cs
|
||||
// Experimental AI System for ProjectR3
|
||||
// Copyright (c) 2014 Robert MacGregor
|
||||
|
||||
exec("scripts/Server/DXAI_Objectives.cs");
|
||||
exec("scripts/Server/DXAI_Helpers.cs");
|
||||
exec("scripts/Server/DXAI_Config.cs");
|
||||
|
||||
$DXAI::ActiveCommanderCount = 2;
|
||||
|
||||
// AICommander
|
||||
// This is a script object that exists for every team in a given
|
||||
// gamemode and performs the coordination of bots in the game.
|
||||
|
||||
function AICommander::notifyPlayerDeath(%this, %killed, %killedBy)
|
||||
{
|
||||
}
|
||||
|
||||
function AICommander::setup(%this)
|
||||
{
|
||||
%this.botList = new Simset();
|
||||
%this.idleBotList = new Simset();
|
||||
|
||||
for (%iteration = 0; %iteration < ClientGroup.getCount(); %iteration++)
|
||||
{
|
||||
%currentClient = ClientGroup.getObject(%iteration);
|
||||
|
||||
if (%currentClient.team == %this.team && %currentClient.isAIControlled())
|
||||
{
|
||||
%this.botList.add(%currentClient);
|
||||
%this.idleBotList.add(%currentClient);
|
||||
|
||||
%currentClient.commander = %this;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function AICommander::removeBot(%this, %bot)
|
||||
{
|
||||
%this.botList.remove(%bot);
|
||||
%this.idleBotList.remove(%bot);
|
||||
|
||||
%bot.commander = -1;
|
||||
}
|
||||
|
||||
function AICommander::addBot(%this, %bot)
|
||||
{
|
||||
if (!%this.botList.isMember(%bot))
|
||||
%this.botList.add(%bot);
|
||||
|
||||
if (!%this.idleBotList.isMember(%bot))
|
||||
%this.idleBotList.add(%bot);
|
||||
|
||||
%bot.commander = %this;
|
||||
}
|
||||
|
||||
function AICommander::cleanup(%this)
|
||||
{
|
||||
%this.botList.delete();
|
||||
%this.idleBotList.delete();
|
||||
}
|
||||
|
||||
function AICommander::update(%this)
|
||||
{
|
||||
for (%iteration = 0; %iteration < %this.botList.getCount(); %iteration++)
|
||||
%this.botList.getObject(%iteration).update();
|
||||
}
|
||||
|
||||
// General DXAI API implementations
|
||||
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;
|
||||
}
|
||||
|
||||
function DXAI::setup(%numTeams)
|
||||
{
|
||||
// Mark the environment as invalidated for each new run so that our hooks
|
||||
// can be verified
|
||||
$DXAI::System::InvalidatedEnvironment = true;
|
||||
|
||||
// Set our setup flag so that the execution hooks can behave correctly
|
||||
$DXAI::System::Setup = true;
|
||||
|
||||
for (%iteration = 1; %iteration < %numTeams + 1; %iteration++)
|
||||
{
|
||||
%commander = new ScriptObject() { class = "AICommander"; team = %iteration; };
|
||||
%commander.setup();
|
||||
|
||||
$DXAI::ActiveCommander[%iteration] = %commander;
|
||||
}
|
||||
|
||||
// And setup the default values
|
||||
for (%iteration = 0; %iteration < ClientGroup.getCount(); %iteration++)
|
||||
{
|
||||
%currentClient = ClientGroup.getObject(%iteration);
|
||||
|
||||
%currentClient.viewDistance = $DXAI::Bot::DefaultViewDistance;
|
||||
%currentClient.fieldOfView = $DXAI::Bot::DefaultFieldOfView;
|
||||
}
|
||||
|
||||
$DXAI::ActiveCommanderCount = %numTeams;
|
||||
}
|
||||
|
||||
function DXAI::validateEnvironment()
|
||||
{
|
||||
%gameModeName = $CurrentMissionType @ "Game";
|
||||
|
||||
%payloadTemplate = %payload = "function " @ %gameModeName @ "::<METHODNAME>() { return DefaultGame::<METHODNAME>($DXAI::System::RuntimeDummy); } ";
|
||||
if (game.AIChooseGameObjective($DXAI::System::RuntimeDummy) != 11595)
|
||||
{
|
||||
error("DXAI: Function 'DefaultGame::AIChooseGameObjective' detected to be overwritten by the current gamemode. Correcting ...");
|
||||
|
||||
eval(%strReplace(%payloadTemplate, "<METHODNAME>", "AIChooseGameObjective"));
|
||||
|
||||
// Make sure the patch took
|
||||
if (game.AIChooseGameObjective($DXAI::System::RuntimeDummy) != 11595)
|
||||
error("DXAI: Failed to patch 'DefaultGame::AIChooseGameObjective'! DXAI may not function correctly.");
|
||||
}
|
||||
|
||||
if (game.onAIRespawn($DXAI::System::RuntimeDummy) != 11595)
|
||||
{
|
||||
error("DXAI: Function 'DefaultGame::onAIRespawn' detected to be overwritten by the current gamemode. Correcting ... ");
|
||||
|
||||
eval(%strReplace(%payloadTemplate, "<METHODNAME>", "onAIRespawn"));
|
||||
|
||||
if (game.onAIRespawn($DXAI::System::RuntimeDummy) != 11595)
|
||||
error("DXAI: Failed to patch 'DefaultGame::onAIRespawn'! DXAI may not function correctly.");
|
||||
}
|
||||
|
||||
$DXAI::System::InvalidatedEnvironment = false;
|
||||
}
|
||||
|
||||
function DXAI::update()
|
||||
{
|
||||
if (isEventPending($DXAI::updateHandle))
|
||||
cancel($DXAI::updateHandle);
|
||||
|
||||
// Check if the bound functions are overwritten by the current gamemode, or if something
|
||||
// may have invalidated our hooks
|
||||
if ($DXAI::System::InvalidatedEnvironment && $DXAI::System::Setup)
|
||||
DXAI::validateEnvironment();
|
||||
|
||||
for (%iteration = 1; %iteration < $DXAI::ActiveCommanderCount + 1; %iteration++)
|
||||
$DXAI::ActiveCommander[%iteration].update();
|
||||
|
||||
// Apparently we can't schedule a bound function otherwise
|
||||
$DXAI::updateHandle = schedule(32,0,"eval", "DXAI::update();");
|
||||
}
|
||||
|
||||
function DXAI::notifyPlayerDeath(%killed, %killedBy)
|
||||
{
|
||||
for (%iteration = 1; %iteration < $DXAI::ActiveCommanderCount + 1; %iteration++)
|
||||
$DXAI::ActiveCommander[%iteration].notifyPlayerDeath(%killed, %killedBy);
|
||||
}
|
||||
|
||||
// AIPlayer
|
||||
// This is a script object that contains DXAI functionality on a per-soldier
|
||||
// basis
|
||||
function AIConnection::initialize(%this, %aiClient)
|
||||
{
|
||||
%this.fieldOfView = 3.14 / 2; // 90* View cone
|
||||
%this.viewDistance = 300;
|
||||
|
||||
if (!isObject(%aiClient))
|
||||
error("AIPlayer: Attempted to initialize with bad AI client connection!");
|
||||
|
||||
%this.client = %aiClient;
|
||||
}
|
||||
|
||||
function AIConnection::update(%this)
|
||||
{
|
||||
}
|
||||
|
||||
function AIConnection::notifyProjectileImpact(%this, %data, %proj, %position)
|
||||
{
|
||||
if (!isObject(%proj.sourceObject) || %proj.sourceObject.client.team == %this.team)
|
||||
return;
|
||||
}
|
||||
|
||||
function AIConnection::isIdle(%this)
|
||||
{
|
||||
if (!isObject(%this.commander))
|
||||
return true;
|
||||
|
||||
return %this.commander.idleBotList.isMember(%this);
|
||||
}
|
||||
|
||||
|
||||
// Hooks for the AI System
|
||||
package DXAI_Hooks
|
||||
{
|
||||
function DefaultGame::gameOver(%game)
|
||||
{
|
||||
parent::gameOver(%game);
|
||||
|
||||
DXAI::cleanup();
|
||||
}
|
||||
|
||||
function DefaultGame::startMatch(%game)
|
||||
{
|
||||
parent::startMatch(%game);
|
||||
|
||||
DXAI::setup(%game.numTeams);
|
||||
DXAI::update();
|
||||
}
|
||||
|
||||
function DefaultGame::AIChangeTeam(%game, %client, %newTeam)
|
||||
{
|
||||
// Remove us from the old commander's control first
|
||||
$DXAI::ActiveCommander[%client.team].removeBot(%client);
|
||||
|
||||
parent::AIChangeTeam(%game, %client, %newTeam);
|
||||
|
||||
$DXAI::ActiveCommander[%newTeam].addBot(%client);
|
||||
}
|
||||
|
||||
function AIConnection::onAIDrop(%client)
|
||||
{
|
||||
if (isObject(%client.commander))
|
||||
%client.commander.removeBot(%client);
|
||||
|
||||
parent::onAIDrop(%client);
|
||||
}
|
||||
|
||||
// Hooks for AI System notification
|
||||
function DefaultGame::onClientKilled(%game, %clVictim, %clKiller, %damageType, %implement, %damageLocation)
|
||||
{
|
||||
parent::onClientKilled(%game, %clVictim, %clKiller, %damageType, %implement, %damageLocation);
|
||||
|
||||
DXAI::notifyPlayerDeath(%clVictim, %clKiller);
|
||||
}
|
||||
|
||||
function DefaultGame::onAIKilled(%game, %clVictim, %clKiller, %damageType, %implement)
|
||||
{
|
||||
parent::onAIKilled(%game, %clVictim, %clKiller, %damageType, %implement);
|
||||
|
||||
DXAI::notifyPlayerDeath(%clVictim, %clKiller);
|
||||
}
|
||||
|
||||
function ProjectileData::onExplode(%data, %proj, %pos, %mod)
|
||||
{
|
||||
parent::onExplode(%data, %proj, %pos, %mod);
|
||||
|
||||
// Look for any bots nearby
|
||||
InitContainerRadiusSearch(%pos, 10, $TypeMasks::PlayerObjectType);
|
||||
|
||||
while ((%targetObject = containerSearchNext()) != 0)
|
||||
{
|
||||
%currentDistance = containerSearchCurrRadDamageDist();
|
||||
|
||||
if (%currentDistance > 10 || !%targetObject.client.isAIControlled())
|
||||
continue;
|
||||
|
||||
// Get the projectile team
|
||||
%projectileTeam = -1;
|
||||
if (isObject(%proj.sourceObject))
|
||||
%projectileTeam = %proj.sourceObject.client.team;
|
||||
|
||||
// Determine if we should run based on team & Team damage
|
||||
%shouldRun = false;
|
||||
if (isObject(%proj.sourceObject) && %projectileTeam == %targetObject.client.team && $TeamDamage)
|
||||
%shouldRun = true;
|
||||
else if (isObject(%proj.sourceObject) && %projectileTeam != %targetObject.client.team)
|
||||
%shouldRun = true;
|
||||
|
||||
// Determine if we 'heard' it. The sound distance seems to be roughly 55m or less and we check the maxDistance
|
||||
// IIRC The 55m distance should scale with the min/max distances and volume but I'm not sure how those interact
|
||||
%heardHit = false;
|
||||
%hitDistance = vectorDist(%targetObject.getWorldBoxCenter(), %pos);
|
||||
|
||||
if (%hitDistance < 55 && %hitDistance <= %data.explosion.soundProfile.description.maxDistance)
|
||||
%heardHit = true;
|
||||
|
||||
// If the thing has any radius damage (and we heard it), run around a little bit if we need to
|
||||
if (%data.indirectDamage != 0 && %shouldRun)
|
||||
%targetObject.client.setDangerLocation(%pos, 20);
|
||||
|
||||
// If we should care and it wasn't a teammate projectile, notify
|
||||
if (%shouldRun && %projectileTeam != %targetObject.client.team)
|
||||
%targetObject.client.notifyProjectileImpact(%data, %proj, %pos);
|
||||
}
|
||||
}
|
||||
|
||||
// Make this do nothing so the bots don't ever get any objectives by default
|
||||
function DefaultGame::AIChooseGameObjective(%game, %client) { return 11595; }
|
||||
|
||||
function DefaultGame::onAIRespawn(%game, %client)
|
||||
{
|
||||
// Make sure the bot has no objectives
|
||||
%client.reset();
|
||||
%client.defaultTasksAdded = true;
|
||||
|
||||
// All bots have this task, see DXAI_Objectives.cs
|
||||
%client.addTask("AIVisualAcuity");
|
||||
|
||||
return 11595;
|
||||
}
|
||||
|
||||
// We package hook the exec() and compile() functions to perform execution environment
|
||||
// checking because these can easily load code that overwrites methods that are otherwise
|
||||
// hooked by DXAI. This can happen with gamemode specific events because DXAI only hooks into
|
||||
// DefaultGame. This is mostly helpful for developers.
|
||||
function exec(%file)
|
||||
{
|
||||
$DXAI::System::InvalidatedEnvironment = true;
|
||||
parent::exec(%file);
|
||||
}
|
||||
|
||||
function compile(%file)
|
||||
{
|
||||
$DXAI::System::InvalidatedEnvironment = true;
|
||||
parent::compile(%file);
|
||||
}
|
||||
};
|
||||
|
||||
if (!isActivePackage(DXAI_Hooks))
|
||||
activatePackage(DXAI_Hooks);
|
||||
75
scripts/DXAI_Objectives.cs
Normal file
75
scripts/DXAI_Objectives.cs
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
// DXAI_Objectives.cs
|
||||
// Objectives for the AI system
|
||||
// Copyright (c) 2014 Robert MacGregor
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// The AIVisualAcuity task is a complementary task for the AI grunt systems
|
||||
// to perform better at recognizing things visually with reasonably
|
||||
// Human perception capabilities.
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
function AIVisualAcuity::initFromObjective(%task, %objective, %client)
|
||||
{
|
||||
// Called to initialize from an objective object
|
||||
}
|
||||
|
||||
function AIVisualAcuity::assume(%task, %client)
|
||||
{
|
||||
// Called when the bot starts the task
|
||||
}
|
||||
|
||||
function AIVisualAcuity::retire(%task, %client)
|
||||
{
|
||||
// Called when the bot stops the task
|
||||
}
|
||||
|
||||
function AIVisualAcuity::weight(%task, %client)
|
||||
{
|
||||
%task.setWeight(999);
|
||||
}
|
||||
|
||||
function AIVisualAcuity::monitor(%task, %client)
|
||||
{
|
||||
// Called when the bot is performing the task
|
||||
|
||||
if (%client.enableVisualDebug)
|
||||
{
|
||||
if (!isObject(%client.originMarker))
|
||||
{
|
||||
%client.originMarker = new Waypoint(){ datablock = "WaypointMarker"; team = %client.team; name = %client.namebase SPC " Origin"; };
|
||||
%client.clockwiseMarker = new Waypoint(){ datablock = "WaypointMarker"; team = %client.team; name = %client.namebase SPC " Clockwise"; };
|
||||
%client.counterClockwiseMarker = new Waypoint(){ datablock = "WaypointMarker"; team = %client.team; name = %client.namebase SPC " Counter Clockwise"; };
|
||||
%client.upperMarker = new Waypoint(){ datablock = "WaypointMarker"; team = %client.team; name = %client.namebase SPC " Upper"; };
|
||||
%client.lowerMarker = new Waypoint(){ datablock = "WaypointMarker"; team = %client.team; name = %client.namebase SPC " Lower"; };
|
||||
}
|
||||
|
||||
%viewCone = %client.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
|
||||
%client.clockwiseMarker.setPosition(%viewConeClockwiseVector);
|
||||
%client.counterClockwiseMarker.setPosition(%viewConeCounterClockwiseVector);
|
||||
%client.upperMarker.setPosition(%viewConeUpperVector);
|
||||
%client.lowerMarker.setPosition(%viewConeLowerVector);
|
||||
%client.originMarker.setPosition(%coneOrigin);
|
||||
}
|
||||
else if (isObject(%client.originMarker))
|
||||
{
|
||||
%client.originMarker.delete();
|
||||
%client.clockwiseMarker.delete();
|
||||
%client.counterClockwiseMarker.delete();
|
||||
%client.upperMarker.delete();
|
||||
%client.lowerMarker.delete();
|
||||
}
|
||||
|
||||
%result = %client.getObjectsInViewcone($TypeMasks::ProjectileObjectType | $TypeMasks::PlayerObjectType, %client.viewDistance, true);
|
||||
|
||||
echo(%result.getCount());
|
||||
|
||||
%result.delete();
|
||||
}
|
||||
Loading…
Reference in a new issue