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

#include "..\Shared\DLLBase.h"
#include "..\Shared\MPVehicles.h"
#include "..\Shared\SubMission.h"
#include "..\Shared\Taunts.h"
#include "..\Shared\StartingVehicles.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 = -2;
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 high up to respawn a pilot
const float RespawnPilotHeight=200.0f;

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

// How long a "spawn" kill lasts, in tenth of second ticks. If the
// time since they were spawned to current is less than this, it's a
// spawn kill, and not counted. Make this an ivar for dm?
#ifdef _DEBUG
const int MaxSpawnKillTime=300; // long time, so we can debug this
#else
const int MaxSpawnKillTime=15; // 15 seconds
#endif

// 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 TempMsgString[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
{
public:
	int m_GameTPS; // How many turns per second there are (10, 15, 20, or 30)

	int
		i_first,
		TotalGameTime,
		RemainingGameTime,
		ElapsedGameTime,
		KillLimit, // As specified from the shell
		LastTauntPrintedAt,
		PointsForAIKill, // ivar14
		KillForAIKill, // ivar15
		RespawnWithSniper, // ivar16
		//	TurretAISkill, // ivar17
		//	NonTurretAISkill, // ivar18
		StartingVehiclesMask, // ivar7
		SpawnedAtTime[MAX_TEAMS],
		i_last;

	bool
		b_first,
		didInit,
		HadMultipleFunctioningTeams,
		TeamIsSetUp[MAX_TEAMS],
		NotedRecyclerLocation[MAX_TEAMS], // of deployed recycler
		gameOver,
		// Flag that we're creating the staring vehicles, so AI skill levels
		// need to be dropped a notch.
		CreatingStartingVehicles,
		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)
	{
		EnableHighTPS(m_GameTPS);

		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)
	{
		// All work handled by this function.
		// subMission->AddObject(h, CreatingStartingVehicles, RecyclerHandles);
		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)
	{
		if(RespawnWithSniper)
			strcpy(TempODFName,"ispilo"); // With sniper.
		else
			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)
	{
		memset(TempODFName,0,sizeof(TempODFName));
		const char *Contents=GetVarItemStr("network.session.svar5");
		if((Contents != NULL) && (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)
	{
#ifndef EDITOR
		if((Team<1) || (Team>=MAX_TEAMS))
			return;
		if((IsTeamplayOn()) && (TeamIsSetUp[Team]))
			return;

		if(Team == 1)
			DoTaunt(TAUNTS_GameStart);

		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, if it's not preplaced on the map.
		if(GetObjectByTeamSlot(Team, DLL_TEAM_SLOT_RECYCLER) == 0)
		{
			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 all optional vehicles for this team.
		Where.x=TeamPos[3*Team+0]; // restore default after we modified this for recy above
		Where.y=TeamPos[3*Team+1];
		Where.z=TeamPos[3*Team+2];
		// Drop skill level while we build things.
		CreatingStartingVehicles=true;
		StartingVehicleManager::CreateVehicles(Team,TeamRace,StartingVehiclesMask,Where);
		CreatingStartingVehicles=false;

		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;
#endif // #ifndef EDITOR
	}

	// 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)
	{
#ifndef EDITOR
		Handle PlayerH;
		Vector Where;

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

		SpawnedAtTime[Team]=ElapsedGameTime; // Note when they spawned in.

		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;
#endif // #ifndef EDITOR
	}

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

#ifndef EDITOR
		// StartingVehicle file. Set if specified.
		const char *Contents=GetVarItemStr("network.session.svar6");
		if((Contents != NULL) && (Contents[0]) )
			StartingVehicleManager::Init(Contents);

		// Set up some variables based on how things appear in the world
		didInit=true;
		KillLimit=GetVarItemInt("network.session.ivar0");
		TotalGameTime=GetVarItemInt("network.session.ivar1");

		StartingVehiclesMask=GetVarItemInt("network.session.ivar7");
		PointsForAIKill=(GetVarItemInt("network.session.ivar14") != 0);
		KillForAIKill=(GetVarItemInt("network.session.ivar15") != 0);
		RespawnWithSniper=(GetVarItemInt("network.session.ivar16") != 0);

		// 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;
			LastTauntPrintedAt=-2000; // Force first message to be printed...
			// Let the taunt system know where to find things it uses.
			InitTaunts(&ElapsedGameTime,&LastTauntPrintedAt);

			if(!RemainingGameTime)
				RemainingGameTime=TotalGameTime * 60 * m_GameTPS; // convert minutes to 1/10 seconds

		} // Server or no network

		// And build the local player for the server
		int LocalTeamNum=GetLocalPlayerTeamNumber(); // Query this from game
		PlayerH=SetupPlayer(LocalTeamNum);
		SetAsUser(PlayerH,LocalTeamNum);
		AddPilotByHandle(PlayerH);
#endif // #ifndef EDITOR
	}

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

		// Are we in a time limited game?
		if(RemainingGameTime>0)
		{
			RemainingGameTime--;
			if(!(RemainingGameTime % m_GameTPS))
			{
				// Convert tenth-of-second ticks to hour/minutes/seconds format.
				int Seconds = RemainingGameTime / m_GameTPS;
				int Minutes = Seconds / 60;
				int Hours = Minutes / 60;
				// Lop seconds and minutes down to 0..59 once we've grabbed
				// non-remainder out.
				Seconds %= 60;
				Minutes %= 60;

				if(Hours)
					sprintf(TempMsgString,"Time Left %d:%02d:%02d\n",Hours,Minutes,Seconds);
				else
					sprintf(TempMsgString,"Time Left %d:%02d\n",Minutes,Seconds);
				SetTimerBox(TempMsgString);

				// Also print this out more visibly at important times....
				if(Hours == 0)
				{
					// Every 5 minutes down to 10 minutes, then every minute
					// thereafter.
					if((Seconds == 0) && ((Minutes <= 10) || (!(Minutes % 5))))
						AddToMessagesBox(TempMsgString);
					else
					{
						// Every 5 seconds when under a minute is remaining.
						if((Minutes == 0) && (!(Seconds % 5)))
							AddToMessagesBox(TempMsgString);
					}
				}
			}

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

		}
		else { 
			// Infinite time game. Periodically update ingame clock.
			if(!(ElapsedGameTime % m_GameTPS))
			{

				// Convert tenth-of-second ticks to hour/minutes/seconds format.
				int Seconds = ElapsedGameTime / m_GameTPS;
				int Minutes = Seconds / 60;
				int Hours = Minutes / 60;
				// Lop seconds and minutes down to 0..59 once we've grabbed
				// non-remainder out.
				Seconds %= 60;
				Minutes %= 60;

				if(Hours)
					sprintf(TempMsgString,"Mission Time %d:%02d:%02d\n",Hours,Minutes,Seconds);
				else
					sprintf(TempMsgString,"Mission Time %d:%02d\n",Minutes,Seconds);
				SetTimerBox(TempMsgString);
			}
		}
	}


	// Check for absence of recycler & factory, gameover if so.
	void ExecuteCheckIfGameOver(void)
	{
		// Exit now!...
		if(gameOver)
			return;

		const 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.
				Handle RecyH = GetObjectByTeamSlot(i,DLL_TEAM_SLOT_RECYCLER);
				if(RecyH)
				{
					Functioning=true;

					// Note deployed location first time it deploys, also every 25.6 seconds
					if((!NotedRecyclerLocation[i]) || (!(ElapsedGameTime % 0xFF)))
					{
						NotedRecyclerLocation[i] = true;
						Vector RecyPos;
						GetPosition(RecyH,RecyPos);
						TeamPos[3*i+0] = RecyPos.x;
						TeamPos[3*i+1] = RecyPos.y;
						TeamPos[3*i+2] = RecyPos.z;
						// Apply this to thugs also.
						if(IsTeamplayOn())
						{
							for(int jj=GetFirstAlliedTeam(i);jj<=GetLastAlliedTeam(i);jj++)
							{
								// In teamplay, store where offense players were created for respawns later
								TeamPos[3*jj+0]=RecyPos.x;
								TeamPos[3*jj+1]=RecyPos.y;
								TeamPos[3*jj+2]=RecyPos.z;
							} // Loop over allies not the commander
						}
					} // Note deployed location
				}
				else
				{
					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])
							{

								if(i ==1 )
									DoTaunt(TAUNTS_CPURecyDestroyed);
								else if(i ==1 )
									DoTaunt(TAUNTS_HumanRecyDestroyed);

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

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

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

		subMission->Execute(TeamIsSetUp, RecyclerHandles);
#endif // #ifndef EDITOR
	}

	bool AddPlayer(DPID id, int Team, bool IsNewPlayer)
	{
#ifndef EDITOR
		if (!didInit)
		{
			Init();
		}
		else if (IsNewPlayer)
			DoTaunt(TAUNTS_NewHuman);

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

#endif // #ifndef EDITOR
		return 1; // BOGUS: always assume successful
	}

	void DeletePlayer(DPID id)
	{
		DoTaunt(TAUNTS_LeftHuman);
	}

	// 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
		{
			Where.x=TeamPos[3*Team+0];
			Where.y=TeamPos[3*Team+1];
			Where.z=TeamPos[3*Team+2];
			SpawnedAtTime[Team]=ElapsedGameTime; // Note when they spawned in.
		}

		Vector OldPos;
		// As this object was just killed, gotta use the slower search for its
		// position.
		GetPosition2(DeadObjectHandle,OldPos);

		// Find out how far we are away from starting location... use
		// default if couldn't get position of DeadObjectHandle
		float RespawnHeight=RespawnPilotHeight;
		if((fabsf(OldPos.x) > 0.01f) && (fabsf(OldPos.z) > 0.01f))
		{
			// Position valid. Use it.
			float DistanceAway=sqrtf((OldPos.x-Where.x) * (OldPos.x-Where.x) + 
				(OldPos.z-Where.z) * (OldPos.z-Where.z));

#ifdef _DEBUG
			char TempStr[1024];
			sprintf(TempStr,"Distance away = %7.2f from (%3.2f %3.2f) (%3.2f %3.2f)",
				DistanceAway,OldPos.x,OldPos.z,Where.x,Where.z);
			AddToMessagesBox(TempStr);
#endif

			RespawnHeight=DistanceAway * 0.5f;
			if(RespawnHeight < 100.0f)
				RespawnHeight = 100.0f;
			else if(RespawnHeight > 500.0f)
				RespawnHeight = 500.0f;
		}

		// Any humans not on the right team? Make life hell for them.
		int Grp = WhichTeamGroup(Team);
		if(Grp != 0)
		{
			RespawnHeight += 600.0f;
		}

		// Randomize starting position somewhat
		Where=GetPositionNear(Where,RespawnMinRadiusAway,RespawnMaxRadiusAway);
		Where.y+=RespawnHeight; // 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, bool WasDeadAI)
	{
		bool KillerIsAI=!IsPlayer(KillersHandle);

		// Get team number of who got waxed.
		int DeadTeam=GetTeamNum(DeadObjectHandle);
		bool IsSpawnKill=false;
		// Flip scoring if this is a spawn kill.
		int SpawnKillMultiplier=1;

		// Give positive or negative points to killer, depending on
		// whether they killed enemy or ally
		if(1) //!KillerIsAI)
		{
			// Spawnkill is a non-suicide, on a human by another human.
			if((DeadObjectHandle != KillersHandle) && 
				(DeadTeam > 0) && (DeadTeam < MAX_TEAMS) && (SpawnedAtTime[DeadTeam] > 0))
			{
				IsSpawnKill = (ElapsedGameTime - SpawnedAtTime[DeadTeam]) < (MaxSpawnKillTime * m_GameTPS);
			}

			if(IsSpawnKill)
			{
				SpawnKillMultiplier=-1;
				sprintf(TempMsgString,"Spawn kill by %s on %s\n",
					GetPlayerName(KillersHandle),GetPlayerName(DeadObjectHandle));
				AddToMessagesBox(TempMsgString);
			}

			if((DeadObjectHandle != KillersHandle) && (!IsAlly(DeadObjectHandle,KillersHandle)))
			{
				// Killed enemy...
				if((!WasDeadAI) || (KillForAIKill))
					AddKills(KillersHandle,1*SpawnKillMultiplier); // Give them a kill

				if((!WasDeadAI) || (PointsForAIKill))
				{
					if(WasDeadPerson)
						AddScore(KillersHandle,ScoreForKillingPerson*SpawnKillMultiplier);
					else
						AddScore(KillersHandle,ScoreForKillingCraft*SpawnKillMultiplier);
				}
			}
			else
			{
				if((!WasDeadAI) || (KillForAIKill))
					AddKills(KillersHandle,-1*SpawnKillMultiplier); // Suicide or teamkill counts as -1 kill

				if((!WasDeadAI) || (PointsForAIKill))
				{
					if(WasDeadPerson)
						AddScore(KillersHandle,-ScoreForKillingPerson*SpawnKillMultiplier);
					else
						AddScore(KillersHandle,-ScoreForKillingCraft*SpawnKillMultiplier);
				}
			}
		} // Killer was a human

		if(1)  //!WasDeadAI)
		{
			// Give points to killee-- this always increases
			AddDeaths(DeadObjectHandle,1*SpawnKillMultiplier);
			if(WasDeadPerson)
				AddScore(DeadObjectHandle,ScoreForDyingAsPerson*SpawnKillMultiplier);
			else
				AddScore(DeadObjectHandle,ScoreForDyingAsCraft*SpawnKillMultiplier);
		}

		// 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.
		if(DeadTeam==0)
			return DoEjectPilot; // Someone on neutral team always gets default behavior

		if(WasDeadAI)
		{
			// Snipe?
			if(WasDeadPerson)
				return DLLHandled;
			else // Nope. Eject.
				return DoEjectPilot;
		}
		else  // Not DeadAI, i.e. a human!
		{
			if((DeadTeam > 0) && (DeadTeam < 6))
				DoTaunt(TAUNTS_HumanShipDestroyed);

			// 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 WasDeadAI=!IsPlayer(DeadObjectHandle);

		bool WasDeadPerson=IsPerson(DeadObjectHandle);

		// Sanity check for multiworld
		if(GetCurWorld() != 0)
			return DoEjectPilot;

		int DeadTeam=GetTeamNum(DeadObjectHandle);
		if(DeadTeam==0)
			return DoEjectPilot; // Someone on neutral team always gets default behavior

		if(((DeadTeam > 0) && (DeadTeam < 6)) && (!WasDeadAI))
			DoTaunt(TAUNTS_HumanShipDestroyed);

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


	EjectKillRetCodes ObjectSniped(int DeadObjectHandle,int KillersHandle)
	{
		bool WasDeadAI=!IsPlayer(DeadObjectHandle);

		if(GetCurWorld() != 0)
		{
			return DLLHandled;
		}

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

	bool Load(bool missionSave)
	{
		EnableHighTPS(m_GameTPS);

		// We're a 1.3 DLL.
		WantBotKillMessages();

		// Make sure we always call this
		StartingVehicleManager::Load(missionSave);

		// Do this for everyone as well.
		ClearObjectives();
		AddObjective("mpobjective_st.otf", WHITE, -1.0f); // negative time means don't change display to show it

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

			// Always do this to hook up clients with the taunt engine as well.
			InitTaunts(&ElapsedGameTime,&LastTauntPrintedAt);

			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)
	{
		// Make sure we always call this
		StartingVehicleManager::Save(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;
	}
};

DLLBase *BuildMission(void)
{
	subMission = BuildSubMission();
	return new Strategy01;
}
