/*************************************************************************** * __________ __ ___. * Open \______ \ ____ ____ | | _\_ |__ _______ ___ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ * \/ \/ \/ \/ \/ * $Id: $ * * Copyright (C) 2006 Tom Ross * * All files in this archive are subject to the GNU General Public License. * See the file COPYING in the source tree root for full license agreement. * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY * KIND, either express or implied. * ****************************************************************************/ #include "plugin.h" #include "card_deck.h" #include "card_back.h" PLUGIN_HEADER /* save files */ #define SCORE_FILE PLUGIN_GAMES_DIR "/blackjack.score" #define SAVE_FILE PLUGIN_GAMES_DIR "/blackjack.save" #define NUM_SCORES LCD_HEIGHT/8-2 /* final game return status */ #define BJ_END 3 #define BJ_USB 2 #define BJ_QUIT 1 #define BJ_LOSE 0 #if CONFIG_KEYPAD == RECORDER_PAD #define BJACK_START BUTTON_ON #define BJACK_QUIT BUTTON_OFF #define BJACK_MAX (BUTTON_ON|BUTTON_UP) #define BJACK_MIN (BUTTON_ON|BUTTON_DOWN) #define BJACK_HIT BUTTON_F1 #define BJACK_STAY BUTTON_F2 #define BJACK_DOUBLEDOWN BUTTON_F3 #define BJACK_SCORES BUTTON_RIGHT #define BJACK_RESUME BUTTON_PLAY #define BJACK_UP BUTTON_UP #define BJACK_DOWN BUTTON_DOWN #define BJACK_RIGHT BUTTON_RIGHT #define BJACK_LEFT BUTTON_LEFT #elif CONFIG_KEYPAD == ONDIO_PAD #define BJACK_START BUTTON_MENU #define BJACK_QUIT BUTTON_OFF #define BJACK_MAX (BUTTON_MENU|BUTTON_UP) #define BJACK_MIN (BUTTON_MENU|BUTTON_DOWN) #define BJACK_HIT BUTTON_LEFT #define BJACK_STAY BUTTON_RIGHT #define BJACK_DOUBLEDOWN BUTTON_UP #define BJACK_SCORES BUTTON_UP #define BJACK_RESUME BUTTON_DOWN #define BJACK_UP BUTTON_UP #define BJACK_DOWN BUTTON_DOWN #define BJACK_RIGHT BUTTON_RIGHT #define BJACK_LEFT BUTTON_LEFT #elif CONFIG_KEYPAD == IRIVER_H10_PAD #define BJACK_START BUTTON_PLAY #define BJACK_QUIT BUTTON_POWER #define BJACK_MAX (BUTTON_PLAY|BUTTON_SCROLL_UP) #define BJACK_MIN (BUTTON_PLAY|BUTTON_SCROLL_DOWN) #define BJACK_HIT BUTTON_PLAY #define BJACK_STAY BUTTON_FF #define BJACK_DOUBLEDOWN BUTTON_REW #define BJACK_SCORES BUTTON_LEFT #define BJACK_RESUME BUTTON_RIGHT #define BJACK_UP BUTTON_SCROLL_UP #define BJACK_DOWN BUTTON_SCROLL_DOWN #define BJACK_RIGHT BUTTON_RIGHT #define BJACK_LEFT BUTTON_LEFT #elif (CONFIG_KEYPAD == IRIVER_H100_PAD) || \ (CONFIG_KEYPAD == IRIVER_H300_PAD) #define BJACK_START BUTTON_ON #define BJACK_QUIT BUTTON_OFF #define BJACK_MAX (BUTTON_ON|BUTTON_UP) #define BJACK_MIN (BUTTON_ON|BUTTON_DOWN) #define BJACK_HIT BUTTON_ON #define BJACK_STAY BUTTON_REC #define BJACK_DOUBLEDOWN BUTTON_SELECT #define BJACK_SCORES BUTTON_SELECT #define BJACK_RESUME BUTTON_MODE #define BJACK_UP BUTTON_UP #define BJACK_DOWN BUTTON_DOWN #define BJACK_RIGHT BUTTON_RIGHT #define BJACK_LEFT BUTTON_LEFT #elif (CONFIG_KEYPAD == IPOD_4G_PAD) || \ (CONFIG_KEYPAD == IPOD_3G_PAD) || \ (CONFIG_KEYPAD == IPOD_1G2G_PAD) #define BJACK_START BUTTON_SELECT #define BJACK_QUIT BUTTON_MENU #define BJACK_MAX (BUTTON_SELECT|BUTTON_SCROLL_FWD) #define BJACK_MIN (BUTTON_SELECT|BUTTON_SCROLL_BACK) #define BJACK_HIT BUTTON_SELECT #define BJACK_STAY BUTTON_RIGHT #define BJACK_DOUBLEDOWN BUTTON_LEFT #define BJACK_SCORES BUTTON_RIGHT #define BJACK_RESUME BUTTON_PLAY #define BJACK_UP BUTTON_SCROLL_FWD #define BJACK_DOWN BUTTON_SCROLL_BACK #define BJACK_RIGHT BUTTON_RIGHT #define BJACK_LEFT BUTTON_LEFT #elif CONFIG_KEYPAD == IAUDIO_X5M5_PAD #define BJACK_START BUTTON_PLAY #define BJACK_QUIT BUTTON_POWER #define BJACK_MAX (BUTTON_PLAY|BUTTON_UP) #define BJACK_MIN (BUTTON_PLAY|BUTTON_DOWN) #define BJACK_HIT BUTTON_SELECT #define BJACK_STAY BUTTON_REC #define BJACK_DOUBLEDOWN BUTTON_PLAY #define BJACK_SCORES BUTTON_RIGHT #define BJACK_RESUME BUTTON_DOWN #define BJACK_UP BUTTON_UP #define BJACK_DOWN BUTTON_DOWN #define BJACK_RIGHT BUTTON_RIGHT #define BJACK_LEFT BUTTON_LEFT #elif CONFIG_KEYPAD == IRIVER_IFP7XX_PAD #define BJACK_START BUTTON_MODE #define BJACK_QUIT BUTTON_PLAY #define BJACK_MAX (BUTTON_EQ|BUTTON_UP) #define BJACK_MIN (BUTTON_EQ|BUTTON_DOWN) #define BJACK_HIT BUTTON_EQ #define BJACK_STAY BUTTON_MODE #define BJACK_DOUBLEDOWN BUTTON_SELECT #define BJACK_SCORES BUTTON_SELECT #define BJACK_RESUME (BUTTON_EQ|BUTTON_MODE) #define BJACK_UP BUTTON_UP #define BJACK_DOWN BUTTON_DOWN #define BJACK_RIGHT BUTTON_RIGHT #define BJACK_LEFT BUTTON_LEFT #elif CONFIG_KEYPAD == GIGABEAT_PAD #define BJACK_START BUTTON_POWER #define BJACK_QUIT BUTTON_A #define BJACK_MAX BUTTON_VOL_UP #define BJACK_MIN BUTTON_VOL_DOWN #define BJACK_HIT BUTTON_VOL_UP #define BJACK_STAY BUTTON_VOL_DOWN #define BJACK_DOUBLEDOWN BUTTON_SELECT #define BJACK_SCORES BUTTON_RIGHT #define BJACK_RESUME BUTTON_MENU #define BJACK_UP BUTTON_UP #define BJACK_DOWN BUTTON_DOWN #define BJACK_RIGHT BUTTON_RIGHT #define BJACK_LEFT BUTTON_LEFT #elif CONFIG_KEYPAD == SANSA_E200_PAD #define BJACK_START BUTTON_SELECT #define BJACK_QUIT BUTTON_POWER #define BJACK_MAX (BUTTON_REC|BUTTON_UP) #define BJACK_MIN (BUTTON_REC|BUTTON_DOWN) #define BJACK_HIT BUTTON_SELECT #define BJACK_STAY BUTTON_RIGHT #define BJACK_DOUBLEDOWN BUTTON_LEFT #define BJACK_SCORES BUTTON_UP #define BJACK_RESUME BUTTON_REC #define BJACK_UP BUTTON_SCROLL_UP #define BJACK_DOWN BUTTON_SCROLL_DOWN #define BJACK_RIGHT BUTTON_RIGHT #define BJACK_LEFT BUTTON_LEFT #elif CONFIG_KEYPAD == SANSA_C200_PAD #define BJACK_START BUTTON_SELECT #define BJACK_QUIT BUTTON_POWER #define BJACK_MAX BUTTON_VOL_UP #define BJACK_MIN BUTTON_VOL_DOWN #define BJACK_HIT BUTTON_SELECT #define BJACK_STAY BUTTON_RIGHT #define BJACK_DOUBLEDOWN BUTTON_LEFT #define BJACK_SCORES BUTTON_REC #define BJACK_RESUME BUTTON_DOWN #define BJACK_UP BUTTON_UP #define BJACK_DOWN BUTTON_DOWN #define BJACK_RIGHT BUTTON_RIGHT #define BJACK_LEFT BUTTON_LEFT #elif CONFIG_KEYPAD == ELIO_TPJ1022_PAD #define BJACK_START BUTTON_MAIN #define BJACK_QUIT BUTTON_POWER #define BJACK_MAX (BUTTON_REC|BUTTON_UP) #define BJACK_MIN (BUTTON_REC|BUTTON_DOWN) #define BJACK_HIT BUTTON_MAIN #define BJACK_STAY BUTTON_MENU #define BJACK_DOUBLEDOWN BUTTON_DOWN #define BJACK_SCORES BUTTON_UP #define BJACK_RESUME BUTTON_FF #define BJACK_UP BUTTON_UP #define BJACK_DOWN BUTTON_DOWN #define BJACK_RIGHT BUTTON_RIGHT #define BJACK_LEFT BUTTON_LEFT #else #error BLACKJACK: Unsupported keypad #endif #ifdef HAVE_LCD_COLOR #define BG_COLOR LCD_RGBPACK(0,157,0) #define FG_COLOR LCD_WHITE #elif LCD_DEPTH > 1 #define BG_COLOR LCD_WHITE #define FG_COLOR LCD_BLACK #endif #define CARD_WIDTH BMPWIDTH_card_back #define CARD_HEIGHT BMPHEIGHT_card_back /* This is the max amount of cards onscreen before condensing */ #define MAX_CARDS LCD_WIDTH/(CARD_WIDTH+4) extern const fb_data card_deck[]; extern const fb_data card_back[]; #define NEXT_CARD bj->player_cards[done][bj->num_player_cards[done]] /* global rockbox api */ static struct plugin_api* rb; MEM_FUNCTION_WRAPPERS(rb); /* dealer and player card positions */ unsigned int dealer_x, dealer_y, player_x, player_y; typedef struct card { unsigned int value; /* Card's value in Blackjack */ unsigned int num; /* Value on card face 0-12 (0=Ace, 1=2, 11=Q) */ unsigned int suit; /* 0:Spades, 1:Hearts, 2: Clubs; 3: Diamonds */ bool is_soft_ace; } card; typedef struct game_context { struct card player_cards[2][22]; /* 22 Cards means the deal was all aces */ struct card dealer_cards[22]; /* That is the worst-case scenario */ unsigned int player_total; unsigned int dealer_total; signed int player_money; unsigned int num_player_cards[2]; unsigned int num_dealer_cards; unsigned int current_bet; unsigned int split_status; /* 0 = split hasn't been asked, * * 1 = split did not occur * * 2 = split occurred * * 3 = split occurred and 1st hand done */ bool is_blackjack; bool end_hand; bool asked_insurance; signed short highscores[NUM_SCORES]; bool resume; bool dirty; } game_context; /***************************************************************************** * blackjack_init() initializes blackjack data structures. ******************************************************************************/ static void blackjack_init(struct game_context* bj) { /* seed the rand generator */ rb->srand(*rb->current_tick); /* reset card positions */ dealer_x = 4; dealer_y = LCD_HEIGHT/4 - CARD_HEIGHT/2; player_x = 4; player_y = LCD_HEIGHT - LCD_HEIGHT/4 - CARD_HEIGHT/2; /* check for resumed game */ if(bj->resume) return; /* reset scoring */ bj->player_total = 0; bj->dealer_total = 0; bj->num_player_cards[0] = 2; bj->num_player_cards[1] = 0; bj->num_dealer_cards = 2; bj->end_hand = false; bj->split_status = 0; bj->is_blackjack = false; bj->asked_insurance = false; } /***************************************************************************** * blackjack_drawtable() draws the table and some text. ******************************************************************************/ static void blackjack_drawtable(struct game_context* bj) { unsigned int w, h, y_loc; char str[10]; #if LCD_HEIGHT <= 64 rb->lcd_getstringsize("Bet", &w, &h); rb->lcd_putsxy(LCD_WIDTH - w, 2*h + 1, "Bet"); rb->snprintf(str, 9, "$%d", bj->current_bet); rb->lcd_getstringsize(str, &w, &h); rb->lcd_putsxy(LCD_WIDTH - w, 3*h + 1, str); y_loc = LCD_HEIGHT/2; #else rb->lcd_getstringsize("Bet", &w, &h); rb->lcd_putsxy(LCD_WIDTH - w, 5*h / 2, "Bet"); rb->snprintf(str, 9, "$%d", bj->current_bet); rb->lcd_getstringsize(str, &w, &h); rb->lcd_putsxy(LCD_WIDTH - w, 7*h / 2, str); rb->lcd_hline(0, LCD_WIDTH, LCD_HEIGHT/2); y_loc = LCD_HEIGHT/2 + h; #endif rb->lcd_putsxy(0,0, "Dealer"); rb->lcd_getstringsize("Player", &w, &h); rb->lcd_putsxy(0, y_loc, "Player"); rb->lcd_getstringsize("Total", &w, &h); rb->lcd_putsxy(LCD_WIDTH - w, y_loc, "Total"); rb->lcd_getstringsize("Money", &w, &h); rb->lcd_putsxy(LCD_WIDTH - w, 0, "Money"); rb->snprintf(str, 9, "$%d", bj->player_money - bj->current_bet); rb->lcd_getstringsize(str, &w, &h); rb->lcd_putsxy(LCD_WIDTH - w, h + 1, str); rb->snprintf(str, 3, "%d", bj->player_total); rb->lcd_getstringsize(str, &w, &h); rb->lcd_putsxy(LCD_WIDTH - w, y_loc + h, str); } /***************************************************************************** * find_value() is passed a card and returns its blackjack value. ******************************************************************************/ static unsigned int find_value(unsigned int number) { unsigned int thisValue; if (number == 0) thisValue = 11; /* Aces get a value of 11 at first */ else if (number < 10) thisValue = number + 1; else thisValue = 10; /* Anything 10 or higher gets a value of 10 */ return thisValue; } /***************************************************************************** * draw_card() draws a card to the screen. ******************************************************************************/ static void draw_card(struct card temp_card, bool shown, unsigned int x, unsigned int y) { if(shown) rb->lcd_bitmap_part(card_deck, CARD_WIDTH*temp_card.num, CARD_HEIGHT*temp_card.suit, BMPWIDTH_card_deck, x+1, y+1, CARD_WIDTH, CARD_HEIGHT); else rb->lcd_bitmap(card_back, x+1, y+1,CARD_WIDTH, CARD_HEIGHT); #if LCD_DEPTH > 1 rb->lcd_set_foreground(LCD_BLACK); #endif /* Print outlines */ #if CARD_WIDTH >= 26 rb->lcd_hline(x+2, x+CARD_WIDTH-1, y); rb->lcd_hline(x+2, x+CARD_WIDTH-1, y+CARD_HEIGHT+1); rb->lcd_vline(x, y+2, y+CARD_HEIGHT-3); rb->lcd_vline(x+CARD_WIDTH+1, y+2, y+CARD_HEIGHT-1); rb->lcd_drawpixel(x+1, y+1); rb->lcd_drawpixel(x+1, y+CARD_HEIGHT); rb->lcd_drawpixel(x+CARD_WIDTH, y+1); rb->lcd_drawpixel(x+CARD_WIDTH, y+CARD_HEIGHT); #else rb->lcd_hline(x+1, x+CARD_WIDTH, y); rb->lcd_hline(x+1, x+CARD_WIDTH, y+CARD_HEIGHT+1); rb->lcd_vline(x, y+1, y+CARD_HEIGHT); rb->lcd_vline(x+CARD_WIDTH+1, y+1, y+CARD_HEIGHT); #endif #if LCD_DEPTH > 1 rb->lcd_set_foreground(FG_COLOR); #endif } /***************************************************************************** * new_card() initializes a new card and gives it values. ******************************************************************************/ static struct card new_card(void) { struct card new_card; new_card.suit = rb->rand()%4; /* Random number 0-3 */ new_card.num = rb->rand()%13; /* Random number 0-12 */ new_card.value = find_value(new_card.num); new_card.is_soft_ace = new_card.num == 0 ? true : false; return new_card; } /***************************************************************************** * deal_init_card() deals and draws to the screen the player's and dealer's * initial cards. ******************************************************************************/ static void deal_init_cards(struct game_context* bj) { bj->dealer_cards[0] = new_card(); bj->dealer_total += bj->dealer_cards[0].value; draw_card(bj->dealer_cards[0], false, dealer_x, dealer_y); bj->dealer_cards[1] = new_card(); bj->dealer_total += bj->dealer_cards[1].value; draw_card(bj->dealer_cards[1], true, dealer_x + CARD_WIDTH + 4, dealer_y); bj->player_cards[0][0] = new_card(); bj->player_total += bj->player_cards[0][0].value; draw_card(bj->player_cards[0][0], true, player_x, player_y); player_x += CARD_WIDTH + 4; bj->player_cards[0][1] = new_card(); bj->player_total += bj->player_cards[0][1].value; draw_card(bj->player_cards[0][1], true, player_x, player_y); player_x += CARD_WIDTH + 4; } /***************************************************************************** * redraw_board() redraws all the cards and the board ******************************************************************************/ static void redraw_board(struct game_context* bj) { unsigned int i, n, upper_bound; rb->lcd_clear_display(); blackjack_drawtable(bj); player_x = 4; dealer_x = 4; upper_bound = bj->split_status > 1 ? 2 : 1; for (i = 0; i < bj->num_dealer_cards; i++) { if (!bj->end_hand) { draw_card(bj->dealer_cards[0], false, dealer_x, dealer_y); /* increment i so the dealer's first card isn't displayed */ i++; dealer_x += CARD_WIDTH + 4; } draw_card(bj->dealer_cards[i], true, dealer_x, dealer_y); if (bj->num_dealer_cards > MAX_CARDS-1) dealer_x += 10; else dealer_x += CARD_WIDTH + 4; } for (n = 0; n < upper_bound; n++) { for (i = 0; i < bj->num_player_cards[n]; i++) { draw_card(bj->player_cards[n][i], true, player_x, player_y); if (bj->split_status>1 || bj->num_player_cards[n]>MAX_CARDS) player_x += 10; else player_x += CARD_WIDTH + 4; } if (bj->split_status > 1) player_x = LCD_WIDTH/2 + 4; } } /***************************************************************************** * update_total updates the player's total ******************************************************************************/ static void update_total(struct game_context* bj) { char total[3]; unsigned int w, h; rb->snprintf(total, 3, "%d", bj->player_total); rb->lcd_getstringsize(total, &w, &h); #if LCD_HEIGHT > 64 h *= 2; #endif rb->lcd_putsxy(LCD_WIDTH - w, LCD_HEIGHT/2 + h, total); rb->lcd_update_rect(LCD_WIDTH - w, LCD_HEIGHT/2 + h, w, h); } /***************************************************************************** * check_for_aces() is passed an array of cards and returns where an ace is * located. Otherwise, returns -1. ******************************************************************************/ static signed int check_for_aces(struct card temp_cards[], unsigned int size) { unsigned int i; for(i = 0; i < size; i++) { if (temp_cards[i].is_soft_ace == true) return i; } return -1; } /***************************************************************************** * check_totals() compares player and dealer totals. * 0: bust 1: loss, 2: push, 3: win, 4: blackjack, 5: something's not right... ******************************************************************************/ static unsigned int check_totals(struct game_context* bj) { unsigned int temp; if (bj->player_total > 21) temp = 0; else if (bj->player_total == 21 && bj->is_blackjack) if (bj->dealer_total == 21 && bj->num_dealer_cards == 2) temp = 2; else temp = 4; else if (bj->player_total == bj->dealer_total) temp = 2; else if (bj->dealer_total > 21 && bj->player_total < 22) temp = 3; else if (bj->dealer_total > bj->player_total) temp = 1; else if (bj->player_total > bj->dealer_total) temp = 3; else temp = 5; return temp; } /***************************************************************************** * finish_dealer() draws cards for the dealer until he has 17 or more. ******************************************************************************/ static void finish_dealer(struct game_context* bj) { signed int temp = 0; if (bj->dealer_total > 16 && bj->dealer_total < 22) return; while (bj->dealer_total < 17) { bj->dealer_cards[bj->num_dealer_cards] = new_card(); bj->dealer_total += bj->dealer_cards[bj->num_dealer_cards].value; bj->num_dealer_cards++; } while (bj->dealer_total > 21) { temp = check_for_aces(bj->dealer_cards, bj->num_dealer_cards); if(temp != -1) { bj->dealer_cards[temp].is_soft_ace = false; bj->dealer_total -= 10; } else return; } } /***************************************************************************** * finish_game() completes the game once player's turn is over. ******************************************************************************/ static void finish_game(struct game_context* bj) { unsigned int rValue, w, h; char str[19]; do { finish_dealer(bj); } while (bj->dealer_total < 17); redraw_board(bj); rValue = check_totals(bj); if (rValue == 0) { rb->snprintf(str, sizeof(str), " Bust! "); bj->player_money -= bj->current_bet; } else if (rValue == 1) { rb->snprintf(str, sizeof(str), " Sorry, you lost. "); bj->player_money -= bj->current_bet; } else if (rValue == 2) { rb->snprintf(str, sizeof(str), " Push "); } else if (rValue == 3) { rb->snprintf(str, sizeof(str), " You won! "); bj->player_money+= bj->current_bet; } else { rb->snprintf(str, sizeof(str), " Blackjack! "); bj->player_money += bj->current_bet * 3 / 2; } rb->lcd_getstringsize(str, &w, &h); #if LCD_HEIGHT <= 64 rb->lcd_set_drawmode(DRMODE_BG+DRMODE_INVERSEVID); rb->lcd_fillrect(0, LCD_HEIGHT/2, LCD_WIDTH, LCD_HEIGHT/2); rb->lcd_set_drawmode(DRMODE_SOLID); rb->lcd_putsxy(LCD_WIDTH/2 - w/2, LCD_HEIGHT/2 + h, str); rb->snprintf(str, 12, "You have %d", bj->player_total); rb->lcd_getstringsize(str, &w, &h); rb->lcd_putsxy(LCD_WIDTH/2 - w/2, LCD_HEIGHT/2, str); #else rb->lcd_putsxy(LCD_WIDTH/2 - w/2, LCD_HEIGHT/2 - h/2, str); #endif rb->lcd_update(); } /***************************************************************************** * blackjack_recordscore() inserts a high score into the high scores list and * returns the high score position. ******************************************************************************/ static unsigned int blackjack_recordscore(struct game_context* bj) { unsigned int i; unsigned int position = 0; signed short current, temp; /* calculate total score */ current = bj->player_money; if(current <= 10) return 0; /* insert the current score into the high scores */ for(i=0; i= bj->highscores[i]) { if(!position) { position = i+1; bj->dirty = true; } temp = bj->highscores[i]; bj->highscores[i] = current; current = temp; } } return position; } /***************************************************************************** * blackjack_loadscores() loads the high scores saved file. ******************************************************************************/ static void blackjack_loadscores(struct game_context* bj) { signed int fd; bj->dirty = false; /* clear high scores */ rb->memset(bj->highscores, 0, sizeof(bj->highscores)); /* open scores file */ fd = rb->open(SCORE_FILE, O_RDONLY); if(fd < 0) return; /* read in high scores */ if(rb->read(fd, bj->highscores, sizeof(bj->highscores)) <= 0) { /* scores are bad, reset */ rb->memset(bj->highscores, 0, sizeof(bj->highscores)); } rb->close(fd); } /***************************************************************************** * blackjack_savescores() saves the high scores saved file. ******************************************************************************/ static void blackjack_savescores(struct game_context* bj) { unsigned int fd; /* write out the high scores to the save file */ fd = rb->open(SCORE_FILE, O_WRONLY|O_CREAT); rb->write(fd, bj->highscores, sizeof(bj->highscores)); rb->close(fd); bj->dirty = false; } /***************************************************************************** * blackjack_loadgame() loads the saved game and returns load success. ******************************************************************************/ static bool blackjack_loadgame(struct game_context* bj) { signed int fd; bool loaded = false; /* open game file */ fd = rb->open(SAVE_FILE, O_RDONLY); if(fd < 0) return loaded; /* read in saved game */ while(true) { if(rb->read(fd, &bj->player_money, sizeof(bj->player_money)) <= 0) break; if(rb->read(fd, &bj->player_total, sizeof(bj->player_total)) <= 0) break; if(rb->read(fd, &bj->dealer_total, sizeof(bj->dealer_total)) <= 0) break; if(rb->read(fd, &bj->num_player_cards, sizeof(bj->num_player_cards))<=0) break; if(rb->read(fd, &bj->num_dealer_cards, sizeof(bj->num_dealer_cards))<=0) break; if(rb->read(fd, &bj->current_bet, sizeof(bj->current_bet)) <= 0) break; if(rb->read(fd, &bj->is_blackjack, sizeof(bj->is_blackjack)) <= 0) break; if(rb->read(fd, &bj->split_status, sizeof(bj->split_status)) <= 0) break; if(rb->read(fd, &bj->asked_insurance, sizeof(bj->asked_insurance)) <= 0) break; if(rb->read(fd, &bj->end_hand, sizeof(bj->end_hand)) <= 0) break; if(rb->read(fd, &bj->player_cards, sizeof(bj->player_cards)) <= 0) break; if(rb->read(fd, &bj->dealer_cards, sizeof(bj->dealer_cards)) <= 0) break; bj->resume = true; loaded = true; break; } rb->close(fd); /* delete saved file */ rb->remove(SAVE_FILE); return loaded; } /***************************************************************************** * blackjack_savegame() saves the current game state. ******************************************************************************/ static void blackjack_savegame(struct game_context* bj) { unsigned int fd; /* write out the game state to the save file */ fd = rb->open(SAVE_FILE, O_WRONLY|O_CREAT); rb->write(fd, &bj->player_money, sizeof(bj->player_money)); rb->write(fd, &bj->player_total, sizeof(bj->player_total)); rb->write(fd, &bj->dealer_total, sizeof(bj->dealer_total)); rb->write(fd, &bj->num_player_cards, sizeof(bj->num_player_cards)); rb->write(fd, &bj->num_dealer_cards, sizeof(bj->num_dealer_cards)); rb->write(fd, &bj->current_bet, sizeof(bj->current_bet)); rb->write(fd, &bj->is_blackjack, sizeof(bj->is_blackjack)); rb->write(fd, &bj->split_status, sizeof(bj->split_status)); rb->write(fd, &bj->asked_insurance, sizeof(bj->asked_insurance)); rb->write(fd, &bj->end_hand, sizeof(bj->end_hand)); rb->write(fd, &bj->player_cards, sizeof(bj->player_cards)); rb->write(fd, &bj->dealer_cards, sizeof(bj->dealer_cards)); rb->close(fd); bj->resume = true; } /***************************************************************************** * blackjack_callback() is the default event handler callback which is called * on usb connect and shutdown. ******************************************************************************/ static void blackjack_callback(void* param) { struct game_context* bj = (struct game_context*) param; if(bj->dirty) { rb->splash(HZ, "Saving high scores..."); blackjack_savescores(bj); } } /***************************************************************************** * blackjack_get_yes_no() gets a yes/no answer from the user ******************************************************************************/ static unsigned int blackjack_get_yes_no(char message[20]) { int button; unsigned int w, h, b, choice = 0; bool breakout = false; char message_yes[24], message_no[24]; rb->strcpy(message_yes, message); rb->strcpy(message_no, message); rb->strcat(message_yes, " Yes"); rb->strcat(message_no, " No"); rb->lcd_getstringsize(message_yes, &w, &h); const char *stg[] = {message_yes, message_no}; #if LCD_HEIGHT <= 64 b = 2*h+1; #else b = h-1; #endif #ifdef HAVE_LCD_COLOR rb->lcd_fillrect(LCD_WIDTH/2 - w/2, LCD_HEIGHT/2 + b, w+1, h+3); rb->lcd_set_foreground(LCD_BLACK); rb->lcd_set_background(LCD_WHITE); #else rb->lcd_set_drawmode(DRMODE_BG+DRMODE_INVERSEVID); rb->lcd_fillrect(LCD_WIDTH/2 - w/2, LCD_HEIGHT/2 + b, w+1, h+3); rb->lcd_set_drawmode(DRMODE_SOLID); #endif rb->lcd_drawrect(LCD_WIDTH/2 - w/2 - 1, LCD_HEIGHT/2 + b - 1, w+3, h+4); while(!breakout) { rb->lcd_putsxy(LCD_WIDTH/2 - w/2, LCD_HEIGHT/2 + b +1, stg[choice]); rb->lcd_update_rect(LCD_WIDTH/2 - w/2 - 1, LCD_HEIGHT/2 + b -1, w+3, h+4); button = rb->button_get(true); switch(button) { case BJACK_LEFT: case (BJACK_LEFT|BUTTON_REPEAT): case BJACK_RIGHT: case (BJACK_RIGHT|BUTTON_REPEAT): choice ^= 1; break; case BJACK_START: breakout = true; break; case BJACK_QUIT: breakout = true; choice = BJ_QUIT; break; } } #if LCD_DEPTH > 1 rb->lcd_set_foreground(FG_COLOR); rb->lcd_set_background(BG_COLOR); #endif return choice; } /***************************************************************************** * blackjack_get_amount() gets an amount from the player to be used ******************************************************************************/ static signed int blackjack_get_amount(char message[20], signed int lower_limit, signed int upper_limit, signed int start) { int button; char str[6]; bool changed = false; unsigned int w, h; signed int amount; rb->lcd_getstringsize("A", &w, &h); /* find the size of one character */ if (start > upper_limit) amount = upper_limit; else if (start < lower_limit) amount = lower_limit; else amount = start; #if LCD_DEPTH > 1 rb->lcd_set_background(LCD_WHITE); rb->lcd_set_foreground(LCD_BLACK); #endif #if LCD_HEIGHT <= 64 rb->lcd_clear_display(); rb->lcd_puts(0, 1, message); rb->snprintf(str, 9, "$%d", amount); rb->lcd_puts(0, 2, str); rb->lcd_puts(0, 3, "RIGHT: +1"); rb->lcd_puts(0, 4, "LEFT: -1"); rb->lcd_puts(0, 5, "UP: +10"); rb->lcd_puts(0, 6, "DOWN: -10"); rb->lcd_update(); #else rb->lcd_set_drawmode(DRMODE_BG+DRMODE_INVERSEVID); rb->lcd_fillrect(LCD_WIDTH/2 - 9*w - 1, LCD_HEIGHT/2 - 4*h - 3, 37*w / 2, 8*h -3); rb->lcd_set_drawmode(DRMODE_SOLID); rb->lcd_drawrect(LCD_WIDTH/2 - 9*w - 1, LCD_HEIGHT/2 - 4*h - 3, 37*w / 2, 8*h -3); rb->lcd_putsxy(LCD_WIDTH/2 - 9*w, LCD_HEIGHT/2 - 4*h - 1, message); rb->snprintf(str, 9, "$%d", amount); rb->lcd_putsxy(LCD_WIDTH/2 - 9*w, LCD_HEIGHT/2 - 3*h, str); #if (CONFIG_KEYPAD == IPOD_4G_PAD) || \ (CONFIG_KEYPAD == IPOD_3G_PAD) || \ (CONFIG_KEYPAD == IPOD_1G2G_PAD) rb->lcd_putsxy(LCD_WIDTH/2 - 9*w, LCD_HEIGHT/2 - h-2, " >>|: +1"); rb->lcd_putsxy(LCD_WIDTH/2 - 9*w, LCD_HEIGHT/2 - 1, " |<<: -1"); rb->lcd_putsxy(LCD_WIDTH/2 - 9*w, LCD_HEIGHT/2 + h, "SCROLL+: +10"); rb->lcd_putsxy(LCD_WIDTH/2 - 9*w, LCD_HEIGHT/2 + 2*h + 1, "SCROLL-: -10"); #elif CONFIG_KEYPAD == IRIVER_H10_PAD rb->lcd_putsxy(LCD_WIDTH/2 - 9*w, LCD_HEIGHT/2 - h-2, "RIGHT: +1"); rb->lcd_putsxy(LCD_WIDTH/2 - 9*w, LCD_HEIGHT/2 - 1, "LEFT: -1"); rb->lcd_putsxy(LCD_WIDTH/2 - 9*w, LCD_HEIGHT/2 + h, "SCROLL+: +10"); rb->lcd_putsxy(LCD_WIDTH/2 - 9*w, LCD_HEIGHT/2 + 2*h + 1, "SCROLL-: -10"); #else rb->lcd_putsxy(LCD_WIDTH/2 - 9*w, LCD_HEIGHT/2 - h-2, "RIGHT: +1"); rb->lcd_putsxy(LCD_WIDTH/2 - 9*w, LCD_HEIGHT/2 - 1, "LEFT: -1"); rb->lcd_putsxy(LCD_WIDTH/2 - 9*w, LCD_HEIGHT/2 + h, "UP: +10"); rb->lcd_putsxy(LCD_WIDTH/2 - 9*w, LCD_HEIGHT/2 + 2*h + 1, "DOWN: -10"); #endif rb->lcd_update_rect(LCD_WIDTH/2 - 9*w - 2, LCD_HEIGHT/2 - 9*h/2, 37*w/2 + 1, 8*h-2); #endif while(true) { button = rb->button_get(true); switch(button) { case BJACK_UP: case (BJACK_UP|BUTTON_REPEAT): if (amount + 10 < upper_limit + 1) { amount += 10; changed = true; } break; case BJACK_DOWN: case (BJACK_DOWN|BUTTON_REPEAT): if (amount - 10 > lower_limit - 1) { amount -= 10; changed = true; } break; case BJACK_RIGHT: case (BJACK_RIGHT|BUTTON_REPEAT): if (amount + 1 < upper_limit + 1) { amount++; changed = true; } break; case BJACK_LEFT: case (BJACK_LEFT|BUTTON_REPEAT): if (amount - 1 > lower_limit - 1) { amount--; changed = true; } break; case BJACK_MAX : amount = upper_limit; changed = true; break; case BJACK_MIN : amount = lower_limit; changed = true; break; case BJACK_QUIT: return 0; case BJACK_START: #if LCD_DEPTH > 1 rb->lcd_set_foreground(FG_COLOR); rb->lcd_set_background(BG_COLOR); #endif rb->lcd_clear_display(); return amount; } if(changed) { rb->snprintf(str, 9, "$%d", amount); #if LCD_HEIGHT <= 64 rb->lcd_puts(0, 2, str); rb->lcd_update(); #else rb->lcd_set_drawmode(DRMODE_BG+DRMODE_INVERSEVID); rb->lcd_fillrect(LCD_WIDTH/2 - 9*w, LCD_HEIGHT/2 - 3*h, 5*w, h); rb->lcd_set_drawmode(DRMODE_SOLID); rb->lcd_putsxy(LCD_WIDTH/2 - 9*w, LCD_HEIGHT/2 - 3*h, str); rb->lcd_update_rect(LCD_WIDTH/2 - 9*w, LCD_HEIGHT/2 - 3*h, 5*w, h); #endif changed = false; } } } /***************************************************************************** * blackjack_get_bet() gets the player's bet. ******************************************************************************/ static void blackjack_get_bet(struct game_context* bj) { bj->current_bet = blackjack_get_amount("Please enter a bet", 10, bj->player_money, bj->current_bet); } /***************************************************************************** * double_down() returns one final card then finishes the game ******************************************************************************/ static void double_down(struct game_context* bj) { bj->current_bet *= 2; bj->player_cards[0][bj->num_player_cards[0]] = new_card(); bj->player_total += bj->player_cards[0][bj->num_player_cards[0]].value; bj->num_player_cards[0]++; } /***************************************************************************** * split() checks if the player wants to split and acts accordingly. * When bj->split_status is 1, no split occurred. 2 means the player split and 3 * means a split has already occurred and the first hand is done. ******************************************************************************/ static void split(struct game_context* bj) { if (blackjack_get_yes_no("Split?") == 1) bj->split_status = 1; else { bj->split_status = 2; bj->current_bet *= 2; bj->num_player_cards[0] = 1; bj->num_player_cards[1] = 1; bj->player_cards[1][0] = bj->player_cards[0][1]; bj->player_total = bj->player_cards[0][0].value; } } /***************************************************************************** * insurance() see if the player wants to buy insurance and how much. ******************************************************************************/ static unsigned int insurance(struct game_context* bj) { unsigned int insurance, max_amount; insurance = blackjack_get_yes_no("Buy Insurance?"); bj->asked_insurance = true; max_amount = bj->current_bet < (unsigned int)bj->player_money ? bj->current_bet/2 : (unsigned int)bj->player_money; if (insurance == 1) return 0; insurance = blackjack_get_amount("How much?", 0, max_amount, 0); redraw_board(bj); return insurance; } /***************************************************************************** * play_again() checks to see if the player wants to keep playing. ******************************************************************************/ static unsigned int play_again(void) { return blackjack_get_yes_no("Play Again?"); } /***************************************************************************** * blackjack_menu() is the initial menu at the start of the game. ******************************************************************************/ static unsigned int blackjack_menu(struct game_context* bj) { int button; char *title = "Blackjack"; char str[18]; unsigned int i, w, h; bool breakout = false; bool showscores = false; while(true){ #if LCD_DEPTH > 1 rb->lcd_set_background(BG_COLOR); rb->lcd_set_foreground(FG_COLOR); #endif rb->lcd_clear_display(); if(!showscores) { /* welcome screen to display key bindings */ rb->lcd_getstringsize(title, &w, &h); rb->lcd_putsxy((LCD_WIDTH-w)/2, 0, title); #if CONFIG_KEYPAD == RECORDER_PAD rb->lcd_puts(0, 1, "ON: start"); rb->lcd_puts(0, 2, "OFF: exit"); rb->lcd_puts(0, 3, "F1: hit"); rb->lcd_puts(0, 4, "F2: stay"); rb->lcd_puts(0, 5, "F3: double down"); rb->lcd_puts(0, 6, "PLAY: save/resume"); rb->snprintf(str, 21, "High Score: $%d", bj->highscores[0]); rb->lcd_puts(0, 7, str); #elif CONFIG_KEYPAD == ONDIO_PAD rb->lcd_puts(0, 1, "MENU: start"); rb->lcd_puts(0, 2, "OFF: exit"); rb->lcd_puts(0, 3, "LEFT: hit"); rb->lcd_puts(0, 4, "RIGHT: stay"); rb->lcd_puts(0, 5, "UP: double down"); rb->lcd_puts(0, 6, "DOWN: save/resume"); rb->snprintf(str, 21, "High Score: $%d", bj->highscores[0]); rb->lcd_puts(0, 7, str); #elif (CONFIG_KEYPAD == IRIVER_H100_PAD) || (CONFIG_KEYPAD == IRIVER_H300_PAD) rb->lcd_puts(0, 2, "PLAY to start & to hit"); rb->lcd_puts(0, 3, "STOP to exit"); rb->lcd_puts(0, 4, "REC to stay"); rb->lcd_puts(0, 5, "NAVI to double down "); rb->lcd_puts(0, 6, " & to view highscores"); rb->lcd_puts(0, 7, "AB to save/resume"); rb->snprintf(str, 21, "High Score: $%d", bj->highscores[0]); rb->lcd_puts(0, 8, str); #elif CONFIG_KEYPAD == IRIVER_H10_PAD rb->lcd_puts(0, 2, "PLAY to start & hit"); rb->lcd_puts(0, 3, "POWER to exit"); rb->lcd_puts(0, 4, ">>| to stay"); rb->lcd_puts(0, 5, "|<< to double down"); rb->lcd_puts(0, 6, "LEFT to view scores"); rb->lcd_puts(0, 7, "RIGHT to save/resume"); rb->snprintf(str, 21, "High Score: $%d", bj->highscores[0]); rb->lcd_puts(0, 8, str); #elif (CONFIG_KEYPAD == IPOD_4G_PAD) || \ (CONFIG_KEYPAD == IPOD_3G_PAD) || \ (CONFIG_KEYPAD == IPOD_1G2G_PAD) #if LCD_WIDTH >=176 rb->lcd_puts(0, 2, "SELECT to start & to hit"); rb->lcd_puts(0, 3, "MENU to exit"); rb->lcd_puts(0, 4, ">>| to stay & to view highscores"); rb->lcd_puts(0, 5, "|<< to double down"); rb->lcd_puts(0, 6, "PLAY to save/resume"); rb->snprintf(str, 21, "High Score: $%d", bj->highscores[0]); rb->lcd_puts(0, 7, str); #else rb->lcd_puts(0, 2, "SELECT to start & to "); rb->lcd_puts(0, 3, " hit"); rb->lcd_puts(0, 4, "MENU to exit"); rb->lcd_puts(0, 5, ">>| to stay & to view "); rb->lcd_puts(0, 6, " highscores"); rb->lcd_puts(0, 7, "|<< to double down"); rb->lcd_puts(0, 8, "PLAY to save/resume"); rb->snprintf(str, 21, "High Score: $%d", bj->highscores[0]); rb->lcd_puts(0, 9, str); #endif #elif CONFIG_KEYPAD == IAUDIO_X5M5_PAD rb->lcd_puts(0, 2, "PLAY to start to hit"); rb->lcd_puts(0, 3, "POWER to exit"); rb->lcd_puts(0, 4, "SELECT to hit"); rb->lcd_puts(0, 5, "REC to stay"); rb->lcd_puts(0, 6, "PLAY to double down"); rb->lcd_puts(0, 7, "RIGHT to view highscores "); rb->lcd_puts(0, 8, "DOWN to save/resume"); rb->snprintf(str, 21, "High Score: $%d", bj->highscores[0]); rb->lcd_puts(0, 9, str); #elif CONFIG_KEYPAD == IRIVER_IFP7XX_PAD rb->lcd_puts(0, 2, "AB to start & to"); rb->lcd_puts(0, 3, " stay"); rb->lcd_puts(0, 4, "EQ to hit"); rb->lcd_puts(0, 5, "PLAY to exit"); rb->lcd_puts(0, 6, "CLICK to double down"); rb->lcd_puts(0, 7, "& to view highscores"); rb->lcd_puts(0, 8, "AB+EQ to save/resume"); rb->snprintf(str, 21, "High Score: $%d", bj->highscores[0]); rb->lcd_puts(0, 9, str); #elif CONFIG_KEYPAD == GIGABEAT_PAD rb->lcd_puts(0, 2, "POWER to start"); rb->lcd_puts(0, 3, "A to exit"); rb->lcd_puts(0, 4, "VOL+ to hit"); rb->lcd_puts(0, 5, "VOL- to stay"); rb->lcd_puts(0, 6, "CENTER to double down"); rb->lcd_puts(0, 6, "RIGHT to view highscores "); rb->lcd_puts(0, 8, "MENU to save/resume"); rb->snprintf(str, 21, "High Score: $%d", bj->highscores[0]); rb->lcd_puts(0, 9, str); #elif (CONFIG_KEYPAD == SANSA_E200_PAD) rb->lcd_puts(0, 2, "SELECT to start & to hit"); rb->lcd_puts(0, 3, "POWER to exit"); rb->lcd_puts(0, 4, "RIGHT to stay"); rb->lcd_puts(0, 5, "LEFT to double down"); rb->lcd_puts(0, 6, "REC to save/resume"); rb->lcd_puts(0, 7, "UP to view scores"); rb->snprintf(str, 21, "High Score: $%d", bj->highscores[0]); rb->lcd_puts(0, 8, str); #elif (CONFIG_KEYPAD == SANSA_C200_PAD) rb->lcd_puts(0, 2, "SELECT to start & to hit"); rb->lcd_puts(0, 3, "POWER to exit"); rb->lcd_puts(0, 4, "RIGHT to stay"); rb->lcd_puts(0, 5, "LEFT to double down"); rb->lcd_puts(0, 6, "DOWN to save/resume"); rb->lcd_puts(0, 7, "REC to view scores"); rb->snprintf(str, 21, "High Score: $%d", bj->highscores[0]); rb->lcd_puts(0, 9, str); #endif } else { rb->snprintf(str, 12, "%s", "High Scores"); rb->lcd_getstringsize(str, &w, &h); rb->lcd_putsxy((LCD_WIDTH-w)/2, 0, str); /* print high scores */ for(i=0; isnprintf(str, 14, "#%02d: $%d", i+1, bj->highscores[i]); rb->lcd_puts(0, i+1, str); } } rb->lcd_update(); /* handle menu button presses */ button = rb->button_get(true); switch(button) { case BJACK_START: /* start playing */ breakout = true; break; case BJACK_QUIT: /* quit program */ if(showscores) { showscores = 0; break; } return BJ_QUIT; case BJACK_RESUME:/* resume game */ if(!blackjack_loadgame(bj)) { rb->splash(HZ*2, "Nothing to resume"); } else { rb->splash(HZ*2, "Loading..."); breakout = true; } break; case BJACK_SCORES:/* toggle high scores */ showscores = !showscores; break; default: if(rb->default_event_handler_ex(button, blackjack_callback, (void*) bj) == SYS_USB_CONNECTED) return BJ_USB; break; } if(breakout) break; } return(0); } /***************************************************************************** * blackjack() is the main game subroutine, it returns the final game status. ******************************************************************************/ static int blackjack(struct game_context* bj) { int button; unsigned int w, h, temp_var, done = 0, todo = 1; signed int temp; bool breakout = false; bool dbl_down = false; /* don't resume by default */ bj->resume = false; /******************** * menu * ********************/ temp_var = blackjack_menu(bj); if (temp_var == BJ_QUIT || temp_var == BJ_USB) return temp_var; /******************** * init * ********************/ blackjack_init(bj); bj->current_bet=10; /******************** * play * ********************/ /* check for resumed game */ if(bj->resume) { bj->resume = false; redraw_board(bj); if (bj->split_status == 2) { todo=2; player_x = bj->num_player_cards[0] * 10 + 4; } else if (bj->split_status == 3) { player_x = bj->num_player_cards[1] * 10 + LCD_WIDTH/2 + 4; todo=2; done=1; } } else { bj->player_money = 1000; blackjack_get_bet(bj); if (bj->current_bet == 0) return BJ_QUIT; rb->lcd_clear_display(); deal_init_cards(bj); blackjack_drawtable(bj); } rb->lcd_update(); breakout = false; while(true){ if(bj->player_total == 21 && bj->num_player_cards[0] == 2) { bj->is_blackjack = true; bj->end_hand = true; finish_game(bj); } else if(bj->dealer_cards[1].is_soft_ace && !breakout && !bj->asked_insurance) { temp_var = insurance(bj); if (bj->dealer_total == 21) { rb->splash(HZ, "Dealer has blackjack"); bj->player_money += temp_var; bj->end_hand = true; breakout = true; redraw_board(bj); finish_game(bj); } else { rb->splash(HZ, "Dealer does not have blackjack"); bj->player_money -= temp_var; breakout = true; redraw_board(bj); rb->lcd_update(); } } if(bj->split_status == 0 && bj->player_cards[0][0].num == bj->player_cards[0][1].num) { split(bj); redraw_board(bj); rb->lcd_update_rect(0, LCD_HEIGHT/2, LCD_WIDTH, LCD_HEIGHT/2); if (bj->split_status == 2) { todo++; player_x = bj->num_player_cards[0] * 10 + 4; } } while(done < todo) { button = rb->button_get(true); switch(button) { case BJACK_HIT: NEXT_CARD = new_card(); bj->player_total += NEXT_CARD.value; draw_card(NEXT_CARD, true, player_x, player_y); bj->num_player_cards[done]++; if (bj->num_player_cards[done] == MAX_CARDS + 1) { redraw_board(bj); rb->lcd_update_rect(0, LCD_HEIGHT/2, LCD_WIDTH, LCD_HEIGHT/2); } else if (bj->num_player_cards[done]>MAX_CARDS || todo > 1) { rb->lcd_update_rect(player_x, player_y, CARD_WIDTH+2, CARD_HEIGHT+2); player_x += 10; } else { rb->lcd_update_rect(player_x, player_y, CARD_WIDTH+2, CARD_HEIGHT+2); player_x += CARD_WIDTH + 4; } update_total(bj); break; case BJACK_STAY: bj->end_hand = true; break; case BJACK_DOUBLEDOWN: if ((signed int)bj->current_bet * 2 < bj->player_money + 1 && bj->num_player_cards[0]==2 && todo==1) { double_down(bj); dbl_down = true; if (bj->player_total < 22) { bj->end_hand = true; finish_game(bj); } } else if((signed int)bj->current_bet * 2 > bj->player_money) { rb->splash(HZ, "Not enough money to double down."); redraw_board(bj); rb->lcd_update(); } break; case BJACK_RESUME: /* save and end game */ rb->splash(HZ, "Saving game..."); blackjack_savegame(bj); /* fall through to BJACK_QUIT */ case BJACK_QUIT: return BJ_END; } while (bj->player_total > 21 && !bj->end_hand) { temp = check_for_aces(bj->player_cards[done], bj->num_player_cards[done]); if(temp != -1) { bj->player_cards[done][temp].is_soft_ace = false; bj->player_total -= 10; update_total(bj); if (dbl_down) { bj->end_hand = true; finish_game(bj); } } else bj->end_hand = true; } if (bj->end_hand) { done++; if(todo > 1) { if (done == 2) { temp = bj->player_total; bj->player_total = temp_var; temp_var = temp; finish_game(bj); rb->lcd_getstringsize(" Split 1 ", &w, &h); rb->lcd_putsxy(LCD_WIDTH/2-w/2, LCD_HEIGHT/2-3*h/2, " Split 1 "); rb->lcd_update_rect(LCD_WIDTH/2-w/2, LCD_HEIGHT/2-3*h/2, w,h); bj->current_bet /= 2; rb->lcd_update_rect(LCD_WIDTH/2-w/2, LCD_HEIGHT/2-3*h/2, w,h); rb->sleep(HZ*2); bj->player_total = temp_var; finish_game(bj); rb->lcd_getstringsize(" Split 2 ", &w, &h); rb->lcd_putsxy(LCD_WIDTH/2-w/2, LCD_HEIGHT/2-3*h/2, " Split 2 "); rb->lcd_update_rect(LCD_WIDTH/2-w/2, LCD_HEIGHT/2-3*h/2, w,h); rb->sleep(HZ*2); } else { bj->end_hand = false; bj->split_status = 3; temp_var = bj->player_total; bj->player_total = bj->player_cards[1][0].value; update_total(bj); redraw_board(bj); player_x += 10; rb->lcd_update(); } } else finish_game(bj); } } if (bj->player_money < 10) { rb->sleep(HZ); return BJ_LOSE; } if (bj->end_hand) { /* If hand is over */ if (play_again() != 0) /* User wants to quit */ return BJ_END; else { /* User keeps playing */ breakout = false; redraw_board(bj); if(dbl_down) { bj->current_bet /= 2; dbl_down = false; } done = 0; todo = 1; blackjack_init(bj); blackjack_get_bet(bj); if (bj->current_bet == 0) return BJ_END; deal_init_cards(bj); blackjack_drawtable(bj); rb->lcd_update(); } } } /* Never reached */ return PLUGIN_OK; } /***************************************************************************** * plugin entry point. ******************************************************************************/ enum plugin_status plugin_start(struct plugin_api* api, void* parameter) { struct game_context bj; bool exit = false; unsigned int position; char str[19]; (void)parameter; rb = api; #if LCD_DEPTH > 1 rb->lcd_set_backdrop(NULL); #endif /* load high scores */ blackjack_loadscores(&bj); rb->lcd_setfont(FONT_SYSFIXED); while(!exit) { switch(blackjack(&bj)){ case BJ_LOSE: rb->splash(HZ, "Not enough money to continue"); /* fall through to BJ_END */ case BJ_END: if(!bj.resume) { if((position = blackjack_recordscore(&bj))) { rb->snprintf(str, 19, "New high score #%d!", position); rb->splash(HZ*2, str); } } break; case BJ_USB: rb->lcd_setfont(FONT_UI); return PLUGIN_USB_CONNECTED; case BJ_QUIT: if(bj.dirty) { rb->splash(HZ, "Saving high scores..."); blackjack_savescores(&bj); } exit = true; break; default: break; } } rb->lcd_setfont(FONT_UI); return PLUGIN_OK; } f='#n1466'>1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590
/* Emacs style mode select   -*- C++ -*-
 *-----------------------------------------------------------------------------
 *
 *
 *  PrBoom a Doom port merged with LxDoom and LSDLDoom
 *  based on BOOM, a modified and improved DOOM engine
 *  Copyright (C) 1999 by
 *  id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman
 *  Copyright (C) 1999-2000 by
 *  Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze
 *
 *  This program is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU General Public License
 *  as published by the Free Software Foundation; either version 2
 *  of the License, or (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 *  02111-1307, USA.
 *
 * DESCRIPTION:  Heads-up displays
 *
 *-----------------------------------------------------------------------------
 */

// killough 5/3/98: remove unnecessary headers

#include "doomstat.h"
#include "hu_stuff.h"
#include "hu_lib.h"
#include "st_stuff.h" /* jff 2/16/98 need loc of status bar */
#include "w_wad.h"
#include "s_sound.h"
#include "dstrings.h"
#include "sounds.h"
//#include "d_deh.h"   /* Ty 03/27/98 - externalization of mapnamesx arrays */
#include "g_game.h"
#include "m_swap.h"

// global heads up display controls

int hud_active;       //jff 2/17/98 controls heads-up display mode
int hud_displayed;    //jff 2/23/98 turns heads-up display on/off
int hud_nosecrets;    //jff 2/18/98 allows secrets line to be disabled in HUD
int hud_distributed;  //jff 3/4/98 display HUD in different places on screen
int hud_graph_keys=1; //jff 3/7/98 display HUD keys as graphics

//
// Locally used constants, shortcuts.
//
// Ty 03/28/98 -
// These four shortcuts modifed to reflect char ** of mapnamesx[]
#define HU_TITLE  (*mapnames[(gameepisode-1)*9+gamemap-1])
#define HU_TITLE2 (*mapnames2[gamemap-1])
#define HU_TITLEP (*mapnamesp[gamemap-1])
#define HU_TITLET (*mapnamest[gamemap-1])
#define HU_TITLEHEIGHT  1
#define HU_TITLEX 0
//jff 2/16/98 change 167 to ST_Y-1
// CPhipps - changed to ST_TY
// proff - changed to 200-ST_HEIGHT for stretching
#define HU_TITLEY ((200-ST_HEIGHT) - 1 - SHORT(hu_font[0].height))

//jff 2/16/98 add coord text widget coordinates
// proff - changed to SCREENWIDTH to 320 for stretching
#define HU_COORDX (320 - 13*SHORT(hu_font2['A'-HU_FONTSTART].width))
//jff 3/3/98 split coord widget into three lines in upper right of screen
#define HU_COORDX_Y (1 + 0*SHORT(hu_font['A'-HU_FONTSTART].height))
#define HU_COORDY_Y (2 + 1*SHORT(hu_font['A'-HU_FONTSTART].height))
#define HU_COORDZ_Y (3 + 2*SHORT(hu_font['A'-HU_FONTSTART].height))

//jff 2/16/98 add ammo, health, armor widgets, 2/22/98 less gap
#define HU_GAPY 8
#define HU_HUDHEIGHT (6*HU_GAPY)
#define HU_HUDX 2
#define HU_HUDY (200-HU_HUDHEIGHT-1)
#define HU_MONSECX (HU_HUDX)
#define HU_MONSECY (HU_HUDY+0*HU_GAPY)
#define HU_KEYSX   (HU_HUDX)
//jff 3/7/98 add offset for graphic key widget
#define HU_KEYSGX  (HU_HUDX+4*SHORT(hu_font2['A'-HU_FONTSTART].width))
#define HU_KEYSY   (HU_HUDY+1*HU_GAPY)
#define HU_WEAPX   (HU_HUDX)
#define HU_WEAPY   (HU_HUDY+2*HU_GAPY)
#define HU_AMMOX   (HU_HUDX)
#define HU_AMMOY   (HU_HUDY+3*HU_GAPY)
#define HU_HEALTHX (HU_HUDX)
#define HU_HEALTHY (HU_HUDY+4*HU_GAPY)
#define HU_ARMORX  (HU_HUDX)
#define HU_ARMORY  (HU_HUDY+5*HU_GAPY)

//jff 3/4/98 distributed HUD positions
#define HU_HUDX_LL 2
#define HU_HUDY_LL (200-2*HU_GAPY-1)
// proff/nicolas 09/20/98: Changed for high-res
#define HU_HUDX_LR (320-120)
#define HU_HUDY_LR (200-2*HU_GAPY-1)
// proff/nicolas 09/20/98: Changed for high-res
#define HU_HUDX_UR (320-96)
#define HU_HUDY_UR 2
#define HU_MONSECX_D (HU_HUDX_LL)
#define HU_MONSECY_D (HU_HUDY_LL+0*HU_GAPY)
#define HU_KEYSX_D   (HU_HUDX_LL)
#define HU_KEYSGX_D  (HU_HUDX_LL+4*SHORT(hu_font2['A'-HU_FONTSTART].width))
#define HU_KEYSY_D   (HU_HUDY_LL+1*HU_GAPY)
#define HU_WEAPX_D   (HU_HUDX_LR)
#define HU_WEAPY_D   (HU_HUDY_LR+0*HU_GAPY)
#define HU_AMMOX_D   (HU_HUDX_LR)
#define HU_AMMOY_D   (HU_HUDY_LR+1*HU_GAPY)
#define HU_HEALTHX_D (HU_HUDX_UR)
#define HU_HEALTHY_D (HU_HUDY_UR+0*HU_GAPY)
#define HU_ARMORX_D  (HU_HUDX_UR)
#define HU_ARMORY_D  (HU_HUDY_UR+1*HU_GAPY)

//#define HU_INPUTTOGGLE  't' // not used                           // phares
#define HU_INPUTX HU_MSGX
#define HU_INPUTY (HU_MSGY + HU_MSGHEIGHT*(SHORT(hu_font[0].height) +1))
#define HU_INPUTWIDTH 64
#define HU_INPUTHEIGHT  1

#define key_alt KEY_RALT
#define key_shift KEY_RSHIFT

const char* chat_macros[] =
   // Ty 03/27/98 - *not* externalized
   // CPhipps - const char*
   {
      HUSTR_CHATMACRO1,
      HUSTR_CHATMACRO1,
      HUSTR_CHATMACRO1,
      HUSTR_CHATMACRO1,
      HUSTR_CHATMACRO1,
      HUSTR_CHATMACRO1,
      HUSTR_CHATMACRO1,
      HUSTR_CHATMACRO1,
      HUSTR_CHATMACRO1,
      HUSTR_CHATMACRO1
   };

const char* player_names[] =
   // Ty 03/27/98 - *not* externalized
   // CPhipps - const char*
   {
      HUSTR_PLRGREEN,
      HUSTR_PLRINDIGO,
      HUSTR_PLRBROWN,
      HUSTR_PLRRED
   };

//jff 3/17/98 translate player colmap to text color ranges
int plyrcoltran[MAXPLAYERS]={CR_GREEN,CR_GRAY,CR_BROWN,CR_RED};

char chat_char;                 // remove later.
static player_t*  plr;

// font sets
patchnum_t hu_font[HU_FONTSIZE];
patchnum_t *hu_font2;
patchnum_t *hu_fontk;//jff 3/7/98 added for graphic key indicators
patchnum_t hu_msgbg[9];          //jff 2/26/98 add patches for message background

// widgets
static hu_textline_t  w_title;
static hu_stext_t     w_message;
static hu_itext_t     w_chat;
static hu_itext_t     w_inputbuffer[MAXPLAYERS];
static hu_textline_t  w_coordx; //jff 2/16/98 new coord widget for automap
static hu_textline_t  w_coordy; //jff 3/3/98 split coord widgets automap
static hu_textline_t  w_coordz; //jff 3/3/98 split coord widgets automap
static hu_textline_t  w_ammo;   //jff 2/16/98 new ammo widget for hud
static hu_textline_t  w_health; //jff 2/16/98 new health widget for hud
static hu_textline_t  w_armor;  //jff 2/16/98 new armor widget for hud
static hu_textline_t  w_weapon; //jff 2/16/98 new weapon widget for hud
static hu_textline_t  w_keys;   //jff 2/16/98 new keys widget for hud
static hu_textline_t  w_gkeys;  //jff 3/7/98 graphic keys widget for hud
static hu_textline_t  w_monsec; //jff 2/16/98 new kill/secret widget for hud
static hu_mtext_t     w_rtext;  //jff 2/26/98 text message refresh widget

static boolean    always_off = false;
static char       chat_dest[MAXPLAYERS];
boolean           chat_on;
static boolean    message_on;
static boolean    message_list; //2/26/98 enable showing list of messages
boolean           message_dontfuckwithme;
static boolean    message_nottobefuckedwith;
static int        message_counter;
extern int        showMessages;
extern boolean    automapactive;
static boolean    headsupactive = false;

//jff 2/16/98 hud supported automap colors added
int hudcolor_titl;  // color range of automap level title
int hudcolor_xyco;  // color range of new coords on automap
//jff 2/16/98 hud text colors, controls added
int hudcolor_mesg;  // color range of scrolling messages
int hudcolor_chat;  // color range of chat lines
int hud_msg_lines;  // number of message lines in window
//jff 2/26/98 hud text colors, controls added
int hudcolor_list;  // list of messages color
int hud_list_bgon;  // enable for solid window background for message list

//jff 2/16/98 initialization strings for ammo, health, armor widgets
static char *hud_coordstrx;
static char *hud_coordstry;
static char *hud_coordstrz;
static char *hud_ammostr;
static char *hud_healthstr;
static char *hud_armorstr;
static char *hud_weapstr;
static char *hud_keysstr;
static char *hud_gkeysstr; //jff 3/7/98 add support for graphic key display
static char *hud_monsecstr;

//jff 2/16/98 declaration of color switch points
extern int ammo_red;
extern int ammo_yellow;
extern int health_red;
extern int health_yellow;
extern int health_green;
extern int armor_red;
extern int armor_yellow;
extern int armor_green;

// key tables
// jff 5/10/98 french support removed,
// as it was not being used and couldn't be easily tested
//
const char* shiftxform;

const char english_shiftxform[] =
   {
      0,
      1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
      11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
      21, 22, 23, 24, 25, 26, 27, 28, 29, 30,
      31,
      ' ', '!', '"', '#', '$', '%', '&',
      '"', // shift-'
      '(', ')', '*', '+',
      '<', // shift-,
      '_', // shift--
      '>', // shift-.
      '?', // shift-/
      ')', // shift-0
      '!', // shift-1
      '@', // shift-2
      '#', // shift-3
      '$', // shift-4
      '%', // shift-5
      '^', // shift-6
      '&', // shift-7
      '*', // shift-8
      '(', // shift-9
      ':',
      ':', // shift-;
      '<',
      '+', // shift-=
      '>', '?', '@',
      'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',
      'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
      '[', // shift-[
      '!', // shift-backslash - OH MY GOD DOES WATCOM SUCK
      ']', // shift-]
      '"', '_',
      '\'', // shift-`
      'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',
      'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
      '{', '|', '}', '~', 127
   };

//
// HU_Init()
//
// Initialize the heads-up display, text that overwrites the primary display
//
// Passed nothing, returns nothing
//
void HU_Init(void)
{
   int  i;
   int  j;
   char  buffer[9];

   shiftxform = english_shiftxform;

   // malloc all the strings, trying to get size down
   hud_ammostr=malloc(80*sizeof(char));
   hud_healthstr=malloc(80*sizeof(char));
   hud_armorstr=malloc(80*sizeof(char));
   hud_weapstr=malloc(80*sizeof(char));
   hud_keysstr=malloc(80*sizeof(char));
   hud_gkeysstr=malloc(80*sizeof(char));
   hud_monsecstr=malloc(80*sizeof(char));
   hud_coordstrx=malloc(32*sizeof(char));
   hud_coordstry=malloc(32*sizeof(char));
   hud_coordstrz=malloc(32*sizeof(char));
   hu_fontk=malloc(HU_FONTSIZE*sizeof(patchnum_t));
   hu_font2=malloc(HU_FONTSIZE*sizeof(patchnum_t));

   // load the heads-up font
   j = HU_FONTSTART;
   for (i=0;i<HU_FONTSIZE;i++,j++)
   {
      if ('0'<=j && j<='9')
      {
         snprintf(buffer, sizeof(buffer), "DIG%d",j-48);
         R_SetPatchNum(hu_font2 +i, buffer);
         snprintf(buffer, sizeof(buffer), "STCFN%s%d", (j/10>0?"0":"00"), j); //NOTE ROCKHACK: "STCFN%.3d"
         R_SetPatchNum(&hu_font[i], buffer);
      }
      else if ('A'<=j && j<='Z')
      {
         snprintf(buffer, sizeof(buffer), "DIG%c",j);
         R_SetPatchNum(hu_font2 +i, buffer);
         snprintf(buffer, sizeof(buffer), "STCFN%s%d", (j/10>0?"0":"00"), j); //NOTE ROCKHACK: "STCFN%.3d"
         R_SetPatchNum(&hu_font[i], buffer);
      }
      else if (j=='-')
      {
         R_SetPatchNum(hu_font2 +i, "DIG45");
         R_SetPatchNum(&hu_font[i], "STCFN045");
      }
      else if (j=='/')
      {
         R_SetPatchNum(hu_font2 +i, "DIG47");
         R_SetPatchNum(&hu_font[i], "STCFN047");
      }
      else if (j==':')
      {
         R_SetPatchNum(hu_font2 +i, "DIG58");
         R_SetPatchNum(&hu_font[i], "STCFN058");
      }
      else if (j=='[')
      {
         R_SetPatchNum(hu_font2 +i, "DIG91");
         R_SetPatchNum(&hu_font[i], "STCFN091");
      }
      else if (j==']')
      {
         R_SetPatchNum(hu_font2 +i, "DIG93");
         R_SetPatchNum(&hu_font[i], "STCFN093");
      }
      else if (j<97)
      {
         snprintf(buffer, sizeof(buffer), "STCFN%s%d", (j/10>0?"0":"00"), j); //NOTE ROCKHACK: "STCFN%.3d"
         R_SetPatchNum(hu_font2 +i, buffer);
         R_SetPatchNum(&hu_font[i], buffer);
         //jff 2/23/98 make all font chars defined, useful or not
      }
      else if (j>122)
      {
         snprintf(buffer, sizeof(buffer), "STBR%d", j); //NOTE: "STBR%.3d"
         R_SetPatchNum(hu_font2 +i, buffer);
         R_SetPatchNum(&hu_font[i], buffer);
      }
      else
         hu_font[i] = hu_font[0]; //jff 2/16/98 account for gap
   }

   // CPhipps - load patches for message background
   for (i=0; i<9; i++) {
      snprintf(buffer, sizeof(buffer), "BOX%c%c", "UCL"[i/3], "LCR"[i%3]);
      R_SetPatchNum(&hu_msgbg[i], buffer);
   }

   // CPhipps - load patches for keys and double keys
   for (i=0; i<6; i++) {
      snprintf(buffer, sizeof(buffer), "STKEYS%d", i);
      R_SetPatchNum(hu_fontk+i, buffer);
   }
}

//
// HU_Stop()
//
// Make the heads-up displays inactive
//
// Passed nothing, returns nothing
//
void HU_Stop(void)
{
   headsupactive = false;
}

//
// HU_Start(void)
//
// Create and initialize the heads-up widgets, software machines to
// maintain, update, and display information over the primary display
//
// This routine must be called after any change to the heads up configuration
// in order for the changes to take effect in the actual displays
//
// Passed nothing, returns nothing
//
void HU_Start(void)
{

   int   i;
   const char* s; /* cph - const */

   if (headsupactive)                    // stop before starting
      HU_Stop();

   plr = &players[displayplayer];        // killough 3/7/98
   message_on = false;
   message_dontfuckwithme = false;
   message_nottobefuckedwith = false;
   chat_on = false;

   // create the message widget
   // messages to player in upper-left of screen
   HUlib_initSText
   (
      &w_message,
      HU_MSGX,
      HU_MSGY,
      HU_MSGHEIGHT,
      hu_font,
      HU_FONTSTART,
      hudcolor_mesg,
      &message_on
   );

   //jff 2/16/98 added some HUD widgets
   // create the map title widget - map title display in lower left of automap
   HUlib_initTextLine
   (
      &w_title,
      HU_TITLEX,
      HU_TITLEY,
      hu_font,
      HU_FONTSTART,
      hudcolor_titl
   );

   // create the hud health widget
   // bargraph and number for amount of health,
   // lower left or upper right of screen
   HUlib_initTextLine
   (
      &w_health,
      hud_distributed? HU_HEALTHX_D : HU_HEALTHX,  //3/4/98 distribute
      hud_distributed? HU_HEALTHY_D : HU_HEALTHY,
      hu_font2,
      HU_FONTSTART,
      CR_GREEN
   );

   // create the hud armor widget
   // bargraph and number for amount of armor,
   // lower left or upper right of screen
   HUlib_initTextLine
   (
      &w_armor,
      hud_distributed? HU_ARMORX_D : HU_ARMORX,    //3/4/98 distribute
      hud_distributed? HU_ARMORY_D : HU_ARMORY,
      hu_font2,
      HU_FONTSTART,
      CR_GREEN
   );

   // create the hud ammo widget
   // bargraph and number for amount of ammo for current weapon,
   // lower left or lower right of screen
   HUlib_initTextLine
   (
      &w_ammo,
      hud_distributed? HU_AMMOX_D : HU_AMMOX,      //3/4/98 distribute
      hud_distributed? HU_AMMOY_D : HU_AMMOY,
      hu_font2,
      HU_FONTSTART,
      CR_GOLD
   );

   // create the hud weapons widget
   // list of numbers of weapons possessed
   // lower left or lower right of screen
   HUlib_initTextLine
   (
      &w_weapon,
      hud_distributed? HU_WEAPX_D : HU_WEAPX,      //3/4/98 distribute
      hud_distributed? HU_WEAPY_D : HU_WEAPY,
      hu_font2,
      HU_FONTSTART,
      CR_GRAY
   );

   // create the hud keys widget
   // display of key letters possessed
   // lower left of screen
   HUlib_initTextLine
   (
      &w_keys,
      hud_distributed? HU_KEYSX_D : HU_KEYSX,      //3/4/98 distribute
      hud_distributed? HU_KEYSY_D : HU_KEYSY,
      hu_font2,
      HU_FONTSTART,
      CR_GRAY
   );

   // create the hud graphic keys widget
   // display of key graphics possessed
   // lower left of screen
   HUlib_initTextLine
   (
      &w_gkeys,
      hud_distributed? HU_KEYSGX_D : HU_KEYSGX,    //3/4/98 distribute
      hud_distributed? HU_KEYSY_D : HU_KEYSY,
      hu_fontk,
      HU_FONTSTART,
      CR_RED
   );

   // create the hud monster/secret widget
   // totals and current values for kills, items, secrets
   // lower left of screen
   HUlib_initTextLine
   (
      &w_monsec,
      hud_distributed? HU_MONSECX_D : HU_MONSECX,  //3/4/98 distribute
      hud_distributed? HU_MONSECY_D : HU_MONSECY,
      hu_font2,
      HU_FONTSTART,
      CR_GRAY
   );

   // create the hud text refresh widget
   // scrolling display of last hud_msg_lines messages received
   if (hud_msg_lines>HU_MAXMESSAGES)
      hud_msg_lines=HU_MAXMESSAGES;
   //jff 4/21/98 if setup has disabled message list while active, turn it off
   message_list = hud_msg_lines > 1; //jff 8/8/98 initialize both ways
   //jff 2/26/98 add the text refresh widget initialization
   HUlib_initMText
   (
      &w_rtext,
      0,
      0,
      320,
      //    SCREENWIDTH,
      (hud_msg_lines+2)*HU_REFRESHSPACING,
      hu_font,
      HU_FONTSTART,
      hudcolor_list,
      hu_msgbg,
      &message_list
   );

   // initialize the automap's level title widget
   if (gamestate == GS_LEVEL) /* cph - stop SEGV here when not in level */
      switch (gamemode)
      {
      case shareware:
      case registered:
      case retail:
         s = HU_TITLE;
         break;

      case commercial:
      default:  // Ty 08/27/98 - modified to check mission for TNT/Plutonia
         s = (gamemission==pack_tnt)  ? HU_TITLET :
             (gamemission==pack_plut) ? HU_TITLEP : HU_TITLE2;
         break;
      } else s = "";
   while (*s)
      HUlib_addCharToTextLine(&w_title, *(s++));

   // create the automaps coordinate widget
   // jff 3/3/98 split coord widget into three lines: x,y,z
   // jff 2/16/98 added
   HUlib_initTextLine
   (
      &w_coordx,
      HU_COORDX,
      HU_COORDX_Y,
      hu_font,
      HU_FONTSTART,
      hudcolor_xyco
   );
   HUlib_initTextLine
   (
      &w_coordy,
      HU_COORDX,
      HU_COORDY_Y,
      hu_font,
      HU_FONTSTART,
      hudcolor_xyco
   );
   HUlib_initTextLine
   (
      &w_coordz,
      HU_COORDX,
      HU_COORDZ_Y,
      hu_font,
      HU_FONTSTART,
      hudcolor_xyco
   );

   // initialize the automaps coordinate widget
   //jff 3/3/98 split coordstr widget into 3 parts
   snprintf(hud_coordstrx,32*sizeof(char),"X: %d",0); //jff 2/22/98 added z
   s = hud_coordstrx;
   while (*s)
      HUlib_addCharToTextLine(&w_coordx, *(s++));
   snprintf(hud_coordstry,32*sizeof(char),"Y: %d",0); //jff 3/3/98 split x,y,z
   s = hud_coordstry;
   while (*s)
      HUlib_addCharToTextLine(&w_coordy, *(s++));
   snprintf(hud_coordstrz,32*sizeof(char),"Z: %d",0); //jff 3/3/98 split x,y,z
   s = hud_coordstrz;
   while (*s)
      HUlib_addCharToTextLine(&w_coordz, *(s++));

   //jff 2/16/98 initialize ammo widget
   strcpy(hud_ammostr,"AMM ");
   s = hud_ammostr;
   while (*s)
      HUlib_addCharToTextLine(&w_ammo, *(s++));

   //jff 2/16/98 initialize health widget
   strcpy(hud_healthstr,"HEL ");
   s = hud_healthstr;
   while (*s)
      HUlib_addCharToTextLine(&w_health, *(s++));

   //jff 2/16/98 initialize armor widget
   strcpy(hud_armorstr,"ARM ");
   s = hud_armorstr;
   while (*s)
      HUlib_addCharToTextLine(&w_armor, *(s++));

   //jff 2/17/98 initialize weapons widget
   strcpy(hud_weapstr,"WEA ");
   s = hud_weapstr;
   while (*s)
      HUlib_addCharToTextLine(&w_weapon, *(s++));

   //jff 2/17/98 initialize keys widget
   if (!deathmatch) //jff 3/17/98 show frags in deathmatch mode
      strcpy(hud_keysstr,"KEY ");
   else
      strcpy(hud_keysstr,"FRG ");
   s = hud_keysstr;
   while (*s)
      HUlib_addCharToTextLine(&w_keys, *(s++));

   //jff 2/17/98 initialize graphic keys widget
   strcpy(hud_gkeysstr," ");
   s = hud_gkeysstr;
   while (*s)
      HUlib_addCharToTextLine(&w_gkeys, *(s++));

   //jff 2/17/98 initialize kills/items/secret widget
   strcpy(hud_monsecstr,"STS ");
   s = hud_monsecstr;
   while (*s)
      HUlib_addCharToTextLine(&w_monsec, *(s++));

   // create the chat widget
   HUlib_initIText
   (
      &w_chat,
      HU_INPUTX,
      HU_INPUTY,
      hu_font,
      HU_FONTSTART,
      hudcolor_chat,
      &chat_on
   );

   // create the inputbuffer widgets, one per player
   for (i=0 ; i<MAXPLAYERS ; i++)
      HUlib_initIText
      (
         &w_inputbuffer[i],
         0,
         0,
         0,
         0,
         hudcolor_chat,
         &always_off
      );

   // now allow the heads-up display to run
   headsupactive = true;
}

//
// HU_MoveHud()
//
// Move the HUD display from distributed to compact mode or vice-versa
//
// Passed nothing, returns nothing
//
//jff 3/9/98 create this externally callable to avoid glitch
// when menu scatter's HUD due to delay in change of position
//
void HU_MoveHud(void)
{
   static int ohud_distributed=-1;

   //jff 3/4/98 move displays around on F5 changing hud_distributed
   if (hud_distributed!=ohud_distributed)
   {
      w_ammo.x =    hud_distributed? HU_AMMOX_D   : HU_AMMOX;
      w_ammo.y =    hud_distributed? HU_AMMOY_D   : HU_AMMOY;
      w_weapon.x =  hud_distributed? HU_WEAPX_D   : HU_WEAPX;
      w_weapon.y =  hud_distributed? HU_WEAPY_D   : HU_WEAPY;
      w_keys.x =    hud_distributed? HU_KEYSX_D   : HU_KEYSX;
      w_keys.y =    hud_distributed? HU_KEYSY_D   : HU_KEYSY;
      w_gkeys.x =   hud_distributed? HU_KEYSGX_D  : HU_KEYSGX;
      w_gkeys.y =   hud_distributed? HU_KEYSY_D   : HU_KEYSY;
      w_monsec.x =  hud_distributed? HU_MONSECX_D : HU_MONSECX;
      w_monsec.y =  hud_distributed? HU_MONSECY_D : HU_MONSECY;
      w_health.x =  hud_distributed? HU_HEALTHX_D : HU_HEALTHX;
      w_health.y =  hud_distributed? HU_HEALTHY_D : HU_HEALTHY;
      w_armor.x =   hud_distributed? HU_ARMORX_D  : HU_ARMORX;
      w_armor.y =   hud_distributed? HU_ARMORY_D  : HU_ARMORY;
   }
   ohud_distributed = hud_distributed;
}

//
// HU_Drawer()
//
// Draw all the pieces of the heads-up display
//
// Passed nothing, returns nothing
//
void HU_Drawer(void)
{
   char *s;
   player_t *plr;
   char ammostr[80];  //jff 3/8/98 allow plenty room for dehacked mods
   char healthstr[80];//jff
   char armorstr[80]; //jff
   int i,doit;

   plr = &players[displayplayer];         // killough 3/7/98
   // draw the automap widgets if automap is displayed
   if (automapmode & am_active)
   {
      // map title
      HUlib_drawTextLine(&w_title, false);

      //jff 2/16/98 output new coord display
      // x-coord
      snprintf(hud_coordstrx,32*sizeof(char),"X: %d", (plr->mo->x)>>FRACBITS);
      HUlib_clearTextLine(&w_coordx);
      s = hud_coordstrx;
      while (*s)
         HUlib_addCharToTextLine(&w_coordx, *(s++));
      HUlib_drawTextLine(&w_coordx, false);

      //jff 3/3/98 split coord display into x,y,z lines
      // y-coord
      snprintf(hud_coordstry,32*sizeof(char),"Y: %d", (plr->mo->y)>>FRACBITS);
      HUlib_clearTextLine(&w_coordy);
      s = hud_coordstry;
      while (*s)
         HUlib_addCharToTextLine(&w_coordy, *(s++));
      HUlib_drawTextLine(&w_coordy, false);

      //jff 3/3/98 split coord display into x,y,z lines
      //jff 2/22/98 added z
      // z-coord
      snprintf(hud_coordstrz,32*sizeof(char),"Z: %d", (plr->mo->z)>>FRACBITS);
      HUlib_clearTextLine(&w_coordz);
      s = hud_coordstrz;
      while (*s)
         HUlib_addCharToTextLine(&w_coordz, *(s++));
      HUlib_drawTextLine(&w_coordz, false);
   }

   // draw the weapon/health/ammo/armor/kills/keys displays if optioned
   //jff 2/17/98 allow new hud stuff to be turned off
   // killough 2/21/98: really allow new hud stuff to be turned off COMPLETELY
   if
   (
      hud_active>0 &&                  // hud optioned on
      hud_displayed &&                 // hud on from fullscreen key
      viewheight==SCREENHEIGHT &&      // fullscreen mode is active
      !(automapmode & am_active)       // automap is not active
   )
   {
      doit = !(gametic&1); //jff 3/4/98 speed update up for slow systems
      if (doit)            //jff 8/7/98 update every time, avoid lag in update
      {
         HU_MoveHud();                  // insure HUD display coords are correct

         // do the hud ammo display
         // clear the widgets internal line
         HUlib_clearTextLine(&w_ammo);
         strcpy(hud_ammostr,"AMM ");
         if (weaponinfo[plr->readyweapon].ammo == am_noammo)
         { // special case for weapon with no ammo selected - blank bargraph + N/A
            strcat(hud_ammostr,"\x7f\x7f\x7f\x7f\x7f\x7f\x7f N/A");
            w_ammo.cm = CR_GRAY;
         }
         else
         {
            int ammo = plr->ammo[weaponinfo[plr->readyweapon].ammo];
            int fullammo = plr->maxammo[weaponinfo[plr->readyweapon].ammo];
            int ammopct = (100*ammo)/fullammo;
            int ammobars = ammopct/4;

            // build the numeric amount init string
            snprintf(ammostr,sizeof(ammostr),"%d/%d",ammo,fullammo);
            // build the bargraph string
            // full bargraph chars
            for (i=4;i<4+ammobars/4;)
               hud_ammostr[i++] = 123;
            // plus one last character with 0,1,2,3 bars
            switch(ammobars%4)
            {
            case 0:
               break;
            case 1:
               hud_ammostr[i++] = 126;
               break;
            case 2:
               hud_ammostr[i++] = 125;
               break;
            case 3:
               hud_ammostr[i++] = 124;
               break;
            }
            // pad string with blank bar characters
            while(i<4+7)
               hud_ammostr[i++] = 127;
            hud_ammostr[i] = '\0';
            strcat(hud_ammostr,ammostr);

            // set the display color from the percentage of total ammo held
            if (ammopct<ammo_red)
               w_ammo.cm = CR_RED;
            else if (ammopct<ammo_yellow)
               w_ammo.cm = CR_GOLD;
            else
               w_ammo.cm = CR_GREEN;
         }
         // transfer the init string to the widget
         s = hud_ammostr;
         while (*s)
            HUlib_addCharToTextLine(&w_ammo, *(s++));
      }
      // display the ammo widget every frame
      HUlib_drawTextLine(&w_ammo, false);

      // do the hud health display
      if (doit)
      {
         int health = plr->health;
         int healthbars = health>100? 25 : health/4;

         // clear the widgets internal line
         HUlib_clearTextLine(&w_health);

         // build the numeric amount init string
         snprintf(healthstr,sizeof(healthstr),"%3d",health);
         // build the bargraph string
         // full bargraph chars
         for (i=4;i<4+healthbars/4;)
            hud_healthstr[i++] = 123;
         // plus one last character with 0,1,2,3 bars
         switch(healthbars%4)
         {
         case 0:
            break;
         case 1:
            hud_healthstr[i++] = 126;
            break;
         case 2:
            hud_healthstr[i++] = 125;
            break;
         case 3:
            hud_healthstr[i++] = 124;
            break;
         }
         // pad string with blank bar characters
         while(i<4+7)
            hud_healthstr[i++] = 127;
         hud_healthstr[i] = '\0';
         strcat(hud_healthstr,healthstr);

         // set the display color from the amount of health posessed
         if (health<health_red)
            w_health.cm = CR_RED;
         else if (health<health_yellow)
            w_health.cm = CR_GOLD;
         else if (health<=health_green)
            w_health.cm = CR_GREEN;
         else
            w_health.cm = CR_BLUE;

         // transfer the init string to the widget
         s = hud_healthstr;
         while (*s)
            HUlib_addCharToTextLine(&w_health, *(s++));
      }
      // display the health widget every frame
      HUlib_drawTextLine(&w_health, false);

      // do the hud armor display
      if (doit)
      {
         int armor = plr->armorpoints;
         int armorbars = armor>100? 25 : armor/4;

         // clear the widgets internal line
         HUlib_clearTextLine(&w_armor);
         // build the numeric amount init string
         snprintf(armorstr,sizeof(armorstr),"%3d",armor);
         // build the bargraph string
         // full bargraph chars
         for (i=4;i<4+armorbars/4;)
            hud_armorstr[i++] = 123;
         // plus one last character with 0,1,2,3 bars
         switch(armorbars%4)
         {
         case 0:
            break;
         case 1:
            hud_armorstr[i++] = 126;
            break;
         case 2:
            hud_armorstr[i++] = 125;
            break;
         case 3:
            hud_armorstr[i++] = 124;
            break;
         }
         // pad string with blank bar characters
         while(i<4+7)
            hud_armorstr[i++] = 127;
         hud_armorstr[i] = '\0';
         strcat(hud_armorstr,armorstr);

         // set the display color from the amount of armor posessed
         if (armor<armor_red)
            w_armor.cm = CR_RED;
         else if (armor<armor_yellow)
            w_armor.cm = CR_GOLD;
         else if (armor<=armor_green)
            w_armor.cm = CR_GREEN;
         else
            w_armor.cm = CR_BLUE;

         // transfer the init string to the widget
         s = hud_armorstr;
         while (*s)
            HUlib_addCharToTextLine(&w_armor, *(s++));
      }
      // display the armor widget every frame
      HUlib_drawTextLine(&w_armor, false);

      // do the hud weapon display
      if (doit)
      {
         int w;
         int ammo,fullammo,ammopct;

         // clear the widgets internal line
         HUlib_clearTextLine(&w_weapon);
         i=4; hud_weapstr[i] = '\0';      //jff 3/7/98 make sure ammo goes away

         // do each weapon that exists in current gamemode
         for (w=0;w<=wp_supershotgun;w++) //jff 3/4/98 show fists too, why not?
         {
            int ok=1;
            //jff avoid executing for weapons that do not exist
            switch (gamemode)
            {
            case shareware:
               if (w>=wp_plasma && w!=wp_chainsaw)
                  ok=0;
               break;
            case retail:
            case registered:
               if (w>=wp_supershotgun)
                  ok=0;
               break;
            default:
            case commercial:
               break;
            }
            if (!ok) continue;

            ammo = plr->ammo[weaponinfo[w].ammo];
            fullammo = plr->maxammo[weaponinfo[w].ammo];
            ammopct=0;

            // skip weapons not currently posessed
            if (!plr->weaponowned[w])
               continue;

            ammopct = fullammo? (100*ammo)/fullammo : 100;

            // display each weapon number in a color related to the ammo for it
            hud_weapstr[i++] = '\x1b'; //jff 3/26/98 use ESC not '\' for paths
            if (weaponinfo[w].ammo==am_noammo) //jff 3/14/98 show berserk on HUD
               hud_weapstr[i++] = plr->powers[pw_strength]? '0'+CR_GREEN : '0'+CR_GRAY;
            else if (ammopct<ammo_red)
               hud_weapstr[i++] = '0'+CR_RED;
            else if (ammopct<ammo_yellow)
               hud_weapstr[i++] = '0'+CR_GOLD;
            else
               hud_weapstr[i++] = '0'+CR_GREEN;
            hud_weapstr[i++] = '0'+w+1;
            hud_weapstr[i++] = ' ';
            hud_weapstr[i] = '\0';
         }

         // transfer the init string to the widget
         s = hud_weapstr;
         while (*s)
            HUlib_addCharToTextLine(&w_weapon, *(s++));
      }
      // display the weapon widget every frame
      HUlib_drawTextLine(&w_weapon, false);

      if (doit && hud_active>1)
      {
         int k;

         hud_keysstr[4] = '\0';    //jff 3/7/98 make sure deleted keys go away
         //jff add case for graphic key display
         if (!deathmatch && hud_graph_keys)
         {
            i=0;
            hud_gkeysstr[i] = '\0'; //jff 3/7/98 init graphic keys widget string
            // build text string whose characters call out graphic keys from fontk
            for (k=0;k<6;k++)
            {
               // skip keys not possessed
               if (!plr->cards[k])
                  continue;

               hud_gkeysstr[i++] = '!'+k;   // key number plus '!' is char for key
               hud_gkeysstr[i++] = ' ';     // spacing
               hud_gkeysstr[i++] = ' ';
            }
            hud_gkeysstr[i]='\0';
         }
         else // not possible in current code, unless deathmatching,
         {
            i=4;
            hud_keysstr[i] = '\0';  //jff 3/7/98 make sure deleted keys go away

            // if deathmatch, build string showing top four frag counts
            if (deathmatch) //jff 3/17/98 show frags, not keys, in deathmatch
            {
               int top1=-999,top2=-999,top3=-999,top4=-999;
               int idx1=-1,idx2=-1,idx3=-1,idx4=-1;
               int fragcount,m;
               char numbuf[32];

               // scan thru players
               for (k=0;k<MAXPLAYERS;k++)
               {
                  // skip players not in game
                  if (!playeringame[k])
                     continue;

                  fragcount = 0;
                  // compute number of times they've fragged each player
                  // minus number of times they've been fragged by them
                  for (m=0;m<MAXPLAYERS;m++)
                  {
                     if (!playeringame[m]) continue;
                     fragcount += (m!=k)?  players[k].frags[m] : -players[k].frags[m];
                  }

                  // very primitive sort of frags to find top four
                  if (fragcount>top1)
                  {
                     top4=top3; top3=top2; top2 = top1; top1=fragcount;
                     idx4=idx3; idx3=idx2; idx2 = idx1; idx1=k;
                  }
                  else if (fragcount>top2)
                  {
                     top4=top3; top3=top2; top2=fragcount;
                     idx4=idx3; idx3=idx2; idx2=k;
                  }
                  else if (fragcount>top3)
                  {
                     top4=top3; top3=fragcount;
                     idx4=idx3; idx3=k;
                  }
                  else if (fragcount>top4)
                  {
                     top4=fragcount;
                     idx4=k;
                  }
               }
               // if the biggest number exists, put it in the init string
               if (idx1>-1)
               {
                  snprintf(numbuf,sizeof(numbuf),"%5d",top1);
                  // make frag count in player's color via escape code
                  hud_keysstr[i++] = '\x1b'; //jff 3/26/98 use ESC not '\' for paths
                  hud_keysstr[i++] = '0'+plyrcoltran[idx1&3];
                  s = numbuf;
                  while (*s)
                     hud_keysstr[i++] = *(s++);
               }
               // if the second biggest number exists, put it in the init string
               if (idx2>-1)
               {
                  snprintf(numbuf,sizeof(numbuf),"%5d",top2);
                  // make frag count in player's color via escape code
                  hud_keysstr[i++] = '\x1b'; //jff 3/26/98 use ESC not '\' for paths
                  hud_keysstr[i++] = '0'+plyrcoltran[idx2&3];
                  s = numbuf;
                  while (*s)
                     hud_keysstr[i++] = *(s++);
               }
               // if the third biggest number exists, put it in the init string
               if (idx3>-1)
               {
                  snprintf(numbuf,sizeof(numbuf),"%5d",top3);
                  // make frag count in player's color via escape code
                  hud_keysstr[i++] = '\x1b'; //jff 3/26/98 use ESC not '\' for paths
                  hud_keysstr[i++] = '0'+plyrcoltran[idx3&3];
                  s = numbuf;
                  while (*s)
                     hud_keysstr[i++] = *(s++);
               }
               // if the fourth biggest number exists, put it in the init string
               if (idx4>-1)
               {
                  snprintf(numbuf,sizeof(numbuf),"%5d",top4);
                  // make frag count in player's color via escape code
                  hud_keysstr[i++] = '\x1b'; //jff 3/26/98 use ESC not '\' for paths
                  hud_keysstr[i++] = '0'+plyrcoltran[idx4&3];
                  s = numbuf;
                  while (*s)
                     hud_keysstr[i++] = *(s++);
               }
               hud_keysstr[i] = '\0';
            } //jff 3/17/98 end of deathmatch clause
            else // build alphabetical key display (not used currently)
            {
               // scan the keys
               for (k=0;k<6;k++)
               {
                  // skip any not possessed by the displayed player's stats
                  if (!plr->cards[k])
                     continue;

                  // use color escapes to make text in key's color
                  hud_keysstr[i++] = '\x1b'; //jff 3/26/98 use ESC not '\' for paths
                  switch(k)
                  {
                  case 0:
                     hud_keysstr[i++] = '0'+CR_BLUE;
                     hud_keysstr[i++] = 'B';
                     hud_keysstr[i++] = 'C';
                     hud_keysstr[i++] = ' ';
                     break;
                  case 1:
                     hud_keysstr[i++] = '0'+CR_GOLD;
                     hud_keysstr[i++] = 'Y';
                     hud_keysstr[i++] = 'C';
                     hud_keysstr[i++] = ' ';
                     break;
                  case 2:
                     hud_keysstr[i++] = '0'+CR_RED;
                     hud_keysstr[i++] = 'R';
                     hud_keysstr[i++] = 'C';
                     hud_keysstr[i++] = ' ';
                     break;
                  case 3:
                     hud_keysstr[i++] = '0'+CR_BLUE;
                     hud_keysstr[i++] = 'B';
                     hud_keysstr[i++] = 'S';
                     hud_keysstr[i++] = ' ';
                     break;
                  case 4:
                     hud_keysstr[i++] = '0'+CR_GOLD;
                     hud_keysstr[i++] = 'Y';
                     hud_keysstr[i++] = 'S';
                     hud_keysstr[i++] = ' ';
                     break;
                  case 5:
                     hud_keysstr[i++] = '0'+CR_RED;
                     hud_keysstr[i++] = 'R';
                     hud_keysstr[i++] = 'S';
                     hud_keysstr[i++] = ' ';
                     break;
                  }
                  hud_keysstr[i]='\0';
               }
            }
         }
      }
      // display the keys/frags line each frame
      if (hud_active>1)
      {
         HUlib_clearTextLine(&w_keys);      // clear the widget strings
         HUlib_clearTextLine(&w_gkeys);

         // transfer the built string (frags or key title) to the widget
         s = hud_keysstr; //jff 3/7/98 display key titles/key text or frags
         while (*s)
            HUlib_addCharToTextLine(&w_keys, *(s++));
         HUlib_drawTextLine(&w_keys, false);

         //jff 3/17/98 show graphic keys in non-DM only
         if (!deathmatch) //jff 3/7/98 display graphic keys
         {
            // transfer the graphic key text to the widget
            s = hud_gkeysstr;
            while (*s)
               HUlib_addCharToTextLine(&w_gkeys, *(s++));
            // display the widget
            HUlib_drawTextLine(&w_gkeys, false);
         }
      }

      // display the hud kills/items/secret display if optioned
      if (!hud_nosecrets)
      {
         if (hud_active>1 && doit)
         {
            // clear the internal widget text buffer
            HUlib_clearTextLine(&w_monsec);
            //jff 3/26/98 use ESC not '\' for paths
            // build the init string with fixed colors
            snprintf
            (
               hud_monsecstr,80*sizeof(char),
               "STS \x1b\x36K \x1b\x33%d \x1b\x36M \x1b\x33%d \x1b\x37I \x1b\x33%d/%d \x1b\x35S \x1b\x33%d/%d",
               plr->killcount,totallive,
               plr->itemcount,totalitems,
               plr->secretcount,totalsecret
            );
            // transfer the init string to the widget
            s = hud_monsecstr;
            while (*s)
               HUlib_addCharToTextLine(&w_monsec, *(s++));
         }
         // display the kills/items/secrets each frame, if optioned
         if (hud_active>1)
            HUlib_drawTextLine(&w_monsec, false);
      }
   }

   //jff 3/4/98 display last to give priority
   HU_Erase(); // jff 4/24/98 Erase current lines before drawing current
   // needed when screen not fullsize

   //jff 4/21/98 if setup has disabled message list while active, turn it off
   if (hud_msg_lines<=1)
      message_list = false;

   // if the message review not enabled, show the standard message widget
   if (!message_list)
      HUlib_drawSText(&w_message);

   // if the message review is enabled show the scrolling message review
   if (hud_msg_lines>1 && message_list)
      HUlib_drawMText(&w_rtext);

   // display the interactive buffer for chat entry
   HUlib_drawIText(&w_chat);
}

//
// HU_Erase()
//
// Erase hud display lines that can be trashed by small screen display
//
// Passed nothing, returns nothing
//
void HU_Erase(void)
{
   // erase the message display or the message review display
   if (!message_list)
      HUlib_eraseSText(&w_message);
   else
      HUlib_eraseMText(&w_rtext);

   // erase the interactive text buffer for chat entry
   HUlib_eraseIText(&w_chat);

   // erase the automap title
   HUlib_eraseTextLine(&w_title);
}

//
// HU_Ticker()
//
// Update the hud displays once per frame
//
// Passed nothing, returns nothing
//
static boolean bsdown; // Is backspace down?
static int bscounter;

void HU_Ticker(void)
{
   int i, rc;
   char c;

   // tick down message counter if message is up
   if (message_counter && !--message_counter)
   {
      message_on = false;
      message_nottobefuckedwith = false;
   }
   if (bsdown && bscounter++ > 9) {
      HUlib_keyInIText(&w_chat, (unsigned char)key_backspace);
      bscounter = 8;
   }

   // if messages on, or "Messages Off" is being displayed
   // this allows the notification of turning messages off to be seen
   if (showMessages || message_dontfuckwithme)
   {
      // display message if necessary
      if ((plr->message && !message_nottobefuckedwith)
            || (plr->message && message_dontfuckwithme))
      {
         //post the message to the message widget
         HUlib_addMessageToSText(&w_message, 0, plr->message);
         //jff 2/26/98 add message to refresh text widget too
         HUlib_addMessageToMText(&w_rtext, 0, plr->message);

         // clear the message to avoid posting multiple times
         plr->message = 0;
         // note a message is displayed
         message_on = true;
         // start the message persistence counter
         message_counter = HU_MSGTIMEOUT;
         // transfer "Messages Off" exception to the "being displayed" variable
         message_nottobefuckedwith = message_dontfuckwithme;
         // clear the flag that "Messages Off" is being posted
         message_dontfuckwithme = 0;
      }
   }

   // check for incoming chat characters
   if (netgame)
   {
      for (i=0; i<MAXPLAYERS; i++)
      {
         if (!playeringame[i])
            continue;
         if (i != consoleplayer
               && (c = players[i].cmd.chatchar))
         {
            if (c <= HU_BROADCAST)
               chat_dest[i] = c;
            else
            {
               if (c >= 'a' && c <= 'z')
                  c = (char) shiftxform[(unsigned char) c];
               rc = HUlib_keyInIText(&w_inputbuffer[i], c);
               if (rc && c == KEY_ENTER)
               {
                  if (w_inputbuffer[i].l.len
                        && (chat_dest[i] == consoleplayer+1
                            || chat_dest[i] == HU_BROADCAST))
                  {
                     HUlib_addMessageToSText(&w_message,
                                             player_names[i],
                                             w_inputbuffer[i].l.l);

                     message_nottobefuckedwith = true;
                     message_on = true;
                     message_counter = HU_MSGTIMEOUT;
                     if ( gamemode == commercial )
                        S_StartSound(0, sfx_radio);
                     else
                        S_StartSound(0, sfx_tink);
                  }
                  HUlib_resetIText(&w_inputbuffer[i]);
               }
            }
            players[i].cmd.chatchar = 0;
         }
      }
   }
}

#define QUEUESIZE   128

static char chatchars[QUEUESIZE];
static int  head = 0;
static int  tail = 0;

//
// HU_queueChatChar()
//
// Add an incoming character to the circular chat queue
//
// Passed the character to queue, returns nothing
//
void HU_queueChatChar(char c)
{
   if (((head + 1) & (QUEUESIZE-1)) == tail)
   {
      plr->message = HUSTR_MSGU;
   }
   else
   {
      chatchars[head] = c;
      head = (head + 1) & (QUEUESIZE-1);
   }
}

//
// HU_dequeueChatChar()
//
// Remove the earliest added character from the circular chat queue
//
// Passed nothing, returns the character dequeued
//
char HU_dequeueChatChar(void)
{
   char c;

   if (head != tail)
   {
      c = chatchars[tail];
      tail = (tail + 1) & (QUEUESIZE-1);
   }
   else
   {
      c = 0;
   }
   return c;
}

//
// HU_Responder()
//
// Responds to input events that affect the heads up displays
//
// Passed the event to respond to, returns true if the event was handled
//
boolean HU_Responder(event_t *ev)
{

   static char   lastmessage[HU_MAXLINELENGTH+1];
   const char*   macromessage; // CPhipps - const char*
   boolean   eatkey = false;
   static boolean  shiftdown = false;
   static boolean  altdown = false;
   unsigned char   c;
   int     i;
   int     numplayers;

   static int    num_nobrainers = 0;

   numplayers = 0;
   for (i=0 ; i<MAXPLAYERS ; i++)
      numplayers += playeringame[i];

   if (ev->data1 == key_shift)
   {
      shiftdown = ev->type == ev_keydown;
      return false;
   }
   else if (ev->data1 == key_alt)
   {
      altdown = ev->type == ev_keydown;
      return false;
   }
   else if (ev->data1 == key_backspace)
   {
      bsdown = ev->type == ev_keydown;
      bscounter = 0;
   }

   if (ev->type != ev_keydown)
      return false;

   if (!chat_on)
   {
      if (ev->data1 == key_enter)                                 // phares
      {
#ifndef INSTRUMENTED  // never turn on message review if INSTRUMENTED defined
         if (hud_msg_lines>1)  // it posts multi-line messages that will trash
         {
            if (message_list) HU_Erase(); //jff 4/28/98 erase behind messages
            message_list = !message_list; //jff 2/26/98 toggle list of messages
         }
#endif
         if (!message_list)              // if not message list, refresh message
         {
            message_on = true;
            message_counter = HU_MSGTIMEOUT;
         }
         eatkey = true;
      }//jff 2/26/98 no chat if message review is displayed
      else if (!message_list && netgame && ev->data1 == key_chat)
      {
         eatkey = chat_on = true;
         HUlib_resetIText(&w_chat);
         HU_queueChatChar(HU_BROADCAST);
      }//jff 2/26/98  no chat if message review is displayed
      // killough 10/02/98: no chat if demo playback
      else if (!demoplayback && !message_list && netgame && numplayers > 2)
      {
         for (i=0; i<MAXPLAYERS ; i++)
         {
            if (ev->data1 == destination_keys[i])
            {
               if (playeringame[i] && i!=consoleplayer)
               {
                  eatkey = chat_on = true;
                  HUlib_resetIText(&w_chat);
                  HU_queueChatChar((char)(i+1));
                  break;
               }
               else if (i == consoleplayer)
               {
                  num_nobrainers++;
                  if (num_nobrainers < 3)
                     plr->message = HUSTR_TALKTOSELF1;
                  else if (num_nobrainers < 6)
                     plr->message = HUSTR_TALKTOSELF2;
                  else if (num_nobrainers < 9)
                     plr->message = HUSTR_TALKTOSELF3;
                  else if (num_nobrainers < 32)
                     plr->message = HUSTR_TALKTOSELF4;
                  else
                     plr->message = HUSTR_TALKTOSELF5;
               }
            }
         }
      }
   }//jff 2/26/98 no chat functions if message review is displayed
   else if (!message_list)
   {
      c = ev->data1;
      // send a macro
      if (altdown)
      {
         c = c - '0';
         if (c > 9)
            return false;
         macromessage = chat_macros[c];

         // kill last message with a '\n'
         HU_queueChatChar((char)key_enter); // DEBUG!!!                // phares

         // send the macro message
         while (*macromessage)
            HU_queueChatChar(*macromessage++);
         HU_queueChatChar((char)key_enter);                            // phares

         // leave chat mode and notify that it was sent
         chat_on = false;
         strcpy(lastmessage, chat_macros[c]);
         plr->message = lastmessage;
         eatkey = true;
      }
      else
      {
         if (shiftdown || (c >= 'a' && c <= 'z'))
            c = shiftxform[c];
         eatkey = HUlib_keyInIText(&w_chat, c);
         if (eatkey)
            HU_queueChatChar(c);

         if (c == key_enter)                                     // phares
         {
            chat_on = false;
            if (w_chat.l.len)
            {
               strcpy(lastmessage, w_chat.l.l);
               plr->message = lastmessage;
            }
         }
         else if (c == key_escape)                               // phares
            chat_on = false;
      }
   }
   return eatkey;
}