+ /* ---------- main game synchronization point ---------- */
+
+ WaitUntilDelayReached(&action_delay, action_delay_value);
+
+ if (network_playing && !network_player_action_received)
+ {
+ /*
+#ifdef DEBUG
+ printf("DEBUG: try to get network player actions in time\n");
+#endif
+ */
+
+#if defined(PLATFORM_UNIX)
+ /* last chance to get network player actions without main loop delay */
+ HandleNetworking();
+#endif
+
+ if (game_status != GAME_MODE_PLAYING)
+ return;
+
+ if (!network_player_action_received)
+ {
+ /*
+#ifdef DEBUG
+ printf("DEBUG: failed to get network player actions in time\n");
+#endif
+ */
+ return;
+ }
+ }
+
+ if (tape.pausing)
+ return;
+
+#if 0
+ printf("::: getting new tape action [%d]\n", FrameCounter);
+#endif
+
+ recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
+
+ for (i = 0; i < MAX_PLAYERS; i++)
+ {
+ summarized_player_action |= stored_player[i].action;
+
+ if (!network_playing)
+ stored_player[i].effective_action = stored_player[i].action;
+ }
+
+#if defined(PLATFORM_UNIX)
+ if (network_playing)
+ SendToServer_MovePlayer(summarized_player_action);
+#endif
+
+ if (!options.network && !setup.team_mode)
+ local_player->effective_action = summarized_player_action;
+
+ for (i = 0; i < MAX_PLAYERS; i++)
+ {
+ int actual_player_action = stored_player[i].effective_action;
+
+ if (stored_player[i].programmed_action)
+ actual_player_action = stored_player[i].programmed_action;
+
+ if (recorded_player_action)
+ actual_player_action = recorded_player_action[i];
+
+ tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
+
+ if (tape.recording && tape_action[i] && !tape.player_participates[i])
+ tape.player_participates[i] = TRUE; /* player just appeared from CE */
+
+ ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
+ }
+
+#if 1
+ if (tape.recording)
+ TapeRecordAction(tape_action);
+#endif
+
+ network_player_action_received = FALSE;
+
+ ScrollScreen(NULL, SCROLL_GO_ON);
+
+#if 0
+ FrameCounter++;
+ TimeFrames++;
+
+ for (i = 0; i < MAX_PLAYERS; i++)
+ stored_player[i].Frame++;
+#endif
+
+#if 1
+ if (game.engine_version < VERSION_IDENT(2,2,0,7))
+ {
+ for (i = 0; i < MAX_PLAYERS; i++)
+ {
+ struct PlayerInfo *player = &stored_player[i];
+ int x = player->jx;
+ int y = player->jy;
+
+ if (player->active && player->is_pushing && player->is_moving &&
+ IS_MOVING(x, y))
+ {
+ ContinueMoving(x, y);
+
+ /* continue moving after pushing (this is actually a bug) */
+ if (!IS_MOVING(x, y))
+ {
+ Stop[x][y] = FALSE;
+ }
+ }
+ }
+ }
+#endif
+
+ for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
+ {
+ Changed[x][y] = CE_BITMASK_DEFAULT;
+ ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
+
+#if DEBUG
+ if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
+ {
+ printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
+ printf("GameActions(): This should never happen!\n");
+
+ ChangePage[x][y] = -1;
+ }
+#endif
+
+ Stop[x][y] = FALSE;
+ if (WasJustMoving[x][y] > 0)
+ WasJustMoving[x][y]--;
+ if (WasJustFalling[x][y] > 0)
+ WasJustFalling[x][y]--;
+
+ GfxFrame[x][y]++;
+
+#if 1
+ /* reset finished pushing action (not done in ContinueMoving() to allow
+ continous pushing animation for elements with zero push delay) */
+ if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
+ {
+ ResetGfxAnimation(x, y);
+ DrawLevelField(x, y);
+ }
+#endif
+
+#if DEBUG
+ if (IS_BLOCKED(x, y))
+ {
+ int oldx, oldy;
+
+ Blocked2Moving(x, y, &oldx, &oldy);
+ if (!IS_MOVING(oldx, oldy))
+ {
+ printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
+ printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
+ printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
+ printf("GameActions(): This should never happen!\n");
+ }
+ }
+#endif
+ }
+
+ for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
+ {
+ element = Feld[x][y];
+#if 1
+ graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
+#else
+ graphic = el2img(element);
+#endif
+
+#if 0
+ if (element == -1)
+ {
+ printf("::: %d,%d: %d [%d]\n", x, y, element, FrameCounter);
+
+ element = graphic = 0;
+ }
+#endif
+
+ if (graphic_info[graphic].anim_global_sync)
+ GfxFrame[x][y] = FrameCounter;
+
+ if (ANIM_MODE(graphic) == ANIM_RANDOM &&
+ IS_NEXT_FRAME(GfxFrame[x][y], graphic))
+ ResetRandomAnimationValue(x, y);
+
+ SetRandomAnimationValue(x, y);
+
+#if 1
+ PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
+#endif
+
+ if (IS_INACTIVE(element))
+ {
+ if (IS_ANIMATED(graphic))
+ DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
+
+ continue;
+ }
+
+#if 1
+ /* this may take place after moving, so 'element' may have changed */
+#if 0
+ if (IS_CHANGING(x, y))
+#else
+ if (IS_CHANGING(x, y) &&
+ (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
+#endif
+ {
+#if 0
+ ChangeElement(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] :
+ element_info[element].event_page_nr[CE_DELAY]);
+#else
+ ChangeElement(x, y, element_info[element].event_page_nr[CE_DELAY]);
+#endif
+
+ element = Feld[x][y];
+ graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
+ }
+#endif
+
+ if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
+ {
+ StartMoving(x, y);
+
+#if 1
+ element = Feld[x][y];
+ graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
+#if 0
+ if (element == EL_MOLE)
+ printf("::: %d, %d, %d [%d]\n",
+ IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y],
+ GfxAction[x][y]);
+#endif
+#if 0
+ if (element == EL_YAMYAM)
+ printf("::: %d, %d, %d\n",
+ IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y]);
+#endif
+#endif
+
+ if (IS_ANIMATED(graphic) &&
+ !IS_MOVING(x, y) &&
+ !Stop[x][y])
+ {
+ DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
+
+#if 0
+ if (element == EL_BUG)
+ printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
+#endif
+
+#if 0
+ if (element == EL_MOLE)
+ printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
+#endif
+ }
+
+ if (IS_GEM(element) || element == EL_SP_INFOTRON)
+ EdelsteinFunkeln(x, y);
+ }
+ else if ((element == EL_ACID ||
+ element == EL_EXIT_OPEN ||
+ element == EL_SP_EXIT_OPEN ||
+ element == EL_SP_TERMINAL ||
+ element == EL_SP_TERMINAL_ACTIVE ||
+ element == EL_EXTRA_TIME ||
+ element == EL_SHIELD_NORMAL ||
+ element == EL_SHIELD_DEADLY) &&
+ IS_ANIMATED(graphic))
+ DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
+ else if (IS_MOVING(x, y))
+ ContinueMoving(x, y);
+ else if (IS_ACTIVE_BOMB(element))
+ CheckDynamite(x, y);
+#if 0
+ else if (element == EL_EXPLOSION && !game.explosions_delayed)
+ Explode(x, y, ExplodePhase[x][y], EX_NORMAL);
+#endif
+ else if (element == EL_AMOEBA_GROWING)
+ AmoebeWaechst(x, y);
+ else if (element == EL_AMOEBA_SHRINKING)
+ AmoebaDisappearing(x, y);
+
+#if !USE_NEW_AMOEBA_CODE
+ else if (IS_AMOEBALIVE(element))
+ AmoebeAbleger(x, y);
+#endif
+
+ else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
+ Life(x, y);
+ else if (element == EL_EXIT_CLOSED)
+ CheckExit(x, y);
+ else if (element == EL_SP_EXIT_CLOSED)
+ CheckExitSP(x, y);
+ else if (element == EL_EXPANDABLE_WALL_GROWING)
+ MauerWaechst(x, y);
+ else if (element == EL_EXPANDABLE_WALL ||
+ element == EL_EXPANDABLE_WALL_HORIZONTAL ||
+ element == EL_EXPANDABLE_WALL_VERTICAL ||
+ element == EL_EXPANDABLE_WALL_ANY)
+ MauerAbleger(x, y);
+ else if (element == EL_FLAMES)
+ CheckForDragon(x, y);
+#if 0
+ else if (IS_AUTO_CHANGING(element))
+ ChangeElement(x, y);
+#endif
+ else if (element == EL_EXPLOSION)
+ ; /* drawing of correct explosion animation is handled separately */
+ else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
+ DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
+
+#if 0
+ /* this may take place after moving, so 'element' may have changed */
+ if (IS_AUTO_CHANGING(Feld[x][y]))
+ ChangeElement(x, y);
+#endif
+
+ if (IS_BELT_ACTIVE(element))
+ PlayLevelSoundAction(x, y, ACTION_ACTIVE);
+
+ if (game.magic_wall_active)
+ {
+ int jx = local_player->jx, jy = local_player->jy;
+
+ /* play the element sound at the position nearest to the player */
+ if ((element == EL_MAGIC_WALL_FULL ||
+ element == EL_MAGIC_WALL_ACTIVE ||
+ element == EL_MAGIC_WALL_EMPTYING ||
+ element == EL_BD_MAGIC_WALL_FULL ||
+ element == EL_BD_MAGIC_WALL_ACTIVE ||
+ element == EL_BD_MAGIC_WALL_EMPTYING) &&
+ ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
+ {
+ magic_wall_x = x;
+ magic_wall_y = y;
+ }
+ }
+ }
+
+#if USE_NEW_AMOEBA_CODE
+ /* new experimental amoeba growth stuff */
+#if 1
+ if (!(FrameCounter % 8))
+#endif
+ {
+ static unsigned long random = 1684108901;
+
+ for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
+ {
+#if 0
+ x = (random >> 10) % lev_fieldx;
+ y = (random >> 20) % lev_fieldy;
+#else
+ x = RND(lev_fieldx);
+ y = RND(lev_fieldy);
+#endif
+ element = Feld[x][y];
+
+ /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
+ if (!IS_PLAYER(x,y) &&
+ (element == EL_EMPTY ||
+ element == EL_SAND ||
+ element == EL_QUICKSAND_EMPTY ||
+ element == EL_ACID_SPLASH_LEFT ||
+ element == EL_ACID_SPLASH_RIGHT))
+ {
+ if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
+ (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
+ (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
+ (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
+ Feld[x][y] = EL_AMOEBA_DROP;
+ }
+
+ random = random * 129 + 1;
+ }
+ }
+#endif
+
+#if 0
+ if (game.explosions_delayed)
+#endif
+ {
+ game.explosions_delayed = FALSE;
+
+ for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
+ {
+ element = Feld[x][y];
+
+ if (ExplodeField[x][y])
+ Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
+ else if (element == EL_EXPLOSION)
+ Explode(x, y, ExplodePhase[x][y], EX_NORMAL);
+
+ ExplodeField[x][y] = EX_NO_EXPLOSION;
+ }
+
+ game.explosions_delayed = TRUE;
+ }
+
+ if (game.magic_wall_active)
+ {
+ if (!(game.magic_wall_time_left % 4))
+ {
+ int element = Feld[magic_wall_x][magic_wall_y];
+
+ if (element == EL_BD_MAGIC_WALL_FULL ||
+ element == EL_BD_MAGIC_WALL_ACTIVE ||
+ element == EL_BD_MAGIC_WALL_EMPTYING)
+ PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
+ else
+ PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
+ }
+
+ if (game.magic_wall_time_left > 0)
+ {
+ game.magic_wall_time_left--;
+ if (!game.magic_wall_time_left)
+ {
+ for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
+ {
+ element = Feld[x][y];
+
+ if (element == EL_MAGIC_WALL_ACTIVE ||
+ element == EL_MAGIC_WALL_FULL)
+ {
+ Feld[x][y] = EL_MAGIC_WALL_DEAD;
+ DrawLevelField(x, y);
+ }
+ else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
+ element == EL_BD_MAGIC_WALL_FULL)
+ {
+ Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
+ DrawLevelField(x, y);
+ }
+ }
+
+ game.magic_wall_active = FALSE;
+ }
+ }
+ }
+
+ if (game.light_time_left > 0)
+ {
+ game.light_time_left--;
+
+ if (game.light_time_left == 0)
+ RedrawAllLightSwitchesAndInvisibleElements();
+ }
+
+ if (game.timegate_time_left > 0)
+ {
+ game.timegate_time_left--;
+
+ if (game.timegate_time_left == 0)
+ CloseAllOpenTimegates();
+ }
+
+ for (i = 0; i < MAX_PLAYERS; i++)
+ {
+ struct PlayerInfo *player = &stored_player[i];
+
+ if (SHIELD_ON(player))
+ {
+ if (player->shield_deadly_time_left)
+ PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
+ else if (player->shield_normal_time_left)
+ PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
+ }
+ }
+
+ if (TimeFrames >= FRAMES_PER_SECOND)
+ {
+ TimeFrames = 0;
+ TimePlayed++;
+
+ for (i = 0; i < MAX_PLAYERS; i++)
+ {
+ struct PlayerInfo *player = &stored_player[i];
+
+ if (SHIELD_ON(player))
+ {
+ player->shield_normal_time_left--;
+
+ if (player->shield_deadly_time_left > 0)
+ player->shield_deadly_time_left--;
+ }
+ }
+
+ if (tape.recording || tape.playing)
+ DrawVideoDisplay(VIDEO_STATE_TIME_ON, TimePlayed);