+ 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;
+ }
+
+#if 0
+ if (Changed[ex][ey]) /* do not change already changed elements */
+ {
+ can_replace[xx][yy] = FALSE;
+ complete_replace = FALSE;
+
+ continue;
+ }
+#endif
+
+ e = Feld[ex][ey];
+
+ if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
+ e = MovingOrBlocked2Element(ex, ey);
+
+#if 1
+
+#if 0
+ is_empty = (IS_FREE(ex, ey) ||
+ (IS_PLAYER(ex, ey) && IS_WALKABLE(content_element)) ||
+ (IS_WALKABLE(e) && ELEM_IS_PLAYER(content_element) &&
+ !IS_MOVING(ex, ey) && !IS_BLOCKED(ex, ey)));
+#else
+
+#if 0
+ is_empty = (IS_FREE(ex, ey) ||
+ (IS_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
+#else
+ is_empty = (IS_FREE(ex, ey) ||
+ (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
+#endif
+
+#endif
+
+ 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;
+#else
+ empty_for_element = (IS_FREE(ex, ey) || (IS_FREE_OR_PLAYER(ex, ey) &&
+ IS_WALKABLE(content_element)));
+#if 1
+ half_destructible = (empty_for_element || IS_DIGGABLE(e));
+#else
+ half_destructible = (IS_FREE(ex, ey) || IS_DIGGABLE(e));
+#endif
+
+ if ((change->replace_when <= CP_WHEN_EMPTY && !empty_for_element) ||
+ (change->replace_when <= CP_WHEN_DIGGABLE && !half_destructible) ||
+ (change->replace_when <= CP_WHEN_DESTRUCTIBLE && IS_INDESTRUCTIBLE(e)))
+ {
+ can_replace[xx][yy] = FALSE;
+ complete_replace = FALSE;
+ }
+#endif
+ }
+
+ 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[xx][yy];
+ target_element = GET_TARGET_ELEMENT(content_element, change);
+
+ ChangeElementNowExt(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);
+ }
+ }
+ else
+ {
+ target_element = GET_TARGET_ELEMENT(change->target_element, change);
+
+ ChangeElementNowExt(x, y, target_element);
+
+ PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
+ }
+
+#if 1
+ /* this uses direct change before indirect change */
+ CheckTriggeredElementChangeByPage(x,y,old_element,CE_OTHER_IS_CHANGING,page);
+#endif
+
+ return TRUE;
+}
+
+static void ChangeElement(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(element) && !CAN_CHANGE(Back[x][y]))
+ {
+ printf("\n\n");
+ printf("ChangeElement(): %d,%d: element = %d ('%s')\n",
+ x, y, element, element_info[element].token_name);
+ printf("ChangeElement(): This should never happen!\n");
+ printf("\n\n");
+ }
+#endif
+
+ /* this can happen with classic bombs on walkable, changing elements */
+ if (!CAN_CHANGE(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] = ( change->delay_fixed * change->delay_frames +
+ RND(change->delay_random * change->delay_frames)) + 1;
+
+ ResetGfxAnimation(x, y);
+ ResetRandomAnimationValue(x, y);
+
+ if (change->pre_change_function)
+ change->pre_change_function(x, y);
+ }
+
+ ChangeDelay[x][y]--;
+
+ if (ChangeDelay[x][y] != 0) /* continue element 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 */
+ {
+ page = ChangePage[x][y];
+ ChangePage[x][y] = -1;
+
+ change = &ei->change_page[page];
+ }
+
+#if 0
+ if (IS_MOVING(x, y) && !change->explode)
+#else
+ if (IS_MOVING(x, y)) /* never change a running system ;-) */
+#endif
+ {
+ ChangeDelay[x][y] = 1; /* try change after next move step */
+ ChangePage[x][y] = page; /* remember page to use for change */
+
+ return;
+ }
+
+ if (ChangeElementNow(x, y, element, page))
+ {
+ if (change->post_change_function)
+ change->post_change_function(x, y);
+ }
+ }
+}
+
+static boolean CheckTriggeredElementChangeExt(int lx, int ly,
+ int trigger_element,
+ int trigger_event,
+ int trigger_player,
+ int trigger_side,
+ int trigger_page)
+{
+ int i, j, x, y;
+ int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
+
+ if (!(trigger_events[trigger_element] & CH_EVENT_BIT(trigger_event)))
+ return FALSE;
+
+ for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
+ {
+ int element = EL_CUSTOM_START + i;
+
+ boolean change_element = FALSE;
+ int page = 0;
+
+ if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
+ continue;
+
+ for (j = 0; j < element_info[element].num_change_pages; j++)
+ {
+ struct ElementChangeInfo *change = &element_info[element].change_page[j];
+
+ if (change->can_change &&
+ change->events & CH_EVENT_BIT(trigger_event) &&
+ change->trigger_side & trigger_side &&
+ change->trigger_player & trigger_player &&
+ change->trigger_page & trigger_page_bits &&
+ IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
+ {
+#if 0
+ if (!(change->events & CH_EVENT_BIT(trigger_event)))
+ printf("::: !!! %d triggers %d: using wrong page %d [event %d]\n",
+ trigger_element-EL_CUSTOM_START+1, i+1, j, trigger_event);
+#endif
+
+ change_element = TRUE;
+ page = j;
+
+ change->actual_trigger_element = trigger_element;
+ change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
+
+ break;
+ }
+ }
+
+ if (!change_element)
+ continue;
+
+ for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
+ {
+#if 0
+ if (x == lx && y == ly) /* do not change trigger element itself */
+ continue;
+#endif
+
+ if (Feld[x][y] == element)
+ {
+ ChangeDelay[x][y] = 1;
+ ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
+ ChangeElement(x, y, page);
+ }
+ }
+ }
+
+ return TRUE;
+}
+
+static boolean CheckElementChangeExt(int x, int y,
+ int element,
+ int trigger_element,
+ int trigger_event,
+ int trigger_player,
+ int trigger_side,
+ int trigger_page)
+{
+ if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
+ return FALSE;
+
+ if (Feld[x][y] == EL_BLOCKED)
+ {
+ Blocked2Moving(x, y, &x, &y);
+ element = Feld[x][y];
+ }
+
+#if 1
+ if (Feld[x][y] != element) /* check if element has already changed */
+ {
+#if 0
+ printf("::: %d ('%s') != %d ('%s') [%d]\n",
+ Feld[x][y], element_info[Feld[x][y]].token_name,
+ element, element_info[element].token_name,
+ trigger_event);
+#endif
+
+ return FALSE;
+ }
+#endif
+
+#if 1
+ if (trigger_page < 0)
+ {
+ boolean change_element = FALSE;
+ int i;
+
+ for (i = 0; i < element_info[element].num_change_pages; i++)
+ {
+ struct ElementChangeInfo *change = &element_info[element].change_page[i];
+
+ if (change->can_change &&
+ change->events & CH_EVENT_BIT(trigger_event) &&
+ change->trigger_side & trigger_side &&
+ change->trigger_player & trigger_player)
+ {
+ change_element = TRUE;
+ trigger_page = i;
+
+ change->actual_trigger_element = trigger_element;
+ change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
+
+ break;
+ }
+ }
+
+ if (!change_element)
+ return FALSE;
+ }
+ else
+ {
+ struct ElementInfo *ei = &element_info[element];
+ struct ElementChangeInfo *change = &ei->change_page[trigger_page];
+
+ change->actual_trigger_element = trigger_element;
+ change->actual_trigger_player = EL_PLAYER_1; /* unused */
+ }
+
+#else
+
+ /* !!! this check misses pages with same event, but different side !!! */
+
+ if (trigger_page < 0)
+ trigger_page = element_info[element].event_page_nr[trigger_event];
+
+ if (!(element_info[element].change_page[trigger_page].trigger_side & trigger_side))
+ return FALSE;
+#endif
+
+ ChangeDelay[x][y] = 1;
+ ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
+ ChangeElement(x, y, trigger_page);
+
+ return TRUE;
+}
+
+static void PlayPlayerSound(struct PlayerInfo *player)
+{
+ int jx = player->jx, jy = player->jy;
+ int element = player->element_nr;
+ int last_action = player->last_action_waiting;
+ int action = player->action_waiting;
+
+ if (player->is_waiting)
+ {
+ if (action != last_action)
+ PlayLevelSoundElementAction(jx, jy, element, action);
+ else
+ PlayLevelSoundElementActionIfLoop(jx, jy, element, action);
+ }
+ else
+ {
+ if (action != last_action)
+ StopSound(element_info[element].sound[last_action]);
+
+ if (last_action == ACTION_SLEEPING)
+ PlayLevelSoundElementAction(jx, jy, element, ACTION_AWAKENING);
+ }
+}
+
+static void PlayAllPlayersSound()
+{
+ int i;