mirror of
https://github.com/1dot13/source.git
synced 2026-07-22 13:40:22 +02:00
git-svn-id: https://ja2svn.mooo.com/source/ja2/trunk/GameSource/ja2_v1.13/Build@8347 3b4a5df2-a311-0410-b5c6-a8a6f20db521
1304 lines
42 KiB
C++
1304 lines
42 KiB
C++
/**
|
|
* @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"
|
|
|
|
// 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;
|
|
|
|
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;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return MINIGAME_SCREEN;
|
|
}
|
|
|
|
UINT32 MiniGameScreenShutdown( void )
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
////////////////////////////////////////////// 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 gTetrisMenuSelection = 0; // In a small menu, we can select some options, so we need to keep track where we are
|
|
int gTetrisMenuScreen = 0; // What page of the menu is currently used?
|
|
int gTetrisInitialLevel = 0;
|
|
int gTetrisHandicap = 0;
|
|
BOOLEAN gTetrisRenewBackground = 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
|
|
|
|
// 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;
|
|
|
|
gTetrisMenuSelection = 0;
|
|
gTetrisMenuScreen = 0;
|
|
gTetrisRenewBackground = 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;
|
|
}
|
|
|
|
void DisplayPNGImage( SGPRect aDstRect, std::string& arStrImage )
|
|
{
|
|
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;
|
|
|
|
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 ) ) );
|
|
}
|
|
}
|
|
|
|
#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)
|
|
|
|
#define TETRIS_BLOCKLENGTH 20
|
|
|
|
// game area
|
|
#define MINIGAME_X_LOW (MINIGAME_BG_X_LOW + 100)
|
|
#define MINIGAME_X_HIGH (MINIGAME_X_LOW + TETRIS_FIELD_COLS * TETRIS_BLOCKLENGTH)
|
|
#define MINIGAME_Y_LOW (MINIGAME_BG_Y_LOW + 100)
|
|
#define MINIGAME_Y_HIGH (MINIGAME_Y_LOW + TETRIS_FIELD_ROWS * TETRIS_BLOCKLENGTH)
|
|
|
|
// we also need a small 4x4 area to display the next tile
|
|
#define TETRIS_PREVIEW_X (MINIGAME_X_HIGH + 50)
|
|
#define TETRIS_PREVIEW_Y (MINIGAME_Y_HIGH - 4 * TETRIS_BLOCKLENGTH)
|
|
|
|
UINT32 MiniGame_Handle_Tetris()
|
|
{
|
|
static UINT32 uiTimeOfLastUpdate = GetJA2Clock( );
|
|
|
|
if ( gMiniGame == TETRIS )
|
|
{
|
|
// coordinates for the background rectangle
|
|
if ( gTetrisRenewBackground )
|
|
{
|
|
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 );
|
|
|
|
gTetrisRenewBackground = 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;
|
|
gTetrisMenuScreen = 3;
|
|
gTetrisMenuSelection = 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 ( gTetrisMenuScreen == 0 )
|
|
{
|
|
int menuentry = 0;
|
|
|
|
swprintf( sText, (gTetrisMenuSelection == menuentry) ? L"-> New Game" : L" New Game" );
|
|
width = StringPixLength( sText, fontused );
|
|
sY += DisplayWrappedString( sX, sY, width, 2, fontused, (gTetrisMenuSelection == menuentry) ? FONT_BLACK : FONT_WHITE,
|
|
sText, (gTetrisMenuSelection == menuentry) ? FONT_MCOLOR_WHITE : FONT_MCOLOR_BLACK, FALSE, 0 );
|
|
++menuentry;
|
|
|
|
swprintf( sText, (gTetrisMenuSelection == menuentry) ? L"-> Options" : L" Options" );
|
|
width = StringPixLength( sText, fontused );
|
|
sY += DisplayWrappedString( sX, sY, width, 2, fontused, (gTetrisMenuSelection == menuentry) ? FONT_BLACK : FONT_WHITE,
|
|
sText, (gTetrisMenuSelection == menuentry) ? FONT_MCOLOR_WHITE : FONT_MCOLOR_BLACK, FALSE, 0 );
|
|
++menuentry;
|
|
|
|
swprintf( sText, (gTetrisMenuSelection == menuentry) ? L"-> Quit" : L" Quit" );
|
|
width = StringPixLength( sText, fontused );
|
|
sY += DisplayWrappedString( sX, sY, width, 2, fontused, (gTetrisMenuSelection == menuentry) ? FONT_BLACK : FONT_WHITE,
|
|
sText, (gTetrisMenuSelection == menuentry) ? FONT_MCOLOR_WHITE : FONT_MCOLOR_BLACK, FALSE, 0 );
|
|
++menuentry;
|
|
|
|
// arrow keys navigate the menu
|
|
if ( keyactivation_up )
|
|
{
|
|
gTetrisMenuSelection = max( 0, gTetrisMenuSelection - 1 );
|
|
}
|
|
else if ( keyactivation_down )
|
|
{
|
|
gTetrisMenuSelection = min( menuentry - 1, gTetrisMenuSelection + 1 );
|
|
}
|
|
|
|
// ENTER selects menu entry
|
|
if ( keyactivation_enter )
|
|
{
|
|
if ( gTetrisMenuSelection == 0 )
|
|
{
|
|
gMiniGameState = MINIGAME_GAME;
|
|
|
|
gTetrisRenewBackground = TRUE;
|
|
}
|
|
else if ( gTetrisMenuSelection == 1 )
|
|
{
|
|
gTetrisMenuScreen = 1;
|
|
|
|
gTetrisRenewBackground = TRUE;
|
|
}
|
|
else if ( gTetrisMenuSelection == 2 )
|
|
{
|
|
return MiniGameExit( );
|
|
}
|
|
}
|
|
}
|
|
else if ( gTetrisMenuScreen == 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, (gTetrisMenuSelection == menuentry) ? L"-> Starting Level: %d" : L" Starting Level: %d", gTetrisInitialLevel );
|
|
width = StringPixLength( sText, fontused );
|
|
sY += DisplayWrappedString( sX, sY, width, 2, fontused, (gTetrisMenuSelection == menuentry) ? FONT_BLACK : FONT_WHITE,
|
|
sText, (gTetrisMenuSelection == menuentry) ? FONT_MCOLOR_WHITE : FONT_MCOLOR_BLACK, FALSE, 0 );
|
|
++menuentry;
|
|
|
|
swprintf( sText, (gTetrisMenuSelection == menuentry) ? L"-> Handicap: %d" : L" Handicap: %d", gTetrisHandicap );
|
|
width = StringPixLength( sText, fontused );
|
|
sY += DisplayWrappedString( sX, sY, width, 2, fontused, (gTetrisMenuSelection == menuentry) ? FONT_BLACK : FONT_WHITE,
|
|
sText, (gTetrisMenuSelection == menuentry) ? FONT_MCOLOR_WHITE : FONT_MCOLOR_BLACK, FALSE, 0 );
|
|
++menuentry;
|
|
|
|
swprintf( sText, (gTetrisMenuSelection == menuentry) ? L"-> Apply" : L" Apply" );
|
|
width = StringPixLength( sText, fontused );
|
|
sY += DisplayWrappedString( sX, sY, width, 2, fontused, (gTetrisMenuSelection == menuentry) ? FONT_BLACK : FONT_WHITE,
|
|
sText, (gTetrisMenuSelection == menuentry) ? FONT_MCOLOR_WHITE : FONT_MCOLOR_BLACK, FALSE, 0 );
|
|
++menuentry;
|
|
|
|
// arrow keys navigate the menu
|
|
if ( keyactivation_up )
|
|
{
|
|
gTetrisMenuSelection = max( 0, gTetrisMenuSelection - 1 );
|
|
}
|
|
else if ( keyactivation_down )
|
|
{
|
|
gTetrisMenuSelection = min( menuentry - 1, gTetrisMenuSelection + 1 );
|
|
}
|
|
// arrow keys alter settings
|
|
else if ( keyactivation_left )
|
|
{
|
|
if ( gTetrisMenuSelection == 0 )
|
|
{
|
|
gTetrisInitialLevel = max( 0, gTetrisInitialLevel - 1 );
|
|
}
|
|
else if ( gTetrisMenuSelection == 1 )
|
|
{
|
|
gTetrisHandicap = max( 0, gTetrisHandicap - 1 );
|
|
}
|
|
}
|
|
else if ( keyactivation_right )
|
|
{
|
|
if ( gTetrisMenuSelection == 0 )
|
|
{
|
|
gTetrisInitialLevel = min( 20, gTetrisInitialLevel + 1 );
|
|
}
|
|
else if ( gTetrisMenuSelection == 1 )
|
|
{
|
|
gTetrisHandicap = min( TETRIS_MAX_HANDICAP, gTetrisHandicap + 1 );
|
|
}
|
|
}
|
|
|
|
// ENTER selects menu entry
|
|
if ( keyactivation_enter )
|
|
{
|
|
if ( gTetrisMenuSelection == 2 )
|
|
{
|
|
gTetrisMenuScreen = 0;
|
|
gTetrisMenuSelection = 1;
|
|
|
|
gTetrisRenewBackground = 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_Y_LOW + 10;
|
|
|
|
// score background
|
|
ColorFillVideoSurfaceArea( FRAME_BUFFER, TETRIS_PREVIEW_X, MINIGAME_Y_LOW, TETRIS_PREVIEW_X + 130, MINIGAME_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;
|
|
gTetrisMenuScreen = 2;
|
|
gTetrisMenuSelection = 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_X_LOW, MINIGAME_Y_LOW, MINIGAME_X_HIGH, MINIGAME_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_X_LOW + j * TETRIS_BLOCKLENGTH;
|
|
UINT16 y = MINIGAME_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_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 ( gTetrisMenuScreen == 2 )
|
|
{
|
|
int menuentry = 0;
|
|
|
|
swprintf( sText, (gTetrisMenuSelection == menuentry) ? L"-> Main Menu" : L" Main Menu" );
|
|
width = StringPixLength( sText, fontused );
|
|
sY += DisplayWrappedString( sX, sY, width, 2, fontused, (gTetrisMenuSelection == menuentry) ? FONT_BLACK : FONT_RED,
|
|
sText, (gTetrisMenuSelection == menuentry) ? FONT_MCOLOR_WHITE : FONT_MCOLOR_BLACK, FALSE, 0 );
|
|
++menuentry;
|
|
|
|
swprintf( sText, (gTetrisMenuSelection == menuentry) ? L"-> Quit" : L" Quit" );
|
|
width = StringPixLength( sText, fontused );
|
|
sY += DisplayWrappedString( sX, sY, width, 2, fontused, (gTetrisMenuSelection == menuentry) ? FONT_BLACK : FONT_RED,
|
|
sText, (gTetrisMenuSelection == menuentry) ? FONT_MCOLOR_WHITE : FONT_MCOLOR_BLACK, FALSE, 0 );
|
|
++menuentry;
|
|
|
|
// arrow keys navigate the menu
|
|
if ( keyactivation_up )
|
|
{
|
|
gTetrisMenuSelection = max( 0, gTetrisMenuSelection - 1 );
|
|
}
|
|
else if ( keyactivation_down )
|
|
{
|
|
gTetrisMenuSelection = min( menuentry - 1, gTetrisMenuSelection + 1 );
|
|
}
|
|
|
|
// ENTER selects menu entry
|
|
if ( keyactivation_enter )
|
|
{
|
|
if ( gTetrisMenuSelection == 0 )
|
|
{
|
|
MiniGame_Init_Tetris();
|
|
|
|
gMiniGameState = MINIGAME_STARTSCREEN;
|
|
gTetrisRenewBackground = TRUE;
|
|
}
|
|
else if ( gTetrisMenuSelection == 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 ( gTetrisMenuScreen == 3 )
|
|
{
|
|
int menuentry = 0;
|
|
|
|
swprintf( sText, (gTetrisMenuSelection == menuentry) ? L"-> Continue" : L" Continue" );
|
|
width = StringPixLength( sText, fontused );
|
|
sY += DisplayWrappedString( sX, sY, width, 2, fontused, (gTetrisMenuSelection == menuentry) ? FONT_BLACK : FONT_RED,
|
|
sText, (gTetrisMenuSelection == menuentry) ? FONT_MCOLOR_WHITE : FONT_MCOLOR_BLACK, FALSE, 0 );
|
|
++menuentry;
|
|
|
|
if ( keyactivation_enter )
|
|
{
|
|
gMiniGameState = MINIGAME_GAME;
|
|
|
|
gTetrisRenewBackground = TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return MINIGAME_SCREEN;
|
|
}
|
|
|
|
////////////////////////////////////////////// TETRIS //////////////////////////////////////////////
|