X-Git-Url: https://git.artsoft.org/?p=rocksndiamonds.git;a=blobdiff_plain;f=src%2Fgame.c;h=41add9e6745afd10fb6ad28fafe4e438d6aa7e9e;hp=7b63ee751bfc090fbb93364d9f12612a937d254c;hb=1afe0b4fb9e16c78f2bb810c1225098f5d77e2d3;hpb=f7165d318c089b77d0a91a77afc44cb8866dc1cc diff --git a/src/game.c b/src/game.c index 7b63ee75..41add9e6 100644 --- a/src/game.c +++ b/src/game.c @@ -15,6 +15,7 @@ #include "init.h" #include "tools.h" #include "screens.h" +#include "events.h" #include "files.h" #include "tape.h" #include "network.h" @@ -970,13 +971,16 @@ static struct GamePanelControlInfo game_panel_controls[] = #define GAME_CTRL_ID_STOP 0 #define GAME_CTRL_ID_PAUSE 1 #define GAME_CTRL_ID_PLAY 2 -#define SOUND_CTRL_ID_MUSIC 3 -#define SOUND_CTRL_ID_LOOPS 4 -#define SOUND_CTRL_ID_SIMPLE 5 -#define GAME_CTRL_ID_SAVE 6 +#define GAME_CTRL_ID_UNDO 3 +#define GAME_CTRL_ID_REDO 4 +#define GAME_CTRL_ID_SAVE 5 +#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 8 +#define NUM_GAME_BUTTONS 11 /* forward declaration for internal use */ @@ -1945,7 +1949,7 @@ static void InitField(int x, int y, boolean init_game) 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); @@ -1955,7 +1959,7 @@ static inline void InitField_WithBug1(int x, int y, boolean 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]; @@ -2575,7 +2579,7 @@ void DisplayGameControlValues() void UpdateAndDisplayGameControlValues() { - if (tape.warp_forward) + if (tape.deactivate_display) return; UpdateGameControlValues(); @@ -3031,6 +3035,16 @@ static void InitGameEngine() setup.scroll_delay ? setup.scroll_delay_value : 0); game.scroll_delay_value = MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY); + + /* ---------- initialize game engine snapshots ---------------------------- */ + for (i = 0; i < MAX_PLAYERS; i++) + game.snapshot.last_action[i] = 0; + game.snapshot.changed_action = FALSE; + game.snapshot.mode = + (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ? + SNAPSHOT_MODE_EVERY_STEP : + strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ? + SNAPSHOT_MODE_EVERY_MOVE : SNAPSHOT_MODE_OFF); } int get_num_special_action(int element, int action_first, int action_last) @@ -3069,6 +3083,7 @@ void InitGame() { 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 */ @@ -3076,22 +3091,30 @@ void InitGame() 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(); @@ -3903,11 +3926,11 @@ void InitGame() /* 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: @@ -3936,9 +3959,14 @@ void InitGame() { 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(); @@ -3991,6 +4019,8 @@ void InitGame() } game.restart_level = FALSE; + + SaveEngineSnapshotToListInitial(); } void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y) @@ -4377,18 +4407,21 @@ void GameEnd() 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; } @@ -4399,7 +4432,7 @@ void GameEnd() game_status = GAME_MODE_MAIN; - DrawAndFadeInMainMenu(REDRAW_FIELD); + DrawMainMenu(); return; } @@ -4438,7 +4471,7 @@ void GameEnd() TapeErase(); } - DrawAndFadeInMainMenu(REDRAW_FIELD); + DrawMainMenu(); } } @@ -4863,7 +4896,11 @@ void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir, 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) { @@ -4942,7 +4979,7 @@ void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir, } } - RedrawPlayfield(TRUE, 0,0,0,0); + RedrawPlayfield(); } else { @@ -4984,7 +5021,6 @@ void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir, offset_y - MIDPOSY); } - ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */ while (scroll_x != scroll_xx || scroll_y != scroll_yy) @@ -10216,9 +10252,50 @@ static void HandleElementChange(int x, int y, int page) { /* !!! 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); @@ -10668,6 +10745,37 @@ static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting) } } +static void CheckSaveEngineSnapshot(struct PlayerInfo *player) +{ + static boolean player_was_moving = FALSE; + static boolean player_was_snapping = FALSE; + static boolean player_was_dropping = FALSE; + + if ((!player->is_moving && player_was_moving) || + (player->MovPos == 0 && player_was_moving) || + (player->is_snapping && !player_was_snapping) || + (player->is_dropping && !player_was_dropping)) + { + if (!SaveEngineSnapshotToList()) + return; + + player_was_moving = FALSE; + player_was_snapping = TRUE; + player_was_dropping = TRUE; + } + else + { + if (player->is_moving) + player_was_moving = TRUE; + + if (!player->is_snapping) + player_was_snapping = FALSE; + + if (!player->is_dropping) + player_was_dropping = FALSE; + } +} + static void CheckSingleStepMode(struct PlayerInfo *player) { if (tape.single_step && tape.recording && !tape.pausing) @@ -10680,6 +10788,8 @@ static void CheckSingleStepMode(struct PlayerInfo *player) SnapField(player, 0, 0); /* stop snapping */ } } + + CheckSaveEngineSnapshot(player); } static byte PlayerActions(struct PlayerInfo *player, byte player_action) @@ -10832,6 +10942,9 @@ static void CheckLevelTime() DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime); } + if (tape.recording || tape.playing) + DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter); + UpdateAndDisplayGameControlValues(); } @@ -10984,9 +11097,18 @@ void GameActions() 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) { @@ -11110,6 +11232,21 @@ void GameActions() #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(); @@ -11120,7 +11257,32 @@ void GameActions() } else { - GameActions_RND(); + GameActions_RND_Main(); + } + + 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; } } @@ -11134,10 +11296,6 @@ 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() @@ -11150,10 +11308,11 @@ 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(); } void GameActions_RND() @@ -11655,32 +11814,9 @@ 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); @@ -11734,16 +11870,16 @@ static boolean AllPlayersInVisibleScreen() void ScrollLevel(int dx, int dy) { - int softscroll_offset = (setup.soft_scrolling ? 2 * TILEX_VAR : 0); + int scroll_offset = 2 * TILEX_VAR; int x, y; BlitBitmap(drawto_field, drawto_field, - FX + TILEX_VAR * (dx == -1) - softscroll_offset, - FY + TILEY_VAR * (dy == -1) - softscroll_offset, - SXSIZE - TILEX_VAR * (dx != 0) + 2 * softscroll_offset, - SYSIZE - TILEY_VAR * (dy != 0) + 2 * softscroll_offset, - FX + TILEX_VAR * (dx == 1) - softscroll_offset, - FY + TILEY_VAR * (dy == 1) - softscroll_offset); + FX + TILEX_VAR * (dx == -1) - scroll_offset, + FY + TILEY_VAR * (dy == -1) - scroll_offset, + SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset, + SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset, + FX + TILEX_VAR * (dx == 1) - scroll_offset, + FY + TILEY_VAR * (dy == 1) - scroll_offset); if (dx != 0) { @@ -12305,6 +12441,9 @@ void ScrollPlayer(struct PlayerInfo *player, int mode) if (tape.single_step && tape.recording && !tape.pausing && !player->programmed_action) TapeTogglePause(TAPE_TOGGLE_AUTOMATIC); + + if (!player->programmed_action) + CheckSaveEngineSnapshot(player); } } @@ -14386,13 +14525,13 @@ void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message) game_status = GAME_MODE_MAIN; - DrawAndFadeInMainMenu(REDRAW_FIELD); + DrawMainMenu(); } else { game_status = GAME_MODE_MAIN; - DrawAndFadeInMainMenu(REDRAW_FIELD); + DrawMainMenu(); } } } @@ -14548,22 +14687,22 @@ static void LoadEngineSnapshotValues_RND() } } -void FreeEngineSnapshot() +void FreeEngineSnapshotSingle() { - FreeEngineSnapshotBuffers(); + FreeSnapshotSingle(); setString(&snapshot_level_identifier, NULL); snapshot_level_nr = -1; } -void SaveEngineSnapshot() +void FreeEngineSnapshotList() { - /* do not save snapshots from editor */ - if (level_editor_test_game) - return; + FreeSnapshotList(); +} - /* free previous snapshot buffers, if needed */ - FreeEngineSnapshotBuffers(); +ListNode *SaveEngineSnapshotBuffers() +{ + ListNode *buffers = NULL; /* copy some special values to a structure better suited for the snapshot */ @@ -14572,87 +14711,82 @@ void SaveEngineSnapshot() if (level.game_engine_type == GAME_ENGINE_TYPE_EM) SaveEngineSnapshotValues_EM(); if (level.game_engine_type == GAME_ENGINE_TYPE_SP) - SaveEngineSnapshotValues_SP(); + SaveEngineSnapshotValues_SP(&buffers); /* save values stored in special snapshot structure */ if (level.game_engine_type == GAME_ENGINE_TYPE_RND) - SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd)); + SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd)); if (level.game_engine_type == GAME_ENGINE_TYPE_EM) - SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em)); + SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em)); if (level.game_engine_type == GAME_ENGINE_TYPE_SP) - SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp)); + SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp)); /* save further RND engine values */ - SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player)); - SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game)); - SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape)); - - SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX)); - SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY)); - SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX)); - SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY)); - - SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter)); - SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames)); - SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed)); - SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft)); - SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime)); - - SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir)); - SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos)); - SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos)); - - SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize)); - - SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone)); - - SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt)); - SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2)); - - SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld)); - SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos)); - SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir)); - SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay)); - SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay)); - SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage)); - SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue)); - SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store)); - SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2)); - SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer)); - SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back)); - SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr)); - SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving)); - SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling)); - SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision)); - SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact)); - SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop)); - SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed)); - - SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount)); - SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent)); - - SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase)); - SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay)); - SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField)); - - SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit)); - SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit)); - - SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame)); - SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom)); - SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement)); - SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction)); - SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir)); - - SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x)); - SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y)); - - /* save level identification information */ - - setString(&snapshot_level_identifier, leveldir_current->identifier); - snapshot_level_nr = level_nr; + SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player)); + SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game)); + SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape)); + + SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZX)); + SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZY)); + SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitX)); + SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitY)); + + SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter)); + SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames)); + SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed)); + SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft)); + SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime)); + + SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir)); + SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos)); + SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos)); + + SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize)); + + SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone)); + + SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt)); + SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2)); + + SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Feld)); + SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos)); + SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir)); + SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay)); + SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay)); + SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage)); + SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue)); + SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store)); + SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2)); + SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer)); + SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back)); + SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr)); + SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving)); + SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling)); + SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision)); + SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact)); + SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop)); + SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed)); + + SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount)); + SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent)); + + SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase)); + SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay)); + SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField)); + + SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit)); + SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit)); + + SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame)); + SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom)); + SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement)); + SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction)); + SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir)); + + SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x)); + SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y)); #if 0 ListNode *node = engine_snapshot_list_rnd; @@ -14667,14 +14801,59 @@ void SaveEngineSnapshot() printf("::: size of engine snapshot: %d bytes\n", num_bytes); #endif + + return buffers; +} + +void SaveEngineSnapshotSingle() +{ + ListNode *buffers = SaveEngineSnapshotBuffers(); + + /* finally save all snapshot buffers to single snapshot */ + SaveSnapshotSingle(buffers); + + /* save level identification information */ + setString(&snapshot_level_identifier, leveldir_current->identifier); + snapshot_level_nr = level_nr; } -void LoadEngineSnapshot() +static boolean SaveEngineSnapshotToListExt(boolean initial_snapshot) { - /* restore generically stored snapshot buffers */ + boolean save_snapshot = + (initial_snapshot || + (game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) || + (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE && + game.snapshot.changed_action)); - LoadEngineSnapshotBuffers(); + game.snapshot.changed_action = FALSE; + if (game.snapshot.mode == SNAPSHOT_MODE_OFF || + tape.quick_resume || + !save_snapshot) + return FALSE; + + ListNode *buffers = SaveEngineSnapshotBuffers(); + + /* finally save all snapshot buffers to snapshot list */ + SaveSnapshotToList(buffers); + + return TRUE; +} + +boolean SaveEngineSnapshotToList() +{ + return SaveEngineSnapshotToListExt(FALSE); +} + +void SaveEngineSnapshotToListInitial() +{ + FreeEngineSnapshotList(); + + SaveEngineSnapshotToListExt(TRUE); +} + +void LoadEngineSnapshotValues() +{ /* restore special values from snapshot structure */ if (level.game_engine_type == GAME_ENGINE_TYPE_RND) @@ -14685,19 +14864,45 @@ void LoadEngineSnapshot() LoadEngineSnapshotValues_SP(); } -boolean CheckEngineSnapshot() +void LoadEngineSnapshotSingle() +{ + LoadSnapshotSingle(); + + LoadEngineSnapshotValues(); +} + +void LoadEngineSnapshot_Undo(int steps) +{ + LoadSnapshotFromList_Older(steps); + + LoadEngineSnapshotValues(); +} + +void LoadEngineSnapshot_Redo(int steps) +{ + LoadSnapshotFromList_Newer(steps); + + LoadEngineSnapshotValues(); +} + +boolean CheckEngineSnapshotSingle() { return (strEqual(snapshot_level_identifier, leveldir_current->identifier) && snapshot_level_nr == level_nr); } +boolean CheckEngineSnapshotList() +{ + return CheckSnapshotList(); +} + /* ---------- new game button stuff ---------------------------------------- */ static struct { int graphic; - struct Rect *pos; + struct XY *pos; int gadget_id; char *infotext; } gamebutton_info[NUM_GAME_BUTTONS] = @@ -14715,24 +14920,36 @@ static struct GAME_CTRL_ID_PLAY, "play game" }, { - IMG_GAME_BUTTON_GFX_SOUND_MUSIC, &game.button.sound_music, - SOUND_CTRL_ID_MUSIC, "background music on/off" - }, - { - IMG_GAME_BUTTON_GFX_SOUND_LOOPS, &game.button.sound_loops, - SOUND_CTRL_ID_LOOPS, "sound loops on/off" + IMG_GAME_BUTTON_GFX_UNDO, &game.button.undo, + GAME_CTRL_ID_UNDO, "undo step" }, { - IMG_GAME_BUTTON_GFX_SOUND_SIMPLE, &game.button.sound_simple, - SOUND_CTRL_ID_SIMPLE, "normal sounds on/off" + IMG_GAME_BUTTON_GFX_REDO, &game.button.redo, + GAME_CTRL_ID_REDO, "redo step" }, { 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" + }, + { + IMG_GAME_BUTTON_GFX_SOUND_MUSIC, &game.button.sound_music, + SOUND_CTRL_ID_MUSIC, "background music on/off" + }, + { + IMG_GAME_BUTTON_GFX_SOUND_LOOPS, &game.button.sound_loops, + SOUND_CTRL_ID_LOOPS, "sound loops on/off" + }, + { + IMG_GAME_BUTTON_GFX_SOUND_SIMPLE, &game.button.sound_simple, + SOUND_CTRL_ID_SIMPLE, "normal sounds on/off" } }; @@ -14743,7 +14960,7 @@ void CreateGameButtons() for (i = 0; i < NUM_GAME_BUTTONS; i++) { struct GraphicInfo *gfx = &graphic_info[gamebutton_info[i].graphic]; - struct Rect *pos = gamebutton_info[i].pos; + struct XY *pos = gamebutton_info[i].pos; struct GadgetInfo *gi; int button_type; boolean checked; @@ -14768,7 +14985,6 @@ void CreateGameButtons() } 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) @@ -14777,6 +14993,13 @@ void CreateGameButtons() checked = FALSE; event_mask = GD_EVENT_RELEASED; } + else if (id == GAME_CTRL_ID_UNDO || + id == GAME_CTRL_ID_REDO) + { + button_type = GD_TYPE_NORMAL_BUTTON; + checked = FALSE; + event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED; + } else { button_type = GD_TYPE_CHECK_BUTTON; @@ -14820,12 +15043,69 @@ void FreeGameButtons() FreeGadget(game_gadget[i]); } +static void MapGameButtonsAtSamePosition(int id) +{ + int i; + + for (i = 0; i < NUM_GAME_BUTTONS; i++) + if (i != id && + gamebutton_info[i].pos->x == gamebutton_info[id].pos->x && + gamebutton_info[i].pos->y == gamebutton_info[id].pos->y) + MapGadget(game_gadget[i]); +} + +static void UnmapGameButtonsAtSamePosition(int id) +{ + int i; + + for (i = 0; i < NUM_GAME_BUTTONS; i++) + if (i != id && + gamebutton_info[i].pos->x == gamebutton_info[id].pos->x && + gamebutton_info[i].pos->y == gamebutton_info[id].pos->y) + UnmapGadget(game_gadget[i]); +} + +void MapUndoRedoButtons() +{ + UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO); + UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO); + + MapGadget(game_gadget[GAME_CTRL_ID_UNDO]); + MapGadget(game_gadget[GAME_CTRL_ID_REDO]); +} + +void UnmapUndoRedoButtons() +{ + UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]); + UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]); + + MapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO); + MapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO); +} + void MapGameButtons() { int i; for (i = 0; i < NUM_GAME_BUTTONS; i++) - MapGadget(game_gadget[i]); + if (i != GAME_CTRL_ID_UNDO && + 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() @@ -14847,8 +15127,47 @@ void RedrawGameButtons() redraw_mask &= ~REDRAW_ALL; } -static void HandleGameButtonsExt(int id) +void GameUndoRedoExt() +{ + ClearPlayerAction(); + + tape.pausing = TRUE; + + RedrawPlayfield(); + UpdateAndDisplayGameControlValues(); + + DrawCompleteVideoDisplay(); + DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime); + DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter); + DrawVideoDisplay((tape.single_step ? VIDEO_STATE_1STEP_ON : + VIDEO_STATE_1STEP_OFF), 0); + + BackToFront(); +} + +void GameUndo(int steps) { + if (!CheckEngineSnapshotList()) + return; + + LoadEngineSnapshot_Undo(steps); + + GameUndoRedoExt(); +} + +void GameRedo(int steps) +{ + if (!CheckEngineSnapshotList()) + return; + + LoadEngineSnapshot_Redo(steps); + + GameUndoRedoExt(); +} + +static void HandleGameButtonsExt(int id, int button) +{ + int steps = BUTTON_STEPSIZE(button); boolean handle_game_buttons = (game_status == GAME_MODE_PLAYING || (game_status == GAME_MODE_MAIN && tape.show_game_buttons)); @@ -14870,6 +15189,7 @@ static void HandleGameButtonsExt(int id) break; case GAME_CTRL_ID_PAUSE: + case GAME_CTRL_ID_PAUSE2: if (options.network && game_status == GAME_MODE_PLAYING) { #if defined(NETWORK_AVALIABLE) @@ -14895,13 +15215,26 @@ static void HandleGameButtonsExt(int id) SendToServer_ContinuePlaying(); else #endif - { - tape.pausing = FALSE; - DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0); - } + TapeTogglePause(TAPE_TOGGLE_MANUAL); } break; + case GAME_CTRL_ID_UNDO: + GameUndo(steps); + break; + + case GAME_CTRL_ID_REDO: + GameRedo(steps); + break; + + case GAME_CTRL_ID_SAVE: + TapeQuickSave(); + break; + + case GAME_CTRL_ID_LOAD: + TapeQuickLoad(); + break; + case SOUND_CTRL_ID_MUSIC: if (setup.sound_music) { @@ -14941,14 +15274,6 @@ static void HandleGameButtonsExt(int id) } break; - case GAME_CTRL_ID_SAVE: - TapeQuickSave(); - break; - - case GAME_CTRL_ID_LOAD: - TapeQuickLoad(); - break; - default: break; } @@ -14956,7 +15281,7 @@ static void HandleGameButtonsExt(int id) static void HandleGameButtons(struct GadgetInfo *gi) { - HandleGameButtonsExt(gi->custom_id); + HandleGameButtonsExt(gi->custom_id, gi->event.button); } void HandleSoundButtonKeys(Key key)