added support for amoeba settings in BD engine to level editor
[rocksndiamonds.git] / src / files.c
index d3031fafb1dad2ee066f0f46cd90817bc394896f..35597a4d5f9d88b563024f7991dc19674783073b 100644 (file)
@@ -165,7 +165,6 @@ static struct LevelFileConfigInfo chunk_config_INFO[] =
     TYPE_INTEGER,                      CONF_VALUE_8_BIT(1),
     &li.game_engine_type,              GAME_ENGINE_TYPE_RND
   },
-
   {
     -1,                                        SAVE_CONF_ALWAYS,
     TYPE_INTEGER,                      CONF_VALUE_16_BIT(1),
@@ -176,102 +175,141 @@ static struct LevelFileConfigInfo chunk_config_INFO[] =
     TYPE_INTEGER,                      CONF_VALUE_16_BIT(2),
     &li.fieldy,                                STD_LEV_FIELDY
   },
-
   {
     -1,                                        SAVE_CONF_ALWAYS,
     TYPE_INTEGER,                      CONF_VALUE_16_BIT(3),
     &li.time,                          100
   },
-
   {
     -1,                                        SAVE_CONF_ALWAYS,
     TYPE_INTEGER,                      CONF_VALUE_16_BIT(4),
     &li.gems_needed,                   0
   },
-
   {
     -1,                                        -1,
     TYPE_INTEGER,                      CONF_VALUE_32_BIT(2),
     &li.random_seed,                   0
   },
-
   {
     -1,                                        -1,
     TYPE_BOOLEAN,                      CONF_VALUE_8_BIT(2),
     &li.use_step_counter,              FALSE
   },
-
   {
     -1,                                        -1,
     TYPE_BITFIELD,                     CONF_VALUE_8_BIT(4),
     &li.wind_direction_initial,                MV_NONE
   },
-
   {
     -1,                                        -1,
     TYPE_BOOLEAN,                      CONF_VALUE_8_BIT(5),
     &li.em_slippery_gems,              FALSE
   },
-
   {
     -1,                                        -1,
     TYPE_BOOLEAN,                      CONF_VALUE_8_BIT(6),
     &li.use_custom_template,           FALSE
   },
-
   {
     -1,                                        -1,
     TYPE_BITFIELD,                     CONF_VALUE_32_BIT(1),
     &li.can_move_into_acid_bits,       ~0      // default: everything can
   },
-
   {
     -1,                                        -1,
     TYPE_BITFIELD,                     CONF_VALUE_8_BIT(7),
     &li.dont_collide_with_bits,                ~0      // default: always deadly
   },
-
   {
     -1,                                        -1,
     TYPE_BOOLEAN,                      CONF_VALUE_8_BIT(8),
     &li.em_explodes_by_fire,           FALSE
   },
-
   {
     -1,                                        -1,
     TYPE_INTEGER,                      CONF_VALUE_16_BIT(5),
     &li.score[SC_TIME_BONUS],          1
   },
-
   {
     -1,                                        -1,
     TYPE_BOOLEAN,                      CONF_VALUE_8_BIT(9),
     &li.auto_exit_sokoban,             FALSE
   },
-
   {
     -1,                                        -1,
     TYPE_BOOLEAN,                      CONF_VALUE_8_BIT(10),
     &li.auto_count_gems,               FALSE
   },
-
   {
     -1,                                        -1,
     TYPE_BOOLEAN,                      CONF_VALUE_8_BIT(11),
     &li.solved_by_one_player,          FALSE
   },
-
   {
     -1,                                        -1,
     TYPE_INTEGER,                      CONF_VALUE_8_BIT(12),
     &li.time_score_base,               1
   },
-
   {
     -1,                                        -1,
     TYPE_BOOLEAN,                      CONF_VALUE_8_BIT(13),
     &li.rate_time_over_score,          FALSE
   },
+  {
+    -1,                                        -1,
+    TYPE_BOOLEAN,                      CONF_VALUE_8_BIT(14),
+    &li.bd_intermission,               FALSE
+  },
+  {
+    -1,                                        -1,
+    TYPE_INTEGER,                      CONF_VALUE_8_BIT(15),
+    &li.bd_scheduling_type,            GD_SCHEDULING_MILLISECONDS
+  },
+  {
+    -1,                                        -1,
+    TYPE_BOOLEAN,                      CONF_VALUE_8_BIT(16),
+    &li.bd_pal_timing,                 FALSE
+  },
+  {
+    -1,                                        -1,
+    TYPE_INTEGER,                      CONF_VALUE_16_BIT(6),
+    &li.bd_cycle_delay_ms,             200
+  },
+  {
+    -1,                                        -1,
+    TYPE_INTEGER,                      CONF_VALUE_8_BIT(17),
+    &li.bd_cycle_delay_c64,            0
+  },
+  {
+    -1,                                        -1,
+    TYPE_INTEGER,                      CONF_VALUE_8_BIT(18),
+    &li.bd_hatching_delay_cycles,      21
+  },
+  {
+    -1,                                        -1,
+    TYPE_INTEGER,                      CONF_VALUE_8_BIT(19),
+    &li.bd_hatching_delay_seconds,     2
+  },
+  {
+    -1,                                        -1,
+    TYPE_BOOLEAN,                      CONF_VALUE_8_BIT(20),
+    &li.bd_line_shifting_borders,      FALSE
+  },
+  {
+    -1,                                        -1,
+    TYPE_BOOLEAN,                      CONF_VALUE_8_BIT(21),
+    &li.bd_scan_first_and_last_row,    TRUE
+  },
+  {
+    -1,                                        -1,
+    TYPE_BOOLEAN,                      CONF_VALUE_8_BIT(22),
+    &li.bd_short_explosions,           TRUE
+  },
+  {
+    -1,                                        -1,
+    TYPE_BOOLEAN,                      CONF_VALUE_8_BIT(23),
+    &li.bd_gravity_affects_all,                TRUE
+  },
 
   {
     -1,                                        -1,
@@ -563,6 +601,53 @@ static struct LevelFileConfigInfo chunk_config_ELEM[] =
     &li.initial_inventory_size[3],     1, MAX_INITIAL_INVENTORY_SIZE
   },
 
+  // (these values are only valid for BD style levels)
+  // (some values for BD style amoeba following below)
+  {
+    EL_BD_PLAYER,                      -1,
+    TYPE_BOOLEAN,                      CONF_VALUE_8_BIT(1),
+    &li.bd_diagonal_movements,         FALSE
+  },
+  {
+    EL_BD_PLAYER,                      -1,
+    TYPE_BOOLEAN,                      CONF_VALUE_8_BIT(2),
+    &li.bd_topmost_player_active,      TRUE
+  },
+  {
+    EL_BD_PLAYER,                      -1,
+    TYPE_INTEGER,                      CONF_VALUE_8_BIT(3),
+    &li.bd_pushing_prob,               25
+  },
+  {
+    EL_BD_PLAYER,                      -1,
+    TYPE_INTEGER,                      CONF_VALUE_8_BIT(4),
+    &li.bd_pushing_prob_with_sweet,    100
+  },
+  {
+    EL_BD_PLAYER,                      -1,
+    TYPE_BOOLEAN,                      CONF_VALUE_8_BIT(5),
+    &li.bd_push_mega_rock_with_sweet,  FALSE
+  },
+
+  {
+    EL_BD_DIAMOND,                     -1,
+    TYPE_INTEGER,                      CONF_VALUE_16_BIT(1),
+    &li.score[SC_DIAMOND_EXTRA],       20
+  },
+
+  {
+    EL_BD_MAGIC_WALL,                  -1,
+    TYPE_BOOLEAN,                      CONF_VALUE_8_BIT(1),
+    &li.bd_magic_wall_wait_hatching,   FALSE
+  },
+  {
+    EL_BD_MAGIC_WALL,                  -1,
+    TYPE_BOOLEAN,                      CONF_VALUE_8_BIT(2),
+    &li.bd_magic_wall_stops_amoeba,    TRUE
+  },
+
+  // (the following values are related to various game elements)
+
   {
     EL_EMERALD,                                -1,
     TYPE_INTEGER,                      CONF_VALUE_16_BIT(1),
@@ -623,6 +708,7 @@ static struct LevelFileConfigInfo chunk_config_ELEM[] =
     &li.score[SC_CRYSTAL],             10
   },
 
+  // (amoeba values used by R'n'D game engine only)
   {
     EL_BD_AMOEBA,                      -1,
     TYPE_ELEMENT,                      CONF_VALUE_16_BIT(1),
@@ -638,6 +724,93 @@ static struct LevelFileConfigInfo chunk_config_ELEM[] =
     TYPE_BOOLEAN,                      CONF_VALUE_8_BIT(1),
     &li.grow_into_diggable,            TRUE
   },
+  // (amoeba values used by BD game engine only)
+  {
+    EL_BD_AMOEBA,                      -1,
+    TYPE_BOOLEAN,                      CONF_VALUE_8_BIT(2),
+    &li.bd_amoeba_wait_for_hatching,   FALSE
+  },
+  {
+    EL_BD_AMOEBA,                      -1,
+    TYPE_BOOLEAN,                      CONF_VALUE_8_BIT(3),
+    &li.bd_amoeba_start_immediately,   TRUE
+  },
+  {
+    EL_BD_AMOEBA,                      -1,
+    TYPE_BOOLEAN,                      CONF_VALUE_8_BIT(4),
+    &li.bd_amoeba_2_explode_by_amoeba, TRUE
+  },
+  {
+    EL_BD_AMOEBA,                      -1,
+    TYPE_INTEGER,                      CONF_VALUE_16_BIT(3),
+    &li.bd_amoeba_threshold_too_big,   200
+  },
+  {
+    EL_BD_AMOEBA,                      -1,
+    TYPE_INTEGER,                      CONF_VALUE_16_BIT(4),
+    &li.bd_amoeba_slow_growth_time,    200
+  },
+  {
+    EL_BD_AMOEBA,                      -1,
+    TYPE_INTEGER,                      CONF_VALUE_8_BIT(5),
+    &li.bd_amoeba_slow_growth_rate,    3
+  },
+  {
+    EL_BD_AMOEBA,                      -1,
+    TYPE_INTEGER,                      CONF_VALUE_8_BIT(6),
+    &li.bd_amoeba_fast_growth_rate,    25
+  },
+  {
+    EL_BD_AMOEBA,                      -1,
+    TYPE_ELEMENT,                      CONF_VALUE_16_BIT(5),
+    &li.bd_amoeba_content_too_big,     EL_BD_ROCK
+  },
+  {
+    EL_BD_AMOEBA,                      -1,
+    TYPE_ELEMENT,                      CONF_VALUE_16_BIT(6),
+    &li.bd_amoeba_content_enclosed,    EL_BD_DIAMOND
+  },
+
+  {
+    EL_BD_AMOEBA_2,                    -1,
+    TYPE_INTEGER,                      CONF_VALUE_16_BIT(3),
+    &li.bd_amoeba_2_threshold_too_big, 200
+  },
+  {
+    EL_BD_AMOEBA_2,                    -1,
+    TYPE_INTEGER,                      CONF_VALUE_16_BIT(4),
+    &li.bd_amoeba_2_slow_growth_time,  200
+  },
+  {
+    EL_BD_AMOEBA_2,                    -1,
+    TYPE_INTEGER,                      CONF_VALUE_8_BIT(5),
+    &li.bd_amoeba_2_slow_growth_rate,  3
+  },
+  {
+    EL_BD_AMOEBA_2,                    -1,
+    TYPE_INTEGER,                      CONF_VALUE_8_BIT(6),
+    &li.bd_amoeba_2_fast_growth_rate,  25
+  },
+  {
+    EL_BD_AMOEBA_2,                    -1,
+    TYPE_ELEMENT,                      CONF_VALUE_16_BIT(5),
+    &li.bd_amoeba_2_content_too_big,   EL_BD_ROCK
+  },
+  {
+    EL_BD_AMOEBA_2,                    -1,
+    TYPE_ELEMENT,                      CONF_VALUE_16_BIT(6),
+    &li.bd_amoeba_2_content_enclosed,  EL_BD_DIAMOND
+  },
+  {
+    EL_BD_AMOEBA_2,                    -1,
+    TYPE_ELEMENT,                      CONF_VALUE_16_BIT(7),
+    &li.bd_amoeba_2_content_exploding, EL_EMPTY
+  },
+  {
+    EL_BD_AMOEBA_2,                    -1,
+    TYPE_ELEMENT,                      CONF_VALUE_16_BIT(8),
+    &li.bd_amoeba_2_content_looks_like,        EL_BD_AMOEBA_2
+  },
 
   {
     EL_YAMYAM,                         -1,
@@ -943,14 +1116,6 @@ static struct LevelFileConfigInfo chunk_config_ELEM[] =
     &li.score[SC_ELEM_BONUS],          10
   },
 
-  // ---------- unused values -------------------------------------------------
-
-  {
-    EL_UNKNOWN,                                SAVE_CONF_NEVER,
-    TYPE_INTEGER,                      CONF_VALUE_16_BIT(1),
-    &li.score[SC_UNKNOWN_15],          10
-  },
-
   {
     -1,                                        -1,
     -1,                                        -1,
@@ -2067,6 +2232,27 @@ static void ActivateLevelTemplate(void)
   }
 }
 
+static boolean checkForPackageFromBasename_BD(char *basename)
+{
+  // check for native BD level file extensions
+  if (!strSuffixLower(basename, ".bd") &&
+      !strSuffixLower(basename, ".bdr") &&
+      !strSuffixLower(basename, ".brc") &&
+      !strSuffixLower(basename, ".gds"))
+    return FALSE;
+
+  // check for standard single-level BD files (like "001.bd")
+  if (strSuffixLower(basename, ".bd") &&
+      strlen(basename) == 6 &&
+      basename[0] >= '0' && basename[0] <= '9' &&
+      basename[1] >= '0' && basename[1] <= '9' &&
+      basename[2] >= '0' && basename[2] <= '9')
+    return FALSE;
+
+  // this is a level package in native BD file format
+  return TRUE;
+}
+
 static char *getLevelFilenameFromBasename(char *basename)
 {
   static char *filename = NULL;
@@ -2102,10 +2288,7 @@ static int getFileTypeFromBasename(char *basename)
     return LEVEL_FILE_TYPE_SB;
 
   // check for typical filename of a Boulder Dash (GDash) level package file
-  if (strSuffixLower(basename, ".bd") ||
-      strSuffixLower(basename, ".bdr") ||
-      strSuffixLower(basename, ".brc") ||
-      strSuffixLower(basename, ".gds"))
+  if (checkForPackageFromBasename_BD(basename))
     return LEVEL_FILE_TYPE_BD;
 
   // ---------- try to determine file type from filesize ----------
@@ -2367,6 +2550,11 @@ static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
   if (fileExists(lfi->filename))
     return;
 
+  // check for native Boulder Dash level file
+  setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_BD, "%03d.bd", nr);
+  if (fileExists(lfi->filename))
+    return;
+
   // check for Emerald Mine level file (V1)
   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "a%c%c",
                                       'a' + (nr / 10) % 26, '0' + nr % 10);
@@ -3667,29 +3855,74 @@ static void CopyNativeLevel_RND_to_BD(struct LevelInfo *level)
   GdCave *cave = NULL; // will be changed below
   int cave_w = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
   int cave_h = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
-  int i, x, y;
+  int x, y;
 
   setLevelInfoToDefaults_BD_Ext(cave_w, cave_h);
 
   // cave and map newly allocated when set to defaults above
   cave = level_bd->cave;
 
-  for (i = 0; i < 5; i++)
-  {
-    cave->level_time[i]                        = level->time;
-    cave->level_diamonds[i]            = level->gems_needed;
-    cave->level_magic_wall_time[i]     = level->time_magic_wall;
-    cave->level_timevalue[i]           = level->score[SC_TIME_BONUS];
-  }
-
-  cave->diamond_value                  = level->score[SC_DIAMOND];
-  cave->extra_diamond_value            = level->score[SC_DIAMOND];
-
-  cave->level_speed[0]                 = 160;  // set cave speed
-
+  // level type
+  cave->intermission                   = level->bd_intermission;
+
+  // level settings
+  cave->level_time[0]                  = level->time;
+  cave->level_diamonds[0]              = level->gems_needed;
+
+  // game timing
+  cave->scheduling                     = level->bd_scheduling_type;
+  cave->pal_timing                     = level->bd_pal_timing;
+  cave->level_speed[0]                 = level->bd_cycle_delay_ms;
+  cave->level_ckdelay[0]               = level->bd_cycle_delay_c64;
+  cave->level_hatching_delay_frame[0]  = level->bd_hatching_delay_cycles;
+  cave->level_hatching_delay_time[0]   = level->bd_hatching_delay_seconds;
+
+  // scores
+  cave->level_timevalue[0]             = level->score[SC_TIME_BONUS];
+  cave->diamond_value                  = level->score[SC_EMERALD];
+  cave->extra_diamond_value            = level->score[SC_DIAMOND_EXTRA];
+
+  // compatibility settings
+  cave->lineshift                      = level->bd_line_shifting_borders;
+  cave->border_scan_first_and_last     = level->bd_scan_first_and_last_row;
+  cave->short_explosions               = level->bd_short_explosions;
+  cave->gravity_affects_all            = level->bd_gravity_affects_all;
+
+  // player properties
+  cave->diagonal_movements             = level->bd_diagonal_movements;
+  cave->active_is_first_found          = level->bd_topmost_player_active;
+  cave->pushing_stone_prob             = level->bd_pushing_prob            * 10000;
+  cave->pushing_stone_prob_sweet       = level->bd_pushing_prob_with_sweet * 10000;
+  cave->mega_stones_pushable_with_sweet        = level->bd_push_mega_rock_with_sweet;
+
+  // element properties
+  cave->level_magic_wall_time[0]       = level->time_magic_wall;
+  cave->magic_timer_wait_for_hatching  = level->bd_magic_wall_wait_hatching;
+  cave->magic_wall_stops_amoeba                = level->bd_magic_wall_stops_amoeba;
+  cave->amoeba_timer_wait_for_hatching = level->bd_amoeba_wait_for_hatching;
+  cave->amoeba_timer_started_immediately= level->bd_amoeba_start_immediately;
+  cave->amoeba_2_explodes_by_amoeba    = level->bd_amoeba_2_explode_by_amoeba;
+  cave->level_amoeba_threshold[0]      = level->bd_amoeba_threshold_too_big;
+  cave->level_amoeba_time[0]           = level->bd_amoeba_slow_growth_time;
+  cave->amoeba_growth_prob             = level->bd_amoeba_slow_growth_rate * 10000;
+  cave->amoeba_fast_growth_prob                = level->bd_amoeba_fast_growth_rate * 10000;
+  cave->level_amoeba_2_threshold[0]    = level->bd_amoeba_2_threshold_too_big;
+  cave->level_amoeba_2_time[0]         = level->bd_amoeba_2_slow_growth_time;
+  cave->amoeba_2_growth_prob           = level->bd_amoeba_2_slow_growth_rate * 10000;
+  cave->amoeba_2_fast_growth_prob      = level->bd_amoeba_2_fast_growth_rate * 10000;
+
+  cave->amoeba_too_big_effect      = map_element_RND_to_BD(level->bd_amoeba_content_too_big);
+  cave->amoeba_enclosed_effect     = map_element_RND_to_BD(level->bd_amoeba_content_enclosed);
+  cave->amoeba_2_too_big_effect     = map_element_RND_to_BD(level->bd_amoeba_2_content_too_big);
+  cave->amoeba_2_enclosed_effect    = map_element_RND_to_BD(level->bd_amoeba_2_content_enclosed);
+  cave->amoeba_2_explosion_effect   = map_element_RND_to_BD(level->bd_amoeba_2_content_exploding);
+  cave->amoeba_2_looks_like        = map_element_RND_to_BD(level->bd_amoeba_2_content_looks_like);
+
+  // level name
   strncpy(cave->name, level->name, sizeof(GdString));
   cave->name[sizeof(GdString) - 1] = '\0';
 
+  // playfield elements
   for (x = 0; x < cave->w; x++)
     for (y = 0; y < cave->h; y++)
       cave->map[y][x] = map_element_RND_to_BD(level->field[x][y]);
@@ -3705,19 +3938,74 @@ static void CopyNativeLevel_BD_to_RND(struct LevelInfo *level)
   level->fieldx = MIN(cave->w, MAX_LEV_FIELDX);
   level->fieldy = MIN(cave->h, MAX_LEV_FIELDY);
 
-  level->time                  = cave->level_time[bd_level_nr];
-  level->gems_needed           = cave->level_diamonds[bd_level_nr];
-  level->time_magic_wall       = cave->level_magic_wall_time[bd_level_nr];
-
-  level->score[SC_TIME_BONUS]  = cave->level_timevalue[bd_level_nr];
-  level->score[SC_DIAMOND]     = cave->diamond_value;
-
-  strncpy(level->name, cave->name, MAX_LEVEL_NAME_LEN);
+  // level type
+  level->bd_intermission               = cave->intermission;
+
+  // level settings
+  level->time                          = cave->level_time[bd_level_nr];
+  level->gems_needed                   = cave->level_diamonds[bd_level_nr];
+
+  // game timing
+  level->bd_scheduling_type            = cave->scheduling;
+  level->bd_pal_timing                 = cave->pal_timing;
+  level->bd_cycle_delay_ms             = cave->level_speed[bd_level_nr];
+  level->bd_cycle_delay_c64            = cave->level_ckdelay[bd_level_nr];
+  level->bd_hatching_delay_cycles      = cave->level_hatching_delay_frame[bd_level_nr];
+  level->bd_hatching_delay_seconds     = cave->level_hatching_delay_time[bd_level_nr];
+
+  // scores
+  level->score[SC_TIME_BONUS]          = cave->level_timevalue[bd_level_nr];
+  level->score[SC_EMERALD]             = cave->diamond_value;
+  level->score[SC_DIAMOND_EXTRA]       = cave->extra_diamond_value;
+
+  // compatibility settings
+  level->bd_line_shifting_borders      = cave->lineshift;
+  level->bd_scan_first_and_last_row    = cave->border_scan_first_and_last;
+  level->bd_short_explosions           = cave->short_explosions;
+  level->bd_gravity_affects_all                = cave->gravity_affects_all;
+
+  // player properties
+  level->bd_diagonal_movements         = cave->diagonal_movements;
+  level->bd_topmost_player_active      = cave->active_is_first_found;
+  level->bd_pushing_prob               = cave->pushing_stone_prob       / 10000;
+  level->bd_pushing_prob_with_sweet    = cave->pushing_stone_prob_sweet / 10000;
+  level->bd_push_mega_rock_with_sweet  = cave->mega_stones_pushable_with_sweet;
+
+  // element properties
+  level->time_magic_wall               = cave->level_magic_wall_time[bd_level_nr];
+  level->bd_magic_wall_wait_hatching   = cave->magic_timer_wait_for_hatching;
+  level->bd_magic_wall_stops_amoeba    = cave->magic_wall_stops_amoeba;
+  level->bd_amoeba_wait_for_hatching   = cave->amoeba_timer_wait_for_hatching;
+  level->bd_amoeba_start_immediately   = cave->amoeba_timer_started_immediately;
+  level->bd_amoeba_2_explode_by_amoeba = cave->amoeba_2_explodes_by_amoeba;
+  level->bd_amoeba_threshold_too_big   = cave->level_amoeba_threshold[0];
+  level->bd_amoeba_slow_growth_time    = cave->level_amoeba_time[0];
+  level->bd_amoeba_slow_growth_rate    = cave->amoeba_growth_prob      / 10000;
+  level->bd_amoeba_fast_growth_rate    = cave->amoeba_fast_growth_prob / 10000;
+  level->bd_amoeba_2_threshold_too_big = cave->level_amoeba_2_threshold[0];
+  level->bd_amoeba_2_slow_growth_time  = cave->level_amoeba_2_time[0];
+  level->bd_amoeba_2_slow_growth_rate  = cave->amoeba_2_growth_prob      / 10000;
+  level->bd_amoeba_2_fast_growth_rate  = cave->amoeba_2_fast_growth_prob / 10000;
+
+  level->bd_amoeba_content_too_big     = map_element_BD_to_RND(cave->amoeba_too_big_effect);
+  level->bd_amoeba_content_enclosed    = map_element_BD_to_RND(cave->amoeba_enclosed_effect);
+  level->bd_amoeba_2_content_too_big   = map_element_BD_to_RND(cave->amoeba_2_too_big_effect);
+  level->bd_amoeba_2_content_enclosed  = map_element_BD_to_RND(cave->amoeba_2_enclosed_effect);
+  level->bd_amoeba_2_content_exploding = map_element_BD_to_RND(cave->amoeba_2_explosion_effect);
+  level->bd_amoeba_2_content_looks_like        = map_element_BD_to_RND(cave->amoeba_2_looks_like);
+
+  // level name
+  char *cave_name = getStringPrint("%s / %d", cave->name, bd_level_nr + 1);
+
+  strncpy(level->name, cave_name, MAX_LEVEL_NAME_LEN);
   level->name[MAX_LEVEL_NAME_LEN] = '\0';
 
+  // playfield elements
   for (x = 0; x < level->fieldx; x++)
     for (y = 0; y < level->fieldy; y++)
       level->field[x][y] = map_element_BD_to_RND(cave->map[y][x]);
+
+  checked_free(cave_name);
 }
 
 static void setTapeInfoToDefaults(void);
@@ -6491,16 +6779,40 @@ void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
 
 void SaveNativeLevel(struct LevelInfo *level)
 {
-  if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
+  // saving native level files only supported for some game engines
+  if (level->game_engine_type != GAME_ENGINE_TYPE_BD &&
+      level->game_engine_type != GAME_ENGINE_TYPE_SP)
+    return;
+
+  char *file_ext = (level->game_engine_type == GAME_ENGINE_TYPE_BD ? "bd" :
+                   level->game_engine_type == GAME_ENGINE_TYPE_SP ? "sp" : "");
+  char *basename = getSingleLevelBasenameExt(level->file_info.nr, file_ext);
+  char *filename = getLevelFilenameFromBasename(basename);
+
+  if (fileExists(filename) && !Request("Native level file already exists! Overwrite it?", REQ_ASK))
+    return;
+
+  boolean success = FALSE;
+
+  if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
   {
-    char *basename = getSingleLevelBasenameExt(level->file_info.nr, "sp");
-    char *filename = getLevelFilenameFromBasename(basename);
+    CopyNativeLevel_RND_to_BD(level);
+    // CopyNativeTape_RND_to_BD(level);
 
+    success = SaveNativeLevel_BD(filename);
+  }
+  else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
+  {
     CopyNativeLevel_RND_to_SP(level);
     CopyNativeTape_RND_to_SP(level);
 
-    SaveNativeLevel_SP(filename);
+    success = SaveNativeLevel_SP(filename);
   }
+
+  if (success)
+    Request("Native level file saved!", REQ_CONFIRM);
+  else
+    Request("Failed to save native level file!", REQ_CONFIRM);
 }
 
 
@@ -11982,7 +12294,7 @@ static int get_anim_action_parameter_value(char *token)
   {
     if (isURL(token))
     {
-      result = get_hash_from_key(token);       // unsigned int => int
+      result = get_hash_from_string(token);    // unsigned int => int
       result = ABS(result);                    // may be negative now
       result += (result < MAX_IMAGE_FILES ? MAX_IMAGE_FILES : 0);
 
@@ -12084,7 +12396,7 @@ int get_parameter_value(char *value_raw, char *suffix, int type)
   else if (strEqual(suffix, ".class"))
   {
     result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
-             get_hash_from_key(value));
+             get_hash_from_string(value));
   }
   else if (strEqual(suffix, ".style"))
   {