X-Git-Url: https://git.artsoft.org/?a=blobdiff_plain;f=src%2Fscreens.c;h=2b575392c2ef6d921c50f91393047e282bd63a6d;hb=c87fc56ebe59b3d5158e7e6f01d7aa9bf667737e;hp=49f390f973f643616266c0e1a78cfce6d6a432ce;hpb=00c3d9afa5a7bbf5e53811b49f6dbc0b7cafa5d2;p=rocksndiamonds.git diff --git a/src/screens.c b/src/screens.c index 49f390f9..2b575392 100644 --- a/src/screens.c +++ b/src/screens.c @@ -22,6 +22,7 @@ #include "network.h" #include "init.h" #include "config.h" +#include "api.h" #define DEBUG_JOYSTICKS 0 @@ -130,6 +131,9 @@ #define MENU_CHOOSE_TREE_FONT(x) (FONT_TEXT_1 + (x)) #define MENU_CHOOSE_TREE_COLOR(ti, a) TREE_COLOR(ti, a) +#define TEXT_NEXT_PAGE "Press any key or button for next page" +#define TEXT_INFO_MENU "Press any key or button for info menu" + // for input setup functions #define SETUPINPUT_SCREEN_POS_START 0 #define SETUPINPUT_SCREEN_POS_EMPTY1 3 @@ -184,26 +188,33 @@ // screen gadget identifiers #define SCREEN_CTRL_ID_PREV_LEVEL 0 #define SCREEN_CTRL_ID_NEXT_LEVEL 1 -#define SCREEN_CTRL_ID_FIRST_LEVEL 2 -#define SCREEN_CTRL_ID_LAST_LEVEL 3 -#define SCREEN_CTRL_ID_LEVEL_NUMBER 4 -#define SCREEN_CTRL_ID_PREV_PLAYER 5 -#define SCREEN_CTRL_ID_NEXT_PLAYER 6 -#define SCREEN_CTRL_ID_INSERT_SOLUTION 7 -#define SCREEN_CTRL_ID_PLAY_SOLUTION 8 -#define SCREEN_CTRL_ID_SWITCH_ECS_AGA 9 -#define SCREEN_CTRL_ID_TOUCH_PREV_PAGE 10 -#define SCREEN_CTRL_ID_TOUCH_NEXT_PAGE 11 -#define SCREEN_CTRL_ID_TOUCH_PREV_PAGE2 12 -#define SCREEN_CTRL_ID_TOUCH_NEXT_PAGE2 13 -#define SCREEN_CTRL_ID_SCROLL_UP 14 -#define SCREEN_CTRL_ID_SCROLL_DOWN 15 -#define SCREEN_CTRL_ID_SCROLL_VERTICAL 16 -#define SCREEN_CTRL_ID_NETWORK_SERVER 17 - -#define NUM_SCREEN_GADGETS 18 - -#define NUM_SCREEN_MENUBUTTONS 14 +#define SCREEN_CTRL_ID_PREV_LEVEL2 2 +#define SCREEN_CTRL_ID_NEXT_LEVEL2 3 +#define SCREEN_CTRL_ID_PREV_SCORE 4 +#define SCREEN_CTRL_ID_NEXT_SCORE 5 +#define SCREEN_CTRL_ID_PLAY_TAPE 6 +#define SCREEN_CTRL_ID_FIRST_LEVEL 7 +#define SCREEN_CTRL_ID_LAST_LEVEL 8 +#define SCREEN_CTRL_ID_LEVEL_NUMBER 9 +#define SCREEN_CTRL_ID_PREV_PLAYER 10 +#define SCREEN_CTRL_ID_NEXT_PLAYER 11 +#define SCREEN_CTRL_ID_INSERT_SOLUTION 12 +#define SCREEN_CTRL_ID_PLAY_SOLUTION 13 +#define SCREEN_CTRL_ID_SWITCH_ECS_AGA 14 +#define SCREEN_CTRL_ID_TOUCH_PREV_PAGE 15 +#define SCREEN_CTRL_ID_TOUCH_NEXT_PAGE 16 +#define SCREEN_CTRL_ID_TOUCH_PREV_PAGE2 17 +#define SCREEN_CTRL_ID_TOUCH_NEXT_PAGE2 18 + +#define NUM_SCREEN_MENUBUTTONS 19 + +#define SCREEN_CTRL_ID_SCROLL_UP 19 +#define SCREEN_CTRL_ID_SCROLL_DOWN 20 +#define SCREEN_CTRL_ID_SCROLL_VERTICAL 21 +#define SCREEN_CTRL_ID_NETWORK_SERVER 22 + +#define NUM_SCREEN_GADGETS 23 + #define NUM_SCREEN_SCROLLBUTTONS 2 #define NUM_SCREEN_SCROLLBARS 1 #define NUM_SCREEN_TEXTINPUT 1 @@ -213,6 +224,8 @@ #define SCREEN_MASK_INPUT (1 << 2) #define SCREEN_MASK_TOUCH (1 << 3) #define SCREEN_MASK_TOUCH2 (1 << 4) +#define SCREEN_MASK_SCORES (1 << 5) +#define SCREEN_MASK_SCORES_INFO (1 << 6) // graphic position and size values for buttons and scrollbars #define SC_MENUBUTTON_XSIZE TILEX @@ -259,6 +272,8 @@ static void HandleChooseTree(int, int, int, int, int, TreeInfo **); static void DrawChoosePlayerName(void); static void DrawChooseLevelSet(void); static void DrawChooseLevelNr(void); +static void DrawScoreInfo(int); +static void DrawScoreInfo_Content(int); static void DrawInfoScreen(void); static void DrawSetupScreen(void); static void DrawTypeName(void); @@ -267,26 +282,38 @@ static void DrawInfoScreen_NotAvailable(char *, char *); static void DrawInfoScreen_HelpAnim(int, int, boolean); static void DrawInfoScreen_HelpText(int, int, int, int); static void HandleInfoScreen_Main(int, int, int, int, int); -static void HandleInfoScreen_TitleScreen(int); -static void HandleInfoScreen_Elements(int); -static void HandleInfoScreen_Music(int); -static void HandleInfoScreen_Credits(int); -static void HandleInfoScreen_Program(int); +static void HandleInfoScreen_TitleScreen(int, int, int); +static void HandleInfoScreen_Elements(int, int, int); +static void HandleInfoScreen_Music(int, int, int); +static void HandleInfoScreen_Credits(int, int, int); +static void HandleInfoScreen_Program(int, int, int); static void HandleInfoScreen_Version(int); static void ModifyGameSpeedIfNeeded(void); static void DisableVsyncIfNeeded(void); +static void RedrawScreenMenuGadgets(int); static void MapScreenMenuGadgets(int); static void UnmapScreenMenuGadgets(int); static void MapScreenGadgets(int); +static void UnmapScreenGadgets(void); static void MapScreenTreeGadgets(TreeInfo *); +static void UnmapScreenTreeGadgets(void); static void UpdateScreenMenuGadgets(int, boolean); +static void AdjustScoreInfoButtons_SelectScore(int, int, int); +static void AdjustScoreInfoButtons_PlayTape(int, int, boolean); static boolean OfferUploadTapes(void); static void execOfferUploadTapes(void); +static void DrawHallOfFame_setScoreEntries(void); +static void HandleHallOfFame_SelectLevel(int, int); +static char *getHallOfFameRankText(int, int); +static char *getHallOfFameScoreText(int, int); + +static struct TokenInfo *getSetupInfoFinal(struct TokenInfo *); + static struct GadgetInfo *screen_gadget[NUM_SCREEN_GADGETS]; static int info_mode = INFO_MODE_MAIN; @@ -348,6 +375,9 @@ static TreeInfo *player_name_current = NULL; static TreeInfo *level_number = NULL; static TreeInfo *level_number_current = NULL; +static TreeInfo *score_entries = NULL; +static TreeInfo *score_entry_current = NULL; + static struct ValueTextInfo window_sizes_list[] = { { 50, "50 %" }, @@ -604,6 +634,10 @@ static int align_yoffset = 0; menu.extra_spacing[GAME_MODE_SETUP] : \ menu.extra_spacing_setup[DRAW_MODE_SETUP(i)]) +#define EXTRA_SPACING_SCORES(i) (EXTRA_SPACING_INFO(i)) + +#define EXTRA_SPACING_SCOREINFO(i) (menu.extra_spacing[GAME_MODE_SCOREINFO]) + #define DRAW_XOFFSET(s) ((s) == GAME_MODE_INFO ? \ DRAW_XOFFSET_INFO(info_mode) : \ (s) == GAME_MODE_SETUP ? \ @@ -618,6 +652,8 @@ static int align_yoffset = 0; EXTRA_SPACING_INFO(info_mode) : \ (s) == GAME_MODE_SETUP ? \ EXTRA_SPACING_SETUP(setup_mode) : \ + (s) == GAME_MODE_SCORES ? \ + EXTRA_SPACING_SCORES(info_mode) : \ menu.extra_spacing[DRAW_MODE(s)]) #define mSX (SX + DRAW_XOFFSET(game_status)) @@ -655,6 +691,18 @@ struct TitleControlInfo struct TitleControlInfo title_controls[MAX_NUM_TITLE_SCREENS]; + +// credits screens definitions + +static int num_credits_screens = 0; +static boolean use_global_credits_screens = FALSE; + + +// program info screens definitions + +static int num_program_info_screens = 0; + + // main menu display and control definitions #define MAIN_CONTROL_NAME 0 @@ -1393,10 +1441,10 @@ static void AdjustScrollbar(int id, int items_max, int items_visible, GDI_SCROLLBAR_ITEM_POSITION, item_position, GDI_END); } -static void AdjustChooseTreeScrollbar(int id, int first_entry, TreeInfo *ti) +static void AdjustChooseTreeScrollbar(TreeInfo *ti, int id) { AdjustScrollbar(id, numTreeInfoInGroup(ti), NUM_MENU_ENTRIES_ON_SCREEN, - first_entry); + ti->cl_first); } static void clearMenuListArea(void) @@ -1410,6 +1458,11 @@ static void clearMenuListArea(void) // clear menu list area, but not title or scrollbar DrawBackground(mSX, mSY + MENU_SCREEN_START_YPOS * 32, scrollbar_xpos - mSX, NUM_MENU_ENTRIES_ON_SCREEN * 32); + + // special compatibility handling for "Snake Bite" graphics set + if (strPrefix(leveldir_current->identifier, "snake_bite")) + ClearRectangle(drawto, mSX, mSY + MENU_SCREEN_START_YPOS * 32, + scrollbar_xpos - mSX, NUM_MENU_ENTRIES_ON_SCREEN * 32); } static void drawCursorExt(int xpos, int ypos, boolean active, int graphic) @@ -1478,6 +1531,15 @@ static int getChooseTreeEditYPos(int ypos_raw) return sy; } +static int getChooseTreeEditXPosReal(int pos) +{ + int xpos = getChooseTreeEditXPos(pos); + int font_nr = getChooseTreeEditFont(FALSE); + int font_xoffset = getFontDrawOffsetX(font_nr); + + return xpos + font_xoffset; +} + static void drawChooseTreeEdit(int ypos_raw, boolean active) { int sx = getChooseTreeEditXPos(POS_LEFT); @@ -1647,6 +1709,21 @@ void DrawMainMenu(void) return; } + // needed if last screen was the playing screen, invoked from hall of fame + if (score_info_tape_play) + { + CloseDoor(DOOR_CLOSE_ALL); + + SetGameStatus(GAME_MODE_SCOREINFO); + + DrawScoreInfo(scores.last_entry_nr); + + return; + } + + // reset flag to continue playing next level from hall of fame + scores.continue_playing = FALSE; + // leveldir_current may be invalid (level group, parent link, node copy) leveldir_current = getValidLevelSeries(leveldir_current, leveldir_last_valid); @@ -1660,8 +1737,6 @@ void DrawMainMenu(void) // store valid level series information leveldir_last_valid = leveldir_current; - init_last = init; // switch to new busy animation - // needed if last screen (level choice) changed graphics, sounds or music ReloadCustomArtwork(0); @@ -1892,7 +1967,7 @@ void HandleTitleScreen(int mx, int my, int dx, int dy, int button) { return_to_main_menu = TRUE; } - else if (button == MB_MENU_CHOICE) + else if (button == MB_MENU_CHOICE || dx) { if (game_status_last_screen == GAME_MODE_INFO && num_title_screens == 0) { @@ -1905,9 +1980,10 @@ void HandleTitleScreen(int mx, int my, int dx, int dy, int button) return; } - title_screen_nr++; + title_screen_nr += + (game_status_last_screen == GAME_MODE_INFO && dx < 0 ? -1 : +1); - if (title_screen_nr < num_title_screens) + if (title_screen_nr >= 0 && title_screen_nr < num_title_screens) { tci = &title_controls[title_screen_nr]; @@ -2007,7 +2083,7 @@ static void HandleMainMenu_SelectLevel(int step, int direction, { // skipping levels is only allowed when trying to skip single level if (setup.skip_levels && new_level_nr == old_level_nr + 1 && - Request("Level still unsolved! Skip despite handicap?", REQ_ASK)) + Request("Level still unsolved! Skip it anyway?", REQ_ASK)) { leveldir_current->handicap_level++; SaveLevelSetup_SeriesInfo(); @@ -2500,6 +2576,9 @@ static void DrawInfoScreen_Main(void) info_info = info_info_main; + // use modified info screen info without info screen entries marked as hidden + info_info = getSetupInfoFinal(info_info); + // determine maximal number of info entries that can be displayed on screen num_info_info = 0; for (i = 0; info_info[i].type != 0 && i < NUM_MENU_ENTRIES_ON_SCREEN; i++) @@ -2883,8 +2962,7 @@ void DrawInfoScreen_NotAvailable(char *text_title, char *text_error) DrawTextSCentered(ystart1, font_title, text_title); DrawTextSCentered(ystart2, font_error, text_error); - DrawTextSCentered(ybottom, font_foot, - "Press any key or button for info menu"); + DrawTextSCentered(ybottom, font_foot, TEXT_INFO_MENU); FadeIn(REDRAW_FIELD); } @@ -2915,9 +2993,7 @@ void DrawInfoScreen_HelpAnim(int start, int max_anims, boolean init) DrawHeadline(); DrawTextSCentered(ystart1, font_title, "The Game Elements:"); - - DrawTextSCentered(ybottom, font_foot, - "Press any key or button for next page"); + DrawTextSCentered(ybottom, font_foot, TEXT_NEXT_PAGE); FrameCounter = 0; } @@ -3064,9 +3140,9 @@ static void DrawInfoScreen_TitleScreen(void) DrawTitleScreen(); } -void HandleInfoScreen_TitleScreen(int button) +void HandleInfoScreen_TitleScreen(int dx, int dy, int button) { - HandleTitleScreen(0, 0, 0, 0, button); + HandleTitleScreen(0, 0, dx, dy, button); } static void DrawInfoScreen_Elements(void) @@ -3078,12 +3154,12 @@ static void DrawInfoScreen_Elements(void) LoadHelpAnimInfo(); LoadHelpTextInfo(); - HandleInfoScreen_Elements(MB_MENU_INITIALIZE); + HandleInfoScreen_Elements(0, 0, MB_MENU_INITIALIZE); FadeIn(REDRAW_FIELD); } -void HandleInfoScreen_Elements(int button) +void HandleInfoScreen_Elements(int dx, int dy, int button) { static unsigned int info_delay = 0; static int num_anims; @@ -3122,16 +3198,16 @@ void HandleInfoScreen_Elements(int button) return; } - else if (button == MB_MENU_CHOICE || button == MB_MENU_INITIALIZE) + else if (button == MB_MENU_CHOICE || button == MB_MENU_INITIALIZE || dx) { if (button != MB_MENU_INITIALIZE) { PlaySound(SND_MENU_ITEM_SELECTING); - page++; + page += (dx < 0 ? -1 : +1); } - if (page >= num_pages) + if (page < 0 || page >= num_pages) { FadeMenuSoundsAndMusic(); @@ -3141,7 +3217,7 @@ void HandleInfoScreen_Elements(int button) return; } - if (page > 0) + if (button != MB_MENU_INITIALIZE) FadeSetNextScreen(); if (button != MB_MENU_INITIALIZE) @@ -3173,12 +3249,12 @@ static void DrawInfoScreen_Music(void) LoadMusicInfo(); - HandleInfoScreen_Music(MB_MENU_INITIALIZE); + HandleInfoScreen_Music(0, 0, MB_MENU_INITIALIZE); FadeIn(REDRAW_FIELD); } -void HandleInfoScreen_Music(int button) +void HandleInfoScreen_Music(int dx, int dy, int button) { static struct MusicFileInfo *list = NULL; int font_title = MENU_INFO_FONT_TITLE; @@ -3203,11 +3279,8 @@ void HandleInfoScreen_Music(int button) ClearField(); DrawHeadline(); - DrawTextSCentered(ystart, font_title, - "No music info for this level set."); - - DrawTextSCentered(ybottom, font_foot, - "Press any key or button for info menu"); + DrawTextSCentered(ystart, font_title, "No music info for this level set."); + DrawTextSCentered(ybottom, font_foot, TEXT_INFO_MENU); return; } @@ -3224,14 +3297,14 @@ void HandleInfoScreen_Music(int button) return; } - else if (button == MB_MENU_CHOICE || button == MB_MENU_INITIALIZE) + else if (button == MB_MENU_CHOICE || button == MB_MENU_INITIALIZE || dx) { if (button != MB_MENU_INITIALIZE) { PlaySound(SND_MENU_ITEM_SELECTING); if (list != NULL) - list = list->next; + list = (dx < 0 ? list->prev : list->next); } if (list == NULL) @@ -3331,8 +3404,7 @@ void HandleInfoScreen_Music(int button) ystart += ystep_head; } - DrawTextSCentered(ybottom, FONT_TEXT_4, - "Press any key or button for next page"); + DrawTextSCentered(ybottom, font_foot, TEXT_NEXT_PAGE); if (button != MB_MENU_INITIALIZE) FadeIn(REDRAW_FIELD); @@ -3345,17 +3417,11 @@ void HandleInfoScreen_Music(int button) static void DrawInfoScreen_CreditsScreen(int screen_nr) { int font_title = MENU_INFO_FONT_TITLE; - int font_head = MENU_INFO_FONT_HEAD; int font_text = MENU_INFO_FONT_TEXT; int font_foot = MENU_INFO_FONT_FOOT; int spacing_title = menu.headline1_spacing_info[info_mode]; - int spacing_head = menu.headline2_spacing_info[info_mode]; - int spacing_para = menu.paragraph_spacing_info[info_mode]; int spacing_line = menu.line_spacing_info[info_mode]; int ystep_title = getMenuTextStep(spacing_title, font_title); - int ystep_head = getMenuTextStep(spacing_head, font_head); - int ystep_para = getMenuTextStep(spacing_para, font_text); - int ystep_line = getMenuTextStep(spacing_line, font_text); int ystart = mSY - SY + MENU_SCREEN_INFO_YSTART1; int ybottom = mSY - SY + MENU_SCREEN_INFO_YBOTTOM; @@ -3363,214 +3429,26 @@ static void DrawInfoScreen_CreditsScreen(int screen_nr) DrawHeadline(); DrawTextSCentered(ystart, font_title, "Credits:"); - ystart += ystep_title; - if (screen_nr == 0) - { - DrawTextSCentered(ystart, font_head, - "Special thanks to"); - ystart += ystep_head; - DrawTextSCentered(ystart, font_text, - "Peter Liepa"); - ystart += ystep_head; - DrawTextSCentered(ystart, font_head, - "for creating"); - ystart += ystep_head; - DrawTextSCentered(ystart, font_text, - "\"Boulder Dash\""); - ystart += ystep_head; - DrawTextSCentered(ystart, font_head, - "in the year"); - ystart += ystep_head; - DrawTextSCentered(ystart, font_text, - "1984"); - ystart += ystep_head; - DrawTextSCentered(ystart, font_head, - "published by"); - ystart += ystep_head; - DrawTextSCentered(ystart, font_text, - "First Star Software"); - } - else if (screen_nr == 1) - { - DrawTextSCentered(ystart, font_head, - "Special thanks to"); - ystart += ystep_head; - DrawTextSCentered(ystart, font_text, - "Klaus Heinz & Volker Wertich"); - ystart += ystep_head; - DrawTextSCentered(ystart, font_head, - "for creating"); - ystart += ystep_head; - DrawTextSCentered(ystart, font_text, - "\"Emerald Mine\""); - ystart += ystep_head; - DrawTextSCentered(ystart, font_head, - "in the year"); - ystart += ystep_head; - DrawTextSCentered(ystart, font_text, - "1987"); - ystart += ystep_head; - DrawTextSCentered(ystart, font_head, - "published by"); - ystart += ystep_head; - DrawTextSCentered(ystart, font_text, - "Kingsoft"); - } - else if (screen_nr == 2) - { - DrawTextSCentered(ystart, font_head, - "Special thanks to"); - ystart += ystep_head; - DrawTextSCentered(ystart, font_text, - "Michael Stopp & Philip Jespersen"); - ystart += ystep_head; - DrawTextSCentered(ystart, font_head, - "for creating"); - ystart += ystep_head; - DrawTextSCentered(ystart, font_text, - "\"Supaplex\""); - ystart += ystep_head; - DrawTextSCentered(ystart, font_head, - "in the year"); - ystart += ystep_head; - DrawTextSCentered(ystart, font_text, - "1991"); - ystart += ystep_head; - DrawTextSCentered(ystart, font_head, - "published by"); - ystart += ystep_head; - DrawTextSCentered(ystart, font_text, - "Digital Integration"); - } - else if (screen_nr == 3) - { - DrawTextSCentered(ystart, font_head, - "Special thanks to"); - ystart += ystep_head; - DrawTextSCentered(ystart, font_text, - "Hiroyuki Imabayashi"); - ystart += ystep_head; - DrawTextSCentered(ystart, font_head, - "for creating"); - ystart += ystep_head; - DrawTextSCentered(ystart, font_text, - "\"Sokoban\""); - ystart += ystep_head; - DrawTextSCentered(ystart, font_head, - "in the year"); - ystart += ystep_head; - DrawTextSCentered(ystart, font_text, - "1982"); - ystart += ystep_head; - DrawTextSCentered(ystart, font_head, - "published by"); - ystart += ystep_head; - DrawTextSCentered(ystart, font_text, - "Thinking Rabbit"); - } - else if (screen_nr == 4) - { - DrawTextSCentered(ystart, font_head, - "Special thanks to"); - ystart += ystep_head; - DrawTextSCentered(ystart, font_text, - "Alan Bond"); - ystart += ystep_head; - DrawTextSCentered(ystart, font_head, - "and"); - ystart += ystep_head; - DrawTextSCentered(ystart, font_text, - "J\xfcrgen Bonhagen"); - ystart += ystep_head; - DrawTextSCentered(ystart, font_head, - "for the continuous creation"); - ystart += ystep_line; - DrawTextSCentered(ystart, font_head, - "of outstanding level sets"); - } - else if (screen_nr == 5) - { - DrawTextSCentered(ystart, font_head, - "Thanks to"); - ystart += ystep_head; - DrawTextSCentered(ystart, font_text, - "Peter Elzner"); - ystart += ystep_head; - DrawTextSCentered(ystart, font_head, - "for ideas and inspiration by"); - ystart += ystep_head; - DrawTextSCentered(ystart, font_text, - "Diamond Caves"); - ystart += ystep_para; - - DrawTextSCentered(ystart, font_head, - "Thanks to"); - ystart += ystep_head; - DrawTextSCentered(ystart, font_text, - "Steffest"); - ystart += ystep_head; - DrawTextSCentered(ystart, font_head, - "for ideas and inspiration by"); - ystart += ystep_head; - DrawTextSCentered(ystart, font_text, - "DX-Boulderdash"); - } - else if (screen_nr == 6) - { - DrawTextSCentered(ystart, font_head, - "Thanks to"); - ystart += ystep_head; - DrawTextSCentered(ystart, font_text, - "David Tritscher"); - ystart += ystep_head; - DrawTextSCentered(ystart, font_head, - "for the code base used for the"); - ystart += ystep_line; - DrawTextSCentered(ystart, font_head, - "native Emerald Mine engine"); - } - else if (screen_nr == 7) - { - DrawTextSCentered(ystart, font_head, - "Thanks to"); - ystart += ystep_head; - DrawTextSCentered(ystart, font_text, - "Guido Schulz"); - ystart += ystep_head; - DrawTextSCentered(ystart, font_head, - "for the initial DOS port"); - ystart += ystep_para; - - DrawTextSCentered(ystart, font_head, - "Thanks to"); - ystart += ystep_head; - DrawTextSCentered(ystart, font_text, - "Karl H\xf6rnell"); - ystart += ystep_head; - DrawTextSCentered(ystart, font_head, - "for some additional toons"); - } - else if (screen_nr == 8) - { - DrawTextSCentered(ystart, font_head, - "And not to forget:"); - ystart += ystep_head; - DrawTextSCentered(ystart, font_head, - "Many thanks to"); - ystart += ystep_head; - DrawTextSCentered(ystart, font_text, - "All those who contributed"); - ystart += ystep_line; - DrawTextSCentered(ystart, font_text, - "levels to this game"); - ystart += ystep_line; - DrawTextSCentered(ystart, font_text, - "since 1995"); - } + char *filename = getCreditsFilename(screen_nr, use_global_credits_screens); + int width = SXSIZE; + int height = MENU_SCREEN_INFO_YBOTTOM - MENU_SCREEN_INFO_YSTART1; + int chars = width / getFontWidth(font_text); + int lines = height / getFontHeight(font_text); + int padx = (width - chars * getFontWidth(font_text)) / 2; + int line_spacing = getMenuTextSpacing(spacing_line, font_text); + boolean autowrap = FALSE; + boolean centered = TRUE; + boolean parse_comments = TRUE; - DrawTextSCentered(ybottom, font_foot, - "Press any key or button for next page"); + DrawTextFile(mSX + padx, mSY + MENU_SCREEN_INFO_YSTART1 + ystep_title, + filename, font_text, chars, -1, lines, line_spacing, -1, + autowrap, centered, parse_comments); + + boolean last_screen = (screen_nr == num_credits_screens - 1); + char *text_foot = (last_screen ? TEXT_INFO_MENU : TEXT_NEXT_PAGE); + + DrawTextSCentered(ybottom, font_foot, text_foot); } static void DrawInfoScreen_Credits(void) @@ -3581,24 +3459,54 @@ static void DrawInfoScreen_Credits(void) FadeOut(REDRAW_FIELD); - HandleInfoScreen_Credits(MB_MENU_INITIALIZE); + HandleInfoScreen_Credits(0, 0, MB_MENU_INITIALIZE); FadeIn(REDRAW_FIELD); } -void HandleInfoScreen_Credits(int button) +void HandleInfoScreen_Credits(int dx, int dy, int button) { static int screen_nr = 0; - int num_screens = 9; if (button == MB_MENU_INITIALIZE) { + int i; + + // determine number of (global or level set specific) credits screens + for (i = 0; i < 2; i++) + { + num_credits_screens = 0; + use_global_credits_screens = i; + + while (getCreditsFilename(num_credits_screens, + use_global_credits_screens) != NULL) + num_credits_screens++; + + if (num_credits_screens > 0) + break; + } + + if (num_credits_screens == 0) + { + int font_title = MENU_INFO_FONT_TITLE; + int font_foot = MENU_INFO_FONT_FOOT; + int ystart = mSY - SY + MENU_SCREEN_INFO_YSTART1; + int ybottom = mSY - SY + MENU_SCREEN_INFO_YBOTTOM; + + ClearField(); + DrawHeadline(); + + DrawTextSCentered(ystart, font_title, "No credits for this level set."); + DrawTextSCentered(ybottom, font_foot, TEXT_INFO_MENU); + + return; + } + screen_nr = 0; - // DrawInfoScreen_CreditsScreen(screen_nr); + DrawInfoScreen_CreditsScreen(screen_nr); } - - if (button == MB_MENU_LEAVE) + else if (button == MB_MENU_LEAVE) { PlaySound(SND_MENU_ITEM_SELECTING); @@ -3607,16 +3515,13 @@ void HandleInfoScreen_Credits(int button) return; } - else if (button == MB_MENU_CHOICE || button == MB_MENU_INITIALIZE) + else if (button == MB_MENU_CHOICE || dx) { - if (button != MB_MENU_INITIALIZE) - { - PlaySound(SND_MENU_ITEM_SELECTING); + PlaySound(SND_MENU_ITEM_SELECTING); - screen_nr++; - } + screen_nr += (dx < 0 ? -1 : +1); - if (screen_nr >= num_screens) + if (screen_nr < 0 || screen_nr >= num_credits_screens) { FadeMenuSoundsAndMusic(); @@ -3626,16 +3531,13 @@ void HandleInfoScreen_Credits(int button) return; } - if (screen_nr > 0) - FadeSetNextScreen(); + FadeSetNextScreen(); - if (button != MB_MENU_INITIALIZE) - FadeOut(REDRAW_FIELD); + FadeOut(REDRAW_FIELD); DrawInfoScreen_CreditsScreen(screen_nr); - if (button != MB_MENU_INITIALIZE) - FadeIn(REDRAW_FIELD); + FadeIn(REDRAW_FIELD); } else { @@ -3643,68 +3545,89 @@ void HandleInfoScreen_Credits(int button) } } -static void DrawInfoScreen_Program(void) +static void DrawInfoScreen_ProgramScreen(int screen_nr) { int font_title = MENU_INFO_FONT_TITLE; - int font_head = MENU_INFO_FONT_HEAD; int font_text = MENU_INFO_FONT_TEXT; int font_foot = MENU_INFO_FONT_FOOT; int spacing_title = menu.headline1_spacing_info[info_mode]; - int spacing_head = menu.headline2_spacing_info[info_mode]; - int spacing_para = menu.paragraph_spacing_info[info_mode]; int spacing_line = menu.line_spacing_info[info_mode]; int ystep_title = getMenuTextStep(spacing_title, font_title); - int ystep_head = getMenuTextStep(spacing_head, font_head); - int ystep_para = getMenuTextStep(spacing_para, font_text); - int ystep_line = getMenuTextStep(spacing_line, font_text); int ystart = mSY - SY + MENU_SCREEN_INFO_YSTART1; int ybottom = mSY - SY + MENU_SCREEN_INFO_YBOTTOM; - SetMainBackgroundImageIfDefined(IMG_BACKGROUND_INFO_PROGRAM); - - FadeOut(REDRAW_FIELD); - ClearField(); DrawHeadline(); DrawTextSCentered(ystart, font_title, "Program Information:"); - ystart += ystep_title; - DrawTextSCentered(ystart, font_head, - "This game is Freeware!"); - ystart += ystep_head; - DrawTextSCentered(ystart, font_head, - "If you like it, send e-mail to:"); - ystart += ystep_head; - DrawTextSCentered(ystart, font_text, - setup.internal.program_email); - ystart += ystep_para; + char *filename = getProgramInfoFilename(screen_nr); + int width = SXSIZE; + int height = MENU_SCREEN_INFO_YBOTTOM - MENU_SCREEN_INFO_YSTART1; + int chars = width / getFontWidth(font_text); + int lines = height / getFontHeight(font_text); + int padx = (width - chars * getFontWidth(font_text)) / 2; + int line_spacing = getMenuTextSpacing(spacing_line, font_text); + boolean autowrap = FALSE; + boolean centered = TRUE; + boolean parse_comments = TRUE; - DrawTextSCentered(ystart, font_head, - "More information and levels:"); - ystart += ystep_head; - DrawTextSCentered(ystart, font_text, - setup.internal.program_website); - ystart += ystep_para; + DrawTextFile(mSX + padx, mSY + MENU_SCREEN_INFO_YSTART1 + ystep_title, + filename, font_text, chars, -1, lines, line_spacing, -1, + autowrap, centered, parse_comments); - DrawTextSCentered(ystart, font_head, - "If you have created new levels,"); - ystart += ystep_line; - DrawTextSCentered(ystart, font_head, - "send them to me to include them!"); - ystart += ystep_head; - DrawTextSCentered(ystart, font_head, - ":-)"); + boolean last_screen = (screen_nr == num_program_info_screens - 1); + char *text_foot = (last_screen ? TEXT_INFO_MENU : TEXT_NEXT_PAGE); - DrawTextSCentered(ybottom, font_foot, - "Press any key or button for info menu"); + DrawTextSCentered(ybottom, font_foot, text_foot); +} + +static void DrawInfoScreen_Program(void) +{ + SetMainBackgroundImageIfDefined(IMG_BACKGROUND_INFO_PROGRAM); + + FadeMenuSoundsAndMusic(); + + FadeOut(REDRAW_FIELD); + + HandleInfoScreen_Program(0, 0, MB_MENU_INITIALIZE); FadeIn(REDRAW_FIELD); } -void HandleInfoScreen_Program(int button) +void HandleInfoScreen_Program(int dx, int dy, int button) { - if (button == MB_MENU_LEAVE) + static int screen_nr = 0; + + if (button == MB_MENU_INITIALIZE) + { + // determine number of program info screens + num_program_info_screens = 0; + + while (getProgramInfoFilename(num_program_info_screens) != NULL) + num_program_info_screens++; + + if (num_program_info_screens == 0) + { + int font_title = MENU_INFO_FONT_TITLE; + int font_foot = MENU_INFO_FONT_FOOT; + int ystart = mSY - SY + MENU_SCREEN_INFO_YSTART1; + int ybottom = mSY - SY + MENU_SCREEN_INFO_YBOTTOM; + + ClearField(); + DrawHeadline(); + + DrawTextSCentered(ystart, font_title, "No program info available."); + DrawTextSCentered(ybottom, font_foot, TEXT_INFO_MENU); + + return; + } + + screen_nr = 0; + + DrawInfoScreen_ProgramScreen(screen_nr); + } + else if (button == MB_MENU_LEAVE) { PlaySound(SND_MENU_ITEM_SELECTING); @@ -3713,14 +3636,29 @@ void HandleInfoScreen_Program(int button) return; } - else if (button == MB_MENU_CHOICE) + else if (button == MB_MENU_CHOICE || dx) { PlaySound(SND_MENU_ITEM_SELECTING); - FadeMenuSoundsAndMusic(); + screen_nr += (dx < 0 ? -1 : +1); - info_mode = INFO_MODE_MAIN; - DrawInfoScreen(); + if (screen_nr < 0 || screen_nr >= num_program_info_screens) + { + FadeMenuSoundsAndMusic(); + + info_mode = INFO_MODE_MAIN; + DrawInfoScreen(); + + return; + } + + FadeSetNextScreen(); + + FadeOut(REDRAW_FIELD); + + DrawInfoScreen_ProgramScreen(screen_nr); + + FadeIn(REDRAW_FIELD); } else { @@ -3893,8 +3831,7 @@ static void DrawInfoScreen_Version(void) DrawTextF(xstart2, ystart, font_text, "%s", setup.system.sdl_audiodriver); DrawTextF(xstart3, ystart, font_text, "%s", driver_name); - DrawTextSCentered(ybottom, font_foot, - "Press any key or button for info menu"); + DrawTextSCentered(ybottom, font_foot, TEXT_INFO_MENU); FadeIn(REDRAW_FIELD); } @@ -3930,6 +3867,7 @@ static void DrawInfoScreen_LevelSet(void) struct TitleMessageInfo *tmi = &readme; char *filename = getLevelSetInfoFilename(); char *title = "Level Set Information:"; + int font_foot = MENU_INFO_FONT_FOOT; int ystart = mSY - SY + MENU_SCREEN_INFO_YSTART1; int ybottom = mSY - SY + MENU_SCREEN_INFO_YBOTTOM; @@ -3981,8 +3919,7 @@ static void DrawInfoScreen_LevelSet(void) filename, tmi->font, tmi->chars, -1, tmi->lines, 0, -1, tmi->autowrap, tmi->centered, tmi->parse_comments); - DrawTextSCentered(ybottom, FONT_TEXT_4, - "Press any key or button for info menu"); + DrawTextSCentered(ybottom, font_foot, TEXT_INFO_MENU); FadeIn(REDRAW_FIELD); } @@ -4041,15 +3978,15 @@ static void DrawInfoScreen(void) void HandleInfoScreen(int mx, int my, int dx, int dy, int button) { if (info_mode == INFO_MODE_TITLE) - HandleInfoScreen_TitleScreen(button); + HandleInfoScreen_TitleScreen(dx, dy, button); else if (info_mode == INFO_MODE_ELEMENTS) - HandleInfoScreen_Elements(button); + HandleInfoScreen_Elements(dx, dy, button); else if (info_mode == INFO_MODE_MUSIC) - HandleInfoScreen_Music(button); + HandleInfoScreen_Music(dx, dy, button); else if (info_mode == INFO_MODE_CREDITS) - HandleInfoScreen_Credits(button); + HandleInfoScreen_Credits(dx, dy, button); else if (info_mode == INFO_MODE_PROGRAM) - HandleInfoScreen_Program(button); + HandleInfoScreen_Program(dx, dy, button); else if (info_mode == INFO_MODE_VERSION) HandleInfoScreen_Version(button); else if (info_mode == INFO_MODE_LEVELSET) @@ -4060,532 +3997,130 @@ void HandleInfoScreen(int mx, int my, int dx, int dy, int button) // ============================================================================ -// rename player API functions +// type name functions // ============================================================================ -struct ApiRenamePlayerThreadData -{ - char *player_name; - char *player_uuid; -}; - -static void *CreateThreadData_ApiRenamePlayer(void) -{ - struct ApiRenamePlayerThreadData *data = - checked_malloc(sizeof(struct ApiRenamePlayerThreadData)); - - data->player_name = getStringCopy(setup.player_name); - data->player_uuid = getStringCopy(setup.player_uuid); - - return data; -} +static TreeInfo *type_name_node = NULL; +static char type_name_last[MAX_PLAYER_NAME_LEN + 1] = { 0 }; +static int type_name_nr = 0; -static void FreeThreadData_ApiRenamePlayer(void *data_raw) +static int getPlayerNameColor(char *name) { - struct ApiRenamePlayerThreadData *data = data_raw; - - checked_free(data->player_name); - checked_free(data->player_uuid); - checked_free(data); + return (strEqual(name, EMPTY_PLAYER_NAME) ? FC_BLUE : FC_RED); } -static boolean SetRequest_ApiRenamePlayer(struct HttpRequest *request, - void *data_raw) +static void drawTypeNameText(char *name, struct TextPosInfo *pos, + boolean active) { - struct ApiRenamePlayerThreadData *data = data_raw; - char *player_name_raw = data->player_name; - char *player_uuid_raw = data->player_uuid; - - request->hostname = setup.api_server_hostname; - request->port = API_SERVER_PORT; - request->method = API_SERVER_METHOD; - request->uri = API_SERVER_URI_RENAME; - - char *player_name = getEscapedJSON(player_name_raw); - char *player_uuid = getEscapedJSON(player_uuid_raw); + char text[MAX_PLAYER_NAME_LEN + 2] = { 0 }; + boolean multiple_users = (game_status == GAME_MODE_PSEUDO_TYPENAMES); + int sx = (multiple_users ? amSX + pos->x : mSX + ALIGNED_TEXT_XPOS(pos)); + int sy = (multiple_users ? amSY + pos->y : mSY + ALIGNED_TEXT_YPOS(pos)); + int font_nr = (active ? FONT_ACTIVE(pos->font) : pos->font); + int font_width = getFontWidth(font_nr); + int font_xoffset = getFontDrawOffsetX(font_nr); + int font_yoffset = getFontDrawOffsetY(font_nr); + int font_sx = sx + font_xoffset; + int font_sy = sy + font_yoffset; - snprintf(request->body, MAX_HTTP_BODY_SIZE, - "{\n" - "%s" - " \"game_version\": \"%s\",\n" - " \"game_platform\": \"%s\",\n" - " \"name\": \"%s\",\n" - " \"uuid\": \"%s\"\n" - "}\n", - getPasswordJSON(setup.api_server_password), - getProgramRealVersionString(), - getProgramPlatformString(), - player_name, - player_uuid); + DrawBackgroundForFont(font_sx, font_sy, pos->width, pos->height, font_nr); - checked_free(player_name); - checked_free(player_uuid); + sprintf(text, "%s%c", name, (active ? '_' : '\0')); - ConvertHttpRequestBodyToServerEncoding(request); + pos->width = strlen(text) * font_width; + sx = (multiple_users ? amSX + pos->x : mSX + ALIGNED_TEXT_XPOS(pos)); - return TRUE; + DrawText(sx, sy, text, font_nr); } -static void HandleResponse_ApiRenamePlayer(struct HttpResponse *response, - void *data_raw) +static void getTypeNameValues(char *name, struct TextPosInfo *pos, int *xpos) { - // nothing to do here -} + struct MainControlInfo *mci = getMainControlInfo(MAIN_CONTROL_NAME); -#if defined(PLATFORM_EMSCRIPTEN) -static void Emscripten_ApiRenamePlayer_Loaded(unsigned handle, void *data_raw, - void *buffer, unsigned int size) -{ - struct HttpResponse *response = GetHttpResponseFromBuffer(buffer, size); + *pos = *mci->pos_input; - if (response != NULL) + if (game_status == GAME_MODE_PSEUDO_TYPENAMES) { - HandleResponse_ApiRenamePlayer(response, data_raw); + TreeInfo *ti = player_name_current; + int first_entry = ti->cl_first; + int entry_pos = first_entry + ti->cl_cursor; + TreeInfo *node_first = getTreeInfoFirstGroupEntry(ti); + int xpos = MENU_SCREEN_START_XPOS; + int ypos = MENU_SCREEN_START_YPOS + ti->cl_cursor; + + type_name_node = getTreeInfoFromPos(node_first, entry_pos); + type_name_nr = entry_pos; + + strcpy(name, type_name_node->name); - checked_free(response); + pos->x = xpos * 32; + pos->y = ypos * 32; + pos->width = MAX_PLAYER_NAME_LEN * 32; } else { - Error("server response too large to handle (%d bytes)", size); + type_name_nr = user.nr; + + strcpy(name, setup.player_name); } - FreeThreadData_ApiRenamePlayer(data_raw); -} + strcpy(type_name_last, name); -static void Emscripten_ApiRenamePlayer_Failed(unsigned handle, void *data_raw, - int code, const char *status) -{ - Error("server failed to handle request: %d %s", code, status); + if (strEqual(name, EMPTY_PLAYER_NAME)) + strcpy(name, ""); - FreeThreadData_ApiRenamePlayer(data_raw); + *xpos = strlen(name); } -static void Emscripten_ApiRenamePlayer_Progress(unsigned handle, void *data_raw, - int bytes, int size) +static void setTypeNameValues_Name(char *name, struct TextPosInfo *pos) { - // nothing to do here -} + // change name of edited user in global list of user names + setString(&global.user_names[type_name_nr], name); -static void Emscripten_ApiRenamePlayer_HttpRequest(struct HttpRequest *request, - void *data_raw) -{ - if (!SetRequest_ApiRenamePlayer(request, data_raw)) + if (game_status == GAME_MODE_PSEUDO_TYPENAMES) { - FreeThreadData_ApiRenamePlayer(data_raw); + TreeInfo *node = type_name_node; - return; - } + // change name of edited user in local menu tree structure + setString(&node->name, name); + setString(&node->name_sorting, name); - emscripten_async_wget2_data(request->uri, - request->method, - request->body, - data_raw, - TRUE, - Emscripten_ApiRenamePlayer_Loaded, - Emscripten_ApiRenamePlayer_Failed, - Emscripten_ApiRenamePlayer_Progress); + node->color = getPlayerNameColor(name); + pos->font = MENU_CHOOSE_TREE_FONT(node->color); + } } -#else - -static void ApiRenamePlayer_HttpRequestExt(struct HttpRequest *request, - struct HttpResponse *response, - void *data_raw) +static void setTypeNameValues(char *name, struct TextPosInfo *pos, + boolean changed) { - if (!SetRequest_ApiRenamePlayer(request, data_raw)) - return; + boolean reset_setup = strEqual(name, ""); + boolean remove_user = strEqual(name, EMPTY_PLAYER_NAME); + boolean create_user = strEqual(type_name_last, EMPTY_PLAYER_NAME); - if (!DoHttpRequest(request, response)) - { - Error("HTTP request failed: %s", GetHttpError()); + if (!changed) + strcpy(name, type_name_last); - return; - } + if (strEqual(name, "")) + strcpy(name, EMPTY_PLAYER_NAME); - if (!HTTP_SUCCESS(response->status_code)) - { - Error("server failed to handle request: %d %s", - response->status_code, - response->status_text); + setTypeNameValues_Name(name, pos); + // if player name not changed, no further action required + if (strEqual(name, type_name_last)) return; - } - HandleResponse_ApiRenamePlayer(response, data_raw); -} + // redraw player name before (possibly) opening request dialogs + drawTypeNameText(name, pos, FALSE); -static void ApiRenamePlayer_HttpRequest(struct HttpRequest *request, - struct HttpResponse *response, - void *data_raw) -{ - ApiRenamePlayer_HttpRequestExt(request, response, data_raw); + int last_user_nr = user.nr; - FreeThreadData_ApiRenamePlayer(data_raw); -} -#endif + if (game_status == GAME_MODE_PSEUDO_TYPENAMES) + { + // save setup of currently active user (may differ from edited user) + SaveSetup(); -static int ApiRenamePlayerThread(void *data_raw) -{ - struct HttpRequest *request = checked_calloc(sizeof(struct HttpRequest)); - struct HttpResponse *response = checked_calloc(sizeof(struct HttpResponse)); - - program.api_thread_count++; - -#if defined(PLATFORM_EMSCRIPTEN) - Emscripten_ApiRenamePlayer_HttpRequest(request, data_raw); -#else - ApiRenamePlayer_HttpRequest(request, response, data_raw); -#endif - - program.api_thread_count--; - - checked_free(request); - checked_free(response); - - return 0; -} - -static void ApiRenamePlayerAsThread(void) -{ - struct ApiRenamePlayerThreadData *data = CreateThreadData_ApiRenamePlayer(); - - ExecuteAsThread(ApiRenamePlayerThread, - "ApiRenamePlayer", data, - "rename player on server"); -} - - -// ============================================================================ -// reset player UUID API functions -// ============================================================================ - -struct ApiResetUUIDThreadData -{ - char *player_name; - char *player_uuid_old; - char *player_uuid_new; -}; - -static void *CreateThreadData_ApiResetUUID(char *uuid_new) -{ - struct ApiResetUUIDThreadData *data = - checked_malloc(sizeof(struct ApiResetUUIDThreadData)); - - data->player_name = getStringCopy(setup.player_name); - data->player_uuid_old = getStringCopy(setup.player_uuid); - data->player_uuid_new = getStringCopy(uuid_new); - - return data; -} - -static void FreeThreadData_ApiResetUUID(void *data_raw) -{ - struct ApiResetUUIDThreadData *data = data_raw; - - checked_free(data->player_name); - checked_free(data->player_uuid_old); - checked_free(data->player_uuid_new); - checked_free(data); -} - -static boolean SetRequest_ApiResetUUID(struct HttpRequest *request, - void *data_raw) -{ - struct ApiResetUUIDThreadData *data = data_raw; - char *player_name_raw = data->player_name; - char *player_uuid_old_raw = data->player_uuid_old; - char *player_uuid_new_raw = data->player_uuid_new; - - request->hostname = setup.api_server_hostname; - request->port = API_SERVER_PORT; - request->method = API_SERVER_METHOD; - request->uri = API_SERVER_URI_RESETUUID; - - char *player_name = getEscapedJSON(player_name_raw); - char *player_uuid_old = getEscapedJSON(player_uuid_old_raw); - char *player_uuid_new = getEscapedJSON(player_uuid_new_raw); - - snprintf(request->body, MAX_HTTP_BODY_SIZE, - "{\n" - "%s" - " \"game_version\": \"%s\",\n" - " \"game_platform\": \"%s\",\n" - " \"name\": \"%s\",\n" - " \"uuid_old\": \"%s\",\n" - " \"uuid_new\": \"%s\"\n" - "}\n", - getPasswordJSON(setup.api_server_password), - getProgramRealVersionString(), - getProgramPlatformString(), - player_name, - player_uuid_old, - player_uuid_new); - - checked_free(player_name); - checked_free(player_uuid_old); - checked_free(player_uuid_new); - - ConvertHttpRequestBodyToServerEncoding(request); - - return TRUE; -} - -static void HandleResponse_ApiResetUUID(struct HttpResponse *response, - void *data_raw) -{ - struct ApiResetUUIDThreadData *data = data_raw; - - // upgrade player UUID in server setup file - setup.player_uuid = getStringCopy(data->player_uuid_new); - setup.player_version = 2; - - SaveSetup_ServerSetup(); -} - -#if defined(PLATFORM_EMSCRIPTEN) -static void Emscripten_ApiResetUUID_Loaded(unsigned handle, void *data_raw, - void *buffer, unsigned int size) -{ - struct HttpResponse *response = GetHttpResponseFromBuffer(buffer, size); - - if (response != NULL) - { - HandleResponse_ApiResetUUID(response, data_raw); - - checked_free(response); - } - else - { - Error("server response too large to handle (%d bytes)", size); - } - - FreeThreadData_ApiResetUUID(data_raw); -} - -static void Emscripten_ApiResetUUID_Failed(unsigned handle, void *data_raw, - int code, const char *status) -{ - Error("server failed to handle request: %d %s", code, status); - - FreeThreadData_ApiResetUUID(data_raw); -} - -static void Emscripten_ApiResetUUID_Progress(unsigned handle, void *data_raw, - int bytes, int size) -{ - // nothing to do here -} - -static void Emscripten_ApiResetUUID_HttpRequest(struct HttpRequest *request, - void *data_raw) -{ - if (!SetRequest_ApiResetUUID(request, data_raw)) - { - FreeThreadData_ApiResetUUID(data_raw); - - return; - } - - emscripten_async_wget2_data(request->uri, - request->method, - request->body, - data_raw, - TRUE, - Emscripten_ApiResetUUID_Loaded, - Emscripten_ApiResetUUID_Failed, - Emscripten_ApiResetUUID_Progress); -} - -#else - -static void ApiResetUUID_HttpRequestExt(struct HttpRequest *request, - struct HttpResponse *response, - void *data_raw) -{ - if (!SetRequest_ApiResetUUID(request, data_raw)) - return; - - if (!DoHttpRequest(request, response)) - { - Error("HTTP request failed: %s", GetHttpError()); - - return; - } - - if (!HTTP_SUCCESS(response->status_code)) - { - Error("server failed to handle request: %d %s", - response->status_code, - response->status_text); - - return; - } - - HandleResponse_ApiResetUUID(response, data_raw); -} - -static void ApiResetUUID_HttpRequest(struct HttpRequest *request, - struct HttpResponse *response, - void *data_raw) -{ - ApiResetUUID_HttpRequestExt(request, response, data_raw); - - FreeThreadData_ApiResetUUID(data_raw); -} -#endif - -static int ApiResetUUIDThread(void *data_raw) -{ - struct HttpRequest *request = checked_calloc(sizeof(struct HttpRequest)); - struct HttpResponse *response = checked_calloc(sizeof(struct HttpResponse)); - - program.api_thread_count++; - -#if defined(PLATFORM_EMSCRIPTEN) - Emscripten_ApiResetUUID_HttpRequest(request, data_raw); -#else - ApiResetUUID_HttpRequest(request, response, data_raw); -#endif - - program.api_thread_count--; - - checked_free(request); - checked_free(response); - - return 0; -} - -static void ApiResetUUIDAsThread(char *uuid_new) -{ - struct ApiResetUUIDThreadData *data = CreateThreadData_ApiResetUUID(uuid_new); - - ExecuteAsThread(ApiResetUUIDThread, - "ApiResetUUID", data, - "reset UUID on server"); -} - - -// ============================================================================ -// type name functions -// ============================================================================ - -static TreeInfo *type_name_node = NULL; -static char type_name_last[MAX_PLAYER_NAME_LEN + 1] = { 0 }; -static int type_name_nr = 0; - -static int getPlayerNameColor(char *name) -{ - return (strEqual(name, EMPTY_PLAYER_NAME) ? FC_BLUE : FC_RED); -} - -static void drawTypeNameText(char *name, struct TextPosInfo *pos, - boolean active) -{ - char text[MAX_PLAYER_NAME_LEN + 2] = { 0 }; - boolean multiple_users = (game_status == GAME_MODE_PSEUDO_TYPENAMES); - int sx = (multiple_users ? amSX + pos->x : mSX + ALIGNED_TEXT_XPOS(pos)); - int sy = (multiple_users ? amSY + pos->y : mSY + ALIGNED_TEXT_YPOS(pos)); - int font_nr = (active ? FONT_ACTIVE(pos->font) : pos->font); - int font_width = getFontWidth(font_nr); - - DrawBackgroundForFont(sx, sy, pos->width, pos->height, font_nr); - - sprintf(text, "%s%c", name, (active ? '_' : '\0')); - - pos->width = strlen(text) * font_width; - sx = (multiple_users ? amSX + pos->x : mSX + ALIGNED_TEXT_XPOS(pos)); - - DrawText(sx, sy, text, font_nr); -} - -static void getTypeNameValues(char *name, struct TextPosInfo *pos, int *xpos) -{ - struct MainControlInfo *mci = getMainControlInfo(MAIN_CONTROL_NAME); - - *pos = *mci->pos_input; - - if (game_status == GAME_MODE_PSEUDO_TYPENAMES) - { - TreeInfo *ti = player_name_current; - int first_entry = ti->cl_first; - int entry_pos = first_entry + ti->cl_cursor; - TreeInfo *node_first = getTreeInfoFirstGroupEntry(ti); - int xpos = MENU_SCREEN_START_XPOS; - int ypos = MENU_SCREEN_START_YPOS + ti->cl_cursor; - - type_name_node = getTreeInfoFromPos(node_first, entry_pos); - type_name_nr = entry_pos; - - strcpy(name, type_name_node->name); - - pos->x = xpos * 32; - pos->y = ypos * 32; - pos->width = MAX_PLAYER_NAME_LEN * 32; - } - else - { - type_name_nr = user.nr; - - strcpy(name, setup.player_name); - } - - strcpy(type_name_last, name); - - if (strEqual(name, EMPTY_PLAYER_NAME)) - strcpy(name, ""); - - *xpos = strlen(name); -} - -static void setTypeNameValues_Name(char *name, struct TextPosInfo *pos) -{ - // change name of edited user in global list of user names - setString(&global.user_names[type_name_nr], name); - - if (game_status == GAME_MODE_PSEUDO_TYPENAMES) - { - TreeInfo *node = type_name_node; - - // change name of edited user in local menu tree structure - setString(&node->name, name); - setString(&node->name_sorting, name); - - node->color = getPlayerNameColor(name); - pos->font = MENU_CHOOSE_TREE_FONT(node->color); - } -} - -static void setTypeNameValues(char *name, struct TextPosInfo *pos, - boolean changed) -{ - boolean reset_setup = strEqual(name, ""); - boolean remove_user = strEqual(name, EMPTY_PLAYER_NAME); - boolean create_user = strEqual(type_name_last, EMPTY_PLAYER_NAME); - - if (!changed) - strcpy(name, type_name_last); - - if (strEqual(name, "")) - strcpy(name, EMPTY_PLAYER_NAME); - - setTypeNameValues_Name(name, pos); - - // if player name not changed, no further action required - if (strEqual(name, type_name_last)) - return; - - // redraw player name before (possibly) opening request dialogs - drawTypeNameText(name, pos, FALSE); - - int last_user_nr = user.nr; - - if (game_status == GAME_MODE_PSEUDO_TYPENAMES) - { - // save setup of currently active user (may differ from edited user) - SaveSetup(); - - // temporarily change active user to edited user - user.nr = type_name_nr; + // temporarily change active user to edited user + user.nr = type_name_nr; if (create_user && Request("Use current setup values for the new player?", REQ_ASK)) @@ -4804,14 +4339,24 @@ static int getAlignYOffsetFromTreeInfo(TreeInfo *ti) return align_yoffset; } +static void StartPlayingFromHallOfFame(void) +{ + level_nr = scores.next_level_nr; + LoadLevel(level_nr); + + StartGameActions(network.enabled, setup.autorecord, level.random_seed); +} + static void DrawChooseTree(TreeInfo **ti_ptr) { int fade_mask = REDRAW_FIELD; + boolean restart_music = (game_status != game_status_last_screen && + game_status_last_screen != GAME_MODE_SCOREINFO); if (CheckFadeAll()) fade_mask = REDRAW_ALL; - if (strEqual((*ti_ptr)->subdir, STRING_TOP_DIRECTORY)) + if (*ti_ptr != NULL && strEqual((*ti_ptr)->subdir, STRING_TOP_DIRECTORY)) { if (game_status == GAME_MODE_SETUP) { @@ -4832,12 +4377,12 @@ static void DrawChooseTree(TreeInfo **ti_ptr) FreeScreenGadgets(); CreateScreenGadgets(); - if (game_status != game_status_last_screen) + if (restart_music) FadeMenuSoundsAndMusic(); FadeOut(fade_mask); - // needed if different viewport properties defined for choosing level (set) + // needed if different viewport properties defined for this screen ChangeViewportPropertiesIfNeeded(); if (game_status == GAME_MODE_NAMES) @@ -4846,17 +4391,24 @@ static void DrawChooseTree(TreeInfo **ti_ptr) SetMainBackgroundImage(IMG_BACKGROUND_LEVELNR); else if (game_status == GAME_MODE_LEVELS) SetMainBackgroundImage(IMG_BACKGROUND_LEVELS); + else if (game_status == GAME_MODE_SCORES) + SetMainBackgroundImage(IMG_BACKGROUND_SCORES); ClearField(); OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW); + // map gadgets for high score screen + if (game_status == GAME_MODE_SCORES) + MapScreenMenuGadgets(SCREEN_MASK_SCORES); + MapScreenTreeGadgets(*ti_ptr); + HandleChooseTree(0, 0, 0, 0, MB_MENU_INITIALIZE, ti_ptr); DrawMaskedBorder(fade_mask); - if (game_status != game_status_last_screen) + if (restart_music) PlayMenuSoundsAndMusic(); FadeIn(fade_mask); @@ -4864,13 +4416,13 @@ static void DrawChooseTree(TreeInfo **ti_ptr) static int getChooseTreeFont(TreeInfo *node, boolean active) { - int font_color = MENU_CHOOSE_TREE_COLOR(node, active); - int font_nr = MENU_CHOOSE_TREE_FONT(font_color); - - return font_nr; + if (game_status == GAME_MODE_SCORES) + return (active ? FONT_TEXT_1_ACTIVE : FONT_TEXT_1); + else + return MENU_CHOOSE_TREE_FONT(MENU_CHOOSE_TREE_COLOR(node, active)); } -static void drawChooseTreeText(int y, boolean active, TreeInfo *ti) +static void drawChooseTreeText(TreeInfo *ti, int y, boolean active) { int num_entries = numTreeInfoInGroup(ti); boolean scrollbar_needed = (num_entries > NUM_MENU_ENTRIES_ON_SCREEN); @@ -4881,36 +4433,83 @@ static void drawChooseTreeText(int y, boolean active, TreeInfo *ti) TreeInfo *node_first = getTreeInfoFirstGroupEntry(ti); TreeInfo *node = getTreeInfoFromPos(node_first, entry_pos); int font_nr = getChooseTreeFont(node, active); - int font_xoffset = getFontBitmapInfo(font_nr)->draw_xoffset; + int font_xoffset = getFontDrawOffsetX(font_nr); int xpos = MENU_SCREEN_START_XPOS; int ypos = MENU_SCREEN_START_YPOS + y; - int startx = amSX + xpos * 32; - int starty = amSY + ypos * 32; + int startdx = xpos * 32; + int startdy = ypos * 32; + int startx = amSX + startdx; + int starty = amSY + startdy; int startx_text = startx + font_xoffset; int endx_text = amSX + screen_width; int max_text_size = endx_text - startx_text; int max_buffer_len = max_text_size / getFontWidth(font_nr); char buffer[max_buffer_len + 1]; - strncpy(buffer, node->name, max_buffer_len); - buffer[max_buffer_len] = '\0'; + if (game_status == GAME_MODE_SCORES && !node->parent_link) + { + int font_nr1 = (active ? FONT_TEXT_1_ACTIVE : FONT_TEXT_1); + int font_nr2 = (active ? FONT_TEXT_2_ACTIVE : FONT_TEXT_2); + int font_nr3 = (active ? FONT_TEXT_3_ACTIVE : FONT_TEXT_3); + int font_nr4 = (active ? FONT_TEXT_4_ACTIVE : FONT_TEXT_4); + int font_size_1 = getFontWidth(font_nr1); + int font_size_3 = getFontWidth(font_nr3); + int font_size_4 = getFontWidth(font_nr4); + int text_size_1 = 4 * font_size_1; + int text_size_4 = 5 * font_size_4; + int border = amSX - SX + getFontDrawOffsetX(font_nr1); + int dx1 = 0; + int dx3 = text_size_1; + int dx4 = SXSIZE - 2 * startdx - 2 * border - text_size_4; + int num_dots = (dx4 - dx3) / font_size_3; + int startx1 = startx + dx1; + int startx3 = startx + dx3; + int startx4 = startx + dx4; + int pos = node->pos; + char *pos_text = getHallOfFameRankText(pos, 3); + int i; + + DrawText(startx1, starty, pos_text, font_nr1); + + for (i = 0; i < num_dots; i++) + DrawText(startx3 + i * font_size_3, starty, ".", font_nr3); - DrawText(startx, starty, buffer, font_nr); + if (!strEqual(scores.entry[pos].name, EMPTY_PLAYER_NAME)) + DrawText(startx3, starty, scores.entry[pos].name, font_nr2); + + DrawText(startx4, starty, getHallOfFameScoreText(pos, 5), font_nr4); + } + else + { + strncpy(buffer, node->name, max_buffer_len); + buffer[max_buffer_len] = '\0'; + + DrawText(startx, starty, buffer, font_nr); + } } -static void drawChooseTreeList(int first_entry, int num_page_entries, - TreeInfo *ti) +static void drawChooseTreeHeadExt(int type, char *title_string) { - int i; - char *title_string = NULL; int yoffset_sets = MENU_TITLE1_YPOS; int yoffset_setup = 16; - int yoffset = (ti->type == TREE_TYPE_LEVEL_DIR || - ti->type == TREE_TYPE_LEVEL_NR ? yoffset_sets : yoffset_setup); - - title_string = ti->infotext; + int yoffset = (type == TREE_TYPE_SCORE_ENTRY || + type == TREE_TYPE_LEVEL_DIR || + type == TREE_TYPE_LEVEL_NR ? yoffset_sets : yoffset_setup); DrawTextSCentered(mSY - SY + yoffset, FONT_TITLE_1, title_string); +} + +static void drawChooseTreeHead(TreeInfo *ti) +{ + drawChooseTreeHeadExt(ti->type, ti->infotext); +} + +static void drawChooseTreeList(TreeInfo *ti) +{ + int first_entry = ti->cl_first; + int num_entries = numTreeInfoInGroup(ti); + int num_page_entries = MIN(num_entries, NUM_MENU_ENTRIES_ON_SCREEN); + int i; clearMenuListArea(); @@ -4922,7 +4521,7 @@ static void drawChooseTreeList(int first_entry, int num_page_entries, node_first = getTreeInfoFirstGroupEntry(ti); node = getTreeInfoFromPos(node_first, entry_pos); - drawChooseTreeText(i, FALSE, ti); + drawChooseTreeText(ti, i, FALSE); if (node->parent_link) initCursor(i, IMG_MENU_BUTTON_LEAVE_MENU); @@ -4931,6 +4530,9 @@ static void drawChooseTreeList(int first_entry, int num_page_entries, else initCursor(i, IMG_MENU_BUTTON); + if (game_status == GAME_MODE_SCORES && node->pos == scores.last_added) + initCursor(i, IMG_MENU_BUTTON_ENTER_MENU); + if (game_status == GAME_MODE_NAMES) drawChooseTreeEdit(i, FALSE); } @@ -4938,21 +4540,26 @@ static void drawChooseTreeList(int first_entry, int num_page_entries, redraw_mask |= REDRAW_FIELD; } -static void drawChooseTreeInfo(int entry_pos, TreeInfo *ti) +static void drawChooseTreeInfo(TreeInfo *ti) { - TreeInfo *node, *node_first; - int x, last_redraw_mask = redraw_mask; + int entry_pos = ti->cl_first + ti->cl_cursor; + int last_redraw_mask = redraw_mask; int ypos = MENU_TITLE2_YPOS; int font_nr = FONT_TITLE_2; + int x; if (ti->type == TREE_TYPE_LEVEL_NR) DrawTextFCentered(ypos, font_nr, leveldir_current->name); + if (ti->type == TREE_TYPE_SCORE_ENTRY) + DrawTextFCentered(ypos, font_nr, "HighScores of Level %d", + scores.last_level_nr); + if (ti->type != TREE_TYPE_LEVEL_DIR) return; - node_first = getTreeInfoFirstGroupEntry(ti); - node = getTreeInfoFromPos(node_first, entry_pos); + TreeInfo *node_first = getTreeInfoFirstGroupEntry(ti); + TreeInfo *node = getTreeInfoFromPos(node_first, entry_pos); DrawBackgroundForFont(SX, SY + ypos, SXSIZE, getFontHeight(font_nr), font_nr); @@ -4973,22 +4580,79 @@ static void drawChooseTreeInfo(int entry_pos, TreeInfo *ti) MarkTileDirty(x, 1); } -static void drawChooseTreeCursorAndText(int y, boolean active, TreeInfo *ti) +static void drawChooseTreeCursorAndText(TreeInfo *ti, boolean active) { - drawChooseTreeCursor(y, active); - drawChooseTreeText(y, active, ti); + drawChooseTreeCursor(ti->cl_cursor, active); + drawChooseTreeText(ti, ti->cl_cursor, active); } static void drawChooseTreeScreen(TreeInfo *ti) { - int num_entries = numTreeInfoInGroup(ti); + drawChooseTreeHead(ti); + drawChooseTreeList(ti); + drawChooseTreeInfo(ti); + drawChooseTreeCursorAndText(ti, TRUE); + + AdjustChooseTreeScrollbar(ti, SCREEN_CTRL_ID_SCROLL_VERTICAL); + + // scroll bar and buttons may just have been added after reloading scores + if (game_status == GAME_MODE_SCORES) + MapScreenTreeGadgets(ti); +} + +static void drawChooseTreeScreen_Scores_NotAvailable(void) +{ + // dirty workaround to use spacing definitions from info screen + info_mode = INFO_MODE_TITLE; + + char *text_info = "HighScores of Level %d"; + char *text_title = "Score information:"; + char *text_error = "No scores for this level."; + char *text_foot = "Press any key or button for main menu"; + int font_info = FONT_TITLE_2; + int font_title = FONT_INITIAL_3; + int font_error = FONT_INITIAL_4; + int font_foot = FONT_INITIAL_2; + int spacing_title = menu.headline1_spacing_info[INFO_MODE_TITLE]; + int ystep_title = getMenuTextStep(spacing_title, font_title); + int ystart1 = mSY - SY + MENU_SCREEN_INFO_YSTART1; + int ystart2 = ystart1 + ystep_title; + int ybottom = mSY - SY + MENU_SCREEN_INFO_YBOTTOM; + int ystart0 = MENU_TITLE2_YPOS; + + drawChooseTreeHeadExt(TREE_TYPE_SCORE_ENTRY, INFOTEXT_SCORE_ENTRY); + DrawTextFCentered(ystart0, font_info, text_info, scores.last_level_nr); + + DrawTextSCentered(ystart1, font_title, text_title); + DrawTextSCentered(ystart2, font_error, text_error); + + DrawTextSCentered(ybottom, font_foot, text_foot); +} + +static TreeInfo *setHallOfFameActiveEntry(TreeInfo **ti_ptr) +{ + int score_pos = scores.last_added; + + if (game_status_last_screen == GAME_MODE_SCOREINFO) + score_pos = scores.last_entry_nr; + + // set current tree entry to last added score entry + *ti_ptr = getTreeInfoFromIdentifier(score_entries, i_to_a(score_pos)); + + // if that fails, set current tree entry to first entry (back link) + if (*ti_ptr == NULL) + *ti_ptr = score_entries->node_group; + + int num_entries = numTreeInfoInGroup(*ti_ptr); int num_page_entries = MIN(num_entries, NUM_MENU_ENTRIES_ON_SCREEN); + int pos_score = getPosFromTreeInfo(*ti_ptr); + int pos_first_raw = pos_score - (num_page_entries + 1) / 2 + 1; + int pos_first = MIN(MAX(0, pos_first_raw), num_entries - num_page_entries); - drawChooseTreeList(ti->cl_first, num_page_entries, ti); - drawChooseTreeInfo(ti->cl_first + ti->cl_cursor, ti); - drawChooseTreeCursorAndText(ti->cl_cursor, TRUE, ti); + (*ti_ptr)->cl_first = pos_first; + (*ti_ptr)->cl_cursor = pos_score - pos_first; - AdjustChooseTreeScrollbar(SCREEN_CTRL_ID_SCROLL_VERTICAL, ti->cl_first, ti); + return *ti_ptr; } static void HandleChooseTree(int mx, int my, int dx, int dy, int button, @@ -4998,14 +4662,56 @@ static void HandleChooseTree(int mx, int my, int dx, int dy, int button, boolean has_scrollbar = screen_gadget[SCREEN_CTRL_ID_SCROLL_VERTICAL]->mapped; int mx_scrollbar = screen_gadget[SCREEN_CTRL_ID_SCROLL_VERTICAL]->x; int mx_right_border = (has_scrollbar ? mx_scrollbar : SX + SXSIZE); - int sx1_edit_name = getChooseTreeEditXPos(POS_LEFT); - int sx2_edit_name = getChooseTreeEditXPos(POS_RIGHT); + int sx1_edit_name = getChooseTreeEditXPosReal(POS_LEFT); + int sx2_edit_name = getChooseTreeEditXPosReal(POS_RIGHT); int x = 0; - int y = ti->cl_cursor; + int y = (ti != NULL ? ti->cl_cursor : 0); int step = (button == 1 ? 1 : button == 2 ? 5 : 10); int num_entries = numTreeInfoInGroup(ti); int num_page_entries = MIN(num_entries, NUM_MENU_ENTRIES_ON_SCREEN); boolean position_set_by_scrollbar = (dx == 999); + boolean button_action = (button == MB_MENU_LEAVE || button == MB_MENU_CHOICE); + boolean button_is_valid = (mx >= 0 && my >= 0); + boolean button_screen_clicked = (button_action && button_is_valid); + + if (game_status == GAME_MODE_SCORES) + { + if (server_scores.updated) + { + // reload scores, using updated server score cache file + LoadLocalAndServerScore(scores.last_level_nr, FALSE); + + server_scores.updated = FALSE; + + DrawHallOfFame_setScoreEntries(); + + if (score_entries != NULL) + { + ti = setHallOfFameActiveEntry(ti_ptr); + + if (button != MB_MENU_INITIALIZE) + drawChooseTreeScreen(ti); + } + } + + if (score_entries == NULL) + { + if (button == MB_MENU_INITIALIZE) + { + drawChooseTreeScreen_Scores_NotAvailable(); + } + else if (button_screen_clicked) + { + PlaySound(SND_MENU_ITEM_SELECTING); + + SetGameStatus(GAME_MODE_MAIN); + + DrawMainMenu(); + } + + return; + } + } if (button == MB_MENU_INITIALIZE) { @@ -5015,11 +4721,16 @@ static void HandleChooseTree(int mx, int my, int dx, int dy, int button, align_xoffset = getAlignXOffsetFromTreeInfo(ti); align_yoffset = getAlignYOffsetFromTreeInfo(ti); - if (ti->cl_first == -1) + if (game_status == GAME_MODE_SCORES) + { + ti = setHallOfFameActiveEntry(ti_ptr); + } + else if (ti->cl_first == -1) { // only on initialization ti->cl_first = MAX(0, entry_pos - num_page_entries + 1); ti->cl_cursor = entry_pos - ti->cl_first; + } else if (ti->cl_cursor >= num_page_entries || (num_entries > num_page_entries && @@ -5039,7 +4750,8 @@ static void HandleChooseTree(int mx, int my, int dx, int dy, int button, } else if (button == MB_MENU_LEAVE) { - FadeSetLeaveMenu(); + if (game_status != GAME_MODE_SCORES) + FadeSetLeaveMenu(); PlaySound(SND_MENU_ITEM_SELECTING); @@ -5093,6 +4805,23 @@ static void HandleChooseTree(int mx, int my, int dx, int dy, int button, return; } +#if defined(PLATFORM_ANDROID) + // touching the screen anywhere continues playing the next level + if ((mx || my) && scores.continue_playing) + { + mx = my = 0; + button = MB_MENU_CHOICE; + } +#endif + + // any mouse click or cursor key stops leaving scores by "Return" key + if ((mx || my || dx || dy) && scores.continue_on_return) + { + scores.continue_on_return = FALSE; + level_nr = scores.last_level_nr; + LoadLevel(level_nr); + } + if (mx || my) // mouse input { x = (mx - amSX) / 32; @@ -5150,7 +4879,13 @@ static void HandleChooseTree(int mx, int my, int dx, int dy, int button, y = ti->cl_cursor + dy; } - if (dx == 1) + if (game_status == GAME_MODE_SCORES && ABS(dx) == 1) + { + HandleHallOfFame_SelectLevel(1, dx); + + return; + } + else if (dx == 1) { TreeInfo *node_first, *node_cursor; int entry_pos = ti->cl_first + y; @@ -5201,11 +4936,13 @@ static void HandleChooseTree(int mx, int my, int dx, int dy, int button, { PlaySound(SND_MENU_ITEM_ACTIVATING); - drawChooseTreeCursorAndText(ti->cl_cursor, FALSE, ti); - drawChooseTreeCursorAndText(y, TRUE, ti); - drawChooseTreeInfo(ti->cl_first + y, ti); + drawChooseTreeCursorAndText(ti, FALSE); ti->cl_cursor = y; + + drawChooseTreeCursorAndText(ti, TRUE); + + drawChooseTreeInfo(ti); } else if (dx < 0) { @@ -5260,14 +4997,16 @@ static void HandleChooseTree(int mx, int my, int dx, int dy, int button, } else if (node_cursor->parent_link) { - FadeSetLeaveMenu(); + if (game_status != GAME_MODE_SCORES) + FadeSetLeaveMenu(); *ti_ptr = node_cursor->node_parent; DrawChooseTree(ti_ptr); } else { - FadeSetEnterMenu(); + if (game_status != GAME_MODE_SCORES) + FadeSetEnterMenu(); node_cursor->cl_first = ti->cl_first; node_cursor->cl_cursor = ti->cl_cursor; @@ -5359,6 +5098,23 @@ static void HandleChooseTree(int mx, int my, int dx, int dy, int button, ChangeCurrentArtworkIfNeeded(ARTWORK_TYPE_SOUNDS); ChangeCurrentArtworkIfNeeded(ARTWORK_TYPE_MUSIC); } + else if (game_status == GAME_MODE_SCORES) + { + if (scores.continue_playing && scores.continue_on_return) + { + StartPlayingFromHallOfFame(); + + return; + } + else if (!scores.continue_on_return) + { + SetGameStatus(GAME_MODE_SCOREINFO); + + DrawScoreInfo(node_cursor->pos); + + return; + } + } SetGameStatus(GAME_MODE_MAIN); @@ -5367,6 +5123,9 @@ static void HandleChooseTree(int mx, int my, int dx, int dy, int button, } } } + + if (game_status == GAME_MODE_SCORES) + PlayMenuSoundIfLoop(); } void DrawChoosePlayerName(void) @@ -5411,6 +5170,9 @@ void DrawChoosePlayerName(void) if (player_name_current == NULL) player_name_current = player_name; + // set text size for main name input (also used on name selection screen) + InitializeMainControls(); + DrawChooseTree(&player_name_current); } @@ -5456,44 +5218,98 @@ void DrawChooseLevelNr(void) LevelStats_getPlayed(i) ? FC_YELLOW : FC_RED); snprintf(identifier, sizeof(identifier), "%d", value); - snprintf(name, sizeof(name), "%03d: %s", value, - (level.no_level_file ? "(no file)" : level.name)); + snprintf(name, sizeof(name), "%03d: %s", value, + (level.no_level_file ? "(no file)" : level.name)); + + setString(&ti->identifier, identifier); + setString(&ti->name, name); + setString(&ti->name_sorting, name); + + pushTreeInfo(&level_number, ti); + } + + // sort level number values to start with lowest level number + sortTreeInfo(&level_number); + + // set current level number to current level number + level_number_current = + getTreeInfoFromIdentifier(level_number, i_to_a(level_nr)); + + // if that also fails, set current level number to first available level + if (level_number_current == NULL) + level_number_current = level_number; + + DrawChooseTree(&level_number_current); +} + +void HandleChooseLevelNr(int mx, int my, int dx, int dy, int button) +{ + HandleChooseTree(mx, my, dx, dy, button, &level_number_current); +} + +static void DrawHallOfFame_setScoreEntries(void) +{ + int max_empty_entries = 10; // at least show "top ten" list, if empty + int max_visible_entries = NUM_MENU_ENTRIES_ON_SCREEN - 1; // w/o back link + int min_score_entries = MIN(max_empty_entries, max_visible_entries); + int score_pos = (scores.last_added >= 0 ? scores.last_added : 0); + int i; + + if (score_entries != NULL) + { + freeTreeInfo(score_entries); + + score_entries = NULL; + } + + for (i = 0; i < MAX_SCORE_ENTRIES; i++) + { + // do not add empty score entries if off-screen + if (scores.entry[i].score == 0 && + scores.entry[i].time == 0 && + i >= min_score_entries) + break; + + TreeInfo *ti = newTreeInfo_setDefaults(TREE_TYPE_SCORE_ENTRY); + char identifier[32], name[64]; + int value = i; + + ti->node_top = &score_entries; + ti->sort_priority = 10000 + value; + ti->color = FC_YELLOW; + ti->pos = i; + + snprintf(identifier, sizeof(identifier), "%d", value); + snprintf(name, sizeof(name), "%03d.", value + 1); setString(&ti->identifier, identifier); setString(&ti->name, name); setString(&ti->name_sorting, name); - pushTreeInfo(&level_number, ti); + pushTreeInfo(&score_entries, ti); } - // sort level number values to start with lowest level number - sortTreeInfo(&level_number); + // sort score entries to start with highest score entry + sortTreeInfo(&score_entries); - // set current level number to current level number - level_number_current = - getTreeInfoFromIdentifier(level_number, i_to_a(level_nr)); + // add top tree node to create back link to main menu + score_entries = addTopTreeInfoNode(score_entries); - // if that also fails, set current level number to first available level - if (level_number_current == NULL) - level_number_current = level_number; + // set current score entry to last added or highest score entry + score_entry_current = + getTreeInfoFromIdentifier(score_entries, i_to_a(score_pos)); - DrawChooseTree(&level_number_current); -} + // if that fails, set current score entry to first valid score entry + if (score_entry_current == NULL) + score_entry_current = getFirstValidTreeInfoEntry(score_entries); -void HandleChooseLevelNr(int mx, int my, int dx, int dy, int button) -{ - HandleChooseTree(mx, my, dx, dy, button, &level_number_current); + // ("score_entries" and "score_entry_current" may be NULL here) } -void DrawHallOfFame(int level_nr) +void DrawHallOfFame(int nr) { - int fade_mask = REDRAW_FIELD; - - if (CheckFadeAll()) - fade_mask = REDRAW_ALL; - - UnmapAllGadgets(); - FadeMenuSoundsAndMusic(); + scores.last_level_nr = nr; + scores.continue_on_return = (game_status_last_screen == GAME_MODE_PLAYING); // (this is needed when called from GameEnd() after winning a game) KeyboardAutoRepeatOn(); @@ -5502,52 +5318,31 @@ void DrawHallOfFame(int level_nr) SetDrawDeactivationMask(REDRAW_NONE); SetDrawBackgroundMask(REDRAW_FIELD); - LoadLocalAndServerScore(level_nr, TRUE); + LoadLocalAndServerScore(scores.last_level_nr, TRUE); + + DrawHallOfFame_setScoreEntries(); if (scores.last_added >= 0) SetAnimStatus(GAME_MODE_PSEUDO_SCORESNEW); FadeSetEnterScreen(); - FadeOut(fade_mask); - - // needed if different viewport properties defined for scores - ChangeViewportPropertiesIfNeeded(); - - PlayMenuSoundsAndMusic(); - - OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW); - - HandleHallOfFame(level_nr, 0, 0, 0, MB_MENU_INITIALIZE); - - DrawMaskedBorder(fade_mask); - - FadeIn(fade_mask); + DrawChooseTree(&score_entry_current); } -static int getHallOfFameFirstEntry(int first_entry, int step) +static char *getHallOfFameRankText(int nr, int size) { - if (step == 0) - first_entry = scores.last_added - (NUM_MENU_ENTRIES_ON_SCREEN + 1) / 2 + 1; - else - first_entry += step; + static char rank_text[10]; + boolean forced = (scores.force_last_added && nr == scores.last_added); + char *rank_text_raw = (forced ? "???" : int2str(nr + 1, size)); - if (first_entry < 0) - first_entry = 0; - else if (first_entry > MAX_SCORE_ENTRIES - NUM_MENU_ENTRIES_ON_SCREEN) - first_entry = MAX(0, MAX_SCORE_ENTRIES - NUM_MENU_ENTRIES_ON_SCREEN); + sprintf(rank_text, "%s%s", rank_text_raw, (size > 0 || !forced ? "." : "")); - return first_entry; + return rank_text; } -static char *getHallOfFameScoreText(int nr) +static char *getHallOfFameTimeText(int nr) { - if (!level.rate_time_over_score) - return int2str(scores.entry[nr].score, 5); // show normal score - - if (level.use_step_counter) - return int2str(scores.entry[nr].time, 5); // show number of steps - static char score_text[10]; int time_seconds = scores.entry[nr].time / FRAMES_PER_SECOND; int mm = (time_seconds / 60) % 60; @@ -5558,124 +5353,317 @@ static char *getHallOfFameScoreText(int nr) return score_text; } -static void drawHallOfFameList(int level_nr, int first_entry) +static char *getHallOfFameScoreText(int nr, int size) { - int i, j; + if (!level.rate_time_over_score) + return int2str(scores.entry[nr].score, size); // show normal score + else if (level.use_step_counter) + return int2str(scores.entry[nr].time, size); // show number of steps + else + return getHallOfFameTimeText(nr); // show playing time +} - SetMainBackgroundImage(IMG_BACKGROUND_SCORES); - ClearField(); +static char *getHallOfFameTapeDateText(struct ScoreEntry *entry) +{ + static char tape_date[MAX_ISO_DATE_LEN + 1]; + int i, j; - DrawTextSCentered(MENU_TITLE1_YPOS, FONT_TITLE_1, "Hall Of Fame"); - DrawTextFCentered(MENU_TITLE2_YPOS, FONT_TITLE_2, - "HighScores of Level %d", level_nr); + if (!strEqual(entry->tape_date, UNKNOWN_NAME) || + strEqual(entry->tape_basename, UNDEFINED_FILENAME)) + return entry->tape_date; - for (i = 0; i < NUM_MENU_ENTRIES_ON_SCREEN; i++) + for (i = 0, j = 0; i < 8; i++, j++) { - int entry = first_entry + i; - boolean active = (entry == scores.last_added); - boolean forced = (scores.force_last_added && active); - int font_nr1 = (active ? FONT_TEXT_1_ACTIVE : FONT_TEXT_1); - int font_nr2 = (active ? FONT_TEXT_2_ACTIVE : FONT_TEXT_2); - int font_nr3 = (active ? FONT_TEXT_3_ACTIVE : FONT_TEXT_3); - int font_nr4 = (active ? FONT_TEXT_4_ACTIVE : FONT_TEXT_4); - int dxoff = getFontDrawOffsetX(font_nr1); - int dx1 = 3 * getFontWidth(font_nr1); - int dx2 = dx1 + getFontWidth(font_nr1); - int dx3 = SXSIZE - 2 * (mSX - SX + dxoff) - 5 * getFontWidth(font_nr4); - int num_dots = (dx3 - dx2) / getFontWidth(font_nr3); - int sy = mSY + 64 + i * 32; - char *pos_text = (forced ? "???" : int2str(entry + 1, 3)); - - DrawText(mSX, sy, pos_text, font_nr1); - DrawText(mSX + dx1, sy, ".", font_nr1); - - for (j = 0; j < num_dots; j++) - DrawText(mSX + dx2 + j * getFontWidth(font_nr3), sy, ".", font_nr3); + tape_date[j] = entry->tape_basename[i]; - if (!strEqual(scores.entry[entry].name, EMPTY_PLAYER_NAME)) - DrawText(mSX + dx2, sy, scores.entry[entry].name, font_nr2); - - DrawText(mSX + dx3, sy, getHallOfFameScoreText(entry), font_nr4); + if (i == 3 || i == 5) + tape_date[++j] = '-'; } - redraw_mask |= REDRAW_FIELD; + tape_date[MAX_ISO_DATE_LEN] = '\0'; + + return tape_date; } -void HandleHallOfFame(int mx, int my, int dx, int dy, int button) +static void HandleHallOfFame_SelectLevel(int step, int direction) { - static int level_nr = 0; - static int first_entry = 0; - int step = (button == 1 ? 1 : button == 2 ? 5 : 10); + int old_level_nr = scores.last_level_nr; + int new_level_nr = old_level_nr + step * direction; - if (button == MB_MENU_INITIALIZE) + if (new_level_nr < leveldir_current->first_level) + new_level_nr = leveldir_current->first_level; + if (new_level_nr > leveldir_current->last_level) + new_level_nr = leveldir_current->last_level; + + if (setup.handicap && new_level_nr > leveldir_current->handicap_level) + new_level_nr = leveldir_current->handicap_level; + + if (new_level_nr != old_level_nr) { - level_nr = mx; + PlaySound(SND_MENU_ITEM_SELECTING); - if (server_scores.updated) + scores.last_level_nr = level_nr = new_level_nr; + scores.last_entry_nr = 0; + + LoadLevel(level_nr); + LoadLocalAndServerScore(level_nr, TRUE); + + DrawHallOfFame_setScoreEntries(); + + if (game_status == GAME_MODE_SCORES) { - // reload scores, using updated server score cache file - LoadLocalAndServerScore(level_nr, FALSE); + // force remapping optional gadgets (especially scroll bar) + UnmapScreenTreeGadgets(); - server_scores.updated = FALSE; + // redraw complete high score screen, as sub-title has changed + ClearField(); + + // redraw level selection buttons (which have just been erased) + RedrawScreenMenuGadgets(SCREEN_MASK_SCORES); + + HandleChooseTree(0, 0, 0, 0, MB_MENU_INITIALIZE, &score_entry_current); + } + else + { + DrawScoreInfo_Content(scores.last_entry_nr); } - first_entry = getHallOfFameFirstEntry(0, 0); + SaveLevelSetup_SeriesInfo(); + } +} + +void HandleHallOfFame(int mx, int my, int dx, int dy, int button) +{ + HandleChooseTree(mx, my, dx, dy, button, &score_entry_current); +} + +static void DrawScoreInfo_Content(int entry_nr) +{ + struct ScoreEntry *entry = &scores.entry[entry_nr]; + char *pos_text = getHallOfFameRankText(entry_nr, 0); + char *tape_date = getHallOfFameTapeDateText(entry); + int font_title = MENU_INFO_FONT_TITLE; + int font_head = MENU_INFO_FONT_HEAD; + int font_text = MENU_INFO_FONT_TEXT; + int font_foot = MENU_INFO_FONT_FOOT; + int spacing_title = menu.headline1_spacing[GAME_MODE_SCOREINFO]; + int spacing_para = menu.paragraph_spacing[GAME_MODE_SCOREINFO]; + int spacing_line = menu.line_spacing[GAME_MODE_SCOREINFO]; + int xstep = getFontWidth(font_text); + int ystep_title = getMenuTextStep(spacing_title, font_title); + int ystep_para = getMenuTextStep(spacing_para, font_text); + int ystep_line = getMenuTextStep(spacing_line, font_text); + int xstart = mSX - SX + menu.left_spacing[GAME_MODE_SCOREINFO]; + int ystart = mSY - SY + menu.top_spacing[GAME_MODE_SCOREINFO]; + int ybottom = mSY - SY + SYSIZE - menu.bottom_spacing[GAME_MODE_SCOREINFO]; + int xstart1 = xstart + xstep; + int xstart2 = xstart + xstep * 12; + int select_x = SX + xstart1; + int select_y1, select_y2; + int play_x, play_y; + int play_height = screen_gadget[SCREEN_CTRL_ID_PLAY_TAPE]->height; + boolean play_visible = !strEqual(tape_date, UNKNOWN_NAME); + int font_width = getFontWidth(font_text); + int font_height = getFontHeight(font_text); + int tape_date_width = getTextWidth(tape_date, font_text); + int pad_left = xstart2; + int pad_right = menu.right_spacing[GAME_MODE_SCOREINFO]; + int max_chars_per_line = (SXSIZE - pad_left - pad_right) / font_width; + int max_lines_per_text = 5; + int lines; + + ClearField(); + + // redraw level selection buttons (which have just been erased) + RedrawScreenMenuGadgets(SCREEN_MASK_SCORES); - drawHallOfFameList(level_nr, first_entry); + if (score_entries == NULL) + { + drawChooseTreeScreen_Scores_NotAvailable(); return; } - if (ABS(dy) == SCROLL_PAGE) // handle scrolling one page - step = NUM_MENU_ENTRIES_ON_SCREEN - 1; + drawChooseTreeHead(score_entries); + drawChooseTreeInfo(score_entries); - if (dy < 0) - { - first_entry = getHallOfFameFirstEntry(first_entry, -step); + DrawTextSCentered(ystart, font_title, "Score Information:"); + ystart += ystep_title; + + DrawTextF(xstart1, ystart, font_head, "Level Set"); + lines = DrawTextBufferS(xstart2, ystart, leveldir_current->name, font_text, + max_chars_per_line, -1, max_lines_per_text, 0, -1, + TRUE, FALSE, FALSE); + ystart += ystep_line + (lines > 0 ? lines - 1 : 0) * font_height; + + DrawTextF(xstart1, ystart, font_head, "Level"); + lines = DrawTextBufferS(xstart2, ystart, level.name, font_text, + max_chars_per_line, -1, max_lines_per_text, 0, -1, + TRUE, FALSE, FALSE); + ystart += ystep_para + (lines > 0 ? lines - 1 : 0) * font_height; + + select_y1 = SY + ystart; + ystart += graphic_info[IMG_MENU_BUTTON_PREV_SCORE].height; - drawHallOfFameList(level_nr, first_entry); + DrawTextF(xstart1, ystart, font_head, "Rank"); + DrawTextF(xstart2, ystart, font_text, pos_text); + ystart += ystep_line; + + DrawTextF(xstart1, ystart, font_head, "Player"); + DrawTextF(xstart2, ystart, font_text, entry->name); + ystart += ystep_line; + + if (level.use_step_counter) + { + DrawTextF(xstart1, ystart, font_head, "Steps"); + DrawTextF(xstart2, ystart, font_text, int2str(entry->time, 5)); + ystart += ystep_line; } - else if (dy > 0) + else { - first_entry = getHallOfFameFirstEntry(first_entry, step); + DrawTextF(xstart1, ystart, font_head, "Time"); + DrawTextF(xstart2, ystart, font_text, getHallOfFameTimeText(entry_nr)); + ystart += ystep_line; + } - drawHallOfFameList(level_nr, first_entry); + if (!level.rate_time_over_score || entry->score > 0) + { + DrawTextF(xstart1, ystart, font_head, "Score"); + DrawTextF(xstart2, ystart, font_text, int2str(entry->score, 5)); + ystart += ystep_line; } - else if (button == MB_MENU_LEAVE || button == MB_MENU_CHOICE) + + ystart += ystep_line; + + play_x = SX + xstart2 + tape_date_width + font_width; + play_y = SY + ystart + (font_height - play_height) / 2; + + DrawTextF(xstart1, ystart, font_head, "Tape Date"); + DrawTextF(xstart2, ystart, font_text, tape_date); + ystart += ystep_line; + + DrawTextF(xstart1, ystart, font_head, "Platform"); + DrawTextF(xstart2, ystart, font_text, entry->platform); + ystart += ystep_line; + + DrawTextF(xstart1, ystart, font_head, "Version"); + DrawTextF(xstart2, ystart, font_text, entry->version); + ystart += ystep_line; + + DrawTextF(xstart1, ystart, font_head, "Country"); + lines = DrawTextBufferS(xstart2, ystart, entry->country_name, font_text, + max_chars_per_line, -1, max_lines_per_text, 0, -1, + TRUE, FALSE, FALSE); + ystart += ystep_line; + + select_y2 = SY + ystart; + + DrawTextSCentered(ybottom, font_foot, "Press any key or button to go back"); + + AdjustScoreInfoButtons_SelectScore(select_x, select_y1, select_y2); + AdjustScoreInfoButtons_PlayTape(play_x, play_y, play_visible); +} + +static void DrawScoreInfo(int entry_nr) +{ + scores.last_entry_nr = entry_nr; + score_info_tape_play = FALSE; + + UnmapAllGadgets(); + + FreeScreenGadgets(); + CreateScreenGadgets(); + + FadeOut(REDRAW_FIELD); + + // needed if different viewport properties defined after playing score tape + ChangeViewportPropertiesIfNeeded(); + + // set this after "ChangeViewportPropertiesIfNeeded()" (which may reset it) + SetDrawDeactivationMask(REDRAW_NONE); + SetDrawBackgroundMask(REDRAW_FIELD); + + // needed if different background image defined after playing score tape + SetMainBackgroundImage(IMG_BACKGROUND_SCORES); + SetMainBackgroundImageIfDefined(IMG_BACKGROUND_SCOREINFO); + + // special compatibility handling for "Snake Bite" graphics set + if (strPrefix(leveldir_current->identifier, "snake_bite")) + ClearRectangle(gfx.background_bitmap, gfx.real_sx, gfx.real_sy + 64, + gfx.full_sxsize, gfx.full_sysize - 64); + + DrawScoreInfo_Content(entry_nr); + + // map gadgets for score info screen + MapScreenMenuGadgets(SCREEN_MASK_SCORES_INFO); + + FadeIn(REDRAW_FIELD); +} + +static void HandleScoreInfo_SelectScore(int step, int direction) +{ + int old_entry_nr = scores.last_entry_nr; + int new_entry_nr = old_entry_nr + step * direction; + int num_nodes = numTreeInfoInGroup(score_entry_current); + int num_entries = num_nodes - 1; // score nodes only, without back link + + if (new_entry_nr < 0) + new_entry_nr = 0; + if (new_entry_nr > num_entries - 1) + new_entry_nr = num_entries - 1; + + if (new_entry_nr != old_entry_nr) { - PlaySound(SND_MENU_ITEM_SELECTING); + scores.last_entry_nr = new_entry_nr; - FadeSound(SND_BACKGROUND_SCORES); + DrawScoreInfo_Content(new_entry_nr); + } +} - if (button == MB_MENU_CHOICE && - game_status_last_screen == GAME_MODE_PLAYING && - setup.auto_play_next_level && setup.increment_levels && - level_nr < leveldir_current->last_level && - !network_playing) - { - StartGameActions(network.enabled, setup.autorecord, level.random_seed); - } - else - { - SetGameStatus(GAME_MODE_MAIN); +static void HandleScoreInfo_PlayTape(void) +{ + if (!PlayScoreTape(scores.last_entry_nr)) + { + DrawScoreInfo_Content(scores.last_entry_nr); - DrawMainMenu(); - } + FadeIn(REDRAW_FIELD); } - else if (server_scores.updated) +} + +void HandleScoreInfo(int mx, int my, int dx, int dy, int button) +{ + boolean button_action = (button == MB_MENU_LEAVE || button == MB_MENU_CHOICE); + boolean button_is_valid = (mx >= 0 && my >= 0); + boolean button_screen_clicked = (button_action && button_is_valid); + + if (server_scores.updated) { // reload scores, using updated server score cache file - LoadLocalAndServerScore(level_nr, FALSE); + LoadLocalAndServerScore(scores.last_level_nr, FALSE); server_scores.updated = FALSE; - first_entry = getHallOfFameFirstEntry(0, 0); + DrawHallOfFame_setScoreEntries(); - drawHallOfFameList(level_nr, first_entry); + DrawScoreInfo_Content(scores.last_entry_nr); } - if (game_status == GAME_MODE_SCORES) - PlayMenuSoundIfLoop(); + if (button_screen_clicked) + { + PlaySound(SND_MENU_ITEM_SELECTING); + + SetGameStatus(GAME_MODE_SCORES); + + DrawHallOfFame(scores.last_level_nr); + } + else if (dx) + { + HandleHallOfFame_SelectLevel(1, SIGN(dx) * (ABS(dx) > 1 ? 10 : 1)); + } + else if (dy) + { + HandleScoreInfo_SelectScore(1, SIGN(dy) * (ABS(dy) > 1 ? 10 : 1)); + } } @@ -7258,6 +7246,15 @@ static struct { &setup.internal.menu_exit, execExitSetup }, { &setup.internal.menu_save_and_exit, execSaveAndExitSetup }, + { &setup.internal.info_title, execInfoTitleScreen }, + { &setup.internal.info_elements, execInfoElements }, + { &setup.internal.info_music, execInfoMusic }, + { &setup.internal.info_credits, execInfoCredits }, + { &setup.internal.info_program, execInfoProgram }, + { &setup.internal.info_version, execInfoVersion }, + { &setup.internal.info_levelset, execInfoLevelSet }, + { &setup.internal.info_exit, execExitInfo }, + { NULL, NULL } }; @@ -7302,8 +7299,8 @@ static struct TokenInfo setup_info_game[] = { TYPE_SWITCH, &setup.multiple_users, "Multiple Users/Teams:" }, { TYPE_YES_NO, &setup.input_on_focus, "Only Move Focussed Player:" }, { TYPE_SWITCH, &setup.time_limit, "Time Limit:" }, - { TYPE_SWITCH, &setup.handicap, "Handicap:" }, - { TYPE_SWITCH, &setup.skip_levels, "Skip Unsolved Levels:" }, + { TYPE_SWITCH, &setup.handicap, "Force Solving Levels:" }, + { TYPE_SWITCH, &setup.skip_levels, "Allow Skipping Levels:" }, { TYPE_SWITCH, &setup.increment_levels,"Increment Solved Levels:" }, { TYPE_SWITCH, &setup.auto_play_next_level,"Auto-play Next Level:" }, { TYPE_SWITCH, &setup.count_score_after_game,"Count Score After Game:" }, @@ -7772,10 +7769,10 @@ static void drawSetupValue(int screen_pos, int setup_info_pos_raw) if (scrollbar_needed && xpos > MENU_SCREEN_START_XPOS) { int max_menu_text_length = 26; // maximum text length for classic menu - int font_xoffset = getFontBitmapInfo(font_nr)->draw_xoffset; + int font_xoffset = getFontDrawOffsetX(font_nr); int text_startx = mSX + MENU_SCREEN_START_XPOS * 32; int text_font_nr = getMenuTextFont(FONT_MENU_2); - int text_font_xoffset = getFontBitmapInfo(text_font_nr)->draw_xoffset; + int text_font_xoffset = getFontDrawOffsetX(text_font_nr); int text_width = max_menu_text_length * getFontWidth(text_font_nr); if (startx + font_xoffset < text_startx + text_width + text_font_xoffset) @@ -7798,11 +7795,11 @@ static void drawSetupValue(int screen_pos, int setup_info_pos_raw) MENU_SCREEN_START_XPOS); int max_menu_text_length_medium = max_menu_text_length_big * 2; int check_font_nr = FONT_OPTION_ON; // known font that needs correction - int font1_xoffset = getFontBitmapInfo(font_nr)->draw_xoffset; - int font2_xoffset = getFontBitmapInfo(check_font_nr)->draw_xoffset; + int font1_xoffset = getFontDrawOffsetX(font_nr); + int font2_xoffset = getFontDrawOffsetX(check_font_nr); int text_startx = mSX + MENU_SCREEN_START_XPOS * 32; int text_font_nr = getMenuTextFont(FONT_MENU_2); - int text_font_xoffset = getFontBitmapInfo(text_font_nr)->draw_xoffset; + int text_font_xoffset = getFontDrawOffsetX(text_font_nr); int text_width = max_menu_text_length_medium * getFontWidth(text_font_nr); boolean correct_font_draw_xoffset = FALSE; @@ -7818,7 +7815,7 @@ static void drawSetupValue(int screen_pos, int setup_info_pos_raw) // (this can happen for extreme/wrong values for font draw offset) if (correct_font_draw_xoffset) { - font_draw_xoffset_old = getFontBitmapInfo(font_nr)->draw_xoffset; + font_draw_xoffset_old = getFontDrawOffsetX(font_nr); font_draw_xoffset_modified = TRUE; if (type & TYPE_KEY) @@ -9509,6 +9506,46 @@ static struct GD_EVENT_PRESSED | GD_EVENT_REPEATED, FALSE, "next level" }, + { + IMG_MENU_BUTTON_PREV_LEVEL2, IMG_MENU_BUTTON_PREV_LEVEL2_ACTIVE, + &menu.scores.button.prev_level, NULL, + SCREEN_CTRL_ID_PREV_LEVEL2, + SCREEN_MASK_SCORES | SCREEN_MASK_SCORES_INFO, + GD_EVENT_PRESSED | GD_EVENT_REPEATED, + FALSE, "previous level" + }, + { + IMG_MENU_BUTTON_NEXT_LEVEL2, IMG_MENU_BUTTON_NEXT_LEVEL2_ACTIVE, + &menu.scores.button.next_level, NULL, + SCREEN_CTRL_ID_NEXT_LEVEL2, + SCREEN_MASK_SCORES | SCREEN_MASK_SCORES_INFO, + GD_EVENT_PRESSED | GD_EVENT_REPEATED, + FALSE, "next level" + }, + { + IMG_MENU_BUTTON_PREV_SCORE, IMG_MENU_BUTTON_PREV_SCORE_ACTIVE, + &menu.scores.button.prev_score, NULL, + SCREEN_CTRL_ID_PREV_SCORE, + SCREEN_MASK_SCORES_INFO, + GD_EVENT_PRESSED | GD_EVENT_REPEATED, + FALSE, "previous score" + }, + { + IMG_MENU_BUTTON_NEXT_SCORE, IMG_MENU_BUTTON_NEXT_SCORE_ACTIVE, + &menu.scores.button.next_score, NULL, + SCREEN_CTRL_ID_NEXT_SCORE, + SCREEN_MASK_SCORES_INFO, + GD_EVENT_PRESSED | GD_EVENT_REPEATED, + FALSE, "next score" + }, + { + IMG_MENU_BUTTON_PLAY_TAPE, IMG_MENU_BUTTON_PLAY_TAPE, + &menu.scores.button.play_tape, NULL, + SCREEN_CTRL_ID_PLAY_TAPE, + SCREEN_MASK_SCORES_INFO, + GD_EVENT_RELEASED, + FALSE, "play tape" + }, { IMG_MENU_BUTTON_FIRST_LEVEL, IMG_MENU_BUTTON_FIRST_LEVEL_ACTIVE, &menu.main.button.first_level, NULL, @@ -9678,8 +9715,12 @@ static void CreateScreenMenubuttons(void) for (i = 0; i < NUM_SCREEN_MENUBUTTONS; i++) { struct MenuPosInfo *pos = menubutton_info[i].pos; + int screen_mask = menubutton_info[i].screen_mask; boolean is_touch_button = menubutton_info[i].is_touch_button; boolean is_check_button = menubutton_info[i].check_value != NULL; + boolean is_score_button = (screen_mask & SCREEN_MASK_SCORES_INFO); + boolean has_gfx_pressed = (menubutton_info[i].gfx_pressed == + menubutton_info[i].gfx_unpressed); Bitmap *gd_bitmap_unpressed, *gd_bitmap_pressed; int gfx_unpressed, gfx_pressed; int x, y, width, height; @@ -9710,7 +9751,7 @@ static void CreateScreenMenubuttons(void) gd_x2a = gd_x2; gd_y2a = gd_y2; - if (is_touch_button) + if (has_gfx_pressed) { gd_x2 += graphic_info[gfx_pressed].pressed_xoffset; gd_y2 += graphic_info[gfx_pressed].pressed_yoffset; @@ -9727,6 +9768,28 @@ static void CreateScreenMenubuttons(void) checked = *menubutton_info[i].check_value; } + if (is_score_button) + { + // if x/y set to -1, dynamically place buttons next to title text + int title_width = getTextWidth(INFOTEXT_SCORE_ENTRY, FONT_TITLE_1); + + // special compatibility handling for "Snake Bite" graphics set + if (strPrefix(leveldir_current->identifier, "snake_bite")) + title_width = strlen(INFOTEXT_SCORE_ENTRY) * 32; + + // use "SX" here to center buttons (ignore horizontal draw offset) + if (pos->x == -1) + x = (id == SCREEN_CTRL_ID_PREV_LEVEL2 ? + SX + (SXSIZE - title_width) / 2 - width * 3 / 2 : + id == SCREEN_CTRL_ID_NEXT_LEVEL2 ? + SX + (SXSIZE + title_width) / 2 + width / 2 : 0); + + // use "mSY" here to place buttons (respect vertical draw offset) + if (pos->y == -1) + y = (id == SCREEN_CTRL_ID_PREV_LEVEL2 || + id == SCREEN_CTRL_ID_NEXT_LEVEL2 ? mSY + MENU_TITLE1_YPOS : 0); + } + gi = CreateGadget(GDI_CUSTOM_ID, id, GDI_CUSTOM_TYPE_ID, i, GDI_IMAGE_ID, gfx_unpressed, @@ -9968,6 +10031,15 @@ void FreeScreenGadgets(void) FreeGadget(screen_gadget[i]); } +static void RedrawScreenMenuGadgets(int screen_mask) +{ + int i; + + for (i = 0; i < NUM_SCREEN_MENUBUTTONS; i++) + if (screen_mask & menubutton_info[i].screen_mask) + RedrawGadget(screen_gadget[menubutton_info[i].gadget_id]); +} + static void MapScreenMenuGadgets(int screen_mask) { int i; @@ -10018,11 +10090,54 @@ static void MapScreenGadgets(int num_entries) MapGadget(screen_gadget[scrollbar_info[i].gadget_id]); } +static void UnmapScreenGadgets() +{ + int i; + + for (i = 0; i < NUM_SCREEN_SCROLLBUTTONS; i++) + UnmapGadget(screen_gadget[scrollbutton_info[i].gadget_id]); + + for (i = 0; i < NUM_SCREEN_SCROLLBARS; i++) + UnmapGadget(screen_gadget[scrollbar_info[i].gadget_id]); +} + static void MapScreenTreeGadgets(TreeInfo *ti) { MapScreenGadgets(numTreeInfoInGroup(ti)); } +static void UnmapScreenTreeGadgets(void) +{ + UnmapScreenGadgets(); +} + +static void AdjustScoreInfoButtons_SelectScore(int x, int y1, int y2) +{ + struct GadgetInfo *gi_1 = screen_gadget[SCREEN_CTRL_ID_PREV_SCORE]; + struct GadgetInfo *gi_2 = screen_gadget[SCREEN_CTRL_ID_NEXT_SCORE]; + struct MenuPosInfo *pos_1 = menubutton_info[SCREEN_CTRL_ID_PREV_SCORE].pos; + struct MenuPosInfo *pos_2 = menubutton_info[SCREEN_CTRL_ID_NEXT_SCORE].pos; + + if (pos_1->x == -1 && pos_1->y == -1) + ModifyGadget(gi_1, GDI_X, x, GDI_Y, y1, GDI_END); + + if (pos_2->x == -1 && pos_2->y == -1) + ModifyGadget(gi_2, GDI_X, x, GDI_Y, y2, GDI_END); +} + +static void AdjustScoreInfoButtons_PlayTape(int x, int y, boolean visible) +{ + struct GadgetInfo *gi = screen_gadget[SCREEN_CTRL_ID_PLAY_TAPE]; + struct MenuPosInfo *pos = menubutton_info[SCREEN_CTRL_ID_PLAY_TAPE].pos; + + // set gadget position dynamically, pre-defined or off-screen + int xx = (visible ? (pos->x == -1 ? x : pos->x) : POS_OFFSCREEN); + int yy = (visible ? (pos->y == -1 ? y : pos->y) : POS_OFFSCREEN); + + ModifyGadget(gi, GDI_X, xx, GDI_Y, yy, GDI_END); + MapGadget(gi); // (needed if deactivated on last score page) +} + static void HandleScreenGadgets(struct GadgetInfo *gi) { int id = gi->custom_id; @@ -10041,6 +10156,26 @@ static void HandleScreenGadgets(struct GadgetInfo *gi) HandleMainMenu_SelectLevel(step, +1, NO_DIRECT_LEVEL_SELECT); break; + case SCREEN_CTRL_ID_PREV_LEVEL2: + HandleHallOfFame_SelectLevel(step, -1); + break; + + case SCREEN_CTRL_ID_NEXT_LEVEL2: + HandleHallOfFame_SelectLevel(step, +1); + break; + + case SCREEN_CTRL_ID_PREV_SCORE: + HandleScoreInfo_SelectScore(step, -1); + break; + + case SCREEN_CTRL_ID_NEXT_SCORE: + HandleScoreInfo_SelectScore(step, +1); + break; + + case SCREEN_CTRL_ID_PLAY_TAPE: + HandleScoreInfo_PlayTape(); + break; + case SCREEN_CTRL_ID_FIRST_LEVEL: HandleMainMenu_SelectLevel(MAX_LEVELS, -1, NO_DIRECT_LEVEL_SELECT); break; @@ -10094,6 +10229,8 @@ static void HandleScreenGadgets(struct GadgetInfo *gi) HandleSetupScreen(0,0, 0, -1 * SCROLL_LINE, MB_MENU_MARK); else if (game_status == GAME_MODE_INFO) HandleInfoScreen(0,0, 0, -1 * SCROLL_LINE, MB_MENU_MARK); + else if (game_status == GAME_MODE_SCORES) + HandleHallOfFame(0,0, 0, -1 * SCROLL_LINE, MB_MENU_MARK); break; case SCREEN_CTRL_ID_SCROLL_DOWN: @@ -10107,6 +10244,8 @@ static void HandleScreenGadgets(struct GadgetInfo *gi) HandleSetupScreen(0,0, 0, +1 * SCROLL_LINE, MB_MENU_MARK); else if (game_status == GAME_MODE_INFO) HandleInfoScreen(0,0, 0, +1 * SCROLL_LINE, MB_MENU_MARK); + else if (game_status == GAME_MODE_SCORES) + HandleHallOfFame(0,0, 0, +1 * SCROLL_LINE, MB_MENU_MARK); break; case SCREEN_CTRL_ID_SCROLL_VERTICAL: @@ -10120,6 +10259,8 @@ static void HandleScreenGadgets(struct GadgetInfo *gi) HandleSetupScreen(0,0, 999,gi->event.item_position,MB_MENU_INITIALIZE); else if (game_status == GAME_MODE_INFO) HandleInfoScreen(0,0, 999,gi->event.item_position,MB_MENU_INITIALIZE); + else if (game_status == GAME_MODE_SCORES) + HandleHallOfFame(0,0, 999,gi->event.item_position,MB_MENU_INITIALIZE); break; case SCREEN_CTRL_ID_NETWORK_SERVER: @@ -10152,6 +10293,12 @@ static void HandleScreenGadgets(struct GadgetInfo *gi) } } +void HandleScreenGadgetKeys(Key key) +{ + if (key == setup.shortcut.tape_play) + HandleScreenGadgets(screen_gadget[SCREEN_CTRL_ID_PLAY_TAPE]); +} + void DumpScreenIdentifiers(void) { int i;