moved code to get font for "choose tree" screen to separate function
[rocksndiamonds.git] / src / screens.c
index 8dd7dde929518db1709d3b83a178379b9a44cb48..49f390f973f643616266c0e1a78cfce6d6a432ce 100644 (file)
@@ -1758,8 +1758,8 @@ void DrawMainMenu(void)
 
   SyncEmscriptenFilesystem();
 
-  // needed once to upload tapes (after program start or after user change)
-  CheckUploadTapes();
+  // needed once after program start or after user change
+  CheckApiServerTasks();
 }
 
 static void gotoTopLevelDir(void)
@@ -3259,7 +3259,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);
@@ -3270,7 +3270,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);
@@ -3338,7 +3338,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);
 }
 
@@ -4060,7 +4060,7 @@ void HandleInfoScreen(int mx, int my, int dx, int dy, int button)
 
 
 // ============================================================================
-// change name functions
+// rename player API functions
 // ============================================================================
 
 struct ApiRenamePlayerThreadData
@@ -4255,6 +4255,216 @@ static void ApiRenamePlayerAsThread(void)
 }
 
 
+// ============================================================================
+// 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
 // ============================================================================
@@ -4622,6 +4832,9 @@ 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)
@@ -4643,9 +4856,20 @@ static void DrawChooseTree(TreeInfo **ti_ptr)
 
   DrawMaskedBorder(fade_mask);
 
+  if (game_status != game_status_last_screen)
+    PlayMenuSoundsAndMusic();
+
   FadeIn(fade_mask);
 }
 
+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;
+}
+
 static void drawChooseTreeText(int y, boolean active, TreeInfo *ti)
 {
   int num_entries = numTreeInfoInGroup(ti);
@@ -4656,8 +4880,7 @@ 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_nr = getChooseTreeFont(node, active);
   int font_xoffset = getFontBitmapInfo(font_nr)->draw_xoffset;
   int xpos = MENU_SCREEN_START_XPOS;
   int ypos = MENU_SCREEN_START_YPOS + y;
@@ -4756,6 +4979,18 @@ static void drawChooseTreeCursorAndText(int y, boolean active, TreeInfo *ti)
   drawChooseTreeText(y, active, ti);
 }
 
+static void drawChooseTreeScreen(TreeInfo *ti)
+{
+  int num_entries = numTreeInfoInGroup(ti);
+  int num_page_entries = MIN(num_entries, NUM_MENU_ENTRIES_ON_SCREEN);
+
+  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);
+}
+
 static void HandleChooseTree(int mx, int my, int dx, int dy, int button,
                             TreeInfo **ti_ptr)
 {
@@ -4797,13 +5032,8 @@ 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;
   }
@@ -4911,14 +5141,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;
     }
@@ -5150,8 +5373,6 @@ void DrawChoosePlayerName(void)
 {
   int i;
 
-  FadeMenuSoundsAndMusic();
-
   if (player_name != NULL)
   {
     freeTreeInfo(player_name);
@@ -5191,8 +5412,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)
@@ -5202,11 +5421,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)
@@ -5218,8 +5433,6 @@ void DrawChooseLevelNr(void)
 {
   int i;
 
-  FadeMenuSoundsAndMusic();
-
   if (level_number != NULL)
   {
     freeTreeInfo(level_number);
@@ -5265,8 +5478,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)
@@ -6911,6 +7122,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) ||
@@ -7076,7 +7298,7 @@ static struct TokenInfo setup_info_game[] =
   { TYPE_SWITCH,       &setup.use_api_server,  "Use Highscore Server:" },
   { TYPE_ENTER_LIST,   execSetupChooseScoresType,"Scores in Highscore List:" },
   { TYPE_STRING,       &scores_type_text,      ""                      },
-  { TYPE_ENTER_LIST,   execOfferUploadTapes,   "Upload All Tapes to Server" },
+  { 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:"           },
@@ -7090,6 +7312,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:" },
@@ -7337,6 +7560,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,                   ""                      },
@@ -7681,7 +7908,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)
@@ -10072,7 +10299,9 @@ 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();
@@ -10080,7 +10309,23 @@ static boolean OfferUploadTapes(void)
 
   if (num_tapes_uploaded < 0)
   {
-    Request("Cannot upload tapes to score server!", REQ_CONFIRM);
+    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;
   }
@@ -10094,29 +10339,38 @@ static boolean OfferUploadTapes(void)
 
   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 TRUE;
 }
 
-void CheckUploadTapes(void)
+static void CheckUploadTapes(void)
 {
   if (!setup.ask_for_uploading_tapes)
     return;
 
-  // after asking for uploading all tapes once, do not ask again
+  // 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
   {
@@ -10126,3 +10380,25 @@ void CheckUploadTapes(void)
 
   SaveSetup_ServerSetup();
 }
+
+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();
+}