+ }
+}
+
+static void InitBuggyBase(int x, int y)
+{
+ int element = Feld[x][y];
+ int activating_delay = FRAMES_PER_SECOND / 4;
+
+ ChangeDelay[x][y] =
+ (element == EL_SP_BUGGY_BASE ?
+ 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
+ element == EL_SP_BUGGY_BASE_ACTIVATING ?
+ activating_delay :
+ element == EL_SP_BUGGY_BASE_ACTIVE ?
+ 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
+}
+
+static void WarnBuggyBase(int x, int y)
+{
+ int i;
+ static int xy[4][2] =
+ {
+ { 0, -1 },
+ { -1, 0 },
+ { +1, 0 },
+ { 0, +1 }
+ };
+
+ for (i=0; i<4; i++)
+ {
+ int xx = x + xy[i][0], yy = y + xy[i][1];
+
+ if (IS_PLAYER(xx, yy))
+ {
+ PlaySoundLevel(x, y, SND_SP_BUGGY_BASE_ACTIVE);
+
+ break;
+ }
+ }
+}
+
+static void InitTrap(int x, int y)
+{
+ ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
+}
+
+static void ActivateTrap(int x, int y)
+{
+ PlaySoundLevel(x, y, SND_TRAP_ACTIVATING);
+}
+
+static void ChangeActiveTrap(int x, int y)
+{
+ int graphic = IMG_TRAP_ACTIVE;
+
+ /* if new animation frame was drawn, correct crumbled sand border */
+ if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
+ DrawLevelFieldCrumbledSand(x, y);
+}
+
+static void ChangeElementNowExt(int x, int y, int target_element)
+{
+ /* check if element under player changes from accessible to unaccessible
+ (needed for special case of dropping element which then changes) */
+ if (IS_PLAYER(x, y) && !PLAYER_PROTECTED(x, y) &&
+ IS_ACCESSIBLE(Feld[x][y]) && !IS_ACCESSIBLE(target_element))
+ {
+ Bang(x, y);
+ return;
+ }
+
+ RemoveField(x, y);
+ Feld[x][y] = target_element;
+
+ Changed[x][y] = TRUE; /* no more changes in this frame */
+
+ ResetGfxAnimation(x, y);
+ ResetRandomAnimationValue(x, y);
+
+ InitField(x, y, FALSE);
+ if (CAN_MOVE(Feld[x][y]))
+ InitMovDir(x, y);
+
+ DrawLevelField(x, y);
+
+ if (CAN_BE_CRUMBLED(Feld[x][y]))
+ DrawLevelFieldCrumbledSandNeighbours(x, y);
+
+ TestIfBadThingTouchesHero(x, y);
+ TestIfPlayerTouchesCustomElement(x, y);
+ TestIfElementTouchesCustomElement(x, y);
+
+ if (ELEM_IS_PLAYER(target_element))
+ RelocatePlayer(x, y, target_element);
+}
+
+static boolean ChangeElementNow(int x, int y, int element, int page)
+{
+ struct ElementChangeInfo *change = &element_info[element].change_page[page];
+
+ if (Changed[x][y]) /* do not change already changed elements */
+ return FALSE;
+
+ Changed[x][y] = TRUE; /* no more changes in this frame */
+
+ CheckTriggeredElementChange(x, y, Feld[x][y], CE_OTHER_IS_CHANGING);
+
+ if (change->explode)
+ {
+ Bang(x, y);
+
+ return TRUE;
+ }
+
+ if (change->use_content)
+ {
+ boolean complete_change = TRUE;
+ boolean can_change[3][3];
+ int xx, yy;
+
+ for (yy = 0; yy < 3; yy++) for(xx = 0; xx < 3 ; xx++)
+ {
+ boolean half_destructible;
+ int ex = x + xx - 1;
+ int ey = y + yy - 1;
+ int e;
+
+ can_change[xx][yy] = TRUE;
+
+ if (ex == x && ey == y) /* do not check changing element itself */
+ continue;
+
+ if (change->content[xx][yy] == EL_EMPTY_SPACE)
+ {
+ can_change[xx][yy] = FALSE; /* do not change empty borders */
+
+ continue;
+ }
+
+ if (!IN_LEV_FIELD(ex, ey))
+ {
+ can_change[xx][yy] = FALSE;
+ complete_change = FALSE;
+
+ continue;
+ }
+
+ e = Feld[ex][ey];
+
+ if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
+ e = MovingOrBlocked2Element(ex, ey);
+
+ half_destructible = (IS_FREE(ex, ey) || IS_DIGGABLE(e));
+
+ if ((change->power <= CP_NON_DESTRUCTIVE && !IS_FREE(ex, ey)) ||
+ (change->power <= CP_HALF_DESTRUCTIVE && !half_destructible) ||
+ (change->power <= CP_FULL_DESTRUCTIVE && IS_INDESTRUCTIBLE(e)))
+ {
+ can_change[xx][yy] = FALSE;
+ complete_change = FALSE;
+ }
+ }
+
+ if (!change->only_complete || complete_change)
+ {
+ boolean something_has_changed = FALSE;
+
+ if (change->only_complete && change->use_random_change &&
+ RND(100) < change->random)
+ return FALSE;
+
+ for (yy = 0; yy < 3; yy++) for(xx = 0; xx < 3 ; xx++)
+ {
+ int ex = x + xx - 1;
+ int ey = y + yy - 1;
+
+ if (can_change[xx][yy] && (!change->use_random_change ||
+ RND(100) < change->random))
+ {
+ if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
+ RemoveMovingField(ex, ey);
+
+ ChangeElementNowExt(ex, ey, change->content[xx][yy]);
+
+ 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)
+ PlaySoundLevelElementAction(x, y, element, ACTION_CHANGING);
+ }
+ }
+ else
+ {
+ ChangeElementNowExt(x, y, change->target_element);
+
+ PlaySoundLevelElementAction(x, y, element, ACTION_CHANGING);
+ }
+
+ return TRUE;
+}
+
+static void ChangeElement(int x, int y, int page)
+{
+ int element = MovingOrBlocked2Element(x, y);
+ struct ElementChangeInfo *change = &element_info[element].change_page[page];
+
+ 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], MovDir[x][y]);
+
+ if (IS_ANIMATED(graphic))
+ DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
+
+ if (change->change_function)
+ change->change_function(x, y);
+ }
+ else /* finish element change */
+ {
+ if (IS_MOVING(x, y)) /* never change a running system ;-) */
+ {
+ ChangeDelay[x][y] = 1; /* try change after next move step */
+
+ return;
+ }
+
+ if (ChangeElementNow(x, y, element, page))
+ {
+ if (change->post_change_function)
+ change->post_change_function(x, y);
+ }
+ }
+}
+
+static boolean CheckTriggeredElementChange(int lx, int ly, int trigger_element,
+ int trigger_event)
+{
+ int i, j, x, y;
+
+ if (!(trigger_events[trigger_element] & CH_EVENT_BIT(trigger_event)))
+ return FALSE;
+
+#if 0
+ /* prevent this function from running into a loop */
+ if (trigger_event == CE_OTHER_IS_CHANGING)
+ Changed[lx][ly] = TRUE;
+#endif
+
+ for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
+ {
+ int element = EL_CUSTOM_START + i;
+
+#if 1
+ boolean change_element = FALSE;
+ int page;
+
+ if (!CAN_CHANGE(element) ||
+ !HAS_ANY_CHANGE_EVENT(element, trigger_event))
+ continue;
+
+ for (j=0; j < element_info[element].num_change_pages; j++)
+ {
+ if (element_info[element].change_page[j].trigger_element ==
+ trigger_element)
+ {
+ change_element = TRUE;
+ page = j;
+
+ break;
+ }
+ }