// BZII Community Project Modified MPI DLL.  This class does certain things to make the AI
// team stronger.  It also handles portal functions, and sends enemy units to portals.
// This class's interaction functions (functions triggered by bzone.exe directly or
// indirectly) are called by functions of a similar class called Strategy01 (StratInstant.cpp).
//#include "..\shared\DllBase.h"
#include "..\shared\SubMission.h"
#include <string.h>

/*
	InstantMission
	Just add water!!
*/

#define _BZDEBUG // Use for coding and debugging phase

#define MAX_GOODIES 40
#define TEST_INSTANT 0
//#define MaxPortals 16
#define MaxPortals 60	//@@@Centerline_FE_MPI - increased to allow for larger maps that use all portal groups
#define MaxPortalUserTypes 35	//@@@NB_FE_MPI02

//@@@@@@NB_FE_MPI02 these are not used:
//const int myForce = 1;
//const int compForce = 2;
//const int difficulty = 2;


// scav limit controls
const int MaxCPUScavs=15;	//@@@NB_FE_MPI02 - scav limitation
const int MinCPUScavs=8;	//@@@NB_FE_MPI02 - scav limitation

// vehicle anti-freeze controls
const int MAX_COMBAT_UNITS = 72;	//@@@NB_FE_MPI02
const int MAX_HOLD_UNITS = 8;		//@@@NB_FE_MPI02

// FE MPI Difficulty Options
const int OPTION_EASY = 0;		//@@@NB_FE_MPI02
const int OPTION_LOW = 1;		//@@@NB_FE_MPI02
const int OPTION_NORMAL = 2;	//@@@NB_FE_MPI02
const int OPTION_HIGH = 3;		//@@@NB_FE_MPI02

// Ally Race Codes
const int ALLY_CERBERI = 1;		//@@@NB_FE_MPI02
const int ALLY_HADEAN = 2;		//@@@NB_FE_MPI02
const int ALLY_ISDF = 3;		//@@@NB_FE_MPI02
const int ALLY_SCION = 4;		//@@@NB_FE_MPI02

//@@@Centerline_FE_MPI - For the SetupRandomScrapPools routine
const int iMaxPoolGroups = 9;		// Maximum number of pools to be placed - don't exceed "MaxValueLen" number of digits

//@@@Centerline_FE_MPI - For the SpawnRampage Rampage routine
const int MAX_RAMPAGE_UNITS = 32;	//Maximum number for Rampage units ever allowed in the game


//@@@NB_FE_MPI02 - modified to designate variant portal users
#define ODFArraySize 27
static char ODFType[ODFArraySize][20] =
{
	"vputank",
	"vpuatank",
	"vpuwalk",
	"vpuewalk",
	"vpuscout",
	"vpusent",
	"vpumisl",
	"vpuarch",
	"vpurckt",
	"vpuscav",
	"vpuserv",
	"vpumbike",
	"vpumort",
	"vputanku",
	"vpuatanku",
	"vpumislu",
	"vpurbomb",
	"vputurr",
	"vpuimbk2",
	"vpuirbmb2",
	"vpuirckt2",
	"vpuesct2",
	"vpuemisl2",
	"vpueatnk2",
	"vpufsct2",
	"vpufrbmb2",
	"vpufwalk2"
};


enum AIPType {
	AIPType0=0,
	AIPType1,
	AIPType2,
	AIPType3,
	AIPTypeA,
	AIPTypeL,
	AIPTypeS,
	MAX_AIP_TYPE,
};
const char *AIPTypeExtensions="0123als"; // AIPType above is offset in this string table

char CustomAIPNameBase[256];
static char TempODFName[64];
bool CheckedSVar3=false;
bool UseCustomAIPs;
int siege_switchtime;		//@@@NB_FE_MPI02
int iHold1Count;		//@@@NB_FE_MPI02
int iHold2Count;		//@@@NB_FE_MPI02
int iHold3Count;		//@@@NB_FE_MPI02
int iHold4Count;		//@@@NB_FE_MPI02
int *iUnitptr;			//@@@NB_FE_MPI02
int *iHold1ptr;			//@@@NB_FE_MPI02
int *iHold2ptr;			//@@@NB_FE_MPI02
int *iHold3ptr;			//@@@NB_FE_MPI02
int *iHold4ptr;			//@@@NB_FE_MPI02
float cordon_sanitaire;		//@@@NB_FE_MPI02

char CaptureeODF[64];		//@@@Centerline_FE_MPI


class instantMission : public SubMission {
public:
	// ______________________________________________________________________
	instantMission(void)
	{
		b_count = &b_last - &b_first - 1;
		b_array = &b_first + 1;

		f_count = &f_last - &f_first - 1;
		f_array = &f_first + 1;

		h_count = &h_last - &h_first - 1;
		h_array = &h_first + 1;

		i_count = &i_last - &i_first - 1;
		i_array = &i_first + 1;

		m_PortalUser = 0;
		PortalCount = 0;
		didInit = false;

		for (int i=0; i<MaxPortalUserTypes; i++)
		{
			PortalUserTypes[i] = -1;
		}

	}

	// Based on player races and type, set AIPlan
	void instantMission::SetCPUAIPlan(const int CPUSide,const int HumanSide,AIPType Type);

	// And set up extra race-specific vehicles at pathpoints in the map.
	void instantMission::SetupExtraVehicles(void);

	//@@@Centerline_FE_MPI - Checks to make sure Group E destination portal exists
	bool instantMission::DestinationPortal(int PortalCount, int PortalNumber[MaxPortals], Handle Portals[MaxPortals]);

	//@@@Centerline_FE_MPI - Function added by Centerline
	// Sets up extra random scrap pools at pathpoints in the map.
	void instantMission::SetupRandomScrapPools(Handle hRandomPools[iMaxPoolGroups]);

	//@@@Centerline_FE_MPI - Function added by Centerline
	// Spawns the Rampage units.
	//void instantMission::SpawnRampage(char RampageUnit[32], Handle *hRampageUnits, int UnitLimit);
	void instantMission::SpawnRampage(char RampageUnit[32], Handle *hRampageUnits, int UnitLimit, int RampageCurrentSlot);

	//@@@Centerline_FE_MPI - Function added by Centerline
	// Logs comments to file. Make sure you include \n in your text string.
	void instantMission::LogToFile(char Comments[64]);

	//@@@Centerline_FE_MPI - Function added by Centerline
	// Spawns an object at a random location around an object within min/max radius
	Handle instantMission::SpawnObject(char TempODF[64], int Team, Handle hReferenceObject, float MinRadiusAway, float MaxRadiusAway);

	//@@@Centerline_FE_MPI - Function added by Centerline
	// This section spawns the extra weapons crates.
	void instantMission::SpawnWeaponCrates();

	//@@@Centerline_FE_MPI - Function added by Centerline
	// This section spawns the object to be captured.
	bool instantMission::CaptureObject();

	//@@@Centerline_FE_MPI - Function added by Centerline
	// This section spawns the attacking monsters.
	void instantMission::MonsterRun();

	//@@@Centerline_FE_MPI - Function added by Centerline
	// This section spawns the extra scrap pools.
	void instantMission::SpawnEasyPools();

	virtual void AddObject(Handle h);
	virtual void DeleteObject(Handle h);
	virtual void Setup(void);
	virtual void CreateObjectives();
	virtual void TestObjectives();
	virtual void Execute(bool *TeamIsSetUp, Handle *RecyclerHandles);
	virtual void DoGenericStrategy(bool *TeamIsSetUp, Handle *RecyclerHandles);
	virtual EjectKillRetCodes PlayerEjected(void);
	virtual EjectKillRetCodes PlayerKilled(int KillersHandle);

	void PortalInit();
	void PortalFunctions();
	void SetPortalUsers();
	Handle GetPortalToUse(Handle PortalUser);
	char* RaceOdf(char Race, char* EndString);
	bool IsType (Handle h, char* EndString);

private:
	// bools
	bool
		b_first,
		start_done,
		inspect_bunker,
		second_objective,
		picking_up[MAX_GOODIES],
		captured[MAX_GOODIES],
		siege_on,
		anti_assault,
		late_game,
		early_game,
		have_armory,
		have_sbay,	//@@@NB_FE_MPI02
		have_techctr,	//@@@NB_FE_MPI02
		have_trainctr,	//@@@NB_FE_MPI02
		have_portals,	//@@@NB_FE_MPI02
		have_groupE,	//@@@NB_FE_MPI02
		sendEAttack,	//@@@NB_FE_MPI02
		drath_rush,	//@@@NB_FE_MPI02
		didInit,
		HadeanWalkerBuilt,
		RampageSpawnTrigger,	//@@@Centerline_FE_MPI
		SpawnCrates,		//@@@Centerline_FE_MPI
		CaptureObjectSetup,	//@@@Centerline_FE_MPI
		CaptureObjectDone,	//@@@Centerline_FE_MPI
		b_last;

	// floats
	float
		f_first,
		f,
		f_last;

	// handles
	Handle
		h_first,
		h,
		obj1,
		bunker,
		bunker2,
		player,
		foe1,
		foe2,
		foe3,
		walker,
		tug,
		recycler,
		enemy_recycler,
		powerup1,
		powerup2,
		powerup3,
		powerup4,
		hBomber,	//@@@NB_FE_MPI02
		hLastGT,	//@@@NB_FE_MPI02
		hPrevGT,	//@@@NB_FE_MPI02
		hTurr1,		//@@@NB_FE_MPI02
		hTurr2,		//@@@NB_FE_MPI02
		hTurr3,		//@@@NB_FE_MPI02
		hTurr4,		//@@@NB_FE_MPI02
		hTurr5,		//@@@NB_FE_MPI02
		hTurr6,		//@@@NB_FE_MPI02
		hTurr7,		//@@@Centerline_FE_MPI - increased count of startup units
		hTurr8,		//@@@Centerline_FE_MPI - increased count of startup units
		hTurr9,		//@@@Centerline_FE_MPI - increased count of startup units
		hTurr10,	//@@@Centerline_FE_MPI - increased count of startup units
		goodies[MAX_GOODIES],
		tuglist[MAX_GOODIES],  // keep track of tugs
		holding[MAX_GOODIES],
		CPUScavList[MaxCPUScavs],	//@@@NB_FE_MPI02 - scav limitation
		hCombatUnit[MAX_COMBAT_UNITS],		//@@@NB_FE_MPI02
		hHold1Units[MAX_HOLD_UNITS],		//@@@NB_FE_MPI02
		hHold2Units[MAX_HOLD_UNITS],		//@@@NB_FE_MPI02
		hHold3Units[MAX_HOLD_UNITS],		//@@@NB_FE_MPI02
		hHold4Units[MAX_HOLD_UNITS],		//@@@NB_FE_MPI02
		Portals[MaxPortals],
		UsedPortals[MaxPortalUserTypes],
		m_PortalUser,
		m_PortalToUse,
		groupEPortal,	//@@@NB_FE_MPI02
		groupETarget,	//@@@NB_FE_MPI02
		HadeanWalker,
		hRandomPools[iMaxPoolGroups],		//@@@Centerline_FE_MPI - array of RandomPools that were built
		hRampageUnits[MAX_RAMPAGE_UNITS],	//@@@Centerline_FE_MPI
		hCrate1,	//@@@Centerline_FE_MPI
		hCrate2,	//@@@Centerline_FE_MPI
		hCrate3,	//@@@Centerline_FE_MPI
		hCapturee,	//@@@Centerline_FE_MPI
		monster1,	//@@@Centerline_FE_MPI
		monster2,	//@@@Centerline_FE_MPI
		h_last;

	// integers
	int
		i_first,
		strat_team,
		i,
		siege_counter,
		assault_counter,
		next_wave,
		next_spawn,
		time_count,
		post_bunker,
		enemy_reinforcement_time,
		friend_reinforcement_time,
		end_counter,
		comp_team,
		powerup_counter,
		turn_counter,
//		myGoal,
		mySide,
		HumanSide,
		NumHumans,
//		difficulty,
		NumCPUScavs,			//@@@NB_FE_MPI02 - scav limitation
		iDiffLvl,				//@@@NB_FE_MPI02
		iScrapBstLvl,			//@@@NB_FE_MPI02
		alt_weapon,				//@@@NB_FE_MPI02
		iNextBomberAttack,		//@@@NB_FE_MPI02
		igroupETrigger,			//@@@NB_FE_MPI02
		iNextgroupEAttack,		//@@@NB_FE_MPI02
		groupETargetType,		//@@@NB_FE_MPI02
		groupERace,				//@@@NB_FE_MPI02
		PortalCount,
		m_PortalGroup,			//@@@NB_FE_MPI02
		PortalGroup[MaxPortals],
		PortalNumber[MaxPortals],	//@@@NB_FE_MPI02
		PortalUserTypes[MaxPortalUserTypes],
		UsedPortalsGroup[MaxPortalUserTypes],	//@@@NB_FE_MPI02
		PortalUserWaitCount,
		RampageUnitLimit,		//@@@Centerline_FE_MPI
		RampageTimer,			//@@@Centerline_FE_MPI
		RampageSpawnSpacer,		//@@@Centerline_FE_MPI
		RampageUnitsSpawned,	//@@@Centerline_FE_MPI
		SpawnCratesSpacer,		//@@@Centerline_FE_MPI
		iMonsterDelay,			//@@@Centerline_FE_MPI
		iMonsterToggle,			//@@@RD_FE13MPI
		i_last;
};


// And set up extra race-specific vehicles at pathpoints in the map.
// Function modified by BS-er to allow a 3rd ODF type to be specified in path names
// for the Hadean race.
// ______________________________________________________________________
void instantMission::SetupExtraVehicles(void)
{
	int pathCount;
	char **pathNames;
	GetAiPaths(pathCount, pathNames);
	unsigned long j,k;

	NumCPUScavs=0;	//@@@NB_FE_MPI02 - scav limitation

	for (int i = 0; i < pathCount; ++i)
	{
		char *Label = pathNames[i];
		if(strncmp(Label,"mpi",3)==0)
		{
			// Starts with MPI. Process it.

			const int MaxODFLen=32;
			char ScionStuff[MaxODFLen+1];
			char ISDFStuff[MaxODFLen+1];
			char HadeanStuff[MaxODFLen+1];

			// Prezap full string contents so we don't have to null-term later
			memset(ScionStuff,0,sizeof(ScionStuff));
			memset(ISDFStuff,0,sizeof(ISDFStuff));
			memset(HadeanStuff,0,sizeof(HadeanStuff));

			j=0;

			// Skip forward until an _
			while((Label[j] != '_') && (Label[j] != '\0'))
			{
				j++;
			}
			if(Label[j]=='\0')
			{
				continue; // Misformat! No _ found! Bail!
			}

			k=0;
			j++; // skip the _. Now, copy out scion stuff
			while((Label[j] != '_') && (Label[j] != '\0'))
			{
				if(k<MaxODFLen)
				{
					ScionStuff[k]=Label[j];
					k++;
				}
				j++;
			}

			if(Label[j]=='\0')
			{
				continue; // Misformat! No _ found! Bail!
			}

			k=0;
			j++; // skip the _. Now, copy out isdf stuff
			ISDFStuff[k]=Label[j]; // Compiler messup test?
			while((Label[j] != '_') && (Label[j] != '\0'))
			{
				if(k<MaxODFLen)
				{
					ISDFStuff[k]=Label[j];
					k++;
				}
				j++;
			}

			if(Label[j]=='\0')
			{
				continue; // Misformat! No _ found! Bail!
			}

			k=0;
			j++; // skip the _. Now, copy out isdf stuff
			HadeanStuff[k]=Label[j]; // Compiler messup test?
			while((Label[j] != '_') && (Label[j] != '\0'))
			{
				if(k<MaxODFLen)
				{
					HadeanStuff[k]=Label[j];
					k++;
				}
				j++;
			}


			// For the CPU?
			if(strncmp(Label,"mpic",4)==0)
			{
				if (mySide==1)  // CPU is Scion
				{
					BuildObject(ScionStuff,comp_team,Label);
				}
				else if (mySide==2)
				{
					BuildObject(ISDFStuff,comp_team,Label);
				}
				else if (mySide==3)
				{
					BuildObject(HadeanStuff,comp_team,Label);
				}
			}
			else if(strncmp(Label,"mpih",4)==0)
			{ // For the human side
				Handle h;
				if (HumanSide==1)  // Humans are Scion
				{
					h=BuildObject(ScionStuff,strat_team,Label);
				}

				//@@@Centerline_FE_MPI - changed this so that human team
				// startup units are correct when Hadean are selected per Natty
				//
				//else if (mySide==3)
				else if (HumanSide==3)  // Humans are Hadean
				{
					h=BuildObject(HadeanStuff,strat_team,Label);
				}
				else
				{
					h=BuildObject(ISDFStuff,strat_team,Label);
				}
				SetGroup(h,1);
			}
		}
	}
}


// Function modified by BS-er to invoke AIP plans that involve Hadeans.
// ______________________________________________________________________
void instantMission::SetCPUAIPlan(const int CPUSide,const int HumanSide,AIPType Type)
{
	if(!CheckedSVar3)
	{
		CheckedSVar3=true;
		const char *Contents=GetVarItemStr("network.session.svar3");
		if(Contents[0]== '\0')
		{
			UseCustomAIPs=false;
		}
		else
		{
			UseCustomAIPs=true;
			strncpy(CustomAIPNameBase,Contents,254);
		}
	}

	if((Type<0) || (Type >=MAX_AIP_TYPE))
	{
		Type=AIPType3; // Default
	}

	char AIPFile[512];
	unsigned long EOS; // End of string

	// Prezap contents of string, so that we can safely append
	// characters w/o worrying about having to re-terminate
	memset(AIPFile,0x00,512);

	if(!UseCustomAIPs)
	{
		if (CPUSide==1)
		{ // Scion
			strcpy(AIPFile,"scioninst");
			EOS=strlen(AIPFile);
			AIPFile[EOS]=AIPTypeExtensions[Type];

			if(HumanSide==1)
			{ // vs Scion
				EOS=strlen(AIPFile);
				AIPFile[EOS]='_';
			}
			else if (HumanSide==2)
			{
				EOS=strlen(AIPFile);
				AIPFile[EOS]='i';
			}
			else
			{
				EOS=strlen(AIPFile);
				AIPFile[EOS]='h';
			}

		} // CPU is Scion
		else if (CPUSide==2)
		{ // CPU is ISDF
			strcpy(AIPFile,"isdfinst");
			EOS=strlen(AIPFile);
			AIPFile[EOS]=AIPTypeExtensions[Type];
			
			if(HumanSide==2)
			{ // vs ISDF
				EOS=strlen(AIPFile);
				AIPFile[EOS]='_';
			} 
			else if (HumanSide==1)
			{
				EOS=strlen(AIPFile);
				AIPFile[EOS]='s';
			}
			else
			{
				EOS=strlen(AIPFile);
				AIPFile[EOS]='h';
			}
		} // CPU is ISDF case
		else
		{// CPU is Hadean
			strcpy(AIPFile,"hadeinst");
			EOS=strlen(AIPFile);
			AIPFile[EOS]=AIPTypeExtensions[Type];
			
			if(HumanSide==2)
			{ // vs ISDF
				EOS=strlen(AIPFile);
				AIPFile[EOS]='i';
			} 
			else if (HumanSide==1)
			{
				EOS=strlen(AIPFile);
				AIPFile[EOS]='s';
			}
			else
			{
				EOS=strlen(AIPFile);
				AIPFile[EOS]='_';
			}
		}
	} // !UseCustomAIPs
	else
	{
		// UseCustomAIPs
		strcpy(AIPFile,CustomAIPNameBase);

		EOS=strlen(AIPFile);
		if(CPUSide==1) // CPU Scion
		{
			AIPFile[EOS]='f';
		}
		else if (CPUSide==2)
		{
			AIPFile[EOS]='i';
		}
		else
		{
			AIPFile[EOS]='e';
		}

		if(HumanSide==1) // vs Human Scion
		{
			AIPFile[EOS+1]='f';
		}
		else if (HumanSide==2)
		{
			AIPFile[EOS+1]='i';
		}
		else
		{
			AIPFile[EOS+1]='e';
		}

		AIPFile[EOS+2]=AIPTypeExtensions[Type];
	}

	// Always tack on file extension
	strcat(AIPFile,".aip");
	SetPlan(AIPFile,comp_team);
}


// ______________________________________________________________________
void instantMission::Setup(void)
{
	/*
		Here's where you set the values at the start.  
	*/

	char tempstr[64];

	int iX;		//@@@NB_FE_MPI02

	start_done=false;
	time_count=1;
	inspect_bunker=false;
	second_objective=false;
	post_bunker=1;
	enemy_reinforcement_time=2000;
	friend_reinforcement_time=2500;
	end_counter=0;
	comp_team=6;
	strat_team=0;  // the default
	obj1=(-1);

	turn_counter=0;

	hCapturee = NULL;				//@@@Centerline_FE_MPI
	CaptureObjectSetup = false;		//@@@Centerline_FE_MPI
	CaptureObjectDone = false;		//@@@Centerline_FE_MPI
	monster1 = NULL;				//@@@Centerline_FE_MPI
	monster2 = NULL;				//@@@Centerline_FE_MPI
	
	siege_on=false;
	anti_assault=false;
	siege_counter=0;
	assault_counter=0;		//@@@NB_FE_MPI02
	late_game=false;
	early_game=true;		//@@@NB_FE_MPI02
	have_armory=false;
	have_sbay=false;		//@@@NB_FE_MPI02
	have_techctr=false;		//@@@NB_FE_MPI02
	have_trainctr=false;	//@@@NB_FE_MPI02
	have_portals=false;		//@@@NB_FE_MPI02
	have_groupE=false;		//@@@NB_FE_MPI02
	alt_weapon=false;		//@@@NB_FE_MPI02
	sendEAttack=false;		//@@@NB_FE_MPI02
	drath_rush=false;		//@@@NB_FE_MPI02

	cordon_sanitaire=300.0f;	//@@@NB_FE_MPI02
	siege_switchtime=300;		//450;	//@@@NB_FE_MPI02
		
	powerup1=NULL;
	powerup2=NULL;
	powerup3=NULL;
	powerup4=NULL;

	for (int temp=0;temp<40;temp++)
	{
		goodies[temp]=NULL;
		picking_up[temp]=false;
		captured[temp]=false;
		tuglist[temp]=NULL;
		holding[temp]=NULL;
	}

	hBomber=NULL;	//@@@NB_FE_MPI02
	hTurr1=NULL;	//@@@NB_FE_MPI02
	hTurr2=NULL;	//@@@NB_FE_MPI02
	hTurr3=NULL;	//@@@NB_FE_MPI02
	hTurr4=NULL;	//@@@NB_FE_MPI02
	hTurr5=NULL;	//@@@NB_FE_MPI02
	hTurr6=NULL;	//@@@NB_FE_MPI02
	hTurr7=NULL;	//@@@Centerline_FE_MPI - increased count of startup units
	hTurr8=NULL;	//@@@Centerline_FE_MPI - increased count of startup units
	hTurr9=NULL;	//@@@Centerline_FE_MPI - increased count of startup units
	hTurr10=NULL;	//@@@Centerline_FE_MPI - increased count of startup units

	igroupETrigger=0;		//@@@NB_FE_MPI02
	iNextgroupEAttack=0;	//@@@NB_FE_MPI02
	groupETarget=NULL;		//@@@NB_FE_MPI02
	groupETargetType=0;		//@@@NB_FE_MPI02
	groupERace=0;			//@@@NB_FE_MPI02

	//@@@NB_FE_MPI02
	iUnitptr = hCombatUnit;
	for (iX=0; iX<MAX_COMBAT_UNITS; ++iX) {
		*(iUnitptr + iX)=NULL;
	}
	
	//@@@NB_FE_MPI02
	iHold1ptr = hHold1Units;
	iHold2ptr = hHold2Units;
	iHold3ptr = hHold3Units;
	iHold4ptr = hHold4Units;
	for (iX=0; iX<MAX_HOLD_UNITS; ++iX) {
		*(iHold1ptr + iX)=NULL;
		*(iHold2ptr + iX)=NULL;
		*(iHold3ptr + iX)=NULL;
		*(iHold4ptr + iX)=NULL;
	}

	NumHumans=CountPlayers();

	HadeanWalkerBuilt = false;

	//@@@NB_FE_MPI02
	iDiffLvl=GetVarItemInt("network.session.ivar30");
	switch (iDiffLvl)
	{
	case 0:		iDiffLvl=0;		break;
	case 1:		iDiffLvl=1;		break;
	case 2:		iDiffLvl=2;		break;
	case 3:		iDiffLvl=3;		break;
	default:	iDiffLvl=1;		break;
	}

	//@@@NB_FE_MPI02
	iScrapBstLvl=GetVarItemInt("network.session.ivar24");
	switch (iScrapBstLvl)
	{
	case 0:		iScrapBstLvl=0;		break;
	case 1:		iScrapBstLvl=1;		break;
	case 2:		iScrapBstLvl=2;		break;
	case 3:		iScrapBstLvl=3;		break;
	default:	iScrapBstLvl=0;		break;
	}

	//------------------------------------------------------------------------
	// @@@Centerline_FE_MPI - Print to console MPI settings from shell
	//

	/*
	PrintConsoleMessage("==========================================\n");
	LogToFile("==========================================\n");
	PrintConsoleMessage("New game initiated!\n");
	LogToFile("New game initiated!\n");
	PrintConsoleMessage("==========================================\n");
	LogToFile("==========================================\n");
	*/

	sprintf(tempstr, "Difficulty Level %i\n", iDiffLvl);
	PrintConsoleMessage(tempstr);
	//LogToFile(tempstr);
	sprintf(tempstr, "Scrap Boost Level %i\n", iScrapBstLvl);
	PrintConsoleMessage(tempstr);
	//LogToFile(tempstr);

	//------------------------------------------------------------------------
	//
	// @@@Centerline_FE_MPI - This section sets up the Rampage options
	//

	// Set time of first Rampage wave.
	int iCheck = (int)GetRandomFloat(6000);
	RampageTimer = 4200+((iCheck%3)*600)+(iCheck%600);

	RampageSpawnTrigger = false;
	RampageSpawnSpacer = 0;
	RampageUnitsSpawned = 0;

	for (iX=0; iX<MAX_RAMPAGE_UNITS+1; iX++)
	{
		hRampageUnits[iX] = NULL;
	}
	
	RampageUnitLimit = GetVarItemInt("network.session.ivar35");
	if (RampageUnitLimit > MAX_RAMPAGE_UNITS)
	{
		RampageUnitLimit = MAX_RAMPAGE_UNITS;
	}
	else if (RampageUnitLimit < 0)
	{
		RampageUnitLimit = 4;
	}

	if (RampageUnitLimit > 0)
	{
		PrintConsoleMessage("Rampage attacks are ON!");
		sprintf(tempstr, "Maximum number of Rampage units allowed: %i\n", RampageUnitLimit);
		PrintConsoleMessage(tempstr);
		//LogToFile(tempstr);
	}
	else
	{
		PrintConsoleMessage("Rampage attacks are OFF!\n");
	}

	//------------------------------------------------------------------------
	//
	// @@@Centerline_FE_MPI - This section sets up the Spawn Crate options
	//
	SpawnCratesSpacer = 1200;  // First respawn occurs 2 minutes into game
	hCrate1 = NULL;
	hCrate2 = NULL;
	hCrate3 = NULL;

	int iVarTemp = 0;
	iVarTemp = GetVarItemInt("network.session.ivar36");
	if (iVarTemp > 0)
	{
		SpawnCrates = true;
		PrintConsoleMessage("Spawn weapons crates is ON!\n\n");
	}
	else
	{
		SpawnCrates = false;
		PrintConsoleMessage("Spawn weapons crates is OFF!\n\n");
	}


	//------------------------------------------------------------------------
	//
	// @@@Centerline_FE_MPI - This section sets up the Monster Run timer
	//

	iMonsterToggle=GetVarItemInt("network.session.ivar38");

	if (iMonsterToggle == 1)
	{
	iMonsterDelay=GetVarItemInt("network.session.ivar40");

	if (iMonsterDelay < 0) iMonsterDelay = 60;
	if (iMonsterDelay > 1800) iMonsterDelay = 600;

	int iShift = (int)GetRandomFloat(float(iMonsterDelay * 0.2));
	iMonsterDelay = (int((iMonsterDelay * 0.9)) + iShift) * 10;

	PrintConsoleMessage("==========================================\n");
	sprintf(tempstr, "Monster Delay is: %i\n", iMonsterDelay);
	PrintConsoleMessage(tempstr);
	PrintConsoleMessage("==========================================\n");
	}
	else
	{
	PrintConsoleMessage("==========================================\n");
	sprintf(tempstr, "Monster Run is OFF!");
	PrintConsoleMessage(tempstr);
	PrintConsoleMessage("==========================================\n");
	}
}


// This function is called when any object is added to the mission.
// Function modified by BS-er to account for Hadean race.
// ______________________________________________________________________
void instantMission::AddObject(Handle h)
{
	int iX;		//@@@NB_FE_MPI02

	if (turn_counter == 0)
	{
		// monitor what objects get built by the mpstrat dll
		if (GetTeamNum(h) == 1)
		{
			if (IsOdf(h, "ivrecy_m"))
			{
				HumanSide=2; // Player is ISDF
				recycler = h;
			}
			else if (IsOdf(h, "fvrecy_m"))
			{
				HumanSide=1; // Player is Scion
				recycler = h;
			}
			else if (IsOdf(h, "evrecy_m"))
			{
				HumanSide=3; // Player is Hadean
				recycler = h;
			}

//			int ShellRace=GetVarItemInt("network.session.ivar13");
			int ShellRace=GetVarItemInt("network.session.ivar41");
			if(ShellRace == 2)
			{
				mySide=2; // Computer is ISDF
			}
			else if (ShellRace == 1)
			{
				mySide=1; // Computer is Scion
			}
			else if (ShellRace == 0)
			{
				mySide=3; // Computer is Hadean
			}
		}
	}

	if (GetTeamNum(h)==comp_team)
	{
		// See if this unit should go to a portal.
		// Cerberi handled in DoGenericStrategy
		if ((GetRace(h)!='c') || (GetRace(h)!='a'))
		{
			Handle TempHandle = GetPortalToUse(h);
			if (TempHandle != 0 && m_PortalUser == 0)
			{
				//PrintConsoleMessage("--- PortalUser Created");

				m_PortalUser = h;
				PortalUserWaitCount = 0;
				m_PortalToUse = TempHandle;
			}
			else
			{
				if (IsOdf(h,"evpumislu") || IsOdf(h,"ivpurckt")
					|| IsOdf(h,"fvpuarch"))
				{
					m_PortalGroup=0;
					m_PortalUser = h;
					PortalUserWaitCount = 0;
					m_PortalToUse = NULL;
				}
				else if (IsOdf(h,"evpuatank") || IsOdf(h,"ivpurbomb")
					|| IsOdf(h,"fvpurbomb"))
				{
					m_PortalGroup=1;
					m_PortalUser = h;
					PortalUserWaitCount = 0;
					m_PortalToUse = NULL;
				}
				else if (IsOdf(h,"evpuatanku") || IsOdf(h,"ivpuatank")
					|| IsOdf(h,"fvpuwalk"))
				{
					m_PortalGroup=2;
					m_PortalUser = h;
					PortalUserWaitCount = 0;
					m_PortalToUse = NULL;
				}
				else if (IsOdf(h,"ivpuewalk") || IsOdf(h,"ivpuwalk")
					|| IsOdf(h,"fvpuatank"))
				{
					m_PortalGroup=3;
					m_PortalUser = h;
					PortalUserWaitCount = 0;
					m_PortalToUse = NULL;
				}
				else if (IsType(h,"vputank") || IsType(h,"vpuscout")
					|| IsType(h,"vpusent"))
				{
					m_PortalGroup=5;
					m_PortalUser = h;
					PortalUserWaitCount = 0;
					m_PortalToUse = NULL;
				}
				else if (IsType(h,"vpuscav") || IsType(h,"vpuserv")
					|| IsType(h,"vpumbike") || IsType(h,"vpumort")
					|| IsType(h,"vputanku"))
				{
					RemoveObject(h);
				}
			}
		}

		//@@@NB_FE_MPI02
		// Scav cleanup code suggested by Nathan Mates 7/20/01:
		if(IsType(h,"vscav"))
		{
			// Add new scav to list:
			if(NumCPUScavs<MaxCPUScavs) CPUScavList[NumCPUScavs++] = h;

			// Cleanup if number of scavs reaches maximum allowed: 
			if(NumCPUScavs>=MaxCPUScavs) {
				int i,j;

				for(i=0,j=0;i<MaxCPUScavs;i++) {
					// Make sure it's still present, on the CPU team, and a vehicle scav.
					if (IsAround(CPUScavList[i]) &&
						 (GetTeamNum(CPUScavList[i])==comp_team) &&
						 IsType(h,"vscav")) {

						// Oldest Scavs get to live. Hopefully, they're the
						// furthest from the recycler.
						if(j<MinCPUScavs)
							CPUScavList[j++]=CPUScavList[i];
						else {
							SetNoScrapFlagByHandle(CPUScavList[i]);
							RemoveObject(CPUScavList[i]);
						}
					}
				} // Loop over all CPU scavs.

				// Clean up rest of list.
				for(i=j;i<MaxCPUScavs;i++) CPUScavList[i]=0;
				NumCPUScavs=j;

			} // Cleanup time triggered.
		}
		else
		// Track all combat vehicles (except portal users):
		{
			if (IsType(h,"vtank") ||
				IsType(h,"vatank") ||
				IsType(h,"vwalk") ||
				IsOdf(h,"ivewalk") ||
				IsType(h,"vscout") ||
				IsType(h,"vsent") ||
				IsType(h,"varch") ||
				IsType(h,"vartl") ||
				IsType(h,"vmisl") ||
				IsType(h,"vrckt") ||
				IsType(h,"vmbike") ||
				IsType(h,"vmort") ||
				IsType(h,"vtanku") ||
				IsType(h,"vatanku") ||
				IsType(h,"vmislu") ||
				IsType(h,"vmislw") ||
				IsType(h,"vmorts") ||
				IsType(h,"vscoutu") ||
				IsType(h,"vtankf") ||
				IsType(h,"vtanks") ||
				IsType(h,"vatankl") ||
				IsType(h,"vatanks") ||
				IsType(h,"vrbomb"))
			{
				for (iX=0; iX<MAX_COMBAT_UNITS; ++iX) {
					if (*(iUnitptr+iX)==NULL) {
						*(iUnitptr+iX)=h;
						break;
					}
				}
			}
		}

		
		//SetSkill(h,difficulty+1);

		//@@@NB_FE_MPI02
		if (iDiffLvl==OPTION_EASY)
		{
			SetSkill(h,1);
		}
		else if (iDiffLvl==OPTION_LOW)
		{
			SetSkill(h,2);
		}
		{
			if (NumHumans>2) SetSkill(h,3);
			else if (NumHumans>1)
			{
				if (mySide==1 && have_sbay) SetSkill(h,3);
				else if (mySide!=1 && have_armory) SetSkill(h,3);
			}
			else if (mySide==1 && have_armory) SetSkill(h,3);
			else if (mySide!=1 && have_sbay) SetSkill(h,3);
			else SetSkill(h,2);
		}


		if ((IsType(h,"barmo")) || (IsOdf(h,"fbstro")))
		{
			have_armory=true;
		}

		//@@@NB_FE_MPI02
		if ((IsType(h,"btrain")) || (IsOdf(h,"fbantm")))
		{
			have_trainctr=true;
		}

		//@@@NB_FE_MPI02
		if ((IsType(h,"btcen")) || (IsOdf(h,"fbover")))
		{
			have_techctr=true;
		}

		//@@@NB_FE_MPI02
		if ((IsType(h,"bsbay")) || (IsOdf(h,"fbdowe")))
		{
			have_sbay=true;
		}


		if (have_armory)
		{
			if (IsOdf(h,"ivtank") || IsOdf(h,"ivputank"))
			{
				GiveWeapon(h,"gspstab_c");
			} 
			else if (IsOdf(h,"ivscout") || IsOdf(h,"ivpuscout"))
			{
				//@@@NB_FE_MPI02
				GiveWeapon(h,"gchain_c");
				if (late_game) GiveWeapon(h,"gbchain_c");
			} 
			else if (IsOdf(h,"ivturr") || IsOdf(h,"ivputurr"))
			{
				//@@@NB_FE_MPI02
				GiveWeapon(h,"gchain_a");
				if (late_game) GiveWeapon(h,"gbchain_a");
			} 
			else if (IsOdf(h,"ivrckt") || IsOdf(h,"ivpurckt"))
			{
				//@@@NB_FE_MPI02
				if (late_game) GiveWeapon(h,"ghornet");
			} 
			
			else if (IsOdf(h,"fvtank") || IsOdf(h,"fvputank"))
			{
				//@@@NB_FE_MPI02
				GiveWeapon(h,"garc_c");
				GiveWeapon(h,"gdeflect");
				if (!early_game) 
				{ 
					GiveWeapon(h,"gabsorb");
				}
			} 
			else if (IsOdf(h,"fvscout") || IsOdf(h,"fvpuscout"))
			{
				//@@@NB_FE_MPI02
				GiveWeapon(h,"gdeflect");
				if (!early_game) 
				{ 
					GiveWeapon(h,"gabsorb");
				}
			} 
			else if (IsOdf(h,"fvsent") || IsOdf(h,"fvpusent"))
			{
				GiveWeapon(h,"gdeflect");
				if (!early_game) 
				{
					//@@@NB_FE_MPI02
					GiveWeapon(h,"gabsorb");
					if (alt_weapon && have_armory)
					{
						GiveWeapon(h,"gcrubex_c");
					}
					else
					{
						GiveWeapon(h,"ggauss_c");
					}
				}
			} 
			else if (IsOdf(h,"fvarch") || IsOdf(h,"fvpuarch"))
			{
				//@@@NB_FE_MPI02
				GiveWeapon(h,"gdeflect");
				if (!early_game) 
				{
					GiveWeapon(h,"gabsorb");
				}
			} 
			
			else if (IsOdf(h,"evtank") || IsOdf(h,"evputank"))
			{
				//@@@NB_FE_MPI02
				if (alt_weapon)
				{
					GiveWeapon(h,"gslagstab_c");
					GiveWeapon(h,"gslicer_c");
				}
			}
			else if (IsOdf(h,"evscout") || IsOdf(h,"evpuscout"))
			{
				//@@@NB_FE_MPI02
				if (alt_weapon) GiveWeapon(h,"gslicer_c");
			}
			else if (IsOdf(h,"evtanku") || IsOdf(h,"evputanku"))
			{
				//@@@NB_FE_MPI02
				if (alt_weapon && have_trainctr)
				{
					GiveWeapon(h,"gfbgun_c");
					GiveWeapon(h,"geburst_c");
				}
			}
			else if (IsOdf(h,"evturr") || IsOdf(h,"evputurr"))
			{
				//@@@NB_FE_MPI02
				if (alt_weapon) GiveWeapon(h,"gcphcg_a");
			}
			else if (IsOdf(h,"evmort") || IsOdf(h,"evpumort"))
			{
				//@@@NB_FE_MPI02
				if (have_techctr || late_game) GiveWeapon(h,"ghfire");
			}
		}
	
		//@@@NB_FE_MPI02
		//initial bomber attack
		if (IsOdf(h,"ivbomb")) 
		{
			Handle hTarget;
			hTarget = GetObjectByTeamSlot(strat_team,DLL_TEAM_SLOT_BOMBERBAY);
			if (!IsAround(hTarget)) 
			{
				hTarget = GetObjectByTeamSlot(strat_team,DLL_TEAM_SLOT_FACTORY);
				if (!IsAround(hTarget)) 
				{
					hTarget = GetObjectByTeamSlot(strat_team,DLL_TEAM_SLOT_RECYCLER);
				}
			}
			Attack(h,hTarget);
			hBomber = h;
			iNextBomberAttack = time_count + ((((time_count/10)%7)+8)*600);
		}
	}

	if (GetTeamNum(h)==strat_team)
	{
		if (IsType(h,"brecy_m"))
		{
			/*
				SetAip here based on turn_counter
			*/

			int stratchoice = turn_counter%2;

			switch (stratchoice)
			{
			case 0:
				//PrintConsoleMessage("Switching to AIP 1.");
				SetCPUAIPlan(mySide,HumanSide,AIPType1);
				break;
			case 1:
				//PrintConsoleMessage("Switching to AIP 3.");
				SetCPUAIPlan(mySide,HumanSide,AIPType3);
				break;
			case 2:
				//PrintConsoleMessage("Switching to AIP 2.");
				SetCPUAIPlan(mySide,HumanSide,AIPType2);
				break;
			}

			//MonsterRun();
		}
		
		//@@@NB_FE_MPI02
		else if ((IsType(h,"bfact")) || (IsOdf(h,"fbkiln"))) {
			if (igroupETrigger==0) {
				// set the alien attack trigger
				igroupETrigger = (turn_counter%2)+1;
			}
		}
		else if ((IsType(h,"barmo")) || (IsOdf(h,"fbstro"))) {
			if (iNextgroupEAttack==0 && igroupETrigger==1) {
				// initialize the alien attack interval
				iNextgroupEAttack = time_count+7200-((NumHumans-1)*900)+((time_count/10)%600);
			}
		}
		else if ((IsType(h,"bsbay")) || (IsOdf(h,"fbdowe"))) {
			if (iNextgroupEAttack==0 && igroupETrigger==2) {
				// initialize the alien attack interval
				iNextgroupEAttack = time_count+7200-((NumHumans-1)*900)+((time_count/10)%600);
			}
		}
		else if ((IsType(h,"bcbun")) || (IsOdf(h,"fbantm"))) {
			if (iNextgroupEAttack==0 && igroupETrigger==3) {
				// initialize the alien attack interval
				iNextgroupEAttack = time_count+7200-((NumHumans-1)*900)+((time_count/10)%600);
			}
		}
		
		else
		{
			if ((IsType(h,"vatank")) 
				|| (IsType(h,"vwalk"))
				|| (IsType(h,"vatanku"))		//@@@NB_FE_MPI02
				|| (IsType(h,"vatankl"))		//@@@NB_FE_MPI02
				|| (IsType(h,"vatanks"))		//@@@NB_FE_MPI02
				|| (IsOdf(h,"ivewalk")))	//@@@NB_FE_MPI02
			{
				assault_counter++;

				//@@@NB_FE_MPI02
				if (anti_assault) {
					groupETarget=h;
					if ((IsType(h,"vatank")) 
						|| (IsType(h,"vatanku"))
						|| (IsType(h,"vatankl"))
						|| (IsType(h,"vatanks"))) 
					{
						groupETargetType=1;
					}
					else 
					{
						groupETargetType=2;
					}
					sendEAttack=true;
				}
			}

			//@@@NB_FE_MPI02
			else if ((IsOdf(h,"ibgtow")) || (IsOdf(h,"fbspir")) 
				|| (IsOdf(h,"ebgt2g")) || (IsOdf(h,"ebgt4g")))
			{
				if (iDiffLvl!=OPTION_LOW)
				{
					hPrevGT=hLastGT;
					hLastGT=h;

					groupETarget=h;
					groupETargetType=3;
					sendEAttack=true;
				}
			}
			else if (IsOdf(h,"ibpgen") || IsOdf(h,"ebpgen"))
			{
				if (iDiffLvl==OPTION_HIGH)
				{
					groupETarget=h;
					groupETargetType=4;
					sendEAttack=true;
				}
			}
			else if (IsOdf(h,"fblung") && ((time_count/10)%3==0))
			{
				if (iDiffLvl==OPTION_HIGH)
				{
					groupETarget=h;
					groupETargetType=4;
					sendEAttack=true;
				}
			}
		}

		//@@@NB_FE_MPI02
		// Set human team unit skill level:
		SetSkill(h,3);
	}	
}


// ______________________________________________________________________
void instantMission::DeleteObject(Handle h)
{
	int iX;		//@@@NB_FE_MPI02

	//@@@NB_FE_MPI02
	if (GetTeamNum(h)==strat_team) 
	{
		if ((IsType(h,"vatank")) 
			|| (IsType(h,"vwalk"))
			|| (IsType(h,"vatanku"))		//@@@NB_FE_MPI02
			|| (IsType(h,"vatankl"))		//@@@NB_FE_MPI02
			|| (IsType(h,"vatanks"))		//@@@NB_FE_MPI02
			|| (IsOdf(h,"ivewalk")))	//@@@NB_FE_MPI02
		{
			--assault_counter;
		}
	}


	//@@@NB_FE_MPI02
	if (GetTeamNum(h)==comp_team)
	{
		//reset armory switch if armory is destroyed
		if ((IsType(h,"barmo")) || (IsOdf(h,"fbstro"))) {
			have_armory=false;
		}

		//@@@NB_FE_MPI02
		else if ((IsType(h,"btrain")) || (IsOdf(h,"fbantm"))) {
			have_trainctr=false;
		}

		//@@@NB_FE_MPI02
		else if ((IsType(h,"btcen")) || (IsOdf(h,"fbover"))) {
			have_techctr=false;
		}

		//@@@NB_FE_MPI02
		else if ((IsType(h,"bsbay")) || (IsOdf(h,"fbdowe"))) {
			have_sbay=false;
		}

		//@@@NB_FE_MPI02
		else if (IsType(h,"vtank") ||
				IsType(h,"vatank") ||
				IsType(h,"vwalk") ||
				IsOdf(h,"ivewalk") ||
				IsType(h,"vscout") ||
				IsType(h,"vsent") ||
				IsType(h,"varch") ||
				IsType(h,"vartl") ||
				IsType(h,"vmisl") ||
				IsType(h,"vrckt") ||
				IsType(h,"vmbike") ||
				IsType(h,"vmort") ||
				IsType(h,"vtanku") ||
				IsType(h,"vatanku") ||
				IsType(h,"vmislu") ||
				IsType(h,"vmislw") ||
				IsType(h,"vmorts") ||
				IsType(h,"vscoutu") ||
				IsType(h,"vtankf") ||
				IsType(h,"vtanks") ||
				IsType(h,"vatankl") ||
				IsType(h,"vatanks") ||
				IsType(h,"vrbomb"))
		{
			for (iX=0; iX<MAX_COMBAT_UNITS; ++iX) {
				if (*(iUnitptr+iX)==h) {
					*(iUnitptr+iX)=NULL;
					break;
				}
			}
		}

	}


	// Make a sniped unit neutral
	Handle Shooter = GetWhoShotMe(h);
	if (IsPlayer(Shooter) && IsPerson(Shooter)
		 && (GetTeamNum(h)==comp_team) && !IsPerson(h))
	{
		SetTeamNum(h, 0);
	}
}


// ______________________________________________________________________
void instantMission::CreateObjectives()
{
}


// ______________________________________________________________________
void instantMission::TestObjectives()
{
	// game end conditions will be handled by the strat code
}


// This function is called every 0.1 seconds.  Due to the net code, its effects on
// the game can be delayed by up to 2 seconds.
// ______________________________________________________________________
void instantMission::Execute(bool *TeamIsSetUp, Handle *RecyclerHandles)
{
	int iX;	//@@@NB_FE_MPI02
	bool dead_portal=false;	//@@@NB_FE_MPI02

	if (!didInit)
	{
		PortalInit();		// Initialize all portals.
		SetPortalUsers();	// Set up the portal user types.
		didInit = true;
	}

	player=GetPlayerHandle();
	turn_counter++;

	//@@@ set random variables here
	alt_weapon = ((((turn_counter/10)%2)==1) ? true : false);	//@@@NB_FE_MPI02

	DoGenericStrategy(TeamIsSetUp, RecyclerHandles);

	// General portal transport.
	if (have_portals) PortalFunctions();	//@@@NB_FE_MPI02

	// If a portal user has been created then send him to his designated portal.
	if (m_PortalUser != 0)
	{
		if (have_portals)	//@@@NB_FE_MPI02
		{
			// Give the unit 1 second after creation, then command it to the portal.
			if (++PortalUserWaitCount > 10)
			{
				if (IsAround(m_PortalToUse))
				{
					Goto(m_PortalUser, m_PortalToUse, 1);
					m_PortalUser = 0;
				}
				else
				{
					dead_portal=true;	//@@@NB_FE_MPI02
				}
			}
		}

		//@@@NB_FE_MPI02
		if (!have_portals || dead_portal)
		{
			switch (m_PortalGroup)
			{
			case 0: 
				Goto(m_PortalUser, "hold1");
				if (!IsType(m_PortalUser,"vputurr"))
				{
					for (iX=0; iX<MAX_HOLD_UNITS; ++iX) {
						if (*(iHold1ptr+iX)==NULL || !IsAround(*(iHold1ptr+iX))) {
							*(iHold1ptr+iX)=m_PortalUser;
							break;
						}
					}
				}
				break;
			case 1: 
				Goto(m_PortalUser, "hold2");
				if (!IsType(m_PortalUser,"vputurr"))
				{
					for (iX=0; iX<MAX_HOLD_UNITS; ++iX) {
						if (*(iHold2ptr+iX)==NULL || !IsAround(*(iHold2ptr+iX))) {
							*(iHold2ptr+iX)=m_PortalUser;
							break;
						}
					}
				}
				break;
			case 2: 
				Goto(m_PortalUser, "hold3");
				if (!IsType(m_PortalUser,"vputurr"))
				{
					for (iX=0; iX<MAX_HOLD_UNITS; ++iX) {
						if (*(iHold3ptr+iX)==NULL || !IsAround(*(iHold3ptr+iX))) {
							*(iHold3ptr+iX)=m_PortalUser;
							break;
						}
					}
				}
				break;
			case 3: 
				Goto(m_PortalUser, "hold4");
				if (!IsType(m_PortalUser,"vputurr"))
				{
					for (iX=0; iX<MAX_HOLD_UNITS; ++iX) {
						if (*(iHold4ptr+iX)==NULL || !IsAround(*(iHold4ptr+iX))) {
							*(iHold4ptr+iX)=m_PortalUser;
							break;
						}
					}
				}
				break;
			case 4: RemoveObject(m_PortalUser); break;	// Cerberi
			default:
				if (IsType(m_PortalUser,"vpuscout") || IsType(m_PortalUser,"vpusent"))
				{
					Patrol(m_PortalUser,"patrolBase1");
				}
				else if (IsType(m_PortalUser,"vputank"))
				{
					Patrol(m_PortalUser,"patrolBase2");
				}
				else
				{
					Handle hTarget;
					hTarget=GetObjectByTeamSlot(1, (((turn_counter/10)%5)+1));
					if (!IsAround(hTarget)) 
					{
						hTarget = GetObjectByTeamSlot(strat_team,DLL_TEAM_SLOT_RECYCLER);
						if (!IsAround(hTarget)) 
						{
							hTarget = GetObjectByTeamSlot(strat_team,DLL_TEAM_SLOT_FACTORY);
						}
					}
					Attack(m_PortalUser, hTarget);
				}
				break;
			}
			m_PortalUser = 0;
		}
	}


	//------------------------------------------------------------------------
	// @@@Centerline_FE_MPI - This section controls when the Rampage units are
	// spawned. Staggered base on game time <30/<60/60+ and difficulty level.
	// The unit ODFs used are hard coded and should be changed to either
	// variables or constants after everything is working correctly.
	//
	// Should be placed in "Execute"

	if (time_count==RampageTimer && (RampageUnitLimit > 0)) // time to kick into gear and spawn some units
	{
		if (siege_on) // Speed things up a bit
		{
			RampageTimer = (time_count+2400)-((NumHumans-1)*400)+((time_count/10)%600); 
		}
		else // Set next normal spawn time
		{
			RampageTimer = (time_count+5400)-((NumHumans-1)*900)+((time_count/10)%600); 
		}

		RampageSpawnTrigger = true;		// Trigger spawning
		RampageSpawnSpacer = 0;			// Spawn now
		RampageUnitsSpawned = 0;		// No units spawned yet this round
	}

	// Create a delay between spawning calls
	if (RampageSpawnSpacer > 0)
	{
		RampageSpawnSpacer--;
	}

	// Reset the spawn trigger once we have built enough units
	if (RampageUnitsSpawned >= RampageUnitLimit)
	{
		RampageSpawnTrigger = false;
	}

	// This section spawns the units
	if (RampageSpawnTrigger && (RampageSpawnSpacer == 0) && (RampageUnitsSpawned < RampageUnitLimit))
	{
		if (siege_on)
		{
			if (early_game)
			{
				switch (iDiffLvl)
				{
					case OPTION_HIGH:
					{
						SpawnRampage ("Rampage2", hRampageUnits, RampageUnitLimit, RampageUnitsSpawned);
						break;
					}
					case OPTION_NORMAL:
					{
						SpawnRampage ("Rampage1", hRampageUnits, RampageUnitLimit, RampageUnitsSpawned);
						break;
					}
					case OPTION_LOW:
					{
						PrintConsoleMessage("We are on LOW so SpawnRampage function has NOT been called.\n");
						break;
					}
					case OPTION_EASY:
					{
						PrintConsoleMessage("We are on EASY so SpawnRampage function has NOT been called.\n");
						break;
					}
				}
			}
			else
			{
				switch (iDiffLvl)
				{
					case OPTION_HIGH:
					{
						int iWhichUnit = (int)GetRandomFloat(3.0);
						if (iWhichUnit > 1)
						{
							SpawnRampage ("Rampage3", hRampageUnits, RampageUnitLimit, RampageUnitsSpawned);
						}
						else
						{
							SpawnRampage ("Rampage2", hRampageUnits, RampageUnitLimit, RampageUnitsSpawned);
						}
						break;
					}
					case OPTION_NORMAL:
					{
						SpawnRampage ("Rampage2", hRampageUnits, RampageUnitLimit, RampageUnitsSpawned);
						break;
					}
					case OPTION_LOW:
					{
						SpawnRampage ("Rampage1", hRampageUnits, RampageUnitLimit, RampageUnitsSpawned);
						break;
					}
					case OPTION_EASY:
					{
						break;
					}
				}
			}
		}
		else if (time_count < 18000)  // less than 30 minutes in
		{
			if (early_game)
			{
				switch (iDiffLvl)
				{
					case OPTION_HIGH:
					{
						SpawnRampage ("Rampage1", hRampageUnits, RampageUnitLimit, RampageUnitsSpawned);
						break;
					}
					case OPTION_NORMAL:
					{
						SpawnRampage ("Rampage1", hRampageUnits, RampageUnitLimit, RampageUnitsSpawned);
						break;
					}
					case OPTION_LOW:
					{
						PrintConsoleMessage("We are on LOW so SpawnRampage function has NOT been called.\n");
						break;
					}
					case OPTION_EASY:
					{
						PrintConsoleMessage("We are on EASY so SpawnRampage function has NOT been called.\n");
						break;
					}
				}
			}
			else
			{
				switch (iDiffLvl)
				{
					case OPTION_HIGH:
					{
						SpawnRampage ("Rampage2", hRampageUnits, RampageUnitLimit, RampageUnitsSpawned);
						break;
					}
					case OPTION_NORMAL:
					{
						SpawnRampage ("Rampage1", hRampageUnits, RampageUnitLimit, RampageUnitsSpawned);
						break;
					}
					case OPTION_LOW:
					{
						PrintConsoleMessage("We are on LOW so SpawnRampage function has NOT been called.\n");
						break;
					}
					case OPTION_EASY:
					{
						PrintConsoleMessage("We are on EASY so SpawnRampage function has NOT been called.\n");
						break;
					}
				}
			}
		}
		else if (time_count < 36000)  // less than 60 minutes in
		{
			if (early_game)
			{
				switch (iDiffLvl)
				{
					case OPTION_HIGH:
					{
						SpawnRampage ("Rampage2", hRampageUnits, RampageUnitLimit, RampageUnitsSpawned);
						break;
					}
					case OPTION_NORMAL:
					{
						SpawnRampage ("Rampage2", hRampageUnits, RampageUnitLimit, RampageUnitsSpawned);
						break;
					}
					case OPTION_LOW:
					{
						PrintConsoleMessage("We are on LOW so SpawnRampage function has NOT been called.\n");
						break;
					}
					case OPTION_EASY:
					{
						PrintConsoleMessage("We are on EASY so SpawnRampage function has NOT been called.\n");
						break;
					}
				}
			}
			else
			{
				switch (iDiffLvl)
				{
					case OPTION_HIGH:
					{
						SpawnRampage ("Rampage2", hRampageUnits, RampageUnitLimit, RampageUnitsSpawned);
						break;
					}
					case OPTION_NORMAL:
					{
						SpawnRampage ("Rampage2", hRampageUnits, RampageUnitLimit, RampageUnitsSpawned);
						break;
					}
					case OPTION_LOW:
					{
						PrintConsoleMessage("We are on LOW so SpawnRampage function has NOT been called.\n");
						break;
					}
					case OPTION_EASY:
					{
						PrintConsoleMessage("We are on EASY so SpawnRampage function has NOT been called.\n");
						break;
					}
				}
			}
		}
		else //the rest of the game
		{
			switch (iDiffLvl)
			{
				case OPTION_HIGH:
				{
					int iWhichUnit = (int)GetRandomFloat(3.0);
					if (iWhichUnit > 1)
					{
						SpawnRampage ("Rampage3", hRampageUnits, RampageUnitLimit, RampageUnitsSpawned);
					}
					else
					{
						SpawnRampage ("Rampage2", hRampageUnits, RampageUnitLimit, RampageUnitsSpawned);
					}	
					break;
				}
				case OPTION_NORMAL:
				{
					SpawnRampage ("Rampage2", hRampageUnits, RampageUnitLimit, RampageUnitsSpawned);
					break;
				}
				case OPTION_LOW:
				{
					PrintConsoleMessage("We are on LOW so SpawnRampage function has NOT been called.\n");
					break;
				}
				case OPTION_EASY:
				{
					PrintConsoleMessage("We are on EASY so SpawnRampage function has NOT been called.\n");
					break;
				}
			}
		}
		
		RampageSpawnSpacer = 20;  // Reset the spacer
		RampageUnitsSpawned++;   // Increment the number of units spawned this cycle
	}
	//------------------------------------------------------------------------

	//	if (iMonsterDelay == time_count)

	if ((iMonsterToggle == 1) && (iMonsterDelay == time_count))
	{
		MonsterRun();
	}
}


//Function modified by BS-er to account for Hadeans.
// ______________________________________________________________________
void instantMission::DoGenericStrategy(bool *TeamIsSetUp, Handle *RecyclerHandles)
{
	int iX;		//@@@NB_FE_MPI02
	
	time_count++;
	
	if (!start_done)
	{
		strat_team=1;

		start_done=true;


		//@@@NB_FE_MPI02
		// Reset team colors:
		ClearTeamColor(strat_team);
		if ((HumanSide==1) && (mySide==1)) { // Both Teams Scion
			SetTeamColor(comp_team, 31, 127, 95);  //green
		}
		else if ((HumanSide==2) && (mySide==2)) { // Both Teams ISDF
			SetTeamColor(comp_team, 31, 95, 127);  //blue
		}
		else if ((HumanSide==3) && (mySide==3)) { // Both Teams Hadean
			SetTeamColor(comp_team, 127, 64, 0);  //red
		}
		else {
			ClearTeamColor(comp_team);
		}


		//@@@!NB_FE_MPI02
		// Set Ally Race
		switch (HumanSide)
		{
		case 1:	//Scion
			switch (mySide)
			{
			case 1:		groupERace=ALLY_ISDF;		break;
			case 2:		groupERace=ALLY_HADEAN;		break;
			case 3:		groupERace=ALLY_CERBERI;	break;
			default:	groupERace=ALLY_CERBERI;	break;
			}
			break;
		case 2:	//ISDF
			switch (mySide)
			{
			case 1:		groupERace=ALLY_HADEAN;		break;
			case 2:		groupERace=ALLY_SCION;		break;
			case 3:		groupERace=ALLY_CERBERI;	break;
			default:	groupERace=ALLY_CERBERI;	break;
			}
			break;
		case 3:	//Hadean
			switch (mySide)
			{
			case 1:		groupERace=ALLY_ISDF;		break;
			case 2:		groupERace=ALLY_SCION;		break;
			case 3:		groupERace=ALLY_CERBERI;	break;
			default:	groupERace=ALLY_CERBERI;	break;
			}
			break;
		default:	groupERace=ALLY_CERBERI;	break;
		}


		SetupExtraVehicles();

		// get Instant My Side
		if (mySide==1)
		{ // CPU is Scion
			enemy_recycler=BuildObject("fvrecy",comp_team,"RecyclerEnemy");
			TeamIsSetUp[comp_team]=true;
			RecyclerHandles[comp_team]=enemy_recycler;

			hTurr1 = BuildObject("fvturr",comp_team,"turretEnemy1");
			hTurr2 = BuildObject("fvturr",comp_team,"turretEnemy2");
			hTurr3 = BuildObject("fvturr",comp_team,"turretEnemy3");
			hTurr4 = BuildObject("fvturr",comp_team,"turretEnemy4");
			hTurr5 = BuildObject("fvturr",comp_team,"turretEnemy5");
			hTurr6 = BuildObject("fvturr",comp_team,"turretEnemy6");
			hTurr7 = BuildObject("fvturr",comp_team,"turretEnemy7");	//@@@Centerline_FE_MPI - increased count of startup units
			hTurr8 = BuildObject("fvturr",comp_team,"turretEnemy8");	//@@@Centerline_FE_MPI - increased count of startup units
			hTurr9 = BuildObject("fvturr",comp_team,"turretEnemy9");	//@@@Centerline_FE_MPI - increased count of startup units
			hTurr10 = BuildObject("fvturr",comp_team,"turretEnemy10");	//@@@Centerline_FE_MPI - increased count of startup units

			BuildObject("fvturr",comp_team,"gtow1");
			BuildObject("fvturr",comp_team,"gtow2");
			BuildObject("fvturr",comp_team,"gtow3");
			BuildObject("fvturr",comp_team,"gtow4");
			BuildObject("fvturr",comp_team,"gtow5");

			BuildObject("fvscout",comp_team,"scoutEnemy1");
			BuildObject("fvscout",comp_team,"scoutEnemy2");
			BuildObject("fvscout",comp_team,"scoutEnemy3");
			BuildObject("fvscout",comp_team,"scoutEnemy4");
			BuildObject("fvscout",comp_team,"scoutEnemy5");

			BuildObject("fvtank",comp_team,"tankEnemy1");
			BuildObject("fvtank",comp_team,"tankEnemy2");
			BuildObject("fvtank",comp_team,"tankEnemy3");

			BuildObject("fbjamm",comp_team,"jamm1");
			BuildObject("fbjamm",comp_team,"jamm2");
		}
		else if (mySide==2)
		{ // CPU is ISDF
			enemy_recycler=BuildObject("ivrecy",comp_team,"RecyclerEnemy");
			TeamIsSetUp[comp_team]=true;
			RecyclerHandles[comp_team]=enemy_recycler;

			hTurr1 = BuildObject("ivturr",comp_team,"turretEnemy1");
			hTurr2 = BuildObject("ivturr",comp_team,"turretEnemy2");
			hTurr3 = BuildObject("ivturr",comp_team,"turretEnemy3");
			hTurr4 = BuildObject("ivturr",comp_team,"turretEnemy4");
			hTurr5 = BuildObject("ivturr",comp_team,"turretEnemy5");
			hTurr6 = BuildObject("ivturr",comp_team,"turretEnemy6");
			hTurr7 = BuildObject("ivturr",comp_team,"turretEnemy7");	//@@@Centerline_FE_MPI - increased count of startup units
			hTurr8 = BuildObject("ivturr",comp_team,"turretEnemy8");	//@@@Centerline_FE_MPI - increased count of startup units
			hTurr9 = BuildObject("ivturr",comp_team,"turretEnemy9");	//@@@Centerline_FE_MPI - increased count of startup units
			hTurr10 = BuildObject("ivturr",comp_team,"turretEnemy10");	//@@@Centerline_FE_MPI - increased count of startup units

			BuildObject("ivturr",comp_team,"gtow1");
			BuildObject("ivturr",comp_team,"gtow2");
			BuildObject("ivturr",comp_team,"gtow3");
			BuildObject("ivturr",comp_team,"gtow4");
			BuildObject("ivturr",comp_team,"gtow5");

			BuildObject("ivscout",comp_team,"scoutEnemy1");
			BuildObject("ivscout",comp_team,"scoutEnemy2");
			BuildObject("ivscout",comp_team,"scoutEnemy3");
			BuildObject("ivscout",comp_team,"scoutEnemy4");
			BuildObject("ivscout",comp_team,"scoutEnemy5");

			BuildObject("ivtank",comp_team,"tankEnemy1");
			BuildObject("ivtank",comp_team,"tankEnemy2");
			BuildObject("ivtank",comp_team,"tankEnemy3");
		}
		else
		{ // CPU is Hadean
			enemy_recycler=BuildObject("evrecy",comp_team,"RecyclerEnemy");
			TeamIsSetUp[comp_team]=true;
			RecyclerHandles[comp_team]=enemy_recycler;

			hTurr1 = BuildObject("evturr",comp_team,"turretEnemy1");
			hTurr2 = BuildObject("evturr",comp_team,"turretEnemy2");
			hTurr3 = BuildObject("evturr",comp_team,"turretEnemy3");
			hTurr4 = BuildObject("evturr",comp_team,"turretEnemy4");
			hTurr5 = BuildObject("evturr",comp_team,"turretEnemy5");
			hTurr6 = BuildObject("evturr",comp_team,"turretEnemy6");
			hTurr7 = BuildObject("evturr",comp_team,"turretEnemy7");	//@@@Centerline_FE_MPI - increased count of startup units
			hTurr8 = BuildObject("evturr",comp_team,"turretEnemy8");	//@@@Centerline_FE_MPI - increased count of startup units
			hTurr9 = BuildObject("evturr",comp_team,"turretEnemy9");	//@@@Centerline_FE_MPI - increased count of startup units
			hTurr10 = BuildObject("evturr",comp_team,"turretEnemy10");	//@@@Centerline_FE_MPI - increased count of startup units

			BuildObject("evturr",comp_team,"gtow1");
			BuildObject("evturr",comp_team,"gtow2");
			BuildObject("evturr",comp_team,"gtow3");
			BuildObject("evturr",comp_team,"gtow4");
			BuildObject("evturr",comp_team,"gtow5");

			BuildObject("evscout",comp_team,"scoutEnemy1");
			BuildObject("evscout",comp_team,"scoutEnemy2");
			BuildObject("evscout",comp_team,"scoutEnemy3");
			BuildObject("evscout",comp_team,"scoutEnemy4");
			BuildObject("evscout",comp_team,"scoutEnemy5");

			BuildObject("evtank",comp_team,"tankEnemy1");
			BuildObject("evtank",comp_team,"tankEnemy2");
			BuildObject("evtank",comp_team,"tankEnemy3");
		}

		int grp=GetFirstEmptyGroup();			
		SetGroup(recycler,grp);

		SetCPUAIPlan(mySide,HumanSide,AIPType0);

		if (mySide==1) // CPU is Scion
			BuildObject("fvscav",comp_team,"ScavengerEnemy");
		else if (mySide==2) // CPU is ISDF
			BuildObject("ivscav",comp_team,"ScavengerEnemy");
		else // CPU is Hadean
			BuildObject("evscav",comp_team,"ScavengerEnemy");

		// power ups?
		BuildObject("apammo",0,"ammo1");
		BuildObject("apammo",0,"ammo2");
		BuildObject("apammo",0,"ammo3");
		BuildObject("aprepa",0,"repair1");
		BuildObject("aprepa",0,"repair2");
		BuildObject("aprepa",0,"repair3");

		SetScrap(comp_team,60);
		SetScrap(1,40);

		//------------------------------------------------------------------------
		//@@@Centerline_FE_MPI - This section spawns the extra weapons crates
		if (SpawnCrates)
		{
			SpawnWeaponCrates();
		}

		//@@@Centerline_FE_MPI - This section spawns extra scrap pools.
		SpawnEasyPools();

		//@@@Centerline_FE_MPI - Build the random scrap pools
		SetupRandomScrapPools(hRandomPools);

		//@@@Centerline_FE_MPI - Build the random scrap pools
		CaptureObjectDone = CaptureObject();

		//------------------------------------------------------------------------

	} // end if start_done not true

	/*
	if (time_count%enemy_reinforcement_time==0)
	{
		if (compForce>0)
		{
			AddScrap(comp_team,10);
		}
	}
	*/

	if ((time_count%100)==0)
	{ // Evaluate this every 10 seconds
		NumHumans=CountPlayers();
	}


	//if (time_count%(30-(compForce*10)-NumHumans+1)==0)  // this speeds up resources
	//	AddScrap(comp_team,1);

	//@@@NB_FE_MPI02
	int iScrapBoostInt = (300-((NumHumans-1)*50));
	if (iScrapBoostInt < 100) iScrapBoostInt = 100;
	if (time_count%iScrapBoostInt==0)
	{
		AddScrap(comp_team,(iDiffLvl*iScrapBstLvl*3)+(NumHumans*iScrapBstLvl*2));

		if (have_sbay) AddScrap(comp_team,(iDiffLvl*iScrapBstLvl));
		if (have_trainctr) AddScrap(comp_team,(iDiffLvl*iScrapBstLvl*2));
		if (have_techctr) AddScrap(comp_team,(iDiffLvl*iScrapBstLvl*3));
	}


	if (time_count%friend_reinforcement_time==0)
	{
		// power ups?
		BuildObject("apammo",0,"ammo1");
		BuildObject("apammo",0,"ammo2");
		BuildObject("apammo",0,"ammo3");
		BuildObject("aprepa",0,"repair1");
		BuildObject("aprepa",0,"repair2");
		BuildObject("aprepa",0,"repair3");

	}


	//@@@NB_FE_MPI02
	//dispatch holding groups
	if (time_count%1200==0)
	{
		iHold1Count=0;
		iHold2Count=0;
		iHold3Count=0;
		iHold4Count=0;

		// Count holding units
		for (iX=0; iX<MAX_HOLD_UNITS; ++iX) {
			if (*(iHold1ptr+iX)!=NULL) {
				if (IsAround(*(iHold1ptr+iX))) ++iHold1Count;
				else *(iHold1ptr+iX)=NULL;
			}
			if (*(iHold2ptr+iX)!=NULL) {
				if (IsAround(*(iHold2ptr+iX))) ++iHold2Count;
				else *(iHold2ptr+iX)=NULL;
			}
			if (*(iHold3ptr+iX)!=NULL) {
				if (IsAround(*(iHold3ptr+iX))) ++iHold3Count;
				else *(iHold3ptr+iX)=NULL;
			}
			if (*(iHold4ptr+iX)!=NULL) {
				if (IsAround(*(iHold4ptr+iX))) ++iHold4Count;
				else *(iHold4ptr+iX)=NULL;
			}
		}

		// Dispatch holding units
		if (iHold1Count>2)
		{
			for (iX=0; iX<MAX_HOLD_UNITS; ++iX) {
				if (*(iHold1ptr+iX)!=NULL) {
					if (IsAround(*(iHold1ptr+iX))) {
						Handle hTarget;
						hTarget=GetObjectByTeamSlot(1, ((iX%5)+1));
						if (!IsAround(hTarget)) {
							hTarget = GetObjectByTeamSlot(strat_team,DLL_TEAM_SLOT_RECYCLER);
							if (!IsAround(hTarget)) hTarget = GetObjectByTeamSlot(strat_team,DLL_TEAM_SLOT_FACTORY);
						}
						Attack(*(iHold1ptr+iX), hTarget);
					}
					else *(iHold1ptr+iX)=NULL;
				}
			}
		}

		if (iHold2Count>2)
		{
			for (iX=0; iX<MAX_HOLD_UNITS; ++iX) {
				if (*(iHold2ptr+iX)!=NULL) {
					if (IsAround(*(iHold2ptr+iX))) {
						Handle hTarget;
						hTarget=GetObjectByTeamSlot(1, ((iX%5)+1));
						if (!IsAround(hTarget)) {
							hTarget = GetObjectByTeamSlot(strat_team,DLL_TEAM_SLOT_RECYCLER);
							if (!IsAround(hTarget)) hTarget = GetObjectByTeamSlot(strat_team,DLL_TEAM_SLOT_FACTORY);
						}
						Attack(*(iHold2ptr+iX), hTarget);
					}
					else *(iHold2ptr+iX)=NULL;
				}
			}
		}

		if (iHold3Count>1)
		{
			for (iX=0; iX<MAX_HOLD_UNITS; ++iX) {
				if (*(iHold3ptr+iX)!=NULL) {
					if (IsAround(*(iHold3ptr+iX))) {
						Handle hTarget;
						hTarget=GetObjectByTeamSlot(1, ((iX%5)+1));
						if (!IsAround(hTarget)) {
							hTarget = GetObjectByTeamSlot(strat_team,DLL_TEAM_SLOT_RECYCLER);
							if (!IsAround(hTarget)) hTarget = GetObjectByTeamSlot(strat_team,DLL_TEAM_SLOT_FACTORY);
						}
						Attack(*(iHold3ptr+iX), hTarget);
					}
					else *(iHold3ptr+iX)=NULL;
				}
			}
		}

		if (iHold4Count>1)
		{
			for (iX=0; iX<MAX_HOLD_UNITS; ++iX) {
				if (*(iHold4ptr+iX)!=NULL) {
					if (IsAround(*(iHold4ptr+iX))) {
						Handle hTarget;
						hTarget=GetObjectByTeamSlot(1, ((iX%5)+1));
						if (!IsAround(hTarget)) {
							hTarget = GetObjectByTeamSlot(strat_team,DLL_TEAM_SLOT_RECYCLER);
							if (!IsAround(hTarget)) hTarget = GetObjectByTeamSlot(strat_team,DLL_TEAM_SLOT_FACTORY);
						}
						Attack(*(iHold4ptr+iX), hTarget);
					}
					else *(iHold4ptr+iX)=NULL;
				}
			}
		}
	}


	//@@@NB_FE_MPI02
	//dispatch bomber attack
	if ((mySide==2) && (IsAround(hBomber)) && (time_count==iNextBomberAttack)) { 
		Handle hTarget=NULL;
		if (((time_count/10)%5)>2) {
			if (IsAround(hLastGT)) hTarget=hLastGT;
			else if (IsAround(hPrevGT)) hTarget=hPrevGT;
		}
		if (hTarget==NULL) {
			switch ((time_count/10)%5) {
			case 0:
				hTarget = GetObjectByTeamSlot(strat_team,DLL_TEAM_SLOT_BOMBERBAY);
				if (!IsAround(hTarget)) {
					hTarget = GetObjectByTeamSlot(strat_team,DLL_TEAM_SLOT_FACTORY);
					if (!IsAround(hTarget)) {
						hTarget = GetObjectByTeamSlot(strat_team,DLL_TEAM_SLOT_RECYCLER);
					}
				}
				break;
			case 1:
				hTarget = GetObjectByTeamSlot(strat_team,DLL_TEAM_SLOT_BOMBERBAY);
				if (!IsAround(hTarget)) {
					hTarget = GetObjectByTeamSlot(strat_team,DLL_TEAM_SLOT_RECYCLER);
					if (!IsAround(hTarget)) {
						hTarget = GetObjectByTeamSlot(strat_team,DLL_TEAM_SLOT_FACTORY);
					}
				}
				break;
			default:
				hTarget=GetNearestEnemy(hBomber);
				if (!hTarget) {
					hTarget = GetObjectByTeamSlot(strat_team,DLL_TEAM_SLOT_TRAINING);
					if (!IsAround(hTarget)) {
						hTarget = GetObjectByTeamSlot(strat_team,DLL_TEAM_SLOT_ARMORY);
						if (!IsAround(hTarget)) {
							hTarget = GetObjectByTeamSlot(strat_team,DLL_TEAM_SLOT_FACTORY);
							if (!IsAround(hTarget)) {
								hTarget = GetObjectByTeamSlot(strat_team,DLL_TEAM_SLOT_RECYCLER);
							}
						}
					}
				}
				break;
			}
		}

		Attack(hBomber,hTarget);

		if (!siege_on) {
			iNextBomberAttack = time_count + ((((time_count/10)%7)+8)*600);
		} else {
			iNextBomberAttack = time_count + ((((time_count/10)%7)+8)*300);
		}
	}


	// AIP switch logic
	if (!siege_on)
	{
		Handle enemy=GetNearestEnemy(enemy_recycler);

		//@@@NB_FE_MPI02
		//if (GetDistance(enemy,enemy_recycler)<300.0f)
		if (GetDistance(enemy,enemy_recycler)<=cordon_sanitaire) {
			if (siege_counter>0) {
				siege_counter++;	// under siege
			}
			else if (!IsPlayer(enemy)) {
				siege_counter++;	// under siege
			}
		}
		else {
			siege_counter=0;
		}

		//@@@NB_FE_MPI02
		//if (siege_counter>450)
		if (siege_counter>siege_switchtime)
		{ // 45 sec, if we don't change the sim rate :-)
			siege_on=true;
			early_game=false;
			anti_assault=false;
			late_game=false;

			//PrintConsoleMessage("Switching to Anti-Siege.");

			SetCPUAIPlan(mySide,HumanSide,AIPTypeS);

			//@@@NB_FE_MPI02
			if (iNextgroupEAttack>0) iNextgroupEAttack = time_count+100;

			//@@@NB_FE_MPI02
			//replace lost turrets
			if (!IsAround(hTurr1)) {
				if (mySide==1) hTurr1 = BuildObject("fvturr",comp_team,"turretEnemy1");
				if (mySide==2) hTurr1 = BuildObject("ivturr",comp_team,"turretEnemy1");
				if (mySide==3) hTurr1 = BuildObject("evturr",comp_team,"turretEnemy1");
			}
			if (!IsAround(hTurr2)) {
				if (mySide==1) hTurr2 = BuildObject("fvturr",comp_team,"turretEnemy2");
				if (mySide==2) hTurr2 = BuildObject("ivturr",comp_team,"turretEnemy2");
				if (mySide==3) hTurr2 = BuildObject("evturr",comp_team,"turretEnemy2");
			}
			if (!IsAround(hTurr3)) {
				if (mySide==1) hTurr3 = BuildObject("fvturr",comp_team,"turretEnemy3");
				if (mySide==2) hTurr3 = BuildObject("ivturr",comp_team,"turretEnemy3");
				if (mySide==3) hTurr3 = BuildObject("evturr",comp_team,"turretEnemy3");
			}
			if (!IsAround(hTurr4)) {
				if (mySide==1) hTurr4 = BuildObject("fvturr",comp_team,"turretEnemy4");
				if (mySide==2) hTurr4 = BuildObject("ivturr",comp_team,"turretEnemy4");
				if (mySide==3) hTurr4 = BuildObject("evturr",comp_team,"turretEnemy4");
			}
			if (!IsAround(hTurr5)) {
				if (mySide==1) hTurr5 = BuildObject("fvturr",comp_team,"turretEnemy5");
				if (mySide==2) hTurr5 = BuildObject("ivturr",comp_team,"turretEnemy5");
				if (mySide==3) hTurr5 = BuildObject("evturr",comp_team,"turretEnemy5");
			}
			if (!IsAround(hTurr6)) {
				if (mySide==1) hTurr6 = BuildObject("fvturr",comp_team,"turretEnemy6");
				if (mySide==2) hTurr6 = BuildObject("ivturr",comp_team,"turretEnemy6");
				if (mySide==3) hTurr6 = BuildObject("evturr",comp_team,"turretEnemy6");
			}
			if (!IsAround(hTurr7)) {	//@@@Centerline_FE_MPI - increased count of startup units
				if (mySide==1) hTurr7 = BuildObject("fvturr",comp_team,"turretEnemy7");
				if (mySide==2) hTurr7 = BuildObject("ivturr",comp_team,"turretEnemy7");
				if (mySide==3) hTurr7 = BuildObject("evturr",comp_team,"turretEnemy7");
			}
			if (!IsAround(hTurr8)) {	//@@@Centerline_FE_MPI - increased count of startup units
				if (mySide==1) hTurr8 = BuildObject("fvturr",comp_team,"turretEnemy8");
				if (mySide==2) hTurr8 = BuildObject("ivturr",comp_team,"turretEnemy8");
				if (mySide==3) hTurr8 = BuildObject("evturr",comp_team,"turretEnemy8");
			}
			if (!IsAround(hTurr9)) {	//@@@Centerline_FE_MPI - increased count of startup units
				if (mySide==1) hTurr9 = BuildObject("fvturr",comp_team,"turretEnemy9");
				if (mySide==2) hTurr9 = BuildObject("ivturr",comp_team,"turretEnemy9");
				if (mySide==3) hTurr9 = BuildObject("evturr",comp_team,"turretEnemy9");
			}
			if (!IsAround(hTurr10)) {	//@@@Centerline_FE_MPI - increased count of startup units
				if (mySide==1) hTurr10 = BuildObject("fvturr",comp_team,"turretEnemy10");
				if (mySide==2) hTurr10 = BuildObject("ivturr",comp_team,"turretEnemy10");
				if (mySide==3) hTurr10 = BuildObject("evturr",comp_team,"turretEnemy10");
			}
		
			//@@@NB_FE_MPI02
			// Send combat vehicles to patrol or hold:
			for (iX=0; iX<MAX_COMBAT_UNITS; ++iX) {
				if (*(iUnitptr+iX)!=NULL) {
					if (IsAround(*(iUnitptr+iX))) {
						if (IsType(h,"vscout") ||
							IsType(h,"vscoutu") ||
							IsType(h,"vsent"))
						{
							Patrol(*(iUnitptr+iX),"patrolBase1");
						}
						else if (IsType(h,"vtank") ||
							IsType(h,"vmisl") ||
							IsType(h,"varch") ||
							IsType(h,"vmort") ||
							IsType(h,"vtanku") ||
							IsType(h,"vmislw") ||
							IsType(h,"vmorts") ||
							IsType(h,"vtankf") ||
							IsType(h,"vtanks") ||
							IsType(h,"vmislu"))
						{
							Patrol(*(iUnitptr+iX),"patrolBase2");
						}
						else if (IsType(h,"vatank") ||
							IsType(h,"vwalk") ||
							IsOdf(h,"ivewalk") ||
							IsType(h,"vartl") ||
							IsType(h,"vrckt") ||
							IsType(h,"vmbike") ||
							IsType(h,"vatanku") ||
							IsType(h,"vatankl") ||
							IsType(h,"vatanks") ||
							IsType(h,"vrbomb"))
						{
							switch (iX%3)
							{
								case 0:		Goto(*(iUnitptr+iX), "hold1"); break;
								case 1:		Goto(*(iUnitptr+iX), "hold2"); break;
								case 2:		Goto(*(iUnitptr+iX), "hold3"); break;
								default:	Goto(*(iUnitptr+iX), "hold1"); break;
							}
						}
					}
					else {
						// the unit died somewhere, clear the wreckage
						*(iUnitptr+iX)=NULL;
					}
				}
			}


			//@@@NB_FE_MPI02
			//if isdf, dispatch the bomber immediately, if it exists
			if ((mySide==2) && (IsAround(hBomber))) { 
				Handle hTarget;
				hTarget = GetObjectByTeamSlot(strat_team,DLL_TEAM_SLOT_RECYCLER);
				if (!IsAround(hTarget)) {
					hTarget = GetObjectByTeamSlot(strat_team,DLL_TEAM_SLOT_FACTORY);
				}
				Attack(hBomber,hTarget);
				iNextBomberAttack = time_count + ((((time_count/10)%7)+8)*300);
			}
		}
	}  // !siege_on
	else
	{
		Handle enemy=GetNearestEnemy(enemy_recycler);

		//@@@NB_FE_MPI02
		//if (GetDistance(enemy,enemy_recycler)>301.0f)
		if (GetDistance(enemy,enemy_recycler)>cordon_sanitaire)
		{
			//			SetPlan("scioninstl.aip",comp_team);
			siege_on=false;
			early_game=false;
			anti_assault=false;
			late_game=true;

			//PrintConsoleMessage("Switching to Late Game.");

			SetCPUAIPlan(mySide,HumanSide,AIPTypeL);

			//@@@NB_FE_MPI02
			if (iNextgroupEAttack>0) iNextgroupEAttack = time_count+100;
			if (iDiffLvl!=OPTION_LOW) drath_rush=true;

			//@@@NB_FE_MPI02
			// Send combat vehicles to attack:
			for (iX=0; iX<MAX_COMBAT_UNITS; ++iX) {
				if (*(iUnitptr+iX)!=NULL) {
					if (IsAround(*(iUnitptr+iX))) {
						Handle hTarget;
						hTarget=GetObjectByTeamSlot(1, ((iX%5)+1));
						if (!IsAround(hTarget)) 
						{
							hTarget = GetObjectByTeamSlot(strat_team,DLL_TEAM_SLOT_RECYCLER);
							if (!IsAround(hTarget)) 
							{
								hTarget = GetObjectByTeamSlot(strat_team,DLL_TEAM_SLOT_FACTORY);
							}
						}
						Attack(*(iUnitptr+iX), hTarget);
					}
					else {
						// the unit died somewhere, no ghosts allowed
						*(iUnitptr+iX)=NULL;
					}
				}
			}

		}
	}  // siege_on

	// assault_test
	if ((early_game || late_game) && (assault_counter>2))
	{
		anti_assault=true;
		early_game=false;
		late_game=false;
		siege_on=false;

		//PrintConsoleMessage("Switching to Anti-Assault.");

		SetCPUAIPlan(mySide,HumanSide,AIPTypeA);

		//@@@NB_FE_MPI02
		if (iNextgroupEAttack>0) iNextgroupEAttack = time_count+100;

		//@@@NB_FE_MPI02
		// Send combat vehicles to attack:
		for (iX=0; iX<MAX_COMBAT_UNITS; ++iX) {
			if (*(iUnitptr+iX)!=NULL) {
				if (IsAround(*(iUnitptr+iX))) {
					Handle hTarget;
					hTarget=GetObjectByTeamSlot(1, ((iX%5)+1));
					if (!IsAround(hTarget)) 
					{
						hTarget = GetObjectByTeamSlot(strat_team,DLL_TEAM_SLOT_RECYCLER);
						if (!IsAround(hTarget)) 
						{
							hTarget = GetObjectByTeamSlot(strat_team,DLL_TEAM_SLOT_FACTORY);
						}
					}
					Attack(*(iUnitptr+iX), hTarget);
				}
				else {
					// the unit died somewhere, out of sight, out of mind
					*(iUnitptr+iX)=NULL;
				}
			}
		}
	} else
	{
		if (anti_assault && (assault_counter<3))
		{
			anti_assault=false;
			early_game=false;
			late_game=true;
			siege_on=false;

			//PrintConsoleMessage("Switching to Late Game.");

			SetCPUAIPlan(mySide,HumanSide,AIPTypeL);
			
			//@@@NB_FE_MPI02
			// Send combat vehicles to attack:
			for (iX=0; iX<MAX_COMBAT_UNITS; ++iX) {
				if (*(iUnitptr+iX)!=NULL) {
					if (IsAround(*(iUnitptr+iX))) {
						Handle hTarget;
						hTarget=GetObjectByTeamSlot(1, ((iX%5)+1));
						if (!IsAround(hTarget)) 
						{
							hTarget = GetObjectByTeamSlot(strat_team,DLL_TEAM_SLOT_RECYCLER);
							if (!IsAround(hTarget)) 
							{
								hTarget = GetObjectByTeamSlot(strat_team,DLL_TEAM_SLOT_FACTORY);
							}
						}
						Attack(*(iUnitptr+iX), hTarget);
					}
					else {
						// the unit died somewhere, sweep away the debris
						*(iUnitptr+iX)=NULL;
					}
				}
			}
		}
	}

	//@@@NB_FE_MPI02
	// build alien off-map reinforcements
	//
	//@@@Centerline_FE_MPI - modified to check if a DestinationPortal exists before we make
	// more units. If no DestinationPortal exists for groupE then we shouldn't build anymore
	// units.
	//
	//if (have_groupE && IsAround(groupEPortal))
	if (have_groupE && IsAround(groupEPortal) && DestinationPortal(PortalCount, PortalNumber, Portals))
	{
		Handle hTemp;
		int tmpint;
		int iX;

		if (sendEAttack)
		{
			if (groupETargetType==1)	// assault tank
			{
				//PrintConsoleMessage("Cerberi should attack tank.");

				if (groupERace==ALLY_CERBERI) hTemp=BuildObject("cvpurbomb", comp_team, "groupE");
				else if (groupERace==ALLY_HADEAN) hTemp=BuildObject("avpuemisl2", comp_team, "groupE");
				else if (groupERace==ALLY_ISDF) hTemp=BuildObject("avpuirbmb2", comp_team, "groupE");
				else if (groupERace==ALLY_SCION) hTemp=BuildObject("avpufrbmb2", comp_team, "groupE");
				Goto(hTemp, groupEPortal, 1);

				for (iX=0; iX<(NumHumans+1); iX++) {
					if ((turn_counter/10)%(2+iX)>0) {
						if (groupERace==ALLY_CERBERI) hTemp=BuildObject("cvputank", comp_team, "groupE");
						else if (groupERace==ALLY_HADEAN) hTemp=BuildObject("avpuemisl2", comp_team, "groupE");
						else if (groupERace==ALLY_ISDF) hTemp=BuildObject("avpuirbmb2", comp_team, "groupE");
						else if (groupERace==ALLY_SCION) hTemp=BuildObject("avpufrbmb2", comp_team, "groupE");
						Goto(hTemp, groupEPortal, 1);
					}
				}
			}
			else if (groupETargetType==2)	// walker
			{
				//PrintConsoleMessage("Cerberi should attack walker.");

				//@@@Centerline_FE_MPI - Reenabled the cvpuwalk and disabled the cvpurbomb
				if (groupERace==ALLY_CERBERI) hTemp=BuildObject("cvpuwalk", comp_team, "groupE");
				//if (groupERace==ALLY_CERBERI) hTemp=BuildObject("cvpurbomb", comp_team, "groupE");
				else if (groupERace==ALLY_HADEAN) hTemp=BuildObject("avpueatnk2", comp_team, "groupE");
				else if (groupERace==ALLY_ISDF) hTemp=BuildObject("avpuirckt2", comp_team, "groupE");
				else if (groupERace==ALLY_SCION) hTemp=BuildObject("avpufwalk2", comp_team, "groupE");
				Goto(hTemp, groupEPortal, 1);

				for (iX=0; iX<(NumHumans+1); iX++) {
					if ((turn_counter/10)%(2+iX)>0) {
						if (groupERace==ALLY_CERBERI) hTemp=BuildObject("cvputank", comp_team, "groupE");
						else if (groupERace==ALLY_HADEAN) hTemp=BuildObject("avpuemisl2", comp_team, "groupE");
						else if (groupERace==ALLY_ISDF) hTemp=BuildObject("avpuirbmb2", comp_team, "groupE");
						else if (groupERace==ALLY_SCION) hTemp=BuildObject("avpufrbmb2", comp_team, "groupE");
						Goto(hTemp, groupEPortal, 1);
					}
				}
			}
			else if (groupETargetType==3)	// tower
			{
				//PrintConsoleMessage("Cerberi should attack tower.");

				if (iDiffLvl==OPTION_HIGH)
				{
					if (late_game || anti_assault) {
						if (groupERace==ALLY_CERBERI) hTemp=BuildObject("cvpurbomb", comp_team, "groupE");
						else if (groupERace==ALLY_HADEAN) hTemp=BuildObject("avpuemisl2", comp_team, "groupE");
						else if (groupERace==ALLY_ISDF) hTemp=BuildObject("avpuirbmb2", comp_team, "groupE");
						else if (groupERace==ALLY_SCION) hTemp=BuildObject("avpufrbmb2", comp_team, "groupE");
						Goto(hTemp, groupEPortal, 1);
					}
					else {
						if (groupERace==ALLY_CERBERI) hTemp=BuildObject("cvputank", comp_team, "groupE");
						else if (groupERace==ALLY_HADEAN) hTemp=BuildObject("avpuemisl2", comp_team, "groupE");
						else if (groupERace==ALLY_ISDF) hTemp=BuildObject("avpuirbmb2", comp_team, "groupE");
						else if (groupERace==ALLY_SCION) hTemp=BuildObject("avpufrbmb2", comp_team, "groupE");
						Goto(hTemp, groupEPortal, 1);
					}
				}

				if (!IsOdf(groupETarget,"ebgt2g")) {
					//if (groupERace==ALLY_CERBERI) hTemp=BuildObject("cvpuatank", comp_team, "groupE");
					if (groupERace==ALLY_CERBERI) hTemp=BuildObject("cvpurbomb", comp_team, "groupE");
					else if (groupERace==ALLY_HADEAN) hTemp=BuildObject("avpuemisl2", comp_team, "groupE");
					else if (groupERace==ALLY_ISDF) hTemp=BuildObject("avpuirbmb2", comp_team, "groupE");
					else if (groupERace==ALLY_SCION) hTemp=BuildObject("avpufrbmb2", comp_team, "groupE");
					Goto(hTemp, groupEPortal, 1);
				}

				if (groupERace==ALLY_HADEAN) hTemp=BuildObject("avpuesct2", comp_team, "groupE");
				else if (groupERace==ALLY_ISDF) hTemp=BuildObject("avpuimbk2", comp_team, "groupE");
				else if (groupERace==ALLY_SCION) hTemp=BuildObject("avpufsct2", comp_team, "groupE");
				if (groupERace!=ALLY_CERBERI) Goto(hTemp, groupEPortal, 1);

				for (iX=0; iX<(NumHumans+2); iX++) {
					if ((turn_counter/10)%(2+iX)>0) {
						if (groupERace==ALLY_CERBERI) hTemp=BuildObject("cvpuscout", comp_team, "groupE");
						else if (groupERace==ALLY_HADEAN) hTemp=BuildObject("avpuesct2", comp_team, "groupE");
						else if (groupERace==ALLY_ISDF) hTemp=BuildObject("avpuimbk2", comp_team, "groupE");
						else if (groupERace==ALLY_SCION) hTemp=BuildObject("avpufsct2", comp_team, "groupE");
						Goto(hTemp, groupEPortal, 1);
					}
				}
			}
			else if (groupETargetType==4)	// power
			{
				//PrintConsoleMessage("Cerberi should attack power.");

				if (late_game || anti_assault) {
					if (groupERace==ALLY_CERBERI) hTemp=BuildObject("cvpurbomb", comp_team, "groupE");
					else if (groupERace==ALLY_HADEAN) hTemp=BuildObject("avpuemisl2", comp_team, "groupE");
					else if (groupERace==ALLY_ISDF) hTemp=BuildObject("avpuirbmb2", comp_team, "groupE");
					else if (groupERace==ALLY_SCION) hTemp=BuildObject("avpufrbmb2", comp_team, "groupE");
					Goto(hTemp, groupEPortal, 1);
				}

				if (groupERace==ALLY_HADEAN) hTemp=BuildObject("avpuesct2", comp_team, "groupE");
				else if (groupERace==ALLY_ISDF) hTemp=BuildObject("avpuimbk2", comp_team, "groupE");
				else if (groupERace==ALLY_SCION) hTemp=BuildObject("avpufsct2", comp_team, "groupE");
				if (groupERace!=ALLY_CERBERI) Goto(hTemp, groupEPortal, 1);

				for (iX=0; iX<(NumHumans+1); iX++) {
					if ((turn_counter/10)%(3+iX)>0) {
						if (groupERace==ALLY_CERBERI) hTemp=BuildObject("cvpuscout", comp_team, "groupE");
						else if (groupERace==ALLY_HADEAN) hTemp=BuildObject("avpuesct2", comp_team, "groupE");
						else if (groupERace==ALLY_ISDF) hTemp=BuildObject("avpuimbk2", comp_team, "groupE");
						else if (groupERace==ALLY_SCION) hTemp=BuildObject("avpufsct2", comp_team, "groupE");
						Goto(hTemp, groupEPortal, 1);
					}
				}
			}
			sendEAttack=false;
		}

		if (time_count==iNextgroupEAttack)
		{
			//PrintConsoleMessage("Cerberi should attack base.");

			// Drath rush
			if (drath_rush && (iDiffLvl==OPTION_HIGH))
			{
				for (iX=0; iX<(NumHumans+2); iX++) {
					if ((turn_counter/10)%(2+iX)>0) {
						//@@@Centerline_FE_MPI - Reenabled the cvpuwalk and disabled the cvpurbomb
						if (groupERace==ALLY_CERBERI) hTemp=BuildObject("cvpuwalk", comp_team, "groupE");
						//if (groupERace==ALLY_CERBERI) hTemp=BuildObject("cvpurbomb", comp_team, "groupE");
						else if (groupERace==ALLY_HADEAN) hTemp=BuildObject("avpueatnk2", comp_team, "groupE");
						else if (groupERace==ALLY_ISDF) hTemp=BuildObject("avpuirckt2", comp_team, "groupE");
						else if (groupERace==ALLY_SCION) hTemp=BuildObject("avpufwalk2", comp_team, "groupE");
						Goto(hTemp, groupEPortal, 1);
					}
				}
				drath_rush=false;
			}

			if (iDiffLvl==OPTION_HIGH)
			{
				for (iX=0; iX<(NumHumans+1); iX++) {
					if ((turn_counter/10)%(3+iX)>0) {
						//if (groupERace==ALLY_CERBERI) hTemp=BuildObject("cvpuwalk", comp_team, "groupE");
						if (groupERace==ALLY_CERBERI) hTemp=BuildObject("cvpurbomb", comp_team, "groupE");
						else if (groupERace==ALLY_HADEAN) hTemp=BuildObject("avpueatnk2", comp_team, "groupE");
						else if (groupERace==ALLY_ISDF) hTemp=BuildObject("avpuirckt2", comp_team, "groupE");
						else if (groupERace==ALLY_SCION) hTemp=BuildObject("avpufwalk2", comp_team, "groupE");
						Goto(hTemp, groupEPortal, 1);
					}
				}
			}

			if (iDiffLvl==OPTION_NORMAL)
			{
				for (iX=0; iX<(NumHumans+1); iX++) {
					if ((turn_counter/10)%(3+iX)>0) {
						//if (groupERace==ALLY_CERBERI) hTemp=BuildObject("cvpuatank", comp_team, "groupE");
						if (groupERace==ALLY_CERBERI) hTemp=BuildObject("cvpurbomb", comp_team, "groupE");
						else if (groupERace==ALLY_HADEAN) hTemp=BuildObject("avpuemisl2", comp_team, "groupE");
						else if (groupERace==ALLY_ISDF) hTemp=BuildObject("avpuirbmb2", comp_team, "groupE");
						else if (groupERace==ALLY_SCION) hTemp=BuildObject("avpufrbmb2", comp_team, "groupE");
						Goto(hTemp, groupEPortal, 1);
					}
				}
			}

			if ((NumHumans>1) && (iDiffLvl!=OPTION_LOW)) {
				for (iX=0; iX<(NumHumans-1); iX++) {
					if ((turn_counter/10)%(2+iX)>0) {
						if (groupERace==ALLY_CERBERI) hTemp=BuildObject("cvpurbomb", comp_team, "groupE");
						else if (groupERace==ALLY_HADEAN) hTemp=BuildObject("avpuemisl2", comp_team, "groupE");
						else if (groupERace==ALLY_ISDF) hTemp=BuildObject("avpuirbmb2", comp_team, "groupE");
						else if (groupERace==ALLY_SCION) hTemp=BuildObject("avpufrbmb2", comp_team, "groupE");
						Goto(hTemp, groupEPortal, 1);
					}
				}
			}

			if (groupERace==ALLY_HADEAN) hTemp=BuildObject("avpuesct2", comp_team, "groupE");
			else if (groupERace==ALLY_ISDF) hTemp=BuildObject("avpuimbk2", comp_team, "groupE");
			else if (groupERace==ALLY_SCION) hTemp=BuildObject("avpufsct2", comp_team, "groupE");
			if (groupERace!=ALLY_CERBERI) Goto(hTemp, groupEPortal, 1);

			for (iX=0; iX<(NumHumans+1); iX++) {
				if ((turn_counter/10)%(3+iX)>0) {
					if (groupERace==ALLY_CERBERI) hTemp=BuildObject("cvpuscout", comp_team, "groupE");
					else if (groupERace==ALLY_HADEAN) hTemp=BuildObject("avpuesct2", comp_team, "groupE");
					else if (groupERace==ALLY_ISDF) hTemp=BuildObject("avpuimbk2", comp_team, "groupE");
					else if (groupERace==ALLY_SCION) hTemp=BuildObject("avpufsct2", comp_team, "groupE");
					Goto(hTemp, groupEPortal, 1);
				}
			}

			if (early_game) tmpint=6600;
			else tmpint=5400;
			if (iDiffLvl==OPTION_LOW) tmpint = tmpint+3000; 
			tmpint = tmpint-((NumHumans-1)*900);
			tmpint = tmpint+((time_count/10)%600);
			if (siege_on) tmpint = tmpint/2;
			iNextgroupEAttack = time_count+tmpint;
		}
	}

	//@@@Centerline_FE_MPI - This section spawns the extra weapons crates
	// at random locations near the human teams pspawn location. That is
	// if the commander has enabled weapons crate spawning in shell ivar36.
	// ______________________________________________________________________
	if (SpawnCrates)
	{
		if (SpawnCratesSpacer == time_count)
		{
			SpawnWeaponCrates();

			// Setup when crates should spawn next
			SpawnCratesSpacer = time_count + (1200 + ((NumHumans - 1)* 250) + (time_count%525));
			//char tempstr[64];
			//sprintf(tempstr, "SpawnCrateSpacer = %i\n", SpawnCratesSpacer);
			//PrintConsoleMessage(tempstr);
			//LogToFile(tempstr);
		}
	}

	TestObjectives();

	//@@@Centerline_FE_MPI
	if (!CaptureObjectDone)
	{
		CaptureObjectDone = CaptureObject();
	}
}


// ______________________________________________________________________
EjectKillRetCodes instantMission::PlayerEjected(void)
{
	return DoEjectPilot;
}


// ______________________________________________________________________
EjectKillRetCodes instantMission::PlayerKilled(int KillersHandle)
{
	return DoEjectPilot;  // Game over, man.
}


// This function initializes all portals that are labelled PortalA0 ,
// PortalB1 etc. up to PortalF9.
// Function by BS-er
// _________________________________________________________________
void instantMission::PortalInit()
{
	char PortalLabel[10] = "PortalA0";
	Handle TempHandle;
	PortalCount = 0;

	for (int i=0; i<10; i++)
	{
		for (int j=0; j<6; j++)
		{
			PortalLabel[6] = 'A' + j;
			TempHandle = GetHandle(PortalLabel);
			if (IsAround(TempHandle))
			{
				Vector PortalPos;
				GetPosition(TempHandle, PortalPos);
				Handle Nav = BuildObject("ibnav",0,PortalPos);
				SetObjectiveName(Nav, "Portal");
				Portals[PortalCount] = TempHandle;
				PortalGroup[PortalCount] = j;
				PortalNumber[PortalCount] = PortalLabel[7] - 48;	//@@@NB_FE_MPI02

				//@@@NB_FE_MPI02
				if (PortalGroup[PortalCount]==4) 
				{
					have_groupE=true;
					if (PortalNumber[PortalCount]==0) 
					{
						groupEPortal=Portals[PortalCount];
					}
				}
/*
#ifdef _BZDEBUG
				//PrintConsoleMessage("----------");
				char tmpstr[64];
				sprintf(tmpstr, "PortalInit: %s", PortalLabel);
				PrintConsoleMessage(tmpstr);
				//sprintf(tmpstr, "PortalGroup: %d", PortalGroup[PortalCount]);
				//PrintConsoleMessage(tmpstr);
				//sprintf(tmpstr, "PortalNumber: %d", PortalNumber[PortalCount]);
				//PrintConsoleMessage(tmpstr);
#endif
*/
				++PortalCount;
				if (PortalCount >= MaxPortals)
				{
					return;
				}
			}
		}
		PortalLabel[6] = 'A';
		PortalLabel[7]++;
	}

	if (PortalCount > 0) have_portals = true;	//@@@NB_FE_MPI02
	return;
}


// This function teleports any object within range of a registered portal.
// It must be called by the Execute function in order to teleport
// objects as necessary.  It is specifically designed to not teleport
// pilots or empty vehicles.
// Function by BS-er
// _________________________________________________________________
void instantMission::PortalFunctions()
{
	int iX;	//@@@NB_FE_MPI02
	bool good_to_go;	//@@@NB_FE_MPI02

	bool TeleportedGroupE;	//@@@Centerline_FE_MPI

	for (int i=0; i<PortalCount; i++)
	{
		good_to_go=true;

		TeleportedGroupE = false;	//@@@Centerline_FE_MPI

		if (IsAround(Portals[i]))
		{
			int PortalTeam = GetTeamNum(Portals[i]);
			Handle NearVehicle = GetNearestObject(Portals[i]);

			if (NearVehicle>0  && !IsOdf(NearVehicle, "ibnav"))
			{
				good_to_go=false;

				//@@@NB_FE_MPI02
				// Teleport only portal user vehicles assigned to the portal group
				for (int n=0; n<MaxPortalUserTypes; n++)
				{
					int TypeIndex = PortalUserTypes[n];
					if (TypeIndex>=0 && IsType(NearVehicle,ODFType[TypeIndex]))
					{
/*
#ifdef _BZDEBUG
						//PrintConsoleMessage("----------");
						char tmpstr[64];
						sprintf(tmpstr, "PortalGroup: %d", PortalGroup[i]);
						PrintConsoleMessage(tmpstr);
						sprintf(tmpstr, "UsedPortalsGroup: %d", UsedPortalsGroup[n]);
						PrintConsoleMessage(tmpstr);
#endif
*/
						if (UsedPortalsGroup[n]==PortalGroup[i])
						{
							good_to_go = true;
							n=MaxPortalUserTypes;	// break the loop
						}
					}
				}
			}

			if ((good_to_go) && (IsWithin(NearVehicle,Portals[i], 15)))
			{
				Vector PortalPos;
				Vector ObjectPos;
				Vector Front;

				GetPosition(NearVehicle, ObjectPos);
				GetPosition(Portals[i], PortalPos);

				if (ObjectPos.y < PortalPos.y + 7.0)
				{
					int j = i;

					for (int k=0; k<PortalCount-1; k++)
					{
						if (++j >= PortalCount)
						{
							j=0;
						}

						if ((PortalGroup[i] == PortalGroup[j]) && IsAround(Portals[j]))
						{
/*
#ifdef _BZDEBUG
							if (IsOdf(NearVehicle, "ibnav")) PrintConsoleMessage("--- Teleporting Nav Beacon");
							PrintConsoleMessage("--- Teleporting PortalUser");
#endif
*/
							int NearVehicleTeam = GetTeamNum(NearVehicle);

							GetPosition(Portals[j], PortalPos);

							GetFront(NearVehicle, Front);
							PortalPos.x += Front.x * 30.0f;
							PortalPos.z += Front.z * 30.0f;

							Front.x *= 8.0f;
							Front.z *= 8.0f;

							if ((PortalTeam == 0) || (PortalTeam == NearVehicleTeam) ||
								IsAlly(NearVehicle, Portals[i]))
							{
								SetVectorPosition(NearVehicle, PortalPos);
							}
							else
							{
								Front.x *= -3;
								Front.z *= -3;
							}
							SetVelocity(NearVehicle, Front);

							if (IsPlayer(NearVehicle))
							{
								if (GetLocalPlayerTeamNumber() == NearVehicleTeam)
								{
									SetColorFade(1.0,1.0,32767);
									StartSoundEffect("teleport.wav");
								}
							}
							else
							{
								Stop(NearVehicle,0);

								if (!IsOdf(NearVehicle, "ibnav"))
								{
/*
#ifdef _BZDEBUG
									PrintConsoleMessage("--- Dispatching PortalUser");
#endif
*/
									//@@@NB_FE_MPI02
									switch (PortalGroup[j]) 
									{
									case 0: 
										Goto(NearVehicle, "hold1");
										if (!IsType(NearVehicle,"vputurr"))
										{
											for (iX=0; iX<MAX_HOLD_UNITS; ++iX) {
												if (*(iHold1ptr+iX)==NULL || !IsAround(*(iHold1ptr+iX))) {
													*(iHold1ptr+iX)=NearVehicle;
													break;
												}
											}
										}
										break;
									case 1: 
										Goto(NearVehicle, "hold2");
										if (!IsType(NearVehicle,"vputurr"))
										{
											for (iX=0; iX<MAX_HOLD_UNITS; ++iX) {
												if (*(iHold2ptr+iX)==NULL || !IsAround(*(iHold2ptr+iX))) {
													*(iHold2ptr+iX)=NearVehicle;
													break;
												}
											}
										}
										break;
									case 2: 
										Goto(NearVehicle, "hold3");
										if (!IsType(NearVehicle,"vputurr"))
										{
											for (iX=0; iX<MAX_HOLD_UNITS; ++iX) {
												if (*(iHold3ptr+iX)==NULL || !IsAround(*(iHold3ptr+iX))) {
													*(iHold3ptr+iX)=NearVehicle;
													break;
												}
											}
										}
										break;
									case 3: 
										Goto(NearVehicle, "hold4");
										if (!IsType(NearVehicle,"vputurr"))
										{
											for (iX=0; iX<MAX_HOLD_UNITS; ++iX) {
												if (*(iHold4ptr+iX)==NULL || !IsAround(*(iHold4ptr+iX))) {
													*(iHold4ptr+iX)=NearVehicle;
													break;
												}
											}
										}
										break;
									case 4:	//Cerberi attack squads
										TeleportedGroupE = true;	//@@@Centerline_FE_MPI
										if (IsAround(groupETarget)) 
										{
											Attack(NearVehicle, groupETarget);
										}
										else
										{
											Handle hTarget;
											hTarget=GetObjectByTeamSlot(1, (((turn_counter/10)%5)+1));
											if (!IsAround(hTarget)) 
											{
												hTarget = GetObjectByTeamSlot(strat_team,DLL_TEAM_SLOT_RECYCLER);
												if (!IsAround(hTarget)) 
												{
													hTarget = GetObjectByTeamSlot(strat_team,DLL_TEAM_SLOT_FACTORY);
												}
											}
											Attack(NearVehicle, hTarget);
										}
										break;
									case 5:	// Patrol squads
										switch (PortalNumber[j])
										{
										case 0: Patrol(NearVehicle, "portalPatrol0"); break;
										case 1: Patrol(NearVehicle, "portalPatrol1"); break;
										case 2: Patrol(NearVehicle, "portalPatrol2"); break;
										case 3: Patrol(NearVehicle, "portalPatrol3"); break;
										case 4: Patrol(NearVehicle, "portalPatrol4"); break;
										case 5: Patrol(NearVehicle, "portalPatrol5"); break;
										case 6: Patrol(NearVehicle, "portalPatrol6"); break;
										case 7: Patrol(NearVehicle, "portalPatrol7"); break;
										case 8: Patrol(NearVehicle, "portalPatrol8"); break;
										case 9: Patrol(NearVehicle, "portalPatrol9"); break;
										}
										break;
									default:
										switch ((turn_counter/10)%3)
										{
										case 0: Goto(NearVehicle, "hold1"); break;
										case 1: Goto(NearVehicle, "hold2"); break;
										case 2: Goto(NearVehicle, "hold3"); break;
										default: Goto(NearVehicle, "hold4"); break;
										}
									}
								}
							}
							k = PortalCount; // Force this loop to terminate
						}
					}
					//
					// An attempt to cut down on the number of stranded third race
					// units left to mill around GroupE0 portal.
					//
					if ((!TeleportedGroupE) && (PortalGroup[i]==4))	//@@@Centerline_FE_MPI
					{
						PrintConsoleMessage("Removing stranded third race unit.");
						RemoveObject(NearVehicle);	//@@@Centerline_FE_MPI
					}
				}
			}
		}
	}
}


// This function looks for all objects labelled PortalUserA thru PortalUserZ,
// and designates those ODF types to go to the portal nearest that object.
// It then deletes the objects, sice their only purpose was to tell the DLL
// what ODF is associated with what which portal.
// Function by BS-er
// _________________________________________________________________
void instantMission::SetPortalUsers()
{
	char PortalUserLabel[20] = "PortalUserA";

	//char tmpstr[64];

	for (int i=0; i<MaxPortalUserTypes; i++)
	{
		Handle PortalUser = GetHandle(PortalUserLabel);

		//debug statement
		//sprintf(tmpstr, "Loop counter for portal users: %d", i);
		//AddToMessagesBox(tmpstr);
		//sprintf(tmpstr, "PortalUserLabel: %s", PortalUserLabel);
		//AddToMessagesBox(tmpstr);

		if(IsAround(PortalUser))
		{
			char Race = GetRace(PortalUser);

			for (int j=0; j<ODFArraySize; j++)
			{
				if (IsType(PortalUser, ODFType[j]))
				{
					PortalUserTypes[i] = j;
					UsedPortals[i] = GetNearestBuilding(PortalUser);
					
					//@@@NB_FE_MPI02
					for (int n=0; n<MaxPortals; n++)
					{
						if (UsedPortals[i]==Portals[n])
						{
							UsedPortalsGroup[i] = PortalGroup[n];

							//PrintConsoleMessage("----------");
							//sprintf(tmpstr, "SetPortalUser: %s", PortalUserLabel);
							//PrintConsoleMessage(tmpstr);
							//AddToMessagesBox(tmpstr);
							//sprintf(tmpstr, "PortalUserTypes: %d", PortalUserTypes[i]);
							//PrintConsoleMessage(tmpstr);
							//sprintf(tmpstr, "UsedPortalsGroup Set: %d", UsedPortalsGroup[i]);
							//PrintConsoleMessage(tmpstr);

							break;
						}
					}
					RemoveObject(PortalUser);
				}
			}
		}
		PortalUserLabel[10]++;
		//if (i==26) PortalUserLabel[10]='1';	//@@@NB_FE_MPI02
		if (i==25) PortalUserLabel[10]='1'; //@@@Centerline_FE_MPI - fixed so it goes from z to 1 correctly.
	}
}


// This function checks to see if the object is supposed to use
// a portal, and if so, returns the portal he is supposed to use.
// Function by BS-er
// _________________________________________________________________
Handle instantMission::GetPortalToUse(Handle PortalUser)
{
	for (int i=0; i<MaxPortalUserTypes; i++)
	{
		int TypeIndex = PortalUserTypes[i];
		if (TypeIndex>=0 && IsType(PortalUser,ODFType[TypeIndex]))
		{
/*
#ifdef _BZDEBUG
			PrintConsoleMessage("--- GetPortalToUse ---");
			char tmpstr[64];
			sprintf(tmpstr, "UsedPortalsGroup: %d", UsedPortalsGroup[i]);
			PrintConsoleMessage(tmpstr);
			sprintf(tmpstr, "PortalUserTypes: %d", PortalUserTypes[i]);
			PrintConsoleMessage(tmpstr);
#endif
*/
			m_PortalGroup = UsedPortalsGroup[i];

			//@@@NB_FE_MPI02
			// Make sure there is a live destination portal in the group:
			for (int j=0; j<PortalCount; j++) {
				if (Portals[j] != UsedPortals[i]) {
					if ((m_PortalGroup == PortalGroup[j]) && IsAround(Portals[j]))
					{
						// Do not allow non-Cerberi to use portal group E:
						if (UsedPortals[i]!=groupEPortal) {
							return UsedPortals[i];
						}
					}
				}
			}
		}
	}

	return 0;
}


// Function by BS-er
// ______________________________________________________________________
char* instantMission::RaceOdf(char Race, char* EndString)
{
	sprintf(TempODFName, "%c%s", Race, EndString);
	return TempODFName;
}


// Function by BS-er
// ______________________________________________________________________
bool instantMission::IsType (Handle h, char* EndString)
{
	return IsOdf(h,RaceOdf(GetRace(h), EndString));
}


//@@@Centerline_FE_MPI - Function added by Centerline
// Checks to see if GroupE has a destination portal. Everything else checks
// to see if PortalE0 exists but not if there is somewhere to go. If the
// human team demolishes the destination portals then GroupE will overbuild
// and lag the game. This check should be used in conjunction with the build
// routine to ensure we don't build any new units after all destination
// portals are demolished.
// ______________________________________________________________________
bool instantMission::DestinationPortal(int PortalCount, int PortalNumber[MaxPortals], Handle Portals[MaxPortals])
{
	bool HaveDestinationPortal=false;

	for (int j=0; j<PortalCount; j++)
	{
		if (PortalGroup[j] == 4)
		{
			if (PortalNumber[j] > 0 && IsAround(Portals[j]))
			{
				HaveDestinationPortal=true;
			}
		}
	}

	return HaveDestinationPortal;
}


//@@@Centerline_FE_MPI - Function added by Centerline
// Sets up extra random scrap pools at pathpoints in the map.
// Looks for mpipathpoints in the form "RandomPool_X_Y_ODF"
// where Y:1..iMaxPoolGroups and X:1..iMaxPoolLocations
// and ODF = the ODF name of the scrap pool object we should build.
// If the ODF name is "NONE" and that path point is the one randomly
// selected to be built for the group -- then no scrap pool will be
// built for that scrap pool group.
// ______________________________________________________________________
void instantMission::SetupRandomScrapPools(Handle hRandomPools[iMaxPoolGroups])
{
	enum PoolTags
	{
		MPIPathPoint=0,
		ODF,
		MaxPoolTags,
	};
	
	// Relocated to top as a global
	//const int iMaxPoolGroups = 9;		// Maximum number of pools to be placed - don't exceed "MaxValueLen" number of digits
	const int iMaxPoolLocations = 9;	// Maximum random locations for any one pool - don't exceed "MaxValueLen" number of digits
	const int MaxODFLen=32;
	const int MaxValueLen=1;			// Maximum allowed number of digits in pool group and pool locations
										// if you increase this you must modify code.
	const int MaxPathName=64;			// Maximum allowed number of characters in a pathpoint name

	int x,y;
	int Locations;
	int pathCount;
	int PoolCount;

	unsigned long j,k;

	char TempLabel[MaxPathName+1];
	char RandomPools[iMaxPoolGroups+1][iMaxPoolLocations+1][MaxPoolTags+1][MaxPathName+1];

	char **pathNames;

	GetAiPaths(pathCount, pathNames);

	// Pre-fill the RandomPools array with null characters
	for (x = 1; x < iMaxPoolGroups + 1; x++)
	{
		for (y = 1; y < iMaxPoolLocations + 1; y++)
		{
			RandomPools[x][y][MPIPathPoint][0] = '\0';
			RandomPools[x][y][ODF][0] = '\0';
		}
	}

	// Parse out the path points to find valid pool locations
	for (int i = 0; i < pathCount; ++i)
	{
		char *Label = pathNames[i];
		if(strncmp(Label,"RandomPool",10)==0)
		{
			// Starts with "RandomPool" so process it.

			char TempPoolODF[MaxODFLen+1];
			char TempPoolGroup[MaxValueLen];
			char TempPoolLocation[MaxValueLen];

			// Prezap full string contents so we don't have to null-term later
			memset(TempPoolODF,0,sizeof(TempPoolODF));
			memset(TempPoolGroup,0,sizeof(TempPoolGroup));
			memset(TempPoolLocation,0,sizeof(TempPoolLocation));

			j=0;

			// Skip forward until an _
			while((Label[j] != '_') && (Label[j] != '\0'))
			{
				j++;
			}
			if(Label[j]=='\0')
			{
				continue; // Misformat! No _ found! Bail!
			}

			k=0;
			j++; // skip the _. Now, copy out X value
			while((Label[j] != '_') && (Label[j] != '\0'))
			{
				if(k<MaxValueLen)
				{
					TempPoolGroup[k]=Label[j];
					k++;
				}
				j++;
			}

			if(Label[j]=='\0')
			{
				continue; // Misformat! No _ found! Bail!
			}

			k=0;
			j++; // skip the _. Now, copy out Y value
			TempPoolLocation[k]=Label[j]; // Compiler messup test?
			while((Label[j] != '_') && (Label[j] != '\0'))
			{
				if(k<MaxValueLen)
				{
					TempPoolLocation[k]=Label[j];
					k++;
				}
				j++;
			}

			if(Label[j]=='\0')
			{
				continue; // Misformat! No _ found! Bail!
			}

			k=0;
			j++; // skip the _. Now, copy out pool ODF
			TempPoolODF[k]=Label[j]; // Compiler messup test?
			while((Label[j] != '_') && (Label[j] != '\0'))
			{
				if(k<MaxODFLen)
				{
					TempPoolODF[k]=Label[j];
					k++;
				}
				j++;
			}


			// Fill the array with all possible build points
			if ((TempPoolGroup[0] != '\0') && (TempPoolLocation[0] != '\0')
				&& (TempPoolODF[0] != '\0'))

			{
				x = TempPoolGroup[0] - 48;
				y = TempPoolLocation[0] - 48;

				memset(TempLabel,0,sizeof(TempLabel));

				strncpy (TempLabel, Label, MaxPathName);
				strncpy (RandomPools[x][y][MPIPathPoint], TempLabel, MaxPathName+1);
				strncpy (RandomPools[x][y][ODF], TempPoolODF, MaxODFLen+1);

				//
				// ****************************************************************
				// *                                                              *
				// * Debug section to put a nav at every detected pool path point *
				// *                                                              *
				// ****************************************************************
				//
				//Handle Nav = BuildObject("ibnav",1,TempLabel);
				//SetObjectiveName(Nav, TempLabel);
			}
		}
	}


	// Now figure out which pools to build and do it.
	x = 0;
	y = 0;
	PoolCount = 0;

	for (x = 1; x < iMaxPoolGroups + 1; x++)
	{
		Locations = 0;

		for (y = 1; y < iMaxPoolLocations + 1; y++)
		{
			if (RandomPools[x][y][MPIPathPoint][0] != '\0')
			{
				Locations++;
			}
		}

		if (Locations > 0)
		{
			y = (int)GetRandomFloat(32000);
			y = y%Locations+1;
		
			if (RandomPools[x][y][ODF] != "NONE")
			{
				hRandomPools[PoolCount++] = BuildObject(RandomPools[x][y][ODF], 0, RandomPools[x][y][MPIPathPoint]);
			}
		}
	}
	return;
}


//@@@Centerline_FE_MPI - Function added by Centerline
// This function spawsn the rampage units and sets their targets.
// Targets are based randomly based on time_count and are always
// the main base building slots.
//
// The unit limit controls how many concurrent rampage units are allowed
// on the map this game, never to exceed MAX_RAMPAGE_UNITS.
// ______________________________________________________________________
void instantMission::SpawnRampage(char RampageUnit[32], Handle *hRampageUnits, int UnitLimit, int RampageCurrentSlot)
{
	//char tempstr[64];
	int iRampageUnit;
	Handle hRampageTarget;

	iRampageUnit = 0;

	if (!IsAround(hRampageUnits[RampageCurrentSlot]))
	{
		hRampageUnits[RampageCurrentSlot] = NULL;
	}

	if (hRampageUnits[RampageCurrentSlot] == NULL) //Unit slot is empty
	{
		hRampageUnits[RampageCurrentSlot] = BuildObject(RampageUnit, comp_team, "RecyclerEnemy");
		SetSkill(hRampageUnits[RampageCurrentSlot], 2);
		hRampageTarget = GetObjectByTeamSlot(strat_team, ((RampageCurrentSlot%5)+1));

		if (!IsAround(hRampageTarget))
		{
			hRampageTarget = GetObjectByTeamSlot(strat_team, DLL_TEAM_SLOT_FACTORY);
			if (!IsAround(hRampageTarget))
			{
				hRampageTarget = GetObjectByTeamSlot(strat_team, DLL_TEAM_SLOT_RECYCLER);
			}
		}

		// Debug output that shows which unit slot is being touched.
		//sprintf(tempstr, "Setting target for Rampage unit in slot: %i\n", RampageCurrentSlot);
		//PrintConsoleMessage(tempstr);
		//LogToFile(tempstr);

		Attack(hRampageUnits[RampageCurrentSlot], hRampageTarget);
	}
	return;
}



//@@@Centerline_FE_MPI - Function added by Centerline
// This writes out information to a log file.
// ______________________________________________________________________
void instantMission::LogToFile(char Comments[64])
{
	FILE *Log;
	Log=fopen("BZ2_FE_MPI.log","a+");  // Opens log file for appending to end of file.

	if(Log == NULL)
	{
		return; // Error opening the log file.
	}

	if (fputs(Comments, Log) == EOF)
	{
		PrintConsoleMessage("There was an error writing to the log file.");
	}

	fclose(Log);
	return;
}


//@@@Centerline_FE_MPI - This section spawns the extra weapons crates.
//
// ______________________________________________________________________
void instantMission::SpawnWeaponCrates()
{
	const float MinRadiusAway = 150.0f;
	const float MaxRadiusAway = 250.0f;
	const int Team = 0;  // Neutral team

	PrintConsoleMessage("Entering weapons crate routine.");

	if (!late_game)
	{
		if (IsAround(hCrate1)) RemoveObject(hCrate1);
		if (IsAround(hCrate2)) RemoveObject(hCrate2);
		if (IsAround(hCrate3)) RemoveObject(hCrate3);
		
		if (!IsAround(hCrate1))
		{
			hCrate1 = SpawnObject("apbolter", Team, recycler, MinRadiusAway, MaxRadiusAway);
		}
	
		if (!IsAround(hCrate2))
		{
			hCrate2 = SpawnObject("apbhole", Team, recycler, MinRadiusAway, MaxRadiusAway);
		}

		if (!IsAround(hCrate3))
		{
			hCrate3 = SpawnObject("apfirestorm", Team, recycler, MinRadiusAway, MaxRadiusAway);
		}
	}
	else if (late_game && (iDiffLvl != OPTION_HIGH))
	{
		if (IsAround(hCrate1)) RemoveObject(hCrate1);
		if (IsAround(hCrate2)) RemoveObject(hCrate2);
		if (IsAround(hCrate3)) RemoveObject(hCrate3);

		if (!IsAround(hCrate1))
		{
			hCrate1 = SpawnObject("ap8brock", Team, recycler, MinRadiusAway, MaxRadiusAway);
		}
	
		if (!IsAround(hCrate2))
		{
			hCrate2 = SpawnObject("approton", Team, recycler, MinRadiusAway, MaxRadiusAway);
		}

		if (!IsAround(hCrate3))
		{
			hCrate3 = SpawnObject("aphfire2", Team, recycler, MinRadiusAway, MaxRadiusAway);
		}
	}
	else if (late_game && (iDiffLvl == OPTION_HIGH))
	{
		if (IsAround(hCrate1)) RemoveObject(hCrate1);
		if (IsAround(hCrate2)) RemoveObject(hCrate2);
		if (IsAround(hCrate3)) RemoveObject(hCrate3);

		PrintConsoleMessage("Late Game: On hard -- not spawning weapons crates!\n");
	}
	return;
}


//@@@Centerline_FE_MPI - This function spawns an object at a random location
// from the passed object handle within a min/max radius on the specified team
// and returns the handle of the newly created object.
//
// ______________________________________________________________________
Handle instantMission::SpawnObject(char TempODF[64], int Team, Handle hReferenceObject, float MinRadiusAway, float MaxRadiusAway)
{
	Handle hTempLocation;
	Handle hTempObject;
	Vector vReferenceSpawnPoint;
	Vector vFinalSpawnPoint;



	//float TempDistance;
	//float LeastDistance;
	//float LastDistance;
	//LeastDistance = MaxRadiusAway;
	//char tempstr[64];


	hTempLocation = BuildObject("ibnav", 0, hReferenceObject);
	vReferenceSpawnPoint = GetPosition(hTempLocation);

	/*
	LastDistance = 0.0f;
	for (int points=0; points<30; points++)
	{
		TempDistance = 0.0f;
		TempDistance = GetDistance(hTempLocation, "edge_path", points);
		//TempDistance = GetDistance(hTempLocation, "edge_path");
		sprintf(tempstr, "Path point count %i\n", points);
		PrintConsoleMessage(tempstr);
		sprintf(tempstr, "Distance value returned %f\n", TempDistance);
		PrintConsoleMessage(tempstr);

		if (TempDistance < 0.0f)
		{
			// End of path list
			PrintConsoleMessage("No more valid path points to evaluate.");
			break;
		}
		else if (TempDistance < LeastDistance)
		{
			// print the value of TempDistance
			PrintConsoleMessage("Found a valid path point to evaluate.");
			LeastDistance = TempDistance;
		}

		if (TempDistance == LastDistance)
		{
			// End of path list
			PrintConsoleMessage("Reached end of valid points.");
			break;
		}
		else
		{
			LastDistance = TempDistance;
		}
	}
	*/

	RemoveObject(hTempLocation);

	/*
	if (LeastDistance <= MinRadiusAway)
	{
		// we are too close to the edge_path so bail
		// print an error to the console
		PrintConsoleMessage("Too close to the edge_path.");
		hTempObject = NULL;
		return hTempObject;
	}
	else
	*/

	//{
		vFinalSpawnPoint = GetPositionNear(vReferenceSpawnPoint, MinRadiusAway, MaxRadiusAway);
		vFinalSpawnPoint.y = TerrainFindFloor(vFinalSpawnPoint.x, vFinalSpawnPoint.z);

		hTempObject = BuildObject(TempODF, Team , vFinalSpawnPoint);

		//PrintConsoleMessage("Far enough from edge_path to spawn.");
		return hTempObject;
	//}
}


//@@@Centerline_FE_MPI - This function spawns an object at a path point
// on team 0 to be captured. Which ever team comes close enough gets it.
//
// ______________________________________________________________________
bool instantMission::CaptureObject()
{
	bool IsCaptured = false;
	int CapturerTeamNum = 0;
	//char CaptureeODF[64];

	if (!CaptureObjectSetup)
	{
		int iCaptureToggle=GetVarItemInt("network.session.ivar39");
		if (iCaptureToggle == 1)
		{
			const char *TempODF=GetVarItemStr("network.session.svar20");
			if(TempODF[0]!= '\0') 
			{
				strncpy(CaptureeODF,TempODF,sizeof(CaptureeODF)-1);
				hCapturee = BuildObject(CaptureeODF, 0, "Capturee");
				if (IsAround(hCapturee))  
				{
					PrintConsoleMessage("Captureable object added to map!");
					IsCaptured = false;
				}
				else
				{
					PrintConsoleMessage("Captureable object was not added to map!");
					IsCaptured = true;	// Bail as the object wasn't built.
				}	
			}
			else
			{
				IsCaptured = true;  // Bail as there is nothing to build.
			}
		}
		else
		{
			IsCaptured = true;  // Bail as the feature has been turned off.
		}

		CaptureObjectSetup = true;
		return IsCaptured;
	}
	else
	{
		Handle hCapturer = GetNearestVehicle(hCapturee);
		if (GetDistance(hCapturer, hCapturee) <= 40.0f)
		{
			CapturerTeamNum = GetTeamNum(hCapturer);

			// switch teams to team of capturer
			if (GetTeamNum(hCapturee)==0)
			{
				PrintConsoleMessage("Item has been captured!");

				// This is a test to fix the power generator issue
				// remove if it totally destabilizes everything
				RemoveObject(hCapturee);
				hCapturee = BuildObject(CaptureeODF, CapturerTeamNum, "Capturee");
				SetSkill(hCapturee, 2);

				//SetTeamNum(hCapturee, CapturerTeamNum);
				//SetSkill(hCapturee, 2);
			}
			IsCaptured = true;
		}
	}
	return IsCaptured;
}


//@@@Centerline_FE_MPI - This function spawns monsters at recycler
// deploy to attack recycler.iMonsterToggle                                                                          
//
// ______________________________________________________________________
void instantMission::MonsterRun()
{
	PrintConsoleMessage("A Monster Is Coming!");

	char MonsterODF[64];

	int iMonsterToggle=GetVarItemInt("network.session.ivar38");

/*
	//-----------------------
	// move to setup state so it can replicate
	int iMonsterDelay=GetVarItemInt("network.session.ivar40");

	if (iMonsterDelay < 0) iMonsterDelay = 60;
	if (iMonsterDelay > 1800) iMonsterDelay = 600;

	int iShift = (int)GetRandomFloat(float(iMonsterDelay * 0.2));
	iMonsterDelay = (int((iMonsterDelay * 0.9)) + iShift) * 10;

	char tempstr[64];
	PrintConsoleMessage("==========================================\n");
	sprintf(tempstr, "MonsterDelay is: %i\n", iMonsterDelay);
	PrintConsoleMessage(tempstr);
	PrintConsoleMessage("==========================================\n");
	//----------------------
*/

	if ((iMonsterToggle == 1) && (iMonsterDelay == time_count))
	{
		PrintConsoleMessage("Starting Monster Run!");
		const char *TempMonsterODF=GetVarItemStr("network.session.svar19");
		if(TempMonsterODF[0]!= '\0')
		{
			strncpy(MonsterODF,TempMonsterODF,sizeof(MonsterODF)-1);
		}
		else
		{
			sprintf(MonsterODF, "monster");
		}

		if (IsAround(recycler) && (iDiffLvl != OPTION_LOW))
		{
			monster1 = BuildObject(MonsterODF, comp_team, "Monster1");
			monster2 = BuildObject(MonsterODF, comp_team, "Monster2");

			Handle hTarget1;
			hTarget1 = GetObjectByTeamSlot(strat_team,DLL_TEAM_SLOT_RECYCLER);
			if (!IsAround(hTarget1)) 
			{
				hTarget1 = GetObjectByTeamSlot(strat_team,DLL_TEAM_SLOT_FACTORY);
				if (!IsAround(hTarget1)) return;
			}
			
			if (IsAround(monster1))
			{
				SetSkill(monster1, 3);
				Attack(monster1, hTarget1);
			}

			if (IsAround(monster2))
			{
				SetSkill(monster2, 3);
				Attack(monster2, hTarget1);
			}

			AudioMessage("bullcall.wav", false);
		}
	}
	return;
}


//@@@Centerline_FE_MPI - This function spawns the extra scrap pools.
//
// ______________________________________________________________________
void instantMission::SpawnEasyPools()
{
	char EasyPoolODF[64];

	int iEasyPoolToggle=GetVarItemInt("network.session.ivar37");

	//if ((iDiffLvl == OPTION_LOW) && (iEasyPoolToggle == 1))
	if (iEasyPoolToggle == 1)
	{
		const char *TempEasyPoolODF=GetVarItemStr("network.session.svar21");
		if(TempEasyPoolODF[0]!= '\0') 
		{
			strncpy(EasyPoolODF,TempEasyPoolODF,sizeof(EasyPoolODF)-1);
			BuildObject(EasyPoolODF,0,"EasyPool1");
			BuildObject(EasyPoolODF,0,"EasyPool2");
		}
		else
		{
			BuildObject("cpstpool01",0,"EasyPool1");
			BuildObject("cpstpool01",0,"EasyPool2");
		}
		PrintConsoleMessage("Extra scrap pool(s) may have been added to map!");
	}
	return;
}

		
// ______________________________________________________________________
SubMission * BuildSubMission(void)
{
	return new instantMission();
}
