changed "http" to "https" in URLs
[rocksndiamonds.git] / src / files.c
index 9f20487fb0e4f5aebb8bd2108b2f7e19040db114..c07043450023b9650e5102c5d9a9cb45b4e29f55 100644 (file)
@@ -4,7 +4,7 @@
 // (c) 1995-2014 by Artsoft Entertainment
 //                         Holger Schemel
 //                 info@artsoft.org
-//                 http://www.artsoft.org/
+//                 https://www.artsoft.org/
 // ----------------------------------------------------------------------------
 // files.c
 // ============================================================================
@@ -768,9 +768,15 @@ static struct LevelFileConfigInfo chunk_config_ELEM[] =
     &li.android_clone_time,            10
   },
   {
-    EL_EMC_ANDROID,                    -1,
+    EL_EMC_ANDROID,                    SAVE_CONF_NEVER,
     TYPE_ELEMENT_LIST,                 CONF_VALUE_BYTES(1),
     &li.android_clone_element[0],      EL_EMPTY, NULL,
+    &li.num_android_clone_elements,    1, MAX_ANDROID_ELEMENTS_OLD
+  },
+  {
+    EL_EMC_ANDROID,                    -1,
+    TYPE_ELEMENT_LIST,                 CONF_VALUE_BYTES(2),
+    &li.android_clone_element[0],      EL_EMPTY, NULL,
     &li.num_android_clone_elements,    1, MAX_ANDROID_ELEMENTS
   },
 
@@ -809,7 +815,7 @@ static struct LevelFileConfigInfo chunk_config_ELEM[] =
   {
     EL_EMC_MAGIC_BALL,                 -1,
     TYPE_BOOLEAN,                      CONF_VALUE_8_BIT(2),
-    &li.ball_state_initial,            FALSE
+    &li.ball_active_initial,           FALSE
   },
   {
     EL_EMC_MAGIC_BALL,                 -1,
@@ -3515,118 +3521,98 @@ static void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
     { 2, 2 },
   };
   struct LevelInfo_EM *level_em = level->native_em_level;
-  struct LEVEL *lev = level_em->lev;
-  struct PLAYER **ply = level_em->ply;
+  struct CAVE *cav = level_em->cav;
   int i, j, x, y;
 
-  lev->width  = MIN(level->fieldx, EM_MAX_CAVE_WIDTH);
-  lev->height = MIN(level->fieldy, EM_MAX_CAVE_HEIGHT);
+  cav->width  = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
+  cav->height = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
+
+  cav->time_seconds    = level->time;
+  cav->gems_needed     = level->gems_needed;
 
-  lev->time_seconds     = level->time;
-  lev->required_initial = level->gems_needed;
+  cav->emerald_score   = level->score[SC_EMERALD];
+  cav->diamond_score   = level->score[SC_DIAMOND];
+  cav->alien_score     = level->score[SC_ROBOT];
+  cav->tank_score      = level->score[SC_SPACESHIP];
+  cav->bug_score       = level->score[SC_BUG];
+  cav->eater_score     = level->score[SC_YAMYAM];
+  cav->nut_score       = level->score[SC_NUT];
+  cav->dynamite_score  = level->score[SC_DYNAMITE];
+  cav->key_score       = level->score[SC_KEY];
+  cav->exit_score      = level->score[SC_TIME_BONUS];
 
-  lev->emerald_score   = level->score[SC_EMERALD];
-  lev->diamond_score   = level->score[SC_DIAMOND];
-  lev->alien_score     = level->score[SC_ROBOT];
-  lev->tank_score      = level->score[SC_SPACESHIP];
-  lev->bug_score       = level->score[SC_BUG];
-  lev->eater_score     = level->score[SC_YAMYAM];
-  lev->nut_score       = level->score[SC_NUT];
-  lev->dynamite_score  = level->score[SC_DYNAMITE];
-  lev->key_score       = level->score[SC_KEY];
-  lev->exit_score      = level->score[SC_TIME_BONUS];
+  cav->num_eater_arrays        = level->num_yamyam_contents;
 
   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
     for (y = 0; y < 3; y++)
       for (x = 0; x < 3; x++)
-       lev->eater_array[i][y * 3 + x] =
-         map_element_RND_to_EM(level->yamyam_content[i].e[x][y]);
+       cav->eater_array[i][y * 3 + x] =
+         map_element_RND_to_EM_cave(level->yamyam_content[i].e[x][y]);
 
-  lev->amoeba_time             = level->amoeba_speed;
-  lev->wonderwall_time_initial = level->time_magic_wall;
-  lev->wheel_time              = level->time_wheel;
+  cav->amoeba_time             = level->amoeba_speed;
+  cav->wonderwall_time         = level->time_magic_wall;
+  cav->wheel_time              = level->time_wheel;
 
-  lev->android_move_time       = level->android_move_time;
-  lev->android_clone_time      = level->android_clone_time;
-  lev->ball_random             = level->ball_random;
-  lev->ball_state_initial      = level->ball_state_initial;
-  lev->ball_time               = level->ball_time;
-  lev->num_ball_arrays         = level->num_ball_contents;
+  cav->android_move_time       = level->android_move_time;
+  cav->android_clone_time      = level->android_clone_time;
+  cav->ball_random             = level->ball_random;
+  cav->ball_active             = level->ball_active_initial;
+  cav->ball_time               = level->ball_time;
+  cav->num_ball_arrays         = level->num_ball_contents;
 
-  lev->lenses_score            = level->lenses_score;
-  lev->magnify_score           = level->magnify_score;
-  lev->slurp_score             = level->slurp_score;
+  cav->lenses_score            = level->lenses_score;
+  cav->magnify_score           = level->magnify_score;
+  cav->slurp_score             = level->slurp_score;
 
-  lev->lenses_time             = level->lenses_time;
-  lev->magnify_time            = level->magnify_time;
+  cav->lenses_time             = level->lenses_time;
+  cav->magnify_time            = level->magnify_time;
 
-  lev->wind_direction_initial =
+  cav->wind_direction =
     map_direction_RND_to_EM(level->wind_direction_initial);
-  lev->wind_cnt_initial = (level->wind_direction_initial != MV_NONE ?
-                          lev->wind_time : 0);
 
   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
     for (j = 0; j < 8; j++)
-      lev->ball_array[i][j] =
-       map_element_RND_to_EM(level->
-                             ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]]);
+      cav->ball_array[i][j] =
+       map_element_RND_to_EM_cave(level->ball_content[i].
+                                  e[ball_xy[j][0]][ball_xy[j][1]]);
 
   map_android_clone_elements_RND_to_EM(level);
 
-  // first fill the complete playfield with the default border element
+  // first fill the complete playfield with the empty space element
   for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
     for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
-      level_em->cave[x][y] = ZBORDER;
-
-  if (BorderElement == EL_STEELWALL)
-  {
-    for (y = 0; y < lev->height + 2; y++)
-      for (x = 0; x < lev->width + 2; x++)
-       level_em->cave[x + 1][y + 1] = map_element_RND_to_EM(EL_STEELWALL);
-  }
+      cav->cave[x][y] = Cblank;
 
   // then copy the real level contents from level file into the playfield
-  for (y = 0; y < lev->height; y++) for (x = 0; x < lev->width; x++)
+  for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
   {
-    int new_element = map_element_RND_to_EM(level->field[x][y]);
-    int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
-    int xx = x + 1 + offset;
-    int yy = y + 1 + offset;
+    int new_element = map_element_RND_to_EM_cave(level->field[x][y]);
 
     if (level->field[x][y] == EL_AMOEBA_DEAD)
-      new_element = map_element_RND_to_EM(EL_AMOEBA_WET);
+      new_element = map_element_RND_to_EM_cave(EL_AMOEBA_WET);
 
-    level_em->cave[xx][yy] = new_element;
+    cav->cave[x][y] = new_element;
   }
 
   for (i = 0; i < MAX_PLAYERS; i++)
   {
-    ply[i]->x_initial = 0;
-    ply[i]->y_initial = 0;
+    cav->player_x[i] = -1;
+    cav->player_y[i] = -1;
   }
 
   // initialize player positions and delete players from the playfield
-  for (y = 0; y < lev->height; y++) for (x = 0; x < lev->width; x++)
+  for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
   {
     if (ELEM_IS_PLAYER(level->field[x][y]))
     {
       int player_nr = GET_PLAYER_NR(level->field[x][y]);
-      int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
-      int xx = x + 1 + offset;
-      int yy = y + 1 + offset;
 
-      ply[player_nr]->x_initial = xx;
-      ply[player_nr]->y_initial = yy;
+      cav->player_x[player_nr] = x;
+      cav->player_y[player_nr] = y;
 
-      level_em->cave[xx][yy] = map_element_RND_to_EM(EL_EMPTY);
+      cav->cave[x][y] = map_element_RND_to_EM_cave(EL_EMPTY);
     }
   }
-
-  if (BorderElement == EL_STEELWALL)
-  {
-    lev->width  += 2;
-    lev->height += 2;
-  }
 }
 
 static void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
@@ -3643,69 +3629,68 @@ static void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
     { 2, 2 },
   };
   struct LevelInfo_EM *level_em = level->native_em_level;
-  struct LEVEL *lev = level_em->lev;
-  struct PLAYER **ply = level_em->ply;
+  struct CAVE *cav = level_em->cav;
   int i, j, x, y;
 
-  level->fieldx = MIN(lev->width,  MAX_LEV_FIELDX);
-  level->fieldy = MIN(lev->height, MAX_LEV_FIELDY);
+  level->fieldx = MIN(cav->width,  MAX_LEV_FIELDX);
+  level->fieldy = MIN(cav->height, MAX_LEV_FIELDY);
 
-  level->time        = lev->time_seconds;
-  level->gems_needed = lev->required_initial;
+  level->time        = cav->time_seconds;
+  level->gems_needed = cav->gems_needed;
 
   sprintf(level->name, "Level %d", level->file_info.nr);
 
-  level->score[SC_EMERALD]     = lev->emerald_score;
-  level->score[SC_DIAMOND]     = lev->diamond_score;
-  level->score[SC_ROBOT]       = lev->alien_score;
-  level->score[SC_SPACESHIP]   = lev->tank_score;
-  level->score[SC_BUG]         = lev->bug_score;
-  level->score[SC_YAMYAM]      = lev->eater_score;
-  level->score[SC_NUT]         = lev->nut_score;
-  level->score[SC_DYNAMITE]    = lev->dynamite_score;
-  level->score[SC_KEY]         = lev->key_score;
-  level->score[SC_TIME_BONUS]  = lev->exit_score;
+  level->score[SC_EMERALD]     = cav->emerald_score;
+  level->score[SC_DIAMOND]     = cav->diamond_score;
+  level->score[SC_ROBOT]       = cav->alien_score;
+  level->score[SC_SPACESHIP]   = cav->tank_score;
+  level->score[SC_BUG]         = cav->bug_score;
+  level->score[SC_YAMYAM]      = cav->eater_score;
+  level->score[SC_NUT]         = cav->nut_score;
+  level->score[SC_DYNAMITE]    = cav->dynamite_score;
+  level->score[SC_KEY]         = cav->key_score;
+  level->score[SC_TIME_BONUS]  = cav->exit_score;
 
-  level->num_yamyam_contents = MAX_ELEMENT_CONTENTS;
+  level->num_yamyam_contents   = cav->num_eater_arrays;
 
-  for (i = 0; i < level->num_yamyam_contents; i++)
+  for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
     for (y = 0; y < 3; y++)
       for (x = 0; x < 3; x++)
        level->yamyam_content[i].e[x][y] =
-         map_element_EM_to_RND(lev->eater_array[i][y * 3 + x]);
+         map_element_EM_to_RND_cave(cav->eater_array[i][y * 3 + x]);
 
-  level->amoeba_speed          = lev->amoeba_time;
-  level->time_magic_wall       = lev->wonderwall_time_initial;
-  level->time_wheel            = lev->wheel_time;
+  level->amoeba_speed          = cav->amoeba_time;
+  level->time_magic_wall       = cav->wonderwall_time;
+  level->time_wheel            = cav->wheel_time;
 
-  level->android_move_time     = lev->android_move_time;
-  level->android_clone_time    = lev->android_clone_time;
-  level->ball_random           = lev->ball_random;
-  level->ball_state_initial    = lev->ball_state_initial;
-  level->ball_time             = lev->ball_time;
-  level->num_ball_contents     = lev->num_ball_arrays;
+  level->android_move_time     = cav->android_move_time;
+  level->android_clone_time    = cav->android_clone_time;
+  level->ball_random           = cav->ball_random;
+  level->ball_active_initial   = cav->ball_active;
+  level->ball_time             = cav->ball_time;
+  level->num_ball_contents     = cav->num_ball_arrays;
 
-  level->lenses_score          = lev->lenses_score;
-  level->magnify_score         = lev->magnify_score;
-  level->slurp_score           = lev->slurp_score;
+  level->lenses_score          = cav->lenses_score;
+  level->magnify_score         = cav->magnify_score;
+  level->slurp_score           = cav->slurp_score;
 
-  level->lenses_time           = lev->lenses_time;
-  level->magnify_time          = lev->magnify_time;
+  level->lenses_time           = cav->lenses_time;
+  level->magnify_time          = cav->magnify_time;
 
   level->wind_direction_initial =
-    map_direction_EM_to_RND(lev->wind_direction_initial);
+    map_direction_EM_to_RND(cav->wind_direction);
 
   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
     for (j = 0; j < 8; j++)
       level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
-       map_element_EM_to_RND(lev->ball_array[i][j]);
+       map_element_EM_to_RND_cave(cav->ball_array[i][j]);
 
   map_android_clone_elements_EM_to_RND(level);
 
   // convert the playfield (some elements need special treatment)
   for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
   {
-    int new_element = map_element_EM_to_RND(level_em->cave[x + 1][y + 1]);
+    int new_element = map_element_EM_to_RND_cave(cav->cave[x][y]);
 
     if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
       new_element = EL_AMOEBA_DEAD;
@@ -3717,8 +3702,8 @@ static void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
   {
     // in case of all players set to the same field, use the first player
     int nr = MAX_PLAYERS - i - 1;
-    int jx = ply[nr]->x_initial - 1;
-    int jy = ply[nr]->y_initial - 1;
+    int jx = cav->player_x[nr];
+    int jy = cav->player_y[nr];
 
     if (jx != -1 && jy != -1)
       level->field[jx][jy] = EL_PLAYER_1 + nr;
@@ -4003,7 +3988,7 @@ static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
     int demo_repeat = (demo->data[i] & 0xf0) >> 4;
     int tape_action = map_key_SP_to_RND(demo_action);
     int tape_repeat = demo_repeat + 1;
-    byte action[MAX_PLAYERS] = { tape_action, 0, 0, 0 };
+    byte action[MAX_TAPE_ACTIONS] = { tape_action };
     boolean success = 0;
     int j;
 
@@ -6809,8 +6794,8 @@ static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
   int chunk_size = 0;
   int x, y;
 
-  for (y = 0; y < level->fieldy; y++) 
-    for (x = 0; x < level->fieldx; x++) 
+  for (y = 0; y < level->fieldy; y++)
+    for (x = 0; x < level->fieldx; x++)
       if (level->encoding_16bit_field)
        chunk_size += putFile16BitBE(file, level->field[x][y]);
       else
@@ -7636,6 +7621,42 @@ static void setTapeInfoToDefaults(void)
   tape.no_valid_file = FALSE;
 }
 
+static int getTapePosSize(struct TapeInfo *tape)
+{
+  int tape_pos_size = 0;
+
+  if (tape->use_key_actions)
+    tape_pos_size += tape->num_participating_players;
+
+  if (tape->use_mouse_actions)
+    tape_pos_size += 3;                // x and y position and mouse button mask
+
+  tape_pos_size += 1;          // tape action delay value
+
+  return tape_pos_size;
+}
+
+static void setTapeActionFlags(struct TapeInfo *tape, int value)
+{
+  tape->use_key_actions = FALSE;
+  tape->use_mouse_actions = FALSE;
+
+  if (value != TAPE_USE_MOUSE_ACTIONS_ONLY)
+    tape->use_key_actions = TRUE;
+
+  if (value != TAPE_USE_KEY_ACTIONS_ONLY)
+    tape->use_mouse_actions = TRUE;
+}
+
+static int getTapeActionValue(struct TapeInfo *tape)
+{
+  return (tape->use_key_actions &&
+         tape->use_mouse_actions ? TAPE_USE_KEY_AND_MOUSE_ACTIONS :
+         tape->use_key_actions   ? TAPE_USE_KEY_ACTIONS_ONLY :
+         tape->use_mouse_actions ? TAPE_USE_MOUSE_ACTIONS_ONLY :
+         TAPE_ACTIONS_DEFAULT);
+}
+
 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
 {
   tape->file_version = getFileVersion(file);
@@ -7671,7 +7692,7 @@ static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
       }
     }
 
-    tape->use_mouse = (getFile8Bit(file) == 1 ? TRUE : FALSE);
+    setTapeActionFlags(tape, getFile8Bit(file));
 
     ReadUnusedBytesFromFile(file, TAPE_CHUNK_HEAD_UNUSED);
 
@@ -7708,8 +7729,7 @@ static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
 {
   int i, j;
-  int tape_pos_size =
-    (tape->use_mouse ? 3 : tape->num_participating_players) + 1;
+  int tape_pos_size = getTapePosSize(tape);
   int chunk_size_expected = tape_pos_size * tape->length;
 
   if (chunk_size_expected != chunk_size)
@@ -7727,20 +7747,12 @@ static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
 
       // tape too large; read and ignore remaining tape data from this chunk
       for (;i < tape->length; i++)
-       ReadUnusedBytesFromFile(file, tape->num_participating_players + 1);
+       ReadUnusedBytesFromFile(file, tape_pos_size);
 
       break;
     }
 
-    if (tape->use_mouse)
-    {
-      tape->pos[i].action[TAPE_ACTION_LX]     = getFile8Bit(file);
-      tape->pos[i].action[TAPE_ACTION_LY]     = getFile8Bit(file);
-      tape->pos[i].action[TAPE_ACTION_BUTTON] = getFile8Bit(file);
-
-      tape->pos[i].action[TAPE_ACTION_UNUSED] = 0;
-    }
-    else
+    if (tape->use_key_actions)
     {
       for (j = 0; j < MAX_PLAYERS; j++)
       {
@@ -7751,6 +7763,13 @@ static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
       }
     }
 
+    if (tape->use_mouse_actions)
+    {
+      tape->pos[i].action[TAPE_ACTION_LX]     = getFile8Bit(file);
+      tape->pos[i].action[TAPE_ACTION_LY]     = getFile8Bit(file);
+      tape->pos[i].action[TAPE_ACTION_BUTTON] = getFile8Bit(file);
+    }
+
     tape->pos[i].delay = getFile8Bit(file);
 
     if (tape->file_version == FILE_VERSION_1_0)
@@ -8074,7 +8093,7 @@ static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
 
   putFile8Bit(file, store_participating_players);
 
-  putFile8Bit(file, (tape->use_mouse ? 1 : 0));
+  putFile8Bit(file, getTapeActionValue(tape));
 
   // unused bytes not at the end here for 4-byte alignment of engine_version
   WriteUnusedBytesToFile(file, TAPE_CHUNK_HEAD_UNUSED);
@@ -8101,19 +8120,20 @@ static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
 
   for (i = 0; i < tape->length; i++)
   {
-    if (tape->use_mouse)
-    {
-      putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LX]);
-      putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LY]);
-      putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_BUTTON]);
-    }
-    else
+    if (tape->use_key_actions)
     {
       for (j = 0; j < MAX_PLAYERS; j++)
        if (tape->player_participates[j])
          putFile8Bit(file, tape->pos[i].action[j]);
     }
 
+    if (tape->use_mouse_actions)
+    {
+      putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LX]);
+      putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LY]);
+      putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_BUTTON]);
+    }
+
     putFile8Bit(file, tape->pos[i].delay);
   }
 }
@@ -8122,7 +8142,6 @@ void SaveTape(int nr)
 {
   char *filename = getTapeFilename(nr);
   FILE *file;
-  int num_participating_players = 0;
   int tape_pos_size;
   int info_chunk_size;
   int body_chunk_size;
@@ -8139,12 +8158,14 @@ void SaveTape(int nr)
   tape.file_version = FILE_VERSION_ACTUAL;
   tape.game_version = GAME_VERSION_ACTUAL;
 
+  tape.num_participating_players = 0;
+
   // count number of participating players
   for (i = 0; i < MAX_PLAYERS; i++)
     if (tape.player_participates[i])
-      num_participating_players++;
+      tape.num_participating_players++;
 
-  tape_pos_size = (tape.use_mouse ? 3 : num_participating_players) + 1;
+  tape_pos_size = getTapePosSize(&tape);
 
   info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
   body_chunk_size = tape_pos_size * tape.length;
@@ -8382,6 +8403,10 @@ static struct TokenInfo global_setup_tokens[] =
     TYPE_SWITCH,
     &setup.scroll_delay,                       "scroll_delay"
   },
+  {
+    TYPE_SWITCH,
+    &setup.forced_scroll_delay,                        "forced_scroll_delay"
+  },
   {
     TYPE_INTEGER,
     &setup.scroll_delay_value,                 "scroll_delay_value"
@@ -8482,6 +8507,10 @@ static struct TokenInfo global_setup_tokens[] =
     TYPE_SWITCH,
     &setup.prefer_aga_graphics,                        "prefer_aga_graphics"
   },
+  {
+    TYPE_SWITCH,
+    &setup.prefer_lowpass_sounds,              "prefer_lowpass_sounds"
+  },
   {
     TYPE_SWITCH,
     &setup.game_speed_extended,                        "game_speed_extended"
@@ -8951,6 +8980,10 @@ static struct TokenInfo internal_setup_tokens[] =
     TYPE_BOOLEAN,
     &setup.internal.show_scaling_in_title,     "show_scaling_in_title"
   },
+  {
+    TYPE_BOOLEAN,
+    &setup.internal.create_user_levelset,      "create_user_levelset"
+  },
   {
     TYPE_BOOLEAN,
     &setup.internal.menu_game,                 "menu_game"
@@ -9123,6 +9156,7 @@ static void setSetupInfoToDefaults(struct SetupInfo *si)
   si->sound_simple = TRUE;
   si->toons = TRUE;
   si->scroll_delay = TRUE;
+  si->forced_scroll_delay = FALSE;
   si->scroll_delay_value = STD_SCROLL_DELAY;
   si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
   si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
@@ -9148,6 +9182,7 @@ static void setSetupInfoToDefaults(struct SetupInfo *si)
   si->quick_switch = FALSE;
   si->input_on_focus = FALSE;
   si->prefer_aga_graphics = TRUE;
+  si->prefer_lowpass_sounds = FALSE;
   si->game_speed_extended = FALSE;
   si->game_frame_delay = GAME_FRAME_DELAY;
   si->sp_show_border_elements = FALSE;
@@ -9324,6 +9359,7 @@ static void setSetupInfoToDefaults(struct SetupInfo *si)
   si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
   si->internal.choose_from_top_leveldir = FALSE;
   si->internal.show_scaling_in_title = TRUE;
+  si->internal.create_user_levelset = TRUE;
 
   si->internal.default_window_width  = WIN_XSIZE_DEFAULT;
   si->internal.default_window_height = WIN_YSIZE_DEFAULT;
@@ -10066,11 +10102,19 @@ static int get_anim_parameter_value(char *s)
 {
   int event_value[] =
   {
-    ANIM_EVENT_CLICK
+    ANIM_EVENT_CLICK,
+    ANIM_EVENT_INIT,
+    ANIM_EVENT_START,
+    ANIM_EVENT_END,
+    ANIM_EVENT_POST
   };
   char *pattern_1[] =
   {
-    "click:anim_"
+    "click:anim_",
+    "init:anim_",
+    "start:anim_",
+    "end:anim_",
+    "post:anim_"
   };
   char *pattern_2 = ".part_";
   char *matching_char = NULL;
@@ -10160,6 +10204,9 @@ static int get_anim_parameter_values(char *s)
       string_has_parameter(s, "self"))
     event_value |= ANIM_EVENT_SELF;
 
+  if (string_has_parameter(s, "unclick:any"))
+    event_value |= ANIM_EVENT_UNCLICK_ANY;
+
   // if animation event found, add it to global animation event list
   if (event_value != ANIM_EVENT_NONE)
     list_pos = AddGlobalAnimEventValue(list_pos, event_value);
@@ -10279,7 +10326,10 @@ int get_parameter_value(char *value_raw, char *suffix, int type)
   {
     result = get_anim_parameter_values(value);
   }
-  else if (strEqual(suffix, ".init_event_action") ||
+  else if (strEqual(suffix, ".init_delay_action") ||
+          strEqual(suffix, ".anim_delay_action") ||
+          strEqual(suffix, ".post_delay_action") ||
+          strEqual(suffix, ".init_event_action") ||
           strEqual(suffix, ".anim_event_action"))
   {
     result = get_anim_action_parameter_value(value_raw);
@@ -10302,6 +10352,9 @@ int get_parameter_value(char *value_raw, char *suffix, int type)
     if (string_has_parameter(value, "reverse"))
       result |= STYLE_REVERSE;
 
+    if (string_has_parameter(value, "block_clicks"))
+      result |= STYLE_BLOCK;
+
     if (string_has_parameter(value, "passthrough_clicks"))
       result |= STYLE_PASSTHROUGH;
 
@@ -10317,6 +10370,12 @@ int get_parameter_value(char *value_raw, char *suffix, int type)
              string_has_parameter(value, "curtain")    ? FADE_MODE_CURTAIN :
              FADE_MODE_DEFAULT);
   }
+  else if (strEqual(suffix, ".auto_delay_unit"))
+  {
+    result = (string_has_parameter(value, "ms")     ? AUTO_DELAY_UNIT_MS :
+             string_has_parameter(value, "frames") ? AUTO_DELAY_UNIT_FRAMES :
+             AUTO_DELAY_UNIT_DEFAULT);
+  }
   else if (strPrefix(suffix, ".font"))         // (may also be ".font_xyz")
   {
     result = gfx.get_font_from_token_function(value);
@@ -10383,10 +10442,14 @@ static void InitMenuDesignSettings_SpecialPreProcessing(void)
     title_initial_first_default.post_delay;
   titlescreen_initial_first_default.auto_delay =
     title_initial_first_default.auto_delay;
+  titlescreen_initial_first_default.auto_delay_unit =
+    title_initial_first_default.auto_delay_unit;
   titlescreen_first_default.fade_mode  = title_first_default.fade_mode;
   titlescreen_first_default.fade_delay = title_first_default.fade_delay;
   titlescreen_first_default.post_delay = title_first_default.post_delay;
   titlescreen_first_default.auto_delay = title_first_default.auto_delay;
+  titlescreen_first_default.auto_delay_unit =
+    title_first_default.auto_delay_unit;
   titlemessage_initial_first_default.fade_mode  =
     title_initial_first_default.fade_mode;
   titlemessage_initial_first_default.fade_delay =
@@ -10395,27 +10458,36 @@ static void InitMenuDesignSettings_SpecialPreProcessing(void)
     title_initial_first_default.post_delay;
   titlemessage_initial_first_default.auto_delay =
     title_initial_first_default.auto_delay;
+  titlemessage_initial_first_default.auto_delay_unit =
+    title_initial_first_default.auto_delay_unit;
   titlemessage_first_default.fade_mode  = title_first_default.fade_mode;
   titlemessage_first_default.fade_delay = title_first_default.fade_delay;
   titlemessage_first_default.post_delay = title_first_default.post_delay;
   titlemessage_first_default.auto_delay = title_first_default.auto_delay;
+  titlemessage_first_default.auto_delay_unit =
+    title_first_default.auto_delay_unit;
 
   titlescreen_initial_default.fade_mode  = title_initial_default.fade_mode;
   titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
   titlescreen_initial_default.post_delay = title_initial_default.post_delay;
   titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
+  titlescreen_initial_default.auto_delay_unit =
+    title_initial_default.auto_delay_unit;
   titlescreen_default.fade_mode  = title_default.fade_mode;
   titlescreen_default.fade_delay = title_default.fade_delay;
   titlescreen_default.post_delay = title_default.post_delay;
   titlescreen_default.auto_delay = title_default.auto_delay;
+  titlescreen_default.auto_delay_unit = title_default.auto_delay_unit;
   titlemessage_initial_default.fade_mode  = title_initial_default.fade_mode;
   titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
   titlemessage_initial_default.post_delay = title_initial_default.post_delay;
-  titlemessage_initial_default.auto_delay = title_initial_default.auto_delay;
+  titlemessage_initial_default.auto_delay_unit =
+    title_initial_default.auto_delay_unit;
   titlemessage_default.fade_mode  = title_default.fade_mode;
   titlemessage_default.fade_delay = title_default.fade_delay;
   titlemessage_default.post_delay = title_default.post_delay;
   titlemessage_default.auto_delay = title_default.auto_delay;
+  titlemessage_default.auto_delay_unit = title_default.auto_delay_unit;
 
   // special case: initialize "ARG_DEFAULT" values in static default config
   // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
@@ -10804,6 +10876,7 @@ static void LoadMenuDesignSettingsFromFilename(char *filename)
     { TYPE_INTEGER,    &tfi.fade_delay,        ".fade_delay"           },
     { TYPE_INTEGER,    &tfi.post_delay,        ".post_delay"           },
     { TYPE_INTEGER,    &tfi.auto_delay,        ".auto_delay"           },
+    { TYPE_INTEGER,    &tfi.auto_delay_unit,   ".auto_delay_unit"      },
 
     { -1,              NULL,                   NULL                    }
   };
@@ -10826,6 +10899,7 @@ static void LoadMenuDesignSettingsFromFilename(char *filename)
     { TYPE_INTEGER,    &tmi.fade_delay,        ".fade_delay"           },
     { TYPE_INTEGER,    &tmi.post_delay,        ".post_delay"           },
     { TYPE_INTEGER,    &tmi.auto_delay,        ".auto_delay"           },
+    { TYPE_INTEGER,    &tmi.auto_delay_unit,   ".auto_delay_unit"      },
 
     { -1,              NULL,                   NULL                    }
   };