mirror of
https://github.com/exogen/t2-mapper.git
synced 2026-01-20 20:54:55 +00:00
1029 lines
29 KiB
C#
1029 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;
|
|
$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);
|
|
}
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|