added setup option to ask to play again after game is over but not solved
[rocksndiamonds.git] / src / screens.c
index b339ced690cbe546f406d7702b9800bf8c462978..3c12d6b5df34b14d528fd4d61f8c7a828e64bfd7 100644 (file)
 #define MENU_SETUP_FONT_TITLE          FONT_TEXT_1
 #define MENU_SETUP_FONT_TEXT           FONT_TITLE_2
 
+#define MAX_SETUP_TEXT_INPUT_LEN       28
+
 /* for various menu stuff  */
 #define MENU_SCREEN_START_XPOS         1
 #define MENU_SCREEN_START_YPOS         2
 #define MAX_MENU_TEXT_LENGTH_BIG       13
 #define MAX_MENU_TEXT_LENGTH_MEDIUM    (MAX_MENU_TEXT_LENGTH_BIG * 2)
 
-/* buttons and scrollbars identifiers */
+/* screen gadget identifiers */
 #define SCREEN_CTRL_ID_PREV_LEVEL      0
 #define SCREEN_CTRL_ID_NEXT_LEVEL      1
 #define SCREEN_CTRL_ID_PREV_PLAYER     2
 #define SCREEN_CTRL_ID_SCROLL_UP       6
 #define SCREEN_CTRL_ID_SCROLL_DOWN     7
 #define SCREEN_CTRL_ID_SCROLL_VERTICAL 8
+#define SCREEN_CTRL_ID_NETWORK_SERVER  9
 
-#define NUM_SCREEN_GADGETS             9
+#define NUM_SCREEN_GADGETS             10
 
 #define NUM_SCREEN_MENUBUTTONS         6
 #define NUM_SCREEN_SCROLLBUTTONS       2
 #define NUM_SCREEN_SCROLLBARS          1
+#define NUM_SCREEN_TEXTINPUT           1
 
 #define SCREEN_MASK_MAIN               (1 << 0)
 #define SCREEN_MASK_MAIN_HAS_SOLUTION  (1 << 1)
@@ -247,6 +251,9 @@ static void HandleInfoScreen_Credits(int);
 static void HandleInfoScreen_Program(int);
 static void HandleInfoScreen_Version(int);
 
+static void ModifyGameSpeedIfNeeded(void);
+static void DisableVsyncIfNeeded(void);
+
 static void MapScreenMenuGadgets(int);
 static void MapScreenGadgets(int);
 static void MapScreenTreeGadgets(TreeInfo *);
@@ -276,6 +283,8 @@ static TreeInfo *scroll_delay_current = NULL;
 static TreeInfo *snapshot_modes = NULL;
 static TreeInfo *snapshot_mode_current = NULL;
 
+static TreeInfo *game_speeds_normal = NULL;
+static TreeInfo *game_speeds_extended = NULL;
 static TreeInfo *game_speeds = NULL;
 static TreeInfo *game_speed_current = NULL;
 
@@ -306,11 +315,7 @@ static TreeInfo *grid_size_current[2][2] = { { NULL, NULL }, { NULL, NULL } };
 static TreeInfo *level_number = NULL;
 static TreeInfo *level_number_current = NULL;
 
-static struct
-{
-  int value;
-  char *text;
-} window_sizes_list[] =
+static struct ValueTextInfo window_sizes_list[] =
 {
   {    50,     "50 %"                          },
   {    80,     "80 %"                          },
@@ -328,11 +333,7 @@ static struct
   {    -1,     NULL                            },
 };
 
-static struct
-{
-  char *value;
-  char *text;
-} scaling_types_list[] =
+static struct StringValueTextInfo scaling_types_list[] =
 {
   {    SCALING_QUALITY_NEAREST, "Off"          },
   {    SCALING_QUALITY_LINEAR,  "Linear"       },
@@ -341,11 +342,7 @@ static struct
   {    NULL,                    NULL           },
 };
 
-static struct
-{
-  char *value;
-  char *text;
-} rendering_modes_list[] =
+static struct StringValueTextInfo rendering_modes_list[] =
 {
   {    STR_SPECIAL_RENDERING_OFF,      "Off (May show artifacts, fast)" },
   {    STR_SPECIAL_RENDERING_BITMAP,   "Bitmap/Texture mode (slower)"   },
@@ -358,11 +355,7 @@ static struct
   {    NULL,                            NULL                            },
 };
 
-static struct
-{
-  char *value;
-  char *text;
-} vsync_modes_list[] =
+static struct StringValueTextInfo vsync_modes_list[] =
 {
   {    STR_VSYNC_MODE_OFF,             "Off"           },
   {    STR_VSYNC_MODE_NORMAL,          "Normal"        },
@@ -371,42 +364,40 @@ static struct
   {    NULL,                            NULL           },
 };
 
-static struct
-{
-  int value;
-  char *text;
-} game_speeds_list[] =
+static struct ValueTextInfo game_speeds_list_normal[] =
 {
-#if 1
   {    30,     "Very Slow"                     },
   {    25,     "Slow"                          },
   {    20,     "Normal"                        },
   {    15,     "Fast"                          },
   {    10,     "Very Fast"                     },
-#else
-  {    1000,   "1/1s (Extremely Slow)"         },
-  {    500,    "1/2s"                          },
-  {    200,    "1/5s"                          },
-  {    100,    "1/10s"                         },
-  {    50,     "1/20s"                         },
-  {    29,     "1/35s (Original Supaplex)"     },
-  {    25,     "1/40s"                         },
-  {    20,     "1/50s (Normal Speed)"          },
-  {    14,     "1/70s (Maximum Supaplex)"      },
-  {    10,     "1/100s"                        },
-  {    5,      "1/200s"                        },
-  {    2,      "1/500s"                        },
-  {    1,      "1/1000s (Extremely Fast)"      },
-#endif
 
   {    -1,     NULL                            },
 };
 
-static struct
-{
-  int value;
-  char *text;
-} scroll_delays_list[] =
+static struct ValueTextInfo game_speeds_list_extended[] =
+{
+  {    1000,   "1 fps (Extremely Slow)"        },
+  {    500,    "2 fps"                         },
+  {    200,    "5 fps"                         },
+  {    100,    "10 fps"                        },
+  {    50,     "20 fps"                        },
+  {    29,     "35 fps (Original Supaplex)"    },
+  {    25,     "40 fps"                        },
+  {    20,     "50 fps (=== Normal Speed ===)" },
+  {    16,     "60 fps (60 Hz VSync Speed)"    },
+  {    14,     "70 fps (Maximum Supaplex)"     },
+  {    10,     "100 fps"                       },
+  {    5,      "200 fps"                       },
+  {    2,      "500 fps"                       },
+  {    1,      "1000 fps (Extremely Fast)"     },
+
+  {    -1,     NULL                            },
+};
+
+static struct ValueTextInfo *game_speeds_list;
+
+static struct ValueTextInfo scroll_delays_list[] =
 {
   {    0,      "0 Tiles (No Scroll Delay)"     },
   {    1,      "1 Tile"                        },
@@ -421,11 +412,7 @@ static struct
   {    -1,     NULL                            },
 };
 
-static struct
-{
-  char *value;
-  char *text;
-} snapshot_modes_list[] =
+static struct StringValueTextInfo snapshot_modes_list[] =
 {
   {    STR_SNAPSHOT_MODE_OFF,                  "Off"           },
   {    STR_SNAPSHOT_MODE_EVERY_STEP,           "Every Step"    },
@@ -435,11 +422,7 @@ static struct
   {    NULL,                                   NULL            },
 };
 
-static struct
-{
-  int value;
-  char *text;
-} volumes_list[] =
+static struct ValueTextInfo volumes_list[] =
 {
   {    0,      "0 %"                           },
   {    1,      "1 %"                           },
@@ -459,11 +442,7 @@ static struct
   {    -1,     NULL                            },
 };
 
-static struct
-{
-  char *value;
-  char *text;
-} touch_controls_list[] =
+static struct StringValueTextInfo touch_controls_list[] =
 {
   {    TOUCH_CONTROL_OFF,              "Off"                   },
   {    TOUCH_CONTROL_VIRTUAL_BUTTONS,  "Virtual Buttons"       },
@@ -473,11 +452,7 @@ static struct
   {    NULL,                           NULL                    },
 };
 
-static struct
-{
-  int value;
-  char *text;
-} distances_list[] =
+static struct ValueTextInfo distances_list[] =
 {
   {    1,      "1 %"                           },
   {    2,      "2 %"                           },
@@ -492,11 +467,7 @@ static struct
   {    -1,     NULL                            },
 };
 
-static struct
-{
-  int value;
-  char *text;
-} transparencies_list[] =
+static struct ValueTextInfo transparencies_list[] =
 {
   {    0,      "0 % (Opaque)"                  },
   {    10,     "10 %"                          },
@@ -513,11 +484,7 @@ static struct
   {    -1,     NULL                            },
 };
 
-static struct
-{
-  int value;
-  char *text;
-} grid_sizes_list[] =
+static struct ValueTextInfo grid_sizes_list[] =
 {
   {    3,      "3"                             },
   {    4,      "4"                             },
@@ -670,6 +637,8 @@ static char str_main_text_first_level[10];
 static char str_main_text_last_level[10];
 static char str_main_text_level_number[10];
 
+static char network_server_hostname[MAX_SETUP_TEXT_INPUT_LEN + 1];
+
 static char *main_text_name                    = str_main_text_name;
 static char *main_text_first_level             = str_main_text_first_level;
 static char *main_text_last_level              = str_main_text_last_level;
@@ -2236,7 +2205,8 @@ static int getMenuTextFont(int type)
              TYPE_PLAYER       |
              TYPE_ECS_AGA      |
              TYPE_KEYTEXT      |
-             TYPE_ENTER_LIST))
+             TYPE_ENTER_LIST   |
+             TYPE_TEXT_INPUT))
     return FONT_MENU_2;
   else
     return FONT_MENU_1;
@@ -2283,6 +2253,7 @@ static void DrawCursorAndText_Setup(int screen_pos, int menu_info_pos_raw,
 
 static char *window_size_text;
 static char *scaling_type_text;
+static char *network_server_text;
 
 static void drawSetupValue(int, int);
 
@@ -2320,6 +2291,25 @@ static void drawMenuInfoList(int first_entry, int num_page_entries,
 
     DrawCursorAndText_Menu(i, menu_info_pos, FALSE);
 
+    if (si->type & TYPE_STRING)
+    {
+      int gadget_id = -1;
+
+      if (value_ptr == &network_server_text)
+       gadget_id = SCREEN_CTRL_ID_NETWORK_SERVER;
+
+      if (gadget_id != -1)
+      {
+       struct GadgetInfo *gi = screen_gadget[gadget_id];
+       int xpos = MENU_SCREEN_START_XPOS;
+       int ypos = MENU_SCREEN_START_YPOS + i;
+       int x = mSX + xpos * 32;
+       int y = mSY + ypos * 32;
+
+       ModifyGadget(gi, GDI_X, x, GDI_Y, y, GDI_END);
+      }
+    }
+
     if (si->type & TYPE_VALUE &&
        menu_info == setup_info)
       drawSetupValue(i, menu_info_pos);
@@ -2665,6 +2655,11 @@ static void HandleMenuScreen(int mx, int my, int dx, int dy, int button,
          menu_info[first_entry + y - 1].type & TYPE_ENTER_LIST)
        y--;
 
+      /* when selecting string value, execute function for text input gadget */
+      if (menu_info[first_entry + y].type & TYPE_STRING && y > 0 &&
+         menu_info[first_entry + y - 1].type & TYPE_TEXT_INPUT)
+       y--;
+
       if (menu_info[first_entry + y].type & TYPE_ENTER_OR_LEAVE)
       {
        void (*menu_callback_function)(void) =
@@ -2674,6 +2669,13 @@ static void HandleMenuScreen(int mx, int my, int dx, int dy, int button,
 
        menu_callback_function();
       }
+      else if (menu_info[first_entry + y].type & TYPE_TEXT_INPUT)
+      {
+       void (*gadget_callback_function)(void) =
+         menu_info[first_entry + y].value;
+
+       gadget_callback_function();
+      }
       else if (menu_info[first_entry + y].type & TYPE_VALUE &&
               menu_info == setup_info)
       {
@@ -4660,6 +4662,7 @@ void HandleHallOfFame(int mx, int my, int dx, int dy, int button)
 
     if (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);
@@ -4692,6 +4695,7 @@ static char *vsync_mode_text;
 static char *scroll_delay_text;
 static char *snapshot_mode_text;
 static char *game_speed_text;
+static char *network_server_text;
 static char *graphics_set_name;
 static char *sounds_set_name;
 static char *music_set_name;
@@ -4711,8 +4715,19 @@ static void execSetupMain(void)
   DrawSetupScreen();
 }
 
-static void execSetupGame_setGameSpeeds(void)
+static void execSetupGame_setGameSpeeds(boolean update_value)
 {
+  if (setup.game_speed_extended)
+  {
+    game_speeds_list = game_speeds_list_extended;
+    game_speeds      = game_speeds_extended;
+  }
+  else
+  {
+    game_speeds_list = game_speeds_list_normal;
+    game_speeds      = game_speeds_normal;
+  }
+
   if (game_speeds == NULL)
   {
     int i;
@@ -4741,6 +4756,11 @@ static void execSetupGame_setGameSpeeds(void)
     /* sort game speed values to start with slowest game speed */
     sortTreeInfo(&game_speeds);
 
+    update_value = TRUE;
+  }
+
+  if (update_value)
+  {
     /* set current game speed to configured game speed value */
     game_speed_current =
       getTreeInfoFromIdentifier(game_speeds, i_to_a(setup.game_frame_delay));
@@ -4753,6 +4773,11 @@ static void execSetupGame_setGameSpeeds(void)
     /* if that also fails, set current game speed to first available speed */
     if (game_speed_current == NULL)
       game_speed_current = game_speeds;
+
+    if (setup.game_speed_extended)
+      game_speeds_extended = game_speeds;
+    else
+      game_speeds_normal = game_speeds;
   }
 
   setup.game_frame_delay = atoi(game_speed_current->identifier);
@@ -4861,15 +4886,40 @@ static void execSetupGame_setSnapshotModes(void)
   snapshot_mode_text = snapshot_mode_current->name;
 }
 
+static void execSetupGame_setNetworkServerText(void)
+{
+  if (strEqual(setup.network_server_hostname, STR_NETWORK_AUTO_DETECT))
+  {
+    strcpy(network_server_hostname, STR_NETWORK_AUTO_DETECT_SETUP);
+  }
+  else
+  {
+    strncpy(network_server_hostname, setup.network_server_hostname,
+           MAX_SETUP_TEXT_INPUT_LEN);
+    network_server_hostname[MAX_SETUP_TEXT_INPUT_LEN] = '\0';
+  }
+
+  /* needed for displaying network server text instead of identifier */
+  network_server_text = network_server_hostname;
+}
+
 static void execSetupGame(void)
 {
-  execSetupGame_setGameSpeeds();
+  boolean check_vsync_mode = (setup_mode == SETUP_MODE_CHOOSE_GAME_SPEED);
+
+  execSetupGame_setGameSpeeds(FALSE);
   execSetupGame_setScrollDelays();
   execSetupGame_setSnapshotModes();
 
+  execSetupGame_setNetworkServerText();
+
   setup_mode = SETUP_MODE_GAME;
 
   DrawSetupScreen();
+
+  // check if vsync needs to be disabled for this game speed to work
+  if (check_vsync_mode)
+    DisableVsyncIfNeeded();
 }
 
 static void execSetupChooseGameSpeed(void)
@@ -5086,7 +5136,7 @@ static void execSetupGraphics_setRenderingModes(void)
   rendering_mode_text = rendering_mode_current->name;
 }
 
-static void execSetupGraphics_setVsyncModes(void)
+static void execSetupGraphics_setVsyncModes(boolean update_value)
 {
   if (vsync_modes == NULL)
   {
@@ -5116,6 +5166,11 @@ static void execSetupGraphics_setVsyncModes(void)
     /* sort vsync mode values to start with lowest vsync mode value */
     sortTreeInfo(&vsync_modes);
 
+    update_value = TRUE;
+  }
+
+  if (update_value)
+  {
     /* set current vsync mode value to configured vsync mode value */
     vsync_mode_current =
       getTreeInfoFromIdentifier(vsync_modes, setup.vsync_mode);
@@ -5138,6 +5193,8 @@ static void execSetupGraphics_setVsyncModes(void)
 
 static void execSetupGraphics(void)
 {
+  boolean check_game_speed = (setup_mode == SETUP_MODE_CHOOSE_VSYNC);
+
   // update "setup.window_scaling_percent" from list selection
   // (in this case, window scaling was changed on setup screen)
   if (setup_mode == SETUP_MODE_CHOOSE_WINDOW_SIZE)
@@ -5149,12 +5206,16 @@ static void execSetupGraphics(void)
 
   execSetupGraphics_setScalingTypes();
   execSetupGraphics_setRenderingModes();
-  execSetupGraphics_setVsyncModes();
+  execSetupGraphics_setVsyncModes(FALSE);
 
   setup_mode = SETUP_MODE_GRAPHICS;
 
   DrawSetupScreen();
 
+  // check if game speed is high enough for 60 Hz vsync to work
+  if (check_game_speed)
+    ModifyGameSpeedIfNeeded();
+
 #if defined(TARGET_SDL2)
   // window scaling may have changed at this point
   ToggleFullscreenOrChangeWindowScalingIfNeeded();
@@ -5877,6 +5938,120 @@ static void execSaveAndExitSetup(void)
   execExitSetup();
 }
 
+static void execGadgetNetworkServer(void)
+{
+  int gadget_id = SCREEN_CTRL_ID_NETWORK_SERVER;
+  struct GadgetInfo *gi = screen_gadget[gadget_id];
+
+  if (strEqual(setup.network_server_hostname, STR_NETWORK_AUTO_DETECT))
+    network_server_hostname[0] = '\0';
+
+  ModifyGadget(gi, GDI_TEXT_VALUE, network_server_hostname, GDI_END);
+
+  MapGadget(gi);
+
+  ClickOnGadget(gi, MB_LEFTBUTTON);
+}
+
+static void ToggleNetworkModeIfNeeded(void)
+{
+  int font_title = FONT_TITLE_1;
+  int font_foot = FC_BLUE;
+  int ystart  = mSY - SY + 16;
+  int ybottom = mSY - SY + SYSIZE - 20;
+  char *text = (setup.network_mode ? "Start Network" : "Stop Network");
+
+  if (setup.network_mode == network.enabled)
+    return;
+
+  network.enabled = setup.network_mode;
+
+  FadeOut(REDRAW_ALL);
+
+  ClearField();
+
+  DrawTextSCentered(ystart, font_title, text);
+
+  FadeIn(REDRAW_ALL);
+
+  if (network.enabled)
+    InitNetworkServer();
+  else
+    DisconnectFromNetworkServer();
+
+  DrawTextSCentered(ybottom, font_foot,
+                   "Press any key or button for setup menu");
+
+  WaitForEventToContinue();
+
+  DrawSetupScreen();
+}
+
+static void ToggleGameSpeedsListIfNeeded(void)
+{
+  boolean using_game_speeds_extended = (game_speeds == game_speeds_extended);
+
+  if (setup.game_speed_extended == using_game_speeds_extended)
+    return;
+
+  /* try to match similar values when changing game speeds list */
+  if (setup.game_speed_extended)
+    setup.game_frame_delay = (setup.game_frame_delay == 15 ? 16 :
+                             setup.game_frame_delay == 30 ? 29 :
+                             setup.game_frame_delay);
+  else
+    setup.game_frame_delay = (setup.game_frame_delay == 14 ? 15 :
+                             setup.game_frame_delay == 16 ? 15 :
+                             setup.game_frame_delay >= 29 ? 30 :
+                             setup.game_frame_delay <= 10 ? 10 :
+                             setup.game_frame_delay);
+
+  execSetupGame_setGameSpeeds(TRUE);
+
+  DrawSetupScreen();
+}
+
+static void ModifyGameSpeedIfNeeded(void)
+{
+  if (strEqual(setup.vsync_mode, STR_VSYNC_MODE_OFF) ||
+      setup.game_frame_delay <= MAX_VSYNC_FRAME_DELAY)
+    return;
+
+  char message[100];
+  char *game_speed_text = "Fast";
+  int game_speed_value = 15;
+
+  if (setup.game_speed_extended)
+  {
+    game_speed_text = "60 fps";
+    game_speed_value = 16;
+  }
+
+  sprintf(message, "Game speed set to %s for VSync to work!", game_speed_text);
+
+  /* set game speed to existing list value that is fast enough for vsync */
+  setup.game_frame_delay = game_speed_value;
+
+  execSetupGame_setGameSpeeds(TRUE);
+
+  Request(message, REQ_CONFIRM);
+}
+
+static void DisableVsyncIfNeeded(void)
+{
+  if (strEqual(setup.vsync_mode, STR_VSYNC_MODE_OFF) ||
+      (setup.game_frame_delay >= MIN_VSYNC_FRAME_DELAY &&
+       setup.game_frame_delay <= MAX_VSYNC_FRAME_DELAY))
+    return;
+
+  /* disable vsync for the selected game speed to work */
+  setup.vsync_mode = STR_VSYNC_MODE_OFF;
+
+  execSetupGraphics_setVsyncModes(TRUE);
+
+  Request("VSync disabled for this game speed to work!", REQ_CONFIRM);
+}
+
 static struct
 {
   void *value;
@@ -5980,6 +6155,8 @@ static struct TokenInfo setup_info_game[] =
   { TYPE_SWITCH,       &setup.team_mode,       "Team-Mode (Multi-Player):" },
   { TYPE_SWITCH,       &setup.network_mode,    "Network Multi-Player Mode:" },
   { TYPE_PLAYER,       &setup.network_player_nr,"Preferred Network Player:" },
+  { TYPE_TEXT_INPUT,   execGadgetNetworkServer, "Network Server Hostname:" },
+  { TYPE_STRING,       &network_server_text,   ""                      },
   { TYPE_YES_NO,       &setup.input_on_focus,  "Only Move Focussed Player:" },
   { TYPE_SWITCH,       &setup.time_limit,      "Time Limit:"           },
   { TYPE_SWITCH,       &setup.handicap,        "Handicap:"             },
@@ -5987,9 +6164,11 @@ static struct TokenInfo setup_info_game[] =
   { TYPE_SWITCH,       &setup.increment_levels,"Increment Solved Levels:" },
   { TYPE_SWITCH,       &setup.auto_play_next_level,"Auto-play Next Level:" },
   { TYPE_SWITCH,       &setup.skip_scores_after_game,"Skip Scores After Game:" },
+  { TYPE_YES_NO,       &setup.ask_on_game_over, "Ask on Game Over:"    },
   { TYPE_SWITCH,       &setup.autorecord,      "Auto-Record Tapes:"    },
   { TYPE_ENTER_LIST,   execSetupChooseGameSpeed, "Game Speed:"         },
   { TYPE_STRING,       &game_speed_text,       ""                      },
+  { TYPE_SWITCH,       &setup.game_speed_extended, "Game Speed Extended List:" },
 #if 1
   { TYPE_ENTER_LIST,   execSetupChooseScrollDelay, "Scroll Delay:"     },
   { TYPE_STRING,       &scroll_delay_text,     ""                      },
@@ -6379,10 +6558,12 @@ static void drawSetupValue(int screen_pos, int setup_info_pos_raw)
   int ypos = MENU_SCREEN_START_YPOS + screen_pos;
   int startx = mSX + xpos * 32;
   int starty = mSY + ypos * 32;
-  int font_nr, font_nr_default, font_width_default;
   int type = si->type;
   void *value = si->value;
   char *value_string = getSetupValue(type, value);
+  int font_nr_default = getSetupValueFont(type, value);
+  int font_width_default = getFontWidth(font_nr_default);
+  int font_nr = font_nr_default;
   int i;
 
   if (value_string == NULL)
@@ -6397,7 +6578,7 @@ static void drawSetupValue(int screen_pos, int setup_info_pos_raw)
   }
   else if (type & TYPE_STRING)
   {
-    int max_value_len = (SCR_FIELDX - 2) * 2;
+    int max_value_len = (SXSIZE - 2 * TILEX) / font_width_default;
 
     xpos = MENU_SCREEN_START_XPOS;
 
@@ -6417,10 +6598,6 @@ static void drawSetupValue(int screen_pos, int setup_info_pos_raw)
 
   startx = mSX + xpos * 32;
   starty = mSY + ypos * 32;
-  font_nr_default = getSetupValueFont(type, value);
-  font_width_default = getFontWidth(font_nr_default);
-
-  font_nr = font_nr_default;
 
   // special check if right-side setup values moved left due to scrollbar
   if (scrollbar_needed && xpos > MENU_SCREEN_START_XPOS)
@@ -6486,10 +6663,17 @@ static void drawSetupValue(int screen_pos, int setup_info_pos_raw)
 
   if (type & TYPE_PLAYER)
   {
+    struct FontBitmapInfo *font = getFontBitmapInfo(font_nr);
     int player_nr = *(int *)value;
-    int xoff = getFontWidth(font_nr);
+    int xoff = font->draw_xoffset + getFontWidth(font_nr);
+    int yoff = font->draw_yoffset + (getFontHeight(font_nr) - TILEY) / 2;
+    int startx2 = startx + xoff;
+    int starty2 = starty + yoff;
+
+    if (DrawingOnBackground(startx2, starty2))
+      ClearRectangleOnBackground(drawto, startx2, starty2, TILEX, TILEY);
 
-    DrawFixedGraphicThruMaskExt(drawto, startx + xoff, starty,
+    DrawFixedGraphicThruMaskExt(drawto, startx2, starty2,
                                PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0);
   }
 
@@ -6545,24 +6729,13 @@ static void changeSetupValue(int screen_pos, int setup_info_pos_raw, int dx)
   if (si->value == &setup.fullscreen)
     ToggleFullscreenOrChangeWindowScalingIfNeeded();
 
-  if (si->value == &setup.network_mode &&
-      setup.network_mode != network.enabled)
-  {
-    network.enabled = setup.network_mode;
-
-    FadeOut(REDRAW_ALL);
+  // network mode may have changed at this point
+  if (si->value == &setup.network_mode)
+    ToggleNetworkModeIfNeeded();
 
-    ClearField();
-
-    FadeIn(REDRAW_ALL);
-
-    if (network.enabled)
-      InitNetworkServer();
-    else
-      DisconnectFromNetworkServer();
-
-    DrawSetupScreen();
-  }
+  // game speed list may have changed at this point
+  if (si->value == &setup.game_speed_extended)
+    ToggleGameSpeedsListIfNeeded();
 }
 
 static struct TokenInfo *getSetupInfoFinal(struct TokenInfo *setup_info_orig)
@@ -8039,9 +8212,16 @@ void HandleSetupScreen(int mx, int my, int dx, int dy, int button)
 
 void HandleGameActions(void)
 {
+  if (setup.ask_on_game_over)
+    CheckGameOver();
+
   if (game.restart_game_message != NULL)
+  {
     RequestRestartGame(game.restart_game_message);
 
+    return;
+  }
+
   if (game_status != GAME_MODE_PLAYING)
     return;
 
@@ -8188,6 +8368,26 @@ static struct
   }
 };
 
+static struct
+{
+  int graphic;
+  int gadget_id;
+  int x, y;
+  int size;
+  char *value;
+  char *infotext;
+} textinput_info[NUM_SCREEN_TEXTINPUT] =
+{
+  {
+    IMG_SETUP_INPUT_TEXT,
+    SCREEN_CTRL_ID_NETWORK_SERVER,
+    -1, -1,    /* these values are not constant, but can change at runtime */
+    MAX_SETUP_TEXT_INPUT_LEN,
+    network_server_hostname,
+    "Network Server Hostname / IP"
+  },
+};
+
 static void CreateScreenMenubuttons(void)
 {
   struct GadgetInfo *gi;
@@ -8402,12 +8602,60 @@ static void CreateScreenScrollbars(void)
   }
 }
 
+static void CreateScreenTextInputGadgets(void)
+{
+  int i;
+
+  for (i = 0; i < NUM_SCREEN_TEXTINPUT; i++)
+  {
+    int graphic = textinput_info[i].graphic;
+    struct GraphicInfo *gd = &graphic_info[graphic];
+    int gd_x1 = gd->src_x;
+    int gd_y1 = gd->src_y;
+    int gd_x2 = gd->src_x + gd->active_xoffset;
+    int gd_y2 = gd->src_y + gd->active_yoffset;
+    struct GadgetInfo *gi;
+    unsigned int event_mask;
+    int id = textinput_info[i].gadget_id;
+    int x = textinput_info[i].x;
+    int y = textinput_info[i].y;
+
+    event_mask = GD_EVENT_TEXT_RETURN | GD_EVENT_TEXT_LEAVING;
+
+    gi = CreateGadget(GDI_CUSTOM_ID, id,
+                     GDI_CUSTOM_TYPE_ID, i,
+                     GDI_INFO_TEXT, textinput_info[i].infotext,
+                     GDI_X, SX + x,
+                     GDI_Y, SY + y,
+                     GDI_TYPE, GD_TYPE_TEXT_INPUT_ALPHANUMERIC,
+                     GDI_TEXT_VALUE, textinput_info[i].value,
+                     GDI_TEXT_SIZE, textinput_info[i].size,
+                     GDI_TEXT_FONT, getSetupValueFont(TYPE_STRING, NULL),
+                     GDI_TEXT_FONT_ACTIVE, FONT_TEXT_1,
+                     GDI_DESIGN_UNPRESSED, gd->bitmap, gd_x1, gd_y1,
+                     GDI_DESIGN_PRESSED, gd->bitmap, gd_x2, gd_y2,
+                     GDI_BORDER_SIZE, gd->border_size, gd->border_size,
+                     GDI_DESIGN_WIDTH, gd->width,
+                     GDI_EVENT_MASK, event_mask,
+                     GDI_CALLBACK_ACTION, HandleScreenGadgets,
+                     GDI_CALLBACK_ACTION_ALWAYS, TRUE,
+                     GDI_END);
+
+    if (gi == NULL)
+      Error(ERR_EXIT, "cannot create gadget");
+
+    screen_gadget[id] = gi;
+  }
+}
+
 void CreateScreenGadgets(void)
 {
   CreateScreenMenubuttons();
 
   CreateScreenScrollbuttons();
   CreateScreenScrollbars();
+
+  CreateScreenTextInputGadgets();
 }
 
 void FreeScreenGadgets(void)
@@ -8540,6 +8788,31 @@ static void HandleScreenGadgets(struct GadgetInfo *gi)
        HandleInfoScreen(0,0, 999,gi->event.item_position,MB_MENU_INITIALIZE);
       break;
 
+    case SCREEN_CTRL_ID_NETWORK_SERVER:
+    {
+      if (!strEqual(gi->textinput.value, ""))
+      {
+       setString(&setup.network_server_hostname, gi->textinput.value);
+
+       network.server_host = setup.network_server_hostname;
+      }
+      else
+      {
+       setString(&setup.network_server_hostname, STR_NETWORK_AUTO_DETECT);
+
+       network.server_host = NULL;
+      }
+
+      if (strEqual(network.server_host, STR_NETWORK_AUTO_DETECT))
+       network.server_host = NULL;
+
+      execSetupGame_setNetworkServerText();
+
+      DrawSetupScreen();
+
+      break;
+    }
+
     default:
       break;
   }