//-----------------------------------// // 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) { $AIItemSet.add(%object); } function AIThrowObject(%object) { $AIItemSet.add(%object); } function AIGrenadeThrown(%object) { $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` if($currentMissionType $= "SinglePlayer") messageAllExcept(%client, -1, 'MsgClientJoin', "", %name, %client, %client.target, true); else 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... }