#include "files.h"
#include "tape.h"
#include "network.h"
+#include "anim.h"
/* DEBUG SETTINGS */
#define DOUBLE_PLAYER_SPEED(p) (HALVE_MOVE_DELAY( (p)->move_delay_value))
#define HALVE_PLAYER_SPEED(p) (DOUBLE_MOVE_DELAY((p)->move_delay_value))
+/* values for scroll positions */
+#define SCROLL_POSITION_X(x) ((x) < SBX_Left + MIDPOSX ? SBX_Left : \
+ (x) > SBX_Right + MIDPOSX ? SBX_Right :\
+ (x) - MIDPOSX)
+#define SCROLL_POSITION_Y(y) ((y) < SBY_Upper + MIDPOSY ? SBY_Upper :\
+ (y) > SBY_Lower + MIDPOSY ? SBY_Lower :\
+ (y) - MIDPOSY)
+
/* values for other actions */
#define MOVE_STEPSIZE_NORMAL (TILEX / MOVE_DELAY_NORMAL_SPEED)
#define MOVE_STEPSIZE_MIN (1)
#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 */
setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
SetAudioMode(setup.sound);
- InitJoysticks();
}
int GetElementFromGroupElement(int element)
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];
return compare_result;
}
+int getPlayerInventorySize(int player_nr)
+{
+ if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
+ return level.native_em_level->ply[player_nr]->dynamite;
+ else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
+ return level.native_sp_level->game_sp->red_disk_count;
+ else
+ return stored_player[player_nr].inventory_size;
+}
+
void InitGameControlValues()
{
int i;
get_key_element_from_nr(k);
}
- if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
- game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
- level.native_em_level->ply[i]->dynamite;
- else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
- game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
- level.native_sp_level->game_sp->red_disk_count;
- else
- game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
- stored_player[i].inventory_size;
+ game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
+ getPlayerInventorySize(i);
if (stored_player[i].num_white_keys > 0)
game_panel_controls[GAME_PANEL_KEY_WHITE].value =
get_key_element_from_nr(k);
}
- if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
- game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
- level.native_em_level->ply[player_nr]->dynamite;
- else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
- game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
- level.native_sp_level->game_sp->red_disk_count;
- else
- game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
- stored_player[player_nr].inventory_size;
+ game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
+ getPlayerInventorySize(player_nr);
if (stored_player[player_nr].num_white_keys > 0)
game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
/* redraw game control buttons */
RedrawGameButtons();
- game_status = GAME_MODE_PSEUDO_PANEL;
+ SetGameStatus(GAME_MODE_PSEUDO_PANEL);
for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
{
redraw_mask |= REDRAW_DOOR_1;
}
- game_status = GAME_MODE_PLAYING;
+ SetGameStatus(GAME_MODE_PLAYING);
}
void UpdateAndDisplayGameControlValues()
game.use_block_last_field_bug =
(game.engine_version < VERSION_IDENT(3,1,1,0));
+ game_em.use_single_button =
+ (game.engine_version > VERSION_IDENT(4,0,0,2));
+
/* ---------------------------------------------------------------------- */
/* set maximal allowed number of custom element changes per game frame */
for (i = 0; i < MAX_PLAYERS; i++)
game.snapshot.last_action[i] = 0;
game.snapshot.changed_action = FALSE;
+ game.snapshot.collected_item = 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);
+ SNAPSHOT_MODE_EVERY_MOVE :
+ strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
+ SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
+ game.snapshot.save_snapshot = FALSE;
}
int get_num_special_action(int element, int action_first, int action_last)
{
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;
-
- StopAnimation();
+ // required here to update video display before fading (FIX THIS)
+ DrawMaskedBorder(REDRAW_DOOR_2);
if (!game.restart_level)
CloseDoor(DOOR_CLOSE_1);
+ SetGameStatus(GAME_MODE_PLAYING);
+
if (level_editor_test_game)
FadeSkipNextFadeIn();
else
FadeSetEnterScreen();
- FadeOut(REDRAW_FIELD);
+ if (CheckIfGlobalBorderHasChanged())
+ fade_mask = REDRAW_ALL;
+
+ FadeSoundsAndMusic();
+
+ ExpireSoundLoops(TRUE);
+
+ FadeOut(fade_mask);
/* needed if different viewport properties defined for playing */
ChangeViewportPropertiesIfNeeded();
+ ClearField();
+
+ OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
+
DrawCompleteVideoDisplay();
InitGameEngine();
player->is_bored = FALSE;
player->is_sleeping = FALSE;
+ player->was_waiting = TRUE;
+ player->was_moving = FALSE;
+ player->was_snapping = FALSE;
+ player->was_dropping = FALSE;
+
+ player->force_dropping = FALSE;
+
player->frame_counter_bored = -1;
player->frame_counter_sleeping = -1;
}
}
- scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
- start_x > SBX_Right + MIDPOSX ? SBX_Right :
- start_x - MIDPOSX);
-
- scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
- start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
- start_y - MIDPOSY);
+ scroll_x = SCROLL_POSITION_X(start_x);
+ scroll_y = SCROLL_POSITION_Y(start_y);
}
else
{
- scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
- local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
- local_player->jx - MIDPOSX);
-
- scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
- local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
- local_player->jy - MIDPOSY);
+ scroll_x = SCROLL_POSITION_X(local_player->jx);
+ scroll_y = SCROLL_POSITION_Y(local_player->jy);
}
/* !!! FIX THIS (START) !!! */
/* 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();
SaveEngineSnapshotToListInitial();
}
-void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
+void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
+ int actual_player_x, int actual_player_y)
{
/* this is used for non-R'n'D game engines to update certain engine values */
+ if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
+ {
+ actual_player_x = correctLevelPosX_EM(actual_player_x);
+ actual_player_y = correctLevelPosY_EM(actual_player_y);
+ }
+
/* needed to determine if sounds are played within the visible screen area */
scroll_x = actual_scroll_x;
scroll_y = actual_scroll_y;
+
+ /* needed to get player position for "follow finger" playing input method */
+ local_player->jx = actual_player_x;
+ local_player->jy = actual_player_y;
}
void InitMovDir(int x, int y)
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;
+ SetGameStatus(GAME_MODE_MAIN);
- DrawAndFadeInMainMenu(REDRAW_FIELD);
+ DrawMainMenu();
return;
}
if (!local_player->LevelSolved_SaveScore)
{
- FadeOut(REDRAW_FIELD);
-
- game_status = GAME_MODE_MAIN;
+ SetGameStatus(GAME_MODE_MAIN);
- DrawAndFadeInMainMenu(REDRAW_FIELD);
+ DrawMainMenu();
return;
}
SaveLevelSetup_SeriesInfo();
}
- if (level_nr < leveldir_current->last_level)
+ if (setup.increment_levels &&
+ level_nr < leveldir_current->last_level)
raise_level = TRUE; /* advance to next level */
if ((hi_pos = NewHiScore()) >= 0)
{
- game_status = GAME_MODE_SCORES;
+ SetGameStatus(GAME_MODE_SCORES);
DrawHallOfFame(hi_pos);
}
else
{
- FadeOut(REDRAW_FIELD);
-
- game_status = GAME_MODE_MAIN;
+ SetGameStatus(GAME_MODE_MAIN);
if (raise_level)
{
TapeErase();
}
- DrawAndFadeInMainMenu(REDRAW_FIELD);
+ DrawMainMenu();
}
}
{
int k, l;
int position = -1;
+ boolean one_score_entry_per_name = !program.many_scores_per_name;
LoadScore(level_nr);
{
int m = MAX_SCORE_ENTRIES - 1;
-#ifdef ONE_PER_NAME
- for (l = k; l < MAX_SCORE_ENTRIES; l++)
- if (strEqual(setup.player_name, highscore[l].Name))
- m = l;
- if (m == k) /* player's new highscore overwrites his old one */
- goto put_into_list;
-#endif
+ if (one_score_entry_per_name)
+ {
+ for (l = k; l < MAX_SCORE_ENTRIES; l++)
+ if (strEqual(setup.player_name, highscore[l].Name))
+ m = l;
+
+ if (m == k) /* player's new highscore overwrites his old one */
+ goto put_into_list;
+ }
for (l = m; l > k; l--)
{
}
}
-#ifdef ONE_PER_NAME
put_into_list:
-#endif
+
strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
highscore[k].Score = local_player->score_final;
position = k;
+
break;
}
-
-#ifdef ONE_PER_NAME
- else if (!strncmp(setup.player_name, highscore[k].Name,
+ else if (one_score_entry_per_name &&
+ !strncmp(setup.player_name, highscore[k].Name,
MAX_PLAYER_NAME_LEN))
break; /* player already there with a higher score */
-#endif
-
}
if (position >= 0)
}
}
-static void ResetGfxFrame(int x, int y, boolean redraw)
+static void ResetGfxFrame(int x, int y)
{
int element = Feld[x][y];
int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
- int last_gfx_frame = GfxFrame[x][y];
if (graphic_info[graphic].anim_global_sync)
GfxFrame[x][y] = FrameCounter;
GfxFrame[x][y] = element_info[element].collect_score;
else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
GfxFrame[x][y] = ChangeDelay[x][y];
-
- if (redraw && GfxFrame[x][y] != last_gfx_frame)
- DrawLevelGraphicAnimation(x, y, graphic);
}
static void ResetGfxAnimation(int x, int y)
GfxDir[x][y] = MovDir[x][y];
GfxFrame[x][y] = 0;
- ResetGfxFrame(x, y, FALSE);
+ ResetGfxFrame(x, y);
}
static void ResetRandomAnimationValue(int x, int y)
void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
boolean center_screen, boolean quick_relocation)
{
+ unsigned int frame_delay_value_old = GetVideoFrameDelay();
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 new_scroll_x, new_scroll_y;
- if (quick_relocation)
+ if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
{
- if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
- {
- if (!level.shifted_relocation || center_screen)
- {
- /* quick relocation (without scrolling), with centering of screen */
-
- scroll_x = (x < SBX_Left + MIDPOSX ? SBX_Left :
- x > SBX_Right + MIDPOSX ? SBX_Right :
- x - MIDPOSX);
-
- scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
- y > SBY_Lower + MIDPOSY ? SBY_Lower :
- y - MIDPOSY);
- }
- else
- {
- /* quick relocation (without scrolling), but do not center screen */
+ /* case 1: quick relocation inside visible screen (without scrolling) */
- int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
- old_x > SBX_Right + MIDPOSX ? SBX_Right :
- old_x - MIDPOSX);
+ RedrawPlayfield();
- int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
- old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
- old_y - MIDPOSY);
-
- int offset_x = x + (scroll_x - center_scroll_x);
- int offset_y = y + (scroll_y - center_scroll_y);
-
- scroll_x = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
- offset_x > SBX_Right + MIDPOSX ? SBX_Right :
- offset_x - MIDPOSX);
-
- scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
- offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
- offset_y - MIDPOSY);
- }
- }
- else
- {
- if (!level.shifted_relocation || center_screen)
- {
- /* quick relocation (without scrolling), with centering of screen */
-
- scroll_x = (x < SBX_Left + MIDPOSX ? SBX_Left :
- x > SBX_Right + MIDPOSX ? SBX_Right :
- x - MIDPOSX);
-
- scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
- y > SBY_Lower + MIDPOSY ? SBY_Lower :
- y - MIDPOSY);
- }
- else
- {
- /* quick relocation (without scrolling), but do not center screen */
-
- int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
- old_x > SBX_Right + MIDPOSX ? SBX_Right :
- old_x - MIDPOSX);
-
- int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
- old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
- old_y - MIDPOSY);
-
- int offset_x = x + (scroll_x - center_scroll_x);
- int offset_y = y + (scroll_y - center_scroll_y);
-
- scroll_x = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
- offset_x > SBX_Right + MIDPOSX ? SBX_Right :
- offset_x - MIDPOSX);
+ return;
+ }
- scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
- offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
- offset_y - MIDPOSY);
- }
- }
+ if (!level.shifted_relocation || center_screen)
+ {
+ /* relocation _with_ centering of screen */
- RedrawPlayfield(TRUE, 0,0,0,0);
+ new_scroll_x = SCROLL_POSITION_X(x);
+ new_scroll_y = SCROLL_POSITION_Y(y);
}
else
{
- int scroll_xx, scroll_yy;
+ /* relocation _without_ centering of screen */
- if (!level.shifted_relocation || center_screen)
- {
- /* visible relocation (with scrolling), with centering of screen */
+ int center_scroll_x = SCROLL_POSITION_X(old_x);
+ int center_scroll_y = SCROLL_POSITION_Y(old_y);
+ int offset_x = x + (scroll_x - center_scroll_x);
+ int offset_y = y + (scroll_y - center_scroll_y);
- scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left :
- x > SBX_Right + MIDPOSX ? SBX_Right :
- x - MIDPOSX);
+ /* for new screen position, apply previous offset to center position */
+ new_scroll_x = SCROLL_POSITION_X(offset_x);
+ new_scroll_y = SCROLL_POSITION_Y(offset_y);
+ }
- scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
- y > SBY_Lower + MIDPOSY ? SBY_Lower :
- y - MIDPOSY);
- }
- else
- {
- /* visible relocation (with scrolling), but do not center screen */
+ if (quick_relocation)
+ {
+ /* case 2: quick relocation (redraw without visible scrolling) */
- int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
- old_x > SBX_Right + MIDPOSX ? SBX_Right :
- old_x - MIDPOSX);
+ scroll_x = new_scroll_x;
+ scroll_y = new_scroll_y;
- int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
- old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
- old_y - MIDPOSY);
+ RedrawPlayfield();
- int offset_x = x + (scroll_x - center_scroll_x);
- int offset_y = y + (scroll_y - center_scroll_y);
+ return;
+ }
- scroll_xx = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
- offset_x > SBX_Right + MIDPOSX ? SBX_Right :
- offset_x - MIDPOSX);
+ /* case 3: visible relocation (with scrolling to new position) */
- scroll_yy = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
- offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
- offset_y - MIDPOSY);
- }
+ ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
+ SetVideoFrameDelay(wait_delay_value);
- ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
+ while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
+ {
+ int dx = 0, dy = 0;
+ int fx = FX, fy = FY;
- while (scroll_x != scroll_xx || scroll_y != scroll_yy)
- {
- int dx = 0, dy = 0;
- int fx = FX, fy = FY;
+ dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
+ dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
- dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
- dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
+ if (dx == 0 && dy == 0) /* no scrolling needed at all */
+ break;
- if (dx == 0 && dy == 0) /* no scrolling needed at all */
- break;
+ scroll_x -= dx;
+ scroll_y -= dy;
- scroll_x -= dx;
- scroll_y -= dy;
+ fx += dx * TILEX / 2;
+ fy += dy * TILEY / 2;
- fx += dx * TILEX / 2;
- fy += dy * TILEY / 2;
+ ScrollLevel(dx, dy);
+ DrawAllPlayers();
- 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);
- /* scroll in two steps of half tile size to make things smoother */
- BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
- Delay(wait_delay_value);
+ /* scroll second step to align at full tile size */
+ BlitScreenToBitmap(window);
+ }
- /* scroll second step to align at full tile size */
- BackToFront();
- Delay(wait_delay_value);
- }
+ DrawAllPlayers();
+ BackToFront();
- DrawAllPlayers();
- BackToFront();
- Delay(wait_delay_value);
- }
+ SetVideoFrameDelay(frame_delay_value_old);
}
void RelocatePlayer(int jx, int jy, int el_player_raw)
DrawPlayer(player);
- BackToFront();
- Delay(wait_delay_value);
+ BackToFront_WithFrameDelay(wait_delay_value);
}
DrawPlayer(player); /* needed here only to cleanup last field */
if (MovDelay[x][y])
GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
- ResetGfxFrame(x, y, FALSE);
+ ResetGfxFrame(x, y);
}
static boolean JustBeingPushed(int x, int y)
{
local_player->gems_still_needed = action_arg_number_new;
+ game.snapshot.collected_item = TRUE;
+
game_panel_controls[GAME_PANEL_GEMS].value =
local_player->gems_still_needed;
{
/* !!! 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);
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 ((!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())
+ if (!CheckSaveEngineSnapshotToList())
return;
- player_was_moving = FALSE;
- player_was_snapping = TRUE;
- player_was_dropping = TRUE;
+ player->was_moving = FALSE;
+ player->was_snapping = TRUE;
+ player->was_dropping = TRUE;
}
else
{
if (player->is_moving)
- player_was_moving = TRUE;
+ player->was_moving = TRUE;
if (!player->is_snapping)
- player_was_snapping = FALSE;
+ player->was_snapping = FALSE;
if (!player->is_dropping)
- player_was_dropping = FALSE;
+ player->was_dropping = FALSE;
}
}
{
/* as it is called "single step mode", just return to pause mode when the
player stopped moving after one tile (or never starts moving at all) */
- if (!player->is_moving && !player->is_pushing)
+ if (!player->is_moving &&
+ !player->is_pushing &&
+ !player->is_dropping_pressed)
{
TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
SnapField(player, 0, 0); /* stop snapping */
InitGame();
}
-void GameActions()
+void GameActionsExt()
{
+#if 0
static unsigned int game_frame_delay = 0;
+#endif
unsigned int game_frame_delay_value;
byte *recorded_player_action;
byte summarized_player_action = 0;
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;
+ SetVideoFrameDelay(game_frame_delay_value);
+
+#if 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
+#endif
if (network_playing && !network_player_action_received)
{
SendToServer_MovePlayer(summarized_player_action);
#endif
+ // summarize all actions at local players mapped input device position
+ // (this allows using different input devices in single player mode)
if (!options.network && !game.team_mode)
- local_player->effective_action = summarized_player_action;
+ stored_player[map_player_action[local_player->index_nr]].effective_action =
+ summarized_player_action;
if (tape.recording &&
setup.team_mode &&
#if USE_NEW_PLAYER_ASSIGNMENTS
// !!! also map player actions in single player mode !!!
// if (game.team_mode)
+ if (1)
{
byte mapped_action[MAX_PLAYERS];
#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();
}
else
{
- GameActions_RND();
+ GameActions_RND_Main();
+ }
+
+ BlitScreenToBitmap(backbuffer);
+
+ CheckLevelTime();
+
+ AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
+
+ if (global.show_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();
+
+ /* always draw FPS to screen after FPS value was updated */
+ redraw_mask |= REDRAW_FPS;
+ }
+
+ /* only draw FPS if no screen areas are deactivated (invisible warp mode) */
+ if (GetDrawDeactivationMask() == REDRAW_NONE)
+ redraw_mask |= REDRAW_FPS;
}
}
+static void GameActions_CheckSaveEngineSnapshot()
+{
+ if (!game.snapshot.save_snapshot)
+ return;
+
+ // clear flag for saving snapshot _before_ saving snapshot
+ game.snapshot.save_snapshot = FALSE;
+
+ SaveEngineSnapshotToList();
+}
+
+void GameActions()
+{
+ GameActionsExt();
+
+ GameActions_CheckSaveEngineSnapshot();
+}
+
void GameActions_EM_Main()
{
byte effective_action[MAX_PLAYERS];
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()
GameActions_SP(effective_action, warp_mode);
- CheckLevelTime();
+ for (i = 0; i < MAX_PLAYERS; i++)
+ {
+ if (stored_player[i].force_dropping)
+ stored_player[i].action |= KEY_BUTTON_DROP;
- AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
+ stored_player[i].force_dropping = FALSE;
+ }
+}
+
+void GameActions_RND_Main()
+{
+ GameActions_RND();
}
void GameActions_RND()
{
int magic_wall_x = 0, magic_wall_y = 0;
- int i, x, y, element, graphic;
+ int i, x, y, element, graphic, last_gfx_frame;
InitPlayfieldScanModeVars();
{
element = Feld[x][y];
graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
+ last_gfx_frame = GfxFrame[x][y];
+
+ ResetGfxFrame(x, y);
- ResetGfxFrame(x, y, TRUE);
+ if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
+ DrawLevelGraphicAnimation(x, y, graphic);
if (ANIM_MODE(graphic) == ANIM_RANDOM &&
IS_NEXT_FRAME(GfxFrame[x][y], graphic))
if (IS_GEM(element) || element == EL_SP_INFOTRON)
TEST_DrawTwinkleOnField(x, y);
}
- else if ((element == EL_ACID ||
- element == EL_EXIT_OPEN ||
+ else if (element == EL_ACID)
+ {
+ if (!Stop[x][y])
+ DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
+ }
+ else if ((element == EL_EXIT_OPEN ||
element == EL_EM_EXIT_OPEN ||
element == EL_SP_EXIT_OPEN ||
element == EL_STEEL_EXIT_OPEN ||
}
#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);
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)
{
AdvanceFrameAndPlayerCounters(player->index_nr);
DrawAllPlayers();
- BackToFront();
+ BackToFront_WithFrameDelay(0);
}
player->move_delay_value = original_move_delay_value;
if (local_player->gems_still_needed < 0)
local_player->gems_still_needed = 0;
+ game.snapshot.collected_item = TRUE;
+
game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
DisplayGameControlValues();
SCAN_PLAYFIELD(xx, yy)
{
if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
+ {
Bang(xx, yy);
+ }
else if (Feld[xx][yy] == EL_SP_TERMINAL)
+ {
Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
+
+ ResetGfxAnimation(xx, yy);
+ TEST_DrawLevelField(xx, yy);
+ }
}
}
else if (IS_BELT_SWITCH(element))
int drop_side = drop_direction;
int drop_element = get_next_dropped_element(player);
- player->is_dropping_pressed = TRUE;
-
/* do not drop an element on top of another element; when holding drop key
pressed without moving, dropped element must move away before the next
element can be dropped (this is especially important if the next element
if (new_element == EL_UNDEFINED)
return FALSE;
+ /* only set if player has anything that can be dropped */
+ player->is_dropping_pressed = TRUE;
+
/* check if drop key was pressed long enough for EM style dynamite */
if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
return FALSE;
#endif
{
if (quick_quit)
- {
FadeSkipNextFadeIn();
- game_status = GAME_MODE_MAIN;
-
- DrawAndFadeInMainMenu(REDRAW_FIELD);
- }
- else
- {
- game_status = GAME_MODE_MAIN;
+ SetGameStatus(GAME_MODE_MAIN);
- DrawAndFadeInMainMenu(REDRAW_FIELD);
- }
+ DrawMainMenu();
}
}
else /* continue playing the game */
snapshot_level_nr = level_nr;
}
-static boolean SaveEngineSnapshotToListExt(boolean initial_snapshot)
+boolean CheckSaveEngineSnapshotToList()
{
boolean save_snapshot =
- (initial_snapshot ||
- (game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
+ ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
(game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
- game.snapshot.changed_action));
+ game.snapshot.changed_action) ||
+ (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
+ game.snapshot.collected_item));
game.snapshot.changed_action = FALSE;
+ game.snapshot.collected_item = FALSE;
+ game.snapshot.save_snapshot = save_snapshot;
+
+ return save_snapshot;
+}
+void SaveEngineSnapshotToList()
+{
if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
- tape.quick_resume ||
- !save_snapshot)
- return FALSE;
+ tape.quick_resume)
+ return;
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);
+ SaveEngineSnapshotToList();
}
void LoadEngineSnapshotValues()
} gamebutton_info[NUM_GAME_BUTTONS] =
{
{
- IMG_GAME_BUTTON_GFX_STOP, &game.button.stop,
+ IMG_GFX_GAME_BUTTON_STOP, &game.button.stop,
GAME_CTRL_ID_STOP, "stop game"
},
{
- IMG_GAME_BUTTON_GFX_PAUSE, &game.button.pause,
+ IMG_GFX_GAME_BUTTON_PAUSE, &game.button.pause,
GAME_CTRL_ID_PAUSE, "pause game"
},
{
- IMG_GAME_BUTTON_GFX_PLAY, &game.button.play,
+ IMG_GFX_GAME_BUTTON_PLAY, &game.button.play,
GAME_CTRL_ID_PLAY, "play game"
},
{
- IMG_GAME_BUTTON_GFX_UNDO, &game.button.undo,
+ IMG_GFX_GAME_BUTTON_UNDO, &game.button.undo,
GAME_CTRL_ID_UNDO, "undo step"
},
{
- IMG_GAME_BUTTON_GFX_REDO, &game.button.redo,
+ IMG_GFX_GAME_BUTTON_REDO, &game.button.redo,
GAME_CTRL_ID_REDO, "redo step"
},
{
- IMG_GAME_BUTTON_GFX_SAVE, &game.button.save,
+ IMG_GFX_GAME_BUTTON_SAVE, &game.button.save,
GAME_CTRL_ID_SAVE, "save game"
},
{
- IMG_GAME_BUTTON_GFX_LOAD, &game.button.load,
+ IMG_GFX_GAME_BUTTON_PAUSE2, &game.button.pause2,
+ GAME_CTRL_ID_PAUSE2, "pause game"
+ },
+ {
+ IMG_GFX_GAME_BUTTON_LOAD, &game.button.load,
GAME_CTRL_ID_LOAD, "load game"
},
{
- IMG_GAME_BUTTON_GFX_SOUND_MUSIC, &game.button.sound_music,
+ IMG_GFX_GAME_BUTTON_SOUND_MUSIC, &game.button.sound_music,
SOUND_CTRL_ID_MUSIC, "background music on/off"
},
{
- IMG_GAME_BUTTON_GFX_SOUND_LOOPS, &game.button.sound_loops,
+ IMG_GFX_GAME_BUTTON_SOUND_LOOPS, &game.button.sound_loops,
SOUND_CTRL_ID_LOOPS, "sound loops on/off"
},
{
- IMG_GAME_BUTTON_GFX_SOUND_SIMPLE, &game.button.sound_simple,
+ IMG_GFX_GAME_BUTTON_SOUND_SIMPLE, &game.button.sound_simple,
SOUND_CTRL_ID_SIMPLE, "normal sounds on/off"
}
};
}
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)
FreeGadget(game_gadget[i]);
}
-static void MapGameButtonsAtSamePosition(int id)
+static void UnmapGameButtonsAtSamePosition(int id)
{
int 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]);
+ UnmapGadget(game_gadget[i]);
}
-static void UnmapGameButtonsAtSamePosition(int id)
+static void UnmapGameButtonsAtSamePosition_All()
+{
+ 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);
+ }
+}
+
+static void MapGameButtonsAtSamePosition(int id)
{
int 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]);
+ MapGadget(game_gadget[i]);
+
+ UnmapGameButtonsAtSamePosition_All();
}
void MapUndoRedoButtons()
{
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]);
+
+ ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, TRUE, GDI_END);
}
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);
+
+ ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, FALSE, GDI_END);
}
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]);
+
+ UnmapGameButtonsAtSamePosition_All();
+
+ RedrawGameButtons();
}
void UnmapGameButtons()
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);
+ DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
BackToFront();
}
static void HandleGameButtonsExt(int id, int button)
{
+ static boolean game_undo_executed = FALSE;
int steps = BUTTON_STEPSIZE(button);
boolean handle_game_buttons =
(game_status == GAME_MODE_PLAYING ||
break;
case GAME_CTRL_ID_PAUSE:
+ case GAME_CTRL_ID_PAUSE2:
if (options.network && game_status == GAME_MODE_PLAYING)
{
#if defined(NETWORK_AVALIABLE)
}
else
TapeTogglePause(TAPE_TOGGLE_MANUAL);
+
+ game_undo_executed = FALSE;
+
break;
case GAME_CTRL_ID_PLAY:
SendToServer_ContinuePlaying();
else
#endif
- TapeTogglePause(TAPE_TOGGLE_MANUAL);
+ TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
}
break;
case GAME_CTRL_ID_UNDO:
+ // Important: When using "save snapshot when collecting an item" mode,
+ // load last (current) snapshot for first "undo" after pressing "pause"
+ // (else the last-but-one snapshot would be loaded, because the snapshot
+ // pointer already points to the last snapshot when pressing "pause",
+ // which is fine for "every step/move" mode, but not for "every collect")
+ if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
+ !game_undo_executed)
+ steps--;
+
+ game_undo_executed = TRUE;
+
GameUndo(steps);
break;