Add platform file for Ipod 1G / 2G. Now only the front image is missing for building...
[Rockbox.git] / apps / plugins / solitaire.c
blob92441baa196e2f760333dd461a044754262af967
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2004-2006 Antoine Cellerier <dionoea @t videolan d.t org>
12 * All files in this archive are subject to the GNU General Public License.
13 * See the file COPYING in the source tree root for full license agreement.
15 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16 * KIND, either express or implied.
18 ****************************************************************************/
20 /*****************************************************************************
21 Solitaire by dionoea
22 Graphics & Fix Bugs by Ben Basha
24 use arrows to move the cursor
25 use ON to select cards, move cards, reveal hidden cards, ...
26 use PLAY to move a card from the remains' stack to the top of the cursor
27 use F1 to put card under cursor on one of the 4 final stacks
28 use F2 to un-select card if a card was selected, else draw 3 new cards
29 out of the remains' stack
30 use F3 to put card on top of the remains' stack on one of the 4 final stacks
32 *****************************************************************************/
34 #include "plugin.h"
35 #include "playback_control.h"
36 #include "configfile.h"
37 #include "button.h"
38 #include "lcd.h"
39 #include "oldmenuapi.h"
41 #ifdef HAVE_LCD_BITMAP
43 PLUGIN_HEADER
45 static struct plugin_api* rb;
46 #define min(a,b) (a<b?a:b)
48 /**
49 * Key definitions
52 #if CONFIG_KEYPAD == RECORDER_PAD
53 # define SOL_QUIT BUTTON_OFF
54 # define SOL_UP BUTTON_UP
55 # define SOL_DOWN BUTTON_DOWN
56 # define SOL_LEFT BUTTON_LEFT
57 # define SOL_RIGHT BUTTON_RIGHT
58 # define SOL_MOVE BUTTON_ON
59 # define SOL_DRAW BUTTON_F1
60 # define SOL_REM2CUR BUTTON_PLAY
61 # define SOL_CUR2STACK BUTTON_F2
62 # define SOL_REM2STACK BUTTON_F3
63 # define HK_MOVE "ON"
64 # define HK_DRAW "F1"
65 # define HK_REM2CUR "PLAY"
66 # define HK_CUR2STACK "F2"
67 # define HK_REM2STACK "F3"
69 #elif CONFIG_KEYPAD == ARCHOS_AV300_PAD
70 # define SOL_QUIT BUTTON_OFF
71 # define SOL_UP BUTTON_UP
72 # define SOL_DOWN BUTTON_DOWN
73 # define SOL_LEFT BUTTON_LEFT
74 # define SOL_RIGHT BUTTON_RIGHT
75 # define SOL_MOVE BUTTON_ON
76 # define SOL_DRAW BUTTON_F1
77 # define SOL_REM2CUR BUTTON_SELECT
78 # define SOL_CUR2STACK BUTTON_F2
79 # define SOL_REM2STACK BUTTON_F3
80 # define HK_MOVE "ON"
81 # define HK_DRAW "F1"
82 # define HK_REM2CUR "SELECT"
83 # define HK_CUR2STACK "F2"
84 # define HK_REM2STACK "F3"
86 #elif CONFIG_KEYPAD == ONDIO_PAD
87 # define SOL_QUIT BUTTON_OFF
88 # define SOL_UP_PRE BUTTON_UP
89 # define SOL_UP (BUTTON_UP | BUTTON_REL)
90 # define SOL_DOWN_PRE BUTTON_DOWN
91 # define SOL_DOWN (BUTTON_DOWN | BUTTON_REL)
92 # define SOL_LEFT_PRE BUTTON_LEFT
93 # define SOL_LEFT (BUTTON_LEFT | BUTTON_REL)
94 # define SOL_RIGHT_PRE BUTTON_RIGHT
95 # define SOL_RIGHT (BUTTON_RIGHT | BUTTON_REL)
96 # define SOL_MOVE_PRE BUTTON_MENU
97 # define SOL_MOVE (BUTTON_MENU | BUTTON_REL)
98 # define SOL_DRAW_PRE BUTTON_MENU
99 # define SOL_DRAW (BUTTON_MENU | BUTTON_REPEAT)
100 # define SOL_REM2CUR_PRE BUTTON_DOWN
101 # define SOL_REM2CUR (BUTTON_DOWN | BUTTON_REPEAT)
102 # define SOL_CUR2STACK_PRE BUTTON_UP
103 # define SOL_CUR2STACK (BUTTON_UP | BUTTON_REPEAT)
104 # define SOL_REM2STACK_PRE BUTTON_RIGHT
105 # define SOL_REM2STACK (BUTTON_RIGHT | BUTTON_REPEAT)
106 # define HK_MOVE "MODE"
107 # define HK_DRAW "MODE.."
108 # define HK_REM2CUR "DOWN.."
109 # define HK_CUR2STACK "UP.."
110 # define HK_REM2STACK "RIGHT.."
112 #elif (CONFIG_KEYPAD == IRIVER_H100_PAD) || \
113 (CONFIG_KEYPAD == IRIVER_H300_PAD)
114 # define SOL_QUIT BUTTON_OFF
115 # define SOL_UP BUTTON_UP
116 # define SOL_DOWN BUTTON_DOWN
117 # define SOL_LEFT BUTTON_LEFT
118 # define SOL_RIGHT BUTTON_RIGHT
119 # define SOL_MOVE_PRE BUTTON_SELECT
120 # define SOL_MOVE (BUTTON_SELECT | BUTTON_REL)
121 # define SOL_DRAW BUTTON_MODE
122 # define SOL_REM2CUR (BUTTON_LEFT | BUTTON_ON)
123 # define SOL_CUR2STACK_PRE BUTTON_SELECT
124 # define SOL_CUR2STACK (BUTTON_SELECT | BUTTON_REPEAT)
125 # define SOL_REM2STACK (BUTTON_RIGHT | BUTTON_ON)
126 # define SOL_REM BUTTON_REC
127 # define SOL_RC_QUIT BUTTON_RC_STOP
128 # define HK_MOVE "NAVI"
129 # define HK_DRAW "A-B"
130 # define HK_REM2CUR "PLAY+LEFT"
131 # define HK_CUR2STACK "NAVI.."
132 # define HK_REM2STACK "PLAY+RIGHT"
134 #elif (CONFIG_KEYPAD == IPOD_4G_PAD) || (CONFIG_KEYPAD == IPOD_3G_PAD) || \
135 (CONFIG_KEYPAD == IPOD_1G2G_PAD)
136 # define SOL_QUIT (BUTTON_SELECT | BUTTON_MENU)
137 # define SOL_UP BUTTON_SCROLL_BACK
138 # define SOL_DOWN BUTTON_SCROLL_FWD
139 # define SOL_LEFT_PRE BUTTON_LEFT
140 # define SOL_LEFT (BUTTON_LEFT | BUTTON_REL)
141 # define SOL_RIGHT_PRE BUTTON_RIGHT
142 # define SOL_RIGHT (BUTTON_RIGHT | BUTTON_REL)
143 # define SOL_MOVE_PRE BUTTON_SELECT
144 # define SOL_MOVE (BUTTON_SELECT | BUTTON_REL)
145 # define SOL_DRAW_PRE BUTTON_MENU
146 # define SOL_DRAW (BUTTON_MENU | BUTTON_REL)
147 # define SOL_REM2CUR BUTTON_PLAY
148 # define SOL_CUR2STACK_PRE BUTTON_MENU
149 # define SOL_CUR2STACK (BUTTON_MENU | BUTTON_REPEAT)
150 # define SOL_REM2STACK_PRE BUTTON_RIGHT
151 # define SOL_REM2STACK (BUTTON_RIGHT | BUTTON_REPEAT)
152 # define HK_UD "SCROLL U/D"
153 # define HK_MOVE "SELECT"
154 # define HK_DRAW "MENU"
155 # define HK_REM2CUR "PLAY"
156 # define HK_CUR2STACK "MENU.."
157 # define HK_REM2STACK "RIGHT.."
159 #elif (CONFIG_KEYPAD == IAUDIO_X5M5_PAD)
160 # define SOL_QUIT BUTTON_POWER
161 # define SOL_UP BUTTON_UP
162 # define SOL_DOWN BUTTON_DOWN
163 # define SOL_LEFT BUTTON_LEFT
164 # define SOL_RIGHT BUTTON_RIGHT
165 # define SOL_MOVE_PRE BUTTON_SELECT
166 # define SOL_MOVE (BUTTON_SELECT | BUTTON_REL)
167 # define SOL_DRAW_PRE BUTTON_PLAY
168 # define SOL_DRAW (BUTTON_PLAY | BUTTON_REL)
169 # define SOL_REM2CUR_PRE BUTTON_PLAY
170 # define SOL_REM2CUR (BUTTON_PLAY | BUTTON_REPEAT)
171 # define SOL_CUR2STACK_PRE BUTTON_SELECT
172 # define SOL_CUR2STACK (BUTTON_SELECT | BUTTON_REPEAT)
173 # define SOL_REM2STACK BUTTON_REC
174 # define HK_MOVE "SELECT"
175 # define HK_DRAW "PLAY"
176 # define HK_REM2CUR "PLAY.."
177 # define HK_CUR2STACK "SELECT.."
178 # define HK_REM2STACK "REC"
180 #elif (CONFIG_KEYPAD == GIGABEAT_PAD)
181 # define SOL_QUIT BUTTON_POWER
182 # define SOL_UP BUTTON_UP
183 # define SOL_DOWN BUTTON_DOWN
184 # define SOL_LEFT BUTTON_LEFT
185 # define SOL_RIGHT BUTTON_RIGHT
186 # define SOL_MOVE_PRE BUTTON_SELECT
187 # define SOL_MOVE (BUTTON_SELECT | BUTTON_REL)
188 # define SOL_DRAW BUTTON_MENU
189 # define SOL_REM2CUR (BUTTON_LEFT | BUTTON_A)
190 # define SOL_CUR2STACK (BUTTON_SELECT | BUTTON_REPEAT)
191 # define SOL_REM2STACK (BUTTON_RIGHT | BUTTON_A)
192 # define HK_MOVE "SELECT"
193 # define HK_DRAW "MENU"
194 # define HK_REM2CUR "A+LEFT"
195 # define HK_CUR2STACK "SELECT.."
196 # define HK_REM2STACK "A+RIGHT"
198 #elif (CONFIG_KEYPAD == SANSA_E200_PAD)
199 # define SOL_QUIT BUTTON_POWER
200 # define SOL_UP BUTTON_UP
201 # define SOL_DOWN BUTTON_DOWN
202 # define SOL_LEFT BUTTON_SCROLL_UP
203 # define SOL_RIGHT BUTTON_SCROLL_DOWN
204 # define SOL_MOVE BUTTON_SELECT
205 # define SOL_DRAW BUTTON_REC
206 # define SOL_REM2CUR BUTTON_LEFT
207 # define SOL_CUR2STACK_PRE BUTTON_REC
208 # define SOL_CUR2STACK (BUTTON_REC | BUTTON_RIGHT)
209 # define SOL_REM2STACK BUTTON_RIGHT
210 # define HK_MOVE "SELECT"
211 # define HK_DRAW "REC"
212 # define HK_REM2CUR "LEFT"
213 # define HK_CUR2STACK "DOUBLE SELECT"
214 # define HK_REM2STACK "RIGHT"
216 #elif (CONFIG_KEYPAD == IRIVER_H10_PAD)
217 # define SOL_QUIT BUTTON_POWER
218 # define SOL_UP BUTTON_SCROLL_UP
219 # define SOL_DOWN BUTTON_SCROLL_DOWN
220 # define SOL_LEFT_PRE BUTTON_LEFT
221 # define SOL_LEFT (BUTTON_LEFT | BUTTON_REL)
222 # define SOL_RIGHT_PRE BUTTON_RIGHT
223 # define SOL_RIGHT (BUTTON_RIGHT | BUTTON_REL)
224 # define SOL_MOVE BUTTON_PLAY
225 # define SOL_DRAW_PRE BUTTON_LEFT
226 # define SOL_DRAW (BUTTON_LEFT | BUTTON_REPEAT)
227 # define SOL_REM2CUR BUTTON_FF
228 # define SOL_CUR2STACK BUTTON_REW
229 # define SOL_REM2STACK_PRE BUTTON_RIGHT
230 # define SOL_REM2STACK (BUTTON_RIGHT | BUTTON_REPEAT)
231 # define HK_MOVE "PLAY"
232 # define HK_DRAW "LEFT.."
233 # define HK_REM2CUR "FF"
234 # define HK_CUR2STACK "REW"
235 # define HK_REM2STACK "RIGHT.."
237 #else
238 # error "Unknown keypad"
239 #endif
241 #define HK_LR "LEFT/RIGHT"
242 #ifndef HK_UD
243 # define HK_UD "UP/DOWN"
244 #endif
247 * Help strings
250 static int helplines;
251 static int displaylines;
253 static char helptext[] =
254 /* Use single spaces only! Close each line with one \0. */
255 "-- Navigation --\0"
256 HK_LR ": Move the cursor to the previous/ next column.\0"
257 HK_UD ": Move the cursor up/ down in the column.\0"
258 HK_MOVE ": Select cards, move cards, reveal hidden cards...\0"
259 HK_DRAW ": Deselect a card if it was selected. Else draw new card(s) from the remains stack.\0"
260 "-- Shortcuts --\0"
261 HK_REM2CUR ": Put the card on top of the remains stack on top of the cursor.\0"
262 HK_CUR2STACK ": Put the card under the cursor on one of the 4 final stacks.\0"
263 HK_REM2STACK ": Put the card on top of the remains stack on one of the 4 final stacks.\0"
267 * Misc constants, graphics and other defines
270 #include "card_back.h"
271 #include "card_deck.h"
272 #include "solitaire_suitsi.h"
274 #define CARD_GFX_WIDTH BMPWIDTH_card_back
275 #define CARD_GFX_HEIGHT BMPHEIGHT_card_back
276 #define CARD_WIDTH (BMPWIDTH_card_back+2)
277 #define CARD_HEIGHT (BMPHEIGHT_card_back+2)
279 #if LCD_WIDTH >= 320
280 # define MARGIN 4
281 # define LARGE_CARD
282 # define SYMBOL_HEIGHT 12
283 #elif LCD_WIDTH >= 220
284 # define MARGIN 3
285 # define LARGE_CARD
286 # define SYMBOL_HEIGHT 12
287 #elif LCD_WIDTH >= 160
288 # define MARGIN 2
289 # define SYMBOL_HEIGHT 11
290 #elif LCD_WIDTH >= 128
291 # define MARGIN 1
292 # define SYMBOL_HEIGHT 10
293 #else
294 # define MARGIN 0
295 # define SYMBOL_HEIGHT 8
296 #endif
298 #define CARD_START (CARD_HEIGHT+2*MARGIN+1)
300 /* background color */
301 #ifdef HAVE_LCD_COLOR
302 # define BACKGROUND_COLOR LCD_RGBPACK(0,157,0)
303 # define FRAME_COLOR LCD_RGBPACK(23,119,218)
304 #elif LCD_DEPTH > 1
305 # define BACKGROUND_COLOR LCD_WHITE
306 # define FRAME_COLOR LCD_BLACK
307 #endif
310 #define CONFIG_FILENAME "sol.cfg"
312 #define NOT_A_CARD -1
314 /* number of cards per suit */
315 #define CARDS_PER_SUIT 13
317 /* number of suits */
318 #define SUITS 4
320 #define NUM_CARDS ( CARDS_PER_SUIT * SUITS )
322 /* number of columns */
323 #define COL_NUM 7
325 /* pseudo column numbers to be used for cursor coordinates */
326 /* columns COL_NUM to COL_NUM + SUITS - 1 correspond to the final stacks */
327 #define STACKS_COL COL_NUM
328 /* column COL_NUM + SUITS corresponds to the remains' stack */
329 #define REM_COL (STACKS_COL + SUITS)
331 #define NOT_A_COL -1
333 typedef struct
335 signed char suit;
336 signed char num;
337 bool known : 1;
338 bool used : 1; /* this is what is used when dealing cards */
339 signed char next;
340 } card_t;
344 * LCD card drawing routines
347 static void draw_cursor( int x, int y )
349 rb->lcd_set_drawmode( DRMODE_COMPLEMENT );
350 rb->lcd_fillrect( x+1, y+1, CARD_GFX_WIDTH, CARD_GFX_HEIGHT );
351 #ifdef LARGE_CARD
352 rb->lcd_drawpixel( x+1, y+1 );
353 rb->lcd_drawpixel( x+1, y+CARD_HEIGHT-2 );
354 rb->lcd_drawpixel( x+CARD_WIDTH-2, y+1 );
355 rb->lcd_drawpixel( x+CARD_WIDTH-2, y+CARD_HEIGHT-2 );
356 #endif
357 rb->lcd_set_drawmode( DRMODE_SOLID );
360 /* Draw a card's border, select it if it's selected and draw the cursor
361 * if the cursor is currently over the card */
362 static void draw_card_ext( int x, int y, bool selected, bool cursor )
364 #if LCD_DEPTH > 1
365 int oldfg = rb->lcd_get_foreground();
367 rb->lcd_set_foreground( LCD_BLACK );
368 #endif
369 #ifdef LARGE_CARD
370 rb->lcd_hline( x+2, x+CARD_WIDTH-3, y );
371 rb->lcd_hline( x+2, x+CARD_WIDTH-3, y+CARD_HEIGHT-1 );
372 rb->lcd_vline( x, y+2, y+CARD_HEIGHT-3 );
373 rb->lcd_vline( x+CARD_WIDTH-1, y+2, y+CARD_HEIGHT-3 );
374 rb->lcd_drawpixel( x+1, y+1 );
375 rb->lcd_drawpixel( x+1, y+CARD_HEIGHT-2 );
376 rb->lcd_drawpixel( x+CARD_WIDTH-2, y+1 );
377 rb->lcd_drawpixel( x+CARD_WIDTH-2, y+CARD_HEIGHT-2 );
378 #else
379 rb->lcd_hline( x+1, x+CARD_WIDTH-2, y );
380 rb->lcd_hline( x+1, x+CARD_WIDTH-2, y+CARD_HEIGHT-1 );
381 rb->lcd_vline( x, y+1, y+CARD_HEIGHT-2 );
382 rb->lcd_vline( x+CARD_WIDTH-1, y+1, y+CARD_HEIGHT-2 );
383 #endif
385 if( selected )
387 #if LCD_DEPTH > 1
388 rb->lcd_set_foreground( FRAME_COLOR );
389 #endif
390 rb->lcd_drawrect( x+1, y+1, CARD_WIDTH-2, CARD_HEIGHT-2 );
391 #ifdef LARGE_CARD
392 rb->lcd_drawrect( x+2, y+2, CARD_WIDTH-4, CARD_HEIGHT-4 );
393 #endif
395 #if LCD_DEPTH > 1
396 rb->lcd_set_foreground( oldfg );
397 #endif
399 if( cursor )
401 draw_cursor( x, y );
405 /* Draw a card's inner graphics */
406 static void draw_card( card_t *card, int x, int y,
407 bool selected, bool cursor )
409 if( card->known )
411 rb->lcd_bitmap_part( card_deck, CARD_GFX_WIDTH * card->num,
412 CARD_GFX_HEIGHT * card->suit, BMPWIDTH_card_deck,
413 x+1, y+1, CARD_GFX_WIDTH, CARD_GFX_HEIGHT );
415 else
417 rb->lcd_bitmap( card_back, x+1, y+1,
418 CARD_GFX_WIDTH, CARD_GFX_HEIGHT );
420 draw_card_ext( x, y, selected, cursor );
423 /* Draw an empty stack */
424 static void draw_empty_stack( int s, int x, int y, bool cursor )
426 rb->lcd_bitmap_part( solitaire_suitsi, 0,
427 CARD_GFX_HEIGHT * s, BMPWIDTH_solitaire_suitsi,
428 x+1, y+1, CARD_GFX_WIDTH, CARD_GFX_HEIGHT );
430 draw_card_ext( x, y, false, cursor );
434 * Help
437 static void init_help(void)
439 int lines = 0;
440 int w_space, w, h;
441 int para_len;
442 char *para = helptext;
444 rb->lcd_getstringsize(" ", &w_space, &h);
445 displaylines = LCD_HEIGHT / h;
446 para_len = rb->strlen(para);
448 while (para_len)
450 bool first = true;
451 int x = 0;
452 char *next, *store;
454 next = rb->strtok_r(para, " ", &store);
455 while (next)
457 rb->lcd_getstringsize(next, &w, NULL);
458 if (!first)
460 if (x + w > LCD_WIDTH)
462 lines++;
463 x = 0;
465 else
466 next[-1] = ' '; /* re-concatenate string */
468 else
469 first = false;
471 x += w + w_space;
472 next = rb->strtok_r(NULL, " ", &store);
475 lines++;
476 para += para_len + 1;
477 para_len = rb->strlen(para);
479 helplines = lines;
482 enum help { HELP_QUIT, HELP_USB };
484 /* help for the not so intuitive interface */
485 enum help solitaire_help( void )
487 int start = 0;
488 int button;
489 int lastbutton = BUTTON_NONE;
490 bool fixed = (displaylines >= helplines);
492 while( true )
494 char *text = helptext;
495 int line = fixed ? (displaylines - helplines) / 2 : 0;
496 int i;
498 rb->lcd_clear_display();
500 for (i = 0; i < start + displaylines; i++)
502 if (i >= start)
503 rb->lcd_puts(0, line++, text);
504 text += rb->strlen(text) + 1;
506 rb->lcd_update();
508 button = rb->button_get( true );
509 switch( button )
511 case SOL_UP:
512 #ifdef SOL_UP_PRE
513 if( lastbutton != SOL_UP_PRE )
514 break;
515 #else
516 case SOL_UP|BUTTON_REPEAT:
517 #endif
518 if (!fixed && start > 0)
519 start--;
520 break;
522 case SOL_DOWN:
523 #ifdef SOL_DOWN_PRE
524 if( lastbutton != SOL_DOWN_PRE )
525 break;
526 #else
527 case SOL_DOWN|BUTTON_REPEAT:
528 #endif
529 if (!fixed && start < helplines - displaylines)
530 start++;
531 break;
533 #ifdef SOL_RC_QUIT
534 case SOL_RC_QUIT:
535 #endif
536 case SOL_QUIT:
537 return HELP_QUIT;
539 default:
540 if( rb->default_event_handler( button ) == SYS_USB_CONNECTED )
541 return HELP_USB;
542 break;
544 if( button != BUTTON_NONE )
545 lastbutton = button;
550 * Custom menu / options
553 #define CFGFILE_VERSION 0
555 struct sol_config {
556 int draw_type;
557 int auto_unhide;
560 struct sol_config sol_disk = {0, 0};
561 struct sol_config sol;
563 static struct configdata config[] = {
564 { TYPE_INT, 0, 1, &sol_disk.draw_type, "draw_type", NULL, NULL },
565 { TYPE_INT, 0, 1, &sol_disk.auto_unhide, "auto_unhide", NULL, NULL }
568 char draw_option_string[32];
569 char unhide_option_string[32];
571 static void create_draw_option_string(void)
573 if (sol.draw_type == 0)
574 rb->strcpy(draw_option_string, "Draw Three Cards");
575 else
576 rb->strcpy(draw_option_string, "Draw One Card");
579 static void create_unhide_option_string(void)
581 if (sol.auto_unhide == 0)
582 rb->strcpy(unhide_option_string, "Unhide manually");
583 else
584 rb->strcpy(unhide_option_string, "Unhide automatically");
587 void solitaire_init(void);
589 /* menu return codes */
590 enum { MENU_RESUME, MENU_QUIT, MENU_USB };
592 int solitaire_menu(bool in_game)
594 int m;
595 int result = -1;
596 int i = 0;
598 struct menu_item items[6];
600 if (in_game)
602 items[i++].desc = "Resume Game";
603 items[i++].desc = "Restart Game";
605 else
607 items[i++].desc = "Start Game";
608 items[i++].desc = draw_option_string;
610 items[i++].desc = unhide_option_string;
611 items[i++].desc = "Help";
612 items[i++].desc = "Audio Playback";
613 items[i++].desc = "Quit";
615 create_draw_option_string();
616 create_unhide_option_string();
617 m = menu_init(rb, items, i, NULL, NULL, NULL, NULL);
618 while (result < 0)
620 switch (menu_show(m))
622 case MENU_SELECTED_EXIT:
623 result = MENU_RESUME;
624 break;
626 case MENU_ATTACHED_USB:
627 result = MENU_USB;
628 break;
630 case 0:
631 result = MENU_RESUME;
632 break;
634 case 1:
635 if (in_game)
637 solitaire_init();
638 result = MENU_RESUME;
640 else
642 sol.draw_type = (sol.draw_type + 1) % 2;
643 create_draw_option_string();
645 break;
647 case 2:
648 sol.auto_unhide = (sol.auto_unhide + 1) % 2;
649 create_unhide_option_string();
650 break;
652 case 3:
653 rb->lcd_setmargins(0, 0);
654 if (solitaire_help() == HELP_USB)
655 result = MENU_USB;
656 break;
658 case 4:
659 playback_control(rb);
660 break;
662 case 5:
663 result = MENU_QUIT;
664 break;
667 menu_exit(m);
668 rb->lcd_setmargins(0, 0);
669 return result;
673 * Global variables
676 /* player's cursor */
677 int cur_card;
678 /* player's cursor column num */
679 int cur_col;
681 /* selected card */
682 int sel_card;
684 /* the deck */
685 card_t deck[ NUM_CARDS ];
687 /* the remaining cards */
688 /* first card of the remains' stack */
689 int rem;
690 /* upper visible card from the remains' stack */
691 int cur_rem;
692 /* number of cards drawn from the remains stack - 1 */
693 int count_rem;
694 /* number of cards per draw of the remains' stack */
695 int cards_per_draw;
697 /* the 7 game columns */
698 int cols[COL_NUM];
699 /* the 4 final stacks */
700 int stacks[SUITS];
703 * Card handling routines
706 int next_random_card( card_t *deck )
708 int i,r;
710 r = rb->rand()%(NUM_CARDS)+1;
711 i = 0;
713 while( r>0 )
715 i = (i + 1)%(NUM_CARDS);
716 if( !deck[i].used ) r--;
719 deck[i].used = true;
721 return i;
725 /* initialize the game */
726 void solitaire_init( void )
729 int c;
730 int i, j;
732 /* number of cards that are drawn on the remains' stack (by pressing F2) */
733 if( sol.draw_type == 0 )
735 cards_per_draw = 3;
737 else
739 cards_per_draw = 1;
742 /* init deck */
743 for( i=0; i<SUITS; i++ )
745 for( j=0; j<CARDS_PER_SUIT; j++ )
747 #define card deck[i*CARDS_PER_SUIT+j]
748 card.suit = i;
749 card.num = j;
750 card.known = true;
751 card.used = false;
752 card.next = NOT_A_CARD;
753 #undef card
757 /* deal the cards ... */
758 /* ... in the columns */
759 for( i=0; i<COL_NUM; i++ )
761 c = NOT_A_CARD;
762 for( j=0; j<=i; j++ )
764 if( c == NOT_A_CARD )
766 cols[i] = next_random_card( deck );
767 c = cols[i];
769 else
771 deck[c].next = next_random_card( deck );
772 c = deck[c].next;
774 if( j < i )
775 deck[c].known = false;
779 /* ... shuffle what's left of the deck */
780 rem = next_random_card(deck);
781 c = rem;
783 for( i=1; i < NUM_CARDS - COL_NUM * (COL_NUM + 1)/2; i++ )
785 deck[c].next = next_random_card( deck );
786 c = deck[c].next;
789 /* we now finished dealing the cards. The game can start ! (at last) */
791 /* init the stack */
792 for( i = 0; i<SUITS; i++ )
794 stacks[i] = NOT_A_CARD;
797 /* the cursor starts on upper left card */
798 cur_card = cols[0];
799 cur_col = 0;
801 /* no card is selected */
802 sel_card = NOT_A_CARD;
804 /* init the remainder */
805 cur_rem = NOT_A_CARD;
807 count_rem = -1;
810 /* find the column number in which 'card' can be found */
811 int find_card_col( int card )
813 int i;
814 int c;
816 if( card == NOT_A_CARD ) return NOT_A_COL;
818 for( i=0; i<COL_NUM; i++ )
820 c = cols[i];
821 while( c != NOT_A_CARD )
823 if( c == card ) return i;
824 c = deck[c].next;
828 for( i=0; i<SUITS; i++ )
830 c = stacks[i];
831 while( c != NOT_A_CARD )
833 if( c == card ) return STACKS_COL + i;
834 c = deck[c].next;
838 return REM_COL;
841 /* find the card preceding 'card' */
842 /* if it doesn't exist, return NOT_A_CARD */
843 int find_prev_card( int card ){
844 int i;
846 for( i=0; i < NUM_CARDS; i++ )
848 if( deck[i].next == card ) return i;
851 return NOT_A_CARD;
854 /* find the last card of a given column */
855 int find_last_card( int col )
857 int c;
859 if( col < COL_NUM )
861 c = cols[col];
863 else if( col < REM_COL )
865 c = stacks[col - STACKS_COL];
867 else
869 c = cur_rem;
872 if(c == NOT_A_CARD)
873 return c;
874 else
876 while(deck[c].next != NOT_A_CARD)
877 c = deck[c].next;
878 return c;
882 enum move { MOVE_OK, MOVE_NOT_OK };
884 enum move move_card( int dest_col, int src_card )
886 /* the column on which to take src_card */
887 int src_col;
889 /* the last card of dest_col */
890 int dest_card;
892 /* the card under src_card */
893 int src_card_prev;
895 /* you can't move no card (at least, it doesn't have any consequence) */
896 if( src_card == NOT_A_CARD ) return MOVE_NOT_OK;
897 /* you can't put a card back on the remains' stack */
898 if( dest_col == REM_COL ) return MOVE_NOT_OK;
899 /* you can't move an unknown card */
900 if( !deck[src_card].known ) return MOVE_NOT_OK;
902 src_col = find_card_col( src_card );
903 dest_card = find_last_card( dest_col );
904 src_card_prev = find_prev_card( src_card );
906 /* you can't move more than one card at a time from the final stack */
907 /* to the rest of the game */
908 if( src_col >= COL_NUM && src_col < REM_COL
909 && deck[src_card].next != NOT_A_CARD )
911 return MOVE_NOT_OK;
914 /* if we (that means dest) are on one of the 7 columns ... */
915 if( dest_col < COL_NUM )
917 /* ... check is we are on an empty color and that the src is a king */
918 if( dest_card == NOT_A_CARD
919 && deck[src_card].num == CARDS_PER_SUIT - 1 )
921 /* this is a winning combination */
922 cols[dest_col] = src_card;
924 /* ... or check if the cards follow one another and have
925 * different colorsuit */
926 else if(( deck[dest_card].suit + deck[src_card].suit)%2==1
927 && deck[dest_card].num == deck[src_card].num + 1 )
929 /* this is a winning combination */
930 deck[dest_card].next = src_card;
932 /* ... or, humpf, well that's not good news */
933 else
935 /* this is not a winning combination */
936 return MOVE_NOT_OK;
939 /* if we are on one of the 4 final stacks ... */
940 else if( dest_col < REM_COL )
942 /* ... check if we are on an empty stack... */
943 if( dest_card == NOT_A_CARD )
945 /* ... and the src is an ace and this is the correct final stack */
946 if( deck[src_card].num == 0
947 && deck[src_card].suit == dest_col - STACKS_COL )
949 /* this is a winning combination */
950 stacks[dest_col - STACKS_COL] = src_card;
952 else
954 /* this is not a winning combination */
955 return MOVE_NOT_OK;
958 else /* non-empty stack */
960 /* ...check if the cards follow one another, have the same suit and
961 * {that src has no .next element or is from the remains' stack} */
962 if( deck[dest_card].suit == deck[src_card].suit
963 && deck[dest_card].num + 1 == deck[src_card].num
964 && (deck[src_card].next == NOT_A_CARD || src_col == REM_COL) )
966 /* this is a winning combination */
967 deck[dest_card].next = src_card;
969 /* ... or, well that's not good news */
970 else
972 /* this is not a winning combination */
973 return MOVE_NOT_OK;
977 /* if we are on the remains' stack */
978 else
980 /* you can't move a card back to the remains' stack */
981 return MOVE_NOT_OK;
984 /* if the src card is from the remains' stack, we don't want to take
985 * the following cards */
986 if( src_col == REM_COL )
988 /* if src card is the first card from the stack */
989 if( src_card_prev == NOT_A_CARD )
991 rem = deck[src_card].next;
993 /* if src card is not the first card from the stack */
994 else
996 deck[src_card_prev].next = deck[src_card].next;
998 deck[src_card].next = NOT_A_CARD;
999 cur_rem = src_card_prev;
1000 count_rem--;
1002 /* if the src card is from somewhere else, just take everything */
1003 else
1005 if( src_card_prev == NOT_A_CARD )
1007 if( src_col < COL_NUM )
1009 cols[src_col] = NOT_A_CARD;
1011 else
1013 stacks[src_col - STACKS_COL] = NOT_A_CARD;
1016 else
1018 deck[src_card_prev].next = NOT_A_CARD;
1019 if (sol.auto_unhide)
1021 deck[src_card_prev].known = true;
1025 sel_card = NOT_A_CARD;
1026 /* tada ! */
1027 return MOVE_OK;
1030 enum { SOLITAIRE_WIN, SOLITAIRE_QUIT, SOLITAIRE_USB };
1033 * Bouncing cards at the end of the game
1036 #define BC_ACCEL ((1<<16)*LCD_HEIGHT/128)
1037 #define BC_MYSPEED (6*BC_ACCEL)
1038 #define BC_MXSPEED (6*LCD_HEIGHT/128)
1040 int bouncing_cards( void )
1042 int i, j, x, vx, y, fp_y, fp_vy, button;
1044 /* flush the button queue */
1045 while( ( button = rb->button_get( false ) ) != BUTTON_NONE )
1047 if( rb->default_event_handler( button ) == SYS_USB_CONNECTED )
1048 return SOLITAIRE_USB;
1051 /* fun stuff :) */
1052 for( i = CARDS_PER_SUIT-1; i>=0; i-- )
1054 for( j = 0; j < SUITS; j++ )
1056 x = LCD_WIDTH-(CARD_WIDTH*4+4+MARGIN)+CARD_WIDTH*j+j+1;
1057 fp_y = MARGIN<<16;
1059 #if LCD_WIDTH > 200
1060 vx = rb->rand() % (4*BC_MXSPEED/3-2) - BC_MXSPEED;
1061 if( vx >= -1 )
1062 vx += 3;
1063 #else
1064 vx = rb->rand() % (4*BC_MXSPEED/3) - BC_MXSPEED;
1065 if( vx >= 0 )
1066 vx++;
1067 #endif
1069 fp_vy = -rb->rand() % BC_MYSPEED;
1071 while( x < LCD_WIDTH && x + CARD_WIDTH > 0 )
1073 fp_vy += BC_ACCEL;
1074 x += vx;
1075 fp_y += fp_vy;
1076 if( fp_y >= (LCD_HEIGHT-CARD_HEIGHT) << 16 )
1078 fp_vy = -fp_vy*4/5;
1079 fp_y = (LCD_HEIGHT-CARD_HEIGHT) << 16;
1081 y = fp_y >> 16;
1082 draw_card( &deck[j*CARDS_PER_SUIT+i], x, y,
1083 false, false );
1084 rb->lcd_update_rect( x<0?0:x, y<0?0:y,
1085 CARD_WIDTH, CARD_HEIGHT );
1087 button = rb->button_get_w_tmo( 2 );
1088 if( rb->default_event_handler( button ) == SYS_USB_CONNECTED )
1089 return SOLITAIRE_USB;
1090 if( button == SOL_QUIT || button == SOL_MOVE )
1091 return SOLITAIRE_WIN;
1095 return SOLITAIRE_WIN;
1099 * The main game loop
1102 int solitaire( void )
1105 int i,j;
1106 int button, lastbutton = 0;
1107 int c,h,prevcard;
1108 int biggest_col_length;
1110 rb->srand( *rb->current_tick );
1111 switch( solitaire_menu(false) )
1113 case MENU_QUIT:
1114 return SOLITAIRE_QUIT;
1116 case MENU_USB:
1117 return SOLITAIRE_USB;
1119 solitaire_init();
1121 while( true )
1123 rb->lcd_clear_display();
1125 /* get the biggest column length so that display can be "optimized" */
1126 biggest_col_length = 0;
1128 for(i=0;i<COL_NUM;i++)
1130 j = 0;
1131 c = cols[i];
1133 if( c != NOT_A_CARD )
1135 while( true )
1137 /* don't include the last card in the column length. */
1138 if( deck[c].next == NOT_A_CARD )
1140 break; /* no successor: get outta here. */
1142 else
1144 if( deck[c].known )
1145 j += 2;
1146 else
1147 j++;
1149 c = deck[c].next;
1151 /* make column distinguishable from an empty column,
1152 * and avoid division by zero while displaying */
1153 if( j == 0 )
1154 j = 1;
1156 if( j > biggest_col_length )
1157 biggest_col_length = j;
1160 /* check if there are cards remaining in the game. */
1161 /* if there aren't any, that means you won :) */
1162 if( biggest_col_length == 0 && rem == NOT_A_CARD )
1164 rb->splash( HZ, "You Won :)" );
1165 return bouncing_cards();
1168 /* draw the columns */
1169 for( i = 0; i < COL_NUM; i++ )
1171 c = cols[i];
1172 j = CARD_START;
1173 while( true )
1175 if( c == NOT_A_CARD )
1177 /* draw the cursor on empty columns */
1178 if( cur_col == i )
1180 draw_cursor( MARGIN + i * (CARD_WIDTH
1181 +(LCD_WIDTH-COL_NUM*CARD_WIDTH-2*MARGIN)/(COL_NUM-1)),
1182 j );
1184 break;
1187 draw_card( &deck[c], MARGIN + i * (CARD_WIDTH
1188 +(LCD_WIDTH-COL_NUM*CARD_WIDTH-2*MARGIN)/(COL_NUM-1)),
1189 j, c == sel_card, c == cur_card );
1191 h = c;
1192 c = deck[c].next;
1193 if( c == NOT_A_CARD ) break;
1195 /* This is where we change the spacing between cards so that
1196 * they don't overflow out of the LCD */
1197 if( h == cur_card )
1198 j += SYMBOL_HEIGHT;
1199 else if( deck[h].known )
1200 j += min( SYMBOL_HEIGHT,
1201 2 * (LCD_HEIGHT - CARD_START - CARD_HEIGHT - MARGIN)
1202 / biggest_col_length );
1203 else
1204 j += min( SYMBOL_HEIGHT / 2,
1205 (LCD_HEIGHT - CARD_START - CARD_HEIGHT - MARGIN)
1206 / biggest_col_length );
1210 /* draw the stacks */
1211 for( i=0; i<SUITS; i++ )
1213 c = find_last_card( STACKS_COL + i );
1215 if( c != NOT_A_CARD )
1217 draw_card( &deck[c],
1218 LCD_WIDTH-(CARD_WIDTH*4+4+MARGIN)+CARD_WIDTH*i+i+1,
1219 MARGIN,
1220 c == sel_card, cur_col == STACKS_COL + i );
1222 else
1224 draw_empty_stack( i,
1225 LCD_WIDTH-(CARD_WIDTH*4+4+MARGIN)+CARD_WIDTH*i+i+1,
1226 MARGIN, cur_col == STACKS_COL + i );
1230 /* draw the remains */
1231 if( rem != NOT_A_CARD &&
1232 ( cur_rem == NOT_A_CARD || deck[cur_rem].next != NOT_A_CARD ) )
1234 /* gruik ! (we want to display a card back) */
1235 deck[rem].known = false;
1236 draw_card( &deck[rem], MARGIN, MARGIN, false, false );
1237 deck[rem].known = true;
1240 if( rem != NOT_A_CARD && cur_rem != NOT_A_CARD )
1242 if( count_rem < 0 )
1244 prevcard = rem;
1245 count_rem = 0;
1246 while( prevcard != cur_rem && count_rem < cards_per_draw-1 )
1248 prevcard = deck[prevcard].next;
1249 count_rem++;
1252 prevcard = cur_rem;
1253 j = CARD_WIDTH+2*MARGIN+1;
1254 for( i = 0; i < count_rem; i++ )
1255 prevcard = find_prev_card(prevcard);
1256 for( i = 0; i <= count_rem; i++ )
1258 draw_card( &deck[prevcard], j,
1259 MARGIN, sel_card == prevcard,
1260 cur_card == prevcard );
1261 prevcard = deck[prevcard].next;
1262 j += CARD_WIDTH/2;
1265 if( ( cur_rem == NOT_A_CARD || rem == NOT_A_CARD )
1266 && cur_col == REM_COL )
1268 draw_cursor( CARD_WIDTH+2*MARGIN+1, MARGIN );
1271 rb->lcd_update();
1273 /* what to do when a key is pressed ... */
1274 button = rb->button_get( true );
1275 #if (CONFIG_KEYPAD == SANSA_E200_PAD)
1276 if (button&(BUTTON_SCROLL_UP|BUTTON_SCROLL_DOWN))
1277 button = button & (~BUTTON_REPEAT);
1278 #endif
1279 switch( button )
1281 /* move cursor to the last card of the previous column
1282 * or to the previous final stack
1283 * or to the remains stack */
1284 case SOL_RIGHT:
1285 #ifdef SOL_RIGHT_PRE
1286 if( lastbutton != SOL_RIGHT_PRE )
1287 break;
1288 #endif
1289 if( cur_col >= COL_NUM )
1291 cur_col = 0;
1293 else if( cur_col == COL_NUM - 1 )
1295 cur_col = REM_COL;
1297 else
1299 cur_col = (cur_col+1)%(REM_COL+1);
1301 if(cur_col == REM_COL)
1303 cur_card = cur_rem;
1304 break;
1306 cur_card = find_last_card( cur_col );
1307 break;
1309 /* move cursor to the last card of the next column
1310 * or to the next final stack
1311 * or to the remains stack */
1312 case SOL_LEFT:
1313 #ifdef SOL_LEFT_PRE
1314 if( lastbutton != SOL_LEFT_PRE )
1315 break;
1316 #endif
1317 if( cur_col == 0 )
1319 cur_col = REM_COL;
1321 else if( cur_col >= COL_NUM )
1323 cur_col = COL_NUM - 1;
1325 else
1327 cur_col = (cur_col + REM_COL)%(REM_COL+1);
1329 if( cur_col == REM_COL )
1331 cur_card = cur_rem;
1332 break;
1334 cur_card = find_last_card( cur_col );
1335 break;
1337 /* move cursor to card that's bellow */
1338 case SOL_DOWN:
1339 #ifdef SOL_DOWN_PRE
1340 if( lastbutton != SOL_DOWN_PRE )
1341 break;
1342 #else
1343 case SOL_DOWN|BUTTON_REPEAT:
1344 #endif
1345 if( cur_col >= COL_NUM )
1347 cur_col = (cur_col - COL_NUM + 1)%(SUITS + 1) + COL_NUM;
1348 if( cur_col == REM_COL )
1350 cur_card = cur_rem;
1352 else
1354 cur_card = find_last_card( cur_col );
1356 break;
1358 if( cur_card == NOT_A_CARD ) break;
1359 if( deck[cur_card].next != NOT_A_CARD )
1361 cur_card = deck[cur_card].next;
1363 else
1365 cur_card = cols[cur_col];
1366 while( !deck[ cur_card].known
1367 && deck[cur_card].next != NOT_A_CARD )
1369 cur_card = deck[cur_card].next;
1372 break;
1374 /* move cursor to card that's above */
1375 case SOL_UP:
1376 #ifdef SOL_UP_PRE
1377 if( lastbutton != SOL_UP_PRE )
1378 break;
1379 #else
1380 case SOL_UP|BUTTON_REPEAT:
1381 #endif
1382 if( cur_col >= COL_NUM )
1384 cur_col = (cur_col - COL_NUM + SUITS)%(SUITS + 1) + COL_NUM;
1385 if( cur_col == REM_COL )
1387 cur_card = cur_rem;
1389 else
1391 cur_card = find_last_card( cur_col );
1393 break;
1395 if( cur_card == NOT_A_CARD ) break;
1396 do {
1397 cur_card = find_prev_card( cur_card );
1398 if( cur_card == NOT_A_CARD )
1400 cur_card = find_last_card( cur_col );
1402 } while( deck[cur_card].next != NOT_A_CARD
1403 && !deck[cur_card].known );
1404 break;
1406 /* Try to put card under cursor on one of the stacks */
1407 case SOL_CUR2STACK:
1408 #ifdef SOL_CUR2STACK_PRE
1409 if( lastbutton != SOL_CUR2STACK_PRE )
1410 break;
1411 #endif
1412 move_card( deck[cur_card].suit + STACKS_COL, cur_card );
1413 break;
1415 /* Move cards arround, Uncover cards, ... */
1416 case SOL_MOVE:
1417 #ifdef SOL_MOVE_PRE
1418 if( lastbutton != SOL_MOVE_PRE )
1419 break;
1420 #endif
1422 if( sel_card == NOT_A_CARD )
1424 if( cur_card != NOT_A_CARD )
1426 if( deck[cur_card].next == NOT_A_CARD
1427 && !deck[cur_card].known )
1429 /* reveal a hidden card */
1430 deck[cur_card].known = true;
1432 else if( cur_col == REM_COL && cur_rem == NOT_A_CARD )
1434 break;
1436 else
1438 /* select a card */
1439 sel_card = cur_card;
1443 else if( sel_card == cur_card )
1445 /* unselect card or try putting card on
1446 * one of the 4 stacks */
1447 if( move_card( deck[sel_card].suit + COL_NUM, sel_card )
1448 == MOVE_OK && cur_col == REM_COL )
1450 cur_card = cur_rem;
1452 sel_card = NOT_A_CARD;
1454 else
1456 /* try moving cards */
1457 /* the code in the else seems to not like moveing kings
1458 so if the selected card is a king do it the simple way */
1459 if (deck[sel_card].num == CARDS_PER_SUIT - 1)
1461 if (move_card( cur_col, sel_card ) == MOVE_NOT_OK)
1462 sel_card = NOT_A_CARD;
1464 else
1466 int retval;
1467 do {
1468 retval = move_card( cur_col, sel_card );
1469 if (retval == MOVE_NOT_OK)
1471 sel_card = find_prev_card(sel_card);
1473 } while ((retval == MOVE_NOT_OK) && (sel_card != NOT_A_CARD));
1476 break;
1478 /* If the card on the top of the remains can be put where
1479 * the cursor is, go ahead */
1480 case SOL_REM2CUR:
1481 #ifdef SOL_REM2CUR_PRE
1482 if( lastbutton != SOL_REM2CUR_PRE )
1483 break;
1484 #endif
1485 move_card( cur_col, cur_rem );
1486 break;
1488 /* If the card on top of the remains can be put on one
1489 * of the stacks, do so */
1490 case SOL_REM2STACK:
1491 #ifdef SOL_REM2STACK_PRE
1492 if( lastbutton != SOL_REM2STACK_PRE )
1493 break;
1494 #endif
1495 move_card( deck[cur_rem].suit + COL_NUM, cur_rem );
1496 break;
1498 #ifdef SOL_REM
1499 case SOL_REM:
1500 if( sel_card != NOT_A_CARD )
1502 /* unselect selected card */
1503 sel_card = NOT_A_CARD;
1504 break;
1506 if( rem != NOT_A_CARD && cur_rem != NOT_A_CARD )
1508 sel_card = cur_rem;
1509 break;
1511 break;
1512 #endif
1514 /* unselect selected card or ...
1515 * draw new cards from the remains of the deck */
1516 case SOL_DRAW:
1517 #ifdef SOL_DRAW_PRE
1518 if( lastbutton != SOL_DRAW_PRE )
1519 break;
1520 #endif
1521 if( sel_card != NOT_A_CARD )
1523 /* unselect selected card */
1524 sel_card = NOT_A_CARD;
1525 break;
1527 if( rem != NOT_A_CARD )
1529 int cur_rem_old = cur_rem;
1530 count_rem = -1;
1531 /* draw new cards form the remains of the deck */
1532 if( cur_rem == NOT_A_CARD )
1534 /*if the cursor card is null*/
1535 cur_rem = rem;
1536 i = cards_per_draw - 1;
1537 count_rem++;
1539 else
1541 i = cards_per_draw;
1544 while( i > 0 && deck[cur_rem].next != NOT_A_CARD )
1546 cur_rem = deck[cur_rem].next;
1547 i--;
1548 count_rem++;
1550 /* test if any cards are really left on
1551 * the remains' stack */
1552 if( i == cards_per_draw )
1554 cur_rem = NOT_A_CARD;
1555 count_rem = -1;
1557 /* if cursor was on remains' stack when new cards were
1558 * drawn, put cursor on top of remains' stack */
1559 if( cur_col == REM_COL && cur_card == cur_rem_old )
1561 cur_card = cur_rem;
1562 sel_card = NOT_A_CARD;
1565 break;
1567 /* Show the menu */
1568 #ifdef SOL_RC_QUIT
1569 case SOL_RC_QUIT:
1570 #endif
1571 case SOL_QUIT:
1572 switch( solitaire_menu(true) )
1574 case MENU_QUIT:
1575 return SOLITAIRE_QUIT;
1577 case MENU_USB:
1578 return SOLITAIRE_USB;
1580 break;
1582 default:
1583 if( rb->default_event_handler( button ) == SYS_USB_CONNECTED )
1584 return SOLITAIRE_USB;
1585 break;
1588 if( button != BUTTON_NONE )
1589 lastbutton = button;
1591 /* fix incoherences concerning cur_col and cur_card */
1592 c = find_card_col( cur_card );
1593 if( c != NOT_A_COL && c != cur_col )
1594 cur_card = find_last_card( cur_col );
1596 if( cur_card == NOT_A_CARD
1597 && find_last_card( cur_col ) != NOT_A_CARD )
1598 cur_card = find_last_card( cur_col );
1603 * Plugin entry point
1606 enum plugin_status plugin_start( struct plugin_api* api, void* parameter )
1608 int result;
1610 /* plugin init */
1611 (void)parameter;
1612 rb = api;
1614 rb->splash( HZ, "Welcome to Solitaire!" );
1616 configfile_init(rb);
1617 configfile_load(CONFIG_FILENAME, config,
1618 sizeof(config) / sizeof(config[0]), CFGFILE_VERSION);
1619 rb->memcpy(&sol, &sol_disk, sizeof(sol)); /* copy to running config */
1621 init_help();
1623 /* play the game :)
1624 * Keep playing if a game was won (that means display the menu after
1625 * winning instead of quiting) */
1626 while( ( result = solitaire() ) == SOLITAIRE_WIN );
1628 if (rb->memcmp(&sol, &sol_disk, sizeof(sol))) /* save settings if changed */
1630 rb->memcpy(&sol_disk, &sol, sizeof(sol));
1631 configfile_save(CONFIG_FILENAME, config,
1632 sizeof(config) / sizeof(config[0]), CFGFILE_VERSION);
1635 /* Exit the plugin */
1636 return ( result == SOLITAIRE_USB ) ? PLUGIN_USB_CONNECTED : PLUGIN_OK;
1639 #endif