TribesReplay/base/scripts/aiDefaultTasks.cs
Robert MacGregor b1941454ec v22228 (04/06/01):
* Fixed a problem that could have caused texture leaking in the interiors
* Fixed an AI problem in Training 2
* Chinese "simplified" keyboard supported
* Korean keyboard supported
* A bug where infinite ammo could be gained by tossing the ammo was prevented.
* Fixed a problem in Training 2 where a waypoint wouldn't update properly.
* Thundersword and Havoc hold steady now when players try to jump in so they don't invert and detonate.
* CD is now required in the drive for on-line play.
* Scoring has been fixed so that it isn't blanked any longer if the admin changes the time limit during a game.
* Active server queries will be cancelled now when you join a game (loads game faster now).
* If standing in an inventory station when it is destroyed you no longer permanently lose weapons.
* Fixed two issues that *could* cause crashes.
* Fixed a problem where the bombardier could create a permanent targeting laser.
* Cleaned up Chat text to remove programming characters.
* Fixed "highlight text with my nick" option so it saves preference to file correctly.
* Made MPB able to climb hills more easily and reduced damage from impact with the ground.
* Added button to stop server queries in progress on "JOIN" screen.
* Observers can now only chat with other observers (no one else can hear them).
* Made deployable inv stations have smaller trigger so they don't "suck you in" from so far away.
* Bots will now claim switches in CnH more accurately.
* Added a "max distance" ring for sensors on the commander map so players can more accurately assess how well they placed the sensor in relation to how much area it is actually sensing.
* Added a "ding" sound when you have filled up a text buffer so that you know why your text isn't showing up.
* Fixed Chat HUD so that page up/page down works better.
* Fixed a situation where Heavies could end up being permanently cloaked.
* The MPBs on the "Alcatraz" map now deploy correctly (Siege map).
* The "edited post" date stamp now works correctly.
* If you jump into a vehicle while zoomed in, the zoom will reset correctly therafter now.
* The Score Screen (F2) is now available while in a vehicle.
* You can now vote to kick observers, if desired.
* The ELF turret is fixed so it no longer fires at players when destroyed (an intermittent bug)
* Some console spam associated with the Wildcat has been removed.
* There was a situation where a player could die twice if he fell out of bounds. That has been resolved.
* Screen resolution information should update properly now when restarting the application.
* The camera should no longer be able to dip below terrain when in third person mode.
2017-07-17 23:05:21 -04:00

1028 lines
29 KiB
C#

//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;
$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);
}
}
}
//-----------------------------------------------------------------------------