Author: beffy (c++programmer)

Here are updated HEAD bot script functions by Michael Schaumburg - thanks for providing them!! :)
 
In GameConnection::spawnPlayer(), add:

// set the number of NPC's and initialize them 
// ---------------------------------------- 
cancel($scanSchedule); // stop schedule function till all the player are created 
%count = MissionGroup.getCount(); // all the members in this group 
for(%i=0; %i < %count; %i++){ // step through the group 
	%obj=MissionGroup.getObject(%i); // get the object ID 
	%objName=%obj.getClassName(); // get the class name of this object 
	if(%objName $= "AIPlayer"){ // if its AIPlayer, we found a NPC 
		%test = %obj.getName(); // wird mit Apply im editor gesetzt 
		//error("NPC heisst:" SPC %test); // z.B. goove 
		$npc[$npcCounter] = %obj; // initial the $npc array 
		npcSetProperties($npc[$npcCounter]); // set the properties of this NPC, in aiPlayer.cs 
		$npcCounter++; // increase array counter 
	} 
} 
Here are some new and adjusted helper functions...
function npcSetProperties(%this) // NPC Properties einstellen 
{ 
	//MissionCleanup.add(%this); // zu mission clean addieren 
	%this.setEnergyLevel(60); 
	// start with a weapon 
	%this.incInventory(Crossbow,1); 
	%this.incInventory(CrossbowAmmo,CrossbowAmmo.maxInventory); 
	%this.use(Crossbow); 
	%this.setMoveSpeed(0.2); 
	startBot(%this); 
} 

function startBot(%this) // schedule function die ständig 
{ // update NPC's
	for(%i = 0;%i<$npcCounter;%i++) // wir gehen alle NPC's durch 
	{ 
	
		// hier erfolgt der letzte Aufruf mit $npc[%i] 
		if(isObject($npc[%i])) 
		AIPlayer::handleWaypointsAndChase($npc[%i]); // von hier werden sämtliche functions aufgerufen 
	} 
	$scanSchedule = schedule(500, 0, "startBot"); // die function ruft sich selbst auf 
} 

function AIPlayer::handleWaypointsAndChase(%this) 
{ 
	%count = ClientGroup.getCount(); 
	// look for next "available" human player...  
	%client = ClientGroup.getObject(AIPlayer::getClosestHuman(%this)); // get the next nearest player 
	$daPlaya = %client.player; // daPlaya wird hier global deklariert 
	if(isObject($daPlaya)){ // only if $daPlaya is still an object 
		%playPos = $daPlaya.getPosition(); 
		AIPlayer::shootHimIfYouSeeHim(%this, %playPos); // bot und human position werden übergeben 
	} 
	else{ // happens when a player is killed 
		%this.fireWeapon(false); // don't shoot on a killed player 
		%this.stopMove(); // no movement 
		%this.clearAim(); 
		return; 
	} 
} 

//-------------------------------------------------------------------- 
// get the next nearest player 
// given is: %this = bot ID 
// return is: %index = player index 
//-------------------------------------------------------------------- 

function AIPlayer::getClosestHuman(%this) 
{ 
	%botpos = %this.getTransform(); // get bot position 
	%count = ClientGroup.getCount(); // number of player 
	%tempDist2 = 32000; // set this great 
	for(%i = 0; %i < %count; %i++) // step through 
	{ 
		%client = ClientGroup.getObject(%i); 
		if(isObject(%client.player)) // only if client.player is an object 
		{ 
			%playPos = %client.player.getPosition(); // player position 
			%tempDist = VectorDist(%playPos, %botPos); // distance player - bot 
			if(%tempDist2 > %tempDist) // if Dist2 greater Dist we take the smaller 
			{ 
				%tempDist2 = %tempDist; // save shortest Dist 
				%index = %i; // set %index 
			} 
		} 
	} 
	return %index; 
} 

//-------------------------------------------------------------------- 
// shoot the player if you see him, if not wait and don't shoot 
// given is: %this = bot ID / %playPos = position from global $daPlaya 
// return is: nothing 
//-------------------------------------------------------------------- 
function AIPlayer::shootHimIfYouSeeHim(%this, %playPos) 
{ 
	%myrange = 70; // ev. adjust
	%iSeeHim = AIPlayer::isObjectInView(%this, $daPlaya); // %iSeeHim >0 NPC can see human 
	//error(%this SPC "isObjectInView:" SPC %iSeeHim); 
	if(%iSeeHim) 
	{ 
		//error(%this @ ": I see him!!!"); 
		%this.setAimLocation($daPlaya.getWorldBoxCenter()); 
		// do we have a weapon? 
		if ( (%this.hasInventory("Crossbow") && %this.hasInventory("CrossbowAmmo")) 
		|| (%this.hasInventory("Rifle") && %this.hasInventory("RifleAmmo")) ) 
		{ 
			%this.setAimObject($daPlaya); 
			%this.setAimLocation($daPlaya.getWorldBoxCenter()); 
			// shoooooot 
			//error("NPC is shooting..." SPC %this); 
			%rnum = getRandom()* 10; 
			if(%rnum >5 && %rnum < 9) 
				%this.fireWeapon(true); 
			else 
				%this.fireWeapon(false); 
				//%this.setImageTrigger(0,true);
		} 
		else 
		{ 
			//error(%this.player SPC "has no weapon/ammo!"); 
		} 
		%this.setMoveDestination(%playPos); // can see the human, move to him 
	
	} 
	// no player in sight? 
	else 
	{ 
		// clear aim 
		%this.clearAim(); 
		// check for next nearest player 
		%nextClient = ClientGroup.getObject(AIPlayer::getClosestHuman(%this)); 
		$daPlaya = %nextClient.player; 
		if(!isObject($daPlaya)) 
		{ 
			return; 
		} 
		%playPos = $daPlaya.getPosition(); 
		//%this.setMoveDestination(%playPos); // without shooting to the human player 
		%this.setAimObject(%nextClient.player); 
		%this.setAimLocation($daPlaya.getWorldBoxCenter()); 
		%this.fireWeapon(false); 
		%this.stopMove(); 
	} 
} 

//-------------------------------------------------------------------- 
// checks if the requested object is in a certain range 
// given is: bot = %this / human = %object 
// return is: 0 = not in sight / 1 = in sight 
//-------------------------------------------------------------------- 
function aiPlayer::isObjectInView(%this, %object) 
{ 
	%botPlayer = %this; // das ist der bot; %object ist der humanPlayer 
	// default sight range of AI: 
	%this.sightRange = 300.0; 
	if (!(isObject(%botPlayer) && isObject(%object))) return; 
	
	%objPos = %object.getWorldBoxCenter(); // player position 
	%eyePoint = %botPlayer.getWorldBoxCenter(); // bot position 
	
	%distance = VectorDist(%objPos,%eyePoint); // distance berechnen 
	
	//error("DISTANCE: " @ %distance); 
	
	if (%distance <= %this.sightRange) // in sichtweite ?? 
	{ 
		// 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 = %botPlayer.getEyeTransform(); 
			%eyePoint = firstWord(%eyeTransform) 
			SPC getWord(%eyeTransform, 1) 
			SPC getWord(%eyeTransform, 2); 
		} 
		%eyeVector = VectorNormalize(%botPlayer.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)) 
		{ 
			//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); 
			// within field of view 
			return (%dot > 0.6); 
		} 
	} 
	return false; 
}