// BZII Community Project Modified MPI DLL.  This class handles win/lose and other game
// management similar to a standard strat DLL.  Several of its functions call functions 
// of a partner class called instantMission (MPInstant.cpp), which handles certain AI
// tasks and portal-related features.  The instance of this class is referenced by the
// pointer variable called subMission.

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

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

extern bool CheckedSVar3;


#define VEHICLE_SPACING_DISTANCE (20.0f)
#define MAX_PLAYERS 9
#define MAX_TEAMS 16
// # of multiple-user teams are possible in Teamplay
#define MAX_MULTIPLAYER_TEAMS 2

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

// How much vertical displacement is tolerated from the center's position
const float MaxVerticalDisplacement=10.0f;
// How many times to try and find a suitable location
const int MaxRetries=32;
const float PI=3.141592654f;

// 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];

// static FILE *stratLog = NULL;

static SubMission *subMission = NULL;

class Strategy01 : public DLLBase
{
	int
	i_first,
		TotalGameTime,
		CurrentGameTime,
		ElapsedGameTime,
		KillLimit, // As specified from the shell
		StartingPrefs, // also from shell-- ivar7
		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
		f_last;

	Handle
	h_first,
		RecyclerHandles[MAX_TEAMS],
		h_last;

	// 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:

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(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_array = &h_first + 1;
		h_count = &h_last - &h_first - 1;

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


	// _________________________________________________________________
	~Strategy01()
	{
		delete subMission;
		subMission = NULL;
	}


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


	// _________________________________________________________________
	void AddObject(Handle h)
	{
		char Race = GetRace(h);
		char turr[10] = "ivturr";
		turr[0] = Race;
		char atank[10] = "ivatank";
		atank[0] = Race;
		char gtow[10] = "ibgtow";
		gtow[0] = Race;


		if (IsOdf(h,turr))
			SetSkill(h, 2);
		else if (IsOdf(h,atank) || IsOdf(h,gtow)) {
			SetSkill(h, 2);
		}
		else
			SetSkill(h, 3);
		
		subMission->AddObject(h);
	}

	
	// _________________________________________________________________
	void DeleteObject(Handle h)
	{
		subMission->DeleteObject(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;
	}


	// 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=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>5)) {
			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
	}


	// _________________________________________________________________
	void Execute(void)
	{
		if (!didInit) {
			Init();
		}

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

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

		subMission->Execute(TeamIsSetUp,RecyclerHandles);
	}


	// _________________________________________________________________
	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
	}

	// _________________________________________________________________
	void DeletePlayer(DPID id)
		{
		}


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

			subMission->Load(missionSave);

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

		subMission->Load(missionSave);

		return ret;
	}

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

		bool ret = true;

		ConvertHandles(h_array, h_count);

		subMission->PostLoad(missionSave);
		CheckedSVar3=false;

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

		subMission->Save(missionSave);

		return ret;
	}

};


// This function creates 2 different class instances, and returns a pointer to
// the instance of Strategy01 that it creates.  The subMission pointer points to
// an instance of instantMission, which handles AI and portal stuff.
// _________________________________________________________________
DLLBase *BuildMission(void)
{
	subMission = BuildSubMission();
	return new Strategy01;
}
