+#if 1
+ /*
+ This is a very special case that seems to be a mixture between
+ CheckElementChange() and CheckTriggeredElementChange(): while
+ the first one only affects single elements that are triggered
+ directly, the second one affects multiple elements in the playfield
+ that are triggered indirectly by another element. This is a third
+ case: Changing the CE score always affects multiple identical CEs,
+ so every affected CE must be checked, not only the single CE for
+ which the CE score was changed in the first place (as every instance
+ of that CE shares the same CE score, and therefore also can change)!
+ */
+ SCAN_PLAYFIELD(xx, yy)
+ {
+ if (Feld[xx][yy] == element)
+ CheckElementChange(xx, yy, element, EL_UNDEFINED,
+ CE_SCORE_GETS_ZERO);
+ }
+#endif
+ }
+ }
+
+#endif
+
+ break;
+ }
+
+ /* ---------- engine actions ------------------------------------------ */
+
+ case CA_SET_ENGINE_SCAN_MODE:
+ {
+ InitPlayfieldScanMode(action_arg);
+
+ break;
+ }
+
+ default:
+ break;
+ }
+}
+
+static void CreateFieldExt(int x, int y, int element, boolean is_change)
+{
+ int old_element = Feld[x][y];
+ int new_element = get_element_from_group_element(element);
+ int previous_move_direction = MovDir[x][y];
+#if USE_NEW_CUSTOM_VALUE
+ int last_ce_value = CustomValue[x][y];
+#endif
+ boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
+ boolean add_player_onto_element = (new_element_is_player &&
+#if USE_CODE_THAT_BREAKS_SNAKE_BITE
+ /* this breaks SnakeBite when a snake is
+ halfway through a door that closes */
+ /* NOW FIXED AT LEVEL INIT IN files.c */
+ new_element != EL_SOKOBAN_FIELD_PLAYER &&
+#endif
+ IS_WALKABLE(old_element));
+
+#if 0
+ /* check if element under the player changes from accessible to unaccessible
+ (needed for special case of dropping element which then changes) */
+ if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
+ IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
+ {
+ Bang(x, y);
+
+ return;
+ }
+#endif
+
+ if (!add_player_onto_element)
+ {
+ if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
+ RemoveMovingField(x, y);
+ else
+ RemoveField(x, y);
+
+ Feld[x][y] = new_element;
+
+#if !USE_GFX_RESET_GFX_ANIMATION
+ ResetGfxAnimation(x, y);
+ ResetRandomAnimationValue(x, y);
+#endif
+
+ if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
+ MovDir[x][y] = previous_move_direction;
+
+#if USE_NEW_CUSTOM_VALUE
+ if (element_info[new_element].use_last_ce_value)
+ CustomValue[x][y] = last_ce_value;
+#endif
+
+ InitField_WithBug1(x, y, FALSE);
+
+ new_element = Feld[x][y]; /* element may have changed */
+
+#if USE_GFX_RESET_GFX_ANIMATION
+ ResetGfxAnimation(x, y);
+ ResetRandomAnimationValue(x, y);
+#endif
+
+ DrawLevelField(x, y);
+
+ if (GFX_CRUMBLED(new_element))
+ DrawLevelFieldCrumbledSandNeighbours(x, y);
+ }
+
+#if 1
+ /* check if element under the player changes from accessible to unaccessible
+ (needed for special case of dropping element which then changes) */
+ /* (must be checked after creating new element for walkable group elements) */
+ if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
+ IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
+ {
+ Bang(x, y);
+
+ return;
+ }
+#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 */
+