mirror of
https://github.com/Ragora/T2-DXAI.git
synced 2026-01-19 18:14:45 +00:00
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:
parent
57a25dd872
commit
5e53dd91ef
|
|
@ -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++)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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
31
scripts/DXAI/loadouts.cs
Normal 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;
|
||||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
@ -160,3 +160,190 @@ 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;
|
||||
}
|
||||
Loading…
Reference in a new issue