#define GAME_CTRL_ID_UNDO 3
#define GAME_CTRL_ID_REDO 4
#define GAME_CTRL_ID_SAVE 5
-#define GAME_CTRL_ID_LOAD 6
-#define SOUND_CTRL_ID_MUSIC 7
-#define SOUND_CTRL_ID_LOOPS 8
-#define SOUND_CTRL_ID_SIMPLE 9
+#define GAME_CTRL_ID_PAUSE2 6
+#define GAME_CTRL_ID_LOAD 7
+#define SOUND_CTRL_ID_MUSIC 8
+#define SOUND_CTRL_ID_LOOPS 9
+#define SOUND_CTRL_ID_SIMPLE 10
-#define NUM_GAME_BUTTONS 10
+#define NUM_GAME_BUTTONS 11
/* forward declaration for internal use */
CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
}
-static inline void InitField_WithBug1(int x, int y, boolean init_game)
+inline static void InitField_WithBug1(int x, int y, boolean init_game)
{
InitField(x, y, init_game);
InitMovDir(x, y);
}
-static inline void InitField_WithBug2(int x, int y, boolean init_game)
+inline static void InitField_WithBug2(int x, int y, boolean init_game)
{
int old_element = Feld[x][y];
{
int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
+ int fade_mask = REDRAW_FIELD;
boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
int initial_move_dir = MV_DOWN;
int i, j, x, y;
- game_status = GAME_MODE_PLAYING;
+ // required here to update video display before fading (FIX THIS)
+ DrawMaskedBorder(REDRAW_DOOR_2);
- StopAnimation();
+ game_status = GAME_MODE_PLAYING;
if (!game.restart_level)
CloseDoor(DOOR_CLOSE_1);
+ /* needed if different viewport properties defined for playing */
+ ChangeViewportPropertiesIfNeeded();
+
if (level_editor_test_game)
FadeSkipNextFadeIn();
else
FadeSetEnterScreen();
- FadeOut(REDRAW_FIELD);
+ if (CheckIfGlobalBorderHasChanged())
+ fade_mask = REDRAW_ALL;
- /* needed if different viewport properties defined for playing */
- ChangeViewportPropertiesIfNeeded();
+ FadeOut(fade_mask);
+
+ OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
+
+ ClearField();
DrawCompleteVideoDisplay();
/* blit playfield from scroll buffer to normal back buffer for fading in */
BlitScreenToBitmap(backbuffer);
-
- redraw_mask |= REDRAW_FROM_BACKBUFFER;
/* !!! FIX THIS (END) !!! */
- FadeIn(REDRAW_FIELD);
+ DrawMaskedBorder(fade_mask);
+
+ FadeIn(fade_mask);
#if 1
// full screen redraw is required at this point in the following cases:
{
UnmapGameButtons();
UnmapTapeButtons();
+
+ FreeGameButtons();
+ CreateGameButtons();
+
game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
+
MapGameButtons();
MapTapeButtons();
local_player->LevelSolved_GameEnd = TRUE;
- CloseDoor(DOOR_CLOSE_1);
+ if (!global.use_envelope_request)
+ CloseDoor(DOOR_CLOSE_1);
if (local_player->LevelSolved_SaveTape)
{
SaveTapeChecked(tape.level_nr); /* ask to save tape */
}
+ CloseDoor(DOOR_CLOSE_ALL);
+
if (level_editor_test_game)
{
game_status = GAME_MODE_MAIN;
- DrawAndFadeInMainMenu(REDRAW_FIELD);
+ DrawMainMenu();
return;
}
game_status = GAME_MODE_MAIN;
- DrawAndFadeInMainMenu(REDRAW_FIELD);
+ DrawMainMenu();
return;
}
{
game_status = GAME_MODE_SCORES;
- /* needed if different viewport properties defined for scores */
- ChangeViewportPropertiesIfNeeded();
-
DrawHallOfFame(hi_pos);
if (raise_level)
TapeErase();
}
- DrawAndFadeInMainMenu(REDRAW_FIELD);
+ DrawMainMenu();
}
}
int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
int wait_delay_value = (no_delay ? 0 : frame_delay_value);
- if (quick_relocation)
+ if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
+ {
+ RedrawPlayfield();
+ }
+ else if (quick_relocation)
{
if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
{
}
}
- RedrawPlayfield(TRUE, 0,0,0,0);
+ RedrawPlayfield();
}
else
{
offset_y - MIDPOSY);
}
-
ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
while (scroll_x != scroll_xx || scroll_y != scroll_yy)
{
/* !!! not clear why graphic animation should be reset at all here !!! */
/* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
+ /* !!! SOLUTION: do not reset if graphics engine set to 4 or above !!! */
+
+ /*
+ GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
+
+ When using an animation frame delay of 1 (this only happens with
+ "sp_zonk.moving.left/right" in the classic graphics), the default
+ (non-moving) animation shows wrong animation frames (while the
+ moving animation, like "sp_zonk.moving.left/right", is correct,
+ so this graphical bug never shows up with the classic graphics).
+ For an animation with 4 frames, this causes wrong frames 0,0,1,2
+ be drawn instead of the correct frames 0,1,2,3. This is caused by
+ "GfxFrame[][]" being reset *twice* (in two successive frames) after
+ an element change: First when the change delay ("ChangeDelay[][]")
+ counter has reached zero after decrementing, then a second time in
+ the next frame (after "GfxFrame[][]" was already incremented) when
+ "ChangeDelay[][]" is reset to the initial delay value again.
+
+ This causes frame 0 to be drawn twice, while the last frame won't
+ be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
+
+ As some animations may already be cleverly designed around this bug
+ (at least the "Snake Bite" snake tail animation does this), it cannot
+ simply be fixed here without breaking such existing animations.
+ Unfortunately, it cannot easily be detected if a graphics set was
+ designed "before" or "after" the bug was fixed. As a workaround,
+ a new graphics set option "game.graphics_engine_version" was added
+ to be able to specify the game's major release version for which the
+ graphics set was designed, which can then be used to decide if the
+ bugfix should be used (version 4 and above) or not (version 3 or
+ below, or if no version was specified at all, as with old sets).
+
+ (The wrong/fixed animation frames can be tested with the test level set
+ "test_gfxframe" and level "000", which contains a specially prepared
+ custom element at level position (x/y) == (11/9) which uses the zonk
+ animation mentioned above. Using "game.graphics_engine_version: 4"
+ fixes the wrong animation frames, showing the correct frames 0,1,2,3.
+ This can also be seen from the debug output for this test element.)
+ */
+
/* when a custom element is about to change (for example by change delay),
do not reset graphic animation when the custom element is moving */
- if (!IS_MOVING(x, y))
+ if (game.graphics_engine_version < 4 &&
+ !IS_MOVING(x, y))
{
ResetGfxAnimation(x, y);
ResetRandomAnimationValue(x, y);
byte tape_action[MAX_PLAYERS];
int i;
- for (i = 0; i < MAX_PLAYERS; i++)
- {
- struct PlayerInfo *player = &stored_player[i];
-
- // allow engine snapshot if movement attempt was stopped
- if ((game.snapshot.last_action[i] & KEY_MOTION) != 0 &&
- (player->action & KEY_MOTION) == 0)
- game.snapshot.changed_action = TRUE;
-
- // allow engine snapshot in case of snapping/dropping attempt
- if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
- (player->action & KEY_BUTTON) != 0)
- game.snapshot.changed_action = TRUE;
-
- game.snapshot.last_action[i] = player->action;
- }
-
/* detect endless loops, caused by custom element programming */
if (recursion_loop_detected && recursion_loop_depth == 0)
{
if (tape.playing && tape.warp_forward && !tape.pausing)
game_frame_delay_value = 0;
+#if 0
+ /* ---------- main game synchronization point ---------- */
+
+ int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
+
+ printf("::: skip == %d\n", skip);
+
+#else
/* ---------- main game synchronization point ---------- */
WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
+#endif
if (network_playing && !network_player_action_received)
{
#endif
#endif
+ for (i = 0; i < MAX_PLAYERS; i++)
+ {
+ // allow engine snapshot in case of changed movement attempt
+ if ((game.snapshot.last_action[i] & KEY_MOTION) !=
+ (stored_player[i].effective_action & KEY_MOTION))
+ game.snapshot.changed_action = TRUE;
+
+ // allow engine snapshot in case of snapping/dropping attempt
+ if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
+ (stored_player[i].effective_action & KEY_BUTTON) != 0)
+ game.snapshot.changed_action = TRUE;
+
+ game.snapshot.last_action[i] = stored_player[i].effective_action;
+ }
+
if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
{
GameActions_EM_Main();
GameActions_RND_Main();
}
- redraw_mask |= REDRAW_FIELD;
+ BlitScreenToBitmap(backbuffer);
+
+ CheckLevelTime();
+
+ AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
+
+ if (options.debug) /* calculate frames per second */
+ {
+ static unsigned int fps_counter = 0;
+ static int fps_frames = 0;
+ unsigned int fps_delay_ms = Counter() - fps_counter;
+
+ fps_frames++;
+
+ if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
+ {
+ global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
+
+ fps_frames = 0;
+ fps_counter = Counter();
+ }
+
+ redraw_mask |= REDRAW_FPS;
+ }
}
void GameActions_EM_Main()
effective_action[i] = stored_player[i].effective_action;
GameActions_EM(effective_action, warp_mode);
-
- CheckLevelTime();
-
- AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
}
void GameActions_SP_Main()
effective_action[i] = stored_player[i].effective_action;
GameActions_SP(effective_action, warp_mode);
-
- CheckLevelTime();
-
- AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
}
void GameActions_RND_Main()
{
GameActions_RND();
-
- BlitScreenToBitmap_RND(backbuffer);
}
void GameActions_RND()
}
#endif
- CheckLevelTime();
-
DrawAllPlayers();
PlayAllPlayersSound();
- if (options.debug) /* calculate frames per second */
- {
- static unsigned int fps_counter = 0;
- static int fps_frames = 0;
- unsigned int fps_delay_ms = Counter() - fps_counter;
-
- fps_frames++;
-
- if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
- {
- global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
-
- fps_frames = 0;
- fps_counter = Counter();
- }
-
- redraw_mask |= REDRAW_FPS;
- }
-
- AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
-
if (local_player->show_envelope != 0 && local_player->MovPos == 0)
{
ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
game_status = GAME_MODE_MAIN;
- DrawAndFadeInMainMenu(REDRAW_FIELD);
+ DrawMainMenu();
}
else
{
game_status = GAME_MODE_MAIN;
- DrawAndFadeInMainMenu(REDRAW_FIELD);
+ DrawMainMenu();
}
}
}
IMG_GAME_BUTTON_GFX_SAVE, &game.button.save,
GAME_CTRL_ID_SAVE, "save game"
},
+ {
+ IMG_GAME_BUTTON_GFX_PAUSE2, &game.button.pause2,
+ GAME_CTRL_ID_PAUSE2, "pause game"
+ },
{
IMG_GAME_BUTTON_GFX_LOAD, &game.button.load,
GAME_CTRL_ID_LOAD, "load game"
}
if (id == GAME_CTRL_ID_STOP ||
- id == GAME_CTRL_ID_PAUSE ||
id == GAME_CTRL_ID_PLAY ||
id == GAME_CTRL_ID_SAVE ||
id == GAME_CTRL_ID_LOAD)
{
UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
- UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
- MapGadget(game_gadget[GAME_CTRL_ID_PLAY]);
}
void UnmapUndoRedoButtons()
{
UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]);
- UnmapGadget(game_gadget[GAME_CTRL_ID_PLAY]);
MapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
MapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
- MapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
}
void MapGameButtons()
for (i = 0; i < NUM_GAME_BUTTONS; i++)
if (i != GAME_CTRL_ID_UNDO &&
- i != GAME_CTRL_ID_REDO &&
- i != GAME_CTRL_ID_PLAY)
+ i != GAME_CTRL_ID_REDO)
MapGadget(game_gadget[i]);
+
+ if (setup.show_snapshot_buttons)
+ {
+ UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
+ UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
+ UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
+ }
+ else
+ {
+ UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
+ UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
+ UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
+ }
+
+ RedrawGameButtons();
}
void UnmapGameButtons()
break;
case GAME_CTRL_ID_PAUSE:
+ case GAME_CTRL_ID_PAUSE2:
if (options.network && game_status == GAME_MODE_PLAYING)
{
#if defined(NETWORK_AVALIABLE)