mirror of
https://github.com/exogen/t2-mapper.git
synced 2026-01-20 12:44:59 +00:00
525 lines
15 KiB
C#
525 lines
15 KiB
C#
//-----------------------------------------------
|
|
// AI functions for Hunters
|
|
//---------------------------------------------------------------------------
|
|
|
|
// globals
|
|
$AIHuntersFlagSearchRadius = 300;
|
|
$AIHuntersCloseFlagDist = 40;
|
|
$AIHuntersAttackClientFlagCount = 10;
|
|
$AIHuntersMinFlagsToCap = 3;
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
function HuntersGame::onAIRespawn(%game, %client)
|
|
{
|
|
//add the default task
|
|
if (! %client.defaultTasksAdded)
|
|
{
|
|
%client.defaultTasksAdded = true;
|
|
%client.addTask(AIEngageTask);
|
|
%client.addTask(AIPickupItemTask);
|
|
%client.addTask(AIUseInventoryTask);
|
|
%client.addTask(AITauntCorpseTask);
|
|
%client.addTask(AIEngageTurretTask);
|
|
%client.addtask(AIDetectMineTask);
|
|
%client.addTask(AIPatrolTask);
|
|
%client.huntersTask = %client.addTask(AIHuntersTask);
|
|
}
|
|
|
|
//set the inv flag
|
|
%client.spawnUseInv = true;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
function HuntersGame::AIInit(%game)
|
|
{
|
|
//call the default AIInit() function
|
|
AIInit();
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
//AIHuntersTask functions
|
|
//---------------------------------------------------------------------------
|
|
function AIHuntersTask::init(%task, %client)
|
|
{
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
function AIHuntersTask::assume(%task, %client)
|
|
{
|
|
%task.setWeightFreq(10);
|
|
%task.setMonitorFreq(10);
|
|
%task.pickupFlag = -1;
|
|
%task.engageTarget = -1;
|
|
%task.getHealth = -1;
|
|
%task.capFlags = false;
|
|
}
|
|
|
|
function AIHuntersTask::retire(%task, %client)
|
|
{
|
|
%task.pickupFlag = -1;
|
|
%task.engageTarget = -1;
|
|
%task.getHealth = -1;
|
|
%task.capFlags = false;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
function AIHuntersTask::weight(%task, %client)
|
|
{
|
|
// init flag search vars
|
|
%player = %client.player;
|
|
if (!isObject(%player))
|
|
return;
|
|
|
|
%clientPos = %player.getWorldBoxCenter();
|
|
%task.pickupFlag = -1;
|
|
%task.engageTarget = -1;
|
|
%task.capFlags = false;
|
|
|
|
//find the closest flag
|
|
%flagVars = AIFindClosestFlag(%client, $AIHuntersFlagSearchRadius);
|
|
%closestFlag = getWord(%flagVars, 0);
|
|
%closestFlagDist = getWord(%flagVars, 1);
|
|
|
|
//find the dist to the nexus
|
|
%nexusPos = Game.Nexus.getId().getWorldBoxCenter();
|
|
%nexusDist = %client.getPathDistance(%nexusPos);
|
|
if (%nexusDist < 0)
|
|
%nexusDist = 32767;
|
|
|
|
//validate the health item
|
|
if (isObject(%task.getHealth))
|
|
{
|
|
if (%task.getHealth.isHidden())
|
|
%task.getHealth = -1;
|
|
else if ((%task.getHealth.getDataBlock().getName() $= "DeployedStationInventory") ||
|
|
(%task.getHealth.getDataBlock().getName() $= "StationInventory"))
|
|
{
|
|
if (%task.getHealth.isDisabled() && !%task.getHealth.isPowered())
|
|
%task.getHealth = -1;
|
|
}
|
|
}
|
|
|
|
//find the dist to the closest health
|
|
%healthDist = 32767;
|
|
%damage = %client.player.getDamagePercent();
|
|
if (%client.flagCount < 5)
|
|
%damageTolerance = 0.7;
|
|
else
|
|
%damageTolerance = 0.25 + ((%client.getSkillLevel() * %client.getSkillLevel()) * 0.35);
|
|
if (%damage > %damageTolerance)
|
|
{
|
|
if (!isObject(%task.getHealth))
|
|
{
|
|
//search for a health kit
|
|
%closestHealth = AIFindSafeItem(%client, "Health");
|
|
if (isObject(%closestHealth))
|
|
{
|
|
%healthDist = %client.getPathDistance(%closestHealth.getWorldBoxCenter());
|
|
if (%healthDist < 0)
|
|
%healthDist = 32767;
|
|
else
|
|
%healthItem = %closestHealth;
|
|
}
|
|
|
|
//else search for an inventory station
|
|
else
|
|
{
|
|
%result = AIFindClosestInventory(%client, false);
|
|
%closestInv = getWord(%result, 0);
|
|
%closestDist = getWord(%result, 1);
|
|
if (isObject(%closestInv))
|
|
{
|
|
%healthDist = %closestDist;
|
|
%healthItem = %closestInv;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
%healthDist = %client.getPathDistance(%task.getHealth.getWorldBoxCenter());
|
|
if (%healthDist < 0)
|
|
{
|
|
%healthDist = 32767;
|
|
%task.getHealth = -1;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
%task.getHealth = -1;
|
|
|
|
//see if we need to cap - make sure we're actually able first
|
|
%mustCap = false;
|
|
%shouldCap = false;
|
|
%numToScore = %client.flagCount - 1;
|
|
%hoardModeOn = Game.hoardModeActive();
|
|
if ((!Game.greedMode || %numToScore >= Game.greedMinFlags) && !Game.hoardModeActive() && %numToScore >= $AIHuntersMinFlagsToCap)
|
|
{
|
|
//find out how many points would be scored
|
|
%potentialScore = (%numToScore * (%numToScore + 1)) / 2;
|
|
|
|
//find out how many flags we need to take the lead...
|
|
%needFlagsForLead = 0;
|
|
%highestScore = 0;
|
|
%clientIsInLead = false;
|
|
if (Game.teamMode)
|
|
{
|
|
%teamCount = Game.numTeams;
|
|
for (%i = 1; %i <= %teamCount; %i++)
|
|
{
|
|
if ($teamScore[%i] > %highestScore)
|
|
%highestScore = $teamScore[%i];
|
|
}
|
|
|
|
//see if we're in the lead...
|
|
if (%highestScore == $teamScore[%client.team])
|
|
%clientIsInLead = true;
|
|
else
|
|
{
|
|
%tempScore = $teamScore[%client.team] + %potentialScore;
|
|
%flagValue = %numToScore + 1;
|
|
while (%tempScore < %highestScore)
|
|
{
|
|
%tempScore += %flagValue;
|
|
%flagValue++;
|
|
%needFlagsForLead++;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
%clientCount = ClientGroup.getCount();
|
|
for (%i = 0; %i < %clientCount; %i++)
|
|
{
|
|
%cl = ClientGroup.getObject(%i);
|
|
if (%cl.score > %highestScore)
|
|
%highestScore = %cl.score;
|
|
}
|
|
|
|
//see if we're in the lead
|
|
if (%highestScore == %client.score)
|
|
%clientIsInLead = true;
|
|
else
|
|
{
|
|
%tempScore = %client.score + %potentialScore;
|
|
%flagValue = %numToScore + 1;
|
|
while (%tempScore < %highestScore)
|
|
{
|
|
%tempScore += %flagValue;
|
|
%flagValue++;
|
|
%needFlagsForLead++;
|
|
}
|
|
}
|
|
}
|
|
|
|
//the current target is more dangerous than the closest enemy
|
|
%currentTarget = %client.getEngageTarget();
|
|
if (AIClientIsAlive(%currentTarget))
|
|
{
|
|
%closestEnemy = %currentTarget;
|
|
%closestEnemydist = %client.getPathDistance(%currentTarget.player.position);
|
|
if (%closestEnemyDist < 0)
|
|
%closestEnemyDist = 32767;
|
|
}
|
|
else
|
|
{
|
|
%losTimeout = $AIClientMinLOSTime + ($AIClientLOSTimeout * %client.getSkillLevel());
|
|
%result = AIFindClosestEnemy(%client, $AIHuntersCloseFlagDist, %losTimeout);
|
|
%closestEnemy = getWord(%result, 0);
|
|
%closestEnemydist = getWord(%result, 1);
|
|
}
|
|
|
|
//find out how much time is left...
|
|
%curTimeLeftMS = ($Host::TimeLimit * 60 * 1000) + $missionStartTime - getSimTime();
|
|
|
|
//If there's a tough or equal enemy nearby, or no flags, think about capping
|
|
//ie. never cap if there are flags nearby and no enemies...
|
|
if ((AICheckEnemyDanger(%client, 35) >= 3 && %damage > %damageTolerance) ||
|
|
(!isObject(%closestFlag) || %closestFlagDist > $AIHuntersCloseFlagDist))
|
|
{
|
|
//if we've got enough to take the lead, and there are no flags in the vicinity
|
|
if ((!%clientIsInLead && %needFlagsForLead == 0) || %highestScore == 0)
|
|
%mustCap = true;
|
|
|
|
//else if we're about to get our butt kicked...
|
|
else if (AIClientIsAlive(%closestEnemy) && AIEngageWhoWillWin(%closestEnemy, %client) == %closestEnemy)
|
|
{
|
|
%mustCap = true;
|
|
}
|
|
|
|
//else if there's no time left in the mission, cap whatever we've got now...
|
|
else if (%curTimeLeftMS <= 30000)
|
|
%mustCap = true;
|
|
|
|
//else we don't need to cap - see if we should to play it smart
|
|
else
|
|
{
|
|
//if we'd need more than just a couple to take the lead...
|
|
%waitForFlagsTolerance = %client.getSkillLevel() * $AIHuntersMinFlagsToCap * 2;
|
|
if (%needFlagsForLead == 0 || (%needFlagsForLead > %waitForFlagsTolerance))
|
|
{
|
|
%numEnemyFlags = 0;
|
|
%clientCount = ClientGroup.getCount();
|
|
for (%i = 0; %i < %clientCount; %i++)
|
|
{
|
|
%cl = ClientGroup.getObject(%i);
|
|
if (%cl != %client && %cl.flagCount > %numEnemyFlags)
|
|
%numEnemyFlags = %cl.flagCount;
|
|
}
|
|
|
|
//if we're in the lead, or no one has the flags we need, or it's team mode,
|
|
//decide whether to cap based on skill level
|
|
if (%needFlagsForLead == 0 || %numEnemyFlags < %needFlagsForLead || Game.teamMode)
|
|
{
|
|
if (%numToScore >= $AIHuntersMinFlagsToCap + (%client.getSkillLevel() * %client.getSkillLevel() * 15))
|
|
{
|
|
%shouldCap = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//now that we've checked all the possibilities, see if we should or must cap
|
|
if (%mustCap || %shouldCap)
|
|
{
|
|
if (%mustCap)
|
|
%task.setWeight($AIHuntersWeightMustCap);
|
|
else
|
|
%task.setWeight($AIHuntersWeightShouldCap);
|
|
|
|
%task.capFlags = true;
|
|
return;
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////
|
|
// if we've made it this far, we either can't cap, or there's no need to cap... //
|
|
////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
//see if we need health
|
|
if (%damage > %damageTolerance && (isObject(%healthItem) || isObject(%task.getHealth)))
|
|
{
|
|
if (!isObject(%task.getHealth))
|
|
%task.getHealth = %healthItem;
|
|
|
|
%task.setWeight($AIHuntersWeightNeedHealth);
|
|
return;
|
|
}
|
|
|
|
//find the closest player with the most flags (that we have los to)
|
|
%losTimeout = $AIClientMinLOSTime + ($AIClientLOSTimeout * %client.getSkillLevel());
|
|
%bestClientToEngage = findClientWithMostFlags(%client, %losTimeout);
|
|
%bestClientDist = 32767;
|
|
if (AIClientIsAlive(%bestClientToEngage))
|
|
{
|
|
%bestClientDist = %client.getPathDistance(%bestClientToEngage.player.position);
|
|
if (%bestClientDist < 0)
|
|
%bestClientDist = 32767;
|
|
}
|
|
|
|
//see if there's a flag
|
|
if (isObject(%closestFlag))
|
|
{
|
|
//see if there's a client to shoot
|
|
if (AIClientIsAlive(%bestClientToEngage))
|
|
{
|
|
//calc weight base on closesness to the nearest flag vs. and number of flags the client has...
|
|
%engageDistFactor = 30 + %bestClientDist - (%bestClientToEngage.flagCount * 5);
|
|
if (%closestFlagDist < %engageDistFactor)
|
|
{
|
|
%task.pickupFlag = %closestFlag;
|
|
%task.engageTarget = %bestClientToEngage;
|
|
%task.setWeight($AIHuntersWeightPickupFlag);
|
|
}
|
|
|
|
//otherwise, ignore the flag, and go for the client
|
|
else
|
|
{
|
|
%task.pickupFlag = -1;
|
|
%task.engageTarget = %bestClientToEngage;
|
|
%task.setWeight($AIHuntersWeightMustEngage);
|
|
}
|
|
}
|
|
|
|
//else no one to attack
|
|
else
|
|
{
|
|
%task.pickupFlag = %closestFlag;
|
|
%task.engageTarget = -1;
|
|
%task.setWeight($AIHuntersWeightPickupFlag);
|
|
}
|
|
}
|
|
|
|
//else no flag, see if we have someone to attack
|
|
else if (AIClientIsAlive(%bestClientToEngage))
|
|
{
|
|
%task.pickupFlag = -1;
|
|
%task.engageTarget = %bestClientToEngage;
|
|
%task.setWeight($AIHuntersWeightShouldEngage);
|
|
}
|
|
|
|
//nothing hunter's related to do...
|
|
else
|
|
{
|
|
%task.pickupFlag = -1;
|
|
%task.engageTarget = -1;
|
|
%task.setWeight(0);
|
|
}
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
function AIHuntersTask::monitor(%task, %client)
|
|
{
|
|
//see if we should cap
|
|
if (%task.capFlags)
|
|
{
|
|
%nexusPos = Game.Nexus.getId().getWorldBoxCenter();
|
|
%client.stepMove(%nexusPos, 0.25);
|
|
}
|
|
|
|
//see if we've got a flag to pick up and/or someone to engage
|
|
else if (isObject(%task.pickupFlag))
|
|
{
|
|
%client.stepMove(%task.pickupFlag.getWorldBoxCenter(), 0.25);
|
|
|
|
if (AIClientIsAlive(%task.engageTarget))
|
|
%client.setEngageTarget(%task.engageTarget);
|
|
}
|
|
|
|
//see if we've should go for health...
|
|
else if (isObject(%task.getHealth))
|
|
{
|
|
%client.stepMove(%task.getHealth.getWorldBoxCenter(), 1);
|
|
|
|
if (AIClientIsAlive(%task.engageTarget))
|
|
%client.setEngageTarget(%task.engageTarget);
|
|
}
|
|
|
|
//else see if there's just someone to engage
|
|
else if (AIClientIsAlive(%task.engageTarget))
|
|
%client.stepEngage(%task.engageTarget);
|
|
|
|
//if we're not engaging someone related to the hunters task, engage whoever the AIEngageTask wants...
|
|
else
|
|
%client.setEngageTarget(%client.shouldEngage);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
// AIHunters utility functions
|
|
//---------------------------------------------------------------------------
|
|
|
|
//this function checks to make sure a bot isn't in a mosh pit of enemies
|
|
//notice it cheats by not using LOS... )
|
|
function AICheckEnemyDanger(%client, %radius)
|
|
{
|
|
%numEnemies = 0;
|
|
for (%i = 0; %i < ClientGroup.getCount(); %i++)
|
|
{
|
|
%cl = ClientGroup.getObject(%i);
|
|
if (AIClientIsAlive(%cl) && %cl.team != %client.team)
|
|
{
|
|
%dist = %client.getPathDistance(%cl.player.position);
|
|
if (%dist < %radius)
|
|
{
|
|
%winner = AIEngageWhoWillWin(%cl, %client);
|
|
if (%winner == %cl)
|
|
%numEnemies += 3;
|
|
else if (%winner != %client)
|
|
%numEnemies++;
|
|
}
|
|
}
|
|
}
|
|
|
|
return %numEnemies;
|
|
}
|
|
|
|
function AIFindClosestFlag(%client, %radius)
|
|
{
|
|
%closestFlag = -1;
|
|
%closestDist = %radius;
|
|
%flagCount = $FlagGroup.getCount();
|
|
for (%i = 0; %i < %flagCount; %i++)
|
|
{
|
|
%flag = $FlagGroup.getObject(%i);
|
|
%flagPos = %flag.getWorldBoxCenter();
|
|
%dist = %client.getPathDistance(%flagPos);
|
|
|
|
if (%dist > 0 && %dist < %closestDist)
|
|
{
|
|
%closestDist = %dist;
|
|
%closestFlag = %flag;
|
|
}
|
|
}
|
|
return %closestFlag @ " " @ %closestDist;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
function findClientWithMostFlags(%srcClient, %losTimeout)
|
|
{
|
|
%clientCount = 0;
|
|
%closestClient = -1;
|
|
%highestFlagFactor = -1; //take both distance and flag count into consideration
|
|
|
|
%count = ClientGroup.getCount();
|
|
for(%i = 0; %i < %count; %i++)
|
|
{
|
|
%cl = ClientGroup.getObject(%i);
|
|
|
|
//make sure we find someone who's alive, and on an opposing team
|
|
if (AIClientIsAlive(%cl) && %cl.team != %srcClient.team)
|
|
{
|
|
%clIsCloaked = false;
|
|
if (%cl.player.getInventory("CloakingPack") > 0 && %cl.player.getImageState($BackpackSlot) $= "activate")
|
|
%clIsCloaked = true;
|
|
|
|
//make sure the client can see the enemy
|
|
%hasLOS = %srcClient.hasLOSToClient(%cl);
|
|
%losTime = %srcClient.getClientLOSTime(%cl);
|
|
if (%hasLOS || (%losTime < %losTimeout && AIClientIsAlive(%cl, %losTime + 1000)))
|
|
{
|
|
%testPos = %cl.player.getWorldBoxCenter();
|
|
%distance = %srcClient.getPathDistance(%testPos);
|
|
if (%distance < 0)
|
|
%distance = 32767;
|
|
|
|
//calculate the flag factor
|
|
%flagFactor = (100 - %distance) + (%cl.flagCount * 5);
|
|
|
|
//see if it's the most suitable client...
|
|
if (%flagFactor > %highestFlagFactor && (!%clIsCloaked || %distance < 8))
|
|
{
|
|
%closestClient = %cl;
|
|
%highestFlagFactor = %flagFactor;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return %closestClient;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
function aih()
|
|
{
|
|
exec("scripts/aiHunters.cs");
|
|
}
|
|
|
|
function aiHlist()
|
|
{
|
|
%count = ClientGroup.getCount();
|
|
for (%i = 0; %i < %count; %i++)
|
|
{
|
|
%cl = ClientGroup.getObject(%i);
|
|
if (%cl.isAIControlled())
|
|
error(%cl SPC getTaggedString(%cl.name) SPC "score:" SPC %cl.score SPC "flags:" SPC %cl.flagCount - 1 SPC "capFlags:" SPC %cl.huntersTask.capFlags);
|
|
}
|
|
}
|
|
|