Added weapon pre-profiler code

This commit is contained in:
Robert MacGregor 2015-10-10 23:55:09 -04:00
parent 167137f755
commit 1ab65e9e70
4 changed files with 187 additions and 9 deletions

View file

@ -15,6 +15,7 @@
//------------------------------------------------------------------------------------------
function AIConnection::initialize(%this)
{
%this.dangerObjects = new SimSet();
%this.fieldOfView = $DXAI::Bot::DefaultFieldOfView;
%this.viewDistance = $DXAI::Bot::DefaultViewDistance;
}
@ -197,6 +198,10 @@ function AIConnection::updateLegs(%this)
%delta = %now - %this.lastUpdateLegs;
%this.lastUpdateLegs = %now;
// Set any danger we may need.
for (%iteration = 0; %iteration < %this.dangerObjects.getCount(); %iteration++)
%this.setDangerLocation(%this.dangerObjects.getObject(%iteration).getPosition(), 3);
if (%this.isMovingToTarget)
{
if (%this.aimAtLocation)
@ -225,23 +230,52 @@ function AIConnection::updateLegs(%this)
//------------------------------------------------------------------------------------------
function AIConnection::updateWeapons(%this)
{
%lockedObject = %this.player;
%mount = %this.player.getObjectMount();
if (isObject(%mount))
%lockedObject = %mount;
// FIXME: Toss %this.player.lockedCount grenades, this will toss all of them basically instantly.
if (%lockedObject.isLocked() && %this.player.invFlareGrenade != 0)
{
%this.pressGrenade();
}
if (isObject(%this.engageTarget))
{
%player = %this.player;
%targetDistance = vectorDist(%player.getPosition(), %this.engageTarget.getPosition());
// Firstly, just aim at them for now
%this.aimAt(%this.engageTarget.getWorldBoxCenter());
%this.aimAt(%this.engageTarget.getPosition());
// 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++)
{
%currentWeapon = %player.weaponSlot[%iteration];
%currentWeaponImage = %currentWeapon.image;
// No ammo?
if (isObject(%currentWeaponImage.ammo) && %this.player.inv[%currentWeaponImage.ammo] <= 0)
continue;
if (%currentWeaponImage.projectileSpread >= 3 && %targetDistance <= 20)
%bestWeapon = %iteration;
else if (%currentWeaponImage.projectileSpread < 3 && %targetDistance >= 20)
%bestWeapon = %iteration;
else if (%targetDistance >= 100 && %currentWeaponImage.projectileType $= "GrenadeProjectile")
%bestWeapon = %iteration;
// Weapons with a decent bit of spread should be used <= 20m
// Arced & precision Weapons should be used at >= 100m
}
%player.selectWeaponSlot(%bestWeapon);
%this.pressFire(200);
}
}
@ -266,7 +300,7 @@ function AIConnection::updateVisualAcuity(%this)
// If we can't even see or if we're downright dead, don't do anything.
if (%this.visibleDistance = 0 || !isObject(%this.player) || %this.player.getState() !$= "Move")
{
%this.visualAcuityTick = %this.schedule(getRandom($DXAI::Bot::MinimumVisualAcuityTime, $DXAI::Bot::MaximumVisualAcuityTime,), "updateVisualAcuity");
%this.visualAcuityTick = %this.schedule(getRandom($DXAI::Bot::MinimumVisualAcuityTime, $DXAI::Bot::MaximumVisualAcuityTime), "updateVisualAcuity");
return;
}
@ -322,7 +356,11 @@ function AIConnection::updateVisualAcuity(%this)
%noticeTime = getRandom(700, 1200);
if (%this.awarenessTime[%current] < %noticeTime)
continue;
// Is it a object we want to avoid?
if (AIGrenadeSet.isMember(%current))
%this.dangerObjects.add(%current);
if (%current.getType() & $TypeMasks::ProjectileObjectType)
{
%className = %current.getClassName();

View file

@ -230,9 +230,10 @@ function GameConnection::getObjectsInViewcone(%this, %typeMask, %distance, %perf
%hitObject = getWord(%raycast, 0);
// Since the engine doesn't do raycasts against projectiles correctly, we just check if the bot
// 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
if (%hitObject == %currentObject || (%currentObject.getType() & $TypeMasks::ProjectileObjectType && !isObject(%hitObject)))
%workaroundTypes = $TypeMasks::ProjectileObjectType | $TypeMasks::ItemObjectType;
if (%hitObject == %currentObject || (%currentObject.getType() & %workaroundTypes && !isObject(%hitObject)))
%result.add(%currentObject);
}
}

View file

@ -47,6 +47,9 @@ function DXAI::setup(%numTeams)
// Set our setup flag so that the execution hooks can behave correctly
$DXAI::System::Setup = true;
// Create the AIGrenadeSet to hold known grenades.
new SimSet(AIGrenadeSet);
for (%iteration = 1; %iteration < %numTeams + 1; %iteration++)
{
%commander = new ScriptObject() { class = "AICommander"; team = %iteration; };
@ -113,6 +116,131 @@ function DXAI::validateEnvironment()
$DXAI::System::InvalidatedEnvironment = false;
}
//------------------------------------------------------------------------------------------
// Description: The weapon profiler loops over the active game datablocks, trying to
// run solely on weapons and precompute useful usage information for the artificial
// intelligence to use during weapon selection & firing.
// Param %printResults: A boolean representing whether or not the results should be
// printed to the console, useful for debugging.
//------------------------------------------------------------------------------------------
function DXAI::runWeaponProfiler(%printResults)
{
if (!isObject(DatablockGroup))
{
error("DXAI: Cannot run weapons profiler, no DatablockGroup exists!");
return;
}
for (%iteration = 0; %iteration < DataBlockGroup.getCount(); %iteration++)
{
%currentItem = DataBlockGroup.getObject(%iteration);
if (%currentItem.getClassName() $= "ItemData")
{
%currentImage = %currentItem.image;
if (isObject(%currentImage))
{
%currentProjectile = %currentImage.Projectile;
%ammoDB = %currentImage.ammo;
%usesAmmo = isObject(%ammoDB);
%usesEnergy = %currentImage.usesEnergy;
%firingEnergy = %currentImage.minEnergy;
%spread = %currentImage.projectileSpread;
if (%currentImage.isSeeker)
{
%dryEffectiveRange = %currentImage.seekRadius;
%wetEffectiveRange = %currentImage.seekRadius;
%dryAccurateRange = %currentImage.seekRadius;
%wetAccurateRange = %currentImage.seekRadius;
}
else if (isObject(%currentProjectile) && %currentProjectile.getClassName() $= "SniperProjectileData")
{
%dryEffectiveRange = %currentProjectile.maxRifleRange;
%wetEffectiveRange = %currentProjectile.maxRifleRange;
%dryAccurateRange = %currentProjectile.maxRifleRange;
%wetAccurateRange = %currentProjectile.maxRifleRange;
}
else
{
%dryEffectiveRange = (%currentProjectile.lifetimeMS / 1000) * %currentProjectile.dryVelocity;
%wetEffectiveRange = (%currentProjectile.lifetimeMS / 1000) * %currentProjectile.wetVelocity;
%dryAccurateRange = %dryEffectiveRange - (%currentImage.projectileSpread * 8);
%wetAccurateRange = %wetEffectiveRange - (%currentImage.projectileSpread * 8);
}
// We want to know if this thing fires underwater: We start at the initial state and look for something
// that prohibits underwater usage.
%firesWet = true;
%firesDry = true;
// First, we map out state names
%stateCount = -1;
%targetState = -1;
while (%currentImage.stateName[%stateCount++] !$= "")
{
%stateMapping[%currentImage.stateName[%stateCount]] = %stateCount;
if (%currentImage.stateFire[%stateCount])
%targetStateID = %stateCount;
}
// Start at the Ready state and go
%currentState = %stateMapping["Ready"];
%stateIteration = -1;
while (%stateIteration++ <= 10)
{
if (%currentImage.stateTransitionOnTriggerDown[%currentState] !$= "")
%currentState = %stateMapping[%currentImage.stateTransitionOnTriggerDown[%currentState]];
else if (%currentImage.stateTransitionOnWet[%currentState] $= "DryFire")
{
%firesWet = false;
// Check if it fires dry here as well
%firesDry = %currentImage.stateTransitionOnNotWet[%currentState] !$= "DryFire" ? true : false;
break;
}
}
if (%stateIteration == 10)
error("DXAI: State analysis timed out on " @ %currentItem.getName() @ "!");
// Perform the assignments and we're done ... probably
%currentItem.firingEnergy = %firingEnergy;
%currentItem.dryEffectiveRange = %dryEffectiveRange;
%currentItem.wetEffectiveRange = %wetEffectiveRange;
%currentItem.dryAccurateRange = %dryAccurateRange;
%currentItem.wetAccurateRange = %wetAccurateRange;
%currentItem.firesWet = %firesWet;
%currentItem.firesDry = %firesDry;
%currentItem.usesAmmo = %usesAmmo;
%currentItem.usesEnergy = %usesEnergy;
%currentItem.firingEnergy = %firingEnergy;
%currentItem.ammoDB = %ammoDB;
%currentItem.spread = %spread;
if (%printResults)
{
error(%currentItem.getName());
error("Dry Range: " @ %dryEffectiveRange);
error("Wet Range: " @ %wetEffectiveRange);
error("Dry Accurate Range: " @ %dryAccurateRange);
error("Wet Accurate Range: " @ %wetAccurateRange);
error("Fires Wet: " @ %firesWet);
error("Fires Dry: " @ %firesDry);
if (!isObject(%currentProjectile))
error("*** COULD NOT FIND PROJECTILE ***");
error("--------------------------------------");
}
}
}
}
}
//------------------------------------------------------------------------------------------
// Description: This update function is scheduled to be called roughly once every 32
// milliseconds which updates each active commander in the game as well as performs
@ -298,6 +426,17 @@ package DXAI_Hooks
// Ensure that the DXAI is active.
DXAI::validateEnvironment();
}
//------------------------------------------------------------------------------------------
// Description: The AIGrenadeThrown function is called to notify the AI code that a
// grenade has been added to the game sim which is how bots will evade any grenade that
// is merely within range of them. However, this is not the behavior we want. We want the
// bots to actually see the grenade before responding to it.
//------------------------------------------------------------------------------------------
function AIGrenadeThrown(%projectile)
{
AIGrenadeSet.add(%projectile);
}
// Make this do nothing so the bots don't ever get any objectives by default
function DefaultGame::AIChooseGameObjective(%game, %client) { return 11595; }

View file

@ -217,9 +217,9 @@ function AIEnhancedEngageTarget::monitor(%task, %client)
return;
}
%client.engageTargetLastPosition = %client.engageTarget.getWorldBoxCenter();
%client.setMoveTarget(getRandomPositionOnTerrain(%client.engageTargetLastPosition, 40));
%client.pressFire();
// %client.engageTargetLastPosition = %client.engageTarget.getWorldBoxCenter();
// %client.setMoveTarget(getRandomPositionOnTerrain(%client.engageTargetLastPosition, 40));
//%client.pressFire();
}
else if (%client.engageTargetLastPosition !$= "")
{