fix FS#8701 - metronome doesnt change back to ui font after drawing
[Rockbox.git] / apps / plugins / blackjack.c
blobb4f0da4b965dd095dc7135aee0ac96fa4638241e
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 #else
238 #error No keymap defined!
239 #endif
241 #ifdef HAVE_LCD_COLOR
242 #define BG_COLOR LCD_RGBPACK(0,157,0)
243 #define FG_COLOR LCD_WHITE
244 #elif LCD_DEPTH > 1
245 #define BG_COLOR LCD_WHITE
246 #define FG_COLOR LCD_BLACK
247 #endif
249 #define CARD_WIDTH BMPWIDTH_card_back
250 #define CARD_HEIGHT BMPHEIGHT_card_back
252 /* This is the max amount of cards onscreen before condensing */
253 #define MAX_CARDS LCD_WIDTH/(CARD_WIDTH+4)
255 extern const fb_data card_deck[];
256 extern const fb_data card_back[];
258 #define NEXT_CARD bj->player_cards[done][bj->num_player_cards[done]]
260 /* global rockbox api */
261 static struct plugin_api* rb;
263 MEM_FUNCTION_WRAPPERS(rb);
265 /* dealer and player card positions */
266 unsigned int dealer_x, dealer_y, player_x, player_y;
268 typedef struct card {
269 unsigned int value; /* Card's value in Blackjack */
270 unsigned int num; /* Value on card face 0-12 (0=Ace, 1=2, 11=Q) */
271 unsigned int suit; /* 0:Spades, 1:Hearts, 2: Clubs; 3: Diamonds */
272 bool is_soft_ace;
273 } card;
275 typedef struct game_context {
276 struct card player_cards[2][22]; /* 22 Cards means the deal was all aces */
277 struct card dealer_cards[22]; /* That is the worst-case scenario */
278 unsigned int player_total;
279 unsigned int dealer_total;
280 signed int player_money;
281 unsigned int num_player_cards[2];
282 unsigned int num_dealer_cards;
283 unsigned int current_bet;
284 unsigned int split_status; /* 0 = split hasn't been asked, *
285 * 1 = split did not occur *
286 * 2 = split occurred *
287 * 3 = split occurred and 1st hand done */
288 bool is_blackjack;
289 bool end_hand;
290 bool asked_insurance;
291 signed short highscores[NUM_SCORES];
292 bool resume;
293 bool dirty;
294 } game_context;
296 /*****************************************************************************
297 * blackjack_init() initializes blackjack data structures.
298 ******************************************************************************/
299 static void blackjack_init(struct game_context* bj) {
300 /* seed the rand generator */
301 rb->srand(*rb->current_tick);
303 /* reset card positions */
304 dealer_x = 4;
305 dealer_y = LCD_HEIGHT/4 - CARD_HEIGHT/2;
306 player_x = 4;
307 player_y = LCD_HEIGHT - LCD_HEIGHT/4 - CARD_HEIGHT/2;
309 /* check for resumed game */
310 if(bj->resume) return;
312 /* reset scoring */
313 bj->player_total = 0;
314 bj->dealer_total = 0;
315 bj->num_player_cards[0] = 2;
316 bj->num_player_cards[1] = 0;
317 bj->num_dealer_cards = 2;
318 bj->end_hand = false;
319 bj->split_status = 0;
320 bj->is_blackjack = false;
321 bj->asked_insurance = false;
324 /*****************************************************************************
325 * blackjack_drawtable() draws the table and some text.
326 ******************************************************************************/
327 static void blackjack_drawtable(struct game_context* bj) {
328 unsigned int w, h, y_loc;
329 char str[10];
331 #if LCD_HEIGHT <= 64
332 rb->lcd_getstringsize("Bet", &w, &h);
333 rb->lcd_putsxy(LCD_WIDTH - w, 2*h + 1, "Bet");
334 rb->snprintf(str, 9, "$%d", bj->current_bet);
335 rb->lcd_getstringsize(str, &w, &h);
336 rb->lcd_putsxy(LCD_WIDTH - w, 3*h + 1, str);
337 y_loc = LCD_HEIGHT/2;
338 #else
339 rb->lcd_getstringsize("Bet", &w, &h);
340 rb->lcd_putsxy(LCD_WIDTH - w, 5*h / 2, "Bet");
341 rb->snprintf(str, 9, "$%d", bj->current_bet);
342 rb->lcd_getstringsize(str, &w, &h);
343 rb->lcd_putsxy(LCD_WIDTH - w, 7*h / 2, str);
344 rb->lcd_hline(0, LCD_WIDTH, LCD_HEIGHT/2);
345 y_loc = LCD_HEIGHT/2 + h;
346 #endif
348 rb->lcd_putsxy(0,0, "Dealer");
349 rb->lcd_getstringsize("Player", &w, &h);
350 rb->lcd_putsxy(0, y_loc, "Player");
351 rb->lcd_getstringsize("Total", &w, &h);
352 rb->lcd_putsxy(LCD_WIDTH - w, y_loc, "Total");
353 rb->lcd_getstringsize("Money", &w, &h);
354 rb->lcd_putsxy(LCD_WIDTH - w, 0, "Money");
355 rb->snprintf(str, 9, "$%d", bj->player_money - bj->current_bet);
356 rb->lcd_getstringsize(str, &w, &h);
357 rb->lcd_putsxy(LCD_WIDTH - w, h + 1, str);
358 rb->snprintf(str, 3, "%d", bj->player_total);
359 rb->lcd_getstringsize(str, &w, &h);
360 rb->lcd_putsxy(LCD_WIDTH - w, y_loc + h, str);
363 /*****************************************************************************
364 * find_value() is passed a card and returns its blackjack value.
365 ******************************************************************************/
366 static unsigned int find_value(unsigned int number) {
367 unsigned int thisValue;
368 if (number == 0)
369 thisValue = 11; /* Aces get a value of 11 at first */
370 else if (number < 10)
371 thisValue = number + 1;
372 else
373 thisValue = 10; /* Anything 10 or higher gets a value of 10 */
375 return thisValue;
378 /*****************************************************************************
379 * draw_card() draws a card to the screen.
380 ******************************************************************************/
381 static void draw_card(struct card temp_card, bool shown, unsigned int x,
382 unsigned int y) {
383 if(shown)
384 rb->lcd_bitmap_part(card_deck, CARD_WIDTH*temp_card.num,
385 CARD_HEIGHT*temp_card.suit, BMPWIDTH_card_deck,
386 x+1, y+1, CARD_WIDTH, CARD_HEIGHT);
387 else
388 rb->lcd_bitmap(card_back, x+1, y+1,CARD_WIDTH, CARD_HEIGHT);
389 #if LCD_DEPTH > 1
390 rb->lcd_set_foreground(LCD_BLACK);
391 #endif
393 /* Print outlines */
394 #if CARD_WIDTH >= 26
395 rb->lcd_hline(x+2, x+CARD_WIDTH-1, y);
396 rb->lcd_hline(x+2, x+CARD_WIDTH-1, y+CARD_HEIGHT+1);
397 rb->lcd_vline(x, y+2, y+CARD_HEIGHT-3);
398 rb->lcd_vline(x+CARD_WIDTH+1, y+2, y+CARD_HEIGHT-1);
399 rb->lcd_drawpixel(x+1, y+1);
400 rb->lcd_drawpixel(x+1, y+CARD_HEIGHT);
401 rb->lcd_drawpixel(x+CARD_WIDTH, y+1);
402 rb->lcd_drawpixel(x+CARD_WIDTH, y+CARD_HEIGHT);
403 #else
404 rb->lcd_hline(x+1, x+CARD_WIDTH, y);
405 rb->lcd_hline(x+1, x+CARD_WIDTH, y+CARD_HEIGHT+1);
406 rb->lcd_vline(x, y+1, y+CARD_HEIGHT);
407 rb->lcd_vline(x+CARD_WIDTH+1, y+1, y+CARD_HEIGHT);
408 #endif
410 #if LCD_DEPTH > 1
411 rb->lcd_set_foreground(FG_COLOR);
412 #endif
415 /*****************************************************************************
416 * new_card() initializes a new card and gives it values.
417 ******************************************************************************/
418 static struct card new_card(void) {
419 struct card new_card;
420 new_card.suit = rb->rand()%4; /* Random number 0-3 */
421 new_card.num = rb->rand()%13; /* Random number 0-12 */
422 new_card.value = find_value(new_card.num);
423 new_card.is_soft_ace = new_card.num == 0 ? true : false;
424 return new_card;
427 /*****************************************************************************
428 * deal_init_card() deals and draws to the screen the player's and dealer's
429 * initial cards.
430 ******************************************************************************/
431 static void deal_init_cards(struct game_context* bj) {
432 bj->dealer_cards[0] = new_card();
433 bj->dealer_total += bj->dealer_cards[0].value;
435 draw_card(bj->dealer_cards[0], false, dealer_x, dealer_y);
437 bj->dealer_cards[1] = new_card();
438 bj->dealer_total += bj->dealer_cards[1].value;
439 draw_card(bj->dealer_cards[1], true, dealer_x + CARD_WIDTH + 4, dealer_y);
441 bj->player_cards[0][0] = new_card();
442 bj->player_total += bj->player_cards[0][0].value;
443 draw_card(bj->player_cards[0][0], true, player_x, player_y);
444 player_x += CARD_WIDTH + 4;
446 bj->player_cards[0][1] = new_card();
447 bj->player_total += bj->player_cards[0][1].value;
448 draw_card(bj->player_cards[0][1], true, player_x, player_y);
449 player_x += CARD_WIDTH + 4;
452 /*****************************************************************************
453 * redraw_board() redraws all the cards and the board
454 ******************************************************************************/
455 static void redraw_board(struct game_context* bj) {
456 unsigned int i, n, upper_bound;
457 rb->lcd_clear_display();
459 blackjack_drawtable(bj);
460 player_x = 4;
461 dealer_x = 4;
462 upper_bound = bj->split_status > 1 ? 2 : 1;
464 for (i = 0; i < bj->num_dealer_cards; i++) {
465 if (!bj->end_hand) {
466 draw_card(bj->dealer_cards[0], false, dealer_x, dealer_y);
468 /* increment i so the dealer's first card isn't displayed */
469 i++;
470 dealer_x += CARD_WIDTH + 4;
472 draw_card(bj->dealer_cards[i], true, dealer_x, dealer_y);
474 if (bj->num_dealer_cards > MAX_CARDS-1)
475 dealer_x += 10;
476 else
477 dealer_x += CARD_WIDTH + 4;
480 for (n = 0; n < upper_bound; n++) {
481 for (i = 0; i < bj->num_player_cards[n]; i++) {
482 draw_card(bj->player_cards[n][i], true, player_x, player_y);
483 if (bj->split_status>1 || bj->num_player_cards[n]>MAX_CARDS)
484 player_x += 10;
485 else
486 player_x += CARD_WIDTH + 4;
488 if (bj->split_status > 1)
489 player_x = LCD_WIDTH/2 + 4;
493 /*****************************************************************************
494 * update_total updates the player's total
495 ******************************************************************************/
496 static void update_total(struct game_context* bj) {
497 char total[3];
498 unsigned int w, h;
499 rb->snprintf(total, 3, "%d", bj->player_total);
500 rb->lcd_getstringsize(total, &w, &h);
501 #if LCD_HEIGHT > 64
502 h *= 2;
503 #endif
504 rb->lcd_putsxy(LCD_WIDTH - w, LCD_HEIGHT/2 + h, total);
505 rb->lcd_update_rect(LCD_WIDTH - w, LCD_HEIGHT/2 + h, w, h);
509 /*****************************************************************************
510 * check_for_aces() is passed an array of cards and returns where an ace is
511 * located. Otherwise, returns -1.
512 ******************************************************************************/
513 static signed int check_for_aces(struct card temp_cards[],
514 unsigned int size) {
515 unsigned int i;
516 for(i = 0; i < size; i++) {
517 if (temp_cards[i].is_soft_ace == true)
518 return i;
520 return -1;
523 /*****************************************************************************
524 * check_totals() compares player and dealer totals.
525 * 0: bust 1: loss, 2: push, 3: win, 4: blackjack, 5: something's not right...
526 ******************************************************************************/
527 static unsigned int check_totals(struct game_context* bj)
529 unsigned int temp;
530 if (bj->player_total > 21)
531 temp = 0;
532 else if (bj->player_total == 21 && bj->is_blackjack)
533 if (bj->dealer_total == 21 && bj->num_dealer_cards == 2)
534 temp = 2;
535 else
536 temp = 4;
537 else if (bj->player_total == bj->dealer_total)
538 temp = 2;
539 else if (bj->dealer_total > 21 && bj->player_total < 22)
540 temp = 3;
541 else if (bj->dealer_total > bj->player_total)
542 temp = 1;
543 else if (bj->player_total > bj->dealer_total)
544 temp = 3;
545 else
546 temp = 5;
548 return temp;
551 /*****************************************************************************
552 * finish_dealer() draws cards for the dealer until he has 17 or more.
553 ******************************************************************************/
554 static void finish_dealer(struct game_context* bj) {
555 signed int temp = 0;
557 if (bj->dealer_total > 16 && bj->dealer_total < 22)
558 return;
560 while (bj->dealer_total < 17) {
561 bj->dealer_cards[bj->num_dealer_cards] = new_card();
562 bj->dealer_total += bj->dealer_cards[bj->num_dealer_cards].value;
563 bj->num_dealer_cards++;
566 while (bj->dealer_total > 21) {
567 temp = check_for_aces(bj->dealer_cards, bj->num_dealer_cards);
568 if(temp != -1) {
569 bj->dealer_cards[temp].is_soft_ace = false;
570 bj->dealer_total -= 10;
572 else
573 return;
577 /*****************************************************************************
578 * finish_game() completes the game once player's turn is over.
579 ******************************************************************************/
580 static void finish_game(struct game_context* bj) {
581 unsigned int rValue, w, h;
582 char str[19];
584 do {
585 finish_dealer(bj);
586 } while (bj->dealer_total < 17);
588 redraw_board(bj);
589 rValue = check_totals(bj);
591 if (rValue == 0) {
592 rb->snprintf(str, sizeof(str), " Bust! ");
593 bj->player_money -= bj->current_bet;
595 else if (rValue == 1) {
596 rb->snprintf(str, sizeof(str), " Sorry, you lost. ");
597 bj->player_money -= bj->current_bet;
599 else if (rValue == 2) {
600 rb->snprintf(str, sizeof(str), " Push ");
602 else if (rValue == 3) {
603 rb->snprintf(str, sizeof(str), " You won! ");
604 bj->player_money+= bj->current_bet;
606 else {
607 rb->snprintf(str, sizeof(str), " Blackjack! ");
608 bj->player_money += bj->current_bet * 3 / 2;
610 rb->lcd_getstringsize(str, &w, &h);
612 #if LCD_HEIGHT <= 64
613 rb->lcd_set_drawmode(DRMODE_BG+DRMODE_INVERSEVID);
614 rb->lcd_fillrect(0, LCD_HEIGHT/2, LCD_WIDTH, LCD_HEIGHT/2);
615 rb->lcd_set_drawmode(DRMODE_SOLID);
616 rb->lcd_putsxy(LCD_WIDTH/2 - w/2, LCD_HEIGHT/2 + h, str);
617 rb->snprintf(str, 12, "You have %d", bj->player_total);
618 rb->lcd_getstringsize(str, &w, &h);
619 rb->lcd_putsxy(LCD_WIDTH/2 - w/2, LCD_HEIGHT/2, str);
620 #else
621 rb->lcd_putsxy(LCD_WIDTH/2 - w/2, LCD_HEIGHT/2 - h/2, str);
622 #endif
623 rb->lcd_update();
626 /*****************************************************************************
627 * blackjack_recordscore() inserts a high score into the high scores list and
628 * returns the high score position.
629 ******************************************************************************/
630 static unsigned int blackjack_recordscore(struct game_context* bj) {
631 unsigned int i;
632 unsigned int position = 0;
633 signed short current, temp;
635 /* calculate total score */
636 current = bj->player_money;
637 if(current <= 10) return 0;
639 /* insert the current score into the high scores */
640 for(i=0; i<NUM_SCORES; i++) {
641 if(current >= bj->highscores[i]) {
642 if(!position) {
643 position = i+1;
644 bj->dirty = true;
646 temp = bj->highscores[i];
647 bj->highscores[i] = current;
648 current = temp;
652 return position;
655 /*****************************************************************************
656 * blackjack_loadscores() loads the high scores saved file.
657 ******************************************************************************/
658 static void blackjack_loadscores(struct game_context* bj) {
659 signed int fd;
661 bj->dirty = false;
663 /* clear high scores */
664 rb->memset(bj->highscores, 0, sizeof(bj->highscores));
666 /* open scores file */
667 fd = rb->open(SCORE_FILE, O_RDONLY);
668 if(fd < 0) return;
670 /* read in high scores */
671 if(rb->read(fd, bj->highscores, sizeof(bj->highscores)) <= 0) {
672 /* scores are bad, reset */
673 rb->memset(bj->highscores, 0, sizeof(bj->highscores));
676 rb->close(fd);
679 /*****************************************************************************
680 * blackjack_savescores() saves the high scores saved file.
681 ******************************************************************************/
682 static void blackjack_savescores(struct game_context* bj) {
683 unsigned int fd;
685 /* write out the high scores to the save file */
686 fd = rb->open(SCORE_FILE, O_WRONLY|O_CREAT);
687 rb->write(fd, bj->highscores, sizeof(bj->highscores));
688 rb->close(fd);
689 bj->dirty = false;
692 /*****************************************************************************
693 * blackjack_loadgame() loads the saved game and returns load success.
694 ******************************************************************************/
695 static bool blackjack_loadgame(struct game_context* bj) {
696 signed int fd;
697 bool loaded = false;
699 /* open game file */
700 fd = rb->open(SAVE_FILE, O_RDONLY);
701 if(fd < 0) return loaded;
703 /* read in saved game */
704 while(true) {
705 if(rb->read(fd, &bj->player_money, sizeof(bj->player_money)) <= 0) break;
706 if(rb->read(fd, &bj->player_total, sizeof(bj->player_total)) <= 0) break;
707 if(rb->read(fd, &bj->dealer_total, sizeof(bj->dealer_total)) <= 0) break;
708 if(rb->read(fd, &bj->num_player_cards, sizeof(bj->num_player_cards))<=0)
709 break;
710 if(rb->read(fd, &bj->num_dealer_cards, sizeof(bj->num_dealer_cards))<=0)
711 break;
712 if(rb->read(fd, &bj->current_bet, sizeof(bj->current_bet)) <= 0) break;
713 if(rb->read(fd, &bj->is_blackjack, sizeof(bj->is_blackjack)) <= 0) break;
714 if(rb->read(fd, &bj->split_status, sizeof(bj->split_status)) <= 0) break;
715 if(rb->read(fd, &bj->asked_insurance, sizeof(bj->asked_insurance)) <= 0)
716 break;
717 if(rb->read(fd, &bj->end_hand, sizeof(bj->end_hand)) <= 0) break;
718 if(rb->read(fd, &bj->player_cards, sizeof(bj->player_cards)) <= 0) break;
719 if(rb->read(fd, &bj->dealer_cards, sizeof(bj->dealer_cards)) <= 0) break;
720 bj->resume = true;
721 loaded = true;
722 break;
725 rb->close(fd);
727 /* delete saved file */
728 rb->remove(SAVE_FILE);
729 return loaded;
732 /*****************************************************************************
733 * blackjack_savegame() saves the current game state.
734 ******************************************************************************/
735 static void blackjack_savegame(struct game_context* bj) {
736 unsigned int fd;
738 /* write out the game state to the save file */
739 fd = rb->open(SAVE_FILE, O_WRONLY|O_CREAT);
740 rb->write(fd, &bj->player_money, sizeof(bj->player_money));
741 rb->write(fd, &bj->player_total, sizeof(bj->player_total));
742 rb->write(fd, &bj->dealer_total, sizeof(bj->dealer_total));
743 rb->write(fd, &bj->num_player_cards, sizeof(bj->num_player_cards));
744 rb->write(fd, &bj->num_dealer_cards, sizeof(bj->num_dealer_cards));
745 rb->write(fd, &bj->current_bet, sizeof(bj->current_bet));
746 rb->write(fd, &bj->is_blackjack, sizeof(bj->is_blackjack));
747 rb->write(fd, &bj->split_status, sizeof(bj->split_status));
748 rb->write(fd, &bj->asked_insurance, sizeof(bj->asked_insurance));
749 rb->write(fd, &bj->end_hand, sizeof(bj->end_hand));
750 rb->write(fd, &bj->player_cards, sizeof(bj->player_cards));
751 rb->write(fd, &bj->dealer_cards, sizeof(bj->dealer_cards));
752 rb->close(fd);
754 bj->resume = true;
757 /*****************************************************************************
758 * blackjack_callback() is the default event handler callback which is called
759 * on usb connect and shutdown.
760 ******************************************************************************/
761 static void blackjack_callback(void* param) {
762 struct game_context* bj = (struct game_context*) param;
763 if(bj->dirty) {
764 rb->splash(HZ, "Saving high scores...");
765 blackjack_savescores(bj);
769 /*****************************************************************************
770 * blackjack_get_yes_no() gets a yes/no answer from the user
771 ******************************************************************************/
772 static unsigned int blackjack_get_yes_no(char message[20]) {
773 int button;
774 unsigned int w, h, b, choice = 0;
775 bool breakout = false;
776 char message_yes[24], message_no[24];
778 rb->strcpy(message_yes, message);
779 rb->strcpy(message_no, message);
780 rb->strcat(message_yes, " Yes");
781 rb->strcat(message_no, " No");
782 rb->lcd_getstringsize(message_yes, &w, &h);
783 const char *stg[] = {message_yes, message_no};
785 #if LCD_HEIGHT <= 64
786 b = 2*h+1;
787 #else
788 b = h-1;
789 #endif
791 #ifdef HAVE_LCD_COLOR
792 rb->lcd_fillrect(LCD_WIDTH/2 - w/2, LCD_HEIGHT/2 + b, w+1, h+3);
793 rb->lcd_set_foreground(LCD_BLACK);
794 rb->lcd_set_background(LCD_WHITE);
795 #else
796 rb->lcd_set_drawmode(DRMODE_BG+DRMODE_INVERSEVID);
797 rb->lcd_fillrect(LCD_WIDTH/2 - w/2, LCD_HEIGHT/2 + b, w+1, h+3);
798 rb->lcd_set_drawmode(DRMODE_SOLID);
799 #endif
800 rb->lcd_drawrect(LCD_WIDTH/2 - w/2 - 1, LCD_HEIGHT/2 + b - 1, w+3, h+4);
802 while(!breakout) {
803 rb->lcd_putsxy(LCD_WIDTH/2 - w/2, LCD_HEIGHT/2 + b +1, stg[choice]);
804 rb->lcd_update_rect(LCD_WIDTH/2 - w/2 - 1, LCD_HEIGHT/2 + b -1,
805 w+3, h+4);
806 button = rb->button_get(true);
808 switch(button) {
809 case BJACK_LEFT:
810 case (BJACK_LEFT|BUTTON_REPEAT):
811 case BJACK_RIGHT:
812 case (BJACK_RIGHT|BUTTON_REPEAT):
813 choice ^= 1;
814 break;
815 case BJACK_START: breakout = true;
816 break;
817 case BJACK_QUIT: breakout = true;
818 choice = BJ_QUIT;
819 break;
823 #if LCD_DEPTH > 1
824 rb->lcd_set_foreground(FG_COLOR);
825 rb->lcd_set_background(BG_COLOR);
826 #endif
827 return choice;
830 /*****************************************************************************
831 * blackjack_get_amount() gets an amount from the player to be used
832 ******************************************************************************/
833 static signed int blackjack_get_amount(char message[20], signed int lower_limit,
834 signed int upper_limit,
835 signed int start) {
836 int button;
837 char str[6];
838 bool changed = false;
839 unsigned int w, h;
840 signed int amount;
842 rb->lcd_getstringsize("A", &w, &h); /* find the size of one character */
844 if (start > upper_limit)
845 amount = upper_limit;
846 else if (start < lower_limit)
847 amount = lower_limit;
848 else
849 amount = start;
851 #if LCD_DEPTH > 1
852 rb->lcd_set_background(LCD_WHITE);
853 rb->lcd_set_foreground(LCD_BLACK);
854 #endif
856 #if LCD_HEIGHT <= 64
857 rb->lcd_clear_display();
858 rb->lcd_puts(0, 1, message);
859 rb->snprintf(str, 9, "$%d", amount);
860 rb->lcd_puts(0, 2, str);
861 rb->lcd_puts(0, 3, "RIGHT: +1");
862 rb->lcd_puts(0, 4, "LEFT: -1");
863 rb->lcd_puts(0, 5, "UP: +10");
864 rb->lcd_puts(0, 6, "DOWN: -10");
865 rb->lcd_update();
866 #else
867 rb->lcd_set_drawmode(DRMODE_BG+DRMODE_INVERSEVID);
868 rb->lcd_fillrect(LCD_WIDTH/2 - 9*w - 1, LCD_HEIGHT/2 - 4*h - 3, 37*w / 2,
869 8*h -3);
870 rb->lcd_set_drawmode(DRMODE_SOLID);
871 rb->lcd_drawrect(LCD_WIDTH/2 - 9*w - 1, LCD_HEIGHT/2 - 4*h - 3, 37*w / 2,
872 8*h -3);
873 rb->lcd_putsxy(LCD_WIDTH/2 - 9*w, LCD_HEIGHT/2 - 4*h - 1, message);
874 rb->snprintf(str, 9, "$%d", amount);
875 rb->lcd_putsxy(LCD_WIDTH/2 - 9*w, LCD_HEIGHT/2 - 3*h, str);
876 #if (CONFIG_KEYPAD == IPOD_4G_PAD) || \
877 (CONFIG_KEYPAD == IPOD_3G_PAD) || \
878 (CONFIG_KEYPAD == IPOD_1G2G_PAD)
879 rb->lcd_putsxy(LCD_WIDTH/2 - 9*w, LCD_HEIGHT/2 - h-2, " >>|: +1");
880 rb->lcd_putsxy(LCD_WIDTH/2 - 9*w, LCD_HEIGHT/2 - 1, " |<<: -1");
881 rb->lcd_putsxy(LCD_WIDTH/2 - 9*w, LCD_HEIGHT/2 + h, "SCROLL+: +10");
882 rb->lcd_putsxy(LCD_WIDTH/2 - 9*w, LCD_HEIGHT/2 + 2*h + 1, "SCROLL-: -10");
883 #elif CONFIG_KEYPAD == IRIVER_H10_PAD
884 rb->lcd_putsxy(LCD_WIDTH/2 - 9*w, LCD_HEIGHT/2 - h-2, "RIGHT: +1");
885 rb->lcd_putsxy(LCD_WIDTH/2 - 9*w, LCD_HEIGHT/2 - 1, "LEFT: -1");
886 rb->lcd_putsxy(LCD_WIDTH/2 - 9*w, LCD_HEIGHT/2 + h, "SCROLL+: +10");
887 rb->lcd_putsxy(LCD_WIDTH/2 - 9*w, LCD_HEIGHT/2 + 2*h + 1, "SCROLL-: -10");
888 #else
889 rb->lcd_putsxy(LCD_WIDTH/2 - 9*w, LCD_HEIGHT/2 - h-2, "RIGHT: +1");
890 rb->lcd_putsxy(LCD_WIDTH/2 - 9*w, LCD_HEIGHT/2 - 1, "LEFT: -1");
891 rb->lcd_putsxy(LCD_WIDTH/2 - 9*w, LCD_HEIGHT/2 + h, "UP: +10");
892 rb->lcd_putsxy(LCD_WIDTH/2 - 9*w, LCD_HEIGHT/2 + 2*h + 1, "DOWN: -10");
893 #endif
894 rb->lcd_update_rect(LCD_WIDTH/2 - 9*w - 2, LCD_HEIGHT/2 - 9*h/2, 37*w/2 + 1,
895 8*h-2);
896 #endif
898 while(true) {
899 button = rb->button_get(true);
901 switch(button) {
902 case BJACK_UP:
903 case (BJACK_UP|BUTTON_REPEAT):
904 if (amount + 10 < upper_limit + 1) {
905 amount += 10;
906 changed = true;
908 break;
909 case BJACK_DOWN:
910 case (BJACK_DOWN|BUTTON_REPEAT):
911 if (amount - 10 > lower_limit - 1) {
912 amount -= 10;
913 changed = true;
915 break;
916 case BJACK_RIGHT:
917 case (BJACK_RIGHT|BUTTON_REPEAT):
918 if (amount + 1 < upper_limit + 1) {
919 amount++;
920 changed = true;
922 break;
923 case BJACK_LEFT:
924 case (BJACK_LEFT|BUTTON_REPEAT):
925 if (amount - 1 > lower_limit - 1) {
926 amount--;
927 changed = true;
929 break;
930 case BJACK_MAX :
931 amount = upper_limit;
932 changed = true;
933 break;
934 case BJACK_MIN :
935 amount = lower_limit;
936 changed = true;
937 break;
938 case BJACK_QUIT:
939 return 0;
940 case BJACK_START:
941 #if LCD_DEPTH > 1
942 rb->lcd_set_foreground(FG_COLOR);
943 rb->lcd_set_background(BG_COLOR);
944 #endif
945 rb->lcd_clear_display();
946 return amount;
949 if(changed) {
950 rb->snprintf(str, 9, "$%d", amount);
951 #if LCD_HEIGHT <= 64
952 rb->lcd_puts(0, 2, str);
953 rb->lcd_update();
954 #else
955 rb->lcd_set_drawmode(DRMODE_BG+DRMODE_INVERSEVID);
956 rb->lcd_fillrect(LCD_WIDTH/2 - 9*w, LCD_HEIGHT/2 - 3*h, 5*w, h);
957 rb->lcd_set_drawmode(DRMODE_SOLID);
958 rb->lcd_putsxy(LCD_WIDTH/2 - 9*w, LCD_HEIGHT/2 - 3*h, str);
959 rb->lcd_update_rect(LCD_WIDTH/2 - 9*w, LCD_HEIGHT/2 - 3*h, 5*w, h);
960 #endif
961 changed = false;
966 /*****************************************************************************
967 * blackjack_get_bet() gets the player's bet.
968 ******************************************************************************/
969 static void blackjack_get_bet(struct game_context* bj) {
970 bj->current_bet = blackjack_get_amount("Please enter a bet", 10,
971 bj->player_money, bj->current_bet);
974 /*****************************************************************************
975 * double_down() returns one final card then finishes the game
976 ******************************************************************************/
977 static void double_down(struct game_context* bj) {
978 bj->current_bet *= 2;
979 bj->player_cards[0][bj->num_player_cards[0]] = new_card();
980 bj->player_total += bj->player_cards[0][bj->num_player_cards[0]].value;
981 bj->num_player_cards[0]++;
984 /*****************************************************************************
985 * split() checks if the player wants to split and acts accordingly.
986 * When bj->split_status is 1, no split occurred. 2 means the player split and 3
987 * means a split has already occurred and the first hand is done.
988 ******************************************************************************/
989 static void split(struct game_context* bj) {
990 if (blackjack_get_yes_no("Split?") == 1)
991 bj->split_status = 1;
992 else {
993 bj->split_status = 2;
994 bj->current_bet *= 2;
995 bj->num_player_cards[0] = 1;
996 bj->num_player_cards[1] = 1;
997 bj->player_cards[1][0] = bj->player_cards[0][1];
998 bj->player_total = bj->player_cards[0][0].value;
1002 /*****************************************************************************
1003 * insurance() see if the player wants to buy insurance and how much.
1004 ******************************************************************************/
1005 static unsigned int insurance(struct game_context* bj) {
1006 unsigned int insurance, max_amount;
1008 insurance = blackjack_get_yes_no("Buy Insurance?");
1009 bj->asked_insurance = true;
1010 max_amount = bj->current_bet < (unsigned int)bj->player_money ?
1011 bj->current_bet/2 : (unsigned int)bj->player_money;
1012 if (insurance == 1) return 0;
1014 insurance = blackjack_get_amount("How much?", 0, max_amount, 0);
1015 redraw_board(bj);
1016 return insurance;
1019 /*****************************************************************************
1020 * play_again() checks to see if the player wants to keep playing.
1021 ******************************************************************************/
1022 static unsigned int play_again(void) {
1023 return blackjack_get_yes_no("Play Again?");
1026 /*****************************************************************************
1027 * blackjack_menu() is the initial menu at the start of the game.
1028 ******************************************************************************/
1029 static unsigned int blackjack_menu(struct game_context* bj) {
1030 int button;
1031 char *title = "Blackjack";
1032 char str[18];
1033 unsigned int i, w, h;
1034 bool breakout = false;
1035 bool showscores = false;
1037 while(true){
1038 #if LCD_DEPTH > 1
1039 rb->lcd_set_background(BG_COLOR);
1040 rb->lcd_set_foreground(FG_COLOR);
1041 #endif
1042 rb->lcd_clear_display();
1044 if(!showscores) {
1045 /* welcome screen to display key bindings */
1046 rb->lcd_getstringsize(title, &w, &h);
1047 rb->lcd_putsxy((LCD_WIDTH-w)/2, 0, title);
1049 #if CONFIG_KEYPAD == RECORDER_PAD
1050 rb->lcd_puts(0, 1, "ON: start");
1051 rb->lcd_puts(0, 2, "OFF: exit");
1052 rb->lcd_puts(0, 3, "F1: hit");
1053 rb->lcd_puts(0, 4, "F2: stay");
1054 rb->lcd_puts(0, 5, "F3: double down");
1055 rb->lcd_puts(0, 6, "PLAY: save/resume");
1056 rb->snprintf(str, 21, "High Score: $%d", bj->highscores[0]);
1057 rb->lcd_puts(0, 7, str);
1058 #elif CONFIG_KEYPAD == ONDIO_PAD
1059 rb->lcd_puts(0, 1, "MENU: start");
1060 rb->lcd_puts(0, 2, "OFF: exit");
1061 rb->lcd_puts(0, 3, "LEFT: hit");
1062 rb->lcd_puts(0, 4, "RIGHT: stay");
1063 rb->lcd_puts(0, 5, "UP: double down");
1064 rb->lcd_puts(0, 6, "DOWN: save/resume");
1065 rb->snprintf(str, 21, "High Score: $%d", bj->highscores[0]);
1066 rb->lcd_puts(0, 7, str);
1067 #elif (CONFIG_KEYPAD == IRIVER_H100_PAD) || (CONFIG_KEYPAD == IRIVER_H300_PAD)
1068 rb->lcd_puts(0, 2, "PLAY to start & to hit");
1069 rb->lcd_puts(0, 3, "STOP to exit");
1070 rb->lcd_puts(0, 4, "REC to stay");
1071 rb->lcd_puts(0, 5, "NAVI to double down ");
1072 rb->lcd_puts(0, 6, " & to view highscores");
1073 rb->lcd_puts(0, 7, "AB to save/resume");
1074 rb->snprintf(str, 21, "High Score: $%d", bj->highscores[0]);
1075 rb->lcd_puts(0, 8, str);
1076 #elif CONFIG_KEYPAD == IRIVER_H10_PAD
1077 rb->lcd_puts(0, 2, "PLAY to start & hit");
1078 rb->lcd_puts(0, 3, "POWER to exit");
1079 rb->lcd_puts(0, 4, ">>| to stay");
1080 rb->lcd_puts(0, 5, "|<< to double down");
1081 rb->lcd_puts(0, 6, "LEFT to view scores");
1082 rb->lcd_puts(0, 7, "RIGHT to save/resume");
1083 rb->snprintf(str, 21, "High Score: $%d", bj->highscores[0]);
1084 rb->lcd_puts(0, 8, str);
1086 #elif (CONFIG_KEYPAD == IPOD_4G_PAD) || \
1087 (CONFIG_KEYPAD == IPOD_3G_PAD) || \
1088 (CONFIG_KEYPAD == IPOD_1G2G_PAD)
1089 #if LCD_WIDTH >=176
1090 rb->lcd_puts(0, 2, "SELECT to start & to hit");
1091 rb->lcd_puts(0, 3, "MENU to exit");
1092 rb->lcd_puts(0, 4, ">>| to stay & to view highscores");
1093 rb->lcd_puts(0, 5, "|<< to double down");
1094 rb->lcd_puts(0, 6, "PLAY to save/resume");
1095 rb->snprintf(str, 21, "High Score: $%d", bj->highscores[0]);
1096 rb->lcd_puts(0, 7, str);
1097 #else
1098 rb->lcd_puts(0, 2, "SELECT to start & to ");
1099 rb->lcd_puts(0, 3, " hit");
1100 rb->lcd_puts(0, 4, "MENU to exit");
1101 rb->lcd_puts(0, 5, ">>| to stay & to view ");
1102 rb->lcd_puts(0, 6, " highscores");
1103 rb->lcd_puts(0, 7, "|<< to double down");
1104 rb->lcd_puts(0, 8, "PLAY to save/resume");
1105 rb->snprintf(str, 21, "High Score: $%d", bj->highscores[0]);
1106 rb->lcd_puts(0, 9, str);
1107 #endif
1108 #elif CONFIG_KEYPAD == IAUDIO_X5M5_PAD
1109 rb->lcd_puts(0, 2, "PLAY to start to hit");
1110 rb->lcd_puts(0, 3, "POWER to exit");
1111 rb->lcd_puts(0, 4, "SELECT to hit");
1112 rb->lcd_puts(0, 5, "REC to stay");
1113 rb->lcd_puts(0, 6, "PLAY to double down");
1114 rb->lcd_puts(0, 7, "RIGHT to view highscores ");
1115 rb->lcd_puts(0, 8, "DOWN to save/resume");
1116 rb->snprintf(str, 21, "High Score: $%d", bj->highscores[0]);
1117 rb->lcd_puts(0, 9, str);
1118 #elif CONFIG_KEYPAD == IRIVER_IFP7XX_PAD
1119 rb->lcd_puts(0, 2, "AB to start & to");
1120 rb->lcd_puts(0, 3, " stay");
1121 rb->lcd_puts(0, 4, "EQ to hit");
1122 rb->lcd_puts(0, 5, "PLAY to exit");
1123 rb->lcd_puts(0, 6, "CLICK to double down");
1124 rb->lcd_puts(0, 7, "& to view highscores");
1125 rb->lcd_puts(0, 8, "AB+EQ to save/resume");
1126 rb->snprintf(str, 21, "High Score: $%d", bj->highscores[0]);
1127 rb->lcd_puts(0, 9, str);
1128 #elif CONFIG_KEYPAD == GIGABEAT_PAD
1129 rb->lcd_puts(0, 2, "POWER to start");
1130 rb->lcd_puts(0, 3, "A to exit");
1131 rb->lcd_puts(0, 4, "VOL+ to hit");
1132 rb->lcd_puts(0, 5, "VOL- to stay");
1133 rb->lcd_puts(0, 6, "CENTER to double down");
1134 rb->lcd_puts(0, 6, "RIGHT to view highscores ");
1135 rb->lcd_puts(0, 8, "MENU to save/resume");
1136 rb->snprintf(str, 21, "High Score: $%d", bj->highscores[0]);
1137 rb->lcd_puts(0, 9, str);
1138 #elif (CONFIG_KEYPAD == SANSA_E200_PAD)
1139 rb->lcd_puts(0, 2, "SELECT to start & to hit");
1140 rb->lcd_puts(0, 3, "POWER to exit");
1141 rb->lcd_puts(0, 4, "RIGHT to stay");
1142 rb->lcd_puts(0, 5, "LEFT to double down");
1143 rb->lcd_puts(0, 6, "REC to save/resume");
1144 rb->lcd_puts(0, 7, "UP to view scores");
1145 rb->snprintf(str, 21, "High Score: $%d", bj->highscores[0]);
1146 rb->lcd_puts(0, 8, str);
1147 #elif (CONFIG_KEYPAD == SANSA_C200_PAD)
1148 rb->lcd_puts(0, 2, "SELECT to start & to hit");
1149 rb->lcd_puts(0, 3, "POWER to exit");
1150 rb->lcd_puts(0, 4, "RIGHT to stay");
1151 rb->lcd_puts(0, 5, "LEFT to double down");
1152 rb->lcd_puts(0, 6, "DOWN to save/resume");
1153 rb->lcd_puts(0, 7, "REC to view scores");
1154 rb->snprintf(str, 21, "High Score: $%d", bj->highscores[0]);
1155 rb->lcd_puts(0, 9, str);
1156 #endif
1157 } else {
1158 rb->snprintf(str, 12, "%s", "High Scores");
1159 rb->lcd_getstringsize(str, &w, &h);
1160 rb->lcd_putsxy((LCD_WIDTH-w)/2, 0, str);
1162 /* print high scores */
1163 for(i=0; i<NUM_SCORES; i++) {
1164 rb->snprintf(str, 14, "#%02d: $%d", i+1, bj->highscores[i]);
1165 rb->lcd_puts(0, i+1, str);
1169 rb->lcd_update();
1171 /* handle menu button presses */
1172 button = rb->button_get(true);
1174 switch(button) {
1175 case BJACK_START: /* start playing */
1176 breakout = true;
1177 break;
1179 case BJACK_QUIT: /* quit program */
1180 if(showscores) {
1181 showscores = 0;
1182 break;
1184 return BJ_QUIT;
1186 case BJACK_RESUME:/* resume game */
1187 if(!blackjack_loadgame(bj)) {
1188 rb->splash(HZ*2, "Nothing to resume");
1189 } else {
1190 rb->splash(HZ*2, "Loading...");
1191 breakout = true;
1193 break;
1195 case BJACK_SCORES:/* toggle high scores */
1196 showscores = !showscores;
1197 break;
1199 default:
1200 if(rb->default_event_handler_ex(button, blackjack_callback,
1201 (void*) bj) == SYS_USB_CONNECTED)
1202 return BJ_USB;
1203 break;
1206 if(breakout) break;
1209 return(0);
1212 /*****************************************************************************
1213 * blackjack() is the main game subroutine, it returns the final game status.
1214 ******************************************************************************/
1215 static int blackjack(struct game_context* bj) {
1216 int button;
1217 unsigned int w, h, temp_var, done = 0, todo = 1;
1218 signed int temp;
1219 bool breakout = false;
1220 bool dbl_down = false;
1222 /* don't resume by default */
1223 bj->resume = false;
1225 /********************
1226 * menu *
1227 ********************/
1228 temp_var = blackjack_menu(bj);
1229 if (temp_var == BJ_QUIT || temp_var == BJ_USB)
1230 return temp_var;
1233 /********************
1234 * init *
1235 ********************/
1236 blackjack_init(bj);
1237 bj->current_bet=10;
1239 /********************
1240 * play *
1241 ********************/
1243 /* check for resumed game */
1244 if(bj->resume) {
1245 bj->resume = false;
1246 redraw_board(bj);
1247 if (bj->split_status == 2) {
1248 todo=2;
1249 player_x = bj->num_player_cards[0] * 10 + 4;
1251 else if (bj->split_status == 3) {
1252 player_x = bj->num_player_cards[1] * 10 + LCD_WIDTH/2 + 4;
1253 todo=2;
1254 done=1;
1258 else {
1259 bj->player_money = 1000;
1260 blackjack_get_bet(bj);
1261 if (bj->current_bet == 0)
1262 return BJ_QUIT;
1263 rb->lcd_clear_display();
1264 deal_init_cards(bj);
1265 blackjack_drawtable(bj);
1268 rb->lcd_update();
1270 breakout = false;
1272 while(true){
1273 if(bj->player_total == 21 && bj->num_player_cards[0] == 2) {
1274 bj->is_blackjack = true;
1275 bj->end_hand = true;
1276 finish_game(bj);
1278 else if(bj->dealer_cards[1].is_soft_ace && !breakout &&
1279 !bj->asked_insurance) {
1280 temp_var = insurance(bj);
1281 if (bj->dealer_total == 21) {
1282 rb->splash(HZ, "Dealer has blackjack");
1283 bj->player_money += temp_var;
1284 bj->end_hand = true;
1285 breakout = true;
1286 redraw_board(bj);
1287 finish_game(bj);
1289 else {
1290 rb->splash(HZ, "Dealer does not have blackjack");
1291 bj->player_money -= temp_var;
1292 breakout = true;
1293 redraw_board(bj);
1294 rb->lcd_update();
1297 if(bj->split_status == 0 &&
1298 bj->player_cards[0][0].num == bj->player_cards[0][1].num) {
1299 split(bj);
1300 redraw_board(bj);
1301 rb->lcd_update_rect(0, LCD_HEIGHT/2, LCD_WIDTH, LCD_HEIGHT/2);
1302 if (bj->split_status == 2) {
1303 todo++;
1304 player_x = bj->num_player_cards[0] * 10 + 4;
1308 while(done < todo) {
1309 button = rb->button_get(true);
1311 switch(button) {
1312 case BJACK_HIT:
1313 NEXT_CARD = new_card();
1314 bj->player_total += NEXT_CARD.value;
1315 draw_card(NEXT_CARD, true, player_x, player_y);
1316 bj->num_player_cards[done]++;
1317 if (bj->num_player_cards[done] == MAX_CARDS + 1) {
1318 redraw_board(bj);
1319 rb->lcd_update_rect(0, LCD_HEIGHT/2, LCD_WIDTH,
1320 LCD_HEIGHT/2);
1322 else if (bj->num_player_cards[done]>MAX_CARDS || todo > 1) {
1323 rb->lcd_update_rect(player_x, player_y, CARD_WIDTH+2,
1324 CARD_HEIGHT+2);
1325 player_x += 10;
1327 else {
1328 rb->lcd_update_rect(player_x, player_y, CARD_WIDTH+2,
1329 CARD_HEIGHT+2);
1330 player_x += CARD_WIDTH + 4;
1332 update_total(bj);
1334 break;
1335 case BJACK_STAY:
1336 bj->end_hand = true;
1337 break;
1338 case BJACK_DOUBLEDOWN:
1339 if ((signed int)bj->current_bet * 2 < bj->player_money + 1 &&
1340 bj->num_player_cards[0]==2 && todo==1) {
1341 double_down(bj);
1342 dbl_down = true;
1343 if (bj->player_total < 22) {
1344 bj->end_hand = true;
1345 finish_game(bj);
1348 else if((signed int)bj->current_bet * 2 > bj->player_money) {
1349 rb->splash(HZ, "Not enough money to double down.");
1350 redraw_board(bj);
1351 rb->lcd_update();
1353 break;
1354 case BJACK_RESUME: /* save and end game */
1355 rb->splash(HZ, "Saving game...");
1356 blackjack_savegame(bj);
1357 /* fall through to BJACK_QUIT */
1359 case BJACK_QUIT:
1360 return BJ_END;
1363 while (bj->player_total > 21 && !bj->end_hand) {
1364 temp = check_for_aces(bj->player_cards[done],
1365 bj->num_player_cards[done]);
1366 if(temp != -1) {
1367 bj->player_cards[done][temp].is_soft_ace = false;
1368 bj->player_total -= 10;
1369 update_total(bj);
1370 if (dbl_down) {
1371 bj->end_hand = true;
1372 finish_game(bj);
1375 else
1376 bj->end_hand = true;
1379 if (bj->end_hand) {
1380 done++;
1381 if(todo > 1) {
1382 if (done == 2) {
1383 temp = bj->player_total;
1384 bj->player_total = temp_var;
1385 temp_var = temp;
1386 finish_game(bj);
1387 rb->lcd_getstringsize(" Split 1 ", &w, &h);
1388 rb->lcd_putsxy(LCD_WIDTH/2-w/2, LCD_HEIGHT/2-3*h/2,
1389 " Split 1 ");
1390 rb->lcd_update_rect(LCD_WIDTH/2-w/2, LCD_HEIGHT/2-3*h/2,
1391 w,h);
1392 bj->current_bet /= 2;
1393 rb->lcd_update_rect(LCD_WIDTH/2-w/2, LCD_HEIGHT/2-3*h/2,
1394 w,h);
1395 rb->sleep(HZ*2);
1396 bj->player_total = temp_var;
1397 finish_game(bj);
1398 rb->lcd_getstringsize(" Split 2 ", &w, &h);
1399 rb->lcd_putsxy(LCD_WIDTH/2-w/2, LCD_HEIGHT/2-3*h/2,
1400 " Split 2 ");
1401 rb->lcd_update_rect(LCD_WIDTH/2-w/2, LCD_HEIGHT/2-3*h/2,
1402 w,h);
1403 rb->sleep(HZ*2);
1405 else {
1406 bj->end_hand = false;
1407 bj->split_status = 3;
1408 temp_var = bj->player_total;
1409 bj->player_total = bj->player_cards[1][0].value;
1410 update_total(bj);
1411 redraw_board(bj);
1412 player_x += 10;
1413 rb->lcd_update();
1416 else
1417 finish_game(bj);
1421 if (bj->player_money < 10) {
1422 rb->sleep(HZ);
1423 return BJ_LOSE;
1426 if (bj->end_hand) { /* If hand is over */
1427 if (play_again() != 0) /* User wants to quit */
1428 return BJ_END;
1429 else { /* User keeps playing */
1430 breakout = false;
1431 redraw_board(bj);
1432 if(dbl_down) {
1433 bj->current_bet /= 2;
1434 dbl_down = false;
1436 done = 0;
1437 todo = 1;
1438 blackjack_init(bj);
1439 blackjack_get_bet(bj);
1440 if (bj->current_bet == 0)
1441 return BJ_END;
1442 deal_init_cards(bj);
1443 blackjack_drawtable(bj);
1444 rb->lcd_update();
1448 /* Never reached */
1449 return PLUGIN_OK;
1452 /*****************************************************************************
1453 * plugin entry point.
1454 ******************************************************************************/
1455 enum plugin_status plugin_start(struct plugin_api* api, void* parameter) {
1456 struct game_context bj;
1457 bool exit = false;
1458 unsigned int position;
1459 char str[19];
1461 (void)parameter;
1462 rb = api;
1464 #if LCD_DEPTH > 1
1465 rb->lcd_set_backdrop(NULL);
1466 #endif
1468 /* load high scores */
1469 blackjack_loadscores(&bj);
1471 rb->lcd_setfont(FONT_SYSFIXED);
1473 while(!exit) {
1474 switch(blackjack(&bj)){
1475 case BJ_LOSE:
1476 rb->splash(HZ, "Not enough money to continue");
1477 /* fall through to BJ_END */
1479 case BJ_END:
1480 if(!bj.resume) {
1481 if((position = blackjack_recordscore(&bj))) {
1482 rb->snprintf(str, 19, "New high score #%d!", position);
1483 rb->splash(HZ*2, str);
1486 break;
1488 case BJ_USB:
1489 rb->lcd_setfont(FONT_UI);
1490 return PLUGIN_USB_CONNECTED;
1492 case BJ_QUIT:
1493 if(bj.dirty) {
1494 rb->splash(HZ, "Saving high scores...");
1495 blackjack_savescores(&bj);
1497 exit = true;
1498 break;
1500 default:
1501 break;
1505 rb->lcd_setfont(FONT_UI);
1506 return PLUGIN_OK;