// 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\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 MaxPortalUserTypes 10

const int myForce = 1;
const int compForce = 2;
const int difficulty = 2;

#define ODFArraySize 16
static char ODFType[ODFArraySize][20] =
{
	"vtank",
	"vatank",
	"vwalk",
	"vewalk",
	"vscout",
	"vsent",
	"vmisl",
	"vrckt",
	"vscav",
	"vserv",
	"vmbike",
	"vmort",
	"vtanku",
	"vatanku",
	"vmislu",
	"vabomb"
};


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;

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 SetCPUAIPlan(const int CPUSide,const int HumanSide,AIPType Type);

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

	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,
		have_armory,
		didInit,
		HadeanWalkerBuilt,
		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,
		goodies[MAX_GOODIES],
		tuglist[MAX_GOODIES],  // keep track of tugs
		holding[MAX_GOODIES],
		Portals[MaxPortals],
		UsedPortals[MaxPortalUserTypes],
		m_PortalUser,
		m_PortalToUse,
		HadeanWalker,
		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,
		PortalCount,
		PortalGroup[MaxPortals],
		PortalUserTypes[MaxPortalUserTypes],
		PortalUserWaitCount,
		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;

	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==3)
				{
					BuildObject(HadeanStuff,comp_team,Label);
				}
				else
				{
					BuildObject(ISDFStuff,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);
				}
				else if (mySide==3)
				{
					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)
			{
			} // No further name mangling needed.
			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)
			{
			} // No further name mangling needed.
			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]='h';
		}

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

		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.  
	*/
	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;

	
	siege_on=false;
	anti_assault=false;
	siege_counter=0;
	assault_counter=false;
	late_game=false;
	have_armory=false;
	
	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;
	}

	NumHumans=CountPlayers();

	HadeanWalkerBuilt = false;
}


// 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)
{
	// See if this unit should go to a portal.
	Handle TempHandle = GetPortalToUse(h);
	if (TempHandle != 0 && m_PortalUser != 0)
	{
		m_PortalUser = h;
		PortalUserWaitCount = 0;
		m_PortalToUse = TempHandle;
	}

	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 EDF
				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");
			if(ShellRace == 0)
			{
				mySide=2; // Computer is EDF
			}
			else if (ShellRace == 1)
			{
				mySide=1; // Computer is Scion
			}
			else
			{
				mySide=3; // Computer is Hadean
			}
		}
	}

	if (GetTeamNum(h)==comp_team)
	{
		SetSkill(h,difficulty+1);

		if ((IsType(h,"barmo")) || (IsOdf(h,"fbstro")))
		{
			have_armory=true;
		}
		if (have_armory)
		{
			if (IsOdf(h,"ivtank"))
			{
				GiveWeapon(h,"gspstab_c");
			} 
			else if (IsOdf(h,"fvtank"))
			{
				GiveWeapon(h,"garc_c");   // what a crutch!  
				GiveWeapon(h,"gshield");
			} 
			else if (IsOdf(h,"evtank"))
			{
				// TODO: Give Hadean tank some kind of weapons!!
			}
		}
	}

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

			int stratchoice = turn_counter%2;
			if (mySide==1)
			{
				switch (stratchoice)
				{
				case 0:
					SetCPUAIPlan(mySide,HumanSide,AIPType1);
					break;
				case 1:
					SetCPUAIPlan(mySide,HumanSide,AIPType3);
					break;
				case 2:
					SetCPUAIPlan(mySide,HumanSide,AIPType2);
					break;
				}
			}
			else
			{
				switch (stratchoice)
				{
				case 0:
					SetCPUAIPlan(mySide,HumanSide,AIPType1);
					break;
				case 1:
					SetCPUAIPlan(mySide,HumanSide,AIPType3);
					break;
				}
	
			}
		}
		else
		{
			if ((IsType(h,"vatank")) ||  (IsType(h,"vwalk")))
			{
				assault_counter++;
			} 
		}
	}	
}


// ______________________________________________________________________
void instantMission::DeleteObject(Handle h)
{
	if ((IsType(h,"vatank")) ||  (IsType(h,"vwalk")))
	{
		--assault_counter;
	}

	// Make a sniped unit neutral
	Handle Shooter = GetWhoShotMe(h);
	if (IsPlayer(Shooter) && IsPerson(Shooter))
	{
		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)
{
	if (!didInit)
	{
		PortalInit(); // Initialize all portals.
		SetPortalUsers(); // Set up the portal user types.
		didInit = true;
	}


	player=GetPlayerHandle();
	turn_counter++;
	DoGenericStrategy(TeamIsSetUp, RecyclerHandles);

	// General portal transport.
	PortalFunctions();

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


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

		start_done=true;

		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;

			BuildObject("fvturr",comp_team,"turretEnemy1");
			BuildObject("fvturr",comp_team,"turretEnemy2");

			BuildObject("fvturr",comp_team,"gtow2");
			BuildObject("fvturr",comp_team,"gtow3");

			BuildObject("fvturr",comp_team,"gtow4");
			BuildObject("fvturr",comp_team,"gtow5");
			BuildObject("fvtank",comp_team,"tankEnemy1");
			BuildObject("fvtank",comp_team,"tankEnemy2");
			BuildObject("fvtank",comp_team,"tankEnemy3");

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

			BuildObject("ivturr",comp_team,"turretEnemy1");
			BuildObject("ivturr",comp_team,"turretEnemy2");

			BuildObject("ivturr",comp_team,"gtow2");
			BuildObject("ivturr",comp_team,"gtow3");

			BuildObject("ivturr",comp_team,"gtow4");
			BuildObject("ivturr",comp_team,"gtow5");
			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;

			BuildObject("evturr",comp_team,"turretEnemy1");
			BuildObject("evturr",comp_team,"turretEnemy2");

			BuildObject("evturr",comp_team,"gtow2");
			BuildObject("evturr",comp_team,"gtow3");

			BuildObject("evturr",comp_team,"gtow4");
			BuildObject("evturr",comp_team,"gtow5");
			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 EDF
			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);
	} // 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);

	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");

	}

	// AIP switch logic
	if (!siege_on)
	{
		Handle enemy=GetNearestEnemy(enemy_recycler);
		if (GetDistance(enemy,enemy_recycler)<300.0f)
		{ // under siege
			siege_counter++;
		}
		else
		{
			siege_counter=0;
		}
		if (siege_counter>450)
		{ // 45 sec, if we don't change the sim rate :-)
			siege_on=true;

			SetCPUAIPlan(mySide,HumanSide,AIPTypeS);
		}
	}  // !siege_on
	else
	{
		Handle enemy=GetNearestEnemy(enemy_recycler);
		if (GetDistance(enemy,enemy_recycler)>301.0f)
		{
			//			SetPlan("scioninstl.aip",comp_team);
			siege_on=false;

			SetCPUAIPlan(mySide,HumanSide,AIPTypeL);
		}
	}  // siege_on
	// assault_test
	if ((!late_game) && (!siege_on) && (!anti_assault) && (assault_counter>2))
	{
		anti_assault=true;

		SetCPUAIPlan(mySide,HumanSide,AIPTypeA);
	} else
	{
		if ((anti_assault) && (assault_counter<3))
		{
			anti_assault=false;
			SetCPUAIPlan(mySide,HumanSide,AIPTypeL);
		}
	}

	TestObjectives();
}


// ______________________________________________________________________
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;
				if (PortalCount >= MaxPortals)
				{
					return;
				}
			}
		}
		PortalLabel[6] = 'A';
		PortalLabel[7]++;
	}
}


// 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()
{
	for (int i=0; i < PortalCount; i++)
	{
		if (IsAround(Portals[i]))
		{
			int PortalTeam = GetTeamNum(Portals[i]);
			Handle NearVehicle = GetNearestObject(Portals[i]);
			if (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]))
						{
							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);
							}
							k = PortalCount; // Force this loop to terminate
						}
					}
				}
			}
		}
	}
}


// This function looks for all objects labelled PortalUser0 thru PortalUser9,
// 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] = "PortalUser0";

	for (int i=0; i<MaxPortalUserTypes; i++)
	{
		Handle PortalUser = GetHandle(PortalUserLabel);
		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);
					RemoveObject(PortalUser);
#ifdef _BZDEBUG
					PrintConsoleMessage(PortalUserLabel);
#endif
				}
			}
		}
		PortalUserLabel[10]++;
	}
}


// 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]))
		{
			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));
}



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