+ ScrollLevel(dx, dy);
+ DrawAllPlayers();
+
+ /* scroll in two steps of half tile size to make things smoother */
+ BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
+ FlushDisplay();
+ Delay(wait_delay_value);
+
+ /* scroll second step to align at full tile size */
+ BackToFront();
+ Delay(wait_delay_value);
+ }
+#endif
+
+ DrawPlayer(player);
+ BackToFront();
+ Delay(wait_delay_value);
+ }
+}
+
+void RelocatePlayer(int jx, int jy, int el_player_raw)
+{
+#if 1
+ int el_player = GET_VALID_PLAYER_ELEMENT(el_player_raw);
+#else
+ int el_player = (el_player_raw == EL_SP_MURPHY ? EL_PLAYER_1 :el_player_raw);
+#endif
+ struct PlayerInfo *player = &stored_player[el_player - EL_PLAYER_1];
+ boolean ffwd_delay = (tape.playing && tape.fast_forward);
+ boolean no_delay = (tape.warp_forward);
+ int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
+ int wait_delay_value = (no_delay ? 0 : frame_delay_value);
+ int old_jx = player->jx;
+ int old_jy = player->jy;
+ int old_element = Feld[old_jx][old_jy];
+ int element = Feld[jx][jy];
+ boolean player_relocated = (old_jx != jx || old_jy != jy);
+
+ int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
+ int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
+#if 1
+ int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
+ int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
+ int leave_side_horiz = move_dir_horiz;
+ int leave_side_vert = move_dir_vert;
+#else
+ static int trigger_sides[4][2] =
+ {
+ /* enter side leave side */
+ { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
+ { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
+ { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
+ { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
+ };
+ int enter_side_horiz = trigger_sides[MV_DIR_BIT(move_dir_horiz)][0];
+ int enter_side_vert = trigger_sides[MV_DIR_BIT(move_dir_vert)][0];
+ int leave_side_horiz = trigger_sides[MV_DIR_BIT(move_dir_horiz)][1];
+ int leave_side_vert = trigger_sides[MV_DIR_BIT(move_dir_vert)][1];
+#endif
+ int enter_side = enter_side_horiz | enter_side_vert;
+ int leave_side = leave_side_horiz | leave_side_vert;
+
+ if (player->GameOver) /* do not reanimate dead player */
+ return;
+
+ if (!player_relocated) /* no need to relocate the player */
+ return;
+
+ if (IS_PLAYER(jx, jy)) /* player already placed at new position */
+ {
+ RemoveField(jx, jy); /* temporarily remove newly placed player */
+ DrawLevelField(jx, jy);
+ }
+
+ if (player->present)
+ {
+ while (player->MovPos)
+ {
+ ScrollPlayer(player, SCROLL_GO_ON);
+ ScrollScreen(NULL, SCROLL_GO_ON);
+
+#if USE_NEW_MOVE_DELAY
+ AdvanceFrameAndPlayerCounters(player->index_nr);
+#else
+ FrameCounter++;
+#endif
+
+ DrawPlayer(player);
+
+ BackToFront();
+ Delay(wait_delay_value);
+ }
+
+ DrawPlayer(player); /* needed here only to cleanup last field */
+ DrawLevelField(player->jx, player->jy); /* remove player graphic */
+
+ player->is_moving = FALSE;
+ }
+
+#if 1
+ if (IS_CUSTOM_ELEMENT(old_element))
+ CheckElementChangeByPlayer(old_jx, old_jy, old_element,
+ CE_LEFT_BY_PLAYER,
+ player->index_bit, leave_side);
+
+ CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
+ CE_OTHER_GETS_LEFT,
+ player->index_bit, leave_side);
+#endif
+
+ Feld[jx][jy] = el_player;
+ InitPlayerField(jx, jy, el_player, TRUE);
+
+ if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
+ {
+ Feld[jx][jy] = element;
+ InitField(jx, jy, FALSE);
+ }
+
+#if 1
+ if (player == local_player) /* only visually relocate local player */
+ DrawRelocatePlayer(player);
+#endif
+
+#if 1
+ TestIfHeroTouchesBadThing(jx, jy);
+ TestIfPlayerTouchesCustomElement(jx, jy);
+#endif
+
+#if 0
+ printf("::: %d,%d: %d\n", jx, jy-1, Changed[jx][jy-1]);
+#endif
+
+#if 0
+#if 0
+ /* needed to allow change of walkable custom element by entering player */
+ if (!(Changed[jx][jy] & CH_EVENT_BIT(CE_ENTERED_BY_PLAYER)))
+ Changed[jx][jy] = 0; /* allow another change (but prevent loop) */
+#else
+ /* needed to allow change of walkable custom element by entering player */
+ Changed[jx][jy] = 0; /* allow another change */
+#endif
+#endif
+
+#if 0
+ printf("::: player entering %d, %d from %s ...\n", jx, jy,
+ enter_side == MV_LEFT ? "left" :
+ enter_side == MV_RIGHT ? "right" :
+ enter_side == MV_UP ? "top" :
+ enter_side == MV_DOWN ? "bottom" : "oops! no idea!");
+#endif
+
+#if 1
+ if (IS_CUSTOM_ELEMENT(element))
+ CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
+ player->index_bit, enter_side);
+
+ CheckTriggeredElementChangeByPlayer(jx, jy, element,
+ CE_OTHER_GETS_ENTERED,
+ player->index_bit, enter_side);
+#endif
+}
+
+void Explode(int ex, int ey, int phase, int mode)
+{
+ int x, y;
+#if 0
+ int num_phase = 9;
+#endif
+
+ /* !!! eliminate this variable !!! */
+ int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
+
+#if 1
+ int last_phase;
+#else
+ int last_phase = num_phase * delay;
+ int half_phase = (num_phase / 2) * delay;
+ int first_phase_after_start = EX_PHASE_START + 1;
+#endif
+ int border_element;
+
+ if (game.explosions_delayed)
+ {
+ ExplodeField[ex][ey] = mode;
+ return;
+ }
+
+ if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
+ {
+ int center_element = Feld[ex][ey];
+
+#if 0
+ printf("::: start explosion %d,%d [%d]\n", ex, ey, FrameCounter);
+#endif
+
+#if 0
+ /* --- This is only really needed (and now handled) in "Impact()". --- */
+ /* do not explode moving elements that left the explode field in time */
+ if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
+ center_element == EL_EMPTY &&
+ (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
+ return;
+#endif
+
+ if (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER)
+ PlayLevelSoundAction(ex, ey, ACTION_EXPLODING);
+
+ /* remove things displayed in background while burning dynamite */
+ if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
+ Back[ex][ey] = 0;
+
+ if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
+ {
+ /* put moving element to center field (and let it explode there) */
+ center_element = MovingOrBlocked2Element(ex, ey);
+ RemoveMovingField(ex, ey);
+ Feld[ex][ey] = center_element;
+ }
+
+#if 1
+
+#if 1
+ last_phase = element_info[center_element].explosion_delay + 1;
+#else
+ last_phase = element_info[center_element].explosion_delay;
+#endif
+
+#if 0
+ printf("::: %d -> %d\n", center_element, last_phase);
+#endif
+#endif
+
+ for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
+ {
+ int xx = x - ex + 1;
+ int yy = y - ey + 1;
+ int element;
+
+#if 1
+#if 1
+ if (!IN_LEV_FIELD(x, y) ||
+ (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
+ (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
+ continue;
+#else
+ if (!IN_LEV_FIELD(x, y) ||
+ (mode != EX_TYPE_NORMAL && (x != ex || y != ey)))
+ continue;
+#endif
+#else
+ if (!IN_LEV_FIELD(x, y) ||
+ ((mode != EX_TYPE_NORMAL ||
+ center_element == EL_AMOEBA_TO_DIAMOND) &&
+ (x != ex || y != ey)))
+ continue;
+#endif
+
+ element = Feld[x][y];
+
+ if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
+ {
+ element = MovingOrBlocked2Element(x, y);
+
+ if (!IS_EXPLOSION_PROOF(element))
+ RemoveMovingField(x, y);
+ }
+
+#if 1
+
+#if 0
+ if (IS_EXPLOSION_PROOF(element))
+ continue;
+#else
+ /* indestructible elements can only explode in center (but not flames) */
+#if 1
+ if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
+ mode == EX_TYPE_BORDER)) ||
+ element == EL_FLAMES)
+ continue;
+#else
+ if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey)) ||
+ element == EL_FLAMES)
+ continue;
+#endif
+#endif
+
+#else
+ if ((IS_INDESTRUCTIBLE(element) &&
+ (game.engine_version < VERSION_IDENT(2,2,0,0) ||
+ (!IS_WALKABLE_OVER(element) && !IS_WALKABLE_UNDER(element)))) ||
+ element == EL_FLAMES)
+ continue;
+#endif
+
+#if 1
+ if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
+ (game.engine_version < VERSION_IDENT(3,1,0,0) ||
+ (x == ex && y == ey && mode != EX_TYPE_BORDER)))
+#else
+ if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
+#endif
+ {
+ if (IS_ACTIVE_BOMB(element))
+ {
+ /* re-activate things under the bomb like gate or penguin */
+#if 1
+ Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
+ Back[x][y] = 0;
+#else
+ Feld[x][y] = (Store[x][y] ? Store[x][y] : EL_EMPTY);
+ Store[x][y] = 0;
+#endif
+
+#if 0
+ printf("::: %d,%d: %d %s [%d, %d]\n", x, y, Feld[x][y],
+ element_info[Feld[x][y]].token_name,
+ Store[x][y], Store2[x][y]);
+#endif
+ }
+
+ continue;
+ }
+
+ /* save walkable background elements while explosion on same tile */
+#if 0
+ if (IS_INDESTRUCTIBLE(element))
+ Back[x][y] = element;
+#else
+#if 1
+#if 1
+ if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
+ (x != ex || y != ey || mode == EX_TYPE_BORDER))
+ Back[x][y] = element;
+#else
+ if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
+ (x != ex || y != ey))
+ Back[x][y] = element;
+#endif
+#else
+ if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element))
+ Back[x][y] = element;
+#endif
+#endif
+
+ /* ignite explodable elements reached by other explosion */
+ if (element == EL_EXPLOSION)
+ element = Store2[x][y];
+
+#if 1
+ if (AmoebaNr[x][y] &&
+ (element == EL_AMOEBA_FULL ||
+ element == EL_BD_AMOEBA ||
+ element == EL_AMOEBA_GROWING))
+ {
+ AmoebaCnt[AmoebaNr[x][y]]--;
+ AmoebaCnt2[AmoebaNr[x][y]]--;
+ }
+
+ RemoveField(x, y);
+#endif
+
+ if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
+ {
+ switch(StorePlayer[ex][ey])
+ {
+ case EL_PLAYER_2:
+ Store[x][y] = EL_PLAYER_IS_EXPLODING_2;
+ break;
+ case EL_PLAYER_3:
+ Store[x][y] = EL_PLAYER_IS_EXPLODING_3;
+ break;
+ case EL_PLAYER_4:
+ Store[x][y] = EL_PLAYER_IS_EXPLODING_4;
+ break;
+ case EL_PLAYER_1:
+ default:
+ Store[x][y] = EL_PLAYER_IS_EXPLODING_1;
+ break;
+ }
+
+#if 1
+ if (PLAYERINFO(ex, ey)->use_murphy_graphic)
+ Store[x][y] = EL_EMPTY;
+#else
+ if (game.emulation == EMU_SUPAPLEX)
+ Store[x][y] = EL_EMPTY;
+#endif
+ }
+ else if (center_element == EL_MOLE)
+ Store[x][y] = EL_EMERALD_RED;
+ else if (center_element == EL_PENGUIN)
+ Store[x][y] = EL_EMERALD_PURPLE;
+ else if (center_element == EL_BUG)
+ Store[x][y] = ((x == ex && y == ey) ? EL_DIAMOND : EL_EMERALD);
+ else if (center_element == EL_BD_BUTTERFLY)
+ Store[x][y] = EL_BD_DIAMOND;
+ else if (center_element == EL_SP_ELECTRON)
+ Store[x][y] = EL_SP_INFOTRON;
+ else if (center_element == EL_AMOEBA_TO_DIAMOND)
+ Store[x][y] = level.amoeba_content;
+ else if (center_element == EL_YAMYAM)
+ Store[x][y] = level.yamyam_content[game.yamyam_content_nr][xx][yy];
+ else if (IS_CUSTOM_ELEMENT(center_element) &&
+ element_info[center_element].content[xx][yy] != EL_EMPTY)
+ Store[x][y] = element_info[center_element].content[xx][yy];
+ else if (element == EL_WALL_EMERALD)
+ Store[x][y] = EL_EMERALD;
+ else if (element == EL_WALL_DIAMOND)
+ Store[x][y] = EL_DIAMOND;
+ else if (element == EL_WALL_BD_DIAMOND)
+ Store[x][y] = EL_BD_DIAMOND;
+ else if (element == EL_WALL_EMERALD_YELLOW)
+ Store[x][y] = EL_EMERALD_YELLOW;
+ else if (element == EL_WALL_EMERALD_RED)
+ Store[x][y] = EL_EMERALD_RED;
+ else if (element == EL_WALL_EMERALD_PURPLE)
+ Store[x][y] = EL_EMERALD_PURPLE;
+ else if (element == EL_WALL_PEARL)
+ Store[x][y] = EL_PEARL;
+ else if (element == EL_WALL_CRYSTAL)
+ Store[x][y] = EL_CRYSTAL;
+ else if (IS_CUSTOM_ELEMENT(element) && !CAN_EXPLODE(element))
+ Store[x][y] = element_info[element].content[1][1];
+ else
+ Store[x][y] = EL_EMPTY;
+
+ if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
+ center_element == EL_AMOEBA_TO_DIAMOND)
+ Store2[x][y] = element;
+
+#if 0
+ printf("::: %d,%d: %d %s\n", x, y, Store2[x][y],
+ element_info[Store2[x][y]].token_name);
+#endif
+
+#if 0
+ if (AmoebaNr[x][y] &&
+ (element == EL_AMOEBA_FULL ||
+ element == EL_BD_AMOEBA ||
+ element == EL_AMOEBA_GROWING))
+ {
+ AmoebaCnt[AmoebaNr[x][y]]--;
+ AmoebaCnt2[AmoebaNr[x][y]]--;
+ }
+
+#if 1
+ RemoveField(x, y);
+#else
+ MovDir[x][y] = MovPos[x][y] = 0;
+ GfxDir[x][y] = MovDir[x][y];
+ AmoebaNr[x][y] = 0;
+#endif
+#endif
+
+ Feld[x][y] = EL_EXPLOSION;
+#if 1
+ GfxElement[x][y] = center_element;
+#else
+ GfxElement[x][y] = EL_UNDEFINED;
+#endif
+
+ ExplodePhase[x][y] = 1;
+#if 1
+ ExplodeDelay[x][y] = last_phase;
+#endif
+
+#if 0
+#if 1
+ GfxFrame[x][y] = 0; /* animation does not start until next frame */
+#else
+ GfxFrame[x][y] = -1; /* animation does not start until next frame */
+#endif
+#endif
+
+ Stop[x][y] = TRUE;
+ }
+
+ if (center_element == EL_YAMYAM)
+ game.yamyam_content_nr =
+ (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
+
+#if 0
+ printf("::: %d,%d: %d %s [%d]\n", ex + 1, ey, Feld[ex + 1][ey],
+ element_info[Feld[ex + 1][ey]].token_name, Store2[ex + 1][ey]);
+#endif
+
+ return;
+ }
+
+ if (Stop[ex][ey])
+ return;
+
+ x = ex;
+ y = ey;
+
+#if 1
+ if (phase == 1)
+ GfxFrame[x][y] = 0; /* restart explosion animation */
+#endif
+
+#if 0
+ printf(":X: phase == %d [%d]\n", phase, GfxFrame[x][y]);
+#endif
+
+#if 1
+ last_phase = ExplodeDelay[x][y];
+#endif
+
+ ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
+
+#ifdef DEBUG
+
+ /* activate this even in non-DEBUG version until cause for crash in
+ getGraphicAnimationFrame() (see below) is found and eliminated */
+#endif
+#if 1
+
+ if (GfxElement[x][y] == EL_UNDEFINED)
+ {
+ printf("\n\n");
+ printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
+ printf("Explode(): This should never happen!\n");
+ printf("\n\n");
+
+ GfxElement[x][y] = EL_EMPTY;
+ }
+#endif
+
+#if 1
+
+ border_element = Store2[x][y];
+#if 1
+ if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
+ border_element = StorePlayer[x][y];
+#else
+ if (IS_PLAYER(x, y))
+ border_element = StorePlayer[x][y];
+#endif
+
+#if 0
+ printf("::: %d,%d: %d %s [%d]\n", x, y, border_element,
+ element_info[border_element].token_name, Store2[x][y]);
+#endif
+
+#if 0
+ printf("::: phase == %d\n", phase);
+#endif