+ for (x = 0; x < lev_fieldx; x++)
+ for (y = 0; y < lev_fieldy; y++)
+ dst[x][y] = src[x][y];
+}
+
+static int setSelectboxValue(int selectbox_id, int new_value)
+{
+ int new_index_value = 0;
+ int i;
+
+ for (i = 0; selectbox_info[selectbox_id].options[i].text != NULL; i++)
+ if (selectbox_info[selectbox_id].options[i].value == new_value)
+ new_index_value = i;
+
+ *selectbox_info[selectbox_id].value =
+ selectbox_info[selectbox_id].options[new_index_value].value;
+
+ return new_index_value;
+}
+
+static void setSelectboxSpecialActionVariablesIfNeeded(void)
+{
+ int i;
+
+ // change action mode and arg variables according to action type variable
+ for (i = 0; action_arg_options[i].value != -1; i++)
+ {
+ if (action_arg_options[i].value == custom_element_change.action_type)
+ {
+ int mode = action_arg_options[i].mode;
+
+ // only change if corresponding selectbox has changed
+ if (selectbox_info[ED_SELECTBOX_ID_ACTION_MODE].options !=
+ action_arg_modes[mode])
+ custom_element_change.action_mode = -1;
+
+ // only change if corresponding selectbox has changed
+ if (selectbox_info[ED_SELECTBOX_ID_ACTION_ARG].options !=
+ action_arg_options[i].options)
+ custom_element_change.action_arg = -1;
+
+ break;
+ }
+ }
+}
+
+static void setSelectboxSpecialActionOptions(void)
+{
+ int i;
+
+ // change action mode and arg selectbox according to action type selectbox
+ for (i = 0; action_arg_options[i].value != -1; i++)
+ {
+ if (action_arg_options[i].value == custom_element_change.action_type)
+ {
+ int mode = action_arg_options[i].mode;
+
+ ModifyEditorSelectboxOptions(ED_SELECTBOX_ID_ACTION_MODE,
+ action_arg_modes[mode]);
+ ModifyEditorSelectboxValue(ED_SELECTBOX_ID_ACTION_MODE,
+ custom_element_change.action_mode);
+
+ ModifyEditorSelectboxOptions(ED_SELECTBOX_ID_ACTION_ARG,
+ action_arg_options[i].options);
+ ModifyEditorSelectboxValue(ED_SELECTBOX_ID_ACTION_ARG,
+ custom_element_change.action_arg);
+ break;
+ }
+ }
+}
+
+static void copy_custom_element_settings(int element_from, int element_to)
+{
+ struct ElementInfo *ei_from = &element_info[element_from];
+ struct ElementInfo *ei_to = &element_info[element_to];
+
+ copyElementInfo(ei_from, ei_to);
+}
+
+static void replace_custom_element_in_settings(int element_from,
+ int element_to)
+{
+ int i, j, x, y;
+
+ for (i = 0; i < NUM_FILE_ELEMENTS; i++)
+ {
+ struct ElementInfo *ei = &element_info[i];
+
+ for (y = 0; y < 3; y++)
+ for (x = 0; x < 3; x++)
+ if (ei->content.e[x][y] == element_from)
+ ei->content.e[x][y] = element_to;
+
+ for (j = 0; j < ei->num_change_pages; j++)
+ {
+ struct ElementChangeInfo *change = &ei->change_page[j];
+
+ if (change->target_element == element_from)
+ change->target_element = element_to;
+
+ if (change->initial_trigger_element == element_from)
+ change->initial_trigger_element = element_to;
+
+ if (change->action_element == element_from)
+ change->action_element = element_to;
+
+ for (y = 0; y < 3; y++)
+ for (x = 0; x < 3; x++)
+ if (change->target_content.e[x][y] == element_from)
+ change->target_content.e[x][y] = element_to;
+ }
+
+ if (ei->group != NULL) // group or internal
+ for (j = 0; j < MAX_ELEMENTS_IN_GROUP; j++)
+ if (ei->group->element[j] == element_from)
+ ei->group->element[j] = element_to;
+ }
+}
+
+static void replace_custom_element_in_playfield(int element_from,
+ int element_to)
+{
+ int x, y;
+
+ for (x = 0; x < lev_fieldx; x++)
+ for (y = 0; y < lev_fieldy; y++)
+ if (Tile[x][y] == element_from)
+ Tile[x][y] = element_to;
+}
+
+static boolean CopyCustomElement(int element_old, int element_new,
+ int copy_mode)
+{
+ int copy_mode_orig = copy_mode;
+
+ if (copy_mode == GADGET_ID_CUSTOM_COPY)
+ {
+ element_new = (IS_CUSTOM_ELEMENT(element_old) ?
+ EL_INTERNAL_CLIPBOARD_CUSTOM : EL_INTERNAL_CLIPBOARD_GROUP);
+ copy_mode = GADGET_ID_CUSTOM_COPY_TO;
+ }
+ else if (copy_mode == GADGET_ID_CUSTOM_PASTE)
+ {
+ element_old = (IS_CUSTOM_ELEMENT(element_new) ?
+ EL_INTERNAL_CLIPBOARD_CUSTOM : EL_INTERNAL_CLIPBOARD_GROUP);
+ copy_mode = GADGET_ID_CUSTOM_COPY_TO;
+
+ level.changed = TRUE;
+ }
+ else if (IS_CUSTOM_ELEMENT(element_old) && !IS_CUSTOM_ELEMENT(element_new))
+ {
+ Request("Please choose custom element!", REQ_CONFIRM);
+
+ return FALSE;
+ }
+ else if (IS_GROUP_ELEMENT(element_old) && !IS_GROUP_ELEMENT(element_new))
+ {
+ Request("Please choose group element!", REQ_CONFIRM);
+
+ return FALSE;
+ }
+ else
+ {
+ level.changed = TRUE;
+ }
+
+ // when modifying custom/group element, ask for copying level template
+ if (copy_mode_orig != GADGET_ID_CUSTOM_COPY && level.use_custom_template)
+ {
+ if (!AskToCopyAndModifyLevelTemplate())
+ return FALSE;
+ }
+
+ if (copy_mode == GADGET_ID_CUSTOM_COPY_FROM)
+ {
+ copy_custom_element_settings(element_new, element_old);
+ }
+ else if (copy_mode == GADGET_ID_CUSTOM_COPY_TO)
+ {
+ copy_custom_element_settings(element_old, element_new);
+ }
+ else if (copy_mode == GADGET_ID_CUSTOM_EXCHANGE)
+ {
+ copy_custom_element_settings(element_old, EL_INTERNAL_DUMMY);
+ copy_custom_element_settings(element_new, element_old);
+ copy_custom_element_settings(EL_INTERNAL_DUMMY, element_new);
+
+ replace_custom_element_in_settings(element_old, EL_INTERNAL_DUMMY);
+ replace_custom_element_in_settings(element_new, element_old);
+ replace_custom_element_in_settings(EL_INTERNAL_DUMMY, element_new);
+
+ replace_custom_element_in_playfield(element_old, EL_INTERNAL_DUMMY);
+ replace_custom_element_in_playfield(element_new, element_old);
+ replace_custom_element_in_playfield(EL_INTERNAL_DUMMY, element_new);
+ }
+
+ UpdateCustomElementGraphicGadgets();
+ DrawPropertiesWindow();
+
+ return TRUE;
+}
+
+static void CopyCustomElementPropertiesToEditor(int element)
+{
+ int i;
+ int current_change_page = element_info[element].current_change_page;
+
+ // dynamically (re)build selectbox for selecting change page
+ for (i = 0; i < element_info[element].num_change_pages; i++)
+ {
+ sprintf(options_change_page_strings[i], "%d", i + 1);
+
+ options_change_page[i].value = i;
+ options_change_page[i].text = options_change_page_strings[i];
+ }
+
+ options_change_page[i].value = -1;
+ options_change_page[i].text = NULL;
+
+ // needed here to initialize combined element properties
+ InitElementPropertiesEngine(level.game_version);
+
+ element_info[element].change =
+ &element_info[element].change_page[current_change_page];
+
+ custom_element = element_info[element];
+ custom_element_change = *element_info[element].change;
+
+ // needed to initially set selectbox options for special action options
+ setSelectboxSpecialActionOptions();
+
+ // needed to initially set selectbox value variables to reliable defaults
+ for (i = 0; i < ED_NUM_SELECTBOX; i++)
+ setSelectboxValue(i, *selectbox_info[i].value);
+
+ for (i = 0; i < NUM_ELEMENT_PROPERTIES; i++)
+ custom_element_properties[i] = HAS_PROPERTY(element, i);
+
+ for (i = 0; i < NUM_CHANGE_EVENTS; i++)
+ custom_element_change_events[i] = HAS_CHANGE_EVENT(element, i);
+
+ // ---------- element settings: configure (custom elements) -----------------
+
+ // set accessible layer selectbox help value
+ custom_element.access_type =
+ (IS_WALKABLE(element) ? EP_WALKABLE :
+ IS_PASSABLE(element) ? EP_PASSABLE :
+ custom_element.access_type);
+ custom_element.access_layer =
+ (IS_ACCESSIBLE_OVER(element) ? EP_ACCESSIBLE_OVER :
+ IS_ACCESSIBLE_INSIDE(element) ? EP_ACCESSIBLE_INSIDE :
+ IS_ACCESSIBLE_UNDER(element) ? EP_ACCESSIBLE_UNDER :
+ custom_element.access_layer);
+ custom_element.access_protected =
+ (IS_PROTECTED(element) ? 1 : 0);
+ custom_element_properties[EP_ACCESSIBLE] =
+ (IS_ACCESSIBLE_OVER(element) ||
+ IS_ACCESSIBLE_INSIDE(element) ||
+ IS_ACCESSIBLE_UNDER(element));
+
+ // set walk-to-object action selectbox help value
+ custom_element.walk_to_action =
+ (IS_DIGGABLE(element) ? EP_DIGGABLE :
+ IS_COLLECTIBLE_ONLY(element) ? EP_COLLECTIBLE_ONLY :
+ IS_DROPPABLE(element) ? EP_DROPPABLE :
+ IS_THROWABLE(element) ? EP_THROWABLE :
+ IS_PUSHABLE(element) ? EP_PUSHABLE :
+ custom_element.walk_to_action);
+ custom_element_properties[EP_WALK_TO_OBJECT] =
+ (IS_DIGGABLE(element) ||
+ IS_COLLECTIBLE_ONLY(element) ||
+ IS_DROPPABLE(element) ||
+ IS_THROWABLE(element) ||
+ IS_PUSHABLE(element));
+
+ // set smash targets selectbox help value
+ custom_element.smash_targets =
+ (CAN_SMASH_EVERYTHING(element) ? EP_CAN_SMASH_EVERYTHING :
+ CAN_SMASH_ENEMIES(element) ? EP_CAN_SMASH_ENEMIES :
+ CAN_SMASH_PLAYER(element) ? EP_CAN_SMASH_PLAYER :
+ custom_element.smash_targets);
+ custom_element_properties[EP_CAN_SMASH] =
+ (CAN_SMASH_EVERYTHING(element) ||
+ CAN_SMASH_ENEMIES(element) ||
+ CAN_SMASH_PLAYER(element));
+
+ // set deadliness selectbox help value
+ custom_element.deadliness =
+ (DONT_TOUCH(element) ? EP_DONT_TOUCH :
+ DONT_GET_HIT_BY(element) ? EP_DONT_GET_HIT_BY :
+ DONT_COLLIDE_WITH(element) ? EP_DONT_COLLIDE_WITH :
+ DONT_RUN_INTO(element) ? EP_DONT_RUN_INTO :
+ custom_element.deadliness);
+ custom_element_properties[EP_DEADLY] =
+ (DONT_TOUCH(element) ||
+ DONT_GET_HIT_BY(element) ||
+ DONT_COLLIDE_WITH(element) ||
+ DONT_RUN_INTO(element));
+
+ // ---------- element settings: advanced (custom elements) ------------------
+
+ // set "change by direct action" selectbox help value
+ custom_element_change.direct_action =
+ (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 :
+ HAS_CHANGE_EVENT(element, CE_PUSHED_BY_PLAYER) ? CE_PUSHED_BY_PLAYER :
+ HAS_CHANGE_EVENT(element, CE_ENTERED_BY_PLAYER) ? CE_ENTERED_BY_PLAYER :
+ HAS_CHANGE_EVENT(element, CE_LEFT_BY_PLAYER) ? CE_LEFT_BY_PLAYER :
+ HAS_CHANGE_EVENT(element, CE_DROPPED_BY_PLAYER) ? CE_DROPPED_BY_PLAYER :
+ HAS_CHANGE_EVENT(element, CE_SWITCHED) ? CE_SWITCHED :
+ HAS_CHANGE_EVENT(element, CE_HITTING_SOMETHING) ? CE_HITTING_SOMETHING :
+ HAS_CHANGE_EVENT(element, CE_HIT_BY_SOMETHING) ? CE_HIT_BY_SOMETHING :
+ HAS_CHANGE_EVENT(element, CE_BLOCKED) ? CE_BLOCKED :
+ HAS_CHANGE_EVENT(element, CE_IMPACT) ? CE_IMPACT :
+ HAS_CHANGE_EVENT(element, CE_SMASHED) ? CE_SMASHED :
+ HAS_CHANGE_EVENT(element, CE_VALUE_CHANGES) ? CE_VALUE_CHANGES :
+ HAS_CHANGE_EVENT(element, CE_SCORE_CHANGES) ? CE_SCORE_CHANGES :
+ HAS_CHANGE_EVENT(element, CE_VALUE_GETS_ZERO) ? CE_VALUE_GETS_ZERO :
+ HAS_CHANGE_EVENT(element, CE_SCORE_GETS_ZERO) ? CE_SCORE_GETS_ZERO :
+ HAS_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) ? CE_CLICKED_BY_MOUSE :
+ HAS_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE) ? CE_PRESSED_BY_MOUSE :
+ custom_element_change.direct_action);
+
+ // set "change by other element action" selectbox help value
+ custom_element_change.other_action =
+ (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 :
+ HAS_CHANGE_EVENT(element, CE_PLAYER_PUSHES_X) ? CE_PLAYER_PUSHES_X :
+ HAS_CHANGE_EVENT(element, CE_PLAYER_ENTERS_X) ? CE_PLAYER_ENTERS_X :
+ HAS_CHANGE_EVENT(element, CE_PLAYER_LEAVES_X) ? CE_PLAYER_LEAVES_X :
+ 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 :
+ HAS_CHANGE_EVENT(element, CE_HIT_BY_X) ? CE_HIT_BY_X :
+ HAS_CHANGE_EVENT(element, CE_SWITCH_OF_X) ? CE_SWITCH_OF_X :
+ HAS_CHANGE_EVENT(element, CE_CHANGE_OF_X) ? CE_CHANGE_OF_X :
+ HAS_CHANGE_EVENT(element, CE_EXPLOSION_OF_X) ? CE_EXPLOSION_OF_X :
+ HAS_CHANGE_EVENT(element, CE_MOVE_OF_X) ? CE_MOVE_OF_X :
+ HAS_CHANGE_EVENT(element, CE_CREATION_OF_X) ? CE_CREATION_OF_X :
+ HAS_CHANGE_EVENT(element, CE_VALUE_CHANGES_OF_X) ? CE_VALUE_CHANGES_OF_X :
+ HAS_CHANGE_EVENT(element, CE_SCORE_CHANGES_OF_X) ? CE_SCORE_CHANGES_OF_X :
+ HAS_CHANGE_EVENT(element, CE_VALUE_GETS_ZERO_OF_X) ? CE_VALUE_GETS_ZERO_OF_X :
+ HAS_CHANGE_EVENT(element, CE_SCORE_GETS_ZERO_OF_X) ? CE_SCORE_GETS_ZERO_OF_X :
+ HAS_CHANGE_EVENT(element, CE_MOUSE_CLICKED_ON_X) ? CE_MOUSE_CLICKED_ON_X :
+ HAS_CHANGE_EVENT(element, CE_MOUSE_PRESSED_ON_X) ? CE_MOUSE_PRESSED_ON_X :
+ custom_element_change.other_action);
+}
+
+static void CopyGroupElementPropertiesToEditor(int element)
+{
+ group_element_info = *element_info[element].group;
+ custom_element = element_info[element]; // needed for description
+}
+
+static void CopyEmptyElementPropertiesToEditor(int element)
+{
+ custom_element = element_info[element];
+}
+
+static void CopyClassicElementPropertiesToEditor(int element)
+{
+ if (IS_PLAYER_ELEMENT(element) || COULD_MOVE_INTO_ACID(element))
+ custom_element_properties[EP_CAN_MOVE_INTO_ACID] =
+ getMoveIntoAcidProperty(&level, element);
+
+ if (MAYBE_DONT_COLLIDE_WITH(element))
+ custom_element_properties[EP_DONT_COLLIDE_WITH] =
+ getDontCollideWithProperty(&level, element);
+}
+
+static void CopyElementPropertiesToEditor(int element)
+{
+ if (IS_CUSTOM_ELEMENT(element))
+ CopyCustomElementPropertiesToEditor(element);
+ else if (IS_GROUP_ELEMENT(element))
+ CopyGroupElementPropertiesToEditor(element);
+ else if (IS_EMPTY_ELEMENT(element))
+ CopyEmptyElementPropertiesToEditor(element);
+ else
+ CopyClassicElementPropertiesToEditor(element);
+}
+
+static boolean AskToCopyAndModifyLevelTemplate(void)
+{
+ if (Request("Copy and modify settings from level template?", REQ_ASK))
+ {
+ level.use_custom_template = FALSE;
+
+ ModifyGadget(level_editor_gadget[GADGET_ID_CUSTOM_USE_TEMPLATE_1],
+ GDI_CHECKED, FALSE, GDI_END);
+ ModifyGadget(level_editor_gadget[GADGET_ID_CUSTOM_USE_TEMPLATE_2],
+ GDI_CHECKED, FALSE, GDI_END);
+
+ return TRUE;
+ }
+ else
+ {
+ LoadLevelTemplate(-1); // this resets all element modifications ...
+
+ DrawEditModeWindow(); // ... and copies them to 'custom_element'
+
+ return FALSE;
+ }
+}
+
+static void CopyCustomElementPropertiesToGame(int element)
+{
+ int i;
+ int access_type_and_layer;
+
+ // mark that this custom element has been modified
+ custom_element.modified_settings = TRUE;
+ level.changed = TRUE;
+
+ if (level.use_custom_template)
+ AskToCopyAndModifyLevelTemplate();
+
+ element_info[element] = custom_element;
+ *element_info[element].change = custom_element_change;
+
+ // ---------- element settings: configure (custom elements) -----------------
+
+ // set accessible property from checkbox and selectbox
+ custom_element_properties[EP_WALKABLE_OVER] = FALSE;
+ custom_element_properties[EP_WALKABLE_INSIDE] = FALSE;
+ custom_element_properties[EP_WALKABLE_UNDER] = FALSE;
+ custom_element_properties[EP_PASSABLE_OVER] = FALSE;
+ custom_element_properties[EP_PASSABLE_INSIDE] = FALSE;
+ custom_element_properties[EP_PASSABLE_UNDER] = FALSE;
+ access_type_and_layer = ((custom_element.access_type == EP_WALKABLE ?
+ EP_WALKABLE_OVER : EP_PASSABLE_OVER) +
+ (custom_element.access_layer - EP_ACCESSIBLE_OVER));
+ custom_element_properties[access_type_and_layer] =
+ custom_element_properties[EP_ACCESSIBLE];
+ custom_element_properties[EP_PROTECTED] =
+ (custom_element.access_protected != 0 &&
+ custom_element_properties[EP_ACCESSIBLE]);
+
+ // set walk-to-object property from checkbox and selectbox
+ custom_element_properties[EP_DIGGABLE] = FALSE;
+ custom_element_properties[EP_COLLECTIBLE_ONLY] = FALSE;
+ custom_element_properties[EP_DROPPABLE] = FALSE;
+ custom_element_properties[EP_THROWABLE] = FALSE;
+ custom_element_properties[EP_PUSHABLE] = FALSE;
+ custom_element_properties[custom_element.walk_to_action] =
+ custom_element_properties[EP_WALK_TO_OBJECT];
+
+ // set smash property from checkbox and selectbox
+ custom_element_properties[EP_CAN_SMASH_PLAYER] = FALSE;
+ custom_element_properties[EP_CAN_SMASH_ENEMIES] = FALSE;
+ custom_element_properties[EP_CAN_SMASH_EVERYTHING] = FALSE;
+ custom_element_properties[custom_element.smash_targets] =
+ custom_element_properties[EP_CAN_SMASH];
+
+ // set deadliness property from checkbox and selectbox
+ custom_element_properties[EP_DONT_RUN_INTO] = FALSE;
+ custom_element_properties[EP_DONT_COLLIDE_WITH] = FALSE;
+ custom_element_properties[EP_DONT_GET_HIT_BY] = FALSE;
+ custom_element_properties[EP_DONT_TOUCH] = FALSE;
+ custom_element_properties[custom_element.deadliness] =
+ custom_element_properties[EP_DEADLY];
+
+ // ---------- 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;
+ custom_element_change_events[CE_SNAPPED_BY_PLAYER] = FALSE;
+ custom_element_change_events[CE_PUSHED_BY_PLAYER] = FALSE;
+ custom_element_change_events[CE_ENTERED_BY_PLAYER] = FALSE;
+ custom_element_change_events[CE_LEFT_BY_PLAYER] = FALSE;
+ custom_element_change_events[CE_DROPPED_BY_PLAYER] = FALSE;
+ custom_element_change_events[CE_SWITCHED] = FALSE;
+ custom_element_change_events[CE_HITTING_SOMETHING] = FALSE;
+ custom_element_change_events[CE_HIT_BY_SOMETHING] = FALSE;
+ custom_element_change_events[CE_BLOCKED] = FALSE;
+ custom_element_change_events[CE_IMPACT] = FALSE;
+ custom_element_change_events[CE_SMASHED] = FALSE;
+ custom_element_change_events[CE_VALUE_CHANGES] = FALSE;
+ custom_element_change_events[CE_SCORE_CHANGES] = FALSE;
+ custom_element_change_events[CE_VALUE_GETS_ZERO] = FALSE;
+ custom_element_change_events[CE_SCORE_GETS_ZERO] = FALSE;
+ custom_element_change_events[CE_CLICKED_BY_MOUSE] = FALSE;
+ custom_element_change_events[CE_PRESSED_BY_MOUSE] = FALSE;
+ custom_element_change_events[custom_element_change.direct_action] =
+ 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;
+ custom_element_change_events[CE_PLAYER_SNAPS_X] = FALSE;
+ custom_element_change_events[CE_PLAYER_PUSHES_X] = FALSE;
+ custom_element_change_events[CE_PLAYER_ENTERS_X] = FALSE;
+ custom_element_change_events[CE_PLAYER_LEAVES_X] = FALSE;
+ 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;
+ custom_element_change_events[CE_HIT_BY_X] = FALSE;
+ custom_element_change_events[CE_SWITCH_OF_X] = FALSE;
+ custom_element_change_events[CE_CHANGE_OF_X] = FALSE;
+ custom_element_change_events[CE_EXPLOSION_OF_X] = FALSE;
+ custom_element_change_events[CE_MOVE_OF_X] = FALSE;
+ custom_element_change_events[CE_CREATION_OF_X] = FALSE;
+ custom_element_change_events[CE_VALUE_CHANGES_OF_X] = FALSE;
+ custom_element_change_events[CE_SCORE_CHANGES_OF_X] = FALSE;
+ custom_element_change_events[CE_VALUE_GETS_ZERO_OF_X] = FALSE;
+ custom_element_change_events[CE_SCORE_GETS_ZERO_OF_X] = FALSE;
+ custom_element_change_events[CE_MOUSE_CLICKED_ON_X] = FALSE;
+ custom_element_change_events[CE_MOUSE_PRESSED_ON_X] = FALSE;
+ custom_element_change_events[custom_element_change.other_action] =
+ custom_element_change_events[CE_BY_OTHER_ACTION];
+
+ for (i = 0; i < NUM_ELEMENT_PROPERTIES; i++)
+ SET_PROPERTY(element, i, custom_element_properties[i]);
+
+ for (i = 0; i < NUM_CHANGE_EVENTS; i++)
+ SET_CHANGE_EVENT(element, i, custom_element_change_events[i]);
+
+ // copy change events also to special level editor variable
+ custom_element = element_info[element];
+ custom_element_change = *element_info[element].change;
+
+ // needed here to restore runtime value "element_info[element].gfx_element"
+ InitElementPropertiesGfxElement();
+}
+
+static void CopyGroupElementPropertiesToGame(int element)
+{
+ // mark that this group element has been modified
+ custom_element.modified_settings = TRUE;
+ level.changed = TRUE;
+
+ if (level.use_custom_template)
+ AskToCopyAndModifyLevelTemplate();
+
+ element_info[element] = custom_element;
+ *element_info[element].group = group_element_info;
+
+ // needed here to restore runtime value "element_info[element].gfx_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 (IS_PLAYER_ELEMENT(element) || COULD_MOVE_INTO_ACID(element))
+ setMoveIntoAcidProperty(&level, element,
+ custom_element_properties[EP_CAN_MOVE_INTO_ACID]);
+
+ if (MAYBE_DONT_COLLIDE_WITH(element))
+ setDontCollideWithProperty(&level, element,
+ custom_element_properties[EP_DONT_COLLIDE_WITH]);
+}
+
+static void CopyElementPropertiesToGame(int element)
+{
+ if (IS_CUSTOM_ELEMENT(element))
+ CopyCustomElementPropertiesToGame(element);
+ else if (IS_GROUP_ELEMENT(element))
+ CopyGroupElementPropertiesToGame(element);
+ else if (IS_EMPTY_ELEMENT(element))
+ CopyEmptyElementPropertiesToGame(element);
+ else
+ CopyClassicElementPropertiesToGame(element);
+}
+
+#if DEBUG
+static void CheckElementDescriptions(void)
+{
+ int i;
+
+ for (i = 0; i < NUM_FILE_ELEMENTS; i++)
+ if (getElementDescriptionFilename(i) == NULL && !IS_OBSOLETE(i))
+ Warn("no element description file for element '%s'", EL_NAME(i));
+}
+#endif
+
+static int getMaxEdFieldX(boolean has_scrollbar)
+{
+ int scrollbar_width = (has_scrollbar ? ED_SCROLLBUTTON_XSIZE : 0);
+ int sxsize = SXSIZE - scrollbar_width;
+ int max_ed_fieldx = sxsize / ed_tilesize;
+
+ return max_ed_fieldx;
+}
+
+static int getMaxEdFieldY(boolean has_scrollbar)
+{
+ int infotext_height = (IN_PIX_FIELD(INFOTEXT_XPOS - SX, INFOTEXT_YPOS - SY) ?
+ INFOTEXT_YSIZE_FULL : 0);
+ int scrollbar_height = (has_scrollbar ? ED_SCROLLBUTTON_YSIZE : 0);
+ int sysize = SYSIZE - scrollbar_height - infotext_height;
+ int max_ed_fieldy = sysize / ed_tilesize;
+
+ return max_ed_fieldy;
+}
+
+static void InitZoomLevelSettings(int zoom_tilesize)
+{
+ static int last_game_engine_type = GAME_ENGINE_TYPE_UNKNOWN;
+
+ if (zoom_tilesize == -1 && level.game_engine_type != last_game_engine_type)
+ {
+ ed_tilesize = setup.auto_setup.editor_zoom_tilesize;
+ ed_tilesize_default = DEFAULT_EDITOR_TILESIZE;
+
+ // make sure that tile size is always a power of 2
+ ed_tilesize = (1 << log_2(ed_tilesize));
+
+ if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
+ {
+ ed_tilesize = DEFAULT_EDITOR_TILESIZE_MM;
+ ed_tilesize_default = DEFAULT_EDITOR_TILESIZE_MM;
+ }
+ }
+
+ last_game_engine_type = level.game_engine_type;
+
+ // limit zoom tilesize by upper and lower bound
+ ed_tilesize = MIN(MAX(MICRO_TILESIZE, ed_tilesize), TILESIZE);
+
+ // store zoom tilesize in auto setup file only if it was manually changed
+ if (zoom_tilesize != -1)
+ setup.auto_setup.editor_zoom_tilesize = ed_tilesize;
+
+ MAX_ED_FIELDX = getMaxEdFieldX(FALSE);
+ MAX_ED_FIELDY = getMaxEdFieldY(FALSE);
+}
+
+static void InitDrawingElements(void)
+{
+ static int game_engine_type_last = GAME_ENGINE_TYPE_UNKNOWN;
+
+ if (level.game_engine_type == game_engine_type_last)
+ return;
+
+ if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
+ {
+ new_element1 = EL_SP_CHIP_SINGLE;
+ new_element2 = EL_EMPTY;
+ new_element3 = EL_SP_BASE;
+ }
+ else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
+ {
+ new_element1 = EL_MM_MIRROR_START;
+ new_element2 = EL_EMPTY;
+ new_element3 = EL_MM_WOODEN_WALL;
+ }
+ else
+ {
+ new_element1 = EL_WALL;
+ new_element2 = EL_EMPTY;
+ new_element3 = EL_SAND;
+ }
+
+ game_engine_type_last = level.game_engine_type;
+}
+
+static void InitLevelSetInfo(void)
+{
+ snprintf(levelset_name, MAX_LEVEL_NAME_LEN + 1,
+ "%s", leveldir_current->name);
+ snprintf(levelset_author, MAX_LEVEL_AUTHOR_LEN + 1,
+ "%s", leveldir_current->author);
+
+ levelset_num_levels = leveldir_current->levels;
+
+ levelset_use_levelset_artwork = FALSE;
+ levelset_copy_level_template = FALSE;
+
+ levelset_save_mode = LEVELSET_SAVE_MODE_UPDATE;
+}
+
+static void ChangeEditorToLevelSet(char *levelset_subdir)
+{
+ leveldir_current = getTreeInfoFromIdentifier(leveldir_first, levelset_subdir);
+
+ // the previous level set might have used custom artwork
+ ReloadCustomArtwork(0);
+
+ LoadLevelSetup_SeriesInfo();
+
+ SaveLevelSetup_LastSeries();
+ SaveLevelSetup_SeriesInfo();
+
+ TapeErase();
+
+ LoadLevel(level_nr);
+ LoadScore(level_nr);
+
+ DrawLevelEd();
+}
+
+static boolean useEditorDoorAnimation(void)
+{
+ struct RectWithBorder *vp_door_1 = &viewport.door_1[GAME_MODE_MAIN];
+ boolean door_1_viewport_unchanged =
+ (vp_door_1->x == DX &&
+ vp_door_1->y == DY &&
+ vp_door_1->width == DXSIZE &&
+ vp_door_1->height == DYSIZE);
+ boolean door_1_contains_toolbox =
+ (EX >= DX &&
+ EY >= DY &&
+ EX + EXSIZE <= DX + DXSIZE &&
+ EY + EYSIZE <= DY + DYSIZE);
+
+ return (door_1_viewport_unchanged && door_1_contains_toolbox);
+}
+
+static void DrawEditorDoorBackground(int graphic, int x, int y,
+ int width, int height)
+{
+ struct GraphicInfo *g = &graphic_info[graphic];
+
+ if (g->bitmap != NULL)
+ BlitBitmap(g->bitmap, drawto, g->src_x, g->src_y,
+ MIN(width, g->width), MIN(height, g->height), x, y);
+ else
+ ClearRectangle(drawto, x, y, width, height);
+}
+
+static void DrawEditorDoorContent(void)
+{
+ // needed for gadgets drawn on background (like palette scrollbar)
+ SetDoorBackgroundImage(IMG_UNDEFINED);
+
+ // copy default editor door content to main double buffer
+ DrawEditorDoorBackground(IMG_BACKGROUND_PALETTE, DX, DY, DXSIZE, DYSIZE);
+
+ // draw bigger door
+ DrawSpecialEditorDoor();
+
+ // draw new control window
+ DrawEditorDoorBackground(IMG_BACKGROUND_TOOLBOX, EX, EY, EXSIZE, EYSIZE);
+
+ // draw all toolbox gadgets to editor doors
+ MapControlButtons();
+
+ // draw all palette gadgets to editor doors
+ ModifyEditorElementList();
+ RedrawDrawingElements();
+
+ // copy actual editor door content to door double buffer for OpenDoor()
+ BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
+}
+
+void DrawLevelEd(void)
+{
+ int fade_mask = REDRAW_FIELD;
+
+ FadeSoundsAndMusic();
+
+ if (CheckFadeAll())
+ fade_mask = REDRAW_ALL;
+
+ FadeOut(fade_mask);
+
+ // needed if different viewport properties defined for editor
+ ChangeViewportPropertiesIfNeeded();
+
+ ClearField();
+
+ InitZoomLevelSettings(-1);
+ InitDrawingElements();
+ InitLevelSetInfo();
+
+#if DEBUG
+ CheckElementDescriptions();
+#endif
+
+ if (level_editor_test_game)
+ {
+ CopyPlayfield(level.field, Tile);
+ CopyPlayfield(TileBackup, level.field);
+
+ level_editor_test_game = FALSE;
+ }
+ else
+ {
+ edit_mode = ED_MODE_DRAWING;
+ edit_mode_levelinfo = ED_MODE_LEVELINFO_LEVEL;
+ edit_mode_properties = ED_MODE_PROPERTIES_INFO;
+
+ ResetUndoBuffer();
+
+ level_xpos = -1;
+ level_ypos = -1;
+ }
+
+ // redraw_mask |= REDRAW_ALL;
+
+ FreeLevelEditorGadgets();
+ CreateLevelEditorGadgets();
+
+ ReinitializeElementList(); // update dynamic level element list
+ ReinitializeElementListButtons(); // custom element may look different
+
+ InitElementPropertiesGfxElement();
+
+ UnmapAllGadgets();
+
+ DrawEditModeWindow_PlayfieldOnly();
+
+ DrawMaskedBorder(fade_mask);
+
+ // use door animation if door 1 viewport is unchanged and contains toolbox
+ if (useEditorDoorAnimation())
+ {
+ FadeIn(fade_mask);
+
+ DrawEditorDoorContent();
+
+ OpenDoor(DOOR_OPEN_1 | DOOR_FORCE_ANIM);
+ }
+ else
+ {
+ DrawEditorDoorContent();
+
+ FadeIn(fade_mask);
+ }
+
+ SetDoorState(DOOR_OPEN_1 | DOOR_OPEN_2);
+}
+
+static void AdjustDrawingAreaGadgets(void)
+{
+ int ed_xsize = lev_fieldx + 2;
+ int ed_ysize = lev_fieldy + 2;
+ int max_ed_fieldx = MAX_ED_FIELDX;
+ int max_ed_fieldy = MAX_ED_FIELDY;
+ boolean horizontal_scrollbar_needed;
+ boolean vertical_scrollbar_needed;
+ int x, y, width, height;
+
+ if (suppressBorderElement())
+ {
+ ed_xsize = max_ed_fieldx;
+ ed_ysize = max_ed_fieldy;
+ }
+
+ // check if we need any scrollbars
+ horizontal_scrollbar_needed = (ed_xsize > max_ed_fieldx);
+ vertical_scrollbar_needed = (ed_ysize > max_ed_fieldy);
+
+ // check if we have a smaller editor field because of scrollbars
+ max_ed_fieldx = getMaxEdFieldX(vertical_scrollbar_needed);
+ max_ed_fieldy = getMaxEdFieldY(horizontal_scrollbar_needed);
+
+ // check again if we now need more scrollbars because of less space
+ horizontal_scrollbar_needed = (ed_xsize > max_ed_fieldx);
+ vertical_scrollbar_needed = (ed_ysize > max_ed_fieldy);
+
+ // check if editor field gets even smaller after adding new scrollbars
+ max_ed_fieldx = getMaxEdFieldX(vertical_scrollbar_needed);
+ max_ed_fieldy = getMaxEdFieldY(horizontal_scrollbar_needed);
+
+ ed_fieldx = (ed_xsize > max_ed_fieldx ? max_ed_fieldx : ed_xsize);
+ ed_fieldy = (ed_ysize > max_ed_fieldy ? max_ed_fieldy : ed_ysize);
+
+ x = SX + ed_fieldx * ed_tilesize;
+ y = SY + ed_fieldy * ed_tilesize;
+
+ width = ed_fieldx * ed_tilesize - 2 * ED_SCROLLBUTTON_XSIZE;
+ height = ed_fieldy * ed_tilesize - 2 * ED_SCROLLBUTTON_YSIZE;
+
+ // adjust drawing area gadget
+ ModifyGadget(level_editor_gadget[GADGET_ID_DRAWING_LEVEL],
+ GDI_AREA_SIZE, ed_fieldx, ed_fieldy,
+ GDI_ITEM_SIZE, ed_tilesize, ed_tilesize,
+ GDI_END);
+
+ // adjust horizontal scrollbar gadgets
+ ModifyGadget(level_editor_gadget[GADGET_ID_SCROLL_LEFT],
+ GDI_Y, y,
+ GDI_END);
+ ModifyGadget(level_editor_gadget[GADGET_ID_SCROLL_RIGHT],
+ GDI_X, x - ED_SCROLLBUTTON_XSIZE,
+ GDI_Y, y,
+ GDI_END);
+ ModifyGadget(level_editor_gadget[GADGET_ID_SCROLL_HORIZONTAL],
+ GDI_Y, y,
+ GDI_WIDTH, width,
+ GDI_SCROLLBAR_ITEMS_VISIBLE, ed_fieldx,
+ GDI_END);
+
+ // adjust vertical scrollbar gadgets
+ ModifyGadget(level_editor_gadget[GADGET_ID_SCROLL_UP],
+ GDI_X, x,
+ GDI_END);
+ ModifyGadget(level_editor_gadget[GADGET_ID_SCROLL_DOWN],
+ GDI_X, x,
+ GDI_Y, y - ED_SCROLLBUTTON_YSIZE,
+ GDI_END);
+ ModifyGadget(level_editor_gadget[GADGET_ID_SCROLL_VERTICAL],
+ GDI_X, x,
+ GDI_HEIGHT, height,
+ GDI_SCROLLBAR_ITEMS_VISIBLE, ed_fieldy,
+ GDI_END);
+}
+
+static void AdjustLevelScrollPosition(void)
+{
+ if (level_xpos < -1)
+ level_xpos = -1;
+ if (level_xpos > lev_fieldx - ed_fieldx + 1)
+ level_xpos = lev_fieldx - ed_fieldx + 1;
+ if (lev_fieldx < ed_fieldx - 2)
+ level_xpos = -1;
+
+ if (level_ypos < -1)
+ level_ypos = -1;
+ if (level_ypos > lev_fieldy - ed_fieldy + 1)
+ level_ypos = lev_fieldy - ed_fieldy + 1;
+ if (lev_fieldy < ed_fieldy - 2)
+ level_ypos = -1;
+
+ if (suppressBorderElement())
+ {
+ level_xpos = 0;
+ level_ypos = 0;
+ }
+}
+
+static void AdjustEditorScrollbar(int id)
+{
+ struct GadgetInfo *gi = level_editor_gadget[id];
+ int items_max, items_visible, item_position;
+
+ if (id == GADGET_ID_SCROLL_HORIZONTAL)
+ {
+ items_max = MAX(lev_fieldx + 2, ed_fieldx);
+ items_visible = ed_fieldx;
+ item_position = level_xpos + 1;
+ }
+ else
+ {
+ items_max = MAX(lev_fieldy + 2, ed_fieldy);
+ items_visible = ed_fieldy;
+ item_position = level_ypos + 1;
+ }
+
+ if (item_position > items_max - items_visible)
+ item_position = items_max - items_visible;
+
+ ModifyGadget(gi, GDI_SCROLLBAR_ITEMS_MAX, items_max,
+ GDI_SCROLLBAR_ITEM_POSITION, item_position, GDI_END);
+}
+
+static void AdjustElementListScrollbar(void)
+{
+ struct GadgetInfo *gi = level_editor_gadget[GADGET_ID_SCROLL_LIST_VERTICAL];
+ int items_max, items_visible, item_position;
+
+ // correct position of element list scrollbar
+ if (element_shift < 0)
+ element_shift = 0;
+ if (element_shift > num_editor_elements - ED_NUM_ELEMENTLIST_BUTTONS)
+ element_shift = num_editor_elements - ED_NUM_ELEMENTLIST_BUTTONS;
+
+ items_max = num_editor_elements / ED_ELEMENTLIST_BUTTONS_HORIZ;
+ items_visible = ED_ELEMENTLIST_BUTTONS_VERT;
+ item_position = element_shift / ED_ELEMENTLIST_BUTTONS_HORIZ;
+
+ ModifyGadget(gi, GDI_SCROLLBAR_ITEMS_MAX, items_max,
+ GDI_SCROLLBAR_ITEMS_VISIBLE, items_visible,
+ GDI_SCROLLBAR_ITEM_POSITION, item_position, GDI_END);
+}
+
+static void ModifyEditorCounterValue(int counter_id, int new_value)
+{
+ int *counter_value = counterbutton_info[counter_id].value;
+ int gadget_id = counterbutton_info[counter_id].gadget_id_text;
+ struct GadgetInfo *gi = level_editor_gadget[gadget_id];
+
+ ModifyGadget(gi, GDI_NUMBER_VALUE, new_value, GDI_END);
+
+ if (counter_value != NULL)
+ *counter_value = gi->textinput.number_value;
+}
+
+static void ModifyEditorCounterLimits(int counter_id, int min, int max)
+{
+ int gadget_id = counterbutton_info[counter_id].gadget_id_text;
+ struct GadgetInfo *gi = level_editor_gadget[gadget_id];
+
+ ModifyGadget(gi, GDI_NUMBER_MIN, min, GDI_NUMBER_MAX, max, GDI_END);
+
+ if (counter_id >= ED_COUNTER_ID_ELEMENT_VALUE1 &&
+ counter_id <= ED_COUNTER_ID_ELEMENT_VALUE4)
+ {
+ int gadget_id_up = counterbutton_info[counter_id].gadget_id_up;
+ struct GadgetInfo *gi_up = level_editor_gadget[gadget_id_up];
+
+ ModifyGadget(gi, GDI_TEXT_SIZE, (max < 10 ? 1 : 3), GDI_END);
+ ModifyGadget(gi_up, GDI_X, gi->x + gi->width + ED_GADGET_SMALL_DISTANCE,
+ GDI_END);
+ }
+}
+
+static void ModifyEditorSelectboxValue(int selectbox_id, int new_value)
+{
+ int gadget_id = selectbox_info[selectbox_id].gadget_id;
+ struct GadgetInfo *gi = level_editor_gadget[gadget_id];
+ int new_index_value = setSelectboxValue(selectbox_id, new_value);
+
+ ModifyGadget(gi, GDI_SELECTBOX_INDEX, new_index_value, GDI_END);
+}
+
+static void ModifyEditorSelectboxOptions(int selectbox_id,
+ struct ValueTextInfo *options)
+{
+ int gadget_id = selectbox_info[selectbox_id].gadget_id;
+ struct GadgetInfo *gi = level_editor_gadget[gadget_id];
+
+ selectbox_info[selectbox_id].options = options;
+
+ // set index to zero -- list may be shorter now (correct later, if needed)
+ ModifyGadget(gi, GDI_SELECTBOX_INDEX, 0,
+ GDI_SELECTBOX_OPTIONS, options, GDI_END);
+}
+
+static void ModifyEditorDrawingArea(int drawingarea_id, int xsize, int ysize)
+{
+ int gadget_id = drawingarea_info[drawingarea_id].gadget_id;
+ struct GadgetInfo *gi = level_editor_gadget[gadget_id];
+
+ drawingarea_info[drawingarea_id].area_xsize = xsize;
+ drawingarea_info[drawingarea_id].area_ysize = ysize;
+
+ ModifyGadget(gi, GDI_AREA_SIZE, xsize, ysize, GDI_END);
+}
+
+static void ModifyEditorElementList(void)
+{
+ int i;
+
+ if (!use_permanent_palette && edit_mode != ED_MODE_PALETTE)
+ return;
+
+ for (i = 0; i < ED_NUM_ELEMENTLIST_BUTTONS; i++)
+ {
+ int gadget_id = GADGET_ID_ELEMENTLIST_FIRST + i;
+ struct GadgetInfo *gi = level_editor_gadget[gadget_id];
+ struct GadgetDesign *gd = &gi->deco.design;
+ int element = editor_elements[element_shift + i];
+ int tile_size = BUTTON_TILE_SIZE(editor.palette.tile_size);
+
+ UnmapGadget(gi);
+
+ getEditorGraphicSource(element, tile_size, &gd->bitmap, &gd->x, &gd->y);
+
+ ModifyGadget(gi, GDI_INFO_TEXT, getElementInfoText(element), GDI_END);
+
+ MapGadget(gi);
+ }
+}
+
+static void DrawDrawingElementGraphic(int element, struct XYTileSize *pos)
+{
+ int graphic = el2edimg(element);
+ int tile_size = BUTTON_TILE_SIZE(pos->tile_size);
+
+ if (pos->x == -1 &&
+ pos->y == -1)
+ return;
+
+ DrawSizedGraphicExt(drawto, DX + pos->x, DY + pos->y, graphic, 0, tile_size);
+}
+
+static void ModifyDrawingElementButton(int element, int id)
+{
+ struct GadgetInfo *gi = level_editor_gadget[id];
+ Bitmap *deco_bitmap;
+ int deco_x, deco_y;
+ int tile_size = gi->deco.width;
+
+ getEditorGraphicSource(element, tile_size, &deco_bitmap, &deco_x, &deco_y);
+
+ ModifyGadget(gi, GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y, GDI_END);
+}
+
+static void PickDrawingElement(int button, int element)
+{
+ struct
+ {
+ int *new_element;
+ struct XYTileSize *pos;
+ int id;
+ } de, drawing_elements[] =
+ {
+ { &new_element1, &editor.palette.element_left, GADGET_ID_ELEMENT_LEFT },
+ { &new_element2, &editor.palette.element_middle, GADGET_ID_ELEMENT_MIDDLE },
+ { &new_element3, &editor.palette.element_right, GADGET_ID_ELEMENT_RIGHT },
+ };
+
+ if (button < 1 || button > 3)
+ return;
+
+ if (IS_MM_WALL(element))
+ element = map_mm_wall_element(element);
+
+ de = drawing_elements[button - 1];
+
+ *de.new_element = element; // update global drawing element variable
+
+ DrawDrawingElementGraphic(element, de.pos);
+ ModifyDrawingElementButton(element, de.id);
+
+ redraw_mask |= REDRAW_DOOR_1;
+}
+
+static void RedrawDrawingElements(void)
+{
+ PickDrawingElement(1, new_element1);
+ PickDrawingElement(2, new_element2);
+ PickDrawingElement(3, new_element3);
+}
+
+static void DrawDrawingWindowExt(boolean remap_toolbox_gadgets)
+{
+ stick_element_properties_window = FALSE;
+
+ SetMainBackgroundImage(IMG_UNDEFINED);
+ ClearField();
+
+ UnmapLevelEditorFieldGadgets();
+
+ AdjustDrawingAreaGadgets();
+ AdjustLevelScrollPosition();
+ AdjustEditorScrollbar(GADGET_ID_SCROLL_HORIZONTAL);
+ AdjustEditorScrollbar(GADGET_ID_SCROLL_VERTICAL);
+
+ DrawEditorLevel(ed_fieldx, ed_fieldy, level_xpos, level_ypos);
+
+ MapMainDrawingArea();
+
+ if (remap_toolbox_gadgets)
+ {
+ UnmapLevelEditorToolboxCustomGadgets();
+ MapLevelEditorToolboxDrawingGadgets();
+ }
+}
+
+static void DrawDrawingWindow(void)
+{
+ DrawDrawingWindowExt(TRUE);
+}
+
+static int getTabulatorBarWidth(void)
+{
+ struct GadgetInfo *gd_gi1 = level_editor_gadget[GADGET_ID_PROPERTIES_INFO];
+ struct GadgetInfo *gd_gi4 = level_editor_gadget[GADGET_ID_PROPERTIES_CHANGE];
+
+ return gd_gi4->x - gd_gi1->x + gd_gi4->width;
+}
+
+static int getTabulatorBarHeight(void)
+{
+ return ED_TAB_BAR_HEIGHT;
+}
+
+static Pixel getTabulatorBarColor(void)
+{
+ struct GadgetInfo *gd_gi1 = level_editor_gadget[GADGET_ID_LEVELINFO_LEVEL];
+ struct GadgetDesign *gd = &gd_gi1->alt_design[GD_BUTTON_UNPRESSED];
+ int gd_x = gd->x + gd_gi1->border.width / 2;
+ int gd_y = gd->y + gd_gi1->height - 1;
+
+ return GetPixel(gd->bitmap, gd_x, gd_y);
+}
+
+static void DrawLevelInfoTabulatorGadgets(void)
+{
+ struct GadgetInfo *gd_gi1 = level_editor_gadget[GADGET_ID_LEVELINFO_LEVEL];
+ Pixel tab_color = getTabulatorBarColor();
+ int id_first = ED_TAB_BUTTON_ID_LEVELINFO_FIRST;
+ int id_last = ED_TAB_BUTTON_ID_LEVELINFO_LAST;
+ int i;
+
+ for (i = id_first; i <= id_last; i++)
+ {
+ int gadget_id = textbutton_info[i].gadget_id;
+ struct GadgetInfo *gi = level_editor_gadget[gadget_id];
+ boolean active = (i != edit_mode_levelinfo);
+
+ // draw background line below tabulator button
+ ClearRectangleOnBackground(drawto, gi->x, gi->y + gi->height, gi->width, 1);
+
+ // draw solid line below inactive tabulator buttons
+ if (!active && tab_color != BLACK_PIXEL) // black => transparent
+ FillRectangle(drawto, gi->x, gi->y + gi->height, gi->width,
+ ED_GADGET_TINY_DISTANCE, tab_color);
+
+ ModifyGadget(gi, GDI_ACTIVE, active, GDI_END);
+ MapTextbuttonGadget(i);
+ }
+
+ // draw little border line below tabulator buttons
+ if (tab_color != BLACK_PIXEL) // black => transparent
+ FillRectangle(drawto, gd_gi1->x, gd_gi1->y + gd_gi1->height +
+ ED_GADGET_TINY_DISTANCE,
+ getTabulatorBarWidth(), getTabulatorBarHeight(), tab_color);
+}
+
+static void DrawPropertiesTabulatorGadgets(void)
+{
+ struct GadgetInfo *gd_gi1 = level_editor_gadget[GADGET_ID_PROPERTIES_INFO];
+ struct GadgetDesign *gd = &gd_gi1->alt_design[GD_BUTTON_UNPRESSED];
+ int gd_x = gd->x + gd_gi1->border.width / 2;
+ int gd_y = gd->y + gd_gi1->height - 1;
+ Pixel tab_color = GetPixel(gd->bitmap, gd_x, gd_y);
+ int id_first = ED_TEXTBUTTON_ID_PROPERTIES_INFO;
+ int id_last = ED_TEXTBUTTON_ID_PROPERTIES_CONFIG;
+ int i;
+
+ // draw two config tabulators for player elements
+ if (IS_PLAYER_ELEMENT(properties_element))
+ id_last = ED_TEXTBUTTON_ID_PROPERTIES_CONFIG_2;
+
+ // draw two config and one "change" tabulator for custom elements
+ if (IS_CUSTOM_ELEMENT(properties_element))
+ id_last = ED_TEXTBUTTON_ID_PROPERTIES_CHANGE;
+
+ for (i = id_first; i <= id_last; i++)
+ {
+ int gadget_id = textbutton_info[i].gadget_id;
+ struct GadgetInfo *gi = level_editor_gadget[gadget_id];
+ boolean active = (i != edit_mode_properties);
+
+ // use "config 1" and "config 2" instead of "config" for players and CEs
+ if (i == ED_TEXTBUTTON_ID_PROPERTIES_CONFIG &&
+ (IS_PLAYER_ELEMENT(properties_element) ||
+ IS_CUSTOM_ELEMENT(properties_element)))
+ continue;
+
+ // draw background line below tabulator button
+ ClearRectangleOnBackground(drawto, gi->x, gi->y + gi->height, gi->width, 1);
+
+ // draw solid line below inactive tabulator buttons
+ if (!active && tab_color != BLACK_PIXEL) // black => transparent
+ FillRectangle(drawto, gi->x, gi->y + gi->height, gi->width,
+ ED_GADGET_TINY_DISTANCE, tab_color);
+
+ ModifyGadget(gi, GDI_ACTIVE, active, GDI_END);
+ MapTextbuttonGadget(i);
+ }
+
+ // draw little border line below tabulator buttons
+ if (tab_color != BLACK_PIXEL) // black => transparent
+ FillRectangle(drawto, gd_gi1->x, gd_gi1->y + gd_gi1->height +
+ ED_GADGET_TINY_DISTANCE,
+ getTabulatorBarWidth(), getTabulatorBarHeight(), tab_color);
+}
+
+static void PrintInfoText(char *text, int font_nr, int xpos, int ypos)
+{
+ DrawText(SX + xpos, SY + ypos, text, font_nr);
+}
+
+static int PrintElementDescriptionFromFile(char *filename, int font_nr,
+ int xpos, int ypos)
+{
+ int font_width = getFontWidth(font_nr);
+ int font_height = getFontHeight(font_nr);
+ int max_chars_per_line = (SXSIZE - 2 * xpos) / font_width;
+ int max_lines_drawable = (SYSIZE - ypos) / font_height - 1;
+
+ return DrawTextFile(SX + xpos, SY + ypos, filename, font_nr,
+ max_chars_per_line, -1, max_lines_drawable, 0, -1,
+ TRUE, FALSE, FALSE);
+}
+
+static void DrawLevelInfoLevel(void)
+{
+ int i;
+
+ // draw counter gadgets
+ for (i = ED_COUNTER_ID_LEVEL_FIRST; i <= ED_COUNTER_ID_LEVEL_LAST; i++)
+ MapCounterButtons(i);
+
+ // draw checkbutton gadgets
+ for (i = ED_CHECKBUTTON_ID_LEVEL_FIRST; i<= ED_CHECKBUTTON_ID_LEVEL_LAST; i++)
+ MapCheckbuttonGadget(i);
+
+ // draw selectbox gadgets
+ for (i = ED_SELECTBOX_ID_LEVEL_FIRST; i <= ED_SELECTBOX_ID_LEVEL_LAST; i++)
+ MapSelectboxGadget(i);
+
+ // draw text input gadgets
+ for (i = ED_TEXTINPUT_ID_LEVEL_FIRST; i <= ED_TEXTINPUT_ID_LEVEL_LAST; i++)
+ MapTextInputGadget(i);
+}
+
+static char *getLevelSubdirFromSaveMode(int save_mode)
+{
+ if (save_mode == LEVELSET_SAVE_MODE_CREATE)
+ return getNewUserLevelSubdir();
+
+ return leveldir_current->subdir;
+}
+
+static void DrawLevelInfoLevelSet_DirectoryInfo(void)
+{
+ char *directory_text = "Level set directory:";
+ char *directory_name = getLevelSubdirFromSaveMode(levelset_save_mode);
+ int font1_nr = FONT_TEXT_1;
+ int font2_nr = FONT_TEXT_2;
+ int font1_height = getFontHeight(font1_nr);
+ int yoffset_above = font1_height + ED_GADGET_LINE_DISTANCE;
+ int x = ED_LEVEL_SETTINGS_X(0);
+ int y = ED_LEVEL_SETTINGS_Y(6);
+
+ PrintInfoText(directory_text, font1_nr, x, y - yoffset_above);
+ PrintInfoText(directory_name, font2_nr, x, y);
+}
+
+static void DrawLevelInfoLevelSet(void)
+{
+ boolean artwork_exists = checkIfCustomArtworkExistsForCurrentLevelSet();
+ boolean template_exists = fileExists(getLocalLevelTemplateFilename());
+ int i;
+
+ // draw counter gadgets
+ for (i = ED_COUNTER_ID_LEVELSET_FIRST; i <= ED_COUNTER_ID_LEVELSET_LAST; i++)
+ MapCounterButtons(i);
+
+ // draw checkbutton gadgets
+ for (i = ED_CHECKBUTTON_ID_LEVELSET_FIRST; i <= ED_CHECKBUTTON_ID_LEVELSET_LAST; i++)
+ {
+ if (levelset_save_mode == LEVELSET_SAVE_MODE_UPDATE ||
+ (i == ED_CHECKBUTTON_ID_USE_LEVELSET_ARTWORK && !artwork_exists) ||
+ (i == ED_CHECKBUTTON_ID_COPY_LEVEL_TEMPLATE && !template_exists))
+ continue;
+
+ MapCheckbuttonGadget(i);
+ }
+
+ // draw selectbox gadgets
+ for (i = ED_SELECTBOX_ID_LEVELSET_FIRST; i <= ED_SELECTBOX_ID_LEVELSET_LAST; i++)
+ MapSelectboxGadget(i);
+
+ // draw text input gadgets
+ for (i = ED_TEXTINPUT_ID_LEVELSET_FIRST; i <= ED_TEXTINPUT_ID_LEVELSET_LAST; i++)
+ MapTextInputGadget(i);
+
+ // draw textbutton gadgets
+ MapTextbuttonGadget(ED_TEXTBUTTON_ID_SAVE_LEVELSET);
+
+ // draw info text
+ DrawLevelInfoLevelSet_DirectoryInfo();
+}
+
+static void DrawLevelInfoEditor(void)
+{
+ int i;
+
+ // draw counter gadgets
+ for (i = ED_COUNTER_ID_EDITOR_FIRST; i <= ED_COUNTER_ID_EDITOR_LAST; i++)
+ MapCounterButtons(i);
+
+ // draw checkbutton gadgets
+ for (i=ED_CHECKBUTTON_ID_EDITOR_FIRST; i<= ED_CHECKBUTTON_ID_EDITOR_LAST; i++)
+ MapCheckbuttonGadget(i);
+
+ // draw radiobutton gadgets
+ for (i=ED_RADIOBUTTON_ID_EDITOR_FIRST; i<= ED_RADIOBUTTON_ID_EDITOR_LAST; i++)
+ MapRadiobuttonGadget(i);
+
+ // draw drawing area
+ MapDrawingArea(ED_DRAWING_ID_RANDOM_BACKGROUND);
+
+ // draw textbutton gadgets
+ MapTextbuttonGadget(ED_TEXTBUTTON_ID_SAVE_AS_TEMPLATE_2);
+}
+
+static void DrawLevelInfoWindow(void)
+{
+ char *text = "Global Settings";
+ int font_nr = FONT_TITLE_1;
+ struct MenuPosInfo *pos = &editor.settings.headline;
+ int sx = SX + ALIGNED_XPOS(pos->x, getTextWidth(text, font_nr), pos->align);
+ int sy = SY + pos->y;
+
+ stick_element_properties_window = FALSE;
+
+ SetAutomaticNumberOfGemsNeeded();
+
+ UnmapLevelEditorFieldGadgets();
+
+ SetMainBackgroundImage(IMG_BACKGROUND_EDITOR);
+ ClearField();
+
+ DrawText(sx, sy, text, font_nr);
+
+ DrawLevelInfoTabulatorGadgets();
+
+ if (edit_mode_levelinfo == ED_MODE_LEVELINFO_LEVEL)
+ DrawLevelInfoLevel();
+ else if (edit_mode_levelinfo == ED_MODE_LEVELINFO_LEVELSET)
+ DrawLevelInfoLevelSet();
+ else if (edit_mode_levelinfo == ED_MODE_LEVELINFO_EDITOR)
+ DrawLevelInfoEditor();
+}
+
+static void DrawCustomContentArea(void)
+{
+ int id = ED_DRAWING_ID_CUSTOM_CONTENT;
+ struct GadgetInfo *gi = level_editor_gadget[drawingarea_info[id].gadget_id];
+ int x1 = right_gadget_border[GADGET_ID_CUSTOM_DEADLINESS];
+ int x2 = right_gadget_border[GADGET_ID_CUSTOM_EXPLOSION_TYPE];
+ int x3 = right_gadget_border[GADGET_ID_CUSTOM_EXPLODE_IMPACT];
+ int xoffset = ED_GADGET_SPACE_DISTANCE;
+
+ // add distance for potential left text (without drawing area border)
+ x2 += getTextWidthForGadget(drawingarea_info[id].text_left);
+
+ ModifyGadget(gi, GDI_X, MAX(x1, MAX(x2, x3)) + xoffset, GDI_END);
+
+ MapDrawingArea(ED_DRAWING_ID_CUSTOM_CONTENT);
+}
+
+static void DrawCustomChangeContentArea(void)
+{
+ int id = ED_DRAWING_ID_CUSTOM_CHANGE_CONTENT;
+ struct GadgetInfo *gi = level_editor_gadget[drawingarea_info[id].gadget_id];
+ int x1 = right_gadget_border[GADGET_ID_CHANGE_USE_CONTENT];
+ int x2 = right_gadget_border[GADGET_ID_CHANGE_REPLACE_WHEN];
+ int x3 = right_gadget_border[GADGET_ID_CHANGE_ONLY_COMPLETE];
+ int xoffset = ED_GADGET_SPACE_DISTANCE;
+
+ ModifyGadget(gi, GDI_X, MAX(x1, MAX(x2, x3)) + xoffset, GDI_END);
+
+ MapDrawingArea(id);
+}
+
+static void RemoveElementContentArea(int id, int font_height)
+{
+ int border_size = ED_DRAWINGAREA_BORDER_SIZE;
+
+ DrawBackground(SX + ED_AREA_SETTINGS_X(drawingarea_info[id]) - border_size,
+ SY + ED_AREA_SETTINGS_Y(drawingarea_info[id]) - border_size,
+ 3 * ED_DRAWINGAREA_TILE_SIZE + 2 * border_size,
+ 3 * ED_DRAWINGAREA_TILE_SIZE + 2 * border_size +
+ ED_GADGET_TEXT_DISTANCE + font_height);
+}
+
+static void DrawYamYamContentAreas(void)
+{
+ int font_nr = FONT_TEXT_1;
+ int font_height = getFontHeight(font_nr);
+ int tilesize = ED_DRAWINGAREA_TILE_SIZE;
+ int yoffset = (tilesize - font_height) / 2;
+ int x = SX + ED_AREA_YAMYAM_CONTENT_X(3) + 4 * tilesize;
+ int y = SY + ED_AREA_YAMYAM_CONTENT_Y(3) + yoffset;
+ int i;
+
+ // display counter to choose number of element content areas
+ MapCounterButtons(ED_COUNTER_ID_YAMYAM_CONTENT);
+
+ for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
+ {
+ int id = ED_DRAWING_ID_YAMYAM_CONTENT_0 + i;
+
+ if (i < level.num_yamyam_contents)
+ {
+ MapDrawingArea(id);
+ }
+ else
+ {
+ UnmapDrawingArea(id);
+
+ // delete content areas in case of reducing number of them
+ RemoveElementContentArea(id, font_height);
+ }
+ }
+
+ DrawText(x, y + 0 * tilesize, "content", font_nr);
+ DrawText(x, y + 1 * tilesize, "when", font_nr);
+ DrawText(x, y + 2 * tilesize, "smashed", font_nr);
+}
+
+static void DrawMagicBallContentAreas(void)
+{
+ int font_nr = FONT_TEXT_1;
+ int font_height = getFontHeight(font_nr);
+ int tilesize = ED_DRAWINGAREA_TILE_SIZE;
+ int yoffset = (tilesize - font_height) / 2;
+ int x = SX + ED_AREA_MAGIC_BALL_CONTENT_X(3) + 4 * tilesize;
+ int y = SY + ED_AREA_MAGIC_BALL_CONTENT_Y(3) + yoffset;
+ int i;
+
+ // display counter to choose number of element content areas
+ MapCounterButtons(ED_COUNTER_ID_BALL_CONTENT);
+
+ for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
+ {
+ int id = ED_DRAWING_ID_MAGIC_BALL_CONTENT_0 + i;
+
+ if (i < level.num_ball_contents)
+ {
+ MapDrawingArea(id);
+ }
+ else
+ {
+ UnmapDrawingArea(id);
+
+ // delete content areas in case of reducing number of them
+ RemoveElementContentArea(id, font_height);
+ }
+ }
+
+ DrawText(x, y + 0 * tilesize, "generated", font_nr);
+ DrawText(x, y + 1 * tilesize, "when", font_nr);
+ DrawText(x, y + 2 * tilesize, "active", font_nr);
+}
+
+static void DrawAndroidElementArea(int element)
+{
+ int id = ED_DRAWING_ID_ANDROID_CONTENT;
+ int num_elements = level.num_android_clone_elements;
+ int border_size = ED_DRAWINGAREA_BORDER_SIZE;
+ int sx = SX + ED_AREA_SETTINGS_X(drawingarea_info[id]) - border_size;
+ int sy = SY + ED_AREA_SETTINGS_Y(drawingarea_info[id]) - border_size;
+ int xsize = MAX_ANDROID_ELEMENTS;
+ int ysize = 1;
+
+ // display counter to choose number of element areas
+ MapCounterButtons(ED_COUNTER_ID_ANDROID_CONTENT);
+
+ if (drawingarea_info[id].text_left != NULL)
+ sx += getTextWidthForDrawingArea(drawingarea_info[id].text_left);
+
+ UnmapDrawingArea(id);
+
+ ModifyEditorDrawingArea(id, num_elements, 1);
+
+ // delete content areas in case of reducing number of them
+ DrawBackground(sx, sy,
+ xsize * ED_DRAWINGAREA_TILE_SIZE + 2 * border_size,
+ ysize * ED_DRAWINGAREA_TILE_SIZE + 2 * border_size);
+
+ MapDrawingArea(id);
+}
+
+static void DrawGroupElementArea(int element)
+{
+ int id = ED_DRAWING_ID_GROUP_CONTENT;
+ int num_elements = group_element_info.num_elements;
+ int border_size = ED_DRAWINGAREA_BORDER_SIZE;
+ int sx = SX + ED_AREA_SETTINGS_X(drawingarea_info[id]) - border_size;
+ int sy = SY + ED_AREA_SETTINGS_Y(drawingarea_info[id]) - border_size;
+ int xsize = MAX_ELEMENTS_IN_GROUP;
+ int ysize = 1;
+
+ if (drawingarea_info[id].text_left != NULL)
+ sx += getTextWidthForDrawingArea(drawingarea_info[id].text_left);
+
+ UnmapDrawingArea(id);
+
+ ModifyEditorDrawingArea(id, num_elements, 1);
+
+ // delete content areas in case of reducing number of them
+ DrawBackground(sx, sy,
+ xsize * ED_DRAWINGAREA_TILE_SIZE + 2 * border_size,
+ ysize * ED_DRAWINGAREA_TILE_SIZE + 2 * border_size);
+
+ MapDrawingArea(id);
+}
+
+static void DrawPlayerInitialInventoryArea(int element)
+{
+ int id = ED_DRAWING_ID_INVENTORY_CONTENT;
+ int player_nr = GET_PLAYER_NR(element);
+ int num_elements = level.initial_inventory_size[player_nr];
+ int border_size = ED_DRAWINGAREA_BORDER_SIZE;
+ int sx = SX + ED_AREA_SETTINGS_X(drawingarea_info[id]) - border_size;
+ int sy = SY + ED_AREA_SETTINGS_Y(drawingarea_info[id]) - border_size;
+ int xsize = MAX_INITIAL_INVENTORY_SIZE;
+ int ysize = 1;
+
+ // determine horizontal position to the right of specified gadget
+ if (drawingarea_info[id].gadget_id_align != GADGET_ID_NONE)
+ sx = (right_gadget_border[drawingarea_info[id].gadget_id_align] +
+ ED_DRAWINGAREA_TEXT_DISTANCE);
+
+ // determine horizontal offset for leading text
+ if (drawingarea_info[id].text_left != NULL)
+ sx += getTextWidthForDrawingArea(drawingarea_info[id].text_left);
+
+ UnmapDrawingArea(id);
+
+ ModifyEditorDrawingArea(id, num_elements, 1);
+
+ // delete content areas in case of reducing number of them
+ DrawBackground(sx, sy,
+ xsize * ED_DRAWINGAREA_TILE_SIZE + 2 * border_size,
+ ysize * ED_DRAWINGAREA_TILE_SIZE + 2 * border_size);
+
+ MapDrawingArea(id);
+}
+
+static void DrawEnvelopeTextArea(int envelope_nr)
+{
+ int id = ED_TEXTAREA_ID_ENVELOPE_INFO;
+ struct GadgetInfo *gi = level_editor_gadget[textarea_info[id].gadget_id];
+
+ UnmapGadget(gi);
+ DrawBackground(gi->x, gi->y, gi->width, gi->height);
+
+ if (envelope_nr != -1)
+ textarea_info[id].value = level.envelope[envelope_nr].text;
+
+ ModifyGadget(gi, GDI_AREA_SIZE,
+ *counterbutton_info[ED_COUNTER_ID_ENVELOPE_XSIZE].value,
+ *counterbutton_info[ED_COUNTER_ID_ENVELOPE_YSIZE].value,
+ GDI_END);
+
+ MapTextAreaGadget(ED_TEXTAREA_ID_ENVELOPE_INFO);
+}
+
+static void DrawPropertiesInfo(void)
+{
+ static struct
+ {
+ int value;
+ char *text;
+ }
+ properties[] =
+ {
+ // configurable properties
+
+ { EP_WALKABLE_OVER, "- player can walk over it" },
+ { EP_WALKABLE_INSIDE, "- player can walk inside it" },
+ { EP_WALKABLE_UNDER, "- player can walk under it" },
+ { EP_PASSABLE_OVER, "- player can pass over it" },
+ { EP_PASSABLE_INSIDE, "- player can pass through it" },
+ { EP_PASSABLE_UNDER, "- player can pass under it" },
+ { EP_PROTECTED, "- player is protected by it" },
+
+ { EP_DIGGABLE, "- can be digged away" },
+ { EP_COLLECTIBLE, "- can be collected" },
+ { EP_DROPPABLE, "- can be dropped after collecting" },
+ { EP_THROWABLE, "- can be thrown after collecting" },
+ { EP_PUSHABLE, "- can be pushed" },
+
+ { EP_CAN_FALL, "- can fall" },
+ { EP_CAN_MOVE, "- can move" },
+
+ { EP_CAN_SMASH_PLAYER, "- can smash player" },
+#if 0
+ { EP_CAN_SMASH_ENEMIES, "- can smash good and bad guys" },
+#endif
+ { EP_CAN_SMASH_EVERYTHING, "- can smash everything smashable" },
+
+ { EP_SLIPPERY, "- slippery for falling elements" },
+ { EP_EM_SLIPPERY_WALL, "- slippery for some gems (EM style)" },
+
+ { EP_DONT_RUN_INTO, "- deadly when running into" },
+ { EP_DONT_COLLIDE_WITH, "- deadly when colliding with" },
+ { EP_DONT_GET_HIT_BY, "- deadly when getting hit by" },
+ { EP_DONT_TOUCH, "- deadly when touching" },
+
+ { EP_INDESTRUCTIBLE, "- indestructible" },
+
+ { EP_CAN_EXPLODE_BY_FIRE, "- can explode by fire or explosions" },
+ { EP_CAN_EXPLODE_SMASHED, "- can explode when smashed" },
+ { EP_CAN_EXPLODE_IMPACT, "- can explode on impact" },
+
+ { EP_CAN_CHANGE, "- can change to other element" },
+
+ // pre-defined properties
+ { EP_CAN_PASS_MAGIC_WALL, "- can pass magic walls" },
+ { EP_CAN_PASS_DC_MAGIC_WALL,"- can pass magic walls (DC style)" },
+ { EP_SWITCHABLE, "- can be switched" },
+#if 0
+ { EP_HAS_EDITOR_CONTENT, "- can contain other elements" },
+#endif
+
+ { -1, NULL }
+ };
+ char *filename = getElementDescriptionFilename(properties_element);
+ char *percentage_text = "In this level: ";
+ char *properties_text = "Standard properties: ";
+ char *description_text = "Description:";
+ char *no_description_text = "No description available.";
+ char *none_text = "None";
+ float percentage;
+ int num_elements_in_level;
+ int num_standard_properties = 0;
+ int font1_nr = FONT_TEXT_1;
+ int font2_nr = FONT_TEXT_2;
+ int font1_width = getFontWidth(font1_nr);
+ int font1_height = getFontHeight(font1_nr);
+ int font2_height = getFontHeight(font2_nr);
+ int line1_height = font1_height + ED_GADGET_LINE_DISTANCE;
+ int font2_yoffset = (font1_height - font2_height) / 2;
+ int percentage_text_len = strlen(percentage_text) * font1_width;
+ int properties_text_len = strlen(properties_text) * font1_width;
+ int xpos = ED_ELEMENT_SETTINGS_X(0);
+ int ypos = ED_ELEMENT_SETTINGS_Y(0) + ED_GADGET_SMALL_DISTANCE;
+ int i, x, y;
+
+ if (setup.editor.show_element_token)
+ {
+ int font3_nr = FONT_TEXT_3;
+ int font3_height = getFontHeight(font3_nr);
+
+ DrawTextF(xpos, ypos, font3_nr,
+ "[%s]", element_info[properties_element].token_name);
+
+ ypos += 2 * font3_height;
+ }
+
+ // ----- print number of elements / percentage of this element in level
+
+ num_elements_in_level = 0;
+ for (y = 0; y < lev_fieldy; y++)
+ for (x = 0; x < lev_fieldx; x++)
+ if (Tile[x][y] == properties_element)
+ num_elements_in_level++;
+ percentage = num_elements_in_level * 100.0 / (lev_fieldx * lev_fieldy);
+
+ DrawTextS(xpos, ypos, font1_nr, percentage_text);
+
+ if (num_elements_in_level > 0)
+ DrawTextF(xpos + percentage_text_len, ypos + font2_yoffset, font2_nr,
+ "%d (%.2f %%)", num_elements_in_level, percentage);
+ else
+ DrawTextF(xpos + percentage_text_len, ypos + font2_yoffset, font2_nr,
+ none_text);
+
+ ypos += 2 * MAX(font1_height, font2_height);
+
+ // ----- print standard properties of this element
+
+ DrawTextS(xpos, ypos, font1_nr, properties_text);
+
+ ypos += line1_height;
+
+ for (i = 0; properties[i].value != -1; i++)
+ {
+ if (!HAS_PROPERTY(properties_element, properties[i].value))
+ continue;
+
+ DrawTextS(xpos, ypos, font2_nr, properties[i].text);
+
+ ypos += font2_height;
+
+ num_standard_properties++;
+ }
+
+ if (num_standard_properties == 0)
+ {
+ DrawTextS(xpos + properties_text_len, ypos - line1_height + font2_yoffset,
+ font2_nr, none_text);
+
+ ypos -= (line1_height - font1_height);
+ }
+
+ ypos += MAX(font1_height, font2_height);
+
+ // ----- print special description of this element
+
+ PrintInfoText(description_text, font1_nr, xpos, ypos);
+
+ ypos += line1_height;
+
+ if (PrintElementDescriptionFromFile(filename, font2_nr, xpos, ypos) == 0)
+ PrintInfoText(no_description_text, font1_nr, xpos, ypos - line1_height);
+}
+
+#define TEXT_COLLECTING "Score for collecting"
+#define TEXT_SMASHING "Score for smashing"
+#define TEXT_SLURPING "Score for slurping robot"
+#define TEXT_CRACKING "Score for cracking"
+#define TEXT_AMOEBA_SPEED "Speed of amoeba growth"
+#define TEXT_DURATION "Duration when activated"
+#define TEXT_DELAY_ON "Delay before activating"
+#define TEXT_DELAY_OFF "Delay before deactivating"
+#define TEXT_DELAY_EXPLODING "Delay before exploding"
+#define TEXT_DELAY_MOVING "Delay before moving"
+#define TEXT_BALL_DELAY "Element generation delay"
+#define TEXT_MOVE_SPEED "Speed of android moving"
+#define TEXT_CLONE_SPEED "Speed of android cloning"
+#define TEXT_GAME_OF_LIFE_1 "Min neighbours to survive"
+#define TEXT_GAME_OF_LIFE_2 "Max neighbours to survive"
+#define TEXT_GAME_OF_LIFE_3 "Min neighbours to create"
+#define TEXT_GAME_OF_LIFE_4 "Max neighbours to create"
+#define TEXT_TIME_BONUS "Extra time to solve level"
+
+static struct