+ Bang(x, y);
+
+ return;
+ }
+#else
+ if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
+ IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
+ {
+ Bang(x, y);
+
+ return;
+ }
+#endif
+#endif
+
+ /* "ChangeCount" not set yet to allow "entered by player" change one time */
+ if (new_element_is_player)
+ RelocatePlayer(x, y, new_element);
+
+ if (is_change)
+ ChangeCount[x][y]++; /* count number of changes in the same frame */
+
+ TestIfBadThingTouchesPlayer(x, y);
+ TestIfPlayerTouchesCustomElement(x, y);
+ TestIfElementTouchesCustomElement(x, y);
+}
+
+static void CreateField(int x, int y, int element)
+{
+ CreateFieldExt(x, y, element, FALSE);
+}
+
+static void CreateElementFromChange(int x, int y, int element)
+{
+ element = GET_VALID_RUNTIME_ELEMENT(element);
+
+#if USE_STOP_CHANGED_ELEMENTS
+ if (game.engine_version >= VERSION_IDENT(3,2,0,7))
+ {
+ int old_element = Feld[x][y];
+
+ /* prevent changed element from moving in same engine frame
+ unless both old and new element can either fall or move */
+ if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
+ (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
+ Stop[x][y] = TRUE;
+ }
+#endif
+
+ CreateFieldExt(x, y, element, TRUE);
+}
+
+static boolean ChangeElement(int x, int y, int element, int page)
+{
+ struct ElementInfo *ei = &element_info[element];
+ struct ElementChangeInfo *change = &ei->change_page[page];
+ int ce_value = CustomValue[x][y];
+ int ce_score = ei->collect_score;
+ int target_element;
+ int old_element = Feld[x][y];
+
+ /* always use default change event to prevent running into a loop */
+ if (ChangeEvent[x][y] == -1)
+ ChangeEvent[x][y] = CE_DELAY;
+
+ if (ChangeEvent[x][y] == CE_DELAY)
+ {
+ /* reset actual trigger element, trigger player and action element */
+ change->actual_trigger_element = EL_EMPTY;
+ change->actual_trigger_player = EL_PLAYER_1;
+ change->actual_trigger_side = CH_SIDE_NONE;
+ change->actual_trigger_ce_value = 0;
+ change->actual_trigger_ce_score = 0;
+ }
+
+ /* do not change elements more than a specified maximum number of changes */
+ if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
+ return FALSE;
+
+ ChangeCount[x][y]++; /* count number of changes in the same frame */
+
+ if (change->explode)
+ {
+ Bang(x, y);
+
+ return TRUE;
+ }
+
+ if (change->use_target_content)
+ {
+ boolean complete_replace = TRUE;
+ boolean can_replace[3][3];
+ int xx, yy;
+
+ for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
+ {
+ boolean is_empty;
+ boolean is_walkable;
+ boolean is_diggable;
+ boolean is_collectible;
+ boolean is_removable;
+ boolean is_destructible;
+ int ex = x + xx - 1;
+ int ey = y + yy - 1;
+ int content_element = change->target_content.e[xx][yy];
+ int e;
+
+ can_replace[xx][yy] = TRUE;
+
+ if (ex == x && ey == y) /* do not check changing element itself */
+ continue;
+
+ if (content_element == EL_EMPTY_SPACE)
+ {
+ can_replace[xx][yy] = FALSE; /* do not replace border with space */
+
+ continue;
+ }
+
+ if (!IN_LEV_FIELD(ex, ey))
+ {
+ can_replace[xx][yy] = FALSE;
+ complete_replace = FALSE;
+
+ continue;
+ }
+
+ e = Feld[ex][ey];
+
+ if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
+ e = MovingOrBlocked2Element(ex, ey);
+
+ is_empty = (IS_FREE(ex, ey) ||
+ (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
+
+ is_walkable = (is_empty || IS_WALKABLE(e));
+ is_diggable = (is_empty || IS_DIGGABLE(e));
+ is_collectible = (is_empty || IS_COLLECTIBLE(e));
+ is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
+ is_removable = (is_diggable || is_collectible);
+
+ can_replace[xx][yy] =
+ (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
+ (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
+ (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
+ (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
+ (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
+ (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
+ !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
+
+ if (!can_replace[xx][yy])
+ complete_replace = FALSE;
+ }
+
+ if (!change->only_if_complete || complete_replace)
+ {
+ boolean something_has_changed = FALSE;
+
+ if (change->only_if_complete && change->use_random_replace &&
+ RND(100) < change->random_percentage)
+ return FALSE;
+
+ for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
+ {
+ int ex = x + xx - 1;
+ int ey = y + yy - 1;
+ int content_element;
+
+ if (can_replace[xx][yy] && (!change->use_random_replace ||
+ RND(100) < change->random_percentage))
+ {
+ if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
+ RemoveMovingField(ex, ey);
+
+ ChangeEvent[ex][ey] = ChangeEvent[x][y];
+
+ content_element = change->target_content.e[xx][yy];
+ target_element = GET_TARGET_ELEMENT(element, content_element, change,
+ ce_value, ce_score);
+
+ CreateElementFromChange(ex, ey, target_element);
+
+ something_has_changed = TRUE;
+
+ /* for symmetry reasons, freeze newly created border elements */
+ if (ex != x || ey != y)
+ Stop[ex][ey] = TRUE; /* no more moving in this frame */
+ }
+ }
+
+ if (something_has_changed)
+ {
+ PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
+ PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
+ }
+ }
+ }
+ else
+ {
+ target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
+ ce_value, ce_score);
+
+ if (element == EL_DIAGONAL_GROWING ||
+ element == EL_DIAGONAL_SHRINKING)
+ {
+ target_element = Store[x][y];
+
+ Store[x][y] = EL_EMPTY;
+ }
+
+ CreateElementFromChange(x, y, target_element);
+
+ PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
+ PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
+ }
+
+ /* this uses direct change before indirect change */
+ CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
+
+ return TRUE;
+}
+
+#if USE_NEW_DELAYED_ACTION
+
+static void HandleElementChange(int x, int y, int page)
+{
+ int element = MovingOrBlocked2Element(x, y);
+ struct ElementInfo *ei = &element_info[element];
+ struct ElementChangeInfo *change = &ei->change_page[page];
+
+#ifdef DEBUG
+ if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
+ !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
+ {
+ printf("\n\n");
+ printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
+ x, y, element, element_info[element].token_name);
+ printf("HandleElementChange(): This should never happen!\n");
+ printf("\n\n");
+ }
+#endif
+
+ /* this can happen with classic bombs on walkable, changing elements */
+ if (!CAN_CHANGE_OR_HAS_ACTION(element))
+ {
+#if 0
+ if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
+ ChangeDelay[x][y] = 0;
+#endif
+
+ return;
+ }
+
+ if (ChangeDelay[x][y] == 0) /* initialize element change */
+ {
+ ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
+
+ if (change->can_change)
+ {
+#if 0
+ /* !!! not clear why graphic animation should be reset at all here !!! */
+#if USE_GFX_RESET_WHEN_NOT_MOVING
+ /* when a custom element is about to change (for example by change delay),
+ do not reset graphic animation when the custom element is moving */
+ if (IS_MOVING(x, y))
+#endif
+ {
+ ResetGfxAnimation(x, y);
+ ResetRandomAnimationValue(x, y);
+ }
+#endif
+
+ if (change->pre_change_function)
+ change->pre_change_function(x, y);
+ }
+ }
+
+ ChangeDelay[x][y]--;
+
+ if (ChangeDelay[x][y] != 0) /* continue element change */
+ {
+ if (change->can_change)
+ {
+ int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
+
+ if (IS_ANIMATED(graphic))
+ DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
+
+ if (change->change_function)
+ change->change_function(x, y);
+ }
+ }
+ else /* finish element change */
+ {
+ if (ChangePage[x][y] != -1) /* remember page from delayed change */