////////////////////////////////////////////////////////////////////////////////
//
// Copyright 2016 RWS Inc, All Rights Reserved
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of version 2 of the GNU General Public License as published by
// the Free Software Foundation
//
// 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, write to the Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
//
// realm.cpp
// Project: Postal
//
// This module impliments the CRealm class.
//
// History:
//		12/31/96 MJR	Started.
//
//		01/20/97 MJR	Changed Update loop to save a temporary iterator so
//							objects that delete themselves during the update
//							loop will still have a valid iterator to get the next
//							item in the Things list.
//
//		01/23/97	JMI	Added instantiation of static member ms_asAttribToLayer[].
//
//		01/27/97	JMI	Now 4 bits are used from the attribute to determine the
//							proper layer for dudes.
//
//		01/28/97	JMI	Added ms_apszLayerNames[] to access the names of the
//							layers.
//
//		01/29/97	JMI	Reordered ms_apszLayerNames to match the new order in the
//							CRealm::Layers enum (fixed to the way Steve is doing it
//							(the opaque layers are now before alpha equivalents)).
//
//		02/03/97	JMI	The Load(char*) will now Open a SAK with the same base name
//							as the *.rlm file, if one exists.  If it does not exist,
//							m_resmgr's BasePath is set to the No SAK Dir.  Once the
//							load is complete, no more new resources are to be requested
//							from m_resmgr.  To enfore this, m_resmgr's base path is
//							set to a condescending message.  If you are not careful, it
//							may taunt you a second time-a!
//
//		02/04/97 BRH	Temporarily (or permanently) took out timeout check in
//							Startup because it caused problems debugging startup
//							code.
//
//		02/04/97 BRH	Added ms_pCurrentNavNet to the realm, moving it from
//							its previous position in gameedit.
//
//		02/10/97	JMI	Now all loops iterate to the next iterator in the STL list
//							before dooing iterative processing just in case the current
//							iterator is invalidated during processing.
//
//		02/13/97	JMI	Added hood pointer to CRealm.
//
//		02/17/97	JMI	Added set lists to CRealm.  Now there is a list for each
//							set of Things.  The sets are enumerated in CThing.  Now
//							new'ed in CRealm() and delete'ed in ~CRealm().
//
//		02/18/97	JMI	Changed uses of CThing::ThingSet to CThing::Things.
//
//		02/19/97	JMI	Got rid of stuff regarding old collision sets and added
//							new CSmashitorium usage.
//
//		02/23/97	JMI	Added m_iNext and m_bUpdating so RemoveThing() and
//							AddThing() can make the necessary adjustments to the next
//							iterator processed in the Update() loop.
//
//		02/23/97 MJR	Added call to class-based Preload() in CRealm::Load().
//
//		02/24/97	JMI	Added same protection for Render() that we implemented for
//							Update() on 02/23/97.
//
//		02/25/97	JMI	The way I had done the parens for *m_iNext++ for Render()
//							and Update() was scaring me so I separated it out more.
//
//		03/13/97	JMI	Load() now passes the file version number to the CThing
//							Load()'s.
//
//		03/13/97	JMI	Now, instead of the file's version number having to
//							exactly match FileVersion, it must just be less than or
//							equal.  If a file's version is greater than the current
//							version known by the code, the CThing that caused the
//							version number to increase will have more, less, or
//							different data to load that it cannot possibly know about.
//
//		03/25/97	JMI	Load() no longer opens a SAK with the same title as the
//							.rlm file (This is now opened by the CHood).
//
//		04/09/97 BRH	Added RMultiGrid for the multi layer attribute maps, but
//							haven't taken out the RAttributeMap yet so that the
//							game will continue to work until we completely switch over.
//
//		04/16/97 BRH	Added Jon's template class CListNode head and tail nodes
//							to CRealm to replace the STL containers that provided the
//							linked lists of CThings.  Once the new methods are proven
//							to work, we will get rid of the m_everthing and m_apthings
//							arrays of STL lists.
//
//		04/17/97 BRH	Took timeout check out of Shutdown and Startup (it was
//							already commented out of Startup).  The 1 second timeout
//							when calling shutdown caused large levels like parade
//							to abort before saving everything.
//
//		04/18/97	JMI	Now Suspend() suspends the game time and Resume() resumes
//							it.  This should alleviate the need for the CThing derived
//							objects to do their own time compensation.
//
//		04/21/97	JMI	Added m_sNumSuspends and IsSuspended().
//
//		05/04/97 BRH	Removed STL references since the CListNode lists seem
//							to be working properly.
//
//		05/13/97	JMI	Added IsPathClear() map functions to determine if a path
//							is clear of terrain that cannot be surmounted.
//
//		05/16/97	JMI	Changed layer bits to be 8 bits (instead of 4).  This
//							caused the REALM_ATTR_EFFECT_MASK to lose 4 bits and the
//							REALM_ATTR_LIGHT_BIT to move up 4 bits (0x0010 to 0x0100).
//							Also, the table ms_asAttribToLayer to be increased from
//							16 entries to 256 entries.
//							Also, removed GetLayerFromAttrib() which was left over from
//							the pre RMultiGrid days.
//
//		05/17/97	JMI	In EditUpdate() no argument list was supplied in the line:
//							pCur->m_powner->EditUpdate;
//							Added a set of parens.
//
//		05/26/97	JMI	Added DrawStatus() function.
//
//		05/27/97	JMI	Changed format of DrawStatus() output string.
//
//		05/29/97	JMI	Changed occurences of m_pHeightMap to m_pTerrainMap.
//							Changed occurences of m_pAttrMap to m_pLayerMap.
//							Also, removed occurences of m_pAttribMap.
//							Also, added CreateLayerMap() that creates the attribute
//							to layer map now that it is so huge.
//
//		06/04/97 BRH	Turned off drawing lines in IsPathClear function.
//
//		06/09/97	JMI	Changed wording of realm status.
//							Also, Suspend() and Resume() pause and resume the sound.
//
//		06/10/97	JMI	Now Resume() does not let you 'over'-resume.
//
//		06/17/97 MJR	Moved some vars that were CPylon statics into the realm
//							so they could be instantiated on a realm-by-realm basis.
//
//		06/20/97 JRD	Replaced code needed to manage allocation and deallocation
//							for the new CSmashatorium
//
//		06/26/97	JMI	Moved Map2DTo3D from reality.h to here.
//							When converting to 2D, Y is now scaled based on the view
//							angle.  This was not applied to Z.  It could be a bug, but
//							it just looked better this way.  I suspect it is a
//							bug/feature of the way most of the code was written
//							(probably a product of being more aware of the noticable
//							difference Y coord has when mapped to 2D when comparing 45
//							to 90 degree type levels while originally designing/coding
//							CThings, the editor, and CScene).
//
//		06/28/97	JMI	Moved attribute access functions from realm.h to realm.cpp
//							while we're getting all the conversion from 3D to the X/Z
//							plane stuff right.  Compiling the entire project for any
//							tweak just doesn't sound very fun.  Hopefully, though,
//							there'll won't be many tweaks.
//							Also, added ScaleZ() functions to scale just Z (useful
//							for attribute map access).
//							Changed references to GetWorldRotX() to GetRealmRotX().
//
//		06/29/97	JMI	Added version of ScaleZ() that takes shorts.
//							Changed both versions of ScaleZ() to MapZ3DtoY2D()
//							and added two versions of MapY2DtoZ3D().
//
//		06/30/97	JMI	Now uses CRealm's new GetRealmWidth() and *Height()
//							for dimensions of realm's X/Z plane.
//
//		06/30/97	JMI	Added bCheckExtents parm to IsPathClear().  See proto for
//							details.
//
//		07/01/97	JMI	Added MapY2DtoY3D() and MapY3DtoY2D().
//							Also, GetHeight() now scales the height into realm
//							coordinates.
//
//		07/01/97	JMI	IsPathClear() had 'step-up' logic (TM) for land based
//							things (like doofuses) that did not work for hovering
//							things (like missiles).  Fixed.
//
//		07/01/97	JMI	Changed the file version for the Hood so it can load and
//							save whether to use the attribute map heights with or
//							without scaling based on the view angle.
//							Also, added function to scale heights that checks the
//							hood value.
//
//		07/07/97 BRH	Added EditModify function to process the realm properties
//							dialog box where you can select the scoring mode and
//							play mode for the realm.
//
//		07/08/97 BRH	Added loading and saving of properties as of version 27.
//
//		07/09/97	JMI	Added function to get the full path for a 2D resource
//							based on the current hood setting for 'Use top-view 2Ds'.
//
//		07/09/97	JMI	Moved m_s2dResPathIndex from CHood to CRealm b/c of order
//							issues when loading.
//
//		07/10/97 BRH	Added cases to IsEndOfLevelGoalMet for the different
//							scoring modes.
//
//		07/11/97	JMI	Minor change in IsEndOfLevelGoalMet() to make it so, if
//							there are zero births, the goal is considered met.  A
//							special case for example levels that have no enemies.
//
//		07/11/97 BRH	Added time calculations here for expiration date.  Checked
//							in again to update source safe with correct date.
//
//		07/12/97	JMI	Added m_bMultiplayer, which signifies whether this is
//							a multi or single player game.
//							Also, added Init().  See proto for details.
//							Moved things that were being initialized both in CRealm()
//							and in Clear() to Init() which is called by these two
//							functions.
//							Also, moved the initialization of the population statistics
//							into Init() even though they were only being done on a
//							Clear().  This might be bad but it did not seem like it
//							could hurt.
//
//		07/12/97 BRH	Made minor change to the EditModify dialog so that the
//							seconds always appears as two digits.
//
//		07/14/97	JMI	Moved initialization of Pylon stuff into Init() and also
//							no m_ucNextPylonId is 1 instead of 0.
//
//		07/14/97 BRH	Fixed a bug that caused the score timer to always count up.
//							Fixed problems with the goal stopping conditions and added
//							m_sFlagbaseCaptured to keep track of flags that were
//							successfully returned to their base.
//
//		07/15/97 BRH	Added the end of level key flag as a parameter to
//							the end of level goal check.
//
//		07/16/97 BRH	Fixed a problem with standard scoring mode on levels
//							that didn't have any enemies, they would end right
//							away using the new method of checking the end of
//							the level.
//
//		07/17/97 BRH	Added the time adjustment to the mac version.
//
//		07/27/97 BRH	Added m_lScoreInitialTime and set it to the same
//							initial value as m_lScoreDisplayTimer.  This was required
//							in order to calculate the time elapsed for the high
//							scores.
//
//		07/30/97 BRH	Added a string to hold the path of the realm which will
//							be used by the high score function to identify the
//							current realm file.
//
//		08/05/97	JMI	Now pauses only active sounds so that new sounds can
//							continue to play while the realm is suspended.
//
//		08/05/97	JMI	Added CRealm::Flags struct and an instance in CRealm,
//							m_flags.
//
//		08/08/97	JMI	Now displays a useful message about the thing that failed
//							to load should one do so.
//
//		08/09/97	JMI	Added progress callback and components.  There is now a
//							callback member that is called (when non-zero) that passes
//							the number of items processed so far and the total number of
//							items to process.  The return value of the callback dictates
//							whether to proceed with the operation.  Although, this is
//							meant to be generic so we can use it for any type of
//							process, it is currently only used by Load() and Save().
//
//		08/11/97 BRH	Changed both Capture the flag goals to flagbases captured
//							rather than flags captured.
//
//		08/19/97 BRH	Fixed end of level goal for the checkpoint scoring so
//							if a specific number of flags is not set in the realm
//							file, it will just continue until the time runs out.
//
//		08/20/97	JMI	Made ms_apsz2dResPaths[] a static class member (was just
//							defined at realm.cpp file scope) and added enum macro for
//							number of elements.
//
//		08/28/97 BRH	Changed level goals for challenge scoring modes to use
//							the population numbers rather than just the hostile numbers
//							so that the victims count also.
//
//		08/30/97	JMI	IsEndOfLevelGoalMet() now allows a player to go on in
//							Timed and Checkpoint, if they hit the 'next level' key.
//
//		08/30/97	JMI	Now initializes hostile deaths and population deaths in
//							Startup().
//
//		09/02/97	JMI	Now resets all population statistics in Startup().
//
//		09/04/97 BRH	Realm::Load now attempts several paths.  It first tries
//							the path passed in for the case where the user
//							specified a full path using the open dialog.  Then it
//							tries the HD path and then the CD path if those fail.
//							This would allow us to send updated realm files
//							that could be loaded instead of the ones on the CD
//							just by putting them in the mirror path on the HD.
//
//		09/07/97 BRH	Changed end of level goal to never end the MPFrag if
//							the kills goal is set to zero.  That will mean no
//							frag limit.
//
//		09/09/97	JMI	Now checks number of flags against the actual number of
//							flags when using CheckPoint.
//
//		09/11/97 MJR	Added DoesFileExist(), which uses the same logic as Load()
//							to determine if a realm file exists.  Also added Open()
//							as a common function for DoesFileExist() and Load() to use.
//
//		09/12/97	JMI	Now, if ENABLE_PLAY_SPECIFIC_REALMS_ONLY is defined, we
//							try to load the .RLM out of memory using
//							GetMemFileResource().
//
//		09/12/97 MJR	Now Open() will detect an empty filename as an error,
//							which avoids the ASSERT() that the Microsoft Runtime
//							library does when you pass open() and empty string.
//
//		11/21/97	JMI	Added bCoopMode flag indicating whether we're in cooperative
//							or deathmatch mode when in multiplayer.
//
////////////////////////////////////////////////////////////////////////////////
#define REALM_CPP

#include "RSPiX.h"
#include "realm.h"
#include "game.h"
#include "reality.h"
#include "score.h"
#include <time.h>
#include "MemFileFest.h"

//#define RSP_PROFILE_ON

#include "ORANGE/Debug/profile.h"

#include "../AICA/aica.h"
////////////////////////////////////////////////////////////////////////////////
// Macros/types/etc.
////////////////////////////////////////////////////////////////////////////////

// Sets the specified value into the data pointed, if the ptr is not NULL.
#define SET(ptr, val) ((ptr != NULL) ? *ptr = val : val)

// Time, in ms, between status updates.
#define STATUS_UPDATE_INTERVAL 1000

#define STATUS_PRINT_X 0
#define STATUS_PRINT_Y 0

#define STATUS_FONT_SIZE 19
#define STATUS_FONT_FORE_INDEX 2
#define STATUS_FONT_BACK_INDEX 0
#define STATUS_FONT_SHADOW_INDEX 0

// Determines the number of elements in the passed array at compile time.
#define NUM_ELEMENTS(a) (sizeof(a) / sizeof(a[0]))

#define MAX_SMASH_DIAMETER 20

#define REALM_DIALOG_FILE "res/editor/realm.gui"

#define TIMER_MIN_EDIT_ID 201
#define TIMER_SEC_EDIT_ID 202
#define KILLS_NUM_EDIT_ID 203
#define KILLS_PCT_EDIT_ID 204
#define FLAGS_NUM_EDIT_ID 205
#define SCORE_MODE_LB_ID 99
#define SCORE_MODE_LIST_BASE 100

////////////////////////////////////////////////////////////////////////////////
// Variables/data
////////////////////////////////////////////////////////////////////////////////

// File counter
int16_t CRealm::ms_sFileCount;

// Maps the layer portion of an attribute to the appropriate
// layer.
// Now that this table is 32K, we generate it table at run time to avoid adding
// an extra 32K of uncompressable space to the exe.
int16_t CRealm::ms_asAttribToLayer[CRealm::LayerAttribMask + 1];

// Names of layers.  Use Layer enum values to index.
char *CRealm::ms_apszLayerNames[TotalLayers] =
	{
		"Background",

		"Sprite1",
		"Opaque1",

		"Sprite2",
		"Alpha1",

		"Sprite3",
		"Opaque2",

		"Sprite4",
		"Alpha2",

		"Sprite5",
		"Opaque3",

		"Sprite6",
		"Alpha3",

		"Sprite7",
		"Opaque4",

		"Sprite8",
		"Alpha4",

		"Sprite9",
		"Opaque5",

		"Sprite10",
		"Alpha5",

		"Sprite11",
		"Opaque6",

		"Sprite12",
		"Alpha6",

		"Sprite13",
		"Opaque7",

		"Sprite14",
		"Alpha7",

		"Sprite15",
		"Opaque8",

		"Sprite16",

		"SpriteTop"
};

// These are the various 2d paths that we currently support.  Eventually, if
// there's more than two, this can be presented in listbox form (instead of
// checkbox form).
char *CRealm::ms_apsz2dResPaths[Num2dPaths] =
	{
		"2d/Top/",
		"2d/Side/",
		"2d/SideBright/",
};

// Used for CRealm oriented drawing tasks.
static RPrint ms_print;

////////////////////////////////////////////////////////////////////////////////
// Function prototypes
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
// Non-member functions.
////////////////////////////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////////////////////////
// Maps a 3D coordinate onto the viewing plane provided the view angle
// (~angle of projection).
///////////////////////////////////////////////////////////////////////////////
template <class TIn, class TOut>
void Map3Dto2D(			// Returns nothing.
	TIn tX,				// In.
	TIn tY,				// In.
	TIn tZ,				// In.
	TOut *ptX,			// Out.
	TOut *ptY,			// Out.
	int16_t sViewAngle) // In:  View angle in degrees.
{

	float cosp;
	float sinp;
	rspSinCos(float(sViewAngle), &sinp, &cosp);

	*ptX = tX;
	*ptY = sinp * tZ - cosp * tY;
}

///////////////////////////////////////////////////////////////////////////////
// Scales a Z coordinate onto the viewing plane provided the
// view angle (~angle of projection).
///////////////////////////////////////////////////////////////////////////////
template <class TIn, class TOut>
void MapZ3DtoY2D(		// Returns nothing.
	TIn tZIn,			// In.
	TOut *ptYOut,		// Out.
	int16_t sViewAngle) // In:  View angle in degrees.
{
	ASSERT(sViewAngle >= 0 && sViewAngle < 360);

	*ptYOut = rspSin(sViewAngle) * tZIn;
}

///////////////////////////////////////////////////////////////////////////////
// Scales a Y coordinate from the viewing plane provided the
// view angle (~angle of projection).
///////////////////////////////////////////////////////////////////////////////
template <class TIn, class TOut>
void MapY2DtoZ3D(		// Returns nothing.
	TIn tYIn,			// In.
	TOut *ptZOut,		// Out.
	int16_t sViewAngle) // In:  View angle in degrees.
{
	ASSERT(sViewAngle >= 0 && sViewAngle < 360);

	REAL rSin = rspSin(sViewAngle);
	if (rSin != 0.0)
	{
		*ptZOut = tYIn / rSin;
	}
	else
	{
		*ptZOut = 0;
	}
}

///////////////////////////////////////////////////////////////////////////////
// Scales a Y coordinate onto the viewing plane provided the
// view angle (~angle of projection).
///////////////////////////////////////////////////////////////////////////////
template <class TIn, class TOut>
void MapY3DtoY2D(		// Returns nothing.
	TIn tYIn,			// In.
	TOut *ptYOut,		// Out.
	int16_t sViewAngle) // In:  View angle in degrees.
{
	ASSERT(sViewAngle >= 0 && sViewAngle < 360);

	*ptYOut = rspCos(sViewAngle) * tYIn;
}

///////////////////////////////////////////////////////////////////////////////
// Scales a Y coordinate from the viewing plane provided the
// view angle (~angle of projection).
///////////////////////////////////////////////////////////////////////////////
template <class TIn, class TOut>
void MapY2DtoY3D(		// Returns nothing.
	TIn tYIn,			// In.
	TOut *ptYOut,		// Out.
	int16_t sViewAngle) // In:  View angle in degrees.
{
	ASSERT(sViewAngle >= 0 && sViewAngle < 360);

	REAL rCos = rspCos(sViewAngle);
	if (rCos != 0.0)
	{
		*ptYOut = tYIn / rCos;
	}
	else
	{
		*ptYOut = 0;
	}
}

////////////////////////////////////////////////////////////////////////////////
// Default (and only) constructor
////////////////////////////////////////////////////////////////////////////////
CRealm::CRealm()
{

	time_t lTime;
	time(&lTime);
#ifndef WIN32
	// Mac version time adjusment back to UTC time.
	lTime -= ((365 * 70UL) + 17) * 24 * 60 * 60; // time_fudge 1900->1970
#endif
	g_lRegValue = lTime - g_lRegTime;
	g_lExpValue = g_lExpTime - lTime;

	CreateLayerMap();

	// Setup render object (it's constructor was automatically called)
	m_scene.SetLayers(TotalLayers);

	// Set attribute map to a safe (but invalid) value
	m_pTerrainMap = 0;
	m_pLayerMap = 0;
	m_pTriggerMap = 0;
	m_pTriggerMapHolder = 0;

	// Set Hood ptr to a safe (but invalid) value.
	m_phood = NULL;

	/*
	// Create a container of things for each element in the array
	short	s;
	for (s = 0; s < CThing::TotalIDs; s++)
		m_apthings[s] = new CThing::Things;
*/

	// Initialize current Navigation Net pointer
	m_pCurrentNavNet = NULL;

	// Not currently updating.
	m_bUpdating = false;

	// Initialize dummy nodes for linked lists of CThings
	m_everythingHead.m_pnNext = &m_everythingTail;
	m_everythingHead.m_pnPrev = NULL;
	m_everythingHead.m_powner = NULL;
	m_everythingTail.m_pnNext = NULL;
	m_everythingTail.m_pnPrev = &m_everythingHead;
	m_everythingTail.m_powner = NULL;

	int16_t i;
	for (i = 0; i < CThing::TotalIDs; i++)
	{
		m_aclassHeads[i].m_pnNext = &(m_aclassTails[i]);
		m_aclassHeads[i].m_pnPrev = NULL;
		m_aclassHeads[i].m_powner = NULL;
		m_aclassTails[i].m_pnNext = NULL;
		m_aclassTails[i].m_pnPrev = &(m_aclassHeads[i]);
		m_aclassTails[i].m_powner = NULL;
		m_asClassNumThings[i] = 0;
	}

	m_sNumThings = 0;

	m_sNumSuspends = 0;

	// Setup print.
	ms_print.SetFont(STATUS_FONT_SIZE, &g_fontBig);
	ms_print.SetColor(
		STATUS_FONT_FORE_INDEX,
		STATUS_FONT_BACK_INDEX,
		STATUS_FONT_SHADOW_INDEX);

	// Initialize flags to defaults for safety.
	// This might be a bad idea if we want to guarantee they get set in which
	// case we should maybe set them to absurd values.
	m_flags.bMultiplayer = false;
	m_flags.bCoopMode = false;
	m_flags.bEditing = false;
	m_flags.bEditPlay = false;
	m_flags.sDifficulty = 5;
	m_flags.bPurgeResOnShutdown = false;

	m_fnProgress = NULL;

	m_bPressedEndLevelKey = false;

	// Initialize.
	Init();
}

////////////////////////////////////////////////////////////////////////////////
// Destructor
////////////////////////////////////////////////////////////////////////////////
CRealm::~CRealm()
{
	// Clear the realm (in case this hasn't been done yet)
	Clear();


		// Scan through class info structs and for each non-0 preload func,
					// call it to give that class a chance to preload stuff.  The intention
					// is to give classes whose objects don't exist at the start of a level
					// a chance to preload resources now rather than during gameplay.					
					for (int16_t sPre = 0; sPre < CThing::TotalIDs; sPre++)
					{						
						CThing::FuncUnload func = CThing::ms_aClassInfo[sPre].funcUnload;
						if (func != 0)
						{
							 (*func)(this);					
						}
					}



	// Double-check to be sure there's nothing left
	if (m_everythingHead.m_pnNext != &m_everythingTail)
		TRACE("CRealm::~CRealm(): There are still %d CThing's in this realm!\n", m_sNumThings);
}

////////////////////////////////////////////////////////////////////////////////
// This will set all values that are to be set on construction and during
// a Clear().  This is called by CRealm() and Clear().  This gives us one
// spot to implement these, rather than having to do it twice.
////////////////////////////////////////////////////////////////////////////////
void CRealm::Init(void) // Returns nothing.  Cannot fail.
{
	m_dKillsPercentGoal = 80.0;
	m_sKillsGoal = 0;
	m_sFlagsGoal = 0;
	m_lScoreTimeDisplay = 0;
	m_lScoreInitialTime = 0;
	m_ScoringMode = Standard;
	m_sFlagsCaptured = 0;
	m_sFlagbaseCaptured = 0;

	// Reset the Population statistics
	m_sPopulationBirths = 0;
	m_sPopulation = 0;
	m_sPopulationDeaths = 0;
	m_sHostiles = 0;
	m_sHostileBirths = 0;
	m_sHostileKills = 0;

	// Initial index for 2D resoruce paths array.
	m_s2dResPathIndex = 1;

	// Reset timer.
	m_lLastStatusDrawTime = -STATUS_UPDATE_INTERVAL;

	// Pylon stuff
	int16_t i;
	for (i = 0; i < 256; i++)
		m_asPylonUIDs[i] = 0; // clear the Pylon UIDs!
	m_sNumPylons = 0;
	m_ucNextPylonID = 1;

	m_flags.bPurgeResOnShutdown = false;

	// todo?
	m_sExitX = 0;
	m_sExitY = 0;
}

////////////////////////////////////////////////////////////////////////////////
// Clear the realm
////////////////////////////////////////////////////////////////////////////////
void CRealm::Clear()
{	
	// Shutdown the realm (in case this hasn't been done yet)
	Shutdown();

	// Destroy all the objects.  We use a copy of the iterator to avoid being
	// stuck with an invalid iterator once the object is gone.
	CListNode<CThing> *pCur;
	CListNode<CThing> *pNext = m_everythingHead.m_pnNext;
	int counter = 0;
	while (pNext->m_powner != NULL)
	{
		pCur = pNext;
		pNext = pNext->m_pnNext;
		counter++;

		if (pCur->m_powner != NULL)
		{
			delete (pCur->m_powner);
		}
	}

	// Clear out any sprites that didn't already remove themselves
	m_scene.RemoveAllSprites();

	// Clear out any residue IDs.  Shouldn't need to, but . . .
	m_idbank.Reset();

	// Reset smashatorium.

#ifdef NEW_SMASH // need to become final at some point...
	m_smashatorium.Destroy();
#else
	m_smashatorium.Reset();
#endif

	// Re-Initialize.
	Init();
}

////////////////////////////////////////////////////////////////////////////////
// Determine if specified file exists according to same rules used by Load()
////////////////////////////////////////////////////////////////////////////////
// static
bool CRealm::DoesFileExist(	 // Returns true if file exists, false otherwise
	const char *pszFileName) // In:  Name of file
{
	bool bResult = false;
	RFile file;

	if (Open(pszFileName, &file) == 0)
	{
		file.Close();
		bResult = true;
	}

	return bResult;
}

////////////////////////////////////////////////////////////////////////////////
// Open the specified realm file
////////////////////////////////////////////////////////////////////////////////
// static
int16_t CRealm::Open(		 // Returns 0 if successfull, non-zero otherwise
	const char *pszFileName, // In:  Name of file to load from
	RFile *pfile)			 // I/O: RFile to be used
{
	int16_t sResult = 0;

	if (strlen(pszFileName) > 0)
	{

#if !defined(ENABLE_PLAY_SPECIFIC_REALMS_ONLY)
		// Try the given path first since it may already have a full path in the
		// case of loading a level, then try the path with the HD path prepended,
		// then try the CD path.		
		sResult = pfile->Open(rspPathToSystem((char *)pszFileName), "rb", RFile::LittleEndian);
		
		if (sResult != 0)
		{
			char pszFullPath[RSP_MAX_PATH];
			strcpy(pszFullPath, FullPathCD((char *)pszFileName));
			sResult = pfile->Open((char *)pszFullPath, "rb", RFile::LittleEndian);
			if (sResult != 0)
			{
				strcpy(pszFullPath, FullPathCD((char *)pszFileName));
				sResult = pfile->Open((char *)pszFullPath, "rb", RFile::LittleEndian);
			}
		}
#else
		// There's only one place it can possibly be and, if it's not there,
		// no realm for you!
		sResult = GetMemFileResource(pszFileName, RFile::LittleEndian, pfile);
#endif // ENABLE_PLAY_SPECIFIC_REALMS_ONLY
	}
	else
	{
		sResult = -1;
		TRACE("CRealm::Open(): Empty file name!\n");
	}

	return sResult;
}

////////////////////////////////////////////////////////////////////////////////
// Load the realm
////////////////////////////////////////////////////////////////////////////////
int16_t CRealm::Load(		 // Returns 0 if successfull, non-zero otherwise
	const char *pszFileName, // In:  Name of file to load from
	bool bEditMode)			 // In:  Use true for edit mode, false otherwise
{
	int16_t sResult = 0;

	// Copy the name to use later for high score purposes
	m_rsRealmString = pszFileName;

	// Open file
	RFile file;
	sResult = Open(pszFileName, &file);
	if (sResult == 0)
	{
		// Use alternate load to do most of the work
		sResult = Load(&file, bEditMode);
		file.Close();
	}
	else
	{
		sResult = -1;
		TRACE("CRealm::Load(): Couldn't open file: %s !\n", pszFileName);
	}

	return sResult;
}

////////////////////////////////////////////////////////////////////////////////
// Load realm
////////////////////////////////////////////////////////////////////////////////
int16_t CRealm::Load( // Returns 0 if successfull, non-zero otherwise
	RFile *pFile,	  // In:  File to load from
	bool bEditMode)	  // In:  Use true for edit mode, false otherwise
{
	int16_t sResult = 0;

	uint32_t totalBytes = 0;
	

	// Clear the realm before loading this new stuff
	Clear();

	// Increment file count
	ms_sFileCount++;
	
	// Read & validate file ID
	uint32_t ulFileID;
	if (pFile->Read(&ulFileID) == 1)
	{
		if (ulFileID == CRealm::FileID)
		{

			// Read & validate file version
			uint32_t ulFileVersion;
			if (pFile->Read(&ulFileVersion) == 1)
			{

				// If a known version . . .
				if (ulFileVersion <= CRealm::FileVersion)
				{
					// Read properties for the realm
					switch (ulFileVersion)
					{
					default:
					case 30:
						pFile->Read(&m_s2dResPathIndex);

					case 29:
						pFile->Read(&m_ScoringMode);
					case 28:
					case 27:
					{
						int16_t sUp;
						pFile->Read(&m_lScoreTimeDisplay);
						m_lScoreInitialTime = m_lScoreTimeDisplay;
						pFile->Read(&sUp);
						if (sUp == 1)
							m_bScoreTimerCountsUp = true;
						else
							m_bScoreTimerCountsUp = false;
						pFile->Read(&m_sKillsGoal);
						pFile->Read(&m_sFlagsGoal);
						pFile->Read(&m_dKillsPercentGoal);
						break;
					}
					case 26:
					case 25:
					case 24:
					case 23:
					case 22:
					case 21:
					case 20:
					case 19:
					case 18:
					case 17:
					case 16:
					case 15:
					case 14:
					case 13:
					case 12:
					case 11:
					case 10:
					case 9:
					case 8:
					case 7:
					case 6:
					case 5:
					case 4:
					case 3:
					case 2:
					case 1:
					case 0:
						break;
					}

					// Scan through class info structs and for each non-0 preload func,
					// call it to give that class a chance to preload stuff.  The intention
					// is to give classes whose objects don't exist at the start of a level
					// a chance to preload resources now rather than during gameplay.					
					for (int16_t sPre = 0; sPre < CThing::TotalIDs; sPre++)
					{						
						CThing::FuncPreload func = CThing::ms_aClassInfo[sPre].funcPreload;
						if (func != 0)
						{
							sResult = (*func)(this);							
							if (sResult != 0)
							{								
								TRACE("CRealm::Load(): Error reported by Preload() for CThing class ID = %hd\n", (int16_t)sPre);								
								break;
							}							
						}
					}

					if (sResult == 0)
					{

						// Read number of things that were written to file (could be 0!)
						int16_t sCount;
						if (pFile->Read(&sCount) == 1)
						{
							CThing::ClassIDType idLastThingLoaded = CThing::TotalIDs;

							// If there's a callback . . .
							if (m_fnProgress)
							{
								// Call it . . .
								if (m_fnProgress(0, sCount) == true)
								{
									// Callback is happy to continue.
								}
								else
								{
									// Callback has decided to end this operation.
									sResult = 1;
								}
							}

							// Load each object that was written to the file (could be 0!)
							for (int16_t s = 0; (s < sCount) && !sResult; s++)
							{								
								// Read class ID of next object in file
								CThing::ClassIDType id;								
								if (pFile->Read(&id) == 1)
								{							
									// Create object based on class ID
									CThing *pThing;									
									sResult = CThing::Construct(id, this, &pThing);
									if (!sResult)
									{
										// Load object assocated with this class ID
										sResult = pThing->Load(pFile, bEditMode, ms_sFileCount, ulFileVersion);
										
										// If successful . . .
										if (sResult == 0)
										{
											// Store last thing to successfully load.
											idLastThingLoaded = id;
											// If there's a callback . . .

											if (m_fnProgress)
											{
												// Call it . . .
												if (m_fnProgress(s + 1, sCount) == true)
												{
													// Callback is happy to continue.
												}
												else
												{
													// Callback has decided to end this operation.
													sResult = 1;
												}
											}
										}
										else
										{
											TRACE("CRealm::Load(): Load() failed for thing of type %s; ",
												  CThing::ms_aClassInfo[id].pszClassName);
											if (idLastThingLoaded != CThing::TotalIDs)
											{
												STRACE("The last thing to successfully\n loaded was a\n %s.\n",
													   CThing::ms_aClassInfo[idLastThingLoaded].pszClassName);
											}
											else
											{
												STRACE("This was the first thing to load.\n");
											}
										}
									}
								}
								else
								{
									sResult = -1;
									TRACE("CRealm::Load(): Error reading class ID!\n");
								}
							}

							// Check for I/O errors (only matters if no errors were reported so far)
							if (!sResult && pFile->Error())
							{
								sResult = -1;
								TRACE("CRealm::Load(): Error reading file!\n");
							}

							// If any errors occurred . . .
							if (sResult)
							{
								// Better clean up stuff that did load.
								Clear();
							}
						}
						else
						{
							sResult = -1;
							TRACE("CRealm::Load(): Error reading count of objects in file!\n");
						}
					}
				}
				else
				{
					sResult = -1;
					TRACE("CRealm::Load(): Incorrect file version (should be 0x%lx or less, was 0x%lx)!\n", CRealm::FileVersion, ulFileVersion);
				}
			}
			else
			{
				sResult = -1;
				TRACE("CRealm::Load(): Error reading file version!\n");
			}
		}
		else
		{
			sResult = -1;
			TRACE("CRealm::Load(): Incorrect file ID (should be 0x%lx, was 0x%lx)!\n", CRealm::FileID, ulFileID);
		}
	}
	else
	{
		sResult = -1;
		TRACE("CRealm::Load(): Error reading file ID!\n");
	}

#ifdef NEW_SMASH
	if (sResult == 0) // a success....
	{
		/* For now, let's see if this is necessary...

		// Allocate the Smashatorium:
		// Kill old...*
		short	sOldW = m_smashatorium.m_sWorldW;
		short	sOldH = m_smashatorium.m_sWorldH;
		short sOldTileW = m_smashatorium.m_sTileW;
		short sOldTileH = m_smashatorium.m_sTileH;


		if (m_smashatorium.m_pGrid) m_smashatorium.Destroy();

		if (m_smashatorium.Alloc(sOldW,sOldH,sOldTileW,sOldTileH) != SUCCESS)
			{
			TRACE("CRealm::Load(): Error reallocating the smashatorium!\n");
			sResult = -1;
			}
		*/
	}
#endif
	
	return sResult;
}

////////////////////////////////////////////////////////////////////////////////
// Startup the realm
////////////////////////////////////////////////////////////////////////////////
int16_t CRealm::Startup(void) // Returns 0 if successfull, non-zero otherwise
{
	int16_t sResult = 0;

	// Initialize Population statistics b/c anyone killed already was not done so
	// by the player.
	m_sPopulationBirths = 0;
	m_sPopulation = 0;
	m_sPopulationDeaths = 0;
	m_sHostiles = 0;
	m_sHostileBirths = 0;
	m_sHostileKills = 0;

	// The idea is to only call Startup() for those objects that were Load()'ed,
	// and NOT for any other objects in the realm.  The m_sCallStartup flags are
	// set during the CRealm::Load() process to ensure that only those objects
	// are called here.  I'm no longer sure I like this idea, so perhaps we
	// should do what Shutdown() does, which is to call Startup() for every
	// object.  The original reasoning was that once the game gets going, any
	// newly created objects will NOT have their Startup() called, so Startup()
	// was seen as a special service for Load()'ed objects so they could interact
	// with other objects once all the objects are Load()'ed.  Actually, that
	// sounds pretty good!  I guess I'll keep it this way pending suggestions.

	// This loop is specifically designed so that it will not end until all of
	// the objects have been scanned in a single pass and none have their flags
	// set.  Keep in mind that since objects may be interacting with one another,
	// calling one object may result in indirectly changing another's flag!
	int16_t sDone;
	int32_t lPassNum = 0;
	do
	{
		// Always assume this will be the last pass.  If it isn't, this flag will
		// be reset to 0, and we'll do the whole thing again.
		sDone = 1;

		// Do this for all the objects.  We use a copy of the iterator to avoid being
		// stuck with an invalid iterator once the object is gone.
		CListNode<CThing> *pCur;
		CListNode<CThing> *pNext = m_everythingHead.m_pnNext;
		// Go through all objects, calling Startup() for those that have the flag set
		int objc = 0;

		while (pNext->m_powner != NULL && !sResult)
		{
			pCur = pNext;
			pNext = pNext->m_pnNext;

			if (pCur->m_powner->m_sCallStartup)
			{
				sDone = 0;
				pCur->m_powner->m_sCallStartup = 0;
				sResult = pCur->m_powner->Startup();
			}

			objc++;
		}

		// Increment pass number for testing/debugging
		lPassNum++;

		// Setup the previous time for the start of the game.
		m_lPrevTime = m_time.GetGameTime();

	} while (!sDone && !sResult);

	return sResult;
}

////////////////////////////////////////////////////////////////////////////////
// Shutdown the realm
////////////////////////////////////////////////////////////////////////////////
int16_t CRealm::Shutdown(void) // Returns 0 if successfull, non-zero otherwise
{
	int16_t sResult = 0;

	// This loop is specifically designed so that it will not end until all of
	// the objects have been scanned in a single pass and none have their flags
	// set.  Keep in mind that since objects may be interacting with one another,
	// calling one object may result in indirectly changing another's flag!
	int16_t sDone;
	int16_t sFirstTime = 1;
	int32_t lPassNum = 0;
	do
	{
		// Always assume this will be the last pass.  If it isn't, this flag will
		// be reset to 0, and we'll do the whole thing again.
		sDone = 1;

		// Do this for all the objects.  We use a copy of the iterator to avoid being
		// stuck with an invalid iterator once the object is gone.
		CListNode<CThing> *pCur;
		CListNode<CThing> *pNext = m_everythingHead.m_pnNext;
		while (pNext->m_powner != NULL && !sResult)
		{
			pCur = pNext;
			pNext = pNext->m_pnNext;

			if (pCur->m_powner->m_sCallShutdown || sFirstTime)
			{
				sDone = 0;
				pCur->m_powner->m_sCallShutdown = 0;
				sResult = pCur->m_powner->Shutdown();
			}
		}

		// Clear first-time flag
		sFirstTime = 0;

		// Increment pass number for testing/debugging
		lPassNum++;

	} while (!sDone && !sResult);

	return sResult;
}

////////////////////////////////////////////////////////////////////////////////
// Suspend the realm
////////////////////////////////////////////////////////////////////////////////
void CRealm::Suspend(void)
{
	m_sNumSuspends++;

	// Do this for all the objects.  We use a copy of the iterator to avoid being
	// stuck with an invalid iterator once the object is gone.
	CListNode<CThing> *pCur;
	CListNode<CThing> *pNext = m_everythingHead.m_pnNext;
	while (pNext->m_powner != NULL)
	{
		pCur = pNext;
		pNext = pNext->m_pnNext;

		pCur->m_powner->Suspend();
	}

	// Suspend the game time.  I don't think it matters whether this is done
	// before or after the CThing->Suspend() calls since game time never actually
	// advances unless CTime->Update() is called (when not suspended, of course).
	m_time.Suspend();

	// Suspend active sounds.
	PauseAllSamples();
}

////////////////////////////////////////////////////////////////////////////////
// Resume the realm
////////////////////////////////////////////////////////////////////////////////
void CRealm::Resume(void)
{
	if (m_sNumSuspends > 0)
	{
		// Do this for all the objects.  We use a copy of the iterator to avoid being
		// stuck with an invalid iterator once the object is gone.
		CListNode<CThing> *pCur;
		CListNode<CThing> *pNext = m_everythingHead.m_pnNext;
		while (pNext->m_powner != NULL)
		{
			pCur = pNext;
			pNext = pNext->m_pnNext;

			pCur->m_powner->Resume();
		}

		// Resume the game time.  I don't think it matters whether this is done
		// before or after the CThing->Resume() calls since game time never actually
		// advances unless CTime->Update() is called (when not suspended, of course).
		m_time.Resume();

		m_sNumSuspends--;

		ASSERT(m_sNumSuspends >= 0);

		// Resume active sounds.
		ResumeAllSamples();
	}
}

////////////////////////////////////////////////////////////////////////////////
// Update the realm
////////////////////////////////////////////////////////////////////////////////
void CRealm::Update(void)
{
	// We want to do this for every CThing in the realm.  We use iNext to get
	// the next iterator before calling the current item to avoid
	// a problem that could otherwise occur where iCur becomes invalidated during
	// the (*iCur)->Update() (like when a CThing deletes itself).
	// This causes an additional problem which is relatively easy to fix.  Since
	// we always insert at the end, if iCur is the last thing (meaning iNext is the
	// .end()), and that call adds a new thing at the end, that thing will never
	// get called b/c iNext already points to .end().  We effectively skip the
	// newly added item.
	// To avoid this, we simply, at the end, check to make sure iNext-- produces
	// iCur.  If not, we process iNext--.
	// Further, if the last item added two things, we need to go back two.

	rspStartProfile("Realm Update");

	// Entering update loop.
	m_bUpdating = true;

	// Do this for everything.
	CThing *pthing;
	m_pNext = m_everythingHead.m_pnNext;
	while (m_pNext->m_powner != NULL)
	{
		pthing = m_pNext->m_powner;
		m_pNext = m_pNext->m_pnNext;
		pthing->Update();
	}

	// Update the display timer
	m_lThisTime = m_time.GetGameTime();
	m_lElapsedTime = m_lThisTime - m_lPrevTime;
	if (m_bScoreTimerCountsUp)
		m_lScoreTimeDisplay += m_lElapsedTime;
	else
		m_lScoreTimeDisplay -= m_lElapsedTime;
	m_lPrevTime = m_lThisTime;

	// Leaving update loop.
	m_bUpdating = false;

	rspEndProfile("Realm Update");
}

////////////////////////////////////////////////////////////////////////////////
// Render the realm
////////////////////////////////////////////////////////////////////////////////
void CRealm::Render(void)
{
	// Entering update loop.
	m_bUpdating = true;

	// Do this for everything.
	CThing *pthing;
	m_pNext = m_everythingHead.m_pnNext;
	while (m_pNext->m_powner != NULL)
	{
		pthing = m_pNext->m_powner;
		m_pNext = m_pNext->m_pnNext;
		pthing->Render();
	}

	// Leaving update loop.
	m_bUpdating = false;
}

// This old way probably doesn't make sense any more since we're going to allow
// for multiple views of a realm.  I don't think we'd want to tell each object
// to render itself for each different view -- not unless the enter concept of
// what each item does to "render" itself changes.  Right now, objects merely
// update their representations in the scene, which really doesn't take too
// long, and needs to get done no matter what view we're talking about.
/*
void CRealm::Render(
	short sViewX,											// In:  X coord of view
	short sViewY,											// In:  Y coord of view
	short sViewW,											// In:  Width of view
	short sViewH,											// In:  Height of view
	RImage* pimDst,										// In:  Image to render to
	short sDstX,											// In:  X coord to draw to
	short sDstY)											// In:  Y coord to draw to
	{
	// It may turn out that some objects want to do their own clipping to see if
	// they need to add themselves to the scene.  I would guess this would be
	// the exception rather than the rule, so when (or if) such a requirement
	// comes up, we shouldn't pass the view info to each object, but instead
	// should create a function that objects can call to get the view info, the
	// idea being that this would be faster overall than passing all that data
	// to each object only to have it ignored most of the time.

	// Do this for everything
	for (CThing::Things::iterator i = m_everything.begin(); i != m_everything.end(); i++)
		(*i)->Render();

	// Render specified view to specified position in specified image
	m_scene.Render(
		sViewX,
		sViewY,
		sViewW,
		sViewH,
		pimDst,
		sDstX,
		sDstY);
	}
*/

/// todo
bool CRealm::IsEndOfLevelGoalPrompt()
{
	bool bEnd = true;

	switch (m_ScoringMode)
	{
	// In a standard level, the user is done when the percentage of hostiles killed
	// is greater than the minimum set in the level and the user presses the
	// 'next level' key.
	case Standard:
	case MPCampaignCoop:
		if (m_sHostileBirths != 0)
		{
			if (((m_sHostileKills * 100) / m_sHostileBirths < m_dKillsPercentGoal))
				bEnd = false;
		}		
		break;

	// In a timed level, the user is done when the time runs out, the population
	// runs out, or the user presses the 'next level' key.
	case Timed:
		if (m_lScoreTimeDisplay > 0 && m_sPopulation > 0)
			bEnd = false;
		break;

	// In a timed goal level, the user must meet the goal within the specified
	// time.
	case TimedGoal:
		if (m_lScoreTimeDisplay > 0 && m_sPopulationDeaths < m_sKillsGoal)
			bEnd = false;
		break;

	// In a timed flag level, the user must get the flag to a base before
	// the goal is considered met.
	case TimedFlag:
	case MPTimedFlag:
		//			if (m_lScoreTimeDisplay > 0 && m_sFlagsCaptured < m_sFlagsGoal)
		if (m_lScoreTimeDisplay > 0 && m_sFlagbaseCaptured < m_sFlagsGoal)
			bEnd = false;
		break;

	// In a capture the flag level, a user must capture a flag and return it
	// to a base to complete the level.
	case CaptureFlag:
	case MPCaptureFlag:
		if (m_sFlagbaseCaptured < m_sFlagsGoal)
			bEnd = false;
		break;

	// In a goal level, the user can only be done when they meet the goal.
	case Goal:
		if (m_sPopulationDeaths < m_sKillsGoal)
			bEnd = false;
		break;

	// In a checkpoint level, the user collects as many flags as possible and
	// can choose to end the level whenever they want (they'll just get a lower
	// score, if they have not gotten all the flags).
	case Checkpoint:
		if (m_sFlagsGoal == 0)
		{
			if (m_lScoreTimeDisplay > 0 && m_sFlagsCaptured < m_asClassNumThings[CThing::CFlagID])
				bEnd = false;
		}
		else
		{
			if (m_lScoreTimeDisplay > 0 && m_sFlagsCaptured < m_sFlagsGoal)
				bEnd = false;
		}
		break;

	case MPFrag:
		// Get highest number of kills from score module and
		if ((m_sKillsGoal < 1) || (ScoreHighestKills(this) < m_sKillsGoal))
			bEnd = false;
		break;

	case MPTimedFrag:
		if (m_lScoreTimeDisplay > 0 && ScoreHighestKills(this) < m_sKillsGoal)
			bEnd = false;
		break;

	case MPLastMan:
		// if (ScorePlayersRemaining() > 1)
		bEnd = false;
		break;

	case MPTimed:
		if (m_lScoreTimeDisplay > 0)
			bEnd = false;
		break;
	}

#if defined(DEBUG_LEVEL_CHEAT)
	//You can actually win a challenge without using this cheat.
	if (!bEnd)
	{
		return bEndLevelKey;
	}
#endif

	return bEnd;
}

////////////////////////////////////////////////////////////////////////////////
// IsEndOfLevelGoalMet - check to see if level is complete based on the
//								 scoring and game play mode.
////////////////////////////////////////////////////////////////////////////////
bool CRealm::IsEndOfLevelGoalMet(bool bEndLevelKey)
{
#ifdef MOBILE
	bool showAndroidKey = true;
	switch (m_ScoringMode)
	{
	case Standard:
		if (m_sHostileBirths != 0)
			if (((m_sHostileKills * 100) / m_sHostileBirths < m_dKillsPercentGoal))
				showAndroidKey = false;
		break;
	default: //Hide the next key for anything else for the moment
		showAndroidKey = false;
	}
	AndroidSetShowEndLevelKey(showAndroidKey);
#endif

	bool bEnd = true;

// todo?
	/*if (m_bPressedEndLevelKey)
	{
		m_bPressedEndLevelKey = false;
		// Hack: don't let the level end immediately if the player is using the debug level skip
		if (m_time.GetGameTime() > 1000)
			bEndLevelKey = true;
	}*/

	switch (m_ScoringMode)
	{
	// In a standard level, the user is done when the percentage of hostiles killed
	// is greater than the minimum set in the level and the user presses the
	// 'next level' key.
	case Standard:
	case MPCampaignCoop:
		if (m_sHostileBirths != 0)
		{
			if (((m_sHostileKills * 100) / m_sHostileBirths < m_dKillsPercentGoal) || !bEndLevelKey)
				bEnd = false;
		}
		else
		{
			if (!bEndLevelKey)
				bEnd = false;
		}
		break;

	// In a timed level, the user is done when the time runs out, the population
	// runs out, or the user presses the 'next level' key.
	case Timed:
		if (m_lScoreTimeDisplay > 0 && m_sPopulation > 0 && !bEndLevelKey)
			bEnd = false;
		break;

	// In a timed goal level, the user must meet the goal within the specified
	// time.
	case TimedGoal:
		if (m_lScoreTimeDisplay > 0 && m_sPopulationDeaths < m_sKillsGoal)
			bEnd = false;
		break;

	// In a timed flag level, the user must get the flag to a base before
	// the goal is considered met.
	case TimedFlag:
	case MPTimedFlag:
		//			if (m_lScoreTimeDisplay > 0 && m_sFlagsCaptured < m_sFlagsGoal)
		if (m_lScoreTimeDisplay > 0 && m_sFlagbaseCaptured < m_sFlagsGoal)
			bEnd = false;
		break;

	// In a capture the flag level, a user must capture a flag and return it
	// to a base to complete the level.
	case CaptureFlag:
	case MPCaptureFlag:
		if (m_sFlagbaseCaptured < m_sFlagsGoal)
			bEnd = false;
		break;

	// In a goal level, the user can only be done when they meet the goal.
	case Goal:
		if (m_sPopulationDeaths < m_sKillsGoal)
			bEnd = false;
		break;

	// In a checkpoint level, the user collects as many flags as possible and
	// can choose to end the level whenever they want (they'll just get a lower
	// score, if they have not gotten all the flags).
	case Checkpoint:
		if (m_sFlagsGoal == 0)
		{
			if (m_lScoreTimeDisplay > 0 && m_sFlagsCaptured < m_asClassNumThings[CThing::CFlagID])
				bEnd = false;
		}
		else
		{
			if (m_lScoreTimeDisplay > 0 && m_sFlagsCaptured < m_sFlagsGoal && !bEndLevelKey)
				bEnd = false;
		}
		break;

	case MPFrag:
		// Get highest number of kills from score module and
		if ((m_sKillsGoal < 1) || (ScoreHighestKills(this) < m_sKillsGoal))
			bEnd = false;
		break;

	case MPTimedFrag:
		if (m_lScoreTimeDisplay > 0 && ScoreHighestKills(this) < m_sKillsGoal)
			bEnd = false;
		break;

	case MPLastMan:
		// if (ScorePlayersRemaining() > 1)
		bEnd = false;
		break;

	case MPTimed:
		if (m_lScoreTimeDisplay > 0)
			bEnd = false;
		break;
	}

#if defined(DEBUG_LEVEL_CHEAT)
	//You can actually win a challenge without using this cheat.
	if (!bEnd)
	{
		return bEndLevelKey;
	}
#endif

	return bEnd;
}

////////////////////////////////////////////////////////////////////////////////
// Determine if a path is clear of terrain.
////////////////////////////////////////////////////////////////////////////////
bool CRealm::IsPathClear(				// Returns true, if the entire path is clear.
										// Returns false, if only a portion of the path is clear.
										// (see *psX, *psY, *psZ).
	int16_t sX,							// In:  Starting X.
	int16_t sY,							// In:  Starting Y.
	int16_t sZ,							// In:  Starting Z.
	int16_t sRotY,						// In:  Rotation around y axis (direction on X/Z plane).
	double dCrawlRate,					// In:  Rate at which to scan ('crawl') path in pixels per
										// iteration.
										// NOTE: Values less than 1.0 are inefficient.
										// NOTE: We scan terrain using GetHeight()
										// at only one pixel.
										// NOTE: We could change this to a speed in pixels per second
										// where we'd assume a certain frame rate.
	int16_t sDistanceXZ,				// In:  Distance on X/Z plane.
	int16_t sVerticalTolerance /*= 0*/, // In:  Max traverser can step up.
	int16_t *psX /*= NULL*/,			// Out: If not NULL, last clear point on path.
	int16_t *psY /*= NULL*/,			// Out: If not NULL, last clear point on path.
	int16_t *psZ /*= NULL*/,			// Out: If not NULL, last clear point on path.
	bool bCheckExtents /*= true*/)		// In:  If true, will consider the edge of the realm a path
										// inhibitor.  If false, reaching the edge of the realm
										// indicates a clear path.
{
	bool bEntirelyClear = false; // Assume entire path is not clear.

	////////////////////////// Traverse path ///////////////////////////////////

	// Get most efficient increments that won't miss any attributes.
	// For the rates we use trig with a hypotenuse of 1 which will give
	// us a rate <= 1.0 and then multiply by the the crawl for
	// a reasonable increase in the speed of this alg.

	// sAngle must be between 0 and 359.
	sRotY = rspMod360(sRotY);

	float fRateX = rspCos(sRotY) * dCrawlRate;
	float fRateZ = -rspSin(sRotY) * dCrawlRate;
	float fRateY = 0.0; // If we ever want vertical movement . . .

	// Set initial position to first point to check (NEVER checks original position).
	float fPosX = sX + fRateX;
	float fPosY = sY + fRateY;
	float fPosZ = sZ + fRateZ;

	// Determine amount traveled per iteration on X/Z plane just once.
	float fIterDistXZ = rspSqrt(ABS2(fRateX, fRateZ));

	float fTotalDistXZ = 0.0F;

	// Store extents.
	int16_t sMaxX = GetRealmWidth();
	int16_t sMaxZ = GetRealmHeight();

	int16_t sMinX = 0;
	int16_t sMinZ = 0;

	int16_t sCurH;

	bool bInsurmountableHeight = false;

	// Scan while in realm.
	while (
		fPosX > sMinX && fPosZ > sMinZ && fPosX < sMaxX && fPosZ < sMaxZ && fTotalDistXZ < sDistanceXZ)
	{
		sCurH = GetHeight((int16_t)fPosX, (int16_t)fPosZ);
		// If too big a height difference . . .
		if (sCurH - fPosY > sVerticalTolerance)
		{
			bInsurmountableHeight = true;
			break;
		}

		// Update position.
		fPosX += fRateX;
		fPosY = MAX(fPosY, (float)sCurH);
		fPosZ += fRateZ;
		// Update distance travelled on X/Z plane.
		fTotalDistXZ += fIterDistXZ;
	}

	// Set end pt.
	SET(psX, fPosX);
	SET(psY, fPosY);
	SET(psZ, fPosZ);

	// If we made it the whole way . . .
	if (fTotalDistXZ >= sDistanceXZ)
	{
		bEntirelyClear = true;
	}
	// Else, if we didn't hit any terrain . . .
	else if (bInsurmountableHeight == false)
	{
		// Only clear if we are not checking extents.
		bEntirelyClear = !bCheckExtents;
	}

#if 0
	// FEEDBACK.
	// Create a line sprite.
	CSpriteLine2d*	psl2d	= new CSpriteLine2d;
	if (psl2d != NULL)
		{
		Map3Dto2D(
			sX, 
			sY, 
			sZ, 
			&(psl2d->m_sX2), 
			&(psl2d->m_sY2) );
		Map3Dto2D(
			fPosX, 
			fPosY, 
			fPosZ, 
			&(psl2d->m_sX2End), 
			&(psl2d->m_sY2End) );
		psl2d->m_sPriority	= sZ;
		psl2d->m_sLayer		= GetLayerViaAttrib(GetLayer(sX, sZ));
		psl2d->m_u8Color		= (bEntirelyClear == false) ? 249 : 250;
		// Destroy when done.
		psl2d->m_sInFlags	= CSprite::InDeleteOnRender;
		// Put 'er there.
		m_scene.UpdateSprite(psl2d);
		}
#endif

	return bEntirelyClear;
}

////////////////////////////////////////////////////////////////////////////////
// Determine if a path is clear of terrain.
////////////////////////////////////////////////////////////////////////////////
bool CRealm::IsPathClear(				// Returns true, if the entire path is clear.
										// Returns false, if only a portion of the path is clear.
										// (see *psX, *psY, *psZ).
	int16_t sX,							// In:  Starting X.
	int16_t sY,							// In:  Starting Y.
	int16_t sZ,							// In:  Starting Z.
	double dCrawlRate,					// In:  Rate at which to scan ('crawl') path in pixels per
										// iteration.
										// NOTE: Values less than 1.0 are inefficient.
										// NOTE: We scan terrain using GetHeight()
										// at only one pixel.
										// NOTE: We could change this to a speed in pixels per second
										// where we'd assume a certain frame rate.
	int16_t sDstX,						// In:  Destination X.
	int16_t sDstZ,						// In:  Destination Z.
	int16_t sVerticalTolerance /*= 0*/, // In:  Max traverser can step up.
	int16_t *psX /*= NULL*/,			// Out: If not NULL, last clear point on path.
	int16_t *psY /*= NULL*/,			// Out: If not NULL, last clear point on path.
	int16_t *psZ /*= NULL*/,			// Out: If not NULL, last clear point on path.
	bool bCheckExtents /*= true*/)		// In:  If true, will consider the edge of the realm a path
										// inhibitor.  If false, reaching the edge of the realm
										// indicates a clear path.
{
	// todo?
	//int16_t	sDistanceXZ	= rspSqrt(ABS2(sDstX - sX, sZ - sDstZ));
	int16_t sDistanceXZ = int16_t(rspSqrt(ABS2(sDstX - sX, sZ - sDstZ)));
	int16_t sRotY = rspATan(sZ - sDstZ, sDstX - sX);

	return IsPathClear(		// Returns true, if the entire path is clear.
							// Returns false, if only a portion of the path is clear.
							// (see *psX, *psY, *psZ).
		sX,					// In:  Starting X.
		sY,					// In:  Starting Y.
		sZ,					// In:  Starting Z.
		sRotY,				// In:  Rotation around y axis (direction on X/Z plane).
		dCrawlRate,			// In:  Rate at which to scan ('crawl') path in pixels per
							// iteration.
							// NOTE: Values less than 1.0 are inefficient.
							// NOTE: We scan terrain using GetHeight()
							// at only one pixel.
							// NOTE: We could change this to a speed in pixels per second
							// where we'd assume a certain frame rate.
		sDistanceXZ,		// In:  Distance on X/Z plane.
		sVerticalTolerance, // In:  Max traverser can step up.
		psX,				// Out: If not NULL, last clear point on path.
		psY,				// Out: If not NULL, last clear point on path.
		psZ,				// Out: If not NULL, last clear point on path.
		bCheckExtents);		// In:  If true, will consider the edge of the realm a path
							// inhibitor.  If false, reaching the edge of the realm
							// indicates a clear path.
}

////////////////////////////////////////////////////////////////////////////////
// Gives this realm an opportunity and drawing surface to display its
// current status.
////////////////////////////////////////////////////////////////////////////////
void CRealm::DrawStatus( // Returns nothing.
	RImage *pim,		 // In:  Image in which to draw status.
	RRect *prc)			 // In:  Rectangle in which to draw status.  Clips to.
{
}

////////////////////////////////////////////////////////////////////////////////
// Maps a 3D coordinate onto the viewing plane provided the view angle
// (~angle of projection).
////////////////////////////////////////////////////////////////////////////////
void CRealm::Map3Dto2D( // Returns nothing.
	int16_t sX,			// In.
	int16_t sY,			// In.
	int16_t sZ,			// In.
	int16_t *psX,		// Out.
	int16_t *psY)		// Out.
{
	::Map3Dto2D(sX, sY, sZ, psX, psY, m_phood->GetRealmRotX());
}

////////////////////////////////////////////////////////////////////////////////
// Maps a 3D coordinate onto the viewing plane provided the view angle
// (~angle of projection).
////////////////////////////////////////////////////////////////////////////////
void CRealm::Map3Dto2D( // Returns nothing.
	double dX,			// In.
	double dY,			// In.
	double dZ,			// In.
	double *pdX,		// Out.
	double *pdY)		// Out.
{
	::Map3Dto2D(dX, dY, dZ, pdX, pdY, m_phood->GetRealmRotX());
}

////////////////////////////////////////////////////////////////////////////////
// Scales a Z coordinate onto the viewing plane using the
// view angle (~angle of projection).
////////////////////////////////////////////////////////////////////////////////
void CRealm::MapZ3DtoY2D( // Returns nothing.
	double dZIn,		  // In.
	double *pdYOut)		  // Out.
{
	::MapZ3DtoY2D(dZIn, pdYOut, m_phood->GetRealmRotX());
}

////////////////////////////////////////////////////////////////////////////////
// Scales a Z coordinate onto the viewing plane using the
// view angle (~angle of projection).
////////////////////////////////////////////////////////////////////////////////
void CRealm::MapZ3DtoY2D( // Returns nothing.
	int16_t sZIn,		  // In.
	int16_t *psYOut)	  // Out.
{
	::MapZ3DtoY2D(sZIn, psYOut, m_phood->GetRealmRotX());
}

////////////////////////////////////////////////////////////////////////////////
// Scales a Y coordinate from the viewing plane using the
// view angle (~angle of projection).
////////////////////////////////////////////////////////////////////////////////
void CRealm::MapY2DtoZ3D( // Returns nothing.
	double dYIn,		  // In.
	double *pdZOut)		  // Out.
{
	::MapY2DtoZ3D(dYIn, pdZOut, m_phood->GetRealmRotX());
}

////////////////////////////////////////////////////////////////////////////////
// Scales a Y coordinate from the viewing plane using the
// view angle (~angle of projection).
////////////////////////////////////////////////////////////////////////////////
void CRealm::MapY2DtoZ3D( // Returns nothing.
	int16_t sYIn,		  // In.
	int16_t *psZOut)	  // Out.
{
	::MapY2DtoZ3D(sYIn, psZOut, m_phood->GetRealmRotX());
}

////////////////////////////////////////////////////////////////////////////////
// Scales a Y coordinate onto the viewing plane using the
// view angle (~angle of projection).
////////////////////////////////////////////////////////////////////////////////
void CRealm::MapY3DtoY2D( // Returns nothing.
	double dYIn,		  // In.
	double *pdYOut)		  // Out.
{
	::MapY3DtoY2D(dYIn, pdYOut, m_phood->GetRealmRotX());
}

////////////////////////////////////////////////////////////////////////////////
// Scales a Y coordinate onto the viewing plane using the
// view angle (~angle of projection).
////////////////////////////////////////////////////////////////////////////////
void CRealm::MapY3DtoY2D( // Returns nothing.
	int16_t sYIn,		  // In.
	int16_t *psYOut)	  // Out.
{
	::MapY3DtoY2D(sYIn, psYOut, m_phood->GetRealmRotX());
}

////////////////////////////////////////////////////////////////////////////////
// Scales a Y coordinate from the viewing plane using the
// view angle (~angle of projection).
////////////////////////////////////////////////////////////////////////////////
void CRealm::MapY2DtoY3D( // Returns nothing.
	double dYIn,		  // In.
	double *pdYOut)		  // Out.
{
	::MapY2DtoY3D(dYIn, pdYOut, m_phood->GetRealmRotX());
}

////////////////////////////////////////////////////////////////////////////////
// Scales a Y coordinate from the viewing plane using the
// view angle (~angle of projection).
////////////////////////////////////////////////////////////////////////////////
void CRealm::MapY2DtoY3D( // Returns nothing.
	int16_t sYIn,		  // In.
	int16_t *psYOut)	  // Out.
{
	::MapY2DtoY3D(sYIn, psYOut, m_phood->GetRealmRotX());
}

////////////////////////////////////////////////////////////////////////////////
// If enabled, scales the specified height based on the view angle.
////////////////////////////////////////////////////////////////////////////////
void CRealm::MapAttribHeight( // Returns nothing.
	int16_t sHIn,			  // In.
	int16_t *psHOut)		  // Out.
{
	// If scaling attrib map heights . . .
	if (m_phood->m_sScaleAttribHeights != FALSE)
	{
		int16_t sRotX = m_phood->GetRealmRotX();

		// Scale into realm.
		::MapY2DtoY3D(sHIn, psHOut, sRotX);
	}
	else
	{
		*psHOut = sHIn;
	}
}

////////////////////////////////////////////////////////////////////////////////
//// Terrrain map access functions /////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
// Note these had no comments describing their function so I made some very
// vague comments that I hope were accurate -- JMI	06/28/97.

// Get the terrain height at an x/z position.
// Zero, if off map.
int16_t CRealm::GetHeight(int16_t sX, int16_t sZ)
{
	int16_t sRotX = m_phood->GetRealmRotX();
	// Scale the Z based on the view angle.
	::MapZ3DtoY2D(sZ, &sZ, sRotX);

	int16_t sH = 4 * (m_pTerrainMap->GetVal(sX, sZ, 0x0000) & REALM_ATTR_HEIGHT_MASK);

	// Scale into realm.
	MapAttribHeight(sH, &sH);

	return sH;
}

// Get the height and 'not walkable' status at the specified location.
// 'No walk', if off map.
int16_t CRealm::GetHeightAndNoWalk( // Returns height at new location.
	int16_t sX,						// In:  X position to check on map.
	int16_t sZ,						// In:  Z position to check on map.
	bool *pbNoWalk)					// Out: true, if 'no walk'.
{
	int16_t sRotX = m_phood->GetRealmRotX();
	// Scale the Z based on the view angle.
	::MapZ3DtoY2D(sZ, &sZ, sRotX);

	U16 u16Attrib = m_pTerrainMap->GetVal(sX, sZ, REALM_ATTR_NOT_WALKABLE);

	int16_t sH = 4 * (u16Attrib & REALM_ATTR_HEIGHT_MASK);

	// Scale into realm.
	MapAttribHeight(sH, &sH);

	// Get 'no walk'.
	if (u16Attrib & REALM_ATTR_NOT_WALKABLE)
	{
		*pbNoWalk = true;
	}
	else
	{
		*pbNoWalk = false;
	}

	return sH;
}

// Get the terrain attributes at an x/z position.
// 'No walk', if off map.
int16_t CRealm::GetTerrainAttributes(int16_t sX, int16_t sZ)
{
	// Scale the Z based on the view angle.
	::MapZ3DtoY2D(sZ, &sZ, m_phood->GetRealmRotX());

	return m_pTerrainMap->GetVal(sX, sZ, REALM_ATTR_NOT_WALKABLE);
}

// Get the floor attributes at an x/z position.
// Zero, if off map.
int16_t CRealm::GetFloorAttribute(int16_t sX, int16_t sZ)
{
	// Scale the Z based on the view angle.
	::MapZ3DtoY2D(sZ, &sZ, m_phood->GetRealmRotX());

	return m_pTerrainMap->GetVal(sX, sZ, 0) & REALM_ATTR_FLOOR_MASK;
}

// Get the floor value at an x/z position.
// sMask, if off map.
int16_t CRealm::GetFloorMapValue(int16_t sX, int16_t sZ, int16_t sMask /* = 0x007f*/)
{
	// Scale the Z based on the view angle.
	::MapZ3DtoY2D(sZ, &sZ, m_phood->GetRealmRotX());

	return m_pTerrainMap->GetVal(sX, sZ, sMask);
}

// Get the all alpha and opaque layer bits at an x/z position.
// Zero, if off map.
int16_t CRealm::GetLayer(int16_t sX, int16_t sZ)
{
	// Scale the Z based on the view angle.
	::MapZ3DtoY2D(sZ, &sZ, m_phood->GetRealmRotX());

	return m_pLayerMap->GetVal(sX, sZ, 0) & REALM_ATTR_LAYER_MASK;
}

// Get effect attributes at an x/z position.
// Zero, if off map.
int16_t CRealm::GetEffectAttribute(int16_t sX, int16_t sZ)
{
	// Scale the Z based on the view angle.
	::MapZ3DtoY2D(sZ, &sZ, m_phood->GetRealmRotX());

	return m_pTerrainMap->GetVal(sX, sZ, 0) & REALM_ATTR_EFFECT_MASK;
}

// Get effect value at an x/z position.
// Zero, if off map.
int16_t CRealm::GetEffectMapValue(int16_t sX, int16_t sZ)
{
	// Scale the Z based on the view angle.
	::MapZ3DtoY2D(sZ, &sZ, m_phood->GetRealmRotX());

	return m_pTerrainMap->GetVal(sX, sZ, 0);
}

////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
// Makes a 2D path based on the current hood setting for 'Use top-view 2Ds'.
// Note that this function returns to you a ptr to its one and only static
// string of length RSP_MAX_PATH.  Do not write to this string and do not
// store this string.  It is best to just use this call to pass a string to
// a function that will just use it right away (i.e., will not store it or
// modify it).
////////////////////////////////////////////////////////////////////////////////
const char *CRealm::Make2dResPath( // Returns a ptr to an internal static buffer
								   // containing the passed string, pszResName,
								   // preceded by the appropriate directory based
								   // on the current hood settings.
	const char *pszResName)		   // In:  Resource name to prepend path to.
{
	static char szFullPath[RSP_MAX_PATH];

	ASSERT(m_s2dResPathIndex < NUM_ELEMENTS(ms_apsz2dResPaths));

	// Get resource path.
	char *pszPath = ms_apsz2dResPaths[m_s2dResPathIndex];

	ASSERT(strlen(pszPath) + strlen(pszResName) < sizeof(szFullPath));

	strcpy(szFullPath, pszPath);
	strcat(szFullPath, pszResName);

	return szFullPath;
}

////////////////////////////////////////////////////////////////////////////////
// Creates the layer map, if it has not already been done.
// Now that the layer map needs to be 32K of uncompressable data, we create it
// at run time.
// (static)
////////////////////////////////////////////////////////////////////////////////
void CRealm::CreateLayerMap(void)
{
	// If table needs to be built . . .
	if (ms_asAttribToLayer[0] != LayerSprite16)
	{
		int32_t l;
		for (l = 0; l < NUM_ELEMENTS(ms_asAttribToLayer); l++)
		{
			if (l & 0x0001)
				ms_asAttribToLayer[l] = LayerSprite1;
			else if (l & 0x0002)
				ms_asAttribToLayer[l] = LayerSprite2;
			else if (l & 0x0004)
				ms_asAttribToLayer[l] = LayerSprite3;
			else if (l & 0x0008)
				ms_asAttribToLayer[l] = LayerSprite4;
			else if (l & 0x0010)
				ms_asAttribToLayer[l] = LayerSprite5;
			else if (l & 0x0020)
				ms_asAttribToLayer[l] = LayerSprite6;
			else if (l & 0x0040)
				ms_asAttribToLayer[l] = LayerSprite7;
			else if (l & 0x0080)
				ms_asAttribToLayer[l] = LayerSprite8;
			else if (l & 0x0100)
				ms_asAttribToLayer[l] = LayerSprite9;
			else if (l & 0x0200)
				ms_asAttribToLayer[l] = LayerSprite10;
			else if (l & 0x0400)
				ms_asAttribToLayer[l] = LayerSprite11;
			else if (l & 0x0800)
				ms_asAttribToLayer[l] = LayerSprite12;
			else if (l & 0x1000)
				ms_asAttribToLayer[l] = LayerSprite13;
			else if (l & 0x2000)
				ms_asAttribToLayer[l] = LayerSprite14;
			else if (l & 0x4000)
				ms_asAttribToLayer[l] = LayerSprite15;
			else
				ms_asAttribToLayer[l] = LayerSprite16;
		}
	}
}

////////////////////////////////////////////////////////////////////////////////
// EOF
////////////////////////////////////////////////////////////////////////////////
