$AIs = 0; $AIdied = 0; $AIweapon = 0; new SimSet(AIGroup); //************************************************************************************** // When AIPlayer is added, start the intellect loop and set any initial orders //************************************************************************************** function AIPlayer::AILoop(%this) { %curAI = AIGroup.getObject($AIs-1); if($AIs % 2) %curAI.setOrders("patrol", %curAI.getPosition(), %curAI.sightMaxRange); else %curAI.setOrders("scout", %curAI.getPosition(), %curAI.sightMaxRange); %curAI.intellectLoop(); } //************************************************************************************** // The intellect loop. This is the core function of the aiBrain // This function continually loops every 500ms to determine what the aiplayers // next action is going to be //************************************************************************************** function AIPlayer::intellectLoop(%this) { //initiate new loop in 500ms cancel(%this.AILoop); %this.AILoop = %this.schedule(500, "intellectLoop"); //Check for enemy (Can we sense an enemy?) %enemyFound = %this.getNextTarget(); //Check if we are blocked (This should be improved, VERY basic pathfinding) %this.blockDestinationCheck(); //We found an enemy....lets get em! if(isObject(%this.attackTarget) && %enemyFound) { %dist = %this.checkDisttoTarget(%this.attackTarget); if(%dist < 150) //within { %this.setAimLocation(%this.attackTarget.getPosition()); //enter attack mode %this.inTransit = false; //************************************************************************************** if($AIweapon != 0)//they don't have the rocket launcher { %this.setAimObject(this.attackTarget,"0 0 50"); //3 is an adjustment for height } else //they have the rocket launcher, so we decrease aim reliability { //*** I increased the reliabilty when I added the jeep,was 200 for z and 100 for x and y, efa (2/25/08) %aim_z = 1 + ((getRandom(100)+1)/100); //make aim randomly pitch, elevation, (efa) 2/13/08 %aim_y = (getRandom(50)+1)/100; //make aim randomly error left-to-right, (efa) 2/13/08 %aim_x = (getRandom(50)+1)/100; //make aim randomly error up-to-down, (efa) 2/13/08 %aim = %aim_x SPC %aim_y SPC %aim_z; %this.setAimObject(this.attackTarget,%aim); } //************************************************************************************** %this.delayed = !%this.delayed; //toggle delayed status %this.fire(%this.attackTarget,true); // ?? we need to delay until we turn around... (efa) 6/02/08 } else { //close on target %pos = %this.getPosition(); %newVec = %this.checkVectortoTarget(%this.attackTarget); %normVec = VectorNormalize(%newVec); %dist = %this.checkDisttoTarget(%this.attackTarget); %newVec = VectorScale(%newVec,%dist-4); %newPos = VectorAdd(%pos,%newVec); %this.setWaypoint(%newPos, true); } } else if(!%this.inTransit) //No enemy.....continue with orders { switch$(%this.currentOrder) { case "scout": %moveVec = (getRandom()*60)-30 SPC (getRandom()*60)-30 SPC 0; %pos = %this.getPosition(); %newPos = VectorAdd(%pos,%moveVec); %this.setAimLocation(%newPos); %this.setWaypoint(%newPos); case "patrol": %origin = %this.currentData[0]; %range = %this.currentData[1]; %xpos = getRandom()*%range*2-%range + getWord(%origin,0); %ypos = getRandom()*%range*2-%range + getWord(%origin,1); %newPos = %xpos SPC %ypos SPC getWord(%origin, 2); %this.setAimLocation(%newPos); %this.setWaypoint(%newPos); } } } //------------------------------------------------------------------------------- // Movement functions //------------------------------------------------------------------------------- //************************************************************************************** // Set a new waypoint for the bot to go to // If the bot already has a destination this will be ignored // unless the force variable is set to true //************************************************************************************** function AIPlayer::setWaypoint(%this, %position, %force) { if(%this.inTransit && !%force) //already moving, so just return return; %this.setMoveDestination(%position); //%this.move(); //probably, no longer needed... %this.inTransit = true; } //************************************************************************************** // This function will move a player in a direction of the vector // from his current location. y value is forward/back and x value is left/right //************************************************************************************** function AIPlayer::moveOffset(%this, %forwardDist, %sideDist) { if(isObject(%this.attackTarget)) %vec = VectorNormalize(%this.checkVectortoTarget(%this.attackTarget)); else %vec = VectorNormalize(%this.getEyeVector()); %adjVec = %forwardDist SPC %sideDist SPC "0"; %forVec = VectorScale(%vec, %forwardDist); %pos = %this.getPosition(); %newPos = VectorAdd(%pos,%forVec); %this.setWaypoint(%newPos); } //************************************************************************************** // Player is stuck, must be no longer in transit //************************************************************************************** function AIPlayer::onStuck(%this,%obj) { %this.inTransit = false; } //************************************************************************************** // Player reached destination, continue with orders. //************************************************************************************** function AIPlayer::onReachDestination(%this,%obj) { if(%this.currentOrder $= "scout" || %this.currentOrder $= "patrol") { %this.scoutSearchPause(); } else %this.inTransit = false; } //************************************************************************************** // Scout/Patrol search. Player will stop and look in 5 // directions before continuing to next waypoint //************************************************************************************** function AIPlayer::scoutSearchPause(%this) { %this.scoutCheck++; switch(%this.scoutCheck) { case 1: %this.setAimLocation("0 0 0"); //look current facing direction case 2: %this.setAimLocation("99999 0 0"); //look east case 3: %this.setAimLocation("-99999 0 0"); //look west case 4: %this.setAimLocation("0 99999 0"); // look north case 5: %this.setAimLocation("0 -99999 0"); //look south case 6: %this.scoutCheck = 0; %this.inTransit = false; } } //************************************************************************************** // Very basic pathfinding. (Very crappy) // You should definetly improve on this //************************************************************************************** function AIPlayer::blockDestinationCheck(%this) { if(!%this.inTransit) //not moving. no need for check, just return. { return false; } if(VectorDist(%this.getPosition(), %this.lastBlockPosition) < 1) //made no progress, must be blocked { %this.inTransit = false; %this.lastBlockPosition = %this.getPosition(); /* if(%this.inVehicle == true) { echo("blockDestinationCheck()::bot is blocked and inVehicle"); } else { echo("blockDestinationCheck()::bot is blocked..."); } */ return true; } %this.lastBlockPosition = %this.getPosition(); return false; } //----------------------------------------------------------------------- // Sight / Detection Functions //----------------------------------------------------------------------- //************************************************************************************** // Check if the bot can visually see the target (LOS = Line Of Sight) // %previous means that it was a previous target (already seen) // hence has more chance of detection //************************************************************************************** function AIPlayer::checkLOStoTarget(%this, %obj, %previous) { if(%this.getShapeName() $= %obj.getShapeName())//don't attack your own-kind (efa) 5/13/08 { return false; } %sightRange = %this.sightMaxRange; //echo("abiBots.cs::checkLOStoTarget()::sightRange =" SPC %sightRange); %eyeTrans = %this.getEyeTransform(); %eyeEnd = %obj.getEyeTransform(); %searchResult = containerRayCast(%eyeTrans, %eyeEnd, $TypeMasks::PlayerObjectType | $TypeMasks::TerrainObjectType | $TypeMasks::InteriorObjectType, %this); //echo("abiBots.cs::checkLOStoTarget()::%searchResult =" SPC %searchResult); if(!%searchResult) return(false); %foundObject = getword(%searchResult,0); //echo("abiBots.cs::checkLOStoTarget()::%foundObject =" SPC %foundObject); //if(%this.getType() & $TypeMasks::PlayerObjectType) if(%foundObject.getType() & $TypeMasks::PlayerObjectType) { %dist = %this.checkDisttoTarget(%obj); %angle = %this.check2DAngletoTarget(%obj); if((%angle > %this.sightArcRange && %angle >= 0) || (%angle < 360-%this.sightArcRange && %angle > 180)) return false; else if(%dist <= %this.sightMinRange) return true; else { %testperc = (%dist-%this.sightMinRange)/(%this.sightMaxRange-%this.sightMinRange); %diff = 1-%this.sightMaxAbility; %testval = (%testperc * %diff) + %this.sightMaxAbility; %testval = %previous ? %testval+0.30 : %testval; if(getRandom() <= %testval) return true; else return false; } } else return false; } //************************************************************************************** // Check if the bot can sense the target via hearing/sixth sense // %previous means that it was a previous target (already seen) // hence has more chance of detection //************************************************************************************** function AIPlayer::checkSensetoTarget(%this, %obj, %previous) { //echo("abiBots.cs::checkSensetoTarget()"); %radius = %this.sixthSenseRange; %position = %this.getPosition(); InitContainerRadiusSearch(%position, %radius, $TypeMasks::PlayerObjectType); while ((%targetObject = containerSearchNext()) != 0) { if(%targetObject != %obj) continue; if(%targetObject.getState$="Dead") continue; %testval = %previous ? %this.sixthSenseAbility+0.30 : %this.sixthSenseAbility; if(getRandom() <= %testval) return true; else return false; } return false; } //************************************************************************************** // Return distance to target //************************************************************************************** function AIPlayer::checkDisttoTarget(%this, %obj) { %newdist = VectorDist(%obj.getPosition(), %this.getPosition()); return %newdist; } //************************************************************************************** // Return vector to target //************************************************************************************** function AIPlayer::checkVectortoTarget(%this, %obj) { %newVec = VectorSub(%obj.getPosition(), %this.getPosition()); return %newVec; } //************************************************************************************** // Return the angle of a vector in relation to world origin //************************************************************************************** function AIPlayer::getAngleofVector(%this, %vec) { %vector = VectorNormalize(%vec); %vecx = getWord(%vector,0); %vecy = getWord(%vector,1); if(%vecx >= 0 && %vecy >= 0) %quad = 1; else if(%vecx >= 0 && %vecy < 0) %quad = 2; else if(%vecx < 0 && %vecy < 0) %quad = 3; else %quad = 4; %angle = mATan(%vecy/%vecx, -1); %degangle = mRadToDeg(%angle); switch(%quad) { case 1: %angle = %degangle-90; case 2: %angle = %degangle+270; case 3: %angle = %degangle+90; case 4: %angle = %degangle+450; } return %degangle; } //************************************************************************************** // Return angle from bots eye vector to target //************************************************************************************** function AIPlayer::check2DAngletoTarget(%this, %obj) { %eyeVec = VectorNormalize(%this.getEyeVector()); %eyeangle = %this.getAngleofVector(%eyeVec); %posVec = VectorSub(%obj.getPosition(), %this.getPosition()); %posangle = %this.getAngleofVector(%posVec); %angle = %posangle - %eyeAngle; %angle = %angle ? %angle : %angle * -1; return %angle; } //************************************************************************************** // Get next target to attack //************************************************************************************** function AIPlayer::getNextTarget(%this) { //Check if old target still exists if(!isObject(%this.attackTarget) || %this.attackTarget.getState $= "Dead") %this.attackTarget = 0; //check if we can still sense old target (if applicable) if(%this.attackTarget) { %sense = false; %sense = %this.checkLOStoTarget(%this.attackTarget, true); if(!%sense) %this.checkSensetoTarget(%this.attackTarget, true); %targetSearch = %sense ? false : true; //If old target is more than 10m away pick closer target if applicable if(!%targetSearch) { %dist = VectorDist(%this.attackTarget.getPosition(), %this.getPosition()); //echo("aiBots.cs::getNextTarget():: %dist =" SPC %dist); if(%dist > 10)//was 10 (efa) 3/22/08 %targetSearch = true; } } //Were we hit by something else that is significantly closer? If so attack that! if(isObject(%this.lastDamage)) { echo("EFA:: I got hit !!!"); } if(isObject(%this.lastDamage) && %this.lastDamage.getState!$="Dead") { echo("aiBots.cs::getNextTarget():: this.attackTarget ... hit by something"); %newdist = VectorDist(%this.lastDamage.getPosition(), %this.getPosition()); if(%newdist+2 < %dist) { %this.attackTarget = %this.lastDamage; return true; } } // We know our target, lets bail on search if(!%targetSearch && isObject(%this.attackTarget) && %this.attackTarget.getState !$= "Dead") {//echo("aiBots.cs::getNextTarget():: We know our target, lets bail on search"); return true; } // If we have to search, lets do it. if(!isObject(%this.attackTarget) || %this.attackTarget.getState $= "Dead" || %targetSearch) {//echo("aiBots.cs::getNextTarget():: Searching for a target"); %this.attackTarget = 0; if(!%this.attackTarget) { %radius = %this.sightMaxRange; %selDist = %radius+1; %position = %this.getPosition(); InitContainerRadiusSearch(%position, %radius, $TypeMasks::PlayerObjectType); while ((%targetObject = containerSearchNext()) != 0) { if(%targetObject == %this) //pointing at another bot, continue search continue; if(%targetObject.getState$="Dead") continue; %sense = false; %sense = %this.checkLOStoTarget(%targetObject, true); if(!%sense) %this.checkSensetoTarget(%targetObject, true); if(!%sense) continue; %dist = containerSearchCurrRadiusDist(); if(%dist < %selDist) { %this.attackTarget = %targetObject; %selDist = %dist; } } } } if(!%this.attackTarget) return false; else return true; } //************************************************************************************** // This function stores the current order and order information // We have a lot more variables than we need currently, for future use //************************************************************************************** function AIPlayer::setOrders(%this, %order, %a, %b, %c, %d, %e, %f) { //echo("aiBots.cs::setOrders()"); %this.currentOrder = %order; %this.currentData[0] = %a; %this.currentData[1] = %b; %this.currentData[2] = %c; %this.currentData[3] = %d; %this.currentData[4] = %e; %this.currentData[5] = %f; } //******************************************************************************************** // spawn:: //******************************************************************************************** function AIPlayer::spawn(%name,%spawnPoint) { if($AIweapon == 0) { // Create the Orc with a Rocket Launcher %player = new AiPlayer(){ dataBlock = PlayerRocketOrc; //added (efa) 2/02/08 path = ""; index = $AIs; sixthSenseRange = 10; //range that bot is likely to detect enemy from sound/sixth sense sixthSenseAbility = 0.33; //(was 20) possibility of detection (out of 1) sightArcRange = 50; //Cone of sight to the left and right of the eye (degrees) sightMinRange = 50; //(was 30)100% chance of seeing up to this range sightMaxRange = 200; //(was 80) maximum range that the creature can see sightMaxAbility = 0.20; //possibility of seeing the target at max sight range(out of 1) inVehicle = false; //are we in a vehicle delayed = false; }; } else { // Create the Orc with a Crossbow %player = new AiPlayer(){ dataBlock = playerCrossbowOrc; //dataBlock = PlayerPistolOrc; path = ""; index = $AIs; sixthSenseRange = 10; //range that bot is likely to detect enemy from sound/sixth sense sixthSenseAbility = 0.33; //possibility of detection (out of 1) sightArcRange = 50; //Cone of sight to the left and right of the eye (degrees) sightMinRange = 50; //100% chance of seeing up to this range sightMaxRange = 200; //maximum range that the creature can see sightMaxAbility = 0.20; //possibility of seeing the target at max sight range(out of 1) inVehicle = false; //are we in a vehicle }; } %name = "Bot"; MissionCleanup.add(%player); %player.setShapeName(%name); %player.setTransform(%spawnPoint); //echo("BBBB %name = " @ %name); AIGroup.add(%player); // Player setup //set up the slot based inventory if($AIweapon == 0) %player.setInventory(slot1,$SLOTROCKETLAUNCHER); // first weapon, the one displayed and ready to use. else %player.setInventory(slot1,$SLOTCROSSBOW); // first weapon, the one displayed and ready to use. //%player.setInventory(slot1,$SLOTPISTOL); // first weapon, the one displayed and ready to use. %player.setInventory(slot2,$SLOTRIFLE); // second weapon, the one hidden, in reserve. if($AIweapon == 0) { %player.setInventory(RocketLauncher,1); %player.setInventory(RocketAmmo,100); %player.mountImage(RocketLauncherImage,0); } else { %player.setInventory(Crossbow,1); %player.setInventory(CrossbowAmmo,100); %player.mountImage(CrossbowImage,0); } //%player.setInventory(slot3,$SLOTGRENADE); // reserver weapon (Grenade???) // %player.setInventory(GrenadeAmmo,50); return %player; } //******************************************************************************************** //******************************************************************************************** function AIPlayer::singleShot(%this) { // The shooting delay is used to pulse the trigger if(%this.delayed $= false) { %this.setImageTrigger(0,true); %this.setImageTrigger(0,false); } } //******************************************************************************************** //******************************************************************************************** function AIPlayer::wait(%this,%time) { %this.schedule(%time * 1000,"nextTask"); } //******************************************************************************************** //******************************************************************************************** function AIPlayer::done(%this,%time) { %this.schedule(0,"delete"); } //******************************************************************************************** //******************************************************************************************** function AIPlayer::fire(%this,%bool) { if (%bool) { cancel(%this.trigger); %this.singleShot(); } else cancel(%this.trigger); //%this.nextTask(); } //******************************************************************************************** //******************************************************************************************** function AIPlayer::isObjectInView(%this, %object, %dist) { %player = %this; // default sight range of AI: %this.sightRange = %dist; if (!(isObject(%player) && isObject(%object))) { return false; } %objPos = %object.getWorldBoxCenter(); %eyePoint = %player.getWorldBoxCenter(); %distance = VectorDist(%objPos, %eyePoint); //error("**************DISTANCE FROM PLAYER = " SPC %distance); //error("**************sightRange = " SPC %this.sightRange); // error("**************distance = " SPC %distance); if (%distance <= %this.sightRange) { // if the object is within 1.5 meters sometimes it can // fall out of the field of view due to the eye height if (%distance > 2) { %eyeTransform = %player.getEyeTransform(); %eyePoint = firstWord(%eyeTransform) SPC getWord(%eyeTransform, 1) SPC getWord(%eyeTransform, 2); //error("******distance is greater than two, w/in 1.5 meters?"); } %eyeVector = VectorNormalize(%player.getEyeVector()); //make sure we're not looking through walls... %mask = $TypeMasks::TerrainObjectType | $TypeMasks::InteriorObjectType | $TypeMasks::StaticShapeObjectType; %losResult = containerRayCast(%objPos, %eyePoint, %mask); %losObject = GetWord(%losResult, 0); if (!isObject(%losObject)) { //error("**************LOS!!"); //create the vector from this client to the client %objVector = VectorNormalize(VectorSub(%objPos, %eyePoint)); // dot product to determine field of view %dot = VectorDot(%objVector, %eyeVector); //error("**************DOT" @ %dot); // within field of view return (%dot > 0.6); } } return false; } //******************************************************************************************** // AIhere:: is there an AI at this location? // missionArea bounding box last updated 2/17/08 (efa) //******************************************************************************************** function AIhere(%obj1) { %team = "AI"; %groupName = "MissionGroup/" @ %team @ "TeamDropPoints"; %group = nameToID(%groupName); if (%group != -1) { %count = %group.getCount(); if (%count != 0) //I have spawn points { for(%c = 0; %c < %count; %c++) { %obj2 = %group.getObject(%c); //if(%obj1 == %obj2) // return true; %dist1 = VectorDist(%obj1.getPosition(), %obj2.getPosition()); if(%dist1 < 100) { //echo("%obj1 = " @ %obj1 @ "%obj2 = " @ %obj2 @ "dist1 = " @ %dist1); return true; } } } } return false; } //*************************************************************************** // pickAISpawnInGame:: pick one of the in-game AI spawn points, if they exist. // last update 5/30/08 (efa) //*************************************************************************** function pickAISpawnInGame() { %team = "AI"; if( $Pref::Server::MissionType !$= "DeathMatch" )// NOT DEATH MATCH { //echo("Selecting spawn point for nonDeathMatch Game Type"); %groupName = "MissionGroup/" @ %team @ "TeamDropPoints"; //%groupName = "MissionGroup/" @ %team @ "SpawnPoints"; %group = nameToID(%groupName); if (%group != -1) { %count = %group.getCount(); if (%count != 0) //I have spawn points { //echo("EFA:: I've got AI spawn points"); //%index = getRandom(%count-1); %index = getRandom(1000) % %count; %spawn = %group.getObject(%index); %tries = 100; while(AIhere(%spawn) == 1) { //%index = getRandom(%count-1); %index = getRandom(1000) % %count; %spawn = %group.getObject(%index); %tries--; if(%tries < 0)//try 100 times before you give up break; } //%curAI = AIGroup.getObject($AIIndex); //%pos = %this.getPosition(); return %spawn.getTransform(); } //else //{ // error("No " @ %team @ " team spawn points found in " @ %groupName); //} } //else //{ // error("Missing " @ %team @ " team spawn points group " @ %groupName); //} } else { //echo("Selecting spawn point for DeathMatch Game Type"); %groupName = "MissionGroup/PlayerDropPoints"; %group = nameToID(%groupName); if (%group != -1) { %count = %group.getCount(); if (%count != 0) { %index = getRandom(%count-1); %spawn = %group.getObject(%index); return %spawn.getTransform(); } //else //{ // error("No spawn points found in " @ %groupName); //} } //else //{ // error("Missing spawn points group " @ %groupName); //} } return 0; //no pre-set spawn points, return zero } //******************************************************************************************** // pickAISpawn:: pick AI spawn points based on weapons locations in game or a random offset from // missionArea bounding box last updated 2/17/08 (efa) //******************************************************************************************** function pickAISpawnPoint() { //%groupName = "MissionGroup/AISpawnPoints"; %groupName = "MissionGroup/Weapons"; //use any weapons in the game as possible spawn points %group = nameToID(%groupName); // 2/13/08 (efa) if (%group != -1) { %count = %group.getCount(); for(%c = 0; %c < %count; %c++) //find the xmin, xmax and the ymin,ymax (efa) 2/17/08 { %spawn = %group.getObject(%c); %pos = %spawn.getPosition(); if(%c == 0) //get starting points for our min maxes... { %xmin = getWord(%pos, 0); %xmax = getWord(%pos, 0); %ymin = getWord(%pos, 1); %ymax = getWord(%pos, 1); } //%pos = %this.getTransform(); %x = getWord(%pos, 0); %y = getWord(%pos, 1); %z = getWord(%pos, 2); if(%x < %xmin) %xmin = %x; if(%x > %xmax) %xmax = %x; //find x extents if(%y < %ymin) %ymin = %y; if(%y > %ymax) %ymax = %y; //find y extents } %x_extent = %xmax - %xmin; //get the delta %x_extent = mAbs(%x_extent); //make sure its positive %y_extent = %ymax - %ymin; //get the delta %y_extent = mAbs(%y_extent); //make sure its positive %x = %xmin + getRandom(%x_extent); //pick a point within the x extent %y = %ymin + getRandom(%y_extent); //pick a point within the y extent %z = 300; //drop us from up high %spawn_here = %x SPC %y SPC %z SPC "1 0 0 0"; return(%spawn_here); } else //o.k., no weapon group, so we will try and use missionArea bounding box { %missionArea = nameToId("MissionArea"); if (isObject(%missionArea)) { //%minz = -1 * %missionArea.flightCeiling; //****** The following code is correct and verified (efa) 2/17/08 ****** %area = %missionArea.getArea(); %xmin = getWord(%area, 0); %ymin = getWord(%area, 1); %xmax = %xmin + getWord(%area, 2); %ymax = %ymin + getWord(%area, 3); %x_extent = getWord(%area, 2); //delta x %y_extent = getWord(%area, 3); //delta y %xcenter = (%xmin + %xmax)/2.0; %ycenter = (%ymin + %ymax)/2.0; %x = %xcenter + getRandom(200); //pick a point delta 200 from the center %y = %ycenter + getRandom(200); //pick a point delta 200 from the center %z = 300; //drop us from up high %spawn_here = %x SPC %y SPC %z SPC "1 0 0 0"; return(%spawn_here); } } // Couldn't determine a reasonable spawn points, in which case we'll stick the // player at the center of the world. return "0 0 300 1 0 0 0"; } //********************************************************************** //Not just for AI, use this to count the objects inside a SimGroup or SimSet //********************************************************************** function searchGroup(%group, %type) { %total = 0; %count = %group.getCount(); for (%i = 0; %i < %count; %i++) { %curObject = %group.getObject(%i); if (%curObject.getType() & %type) %total++; } return %total; } //********************************************************************** //********************************************************************** function AIManager::think(%this) { if($AIs <= 5) //set number of AIs to 5 in a game, if their were sufficent weapons { //$AIweapon = getrandom(2); //rocket launcher and cross-bow $AIweapon = 0; //rocket launcher %this.spawn1(); %count = AIGroup.getCount(); %curAI = AIGroup.getObject(%count-1); //echo("think:: $AIs = " @ $AIs @ "%curAI = " @ %curAI); %curAI.AILoop(); //call the AI Intelligent loop //echo("think::****************************************"); //for(%c=0; %c < %count; %c++) //{ // echo("think:: AIGroup.getObject(%c); " @ AIGroup.getObject(%c) @ "%c = " @ %c); //} //echo("think::****************************************"); } %groupName = "MissionGroup/Weapons"; %group = nameToID(%groupName); if($AIs > 0)//at least 1 active bot { %this.schedule(50, think); } } //********************************************************************** //********************************************************************** function AIManager::spawn1(%this) { // %spawn = pickAISpawnPoint(); %spawn = pickAISpawnInGame(); if (%spawn == 0) //no preset spawn points, choose random spawn-point %spawn = pickAISpawnPoint(); //return; //%AIPlayer = AIPlayer::spawn(%name, %spawn.getPosition()); %AIPlayer = AIPlayer::spawn(%name, %spawn); %AIPlayer.spawnPoint = %spawn; if (isObject(%AIplayer)) { $AIs++; return %AIplayer; } else return 0; } //**********************************************************************