/**
* @file
* @author Flugente (bears-pit.com)
*/

#include "builddefines.h"
#include <stdio.h>
#include <time.h>
#include "sgp.h"
#include "gameloop.h"
#include "MiniGame.h"
#include "WCheck.h"
#include "Utilities.h"
#include "WordWrap.h"
#include "Cursors.h"
#include "Line.h"
#include "Insurance Text.h"
#include "Encrypted File.h"
#include "Text.h"
#include "Multi Language Graphic Utils.h"
#include "random.h"
#include "Interface.h"
#include "DropDown.h"
#include "finances.h"
#include "Game Clock.h"
#include "GameVersion.h"
#include "GameSettings.h"
#include "message.h"
#include "Game Event Hook.h"
#include "Overhead.h"
#include "Sound Control.h"
#include "himage.h"
#include "vobject.h"
#include "vobject_blitters.h"
#include "wcheck.h"
#include "input.h"
#include "font.h"
#include "timer.h"
#include "mousesystem.h"
#include <wchar.h>
#include <tchar.h>
#include "Timer Control.h"
#include "Sys Globals.h"
#include "interface.h"
#include "utilities.h"
#include "render dirty.h"
#include "cursor control.h"
#include "utilities.h"
#include "english.h"
#include "WordWrap.h"
#include "gamescreen.h"
#include "Isometric Utils.h"
#include "math.h"

// remember the previous screen
UINT32 guiMiniGamePreviousScreen = GAME_SCREEN;

int gMiniGame = MINIGAME_FIRST;
int gMiniGameState = MINIGAME_STARTSCREEN;
BOOLEAN gMiniGameIsInit = FALSE;

void SetNextGame( UINT32 ausGame, UINT32 ausGameState )
{
	gMiniGame = ausGame;
	gMiniGameState = ausGameState;

	guiMiniGamePreviousScreen = guiCurrentScreen;
}

void MiniGameDataInit()
{
	if ( !gMiniGameIsInit )
	{
		switch ( gMiniGame )
		{
		case TETRIS:
			MiniGame_Init_Tetris( );
			break;

		case PONG:
			MiniGame_Init_Pong( );
			break;

		case PICTURE:
			MiniGame_Init_Picture();
			break;

		default:
			break;
		}

		gMiniGameIsInit = TRUE;
	}
}

BOOLEAN MiniGameExit()
{
	gMiniGameIsInit = FALSE;

	gMiniGame = MINIGAME_FIRST;
	gMiniGameState = MINIGAME_STARTSCREEN;

	SetPendingNewScreen( GAME_SCREEN );

	FadeInGameScreen();

	HandleTacticalUI( );

	return GAME_SCREEN;
}

UINT32	MiniGameScreenInit( void )
{
	return TRUE;
}

UINT32	MiniGameScreenHandle( void )
{
	if ( gfKeyState[ESC] )
	{
		return MiniGameExit();
	}

	// init game data if not already done so
	MiniGameDataInit();

	switch ( gMiniGame )
	{
	case TETRIS:
		return MiniGame_Handle_Tetris( );
		break;

	case PONG:
		return MiniGame_Handle_Pong( );
		break;

	case PICTURE:
		return MiniGame_Handle_Picture();
		break;

	default:
		break;
	}

	return MINIGAME_SCREEN;
}

UINT32	MiniGameScreenShutdown( void )
{
	return TRUE;
}

void DisplayPNGImage( SGPRect aDstRect, std::string& arStrImage, bool aStretch )
{
	VSURFACE_DESC		vs_desc = {};
	HVSURFACE			hVSurface;
	UINT32				uiLoadScreen;

	vs_desc.fCreateFlags = VSURFACE_CREATE_FROMFILE | VSURFACE_SYSTEM_MEM_USAGE | VSURFACE_CREATE_FROMPNG_FALLBACK;

	arStrImage.copy( vs_desc.ImageFile, sizeof(vs_desc.ImageFile) - 1 );

	if ( FileExists( vs_desc.ImageFile ) && AddVideoSurface( &vs_desc, &uiLoadScreen ) )
	{
		//Blit the background image
		GetVideoSurface( &hVSurface, uiLoadScreen );

		// Stretch the background image
		SGPRect SrcRect;
		SrcRect.iLeft = 0;
		SrcRect.iTop = 0;
		SrcRect.iRight = hVSurface->usWidth;
		SrcRect.iBottom = hVSurface->usHeight;

		// if we're not supposed to stretch it, alter aDstRect
		INT32 width = SrcRect.iRight - SrcRect.iLeft;
		INT32 height = SrcRect.iBottom - SrcRect.iTop;

		INT32 width_dest = aDstRect.iRight - aDstRect.iLeft;
		INT32 height_dest = aDstRect.iBottom - aDstRect.iTop; 
		
		if ( !aStretch || width > width_dest || height > height_dest )
		{
			if ( width > 0 && height > 0 )
			{
				aDstRect.iLeft   = max(0, ( aDstRect.iRight + aDstRect.iLeft ) / 2 - width / 2 );
				aDstRect.iRight  = min( aDstRect.iRight, aDstRect.iLeft + width);
				aDstRect.iTop	 = max(0, ( aDstRect.iBottom + aDstRect.iTop ) / 2 - height / 2 );
				aDstRect.iBottom = min( aDstRect.iBottom, aDstRect.iTop + height );
			}
		}

		BltStretchVideoSurface( FRAME_BUFFER, uiLoadScreen, 0, 0, 0, &SrcRect, &aDstRect );

		DeleteVideoSurfaceFromIndex( uiLoadScreen );
	}
	else
	{
		ColorFillVideoSurfaceArea( FRAME_BUFFER, aDstRect.iLeft, aDstRect.iTop, aDstRect.iRight, aDstRect.iBottom, Get16BPPColor( FROMRGB( 55, 55, 55 ) ) );
	}
}

// On order to wrk in all resolutions, limit our screen to this
#define MINIGAME_BG_X_LOW	iScreenWidthOffset
#define MINIGAME_BG_X_HIGH	(SCREEN_WIDTH - iScreenWidthOffset)
#define MINIGAME_BG_Y_LOW	iScreenHeightOffset
#define MINIGAME_BG_Y_HIGH	(SCREEN_HEIGHT - iScreenHeightOffset)

BOOLEAN gMiniGameRenewBackground = TRUE;						// In order to save cpu-time (not really an issue here, but it's the thought that counts!), only redraw the background if we have to

int gMiniGameMenuSelection = 0;								// In a small menu, we can select some options, so we need to keep track where we are
int gMiniGameMenuScreen = 0;								// What page of the menu is currently used?

////////////////////////////////////////////// TETRIS //////////////////////////////////////////////
// In case you somehow don't know how Tetris works:
// In this game, the player has to rotate and move a tile, consisting of several types of each 4 blocks put together in a variety of orientations, while it falls down.
// Once the tile has been placed, a new tile appears.
// If the player manages t complete a full line of blocks, that line is erased, and points are awarded.
// The game is to reach a high a score as possible.

// This defines what tiles a block can belong to
enum
{
	TETRISBLOCKTYPE_NONE = 0,			// empty block
	TETRISBLOCKTYPE_L,
	TETRISBLOCKTYPE_L_INVERTED,
	TETRISBLOCKTYPE_T,
	TETRISBLOCKTYPE_SQUARE,
	TETRISBLOCKTYPE_LONG,
	TETRISBLOCKTYPE_Z,
	TETRISBLOCKTYPE_Z_INVERTED,
	TETRISBLOCKTYPE_CONFLICT,			// we spawned a new tile into an existing tile. If this happens the game will end

	TETRISBLOCKTYPE_MAX
};

// we use this struct to remember whether a block is moving, controlled by the player, and what block type it has (necessary for colouring)
typedef struct
{
	BOOLEAN resting;
	BOOLEAN playercontrolled;
	INT8 blocktype;
} TETRIS_BLOCK;

// as it would be tedious to always determine the player-controlled blocks again, we remember them with this struct
typedef struct
{
	int x;
	int y;
} TETRIS_BLOCK_PLAYER;

// the dimensions of the playing field
#define TETRIS_FIELD_ROWS	18
#define TETRIS_FIELD_COLS	10

// we define out playing field
TETRIS_BLOCK gTetrisBlock[TETRIS_FIELD_ROWS][TETRIS_FIELD_COLS];

// rememeber the tiles the player controls (easier than to always look for them)
TETRIS_BLOCK_PLAYER gTetrisBlockPlayer[4];

// each tile can be rotated, so a tile has 4 orientations (for some types, these are identical)
#define TETRIS_ORIENTATIONS 4

// a little helper struct to define a pair of coordinates
typedef struct coordpair
{
	int x;
	int y;

	coordpair( int a, int b ) : x( a ), y( b ) {}
	coordpair( ){}
} coordpair;

// Each blocktype has 4 orientations. This struct contains the x- and y-offsets for each of the 4 blocks
// When we later place a tile, we simpyl determine the 'anchor'-coordinates, and then simply have to add the offsets to get the 4 blocks
// This of course relies on one block (the first one) retaining the same coordinates (no offset then).
typedef struct TETRIS_BLOCK_ORIENTATION
{
	coordpair cords[4];

	TETRIS_BLOCK_ORIENTATION( coordpair a, coordpair b, coordpair c, coordpair d )
	{
		cords[0] = a;
		cords[1] = b;
		cords[2] = c;
		cords[3] = d;
	}

	TETRIS_BLOCK_ORIENTATION( ){}
} TETRIS_BLOCK_ORIENTATION;

TETRIS_BLOCK_ORIENTATION gTetrisOrientation[TETRISBLOCKTYPE_MAX][TETRIS_ORIENTATIONS] =
{
	// TETRISBLOCKTYPE_NONE
	TETRIS_BLOCK_ORIENTATION( coordpair( 0, 0 ), coordpair( 0, 0 ), coordpair( 0, 0 ), coordpair( 0, 0 ) ),
	TETRIS_BLOCK_ORIENTATION( coordpair( 0, 0 ), coordpair( 0, 0 ), coordpair( 0, 0 ), coordpair( 0, 0 ) ),
	TETRIS_BLOCK_ORIENTATION( coordpair( 0, 0 ), coordpair( 0, 0 ), coordpair( 0, 0 ), coordpair( 0, 0 ) ),
	TETRIS_BLOCK_ORIENTATION( coordpair( 0, 0 ), coordpair( 0, 0 ), coordpair( 0, 0 ), coordpair( 0, 0 ) ),

	// TETRISBLOCKTYPE_L
	TETRIS_BLOCK_ORIENTATION( coordpair( 0, 0 ), coordpair( 0, -1 ), coordpair( 0, 1 ), coordpair( 1, 1 ) ),
	TETRIS_BLOCK_ORIENTATION( coordpair( 0, 0 ), coordpair( -1, 1 ), coordpair( -1, 0 ), coordpair( 1, 0 ) ),
	TETRIS_BLOCK_ORIENTATION( coordpair( 0, 0 ), coordpair( -1, -1 ), coordpair( 0, -1 ), coordpair( 0, 1 ) ),
	TETRIS_BLOCK_ORIENTATION( coordpair( 0, 0 ), coordpair( -1, 0 ), coordpair( 1, 0 ), coordpair( 1, -1 ) ),

	// TETRISBLOCKTYPE_L_INVERTED
	TETRIS_BLOCK_ORIENTATION( coordpair( 0, 0 ), coordpair( 0, -1 ), coordpair( 0, 1 ), coordpair( -1, 1 ) ),
	TETRIS_BLOCK_ORIENTATION( coordpair( 0, 0 ), coordpair( 1, 0 ), coordpair( -1, 0 ), coordpair( -1, -1 ) ),
	TETRIS_BLOCK_ORIENTATION( coordpair( 0, 0 ), coordpair( 0, -1 ), coordpair( 0, 1 ), coordpair( 1, -1 ) ),
	TETRIS_BLOCK_ORIENTATION( coordpair( 0, 0 ), coordpair( 1, 0 ), coordpair( -1, 0 ), coordpair( 1, 1 ) ),

	// TETRISBLOCKTYPE_T	
	TETRIS_BLOCK_ORIENTATION( coordpair( 0, 0 ), coordpair( -1, 0 ), coordpair( 1, 0 ), coordpair( 0, 1 ) ),
	TETRIS_BLOCK_ORIENTATION( coordpair( 0, 0 ), coordpair( 1, 0 ), coordpair( 0, 1 ), coordpair( 0, -1 ) ),
	TETRIS_BLOCK_ORIENTATION( coordpair( 0, 0 ), coordpair( -1, 0 ), coordpair( 1, 0 ), coordpair( 0, -1 ) ),
	TETRIS_BLOCK_ORIENTATION( coordpair( 0, 0 ), coordpair( -1, 0 ), coordpair( 0, -1 ), coordpair( 0, 1 ) ),

	// TETRISBLOCKTYPE_SQUARE
	TETRIS_BLOCK_ORIENTATION( coordpair( 0, 0 ), coordpair( -1, 0 ), coordpair( 0, -1 ), coordpair( -1, -1 ) ),
	TETRIS_BLOCK_ORIENTATION( coordpair( 0, 0 ), coordpair( -1, 0 ), coordpair( 0, -1 ), coordpair( -1, -1 ) ),
	TETRIS_BLOCK_ORIENTATION( coordpair( 0, 0 ), coordpair( -1, 0 ), coordpair( 0, -1 ), coordpair( -1, -1 ) ),
	TETRIS_BLOCK_ORIENTATION( coordpair( 0, 0 ), coordpair( -1, 0 ), coordpair( 0, -1 ), coordpair( -1, -1 ) ),

	// TETRISBLOCKTYPE_LONG
	TETRIS_BLOCK_ORIENTATION( coordpair( 0, 0 ), coordpair( 0, -1 ), coordpair( 0, 1 ), coordpair( 0, 2 ) ),
	TETRIS_BLOCK_ORIENTATION( coordpair( 0, 0 ), coordpair( -1, 0 ), coordpair( 1, 0 ), coordpair( 2, 0 ) ),
	TETRIS_BLOCK_ORIENTATION( coordpair( 0, 0 ), coordpair( 0, -1 ), coordpair( 0, 1 ), coordpair( 0, 2 ) ),
	TETRIS_BLOCK_ORIENTATION( coordpair( 0, 0 ), coordpair( -1, 0 ), coordpair( 1, 0 ), coordpair( 2, 0 ) ),

	// TETRISBLOCKTYPE_Z
	TETRIS_BLOCK_ORIENTATION( coordpair( 0, 0 ), coordpair( 1, 0 ), coordpair( 0, 1 ), coordpair( -1, 1 ) ),
	TETRIS_BLOCK_ORIENTATION( coordpair( 0, 0 ), coordpair( -1, -1 ), coordpair( -1, 0 ), coordpair( 0, 1 ) ),
	TETRIS_BLOCK_ORIENTATION( coordpair( 0, 0 ), coordpair( 1, 0 ), coordpair( 0, 1 ), coordpair( -1, 1 ) ),
	TETRIS_BLOCK_ORIENTATION( coordpair( 0, 0 ), coordpair( -1, -1 ), coordpair( -1, 0 ), coordpair( 0, 1 ) ),

	// TETRISBLOCKTYPE_Z_INVERTED
	TETRIS_BLOCK_ORIENTATION( coordpair( 0, 0 ), coordpair( -1, 0 ), coordpair( 0, 1 ), coordpair( 1, 1 ) ),
	TETRIS_BLOCK_ORIENTATION( coordpair( 0, 0 ), coordpair( 0, -1 ), coordpair( -1, 0 ), coordpair( -1, 1 ) ),
	TETRIS_BLOCK_ORIENTATION( coordpair( 0, 0 ), coordpair( -1, 0 ), coordpair( 0, 1 ), coordpair( 1, 1 ) ),
	TETRIS_BLOCK_ORIENTATION( coordpair( 0, 0 ), coordpair( 0, -1 ), coordpair( -1, 0 ), coordpair( -1, 1 ) ),

	// TETRISBLOCKTYPE_CONFLICT
	TETRIS_BLOCK_ORIENTATION( coordpair( 0, 0 ), coordpair( 0, 0 ), coordpair( 0, 0 ), coordpair( 0, 0 ) ),
	TETRIS_BLOCK_ORIENTATION( coordpair( 0, 0 ), coordpair( 0, 0 ), coordpair( 0, 0 ), coordpair( 0, 0 ) ),
	TETRIS_BLOCK_ORIENTATION( coordpair( 0, 0 ), coordpair( 0, 0 ), coordpair( 0, 0 ), coordpair( 0, 0 ) ),
	TETRIS_BLOCK_ORIENTATION( coordpair( 0, 0 ), coordpair( 0, 0 ), coordpair( 0, 0 ), coordpair( 0, 0 ) ),
};

// a few global variables
BOOLEAN gTetrisplayercontrolledtile = FALSE;				// Does the player currently control a tile? (If not, maybe we should add a new one...)
int gTetrisTileSpawnTime = 0;								// The last tile was spawned at this time (we use this to award points fo placing tiles fast)
int gTetrisplayertileOrientation = 0;						// Orientation of the current tile?
int gTetrisNextTile = TETRISBLOCKTYPE_NONE;					// What will be the type of the next tile that will spawn? We need this as we preview show the player
int gTetrisScore = 0;
int gTetrisLinesDone = 0;
int gTetrisInitialLevel = 0;
int gTetrisHandicap = 0;

// for each level of handicap, we add 2 partially filled lines
#define TETRIS_MAX_HANDICAP	5

void MiniGame_Init_Tetris()
{
	for ( int i = 0; i < TETRIS_FIELD_ROWS; ++i )
	{
		for ( int j = 0; j < TETRIS_FIELD_COLS; ++j )
		{
			gTetrisBlock[i][j].resting = TRUE;
			gTetrisBlock[i][j].playercontrolled = FALSE;
			gTetrisBlock[i][j].blocktype = TETRISBLOCKTYPE_NONE;
		}
	}

	for ( int i = 0; i < 4; ++i )
	{
		gTetrisBlockPlayer[i].x = 0;
		gTetrisBlockPlayer[i].y = 0;
	}

	gTetrisplayercontrolledtile = FALSE;
	gTetrisTileSpawnTime = 0;
	gTetrisplayertileOrientation = 0;
	gTetrisNextTile = TETRISBLOCKTYPE_NONE;
	gTetrisScore = 0;
	gTetrisLinesDone = 0;
		
	gMiniGameMenuSelection = 0;
	gMiniGameMenuScreen = 0;
	gMiniGameRenewBackground = TRUE;

	// don't reset, so we don't have to set it again every time we start a game
	//gTetrisInitialLevel = 0;
	//gTetrisHandicap = 0;
}

void AddScore_Tetris( int aPoints )
{
	// bonus points:
	// - per level (either initial or lines completed / 10) 15%
	// per handicap 10%
	FLOAT bonusperc = 1.0f + (FLOAT)((15 * (gTetrisInitialLevel + gTetrisLinesDone / 10) + gTetrisHandicap * 10) / 100.0f);

	gTetrisScore += aPoints * bonusperc;
}

#define TETRIS_BLOCKLENGTH	20

// game area
#define MINIGAME_TETRIS_X_LOW		(MINIGAME_BG_X_LOW + 100)
#define MINIGAME_TETRIS_X_HIGH		(MINIGAME_TETRIS_X_LOW + TETRIS_FIELD_COLS * TETRIS_BLOCKLENGTH)
#define MINIGAME_TETRIS_Y_LOW		(MINIGAME_BG_Y_LOW + 100)
#define MINIGAME_TETRIS_Y_HIGH		(MINIGAME_TETRIS_Y_LOW + TETRIS_FIELD_ROWS * TETRIS_BLOCKLENGTH)

// we also need a small 4x4 area to display the next tile
#define TETRIS_PREVIEW_X	(MINIGAME_TETRIS_X_HIGH + 50)
#define TETRIS_PREVIEW_Y	(MINIGAME_TETRIS_Y_HIGH - 4 * TETRIS_BLOCKLENGTH)

UINT32 MiniGame_Handle_Tetris()
{
	static UINT32 uiTimeOfLastUpdate = GetJA2Clock( );

	if ( gMiniGame == TETRIS )
	{
		// coordinates for the background rectangle
		if ( gMiniGameRenewBackground )
		{
			SGPRect backgroundrect;
			backgroundrect.iLeft = MINIGAME_BG_X_LOW;
			backgroundrect.iTop = MINIGAME_BG_Y_LOW;
			backgroundrect.iRight = MINIGAME_BG_X_HIGH;
			backgroundrect.iBottom = MINIGAME_BG_Y_HIGH;

			// background: display a png picture
			std::string strImage = "Interface\\background_tetris_game.png";
			if ( gMiniGameState == MINIGAME_STARTSCREEN )
				strImage = "Interface\\background_tetris_startscreen.png";
			else if ( gMiniGameState == MINIGAME_GAMEOVER )
				strImage = "Interface\\background_tetris_gameover.png";

			DisplayPNGImage( backgroundrect, strImage, false );

			gMiniGameRenewBackground = FALSE;
		}
				
		UINT16 menu_x_low = MINIGAME_BG_X_LOW + 250;
		UINT16 menu_x_high = MINIGAME_BG_X_HIGH - 250;
		UINT16 menu_y_low = MINIGAME_BG_Y_LOW + 200;
		UINT16 menu_y_high = MINIGAME_BG_Y_HIGH - 200;

		INT32 fontused = LARGEFONT1;

		SetFontShadow( MILITARY_SHADOW );

		// we have to keep key presses on a delay, otherwise we move to fast
		BOOLEAN keyactivation_up	= FALSE;
		BOOLEAN keyactivation_down	= FALSE;
		BOOLEAN keyactivation_left	= FALSE;
		BOOLEAN keyactivation_right = FALSE;
		BOOLEAN keyactivation_enter	= FALSE;
		BOOLEAN keyactivation_p		= FALSE;

		UINT32 TETRIS_MENU_INPUTDELAY = 120;
		static int gTetristimesincelastmove = 0;
		if ( GetJA2Clock( ) - gTetristimesincelastmove > TETRIS_MENU_INPUTDELAY )
		{
			// arrow keys navigate the menu
			if ( gfKeyState[UPARROW] )
			{
				gTetristimesincelastmove = GetJA2Clock( );

				keyactivation_up = TRUE;
			}

			if ( gfKeyState[DNARROW] )
			{
				gTetristimesincelastmove = GetJA2Clock( );

				keyactivation_down = TRUE;
			}

			if ( gfKeyState[LEFTARROW] )
			{
				gTetristimesincelastmove = GetJA2Clock( );

				keyactivation_left = TRUE;
			}

			if ( gfKeyState[RIGHTARROW] )
			{
				gTetristimesincelastmove = GetJA2Clock( );

				keyactivation_right = TRUE;
			}

			if ( gfKeyState[ENTER] )
			{
				gTetristimesincelastmove = GetJA2Clock( );

				keyactivation_enter = TRUE;
			}

			// 'p' key is 80
			if ( gfKeyState[80] )
			{
				gTetristimesincelastmove = GetJA2Clock( );

				keyactivation_p = TRUE;
			}
		}
		
		InvalidateScreen( );

		SetCurrentCursorFromDatabase( VIDEO_NO_CURSOR );

		if ( keyactivation_p && gMiniGameState == MINIGAME_GAME )
		{
			gMiniGameState = MINIGAME_PAUSE;
			gMiniGameMenuScreen = 3;
			gMiniGameMenuSelection = 0;
		}

		if ( gMiniGameState == MINIGAME_STARTSCREEN )
		{
			// menu background
			ColorFillVideoSurfaceArea( FRAME_BUFFER, menu_x_low, menu_y_low, menu_x_high, menu_y_high, Get16BPPColor( FROMRGB( 0, 0, 0 ) ) );

			// display our score
			UINT16 sX = menu_x_low + 10;
			UINT16 sY = menu_y_low + 10;
			UINT16 width = 0;
						
			CHAR16		sText[800];
			swprintf( sText, L"" );
			
			// different options are here, the one currently selected is coloured differently
			if ( gMiniGameMenuScreen == 0 )
			{
				int menuentry = 0;

				swprintf( sText, (gMiniGameMenuSelection == menuentry) ? L"-> New Game" : L"   New Game" );
				width = StringPixLength( sText, fontused );
				sY += DisplayWrappedString( sX, sY, width, 2, fontused, (gMiniGameMenuSelection == menuentry) ? FONT_BLACK : FONT_WHITE, 
											sText, (gMiniGameMenuSelection == menuentry) ? FONT_MCOLOR_WHITE : FONT_MCOLOR_BLACK, FALSE, 0 );
				++menuentry;

				swprintf( sText, (gMiniGameMenuSelection == menuentry) ? L"-> Options" : L"   Options" );
				width = StringPixLength( sText, fontused );
				sY += DisplayWrappedString( sX, sY, width, 2, fontused, (gMiniGameMenuSelection == menuentry) ? FONT_BLACK : FONT_WHITE,
											sText, (gMiniGameMenuSelection == menuentry) ? FONT_MCOLOR_WHITE : FONT_MCOLOR_BLACK, FALSE, 0 );
				++menuentry;

				swprintf( sText, (gMiniGameMenuSelection == menuentry) ? L"-> Quit" : L"   Quit" );
				width = StringPixLength( sText, fontused );
				sY += DisplayWrappedString( sX, sY, width, 2, fontused, (gMiniGameMenuSelection == menuentry) ? FONT_BLACK : FONT_WHITE,
											sText, (gMiniGameMenuSelection == menuentry) ? FONT_MCOLOR_WHITE : FONT_MCOLOR_BLACK, FALSE, 0 );
				++menuentry;

				// arrow keys navigate the menu
				if ( keyactivation_up )
				{
					gMiniGameMenuSelection = max( 0, gMiniGameMenuSelection - 1 );
				}
				else if ( keyactivation_down )
				{
					gMiniGameMenuSelection = min( menuentry - 1, gMiniGameMenuSelection + 1 );
				}
								
				// ENTER selects menu entry
				if ( keyactivation_enter )
				{
					if ( gMiniGameMenuSelection == 0 )
					{
						gMiniGameState = MINIGAME_GAME;

						gMiniGameRenewBackground = TRUE;
					}
					else if ( gMiniGameMenuSelection == 1 )
					{
						gMiniGameMenuScreen = 1;

						gMiniGameRenewBackground = TRUE;
					}
					else if ( gMiniGameMenuSelection == 2 )
					{
						return MiniGameExit( );
					}
				}
			}
			else if ( gMiniGameMenuScreen == 1 )
			{
				// menu background
				ColorFillVideoSurfaceArea( FRAME_BUFFER, menu_x_low, menu_y_low, menu_x_high + 100, menu_y_high, Get16BPPColor( FROMRGB( 0, 0, 0 ) ) );

				int menuentry = 0;

				swprintf( sText, (gMiniGameMenuSelection == menuentry) ? L"-> Starting Level: %d" : L"   Starting Level: %d", gTetrisInitialLevel );
				width = StringPixLength( sText, fontused );
				sY += DisplayWrappedString( sX, sY, width, 2, fontused, (gMiniGameMenuSelection == menuentry) ? FONT_BLACK : FONT_WHITE,
											sText, (gMiniGameMenuSelection == menuentry) ? FONT_MCOLOR_WHITE : FONT_MCOLOR_BLACK, FALSE, 0 );
				++menuentry;

				swprintf( sText, (gMiniGameMenuSelection == menuentry) ? L"-> Handicap: %d" : L"   Handicap: %d", gTetrisHandicap );
				width = StringPixLength( sText, fontused );
				sY += DisplayWrappedString( sX, sY, width, 2, fontused, (gMiniGameMenuSelection == menuentry) ? FONT_BLACK : FONT_WHITE,
											sText, (gMiniGameMenuSelection == menuentry) ? FONT_MCOLOR_WHITE : FONT_MCOLOR_BLACK, FALSE, 0 );
				++menuentry;

				swprintf( sText, (gMiniGameMenuSelection == menuentry) ? L"-> Apply" : L"   Apply" );
				width = StringPixLength( sText, fontused );
				sY += DisplayWrappedString( sX, sY, width, 2, fontused, (gMiniGameMenuSelection == menuentry) ? FONT_BLACK : FONT_WHITE,
											sText, (gMiniGameMenuSelection == menuentry) ? FONT_MCOLOR_WHITE : FONT_MCOLOR_BLACK, FALSE, 0 );
				++menuentry;
				
				// arrow keys navigate the menu
				if ( keyactivation_up )
				{
					gMiniGameMenuSelection = max( 0, gMiniGameMenuSelection - 1 );
				}
				else if ( keyactivation_down )
				{
					gMiniGameMenuSelection = min( menuentry - 1, gMiniGameMenuSelection + 1 );
				}
				// arrow keys alter settings
				else if ( keyactivation_left )
				{
					if ( gMiniGameMenuSelection == 0 )
					{
						gTetrisInitialLevel = max( 0, gTetrisInitialLevel - 1 );
					}
					else if ( gMiniGameMenuSelection == 1 )
					{
						gTetrisHandicap = max( 0, gTetrisHandicap - 1 );
					}
				}
				else if ( keyactivation_right )
				{
					if ( gMiniGameMenuSelection == 0 )
					{
						gTetrisInitialLevel = min( 20, gTetrisInitialLevel + 1 );
					}
					else if ( gMiniGameMenuSelection == 1 )
					{
						gTetrisHandicap = min( TETRIS_MAX_HANDICAP, gTetrisHandicap + 1 );
					}
				}

				// ENTER selects menu entry
				if ( keyactivation_enter )
				{
					if ( gMiniGameMenuSelection == 2 )
					{
						gMiniGameMenuScreen = 0;
						gMiniGameMenuSelection = 1;

						gMiniGameRenewBackground = TRUE;
					}
				}
			}
		}
		else if ( gMiniGameState == MINIGAME_GAME )
		{
			UINT32 TETRIS_TICK_FREQUENCY = max( 150, 800 - (gTetrisInitialLevel + gTetrisLinesDone / 10) * 35 );
			
			BOOLEAN fDoLineUpdate = FALSE;

			if ( GetJA2Clock( ) - uiTimeOfLastUpdate > TETRIS_TICK_FREQUENCY )
			{
				uiTimeOfLastUpdate = GetJA2Clock( );
				fDoLineUpdate = TRUE;
			}

			// on start of the game, if we have a handicap, place handicap tiles
			if ( !gTetrisTileSpawnTime && gTetrisHandicap )
			{
				for ( int i = max(0, TETRIS_FIELD_ROWS - 2 * gTetrisHandicap); i < TETRIS_FIELD_ROWS; ++i )
				{
					// add several lone blocks per line
					int tilestoadd = min( 2 + Random( 4 ), TETRIS_FIELD_COLS - 1);

					while ( tilestoadd )
					{
						int j = Random( TETRIS_FIELD_COLS );

						if ( gTetrisBlock[i][j].blocktype == TETRISBLOCKTYPE_NONE )
						{
							gTetrisBlock[i][j].resting = TRUE;
							gTetrisBlock[i][j].playercontrolled = FALSE;
							gTetrisBlock[i][j].blocktype = 1 + Random( TETRISBLOCKTYPE_Z_INVERTED );

							--tilestoadd;
						}
					}
				}

				gTetrisTileSpawnTime = 1;
			}

			// coloured block that we can move
			int block_x_delta = 0;
			int block_y_delta = 0;

			BOOLEAN turnmove = FALSE;
			
			// we check keypresses to decide whether the player wants to move a piece
			// however, after doing a move, wait for a 'cooldown' - we don't want to move to fast
			if ( !fDoLineUpdate )
			{
				if ( keyactivation_left )
					block_x_delta += -1;

				if ( keyactivation_right )
					block_x_delta += 1;

				if ( keyactivation_up )
					turnmove = TRUE;

				if ( keyactivation_down )
					block_y_delta += 1;
			}

			// only continue if there  is something to do
			if ( fDoLineUpdate || block_x_delta || block_y_delta || turnmove )
			{
				// display our score
				UINT16 sX = TETRIS_PREVIEW_X + 10;
				UINT16 sY = MINIGAME_TETRIS_Y_LOW + 10;

				// score background
				ColorFillVideoSurfaceArea( FRAME_BUFFER, TETRIS_PREVIEW_X, MINIGAME_TETRIS_Y_LOW, TETRIS_PREVIEW_X + 130, MINIGAME_TETRIS_Y_LOW + 80, Get16BPPColor( FROMRGB( 0, 0, 0 ) ) );
					
				UINT16 width = 0;

				CHAR16		sText[800];
				swprintf( sText, L"" );

				swprintf( sText, L"Score: %d", gTetrisScore );
				width = StringPixLength( sText, fontused );
				sY += DisplayWrappedString( sX, sY, width, 2, fontused, FONT_WHITE, sText, FONT_MCOLOR_BLACK, FALSE, 0 );

				swprintf( sText, L"Lines: %d", gTetrisLinesDone );
				width = StringPixLength( sText, fontused );
				sY += DisplayWrappedString( sX, sY, width, 2, fontused, FONT_WHITE, sText, FONT_MCOLOR_BLACK, FALSE, 0 );

				swprintf( sText, L"Level: %d", gTetrisInitialLevel + gTetrisLinesDone / 10 );
				width = StringPixLength( sText, fontused );
				sY += DisplayWrappedString( sX, sY, width, 2, fontused, FONT_WHITE, sText, FONT_MCOLOR_BLACK, FALSE, 0 );

				swprintf( sText, L"Handicap: %d", gTetrisHandicap );
				width = min( SCREEN_WIDTH - 2 * sX, StringPixLength( sText, fontused ) );
				sY += DisplayWrappedString( sX, sY, width, 2, fontused, FONT_WHITE, sText, FONT_MCOLOR_BLACK, FALSE, 0 );

				if ( turnmove )
				{
					int anchor_x = gTetrisBlockPlayer[0].x;
					int anchor_y = gTetrisBlockPlayer[0].y;

					INT8 playerblocktype = gTetrisBlock[anchor_y][anchor_x].blocktype;

					// check whether new orientation can be applied
					BOOLEAN turnpossible = FALSE;

					int turntestcnt = 0;

					while ( !turnpossible && turntestcnt < 3)
					{
						++turntestcnt;

						turnpossible = TRUE;

						++gTetrisplayertileOrientation;
						if ( gTetrisplayertileOrientation >= TETRIS_ORIENTATIONS )
							gTetrisplayertileOrientation = 0;

						TETRIS_BLOCK_ORIENTATION orientation = gTetrisOrientation[playerblocktype][gTetrisplayertileOrientation];

						for ( int k = 0; k < 4; ++k )
						{
							int new_x = anchor_x + orientation.cords[k].x;
							int new_y = anchor_y + orientation.cords[k].y;

							if ( new_x < 0 ||
								 new_x >= TETRIS_FIELD_COLS ||
								 new_y < 0 ||
								 new_y >= TETRIS_FIELD_ROWS ||
								 (gTetrisBlock[new_y][new_x].blocktype != TETRISBLOCKTYPE_NONE && !gTetrisBlock[new_y][new_x].playercontrolled)
								 )
							{
								turnpossible = FALSE;
							}
						}

						// if turn is possible, well, move the tile
						if ( turnpossible )
						{
							for ( int k = 0; k < 4; ++k )
							{
								gTetrisBlock[gTetrisBlockPlayer[k].y][gTetrisBlockPlayer[k].x].resting = TRUE;
								gTetrisBlock[gTetrisBlockPlayer[k].y][gTetrisBlockPlayer[k].x].playercontrolled = FALSE;
								gTetrisBlock[gTetrisBlockPlayer[k].y][gTetrisBlockPlayer[k].x].blocktype = TETRISBLOCKTYPE_NONE;
							}

							for ( int k = 0; k < 4; ++k )
							{
								gTetrisBlockPlayer[k].x = anchor_x + orientation.cords[k].x;
								gTetrisBlockPlayer[k].y = anchor_y + orientation.cords[k].y;

								gTetrisBlock[gTetrisBlockPlayer[k].y][gTetrisBlockPlayer[k].x].resting = FALSE;
								gTetrisBlock[gTetrisBlockPlayer[k].y][gTetrisBlockPlayer[k].x].playercontrolled = TRUE;
								gTetrisBlock[gTetrisBlockPlayer[k].y][gTetrisBlockPlayer[k].x].blocktype = playerblocktype;
							}

							PlayJA2Sample( COMPUTER_SWITCH1_OUT, RATE_11025, 15, 1, MIDDLEPAN );
						}
					}
				}

				if ( fDoLineUpdate || block_x_delta || block_y_delta )
				{
					BOOLEAN playercontrollost = FALSE;

					if ( fDoLineUpdate )
					{
						block_y_delta += 1;
					}

					if ( gTetrisplayercontrolledtile && (block_x_delta || block_y_delta) )
					{
						// move the player-controlled blocks
						INT8 playerblocktype = TETRISBLOCKTYPE_NONE;

						for ( int k = 0; k < 4; ++k )
						{
							int x = gTetrisBlockPlayer[k].x;
							int y = gTetrisBlockPlayer[k].y;

							playerblocktype = gTetrisBlock[y][x].blocktype;

							// check whether move is possible
							if ( block_x_delta &&
								 (x + block_x_delta < 0 ||
								 x + block_x_delta >= TETRIS_FIELD_COLS ||
								 (gTetrisBlock[y][x + block_x_delta].blocktype != TETRISBLOCKTYPE_NONE && !gTetrisBlock[y][x + block_x_delta].playercontrolled))
								 )
							{
								block_x_delta = 0;
							}

							if ( block_y_delta &&
								 (y + block_y_delta < 0 ||
								 y + block_y_delta >= TETRIS_FIELD_ROWS ||
								 (gTetrisBlock[y + block_y_delta][x].blocktype != TETRISBLOCKTYPE_NONE && !gTetrisBlock[y + block_y_delta][x].playercontrolled))
								 )
							{
								block_y_delta = 0;
							}

							gTetrisBlock[y][x].resting = TRUE;
							gTetrisBlock[y][x].playercontrolled = FALSE;
							gTetrisBlock[y][x].blocktype = TETRISBLOCKTYPE_NONE;
						}

						for ( int k = 0; k < 4; ++k )
						{
							gTetrisBlockPlayer[k].x += block_x_delta;
							gTetrisBlockPlayer[k].y += block_y_delta;
						}

						// set the new ones
						for ( int k = 0; k < 4; ++k )
						{
							gTetrisBlock[gTetrisBlockPlayer[k].y][gTetrisBlockPlayer[k].x].resting = FALSE;
							gTetrisBlock[gTetrisBlockPlayer[k].y][gTetrisBlockPlayer[k].x].playercontrolled = TRUE;
							gTetrisBlock[gTetrisBlockPlayer[k].y][gTetrisBlockPlayer[k].x].blocktype = playerblocktype;
						}

						if ( fDoLineUpdate && !block_y_delta )
						{
							playercontrollost = TRUE;

							// give points to the player, depending on how fast the block was placed

							// level of highest placed tile
							int highestlevel = TETRIS_FIELD_ROWS;
							for ( int i = 0; i < TETRIS_FIELD_ROWS; ++i )
							{
								for ( int j = 0; j < TETRIS_FIELD_COLS; ++j )
								{
									if ( gTetrisBlock[i][j].blocktype != TETRISBLOCKTYPE_NONE )
									{
										highestlevel = i;

										break;
									}
								}

								if ( highestlevel < TETRIS_FIELD_ROWS )
									break;
							}

							// time the tile would have taken if it dropped on its own, assuming it would land on the highest tile
							// tile spawned a bit lower, take that into account
							int maxdroptime = TETRIS_TICK_FREQUENCY * ((highestlevel - 4));

							if ( maxdroptime > 0 )
							{
								int timetaken = GetJA2Clock( ) - gTetrisTileSpawnTime;

								int speedbonuspoints = 10 * max( 0, (maxdroptime - timetaken) ) / maxdroptime;

								AddScore_Tetris( speedbonuspoints );
							}
						}
					}

					if ( fDoLineUpdate )
					{
						for ( int i = TETRIS_FIELD_ROWS - 1; i >= 0; --i )
						{
							BOOLEAN lowerline = TRUE;

							// first, check whether we can lower the line in the first place
							BOOLEAN emptyline = TRUE;

							for ( int j = 0; j < TETRIS_FIELD_COLS; ++j )
							{
								if ( i == TETRIS_FIELD_ROWS - 1 )
								{
									lowerline = FALSE;
									break;
								}
								else if ( gTetrisBlock[i][j].resting )
								{
									lowerline = FALSE;
									break;
								}
								else if ( !gTetrisBlock[i][j].playercontrolled && gTetrisBlock[i + 1][j].blocktype != TETRISBLOCKTYPE_NONE )
								{
									lowerline = FALSE;
									break;
								}

								if ( gTetrisBlock[i][j].blocktype != TETRISBLOCKTYPE_NONE )
								{
									emptyline = FALSE;
								}
							}

							// don't bother with empty lines
							if ( emptyline )
								lowerline = FALSE;

							if ( lowerline )
							{
								// there is space below us - move down
								for ( int j = 0; j < TETRIS_FIELD_COLS; ++j )
								{
									if ( !gTetrisBlock[i][j].playercontrolled )
									{
										gTetrisBlock[i + 1][j].resting = gTetrisBlock[i][j].resting;
										gTetrisBlock[i + 1][j].playercontrolled = gTetrisBlock[i][j].playercontrolled;
										gTetrisBlock[i + 1][j].blocktype = gTetrisBlock[i][j].blocktype;

										gTetrisBlock[i][j].resting = TRUE;
										gTetrisBlock[i][j].playercontrolled = FALSE;
										gTetrisBlock[i][j].blocktype = TETRISBLOCKTYPE_NONE;
									}
								}
							}
							else
							{
								// otherwise stop moving
								for ( int j = 0; j < TETRIS_FIELD_COLS; ++j )
								{
									if ( !gTetrisBlock[i][j].playercontrolled )
									{
										gTetrisBlock[i][j].resting = TRUE;
									}
								}
							}
						}
					}

					// if the player-controlled tile has been put down,
					// - release player control for all player-controlled blocks if one loses controls
					// - erase completed lines
					// - award points
					BOOLEAN erasecompleteline = FALSE;
					if ( playercontrollost )
					{
						for ( int i = 0; i < 4; ++i )
						{
							gTetrisBlock[gTetrisBlockPlayer[i].y][gTetrisBlockPlayer[i].x].playercontrolled = FALSE;
							gTetrisBlock[gTetrisBlockPlayer[i].y][gTetrisBlockPlayer[i].x].resting = TRUE;

							gTetrisBlockPlayer[i].x = 0;
							gTetrisBlockPlayer[i].y = 0;
						}

						gTetrisplayercontrolledtile = FALSE;

						PlayJA2Sample( COMPUTER_BEEP2_IN, RATE_11025, 15, 1, MIDDLEPAN );

						// 
						int linesdone = 0;
						for ( int i = TETRIS_FIELD_ROWS - 1; i >= 0; --i )
						{
							BOOLEAN eraseline = TRUE;

							for ( int j = 0; j < TETRIS_FIELD_COLS; ++j )
							{
								if ( gTetrisBlock[i][j].blocktype == TETRISBLOCKTYPE_NONE )
								{
									eraseline = FALSE;
								}

								// if a line has been erased below, we will fall down...
								if ( erasecompleteline )
								{
									gTetrisBlock[i][j].resting = FALSE;
								}
							}

							if ( eraseline )
							{
								erasecompleteline = TRUE;

								for ( int j = 0; j < TETRIS_FIELD_COLS; ++j )
								{
									gTetrisBlock[i][j].resting = TRUE;
									gTetrisBlock[i][j].playercontrolled = FALSE;
									gTetrisBlock[i][j].blocktype = TETRISBLOCKTYPE_NONE;
								}

								++linesdone;
							}
						}

						if ( linesdone )
						{
							switch ( linesdone )
							{
							case 1: AddScore_Tetris( 40 ); break;
							case 2: AddScore_Tetris( 100 ); break;
							case 3: AddScore_Tetris( 300 ); break;
							case 4:
								AddScore_Tetris( 1200 );

								if ( gusSelectedSoldier != NOBODY )
								{
									// Set to current
									SOLDIERTYPE* pSoldier = NULL;
									GetSoldier( &pSoldier, gusSelectedSoldier );

									if ( pSoldier )
									{
										pSoldier->DoMercBattleSound( BATTLE_SOUND_COOL1 );
									}
								}
								break;
							}

							gTetrisLinesDone += linesdone;
						}
					}

					// if the plyer doesn't currently control a tile, add next one
					if ( !gTetrisplayercontrolledtile )
					{
						// check whether there are still tiles dropping... if not, add new tile
						BOOLEAN stilldropping = FALSE;
						for ( int i = TETRIS_FIELD_ROWS - 1; i >= 0; --i )
						{
							for ( int j = 0; j < TETRIS_FIELD_COLS; ++j )
							{
								if ( !gTetrisBlock[i][j].resting )
								{
									stilldropping = TRUE;
									break;
								}
							}
						}

						if ( !stilldropping )
						{
							// add new playercontrolled piece
							// check if entry space is free
							BOOLEAN spaceisfree = TRUE;

							if ( gTetrisNextTile == TETRISBLOCKTYPE_NONE )
								gTetrisNextTile = 1 + Random( TETRISBLOCKTYPE_Z_INVERTED );

							int playerblocktype = gTetrisNextTile;

							gTetrisNextTile = 1 + Random( TETRISBLOCKTYPE_Z_INVERTED );

							gTetrisplayertileOrientation = 0;

							TETRIS_BLOCK_ORIENTATION orientation = gTetrisOrientation[playerblocktype][gTetrisplayertileOrientation];

							int anchor_x = 4;
							int anchor_y = 1;

							for ( int k = 0; k < 4; ++k )
							{
								gTetrisBlockPlayer[k].x = anchor_x + orientation.cords[k].x;
								gTetrisBlockPlayer[k].y = anchor_y + orientation.cords[k].y;

								gTetrisBlock[gTetrisBlockPlayer[k].y][gTetrisBlockPlayer[k].x].resting = FALSE;
								gTetrisBlock[gTetrisBlockPlayer[k].y][gTetrisBlockPlayer[k].x].playercontrolled = TRUE;

								if ( gTetrisBlock[gTetrisBlockPlayer[k].y][gTetrisBlockPlayer[k].x].blocktype != TETRISBLOCKTYPE_NONE )
								{
									gTetrisBlock[gTetrisBlockPlayer[k].y][gTetrisBlockPlayer[k].x].blocktype = TETRISBLOCKTYPE_CONFLICT;
									spaceisfree = FALSE;
								}
								else
								{
									gTetrisBlock[gTetrisBlockPlayer[k].y][gTetrisBlockPlayer[k].x].blocktype = playerblocktype;
								}
							}

							gTetrisplayercontrolledtile = TRUE;
							gTetrisTileSpawnTime = GetJA2Clock();

							if ( !spaceisfree )
							{
								gMiniGameState = MINIGAME_GAMEOVER;
								gMiniGameMenuScreen = 2;
								gMiniGameMenuSelection = 0;

								// game over, man, game over!
								if ( gusSelectedSoldier != NOBODY )
								{
									// Set to current
									SOLDIERTYPE* pSoldier = NULL;
									GetSoldier( &pSoldier, gusSelectedSoldier );

									if ( pSoldier )
									{
										pSoldier->DoMercBattleSound( BATTLE_SOUND_CURSE1 );
									}
								}
							}
						}
					}
				}

				// draw stuff
				{
					UINT16 col_red = Get16BPPColor( FROMRGB( 255, 0, 0 ) );
					UINT16 col_green = Get16BPPColor( FROMRGB( 0, 255, 0 ) );
					UINT16 col_blue = Get16BPPColor( FROMRGB( 0, 0, 255 ) );
					UINT16 col_yellow = Get16BPPColor( FROMRGB( 255, 255, 0 ) );
					UINT16 col_violet = Get16BPPColor( FROMRGB( 127, 0, 127 ) );
					UINT16 col_cyan = Get16BPPColor( FROMRGB( 0, 127, 127 ) );
					UINT16 col_orange = Get16BPPColor( FROMRGB( 255, 127, 0 ) );
					UINT16 col_pink = Get16BPPColor( FROMRGB( 255, 0, 255 ) );
					UINT16 col_grey = Get16BPPColor( FROMRGB( 55, 55, 55 ) );

					// draw blocks
					{
						// background
						ColorFillVideoSurfaceArea( FRAME_BUFFER, MINIGAME_TETRIS_X_LOW, MINIGAME_TETRIS_Y_LOW, MINIGAME_TETRIS_X_HIGH, MINIGAME_TETRIS_Y_HIGH, Get16BPPColor( FROMRGB( 0, 0, 0 ) ) );

						for ( int i = 0; i < TETRIS_FIELD_ROWS; ++i )
						{
							for ( int j = 0; j < TETRIS_FIELD_COLS; ++j )
							{
								if ( gTetrisBlock[i][j].blocktype != TETRISBLOCKTYPE_NONE )
								{
									UINT16 x = MINIGAME_TETRIS_X_LOW + j * TETRIS_BLOCKLENGTH;
									UINT16 y = MINIGAME_TETRIS_Y_LOW + i * TETRIS_BLOCKLENGTH;

									UINT16 colour1 = 0;
									UINT16 colour2 = col_grey;

									switch ( gTetrisBlock[i][j].blocktype )
									{
									case TETRISBLOCKTYPE_L:				colour1 = col_red;			break;
									case TETRISBLOCKTYPE_L_INVERTED:	colour1 = col_green;		break;
									case TETRISBLOCKTYPE_T:				colour1 = col_blue;			break;
									case TETRISBLOCKTYPE_SQUARE:		colour1 = col_yellow;		break;
									case TETRISBLOCKTYPE_LONG:			colour1 = col_violet;		break;
									case TETRISBLOCKTYPE_Z:				colour1 = col_cyan;			break;
									case TETRISBLOCKTYPE_Z_INVERTED:	colour1 = col_orange;		break;
									case TETRISBLOCKTYPE_CONFLICT:		colour1 = col_pink;			break;
									case TETRISBLOCKTYPE_NONE:
									default:
										colour1 = Get16BPPColor( FROMRGB( 0, 0, 0 ) );
										colour2 = colour1;
										break;
									}

									ColorFillVideoSurfaceArea( FRAME_BUFFER, x, y, x + TETRIS_BLOCKLENGTH, y + TETRIS_BLOCKLENGTH, colour2 );
									ColorFillVideoSurfaceArea( FRAME_BUFFER, x + 1, y + 1, x + TETRIS_BLOCKLENGTH - 1, y + TETRIS_BLOCKLENGTH - 1, colour1 );
								}
							}
						}
					}

					// draw preview area
					{
						// we also need a small 4x4 area to display the next tile
						ColorFillVideoSurfaceArea( FRAME_BUFFER, TETRIS_PREVIEW_X, TETRIS_PREVIEW_Y, TETRIS_PREVIEW_X + 4 * TETRIS_BLOCKLENGTH, MINIGAME_TETRIS_Y_HIGH, Get16BPPColor( FROMRGB( 0, 0, 0 ) ) );

						int preview_anchor_x = 2;
						int preview_anchor_y = 1;

						TETRIS_BLOCK_ORIENTATION previeworientation = gTetrisOrientation[gTetrisNextTile][0];

						UINT16 colour1 = 0;
						UINT16 colour2 = col_grey;

						switch ( gTetrisNextTile )
						{
						case TETRISBLOCKTYPE_L:				colour1 = col_red;			break;
						case TETRISBLOCKTYPE_L_INVERTED:	colour1 = col_green;		break;
						case TETRISBLOCKTYPE_T:				colour1 = col_blue;			break;
						case TETRISBLOCKTYPE_SQUARE:		colour1 = col_yellow;		break;
						case TETRISBLOCKTYPE_LONG:			colour1 = col_violet;		break;
						case TETRISBLOCKTYPE_Z:				colour1 = col_cyan;			break;
						case TETRISBLOCKTYPE_Z_INVERTED:	colour1 = col_orange;		break;
						case TETRISBLOCKTYPE_CONFLICT:		colour1 = col_pink;			break;
						case TETRISBLOCKTYPE_NONE:
						default:
							colour1 = Get16BPPColor( FROMRGB( 0, 0, 0 ) );
							colour2 = colour1;
							break;
						}

						for ( int k = 0; k < 4; ++k )
						{
							UINT16 x = TETRIS_PREVIEW_X + (preview_anchor_x + previeworientation.cords[k].x) * TETRIS_BLOCKLENGTH;
							UINT16 y = TETRIS_PREVIEW_Y + (preview_anchor_y + previeworientation.cords[k].y) * TETRIS_BLOCKLENGTH;

							ColorFillVideoSurfaceArea( FRAME_BUFFER, x, y, x + TETRIS_BLOCKLENGTH, y + TETRIS_BLOCKLENGTH, colour2 );
							ColorFillVideoSurfaceArea( FRAME_BUFFER, x + 1, y + 1, x + TETRIS_BLOCKLENGTH - 1, y + TETRIS_BLOCKLENGTH - 1, colour1 );
						}
					}
				}
			}
		}
		else if ( gMiniGameState == MINIGAME_GAMEOVER )
		{
			ColorFillVideoSurfaceArea( FRAME_BUFFER, menu_x_low, menu_y_low, menu_x_high, menu_y_high, Get16BPPColor( FROMRGB( 0, 0, 0 ) ) );

			// display our score
			UINT16 sX = menu_x_low + 10;
			UINT16 sY = menu_y_low + 10;
			UINT16 width = 0;

			CHAR16		sText[800];
			swprintf( sText, L"" );

			swprintf( sText, L"GAME OVER" );
			width = StringPixLength( sText, fontused );
			sY += DisplayWrappedString( sX, sY, width, 2, fontused, FONT_RED, sText, FONT_MCOLOR_BLACK, FALSE, 0 );

			if ( gMiniGameMenuScreen == 2 )
			{
				int menuentry = 0;

				swprintf( sText, (gMiniGameMenuSelection == menuentry) ? L"-> Main Menu" : L"   Main Menu" );
				width = StringPixLength( sText, fontused );
				sY += DisplayWrappedString( sX, sY, width, 2, fontused, (gMiniGameMenuSelection == menuentry) ? FONT_BLACK : FONT_RED,
											sText, (gMiniGameMenuSelection == menuentry) ? FONT_MCOLOR_WHITE : FONT_MCOLOR_BLACK, FALSE, 0 );
				++menuentry;
				
				swprintf( sText, (gMiniGameMenuSelection == menuentry) ? L"-> Quit" : L"   Quit" );
				width = StringPixLength( sText, fontused );
				sY += DisplayWrappedString( sX, sY, width, 2, fontused, (gMiniGameMenuSelection == menuentry) ? FONT_BLACK : FONT_RED,
											sText, (gMiniGameMenuSelection == menuentry) ? FONT_MCOLOR_WHITE : FONT_MCOLOR_BLACK, FALSE, 0 );
				++menuentry;
				
				// arrow keys navigate the menu
				if ( keyactivation_up )
				{
					gMiniGameMenuSelection = max( 0, gMiniGameMenuSelection - 1 );
				}
				else if ( keyactivation_down )
				{
					gMiniGameMenuSelection = min( menuentry - 1, gMiniGameMenuSelection + 1 );
				}

				// ENTER selects menu entry
				if ( keyactivation_enter )
				{
					if ( gMiniGameMenuSelection == 0 )
					{
						MiniGame_Init_Tetris();

						gMiniGameState = MINIGAME_STARTSCREEN;
						gMiniGameRenewBackground = TRUE;
					}
					else if ( gMiniGameMenuSelection == 1 )
					{
						return MiniGameExit( );
					}
				}
			}
		}
		else if ( gMiniGameState == MINIGAME_PAUSE )
		{
			ColorFillVideoSurfaceArea( FRAME_BUFFER, menu_x_low, menu_y_low, menu_x_high, menu_y_high - 20, Get16BPPColor( FROMRGB( 0, 0, 0 ) ) );

			// display our score
			UINT16 sX = menu_x_low + 10;
			UINT16 sY = menu_y_low + 10;
			UINT16 width = 0;

			CHAR16		sText[800];
			swprintf( sText, L"" );

			swprintf( sText, L"Game paused" );
			width = StringPixLength( sText, fontused );
			sY += DisplayWrappedString( sX, sY, width, 2, fontused, FONT_RED, sText, FONT_MCOLOR_BLACK, FALSE, 0 );

			if ( gMiniGameMenuScreen == 3 )
			{
				int menuentry = 0;

				swprintf( sText, (gMiniGameMenuSelection == menuentry) ? L"-> Continue" : L"   Continue" );
				width = StringPixLength( sText, fontused );
				sY += DisplayWrappedString( sX, sY, width, 2, fontused, (gMiniGameMenuSelection == menuentry) ? FONT_BLACK : FONT_RED,
											sText, (gMiniGameMenuSelection == menuentry) ? FONT_MCOLOR_WHITE : FONT_MCOLOR_BLACK, FALSE, 0 );
				++menuentry;

				if ( keyactivation_enter )
				{
					gMiniGameState = MINIGAME_GAME;

					gMiniGameRenewBackground = TRUE;
				}
			}
		}
	}
	else if ( gMiniGame == PONG )
	{

	}

	return MINIGAME_SCREEN;
}
////////////////////////////////////////////// TETRIS //////////////////////////////////////////////

////////////////////////////////////////////// PONG //////////////////////////////////////////////
// Pong is one of the very first computer games ever created.
// Basically, you have 2 players who each control a paddle on opposing sides of the playing field.
// A ball travels between the two sides, the goal is to prevent the other player from guiding the ball behind you.
// We implement that here with  few extra options (multiple players and such)

// the different types of objects we use 
enum
{
	PLAYER_PAD = 0,
	BALL,
};

// this struct is used to stre data of paddles and balls
typedef struct PONG_OBJECT
{
	PONG_OBJECT( ) : x( 0 ), y( 0 ), angle( 0 ), speed( 0 ), r( 1 ), type( BALL ), timepassedsincespeedup( GetJA2Clock( ) ), lastplayerthattouchedthis( 0 ), erasethis( FALSE ) {}

	double x;
	double y;
	
	double angle;
	double speed;

	double r;
	int type;

	int timepassedsincespeedup;			// the longer a ball is in play, the faster it gets (to prevent the game from going on forever).
	int lastplayerthattouchedthis;		// we log the last player paddle that touched a ball here

	BOOLEAN erasethis;					// marker: delete this thing during deletion procedure (ball going out of game area)
} PONG_OBJECT;

int gPongPlayers = 1;				// number of players (1 to 4), with 2 or 4 paddles used accordingly

// game area
#define MINIGAME_PONG_X_LOW		(MINIGAME_BG_X_LOW + 20)
#define MINIGAME_PONG_X_HIGH	((!gPongForce4Paddles && gPongPlayers < 3) ? MINIGAME_BG_X_HIGH - 100 : MINIGAME_PONG_X_LOW + 400 )
#define MINIGAME_PONG_Y_LOW		(MINIGAME_BG_Y_LOW + 60)
#define MINIGAME_PONG_Y_HIGH	((!gPongForce4Paddles && gPongPlayers < 3) ? MINIGAME_BG_Y_HIGH - 100 : MINIGAME_PONG_Y_LOW + 400)

#define PONG_MAX_SPEED	30		// max speed
#define PONG_SPEED_INCREASE_TIMER	10000

#define PONG_MAX_BALLS	100		// maximum number of balls

std::vector< PONG_OBJECT > gPongObjects;
int gPongBallsToUse = 1;
BOOLEAN gPongForce4Paddles = FALSE;
BOOLEAN gPongAIHard = FALSE;
int gPongMaxSpeed = PONG_MAX_SPEED / 2;		// max speed internally (the player can set this up to PONG_MAX_SPEED)
int gPongScorePlayer[5];

// as movement depends on time spent, we have to create an offset whenever we enter pause mode
int gPongTimePauseActivated = 0;
int gPongPauseTimeOffset = 0;

BOOLEAN gPongObjectsInit = FALSE;

void MiniGame_Init_Pong()
{
	gPongObjects.clear();

	for (int i = 0; i < 5; ++i )
		gPongScorePlayer[i] = 0;

	gPongTimePauseActivated = 0;
	gPongPauseTimeOffset = 0;

	gMiniGameMenuSelection = 0;
	gMiniGameMenuScreen = 0;

	gMiniGameRenewBackground = TRUE;

	gPongObjectsInit = FALSE;
}

UINT32 MiniGame_Handle_Pong()
{
	static UINT32 uiTimeOfLastUpdate = GetJA2Clock( );

	if ( gMiniGame == PONG )
	{
		// coordinates for the background rectangle
		if ( gMiniGameRenewBackground )
		{
			SGPRect backgroundrect;
			backgroundrect.iLeft = MINIGAME_BG_X_LOW;
			backgroundrect.iTop = MINIGAME_BG_Y_LOW;
			backgroundrect.iRight = MINIGAME_BG_X_HIGH;
			backgroundrect.iBottom = MINIGAME_BG_Y_HIGH;

			// background: display a png picture
			std::string strImage = "Interface\\background_pong_game.png";
			if ( gMiniGameState == MINIGAME_STARTSCREEN )
				strImage = "Interface\\background_tetris_startscreen.png";
			else if ( gMiniGameState == MINIGAME_GAMEOVER )
				strImage = "Interface\\background_tetris_gameover.png";

			DisplayPNGImage( backgroundrect, strImage, false );

			gMiniGameRenewBackground = FALSE;
		}

		UINT16 menu_x_low = MINIGAME_BG_X_LOW + 250;
		UINT16 menu_x_high = MINIGAME_BG_X_HIGH - 200;
		UINT16 menu_y_low = MINIGAME_BG_Y_LOW + 200;
		UINT16 menu_y_high = MINIGAME_BG_Y_HIGH - 150;

		INT32 fontused = LARGEFONT1;

		SetFontShadow( MILITARY_SHADOW );

		// we have to keep key presses on a delay, otherwise we move to fast
		BOOLEAN keyactivation_up = FALSE;
		BOOLEAN keyactivation_down = FALSE;
		BOOLEAN keyactivation_left = FALSE;
		BOOLEAN keyactivation_right = FALSE;
		BOOLEAN keyactivation_enter = FALSE;
		BOOLEAN keyactivation_p = FALSE;

		UINT32 TETRIS_MENU_INPUTDELAY = 120;
		static int gTetristimesincelastmove = 0;
		if ( GetJA2Clock( ) - gTetristimesincelastmove > TETRIS_MENU_INPUTDELAY )
		{
			// arrow keys navigate the menu
			if ( gfKeyState[UPARROW] )
			{
				gTetristimesincelastmove = GetJA2Clock( );

				keyactivation_up = TRUE;
			}

			if ( gfKeyState[DNARROW] )
			{
				gTetristimesincelastmove = GetJA2Clock( );

				keyactivation_down = TRUE;
			}

			if ( gfKeyState[LEFTARROW] )
			{
				gTetristimesincelastmove = GetJA2Clock( );

				keyactivation_left = TRUE;
			}

			if ( gfKeyState[RIGHTARROW] )
			{
				gTetristimesincelastmove = GetJA2Clock( );

				keyactivation_right = TRUE;
			}

			if ( gfKeyState[ENTER] )
			{
				gTetristimesincelastmove = GetJA2Clock( );

				keyactivation_enter = TRUE;
			}

			// 'p' key is 80
			if ( gfKeyState[80] )
			{
				gTetristimesincelastmove = GetJA2Clock( );

				keyactivation_p = TRUE;
			}
		}

		InvalidateScreen( );

		SetCurrentCursorFromDatabase( VIDEO_NO_CURSOR );

		if ( keyactivation_p && gMiniGameState == MINIGAME_GAME )
		{
			gMiniGameState = MINIGAME_PAUSE;
			gMiniGameMenuScreen = 3;
			gMiniGameMenuSelection = 0;

			gPongTimePauseActivated = GetJA2Clock();
			gPongPauseTimeOffset = 0;
		}

		if ( gMiniGameState == MINIGAME_STARTSCREEN )
		{
			// menu background
			ColorFillVideoSurfaceArea( FRAME_BUFFER, menu_x_low, menu_y_low, menu_x_high, menu_y_high, Get16BPPColor( FROMRGB( 0, 0, 0 ) ) );

			// display our score
			UINT16 sX = menu_x_low + 10;
			UINT16 sY = menu_y_low + 10;
			UINT16 width = 0;

			CHAR16		sText[800];
			swprintf( sText, L"" );

			// different options are here, the one currently selected is coloured differently
			if ( gMiniGameMenuScreen == 0 )
			{
				int menuentry = 0;

				swprintf( sText, L"%s %d Players", (gMiniGameMenuSelection == menuentry) ? L"->" : L"  ", gPongPlayers );
				width = StringPixLength( sText, fontused );
				sY += DisplayWrappedString( sX, sY, width, 2, fontused, (gMiniGameMenuSelection == menuentry) ? FONT_BLACK : FONT_WHITE,
											sText, (gMiniGameMenuSelection == menuentry) ? FONT_MCOLOR_WHITE : FONT_MCOLOR_BLACK, FALSE, 0 );
				++menuentry;

				swprintf( sText, (gMiniGameMenuSelection == menuentry) ? L"-> Options" : L"   Options" );
				width = StringPixLength( sText, fontused );
				sY += DisplayWrappedString( sX, sY, width, 2, fontused, (gMiniGameMenuSelection == menuentry) ? FONT_BLACK : FONT_WHITE,
											sText, (gMiniGameMenuSelection == menuentry) ? FONT_MCOLOR_WHITE : FONT_MCOLOR_BLACK, FALSE, 0 );
				++menuentry;

				swprintf( sText, (gMiniGameMenuSelection == menuentry) ? L"-> Quit" : L"   Quit" );
				width = StringPixLength( sText, fontused );
				sY += DisplayWrappedString( sX, sY, width, 2, fontused, (gMiniGameMenuSelection == menuentry) ? FONT_BLACK : FONT_WHITE,
											sText, (gMiniGameMenuSelection == menuentry) ? FONT_MCOLOR_WHITE : FONT_MCOLOR_BLACK, FALSE, 0 );
				++menuentry;

				// arrow keys navigate the menu
				if ( keyactivation_up )
				{
					gMiniGameMenuSelection = max( 0, gMiniGameMenuSelection - 1 );
				}
				else if ( keyactivation_down )
				{
					gMiniGameMenuSelection = min( menuentry - 1, gMiniGameMenuSelection + 1 );
				}
				// arrow keys alter settings
				else if ( keyactivation_left )
				{
					if ( gMiniGameMenuSelection == 0 )
					{
						gPongPlayers = max( 1, gPongPlayers - 1);
					}
				}
				else if ( keyactivation_right )
				{
					if ( gMiniGameMenuSelection == 0 )
					{
						gPongPlayers = min( 4, gPongPlayers + 1 );
					}
				}

				// ENTER selects menu entry
				if ( keyactivation_enter )
				{
					if ( gMiniGameMenuSelection == 0 )
					{
						gMiniGameState = MINIGAME_GAME;

						gMiniGameRenewBackground = TRUE;
					}
					else if ( gMiniGameMenuSelection == 1 )
					{
						gMiniGameMenuScreen = 1;

						gMiniGameRenewBackground = TRUE;
					}
					else if ( gMiniGameMenuSelection == 2 )
					{
						return MiniGameExit( );
					}
				}
			}
			else if ( gMiniGameMenuScreen == 1 )
			{
				// menu background
				ColorFillVideoSurfaceArea( FRAME_BUFFER, menu_x_low, menu_y_low, menu_x_high + 100, menu_y_high, Get16BPPColor( FROMRGB( 0, 0, 0 ) ) );

				int menuentry = 0;

				swprintf( sText, (gMiniGameMenuSelection == menuentry) ? L"-> Max Speed: %d" : L"   Max Speed: %d", gPongMaxSpeed );
				width = StringPixLength( sText, fontused );
				sY += DisplayWrappedString( sX, sY, width, 2, fontused, (gMiniGameMenuSelection == menuentry) ? FONT_BLACK : FONT_WHITE,
											sText, (gMiniGameMenuSelection == menuentry) ? FONT_MCOLOR_WHITE : FONT_MCOLOR_BLACK, FALSE, 0 );
				++menuentry;

				swprintf( sText, (gMiniGameMenuSelection == menuentry) ? L"-> Balls: %d" : L"   Balls: %d", gPongBallsToUse );
				width = StringPixLength( sText, fontused );
				sY += DisplayWrappedString( sX, sY, width, 2, fontused, (gMiniGameMenuSelection == menuentry) ? FONT_BLACK : FONT_WHITE,
											sText, (gMiniGameMenuSelection == menuentry) ? FONT_MCOLOR_WHITE : FONT_MCOLOR_BLACK, FALSE, 0 );
				++menuentry;

				swprintf( sText, L"%s Force 4 Paddles: %s", (gMiniGameMenuSelection == menuentry) ? L"->" : L"  ", gPongForce4Paddles ? L"True" : L"False" );
				width = StringPixLength( sText, fontused );
				sY += DisplayWrappedString( sX, sY, width, 2, fontused, (gMiniGameMenuSelection == menuentry) ? FONT_BLACK : FONT_WHITE,
											sText, (gMiniGameMenuSelection == menuentry) ? FONT_MCOLOR_WHITE : FONT_MCOLOR_BLACK, FALSE, 0 );
				++menuentry;

				swprintf( sText, L"%s AI: %s", (gMiniGameMenuSelection == menuentry) ? L"->" : L"  ", gPongAIHard ? L"Hard" : L"Easy" );
				width = StringPixLength( sText, fontused );
				sY += DisplayWrappedString( sX, sY, width, 2, fontused, (gMiniGameMenuSelection == menuentry) ? FONT_BLACK : FONT_WHITE,
											sText, (gMiniGameMenuSelection == menuentry) ? FONT_MCOLOR_WHITE : FONT_MCOLOR_BLACK, FALSE, 0 );
				++menuentry;

				swprintf( sText, (gMiniGameMenuSelection == menuentry) ? L"-> Apply" : L"   Apply" );
				width = StringPixLength( sText, fontused );
				sY += DisplayWrappedString( sX, sY, width, 2, fontused, (gMiniGameMenuSelection == menuentry) ? FONT_BLACK : FONT_WHITE,
											sText, (gMiniGameMenuSelection == menuentry) ? FONT_MCOLOR_WHITE : FONT_MCOLOR_BLACK, FALSE, 0 );
				++menuentry;
				
				// arrow keys navigate the menu
				if ( keyactivation_up )
				{
					gMiniGameMenuSelection = max( 0, gMiniGameMenuSelection - 1 );
				}
				else if ( keyactivation_down )
				{
					gMiniGameMenuSelection = min( menuentry - 1, gMiniGameMenuSelection + 1 );
				}
				// arrow keys alter settings
				else if ( keyactivation_left )
				{
					if ( gMiniGameMenuSelection == 0 )
					{
						gPongMaxSpeed = max( 1, gPongMaxSpeed - 1 );
					}
					else if ( gMiniGameMenuSelection == 1 )
					{
						gPongBallsToUse = max( 1, gPongBallsToUse - 1 );
					}
					else if ( gMiniGameMenuSelection == 2 )
					{
						gPongForce4Paddles = FALSE;
					}
					else if ( gMiniGameMenuSelection == 3 )
					{
						gPongAIHard = FALSE;
					}
				}
				else if ( keyactivation_right )
				{
					if ( gMiniGameMenuSelection == 0 )
					{
						gPongMaxSpeed = min( PONG_MAX_SPEED, gPongMaxSpeed + 1 );
					}
					else if ( gMiniGameMenuSelection == 1 )
					{
						gPongBallsToUse = min( PONG_MAX_BALLS, gPongBallsToUse + 1 );
					}
					else if ( gMiniGameMenuSelection == 2 )
					{
						gPongForce4Paddles = TRUE;
					}
					else if ( gMiniGameMenuSelection == 3 )
					{
						gPongAIHard = TRUE;
					}
				}

				// ENTER selects menu entry
				if ( keyactivation_enter )
				{
					if ( gMiniGameMenuSelection == menuentry - 1 )
					{
						gMiniGameMenuScreen = 0;
						gMiniGameMenuSelection = 1;

						gMiniGameRenewBackground = TRUE;
					}
				}
			}
		}
		else if ( gMiniGameState == MINIGAME_GAME )
		{
			// we also have to account for any time we spent in pause mode
			int timesincelasttick = GetJA2Clock( ) - uiTimeOfLastUpdate - gPongPauseTimeOffset;

			for ( std::vector<PONG_OBJECT>::iterator it = gPongObjects.begin(); it != gPongObjects.end(); ++it )
			{
				if ( it->type == BALL )
				{
					it->timepassedsincespeedup += gPongPauseTimeOffset;
				}
			}

			gPongPauseTimeOffset = 0;
			uiTimeOfLastUpdate = GetJA2Clock( );

			if ( !gPongObjectsInit )
			{
				PONG_OBJECT player1paddle;
				player1paddle.x = MINIGAME_PONG_X_LOW + (MINIGAME_PONG_X_HIGH - MINIGAME_PONG_X_LOW) / 2;
				player1paddle.y = MINIGAME_PONG_Y_HIGH;
				player1paddle.r = 20;
				player1paddle.type = PLAYER_PAD;

				gPongObjects.push_back( player1paddle );

				PONG_OBJECT player2paddle;
				player2paddle.x = MINIGAME_PONG_X_LOW + (MINIGAME_PONG_X_HIGH - MINIGAME_PONG_X_LOW) / 2;
				player2paddle.y = MINIGAME_PONG_Y_LOW;
				player2paddle.r = 20;
				player2paddle.type = PLAYER_PAD;

				gPongObjects.push_back( player2paddle );

				if ( gPongForce4Paddles || gPongPlayers >= 3 )
				{
					PONG_OBJECT player3paddle;
					player3paddle.x = MINIGAME_PONG_X_LOW;
					player3paddle.y = MINIGAME_PONG_Y_LOW + (MINIGAME_PONG_Y_HIGH - MINIGAME_PONG_Y_LOW) / 2;
					player3paddle.r = 20;
					player3paddle.type = PLAYER_PAD;

					gPongObjects.push_back( player3paddle );

					PONG_OBJECT player4paddle;
					player4paddle.x = MINIGAME_PONG_X_HIGH;
					player4paddle.y = MINIGAME_PONG_Y_LOW + (MINIGAME_PONG_Y_HIGH - MINIGAME_PONG_Y_LOW) / 2;
					player4paddle.r = 20;
					player4paddle.type = PLAYER_PAD;

					gPongObjects.push_back( player4paddle );
				}

				if ( gPongBallsToUse > 3 && gusSelectedSoldier != NOBODY )
				{
					// Set to current
					SOLDIERTYPE* pSoldier = NULL;
					GetSoldier( &pSoldier, gusSelectedSoldier );

					if ( pSoldier )
					{
						pSoldier->DoMercBattleSound( BATTLE_SOUND_CURSE1 );
					}
				}

				gPongObjectsInit = TRUE;
			}

			// check whether we need to add a ball
			int ballsinplay = 0;
			for ( std::vector<PONG_OBJECT>::iterator it = gPongObjects.begin(); it != gPongObjects.end(); ++it )
			{
				if ( it->type == BALL )
					++ballsinplay;
			}

			// as balls can get erased, we always check whether we have to add new ones
			while ( ballsinplay < gPongBallsToUse )
			{
				PONG_OBJECT ball;
				ball.x = MINIGAME_PONG_X_LOW + (MINIGAME_PONG_X_HIGH - MINIGAME_PONG_X_LOW) / 2 - 50 + Random(100);
				ball.y = MINIGAME_PONG_Y_LOW + (MINIGAME_PONG_Y_HIGH - MINIGAME_PONG_Y_LOW) / 2 - 30 + Random(60);
				ball.angle = Random(10);
				ball.speed = min( gPongMaxSpeed, gPongMaxSpeed / 2 + Random( gPongMaxSpeed ) );
				ball.r = 4;

				gPongObjects.push_back( ball );

				++ballsinplay;
			}

			// iterators to player-paddles
			// note: as we use iterators and check against gPongObjects.end( ) to know whether a paddle exists, erasing elements should only be done at the very end
			std::vector<PONG_OBJECT>::iterator it_player1 = gPongObjects.end( );
			std::vector<PONG_OBJECT>::iterator it_player2 = gPongObjects.end( );
			std::vector<PONG_OBJECT>::iterator it_player3 = gPongObjects.end( );
			std::vector<PONG_OBJECT>::iterator it_player4 = gPongObjects.end( );

			// for the 'AI' we need to know which ball will be the next one to hit our side
			std::vector<PONG_OBJECT>::iterator it_nextballfor_player2 = gPongObjects.end( );
			std::vector<PONG_OBJECT>::iterator it_nextballfor_player3 = gPongObjects.end( );
			std::vector<PONG_OBJECT>::iterator it_nextballfor_player4 = gPongObjects.end( );

			double nextballfor_player2_ydist = 99999;
			double nextballfor_player3_ydist = 99999;
			double nextballfor_player4_ydist = 99999;
			int cnt = 0;
			for ( std::vector<PONG_OBJECT>::iterator it = gPongObjects.begin(); it != gPongObjects.end(); ++it )
			{
				if ( it->type == PLAYER_PAD )
				{
					// also, control stuff
					if ( cnt == 0 )
						it_player1 = it;
					else if ( cnt == 1 )
						it_player2 = it;
					else if ( cnt == 2 )
						it_player3 = it;
					else if ( cnt == 3 )
						it_player4 = it;
				}
				else if ( it->type == BALL )
				{
					// only balls that travel in our direction are important
					if ( cos( it->angle ) < 0 )
					{
						double dist = it->y - MINIGAME_PONG_Y_LOW;

						if ( dist < nextballfor_player2_ydist )
						{
							nextballfor_player2_ydist = dist;
							it_nextballfor_player2 = it;
						}
					}

					if ( sin( it->angle ) < 0 )
					{
						double dist = it->x - MINIGAME_PONG_X_LOW;

						if ( dist < nextballfor_player3_ydist )
						{
							nextballfor_player3_ydist = dist;
							it_nextballfor_player3 = it;
						}
					}

					if ( sin( it->angle ) > 0 )
					{
						double dist = MINIGAME_PONG_X_HIGH - it->x;

						if ( dist < nextballfor_player4_ydist )
						{
							nextballfor_player4_ydist = dist;
							it_nextballfor_player4 = it;
						}
					}
				}

				++cnt;
			}
			
			if ( it_player1 != gPongObjects.end() )
			{
				it_player1->angle = 0;
				it_player1->speed = 0;

				if ( gfKeyState[LEFTARROW] )
				{
					it_player1->angle -= PI / 2;
					it_player1->speed = gPongMaxSpeed;
				}
				if ( gfKeyState[RIGHTARROW] )
				{
					it_player1->angle += PI / 2;
					it_player1->speed = gPongMaxSpeed;
				}
			}

			if ( it_player2 != gPongObjects.end( ) )
			{
				it_player2->angle = 0;
				it_player2->speed = 0;

				if ( !gPongForce4Paddles && gPongPlayers >= 2 )
				{
					// control with 4 and 6 in numblock
					if ( gfKeyState[239] )
					{
						it_player2->angle -= PI / 2;
						it_player2->speed = gPongMaxSpeed;
					}
					if ( gfKeyState[241] )
					{
						it_player2->angle += PI / 2;
						it_player2->speed = gPongMaxSpeed;
					}
				}
				// 'AI'
				else
				{
					if ( it_nextballfor_player2 != gPongObjects.end() )
					{
						double offset = gPongAIHard ? 0 : it_player2->r;

						if ( it_nextballfor_player2->x < it_player2->x + offset )
						{
							it_player2->angle -= PI / 2;
							it_player2->speed = gPongMaxSpeed;
						}
						else if ( it_nextballfor_player2->x > it_player2->x - offset )
						{
							it_player2->angle += PI / 2;
							it_player2->speed = gPongMaxSpeed;
						}
					}
				}
			}

			if ( it_player3 != gPongObjects.end( ) )
			{
				it_player3->angle = 0;
				it_player3->speed = 0;

				if ( !gPongForce4Paddles && gPongPlayers >= 3 )
				{
					// 'w'
					if ( gfKeyState[87] )
					{
						it_player3->angle += PI;
						it_player3->speed = gPongMaxSpeed;
					}
					// 's'
					if ( gfKeyState[83] )
					{
						it_player3->angle -= 0;
						it_player3->speed = gPongMaxSpeed;
					}
				}
				// 'AI'
				else
				{
					if ( it_nextballfor_player3 != gPongObjects.end( ) )
					{
						double offset = gPongAIHard ? 0 : it_player3->r;

						if ( it_nextballfor_player3->y < it_player3->y + offset )
						{
							it_player3->angle += PI;
							it_player3->speed = gPongMaxSpeed;
						}
						else if ( it_nextballfor_player3->y > it_player3->y - offset )
						{
							it_player3->angle -= 0;
							it_player3->speed = gPongMaxSpeed;
						}
					}
				}
			}

			if ( it_player4 != gPongObjects.end( ) )
			{
				it_player4->angle = 0;
				it_player4->speed = 0;

				if ( !gPongForce4Paddles && gPongPlayers >= 4 )
				{
					// 'i'
					if ( gfKeyState[73] )
					{
						it_player4->angle += PI;
						it_player4->speed = gPongMaxSpeed;
					}
					// 'k'
					if ( gfKeyState[75] )
					{
						it_player4->angle -= 0;
						it_player4->speed = gPongMaxSpeed;
					}
				}
				// 'AI'
				else
				{
					if ( it_nextballfor_player4 != gPongObjects.end( ) )
					{
						double offset = gPongAIHard ? 0 : it_player4->r;

						if ( it_nextballfor_player4->y < it_player4->y + offset )
						{
							it_player4->angle += PI;
							it_player4->speed = gPongMaxSpeed;
						}
						else if ( it_nextballfor_player4->y > it_player4->y - offset )
						{
							it_player4->angle -= 0;
							it_player4->speed = gPongMaxSpeed;
						}
					}
				}
			}
						
			// move stuff
			// only if time has passed
			if ( timesincelasttick > 0 )
			{
				// display our score
				UINT16 sX = (!gPongForce4Paddles && gPongPlayers < 3) ? MINIGAME_BG_X_LOW + 200 : MINIGAME_BG_X_LOW + 100;
				UINT16 sY = MINIGAME_BG_Y_LOW + 10;

				// score background
				ColorFillVideoSurfaceArea( FRAME_BUFFER, sX - 10, sY - 2, (!gPongForce4Paddles && gPongPlayers < 3) ? sX + 220 : sX + 500, sY + 20, Get16BPPColor( FROMRGB( 0, 0, 0 ) ) );
				
				CHAR16		sText[800];
				swprintf( sText, L"" );

				CHAR16		player1name[20];
				swprintf( player1name, L"Player 1" );
								
				CHAR16		player2name[20];
				swprintf( player2name, L"Player 2" );

				CHAR16		player3name[20];
				swprintf( player3name, L"Player 3" );

				CHAR16		player4name[20];
				swprintf( player4name, L"Player 4" );

				// get the name of the players - use the currently controlled mercs and anybody nearby for this
				BOOLEAN foundplayer_1 = FALSE;
				BOOLEAN foundplayer_2 = FALSE;
				BOOLEAN foundplayer_3 = FALSE;
				BOOLEAN foundplayer_4 = FALSE;
				INT32 player1gridno = NOWHERE;
				SOLDIERTYPE*		pSoldier = NULL;
				if ( gusSelectedSoldier != NOBODY )
				{
					GetSoldier( &pSoldier, gusSelectedSoldier );

					if ( pSoldier )
					{
						swprintf( player1name, pSoldier->GetName( ) );
						player1gridno = pSoldier->sGridNo;
					}
				}

				UINT16				bMercID = 0;
				for ( pSoldier = MercPtrs[bMercID]; bMercID <= TOTAL_SOLDIERS; ++bMercID, ++pSoldier )
				{
					// everybody other merc in the same sector gets annoyed
					if ( bMercID != gusSelectedSoldier && pSoldier->bActive && pSoldier->bInSector )
					{						
						if ( PythSpacesAway( player1gridno, pSoldier->sGridNo ) < 6 )
						{
							if ( !foundplayer_2 )
							{
								swprintf( player2name, pSoldier->GetName( ) );
								foundplayer_2 = TRUE;

								if ( !gPongForce4Paddles && gPongPlayers < 3 )
									break;
							}
							else if ( gPongForce4Paddles || gPongPlayers > 2 )
							{
								if ( !foundplayer_3 )
								{
									swprintf( player3name, pSoldier->GetName( ) );
									foundplayer_3 = TRUE;
								}
								else if ( !foundplayer_4 )
								{
									swprintf( player4name, pSoldier->GetName( ) );
									foundplayer_4 = TRUE;

									break;
								}
							}
						}
					}
				}

				if ( !gPongForce4Paddles && gPongPlayers < 3 )
					swprintf( sText, L"%s %d - %s %d", player1name, gPongScorePlayer[1], player2name, gPongScorePlayer[2] );
				else
					swprintf( sText, L"%s %d - %s %d - %s %d - %s %d", player1name, gPongScorePlayer[1], player2name, gPongScorePlayer[2], player3name, gPongScorePlayer[3], player4name, gPongScorePlayer[4] );

				sY += DisplayWrappedString( sX, sY, StringPixLength( sText, fontused ), 2, fontused, FONT_WHITE, sText, FONT_MCOLOR_BLACK, FALSE, 0 );

				// display key bindings
				sX = MINIGAME_PONG_X_HIGH + 20;
				sY = MINIGAME_PONG_Y_LOW;

				swprintf( sText, L"Key bindings" );
				sY += DisplayWrappedString( sX, sY, StringPixLength( sText, fontused ), 2, fontused, FONT_WHITE, sText, FONT_MCOLOR_BLACK, FALSE, 0 );

				swprintf( sText, L"%s: <, >", player1name );
				sY += DisplayWrappedString( sX, sY, StringPixLength( sText, fontused ), 2, fontused, FONT_WHITE, sText, FONT_MCOLOR_BLACK, FALSE, 0 );

				if ( !gPongForce4Paddles )
				{
					if ( gPongPlayers >= 2 )
					{
						swprintf( sText, L"%s: 4, 6", player2name );
						sY += DisplayWrappedString( sX, sY, StringPixLength( sText, fontused ), 2, fontused, FONT_WHITE, sText, FONT_MCOLOR_BLACK, FALSE, 0 );
					}

					if ( gPongPlayers >= 3 )
					{
						swprintf( sText, L"%s: w, s", player3name );
						sY += DisplayWrappedString( sX, sY, StringPixLength( sText, fontused ), 2, fontused, FONT_WHITE, sText, FONT_MCOLOR_BLACK, FALSE, 0 );
					}

					if ( gPongPlayers >= 4 )
					{
						swprintf( sText, L"%s: i, k", player4name );
						sY += DisplayWrappedString( sX, sY, StringPixLength( sText, fontused ), 2, fontused, FONT_WHITE, sText, FONT_MCOLOR_BLACK, FALSE, 0 );
					}
				}

				// movement is meant per, say, 100 ticks
				FLOAT movefraction = (FLOAT)(timesincelasttick) / 50.0f;

				if ( movefraction > 0.0f )
				{
					// due to our use of iterators, we only erase objects later on
					// this might seem somewhat odd, but makes use of iterators easier
					BOOLEAN eraseobject = FALSE;

					for ( std::vector<PONG_OBJECT>::iterator it = gPongObjects.begin(); it != gPongObjects.end(); ++it )
					{
						double delta_x = sin( it->angle ) * it->speed;
						double delta_y = cos( it->angle ) * it->speed;

						if ( it->type == PLAYER_PAD )
						{
							if ( it == it_player1 || it == it_player2 )
							{
								// for safety
								delta_y = 0;
							}
							else if ( it == it_player3 || it == it_player4 )
							{
								// for safety
								delta_x = 0;
							}
						}
						
						it->x += movefraction * delta_x;
						it->y += movefraction * delta_y;

						if ( it->type == BALL )
						{
							if ( GetJA2Clock( ) - it->timepassedsincespeedup > PONG_SPEED_INCREASE_TIMER )
							{
								it->speed = min( gPongMaxSpeed, it->speed + 1 );

								it->timepassedsincespeedup = 0;
							}
						}
						
						if ( it->type == PLAYER_PAD )
						{
							if ( it == it_player1 || it == it_player2 )
							{
								// for safety
								it->x = min( MINIGAME_PONG_X_HIGH - it->r, max( MINIGAME_PONG_X_LOW + it->r, it->x ) );
							}
							else if ( it == it_player3 || it == it_player4 )
							{
								// for safety
								it->y = min( MINIGAME_PONG_Y_HIGH - it->r, max( MINIGAME_PONG_Y_LOW + it->r, it->y ) );
							}
						}
						
						double factor_move_offset = 0.1f;
						double factor_paddle_pos_offset = 0.1f;
												
						if ( it->type == BALL )
						{
							// check whether we will be reflected by a paddle
							if ( it->y + it->r > MINIGAME_PONG_Y_HIGH )
							{
								if ( it_player1 != gPongObjects.end() )
								{
									if ( it->x >= it_player1->x - it_player1->r && it->x <= it_player1->x + it_player1->r )
									{
										it->y = MINIGAME_PONG_Y_HIGH - it->r;

										// if a paddle moves, add an offset
										double paddle_move_offset = PI - it_player1->angle;

										// add an offset depending on where th paddle is hit - the further the hit is from the center, the bigger the offset
										double paddle_pos_offset = ((it->x - it_player1->x) / it_player1->r) * PI / 2;

										// we need the perpendicular
										double n = 2 * PI;
										it->angle = 2 * n + PI - it->angle + factor_move_offset * paddle_move_offset + factor_paddle_pos_offset * paddle_pos_offset;

										it->lastplayerthattouchedthis = 1;

										PlayJA2Sample( BIG_SWITCH3_IN, RATE_11025, 15, 1, MIDDLEPAN );
									}
								}
							}

							if ( it->y - it->r < MINIGAME_PONG_Y_LOW )
							{
								if ( it_player2 != gPongObjects.end( ) )
								{
									if ( it->x >= it_player2->x - it_player2->r && it->x <= it_player2->x + it_player2->r )
									{
										it->y = MINIGAME_PONG_Y_LOW + it->r;
										
										// if a paddle moves, add an offset
										double paddle_move_offset = PI - it_player2->angle;

										// add an offset depending on where th paddle is hit - the further the hit is from the center, the bigger the offset
										double paddle_pos_offset = ((it->x - it_player2->x) / it_player2->r) * PI / 2;

										// we need the perpendicular
										double n = PI;
										it->angle = 2 * n + PI - it->angle + factor_move_offset * paddle_move_offset + factor_paddle_pos_offset * paddle_pos_offset;

										it->lastplayerthattouchedthis = 2;

										PlayJA2Sample( BIG_SWITCH3_IN, RATE_11025, 15, 1, MIDDLEPAN );
									}
								}
							}

							// depending on how many players there are, there are paddles or walls on the sides
							if ( it->x - it->r < MINIGAME_PONG_X_LOW )
							{
								if ( it_player3 != gPongObjects.end( ) )
								{
									if ( it->y >= it_player3->y - it_player3->r && it->y <= it_player3->y + it_player3->r )
									{
										it->x = MINIGAME_PONG_X_LOW + it->r;

										// if a paddle moves, add an offset
										double paddle_move_offset = PI - it_player3->angle;

										// add an offset depending on where th paddle is hit - the further the hit is from the center, the bigger the offset
										double paddle_pos_offset = ((it->y - it_player3->y) / it_player3->r) * PI / 2;

										// we need the perpendicular
										double n = PI / 2;
										it->angle = 2 * n + PI - it->angle + factor_move_offset * paddle_move_offset + factor_paddle_pos_offset * paddle_pos_offset;

										it->lastplayerthattouchedthis = 3;

										PlayJA2Sample( BIG_SWITCH3_IN, RATE_11025, 15, 1, MIDDLEPAN );
									}
								}
								else
								{
									it->x = MINIGAME_PONG_X_LOW + it->r;

									double n = PI / 2;
									it->angle = 2 * n + PI - it->angle;
								}
							}
							else if ( it->x + it->r > MINIGAME_PONG_X_HIGH )
							{
								if ( it_player4 != gPongObjects.end( ) )
								{
									if ( it->y >= it_player4->y - it_player4->r && it->y <= it_player4->y + it_player4->r )
									{
										it->x = MINIGAME_PONG_X_HIGH - it->r;

										// if a paddle moves, add an offset
										double paddle_move_offset = PI - it_player4->angle;

										// add an offset depending on where th paddle is hit - the further the hit is from the center, the bigger the offset
										double paddle_pos_offset = ((it->y - it_player4->y) / it_player4->r) * PI / 2;

										// we need the perpendicular
										double n = - PI / 2;
										it->angle = 2 * n + PI - it->angle + factor_move_offset * paddle_move_offset + factor_paddle_pos_offset * paddle_pos_offset;

										it->lastplayerthattouchedthis = 4;

										PlayJA2Sample( BIG_SWITCH3_IN, RATE_11025, 15, 1, MIDDLEPAN );
									}
								}
								else
								{
									it->x = MINIGAME_PONG_X_HIGH - it->r;

									double n = -PI / 2;
									it->angle = 2 * n + PI - it->angle;
								}
							}

							// check whether we will be reflected by another ball
							std::vector<PONG_OBJECT>::iterator it2 = it;
							++it2;
							for ( ; it2 != gPongObjects.end(); ++it2)
							{
								if ( it2->type == BALL )
								{
									if ( sqrt( (it->x - it2->x) * (it->x - it2->x) + (it->y - it2->y) * (it->y - it2->y) ) <= it->r )
									{
										double n2 = PI / 2 + it2->angle;
										double n1 = PI / 2 + it->angle;
										it->angle = 2 * n2 + PI - it->angle;
										it2->angle = 2 * n1 + PI - it2->angle;

										double tmp = it->speed;
										it->speed = it2->speed;
										it2->speed = tmp;
										
										double delta_x = sin( it2->angle ) * it2->speed;
										double delta_y = cos( it2->angle ) * it2->speed;
										
										it2->x += movefraction * delta_x;
										it2->y += movefraction * delta_y;

										PlayJA2Sample( VSM_SWITCH2_OUT, RATE_11025, 15, 1, MIDDLEPAN );
									}
								}
							}
							
							// award points to the last player that touched the ball (if he doesn't somehow knock it into his own side)
							if ( it->y < MINIGAME_PONG_Y_LOW )
							{
								eraseobject = TRUE;
								it->erasethis = TRUE;

								if ( it->lastplayerthattouchedthis != 2 )
									gPongScorePlayer[it->lastplayerthattouchedthis] += 1;
							}
							else if ( it->y > MINIGAME_PONG_Y_HIGH )
							{
								eraseobject = TRUE;
								it->erasethis = TRUE;

								if ( it->lastplayerthattouchedthis != 1 )
									gPongScorePlayer[it->lastplayerthattouchedthis] += 1;
							}
							else if ( (gPongForce4Paddles || gPongPlayers >= 3) && it->x < MINIGAME_PONG_X_LOW )
							{
								eraseobject = TRUE;
								it->erasethis = TRUE;

								if ( it->lastplayerthattouchedthis != 3 )
									gPongScorePlayer[it->lastplayerthattouchedthis] += 1;
							}
							else if ( (gPongForce4Paddles || gPongPlayers >= 3) && it->x > MINIGAME_PONG_X_HIGH )
							{
								eraseobject = TRUE;
								it->erasethis = TRUE;

								if ( it->lastplayerthattouchedthis != 4 )
									gPongScorePlayer[it->lastplayerthattouchedthis] += 1;
							}
						}
					}

					// erase objects now
					if ( eraseobject )
					{
						for ( std::vector<PONG_OBJECT>::iterator it = gPongObjects.begin(); it != gPongObjects.end(); )
						{
							if ( it->erasethis )
								it = gPongObjects.erase( it );
							else
								++it;
						}

						PlayJA2Sample( BIG_SWITCH3_OUT, RATE_11025, 15, 1, MIDDLEPAN );
					}
				}
			}

			// draw stuff
			{
				UINT16 col_green = Get16BPPColor( FROMRGB( 0, 255, 0 ) );
				UINT16 col_white = Get16BPPColor( FROMRGB( 255, 255, 255 ) );

				// background
				ColorFillVideoSurfaceArea( FRAME_BUFFER, MINIGAME_PONG_X_LOW, MINIGAME_PONG_Y_LOW, MINIGAME_PONG_X_HIGH, MINIGAME_PONG_Y_HIGH, Get16BPPColor( FROMRGB( 0, 0, 0 ) ) );

				for ( std::vector<PONG_OBJECT>::iterator it = gPongObjects.begin( ); it != gPongObjects.end( ); ++it )
				{
					switch ( it->type )
					{
					case PLAYER_PAD:
						{
							if ( it == it_player1 || it == it_player2 )
							{
								ColorFillVideoSurfaceArea( FRAME_BUFFER, 
															max( MINIGAME_PONG_X_LOW, it->x - it->r), 
															max( MINIGAME_PONG_Y_LOW, it->y - 7),
															min( MINIGAME_PONG_X_HIGH, it->x + it->r),
															min( MINIGAME_PONG_Y_HIGH, it->y + 7),
															col_green );
							}
							else
							{
								ColorFillVideoSurfaceArea( FRAME_BUFFER,
														   max( MINIGAME_PONG_X_LOW, it->x - 7 ),
														   max( MINIGAME_PONG_Y_LOW, it->y - it->r ),
														   min( MINIGAME_PONG_X_HIGH, it->x + 7 ),
														   min( MINIGAME_PONG_Y_HIGH, it->y + it->r ),
														   col_green );
							}
						}
						break;

					case BALL:
						{
							for ( int x = it->x - it->r + 1; x <= it->x + it->r; ++x )
							{
								// colour x, y if
								// (it->x - x)^2 + (it->y - y)^2 <= r^2
								// <=> (it->y - y) <= +/- sqrt( r^2 - (it->x - x)^2 )
								// <=> y >= it->y +/- sqrt( r^2 - (it->x - x)^2 )
								int sqrstuff = sqrt( it->r * it->r - (it->x - x) * (it->x - x) );
								
								ColorFillVideoSurfaceArea( FRAME_BUFFER, 
														   max( MINIGAME_PONG_X_LOW, x),
														   max( MINIGAME_PONG_Y_LOW, it->y - sqrstuff),
														   min( MINIGAME_PONG_X_HIGH, x + 1),
														   min( MINIGAME_PONG_Y_HIGH, it->y + sqrstuff),
														   col_white );
							}
						}
						break;

					default:
						break;
					}
				}
			}
			
		}
		else if ( gMiniGameState == MINIGAME_GAMEOVER )
		{

		}
		else if ( gMiniGameState == MINIGAME_PAUSE )
		{
			ColorFillVideoSurfaceArea( FRAME_BUFFER, menu_x_low, menu_y_low, menu_x_high, menu_y_high - 20, Get16BPPColor( FROMRGB( 0, 0, 0 ) ) );

			// display our score
			UINT16 sX = menu_x_low + 10;
			UINT16 sY = menu_y_low + 10;
			UINT16 width = 0;

			CHAR16		sText[800];
			swprintf( sText, L"" );

			swprintf( sText, L"Game paused" );
			width = StringPixLength( sText, fontused );
			sY += DisplayWrappedString( sX, sY, width, 2, fontused, FONT_RED, sText, FONT_MCOLOR_BLACK, FALSE, 0 );

			if ( gMiniGameMenuScreen == 3 )
			{
				int menuentry = 0;

				swprintf( sText, (gMiniGameMenuSelection == menuentry) ? L"-> Continue" : L"   Continue" );
				width = StringPixLength( sText, fontused );
				sY += DisplayWrappedString( sX, sY, width, 2, fontused, (gMiniGameMenuSelection == menuentry) ? FONT_BLACK : FONT_RED,
											sText, (gMiniGameMenuSelection == menuentry) ? FONT_MCOLOR_WHITE : FONT_MCOLOR_BLACK, FALSE, 0 );
				++menuentry;

				if ( keyactivation_enter )
				{
					gMiniGameState = MINIGAME_GAME;

					gMiniGameRenewBackground = TRUE;

					gPongPauseTimeOffset = GetJA2Clock( ) - gPongTimePauseActivated;
					gPongTimePauseActivated = 0;
				}
			}
		}
	}

	return MINIGAME_SCREEN;
}
////////////////////////////////////////////// PONG //////////////////////////////////////////////

////////////////////////////////////////////// PICTURE //////////////////////////////////////////////
// We display a picture. That's it.

std::string gstrInteractivePictureDisplay = "Interface\\background_tetris_startscreen.png";
bool gstrInteractivePictureStretch = false;

void SetInteractivePicture( std::string aStr, bool aVal ) { gstrInteractivePictureDisplay = aStr; gstrInteractivePictureStretch = aVal; }

void MiniGame_Init_Picture()
{
	gMiniGameRenewBackground = TRUE;
}

UINT32 MiniGame_Handle_Picture()
{
	// coordinates for the background rectangle
	if ( gMiniGameRenewBackground )
	{
		SGPRect backgroundrect;
		backgroundrect.iLeft = 0;
		backgroundrect.iTop = 0;
		backgroundrect.iRight = ( SCREEN_WIDTH - 0 );
		backgroundrect.iBottom = ( SCREEN_HEIGHT - 0 );

		// background: display a png picture
		DisplayPNGImage( backgroundrect, gstrInteractivePictureDisplay, gstrInteractivePictureStretch );

		gMiniGameRenewBackground = FALSE;
	}
				
	InvalidateScreen();

	SetCurrentCursorFromDatabase( VIDEO_NO_CURSOR );

	return MINIGAME_SCREEN;
}
////////////////////////////////////////////// PICTURE //////////////////////////////////////////////
