Bots now all have an engage task; bots now all have a return flag task; AI engage has some basic engage functionality; began work on the new AI-player interaction system; Bots will spot players via their view cone now

This commit is contained in:
Robert MacGregor 2015-10-05 05:03:45 -04:00
parent 57a25dd872
commit 5e53dd91ef
5 changed files with 342 additions and 6 deletions

View file

@ -132,7 +132,22 @@ function AICommander::loadObjectives(%this)
function AICommander::assignTasks(%this)
{
// Calculate how much priority we have total, first
// First, assign objectives that all bots should have
for (%iteration = 0; %iteration < %this.botList.getCount(); %iteration++)
{
%bot = %this.botList.getObject(%iteration);
%bot.addTask(AIEnhancedEngageTarget);
%bot.addTask(AIEnhancedRearmTask);
// We only need this task if we're actually playing CTF.
if ($CurrentMissionType $= "CTF")
%bot.addTask(AIEnhancedReturnFlagTask);
%bot.targetLoadout = 0;
%bot.shouldRearm = true;
}
// Calculate how much priority we have total
%totalPriority = 0.0;
for (%iteration = 0; %iteration < $DXAI::Priorities::Count; %iteration++)
{

View file

@ -103,6 +103,59 @@ function GameConnection::calculateViewCone(%this, %distance)
return %coneOrigin SPC %viewConeClockwisePoint SPC %viewConeCounterClockwisePoint SPC %viewConeUpperPoint SPC %viewConeLowerPoint;
}
function SimSet::recurse(%this, %result)
{
if (!isObject(%result))
%result = new SimSet();
for (%iteration = 0; %iteration < %this.getCount(); %iteration++)
{
%current = %this.getObject(%iteration);
if (%current.getClassName() $= "SimGroup" || %current.getClassName() $= "SimSet")
%current.recurse(%result);
else
%result.add(%current);
}
return %result;
}
// TODO: Use the nav graph to estimate an actual distance?
function GameConnection::getClosestInventory(%this)
{
if (!isObject(%this.player))
return -1;
%group = nameToID("Team" @ %this.team);
if (!isObject(%group))
return -1;
%teamObjects = %group.recurse();
%closestInventory = -1;
%closestInventoryDistance = 9999;
for (%iteration = 0; %iteration < %teamObjects.getCount(); %iteration++)
{
%current = %teamObjects.getObject(%iteration);
if (%current.getClassName() $= "StaticShape" && %current.getDatablock().getName() $= "StationInventory")
{
%inventoryDistance = vectorDist(%current.getPosition(), %this.player.getPosition());
if (%inventoryDistance < %closestInventoryDistance)
{
%closestInventoryDistance = %inventoryDistance;
%closestInventory = %current;
}
}
}
%teamObjects.delete();
return %closestInventory;
}
// View cone simulation function
function GameConnection::getObjectsInViewcone(%this, %typeMask, %distance, %performLOSTest)
{
@ -194,3 +247,13 @@ $DXAI::System::RuntimeDummy = new ScriptObject(RuntimeDummy) { class = "RuntimeD
function RuntimeDummy::addTask() { }
function RuntimeDummy::reset() { }
$TypeMasks::InteractiveObjectType = $TypeMasks::PlayerObjectType | $TypeMasks::VehicleObjectType | $TypeMasks::WaterObjectType | $TypeMasks::ProjectileObjectType | $TypeMasks::ItemObjectType | $TypeMasks::CorpseObjectType;
$TypeMasks::UnInteractiveObjectType = $TypeMasks::StaticObjectType | $TypeMasks::TerrainObjectType | $TypeMasks::InteriorObjectType | $TypeMasks::StaticTSObjectType | $TypeMasks::StaticRenderedObjectType;
$TypeMasks::BaseAssetObjectType = $TypeMasks::ForceFieldObjectType | $TypeMasks::TurretObjectType | $TypeMasks::SensorObjectType | $TypeMasks::StationObjectType | $TypeMasks::GeneratorObjectType;
$TypeMasks::GameSupportObjectType = $TypeMasks::TriggerObjectType | $TypeMasks::MarkerObjectType | $TypeMasks::CameraObjectType | $TypeMasks::VehicleBlockerObjectType | $TypeMasks::PhysicalZoneObjectType;
$TypeMasks::GameContentObjectType = $TypeMasks::ExplosionObjectType | $TypeMasks::CorpseObjectType | $TypeMasks::DebrisObjectType;
$TypeMasks::DefaultLOSObjectType = $TypeMasks::TerrainObjectType | $TypeMasks::InteriorObjectType | $TypeMasks::StaticObjectType;
// We declare the AllObjectType like this instead of -1 because it seems -1 can sometimes not work?
$TypeMasks::AllObjectType = $TypeMasks::InteractiveObjectType | $TypeMasks::DefaultLOSObjectType | $TypeMasks::GameContentObjectType | $TypeMasks::GameSupportObjectType | $TypeMasks::BaseAssetObjectType | $TypeMasks::UnInteractiveObjectType;

31
scripts/DXAI/loadouts.cs Normal file
View file

@ -0,0 +1,31 @@
//------------------------------------------------------------------------------------------
// loadouts.cs
// Source file declaring usable loadouts for the bots and mapping them to their most
// appropriate tasks.
// 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.
//------------------------------------------------------------------------------------------
$DXAI::Loadouts[0, "Name"] = "Light Scout";
$DXAI::Loadouts[0, "Weapon", 0] = ChainGun;
$DXAI::Loadouts[0, "Weapon", 1] = Disc;
$DXAI::Loadouts[0, "Weapon", 2] = GrenadeLauncher;
$DXAI::Loadouts[0, "Pack"] = EnergyPack;
$DXAI::Loadouts[0, "WeaponCount"] = 3;
$DXAI::Loadouts[0, "Armor"] = "Light";
$DXAI::Loadouts[1, "Name"] = "Defender";
$DXAI::Loadouts[1, "Weapon", 0] = ChainGun;
$DXAI::Loadouts[1, "Weapon", 1] = Disc;
$DXAI::Loadouts[1, "Weapon", 2] = GrenadeLauncher;
$DXAI::Loadouts[1, "Weapon", 3] = GrenadeLauncher;
$DXAI::Loadouts[1, "Pack"] = AmmoPack;
$DXAI::Loadouts[1, "WeaponCount"] = 4;
$DXAI::Loadouts[1, "Armor"] = "Medium";
$DXAI::OptimalLoadouts["AIEnhancedDefendLocation"] = "1";
$DXAI::Loadouts::Count = 2;
$DXAI::Loadouts::Default = 0;

View file

@ -14,6 +14,7 @@ exec("scripts/DXAI/aicommander.cs");
exec("scripts/DXAI/aiconnection.cs");
exec("scripts/DXAI/priorityqueue.cs");
exec("scripts/DXAI/cyclicset.cs");
exec("scripts/DXAI/loadouts.cs");
// General DXAI API implementations
function DXAI::cleanup()
@ -208,9 +209,13 @@ package DXAI_Hooks
if (%hitDistance <= 20 && %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 the thing has any radius damage (and we heard it), run around a little bit if we need to, and look at it for a bit
if (%data.indirectDamage != 0 && %heardHit)
{
%targetObject.client.schedule(getRandom(250, 400), "setDangerLocation", %pos, 20);
// TODO: Perhaps attempt to discern the direction of fire?
%targetObject.client.aimAt(%pos);
}
// If we should care and it wasn't a teammate projectile, notify
if (%shouldRun && %projectileTeam != %targetObject.client.team)
@ -237,6 +242,8 @@ package DXAI_Hooks
// Make sure the bot has no objectives
// %client.reset();
// %client.defaultTasksAdded = true;
%client.shouldRearm = true;
%client.targetLoadout = 1;
return 11595;
}
@ -256,6 +263,39 @@ package DXAI_Hooks
$DXAI::System::InvalidatedEnvironment = true;
parent::compile(%file);
}
function AIRespondToEvent(%client, %eventTag, %targetClient)
{
%clientPos = %client.player.getWorldBoxCenter();
//switch$ (%eventTag)
//{
schedule(250, %targetClient, "AIPlayAnimSound", %targetClient, %clientPos, "cmd.decline", $AIAnimSalute, $AIAnimSalute, 0);
schedule(2000, %targetClient, "AIPlayAnimSound", %targetClient, %clientPos, ObjectiveNameToVoice(%targetClient.getTaskName()), $AIAnimSalute, $AIAnimSalute, 0);
schedule(3700, %targetClient, "AIPlayAnimSound", %targetClient, %clientPos, "vqk.sorry", $AIAnimSalute, $AIAnimSalute, 0);
}
function Station::stationTriggered(%data, %obj, %isTriggered)
{
parent::stationTriggered(%data, %obj, %isTriggered);
// TODO: If the bot isn't supposed to be on the station, at least restock ammunition?
if (%isTriggered && %obj.triggeredBy.client.isAIControlled() && %obj.triggeredBy.client.shouldRearm)
{
%bot = %obj.triggeredBy.client;
%bot.shouldRearm = false;
%bot.player.clearInventory();
%bot.player.setArmor($DXAI::Loadouts[%bot.targetLoadout, "Armor"]);
%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);
%bot.player.setInventory($DXAI::Loadouts[%bot.targetLoadout, "Weapon", %iteration].Image.Ammo, 999, true); // TODO: Make it actually top out correctly!
}
}
}
};
// Only activate the package if it isn't already active.

View file

@ -20,7 +20,7 @@
function AIEnhancedEscort::initFromObjective(%task, %objective, %client) { }
function AIEnhancedEscort::assume(%task, %client) { %task.setMonitorFreq(1); }
function AIEnhancedEscort::retire(%task, %client) { %client.isMoving = false; %client.manualAim = false; }
function AIEnhancedEscort::weight(%task, %client) { %task.setWeight(1000); }
function AIEnhancedEscort::weight(%task, %client) { %task.setWeight(500); }
function AIEnhancedEscort::monitor(%task, %client)
{
@ -53,7 +53,7 @@ function AIEnhancedEscort::monitor(%task, %client)
function AIEnhancedDefendLocation::initFromObjective(%task, %objective, %client) { }
function AIEnhancedDefendLocation::assume(%task, %client) { %task.setMonitorFreq(1); }
function AIEnhancedDefendLocation::retire(%task, %client) { %client.isMoving = false; }
function AIEnhancedDefendLocation::weight(%task, %client) { %task.setWeight(1000); }
function AIEnhancedDefendLocation::weight(%task, %client) { %task.setWeight(500); }
function AIEnhancedDefendLocation::monitor(%task, %client)
{
@ -103,7 +103,7 @@ function AIEnhancedDefendLocation::monitor(%task, %client)
function AIEnhancedScoutLocation::initFromObjective(%task, %objective, %client) { }
function AIEnhancedScoutLocation::assume(%task, %client) { %task.setMonitorFreq(1); %client.currentNode = -1; }
function AIEnhancedScoutLocation::retire(%task, %client) { }
function AIEnhancedScoutLocation::weight(%task, %client) { %task.setWeight(1000); }
function AIEnhancedScoutLocation::weight(%task, %client) { %task.setWeight(500); }
function AIEnhancedScoutLocation::monitor(%task, %client)
{
@ -159,4 +159,191 @@ function AIEnhancedScoutLocation::monitor(%task, %client)
}
}
}
//------------------------------------------------------------------------------------------
//------------------------------------------------------------------------------------------
//------------------------------------------------------------------------------------------
// +Param %bot.engangeDistance: The maximum distance at which the bot will go out to
// attack a hostile.
// +Param %bot.engageTarget: A manually assigned engage target to go after.
// +Description: The AIEnhancedEngageTarget is a better implementation of the base
// AI engage logic.
//------------------------------------------------------------------------------------------`
function AIEnhancedEngageTarget::initFromObjective(%task, %objective, %client) { }
function AIEnhancedEngageTarget::assume(%task, %client) { %task.setMonitorFreq(1); }
function AIEnhancedEngageTarget::retire(%task, %client) { }
function AIEnhancedEngageTarget::weight(%task, %client)
{
if (!isObject(%client.engageTarget))
{
%visibleObjects = %client.getObjectsInViewcone($TypeMasks::PlayerObjectType, %client.viewDistance, true);
// Choose the closest target
// TODO: Choose on more advanced metrics like HP
%chosenTarget = -1;
%chosenTargetDistance = 9999;
for (%iteration = 0; %iteration < %visibleObjects.getCount(); %iteration++)
{
%potentialTarget = %visibleObjects.getObject(%iteration);
%potentialTargetDistance = vectorDist(%potentialTarget.getPosition(), %client.player.getPosition());
if (%potentialTarget.client.team != %client.team && %potentialTargetDistance < %chosenTargetDistance)
{
%chosenTargetDistance = %potentialTargetDistance;
%chosenTarget = %potentialTarget;
}
}
%visibleObjects.delete();
%client.engageTarget = %chosenTarget;
}
else
{
// Can we still see them?
%rayCast = containerRayCast(%client.player.getWorldBoxCenter(), %client.engageTarget.getWorldBoxCenter(), -1, %client.player);
%hitObject = getWord(%raycast, 0);
// TODO: Go to the last known position.
if (%hitObject != %client.engageTarget)
%client.engageTarget = -1;
}
if (!isObject(%client.engageTarget) && %client.engageTargetLastPosition $= "")
%task.setWeight(0);
else
%task.setWeight(1000);
}
function AIEnhancedEngageTarget::monitor(%task, %client)
{
if (isObject(%client.engageTarget))
{
%player = %client.player;
%targetDistance = vectorDist(%player.getPosition(), %client.engageTarget.getPosition());
// Firstly, just aim at them for now
%client.aimAt(%client.engageTarget.getWorldBoxCenter());
// What is our current best weapon? Right now we just check target distance and weapon spread.
%bestWeapon = 0;
for (%iteration = 0; %iteration < %player.weaponSlotCount; %iteration++)
{
// Weapons with a decent bit of spread should be used <= 20m
}
%player.selectWeaponSlot(%bestWeapon);
%client.engageTargetLastPosition = %client.engageTarget.getWorldBoxCenter();
%client.isMoving = true;
%client.moveLocation = getRandomPositionOnTerrain(%client.engageTargetLastPosition, 40);
%client.pressFire();
}
else if (%client.engageTargetLastPosition !$= "")
{
%client.isMoving = true;
%client.moveLocation = %client.engageTargetLastPosition;
if (vectorDist(%client.player.getPosition(), %client.engageTargetLastPosition) <= 10)
{
%client.engageTargetLastPosition = "";
%client.isMoving = false;
}
}
}
//------------------------------------------------------------------------------------------
//------------------------------------------------------------------------------------------
// +Param %bot.shouldRearm: A boolean representing whether or not this bot should go
// and rearm.
// +Param %bot.targetInventory: The ID of the inventory station to rearm at.
//------------------------------------------------------------------------------------------`
function AIEnhancedRearmTask::initFromObjective(%task, %objective, %client) { }
function AIEnhancedRearmTask::assume(%task, %client) { %task.setMonitorFreq(1); }
function AIEnhancedRearmTask::retire(%task, %client) { }
function AIEnhancedRearmTask::weight(%task, %client)
{
if (%client.shouldRearm)
%task.setWeight(600);
else
%task.setWeight(0);
}
function AIEnhancedRearmTask::monitor(%task, %client)
{
if (!isObject(%client.targetInventory))
%client.targetInventory = %client.getClosestInventory();
if (isObject(%client.targetInventory))
{
// Politely wait if someone is already on it.
if (vectorDist(%client.targetInventory.getPosition(), %client.player.getPosition()) <= 7 && isObject(%client.targetInventory.triggeredBy))
%client.isMoving = false;
else
{
%client.isMoving = true;
%client.moveLocation = %client.targetInventory.getPosition();
}
}
else
%client.shouldRearm = false; // No inventories?
}
//------------------------------------------------------------------------------------------
//------------------------------------------------------------------------------------------
// Description: A task that actually makes the bots return a flag that's nearby.
//------------------------------------------------------------------------------------------`
function AIEnhancedReturnFlagTask::initFromObjective(%task, %objective, %client) { }
function AIEnhancedReturnFlagTask::assume(%task, %client) { %task.setMonitorFreq(1); }
function AIEnhancedReturnFlagTask::retire(%task, %client) { }
function AIEnhancedReturnFlagTask::weight(%task, %client)
{
%flag = nameToID("Team" @ %client.team @ "Flag1");
if (!isObject(%flag) || %flag.isHome)
{
%task.setWeight(0);
%client.targetFlag = -1;
%client.isMoving = false;
}
else
{
// TODO: For now, all the bots go after it! Make this check if the bot is range.
%task.setWeight(700);
%client.targetFlag = %flag;
}
}
function AIEnhancedReturnFlagTask::monitor(%task, %client)
{
if (!isObject(%client.targetFlag))
return;
// TODO: Make the bot engage the flag runner if its currently held.
%client.isMoving = true;
%client.moveLocation = %client.targetFlag.getPosition();
}
//------------------------------------------------------------------------------------------
function ObjectiveNameToVoice(%objective)
{
%result = "avo.grunt";
switch$(%objective)
{
case "AIEnhancedReturnFlagTask":
%result = "slf.def.flag";
case "AIEnhancedRearmTask":
%result = "avo.grunt";
case "AIEnhancedEngageTarget":
%result = "slf.att.attack";
case "AIEnhancedScoutLocation":
%result = "slf.def.defend";
case "AIEnhancedEscort":
%result = "slf.tsk.cover";
}
return %result;
}