rockbox/apps/plugins/solitaire.c
Daniel Stenberg 2acc0ac542 Updated our source code header to explicitly mention that we are GPL v2 or
later. We still need to hunt down snippets used that are not. 1324 modified
files...
http://www.rockbox.org/mail/archive/rockbox-dev-archive-2008-06/0060.shtml


git-svn-id: svn://svn.rockbox.org/rockbox/trunk@17847 a1c6a512-1295-4272-9138-f99709370657
2008-06-28 18:10:04 +00:00

1871 lines
54 KiB
C

/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2004-2007 Antoine Cellerier <dionoea @t videolan d.t 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"
#include "playback_control.h"
#include "configfile.h"
#include "button.h"
#include "lcd.h"
#include "oldmenuapi.h"
#ifdef HAVE_LCD_BITMAP
PLUGIN_HEADER
static const struct plugin_api* rb;
#define min(a,b) (a<b?a:b)
/**
* Key definitions
*/
#if CONFIG_KEYPAD == RECORDER_PAD
# define SOL_QUIT BUTTON_OFF
# define SOL_UP BUTTON_UP
# define SOL_DOWN BUTTON_DOWN
# define SOL_LEFT BUTTON_LEFT
# define SOL_RIGHT BUTTON_RIGHT
# define SOL_MOVE BUTTON_ON
# define SOL_DRAW BUTTON_F1
# define SOL_REM2CUR BUTTON_PLAY
# define SOL_CUR2STACK BUTTON_F2
# define SOL_REM2STACK BUTTON_F3
# define HK_MOVE "ON"
# define HK_DRAW "F1"
# define HK_REM2CUR "PLAY"
# define HK_CUR2STACK "F2"
# define HK_REM2STACK "F3"
#elif CONFIG_KEYPAD == ARCHOS_AV300_PAD
# define SOL_QUIT BUTTON_OFF
# define SOL_UP BUTTON_UP
# define SOL_DOWN BUTTON_DOWN
# define SOL_LEFT BUTTON_LEFT
# define SOL_RIGHT BUTTON_RIGHT
# define SOL_MOVE BUTTON_ON
# define SOL_DRAW BUTTON_F1
# define SOL_REM2CUR BUTTON_SELECT
# define SOL_CUR2STACK BUTTON_F2
# define SOL_REM2STACK BUTTON_F3
# define HK_MOVE "ON"
# define HK_DRAW "F1"
# define HK_REM2CUR "SELECT"
# define HK_CUR2STACK "F2"
# define HK_REM2STACK "F3"
#elif CONFIG_KEYPAD == ONDIO_PAD
# define SOL_QUIT BUTTON_OFF
# define SOL_UP_PRE BUTTON_UP
# define SOL_UP (BUTTON_UP | BUTTON_REL)
# define SOL_DOWN_PRE BUTTON_DOWN
# define SOL_DOWN (BUTTON_DOWN | BUTTON_REL)
# define SOL_LEFT_PRE BUTTON_LEFT
# define SOL_LEFT (BUTTON_LEFT | BUTTON_REL)
# define SOL_RIGHT_PRE BUTTON_RIGHT
# define SOL_RIGHT (BUTTON_RIGHT | BUTTON_REL)
# define SOL_MOVE_PRE BUTTON_MENU
# define SOL_MOVE (BUTTON_MENU | BUTTON_REL)
# define SOL_DRAW_PRE BUTTON_MENU
# define SOL_DRAW (BUTTON_MENU | BUTTON_REPEAT)
# define SOL_REM2CUR_PRE BUTTON_DOWN
# define SOL_REM2CUR (BUTTON_DOWN | BUTTON_REPEAT)
# define SOL_CUR2STACK_PRE BUTTON_UP
# define SOL_CUR2STACK (BUTTON_UP | BUTTON_REPEAT)
# define SOL_REM2STACK_PRE BUTTON_RIGHT
# define SOL_REM2STACK (BUTTON_RIGHT | BUTTON_REPEAT)
# define HK_MOVE "MODE"
# define HK_DRAW "MODE.."
# define HK_REM2CUR "DOWN.."
# define HK_CUR2STACK "UP.."
# define HK_REM2STACK "RIGHT.."
#elif (CONFIG_KEYPAD == IRIVER_H100_PAD) || \
(CONFIG_KEYPAD == IRIVER_H300_PAD)
# define SOL_QUIT BUTTON_OFF
# define SOL_UP BUTTON_UP
# define SOL_DOWN BUTTON_DOWN
# define SOL_LEFT BUTTON_LEFT
# define SOL_RIGHT BUTTON_RIGHT
# define SOL_MOVE_PRE BUTTON_SELECT
# define SOL_MOVE (BUTTON_SELECT | BUTTON_REL)
# define SOL_DRAW BUTTON_MODE
# define SOL_REM2CUR (BUTTON_LEFT | BUTTON_ON)
# define SOL_CUR2STACK_PRE BUTTON_SELECT
# define SOL_CUR2STACK (BUTTON_SELECT | BUTTON_REPEAT)
# define SOL_REM2STACK (BUTTON_RIGHT | BUTTON_ON)
# define SOL_REM BUTTON_REC
# define SOL_RC_QUIT BUTTON_RC_STOP
# define HK_MOVE "NAVI"
# define HK_DRAW "A-B"
# define HK_REM2CUR "PLAY+LEFT"
# define HK_CUR2STACK "NAVI.."
# define HK_REM2STACK "PLAY+RIGHT"
#elif (CONFIG_KEYPAD == IPOD_4G_PAD) || (CONFIG_KEYPAD == IPOD_3G_PAD) || \
(CONFIG_KEYPAD == IPOD_1G2G_PAD)
# define SOL_QUIT (BUTTON_SELECT | BUTTON_MENU)
# define SOL_UP BUTTON_SCROLL_BACK
# define SOL_DOWN BUTTON_SCROLL_FWD
# define SOL_LEFT_PRE BUTTON_LEFT
# define SOL_LEFT (BUTTON_LEFT | BUTTON_REL)
# define SOL_RIGHT_PRE BUTTON_RIGHT
# define SOL_RIGHT (BUTTON_RIGHT | BUTTON_REL)
# define SOL_MOVE_PRE BUTTON_SELECT
# define SOL_MOVE (BUTTON_SELECT | BUTTON_REL)
# define SOL_DRAW_PRE BUTTON_MENU
# define SOL_DRAW (BUTTON_MENU | BUTTON_REL)
# define SOL_REM2CUR BUTTON_PLAY
# define SOL_CUR2STACK_PRE BUTTON_MENU
# define SOL_CUR2STACK (BUTTON_MENU | BUTTON_REPEAT)
# define SOL_REM2STACK_PRE BUTTON_RIGHT
# define SOL_REM2STACK (BUTTON_RIGHT | BUTTON_REPEAT)
# define HK_UD "SCROLL U/D"
# define HK_MOVE "SELECT"
# define HK_DRAW "MENU"
# define HK_REM2CUR "PLAY"
# define HK_CUR2STACK "MENU.."
# define HK_REM2STACK "RIGHT.."
#elif (CONFIG_KEYPAD == IAUDIO_X5M5_PAD)
# define SOL_QUIT BUTTON_POWER
# define SOL_UP BUTTON_UP
# define SOL_DOWN BUTTON_DOWN
# define SOL_LEFT BUTTON_LEFT
# define SOL_RIGHT BUTTON_RIGHT
# define SOL_MOVE_PRE BUTTON_SELECT
# define SOL_MOVE (BUTTON_SELECT | BUTTON_REL)
# define SOL_DRAW_PRE BUTTON_PLAY
# define SOL_DRAW (BUTTON_PLAY | BUTTON_REL)
# define SOL_REM2CUR_PRE BUTTON_PLAY
# define SOL_REM2CUR (BUTTON_PLAY | BUTTON_REPEAT)
# define SOL_CUR2STACK_PRE BUTTON_SELECT
# define SOL_CUR2STACK (BUTTON_SELECT | BUTTON_REPEAT)
# define SOL_REM2STACK BUTTON_REC
# define HK_MOVE "SELECT"
# define HK_DRAW "PLAY"
# define HK_REM2CUR "PLAY.."
# define HK_CUR2STACK "SELECT.."
# define HK_REM2STACK "REC"
#elif (CONFIG_KEYPAD == GIGABEAT_PAD)
# define SOL_QUIT BUTTON_POWER
# define SOL_UP BUTTON_UP
# define SOL_DOWN BUTTON_DOWN
# define SOL_LEFT BUTTON_LEFT
# define SOL_RIGHT BUTTON_RIGHT
# define SOL_MOVE_PRE BUTTON_SELECT
# define SOL_MOVE (BUTTON_SELECT | BUTTON_REL)
# define SOL_DRAW BUTTON_MENU
# define SOL_REM2CUR (BUTTON_LEFT | BUTTON_A)
# define SOL_CUR2STACK (BUTTON_SELECT | BUTTON_REPEAT)
# define SOL_REM2STACK (BUTTON_RIGHT | BUTTON_A)
# define HK_MOVE "SELECT"
# define HK_DRAW "MENU"
# define HK_REM2CUR "A+LEFT"
# define HK_CUR2STACK "SELECT.."
# define HK_REM2STACK "A+RIGHT"
#elif (CONFIG_KEYPAD == SANSA_E200_PAD)
# define SOL_QUIT BUTTON_POWER
# define SOL_UP BUTTON_UP
# define SOL_DOWN BUTTON_DOWN
# define SOL_LEFT BUTTON_SCROLL_BACK
# define SOL_RIGHT BUTTON_SCROLL_FWD
# define SOL_MOVE BUTTON_SELECT
# define SOL_DRAW BUTTON_REC
# define SOL_REM2CUR BUTTON_LEFT
# define SOL_CUR2STACK_PRE BUTTON_REC
# define SOL_CUR2STACK (BUTTON_REC | BUTTON_RIGHT)
# define SOL_REM2STACK BUTTON_RIGHT
# define HK_MOVE "SELECT"
# define HK_DRAW "REC"
# define HK_REM2CUR "LEFT"
# define HK_CUR2STACK "DOUBLE SELECT"
# define HK_REM2STACK "RIGHT"
#elif (CONFIG_KEYPAD == SANSA_C200_PAD)
# define SOL_QUIT BUTTON_POWER
# define SOL_UP BUTTON_UP
# define SOL_DOWN BUTTON_DOWN
# define SOL_LEFT BUTTON_LEFT
# define SOL_RIGHT BUTTON_RIGHT
# define SOL_MOVE_PRE BUTTON_SELECT
# define SOL_MOVE (BUTTON_SELECT | BUTTON_REL)
# define SOL_DRAW BUTTON_VOL_DOWN
# define SOL_REM2CUR BUTTON_REC
# define SOL_CUR2STACK_PRE BUTTON_SELECT
# define SOL_CUR2STACK (BUTTON_SELECT | BUTTON_REPEAT)
# define SOL_REM2STACK BUTTON_VOL_UP
# define HK_MOVE "SELECT"
# define HK_DRAW "REC"
# define HK_REM2CUR "LEFT"
# define HK_CUR2STACK "DOUBLE SELECT"
# define HK_REM2STACK "RIGHT"
#elif (CONFIG_KEYPAD == IRIVER_H10_PAD)
# define SOL_QUIT BUTTON_POWER
# define SOL_UP BUTTON_SCROLL_UP
# define SOL_DOWN BUTTON_SCROLL_DOWN
# define SOL_LEFT_PRE BUTTON_LEFT
# define SOL_LEFT (BUTTON_LEFT | BUTTON_REL)
# define SOL_RIGHT_PRE BUTTON_RIGHT
# define SOL_RIGHT (BUTTON_RIGHT | BUTTON_REL)
# define SOL_MOVE BUTTON_PLAY
# define SOL_DRAW_PRE BUTTON_LEFT
# define SOL_DRAW (BUTTON_LEFT | BUTTON_REPEAT)
# define SOL_REM2CUR BUTTON_FF
# define SOL_CUR2STACK BUTTON_REW
# define SOL_REM2STACK_PRE BUTTON_RIGHT
# define SOL_REM2STACK (BUTTON_RIGHT | BUTTON_REPEAT)
# define HK_MOVE "PLAY"
# define HK_DRAW "LEFT.."
# define HK_REM2CUR "FF"
# define HK_CUR2STACK "REW"
# define HK_REM2STACK "RIGHT.."
#elif (CONFIG_KEYPAD == GIGABEAT_S_PAD)
# define SOL_QUIT BUTTON_BACK
# define SOL_UP BUTTON_UP
# define SOL_DOWN BUTTON_DOWN
# define SOL_LEFT BUTTON_LEFT
# define SOL_RIGHT BUTTON_RIGHT
# define SOL_MOVE_PRE BUTTON_SELECT
# define SOL_MOVE (BUTTON_SELECT | BUTTON_REL)
# define SOL_DRAW BUTTON_MENU
# define SOL_REM2CUR (BUTTON_LEFT | BUTTON_SELECT)
# define SOL_CUR2STACK (BUTTON_SELECT | BUTTON_REPEAT)
# define SOL_REM2STACK (BUTTON_RIGHT | BUTTON_SELECT)
# define HK_MOVE "SELECT"
# define HK_DRAW "MENU"
# define HK_REM2CUR "SELECT+LEFT"
# define HK_CUR2STACK "SELECT.."
# define HK_REM2STACK "SELECT+RIGHT"
#elif (CONFIG_KEYPAD == MROBE100_PAD)
# define SOL_QUIT BUTTON_POWER
# define SOL_UP BUTTON_UP
# define SOL_DOWN BUTTON_DOWN
# define SOL_LEFT BUTTON_LEFT
# define SOL_RIGHT BUTTON_RIGHT
# define SOL_MOVE_PRE BUTTON_SELECT
# define SOL_MOVE (BUTTON_SELECT | BUTTON_REL)
# define SOL_DRAW BUTTON_MENU
# define SOL_REM2CUR (BUTTON_LEFT | BUTTON_DISPLAY)
# define SOL_CUR2STACK (BUTTON_SELECT | BUTTON_REPEAT)
# define SOL_REM2STACK (BUTTON_RIGHT | BUTTON_DISPLAY)
# define HK_MOVE "SELECT"
# define HK_DRAW "MENU"
# define HK_REM2CUR "DISPLAY+LEFT"
# define HK_CUR2STACK "SELECT.."
# define HK_REM2STACK "DISPLAY+RIGHT"
#elif CONFIG_KEYPAD == IAUDIO_M3_PAD
# define SOL_QUIT BUTTON_RC_REC
# define SOL_UP BUTTON_RC_VOL_UP
# define SOL_DOWN BUTTON_RC_VOL_DOWN
# define SOL_LEFT BUTTON_RC_REW
# define SOL_RIGHT BUTTON_RC_FF
# define SOL_MOVE BUTTON_RC_PLAY
# define SOL_DRAW_PRE BUTTON_RC_MENU
# define SOL_DRAW (BUTTON_RC_MENU | BUTTON_REL)
# define SOL_REM2CUR_PRE BUTTON_RC_MENU
# define SOL_REM2CUR (BUTTON_RC_MENU | BUTTON_REPEAT)
# define SOL_CUR2STACK_PRE BUTTON_RC_MODE
# define SOL_CUR2STACK (BUTTON_RC_MODE | BUTTON_REL)
# define SOL_REM2STACK_PRE BUTTON_RC_MODE
# define SOL_REM2STACK (BUTTON_RC_MODE | BUTTON_REPEAT)
# define HK_LR "REW/FF"
# define HK_UD "VOL UP/DOWN"
# define HK_MOVE "PLAY"
# define HK_DRAW "MENU"
# define HK_REM2CUR "MENU.."
# define HK_CUR2STACK "MODE"
# define HK_REM2STACK "MODE.."
#elif (CONFIG_KEYPAD == COWOND2_PAD)
# define SOL_QUIT BUTTON_POWER
#else
#error No keymap defined!
#endif
#ifdef HAVE_TOUCHPAD
//#ifndef SOL_QUIT
//# define SOL_QUIT BUTTON_TOPLEFT
//endif
#ifndef SOL_UP
# define SOL_UP BUTTON_TOPMIDDLE
#endif
#ifndef SOL_DOWN
# define SOL_DOWN BUTTON_BOTTOMMIDDLE
#endif
#ifndef SOL_LEFT
# define SOL_LEFT BUTTON_MIDLEFT
#endif
#ifndef SOL_RIGHT
# define SOL_RIGHT BUTTON_MIDRIGHT
#endif
#ifndef SOL_MOVE
# define SOL_MOVE BUTTON_CENTER
# define HK_MOVE "CENTRE"
#endif
#ifndef SOL_DRAW
# define SOL_DRAW BUTTON_TOPLEFT
# define HK_DRAW "TOPLEFT"
#endif
#ifndef SOL_REM2CUR
# define SOL_REM2CUR BUTTON_TOPRIGHT
# define HK_REM2CUR "TOPRIGHT"
#endif
#ifndef SOL_CUR2STACK
# define SOL_CUR2STACK BUTTON_BOTTOMLEFT
# define HK_CUR2STACK "BOTTOMLEFT"
#endif
#ifndef SOL_REM2STACK
# define SOL_REM2STACK BUTTON_BOTTOMRIGHT
# define HK_REM2STACK "BOTTOMRIGHT"
#endif
#endif
#ifndef HK_LR
# define HK_LR "LEFT/RIGHT"
#endif
#ifndef HK_UD
# define HK_UD "UP/DOWN"
#endif
/**
* Help strings
*/
static int helplines;
static int displaylines;
static char helptext[] =
/* Use single spaces only! Close each line with one \0. */
"-- Navigation --\0"
HK_LR ": Move the cursor to the previous/ next column.\0"
HK_UD ": Move the cursor up/ down in the column.\0"
HK_MOVE ": Select cards, move cards, reveal hidden cards...\0"
HK_DRAW ": Deselect a card if it was selected. Else draw new card(s) from the remains stack.\0"
"-- Shortcuts --\0"
HK_REM2CUR ": Put the card on top of the remains stack on top of the cursor.\0"
HK_CUR2STACK ": Put the card under the cursor on one of the 4 final stacks.\0"
HK_REM2STACK ": Put the card on top of the remains stack on one of the 4 final stacks.\0"
;
/**
* Misc constants, graphics and other defines
*/
#include "card_back.h"
#include "card_deck.h"
#include "solitaire_suitsi.h"
#define CARD_GFX_WIDTH BMPWIDTH_card_back
#define CARD_GFX_HEIGHT BMPHEIGHT_card_back
#define CARD_WIDTH (BMPWIDTH_card_back+2)
#define CARD_HEIGHT (BMPHEIGHT_card_back+2)
#if LCD_WIDTH >= 320
# define MARGIN 4
# define LARGE_CARD
# define SYMBOL_HEIGHT 12
#elif LCD_WIDTH >= 220
# define MARGIN 3
# define LARGE_CARD
# define SYMBOL_HEIGHT 12
#elif LCD_WIDTH >= 160
# define MARGIN 2
# define SYMBOL_HEIGHT 11
#elif LCD_WIDTH >= 128
# define MARGIN 1
# define SYMBOL_HEIGHT 10
#else
# define MARGIN 0
# define SYMBOL_HEIGHT 8
#endif
#define CARD_START (CARD_HEIGHT+2*MARGIN+1)
/* background color */
#ifdef HAVE_LCD_COLOR
# define BACKGROUND_COLOR LCD_RGBPACK(0,157,0)
# define FRAME_COLOR LCD_RGBPACK(23,119,218)
#elif LCD_DEPTH > 1
# define BACKGROUND_COLOR LCD_WHITE
# define FRAME_COLOR LCD_BLACK
#endif
#define CONFIG_FILENAME "sol.cfg"
#define NOT_A_CARD -1
/* number of cards per suit */
#define CARDS_PER_SUIT 13
/* number of suits */
#define SUITS 4
#define NUM_CARDS ( CARDS_PER_SUIT * SUITS )
/* number of columns */
#define COL_NUM 7
/* pseudo column numbers to be used for cursor coordinates */
/* columns COL_NUM to COL_NUM + SUITS - 1 correspond to the final stacks */
#define STACKS_COL COL_NUM
/* column COL_NUM + SUITS corresponds to the remains' stack */
#define REM_COL (STACKS_COL + SUITS)
#define NOT_A_COL -1
typedef struct
{
signed char suit;
signed char num;
bool known : 1;
bool used : 1; /* this is what is used when dealing cards */
signed char next;
} card_t;
/**
* LCD card drawing routines
*/
static void draw_cursor( int x, int y )
{
rb->lcd_set_drawmode( DRMODE_COMPLEMENT );
rb->lcd_fillrect( x+1, y+1, CARD_GFX_WIDTH, CARD_GFX_HEIGHT );
#ifdef LARGE_CARD
rb->lcd_drawpixel( x+1, y+1 );
rb->lcd_drawpixel( x+1, y+CARD_HEIGHT-2 );
rb->lcd_drawpixel( x+CARD_WIDTH-2, y+1 );
rb->lcd_drawpixel( x+CARD_WIDTH-2, y+CARD_HEIGHT-2 );
#endif
rb->lcd_set_drawmode( DRMODE_SOLID );
}
/* Draw a card's border, select it if it's selected and draw the cursor
* if the cursor is currently over the card */
static void draw_card_ext( int x, int y, bool selected, bool cursor )
{
#if LCD_DEPTH > 1
int oldfg = rb->lcd_get_foreground();
rb->lcd_set_foreground( LCD_BLACK );
#endif
#ifdef LARGE_CARD
rb->lcd_hline( x+2, x+CARD_WIDTH-3, y );
rb->lcd_hline( x+2, x+CARD_WIDTH-3, y+CARD_HEIGHT-1 );
rb->lcd_vline( x, y+2, y+CARD_HEIGHT-3 );
rb->lcd_vline( x+CARD_WIDTH-1, y+2, y+CARD_HEIGHT-3 );
rb->lcd_drawpixel( x+1, y+1 );
rb->lcd_drawpixel( x+1, y+CARD_HEIGHT-2 );
rb->lcd_drawpixel( x+CARD_WIDTH-2, y+1 );
rb->lcd_drawpixel( x+CARD_WIDTH-2, y+CARD_HEIGHT-2 );
#else
rb->lcd_hline( x+1, x+CARD_WIDTH-2, y );
rb->lcd_hline( x+1, x+CARD_WIDTH-2, y+CARD_HEIGHT-1 );
rb->lcd_vline( x, y+1, y+CARD_HEIGHT-2 );
rb->lcd_vline( x+CARD_WIDTH-1, y+1, y+CARD_HEIGHT-2 );
#endif
if( selected )
{
#if LCD_DEPTH > 1
rb->lcd_set_foreground( FRAME_COLOR );
#endif
rb->lcd_drawrect( x+1, y+1, CARD_WIDTH-2, CARD_HEIGHT-2 );
#ifdef LARGE_CARD
rb->lcd_drawrect( x+2, y+2, CARD_WIDTH-4, CARD_HEIGHT-4 );
#endif
}
#if LCD_DEPTH > 1
rb->lcd_set_foreground( oldfg );
#endif
if( cursor )
{
draw_cursor( x, y );
}
}
/* Draw a card's inner graphics */
static void draw_card( card_t *card, int x, int y,
bool selected, bool cursor )
{
if( card->known )
{
rb->lcd_bitmap_part( card_deck, CARD_GFX_WIDTH * card->num,
CARD_GFX_HEIGHT * card->suit, BMPWIDTH_card_deck,
x+1, y+1, CARD_GFX_WIDTH, CARD_GFX_HEIGHT );
}
else
{
rb->lcd_bitmap( card_back, x+1, y+1,
CARD_GFX_WIDTH, CARD_GFX_HEIGHT );
}
draw_card_ext( x, y, selected, cursor );
}
/* Draw an empty stack */
static void draw_empty_stack( int s, int x, int y, bool cursor )
{
rb->lcd_bitmap_part( solitaire_suitsi, 0,
CARD_GFX_HEIGHT * s, BMPWIDTH_solitaire_suitsi,
x+1, y+1, CARD_GFX_WIDTH, CARD_GFX_HEIGHT );
draw_card_ext( x, y, false, cursor );
}
/**
* Help
*/
static void init_help(void)
{
int lines = 0;
int w_space, w, h;
int para_len;
char *para = helptext;
rb->lcd_getstringsize(" ", &w_space, &h);
displaylines = LCD_HEIGHT / h;
para_len = rb->strlen(para);
while (para_len)
{
bool first = true;
int x = 0;
char *next, *store;
next = rb->strtok_r(para, " ", &store);
while (next)
{
rb->lcd_getstringsize(next, &w, NULL);
if (!first)
{
if (x + w > LCD_WIDTH)
{
lines++;
x = 0;
}
else
next[-1] = ' '; /* re-concatenate string */
}
else
first = false;
x += w + w_space;
next = rb->strtok_r(NULL, " ", &store);
}
lines++;
para += para_len + 1;
para_len = rb->strlen(para);
}
helplines = lines;
}
enum help { HELP_QUIT, HELP_USB };
/* help for the not so intuitive interface */
enum help solitaire_help( void )
{
int start = 0;
int button;
int lastbutton = BUTTON_NONE;
bool fixed = (displaylines >= helplines);
while( true )
{
char *text = helptext;
int line = fixed ? (displaylines - helplines) / 2 : 0;
int i;
rb->lcd_clear_display();
for (i = 0; i < start + displaylines; i++)
{
if (i >= start)
rb->lcd_puts(0, line++, text);
text += rb->strlen(text) + 1;
}
rb->lcd_update();
button = rb->button_get( true );
switch( button )
{
case SOL_UP:
#ifdef SOL_UP_PRE
if( lastbutton != SOL_UP_PRE )
break;
#else
case SOL_UP|BUTTON_REPEAT:
#endif
if (!fixed && start > 0)
start--;
break;
case SOL_DOWN:
#ifdef SOL_DOWN_PRE
if( lastbutton != SOL_DOWN_PRE )
break;
#else
case SOL_DOWN|BUTTON_REPEAT:
#endif
if (!fixed && start < helplines - displaylines)
start++;
break;
#ifdef SOL_RC_QUIT
case SOL_RC_QUIT:
#endif
case SOL_QUIT:
return HELP_QUIT;
default:
if( rb->default_event_handler( button ) == SYS_USB_CONNECTED )
return HELP_USB;
break;
}
if( button != BUTTON_NONE )
lastbutton = button;
}
}
/**
* Custom menu / options
*/
#define CFGFILE_VERSION 0
struct sol_config {
int draw_type;
};
struct sol_config sol_disk = {0};
struct sol_config sol;
static struct configdata config[] = {
{ TYPE_INT, 0, 1, &sol_disk.draw_type, "draw_type", NULL, NULL },
};
char draw_option_string[32];
static void create_draw_option_string(void)
{
if (sol.draw_type == 0)
rb->strcpy(draw_option_string, "Draw Three Cards");
else
rb->strcpy(draw_option_string, "Draw One Card");
}
void solitaire_init(void);
/* menu return codes */
enum { MENU_RESUME, MENU_SAVE_AND_QUIT, MENU_QUIT, MENU_USB };
int solitaire_menu(bool in_game)
{
int m;
int result = -1;
int i = 0;
struct menu_item items[6];
if( in_game )
{
items[i++].desc = "Resume Game";
items[i++].desc = "Restart Game";
}
else
{
items[i++].desc = "Start Game";
items[i++].desc = draw_option_string;
}
items[i++].desc = "Help";
items[i++].desc = "Audio Playback";
if( in_game )
{
items[i++].desc = "Save and Quit";
}
items[i++].desc = "Quit";
create_draw_option_string();
m = menu_init(rb, items, i, NULL, NULL, NULL, NULL);
while (result < 0)
{
switch (menu_show(m))
{
case MENU_SELECTED_EXIT:
result = MENU_RESUME;
break;
case MENU_ATTACHED_USB:
result = MENU_USB;
break;
case 0:
result = MENU_RESUME;
break;
case 1:
if (in_game)
{
solitaire_init();
result = MENU_RESUME;
}
else
{
sol.draw_type = (sol.draw_type + 1) % 2;
create_draw_option_string();
}
break;
case 2:
if (solitaire_help() == HELP_USB)
result = MENU_USB;
break;
case 3:
playback_control(rb, NULL);
break;
case 4:
if( in_game )
result = MENU_SAVE_AND_QUIT;
else
result = MENU_QUIT;
break;
case 5:
result = MENU_QUIT;
break;
}
}
menu_exit(m);
return result;
}
/**
* Global variables
*/
/* player's cursor */
int cur_card;
/* player's cursor column num */
int cur_col;
/* selected card */
int sel_card;
/* the deck */
card_t deck[ NUM_CARDS ];
/* the remaining cards */
/* first card of the remains' stack */
int rem;
/* upper visible card from the remains' stack */
int cur_rem;
/* number of cards drawn from the remains stack - 1 */
int count_rem;
/* number of cards per draw of the remains' stack */
int cards_per_draw;
/* the 7 game columns */
int cols[COL_NUM];
/* the 4 final stacks */
int stacks[SUITS];
/**
* Card handling routines
*/
int next_random_card( card_t *deck )
{
int i,r;
r = rb->rand()%(NUM_CARDS)+1;
i = 0;
while( r>0 )
{
i = (i + 1)%(NUM_CARDS);
if( !deck[i].used ) r--;
}
deck[i].used = true;
return i;
}
/* initialize the game */
void solitaire_init( void )
{
int c;
int i, j;
/* number of cards that are drawn on the remains' stack (by pressing F2) */
if( sol.draw_type == 0 )
{
cards_per_draw = 3;
}
else
{
cards_per_draw = 1;
}
/* init deck */
for( i=0; i<SUITS; i++ )
{
for( j=0; j<CARDS_PER_SUIT; j++ )
{
#define card deck[i*CARDS_PER_SUIT+j]
card.suit = i;
card.num = j;
card.known = true;
card.used = false;
card.next = NOT_A_CARD;
#undef card
}
}
/* deal the cards ... */
/* ... in the columns */
for( i=0; i<COL_NUM; i++ )
{
c = NOT_A_CARD;
for( j=0; j<=i; j++ )
{
if( c == NOT_A_CARD )
{
cols[i] = next_random_card( deck );
c = cols[i];
}
else
{
deck[c].next = next_random_card( deck );
c = deck[c].next;
}
if( j < i )
deck[c].known = false;
}
}
/* ... shuffle what's left of the deck */
rem = next_random_card(deck);
c = rem;
for( i=1; i < NUM_CARDS - COL_NUM * (COL_NUM + 1)/2; i++ )
{
deck[c].next = next_random_card( deck );
c = deck[c].next;
}
/* we now finished dealing the cards. The game can start ! (at last) */
/* init the stack */
for( i = 0; i<SUITS; i++ )
{
stacks[i] = NOT_A_CARD;
}
/* the cursor starts on upper left card */
cur_card = cols[0];
cur_col = 0;
/* no card is selected */
sel_card = NOT_A_CARD;
/* init the remainder */
cur_rem = NOT_A_CARD;
count_rem = -1;
}
/* find the column number in which 'card' can be found */
int find_card_col( int card )
{
int i;
int c;
if( card == NOT_A_CARD ) return NOT_A_COL;
for( i=0; i<COL_NUM; i++ )
{
c = cols[i];
while( c != NOT_A_CARD )
{
if( c == card ) return i;
c = deck[c].next;
}
}
for( i=0; i<SUITS; i++ )
{
c = stacks[i];
while( c != NOT_A_CARD )
{
if( c == card ) return STACKS_COL + i;
c = deck[c].next;
}
}
return REM_COL;
}
/* find the card preceding 'card' */
/* if it doesn't exist, return NOT_A_CARD */
int find_prev_card( int card ){
int i;
for( i=0; i < NUM_CARDS; i++ )
{
if( deck[i].next == card ) return i;
}
return NOT_A_CARD;
}
/* find the last card of a given column */
int find_last_card( int col )
{
int c;
if( col < COL_NUM )
{
c = cols[col];
}
else if( col < REM_COL )
{
c = stacks[col - STACKS_COL];
}
else
{
c = cur_rem;
}
if(c == NOT_A_CARD)
return c;
else
{
while(deck[c].next != NOT_A_CARD)
c = deck[c].next;
return c;
}
}
enum move { MOVE_OK, MOVE_NOT_OK };
enum move move_card( int dest_col, int src_card )
{
/* the column on which to take src_card */
int src_col;
/* the last card of dest_col */
int dest_card;
/* the card under src_card */
int src_card_prev;
/* you can't move no card (at least, it doesn't have any consequence) */
if( src_card == NOT_A_CARD ) return MOVE_NOT_OK;
/* you can't put a card back on the remains' stack */
if( dest_col == REM_COL ) return MOVE_NOT_OK;
/* you can't move an unknown card */
if( !deck[src_card].known ) return MOVE_NOT_OK;
src_col = find_card_col( src_card );
dest_card = find_last_card( dest_col );
src_card_prev = find_prev_card( src_card );
/* you can't move more than one card at a time from the final stack */
/* to the rest of the game */
if( src_col >= COL_NUM && src_col < REM_COL
&& deck[src_card].next != NOT_A_CARD )
{
return MOVE_NOT_OK;
}
/* if we (that means dest) are on one of the 7 columns ... */
if( dest_col < COL_NUM )
{
/* ... check is we are on an empty color and that the src is a king */
if( dest_card == NOT_A_CARD
&& deck[src_card].num == CARDS_PER_SUIT - 1 )
{
/* this is a winning combination */
cols[dest_col] = src_card;
}
/* ... or check if the cards follow one another and have
* different colorsuit */
else if(( deck[dest_card].suit + deck[src_card].suit)%2==1
&& deck[dest_card].num == deck[src_card].num + 1 )
{
/* this is a winning combination */
deck[dest_card].next = src_card;
}
/* ... or, humpf, well that's not good news */
else
{
/* this is not a winning combination */
return MOVE_NOT_OK;
}
}
/* if we are on one of the 4 final stacks ... */
else if( dest_col < REM_COL )
{
/* ... check if we are on an empty stack... */
if( dest_card == NOT_A_CARD )
{
/* ... and the src is an ace and this is the correct final stack */
if( deck[src_card].num == 0
&& deck[src_card].suit == dest_col - STACKS_COL )
{
/* this is a winning combination */
stacks[dest_col - STACKS_COL] = src_card;
}
else
{
/* this is not a winning combination */
return MOVE_NOT_OK;
}
}
else /* non-empty stack */
{
/* ...check if the cards follow one another, have the same suit and
* {that src has no .next element or is from the remains' stack} */
if( deck[dest_card].suit == deck[src_card].suit
&& deck[dest_card].num + 1 == deck[src_card].num
&& (deck[src_card].next == NOT_A_CARD || src_col == REM_COL) )
{
/* this is a winning combination */
deck[dest_card].next = src_card;
}
/* ... or, well that's not good news */
else
{
/* this is not a winning combination */
return MOVE_NOT_OK;
}
}
}
/* if we are on the remains' stack */
else
{
/* you can't move a card back to the remains' stack */
return MOVE_NOT_OK;
}
/* if the src card is from the remains' stack, we don't want to take
* the following cards */
if( src_col == REM_COL )
{
/* if src card is the first card from the stack */
if( src_card_prev == NOT_A_CARD )
{
rem = deck[src_card].next;
}
/* if src card is not the first card from the stack */
else
{
deck[src_card_prev].next = deck[src_card].next;
}
deck[src_card].next = NOT_A_CARD;
cur_rem = src_card_prev;
count_rem--;
}
/* if the src card is from somewhere else, just take everything */
else
{
if( src_card_prev == NOT_A_CARD )
{
if( src_col < COL_NUM )
{
cols[src_col] = NOT_A_CARD;
}
else
{
stacks[src_col - STACKS_COL] = NOT_A_CARD;
}
}
else
{
deck[src_card_prev].next = NOT_A_CARD;
deck[src_card_prev].known = true;
}
}
sel_card = NOT_A_CARD;
/* tada ! */
return MOVE_OK;
}
enum { SOLITAIRE_WIN, SOLITAIRE_SAVE_AND_QUIT, SOLITAIRE_QUIT, SOLITAIRE_USB };
/**
* Bouncing cards at the end of the game
*/
#define BC_ACCEL ((1<<16)*LCD_HEIGHT/128)
#define BC_MYSPEED (6*BC_ACCEL)
#define BC_MXSPEED (6*LCD_HEIGHT/128)
int bouncing_cards( void )
{
int i, j, x, vx, y, fp_y, fp_vy, button;
/* flush the button queue */
while( ( button = rb->button_get( false ) ) != BUTTON_NONE )
{
if( rb->default_event_handler( button ) == SYS_USB_CONNECTED )
return SOLITAIRE_USB;
}
/* fun stuff :) */
for( i = CARDS_PER_SUIT-1; i>=0; i-- )
{
for( j = 0; j < SUITS; j++ )
{
x = LCD_WIDTH-(CARD_WIDTH*4+4+MARGIN)+CARD_WIDTH*j+j+1;
fp_y = MARGIN<<16;
#if LCD_WIDTH > 200
vx = rb->rand() % (4*BC_MXSPEED/3-2) - BC_MXSPEED;
if( vx >= -1 )
vx += 3;
#else
vx = rb->rand() % (4*BC_MXSPEED/3) - BC_MXSPEED;
if( vx >= 0 )
vx++;
#endif
fp_vy = -rb->rand() % BC_MYSPEED;
while( x < LCD_WIDTH && x + CARD_WIDTH > 0 )
{
fp_vy += BC_ACCEL;
x += vx;
fp_y += fp_vy;
if( fp_y >= (LCD_HEIGHT-CARD_HEIGHT) << 16 )
{
fp_vy = -fp_vy*4/5;
fp_y = (LCD_HEIGHT-CARD_HEIGHT) << 16;
}
y = fp_y >> 16;
draw_card( &deck[j*CARDS_PER_SUIT+i], x, y,
false, false );
rb->lcd_update_rect( x<0?0:x, y<0?0:y,
CARD_WIDTH, CARD_HEIGHT );
button = rb->button_get_w_tmo( 2 );
if( rb->default_event_handler( button ) == SYS_USB_CONNECTED )
return SOLITAIRE_USB;
if( button == SOL_QUIT || button == SOL_MOVE )
return SOLITAIRE_WIN;
}
}
}
return SOLITAIRE_WIN;
}
/**
* Game save/load routines
*/
void get_save_filename( char *buf )
{
char *s;
rb->strcpy( buf, rb->plugin_get_current_filename() );
s = rb->strrchr( buf, '/' ) + 1;
*s = '\0';
rb->strcat( s, "sol.save" );
}
int open_save_file( int flags )
{
char buf[MAX_PATH];
get_save_filename( buf );
return rb->open( buf, flags );
}
void delete_save_file( void )
{
char buf[MAX_PATH];
get_save_filename( buf );
rb->remove( buf );
}
#ifdef write
# undef write
#endif
int save_write( int fd, const void *buf, size_t count, int *checksum )
{
size_t i;
if( rb->PREFIX(write)( fd, buf, count ) < (ssize_t)count )
return 1;
for( i = 0; i < count; i++ )
*checksum += (int)(((const char *)buf)[i]);
return 0;
}
#ifdef read
# undef read
#endif
int save_read( int fd, void *buf, size_t count, int *checksum )
{
size_t i;
if( rb->PREFIX(read)( fd, buf, count ) < (ssize_t)count )
return 1;
for( i = 0; i < count; i++ )
*checksum -= (int)(((const char *)buf)[i]);
return 0;
}
int save_game( void )
{
int fd = open_save_file( O_CREAT|O_WRONLY|O_TRUNC );
int checksum = 42;
if( fd < 0 )
return -1;
if( save_write( fd, &cur_card, sizeof( int ), &checksum )
|| save_write( fd, &cur_col, sizeof( int ), &checksum )
|| save_write( fd, &sel_card, sizeof( int ), &checksum )
|| save_write( fd, deck, NUM_CARDS * sizeof( card_t ), &checksum )
|| save_write( fd, &rem, sizeof( int ), &checksum )
|| save_write( fd, &cur_rem, sizeof( int ), &checksum )
|| save_write( fd, &count_rem, sizeof( int ), &checksum )
|| save_write( fd, &cards_per_draw, sizeof( int ), &checksum )
|| save_write( fd, cols, COL_NUM * sizeof( int ), &checksum )
|| save_write( fd, stacks, SUITS * sizeof( int ), &checksum )
|| ( rb->PREFIX(write)( fd, &checksum, sizeof( int ) ) < (ssize_t)(sizeof( int ) ) ) )
{
rb->close( fd );
rb->splash( 2*HZ, "Error while saving game. Aborting." );
return -2;
}
rb->close( fd );
return 0;
}
int load_game( void )
{
int fd = open_save_file( O_RDONLY );
int checksum;
if( fd < 0 )
return -1;
if( ( rb->PREFIX(lseek)( fd, -sizeof( int ), SEEK_END ) == -((ssize_t)sizeof( int ))-1 )
|| ( rb->PREFIX(read)( fd, &checksum, sizeof( int ) ) < ((ssize_t)sizeof( int )) )
|| ( rb->PREFIX(lseek)( fd, 0, SEEK_SET ) == -1 )
|| save_read( fd, &cur_card, sizeof( int ), &checksum )
|| save_read( fd, &cur_col, sizeof( int ), &checksum )
|| save_read( fd, &sel_card, sizeof( int ), &checksum )
|| save_read( fd, deck, NUM_CARDS * sizeof( card_t ), &checksum )
|| save_read( fd, &rem, sizeof( int ), &checksum )
|| save_read( fd, &cur_rem, sizeof( int ), &checksum )
|| save_read( fd, &count_rem, sizeof( int ), &checksum )
|| save_read( fd, &cards_per_draw, sizeof( int ), &checksum )
|| save_read( fd, cols, COL_NUM * sizeof( int ), &checksum )
|| save_read( fd, stacks, SUITS * sizeof( int ), &checksum ) )
{
rb->close( fd );
rb->splash( 2*HZ, "Error while loading saved game. Aborting." );
delete_save_file();
return -2;
}
rb->close( fd );
if( checksum != 42 )
{
rb->splash( 2*HZ, "Save file was corrupted. Aborting." );
delete_save_file();
return -3;
}
return 0;
}
/**
* The main game loop
*
* If skipmenu is defined to SOLITAIRE_QUIT, the menu will be skipped and
* game will resume.
*/
int solitaire( int skipmenu )
{
int i,j;
int button, lastbutton = 0;
int c,h,prevcard;
int biggest_col_length;
rb->srand( *rb->current_tick );
if( skipmenu != SOLITAIRE_QUIT )
{
switch( solitaire_menu(false) )
{
case MENU_QUIT:
return SOLITAIRE_QUIT;
case MENU_USB:
return SOLITAIRE_USB;
}
solitaire_init();
}
while( true )
{
rb->lcd_clear_display();
/* get the biggest column length so that display can be "optimized" */
biggest_col_length = 0;
for(i=0;i<COL_NUM;i++)
{
j = 0;
c = cols[i];
if( c != NOT_A_CARD )
{
while( true )
{
/* don't include the last card in the column length. */
if( deck[c].next == NOT_A_CARD )
{
break; /* no successor: get outta here. */
}
else
{
if( deck[c].known )
j += 2;
else
j++;
}
c = deck[c].next;
}
/* make column distinguishable from an empty column,
* and avoid division by zero while displaying */
if( j == 0 )
j = 1;
}
if( j > biggest_col_length )
biggest_col_length = j;
}
/* check if there are cards remaining in the game. */
/* if there aren't any, that means you won :) */
if( biggest_col_length == 0 && rem == NOT_A_CARD )
{
rb->splash( HZ, "You Won :)" );
return bouncing_cards();
}
/* draw the columns */
for( i = 0; i < COL_NUM; i++ )
{
c = cols[i];
j = CARD_START;
while( true )
{
if( c == NOT_A_CARD )
{
/* draw the cursor on empty columns */
if( cur_col == i )
{
draw_cursor( MARGIN + i * (CARD_WIDTH
+(LCD_WIDTH-COL_NUM*CARD_WIDTH-2*MARGIN)/(COL_NUM-1)),
j );
}
break;
}
draw_card( &deck[c], MARGIN + i * (CARD_WIDTH
+(LCD_WIDTH-COL_NUM*CARD_WIDTH-2*MARGIN)/(COL_NUM-1)),
j, c == sel_card, c == cur_card );
h = c;
c = deck[c].next;
if( c == NOT_A_CARD ) break;
/* This is where we change the spacing between cards so that
* they don't overflow out of the LCD */
if( h == cur_card )
j += SYMBOL_HEIGHT;
else if( deck[h].known )
j += min( SYMBOL_HEIGHT,
2 * (LCD_HEIGHT - CARD_START - CARD_HEIGHT - MARGIN)
/ biggest_col_length );
else
j += min( SYMBOL_HEIGHT / 2,
(LCD_HEIGHT - CARD_START - CARD_HEIGHT - MARGIN)
/ biggest_col_length );
}
}
/* draw the stacks */
for( i=0; i<SUITS; i++ )
{
c = find_last_card( STACKS_COL + i );
if( c != NOT_A_CARD )
{
draw_card( &deck[c],
LCD_WIDTH-(CARD_WIDTH*4+4+MARGIN)+CARD_WIDTH*i+i+1,
MARGIN,
c == sel_card, cur_col == STACKS_COL + i );
}
else
{
draw_empty_stack( i,
LCD_WIDTH-(CARD_WIDTH*4+4+MARGIN)+CARD_WIDTH*i+i+1,
MARGIN, cur_col == STACKS_COL + i );
}
}
/* draw the remains */
if( rem != NOT_A_CARD &&
( cur_rem == NOT_A_CARD || deck[cur_rem].next != NOT_A_CARD ) )
{
/* gruik ! (we want to display a card back) */
deck[rem].known = false;
draw_card( &deck[rem], MARGIN, MARGIN, false, false );
deck[rem].known = true;
}
if( rem != NOT_A_CARD && cur_rem != NOT_A_CARD )
{
if( count_rem < 0 )
{
prevcard = rem;
count_rem = 0;
while( prevcard != cur_rem && count_rem < cards_per_draw-1 )
{
prevcard = deck[prevcard].next;
count_rem++;
}
}
prevcard = cur_rem;
j = CARD_WIDTH+2*MARGIN+1;
for( i = 0; i < count_rem; i++ )
prevcard = find_prev_card(prevcard);
for( i = 0; i <= count_rem; i++ )
{
draw_card( &deck[prevcard], j,
MARGIN, sel_card == prevcard,
cur_card == prevcard );
prevcard = deck[prevcard].next;
j += CARD_WIDTH/2;
}
}
if( ( cur_rem == NOT_A_CARD || rem == NOT_A_CARD )
&& cur_col == REM_COL )
{
draw_cursor( CARD_WIDTH+2*MARGIN+1, MARGIN );
}
rb->lcd_update();
/* what to do when a key is pressed ... */
button = rb->button_get( true );
#if (CONFIG_KEYPAD == SANSA_E200_PAD)
if (button&(BUTTON_SCROLL_BACK|BUTTON_SCROLL_FWD))
button = button & (~BUTTON_REPEAT);
#endif
switch( button )
{
/* move cursor to the last card of the previous column
* or to the previous final stack
* or to the remains stack */
case SOL_RIGHT:
#ifdef SOL_RIGHT_PRE
if( lastbutton != SOL_RIGHT_PRE )
break;
#endif
if( cur_col >= COL_NUM )
{
cur_col = 0;
}
else if( cur_col == COL_NUM - 1 )
{
cur_col = REM_COL;
}
else
{
cur_col = (cur_col+1)%(REM_COL+1);
}
if(cur_col == REM_COL)
{
cur_card = cur_rem;
break;
}
cur_card = find_last_card( cur_col );
break;
/* move cursor to the last card of the next column
* or to the next final stack
* or to the remains stack */
case SOL_LEFT:
#ifdef SOL_LEFT_PRE
if( lastbutton != SOL_LEFT_PRE )
break;
#endif
if( cur_col == 0 )
{
cur_col = REM_COL;
}
else if( cur_col >= COL_NUM )
{
cur_col = COL_NUM - 1;
}
else
{
cur_col = (cur_col + REM_COL)%(REM_COL+1);
}
if( cur_col == REM_COL )
{
cur_card = cur_rem;
break;
}
cur_card = find_last_card( cur_col );
break;
/* move cursor to card that's bellow */
case SOL_DOWN:
#ifdef SOL_DOWN_PRE
if( lastbutton != SOL_DOWN_PRE )
break;
#else
case SOL_DOWN|BUTTON_REPEAT:
#endif
if( cur_col >= COL_NUM )
{
cur_col = (cur_col - COL_NUM + 1)%(SUITS + 1) + COL_NUM;
if( cur_col == REM_COL )
{
cur_card = cur_rem;
}
else
{
cur_card = find_last_card( cur_col );
}
break;
}
if( cur_card == NOT_A_CARD ) break;
if( deck[cur_card].next != NOT_A_CARD )
{
cur_card = deck[cur_card].next;
}
else
{
cur_card = cols[cur_col];
while( !deck[ cur_card].known
&& deck[cur_card].next != NOT_A_CARD )
{
cur_card = deck[cur_card].next;
}
}
break;
/* move cursor to card that's above */
case SOL_UP:
#ifdef SOL_UP_PRE
if( lastbutton != SOL_UP_PRE )
break;
#else
case SOL_UP|BUTTON_REPEAT:
#endif
if( cur_col >= COL_NUM )
{
cur_col = (cur_col - COL_NUM + SUITS)%(SUITS + 1) + COL_NUM;
if( cur_col == REM_COL )
{
cur_card = cur_rem;
}
else
{
cur_card = find_last_card( cur_col );
}
break;
}
if( cur_card == NOT_A_CARD ) break;
do {
cur_card = find_prev_card( cur_card );
if( cur_card == NOT_A_CARD )
{
cur_card = find_last_card( cur_col );
}
} while( deck[cur_card].next != NOT_A_CARD
&& !deck[cur_card].known );
break;
/* Try to put card under cursor on one of the stacks */
case SOL_CUR2STACK:
#ifdef SOL_CUR2STACK_PRE
if( lastbutton != SOL_CUR2STACK_PRE )
break;
#endif
move_card( deck[cur_card].suit + STACKS_COL, cur_card );
break;
/* Move cards arround, Uncover cards, ... */
case SOL_MOVE:
#ifdef SOL_MOVE_PRE
if( lastbutton != SOL_MOVE_PRE )
break;
#endif
if( sel_card == NOT_A_CARD )
{
if( cur_card != NOT_A_CARD )
{
if( deck[cur_card].next == NOT_A_CARD
&& !deck[cur_card].known )
{
/* reveal a hidden card */
deck[cur_card].known = true;
}
else if( cur_col == REM_COL && cur_rem == NOT_A_CARD )
{
break;
}
else
{
/* select a card */
sel_card = cur_card;
}
}
}
else if( sel_card == cur_card )
{
/* unselect card or try putting card on
* one of the 4 stacks */
if( move_card( deck[sel_card].suit + COL_NUM, sel_card )
== MOVE_OK && cur_col == REM_COL )
{
cur_card = cur_rem;
}
sel_card = NOT_A_CARD;
}
else
{
/* try moving cards */
/* The flexible move must not be used from the remains stack. */
if (find_card_col(sel_card) == REM_COL)
{
if (move_card( cur_col, sel_card ) == MOVE_NOT_OK)
sel_card = NOT_A_CARD;
}
else
{
do {
if (move_card( cur_col, sel_card) == MOVE_OK)
break;
sel_card = find_prev_card(sel_card);
} while (sel_card != NOT_A_CARD);
}
}
break;
/* If the card on the top of the remains can be put where
* the cursor is, go ahead */
case SOL_REM2CUR:
#ifdef SOL_REM2CUR_PRE
if( lastbutton != SOL_REM2CUR_PRE )
break;
#endif
move_card( cur_col, cur_rem );
break;
/* If the card on top of the remains can be put on one
* of the stacks, do so */
case SOL_REM2STACK:
#ifdef SOL_REM2STACK_PRE
if( lastbutton != SOL_REM2STACK_PRE )
break;
#endif
move_card( deck[cur_rem].suit + COL_NUM, cur_rem );
break;
#ifdef SOL_REM
case SOL_REM:
if( sel_card != NOT_A_CARD )
{
/* unselect selected card */
sel_card = NOT_A_CARD;
break;
}
if( rem != NOT_A_CARD && cur_rem != NOT_A_CARD )
{
sel_card = cur_rem;
break;
}
break;
#endif
/* unselect selected card or ...
* draw new cards from the remains of the deck */
case SOL_DRAW:
#ifdef SOL_DRAW_PRE
if( lastbutton != SOL_DRAW_PRE )
break;
#endif
if( sel_card != NOT_A_CARD )
{
/* unselect selected card */
sel_card = NOT_A_CARD;
break;
}
if( rem != NOT_A_CARD )
{
int cur_rem_old = cur_rem;
count_rem = -1;
/* draw new cards form the remains of the deck */
if( cur_rem == NOT_A_CARD )
{
/*if the cursor card is null*/
cur_rem = rem;
i = cards_per_draw - 1;
count_rem++;
}
else
{
i = cards_per_draw;
}
while( i > 0 && deck[cur_rem].next != NOT_A_CARD )
{
cur_rem = deck[cur_rem].next;
i--;
count_rem++;
}
/* test if any cards are really left on
* the remains' stack */
if( i == cards_per_draw )
{
cur_rem = NOT_A_CARD;
count_rem = -1;
}
/* if cursor was on remains' stack when new cards were
* drawn, put cursor on top of remains' stack */
if( cur_col == REM_COL && cur_card == cur_rem_old )
{
cur_card = cur_rem;
sel_card = NOT_A_CARD;
}
}
break;
/* Show the menu */
#ifdef SOL_RC_QUIT
case SOL_RC_QUIT:
#endif
case SOL_QUIT:
switch( solitaire_menu(true) )
{
case MENU_SAVE_AND_QUIT:
return SOLITAIRE_SAVE_AND_QUIT;
case MENU_QUIT:
return SOLITAIRE_QUIT;
case MENU_USB:
return SOLITAIRE_USB;
}
break;
case SYS_POWEROFF:
return SOLITAIRE_SAVE_AND_QUIT;
default:
if( rb->default_event_handler( button ) == SYS_USB_CONNECTED )
return SOLITAIRE_USB;
break;
}
if( button != BUTTON_NONE )
lastbutton = button;
/* fix incoherences concerning cur_col and cur_card */
c = find_card_col( cur_card );
if( c != NOT_A_COL && c != cur_col )
cur_card = find_last_card( cur_col );
if( cur_card == NOT_A_CARD
&& find_last_card( cur_col ) != NOT_A_CARD )
cur_card = find_last_card( cur_col );
}
}
/**
* Plugin entry point
*/
enum plugin_status plugin_start( const struct plugin_api* api, const void* parameter )
{
int result;
/* plugin init */
(void)parameter;
rb = api;
rb->splash( HZ, "Welcome to Solitaire!" );
configfile_init(rb);
configfile_load(CONFIG_FILENAME, config,
sizeof(config) / sizeof(config[0]), CFGFILE_VERSION);
rb->memcpy(&sol, &sol_disk, sizeof(sol)); /* copy to running config */
if( load_game() == 0 )
{
rb->splash( HZ, "Resuming saved game." );
result = SOLITAIRE_QUIT;
}
else
result = SOLITAIRE_WIN;
init_help();
/* play the game :)
* Keep playing if a game was won (that means display the menu after
* winning instead of quiting) */
while( ( result = solitaire( result ) ) == SOLITAIRE_WIN );
if( result == SOLITAIRE_QUIT )
delete_save_file();
else /* result == SOLITAIRE_USB || result == SOLITAIRE_SAVE_AND_QUIT */
save_game();
if (rb->memcmp(&sol, &sol_disk, sizeof(sol))) /* save settings if changed */
{
rb->memcpy(&sol_disk, &sol, sizeof(sol));
configfile_save(CONFIG_FILENAME, config,
sizeof(config) / sizeof(config[0]), CFGFILE_VERSION);
}
/* Exit the plugin */
return ( result == SOLITAIRE_USB ) ? PLUGIN_USB_CONNECTED : PLUGIN_OK;
}
#endif