added editing empty space element settings in level editor
[rocksndiamonds.git] / src / editor.c
index b6736132cb1e5115ec852fe36e5ea3e690b7c004..1fd4b463ce1280b3a93fdf5c69aa2aefac166ae0 100644 (file)
@@ -489,6 +489,12 @@ enum
   GADGET_ID_MOVE_DELAY_RND_DOWN,
   GADGET_ID_MOVE_DELAY_RND_TEXT,
   GADGET_ID_MOVE_DELAY_RND_UP,
+  GADGET_ID_STEP_DELAY_FIX_DOWN,
+  GADGET_ID_STEP_DELAY_FIX_TEXT,
+  GADGET_ID_STEP_DELAY_FIX_UP,
+  GADGET_ID_STEP_DELAY_RND_DOWN,
+  GADGET_ID_STEP_DELAY_RND_TEXT,
+  GADGET_ID_STEP_DELAY_RND_UP,
   GADGET_ID_EXPLOSION_DELAY_DOWN,
   GADGET_ID_EXPLOSION_DELAY_TEXT,
   GADGET_ID_EXPLOSION_DELAY_UP,
@@ -559,6 +565,7 @@ enum
   // selectbox identifiers
 
   GADGET_ID_TIME_OR_STEPS,
+  GADGET_ID_TIME_SCORE_BASE,
   GADGET_ID_GAME_ENGINE_TYPE,
   GADGET_ID_LEVELSET_SAVE_MODE,
   GADGET_ID_WIND_DIRECTION,
@@ -630,6 +637,7 @@ enum
   // checkbuttons/radiobuttons for level/element properties
 
   GADGET_ID_AUTO_COUNT_GEMS,
+  GADGET_ID_RATE_TIME_OVER_SCORE,
   GADGET_ID_USE_LEVELSET_ARTWORK,
   GADGET_ID_COPY_LEVEL_TEMPLATE,
   GADGET_ID_RANDOM_PERCENTAGE,
@@ -649,6 +657,7 @@ enum
   GADGET_ID_AUTO_EXIT_SOKOBAN,
   GADGET_ID_SOLVED_BY_ONE_PLAYER,
   GADGET_ID_FINISH_DIG_COLLECT,
+  GADGET_ID_KEEP_WALKABLE_CE,
   GADGET_ID_CONTINUOUS_SNAPPING,
   GADGET_ID_BLOCK_SNAP_FIELD,
   GADGET_ID_BLOCK_LAST_FIELD,
@@ -751,6 +760,8 @@ enum
   ED_COUNTER_ID_DROP_DELAY_RND,
   ED_COUNTER_ID_MOVE_DELAY_FIX,
   ED_COUNTER_ID_MOVE_DELAY_RND,
+  ED_COUNTER_ID_STEP_DELAY_FIX,
+  ED_COUNTER_ID_STEP_DELAY_RND,
   ED_COUNTER_ID_EXPLOSION_DELAY,
   ED_COUNTER_ID_IGNITION_DELAY,
   ED_COUNTER_ID_GROUP_CONTENT,
@@ -840,6 +851,7 @@ enum
 enum
 {
   ED_SELECTBOX_ID_TIME_OR_STEPS,
+  ED_SELECTBOX_ID_TIME_SCORE_BASE,
   ED_SELECTBOX_ID_GAME_ENGINE_TYPE,
   ED_SELECTBOX_ID_LEVELSET_SAVE_MODE,
   ED_SELECTBOX_ID_WIND_DIRECTION,
@@ -936,6 +948,7 @@ enum
 enum
 {
   ED_CHECKBUTTON_ID_AUTO_COUNT_GEMS,
+  ED_CHECKBUTTON_ID_RATE_TIME_OVER_SCORE,
   ED_CHECKBUTTON_ID_USE_LEVELSET_ARTWORK,
   ED_CHECKBUTTON_ID_COPY_LEVEL_TEMPLATE,
   ED_CHECKBUTTON_ID_RANDOM_RESTRICTED,
@@ -955,6 +968,7 @@ enum
   ED_CHECKBUTTON_ID_AUTO_EXIT_SOKOBAN,
   ED_CHECKBUTTON_ID_SOLVED_BY_ONE_PLAYER,
   ED_CHECKBUTTON_ID_FINISH_DIG_COLLECT,
+  ED_CHECKBUTTON_ID_KEEP_WALKABLE_CE,
   ED_CHECKBUTTON_ID_CONTINUOUS_SNAPPING,
   ED_CHECKBUTTON_ID_BLOCK_SNAP_FIELD,
   ED_CHECKBUTTON_ID_BLOCK_LAST_FIELD,
@@ -1009,7 +1023,7 @@ enum
 };
 
 #define ED_CHECKBUTTON_ID_LEVEL_FIRST  ED_CHECKBUTTON_ID_AUTO_COUNT_GEMS
-#define ED_CHECKBUTTON_ID_LEVEL_LAST   ED_CHECKBUTTON_ID_AUTO_COUNT_GEMS
+#define ED_CHECKBUTTON_ID_LEVEL_LAST   ED_CHECKBUTTON_ID_RATE_TIME_OVER_SCORE
 
 #define ED_CHECKBUTTON_ID_LEVELSET_FIRST ED_CHECKBUTTON_ID_USE_LEVELSET_ARTWORK
 #define ED_CHECKBUTTON_ID_LEVELSET_LAST         ED_CHECKBUTTON_ID_COPY_LEVEL_TEMPLATE
@@ -1410,10 +1424,10 @@ static struct
     GADGET_ID_LEVEL_TIMESCORE_DOWN,    GADGET_ID_LEVEL_TIMESCORE_UP,
     GADGET_ID_LEVEL_TIMESCORE_TEXT,    GADGET_ID_NONE,
     &level.score[SC_TIME_BONUS],
-    "score for each second/step left:",        NULL, NULL
+    "score for time or steps left:",   NULL, NULL
   },
   {
-    ED_LEVEL_SETTINGS_XPOS(0),         ED_LEVEL_SETTINGS_YPOS(12),
+    ED_LEVEL_SETTINGS_XPOS(0),         ED_LEVEL_SETTINGS_YPOS(13),
     0,                                 9999,
     GADGET_ID_LEVEL_RANDOM_SEED_DOWN,  GADGET_ID_LEVEL_RANDOM_SEED_UP,
     GADGET_ID_LEVEL_RANDOM_SEED_TEXT,  GADGET_ID_NONE,
@@ -1606,7 +1620,23 @@ static struct
     NULL,                              "+random", NULL
   },
   {
-    ED_ELEMENT_SETTINGS_XPOS(1),       ED_ELEMENT_SETTINGS_YPOS(12),
+    ED_ELEMENT_SETTINGS_XPOS(1),       ED_ELEMENT_SETTINGS_YPOS(6),
+    0,                                 999,
+    GADGET_ID_STEP_DELAY_FIX_DOWN,     GADGET_ID_STEP_DELAY_FIX_UP,
+    GADGET_ID_STEP_DELAY_FIX_TEXT,     GADGET_ID_NONE,
+    &custom_element.step_delay_fixed,
+    NULL,                              "step delay", NULL
+  },
+  {
+    -1,                                        ED_ELEMENT_SETTINGS_YPOS(6),
+    0,                                 999,
+    GADGET_ID_STEP_DELAY_RND_DOWN,     GADGET_ID_STEP_DELAY_RND_UP,
+    GADGET_ID_STEP_DELAY_RND_TEXT,     GADGET_ID_STEP_DELAY_FIX_UP,
+    &custom_element.step_delay_random,
+    NULL,                              "+random", NULL
+  },
+  {
+    ED_ELEMENT_SETTINGS_XPOS(1),       ED_ELEMENT_SETTINGS_YPOS(13),
     0,                                 999,
     GADGET_ID_EXPLOSION_DELAY_DOWN,    GADGET_ID_EXPLOSION_DELAY_UP,
     GADGET_ID_EXPLOSION_DELAY_TEXT,    GADGET_ID_NONE,
@@ -1614,7 +1644,7 @@ static struct
     NULL,                              "explosion delay", NULL
   },
   {
-    ED_ELEMENT_SETTINGS_XPOS(1),       ED_ELEMENT_SETTINGS_YPOS(13),
+    ED_ELEMENT_SETTINGS_XPOS(1),       ED_ELEMENT_SETTINGS_YPOS(14),
     0,                                 999,
     GADGET_ID_IGNITION_DELAY_DOWN,     GADGET_ID_IGNITION_DELAY_UP,
     GADGET_ID_IGNITION_DELAY_TEXT,     GADGET_ID_NONE,
@@ -1733,6 +1763,14 @@ static struct ValueTextInfo options_time_or_steps[] =
   { -1,                                NULL                            }
 };
 
+static struct ValueTextInfo options_time_score_base[] =
+{
+  { 1,                         "per second/step"               },
+  { 10,                                "per 10 seconds/steps"          },
+
+  { -1,                                NULL                            }
+};
+
 static struct ValueTextInfo options_game_engine_type[] =
 {
   { GAME_ENGINE_TYPE_RND,      "Rocks'n'Diamonds"              },
@@ -1971,6 +2009,9 @@ static struct ValueTextInfo options_change_direct_action[] =
   { CE_HEADLINE_SPECIAL_EVENTS,        "[mouse events]"                },
   { CE_CLICKED_BY_MOUSE,       "clicked by mouse"              },
   { CE_PRESSED_BY_MOUSE,       "pressed by mouse"              },
+  { CE_UNDEFINED,              " "                             },
+  { CE_HEADLINE_SPECIAL_EVENTS,        "[static states]"               },
+  { CE_NEXT_TO_PLAYER,         "next to player"                },
 
   { -1,                                NULL                            }
 };
@@ -2004,6 +2045,10 @@ static struct ValueTextInfo options_change_other_action[] =
   { CE_HEADLINE_SPECIAL_EVENTS,        "[mouse events]"                },
   { CE_MOUSE_CLICKED_ON_X,     "mouse clicked on"              },
   { CE_MOUSE_PRESSED_ON_X,     "mouse pressed on"              },
+  { CE_UNDEFINED,              " "                             },
+  { CE_HEADLINE_SPECIAL_EVENTS,        "[static states]"               },
+  { CE_PLAYER_NEXT_TO_X,       "player next to"                },
+  { CE_NEXT_TO_X,              "next to"                       },
 
   { -1,                                NULL                            }
 };
@@ -2468,7 +2513,15 @@ static struct
     NULL, NULL, "(0 => no limit)",     "time or step limit"
   },
   {
-    ED_LEVEL_SETTINGS_XPOS(0),         ED_LEVEL_SETTINGS_YPOS(11),
+    -1,                                        ED_LEVEL_SETTINGS_YPOS(10),
+    GADGET_ID_TIME_SCORE_BASE,         GADGET_ID_LEVEL_TIMESCORE_UP,
+    -1,
+    options_time_score_base,
+    &level.time_score_base,
+    NULL, NULL, NULL,                  "time score for 1 or 10 seconds/steps"
+  },
+  {
+    ED_LEVEL_SETTINGS_XPOS(0),         ED_LEVEL_SETTINGS_YPOS(12),
     GADGET_ID_GAME_ENGINE_TYPE,                GADGET_ID_NONE,
     -1,
     options_game_engine_type,
@@ -2584,7 +2637,7 @@ static struct
     NULL, "            can", ":",      "leave behind or change element"
   },
   {
-    -1,                                        ED_ELEMENT_SETTINGS_YPOS(7),
+    -1,                                        ED_ELEMENT_SETTINGS_YPOS(8),
     GADGET_ID_CUSTOM_SMASH_TARGETS,    GADGET_ID_CUSTOM_CAN_SMASH,
     -1,
     options_smash_targets,
@@ -2592,7 +2645,7 @@ static struct
     NULL, "can smash", NULL,           "elements that can be smashed"
   },
   {
-    ED_ELEMENT_SETTINGS_XPOS(1),       ED_ELEMENT_SETTINGS_YPOS(8),
+    ED_ELEMENT_SETTINGS_XPOS(1),       ED_ELEMENT_SETTINGS_YPOS(9),
     GADGET_ID_CUSTOM_SLIPPERY_TYPE,    GADGET_ID_NONE,
     -1,
     options_slippery_type,
@@ -2600,7 +2653,7 @@ static struct
     NULL, "slippery", NULL,            "where other elements fall down"
   },
   {
-    ED_ELEMENT_SETTINGS_XPOS(1),       ED_ELEMENT_SETTINGS_YPOS(9),
+    ED_ELEMENT_SETTINGS_XPOS(1),       ED_ELEMENT_SETTINGS_YPOS(10),
     GADGET_ID_CUSTOM_DEADLINESS,       GADGET_ID_NONE,
     -1,
     options_deadliness,
@@ -2608,7 +2661,7 @@ static struct
     NULL, "deadly when", NULL,         "deadliness of element"
   },
   {
-    ED_ELEMENT_SETTINGS_XPOS(1),       ED_ELEMENT_SETTINGS_YPOS(10),
+    ED_ELEMENT_SETTINGS_XPOS(1),       ED_ELEMENT_SETTINGS_YPOS(11),
     GADGET_ID_CUSTOM_EXPLOSION_TYPE,   GADGET_ID_NONE,
     -1,
     options_explosion_type,
@@ -2983,6 +3036,13 @@ static struct
     NULL, NULL,
     "automatically count gems needed", "set counter to number of gems"
   },
+  {
+    ED_LEVEL_SETTINGS_XPOS(0),         ED_LEVEL_SETTINGS_YPOS(11),
+    GADGET_ID_RATE_TIME_OVER_SCORE,    GADGET_ID_NONE,
+    &level.rate_time_over_score,
+    NULL, NULL,
+    "rate time/steps used over score", "sort high scores by playing time/steps"
+  },
   {
     ED_LEVEL_SETTINGS_XPOS(0),         ED_LEVEL_SETTINGS_YPOS(7),
     GADGET_ID_USE_LEVELSET_ARTWORK,    GADGET_ID_NONE,
@@ -3119,6 +3179,13 @@ static struct
     NULL, NULL,
     "CE action on finished dig/collect", "only finished dig/collect triggers CE"
   },
+  {
+    ED_ELEMENT_SETTINGS_XPOS(0),       ED_ELEMENT_SETTINGS_YPOS(4),
+    GADGET_ID_KEEP_WALKABLE_CE,                GADGET_ID_NONE,
+    &level.keep_walkable_ce,
+    NULL, NULL,
+    "keep walkable CE changed to player", "keep CE changing to player if walkable"
+  },
   {
     ED_ELEMENT_SETTINGS_XPOS(0),       ED_ELEMENT_SETTINGS_YPOS(9),
     GADGET_ID_CONTINUOUS_SNAPPING,     GADGET_ID_NONE,
@@ -3350,56 +3417,56 @@ static struct
     NULL,                              "element can move with some pattern"
   },
   {
-    ED_ELEMENT_SETTINGS_XPOS(0),       ED_ELEMENT_SETTINGS_YPOS(7),
+    ED_ELEMENT_SETTINGS_XPOS(0),       ED_ELEMENT_SETTINGS_YPOS(8),
     GADGET_ID_CUSTOM_CAN_FALL,         GADGET_ID_NONE,
     &custom_element_properties[EP_CAN_FALL],
     NULL, NULL,
     "can fall",                                "element can fall down"
   },
   {
-    -1,                                        ED_ELEMENT_SETTINGS_YPOS(7),
+    -1,                                        ED_ELEMENT_SETTINGS_YPOS(8),
     GADGET_ID_CUSTOM_CAN_SMASH,                GADGET_ID_CUSTOM_CAN_FALL,
     &custom_element_properties[EP_CAN_SMASH],
     NULL, " ",
     NULL,                              "element can smash other elements"
   },
   {
-    ED_ELEMENT_SETTINGS_XPOS(0),       ED_ELEMENT_SETTINGS_YPOS(8),
+    ED_ELEMENT_SETTINGS_XPOS(0),       ED_ELEMENT_SETTINGS_YPOS(9),
     GADGET_ID_CUSTOM_SLIPPERY,         GADGET_ID_NONE,
     &custom_element_properties[EP_SLIPPERY],
     NULL, NULL,
     NULL,                              "other elements can fall down from it"
   },
   {
-    ED_ELEMENT_SETTINGS_XPOS(0),       ED_ELEMENT_SETTINGS_YPOS(9),
+    ED_ELEMENT_SETTINGS_XPOS(0),       ED_ELEMENT_SETTINGS_YPOS(10),
     GADGET_ID_CUSTOM_DEADLY,           GADGET_ID_NONE,
     &custom_element_properties[EP_DEADLY],
     NULL, NULL,
     NULL,                              "element can kill the player"
   },
   {
-    ED_ELEMENT_SETTINGS_XPOS(0),       ED_ELEMENT_SETTINGS_YPOS(10),
+    ED_ELEMENT_SETTINGS_XPOS(0),       ED_ELEMENT_SETTINGS_YPOS(11),
     GADGET_ID_CUSTOM_CAN_EXPLODE,      GADGET_ID_NONE,
     &custom_element_properties[EP_CAN_EXPLODE],
     NULL, NULL,
     NULL,                              "element can explode"
   },
   {
-    ED_ELEMENT_SETTINGS_XPOS(1),       ED_ELEMENT_SETTINGS_YPOS(11),
+    ED_ELEMENT_SETTINGS_XPOS(1),       ED_ELEMENT_SETTINGS_YPOS(12),
     GADGET_ID_CUSTOM_EXPLODE_FIRE,     GADGET_ID_NONE,
     &custom_element_properties[EP_EXPLODES_BY_FIRE],
     NULL, NULL,
     "by fire",                         "element can explode by fire/explosion"
   },
   {
-    -1,                                        ED_ELEMENT_SETTINGS_YPOS(11),
+    -1,                                        ED_ELEMENT_SETTINGS_YPOS(12),
     GADGET_ID_CUSTOM_EXPLODE_SMASH,    GADGET_ID_CUSTOM_EXPLODE_FIRE,
     &custom_element_properties[EP_EXPLODES_SMASHED],
     NULL, " ",
     "smashed",                         "element can explode when smashed"
   },
   {
-    -1,                                        ED_ELEMENT_SETTINGS_YPOS(11),
+    -1,                                        ED_ELEMENT_SETTINGS_YPOS(12),
     GADGET_ID_CUSTOM_EXPLODE_IMPACT,   GADGET_ID_CUSTOM_EXPLODE_SMASH,
     &custom_element_properties[EP_EXPLODES_IMPACT],
     NULL, " ",
@@ -3690,7 +3757,7 @@ static struct
   // ---------- custom content (when exploding) -------------------------------
 
   {
-    -1,                                        ED_AREA_3X3_SETTINGS_YPOS(10),
+    -1,                                        ED_AREA_3X3_SETTINGS_YPOS(11),
     0,                                 ED_AREA_3X3_SETTINGS_YOFF,
     GADGET_ID_CUSTOM_CONTENT,          GADGET_ID_NONE, // align three rows
     &custom_element.content.e[0][0],   3, 3,
@@ -5205,6 +5272,41 @@ static int *editor_el_group_ptr = editor_el_group;
 static int num_editor_hl_group = ARRAY_SIZE(editor_hl_group);
 static int num_editor_el_group = ARRAY_SIZE(editor_el_group);
 
+static int editor_hl_empty_space[] =
+{
+  EL_INTERNAL_CASCADE_ES_ACTIVE,
+  EL_CHAR('E'),
+  EL_CHAR('S'),
+  EL_EMPTY,
+};
+
+static int editor_el_empty_space[] =
+{
+  EL_EMPTY_SPACE_1,
+  EL_EMPTY_SPACE_2,
+  EL_EMPTY_SPACE_3,
+  EL_EMPTY_SPACE_4,
+
+  EL_EMPTY_SPACE_5,
+  EL_EMPTY_SPACE_6,
+  EL_EMPTY_SPACE_7,
+  EL_EMPTY_SPACE_8,
+
+  EL_EMPTY_SPACE_9,
+  EL_EMPTY_SPACE_10,
+  EL_EMPTY_SPACE_11,
+  EL_EMPTY_SPACE_12,
+
+  EL_EMPTY_SPACE_13,
+  EL_EMPTY_SPACE_14,
+  EL_EMPTY_SPACE_15,
+  EL_EMPTY_SPACE_16
+};
+static int *editor_hl_empty_space_ptr = editor_hl_empty_space;
+static int *editor_el_empty_space_ptr = editor_el_empty_space;
+static int num_editor_hl_empty_space = ARRAY_SIZE(editor_hl_empty_space);
+static int num_editor_el_empty_space = ARRAY_SIZE(editor_el_empty_space);
+
 static int editor_hl_reference[] =
 {
   EL_INTERNAL_CASCADE_REF_ACTIVE,
@@ -5417,6 +5519,12 @@ editor_elements_info[] =
     &editor_hl_group_ptr,              &num_editor_hl_group,
     &editor_el_group_ptr,              &num_editor_el_group
   },
+  {
+    &setup_editor_el_custom,
+    &setup.editor_cascade.el_es,
+    &editor_hl_empty_space_ptr,                &num_editor_hl_empty_space,
+    &editor_el_empty_space_ptr,                &num_editor_el_empty_space
+  },
   {
     &setup_editor_el_custom,
     &setup.editor_cascade.el_ref,
@@ -7731,7 +7839,7 @@ static boolean PrepareSavingIntoPersonalLevelSet(void)
     return TRUE;
   }
 
-  if (!Request("This level is read only! "
+  if (!Request("This level is read-only! "
               "Save into personal level set?", REQ_ASK))
     return FALSE;
 
@@ -8145,7 +8253,8 @@ static void CopyCustomElementPropertiesToEditor(int element)
 
   // set "change by direct action" selectbox help value
   custom_element_change.direct_action =
-    (HAS_CHANGE_EVENT(element, CE_TOUCHED_BY_PLAYER) ? CE_TOUCHED_BY_PLAYER :
+    (HAS_CHANGE_EVENT(element, CE_NEXT_TO_PLAYER) ? CE_NEXT_TO_PLAYER :
+     HAS_CHANGE_EVENT(element, CE_TOUCHED_BY_PLAYER) ? CE_TOUCHED_BY_PLAYER :
      HAS_CHANGE_EVENT(element, CE_PRESSED_BY_PLAYER) ? CE_PRESSED_BY_PLAYER :
      HAS_CHANGE_EVENT(element, CE_SWITCHED_BY_PLAYER) ? CE_SWITCHED_BY_PLAYER :
      HAS_CHANGE_EVENT(element, CE_SNAPPED_BY_PLAYER) ? CE_SNAPPED_BY_PLAYER :
@@ -8169,7 +8278,8 @@ static void CopyCustomElementPropertiesToEditor(int element)
 
   // set "change by other element action" selectbox help value
   custom_element_change.other_action =
-    (HAS_CHANGE_EVENT(element, CE_PLAYER_TOUCHES_X) ? CE_PLAYER_TOUCHES_X :
+    (HAS_CHANGE_EVENT(element, CE_PLAYER_NEXT_TO_X) ? CE_PLAYER_NEXT_TO_X :
+     HAS_CHANGE_EVENT(element, CE_PLAYER_TOUCHES_X) ? CE_PLAYER_TOUCHES_X :
      HAS_CHANGE_EVENT(element, CE_PLAYER_PRESSES_X) ? CE_PLAYER_PRESSES_X :
      HAS_CHANGE_EVENT(element, CE_PLAYER_SWITCHES_X) ? CE_PLAYER_SWITCHES_X :
      HAS_CHANGE_EVENT(element, CE_PLAYER_SNAPS_X) ? CE_PLAYER_SNAPS_X :
@@ -8179,6 +8289,7 @@ static void CopyCustomElementPropertiesToEditor(int element)
      HAS_CHANGE_EVENT(element, CE_PLAYER_DIGS_X) ? CE_PLAYER_DIGS_X :
      HAS_CHANGE_EVENT(element, CE_PLAYER_COLLECTS_X) ? CE_PLAYER_COLLECTS_X :
      HAS_CHANGE_EVENT(element, CE_PLAYER_DROPS_X) ? CE_PLAYER_DROPS_X :
+     HAS_CHANGE_EVENT(element, CE_NEXT_TO_X) ? CE_NEXT_TO_X :
      HAS_CHANGE_EVENT(element, CE_TOUCHING_X) ? CE_TOUCHING_X :
      HAS_CHANGE_EVENT(element, CE_HITTING_X) ? CE_HITTING_X :
      HAS_CHANGE_EVENT(element, CE_DIGGING_X) ? CE_DIGGING_X :
@@ -8203,9 +8314,14 @@ static void CopyGroupElementPropertiesToEditor(int element)
   custom_element = element_info[element];      // needed for description
 }
 
+static void CopyEmptyElementPropertiesToEditor(int element)
+{
+  custom_element = element_info[element];
+}
+
 static void CopyClassicElementPropertiesToEditor(int element)
 {
-  if (ELEM_IS_PLAYER(element) || COULD_MOVE_INTO_ACID(element))
+  if (IS_PLAYER_ELEMENT(element) || COULD_MOVE_INTO_ACID(element))
     custom_element_properties[EP_CAN_MOVE_INTO_ACID] =
       getMoveIntoAcidProperty(&level, element);
 
@@ -8220,6 +8336,8 @@ static void CopyElementPropertiesToEditor(int element)
     CopyCustomElementPropertiesToEditor(element);
   else if (IS_GROUP_ELEMENT(element))
     CopyGroupElementPropertiesToEditor(element);
+  else if (IS_EMPTY_ELEMENT(element))
+    CopyEmptyElementPropertiesToEditor(element);
   else
     CopyClassicElementPropertiesToEditor(element);
 }
@@ -8307,6 +8425,7 @@ static void CopyCustomElementPropertiesToGame(int element)
   // ---------- element settings: advanced (custom elements) ------------------
 
   // set player change event from checkbox and selectbox
+  custom_element_change_events[CE_NEXT_TO_PLAYER] = FALSE;
   custom_element_change_events[CE_TOUCHED_BY_PLAYER] = FALSE;
   custom_element_change_events[CE_PRESSED_BY_PLAYER] = FALSE;
   custom_element_change_events[CE_SWITCHED_BY_PLAYER] = FALSE;
@@ -8331,6 +8450,7 @@ static void CopyCustomElementPropertiesToGame(int element)
     custom_element_change_events[CE_BY_DIRECT_ACTION];
 
   // set other element action change event from checkbox and selectbox
+  custom_element_change_events[CE_PLAYER_NEXT_TO_X] = FALSE;
   custom_element_change_events[CE_PLAYER_TOUCHES_X] = FALSE;
   custom_element_change_events[CE_PLAYER_PRESSES_X] = FALSE;
   custom_element_change_events[CE_PLAYER_SWITCHES_X] = FALSE;
@@ -8341,6 +8461,7 @@ static void CopyCustomElementPropertiesToGame(int element)
   custom_element_change_events[CE_PLAYER_DIGS_X] = FALSE;
   custom_element_change_events[CE_PLAYER_COLLECTS_X] = FALSE;
   custom_element_change_events[CE_PLAYER_DROPS_X] = FALSE;
+  custom_element_change_events[CE_NEXT_TO_X] = FALSE;
   custom_element_change_events[CE_TOUCHING_X] = FALSE;
   custom_element_change_events[CE_HITTING_X] = FALSE;
   custom_element_change_events[CE_DIGGING_X] = FALSE;
@@ -8389,9 +8510,24 @@ static void CopyGroupElementPropertiesToGame(int element)
   InitElementPropertiesGfxElement();
 }
 
+static void CopyEmptyElementPropertiesToGame(int element)
+{
+  // mark that this empty element has been modified
+  custom_element.modified_settings = TRUE;
+  level.changed = TRUE;
+
+  if (level.use_custom_template)
+    AskToCopyAndModifyLevelTemplate();
+
+  element_info[element] = custom_element;
+
+  // needed here to restore runtime value "element_info[element].gfx_element"
+  InitElementPropertiesGfxElement();
+}
+
 static void CopyClassicElementPropertiesToGame(int element)
 {
-  if (ELEM_IS_PLAYER(element) || COULD_MOVE_INTO_ACID(element))
+  if (IS_PLAYER_ELEMENT(element) || COULD_MOVE_INTO_ACID(element))
     setMoveIntoAcidProperty(&level, element,
                            custom_element_properties[EP_CAN_MOVE_INTO_ACID]);
 
@@ -8406,6 +8542,8 @@ static void CopyElementPropertiesToGame(int element)
     CopyCustomElementPropertiesToGame(element);
   else if (IS_GROUP_ELEMENT(element))
     CopyGroupElementPropertiesToGame(element);
+  else if (IS_EMPTY_ELEMENT(element))
+    CopyEmptyElementPropertiesToGame(element);
   else
     CopyClassicElementPropertiesToGame(element);
 }
@@ -9061,7 +9199,7 @@ static void DrawPropertiesTabulatorGadgets(void)
   int i;
 
   // draw two config tabulators for player elements
-  if (ELEM_IS_PLAYER(properties_element))
+  if (IS_PLAYER_ELEMENT(properties_element))
     id_last = ED_TEXTBUTTON_ID_PROPERTIES_CONFIG_2;
 
   // draw two config and one "change" tabulator for custom elements
@@ -9076,7 +9214,7 @@ static void DrawPropertiesTabulatorGadgets(void)
 
     // use "config 1" and "config 2" instead of "config" for players and CEs
     if (i == ED_TEXTBUTTON_ID_PROPERTIES_CONFIG &&
-       (ELEM_IS_PLAYER(properties_element) ||
+       (IS_PLAYER_ELEMENT(properties_element) ||
         IS_CUSTOM_ELEMENT(properties_element)))
       continue;
 
@@ -9769,11 +9907,12 @@ static boolean checkPropertiesConfig(int element)
   if (IS_GEM(element) ||
       IS_CUSTOM_ELEMENT(element) ||
       IS_GROUP_ELEMENT(element) ||
+      IS_EMPTY_ELEMENT(element) ||
       IS_BALLOON_ELEMENT(element) ||
       IS_ENVELOPE(element) ||
       IS_MM_MCDUFFIN(element) ||
       IS_DF_LASER(element) ||
-      ELEM_IS_PLAYER(element) ||
+      IS_PLAYER_ELEMENT(element) ||
       HAS_EDITOR_CONTENT(element) ||
       CAN_GROW(element) ||
       COULD_MOVE_INTO_ACID(element) ||
@@ -9930,7 +10069,7 @@ static void DrawPropertiesConfig(void)
       DrawAndroidElementArea(properties_element);
   }
 
-  if (ELEM_IS_PLAYER(properties_element))
+  if (IS_PLAYER_ELEMENT(properties_element))
   {
     int player_nr = GET_PLAYER_NR(properties_element);
 
@@ -9993,6 +10132,7 @@ static void DrawPropertiesConfig(void)
       // draw checkbutton gadgets
       MapCheckbuttonGadget(ED_CHECKBUTTON_ID_USE_INITIAL_INVENTORY);
       MapCheckbuttonGadget(ED_CHECKBUTTON_ID_FINISH_DIG_COLLECT);
+      MapCheckbuttonGadget(ED_CHECKBUTTON_ID_KEEP_WALKABLE_CE);
 
       // draw counter gadgets
       MapCounterButtons(ED_COUNTER_ID_INVENTORY_SIZE);
@@ -10009,7 +10149,7 @@ static void DrawPropertiesConfig(void)
     MapCheckbuttonGadget(ED_CHECKBUTTON_ID_EM_EXPLODES_BY_FIRE);
 
   if (COULD_MOVE_INTO_ACID(properties_element) &&
-      !ELEM_IS_PLAYER(properties_element) &&
+      !IS_PLAYER_ELEMENT(properties_element) &&
       (!IS_CUSTOM_ELEMENT(properties_element) ||
        edit_mode_properties == ED_MODE_PROPERTIES_CONFIG_2))
   {
@@ -10017,7 +10157,7 @@ static void DrawPropertiesConfig(void)
     checkbutton_info[ED_CHECKBUTTON_ID_CAN_MOVE_INTO_ACID].x =
       ED_ELEMENT_SETTINGS_XPOS(IS_CUSTOM_ELEMENT(properties_element) ? 1 : 0);
     checkbutton_info[ED_CHECKBUTTON_ID_CAN_MOVE_INTO_ACID].y =
-      ED_ELEMENT_SETTINGS_YPOS(IS_CUSTOM_ELEMENT(properties_element) ? 6 :
+      ED_ELEMENT_SETTINGS_YPOS(IS_CUSTOM_ELEMENT(properties_element) ? 7 :
                               IS_BALLOON_ELEMENT(properties_element) ||
                               HAS_EDITOR_CONTENT(properties_element) ? 1 : 0);
 
@@ -10191,6 +10331,23 @@ static void DrawPropertiesConfig(void)
 
     draw_footer_line = TRUE;
   }
+  else if (IS_EMPTY_ELEMENT(properties_element))
+  {
+    // draw stickybutton gadget
+    MapCheckbuttonGadget(ED_CHECKBUTTON_ID_STICK_ELEMENT);
+
+    // draw checkbutton gadgets
+    MapCheckbuttonGadget(ED_CHECKBUTTON_ID_CUSTOM_USE_GRAPHIC);
+    MapCheckbuttonGadget(ED_CHECKBUTTON_ID_CUSTOM_USE_TEMPLATE_1);
+
+    // draw textbutton gadgets
+    MapTextbuttonGadget(ED_TEXTBUTTON_ID_SAVE_AS_TEMPLATE_1);
+
+    // draw drawing area gadgets
+    MapDrawingArea(ED_DRAWING_ID_CUSTOM_GRAPHIC);
+
+    draw_footer_line = TRUE;
+  }
 
   // draw little footer border line above CE/GE use/save template gadgets
   if (draw_footer_line)
@@ -10347,12 +10504,12 @@ static void DrawPropertiesWindow(void)
     edit_mode_properties = ED_MODE_PROPERTIES_CONFIG_2;
 
   if (edit_mode_properties > ED_MODE_PROPERTIES_CONFIG &&
-      !ELEM_IS_PLAYER(properties_element) &&
+      !IS_PLAYER_ELEMENT(properties_element) &&
       !IS_CUSTOM_ELEMENT(properties_element))
     edit_mode_properties = ED_MODE_PROPERTIES_CONFIG;
 
   if (edit_mode_properties == ED_MODE_PROPERTIES_CONFIG &&
-      (ELEM_IS_PLAYER(properties_element) ||
+      (IS_PLAYER_ELEMENT(properties_element) ||
        IS_CUSTOM_ELEMENT(properties_element)))
     edit_mode_properties = ED_MODE_PROPERTIES_CONFIG_1;
 
@@ -12260,7 +12417,7 @@ static void CopyBrushExt(int from_x, int from_y, int to_x, int to_y,
        {
          int element = Tile[x][y];
 
-         if (!IS_EM_ELEMENT(element) && !ELEM_IS_PLAYER(element))
+         if (!IS_EM_ELEMENT(element) && !IS_PLAYER_ELEMENT(element))
            use_em_engine = FALSE;
 
          if (!IS_SP_ELEMENT(element))
@@ -12943,7 +13100,7 @@ static void HandleDrawingAreas(struct GadgetInfo *gi)
        {
          SetDrawModeHiRes(new_element);
 
-         if (ELEM_IS_PLAYER(new_element))
+         if (IS_PLAYER_ELEMENT(new_element))
          {
            // remove player at old position
            for (y = 0; y < lev_fieldy; y++)
@@ -12952,7 +13109,7 @@ static void HandleDrawingAreas(struct GadgetInfo *gi)
              {
                int old_element = Tile[x][y];
 
-               if (ELEM_IS_PLAYER(old_element))
+               if (IS_PLAYER_ELEMENT(old_element))
                {
                  int replaced_with_element =
                    (old_element == EL_SOKOBAN_FIELD_PLAYER &&
@@ -13392,7 +13549,7 @@ static void HandleTextbuttonGadgets(struct GadgetInfo *gi)
     if (levelset_save_mode == LEVELSET_SAVE_MODE_UPDATE &&
        leveldir_current->readonly)
     {
-      Request("This level set is read only!", REQ_CONFIRM);
+      Request("This level set is read-only!", REQ_CONFIRM);
 
       return;
     }
@@ -13583,9 +13740,11 @@ static void HandleCheckbuttons(struct GadgetInfo *gi)
     boolean template_related_changes_found = FALSE;
     int i;
 
-    // check if any custom or group elements have been changed
+    // check if any custom, group or empty elements have been changed
     for (i = 0; i < NUM_FILE_ELEMENTS; i++)
-      if ((IS_CUSTOM_ELEMENT(i) || IS_GROUP_ELEMENT(i)) &&
+      if ((IS_CUSTOM_ELEMENT(i) ||
+          IS_GROUP_ELEMENT(i) ||
+          IS_EMPTY_ELEMENT(i)) &&
          element_info[i].modified_settings)
        template_related_changes_found = TRUE;