//===========================================================================//
// File:	MWMission.cpp																		  //
//---------------------------------------------------------------------------//
//   Date    Who  Modification                                               //
// --------  ---  ---------------------------------------------------------- //
// 09/09/98  JSE  Inital base class based off of Shadowrun/Adept                                //
//---------------------------------------------------------------------------//
// Copyright (C) 1998, Fasa Interactive										 //
// All Rights reserved worldwide                                             //
// This unpublished sourcecode is PROPRIETARY and CONFIDENTIAL               //
//===========================================================================//

#include "MW4Headers.hpp"

#include "MWMission.hpp"
#include "VehicleInterface.hpp"
#include "Narc.hpp"
#include "MWGUIManager.hpp"
#include "MWGame.hpp"
#include "rail_move.hpp"
#include "move_formation.hpp"
#include "abl.h"
#include "obstacle.hpp"
#include "ai.hpp"
#include "gameinfo.hpp"
#include "AI_CombatTacticInterface.hpp"
#include "cameraship.hpp"
#include "ai_groups.hpp"
#include "data_client.hpp"
#include "AI_Statistics.hpp"
#include "AI_UserConstants.hpp"
#include "AI_Types.hpp"
#include "MWDebugHelper.hpp"
#include "MWApplication.hpp"
#include "GameTypes.h"
#include "MWObject.hpp"
#include "CombatAI.hpp"
#include "mwguimanager.hpp"
#include "hudcomm.hpp"
#include "DamageDispatch.hpp"
#include "ABLAudio.hpp"
#include "MWPlayer.hpp"
#include "HeatManager.hpp"

#include <Adept\Tool.hpp>
#include <Adept\Mission.hpp>
#include <Adept\Controls.hpp>
// MSL 5.02 headshot
#include <Adept\DamageObject.hpp>
#include <Adept\VideoRenderer.hpp>
#include <Adept\CameraComponent.hpp>
#include <Adept\Map.hpp>
#include "NavPoint.hpp"
#include "mech.hpp"
#include "Team.hpp"

#include <ElementRenderer\StateChange.hpp>

#include <MLR\MLRTexturePool.hpp>
#include <MLR\MLRTexture.hpp>

#include <MissionLang\Resource.h>

#include <mbstring.h>
#define __mbsrchr(s,c) (char*)_mbsrchr((const unsigned char*)(s),(c))

#include "bucket.hpp"

// ngLog addition	// JKK
#if !defined(NO_LOG)
#include "nglog_mw4.hpp"
#endif // !defined(NO_LOG)

#include "windows.h"
#include "ctcls.h" // jcem
#include "ctcl.h" // jcem
#include "recscore.h" // jcem

extern int g_nMR;

#ifdef LAB_ONLY
extern float frameRate;

extern DWORD PrimitivesRendered;
extern DWORD PointsRendered;
extern DWORD LinesRendered;
extern DWORD TrianglesRendered;
extern DWORD QuadsRendered;
extern DWORD TextureKUsed,AlphaTextureKUsed,TextureVKUsed;
extern DWORD MidLevelRenderer::LitVertices;
#endif

extern int GetScore4Player(int& nKills, int& nDeaths);
namespace ABL
{
    bool ObjectIDisGroup(long object_id);
    Group::identifier ObjectIDtoGroupID(long object_id);
};

#pragma warning (push)
#include <vector>
#pragma warning (pop)

DECLARE_TIMER(static, Mission_Script_Time);

const Stuff::Scalar nav_refresh_delay = 2.0f;

//#############################################################################
//###############################    MWMission    #############################
//#############################################################################

MWMission::ClassData*
	MWMission::DefaultData = NULL;

void ABL_Debug_func (char *s);
namespace ABL
{
	extern TypePtr			IntegerTypePtr;
	TypePtr execStandardRoutineCall (SymTableNodePtr routineIdPtr);
};
namespace MW4AI
{
	extern __int64	tABLTime;
};

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	MWMission::InitializeClass()
{


	Verify(!DefaultData);
	DefaultData =
		new ClassData(
			MWMissionClassID,
	        "MechWarrior4::MWMission",
			Mission::DefaultData,
			0, NULL,
			(Entity::Factory)Make,
			(Entity::CreateMessage::Factory)CreateMessage::ConstructCreateMessage,
			ExecutionStateEngine::DefaultData,
			(Entity::GameModel::Factory)GameModel::ConstructGameModel,
			NULL,
			(Entity::GameModel::ReadAndVerifier)GameModel::ReadAndVerify,
			(Entity::GameModel::ModelWrite)GameModel::WriteToText,
			(Entity::GameModel::ModelSave)GameModel::SaveGameModel
		);
	Register_Object(DefaultData);

	CUSTOM_DIRECT_ATTRIBUTE(MWMission,
		StartMusicStatus,startMusicStatus,int,IntClassID);
	CUSTOM_DIRECT_ATTRIBUTE(MWMission,
		TransMusicStatus,transMusicStatus,int,IntClassID);

	Initialize_Timer(Mission_Script_Time, "Mission_Script_Time");
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	MWMission::TerminateClass()
{
	Unregister_Object(DefaultData);
	delete DefaultData;
	DefaultData = NULL;
}

bool MWMission::ReloadABL(ABL::ABLError *err)
{
	UnloadABL ();

	ABL::ABLi_init(767999, //AblSymbolTableHeapSize,
			  511999, //AblStackHeapSize,
			  307199, //AblCodeHeapSize,
			  20479, //AblRunTimeStackSize,
			  51199, //AblMaxCodeBlockSize,
			  200, //AblMaxRegisteredModules,
			  100, //AblMaxStaticVariables,
#ifdef LAB_ONLY
				ABL_Debug_func,
				true,
				true,
				true
#else
				NULL,
				false,
				false,
				false
#endif
				);


	char filename[128];
	Str_Copy(filename, "content\\ablscripts\\miscfuncs.abl", ELEMENTS(filename));
	if (ABL::ABLi_loadLibrary(filename, err,NULL,NULL) < 0)
		return false;
	return true;
}

void MWMission::UnloadABL(void)
{
	ABL::ABLi_close ();
}

void MWMission::UnloadScript (void)
{
	if (missionBrain)
	{
		Check_Pointer (missionBrain);
		delete missionBrain;
		missionBrain = NULL;
	}
}

bool MWMission::ReloadScript (ABL::ABLError *err)
{
	UnloadScript ();
	ABLConstNameTable ();
	MW4AI::InitFormation ();
	char name[128];

	missionBrain = NULL;
	m_FirstFrame = true;

	if (scriptName[0] != 0)
	{
		Str_Copy(name, scriptName, ELEMENTS(name));
		missionScriptHandle = ABL::ABLi_preProcess(name, err,NULL,NULL);
		if (missionScriptHandle <0)
			return false;

		gosASSERT(missionScriptHandle >= 0);
		
		missionBrain = new ABL::ABLModule;
		gosASSERT(missionBrain != NULL);
			
		long brainErr = missionBrain->init(missionScriptHandle);
		gosASSERT(brainErr == 0);
		if (brainErr)
			STOP(("1???"));			// todo fix this to proper exit.
		
		missionBrain->setName("MW4Mission");
	}
	else
	{
		missionScriptHandle = 0;
		missionBrain = NULL;
		scriptName[0] = 0;
	}
	return true;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
MWMission*
	MWMission::Make(
		CreateMessage *message,
		ReplicatorID *base_id
	)
{
	Check_Object(message);

	if (MW4AI::Statistics::Enabled() == true)
	{
		InitDataClient(0);
	}

	gos_PushCurrentHeap(s_Heap);
	Verify(message->m_mapResourceID == ResourceID(MapResourceFileID,1));
	MWMission *new_entity =
		new MWMission(DefaultData, message, base_id, NULL);
	gos_PopCurrentHeap();
	Check_Object(new_entity);
	return new_entity;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Replicator::CreateMessage*
	MWMission::SaveMakeMessage(MemoryStream *stream, ResourceFile *res_file)
{
	Check_Object(this);
	Check_Object(stream);

	stream->AllocateBytes(sizeof(CreateMessage));
	Mission::SaveMakeMessage(stream, res_file);
	CreateMessage *message = Cast_Pointer(CreateMessage*, stream->GetPointer());
	message->messageLength = sizeof(*message);
	message->railgraphStreamResourceID = railgraphStreamResourceID;
	Str_Copy (message->scriptName,scriptName,ELEMENTS(message->scriptName));
	return message;
}


//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//

bool OldMission(const char *mission_name)
{
#if 1 // jcem - no old missions^^
	MString lower_mission_name = mission_name;
	lower_mission_name.ToLower();

	// these are exact matches as opposed to substrings like in the rest of the function
	if (0 == strcmp(lower_mission_name,"jungle"))
	{
		return true;
	}

	if (0 == strcmp(lower_mission_name,"tropical"))
	{
		return true;
	}

	if (0 == strcmp(lower_mission_name,"gladiatorpit"))
	{
		return true;
	}

	if (0 == strcmp(lower_mission_name,"rubblepile"))
	{
		return true;
	}

	if (0 == strcmp(lower_mission_name,"manorhouseassault"))
	{
		return true;
	}

	if (0 == strcmp(lower_mission_name,"paradisehold"))
	{
		return true;
	}

	if (0 == strcmp(lower_mission_name,"tumbleweedassault"))
	{
		return true;
	}

	if (0 == strcmp(lower_mission_name,"royalguard"))
	{
		return true;
	}

	if (0 == strcmp(lower_mission_name,"defianceholds"))
	{
		return true;
	}

	if (0 == strcmp(lower_mission_name,"griffonbase"))
	{
		return true;
	}

	if (strstr(lower_mission_name,"equinox"))
	{
		return true;
	}

	if (strstr(lower_mission_name,"highlands"))
	{
		return true;
	}

	if (strstr(lower_mission_name,"hogsback"))
	{
		return true;
	}

	if (strstr(lower_mission_name,"polartundra"))
	{
		return true;
	}

	if (strstr(lower_mission_name,"snowbound"))
	{
		return true;
	}

	if (strstr(lower_mission_name,"wetlands"))
	{
		return true;
	}

	if (strstr(lower_mission_name,"scrub"))
	{
		return true;
	}

	if (strstr(lower_mission_name,"lakefront"))
	{
		return true;
	}

	if (strstr(lower_mission_name,"bigcity"))
	{
		return true;
	}

	if (strstr(lower_mission_name,"centralpark"))
	{
		return true;
	}

	if (strstr(lower_mission_name,"dustbowl"))
	{
		return true;
	}

	if (strstr(lower_mission_name,"frostbite"))
	{
		return true;
	}

	if (strstr(lower_mission_name,"gatorbait"))
	{
		return true;
	}

	if (strstr(lower_mission_name,"innercity"))
	{
		return true;
	}

	if (strstr(lower_mission_name,"lunacy"))
	{
		return true;
	}

	if (strstr(lower_mission_name,"palacegates"))
	{
		return true;
	}

	if (strstr(lower_mission_name,"snowjob"))
	{
		return true;
	}

	if (strstr(lower_mission_name,"timberline"))
	{
		return true;
	}

	if (strstr(lower_mission_name,"coliseum"))
	{
		return true;
	}

	if (strstr(lower_mission_name,"factory"))
	{
		return true;
	}

	if (strstr(lower_mission_name,"canyon"))
	{
		return true;
	}

	if (strstr(lower_mission_name, "ghosthighway"))
	{
		return true;
	}

	if (strstr(lower_mission_name, "spaceport"))
	{
		return true;
	}

	if (strstr(lower_mission_name, "rubble"))
	{
		return true;
	}

	if (strstr(lower_mission_name, "vbase"))
	{
		return true;
	}

	if (strstr(lower_mission_name, "aspen"))
	{
		return true;
	}

	if (strstr(lower_mission_name, "cantina"))
	{
		return true;
	}

	if (strstr (lower_mission_name, "minehq"))
	{
		return true;
	}

	if (strstr (lower_mission_name, "riogrande"))
	{
		return true;
	}

	if (strstr (lower_mission_name, "mechworks"))
	{
		return true;
	}

	if (strstr (lower_mission_name, "lakeside"))
	{
		return true;
	}

	if (strstr (lower_mission_name, "lakesidehold"))
	{
		return true;
	}
	
	if (strstr (lower_mission_name, "mountain"))
	{
		return true;
	}

	if (strstr (lower_mission_name, "mountainhold"))
	{
		return true;
	}

	if (strstr (lower_mission_name, "hideaway"))
	{
		return true;
	}

	if (strstr (lower_mission_name, "hotplate"))
	{
		return true;
	}

	if (strstr (lower_mission_name, "freezer"))
	{
		return true;
	}

	if (strstr (lower_mission_name, "grassland"))
	{
		return true;
	}

	if (strstr (lower_mission_name, "peaks"))
	{
		return true;
	}
#endif // jcem

	return false;
}



//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
MWMission::MWMission(
	ClassData *class_data,
	CreateMessage *message,
	ReplicatorID *base_id,
	ElementRenderer::Element *element
):
	Mission(class_data, message, base_id, element),
	m_CheckChain (NULL),
	m_narcs(NULL, true),
	m_ChanceLancematesEject(-1),
	m_ChanceLancematesOKWhenEjecting(-1),
	m_ChanceLancematesInjuredWhenEjecting(-1),
	animatedTextures(NULL),
	animatedTexturesMultipliers(NULL),
	m_textureAnimInstances(NULL),
	m_textureAnimIterators(NULL),
	animatedTextureNames(NULL),
	m_LancemateChain (NULL),
	m_reactionSpheres(NULL),
	m_playerOnlyReactionSpheres(NULL),
	m_LastNavRefresh(0),
	m_FlagCaptureEnabled(true),
	m_FlagsAsNavPoints(false),
	m_ConstraintTopLeft(Point3D::Identity),
	m_ConstraintBottomRight(Point3D::Identity),
    m_b30notified(false), m_b60notified(false), // jcem - 30/60 seconds to mission termination
    m_fShouldBeStopped(0.0f), // jcem - no abrupt ending for "No Return" game...
	m_bOrgBoundsStored(false),
	m_bBoundsWarningsEnabled(false)
	//m_warningPolygonOld,
	//m_missionPolygonOld,
	//m_ConstraintTopLeftOld,
	//m_ConstraintBottomRightOld,
{
    InitCOOP(); // jcem

	Check_Pointer(this);
	Check_Object(message);

	m_MissionSuccess = false;
	m_BucketManager = new CBucketManager ();

	//m_BucketManager->SetupTrack (CBucketManager::KILLS);
	//m_BucketManager->SetupTrack (CBucketManager::DEATHS);

	lastShutdownTest = (float)gos_GetElapsedTime();;
	shutDownTimer = (float)gos_GetElapsedTime();;

	m_numberOfAnims = 0;
	iteratorManager = NULL;

	m_MaxFlagsCarried = 1;
	m_FlagDropReturnTime = 30;
	m_FlagCaptureReturnTime = 0;
	m_FlagsEnabled = FLAGS_HIDE;

	MW4AI::UserConstants::IncrementRefCount();
	MW4AI::Types::IncrementRefCount();
	ABL::AudioManager::IncrementRefCount();

	char missname[255];
	memset (missname,0,255);

	{
		Check_Object(this);
		char *loc,*loc2;
		const char *name;
		
		name = GetModelName ();
		Verify (name);
		loc = __mbsrchr (name,'\\');
		if (loc)
		{
			loc2 = strchr (loc,'.');
			if (loc2)
			{
				Verify ((loc2 - loc) < 255);
				strncpy (missname,loc+1,loc2 - loc-1);
				missname[255-1] = 0;
				AddCreationData ("Mission", (const char *) missname,0);
			}
			else
			{
				strncpy (missname,loc,255);
				missname[255-1] = 0;
				AddCreationData ("Mission", (const char *) missname,0);
			}
		}
		else
		{
			strncpy (missname,name,255);
			missname[255-1] = 0;
			AddCreationData ("Mission", (const char *) missname,0);
		}
	}

	// Determine if this mission needs the special crowd noise support
	// Note, for Mercs, the designers have made all the single-player missions play
	// sounds, etc. by hand in .ABL scripts instead of using this code, so the
	// below matches only the multiplayer maps.
	MString lower_mission_name = missname;
	lower_mission_name.ToLower();
	if (0 == strcmp(lower_mission_name,"coliseum"))
		m_missionType = MISSIONTYPE_COLISEUM;
	else if (0 == strcmp(lower_mission_name,"factory"))
		m_missionType = MISSIONTYPE_FACTORY;
	else if (0 == strcmp(lower_mission_name,"gladiatorpit"))
		m_missionType = MISSIONTYPE_COLISEUM;
	else if (0 == strcmp(lower_mission_name,"rubblepile"))
		m_missionType = MISSIONTYPE_FACTORY;
	else if (0 == strcmp(lower_mission_name,"grassland"))
		m_missionType = MISSIONTYPE_JUNGLE;
	else if (0 == strcmp(lower_mission_name,"jungle"))
		m_missionType = MISSIONTYPE_JUNGLE;
	else if (0 == strcmp(lower_mission_name,"tropical"))
		m_missionType = MISSIONTYPE_JUNGLE;
	else if (0 == strcmp(lower_mission_name,"snowjob"))
		m_missionType = MISSIONTYPE_ARCTIC;
	else if (0 == strcmp(lower_mission_name,"frostbite"))
		m_missionType = MISSIONTYPE_ARCTIC;
	else if (0 == strcmp(lower_mission_name,"freezer"))
		m_missionType = MISSIONTYPE_ARCTIC;
	else if (0 == strcmp(lower_mission_name,"gatorbait"))
		m_missionType = MISSIONTYPE_SWAMP;
	else if (0 == strcmp(lower_mission_name,"hotplate"))
		m_missionType = MISSIONTYPE_HOTPLATE;
	else
		m_missionType = MISSIONTYPE_UNSPECIFIED;


#if 0

	// this key mapping is a little odd
	// it is in MWMission so that you don't
	// need an player/vehicle/interface
	// to get a pause menu
	// Dedicated servers run without local players.

	Check_Object(ControlsManager::Instance);
	ControlsManager::Instance->CreateMapping(
		NULL,
		0,
		Application::GetInstance(),
		Application::PauseGameMessageID,
		Application::GetInstance(),
		-1,
		ControlsManager::VirtualButtonGroupID,
		KEY_ESCAPE,
		false,
		1,
		1
	);

#endif


	MechWarrior4::AI::CreateGraveyard();
	MoverAI::g_AlwaysIgnoreObstacles = false;

	ABLConstNameTable ();
	MW4AI::InitFormation ();
	

	m_DistanceCheck = message->distanceCheck;
	missionBrain = NULL;
	m_FirstFrame = true;
	ABL::ABLError err;

	if (message->scriptName[0] != 0 && Network::GetInstance()->AmIServer())
	{
		MString script_name;
		Str_Copy(scriptName,message->scriptName,ELEMENTS(scriptName));
		script_name = scriptName;

		NetMissionParameters::MWNetMissionParameters* net_params = MWApplication::GetInstance()->GetLocalNetParams();

		if (net_params != 0 && MWApplication::GetInstance()->networkingFlag)
		{

		
			if (OldMission(missname))
			{
				switch (net_params->m_ruleType)
				{
				case StockDestruction:
					script_name.StripExtension();
					script_name += "_Destruction.abl";
					break;
				case StockTeamDestruction:
					script_name.StripExtension();
					script_name += "_TeamDestruction.abl";
					break;
				case StockAttrition:
					script_name.StripExtension();
					script_name += "_Attrition.abl";
					break;
				case StockTeamAttrition:
					script_name.StripExtension();
					script_name += "_TeamAttrition.abl";
					break;
				case StockCaptureTheFlag:
					script_name.StripExtension();
					script_name += "_CTF.abl";
					break;
				case StockKingOfTheHill:
					script_name.StripExtension();
					script_name += "_KOTH.abl";
					break;
				case StockTeamKingOfTheHill:
					script_name.StripExtension();
					script_name += "_TKOTH.abl";
					break;
				case StockTerritories:
					script_name.StripExtension();
					script_name += "_Territories.abl";
					break;
				case StockStealTheBacon:
					script_name.StripExtension();
					script_name += "_STB.abl";
					break;
				case StockCaptureBase:
					script_name.StripExtension();
					script_name += "_CaptureBase.abl";
					break;
				case StockDestroyObjective:
					script_name.StripExtension();
					script_name += "_DestroyObjective.abl";
					break;
				case StockEscort:				
					script_name.StripExtension();
					script_name += "_Escort.abl";
					break;
				case StockMasterTrial:				
					script_name.StripExtension();
					script_name += "_MasterTrial.abl";
					break;
				case StockSiegeAssault:				
					script_name.StripExtension();
					script_name += "_SiegeAssault.abl";
					break;
				}
			}
			else
			{
				switch (net_params->m_ruleType)
				{
				case StockDestruction:
					script_name = "Content\\ABLScripts\\StockScripts\\Stock_Destruction.abl";
					break;
				case StockTeamDestruction:
					script_name = "Content\\ABLScripts\\StockScripts\\Stock_TeamDestruction.abl";
					break;
				case StockAttrition:
					script_name = "Content\\ABLScripts\\StockScripts\\Stock_Attrition.abl";
					break;
				case StockTeamAttrition:
					script_name = "Content\\ABLScripts\\StockScripts\\Stock_TeamAttrition.abl";
					break;
				case StockCaptureTheFlag:
					script_name = "Content\\ABLScripts\\StockScripts\\Stock_CTF.abl";
					break;
				case StockKingOfTheHill:
					script_name = "Content\\ABLScripts\\StockScripts\\Stock_KOTH.abl";
					break;
				case StockTeamKingOfTheHill:
					script_name = "Content\\ABLScripts\\StockScripts\\Stock_TKOTH.abl";
					break;
				case StockTerritories:
					script_name = "Content\\ABLScripts\\StockScripts\\Stock_Territories.abl";
					break;
				case StockStealTheBacon:
					script_name = "Content\\ABLScripts\\StockScripts\\Stock_STB.abl";
					break;
				case StockCaptureBase:
					script_name = "Content\\ABLScripts\\StockScripts\\Stock_CaptureBase.abl";
					break;
				case StockDestroyObjective:
					script_name = "Content\\ABLScripts\\StockScripts\\Stock_DestroyObjective.abl";
					break;
				case StockEscort:
					script_name = "Content\\ABLScripts\\StockScripts\\Stock_Escort.abl";
					break;
				case StockMasterTrial:
					script_name = "Content\\ABLScripts\\StockScripts\\Stock_MasterTrial.abl";
					break;
				case StockSiegeAssault:
					script_name = "Content\\ABLScripts\\StockScripts\\Stock_SiegeAssault.abl";
					break;
				}
			}


			switch (net_params->m_ruleType)
			{
			case CustomDestruction:
			case CustomTeamDestruction:
			case CustomAttrition:
			case CustomTeamAttrition:
			case CustomCaptureTheFlag:
			case CustomKingOfTheHill:	
			case CustomTeamKingOfTheHill:
			case CustomTerritories:	
			case CustomStealTheBacon:		
			case CustomCaptureBase:		
			case CustomDestroyObjective:	
			case CustomEscort:
			case CustomMasterTrial:
			case CustomSiegeAssault:
			case CustomCampaign:
			case CustomUndefined:
				script_name = net_params->m_scriptName;
			}


		}


		missionScriptHandle = ABL::ABLi_preProcess(script_name, &err,NULL,NULL);
		if (missionScriptHandle < 0)
		{
			ablREPORT (missionScriptHandle >= 0,err.Details ());
			missionScriptHandle = 0;
			missionBrain = NULL;
		}
		else
		{
			missionBrain = new ABL::ABLModule;
			gosASSERT(missionBrain != NULL);
				
			long brainErr = missionBrain->init(missionScriptHandle);
			gosASSERT(brainErr == 0);
			if (brainErr)
				STOP(("1???"));			// todo fix this to proper exit.
			
			missionBrain->setName("MW4Mission");
		}
	}
	else
	{
		missionScriptHandle = 0;
		missionBrain = NULL;
		scriptName[0] = 0;
	}

//////////////////////////////////////////
//  Load the rail graph
//////////////////////////////////////////

	railgraphStreamResourceID = message->railgraphStreamResourceID;

	MW4AI::g_MissionGraph = NULL;
	if (message->railgraphStreamResourceID != ResourceID::Null)
	{
		Resource stream(railgraphStreamResourceID);
		Verify(stream.DoesResourceExist());

		RegisteredClass::ClassID class_id;
		stream >> class_id;
		MW4AI::CRailGraph::ClassData *class_data = Cast_Pointer(MW4AI::CRailGraph::ClassData*, RegisteredClass::FindClassData(class_id));
		Check_Object(class_data);

		gos_PushCurrentHeap(Heap);
		MW4AI::g_MissionGraph = new MW4AI::CRailGraph(class_data,&stream);
		gos_PopCurrentHeap();
		Check_Object(MW4AI::g_MissionGraph);
		stream.UnloadData();
	}

	ABL::SetABLExecFunction (ABL::execStandardRoutineCall);

	startMusicStatus = 1;
	transMusicStatus = 0;

	MW4AI::SetupPermRects ();
	MW4AI::SetupAIStagger ();
	MW4AI::ResetGlobalTriggers();

	#ifdef LAB_ONLY
	MW4AI::CombatTacticInterface::g_DisableMovement = false;
	MW4AI::CombatTacticInterface::g_DisableFiring = false;

	MechWarrior4::DebugHelper::m_AIOff = false;
	#endif

	m_numberOfAnims = 0;
	m_numberOfMovies = 0;

	Building::AddAllBuildingsToSensorCellMap();
}


//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
MWMission::~MWMission()
{
	Check_Object(this);
	Check_Object(MWApplication::GetInstance());

	HeatManager::g_OverrideHeatOption = false;
	InputTrainer::GetInstance()->SetEnabled(InputTrainer::ALL,true);

	if(MWPlayer::GetInstance())
	{
		Check_Object(MWPlayer::GetInstance());

		if(MWPlayer::GetInstance()->vehicle)
		{
			ScoreObject *score_object = GetScoreMember(MWPlayer::GetInstance()->vehicle->GetReplicatorID());
			if(score_object)
			{
				MWApplication::GetInstance()->m_lastMissionDeaths = score_object->GetDeaths();
				MWApplication::GetInstance()->m_lastMissionKills = score_object->GetKills();
			}
		}
	}

	Building::RemoveAllBuildingsFromSensorCellMap();

	ABL::AudioManager::DecrementRefCount();
	MW4AI::UserConstants::DecrementRefCount();
	MW4AI::Types::DecrementRefCount();

	#ifdef LAB_ONLY
	if (MW4AI::Statistics::Enabled() == true)
	{
		MW4AI::Statistics::Quit();
	}
	#endif

	MW4AI::KillPermRects ();

	if (MW4AI::g_MissionGraph)
	{
		Check_Object(MW4AI::g_MissionGraph);
		delete MW4AI::g_MissionGraph;
		MW4AI::g_MissionGraph = NULL;
	}
	MW4AI::g_PathManager->CleanRequests ();
	if (missionBrain)
	{
		Check_Pointer (missionBrain);
		delete missionBrain;
		missionBrain = NULL;
	}

	MW4AI::ClearFormation ();

	MechWarrior4::AI::DestroyGraveyard();

	if (MW4AI::Statistics::Enabled() == true)
	{
		KillDataClient ();
	}

	m_textureAnimInstances.DeletePlugs();
	m_textureAnimIterators.DeletePlugs();
	if(iteratorManager)
		delete iteratorManager;


	int size,i;
	size = animatedTextureNames.GetLength ();
	for (i=0;i<size;i++)
	{
		delete animatedTextureNames[i];
	}

	size = movieTextureNames.GetLength ();
	for (i=0;i<size;i++)
	{
		delete movieTextureNames[i];
	}
	movieTextureNames.SetLength(0);

	size = movieTextures.GetLength ();
	for (i=0;i<size;i++)
	{
		delete movieTextures[i];
	}
	movieTextures.SetLength(0);

	movieTexturesFrameTime.SetLength(0);

	delete m_BucketManager;
	m_BucketManager = NULL;

	m_reactionSpheres.DeletePlugs();
	m_playerOnlyReactionSpheres.DeletePlugs();

    ExitCOOP(); // jcem
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	MWMission::PreCollisionExecute(Time till)
{
	Check_Object(this);
	PRECOLLISION_LOGIC("MWMission");

	MWApplication* app = Cast_Object(MWApplication*,MWApplication::GetInstance());


	if (app->GetApplicationState() != ApplicationStateEngine::PreRenderState)
	{
		// don't run abl mission stuff
		

		gos_PushCurrentHeap(Heap);



		NetMissionParameters::MWNetMissionParameters *params = app->GetLocalNetParams();


		bool join_in_progress_check = false;
		if (!params->m_joinInProgress)
		{
			join_in_progress_check = true;
		}
		if ((params->m_joinInProgressCutOff) && GetMissionTime() > params->m_joinInProgressCutOffTime*60)
		{
			join_in_progress_check = true;
		}

		if (app->networkingFlag == true)
		{
			if (params->m_respawnLimit)
			{
				if ((float)gos_GetElapsedTime() - lastShutdownTest > 2.0f)
				{
					lastShutdownTest = (float)gos_GetElapsedTime();

					if (!TestForGameShutDown(ReplicatorID::Null))
					{
						shutDownTimer = (float)gos_GetElapsedTime();
					}
// MSL 5.02 No Return
// Timer After fade to black for No Return
					// we haven't been told to not shut down in one minute...
					if (gos_GetElapsedTime() - shutDownTimer > 3.0f)
					{
						app->QueStopGame();
					}
				}
			}
			else if (join_in_progress_check)
			{
				if ((float)gos_GetElapsedTime() - lastShutdownTest > 2.0f)
				{
					lastShutdownTest = (float)gos_GetElapsedTime();

					if (!TestForGameShutDownUnlimitedRespawn())
					{
						shutDownTimer = (float)gos_GetElapsedTime();
					}
// MSL 5.02 No Return
// Timer After fade to black for No Return
					// we haven't been told to not shut down in one minute...
					if (gos_GetElapsedTime() - shutDownTimer > 3.0f)
					{
						app->QueStopGame();
					}
				}
			}
		}



		if(m_FirstFrame)
		{
#ifdef LAB_ONLY
			AddDebugData ("FrameRate",&frameRate,hud_float_type);
//			AddDebugData ("Video Mem used",&TextureKUsed,hud_int_type);
			AddDebugData ("Lit vertices",&MidLevelRenderer::LitVertices,hud_int_type);
			AddDebugData ("Primitives Rendered",&PrimitivesRendered,hud_int_type);

			AddDebugData ("Active Mechs",&AI::m_LastActiveMechAI,hud_int_type);
			AddDebugData ("Active Vehicles",&AI::m_LastActiveVehicleAI,hud_int_type);
			AddDebugData ("BoardGame Mechs",&AI::m_LastBoardGameMechAI,hud_int_type);
			AddDebugData ("BoardGame Vehicles",&AI::m_LastBoardGameVehicleAI,hud_int_type);
#endif
			CreateLancemateGroups ();
			const GameModel *model = GetGameModel();
			Check_Object(model);
			if(model->m_animatedTexturesResourceID != ResourceID::Null)
			{
				InitializeTextureAnimations(model->m_animatedTexturesResourceID);
			}
			if(model->m_movieTexturesResourceID != ResourceID::Null)
			{
				InitializeTextureMovies(model->m_movieTexturesResourceID);
			}
		}
		Scalar time_slice = GetTimeSlice(till);
		if ((m_BucketManager) &&
			(app->networkingFlag == true))
		{
			m_BucketManager->UpdateFrame (time_slice);
		}
		PlayAnimations(time_slice);
		PlayTextureMovies(time_slice);

		ChainIteratorOf<Entity *> iterator(&m_CheckChain);
		Entity *ent;
		while((ent = iterator.ReadAndNext()) != NULL)
		{
			m_CheckChain.Remove (ent);
		}
		int j,size,current,ia;
	  NameTable *table = NameTable::GetInstance();

		size = table->nameTableArray[NameTable::PlayerArray].GetLength ();
		current = 0;
		stlport::vector<MWObject *> lance;
		stlport::vector<bool> lanceadd;
		stlport::vector<MWObject *>::iterator iter;
		stlport::vector<bool>::iterator iter2;

		MW4AI::Groups::GetLancemates (lance);

		lanceadd.assign (lance.size (),false);
		for (j=0;j<size;j++)
		{
			NameTableEntry *data;

			data = &table->nameTableArray	[NameTable::PlayerArray][j];
			ent = data->dataPointer->GetCurrent ();
			if (!ent)
				continue;
			m_CheckChain.Add (ent);
			ia=0;
			for (iter = lance.begin ();iter!=lance.end ();iter++)
			{
				if (!(*iter)->IsWithin (ent,100))
					lanceadd[ia] = true;
				ia++;
			}
		}
		ia = 0;
		for (iter2 = lanceadd.begin ();iter2!=lanceadd.end ();iter2++)
		{
			if (*iter2)
				m_CheckChain.Add (lance[ia]);
			ia++;
		}
		if (CameraShipManager::Instance)
		{
			Entity *ent = CameraShipManager::Instance->GetActiveCamera();
			if (ent)
				m_CheckChain.Add (ent);
		}



		MW4AI::g_AIExecutionNumber++;
		MW4AI::g_AIExecutionNumber %= MW4AI::AIExecutionDelta;
#ifdef LAB_ONLY
		if (MW4AI::g_AIExecutionNumber == 0)
		{
			AI::m_LastBoardGameMechAI = AI::m_BoardGameMechAI;
			AI::m_LastBoardGameVehicleAI = AI::m_BoardGameVehicleAI;
			AI::m_LastActiveMechAI = AI::m_ActiveMechAI;
			AI::m_LastActiveVehicleAI = AI::m_ActiveVehicleAI;
			AI::m_BoardGameMechAI = 0;
			AI::m_BoardGameVehicleAI = 0;
			AI::m_ActiveMechAI = 0;
			AI::m_ActiveVehicleAI = 0;
		}
#endif
		if ((missionBrain) && (!m_FirstFrame))
		{
			PRECOLLISION_LOGIC("Mission::Script");
			__int64 subtime = GetCycles ();
			Start_Timer(Mission_Script_Time);
	#ifdef LAB_ONLY
			try
			{
 				missionBrain->execute ();
			}
			catch (...)
			{
				ablREPORT (false,ABL::ablERROR ("Critical failure in abl mission script, turning off script.  Continue at your own risk"));
				delete missionBrain;
				missionBrain = NULL;
			}
	#else
			missionBrain->execute();
	#endif
			MW4AI::tABLTime+=GetCycles()-subtime;
			Stop_Timer(Mission_Script_Time);
		}
		else if (m_FirstFrame)
		{
			#ifdef LAB_ONLY
			MWGameInfo::InitMissionInfo ();

			if (MW4AI::Statistics::Enabled() == true)
			{
				MW4AI::Statistics::Init();
			}
			#endif
		}

        if (m_EndMissionTimer.Running ()) {
          Scalar fCurTime = m_EndMissionTimer.CurrTime ();
          if (g_nMR != 2) {
              // jcem - 30/60 seconds to mission termination
              if (!m_b30notified) {
                  Scalar fRemain = m_EndTime - fCurTime;
                  if (fRemain > 0.0f) {
                      if (!m_b60notified) {
                          if ((58.0 <= fRemain) && (fRemain < 60.9)) {
                              AudioCommand *asdf = AudioCommand::Create(AudioRenderer::MechanicalType, "vo\\created\\end60.wav{handle}", 1.0f, 1.0f);
                              if (asdf) {
                                  Check_Object(asdf);
                                  asdf->Play();
                              }
                              m_b60notified = true;
                          }
                      }
                      if ((29.0 <= fRemain) && (fRemain < 30.9)) {
                          AudioCommand *asdf = AudioCommand::Create(AudioRenderer::MechanicalType, "vo\\created\\end30.wav{handle}", 1.0f, 1.0f);
                          Check_Object(asdf);
                          asdf->Play();
                          m_b30notified = true;
                      }
                  }
              }
          }

          if (Application::GetInstance()->serverFlag) {
#ifdef _DEBUG
          if (g_nMR != 2) {
#endif // _DEBUG
			if (fCurTime >= m_EndTime)
			{
				DisconnectLancemates ();
				Check_Object (Application::GetInstance());

				//Just incase the player dies even though he succeeded the goals before the mission ends
				if(MWPlayer::GetInstance())
				{
					if(MWPlayer::GetInstance()->vehicle)
					{
						if(MWPlayer::GetInstance()->vehicle->IsDestroyed())
						{
							EndMissionState(false);
						}
					}
				}
				app->m_currentMissionStatus = EndMissionState();
				if(MWGame::GetInstance())
					MWGame::GetInstance()->ReactToEndMission(EndMissionState());
				Application::GetInstance()->QueStopGame ();
			}
#ifdef _DEBUG
          }
#endif // _DEBUG
          }
        }

	    m_FirstFrame = false;
	
	    gos_PopCurrentHeap();
	}

	ABL::AudioManager::GetInstance()->Update();
	ResolveReactionSpheres();
	Mission::PreCollisionExecute(till);
}


void MWMission::CreateLancemateGroups()
{
	VehicleInterface* vehicle_interface = VehicleInterface::GetInstance();
	if ((vehicle_interface != 0) &&
		(vehicle_interface->vehicle != 0) &&
		(vehicle_interface->vehicle->IsDerivedFrom(Mech::DefaultData) == true))
	{
													// make sure the player's vehicle is in the group
		Mech* player_mech = Cast_Object(Mech*,vehicle_interface->vehicle);
		AddToLancemateGroup(player_mech,player_mech->GetTeam());
	}

	ChainIteratorOf<AI *> iterator(&m_LancemateChain);
	AI *ai;
	while((ai = iterator.ReadAndNext()) != NULL)
	{
		if ((ai->getEntity() != 0) &&
			(ai->getEntity()->IsDestroyed() == false) &&
			(ai->getEntity()->IsDerivedFrom(Mech::DefaultData) == true))
		{
			Mech *mech = Cast_Object (Mech *,ai->getEntity ());

			if (MWApplication::GetAlignmentSchema() != MWApplication::MP_NON_TEAMPLAY)
			{
				AddToLancemateGroup(mech,mech->GetTeam());
			}
		}
	}
	MWGUIManager *gui;
	gui = MWGUIManager::GetInstance ();
	if (gui)
	{
		if (gui->Component (MWGUIManager::HUD_COMM))
		{
			HUDComm *comm;
			comm = Cast_Object (HUDComm *,gui->Component (MWGUIManager::HUD_COMM));
			if (comm)
			{
				comm->SetupLancemates ();
			}
		}
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void MWMission::AddToLancemateGroup(Mech *lancemate, int team_number)
{
	Verify (lancemate);

	if (lancemate == 0)
	{
		return;
	}

	if ((Adept::Mission::GetInstance() != 0) &&
		(Adept::Mission::GetInstance()->IsDerivedFrom(MWMission::DefaultData) == true))
	{
		MWMission* mwmission = Cast_Object(MWMission*,Adept::Mission::GetInstance());
		GroupContainer& group_container = mwmission->GetGroupContainer();

		Group::identifier group_id;
		
		if (team_number == MWApplication::No_Team)
		{
			group_id = 1;
		}
		else
		{
			group_id = 950 + team_number;
		}
														// create the group if it doesn't exist
		if (group_container.GroupExists(group_id) == false)
		{
			group_container.AddObjectToGroup(*lancemate,group_id);
		}

		Verify(group_container.GroupExists(group_id) == true);
		if (group_container.GroupExists(group_id) == false)
		{
			return;
		}

														// add us to the group if we're not in it
		Group& group = group_container.GetGroup(group_id);

		if (std::find(lancemate->GetGroups().begin(),lancemate->GetGroups().end(),group_id) == lancemate->GetGroups().end())
		{
			lancemate->GetGroups().push_back(group_id);
		}

		std::vector<MWObject*> members;
		group.GetMembers(members);
		if (std::find(members.begin(),members.end(),lancemate) == members.end())
		{
			group.AddObject(lancemate->objectID);
		}

		bool bSetSquadAI;

		if (MWApplication::GetInstance()->networkingFlag == false) {
			bSetSquadAI = true;
		} else {
			if (Application::GetInstance()->serverFlag)
				bSetSquadAI = true;
			else
				bSetSquadAI = false;
		}
														// make sure the group has a GROUPAI_LANCEMATE AI type
		if (bSetSquadAI)
		{
			if ((group.GetAI() == 0) ||
				(group.GetAI()->GetID() != MW4AI::Squad::GROUPAI_LANCEMATE))
			{
				Stuff::Auto_Ptr<MW4AI::Squad::AI> ai_ptr(MW4AI::Squad::CreateAI((MW4AI::Squad::ID)MW4AI::Squad::GROUPAI_LANCEMATE));
				group.SetSquadAI(ai_ptr);
			}
		}
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void MWMission::AddLancemate(AI *lancemate)
{
	m_LancemateChain.Add (lancemate);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void MWMission::DisconnectLancemates(void)
{
	ChainIteratorOf<AI *> iterator(&m_LancemateChain);
	AI *ai;
	while((ai = iterator.ReadAndNext()) != NULL)
	{
		ai->DisconnectLancemate ();
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	MWMission::ResetMissionLights()
{
	Check_Object(this);
	Check_Object(VideoRenderer::Instance);
	
	Mission::ResetMissionLights();

#if 0
	const GameModel *model = GetGameModel();
	Check_Object(model);

	Adept::CameraComponent *camera = VideoRenderer::Instance->GetSecondaryCamera(1);

	if (camera)
	{
		Check_Object(camera);

		ElementRenderer::StateChange *view_states = camera->GetElement()->GetViewingStateChange();
		view_states->EnableChildLightingControl();

		if(m_isNightMission)
		{
			view_states->SetFogColor(model->m_nightFogColor);
			if(m_nightSky)
				camera->GetElement()->AdoptSkyElement(m_nightSky);
		}

	}
#endif

}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	MWMission::SetLightAmpMissionLights()
{
	Check_Object(this);
	Check_Object(VideoRenderer::Instance);

	Mission::SetLightAmpMissionLights();

#if 0
	const GameModel *model = GetGameModel();
	Check_Object(model);

	Adept::CameraComponent *camera = VideoRenderer::Instance->GetSecondaryCamera(1);

	if (camera)
	{
		Check_Object(camera);

		ElementRenderer::StateChange *view_states = camera->GetElement()->GetViewingStateChange();


		view_states->DisableChildLightingControl();
		view_states->SetFogColor(model->m_fogColor);
		camera->GetElement()->AdoptSkyElement(m_sky);
	}
#endif
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	MWMission::AddNarc(Narc *narc, int alignment)
{
	Check_Object(this);
	Check_Object(narc);

	//Verify(alignment < Entity::LastAlignment);

	Narc *current_narc = m_narcs.Find(alignment);

	if(current_narc)
	{
		m_narcs.Remove(current_narc);
		current_narc->SentenceToDeathRow();
	}

	m_narcs.AddValue(narc, alignment);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Narc
	*MWMission::GetNarc(int alignment)
{
	Check_Object(this);
	
	return m_narcs.Find(alignment);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	MWMission::InitializeTextureAnimations(const ResourceID& anim_resource_id)
{
	Check_Object(this);

	if(anim_resource_id != ResourceID::Null)
	{
		Resource stream(anim_resource_id);
		Verify(stream.DoesResourceExist());
	
		int size;
		stream >> size;
		animatedTextures.SetLength(size);
		animatedTexturesMultipliers.SetLength(size);
		animatedTextureNames.SetLength(size);
		m_numberOfAnims = 0;
		for(int i=0; i<size; i++)
		{
			animatedTextures[i] = NULL;
		}

		for(i=0; i<size; i++)
		{
			animatedTexturesMultipliers[i] = 1.0f;
		}

		
		for(i=0; i<size; i++)
		{
			MString texture_name;
			stream >> texture_name;
			animatedTextureNames[i] = new PlugOf<MString>(texture_name);
			MidLevelRenderer::MLRTexture *texture = (*MidLevelRenderer::MLRTexturePool::Instance)((const char *)texture_name);
			if(texture)
			{
				texture->SetAnimateTexture(true);
			}
			animatedTextures[i] = texture;
			MString animation_name;
			stream >> animation_name;
			MW4Animation::AnimInstance *anim_instance = MW4Animation::AnimInstanceManager::Anim_Instance_Manager->MakeAnimInstance(
				animation_name,
				(MW4Animation::AnimInstanceManager::UserDefinedSearchFunction)FindIndex,
				(void *)this
			);
			m_textureAnimInstances.Add(anim_instance);

			Scalar anim_speed;
			stream >> anim_speed;
			animatedTexturesMultipliers[i] = anim_speed;

			MW4Animation::AnimIterator *anim_iterator = new MW4Animation::AnimIterator(anim_instance);
			anim_iterator->ResetStart();
			m_textureAnimIterators.Add(anim_iterator);
			
			m_numberOfAnims ++;
		}

		iteratorManager = new MW4Animation::AnimHierarchyIteratorManager(
			(MW4Animation::AnimHierarchyIteratorManager::UserDefinedApplyChannelFunction)MWMission::ApplyChannel,
			this
		);
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	MWMission::ApplyChannel(
		MW4Animation::ChannelApplication &channel_data,
		int joint_index,
		void *user_data
	)
{
	MWMission *mission;
	mission = Cast_Object(MWMission*, user_data);



	MidLevelRenderer::MLRTexture *texture = mission->animatedTextures[joint_index];

	//If we didn't find it then it must not be loaded yet...try to load it
	if(!texture)
	{
		MString texture_name = mission->animatedTextureNames[joint_index]->GetItem();
		texture = (*MidLevelRenderer::MLRTexturePool::Instance)((const char *)texture_name);
		if(texture)
		{
			texture->SetAnimateTexture(true);
			mission->animatedTextures[joint_index] = texture;
		}
	}

	//We have the texture and we are going to animate
	if(texture)
	{
		Scalar anim_speed = mission->animatedTexturesMultipliers[joint_index];
		texture->SetAnimateTexture(true);
		Stuff::AffineMatrix4D *texture_matrix = texture->GetTextureMatrix();
		Verify(channel_data.hasPosition);
		texture_matrix->entries[3] = channel_data.positionValue.x * anim_speed;
		texture_matrix->entries[7] = channel_data.positionValue.z * anim_speed;
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
int
	MWMission::FindIndex(const char *joint_name, void *user_data)
{
	MWMission *mission;
	mission = Cast_Object(MWMission*, user_data);

	return mission->m_numberOfAnims;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	MWMission::PlayAnimations(Scalar time_slice)
{
	Check_Object(this);
	
	MW4Animation::AnimIterator *anim_iterator;
	ChainIteratorOf<MW4Animation::AnimIterator *> iterator(&m_textureAnimIterators);

	time_slice *= 0.07f;



	if(iteratorManager)
	{
		Check_Object(iteratorManager);
		iteratorManager->PushLayer(1.0f);
		while((anim_iterator = iterator.ReadAndNext()) != NULL)
		{
			if (anim_iterator->IncrementTime(time_slice))
	
			{
				// anim ended so restart it and add the carryover
				Scalar carryOver = anim_iterator->GetCarryOverTime();
				anim_iterator->SetPosition(carryOver);
			}
			iteratorManager->AddAnimToCurrentLayer(anim_iterator);
		}
		iteratorManager->PopLayer();
		iteratorManager->BlendAndApply(m_numberOfAnims);
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	MWMission::InitializeTextureMovies(const ResourceID& movie_resource_id)
{
	Check_Object(this);
	if(movie_resource_id != ResourceID::Null)
	{
		Resource stream(movie_resource_id);
		Verify(stream.DoesResourceExist());
	
		int size;
		stream >> size;
		movieTextures.SetLength(size);
		movieTexturesFrameTime.SetLength(size);
		movieTextureNames.SetLength(size);
		m_numberOfMovies = 0;

		for(int i=0; i<size; i++)
		{
			movieTextures[i] = NULL;
		}

		for(i=0; i<size; i++)
		{
			movieTexturesFrameTime[i] = 0.01f;
		}

		
		for(i=0; i<size; i++)
		{
			MString texture_name;
			stream >> texture_name;
			stream >> movieTexturesFrameTime[i];

			movieTextureNames[i] = new PlugOf<MString>(texture_name);
			MidLevelRenderer::MLRTexture *texture = (*MidLevelRenderer::MLRTexturePool::Instance)((const char *)texture_name);
			if(texture)
			{
				movieTextures[i] = Cast_Object(MidLevelRenderer::MLRMovieTexture *, texture);

				movieTextures[i]->SetFrameTime(movieTexturesFrameTime[i]);
			}


			m_numberOfMovies ++;
		}
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	MWMission::PlayTextureMovies(Scalar time_slice)
{
	Check_Object(this);

	for(int i=0;i<m_numberOfMovies;i++)
	{
		if(movieTextures[i]==NULL)
		{
			MString texture_name = movieTextureNames[i]->GetItem();
			MidLevelRenderer::MLRTexture *texture = (*MidLevelRenderer::MLRTexturePool::Instance)((const char *)texture_name);
			if(texture)
			{
				movieTextures[i] = Cast_Object(MidLevelRenderer::MLRMovieTexture *, texture);

				movieTextures[i]->SetFrameTime(movieTexturesFrameTime[i]);

				movieTextures[i]->RollAFrame(time_slice);
			}
		}
		else
		{
			movieTextures[i]->RollAFrame(time_slice);
		}
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	MWMission::TestInstance() const
{
	Verify(IsDerivedFrom(DefaultData));
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
GroupContainer&
	MWMission::GetGroupContainer()
{
	return m_GroupContainer;
}

const GroupContainer&
	MWMission::GetGroupContainer() const
{
	return m_GroupContainer;
}

void MWMission::ABLConstNameTable (void)
{
	int i,j,size;
	char prefix[5];
  NameTable *table = NameTable::GetInstance();
	Check_Object (table);

	prefix[4] = 0;
	for (i=NameTable::DefaultArray;i<NameTable::TableSize;i++)
	{
		if (i == NameTable::PlayerArray)
		{
			continue;
		}

		switch (i)
		{
			case NameTable::VehicleArray:
				strncpy (prefix,"eve_",4);
				break;
			case NameTable::BuildingArray:
				strncpy (prefix,"ebu_",4);
				break;
			case NameTable::SoundArray:
				strncpy (prefix,"eso_",4);
				break;
			case NameTable::PathArray:
				strncpy (prefix,"epa_",4);
				break;
			case NameTable::NavArray:
				strncpy (prefix,"ena_",4);
				break;
			case NameTable::ObjectiveArray:
				strncpy (prefix,"eob_",4);
				break;
			case NameTable::DropZoneArray:
				strncpy (prefix,"edr_",4);
				break;
			case NameTable::AIArray:
				strncpy (prefix,"eai_",4);
				break;
			case NameTable::TurretArray:
				strncpy (prefix,"etu_",4);
				break;
			case NameTable::CameraShipArray:
				strncpy (prefix,"eca_",4);
				break;
			case NameTable::FlagArray:
				strncpy (prefix,"efl_",4);
				break;
			default:
				strncpy (prefix,"ede_",4);
				break;
		}
		size = table->nameTableArray[i].GetLength ();
		for (j=0;j<size;j++)
		{
			NameTableEntry *data;
			char name[256];
			char *temp;

			data = &table->nameTableArray	[i][j];

			ABL::SymTableNodePtr constantIdPtr;
			sprintf (name,"%s%s",prefix,(char *) data->objectName);
			temp = name;
			while (*temp != 0)
			{
				*temp = (char) tolower (*temp);

				if (*temp == ' ')
				{
					*temp = '_';
				}

				temp++;
			}
			if ((constantIdPtr = ABL::searchSymTable(name, ABL::SymTableDisplay[0])) == NULL)
				constantIdPtr = ABL::enterSymTable(name, &ABL::SymTableDisplay[0]);

			constantIdPtr->defn.key = ABL::DFN_CONST;
			constantIdPtr->library = NULL;

			constantIdPtr->typePtr = ABL::setType(ABL::IntegerTypePtr);
			constantIdPtr->defn.info.constant.value.integer = data->GetObjectID ();
		}
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
	Stuff::Scalar GetTonnage(const Adept::ReplicatorID& rep_id)
	{
		Connection	*connection = Network::GetInstance()->GetConnection(rep_id.connectionID);
		if (connection == 0)
		{
			return (-1);
		}

		Check_Object(connection);
		Replicator* rep = connection->FindReplicator(rep_id);
		if (rep == 0)
		{
			return (-1);
		}

		Entity *ent = Cast_Object(Entity*,rep);
		if (ent->IsDerivedFrom(MWObject::DefaultData) == false)
		{
			return (-1);
		}

		MWObject* mwobject = Cast_Object(MWObject*,ent);
		return (mwobject->GetTonage());
	}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// MSL 5.02 headshot
//void MWMission::ScoringReactToMechDeath(const Adept::ReplicatorID& inflicting_id, const Adept::ReplicatorID& victim_id)
void MWMission::ScoringReactToMechDeath(const Adept::ReplicatorID& inflicting_id, const Adept::ReplicatorID& victim_id, int damageMode)
{
	if (!Application::GetInstance()->serverFlag)
		return;

	ReplicatorID inflicting_id2 = inflicting_id;
    Mech* pMech2 = MechFromRepId(inflicting_id2);
	Connection *connection = Network::GetInstance()->GetConnection(inflicting_id2.connectionID);

	if (connection != 0)
	{
		Check_Object(connection);
		Replicator* rep = connection->FindReplicator(inflicting_id2);

		if (rep != 0)
		{
			Entity* ent = Cast_Object(Entity*,rep);

			if (ent->IsDerivedFrom(Mech::DefaultData) == true)
			{
				Mech* inflicting_mech = Cast_Object(Mech*,ent);

				if ((inflicting_mech->IsDestroyed() == true) &&
					(inflicting_mech->m_whoShotMeLast != ReplicatorID::Null))
				{
					inflicting_id2 = inflicting_mech->m_whoShotMeLast;
					pMech2 = MechFromRepId(inflicting_id2);
				}
			}
		}
	}
    Mech* pMechVictim = MechFromRepId(victim_id);

// MSL 5.05
	MWApplication* app = Cast_Object(MWApplication*,MWApplication::GetInstance());
//
//	ReplicatorID inflicting_id2 = inflicting_id;
//	Mech* pMech2 = MechFromRepId(inflicting_id2);
//	if (pMech2) {
//		if (pMech2->IsDestroyed() && (pMech2->m_whoShotMeLast != ReplicatorID::Null))
//		{
//			inflicting_id2 = pMech2->m_whoShotMeLast;
//			pMech2 = MechFromRepId(inflicting_id2);
//		}
//	}
//	Mech* pMechVictim = MechFromRepId(victim_id);


/*
    if (CTCL_IsConsoleX() && (g_nMR != 2)) { // jcem
        if ((inflicting_id2 != Adept::ReplicatorID::Null) && (victim_id != Adept::ReplicatorID::Null)) {
            if (inflicting_id2 != victim_id) {
	            bool player_audio_event = false;

	            if ((VehicleInterface::GetInstance() != 0) &&
		            (VehicleInterface::GetInstance()->vehicle != 0) &&
		            (VehicleInterface::GetInstance()->vehicle->GetReplicatorID() == inflicting_id2))
	            {
		            player_audio_event = true;
	            }

	            if (app->TeamsAreFriendly(inflicting_id2,victim_id) == true)
	            {
			        if ((victim_id != inflicting_id2) ||
				        (inflicting_id != inflicting_id2))
			        {
				        if (player_audio_event == true)
				        {
					        VehicleInterface::GetInstance()->ReactToEvent(VehicleInterface::TEAMMATEDESTROYED);
				        }
			        }
	            }
	            else
	            {
		            if (player_audio_event == true)
		            {
			            VehicleInterface::GetInstance()->ReactToEvent(VehicleInterface::KILLAWARDED);
		            }
                }
            }
        }
    } // jcem
*/

// MSL 5.05
//    if (!app->serverFlag)
//		return;

// MSL 5.02 headshot
	Mission::ScoringReactToMechDeath (inflicting_id2,victim_id,damageMode);

	if ((inflicting_id2 != Adept::ReplicatorID::Null) &&
		(victim_id != Adept::ReplicatorID::Null))
	{
		std::string s;

		ScoreObject* victim_score_object = m_scoringChain.Find(victim_id);
		ScoreObject* inflicting_score_object = m_scoringChain.Find(inflicting_id2);

		if ((victim_score_object != 0) &&
			(inflicting_score_object != 0))
		{
// MSL 5.02 Merc Bug Fix
			MWApplication* app = Cast_Object(MWApplication*,MWApplication::GetInstance());

			if (inflicting_id2 != victim_id)
			{
				s += inflicting_score_object->GetName();
				s += " ";
// MSL 5.02 headshot
//				s += app->GetLocString(IDS_MISSION_CPP_DESTROYED);
				if (damageMode == InternalDamageObject::HeadShotDamageMode)
					s += app->GetLocString(IDS_MISSION_CPP_HEAD_SHOT_ON); //head shot damage mode addition
				else
					s += app->GetLocString(IDS_MISSION_CPP_DESTROYED);
				s += " ";
				s += victim_score_object->GetName();
// MSL 5.03 Teammate Chat message added
				if (app->TeamsAreFriendly(inflicting_id2,victim_id) == true)
				{
					s += " (TeamMate)";
				}


				app->SendChat(Connection::Local->GetID(),
						  MWApplication::SystemBroadcast,
						  0,
						  (char*)s.c_str());
			}
			else
			{
				if (victim_id == inflicting_id2)
				{
					s += victim_score_object->GetName();
					s += " ";
					s += app->GetLocString(IDS_MISSION_CPP_SUICIDE);

					app->SendChat(Connection::Local->GetID(),
						  MWApplication::SystemBroadcast,
						  0,
						  (char*)s.c_str());
				}
			}

			
		}
        if (pMechVictim && pMech2)
        {
			if (inflicting_id2 != victim_id)
			{
                Mech_CheckDamage(pMechVictim, pMech2, true);
			}
			else if (inflicting_id == inflicting_id2)
            {
                Mech_CheckDamage(pMechVictim, pMech2, true);
            }
        }
	}


	NetMissionParameters::MWNetMissionParameters *params = app->GetLocalNetParams();
	if (params->m_killLimit)
	{
		ScoreObject *inflicting = m_scoringChain.Find(inflicting_id2);
		if ((inflicting != 0) &&
			(inflicting->GetKills() >= params->m_killLimitNumber))
		{
			Check_Object (Application::GetInstance());
			app->m_iaDisplayGameType = 1;
			app->QueStopGame ();
		}
	}
			
	// Notify the player's group that a 'Mech was destroyed
	GroupContainer& group_container = GetGroupContainer();
	if (group_container.GroupExists(1) == true)
	{
		Group& group = group_container.GetGroup(1);
		group.NotifyMechDestroyed(inflicting_id2,victim_id);
	}

	Verify (m_BucketManager);

	//SPEWALWAYS(("jerryeds", "RULE CHECK-----------"));


	if ((app->networkingFlag == true) || app->IsCampCOOP()) // jcem IsCampCOOP()
	{
		if (params->m_respawnLimit && !app->IsCampCOOP()) // jcem IsCampCOOP()
		{
			if (TestForGameShutDown(victim_id))
			{
				app->QueStopGame ();
			}
		}

		if ((inflicting_id2 != victim_id) && (inflicting_id2 != ReplicatorID::Null))
		{
			m_BucketManager->NotifyAction (CBucketManager::KILL_LINK,inflicting_id2,1);
			m_BucketManager->NotifyAction (CBucketManager::KILLS,inflicting_id2,1);
            g_RSF.RecScore(CBucketManager::KILLS, inflicting_id2, victim_id); // jcem
		}
		else
		{
// MSL 5.04 Bug Fix?
//			if (inflicting_id == inflicting_id2)
			if (victim_id == inflicting_id2)
			{
				m_BucketManager->NotifyAction(CBucketManager::SUICIDES,victim_id,1,victim_id);
                g_RSF.RecScore(CBucketManager::SUICIDES, victim_id); // jcem
			}
		}

		m_BucketManager->NotifyAction (CBucketManager::DEATH_LINK,victim_id,1);
		m_BucketManager->NotifyAction (CBucketManager::DEATHS,victim_id,1);

		int inflicting_tonnage = (int)GetTonnage(inflicting_id2);
		int victim_tonnage = (int)GetTonnage(victim_id);

		int tonnage_delta = 0;

		if ((inflicting_tonnage != -1) &&
			(victim_tonnage != -1) &&
			(victim_tonnage > inflicting_tonnage))
		{
			tonnage_delta = (victim_tonnage - inflicting_tonnage);
		}

//		PAUSE(("Tonnage delta: %d - inf:%d, vit:%d\n", tonnage_delta, inflicting_tonnage, victim_tonnage));

		if (tonnage_delta != 0)
		{
			m_BucketManager->NotifyAction(CBucketManager::KILLS_BY_TONNAGE,inflicting_id2,tonnage_delta);
		}

        /*
		bool player_audio_event = false;

        if (!CTCL_IsConsoleX()) { // jcem
		if ((VehicleInterface::GetInstance() != 0) &&
			(VehicleInterface::GetInstance()->vehicle != 0) &&
			(VehicleInterface::GetInstance()->vehicle->GetReplicatorID() == inflicting_id2))
		{
			player_audio_event = true;
		}
        } // jcem
        */

// MSL 5.03 If I destroy myself thru splash, I should get a Suicide, not teammatedestroyed
		if (app->TeamsAreFriendly(inflicting_id2,victim_id) == true)
		{
// MSL 5.04 Bug Fix?
//			if (victim_id != inflicting_id2)
			if (victim_id != inflicting_id2)
			{
				if (g_nMR != 2)
				app->SendSound(inflicting_id2, VehicleInterface::TEAMMATEDESTROYED);

				m_BucketManager->NotifyAction(CBucketManager::FRIENDLY_KILLS,inflicting_id2,1,victim_id);

				if (tonnage_delta != 0)
				{
					m_BucketManager->NotifyAction(CBucketManager::FRIENDLY_KILLS_BY_TONNAGE,inflicting_id2,tonnage_delta,victim_id);
				}
			}
		}
		else
		{
            if (g_nMR != 2)
            app->SendSound(inflicting_id2, VehicleInterface::KILLAWARDED);

			m_BucketManager->NotifyAction(CBucketManager::ENEMY_KILLS,inflicting_id2,1,victim_id);

			if (tonnage_delta != 0)
			{
				m_BucketManager->NotifyAction(CBucketManager::ENEMY_KILLS_BY_TONNAGE,inflicting_id2,tonnage_delta,victim_id);
			}
		}
	}
}

void MWMission::ScoringReactToTurretDeath (const Adept::ReplicatorID& inflicting_id, const Adept::ReplicatorID& victim_id)
{
	if (!Application::GetInstance()->serverFlag)
		return;

	ReplicatorID inflicting_id2 = inflicting_id;
	Connection *connection = Network::GetInstance()->GetConnection(inflicting_id2.connectionID);

	if (connection != 0)
	{
		Check_Object(connection);
		Replicator* rep = connection->FindReplicator(inflicting_id2);

		if (rep != 0)
		{
			Entity* ent = Cast_Object(Entity*,rep);

			if (ent->IsDerivedFrom(Mech::DefaultData) == true)
			{
				Mech* inflicting_mech = Cast_Object(Mech*,ent);

				if ((inflicting_mech->IsDestroyed() == true) &&
					(inflicting_mech->m_whoShotMeLast != ReplicatorID::Null))
				{
					inflicting_id2 = inflicting_mech->m_whoShotMeLast;
				}
			}
		}
	}

	if ((inflicting_id2 != Adept::ReplicatorID::Null) &&
		(victim_id != Adept::ReplicatorID::Null))
	{
		std::string s;
		
		ScoreObject* inflicting_score_object = m_scoringChain.Find(inflicting_id2);
		if (inflicting_score_object != NULL)
		{
			MWApplication* app = Cast_Object(MWApplication*,MWApplication::GetInstance());
			if (inflicting_id2 != victim_id)
			{
				s += inflicting_score_object->GetName();
				s += " ";
				s += app -> GetLocString (IDS_MISSION_CPP_DESTROYED_TURRET);
				app->SendChat(Connection::Local->GetID(),
						  MWApplication::SystemBroadcast,
						  0,
						  (char*)s.c_str());
			}
		}
	}

	MWApplication* mwapplication = Cast_Object(MWApplication*,MWApplication::GetInstance());

	if (mwapplication->TeamsAreFriendly(inflicting_id2,victim_id) == true)
	{
		if ((victim_id != inflicting_id2) ||
			(inflicting_id != inflicting_id2))
		{
			m_BucketManager->NotifyAction(CBucketManager::FRIENDLY_TURRET_KILLS,inflicting_id2,1);
		}
	}
	else
	{
		m_BucketManager->NotifyAction(CBucketManager::ENEMY_TURRET_KILLS,inflicting_id2,1);
	}



}

void MWMission::ScoringReactToBuildingDeath (const Adept::ReplicatorID& inflicting_id, const Adept::ReplicatorID& victim_id)
{
	if (!Application::GetInstance()->serverFlag)
		return;

	ReplicatorID inflicting_id2 = inflicting_id;
	Connection *connection = Network::GetInstance()->GetConnection(inflicting_id2.connectionID);

	if (connection != 0)
	{
		Check_Object(connection);
		Replicator* rep = connection->FindReplicator(inflicting_id2);

		if (rep != 0)
		{
			Entity* ent = Cast_Object(Entity*,rep);

			if (ent->IsDerivedFrom(Mech::DefaultData) == true)
			{
				Mech* inflicting_mech = Cast_Object(Mech*,ent);

				if ((inflicting_mech->IsDestroyed() == true) &&
					(inflicting_mech->m_whoShotMeLast != ReplicatorID::Null))
				{
					inflicting_id2 = inflicting_mech->m_whoShotMeLast;
				}
			}
		}
	}

	MWApplication* mwapplication = Cast_Object(MWApplication*,MWApplication::GetInstance());
	int chatMessageID = IDS_MISSION_CPP_DESTROYED_ENEMY_BUILDING;

	if (mwapplication->TeamsAreFriendly(inflicting_id2,victim_id) == true)
	{
		if ((victim_id != inflicting_id2) ||
			(inflicting_id != inflicting_id2))
		{
			m_BucketManager->NotifyAction(CBucketManager::FRIENDLY_BUILDING_KILLS,inflicting_id2,1);
            g_RSF.RecScore(CBucketManager::FRIENDLY_BUILDING_KILLS, inflicting_id2, victim_id); // jcem
			chatMessageID = IDS_MISSION_CPP_DESTROYED_FRIENDLY_BUILDING;
		}
	}
	else
	{
		m_BucketManager->NotifyAction(CBucketManager::ENEMY_BUILDING_KILLS,inflicting_id2,1);
		g_RSF.RecScore(CBucketManager::ENEMY_BUILDING_KILLS, inflicting_id2, victim_id); // jcem
		chatMessageID = IDS_MISSION_CPP_DESTROYED_ENEMY_BUILDING;
	}

	if ((inflicting_id2 != Adept::ReplicatorID::Null) &&
		(victim_id != Adept::ReplicatorID::Null))
	{
		std::string s;
		
		ScoreObject* inflicting_score_object = m_scoringChain.Find(inflicting_id2);
		if (inflicting_score_object != NULL)
		{
			MWApplication* app = Cast_Object(MWApplication*,MWApplication::GetInstance());
			if (inflicting_id2 != victim_id)
			{
				s += inflicting_score_object->GetName();
				s += " ";
				s += app -> GetLocString (chatMessageID);
				app->SendChat(Connection::Local->GetID(),
						  MWApplication::SystemBroadcast,
						  0,
						  (char*)s.c_str());
			}
		}
	}
}




//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
bool MWMission::TestForGameShutDown (ReplicatorID victim_id)
{
    if (0.0f < m_fShouldBeStopped) {
        if (CheckEndMissionTime() <= 0.0f)
            return true;
    } else {
        if (real_TestForGameShutDown(victim_id)) {
            m_fShouldBeStopped = GetMissionTime() + 10.0f;
        }
    }

    return false;
}

bool MWMission::real_TestForGameShutDown(ReplicatorID victim_id)
{
	if (GetMissionTime() < 30.0f)
		return false;

	MWApplication* app = Cast_Object(MWApplication*,MWApplication::GetInstance());

	NetMissionParameters::MWNetMissionParameters *params = MWApplication::GetInstance()->GetLocalNetParams();

	if (!params->m_teamAllowed)
	{
		int players_left = 0;
		for (int i = 0; i < Maximum_Players; ++i)
		{
			if (app->servedConnectionData[i].clientConnected)
			{
				if (app->servedConnectionData[i].clientPlayer && app->servedConnectionData[i].clientPlayer->vehicle)
				{
					Vehicle *veh = Cast_Object(Vehicle*, app->servedConnectionData[i].clientPlayer->vehicle);
					if (veh->GetClassID() == MechClassID)
					{

						if (victim_id != veh->GetReplicatorID())
						{
							if (veh->IsDestroyed() && veh->respawnCount < params->m_respawnLimitNumber)
							{
								players_left++;
							}
							else if (!veh->IsDestroyed())
							{
								players_left++;
							}
						}
						else
						{
							if (veh->respawnCount < params->m_respawnLimitNumber)
							{
								players_left++;
							}
						}
					}
				}
			}
		}

		for (i = 0; i < Maximum_Lancemates; ++i)
		{
			if (app->lancemateConnectionData[i].lancemateMech)
			{
			
				if (victim_id != app->lancemateConnectionData[i].lancemateMech->GetReplicatorID())
				{

					if (app->lancemateConnectionData[i].lancemateMech->IsDestroyed() && app->lancemateConnectionData[i].lancemateMech->respawnCount < params->m_respawnLimitNumber)
					{
						players_left++;
					}
					else if (!app->lancemateConnectionData[i].lancemateMech->IsDestroyed())
					{
						players_left++;
					}
				}
				else
				{
					if (app->lancemateConnectionData[i].lancemateMech->respawnCount < params->m_respawnLimitNumber)
					{
						players_left++;
					}
				}
			}
		}

		if (players_left <= 1)
		{
			return true;
		}
	}
	else
	{
		
		//SPEWALWAYS(("jerryeds", "TEAMS"));

		int team_alive[8];
		for (int i = 0; i < 8; ++i)
		{
			team_alive[i] = 0;
		}

		for (i = 0; i < Maximum_Players; ++i)
		{
			if (app->servedConnectionData[i].clientConnected)
			{
				if (app->servedConnectionData[i].clientPlayer && app->servedConnectionData[i].clientPlayer->vehicle)
				{
					Vehicle *veh = Cast_Object(Vehicle*, app->servedConnectionData[i].clientPlayer->vehicle);
					if (veh->GetClassID() == MechClassID)
					{

						//SPEWALWAYS(("jerryeds", "CHECK PLAYER %d:%d", veh->GetTeam(), i));

						if (victim_id != veh->GetReplicatorID())
						{
							if (veh->IsDestroyed() && veh->respawnCount < params->m_respawnLimitNumber)
							{
								team_alive[veh->GetTeam()]++;
								//SPEWALWAYS(("jerryeds", "ALIVE 1"));
							}
							else if (!veh->IsDestroyed())
							{
								team_alive[veh->GetTeam()]++;
								//SPEWALWAYS(("jerryeds", "ALIVE 2"));
							}
						}
						else
						{
							if (veh->respawnCount < params->m_respawnLimitNumber)
							{
								team_alive[veh->GetTeam()]++;
								//SPEWALWAYS(("jerryeds", "ALIVE 3"));
							}
						}
					}
				}
			}
		}

		for (i = 0; i < Maximum_Lancemates; ++i)
		{
			if (app->lancemateConnectionData[i].lancemateMech)
			{
			
				//SPEWALWAYS(("jerryeds", "CHECK LANCE %d:%d", app->lancemateConnectionData[i].lancemateMech->GetTeam(), i));

				if (victim_id != app->lancemateConnectionData[i].lancemateMech->GetReplicatorID())
				{

					if (app->lancemateConnectionData[i].lancemateMech->IsDestroyed() && app->lancemateConnectionData[i].lancemateMech->respawnCount < params->m_respawnLimitNumber)
					{
						//SPEWALWAYS(("jerryeds", "ALIVE 1"));
						team_alive[app->lancemateConnectionData[i].lancemateMech->GetTeam()]++;
					}
					else if (!app->lancemateConnectionData[i].lancemateMech->IsDestroyed())
					{
						//SPEWALWAYS(("jerryeds", "ALIVE 2"));
						team_alive[app->lancemateConnectionData[i].lancemateMech->GetTeam()]++;
					}
				}
				else
				{
					if (app->lancemateConnectionData[i].lancemateMech->respawnCount < params->m_respawnLimitNumber)
					{
						//SPEWALWAYS(("jerryeds", "ALIVE 3"));
						team_alive[app->lancemateConnectionData[i].lancemateMech->GetTeam()]++;
					}
				}
			}
		}



		//If we are playing an escort game teams 3 and 4  (-1) are the VIP's. 
		//if they are dead and out of lives than the game is over in limited respawn

		if (params->m_ruleType == StockEscort)
		{
			if (!team_alive[2])
			{
				return true;
			}

			if (!team_alive[3])
			{
				return true;
			}
		}


		
		MWApplication *app = MWApplication::GetInstance();

		bool teams_have_same_alignemnt = true;
		
		//for (i = 0; i < 8; ++i)
		//{
		//	SPEWALWAYS(("jerryeds", "TEAM : %d:%d", i, team_alive[i]));
		//}

		for (i = 0; i < 8; ++i)
		{
			if (team_alive[i])
			{
				
				//SPEWALWAYS(("jerryeds", "START TEAM ALIVE: %d", i));

				int alignment_check = app->teams[i]->GetCurrent()->GetTeamAlignment();

				for (int j = 0; j < 8; ++j)
				{
					if (team_alive[j])
					{
						//SPEWALWAYS(("jerryeds", "CHECK TEAM ALIVE: %d", j));

						int alignment = app->teams[j]->GetCurrent()->GetTeamAlignment();

						//SPEWALWAYS(("jerryeds", "ALIGN CHECK: %d - %d", alignment_check, alignment));

						if (alignment_check != alignment)
						{
							teams_have_same_alignemnt = false;
							break;
						}
					}
				}
				break;
			}
		}


		if (teams_have_same_alignemnt)
		{
			//SPEWALWAYS(("jerryeds", "STOP GAME"));
			return true;
		}
	}

	return false;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
bool MWMission::TestForGameShutDownUnlimitedRespawn ()
{

	if (GetMissionTime() < 30.0f)
		return false;

	MWApplication* app = Cast_Object(MWApplication*,MWApplication::GetInstance());

	NetMissionParameters::MWNetMissionParameters *params = MWApplication::GetInstance()->GetLocalNetParams();

	if (!params->m_teamAllowed)
	{
		int players_left = 0;
		for (int i = 0; i < Maximum_Players; ++i)
		{
			if (app->servedConnectionData[i].clientConnected)
			{
				if (app->servedConnectionData[i].clientPlayer && app->servedConnectionData[i].clientPlayer->vehicle)
				{
					Vehicle *veh = Cast_Object(Vehicle*, app->servedConnectionData[i].clientPlayer->vehicle);
					if (veh->GetClassID() == MechClassID)
					{
						players_left++;
					}
				}
			}
		}

		for (i = 0; i < Maximum_Lancemates; ++i)
		{
			if (app->lancemateConnectionData[i].lancemateMech)
			{
				players_left++;
			}
		}

		if (players_left <= 1)
		{
			return true;
		}
	}
	else
	{
		
		//SPEWALWAYS(("jerryeds", "TEAMS"));

		int team_alive[8];
		for (int i = 0; i < 8; ++i)
		{
			team_alive[i] = 0;
		}

		for (i = 0; i < Maximum_Players; ++i)
		{
			if (app->servedConnectionData[i].clientConnected)
			{
				if (app->servedConnectionData[i].clientPlayer && app->servedConnectionData[i].clientPlayer->vehicle)
				{
					Vehicle *veh = Cast_Object(Vehicle*, app->servedConnectionData[i].clientPlayer->vehicle);
					if (veh->GetClassID() == MechClassID)
					{

						//SPEWALWAYS(("jerryeds", "CHECK PLAYER %d:%d", veh->GetTeam(), i));
						team_alive[veh->GetTeam()]++;
							
					}
				}
			}
		}

		for (i = 0; i < Maximum_Lancemates; ++i)
		{
			if (app->lancemateConnectionData[i].lancemateMech)
			{
				//SPEWALWAYS(("jerryeds", "CHECK LANCE %d:%d", app->lancemateConnectionData[i].lancemateMech->GetTeam(), i));
				team_alive[app->lancemateConnectionData[i].lancemateMech->GetTeam()]++;
			}
		}



		//If we are playing an escort game teams 3 and 4  (-1) are the VIP's. 
		//if they are dead and out of lives than the game is over in limited respawn

		if (params->m_ruleType == StockEscort)
		{
			if (!team_alive[2])
			{
				return true;
			}

			if (!team_alive[3])
			{
				return true;
			}
		}


		
		MWApplication *app = MWApplication::GetInstance();

		bool teams_have_same_alignemnt = true;
		
		//for (i = 0; i < 8; ++i)
		//{
		//	SPEWALWAYS(("jerryeds", "TEAM : %d:%d", i, team_alive[i]));
		//}

		for (i = 0; i < 8; ++i)
		{
			if (team_alive[i])
			{
				
				//SPEWALWAYS(("jerryeds", "START TEAM ALIVE: %d", i));

				int alignment_check = app->teams[i]->GetCurrent()->GetTeamAlignment();

				for (int j = 0; j < 8; ++j)
				{
					if (team_alive[j])
					{
						//SPEWALWAYS(("jerryeds", "CHECK TEAM ALIVE: %d", j));

						int alignment = app->teams[j]->GetCurrent()->GetTeamAlignment();

						//SPEWALWAYS(("jerryeds", "ALIGN CHECK: %d - %d", alignment_check, alignment));

						if (alignment_check != alignment)
						{
							teams_have_same_alignemnt = false;
							break;
						}
					}
				}
				break;
			}
		}


		if (teams_have_same_alignemnt)
		{
			//SPEWALWAYS(("jerryeds", "STOP GAME"));
			return true;
		}
	}

	return false;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void MWMission::AddScoreMember (const Adept::ReplicatorID& member, const Stuff::MString& name)
{
	Verify (m_BucketManager);
	Mission::AddScoreMember (member,name);
	//int id;
	//id = m_BucketManager->AddBucket (CBucketManager::KILL_LINK,member);
	//m_BucketManager->ShowBucket (id);
	//id = m_BucketManager->AddBucket (CBucketManager::DEATH_LINK,member);
	//m_BucketManager->ShowBucket (id);
	m_BucketManager->NotifyAddPlayer (member);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void MWMission::RemoveScoreMember (const Adept::ReplicatorID& member)
{
	Verify (m_BucketManager);
	Mission::RemoveScoreMember (member);
	//m_BucketManager->KillBucket (CBucketManager::KILL_LINK,member);
	//m_BucketManager->KillBucket (CBucketManager::DEATH_LINK,member);
	m_BucketManager->NotifyRemovePlayer (member);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	MWMission::EndMissionState(bool succeeded)
{
	Check_Object(this);
	m_MissionSuccess = succeeded;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
MW4AI::DiagnosticsInterface&
	MWMission::GetDiagnosticsInterface()
{
	return (m_DiagnosticsInterface);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
const MW4AI::DiagnosticsInterface&
	MWMission::GetDiagnosticsInterface() const
{
	return (m_DiagnosticsInterface);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	MWMission::ProcessReactionSpheres(Mech *target_mech)
{
	Check_Object(this);
	Check_Object(target_mech);

	ChainIteratorOf<ReactionSphere *> iterator(&m_reactionSpheres);
	ReactionSphere *reaction_sphere;

	Point3D target_point;
	target_point = target_mech->GetLocalToWorld();

	while((reaction_sphere = iterator.ReadAndNext()) != NULL)
	{
		Check_Object(reaction_sphere);
		if(reaction_sphere->DoesEffect(target_point))
			reaction_sphere->ApplyEffect(target_mech);
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	MWMission::ProcessPlayerReactionSpheres(Mech *target_mech)
{
	Check_Object(this);
	Check_Object(target_mech);

	ChainIteratorOf<ReactionSphere *> iterator(&m_playerOnlyReactionSpheres);
	ReactionSphere *reaction_sphere;

	Point3D target_point;
	target_point = target_mech->GetLocalToWorld();

	while((reaction_sphere = iterator.ReadAndNext()) != NULL)
	{
		Check_Object(reaction_sphere);
		if(reaction_sphere->DoesEffect(target_point))
			reaction_sphere->ApplyEffect(target_mech);
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	MWMission::ResolveReactionSpheres()
{
	Check_Object(this);
	
	ChainIteratorOf<ReactionSphere *> iterator(&m_reactionSpheres);
	ReactionSphere *reaction_sphere;

	while((reaction_sphere = iterator.ReadAndNext()) != NULL)
	{
		Check_Object(reaction_sphere);
		if(reaction_sphere->IsOver())
			delete reaction_sphere;
	}
	
	ChainIteratorOf<ReactionSphere *> player_iterator(&m_playerOnlyReactionSpheres);

	while((reaction_sphere = player_iterator.ReadAndNext()) != NULL)
	{
		Check_Object(reaction_sphere);
		if(reaction_sphere->IsOver())
			delete reaction_sphere;
	}
	
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	MWMission::CreateNewHeatSphere(const Stuff::Point3D& center, Scalar radius, Scalar secs, Scalar heat_e)
{
	Check_Object(this);

	HeatReactionSphere *heat_sphere;
	heat_sphere = new HeatReactionSphere(center, radius, secs, heat_e);
	Check_Object(heat_sphere);

	m_reactionSpheres.Add(heat_sphere);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	MWMission::CreateNewRadarSphere(const Stuff::Point3D& center, Scalar radius, Scalar secs, Scalar radar_e)
{
	Check_Object(this);

	RadarReactionSphere *radar_sphere;
	radar_sphere = new RadarReactionSphere(center, radius, secs, radar_e);
	Check_Object(radar_sphere);

	m_reactionSpheres.Add(radar_sphere);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	MWMission::CreateNewInstantHeatSphere(const Stuff::Point3D& center, Scalar radius, Scalar heat_amount)
{
	Check_Object(this);

	InstantHeatReactionSphere *heat_sphere;
	heat_sphere = new InstantHeatReactionSphere(center, radius, 0.0f, heat_amount);
	Check_Object(heat_sphere);

	m_reactionSpheres.Add(heat_sphere);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	MWMission::CreateNewFogSphere(const Stuff::Point3D& center, Scalar radius, Scalar secs)
{
	Check_Object(this);

	FogReactionSphere *fog_sphere;
	fog_sphere = new FogReactionSphere(center, radius, secs);
	Check_Object(fog_sphere);

	m_playerOnlyReactionSpheres.Add(fog_sphere);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	MWMission::RespawnMission()
{
	Check_Object(this);

	if (propListResourceID != ResourceID::Null)
	{
		Resource prop_stream(propListResourceID);
		prop_stream.AdvancePointer(sizeof(WORD));
		Verify(prop_stream.DoesResourceExist());
		ChainIteratorOf<Entity *> iterator(&m_respawnProps);
		Entity *entity;
		while ((prop_stream.GetBytesRemaining() > 0) && ((entity = iterator.ReadAndNext()) != NULL))
		{
			Entity::CreateMessage *message =
				Cast_Pointer(Entity::CreateMessage*, prop_stream.GetPointer());
			Check_Object(message);
			entity->Respawn(message);
			if(entity->m_deathEntity.GetCurrent())
			{
				entity->m_deathEntity.GetCurrent()->SentenceToDeathRow();
			}
		
			entity->SyncMatrices(true);
	
			prop_stream.AdvancePointer(message->messageLength);
		}
		prop_stream.UnloadData();
	}

	Mission::RespawnMission();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	MWMission::SetEndMissionTime (Stuff::Scalar seconds)
{
	if (!m_EndMissionTimer.Running ())
	{
		m_EndMissionTimer.Start();
	}
	
	MWApplication* app = Cast_Object(MWApplication*,MWApplication::GetInstance());
	if (app->networkingFlag)
	{
		m_EndTime = (Stuff::Scalar)m_EndMissionTimer.CurrTime()+seconds;
        if (seconds > 60.0f) {// jcem
            m_b30notified = m_b60notified = false;
        } else {
            m_b60notified = true;
            if (seconds > 30.0f) // jcem
                m_b30notified = false;
            else
                m_b30notified = true;
        }
	}
	else
	{
        m_b30notified = m_b60notified = true; // jcem
		m_EndTime = seconds;
	}
}

Stuff::Scalar MWMission::CheckEndMissionTime ()
{
    Stuff::Scalar fTime = GetEndMissionTime ();
    if (0.0f < m_fShouldBeStopped) {
        Stuff::Scalar fTime2 = GetMissionTime();
        if (m_fShouldBeStopped <= fTime2)
            return 0.0f;
        fTime2 = m_fShouldBeStopped - fTime2;
        if (fTime2 < fTime)
            fTime = fTime2;
    }

    return fTime;
}

void MWMission::StoreOriginalBounds()
{
	if (!m_bOrgBoundsStored) {
		m_bBoundsWarningsEnabled = VehicleInterface::GetInstance()->GetBoundsWarningsEnabled();

		GetWarningPolygon(m_warningPolygonOld);
		GetMissionPolygon(m_missionPolygonOld);
		m_ConstraintTopLeftOld = m_ConstraintTopLeft;
		m_ConstraintBottomRightOld = m_ConstraintBottomRight;

		m_bOrgBoundsStored = true;
	}
}

void MWMission::RestoreOriginalBounds()
{
	gosASSERT(m_bOrgBoundsStored);

	VehicleInterface::GetInstance()->SetBoundsWarningsEnabled(m_bBoundsWarningsEnabled);

	SetWarningPolygon(m_warningPolygonOld);
	SetMissionPolygon(m_missionPolygonOld);
	m_ConstraintTopLeft = m_ConstraintTopLeftOld;
	m_ConstraintBottomRight = m_ConstraintBottomRightOld;
}

void MWMission::SetBoundsFromPaths(const MechWarrior4::Path& PathMission, const MechWarrior4::Path& PathWarning)
{
	gosASSERT(&PathMission);

	StoreOriginalBounds();

	PathMission.SaveAs2DPoly(missionPolygon);
	if (&PathWarning) {
		PathWarning.SaveAs2DPoly(warningPolygon);
		VehicleInterface::GetInstance()->SetBoundsWarningsEnabled(true);
	} else {
		DynamicArrayOf<Vector2DOf<Scalar> >& bounds_list = warningPolygon;
		bounds_list.SetLength(4);
		bounds_list[0].x = 0;
		bounds_list[0].y = 0;
		bounds_list[1].x = 0;
		bounds_list[1].y = 0.0001f;
		bounds_list[2].x = 0.0001f;
		bounds_list[2].y = 0.0001f;
		bounds_list[3].x = 0.0001f;
		bounds_list[3].y = 0;
		VehicleInterface::GetInstance()->SetBoundsWarningsEnabled(false);
	}

	m_ConstraintTopLeft = Point3D::Identity;
	m_ConstraintBottomRight = Point3D::Identity;
}

void MWMission::InitCOOP()
{
    memset(&m_naCOOPs[0], 0, sizeof(m_naCOOPs));
    m_naCOOPs[COOP_ENEMY_GROUP] = -1;
    m_naCOOPs[COOP_ENEMY_GROUP_OLD] = -1;
}

void MWMission::ExitCOOP()
{
}

int MWMission::GetCOOP(int nIdx, void* pDest/* = (void*)NULL*/)
{
    int nParam;

    switch(nIdx)
    {
    case -1:
    case -2:
      {
        extern int MAX_CAMP_STAGES;
	    MWApplication *app = MWApplication::GetInstance ();
        int nStage = app->m_nCamp_Stage;
        nStage--;
        if (nIdx == -1) {
            nStage %= MAX_CAMP_STAGES;
            nStage++;
        } else {
            nStage /= MAX_CAMP_STAGES;
        }
        return nStage;
      }
    case COOP_SUBSTAGE_SCORE_OLD:
    case COOP_MODAL_IS_ENDED:
        return m_naCOOPs[nIdx];
    case COOP_ENEMY_GROUP:
    case COOP_ENEMY_GROUP_OLD:
        nParam = m_naCOOPs[nIdx];
        if ((nParam != -1) && (ABL::ObjectIDisGroup(nParam) == true)) {
            Group::identifier group_id = ABL::ObjectIDtoGroupID(nParam);
            ((POINT*)pDest)->x = MW4AI::Groups::NumDead(group_id);
            ((POINT*)pDest)->y = MW4AI::Groups::Size(group_id);

            return +1;
        }
        break;
    default:
        //gosASSERT(FALSE);
        gosASSERT((0 <= nIdx) && (nIdx < MAX_COOP_VARS));
        return m_naCOOPs[nIdx];
    }

    return 0;
}

int MWMission::SetCOOP(int nIdx, int nParam)
{
    switch(nIdx)
    {
    case COOP_SUBSTAGE_SCORE_OLD:
        gosASSERT(FALSE);
        break;
    case COOP_ENEMY_GROUP:
        if (nParam != -1) {
            gosASSERT(ABL::ObjectIDisGroup(nParam) == true);

		    int nScore, nKills, nDeaths;

		    nScore = GetScore4Player(nKills, nDeaths);

	        MWApplication *app = MWApplication::GetInstance ();

		    nScore += app->m_nCamp_Score;
		    nKills += app->m_nCamp_Kills;
		    nDeaths += app->m_nCamp_Deaths;

            m_naCOOPs[COOP_SUBSTAGE_SCORE_OLD] = nScore;
        }
        m_naCOOPs[COOP_ENEMY_GROUP] = nParam;
        break;
    case COOP_ENEMY_GROUP_OLD:
        if (nParam != -1) {
            gosASSERT(ABL::ObjectIDisGroup(nParam) == true);
        }
        m_naCOOPs[COOP_ENEMY_GROUP_OLD] = nParam;
        break;
    case COOP_MODAL_START:
        //gosASSERT(!m_naCOOPs[COOP_MODAL_START]);
        m_naCOOPs[COOP_MODAL_START] = nParam;
        m_naCOOPs[COOP_MODAL_IS_ENDED] = 0;
        break;
    default:
        //gosASSERT(FALSE);
        gosASSERT((0 <= nIdx) && (nIdx < MAX_COOP_VARS));
        m_naCOOPs[nIdx] = nParam;
        break;
    }

    return 0;
}
