mirror of
https://github.com/Ragora/T2-ACCM.git
synced 2026-01-19 19:04:46 +00:00
912 lines
26 KiB
C#
912 lines
26 KiB
C#
//-----------------------------------//
|
|
// AI SCRIPT FUNCTIONS //
|
|
//-----------------------------------//
|
|
|
|
//first, exec the supporting scripts
|
|
exec("scripts/aiDebug.cs");
|
|
exec("scripts/aiDefaultTasks.cs");
|
|
exec("scripts/aiObjectives.cs");
|
|
exec("scripts/aiInventory.cs");
|
|
exec("scripts/aiChat.cs");
|
|
exec("scripts/aiHumanTasks.cs");
|
|
exec("scripts/aiObjectiveBuilder.cs");
|
|
exec("scripts/aiBotProfiles.cs");
|
|
|
|
$AIModeStop = 0;
|
|
$AIModeWalk = 1;
|
|
$AIModeGainHeight = 2;
|
|
$AIModeExpress = 3;
|
|
$AIModeMountVehicle = 4;
|
|
|
|
$AIClientLOSTimeout = 15000; //how long a client has to remain out of sight of the bot
|
|
//before the bot "can't see" the client anymore...
|
|
$AIClientMinLOSTime = 10000; //how long a bot will search for a client
|
|
|
|
|
|
//-----------------------------------//
|
|
//Objective weights - level 1
|
|
|
|
$AIWeightCapFlag[1] = 5000; //range 5100 to 5320
|
|
$AIWeightKillFlagCarrier[1] = 4800; //range 4800 to 5120
|
|
$AIWeightReturnFlag[1] = 5001; //range 5101 to 5321
|
|
$AIWeightDefendFlag[1] = 3900; //range 4000 to 4220
|
|
$AIWeightGrabFlag[1] = 3850; //range 3950 to 4170
|
|
|
|
$AIWeightDefendFlipFlop[1] = 3900; //range 4000 to 4220
|
|
$AIWeightCaptureFlipFlop[1] = 3850; //range 3850 to 4170
|
|
|
|
$AIWeightAttackGenerator[1] = 3100; //range 3200 to 3520
|
|
$AIWeightRepairGenerator[1] = 3200; //range 3300 to 3620
|
|
$AIWeightDefendGenerator[1] = 3100; //range 3200 to 3420
|
|
|
|
$AIWeightMortarTurret[1] = 3400; //range 3500 to 3600
|
|
$AIWeightLazeObject[1] = 3200; //range 3300 to 3400
|
|
$AIWeightRepairTurret[1] = 3100; //range 3200 to 3420
|
|
|
|
$AIWeightAttackInventory[1] = 2900; //range 2800 to 2920
|
|
$AIWeightRepairInventory[1] = 2900; //range 2800 to 2920
|
|
|
|
$AIWeightEscortOffense[1] = 2900; //range 2800 to 2920
|
|
$AIWeightEscortCapper[1] = 3250; //range 3350 to 3470
|
|
|
|
//used to allow a bot to finish tasks once started.
|
|
$AIWeightContinueDeploying = 4250;
|
|
$AIWeightContinueRepairing = 4250;
|
|
|
|
//Objective weights from human
|
|
$AIWeightHumanIssuedCommand = 4450;
|
|
$AIWeightHumanIssuedEscort = 4425;
|
|
|
|
//Objective weights - level 2
|
|
$AIWeightCapFlag[2] = 0; //only one person can ever cap a flag
|
|
$AIWeightKillFlagCarrier[2] = 4800; //range 4800 to 5020
|
|
$AIWeightReturnFlag[2] = 4100; //range 4200 to 4320
|
|
$AIWeightDefendFlag[2] = 2000; //range 2100 to 2220
|
|
$AIWeightGrabFlag[2] = 2000; //range 2100 to 2220
|
|
|
|
$AIWeightDefendFlipFlop[2] = 2000; //range 2100 to 2220
|
|
$AIWeightDefendFlipFlop[3] = 1500; //range 1600 to 1720
|
|
$AIWeightDefendFlipFlop[4] = 1000; //range 1100 to 1220
|
|
|
|
$AIWeightAttackGenerator[2] = 1600; //range 1700 to 1920
|
|
$AIWeightRepairGenerator[2] = 1600; //range 1700 to 1920
|
|
$AIWeightDefendGenerator[2] = 1500; //range 1600 to 1720
|
|
|
|
$AIWeightAttackInventory[2] = 1400; //range 1500 to 1720
|
|
$AIWeightRepairInventory[2] = 1400; //range 1500 to 1720
|
|
|
|
$AIWeightMortarTurret[2] = 1000; //range 1100 to 1320
|
|
$AIWeightLazeObject[2] = 0; //no need to have more than one targetter
|
|
$AIWeightRepairTurret[2] = 1000; //range 1100 to 1320
|
|
|
|
$AIWeightEscortOffense[2] = 2900; //range 3300 to 3420
|
|
$AIWeightEscortCapper[2] = 3000; //range 3100 to 3220
|
|
|
|
|
|
function AIInit()
|
|
{
|
|
AISlicerInit();
|
|
installNavThreats();
|
|
NavDetectForceFields();
|
|
// ShowFPS();
|
|
|
|
//enable the use of grenades
|
|
$AIDisableGrenades = false;
|
|
$AIDisableChat = false;
|
|
|
|
//create the "objective delete set"
|
|
if(nameToId("AIBombLocationSet") <= 0)
|
|
{
|
|
$AIBombLocationSet = new SimSet("AIBombLocationSet");
|
|
MissionCleanup.add($AIBombLocationSet);
|
|
}
|
|
|
|
//create the Inventory group
|
|
if(nameToId("AIStationInventorySet") <= 0)
|
|
{
|
|
$AIInvStationSet = new SimSet("AIStationInventorySet");
|
|
MissionCleanup.add($AIInvStationSet);
|
|
}
|
|
|
|
//create the Item group
|
|
if (nameToId("AIItemSet") <= 0)
|
|
{
|
|
$AIItemSet = new SimSet("AIItemSet");
|
|
MissionCleanup.add($AIItemSet);
|
|
}
|
|
|
|
//create the Item group
|
|
if (nameToId("AIGrenadeSet") <= 0)
|
|
{
|
|
$AIGrenadeSet = new SimSet("AIGrenadeSet");
|
|
MissionCleanup.add($AIGrenadeSet);
|
|
}
|
|
|
|
//create the weapon group
|
|
if (nameToId("AIWeaponSet") <= 0)
|
|
{
|
|
$AIWeaponSet = new SimSet("AIWeaponSet");
|
|
MissionCleanup.add($AIWeaponSet);
|
|
}
|
|
|
|
//create the deployed turret group
|
|
if (nameToID("AIRemoteTurretSet") <= 0)
|
|
{
|
|
$AIRemoteTurretSet = new SimSet("AIRemoteTurretSet");
|
|
MissionCleanup.add($AIRemoteTurretSet);
|
|
}
|
|
|
|
//create the deployed turret group
|
|
if (nameToID("AIDeployedMineSet") <= 0)
|
|
{
|
|
$AIDeployedMineSet = new SimSet("AIDeployedMineSet");
|
|
MissionCleanup.add($AIDeployedMineSet);
|
|
}
|
|
|
|
//create the deployed turret group
|
|
if (nameToID("AIVehicleSet") <= 0)
|
|
{
|
|
$AIVehicleSet = new SimSet("AIVehicleSet");
|
|
MissionCleanup.add($AIVehicleSet);
|
|
}
|
|
|
|
%missionGroupFolder = nameToID("MissionGroup");
|
|
%missionGroupFolder.AIMissionInit();
|
|
}
|
|
|
|
// this is called at mission load by the specific game type
|
|
function AIInitObjectives(%team, %game)
|
|
{
|
|
%group = nameToID("MissionGroup/Teams/team" @ %team @ "/AIObjectives");
|
|
if(%group < 0)
|
|
return; // opps, there is no Objectives set for this team.
|
|
|
|
// add the grouped objectives to the teams Q
|
|
%count = %group.getCount();
|
|
for (%i = 0; %i < %count; %i++)
|
|
{
|
|
%objective = %group.getObject(%i);
|
|
if (%objective.getClassName() !$= "AIObjective")
|
|
{
|
|
%grpCount = %objective.getCount();
|
|
for (%j = 0; %j < %grpCount; %j++)
|
|
{
|
|
%grpObj = %objective.getObject(%j);
|
|
if (%objective.gameType $= "" || %objective.gameType $= "all")
|
|
%objType = "";
|
|
else
|
|
%objType = %objective.gameType @ "Game";
|
|
|
|
if (%objType $= "" || %objType $= %game.class)
|
|
{
|
|
%grpObj.group = %objective;
|
|
$ObjectiveQ[%team].add(%grpObj);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// add the non-grouped objectives to the teams Q
|
|
%count = %group.getCount();
|
|
for(%i = 0; %i < %count; %i++)
|
|
{
|
|
%objective = %group.getObject(%i);
|
|
|
|
//if the objective is not an "AIObjective", assume it's a group and continue
|
|
if (%objective.getClassName() !$= "AIObjective")
|
|
continue;
|
|
|
|
if (%objective.gameType $= "" || %objective.gameType $= "all")
|
|
%objType = "";
|
|
else
|
|
%objType = %objective.gameType @ "Game";
|
|
|
|
if (%objType $= "" || %objType $= %game.class)
|
|
{
|
|
%objective.group = "";
|
|
$ObjectiveQ[%team].add(%objective);
|
|
}
|
|
}
|
|
|
|
// initialize the objectives
|
|
%count = $ObjectiveQ[%team].getCount();
|
|
for(%i = 0; %i < %count; %i++)
|
|
{
|
|
%objective = $ObjectiveQ[%team].getObject(%i);
|
|
|
|
//clear out any dynamic fields
|
|
%objective.clientLevel1 = "";
|
|
%objective.clientLevel2 = "";
|
|
%objective.clientLevel3 = "";
|
|
%objective.isInvalid = false;
|
|
%objective.repairObjective = "";
|
|
|
|
//set the location, if required
|
|
if (%objective.position !$= "0 0 0")
|
|
%objective.location = %objective.position;
|
|
|
|
// find targeted object ID's
|
|
if(%objective.targetObject !$= "")
|
|
%objective.targetObjectId = NameToId(%objective.targetObject);
|
|
else
|
|
%objective.targetObjectId = -1;
|
|
|
|
if(%objective.targetClientObject !$= "")
|
|
%objective.targetClientId = NameToId(%objective.targetClient);
|
|
else
|
|
%objective.targetClientId = -1;
|
|
|
|
if (%objective.position $= "0 0 0")
|
|
{
|
|
if (%objective.location $= "0 0 0")
|
|
{
|
|
if (%objective.targetObjectId > 0)
|
|
%objective.position = %objective.targetObjectId.position;
|
|
}
|
|
else
|
|
%objective.position = %objective.location;
|
|
}
|
|
}
|
|
|
|
//finally, sort the objectiveQ
|
|
$ObjectiveQ[%team].sortByWeight();
|
|
}
|
|
|
|
//This function is designed to clear out the objective Q's, and clear the task lists from all the AIs
|
|
function AIMissionEnd()
|
|
{
|
|
//disable the AI system
|
|
AISystemEnabled(false);
|
|
|
|
//loop through the client list, and clear the tasks of each bot
|
|
%count = ClientGroup.getCount();
|
|
for (%i = 0; %i < %count; %i++)
|
|
{
|
|
%client = ClientGroup.getObject(%i);
|
|
if (%client.isAIControlled())
|
|
{
|
|
//cancel the respawn thread and the objective thread...
|
|
cancel(%client.respawnThread);
|
|
cancel(%client.objectiveThread);
|
|
|
|
//reset the clients tasks, variables, etc...
|
|
AIUnassignClient(%client);
|
|
%client.stop();
|
|
%client.clearTasks();
|
|
%client.clearStep();
|
|
%client.lastDamageClient = -1;
|
|
%client.lastDamageTurret = -1;
|
|
%client.shouldEngage = -1;
|
|
%client.setEngageTarget(-1);
|
|
%client.setTargetObject(-1);
|
|
%client.pilotVehicle = false;
|
|
%client.defaultTasksAdded = false;
|
|
|
|
//do the nav graph cleanup
|
|
%client.missionCycleCleanup();
|
|
}
|
|
}
|
|
|
|
//clear the objective Q's
|
|
for (%i = 0; %i <= Game.numTeams; %i++)
|
|
{
|
|
if (isObject($ObjectiveQ[%i]))
|
|
{
|
|
$ObjectiveQ[%i].clear();
|
|
$ObjectiveQ[%i].delete();
|
|
}
|
|
$ObjectiveQ[%i] = "";
|
|
}
|
|
|
|
//now delete all the sets used by the AI system...
|
|
if (isObject($AIBombLocationSet))
|
|
$AIBombLocationSet.delete();
|
|
$AIBombLocationSet = "";
|
|
|
|
if (isObject($AIInvStationSet))
|
|
$AIInvStationSet.delete();
|
|
$AIInvStationSet = "";
|
|
|
|
if (isObject($AIItemSet))
|
|
$AIItemSet.delete();
|
|
$AIItemSet = "";
|
|
|
|
if (isObject($AIGrenadeSet))
|
|
$AIGrenadeSet.delete();
|
|
$AIGrenadeSet = "";
|
|
|
|
if (isObject($AIWeaponSet))
|
|
$AIWeaponSet.delete();
|
|
$AIWeaponSet = "";
|
|
|
|
if (isObject($AIRemoteTurretSet))
|
|
$AIRemoteTurretSet.delete();
|
|
$AIRemoteTurretSet = "";
|
|
|
|
if (isObject($AIDeployedMineSet))
|
|
$AIDeployedMineSet.delete();
|
|
$AIDeployedMineSet = "";
|
|
|
|
if (isObject($AIVehicleSet))
|
|
$AIVehicleSet.delete();
|
|
$AIVehicleSet = "";
|
|
}
|
|
|
|
//FUNCTIONS ON EACH OBJECT EXECUTED AT MISSION LOAD TIME
|
|
function SimGroup::AIMissionInit(%this)
|
|
{
|
|
for(%i = 0; %i < %this.getCount(); %i++)
|
|
%this.getObject(%i).AIMissionInit(%this);
|
|
}
|
|
|
|
function GameBase::AIMissionInit(%this)
|
|
{
|
|
%this.getDataBlock().AIMissionInit(%this);
|
|
}
|
|
|
|
function StationInventory::AIMissionInit(%data, %object)
|
|
{
|
|
$AIInvStationSet.add(%object);
|
|
}
|
|
|
|
function Flag::AIMissionInit(%data, %object)
|
|
{
|
|
if (%object.team >= 0)
|
|
$AITeamFlag[%object.team] = %object;
|
|
}
|
|
|
|
function SimObject::AIMissionInit(%this)
|
|
{
|
|
//this function is declared to prevent console error msg spam...
|
|
}
|
|
|
|
function ItemData::AIMissionInit(%data, %object)
|
|
{
|
|
if (isObject($AIItemSet))
|
|
$AIItemSet.add(%object);
|
|
}
|
|
|
|
function AIThrowObject(%object)
|
|
{
|
|
if (isObject($AIItemSet))
|
|
$AIItemSet.add(%object);
|
|
}
|
|
|
|
function AIGrenadeThrown(%object)
|
|
{
|
|
if (isObject($AIItemSet))
|
|
$AIGrenadeSet.add(%object);
|
|
}
|
|
|
|
function AIDeployObject(%client, %object)
|
|
{
|
|
//first, set the object id on the client
|
|
%client.lastDeployedObject = %object;
|
|
|
|
//now see if it was a turret...
|
|
%type = %object.getDataBlock().getName();
|
|
if (%type $= "TurretDeployedFloorIndoor" || %type $= "TurretDeployedWallIndoor" ||
|
|
%type $= "TurretDeployedCeilingIndoor" || %type $= "TurretDeployedOutdoor")
|
|
{
|
|
$AIRemoteTurretSet.add(%object);
|
|
}
|
|
}
|
|
|
|
function AIDeployMine(%object)
|
|
{
|
|
$AIDeployedMineSet.add(%object);
|
|
}
|
|
|
|
function AIVehicleMounted(%vehicle)
|
|
{
|
|
$AIVehicleSet.add(%vehicle);
|
|
}
|
|
|
|
function AICorpseAdded(%corpse)
|
|
{
|
|
if (isObject(%corpse))
|
|
{
|
|
%corpse.isCorpse = true;
|
|
$AIItemSet.add(%corpse);
|
|
}
|
|
}
|
|
|
|
//OTHER UTILITY FUNCTIONS
|
|
|
|
function AIConnection::onAIDrop(%client)
|
|
{
|
|
//make sure we're trying to drop an AI
|
|
if (!isObject(%client) || !%client.isAIControlled())
|
|
return;
|
|
|
|
//clear the ai from any objectives, etc...
|
|
AIUnassignClient(%client);
|
|
%client.clearTasks();
|
|
%client.clearStep();
|
|
%client.defaultTasksAdded = false;
|
|
|
|
//kill the player, which should cause the Game object to perform whatever cleanup is required.
|
|
if (isObject(%client.player))
|
|
%client.player.scriptKill(0);
|
|
|
|
//do the nav graph cleanup
|
|
%client.missionCycleCleanup();
|
|
}
|
|
|
|
function AIConnection::endMission(%client)
|
|
{
|
|
//cancel the respawn thread, and spawn them manually
|
|
cancel(%client.respawnThread);
|
|
cancel(%client.objectiveThread);
|
|
}
|
|
|
|
function AIConnection::startMission(%client)
|
|
{
|
|
//assign the team
|
|
if (%client.team <= 0)
|
|
Game.assignClientTeam(%client);
|
|
|
|
//set the client's sensor group...
|
|
setTargetSensorGroup( %client.target, %client.team );
|
|
%client.setSensorGroup( %client.team );
|
|
|
|
//sends a message so everyone know the bot is in the game...
|
|
Game.AIHasJoined(%client);
|
|
%client.matchStartReady = true;
|
|
|
|
//spawn the bot...
|
|
onAIRespawn(%client);
|
|
}
|
|
|
|
function AIConnection::onAIConnect(%client, %name, %team, %skill, %offense, %voice, %voicePitch)
|
|
{
|
|
// Sex/Race defaults
|
|
%client.sex = "Male";
|
|
%client.race = "Human";
|
|
%client.armor = "Light";
|
|
|
|
//setup the voice and voicePitch
|
|
if (%voice $= "")
|
|
%voice = "Bot1";
|
|
%client.voice = %voice;
|
|
%client.voiceTag = addTaggedString(%voice);
|
|
|
|
if (%voicePitch $= "" || %voicePitch < 0.5 || %voicePitch > 2.0)
|
|
%voicePitch = 1.0;
|
|
%client.voicePitch = %voicePitch;
|
|
|
|
%client.name = addTaggedString( "\cp\c9" @ %name @ "\co" );
|
|
%client.nameBase = %name;
|
|
|
|
echo(%client.name);
|
|
echo("CADD: " @ %client @ " " @ %client.getAddress());
|
|
$HostGamePlayerCount++;
|
|
|
|
//set the initial team - Game.assignClientTeam() should be called later on...
|
|
%client.team = %team;
|
|
if ( %client.team & 1 )
|
|
%client.skin = addTaggedString( "basebot" );
|
|
else
|
|
%client.skin = addTaggedString( "basebbot" );
|
|
|
|
//setup the target for use with the sensor net, etc...
|
|
%client.target = allocClientTarget(%client, %client.name, %client.skin, %client.voiceTag, '_ClientConnection', 0, 0, %client.voicePitch);
|
|
|
|
//i need to send a "silent" version of this for single player but still use the callback -jr`
|
|
echo(%name SPC getTaggedString(%name));
|
|
if($currentMissionType $= "SinglePlayer")
|
|
messageAllExcept(%client, -1, 'MsgClientJoin', "", %name, %client, %client.target, true);
|
|
else if(%name !$= "_AISent") // Eolk Sentinels
|
|
messageAllExcept(%client, -1, 'MsgClientJoin', '\c1%1 joined the game.', %name, %client, %client.target, true);
|
|
|
|
//assign the skill
|
|
%client.setSkillLevel(%skill);
|
|
|
|
//assign the affinity
|
|
%client.offense = %offense;
|
|
|
|
//clear any flags
|
|
%client.stop(); // this will clear the players move state
|
|
%client.clearStep();
|
|
%client.lastDamageClient = -1;
|
|
%client.lastDamageTurret = -1;
|
|
%client.setEngageTarget(-1);
|
|
%client.setTargetObject(-1);
|
|
%client.objective = "";
|
|
|
|
//clear the defaulttasks flag
|
|
%client.defaultTasksAdded = false;
|
|
|
|
//if the mission is already running, spawn the bot
|
|
if ($missionRunning)
|
|
%client.startMission();
|
|
}
|
|
|
|
// This routes through C++ code so profiler can register it. Also, the console function
|
|
// ProfilePatch1() tracks time spent (at MS resolution), # calls, average time per call.
|
|
// See console variables $patch1Total (MS so far in routine), $patch1Avg (average MS
|
|
// per call), and $patch1Calls (# of calls).
|
|
function patchForTimeTest(%client)
|
|
{
|
|
if( isObject( Game ) )
|
|
Game.AIChooseGameObjective(%client);
|
|
}
|
|
|
|
function AIReassessObjective(%client)
|
|
{
|
|
ProfilePatch1(patchForTimeTest, %client);
|
|
// Game.AIChooseGameObjective(%client);
|
|
%client.objectiveThread = schedule(5000, %client, "AIReassessObjective", %client);
|
|
}
|
|
|
|
function onAIRespawn(%client)
|
|
{
|
|
%markerObj = Game.pickPlayerSpawn(%client, true);
|
|
Game.createPlayer(%client, %markerObj);
|
|
|
|
//make sure the player object is the AI's control object - even during the mission warmup time
|
|
//the function AISystemEnabled(true/false) will control whether they actually move...
|
|
%client.setControlObject(%client.player);
|
|
|
|
if (%client.objective)
|
|
error("ERROR!!! " @ %client @ " is still assigned to objective: " @ %client.objective);
|
|
|
|
//clear the objective and choose a new one
|
|
AIUnassignClient(%client);
|
|
%client.stop();
|
|
%client.clearStep();
|
|
%client.lastDamageClient = -1;
|
|
%client.lastDamageTurret = -1;
|
|
%client.shouldEngage = -1;
|
|
%client.setEngageTarget(-1);
|
|
%client.setTargetObject(-1);
|
|
%client.pilotVehicle = false;
|
|
|
|
//set the spawn time
|
|
%client.spawnTime = getSimTime();
|
|
%client.respawnThread = "";
|
|
|
|
//timeslice the objective reassessment for the bots
|
|
if (!isEventPending(%client.objectiveThread))
|
|
{
|
|
%curTime = getSimTime();
|
|
%remainder = %curTime % 5000;
|
|
%schedTime = $AITimeSliceReassess - %remainder;
|
|
if (%schedTime <= 0)
|
|
%schedTime += 5000;
|
|
%client.objectiveThread = schedule(%schedTime, %client, "AIReassessObjective", %client);
|
|
|
|
//set the next time slice "slot"
|
|
$AITimeSliceReassess += 300;
|
|
if ($AITimeSliceReassess > 5000)
|
|
$AITimeSliceReassess -= 5000;
|
|
}
|
|
|
|
//call the game specific spawn function
|
|
Game.onAIRespawn(%client);
|
|
}
|
|
|
|
function AIClientIsAlive(%client, %duration)
|
|
{
|
|
if(%client < 0 || %client.player <= 0)
|
|
return false;
|
|
if (isObject(%client.player))
|
|
{
|
|
%state = %client.player.getState();
|
|
if (%state !$= "Dead" && %state !$= "" && (%duration $= "" || getSimTime() - %client.spawnTime >= %duration))
|
|
return true;
|
|
else
|
|
return false;
|
|
}
|
|
else
|
|
return false;
|
|
}
|
|
|
|
//------------------------------
|
|
function AIFindClosestEnemy(%srcClient, %radius, %losTimeout)
|
|
{
|
|
//see if there's an enemy near our defense location...
|
|
if (isObject(%srcClient.player))
|
|
%srcLocation = %srcClient.player.getWorldBoxCenter();
|
|
else
|
|
%srcLocation = "0 0 0";
|
|
return AIFindClosestEnemyToLoc(%srcClient, %srcLocation, %radius, %losTimeout, false, true);
|
|
}
|
|
|
|
function AIFindClosestEnemyToLoc(%srcClient, %srcLocation, %radius, %losTimeout, %ignoreLOS, %distFromClient)
|
|
{
|
|
if (%ignoreLOS $= "")
|
|
%ignoreLOS = false;
|
|
if (%distFromClient $= "")
|
|
%distFromClient = false;
|
|
|
|
%count = ClientGroup.getCount();
|
|
%closestClient = -1;
|
|
%closestDistance = 32767;
|
|
for(%i = 0; %i < %count; %i++)
|
|
{
|
|
%cl = ClientGroup.getObject(%i);
|
|
|
|
//make sure we find someone who's alive
|
|
if (AIClientIsAlive(%cl) && %cl.team != %srcClient.team)
|
|
{
|
|
%clIsCloaked = !isTargetVisible(%cl.target, %srcClient.getSensorGroup());
|
|
|
|
//make sure the client can see the enemy
|
|
%hasLOS = %srcClient.hasLOSToClient(%cl);
|
|
%losTime = %srcClient.getClientLOSTime(%cl);
|
|
if (%ignoreLOS || %hasLOS || (%losTime < %losTimeout && AIClientIsAlive(%cl, %losTime + 1000)))
|
|
{
|
|
%testPos = %cl.player.getWorldBoxCenter();
|
|
if (%distFromClient)
|
|
%distance = %srcClient.getPathDistance(%testPos);
|
|
else
|
|
%distance = AIGetPathDistance(%srcLocation, %testPos);
|
|
if (%distance > 0 && (%radius < 0 || %distance < %radius) && %distance < %closestDistance && (!%clIsCloaked || %distance < 8))
|
|
{
|
|
%closestClient = %cl;
|
|
%closestDistance = %distance;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return %closestClient SPC %closestDistance;
|
|
}
|
|
|
|
function AIFindClosestEnemyPilot(%client, %radius, %losTimeout)
|
|
{
|
|
//loop through the vehicle set, looking for pilotted vehicles...
|
|
%closestPilot = -1;
|
|
%closestDist = %radius;
|
|
%count = $AIVehicleSet.getCount();
|
|
for (%i = 0; %i < %count; %i++)
|
|
{
|
|
//first, make sure the vehicle is mounted by pilotted
|
|
%vehicle = $AIVehicleSet.getObject(%i);
|
|
%pilot = %vehicle.getMountNodeObject(0);
|
|
if (%pilot <= 0 || !AIClientIsAlive(%pilot.client))
|
|
continue;
|
|
|
|
//make sure the pilot is an enemy
|
|
if (%pilot.client.team == %client.team)
|
|
continue;
|
|
|
|
//see if the pilot has been seen by the client
|
|
%hasLOS = %client.hasLOSToClient(%pilot.client);
|
|
%losTime = %client.getClientLOSTime(%pilot.client);
|
|
if (%hasLOS || (%losTime < %losTimeout && AIClientIsAlive(%pilot.client, %losTime + 1000)))
|
|
{
|
|
//see if it's the closest
|
|
%clientPos = %client.player.getWorldBoxCenter();
|
|
%pilotPos = %pilot.getWorldBoxCenter();
|
|
%dist = VectorDist(%clientPos, %pilotPos);
|
|
if (%dist < %closestDist)
|
|
{
|
|
%closestPilot = %pilot.client;
|
|
%closestDist = %dist;
|
|
}
|
|
}
|
|
}
|
|
|
|
return %closestPilot SPC %closestDist;
|
|
}
|
|
|
|
function AIFindAIClientInView(%srcClient, %team, %radius)
|
|
{
|
|
//make sure the player is alive
|
|
if (! AIClientIsAlive(%srcClient))
|
|
return -1;
|
|
|
|
//get various info about the player's eye
|
|
%srcEyeTransform = %srcClient.player.getEyeTransform();
|
|
%srcEyePoint = firstWord(%srcEyeTransform) @ " " @ getWord(%srcEyeTransform, 1) @ " " @ getWord(%srcEyeTransform, 2);
|
|
%srcEyeVector = VectorNormalize(%srcClient.player.getEyeVector());
|
|
|
|
//see if there's an enemy near our defense location...
|
|
%count = ClientGroup.getCount();
|
|
%viewedClient = -1;
|
|
%clientDot = -1;
|
|
for(%i = 0; %i < %count; %i++)
|
|
{
|
|
%cl = ClientGroup.getObject(%i);
|
|
|
|
//make sure we find an AI who's alive and not the srcClient
|
|
if (%cl != %srcClient && AIClientIsAlive(%cl) && %cl.isAIControlled() && (%team < 0 || %cl.team == %team))
|
|
{
|
|
//make sure the player is within range
|
|
%clPos = %cl.player.getWorldBoxCenter();
|
|
%distance = VectorDist(%clPos, %srcEyePoint);
|
|
if (%radius <= 0 || %distance <= %radius)
|
|
{
|
|
//create the vector from the srcClient to the client
|
|
%clVector = VectorNormalize(VectorSub(%clPos, %srcEyePoint));
|
|
|
|
//see if the dot product is greater than our current, and greater than 0.6
|
|
%dot = VectorDot(%clVector, %srcEyeVector);
|
|
|
|
if (%dot > 0.6 && %dot > %clientDot)
|
|
{
|
|
%viewedClient = %cl;
|
|
%clientDot = %dot;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return %viewedClient;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//AI VEHICLE FUNCTIONS
|
|
|
|
function Armor::AIonMount(%this, %obj, %vehicle, %node)
|
|
{
|
|
//set the client var...
|
|
%client = %obj.client;
|
|
%client.turretMounted = -1;
|
|
|
|
//make sure the AI was *supposed* to mount the vehicle
|
|
if (!%client.isMountingVehicle())
|
|
{
|
|
AIDisembarkVehicle(%client);
|
|
return;
|
|
}
|
|
|
|
//get the vehicle's pilot
|
|
%pilot = %vehicle.getMountNodeObject(0);
|
|
|
|
//make sure the bot is in node 0 if'f the bot is piloting the vehicle
|
|
if ((%node == 0 && !%client.pilotVehicle) || (%node > 0 && %client.pilotVehicle))
|
|
{
|
|
AIDisembarkVehicle(%client);
|
|
return;
|
|
}
|
|
|
|
//make sure the bot didn't is on the same team as the pilot
|
|
if (%pilot > 0 && isObject(%pilot) && %pilot.client.team != %client.team)
|
|
{
|
|
AIDisembarkVehicle(%client);
|
|
return;
|
|
}
|
|
|
|
//if we're supposed to pilot the vehicle, set the control object
|
|
if (%client.pilotVehicle)
|
|
%client.setControlObject(%vehicle);
|
|
|
|
//each vehicle may be built differently...
|
|
if (%vehicle.getDataBlock().getName() $= "AssaultVehicle")
|
|
{
|
|
//node 1 is this vehicle's turret seat
|
|
if (%node == 1)
|
|
{
|
|
%turret = %vehicle.getMountNodeObject(10);
|
|
%skill = %client.getSkillLevel();
|
|
%turret.setSkill(%skill);
|
|
%client.turretMounted = %turret;
|
|
%turret.setAutoFire(true);
|
|
}
|
|
}
|
|
|
|
else if (%vehicle.getDataBlock().getName() $= "BomberFlyer")
|
|
{
|
|
//node 1 is this vehicle's turret seat
|
|
if (%node == 1)
|
|
{
|
|
%turret = %vehicle.getMountNodeObject(10);
|
|
%skill = %client.getSkillLevel();
|
|
%turret.setSkill(%skill);
|
|
%client.turretMounted = %turret;
|
|
%client.setTurretMounted(%turret);
|
|
%turret.setAutoFire(true);
|
|
}
|
|
}
|
|
}
|
|
|
|
function Armor::AIonUnMount(%this, %obj, %vehicle, %node)
|
|
{
|
|
//get the client var
|
|
%client = %obj.client;
|
|
|
|
//reset the control object
|
|
if (%client.pilotVehicle)
|
|
%client.setControlObject(%client.player);
|
|
%client.pilotVehicle = false;
|
|
|
|
//if the client had mounted a turret, turn the turret back off
|
|
if (%client.turretMounted > 0)
|
|
%client.turretMounted.setAutoFire(false);
|
|
%client.turretMounted = -1;
|
|
%client.setTurretMounted(-1);
|
|
|
|
// reset the turret skill level
|
|
if(%vehicle.getDataBlock().getName() $= "AssaultVehicle")
|
|
if (%node == 1)
|
|
%vehicle.getMountNodeObject(10).setSkill(1.0);
|
|
|
|
if(%vehicle.getDataBlock().getName() $= "BomberFlyer")
|
|
if(%node == 1)
|
|
%vehicle.getMountNodeObject(10).setSkill(1.0);
|
|
}
|
|
|
|
function AIDisembarkVehicle(%client)
|
|
{
|
|
if (%client.player.isMounted())
|
|
{
|
|
if (%client.pilotVehicle)
|
|
%client.setControlObject(%client.player);
|
|
%client.pressJump();
|
|
}
|
|
}
|
|
|
|
function AIProcessVehicle(%client)
|
|
{
|
|
//see if we're mounted on a turret, and if that turret has a target
|
|
if (%client.turretMounted > 0)
|
|
{
|
|
%turretDB = %client.turretMounted.getDataBlock();
|
|
|
|
//see if we're in a bomber close to a bomb site...
|
|
if (%turretDB.getName() $= "BomberTurret")
|
|
{
|
|
%clientPos = getWords(%client.player.position, 0, 1) @ " 0";
|
|
%found = false;
|
|
%count = $AIBombLocationSet.getCount();
|
|
for (%i = 0; %i < %count; %i++)
|
|
{
|
|
%bombObj = $AIBombLocationSet.getObject(%i);
|
|
%bombLocation = %bombObj.location;
|
|
|
|
//make sure the objective was issued by someone in the vehicle
|
|
if (%bombObj.issuedByClientId.vehicleMounted == %client.vehicleMounted)
|
|
{
|
|
//find out where the bomb is going to drop... first, how high up are we...
|
|
%bombLocation2D = getWord(%bombLocation, 0) SPC getWord(%bombLocation, 1) SPC "0";
|
|
%height = getWord(%client.vehicleMounted.position, 2) - getWord(%bombLocation, 2);
|
|
|
|
//find out how long it'll take the bomb to fall that far...
|
|
//assume no initial velocity in the Z axis...
|
|
%timeToFall = mSqrt((2.0 * %height) / 9.81);
|
|
|
|
//how fast is the vehicle moving in the XY plane...
|
|
%myLocation = %client.vehicleMounted.position;
|
|
%myLocation2D = getWord(%myLocation, 0) SPC getWord(%myLocation, 1) SPC "0";
|
|
%vel = %client.vehicleMounted.getVelocity();
|
|
%vel2D = getWord(%vel, 0) SPC getWord(%vel, 1) SPC "0";
|
|
|
|
%bombImpact2D = VectorAdd(%myLocation2D, VectorScale(%vel2D, %timeToFall));
|
|
|
|
//see if the bomb inpact position is within 20m of the desired bomb site...
|
|
%distToBombsite2D = VectorDist(%bombImpact2D, %bombLocation2D);
|
|
if (%height > 20 && %distToBombsite2D < 25)
|
|
{
|
|
%found = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
//see if we found a bomb site
|
|
if (%found)
|
|
{
|
|
%client.turretMounted.selectedWeapon = 2;
|
|
%turretDB.onTrigger(%client.turretMounted, 0, true);
|
|
return;
|
|
}
|
|
}
|
|
|
|
//we're not bombing, make sure we have the regular weapon selected
|
|
%client.turretMounted.selectedWeapon = 1;
|
|
if (isObject(%client.turretMounted.getTargetObject()))
|
|
%turretDB.onTrigger(%client.turretMounted, 0, true);
|
|
else
|
|
%turretDB.onTrigger(%client.turretMounted, 0, false);
|
|
}
|
|
}
|
|
|
|
function AIPilotVehicle(%client)
|
|
{
|
|
//this is not very well supported, but someone will find a use for this function...
|
|
}
|