+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) &&
+ IS_ACCESSIBLE(Feld[x][y]) && !IS_ACCESSIBLE(target_element))
+ {
+ Bang(x, y);
+ return;
+ }
+
+ RemoveField(x, y);
+ Feld[x][y] = target_element;
+
+ 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 void ChangeElementNow(int x, int y, int element)
+{
+ struct ElementChangeInfo *change = &element_info[element].change;
+
+#if 0
+ if (element >= EL_CUSTOM_START + 17 && element <= EL_CUSTOM_START + 39)
+ printf("::: changing... [%d]\n", FrameCounter);
+#endif
+
+#if 0
+ /* prevent CheckTriggeredElementChange() from looping */
+ Changing[x][y] = TRUE;
+#endif
+
+ CheckTriggeredElementChange(x, y, Feld[x][y], CE_OTHER_IS_CHANGING);
+
+#if 0
+ Changing[x][y] = FALSE;
+#endif
+
+ if (change->explode)
+ {
+ Bang(x, y);
+ return;
+ }
+
+ 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;
+
+ 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, stop newly created border elements */
+ if (ex != x || ey != y)
+ Stop[ex][ey] = TRUE;
+ }
+ }
+
+ if (something_has_changed)
+ PlaySoundLevelElementAction(x, y, element, ACTION_CHANGING);
+ }
+ }
+ else
+ {
+ ChangeElementNowExt(x, y, change->target_element);
+
+ PlaySoundLevelElementAction(x, y, element, ACTION_CHANGING);
+ }
+}
+
+static void ChangeElement(int x, int y)
+{
+#if 1
+ int element = MovingOrBlocked2Element(x, y);
+#else
+ int element = Feld[x][y];
+#endif
+ struct ElementChangeInfo *change = &element_info[element].change;
+
+ if (ChangeDelay[x][y] == 0) /* initialize element change */
+ {
+#if 1
+ ChangeDelay[x][y] = ( change->delay_fixed * change->delay_frames +
+ RND(change->delay_random * change->delay_frames)) + 1;
+#else
+ ChangeDelay[x][y] = changing_element[element].change_delay + 1;
+
+ if (IS_CUSTOM_ELEMENT(element) && HAS_CHANGE_EVENT(element, CE_DELAY))
+ {
+ int max_random_delay = element_info[element].change.delay_random;
+ int delay_frames = element_info[element].change.delay_frames;
+
+ ChangeDelay[x][y] += RND(max_random_delay * delay_frames);
+ }
+#endif
+
+ ResetGfxAnimation(x, y);
+ ResetRandomAnimationValue(x, y);
+
+#if 1
+ if (change->pre_change_function)
+ change->pre_change_function(x, y);
+#else
+ if (changing_element[element].pre_change_function)
+ changing_element[element].pre_change_function(x, y);
+#endif
+ }
+
+ 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 1
+ if (change->change_function)
+ change->change_function(x, y);
+#else
+ if (changing_element[element].change_function)
+ changing_element[element].change_function(x, y);
+#endif
+ }
+ else /* finish element change */
+ {
+#if 0
+ int next_element = changing_element[element].next_element;
+#endif
+
+ if (IS_MOVING(x, y)) /* never change a running system ;-) */
+ {
+ ChangeDelay[x][y] = 1; /* try change after next move step */
+
+ return;
+ }
+
+#if 1
+ ChangeElementNow(x, y, element);
+
+ if (change->post_change_function)
+ change->post_change_function(x, y);
+#else
+ if (next_element != EL_UNDEFINED)
+ ChangeElementNow(x, y, next_element);
+ else
+ ChangeElementNow(x, y, element_info[element].change.target_element);
+
+ if (changing_element[element].post_change_function)
+ changing_element[element].post_change_function(x, y);
+#endif
+ }
+}
+
+static boolean CheckTriggeredElementChange(int lx, int ly, int trigger_element,
+ int trigger_event)