changed using macros for checking sound and music loops
[rocksndiamonds.git] / src / screens.c
index 30bb2b20ee43b65fea9d8ed112b4aea9a694df89..a5b808a1969462bac0a1bc7b1aefd001a1b49cb3 100644 (file)
 #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)
 
 #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"
@@ -282,6 +284,9 @@ static void MapScreenTreeGadgets(TreeInfo *);
 
 static void UpdateScreenMenuGadgets(int, boolean);
 
+static boolean OfferUploadTapes(void);
+static void execOfferUploadTapes(void);
+
 static struct GadgetInfo *screen_gadget[NUM_SCREEN_GADGETS];
 
 static int info_mode = INFO_MODE_MAIN;
@@ -305,6 +310,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;
@@ -389,6 +397,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"                     },
@@ -1740,6 +1757,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)
@@ -2229,9 +2249,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
       }
     }
   }
@@ -3235,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);
@@ -3246,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);
@@ -3314,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);
 }
 
@@ -4036,14 +4060,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;
@@ -4056,11 +4108,13 @@ static void RenamePlayerOnServerExt(struct HttpRequest *request,
           "{\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);
 
@@ -4069,6 +4123,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());
@@ -4084,49 +4210,258 @@ static void RenamePlayerOnServerExt(struct HttpRequest *request,
 
     return;
   }
+
+  HandleResponse_ApiRenamePlayer(response, data_raw);
+}
+
+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 void RenamePlayerOnServer(char *player_name, char *player_uuid)
+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);
 
-  RenamePlayerOnServer(data->player_name, data->player_uuid);
+  return data;
+}
+
+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");
 }
 
 
@@ -4252,10 +4587,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();
@@ -4268,7 +4610,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)
   {
@@ -4688,7 +5030,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();
@@ -4855,7 +5198,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();
@@ -4927,7 +5271,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();
@@ -5348,6 +5693,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;
@@ -5368,6 +5714,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)
@@ -5561,11 +5957,15 @@ static void execSetupGame(void)
   boolean check_vsync_mode = (setup_mode == SETUP_MODE_CHOOSE_GAME_SPEED);
 
   execSetupGame_setGameSpeeds(FALSE);
+  execSetupGame_setScoresType();
   execSetupGame_setScrollDelays();
   execSetupGame_setSnapshotModes();
 
   execSetupGame_setNetworkServerText();
 
+  if (!setup.provide_uploading_tapes)
+    setHideSetupEntry(execOfferUploadTapes);
+
   setup_mode = SETUP_MODE_GAME;
 
   DrawSetupScreen();
@@ -5575,6 +5975,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;
@@ -6651,6 +7058,11 @@ static void execGadgetNetworkServer(void)
   ClickOnGadget(gi, MB_LEFTBUTTON);
 }
 
+static void execOfferUploadTapes(void)
+{
+  OfferUploadTapes();
+}
+
 static void ToggleNetworkModeIfNeeded(void)
 {
   int font_title = FONT_TITLE_1;
@@ -6709,6 +7121,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) ||
@@ -6756,6 +7179,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                },
 
@@ -6868,8 +7294,10 @@ static struct TokenInfo setup_info_game[] =
   { TYPE_PLAYER,       &setup.network_player_nr,"Preferred Network Player:" },
   { TYPE_TEXT_INPUT,   execGadgetNetworkServer, "Network Server Hostname:" },
   { TYPE_STRING,       &network_server_text,   ""                      },
-  { TYPE_SWITCH,       &setup.api_server,      "Use Highscore Server:" },
-  { TYPE_SWITCH,       &setup.only_show_local_scores, "Only Show Local Scores:" },
+  { 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 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:"           },
@@ -7473,8 +7901,8 @@ static void changeSetupValue(int screen_pos, int setup_info_pos_raw, int dx)
     ToggleNetworkModeIfNeeded();
 
   // API server mode may have changed at this point
-  if (si->value == &setup.api_server)
-    runtime.api_server = setup.api_server;
+  if (si->value == &setup.use_api_server)
+    ToggleUseApiServerIfNeeded();
 
   // game speed list may have changed at this point
   if (si->value == &setup.game_speed_extended)
@@ -8891,6 +9319,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)
@@ -8973,6 +9403,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)
@@ -9859,29 +10291,108 @@ static int UploadTapes(void)
   return num_tapes_uploaded;
 }
 
-void CheckUploadTapes(void)
+static boolean OfferUploadTapes(void)
+{
+  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];
+
+  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_ServerSetup();
+
+  return TRUE;
+}
+
+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)))
   {
-    if (Request("Upload all your tapes to the high score server now?", REQ_ASK))
-    {
-      int num_tapes_uploaded = UploadTapes();
-      char message[100];
-
-      sprintf(message, "%d tapes uploaded!", num_tapes_uploaded);
+    boolean tapes_uploaded = OfferUploadTapes();
 
-      Request(message, REQ_CONFIRM);
-    }
-    else
+    if (!tapes_uploaded)
     {
-      Request("You can upload your tapes from the setup menu later!", REQ_CONFIRM);
+      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
+  {
+    // if tapes directory does not exist yet, never offer uploading all tapes
+    setup.provide_uploading_tapes = FALSE;
+  }
 
-  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();
 }