Fixes FS#8651 (noise and/or crash while crossfading). Latest ARM-asm submit for dsp...
[maemo-rb.git] / apps / plugins / blackjack.c
blobc9814abf899b6d445ec49fcce0e2bffdfc7d60ec
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id: $
10 * Copyright (C) 2006 Tom Ross
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 #include "plugin.h"
21 #include "card_deck.h"
22 #include "card_back.h"
24 PLUGIN_HEADER
26 /* save files */
27 #define SCORE_FILE PLUGIN_GAMES_DIR "/blackjack.score"
28 #define SAVE_FILE PLUGIN_GAMES_DIR "/blackjack.save"
30 #define NUM_SCORES LCD_HEIGHT/8-2
32 /* final game return status */
33 #define BJ_END 3
34 #define BJ_USB 2
35 #define BJ_QUIT 1
36 #define BJ_LOSE 0
38 #if CONFIG_KEYPAD == RECORDER_PAD
39 #define BJACK_START BUTTON_ON
40 #define BJACK_QUIT BUTTON_OFF
41 #define BJACK_MAX (BUTTON_ON|BUTTON_UP)
42 #define BJACK_MIN (BUTTON_ON|BUTTON_DOWN)
43 #define BJACK_HIT BUTTON_F1
44 #define BJACK_STAY BUTTON_F2
45 #define BJACK_DOUBLEDOWN BUTTON_F3
46 #define BJACK_SCORES BUTTON_RIGHT
47 #define BJACK_RESUME BUTTON_PLAY
48 #define BJACK_UP BUTTON_UP
49 #define BJACK_DOWN BUTTON_DOWN
50 #define BJACK_RIGHT BUTTON_RIGHT
51 #define BJACK_LEFT BUTTON_LEFT
53 #elif CONFIG_KEYPAD == ONDIO_PAD
54 #define BJACK_START BUTTON_MENU
55 #define BJACK_QUIT BUTTON_OFF
56 #define BJACK_MAX (BUTTON_MENU|BUTTON_UP)
57 #define BJACK_MIN (BUTTON_MENU|BUTTON_DOWN)
58 #define BJACK_HIT BUTTON_LEFT
59 #define BJACK_STAY BUTTON_RIGHT
60 #define BJACK_DOUBLEDOWN BUTTON_UP
61 #define BJACK_SCORES BUTTON_UP
62 #define BJACK_RESUME BUTTON_DOWN
63 #define BJACK_UP BUTTON_UP
64 #define BJACK_DOWN BUTTON_DOWN
65 #define BJACK_RIGHT BUTTON_RIGHT
66 #define BJACK_LEFT BUTTON_LEFT
68 #elif CONFIG_KEYPAD == IRIVER_H10_PAD
69 #define BJACK_START BUTTON_PLAY
70 #define BJACK_QUIT BUTTON_POWER
71 #define BJACK_MAX (BUTTON_PLAY|BUTTON_SCROLL_UP)
72 #define BJACK_MIN (BUTTON_PLAY|BUTTON_SCROLL_DOWN)
73 #define BJACK_HIT BUTTON_PLAY
74 #define BJACK_STAY BUTTON_FF
75 #define BJACK_DOUBLEDOWN BUTTON_REW
76 #define BJACK_SCORES BUTTON_LEFT
77 #define BJACK_RESUME BUTTON_RIGHT
78 #define BJACK_UP BUTTON_SCROLL_UP
79 #define BJACK_DOWN BUTTON_SCROLL_DOWN
80 #define BJACK_RIGHT BUTTON_RIGHT
81 #define BJACK_LEFT BUTTON_LEFT
83 #elif (CONFIG_KEYPAD == IRIVER_H100_PAD) || \
84 (CONFIG_KEYPAD == IRIVER_H300_PAD)
85 #define BJACK_START BUTTON_ON
86 #define BJACK_QUIT BUTTON_OFF
87 #define BJACK_MAX (BUTTON_ON|BUTTON_UP)
88 #define BJACK_MIN (BUTTON_ON|BUTTON_DOWN)
89 #define BJACK_HIT BUTTON_ON
90 #define BJACK_STAY BUTTON_REC
91 #define BJACK_DOUBLEDOWN BUTTON_SELECT
92 #define BJACK_SCORES BUTTON_SELECT
93 #define BJACK_RESUME BUTTON_MODE
94 #define BJACK_UP BUTTON_UP
95 #define BJACK_DOWN BUTTON_DOWN
96 #define BJACK_RIGHT BUTTON_RIGHT
97 #define BJACK_LEFT BUTTON_LEFT
99 #elif (CONFIG_KEYPAD == IPOD_4G_PAD) || \
100 (CONFIG_KEYPAD == IPOD_3G_PAD) || \
101 (CONFIG_KEYPAD == IPOD_1G2G_PAD)
102 #define BJACK_START BUTTON_SELECT
103 #define BJACK_QUIT BUTTON_MENU
104 #define BJACK_MAX (BUTTON_SELECT|BUTTON_SCROLL_FWD)
105 #define BJACK_MIN (BUTTON_SELECT|BUTTON_SCROLL_BACK)
106 #define BJACK_HIT BUTTON_SELECT
107 #define BJACK_STAY BUTTON_RIGHT
108 #define BJACK_DOUBLEDOWN BUTTON_LEFT
109 #define BJACK_SCORES BUTTON_RIGHT
110 #define BJACK_RESUME BUTTON_PLAY
111 #define BJACK_UP BUTTON_SCROLL_FWD
112 #define BJACK_DOWN BUTTON_SCROLL_BACK
113 #define BJACK_RIGHT BUTTON_RIGHT
114 #define BJACK_LEFT BUTTON_LEFT
116 #elif CONFIG_KEYPAD == IAUDIO_X5M5_PAD
117 #define BJACK_START BUTTON_PLAY
118 #define BJACK_QUIT BUTTON_POWER
119 #define BJACK_MAX (BUTTON_PLAY|BUTTON_UP)
120 #define BJACK_MIN (BUTTON_PLAY|BUTTON_DOWN)
121 #define BJACK_HIT BUTTON_SELECT
122 #define BJACK_STAY BUTTON_REC
123 #define BJACK_DOUBLEDOWN BUTTON_PLAY
124 #define BJACK_SCORES BUTTON_RIGHT
125 #define BJACK_RESUME BUTTON_DOWN
126 #define BJACK_UP BUTTON_UP
127 #define BJACK_DOWN BUTTON_DOWN
128 #define BJACK_RIGHT BUTTON_RIGHT
129 #define BJACK_LEFT BUTTON_LEFT
131 #elif CONFIG_KEYPAD == IRIVER_IFP7XX_PAD
132 #define BJACK_START BUTTON_MODE
133 #define BJACK_QUIT BUTTON_PLAY
134 #define BJACK_MAX (BUTTON_EQ|BUTTON_UP)
135 #define BJACK_MIN (BUTTON_EQ|BUTTON_DOWN)
136 #define BJACK_HIT BUTTON_EQ
137 #define BJACK_STAY BUTTON_MODE
138 #define BJACK_DOUBLEDOWN BUTTON_SELECT
139 #define BJACK_SCORES BUTTON_SELECT
140 #define BJACK_RESUME (BUTTON_EQ|BUTTON_MODE)
141 #define BJACK_UP BUTTON_UP
142 #define BJACK_DOWN BUTTON_DOWN
143 #define BJACK_RIGHT BUTTON_RIGHT
144 #define BJACK_LEFT BUTTON_LEFT
146 #elif CONFIG_KEYPAD == GIGABEAT_PAD
147 #define BJACK_START BUTTON_POWER
148 #define BJACK_QUIT BUTTON_A
149 #define BJACK_MAX BUTTON_VOL_UP
150 #define BJACK_MIN BUTTON_VOL_DOWN
151 #define BJACK_HIT BUTTON_VOL_UP
152 #define BJACK_STAY BUTTON_VOL_DOWN
153 #define BJACK_DOUBLEDOWN BUTTON_SELECT
154 #define BJACK_SCORES BUTTON_RIGHT
155 #define BJACK_RESUME BUTTON_MENU
156 #define BJACK_UP BUTTON_UP
157 #define BJACK_DOWN BUTTON_DOWN
158 #define BJACK_RIGHT BUTTON_RIGHT
159 #define BJACK_LEFT BUTTON_LEFT
161 #elif CONFIG_KEYPAD == SANSA_E200_PAD
162 #define BJACK_START BUTTON_SELECT
163 #define BJACK_QUIT BUTTON_POWER
164 #define BJACK_MAX (BUTTON_REC|BUTTON_UP)
165 #define BJACK_MIN (BUTTON_REC|BUTTON_DOWN)
166 #define BJACK_HIT BUTTON_SELECT
167 #define BJACK_STAY BUTTON_RIGHT
168 #define BJACK_DOUBLEDOWN BUTTON_LEFT
169 #define BJACK_SCORES BUTTON_UP
170 #define BJACK_RESUME BUTTON_REC
171 #define BJACK_UP BUTTON_SCROLL_FWD
172 #define BJACK_DOWN BUTTON_SCROLL_BACK
173 #define BJACK_RIGHT BUTTON_RIGHT
174 #define BJACK_LEFT BUTTON_LEFT
176 #elif CONFIG_KEYPAD == SANSA_C200_PAD
177 #define BJACK_START BUTTON_SELECT
178 #define BJACK_QUIT BUTTON_POWER
179 #define BJACK_MAX BUTTON_VOL_UP
180 #define BJACK_MIN BUTTON_VOL_DOWN
181 #define BJACK_HIT BUTTON_SELECT
182 #define BJACK_STAY BUTTON_RIGHT
183 #define BJACK_DOUBLEDOWN BUTTON_LEFT
184 #define BJACK_SCORES BUTTON_REC
185 #define BJACK_RESUME BUTTON_DOWN
186 #define BJACK_UP BUTTON_UP
187 #define BJACK_DOWN BUTTON_DOWN
188 #define BJACK_RIGHT BUTTON_RIGHT
189 #define BJACK_LEFT BUTTON_LEFT
191 #elif CONFIG_KEYPAD == ELIO_TPJ1022_PAD
192 #define BJACK_START BUTTON_MAIN
193 #define BJACK_QUIT BUTTON_POWER
194 #define BJACK_MAX (BUTTON_REC|BUTTON_UP)
195 #define BJACK_MIN (BUTTON_REC|BUTTON_DOWN)
196 #define BJACK_HIT BUTTON_MAIN
197 #define BJACK_STAY BUTTON_MENU
198 #define BJACK_DOUBLEDOWN BUTTON_DOWN
199 #define BJACK_SCORES BUTTON_UP
200 #define BJACK_RESUME BUTTON_FF
201 #define BJACK_UP BUTTON_UP
202 #define BJACK_DOWN BUTTON_DOWN
203 #define BJACK_RIGHT BUTTON_RIGHT
204 #define BJACK_LEFT BUTTON_LEFT
206 #elif CONFIG_KEYPAD == GIGABEAT_S_PAD
207 #define BJACK_START BUTTON_PLAY
208 #define BJACK_QUIT BUTTON_BACK
209 #define BJACK_MAX BUTTON_VOL_UP
210 #define BJACK_MIN BUTTON_VOL_DOWN
211 #define BJACK_HIT BUTTON_VOL_UP
212 #define BJACK_STAY BUTTON_VOL_DOWN
213 #define BJACK_DOUBLEDOWN BUTTON_SELECT
214 #define BJACK_SCORES BUTTON_RIGHT
215 #define BJACK_RESUME BUTTON_MENU
216 #define BJACK_UP BUTTON_UP
217 #define BJACK_DOWN BUTTON_DOWN
218 #define BJACK_RIGHT BUTTON_RIGHT
219 #define BJACK_LEFT BUTTON_LEFT
221 #elif CONFIG_KEYPAD == MROBE100_PAD
223 #define BJACK_START BUTTON_SELECT
224 #define BJACK_QUIT BUTTON_POWER
225 #define BJACK_MAX BUTTON_MENU
226 #define BJACK_MIN BUTTON_DISPLAY
227 #define BJACK_HIT BUTTON_MENU
228 #define BJACK_STAY BUTTON_DISPLAY
229 #define BJACK_DOUBLEDOWN BUTTON_SELECT
230 #define BJACK_SCORES BUTTON_RIGHT
231 #define BJACK_RESUME BUTTON_PLAY
232 #define BJACK_UP BUTTON_UP
233 #define BJACK_DOWN BUTTON_DOWN
234 #define BJACK_RIGHT BUTTON_RIGHT
235 #define BJACK_LEFT BUTTON_LEFT
237 #elif CONFIG_KEYPAD == IAUDIO_M3_PAD
239 #define BJACK_START BUTTON_RC_PLAY
240 #define BJACK_QUIT BUTTON_RC_REC
241 #define BJACK_MAX (BUTTON_RC_PLAY|BUTTON_RC_VOL_UP)
242 #define BJACK_MIN (BUTTON_RC_PLAY|BUTTON_RC_VOL_DOWN)
243 #define BJACK_HIT BUTTON_RC_PLAY
244 #define BJACK_STAY BUTTON_RC_FF
245 #define BJACK_DOUBLEDOWN BUTTON_RC_REW
246 #define BJACK_SCORES BUTTON_RC_MENU
247 #define BJACK_RESUME BUTTON_RC_MODE
248 #define BJACK_UP BUTTON_RC_VOL_UP
249 #define BJACK_DOWN BUTTON_RC_VOL_DOWN
250 #define BJACK_RIGHT BUTTON_RC_FF
251 #define BJACK_LEFT BUTTON_RC_REW
253 #elif CONFIG_KEYPAD == COWOND2_PAD
254 #define BJACK_START BUTTON_SELECT
255 #define BJACK_QUIT BUTTON_POWER
256 #define BJACK_MAX BUTTON_PLUS
257 #define BJACK_MIN BUTTON_MINUS
258 #define BJACK_HIT BUTTON_SELECT
259 #define BJACK_STAY BUTTON_MENU
260 #define BJACK_DOUBLEDOWN (BUTTON_DOWN|BUTTON_MENU)
261 #define BJACK_SCORES (BUTTON_SELECT|BUTTON_MENU)
262 #define BJACK_RESUME (BUTTON_SELECT|BUTTON_PLUS)
263 #define BJACK_UP BUTTON_UP
264 #define BJACK_DOWN BUTTON_DOWN
265 #define BJACK_RIGHT BUTTON_RIGHT
266 #define BJACK_LEFT BUTTON_LEFT
268 #else
269 #error No keymap defined!
270 #endif
272 #ifdef HAVE_LCD_COLOR
273 #define BG_COLOR LCD_RGBPACK(0,157,0)
274 #define FG_COLOR LCD_WHITE
275 #elif LCD_DEPTH > 1
276 #define BG_COLOR LCD_WHITE
277 #define FG_COLOR LCD_BLACK
278 #endif
280 #define CARD_WIDTH BMPWIDTH_card_back
281 #define CARD_HEIGHT BMPHEIGHT_card_back
283 /* This is the max amount of cards onscreen before condensing */
284 #define MAX_CARDS LCD_WIDTH/(CARD_WIDTH+4)
286 extern const fb_data card_deck[];
287 extern const fb_data card_back[];
289 #define NEXT_CARD bj->player_cards[done][bj->num_player_cards[done]]
291 /* global rockbox api */
292 static struct plugin_api* rb;
294 MEM_FUNCTION_WRAPPERS(rb);
296 /* dealer and player card positions */
297 unsigned int dealer_x, dealer_y, player_x, player_y;
299 typedef struct card {
300 unsigned int value; /* Card's value in Blackjack */
301 unsigned int num; /* Value on card face 0-12 (0=Ace, 1=2, 11=Q) */
302 unsigned int suit; /* 0:Spades, 1:Hearts, 2: Clubs; 3: Diamonds */
303 bool is_soft_ace;
304 } card;
306 typedef struct game_context {
307 struct card player_cards[2][22]; /* 22 Cards means the deal was all aces */
308 struct card dealer_cards[22]; /* That is the worst-case scenario */
309 unsigned int player_total;
310 unsigned int dealer_total;
311 signed int player_money;
312 unsigned int num_player_cards[2];
313 unsigned int num_dealer_cards;
314 unsigned int current_bet;
315 unsigned int split_status; /* 0 = split hasn't been asked, *
316 * 1 = split did not occur *
317 * 2 = split occurred *
318 * 3 = split occurred and 1st hand done */
319 bool is_blackjack;
320 bool end_hand;
321 bool asked_insurance;
322 signed short highscores[NUM_SCORES];
323 bool resume;
324 bool dirty;
325 } game_context;
327 /*****************************************************************************
328 * blackjack_init() initializes blackjack data structures.
329 ******************************************************************************/
330 static void blackjack_init(struct game_context* bj) {
331 /* seed the rand generator */
332 rb->srand(*rb->current_tick);
334 /* reset card positions */
335 dealer_x = 4;
336 dealer_y = LCD_HEIGHT/4 - CARD_HEIGHT/2;
337 player_x = 4;
338 player_y = LCD_HEIGHT - LCD_HEIGHT/4 - CARD_HEIGHT/2;
340 /* check for resumed game */
341 if(bj->resume) return;
343 /* reset scoring */
344 bj->player_total = 0;
345 bj->dealer_total = 0;
346 bj->num_player_cards[0] = 2;
347 bj->num_player_cards[1] = 0;
348 bj->num_dealer_cards = 2;
349 bj->end_hand = false;
350 bj->split_status = 0;
351 bj->is_blackjack = false;
352 bj->asked_insurance = false;
355 /*****************************************************************************
356 * blackjack_drawtable() draws the table and some text.
357 ******************************************************************************/
358 static void blackjack_drawtable(struct game_context* bj) {
359 unsigned int w, h, y_loc;
360 char str[10];
362 #if LCD_HEIGHT <= 64
363 rb->lcd_getstringsize("Bet", &w, &h);
364 rb->lcd_putsxy(LCD_WIDTH - w, 2*h + 1, "Bet");
365 rb->snprintf(str, 9, "$%d", bj->current_bet);
366 rb->lcd_getstringsize(str, &w, &h);
367 rb->lcd_putsxy(LCD_WIDTH - w, 3*h + 1, str);
368 y_loc = LCD_HEIGHT/2;
369 #else
370 rb->lcd_getstringsize("Bet", &w, &h);
371 rb->lcd_putsxy(LCD_WIDTH - w, 5*h / 2, "Bet");
372 rb->snprintf(str, 9, "$%d", bj->current_bet);
373 rb->lcd_getstringsize(str, &w, &h);
374 rb->lcd_putsxy(LCD_WIDTH - w, 7*h / 2, str);
375 rb->lcd_hline(0, LCD_WIDTH, LCD_HEIGHT/2);
376 y_loc = LCD_HEIGHT/2 + h;
377 #endif
379 rb->lcd_putsxy(0,0, "Dealer");
380 rb->lcd_getstringsize("Player", &w, &h);
381 rb->lcd_putsxy(0, y_loc, "Player");
382 rb->lcd_getstringsize("Total", &w, &h);
383 rb->lcd_putsxy(LCD_WIDTH - w, y_loc, "Total");
384 rb->lcd_getstringsize("Money", &w, &h);
385 rb->lcd_putsxy(LCD_WIDTH - w, 0, "Money");
386 rb->snprintf(str, 9, "$%d", bj->player_money - bj->current_bet);
387 rb->lcd_getstringsize(str, &w, &h);
388 rb->lcd_putsxy(LCD_WIDTH - w, h + 1, str);
389 rb->snprintf(str, 3, "%d", bj->player_total);
390 rb->lcd_getstringsize(str, &w, &h);
391 rb->lcd_putsxy(LCD_WIDTH - w, y_loc + h, str);
394 /*****************************************************************************
395 * find_value() is passed a card and returns its blackjack value.
396 ******************************************************************************/
397 static unsigned int find_value(unsigned int number) {
398 unsigned int thisValue;
399 if (number == 0)
400 thisValue = 11; /* Aces get a value of 11 at first */
401 else if (number < 10)
402 thisValue = number + 1;
403 else
404 thisValue = 10; /* Anything 10 or higher gets a value of 10 */
406 return thisValue;
409 /*****************************************************************************
410 * draw_card() draws a card to the screen.
411 ******************************************************************************/
412 static void draw_card(struct card temp_card, bool shown, unsigned int x,
413 unsigned int y) {
414 if(shown)
415 rb->lcd_bitmap_part(card_deck, CARD_WIDTH*temp_card.num,
416 CARD_HEIGHT*temp_card.suit, BMPWIDTH_card_deck,
417 x+1, y+1, CARD_WIDTH, CARD_HEIGHT);
418 else
419 rb->lcd_bitmap(card_back, x+1, y+1,CARD_WIDTH, CARD_HEIGHT);
420 #if LCD_DEPTH > 1
421 rb->lcd_set_foreground(LCD_BLACK);
422 #endif
424 /* Print outlines */
425 #if CARD_WIDTH >= 26
426 rb->lcd_hline(x+2, x+CARD_WIDTH-1, y);
427 rb->lcd_hline(x+2, x+CARD_WIDTH-1, y+CARD_HEIGHT+1);
428 rb->lcd_vline(x, y+2, y+CARD_HEIGHT-3);
429 rb->lcd_vline(x+CARD_WIDTH+1, y+2, y+CARD_HEIGHT-1);
430 rb->lcd_drawpixel(x+1, y+1);
431 rb->lcd_drawpixel(x+1, y+CARD_HEIGHT);
432 rb->lcd_drawpixel(x+CARD_WIDTH, y+1);
433 rb->lcd_drawpixel(x+CARD_WIDTH, y+CARD_HEIGHT);
434 #else
435 rb->lcd_hline(x+1, x+CARD_WIDTH, y);
436 rb->lcd_hline(x+1, x+CARD_WIDTH, y+CARD_HEIGHT+1);
437 rb->lcd_vline(x, y+1, y+CARD_HEIGHT);
438 rb->lcd_vline(x+CARD_WIDTH+1, y+1, y+CARD_HEIGHT);
439 #endif
441 #if LCD_DEPTH > 1
442 rb->lcd_set_foreground(FG_COLOR);
443 #endif
446 /*****************************************************************************
447 * new_card() initializes a new card and gives it values.
448 ******************************************************************************/
449 static struct card new_card(void) {
450 struct card new_card;
451 new_card.suit = rb->rand()%4; /* Random number 0-3 */
452 new_card.num = rb->rand()%13; /* Random number 0-12 */
453 new_card.value = find_value(new_card.num);
454 new_card.is_soft_ace = new_card.num == 0 ? true : false;
455 return new_card;
458 /*****************************************************************************
459 * deal_init_card() deals and draws to the screen the player's and dealer's
460 * initial cards.
461 ******************************************************************************/
462 static void deal_init_cards(struct game_context* bj) {
463 bj->dealer_cards[0] = new_card();
464 bj->dealer_total += bj->dealer_cards[0].value;
466 draw_card(bj->dealer_cards[0], false, dealer_x, dealer_y);
468 bj->dealer_cards[1] = new_card();
469 bj->dealer_total += bj->dealer_cards[1].value;
470 draw_card(bj->dealer_cards[1], true, dealer_x + CARD_WIDTH + 4, dealer_y);
472 bj->player_cards[0][0] = new_card();
473 bj->player_total += bj->player_cards[0][0].value;
474 draw_card(bj->player_cards[0][0], true, player_x, player_y);
475 player_x += CARD_WIDTH + 4;
477 bj->player_cards[0][1] = new_card();
478 bj->player_total += bj->player_cards[0][1].value;
479 draw_card(bj->player_cards[0][1], true, player_x, player_y);
480 player_x += CARD_WIDTH + 4;
483 /*****************************************************************************
484 * redraw_board() redraws all the cards and the board
485 ******************************************************************************/
486 static void redraw_board(struct game_context* bj) {
487 unsigned int i, n, upper_bound;
488 rb->lcd_clear_display();
490 blackjack_drawtable(bj);
491 player_x = 4;
492 dealer_x = 4;
493 upper_bound = bj->split_status > 1 ? 2 : 1;
495 for (i = 0; i < bj->num_dealer_cards; i++) {
496 if (!bj->end_hand) {
497 draw_card(bj->dealer_cards[0], false, dealer_x, dealer_y);
499 /* increment i so the dealer's first card isn't displayed */
500 i++;
501 dealer_x += CARD_WIDTH + 4;
503 draw_card(bj->dealer_cards[i], true, dealer_x, dealer_y);
505 if (bj->num_dealer_cards > MAX_CARDS-1)
506 dealer_x += 10;
507 else
508 dealer_x += CARD_WIDTH + 4;
511 for (n = 0; n < upper_bound; n++) {
512 for (i = 0; i < bj->num_player_cards[n]; i++) {
513 draw_card(bj->player_cards[n][i], true, player_x, player_y);
514 if (bj->split_status>1 || bj->num_player_cards[n]>MAX_CARDS)
515 player_x += 10;
516 else
517 player_x += CARD_WIDTH + 4;
519 if (bj->split_status > 1)
520 player_x = LCD_WIDTH/2 + 4;
524 /*****************************************************************************
525 * update_total updates the player's total
526 ******************************************************************************/
527 static void update_total(struct game_context* bj) {
528 char total[3];
529 unsigned int w, h;
530 rb->snprintf(total, 3, "%d", bj->player_total);
531 rb->lcd_getstringsize(total, &w, &h);
532 #if LCD_HEIGHT > 64
533 h *= 2;
534 #endif
535 rb->lcd_putsxy(LCD_WIDTH - w, LCD_HEIGHT/2 + h, total);
536 rb->lcd_update_rect(LCD_WIDTH - w, LCD_HEIGHT/2 + h, w, h);
540 /*****************************************************************************
541 * check_for_aces() is passed an array of cards and returns where an ace is
542 * located. Otherwise, returns -1.
543 ******************************************************************************/
544 static signed int check_for_aces(struct card temp_cards[],
545 unsigned int size) {
546 unsigned int i;
547 for(i = 0; i < size; i++) {
548 if (temp_cards[i].is_soft_ace == true)
549 return i;
551 return -1;
554 /*****************************************************************************
555 * check_totals() compares player and dealer totals.
556 * 0: bust 1: loss, 2: push, 3: win, 4: blackjack, 5: something's not right...
557 ******************************************************************************/
558 static unsigned int check_totals(struct game_context* bj)
560 unsigned int temp;
561 if (bj->player_total > 21)
562 temp = 0;
563 else if (bj->player_total == 21 && bj->is_blackjack)
564 if (bj->dealer_total == 21 && bj->num_dealer_cards == 2)
565 temp = 2;
566 else
567 temp = 4;
568 else if (bj->player_total == bj->dealer_total)
569 temp = 2;
570 else if (bj->dealer_total > 21 && bj->player_total < 22)
571 temp = 3;
572 else if (bj->dealer_total > bj->player_total)
573 temp = 1;
574 else if (bj->player_total > bj->dealer_total)
575 temp = 3;
576 else
577 temp = 5;
579 return temp;
582 /*****************************************************************************
583 * finish_dealer() draws cards for the dealer until he has 17 or more.
584 ******************************************************************************/
585 static void finish_dealer(struct game_context* bj) {
586 signed int temp = 0;
588 if (bj->dealer_total > 16 && bj->dealer_total < 22)
589 return;
591 while (bj->dealer_total < 17) {
592 bj->dealer_cards[bj->num_dealer_cards] = new_card();
593 bj->dealer_total += bj->dealer_cards[bj->num_dealer_cards].value;
594 bj->num_dealer_cards++;
597 while (bj->dealer_total > 21) {
598 temp = check_for_aces(bj->dealer_cards, bj->num_dealer_cards);
599 if(temp != -1) {
600 bj->dealer_cards[temp].is_soft_ace = false;
601 bj->dealer_total -= 10;
603 else
604 return;
608 /*****************************************************************************
609 * finish_game() completes the game once player's turn is over.
610 ******************************************************************************/
611 static void finish_game(struct game_context* bj) {
612 unsigned int rValue, w, h;
613 char str[19];
615 do {
616 finish_dealer(bj);
617 } while (bj->dealer_total < 17);
619 redraw_board(bj);
620 rValue = check_totals(bj);
622 if (rValue == 0) {
623 rb->snprintf(str, sizeof(str), " Bust! ");
624 bj->player_money -= bj->current_bet;
626 else if (rValue == 1) {
627 rb->snprintf(str, sizeof(str), " Sorry, you lost. ");
628 bj->player_money -= bj->current_bet;
630 else if (rValue == 2) {
631 rb->snprintf(str, sizeof(str), " Push ");
633 else if (rValue == 3) {
634 rb->snprintf(str, sizeof(str), " You won! ");
635 bj->player_money+= bj->current_bet;
637 else {
638 rb->snprintf(str, sizeof(str), " Blackjack! ");
639 bj->player_money += bj->current_bet * 3 / 2;
641 rb->lcd_getstringsize(str, &w, &h);
643 #if LCD_HEIGHT <= 64
644 rb->lcd_set_drawmode(DRMODE_BG+DRMODE_INVERSEVID);
645 rb->lcd_fillrect(0, LCD_HEIGHT/2, LCD_WIDTH, LCD_HEIGHT/2);
646 rb->lcd_set_drawmode(DRMODE_SOLID);
647 rb->lcd_putsxy(LCD_WIDTH/2 - w/2, LCD_HEIGHT/2 + h, str);
648 rb->snprintf(str, 12, "You have %d", bj->player_total);
649 rb->lcd_getstringsize(str, &w, &h);
650 rb->lcd_putsxy(LCD_WIDTH/2 - w/2, LCD_HEIGHT/2, str);
651 #else
652 rb->lcd_putsxy(LCD_WIDTH/2 - w/2, LCD_HEIGHT/2 - h/2, str);
653 #endif
654 rb->lcd_update();
657 /*****************************************************************************
658 * blackjack_recordscore() inserts a high score into the high scores list and
659 * returns the high score position.
660 ******************************************************************************/
661 static unsigned int blackjack_recordscore(struct game_context* bj) {
662 unsigned int i;
663 unsigned int position = 0;
664 signed short current, temp;
666 /* calculate total score */
667 current = bj->player_money;
668 if(current <= 10) return 0;
670 /* insert the current score into the high scores */
671 for(i=0; i<NUM_SCORES; i++) {
672 if(current >= bj->highscores[i]) {
673 if(!position) {
674 position = i+1;
675 bj->dirty = true;
677 temp = bj->highscores[i];
678 bj->highscores[i] = current;
679 current = temp;
683 return position;
686 /*****************************************************************************
687 * blackjack_loadscores() loads the high scores saved file.
688 ******************************************************************************/
689 static void blackjack_loadscores(struct game_context* bj) {
690 signed int fd;
692 bj->dirty = false;
694 /* clear high scores */
695 rb->memset(bj->highscores, 0, sizeof(bj->highscores));
697 /* open scores file */
698 fd = rb->open(SCORE_FILE, O_RDONLY);
699 if(fd < 0) return;
701 /* read in high scores */
702 if(rb->read(fd, bj->highscores, sizeof(bj->highscores)) <= 0) {
703 /* scores are bad, reset */
704 rb->memset(bj->highscores, 0, sizeof(bj->highscores));
707 rb->close(fd);
710 /*****************************************************************************
711 * blackjack_savescores() saves the high scores saved file.
712 ******************************************************************************/
713 static void blackjack_savescores(struct game_context* bj) {
714 unsigned int fd;
716 /* write out the high scores to the save file */
717 fd = rb->open(SCORE_FILE, O_WRONLY|O_CREAT);
718 rb->write(fd, bj->highscores, sizeof(bj->highscores));
719 rb->close(fd);
720 bj->dirty = false;
723 /*****************************************************************************
724 * blackjack_loadgame() loads the saved game and returns load success.
725 ******************************************************************************/
726 static bool blackjack_loadgame(struct game_context* bj) {
727 signed int fd;
728 bool loaded = false;
730 /* open game file */
731 fd = rb->open(SAVE_FILE, O_RDONLY);
732 if(fd < 0) return loaded;
734 /* read in saved game */
735 while(true) {
736 if(rb->read(fd, &bj->player_money, sizeof(bj->player_money)) <= 0) break;
737 if(rb->read(fd, &bj->player_total, sizeof(bj->player_total)) <= 0) break;
738 if(rb->read(fd, &bj->dealer_total, sizeof(bj->dealer_total)) <= 0) break;
739 if(rb->read(fd, &bj->num_player_cards, sizeof(bj->num_player_cards))<=0)
740 break;
741 if(rb->read(fd, &bj->num_dealer_cards, sizeof(bj->num_dealer_cards))<=0)
742 break;
743 if(rb->read(fd, &bj->current_bet, sizeof(bj->current_bet)) <= 0) break;
744 if(rb->read(fd, &bj->is_blackjack, sizeof(bj->is_blackjack)) <= 0) break;
745 if(rb->read(fd, &bj->split_status, sizeof(bj->split_status)) <= 0) break;
746 if(rb->read(fd, &bj->asked_insurance, sizeof(bj->asked_insurance)) <= 0)
747 break;
748 if(rb->read(fd, &bj->end_hand, sizeof(bj->end_hand)) <= 0) break;
749 if(rb->read(fd, &bj->player_cards, sizeof(bj->player_cards)) <= 0) break;
750 if(rb->read(fd, &bj->dealer_cards, sizeof(bj->dealer_cards)) <= 0) break;
751 bj->resume = true;
752 loaded = true;
753 break;
756 rb->close(fd);
758 /* delete saved file */
759 rb->remove(SAVE_FILE);
760 return loaded;
763 /*****************************************************************************
764 * blackjack_savegame() saves the current game state.
765 ******************************************************************************/
766 static void blackjack_savegame(struct game_context* bj) {
767 unsigned int fd;
769 /* write out the game state to the save file */
770 fd = rb->open(SAVE_FILE, O_WRONLY|O_CREAT);
771 rb->write(fd, &bj->player_money, sizeof(bj->player_money));
772 rb->write(fd, &bj->player_total, sizeof(bj->player_total));
773 rb->write(fd, &bj->dealer_total, sizeof(bj->dealer_total));
774 rb->write(fd, &bj->num_player_cards, sizeof(bj->num_player_cards));
775 rb->write(fd, &bj->num_dealer_cards, sizeof(bj->num_dealer_cards));
776 rb->write(fd, &bj->current_bet, sizeof(bj->current_bet));
777 rb->write(fd, &bj->is_blackjack, sizeof(bj->is_blackjack));
778 rb->write(fd, &bj->split_status, sizeof(bj->split_status));
779 rb->write(fd, &bj->asked_insurance, sizeof(bj->asked_insurance));
780 rb->write(fd, &bj->end_hand, sizeof(bj->end_hand));
781 rb->write(fd, &bj->player_cards, sizeof(bj->player_cards));
782 rb->write(fd, &bj->dealer_cards, sizeof(bj->dealer_cards));
783 rb->close(fd);
785 bj->resume = true;
788 /*****************************************************************************
789 * blackjack_callback() is the default event handler callback which is called
790 * on usb connect and shutdown.
791 ******************************************************************************/
792 static void blackjack_callback(void* param) {
793 struct game_context* bj = (struct game_context*) param;
794 if(bj->dirty) {
795 rb->splash(HZ, "Saving high scores...");
796 blackjack_savescores(bj);
800 /*****************************************************************************
801 * blackjack_get_yes_no() gets a yes/no answer from the user
802 ******************************************************************************/
803 static unsigned int blackjack_get_yes_no(char message[20]) {
804 int button;
805 unsigned int w, h, b, choice = 0;
806 bool breakout = false;
807 char message_yes[24], message_no[24];
809 rb->strcpy(message_yes, message);
810 rb->strcpy(message_no, message);
811 rb->strcat(message_yes, " Yes");
812 rb->strcat(message_no, " No");
813 rb->lcd_getstringsize(message_yes, &w, &h);
814 const char *stg[] = {message_yes, message_no};
816 #if LCD_HEIGHT <= 64
817 b = 2*h+1;
818 #else
819 b = h-1;
820 #endif
822 #ifdef HAVE_LCD_COLOR
823 rb->lcd_fillrect(LCD_WIDTH/2 - w/2, LCD_HEIGHT/2 + b, w+1, h+3);
824 rb->lcd_set_foreground(LCD_BLACK);
825 rb->lcd_set_background(LCD_WHITE);
826 #else
827 rb->lcd_set_drawmode(DRMODE_BG+DRMODE_INVERSEVID);
828 rb->lcd_fillrect(LCD_WIDTH/2 - w/2, LCD_HEIGHT/2 + b, w+1, h+3);
829 rb->lcd_set_drawmode(DRMODE_SOLID);
830 #endif
831 rb->lcd_drawrect(LCD_WIDTH/2 - w/2 - 1, LCD_HEIGHT/2 + b - 1, w+3, h+4);
833 while(!breakout) {
834 rb->lcd_putsxy(LCD_WIDTH/2 - w/2, LCD_HEIGHT/2 + b +1, stg[choice]);
835 rb->lcd_update_rect(LCD_WIDTH/2 - w/2 - 1, LCD_HEIGHT/2 + b -1,
836 w+3, h+4);
837 button = rb->button_get(true);
839 switch(button) {
840 case BJACK_LEFT:
841 case (BJACK_LEFT|BUTTON_REPEAT):
842 case BJACK_RIGHT:
843 case (BJACK_RIGHT|BUTTON_REPEAT):
844 choice ^= 1;
845 break;
846 case BJACK_START: breakout = true;
847 break;
848 case BJACK_QUIT: breakout = true;
849 choice = BJ_QUIT;
850 break;
854 #if LCD_DEPTH > 1
855 rb->lcd_set_foreground(FG_COLOR);
856 rb->lcd_set_background(BG_COLOR);
857 #endif
858 return choice;
861 /*****************************************************************************
862 * blackjack_get_amount() gets an amount from the player to be used
863 ******************************************************************************/
864 static signed int blackjack_get_amount(char message[20], signed int lower_limit,
865 signed int upper_limit,
866 signed int start) {
867 int button;
868 char str[6];
869 bool changed = false;
870 unsigned int w, h;
871 signed int amount;
873 rb->lcd_getstringsize("A", &w, &h); /* find the size of one character */
875 if (start > upper_limit)
876 amount = upper_limit;
877 else if (start < lower_limit)
878 amount = lower_limit;
879 else
880 amount = start;
882 #if LCD_DEPTH > 1
883 rb->lcd_set_background(LCD_WHITE);
884 rb->lcd_set_foreground(LCD_BLACK);
885 #endif
887 #if LCD_HEIGHT <= 64
888 rb->lcd_clear_display();
889 rb->lcd_puts(0, 1, message);
890 rb->snprintf(str, 9, "$%d", amount);
891 rb->lcd_puts(0, 2, str);
892 rb->lcd_puts(0, 3, "RIGHT: +1");
893 rb->lcd_puts(0, 4, "LEFT: -1");
894 rb->lcd_puts(0, 5, "UP: +10");
895 rb->lcd_puts(0, 6, "DOWN: -10");
896 rb->lcd_update();
897 #else
898 rb->lcd_set_drawmode(DRMODE_BG+DRMODE_INVERSEVID);
899 rb->lcd_fillrect(LCD_WIDTH/2 - 9*w - 1, LCD_HEIGHT/2 - 4*h - 3, 37*w / 2,
900 8*h -3);
901 rb->lcd_set_drawmode(DRMODE_SOLID);
902 rb->lcd_drawrect(LCD_WIDTH/2 - 9*w - 1, LCD_HEIGHT/2 - 4*h - 3, 37*w / 2,
903 8*h -3);
904 rb->lcd_putsxy(LCD_WIDTH/2 - 9*w, LCD_HEIGHT/2 - 4*h - 1, message);
905 rb->snprintf(str, 9, "$%d", amount);
906 rb->lcd_putsxy(LCD_WIDTH/2 - 9*w, LCD_HEIGHT/2 - 3*h, str);
907 #if (CONFIG_KEYPAD == IPOD_4G_PAD) || \
908 (CONFIG_KEYPAD == IPOD_3G_PAD) || \
909 (CONFIG_KEYPAD == IPOD_1G2G_PAD)
910 rb->lcd_putsxy(LCD_WIDTH/2 - 9*w, LCD_HEIGHT/2 - h-2, " >>|: +1");
911 rb->lcd_putsxy(LCD_WIDTH/2 - 9*w, LCD_HEIGHT/2 - 1, " |<<: -1");
912 rb->lcd_putsxy(LCD_WIDTH/2 - 9*w, LCD_HEIGHT/2 + h, "SCROLL+: +10");
913 rb->lcd_putsxy(LCD_WIDTH/2 - 9*w, LCD_HEIGHT/2 + 2*h + 1, "SCROLL-: -10");
914 #elif CONFIG_KEYPAD == IRIVER_H10_PAD
915 rb->lcd_putsxy(LCD_WIDTH/2 - 9*w, LCD_HEIGHT/2 - h-2, "RIGHT: +1");
916 rb->lcd_putsxy(LCD_WIDTH/2 - 9*w, LCD_HEIGHT/2 - 1, "LEFT: -1");
917 rb->lcd_putsxy(LCD_WIDTH/2 - 9*w, LCD_HEIGHT/2 + h, "SCROLL+: +10");
918 rb->lcd_putsxy(LCD_WIDTH/2 - 9*w, LCD_HEIGHT/2 + 2*h + 1, "SCROLL-: -10");
919 #else
920 rb->lcd_putsxy(LCD_WIDTH/2 - 9*w, LCD_HEIGHT/2 - h-2, "RIGHT: +1");
921 rb->lcd_putsxy(LCD_WIDTH/2 - 9*w, LCD_HEIGHT/2 - 1, "LEFT: -1");
922 rb->lcd_putsxy(LCD_WIDTH/2 - 9*w, LCD_HEIGHT/2 + h, "UP: +10");
923 rb->lcd_putsxy(LCD_WIDTH/2 - 9*w, LCD_HEIGHT/2 + 2*h + 1, "DOWN: -10");
924 #endif
925 rb->lcd_update_rect(LCD_WIDTH/2 - 9*w - 2, LCD_HEIGHT/2 - 9*h/2, 37*w/2 + 1,
926 8*h-2);
927 #endif
929 while(true) {
930 button = rb->button_get(true);
932 switch(button) {
933 case BJACK_UP:
934 case (BJACK_UP|BUTTON_REPEAT):
935 if (amount + 10 < upper_limit + 1) {
936 amount += 10;
937 changed = true;
939 break;
940 case BJACK_DOWN:
941 case (BJACK_DOWN|BUTTON_REPEAT):
942 if (amount - 10 > lower_limit - 1) {
943 amount -= 10;
944 changed = true;
946 break;
947 case BJACK_RIGHT:
948 case (BJACK_RIGHT|BUTTON_REPEAT):
949 if (amount + 1 < upper_limit + 1) {
950 amount++;
951 changed = true;
953 break;
954 case BJACK_LEFT:
955 case (BJACK_LEFT|BUTTON_REPEAT):
956 if (amount - 1 > lower_limit - 1) {
957 amount--;
958 changed = true;
960 break;
961 case BJACK_MAX :
962 amount = upper_limit;
963 changed = true;
964 break;
965 case BJACK_MIN :
966 amount = lower_limit;
967 changed = true;
968 break;
969 case BJACK_QUIT:
970 return 0;
971 case BJACK_START:
972 #if LCD_DEPTH > 1
973 rb->lcd_set_foreground(FG_COLOR);
974 rb->lcd_set_background(BG_COLOR);
975 #endif
976 rb->lcd_clear_display();
977 return amount;
980 if(changed) {
981 rb->snprintf(str, 9, "$%d", amount);
982 #if LCD_HEIGHT <= 64
983 rb->lcd_puts(0, 2, str);
984 rb->lcd_update();
985 #else
986 rb->lcd_set_drawmode(DRMODE_BG+DRMODE_INVERSEVID);
987 rb->lcd_fillrect(LCD_WIDTH/2 - 9*w, LCD_HEIGHT/2 - 3*h, 5*w, h);
988 rb->lcd_set_drawmode(DRMODE_SOLID);
989 rb->lcd_putsxy(LCD_WIDTH/2 - 9*w, LCD_HEIGHT/2 - 3*h, str);
990 rb->lcd_update_rect(LCD_WIDTH/2 - 9*w, LCD_HEIGHT/2 - 3*h, 5*w, h);
991 #endif
992 changed = false;
997 /*****************************************************************************
998 * blackjack_get_bet() gets the player's bet.
999 ******************************************************************************/
1000 static void blackjack_get_bet(struct game_context* bj) {
1001 bj->current_bet = blackjack_get_amount("Please enter a bet", 10,
1002 bj->player_money, bj->current_bet);
1005 /*****************************************************************************
1006 * double_down() returns one final card then finishes the game
1007 ******************************************************************************/
1008 static void double_down(struct game_context* bj) {
1009 bj->current_bet *= 2;
1010 bj->player_cards[0][bj->num_player_cards[0]] = new_card();
1011 bj->player_total += bj->player_cards[0][bj->num_player_cards[0]].value;
1012 bj->num_player_cards[0]++;
1015 /*****************************************************************************
1016 * split() checks if the player wants to split and acts accordingly.
1017 * When bj->split_status is 1, no split occurred. 2 means the player split and 3
1018 * means a split has already occurred and the first hand is done.
1019 ******************************************************************************/
1020 static void split(struct game_context* bj) {
1021 if (blackjack_get_yes_no("Split?") == 1)
1022 bj->split_status = 1;
1023 else {
1024 bj->split_status = 2;
1025 bj->current_bet *= 2;
1026 bj->num_player_cards[0] = 1;
1027 bj->num_player_cards[1] = 1;
1028 bj->player_cards[1][0] = bj->player_cards[0][1];
1029 bj->player_total = bj->player_cards[0][0].value;
1033 /*****************************************************************************
1034 * insurance() see if the player wants to buy insurance and how much.
1035 ******************************************************************************/
1036 static unsigned int insurance(struct game_context* bj) {
1037 unsigned int insurance, max_amount;
1039 insurance = blackjack_get_yes_no("Buy Insurance?");
1040 bj->asked_insurance = true;
1041 max_amount = bj->current_bet < (unsigned int)bj->player_money ?
1042 bj->current_bet/2 : (unsigned int)bj->player_money;
1043 if (insurance == 1) return 0;
1045 insurance = blackjack_get_amount("How much?", 0, max_amount, 0);
1046 redraw_board(bj);
1047 return insurance;
1050 /*****************************************************************************
1051 * play_again() checks to see if the player wants to keep playing.
1052 ******************************************************************************/
1053 static unsigned int play_again(void) {
1054 return blackjack_get_yes_no("Play Again?");
1057 /*****************************************************************************
1058 * blackjack_menu() is the initial menu at the start of the game.
1059 ******************************************************************************/
1060 static unsigned int blackjack_menu(struct game_context* bj) {
1061 int button;
1062 char *title = "Blackjack";
1063 char str[18];
1064 unsigned int i, w, h;
1065 bool breakout = false;
1066 bool showscores = false;
1068 while(true){
1069 #if LCD_DEPTH > 1
1070 rb->lcd_set_background(BG_COLOR);
1071 rb->lcd_set_foreground(FG_COLOR);
1072 #endif
1073 rb->lcd_clear_display();
1075 if(!showscores) {
1076 /* welcome screen to display key bindings */
1077 rb->lcd_getstringsize(title, &w, &h);
1078 rb->lcd_putsxy((LCD_WIDTH-w)/2, 0, title);
1080 #if CONFIG_KEYPAD == RECORDER_PAD
1081 rb->lcd_puts(0, 1, "ON: start");
1082 rb->lcd_puts(0, 2, "OFF: exit");
1083 rb->lcd_puts(0, 3, "F1: hit");
1084 rb->lcd_puts(0, 4, "F2: stay");
1085 rb->lcd_puts(0, 5, "F3: double down");
1086 rb->lcd_puts(0, 6, "PLAY: save/resume");
1087 rb->snprintf(str, 21, "High Score: $%d", bj->highscores[0]);
1088 rb->lcd_puts(0, 7, str);
1089 #elif CONFIG_KEYPAD == ONDIO_PAD
1090 rb->lcd_puts(0, 1, "MENU: start");
1091 rb->lcd_puts(0, 2, "OFF: exit");
1092 rb->lcd_puts(0, 3, "LEFT: hit");
1093 rb->lcd_puts(0, 4, "RIGHT: stay");
1094 rb->lcd_puts(0, 5, "UP: double down");
1095 rb->lcd_puts(0, 6, "DOWN: save/resume");
1096 rb->snprintf(str, 21, "High Score: $%d", bj->highscores[0]);
1097 rb->lcd_puts(0, 7, str);
1098 #elif (CONFIG_KEYPAD == IRIVER_H100_PAD) || (CONFIG_KEYPAD == IRIVER_H300_PAD)
1099 rb->lcd_puts(0, 2, "PLAY to start & to hit");
1100 rb->lcd_puts(0, 3, "STOP to exit");
1101 rb->lcd_puts(0, 4, "REC to stay");
1102 rb->lcd_puts(0, 5, "NAVI to double down ");
1103 rb->lcd_puts(0, 6, " & to view highscores");
1104 rb->lcd_puts(0, 7, "AB to save/resume");
1105 rb->snprintf(str, 21, "High Score: $%d", bj->highscores[0]);
1106 rb->lcd_puts(0, 8, str);
1107 #elif CONFIG_KEYPAD == IRIVER_H10_PAD
1108 rb->lcd_puts(0, 2, "PLAY to start & hit");
1109 rb->lcd_puts(0, 3, "POWER to exit");
1110 rb->lcd_puts(0, 4, ">>| to stay");
1111 rb->lcd_puts(0, 5, "|<< to double down");
1112 rb->lcd_puts(0, 6, "LEFT to view scores");
1113 rb->lcd_puts(0, 7, "RIGHT to save/resume");
1114 rb->snprintf(str, 21, "High Score: $%d", bj->highscores[0]);
1115 rb->lcd_puts(0, 8, str);
1117 #elif (CONFIG_KEYPAD == IPOD_4G_PAD) || \
1118 (CONFIG_KEYPAD == IPOD_3G_PAD) || \
1119 (CONFIG_KEYPAD == IPOD_1G2G_PAD)
1120 #if LCD_WIDTH >=176
1121 rb->lcd_puts(0, 2, "SELECT to start & to hit");
1122 rb->lcd_puts(0, 3, "MENU to exit");
1123 rb->lcd_puts(0, 4, ">>| to stay & to view highscores");
1124 rb->lcd_puts(0, 5, "|<< to double down");
1125 rb->lcd_puts(0, 6, "PLAY to save/resume");
1126 rb->snprintf(str, 21, "High Score: $%d", bj->highscores[0]);
1127 rb->lcd_puts(0, 7, str);
1128 #else
1129 rb->lcd_puts(0, 2, "SELECT to start & to ");
1130 rb->lcd_puts(0, 3, " hit");
1131 rb->lcd_puts(0, 4, "MENU to exit");
1132 rb->lcd_puts(0, 5, ">>| to stay & to view ");
1133 rb->lcd_puts(0, 6, " highscores");
1134 rb->lcd_puts(0, 7, "|<< to double down");
1135 rb->lcd_puts(0, 8, "PLAY to save/resume");
1136 rb->snprintf(str, 21, "High Score: $%d", bj->highscores[0]);
1137 rb->lcd_puts(0, 9, str);
1138 #endif
1139 #elif CONFIG_KEYPAD == IAUDIO_X5M5_PAD
1140 rb->lcd_puts(0, 2, "PLAY to start to hit");
1141 rb->lcd_puts(0, 3, "POWER to exit");
1142 rb->lcd_puts(0, 4, "SELECT to hit");
1143 rb->lcd_puts(0, 5, "REC to stay");
1144 rb->lcd_puts(0, 6, "PLAY to double down");
1145 rb->lcd_puts(0, 7, "RIGHT to view highscores ");
1146 rb->lcd_puts(0, 8, "DOWN to save/resume");
1147 rb->snprintf(str, 21, "High Score: $%d", bj->highscores[0]);
1148 rb->lcd_puts(0, 9, str);
1149 #elif CONFIG_KEYPAD == IRIVER_IFP7XX_PAD
1150 rb->lcd_puts(0, 2, "AB to start & to");
1151 rb->lcd_puts(0, 3, " stay");
1152 rb->lcd_puts(0, 4, "EQ to hit");
1153 rb->lcd_puts(0, 5, "PLAY to exit");
1154 rb->lcd_puts(0, 6, "CLICK to double down");
1155 rb->lcd_puts(0, 7, "& to view highscores");
1156 rb->lcd_puts(0, 8, "AB+EQ to save/resume");
1157 rb->snprintf(str, 21, "High Score: $%d", bj->highscores[0]);
1158 rb->lcd_puts(0, 9, str);
1159 #elif CONFIG_KEYPAD == GIGABEAT_PAD
1160 rb->lcd_puts(0, 2, "POWER to start");
1161 rb->lcd_puts(0, 3, "A to exit");
1162 rb->lcd_puts(0, 4, "VOL+ to hit");
1163 rb->lcd_puts(0, 5, "VOL- to stay");
1164 rb->lcd_puts(0, 6, "CENTER to double down");
1165 rb->lcd_puts(0, 6, "RIGHT to view highscores ");
1166 rb->lcd_puts(0, 8, "MENU to save/resume");
1167 rb->snprintf(str, 21, "High Score: $%d", bj->highscores[0]);
1168 rb->lcd_puts(0, 9, str);
1169 #elif (CONFIG_KEYPAD == SANSA_E200_PAD)
1170 rb->lcd_puts(0, 2, "SELECT to start & to hit");
1171 rb->lcd_puts(0, 3, "POWER to exit");
1172 rb->lcd_puts(0, 4, "RIGHT to stay");
1173 rb->lcd_puts(0, 5, "LEFT to double down");
1174 rb->lcd_puts(0, 6, "REC to save/resume");
1175 rb->lcd_puts(0, 7, "UP to view scores");
1176 rb->snprintf(str, 21, "High Score: $%d", bj->highscores[0]);
1177 rb->lcd_puts(0, 8, str);
1178 #elif (CONFIG_KEYPAD == SANSA_C200_PAD)
1179 rb->lcd_puts(0, 2, "SELECT to start & to hit");
1180 rb->lcd_puts(0, 3, "POWER to exit");
1181 rb->lcd_puts(0, 4, "RIGHT to stay");
1182 rb->lcd_puts(0, 5, "LEFT to double down");
1183 rb->lcd_puts(0, 6, "DOWN to save/resume");
1184 rb->lcd_puts(0, 7, "REC to view scores");
1185 rb->snprintf(str, 21, "High Score: $%d", bj->highscores[0]);
1186 rb->lcd_puts(0, 9, str);
1187 #elif CONFIG_KEYPAD == IAUDIO_M3_PAD
1188 rb->lcd_puts(0, 2, "PLAY to start & to");
1189 rb->lcd_puts(0, 3, " hit");
1190 rb->lcd_puts(0, 4, "REC to exit");
1191 rb->lcd_puts(0, 5, "FF to stay");
1192 rb->lcd_puts(0, 6, "REW to double down");
1193 rb->lcd_puts(0, 7, "MODE to save/resume");
1194 rb->lcd_puts(0, 8, "MENU to view scores");
1195 rb->snprintf(str, 21, "High Score: $%d", bj->highscores[0]);
1196 rb->lcd_puts(0, 10, str);
1197 #endif
1198 } else {
1199 rb->snprintf(str, 12, "%s", "High Scores");
1200 rb->lcd_getstringsize(str, &w, &h);
1201 rb->lcd_putsxy((LCD_WIDTH-w)/2, 0, str);
1203 /* print high scores */
1204 for(i=0; i<NUM_SCORES; i++) {
1205 rb->snprintf(str, 14, "#%02d: $%d", i+1, bj->highscores[i]);
1206 rb->lcd_puts(0, i+1, str);
1210 rb->lcd_update();
1212 /* handle menu button presses */
1213 button = rb->button_get(true);
1215 switch(button) {
1216 case BJACK_START: /* start playing */
1217 breakout = true;
1218 break;
1220 case BJACK_QUIT: /* quit program */
1221 if(showscores) {
1222 showscores = 0;
1223 break;
1225 return BJ_QUIT;
1227 case BJACK_RESUME:/* resume game */
1228 if(!blackjack_loadgame(bj)) {
1229 rb->splash(HZ*2, "Nothing to resume");
1230 } else {
1231 rb->splash(HZ*2, "Loading...");
1232 breakout = true;
1234 break;
1236 case BJACK_SCORES:/* toggle high scores */
1237 showscores = !showscores;
1238 break;
1240 default:
1241 if(rb->default_event_handler_ex(button, blackjack_callback,
1242 (void*) bj) == SYS_USB_CONNECTED)
1243 return BJ_USB;
1244 break;
1247 if(breakout) break;
1250 return(0);
1253 /*****************************************************************************
1254 * blackjack() is the main game subroutine, it returns the final game status.
1255 ******************************************************************************/
1256 static int blackjack(struct game_context* bj) {
1257 int button;
1258 unsigned int w, h, temp_var, done = 0, todo = 1;
1259 signed int temp;
1260 bool breakout = false;
1261 bool dbl_down = false;
1263 /* don't resume by default */
1264 bj->resume = false;
1266 /********************
1267 * menu *
1268 ********************/
1269 temp_var = blackjack_menu(bj);
1270 if (temp_var == BJ_QUIT || temp_var == BJ_USB)
1271 return temp_var;
1274 /********************
1275 * init *
1276 ********************/
1277 blackjack_init(bj);
1278 bj->current_bet=10;
1280 /********************
1281 * play *
1282 ********************/
1284 /* check for resumed game */
1285 if(bj->resume) {
1286 bj->resume = false;
1287 redraw_board(bj);
1288 if (bj->split_status == 2) {
1289 todo=2;
1290 player_x = bj->num_player_cards[0] * 10 + 4;
1292 else if (bj->split_status == 3) {
1293 player_x = bj->num_player_cards[1] * 10 + LCD_WIDTH/2 + 4;
1294 todo=2;
1295 done=1;
1299 else {
1300 bj->player_money = 1000;
1301 blackjack_get_bet(bj);
1302 if (bj->current_bet == 0)
1303 return BJ_QUIT;
1304 rb->lcd_clear_display();
1305 deal_init_cards(bj);
1306 blackjack_drawtable(bj);
1309 rb->lcd_update();
1311 breakout = false;
1313 while(true){
1314 if(bj->player_total == 21 && bj->num_player_cards[0] == 2) {
1315 bj->is_blackjack = true;
1316 bj->end_hand = true;
1317 finish_game(bj);
1319 else if(bj->dealer_cards[1].is_soft_ace && !breakout &&
1320 !bj->asked_insurance) {
1321 temp_var = insurance(bj);
1322 if (bj->dealer_total == 21) {
1323 rb->splash(HZ, "Dealer has blackjack");
1324 bj->player_money += temp_var;
1325 bj->end_hand = true;
1326 breakout = true;
1327 redraw_board(bj);
1328 finish_game(bj);
1330 else {
1331 rb->splash(HZ, "Dealer does not have blackjack");
1332 bj->player_money -= temp_var;
1333 breakout = true;
1334 redraw_board(bj);
1335 rb->lcd_update();
1338 if(bj->split_status == 0 &&
1339 bj->player_cards[0][0].num == bj->player_cards[0][1].num) {
1340 split(bj);
1341 redraw_board(bj);
1342 rb->lcd_update_rect(0, LCD_HEIGHT/2, LCD_WIDTH, LCD_HEIGHT/2);
1343 if (bj->split_status == 2) {
1344 todo++;
1345 player_x = bj->num_player_cards[0] * 10 + 4;
1349 while(done < todo) {
1350 button = rb->button_get(true);
1352 switch(button) {
1353 case BJACK_HIT:
1354 NEXT_CARD = new_card();
1355 bj->player_total += NEXT_CARD.value;
1356 draw_card(NEXT_CARD, true, player_x, player_y);
1357 bj->num_player_cards[done]++;
1358 if (bj->num_player_cards[done] == MAX_CARDS + 1) {
1359 redraw_board(bj);
1360 rb->lcd_update_rect(0, LCD_HEIGHT/2, LCD_WIDTH,
1361 LCD_HEIGHT/2);
1363 else if (bj->num_player_cards[done]>MAX_CARDS || todo > 1) {
1364 rb->lcd_update_rect(player_x, player_y, CARD_WIDTH+2,
1365 CARD_HEIGHT+2);
1366 player_x += 10;
1368 else {
1369 rb->lcd_update_rect(player_x, player_y, CARD_WIDTH+2,
1370 CARD_HEIGHT+2);
1371 player_x += CARD_WIDTH + 4;
1373 update_total(bj);
1375 break;
1376 case BJACK_STAY:
1377 bj->end_hand = true;
1378 break;
1379 case BJACK_DOUBLEDOWN:
1380 if ((signed int)bj->current_bet * 2 < bj->player_money + 1 &&
1381 bj->num_player_cards[0]==2 && todo==1) {
1382 double_down(bj);
1383 dbl_down = true;
1384 if (bj->player_total < 22) {
1385 bj->end_hand = true;
1386 finish_game(bj);
1389 else if((signed int)bj->current_bet * 2 > bj->player_money) {
1390 rb->splash(HZ, "Not enough money to double down.");
1391 redraw_board(bj);
1392 rb->lcd_update();
1394 break;
1395 case BJACK_RESUME: /* save and end game */
1396 rb->splash(HZ, "Saving game...");
1397 blackjack_savegame(bj);
1398 /* fall through to BJACK_QUIT */
1400 case BJACK_QUIT:
1401 return BJ_END;
1404 while (bj->player_total > 21 && !bj->end_hand) {
1405 temp = check_for_aces(bj->player_cards[done],
1406 bj->num_player_cards[done]);
1407 if(temp != -1) {
1408 bj->player_cards[done][temp].is_soft_ace = false;
1409 bj->player_total -= 10;
1410 update_total(bj);
1411 if (dbl_down) {
1412 bj->end_hand = true;
1413 finish_game(bj);
1416 else
1417 bj->end_hand = true;
1420 if (bj->end_hand) {
1421 done++;
1422 if(todo > 1) {
1423 if (done == 2) {
1424 temp = bj->player_total;
1425 bj->player_total = temp_var;
1426 temp_var = temp;
1427 finish_game(bj);
1428 rb->lcd_getstringsize(" Split 1 ", &w, &h);
1429 rb->lcd_putsxy(LCD_WIDTH/2-w/2, LCD_HEIGHT/2-3*h/2,
1430 " Split 1 ");
1431 rb->lcd_update_rect(LCD_WIDTH/2-w/2, LCD_HEIGHT/2-3*h/2,
1432 w,h);
1433 bj->current_bet /= 2;
1434 rb->lcd_update_rect(LCD_WIDTH/2-w/2, LCD_HEIGHT/2-3*h/2,
1435 w,h);
1436 rb->sleep(HZ*2);
1437 bj->player_total = temp_var;
1438 finish_game(bj);
1439 rb->lcd_getstringsize(" Split 2 ", &w, &h);
1440 rb->lcd_putsxy(LCD_WIDTH/2-w/2, LCD_HEIGHT/2-3*h/2,
1441 " Split 2 ");
1442 rb->lcd_update_rect(LCD_WIDTH/2-w/2, LCD_HEIGHT/2-3*h/2,
1443 w,h);
1444 rb->sleep(HZ*2);
1446 else {
1447 bj->end_hand = false;
1448 bj->split_status = 3;
1449 temp_var = bj->player_total;
1450 bj->player_total = bj->player_cards[1][0].value;
1451 update_total(bj);
1452 redraw_board(bj);
1453 player_x += 10;
1454 rb->lcd_update();
1457 else
1458 finish_game(bj);
1462 if (bj->player_money < 10) {
1463 rb->sleep(HZ);
1464 return BJ_LOSE;
1467 if (bj->end_hand) { /* If hand is over */
1468 if (play_again() != 0) /* User wants to quit */
1469 return BJ_END;
1470 else { /* User keeps playing */
1471 breakout = false;
1472 redraw_board(bj);
1473 if(dbl_down) {
1474 bj->current_bet /= 2;
1475 dbl_down = false;
1477 done = 0;
1478 todo = 1;
1479 blackjack_init(bj);
1480 blackjack_get_bet(bj);
1481 if (bj->current_bet == 0)
1482 return BJ_END;
1483 deal_init_cards(bj);
1484 blackjack_drawtable(bj);
1485 rb->lcd_update();
1489 /* Never reached */
1490 return PLUGIN_OK;
1493 /*****************************************************************************
1494 * plugin entry point.
1495 ******************************************************************************/
1496 enum plugin_status plugin_start(struct plugin_api* api, void* parameter) {
1497 struct game_context bj;
1498 bool exit = false;
1499 unsigned int position;
1500 char str[19];
1502 (void)parameter;
1503 rb = api;
1505 #if LCD_DEPTH > 1
1506 rb->lcd_set_backdrop(NULL);
1507 #endif
1509 /* load high scores */
1510 blackjack_loadscores(&bj);
1512 rb->lcd_setfont(FONT_SYSFIXED);
1514 while(!exit) {
1515 switch(blackjack(&bj)){
1516 case BJ_LOSE:
1517 rb->splash(HZ, "Not enough money to continue");
1518 /* fall through to BJ_END */
1520 case BJ_END:
1521 if(!bj.resume) {
1522 if((position = blackjack_recordscore(&bj))) {
1523 rb->snprintf(str, 19, "New high score #%d!", position);
1524 rb->splash(HZ*2, str);
1527 break;
1529 case BJ_USB:
1530 rb->lcd_setfont(FONT_UI);
1531 return PLUGIN_USB_CONNECTED;
1533 case BJ_QUIT:
1534 if(bj.dirty) {
1535 rb->splash(HZ, "Saving high scores...");
1536 blackjack_savescores(&bj);
1538 exit = true;
1539 break;
1541 default:
1542 break;
1546 rb->lcd_setfont(FONT_UI);
1547 return PLUGIN_OK;