X-Git-Url: https://git.artsoft.org/?p=rocksndiamonds.git;a=blobdiff_plain;f=src%2Fgame.c;h=3d2f055f27c88415dccb499d83d65932ad8a5f1e;hp=a88b3f04ec1a8525e904b45e13418c94580ea9c3;hb=567cfcb396e3fc031a25362b747dd0a5a096b4ec;hpb=1e422c29292f0583391b0ce9e9c872b38f035ac0 diff --git a/src/game.c b/src/game.c index a88b3f04..3d2f055f 100644 --- a/src/game.c +++ b/src/game.c @@ -1,15 +1,13 @@ -/*********************************************************** -* Rocks'n'Diamonds -- McDuffin Strikes Back! * -*----------------------------------------------------------* -* (c) 1995-2006 Artsoft Entertainment * -* Holger Schemel * -* Detmolder Strasse 189 * -* 33604 Bielefeld * -* Germany * -* e-mail: info@artsoft.org * -*----------------------------------------------------------* -* game.c * -***********************************************************/ +// ============================================================================ +// Rocks'n'Diamonds - McDuffin Strikes Back! +// ---------------------------------------------------------------------------- +// (c) 1995-2014 by Artsoft Entertainment +// Holger Schemel +// info@artsoft.org +// http://www.artsoft.org/ +// ---------------------------------------------------------------------------- +// game.c +// ============================================================================ #include "libgame/libgame.h" @@ -17,57 +15,25 @@ #include "init.h" #include "tools.h" #include "screens.h" +#include "events.h" #include "files.h" #include "tape.h" #include "network.h" +#include "anim.h" + + +/* DEBUG SETTINGS */ +#define DEBUG_INIT_PLAYER 1 +#define DEBUG_PLAYER_ACTIONS 0 /* EXPERIMENTAL STUFF */ #define USE_NEW_AMOEBA_CODE FALSE /* EXPERIMENTAL STUFF */ -#define USE_NEW_STUFF ( 1) - -#define USE_NEW_SP_SLIPPERY (USE_NEW_STUFF * 1) -#define USE_NEW_CUSTOM_VALUE (USE_NEW_STUFF * 1) -#define USE_NEW_PLAYER_ANIM (USE_NEW_STUFF * 1) -#define USE_NEW_ALL_SLIPPERY (USE_NEW_STUFF * 1) -#define USE_NEW_PLAYER_SPEED (USE_NEW_STUFF * 1) -#define USE_NEW_DELAYED_ACTION (USE_NEW_STUFF * 1) -#define USE_NEW_SNAP_DELAY (USE_NEW_STUFF * 1) -#define USE_ONLY_ONE_CHANGE_PER_FRAME (USE_NEW_STUFF * 1) -#define USE_ONE_MORE_CHANGE_PER_FRAME (USE_NEW_STUFF * 1) -#define USE_FIXED_DONT_RUN_INTO (USE_NEW_STUFF * 1) -#define USE_NEW_SPRING_BUMPER (USE_NEW_STUFF * 1) -#define USE_STOP_CHANGED_ELEMENTS (USE_NEW_STUFF * 1) -#define USE_ELEMENT_TOUCHING_BUGFIX (USE_NEW_STUFF * 1) -#define USE_NEW_CONTINUOUS_SNAPPING (USE_NEW_STUFF * 1) -#define USE_GFX_RESET_GFX_ANIMATION (USE_NEW_STUFF * 1) -#define USE_BOTH_SWITCHGATE_SWITCHES (USE_NEW_STUFF * 1) -#define USE_PLAYER_GRAVITY (USE_NEW_STUFF * 1) -#define USE_FIXED_BORDER_RUNNING_GFX (USE_NEW_STUFF * 1) -#define USE_QUICKSAND_BD_ROCK_BUGFIX (USE_NEW_STUFF * 0) - -#define USE_QUICKSAND_IMPACT_BUGFIX (USE_NEW_STUFF * 0) - -#define USE_CODE_THAT_BREAKS_SNAKE_BITE (USE_NEW_STUFF * 1) - -#define USE_UFAST_PLAYER_EXIT_BUGFIX (USE_NEW_STUFF * 1) - -#define USE_GFX_RESET_ONLY_WHEN_MOVING (USE_NEW_STUFF * 1) -#define USE_GFX_RESET_PLAYER_ARTWORK (USE_NEW_STUFF * 1) - -#define USE_FIX_KILLED_BY_NON_WALKABLE (USE_NEW_STUFF * 1) -#define USE_FIX_IMPACT_COLLISION (USE_NEW_STUFF * 1) -#define USE_FIX_CE_ACTION_WITH_PLAYER (USE_NEW_STUFF * 1) -#define USE_FIX_NO_ACTION_AFTER_CHANGE (USE_NEW_STUFF * 1) - -#define USE_PLAYER_REANIMATION (USE_NEW_STUFF * 1) - -#define USE_GFX_RESET_WHEN_NOT_MOVING (USE_NEW_STUFF * 1) - -#define USE_NEW_PLAYER_ASSIGNMENTS (USE_NEW_STUFF * 1) - -#define USE_DELAYED_GFX_REDRAW (USE_NEW_STUFF * 0) +#define USE_QUICKSAND_BD_ROCK_BUGFIX 0 +#define USE_QUICKSAND_IMPACT_BUGFIX 0 +#define USE_DELAYED_GFX_REDRAW 0 +#define USE_NEW_PLAYER_ASSIGNMENTS 1 #if USE_DELAYED_GFX_REDRAW #define TEST_DrawLevelField(x, y) \ @@ -120,45 +86,7 @@ #define PANEL_XPOS(p) (DX + ALIGNED_TEXT_XPOS(p)) #define PANEL_YPOS(p) (DY + ALIGNED_TEXT_YPOS(p)) -/* special positions in the game control window (relative to control window) */ -#define XX_LEVEL1 (PANEL_XPOS(game.panel.level)) -#define XX_LEVEL2 (PANEL_XPOS(game.panel.level) - 1) -#define XX_LEVEL (PANEL_XPOS(game.panel.level)) -#define YY_LEVEL (PANEL_YPOS(game.panel.level)) -#define XX_EMERALDS (PANEL_XPOS(game.panel.gems)) -#define YY_EMERALDS (PANEL_YPOS(game.panel.gems)) -#define XX_DYNAMITE (PANEL_XPOS(game.panel.inventory)) -#define YY_DYNAMITE (PANEL_YPOS(game.panel.inventory)) -#define XX_KEYS (PANEL_XPOS(game.panel.keys)) -#define YY_KEYS (PANEL_YPOS(game.panel.keys)) -#define XX_SCORE (PANEL_XPOS(game.panel.score)) -#define YY_SCORE (PANEL_YPOS(game.panel.score)) -#define XX_TIME1 (PANEL_XPOS(game.panel.time)) -#define XX_TIME2 (PANEL_XPOS(game.panel.time) + 1) -#define XX_TIME (PANEL_XPOS(game.panel.time)) -#define YY_TIME (PANEL_YPOS(game.panel.time)) - -/* special positions in the game control window (relative to main window) */ -#define DX_LEVEL1 (DX + XX_LEVEL1) -#define DX_LEVEL2 (DX + XX_LEVEL2) -#define DX_LEVEL (DX + XX_LEVEL) -#define DY_LEVEL (DY + YY_LEVEL) -#define DX_EMERALDS (DX + XX_EMERALDS) -#define DY_EMERALDS (DY + YY_EMERALDS) -#define DX_DYNAMITE (DX + XX_DYNAMITE) -#define DY_DYNAMITE (DY + YY_DYNAMITE) -#define DX_KEYS (DX + XX_KEYS) -#define DY_KEYS (DY + YY_KEYS) -#define DX_SCORE (DX + XX_SCORE) -#define DY_SCORE (DY + YY_SCORE) -#define DX_TIME1 (DX + XX_TIME1) -#define DX_TIME2 (DX + XX_TIME2) -#define DX_TIME (DX + XX_TIME) -#define DY_TIME (DY + YY_TIME) - -#if 1 /* game panel display and control definitions */ - #define GAME_PANEL_LEVEL_NUMBER 0 #define GAME_PANEL_GEMS 1 #define GAME_PANEL_INVENTORY_COUNT 2 @@ -876,8 +804,6 @@ static struct GamePanelControlInfo game_panel_controls[] = -1, } }; -#endif - /* values for delayed check of falling and moving elements and for collision */ #define CHECK_DELAY_MOVING 3 @@ -1046,11 +972,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_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 6 +#define NUM_GAME_BUTTONS 11 /* forward declaration for internal use */ @@ -1082,9 +1013,6 @@ static void KillPlayerUnlessExplosionProtected(int, int); static void TestIfPlayerTouchesCustomElement(int, int); static void TestIfElementTouchesCustomElement(int, int); static void TestIfElementHitsCustomElement(int, int, int); -#if 0 -static void TestIfElementSmashesCustomElement(int, int, int); -#endif static void HandleElementChange(int, int, int); static void ExecuteCustomElementAction(int, int, int, int); @@ -1117,7 +1045,6 @@ static void PlayLevelSoundActionIfLoop(int, int, int); static void StopLevelSoundActionIfLoop(int, int, int); static void PlayLevelMusic(); -static void MapGameButtons(); static void HandleGameButtons(struct GadgetInfo *); int AmoebeNachbarNr(int, int); @@ -1271,11 +1198,7 @@ static struct ChangingElementInfo change_delay_list[] = }, { EL_EM_EXIT_CLOSING, -#if 1 EL_EMPTY, -#else - EL_EM_EXIT_CLOSED, -#endif 29, NULL, NULL, @@ -1291,11 +1214,7 @@ static struct ChangingElementInfo change_delay_list[] = }, { EL_EM_STEEL_EXIT_CLOSING, -#if 1 EL_STEELWALL, -#else - EL_EM_STEEL_EXIT_CLOSED, -#endif 29, NULL, NULL, @@ -1780,14 +1699,16 @@ static void InitPlayerField(int x, int y, int element, boolean init_game) StorePlayer[x][y] = Feld[x][y]; +#if DEBUG_INIT_PLAYER if (options.debug) { - printf("Player %d activated.\n", player->element_nr); - printf("[Local player is %d and currently %s.]\n", + printf("- player element %d activated", player->element_nr); + printf(" (local player is %d and currently %s)\n", local_player->element_nr, local_player->active ? "active" : "not active"); } } +#endif Feld[x][y] = EL_EMPTY; @@ -1795,7 +1716,6 @@ static void InitPlayerField(int x, int y, int element, boolean init_game) player->jy = player->last_jy = y; } -#if USE_PLAYER_REANIMATION if (!init_game) { int player_nr = GET_PLAYER_NR(element); @@ -1804,7 +1724,6 @@ static void InitPlayerField(int x, int y, int element, boolean init_game) if (player->active && player->killed) player->reanimated = TRUE; /* if player was just killed, reanimate him */ } -#endif } static void InitField(int x, int y, boolean init_game) @@ -1957,18 +1876,6 @@ static void InitField(int x, int y, boolean init_game) } break; -#if !USE_BOTH_SWITCHGATE_SWITCHES - case EL_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */ - if (init_game) - Feld[x][y] = EL_SWITCHGATE_SWITCH_UP; - break; - - case EL_DC_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */ - if (init_game) - Feld[x][y] = EL_DC_SWITCHGATE_SWITCH_UP; - break; -#endif - case EL_LIGHT_SWITCH_ACTIVE: if (init_game) game.light_time_left = level.time_light * FRAMES_PER_SECOND; @@ -2026,10 +1933,8 @@ static void InitField(int x, int y, boolean init_game) if (CAN_MOVE(element)) InitMovDir(x, y); -#if USE_NEW_CUSTOM_VALUE if (!element_info[element].use_last_ce_value || init_game) CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]); -#endif } else if (IS_GROUP_ELEMENT(element)) { @@ -2045,7 +1950,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); @@ -2055,7 +1960,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]; @@ -2076,8 +1981,6 @@ static inline void InitField_WithBug2(int x, int y, boolean init_game) */ } -#if 1 - static int get_key_element_from_nr(int key_nr) { int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS : @@ -2229,7 +2132,7 @@ void UpdateGameControlValues() level.native_em_level->lev->time : level.game_engine_type == GAME_ENGINE_TYPE_SP ? level.native_sp_level->game_sp->time_played : - level.time == 0 ? TimePlayed : TimeLeft); + game.no_time_limit ? TimePlayed : TimeLeft); int score = (local_player->LevelSolved ? local_player->LevelSolved_CountingScore : level.game_engine_type == GAME_ENGINE_TYPE_EM ? @@ -2437,12 +2340,8 @@ void UpdateGameControlValues() game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value = game.magic_wall_time_left; -#if USE_PLAYER_GRAVITY game_panel_controls[GAME_PANEL_GRAVITY_STATE].value = local_player->gravity; -#else - game_panel_controls[GAME_PANEL_GRAVITY_STATE].value = game.gravity; -#endif for (i = 0; i < NUM_PANEL_GRAPHICS; i++) game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i; @@ -2539,47 +2438,25 @@ void DisplayGameControlValues() return; /* copy default game door content to main double buffer */ -#if 1 + /* !!! CHECK AGAIN !!! */ SetPanelBackground(); // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL); DrawBackground(DX, DY, DXSIZE, DYSIZE); -#else - BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, - DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY); -#endif /* redraw game control buttons */ -#if 1 RedrawGameButtons(); -#else - UnmapGameButtons(); - MapGameButtons(); -#endif - game_status = GAME_MODE_PSEUDO_PANEL; + SetGameStatus(GAME_MODE_PSEUDO_PANEL); -#if 1 for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++) -#else - for (i = 0; game_panel_controls[i].nr != -1; i++) -#endif { -#if 1 int nr = game_panel_order[i].nr; struct GamePanelControlInfo *gpc = &game_panel_controls[nr]; -#else - struct GamePanelControlInfo *gpc = &game_panel_controls[i]; - int nr = gpc->nr; -#endif struct TextPosInfo *pos = gpc->pos; int type = gpc->type; int value = gpc->value; int frame = gpc->frame; -#if 0 - int last_value = gpc->last_value; - int last_frame = gpc->last_frame; -#endif int size = pos->size; int font = pos->font; boolean draw_masked = pos->draw_masked; @@ -2588,18 +2465,9 @@ void DisplayGameControlValues() if (PANEL_DEACTIVATED(pos)) continue; -#if 0 - if (value == last_value && frame == last_frame) - continue; -#endif - gpc->last_value = value; gpc->last_frame = frame; -#if 0 - printf("::: value %d changed from %d to %d\n", nr, last_value, value); -#endif - if (type == TYPE_INTEGER) { if (nr == GAME_PANEL_LEVEL_NUMBER || @@ -2617,33 +2485,15 @@ void DisplayGameControlValues() size = (value < value_change ? size1 : size2); font = (value < value_change ? font1 : font2); - -#if 0 - /* clear background if value just changed its size (dynamic digits) */ - if ((last_value < value_change) != (value < value_change)) - { - int width1 = size1 * getFontWidth(font1); - int width2 = size2 * getFontWidth(font2); - int max_width = MAX(width1, width2); - int max_height = MAX(getFontHeight(font1), getFontHeight(font2)); - - pos->width = max_width; - - ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos), - max_width, max_height); - } -#endif } } -#if 1 /* correct text size if "digits" is zero or less */ if (size <= 0) size = strlen(int2str(value, size)); /* dynamically correct text alignment */ pos->width = size * getFontWidth(font); -#endif DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, size), font, mask_mode); @@ -2657,7 +2507,6 @@ void DisplayGameControlValues() int dst_x = PANEL_XPOS(pos); int dst_y = PANEL_YPOS(pos); -#if 1 if (value != EL_UNDEFINED && value != EL_EMPTY) { element = value; @@ -2665,10 +2514,8 @@ void DisplayGameControlValues() // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size); -#if 1 if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0) size = TILESIZE; -#endif getSizedGraphicSource(graphic, frame, size, &src_bitmap, &src_x, &src_y); @@ -2677,41 +2524,12 @@ void DisplayGameControlValues() height = graphic_info[graphic].height * size / TILESIZE; if (draw_masked) - { - SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc, - dst_x - src_x, dst_y - src_y); BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height, dst_x, dst_y); - } else - { BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height, dst_x, dst_y); - } - } -#else - if (value == EL_UNDEFINED || value == EL_EMPTY) - { - element = (last_value == EL_UNDEFINED ? EL_EMPTY : last_value); - graphic = el2panelimg(element); - - src_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap; - src_x = DOOR_GFX_PAGEX5 + ALIGNED_TEXT_XPOS(pos); - src_y = DOOR_GFX_PAGEY1 + ALIGNED_TEXT_YPOS(pos); - } - else - { - element = value; - graphic = el2panelimg(value); - - getSizedGraphicSource(graphic, frame, size, &src_bitmap, &src_x,&src_y); } - - width = graphic_info[graphic].width * size / TILESIZE; - height = graphic_info[graphic].height * size / TILESIZE; - - BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height, dst_x, dst_y); -#endif } else if (type == TYPE_STRING) { @@ -2728,20 +2546,6 @@ void DisplayGameControlValues() { int font1 = pos->font; /* (used for normal state) */ int font2 = pos->font_alt; /* (used for active state) */ -#if 0 - int size1 = strlen(state_normal); - int size2 = strlen(state_active); - int width1 = size1 * getFontWidth(font1); - int width2 = size2 * getFontWidth(font2); - int max_width = MAX(width1, width2); - int max_height = MAX(getFontHeight(font1), getFontHeight(font2)); - - pos->width = max_width; - - /* clear background for values that may have changed its size */ - ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos), - max_width, max_height); -#endif font = (active ? font2 : font1); } @@ -2750,7 +2554,6 @@ void DisplayGameControlValues() { char *s_cut; -#if 1 if (size <= 0) { /* don't truncate output if "chars" is zero or less */ @@ -2759,7 +2562,6 @@ void DisplayGameControlValues() /* dynamically correct text alignment */ pos->width = size * getFontWidth(font); } -#endif s_cut = getStringCopyN(s, size); @@ -2773,597 +2575,157 @@ void DisplayGameControlValues() redraw_mask |= REDRAW_DOOR_1; } - game_status = GAME_MODE_PLAYING; + SetGameStatus(GAME_MODE_PLAYING); } void UpdateAndDisplayGameControlValues() { - if (tape.warp_forward) + if (tape.deactivate_display) return; UpdateGameControlValues(); DisplayGameControlValues(); } -void DrawGameValue_Emeralds(int value) +void UpdateGameDoorValues() { - struct TextPosInfo *pos = &game.panel.gems; -#if 1 - int font_nr = pos->font; -#else - int font_nr = FONT_TEXT_2; -#endif - int font_width = getFontWidth(font_nr); - int chars = pos->size; - -#if 1 - return; /* !!! USE NEW STUFF !!! */ -#endif - - if (PANEL_DEACTIVATED(pos)) - return; - - pos->width = chars * font_width; - - DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr); + UpdateGameControlValues(); } -void DrawGameValue_Dynamite(int value) +void DrawGameDoorValues() { - struct TextPosInfo *pos = &game.panel.inventory_count; -#if 1 - int font_nr = pos->font; -#else - int font_nr = FONT_TEXT_2; -#endif - int font_width = getFontWidth(font_nr); - int chars = pos->size; - -#if 1 - return; /* !!! USE NEW STUFF !!! */ -#endif - - if (PANEL_DEACTIVATED(pos)) - return; + DisplayGameControlValues(); +} - pos->width = chars * font_width; - DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr); -} +/* + ============================================================================= + InitGameEngine() + ----------------------------------------------------------------------------- + initialize game engine due to level / tape version number + ============================================================================= +*/ -void DrawGameValue_Score(int value) +static void InitGameEngine() { - struct TextPosInfo *pos = &game.panel.score; -#if 1 - int font_nr = pos->font; -#else - int font_nr = FONT_TEXT_2; -#endif - int font_width = getFontWidth(font_nr); - int chars = pos->size; + int i, j, k, l, x, y; -#if 1 - return; /* !!! USE NEW STUFF !!! */ -#endif + /* set game engine from tape file when re-playing, else from level file */ + game.engine_version = (tape.playing ? tape.engine_version : + level.game_version); - if (PANEL_DEACTIVATED(pos)) - return; + /* set single or multi-player game mode (needed for re-playing tapes) */ + game.team_mode = setup.team_mode; - pos->width = chars * font_width; + if (tape.playing) + { + int num_players = 0; - DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr); -} + for (i = 0; i < MAX_PLAYERS; i++) + if (tape.player_participates[i]) + num_players++; -void DrawGameValue_Time(int value) -{ - struct TextPosInfo *pos = &game.panel.time; - static int last_value = -1; - int chars1 = 3; - int chars2 = 4; - int chars = pos->size; -#if 1 - int font1_nr = pos->font; - int font2_nr = pos->font_alt; -#else - int font1_nr = FONT_TEXT_2; - int font2_nr = FONT_TEXT_1; -#endif - int font_nr = font1_nr; - boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE); + /* multi-player tapes contain input data for more than one player */ + game.team_mode = (num_players > 1); + } -#if 1 - return; /* !!! USE NEW STUFF !!! */ -#endif + /* ---------------------------------------------------------------------- */ + /* set flags for bugs and changes according to active game engine version */ + /* ---------------------------------------------------------------------- */ - if (PANEL_DEACTIVATED(pos)) - return; + /* + Summary of bugfix/change: + Fixed handling for custom elements that change when pushed by the player. - if (use_dynamic_chars) /* use dynamic number of chars */ - { - chars = (value < 1000 ? chars1 : chars2); - font_nr = (value < 1000 ? font1_nr : font2_nr); - } + Fixed/changed in version: + 3.1.0 - /* clear background if value just changed its size (dynamic chars only) */ - if (use_dynamic_chars && (last_value < 1000) != (value < 1000)) - { - int width1 = chars1 * getFontWidth(font1_nr); - int width2 = chars2 * getFontWidth(font2_nr); - int max_width = MAX(width1, width2); - int max_height = MAX(getFontHeight(font1_nr), getFontHeight(font2_nr)); + Description: + Before 3.1.0, custom elements that "change when pushing" changed directly + after the player started pushing them (until then handled in "DigField()"). + Since 3.1.0, these custom elements are not changed until the "pushing" + move of the element is finished (now handled in "ContinueMoving()"). - pos->width = max_width; + Affected levels/tapes: + The first condition is generally needed for all levels/tapes before version + 3.1.0, which might use the old behaviour before it was changed; known tapes + that are affected are some tapes from the level set "Walpurgis Gardens" by + Jamie Cullen. + The second condition is an exception from the above case and is needed for + the special case of tapes recorded with game (not engine!) version 3.1.0 or + above (including some development versions of 3.1.0), but before it was + known that this change would break tapes like the above and was fixed in + 3.1.1, so that the changed behaviour was active although the engine version + while recording maybe was before 3.1.0. There is at least one tape that is + affected by this exception, which is the tape for the one-level set "Bug + Machine" by Juergen Bonhagen. + */ - ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos), - max_width, max_height); - } + game.use_change_when_pushing_bug = + (game.engine_version < VERSION_IDENT(3,1,0,0) && + !(tape.playing && + tape.game_version >= VERSION_IDENT(3,1,0,0) && + tape.game_version < VERSION_IDENT(3,1,1,0))); - pos->width = chars * getFontWidth(font_nr); + /* + Summary of bugfix/change: + Fixed handling for blocking the field the player leaves when moving. - DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr); + Fixed/changed in version: + 3.1.1 - last_value = value; -} + Description: + Before 3.1.1, when "block last field when moving" was enabled, the field + the player is leaving when moving was blocked for the time of the move, + and was directly unblocked afterwards. This resulted in the last field + being blocked for exactly one less than the number of frames of one player + move. Additionally, even when blocking was disabled, the last field was + blocked for exactly one frame. + Since 3.1.1, due to changes in player movement handling, the last field + is not blocked at all when blocking is disabled. When blocking is enabled, + the last field is blocked for exactly the number of frames of one player + move. Additionally, if the player is Murphy, the hero of Supaplex, the + last field is blocked for exactly one more than the number of frames of + one player move. -void DrawGameValue_Level(int value) -{ - struct TextPosInfo *pos = &game.panel.level_number; - int chars1 = 2; - int chars2 = 3; - int chars = pos->size; -#if 1 - int font1_nr = pos->font; - int font2_nr = pos->font_alt; -#else - int font1_nr = FONT_TEXT_2; - int font2_nr = FONT_TEXT_1; -#endif - int font_nr = font1_nr; - boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE); + Affected levels/tapes: + (!!! yet to be determined -- probably many !!!) + */ -#if 1 - return; /* !!! USE NEW STUFF !!! */ -#endif + game.use_block_last_field_bug = + (game.engine_version < VERSION_IDENT(3,1,1,0)); - if (PANEL_DEACTIVATED(pos)) - return; + /* ---------------------------------------------------------------------- */ - if (use_dynamic_chars) /* use dynamic number of chars */ - { - chars = (level_nr < 100 ? chars1 : chars2); - font_nr = (level_nr < 100 ? font1_nr : font2_nr); - } + /* set maximal allowed number of custom element changes per game frame */ + game.max_num_changes_per_frame = 1; - pos->width = chars * getFontWidth(font_nr); + /* default scan direction: scan playfield from top/left to bottom/right */ + InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL); - DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr); -} + /* dynamically adjust element properties according to game engine version */ + InitElementPropertiesEngine(game.engine_version); -void DrawGameValue_Keys(int key[MAX_NUM_KEYS]) -{ -#if 0 - struct TextPosInfo *pos = &game.panel.keys; -#endif #if 0 - int base_key_graphic = EL_KEY_1; + printf("level %d: level version == %06d\n", level_nr, level.game_version); + printf(" tape version == %06d [%s] [file: %06d]\n", + tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"), + tape.file_version); + printf(" => game.engine_version == %06d\n", game.engine_version); #endif - int i; -#if 1 - return; /* !!! USE NEW STUFF !!! */ -#endif + /* ---------- initialize player's initial move delay --------------------- */ -#if 0 - if (PANEL_DEACTIVATED(pos)) - return; -#endif + /* dynamically adjust player properties according to level information */ + for (i = 0; i < MAX_PLAYERS; i++) + game.initial_move_delay_value[i] = + get_move_delay_from_stepsize(level.initial_player_stepsize[i]); -#if 0 - if (level.game_engine_type == GAME_ENGINE_TYPE_EM) - base_key_graphic = EL_EM_KEY_1; -#endif - -#if 0 - pos->width = 4 * MINI_TILEX; -#endif - -#if 1 - for (i = 0; i < MAX_NUM_KEYS; i++) -#else - /* currently only 4 of 8 possible keys are displayed */ - for (i = 0; i < STD_NUM_KEYS; i++) -#endif - { -#if 1 - struct TextPosInfo *pos = &game.panel.key[i]; -#endif - int src_x = DOOR_GFX_PAGEX5 + 18 + (i % 4) * MINI_TILEX; - int src_y = DOOR_GFX_PAGEY1 + 123; -#if 1 - int dst_x = PANEL_XPOS(pos); - int dst_y = PANEL_YPOS(pos); -#else - int dst_x = PANEL_XPOS(pos) + i * MINI_TILEX; - int dst_y = PANEL_YPOS(pos); -#endif - -#if 1 - int element = (i >= STD_NUM_KEYS ? EL_EMC_KEY_5 - 4 : - level.game_engine_type == GAME_ENGINE_TYPE_EM ? EL_EM_KEY_1 : - EL_KEY_1) + i; - int graphic = el2edimg(element); -#endif - -#if 1 - if (PANEL_DEACTIVATED(pos)) - continue; -#endif - -#if 0 - /* masked blit with tiles from half-size scaled bitmap does not work yet - (no mask bitmap created for these sizes after loading and scaling) -- - solution: load without creating mask, scale, then create final mask */ - - BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y, - MINI_TILEX, MINI_TILEY, dst_x, dst_y); - - if (key[i]) - { -#if 0 - int graphic = el2edimg(base_key_graphic + i); -#endif - Bitmap *src_bitmap; - int src_x, src_y; - - getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y); - - SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc, - dst_x - src_x, dst_y - src_y); - BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, MINI_TILEX, MINI_TILEY, - dst_x, dst_y); - } -#else -#if 1 - if (key[i]) - DrawMiniGraphicExt(drawto, dst_x, dst_y, graphic); - else - BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y, - MINI_TILEX, MINI_TILEY, dst_x, dst_y); -#else - if (key[i]) - DrawMiniGraphicExt(drawto, dst_x, dst_y, el2edimg(base_key_graphic + i)); - else - BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y, - MINI_TILEX, MINI_TILEY, dst_x, dst_y); -#endif -#endif - } -} - -#else - -void DrawGameValue_Emeralds(int value) -{ - int font_nr = FONT_TEXT_2; - int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2; - - if (PANEL_DEACTIVATED(game.panel.gems)) - return; - - DrawText(DX_EMERALDS + xpos, DY_EMERALDS, int2str(value, 3), font_nr); -} - -void DrawGameValue_Dynamite(int value) -{ - int font_nr = FONT_TEXT_2; - int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2; - - if (PANEL_DEACTIVATED(game.panel.inventory_count)) - return; - - DrawText(DX_DYNAMITE + xpos, DY_DYNAMITE, int2str(value, 3), font_nr); -} - -void DrawGameValue_Score(int value) -{ - int font_nr = FONT_TEXT_2; - int xpos = (5 * 14 - 5 * getFontWidth(font_nr)) / 2; - - if (PANEL_DEACTIVATED(game.panel.score)) - return; - - DrawText(DX_SCORE + xpos, DY_SCORE, int2str(value, 5), font_nr); -} - -void DrawGameValue_Time(int value) -{ - int font1_nr = FONT_TEXT_2; -#if 1 - int font2_nr = FONT_TEXT_1; -#else - int font2_nr = FONT_LEVEL_NUMBER; -#endif - int xpos3 = (3 * 14 - 3 * getFontWidth(font1_nr)) / 2; - int xpos4 = (4 * 10 - 4 * getFontWidth(font2_nr)) / 2; - - if (PANEL_DEACTIVATED(game.panel.time)) - return; - - /* clear background if value just changed its size */ - if (value == 999 || value == 1000) - ClearRectangleOnBackground(drawto, DX_TIME1, DY_TIME, 14 * 3, 14); - - if (value < 1000) - DrawText(DX_TIME1 + xpos3, DY_TIME, int2str(value, 3), font1_nr); - else - DrawText(DX_TIME2 + xpos4, DY_TIME, int2str(value, 4), font2_nr); -} - -void DrawGameValue_Level(int value) -{ - int font1_nr = FONT_TEXT_2; -#if 1 - int font2_nr = FONT_TEXT_1; -#else - int font2_nr = FONT_LEVEL_NUMBER; -#endif - - if (PANEL_DEACTIVATED(game.panel.level)) - return; - - if (level_nr < 100) - DrawText(DX_LEVEL1, DY_LEVEL, int2str(value, 2), font1_nr); - else - DrawText(DX_LEVEL2, DY_LEVEL, int2str(value, 3), font2_nr); -} - -void DrawGameValue_Keys(int key[MAX_NUM_KEYS]) -{ - int base_key_graphic = EL_KEY_1; - int i; - - if (PANEL_DEACTIVATED(game.panel.keys)) - return; - - if (level.game_engine_type == GAME_ENGINE_TYPE_EM) - base_key_graphic = EL_EM_KEY_1; - - /* currently only 4 of 8 possible keys are displayed */ - for (i = 0; i < STD_NUM_KEYS; i++) - { - int x = XX_KEYS + i * MINI_TILEX; - int y = YY_KEYS; - - if (key[i]) - DrawMiniGraphicExt(drawto, DX + x,DY + y, el2edimg(base_key_graphic + i)); - else - BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, - DOOR_GFX_PAGEX5 + x, y, MINI_TILEX, MINI_TILEY, DX + x,DY + y); - } -} - -#endif - -void DrawAllGameValues(int emeralds, int dynamite, int score, int time, - int key_bits) -{ - int key[MAX_NUM_KEYS]; - int i; - - /* prevent EM engine from updating time/score values parallel to GameWon() */ - if (level.game_engine_type == GAME_ENGINE_TYPE_EM && - local_player->LevelSolved) - return; - - for (i = 0; i < MAX_NUM_KEYS; i++) - key[i] = key_bits & (1 << i); - - DrawGameValue_Level(level_nr); - - DrawGameValue_Emeralds(emeralds); - DrawGameValue_Dynamite(dynamite); - DrawGameValue_Score(score); - DrawGameValue_Time(time); - - DrawGameValue_Keys(key); -} - -void UpdateGameDoorValues() -{ - UpdateGameControlValues(); -} - -void DrawGameDoorValues() -{ - DisplayGameControlValues(); -} - -void DrawGameDoorValues_OLD() -{ - int time_value = (level.time == 0 ? TimePlayed : TimeLeft); - int dynamite_value = 0; - int score_value = (local_player->LevelSolved ? local_player->score_final : - local_player->score); - int gems_value = local_player->gems_still_needed; - int key_bits = 0; - int i, j; - - if (level.game_engine_type == GAME_ENGINE_TYPE_EM) - { - DrawGameDoorValues_EM(); - - return; - } - - if (game.centered_player_nr == -1) - { - for (i = 0; i < MAX_PLAYERS; i++) - { - for (j = 0; j < MAX_NUM_KEYS; j++) - if (stored_player[i].key[j]) - key_bits |= (1 << j); - - dynamite_value += stored_player[i].inventory_size; - } - } - else - { - int player_nr = game.centered_player_nr; - - for (i = 0; i < MAX_NUM_KEYS; i++) - if (stored_player[player_nr].key[i]) - key_bits |= (1 << i); - - dynamite_value = stored_player[player_nr].inventory_size; - } - - DrawAllGameValues(gems_value, dynamite_value, score_value, time_value, - key_bits); -} - - -/* - ============================================================================= - InitGameEngine() - ----------------------------------------------------------------------------- - initialize game engine due to level / tape version number - ============================================================================= -*/ - -static void InitGameEngine() -{ - int i, j, k, l, x, y; - - /* set game engine from tape file when re-playing, else from level file */ - game.engine_version = (tape.playing ? tape.engine_version : - level.game_version); - - /* ---------------------------------------------------------------------- */ - /* set flags for bugs and changes according to active game engine version */ - /* ---------------------------------------------------------------------- */ - - /* - Summary of bugfix/change: - Fixed handling for custom elements that change when pushed by the player. - - Fixed/changed in version: - 3.1.0 - - Description: - Before 3.1.0, custom elements that "change when pushing" changed directly - after the player started pushing them (until then handled in "DigField()"). - Since 3.1.0, these custom elements are not changed until the "pushing" - move of the element is finished (now handled in "ContinueMoving()"). - - Affected levels/tapes: - The first condition is generally needed for all levels/tapes before version - 3.1.0, which might use the old behaviour before it was changed; known tapes - that are affected are some tapes from the level set "Walpurgis Gardens" by - Jamie Cullen. - The second condition is an exception from the above case and is needed for - the special case of tapes recorded with game (not engine!) version 3.1.0 or - above (including some development versions of 3.1.0), but before it was - known that this change would break tapes like the above and was fixed in - 3.1.1, so that the changed behaviour was active although the engine version - while recording maybe was before 3.1.0. There is at least one tape that is - affected by this exception, which is the tape for the one-level set "Bug - Machine" by Juergen Bonhagen. - */ - - game.use_change_when_pushing_bug = - (game.engine_version < VERSION_IDENT(3,1,0,0) && - !(tape.playing && - tape.game_version >= VERSION_IDENT(3,1,0,0) && - tape.game_version < VERSION_IDENT(3,1,1,0))); - - /* - Summary of bugfix/change: - Fixed handling for blocking the field the player leaves when moving. - - Fixed/changed in version: - 3.1.1 - - Description: - Before 3.1.1, when "block last field when moving" was enabled, the field - the player is leaving when moving was blocked for the time of the move, - and was directly unblocked afterwards. This resulted in the last field - being blocked for exactly one less than the number of frames of one player - move. Additionally, even when blocking was disabled, the last field was - blocked for exactly one frame. - Since 3.1.1, due to changes in player movement handling, the last field - is not blocked at all when blocking is disabled. When blocking is enabled, - the last field is blocked for exactly the number of frames of one player - move. Additionally, if the player is Murphy, the hero of Supaplex, the - last field is blocked for exactly one more than the number of frames of - one player move. - - Affected levels/tapes: - (!!! yet to be determined -- probably many !!!) - */ - - game.use_block_last_field_bug = - (game.engine_version < VERSION_IDENT(3,1,1,0)); - - /* - Summary of bugfix/change: - Changed behaviour of CE changes with multiple changes per single frame. - - Fixed/changed in version: - 3.2.0-6 - - Description: - Before 3.2.0-6, only one single CE change was allowed in each engine frame. - This resulted in race conditions where CEs seem to behave strange in some - situations (where triggered CE changes were just skipped because there was - already a CE change on that tile in the playfield in that engine frame). - Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME. - (The number of changes per frame must be limited in any case, because else - it is easily possible to define CE changes that would result in an infinite - loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value - should be set large enough so that it would only be reached in cases where - the corresponding CE change conditions run into a loop. Therefore, it seems - to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the - maximal number of change pages for custom elements.) - - Affected levels/tapes: - Probably many. - */ - -#if USE_ONLY_ONE_CHANGE_PER_FRAME - game.max_num_changes_per_frame = 1; -#else - game.max_num_changes_per_frame = - (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32); -#endif - - /* ---------------------------------------------------------------------- */ - - /* default scan direction: scan playfield from top/left to bottom/right */ - InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL); - - /* dynamically adjust element properties according to game engine version */ - InitElementPropertiesEngine(game.engine_version); - -#if 0 - printf("level %d: level version == %06d\n", level_nr, level.game_version); - printf(" tape version == %06d [%s] [file: %06d]\n", - tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"), - tape.file_version); - printf(" => game.engine_version == %06d\n", game.engine_version); -#endif - - /* ---------- initialize player's initial move delay --------------------- */ - - /* dynamically adjust player properties according to level information */ - for (i = 0; i < MAX_PLAYERS; i++) - game.initial_move_delay_value[i] = - get_move_delay_from_stepsize(level.initial_player_stepsize[i]); - - /* dynamically adjust player properties according to game engine version */ - for (i = 0; i < MAX_PLAYERS; i++) - game.initial_move_delay[i] = - (game.engine_version <= VERSION_IDENT(2,0,1,0) ? - game.initial_move_delay_value[i] : 0); + /* dynamically adjust player properties according to game engine version */ + for (i = 0; i < MAX_PLAYERS; i++) + game.initial_move_delay[i] = + (game.engine_version <= VERSION_IDENT(2,0,1,0) ? + game.initial_move_delay_value[i] : 0); /* ---------- initialize player's initial push delay --------------------- */ @@ -3458,7 +2820,6 @@ static void InitGameEngine() } } -#if 1 /* ---------- initialize reference elements in change conditions --------- */ for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++) @@ -3477,7 +2838,6 @@ static void InitGameEngine() ei->change_page[j].trigger_element = trigger_element; } } -#endif /* ---------- initialize run-time trigger player and element ------------- */ @@ -3676,6 +3036,20 @@ 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.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 : + 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) @@ -3712,29 +3086,46 @@ int get_num_special_action(int element, int action_first, int action_last) 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 */ boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */ -#if 0 - boolean do_fading = (game_status == GAME_MODE_MAIN); -#endif -#if 1 int initial_move_dir = MV_DOWN; -#else - int initial_move_dir = MV_NONE; -#endif int i, j, x, y; - game_status = GAME_MODE_PLAYING; + // 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(); + + if (CheckIfGlobalBorderHasChanged()) + fade_mask = REDRAW_ALL; + + FadeSoundsAndMusic(); + + ExpireSoundLoops(TRUE); + + FadeOut(fade_mask); -#if 1 /* needed if different viewport properties defined for playing */ ChangeViewportPropertiesIfNeeded(); -#endif -#if 1 + ClearField(); + + OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW); + DrawCompleteVideoDisplay(); -#endif InitGameEngine(); InitGameControlValues(); @@ -3822,6 +3213,11 @@ void InitGame() 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->frame_counter_bored = -1; player->frame_counter_sleeping = -1; @@ -3923,6 +3319,8 @@ void InitGame() AllPlayersGone = FALSE; + game.no_time_limit = (level.time == 0); + game.yamyam_content_nr = 0; game.robot_wheel_active = FALSE; game.magic_wall_active = FALSE; @@ -3932,11 +3330,6 @@ void InitGame() game.switchgate_pos = 0; game.wind_direction = level.wind_direction_initial; -#if !USE_PLAYER_GRAVITY - game.gravity = FALSE; - game.explosions_delayed = TRUE; -#endif - game.lenses_time_left = 0; game.magnify_time_left = 0; @@ -3966,15 +3359,20 @@ void InitGame() for (i = 0; i < MAX_NUM_AMOEBA; i++) AmoebaCnt[i] = AmoebaCnt2[i] = 0; +#if DEBUG_INIT_PLAYER + if (options.debug) + { + printf("Player status at level initialization:\n"); + } +#endif + SCAN_PLAYFIELD(x, y) { Feld[x][y] = level.field[x][y]; MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0; ChangeDelay[x][y] = 0; ChangePage[x][y] = -1; -#if USE_NEW_CUSTOM_VALUE CustomValue[x][y] = 0; /* initialized in InitField() */ -#endif Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0; AmoebaNr[x][y] = 0; WasJustMoving[x][y] = 0; @@ -4035,7 +3433,6 @@ void InitGame() emulate_sb ? EMU_SOKOBAN : emulate_sp ? EMU_SUPAPLEX : EMU_NONE); -#if USE_NEW_ALL_SLIPPERY /* initialize type of slippery elements */ for (i = 0; i < MAX_NUM_ELEMENTS; i++) { @@ -4053,7 +3450,6 @@ void InitGame() element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT; } } -#endif /* initialize explosion and ignition delay */ for (i = 0; i < MAX_NUM_ELEMENTS; i++) @@ -4073,14 +3469,6 @@ void InitGame() if (i == EL_BLACK_ORB) element_info[i].ignition_delay = 1; } - -#if 0 - if (element_info[i].explosion_delay < 1) /* !!! check again !!! */ - element_info[i].explosion_delay = 1; - - if (element_info[i].ignition_delay < 1) /* !!! check again !!! */ - element_info[i].ignition_delay = 1; -#endif } /* correct non-moving belts to start moving left */ @@ -4101,14 +3489,10 @@ void InitGame() if (tape.playing) { - /* try to guess locally connected team mode players (needed for correct - assignment of player figures from level to locally playing players) */ - for (i = 0; i < MAX_PLAYERS; i++) - if (tape.player_participates[i]) - stored_player[i].connected = TRUE; + stored_player[i].connected = tape.player_participates[i]; } - else if (setup.team_mode && !options.network) + else if (game.team_mode && !options.network) { /* try to guess locally connected team mode players (needed for correct assignment of player figures from level to locally playing players) */ @@ -4119,14 +3503,32 @@ void InitGame() stored_player[i].connected = TRUE; } -#if 0 - for (i = 0; i < MAX_PLAYERS; i++) - printf("::: player %d: %s\n", i, - (stored_player[i].connected ? "connected" : "not connected")); +#if DEBUG_INIT_PLAYER + if (options.debug) + { + printf("Player status after level initialization:\n"); - for (i = 0; i < MAX_PLAYERS; i++) - printf("::: player %d: %s\n", i, - (stored_player[i].present ? "present" : "not present")); + for (i = 0; i < MAX_PLAYERS; i++) + { + struct PlayerInfo *player = &stored_player[i]; + + printf("- player %d: present == %d, connected == %d, active == %d", + i + 1, + player->present, + player->connected, + player->active); + + if (local_player == player) + printf(" (local player)"); + + printf("\n"); + } + } +#endif + +#if DEBUG_INIT_PLAYER + if (options.debug) + printf("Reassigning players ...\n"); #endif /* check if any connected player was not found in playfield */ @@ -4138,33 +3540,35 @@ void InitGame() { struct PlayerInfo *field_player = NULL; -#if 0 - printf("::: looking for field player for player %d ...\n", i); +#if DEBUG_INIT_PLAYER + if (options.debug) + printf("- looking for field player for player %d ...\n", i + 1); #endif /* assign first free player found that is present in the playfield */ /* first try: look for unmapped playfield player that is not connected */ - if (field_player == NULL) - for (j = 0; j < MAX_PLAYERS; j++) - if (stored_player[j].present && - !stored_player[j].mapped && - !stored_player[j].connected) - field_player = &stored_player[j]; + for (j = 0; j < MAX_PLAYERS; j++) + if (field_player == NULL && + stored_player[j].present && + !stored_player[j].mapped && + !stored_player[j].connected) + field_player = &stored_player[j]; /* second try: look for *any* unmapped playfield player */ - if (field_player == NULL) - for (j = 0; j < MAX_PLAYERS; j++) - if (stored_player[j].present && - !stored_player[j].mapped) - field_player = &stored_player[j]; + for (j = 0; j < MAX_PLAYERS; j++) + if (field_player == NULL && + stored_player[j].present && + !stored_player[j].mapped) + field_player = &stored_player[j]; if (field_player != NULL) { int jx = field_player->jx, jy = field_player->jy; -#if 0 - printf("::: found player figure %d\n", field_player->index_nr); +#if DEBUG_INIT_PLAYER + if (options.debug) + printf("- found player %d\n", field_player->index_nr + 1); #endif player->present = FALSE; @@ -4193,9 +3597,10 @@ void InitGame() field_player->mapped = TRUE; -#if 0 - printf("::: map_player_action[%d] == %d\n", - field_player->index_nr, i); +#if DEBUG_INIT_PLAYER + if (options.debug) + printf("- map_player_action[%d] == %d\n", + field_player->index_nr + 1, i + 1); #endif } } @@ -4204,6 +3609,29 @@ void InitGame() player->mapped = TRUE; } +#if DEBUG_INIT_PLAYER + if (options.debug) + { + printf("Player status after player assignment (first stage):\n"); + + for (i = 0; i < MAX_PLAYERS; i++) + { + struct PlayerInfo *player = &stored_player[i]; + + printf("- player %d: present == %d, connected == %d, active == %d", + i + 1, + player->present, + player->connected, + player->active); + + if (local_player == player) + printf(" (local player)"); + + printf("\n"); + } + } +#endif + #else /* check if any connected player was not found in playfield */ @@ -4254,20 +3682,31 @@ void InitGame() /* when playing a tape, eliminate all players who do not participate */ #if USE_NEW_PLAYER_ASSIGNMENTS - for (i = 0; i < MAX_PLAYERS; i++) + + if (!game.team_mode) { - if (stored_player[i].active && - !tape.player_participates[map_player_action[i]]) + for (i = 0; i < MAX_PLAYERS; i++) { - struct PlayerInfo *player = &stored_player[i]; - int jx = player->jx, jy = player->jy; + if (stored_player[i].active && + !tape.player_participates[map_player_action[i]]) + { + struct PlayerInfo *player = &stored_player[i]; + int jx = player->jx, jy = player->jy; - player->active = FALSE; - StorePlayer[jx][jy] = 0; - Feld[jx][jy] = EL_EMPTY; +#if DEBUG_INIT_PLAYER + if (options.debug) + printf("Removing player %d at (%d, %d)\n", i + 1, jx, jy); +#endif + + player->active = FALSE; + StorePlayer[jx][jy] = 0; + Feld[jx][jy] = EL_EMPTY; + } } } + #else + for (i = 0; i < MAX_PLAYERS; i++) { if (stored_player[i].active && @@ -4283,7 +3722,7 @@ void InitGame() } #endif } - else if (!options.network && !setup.team_mode) /* && !tape.playing */ + else if (!options.network && !game.team_mode) /* && !tape.playing */ { /* when in single player mode, eliminate all but the first active player */ @@ -4323,21 +3762,28 @@ void InitGame() #endif } +#if DEBUG_INIT_PLAYER if (options.debug) { + printf("Player status after player assignment (final stage):\n"); + for (i = 0; i < MAX_PLAYERS; i++) { struct PlayerInfo *player = &stored_player[i]; - printf("Player %d: present == %d, connected == %d, active == %d.\n", - i+1, + printf("- player %d: present == %d, connected == %d, active == %d", + i + 1, player->present, player->connected, player->active); + if (local_player == player) - printf("Player %d is local player.\n", i+1); + printf(" (local player)"); + + printf("\n"); } } +#endif if (BorderElement == EL_EMPTY) { @@ -4354,12 +3800,16 @@ void InitGame() SBY_Lower = lev_fieldy - SCR_FIELDY + 1; } - if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX) + if (full_lev_fieldx <= SCR_FIELDX) SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2; - - if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY) + if (full_lev_fieldy <= SCR_FIELDY) SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2; + if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX) + SBX_Left--; + if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY) + SBY_Upper--; + /* if local player not found, look for custom element that might create the player (make some assumptions about the right custom element) */ if (!local_player->present) @@ -4469,126 +3919,73 @@ void InitGame() local_player->jy - MIDPOSY); } -#if 0 - /* do not use PLAYING mask for fading out from main screen */ - game_status = GAME_MODE_MAIN; -#endif - - StopAnimation(); - - if (!game.restart_level) - CloseDoor(DOOR_CLOSE_1); - -#if 1 - if (level_editor_test_game) - FadeSkipNextFadeIn(); - else - FadeSetEnterScreen(); -#else - if (level_editor_test_game) - fading = fading_none; - else - fading = menu.destination; -#endif - -#if 1 - FadeOut(REDRAW_FIELD); -#else - if (do_fading) - FadeOut(REDRAW_FIELD); -#endif - -#if 0 - game_status = GAME_MODE_PLAYING; -#endif - /* !!! FIX THIS (START) !!! */ if (level.game_engine_type == GAME_ENGINE_TYPE_EM) { InitGameEngine_EM(); - - /* blit playfield from scroll buffer to normal back buffer for fading in */ - BlitScreenToBitmap_EM(backbuffer); } else if (level.game_engine_type == GAME_ENGINE_TYPE_SP) { InitGameEngine_SP(); - - /* blit playfield from scroll buffer to normal back buffer for fading in */ - BlitScreenToBitmap_SP(backbuffer); } else { - DrawLevel(); + DrawLevel(REDRAW_FIELD); DrawAllPlayers(); /* after drawing the level, correct some elements */ if (game.timegate_time_left == 0) CloseAllOpenTimegates(); - - /* blit playfield from scroll buffer to normal back buffer for fading in */ - if (setup.soft_scrolling) - BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY); - - redraw_mask |= REDRAW_FROM_BACKBUFFER; } + + /* blit playfield from scroll buffer to normal back buffer for fading in */ + BlitScreenToBitmap(backbuffer); /* !!! FIX THIS (END) !!! */ -#if 1 - FadeIn(REDRAW_FIELD); -#else - if (do_fading) - FadeIn(REDRAW_FIELD); + DrawMaskedBorder(fade_mask); + + FadeIn(fade_mask); +#if 1 + // full screen redraw is required at this point in the following cases: + // - special editor door undrawn when game was started from level editor + // - drawing area (playfield) was changed and has to be removed completely + redraw_mask = REDRAW_ALL; BackToFront(); #endif if (!game.restart_level) { /* copy default game door content to main double buffer */ -#if 1 -#if 1 + /* !!! CHECK AGAIN !!! */ SetPanelBackground(); // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL); DrawBackground(DX, DY, DXSIZE, DYSIZE); -#else - struct GraphicInfo *gfx = &graphic_info[IMG_BACKGROUND_PANEL]; - - /* (ClearRectangle() only needed if panel bitmap is smaller than panel) */ - ClearRectangle(drawto, DX, DY, DXSIZE, DYSIZE); - BlitBitmap(gfx->bitmap, drawto, gfx->src_x, gfx->src_y, - MIN(gfx->width, DXSIZE), MIN(gfx->height, DYSIZE), DX, DY); -#endif -#else - BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, - DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY); -#endif } SetPanelBackground(); SetDrawBackgroundMask(REDRAW_DOOR_1); -#if 1 UpdateAndDisplayGameControlValues(); -#else - UpdateGameDoorValues(); - DrawGameDoorValues(); -#endif if (!game.restart_level) { 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(); /* copy actual game door content to door double buffer for OpenDoor() */ - BlitBitmap(drawto, bitmap_db_door, - DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1); + BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0); OpenDoor(DOOR_OPEN_ALL); @@ -4599,31 +3996,65 @@ void InitGame() KeyboardAutoRepeatOffUnlessAutoplay(); +#if DEBUG_INIT_PLAYER if (options.debug) { + printf("Player status (final):\n"); + for (i = 0; i < MAX_PLAYERS; i++) - printf("Player %d %sactive.\n", - i + 1, (stored_player[i].active ? "" : "not ")); + { + struct PlayerInfo *player = &stored_player[i]; + + printf("- player %d: present == %d, connected == %d, active == %d", + i + 1, + player->present, + player->connected, + player->active); + + if (local_player == player) + printf(" (local player)"); + + printf("\n"); + } } +#endif } -#if 1 UnmapAllGadgets(); MapGameButtons(); MapTapeButtons(); -#endif + + if (!game.restart_level && !tape.playing) + { + LevelStats_incPlayed(level_nr); + + SaveLevelSetup_SeriesInfo(); + } game.restart_level = FALSE; + + 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) @@ -4832,7 +4263,8 @@ static void PlayerWins(struct PlayerInfo *player) player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ? level.native_em_level->lev->score : player->score); - player->LevelSolved_CountingTime = (level.time == 0 ? TimePlayed : TimeLeft); + player->LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed : + TimeLeft); player->LevelSolved_CountingScore = player->score_final; } @@ -4857,17 +4289,22 @@ void GameWon() local_player->LevelSolved_SaveTape = tape.recording; local_player->LevelSolved_SaveScore = !tape.playing; + if (!tape.playing) + { + LevelStats_incSolved(level_nr); + + SaveLevelSetup_SeriesInfo(); + } + if (tape.auto_play) /* tape might already be stopped here */ tape.auto_play_level_solved = TRUE; -#if 1 TapeStop(); -#endif game_over_delay_1 = game_over_delay_value_1; game_over_delay_2 = game_over_delay_value_2; - time = time_final = (level.time == 0 ? TimePlayed : TimeLeft); + time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft); score = score_final = local_player->score_final; if (TimeLeft > 0) @@ -4875,7 +4312,7 @@ void GameWon() time_final = 0; score_final += TimeLeft * level.score[SC_TIME_BONUS]; } - else if (level.time == 0 && TimePlayed < 999) + else if (game.no_time_limit && TimePlayed < 999) { time_final = 999; score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS]; @@ -4888,7 +4325,6 @@ void GameWon() time = time_final; score = score_final; -#if 1 local_player->LevelSolved_CountingTime = time; local_player->LevelSolved_CountingScore = score; @@ -4896,10 +4332,6 @@ void GameWon() game_panel_controls[GAME_PANEL_SCORE].value = score; DisplayGameControlValues(); -#else - DrawGameValue_Time(time); - DrawGameValue_Score(score); -#endif } if (level.game_engine_type == GAME_ENGINE_TYPE_RND) @@ -4916,24 +4348,14 @@ void GameWon() { int element = Feld[ExitX][ExitY]; -#if 0 - if (element == EL_EM_EXIT_OPEN || - element == EL_EM_STEEL_EXIT_OPEN) - { - Bang(ExitX, ExitY); - } - else -#endif - { - Feld[ExitX][ExitY] = - (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING : - element == EL_EM_EXIT_OPEN ? EL_EM_EXIT_CLOSING : - element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING: - element == EL_STEEL_EXIT_OPEN ? EL_STEEL_EXIT_CLOSING: - EL_EM_STEEL_EXIT_CLOSING); - - PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING); - } + Feld[ExitX][ExitY] = + (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING : + element == EL_EM_EXIT_OPEN ? EL_EM_EXIT_CLOSING : + element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING: + element == EL_STEEL_EXIT_OPEN ? EL_STEEL_EXIT_CLOSING: + EL_EM_STEEL_EXIT_CLOSING); + + PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING); } /* player disappears */ @@ -4973,7 +4395,6 @@ void GameWon() time += time_count_steps * time_count_dir; score += time_count_steps * level.score[SC_TIME_BONUS]; -#if 1 local_player->LevelSolved_CountingTime = time; local_player->LevelSolved_CountingScore = score; @@ -4981,10 +4402,6 @@ void GameWon() game_panel_controls[GAME_PANEL_SCORE].value = score; DisplayGameControlValues(); -#else - DrawGameValue_Time(time); - DrawGameValue_Score(score); -#endif if (time == time_final) StopSound(SND_GAME_LEVELTIME_BONUS); @@ -5005,9 +4422,7 @@ void GameWon() return; } -#if 1 GameEnd(); -#endif } void GameEnd() @@ -5017,43 +4432,30 @@ 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) { -#if 0 - TapeStop(); -#endif - -#if 1 SaveTapeChecked(tape.level_nr); /* ask to save tape */ -#else - SaveTape(tape.level_nr); /* ask to save tape */ -#endif } + CloseDoor(DOOR_CLOSE_ALL); + if (level_editor_test_game) { - game_status = GAME_MODE_MAIN; + SetGameStatus(GAME_MODE_MAIN); -#if 1 - DrawAndFadeInMainMenu(REDRAW_FIELD); -#else DrawMainMenu(); -#endif return; } if (!local_player->LevelSolved_SaveScore) { -#if 1 - FadeOut(REDRAW_FIELD); -#endif + SetGameStatus(GAME_MODE_MAIN); - game_status = GAME_MODE_MAIN; - - DrawAndFadeInMainMenu(REDRAW_FIELD); + DrawMainMenu(); return; } @@ -5061,6 +4463,7 @@ void GameEnd() if (level_nr == leveldir_current->handicap_level) { leveldir_current->handicap_level++; + SaveLevelSetup_SeriesInfo(); } @@ -5069,7 +4472,7 @@ void GameEnd() if ((hi_pos = NewHiScore()) >= 0) { - game_status = GAME_MODE_SCORES; + SetGameStatus(GAME_MODE_SCORES); DrawHallOfFame(hi_pos); @@ -5081,11 +4484,7 @@ void GameEnd() } else { -#if 1 - FadeOut(REDRAW_FIELD); -#endif - - game_status = GAME_MODE_MAIN; + SetGameStatus(GAME_MODE_MAIN); if (raise_level) { @@ -5093,7 +4492,7 @@ void GameEnd() TapeErase(); } - DrawAndFadeInMainMenu(REDRAW_FIELD); + DrawMainMenu(); } } @@ -5188,11 +4587,6 @@ void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir) { if (player->GfxAction != action || player->GfxDir != dir) { -#if 0 - printf("Player frame reset! (%d => %d, %d => %d)\n", - player->GfxAction, action, player->GfxDir, dir); -#endif - player->GfxAction = action; player->GfxDir = dir; player->Frame = 0; @@ -5200,7 +4594,6 @@ void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir) } } -#if USE_GFX_RESET_GFX_ANIMATION static void ResetGfxFrame(int x, int y, boolean redraw) { int element = Feld[x][y]; @@ -5219,7 +4612,6 @@ static void ResetGfxFrame(int x, int y, boolean redraw) if (redraw && GfxFrame[x][y] != last_gfx_frame) DrawLevelGraphicAnimation(x, y, graphic); } -#endif static void ResetGfxAnimation(int x, int y) { @@ -5227,9 +4619,7 @@ static void ResetGfxAnimation(int x, int y) GfxDir[x][y] = MovDir[x][y]; GfxFrame[x][y] = 0; -#if USE_GFX_RESET_GFX_ANIMATION ResetGfxFrame(x, y, FALSE); -#endif } static void ResetRandomAnimationValue(int x, int y) @@ -5245,51 +4635,24 @@ void InitMovingField(int x, int y, int direction) int newx = x + dx; int newy = y + dy; boolean is_moving_before, is_moving_after; -#if 0 - boolean continues_moving = (WasJustMoving[x][y] && direction == MovDir[x][y]); -#endif /* check if element was/is moving or being moved before/after mode change */ -#if 1 -#if 1 is_moving_before = (WasJustMoving[x][y] != 0); -#else - /* (!!! this does not work -- WasJustMoving is NOT a boolean value !!!) */ - is_moving_before = WasJustMoving[x][y]; -#endif -#else - is_moving_before = (getElementMoveStepsizeExt(x, y, MovDir[x][y]) != 0); -#endif is_moving_after = (getElementMoveStepsizeExt(x, y, direction) != 0); /* reset animation only for moving elements which change direction of moving or which just started or stopped moving (else CEs with property "can move" / "not moving" are reset each frame) */ -#if USE_GFX_RESET_ONLY_WHEN_MOVING -#if 1 if (is_moving_before != is_moving_after || direction != MovDir[x][y]) ResetGfxAnimation(x, y); -#else - if ((is_moving_before || is_moving_after) && !continues_moving) - ResetGfxAnimation(x, y); -#endif -#else - if (!continues_moving) - ResetGfxAnimation(x, y); -#endif MovDir[x][y] = direction; GfxDir[x][y] = direction; -#if USE_GFX_RESET_ONLY_WHEN_MOVING GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING : direction == MV_DOWN && CAN_FALL(element) ? ACTION_FALLING : ACTION_MOVING); -#else - GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ? - ACTION_FALLING : ACTION_MOVING); -#endif /* this is needed for CEs with property "can move" / "not moving" */ @@ -5300,9 +4663,7 @@ void InitMovingField(int x, int y, int direction) MovDir[newx][newy] = MovDir[x][y]; -#if USE_NEW_CUSTOM_VALUE CustomValue[newx][newy] = CustomValue[x][y]; -#endif GfxFrame[newx][newy] = GfxFrame[x][y]; GfxRandom[newx][newy] = GfxRandom[x][y]; @@ -5385,26 +4746,16 @@ static void RemoveField(int x, int y) MovDir[x][y] = 0; MovDelay[x][y] = 0; -#if USE_NEW_CUSTOM_VALUE CustomValue[x][y] = 0; -#endif AmoebaNr[x][y] = 0; ChangeDelay[x][y] = 0; ChangePage[x][y] = -1; Pushed[x][y] = FALSE; -#if 0 - ExplodeField[x][y] = EX_TYPE_NONE; -#endif - GfxElement[x][y] = EL_UNDEFINED; GfxAction[x][y] = ACTION_DEFAULT; GfxDir[x][y] = MV_NONE; -#if 0 - /* !!! this would prevent the removed tile from being redrawn !!! */ - GfxRedraw[x][y] = GFX_REDRAW_NONE; -#endif } void RemoveMovingField(int x, int y) @@ -5561,204 +4912,107 @@ static void setScreenCenteredToAllPlayers(int *sx, int *sy) 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 */ - - int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left : - old_x > SBX_Right + MIDPOSX ? SBX_Right : - old_x - MIDPOSX); + /* case 1: quick relocation inside visible screen (without scrolling) */ - 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); - - 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); - } - } - else - { -#if 1 - if (!level.shifted_relocation || center_screen) - { - /* quick relocation (without scrolling), with centering of screen */ + if (!level.shifted_relocation || center_screen) + { + /* relocation _with_ centering of screen */ - scroll_x = (x < SBX_Left + MIDPOSX ? SBX_Left : + new_scroll_x = (x < SBX_Left + MIDPOSX ? SBX_Left : x > SBX_Right + MIDPOSX ? SBX_Right : x - MIDPOSX); - scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper : + new_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 */ + } + else + { + /* relocation _without_ centering of 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_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 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); + 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 : + new_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 : + new_scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper : offset_y > SBY_Lower + MIDPOSY ? SBY_Lower : offset_y - MIDPOSY); - } -#else - /* quick relocation (without scrolling), inside visible screen area */ - - int offset = game.scroll_delay_value; - - if ((move_dir == MV_LEFT && scroll_x > x - MIDPOSX + offset) || - (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset)) - scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset); - - if ((move_dir == MV_UP && scroll_y > y - MIDPOSY + offset) || - (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset)) - scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset); - - /* don't scroll over playfield boundaries */ - if (scroll_x < SBX_Left || scroll_x > SBX_Right) - scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right); - - /* don't scroll over playfield boundaries */ - if (scroll_y < SBY_Upper || scroll_y > SBY_Lower) - scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower); -#endif - } - - RedrawPlayfield(TRUE, 0,0,0,0); } - else - { -#if 1 - int scroll_xx, scroll_yy; - - if (!level.shifted_relocation || center_screen) - { - /* visible relocation (with scrolling), with centering of screen */ - - scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left : - x > SBX_Right + MIDPOSX ? SBX_Right : - x - MIDPOSX); - - 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 */ - 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); + if (quick_relocation) + { + /* case 2: quick relocation (redraw without visible scrolling) */ - scroll_xx = (offset_x < SBX_Left + MIDPOSX ? SBX_Left : - offset_x > SBX_Right + MIDPOSX ? SBX_Right : - offset_x - MIDPOSX); + scroll_x = new_scroll_x; + scroll_y = new_scroll_y; - scroll_yy = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper : - offset_y > SBY_Lower + MIDPOSY ? SBY_Lower : - offset_y - MIDPOSY); - } + RedrawPlayfield(); -#else + return; + } - /* visible relocation (with scrolling), with centering of screen */ + /* case 3: visible relocation (with scrolling to new position) */ - int scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left : - x > SBX_Right + MIDPOSX ? SBX_Right : - x - MIDPOSX); + ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */ - int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper : - y > SBY_Lower + MIDPOSY ? SBY_Lower : - y - MIDPOSY); -#endif + 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); - FlushDisplay(); - 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) @@ -5808,8 +5062,7 @@ 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 */ @@ -5837,12 +5090,6 @@ void RelocatePlayer(int jx, int jy, int el_player_raw) if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */ { Feld[jx][jy] = element; /* restore previously existing element */ -#if 0 - /* !!! do not initialize already initialized element a second time !!! */ - /* (this causes at least problems with "element creation" CE trigger for - already existing elements, and existing Sokoban fields counted twice) */ - InitField(jx, jy, FALSE); -#endif } /* only visually relocate centered player */ @@ -5859,7 +5106,6 @@ void RelocatePlayer(int jx, int jy, int el_player_raw) CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X, player->index_bit, enter_side); -#if 1 if (player->is_switching) { /* ensure that relocation while still switching an element does not cause @@ -5872,7 +5118,6 @@ void RelocatePlayer(int jx, int jy, int el_player_raw) player->switch_x += jx - old_jx; player->switch_y += jy - old_jy; } -#endif } void Explode(int ex, int ey, int phase, int mode) @@ -5895,23 +5140,6 @@ void Explode(int ex, int ey, int phase, int mode) int center_element = Feld[ex][ey]; int artwork_element, explosion_element; /* set these values later */ -#if 0 - /* --- This is only really needed (and now handled) in "Impact()". --- */ - /* do not explode moving elements that left the explode field in time */ - if (game.engine_version >= VERSION_IDENT(2,2,0,7) && - center_element == EL_EMPTY && - (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER)) - return; -#endif - -#if 0 - /* !!! at this place, the center element may be EL_BLOCKED !!! */ - if (mode == EX_TYPE_NORMAL || - mode == EX_TYPE_CENTER || - mode == EX_TYPE_CROSS) - PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING); -#endif - /* remove things displayed in background while burning dynamite */ if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey])) Back[ex][ey] = 0; @@ -5941,12 +5169,10 @@ void Explode(int ex, int ey, int phase, int mode) } } -#if 1 if (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER || mode == EX_TYPE_CROSS) PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING); -#endif last_phase = element_info[explosion_element].explosion_delay + 1; @@ -6083,31 +5309,9 @@ void Explode(int ex, int ey, int phase, int mode) ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0); -#ifdef DEBUG - - /* activate this even in non-DEBUG version until cause for crash in - getGraphicAnimationFrame() (see below) is found and eliminated */ - -#endif -#if 1 - -#if 1 /* this can happen if the player leaves an explosion just in time */ if (GfxElement[x][y] == EL_UNDEFINED) GfxElement[x][y] = EL_EMPTY; -#else - if (GfxElement[x][y] == EL_UNDEFINED) - { - printf("\n\n"); - printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y); - printf("Explode(): This should never happen!\n"); - printf("\n\n"); - - GfxElement[x][y] = EL_EMPTY; - } -#endif - -#endif border_element = Store2[x][y]; if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y)) @@ -6179,9 +5383,7 @@ void Explode(int ex, int ey, int phase, int mode) ChangeDelay[x][y] = 0; ChangePage[x][y] = -1; -#if USE_NEW_CUSTOM_VALUE CustomValue[x][y] = 0; -#endif InitField_WithBug2(x, y, FALSE); @@ -6281,12 +5483,7 @@ void Bang(int x, int y) { struct PlayerInfo *player = PLAYERINFO(x, y); -#if USE_FIX_CE_ACTION_WITH_PLAYER element = Feld[x][y] = player->initial_element; -#else - element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY : - player->element_nr); -#endif if (level.use_explosion_element[player->index_nr]) { @@ -6324,10 +5521,6 @@ void Bang(int x, int y) break; case EL_DC_LANDMINE: -#if 0 - case EL_EM_EXIT_OPEN: - case EL_EM_STEEL_EXIT_OPEN: -#endif explosion_type = EX_TYPE_CENTER; break; @@ -6551,20 +5744,6 @@ static void ToggleSwitchgateSwitch(int x, int y) { int element = Feld[xx][yy]; -#if !USE_BOTH_SWITCHGATE_SWITCHES - if (element == EL_SWITCHGATE_SWITCH_UP || - element == EL_SWITCHGATE_SWITCH_DOWN) - { - Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos; - TEST_DrawLevelField(xx, yy); - } - else if (element == EL_DC_SWITCHGATE_SWITCH_UP || - element == EL_DC_SWITCHGATE_SWITCH_DOWN) - { - Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP + game.switchgate_pos; - TEST_DrawLevelField(xx, yy); - } -#else if (element == EL_SWITCHGATE_SWITCH_UP) { Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN; @@ -6585,7 +5764,6 @@ static void ToggleSwitchgateSwitch(int x, int y) Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP; TEST_DrawLevelField(xx, yy); } -#endif else if (element == EL_SWITCHGATE_OPEN || element == EL_SWITCHGATE_OPENING) { @@ -6818,12 +5996,8 @@ static void ActivateTimegateSwitch(int x, int y) } -#if 1 Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE : EL_DC_TIMEGATE_SWITCH_ACTIVE); -#else - Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE; -#endif } void Impact(int x, int y) @@ -7049,10 +6223,6 @@ void Impact(int x, int y) } else { -#if 0 - TestIfElementSmashesCustomElement(x, y, MV_DOWN); -#endif - CheckElementChange(x, y + 1, smashed, element, CE_SMASHED); CheckElementChangeBySide(x, y + 1, smashed, element, @@ -7354,7 +6524,6 @@ inline static void TurnRoundExt(int x, int y) } else if (element == EL_SPRING) { -#if USE_NEW_SPRING_BUMPER if (MovDir[x][y] & MV_HORIZONTAL) { if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) && @@ -7370,12 +6539,6 @@ inline static void TurnRoundExt(int x, int y) SPRING_CAN_ENTER_FIELD(element, x, y + 1)) MovDir[x][y] = MV_NONE; } -#else - if (MovDir[x][y] & MV_HORIZONTAL && - (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) || - SPRING_CAN_ENTER_FIELD(element, x, y + 1))) - MovDir[x][y] = MV_NONE; -#endif MovDelay[x][y] = 0; } @@ -8235,13 +7398,8 @@ void StartMoving(int x, int y) Store[x][y] = EL_ACID; } else if ( -#if USE_FIX_IMPACT_COLLISION (game.engine_version >= VERSION_IDENT(3,1,0,0) && CheckImpact[x][y] && !IS_FREE(x, y + 1)) || -#else - (game.engine_version >= VERSION_IDENT(3,1,0,0) && - CheckCollision[x][y] && !IS_FREE(x, y + 1)) || -#endif (game.engine_version >= VERSION_IDENT(3,0,7,0) && CAN_FALL(element) && WasJustFalling[x][y] && (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) || @@ -8301,7 +7459,6 @@ void StartMoving(int x, int y) boolean can_fall_both = (can_fall_left && can_fall_right); int slippery_type = element_info[Feld[x][y + 1]].slippery_type; -#if USE_NEW_ALL_SLIPPERY if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM) { if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both) @@ -8316,37 +7473,7 @@ void StartMoving(int x, int y) can_fall_any = (can_fall_left || can_fall_right); can_fall_both = FALSE; } -#else - if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1])) - { - if (slippery_type == SLIPPERY_ONLY_LEFT) - can_fall_right = FALSE; - else if (slippery_type == SLIPPERY_ONLY_RIGHT) - can_fall_left = FALSE; - else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both) - can_fall_right = FALSE; - else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both) - can_fall_left = FALSE; - - can_fall_any = (can_fall_left || can_fall_right); - can_fall_both = (can_fall_left && can_fall_right); - } -#endif - -#if USE_NEW_ALL_SLIPPERY -#else -#if USE_NEW_SP_SLIPPERY - /* !!! better use the same properties as for custom elements here !!! */ - else if (game.engine_version >= VERSION_IDENT(3,1,1,0) && - can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1])) - { - can_fall_right = FALSE; /* slip down on left side */ - can_fall_both = FALSE; - } -#endif -#endif -#if USE_NEW_ALL_SLIPPERY if (can_fall_both) { if (element == EL_BD_ROCK || element == EL_BD_DIAMOND) @@ -8356,18 +7483,6 @@ void StartMoving(int x, int y) can_fall_both = FALSE; } -#else - if (can_fall_both) - { - if (game.emulation == EMU_BOULDERDASH || - element == EL_BD_ROCK || element == EL_BD_DIAMOND) - can_fall_right = FALSE; /* slip down on left side */ - else - can_fall_left = !(can_fall_right = RND(2)); - - can_fall_both = FALSE; - } -#endif if (can_fall_any) { @@ -8376,11 +7491,7 @@ void StartMoving(int x, int y) started_moving = TRUE; } } -#if 0 - else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element)) -#else else if (IS_BELT_ACTIVE(Feld[x][y + 1])) -#endif { boolean left_is_free = (x > 0 && IS_FREE(x - 1, y)); boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y)); @@ -8408,26 +7519,11 @@ void StartMoving(int x, int y) } /* not "else if" because of elements that can fall and move (EL_SPRING) */ -#if 0 - if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE) -#else if (CAN_MOVE(element) && !started_moving) -#endif { int move_pattern = element_info[element].move_pattern; int newx, newy; -#if 0 -#if DEBUG - if (MovDir[x][y] == MV_NONE) - { - printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n", - x, y, element, element_info[element].token_name); - printf("StartMoving(): This should never happen!\n"); - } -#endif -#endif - Moving2Blocked(x, y, &newx, &newy); if (IS_PUSHABLE(element) && JustBeingPushed(x, y)) @@ -8520,20 +7616,10 @@ void StartMoving(int x, int y) { int flamed = MovingOrBlocked2Element(xx, yy); - /* !!! */ -#if 0 - if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed)) - Bang(xx, yy); - else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy)) - RemoveMovingField(xx, yy); - else - RemoveField(xx, yy); -#else if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed)) Bang(xx, yy); else RemoveMovingField(xx, yy); -#endif ChangeDelay[xx][yy] = 0; @@ -8729,78 +7815,14 @@ void StartMoving(int x, int y) } else if (!IS_FREE(newx, newy)) { -#if 0 - if (IS_PLAYER(x, y)) - DrawPlayerField(x, y); - else - TEST_DrawLevelField(x, y); -#endif - return; } } else if (IS_CUSTOM_ELEMENT(element) && CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy)) { -#if 1 if (!DigFieldByCE(newx, newy, element)) return; -#else - int new_element = Feld[newx][newy]; - - if (!IS_FREE(newx, newy)) - { - int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING : - IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING : - ACTION_BREAKING); - - /* no element can dig solid indestructible elements */ - if (IS_INDESTRUCTIBLE(new_element) && - !IS_DIGGABLE(new_element) && - !IS_COLLECTIBLE(new_element)) - return; - - if (AmoebaNr[newx][newy] && - (new_element == EL_AMOEBA_FULL || - new_element == EL_BD_AMOEBA || - new_element == EL_AMOEBA_GROWING)) - { - AmoebaCnt[AmoebaNr[newx][newy]]--; - AmoebaCnt2[AmoebaNr[newx][newy]]--; - } - - if (IS_MOVING(newx, newy)) - RemoveMovingField(newx, newy); - else - { - RemoveField(newx, newy); - TEST_DrawLevelField(newx, newy); - } - - /* if digged element was about to explode, prevent the explosion */ - ExplodeField[newx][newy] = EX_TYPE_NONE; - - PlayLevelSoundAction(x, y, action); - } - - Store[newx][newy] = EL_EMPTY; - -#if 1 - /* this makes it possible to leave the removed element again */ - if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element))) - Store[newx][newy] = new_element; -#else - if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element))) - { - int move_leave_element = element_info[element].move_leave_element; - - /* this makes it possible to leave the removed element again */ - Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ? - new_element : move_leave_element); - } -#endif - -#endif if (move_pattern & MV_MAZE_RUNNER_STYLE) { @@ -8848,25 +7870,11 @@ void StartMoving(int x, int y) MovDelay[x][y] = 50; - /* !!! */ -#if 0 - RemoveField(newx, newy); -#endif Feld[newx][newy] = EL_FLAMES; if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY) - { -#if 0 - RemoveField(newx1, newy1); -#endif Feld[newx1][newy1] = EL_FLAMES; - } if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY) - { -#if 0 - RemoveField(newx2, newy2); -#endif Feld[newx2][newy2] = EL_FLAMES; - } return; } @@ -8896,18 +7904,10 @@ void StartMoving(int x, int y) AmoebaCnt[AmoebaNr[newx][newy]]--; } -#if 0 - /* !!! test !!! */ - if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy)) - { - RemoveMovingField(newx, newy); - } -#else if (IS_MOVING(newx, newy)) { RemoveMovingField(newx, newy); } -#endif else { Feld[newx][newy] = EL_EMPTY; @@ -8958,22 +7958,7 @@ void StartMoving(int x, int y) { /* object was running against a wall */ - TurnRound(x, y); - -#if 0 - /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */ - if (move_pattern & MV_ANY_DIRECTION && - move_pattern == MovDir[x][y]) - { - int blocking_element = - (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement); - - CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED, - MovDir[x][y]); - - element = Feld[x][y]; /* element might have changed */ - } -#endif + TurnRound(x, y); if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */ DrawLevelElementAnimation(x, y, element); @@ -9014,17 +7999,6 @@ void ContinueMoving(int x, int y) if (ABS(MovPos[x][y]) < TILEX) { -#if 0 - int ee = Feld[x][y]; - int gg = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]); - int ff = getGraphicAnimationFrame(gg, GfxFrame[x][y]); - - printf("::: %d.%d: moving %d ... [%d, %d, %d] [%d, %d, %d]\n", - x, y, ABS(MovPos[x][y]), - ee, gg, ff, - GfxAction[x][y], GfxDir[x][y], GfxFrame[x][y]); -#endif - TEST_DrawLevelField(x, y); return; /* element is still moving */ @@ -9080,9 +8054,7 @@ void ContinueMoving(int x, int y) Feld[x][y] = EL_MAGIC_WALL_DEAD; element = Feld[newx][newy] = Store[x][y]; -#if USE_NEW_CUSTOM_VALUE InitField(newx, newy, FALSE); -#endif } else if (element == EL_BD_MAGIC_WALL_FILLING) { @@ -9098,9 +8070,7 @@ void ContinueMoving(int x, int y) Feld[x][y] = EL_BD_MAGIC_WALL_DEAD; element = Feld[newx][newy] = Store[x][y]; -#if USE_NEW_CUSTOM_VALUE InitField(newx, newy, FALSE); -#endif } else if (element == EL_DC_MAGIC_WALL_FILLING) { @@ -9116,9 +8086,7 @@ void ContinueMoving(int x, int y) Feld[x][y] = EL_DC_MAGIC_WALL_DEAD; element = Feld[newx][newy] = Store[x][y]; -#if USE_NEW_CUSTOM_VALUE InitField(newx, newy, FALSE); -#endif } else if (element == EL_AMOEBA_DROPPING) { @@ -9152,18 +8120,14 @@ void ContinueMoving(int x, int y) ChangeEvent[newx][newy] = ChangeEvent[x][y]; } -#if USE_NEW_CUSTOM_VALUE CustomValue[newx][newy] = CustomValue[x][y]; -#endif ChangeDelay[x][y] = 0; ChangePage[x][y] = -1; ChangeCount[x][y] = 0; ChangeEvent[x][y] = -1; -#if USE_NEW_CUSTOM_VALUE CustomValue[x][y] = 0; -#endif /* copy animation control values to new field */ GfxFrame[newx][newy] = GfxFrame[x][y]; @@ -9174,34 +8138,15 @@ void ContinueMoving(int x, int y) Pushed[x][y] = Pushed[newx][newy] = FALSE; /* some elements can leave other elements behind after moving */ -#if 1 if (ei->move_leave_element != EL_EMPTY && (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) && (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element))) -#else - if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY && - (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) && - (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element))) -#endif { int move_leave_element = ei->move_leave_element; -#if 1 -#if 1 /* this makes it possible to leave the removed element again */ if (ei->move_leave_element == EL_TRIGGER_ELEMENT) move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored); -#else - /* this makes it possible to leave the removed element again */ - if (ei->move_leave_element == EL_TRIGGER_ELEMENT) - move_leave_element = stored; -#endif -#else - /* this makes it possible to leave the removed element again */ - if (ei->move_leave_type == LEAVE_TYPE_LIMITED && - ei->move_leave_element == EL_TRIGGER_ELEMENT) - move_leave_element = stored; -#endif Feld[x][y] = move_leave_element; @@ -9256,10 +8201,8 @@ void ContinueMoving(int x, int y) if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again) CheckCollision[newx][newy] = CHECK_DELAY_COLLISION; -#if USE_FIX_IMPACT_COLLISION if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again) CheckImpact[newx][newy] = CHECK_DELAY_IMPACT; -#endif } if (DONT_TOUCH(element)) /* object may be nasty to player or others */ @@ -9302,29 +8245,6 @@ void ContinueMoving(int x, int y) CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction); TestIfElementTouchesCustomElement(x, y); /* empty or new element */ - -#if 0 - if (ChangePage[newx][newy] != -1) /* delayed change */ - { - int page = ChangePage[newx][newy]; - struct ElementChangeInfo *change = &ei->change_page[page]; - - ChangePage[newx][newy] = -1; - - if (change->can_change) - { - if (ChangeElement(newx, newy, element, page)) - { - if (change->post_change_function) - change->post_change_function(newx, newy); - } - } - - if (change->has_action) - ExecuteCustomElementAction(newx, newy, element, page); - } -#endif - TestIfElementHitsCustomElement(newx, newy, direction); TestIfPlayerTouchesCustomElement(newx, newy); TestIfElementTouchesCustomElement(newx, newy); @@ -9508,8 +8428,8 @@ void AmoebeUmwandelnBD(int ax, int ay, int new_element) void AmoebeWaechst(int x, int y) { - static unsigned long sound_delay = 0; - static unsigned long sound_delay_value = 0; + static unsigned int sound_delay = 0; + static unsigned int sound_delay_value = 0; if (!MovDelay[x][y]) /* start new growing cycle */ { @@ -9544,8 +8464,8 @@ void AmoebeWaechst(int x, int y) void AmoebaDisappearing(int x, int y) { - static unsigned long sound_delay = 0; - static unsigned long sound_delay_value = 0; + static unsigned int sound_delay = 0; + static unsigned int sound_delay_value = 0; if (!MovDelay[x][y]) /* start new shrinking cycle */ { @@ -9845,11 +8765,7 @@ static void RunTimegateWheel(int x, int y) static void InitMagicBallDelay(int x, int y) { -#if 1 ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1; -#else - ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1; -#endif } static void ActivateMagicBall(int bx, int by) @@ -10462,11 +9378,6 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page) EL_EMPTY); int action_arg_element = GetElementFromGroupElement(action_arg_element_raw); -#if 0 - if (action_arg_element_raw == EL_GROUP_START) - printf("::: %d,%d: %d ('%s')\n", x, y, element, EL_NAME(element)); -#endif - int action_arg_direction = (action_arg >= CA_ARG_DIRECTION_LEFT && action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION : @@ -10494,11 +9405,7 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page) action_type == CA_SET_LEVEL_GEMS ? level.gems_needed : action_type == CA_SET_LEVEL_TIME ? level.time : action_type == CA_SET_LEVEL_SCORE ? 0 : -#if USE_NEW_CUSTOM_VALUE action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) : -#else - action_type == CA_SET_CE_VALUE ? ei->custom_value_initial : -#endif action_type == CA_SET_CE_SCORE ? 0 : 0); @@ -10510,11 +9417,7 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page) action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min : action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max : action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset : -#if USE_NEW_CUSTOM_VALUE action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] : -#else - action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial : -#endif action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score : action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) : action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value : @@ -10544,17 +9447,9 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page) action_mode, action_arg_number, action_arg_number_min, action_arg_number_max); -#if 1 int trigger_player_bits = (change->actual_trigger_player_bits != CH_PLAYER_NONE ? change->actual_trigger_player_bits : change->trigger_player); -#else - int trigger_player_bits = - (change->actual_trigger_player >= EL_PLAYER_1 && - change->actual_trigger_player <= EL_PLAYER_4 ? - (1 << (change->actual_trigger_player - EL_PLAYER_1)) : - PLAYER_BITS_ANY); -#endif int action_arg_player_bits = (action_arg >= CA_ARG_PLAYER_1 && @@ -10598,13 +9493,9 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page) { TimeLeft = action_arg_number_new; -#if 1 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft; DisplayGameControlValues(); -#else - DrawGameValue_Time(TimeLeft); -#endif if (!TimeLeft && setup.time_limit) for (i = 0; i < MAX_PLAYERS; i++) @@ -10618,13 +9509,9 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page) { local_player->score = action_arg_number_new; -#if 1 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score; DisplayGameControlValues(); -#else - DrawGameValue_Score(local_player->score); -#endif break; } @@ -10633,27 +9520,15 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page) { local_player->gems_still_needed = action_arg_number_new; -#if 1 - game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed; + game.snapshot.collected_item = TRUE; - DisplayGameControlValues(); -#else - DrawGameValue_Emeralds(local_player->gems_still_needed); -#endif + game_panel_controls[GAME_PANEL_GEMS].value = + local_player->gems_still_needed; - break; - } + DisplayGameControlValues(); -#if !USE_PLAYER_GRAVITY - case CA_SET_LEVEL_GRAVITY: - { - game.gravity = (action_arg == CA_ARG_GRAVITY_OFF ? FALSE : - action_arg == CA_ARG_GRAVITY_ON ? TRUE : - action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity : - game.gravity); break; } -#endif case CA_SET_LEVEL_WIND: { @@ -10664,27 +9539,8 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page) case CA_SET_LEVEL_RANDOM_SEED: { -#if 1 /* ensure that setting a new random seed while playing is predictable */ InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1); -#else - InitRND(action_arg_number_new); -#endif - -#if 0 - printf("::: %d -> %d\n", action_arg_number_new, RND(10)); -#endif - -#if 0 - { - int i; - - printf("::: "); - for (i = 0; i < 9; i++) - printf("%d, ", RND(2)); - printf("\n"); - } -#endif break; } @@ -10743,10 +9599,6 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page) case CA_SET_PLAYER_SPEED: { -#if 0 - printf("::: trigger_player_bits == %d\n", trigger_player_bits); -#endif - for (i = 0; i < MAX_PLAYERS; i++) { if (trigger_player_bits & (1 << i)) @@ -10810,7 +9662,6 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page) break; } -#if USE_PLAYER_GRAVITY case CA_SET_PLAYER_GRAVITY: { for (i = 0; i < MAX_PLAYERS; i++) @@ -10827,7 +9678,6 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page) break; } -#endif case CA_SET_PLAYER_ARTWORK: { @@ -10842,10 +9692,8 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page) (level.use_artwork_element[i] ? level.artwork_element[i] : stored_player[i].element_nr); -#if USE_GFX_RESET_PLAYER_ARTWORK if (stored_player[i].artwork_element != artwork_element) stored_player[i].Frame = 0; -#endif stored_player[i].artwork_element = artwork_element; @@ -10966,7 +9814,6 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page) case CA_SET_CE_VALUE: { -#if USE_NEW_CUSTOM_VALUE int last_ce_value = CustomValue[x][y]; CustomValue[x][y] = action_arg_number_new; @@ -10982,14 +9829,12 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page) CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X); } } -#endif break; } case CA_SET_CE_SCORE: { -#if USE_NEW_CUSTOM_VALUE int last_ce_score = ei->collect_score; ei->collect_score = action_arg_number_new; @@ -11025,7 +9870,6 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page) } } } -#endif break; } @@ -11081,32 +9925,13 @@ static void CreateFieldExt(int x, int y, int element, boolean is_change) int old_element = Feld[x][y]; int new_element = GetElementFromGroupElement(element); int previous_move_direction = MovDir[x][y]; -#if USE_NEW_CUSTOM_VALUE int last_ce_value = CustomValue[x][y]; -#endif boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y); boolean new_element_is_player = ELEM_IS_PLAYER(new_element); boolean add_player_onto_element = (new_element_is_player && -#if USE_CODE_THAT_BREAKS_SNAKE_BITE - /* this breaks SnakeBite when a snake is - halfway through a door that closes */ - /* NOW FIXED AT LEVEL INIT IN files.c */ new_element != EL_SOKOBAN_FIELD_PLAYER && -#endif IS_WALKABLE(old_element)); -#if 0 - /* check if element under the player changes from accessible to unaccessible - (needed for special case of dropping element which then changes) */ - if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) && - IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element)) - { - Bang(x, y); - - return; - } -#endif - if (!add_player_onto_element) { if (IS_MOVING(x, y) || IS_BLOCKED(x, y)) @@ -11116,27 +9941,18 @@ static void CreateFieldExt(int x, int y, int element, boolean is_change) Feld[x][y] = new_element; -#if !USE_GFX_RESET_GFX_ANIMATION - ResetGfxAnimation(x, y); - ResetRandomAnimationValue(x, y); -#endif - if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS) MovDir[x][y] = previous_move_direction; -#if USE_NEW_CUSTOM_VALUE if (element_info[new_element].use_last_ce_value) CustomValue[x][y] = last_ce_value; -#endif InitField_WithBug1(x, y, FALSE); new_element = Feld[x][y]; /* element may have changed */ -#if USE_GFX_RESET_GFX_ANIMATION ResetGfxAnimation(x, y); ResetRandomAnimationValue(x, y); -#endif TEST_DrawLevelField(x, y); @@ -11144,11 +9960,9 @@ static void CreateFieldExt(int x, int y, int element, boolean is_change) TEST_DrawLevelFieldCrumbledNeighbours(x, y); } -#if 1 /* check if element under the player changes from accessible to unaccessible (needed for special case of dropping element which then changes) */ /* (must be checked after creating new element for walkable group elements) */ -#if USE_FIX_KILLED_BY_NON_WALKABLE if (IS_PLAYER(x, y) && !player_explosion_protected && IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element)) { @@ -11156,16 +9970,6 @@ static void CreateFieldExt(int x, int y, int element, boolean is_change) return; } -#else - if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) && - IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element)) - { - Bang(x, y); - - return; - } -#endif -#endif /* "ChangeCount" not set yet to allow "entered by player" change one time */ if (new_element_is_player) @@ -11188,7 +9992,6 @@ static void CreateElementFromChange(int x, int y, int element) { element = GET_VALID_RUNTIME_ELEMENT(element); -#if USE_STOP_CHANGED_ELEMENTS if (game.engine_version >= VERSION_IDENT(3,2,0,7)) { int old_element = Feld[x][y]; @@ -11199,7 +10002,6 @@ static void CreateElementFromChange(int x, int y, int element) (!CAN_MOVE(old_element) || !CAN_MOVE(element))) Stop[x][y] = TRUE; } -#endif CreateFieldExt(x, y, element, TRUE); } @@ -11375,8 +10177,6 @@ static boolean ChangeElement(int x, int y, int element, int page) return TRUE; } -#if USE_NEW_DELAYED_ACTION - static void HandleElementChange(int x, int y, int page) { int element = MovingOrBlocked2Element(x, y); @@ -11399,11 +10199,6 @@ static void HandleElementChange(int x, int y, int page) /* this can happen with classic bombs on walkable, changing elements */ if (!CAN_CHANGE_OR_HAS_ACTION(element)) { -#if 0 - if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */ - ChangeDelay[x][y] = 0; -#endif - return; } @@ -11413,19 +10208,56 @@ static void HandleElementChange(int x, int y, int page) if (change->can_change) { -#if 1 /* !!! not clear why graphic animation should be reset at all here !!! */ /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */ -#if USE_GFX_RESET_WHEN_NOT_MOVING + /* !!! 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)) -#endif + if (game.graphics_engine_version < 4 && + !IS_MOVING(x, y)) { ResetGfxAnimation(x, y); ResetRandomAnimationValue(x, y); } -#endif if (change->pre_change_function) change->pre_change_function(x, y); @@ -11465,14 +10297,12 @@ static void HandleElementChange(int x, int y, int page) return; } -#if 1 /* special case: set new level random seed before changing element */ if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED) handle_action_before_change = TRUE; if (change->has_action && handle_action_before_change) ExecuteCustomElementAction(x, y, element, page); -#endif if (change->can_change) { @@ -11488,87 +10318,6 @@ static void HandleElementChange(int x, int y, int page) } } -#else - -static void HandleElementChange(int x, int y, int page) -{ - int element = MovingOrBlocked2Element(x, y); - struct ElementInfo *ei = &element_info[element]; - struct ElementChangeInfo *change = &ei->change_page[page]; - -#ifdef DEBUG - if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y])) - { - printf("\n\n"); - printf("HandleElementChange(): %d,%d: element = %d ('%s')\n", - x, y, element, element_info[element].token_name); - printf("HandleElementChange(): This should never happen!\n"); - printf("\n\n"); - } -#endif - - /* this can happen with classic bombs on walkable, changing elements */ - if (!CAN_CHANGE(element)) - { -#if 0 - if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */ - ChangeDelay[x][y] = 0; -#endif - - return; - } - - if (ChangeDelay[x][y] == 0) /* initialize element change */ - { - ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1; - - ResetGfxAnimation(x, y); - ResetRandomAnimationValue(x, y); - - if (change->pre_change_function) - change->pre_change_function(x, y); - } - - ChangeDelay[x][y]--; - - if (ChangeDelay[x][y] != 0) /* continue element change */ - { - int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]); - - if (IS_ANIMATED(graphic)) - DrawLevelGraphicAnimationIfNeeded(x, y, graphic); - - if (change->change_function) - change->change_function(x, y); - } - else /* finish element change */ - { - if (ChangePage[x][y] != -1) /* remember page from delayed change */ - { - page = ChangePage[x][y]; - ChangePage[x][y] = -1; - - change = &ei->change_page[page]; - } - - if (IS_MOVING(x, y)) /* never change a running system ;-) */ - { - ChangeDelay[x][y] = 1; /* try change after next move step */ - ChangePage[x][y] = page; /* remember page to use for change */ - - return; - } - - if (ChangeElement(x, y, element, page)) - { - if (change->post_change_function) - change->post_change_function(x, y); - } - } -} - -#endif - static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y, int trigger_element, int trigger_event, @@ -11583,12 +10332,6 @@ static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y, if (!(trigger_events[trigger_element][trigger_event])) return FALSE; -#if 0 - printf("::: CheckTriggeredElementChangeExt %d ... [%d, %d, %d, '%s']\n", - trigger_event, recursion_loop_depth, recursion_loop_detected, - recursion_loop_element, EL_NAME(recursion_loop_element)); -#endif - RECURSION_LOOP_DETECTION_START(trigger_element, FALSE); for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++) @@ -11619,11 +10362,6 @@ static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y, change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y]; change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element); -#if 0 - printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d\n", - element, EL_NAME(element), p); -#endif - if ((change->can_change && !change_done) || change->has_action) { int x, y; @@ -11634,7 +10372,6 @@ static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y, { if (change->can_change && !change_done) { -#if USE_FIX_NO_ACTION_AFTER_CHANGE /* if element already changed in this frame, not only prevent another element change (checked in ChangeElement()), but also prevent additional element actions for this element */ @@ -11642,22 +10379,14 @@ static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y, if (ChangeCount[x][y] >= game.max_num_changes_per_frame && !level.use_action_after_change_bug) continue; -#endif - -#if 0 - printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- CHANGE\n", - element, EL_NAME(element), p); -#endif ChangeDelay[x][y] = 1; ChangeEvent[x][y] = trigger_event; HandleElementChange(x, y, p); } -#if USE_NEW_DELAYED_ACTION else if (change->has_action) { -#if USE_FIX_NO_ACTION_AFTER_CHANGE /* if element already changed in this frame, not only prevent another element change (checked in ChangeElement()), but also prevent additional element actions for this element */ @@ -11665,24 +10394,10 @@ static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y, if (ChangeCount[x][y] >= game.max_num_changes_per_frame && !level.use_action_after_change_bug) continue; -#endif - - -#if 0 - printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- ACTION\n", - element, EL_NAME(element), p); -#endif ExecuteCustomElementAction(x, y, element, p); PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p); } -#else - if (change->has_action) - { - ExecuteCustomElementAction(x, y, element, p); - PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p); - } -#endif } } @@ -11690,12 +10405,6 @@ static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y, { change_done = TRUE; change_done_any = TRUE; - -#if 0 - printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- DONE\n", - element, EL_NAME(element), p); -#endif - } } } @@ -11727,11 +10436,6 @@ static boolean CheckElementChangeExt(int x, int y, element = Feld[x][y]; } -#if 0 - /* check if element has already changed */ - if (Feld[x][y] != element) - return FALSE; -#else /* check if element has already changed or is about to change after moving */ if ((game.engine_version < VERSION_IDENT(3,2,0,7) && Feld[x][y] != element) || @@ -11740,20 +10444,9 @@ static boolean CheckElementChangeExt(int x, int y, (ChangeCount[x][y] >= game.max_num_changes_per_frame || ChangePage[x][y] != -1))) return FALSE; -#endif - -#if 0 - printf("::: CheckElementChangeExt %d ... [%d, %d, %d, '%s']\n", - trigger_event, recursion_loop_depth, recursion_loop_detected, - recursion_loop_element, EL_NAME(recursion_loop_element)); -#endif RECURSION_LOOP_DETECTION_START(trigger_element, FALSE); -#if 0 - printf("::: X: trigger_player_bits == %d\n", trigger_player); -#endif - for (p = 0; p < element_info[element].num_change_pages; p++) { struct ElementChangeInfo *change = &element_info[element].change_page[p]; @@ -11766,10 +10459,7 @@ static boolean CheckElementChangeExt(int x, int y, (trigger_event == CE_TOUCHING_X || trigger_event == CE_HITTING_X || trigger_event == CE_HIT_BY_X || -#if 1 - /* this one was forgotten until 3.2.3 */ - trigger_event == CE_DIGGING_X); -#endif + trigger_event == CE_DIGGING_X); /* this one was forgotten until 3.2.3 */ if (change->can_change_or_has_action && change->has_event[trigger_event] && @@ -11818,19 +10508,11 @@ static boolean CheckElementChangeExt(int x, int y, change_done = TRUE; } -#if USE_NEW_DELAYED_ACTION else if (change->has_action) { ExecuteCustomElementAction(x, y, element, p); PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p); } -#else - if (change->has_action) - { - ExecuteCustomElementAction(x, y, element, p); - PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p); - } -#endif } } @@ -12013,11 +10695,38 @@ static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting) player->anim_delay_counter = 0; player->post_delay_counter = 0; - player->dir_waiting = player->MovDir; - player->action_waiting = ACTION_DEFAULT; + player->dir_waiting = player->MovDir; + player->action_waiting = ACTION_DEFAULT; + + player->special_action_bored = ACTION_DEFAULT; + player->special_action_sleeping = ACTION_DEFAULT; + } +} + +static void CheckSaveEngineSnapshot(struct PlayerInfo *player) +{ + 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 (!CheckSaveEngineSnapshotToList()) + 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; - player->special_action_bored = ACTION_DEFAULT; - player->special_action_sleeping = ACTION_DEFAULT; + if (!player->is_dropping) + player->was_dropping = FALSE; } } @@ -12033,11 +10742,12 @@ static void CheckSingleStepMode(struct PlayerInfo *player) SnapField(player, 0, 0); /* stop snapping */ } } + + CheckSaveEngineSnapshot(player); } static byte PlayerActions(struct PlayerInfo *player, byte player_action) { - boolean moved = FALSE, snapped = FALSE, dropped = FALSE; int left = player_action & JOY_LEFT; int right = player_action & JOY_RIGHT; int up = player_action & JOY_UP; @@ -12053,13 +10763,13 @@ static byte PlayerActions(struct PlayerInfo *player, byte player_action) if (player_action) { if (button1) - snapped = SnapField(player, dx, dy); + SnapField(player, dx, dy); else { if (button2) - dropped = DropElement(player); + DropElement(player); - moved = MovePlayer(player, dx, dy); + MovePlayer(player, dx, dy); } CheckSingleStepMode(player); @@ -12159,13 +10869,10 @@ static void CheckLevelTime() if (TimeLeft <= 10 && setup.time_limit) PlaySound(SND_GAME_RUNNING_OUT_OF_TIME); -#if 1 - game_panel_controls[GAME_PANEL_TIME].value = TimeLeft; + /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value + is reset from other values in UpdateGameDoorValues() -- FIX THIS */ - DisplayGameControlValues(); -#else - DrawGameValue_Time(TimeLeft); -#endif + game_panel_controls[GAME_PANEL_TIME].value = TimeLeft; if (!TimeLeft && setup.time_limit) { @@ -12176,32 +10883,23 @@ static void CheckLevelTime() KillPlayer(&stored_player[i]); } } -#if 1 - else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */ + else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */ { game_panel_controls[GAME_PANEL_TIME].value = TimePlayed; - - DisplayGameControlValues(); } -#else - else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */ - DrawGameValue_Time(TimePlayed); -#endif level.native_em_level->lev->time = - (level.time == 0 ? TimePlayed : TimeLeft); + (game.no_time_limit ? TimePlayed : TimeLeft); } if (tape.recording || tape.playing) DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime); } -#if 1 + if (tape.recording || tape.playing) + DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter); + UpdateAndDisplayGameControlValues(); -#else - UpdateGameDoorValues(); - DrawGameDoorValues(); -#endif } void AdvanceFrameAndPlayerCounters(int player_nr) @@ -12222,7 +10920,6 @@ void AdvanceFrameAndPlayerCounters(int player_nr) if (!advance_player_counters) /* not all players may be affected */ continue; -#if USE_NEW_PLAYER_ANIM if (move_frames == 0) /* less than one move per game frame */ { int stepsize = TILEX / move_delay_value; @@ -12233,7 +10930,6 @@ void AdvanceFrameAndPlayerCounters(int player_nr) if (count % delay == 0) move_frames = 1; } -#endif stored_player[i].Frame += move_frames; @@ -12256,9 +10952,9 @@ void AdvanceFrameAndPlayerCounters(int player_nr) } void StartGameActions(boolean init_network_game, boolean record_tape, - long random_seed) + int random_seed) { - unsigned long new_random_seed = InitRND(random_seed); + unsigned int new_random_seed = InitRND(random_seed); if (record_tape) TapeStartRecording(new_random_seed); @@ -12275,10 +10971,12 @@ void StartGameActions(boolean init_network_game, boolean record_tape, InitGame(); } -void GameActions() +void GameActionsExt() { - static unsigned long game_frame_delay = 0; - unsigned long game_frame_delay_value; +#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]; @@ -12287,9 +10985,9 @@ void GameActions() /* detect endless loops, caused by custom element programming */ if (recursion_loop_detected && recursion_loop_depth == 0) { - char *message = getStringCat3("Internal Error ! Element ", + char *message = getStringCat3("Internal Error! Element ", EL_NAME(recursion_loop_element), - " caused endless loop ! Quit the game ?"); + " caused endless loop! Quit the game?"); Error(ERR_WARN, "element '%s' caused endless loop in game engine", EL_NAME(recursion_loop_element)); @@ -12355,9 +11053,22 @@ void GameActions() 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) { @@ -12388,11 +11099,9 @@ void GameActions() /* when playing tape, read previously recorded player input from tape data */ recorded_player_action = (tape.playing ? TapePlayAction() : NULL); -#if 1 /* TapePlayAction() may return NULL when toggling to "pause before death" */ if (tape.pausing) return; -#endif if (tape.set_centered_player) { @@ -12404,7 +11113,7 @@ void GameActions() { summarized_player_action |= stored_player[i].action; - if (!network_playing) + if (!network_playing && (game.team_mode || tape.playing)) stored_player[i].effective_action = stored_player[i].action; } @@ -12413,10 +11122,16 @@ void GameActions() SendToServer_MovePlayer(summarized_player_action); #endif - if (!options.network && !setup.team_mode) - local_player->effective_action = summarized_player_action; + // 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) + stored_player[map_player_action[local_player->index_nr]].effective_action = + summarized_player_action; - if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1) + if (tape.recording && + setup.team_mode && + setup.input_on_focus && + game.centered_player_nr != -1) { for (i = 0; i < MAX_PLAYERS; i++) stored_player[i].effective_action = @@ -12431,9 +11146,13 @@ void GameActions() { tape_action[i] = stored_player[i].effective_action; - /* (this can only happen in the R'n'D game engine) */ - if (tape.recording && tape_action[i] && !tape.player_participates[i]) - tape.player_participates[i] = TRUE; /* player just appeared from CE */ + /* (this may happen in the RND game engine if a player was not present on + the playfield on level start, but appeared later from a custom element */ + if (setup.team_mode && + tape.recording && + tape_action[i] && + !tape.player_participates[i]) + tape.player_participates[i] = TRUE; } /* only record actions from input devices, but not programmed actions */ @@ -12441,16 +11160,56 @@ void GameActions() TapeRecordAction(tape_action); #if USE_NEW_PLAYER_ASSIGNMENTS + // !!! also map player actions in single player mode !!! + // if (game.team_mode) + if (1) { byte mapped_action[MAX_PLAYERS]; +#if DEBUG_PLAYER_ACTIONS + printf(":::"); + for (i = 0; i < MAX_PLAYERS; i++) + printf(" %d, ", stored_player[i].effective_action); +#endif + for (i = 0; i < MAX_PLAYERS; i++) mapped_action[i] = stored_player[map_player_action[i]].effective_action; for (i = 0; i < MAX_PLAYERS; i++) stored_player[i].effective_action = mapped_action[i]; + +#if DEBUG_PLAYER_ACTIONS + printf(" =>"); + for (i = 0; i < MAX_PLAYERS; i++) + printf(" %d, ", stored_player[i].effective_action); + printf("\n"); +#endif + } +#if DEBUG_PLAYER_ACTIONS + else + { + printf(":::"); + for (i = 0; i < MAX_PLAYERS; i++) + printf(" %d, ", stored_player[i].effective_action); + printf("\n"); } #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) { @@ -12462,10 +11221,53 @@ 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; } } +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]; @@ -12476,10 +11278,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() @@ -12492,10 +11290,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() @@ -12505,7 +11304,6 @@ void GameActions_RND() InitPlayfieldScanModeVars(); -#if USE_ONE_MORE_CHANGE_PER_FRAME if (game.engine_version >= VERSION_IDENT(3,2,0,7)) { SCAN_PLAYFIELD(x, y) @@ -12514,7 +11312,6 @@ void GameActions_RND() ChangeEvent[x][y] = -1; } } -#endif if (game.set_centered_player) { @@ -12614,10 +11411,6 @@ void GameActions_RND() } } -#if 0 - debug_print_timestamp(0, "start main loop profiling"); -#endif - SCAN_PLAYFIELD(x, y) { ChangeCount[x][y] = 0; @@ -12631,7 +11424,6 @@ void GameActions_RND() RemoveField(x, y); } -#if USE_NEW_SNAP_DELAY if (Feld[x][y] == EL_ELEMENT_SNAPPING) { MovDelay[x][y]--; @@ -12643,7 +11435,6 @@ void GameActions_RND() TestIfElementTouchesCustomElement(x, y); /* for empty space */ } } -#endif #if DEBUG if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1) @@ -12692,63 +11483,6 @@ void GameActions_RND() #endif } -#if 0 - debug_print_timestamp(0, "- time for pre-main loop:"); -#endif - -#if 0 // -------------------- !!! TEST ONLY !!! -------------------- - SCAN_PLAYFIELD(x, y) - { - element = Feld[x][y]; - graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]); - -#if 1 - { -#if 1 - int element2 = element; - int graphic2 = graphic; -#else - int element2 = Feld[x][y]; - int graphic2 = el_act_dir2img(element2, GfxAction[x][y], GfxDir[x][y]); -#endif - int last_gfx_frame = GfxFrame[x][y]; - - if (graphic_info[graphic2].anim_global_sync) - GfxFrame[x][y] = FrameCounter; - else if (ANIM_MODE(graphic2) == ANIM_CE_VALUE) - GfxFrame[x][y] = CustomValue[x][y]; - else if (ANIM_MODE(graphic2) == ANIM_CE_SCORE) - GfxFrame[x][y] = element_info[element2].collect_score; - else if (ANIM_MODE(graphic2) == ANIM_CE_DELAY) - GfxFrame[x][y] = ChangeDelay[x][y]; - - if (redraw && GfxFrame[x][y] != last_gfx_frame) - DrawLevelGraphicAnimation(x, y, graphic2); - } -#else - ResetGfxFrame(x, y, TRUE); -#endif - -#if 1 - if (ANIM_MODE(graphic) == ANIM_RANDOM && - IS_NEXT_FRAME(GfxFrame[x][y], graphic)) - ResetRandomAnimationValue(x, y); -#endif - -#if 1 - SetRandomAnimationValue(x, y); -#endif - -#if 1 - PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]); -#endif - } -#endif // -------------------- !!! TEST ONLY !!! -------------------- - -#if 0 - debug_print_timestamp(0, "- time for TEST loop: -->"); -#endif - SCAN_PLAYFIELD(x, y) { element = Feld[x][y]; @@ -12778,157 +11512,12 @@ void GameActions_RND() { int page = element_info[element].event_page_nr[CE_DELAY]; -#if 1 HandleElementChange(x, y, page); -#else - if (CAN_CHANGE(element)) - HandleElementChange(x, y, page); - - if (HAS_ACTION(element)) - ExecuteCustomElementAction(x, y, element, page); -#endif - - element = Feld[x][y]; - graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]); - } - -#if 0 // --------------------------------------------------------------------- - - if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element))) - { - StartMoving(x, y); element = Feld[x][y]; graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]); - - if (IS_ANIMATED(graphic) && - !IS_MOVING(x, y) && - !Stop[x][y]) - DrawLevelGraphicAnimationIfNeeded(x, y, graphic); - - if (IS_GEM(element) || element == EL_SP_INFOTRON) - TEST_DrawTwinkleOnField(x, y); - } - else if (IS_MOVING(x, y)) - ContinueMoving(x, y); - else - { - switch (element) - { - case EL_ACID: - case EL_EXIT_OPEN: - case EL_EM_EXIT_OPEN: - case EL_SP_EXIT_OPEN: - case EL_STEEL_EXIT_OPEN: - case EL_EM_STEEL_EXIT_OPEN: - case EL_SP_TERMINAL: - case EL_SP_TERMINAL_ACTIVE: - case EL_EXTRA_TIME: - case EL_SHIELD_NORMAL: - case EL_SHIELD_DEADLY: - if (IS_ANIMATED(graphic)) - DrawLevelGraphicAnimationIfNeeded(x, y, graphic); - break; - - case EL_DYNAMITE_ACTIVE: - case EL_EM_DYNAMITE_ACTIVE: - case EL_DYNABOMB_PLAYER_1_ACTIVE: - case EL_DYNABOMB_PLAYER_2_ACTIVE: - case EL_DYNABOMB_PLAYER_3_ACTIVE: - case EL_DYNABOMB_PLAYER_4_ACTIVE: - case EL_SP_DISK_RED_ACTIVE: - CheckDynamite(x, y); - break; - - case EL_AMOEBA_GROWING: - AmoebeWaechst(x, y); - break; - - case EL_AMOEBA_SHRINKING: - AmoebaDisappearing(x, y); - break; - -#if !USE_NEW_AMOEBA_CODE - case EL_AMOEBA_WET: - case EL_AMOEBA_DRY: - case EL_AMOEBA_FULL: - case EL_BD_AMOEBA: - case EL_EMC_DRIPPER: - AmoebeAbleger(x, y); - break; -#endif - - case EL_GAME_OF_LIFE: - case EL_BIOMAZE: - Life(x, y); - break; - - case EL_EXIT_CLOSED: - CheckExit(x, y); - break; - - case EL_EM_EXIT_CLOSED: - CheckExitEM(x, y); - break; - - case EL_STEEL_EXIT_CLOSED: - CheckExitSteel(x, y); - break; - - case EL_EM_STEEL_EXIT_CLOSED: - CheckExitSteelEM(x, y); - break; - - case EL_SP_EXIT_CLOSED: - CheckExitSP(x, y); - break; - - case EL_EXPANDABLE_WALL_GROWING: - case EL_EXPANDABLE_STEELWALL_GROWING: - MauerWaechst(x, y); - break; - - case EL_EXPANDABLE_WALL: - case EL_EXPANDABLE_WALL_HORIZONTAL: - case EL_EXPANDABLE_WALL_VERTICAL: - case EL_EXPANDABLE_WALL_ANY: - case EL_BD_EXPANDABLE_WALL: - MauerAbleger(x, y); - break; - - case EL_EXPANDABLE_STEELWALL_HORIZONTAL: - case EL_EXPANDABLE_STEELWALL_VERTICAL: - case EL_EXPANDABLE_STEELWALL_ANY: - MauerAblegerStahl(x, y); - break; - - case EL_FLAMES: - CheckForDragon(x, y); - break; - - case EL_EXPLOSION: - break; - - case EL_ELEMENT_SNAPPING: - case EL_DIAGONAL_SHRINKING: - case EL_DIAGONAL_GROWING: - { - graphic = - el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]); - - DrawLevelGraphicAnimationIfNeeded(x, y, graphic); - break; - } - - default: - if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y)) - DrawLevelGraphicAnimationIfNeeded(x, y, graphic); - break; - } } -#else // --------------------------------------------------------------------- - if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element))) { StartMoving(x, y); @@ -13011,8 +11600,6 @@ void GameActions_RND() else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y)) DrawLevelGraphicAnimationIfNeeded(x, y, graphic); -#endif // --------------------------------------------------------------------- - if (IS_BELT_ACTIVE(element)) PlayLevelSoundAction(x, y, ACTION_ACTIVE); @@ -13038,15 +11625,11 @@ void GameActions_RND() } } -#if 0 - debug_print_timestamp(0, "- time for MAIN loop: -->"); -#endif - #if USE_NEW_AMOEBA_CODE /* new experimental amoeba growth stuff */ if (!(FrameCounter % 8)) { - static unsigned long random = 1684108901; + static unsigned int random = 1684108901; for (i = 0; i < level.amoeba_speed * 28 / 8; i++) { @@ -13074,27 +11657,22 @@ void GameActions_RND() } #endif -#if 0 - if (game.explosions_delayed) -#endif - { - game.explosions_delayed = FALSE; - - SCAN_PLAYFIELD(x, y) - { - element = Feld[x][y]; + game.explosions_delayed = FALSE; - 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_TYPE_NORMAL); + SCAN_PLAYFIELD(x, y) + { + element = Feld[x][y]; - ExplodeField[x][y] = EX_TYPE_NONE; - } + 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_TYPE_NORMAL); - game.explosions_delayed = TRUE; + ExplodeField[x][y] = EX_TYPE_NONE; } + game.explosions_delayed = TRUE; + if (game.magic_wall_active) { if (!(game.magic_wall_time_left % 4)) @@ -13196,12 +11774,7 @@ void GameActions_RND() #if USE_DELAYED_GFX_REDRAW SCAN_PLAYFIELD(x, y) { -#if 1 if (GfxRedraw[x][y] != GFX_REDRAW_NONE) -#else - if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)) && - GfxRedraw[x][y] != GFX_REDRAW_NONE) -#endif { /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */ @@ -13215,39 +11788,16 @@ void GameActions_RND() if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS) DrawLevelFieldCrumbledNeighbours(x, y); - if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED) - DrawTwinkleOnField(x, y); - } - - GfxRedraw[x][y] = GFX_REDRAW_NONE; - } -#endif - - CheckLevelTime(); - - DrawAllPlayers(); - PlayAllPlayersSound(); - - if (options.debug) /* calculate frames per second */ - { - static unsigned long fps_counter = 0; - static int fps_frames = 0; - unsigned long 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(); + if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED) + DrawTwinkleOnField(x, y); } - redraw_mask |= REDRAW_FPS; + GfxRedraw[x][y] = GFX_REDRAW_NONE; } +#endif - AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */ + DrawAllPlayers(); + PlayAllPlayersSound(); if (local_player->show_envelope != 0 && local_player->MovPos == 0) { @@ -13256,11 +11806,6 @@ void GameActions_RND() local_player->show_envelope = 0; } -#if 0 - debug_print_timestamp(0, "stop main loop profiling "); - printf("----------------------------------------------------------\n"); -#endif - /* use random number generator in every frame to make it less predictable */ if (game.engine_version >= VERSION_IDENT(3,1,1,0)) RND(1); @@ -13307,101 +11852,16 @@ static boolean AllPlayersInVisibleScreen() void ScrollLevel(int dx, int dy) { -#if 0 - /* (directly solved in BlitBitmap() now) */ - static Bitmap *bitmap_db_field2 = NULL; - int softscroll_offset = (setup.soft_scrolling ? TILEX : 0); - int x, y; -#else + int scroll_offset = 2 * TILEX_VAR; int x, y; -#endif - -#if 0 - /* !!! THIS IS APPARENTLY WRONG FOR PLAYER RELOCATION !!! */ - /* only horizontal XOR vertical scroll direction allowed */ - if ((dx == 0 && dy == 0) || (dx != 0 && dy != 0)) - return; -#endif - -#if 0 - /* (directly solved in BlitBitmap() now) */ - if (bitmap_db_field2 == NULL) - bitmap_db_field2 = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH); - - /* needed when blitting directly to same bitmap -- should not be needed with - recent SDL libraries, but apparently does not work in 1.2.11 directly */ - BlitBitmap(drawto_field, bitmap_db_field2, - FX + TILEX * (dx == -1) - softscroll_offset, - FY + TILEY * (dy == -1) - softscroll_offset, - SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset, - SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset, - FX + TILEX * (dx == 1) - softscroll_offset, - FY + TILEY * (dy == 1) - softscroll_offset); - BlitBitmap(bitmap_db_field2, drawto_field, - FX + TILEX * (dx == 1) - softscroll_offset, - FY + TILEY * (dy == 1) - softscroll_offset, - SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset, - SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset, - FX + TILEX * (dx == 1) - softscroll_offset, - FY + TILEY * (dy == 1) - softscroll_offset); - -#else - -#if 0 - /* !!! DOES NOT WORK FOR DIAGONAL PLAYER RELOCATION !!! */ - int xsize = (BX2 - BX1 + 1); - int ysize = (BY2 - BY1 + 1); - int start = (dx != 0 ? (dx == -1 ? BX1 : BX2) : (dy == -1 ? BY1 : BY2)); - int end = (dx != 0 ? (dx == -1 ? BX2 : BX1) : (dy == -1 ? BY2 : BY1)); - int step = (start < end ? +1 : -1); - - for (i = start; i != end; i += step) - { - BlitBitmap(drawto_field, drawto_field, - FX + TILEX * (dx != 0 ? i + step : 0), - FY + TILEY * (dy != 0 ? i + step : 0), - TILEX * (dx != 0 ? 1 : xsize), - TILEY * (dy != 0 ? 1 : ysize), - FX + TILEX * (dx != 0 ? i : 0), - FY + TILEY * (dy != 0 ? i : 0)); - } - -#else -#if NEW_TILESIZE -#if NEW_SCROLL - int softscroll_offset = (setup.soft_scrolling ? 2 * TILEX_VAR : 0); -#else - int softscroll_offset = (setup.soft_scrolling ? TILEX_VAR : 0); -#endif -#else -#if NEW_SCROLL - int softscroll_offset = (setup.soft_scrolling ? 2 * TILEX : 0); -#else - int softscroll_offset = (setup.soft_scrolling ? TILEX : 0); -#endif -#endif - -#if NEW_TILESIZE - 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); -#else BlitBitmap(drawto_field, drawto_field, - FX + TILEX * (dx == -1) - softscroll_offset, - FY + TILEY * (dy == -1) - softscroll_offset, - SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset, - SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset, - FX + TILEX * (dx == 1) - softscroll_offset, - FY + TILEY * (dy == 1) - softscroll_offset); -#endif - -#endif -#endif + 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) { @@ -13464,11 +11924,7 @@ static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir) static void CheckGravityMovement(struct PlayerInfo *player) { -#if USE_PLAYER_GRAVITY if (player->gravity && !player->programmed_action) -#else - if (game.gravity && !player->programmed_action) -#endif { int move_dir_horizontal = player->effective_action & MV_HORIZONTAL; int move_dir_vertical = player->effective_action & MV_VERTICAL; @@ -13490,11 +11946,7 @@ static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player) { return CheckGravityMovement(player); -#if USE_PLAYER_GRAVITY if (player->gravity && !player->programmed_action) -#else - if (game.gravity && !player->programmed_action) -#endif { int jx = player->jx, jy = player->jy; boolean field_under_player_is_free = @@ -13521,9 +11973,6 @@ boolean MovePlayerOneStep(struct PlayerInfo *player, { int jx = player->jx, jy = player->jy; int new_jx = jx + dx, new_jy = jy + dy; -#if !USE_FIXED_DONT_RUN_INTO - int element; -#endif int can_move; boolean player_can_move = !player->cannot_move; @@ -13550,36 +11999,9 @@ boolean MovePlayerOneStep(struct PlayerInfo *player, } } -#if 1 if (!options.network && game.centered_player_nr == -1 && !AllPlayersInSight(player, new_jx, new_jy)) return MP_NO_ACTION; -#else - if (!options.network && !AllPlayersInSight(player, new_jx, new_jy)) - return MP_NO_ACTION; -#endif - -#if !USE_FIXED_DONT_RUN_INTO - element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy); - - /* (moved to DigField()) */ - if (player_can_move && DONT_RUN_INTO(element)) - { - if (element == EL_ACID && dx == 0 && dy == 1) - { - SplashAcid(new_jx, new_jy); - Feld[jx][jy] = EL_PLAYER_1; - InitMovingField(jx, jy, MV_DOWN); - Store[jx][jy] = EL_ACID; - ContinueMoving(jx, jy); - BuryPlayer(player); - } - else - TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir); - - return MP_MOVING; - } -#endif can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG); if (can_move != MP_MOVING) @@ -13609,9 +12031,7 @@ boolean MovePlayerOneStep(struct PlayerInfo *player, PlayerVisit[jx][jy] = FrameCounter; -#if USE_UFAST_PLAYER_EXIT_BUGFIX player->is_moving = TRUE; -#endif #if 1 /* should better be called in MovePlayer(), but this breaks some tapes */ @@ -13663,7 +12083,7 @@ boolean MovePlayer(struct PlayerInfo *player, int dx, int dy) int original_move_delay_value = player->move_delay_value; #if DEBUG - printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n", + printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]\n", tape.counter); #endif @@ -13678,7 +12098,7 @@ boolean MovePlayer(struct PlayerInfo *player, int dx, int dy) AdvanceFrameAndPlayerCounters(player->index_nr); DrawAllPlayers(); - BackToFront(); + BackToFront_WithFrameDelay(0); } player->move_delay_value = original_move_delay_value; @@ -13697,7 +12117,6 @@ boolean MovePlayer(struct PlayerInfo *player, int dx, int dy) moved |= MovePlayerOneStep(player, 0, dy, dx, dy); } -#if USE_FIXED_BORDER_RUNNING_GFX if (!moved && !player->is_active) { player->is_moving = FALSE; @@ -13706,19 +12125,13 @@ boolean MovePlayer(struct PlayerInfo *player, int dx, int dy) player->is_snapping = FALSE; player->is_pushing = FALSE; } -#endif jx = player->jx; jy = player->jy; -#if 1 if (moved & MP_MOVING && !ScreenMovPos && (player->index_nr == game.centered_player_nr || game.centered_player_nr == -1)) -#else - if (moved & MP_MOVING && !ScreenMovPos && - (player == local_player || !options.network)) -#endif { int old_scroll_x = scroll_x, old_scroll_y = scroll_y; int offset = game.scroll_delay_value; @@ -13773,7 +12186,6 @@ boolean MovePlayer(struct PlayerInfo *player, int dx, int dy) if (scroll_x != old_scroll_x || scroll_y != old_scroll_y) { -#if 1 if (!options.network && game.centered_player_nr == -1 && !AllPlayersInVisibleScreen()) { @@ -13781,14 +12193,6 @@ boolean MovePlayer(struct PlayerInfo *player, int dx, int dy) scroll_y = old_scroll_y; } else -#else - if (!options.network && !AllPlayersInVisibleScreen()) - { - scroll_x = old_scroll_x; - scroll_y = old_scroll_y; - } - else -#endif { ScrollScreen(player, SCROLL_INIT); ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y); @@ -13856,16 +12260,11 @@ void ScrollPlayer(struct PlayerInfo *player, int mode) int last_jx = player->last_jx, last_jy = player->last_jy; int move_stepsize = TILEX / player->move_delay_value; -#if USE_NEW_PLAYER_SPEED if (!player->active) return; if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */ return; -#else - if (!player->active || player->MovPos == 0) - return; -#endif if (mode == SCROLL_INIT) { @@ -13884,13 +12283,8 @@ void ScrollPlayer(struct PlayerInfo *player, int mode) last_field_block_delay += player->move_delay_value; /* when blocking enabled, prevent moving up despite gravity */ -#if USE_PLAYER_GRAVITY if (player->gravity && player->MovDir == MV_UP) block_delay_adjustment = -1; -#else - if (game.gravity && player->MovDir == MV_UP) - block_delay_adjustment = -1; -#endif } /* add block delay adjustment (also possible when not blocking) */ @@ -13900,17 +12294,12 @@ void ScrollPlayer(struct PlayerInfo *player, int mode) MovDelay[last_jx][last_jy] = last_field_block_delay + 1; } -#if USE_NEW_PLAYER_SPEED if (player->MovPos != 0) /* player has not yet reached destination */ return; -#else - return; -#endif } else if (!FrameReached(&player->actual_frame_counter, 1)) return; -#if USE_NEW_PLAYER_SPEED if (player->MovPos != 0) { player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize; @@ -13920,14 +12309,6 @@ void ScrollPlayer(struct PlayerInfo *player, int mode) if (player->MovPos == 0) CheckGravityMovement(player); } -#else - player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize; - player->GfxPos = move_stepsize * (player->MovPos / move_stepsize); - - /* before DrawPlayer() to draw correct player graphic for this case */ - if (player->MovPos == 0) - CheckGravityMovement(player); -#endif if (player->MovPos == 0) /* player reached destination field */ { @@ -13950,14 +12331,10 @@ void ScrollPlayer(struct PlayerInfo *player, int mode) if (Feld[jx][jy] == EL_EXIT_OPEN || Feld[jx][jy] == EL_EM_EXIT_OPEN || -#if 1 Feld[jx][jy] == EL_EM_EXIT_OPENING || -#endif Feld[jx][jy] == EL_STEEL_EXIT_OPEN || Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN || -#if 1 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING || -#endif Feld[jx][jy] == EL_SP_EXIT_OPEN || Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */ { @@ -13996,13 +12373,8 @@ void ScrollPlayer(struct PlayerInfo *player, int mode) CE_PLAYER_ENTERS_X, player->index_bit, enter_side); -#if USE_FIX_CE_ACTION_WITH_PLAYER CheckTriggeredElementChangeBySide(jx, jy, player->initial_element, CE_MOVE_OF_X, move_direction); -#else - CheckTriggeredElementChangeBySide(jx, jy, player->element_nr, - CE_MOVE_OF_X, move_direction); -#endif } if (game.engine_version >= VERSION_IDENT(3,0,7,0)) @@ -14032,40 +12404,34 @@ void ScrollPlayer(struct PlayerInfo *player, int mode) if (TimeLeft <= 10 && setup.time_limit) PlaySound(SND_GAME_RUNNING_OUT_OF_TIME); -#if 1 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft; DisplayGameControlValues(); -#else - DrawGameValue_Time(TimeLeft); -#endif if (!TimeLeft && setup.time_limit) for (i = 0; i < MAX_PLAYERS; i++) KillPlayer(&stored_player[i]); } -#if 1 - else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */ + else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */ { game_panel_controls[GAME_PANEL_TIME].value = TimePlayed; DisplayGameControlValues(); } -#else - else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */ - DrawGameValue_Time(TimePlayed); -#endif } if (tape.single_step && tape.recording && !tape.pausing && !player->programmed_action) TapeTogglePause(TAPE_TOGGLE_AUTOMATIC); + + if (!player->programmed_action) + CheckSaveEngineSnapshot(player); } } void ScrollScreen(struct PlayerInfo *player, int mode) { - static unsigned long screen_frame_counter = 0; + static unsigned int screen_frame_counter = 0; if (mode == SCROLL_INIT) { @@ -14148,7 +12514,6 @@ void TestIfPlayerTouchesCustomElement(int x, int y) CE_PLAYER_TOUCHES_X, player->index_bit, border_side); -#if USE_FIX_CE_ACTION_WITH_PLAYER { /* use player element that is initially defined in the level playfield, not the player element that corresponds to the runtime player number @@ -14159,7 +12524,6 @@ void TestIfPlayerTouchesCustomElement(int x, int y) CheckElementChangeBySide(xx, yy, border_element, player_element, CE_TOUCHING_X, border_side); } -#endif } else if (IS_PLAYER(xx, yy)) /* player found at border element */ { @@ -14177,7 +12541,6 @@ void TestIfPlayerTouchesCustomElement(int x, int y) CE_PLAYER_TOUCHES_X, player->index_bit, center_side); -#if USE_FIX_CE_ACTION_WITH_PLAYER { /* use player element that is initially defined in the level playfield, not the player element that corresponds to the runtime player number @@ -14188,15 +12551,12 @@ void TestIfPlayerTouchesCustomElement(int x, int y) CheckElementChangeBySide(x, y, center_element, player_element, CE_TOUCHING_X, center_side); } -#endif break; } } } -#if USE_ELEMENT_TOUCHING_BUGFIX - void TestIfElementTouchesCustomElement(int x, int y) { static int xy[4][2] = @@ -14282,7 +12642,6 @@ void TestIfElementTouchesCustomElement(int x, int y) CheckElementChangeBySide(x, y, center_element, border_element, CE_TOUCHING_X, border_side); -#if USE_FIX_CE_ACTION_WITH_PLAYER if (IS_PLAYER(xx, yy)) { /* use player element that is initially defined in the level playfield, @@ -14294,74 +12653,9 @@ void TestIfElementTouchesCustomElement(int x, int y) CheckElementChangeBySide(x, y, center_element, player_element, CE_TOUCHING_X, border_side); } -#endif - } -} - -#else - -void TestIfElementTouchesCustomElement_OLD(int x, int y) -{ - static int xy[4][2] = - { - { 0, -1 }, - { -1, 0 }, - { +1, 0 }, - { 0, +1 } - }; - static int trigger_sides[4][2] = - { - /* center side border side */ - { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */ - { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */ - { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */ - { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */ - }; - static int touch_dir[4] = - { - MV_LEFT | MV_RIGHT, - MV_UP | MV_DOWN, - MV_UP | MV_DOWN, - MV_LEFT | MV_RIGHT - }; - boolean change_center_element = FALSE; - int center_element = Feld[x][y]; /* should always be non-moving! */ - int i; - - for (i = 0; i < NUM_DIRECTIONS; i++) - { - int xx = x + xy[i][0]; - int yy = y + xy[i][1]; - int center_side = trigger_sides[i][0]; - int border_side = trigger_sides[i][1]; - int border_element; - - if (!IN_LEV_FIELD(xx, yy)) - continue; - - if (game.engine_version < VERSION_IDENT(3,0,7,0)) - border_element = Feld[xx][yy]; /* may be moving! */ - else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy)) - border_element = Feld[xx][yy]; - else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */ - border_element = MovingOrBlocked2Element(xx, yy); - else - continue; /* center and border element do not touch */ - - /* check for change of center element (but change it only once) */ - if (!change_center_element) - change_center_element = - CheckElementChangeBySide(x, y, center_element, border_element, - CE_TOUCHING_X, border_side); - - /* check for change of border element */ - CheckElementChangeBySide(xx, yy, border_element, center_element, - CE_TOUCHING_X, center_side); } } -#endif - void TestIfElementHitsCustomElement(int x, int y, int direction) { int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0); @@ -14398,7 +12692,6 @@ void TestIfElementHitsCustomElement(int x, int y, int direction) CheckElementChangeBySide(hitx, hity, touched_element, hitting_element, CE_HIT_BY_SOMETHING, opposite_direction); -#if USE_FIX_CE_ACTION_WITH_PLAYER if (IS_PLAYER(hitx, hity)) { /* use player element that is initially defined in the level playfield, @@ -14410,7 +12703,6 @@ void TestIfElementHitsCustomElement(int x, int y, int direction) CheckElementChangeBySide(x, y, hitting_element, player_element, CE_HITTING_X, touched_side); } -#endif } } @@ -14419,69 +12711,6 @@ void TestIfElementHitsCustomElement(int x, int y, int direction) CE_HITTING_SOMETHING, direction); } -#if 0 -void TestIfElementSmashesCustomElement(int x, int y, int direction) -{ - int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0); - int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0); - int hitx = x + dx, hity = y + dy; - int hitting_element = Feld[x][y]; - int touched_element; -#if 0 - boolean object_hit = (IN_LEV_FIELD(hitx, hity) && - !IS_FREE(hitx, hity) && - (!IS_MOVING(hitx, hity) || - MovDir[hitx][hity] != direction || - ABS(MovPos[hitx][hity]) <= TILEY / 2)); -#endif - - if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity)) - return; - -#if 0 - if (IN_LEV_FIELD(hitx, hity) && !object_hit) - return; -#endif - - touched_element = (IN_LEV_FIELD(hitx, hity) ? - MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL); - - CheckElementChangeBySide(x, y, hitting_element, touched_element, - EP_CAN_SMASH_EVERYTHING, direction); - - if (IN_LEV_FIELD(hitx, hity)) - { - int opposite_direction = MV_DIR_OPPOSITE(direction); - int hitting_side = direction; - int touched_side = opposite_direction; -#if 0 - int touched_element = MovingOrBlocked2Element(hitx, hity); -#endif -#if 1 - boolean object_hit = (!IS_MOVING(hitx, hity) || - MovDir[hitx][hity] != direction || - ABS(MovPos[hitx][hity]) <= TILEY / 2); - - object_hit = TRUE; -#endif - - if (object_hit) - { - int i; - - CheckElementChangeBySide(hitx, hity, touched_element, hitting_element, - CE_SMASHED_BY_SOMETHING, opposite_direction); - - CheckElementChangeBySide(x, y, hitting_element, touched_element, - CE_OTHER_IS_SMASHING, touched_side); - - CheckElementChangeBySide(hitx, hity, touched_element, hitting_element, - CE_OTHER_GETS_SMASHED, hitting_side); - } - } -} -#endif - void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir) { int i, kill_x = -1, kill_y = -1; @@ -14813,19 +13042,10 @@ void KillPlayer(struct PlayerInfo *player) player->killed, player->active, player->reanimated); #endif -#if USE_PLAYER_REANIMATION -#if 1 if (player->reanimated) /* killed player may have been reanimated */ player->killed = player->reanimated = FALSE; else BuryPlayer(player); -#else - if (player->killed) /* player may have been reanimated */ - BuryPlayer(player); -#endif -#else - BuryPlayer(player); -#endif } static void KillPlayerUnlessEnemyProtected(int x, int y) @@ -14879,7 +13099,6 @@ void RemovePlayer(struct PlayerInfo *player) ExitY = ZY = jy; } -#if USE_NEW_SNAP_DELAY static void setFieldForSnapping(int x, int y, int element, int direction) { struct ElementInfo *ei = &element_info[element]; @@ -14898,7 +13117,6 @@ static void setFieldForSnapping(int x, int y, int element, int direction) GfxDir[x][y] = direction; GfxFrame[x][y] = -1; } -#endif /* ============================================================================= @@ -14955,11 +13173,7 @@ static int DigField(struct PlayerInfo *player, int opposite_direction = MV_DIR_OPPOSITE(move_direction); int dig_side = MV_DIR_OPPOSITE(move_direction); int old_element = Feld[jx][jy]; -#if USE_FIXED_DONT_RUN_INTO int element = MovingOrBlocked2ElementIfNotLeaving(x, y); -#else - int element; -#endif int collect_count; if (is_player) /* function can also be called by EL_PENGUIN */ @@ -14982,11 +13196,6 @@ static int DigField(struct PlayerInfo *player, } } -#if !USE_FIXED_DONT_RUN_INTO - if (IS_MOVING(x, y) || IS_PLAYER(x, y)) - return MP_NO_ACTION; -#endif - if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0)) old_element = Back[jx][jy]; @@ -15001,7 +13210,6 @@ static int DigField(struct PlayerInfo *player, if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction)) return MP_NO_ACTION; /* field has no opening in this direction */ -#if USE_FIXED_DONT_RUN_INTO if (player_can_move && element == EL_ACID && move_direction == MV_DOWN) { SplashAcid(x, y); @@ -15014,25 +13222,16 @@ static int DigField(struct PlayerInfo *player, return MP_DONT_RUN_INTO; } -#endif -#if USE_FIXED_DONT_RUN_INTO if (player_can_move && DONT_RUN_INTO(element)) { TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir); return MP_DONT_RUN_INTO; } -#endif -#if USE_FIXED_DONT_RUN_INTO if (IS_MOVING(x, y) || IS_PLAYER(x, y)) return MP_NO_ACTION; -#endif - -#if !USE_FIXED_DONT_RUN_INTO - element = Feld[x][y]; -#endif collect_count = element_info[element].collect_count_initial; @@ -15059,17 +13258,10 @@ static int DigField(struct PlayerInfo *player, return MP_NO_ACTION; } -#if USE_PLAYER_GRAVITY if (player->gravity && is_player && !player->is_auto_moving && canFallDown(player) && move_direction != MV_DOWN && !canMoveToValidFieldWithGravity(jx, jy, move_direction)) return MP_NO_ACTION; /* player cannot walk here due to gravity */ -#else - if (game.gravity && is_player && !player->is_auto_moving && - canFallDown(player) && move_direction != MV_DOWN && - !canMoveToValidFieldWithGravity(jx, jy, move_direction)) - return MP_NO_ACTION; /* player cannot walk here due to gravity */ -#endif if (player_can_move && IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction)) @@ -15094,14 +13286,10 @@ static int DigField(struct PlayerInfo *player, } else if (element == EL_EXIT_OPEN || element == EL_EM_EXIT_OPEN || -#if 1 element == EL_EM_EXIT_OPENING || -#endif element == EL_STEEL_EXIT_OPEN || element == EL_EM_STEEL_EXIT_OPEN || -#if 1 element == EL_EM_STEEL_EXIT_OPENING || -#endif element == EL_SP_EXIT_OPEN || element == EL_SP_EXIT_OPENING) { @@ -15172,29 +13360,17 @@ static int DigField(struct PlayerInfo *player, element == EL_SP_GRAVITY_PORT_RIGHT || element == EL_SP_GRAVITY_PORT_UP || element == EL_SP_GRAVITY_PORT_DOWN) -#if USE_PLAYER_GRAVITY - player->gravity = !player->gravity; -#else - game.gravity = !game.gravity; -#endif + player->gravity = !player->gravity; else if (element == EL_SP_GRAVITY_ON_PORT_LEFT || element == EL_SP_GRAVITY_ON_PORT_RIGHT || element == EL_SP_GRAVITY_ON_PORT_UP || element == EL_SP_GRAVITY_ON_PORT_DOWN) -#if USE_PLAYER_GRAVITY player->gravity = TRUE; -#else - game.gravity = TRUE; -#endif else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT || element == EL_SP_GRAVITY_OFF_PORT_RIGHT || element == EL_SP_GRAVITY_OFF_PORT_UP || element == EL_SP_GRAVITY_OFF_PORT_DOWN) -#if USE_PLAYER_GRAVITY player->gravity = FALSE; -#else - game.gravity = FALSE; -#endif } /* automatically move to the next field with double speed */ @@ -15226,14 +13402,10 @@ static int DigField(struct PlayerInfo *player, if (mode == DF_SNAP) { -#if USE_NEW_SNAP_DELAY if (level.block_snap_field) setFieldForSnapping(x, y, element, move_direction); else TestIfElementTouchesCustomElement(x, y); /* for empty space */ -#else - TestIfElementTouchesCustomElement(x, y); /* for empty space */ -#endif CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X, player->index_bit, dig_side); @@ -15257,13 +13429,9 @@ static int DigField(struct PlayerInfo *player, { TimeLeft += level.extra_time; -#if 1 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft; DisplayGameControlValues(); -#else - DrawGameValue_Time(TimeLeft); -#endif } else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY) { @@ -15342,13 +13510,11 @@ static int DigField(struct PlayerInfo *player, if (local_player->gems_still_needed < 0) local_player->gems_still_needed = 0; -#if 1 + game.snapshot.collected_item = TRUE; + game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed; DisplayGameControlValues(); -#else - DrawGameValue_Emeralds(local_player->gems_still_needed); -#endif } RaiseScoreElement(element); @@ -15360,14 +13526,10 @@ static int DigField(struct PlayerInfo *player, if (mode == DF_SNAP) { -#if USE_NEW_SNAP_DELAY if (level.block_snap_field) setFieldForSnapping(x, y, element, move_direction); else TestIfElementTouchesCustomElement(x, y); /* for empty space */ -#else - TestIfElementTouchesCustomElement(x, y); /* for empty space */ -#endif CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X, player->index_bit, dig_side); @@ -15479,13 +13641,8 @@ static int DigField(struct PlayerInfo *player, PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY, ACTION_FILLING); -#if 1 if (local_player->sokobanfields_still_needed == 0 && (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban)) -#else - if (local_player->sokobanfields_still_needed == 0 && - game.emulation == EMU_SOKOBAN) -#endif { PlayerWins(player); @@ -15553,9 +13710,16 @@ static int DigField(struct PlayerInfo *player, 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)) @@ -15608,14 +13772,11 @@ static int DigField(struct PlayerInfo *player, if (level.time > 0 || level.use_time_orb_bug) { TimeLeft += level.time_orb_time; + game.no_time_limit = FALSE; -#if 1 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft; DisplayGameControlValues(); -#else - DrawGameValue_Time(TimeLeft); -#endif } ResetGfxAnimation(x, y); @@ -15742,20 +13903,9 @@ static boolean DigFieldByCE(int x, int y, int digging_element) Store[x][y] = EL_EMPTY; -#if 1 /* this makes it possible to leave the removed element again */ if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element))) Store[x][y] = element; -#else - if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element))) - { - int move_leave_element = element_info[digging_element].move_leave_element; - - /* this makes it possible to leave the removed element again */ - Store[x][y] = (move_leave_element == EL_TRIGGER_ELEMENT ? - element : move_leave_element); - } -#endif return TRUE; } @@ -15797,14 +13947,9 @@ static boolean SnapField(struct PlayerInfo *player, int dx, int dy) return FALSE; } -#if USE_NEW_CONTINUOUS_SNAPPING /* prevent snapping with already pressed snap key when not allowed */ if (player->is_snapping && !can_continue_snapping) return FALSE; -#else - if (player->is_snapping) - return FALSE; -#endif player->MovDir = snap_direction; @@ -15846,17 +13991,7 @@ static boolean DropElement(struct PlayerInfo *player) int dropx = player->jx, dropy = player->jy; int drop_direction = player->MovDir; int drop_side = drop_direction; -#if 1 int drop_element = get_next_dropped_element(player); -#else - int drop_element = (player->inventory_size > 0 ? - player->inventory_element[player->inventory_size - 1] : - player->inventory_infinite_element != EL_UNDEFINED ? - player->inventory_infinite_element : - player->dynabombs_left > 0 ? - EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr : - EL_UNDEFINED); -#endif player->is_dropping_pressed = TRUE; @@ -15962,23 +14097,13 @@ static boolean DropElement(struct PlayerInfo *player) if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) && element_info[new_element].move_pattern == MV_WHEN_DROPPED) { - int move_direction, nextx, nexty; - if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC) MovDir[dropx][dropy] = drop_direction; - move_direction = MovDir[dropx][dropy]; - nextx = dropx + GET_DX_FROM_DIR(move_direction); - nexty = dropy + GET_DY_FROM_DIR(move_direction); - ChangeCount[dropx][dropy] = 0; /* allow at least one more change */ -#if USE_FIX_IMPACT_COLLISION /* do not cause impact style collision by dropping elements that can fall */ CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION; -#else - CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION; -#endif } player->drop_delay = GET_NEW_DROP_DELAY(drop_element); @@ -16279,49 +14404,16 @@ void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp) int x = xx - offset; int y = yy - offset; -#if 0 - printf("::: %d -> %d\n", element_sp, action_sp); -#endif - PlayLevelSoundElementAction(x, y, element, action); } -#if 0 -void ChangeTime(int value) -{ - int *time = (level.time == 0 ? &TimePlayed : &TimeLeft); - - *time += value; - - /* EMC game engine uses value from time counter of RND game engine */ - level.native_em_level->lev->time = *time; - - DrawGameValue_Time(*time); -} - -void RaiseScore(int value) -{ - /* EMC game engine and RND game engine have separate score counters */ - int *score = (level.game_engine_type == GAME_ENGINE_TYPE_EM ? - &level.native_em_level->lev->score : &local_player->score); - - *score += value; - - DrawGameValue_Score(*score); -} -#endif - void RaiseScore(int value) { local_player->score += value; -#if 1 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score; DisplayGameControlValues(); -#else - DrawGameValue_Score(local_player->score); -#endif } void RaiseScoreElement(int element) @@ -16408,6 +14500,10 @@ void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message) { if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED)) { + /* closing door required in case of envelope style request dialogs */ + if (!skip_request) + CloseDoor(DOOR_CLOSE_1); + #if defined(NETWORK_AVALIABLE) if (options.network) SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER); @@ -16415,37 +14511,11 @@ void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message) #endif { if (quick_quit) - { -#if 1 - -#if 1 FadeSkipNextFadeIn(); -#else - fading = fading_none; -#endif - -#else - OpenDoor(DOOR_CLOSE_1); -#endif - - game_status = GAME_MODE_MAIN; - -#if 1 - DrawAndFadeInMainMenu(REDRAW_FIELD); -#else - DrawMainMenu(); -#endif - } - else - { -#if 0 - FadeOut(REDRAW_FIELD); -#endif - game_status = GAME_MODE_MAIN; + SetGameStatus(GAME_MODE_MAIN); - DrawAndFadeInMainMenu(REDRAW_FIELD); - } + DrawMainMenu(); } } else /* continue playing the game */ @@ -16466,7 +14536,7 @@ void RequestQuitGame(boolean ask_if_really_quit) boolean skip_request = AllPlayersGone || quick_quit; RequestQuitGameExt(skip_request, quick_quit, - "Do you really want to quit the game ?"); + "Do you really want to quit the game?"); } @@ -16474,22 +14544,11 @@ void RequestQuitGame(boolean ask_if_really_quit) /* random generator functions */ /* ------------------------------------------------------------------------- */ -unsigned int InitEngineRandom_RND(long seed) +unsigned int InitEngineRandom_RND(int seed) { game.num_random_calls = 0; -#if 0 - unsigned int rnd_seed = InitEngineRandom(seed); - - printf("::: START RND: %d\n", rnd_seed); - - return rnd_seed; -#else - return InitEngineRandom(seed); - -#endif - } unsigned int RND(int max) @@ -16567,7 +14626,7 @@ static void SaveEngineSnapshotValues_RND() static void LoadEngineSnapshotValues_RND() { - unsigned long num_random_calls = game.num_random_calls; + unsigned int num_random_calls = game.num_random_calls; int i, j; for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++) @@ -16611,97 +14670,106 @@ static void LoadEngineSnapshotValues_RND() } } -void SaveEngineSnapshot() +void FreeEngineSnapshotSingle() { - /* do not save snapshots from editor */ - if (level_editor_test_game) - return; + FreeSnapshotSingle(); + + setString(&snapshot_level_identifier, NULL); + snapshot_level_nr = -1; +} + +void FreeEngineSnapshotList() +{ + 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 */ - SaveEngineSnapshotValues_RND(); - SaveEngineSnapshotValues_EM(); - SaveEngineSnapshotValues_SP(); + if (level.game_engine_type == GAME_ENGINE_TYPE_RND) + SaveEngineSnapshotValues_RND(); + if (level.game_engine_type == GAME_ENGINE_TYPE_EM) + SaveEngineSnapshotValues_EM(); + if (level.game_engine_type == GAME_ENGINE_TYPE_SP) + SaveEngineSnapshotValues_SP(&buffers); /* save values stored in special snapshot structure */ - SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd)); - SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em)); - SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp)); + if (level.game_engine_type == GAME_ENGINE_TYPE_RND) + SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd)); + if (level.game_engine_type == GAME_ENGINE_TYPE_EM) + SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em)); + if (level.game_engine_type == GAME_ENGINE_TYPE_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; @@ -16716,60 +14784,154 @@ 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; +} + +boolean CheckSaveEngineSnapshotToList() +{ + boolean save_snapshot = + ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) || + (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE && + 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 LoadEngineSnapshot() +void SaveEngineSnapshotToList() { - /* restore generically stored snapshot buffers */ + if (game.snapshot.mode == SNAPSHOT_MODE_OFF || + tape.quick_resume) + return; + + ListNode *buffers = SaveEngineSnapshotBuffers(); + + /* finally save all snapshot buffers to snapshot list */ + SaveSnapshotToList(buffers); +} - LoadEngineSnapshotBuffers(); +void SaveEngineSnapshotToListInitial() +{ + FreeEngineSnapshotList(); + + SaveEngineSnapshotToList(); +} +void LoadEngineSnapshotValues() +{ /* restore special values from snapshot structure */ - LoadEngineSnapshotValues_RND(); - LoadEngineSnapshotValues_EM(); - LoadEngineSnapshotValues_SP(); + if (level.game_engine_type == GAME_ENGINE_TYPE_RND) + LoadEngineSnapshotValues_RND(); + if (level.game_engine_type == GAME_ENGINE_TYPE_EM) + LoadEngineSnapshotValues_EM(); + if (level.game_engine_type == GAME_ENGINE_TYPE_SP) + LoadEngineSnapshotValues_SP(); +} + +void LoadEngineSnapshotSingle() +{ + LoadSnapshotSingle(); + + LoadEngineSnapshotValues(); +} + +void LoadEngineSnapshot_Undo(int steps) +{ + LoadSnapshotFromList_Older(steps); + + LoadEngineSnapshotValues(); +} + +void LoadEngineSnapshot_Redo(int steps) +{ + LoadSnapshotFromList_Newer(steps); + + LoadEngineSnapshotValues(); } -boolean CheckEngineSnapshot() +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] = { { - 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_SOUND_MUSIC, &game.button.sound_music, + IMG_GFX_GAME_BUTTON_UNDO, &game.button.undo, + GAME_CTRL_ID_UNDO, "undo step" + }, + { + IMG_GFX_GAME_BUTTON_REDO, &game.button.redo, + GAME_CTRL_ID_REDO, "redo step" + }, + { + IMG_GFX_GAME_BUTTON_SAVE, &game.button.save, + GAME_CTRL_ID_SAVE, "save game" + }, + { + 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_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" } }; @@ -16781,11 +14943,13 @@ 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; - unsigned long event_mask; + unsigned int event_mask; + int base_x = (tape.show_game_buttons ? VX : DX); + int base_y = (tape.show_game_buttons ? VY : DY); int gd_x = gfx->src_x; int gd_y = gfx->src_y; int gd_xp = gfx->src_x + gfx->pressed_xoffset; @@ -16796,14 +14960,29 @@ void CreateGameButtons() int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset; int id = i; + if (gfx->bitmap == NULL) + { + game_gadget[id] = NULL; + + continue; + } + if (id == GAME_CTRL_ID_STOP || - id == GAME_CTRL_ID_PAUSE || - id == GAME_CTRL_ID_PLAY) + id == GAME_CTRL_ID_PLAY || + id == GAME_CTRL_ID_SAVE || + id == GAME_CTRL_ID_LOAD) { button_type = GD_TYPE_NORMAL_BUTTON; 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; @@ -16816,8 +14995,8 @@ void CreateGameButtons() gi = CreateGadget(GDI_CUSTOM_ID, id, GDI_INFO_TEXT, gamebutton_info[i].infotext, - GDI_X, DX + pos->x, - GDI_Y, DY + pos->y, + GDI_X, base_x + GDI_ACTIVE_POS(pos->x), + GDI_Y, base_y + GDI_ACTIVE_POS(pos->y), GDI_WIDTH, gfx->width, GDI_HEIGHT, gfx->height, GDI_TYPE, button_type, @@ -16847,12 +15026,80 @@ void FreeGameButtons() FreeGadget(game_gadget[i]); } -static void MapGameButtons() +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]); +} + +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; + + 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]); + + UnmapGameButtonsAtSamePosition_All(); +} + +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]); + + 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]); + + MapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO); + MapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO); + + ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, FALSE, GDI_END); +} + +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]); + + UnmapGameButtonsAtSamePosition_All(); + + RedrawGameButtons(); } void UnmapGameButtons() @@ -16869,24 +15116,75 @@ void RedrawGameButtons() for (i = 0; i < NUM_GAME_BUTTONS; i++) RedrawGadget(game_gadget[i]); + + // RedrawGadget() may have set REDRAW_ALL if buttons are defined off-area + redraw_mask &= ~REDRAW_ALL; +} + +void GameUndoRedoExt() +{ + ClearPlayerAction(); + + tape.pausing = TRUE; + + RedrawPlayfield(); + UpdateAndDisplayGameControlValues(); + + DrawCompleteVideoDisplay(); + DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime); + DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter); + DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 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) +static void HandleGameButtonsExt(int id, int button) { - if (game_status != GAME_MODE_PLAYING) + static boolean game_undo_executed = FALSE; + int steps = BUTTON_STEPSIZE(button); + boolean handle_game_buttons = + (game_status == GAME_MODE_PLAYING || + (game_status == GAME_MODE_MAIN && tape.show_game_buttons)); + + if (!handle_game_buttons) return; switch (id) { case GAME_CTRL_ID_STOP: + if (game_status == GAME_MODE_MAIN) + break; + if (tape.playing) TapeStop(); else RequestQuitGame(TRUE); + break; case GAME_CTRL_ID_PAUSE: - if (options.network) + case GAME_CTRL_ID_PAUSE2: + if (options.network && game_status == GAME_MODE_PLAYING) { #if defined(NETWORK_AVALIABLE) if (tape.pausing) @@ -16897,23 +15195,54 @@ static void HandleGameButtonsExt(int id) } else TapeTogglePause(TAPE_TOGGLE_MANUAL); + + game_undo_executed = FALSE; + break; case GAME_CTRL_ID_PLAY: - if (tape.pausing) + if (game_status == GAME_MODE_MAIN) + { + StartGameActions(options.network, setup.autorecord, level.random_seed); + } + else if (tape.pausing) { #if defined(NETWORK_AVALIABLE) if (options.network) SendToServer_ContinuePlaying(); else #endif - { - tape.pausing = FALSE; - DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0); - } + 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; + + 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) { @@ -16960,24 +15289,16 @@ 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) { -#if 1 + if (key == setup.shortcut.sound_simple) ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON); else if (key == setup.shortcut.sound_loops) ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON); else if (key == setup.shortcut.sound_music) ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON); -#else - if (key == setup.shortcut.sound_simple) - HandleGameButtonsExt(SOUND_CTRL_ID_SIMPLE); - else if (key == setup.shortcut.sound_loops) - HandleGameButtonsExt(SOUND_CTRL_ID_LOOPS); - else if (key == setup.shortcut.sound_music) - HandleGameButtonsExt(SOUND_CTRL_ID_MUSIC); -#endif }