// BZII Community Project Modified strat DLL.  In addition to the standard strat functions
// of the Pandemic code, this DLL implements portal functionality for appropriately labelled
// portals (PortalA0, PortalB1 etc.).  This DLL also implements a shield around each deployed
// recycler that repels enemy units (but not ordinance).  The size of the shield is based on
// the number of deployed scavengers on the recycler's team.

#include "..\Shared\DLLBase.h"
#include "..\Shared\MPVehicles.h"

#include <math.h>
#include <string.h>

#define _BZDEBUG // Keep this defined only for coding and debugging phase

#define VEHICLE_SPACING_DISTANCE (20.0f)
#define MaxPortals 16 // Maximum allowable number of portals
#define ODFArraySize 10
#define MaxPortalUserTypes 10


// ---------- Scoring Values-- these are delta scores, added to current score --------
const int ScoreForKillingCraft = 5; // User-piloted craft only
const int ScoreForKillingPerson = 10;
const int ScoreForDyingAsCraft = 0;
const int ScoreForDyingAsPerson = -5;
const int ScoreForWinning = 100;
// -----------------------------------------------

// How far away a new craft will be from the old one
const float RespawnMinRadiusAway=15.0f;
const float RespawnMaxRadiusAway=20.0f;

// How far allies will be from the commander's position
const float AllyMinRadiusAway=30.0f;
const float AllyMaxRadiusAway=60.0f;

// Temporary strings for blasting stuff into for output. NOT to be
// used to store anything you care about.
char OutTimeString[1024];

// Temporary name for blasting ODF names into while building
// them. *not* saved, do *not* count on its contents being valid
// next time the dll is called.
static char TempODFName[64];

#ifdef _BZDEBUG
		char ConsoleString[50];
#endif

class Strategy01 : public DLLBase
{

	// WARNING: If you make an array in here based on MAX_PLAYERS, be
	// *SURE* to properly shuffle its contents down in the
	// DeletePlayer() call.  Otherwise, things will be incorrectly
	// credited after someone leaves. Arrays based on MAX_TEAMS are not
	// shuffled by default on player deletion

private:
	int
	i_first,
		TotalGameTime,
		CurrentGameTime,
		ElapsedGameTime,
		KillLimit, // As specified from the shell
		StartingPrefs, // also from shell-- ivar7
		PortalCount,
		PortalGroup[MaxPortals],
		i_last;

	bool
	b_first,
		didInit,
		HadMultipleFunctioningTeams,
		TeamIsSetUp[MAX_TEAMS],
		gameOver,
		b_last;

	float
	f_first,
		TeamPos[3*(MAX_TEAMS+1)]; // Where they started out, used in respawning
	Matrix TestPos;
		float f_last;

	Handle
	h_first,
		RecyclerHandles[MAX_TEAMS],
		Portals[MaxPortals],
		m_PortalUser,
		m_PortalToUse,
		HadeanWalker,
		h_last;

protected:
	bool *b_array;
	int b_count;

	float *f_array;
	int f_count;

	int *h_array;
	int h_count;

	int *i_array;
	int i_count;

public:

	// Don't do much at class creation; do in InitialSetup()
	// ______________________________________________________________________
	Strategy01()
	{
		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_array = &h_first + 1;
		h_count = &h_last - &h_first - 1;

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

	void InitialSetup()
	{
		PreloadODF("hspilo");
	}


	// ______________________________________________________________________
	char *GetNextRandomVehicleODF(int Team)
	{
		return GetPlayerODF(Team);
	}


	// This function runs whenever a new object is added to the game.  The object
	// handle is passed to the function.  The function is also called when certain
	// buildings are upgraded (not scavs).
	// ______________________________________________________________________
	void AddObject(Handle h)
	{
		char Race = GetRace(h);
		
		if (IsType(h,"vturr"))
		{
			SetSkill(h, 2);
		}
		else if (IsType(h,"vatank") || IsOdf(h,"ibgtow"))
		{
			SetSkill(h, 2);
		}
		else
			SetSkill(h, 3);

		if(IsOdf(h,"ibbomb"))
		{
			if(!IsAlly(GetPlayerHandle(GetLocalPlayerTeamNumber()), h))
			{
				AddToMessagesBox("Intell report: Bomber bay!");
			}
		}
	}

	void DeleteObject(Handle h)
	{
		long Health = GetCurHealth(h);
		int Team = GetTeamNum(h);
	}


	// Gets the initial player vehicle as selected in the shell
	// ______________________________________________________________________
	char *GetInitialPlayerVehicleODF(int Team)
	{
		return GetPlayerODF(Team);
	}

	// Given a race identifier, get the pilot back (used during a respawn)
	// ______________________________________________________________________
	char *GetInitialPlayerPilotODF(char Race)
	{
		strcpy(TempODFName,"isuser_m"); // Note-- this is the sniper-less variant for a respawn

		TempODFName[0]=Race;
		return TempODFName;
	}

	// Given a race identifier, get the recycler ODF back
	// ______________________________________________________________________
	char *GetInitialRecyclerODF(char Race)
	{
		// THIS CODE IS NOT TESTED AND NOT PART OF THE SHIPPED
		// STRATEGY02.DLL SOURCE CODE!
		const char *Contents=GetVarItemStr("network.session.svar5");
		if(Contents[0]!= '\0') {
			strncpy(TempODFName,Contents,sizeof(TempODFName)-1);
		}
		else
			strcpy(TempODFName,"ivrecy_m");

		TempODFName[0]=Race;
		return TempODFName;
	}


	// Given a race identifier, get the scavenger ODF back
	// ______________________________________________________________________
	char *GetInitialScavengerODF(char Race)
	{
		strcpy(TempODFName,"ivscav");
		TempODFName[0]=Race;
		return TempODFName;
	}


	// Given a race identifier, get the constructor ODF back
	// ______________________________________________________________________
	char *GetInitialConstructorODF(char Race)
	{
		strcpy(TempODFName,"ivcons");
		TempODFName[0]=Race;
		return TempODFName;
	}


	// Given a race identifier, get the healer (repairbot) ODF back
	// ______________________________________________________________________
	char *GetInitialHealerODF(char Race)
	{
		strcpy(TempODFName,"ivserv");
		TempODFName[0]=Race;
		return TempODFName;
	}


	// ______________________________________________________________________
	char* RaceOdf(char Race, char* EndString)
	{
		sprintf(TempODFName, "%c%s", Race, EndString);
		return TempODFName;
	}


	// ______________________________________________________________________
	bool IsType (Handle h, char* EndString)
	{
		return IsOdf(h,RaceOdf(GetRace(h), EndString));
	}


	// Sets up the side's commander's extra vehicles, such a recycler or
	// more.  Does *not* create the player vehicle for them,
	// however. [That's to be done in SetupPlayer.]  Safe to be called
	// multiple times for each player on that team
	//
	// If Teamplay is off, this function is called once per player.
	//
	// If Teamplay is on, this function is called only on the
	// _defensive_ team number for an alliance. 
	// ______________________________________________________________________
	void SetupTeam(int Team)
	{
		if((Team<1) || (Team>=MAX_TEAMS))
			return;
		if((IsTeamplayOn()) && (TeamIsSetUp[Team]))
			return;

		char TeamRace=GetRaceOfTeam(Team);
		if(IsTeamplayOn())
			SetMPTeamRace(WhichTeamGroup(Team),TeamRace); // Lock this down to prevent changes.

		Vector Where;
		// First pass: see if a spawnpoint exists with this team #
		Where=GetSpawnpoint(Team);

		// If that returned a null vector, go with the older method of randomizing
		if(((Where.x * Where.x) + (Where.y * Where.y) + (Where.z * Where.z)) < 0.1f)
			Where=GetRandomSpawnpoint(Team);

		// Store position we created them at for later
		TeamPos[3*Team+0]=Where.x;
		TeamPos[3*Team+1]=Where.y;
		TeamPos[3*Team+2]=Where.z;

		// Build recycler some distance away
		Where=GetPositionNear(Where, VEHICLE_SPACING_DISTANCE, 2*VEHICLE_SPACING_DISTANCE);

		int VehicleH;
		VehicleH=BuildObject(GetInitialRecyclerODF(TeamRace),Team,Where);
		SetRandomHeadingAngle(VehicleH);
		RecyclerHandles[Team]=VehicleH;
		SetGroup(VehicleH,0);

		// Build optionally-specified vechicles if the ivars want it
		if(StartingPrefs & 0x100) {
			Where.x=TeamPos[3*Team+0];
			Where.z=TeamPos[3*Team+2]+VEHICLE_SPACING_DISTANCE;
			Where.y=TerrainFindFloor(Where.x,Where.z);
			VehicleH=BuildObject(GetInitialScavengerODF(TeamRace),Team,Where);
			SetRandomHeadingAngle(VehicleH);
			SetGroup(VehicleH,1);
		}

		if(StartingPrefs & 0x200) {
			Where.x=TeamPos[3*Team+0]-VEHICLE_SPACING_DISTANCE;
			Where.z=TeamPos[3*Team+2]+VEHICLE_SPACING_DISTANCE;
			Where.y=TerrainFindFloor(Where.x,Where.z);
			VehicleH=BuildObject(GetInitialScavengerODF(TeamRace),Team,Where);
			SetRandomHeadingAngle(VehicleH);
			SetGroup(VehicleH,2);
		}

		if(StartingPrefs & 0x400) {
			Where.x=TeamPos[3*Team+0]-2*VEHICLE_SPACING_DISTANCE;
			Where.z=TeamPos[3*Team+2]+VEHICLE_SPACING_DISTANCE;
			Where.y=TerrainFindFloor(Where.x,Where.z);
			VehicleH=BuildObject(GetInitialScavengerODF(TeamRace),Team,Where);
			SetRandomHeadingAngle(VehicleH);
			SetGroup(VehicleH,2);
		}

		if(StartingPrefs & 0x800) {
			Where.x=TeamPos[3*Team+0]+VEHICLE_SPACING_DISTANCE;
			Where.z=TeamPos[3*Team+2];
			Where.y=TerrainFindFloor(Where.x,Where.z);
			VehicleH=BuildObject(GetInitialConstructorODF(TeamRace),Team,Where);
			SetRandomHeadingAngle(VehicleH);
			SetGroup(VehicleH,3);
		}

		if(StartingPrefs & 0x1000) {
			Where.x=TeamPos[3*Team+0]-VEHICLE_SPACING_DISTANCE;
			Where.z=TeamPos[3*Team+2];
			Where.y=TerrainFindFloor(Where.x,Where.z);
			VehicleH=BuildObject(GetInitialConstructorODF(TeamRace),Team,Where);
			SetRandomHeadingAngle(VehicleH);
			SetGroup(VehicleH,4);
		}

		if(StartingPrefs & 0x2000) {
			Where.x=TeamPos[3*Team+0]-VEHICLE_SPACING_DISTANCE;
			Where.z=TeamPos[3*Team+2]-VEHICLE_SPACING_DISTANCE;
			Where.y=TerrainFindFloor(Where.x,Where.z);
			VehicleH=BuildObject(GetInitialHealerODF(TeamRace),Team,Where);
			SetRandomHeadingAngle(VehicleH);
			SetGroup(VehicleH,5);
		}
		if(StartingPrefs & 0x4000) {
			Where.x=TeamPos[3*Team+0]-VEHICLE_SPACING_DISTANCE;
			Where.z=TeamPos[3*Team+2]-VEHICLE_SPACING_DISTANCE;
			Where.y=TerrainFindFloor(Where.x,Where.z);
			VehicleH=BuildObject(GetInitialHealerODF(TeamRace),Team,Where);
			SetRandomHeadingAngle(VehicleH);
			SetGroup(VehicleH,5);
		}

		if(StartingPrefs & 0x8000) {
			Where.x=TeamPos[3*Team+0]-2*VEHICLE_SPACING_DISTANCE;
			Where.z=TeamPos[3*Team+2];
			Where.y=TerrainFindFloor(Where.x,Where.z);
			VehicleH=BuildObject(GetInitialPlayerVehicleODF(TeamRace),Team,Where);
			SetRandomHeadingAngle(VehicleH);
			SetGroup(VehicleH,6);
		}

		if(StartingPrefs & 0xFF0000)
			SetScrap(Team,(StartingPrefs>>16) & 0xFF);
		else
			SetScrap(Team,40);

		if(IsTeamplayOn()) 
			for(int i=GetFirstAlliedTeam(Team);i<=GetLastAlliedTeam(Team);i++) 
				if(i!=Team) {
					// Get a new position near the team's central position
					Vector NewPosition=GetPositionNear(Where,AllyMinRadiusAway,AllyMaxRadiusAway);
					
					// In teamplay, store where offense players were created for respawns later
					TeamPos[3*i+0]=NewPosition.x;
					TeamPos[3*i+1]=NewPosition.y;
					TeamPos[3*i+2]=NewPosition.z;
				} // Loop over allies not the commander
		
		TeamIsSetUp[Team]=true;
	}


	// Given an index into the Player list, build everything for a given
	// single player (i.e. a vehicle of some sorts), and set up the team
	// as well as necessary
	// ______________________________________________________________________
	Handle SetupPlayer(int Team)
	{
		Handle PlayerH;
		Vector Where;

		if((Team<0) || (Team>=MAX_TEAMS))
			return 0; // Sanity check... do NOT proceed

		int TeamBlock=WhichTeamGroup(Team);

		if((!IsTeamplayOn()) || (TeamBlock<0)) {
			// This player is their own commander; set up their equipment.
			SetupTeam(Team);

			// Now put player near his recycler
			Where.x=TeamPos[3*Team+0];
			Where.z=TeamPos[3*Team+2];
			Where.y=TerrainFindFloor(Where.x,Where.z);
		}
		else {
			// Teamplay. Gotta put them near their defensive player. Also,
			// always ensure the recycler/etc has been set up for this
			// team if we know who they are
			SetupTeam(GetCommanderTeam(Team));

			// SetupTeam will fill in the TeamPos[] array of positions
			// for both the commander and offense players, so read out the
			// results
			Where.x=TeamPos[3*Team+0];
			Where.z=TeamPos[3*Team+2];
			Where.y=TerrainFindFloor(Where.x,Where.z);
		} // Teamplay setup

		PlayerH=BuildObject(GetPlayerODF(Team),Team,Where);
		SetRandomHeadingAngle(PlayerH);

		// If on team 0 (dedicated server team), make this object gone from the world
		if(!Team)
			MakeInert(PlayerH);

		return PlayerH;
	}


	// ______________________________________________________________________
	void Init(void)
	{
		Handle PlayerH,PlayerEntryH;

		// Ensure variables pre-init'd to zero at the start

		if(i_array)
			memset(i_array,0,i_count*sizeof(int));
		if(f_array)
			memset(f_array,0,f_count*sizeof(float));
		if(h_array)
			memset(h_array,0,h_count*sizeof(Handle));
		if(b_array)
			memset(b_array,0,b_count*sizeof(bool));

		// Set up some variables based on how things appear in the world
		didInit=true;
		KillLimit=GetVarItemInt("network.session.ivar0");
		TotalGameTime=GetVarItemInt("network.session.ivar1");
		// Skip ivar2-- player limit. Assume the netmgr takes care of that.
		// ivar4 (vehicle prefs 1) read elsewhere.
		StartingPrefs=GetVarItemInt("network.session.ivar7");

		// The BZN has a player in the world. We need to delete them, as
		// this code (either on this machine or remote machines) handles
		// creation of the proper vehicles in the right places for
		// everyone.
		PlayerEntryH=GetPlayerHandle();
		if(PlayerEntryH) 
			RemoveObject(PlayerEntryH);

		// Do all the one-time server side init of varbs. These varbs are
		// saved out and read in on clientside, if saved in the proper
		// place above. This needs to be done after toasting the initial
		// vehicle
		if((ImServer()) || (!IsNetworkOn())) {
			ElapsedGameTime=0;
			if(!CurrentGameTime)
				CurrentGameTime=TotalGameTime*60*10; // convert minutes to 1/10 seconds
				
			// And build the local player for the server
			int LocalTeamNum=GetLocalPlayerTeamNumber(); // Query this from game
			PlayerH=SetupPlayer(LocalTeamNum);
			SetAsUser(PlayerH,LocalTeamNum);
			AddPilotByHandle(PlayerH);
		} // Server or no network
	}


	// Called via execute, 1/10 of a second has elapsed. Update everything.
	// ______________________________________________________________________
	void UpdateGameTime(void)
	{
		ElapsedGameTime++;

		// Are we in a time limited game?
		if(CurrentGameTime>0) {
			CurrentGameTime--;
			if(!(CurrentGameTime%10)) {
				int MinutesLeft=(CurrentGameTime/(10*60));
				int SecondsLeft=(CurrentGameTime/10)%60;
				sprintf(OutTimeString,"Time Left %02d:%02d\n",MinutesLeft,SecondsLeft);
				SetTimerBox(OutTimeString);

				// Also print this out more visibly at important times....
				if((!SecondsLeft) && ((MinutesLeft<=10) || (!(MinutesLeft%5))))
					AddToMessagesBox(OutTimeString);
				else if((!MinutesLeft) && (!(SecondsLeft%5)))
					AddToMessagesBox(OutTimeString);

			}

			// Game over due to timeout?
			if(!CurrentGameTime) {
				NoteGameoverByTimelimit();
				DoGameover(10.0f);
			}

		}
		else { // Infinite time game
			if(!(ElapsedGameTime%10)) {

				sprintf(OutTimeString,"Mission Time %02d:%02d\n",(ElapsedGameTime/(10*60)),(ElapsedGameTime/10)%60);
				SetTimerBox(OutTimeString);
			}
		}
	}


	// Check for absence of recycler & factory, gameover if so.
	// ______________________________________________________________________
	void ExecuteCheckIfGameOver(void)
	{
		float endDelta = 10.0f;

		// Check for a gameover by no recycler & factory
		int i,NumFunctioningTeams=0;
		bool TeamIsFunctioning[MAX_TEAMS];

		memset(TeamIsFunctioning,0,sizeof(TeamIsFunctioning));
		
		for(i=0;i<MAX_TEAMS;i++)				
			if(TeamIsSetUp[i]) {
				bool Functioning=false; // Assume so for now.

				// Check if recycler vehicle still exists. Side effect to
				// note: IsAliveAndPilot zeroes the handle if pilot missing;
				// that'd be bad for us here if we want to manually remove
				// it. Thus, we have a sacrificial varb
				Handle TempH=RecyclerHandles[i];					
				if((!IsAlive(TempH)) || (TempH==0))
					RecyclerHandles[i]=0; // Clear this out for later
				else Functioning=true; 
				
				// Check buildings as well.
				if(GetObjectByTeamSlot(i,DLL_TEAM_SLOT_RECYCLER)!=0)
					Functioning=true;
				if(GetObjectByTeamSlot(i,DLL_TEAM_SLOT_FACTORY)!=0)
					Functioning=true;
					
				if(Functioning) {
					TeamIsFunctioning[i]=true;
					NumFunctioningTeams++;
				} // Has recyclerV,recyclerB or FactoryB
			} // Loop over all set up teams


		// Keep track if we ever had several teams playing. Don't need
		// to check for gameover if so-- 
		if(NumFunctioningTeams>1) {
			HadMultipleFunctioningTeams=true;
			return; // Exit function early
		}

		// Easy Gameover case: nobody's got a functioning base. End everything now.
#ifndef SERVER
		if((NumFunctioningTeams==0) && (ElapsedGameTime>50)) {
			NoteGameoverByNoBases();
			DoGameover(endDelta);
			gameOver=true;
		}
		else
#endif
			if((HadMultipleFunctioningTeams) && (NumFunctioningTeams==1)) {
			// Ok, at one point we had >1 teams playing, now we've got
			// 1. They're the winner.
			
			// In teamplay, report the team as the winner
			if(IsTeamplayOn()) {
				int WinningTeamgroup=-1;
				for(i=0;i<MAX_TEAMS;i++)
					if(TeamIsFunctioning[i]) {
						WinningTeamgroup=WhichTeamGroup(i);
						NoteGameoverByLastTeamWithBase(WinningTeamgroup);
					}
				
				// Also, give all players on winning team points...
				for(i=0;i<MAX_TEAMS;i++) {
					if(WhichTeamGroup(i) == WinningTeamgroup)
						AddScore(GetPlayerHandle(i),ScoreForWinning);
				}
			} // Teamplay is on
			else { // Non-teamplay, report individual winner
				NoteGameoverByLastWithBase(GetPlayerHandle(1));
				for(i=0;i<MAX_TEAMS;i++) {
					if(TeamIsFunctioning[i]) {
						NoteGameoverByLastWithBase(i);
						AddScore(GetPlayerHandle(i),ScoreForWinning);
					} // Found winner team
				}
			} // Non-teamplay.

			DoGameover(endDelta);
			gameOver=true;
		} // Winner
	}


	// This is the function that gets called every 0.1 seconds.
	// ______________________________________________________________________
	void Execute(void)
	{
		if (!didInit)
		{
			Init();
			PortalInit();
		}

		PortalFunctions();

		// Check for absence of recycler & factory, gameover if so.
		ExecuteCheckIfGameOver();

		// Do this as well...
		UpdateGameTime();
	}


	// ______________________________________________________________________
	bool AddPlayer(DPID id, int Team, bool IsNewPlayer)
	{
		if (!didInit) {
			Init();
		}

		if(IsNewPlayer) {
			Handle PlayerH=SetupPlayer(Team);
			SetAsUser(PlayerH,Team);
			AddPilotByHandle(PlayerH);
		}

		return 1; // BOGUS: always assume successful
	}


	// Rebuilds pilot
	// ______________________________________________________________________
	EjectKillRetCodes	RespawnPilot(Handle DeadObjectHandle,int Team)
	{
		Vector Where;

		// Only use safest place if invalid team #
		if((Team<1) || (Team>=MAX_TEAMS)) {
			Where=GetSafestSpawnpoint();
		}
		else {
			// Place them back where originally created
			Where.x=TeamPos[3*Team+0];
			Where.y=TeamPos[3*Team+1];
			Where.z=TeamPos[3*Team+2];
		}

		// Randomize starting position somewhat
		Where=GetPositionNear(Where,RespawnMinRadiusAway,RespawnMaxRadiusAway);
		Where.y=Where.y+25.0f; // Bounce them in the air to prevent multi-kills

		Handle NewPerson=BuildObject(GetInitialPlayerPilotODF(GetRaceOfTeam(Team)),Team,Where);
		SetAsUser(NewPerson,Team);
		AddPilotByHandle(NewPerson);
		SetRandomHeadingAngle(NewPerson);

		// If on team 0 (dedicated server team), make this object gone from the world
		if(!Team)
			MakeInert(NewPerson);

		return DLLHandled; // Dead pilots get handled by DLL
	}


	// ______________________________________________________________________
	EjectKillRetCodes DeadObject(int DeadObjectHandle, int KillersHandle, bool WasDeadPerson)
	{
		// Give positive or negative points to killer, depending on
		// whether they killed enemy or ally
		if((DeadObjectHandle != KillersHandle) && (!IsAlly(DeadObjectHandle,KillersHandle))) {
			// Killed enemy...
			AddKills(KillersHandle,1); // Give them a kill
			if(WasDeadPerson)
				AddScore(KillersHandle,ScoreForKillingPerson);
			else
				AddScore(KillersHandle,ScoreForKillingCraft);
		}
		else {
			AddKills(KillersHandle,-1); // Suicide or teamkill counts as -1 kill
			if(WasDeadPerson)
				AddScore(KillersHandle,-ScoreForKillingPerson);
			else
				AddScore(KillersHandle,-ScoreForKillingCraft);
		}
		
		// Give points to killee-- this always increases
		AddDeaths(DeadObjectHandle,1);
		if(WasDeadPerson)
			AddScore(DeadObjectHandle,ScoreForDyingAsPerson);
		else
			AddScore(DeadObjectHandle,ScoreForDyingAsCraft);

		// Check to see if we have a KillLimit winner
		if((KillLimit) && (GetKills(KillersHandle)>=KillLimit)) {
			NoteGameoverByKillLimit(KillersHandle);
			DoGameover(10.0f);
		}

		// Get team number of who got waxed.
		int DeadTeam=GetTeamNum(DeadObjectHandle);
		if(DeadTeam==0)
			return DoEjectPilot; // Someone on neutral team always gets default behavior

		// If this was a dead pilot, we need to build another pilot back
		// at base. Otherwise, we just eject a pilot from the
		// craft. [This is strat, nobody gets a craft for free when they
		// lose one.]
		if(WasDeadPerson)
			return RespawnPilot(DeadObjectHandle,DeadTeam);
		else 
			return DoEjectPilot;
	}


	// ______________________________________________________________________
	EjectKillRetCodes PlayerEjected(Handle DeadObjectHandle)
	{
		int DeadTeam=GetTeamNum(DeadObjectHandle);
		if(DeadTeam==0)
			return DLLHandled; // Invalid team. Do nothing

		// Update Deaths, Kills, Score for this player
		AddDeaths(DeadObjectHandle,1);
		AddKills(DeadObjectHandle,-1);
		AddScore(DeadObjectHandle,ScoreForDyingAsCraft-ScoreForKillingCraft);

		return DoEjectPilot; // Tell main code to allow the ejection
	}


	// ______________________________________________________________________
	EjectKillRetCodes ObjectKilled(int DeadObjectHandle,int KillersHandle)
	{
		bool WasDeadPerson=IsPerson(DeadObjectHandle);
		// If a craft died, just eject the pilot
		if((GetCurWorld() != 0) || (!WasDeadPerson)) {
			return DoEjectPilot;
		}

		// If a person died, respawn them, etc
		return DeadObject(DeadObjectHandle,KillersHandle,WasDeadPerson);
	}


	// ______________________________________________________________________
	EjectKillRetCodes ObjectSniped(int DeadObjectHandle,int KillersHandle)
	{
		if(GetCurWorld() != 0) {
			return DLLHandled;
		}

		// Dead person means we must always respawn a new person
		return DeadObject(DeadObjectHandle,KillersHandle,true);
	}


	// ______________________________________________________________________
	bool Load(bool missionSave)
	{
		if (missionSave) {
			int i;

			// init bools
			if((b_array) && (b_count))
				for (i = 0; i < b_count; i++)
					b_array[i] = false;

			// init floats
			if((f_array) && (f_count))
				for (i = 0; i < f_count; i++)
					f_array[i] = 0.0f;

			// init handles
			if((h_array) && (h_count))
				for (i = 0; i < h_count; i++)
					h_array[i] = 0;

			// init ints
			if((i_array) && (i_count))
				for (i = 0; i < i_count; i++)
					i_array[i] = 0;

			return true;
		}

		bool ret = true;

		// bools
		if (b_array != NULL)
			Read(b_array, b_count);

		// floats
		if (f_array != NULL)
			Read(f_array, f_count);

		// Handles
		if (h_array != NULL)
			Read(h_array, h_count);

		// ints
		if (i_array != NULL)
			Read(i_array, i_count);

		return ret;
	}


	// ______________________________________________________________________
	bool PostLoad(bool missionSave)
	{
		if (missionSave)
			return true;

		bool ret = true;

		ConvertHandles(h_array, h_count);

		return ret;
	}


	// ______________________________________________________________________
	bool Save(bool missionSave)
	{
		if (missionSave)
			return true;

		bool ret = true;

		// bools
		if (b_array != NULL)
			Write(b_array, b_count);

		// floats
		if (f_array != NULL)
			Write(f_array, f_count);

		// Handles
		if (h_array != NULL)
			Write(h_array, h_count);

		// ints
		if (i_array != NULL)
			Write(i_array, i_count);

		return ret;
	}



	// This function initializes all portals that are labelled PortalA0 ,
	// PortalB1 etc. up to PortalF9.
	// Function by BS-er
	// _________________________________________________________________
	void 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 PortalFunctions()
	{
		for (int i=0; i < PortalCount; i++)
		{
			if (IsAround(Portals[i]))
			{
				int PortalTeam = GetTeamNum(Portals[i]);
				Handle NearVehicle = GetNearestVehicle(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
							}
						}
					}
				}
			}
		}
	}

};



// ______________________________________________________________________
DLLBase *BuildMission(void)
{
	return new Strategy01;
}
