ironsphererpg/scripts/aiDefaultTasks.cs

1029 lines
29 KiB
C#
Raw Permalink Normal View History

//All tasks for deathmatch, hunters, and tasks that coincide with the current objective task live here...
//Weights for tasks that override the objective task: must be between 4300 and 4700
$AIWeightVehicleMountedEscort = 4700;
$AIWeightReturnTurretFire = 4675;
$AIWeightNeedItemBadly = 4650;
$AIWeightReturnFire = 4600;
$AIWeightDetectMine = 4500;
$AIWeightTauntVictim = 4400;
$AIWeightNeedItem = 4350;
$AIWeightDestroyTurret = 4300;
//Weights that allow the objective task to continue: must be 3000 or less
$AIWeightFoundEnemy = 3000;
$AIWeightFoundItem = 2500;
$AIWeightFoundToughEnemy = 1000;
$AIWeightPatrolling = 2000;
//Hunters weights...
$AIHuntersWeightMustCap = 4690;
$AIHuntersWeightNeedHealth = 4625;
$AIHuntersWeightShouldCap = 4425;
$AIHuntersWeightMustEngage = 4450;
$AIHuntersWeightShouldEngage = 4325;
$AIHuntersWeightPickupFlag = 4425;
//Rabbit weights...
$AIRabbitWeightDefault = 4625;
$AIRabbitWeightNeedInv = 4325;
//Bounty weights...
$AIBountyWeightShouldEngage = 4325;
//-----------------------------------------------------------------------------
//AIEngageTask is responsible for anything to do with engaging an enemy
function AIEngageWhoWillWin(%client1, %client2)
{
//assume both clients are alive - gather some info
if (%client1.isAIControlled())
%skill1 = %client1.getSkillLevel();
else
%skill1 = 0.5;
if (%client2.isAIControlled())
%skill2 = %client2.getSkillLevel();
else
%skill2 = 0.5;
%damage1 = %client1.player.getDamagePercent();
%damage2 = %client2.player.getDamagePercent();
//first compare health
%tolerance1 = 0.5 + ((%skill1 - %skill2) * 0.3);
%tolerance2 = 0.5 + ((%skill2 - %skill1) * 0.3);
if (%damage1 - %damage2 > %tolerance1)
return %client2;
else if (%damage2 - %damage1 > %tolerance2)
return %client1;
//health not a problem, see how the equipment compares for the two...
%weaponry1 = AIEngageWeaponRating(%client1);
%weaponry2 = AIEngageWeaponRating(%client2);
%effective = 20;
if (%weaponry1 < %effective && %weaponry2 >= %effective)
return %client2;
else if (%weaponry1 >= %effective && %weaponry2 < %effective)
return %client1;
//no other criteria for now... return -1 to indicate a tie...
return -1;
}
function AIEngageTask::init(%task, %client)
{
}
function AIEngageTask::assume(%task, %client)
{
%task.setWeightFreq(9);
%task.setMonitorFreq(9);
%task.searching = false;
if (isObject(%client.shouldEngage.player))
%task.searchLocation = %client.shouldEngage.player.getWorldBoxCenter();
}
function AIEngageTask::retire(%task, %client)
{
}
function AIEngageTask::weight(%task, %client)
{
%player = %client.player;
if (!isObject(%player))
return;
%clientPos = %player.getWorldBoxCenter();
%currentTarget = %client.shouldEngage;
if (!AIClientIsAlive(%currentTarget))
%currentTarget = %client.getEngageTarget();
%client.shouldEngage = -1;
%mustEngage = false;
%tougherEnemy = false;
//first, make sure we actually can engage
if (AIEngageOutOfAmmo(%client))
{
%client.shouldEngage = -1;
%task.setWeight(0);
return;
}
//see if anyone has fired on us recently...
%losTimeout = $AIClientMinLOSTime + ($AIClientLOSTimeout * %client.getSkillLevel());
if (AIClientIsAlive(%client.lastDamageClient, %losTimeout) && getSimTime() - %client.lastDamageTime < %losTimeout)
{
//see if we should turn on the new attacker
if (AIClientIsAlive(%currentTarget))
{
%targPos = %currentTarget.player.getWorldBoxCenter();
%curTargDist = %client.getPathDistance(%targPos);
%newTargPos = %client.lastDamageClient.player.getWorldBoxCenter();
%newTargDist = %client.getPathDistance(%newTargPos);
//see if the new targ is no more than 30 m further
if (%newTargDist > 0 && %newTargDist < %curTargDist + 30)
{
%client.shouldEngage = %client.lastDamageClient;
%mustEngage = true;
}
}
else
{
%client.shouldEngage = %client.lastDamageClient;
%mustEngage = true;
}
}
//no one has fired at us recently, see if we're near an enemy
else
{
%result = AIFindClosestEnemy(%client, 100, %losTimeout);
%closestEnemy = getWord(%result, 0);
%closestdist = getWord(%result, 1);
if (%closestEnemy > 0)
{
//see if we're right on top of them
%targPos = %closestEnemy.player.getWorldBoxCenter();
%dist = %client.getPathDistance(%targPos);
if (%dist > 0 && %dist < 20)
{
%client.shouldEngage = %closestEnemy;
%mustEngage = true;
}
//else choose them only if we're not already attacking someone
else if (%currentTarget <= 0)
{
%client.shouldEngage = %closestEnemy;
%mustEngage = false;
//Make sure the odds are not overwhelmingly in favor of the enemy...
if (AIEngageWhoWillWin(%client, %closestEnemy) == %closestEnemy)
%tougherEnemy = true;
}
}
}
//if we still haven't found a new target, keep fighting the old one
if (%client.shouldEngage <= 0)
{
if (AIClientIsAlive(%currentTarget))
{
//see if we still have sight of the current target
%hasLOS = %client.hasLOSToClient(%currentTarget);
%losTime = %client.getClientLOSTime(%currentTarget);
if (%hasLOS || %losTime < %losTimeout)
%client.shouldEngage = %currentTarget;
else
%client.shouldEngage = -1;
}
else
%client.shouldEngage = -1;
%mustEngage = false;
}
//finally, set the weight
if (%client.shouldEngage > 0)
{
if (%mustEngage)
%task.setWeight($AIWeightReturnFire);
else if (%tougherEnemy)
%task.setWeight($AIWeightFoundToughEnemy);
else
%task.setWeight($AIWeightFoundEnemy);
}
else
%task.setWeight(0);
}
function AIEngageTask::monitor(%task, %client)
{
if (!AIClientIsAlive(%client.shouldEngage))
{
%client.stop();
%client.clearStep();
%client.setEngageTarget(-1);
return;
}
%hasLOS = %client.hasLOSToClient(%client.shouldEngage);
%losTime = %client.getClientLOSTime(%client.shouldEngage);
//%detectLocation = %client.getDetectLocation(%client.shouldEngage);
%detectPeriod = %client.getDetectPeriod();
//if we can see the target, engage...
if (%hasLOS || %losTime < %detectPeriod)
{
%client.stepEngage(%client.shouldEngage);
%task.searching = false;
%task.searchLocation = %client.shouldEngage.player.getWorldBoxCenter();
}
//else if we haven't for approx 5 sec... move to the last known location
else
{
//clear the engage target
%client.setEngageTarget(-1);
if (! %task.searching)
{
%dist = VectorDist(%client.player.getWorldBoxCenter(), %task.searchLocation);
if (%dist < 4)
{
%client.stepIdle(%task.searchLocation);
%task.searching = true;
}
else
%client.stepMove(%task.searchLocation, 4.0);
}
}
}
//-----------------------------------------------------------------------------
//AIPickupItemTask is responsible for anything to do with picking up an item
function AIPickupItemTask::init(%task, %client)
{
}
function AIPickupItemTask::assume(%task, %client)
{
%task.setWeightFreq(10);
%task.setMonitorFreq(10);
%task.pickUpItem = -1;
}
function AIPickupItemTask::retire(%task, %client)
{
}
function AIPickupItemTask::weight(%task, %client)
{
//if we're already picking up an item, make sure it's still valid, then keep the weight the same...
if (%task.pickupItem > 0)
{
if (isObject(%task.pickupItem) && !%task.pickupItem.isHidden() && AICouldUseItem(%client, %task.pickupItem))
return;
else
%task.pickupItem = -1;
}
//otherwise, search for objects
//first, see if we can pick up health
%player = %client.player;
if (!isObject(%player))
return;
%damage = %player.getDamagePercent();
%healthRad = %damage * 100;
%closestHealth = -1;
%closestHealthDist = %healthRad;
%closestHealthLOS = false;
%closestItem = -1;
%closestItemDist = 32767;
%closestItemLOS = false;
//loop through the item list, looking for things to pick up
%itemCount = $AIItemSet.getCount();
for (%i = 0; %i < %itemCount; %i++)
{
%item = $AIItemSet.getObject(%i);
if (!%item.isHidden())
{
%dist = %client.getPathDistance(%item.getWorldBoxCenter());
if (((%item.getDataBlock().getName() $= "RepairKit" || %item.getDataBlock().getName() $= "RepairPatch") ||
(%item.isCorpse && %item.getInventory("RepairKit") > 0)) &&
%player.getInventory("RepairKit") <= 0 && %damage > 0.3)
{
if (%dist > 0 && %dist < %closestHealthDist)
{
%closestHealth = %item;
%closestHealthDist = %dist;
//check for LOS
%mask = $TypeMasks::TerrainObjectType | $TypeMasks::InteriorObjectType;
%closestHealthLOS = !containerRayCast(%client.player.getWorldBoxCenter(), %item.getWorldBoxCenter(), %mask, 0);
}
}
else
{
//only pick up stuff within 35m
if (%dist < 35)
{
if (AICouldUseItem(%client, %item))
{
if (%dist < %closestItemDist)
{
%closestItem = %item;
%closestItemDist = %dist;
//check for LOS
%mask = $TypeMasks::TerrainObjectType | $TypeMasks::InteriorObjectType;
%closestItemLOS = !containerRayCast(%client.player.getWorldBoxCenter(), %item.getWorldBoxCenter(), %mask, 0);
}
}
}
}
}
}
//now set the weight
if (%closestHealth > 0)
{
//only choose an item if it's at least 25 m closer than health...
//and we're not engageing someone or not that badly in need
%currentTarget = %client.getEngageTarget();
if (%closestItem > 0 && %closetItemDist < %closestHealthDist - 25 && (%damage < 0.6 || %currentTarget <= 0) && %closestItemLOS)
{
%task.pickupItem = %closestItem;
if (AIEngageWeaponRating(%client) < 20)
%task.setWeight($AIWeightNeedItemBadly);
else if (%closestItemDist < 10 && %closestItemLOS)
%task.setWeight($AIWeightNeedItem);
else if (%closestItemLOS)
%task.setWeight($AIWeightFoundItem);
else
%task.setWeight(0);
}
else
{
if (%damage > 0.8)
{
%task.pickupItem = %closestHealth;
%task.setWeight($AIWeightNeedItemBadly);
}
else if (%closestHealthLOS)
{
%task.pickupItem = %closestHealth;
%task.setWeight($AIWeightNeedItem);
}
else
%task.setWeight(0);
}
}
else if (%closestItem > 0)
{
%task.pickupItem = %closestItem;
if (AIEngageWeaponRating(%client) < 20)
%task.setWeight($AIWeightNeedItemBadly);
else if (%closestItemDist < 10 && %closestItemLOS)
%task.setWeight($AIWeightNeedItem);
else if (%closestItemLOS)
%task.setWeight($AIWeightFoundItem);
else
%task.setWeight(0);
}
else
%task.setWeight(0);
}
function AIPickupItemTask::monitor(%task, %client)
{
//move to the pickup location
if (isObject(%task.pickupItem))
%client.stepMove(%task.pickupItem.getWorldBoxCenter(), 0.25);
//this call works in conjunction with AIEngageTask
%client.setEngageTarget(%client.shouldEngage);
}
//-----------------------------------------------------------------------------
//AIUseInventoryTask will cause them to use an inv station if they're low
//on ammo. This task should be used only for DM and Hunters - most objectives
//have their own logic for when to use an inv station...
function AIUseInventoryTask::init(%task, %client)
{
}
function AIUseInventoryTask::assume(%task, %client)
{
%task.setWeightFreq(15);
%task.setMonitorFreq(5);
//mark the current time for the buy inventory state machine
%task.buyInvTime = getSimTime();
}
function AIUseInventoryTask::retire(%task, %client)
{
//reset the state machine time stamp...
%task.buyInvTime = getSimTime();
}
function AIUseInventoryTask::weight(%task, %client)
{
//first, see if we can pick up health
%player = %client.player;
if (!isObject(%player))
return;
%damage = %player.getDamagePercent();
%weaponry = AIEngageWeaponRating(%client);
//if there's an inv station, and we haven't used an inv station since we
//spawned, the bot should use an inv once regardless
if (%client.spawnUseInv)
{
//see if we're already heading there
if (%client.buyInvTime != %task.buyInvTime)
{
//see if there's an inventory we can use
%result = AIFindClosestInventory(%client, false);
%closestInv = getWord(%result, 0);
if (isObject(%closestInv))
{
%task.setWeight($AIWeightNeedItem);
return;
}
else
%client.spawnUseInv = false;
}
else
{
%task.setWeight($AIWeightNeedItem);
return;
}
}
//first, see if we need equipment or health
if (%damage < 0.3 && %weaponry >= 40)
{
%task.setWeight(0);
return;
}
//don't use inv stations if we're not that badly damaged, and we're in the middle of a fight
if (%damage < 0.6 && %client.getEngageTarget() > 0 && !AIEngageOutOfAmmo(%client))
{
%task.setWeight(0);
return;
}
//if we're already buying, continue
if (%task.buyInvTime == %client.buyInvTime)
{
//set the weight - if our damage is above 0.8 or our we're out of ammo
if (%damage > 0.8 || AIEngageOutOfAmmo(%client))
%task.setWeight($AIWeightNeedItemBadly);
else
%task.setWeight($AIWeightNeedItem);
return;
}
//we need to search for an inv station near us...
%result = AIFindClosestInventory(%client, false);
%closestInv = getWord(%result, 0);
%closestDist = getWord(%result, 1);
//only use inv stations if we're right near them... patrolTask will get us nearer if required
if (%closestDist > 35)
{
%task.setWeight(0);
return;
}
//set the weight...
%task.closestInv = %closestInv;
if (%damage > 0.8 || AIEngageOutOfAmmo(%client))
%task.setWeight($AIWeightNeedItemBadly);
else if (%closestDist < 20 && (AIEngageWeaponRating(%client) <= 30 || %damage > 0.4))
%task.setWeight($AIWeightNeedItem);
else if (%hasLOS)
%task.setWeight($AIWeightFoundItem);
else
%task.setWeight(0);
}
function AIUseInventoryTask::monitor(%task, %client)
{
//make sure we still need equipment
%player = %client.player;
if (!isObject(%player))
return;
%damage = %player.getDamagePercent();
%weaponry = AIEngageWeaponRating(%client);
if (%damage < 0.3 && %weaponry >= 40 && !%client.spawnUseInv)
{
%task.buyInvTime = getSimTime();
return;
}
//pick a random set based on armor...
%randNum = getRandom();
if (%randNum < 0.4)
%buySet = "LightEnergyDefault MediumEnergySet HeavyEnergySet";
else if (%randNum < 0.6)
%buySet = "LightShieldSet MediumShieldSet HeavyShieldSet";
else if (%randNum < 0.8)
%buySet = "LightEnergyELF MediumRepairSet HeavyAmmoSet";
else
%buySet = "LightEnergySniper MediumEnergySet HeavyEnergySet";
//process the inv buying state machine
%result = AIBuyInventory(%client, "", %buySet, %task.buyInvTime);
//if we succeeded, reset the spawn flag
if (%result $= "Finished")
%client.spawnUseInv = false;
//if we succeeded or failed, reset the state machine...
if (%result !$= "InProgress")
%task.buyInvTime = getSimTime();
//this call works in conjunction with AIEngageTask
%client.setEngageTarget(%client.shouldEngage);
}
//-----------------------------------------------------------------------------
//AITauntCorpseTask is should happen only after an enemy is freshly killed
function AITauntCorpseTask::init(%task, %client)
{
}
function AITauntCorpseTask::assume(%task, %client)
{
%task.setWeightFreq(11);
%task.setMonitorFreq(11);
}
function AITauntCorpseTask::retire(%task, %client)
{
}
function AITauntCorpseTask::weight(%task, %client)
{
%task.corpse = %client.getVictimCorpse();
if (%task.corpse > 0 && getSimTime() - %client.getVictimTime() < 15000)
{
//see if we're already taunting, and if it's time to stop
if ((%task.tauntTime > %client.getVictimTime()) && (getSimTime() - %task.tauntTime > 5000))
%task.corpse = -1;
else
{
//if the corpse is within 50m, taunt
%distToCorpse = %client.getPathDistance(%task.corpse.getWorldBoxCenter());
if (%dist < 0 || %distToCorpse > 50)
%task.corpse = -1;
}
}
else
%task.corpse = -1;
//set the weight
if (%task.corpse > 0)
{
//don't taunt someone if there's an enemy right near by...
%result = AIFindClosestEnemy(%client, 40, 15000);
%closestEnemy = getWord(%result, 0);
%closestdist = getWord(%result, 1);
if (%closestEnemy > 0)
%task.setWeight(0);
else
%task.setWeight($AIWeightTauntVictim);
}
else
%task.setWeight(0);
}
$AITauntChat[0] = "gbl.aww";
$AITauntChat[1] = "gbl.brag";
$AITauntChat[2] = "gbl.obnoxious";
$AITauntChat[3] = "gbl.sarcasm";
$AITauntChat[4] = "gbl.when";
function AITauntCorpseTask::monitor(%task, %client)
{
//make sure we still have a corpse, and are not fighting anyone
if (%client.getEngageTarget() <= 0 && %task.corpse > 0 && isObject(%task.corpse))
{
%clientPos = %client.player.getWorldBoxCenter();
%corpsePos = %task.corpse.getWorldBoxCenter();
%distToCorpse = VectorDist(%clientPos, %corpsePos);
if (%distToCorpse < 2.0)
{
//start the taunt!
if (%task.tauntTime < %client.getVictimTime())
{
%task.tauntTime = getSimTime();
%client.stop();
if (getRandom() > 0.2)
{
//pick the sound and taunt cels
%sound = $AITauntChat[mFloor(getRandom() * 3.99)];
%minCel = 2;
%maxCel = 8;
schedule(250, %client, "AIPlayAnimSound", %client, %corpsePos, %sound, %minCel, %maxCel, 0);
}
//say 'bye' :)
else
schedule(250, %client, "AIPlayAnimSound", %client, %corpsePos, "gbl.bye", 2, 2, 0);
}
}
else
%client.stepMove(%task.corpse.getWorldBoxCenter(), 1.75);
}
}
//-----------------------------------------------------------------------------
//AIPatrolTask used to wander around the map (DM and Hunters mainly) looking for something to do...
function AIPatrolTask::init(%task, %client)
{
}
function AIPatrolTask::assume(%task, %client)
{
%task.setWeightFreq(13);
%task.setMonitorFreq(13);
%task.findLocation = true;
%task.patrolLocation = "0 0 0";
%task.idleing = false;
%task.idleEndTime = 0;
}
function AIPatrolTask::retire(%task, %client)
{
}
function AIPatrolTask::weight(%task, %client)
{
%task.setWeight($AIWeightPatrolling);
}
function AIPatrolTask::monitor(%task, %client)
{
//this call works in conjunction with AIEngageTask
%client.setEngageTarget(%client.shouldEngage);
//see if we're close enough to our patrol point
if (%task.idleing)
{
if (getSimTime() > %task.idleEndTime)
{
%task.findLocation = true;
%task.idleing = false;
}
}
//see if we need to find a place to go...
else if (%task.findLocation)
{
//first, see if we're in need of either health, or ammo
//note: normally, I'd be tempted to put this kind of "looking for health" code
//into the AIPickupItemTask, however, that task will be used in CTF, where you
//don't want people on AIDefendLocation to leave their post to hunt for health, etc...
//AIPickupItemTask only deals with items within a 30m radius around the bot.
//AIPatrolTask will move the bot to the vicinity of an item, then AIPickUpItemTask
//will finish the job...
%foundItemLocation = false;
%damage = %client.player.getDamagePercent();
if (%damage > 0.7)
{
//search for a health kit
%closestHealth = AIFindSafeItem(%client, "Health");
if (%closestHealth > 0)
{
%task.patrolLocation = %closestHealth.getWorldBoxCenter();
%foundItemLocation = true;
}
}
else if (AIEngageOutOfAmmo(%client))
{
//search for a Ammo or a weapon...
%closestItem = AIFindSafeItem(%client, "Ammo");
if (%closestItem > 0)
{
%task.patrolLocation = %closestItem.getWorldBoxCenter();
%foundItemLocation = true;
}
}
//now see if we don't really have good equipment...
if (!%foundItemLocation && AIEngageWeaponRating(%client) < 20)
{
//search for any useful item
%closestItem = AIFindSafeItem(%client, "Any");
if (%closestItem > 0)
{
%task.patrolLocation = %closestItem.getWorldBoxCenter();
%foundItemLocation = true;
}
}
//choose a randomish location only if we're not in need of health or ammo
if (!%foundItemLocation)
{
//find a random item/inventory in the map, and pick a spawn point near it...
%pickGraphNode = false;
%chooseSet = 0;
if ($AIInvStationSet.getCount() > 0)
%chooseSet = $AIInvStationSet;
else if ($AIWeaponSet.getCount() > 0)
%chooseSet = $AIWeaponSet;
else if ($AIItemSet.getCount() > 0)
%chooseSet = $AIItemSet;
if (!%chooseSet)
%pickGraphNode = true;
//here we pick whether we choose a random map point, or a point based on an item...
if (getRandom() < 0.3)
%pickGraphNode = true;
//here we decide whether we should choose a player location... a bit of a cheat but
//it's scaled by the bot skill level
%pickPlayerLocation = false;
%skill = %client.getSkillLevel();
if (%skill < 1.0)
%skill = %skill / 2.0;
if (getRandom() < (%skill * %skill) && ClientGroup.getCount() > 1)
{
//find a random client
%count = ClientGroup.getCount();
%index = (getRandom() * (%count - 0.1));
%cl = ClientGroup.getObject(%index);
if (%cl != %client && AIClientIsAlive(%cl))
{
%task.patrolLocation = %cl.player.getWorldBoxCenter();
%pickGraphNode = false;
%pickPlayerLocation = true;
}
}
if (!%pickGraphNode && !%pickPlayerLocation)
{
%itemCount = %chooseSet.getCount();
%item = %chooseSet.getObject(getRandom() * (%itemCount - 0.1));
%nodeIndex = navGraph.randNode(%item.getWorldBoxCenter(), 10, true, true);
if (%nodeIndex <= 0)
%pickGraphNode = true;
else
%task.patrolLocation = navGraph.randNodeLoc(%nodeIndex);
}
//see if we failed above or have to pick just a random spot on the graph - use the spawn points...
if (%pickGraphNode)
{
%task.patrolLocation = Game.pickPlayerSpawn(%client, true);
if (%task.patrolLocation == -1)
{
%client.stepIdle(%client.player.getWorldBoxCenter());
return;
}
}
}
//now that we have a new location - move towards it
%task.findLocation = false;
%client.stepMove(%task.patrolLocation, 8.0);
}
//else we're on patrol - see if we're close to our destination
else
{
%client.stepMove(%task.patrolLocation, 8.0);
%distToDest = %client.getPathDistance(%task.patrolLocation);
if (%distToDest > 0 && %distToDest < 10)
{
%task.idleing = true;
%task.idleEndTime = 4000 + getSimTime() + (getRandom() * 6000);
%client.stepIdle(%client.player.getWorldBoxCenter());
}
}
}
//-----------------------------------------------------------------------------
//AIEngageTurretTask is responsible for returning turret fire...
function AIEngageTurretTask::init(%task, %client)
{
}
function AIEngageTurretTask::assume(%task, %client)
{
%task.setWeightFreq(4);
%task.setMonitorFreq(4);
}
function AIEngageTurretTask::retire(%task, %client)
{
%client.engageTurret = -1;
%client.setEngagetarget(-1);
}
function AIEngageTurretTask::weight(%task, %client)
{
//see if we're still fighting a turret
%elapsedTime = getSimTime() - %task.startAttackTime;
if (isObject(%task.engageTurret) && %task.engageTurret.getDataBlock().getClassName() $= "TurretData")
{
if (%task.engageTurret == %client.lastdamageTurret)
{
if (%task.engageTurret.isEnabled() && getSimTime() - %client.lastDamageTurretTime < 5000)
%task.setWeight($AIWeightReturnTurretFire);
else
%task.setWeight($AIWeightDestroyTurret);
}
else if (AIClientIsAlive(%client, %elapsedTime))
{
//if another turret is shooting us, disable this one first...
if (isObject(%client.lastDamageTurret) && %client.lastDamageTurret.getDataBlock().getClassName() $= "TurretData")
{
if (%task.engageTurret.isEnabled())
%task.setWeight($AIWeightReturnTurretFire);
else
{
//see if we need to switch to the new turret
if (%client.lastDamageTurret.isEnabled() && %client.lastDamageTurretTime < 5000)
{
%task.engageTurret = %client.lastDamageTurret;
%task.attackInitted = false;
%task.setWeight($AIWeightDestroyTurret);
}
else
%task.setWeight($AIWeightReturnTurretFire);
}
}
else
{
if (%task.engageTurret.isEnabled() && getSimTime() - %client.lastDamageTurretTime < 5000)
%task.setWeight($AIWeightReturnTurretFire);
else
%task.setWeight($AIWeightDestroyTurret);
}
}
//else we died since - clear out the vars
else
{
%task.engageTurret = -1;
%task.setWeight(0);
}
}
//else see if we have a new target
else if (isObject(%client.lastDamageTurret) && %client.lastDamageTurret.getDataBlock().getClassName() $= "TurretData")
{
%task.engageTurret = %client.lastDamageTurret;
%task.attackInitted = false;
if (%client.lastDamageTurret.isEnabled() && %client.lastDamageTurretTime < 5000)
%task.setWeight($AIWeightReturnTurretFire);
else
%task.setWeight($AIWeightDestroyTurret);
}
//else no turret to attack... (later, do a query to find turrets before they attack)
else
{
%task.engageTurret = -1;
%task.setWeight(0);
}
}
function AIEngageTurretTask::monitor(%task, %client)
{
if (isObject(%task.engageTurret) && %task.engageTurret.getDataBlock().getClassName() $= "TurretData")
{
//set the AI to fire at the turret
%client.setEngageTarget(-1);
%client.setTargetObject(%task.engageTurret);
%clientPos = %client.player.getWorldBoxCenter();
%turretPos = %task.engageTurret.getWorldBoxCenter();
//control the movement - first, hide, then wait, then attack
if (!%task.attackInitted)
{
%task.attackInitted = true;
%task.startAttackTime = getSimTime();
%task.hideLocation = %client.getHideLocation(%turretPos, 40.0, %clientPos, 4.0);
%client.stepMove(%task.hideLocation, 2.0);
}
else if (getSimTime() - %task.startAttackTime > 5000)
{
%client.stepMove(%task.engageTurret.getWorldBoxCenter(), 8.0);
}
}
}
//-----------------------------------------------------------------------------
//AIAvoidMineTask is responsible for detecting/destroying enemy mines...
function AIDetectMineTask::init(%task, %client)
{
}
function AIDetectMineTask::assume(%task, %client)
{
%task.setWeightFreq(7);
%task.setMonitorFreq(7);
}
function AIDetectMineTask::retire(%task, %client)
{
%task.engageMine = -1;
%task.attackInitted = false;
%client.setTargetObject(-1);
}
function AIDetectMineTask::weight(%task, %client)
{
//crappy hack, but they need the proper weapon before they can destroy a mine...
%player = %client.player;
if (!isObject(%player))
return;
%hasPlasma = (%player.getInventory("Plasma") > 0) && (%player.getInventory("PlasmaAmmo") > 0);
%hasDisc = (%player.getInventory("Disc") > 0) && (%player.getInventory("DiscAmmo") > 0);
if (!%hasPlasma && !%hasDisc)
{
%task.setWeight(0);
return;
}
//if we're already attacking a mine,
if (%task.engageMine > 0 && isObject(%task.engageMine))
{
%task.setWeight($AIWeightDetectMine);
return;
}
//see if we're within the viscinity of a new (enemy) mine
%task.engageMine = -1;
%closestMine = -1;
%closestDist = 15; //initialize so only mines within 15 m will be detected...
%mineCount = $AIDeployedMineSet.getCount();
for (%i = 0; %i < %mineCount; %i++)
{
%mine = $AIDeployedMineSet.getObject(%i);
%mineTeam = %mine.sourceObject.team;
%minePos = %mine.getWorldBoxCenter();
%clPos = %client.player.getWorldBoxCenter();
//see if the mine is the closest...
%mineDist = VectorDist(%minePos, %clPos);
if (%mineDist < %closestDist)
{
//now see if we're more or less heading towards the mine...
%clVelocity = %client.player.getVelocity();
%clVelocity = getWord(%clVelocity, 0) SPC getWord(%clVelocity, 1) SPC "0";
%mineVector = VectorSub(%minePos, %clPos);
%mineVector = getWord(%mineVector, 0) SPC getWord(%mineVector, 1) SPC "0";
if (VectorLen(%clVelocity) > 2.0 && VectorLen(%mineVector > 2.0))
{
%clNormal = VectorNormalize(%clVelocity);
%mineNormal = VectorNormalize(%mineVector);
if (VectorDot(%clNormal, %mineNormal) > 0.3)
{
%closestMine = %mine;
%closestDist = %mineDist;
}
}
}
}
//see if we found a mine to attack
if (%closestMine > 0)
{
%task.engageMine = %closestMine;
%task.attackInitted = false;
%task.setWeight($AIWeightDetectMine);
}
else
%task.setWeight(0);
}
function AIDetectMineTask::monitor(%task, %client)
{
if (%task.engageMine > 0 && isObject(%task.engageMine))
{
if (!%task.attackInitted)
{
%task.attackInitted = true;
%client.stepRangeObject(%task.engageMine, "DefaultRepairBeam", 6, 12);
%client.setEngageTarget(-1);
%client.setTargetObject(-1);
}
else if (%client.getStepStatus() $= "Finished")
{
%client.stop();
%client.setTargetObject(%task.engageMine);
}
}
}
//-----------------------------------------------------------------------------