X-Git-Url: https://git.artsoft.org/?a=blobdiff_plain;f=src%2Fscreens.c;h=62eb2979cd59bbc752c5ff95d8e1e8f206ab9c63;hb=9e57366e6032f1c142eb712ece9b46067c159299;hp=369124a77f917106b90e39b64d3d69e5902298d7;hpb=f2b5c31430a9feeb0d7bb1aa9e21c2f8a230f548;p=rocksndiamonds.git diff --git a/src/screens.c b/src/screens.c index 369124a7..62eb2979 100644 --- a/src/screens.c +++ b/src/screens.c @@ -63,30 +63,31 @@ #define SETUP_MODE_CHOOSE_OTHER 16 // sub-screens on the setup screen (specific) -#define SETUP_MODE_CHOOSE_GAME_SPEED 17 -#define SETUP_MODE_CHOOSE_SCROLL_DELAY 18 -#define SETUP_MODE_CHOOSE_SNAPSHOT_MODE 19 -#define SETUP_MODE_CHOOSE_WINDOW_SIZE 20 -#define SETUP_MODE_CHOOSE_SCALING_TYPE 21 -#define SETUP_MODE_CHOOSE_RENDERING 22 -#define SETUP_MODE_CHOOSE_VSYNC 23 -#define SETUP_MODE_CHOOSE_GRAPHICS 24 -#define SETUP_MODE_CHOOSE_SOUNDS 25 -#define SETUP_MODE_CHOOSE_MUSIC 26 -#define SETUP_MODE_CHOOSE_VOLUME_SIMPLE 27 -#define SETUP_MODE_CHOOSE_VOLUME_LOOPS 28 -#define SETUP_MODE_CHOOSE_VOLUME_MUSIC 29 -#define SETUP_MODE_CHOOSE_TOUCH_CONTROL 30 -#define SETUP_MODE_CHOOSE_MOVE_DISTANCE 31 -#define SETUP_MODE_CHOOSE_DROP_DISTANCE 32 -#define SETUP_MODE_CHOOSE_TRANSPARENCY 33 -#define SETUP_MODE_CHOOSE_GRID_XSIZE_0 34 -#define SETUP_MODE_CHOOSE_GRID_YSIZE_0 35 -#define SETUP_MODE_CHOOSE_GRID_XSIZE_1 36 -#define SETUP_MODE_CHOOSE_GRID_YSIZE_1 37 -#define SETUP_MODE_CONFIG_VIRT_BUTTONS 38 - -#define MAX_SETUP_MODES 39 +#define SETUP_MODE_CHOOSE_SCORES_TYPE 17 +#define SETUP_MODE_CHOOSE_GAME_SPEED 18 +#define SETUP_MODE_CHOOSE_SCROLL_DELAY 19 +#define SETUP_MODE_CHOOSE_SNAPSHOT_MODE 20 +#define SETUP_MODE_CHOOSE_WINDOW_SIZE 21 +#define SETUP_MODE_CHOOSE_SCALING_TYPE 22 +#define SETUP_MODE_CHOOSE_RENDERING 23 +#define SETUP_MODE_CHOOSE_VSYNC 24 +#define SETUP_MODE_CHOOSE_GRAPHICS 25 +#define SETUP_MODE_CHOOSE_SOUNDS 26 +#define SETUP_MODE_CHOOSE_MUSIC 27 +#define SETUP_MODE_CHOOSE_VOLUME_SIMPLE 28 +#define SETUP_MODE_CHOOSE_VOLUME_LOOPS 29 +#define SETUP_MODE_CHOOSE_VOLUME_MUSIC 30 +#define SETUP_MODE_CHOOSE_TOUCH_CONTROL 31 +#define SETUP_MODE_CHOOSE_MOVE_DISTANCE 32 +#define SETUP_MODE_CHOOSE_DROP_DISTANCE 33 +#define SETUP_MODE_CHOOSE_TRANSPARENCY 34 +#define SETUP_MODE_CHOOSE_GRID_XSIZE_0 35 +#define SETUP_MODE_CHOOSE_GRID_YSIZE_0 36 +#define SETUP_MODE_CHOOSE_GRID_XSIZE_1 37 +#define SETUP_MODE_CHOOSE_GRID_YSIZE_1 38 +#define SETUP_MODE_CONFIG_VIRT_BUTTONS 39 + +#define MAX_SETUP_MODES 40 #define MAX_MENU_MODES MAX(MAX_INFO_MODES, MAX_SETUP_MODES) @@ -104,6 +105,7 @@ #define STR_SETUP_EXIT "Exit" #define STR_SETUP_SAVE_AND_EXIT "Save and Exit" +#define STR_SETUP_CHOOSE_SCORES_TYPE "Scores Type" #define STR_SETUP_CHOOSE_GAME_SPEED "Game Speed" #define STR_SETUP_CHOOSE_SCROLL_DELAY "Scroll Delay" #define STR_SETUP_CHOOSE_SNAPSHOT_MODE "Snapshot Mode" @@ -275,16 +277,22 @@ 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 boolean OfferUploadTapes(void); static void execOfferUploadTapes(void); +static void DrawHallOfFame_setScoreEntries(void); +static char *getHallOfFameScoreText(int); + static struct GadgetInfo *screen_gadget[NUM_SCREEN_GADGETS]; static int info_mode = INFO_MODE_MAIN; @@ -308,6 +316,9 @@ static TreeInfo *scroll_delay_current = NULL; static TreeInfo *snapshot_modes = NULL; static TreeInfo *snapshot_mode_current = NULL; +static TreeInfo *scores_types = NULL; +static TreeInfo *scores_type_current = NULL; + static TreeInfo *game_speeds_normal = NULL; static TreeInfo *game_speeds_extended = NULL; static TreeInfo *game_speeds = NULL; @@ -343,6 +354,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 %" }, @@ -392,6 +406,15 @@ static struct StringValueTextInfo vsync_modes_list[] = { NULL, NULL }, }; +static struct StringValueTextInfo scores_types_list[] = +{ + { STR_SCORES_TYPE_LOCAL_ONLY, "Local scores only" }, + { STR_SCORES_TYPE_SERVER_ONLY, "Server scores only" }, + { STR_SCORES_TYPE_LOCAL_AND_SERVER, "Local and server scores" }, + + { NULL, NULL }, +}; + static struct ValueTextInfo game_speeds_list_normal[] = { { 30, "Very Slow" }, @@ -590,6 +613,8 @@ 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 DRAW_XOFFSET(s) ((s) == GAME_MODE_INFO ? \ DRAW_XOFFSET_INFO(info_mode) : \ (s) == GAME_MODE_SETUP ? \ @@ -604,6 +629,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)) @@ -1379,10 +1406,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) @@ -1743,6 +1770,9 @@ void DrawMainMenu(void) OpenDoor(DOOR_CLOSE_1 | DOOR_OPEN_2); SyncEmscriptenFilesystem(); + + // needed once after program start or after user change + CheckApiServerTasks(); } static void gotoTopLevelDir(void) @@ -2232,9 +2262,13 @@ void HandleMainMenu(int mx, int my, int dx, int dy, int button) SaveLevelSetup_LastSeries(); SaveLevelSetup_SeriesInfo(); +#if defined(PLATFORM_EMSCRIPTEN) + Request("Close the browser window to quit!", REQ_CONFIRM); +#else if (!setup.ask_on_quit_program || Request("Do you really want to quit?", REQ_ASK | REQ_STAY_CLOSED)) SetGameStatus(GAME_MODE_QUIT); +#endif } } } @@ -3238,7 +3272,7 @@ void HandleInfoScreen_Music(int button) { int sound = list->music; - if (sound_info[sound].loop) + if (IS_LOOP_SOUND(sound)) PlaySoundLoop(sound); else PlaySound(sound); @@ -3249,7 +3283,7 @@ void HandleInfoScreen_Music(int button) { int music = list->music; - if (music_info[music].loop) + if (IS_LOOP_MUSIC(music)) PlayMusicLoop(music); else PlayMusic(music); @@ -3317,7 +3351,7 @@ void HandleInfoScreen_Music(int button) FadeIn(REDRAW_FIELD); } - if (list != NULL && list->is_sound && sound_info[list->music].loop) + if (list != NULL && list->is_sound && IS_LOOP_SOUND(list->music)) PlaySoundLoop(list->music); } @@ -4039,14 +4073,42 @@ void HandleInfoScreen(int mx, int my, int dx, int dy, int button) // ============================================================================ -// change name functions +// rename player API functions // ============================================================================ -static void RenamePlayerOnServerExt(struct HttpRequest *request, - struct HttpResponse *response, - char *player_name_raw, - char *player_uuid_raw) +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 void FreeThreadData_ApiRenamePlayer(void *data_raw) +{ + struct ApiRenamePlayerThreadData *data = data_raw; + + checked_free(data->player_name); + checked_free(data->player_uuid); + checked_free(data); +} + +static boolean SetRequest_ApiRenamePlayer(struct HttpRequest *request, + void *data_raw) +{ + 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; @@ -4074,6 +4136,78 @@ static void RenamePlayerOnServerExt(struct HttpRequest *request, ConvertHttpRequestBodyToServerEncoding(request); + return TRUE; +} + +static void HandleResponse_ApiRenamePlayer(struct HttpResponse *response, + void *data_raw) +{ + // nothing to do here +} + +#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); + + if (response != NULL) + { + HandleResponse_ApiRenamePlayer(response, data_raw); + + checked_free(response); + } + else + { + Error("server response too large to handle (%d bytes)", size); + } + + FreeThreadData_ApiRenamePlayer(data_raw); +} + +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); + + FreeThreadData_ApiRenamePlayer(data_raw); +} + +static void Emscripten_ApiRenamePlayer_Progress(unsigned handle, void *data_raw, + int bytes, int size) +{ + // nothing to do here +} + +static void Emscripten_ApiRenamePlayer_HttpRequest(struct HttpRequest *request, + void *data_raw) +{ + if (!SetRequest_ApiRenamePlayer(request, data_raw)) + { + FreeThreadData_ApiRenamePlayer(data_raw); + + return; + } + + emscripten_async_wget2_data(request->uri, + request->method, + request->body, + data_raw, + TRUE, + Emscripten_ApiRenamePlayer_Loaded, + Emscripten_ApiRenamePlayer_Failed, + Emscripten_ApiRenamePlayer_Progress); +} + +#else + +static void ApiRenamePlayer_HttpRequestExt(struct HttpRequest *request, + struct HttpResponse *response, + void *data_raw) +{ + if (!SetRequest_ApiRenamePlayer(request, data_raw)) + return; + if (!DoHttpRequest(request, response)) { Error("HTTP request failed: %s", GetHttpError()); @@ -4089,49 +4223,258 @@ static void RenamePlayerOnServerExt(struct HttpRequest *request, return; } + + HandleResponse_ApiRenamePlayer(response, data_raw); } -static void RenamePlayerOnServer(char *player_name, char *player_uuid) +static void ApiRenamePlayer_HttpRequest(struct HttpRequest *request, + struct HttpResponse *response, + void *data_raw) +{ + ApiRenamePlayer_HttpRequestExt(request, response, data_raw); + + FreeThreadData_ApiRenamePlayer(data_raw); +} +#endif + +static int ApiRenamePlayerThread(void *data_raw) { struct HttpRequest *request = checked_calloc(sizeof(struct HttpRequest)); struct HttpResponse *response = checked_calloc(sizeof(struct HttpResponse)); - RenamePlayerOnServerExt(request, response, player_name, player_uuid); + 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"); } -struct RenamePlayerOnServerThreadData + +// ============================================================================ +// reset player UUID API functions +// ============================================================================ + +struct ApiResetUUIDThreadData { char *player_name; - char *player_uuid; + char *player_uuid_old; + char *player_uuid_new; }; -static int RenamePlayerOnServerThread(void *data_raw) +static void *CreateThreadData_ApiResetUUID(char *uuid_new) { - struct RenamePlayerOnServerThreadData *data = data_raw; + 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; +} - RenamePlayerOnServer(data->player_name, data->player_uuid); +static void FreeThreadData_ApiResetUUID(void *data_raw) +{ + struct ApiResetUUIDThreadData *data = data_raw; checked_free(data->player_name); - checked_free(data->player_uuid); + checked_free(data->player_uuid_old); + checked_free(data->player_uuid_new); checked_free(data); +} - return 0; +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 RenamePlayerOnServerAsThread(void) +static void HandleResponse_ApiResetUUID(struct HttpResponse *response, + void *data_raw) { - struct RenamePlayerOnServerThreadData *data = - checked_malloc(sizeof(struct RenamePlayerOnServerThreadData)); + struct ApiResetUUIDThreadData *data = data_raw; - data->player_name = getStringCopy(setup.player_name); - data->player_uuid = getStringCopy(setup.player_uuid); + // upgrade player UUID in server setup file + setup.player_uuid = getStringCopy(data->player_uuid_new); + setup.player_version = 2; - ExecuteAsThread(RenamePlayerOnServerThread, - "RenamePlayerOnServer", data, - "rename player on server"); + 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"); } @@ -4257,10 +4600,17 @@ static void setTypeNameValues(char *name, struct TextPosInfo *pos, // temporarily change active user to edited user user.nr = type_name_nr; - // load setup of edited user (unless creating user with current setup) - if (!create_user || - !Request("Use current setup values for the new player?", REQ_ASK)) + if (create_user && + Request("Use current setup values for the new player?", REQ_ASK)) + { + // use current setup values for new user, but create new player UUID + setup.player_uuid = getStringCopy(getUUID()); + } + else + { + // load setup for existing user (or start with defaults for new user) LoadSetup(); + } } char *setup_filename = getSetupFilename(); @@ -4273,7 +4623,7 @@ static void setTypeNameValues(char *name, struct TextPosInfo *pos, SaveSetup(); // change name of edited user on score server - RenamePlayerOnServerAsThread(); + ApiRenamePlayerAsThread(); if (game_status == GAME_MODE_PSEUDO_TYPENAMES || reset_setup) { @@ -4474,7 +4824,7 @@ static void DrawChooseTree(TreeInfo **ti_ptr) 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) { @@ -4495,9 +4845,12 @@ static void DrawChooseTree(TreeInfo **ti_ptr) FreeScreenGadgets(); CreateScreenGadgets(); + if (game_status != game_status_last_screen) + 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) @@ -4506,6 +4859,8 @@ 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(); @@ -4516,10 +4871,21 @@ static void DrawChooseTree(TreeInfo **ti_ptr) DrawMaskedBorder(fade_mask); + if (game_status != game_status_last_screen) + PlayMenuSoundsAndMusic(); + FadeIn(fade_mask); } -static void drawChooseTreeText(int y, boolean active, TreeInfo *ti) +static int getChooseTreeFont(TreeInfo *node, boolean active) +{ + 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(TreeInfo *ti, int y, boolean active) { int num_entries = numTreeInfoInGroup(ti); boolean scrollbar_needed = (num_entries > NUM_MENU_ENTRIES_ON_SCREEN); @@ -4529,38 +4895,89 @@ static void drawChooseTreeText(int y, boolean active, TreeInfo *ti) int entry_pos = first_entry + y; TreeInfo *node_first = getTreeInfoFirstGroupEntry(ti); TreeInfo *node = getTreeInfoFromPos(node_first, entry_pos); - int font_color = MENU_CHOOSE_TREE_COLOR(node, active); - int font_nr = MENU_CHOOSE_TREE_FONT(font_color); - int font_xoffset = getFontBitmapInfo(font_nr)->draw_xoffset; + int font_nr = getChooseTreeFont(node, active); + 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 = 3 * font_size_1; + int text_size_4 = 5 * font_size_4; + int border = amSX - SX + getFontDrawOffsetX(font_nr1); + int dx1 = 0; + int dx2 = text_size_1; + int dx3 = dx2 + font_size_1; + int dx4 = screen_width - startdx - 2 * border - text_size_4; + int num_dots = (dx4 - dx3) / font_size_3; + int startx1 = startx + dx1; + int startx2 = startx + dx2; + int startx3 = startx + dx3; + int startx4 = startx + dx4; + int pos = node->pos; + boolean forced = (scores.force_last_added && pos == scores.last_added); + char *pos_text = (forced ? "???" : int2str(pos + 1, 3)); + char *dot_text = "."; + int i; + + DrawText(startx1, starty, pos_text, font_nr1); + DrawText(startx2, starty, dot_text, font_nr1); + + for (i = 0; i < num_dots; i++) + DrawText(startx3 + i * font_size_3, starty, dot_text, font_nr3); + + if (!strEqual(scores.entry[pos].name, EMPTY_PLAYER_NAME)) + DrawText(startx3, starty, scores.entry[pos].name, font_nr2); - DrawText(startx, starty, buffer, font_nr); + DrawText(startx4, starty, getHallOfFameScoreText(pos), 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(); @@ -4572,7 +4989,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); @@ -4581,6 +4998,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); } @@ -4588,21 +5008,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); @@ -4623,27 +5048,127 @@ static void drawChooseTreeInfo(int entry_pos, TreeInfo *ti) MarkTileDirty(x, 1); } -static void drawChooseTreeCursorAndText(int y, boolean active, TreeInfo *ti) -{ - drawChooseTreeCursor(y, active); - drawChooseTreeText(y, active, ti); -} +static void drawChooseTreeCursorAndText(TreeInfo *ti, boolean active) +{ + drawChooseTreeCursor(ti->cl_cursor, active); + drawChooseTreeText(ti, ti->cl_cursor, active); +} + +static void drawChooseTreeScreen(TreeInfo *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) +{ + // set current tree entry to last added score entry + *ti_ptr = getTreeInfoFromIdentifier(score_entries, i_to_a(scores.last_added)); + + // 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); + + (*ti_ptr)->cl_first = pos_first; + (*ti_ptr)->cl_cursor = pos_score - pos_first; + + return *ti_ptr; +} + +static void HandleChooseTree(int mx, int my, int dx, int dy, int button, + TreeInfo **ti_ptr) +{ + TreeInfo *ti = *ti_ptr; + 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 x = 0; + 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); + + 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(); + + 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 == MB_MENU_LEAVE || button == MB_MENU_CHOICE) + { + PlaySound(SND_MENU_ITEM_SELECTING); + + SetGameStatus(GAME_MODE_MAIN); + + DrawMainMenu(); + } -static void HandleChooseTree(int mx, int my, int dx, int dy, int button, - TreeInfo **ti_ptr) -{ - TreeInfo *ti = *ti_ptr; - 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 x = 0; - int y = ti->cl_cursor; - 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); + return; + } + } if (button == MB_MENU_INITIALIZE) { @@ -4653,11 +5178,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 && @@ -4670,19 +5200,15 @@ static void HandleChooseTree(int mx, int my, int dx, int dy, int button, if (position_set_by_scrollbar) ti->cl_first = dy; - else - AdjustChooseTreeScrollbar(SCREEN_CTRL_ID_SCROLL_VERTICAL, - ti->cl_first, ti); - drawChooseTreeList(ti->cl_first, num_page_entries, ti); - drawChooseTreeInfo(ti->cl_first + ti->cl_cursor, ti); - drawChooseTreeCursorAndText(ti->cl_cursor, TRUE, ti); + drawChooseTreeScreen(ti); return; } else if (button == MB_MENU_LEAVE) { - FadeSetLeaveMenu(); + if (game_status != GAME_MODE_SCORES) + FadeSetLeaveMenu(); PlaySound(SND_MENU_ITEM_SELECTING); @@ -4693,7 +5219,8 @@ static void HandleChooseTree(int mx, int my, int dx, int dy, int button, } else if (game_status == GAME_MODE_SETUP) { - if (setup_mode == SETUP_MODE_CHOOSE_GAME_SPEED || + if (setup_mode == SETUP_MODE_CHOOSE_SCORES_TYPE || + setup_mode == SETUP_MODE_CHOOSE_GAME_SPEED || setup_mode == SETUP_MODE_CHOOSE_SCROLL_DELAY || setup_mode == SETUP_MODE_CHOOSE_SNAPSHOT_MODE) execSetupGame(); @@ -4783,14 +5310,7 @@ static void HandleChooseTree(int mx, int my, int dx, int dy, int button, } if (redraw) - { - drawChooseTreeList(ti->cl_first, num_page_entries, ti); - drawChooseTreeInfo(ti->cl_first + ti->cl_cursor, ti); - drawChooseTreeCursorAndText(ti->cl_cursor, TRUE, ti); - - AdjustChooseTreeScrollbar(SCREEN_CTRL_ID_SCROLL_VERTICAL, - ti->cl_first, ti); - } + drawChooseTreeScreen(ti); return; } @@ -4850,17 +5370,20 @@ 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) { if (game_status == GAME_MODE_SETUP) { - if (setup_mode == SETUP_MODE_CHOOSE_GAME_SPEED || + if (setup_mode == SETUP_MODE_CHOOSE_SCORES_TYPE || + setup_mode == SETUP_MODE_CHOOSE_GAME_SPEED || setup_mode == SETUP_MODE_CHOOSE_SCROLL_DELAY || setup_mode == SETUP_MODE_CHOOSE_SNAPSHOT_MODE) execSetupGame(); @@ -4908,14 +5431,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; @@ -4932,7 +5457,8 @@ static void HandleChooseTree(int mx, int my, int dx, int dy, int button, if (game_status == GAME_MODE_SETUP) { - if (setup_mode == SETUP_MODE_CHOOSE_GAME_SPEED || + if (setup_mode == SETUP_MODE_CHOOSE_SCORES_TYPE || + setup_mode == SETUP_MODE_CHOOSE_GAME_SPEED || setup_mode == SETUP_MODE_CHOOSE_SCROLL_DELAY || setup_mode == SETUP_MODE_CHOOSE_SNAPSHOT_MODE) execSetupGame(); @@ -5006,6 +5532,18 @@ 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 (game_status_last_screen == GAME_MODE_PLAYING && + setup.auto_play_next_level && setup.increment_levels && + scores.last_level_nr < leveldir_current->last_level && + !network_playing) + { + StartGameActions(network.enabled, setup.autorecord, + level.random_seed); + return; + } + } SetGameStatus(GAME_MODE_MAIN); @@ -5014,14 +5552,15 @@ static void HandleChooseTree(int mx, int my, int dx, int dy, int button, } } } + + if (game_status == GAME_MODE_SCORES) + PlayMenuSoundIfLoop(); } void DrawChoosePlayerName(void) { int i; - FadeMenuSoundsAndMusic(); - if (player_name != NULL) { freeTreeInfo(player_name); @@ -5061,8 +5600,6 @@ void DrawChoosePlayerName(void) player_name_current = player_name; DrawChooseTree(&player_name_current); - - PlayMenuSoundsAndMusic(); } void HandleChoosePlayerName(int mx, int my, int dx, int dy, int button) @@ -5072,11 +5609,7 @@ void HandleChoosePlayerName(int mx, int my, int dx, int dy, int button) void DrawChooseLevelSet(void) { - FadeMenuSoundsAndMusic(); - DrawChooseTree(&leveldir_current); - - PlayMenuSoundsAndMusic(); } void HandleChooseLevelSet(int mx, int my, int dx, int dy, int button) @@ -5088,8 +5621,6 @@ void DrawChooseLevelNr(void) { int i; - FadeMenuSoundsAndMusic(); - if (level_number != NULL) { freeTreeInfo(level_number); @@ -5135,8 +5666,6 @@ void DrawChooseLevelNr(void) level_number_current = level_number; DrawChooseTree(&level_number_current); - - PlayMenuSoundsAndMusic(); } void HandleChooseLevelNr(int mx, int my, int dx, int dy, int button) @@ -5144,59 +5673,82 @@ void HandleChooseLevelNr(int mx, int my, int dx, int dy, int button) HandleChooseTree(mx, my, dx, dy, button, &level_number_current); } -void DrawHallOfFame(int level_nr) +static void DrawHallOfFame_setScoreEntries(void) { - int fade_mask = REDRAW_FIELD; - - if (CheckFadeAll()) - fade_mask = REDRAW_ALL; + int score_pos = (scores.last_added >= 0 ? scores.last_added : 0); + int i; - UnmapAllGadgets(); - FadeMenuSoundsAndMusic(); + if (score_entries != NULL) + { + freeTreeInfo(score_entries); - // (this is needed when called from GameEnd() after winning a game) - KeyboardAutoRepeatOn(); + score_entries = NULL; + } - // (this is needed when called from GameEnd() after winning a game) - SetDrawDeactivationMask(REDRAW_NONE); - SetDrawBackgroundMask(REDRAW_FIELD); + for (i = 0; i < MAX_SCORE_ENTRIES; i++) + { + // do not add empty score entries + if (scores.entry[i].score == 0 && + scores.entry[i].time == 0) + break; - LoadLocalAndServerScore(level_nr, TRUE); + TreeInfo *ti = newTreeInfo_setDefaults(TREE_TYPE_SCORE_ENTRY); + char identifier[32], name[64]; + int value = i; - if (scores.last_added >= 0) - SetAnimStatus(GAME_MODE_PSEUDO_SCORESNEW); + ti->node_top = &score_entries; + ti->sort_priority = 10000 + value; + ti->color = FC_YELLOW; + ti->pos = i; - FadeSetEnterScreen(); + snprintf(identifier, sizeof(identifier), "%d", value); + snprintf(name, sizeof(name), "%03d.", value + 1); - FadeOut(fade_mask); + setString(&ti->identifier, identifier); + setString(&ti->name, name); + setString(&ti->name_sorting, name); - // needed if different viewport properties defined for scores - ChangeViewportPropertiesIfNeeded(); + pushTreeInfo(&score_entries, ti); + } - PlayMenuSoundsAndMusic(); + // sort score entries to start with highest score entry + sortTreeInfo(&score_entries); - OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW); + // add top tree node to create back link to main menu + score_entries = addTopTreeInfoNode(score_entries); - HandleHallOfFame(level_nr, 0, 0, 0, MB_MENU_INITIALIZE); + // set current score entry to last added or highest score entry + score_entry_current = + getTreeInfoFromIdentifier(score_entries, i_to_a(score_pos)); - DrawMaskedBorder(fade_mask); + // if that fails, set current score entry to first valid score entry + if (score_entry_current == NULL) + score_entry_current = getFirstValidTreeInfoEntry(score_entries); - FadeIn(fade_mask); + // ("score_entries" and "score_entry_current" may be NULL here) } -static int getHallOfFameFirstEntry(int first_entry, int step) +void DrawHallOfFame(int level_nr) { - if (step == 0) - first_entry = scores.last_added - (NUM_MENU_ENTRIES_ON_SCREEN + 1) / 2 + 1; - else - first_entry += step; + scores.last_level_nr = level_nr; - 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); + // (this is needed when called from GameEnd() after winning a game) + KeyboardAutoRepeatOn(); + + // (this is needed when called from GameEnd() after winning a game) + SetDrawDeactivationMask(REDRAW_NONE); + SetDrawBackgroundMask(REDRAW_FIELD); - return first_entry; + LoadLocalAndServerScore(level_nr, TRUE); + + DrawHallOfFame_setScoreEntries(); + + if (scores.last_added >= 0) + SetAnimStatus(GAME_MODE_PSEUDO_SCORESNEW); + + FadeSetEnterScreen(); + + DrawChooseTree(&score_entry_current); } static char *getHallOfFameScoreText(int nr) @@ -5217,124 +5769,9 @@ static char *getHallOfFameScoreText(int nr) return score_text; } -static void drawHallOfFameList(int level_nr, int first_entry) -{ - int i, j; - - SetMainBackgroundImage(IMG_BACKGROUND_SCORES); - ClearField(); - - DrawTextSCentered(MENU_TITLE1_YPOS, FONT_TITLE_1, "Hall Of Fame"); - DrawTextFCentered(MENU_TITLE2_YPOS, FONT_TITLE_2, - "HighScores of Level %d", level_nr); - - for (i = 0; i < NUM_MENU_ENTRIES_ON_SCREEN; i++) - { - 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); - - 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); - } - - redraw_mask |= REDRAW_FIELD; -} - void HandleHallOfFame(int mx, int my, int dx, int dy, int button) { - static int level_nr = 0; - static int first_entry = 0; - int step = (button == 1 ? 1 : button == 2 ? 5 : 10); - - if (button == MB_MENU_INITIALIZE) - { - level_nr = mx; - - if (server_scores.updated) - { - // reload scores, using updated server score cache file - LoadLocalAndServerScore(level_nr, FALSE); - - server_scores.updated = FALSE; - } - - first_entry = getHallOfFameFirstEntry(0, 0); - - drawHallOfFameList(level_nr, first_entry); - - return; - } - - if (ABS(dy) == SCROLL_PAGE) // handle scrolling one page - step = NUM_MENU_ENTRIES_ON_SCREEN - 1; - - if (dy < 0) - { - first_entry = getHallOfFameFirstEntry(first_entry, -step); - - drawHallOfFameList(level_nr, first_entry); - } - else if (dy > 0) - { - first_entry = getHallOfFameFirstEntry(first_entry, step); - - drawHallOfFameList(level_nr, first_entry); - } - else if (button == MB_MENU_LEAVE || button == MB_MENU_CHOICE) - { - PlaySound(SND_MENU_ITEM_SELECTING); - - FadeSound(SND_BACKGROUND_SCORES); - - 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); - - DrawMainMenu(); - } - } - else if (server_scores.updated) - { - // reload scores, using updated server score cache file - LoadLocalAndServerScore(level_nr, FALSE); - - server_scores.updated = FALSE; - - first_entry = getHallOfFameFirstEntry(0, 0); - - drawHallOfFameList(level_nr, first_entry); - } - - if (game_status == GAME_MODE_SCORES) - PlayMenuSoundIfLoop(); + HandleChooseTree(mx, my, dx, dy, button, &score_entry_current); } @@ -5353,6 +5790,7 @@ static char *vsync_mode_text; static char *scroll_delay_text; static char *snapshot_mode_text; static char *game_speed_text; +static char *scores_type_text; static char *network_server_text; static char *graphics_set_name; static char *sounds_set_name; @@ -5373,6 +5811,56 @@ static void execSetupMain(void) DrawSetupScreen(); } +static void execSetupGame_setScoresType(void) +{ + if (scores_types == NULL) + { + int i; + + for (i = 0; scores_types_list[i].value != NULL; i++) + { + TreeInfo *ti = newTreeInfo_setDefaults(TREE_TYPE_UNDEFINED); + char identifier[32], name[32]; + char *value = scores_types_list[i].value; + char *text = scores_types_list[i].text; + + ti->node_top = &scores_types; + ti->sort_priority = i; + + sprintf(identifier, "%s", value); + sprintf(name, "%s", text); + + setString(&ti->identifier, identifier); + setString(&ti->name, name); + setString(&ti->name_sorting, name); + setString(&ti->infotext, STR_SETUP_CHOOSE_SCORES_TYPE); + + pushTreeInfo(&scores_types, ti); + } + + // sort scores type values to start with lowest scores type value + sortTreeInfo(&scores_types); + + // set current scores type value to configured scores type value + scores_type_current = + getTreeInfoFromIdentifier(scores_types, setup.scores_in_highscore_list); + + // if that fails, set current scores type to reliable default value + if (scores_type_current == NULL) + scores_type_current = + getTreeInfoFromIdentifier(scores_types, STR_SCORES_TYPE_DEFAULT); + + // if that also fails, set current scores type to first available value + if (scores_type_current == NULL) + scores_type_current = scores_types; + } + + setup.scores_in_highscore_list = scores_type_current->identifier; + + // needed for displaying scores type text instead of identifier + scores_type_text = scores_type_current->name; +} + static void execSetupGame_setGameSpeeds(boolean update_value) { if (setup.game_speed_extended) @@ -5566,6 +6054,7 @@ static void execSetupGame(void) boolean check_vsync_mode = (setup_mode == SETUP_MODE_CHOOSE_GAME_SPEED); execSetupGame_setGameSpeeds(FALSE); + execSetupGame_setScoresType(); execSetupGame_setScrollDelays(); execSetupGame_setSnapshotModes(); @@ -5583,6 +6072,13 @@ static void execSetupGame(void) DisableVsyncIfNeeded(); } +static void execSetupChooseScoresType(void) +{ + setup_mode = SETUP_MODE_CHOOSE_SCORES_TYPE; + + DrawSetupScreen(); +} + static void execSetupChooseGameSpeed(void) { setup_mode = SETUP_MODE_CHOOSE_GAME_SPEED; @@ -6722,6 +7218,17 @@ static void ToggleGameSpeedsListIfNeeded(void) DrawSetupScreen(); } +static void ToggleUseApiServerIfNeeded(void) +{ + if (runtime.use_api_server == setup.use_api_server) + return; + + runtime.use_api_server = setup.use_api_server; + + if (runtime.use_api_server) + CheckApiServerTasks(); +} + static void ModifyGameSpeedIfNeeded(void) { if (strEqual(setup.vsync_mode, STR_VSYNC_MODE_OFF) || @@ -6769,6 +7276,9 @@ static struct void *related_value; } hide_related_entry_list[] = { + { &setup.scores_in_highscore_list, execSetupChooseScoresType }, + { &setup.scores_in_highscore_list, &scores_type_text }, + { &setup.game_frame_delay, execSetupChooseGameSpeed }, { &setup.game_frame_delay, &game_speed_text }, @@ -6882,8 +7392,9 @@ static struct TokenInfo setup_info_game[] = { TYPE_TEXT_INPUT, execGadgetNetworkServer, "Network Server Hostname:" }, { TYPE_STRING, &network_server_text, "" }, { TYPE_SWITCH, &setup.use_api_server, "Use Highscore Server:" }, - { TYPE_SWITCH, &setup.only_show_local_scores, "Only Show Local Scores:" }, - { TYPE_ENTER_LIST, execOfferUploadTapes, "Upload All Tapes to Server" }, + { TYPE_ENTER_LIST, execSetupChooseScoresType,"Scores in Highscore List:" }, + { TYPE_STRING, &scores_type_text, "" }, + { TYPE_ENTER_LIST, execOfferUploadTapes, "Upload Tapes to Server" }, { 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:" }, @@ -6897,6 +7408,7 @@ static struct TokenInfo setup_info_game[] = { TYPE_YES_NO, &setup.ask_on_quit_game, "Ask on Quit Game:" }, { TYPE_YES_NO, &setup.ask_on_quit_program, "Ask on Quit Program:" }, { TYPE_SWITCH, &setup.autorecord, "Auto-Record Tapes:" }, + { TYPE_SWITCH, &setup.auto_pause_on_start, "Start Game in Pause Mode:" }, { TYPE_ENTER_LIST, execSetupChooseGameSpeed, "Game Speed:" }, { TYPE_STRING, &game_speed_text, "" }, { TYPE_SWITCH, &setup.game_speed_extended, "Game Speed Extended List:" }, @@ -7144,6 +7656,10 @@ static struct TokenInfo setup_info_shortcuts_1[] = { TYPE_KEY, &setup.shortcut.save_game, "" }, { TYPE_KEYTEXT, NULL, "Quick Load Game from Tape:", }, { TYPE_KEY, &setup.shortcut.load_game, "" }, + { TYPE_KEYTEXT, NULL, "Restart Game:", }, + { TYPE_KEY, &setup.shortcut.restart_game, "" }, + { TYPE_KEYTEXT, NULL, "Replay & Pause Before End:", }, + { TYPE_KEY, &setup.shortcut.pause_before_end, "" }, { TYPE_KEYTEXT, NULL, "Start Game & Toggle Pause:", }, { TYPE_KEY, &setup.shortcut.toggle_pause, "" }, { TYPE_EMPTY, NULL, "" }, @@ -7352,10 +7868,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) @@ -7378,11 +7894,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; @@ -7398,7 +7914,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) @@ -7488,7 +8004,7 @@ static void changeSetupValue(int screen_pos, int setup_info_pos_raw, int dx) // API server mode may have changed at this point if (si->value == &setup.use_api_server) - runtime.use_api_server = setup.use_api_server; + ToggleUseApiServerIfNeeded(); // game speed list may have changed at this point if (si->value == &setup.game_speed_extended) @@ -8905,6 +9421,8 @@ void DrawSetupScreen(void) if (setup_mode == SETUP_MODE_INPUT) DrawSetupScreen_Input(); + else if (setup_mode == SETUP_MODE_CHOOSE_SCORES_TYPE) + DrawChooseTree(&scores_type_current); else if (setup_mode == SETUP_MODE_CHOOSE_GAME_SPEED) DrawChooseTree(&game_speed_current); else if (setup_mode == SETUP_MODE_CHOOSE_SCROLL_DELAY) @@ -8987,6 +9505,8 @@ void HandleSetupScreen(int mx, int my, int dx, int dy, int button) { if (setup_mode == SETUP_MODE_INPUT) HandleSetupScreen_Input(mx, my, dx, dy, button); + else if (setup_mode == SETUP_MODE_CHOOSE_SCORES_TYPE) + HandleChooseTree(mx, my, dx, dy, button, &scores_type_current); else if (setup_mode == SETUP_MODE_CHOOSE_GAME_SPEED) HandleChooseTree(mx, my, dx, dy, button, &game_speed_current); else if (setup_mode == SETUP_MODE_CHOOSE_SCROLL_DELAY) @@ -9544,6 +10064,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; @@ -9594,11 +10123,27 @@ 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 HandleScreenGadgets(struct GadgetInfo *gi) { int id = gi->custom_id; @@ -9670,6 +10215,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: @@ -9683,6 +10230,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: @@ -9696,6 +10245,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: @@ -9875,36 +10426,78 @@ static int UploadTapes(void) static boolean OfferUploadTapes(void) { - if (!Request("Upload all your tapes to the high score server now?", REQ_ASK)) + if (!Request(setup.has_remaining_tapes ? + "Upload missing tapes to the high score server now?" : + "Upload all your tapes to the high score server now?", REQ_ASK)) return FALSE; int num_tapes_uploaded = UploadTapes(); char message[100]; - sprintf(message, "%d tapes uploaded!", num_tapes_uploaded); + if (num_tapes_uploaded < 0) + { + num_tapes_uploaded = -num_tapes_uploaded - 1; + + if (num_tapes_uploaded == 0) + sprintf(message, "Upload failed! No tapes uploaded!"); + else if (num_tapes_uploaded == 1) + sprintf(message, "Upload failed! Only 1 tape uploaded!"); + else + sprintf(message, "Upload failed! Only %d tapes uploaded!", + num_tapes_uploaded); + + Request(message, REQ_CONFIRM); + + // if uploading tapes failed, add tape upload entry to setup menu + setup.provide_uploading_tapes = TRUE; + setup.has_remaining_tapes = TRUE; + + SaveSetup_ServerSetup(); + + return FALSE; + } + + if (num_tapes_uploaded == 0) + sprintf(message, "No tapes uploaded!"); + else if (num_tapes_uploaded == 1) + sprintf(message, "1 tape uploaded!"); + else + sprintf(message, "%d tapes uploaded!", num_tapes_uploaded); Request(message, REQ_CONFIRM); + if (num_tapes_uploaded > 0) + Request("New scores will be visible after a few minutes!", REQ_CONFIRM); + // after all tapes have been uploaded, remove entry from setup menu setup.provide_uploading_tapes = FALSE; + setup.has_remaining_tapes = FALSE; - SaveSetup(); + SaveSetup_ServerSetup(); - return (num_tapes_uploaded > 0); + return TRUE; } -void CheckUploadTapes(void) +static void CheckUploadTapes(void) { if (!setup.ask_for_uploading_tapes) return; + // after asking for uploading tapes, do not ask again + setup.ask_for_uploading_tapes = FALSE; + setup.ask_for_remaining_tapes = FALSE; + if (directoryExists(getTapeDir(NULL))) { boolean tapes_uploaded = OfferUploadTapes(); if (!tapes_uploaded) - Request("You can upload your tapes from the setup menu later!", + { + Request(setup.has_remaining_tapes ? + "You can upload missing tapes from the setup menu later!" : + "You can upload your tapes from the setup menu later!", REQ_CONFIRM); + } } else { @@ -9912,8 +10505,27 @@ void CheckUploadTapes(void) setup.provide_uploading_tapes = FALSE; } - // after asking for uploading all tapes once, do not ask again - setup.ask_for_uploading_tapes = FALSE; + SaveSetup_ServerSetup(); +} - SaveSetup(); +static void UpgradePlayerUUID(void) +{ + ApiResetUUIDAsThread(getUUID()); +} + +static void CheckUpgradePlayerUUID(void) +{ + if (setup.player_version > 1) + return; + + UpgradePlayerUUID(); +} + +void CheckApiServerTasks(void) +{ + // check if the player's UUID has to be upgraded + CheckUpgradePlayerUUID(); + + // check if there are any tapes to be uploaded + CheckUploadTapes(); }