X-Git-Url: https://git.artsoft.org/?a=blobdiff_plain;f=src%2Fgame.c;h=be311d801f790c1e62d5091c7e4acd9ca63b020a;hb=cb8b58732432cc7f3163446dedd54c63126b23c3;hp=fea0d6ce8e1588f28bfc94634545cf5e5a180c74;hpb=7c0b78ef65acbe880d7f905a5e38fb7bdac8f007;p=rocksndiamonds.git diff --git a/src/game.c b/src/game.c index fea0d6ce..be311d80 100644 --- a/src/game.c +++ b/src/game.c @@ -53,6 +53,12 @@ #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) + /* for DigField() */ #define DF_NO_PUSH 0 @@ -115,8 +121,9 @@ /* values for delayed check of falling and moving elements and for collision */ #define CHECK_DELAY_MOVING 3 -#define CHECK_DELAY_FALLING 3 +#define CHECK_DELAY_FALLING CHECK_DELAY_MOVING #define CHECK_DELAY_COLLISION 2 +#define CHECK_DELAY_IMPACT CHECK_DELAY_COLLISION /* values for initial player move delay (initial delay counter value) */ #define INITIAL_MOVE_DELAY_OFF -1 @@ -142,7 +149,7 @@ #define GET_DX_FROM_DIR(d) ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0) #define GET_DY_FROM_DIR(d) ((d) == MV_UP ? -1 : (d) == MV_DOWN ? 1 : 0) -#define INIT_GFX_RANDOM() (SimpleRND(1000000)) +#define INIT_GFX_RANDOM() (GetSimpleRandom(1000000)) #define GET_NEW_PUSH_DELAY(e) ( (element_info[e].push_delay_fixed) + \ RND(element_info[e].push_delay_random)) @@ -176,7 +183,7 @@ (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score : \ (e) == EL_CURRENT_CE_VALUE ? (cv) : \ (e) == EL_CURRENT_CE_SCORE ? (cs) : \ - (e) >= EL_LAST_CE_8 && (e) <= EL_NEXT_CE_8 ? \ + (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ? \ RESOLVED_REFERENCE_ELEMENT(be, e) : \ (e)) @@ -237,7 +244,10 @@ ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y])) #define PENGUIN_CAN_ENTER_FIELD(e, x, y) \ - ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN ||\ + ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \ + Feld[x][y] == EL_EM_EXIT_OPEN || \ + Feld[x][y] == EL_STEEL_EXIT_OPEN || \ + Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \ IS_FOOD_PENGUIN(Feld[x][y]))) #define DRAGON_CAN_ENTER_FIELD(e, x, y) \ ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0) @@ -372,6 +382,33 @@ static int getInvisibleFromInvisibleActiveElement(int); static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS]; +/* for detection of endless loops, caused by custom element programming */ +/* (using maximal playfield width x 10 is just a rough approximation) */ +#define MAX_ELEMENT_CHANGE_RECURSION_DEPTH (MAX_PLAYFIELD_WIDTH * 10) + +#define RECURSION_LOOP_DETECTION_START(e, rc) \ +{ \ + if (recursion_loop_detected) \ + return (rc); \ + \ + if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH) \ + { \ + recursion_loop_detected = TRUE; \ + recursion_loop_element = (e); \ + } \ + \ + recursion_loop_depth++; \ +} + +#define RECURSION_LOOP_DETECTION_END() \ +{ \ + recursion_loop_depth--; \ +} + +static int recursion_loop_depth; +static boolean recursion_loop_detected; +static boolean recursion_loop_element; + /* ------------------------------------------------------------------------- */ /* definition of elements that automatically change to other elements after */ @@ -440,6 +477,62 @@ static struct ChangingElementInfo change_delay_list[] = NULL, NULL }, + { + EL_STEEL_EXIT_OPENING, + EL_STEEL_EXIT_OPEN, + 29, + NULL, + NULL, + NULL + }, + { + EL_STEEL_EXIT_CLOSING, + EL_STEEL_EXIT_CLOSED, + 29, + NULL, + NULL, + NULL + }, + { + EL_EM_EXIT_OPENING, + EL_EM_EXIT_OPEN, + 29, + NULL, + NULL, + NULL + }, + { + EL_EM_EXIT_CLOSING, +#if 1 + EL_EMPTY, +#else + EL_EM_EXIT_CLOSED, +#endif + 29, + NULL, + NULL, + NULL + }, + { + EL_EM_STEEL_EXIT_OPENING, + EL_EM_STEEL_EXIT_OPEN, + 29, + NULL, + NULL, + NULL + }, + { + EL_EM_STEEL_EXIT_CLOSING, +#if 1 + EL_STEELWALL, +#else + EL_EM_STEEL_EXIT_CLOSED, +#endif + 29, + NULL, + NULL, + NULL + }, { EL_SP_EXIT_OPENING, EL_SP_EXIT_OPEN, @@ -561,6 +654,14 @@ static struct ChangingElementInfo change_delay_list[] = RunTimegateWheel, NULL }, + { + EL_DC_TIMEGATE_SWITCH_ACTIVE, + EL_DC_TIMEGATE_SWITCH, + 0, + InitTimegateWheel, + RunTimegateWheel, + NULL + }, { EL_EMC_MAGIC_BALL_ACTIVE, EL_EMC_MAGIC_BALL_ACTIVE, @@ -633,10 +734,14 @@ move_stepsize_list[] = { EL_AMOEBA_DROPPING, 2 }, { EL_QUICKSAND_FILLING, 1 }, { EL_QUICKSAND_EMPTYING, 1 }, + { EL_QUICKSAND_FAST_FILLING, 2 }, + { EL_QUICKSAND_FAST_EMPTYING, 2 }, { EL_MAGIC_WALL_FILLING, 2 }, - { EL_BD_MAGIC_WALL_FILLING, 2 }, { EL_MAGIC_WALL_EMPTYING, 2 }, + { EL_BD_MAGIC_WALL_FILLING, 2 }, { EL_BD_MAGIC_WALL_EMPTYING, 2 }, + { EL_DC_MAGIC_WALL_FILLING, 2 }, + { EL_DC_MAGIC_WALL_EMPTYING, 2 }, { EL_UNDEFINED, 0 }, }; @@ -1126,6 +1231,11 @@ static void InitField(int x, int y, boolean init_game) 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: @@ -1779,6 +1889,11 @@ static void InitGameEngine() EL_EMPTY); } } + + /* ---------- initialize recursion detection ------------------------------ */ + recursion_loop_depth = 0; + recursion_loop_detected = FALSE; + recursion_loop_element = EL_UNDEFINED; } int get_num_special_action(int element, int action_first, int action_last) @@ -1838,6 +1953,7 @@ void InitGame() player->present = FALSE; player->active = FALSE; + player->killed = FALSE; player->action = 0; player->effective_action = 0; @@ -1854,6 +1970,8 @@ void InitGame() for (j = 0; j < MAX_NUM_KEYS; j++) player->key[j] = FALSE; + player->num_white_keys = 0; + player->dynabomb_count = 0; player->dynabomb_size = 1; player->dynabombs_left = 0; @@ -1929,8 +2047,10 @@ void InitGame() player->drop_delay = 0; player->drop_pressed_delay = 0; - player->last_jx = player->last_jy = 0; - player->jx = player->jy = 0; + player->last_jx = -1; + player->last_jy = -1; + player->jx = -1; + player->jy = -1; player->shield_normal_time_left = 0; player->shield_deadly_time_left = 0; @@ -2030,6 +2150,7 @@ void InitGame() WasJustMoving[x][y] = 0; WasJustFalling[x][y] = 0; CheckCollision[x][y] = 0; + CheckImpact[x][y] = 0; Stop[x][y] = FALSE; Pushed[x][y] = FALSE; @@ -2445,6 +2566,13 @@ void InitGame() } } +#if 1 + UnmapAllGadgets(); + + MapGameButtons(); + MapTapeButtons(); +#endif + game.restart_level = FALSE; } @@ -2474,7 +2602,7 @@ void InitMovDir(int x, int y) { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN } }; - switch(element) + switch (element) { case EL_BUG_RIGHT: case EL_BUG_UP: @@ -2671,12 +2799,14 @@ void GameWon() static int game_over_delay = 0; int game_over_delay_value = 50; - /* do not start end game actions before the player stops moving (to exit) */ - if (local_player->MovPos) - return; - if (!local_player->LevelSolved_GameEnd) { + int i; + + /* do not start end game actions before the player stops moving (to exit) */ + if (local_player->MovPos) + return; + local_player->LevelSolved_GameEnd = TRUE; local_player->LevelSolved_SaveTape = tape.recording; local_player->LevelSolved_SaveScore = !tape.playing; @@ -2684,6 +2814,10 @@ void GameWon() if (tape.auto_play) /* tape might already be stopped here */ tape.auto_play_level_solved = TRUE; +#if 1 + TapeStop(); +#endif + game_over_delay = game_over_delay_value; time = time_final = (level.time == 0 ? TimePlayed : TimeLeft); @@ -2711,23 +2845,56 @@ void GameWon() DrawGameValue_Score(score); } - if (ExitX >= 0 && ExitY >= 0) /* local player has left the level */ + if (level.game_engine_type == GAME_ENGINE_TYPE_RND) { - /* close exit door after last player */ - if (AllPlayersGone && - (Feld[ExitX][ExitY] == EL_EXIT_OPEN || - Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN)) + if (ExitX >= 0 && ExitY >= 0) /* local player has left the level */ { - int element = Feld[ExitX][ExitY]; + /* close exit door after last player */ + if ((AllPlayersGone && + (Feld[ExitX][ExitY] == EL_EXIT_OPEN || + Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN || + Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) || + Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN || + Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN) + { + int element = Feld[ExitX][ExitY]; - Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING : - EL_SP_EXIT_CLOSING); +#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); + } + } - PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING); + /* player disappears */ + DrawLevelField(ExitX, ExitY); } - /* player disappears */ - DrawLevelField(ExitX, ExitY); + for (i = 0; i < MAX_PLAYERS; i++) + { + struct PlayerInfo *player = &stored_player[i]; + + if (player->present) + { + RemovePlayer(player); + + /* player disappears */ + DrawLevelField(player->jx, player->jy); + } + } } PlaySound(SND_GAME_WINNING); @@ -2770,9 +2937,15 @@ void GameEnd() 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 } if (level_editor_test_game) @@ -2892,10 +3065,9 @@ int NewHiScore() return position; } -inline static int getElementMoveStepsize(int x, int y) +inline static int getElementMoveStepsizeExt(int x, int y, int direction) { int element = Feld[x][y]; - int direction = MovDir[x][y]; int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0); int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0); int horiz_move = (dx != 0); @@ -2915,6 +3087,11 @@ inline static int getElementMoveStepsize(int x, int y) return step; } +inline static int getElementMoveStepsize(int x, int y) +{ + return getElementMoveStepsizeExt(x, y, MovDir[x][y]); +} + void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir) { if (player->GfxAction != action || player->GfxDir != dir) @@ -2975,18 +3152,51 @@ void InitMovingField(int x, int y, int direction) int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0); 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 + is_moving_before = WasJustMoving[x][y]; +#else + is_moving_before = (getElementMoveStepsizeExt(x, y, MovDir[x][y]) != 0); +#endif + is_moving_after = (getElementMoveStepsizeExt(x, y, direction) != 0); - if (!WasJustMoving[x][y] || direction != MovDir[x][y]) + /* 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" */ - if (getElementMoveStepsize(x, y) != 0) /* moving or being moved */ + if (is_moving_after) { if (Feld[newx][newy] == EL_EMPTY) Feld[newx][newy] = EL_BLOCKED; @@ -3133,8 +3343,10 @@ void RemoveMovingField(int x, int y) if (element == EL_BLOCKED && (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING || + Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING || Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING || Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING || + Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING || Feld[oldx][oldy] == EL_AMOEBA_DROPPING)) next_element = get_next_element(Feld[oldx][oldy]); @@ -3245,8 +3457,8 @@ static void setScreenCenteredToAllPlayers(int *sx, int *sy) *sy = (sy1 + sy2) / 2; } -void DrawRelocateScreen(int x, int y, int move_dir, boolean center_screen, - boolean quick_relocation) +void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir, + boolean center_screen, boolean quick_relocation) { boolean ffwd_delay = (tape.playing && tape.fast_forward); boolean no_delay = (tape.warp_forward); @@ -3259,13 +3471,39 @@ void DrawRelocateScreen(int x, int y, int move_dir, boolean center_screen, if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen) { - scroll_x = (x < SBX_Left + MIDPOSX ? SBX_Left : - x > SBX_Right + MIDPOSX ? SBX_Right : - x - MIDPOSX); + if (center_screen) + { + scroll_x = (x < SBX_Left + MIDPOSX ? SBX_Left : + x > SBX_Right + MIDPOSX ? SBX_Right : + x - MIDPOSX); + + scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper : + y > SBY_Lower + MIDPOSY ? SBY_Lower : + y - MIDPOSY); + } + else + { + /* quick relocation (without scrolling), but do not center screen */ + + int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left : + old_x > SBX_Right + MIDPOSX ? SBX_Right : + old_x - MIDPOSX); + + int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper : + old_y > SBY_Lower + MIDPOSY ? SBY_Lower : + old_y - MIDPOSY); + + int offset_x = x + (scroll_x - center_scroll_x); + int offset_y = y + (scroll_y - center_scroll_y); - scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper : - y > SBY_Lower + MIDPOSY ? SBY_Lower : - y - MIDPOSY); + scroll_x = (offset_x < SBX_Left + MIDPOSX ? SBX_Left : + offset_x > SBX_Right + MIDPOSX ? SBX_Right : + offset_x - MIDPOSX); + + scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper : + offset_y > SBY_Lower + MIDPOSY ? SBY_Lower : + offset_y - MIDPOSY); + } } else { @@ -3412,8 +3650,8 @@ void RelocatePlayer(int jx, int jy, int el_player_raw) } /* only visually relocate centered player */ - DrawRelocateScreen(player->jx, player->jy, player->MovDir, FALSE, - level.instant_relocation); + DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir, + FALSE, level.instant_relocation); TestIfPlayerTouchesBadThing(jx, jy); TestIfPlayerTouchesCustomElement(jx, jy); @@ -3846,7 +4084,7 @@ void Bang(int x, int y) } } - switch(element) + switch (element) { case EL_BUG: case EL_SPACESHIP: @@ -3870,6 +4108,14 @@ void Bang(int x, int y) explosion_type = EX_TYPE_DYNA; 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; + case EL_PENGUIN: case EL_LAMP: case EL_LAMP_ACTIVE: @@ -4083,6 +4329,12 @@ static void ToggleSwitchgateSwitch(int x, int y) Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos; 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; + DrawLevelField(xx, yy); + } #else if (element == EL_SWITCHGATE_SWITCH_UP) { @@ -4094,6 +4346,16 @@ static void ToggleSwitchgateSwitch(int x, int y) Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP; DrawLevelField(xx, yy); } + else if (element == EL_DC_SWITCHGATE_SWITCH_UP) + { + Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN; + DrawLevelField(xx, yy); + } + else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN) + { + Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP; + DrawLevelField(xx, yy); + } #endif else if (element == EL_SWITCHGATE_OPEN || element == EL_SWITCHGATE_OPENING) @@ -4310,7 +4572,7 @@ static void ActivateTimegateSwitch(int x, int y) element == EL_TIMEGATE_CLOSING) { Feld[xx][yy] = EL_TIMEGATE_OPENING; - PlayLevelSound(xx, yy, SND_TIMEGATE_OPENING); + PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING); } /* @@ -4323,7 +4585,12 @@ 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) @@ -4358,6 +4625,16 @@ void Impact(int x, int y) object_hit = TRUE; } + + if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE) + { + RemoveMovingField(x, y + 1); + Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY; + Feld[x][y + 2] = EL_ROCK; + DrawLevelField(x, y + 2); + + object_hit = TRUE; + } #endif if (object_hit) @@ -4386,7 +4663,8 @@ void Impact(int x, int y) Bang(x, y); return; } - else if (impact && element == EL_PEARL) + else if (impact && element == EL_PEARL && + smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE) { ResetGfxAnimation(x, y); @@ -4419,26 +4697,33 @@ void Impact(int x, int y) if (object_hit) /* check which object was hit */ { - if (CAN_PASS_MAGIC_WALL(element) && - (smashed == EL_MAGIC_WALL || - smashed == EL_BD_MAGIC_WALL)) + if ((CAN_PASS_MAGIC_WALL(element) && + (smashed == EL_MAGIC_WALL || + smashed == EL_BD_MAGIC_WALL)) || + (CAN_PASS_DC_MAGIC_WALL(element) && + smashed == EL_DC_MAGIC_WALL)) { int xx, yy; int activated_magic_wall = (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE : - EL_BD_MAGIC_WALL_ACTIVE); + smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE : + EL_DC_MAGIC_WALL_ACTIVE); /* activate magic wall / mill */ SCAN_PLAYFIELD(xx, yy) + { if (Feld[xx][yy] == smashed) Feld[xx][yy] = activated_magic_wall; + } game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND; game.magic_wall_active = TRUE; PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ? SND_MAGIC_WALL_ACTIVATING : - SND_BD_MAGIC_WALL_ACTIVATING)); + smashed == EL_BD_MAGIC_WALL ? + SND_BD_MAGIC_WALL_ACTIVATING : + SND_DC_MAGIC_WALL_ACTIVATING)); } if (IS_PLAYER(x, y + 1)) @@ -4518,7 +4803,9 @@ void Impact(int x, int y) ToggleBeltSwitch(x, y + 1); } else if (smashed == EL_SWITCHGATE_SWITCH_UP || - smashed == EL_SWITCHGATE_SWITCH_DOWN) + smashed == EL_SWITCHGATE_SWITCH_DOWN || + smashed == EL_DC_SWITCHGATE_SWITCH_UP || + smashed == EL_DC_SWITCHGATE_SWITCH_DOWN) { ToggleSwitchgateSwitch(x, y + 1); } @@ -4551,12 +4838,15 @@ void Impact(int x, int y) /* play sound of magic wall / mill */ if (!last_line && (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE || - Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) + Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE || + Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)) { if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE) PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING); else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE) PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING); + else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE) + PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING); return; } @@ -4913,7 +5203,10 @@ inline static void TurnRoundExt(int x, int y) int ex = x + xy[i][0]; int ey = y + xy[i][1]; - if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN) + if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN || + Feld[ex][ey] == EL_EM_EXIT_OPEN || + Feld[ex][ey] == EL_STEEL_EXIT_OPEN || + Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN)) { attr_x = ex; attr_y = ey; @@ -5470,6 +5763,43 @@ void StartMoving(int x, int y) PlayLevelSoundAction(x, y, ACTION_FILLING); } } + else if (element == EL_QUICKSAND_FAST_FULL) + { + if (IS_FREE(x, y + 1)) + { + InitMovingField(x, y, MV_DOWN); + started_moving = TRUE; + + Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING; +#if USE_QUICKSAND_BD_ROCK_BUGFIX + if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK) + Store[x][y] = EL_ROCK; +#else + Store[x][y] = EL_ROCK; +#endif + + PlayLevelSoundAction(x, y, ACTION_EMPTYING); + } + else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY) + { + if (!MovDelay[x][y]) + MovDelay[x][y] = TILEY + 1; + + if (MovDelay[x][y]) + { + MovDelay[x][y]--; + if (MovDelay[x][y]) + return; + } + + Feld[x][y] = EL_QUICKSAND_FAST_EMPTY; + Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL; + Store[x][y + 1] = Store[x][y]; + Store[x][y] = 0; + + PlayLevelSoundAction(x, y, ACTION_FILLING); + } + } else if ((element == EL_ROCK || element == EL_BD_ROCK) && Feld[x][y + 1] == EL_QUICKSAND_EMPTY) { @@ -5481,6 +5811,17 @@ void StartMoving(int x, int y) PlayLevelSoundAction(x, y, ACTION_FILLING); } + else if ((element == EL_ROCK || element == EL_BD_ROCK) && + Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY) + { + InitMovingField(x, y, MV_DOWN); + started_moving = TRUE; + + Feld[x][y] = EL_QUICKSAND_FAST_FILLING; + Store[x][y] = element; + + PlayLevelSoundAction(x, y, ACTION_FILLING); + } else if (element == EL_MAGIC_WALL_FULL) { if (IS_FREE(x, y + 1)) @@ -5517,7 +5858,7 @@ void StartMoving(int x, int y) started_moving = TRUE; Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING; - Store[x][y] = EL_CHANGED2(Store[x][y]); + Store[x][y] = EL_CHANGED_BD(Store[x][y]); } else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE) { @@ -5533,20 +5874,52 @@ void StartMoving(int x, int y) Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE; Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL; - Store[x][y + 1] = EL_CHANGED2(Store[x][y]); + Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]); + Store[x][y] = 0; + } + } + else if (element == EL_DC_MAGIC_WALL_FULL) + { + if (IS_FREE(x, y + 1)) + { + InitMovingField(x, y, MV_DOWN); + started_moving = TRUE; + + Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING; + Store[x][y] = EL_CHANGED_DC(Store[x][y]); + } + else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE) + { + if (!MovDelay[x][y]) + MovDelay[x][y] = TILEY/4 + 1; + + if (MovDelay[x][y]) + { + MovDelay[x][y]--; + if (MovDelay[x][y]) + return; + } + + Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE; + Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL; + Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]); Store[x][y] = 0; } } - else if (CAN_PASS_MAGIC_WALL(element) && - (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE || - Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) + else if ((CAN_PASS_MAGIC_WALL(element) && + (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE || + Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) || + (CAN_PASS_DC_MAGIC_WALL(element) && + (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))) + { InitMovingField(x, y, MV_DOWN); started_moving = TRUE; Feld[x][y] = (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING : - EL_BD_MAGIC_WALL_FILLING); + Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING : + EL_DC_MAGIC_WALL_FILLING); Store[x][y] = element; } else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID) @@ -5558,9 +5931,14 @@ void StartMoving(int x, int y) Store[x][y] = EL_ACID; } - else if ((game.engine_version >= VERSION_IDENT(3,1,0,0) && + 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))) || @@ -5580,6 +5958,7 @@ void StartMoving(int x, int y) simply not covered here... :-/ ) */ CheckCollision[x][y] = 0; + CheckImpact[x][y] = 0; Impact(x, y); } @@ -5904,7 +6283,10 @@ void StartMoving(int x, int y) } else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy)) { - if (Feld[newx][newy] == EL_EXIT_OPEN) + if (Feld[newx][newy] == EL_EXIT_OPEN || + Feld[newx][newy] == EL_EM_EXIT_OPEN || + Feld[newx][newy] == EL_STEEL_EXIT_OPEN || + Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN) { RemoveField(x, y); DrawLevelField(x, y); @@ -6353,6 +6735,16 @@ void ContinueMoving(int x, int y) Feld[x][y] = get_next_element(element); element = Feld[newx][newy] = Store[x][y]; } + else if (element == EL_QUICKSAND_FAST_FILLING) + { + element = Feld[newx][newy] = get_next_element(element); + Store[newx][newy] = Store[x][y]; + } + else if (element == EL_QUICKSAND_FAST_EMPTYING) + { + Feld[x][y] = get_next_element(element); + element = Feld[newx][newy] = Store[x][y]; + } else if (element == EL_MAGIC_WALL_FILLING) { element = Feld[newx][newy] = get_next_element(element); @@ -6385,6 +6777,24 @@ 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) + { + element = Feld[newx][newy] = get_next_element(element); + if (!game.magic_wall_active) + element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD; + Store[newx][newy] = Store[x][y]; + } + else if (element == EL_DC_MAGIC_WALL_EMPTYING) + { + Feld[x][y] = get_next_element(element); + if (!game.magic_wall_active) + 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 @@ -6524,6 +6934,11 @@ 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 */ @@ -6591,7 +7006,7 @@ void ContinueMoving(int x, int y) if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY && IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element)) CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X, - MV_DIR_OPPOSITE(direction)); + MV_DIR_OPPOSITE(direction)); } int AmoebeNachbarNr(int ax, int ay) @@ -6883,7 +7298,8 @@ void AmoebeAbleger(int ax, int ay) if (IS_FREE(x, y) || CAN_GROW_INTO(Feld[x][y]) || - Feld[x][y] == EL_QUICKSAND_EMPTY) + Feld[x][y] == EL_QUICKSAND_EMPTY || + Feld[x][y] == EL_QUICKSAND_FAST_EMPTY) { newax = x; neway = y; @@ -6908,7 +7324,8 @@ void AmoebeAbleger(int ax, int ay) if (IS_FREE(x, y) || CAN_GROW_INTO(Feld[x][y]) || - Feld[x][y] == EL_QUICKSAND_EMPTY) + Feld[x][y] == EL_QUICKSAND_EMPTY || + Feld[x][y] == EL_QUICKSAND_FAST_EMPTY) { newax = x; neway = y; @@ -7093,7 +7510,7 @@ static void InitTimegateWheel(int x, int y) static void RunTimegateWheel(int x, int y) { - PlayLevelSound(x, y, SND_TIMEGATE_SWITCH_ACTIVE); + PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE); } static void InitMagicBallDelay(int x, int y) @@ -7160,9 +7577,78 @@ void CheckExit(int x, int y) PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING); } -void CheckExitSP(int x, int y) +void CheckExitEM(int x, int y) { - if (local_player->gems_still_needed > 0) + if (local_player->gems_still_needed > 0 || + local_player->sokobanfields_still_needed > 0 || + local_player->lights_still_needed > 0) + { + int element = Feld[x][y]; + int graphic = el2img(element); + + if (IS_ANIMATED(graphic)) + DrawLevelGraphicAnimationIfNeeded(x, y, graphic); + + return; + } + + if (AllPlayersGone) /* do not re-open exit door closed after last player */ + return; + + Feld[x][y] = EL_EM_EXIT_OPENING; + + PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING); +} + +void CheckExitSteel(int x, int y) +{ + if (local_player->gems_still_needed > 0 || + local_player->sokobanfields_still_needed > 0 || + local_player->lights_still_needed > 0) + { + int element = Feld[x][y]; + int graphic = el2img(element); + + if (IS_ANIMATED(graphic)) + DrawLevelGraphicAnimationIfNeeded(x, y, graphic); + + return; + } + + if (AllPlayersGone) /* do not re-open exit door closed after last player */ + return; + + Feld[x][y] = EL_STEEL_EXIT_OPENING; + + PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING); +} + +void CheckExitSteelEM(int x, int y) +{ + if (local_player->gems_still_needed > 0 || + local_player->sokobanfields_still_needed > 0 || + local_player->lights_still_needed > 0) + { + int element = Feld[x][y]; + int graphic = el2img(element); + + if (IS_ANIMATED(graphic)) + DrawLevelGraphicAnimationIfNeeded(x, y, graphic); + + return; + } + + if (AllPlayersGone) /* do not re-open exit door closed after last player */ + return; + + Feld[x][y] = EL_EM_STEEL_EXIT_OPENING; + + PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING); +} + +void CheckExitSP(int x, int y) +{ + if (local_player->gems_still_needed > 0) { int element = Feld[x][y]; int graphic = el2img(element); @@ -7198,7 +7684,7 @@ static void CloseAllOpenTimegates() } } -void EdelsteinFunkeln(int x, int y) +void DrawTwinkleOnField(int x, int y) { if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y)) return; @@ -7207,7 +7693,7 @@ void EdelsteinFunkeln(int x, int y) return; if (MovDelay[x][y] == 0) /* next animation frame */ - MovDelay[x][y] = 11 * !SimpleRND(500); + MovDelay[x][y] = 11 * !GetSimpleRandom(500); if (MovDelay[x][y] != 0) /* wait some time before next frame */ { @@ -7398,6 +7884,108 @@ void MauerAbleger(int ax, int ay) PlayLevelSoundAction(ax, ay, ACTION_GROWING); } +void MauerAblegerStahl(int ax, int ay) +{ + int element = Feld[ax][ay]; + int graphic = el2img(element); + boolean oben_frei = FALSE, unten_frei = FALSE; + boolean links_frei = FALSE, rechts_frei = FALSE; + boolean oben_massiv = FALSE, unten_massiv = FALSE; + boolean links_massiv = FALSE, rechts_massiv = FALSE; + boolean new_wall = FALSE; + + if (IS_ANIMATED(graphic)) + DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic); + + if (!MovDelay[ax][ay]) /* start building new wall */ + MovDelay[ax][ay] = 6; + + if (MovDelay[ax][ay]) /* wait some time before building new wall */ + { + MovDelay[ax][ay]--; + if (MovDelay[ax][ay]) + return; + } + + if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1)) + oben_frei = TRUE; + if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1)) + unten_frei = TRUE; + if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay)) + links_frei = TRUE; + if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay)) + rechts_frei = TRUE; + + if (element == EL_EXPANDABLE_STEELWALL_VERTICAL || + element == EL_EXPANDABLE_STEELWALL_ANY) + { + if (oben_frei) + { + Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING; + Store[ax][ay-1] = element; + GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP; + if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1))) + DrawGraphic(SCREENX(ax), SCREENY(ay - 1), + IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0); + new_wall = TRUE; + } + if (unten_frei) + { + Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING; + Store[ax][ay+1] = element; + GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN; + if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1))) + DrawGraphic(SCREENX(ax), SCREENY(ay + 1), + IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0); + new_wall = TRUE; + } + } + + if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL || + element == EL_EXPANDABLE_STEELWALL_ANY) + { + if (links_frei) + { + Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING; + Store[ax-1][ay] = element; + GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT; + if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay))) + DrawGraphic(SCREENX(ax - 1), SCREENY(ay), + IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0); + new_wall = TRUE; + } + + if (rechts_frei) + { + Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING; + Store[ax+1][ay] = element; + GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT; + if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay))) + DrawGraphic(SCREENX(ax + 1), SCREENY(ay), + IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0); + new_wall = TRUE; + } + } + + if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1])) + oben_massiv = TRUE; + if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1])) + unten_massiv = TRUE; + if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay])) + links_massiv = TRUE; + if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay])) + rechts_massiv = TRUE; + + if (((oben_massiv && unten_massiv) || + element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) && + ((links_massiv && rechts_massiv) || + element == EL_EXPANDABLE_STEELWALL_VERTICAL)) + Feld[ax][ay] = EL_WALL; + + if (new_wall) + PlayLevelSoundAction(ax, ay, ACTION_GROWING); +} + void CheckForDragon(int x, int y) { int i, j; @@ -7641,7 +8229,7 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page) /* ---------- execute action -------------------------------------------- */ - switch(action_type) + switch (action_type) { case CA_NO_ACTION: { @@ -7869,6 +8457,11 @@ 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; SetPlayerWaiting(&stored_player[i], FALSE); @@ -7976,6 +8569,7 @@ static void CreateFieldExt(int x, int y, int element, boolean is_change) #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 @@ -8039,6 +8633,15 @@ static void CreateFieldExt(int x, int y, int element, boolean is_change) /* 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)) + { + Bang(x, y); + + return; + } +#else if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) && IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element)) { @@ -8046,6 +8649,7 @@ static void CreateFieldExt(int x, int y, int element, boolean is_change) return; } +#endif #endif /* "ChangeCount" not set yet to allow "entered by player" change one time */ @@ -8442,6 +9046,14 @@ 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++) { int element = EL_CUSTOM_START + i; @@ -8510,6 +9122,8 @@ static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y, } } + RECURSION_LOOP_DETECTION_END(); + return change_done_any; } @@ -8548,14 +9162,30 @@ static boolean CheckElementChangeExt(int x, int y, 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); + for (p = 0; p < element_info[element].num_change_pages; p++) { struct ElementChangeInfo *change = &element_info[element].change_page[p]; + /* check trigger element for all events where the element that is checked + for changing interacts with a directly adjacent element -- this is + different to element changes that affect other elements to change on the + whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */ boolean check_trigger_element = (trigger_event == CE_TOUCHING_X || trigger_event == CE_HITTING_X || - trigger_event == CE_HIT_BY_X); + trigger_event == CE_HIT_BY_X || +#if 1 + /* this one was forgotten until 3.2.3 */ + trigger_event == CE_DIGGING_X); +#endif if (change->can_change_or_has_action && change->has_event[trigger_event] && @@ -8619,6 +9249,8 @@ static boolean CheckElementChangeExt(int x, int y, } } + RECURSION_LOOP_DETECTION_END(); + return change_done; } @@ -8672,11 +9304,11 @@ static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting) player->frame_counter_bored = FrameCounter + game.player_boring_delay_fixed + - SimpleRND(game.player_boring_delay_random); + GetSimpleRandom(game.player_boring_delay_random); player->frame_counter_sleeping = FrameCounter + game.player_sleeping_delay_fixed + - SimpleRND(game.player_sleeping_delay_random); + GetSimpleRandom(game.player_sleeping_delay_random); InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir); } @@ -8732,10 +9364,10 @@ static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting) player->anim_delay_counter = graphic_info[special_graphic].anim_delay_fixed + - SimpleRND(graphic_info[special_graphic].anim_delay_random); + GetSimpleRandom(graphic_info[special_graphic].anim_delay_random); player->post_delay_counter = graphic_info[special_graphic].post_delay_fixed + - SimpleRND(graphic_info[special_graphic].post_delay_random); + GetSimpleRandom(graphic_info[special_graphic].post_delay_random); player->special_action_sleeping = special_action; } @@ -8758,16 +9390,16 @@ static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting) if (player->anim_delay_counter == 0 && player->post_delay_counter == 0) { int special_action = - ACTION_BORING_1 + SimpleRND(player->num_special_action_bored); + ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored); int special_graphic = el_act_dir2img(player->artwork_element, special_action, move_dir); player->anim_delay_counter = graphic_info[special_graphic].anim_delay_fixed + - SimpleRND(graphic_info[special_graphic].anim_delay_random); + GetSimpleRandom(graphic_info[special_graphic].anim_delay_random); player->post_delay_counter = graphic_info[special_graphic].post_delay_fixed + - SimpleRND(graphic_info[special_graphic].post_delay_random); + GetSimpleRandom(graphic_info[special_graphic].post_delay_random); player->special_action_bored = special_action; } @@ -9020,6 +9652,25 @@ void GameActions() byte tape_action[MAX_PLAYERS]; int i; + /* detect endless loops, caused by custom element programming */ + if (recursion_loop_detected && recursion_loop_depth == 0) + { + char *message = getStringCat3("Internal Error ! Element ", + EL_NAME(recursion_loop_element), + " caused endless loop ! Quit the game ?"); + + Error(ERR_WARN, "element '%s' caused endless loop in game engine", + EL_NAME(recursion_loop_element)); + + RequestQuitGameExt(FALSE, level_editor_test_game, message); + + recursion_loop_detected = FALSE; /* if game should be continued */ + + free(message); + + return; + } + if (game.restart_level) StartGameActions(options.network, setup.autorecord, NEW_RANDOMIZE); @@ -9223,7 +9874,7 @@ void GameActions_RND() game.centered_player_nr = game.centered_player_nr_next; game.set_centered_player = FALSE; - DrawRelocateScreen(sx, sy, MV_NONE, TRUE, setup.quick_switch); + DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch); DrawGameDoorValues(); } @@ -9329,6 +9980,8 @@ void GameActions_RND() WasJustFalling[x][y]--; if (CheckCollision[x][y] > 0) CheckCollision[x][y]--; + if (CheckImpact[x][y] > 0) + CheckImpact[x][y]--; GfxFrame[x][y]++; @@ -9413,11 +10066,14 @@ void GameActions_RND() DrawLevelGraphicAnimationIfNeeded(x, y, graphic); if (IS_GEM(element) || element == EL_SP_INFOTRON) - EdelsteinFunkeln(x, y); + DrawTwinkleOnField(x, y); } else if ((element == EL_ACID || element == EL_EXIT_OPEN || + element == EL_EM_EXIT_OPEN || element == EL_SP_EXIT_OPEN || + element == EL_STEEL_EXIT_OPEN || + element == EL_EM_STEEL_EXIT_OPEN || element == EL_SP_TERMINAL || element == EL_SP_TERMINAL_ACTIVE || element == EL_EXTRA_TIME || @@ -9443,9 +10099,16 @@ void GameActions_RND() Life(x, y); else if (element == EL_EXIT_CLOSED) CheckExit(x, y); + else if (element == EL_EM_EXIT_CLOSED) + CheckExitEM(x, y); + else if (element == EL_STEEL_EXIT_CLOSED) + CheckExitSteel(x, y); + else if (element == EL_EM_STEEL_EXIT_CLOSED) + CheckExitSteelEM(x, y); else if (element == EL_SP_EXIT_CLOSED) CheckExitSP(x, y); - else if (element == EL_EXPANDABLE_WALL_GROWING) + else if (element == EL_EXPANDABLE_WALL_GROWING || + element == EL_EXPANDABLE_STEELWALL_GROWING) MauerWaechst(x, y); else if (element == EL_EXPANDABLE_WALL || element == EL_EXPANDABLE_WALL_HORIZONTAL || @@ -9453,6 +10116,10 @@ void GameActions_RND() element == EL_EXPANDABLE_WALL_ANY || element == EL_BD_EXPANDABLE_WALL) MauerAbleger(x, y); + else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL || + element == EL_EXPANDABLE_STEELWALL_VERTICAL || + element == EL_EXPANDABLE_STEELWALL_ANY) + MauerAblegerStahl(x, y); else if (element == EL_FLAMES) CheckForDragon(x, y); else if (element == EL_EXPLOSION) @@ -9481,7 +10148,10 @@ void GameActions_RND() element == EL_MAGIC_WALL_EMPTYING || element == EL_BD_MAGIC_WALL_FULL || element == EL_BD_MAGIC_WALL_ACTIVE || - element == EL_BD_MAGIC_WALL_EMPTYING) && + element == EL_BD_MAGIC_WALL_EMPTYING || + element == EL_DC_MAGIC_WALL_FULL || + element == EL_DC_MAGIC_WALL_ACTIVE || + element == EL_DC_MAGIC_WALL_EMPTYING) && ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy)) { magic_wall_x = x; @@ -9506,6 +10176,7 @@ void GameActions_RND() (element == EL_EMPTY || CAN_GROW_INTO(element) || element == EL_QUICKSAND_EMPTY || + element == EL_QUICKSAND_FAST_EMPTY || element == EL_ACID_SPLASH_LEFT || element == EL_ACID_SPLASH_RIGHT)) { @@ -9552,6 +10223,10 @@ void GameActions_RND() element == EL_BD_MAGIC_WALL_ACTIVE || element == EL_BD_MAGIC_WALL_EMPTYING) PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE); + else if (element == EL_DC_MAGIC_WALL_FULL || + element == EL_DC_MAGIC_WALL_ACTIVE || + element == EL_DC_MAGIC_WALL_EMPTYING) + PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE); else PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE); } @@ -9577,6 +10252,12 @@ void GameActions_RND() Feld[x][y] = EL_BD_MAGIC_WALL_DEAD; DrawLevelField(x, y); } + else if (element == EL_DC_MAGIC_WALL_ACTIVE || + element == EL_DC_MAGIC_WALL_FULL) + { + Feld[x][y] = EL_DC_MAGIC_WALL_DEAD; + DrawLevelField(x, y); + } } game.magic_wall_active = FALSE; @@ -10265,6 +10946,9 @@ void ScrollPlayer(struct PlayerInfo *player, int mode) player->last_jy = jy; if (Feld[jx][jy] == EL_EXIT_OPEN || + Feld[jx][jy] == EL_EM_EXIT_OPEN || + Feld[jx][jy] == EL_STEEL_EXIT_OPEN || + Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN || Feld[jx][jy] == EL_SP_EXIT_OPEN || Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */ { @@ -10940,6 +11624,23 @@ void KillPlayer(struct PlayerInfo *player) if (!player->active) return; + /* the following code was introduced to prevent an infinite loop when calling + -> Bang() + -> CheckTriggeredElementChangeExt() + -> ExecuteCustomElementAction() + -> KillPlayer() + -> (infinitely repeating the above sequence of function calls) + which occurs when killing the player while having a CE with the setting + "kill player X when explosion of "; the solution using a new + field "player->killed" was chosen for backwards compatibility, although + clever use of the fields "player->active" etc. would probably also work */ +#if 1 + if (player->killed) + return; +#endif + + player->killed = TRUE; + /* remove accessible field at the player's position */ Feld[jx][jy] = EL_EMPTY; @@ -11173,6 +11874,9 @@ int DigField(struct PlayerInfo *player, CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X, player->index_bit, dig_side); + if (element == EL_DC_LANDMINE) + Bang(x, y); + if (Feld[x][y] != element) /* field changed by snapping */ return MP_ACTION; @@ -11213,6 +11917,9 @@ int DigField(struct PlayerInfo *player, return MP_NO_ACTION; } else if (element == EL_EXIT_OPEN || + element == EL_EM_EXIT_OPEN || + element == EL_STEEL_EXIT_OPEN || + element == EL_EM_STEEL_EXIT_OPEN || element == EL_SP_EXIT_OPEN || element == EL_SP_EXIT_OPENING) { @@ -11268,6 +11975,15 @@ int DigField(struct PlayerInfo *player, if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)]) return MP_NO_ACTION; } + else if (element == EL_DC_GATE_WHITE || + element == EL_DC_GATE_WHITE_GRAY || + element == EL_DC_GATE_WHITE_GRAY_ACTIVE) + { + if (player->num_white_keys == 0) + return MP_NO_ACTION; + + player->num_white_keys--; + } else if (IS_SP_PORT(element)) { if (element == EL_SP_GRAVITY_PORT_LEFT || @@ -11394,6 +12110,13 @@ int DigField(struct PlayerInfo *player, DrawGameDoorValues(); } + else if (element == EL_DC_KEY_WHITE) + { + player->num_white_keys++; + + /* display white keys? */ + /* DrawGameDoorValues(); */ + } else if (IS_ENVELOPE(element)) { player->show_envelope = element; @@ -11629,7 +12352,9 @@ int DigField(struct PlayerInfo *player, ToggleBeltSwitch(x, y); } else if (element == EL_SWITCHGATE_SWITCH_UP || - element == EL_SWITCHGATE_SWITCH_DOWN) + element == EL_SWITCHGATE_SWITCH_DOWN || + element == EL_DC_SWITCHGATE_SWITCH_UP || + element == EL_DC_SWITCHGATE_SWITCH_DOWN) { ToggleSwitchgateSwitch(x, y); } @@ -11638,7 +12363,8 @@ int DigField(struct PlayerInfo *player, { ToggleLightSwitch(x, y); } - else if (element == EL_TIMEGATE_SWITCH) + else if (element == EL_TIMEGATE_SWITCH || + element == EL_DC_TIMEGATE_SWITCH) { ActivateTimegateSwitch(x, y); } @@ -11965,7 +12691,13 @@ boolean DropElement(struct PlayerInfo *player) 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); @@ -12292,7 +13024,7 @@ void RaiseScore(int value) void RaiseScoreElement(int element) { - switch(element) + switch (element) { case EL_EMERALD: case EL_BD_DIAMOND: @@ -12361,6 +13093,7 @@ void RaiseScoreElement(int element) case EL_EMC_KEY_6: case EL_EMC_KEY_7: case EL_EMC_KEY_8: + case EL_DC_KEY_WHITE: RaiseScore(level.score[SC_KEY]); break; default: @@ -12369,13 +13102,9 @@ void RaiseScoreElement(int element) } } -void RequestQuitGame(boolean ask_if_really_quit) +void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message) { - if (AllPlayersGone || - !ask_if_really_quit || - level_editor_test_game || - Request("Do you really want to quit the game ?", - REQ_ASK | REQ_STAY_CLOSED)) + if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED)) { #if defined(NETWORK_AVALIABLE) if (options.network) @@ -12383,7 +13112,7 @@ void RequestQuitGame(boolean ask_if_really_quit) else #endif { - if (!ask_if_really_quit || level_editor_test_game) + if (quick_quit) { game_status = GAME_MODE_MAIN; @@ -12399,7 +13128,7 @@ void RequestQuitGame(boolean ask_if_really_quit) } } } - else + else /* continue playing the game */ { if (tape.playing && tape.deactivate_display) TapeDeactivateDisplayOff(TRUE); @@ -12411,6 +13140,325 @@ void RequestQuitGame(boolean ask_if_really_quit) } } +void RequestQuitGame(boolean ask_if_really_quit) +{ + boolean quick_quit = (!ask_if_really_quit || level_editor_test_game); + boolean skip_request = AllPlayersGone || quick_quit; + + RequestQuitGameExt(skip_request, quick_quit, + "Do you really want to quit the game ?"); +} + + +/* ------------------------------------------------------------------------- */ +/* random generator functions */ +/* ------------------------------------------------------------------------- */ + +unsigned int InitEngineRandom_RND(long 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) +{ + if (max > 0) + { + game.num_random_calls++; + + return GetEngineRandom(max); + } + + return 0; +} + + +/* ------------------------------------------------------------------------- */ +/* game engine snapshot handling functions */ +/* ------------------------------------------------------------------------- */ + +#define ARGS_ADDRESS_AND_SIZEOF(x) (&(x)), (sizeof(x)) + +struct EngineSnapshotInfo +{ + /* runtime values for custom element collect score */ + int collect_score[NUM_CUSTOM_ELEMENTS]; + + /* runtime values for group element choice position */ + int choice_pos[NUM_GROUP_ELEMENTS]; + + /* runtime values for belt position animations */ + int belt_graphic[4 * NUM_BELT_PARTS]; + int belt_anim_mode[4 * NUM_BELT_PARTS]; +}; + +struct EngineSnapshotNodeInfo +{ + void *buffer_orig; + void *buffer_copy; + int size; +}; + +static struct EngineSnapshotInfo engine_snapshot_rnd; +static ListNode *engine_snapshot_list = NULL; +static char *snapshot_level_identifier = NULL; +static int snapshot_level_nr = -1; + +void FreeEngineSnapshot() +{ + while (engine_snapshot_list != NULL) + deleteNodeFromList(&engine_snapshot_list, engine_snapshot_list->key, + checked_free); + + setString(&snapshot_level_identifier, NULL); + snapshot_level_nr = -1; +} + +static void SaveEngineSnapshotValues_RND() +{ + static int belt_base_active_element[4] = + { + EL_CONVEYOR_BELT_1_LEFT_ACTIVE, + EL_CONVEYOR_BELT_2_LEFT_ACTIVE, + EL_CONVEYOR_BELT_3_LEFT_ACTIVE, + EL_CONVEYOR_BELT_4_LEFT_ACTIVE + }; + int i, j; + + for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++) + { + int element = EL_CUSTOM_START + i; + + engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score; + } + + for (i = 0; i < NUM_GROUP_ELEMENTS; i++) + { + int element = EL_GROUP_START + i; + + engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos; + } + + for (i = 0; i < 4; i++) + { + for (j = 0; j < NUM_BELT_PARTS; j++) + { + int element = belt_base_active_element[i] + j; + int graphic = el2img(element); + int anim_mode = graphic_info[graphic].anim_mode; + + engine_snapshot_rnd.belt_graphic[i * 4 + j] = graphic; + engine_snapshot_rnd.belt_anim_mode[i * 4 + j] = anim_mode; + } + } +} + +static void LoadEngineSnapshotValues_RND() +{ + unsigned long num_random_calls = game.num_random_calls; + int i, j; + + for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++) + { + int element = EL_CUSTOM_START + i; + + element_info[element].collect_score = engine_snapshot_rnd.collect_score[i]; + } + + for (i = 0; i < NUM_GROUP_ELEMENTS; i++) + { + int element = EL_GROUP_START + i; + + element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i]; + } + + for (i = 0; i < 4; i++) + { + for (j = 0; j < NUM_BELT_PARTS; j++) + { + int graphic = engine_snapshot_rnd.belt_graphic[i * 4 + j]; + int anim_mode = engine_snapshot_rnd.belt_anim_mode[i * 4 + j]; + + graphic_info[graphic].anim_mode = anim_mode; + } + } + + if (level.game_engine_type == GAME_ENGINE_TYPE_RND) + { + InitRND(tape.random_seed); + for (i = 0; i < num_random_calls; i++) + RND(1); + } + + if (game.num_random_calls != num_random_calls) + { + Error(ERR_RETURN, "number of random calls out of sync"); + Error(ERR_RETURN, "number of random calls should be %d", num_random_calls); + Error(ERR_RETURN, "number of random calls is %d", game.num_random_calls); + Error(ERR_EXIT, "this should not happen -- please debug"); + } +} + +static void SaveEngineSnapshotBuffer(void *buffer, int size) +{ + struct EngineSnapshotNodeInfo *bi = + checked_calloc(sizeof(struct EngineSnapshotNodeInfo)); + + bi->buffer_orig = buffer; + bi->buffer_copy = checked_malloc(size); + bi->size = size; + + memcpy(bi->buffer_copy, buffer, size); + + addNodeToList(&engine_snapshot_list, NULL, bi); +} + +void SaveEngineSnapshot() +{ + FreeEngineSnapshot(); /* free previous snapshot, if needed */ + + if (level_editor_test_game) /* do not save snapshots from editor */ + return; + + /* copy some special values to a structure better suited for the snapshot */ + + SaveEngineSnapshotValues_RND(); + SaveEngineSnapshotValues_EM(); + + /* save values stored in special snapshot structure */ + + SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd)); + SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em)); + + /* 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; + +#if 0 + ListNode *node = engine_snapshot_list; + int num_bytes = 0; + + while (node != NULL) + { + num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size; + + node = node->next; + } + + printf("::: size of engine snapshot: %d bytes\n", num_bytes); +#endif +} + +static void LoadEngineSnapshotBuffer(struct EngineSnapshotNodeInfo *bi) +{ + memcpy(bi->buffer_orig, bi->buffer_copy, bi->size); +} + +void LoadEngineSnapshot() +{ + ListNode *node = engine_snapshot_list; + + if (engine_snapshot_list == NULL) + return; + + while (node != NULL) + { + LoadEngineSnapshotBuffer((struct EngineSnapshotNodeInfo *)node->content); + + node = node->next; + } + + /* restore special values from snapshot structure */ + + LoadEngineSnapshotValues_RND(); + LoadEngineSnapshotValues_EM(); +} + +boolean CheckEngineSnapshot() +{ + return (strEqual(snapshot_level_identifier, leveldir_current->identifier) && + snapshot_level_nr == level_nr); +} + /* ---------- new game button stuff ---------------------------------------- */