////////////////////////////////////////////////////////////////////////////////
//
// 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
//
// Score.cpp
// Project: Postal
//
//	Description:
//		This module will be used to collect and display the scoring for the
//		game.  Characters that die will call a function to register the kill
//		giving their ID and the ID of the character that killed them.  Play
//		will call a function to update the display for the score and depending
//		on the mode of scoring, this module will draw the score into the given
//		image.  The first scoring mode will be for multiplayer where all 8
//		players will have the number of kills displayed with their names.  
//		Single player score may show something like Number of victims and
//		number of enemies.  A timed challenge level may show the kills and the
//		time remaining.  
// 
// History:
//		
//		06/11/97 BRH	Started this module for scoring of different types.  
//
//		06/12/97 BRH	Added Scoring display for multiplayer and moved the 
//							single player score from Realm to the same display
//							function here in score.
//
//		06/13/97 BRH	Fixed the formatting for multiplayer mode.
//
//		06/15/97 BRH	Took out temporary code that forced it into multiplayer
//							display for testing.  Put back the normal code to 
//							work for either single or multiplayer mode.
//
//		06/16/97 BRH	Added a reset function to reset the scores and 
//							display time.
//
//		06/17/97 BRH	Fixed the display area for the score.
//
//		06/26/97 BRH	Changed the score so that the population number goes
//							down when someone is killed.
//
//		06/29/97 BRH	Separated the score and status lines so that the mission
//							goal can be displayed for a short period of time while
//							the score displays constantly.
//
//		07/04/97 BRH	Added scoring displays for some of the other types
//							of games, timed, goal, capture the flag, etc.
//
//		07/06/97 BRH	Added scoring displays and staus lines for the challenge
//							levels.
//
//		07/07/97	JMI	Now ScoreUpdateDisplay() returns true if it drew into
//							the passed in buffer and false otherwise.
//
//		07/08/97 BRH	Changed the check for scoring mode from the scoreboard
//							to the realm's enumerated type.
//
//		07/14/97 BRH	Fixed clock format to use %2.2d to correctly display
//							2 digits for seconds.
//
//		07/22/97 BRH	Added ScoreDisplayHighScores function which is called
//							from Play.cpp after the goal level has been met.  This
//							will check the player's score against the top 5 stored
//							scores for the level and if they place, it will allow
//							them to enter their name.  The initial version of this
//							will just skip the dialog for now, but will soon
//							do a switch statement for each scoring mode.
//
//		07/26/97 BRH	Added loading and saving of high scores in a prefs type
//							file.  Now the only thing that needs to be changed is
//							to use the function that returns the realm name or
//							descriptive string which will be used as the section
//							name to find the scores particular to this realm.
//
//		07/27/97 BRH	Made use of the new realm variable m_lScoreIntialTime 
//							to calculate the elapsed time for two of the scoring
//							modes.
//
//		07/30/97 BRH	Used the realm's string to identify the current realm
//							being played so that the high scores for this realm
//							can be loaded and saved.
//
//		08/10/97	JRD	Modified ScoreUpdateDisplay to accept a hood pointer so
//							it could create a graphical back drop.
//
//		09/10/97	JRD	Added color matching and shadow drop to the upper score bar.
//
//		08/14/97 BRH	Changed location of gui for high scores, moved from 
//							/game/ to /shell directory so that the textures used
//							for it could be shared easier with the other shell gui's
//
//		08/17/97 MJR	Now uses g_resmgrShell to load gui's.  Also now frees
//							the resources (ooooohhh Bill!)
//
//		08/19/97 BRH	Fixed the problem with checkpoint scoring, probably 
//							some typo that Mike put in by accident.
//
//		08/19/97	JMI	There was a missing '}' ala Mike via Bill.
//
//		08/20/97 BRH	No thanks to Mike or Jon, I had to fix the multiplayer
//							score displays so that new time information would
//							fit on a third line.
//
//		08/25/97	JMI	Moved gsStatus* to toolbar.h.
//
//		08/28/97	JMI	Added GetRes/ReleaseRes callbacks to dialogs, PalTrans,
//							Postal Font, and sound to name edit field.
//
//		08/29/97 BRH	Changed challenge modes to use Population numbers
//							rather than just hostile numbers so that the victims
//							count also.
//
//		08/29/97	JMI	Now ScoreDisplayHighScores() quits immediately for
//							scoring modes that don't track high scores (currently,
//							only standard).
//
//		08/30/97	JMI	Now uses population deaths instead of hostile deaths
//							for timed challenge scoring.
//							Also, sets the palette every time the dialog is shown.
//
//		09/01/97	JMI	Now your high score is entered in the high score dialog
//							itself.  Also, now uses a listbox for high scores so
//							that we could edit merely one high score item and fill
//							the listbox with instances of that same item.
//
//		09/01/97	JMI	Now displays new highscore in different color.
//
//		09/02/97	JMI	Now ScoreDisplayHighScores() makes sure you're not playing
//							a demo before displaying the highscores.
//
//		09/02/97	JMI	Now has separate cases for determing scoring for every
//							mode in ScoreDisplayHighScores().
//
//		09/04/97 BRH	Fixed font size for the MPFrag scoring mode.
//
//		09/07/97	JMI	Added ability to display high scores for multiplayer.
//							Also, added optional high score display timeout.
//
//		09/07/97 BRH	Fixed problem with long multi player names wrapping
//							to the next line.
//
//		09/07/97	JMI	Now colors this player's score in MP mode.  Also, dialog
//							is much smaller so name length is restricted more.  But
//							does allow up to 16 scores now.
//
//		09/08/97 BRH	Fixed Goal and TimedGoal to display the Remaining: as
//							the number remaining in the goal, not the number of
//							people remaining in the realm.
//
//		09/08/97 BRH	Adjusted the posiiton of the MP mission goals since
//							the font size is smaller, they weren't centered properly.
//
//		09/08/97	JMI	Changed "Congratulations! Please enter your name for your 
//							score." to "Please enter your name, Asshole." but the end
//							clips off (just kidding).
//
//		09/08/97	JMI	Now sorts multiplayer names by score.
//
//		06/04/98	JMI	Strings used for storage and sorting of multiplayer names
//							were sized at MAX_PLAYER_NAME_LEN (to fit the high score
//							GUI without overlapping the score) but Postal's net 
//							client (from which the names are querried) allows longer
//							names (specifically Net::MaxPlayerNameSize).  Now only
//							copies first MAX_PLAYER_NAME_LEN chars from the player's
//							name.
//
//////////////////////////////////////////////////////////////////////////////

#define SCORE_CPP

//TODO:
#include <climits>

//////////////////////////////////////////////////////////////////////////////
// C Headers
//////////////////////////////////////////////////////////////////////////////

#include "RSPiX.h"
#include "score.h"
#include "dude.h"
#include "toolbar.h"
#include "update.h"

#include "pvr.h"

extern PVR::cRenderMgr *g_pvrRenderer;

//////////////////////////////////////////////////////////////////////////////
// Macros
//////////////////////////////////////////////////////////////////////////////

// Time, in ms, between status updates.
#define SCORE_UPDATE_INTERVAL			1000
#define STATUS_DISPLAY_TIMEOUT		8000

#define STATUS_PRINT_X					2
#define STATUS_PRINT_Y					0
#define STATUS_PRINT_Y2					14
#define STATUS_PRINT_Y3					28

#define STATUS_FONT_SIZE				19
#define STATUS_DISPLAY_HEIGHT			40

#define MP_FONT_SIZE						11 //15
#define MP_PRINT_X						2
#define MP_PRINT_Y1						0
#define MP_PRINT_Y2						11
#define MP_PRINT_Y3						22

//------------------------ These are color matched in the toolbar module

#define HIGHSCORE_NAMEDIALOG_FILE	"menu/addname.gui"
#define HIGHSCORE_DIALOG_FILE			"menu/hiscore.gui"
#define HIGHSCORE_ITEM_FILE			"menu/HiScoreItem.gui"
#define HIGH_SCORE_DLG

#define HIGHSCORE_SCORES_FILE						"savegame/high.ini"

#define TEXT_SHADOW_COLOR				220

#define TEXT_HIGHLIGHT_COLOR			9

// This path gets prepended to the resource path before passing the
// GUI res request to the resource manager.  This makes it easy to
// keep the path for the file simple while in the GUI Editor so the default
// handling can take care of it there.  The GUI Editor does not use the resmgr,
// it simply loads it blindly with no regard to its current directory.
#define GUI_RES_DIR						"menu/"

// Maximum name length.
#define MAX_PLAYER_NAME_LEN			17


#define MAX_HIGH_SCORES					8

// defaults to -1 for some reason
// #define LONG_MAX 0x7FFFFFFFL

//////////////////////////////////////////////////////////////////////////////
// Variables
//////////////////////////////////////////////////////////////////////////////

CScoreboard g_scoreboard;
RPrint		ms_print;

//////////////////////////////////////////////////////////////////////////////
// Functions.
//////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////
// Helper to make a time value.
//////////////////////////////////////////////////////////////////////////////
inline char* CreateTimeString(	// Returns time string.  No failures.
	int32_t	lTimeVal)					// In:  Time value in milliseconds.
	{
	static char	szTime[100];

	int16_t sMinutes = lTimeVal / 60000;
	int16_t	sSeconds = (lTimeVal / 1000) % 60;
	sprintf(szTime, "%2.2d:%2.2d", sMinutes, sSeconds);

	return szTime;
	}


//////////////////////////////////////////////////////////////////////////////
// GuiReleaseRes - Release a resource that the requesting GUI wants to 
// discard.
//////////////////////////////////////////////////////////////////////////////

static void GuiReleaseRes(	// Returns nothing.
	RGuiItem* pgui)			// In:  Requesting GUI.
	{		
	}

//////////////////////////////////////////////////////////////////////////////
// GuiGetRes - Get a resource that the requesting GUI wants to use for its
// background or state image.
//////////////////////////////////////////////////////////////////////////////

static int16_t GuiGetRes(		// Returns 0 on success; non-zero on failure.
	RGuiItem* pgui)			// In:  Requesting GUI.
	{
	int16_t sResult = -1;
	return sResult;
	}

//////////////////////////////////////////////////////////////////////////////
// EditInputUserFeedback -- Whines when the user causes an input 
// disgruntlement.
//////////////////////////////////////////////////////////////////////////////

static void EditInputUserFeedback(	// Called when a user input notification
												// should occur.
	REdit*	pedit)						// In:  Edit field.
	{
	PlaySample(g_smidEmptyWeapon, SampleMaster::UserFeedBack);
	}

////////////////////////////////////////////////////////////////////////////////
//
// Callback from RProcessGui for system update.
//
////////////////////////////////////////////////////////////////////////////////
static int32_t SysUpdate(			// Returns a non-zero ID to abort or zero
										// to continue.                          
	RInputEvent*	pie)			// Out: Next input event to process.     
	{
	int32_t	lIdRes	= 0;	// Assume no GUI ID pressed (i.e., continue).

	UpdateSystem();
	rspGetNextInputEvent(pie);

	return lIdRes;
	}

//////////////////////////////////////////////////////////////////////////////
// ScoreInit - Set up the RPrint for the score
//////////////////////////////////////////////////////////////////////////////

void ScoreInit(void)
{
	// Setup print.
	ms_print.SetFont(STATUS_FONT_SIZE, &g_fontBig);
	ms_print.SetColor(
		gsStatusFontForeIndex, 
		gsStatusFontBackIndex, 
		gsStatusFontShadowIndex);

	// Make sure shadow is one off:
	ms_print.SetEffectAbs(RPrint::SHADOW_X,+1);
	ms_print.SetEffectAbs(RPrint::SHADOW_Y,+1);
	
	// Warning!  This may need to be set back again for gui items - if so, use 
	// my OWN print!

	
}

//////////////////////////////////////////////////////////////////////////////
// ScoreReset - Reset the scores and the display time
//////////////////////////////////////////////////////////////////////////////

void ScoreReset(void)
{
	g_scoreboard.Reset();
}

//////////////////////////////////////////////////////////////////////////////
// ScoreResetDisplay - Reset the timer for the display before the start 
//							  of each realm
//////////////////////////////////////////////////////////////////////////////

void ScoreResetDisplay(void)
{
	g_scoreboard.m_lLastStatusDrawTime = 0;
	g_scoreboard.m_lLastScoreDrawTime = 0;
}

//////////////////////////////////////////////////////////////////////////////
// ScoreRegisterKill - Characters should call this when they die
//////////////////////////////////////////////////////////////////////////////

void ScoreRegisterKill(CRealm* pRealm, U16 u16DeadGuy, U16 u16Killer)
{	
	CThing* pShooter = NULL;
	CThing* pCorpse = NULL;
	pRealm->m_idbank.GetThingByID(&pShooter, u16Killer);
	pRealm->m_idbank.GetThingByID(&pCorpse, u16DeadGuy);
	if (pShooter && pShooter->GetClassID() == CThing::CDudeID)
	{
		// Suicide or friendly fire.
		if (u16DeadGuy == u16Killer || pCorpse->GetClassID() == CThing::CDudeID)
		{
			g_scoreboard.SubtractOne(((CDude*) pShooter)->m_sDudeNum);			
		}
		else
			g_scoreboard.AddOne(((CDude*) pShooter)->m_sDudeNum);
	}	
}

void ScoreRegisterRevive(CRealm* pRealm, U16 u16Reviver)
{
	CThing* pReviver = NULL;
	pRealm->m_idbank.GetThingByID(&pReviver, u16Reviver);
	if (pReviver && pReviver->GetClassID() == CThing::CDudeID)
	{
		g_scoreboard.AddOne(((CDude*) pReviver)->m_sDudeNum, CScoreboard::Revives);
	}	
}

void ScoreRegisterDeath(CRealm* pRealm, U16 u16Corpse)
{
	CThing* pCorpse = NULL;
	pRealm->m_idbank.GetThingByID(&pCorpse, u16Corpse);
	if (pCorpse && pCorpse->GetClassID() == CThing::CDudeID)
	{
		g_scoreboard.AddOne(((CDude*) pCorpse)->m_sDudeNum, CScoreboard::Deaths);
	}	
}


//////////////////////////////////////////////////////////////////////////////
// ScoreUpdateDisplay - Display the score for the current mode
// Returns true, if pImage was updated; false otherwise.
//////////////////////////////////////////////////////////////////////////////
bool ScoreUpdateDisplayPVR(RRect* prc, CRealm* pRealm, int16_t sDstX,int16_t sDstY,CHood* pHood)
{
	RRect rcBox;
	RRect rcDst;
	rcDst.sX = prc->sX + STATUS_PRINT_X;
	rcDst.sY = prc->sY + STATUS_PRINT_Y;
	rcDst.sW = prc->sW - 2 * STATUS_PRINT_X;
	rcDst.sH = prc->sH - STATUS_PRINT_Y;
	int16_t sMinutes = int16_t(rspSlowDiv(float(pRealm->m_lScoreTimeDisplay), 60000.f));
	int16_t sSeconds = int16_t(rspSlowDiv(float(pRealm->m_lScoreTimeDisplay), 1000.f)) % 60;
	int32_t	lCurTime	= pRealm->m_time.GetGameTime();
	int16_t sNumDudes = pRealm->m_asClassNumThings[CThing::CDudeID];	
	int16_t i;
	bool	bDrew	= false;	// Assume we do not draw.	
	char *pScoreString;
	char *pGoalString;	

	ms_print.m_bColourFixed = false;
	if (g_GameSettings.m_cHudMode == 1)
	{		
		ms_print.SetWordWrap(1);
		rcDst.sY += 20;
		rcDst.sH += 50;
		ms_print.SetFont(24, &g_fontPostal);

	if (pRealm->IsSuspended())
		{
				ms_print.SetFont(0, &g_fontPostal);
			}

		ms_print.SetColor(	// set current color
			gsStatusFontShadowIndex,		
			gsStatusFontBackIndex, 
			gsStatusFontForeIndex
		);	
	}
	else
	{		
		ms_print.SetFont(STATUS_FONT_SIZE, &g_fontBig);
		ms_print.SetColor(	// set current color
		gsStatusFontForeIndex, 
		gsStatusFontBackIndex, 
		gsStatusFontShadowIndex);
	}

	if (g_GameSettings.m_cHudMode == 1)
	{				
		pScoreString = g_apszScoreDisplayTextMin[pRealm->m_ScoringMode][g_GameSettings.m_cActiveLanguage];
		pGoalString = g_apszScoreGoalTextMin[pRealm->m_ScoringMode][g_GameSettings.m_cActiveLanguage];
	}
	else
	{
		pScoreString = g_apszScoreDisplayText[pRealm->m_ScoringMode][g_GameSettings.m_cActiveLanguage];
		pGoalString = g_apszScoreGoalText[pRealm->m_ScoringMode][g_GameSettings.m_cActiveLanguage];
	}

	switch (pRealm->m_ScoringMode)
	{		
		case CRealm::Standard:
		case CRealm::MPCampaignCoop:
			ms_print.SetColumn(rcDst.sX, rcDst.sY, rcDst.sW, rcDst.sH);			
			if (g_GameSettings.m_cHudMode == 1)
			{				
				pScoreString = g_apszScoreDisplayTextMin[CRealm::Standard][g_GameSettings.m_cActiveLanguage];
			}
			else
			{
				pScoreString = g_apszScoreDisplayText[CRealm::Standard][g_GameSettings.m_cActiveLanguage];
			}

			ms_print.printPVR(					
				rcDst.sX, 
				rcDst.sY, 
				pScoreString,
				pRealm->m_sPopulation,
				pRealm->m_sHostiles,
				pRealm->m_sHostileKills,
				pRealm->m_sHostileKills * 100 / ((pRealm->m_sHostileBirths != 0) ? pRealm->m_sHostileBirths : 1),
				(int16_t) pRealm->m_dKillsPercentGoal
				);
		break;

		case CRealm::Timed:
				ms_print.SetColumn(rcDst.sX, rcDst.sY, rcDst.sW, rcDst.sH);
				 ms_print.printPVR(					
					rcDst.sX,
					rcDst.sY,
//					" Time Remaining %d:%2.2d                                Kills %d",
					pScoreString,
					sMinutes,
					sSeconds,
					pRealm->m_sPopulationDeaths
					);
		break;

		case CRealm::TimedGoal:
				ms_print.SetColumn(rcDst.sX, rcDst.sY, rcDst.sW, rcDst.sH);
				 ms_print.printPVR(					
					rcDst.sX,
					rcDst.sY,
					pScoreString,
					sMinutes,
					sSeconds,
					pRealm->m_sPopulationDeaths,
					pRealm->m_sKillsGoal - pRealm->m_sPopulationDeaths, pRealm->m_sPopulation
					);
		break;

		case CRealm::TimedFlag:
				ms_print.SetColumn(rcDst.sX, rcDst.sY, rcDst.sW, rcDst.sH);
				 ms_print.printPVR(					
					rcDst.sX,
					rcDst.sY,
					pScoreString,
					sMinutes,
					sSeconds
					);
		break;

		case CRealm::Goal:
				ms_print.SetColumn(rcDst.sX, rcDst.sY, rcDst.sW, rcDst.sH);
				 ms_print.printPVR(					
					rcDst.sX,
					rcDst.sY,
					pScoreString,
					pRealm->m_sPopulationDeaths,
					pRealm->m_sKillsGoal - pRealm->m_sPopulationDeaths /*pRealm->m_sPopulation*/,
					sMinutes,
					sSeconds
					);
		break;

		case CRealm::CaptureFlag:
				ms_print.SetColumn(rcDst.sX, rcDst.sY, rcDst.sW, rcDst.sH);
				 ms_print.printPVR(					
					rcDst.sX,
					rcDst.sY,
//					" Time Elapsed %d:%2.2d",
					pScoreString,
					sMinutes,
					sSeconds
					);
				break;

			case CRealm::Checkpoint:
				ms_print.SetColumn(rcDst.sX, rcDst.sY, rcDst.sW, rcDst.sH);
				 ms_print.printPVR(					
					rcDst.sX,
					rcDst.sY,
//					" Clock %d:%2.2d    You have %d flags    Flags Remaining %d",
					pScoreString,
					sMinutes,
					sSeconds,
					pRealm->m_sFlagsCaptured,
					pRealm->m_asClassNumThings[CThing::CFlagID] - pRealm->m_sFlagsCaptured
					);
				break;	
			default:
				break;
		}
		g_scoreboard.m_lLastScoreDrawTime	= lCurTime;
		
		int16_t offsetY = (g_GameSettings.m_cHudMode == 1) ? 30 : 0;
				
		// Display the status or mission statement line
		if (lCurTime < g_scoreboard.m_lLastStatusDrawTime + STATUS_DISPLAY_TIMEOUT)
		{				
			
			if (g_GameSettings.m_cHudMode == 1)
			{
				ms_print.SetColorRGB(255, 255, 0);
				ms_print.SetShadowColorRGB(64, 64, 0);
			}
							
			switch (pRealm->m_ScoringMode)
			{
				case CRealm::MPFrag:
					rcDst.sY = prc->sY + MP_PRINT_Y3 + offsetY;
					if (pRealm->m_sKillsGoal < 1)
					{
						rcDst.sX = prc->sX;
						ms_print.SetColumn(rcDst.sX, rcDst.sY, rcDst.sW, rcDst.sH);
						 ms_print.printPVR(
							rcDst.sX,
							rcDst.sY,
//							" There are no time or kill limits on this game - play as long as you like"
							g_apszScoreGoalText[CRealm::MPLastManTimedFrag][g_GameSettings.m_cActiveLanguage] // Cheating since this is
																							// Really none of the scoring modes
						);
					}
					else
					{
						rcDst.sX = prc->sX + 220;
						ms_print.SetColumn(rcDst.sX, rcDst.sY, rcDst.sW, rcDst.sH);
						 ms_print.printPVR(
							rcDst.sX,
							rcDst.sY,
//							" The first player to get %d kills wins",
							pGoalString,
							pRealm->m_sKillsGoal
						);
					}

					break;

				case CRealm::MPTimed:
					rcDst.sY = prc->sY + MP_PRINT_Y3 + offsetY;
					rcDst.sX = prc->sX + 180;
					ms_print.SetColumn(rcDst.sX, rcDst.sY, rcDst.sW, rcDst.sH);
					 ms_print.printPVR(
						rcDst.sX,
						rcDst.sY,
//						" The player with the most kills when time expires is the winner"
						pGoalString
						);
					break;

				case CRealm::MPTimedFrag:
					rcDst.sY = prc->sY + MP_PRINT_Y3 + offsetY;
					rcDst.sX = prc->sX + 220;
					ms_print.SetColumn(rcDst.sX, rcDst.sY, rcDst.sW, rcDst.sH);
					 ms_print.printPVR(
						rcDst.sX,
						rcDst.sY,
//						" Try to reach %d kills before time expires",
						pGoalString,
						pRealm->m_sKillsGoal
						);
					break;

				case CRealm::Standard:
					rcDst.sY = prc->sY + STATUS_PRINT_Y2 + offsetY;
					ms_print.SetColumn(rcDst.sX, rcDst.sY, rcDst.sW, rcDst.sH);
					 ms_print.printPVR(
						rcDst.sX,
						rcDst.sY,
//						"      You must kill %d%% of the hostiles.",
						pGoalString,
						(int16_t) pRealm->m_dKillsPercentGoal
						);
					break;

				case CRealm::Timed:
					//ms_print.SetFont(22, &g_fontPostal);
					rcDst.sY = prc->sY + STATUS_PRINT_Y2 + offsetY;
					ms_print.SetColumn(rcDst.sX, rcDst.sY, rcDst.sW, rcDst.sH);
					 ms_print.printPVR(
						rcDst.sX,
						rcDst.sY,
//						" Score as many kills as possible in the time remaining."
						pGoalString
						);
					break;

				case CRealm::TimedGoal:
					rcDst.sY = prc->sY + STATUS_PRINT_Y2;
					ms_print.SetColumn(rcDst.sX, rcDst.sY, rcDst.sW, rcDst.sH);
					 ms_print.printPVR(
						rcDst.sX,
						rcDst.sY,
//						" Kill everyone before time runs out."
						pGoalString
						);
					break;

				case CRealm::TimedFlag:
					rcDst.sY = prc->sY + STATUS_PRINT_Y2 + offsetY;
					ms_print.SetColumn(rcDst.sX, rcDst.sY, rcDst.sW, rcDst.sH);
					 ms_print.printPVR(
						rcDst.sX,
						rcDst.sY,
//						" Capture the flag before time runs out."
						pGoalString
						);
						break;

				case CRealm::Goal:
					rcDst.sY = prc->sY + STATUS_PRINT_Y2 + offsetY;
					ms_print.SetColumn(rcDst.sX, rcDst.sY, rcDst.sW, rcDst.sH);
					 ms_print.printPVR(
						rcDst.sX,
						rcDst.sY,
//						" Kill %d People in as little time as possible.",
						pGoalString,
						pRealm->m_sKillsGoal
						);
					break;

				case CRealm::CaptureFlag:
					rcDst.sY = prc->sY + STATUS_PRINT_Y2 + offsetY;
					ms_print.SetColumn(rcDst.sX, rcDst.sY, rcDst.sW, rcDst.sH);
					 ms_print.printPVR(
						rcDst.sX,
						rcDst.sY,
//						" Capture the flag in as little time as possible."
						pGoalString
						);
					break;

				case CRealm::Checkpoint:
					rcDst.sY = prc->sY + STATUS_PRINT_Y2 + offsetY;
					ms_print.SetColumn(rcDst.sX, rcDst.sY, rcDst.sW, rcDst.sH);
					 ms_print.printPVR(
						rcDst.sX,
						rcDst.sY,
//						" Grab as many flags as possible before time runs out."
						pGoalString
						);
					break;

				default:
					break;
			}
		}

		// Note that we drew.
		bDrew	= true;
	

	return bDrew;
}


//////////////////////////////////////////////////////////////////////////////
// ScoreDisplayStatus
//
//		Sets the status display timer so it will show for a few seconds
//////////////////////////////////////////////////////////////////////////////

void ScoreDisplayStatus(CRealm* pRealm)
{
	g_scoreboard.m_lLastStatusDrawTime = pRealm->m_time.GetGameTime();
}

//////////////////////////////////////////////////////////////////////////////
// ScoreSetMode - Set mode of scoring
//////////////////////////////////////////////////////////////////////////////

void ScoreSetMode(CScoreboard::ScoringMode Mode)
{
	g_scoreboard.SetScoringMode(Mode);
}

//////////////////////////////////////////////////////////////////////////////
// ScoreDisplayHighScores
//////////////////////////////////////////////////////////////////////////////

void ScoreDisplayHighScores(	// Returns nothing.
	CRealm* pRealm,				// In:  Realm won.		
	int16_t sRealmNum)
{
	RGuiItem* pguiRoot;
	RGuiItem::ms_print.SetFont(15, &g_fontPostal);
	RProcessGui guiDialog;
	int16_t sResult;
	char szScoringExplanation[512]	= "";
	int32_t alScores[MAX_HIGH_SCORES];
	char astrNames[MAX_HIGH_SCORES][MAX_PLAYER_NAME_LEN + 1];
	char szKeyName[256];
	RPrefs scores;

	typedef enum
		{
		Value,
		Time
		} ValType;

	ValType	vtScoringUnit	= Value;

	//char gg[100];
	//sprintf(gg, "%d", pRealm->id)
	TRACE("LONG_MAX = %d\n", LONG_MAX);
	// Let's just not do any of this for modes that have no scoring . . .
	if (pRealm->m_ScoringMode >= CRealm::Timed && pRealm->m_ScoringMode <= CRealm::MPCampaignCoop && GetInputMode() != INPUT_MODE_PLAYBACK)
	{
		// Determine player's score and note how we determined it in a string
		// for the user.
		int32_t	lPlayerScore	= 0;
		switch (pRealm->m_ScoringMode)
			{
			case CRealm::Standard:
				// No high scores for this mode
				break;

			case CRealm::MPCampaignCoop:
				sprintf(szScoringExplanation, g_apszScoreExplanations[pRealm->m_ScoringMode][g_GameSettings.m_cActiveLanguage]);
				// Number of deaths.
				lPlayerScore	= pRealm->m_sPopulationDeaths;
				vtScoringUnit	= Value;
				break;

			case CRealm::Timed:
				sprintf(szScoringExplanation, g_apszScoreExplanations[pRealm->m_ScoringMode][g_GameSettings.m_cActiveLanguage], CreateTimeString(pRealm->m_lScoreInitialTime) );
				// Number of deaths.
				lPlayerScore	= pRealm->m_sPopulationDeaths;
				vtScoringUnit	= Value;
				break;

			case CRealm::TimedGoal:
				sprintf(szScoringExplanation, g_apszScoreExplanations[pRealm->m_ScoringMode][g_GameSettings.m_cActiveLanguage], pRealm->m_sKillsGoal);
				// Elapsed time, if goal met.
				if (pRealm->m_sPopulationDeaths >= pRealm->m_sKillsGoal)
					{
					lPlayerScore	= pRealm->m_lScoreInitialTime - pRealm->m_lScoreTimeDisplay;
					}
				else
					{
					// Really bad elapsed time.
					lPlayerScore	= LONG_MAX;
					}

				vtScoringUnit	= Time;
				break;

			case CRealm::TimedFlag:
				sprintf(szScoringExplanation, g_apszScoreExplanations[pRealm->m_ScoringMode][g_GameSettings.m_cActiveLanguage], pRealm->m_sKillsGoal);
				// Elapsed time, if goal met.
				if (pRealm->m_sFlagbaseCaptured >= pRealm->m_sFlagsGoal)
					{
					lPlayerScore	= pRealm->m_lScoreInitialTime - pRealm->m_lScoreTimeDisplay;
					}
				else
					{
					// Really bad elapsed time.
					lPlayerScore	= LONG_MAX;
					}

				vtScoringUnit	= Time;
				break;

			case CRealm::CaptureFlag:
				sprintf(szScoringExplanation, g_apszScoreExplanations[pRealm->m_ScoringMode][g_GameSettings.m_cActiveLanguage], pRealm->m_sKillsGoal);
				// Time left, if goal met.
				if (pRealm->m_sFlagbaseCaptured >= pRealm->m_sFlagsGoal)
					{
					lPlayerScore	= pRealm->m_lScoreTimeDisplay;
					}
				else
					{
					// Really bad time.
					lPlayerScore	= LONG_MIN;
					}

				vtScoringUnit	= Time;
				break;

			case CRealm::Goal:
				sprintf(szScoringExplanation, g_apszScoreExplanations[pRealm->m_ScoringMode][g_GameSettings.m_cActiveLanguage], pRealm->m_sKillsGoal);
				// Time left, if goal met.
				if (pRealm->m_sPopulationDeaths >= pRealm->m_sKillsGoal)
					{
					lPlayerScore	= pRealm->m_lScoreTimeDisplay;
					}
				else
					{
					// Really bad time.
					lPlayerScore	= LONG_MIN;
					}

				vtScoringUnit	= Time;
				break;

			case CRealm::Checkpoint:
				sprintf(szScoringExplanation, g_apszScoreExplanations[pRealm->m_ScoringMode][g_GameSettings.m_cActiveLanguage], 0);
				// Number of flags captured.
				lPlayerScore	= pRealm->m_sFlagsCaptured;
				vtScoringUnit	= Value;
				break;

			case CRealm::MPTimed:
			case CRealm::MPFrag:
			case CRealm::MPLastMan:
			case CRealm::MPCaptureFlag:
			case CRealm::MPTimedFlag:
			case CRealm::MPTimedFrag:
			case CRealm::MPLastManFrag:
			case CRealm::MPLastManTimed:
			case CRealm::MPLastManTimedFrag:

				sprintf(szScoringExplanation, g_apszScoreExplanations[pRealm->m_ScoringMode][g_GameSettings.m_cActiveLanguage], MAX_HIGH_SCORES);
				vtScoringUnit	= Value;
				break;
			}

		// Get the name or description string for this realm file and use that as
		// the prefs section name from which to get the high scores.  Scores are
		// stored as longs whether they be times in seconds or counts and the names
		// are stored as strings.

		// Temporarily I will base the section name on the scoring method
		// until the realm description function is available.

//		static int16_t	sPlayersScorePosition	= -1;	// Valid score index once/if we find a slot for this player's score.
		int16_t sScoreNum = (g_sActivePlayers > PLAYER_1) ? g_cPlayerCount : MAX_HIGH_SCORES;
		RTxt**	pguiItems =	(RTxt**)malloc(sScoreNum * sizeof(RTxt*));
				
		// If not a multiplayer scoring . . .
		//if (g_sActivePlayers == PLAYER_1)
		{							
			int16_t	sSrcIndex;
			int16_t	sDstIndex;
			int32_t lCurScore = lPlayerScore;
			bool bHighScoreSet = false;		

			if (!g_bHighScoreUpdated && g_sActivePlayers == PLAYER_1)
			{
				g_bHighScoreUpdated = true;
			
				for (sDstIndex = 0; sDstIndex < MAX_HIGH_SCORES; sDstIndex++)
				{
					// Determine if our score beat this score.
					bool	bPlayerBeatThisScore	= false;

					// Some scoring modes need to default to zero scores, but the timed levels
					// need to default to some high, easy to beat time if there is no saved score
					alScores[sDstIndex] = g_HighScores[sRealmNum][sDstIndex];
				}

				for (sSrcIndex = 0, sDstIndex = 0; sDstIndex < MAX_HIGH_SCORES; sDstIndex++)
				{
					// Determine if our score beat this score.
					bool	bPlayerBeatThisScore	= false;
					int32_t lActualScore =  alScores[sDstIndex] & ~0xF8000000;		

					// Some scoring modes need to default to zero scores, but the timed levels
					// need to default to some high, easy to beat time if there is no saved score					
					if (vtScoringUnit == Value)
					{																				
						// Did we get a higher value than in score?
						if ((lCurScore & ~0xF8000000) >= lActualScore)
						{					
							bPlayerBeatThisScore = true;
						}
					}
					else
					{					
						if (lActualScore == 0 || lActualScore > 3600000)
						{
							lActualScore = 3600000;
						}											
			
						// Did we get a better time than read in score?
						if ((lCurScore & ~0xF8000000) <= lActualScore)
						{						
							bPlayerBeatThisScore	= true;
						}
					}
				
					// If we beat this score and haven't yet found a score position . . .
					if (bPlayerBeatThisScore == true)
					{					
						// Remember player's score.					
						int32_t tmp = alScores[sDstIndex];												 						
						g_bSaveHighScore = true;
						
						// Remember player's score position.
						if (!bHighScoreSet)
						{							
							switch (pRealm->m_flags.sDifficulty)
							{
								case 1:
									alScores[sDstIndex] = lCurScore | (1<<27);
								break;
								case 4:
									alScores[sDstIndex] = lCurScore | (1<<28);
								break;
								case 7:
									alScores[sDstIndex] = lCurScore | (1<<29);
								break;
								case 11:
									alScores[sDstIndex]	= lCurScore | (1<<30);
								break;
							}									
							bHighScoreSet = true;									
							g_sPlayerScorePos = sDstIndex;
						}						
						else
						{
							alScores[sDstIndex]	= lCurScore;
						}	

						lCurScore = tmp;
					}
					else
					{					
						// Move to the next source score val
						sSrcIndex++;						
					}												
				}

				for (int i = 0; i < MAX_HIGH_SCORES; i++)
				{					
					g_HighScores[sRealmNum][i] = alScores[i];
				}
			}
						
			if (rspGetResource(&g_resmgrShell, HIGHSCORE_DIALOG_FILE, (RDlg**)&pguiRoot) == 0)
			{			
				TRACE("Bazooper.\n");
									
				RTxt* ptextExplain1		= (RTxt*) pguiRoot->GetItemFromId(50);
				RTxt* ptextExplain2		= (RTxt*) pguiRoot->GetItemFromId(51);			
				// Create and add all score items.
				int16_t	sScoreIndex;
				bool	bGotAllScoreItems	= true;
		
				int16_t offY = 0;				
							
				if (ptextExplain1 != NULL && ptextExplain2 != NULL)				 
				{						
					// Recompose the root item (does not recompose children).  This time
					// it should successfully find the resources via the Get callback.
					pguiRoot->Compose();
																		
					// Make the explanation texts shadowed.
					ptextExplain1->m_sTextEffects			|= RGuiItem::Shadow;
					ptextExplain1->m_u32TextShadowColor	=	TEXT_SHADOW_COLOR;
				
					ptextExplain2->m_sTextEffects			|= RGuiItem::Shadow;
					ptextExplain2->m_u32TextShadowColor	=	TEXT_SHADOW_COLOR;					
					ptextExplain2->Compose();
									
					// Set the explanation text.
					ptextExplain1->SetText("%s", szScoringExplanation);
					ptextExplain1->Compose();
					
					// This time we want the screen cleared.
					guiDialog.m_sFlags	= 0;
					// Display the high scores.
					TRACE("Do you know what 'modal' means?\n");

					pguiRoot->isPvr = true;
					pguiRoot->widthPvr = 450;
					pguiRoot->heightPvr = 460;
				
					// todo: clean up
					uint16_t menuWidth = 450;
					uint16_t menuHeight = 460;									
					int16_t sX, sY;					
					PVR::cTexture *bg = g_pvrRenderer->m_TextureRepository.Get(PVR_TEXTURE_MENU_BG);
	
					// Center the GUI root.
					pguiRoot->Move(
					640 / 2 - pguiRoot->widthPvr / 2,
					480 / 2 - pguiRoot->heightPvr / 2);

					g_pvrRenderer->DrawQuadDirect(
						bg,
						pguiRoot->m_sX,
						pguiRoot->m_sY,
						menuWidth,
						menuHeight,
						43); // todo: layer	
					
					pguiRoot->DrawPVR();


					RPrint print;
					// Create text layer
					print.SetFont(28, &g_fontPostal);
					print.SetColorRGB(192, 0, 0);									
					print.SetEffectAbs(RPrint::SHADOW_X, 1);
					print.SetEffectAbs(RPrint::SHADOW_Y, 1);
					print.SetWordWrap(TRUE);
					print.SetColumn(0, 0, 300, 200);	
					print.offsetX = pguiRoot->m_sX;
					print.offsetY = 360;

					switch (g_GameSettings.m_cActiveLanguage)
					{
						case 0:
							print.offsetX += 140;
						break;
						case 1:
							print.offsetX += 125;
						break;
						case 2:
							print.offsetX += 105;
						break;
						case 3:
							print.offsetX += 140;
						break;
						case 4:
							print.offsetX += 140;
						break;
						case 5:
							print.offsetX += 140;
						break;
					}

					print.printPVR(0, 0, g_pszPressStartUc[g_GameSettings.m_cActiveLanguage]);

					for (sScoreIndex = 0; sScoreIndex < sScoreNum; sScoreIndex++)
					{						
						if (rspGetResourceInstance(&g_resmgrShell, HIGHSCORE_ITEM_FILE, &pguiItems[sScoreIndex]) == 0)
						{
							RTxt*	pguiScore = (RTxt*)pguiItems[sScoreIndex]->GetItemFromId(101);
							RTxt*	pguiPlace = (RTxt*)pguiItems[sScoreIndex]->GetItemFromId(102);
							char*	pszDifficulty = nullptr;	

							if (pguiScore)
							{
								// Add shadow attributes.																			
								pguiScore->m_sTextEffects |= RGuiItem::Shadow;								
								pguiScore->m_u32TextShadowColor	= TEXT_SHADOW_COLOR;															
								// Set placement.	

								pguiPlace->m_sOffsetX = pguiRoot->m_sX;
								if (g_sActivePlayers > PLAYER_1)
								{									
									pguiPlace->m_sOffsetX -= 10;
									pguiPlace->SetText("P%d)", sScoreIndex + 1);																												
								}
								else
								{
									pguiPlace->m_sOffsetX -= 10;
									pguiPlace->SetText("%d)", sScoreIndex + 1);																												
								}								
								
								pguiPlace->m_sOffsetY = pguiRoot->m_sY + 100 + offY;
								pguiPlace->widthPvr = 500;
								pguiPlace->isPvr = true;								
								pguiPlace->Compose();
			
								if (sScoreIndex == g_sPlayerScorePos)
								{
									pguiScore->m_u32TextColor = 249;
									pguiScore->m_u32TextShadowColor = 1;									
								}
								
								pguiPlace->isPvr = true;
								pguiPlace->DrawPVR();

								if (g_sActivePlayers == PLAYER_1)
								{
									if (g_HighScores[sRealmNum][sScoreIndex] & (1<<27))
									{
										pszDifficulty = g_pszDifficulty_Easy[g_GameSettings.m_cActiveLanguage];
									}
									else if (g_HighScores[sRealmNum][sScoreIndex] & (1<<28))
									{
										pszDifficulty = g_pszDifficulty_Medium[g_GameSettings.m_cActiveLanguage];
									}
									else if (g_HighScores[sRealmNum][sScoreIndex] & (1<<29))
									{
										pszDifficulty = g_pszDifficulty_Hard[g_GameSettings.m_cActiveLanguage];
									}
									else if (g_HighScores[sRealmNum][sScoreIndex] & (1<<30))
									{
										pszDifficulty = g_pszDifficulty_Nightmare[g_GameSettings.m_cActiveLanguage];
									}
								}			
								
								// Set score.
								// There are two types of scores.
								if (vtScoringUnit == Value)
								{
									// Value.
									if (g_sActivePlayers > PLAYER_1)
									{
										pguiScore->SetText("%03d      %03d      %03d", 
										g_scoreboard.m_asScores[sScoreIndex][0], g_scoreboard.m_asScores[sScoreIndex][1], g_scoreboard.m_asScores[sScoreIndex][2]);
									}
									else
									{					
										if (pszDifficulty)
										{
											pguiScore->SetText("%ld %s (%s)",  g_HighScores[sRealmNum][sScoreIndex] & ~0xF8000000, g_apszScoreUnits[pRealm->m_ScoringMode][g_GameSettings.m_cActiveLanguage], pszDifficulty);
										}
										else
										{
											pguiScore->SetText("%ld %s", g_HighScores[sRealmNum][sScoreIndex] & ~0xF8000000, g_apszScoreUnits[pRealm->m_ScoringMode][g_GameSettings.m_cActiveLanguage] );
										}
									}									
								}
								else
								{
									// Time.
									if (pszDifficulty)
									{
										pguiScore->SetText("%s %s (%s)", CreateTimeString(g_HighScores[sRealmNum][sScoreIndex] & ~0xF8000000), g_apszScoreUnits[pRealm->m_ScoringMode][g_GameSettings.m_cActiveLanguage], pszDifficulty);
									}
									else
									{
										pguiScore->SetText("%s %s", CreateTimeString(g_HighScores[sRealmNum][sScoreIndex] & ~0xF8000000), g_apszScoreUnits[pRealm->m_ScoringMode][g_GameSettings.m_cActiveLanguage] );
									}
								}
																						
								pguiScore->m_sOffsetX = pguiRoot->m_sX;
								pguiScore->m_sOffsetY = pguiRoot->m_sY + 98 + offY;

								pguiScore->m_justification = RGuiItem::Left;
								pguiScore->SetJustification();																
								pguiScore->m_sOffsetX -= 200;
								pguiScore->widthPvr = 300;
																																
								pguiScore->Compose();
								pguiScore->isPvr = true;
										
								pguiScore->DrawPVR();
							
								offY += 30;							
							}
							else
							{
								TRACE("ScoreDisplayHighScores(): Missing items in this instance of \"%d\".\n",
									HIGHSCORE_ITEM_FILE);
								bGotAllScoreItems	= false;
							}
						}
						else
						{
							TRACE("ScoreDisplayHighScores(): Failed to get instance of \"%d\".\n",
							HIGHSCORE_ITEM_FILE);
							bGotAllScoreItems	= false;
						}
					}
					
					TRACE("Hooray!\n");
			
					//TRACE("sPlayersScorePosition = %d, pRealm->m_flags.bMultiplayer = %s\n", sPlayersScorePosition, pRealm->m_flags.bMultiplayer ? "true" : "false");
					
			
					TRACE("Whew, that was a wild ride.\n");

					for (int i = 0; i < sScoreNum; i++)
					{		
						if (pguiItems[i] != NULL)
						{
						//	pguiItems[i]->SetParent(NULL);
							rspReleaseResourceInstance(&g_resmgrShell, &pguiItems[i]);
						}															
					}

					free(pguiItems);

					/*RGuiItem *item = pguiRoot->m_listguiChildren.GetHead();
					while (item != NULL)
					{						
						rspReleaseResourceInstance(&g_resmgrShell, &item);
						item = pguiRoot->m_listguiChildren.GetNext();
					}*/

					rspReleaseResource(&g_resmgrShell, &pguiRoot);
				}
				else 
				{
					TRACE("failed to load GUI resource\n");
				}
			}
		}
	}
}

//////////////////////////////////////////////////////////////////////////////
// ScoreHighestKills
//
// Return the highest number of kills among all of the players.  This will
// be called to determine if a frag limit level is over.
//
//////////////////////////////////////////////////////////////////////////////

int16_t ScoreHighestKills(CRealm* pRealm)
{
	int16_t sHighest = 0;
	int16_t sNumDudes = pRealm->m_asClassNumThings[CThing::CDudeID];	
	int16_t i;

	for (i = 0; i < sNumDudes; i++)
	{
		if (g_scoreboard.m_asScores[i][0] > sHighest)
			sHighest = g_scoreboard.m_asScores[i][0];
	}
	
	return sHighest;	
}

//////////////////////////////////////////////////////////////////////////////
// Score_GetBestForLvl
//
// Return the highest number of kills among all of the players.  This will
// be called to determine if a frag limit level is over.
//
//////////////////////////////////////////////////////////////////////////////


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