X-Git-Url: https://git.artsoft.org/?a=blobdiff_plain;f=src%2Fgame.c;h=e6e78cbf92b0c713ed52641422fb08e6c7c1254d;hb=b09d5604ce8b27c7fdff1ce3839fccd62f5b4bf6;hp=0d1830340a376487b5c2b7d6cd972b537a7060d7;hpb=064d7909aadd84f633fefcccee3c6f6eb44d47ff;p=rocksndiamonds.git diff --git a/src/game.c b/src/game.c index 0d183034..e6e78cbf 100644 --- a/src/game.c +++ b/src/game.c @@ -89,123 +89,126 @@ // game panel display and control definitions #define GAME_PANEL_LEVEL_NUMBER 0 #define GAME_PANEL_GEMS 1 -#define GAME_PANEL_INVENTORY_COUNT 2 -#define GAME_PANEL_INVENTORY_FIRST_1 3 -#define GAME_PANEL_INVENTORY_FIRST_2 4 -#define GAME_PANEL_INVENTORY_FIRST_3 5 -#define GAME_PANEL_INVENTORY_FIRST_4 6 -#define GAME_PANEL_INVENTORY_FIRST_5 7 -#define GAME_PANEL_INVENTORY_FIRST_6 8 -#define GAME_PANEL_INVENTORY_FIRST_7 9 -#define GAME_PANEL_INVENTORY_FIRST_8 10 -#define GAME_PANEL_INVENTORY_LAST_1 11 -#define GAME_PANEL_INVENTORY_LAST_2 12 -#define GAME_PANEL_INVENTORY_LAST_3 13 -#define GAME_PANEL_INVENTORY_LAST_4 14 -#define GAME_PANEL_INVENTORY_LAST_5 15 -#define GAME_PANEL_INVENTORY_LAST_6 16 -#define GAME_PANEL_INVENTORY_LAST_7 17 -#define GAME_PANEL_INVENTORY_LAST_8 18 -#define GAME_PANEL_KEY_1 19 -#define GAME_PANEL_KEY_2 20 -#define GAME_PANEL_KEY_3 21 -#define GAME_PANEL_KEY_4 22 -#define GAME_PANEL_KEY_5 23 -#define GAME_PANEL_KEY_6 24 -#define GAME_PANEL_KEY_7 25 -#define GAME_PANEL_KEY_8 26 -#define GAME_PANEL_KEY_WHITE 27 -#define GAME_PANEL_KEY_WHITE_COUNT 28 -#define GAME_PANEL_SCORE 29 -#define GAME_PANEL_HIGHSCORE 30 -#define GAME_PANEL_TIME 31 -#define GAME_PANEL_TIME_HH 32 -#define GAME_PANEL_TIME_MM 33 -#define GAME_PANEL_TIME_SS 34 -#define GAME_PANEL_TIME_ANIM 35 -#define GAME_PANEL_HEALTH 36 -#define GAME_PANEL_HEALTH_ANIM 37 -#define GAME_PANEL_FRAME 38 -#define GAME_PANEL_SHIELD_NORMAL 39 -#define GAME_PANEL_SHIELD_NORMAL_TIME 40 -#define GAME_PANEL_SHIELD_DEADLY 41 -#define GAME_PANEL_SHIELD_DEADLY_TIME 42 -#define GAME_PANEL_EXIT 43 -#define GAME_PANEL_EMC_MAGIC_BALL 44 -#define GAME_PANEL_EMC_MAGIC_BALL_SWITCH 45 -#define GAME_PANEL_LIGHT_SWITCH 46 -#define GAME_PANEL_LIGHT_SWITCH_TIME 47 -#define GAME_PANEL_TIMEGATE_SWITCH 48 -#define GAME_PANEL_TIMEGATE_SWITCH_TIME 49 -#define GAME_PANEL_SWITCHGATE_SWITCH 50 -#define GAME_PANEL_EMC_LENSES 51 -#define GAME_PANEL_EMC_LENSES_TIME 52 -#define GAME_PANEL_EMC_MAGNIFIER 53 -#define GAME_PANEL_EMC_MAGNIFIER_TIME 54 -#define GAME_PANEL_BALLOON_SWITCH 55 -#define GAME_PANEL_DYNABOMB_NUMBER 56 -#define GAME_PANEL_DYNABOMB_SIZE 57 -#define GAME_PANEL_DYNABOMB_POWER 58 -#define GAME_PANEL_PENGUINS 59 -#define GAME_PANEL_SOKOBAN_OBJECTS 60 -#define GAME_PANEL_SOKOBAN_FIELDS 61 -#define GAME_PANEL_ROBOT_WHEEL 62 -#define GAME_PANEL_CONVEYOR_BELT_1 63 -#define GAME_PANEL_CONVEYOR_BELT_2 64 -#define GAME_PANEL_CONVEYOR_BELT_3 65 -#define GAME_PANEL_CONVEYOR_BELT_4 66 -#define GAME_PANEL_CONVEYOR_BELT_1_SWITCH 67 -#define GAME_PANEL_CONVEYOR_BELT_2_SWITCH 68 -#define GAME_PANEL_CONVEYOR_BELT_3_SWITCH 69 -#define GAME_PANEL_CONVEYOR_BELT_4_SWITCH 70 -#define GAME_PANEL_MAGIC_WALL 71 -#define GAME_PANEL_MAGIC_WALL_TIME 72 -#define GAME_PANEL_GRAVITY_STATE 73 -#define GAME_PANEL_GRAPHIC_1 74 -#define GAME_PANEL_GRAPHIC_2 75 -#define GAME_PANEL_GRAPHIC_3 76 -#define GAME_PANEL_GRAPHIC_4 77 -#define GAME_PANEL_GRAPHIC_5 78 -#define GAME_PANEL_GRAPHIC_6 79 -#define GAME_PANEL_GRAPHIC_7 80 -#define GAME_PANEL_GRAPHIC_8 81 -#define GAME_PANEL_ELEMENT_1 82 -#define GAME_PANEL_ELEMENT_2 83 -#define GAME_PANEL_ELEMENT_3 84 -#define GAME_PANEL_ELEMENT_4 85 -#define GAME_PANEL_ELEMENT_5 86 -#define GAME_PANEL_ELEMENT_6 87 -#define GAME_PANEL_ELEMENT_7 88 -#define GAME_PANEL_ELEMENT_8 89 -#define GAME_PANEL_ELEMENT_COUNT_1 90 -#define GAME_PANEL_ELEMENT_COUNT_2 91 -#define GAME_PANEL_ELEMENT_COUNT_3 92 -#define GAME_PANEL_ELEMENT_COUNT_4 93 -#define GAME_PANEL_ELEMENT_COUNT_5 94 -#define GAME_PANEL_ELEMENT_COUNT_6 95 -#define GAME_PANEL_ELEMENT_COUNT_7 96 -#define GAME_PANEL_ELEMENT_COUNT_8 97 -#define GAME_PANEL_CE_SCORE_1 98 -#define GAME_PANEL_CE_SCORE_2 99 -#define GAME_PANEL_CE_SCORE_3 100 -#define GAME_PANEL_CE_SCORE_4 101 -#define GAME_PANEL_CE_SCORE_5 102 -#define GAME_PANEL_CE_SCORE_6 103 -#define GAME_PANEL_CE_SCORE_7 104 -#define GAME_PANEL_CE_SCORE_8 105 -#define GAME_PANEL_CE_SCORE_1_ELEMENT 106 -#define GAME_PANEL_CE_SCORE_2_ELEMENT 107 -#define GAME_PANEL_CE_SCORE_3_ELEMENT 108 -#define GAME_PANEL_CE_SCORE_4_ELEMENT 109 -#define GAME_PANEL_CE_SCORE_5_ELEMENT 110 -#define GAME_PANEL_CE_SCORE_6_ELEMENT 111 -#define GAME_PANEL_CE_SCORE_7_ELEMENT 112 -#define GAME_PANEL_CE_SCORE_8_ELEMENT 113 -#define GAME_PANEL_PLAYER_NAME 114 -#define GAME_PANEL_LEVEL_NAME 115 -#define GAME_PANEL_LEVEL_AUTHOR 116 - -#define NUM_GAME_PANEL_CONTROLS 117 +#define GAME_PANEL_GEMS_TOTAL 2 +#define GAME_PANEL_GEMS_COLLECTED 3 +#define GAME_PANEL_GEMS_SCORE 4 +#define GAME_PANEL_INVENTORY_COUNT 5 +#define GAME_PANEL_INVENTORY_FIRST_1 6 +#define GAME_PANEL_INVENTORY_FIRST_2 7 +#define GAME_PANEL_INVENTORY_FIRST_3 8 +#define GAME_PANEL_INVENTORY_FIRST_4 9 +#define GAME_PANEL_INVENTORY_FIRST_5 10 +#define GAME_PANEL_INVENTORY_FIRST_6 11 +#define GAME_PANEL_INVENTORY_FIRST_7 12 +#define GAME_PANEL_INVENTORY_FIRST_8 13 +#define GAME_PANEL_INVENTORY_LAST_1 14 +#define GAME_PANEL_INVENTORY_LAST_2 15 +#define GAME_PANEL_INVENTORY_LAST_3 16 +#define GAME_PANEL_INVENTORY_LAST_4 17 +#define GAME_PANEL_INVENTORY_LAST_5 18 +#define GAME_PANEL_INVENTORY_LAST_6 19 +#define GAME_PANEL_INVENTORY_LAST_7 20 +#define GAME_PANEL_INVENTORY_LAST_8 21 +#define GAME_PANEL_KEY_1 22 +#define GAME_PANEL_KEY_2 23 +#define GAME_PANEL_KEY_3 24 +#define GAME_PANEL_KEY_4 25 +#define GAME_PANEL_KEY_5 26 +#define GAME_PANEL_KEY_6 27 +#define GAME_PANEL_KEY_7 28 +#define GAME_PANEL_KEY_8 29 +#define GAME_PANEL_KEY_WHITE 30 +#define GAME_PANEL_KEY_WHITE_COUNT 31 +#define GAME_PANEL_SCORE 32 +#define GAME_PANEL_HIGHSCORE 33 +#define GAME_PANEL_TIME 34 +#define GAME_PANEL_TIME_HH 35 +#define GAME_PANEL_TIME_MM 36 +#define GAME_PANEL_TIME_SS 37 +#define GAME_PANEL_TIME_ANIM 38 +#define GAME_PANEL_HEALTH 39 +#define GAME_PANEL_HEALTH_ANIM 40 +#define GAME_PANEL_FRAME 41 +#define GAME_PANEL_SHIELD_NORMAL 42 +#define GAME_PANEL_SHIELD_NORMAL_TIME 43 +#define GAME_PANEL_SHIELD_DEADLY 44 +#define GAME_PANEL_SHIELD_DEADLY_TIME 45 +#define GAME_PANEL_EXIT 46 +#define GAME_PANEL_EMC_MAGIC_BALL 47 +#define GAME_PANEL_EMC_MAGIC_BALL_SWITCH 48 +#define GAME_PANEL_LIGHT_SWITCH 49 +#define GAME_PANEL_LIGHT_SWITCH_TIME 50 +#define GAME_PANEL_TIMEGATE_SWITCH 51 +#define GAME_PANEL_TIMEGATE_SWITCH_TIME 52 +#define GAME_PANEL_SWITCHGATE_SWITCH 53 +#define GAME_PANEL_EMC_LENSES 54 +#define GAME_PANEL_EMC_LENSES_TIME 55 +#define GAME_PANEL_EMC_MAGNIFIER 56 +#define GAME_PANEL_EMC_MAGNIFIER_TIME 57 +#define GAME_PANEL_BALLOON_SWITCH 58 +#define GAME_PANEL_DYNABOMB_NUMBER 59 +#define GAME_PANEL_DYNABOMB_SIZE 60 +#define GAME_PANEL_DYNABOMB_POWER 61 +#define GAME_PANEL_PENGUINS 62 +#define GAME_PANEL_SOKOBAN_OBJECTS 63 +#define GAME_PANEL_SOKOBAN_FIELDS 64 +#define GAME_PANEL_ROBOT_WHEEL 65 +#define GAME_PANEL_CONVEYOR_BELT_1 66 +#define GAME_PANEL_CONVEYOR_BELT_2 67 +#define GAME_PANEL_CONVEYOR_BELT_3 68 +#define GAME_PANEL_CONVEYOR_BELT_4 69 +#define GAME_PANEL_CONVEYOR_BELT_1_SWITCH 70 +#define GAME_PANEL_CONVEYOR_BELT_2_SWITCH 71 +#define GAME_PANEL_CONVEYOR_BELT_3_SWITCH 72 +#define GAME_PANEL_CONVEYOR_BELT_4_SWITCH 73 +#define GAME_PANEL_MAGIC_WALL 74 +#define GAME_PANEL_MAGIC_WALL_TIME 75 +#define GAME_PANEL_GRAVITY_STATE 76 +#define GAME_PANEL_GRAPHIC_1 77 +#define GAME_PANEL_GRAPHIC_2 78 +#define GAME_PANEL_GRAPHIC_3 79 +#define GAME_PANEL_GRAPHIC_4 80 +#define GAME_PANEL_GRAPHIC_5 81 +#define GAME_PANEL_GRAPHIC_6 82 +#define GAME_PANEL_GRAPHIC_7 83 +#define GAME_PANEL_GRAPHIC_8 84 +#define GAME_PANEL_ELEMENT_1 85 +#define GAME_PANEL_ELEMENT_2 86 +#define GAME_PANEL_ELEMENT_3 87 +#define GAME_PANEL_ELEMENT_4 88 +#define GAME_PANEL_ELEMENT_5 89 +#define GAME_PANEL_ELEMENT_6 90 +#define GAME_PANEL_ELEMENT_7 91 +#define GAME_PANEL_ELEMENT_8 92 +#define GAME_PANEL_ELEMENT_COUNT_1 93 +#define GAME_PANEL_ELEMENT_COUNT_2 94 +#define GAME_PANEL_ELEMENT_COUNT_3 95 +#define GAME_PANEL_ELEMENT_COUNT_4 96 +#define GAME_PANEL_ELEMENT_COUNT_5 97 +#define GAME_PANEL_ELEMENT_COUNT_6 98 +#define GAME_PANEL_ELEMENT_COUNT_7 99 +#define GAME_PANEL_ELEMENT_COUNT_8 100 +#define GAME_PANEL_CE_SCORE_1 101 +#define GAME_PANEL_CE_SCORE_2 102 +#define GAME_PANEL_CE_SCORE_3 103 +#define GAME_PANEL_CE_SCORE_4 104 +#define GAME_PANEL_CE_SCORE_5 105 +#define GAME_PANEL_CE_SCORE_6 106 +#define GAME_PANEL_CE_SCORE_7 107 +#define GAME_PANEL_CE_SCORE_8 108 +#define GAME_PANEL_CE_SCORE_1_ELEMENT 109 +#define GAME_PANEL_CE_SCORE_2_ELEMENT 110 +#define GAME_PANEL_CE_SCORE_3_ELEMENT 111 +#define GAME_PANEL_CE_SCORE_4_ELEMENT 112 +#define GAME_PANEL_CE_SCORE_5_ELEMENT 113 +#define GAME_PANEL_CE_SCORE_6_ELEMENT 114 +#define GAME_PANEL_CE_SCORE_7_ELEMENT 115 +#define GAME_PANEL_CE_SCORE_8_ELEMENT 116 +#define GAME_PANEL_PLAYER_NAME 117 +#define GAME_PANEL_LEVEL_NAME 118 +#define GAME_PANEL_LEVEL_AUTHOR 119 + +#define NUM_GAME_PANEL_CONTROLS 120 struct GamePanelOrderInfo { @@ -242,6 +245,21 @@ static struct GamePanelControlInfo game_panel_controls[] = &game.panel.gems, TYPE_INTEGER, }, + { + GAME_PANEL_GEMS_TOTAL, + &game.panel.gems_total, + TYPE_INTEGER, + }, + { + GAME_PANEL_GEMS_COLLECTED, + &game.panel.gems_collected, + TYPE_INTEGER, + }, + { + GAME_PANEL_GEMS_SCORE, + &game.panel.gems_score, + TYPE_INTEGER, + }, { GAME_PANEL_INVENTORY_COUNT, &game.panel.inventory_count, @@ -1017,19 +1035,22 @@ static struct GamePanelControlInfo game_panel_controls[] = #define GAME_CTRL_ID_SAVE 5 #define GAME_CTRL_ID_PAUSE2 6 #define GAME_CTRL_ID_LOAD 7 -#define GAME_CTRL_ID_PANEL_STOP 8 -#define GAME_CTRL_ID_PANEL_PAUSE 9 -#define GAME_CTRL_ID_PANEL_PLAY 10 -#define GAME_CTRL_ID_TOUCH_STOP 11 -#define GAME_CTRL_ID_TOUCH_PAUSE 12 -#define SOUND_CTRL_ID_MUSIC 13 -#define SOUND_CTRL_ID_LOOPS 14 -#define SOUND_CTRL_ID_SIMPLE 15 -#define SOUND_CTRL_ID_PANEL_MUSIC 16 -#define SOUND_CTRL_ID_PANEL_LOOPS 17 -#define SOUND_CTRL_ID_PANEL_SIMPLE 18 - -#define NUM_GAME_BUTTONS 19 +#define GAME_CTRL_ID_RESTART 8 +#define GAME_CTRL_ID_PANEL_STOP 9 +#define GAME_CTRL_ID_PANEL_PAUSE 10 +#define GAME_CTRL_ID_PANEL_PLAY 11 +#define GAME_CTRL_ID_PANEL_RESTART 12 +#define GAME_CTRL_ID_TOUCH_STOP 13 +#define GAME_CTRL_ID_TOUCH_PAUSE 14 +#define GAME_CTRL_ID_TOUCH_RESTART 15 +#define SOUND_CTRL_ID_MUSIC 16 +#define SOUND_CTRL_ID_LOOPS 17 +#define SOUND_CTRL_ID_SIMPLE 18 +#define SOUND_CTRL_ID_PANEL_MUSIC 19 +#define SOUND_CTRL_ID_PANEL_LOOPS 20 +#define SOUND_CTRL_ID_PANEL_SIMPLE 21 + +#define NUM_GAME_BUTTONS 22 // forward declaration for internal use @@ -2264,6 +2285,9 @@ static void UpdateGameControlValues(void) level.game_engine_type == GAME_ENGINE_TYPE_MM ? game_mm.kettles_still_needed : game.gems_still_needed); + int gems_total = level.gems_needed; + int gems_collected = gems_total - gems; + int gems_score = level.score[SC_EMERALD]; int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ? game_em.lev->gems_needed > 0 : level.game_engine_type == GAME_ENGINE_TYPE_SP ? @@ -2289,6 +2313,9 @@ static void UpdateGameControlValues(void) // used instead of "level_nr" (for network games) game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = levelset.level_nr; game_panel_controls[GAME_PANEL_GEMS].value = gems; + game_panel_controls[GAME_PANEL_GEMS_TOTAL].value = gems_total; + game_panel_controls[GAME_PANEL_GEMS_COLLECTED].value = gems_collected; + game_panel_controls[GAME_PANEL_GEMS_SCORE].value = gems_score; game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0; for (i = 0; i < MAX_NUM_KEYS; i++) @@ -3095,6 +3122,9 @@ static void InitGameEngine(void) game_em.use_single_button = (game.engine_version > VERSION_IDENT(4,0,0,2)); + game_em.use_push_delay = + (game.engine_version > VERSION_IDENT(4,3,7,1)); + game_em.use_snap_key_bug = (game.engine_version < VERSION_IDENT(4,0,1,0)); @@ -3279,6 +3309,8 @@ static void InitGameEngine(void) change->actual_trigger_side = CH_SIDE_NONE; change->actual_trigger_ce_value = 0; change->actual_trigger_ce_score = 0; + change->actual_trigger_x = -1; + change->actual_trigger_y = -1; } } @@ -3474,8 +3506,9 @@ static void InitGameEngine(void) level.game_engine_type == GAME_ENGINE_TYPE_EM && !setup.forced_scroll_delay ? 0 : setup.scroll_delay ? setup.scroll_delay_value : 0); - game.scroll_delay_value = - MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY); + if (game.forced_scroll_delay_value == -1) + 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++) @@ -3629,6 +3662,10 @@ void InitGame(void) // force restarting global animations displayed during game play RestartGlobalAnimsByStatus(GAME_MODE_PSEUDO_RESTARTING); + // this is required for "transforming" fade modes like cross-fading + // (else global animations will be stopped, but not restarted here) + SetAnimStatusBeforeFading(GAME_MODE_PSEUDO_RESTARTING); + SetGameStatus(GAME_MODE_PLAYING); } @@ -3830,6 +3867,8 @@ void InitGame(void) TimeFrames = 0; TimePlayed = 0; TimeLeft = level.time; + + TapeTimeFrames = 0; TapeTime = 0; ScreenMovDir = MV_NONE; @@ -3860,6 +3899,8 @@ void InitGame(void) game.LevelSolved_CountingScore = 0; game.LevelSolved_CountingHealth = 0; + game.RestartGameRequested = FALSE; + game.panel.active = TRUE; game.no_level_time_limit = (level.time == 0); @@ -3966,6 +4007,10 @@ void InitGame(void) InitBeltMovement(); + // required if level does not contain any "empty space" element + if (element_info[EL_EMPTY].use_gfx_element) + game.use_masked_elements = TRUE; + for (i = 0; i < MAX_PLAYERS; i++) { struct PlayerInfo *player = &stored_player[i]; @@ -4440,6 +4485,11 @@ void InitGame(void) scroll_y = SCROLL_POSITION_Y(local_player->jy); } + if (game.forced_scroll_x != ARG_UNDEFINED_VALUE) + scroll_x = game.forced_scroll_x; + if (game.forced_scroll_y != ARG_UNDEFINED_VALUE) + scroll_y = game.forced_scroll_y; + // !!! FIX THIS (START) !!! if (level.game_engine_type == GAME_ENGINE_TYPE_EM) { @@ -4530,9 +4580,7 @@ void InitGame(void) } game.restart_level = FALSE; - game.request_active = FALSE; - game.request_active_or_moving = FALSE; if (level.game_engine_type == GAME_ENGINE_TYPE_MM) InitGameActions_MM(); @@ -5670,14 +5718,47 @@ static void DrawRelocateScreen(int old_x, int old_y, int x, int y, { // relocation _without_ centering of screen - int center_scroll_x = SCROLL_POSITION_X(old_x); - int center_scroll_y = SCROLL_POSITION_Y(old_y); - int offset_x = x + (scroll_x - center_scroll_x); - int offset_y = y + (scroll_y - center_scroll_y); + // apply distance between old and new player position to scroll position + int shifted_scroll_x = scroll_x + (x - old_x); + int shifted_scroll_y = scroll_y + (y - old_y); + + // make sure that shifted scroll position does not scroll beyond screen + new_scroll_x = SCROLL_POSITION_X(shifted_scroll_x + MIDPOSX); + new_scroll_y = SCROLL_POSITION_Y(shifted_scroll_y + MIDPOSY); + + // special case for teleporting from one end of the playfield to the other + // (this kludge prevents the destination area to be shifted by half a tile + // against the source destination for even screen width or screen height; + // probably most useful when used with high "game.forced_scroll_delay_value" + // in combination with "game.forced_scroll_x" and "game.forced_scroll_y") + if (quick_relocation) + { + if (EVEN(SCR_FIELDX)) + { + // relocate (teleport) between left and right border (half or full) + if (scroll_x == SBX_Left && new_scroll_x == SBX_Right - 1) + new_scroll_x = SBX_Right; + else if (scroll_x == SBX_Left + 1 && new_scroll_x == SBX_Right) + new_scroll_x = SBX_Right - 1; + else if (scroll_x == SBX_Right && new_scroll_x == SBX_Left + 1) + new_scroll_x = SBX_Left; + else if (scroll_x == SBX_Right - 1 && new_scroll_x == SBX_Left) + new_scroll_x = SBX_Left + 1; + } - // for new screen position, apply previous offset to center position - new_scroll_x = SCROLL_POSITION_X(offset_x); - new_scroll_y = SCROLL_POSITION_Y(offset_y); + if (EVEN(SCR_FIELDY)) + { + // relocate (teleport) between top and bottom border (half or full) + if (scroll_y == SBY_Upper && new_scroll_y == SBY_Lower - 1) + new_scroll_y = SBY_Lower; + else if (scroll_y == SBY_Upper + 1 && new_scroll_y == SBY_Lower) + new_scroll_y = SBY_Lower - 1; + else if (scroll_y == SBY_Lower && new_scroll_y == SBY_Upper + 1) + new_scroll_y = SBY_Upper; + else if (scroll_y == SBY_Lower - 1 && new_scroll_y == SBY_Upper) + new_scroll_y = SBY_Upper + 1; + } + } } if (quick_relocation) @@ -9393,7 +9474,7 @@ static void Life(int ax, int ay) for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++) { - int xx = ax+x1, yy = ay+y1; + int xx = ax + x1, yy = ay + y1; int old_element = Tile[xx][yy]; int num_neighbours = 0; @@ -9402,7 +9483,7 @@ static void Life(int ax, int ay) for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++) { - int x = xx+x2, y = yy+y2; + int x = xx + x2, y = yy + y2; if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy)) continue; @@ -10637,17 +10718,26 @@ static void CreateFieldExt(int x, int y, int element, boolean is_change) if (GFX_CRUMBLED(new_element)) TEST_DrawLevelFieldCrumbledNeighbours(x, y); - } - // 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 (IS_PLAYER(x, y) && !player_explosion_protected && - IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element)) - { - Bang(x, y); + if (old_element == EL_EXPLOSION) + { + Store[x][y] = Store2[x][y] = 0; - return; + // check if new element replaces an exploding player, requiring cleanup + if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present) + StorePlayer[x][y] = 0; + } + + // 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 (IS_PLAYER(x, y) && !player_explosion_protected && + IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element)) + { + KillPlayer(PLAYERINFO(x, y)); + + return; + } } // "ChangeCount" not set yet to allow "entered by player" change one time @@ -10707,6 +10797,8 @@ static boolean ChangeElement(int x, int y, int element, int page) change->actual_trigger_side = CH_SIDE_NONE; change->actual_trigger_ce_value = 0; change->actual_trigger_ce_score = 0; + change->actual_trigger_x = -1; + change->actual_trigger_y = -1; } // do not change elements more than a specified maximum number of changes @@ -10716,7 +10808,9 @@ static boolean ChangeElement(int x, int y, int element, int page) ChangeCount[x][y]++; // count number of changes in the same frame if (ei->has_anim_event) - HandleGlobalAnimEventByElementChange(element); + HandleGlobalAnimEventByElementChange(element, page, x, y, + change->actual_trigger_x, + change->actual_trigger_y); if (change->explode) { @@ -11046,6 +11140,8 @@ static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y, change->actual_trigger_side = trigger_side; change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y]; change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element); + change->actual_trigger_x = trigger_x; + change->actual_trigger_y = trigger_y; if ((change->can_change && !change_done) || change->has_action) { @@ -11160,6 +11256,8 @@ static boolean CheckElementChangeExt(int x, int y, change->actual_trigger_side = trigger_side; change->actual_trigger_ce_value = CustomValue[x][y]; change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element); + change->actual_trigger_x = x; + change->actual_trigger_y = y; // special case: trigger element not at (x,y) position for some events if (check_trigger_element) @@ -11183,6 +11281,8 @@ static boolean CheckElementChangeExt(int x, int y, change->actual_trigger_ce_value = CustomValue[xx][yy]; change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element); + change->actual_trigger_x = xx; + change->actual_trigger_y = yy; } if (change->can_change && !change_done) @@ -11605,7 +11705,6 @@ static void CheckLevelTime(void) if (TimeFrames >= FRAMES_PER_SECOND) { TimeFrames = 0; - TapeTime++; for (i = 0; i < MAX_PLAYERS; i++) { @@ -11652,6 +11751,12 @@ static void CheckLevelTime(void) game_em.lev->time = (game.no_level_time_limit ? TimePlayed : TimeLeft); } + } + + if (TapeTimeFrames >= FRAMES_PER_SECOND) + { + TapeTimeFrames = 0; + TapeTime++; if (tape.recording || tape.playing) DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime); @@ -11667,8 +11772,11 @@ void AdvanceFrameAndPlayerCounters(int player_nr) { int i; - // advance frame counters (global frame counter and time frame counter) + // advance frame counters (global frame counter and tape time frame counter) FrameCounter++; + TapeTimeFrames++; + + // advance time frame counter (used to control available time to solve level) TimeFrames++; // advance player counters (counters for move delay, move animation etc.) @@ -13461,8 +13569,9 @@ void TestIfPlayerTouchesCustomElement(int x, int y) incorrectly give EL_PLAYER_1 for "player->element_nr") */ int player_element = PLAYERINFO(x, y)->initial_element; + // as element "X" is the player here, check opposite (center) side CheckElementChangeBySide(xx, yy, border_element, player_element, - CE_TOUCHING_X, border_side); + CE_TOUCHING_X, center_side); } } else if (IS_PLAYER(xx, yy)) // player found at border element @@ -13488,8 +13597,9 @@ void TestIfPlayerTouchesCustomElement(int x, int y) incorrectly give EL_PLAYER_1 for "player->element_nr") */ int player_element = PLAYERINFO(xx, yy)->initial_element; + // as element "X" is the player here, check opposite (border) side CheckElementChangeBySide(x, y, center_element, player_element, - CE_TOUCHING_X, center_side); + CE_TOUCHING_X, border_side); } break; @@ -13596,7 +13706,7 @@ void TestIfElementTouchesCustomElement(int x, int y) CheckElementChangeBySide(xx, yy, border_element, center_element, CE_TOUCHING_X, center_side); - // (center element cannot be player, so we dont have to check this here) + // (center element cannot be player, so we don't have to check this here) } for (i = 0; i < NUM_DIRECTIONS; i++) @@ -13623,6 +13733,7 @@ void TestIfElementTouchesCustomElement(int x, int y) incorrectly give EL_PLAYER_1 for "player->element_nr") */ int player_element = PLAYERINFO(xx, yy)->initial_element; + // as element "X" is the player here, check opposite (border) side CheckElementChangeBySide(x, y, center_element, player_element, CE_TOUCHING_X, border_side); } @@ -15628,7 +15739,9 @@ void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message) } if (network.enabled) + { SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER); + } else { if (quick_quit) @@ -15730,10 +15843,14 @@ boolean CheckRestartGame(void) return FALSE; } - // do not handle game over if request dialog is already active + // do not ask to play again if request dialog is already active if (game.request_active) return FALSE; + // do not ask to play again if request dialog already handled + if (game.RestartGameRequested) + return FALSE; + // do not ask to play again if game was never actually played if (!game.GamePlayed) return FALSE; @@ -15742,6 +15859,8 @@ boolean CheckRestartGame(void) if (!setup.ask_on_game_over) return FALSE; + game.RestartGameRequested = TRUE; + RequestRestartGame(); return TRUE; @@ -15951,6 +16070,7 @@ static ListNode *SaveEngineSnapshotBuffers(void) 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(TapeTimeFrames)); SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime)); SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir)); @@ -16166,6 +16286,11 @@ static struct GAME_CTRL_ID_LOAD, NULL, TRUE, FALSE, "load game" }, + { + IMG_GFX_GAME_BUTTON_RESTART, &game.button.restart, + GAME_CTRL_ID_RESTART, NULL, + TRUE, FALSE, "restart game" + }, { IMG_GFX_GAME_BUTTON_PANEL_STOP, &game.button.panel_stop, GAME_CTRL_ID_PANEL_STOP, NULL, @@ -16181,6 +16306,11 @@ static struct GAME_CTRL_ID_PANEL_PLAY, NULL, FALSE, FALSE, "play game" }, + { + IMG_GFX_GAME_BUTTON_PANEL_RESTART, &game.button.panel_restart, + GAME_CTRL_ID_PANEL_RESTART, NULL, + FALSE, FALSE, "restart game" + }, { IMG_GFX_GAME_BUTTON_TOUCH_STOP, &game.button.touch_stop, GAME_CTRL_ID_TOUCH_STOP, NULL, @@ -16191,6 +16321,11 @@ static struct GAME_CTRL_ID_TOUCH_PAUSE, NULL, FALSE, TRUE, "pause game" }, + { + IMG_GFX_GAME_BUTTON_TOUCH_RESTART, &game.button.touch_restart, + GAME_CTRL_ID_TOUCH_RESTART, NULL, + FALSE, TRUE, "restart game" + }, { IMG_GFX_GAME_BUTTON_SOUND_MUSIC, &game.button.sound_music, SOUND_CTRL_ID_MUSIC, &setup.sound_music, @@ -16270,7 +16405,10 @@ void CreateGameButtons(void) id == GAME_CTRL_ID_PLAY || id == GAME_CTRL_ID_PANEL_PLAY || id == GAME_CTRL_ID_SAVE || - id == GAME_CTRL_ID_LOAD) + id == GAME_CTRL_ID_LOAD || + id == GAME_CTRL_ID_RESTART || + id == GAME_CTRL_ID_PANEL_RESTART || + id == GAME_CTRL_ID_TOUCH_RESTART) { button_type = GD_TYPE_NORMAL_BUTTON; checked = FALSE; @@ -16393,6 +16531,10 @@ void ModifyPauseButtons(void) }; int i; + // do not redraw pause button on closed door (may happen when restarting game) + if (!(GetDoorState() & DOOR_OPEN_1)) + return; + for (i = 0; ids[i] > -1; i++) ModifyGadget(game_gadget[ids[i]], GDI_CHECKED, tape.pausing, GDI_END); } @@ -16614,6 +16756,13 @@ static void HandleGameButtonsExt(int id, int button) TapeQuickLoad(); break; + case GAME_CTRL_ID_RESTART: + case GAME_CTRL_ID_PANEL_RESTART: + case GAME_CTRL_ID_TOUCH_RESTART: + TapeRestartGame(); + + break; + case SOUND_CTRL_ID_MUSIC: case SOUND_CTRL_ID_PANEL_MUSIC: if (setup.sound_music)