23d9812273
struct plugin_api *rb is declared in PLUGIN_HEADER, and pointed to by __header.api the loader uses this pointer to initialize rb before calling entry_point entry_point is no longer passed a pointer to the plugin API all plugins, and pluginlib functions, are modified to refer to the global rb pluginlib functions which only served to copy the API pointer are removed git-svn-id: svn://svn.rockbox.org/rockbox/trunk@19776 a1c6a512-1295-4272-9138-f99709370657
832 lines
24 KiB
C
832 lines
24 KiB
C
/***************************************************************************
|
|
* __________ __ ___.
|
|
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
|
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
|
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
|
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
|
* \/ \/ \/ \/ \/
|
|
* $Id$
|
|
*
|
|
* Copyright (C) 2004-2006 Antoine Cellerier <dionoea -at- videolan -dot- org>
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
*
|
|
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
|
* KIND, either express or implied.
|
|
*
|
|
****************************************************************************/
|
|
|
|
#include "plugin.h"
|
|
|
|
#ifdef HAVE_LCD_BITMAP
|
|
|
|
PLUGIN_HEADER
|
|
|
|
/* what the minesweeper() function can return */
|
|
enum minesweeper_status {
|
|
MINESWEEPER_WIN,
|
|
MINESWEEPER_LOSE,
|
|
MINESWEEPER_QUIT,
|
|
MINESWEEPER_USB
|
|
};
|
|
|
|
/* variable button definitions */
|
|
#if CONFIG_KEYPAD == RECORDER_PAD
|
|
# define MINESWP_LEFT BUTTON_LEFT
|
|
# define MINESWP_RIGHT BUTTON_RIGHT
|
|
# define MINESWP_UP BUTTON_UP
|
|
# define MINESWP_DOWN BUTTON_DOWN
|
|
# define MINESWP_QUIT BUTTON_OFF
|
|
# define MINESWP_TOGGLE BUTTON_ON
|
|
# define MINESWP_TOGGLE2 BUTTON_F1
|
|
# define MINESWP_DISCOVER BUTTON_PLAY
|
|
# define MINESWP_DISCOVER2 BUTTON_F2
|
|
# define MINESWP_INFO BUTTON_F3
|
|
|
|
#elif CONFIG_KEYPAD == ARCHOS_AV300_PAD
|
|
# define MINESWP_LEFT BUTTON_LEFT
|
|
# define MINESWP_RIGHT BUTTON_RIGHT
|
|
# define MINESWP_UP BUTTON_UP
|
|
# define MINESWP_DOWN BUTTON_DOWN
|
|
# define MINESWP_QUIT BUTTON_OFF
|
|
# define MINESWP_TOGGLE BUTTON_ON
|
|
# define MINESWP_TOGGLE2 BUTTON_F1
|
|
# define MINESWP_DISCOVER BUTTON_SELECT
|
|
# define MINESWP_DISCOVER2 BUTTON_F2
|
|
# define MINESWP_INFO BUTTON_F3
|
|
|
|
#elif CONFIG_KEYPAD == ONDIO_PAD
|
|
# define MINESWP_LEFT BUTTON_LEFT
|
|
# define MINESWP_RIGHT BUTTON_RIGHT
|
|
# define MINESWP_UP BUTTON_UP
|
|
# define MINESWP_DOWN BUTTON_DOWN
|
|
# define MINESWP_QUIT BUTTON_OFF
|
|
# define MINESWP_TOGGLE_PRE BUTTON_MENU
|
|
# define MINESWP_TOGGLE (BUTTON_MENU | BUTTON_REL)
|
|
# define MINESWP_DISCOVER (BUTTON_MENU | BUTTON_REPEAT)
|
|
# define MINESWP_INFO (BUTTON_MENU | BUTTON_OFF)
|
|
|
|
#elif (CONFIG_KEYPAD == IRIVER_H100_PAD) || \
|
|
(CONFIG_KEYPAD == IRIVER_H300_PAD)
|
|
# define MINESWP_LEFT BUTTON_LEFT
|
|
# define MINESWP_RIGHT BUTTON_RIGHT
|
|
# define MINESWP_UP BUTTON_UP
|
|
# define MINESWP_DOWN BUTTON_DOWN
|
|
# define MINESWP_QUIT BUTTON_OFF
|
|
# define MINESWP_TOGGLE BUTTON_ON
|
|
# define MINESWP_TOGGLE2 BUTTON_REC
|
|
# define MINESWP_DISCOVER BUTTON_SELECT
|
|
# define MINESWP_INFO BUTTON_MODE
|
|
|
|
# define MINESWP_RC_QUIT BUTTON_RC_STOP
|
|
|
|
#elif (CONFIG_KEYPAD == IPOD_4G_PAD) || \
|
|
(CONFIG_KEYPAD == IPOD_3G_PAD) || \
|
|
(CONFIG_KEYPAD == IPOD_1G2G_PAD)
|
|
# define MINESWP_SCROLLWHEEL
|
|
# define MINESWP_LEFT BUTTON_LEFT
|
|
# define MINESWP_RIGHT BUTTON_RIGHT
|
|
# define MINESWP_UP BUTTON_MENU
|
|
# define MINESWP_DOWN BUTTON_PLAY
|
|
# define MINESWP_NEXT BUTTON_SCROLL_FWD
|
|
# define MINESWP_PREV BUTTON_SCROLL_BACK
|
|
# define MINESWP_QUIT (BUTTON_SELECT | BUTTON_MENU)
|
|
# define MINESWP_TOGGLE_PRE BUTTON_SELECT
|
|
# define MINESWP_TOGGLE (BUTTON_SELECT | BUTTON_REL)
|
|
# define MINESWP_DISCOVER (BUTTON_SELECT | BUTTON_REPEAT)
|
|
# define MINESWP_INFO (BUTTON_SELECT | BUTTON_PLAY)
|
|
|
|
#elif (CONFIG_KEYPAD == IAUDIO_X5M5_PAD)
|
|
# define MINESWP_LEFT BUTTON_LEFT
|
|
# define MINESWP_RIGHT BUTTON_RIGHT
|
|
# define MINESWP_UP BUTTON_UP
|
|
# define MINESWP_DOWN BUTTON_DOWN
|
|
# define MINESWP_QUIT BUTTON_POWER
|
|
# define MINESWP_TOGGLE BUTTON_PLAY
|
|
# define MINESWP_DISCOVER BUTTON_SELECT
|
|
# define MINESWP_INFO BUTTON_REC
|
|
|
|
#elif (CONFIG_KEYPAD == GIGABEAT_PAD)
|
|
# define MINESWP_LEFT BUTTON_LEFT
|
|
# define MINESWP_RIGHT BUTTON_RIGHT
|
|
# define MINESWP_UP BUTTON_UP
|
|
# define MINESWP_DOWN BUTTON_DOWN
|
|
# define MINESWP_QUIT BUTTON_POWER
|
|
# define MINESWP_TOGGLE BUTTON_A
|
|
# define MINESWP_DISCOVER BUTTON_SELECT
|
|
# define MINESWP_INFO BUTTON_MENU
|
|
|
|
#elif (CONFIG_KEYPAD == SANSA_E200_PAD)
|
|
|
|
# define MINESWP_SCROLLWHEEL
|
|
# define MINESWP_LEFT BUTTON_LEFT
|
|
# define MINESWP_RIGHT BUTTON_RIGHT
|
|
# define MINESWP_UP BUTTON_UP
|
|
# define MINESWP_DOWN BUTTON_DOWN
|
|
# define MINESWP_QUIT BUTTON_POWER
|
|
# define MINESWP_NEXT BUTTON_SCROLL_FWD
|
|
# define MINESWP_PREV BUTTON_SCROLL_BACK
|
|
# define MINESWP_TOGGLE BUTTON_REC
|
|
# define MINESWP_DISCOVER BUTTON_SELECT
|
|
# define MINESWP_INFO (BUTTON_REC|BUTTON_REPEAT)
|
|
|
|
#elif (CONFIG_KEYPAD == SANSA_FUZE_PAD)
|
|
|
|
# define MINESWP_LEFT BUTTON_LEFT
|
|
# define MINESWP_RIGHT BUTTON_RIGHT
|
|
# define MINESWP_UP BUTTON_UP
|
|
# define MINESWP_DOWN BUTTON_DOWN
|
|
# define MINESWP_QUIT BUTTON_POWER
|
|
# define MINESWP_TOGGLE BUTTON_SCROLL_FWD
|
|
# define MINESWP_DISCOVER BUTTON_SELECT
|
|
# define MINESWP_INFO BUTTON_SCROLL_BACK
|
|
|
|
#elif (CONFIG_KEYPAD == SANSA_C200_PAD) || \
|
|
(CONFIG_KEYPAD == SANSA_CLIP_PAD) || \
|
|
(CONFIG_KEYPAD == SANSA_M200_PAD)
|
|
# define MINESWP_LEFT BUTTON_LEFT
|
|
# define MINESWP_RIGHT BUTTON_RIGHT
|
|
# define MINESWP_UP BUTTON_UP
|
|
# define MINESWP_DOWN BUTTON_DOWN
|
|
# define MINESWP_QUIT BUTTON_POWER
|
|
# define MINESWP_TOGGLE_PRE BUTTON_SELECT
|
|
# define MINESWP_TOGGLE (BUTTON_SELECT | BUTTON_REL)
|
|
# define MINESWP_TOGGLE2 BUTTON_VOL_DOWN
|
|
# define MINESWP_DISCOVER (BUTTON_SELECT | BUTTON_REPEAT)
|
|
# define MINESWP_DISCOVER2 BUTTON_VOL_UP
|
|
# define MINESWP_INFO (BUTTON_SELECT | BUTTON_UP)
|
|
|
|
#elif (CONFIG_KEYPAD == IRIVER_H10_PAD)
|
|
# define MINESWP_LEFT BUTTON_LEFT
|
|
# define MINESWP_RIGHT BUTTON_RIGHT
|
|
# define MINESWP_UP BUTTON_SCROLL_UP
|
|
# define MINESWP_DOWN BUTTON_SCROLL_DOWN
|
|
# define MINESWP_QUIT BUTTON_POWER
|
|
# define MINESWP_TOGGLE BUTTON_PLAY
|
|
# define MINESWP_DISCOVER BUTTON_REW
|
|
# define MINESWP_INFO (BUTTON_REW | BUTTON_PLAY)
|
|
|
|
#elif (CONFIG_KEYPAD == GIGABEAT_S_PAD)
|
|
# define MINESWP_LEFT BUTTON_LEFT
|
|
# define MINESWP_RIGHT BUTTON_RIGHT
|
|
# define MINESWP_UP BUTTON_UP
|
|
# define MINESWP_DOWN BUTTON_DOWN
|
|
# define MINESWP_QUIT BUTTON_BACK
|
|
# define MINESWP_TOGGLE BUTTON_PLAY
|
|
# define MINESWP_DISCOVER BUTTON_SELECT
|
|
# define MINESWP_INFO BUTTON_MENU
|
|
|
|
#elif (CONFIG_KEYPAD == MROBE100_PAD)
|
|
# define MINESWP_LEFT BUTTON_LEFT
|
|
# define MINESWP_RIGHT BUTTON_RIGHT
|
|
# define MINESWP_UP BUTTON_UP
|
|
# define MINESWP_DOWN BUTTON_DOWN
|
|
# define MINESWP_QUIT BUTTON_POWER
|
|
# define MINESWP_TOGGLE BUTTON_DISPLAY
|
|
# define MINESWP_DISCOVER BUTTON_SELECT
|
|
# define MINESWP_INFO BUTTON_MENU
|
|
|
|
#elif CONFIG_KEYPAD == IAUDIO_M3_PAD
|
|
# define MINESWP_LEFT BUTTON_RC_REW
|
|
# define MINESWP_RIGHT BUTTON_RC_FF
|
|
# define MINESWP_UP BUTTON_RC_VOL_UP
|
|
# define MINESWP_DOWN BUTTON_RC_VOL_DOWN
|
|
# define MINESWP_QUIT BUTTON_RC_REC
|
|
# define MINESWP_TOGGLE BUTTON_RC_MODE
|
|
# define MINESWP_DISCOVER BUTTON_RC_PLAY
|
|
# define MINESWP_INFO BUTTON_RC_MENU
|
|
|
|
#elif (CONFIG_KEYPAD == COWOND2_PAD)
|
|
# define MINESWP_QUIT BUTTON_POWER
|
|
|
|
#elif CONFIG_KEYPAD == CREATIVEZVM_PAD
|
|
# define MINESWP_LEFT BUTTON_LEFT
|
|
# define MINESWP_RIGHT BUTTON_RIGHT
|
|
# define MINESWP_UP BUTTON_UP
|
|
# define MINESWP_DOWN BUTTON_DOWN
|
|
# define MINESWP_QUIT BUTTON_BACK
|
|
# define MINESWP_TOGGLE BUTTON_SELECT
|
|
# define MINESWP_DISCOVER BUTTON_PLAY
|
|
# define MINESWP_INFO BUTTON_MENU
|
|
|
|
#else
|
|
#error No keymap defined!
|
|
#endif
|
|
|
|
#ifdef HAVE_TOUCHSCREEN
|
|
#ifndef MINESWP_QUIT
|
|
# define MINESWP_QUIT BUTTON_TOPLEFT
|
|
#endif
|
|
#ifndef MINESWP_LEFT
|
|
# define MINESWP_LEFT BUTTON_MIDLEFT
|
|
#endif
|
|
#ifndef MINESWP_RIGHT
|
|
# define MINESWP_RIGHT BUTTON_MIDRIGHT
|
|
#endif
|
|
#ifndef MINESWP_UP
|
|
# define MINESWP_UP BUTTON_TOPMIDDLE
|
|
#endif
|
|
#ifndef MINESWP_DOWN
|
|
# define MINESWP_DOWN BUTTON_BOTTOMMIDDLE
|
|
#endif
|
|
#ifndef MINESWP_TOGGLE
|
|
# define MINESWP_TOGGLE BUTTON_CENTER
|
|
#endif
|
|
#ifndef MINESWP_DISCOVER
|
|
# define MINESWP_DISCOVER BUTTON_BOTTOMLEFT
|
|
#endif
|
|
#ifndef MINESWP_INFO
|
|
# define MINESWP_INFO BUTTON_BOTTOMRIGHT
|
|
#endif
|
|
#endif
|
|
|
|
extern const fb_data minesweeper_tiles[];
|
|
|
|
#ifdef HAVE_LCD_COLOR
|
|
# if ( LCD_HEIGHT * LCD_WIDTH ) / ( 16 * 16 ) >= 130
|
|
/* We want to have at least 130 tiles on the screen */
|
|
# define TileSize 16
|
|
# elif ( LCD_HEIGHT * LCD_WIDTH ) / ( 12 * 12 ) >= 130
|
|
# define TileSize 12
|
|
# else
|
|
# define TileSize 10
|
|
# endif
|
|
# define BackgroundColor LCD_RGBPACK( 128, 128, 128 )
|
|
#elif LCD_DEPTH > 1
|
|
# define TileSize 12
|
|
#else
|
|
# define TileSize 8
|
|
#endif
|
|
|
|
#define Mine 9
|
|
#define Flag 10
|
|
#define Unknown 11
|
|
#define ExplodedMine 12
|
|
|
|
#define draw_tile( num, x, y ) \
|
|
rb->lcd_bitmap_part( minesweeper_tiles, 0, num * TileSize, \
|
|
TileSize, left+x*TileSize, top+y*TileSize, \
|
|
TileSize, TileSize )
|
|
|
|
#define invert_tile( x, y ) \
|
|
rb->lcd_set_drawmode(DRMODE_COMPLEMENT); \
|
|
rb->lcd_fillrect( left+x*TileSize, top+y*TileSize, TileSize, TileSize ); \
|
|
rb->lcd_set_drawmode(DRMODE_SOLID);
|
|
|
|
|
|
/* the tile struct
|
|
* if there is a mine, mine is true
|
|
* if tile is known by player, known is true
|
|
* if tile has a flag, flag is true
|
|
* neighbors is the total number of mines arround tile
|
|
*/
|
|
typedef struct tile
|
|
{
|
|
unsigned char mine : 1;
|
|
unsigned char known : 1;
|
|
unsigned char flag : 1;
|
|
unsigned char neighbors : 4;
|
|
} tile;
|
|
|
|
/* the height and width of the field */
|
|
#define MAX_HEIGHT (LCD_HEIGHT/TileSize)
|
|
#define MAX_WIDTH (LCD_WIDTH/TileSize)
|
|
int height = MAX_HEIGHT;
|
|
int width = MAX_WIDTH;
|
|
int top;
|
|
int left;
|
|
|
|
/* The Minefield. Caution it is defined as Y, X! Not the opposite. */
|
|
tile minefield[MAX_HEIGHT][MAX_WIDTH];
|
|
|
|
/* total number of mines on the game */
|
|
int mine_num = 0;
|
|
|
|
/* percentage of mines on minefield used during generation */
|
|
int p = 16;
|
|
|
|
/* number of tiles left on the game */
|
|
int tiles_left;
|
|
|
|
/* number of used flags on the game */
|
|
int flags_used;
|
|
|
|
/* Because mines are set after the first move... */
|
|
bool no_mines = true;
|
|
|
|
/* We need a stack (created on discover()) for the cascade algorithm. */
|
|
int stack_pos = 0;
|
|
|
|
/* a usefull string for snprintf */
|
|
char str[30];
|
|
|
|
#ifdef HAVE_TOUCHSCREEN
|
|
|
|
#include "lib/touchscreen.h"
|
|
static struct ts_raster mine_raster = { 0, 0, MAX_WIDTH, MAX_HEIGHT, TileSize, TileSize };
|
|
#endif
|
|
|
|
|
|
void push( int *stack, int y, int x )
|
|
{
|
|
if( stack_pos <= height*width )
|
|
{
|
|
stack[++stack_pos] = y;
|
|
stack[++stack_pos] = x;
|
|
}
|
|
}
|
|
|
|
/* Unveil tiles and push them to stack if they are empty. */
|
|
void unveil( int *stack, int y, int x )
|
|
{
|
|
if( x < 0 || y < 0 || x > width - 1 || y > height - 1
|
|
|| minefield[y][x].known
|
|
|| minefield[y][x].mine || minefield[y][x].flag ) return;
|
|
|
|
minefield[y][x].known = 1;
|
|
|
|
if( minefield[y][x].neighbors == 0 )
|
|
push( stack, y, x );
|
|
}
|
|
|
|
void discover( int y, int x )
|
|
{
|
|
int stack[height*width];
|
|
|
|
/* Selected tile. */
|
|
if( x < 0 || y < 0 || x > width - 1 || y > height - 1
|
|
|| minefield[y][x].known
|
|
|| minefield[y][x].mine || minefield[y][x].flag ) return;
|
|
|
|
minefield[y][x].known = 1;
|
|
/* Exit if the tile is not empty. (no mines nearby) */
|
|
if( minefield[y][x].neighbors ) return;
|
|
|
|
push( stack, y, x );
|
|
|
|
/* Scan all nearby tiles. If we meet a tile with a number we just unveil
|
|
* it. If we meet an empty tile, we push the location in stack. For each
|
|
* location in stack we do the same thing. (scan again all nearby tiles)
|
|
*/
|
|
while( stack_pos )
|
|
{
|
|
/* Pop x, y from stack. */
|
|
x = stack[stack_pos--];
|
|
y = stack[stack_pos--];
|
|
|
|
unveil( stack, y-1, x-1 );
|
|
unveil( stack, y-1, x );
|
|
unveil( stack, y-1, x+1 );
|
|
unveil( stack, y, x+1 );
|
|
unveil( stack, y+1, x+1 );
|
|
unveil( stack, y+1, x );
|
|
unveil( stack, y+1, x-1 );
|
|
unveil( stack, y, x-1 );
|
|
}
|
|
}
|
|
|
|
/* Reset the whole board for a new game. */
|
|
void minesweeper_init( void )
|
|
{
|
|
int i,j;
|
|
|
|
for( i = 0; i < MAX_HEIGHT; i++ )
|
|
{
|
|
for( j = 0; j < MAX_WIDTH; j++ )
|
|
{
|
|
minefield[i][j].known = 0;
|
|
minefield[i][j].flag = 0;
|
|
minefield[i][j].mine = 0;
|
|
minefield[i][j].neighbors = 0;
|
|
}
|
|
}
|
|
no_mines = true;
|
|
tiles_left = width*height;
|
|
}
|
|
|
|
|
|
/* put mines on the mine field */
|
|
/* there is p% chance that a tile is a mine */
|
|
/* if the tile has coordinates (x,y), then it can't be a mine */
|
|
void minesweeper_putmines( int p, int x, int y )
|
|
{
|
|
int i,j;
|
|
|
|
mine_num = 0;
|
|
for( i = 0; i < height; i++ )
|
|
{
|
|
for( j = 0; j < width; j++ )
|
|
{
|
|
if( rb->rand()%100 < p && !( y==i && x==j ) )
|
|
{
|
|
minefield[i][j].mine = 1;
|
|
mine_num++;
|
|
}
|
|
else
|
|
{
|
|
minefield[i][j].mine = 0;
|
|
}
|
|
minefield[i][j].neighbors = 0;
|
|
}
|
|
}
|
|
|
|
/* we need to compute the neighbor element for each tile */
|
|
for( i = 0; i < height; i++ )
|
|
{
|
|
for( j = 0; j < width; j++ )
|
|
{
|
|
if( i > 0 )
|
|
{
|
|
if( j > 0 )
|
|
minefield[i][j].neighbors += minefield[i-1][j-1].mine;
|
|
minefield[i][j].neighbors += minefield[i-1][j].mine;
|
|
if( j < width - 1 )
|
|
minefield[i][j].neighbors += minefield[i-1][j+1].mine;
|
|
}
|
|
if( j > 0 )
|
|
minefield[i][j].neighbors += minefield[i][j-1].mine;
|
|
if( j < width - 1 )
|
|
minefield[i][j].neighbors += minefield[i][j+1].mine;
|
|
if( i < height - 1 )
|
|
{
|
|
if( j > 0 )
|
|
minefield[i][j].neighbors += minefield[i+1][j-1].mine;
|
|
minefield[i][j].neighbors += minefield[i+1][j].mine;
|
|
if( j < width - 1 )
|
|
minefield[i][j].neighbors += minefield[i+1][j+1].mine;
|
|
}
|
|
}
|
|
}
|
|
|
|
no_mines = false;
|
|
|
|
/* In case the user is lucky and there are no mines positioned. */
|
|
if( !mine_num && height*width != 1 )
|
|
{
|
|
minesweeper_putmines(p, x, y);
|
|
}
|
|
}
|
|
|
|
/* A function that will uncover all the board, when the user wins or loses.
|
|
can easily be expanded, (just a call assigned to a button) as a solver. */
|
|
void mine_show( void )
|
|
{
|
|
int i, j, button;
|
|
|
|
for( i = 0; i < height; i++ )
|
|
{
|
|
for( j = 0; j < width; j++ )
|
|
{
|
|
if( minefield[i][j].mine )
|
|
{
|
|
if( minefield[i][j].known )
|
|
{
|
|
draw_tile( ExplodedMine, j, i );
|
|
}
|
|
else
|
|
{
|
|
draw_tile( Mine, j, i );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
draw_tile( minefield[i][j].neighbors, j, i );
|
|
}
|
|
}
|
|
}
|
|
rb->lcd_update();
|
|
|
|
do
|
|
button = rb->button_get(true);
|
|
while( ( button == BUTTON_NONE )
|
|
|| ( button & (BUTTON_REL|BUTTON_REPEAT) ) );
|
|
#ifdef HAVE_TOUCHSCREEN
|
|
button = BUTTON_NONE;
|
|
#endif
|
|
}
|
|
|
|
int count_tiles_left( void )
|
|
{
|
|
int tiles_left = 0;
|
|
int i, j;
|
|
for( i = 0; i < height; i++ )
|
|
for( j = 0; j < width; j++ )
|
|
if( minefield[i][j].known == 0 )
|
|
tiles_left++;
|
|
return tiles_left;
|
|
}
|
|
|
|
int count_flags( void )
|
|
{
|
|
int flags_used = 0;
|
|
int i, j;
|
|
for( i = 0; i < height; i++ )
|
|
for( j = 0; j < width; j++ )
|
|
if( minefield[i][j].flag == 1 )
|
|
flags_used++;
|
|
return flags_used;
|
|
}
|
|
|
|
/* welcome screen where player can chose mine percentage */
|
|
enum minesweeper_status menu( void )
|
|
{
|
|
int selection, result = MINESWEEPER_QUIT;
|
|
bool menu_quit = false;
|
|
|
|
MENUITEM_STRINGLIST( menu, "Minesweeper Menu", NULL, "Play Minesweeper",
|
|
"Mine Percentage", "Number of Rows",
|
|
"Number of Columns", "Quit" );
|
|
|
|
#ifdef HAVE_LCD_COLOR
|
|
rb->lcd_set_foreground( rb->global_settings->fg_color );
|
|
rb->lcd_set_background( rb->global_settings->bg_color );
|
|
#endif
|
|
|
|
while( !menu_quit )
|
|
{
|
|
switch( rb->do_menu( &menu, &selection, NULL, false ) )
|
|
{
|
|
case 0:
|
|
result = MINESWEEPER_WIN; /* start playing */
|
|
menu_quit = true;
|
|
break;
|
|
|
|
case 1:
|
|
rb->set_int( "Mine Percentage", "%", UNIT_INT, &p, NULL,
|
|
1, 2, 98, NULL );
|
|
break;
|
|
|
|
case 2:
|
|
rb->set_int( "Number of Rows", "", UNIT_INT, &height, NULL,
|
|
1, 1, MAX_HEIGHT, NULL );
|
|
break;
|
|
|
|
case 3:
|
|
rb->set_int( "Number of Columns", "", UNIT_INT, &width, NULL,
|
|
1, 1, MAX_WIDTH, NULL );
|
|
break;
|
|
|
|
default:
|
|
result = MINESWEEPER_QUIT; /* quit program */
|
|
menu_quit = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/* the big and ugly game function */
|
|
enum minesweeper_status minesweeper( void )
|
|
{
|
|
int i, j;
|
|
int button;
|
|
int lastbutton = BUTTON_NONE;
|
|
|
|
/* the cursor coordinates */
|
|
int x=0, y=0;
|
|
|
|
/**
|
|
* Show the menu
|
|
*/
|
|
if( ( i = menu() ) != MINESWEEPER_WIN ) return i;
|
|
|
|
/**
|
|
* Init game
|
|
*/
|
|
top = (LCD_HEIGHT-height*TileSize)/2;
|
|
left = (LCD_WIDTH-width*TileSize)/2;
|
|
|
|
#ifdef HAVE_TOUCHSCREEN
|
|
mine_raster.tl_x = left;
|
|
mine_raster.tl_y = top;
|
|
mine_raster.width = width*TileSize;
|
|
mine_raster.height = height*TileSize;
|
|
#endif
|
|
|
|
rb->srand( *rb->current_tick );
|
|
minesweeper_init();
|
|
x = 0;
|
|
y = 0;
|
|
|
|
/**
|
|
* Play
|
|
*/
|
|
while( true )
|
|
{
|
|
|
|
/* clear the screen buffer */
|
|
#ifdef HAVE_LCD_COLOR
|
|
rb->lcd_set_background( BackgroundColor );
|
|
#endif
|
|
rb->lcd_clear_display();
|
|
|
|
/* display the mine field */
|
|
for( i = 0; i < height; i++ )
|
|
{
|
|
for( j = 0; j < width; j++ )
|
|
{
|
|
if( minefield[i][j].known )
|
|
{
|
|
draw_tile( minefield[i][j].neighbors, j, i );
|
|
}
|
|
else if(minefield[i][j].flag)
|
|
{
|
|
draw_tile( Flag, j, i );
|
|
}
|
|
else
|
|
{
|
|
draw_tile( Unknown, j, i );
|
|
}
|
|
}
|
|
}
|
|
|
|
/* display the cursor */
|
|
invert_tile( x, y );
|
|
|
|
/* update the screen */
|
|
rb->lcd_update();
|
|
|
|
button = rb->button_get(true);
|
|
#ifdef HAVE_TOUCHSCREEN
|
|
if(button & BUTTON_TOUCHSCREEN)
|
|
{
|
|
struct ts_raster_result res;
|
|
if(touchscreen_map_raster(&mine_raster, rb->button_get_data() >> 16, rb->button_get_data() & 0xffff, &res) == 1)
|
|
{
|
|
button &= ~BUTTON_TOUCHSCREEN;
|
|
lastbutton &= ~BUTTON_TOUCHSCREEN;
|
|
|
|
if(button & BUTTON_REPEAT && lastbutton != MINESWP_TOGGLE && lastbutton ^ BUTTON_REPEAT)
|
|
button = MINESWP_TOGGLE;
|
|
else if(button == BUTTON_REL && lastbutton ^ BUTTON_REPEAT)
|
|
button = MINESWP_DISCOVER;
|
|
else
|
|
button |= BUTTON_TOUCHSCREEN;
|
|
|
|
x = res.x;
|
|
y = res.y;
|
|
}
|
|
}
|
|
#endif
|
|
switch(button)
|
|
{
|
|
/* quit minesweeper (you really shouldn't use this button ...) */
|
|
#ifdef MINESWP_RC_QUIT
|
|
case MINESWP_RC_QUIT:
|
|
#endif
|
|
case MINESWP_QUIT:
|
|
return MINESWEEPER_QUIT;
|
|
|
|
/* move cursor left */
|
|
case MINESWP_LEFT:
|
|
case MINESWP_LEFT|BUTTON_REPEAT:
|
|
x = ( x + width - 1 )%width;
|
|
break;
|
|
|
|
/* move cursor right */
|
|
case MINESWP_RIGHT:
|
|
case MINESWP_RIGHT|BUTTON_REPEAT:
|
|
x = ( x + 1 )%width;
|
|
break;
|
|
|
|
/* move cursor down */
|
|
case MINESWP_DOWN:
|
|
case MINESWP_DOWN|BUTTON_REPEAT:
|
|
y = ( y + 1 )%height;
|
|
break;
|
|
|
|
/* move cursor up */
|
|
case MINESWP_UP:
|
|
case MINESWP_UP|BUTTON_REPEAT:
|
|
y = ( y + height - 1 )%height;
|
|
break;
|
|
|
|
/*move cursor though the entire field*/
|
|
#ifdef MINESWP_SCROLLWHEEL
|
|
case MINESWP_NEXT:
|
|
case MINESWP_NEXT|BUTTON_REPEAT:
|
|
if (x == width -1 ) {
|
|
y = ( y + 1 )%height;
|
|
}
|
|
x = ( x + 1 )%width;
|
|
break;
|
|
|
|
case MINESWP_PREV:
|
|
case MINESWP_PREV|BUTTON_REPEAT:
|
|
if (x == 0) {
|
|
y = ( y + height - 1 )%height;
|
|
}
|
|
x = ( x + width - 1 )%width;
|
|
break;
|
|
#endif
|
|
/* discover a tile (and it's neighbors if .neighbors == 0) */
|
|
case MINESWP_DISCOVER:
|
|
#ifdef MINESWP_DISCOVER2
|
|
case MINESWP_DISCOVER2:
|
|
#endif
|
|
if( minefield[y][x].flag ) break;
|
|
/* we put the mines on the first "click" so that you don't
|
|
* lose on the first "click" */
|
|
if( tiles_left == width*height && no_mines )
|
|
minesweeper_putmines(p,x,y);
|
|
|
|
discover(y, x);
|
|
|
|
if( minefield[y][x].mine )
|
|
{
|
|
minefield[y][x].known = 1;
|
|
return MINESWEEPER_LOSE;
|
|
}
|
|
tiles_left = count_tiles_left();
|
|
if( tiles_left == mine_num )
|
|
{
|
|
return MINESWEEPER_WIN;
|
|
}
|
|
break;
|
|
|
|
/* toggle flag under cursor */
|
|
case MINESWP_TOGGLE:
|
|
#ifdef MINESWP_TOGGLE_PRE
|
|
if( lastbutton != MINESWP_TOGGLE_PRE )
|
|
break;
|
|
#endif
|
|
#ifdef MINESWP_TOGGLE2
|
|
case MINESWP_TOGGLE2:
|
|
#endif
|
|
minefield[y][x].flag = ( minefield[y][x].flag + 1 )%2;
|
|
break;
|
|
|
|
/* show how many mines you think you have found and how many
|
|
* there really are on the game */
|
|
case MINESWP_INFO:
|
|
if( no_mines )
|
|
break;
|
|
flags_used = count_flags();
|
|
if (flags_used == 1) {
|
|
rb->splashf( HZ*2, "You marked 1 field. There are %d mines.",
|
|
mine_num );
|
|
}
|
|
else
|
|
{
|
|
rb->splashf( HZ*2, "You marked %d fields. There are %d mines.",
|
|
flags_used, mine_num );
|
|
}
|
|
break;
|
|
|
|
default:
|
|
if( rb->default_event_handler( button ) == SYS_USB_CONNECTED )
|
|
return MINESWEEPER_USB;
|
|
break;
|
|
}
|
|
if( button != BUTTON_NONE )
|
|
lastbutton = button;
|
|
}
|
|
|
|
}
|
|
|
|
/* plugin entry point */
|
|
enum plugin_status plugin_start(const void* parameter)
|
|
{
|
|
bool exit = false;
|
|
|
|
(void)parameter;
|
|
#if LCD_DEPTH > 1
|
|
rb->lcd_set_backdrop(NULL);
|
|
#endif
|
|
|
|
while( !exit )
|
|
{
|
|
switch( minesweeper() )
|
|
{
|
|
case MINESWEEPER_WIN:
|
|
rb->splash( HZ, "You Win!" );
|
|
rb->lcd_clear_display();
|
|
mine_show();
|
|
break;
|
|
|
|
case MINESWEEPER_LOSE:
|
|
rb->splash( HZ, "You Lose!" );
|
|
rb->lcd_clear_display();
|
|
mine_show();
|
|
break;
|
|
|
|
case MINESWEEPER_USB:
|
|
return PLUGIN_USB_CONNECTED;
|
|
|
|
case MINESWEEPER_QUIT:
|
|
exit = true;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
return PLUGIN_OK;
|
|
}
|
|
|
|
#endif
|