/*
**	Command & Conquer Generals Zero Hour(tm)
**	Copyright 2025 Electronic Arts Inc.
**
**	This program is free software: you can redistribute it and/or modify
**	it under the terms of the GNU General Public License as published by
**	the Free Software Foundation, either version 3 of the License, or
**	(at your option) any later version.
**
**	This program is distributed in the hope that it will be useful,
**	but WITHOUT ANY WARRANTY; without even the implied warranty of
**	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
**	GNU General Public License for more details.
**
**	You should have received a copy of the GNU General Public License
**	along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

////////////////////////////////////////////////////////////////////////////////
//																																						//
//  (c) 2001-2003 Electronic Arts Inc.																				//
//																																						//
////////////////////////////////////////////////////////////////////////////////

// FILE: GameLogic.cpp ////////////////////////////////////////////////////////////////////////////
// GameLogic class implementation
// Author: Michael S. Booth, October 2000
///////////////////////////////////////////////////////////////////////////////////////////////////

#include "PreRTS.h"	// This must go first in EVERY cpp file int the GameEngine

#include "Common/AudioAffect.h"
#include "Common/AudioHandleSpecialValues.h"
#include "Common/BuildAssistant.h"
#include "Common/CopyProtection.h"
#include "Common/CRCDebug.h"
#include "Common/GameAudio.h"
#include "Common/GameEngine.h"
#include "Common/GameLOD.h"
#include "Common/GameState.h"
#include "Common/INI.h"
#include "Common/LatchRestore.h"
#include "Common/MapObject.h"
#include "Common/MultiplayerSettings.h"
#include "Common/PerfTimer.h"
#include "Common/Player.h"
#include "Common/PlayerList.h"
#include "Common/PlayerTemplate.h"
#include "Common/Radar.h"
#include "Common/RandomValue.h"
#include "Common/Recorder.h"
#include "Common/StatsCollector.h"
#include "Common/ThingFactory.h"
#include "Common/Team.h"
#include "Common/ThingTemplate.h"
#include "GameClient/Water.h"
#include "GameClient/Snow.h"
#include "Common/WellKnownKeys.h"
#include "Common/Xfer.h"
#include "Common/XferCRC.h"
#include "Common/XferDeepCRC.h"
#include "Common/GameSpyMiscPreferences.h"

#include "GameClient/ControlBar.h"
#include "GameClient/Drawable.h"
#include "GameClient/GameClient.h"
#include "GameClient/GameText.h"
#include "GameClient/GUICallbacks.h"
#include "GameClient/InGameUI.h"
#include "GameClient/LoadScreen.h"
#include "GameClient/MapUtil.h"
#include "GameClient/Mouse.h"
#include "GameClient/ParticleSys.h"
#include "GameClient/TerrainVisual.h"
#include "GameClient/View.h"
#include "GameClient/ControlBar.h"
#include "GameClient/CampaignManager.h"
#include "GameClient/GameWindowTransitions.h"

#include "GameLogic/AI.h"
#include "GameLogic/AIPathfind.h"
#include "GameLogic/CaveSystem.h"
#include "GameLogic/CrateSystem.h"
#include "GameLogic/FPUControl.h"
#include "GameLogic/GameLogic.h"
#include "GameLogic/Locomotor.h"
#include "GameLogic/Object.h"
#include "GameLogic/Module/AIUpdate.h"
#include "GameLogic/Module/BodyModule.h"
#include "GameLogic/Module/CreateModule.h"
#include "GameLogic/Module/DestroyModule.h"
#include "GameLogic/Module/OpenContain.h"
#include "GameLogic/PartitionManager.h"
#include "GameLogic/PolygonTrigger.h"
#include "GameLogic/ScriptActions.h"
#include "GameLogic/ScriptConditions.h"
#include "GameLogic/ScriptEngine.h"
#include "GameLogic/SidesList.h"
#include "GameLogic/VictoryConditions.h"
#include "GameLogic/Weapon.h"
#include "GameLogic/GhostObject.h"

#include "Common/DataChunk.h"
#include "GameLogic/Scripts.h"

#include "GameNetwork/GameSpy/BuddyThread.h"
#include "GameNetwork/GameSpy/PeerDefs.h"
#include "GameNetwork/GameSpy/ThreadUtils.h"
#include "GameNetwork/LANAPICallbacks.h"
#include "GameNetwork/NetworkInterface.h"
#include "GameNetwork/GameSpy/PersistentStorageThread.h"

#include <rts/profile.h>

DECLARE_PERF_TIMER(SleepyMaintenance)

#include "Common/UnitTimings.h" //Contains the DO_UNIT_TIMINGS define jba.		 
// If defined, the game times various units.
#ifdef DO_UNIT_TIMINGS
#pragma MESSAGE("*** WARNING *** DOING DO_UNIT_TIMINGS!!!!")
Bool g_UT_gotUnit = false;
const ThingTemplate *g_UT_curThing = NULL;
Bool g_UT_startTiming = false;
FILE *g_UT_timingLog=NULL;
FILE *g_UT_commaLog=NULL;
// Note - this is only for gathering timing data!  DO NOT DO THIS IN REGULAR CODE!!!  JBA
#define BRUTAL_TIMING_HACK
#include "../../gameenginedevice/include/W3DDevice/GameClient/Module/W3DModelDraw.h"
extern void externalAddTree(Coord3D location, Real scale, Real angle, AsciiString name);
#endif


#ifdef _INTERNAL
// for occasional debugging...
//#pragma optimize("", off)
//#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
#endif




// I'm making this larger now that we know how big our maps are going to be. 
enum { OBJ_HASH_SIZE	= 8192 };

/// The GameLogic singleton instance
GameLogic *TheGameLogic = NULL;

static void findAndSelectCommandCenter(Object *obj, void* alreadyFound);


// ------------------------------------------------------------------------------------------------
/** This enum is for loading screen bar progress */
// ------------------------------------------------------------------------------------------------
enum
{
	LOAD_PROGRESS_START =0,
	LOAD_PROGRESS_POST_PARTICLE_INI_LOAD = LOAD_PROGRESS_START + 1,
	LOAD_PROGRESS_POST_LOAD_MAP = LOAD_PROGRESS_POST_PARTICLE_INI_LOAD + 1,
	LOAD_PROGRESS_SIDE_POPULATION = LOAD_PROGRESS_POST_LOAD_MAP + 1,  // Increment the next one by at least MAX_SLOTS
	LOAD_PROGRESS_POST_SIDE_LIST_INIT = LOAD_PROGRESS_SIDE_POPULATION + 1 + MAX_SLOTS,
	LOAD_PROGRESS_POST_PLAYER_LIST_RESET = LOAD_PROGRESS_POST_SIDE_LIST_INIT + 1,
	LOAD_PROGRESS_POST_SCRIPT_ENGINE_NEW_MAP = LOAD_PROGRESS_POST_PLAYER_LIST_RESET + 1,
	LOAD_PROGRESS_POST_VICTORY_CONDITION_SETUP = LOAD_PROGRESS_POST_SCRIPT_ENGINE_NEW_MAP + 2,
	LOAD_PROGRESS_POST_VICTORY_CONDITION_SET_VICTORY_CONDITION = LOAD_PROGRESS_POST_VICTORY_CONDITION_SETUP + 1,
	LOAD_PROGRESS_POST_GHOST_OBJECT_MANAGER_RESET = LOAD_PROGRESS_POST_VICTORY_CONDITION_SET_VICTORY_CONDITION + 1,
	LOAD_PROGRESS_POST_TERRAIN_LOGIC_NEW_MAP = LOAD_PROGRESS_POST_GHOST_OBJECT_MANAGER_RESET + 1,
	LOAD_PROGRESS_POST_BRIDGE_LOAD = LOAD_PROGRESS_POST_TERRAIN_LOGIC_NEW_MAP + 1,
	LOAD_PROGRESS_POST_PATHFINDER_NEW_MAP = LOAD_PROGRESS_POST_BRIDGE_LOAD + 1,

	LOAD_PROGRESS_LOOP_ALL_THE_FREAKN_OBJECTS = LOAD_PROGRESS_POST_BRIDGE_LOAD + 1,
	LOAD_PROGRESS_MAX_ALL_THE_FREAKN_OBJECTS = 80,// THE END OF THE BIG ASS PROGRESS PAUSE

	LOAD_PROGRESS_LOOP_INITIAL_NETWORK_BUILDINGS = LOAD_PROGRESS_MAX_ALL_THE_FREAKN_OBJECTS + 1,  // Increment the next one by at least MAX_SLOTS
	LOAD_PROGRESS_POST_INITIAL_NETWORK_BUILDINGS = LOAD_PROGRESS_LOOP_INITIAL_NETWORK_BUILDINGS + MAX_SLOTS + 1,

	LOAD_PROGRESS_POST_PRELOAD_ASSETS = LOAD_PROGRESS_POST_INITIAL_NETWORK_BUILDINGS + 1,
	LOAD_PROGRESS_POST_STARTING_CAMERA = LOAD_PROGRESS_POST_PRELOAD_ASSETS + 1,
	LOAD_PROGRESS_POST_STARTING_CAMERA_2 = LOAD_PROGRESS_POST_STARTING_CAMERA + 1,
	LOAD_PROGRESS_END = 100,
};

// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
static Waypoint * findNamedWaypoint(AsciiString name)
{
	for (Waypoint *way = TheTerrainLogic->getFirstWaypoint(); way; way = way->getNext())
	{
		if (way->getName() == name)
		{
			return way;
		}
	}
	return NULL;
}

// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
void setFPMode( void )
{
  // Set floating point round mode to CHOP, which only comes
  // into play when precision is exceeded.  This is necessary
  // for the fast float to int routines used elsewhere in the
  // system.
	//
	// Also set floating point precision to low.  It could be
	// anything as long as it is consistent, really, but this
	// is in the (vain?) hope of any slight speed boost.
	//
	_fpreset();

	UnsignedInt curVal = _statusfp();
	UnsignedInt newVal = curVal;
	newVal = (newVal & ~_MCW_RC) | (_RC_NEAR & _MCW_RC);
	//newVal = (newVal & ~_MCW_RC) | (_RC_CHOP & _MCW_RC);
	newVal = (newVal & ~_MCW_PC) | (_PC_24   & _MCW_PC);

	_controlfp(newVal, _MCW_PC | _MCW_RC);
}

// ------------------------------------------------------------------------------------------------
/** GameLogic class constructor */
// ------------------------------------------------------------------------------------------------
GameLogic::GameLogic( void )
{
	//Added By Sadullah Nader
	//Initializations missing and necessary 
	m_background = NULL;
	m_CRC = 0;
	m_isInUpdate = FALSE;

	m_rankPointsToAddAtGameStart = 0;

	for(Int i = 0; i < MAX_SLOTS; i++)
	{
		m_progressComplete[i] = FALSE;
		m_progressCompleteTimeout[i] = 0;
	}

	m_shouldValidateCRCs = FALSE;
	
	m_startNewGame = FALSE;
	//

	m_frame = 0;
	m_frameObjectsChangedTriggerAreas = 0;
	m_width = 0;
	m_height = 0;
	m_objList = NULL;
	m_curUpdateModule = NULL;
	m_nextObjID = INVALID_ID;
	m_startNewGame = FALSE;
	m_gameMode = GAME_NONE;
	m_rankLevelLimit = 1000;
	m_gamePaused = FALSE;
	m_inputEnabledMemory = TRUE;
	m_mouseVisibleMemory = TRUE;
	m_loadScreen = NULL;
	m_forceGameStartByTimeOut = FALSE;
#ifdef DUMP_PERF_STATS
	m_overallFailedPathfinds = 0;
#endif

	m_loadingMap = FALSE;
	m_loadingSave = FALSE;
	m_clearingGameData = FALSE;
}

// ------------------------------------------------------------------------------------------------
/** Utility function to set class variables to default values. */
// ------------------------------------------------------------------------------------------------
void GameLogic::setDefaults( Bool loadingSaveGame )
{
	m_frame = 0;
	m_width = DEFAULT_WORLD_WIDTH;
	m_height = DEFAULT_WORLD_HEIGHT;
	m_objList = NULL;
#ifdef ALLOW_NONSLEEPY_UPDATES
	m_normalUpdates.clear();
#endif
	for (std::vector<UpdateModulePtr>::iterator it = m_sleepyUpdates.begin(); it != m_sleepyUpdates.end(); ++it)
	{
		(*it)->friend_setIndexInLogic(-1);
	}
	m_sleepyUpdates.clear();
	m_curUpdateModule = NULL;

	//
	// only reset the next object ID allocater counter when we're not loading a save game.
	// for save games, we read this value out of the save game file and it is important
	// that we preserve it as we load and execute the game
	//
	if( loadingSaveGame == FALSE )
		m_nextObjID = (ObjectID)1;

}

//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
Bool GameLogic::isInSinglePlayerGame( void )
{
	return (m_gameMode == GAME_SINGLE_PLAYER ||
		(TheRecorder && TheRecorder->getMode() == RECORDERMODETYPE_PLAYBACK && TheRecorder->getGameMode() == GAME_SINGLE_PLAYER));
}


//-------------------------------------------------------------------------------------------------
/** Destroy all objects immediately */
//-------------------------------------------------------------------------------------------------
void GameLogic::destroyAllObjectsImmediate()
{
	// destroy all remaining objects
	Object *obj;
	Object *nextObj;
	for( obj = m_objList; obj; obj = nextObj )
	{
		nextObj = obj->getNextObject();
		destroyObject( obj );
	}

	// process the destroy list immediately
	processDestroyList();
	DEBUG_ASSERTCRASH( m_objList == NULL, ("destroyAllObjectsImmediate: Object list not cleared\n") );

}  // end destroyAllObjectsImmediate

//-------------------------------------------------------------------------------------------------
/**GameLogic class destructor, the destruction order should mirror the
 * initialization order */
// ------------------------------------------------------------------------------------------------
GameLogic::~GameLogic()
{

	// clear any object TOC we might have
	m_objectTOC.clear();

	if (m_background)
	{
		m_background->destroyWindows();
		m_background->deleteInstance();
		m_background = NULL;
	}

	// destroy all remaining objects
	destroyAllObjectsImmediate();

	// delete the logical terrain
	delete TheTerrainLogic;
	TheTerrainLogic = NULL;

	delete TheGhostObjectManager;
	TheGhostObjectManager=NULL;

	// delete the partition manager
	delete ThePartitionManager;
	ThePartitionManager = NULL;

	delete TheScriptActions;
	TheScriptActions = NULL;

	delete TheScriptConditions;
	TheScriptConditions = NULL;

	// delete the Script Engine
	delete TheScriptEngine;
	TheScriptEngine = NULL;
	
	// Null out TheGameLogic
	TheGameLogic = NULL;
}

// ------------------------------------------------------------------------------------------------
/** (re)initialize the instance. */
// ------------------------------------------------------------------------------------------------
void GameLogic::init( void )
{

	setFPMode();

	/// @todo Clear object and destroy lists
	setDefaults( FALSE );

	// create the partition manager
	ThePartitionManager = NEW PartitionManager;
	ThePartitionManager->init();
	ThePartitionManager->setName("ThePartitionManager");


	// Create system for holding deleted objects that are
	// still in the partition manager because player has a fogged
	// view of them.
	TheGhostObjectManager = createGhostObjectManager();

	// create the terrain logic
	TheTerrainLogic = createTerrainLogic();
	TheTerrainLogic->init();
	TheTerrainLogic->setName("TheTerrainLogic");

	// Create script engine system.
	TheScriptActions = NEW ScriptActions;		 // Basically, a subsystem of TheScriptEngine.
	TheScriptConditions = NEW ScriptConditions;	 // Basically, a subsystem of TheScriptEngine.
	TheScriptEngine = NEW ScriptEngine;
	TheScriptEngine->init();
	TheScriptEngine->setName("TheScriptEngine");

	// create a team for the player
	//DEBUG_ASSERTCRASH(ThePlayerList, ("null ThePlayerList"));
	//ThePlayerList->setLocalPlayer(0);

	m_CRC = 0;
	m_gamePaused = FALSE;
	m_inputEnabledMemory = TRUE;
	m_mouseVisibleMemory = TRUE;
	for(Int i = 0; i < MAX_SLOTS; ++i)
	{
		m_progressComplete[i] = FALSE;
		m_progressCompleteTimeout[i] = 0;
	}
	m_forceGameStartByTimeOut = FALSE;

	m_isScoringEnabled = TRUE;
	m_showBehindBuildingMarkers = TRUE;
	m_drawIconUI = TRUE;
	m_showDynamicLOD = TRUE;
	m_scriptHulkMaxLifetimeOverride = -1;
	
	m_isInUpdate = FALSE;

	m_rankPointsToAddAtGameStart = 0;
}

//-------------------------------------------------------------------------------------------------
/** Reset the game logic systems */
//-------------------------------------------------------------------------------------------------
void GameLogic::reset( void )
{
	m_thingTemplateBuildableOverrides.clear();
	m_controlBarOverrides.clear();

	// set the hash to be rather large. We need to optimize this value later.
//	m_objHash.clear();
//	m_objHash.resize(OBJ_HASH_SIZE);
	m_objVector.clear();
	m_objVector.resize(OBJ_HASH_SIZE, NULL);

	m_gamePaused = FALSE;
	m_inputEnabledMemory = TRUE;
	m_mouseVisibleMemory = TRUE;
	setFPMode();

	// destroy all objects
	destroyAllObjectsImmediate();

	m_nextObjID = (ObjectID)1;

	m_frameObjectsChangedTriggerAreas = 0;

	TheGhostObjectManager->reset();
	ThePartitionManager->reset();
	TheTerrainLogic->reset();
	TheAI->reset();
	TheScriptEngine->reset();

	m_CRC = 0;
	for(Int i = 0; i < MAX_SLOTS; ++i)
	{
		m_progressComplete[i] = FALSE;
		m_progressCompleteTimeout[i] = 0;
	}
	m_forceGameStartByTimeOut = FALSE;

	if(TheStatsCollector)
	{
		delete TheStatsCollector;
		TheStatsCollector = NULL;
	}

	// clear any table of contents we have
	m_objectTOC.clear();

	setDefaults( FALSE );

	m_isScoringEnabled = TRUE;
	m_showBehindBuildingMarkers = TRUE;
	m_drawIconUI = TRUE;
	m_showDynamicLOD = TRUE;
	m_scriptHulkMaxLifetimeOverride = -1;

	// Clean up any water transparency overrides that were generated for this map.
	WaterTransparencySetting *wt = (WaterTransparencySetting*) TheWaterTransparency.getNonOverloadedPointer();
	TheWaterTransparency = (WaterTransparencySetting*) wt->deleteOverrides();

	// Clean up any weather overrides that were generated for this map.
	WeatherSetting *ws = (WeatherSetting*) TheWeatherSetting.getNonOverloadedPointer();
	TheWeatherSetting = (WeatherSetting*) ws->deleteOverrides();

	m_rankPointsToAddAtGameStart = 0;
}  // end reset

static Object * placeObjectAtPosition(Int slotNum, AsciiString objectTemplateName, Coord3D& pos, Player *pPlayer,
																	const PlayerTemplate *pTemplate)
{
	const ThingTemplate* btt = TheThingFactory->findTemplate(objectTemplateName);

	DEBUG_ASSERTCRASH(btt, ("TheThingFactory didn't find a template in placeObjectAtPosition()") );

	Object *obj = TheThingFactory->newObject( btt, pPlayer->getDefaultTeam() );
	DEBUG_ASSERTCRASH(obj, ("TheThingFactory didn't give me a valid Object for player %d's (%ls) starting building\n",
		slotNum, pTemplate->getDisplayName().str()));
	if (obj)
	{
		obj->setOrientation(obj->getTemplate()->getPlacementViewAngle());	
		obj->setPosition( &pos );

		//DEBUG_LOG(("Placed a starting building for %s at waypoint %s\n", playerName.str(), waypointName.str()));
		CRCDEBUG_LOG(("Placed an object for %ls at pos (%g,%g,%g)\n", pPlayer->getPlayerDisplayName().str(),
			pos.x, pos.y, pos.z));
		DUMPCOORD3D(&pos);

		Team *team = pPlayer->getDefaultTeam();
		// Now onCreates were called at the constructor.  This magically created
		// thing needs to be considered as Built for Game specific stuff.
		for (BehaviorModule** m = obj->getBehaviorModules(); *m; ++m)
		{
			CreateModuleInterface* create = (*m)->getCreate();
			if (!create)
				continue;
			create->onBuildComplete();
		}

		// Since the team now has members, activate it.
		// srj sez: team should not be null, but could be for ill-formed user maps. so don't crash.
		if (team)
			team->setActive();
		TheAI->pathfinder()->addObjectToPathfindMap(obj);
		if (obj->getAIUpdateInterface() && !obj->isKindOf(KINDOF_IMMOBILE)) {
			CRCDEBUG_LOG(("Not immobile - adjusting dest\n"));
			if (TheAI->pathfinder()->adjustDestination(obj, obj->getAIUpdateInterface()->getLocomotorSet(), &pos)) {
				DUMPCOORD3D(&pos);
				TheAI->pathfinder()->updateGoal(obj, &pos, LAYER_GROUND);	// Units always start on the ground for now.  jba.
				obj->setPosition( &pos );
			}
		}
	}

	return obj;
}

static void placeNetworkBuildingsForPlayer(Int slotNum, const GameSlot *pSlot, Player *pPlayer, const PlayerTemplate *pTemplate)
{
	Int startPos = pSlot->getStartPos();
	AsciiString waypointName;
	waypointName.format("Player_%d_Start", startPos+1); // start pos waypoints are 1-based

	AsciiString rallyWaypointName;
	rallyWaypointName.format("Player_%d_Rally", startPos+1); // start pos waypoints are 1-based

	Waypoint *waypoint = findNamedWaypoint(waypointName);
	Waypoint *rallyWaypoint = findNamedWaypoint(rallyWaypointName);
	DEBUG_ASSERTCRASH(waypoint, ("Player %d has no starting waypoint (Player_%d_Start)\n", slotNum, startPos));
	if (!waypoint)
		return;

	Coord3D pos = *waypoint->getLocation();
	pos.z = TheTerrainLogic->getGroundHeight( pos.x, pos.y );

	AsciiString buildingTemplateName = pTemplate->getStartingBuilding();

	DEBUG_ASSERTCRASH(!buildingTemplateName.isEmpty(), ("no starting building type for player %d (playertemplate %ls)\n",
		slotNum, pTemplate->getDisplayName().str()));
	if (buildingTemplateName.isEmpty())
		return;

	DEBUG_LOG(("Placing starting building at waypoint %s\n", waypointName.str()));
	Object *conYard = placeObjectAtPosition(slotNum, buildingTemplateName, pos, pPlayer, pTemplate);

	if (!conYard)
		return;

	pPlayer->onStructureCreated(NULL, conYard);
	pPlayer->onStructureConstructionComplete(NULL, conYard, FALSE);

	//pos.x -= conYard->getGeometryInfo().getBoundingSphereRadius()/2;
	pos.y -= conYard->getGeometryInfo().getBoundingSphereRadius()/2;
	pos.z = TheTerrainLogic->getGroundHeight( pos.x, pos.y );

	if (rallyWaypoint)
	{
		pos = *rallyWaypoint->getLocation();
		pos.z = TheTerrainLogic->getGroundHeight( pos.x, pos.y );
	}

	for (Int i=0; i<MAX_MP_STARTING_UNITS; ++i)
	{
		AsciiString objName = pTemplate->getStartingUnit(i);
		if (objName.isNotEmpty())
		{
			Coord3D objPos = pos;
			FindPositionOptions options;
			options.minRadius = conYard->getGeometryInfo().getBoundingSphereRadius() * 0.7f;
			options.maxRadius = conYard->getGeometryInfo().getBoundingSphereRadius() * 1.3f;
			DEBUG_LOG(("Placing starting object %d (%s)\n", i, objName.str()));
			ThePartitionManager->update();
			Bool foundPos = ThePartitionManager->findPositionAround(&pos, &options, &objPos);
			if (foundPos)
			{
				Object *unit = placeObjectAtPosition(slotNum, objName, objPos, pPlayer, pTemplate);
				if (unit) {
					pPlayer->onUnitCreated(NULL, unit);
				}
			}
			else
			{
				DEBUG_LOG(("Could not find position\n"));
			}
		}
	}
}

// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
LoadScreen *GameLogic::getLoadScreen( Bool loadingSaveGame )
{
	switch (m_gameMode) 
	{
	case GAME_SHELL:
		return NEW ShellGameLoadScreen;
		break;                         
	case GAME_SINGLE_PLAYER:
	{
		Campaign* currentCampaign = TheCampaignManager->getCurrentCampaign();
		if( currentCampaign && loadingSaveGame == FALSE )
		{
			if ( currentCampaign->m_isChallengeCampaign) 
			{
				return NEW ChallengeLoadScreen;
			}
			return NEW SinglePlayerLoadScreen;
		}
		else
			return NEW ShellGameLoadScreen;
		break;
	}
	case GAME_SKIRMISH:
		return NEW MultiPlayerLoadScreen;
		break;
	case GAME_LAN:
		return NEW MultiPlayerLoadScreen;
		break;
	case GAME_REPLAY:
		return NEW ShellGameLoadScreen;
		break;
	case GAME_INTERNET:
		return NEW GameSpyLoadScreen;
		break;
	case GAME_NONE:
	default:
		return NULL;
	}

}

// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
void handleNameChange( MapObject *mapObj )
{
	if( !mapObj->getName().compare( "AmericaTankLeopard" ) )
	{
		mapObj->setName( "AmericaTankCrusader" );
		const ThingTemplate *thingTemplate = TheThingFactory->findTemplate( mapObj->getName() );
		mapObj->setThingTemplate( thingTemplate );
	}
	if( !mapObj->getName().compare( "AmericaVehicleHumVee" ) )
	{
		mapObj->setName( "AmericaVehicleHumvee" );
		const ThingTemplate *thingTemplate = TheThingFactory->findTemplate( mapObj->getName() );
		mapObj->setThingTemplate( thingTemplate );
	}
}

// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
static void checkForDuplicateColors( GameInfo *game )
{
	if(!game)
		return;
	Int i;

	// In QuickMatch, people can possibly set preferred color and house.
	// Here, we check for collisions in the color choice.  If there is a
	// collision, the first player will get the color, and the other(s)
	// will map to random.
	for (i=MAX_SLOTS-1; i>=0; --i)
	{
		GameSlot *slot = game->getSlot(i);

		if (!slot || !slot->isOccupied())
			continue;

		Int colorIdx = slot->getColor();
		if (colorIdx < 0 || colorIdx >= TheMultiplayerSettings->getNumColors())
			continue; // don't need to fix up random ones

		slot->setColor(-1);
		if (!game->isColorTaken(colorIdx))
		{
			// we were the only one using the color.  it is safe to keep using it.
			slot->setColor(colorIdx);
		}
		else if (colorIdx >= 0)
		{
			DEBUG_LOG(("Clearing color %d for player %d\n", colorIdx, i));
		}
	}
}

// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
static void populateRandomSideAndColor( GameInfo *game )
{
	if(!game)
		return;
	Int i;

#define MORE_RANDOM
#ifdef MORE_RANDOM
	std::vector<Int> startSlots;
	for (i = 0; i < ThePlayerTemplateStore->getPlayerTemplateCount(); ++i)
	{
		const PlayerTemplate* ptTest = ThePlayerTemplateStore->getNthPlayerTemplate(i);
		if (!ptTest || ptTest->getStartingBuilding().isEmpty())
			continue;

		if ( game->oldFactionsOnly() && !ptTest->isOldFaction() )
		  continue;

		// Prevent from selecting the disabled Generals.
		// This is also enforced at GUI setup (GUIUtil.cpp).
		// @todo: unlock these when something rad happens
		Bool disallowLockedGenerals = TRUE;
		const GeneralPersona *general = TheChallengeGenerals->getGeneralByTemplateName(ptTest->getName());
		Bool startsLocked = general ? !general->isStartingEnabled() : FALSE;
		if (disallowLockedGenerals && startsLocked)
			continue;

		startSlots.push_back(i);
	}
#endif

	for (i=0; i<MAX_SLOTS; ++i)
	{
		GameSlot *slot = game->getSlot(i);

		if (!slot || !slot->isOccupied())
			continue;

		// clean up random factions
		Int playerTemplateIdx = slot->getPlayerTemplate();
		DEBUG_LOG(("Player %d has playerTemplate index %d\n", i, playerTemplateIdx));
		while (playerTemplateIdx != PLAYERTEMPLATE_OBSERVER && (playerTemplateIdx < 0 || playerTemplateIdx >= ThePlayerTemplateStore->getPlayerTemplateCount()))
		{
			DEBUG_ASSERTCRASH(playerTemplateIdx == PLAYERTEMPLATE_RANDOM, ("Non-random bad playerTemplate %d in slot %d\n", playerTemplateIdx, i));
#ifdef MORE_RANDOM
			// our RNG is basically shit -- horribly nonrandom at the start of the sequence.
			// get a few values at random to get rid of the dreck.
			// there's no mathematical basis for this, but empirically, it helps a lot.
			UnsignedInt silly = GetGameLogicRandomSeed() % 7;
			for (Int poo = 0; poo < silly; ++poo) 
			{
				GameLogicRandomValue(0, 1);	// ignore result
			}
			Int idxIdx = GameLogicRandomValue(0, 1000) % startSlots.size();
			playerTemplateIdx = startSlots[idxIdx];
#else
			playerTemplateIdx = GameLogicRandomValue(0, ThePlayerTemplateStore->getPlayerTemplateCount()-1);
#endif
			const PlayerTemplate* pt = ThePlayerTemplateStore->getNthPlayerTemplate(playerTemplateIdx);
			if (!pt || pt->getStartingBuilding().isEmpty())
			{
#ifdef MORE_RANDOM
				DEBUG_CRASH(("should not be possible"));
#endif
				playerTemplateIdx = -1; // only pick playable factions
			}
			else
			{
				DEBUG_LOG(("Setting playerTemplateIdx %d to %d\n", i, playerTemplateIdx));
				slot->setPlayerTemplate(playerTemplateIdx);
			}
		}

		Int colorIdx = slot->getColor();
		if (colorIdx < 0 || colorIdx >= TheMultiplayerSettings->getNumColors())
		{
			DEBUG_ASSERTCRASH(colorIdx == -1, ("Non-random bad color %d in slot %d\n", colorIdx, i));
			while (colorIdx == -1)
			{
				colorIdx = GameLogicRandomValue(0, TheMultiplayerSettings->getNumColors()-1);
				if (game->isColorTaken(colorIdx))
					colorIdx = -1;
			}
			DEBUG_LOG(("Setting color %d to %d\n", i, colorIdx));
			slot->setColor(colorIdx);
		}
	}
}

// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
static void populateRandomStartPosition( GameInfo *game )
{
	if(!game)
		return;

	Int i;
	Int numPlayers = MAX_SLOTS;
	const MapMetaData *md = TheMapCache->findMap( game->getMap() );
  DEBUG_ASSERTCRASH( md , ("Could not find map %s in the mapcache", game->getMap().str()));
	if (md)
		numPlayers = md->m_numPlayers;

	// generate a map of start spot distances
	Real startSpotDistance[MAX_SLOTS][MAX_SLOTS];
	for (i=0; i<MAX_SLOTS; ++i)
	{
		for (Int j=0; j<MAX_SLOTS; ++j)
		{
			if (i != j && (i<numPlayers && j<numPlayers))
			{
				AsciiString w1, w2;
				w1.format("Player_%d_Start", i+1);
				w2.format("Player_%d_Start", j+1);
				WaypointMap::const_iterator c1 = md->m_waypoints.find(w1);
				WaypointMap::const_iterator c2 = md->m_waypoints.find(w2);
				if (c1 == md->m_waypoints.end() || c2 == md->m_waypoints.end())
				{
					// couldn't find a waypoint.  must be kinda far away.
					startSpotDistance[i][j] = 1000000.0f;
				}
				else
				{
					Coord3D p1 = c1->second;
					Coord3D p2 = c2->second;
					startSpotDistance[i][j] = sqrt( sqr(p1.x-p2.x) + sqr(p1.y-p2.y) );
				}
			}
			else
			{
				startSpotDistance[i][j] = 0.0f; // not gonna need this
			}
		}
	}

	// see if a start spot has been chosen at all yet
	Bool hasStartSpotBeenPicked = FALSE;
	Bool taken[MAX_SLOTS];
	for (i=0; i<MAX_SLOTS; ++i)
	{
		taken[i] = (i<numPlayers)?FALSE:TRUE;
	}
	for (i=0; i<MAX_SLOTS; ++i)
	{
		GameSlot *slot = game->getSlot(i);

		if (!slot || !slot->isOccupied() || slot->getPlayerTemplate() == PLAYERTEMPLATE_OBSERVER)
			continue;

		Int posIdx = slot->getStartPos();
		if (posIdx >= 0 || posIdx >= numPlayers)
		{
			hasStartSpotBeenPicked = TRUE;
			taken[posIdx] = TRUE;
		}
	}

#if 0  //GS  The old way puts everyone as far apart as possible.
	// now pick non-observer spots
	for (i=0; i<MAX_SLOTS; ++i)
	{
		GameSlot *slot = game->getSlot(i);

		if (!slot || !slot->isOccupied() || slot->getPlayerTemplate() == PLAYERTEMPLATE_OBSERVER)
			continue;

		// clean up random start spots
		Int posIdx = slot->getStartPos();
		if (posIdx < 0 || posIdx >= numPlayers)
		{
			DEBUG_ASSERTCRASH(posIdx == -1, ("Non-random bad start position %d in slot %d\n", posIdx, i));
			if (hasStartSpotBeenPicked)
			{
				// pick the farthest spot away
				Real farthestDistance = 0.0f;
				Int farthestIndex = -1;
				for (posIdx = 0; posIdx < numPlayers; ++posIdx)
				{
					if (!taken[posIdx])
					{
						if (farthestIndex < 0)
						{
							farthestIndex = posIdx; // take this one as best if none else
							for (Int n=0; n<numPlayers; ++n)
							{
								if (taken[n] && n != posIdx)
									farthestDistance += startSpotDistance[posIdx][n];
							}
						}
						else
						{
							Real dist = 0.0f;
							for (Int n=0; n<numPlayers; ++n)
							{
								if (taken[n] && n != posIdx)
									dist += startSpotDistance[posIdx][n];
							}
							if (dist > farthestDistance)
							{
								farthestDistance = dist;
								farthestIndex = posIdx;
							}
						}
					}
				}
				DEBUG_ASSERTCRASH(farthestIndex >= 0, ("Couldn't find a farthest spot!\n"));
				slot->setStartPos(farthestIndex);
				taken[farthestIndex] = TRUE;
			}
			else
			{
				// We're the first real spot.  Pick randomly.
				// This while loop shouldn't be neccessary, since we're first.  Why not, though?
				while (posIdx == -1)
				{
					posIdx = GameLogicRandomValue(0, numPlayers-1);
					if (game->isStartPositionTaken(posIdx))
						posIdx = -1;
				}
				DEBUG_LOG(("Setting start position %d to %d (random choice)\n", i, posIdx));
				slot->setStartPos(posIdx);
				taken[posIdx] = TRUE;
				hasStartSpotBeenPicked = TRUE;
			}
		}
	}
#else  //GS  The new way puts teammates next to each other.
	Int teamPosIdx[MAX_SLOTS];
	for (i=0; i<MAX_SLOTS; ++i)
		teamPosIdx[i] = -1;  //team has no starting position yet

	// now pick non-observer spots
	for (i=0; i<MAX_SLOTS; ++i)
	{
		GameSlot *slot = game->getSlot(i);

		if (!slot || !slot->isOccupied() || slot->getPlayerTemplate() == PLAYERTEMPLATE_OBSERVER)
			continue;  //slot not used

		Int posIdx = slot->getStartPos();
		if (posIdx >= 0  &&  posIdx < numPlayers)
			continue;  //position already assigned
		DEBUG_ASSERTCRASH(posIdx == -1, ("Non-random bad start position %d in slot %d\n", posIdx, i));

		//choose a starting position
		Int team = slot->getTeamNumber();
		if( !hasStartSpotBeenPicked )
		{	// We're the first real spot.  Pick randomly.
			while (posIdx == -1)
			{	// This while loop shouldn't be neccessary, since we're first.  Why not, though?
				posIdx = GameLogicRandomValue(0, numPlayers-1);
				if (game->isStartPositionTaken(posIdx))
					posIdx = -1;
			}
			DEBUG_LOG(("Setting start position %d to %d (random choice)\n", i, posIdx));
			hasStartSpotBeenPicked = TRUE;
			slot->setStartPos(posIdx);
			taken[posIdx] = TRUE;
			if( team > -1 )
				teamPosIdx[team] = posIdx;  //remember where this team is
		} else
		{	//pick teams far apart, team members close together
			if( team < 0  ||  teamPosIdx[ team ] == -1 )  //if team None or team not yet placed
			{	//pick position furthest from all other teams
				Real farthestDistance = 0.0f;
				Int farthestIndex = -1;
				for (posIdx = 0; posIdx < numPlayers; ++posIdx)
				{
					if (taken[posIdx])
						continue;  //skip occupied positions

					if (farthestIndex < 0)
					{	//take this one as best if none else
						farthestIndex = posIdx;
						for (Int n=0; n<numPlayers; ++n)
						{
							if (taken[n] && n != posIdx)
								farthestDistance += startSpotDistance[posIdx][n];
						}
					}
					else
					{	//find empty position furthest from all taken positions
						Real dist = 0.0f;
						for (Int n=0; n<numPlayers; ++n)
						{
							if (taken[n] && n != posIdx)
								dist += startSpotDistance[posIdx][n];
						}
						if (dist > farthestDistance)
						{
							farthestDistance = dist;
							farthestIndex = posIdx;
						}
					}
				} //for posInx

				DEBUG_ASSERTCRASH(farthestIndex >= 0, ("Couldn't find a farthest spot!\n"));
				slot->setStartPos(farthestIndex);
				taken[farthestIndex] = TRUE;
				if( team > -1 )
					teamPosIdx[team] = farthestIndex;  //remember where this team is
			} //pick farthest
			else  //team already has a starting position
			{	//pick position closest to team
				Real closestDist = FLT_MAX;
				Int  closestIdx = 0;
				for( Int n=0;  n < numPlayers;  ++n )
				{
					Real dist = startSpotDistance[ teamPosIdx[team] ][n];
					if( !taken[n]  &&  dist < closestDist )
					{	//found a better match
						closestDist = dist;
						closestIdx = n;
					}
				} //for n
				DEBUG_ASSERTCRASH( closestDist < FLT_MAX, ("Couldn't find a closest starting positon!\n"));
				slot->setStartPos(closestIdx);
				taken[closestIdx] = TRUE;
			}
		}
	} //for i
#endif 0

	// now go back & assign observer spots
	Int numPlayersInGame = 0;
	for (i=0; i<MAX_SLOTS; ++i)
	{
		const GameSlot *slot = game->getConstSlot(i);
		if (slot->isOccupied() && slot->getPlayerTemplate() != PLAYERTEMPLATE_OBSERVER)
			++numPlayersInGame;
	}
	for (i=0; i<MAX_SLOTS; ++i)
	{
		GameSlot *slot = game->getSlot(i);

		if (!slot || !slot->isOccupied())
			continue;

		if (slot->getPlayerTemplate() != PLAYERTEMPLATE_OBSERVER)
			continue;

		// clean up random start spots
		Int posIdx = -1;
		if (numPlayersInGame == 0)
			posIdx = 0;
		while (posIdx == -1)
		{
			posIdx = GameLogicRandomValue(0, numPlayers-1);
			if (!game->isStartPositionTaken(posIdx))
				posIdx = -1;
		}
		DEBUG_LOG(("Setting observer start position %d to %d\n", i, posIdx));
		slot->setStartPos(posIdx);
	}
}

// ------------------------------------------------------------------------------------------------
/** Update the load screen progress */
// ------------------------------------------------------------------------------------------------
void GameLogic::updateLoadProgress( Int progress )
{
	
	if( m_loadScreen )
		m_loadScreen->update( progress );

}  // end updateLoadProgress

// ------------------------------------------------------------------------------------------------
/** Delete the load screen */
// ------------------------------------------------------------------------------------------------
void GameLogic::deleteLoadScreen( void )
{

	if( m_loadScreen )
	{

		delete m_loadScreen;
		m_loadScreen = NULL;

	}  // end if

}  // end deleteLoadScreen

// ------------------------------------------------------------------------------------------------
/** Entry point for starting a new game, the engine is already in clean state at this
	* point and ready to load up with all the data */
// ------------------------------------------------------------------------------------------------
void GameLogic::startNewGame( Bool loadingSaveGame )
{

	#ifdef DUMP_PERF_STATS
	__int64 startTime64;
	__int64 endTime64,freq64;
	GetPrecisionTimerTicksPerSec(&freq64);
	GetPrecisionTimer(&startTime64);
	#endif

	setLoadingMap( TRUE );

	if( loadingSaveGame == FALSE )
	{

		// record pristine map name when we're loading from the map (not a save game)
		TheGameState->setPristineMapName( TheGlobalData->m_mapName );

		//
		// sanity, the pristine map should never start with "Save", otherwise that would be
		// a map from the save directory
		//
		if (TheGameState->isInSaveDirectory(TheGlobalData->m_mapName))
		{

			DEBUG_CRASH(( "FATAL SAVE/LOAD ERROR! - Setting a pristine map name that refers to a map in the save directory.  The pristine map should always refer to the ORIGINAL map in the Maps directory, if the pristine map string is corrupt then map.ini files will not load correctly.\n" ));

		}  // end if

		if( m_startNewGame == FALSE )
		{
			/// @todo: Here is where we would look at the game mode & play an intro movie or something.
			// Failing that, we just set the flag so the actual game can start from a uniform
			// entry point (startNewGame() called from update()).
			if( m_gameMode == GAME_SINGLE_PLAYER )
			{

				if(m_background)
				{
					m_background->destroyWindows();
					m_background->deleteInstance();
					m_background = NULL;
				}
				m_loadScreen = getLoadScreen( loadingSaveGame );
				if(m_loadScreen)
				{
					TheWritableGlobalData->m_loadScreenRender = TRUE;	///< mark it so only a few select things are rendered during load
					m_loadScreen->init(NULL);
				}

			}

			m_startNewGame = TRUE;
			return;

		}  

	}  // end if

	m_rankLevelLimit = 1000;	// this is reset every game.
	setDefaults( loadingSaveGame );
	TheWritableGlobalData->m_loadScreenRender = TRUE;	///< mark it so only a few select things are rendered during load	
	TheWritableGlobalData->m_TiVOFastMode = FALSE;	//always disable the TIVO fast-forward mode at the start of a new game.

	m_showBehindBuildingMarkers = TRUE;
	m_drawIconUI = TRUE;
	m_showDynamicLOD = TRUE;
	m_scriptHulkMaxLifetimeOverride = -1;

	Campaign* currentCampaign = TheCampaignManager->getCurrentCampaign();
	Bool isChallengeCampaign = m_gameMode == GAME_SINGLE_PLAYER && currentCampaign && currentCampaign->m_isChallengeCampaign;

	// Fill in the game color and Factions before we do the Load Screen
	GameInfo *game = NULL;
	TheGameInfo = NULL;
	Int localSlot = 0;
	if (TheNetwork)
	{
		if (TheLAN)
		{
			DEBUG_LOG(("Starting network game\n"));
			TheGameInfo = game = TheLAN->GetMyGame();
		}
		else
		{
			DEBUG_LOG(("Starting gamespy game\n"));
			TheGameInfo = game = TheGameSpyGame;	/// @todo: MDC add back in after demo
		}
	}
	else
	{
		if (TheRecorder && TheRecorder->getMode() == RECORDERMODETYPE_PLAYBACK)
		{
			TheGameInfo = game = TheRecorder->getGameInfo();
		}
		else if(m_gameMode == GAME_SKIRMISH)
		{
		  TheGameInfo = game = TheSkirmishGameInfo;
		}
		else if(isChallengeCampaign)
		{
			TheGameInfo = game = TheChallengeGameInfo;
		}
	}

  // On a NEW game, we need to copy the superweapon restrictions from the game info to here
  // (because TheGameInfo is not always saved and doesn't carry over to replays). On a save
  // game, we save the superweapon restrictions in GameLogic::xfer()
  if ( !loadingSaveGame )
  {
    if ( TheGameInfo )
    {
      m_superweaponRestriction = TheGameInfo->getSuperweaponRestriction();
    }
    else
    {
      // ??? Apparently this is legit? Oh well, use defaults
      m_superweaponRestriction = 0;
    }
  }

	checkForDuplicateColors( game );

	Bool isSkirmishOrSkirmishReplay = FALSE;
	if (game)
	{
		for (Int i=0; i<MAX_SLOTS; ++i)
		{
			GameSlot *slot = game->getSlot(i);
			if (!loadingSaveGame) {
				slot->saveOffOriginalInfo();
			}
			if (slot->isAI())
			{
				isSkirmishOrSkirmishReplay = TRUE;
				continue;
			}
		}
	} else {
		if (m_gameMode == GAME_SINGLE_PLAYER)	{
			if (TheSkirmishGameInfo) {
				delete TheSkirmishGameInfo;
				TheSkirmishGameInfo = NULL;
			}
		}
	}

	populateRandomSideAndColor( game );
	populateRandomStartPosition( game );

	//****************************//
	// Start the LoadScreen Now!	//
	//****************************//

	// Get the m_loadScreen for this kind of game
	if(!m_loadScreen)
	{
		m_loadScreen = getLoadScreen( loadingSaveGame );
		if(m_loadScreen)
		{
			TheMouse->setVisibility(FALSE);
			m_loadScreen->init(game);

			// 
			updateLoadProgress( LOAD_PROGRESS_START );
		}
	}
	if(m_background)
	{
		m_background->destroyWindows();
		m_background->deleteInstance();
		m_background = NULL;
	}
	setFPMode();
	if(TheCampaignManager)
		TheCampaignManager->SetVictorious(FALSE);
	m_startNewGame = FALSE;

	// update the loadscreen 
	if(m_loadScreen)
		updateLoadProgress(LOAD_PROGRESS_POST_PARTICLE_INI_LOAD);

	// reset the frame counter
	m_frame = 0;

	// before loading the map, load the map.ini file in the same directory.
	loadMapINI( TheGlobalData->m_mapName );

	// load a map
	TheTerrainLogic->loadMap( TheGlobalData->m_mapName, false );
	// anytime the world's size changes, must reset the partition mgr
	//ThePartitionManager->init();

	// update the loadscreen 
	updateLoadProgress(LOAD_PROGRESS_POST_LOAD_MAP);

	#ifdef DUMP_PERF_STATS
	GetPrecisionTimer(&endTime64);
	char Buf[256];
	sprintf(Buf,"After terrainlogic->loadmap=%f\n",((double)(endTime64-startTime64)/(double)(freq64)*1000.0));
		//DEBUG_LOG(("Placed a starting building for %s at waypoint %s\n", playerName.str(), waypointName.str()));
	DEBUG_LOG(("%s", Buf));
	#endif

	Int progressCount = LOAD_PROGRESS_SIDE_POPULATION;
	if (game)
	{
		 
		if (TheGameEngine->isMultiplayerSession() || isSkirmishOrSkirmishReplay)
		{
			// Saves off any player, and resets the sides to 0 players so we can add the skirmish players.
			TheSidesList->prepareForMP_or_Skirmish();
		}

		//DEBUG_LOG(("Starting LAN game with %d players\n", game->getNumPlayers()));
		Dict d;
		for (int i=0; i<MAX_SLOTS; ++i)
		{
			// Add a Side to TheSidesList
			GameSlot *slot = game->getSlot(i);

			if (!slot || !slot->isHuman())
			{
				m_progressComplete[i] = TRUE;
				lastHeardFrom(i);
			}

			if (!slot || !slot->isOccupied())
				continue;

			d.clear();
			AsciiString playerName;
			playerName.format("player%d", i);
			d.setAsciiString(TheKey_playerName, playerName);
			d.setBool(TheKey_playerIsHuman, slot->isHuman());
			d.setUnicodeString(TheKey_playerDisplayName, slot->getName());
			const PlayerTemplate* pt;
			if (slot->getPlayerTemplate() >= 0)
				pt = ThePlayerTemplateStore->getNthPlayerTemplate(slot->getPlayerTemplate());
			else
				pt = ThePlayerTemplateStore->findPlayerTemplate( TheNameKeyGenerator->nameToKey("FactionObserver") );
			if (pt)
			{
				d.setAsciiString(TheKey_playerFaction, KEYNAME(pt->getNameKey()));
			}
			
			if (game->isPlayerPreorder(i))
			{
				d.setBool(TheKey_playerIsPreorder, TRUE);
			}
					
			AsciiString enemiesString, alliesString;
			Int team = slot->getTeamNumber();
			DEBUG_LOG(("Looking for allies of player %d, team %d\n", i, team));
			for(int j=0; j < MAX_SLOTS; ++j)
			{
				GameSlot *teamSlot = game->getSlot(j);
				// for check to see if we're trying to add ourselves
				if(i == j || !teamSlot->isOccupied())
					continue;
				
				DEBUG_LOG(("Player %d is team %d\n", j, teamSlot->getTeamNumber()));

				AsciiString teamPlayerName;
				teamPlayerName.format("player%d", j);
				// if our team is None, or our team is not equal to their team, 
				// then their our enemy
				Bool isEnemy = FALSE;
				if(team == -1 || teamSlot->getTeamNumber() != team ) isEnemy = TRUE;
				DEBUG_LOG(("Player %d is %s\n", j, (isEnemy)?"enemy":"ally"));

				if (isEnemy)
				{
					if(!enemiesString.isEmpty())
						enemiesString.concat(" ");
					enemiesString.concat(teamPlayerName);
				}
				else
				{
					// he's on our team, add him!
					if(!alliesString.isEmpty())
						alliesString.concat(" ");
					alliesString.concat(teamPlayerName);
				}				
			}
			d.setAsciiString(TheKey_playerAllies, alliesString);
			d.setAsciiString(TheKey_playerEnemies, enemiesString);
			DEBUG_LOG(("Player %d's teams are: allies=%s, enemies=%s\n", i,alliesString.str(),enemiesString.str()));
/*

			Int colorIdx = slot->getColor();
			if (colorIdx < 0 || colorIdx >= TheMultiplayerSettings->getNumColors())
			{
				DEBUG_ASSERTCRASH(colorIdx == -1, ("Non-random bad color %d in slot %d\n", colorIdx, i));
				while (colorIdx == -1)
				{
					colorIdx = GameLogicRandomValue(0, TheMultiplayerSettings->getNumColors()-1);
					if (game->isColorTaken(colorIdx))
						colorIdx = -1;
				}
				DEBUG_LOG(("Setting color %d to %d\n", i, colorIdx));
				slot->setColor(colorIdx);
			}
			*/

			d.setInt(TheKey_playerColor, TheMultiplayerSettings->getColor(slot->getColor())->getColor());
			d.setInt(TheKey_playerNightColor, TheMultiplayerSettings->getColor(slot->getColor())->getNightColor());
			d.setInt(TheKey_multiplayerStartIndex, slot->getStartPos());
//			d.setBool(TheKey_multiplayerIsLocal, slot->isLocalPlayer());
//			d.setBool(TheKey_multiplayerIsLocal, slot->getIP() == game->getLocalIP());
			d.setBool(TheKey_multiplayerIsLocal, slot->isHuman() && (slot->getName().compare(game->getSlot(game->getLocalSlotNum())->getName().str()) == 0));

/*
			if (slot->getIP() == game->getLocalIP())
			{
				localSlot = i;
				DEBUG_LOG(("GameLogic::StartNewGame - local slot is %d\n", localSlot));
			}
*/

			if (isSkirmishOrSkirmishReplay) 
			{
				d.setBool(TheKey_playerIsSkirmish, true);
				switch (slot->getState()) {
					case SLOT_EASY_AI : d.setInt(TheKey_skirmishDifficulty, DIFFICULTY_EASY); break;
					case SLOT_MED_AI : d.setInt(TheKey_skirmishDifficulty, DIFFICULTY_NORMAL); break;
					case SLOT_BRUTAL_AI : d.setInt(TheKey_skirmishDifficulty, DIFFICULTY_HARD); break;
					default: break;	 // no setting.
				}
			}

			AsciiString slotNameAscii;
			slotNameAscii.translate(slot->getName());
			if (slot->isHuman() && game->getSlotNum(slotNameAscii) == game->getLocalSlotNum()) {
				localSlot = i;
			}
			TheSidesList->addSide(&d);

			AsciiString playerTeamName;
			playerTeamName.set("team");
			playerTeamName.concat(playerName);

			d.clear();
			d.setAsciiString(TheKey_teamName, playerTeamName);
			d.setAsciiString(TheKey_teamOwner, playerName);
			d.setBool(TheKey_teamIsSingleton, true);
			TheSidesList->addTeam(&d);

			DEBUG_LOG(("Added side %d\n", i));
			updateLoadProgress(progressCount + i);
		}		
	}
	//if(m_gameMode != GAME_REPLAY)
	//{
	
		// Always add in an observer Player
		Dict d;
		d.setAsciiString(TheKey_playerName, "ReplayObserver");
		d.setBool(TheKey_playerIsHuman, TRUE);
		d.setUnicodeString(TheKey_playerDisplayName, UnicodeString(L"Observer"));
		const PlayerTemplate* pt;
		pt = ThePlayerTemplateStore->findPlayerTemplate( TheNameKeyGenerator->nameToKey("FactionObserver") );
		if (pt)
		{
			d.setAsciiString(TheKey_playerFaction, KEYNAME(pt->getNameKey()));
		}
		d.setAsciiString(TheKey_playerAllies, AsciiString::TheEmptyString);
		d.setAsciiString(TheKey_playerEnemies, AsciiString::TheEmptyString);
		d.setInt(TheKey_playerColor, TheMultiplayerSettings->getColor(0)->getColor());
		d.setInt(TheKey_playerNightColor, TheMultiplayerSettings->getColor(0)->getNightColor());
		d.setInt(TheKey_multiplayerStartIndex, 0);
		d.setBool(TheKey_multiplayerIsLocal, FALSE);

		TheSidesList->addSide(&d);	
		d.clear();
		d.setAsciiString(TheKey_teamName, "teamReplayObserver");
		d.setAsciiString(TheKey_teamOwner, "ReplayObserver");
		d.setBool(TheKey_teamIsSingleton, true);
		TheSidesList->addTeam(&d);
	//}
	TheSidesList->validateSides();		

	// update the loadscreen 
	updateLoadProgress(LOAD_PROGRESS_POST_SIDE_LIST_INIT);

	// update the player list to match the new map.
	TheTeamFactory->reset();
	ThePlayerList->newGame();
	
	// update the loadscreen 
	updateLoadProgress(LOAD_PROGRESS_POST_PLAYER_LIST_RESET);

	// Tell the script engine that a newe set of scripts is loaded.
	TheScriptEngine->newMap();

	// update the loadscreen 
	updateLoadProgress(LOAD_PROGRESS_POST_SCRIPT_ENGINE_NEW_MAP);

	if (TheGameEngine->isMultiplayerSession() || isSkirmishOrSkirmishReplay)
	{
		// if there are no other teams (happens for debugging) don't end the game immediately
		Int numTeams = 0; // this can be higher than expected, but is accurate for determining 0, 1, 2+
		Int lastTeam = -1;
		if (game)
		{
			for (int i=0; i<MAX_SLOTS; ++i)
			{
				const GameSlot *slot = game->getConstSlot(i);
				if (slot->isOccupied() && slot->getPlayerTemplate() != PLAYERTEMPLATE_OBSERVER)
				{
					if (slot->getTeamNumber() == -1 || slot->getTeamNumber() != lastTeam)
					{
						++numTeams;
						lastTeam = slot->getTeamNumber();
					}
				}
			}
		}

		if (numTeams > 1)
		{
			// add in the multiplayer victory/defeat scripts
			AsciiString path = "Data\\Scripts\\MultiplayerScripts.scb";
			CachedFileInputStream theInputStream;
			if (theInputStream.open(path))
			{ 
				ChunkInputStream *pStrm = &theInputStream;
				DataChunkInput file( pStrm );
				file.registerParser( AsciiString("PlayerScriptsList"), AsciiString::TheEmptyString, ScriptList::ParseScriptsDataChunk );
				if (!file.parse(NULL)) {
					DEBUG_LOG(("ERROR - Unable to read in multiplayer scripts.\n"));
					return;
				}
				ScriptList *scripts[MAX_PLAYER_COUNT];
				Int count = ScriptList::getReadScripts(scripts);
				if (count)
				{
					ScriptList *pSL = TheSidesList->getSideInfo(0)->getScriptList();
					Script *next = scripts[0]->getScript();
					while (next)
					{
						Script *dupe = next->duplicate();
						pSL->addScript(dupe, 0);
						next = next->getNext();
					}
				}
				for (Int i=0; i<count; ++i)
				{
					scripts[i]->deleteInstance();
				}
			}
		}

		/*
		// add multiplayer single-player defeat script
		if (numTeams > 1)
		{
			AsciiString name;
			name.format("MultiplayerPlayerDefeat"); // name is for the ui in the editor
			Script *pNewScript = newInstance(Script);
			pNewScript->setName(name);

			Condition *pFalse1 = newInstance(Condition)(Condition::MULTIPLAYER_PLAYER_DEFEAT);
			OrCondition *pOr = newInstance(OrCondition);
			pOr->setFirstAndCondition(pFalse1);
			pNewScript->setOrCondition(pOr);

			ScriptAction *action = newInstance(ScriptAction)(ScriptAction::LOCALDEFEAT);
			pNewScript->setAction(action);

			ScriptList *pSL = TheSidesList->getSideInfo(0)->getScriptList();
			pSL->addScript(pNewScript, 0); // adds this as the player's first script (index 0)
		}

		// add multiplayer victory scripts
		if (numTeams > 1)
		{
			AsciiString name;
			name.format("MultiplayerVictory"); // name is for the ui in the editor
			Script *pNewScript = newInstance( Script );
			pNewScript->setName(name);

			Condition *pFalse1 = newInstance(Condition)(Condition::MULTIPLAYER_ALLIED_VICTORY);
			OrCondition *pOr = newInstance(OrCondition);
			pOr->setFirstAndCondition(pFalse1);
			pNewScript->setOrCondition(pOr);

			ScriptAction *action = newInstance(ScriptAction)(ScriptAction::VICTORY);
			pNewScript->setAction(action);

			ScriptList *pSL = TheSidesList->getSideInfo(0)->getScriptList();
			pSL->addScript(pNewScript, 0); // adds this as the player's first script (index 0)
		}

		// add multiplayer defeat scripts
		if (numTeams > 1)
		{
			AsciiString name;
			name.format("MultiplayerDefeat"); // name is for the ui in the editor
			Script *pNewScript = newInstance(Script);
			pNewScript->setName(name);

			Condition *pFalse1 = newInstance(Condition)(Condition::MULTIPLAYER_ALLIED_DEFEAT);
			OrCondition *pOr = newInstance(OrCondition);
			pOr->setFirstAndCondition(pFalse1);
			pNewScript->setOrCondition(pOr);

			ScriptAction *action = newInstance(ScriptAction)(ScriptAction::DEFEAT);
			pNewScript->setAction(action);

			ScriptList *pSL = TheSidesList->getSideInfo(0)->getScriptList();
			pSL->addScript(pNewScript, 0); // adds this as the player's first script (index 0)
		}
		*/
	}

	// update the loadscreen 
	updateLoadProgress(LOAD_PROGRESS_POST_VICTORY_CONDITION_SETUP);

	// set the radar as on a new map
	TheRadar->newMap( TheTerrainLogic );
	TheInGameUI->setClientQuiet( FALSE ); // okay to start beeping and stuff

	// Tell the multiplayer victory condition singleton that the players are created
	TheVictoryConditions->cachePlayerPtrs();
	TheVictoryConditions->setVictoryConditions(VICTORY_NOBUILDINGS);

	// update the loadscreen 
	updateLoadProgress(LOAD_PROGRESS_POST_VICTORY_CONDITION_SET_VICTORY_CONDITION);

	// set the world extents to that of the map
	Region3D extent;
	TheTerrainLogic->getExtent( &extent );

	TheGameLogic->setWidth( extent.hi.x - extent.lo.x );
	TheGameLogic->setHeight( extent.hi.y - extent.lo.y );

	// anytime the world's size changes, must reset the partition mgr
	ThePartitionManager->init();
	ThePartitionManager->refreshShroudForLocalPlayer();// Can't do this until after init, and doesn't seem right to do in init

	TheGhostObjectManager->setLocalPlayerIndex(ThePlayerList->getLocalPlayer()->getPlayerIndex());
	TheGhostObjectManager->reset();
	
	// update the loadscreen 
	updateLoadProgress(LOAD_PROGRESS_POST_GHOST_OBJECT_MANAGER_RESET);

	// update the terrain logic now that all is loaded
	TheTerrainLogic->newMap( loadingSaveGame );

	// update the loadscreen 
	updateLoadProgress(LOAD_PROGRESS_POST_TERRAIN_LOGIC_NEW_MAP);

	#ifdef DUMP_PERF_STATS
	GetPrecisionTimer(&endTime64);
	sprintf(Buf,"After terrainlogic->newmap=%f\n",((double)(endTime64-startTime64)/(double)(freq64)*1000.0));
	DEBUG_LOG(("%s", Buf));
	#endif

		// Special case, load any bridge map objects.
 	const ThingTemplate *thingTemplate;
	MapObject *pMapObj;
	for (pMapObj = MapObject::getFirstMapObject(); pMapObj; pMapObj = pMapObj->getNext()) 
	{

		if (pMapObj->getFlag(FLAG_BRIDGE_FLAGS) || pMapObj->getFlag(FLAG_ROAD_FLAGS)) 
			continue;	// these roads & bridges are special cased in the terrain side.

		// get thing template based from map object name
		thingTemplate = pMapObj->getThingTemplate();
		if( thingTemplate == NULL )
			continue;	

		Bool isBridgeLikeObject = false;

		if (thingTemplate->isBridge()) 
			isBridgeLikeObject = true;
		if (thingTemplate->isKindOf(KINDOF_WALK_ON_TOP_OF_WALL)) 
			isBridgeLikeObject = true;

		if (!isBridgeLikeObject)
			continue;

		Team *team = ThePlayerList->getNeutralPlayer()->getDefaultTeam();
		// create new object in the world
		Object *obj = TheThingFactory->newObject( thingTemplate, team ); //, OBJECT_STATUS_LOADING_FROM_MAP );
		if( obj )
		{
			Coord3D pos = *pMapObj->getLocation();
			pos.z += TheTerrainLogic->getGroundHeight( pos.x, pos.y );

			Real angle = normalizeAngle(pMapObj->getAngle());  
			obj->setOrientation(angle);
			obj->setPosition( &pos );
			if (thingTemplate->isBridge()) {
				TheTerrainLogic->addLandmarkBridgeToLogic(obj);
			}
			if (thingTemplate->isKindOf(KINDOF_WALK_ON_TOP_OF_WALL)) {
				TheAI->pathfinder()->addWallPiece(obj);
			}

			// Do this after positioning the object, because we may place objects in response to keys
			// in the map object properties.
			// update this object instance with properties from the map object
			obj->updateObjValuesFromMapProperties( pMapObj->getProperties() );
		}  // end if

	}	// for, loading bridge map objects

	// update the loadscreen 
	updateLoadProgress(LOAD_PROGRESS_POST_BRIDGE_LOAD);

	// refresh the radar to reflect loaded bridges
	TheRadar->refreshTerrain( TheTerrainLogic );

	// tell the AI about it
	// Note that it is important that the pathfinder be called before the map objects are loaded.
	TheAI->pathfinder()->newMap( );

	// update the loadscreen 
	updateLoadProgress(LOAD_PROGRESS_POST_PATHFINDER_NEW_MAP);

	// reveal the map for the permanent observer
	ThePartitionManager->revealMapForPlayerPermanently( ThePlayerList->findPlayerWithNameKey(TheNameKeyGenerator->nameToKey("ReplayObserver"))->getPlayerIndex() );
	DEBUG_LOG(("Reveal shroud for %ls whose index is %d\n", ThePlayerList->findPlayerWithNameKey(TheNameKeyGenerator->nameToKey("ReplayObserver"))->getPlayerDisplayName().str(),ThePlayerList->findPlayerWithNameKey(TheNameKeyGenerator->nameToKey("ReplayObserver"))->getPlayerIndex()));
	
	if (game)
	{
		for (int i=0; i<MAX_SLOTS; ++i)
		{
			GameSlot *slot = game->getSlot(i);
			
			if (!slot || !slot->isOccupied())
				continue;
			
			AsciiString playerName;
			playerName.format("player%d", i);
			Player *player = ThePlayerList->findPlayerWithNameKey(TheNameKeyGenerator->nameToKey(playerName));
			
			if (slot->getPlayerTemplate() == PLAYERTEMPLATE_OBSERVER)
			{
				DEBUG_LOG(("Clearing shroud for observer %s in playerList slot %d\n", playerName.str(), player->getPlayerIndex()));
				ThePartitionManager->revealMapForPlayerPermanently( player->getPlayerIndex() );
			}
			else
			{
				// remove shroud for the player in MP games
				if (!TheMultiplayerSettings->isShroudInMultiplayer())
					ThePartitionManager->revealMapForPlayer( player->getPlayerIndex() );
			}
		}
	}

	#ifdef DUMP_PERF_STATS
	GetPrecisionTimer(&endTime64);
	sprintf(Buf,"Before loading objects=%f\n",((double)(endTime64-startTime64)/(double)(freq64)*1000.0));
	DEBUG_LOG(("%s", Buf));
	#endif

	Bool useTrees = TheGlobalData->m_useTrees;

	// If forceFluffToProp == true, removable objects get created on client only. [7/14/2003]
	// If static lod is HIGH, we don't do force fluff to client side only (create logic side props, more expensive. jba)
	Bool forceFluffToProp = TheGameLODManager->getStaticLODLevel() != STATIC_GAME_LOD_HIGH;
	if (TheGameLODManager->getStaticLODLevel() == STATIC_GAME_LOD_CUSTOM && 
			TheGlobalData->m_useShadowVolumes) {
		// Custom LOD, and volumetric shadows turned on - very high detail.  So use logic props too. jba. [7/14/2003]
		forceFluffToProp = false;
	}

	if (TheRecorder && TheRecorder->isMultiplayer()) {
		useTrees = TRUE; // Always use trees in multiplayer, cause we want it to sync properly. jba.
		forceFluffToProp = TRUE; // Always do client side fluff - faster, and syncs properly. jba.
	}

	progressCount = LOAD_PROGRESS_LOOP_ALL_THE_FREAKN_OBJECTS;
	Int timer = timeGetTime();
	if( loadingSaveGame ) {
		// Loading a loadingSaveGame, need to add the trees to the client. jba. [8/11/2003]
		for (pMapObj = MapObject::getFirstMapObject(); pMapObj; pMapObj = pMapObj->getNext()) 
		{
			// get thing template based from map object name
			thingTemplate = pMapObj->getThingTemplate();
			if( thingTemplate == NULL )
				continue;
			// don't create trees and shrubs if this is one and we have that option off
			if( thingTemplate->isKindOf( KINDOF_SHRUBBERY ) && !useTrees )
				continue;

			Coord3D pos = *pMapObj->getLocation();
			pos.z += TheTerrainLogic->getGroundHeight( pos.x, pos.y );
			Real angle = normalizeAngle(pMapObj->getAngle());
			if (thingTemplate->isKindOf(KINDOF_OPTIMIZED_TREE)) {
				// Opt trees and props just get drawables to tell the client about it, then deleted. jba [6/5/2003]
				// This way there is no logic object to slow down partition manager and core logic stuff.				
				Drawable *draw = TheThingFactory->newDrawable(thingTemplate, DRAWABLE_STATUS_NONE);
				if (draw) {
					draw->setOrientation(angle);
					draw->setPosition( &pos );
					TheGameClient->destroyDrawable(draw);
				}
			}
		}
	} 
	else 
	{

		for (pMapObj = MapObject::getFirstMapObject(); pMapObj; pMapObj = pMapObj->getNext()) 
		{
		
			if (pMapObj->getFlag(FLAG_BRIDGE_FLAGS) || pMapObj->getFlag(FLAG_ROAD_FLAGS)) {
				continue;	// roads & bridges are special cased in the terrain side.
			}
			//Kris: Added this function so that we can rename objects and preserve them. If the patch
			//      entry is missing for a particular unit, it doesn't get added to the world.
			handleNameChange( pMapObj );

			// get thing template based from map object name
			thingTemplate = pMapObj->getThingTemplate();

			//
			// if no template continue, some map objects don't have thing templates like
			// lights (handled in the device).  Objects that are test objects have a 
			// test string (designated with *** and the define TEST_STRING) and will 
			// have temporary templates created 'on the fly' during a 
			// ThingFactory->findTemplate() call when loading from the map file
			//
			if( thingTemplate == NULL )
				continue;

			Bool isBridgeLikeObject = false;
			if (thingTemplate->isBridge()) isBridgeLikeObject = true;
			if (thingTemplate->isKindOf(KINDOF_WALK_ON_TOP_OF_WALL)) isBridgeLikeObject = true;
			if (isBridgeLikeObject)
				continue;	// bridges have to be added earlier.

			// don't create trees and shrubs if this is one and we have that option off
			if( thingTemplate->isKindOf( KINDOF_SHRUBBERY ) && !useTrees )
				continue;

			Coord3D pos = *pMapObj->getLocation();
			pos.z += TheTerrainLogic->getGroundHeight( pos.x, pos.y );
			Real angle = normalizeAngle(pMapObj->getAngle());
			if (thingTemplate->isKindOf(KINDOF_OPTIMIZED_TREE)) {
				// Opt trees and props just get drawables to tell the client about it, then deleted. jba [6/5/2003]
				// This way there is no logic object to slow down partition manager and core logic stuff.
				
				Drawable *draw = TheThingFactory->newDrawable(thingTemplate, DRAWABLE_STATUS_NONE);
				if (draw) {
					draw->setOrientation(angle);
					draw->setPosition( &pos );

					TheGameClient->destroyDrawable(draw);
				}
				continue;
			}
#if 1
			Bool isProp = thingTemplate->isKindOf(KINDOF_PROP);
			Bool isFluff = false;
			if (thingTemplate->isKindOf(KINDOF_CLEARED_BY_BUILD)) {
				if (thingTemplate->getFenceWidth() == 0.0f) {
					// Objects that are cleared by building, but aren't fences, are fluff. jba. [7/14/2003]
					isFluff = true;
				}
			}
			if (isProp || (isFluff && forceFluffToProp)) {
				TheTerrainVisual->addProp(thingTemplate, &pos, angle);
				continue;
			}
#endif
			
			// Get the team information
			DEBUG_ASSERTCRASH(pMapObj->getProperties()->getType(TheKey_originalOwner) == Dict::DICT_ASCIISTRING, ("unit %s has no original owner specified (obsolete map file)\n",pMapObj->getName().str()));
			AsciiString originalOwner = pMapObj->getProperties()->getAsciiString(TheKey_originalOwner);
			Team *team = ThePlayerList->validateTeam(originalOwner);

			// create new object in the world
			Object *obj = TheThingFactory->newObject( thingTemplate, team ); //, OBJECT_STATUS_LOADING_FROM_MAP );
			if( obj )
			{
				if(pMapObj->getFlag(FLAG_DRAWS_IN_MIRROR) || obj->isKindOf(KINDOF_CAN_CAST_REFLECTIONS)) 
				{
					Drawable* draw = obj->getDrawable();
					if(draw)
						draw->setDrawableStatus( DRAWABLE_STATUS_DRAWS_IN_MIRROR );
				}
				obj->setOrientation(angle);
				obj->setPosition( &pos );

				// Do this after positioning the object, because we may place objects in response to keys
				// in the map object properties.
				// update this object instance with properties from the map object
				obj->updateObjValuesFromMapProperties( pMapObj->getProperties() );

				PathfindLayerEnum	layer = TheTerrainLogic->getLayerForDestination(&pos);
				obj->setLayer(layer);
				// Now onCreates were called at the constructor.  This magically created
				// thing needs to be considered as Built for Game specific stuff.
				for (BehaviorModule** m = obj->getBehaviorModules(); *m; ++m)
				{
					CreateModuleInterface* create = (*m)->getCreate();
					if (!create)
						continue;
					create->onBuildComplete();
				}

				// Since the team now has members, activate it.
				team->setActive();
				TheAI->pathfinder()->addObjectToPathfindMap( obj );

			}  // end if
		
			if(timeGetTime() > timer + 500)
			{
				if(progressCount < LOAD_PROGRESS_MAX_ALL_THE_FREAKN_OBJECTS)
					progressCount ++;
				updateLoadProgress(progressCount);
				timer = timeGetTime();
			}

		}	// for, loading map objects

	}  // end if, not loading save game

	#ifdef DUMP_PERF_STATS
	GetPrecisionTimer(&endTime64);
	sprintf(Buf,"After loading objects=%f\n",((double)(endTime64-startTime64)/(double)(freq64)*1000.0));
	DEBUG_LOG(("%s", Buf));
	#endif

	progressCount = LOAD_PROGRESS_LOOP_INITIAL_NETWORK_BUILDINGS;
	// place initial network buildings/units
	if (game && !loadingSaveGame)
	{
		for (int i=0; i<MAX_SLOTS; ++i)
		{
			GameSlot *slot = game->getSlot(i);
			
			if (!slot || !slot->isOccupied())
				continue;
			
			AsciiString playerName;
			playerName.format("player%d", i);
			Player *player = ThePlayerList->findPlayerWithNameKey(TheNameKeyGenerator->nameToKey(playerName));
			
			if (slot->getPlayerTemplate() == PLAYERTEMPLATE_OBSERVER)
			{
				slot->setPlayerTemplate(0);
				const PlayerTemplate *pt = ThePlayerTemplateStore->findPlayerTemplate( TheNameKeyGenerator->nameToKey("FactionObserver") );
				if (pt)
				{
					for (Int j=0; j<ThePlayerTemplateStore->getPlayerTemplateCount(); ++j)
					{
						if (pt == ThePlayerTemplateStore->getNthPlayerTemplate(j))
						{
							slot->setPlayerTemplate(j);
							break;
						}
					}
				}
				DEBUG_LOG(("Setting observer's playerTemplate to %d in slot %d\n", slot->getPlayerTemplate(), i));
			}
			else
			{
				const PlayerTemplate* pt = NULL;
				pt = ThePlayerTemplateStore->getNthPlayerTemplate(slot->getPlayerTemplate());

				// Prevent from loading the disabled Generals, in case your game peer hacked their GUI.
				// The game will start, but the cheater will be instantly defeated because he has no troops.
				// This is also enforced at GUI setup (GUIUtil.cpp and UserPreferences.cpp).
				// @todo: unlock these when something rad happens
				Bool disallowLockedGenerals = TRUE;
				const GeneralPersona *general = TheChallengeGenerals->getGeneralByTemplateName(pt->getName());
				Bool startsLocked = general ? !general->isStartingEnabled() : FALSE;
				if (disallowLockedGenerals && startsLocked)
					continue;

				// prevent from loading disallowed templates, in case your peer hacked their GUI.
        

        // So that the global flag for restricting factions to "OLD" is applied only in the appropriate context!
        // Trouble was that skirmish games would get no command centers upon start, if this was set true in a GameSpyMenu
        if ( isInInternetGame() )
        {
				  if ( game->oldFactionsOnly() && !pt->isOldFaction() )
				    continue;
        }




				placeNetworkBuildingsForPlayer(i, slot, player, pt);
			}

			updateLoadProgress(progressCount++);

		}
	}
	// update the loadscreen 
	updateLoadProgress(LOAD_PROGRESS_POST_INITIAL_NETWORK_BUILDINGS);

	//
	// tell the client to pre-load some assets that we will use such as faction things we
	// will build and various damage states for all the structures on the map so that we
	// don't have big pauses when building those objects or switching to those states
	//
	if( TheGlobalData->m_preloadAssets )
	{
		if (TheGlobalData->m_preloadEverything)
		{
			for (Int td = TIME_OF_DAY_FIRST; td < TIME_OF_DAY_COUNT; ++td)
				TheGameClient->preloadAssets((TimeOfDay)td);
		}
		else
		{
			TheGameClient->preloadAssets( TheGlobalData->m_timeOfDay );
		}
	}

	//put this here somewhat randomly.
	TheControlBar->hideCommunicator( FALSE );

	// update the loadscreen 
	updateLoadProgress(LOAD_PROGRESS_POST_PRELOAD_ASSETS);

	TheTacticalView->setAngleAndPitchToDefault();
	TheTacticalView->setZoomToDefault();

	if( TheRecorder )
		TheRecorder->initControls();

	// Note - WorldBuilderDoc.cpp also uses initial camera position, so if changed, update both.  jba
	// Note - We construct the multiplayer start spot name manually here, so change this if you
	//        change TheKey_Player_1_Start etc.  mdc
	AsciiString startingCamName = TheNameKeyGenerator->keyToName(TheKey_InitialCameraPosition);
	if (game)
	{
		GameSlot *slot = game->getSlot(localSlot);
		DEBUG_ASSERTCRASH(slot, ("Starting a LAN game without ourselves!"));
		
		if (slot->isHuman())
		{
			Int startPos = slot->getStartPos();
			startingCamName.format("Player_%d_Start", startPos+1); // start pos waypoints are 1-based
			DEBUG_LOG(("Using %s as the multiplayer initial camera position\n", startingCamName.str()));
		}
	}

	// update the loadscreen 
	updateLoadProgress(LOAD_PROGRESS_POST_STARTING_CAMERA);

	Waypoint *way = findNamedWaypoint(startingCamName);
	if (way)
	{
		Coord3D pos = *way->getLocation();
		TheTacticalView->lookAt( &pos );
	}
	else
	{
		// Just look somewhere.  lookAt does some terrain specific setup, so it is good
		// to call it.  jba
		Coord3D pos;
		pos.x = 50;
		pos.y = 50;
		pos.z = 0;
		TheTacticalView->lookAt( &pos );
		DEBUG_LOG(("Failed to find initial camera position waypoint %s\n", startingCamName.str()));
	}

	// Set up the camera height based on the map height & globalData.
	TheTacticalView->initHeightForMap();
	TheTacticalView->setAngleAndPitchToDefault();
	TheTacticalView->setZoomToDefault();

	// update the loadscreen 
	updateLoadProgress(LOAD_PROGRESS_POST_STARTING_CAMERA_2);

	// update partition info - We need to do the initial update so that it can be queried
	// during the first frame.  jba.
	ThePartitionManager->update();

	#ifdef DUMP_PERF_STATS
	GetPrecisionTimer(&endTime64);
	sprintf(Buf,"After partition manager update=%f\n",((double)(endTime64-startTime64)/(double)(freq64)*1000.0));
	DEBUG_LOG(("%s", Buf));
	#endif


	// final step, run newMap for all players
	if( loadingSaveGame == FALSE )
		ThePlayerList->newMap();

	if ( isChallengeCampaign )
	{
		// Establish local player relationships with other teams as
		// they're set up for "ThePlayer" in challenge mode map.
		// Some designers have used "ThePlayer" as an empty player for Generals' 
		// Challenge maps which they reference in script and by which they set 
		// player relationships.  The skirmish local player will receive these
		// relationships.  If there is no "ThePlayer", we have to assume that
		// all players on the map are the local player's enemy, except neutral
		// and civilian players.
		Player *localPlayer = ThePlayerList->getLocalPlayer();
		DEBUG_ASSERTCRASH(localPlayer, ("Local player has not been established for Challenge map."));
		Player *placeholderThePlayer = ThePlayerList->findPlayerWithNameKey(NAMEKEY("ThePlayer"));
		DEBUG_ASSERTCRASH(placeholderThePlayer, ("Challenge maps without player \"ThePlayer\" assume that the local player is mutual enemies with all other players except the neutral and civilian players."));

		if (placeholderThePlayer)
		{
			PlayerMaskType thePlayerEnemysMask = ThePlayerList->getPlayersWithRelationship(placeholderThePlayer->getPlayerIndex(), ALLOW_ENEMIES);
			while( thePlayerEnemysMask )
			{
				// set local player enemy relationships based on "ThePlayer" enemy relationships
				Player *thePlayerEnemy = ThePlayerList->getEachPlayerFromMask(thePlayerEnemysMask);
				thePlayerEnemy->setPlayerRelationship(localPlayer, ENEMIES);
				localPlayer->setPlayerRelationship(thePlayerEnemy, ENEMIES);
			}
		} 
		else 
		{
			// This map has no "ThePlayer" by which to model the local player's 
			// relationships, so make assumptions.
			for (Int i = 0; i < ThePlayerList->getPlayerCount(); i++)
			{
				Relationship rel = NEUTRAL;
				Player *thatPlayer = ThePlayerList->getNthPlayer(i);
				if (thatPlayer == localPlayer)
				{
					rel = ALLIES;
				} 
				else if (thatPlayer != ThePlayerList->getNeutralPlayer()
					&& thatPlayer != ThePlayerList->findPlayerWithNameKey(NAMEKEY("PlyrCivilian")))
				{
					rel = ENEMIES;
				} 
				thatPlayer->setPlayerRelationship(localPlayer, rel);
				localPlayer->setPlayerRelationship(thatPlayer, rel);
			}
		}
	}


	// reset all the skill points in a single player game
	if(loadingSaveGame == FALSE && isInSinglePlayerGame())
	{
		for (Int i=0; i<MAX_PLAYER_COUNT; ++i)
		{
			Player *pPlayer = ThePlayerList->getNthPlayer(i);
			if (pPlayer && pPlayer->getPlayerType() != PLAYER_HUMAN)
				pPlayer = NULL;

			if (pPlayer)
			{
				pPlayer->addSkillPoints(m_rankPointsToAddAtGameStart);
				DEBUG_LOG(("GameLogic::startNewGame() - adding m_rankPointsToAddAtGameStart==%d to player %d(%ls)\n",
					m_rankPointsToAddAtGameStart, i, pPlayer->getPlayerDisplayName().str()));
			}
		}
	}

	updateLoadProgress(LOAD_PROGRESS_END);

	if(isInMultiplayerGame() && TheNetwork)
	{
		TheNetwork->loadProgressComplete();
		TheNetwork->liteupdate();
	}

	while(!isProgressComplete())
	{
		updateLoadProgress(101); // keep greater then 100
		testTimeOut();
		Sleep(100);
	}

	// if we're in a load game, don't fade yet
	if( loadingSaveGame == FALSE )
	{
		TheTransitionHandler->setGroup("FadeWholeScreen");
		while(!TheTransitionHandler->isFinished())
		{
			TheWindowManager->update();
			if(!TheTransitionHandler->isFinished())
			{
				TheDisplay->draw();
				setFPMode();
				Sleep(33);
			}
			
		}
	}

	if(m_loadScreen)
	{
		TheMouse->setVisibility(TRUE);

/*
		//
		// delete load screen only when not loading a save game, for save games we still
		// have more work to do and the load screen will be deleted elsewhere after
		// we're all done with the load game progress
		//
		if( loadingSaveGame == FALSE )
*/
			deleteLoadScreen();

	}
	
	#ifdef DUMP_PERF_STATS
	GetPrecisionTimer(&endTime64);
	sprintf(Buf,"After delete load screen=%f\n",((double)(endTime64-startTime64)/(double)(freq64)*1000.0));
	DEBUG_LOG(("%s", Buf));
	#endif

	if(m_gameMode == GAME_SHELL)
	{
		if(TheShell->getScreenCount() == 0)
			TheShell->push( AsciiString("Menus/MainMenu.wnd") );
		else if (TheShell->top())
		{
			TheShell->top()->hide(FALSE);
			TheShell->top()->bringForward();
		}
		HideControlBar();
	}
	else
	{
		
//		TheShell->hideShell();
		if(TheStatsCollector)
		{
			TheStatsCollector->reset();
		}
		else if( TheGlobalData->m_playStats > 0)
		{
			TheStatsCollector = NEW StatsCollector;
			TheStatsCollector->reset();
		}

///		ShowControlBar(FALSE);

		// explicitly set the Control bar to Observer Mode
		if(m_gameMode == GAME_REPLAY )
		{
			
			ThePlayerList->setLocalPlayer(ThePlayerList->findPlayerWithNameKey(TheNameKeyGenerator->nameToKey("ReplayObserver")));
			TheRadar->forceOn(TRUE);
			ThePartitionManager->refreshShroudForLocalPlayer();
			TheControlBar->setControlBarSchemeByPlayer( ThePlayerList->getLocalPlayer());
			DEBUG_LOG(("Start of a replay game %ls, %d\n",ThePlayerList->getLocalPlayer()->getPlayerDisplayName().str(), ThePlayerList->getLocalPlayer()->getPlayerIndex()));
		}
		else
			TheControlBar->setControlBarSchemeByPlayer(ThePlayerList->getLocalPlayer());
//		ShowControlBar();
		
	}
	TheTacticalView->setOkToAdjustHeight(TRUE);

// If defined, the game times various units.
#ifdef DO_UNIT_TIMINGS
	g_UT_curThing = TheThingFactory->firstTemplate();
	g_UT_startTiming = true;
	g_UT_gotUnit = false;
	g_UT_timingLog = fopen("TimingLog.txt", "w");	 
	g_UT_commaLog = fopen("TimingCDL.txt", "w");
	fputs("Full,100*ms,NoPart-NoSpawn,,No Spawn,100*ms,Logic,100*ms,Thing,Model,Kind,Side,DrawCalls All,DrawCalls NoPart-NoSpawn,DrawCalls NoSpawn\n", g_UT_commaLog); 

	// Turn off shadows
	TheWritableGlobalData->m_useShadowVolumes = false;
	TheWritableGlobalData->m_debugIgnoreAsserts = TRUE;

	// Just look somewhere.  lookAt does some terrain specific setup, so it is good
	// to call it.  jba
	Coord3D thePos;
	thePos.x = 50;
	thePos.y = 50;
	thePos.z = 0;
		TheTacticalView->lookAt( &thePos );
#endif


	// @todo remove this hack
//	TheGlobalData->m_inGame = TRUE;

	// If we are now starting a multiplayer or skirmish game, let us set the local players selectionto be the command center
	// We'll ask the Recorder, so we survive replays
	if( TheRecorder->isMultiplayer() )
	{
		// Iterate through each player's objects, and ask if the object 
		//is a command center, and if so, select it for that player
		for (Int i = 0; i < MAX_PLAYER_COUNT; ++i) 
		{
			Player *player = ThePlayerList->getNthPlayer(i);
			if (player && player->isPlayerActive())
			{
				// we need to iterate their objects, and select the first Command Center we find
				Bool alreadyFound = FALSE;
				player->iterateObjects(findAndSelectCommandCenter, &alreadyFound);
			}
		}
	}
	TheControlBar->initSpecialPowershortcutBar(ThePlayerList->getLocalPlayer());

	if(m_gameMode == GAME_SHELL)
	{
		HideControlBar();
	}
	else
	{
		ShowControlBar(FALSE);
	}

#ifdef DO_UNIT_TIMINGS
	// Turn off the UI
	HideControlBar();
#endif
	TheWritableGlobalData->m_loadScreenRender = FALSE;	///< mark to resume rendering as normal
	
	// if we're in a gamespy game, mark us as playing
	if (TheGameSpyBuddyMessageQueue && TheGameSpyGame && isInInternetGame())
	{
		BuddyRequest req;
		req.buddyRequestType = BuddyRequest::BUDDYREQUEST_SETSTATUS;
		req.arg.status.status = GP_PLAYING;
		strcpy(req.arg.status.statusString, "Playing");
		sprintf(req.arg.status.locationString, "%s", WideCharStringToMultiByte(TheGameSpyGame->getGameName().str()).c_str());
		TheGameSpyBuddyMessageQueue->addRequest(req);
	}	
	
  if( loadingSaveGame == FALSE )
  {
    // Drawables need to do some work on level start; give them a chance to do it
    Drawable * drawable = TheGameClient->getDrawableList();
  
    while ( drawable != NULL )
    {
      drawable->onLevelStart();
      drawable = drawable->getNextDrawable();
    }
  }
  
	//Added By Sadullah Nader
	//Added to fix the quit menu 
	//ReAllows quit menu to work during loading scene
	//setGameLoading(FALSE);
	setLoadingMap( FALSE );

#ifdef DUMP_PERF_STATS
	GetPrecisionTimer(&endTime64);
	sprintf(Buf,"Total startnewgame=%f\n",((double)(endTime64-startTime64)/(double)(freq64)*1000.0));
	DEBUG_LOG(("%s", Buf));
#endif

	//Assume that getting this far means we've successfully entered an online game.
	//Add an additional disconnection to player stats in case he doesn't complete this game. -MW
	if (TheGameSpyInfo)
		TheGameSpyInfo->updateAdditionalGameSpyDisconnections(1);

  
  if ( isInReplayGame() && TheInGameUI && TheGameText )
  {
		TheInGameUI->message( TheGameText->fetch( "GUI:FastForwardInstructions" ) );
  }


}  // end startNewGame

//-----------------------------------------------------------------------------------------
static void findAndSelectCommandCenter(Object *obj, void* alreadyFound)
{
	if (!((*(Bool*)alreadyFound)) && obj && obj->isKindOf(KINDOF_COMMANDCENTER) )
	{
		((*(Bool*)alreadyFound)) = TRUE;
		TheGameLogic->selectObject(obj, TRUE, obj->getControllingPlayer()->getPlayerMask(), obj->isLocallyControlled());

	}
}
//-----------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------





// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
void GameLogic::loadMapINI( AsciiString mapName )
{

	if (!TheMapCache) {
		// Need the map cache to get the map and user map directories.
		return;
	}

	char filename[_MAX_PATH];
	char fullFledgeFilename[_MAX_PATH];

	memset(filename, 0, _MAX_PATH);
	strcpy(filename, mapName.str());

	//
	// if map name begins with a "SAVE_DIRECTORY\", then the map refers to a map
	// that has been extracted from a save game file ... in that case we need to get
	// the pristine map name string in order to manipulate and load the right map.ini
	// for that map from it's original location
	//
	if (TheGameState->isInSaveDirectory(filename))
		strcpy( filename, TheGameState->getSaveGameInfo()->pristineMapName.str() );

	// sanity
	int length = strlen(filename);
	if (length < 4) { 
		return;
	}

	// back up over the ".map" extension and to the first directory separator
	char *extension = filename + length - 4;
	while ((extension > filename) && (*extension != '\\') && (*extension != '/')) {
		--extension;
	}
	*extension = 0;


	sprintf(fullFledgeFilename, "%s\\map.ini", filename);
	if (TheFileSystem->doesFileExist(fullFledgeFilename)) {
		DEBUG_LOG(("Loading map.ini\n"));
		INI ini;
		ini.load( AsciiString(fullFledgeFilename), INI_LOAD_CREATE_OVERRIDES, NULL );
	}

	sprintf(fullFledgeFilename, "%s\\solo.ini", filename);
	if (TheFileSystem->doesFileExist(fullFledgeFilename)) {
		DEBUG_LOG(("Loading solo.ini\n"));
		INI ini;
		ini.load( AsciiString(fullFledgeFilename), INI_LOAD_CREATE_OVERRIDES, NULL );
	}
	
	// No error here. There could've just *not* been a map.ini file.

	// now look for a string file
	sprintf(fullFledgeFilename, "%s\\map.str", filename);

	if (TheFileSystem->doesFileExist(fullFledgeFilename)) {
		TheGameText->initMapStringFile(fullFledgeFilename);
	}

	// we want to do this before doing the actual map load!
	if (TheDisplay)
	{
		const char* ASSET_USAGE_FILE_NAME = "AssetUsage.txt";
		sprintf(fullFledgeFilename, "%s\\%s", filename, ASSET_USAGE_FILE_NAME);
		// note: call this EVEN IF THE FILE IN QUESTION DOES NOT EXIST.
		TheDisplay->doSmartAssetPurgeAndPreload(fullFledgeFilename);
	}

}

// ------------------------------------------------------------------------------------------------
/** Process the destroy list, destroying all pending objects.
 * The destroy list exists to ensure that all objects have a chance to
 * see each other at each simulation frame - the object list is the
 * same at the start of the update as it is at the end of the update. */
// ------------------------------------------------------------------------------------------------
//DECLARE_PERF_TIMER(processDestroyList)
void GameLogic::processDestroyList( void )
{
	//USE_PERF_TIMER(processDestroyList)

	for( ObjectPointerListIterator iterator = m_objectsToDestroy.begin(); iterator != m_objectsToDestroy.end(); iterator++ )
	{
		Object* currentObject = (*iterator);

#ifdef ALLOW_NONSLEEPY_UPDATES
		for (std::list<UpdateModulePtr>::iterator it = m_normalUpdates.begin(); it != m_normalUpdates.end(); /* nothing */)
		{
			if ((*it)->friend_getObject() == currentObject)
			{
				it = m_normalUpdates.erase(it);
			}
			else
			{
				++it;
			}
		}
#endif

		/*
			this looks odd, but is necessary; since erasing a single entry can shuffle others in the list
			(in order to maintain its heap-ness), we must do two passes: one to find the updates for this
			object, another to actually erase 'em. 
			
			(in case you're wondering: yes, this is still more efficient than just deleting them
			and rebalancing the entire heap afterwards, at least for real-world maps, since an individual
			rebalance is O(log N) and a full rebalance is O(N)... so unless you are deleting the majority
			of the objects in the world every frame, we come out well ahead this way.)
		*/

		const Int MAX_SUO = 256;
		UpdateModulePtr sleepyUpdatesForThisObject[MAX_SUO];
		Int numSUO = 0;

		for (std::vector<UpdateModulePtr>::iterator it2 = m_sleepyUpdates.begin(); it2 != m_sleepyUpdates.end(); ++it2)
		{
			UpdateModulePtr u = *it2;
			if (u->friend_getObject() == currentObject && numSUO < MAX_SUO)
			{
				sleepyUpdatesForThisObject[numSUO++] = u;
			}
		}

		for (--numSUO; numSUO >= 0; --numSUO)
		{
			// have to re-get idx each time since each call to erase might change others.
			Int idx = sleepyUpdatesForThisObject[numSUO]->friend_getIndexInLogic();
			DEBUG_ASSERTCRASH(m_sleepyUpdates[idx] == sleepyUpdatesForThisObject[numSUO], ("Hmm, expected update mismatch here"));
			eraseSleepyUpdate(idx);
			DEBUG_ASSERTCRASH(sleepyUpdatesForThisObject[numSUO]->friend_getIndexInLogic() == -1, ("Hmm, expected index to be -1 here"));
		}


		currentObject->removeFromList(&m_objList);//remove from object list

		// remove object from lookup table
		removeObjectFromLookupTable( currentObject );

		currentObject->friend_deleteInstance();//actual delete
	}

	m_objectsToDestroy.clear();//list full of bad pointers now, clear it.  If anyone's deletion resulted
	//in the request for a new deletion (sub-object), the new object was added to the end of this list.
}

//-------------------------------------------------------------------------------------------------
/** Process the command list passed to the logic from the network */
//-------------------------------------------------------------------------------------------------
void GameLogic::processCommandList( CommandList *list )
{
	m_cachedCRCs.clear();
	m_shouldValidateCRCs = FALSE;

	GameMessage* msg;

	for( msg = list->getFirstMessage(); msg; msg = msg->next() )
	{
#ifdef _DEBUG
		DEBUG_ASSERTCRASH(msg != NULL && msg != (GameMessage*)0xdeadbeef, ("bad msg"));
#endif
		logicMessageDispatcher( msg, NULL );
	}

	if (m_shouldValidateCRCs && !TheNetwork->sawCRCMismatch())
	{
		Bool sawCRCMismatch = FALSE;
		Int numPlayers = 0;
		DEBUG_ASSERTCRASH(TheNetwork, ("No Network!"));
		if (TheNetwork)
		{
			for (Int i=0; i<MAX_SLOTS; ++i)
			{
				if (TheNetwork->isPlayerConnected(i))
					++numPlayers;
			}

			if (m_cachedCRCs.size() < numPlayers)
			{
				DEBUG_CRASH(("Not enough CRCs!"));
				sawCRCMismatch = TRUE;
			}
			else
			{
				//DEBUG_LOG(("Comparing %d CRCs on frame %d\n", m_cachedCRCs.size(), m_frame));
				std::map<Int, UnsignedInt>::const_iterator crcIt = m_cachedCRCs.begin();
				Int validatorCRC = crcIt->second;
				//DEBUG_LOG(("Validator CRC from player %d is %8.8X\n", crcIt->first, validatorCRC));
				while (++crcIt != m_cachedCRCs.end())
				{
					Int validatedCRC = crcIt->second;
					//DEBUG_LOG(("CRC to validate is from player %d: %8.8X\n", crcIt->first, validatedCRC));
					if (validatorCRC != validatedCRC)
					{
						DEBUG_CRASH(("CRC mismatch!"));
						sawCRCMismatch = TRUE;
					}
				}
			}
		}

		if (sawCRCMismatch)
		{
#ifdef DEBUG_LOGGING
			DEBUG_LOG(("CRC Mismatch - saw %d CRCs from %d players\n", m_cachedCRCs.size(), numPlayers));
			for (std::map<Int, UnsignedInt>::const_iterator crcIt = m_cachedCRCs.begin(); crcIt != m_cachedCRCs.end(); ++crcIt)
			{
				Player *player = ThePlayerList->getNthPlayer(crcIt->first);
				DEBUG_LOG(("CRC from player %d (%ls) = %X\n", crcIt->first,
					player?player->getPlayerDisplayName().str():L"<NONE>", crcIt->second));
			}
#endif DEBUG_LOGGING
			TheNetwork->setSawCRCMismatch();
		}
	}

}  // end processCommandList

// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
Bool GameLogic::isIntroMoviePlaying()
{
	/// @todo remove this hack
	return m_startNewGame && TheDisplay->isMoviePlaying();
}

// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
void GameLogic::selectObject(Object *obj, Bool createNewSelection, PlayerMaskType playerMask, Bool affectClient)
{
	if (!obj) 
	{
		return;
	}

	if (!obj->isMassSelectable() && !createNewSelection) 
	{
		DEBUG_LOG(("GameLogic::selectObject() - Object attempted to be added to selection, but isn't mass-selectable.\n"));
		return;
	}

	while( playerMask ) 
	{
		Player *player = ThePlayerList->getEachPlayerFromMask(playerMask);
		if( !player ) 
		{
			return;
		}

		AIGroup *group = NULL;
		CRCGEN_LOG(( "Creating AIGroup in GameLogic::selectObject()\n" ));
		group = TheAI->createGroup();
		group->add(obj);

		// add all selected agents to the AI group
		if (createNewSelection)	
		{
			player->setCurrentlySelectedAIGroup(group);
		} 
		else 
		{
			player->addAIGroupToCurrentSelection(group);
		}

		TheAI->destroyGroup(group);

		if( affectClient ) 
		{
			Drawable *draw = obj->getDrawable();
			if( draw ) 
			{
				TheInGameUI->selectDrawable(draw);
			}
		}
	}
}

// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
void GameLogic::deselectObject(Object *obj, PlayerMaskType playerMask, Bool affectClient)
{
	if (!obj) {
		return;
	}

	while (playerMask) {
		Player *player = ThePlayerList->getEachPlayerFromMask(playerMask);
		if (!player) {
			return;
		}

		AIGroup *group = NULL;
		CRCGEN_LOG(( "Removing a unit from a selected group in GameLogic::deselectObject()\n" ));
		group = TheAI->createGroup();
		player->getCurrentSelectionAsAIGroup(group);
		
		Bool deleted = FALSE;
		Bool actuallyRemoved = FALSE;
		
		if (group) {
			deleted = group->remove(obj);
			actuallyRemoved = TRUE;
		}
		
		if (actuallyRemoved) {
			// Set this to be the currently selected group.
			if (!deleted) {
				player->setCurrentlySelectedAIGroup(group);
				// Then, cleanup the group.
				TheAI->destroyGroup(group);
			} else {
				// NULL will clear the group.
				player->setCurrentlySelectedAIGroup(NULL);
			}

			if (affectClient) {
				Drawable *draw = obj->getDrawable();
				if (draw) {
					TheInGameUI->deselectDrawable(draw);
				}
			}
		}
	}
}

// ------------------------------------------------------------------------------------------------
inline void GameLogic::validateSleepyUpdate() const
{
// pretty slow, so do only for DEBUG for now. turn on if you suspect wonkiness.
#ifdef _DEBUG
	#define SLEEPY_DEBUG
#endif
#ifdef SLEEPY_DEBUG
	int sz = m_sleepyUpdates.size();
	if (sz == 0)
		return;

	int i;
	//DEBUG_LOG(("\n\n"));
	//for (i = 0; i < sz; ++i)
	//{
	//	DEBUG_LOG(("u %04d: %08lx %08lx\n",i,m_sleepyUpdates[i],m_sleepyUpdates[i]->friend_getNextCallFrame()));
	//}
	for (i = 0; i < sz; ++i)
	{
		DEBUG_ASSERTCRASH(m_sleepyUpdates[i]->friend_getIndexInLogic() == i, ("index mismatch: expected %d, got %d\n",i,m_sleepyUpdates[i]->friend_getIndexInLogic()));
		UnsignedInt pri = m_sleepyUpdates[i]->friend_getPriority();
		if (i > 0)
		{
			Int i0 = (i+1)/2-1;
			UnsignedInt pri0 = m_sleepyUpdates[i0]->friend_getPriority();
			DEBUG_ASSERTCRASH(pri >= pri0, ("sleepyUpdates are munged (0)"));
		}
		Int i1 = 2*(i+1)-1;
		Int i2 = 2*(i+1);
		if (i1 < sz)
		{
			UnsignedInt pri1 = m_sleepyUpdates[i1]->friend_getPriority();
			DEBUG_ASSERTCRASH(pri <= pri1, ("sleepyUpdates are munged (1)"));
		}
		if (i2 < sz)
		{
			UnsignedInt pri2 = m_sleepyUpdates[i2]->friend_getPriority();
			DEBUG_ASSERTCRASH(pri <= pri2, ("sleepyUpdates are munged (2)"));
		}
	}
#endif
}

// ------------------------------------------------------------------------------------------------
void GameLogic::eraseSleepyUpdate(Int i)
{
	USE_PERF_TIMER(SleepyMaintenance)

	DEBUG_ASSERTCRASH(i >= 0 && i < m_sleepyUpdates.size(), ("bad sleepy idx"));

	// swap with the final item, toss the final item, then rebalance
	m_sleepyUpdates[i]->friend_setIndexInLogic(-1);

	Int final = m_sleepyUpdates.size() - 1;
	if (i < final)
	{
		m_sleepyUpdates[i] = m_sleepyUpdates[final];
		m_sleepyUpdates[i]->friend_setIndexInLogic(i);
		m_sleepyUpdates.pop_back();
		rebalanceSleepyUpdate(i);
	}
	else
	{
		m_sleepyUpdates.pop_back();
	}
}

// ------------------------------------------------------------------------------------------------
inline Bool isLowerPriority(const UpdateModulePtr a, const UpdateModulePtr b)
{
	// return true iff a is lower pri than b.
	// remember: lower ordinal value means higher priority.
	// therefore, higher ordinal value means lower priority.
	DEBUG_ASSERTCRASH(a && b, ("these may no longer be null"));
	UnsignedInt f1 = a->friend_getPriority();
	UnsignedInt f2 = b->friend_getPriority();
	return f1 > f2;
}

// ------------------------------------------------------------------------------------------------
Int GameLogic::rebalanceParentSleepyUpdate(Int i)
{
	USE_PERF_TIMER(SleepyMaintenance)

	DEBUG_ASSERTCRASH(i >= 0 && i < m_sleepyUpdates.size(), ("bad sleepy idx"));

	Int parent = ((i+1)>>1)-1;
	while (parent >= 0 && isLowerPriority(m_sleepyUpdates[parent], m_sleepyUpdates[i]))
	{
		UpdateModulePtr a = m_sleepyUpdates[parent];
		UpdateModulePtr b = m_sleepyUpdates[i];

		m_sleepyUpdates[i] = a;
		m_sleepyUpdates[parent] = b;

		a->friend_setIndexInLogic(i);
		b->friend_setIndexInLogic(parent);

		i = parent;
		parent = ((parent+1)>>1)-1;
	}

	return i;
}

// ------------------------------------------------------------------------------------------------
Int GameLogic::rebalanceChildSleepyUpdate(Int i)
{
	USE_PERF_TIMER(SleepyMaintenance)

	DEBUG_ASSERTCRASH(i >= 0 && i < m_sleepyUpdates.size(), ("bad sleepy idx"));

// this function gets the brunt of the work (we frequently
// balance down, not up), so this one is hand-unrolled for
// max efficiency. I have left the pristine non-unrolled
// version present for clarity. (Yes, this is worth doing.) (srj) 
#if 1
	UpdateModulePtr* pI = &m_sleepyUpdates[i];

	// our children are i*2 and i*2+1
  Int child = ((i+1)<<1)-1;
	UpdateModulePtr* pChild = &m_sleepyUpdates[child];
	UpdateModulePtr* pSZ = &m_sleepyUpdates[m_sleepyUpdates.size()];	// yes, this is off the end.

  while (pChild < pSZ) 
	{
		// choose the higher-priority of the two children; we must be higher-pri than that.
		if (pChild < pSZ-1 && isLowerPriority(*pChild, *(pChild+1)))
		{
      ++pChild;
			++child;
		}

		// if we're higher-pri than our children, we're done.
		if (!isLowerPriority(*pI, *pChild))
		{
			break;
		}

		// doh. swap with the highest-pri child we have.
		UpdateModulePtr a = *pChild;
		UpdateModulePtr b = *pI;

		*pI = a;
		*pChild = b;

		a->friend_setIndexInLogic(i);
		b->friend_setIndexInLogic(child);

		i = child;
		pI = pChild;

		child = ((i+1)<<1)-1;
		pChild = &m_sleepyUpdates[child];
  }
#else
	// our children are i*2 and i*2+1
	Int sz = m_sleepyUpdates.size();
  Int child = ((i+1)<<1)-1;
  while (child < sz) 
	{
		// choose the higher-priority of the two children; we must be higher-pri than that.
		if (child < sz-1 && isLowerPriority(m_sleepyUpdates[child], m_sleepyUpdates[child+1]))
      ++child;
		
		// if we're higher-pri than our children, we're done.
		if (!isLowerPriority(m_sleepyUpdates[i], m_sleepyUpdates[child]))
		{
			break;
		}

		// doh. swap with the highest-pri child we have.
		UpdateModulePtr a = m_sleepyUpdates[child];
		UpdateModulePtr b = m_sleepyUpdates[i];

		m_sleepyUpdates[i] = a;
		m_sleepyUpdates[child] = b;

		a->friend_setIndexInLogic(i);
		b->friend_setIndexInLogic(child);
		i = child;
		child = ((i+1)<<1)-1;
  }
#endif
	return i;
}

// ------------------------------------------------------------------------------------------------
void GameLogic::rebalanceSleepyUpdate(Int i)
{
	USE_PERF_TIMER(SleepyMaintenance)

	i = rebalanceParentSleepyUpdate(i);
	i = rebalanceChildSleepyUpdate(i);
}

// ------------------------------------------------------------------------------------------------
void GameLogic::remakeSleepyUpdate()
{
	USE_PERF_TIMER(SleepyMaintenance)

	Int parent = m_sleepyUpdates.size() / 2;
  while (true) 
	{
    rebalanceChildSleepyUpdate(parent);
    if (parent == 0)
			break;
    --parent;
  }

	validateSleepyUpdate();
}

// ------------------------------------------------------------------------------------------------
void GameLogic::pushSleepyUpdate(UpdateModulePtr u)
{
	USE_PERF_TIMER(SleepyMaintenance)

	DEBUG_ASSERTCRASH(u != NULL, ("You may not pass null for sleepy update info"));

	m_sleepyUpdates.push_back(u);
	u->friend_setIndexInLogic(m_sleepyUpdates.size() - 1);
	
	rebalanceParentSleepyUpdate(m_sleepyUpdates.size()-1);
}

// ------------------------------------------------------------------------------------------------
UpdateModulePtr GameLogic::peekSleepyUpdate() const
{
	USE_PERF_TIMER(SleepyMaintenance)

	UpdateModulePtr u = m_sleepyUpdates.front();
	DEBUG_ASSERTCRASH(u->friend_getIndexInLogic() == 0, ("index mismatch: expected %d, got %d\n",0,u->friend_getIndexInLogic()));
	return u;
}

// ------------------------------------------------------------------------------------------------
void GameLogic::popSleepyUpdate()
{
	USE_PERF_TIMER(SleepyMaintenance)

	Int sz = m_sleepyUpdates.size();
	if (sz == 0)
	{
		DEBUG_CRASH(("should not happen"));
		return;
	}

	m_sleepyUpdates[0]->friend_setIndexInLogic(-1);
	if (sz > 1)
	{
		m_sleepyUpdates[0] = m_sleepyUpdates[sz-1];
		m_sleepyUpdates[0]->friend_setIndexInLogic(0);
		m_sleepyUpdates.pop_back();
		rebalanceChildSleepyUpdate(0);
	}
	else
	{
		m_sleepyUpdates.pop_back();
	}
}

// ------------------------------------------------------------------------------------------------
// this should be called only by UpdateModule, thanks.
// ------------------------------------------------------------------------------------------------
//DECLARE_PERF_TIMER(friend_awakenUpdateModule)
void GameLogic::friend_awakenUpdateModule(Object* obj, UpdateModulePtr u, UnsignedInt whenToWakeUp)
{
	//USE_PERF_TIMER(friend_awakenUpdateModule)
	UnsignedInt now = TheGameLogic->getFrame();
	DEBUG_ASSERTCRASH(whenToWakeUp >= now, ("setWakeFrame frame is in the past... are you sure this is what you want?"));

	if (u == m_curUpdateModule)
	{
		DEBUG_CRASH(("You should not call setWakeFrame() from inside your update(), because it will be ignored, in favor of the return code from update.\n"));
		return;
	}

	if (whenToWakeUp == u->friend_getNextCallFrame())
		return;	// my, that was easy

	if ((now > 0) && (u->friend_getNextCallFrame() == now) && (whenToWakeUp == now + 1))
	{
		// subtle but important case: if we already awake, and someone calls 
		// setWakeFrame(self, UPDATE_SLEEP_NONE), we don't want to reset our wake frame,
		// since that would prevent us from getting called THIS frame. since UPDATE_SLEEP_NONE
		// really means "wake up as soon as possible", we don't want to change our status
		// if we are already awake. (srj)
		return;
	}

	Int idx = u->friend_getIndexInLogic();
	if (obj->isInList(&m_objList))
	{
		if (idx < 0 || idx >= m_sleepyUpdates.size())
		{
			RELEASE_CRASH("fatal error! sleepy update module illegal index.\n");
			return;
		}

		if (m_sleepyUpdates[idx] != u)
		{
			RELEASE_CRASH("fatal error! sleepy update module index mismatch.\n");
			return;
		}

		// update the value.
		u->friend_setNextCallFrame(whenToWakeUp);

		// rebalance.
		rebalanceSleepyUpdate(idx);
		
		// validate. (harmless except in debug mode)
		validateSleepyUpdate();

		return;
	}
	else
	{
		if (idx != -1)
		{
			RELEASE_CRASH("fatal error! sleepy update module index mismatch.\n");
			return;
		}

		// this can happen if stuff happens during object initialization. fortunately, 
		// it's easy to deal with:
		u->friend_setNextCallFrame(whenToWakeUp);
		return;
	}
}







// ------------------------------------------------------------------------------------------------
#ifdef DO_UNIT_TIMINGS

void drawGraph( const char* style, Real scale, double value )
{
  for ( Int t = 0; t < value*scale; ++t)
  {
    DEBUG_LOG((style));
    if ( t%200 == 199 )
      DEBUG_LOG(("...\n"));
  }

  DEBUG_LOG(("\n"));

}


	enum {TIME_FRAMES=20};
	enum {SETTLE_FRAMES=10};
static void unitTimings(void)
{
	static Int settleFrames = 0;
	static Int timeFrames = 0;
	enum { INFANTRY, VEHICLE, STRUCTURE, OTHER, END}; 
	static Int unitTypes = INFANTRY;
	AsciiString sides[16];


	const Int UNIT_X = 10;
	const Int UNIT_Y = 10;
	const Int TOTAL_UNITS = UNIT_X*UNIT_Y;


	const Int UNIT_SPACING = 30;
	const Int UNIT_BORDER = 30;


	Int sideCount = 0;
  #define no_SINGLE_UNIT "AmericaInfantryRanger"

#define DO_FACTION
#ifdef DO_FACTION
	sides[sideCount++] = "America";
	sides[sideCount++] = "China";
	sides[sideCount++] = "GLA";

  //The MissionDisk new 'sides'
	sides[sideCount++] = "AmericaSuperWeaponGeneral";
	sides[sideCount++] = "AmericaLaserGeneral";
	sides[sideCount++] = "AmericaAirForceGeneral";
	sides[sideCount++] = "ChinaTankGeneral";
	sides[sideCount++] = "ChinaInfantryGeneral";
	sides[sideCount++] = "ChinaNukeGeneral";
	sides[sideCount++] = "GLAToxinGeneral";
	sides[sideCount++] = "GLADemolitionGeneral";
	sides[sideCount++] = "GLAStealthGeneral";

//  sides[sideCount++] = "*"; // wildcard for unspecified side


#endif

#define DO_CIVILIAN
#ifdef DO_CIVILIAN
	sides[sideCount++] = "Civilian";
#endif

#define DO_SINGLE_KIND
#ifdef DO_SINGLE_KIND
	static KindOfType goalKind = KINDOF_STRUCTURE;
	//static KindOfType goalKind = KINDOF_INFANTRY;
	//static KindOfType goalKind = KINDOF_VEHICLE;
#endif

	sides[sideCount] = "";
	static bool veryFirstTime = true;


	static Int side = 0;

	static __int64 startTime64;
	static __int64 endTime64,freq64;
	static int drawCallTotal;
	static enum { LOGIC, NO_PARTICLES, NO_SPAWN, ALL} mode;
	static double timeAll, timeAllNoAnim, timeNoPart, timeNoSpawn, timeLogic, timeLogicNoAnim;
	static float drawCallAll,drawCallNoPart,drawCallNoSpawn,drawCallLogic;
	
	if (settleFrames>0) {
		settleFrames--;
		if (settleFrames>0) return;

		QueryPerformanceCounter((LARGE_INTEGER *)&startTime64);
		QueryPerformanceFrequency((LARGE_INTEGER *)&freq64);
		timeFrames = TIME_FRAMES;

		// reset the draw counter
		drawCallTotal = 0;
		return;
	}
	if (timeFrames>0) {
		drawCallTotal += TheDisplay->getLastFrameDrawCalls();
		timeFrames--;
		if (timeFrames>0) return;
		
		QueryPerformanceCounter((LARGE_INTEGER *)&endTime64);
		double timeToUpdate = ((double)(endTime64-startTime64) / (double)(freq64));

//		Real timeToUpdateMicrosec = timeToUpdate*1E6/(TIME_FRAMES * TOTAL_UNITS);
		timeToUpdate *= 100.0f/TIME_FRAMES;


		if (mode == LOGIC) {
			timeLogic = timeToUpdate;
			drawCallLogic = (float)drawCallTotal / (float)(TIME_FRAMES * TOTAL_UNITS);  // 100 units for TIME_FRAMES

			mode = ALL;
			settleFrames = SETTLE_FRAMES;
			//g_timing_no_anim = true;
			Coord3D thePos;
			thePos.x = 50;
			thePos.y = 50;
			thePos.z = 0;
			TheTacticalView->lookAt( &thePos );
			return;
		}
		if (mode == ALL) {
			timeAll = timeToUpdate;
			drawCallAll = (float)drawCallTotal / (float)(TIME_FRAMES * TOTAL_UNITS);  // 100 units for TIME_FRAMES

			mode = NO_PARTICLES; 
			settleFrames = SETTLE_FRAMES;
			if (TheParticleSystemManager->getParticleCount()>1) {
				TheParticleSystemManager->reset();
				DEBUG_LOG(("Starting noParticles - \n"));
			}
			return;
		} 
		if (mode == NO_PARTICLES) {
			timeNoPart = timeToUpdate;
			drawCallNoPart = (float)drawCallTotal / (float)(TIME_FRAMES * TOTAL_UNITS);  // 100 units for TIME_FRAMES

			mode = NO_SPAWN;
			Object *obj = TheGameLogic->getFirstObject();
			Bool gotSpawn;
			while (obj) {
				if (obj->getTemplate() != g_UT_curThing) {
					TheGameLogic->destroyObject(obj);
					gotSpawn = true;
				}
				obj = obj->getNextObject();
			}	
			if (gotSpawn) {
				DEBUG_LOG(("Starting noSpawn - \n"));
				settleFrames = SETTLE_FRAMES;
				return;
			}
		}	
		if (mode==NO_SPAWN) {
			timeNoSpawn = timeToUpdate;
			drawCallNoSpawn = (float)drawCallTotal / (float)(TIME_FRAMES * TOTAL_UNITS);  // 100 units for TIME_FRAMES
		}
		if (g_UT_curThing==NULL) return;


		char remark[2048];
    Real graphScale = 20.0f;
		AsciiString thingName = g_UT_curThing->getName();
		if (veryFirstTime) {
			thingName = "No Object";
		}

    sprintf(remark, "All %f: (%d ms) for %d %s's\n", timeAll, (Int)(timeAll*1000/TIME_FRAMES), TOTAL_UNITS, thingName.str() );
    DEBUG_LOG((remark));
    drawGraph( "@", graphScale, timeAll );

		sprintf(remark, "Without Particles %f\n", timeNoPart);
    DEBUG_LOG((remark));
    drawGraph( "@", graphScale, timeNoPart );
        
 		sprintf(remark, "Without Spawn %f  \n", timeNoSpawn );
    DEBUG_LOG((remark));
    drawGraph( "@", graphScale, timeNoSpawn );

 		sprintf(remark, "Logic %f \n", timeLogic);
    DEBUG_LOG((remark));
    drawGraph( "@", graphScale, timeLogic );
    
    
		sprintf(remark, "DrawCalls for %s \n", thingName.str() ) ;
		DEBUG_LOG((remark));

		sprintf(remark, "All %f\n", drawCallAll );
		DEBUG_LOG((remark));
    drawGraph( "#", graphScale, drawCallAll );

		sprintf(remark, "Without Particles %f\n", drawCallNoPart );
		DEBUG_LOG((remark));
    drawGraph( "#", graphScale, drawCallNoPart );

		sprintf(remark, "Without Spawn %f \n", drawCallNoSpawn );
		DEBUG_LOG((remark));
    drawGraph( "#", graphScale, drawCallNoSpawn );

		sprintf(remark, "Draw Call Logic %f \n", drawCallLogic );
		DEBUG_LOG((remark));
    drawGraph( "#", graphScale, drawCallLogic );


		if (g_UT_timingLog) {
			fputs(remark, g_UT_timingLog);
		}
		if (g_UT_commaLog) {
			AsciiString type;
			if (unitTypes == INFANTRY) {
				type="Infantry";
			}	else if (unitTypes == VEHICLE) {
				type="Vehicle";
			}	else if (unitTypes == STRUCTURE) {
				type="Structure";
			}	else {
				type="Other";
			}						 
			AsciiString modelName;
			ModelConditionFlags state;
			state.clear();
			const ModuleInfo& mi = g_UT_curThing->getDrawModuleInfo();
			if (mi.getCount() > 0)
			{
				const ModuleData* mdd = mi.getNthData(0);
				const W3DModelDrawModuleData* md = mdd ? mdd->getAsW3DModelDrawModuleData() : NULL;
				if (md)
				{
					modelName = md->getBestModelNameForWB(state);
				}
			}
			if (veryFirstTime) {
				modelName = "**NO MODEL**";
				veryFirstTime = false;
			}
			sprintf(remark, "%f,%d,%f,%d,%f,%d,%f,%d,%s,%s,%s,%s,%f,%f,%f\n", timeAll, 
			(Int)(timeAll*1000/TIME_FRAMES),timeNoPart, 
			(Int)(timeNoPart*1000/TIME_FRAMES),timeNoSpawn, 
			(Int)(timeNoSpawn*1000/TIME_FRAMES),timeLogic, 
			(Int)(timeLogic*1000/TIME_FRAMES), thingName.str(), modelName.str(), type.str(),
			sides[side].str(),
			drawCallAll,drawCallNoPart,drawCallNoSpawn);
			fputs(remark, g_UT_commaLog);
		}
		TheParticleSystemManager->reset();
		g_UT_gotUnit = false;
	}

#ifdef SINGLE_UNIT
	if (g_UT_curThing && g_UT_startTiming) {
		if (g_UT_curThing->getName()==SINGLE_UNIT) {
			if (g_UT_timingLog) {
				fclose(g_UT_timingLog);
				g_UT_timingLog = NULL;
			}
			if (g_UT_commaLog) {
				fclose(g_UT_commaLog);
				g_UT_commaLog = NULL;
			}
			return;
		}
		while (g_UT_curThing->friend_getNextTemplate() 
			&& g_UT_curThing->friend_getNextTemplate()->getName()!=SINGLE_UNIT) 
			g_UT_curThing = g_UT_curThing->friend_getNextTemplate();
		
	}
#endif

	Object *obj = TheGameLogic->getFirstObject();
	while (obj) {
		TheGameLogic->destroyObject(obj);
		obj = obj->getNextObject();
	}												

	if (g_UT_startTiming && g_UT_curThing && !g_UT_gotUnit) {
		TheWritableGlobalData->m_framesPerSecondLimit = 10000;
		TheWritableGlobalData->m_useFpsLimit = false;
		while (!g_UT_gotUnit) {
			if (veryFirstTime) {
				g_UT_gotUnit = true;
				break;
			}
			g_UT_curThing = g_UT_curThing->friend_getNextTemplate();

			if (g_UT_curThing == NULL) {
				unitTypes++;
				if (unitTypes==END) {
					side++;
					unitTypes = INFANTRY;
					if (sides[side].isEmpty() ) // end of sides list
          {
						g_UT_startTiming = false;
						if (g_UT_timingLog) 
            {
							fclose(g_UT_timingLog);
							g_UT_timingLog = NULL;
						}
						if (g_UT_commaLog) 
            {
							fclose(g_UT_commaLog);
							g_UT_commaLog = NULL;
						}
						break;
					}
				}
				g_UT_curThing = TheThingFactory->firstTemplate();
			}

			const ThingTemplate* btt = g_UT_curThing;

#ifndef SINGLE_UNIT
      Bool unspecified = FALSE;
			if (btt->getDefaultOwningSide() != sides[side]) 
      {
        if (sides[side] == "*")
        {
          if ( btt->getDefaultOwningSide() == ("") )// wildcard for unspecified side
            unspecified = TRUE;
          else
            continue;
        }
        else
    		  continue;

			}

			if (unitTypes == INFANTRY) {
				if (!btt->isKindOf(KINDOF_INFANTRY)) continue;
			}	else if (unitTypes == VEHICLE) {
				if (!btt->isKindOf(KINDOF_VEHICLE)) continue;
			}	else if (unitTypes == STRUCTURE) {
				if (!btt->isKindOf(KINDOF_STRUCTURE)) continue;
			}	else {
				if (btt->isKindOf(KINDOF_INFANTRY)) continue;
				if (btt->isKindOf(KINDOF_VEHICLE)) continue;
				if (btt->isKindOf(KINDOF_STRUCTURE)) continue;
			}
#endif
#ifdef DO_SINGLE_KIND
			if (!btt->isKindOf(goalKind)) continue;
#endif


      static char *illegalTemplateNames[] = 
      {
	      "EMPPulseBomb",
	      "GLAAngryMobRockProjectileObject",
	      "ClusterMinesBomb",
	      "BlackNapalmFirestormSmall",
	      "CabooseFullOfTerrorists",
	      "GLAAngryMobMolotovCocktailProjectileObject",
	      "Firestorm",
	      "Avalanche",
	      "InfernoTankShell",
	      "ChinaArtilleryBarrageShell",
	      "ChinaTankOverlordBattleBunker",
	      "ChinaTankOverlordPropagandaTower",
	      "ChinaTankOverlordGattlingCannon",
	      "CINE",
	      "GLAInfantryAngryMobNexus",
	      "AircraftCarrier",
        "GermanMuseum",
        "Cin_",
        "Amb_",
        "Ambient",
        "GC_",
		"SpecialEffectsTrainCrashObject",
	      NULL
      };

      Bool skip = FALSE;

      for ( Int test = 0; test < sizeof( illegalTemplateNames ) ; ++test )
      {
        if ( illegalTemplateNames[test] == NULL )
          break;

        if (btt->getName().startsWith(illegalTemplateNames[test])) 
        {
          skip = TRUE;
          break;
        }
        if (btt->getName().endsWith(illegalTemplateNames[test])) 
        {
          skip = TRUE;
          break;
        }
        if (btt->getName() == illegalTemplateNames[test] ) 
        {
          skip = TRUE;
          break;
        }
      }

      if ( skip ) 
        continue;

//			if (btt->getName() endsWith("EMPPulseBomb")) continue; // 100 overloads system.
//			if (btt->getName() endsWith("GLAAngryMobRockProjectileObject")) continue; // 100 overloads system.
//			if (btt->getName() endsWith("ClusterMinesBomb")) continue; // 100 overloads system.
//			if (btt->getName() endsWith("BlackNapalmFirestormSmall")) continue; // 100 overloads system.
//			if (btt->getName() endsWith("CabooseFullOfTerrorists")) continue; // 100 overloads system.
//			if (btt->getName() endsWith("GLAAngryMobMolotovCocktailProjectileObject")) continue; // 100 overloads system.
//			if (btt->getName().startsWith("Firestorm"))	continue;	// 100 crashes
//			if (btt->getName().startsWith("Avalanche"))	continue;	// 100 crashes
//			if (btt->getName().startsWith("InfernoTankShell"))	continue;	// 100 crashes
//			
//			if (btt->getName() endsWith("ChinaArtilleryBarrageShell") continue; // 100 takes really, freaking long. Doesn't crash jba.
//			if (btt->getName() endsWith("ChinaTankOverlordBattleBunker") continue; // 100 seems to hang gth.
//			if (btt->getName() endsWith("ChinaTankOverlordPropagandaTower") continue; // 100 seems to hang gth.
//			if (btt->getName() endsWith("ChinaTankOverlordGattlingCannon") continue; // 100 seems to hang gth.
//			if (btt->getName().startsWith("CINE")) continue;
//			if (btt->getName() endsWith("GLAInfantryAngryMobNexus") continue;
//
//      //missiondisk perps
//      
//      if (btt->getName() == "AmericaAircraftCarrier") continue;



#ifdef SINGLE_UNIT
			if (btt->getName()!=SINGLE_UNIT) {
				DEBUG_LOG(("Skipping %s\n", btt->getName().str()));
				continue;
			}
#endif
			DEBUG_LOG(("\n===Doing thing %s ===\n", btt->getName().str()));


#define dont_DO_ATTACK
#ifdef DO_ATTACK
			Coord3D attackedPos;
			attackedPos.x = UNIT_SPACING * 4.5 + UNIT_BORDER;
			attackedPos.y = UNIT_SPACING * 4.5 + UNIT_BORDER;
			attackedPos.z = TheTerrainLogic->getGroundHeight( attackedPos.x, attackedPos.y );

#endif

			Int i, j;
			for (i=0; i<10; i++) {
				for (j=0; j<10; j++) {
					Coord3D pos;
					pos.x = UNIT_SPACING*i+UNIT_BORDER;
					pos.y = UNIT_SPACING*j+UNIT_BORDER;
					pos.z = TheTerrainLogic->getGroundHeight( pos.x, pos.y );
					Team *team = ThePlayerList->getNthPlayer(1)->getDefaultTeam();
					Object *obj = TheThingFactory->newObject( btt, team );
					if (obj==NULL) break;
#define dont_DO_TREE	1
#ifdef DO_TREE
					TheGameLogic->destroyObject(obj);
					externalAddTree(pos, 1.0f, 0.0f, "TreeOakFall1");
					g_UT_gotUnit = true;
#else
					if (obj)
					{
						g_UT_gotUnit = true;
					
						obj->setOrientation(0);	
						obj->setPosition( &pos );
						
						// Now onCreates were called at the constructor.  This magically created
						// thing needs to be considered as Built for Game specific stuff.
						for (BehaviorModule** m = obj->getBehaviorModules(); *m; ++m)
						{
							CreateModuleInterface* create = (*m)->getCreate();
							if (!create)
								continue;
							create->onBuildComplete();
						}
						// Since the team now has members, activate it.
						team->setActive();
						TheAI->pathfinder()->addObjectToPathfindMap(obj);
#ifdef DO_ATTACK
						AIUpdateInterface *myAI = obj->getAI();
						if(myAI)
						{
							myAI->aiAttackPosition(&attackedPos, 9999, CMD_FROM_AI);
						}
#endif
					}
#endif 
				}
			}
		}
		if (g_UT_gotUnit) {
			settleFrames = TIME_FRAMES/2;
			Coord3D thePos;
			thePos.x = 5000;
			thePos.y = 50;
			thePos.z = 0;
			TheTacticalView->lookAt( &thePos );
			mode = LOGIC;
			return;
		}
	}
}
#endif

DECLARE_PERF_TIMER(GameLogic_update)
DECLARE_PERF_TIMER(GameLogic_update_normal)
DECLARE_PERF_TIMER(GameLogic_update_sleepy)

#ifdef DUMP_PERF_STATS
extern __int64 Total_Get_Texture_Time;
extern __int64 Total_Get_HAnim_Time;
extern __int64 Total_Create_Render_Obj_Time;
extern __int64 Total_Load_3D_Assets;
#endif

// ------------------------------------------------------------------------------------------------
/** Update all objects in the world by invoking their update() methods. */
// ------------------------------------------------------------------------------------------------
void GameLogic::update( void )
{
	USE_PERF_TIMER(GameLogic_update)

	LatchRestore<Bool> inUpdateLatch(m_isInUpdate, TRUE);
#ifdef DO_UNIT_TIMINGS
	unitTimings();
#endif

	setFPMode();
	
	/// @todo remove this hack
	if ( m_startNewGame && !TheDisplay->isMoviePlaying())
	{
	#ifdef DUMP_PERF_STATS
		Total_Get_Texture_Time=0;
		Total_Get_HAnim_Time=0;
		Total_Create_Render_Obj_Time=0;
		Total_Load_3D_Assets=0;
	#endif

#ifdef _PROFILE
    Profile::StartRange("map_load");
#endif
		startNewGame( FALSE );
#ifdef _PROFILE
    Profile::StopRange("map_load");
#endif
		m_startNewGame = FALSE;

	#ifdef DUMP_PERF_STATS
		char Buf[1024];
		__int64 freq64;
		GetPrecisionTimerTicksPerSec(&freq64);

		sprintf(Buf,"Texture=%f, Anim=%f, CreateRobj=%f, Load3DAssets=%f\n",
			((double)Total_Get_Texture_Time/(double)(freq64)*1000.0),
			((double)Total_Get_HAnim_Time/(double)(freq64)*1000.0),
			((double)Total_Create_Render_Obj_Time/(double)(freq64)*1000.0),
			((double)Total_Load_3D_Assets/(double)(freq64)*1000.0));

	DEBUG_LOG(("%s", Buf));
	#endif
	}

	// send the current time to the GameClient
	DEBUG_ASSERTCRASH(TheGameLogic == this, ("hmm, TheGameLogic is not right"));
	UnsignedInt now = TheGameLogic->getFrame();
	TheGameClient->setFrame(now);

	// update (execute) scripts
	{
		TheScriptEngine->UPDATE();
	}

	Bool freezeTime = TheTacticalView->isTimeFrozen() && !TheTacticalView->isCameraMovementFinished();
	freezeTime = freezeTime || TheScriptEngine->isTimeFrozenDebug() || TheScriptEngine->isTimeFrozenScript();
	
	if (freezeTime) 
	{
		if (TheCommandList->containsMessageOfType(GameMessage::MSG_CLEAR_GAME_DATA)) 
		{
			TheScriptEngine->forceUnfreezeTime();
		} 
		else 
		{
			/// @todo - make sure this never happens during a network game.  jba.
			return;
		}
	}

	// Note - TerrainLogic update needs to happen after ScriptEngine update, but before object updates.  jba.
	// This way changes in bridges are noted in the script engine before being cleared in TerrainLogic->update
	{
		TheTerrainLogic->UPDATE();
	}

	// force CRC calculation, so we can keep a cache of the last N CRCs.  We do this right where the recorder
	// would be getting the CRC anyway, so replays can get the CRCs from the exact instant in time as the original.
	Bool isMPGameOrReplay = (TheRecorder && TheRecorder->isMultiplayer() && getGameMode() != GAME_SHELL && getGameMode() != GAME_NONE);
	Bool isSoloGameOrReplay = (TheRecorder && !TheRecorder->isMultiplayer() && getGameMode() != GAME_SHELL && getGameMode() != GAME_NONE);
	Bool generateForMP = (isMPGameOrReplay && (m_frame % TheGameInfo->getCRCInterval()) == 0);
#if defined(_DEBUG) || defined(_INTERNAL)
	Bool generateForSolo = isSoloGameOrReplay && ((m_frame && (m_frame%100 == 0)) ||
		(getFrame() > TheCRCFirstFrameToLog && getFrame() < TheCRCLastFrameToLog && ((m_frame % REPLAY_CRC_INTERVAL) == 0)));
#else
	Bool generateForSolo = isSoloGameOrReplay && ((m_frame % REPLAY_CRC_INTERVAL) == 0);
#endif // defined(_DEBUG) || defined(_INTERNAL)

	if (generateForSolo || generateForMP)
	{
		m_CRC = getCRC( CRC_RECALC );
		if (isMPGameOrReplay)
		{
			GameMessage *msg = TheMessageStream->appendMessage( GameMessage::MSG_LOGIC_CRC );
			msg->appendIntegerArgument( m_CRC );
			msg->appendBooleanArgument( (TheRecorder && TheRecorder->getMode() == RECORDERMODETYPE_PLAYBACK) ); // playback CRC
			//DEBUG_LOG(("Appended CRC of %8.8X on frame %d\n", m_CRC, m_frame));
		}
		else
		{
			GameMessage *msg = TheMessageStream->appendMessage( GameMessage::MSG_LOGIC_CRC );
			msg->appendIntegerArgument( m_CRC );
			msg->appendBooleanArgument( (TheRecorder && TheRecorder->getMode() == RECORDERMODETYPE_PLAYBACK) ); // playback CRC
			//DEBUG_LOG(("Appended Playback CRC of %8.8X on frame %d\n", m_CRC, m_frame));
		}
	}

	// collect stats
	if(TheStatsCollector)
	{
		TheStatsCollector->update();
	}

	// Update the Recorder
	{
		TheRecorder->UPDATE();
	}

	// process client commands
	{
		processCommandList( TheCommandList );
	}

#ifdef ALLOW_NONSLEEPY_UPDATES
	{
		for (std::list<UpdateModulePtr>::const_iterator it = m_normalUpdates.begin(); it != m_normalUpdates.end(); ++it)
		{
			UpdateModulePtr u = *it;
			DisabledMaskType dis = u->friend_getObject()->getDisabledFlags();
			if (!dis.any() || dis.anyIntersectionWith(u->getDisabledTypesToProcess()))
			{
				USE_PERF_TIMER(GameLogic_update_normal)

				m_curUpdateModule = u;

				#ifdef DEBUG_LOGGING
					UpdateSleepTime sleep = u->update();
					DEBUG_ASSERTCRASH(sleep == UPDATE_SLEEP_NONE, ("you must return SLEEPNONE from all nonsleepy modules"));
				#else
					u->update();
				#endif

				m_curUpdateModule = NULL;
			}
		}
	}
#endif

	{
		while (!m_sleepyUpdates.empty())
		{
			UpdateModulePtr u = peekSleepyUpdate();

			if (!u)
			{
				DEBUG_CRASH(("Null update. should not happen."));
				continue;
			}

			// we're done, everyone else is sleeping. 
			// break from the loop BEFORE we pop this item off.
			if (u->friend_getNextCallFrame() > now)
			{
				break;
			}

			UpdateSleepTime sleepLen = UPDATE_SLEEP_NONE;	// default, if it is disabled.

			DisabledMaskType dis = u->friend_getObject()->getDisabledFlags();
			if (!dis.any() || dis.anyIntersectionWith(u->getDisabledTypesToProcess()))
			{
				USE_PERF_TIMER(GameLogic_update_sleepy)

				//DEBUG_LOG(("calling update %08lx (%d %d)... ",update,update->friend_getNextCallFrame(),update->friend_getNextCallPhase()));
				m_curUpdateModule = u;

				sleepLen = u->update();
				DEBUG_ASSERTCRASH(sleepLen > 0, ("you may not return 0 from update"));
				if (sleepLen < 1) 
					sleepLen = UPDATE_SLEEP_NONE;

				m_curUpdateModule = NULL;

			}

			// else defer it till next frame and re-push it
			u->friend_setNextCallFrame(now + sleepLen);
			rebalanceSleepyUpdate(0);
		}
	}

	validateSleepyUpdate();

	// update the Artificial Intelligence system
	{
		TheAI->UPDATE();
	}

	// production updates
	{
		TheBuildAssistant->UPDATE();
	}

	// update partition info
	{
		ThePartitionManager->UPDATE();
	}

	//
	// End of frame clean-up
	//

	// destroy all pending objects
	processDestroyList();

	// reset the command list, destroying all messages
	TheCommandList->reset();

	TheWeaponStore->UPDATE();	
	TheLocomotorStore->UPDATE();	
	TheVictoryConditions->UPDATE();

#ifdef DO_COPY_PROTECTION
	if (!isInShellGame() && isInGame())
	{
		if ((m_frame == 1024) && !CopyProtect::validate())
		{
			DEBUG_LOG(("Copy protection failed - bailing"));
			GameMessage *msg = TheMessageStream->appendMessage(GameMessage::MSG_SELF_DESTRUCT);
			msg->appendBooleanArgument(FALSE);
		}
	}
#endif

	{
		//Handle disabled statii (and re-enable objects once frame matches)
		for( Object *obj = m_objList; obj; obj = obj->getNextObject() )
		{
			if( obj->isDisabled() )
			{
				obj->checkDisabledStatus();
			}
		}
	}

  



	// increment world time
	if (!m_startNewGame)
	{
		m_frame++;
	}
}

// ------------------------------------------------------------------------------------------------
/** Return the first object in the world list */
// ------------------------------------------------------------------------------------------------
Object *GameLogic::getFirstObject( void )
{
	return m_objList;
}

// ------------------------------------------------------------------------------------------------
/** Return a new unique object id. */
// ------------------------------------------------------------------------------------------------
ObjectID GameLogic::allocateObjectID( void )
{
	/// @todo Find unused value in current object set
	ObjectID ret = m_nextObjID;
	m_nextObjID = (ObjectID)((UnsignedInt)m_nextObjID + 1);
	return ret;
}

// ------------------------------------------------------------------------------------------------
/** Add object ID to the lookup table */
// ------------------------------------------------------------------------------------------------
void GameLogic::addObjectToLookupTable( Object *obj )
{

	// sanity
	if( obj == NULL )
		return;

	// add to lookup
//	m_objHash[ obj->getID() ] = obj;
	ObjectID newID = obj->getID();
	while( newID >= m_objVector.size() ) // Fail case is hella rare, so faster to double up on size() call
		m_objVector.resize(m_objVector.size() * 2, NULL);

	m_objVector[ newID ] = obj;

}  // end addObjectToLookupTable

// ------------------------------------------------------------------------------------------------
/** Remove object from the ID lookup table */
// ------------------------------------------------------------------------------------------------
void GameLogic::removeObjectFromLookupTable( Object *obj )
{

	// sanity
	if( obj == NULL )
		return;

	// remove from lookup table
//	m_objHash.erase( obj->getID() );
	m_objVector[ obj->getID() ] = NULL;

}  // end removeObjectFromLookupTable

// ------------------------------------------------------------------------------------------------
/** Given an object, register it with the GameLogic and give it a unique ID. */
// ------------------------------------------------------------------------------------------------
void GameLogic::registerObject( Object *obj )
{

	// add the object to the global list
	obj->prependToList(&m_objList);

	// add object to lookup table
	addObjectToLookupTable( obj );

	UnsignedInt now = TheGameLogic->getFrame();
	if (now == 0)
		now = 1;
	for (BehaviorModule** b = obj->getBehaviorModules(); *b; ++b)
	{
#ifdef DIRECT_UPDATEMODULE_ACCESS
		// evil, but necessary at this point. (srj)
		UpdateModulePtr u = (UpdateModulePtr)((*b)->getUpdate());
#else
		UpdateModulePtr u = (*b)->getUpdate();
#endif
		if (!u)
			continue;

		UnsignedInt when = u->friend_getNextCallFrame();
#ifdef ALLOW_NONSLEEPY_UPDATES
		if (when == 0)
		{
			// zero is the magic value for "never sleeps"
			m_normalUpdates.push_back(u);
		}
		else
#else
		// note that 'when' can be zero here for any update module
		// that didn't bother to call setWakeFrame() in its ctor.
		// this is legal.
		if (when == 0)
			u->friend_setNextCallFrame(now);
#endif
		{
			DEBUG_ASSERTCRASH(u->friend_getNextCallFrame() >= now, ("you may not specify a zero initial sleep time for sleepy modules (%d %d)",u->friend_getNextCallFrame(),now));
			pushSleepyUpdate(u);
		}
	}

}

// ------------------------------------------------------------------------------------------------
/** create an object based on the thing template, the reason that this is
	* here and not in the ThingFactory is so that we can mirror what the
	* creation process is on the client side because clients have specific
	* device dependent drawables (such as a W3DDrawable derived from Drawable).
	* here we will allocate an object of the correct type based on thing
	* template properties
	*
	* if we want to have the thing manager actually contain the pools of
	* object and drawable storage it seems OK to have it be friends with the
	* GameLogic/Client for those purposes, or we could put the allocation pools
	* in the GameLogic and GameClient themselves */
// ------------------------------------------------------------------------------------------------
Object *GameLogic::friend_createObject( const ThingTemplate *thing, const ObjectStatusMaskType &statusBits, Team *team )
{
	Object *obj;

	obj = newInstance(Object)( thing, statusBits, team );

	return obj;
} 

// ------------------------------------------------------------------------------------------------
/** Mark the object as destroyed, and place on list for deletion at the end of the next update.
 * This is the only interface to destroy objects - objects cannot be directly deleted. */
// ------------------------------------------------------------------------------------------------
void GameLogic::destroyObject( Object *obj )
{
	DEBUG_ASSERTCRASH(obj != NULL, ("destroying null object"));

	// if already flagged for destruction, ignore
	if (!obj || obj->isDestroyed())
		return;

	// run the object onDestroy event if provied
	for (BehaviorModule** m = obj->getBehaviorModules(); *m; ++m)
	{
		DestroyModuleInterface* destroy = (*m)->getDestroy();
		if (destroy)
			destroy->onDestroy();
	}

	// mark object as destroyed
	obj->setStatus( MAKE_OBJECT_STATUS_MASK( OBJECT_STATUS_DESTROYED ) );

	// We desperately need to stop here, or else the destructor of the statemachine will try to do
	// stopping logic, which uses virtual functions and deleted modules, which will crash us.
	AIUpdateInterface *ai = obj->getAIUpdateInterface();
	if( ai )
	{
		ai->setLocomotorGoalNone();
		ai->destroyPath();
	}

	// add to end of destruction list, in case something is being destroyed and trying to destroy subobjects
	m_objectsToDestroy.push_back(obj);

	// run any on destroy logic internal to the object
	obj->onDestroy();

	// remove wall pieces from the pathfinder
	if( obj->isKindOf( KINDOF_WALK_ON_TOP_OF_WALL ) )
		TheAI->pathfinder()->removeWallPiece( obj );

	//Clean up special power shortcut bars
	if( obj->hasAnySpecialPower() )
	{
		if( ThePlayerList->getLocalPlayer() == obj->getControllingPlayer() )
		{
			TheControlBar->markUIDirty();
		}
	}


}  // end destroyObject

// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
Bool inCRCGen = FALSE;
UnsignedInt GameLogic::getCRC( Int mode, AsciiString deepCRCFileName )
{
	if (mode != CRC_RECALC)
		return m_CRC;

	setFPMode();

	LatchRestore<Bool> latch(inCRCGen, !isInGameLogicUpdate());

	XferCRC *xferCRC;
	AsciiString marker;
	if (deepCRCFileName.isNotEmpty())
	{
		xferCRC = NEW XferDeepCRC;
		xferCRC->open(deepCRCFileName.str());
	}
	else
	{
		AsciiString crcName;
#ifdef DEBUG_CRC
		if (isInGameLogicUpdate() && g_keepCRCSaves && m_frame < 5)
		{
			xferCRC = NEW XferDeepCRC;
			crcName.format("logicFrame%d.crc", (m_frame%5));
		}
		else
#endif DEBUG_CRC
		{
			xferCRC = NEW XferCRC;
			crcName = "lightCRC";
		}
		xferCRC->open(crcName);
	}

	// calculate CRCs
	Object *obj;
	DEBUG_ASSERTCRASH(this == TheGameLogic, ("Not in GameLogic"));
	if (isInGameLogicUpdate())
	{
		CRCGEN_LOG(("CRC at start of frame %d is 0x%8.8X\n", m_frame, xferCRC->getCRC()));
	}

	marker = "MARKER:Objects";
	xferCRC->xferAsciiString(&marker);
	for( obj = m_objList; obj; obj=obj->getNextObject() )
	{
		xferCRC->xferSnapshot( obj );
	}
	UnsignedInt seed = GetGameLogicRandomSeedCRC();
	if (isInGameLogicUpdate())
	{
		CRCGEN_LOG(("CRC after objects for frame %d is 0x%8.8X\n", m_frame, xferCRC->getCRC()));
	}

	if (isInGameLogicUpdate())
	{
		CRCGEN_LOG(("RandomSeed: %d\n", seed));
	}
	if (xferCRC->getXferMode() == XFER_CRC)
	{
		xferCRC->xferUnsignedInt( &seed );
	}
	marker = "MARKER:ThePartitionManager";
	xferCRC->xferAsciiString(&marker);
	xferCRC->xferSnapshot( ThePartitionManager );
	if (isInGameLogicUpdate())
	{
		CRCGEN_LOG(("CRC after partition manager for frame %d is 0x%8.8X\n", m_frame, xferCRC->getCRC()));
	}

#ifdef DEBUG_CRC
	if ((g_crcModuleDataFromClient && !isInGameLogicUpdate()) ||
		(g_crcModuleDataFromLogic && isInGameLogicUpdate()))
	{
		marker = "MARKER:TheModuleFactory";
		xferCRC->xferAsciiString(&marker);
		xferCRC->xferSnapshot( TheModuleFactory );
		if (isInGameLogicUpdate())
		{
			CRCGEN_LOG(("CRC after module factory for frame %d is 0x%8.8X\n", m_frame, xferCRC->getCRC()));
		}
	}
#endif DEBUG_CRC

	marker = "MARKER:ThePlayerList";
	xferCRC->xferAsciiString(&marker);
	xferCRC->xferSnapshot( ThePlayerList );
	if (isInGameLogicUpdate())
	{
		CRCGEN_LOG(("CRC after PlayerList for frame %d is 0x%8.8X\n", m_frame, xferCRC->getCRC()));
	}

	marker = "MARKER:TheAI";
	xferCRC->xferAsciiString(&marker);
	xferCRC->xferSnapshot( TheAI );
	if (isInGameLogicUpdate())
	{
		CRCGEN_LOG(("CRC after AI for frame %d is 0x%8.8X\n", m_frame, xferCRC->getCRC()));
	}

	if (xferCRC->getXferMode() == XFER_SAVE)
	{
		marker = "MARKER:GameSave";
		xferCRC->xferAsciiString(&marker);
		TheGameState->friend_xferSaveDataForCRC(xferCRC, SNAPSHOT_DEEPCRC_LOGICONLY);
	}

	xferCRC->close();

	UnsignedInt theCRC = xferCRC->getCRC();

	delete xferCRC;
	xferCRC = NULL;

	if (isInGameLogicUpdate())
	{
		CRCGEN_LOG(("CRC for frame %d is 0x%8.8X\n", m_frame, theCRC));
	}
	return theCRC;
}

// ------------------------------------------------------------------------------------------------
/** A new GameLogic object has been constructed, therefore create
 * a corresponding drawable and bind them together. */
// ------------------------------------------------------------------------------------------------
void GameLogic::sendObjectCreated( Object *obj )
{
	Drawable *draw = TheThingFactory->newDrawable(obj->getTemplate());

/// @todo COLIN ... shouldn't we have a check here for existing drawable!!!!!

	// bind drawable to object and object to drawable
	bindObjectAndDrawable(obj, draw);

}

// ------------------------------------------------------------------------------------------------
void GameLogic::bindObjectAndDrawable(Object* obj, Drawable* draw)
{
	draw->friend_bindToObject( obj );
	obj->friend_bindToDrawable( draw );
}

// ------------------------------------------------------------------------------------------------
/** Send notification of object destruction. */
// ------------------------------------------------------------------------------------------------
void GameLogic::sendObjectDestroyed( Object *obj )
{
	// Because this implementation is a bridge between the Logic and Interface,
	// we must take extra care to handle such cases as when the system it
	// shutting down.
	if(TheGameClient == NULL)
		return;

	// destroy the drawable
	Drawable *draw = obj->getDrawable();
	if(draw)
	{
		TheGameClient->destroyDrawable( draw );
	}

	// erase the binding of the drawable to this object
	obj->friend_bindToDrawable( NULL );

}

// ------------------------------------------------------------------------------------------------
/** Return if the game is paused or not */
// ------------------------------------------------------------------------------------------------
Bool GameLogic::isGamePaused( void )
{
	return m_gamePaused;
}

// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
void GameLogic::setGamePaused( Bool paused, Bool pauseMusic )
{
	// We need to ignore an unpause called when we are unpaused or else:
	// Mouse is hidden for some reason (script or something)
	// GamePaused
	// Remember that we were hidden
	// Show mouse
	// GameUnpaused
	// Set mouse the way it was

	// Time passes, mouse is unhidden

	// GameUnpaused
	// Set mouse the way it "was" <--- Was counting on right answer being set in Pause.
	if( paused == m_gamePaused )
		return;

	m_gamePaused = paused; 

	AudioAffect audToAffect = (AudioAffect)(pauseMusic ? AudioAffect_All : (AudioAffect_All & ~AudioAffect_Music));
	
	if(paused)
	{
		// remember the state of the mouse/input so we can return to the same state once we "unpause"
		m_inputEnabledMemory = TheInGameUI->getInputEnabled();
		m_mouseVisibleMemory = TheMouse->getVisibility();
		
		// Make sure the mouse is visible and the cursor is an arrow
		TheMouse->setVisibility(TRUE);
		TheMouse->setCursor( Mouse::ARROW );
		
		// if Input is enabled, disable it
		if(m_inputEnabledMemory)
		{
			TheInGameUI->setInputEnabled(FALSE);
		}
		TheAudio->pauseAudio(audToAffect);

#if 0 // Kris added this code some time ago. I'm not sure why -- the pauseAudio should stop the 
      // ambients by itself. Everything seems to work fine without it and it's messing up my 
      // custom ambient code. Hopefully he can explain it to me, but until he gets back, I'm
      // disabling it. -Ian
    
		//Stop all ambient sounds!
		Drawable *drawable = TheGameClient->getDrawableList();
		while( drawable )
		{
			drawable->stopAmbientSound();
			drawable = drawable->getNextDrawable();
		}
#endif
	}
	else
	{
		// set the mouse/input states to what they were before we paused.
		TheMouse->setVisibility(m_mouseVisibleMemory);
		if(m_inputEnabledMemory)
			TheInGameUI->setInputEnabled(TRUE);
		TheAudio->resumeAudio(audToAffect);

#if 0
		//Start all ambient sounds!
		Drawable *drawable = TheGameClient->getDrawableList();
		while( drawable )
		{
			drawable->startAmbientSound();
			TheAudio->stopAllAmbientsBy( drawable );
			drawable = drawable->getNextDrawable();
		}
#endif
	}
}

// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
void GameLogic::processProgress(Int playerId, Int percentage)
{
	if(m_loadScreen)
		m_loadScreen->processProgress(playerId, percentage);
	lastHeardFrom(playerId);
}

// ------------------------------------------------------------------------------------------------
/** Whenever we get a progress complete packet for a Net Game,
	* Set a flag that that player is ready */
// ------------------------------------------------------------------------------------------------
void GameLogic::processProgressComplete(Int playerId)
{
	if(playerId < 0 || playerId >= MAX_SLOTS)
	{
		DEBUG_ASSERTCRASH(FALSE,("GameLogic::processProgressComplete, Invalid playerid was passed in %d\n", playerId));
		return;
	}
	if(m_progressComplete[playerId] == TRUE)
	{
		DEBUG_LOG(("GameLogic::processProgressComplete, playerId %d is marked TRUE already yet we're trying to mark him as true again\n", playerId));
		return;
	}
	DEBUG_LOG(("Progress Complete for Player %d\n", playerId));
	m_progressComplete[playerId] = TRUE;
	lastHeardFrom(playerId);
}

// ------------------------------------------------------------------------------------------------
/// @TODO: Add check to account for timeouts
// ------------------------------------------------------------------------------------------------
Bool GameLogic::isProgressComplete( void )
{
	//If we're not in a network game, always return true
	if(!isInMultiplayerGame() || !TheNetwork || m_forceGameStartByTimeOut)
		return TRUE;

	// Only loop on the Number of players we got in here
	for(Int i =0; i < MAX_SLOTS; ++i)
	{
		if(!m_progressComplete[i])
			return FALSE;
	}
	return TRUE;
}

// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
void GameLogic::lastHeardFrom( Int playerId )
{
	if( playerId < 0 || playerId >= MAX_SLOTS)
		return;
	m_progressCompleteTimeout[playerId] = timeGetTime();
}

// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
void GameLogic::testTimeOut( void )
{
	// if everyone is loaded, lets just load the game like normal.
	if(isProgressComplete())
		return;

	Int curTime = timeGetTime();
	// Loop and test everyone in our game.
	for(Int i =0; i < MAX_SLOTS; ++i)
	{
		// If they've completed their progress, ignore them
		if(m_progressComplete[i])
			continue;
		if(	m_progressCompleteTimeout[i] + PROGRESS_COMPLETE_TIMEOUT > curTime )
			return;
	}
	// if we made it this far, that means everyone has timed out.
	m_forceGameStartByTimeOut = TRUE;
}

// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
void GameLogic::timeOutGameStart( void )
{
	DEBUG_LOG(("We got the Force TimeOut Start Message\n"));
	m_forceGameStartByTimeOut = TRUE;
}

// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
void GameLogic::initTimeOutValues( void )
{
	if (!TheNetwork)
		return;
	for(Int i = 0; i < TheNetwork->getNumPlayers(); ++i)
	{
		m_progressCompleteTimeout[i] = timeGetTime();
	}
}

// ------------------------------------------------------------------------------------------------
/** returns the total number of objects in the world */
// ------------------------------------------------------------------------------------------------
UnsignedInt GameLogic::getObjectCount( void )
{
	UnsignedInt totalObjects = 0;
	Object *obj;
	for( obj = getFirstObject(); obj; obj = obj->getNextObject() )
	{
		++totalObjects;
	}
	return totalObjects;
}

// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
GhostObjectManager *GameLogic::createGhostObjectManager(void)
{ 
	return NEW GhostObjectManager;
}

// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
TerrainLogic *GameLogic::createTerrainLogic( void )
{
	return NEW TerrainLogic;
}

// ------------------------------------------------------------------------------------------------
void GameLogic::setBuildableStatusOverride(const ThingTemplate* tt, BuildableStatus bs)
{
	if (tt)
	{
		m_thingTemplateBuildableOverrides[tt->getName()] = bs;
	}
}

// ------------------------------------------------------------------------------------------------
Bool GameLogic::findBuildableStatusOverride(const ThingTemplate* tt, BuildableStatus& bs) const
{
	if (tt)
	{
		BuildableMap::const_iterator it = m_thingTemplateBuildableOverrides.find(tt->getName());
		if (it != m_thingTemplateBuildableOverrides.end())
		{
			bs = it->second;
			return true;
		}
	}
	return false;
}

// ------------------------------------------------------------------------------------------------
void GameLogic::setControlBarOverride(const AsciiString& commandSetName, Int slot, ConstCommandButtonPtr commandButton)
{
	char buf[256];
	buf[0] = '0' + slot;
	strcpy(&buf[1], commandSetName.str());
	m_controlBarOverrides[buf] = commandButton;
}

// ------------------------------------------------------------------------------------------------
Bool GameLogic::findControlBarOverride(const AsciiString& commandSetName, Int slot, ConstCommandButtonPtr& commandButton) const
{
	char buf[256];
	buf[0] = '0' + slot;
	strcpy(&buf[1], commandSetName.str());

	ControlBarOverrideMap::const_iterator it = m_controlBarOverrides.find(buf);
	if (it != m_controlBarOverrides.end())
	{
		commandButton = it->second;	// could be null.
		return true;
	}

	// leave commandButton unmodified.
	return false;
}

#ifdef DUMP_PERF_STATS
// ------------------------------------------------------------------------------------------------
void GameLogic::getAIMetricsStatistics( UnsignedInt *numAI, UnsignedInt *numMoving, UnsignedInt *numAttacking, UnsignedInt *numWaitingForPath, UnsignedInt *overallFailedPathfinds )
{
	Object *obj;
	*numAI = 0;
	*numMoving = 0;
	*numAttacking = 0;
	*numWaitingForPath = 0;
	for( obj = getFirstObject(); obj; obj = obj->getNextObject() )
	{
		AIUpdateInterface *ai = obj->getAI();
		if( ai )
		{
			(*numAI)++;

			if( ai->isMoving() )
			{
				(*numMoving)++;
			}
			if( ai->isWaitingForPath() )
			{
				(*numWaitingForPath)++;
			}
			if( ai->isAttacking() )
			{
				(*numAttacking)++;
			}
		}
	}
	*overallFailedPathfinds = m_overallFailedPathfinds;
}
#endif

// ------------------------------------------------------------------------------------------------
/** Light CRC */
// ------------------------------------------------------------------------------------------------
void GameLogic::crc( Xfer *xfer )
{

}  // end crc

// ------------------------------------------------------------------------------------------------
/** Given a string name, find the object TOC entry (if any) associated with it */
// ------------------------------------------------------------------------------------------------
GameLogic::ObjectTOCEntry *GameLogic::findTOCEntryByName( AsciiString name )
{

	for( ObjectTOCListIterator it = m_objectTOC.begin(); it != m_objectTOC.end(); ++it )
		if( (*it).name == name )
			return &(*it);

	return NULL;

}  // end findTOCEntryByname

// ------------------------------------------------------------------------------------------------
/** Given a object TOC identifier, find the object TOC if any */
// ------------------------------------------------------------------------------------------------
GameLogic::ObjectTOCEntry *GameLogic::findTOCEntryById( UnsignedShort id )
{

	for( ObjectTOCListIterator it = m_objectTOC.begin(); it != m_objectTOC.end(); ++it )
		if( (*it).id == id )
			return &(*it);

	return NULL;

}  // end findTOCEntryById

// ------------------------------------------------------------------------------------------------
/** Add an object TOC entry */
// ------------------------------------------------------------------------------------------------
void GameLogic::addTOCEntry( AsciiString name, UnsignedShort id )
{

	ObjectTOCEntry tocEntry;
	tocEntry.name = name;
	tocEntry.id = id;
	m_objectTOC.push_back( tocEntry );

}  // end addTOCEntry

// ------------------------------------------------------------------------------------------------
/** Xfer object table of contents */
// ------------------------------------------------------------------------------------------------
void GameLogic::xferObjectTOC( Xfer *xfer )
{

	// version
	const XferVersion currentVersion = 1;
	XferVersion version = currentVersion;
	xfer->xferVersion( &version, currentVersion );

	// clear our current table of contents
	m_objectTOC.clear();

	// xfer the table
	UnsignedInt tocCount = 0;
	if( xfer->getXferMode() == XFER_SAVE )
	{
		AsciiString templateName;

		// generate a new TOC based on the objects that are in the map
		for( Object *obj = getFirstObject(); obj; obj = obj->getNextObject() )
		{

			// get the name we're working with
			templateName = obj->getTemplate()->getName();

			// if is this object name already in the TOC, skip it
			if( findTOCEntryByName( templateName ) != NULL )
				continue;
			
			// add this entry to the TOC
			addTOCEntry( obj->getTemplate()->getName(), ++tocCount );

		}  // end for obj

		// xfer entries in the TOC
		xfer->xferUnsignedInt( &tocCount );

		// xfer each TOC entry
		ObjectTOCListIterator it;
		ObjectTOCEntry *tocEntry;
		for( it = m_objectTOC.begin(); it != m_objectTOC.end(); ++it )
		{

			// get this toc entry
			tocEntry = &(*it);

			// xfer the name
			xfer->xferAsciiString( &tocEntry->name );

			// xfer the paired id
			xfer->xferUnsignedShort( &tocEntry->id );

		}  // end for

	}  // end if
	else
	{
		AsciiString templateName;
		UnsignedShort id;

		// how many entries are we going to read
		xfer->xferUnsignedInt( &tocCount );

		// read all the entries
		for( UnsignedInt i = 0; i < tocCount; ++i )
		{

			// read the name
			xfer->xferAsciiString( &templateName );

			// read the id
			xfer->xferUnsignedShort( &id );
			
			// add this to the TOC
			addTOCEntry( templateName, id );
					
		}  // end for i

	}  // end else

}  // end xferObjectTOC

// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
void GameLogic::prepareLogicForObjectLoad( void )
{

	//
	// this is a band-aid :(
	// when loading from a map file, objects were created for the bridges, towers, walls etc.
	// since we're going to load that data from the save game file we will destroy any of
	// those bridge objects and tower objects so that we can create them from the save data
	//
	Object *obj, *next;
	for( obj = getFirstObject(); obj; obj = next )
	{

		// get next
		next = obj->getNextObject();

		// is this a bridge object?
		if( obj->isKindOf( KINDOF_BRIDGE ) )
		{
			Bridge *bridge = TheTerrainLogic->findBridgeAt( obj->getPosition() );

			// sanity
			DEBUG_ASSERTCRASH( bridge, ("GameLogic::prepareLogicForObjectLoad - Unable to find bridge\n" ));

			// get the old object that is in the bridge info
			const BridgeInfo *bridgeInfo = bridge->peekBridgeInfo();
			Object *oldObject = findObjectByID( bridgeInfo->bridgeObjectID );
			DEBUG_ASSERTCRASH( oldObject, ("GameLogic::prepareLogicForObjectLoad - Unable to find old bridge object\n") );
			DEBUG_ASSERTCRASH( oldObject == obj, ("GameLogic::prepareLogicForObjectLoad - obj != oldObject\n") );

			//
			// destroy the 4 towers that are attached to this old object (they will be loaded from
			// the save game file
			//
			Object *oldTower;
			for( Int i = 0; i < BRIDGE_MAX_TOWERS; ++i )
			{

				oldTower = findObjectByID( bridgeInfo->towerObjectID[ i ] );
				if (oldTower) {
					destroyObject( oldTower );
				}

			}  // end for, i

			// destroy the old bridge object
			destroyObject( oldObject );

		}  // end if, bridge
		else if( obj->isKindOf( KINDOF_WALK_ON_TOP_OF_WALL ) )
		{

			// destroy walk on top of wall things too
			destroyObject( obj );

		}  // end else if

	}  // end for, obj

	// process the destruction of these objects immediately before we proceed with the load process
	processDestroyList();

	// there should be no objects anywhere
	DEBUG_ASSERTCRASH( getFirstObject() == NULL,
										 ("GameLogic::prepareLogicForObjectLoad - There are still objects loaded in the engine, but it should be empty (Top is '%s')\n",
										 getFirstObject()->getTemplate()->getName().str()) );

}  // end prepareLogicForObjectLoad

// ------------------------------------------------------------------------------------------------
/** Load/Save game logic to xfer 
	*	Version Info:
	* 1: Initial version
	* 2: Added m_isScoringEnabled flag (BGC) 
	* 3: Added polygon triggers (CBD)
	* 4: Added block markers around object data, no version checking is done and therefore
	*		 this version breaks compatibility with previous versions. (CBD)
	* 5: Added xfering the BuildAssistant's sell list.
	* 9: Added m_rankPointsToAddAtGameStart, or else on a load game, your RestartGame button will forget your exp
  * 10: xfer m_superweaponRestriction
	*/	
// ------------------------------------------------------------------------------------------------
void GameLogic::xfer( Xfer *xfer )
{
  
	// version
	const XferVersion currentVersion = 10;
	XferVersion version = currentVersion;
	xfer->xferVersion( &version, currentVersion );

	// logic frame number
	xfer->xferUnsignedInt( &m_frame );

	//
	// note that we do not do the id counter here, we did it in the game state block because
	// it's important to do that part very early in the load process
	//
	// !!!DON'T DO THIS!!! ----> xfer->xferObjectID( &m_nextObjectID ); <---- !!!DON'T DO THIS!!! 

	//
	// xfer a table of contents that contain thing template and indentifier pairs.  this
	// table of contents is good for this save file only as unique numbers are only
	// generated and stored for the actual things that are on this map
	//
	xferObjectTOC( xfer );

	// when loading, we need to clean up bridges and get them ready for a load
	if( xfer->getXferMode() == XFER_LOAD )
		prepareLogicForObjectLoad();

	// object count
	UnsignedInt objectCount = getObjectCount();
	xfer->xferUnsignedInt( &objectCount );

	// object data
	Object *obj;
	ObjectTOCEntry *tocEntry;
	if( xfer->getXferMode() == XFER_SAVE )
	{

		for( obj = getFirstObject(); obj; obj = obj->getNextObject() )
		{

			// get the object TOC entry for this template
			tocEntry = findTOCEntryByName( obj->getTemplate()->getName() );
			if( tocEntry == NULL )
			{

				DEBUG_CRASH(( "GameLogic::xfer - Object TOC entry not found for '%s'\n", obj->getTemplate()->getName().str() ));
				throw SC_INVALID_DATA;

			}  // end if

			// transfer TOC id entry
			xfer->xferUnsignedShort( &tocEntry->id );

			// begin a block of data
			xfer->beginBlock();

			// write object data
			xfer->xferSnapshot( obj );

			// end a block of data
			xfer->endBlock();

		}  // end for

	}  // end if, save
	else
	{
		Team *defaultTeam = ThePlayerList->getNeutralPlayer()->getDefaultTeam();
		const ThingTemplate *thingTemplate;

		// read all objects
		Int objectDataSize;
		UnsignedShort tocID;
		ObjectTOCEntry *tocEntry;
		for( UnsignedInt i = 0; i < objectCount; ++i )
		{

			// read toc entry identifier
			xfer->xferUnsignedShort( &tocID );

			// find Object TOC entry with this identifier
			tocEntry = findTOCEntryById( tocID );
			if( tocEntry == NULL )
			{

				DEBUG_CRASH(( "GameLogic::xfer - No TOC entry match for id '%d'\n", tocID ));
				throw SC_INVALID_DATA;

			}  // end if

			// a block of data has begun
			objectDataSize = xfer->beginBlock();

			// find matching thing template
			thingTemplate = TheThingFactory->findTemplate( tocEntry->name );
			if( thingTemplate == NULL )
			{

				DEBUG_CRASH(( "GameLogic::xfer - Unrecognized thing template name '%s', skipping.  ENGINEERS - Are you *sure* it's OK to be ignoring this object from the save file???  Think hard about it!\n",
											tocEntry->name.str() ));
				xfer->skip( objectDataSize );
				continue;
							
			}  // end if

			// create new object
			obj = TheThingFactory->newObject( thingTemplate, defaultTeam );

			// xfer the rest of the object data
			xfer->xferSnapshot( obj );

			// end of block of data (not necessary in a load, but looks symettrically nice)
			xfer->endBlock();

			// special case for wall pieces, need to add them to the pathfinder
			if( obj->isKindOf( KINDOF_WALK_ON_TOP_OF_WALL ) )
				TheAI->pathfinder()->addWallPiece( obj );

		}  // end for, i

	}  // end else

	// campaign info
	xfer->xferSnapshot( TheCampaignManager );

	// cave system info
	xfer->xferSnapshot( TheCaveSystem );

	// is scoring enabled
	if( version >= 2 ) 
		xfer->xferBool(&m_isScoringEnabled);

	// polygon triggers
	if( version >= 3 )
	{
		PolygonTrigger *poly;

		// count the number of polygon triggers we have
		UnsignedInt triggerCount = 0;
		for( poly = PolygonTrigger::getFirstPolygonTrigger(); poly; poly = poly->getNext() )
			triggerCount++;

		// sanity count
		UnsignedInt sanityTriggerCount = triggerCount;

		// xfer count
		xfer->xferUnsignedInt( &triggerCount );

		//
		// since the save game loaded should have exactly the same number of polygon triggers
		// in it that we did from loading the map this is just a sanity check here
		//
		if( sanityTriggerCount != triggerCount )
		{

			DEBUG_CRASH(( "GameLogic::xfer - Polygon trigger count mismatch.  Save file has a count of '%d', but map had '%d' triggers\n",
										sanityTriggerCount, triggerCount ));
			throw SC_INVALID_DATA;

		}  // end if

		// xfer each of the polygon triggers
		if( xfer->getXferMode() == XFER_SAVE )
		{
			Int triggerID;

			for( poly = PolygonTrigger::getFirstPolygonTrigger(); poly; poly = poly->getNext() )
			{

				// write polygon ID
				triggerID = poly->getID();	
				xfer->xferInt( &triggerID );

				// xfer polygon data
				xfer->xferSnapshot( poly );

			}  // end for, poly

		}  // end if, save
		else
		{
			Int triggerID;

			// loop through all triggers
			for( UnsignedInt i = 0; i < triggerCount; ++i )
			{

				// read ID
				xfer->xferInt( &triggerID );

				// find this polygon trigger
				poly = PolygonTrigger::getPolygonTriggerByID( triggerID );

				// sanity
				if( poly == NULL )
				{

					DEBUG_CRASH(( "GameLogic::xfer - Unable to find polygon trigger with id '%d'\n",
												triggerID ));
					throw SC_INVALID_DATA;

				}  // end if

				// xfer polygon data
				xfer->xferSnapshot( poly );

			}  // end for, i

			//
			// force a recalculation of the pathfinding cause some of these polygon triggers
			// are water which can move during run-time, and therefore affecting the area
			// that objects can move on. Also, map objects need recalculation. jba.
			//
			TheAI->pathfinder()->newMap();

		}  // end else, load

	}	 // end if, version >= 3

	// note that version=4 is the same as version=3
	if (version >= 5)
	{
		xfer->xferInt(&m_rankLevelLimit);
	}

	if (version>=6) {
		// We need the list of buildings in process of being sold.
		TheBuildAssistant->xferTheSellList(xfer);
	}

	if (version >= 7) 
	{
		if( xfer->getXferMode() == XFER_SAVE )
		{
			for (BuildableMap::const_iterator it = m_thingTemplateBuildableOverrides.begin(); it != m_thingTemplateBuildableOverrides.end(); ++it )
			{
				AsciiString name = it->first;
				BuildableStatus bs = it->second;
				xfer->xferAsciiString(&name);
				xfer->xferUser(&bs, sizeof(bs));
			}
			AsciiString empty;
			xfer->xferAsciiString(&empty);
		}
		else if (xfer->getXferMode() == XFER_LOAD)
		{
			if (m_thingTemplateBuildableOverrides.empty() == false)
			{
				DEBUG_CRASH(( "GameLogic::xfer - m_thingTemplateBuildableOverrides should be empty, but is not\n"));
				throw SC_INVALID_DATA;
			}
			
			for (;;) 
			{
				AsciiString name;
				xfer->xferAsciiString(&name);
				if (name.isEmpty())
					break;
				BuildableStatus bs;
				xfer->xferUser(&bs, sizeof(bs));
				m_thingTemplateBuildableOverrides[name] = bs;
			}
		}
	}

	if (version >= 8)
	{
		xfer->xferBool(&m_showBehindBuildingMarkers);
		xfer->xferBool(&m_drawIconUI);
		xfer->xferBool(&m_showDynamicLOD);
		xfer->xferInt(&m_scriptHulkMaxLifetimeOverride);

		if( xfer->getXferMode() == XFER_SAVE )
		{
			for (ControlBarOverrideMap::const_iterator it = m_controlBarOverrides.begin(); it != m_controlBarOverrides.end(); ++it )
			{
				AsciiString name = it->first;
				AsciiString value = it->second ? it->second->getName() : AsciiString::TheEmptyString;
				xfer->xferAsciiString(&name);
				xfer->xferAsciiString(&value);
			}
			AsciiString empty;
			xfer->xferAsciiString(&empty);
		}
		else if (xfer->getXferMode() == XFER_LOAD)
		{
			if (m_controlBarOverrides.empty() == false)
			{
				DEBUG_CRASH(( "GameLogic::xfer - m_controlBarOverrides should be empty, but is not\n"));
				throw SC_INVALID_DATA;
			}
			
			for (;;) 
			{
				AsciiString name;
				xfer->xferAsciiString(&name);
				if (name.isEmpty())
					break;
				AsciiString value;
				xfer->xferAsciiString(&value);
				ConstCommandButtonPtr button = NULL;
				if (value.isNotEmpty())
				{
					button = TheControlBar->findCommandButton(value);
					DEBUG_ASSERTCRASH(button != NULL, ("Could not find button %s\n",value.str()));
				}
				m_controlBarOverrides[name] = button;
			}
		}
	}

	if (version >= 9)
	{
		xfer->xferInt(&m_rankPointsToAddAtGameStart);
	}

  if ( version >= 10 )
  {
    xfer->xferUnsignedShort( &m_superweaponRestriction );
  }
  else if ( xfer->getXferMode() == XFER_LOAD )
  {
    m_superweaponRestriction = 0;
  }
}  // end xfer

// ------------------------------------------------------------------------------------------------
/** Load post process entry point */
// ------------------------------------------------------------------------------------------------
void GameLogic::loadPostProcess( void )
{

	//
	// the act of loading objects can (theoretically) as a side effect create other objects, 
	// our m_nextObjID that we maintain to give objects unique ID is also continually
	// climbing higher and higher due to us allocating objects during load (even though
	// those objects have their ids overwritten with data from the file.  To prevent the
	// m_nextObjID from getting un-necessarily high we will set it to the next available
	// id from the objects that are now in the world and actually in use
	//
	m_nextObjID = INVALID_ID;
	Object *obj;
	for( obj = getFirstObject(); obj; obj = obj->getNextObject() )
		if( obj->getID() >= m_nextObjID )
			m_nextObjID = (ObjectID)((UnsignedInt)obj->getID() + 1);

	// blow away the sleepy update and normal update module lists
	for (std::vector<UpdateModulePtr>::iterator it = m_sleepyUpdates.begin(); it != m_sleepyUpdates.end(); ++it)
	{
		(*it)->friend_setIndexInLogic(-1);
	}
	m_sleepyUpdates.clear();
#ifdef ALLOW_NONSLEEPY_UPDATES
	m_normalUpdates.clear();
#else
	UnsignedInt now = TheGameLogic->getFrame();
	if (now == 0)
		now = 1;
#endif

	// go through all objects, examine each update module and put it on the appropriate update list
	for( obj = getFirstObject(); obj; obj = obj->getNextObject() )
	{

		// get the update list of modules for this object
		for( BehaviorModule** b = obj->getBehaviorModules(); *b; ++b )
		{
#ifdef DIRECT_UPDATEMODULE_ACCESS
			// evil, but necessary at this point. (srj)
			UpdateModulePtr u = (UpdateModulePtr)((*b)->getUpdate());
#else
			UpdateModulePtr u = (*b)->getUpdate();
#endif
			if (!u)
				continue;
			
			DEBUG_ASSERTCRASH(u->friend_getIndexInLogic() == -1, ("Hmm, expected index to be -1 here"));

			// check each update module
			UnsignedInt when = u->friend_getNextCallFrame();
#ifdef ALLOW_NONSLEEPY_UPDATES
			if( when == 0 )
			{
				// zero if the magic value for "never sleeps"
				m_normalUpdates.push_back(u);
			}
			else
#else
			// note that 'when' will only be zero for legacy save files.
			if (when == 0)
				u->friend_setNextCallFrame(now);
#endif
			{
				m_sleepyUpdates.push_back(u);
				u->friend_setIndexInLogic(m_sleepyUpdates.size() - 1);
			}
				
		}  // end for, u

	}  // end for, obj

	// re-sort the priority queue all at once now that all modules are on it
	remakeSleepyUpdate();

}  // end loadPostProcess


