#ifdef PRECOMPILEDHEADERS
	#include "Strategic All.h"
#else
	#include "Map Screen Interface Bottom.h"
	#include "Map Screen Interface Border.h"
	#include "Types.h"
	#include "vsurface.h"
	#include "mousesystem.h"
	#include "Button System.h"
	#include "sgp.h"
	#include "Utilities.h"
	#include "message.h"
	#include "mapscreen.h"
	#include "strategicmap.h"
	#include "font control.h"
	#include "Radar Screen.h"
	#include "game clock.h"
	#include "sysutil.h"
	#include "Render Dirty.h"
	#include "Map Screen Interface.h"
	#include "Map Screen Interface Map.h"
	#include "Text.h"
	#include "Overhead.h"
	#include "Prebattle Interface.h"
	#include "Options Screen.h"
	#include "Cursor Control.h"
	#include "gameloop.h"
	#include "ai.h"
	#include "Tactical Save.h"
	#include "Campaign Types.h"
	#include "Air Raid.h"
	#include "Finances.h"
	#include "LaptopSave.h"
	#include "Interface Items.h"
	#include "wordwrap.h"
	#include "meanwhile.h"
	#include "Dialogue Control.h"
	#include "Map Screen Helicopter.h"
	#include "Map Screen Interface TownMine Info.h"
	#include "Merc Contract.h"
	#include "Map Screen Interface Map Inventory.h"
	#include "Explosion Control.h"
	#include "Creature Spreading.h"
	#include "Assignments.h"
	#include "Soldier macros.h"
	#include "GameSettings.h"
	#include "SaveLoadScreen.h"
	#include "Interface Control.h"
	#include "Sys Globals.h"
#include "game init.h"
#endif

#ifdef JA2UB
#include "Ja25 Strategic Ai.h"
#include "MapScreen Quotes.h"
#include "SaveLoadGame.h"
#include "strategicmap.h"
#endif

#include "connect.h"


struct UILayout_BottomButtons
{
	SGPPoint CompressTime;
	SGPPoint CompressLess;
	SGPPoint Laptop;
	SGPPoint Options;
	SGPPoint Tactical;
	SGPPoint ScrollUp;
	SGPPoint ScrollDown;
};
struct UILayout_BottomTexts
{
	SGPRectangle BalanceTitle;
	SGPRectangle IncomeTitle;
	SGPRectangle ExpensesTitle;
	SGPRectangle CurrentBalance;
	SGPRectangle CurrentIncome;
	SGPRectangle CurrentExpenses;
	SGPRectangle CompressTime;
	SGPRectangle LoadedSector;
};
struct BottomUILayout
{
	SGPRectangle Region;
	UILayout_BottomButtons Button;
	UILayout_BottomTexts Text;
};

BottomUILayout UI_BOTTOM;
UINT16 UI_BOTTOM_X;
UINT16 UI_BOTTOM_Y;
UINT16 MAP_BOTTOM_LAPTOP_X;
UINT16 MAP_BOTTOM_LAPTOP_Y;

//#define MESSAGE_SCROLL_AREA_START_X				(SCREEN_WIDTH - 534)	//330
//#define MESSAGE_SCROLL_AREA_END_X				(SCREEN_WIDTH - 522)	//344
#define MESSAGE_SCROLL_AREA_WIDTH				( MESSAGE_SCROLL_AREA_END_X - MESSAGE_SCROLL_AREA_START_X + 1 )

#define MESSAGE_SCROLL_AREA_START_Y				(SCREEN_HEIGHT - 90)	//390
#define MESSAGE_SCROLL_AREA_END_Y				(SCREEN_HEIGHT - 32)		//448
#define MESSAGE_SCROLL_AREA_HEIGHT				( MESSAGE_SCROLL_AREA_END_Y - MESSAGE_SCROLL_AREA_START_Y + 1 )

// CHRISL: Use these if we want scroll bar based on left edge of screen
// HEADROCK HAM 3.6: Now global variables to be changed when initializing map mode.
// See InitMapScreenInterfaceBottomCoords()
//#define MESSAGE_SCROLL_AREA_START_X				330
//#define MESSAGE_SCROLL_AREA_END_X				344
UINT16 MESSAGE_SCROLL_AREA_START_X;
UINT16 MESSAGE_SCROLL_AREA_END_X;

#define SLIDER_HEIGHT							11
#define SLIDER_WIDTH							11

#define SLIDER_BAR_RANGE						( MESSAGE_SCROLL_AREA_HEIGHT - SLIDER_HEIGHT )



#define MESSAGE_BTN_SCROLL_TIME					100

// delay for paused flash
#define PAUSE_GAME_TIMER						500

#define MAP_BOTTOM_FONT_COLOR					( 32 * 4 - 9 )

/*
// delay to start auto message scroll
#define DELAY_TO_START_MESSAGE_SCROLL 3000
// delay per auto message scroll
#define DELAY_PER_MESSAGE_SCROLL 300
*/

// button enums
enum{
	MAP_SCROLL_MESSAGE_UP =0,
	MAP_SCROLL_MESSAGE_DOWN,
};

enum{
	MAP_TIME_COMPRESS_MORE = 0,
	MAP_TIME_COMPRESS_LESS,
};



// GLOBALS


// the dirty state of the mapscreen interface bottom
BOOLEAN fMapScreenBottomDirty = TRUE;

BOOLEAN fMapBottomDirtied = FALSE;

//Used to flag the transition animation from mapscreen to laptop.
BOOLEAN gfStartMapScreenToLaptopTransition = FALSE;

// leaving map screen
BOOLEAN fLeavingMapScreen = FALSE;

// don't start transition from laptop to tactical stuff
BOOLEAN gfDontStartTransitionFromLaptop = FALSE;

// exiting to laptop?
BOOLEAN fLapTop = FALSE;

BOOLEAN gfOneFramePauseOnExit = FALSE;

// we've just scrolled to a new message (for autoscrolling only)
//BOOLEAN gfNewScrollMessage = FALSE;

// exit states
INT8 gbExitingMapScreenToWhere = -1;

UINT8 gubFirstMapscreenMessageIndex = 0;

UINT32 guiCompressionStringBaseTime = 0;

// graphics
UINT32 guiMAPBOTTOMPANEL;
UINT32 guiSliderBar;

// buttons
UINT32 guiMapMessageScrollButtons[ 2 ];
UINT32 guiMapBottomExitButtons[ 3 ];
UINT32 guiMapBottomTimeButtons[ 2 ];

// buttons images
UINT32 guiMapMessageScrollButtonsImage[ 2 ];
UINT32 guiMapBottomExitButtonsImage[ 3 ];
UINT32 guiMapBottomTimeButtonsImage[ 2 ];

// mouse regions
MOUSE_REGION gMapMessageScrollBarRegion;
MOUSE_REGION gMapMessageRegion;
MOUSE_REGION gMapPauseRegion;

MOUSE_REGION gTimeCompressionMask[ 3 ];

// EXTERNS

extern UINT8 gubStartOfMapScreenMessageList;

extern INT32 giMapInvDoneButton;

extern BOOLEAN fInMapMode;
extern BOOLEAN fShowInventoryFlag;
extern BOOLEAN fShowDescriptionFlag;

extern MOUSE_REGION gMPanelRegion;

extern BOOLEAN gfDisableTacticalPanelButtons;

// PROTOTYPES


void LoadMessageSliderBar( void );
void DeleteMessageSliderBar( void );
void DisplayScrollBarSlider( );

void CreateMapScreenBottomMessageScrollBarRegion( void );
void DeleteMapScreenBottomMessageScrollRegion( void );

void DisplayCurrentBalanceForMapBottom( void );
void DisplayCurrentBalanceTitleForMapBottom( void );
void DisplayProjectedDailyMineIncome( void );
void DisplayProjectedDailyExpenses( void ); // HEADROCK HAM 3.6: New expenses window shows running costs today.
void DrawNameOfLoadedSector( void );

void EnableDisableBottomButtonsAndRegions( void );
void EnableDisableTimeCompressButtons( void );
void EnableDisableMessageScrollButtonsAndRegions( void );

void DisplayCompressMode( void );
void RemoveCompressModePause( void );
void CreateCompressModePause( void );

void BtnLaptopCallback(GUI_BUTTON *btn,INT32 reason);
void BtnTacticalCallback( GUI_BUTTON *btn,INT32 reason );
void BtnOptionsFromMapScreenCallback( GUI_BUTTON *btn, INT32 reason );

void CompressModeClickCallback( MOUSE_REGION * pRegion, INT32 iReason );
void CompressMaskClickCallback( MOUSE_REGION * pRegion, INT32 iReason );

void BtnTimeCompressMoreMapScreenCallback( GUI_BUTTON *btn,INT32 reason );
void BtnTimeCompressLessMapScreenCallback( GUI_BUTTON *btn,INT32 reason );

void BtnMessageDownMapScreenCallback( GUI_BUTTON *btn,INT32 reason );
void BtnMessageUpMapScreenCallback( GUI_BUTTON *btn,INT32 reason );

void MapScreenMessageScrollBarCallBack(MOUSE_REGION * pRegion, INT32 iReason );

//void CheckForAndHandleAutoMessageScroll( void );



// FUNCTIONS



void HandleLoadOfMapBottomGraphics( void )
{
	// will load the graphics needed for the mapscreen interface bottom
	VOBJECT_DESC	VObjectDesc;

	// will create buttons for interface bottom
	VObjectDesc.fCreateFlags=VOBJECT_CREATE_FROMFILE;

	if (iResolution >= _640x480 && iResolution < _800x600)
	{
		FilenameForBPP( "INTERFACE\\map_screen_bottom.sti", VObjectDesc.ImageFile );
	}
	else if (iResolution == _1280x720)
	{
		FilenameForBPP("INTERFACE\\map_screen_bottom_1280x720.sti", VObjectDesc.ImageFile);
	}
	else if (iResolution < _1024x768)
	{
		FilenameForBPP( "INTERFACE\\map_screen_bottom_800x600.sti", VObjectDesc.ImageFile );
	}
	else
	{
		FilenameForBPP( "INTERFACE\\map_screen_bottom_1024x768.sti", VObjectDesc.ImageFile );
	}

	if( !AddVideoObject( &VObjectDesc, &guiMAPBOTTOMPANEL ) )
		return;


	// load slider bar icon
	LoadMessageSliderBar( );

	return;
}

BOOLEAN LoadMapScreenInterfaceBottom( void )
{
	CreateButtonsForMapScreenInterfaceBottom( );
	CreateMapScreenBottomMessageScrollBarRegion( );

	// create pause region
	CreateCompressModePause( );

	return( TRUE );
}

void DeleteMapBottomGraphics( void )
{
	DeleteVideoObjectFromIndex( guiMAPBOTTOMPANEL );

	// delete slider bar icon
	DeleteMessageSliderBar( );

	return;
}

void DeleteMapScreenInterfaceBottom( void )
{
	// will delete graphics loaded for the mapscreen interface bottom

	DestroyButtonsForMapScreenInterfaceBottom( );
	DeleteMapScreenBottomMessageScrollRegion( );

	// remove comrpess mode pause
	RemoveCompressModePause( );

	return;
}


// Headrock: this function used to accept no arguments. It now accepts a Boolean so it can be forced to run
// the whole process when we call it from other files. See also .h file!!!
void RenderMapScreenInterfaceBottom( BOOLEAN fForceMapscreenFullRender )
{
	// will render the map screen bottom interface
	HVOBJECT hHandle;
	CHAR8 bFilename[ 32 ];

	fDisplayOverheadMap = FALSE;

	// render whole panel
	// HEADROCK Changed this line to accept outside influence through the new boolean:
	//if ( fForceMapscreenFullRender == TRUE || fMapScreenBottomDirty == TRUE)
	// HEADROCK HAM 3.6: OK, let's always render this panel, as long as the team inventory screen isn't open.
	// sevenfm: improved r8524 fix to work with 1280x720 resolution
	//if (fMapScreenBottomDirty || ((!fShowInventoryFlag || iResolution > _1024x600) && fForceMapscreenFullRender))
	if ((!fShowInventoryFlag || iResolution > _1280x720) && (fMapScreenBottomDirty || fForceMapscreenFullRender))
	{
		// get and blt panel
		GetVideoObject(&hHandle, guiMAPBOTTOMPANEL );

		BltVideoObject( guiSAVEBUFFER , hHandle, 0, UI_BOTTOM.Region.x, UI_BOTTOM.Region.y, VO_BLT_SRCTRANSPARENCY,NULL );
	
		// WANNE - MP: Radarmap image should be displayed on every sector in multiplayer game
		if( GetSectorFlagStatus( sSelMapX, sSelMapY, ( UINT8 )iCurrentMapSectorZ, SF_ALREADY_VISITED ) == TRUE 
			|| is_networked)
		{
			GetMapFileName( sSelMapX, sSelMapY, ( UINT8 )iCurrentMapSectorZ, bFilename, TRUE, TRUE );
			LoadRadarScreenBitmap( bFilename );
		}
		else
		{
			ClearOutRadarMapImage();
		}

		fInterfacePanelDirty = DIRTYLEVEL2;

		// display title
		DisplayCurrentBalanceTitleForMapBottom( );

		// dirty buttons
		MarkButtonsDirty( );

		RestoreExternBackgroundRect(UI_BOTTOM.Region);

		// re render radar map
		RenderRadarScreen( );

		// reset dirty flag
		fMapScreenBottomDirty = FALSE;
		fMapBottomDirtied = TRUE;

		// Headrock: Moved this next line from the above marker ^
		fMapScreenBottomDirty = FALSE;

		// CHRISL: Don't display messagelist if inventory panel is open
		// CHRISL: Only run this condition if we're drawing the message list on the left
		// display messages that can be scrolled through
		// HEADROCK HAM 3.6: Hmmmm, why not draw them? LEt's just move this whole segment here, shall we?
		//if(!fShowInventoryFlag)
		DisplayStringsInMapScreenMessageList( );
	}

	DisplayCompressMode( );

	DisplayCurrentBalanceForMapBottom( );
	DisplayProjectedDailyMineIncome( );
	DisplayProjectedDailyExpenses( );

	// draw the name of the loaded sector
	DrawNameOfLoadedSector( );

	// display slider on the scroll bar
	DisplayScrollBarSlider( );

		
	// handle auto scroll
	//CheckForAndHandleAutoMessageScroll( );

	EnableDisableMessageScrollButtonsAndRegions( );

	EnableDisableBottomButtonsAndRegions( );

	fMapBottomDirtied = FALSE;
	
	#ifdef JA113DEMO
	DisableButton (guiMapBottomExitButtons[MAP_EXIT_TO_LAPTOP]);
	#endif
	
	return;
}


BOOLEAN CreateButtonsForMapScreenInterfaceBottom( void )
{
	// laptop
	guiMapBottomExitButtonsImage[ MAP_EXIT_TO_LAPTOP ]=	LoadButtonImage( "INTERFACE\\map_border_buttons.sti" ,-1,6,-1,15,-1 );
	guiMapBottomExitButtons[ MAP_EXIT_TO_LAPTOP ] = QuickCreateButton( guiMapBottomExitButtonsImage[ MAP_EXIT_TO_LAPTOP ], UI_BOTTOM.Button.Laptop.iX, UI_BOTTOM.Button.Laptop.iY,
										BUTTON_TOGGLE, MSYS_PRIORITY_HIGHEST - 1,
										(GUI_CALLBACK)BtnGenericMouseMoveButtonCallback, (GUI_CALLBACK)BtnLaptopCallback);

	// tactical
	guiMapBottomExitButtonsImage[ MAP_EXIT_TO_TACTICAL ]=	LoadButtonImage( "INTERFACE\\map_border_buttons.sti" ,-1,7,-1,16,-1 );

	guiMapBottomExitButtons[ MAP_EXIT_TO_TACTICAL ] = QuickCreateButton( guiMapBottomExitButtonsImage[ MAP_EXIT_TO_TACTICAL ], UI_BOTTOM.Button.Tactical.iX, UI_BOTTOM.Button.Tactical.iY,
									BUTTON_TOGGLE, MSYS_PRIORITY_HIGHEST - 1,
									(GUI_CALLBACK)BtnGenericMouseMoveButtonCallback, (GUI_CALLBACK)BtnTacticalCallback);

	// options
	guiMapBottomExitButtonsImage[ MAP_EXIT_TO_OPTIONS ]=	LoadButtonImage( "INTERFACE\\map_border_buttons.sti" ,-1,18,-1,19,-1 );
	guiMapBottomExitButtons[ MAP_EXIT_TO_OPTIONS ] = QuickCreateButton( guiMapBottomExitButtonsImage[ MAP_EXIT_TO_OPTIONS ], UI_BOTTOM.Button.Options.iX, UI_BOTTOM.Button.Options.iY,
										BUTTON_TOGGLE, MSYS_PRIORITY_HIGHEST - 1,
										(GUI_CALLBACK)BtnGenericMouseMoveButtonCallback, (GUI_CALLBACK)BtnOptionsFromMapScreenCallback);


	SetButtonFastHelpText( guiMapBottomExitButtons[ 0 ], pMapScreenBottomFastHelp[ 0 ] );
	SetButtonFastHelpText( guiMapBottomExitButtons[ 1 ], pMapScreenBottomFastHelp[ 1 ] );
	SetButtonFastHelpText( guiMapBottomExitButtons[ 2 ], pMapScreenBottomFastHelp[ 2 ] );

	SetButtonCursor(guiMapBottomExitButtons[ 0 ], MSYS_NO_CURSOR );
	SetButtonCursor(guiMapBottomExitButtons[ 1 ], MSYS_NO_CURSOR );
	SetButtonCursor(guiMapBottomExitButtons[ 2 ], MSYS_NO_CURSOR );


	// time compression buttons
	guiMapBottomTimeButtonsImage[ MAP_TIME_COMPRESS_MORE ]=	LoadButtonImage( "INTERFACE\\map_screen_bottom_arrows.sti" ,10,1,-1,3,-1 );
	guiMapBottomTimeButtons[ MAP_TIME_COMPRESS_MORE ] = QuickCreateButton( guiMapBottomTimeButtonsImage[ MAP_TIME_COMPRESS_MORE ], UI_BOTTOM.Button.CompressTime.iX, UI_BOTTOM.Button.CompressTime.iY,
										BUTTON_TOGGLE, MSYS_PRIORITY_HIGHEST - 2 ,
										(GUI_CALLBACK)BtnGenericMouseMoveButtonCallback, (GUI_CALLBACK)BtnTimeCompressMoreMapScreenCallback);

	guiMapBottomTimeButtonsImage[ MAP_TIME_COMPRESS_LESS ]=	LoadButtonImage( "INTERFACE\\map_screen_bottom_arrows.sti" ,9,0,-1,2,-1 );
	guiMapBottomTimeButtons[ MAP_TIME_COMPRESS_LESS ] = QuickCreateButton( guiMapBottomTimeButtonsImage[ MAP_TIME_COMPRESS_LESS ], UI_BOTTOM.Button.CompressLess.iX, UI_BOTTOM.Button.CompressLess.iY,
										BUTTON_TOGGLE, MSYS_PRIORITY_HIGHEST - 2,
					(GUI_CALLBACK)BtnGenericMouseMoveButtonCallback, (GUI_CALLBACK)BtnTimeCompressLessMapScreenCallback);


	SetButtonFastHelpText( guiMapBottomTimeButtons[ 0 ], pMapScreenBottomFastHelp[ 3 ] );
	SetButtonFastHelpText( guiMapBottomTimeButtons[ 1 ], pMapScreenBottomFastHelp[ 4 ] );

	SetButtonCursor(guiMapBottomTimeButtons[ 0 ], MSYS_NO_CURSOR );
	SetButtonCursor(guiMapBottomTimeButtons[ 1 ], MSYS_NO_CURSOR );


 // scroll buttons
// CHRISL: Changed coordinates to dynamically place scroll buttons from the right edge of screen.
  guiMapMessageScrollButtonsImage[ MAP_SCROLL_MESSAGE_UP ]=  LoadButtonImage( "INTERFACE\\map_screen_bottom_arrows.sti" ,11,4,-1,6,-1 );
	guiMapMessageScrollButtonsImage[ MAP_SCROLL_MESSAGE_DOWN ]=  LoadButtonImage( "INTERFACE\\map_screen_bottom_arrows.sti" ,12,5,-1,7,-1 );

	guiMapMessageScrollButtons[ MAP_SCROLL_MESSAGE_UP ] = QuickCreateButton( guiMapMessageScrollButtonsImage[ MAP_SCROLL_MESSAGE_UP ], UI_BOTTOM.Button.ScrollUp.iX, UI_BOTTOM.Button.ScrollUp.iY,
										BUTTON_TOGGLE, MSYS_PRIORITY_HIGHEST - 1,
										(GUI_CALLBACK)BtnGenericMouseMoveButtonCallback, (GUI_CALLBACK)BtnMessageUpMapScreenCallback);
	guiMapMessageScrollButtons[ MAP_SCROLL_MESSAGE_DOWN ] = QuickCreateButton( guiMapMessageScrollButtonsImage[ MAP_SCROLL_MESSAGE_DOWN ], UI_BOTTOM.Button.ScrollDown.iX, UI_BOTTOM.Button.ScrollDown.iY,
										BUTTON_TOGGLE, MSYS_PRIORITY_HIGHEST - 1,
										(GUI_CALLBACK)BtnGenericMouseMoveButtonCallback, (GUI_CALLBACK)BtnMessageDownMapScreenCallback);

	SetButtonFastHelpText( guiMapMessageScrollButtons[ 0 ], pMapScreenBottomFastHelp[ 5 ] );
	SetButtonFastHelpText( guiMapMessageScrollButtons[ 1 ], pMapScreenBottomFastHelp[ 6 ] );
	SetButtonCursor(guiMapMessageScrollButtons[ 0 ], MSYS_NO_CURSOR );
	SetButtonCursor(guiMapMessageScrollButtons[ 1 ], MSYS_NO_CURSOR );

	return( TRUE );
}


void DestroyButtonsForMapScreenInterfaceBottom( void )
{
	// will destroy the buttons for the mapscreen bottom interface

	RemoveButton( guiMapBottomExitButtons[ 0 ] );
	RemoveButton( guiMapBottomExitButtons[ 1 ] );
	RemoveButton( guiMapBottomExitButtons[ 2 ] );
	RemoveButton( guiMapMessageScrollButtons[ 0 ] );
	RemoveButton( guiMapMessageScrollButtons[ 1 ] );
	RemoveButton( guiMapBottomTimeButtons[ 0 ] );
	RemoveButton( guiMapBottomTimeButtons[ 1 ] );


	UnloadButtonImage( guiMapBottomExitButtonsImage[ 0 ] );
	UnloadButtonImage( guiMapBottomExitButtonsImage[ 1 ] );
	UnloadButtonImage( guiMapBottomExitButtonsImage[ 2 ] );
	UnloadButtonImage( guiMapMessageScrollButtonsImage[ 0 ] );
	UnloadButtonImage( guiMapMessageScrollButtonsImage[ 1 ] );
	UnloadButtonImage( guiMapBottomTimeButtonsImage[ 0 ] );
	UnloadButtonImage( guiMapBottomTimeButtonsImage[ 1 ] );

	// reset dirty flag
	fMapScreenBottomDirty = TRUE;

	return;
}


void BtnLaptopCallback(GUI_BUTTON *btn,INT32 reason)
{
	if(reason & MSYS_CALLBACK_REASON_LBUTTON_DWN )
	{
		if( IsMapScreenHelpTextUp() )
		{
			// stop mapscreen text
			StopMapScreenHelpText( );
		}

		// redraw region
		if( btn->Area.uiFlags & MSYS_HAS_BACKRECT )
		{
			fMapScreenBottomDirty = TRUE;
		}

	btn->uiFlags|=(BUTTON_CLICKED_ON);
	}
	else if(reason & MSYS_CALLBACK_REASON_LBUTTON_UP )
	{
		if( IsMapScreenHelpTextUp() )
		{
			// stop mapscreen text
			StopMapScreenHelpText( );
		}

	if (btn->uiFlags & BUTTON_CLICKED_ON)
		{
			btn->uiFlags&=~(BUTTON_CLICKED_ON | BUTTON_DIRTY);
			DrawButton( btn->IDNum );

			RequestTriggerExitFromMapscreen( MAP_EXIT_TO_LAPTOP );
	}
	}
	else if(reason & MSYS_CALLBACK_REASON_RBUTTON_DWN )
	{
		if( IsMapScreenHelpTextUp() )
		{
			// stop mapscreen text
			StopMapScreenHelpText( );
		}
	}
}


void BtnTacticalCallback( GUI_BUTTON *btn,INT32 reason )
{

	if(reason & MSYS_CALLBACK_REASON_LBUTTON_DWN )
	{
		if( IsMapScreenHelpTextUp() )
		{
			// stop mapscreen text
			StopMapScreenHelpText( );
			return;
		}

		// redraw region
		if( btn->Area.uiFlags & MSYS_HAS_BACKRECT )
		{
			fMapScreenBottomDirty = TRUE;
		}

	btn->uiFlags|=(BUTTON_CLICKED_ON);
	}
	else if(reason & MSYS_CALLBACK_REASON_LBUTTON_UP )
	{
	if (btn->uiFlags & BUTTON_CLICKED_ON)
		{
			btn->uiFlags&=~(BUTTON_CLICKED_ON);

			RequestTriggerExitFromMapscreen( MAP_EXIT_TO_TACTICAL );
	}
	}
	else if( reason & MSYS_CALLBACK_REASON_RBUTTON_DWN )
	{
		if( IsMapScreenHelpTextUp() )
		{
			// stop mapscreen text
			StopMapScreenHelpText( );
			return;
		}
	}
}

void BtnOptionsFromMapScreenCallback( GUI_BUTTON *btn, INT32 reason )
{

	if(reason & MSYS_CALLBACK_REASON_LBUTTON_DWN )
	{

		if( IsMapScreenHelpTextUp() )
		{
			// stop mapscreen text
			StopMapScreenHelpText( );
			return;
		}

		// redraw region
		if( btn->uiFlags & MSYS_HAS_BACKRECT )
		{
			fMapScreenBottomDirty = TRUE;
		}

	btn->uiFlags|=(BUTTON_CLICKED_ON);
	}
	else if(reason & MSYS_CALLBACK_REASON_LBUTTON_UP )
	{
	if (btn->uiFlags & BUTTON_CLICKED_ON)
		{
			btn->uiFlags&=~(BUTTON_CLICKED_ON);
			fMapScreenBottomDirty = TRUE;

			RequestTriggerExitFromMapscreen( MAP_EXIT_TO_OPTIONS );
	}
	}
	else if( reason & MSYS_CALLBACK_REASON_RBUTTON_DWN )
	{
		if( IsMapScreenHelpTextUp() )
		{
			// stop mapscreen text
			StopMapScreenHelpText( );
			return;
		}
	}
}

void DrawNameOfLoadedSector( void )
{
	CHAR16 sString[ 128 ];
	INT16 sFontX, sFontY;


	SetFontDestBuffer( FRAME_BUFFER, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, FALSE );

	SetFont( COMPFONT );
	SetFontForeground( 183 );
	SetFontBackground( FONT_BLACK );


	GetSectorIDString( sSelMapX, sSelMapY, ( INT8 )( iCurrentMapSectorZ ),sString, TRUE );
	ReduceStringLength( sString, 80, COMPFONT );

	VarFindFontCenterCoordinates( 
		UI_BOTTOM.Text.LoadedSector.x, 
		UI_BOTTOM.Text.LoadedSector.y, 
		UI_BOTTOM.Text.LoadedSector.width, 
		UI_BOTTOM.Text.LoadedSector.height,
		COMPFONT, &sFontX, &sFontY, sString 
	);

	mprintf( sFontX, sFontY, L"%s", sString );
}


void CompressModeClickCallback( MOUSE_REGION * pRegion, INT32 iReason )
{
	if( iReason & ( MSYS_CALLBACK_REASON_RBUTTON_UP | MSYS_CALLBACK_REASON_LBUTTON_UP ) )
	{
		if ( CommonTimeCompressionChecks() == TRUE )
			return;

		RequestToggleTimeCompression();
	}
}


void BtnTimeCompressMoreMapScreenCallback( GUI_BUTTON *btn,INT32 reason )
{

	if(reason & MSYS_CALLBACK_REASON_LBUTTON_DWN )
	{
		if ( CommonTimeCompressionChecks() == TRUE )
			return;

		// redraw region
		if( btn->uiFlags & MSYS_HAS_BACKRECT )
		{
			fMapScreenBottomDirty = TRUE;
		}

	btn->uiFlags|=(BUTTON_CLICKED_ON);
	}
	else if(reason & MSYS_CALLBACK_REASON_LBUTTON_UP )
	{
		if (btn->uiFlags & BUTTON_CLICKED_ON)
		{
			btn->uiFlags&=~(BUTTON_CLICKED_ON);
			fMapScreenBottomDirty = TRUE;

			RequestIncreaseInTimeCompression();
	}
	}
	else 	if(reason & MSYS_CALLBACK_REASON_RBUTTON_DWN )
	{
		if ( CommonTimeCompressionChecks() == TRUE )
			return;
	}
}



void BtnTimeCompressLessMapScreenCallback( GUI_BUTTON *btn,INT32 reason )
{

	if(reason & MSYS_CALLBACK_REASON_LBUTTON_DWN )
	{
		if ( CommonTimeCompressionChecks() == TRUE )
			return;

		// redraw region
		if( btn->uiFlags & MSYS_HAS_BACKRECT )
		{
			fMapScreenBottomDirty = TRUE;
		}

	btn->uiFlags|=(BUTTON_CLICKED_ON);
	}
	else if(reason & MSYS_CALLBACK_REASON_LBUTTON_UP )
	{
		if (btn->uiFlags & BUTTON_CLICKED_ON)
		{
		btn->uiFlags&=~(BUTTON_CLICKED_ON);
			fMapScreenBottomDirty = TRUE;

			RequestDecreaseInTimeCompression();
	}
	}
	else 	if(reason & MSYS_CALLBACK_REASON_RBUTTON_DWN )
	{
		if ( CommonTimeCompressionChecks() == TRUE )
			return;
	}
}



void BtnMessageDownMapScreenCallback( GUI_BUTTON *btn,INT32 reason )
{
	static INT32 iLastRepeatScrollTime = 0;

	if( reason & MSYS_CALLBACK_REASON_LBUTTON_DWN )
	{
		if( IsMapScreenHelpTextUp() )
		{
			// stop mapscreen text
			StopMapScreenHelpText( );
			return;
		}

		// redraw region
		if( btn->uiFlags & MSYS_HAS_BACKRECT )
		{
			fMapScreenBottomDirty = TRUE;
		}

	btn->uiFlags|=(BUTTON_CLICKED_ON);

	iLastRepeatScrollTime = 0;
	}
	else if(reason & MSYS_CALLBACK_REASON_LBUTTON_UP )
	{
		if (btn->uiFlags & BUTTON_CLICKED_ON)
		{
		btn->uiFlags&=~(BUTTON_CLICKED_ON);

			// redraw region
			if( btn->uiFlags & MSYS_HAS_BACKRECT )
			{
				fMapScreenBottomDirty = TRUE;
			}

			// down a line
			MapScreenMsgScrollDown( 1 );
	}
	}
	else if( reason & MSYS_CALLBACK_REASON_LBUTTON_REPEAT )
	{
		if( GetJA2Clock() - iLastRepeatScrollTime >= MESSAGE_BTN_SCROLL_TIME )
		{
			// down a line
			MapScreenMsgScrollDown( 1 );

		iLastRepeatScrollTime = GetJA2Clock( );
		}
	}
	else if( reason & MSYS_CALLBACK_REASON_RBUTTON_DWN )
	{
		if( IsMapScreenHelpTextUp() )
		{
			// stop mapscreen text
			StopMapScreenHelpText( );
			return;
		}

		// redraw region
		if( btn->uiFlags & MSYS_HAS_BACKRECT )
		{
			fMapScreenBottomDirty = TRUE;
		}

	btn->uiFlags|=(BUTTON_CLICKED_ON);

	iLastRepeatScrollTime = 0;
	}
	else if(reason & MSYS_CALLBACK_REASON_RBUTTON_UP )
	{
		if (btn->uiFlags & BUTTON_CLICKED_ON)
		{
		btn->uiFlags&=~(BUTTON_CLICKED_ON);

			// redraw region
			if( btn->uiFlags & MSYS_HAS_BACKRECT )
			{
				fMapScreenBottomDirty = TRUE;
			}

			// down a page
			MapScreenMsgScrollDown( MAX_MESSAGES_ON_MAP_BOTTOM );
	}
	}
	else if( reason & MSYS_CALLBACK_REASON_RBUTTON_REPEAT )
	{
		if( GetJA2Clock() - iLastRepeatScrollTime >= MESSAGE_BTN_SCROLL_TIME )
		{
			// down a page
			MapScreenMsgScrollDown( MAX_MESSAGES_ON_MAP_BOTTOM );

		iLastRepeatScrollTime = GetJA2Clock( );
		}
	}
}


void BtnMessageUpMapScreenCallback( GUI_BUTTON *btn,INT32 reason )
{
	static INT32 iLastRepeatScrollTime = 0;


	if(reason & MSYS_CALLBACK_REASON_LBUTTON_DWN )
	{
		if( IsMapScreenHelpTextUp() )
		{
			// stop mapscreen text
			StopMapScreenHelpText( );
			return;
		}

	btn->uiFlags|=(BUTTON_CLICKED_ON);

	// redraw region
		if( btn->Area.uiFlags & MSYS_HAS_BACKRECT )
		{
			fMapScreenBottomDirty = TRUE;
		}

	iLastRepeatScrollTime = 0;
	}

	else if(reason & MSYS_CALLBACK_REASON_LBUTTON_UP )
	{
		if (btn->uiFlags & BUTTON_CLICKED_ON)
		{
			btn->uiFlags&=~(BUTTON_CLICKED_ON);

			// redraw region
			if( btn->uiFlags & MSYS_HAS_BACKRECT )
			{
				fMapScreenBottomDirty = TRUE;
			}

			// up a line
			MapScreenMsgScrollUp( 1 );
	}
	}
	else if( reason & MSYS_CALLBACK_REASON_LBUTTON_REPEAT )
	{
		if( GetJA2Clock() - iLastRepeatScrollTime >= MESSAGE_BTN_SCROLL_TIME )
		{
			// up a line
			MapScreenMsgScrollUp( 1 );

		iLastRepeatScrollTime = GetJA2Clock( );
		}
	}
	else if( reason & MSYS_CALLBACK_REASON_RBUTTON_DWN )
	{
		if( IsMapScreenHelpTextUp() )
		{
			// stop mapscreen text
			StopMapScreenHelpText( );
			return;
		}

		// redraw region
		if( btn->uiFlags & MSYS_HAS_BACKRECT )
		{
			fMapScreenBottomDirty = TRUE;
		}

	btn->uiFlags|=(BUTTON_CLICKED_ON);

	iLastRepeatScrollTime = 0;
	}
	else if(reason & MSYS_CALLBACK_REASON_RBUTTON_UP )
	{
		if (btn->uiFlags & BUTTON_CLICKED_ON)
		{
		btn->uiFlags&=~(BUTTON_CLICKED_ON);

			// redraw region
			if( btn->uiFlags & MSYS_HAS_BACKRECT )
			{
				fMapScreenBottomDirty = TRUE;
			}

			// up a page
			MapScreenMsgScrollUp( MAX_MESSAGES_ON_MAP_BOTTOM );
	}
	}
	else if( reason & MSYS_CALLBACK_REASON_RBUTTON_REPEAT )
	{
		if( GetJA2Clock() - iLastRepeatScrollTime >= MESSAGE_BTN_SCROLL_TIME )
		{
			// up a page
			MapScreenMsgScrollUp( MAX_MESSAGES_ON_MAP_BOTTOM );

		iLastRepeatScrollTime = GetJA2Clock( );
		}
	}
}



void EnableDisableMessageScrollButtonsAndRegions( void )
{
	UINT8 ubNumMessages;

	ubNumMessages = GetRangeOfMapScreenMessages();

	// if no scrolling required, or already showing the topmost message
	if( ( ubNumMessages <= MAX_MESSAGES_ON_MAP_BOTTOM ) || ( gubFirstMapscreenMessageIndex == 0 ) )
	{
		DisableButton( guiMapMessageScrollButtons[ MAP_SCROLL_MESSAGE_UP ] );
		ButtonList[ guiMapMessageScrollButtons[ MAP_SCROLL_MESSAGE_UP ] ]->uiFlags &= ~( BUTTON_CLICKED_ON );
	}
	else
	{
		EnableButton( guiMapMessageScrollButtons[ MAP_SCROLL_MESSAGE_UP ] );
	}

	// if no scrolling required, or already showing the last message
	if( ( ubNumMessages <= MAX_MESSAGES_ON_MAP_BOTTOM ) ||
			( ( gubFirstMapscreenMessageIndex + MAX_MESSAGES_ON_MAP_BOTTOM ) >= ubNumMessages ) )
	{
		DisableButton( guiMapMessageScrollButtons[ MAP_SCROLL_MESSAGE_DOWN ] );
		ButtonList[ guiMapMessageScrollButtons[ MAP_SCROLL_MESSAGE_DOWN ] ]->uiFlags &= ~( BUTTON_CLICKED_ON );
	}
	else
	{
		EnableButton( guiMapMessageScrollButtons[ MAP_SCROLL_MESSAGE_DOWN ] );
	}

	if( ubNumMessages <= MAX_MESSAGES_ON_MAP_BOTTOM )
	{
		MSYS_DisableRegion( &gMapMessageScrollBarRegion );
	}
	else
	{
		MSYS_EnableRegion( &gMapMessageScrollBarRegion );
	}
}


void DisplayCompressMode( void )
{
	INT16 sX, sY;
	CHAR16 sString[ 128 ];
	static UINT8 usColor = FONT_LTGREEN;

	// get compress speed
	if( giTimeCompressMode != NOT_USING_TIME_COMPRESSION )
	{
		if( IsTimeBeingCompressed() )
		{
			swprintf( sString, L"%s", sTimeStrings[ giTimeCompressMode ] );
		}
		else
		{
			swprintf( sString, L"%s", sTimeStrings[ 0 ] );
		}
	}

	// ABCDEFG
	RestoreExternBackgroundRect(UI_BOTTOM.Text.CompressTime);
	SetFontDestBuffer( FRAME_BUFFER, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, FALSE );
	SetFont( COMPFONT );

	if( GetJA2Clock() - guiCompressionStringBaseTime >= PAUSE_GAME_TIMER )
	{
		if( usColor == FONT_LTGREEN )
		{
			usColor = FONT_WHITE;
		}
		else
		{
			usColor = FONT_LTGREEN;
		}

		guiCompressionStringBaseTime = GetJA2Clock();
	}

	if( ( giTimeCompressMode != 0 ) && ( GamePaused( ) == FALSE ) )
	{
		usColor = FONT_LTGREEN;
	}

	SetFontForeground( usColor );
	SetFontBackground( FONT_BLACK );

	FindFontCenterCoordinates(UI_BOTTOM.Text.CompressTime.x, UI_BOTTOM.Text.CompressTime.y, 33, 13, sString, COMPFONT, &sX, &sY );
		
	mprintf( sX, sY, sString );

	return;
}


void CreateCompressModePause( void )
{
	const auto tlx = UI_BOTTOM.Text.CompressTime.x;
	const auto tly = UI_BOTTOM.Text.CompressTime.y;
	const auto brx = tlx + UI_BOTTOM.Text.CompressTime.width;
	const auto bry = tly + UI_BOTTOM.Text.CompressTime.height;
	MSYS_DefineRegion( &gMapPauseRegion, tlx, tly, brx, bry, MSYS_PRIORITY_HIGH,
							MSYS_NO_CURSOR, MSYS_NO_CALLBACK, CompressModeClickCallback );

	SetRegionFastHelpText( &gMapPauseRegion, pMapScreenBottomFastHelp[ 7 ] );
}

void RemoveCompressModePause( void )
{
	MSYS_RemoveRegion( &gMapPauseRegion );
}


void LoadMessageSliderBar( void )
{
	// this function will load the message slider bar
	VOBJECT_DESC	VObjectDesc;

	VObjectDesc.fCreateFlags=VOBJECT_CREATE_FROMFILE;
	FilenameForBPP( "INTERFACE\\map_screen_bottom_arrows.sti", VObjectDesc.ImageFile );
	if( !AddVideoObject( &VObjectDesc, &guiSliderBar ) )
		return;
}

void DeleteMessageSliderBar( void )
{
	// this function will delete message slider bar
	DeleteVideoObjectFromIndex( guiSliderBar );
}


void CreateMapScreenBottomMessageScrollBarRegion( void )
{
		MSYS_DefineRegion( &gMapMessageScrollBarRegion,
			MESSAGE_SCROLL_AREA_START_X, 
			MESSAGE_SCROLL_AREA_START_Y,
			MESSAGE_SCROLL_AREA_END_X, 
			MESSAGE_SCROLL_AREA_END_Y,
			MSYS_PRIORITY_NORMAL, MSYS_NO_CURSOR, MSYS_NO_CALLBACK, MapScreenMessageScrollBarCallBack 
		);

		MSYS_DefineRegion( &gMapMessageRegion,
			UI_BOTTOM.Region.x + 16,
			MESSAGE_SCROLL_AREA_START_Y-16, 
			MESSAGE_SCROLL_AREA_END_X-22, 
			MESSAGE_SCROLL_AREA_END_Y+12,
			MSYS_PRIORITY_NORMAL, MSYS_NO_CURSOR, MSYS_NO_CALLBACK, MSYS_NO_CALLBACK
		);
}


void DeleteMapScreenBottomMessageScrollRegion( void )
{
	MSYS_RemoveRegion( &gMapMessageScrollBarRegion );
	MSYS_RemoveRegion(&gMapMessageRegion);
}



void MapScreenMessageScrollBarCallBack( MOUSE_REGION *pRegion, INT32 iReason )
{
	POINT MousePos;
	UINT8 ubMouseYOffset;
	UINT8	ubDesiredSliderOffset;
	UINT8 ubDesiredMessageIndex;
	UINT8 ubNumMessages;


	if (iReason & MSYS_CALLBACK_REASON_INIT)
	{
	return;
	}


	if ( iReason & ( MSYS_CALLBACK_REASON_LBUTTON_DWN | MSYS_CALLBACK_REASON_LBUTTON_REPEAT ) )
	{
		// how many messages are there?
	ubNumMessages = GetRangeOfMapScreenMessages();

		// region is supposed to be disabled if there aren't enough messages to scroll.	Formulas assume this
		if ( ubNumMessages > MAX_MESSAGES_ON_MAP_BOTTOM )
		{
			// where is the mouse?
			GetCursorPos( &MousePos );
			ScreenToClient(ghWindow, &MousePos); // In window coords!

			ubMouseYOffset = (UINT8) MousePos.y - MESSAGE_SCROLL_AREA_START_Y;

			// if clicking in the top 5 pixels of the slider bar
			if ( ubMouseYOffset < ( SLIDER_HEIGHT / 2 ) )
			{
				// scroll all the way to the top
				ubDesiredMessageIndex = 0;
			}
			// if clicking in the bottom 6 pixels of the slider bar
			else if ( ubMouseYOffset >= ( MESSAGE_SCROLL_AREA_HEIGHT - ( SLIDER_HEIGHT / 2 ) ) )
			{
				// scroll all the way to the bottom
				ubDesiredMessageIndex = ubNumMessages - MAX_MESSAGES_ON_MAP_BOTTOM;
			}
			else
			{
				// somewhere in between
				ubDesiredSliderOffset = ubMouseYOffset - ( SLIDER_HEIGHT / 2 );

				Assert( ubDesiredSliderOffset <= SLIDER_BAR_RANGE );

				// calculate what the index should be to place the slider at this offset (round fractions of .5+ up)
				ubDesiredMessageIndex = ( ( ubDesiredSliderOffset * ( ubNumMessages - MAX_MESSAGES_ON_MAP_BOTTOM ) ) + ( SLIDER_BAR_RANGE / 2 ) ) / SLIDER_BAR_RANGE;
			}

			// if it's a change
			if ( ubDesiredMessageIndex != gubFirstMapscreenMessageIndex )
			{
				ChangeCurrentMapscreenMessageIndex( ubDesiredMessageIndex );
			}
		}
	}
}


void DisplayScrollBarSlider( )
{
	// will display the scroll bar icon
	UINT8 ubNumMessages;
	UINT8 ubSliderOffset;
	HVOBJECT hHandle;


	ubNumMessages = GetRangeOfMapScreenMessages();

	// only show the slider if there are more messages than will fit on screen
	if ( ubNumMessages > MAX_MESSAGES_ON_MAP_BOTTOM )
	{
		// calculate where slider should be positioned
		ubSliderOffset = ( SLIDER_BAR_RANGE * gubFirstMapscreenMessageIndex ) / ( ubNumMessages - MAX_MESSAGES_ON_MAP_BOTTOM );

		GetVideoObject( &hHandle, guiSliderBar );
		BltVideoObject( FRAME_BUFFER, hHandle, 8, MESSAGE_SCROLL_AREA_START_X + 2, MESSAGE_SCROLL_AREA_START_Y + ubSliderOffset, VO_BLT_SRCTRANSPARENCY, NULL );
	}
}


/*
void CheckForAndHandleAutoMessageScroll( void )
{
	// will check if we are not at the most recent message, if not, scroll to it
	static INT32 iBaseScrollTime =0;
	static INT32 iBaseScrollDelay =0;
	static BOOLEAN fScrollMessage = FALSE;

	// check if we are at the last message, if so, leave
	if( IsThisTheLastMessageInTheList( ) )
	{
		// leave
		// reset flag
		fScrollMessage = FALSE;
		return;
	}

	// we are not, check how long we have been here?
	if( gfNewScrollMessage == TRUE )
	{
		// we just scrolled to a new message, reset timer
		iBaseScrollTime = GetJA2Clock( );

		// reset flag
		gfNewScrollMessage = FALSE;
		fScrollMessage = FALSE;
	}

	// check timer

	if( GetJA2Clock( ) - iBaseScrollTime > DELAY_TO_START_MESSAGE_SCROLL )
	{

		if( fScrollMessage == FALSE )
		{
		// set up scroll delay
		iBaseScrollDelay = GetJA2Clock( );

		// start scroll
		fScrollMessage = TRUE;

		}

		iBaseScrollTime = GetJA2Clock( );
	}

	if( fScrollMessage == TRUE )
	{
		if( GetJA2Clock( ) - iBaseScrollDelay > DELAY_PER_MESSAGE_SCROLL )
		{
			// scroll to next message
			MoveCurrentMessagePointerDownList( );

			// dirty region
			fMapScreenBottomDirty = TRUE;

			// reset delay timer
			iBaseScrollDelay = GetJA2Clock( );
		}
	}


	return;
}
*/



void EnableDisableBottomButtonsAndRegions( void )
{
	INT8 iExitButtonIndex;

	// this enables and disables the buttons MAP_EXIT_TO_LAPTOP, MAP_EXIT_TO_TACTICAL, and MAP_EXIT_TO_OPTIONS
	for ( iExitButtonIndex = 0; iExitButtonIndex < 3; iExitButtonIndex++ )
	{
		if ( AllowedToExitFromMapscreenTo( iExitButtonIndex ) )
		{
			EnableButton( guiMapBottomExitButtons[ iExitButtonIndex ]);
		}
		else
		{
			DisableButton( guiMapBottomExitButtons[ iExitButtonIndex ]);
		}
	}

	// enable/disable time compress buttons and region masks
	EnableDisableTimeCompressButtons( );
	CreateDestroyMouseRegionMasksForTimeCompressionButtons( );


	// Enable/Disable map inventory panel buttons

	// if in merc inventory panel
	if( fShowInventoryFlag )
	{
		// and an item is in the cursor
		if( ( gMPanelRegion.Cursor == EXTERN_CURSOR ) || ( InKeyRingPopup( ) == TRUE ) || InItemStackPopup() )
		{
			DisableButton( giMapInvDoneButton );
		}
		else
		{
			EnableButton( giMapInvDoneButton );
		}

		if( fShowDescriptionFlag )
		{
			ForceButtonUnDirty( giMapInvDoneButton );
		}
	}
}


void EnableDisableTimeCompressButtons( void )
{
	if( AllowedToTimeCompress( ) == FALSE )
	{
		DisableButton( guiMapBottomTimeButtons[ MAP_TIME_COMPRESS_MORE ] );
		DisableButton( guiMapBottomTimeButtons[ MAP_TIME_COMPRESS_LESS ] );
	}
	else
	{
		// disable LESS if time compression is at minimum or OFF
		if ( !IsTimeCompressionOn() || giTimeCompressMode == TIME_COMPRESS_X0 )
		{
			DisableButton( guiMapBottomTimeButtons[ MAP_TIME_COMPRESS_LESS ] );
		}
		else
		{
			EnableButton( guiMapBottomTimeButtons[ MAP_TIME_COMPRESS_LESS] );
		}

		// disable MORE if we're not paused and time compression is at maximum
		// only disable MORE if we're not paused and time compression is at maximum
		if ( IsTimeCompressionOn() && ( giTimeCompressMode == TIME_COMPRESS_60MINS ) )
		{
			DisableButton( guiMapBottomTimeButtons[ MAP_TIME_COMPRESS_MORE ] );
		}
		else
		{
			EnableButton( guiMapBottomTimeButtons[ MAP_TIME_COMPRESS_MORE ] );
		}
	}
}


void EnableDisAbleMapScreenOptionsButton( BOOLEAN fEnable )
{
	if( fEnable )
	{
		EnableButton( guiMapBottomExitButtons[ MAP_EXIT_TO_OPTIONS ] );
	}
	else
	{
		DisableButton( guiMapBottomExitButtons[ MAP_EXIT_TO_OPTIONS ] );
	}
}


BOOLEAN AllowedToTimeCompress( void )
{
	// WANNE - MP: In multiplayer mode we do not allow compress time, for now.
	if (is_networked)
	{
		return ( FALSE );
	}

	// if already leaving, disallow any other attempts to exit
	if ( fLeavingMapScreen )
	{
		return( FALSE );
	}

	// if already going someplace
	if ( gbExitingMapScreenToWhere != -1 )
	{
		return( FALSE );
	}

	// if we're locked into paused time compression by some event that enforces that
	if ( PauseStateLocked() )
	{
		return( FALSE );
	}

#ifdef JA2UB
//Ja25 no meanwhiles
#else
	// meanwhile coming up
	if ( gfMeanwhileTryingToStart )
	{
		return( FALSE );
	}
#endif

	// someone has something to say
	if ( !DialogueQueueIsEmpty() )
	{
		return( FALSE );
	}

	// moving / confirming movement
	if ( ( GetSelectedDestChar() != -1) || fPlotForHelicopter || fPlotForMilitia || gfInConfirmMapMoveMode || fShowMapScreenMovementList )
	{
		return( FALSE );
	}

	// HEADROCK HAM 3.6: Added Facility Menus
	if( fShowAssignmentMenu || gAssignMenuState > ASMENU_NONE || fShowAttributeMenu || fShowContractMenu || fShowRemoveMenu || fShowFacilityAssignmentMenu )
	{
		return( FALSE );
	}

	if( fShowUpdateBox || fShowTownInfo || ( sSelectedMilitiaTown != 0 ) )
	{
		return( FALSE );
	}

	// renewing contracts
	if ( gfContractRenewalSquenceOn )
	{
		return( FALSE );
	}

	// disabled due to battle?
	if( ( fDisableMapInterfaceDueToBattle ) || ( fDisableDueToBattleRoster ) )
	{
		return( FALSE );
	}

	// if holding an inventory item
	if ( fMapInventoryItem )
	{
		return( FALSE );
	}

	// show the inventory pool?
	if( fShowMapInventoryPool )
	{
		// prevent time compress (items get stolen over time, etc.)
		return( FALSE );
	}

	// no mercs have ever been hired
	if( gfAtLeastOneMercWasHired == FALSE )
	{
		return( FALSE );
	}

/*
	//in air raid
	if( InAirRaid( ) == TRUE )
	{
		return( FALSE );
	}
*/

	// no usable mercs on team!
	if ( !AnyUsableRealMercenariesOnTeam() )
	{
		return( FALSE );
	}

		// must wait till bombs go off
	if ( ActiveTimedBombExists() )
	{
		return( FALSE );
	}

	// hostile sector / in battle
	if( (gTacticalStatus.uiFlags & INCOMBAT ) || ( gTacticalStatus.fEnemyInSector ) )
	{
		return( FALSE );
	}

	if( PlayerGroupIsInACreatureInfestedMine() )
	{
		return FALSE;
	}

	// Flugente: no time compression if hostile civilians bloodcats are in the current sector. 
	// Otherwise time progresses, but squad arrivals will be delayed as long as they still exist
	if ( HostileCiviliansPresent() || HostileBloodcatsPresent() )
	{
		return FALSE;
	}

#ifdef JA2UB  
		//if the player hasnt been to the initial sector yet
	if( !GetSectorFlagStatus( gGameExternalOptions.ubDefaultArrivalSectorX, gGameExternalOptions.ubDefaultArrivalSectorY, 0, SF_HAS_ENTERED_TACTICAL ) ) //7, 8
	{
		//if there is something that jerry wants to say
		if( !WillJerryMiloAllowThePlayerToCompressTimeAtBeginingOfGame() )
		{
			return( FALSE );
		}
	}
#endif
	
	return( TRUE );
}


void DisplayCurrentBalanceTitleForMapBottom( void )
{
	CHAR16 sString[ 128 ];
	INT16 sFontX, sFontY;

	// ste the font buffer
	SetFontDestBuffer( guiSAVEBUFFER, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, FALSE );

	SetFont( COMPFONT );
	SetFontForeground( MAP_BOTTOM_FONT_COLOR );
	SetFontBackground( FONT_BLACK );

	swprintf( sString, L"%s", pMapScreenBottomText[ 0 ] );

	// center it
	VarFindFontCenterCoordinates(UI_BOTTOM.Text.BalanceTitle.x, UI_BOTTOM.Text.BalanceTitle.y, UI_BOTTOM.Text.BalanceTitle.width, UI_BOTTOM.Text.BalanceTitle.height, COMPFONT, &sFontX, &sFontY, sString );
	
	// print it
	mprintf( sFontX, sFontY, L"%s", sString );

	swprintf( sString, L"%s", zMarksMapScreenText[ 2 ] );

	// center it
	VarFindFontCenterCoordinates(UI_BOTTOM.Text.IncomeTitle.x, UI_BOTTOM.Text.IncomeTitle.y, UI_BOTTOM.Text.IncomeTitle.width, UI_BOTTOM.Text.IncomeTitle.height, COMPFONT, &sFontX, &sFontY, sString );

	// print it
	mprintf( sFontX, sFontY, L"%s", sString );

	swprintf( sString, L"%s", New113HAMMessage[ 20 ] );

	// HEADROCK HAM 3.6: Projected expenses for today, with facilities (and in the future, merc contracts) taken into
	// account.
	VarFindFontCenterCoordinates(UI_BOTTOM.Text.ExpensesTitle.x, UI_BOTTOM.Text.ExpensesTitle.y, UI_BOTTOM.Text.ExpensesTitle.width, UI_BOTTOM.Text.ExpensesTitle.height, COMPFONT, &sFontX, &sFontY, sString );

	// print it
	mprintf( sFontX, sFontY, L"%s", sString );

	// ste the font buffer
	SetFontDestBuffer( FRAME_BUFFER, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, FALSE );
	
	return;
}

void DisplayCurrentBalanceForMapBottom( void )
{
	// show the current balance for the player on the map panel bottom
	CHAR16 sString[ 128 ];
	INT16 sFontX, sFontY;

	// ste the font buffer
	SetFontDestBuffer( FRAME_BUFFER, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, FALSE );

	// set up the font
	SetFont( COMPFONT );
	SetFontForeground( 183 );
	SetFontBackground( FONT_BLACK );

	swprintf( sString, L"%d", LaptopSaveInfo.iCurrentBalance );

	// insert
	InsertCommasForDollarFigure( sString );
	InsertDollarSignInToString( sString );

	VarFindFontCenterCoordinates( 
		UI_BOTTOM.Text.CurrentBalance.x, 
		UI_BOTTOM.Text.CurrentBalance.y, 
		UI_BOTTOM.Text.CurrentBalance.width, 
		UI_BOTTOM.Text.CurrentBalance.height, 
		COMPFONT, &sFontX, &sFontY, sString 
	);
	
	// print it
	mprintf( sFontX, sFontY, L"%s", sString );

	return;
}

void CreateDestroyMouseRegionMasksForTimeCompressionButtons( void )
{
	BOOLEAN fDisabled = FALSE;
	static BOOLEAN fCreated = FALSE;

	// allowed to time compress?
	if( AllowedToTimeCompress( )== FALSE )
	{
		// no, disable buttons
		fDisabled = TRUE;
	}

	if( fInMapMode == FALSE )
	{
		fDisabled = FALSE;
	}

	// check if disabled and not created, create
	if( ( fDisabled ) && ( fCreated == FALSE ) )
	{
		// mask over compress more button
		MSYS_DefineRegion( &gTimeCompressionMask[ 0 ],
			UI_BOTTOM.Button.CompressTime.iX,
			UI_BOTTOM.Button.CompressTime.iY,
			UI_BOTTOM.Button.CompressTime.iX + 13,
			UI_BOTTOM.Button.CompressTime.iY + 16,
			MSYS_PRIORITY_HIGHEST - 1, MSYS_NO_CURSOR, MSYS_NO_CALLBACK, CompressMaskClickCallback
		);

		// mask over compress less button
		MSYS_DefineRegion( &gTimeCompressionMask[ 1 ],
			UI_BOTTOM.Button.CompressLess.iX, 
			UI_BOTTOM.Button.CompressLess.iY,
			UI_BOTTOM.Button.CompressLess.iX + 13,
			UI_BOTTOM.Button.CompressLess.iY + 16,
			MSYS_PRIORITY_HIGHEST - 1, MSYS_NO_CURSOR, MSYS_NO_CALLBACK, CompressMaskClickCallback 
		);

		// mask over pause game button
		MSYS_DefineRegion( &gTimeCompressionMask[ 2 ],
			UI_BOTTOM.Text.CompressTime.x,
			UI_BOTTOM.Text.CompressTime.y,
			UI_BOTTOM.Text.CompressTime.width,
			UI_BOTTOM.Text.CompressTime.height,
			MSYS_PRIORITY_HIGHEST - 1, MSYS_NO_CURSOR, MSYS_NO_CALLBACK, CompressMaskClickCallback 
		);
		RemoveMouseRegionForPauseOfClock();
		fCreated = TRUE;
	}
	else if( ( fDisabled == FALSE ) && ( fCreated ) )
	{
		// created and no longer need to disable
		MSYS_RemoveRegion( &gTimeCompressionMask[ 0 ] );
		MSYS_RemoveRegion( &gTimeCompressionMask[ 1 ] );
		MSYS_RemoveRegion( &gTimeCompressionMask[ 2 ] );
		CreateMouseRegionForPauseOfClock( INTERFACE_CLOCK_X, INTERFACE_CLOCK_Y );
		fCreated = FALSE;
	}
}


void CompressMaskClickCallback( MOUSE_REGION * pRegion, INT32 iReason )
{
	if( iReason & MSYS_CALLBACK_REASON_LBUTTON_UP )
	{
		TellPlayerWhyHeCantCompressTime( );
	}
}


void DisplayProjectedDailyMineIncome( void )
{
	INT32 iRate = 0;
	static INT32 iOldRate = -1;
	CHAR16 sString[ 128 ];
	INT16 sFontX, sFontY;

	// grab the rate from the financial system
	iRate = GetProjectedTotalDailyIncome( );

	if( iRate != iOldRate )
	{
		iOldRate = iRate;
		fMapScreenBottomDirty = TRUE;

		// if screen was not dirtied, leave
		if( fMapBottomDirtied == FALSE )
		{
			return;
		}
	}
		// ste the font buffer
	SetFontDestBuffer( FRAME_BUFFER, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, FALSE );

	// set up the font
	SetFont( COMPFONT );
	SetFontForeground( 183 );
	SetFontBackground( FONT_BLACK );

	swprintf( sString, L"%d", iRate );

	// insert
	InsertCommasForDollarFigure( sString );
	InsertDollarSignInToString( sString );

	VarFindFontCenterCoordinates( 
		UI_BOTTOM.Text.CurrentIncome.x, 
		UI_BOTTOM.Text.CurrentIncome.y,
		UI_BOTTOM.Text.CurrentIncome.width,
		UI_BOTTOM.Text.CurrentIncome.height,
		COMPFONT, &sFontX, &sFontY, sString 
	);

	// print it
	mprintf( sFontX, sFontY, L"%s", sString );

	return;
}

void DisplayProjectedDailyExpenses( void )
{
	INT32 iRate = 0;
	static INT32 iOldExpensesRate = -1;
	CHAR16 sString[ 128 ];
	INT16 sFontX, sFontY;

	// grab the rate from the financial system
	iRate = GetProjectedExpenses( );

	if( iRate != iOldExpensesRate )
	{
		iOldExpensesRate = iRate;
		fMapScreenBottomDirty = TRUE;

		// if screen was not dirtied, leave
		if( fMapBottomDirtied == FALSE )
		{
			return;
		}
	}

	// set the font buffer
	SetFontDestBuffer( FRAME_BUFFER, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, FALSE );

	// set up the font
	SetFont( COMPFONT );
	if (iRate == 0)
	{
		SetFontForeground( 183 );
	}
	else
	{
		SetFontForeground( FONT_MCOLOR_LTRED );
	}
	SetFontBackground( FONT_BLACK );

	swprintf( sString, L"%d", iRate );

	// insert extra characters
	InsertCommasForDollarFigure( sString );
	InsertDollarSignInToString( sString );

	VarFindFontCenterCoordinates(
		UI_BOTTOM.Text.CurrentExpenses.x, 
		UI_BOTTOM.Text.CurrentExpenses.y, 
		UI_BOTTOM.Text.CurrentExpenses.width,
		UI_BOTTOM.Text.CurrentExpenses.height,
		COMPFONT, &sFontX, &sFontY, sString 
	);

	// print it
	mprintf( sFontX, sFontY, L"%s", sString );

	return;
}


BOOLEAN CommonTimeCompressionChecks( void )
{
	if( IsMapScreenHelpTextUp() )
	{
		// stop mapscreen text
		StopMapScreenHelpText( );
		return( TRUE );
	}

	if ( ( GetSelectedDestChar() != -1) || fPlotForHelicopter || fPlotForMilitia )
	{
		// abort plotting movement
		AbortMovementPlottingMode( );
		return( TRUE );
	}

//	if ( gfInAirRaid )
//		return TRUE;

	return( FALSE );
}



BOOLEAN AnyUsableRealMercenariesOnTeam( void )
{
	SOLDIERTYPE *pSoldier = NULL;
	INT32 iCounter = 0, iNumberOnTeam = 0;

	// this is for speed, this runs once/frame
	iNumberOnTeam = gTacticalStatus.Team[ OUR_TEAM ].bLastID;

	// get number of mercs on team who are not vehicles or robot, POWs or EPCs
	for( iCounter = 0; iCounter < iNumberOnTeam; iCounter++ )
	{
		pSoldier = &Menptr[ iCounter ];

		if( ( pSoldier->bActive ) && ( pSoldier->stats.bLife > 0 ) &&
				!( pSoldier->flags.uiStatusFlags & SOLDIER_VEHICLE ) && !AM_A_ROBOT( pSoldier ) &&
				( pSoldier->bAssignment != ASSIGNMENT_POW ) &&
				( pSoldier->bAssignment != ASSIGNMENT_DEAD ) &&
				( pSoldier->bAssignment != ASSIGNMENT_MINIEVENT ) &&
				( pSoldier->ubWhatKindOfMercAmI != MERC_TYPE__EPC ) )
		{
			return( TRUE );
		}
	}

	return( FALSE );
}


void RequestTriggerExitFromMapscreen( INT8 bExitToWhere )
{
#ifdef JA2UB
	Assert( ( bExitToWhere >= MAP_EXIT_TO_LAPTOP ) && ( bExitToWhere <= MAP_EXIT_TO_INTRO_SCREEN ));//MAP_EXIT_TO_MAINMENU ) );
#else
	Assert( ( bExitToWhere >= MAP_EXIT_TO_LAPTOP ) && ( bExitToWhere <= MAP_EXIT_TO_MAINMENU ));
#endif
	// if allowed to do so
	if ( AllowedToExitFromMapscreenTo( bExitToWhere ) )
	{
		//if the screen to exit to is the SAVE screen
		if( bExitToWhere == MAP_EXIT_TO_SAVE )
		{
			//if the game CAN NOT be saved
			if( !CanGameBeSaved() )
			{
				//Display a message saying the player cant save now
				switch( gGameOptions.ubIronManMode )
				{
					case 2:
						DoMapMessageBox( MSG_BOX_BASIC_STYLE, zNewTacticalMessages[ TCTL_MSG__SOFT_IRON_MAN_CANT_SAVE_NOW ], MAP_SCREEN, MSG_BOX_FLAG_OK, NULL );
						break;
					case 3:
						CHAR16 zTemp[320];
						swprintf( zTemp, zNewTacticalMessages[ TCTL_MSG__EXTREME_IRON_MAN_CANT_SAVE_NOW ], gGameExternalOptions.ubExtremeIronManSavingHour);
						DoMapMessageBox( MSG_BOX_BASIC_STYLE, zTemp, MAP_SCREEN, MSG_BOX_FLAG_OK, NULL );
						break;
					default:
						DoMapMessageBox( MSG_BOX_BASIC_STYLE, zNewTacticalMessages[ TCTL_MSG__IRON_MAN_CANT_SAVE_NOW ], MAP_SCREEN, MSG_BOX_FLAG_OK, NULL );
						break;
				}
				return;
			}
		}
		else if (!(bExitToWhere == MAP_EXIT_TO_TACTICAL || bExitToWhere == MAP_EXIT_TO_MAINMENU))
		{
			// OJW - 20090301
			if (is_networked && is_client)
			{
				if (client_ready[CLIENT_NUM-1]==1 && is_game_started == false)
				{
					// un-ready if we are not on map screen
					start_battle();
				}
			}
		}

		// permit it, and get the ball rolling
		gbExitingMapScreenToWhere = bExitToWhere;

		// delay until mapscreen has had a chance to render at least one full frame
		gfOneFramePauseOnExit = TRUE;

		//CHRISL: To force activation of tactical team panel buttons when switching to tactical
		gfDisableTacticalPanelButtons = FALSE;
	}
}


BOOLEAN AllowedToExitFromMapscreenTo( INT8 bExitToWhere )
{
#ifdef JA2UB
	Assert( ( bExitToWhere >= MAP_EXIT_TO_LAPTOP ) && ( bExitToWhere <= MAP_EXIT_TO_INTRO_SCREEN)); //MAP_EXIT_TO_MAINMENU ) );
#else
	Assert( ( bExitToWhere >= MAP_EXIT_TO_LAPTOP ) && ( bExitToWhere <= MAP_EXIT_TO_MAINMENU ) );
#endif
	// if already leaving, disallow any other attempts to exit
	if ( fLeavingMapScreen )
	{
		return( FALSE );
	}

	// OJW - 20090210 - clean resources on disconnect
	if (bExitToWhere == MAP_EXIT_TO_MAINMENU)
	{
		// always allow this
		return( TRUE );
	}

	// WANNE: At least one merc must be hired so we can go to tactical.
	// This was an exploit. If you go to tactical in Omerta and have not hired any merc
	// then go back to laptop and hire a merc. After the merc arrives the heli landing was not
	// shown and the sector was free of enemies.
	if (bExitToWhere == MAP_EXIT_TO_TACTICAL && gfAtLeastOneMercWasHired == FALSE)
	{
		return ( FALSE );
	}

	// if already going someplace else
	if ( ( gbExitingMapScreenToWhere != -1 ) && ( gbExitingMapScreenToWhere != bExitToWhere ) )
	{
		return( FALSE );
	}

	// someone has something to say
	if ( !DialogueQueueIsEmpty() )
	{
		return( FALSE );
	}

#ifdef JA2UB
//Ja25 No meanwhiles
#else
	// meanwhile coming up
	if ( gfMeanwhileTryingToStart )
	{
		return( FALSE );
	}
#endif

	// if we're locked into paused time compression by some event that enforces that
	if ( PauseStateLocked() )
	{
		return( FALSE );
	}

	// if holding an inventory item
	if ( fMapInventoryItem || ( gMPanelRegion.Cursor == EXTERN_CURSOR ) )
	{
		return( FALSE );
	}

	if( fShowUpdateBox || fShowTownInfo || ( sSelectedMilitiaTown != 0 ) )
	{
		return( FALSE );
	}

	// renewing contracts
	if( gfContractRenewalSquenceOn )
	{
		return( FALSE );
	}

	// battle about to occur?
	if( ( fDisableDueToBattleRoster ) || ( fDisableMapInterfaceDueToBattle ) )
	{
		return( FALSE );
	}

/*
	// air raid starting
	if( gubAirRaidMode == AIR_RAID_START )
	{
		// nope
		return( FALSE );
	}
*/


	// the following tests apply to going tactical screen only
	if ( bExitToWhere == MAP_EXIT_TO_TACTICAL )
	{
		// if in battle or air raid, the ONLY sector we can go tactical in is the one that's loaded
		if ( ( ( gTacticalStatus.uiFlags & INCOMBAT ) || ( gTacticalStatus.fEnemyInSector ) /*|| InAirRaid( )*/ ) &&
				( ( sSelMapX != gWorldSectorX ) || ( sSelMapY != gWorldSectorY ) || ( ( UINT8 )iCurrentMapSectorZ ) != gbWorldSectorZ ) )
		{
			return( FALSE );
		}

		// must have some mercs there
		if( !CanGoToTacticalInSector( sSelMapX, sSelMapY, ( UINT8 )iCurrentMapSectorZ ) )
		{
			return( FALSE );
		}
		if ( (( sSelMapX != gWorldSectorX ) || ( sSelMapY != gWorldSectorY ) || (( UINT8 )iCurrentMapSectorZ ) != gbWorldSectorZ  ) && is_client) //hayden (PBI only)
		{
			return (FALSE);
		}
	}

	//if we are map screen sector inventory
	if( fShowMapInventoryPool )
	{
		//dont allow it
		return( FALSE );
	}

	//hayden
	if( bExitToWhere == MAP_EXIT_TO_LAPTOP && !allowlaptop && is_networked /*&& (is_client || is_server)*/)
	{
		//dont allow it
		return( FALSE );
	}

	// OK to go there, passed all the checks
	return( TRUE );
}


void HandleExitsFromMapScreen( void )
{
	// if going somewhere
	if ( gbExitingMapScreenToWhere != -1 )
	{
		// delay all exits by one frame...
		if( gfOneFramePauseOnExit == TRUE )
		{
			gfOneFramePauseOnExit = FALSE;
			return;
		}

		// make sure it's still legal to do this!
		if ( AllowedToExitFromMapscreenTo( gbExitingMapScreenToWhere ) )
		{
			// see where we're trying to go
			switch ( gbExitingMapScreenToWhere )
			{
				case MAP_EXIT_TO_LAPTOP:
					fLapTop = TRUE;
					SetPendingNewScreen(LAPTOP_SCREEN);

					if( gfExtraBuffer )
					{ //Then initiate the transition animation from the mapscreen to laptop...

						BlitBufferToBuffer( FRAME_BUFFER, guiEXTRABUFFER, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT );
						gfStartMapScreenToLaptopTransition = TRUE;
					}
					break;

				case MAP_EXIT_TO_TACTICAL:
					SetCurrentWorldSector( sSelMapX, sSelMapY, ( INT8 )iCurrentMapSectorZ );
					// silversurfer: bugfix for panel redraw issue after quickload from tactical screen
					// this will make sure that the tactical screen gets refreshed and guiTacticalInterfaceFlags is reset
					fFirstTimeInGameScreen = TRUE;
					guiTacticalInterfaceFlags &= (~INTERFACE_MAPSCREEN );
					break;

				case MAP_EXIT_TO_OPTIONS:
					SetOptionsPreviousScreen(guiCurrentScreen);
					SetPendingNewScreen( OPTIONS_SCREEN );
					break;

				case MAP_EXIT_TO_SAVE:
				case MAP_EXIT_TO_LOAD:
					gfCameDirectlyFromGame = TRUE;
					SetOptionsPreviousScreen(guiCurrentScreen);
					SetPendingNewScreen( SAVE_LOAD_SCREEN );
					break;
				// OJW - 20090210 - clean resources on disconnect
				case MAP_EXIT_TO_MAINMENU:
					// Re-initialise the game
					ReStartingGame();
					SetPendingNewScreen( MAINMENU_SCREEN );
					break;
#ifdef JA2UB
				//JA25 ub
				case MAP_EXIT_TO_INTRO_SCREEN:
				//	SetPendingNewScreen( INTRO_SCREEN );
					BeginLoadScreen();
					break;
#endif
				default:
					// invalid exit type
					Assert( FALSE );
			}

			// time compression during mapscreen exit doesn't seem to cause any problems, but turn it off as early as we can
			StopTimeCompression();

			// now leaving mapscreen
			fLeavingMapScreen = TRUE;
		}

		// cancel exit, either we're on our way, or we're not allowed to go
		gbExitingMapScreenToWhere = -1;
	}
}



void MapScreenMsgScrollDown( UINT8 ubLinesDown )
{
	UINT8 ubNumMessages;

	ubNumMessages = GetRangeOfMapScreenMessages();

	// check if we can go that far, only go as far as we can
	if ( ( gubFirstMapscreenMessageIndex + MAX_MESSAGES_ON_MAP_BOTTOM + ubLinesDown ) > ubNumMessages )
	{
		ubLinesDown = ubNumMessages - gubFirstMapscreenMessageIndex - min( ubNumMessages, MAX_MESSAGES_ON_MAP_BOTTOM );
	}

	if ( ubLinesDown > 0 )
	{
		ChangeCurrentMapscreenMessageIndex( ( UINT8 ) ( gubFirstMapscreenMessageIndex + ubLinesDown ) );
	}
}


void MapScreenMsgScrollUp( UINT8 ubLinesUp )
{
	UINT8 ubNumMessages;

	ubNumMessages = GetRangeOfMapScreenMessages();

	// check if we can go that far, only go as far as we can
	if ( gubFirstMapscreenMessageIndex < ubLinesUp )
	{
		ubLinesUp = gubFirstMapscreenMessageIndex;
	}

	if ( ubLinesUp > 0 )
	{
		ChangeCurrentMapscreenMessageIndex( ( UINT8 ) ( gubFirstMapscreenMessageIndex - ubLinesUp ) );
	}
}



void MoveToEndOfMapScreenMessageList( void )
{
	UINT8 ubDesiredMessageIndex;
	UINT8 ubNumMessages;

	ubNumMessages = GetRangeOfMapScreenMessages();

	ubDesiredMessageIndex = ubNumMessages - min( ubNumMessages, MAX_MESSAGES_ON_MAP_BOTTOM );
	ChangeCurrentMapscreenMessageIndex( ubDesiredMessageIndex );
}



void ChangeCurrentMapscreenMessageIndex( UINT8 ubNewMessageIndex )
{
	Assert( ubNewMessageIndex + MAX_MESSAGES_ON_MAP_BOTTOM <= max( MAX_MESSAGES_ON_MAP_BOTTOM, GetRangeOfMapScreenMessages() ) );

	gubFirstMapscreenMessageIndex = ubNewMessageIndex;
	gubCurrentMapMessageString = ( gubStartOfMapScreenMessageList + gubFirstMapscreenMessageIndex ) % 256;

	// set fact we just went to a new message
//	gfNewScrollMessage = TRUE;

	// refresh screen
	fMapScreenBottomDirty = TRUE;
}


void InitMapScreenInterfaceBottomCoords( void )
{
	if (isWidescreenUI())
	{
		UI_BOTTOM.Region = { 0, (SCREEN_HEIGHT - 116), 1280, 116 };
		const auto x = UI_BOTTOM.Region.x;
		const auto y = UI_BOTTOM.Region.y;
		const auto width = UI_BOTTOM.Region.width;
		const auto height = UI_BOTTOM.Region.height;
		UI_BOTTOM.Text.CompressTime = { x + 1129,  y + 94, 34, 13 };
		UI_BOTTOM.Text.BalanceTitle = { x + 1002, y + 5, 78, 10 };
		UI_BOTTOM.Text.IncomeTitle = { x + 1002, y + 42, 78, 10 };
		UI_BOTTOM.Text.ExpensesTitle = { x + 1002, y + 79, 78, 10 };
		UI_BOTTOM.Text.CurrentBalance= { x + 1002, y + 22, 78, 10 };
		UI_BOTTOM.Text.CurrentIncome= { x + 1002, y + 59, 78, 10 };
		UI_BOTTOM.Text.CurrentExpenses= { x + 1002, y + 96, 78, 10 };
		UI_BOTTOM.Text.LoadedSector = { x + (width - 92), y + (height - 55), 80, 16, };

		UI_BOTTOM.Button.CompressTime = { x + 1168, y + 94 };
		UI_BOTTOM.Button.CompressLess = { x + 1106, y + 94 };
		UI_BOTTOM.Button.Laptop = { x + 1096, y + 48 };
		UI_BOTTOM.Button.Options = { x + 1098, y + 10 };
		UI_BOTTOM.Button.Tactical = { x + 1136, y + 48 };
		UI_BOTTOM.Button.ScrollUp = { x + 718, y + 9 };
		UI_BOTTOM.Button.ScrollDown = { x + 718, y + 90 };

		// For external use
		UI_BOTTOM_X = x;
		UI_BOTTOM_Y = y;
		MAP_BOTTOM_LAPTOP_X = UI_BOTTOM.Button.Laptop.iX;
		MAP_BOTTOM_LAPTOP_Y = UI_BOTTOM.Button.Laptop.iY;

		// HEADROCK HAM 3.6: Message window is now as wide as possible.
		MESSAGE_SCROLL_AREA_START_X = x + 718;
		MESSAGE_SCROLL_AREA_END_X = x + 732;
	}
	else
	{
		UI_BOTTOM.Region = { xResOffset, (SCREEN_HEIGHT - 121), xResSize, 121 };
		const auto x = UI_BOTTOM.Region.x;
		const auto y = UI_BOTTOM.Region.y;
		const auto width = UI_BOTTOM.Region.width;
		const auto height = UI_BOTTOM.Region.height;
		UI_BOTTOM.Text.CompressTime = { x + width - 153,  y + 96, 34, 13 };
		UI_BOTTOM.Text.BalanceTitle = { x + width - 278, y + 10, 78, 10 };
		UI_BOTTOM.Text.IncomeTitle = { x + width - 278, y + 47, 78, 10 };
		UI_BOTTOM.Text.ExpensesTitle = { x + width - 278, y + 84, 78, 10 };
		UI_BOTTOM.Text.CurrentBalance = { x + width - 278, y + height - 94, 78, 10 };
		UI_BOTTOM.Text.CurrentIncome = { x + width - 278, y + height - 57, 78, 10 };
		UI_BOTTOM.Text.CurrentExpenses = { x + width - 278, y + height - 20, 78, 10 };
		UI_BOTTOM.Text.LoadedSector = { x + (width - 92), y + (height - 55), 80, 16, };

		UI_BOTTOM.Button.CompressTime = { x + (width - 112), y + 97 };
		UI_BOTTOM.Button.CompressLess = { x + (width - 174), y + 97 };
		UI_BOTTOM.Button.Laptop = { x + (width - 184), y + 51 };
		UI_BOTTOM.Button.Options = { x + (width - 182), y + 13 };
		UI_BOTTOM.Button.Tactical = { x + (width - 144), y + 51 };
		UI_BOTTOM.Button.ScrollUp = { x + (width - 306), y + 12 };
		UI_BOTTOM.Button.ScrollDown = { x + (width - 306), y + 93 };

		UI_BOTTOM_X = x;
		UI_BOTTOM_Y = y;
		MAP_BOTTOM_LAPTOP_X = UI_BOTTOM.Button.Laptop.iX;
		MAP_BOTTOM_LAPTOP_Y = UI_BOTTOM.Button.Laptop.iY;

		// HEADROCK HAM 3.6: Message window is now as wide as possible.
		MESSAGE_SCROLL_AREA_START_X = xResOffset + xResSize - 307;
		MESSAGE_SCROLL_AREA_END_X = xResOffset + xResSize - 293;
	}
}
