X-Git-Url: https://git.artsoft.org/?a=blobdiff_plain;f=src%2Fgame.c;h=47f3611731f7bf4bff3741f45681d2b1fb37b73d;hb=50e762bca8b7cbca1d0a12674b559e81a4f2c3f9;hp=4b7533ad779cabe27e85c770da5dae961eaf2378;hpb=892db4c42f305573f4ca35d93f34d295b7d239d6;p=rocksndiamonds.git diff --git a/src/game.c b/src/game.c index 4b7533ad..47f36117 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, @@ -962,7 +980,7 @@ static struct GamePanelControlInfo game_panel_controls[] = ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Tile[x][y] == EL_DIAMOND) #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y) \ - ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Tile[x][y])) + ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_FOOD_DARK_YAMYAM(Tile[x][y])) #define PACMAN_CAN_ENTER_FIELD(e, x, y) \ ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Tile[x][y])) @@ -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 @@ -1069,9 +1090,9 @@ static void HandleElementChange(int, int, int); static void ExecuteCustomElementAction(int, int, int, int); static boolean ChangeElement(int, int, int, int); -static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int); +static boolean CheckTriggeredElementChangeExt(int, int, int, int, int, int, int); #define CheckTriggeredElementChange(x, y, e, ev) \ - CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1) + CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, -1) #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s) \ CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1) #define CheckTriggeredElementChangeBySide(x, y, e, ev, s) \ @@ -1784,7 +1805,7 @@ static void InitPlayerField(int x, int y, int element, boolean init_game) player->active = TRUE; // remove potentially duplicate players - if (StorePlayer[jx][jy] == Tile[x][y]) + if (IN_LEV_FIELD(jx, jy) && StorePlayer[jx][jy] == Tile[x][y]) StorePlayer[jx][jy] = 0; StorePlayer[x][y] = Tile[x][y]; @@ -2241,6 +2262,8 @@ static void UpdateGameControlValues(void) int i, k; int time = (game.LevelSolved ? game.LevelSolved_CountingTime : + level.game_engine_type == GAME_ENGINE_TYPE_BD ? + game_bd.time_played : level.game_engine_type == GAME_ENGINE_TYPE_EM ? game_em.lev->time : level.game_engine_type == GAME_ENGINE_TYPE_SP ? @@ -2250,6 +2273,8 @@ static void UpdateGameControlValues(void) game.no_level_time_limit ? TimePlayed : TimeLeft); int score = (game.LevelSolved ? game.LevelSolved_CountingScore : + level.game_engine_type == GAME_ENGINE_TYPE_BD ? + game_bd.score : level.game_engine_type == GAME_ENGINE_TYPE_EM ? game_em.lev->score : level.game_engine_type == GAME_ENGINE_TYPE_SP ? @@ -2257,14 +2282,25 @@ static void UpdateGameControlValues(void) level.game_engine_type == GAME_ENGINE_TYPE_MM ? game_mm.score : game.score); - int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ? + int gems = (level.game_engine_type == GAME_ENGINE_TYPE_BD ? + game_bd.gems_still_needed : + level.game_engine_type == GAME_ENGINE_TYPE_EM ? game_em.lev->gems_needed : level.game_engine_type == GAME_ENGINE_TYPE_SP ? game_sp.infotrons_still_needed : level.game_engine_type == GAME_ENGINE_TYPE_MM ? game_mm.kettles_still_needed : game.gems_still_needed); - int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ? + int gems_total = level.gems_needed; + int gems_collected = (level.game_engine_type == GAME_ENGINE_TYPE_BD ? + game_bd.game->cave->diamonds_collected : + gems_total - gems); + int gems_score = (level.game_engine_type == GAME_ENGINE_TYPE_BD ? + game_bd.game->cave->diamond_value : + level.score[SC_EMERALD]); + int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_BD ? + game_bd.gems_still_needed > 0 : + level.game_engine_type == GAME_ENGINE_TYPE_EM ? game_em.lev->gems_needed > 0 : level.game_engine_type == GAME_ENGINE_TYPE_SP ? game_sp.infotrons_still_needed > 0 : @@ -2289,6 +2325,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++) @@ -2566,7 +2605,9 @@ static void UpdateGameControlValues(void) int element = gpc->value; int graphic = el2panelimg(element); int init_gfx_random = (graphic_info[graphic].anim_global_sync ? - sync_random_frame : INIT_GFX_RANDOM()); + sync_random_frame : + graphic_info[graphic].anim_global_anim_sync ? + getGlobalAnimSyncFrame() : INIT_GFX_RANDOM()); if (gpc->value != gpc->last_value) { @@ -2601,7 +2642,9 @@ static void UpdateGameControlValues(void) int last_anim_random_frame = gfx.anim_random_frame; int graphic = gpc->graphic; int init_gfx_random = (graphic_info[graphic].anim_global_sync ? - sync_random_frame : INIT_GFX_RANDOM()); + sync_random_frame : + graphic_info[graphic].anim_global_anim_sync ? + getGlobalAnimSyncFrame() : INIT_GFX_RANDOM()); if (gpc->value != gpc->last_value) { @@ -3091,6 +3134,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)); @@ -3191,6 +3237,17 @@ static void InitGameEngine(void) SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE); } + // ---------- initialize if element can trigger global animations ----------- + + for (i = 0; i < MAX_NUM_ELEMENTS; i++) + { + struct ElementInfo *ei = &element_info[i]; + + ei->has_anim_event = FALSE; + } + + InitGlobalAnimEventsForCustomElements(); + // ---------- initialize internal run-time variables ------------------------ for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++) @@ -3256,12 +3313,16 @@ static void InitGameEngine(void) for (j = 0; j < ei->num_change_pages; j++) { - ei->change_page[j].actual_trigger_element = EL_EMPTY; - ei->change_page[j].actual_trigger_player = EL_EMPTY; - ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE; - ei->change_page[j].actual_trigger_side = CH_SIDE_NONE; - ei->change_page[j].actual_trigger_ce_value = 0; - ei->change_page[j].actual_trigger_ce_score = 0; + struct ElementChangeInfo *change = &ei->change_page[j]; + + change->actual_trigger_element = EL_EMPTY; + change->actual_trigger_player = EL_EMPTY; + change->actual_trigger_player_bits = CH_PLAYER_NONE; + 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; } } @@ -3279,16 +3340,18 @@ static void InitGameEngine(void) for (j = 0; j < ei->num_change_pages; j++) { - if (!ei->change_page[j].can_change_or_has_action) + struct ElementChangeInfo *change = &ei->change_page[j]; + + if (!change->can_change_or_has_action) continue; - if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION]) + if (change->has_event[CE_BY_OTHER_ACTION]) { - int trigger_element = ei->change_page[j].trigger_element; + int trigger_element = change->trigger_element; for (k = 0; k < NUM_CHANGE_EVENTS; k++) { - if (ei->change_page[j].has_event[k]) + if (change->has_event[k]) { if (IS_GROUP_ELEMENT(trigger_element)) { @@ -3455,8 +3518,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++) @@ -3497,10 +3561,10 @@ static void InitGameEngine(void) { int element = EL_CUSTOM_START + i; - if (HAS_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) || - HAS_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE) || - HAS_CHANGE_EVENT(element, CE_MOUSE_CLICKED_ON_X) || - HAS_CHANGE_EVENT(element, CE_MOUSE_PRESSED_ON_X)) + if (HAS_ANY_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) || + HAS_ANY_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE) || + HAS_ANY_CHANGE_EVENT(element, CE_MOUSE_CLICKED_ON_X) || + HAS_ANY_CHANGE_EVENT(element, CE_MOUSE_PRESSED_ON_X)) game.use_mouse_actions = TRUE; } } @@ -3569,7 +3633,7 @@ void InitGame(void) 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 restarting = (game_status == GAME_MODE_PLAYING); boolean emulate_bd = TRUE; // unless non-BOULDERDASH elements found boolean emulate_sp = TRUE; // unless non-SUPAPLEX elements found int initial_move_dir = MV_DOWN; @@ -3581,7 +3645,15 @@ void InitGame(void) if (!game.restart_level) CloseDoor(DOOR_CLOSE_1); - SetGameStatus(GAME_MODE_PLAYING); + if (restarting) + { + // force fading out global animations displayed during game play + SetGameStatus(GAME_MODE_PSEUDO_RESTARTING); + } + else + { + SetGameStatus(GAME_MODE_PLAYING); + } if (level_editor_test_game) FadeSkipNextFadeOut(); @@ -3597,6 +3669,18 @@ void InitGame(void) FadeOut(fade_mask); + if (restarting) + { + // 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); + } + if (level_editor_test_game) FadeSkipNextFadeIn(); @@ -3795,6 +3879,8 @@ void InitGame(void) TimeFrames = 0; TimePlayed = 0; TimeLeft = level.time; + + TapeTimeFrames = 0; TapeTime = 0; ScreenMovDir = MV_NONE; @@ -3825,6 +3911,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); @@ -3931,6 +4019,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]; @@ -4405,8 +4497,17 @@ 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) + if (level.game_engine_type == GAME_ENGINE_TYPE_BD) + { + InitGameEngine_BD(); + } + else if (level.game_engine_type == GAME_ENGINE_TYPE_EM) { InitGameEngine_EM(); } @@ -4495,10 +4596,7 @@ void InitGame(void) } game.restart_level = FALSE; - game.restart_game_message = NULL; - game.request_active = FALSE; - game.request_active_or_moving = FALSE; if (level.game_engine_type == GAME_ENGINE_TYPE_MM) InitGameActions_MM(); @@ -4736,14 +4834,14 @@ void InitAmoebaNr(int x, int y) static void LevelSolved_SetFinalGameValues(void) { - game.time_final = (game.no_level_time_limit ? TimePlayed : TimeLeft); + game.time_final = (level.game_engine_type == GAME_ENGINE_TYPE_BD ? game_bd.time_played : + game.no_level_time_limit ? TimePlayed : TimeLeft); game.score_time_final = (level.use_step_counter ? TimePlayed : TimePlayed * FRAMES_PER_SECOND + TimeFrames); - game.score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ? - game_em.lev->score : - level.game_engine_type == GAME_ENGINE_TYPE_MM ? - game_mm.score : + game.score_final = (level.game_engine_type == GAME_ENGINE_TYPE_BD ? game_bd.score : + level.game_engine_type == GAME_ENGINE_TYPE_EM ? game_em.lev->score : + level.game_engine_type == GAME_ENGINE_TYPE_MM ? game_mm.score : game.score); game.health_final = (level.game_engine_type == GAME_ENGINE_TYPE_MM ? @@ -4777,6 +4875,8 @@ static void LevelSolved(void) game.LevelSolved = TRUE; game.GameOver = TRUE; + tape.solved = TRUE; + // needed here to display correct panel values while player walks into exit LevelSolved_SetFinalGameValues(); } @@ -4855,7 +4955,13 @@ void GameWon(void) time_count_steps = MAX(1, ABS(time_final - time) / 100); - if (level.game_engine_type == GAME_ENGINE_TYPE_MM) + if (level.game_engine_type == GAME_ENGINE_TYPE_BD) + { + // keep previous values (final values already processed here) + time_final = time; + score_final = score; + } + else if (level.game_engine_type == GAME_ENGINE_TYPE_MM) { health_final = 0; score_final += health * time_score; @@ -4866,7 +4972,8 @@ void GameWon(void) } // if not counting score after game, immediately update game panel values - if (level_editor_test_game || !setup.count_score_after_game) + if (level_editor_test_game || !setup.count_score_after_game || + level.game_engine_type == GAME_ENGINE_TYPE_BD) { time = time_final; score = score_final; @@ -5116,10 +5223,11 @@ static int addScoreEntry(struct ScoreInfo *list, struct ScoreEntry *new_entry, if (strEqual(new_entry->tape_basename, entry->tape_basename) && !strEqual(new_entry->tape_basename, UNDEFINED_FILENAME)) { - // special case: use server score instead of local score value if higher - // (historic scores might have been truncated to 16-bit values locally) - if (score_is_better) - entry->score = new_entry->score; + // add fields from server score entry not stored in local score entry + // (currently, this means setting platform, version and country fields; + // in rare cases, this may also correct an invalid score value, as + // historic scores might have been truncated to 16-bit values locally) + *entry = *new_entry; return -1; } @@ -5302,6 +5410,8 @@ static void ResetGfxFrame(int x, int y) if (graphic_info[graphic].anim_global_sync) GfxFrame[x][y] = FrameCounter; + else if (graphic_info[graphic].anim_global_anim_sync) + GfxFrame[x][y] = getGlobalAnimSyncFrame(); else if (ANIM_MODE(graphic) == ANIM_CE_VALUE) GfxFrame[x][y] = CustomValue[x][y]; else if (ANIM_MODE(graphic) == ANIM_CE_SCORE) @@ -5381,17 +5491,9 @@ void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y) void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y) { - int oldx = x, oldy = y; int direction = MovDir[x][y]; - - if (direction == MV_LEFT) - oldx++; - else if (direction == MV_RIGHT) - oldx--; - else if (direction == MV_UP) - oldy++; - else if (direction == MV_DOWN) - oldy--; + int oldx = x + (direction & MV_LEFT ? +1 : direction & MV_RIGHT ? -1 : 0); + int oldy = y + (direction & MV_UP ? +1 : direction & MV_DOWN ? -1 : 0); *comes_from_x = oldx; *comes_from_y = oldy; @@ -5406,16 +5508,17 @@ static int MovingOrBlocked2Element(int x, int y) int oldx, oldy; Blocked2Moving(x, y, &oldx, &oldy); + return Tile[oldx][oldy]; } - else - return element; + + return element; } static int MovingOrBlocked2ElementIfNotLeaving(int x, int y) { // like MovingOrBlocked2Element(), but if element is moving - // and (x,y) is the field the moving element is just leaving, + // and (x, y) is the field the moving element is just leaving, // return EL_BLOCKED instead of the element value int element = Tile[x][y]; @@ -5608,7 +5711,7 @@ static void setScreenCenteredToAllPlayers(int *sx, int *sy) *sy = (sy1 + sy2) / 2; } -static void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir, +static void DrawRelocateScreen(int old_x, int old_y, int x, int y, boolean center_screen, boolean quick_relocation) { unsigned int frame_delay_value_old = GetVideoFrameDelay(); @@ -5638,14 +5741,47 @@ static void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir, { // 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); - // 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); + // 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; + } + + 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) @@ -5779,7 +5915,7 @@ static void RelocatePlayer(int jx, int jy, int el_player_raw) } // only visually relocate centered player - DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir, + DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, FALSE, level.instant_relocation); TestIfPlayerTouchesBadThing(jx, jy); @@ -5812,9 +5948,6 @@ static void Explode(int ex, int ey, int phase, int mode) int last_phase; int border_element; - // !!! eliminate this variable !!! - int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2); - if (game.explosions_delayed) { ExplodeField[ex][ey] = mode; @@ -5824,6 +5957,8 @@ static void Explode(int ex, int ey, int phase, int mode) if (phase == EX_PHASE_START) // initialize 'Store[][]' field { int center_element = Tile[ex][ey]; + int ce_value = CustomValue[ex][ey]; + int ce_score = element_info[center_element].collect_score; int artwork_element, explosion_element; // set these values later // remove things displayed in background while burning dynamite @@ -5962,6 +6097,14 @@ static void Explode(int ex, int ey, int phase, int mode) else Store[x][y] = EL_EMPTY; + if (IS_CUSTOM_ELEMENT(center_element)) + Store[x][y] = (Store[x][y] == EL_CURRENT_CE_VALUE ? ce_value : + Store[x][y] == EL_CURRENT_CE_SCORE ? ce_score : + Store[x][y] >= EL_PREV_CE_8 && + Store[x][y] <= EL_NEXT_CE_8 ? + RESOLVED_REFERENCE_ELEMENT(center_element, Store[x][y]) : + Store[x][y]); + if (x != ex || y != ey || mode == EX_TYPE_BORDER || center_element == EL_AMOEBA_TO_DIAMOND) Store2[x][y] = element; @@ -6095,7 +6238,7 @@ static void Explode(int ex, int ey, int phase, int mode) int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING); int frame = getGraphicAnimationFrameXY(graphic, x, y); - if (phase == delay) + if (phase == 1) TEST_DrawLevelFieldCrumbled(x, y); if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY) @@ -6418,7 +6561,7 @@ static void ToggleBeltSwitch(int x, int y) } } -static void ToggleSwitchgateSwitch(int x, int y) +static void ToggleSwitchgateSwitch(void) { int xx, yy; @@ -6898,7 +7041,7 @@ static void Impact(int x, int y) smashed == EL_DC_SWITCHGATE_SWITCH_UP || smashed == EL_DC_SWITCHGATE_SWITCH_DOWN) { - ToggleSwitchgateSwitch(x, y + 1); + ToggleSwitchgateSwitch(); } else if (smashed == EL_LIGHT_SWITCH || smashed == EL_LIGHT_SWITCH_ACTIVE) @@ -7521,7 +7664,7 @@ static void TurnRoundExt(int x, int y) boolean can_turn_left = CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y); boolean can_turn_right = - CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y); + CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y); if (element_info[element].move_stepsize == 0) // "not moving" return; @@ -8351,7 +8494,7 @@ static void StartMoving(int x, int y) PlayLevelSound(newx, newy, SND_PENGUIN_PASSING); if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy))) - DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0); + DrawGraphicThruMask(SCREENX(newx), SCREENY(newy), el2img(element), 0); game.friends_still_needed--; if (!game.friends_still_needed && @@ -8363,7 +8506,7 @@ static void StartMoving(int x, int y) } else if (IS_FOOD_PENGUIN(Tile[newx][newy])) { - if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING) + if (DigField(local_player, x, y, newx, newy, 0, 0, DF_DIG) == MP_MOVING) TEST_DrawLevelField(newx, newy); else GfxDir[x][y] = MovDir[x][y] = MV_NONE; @@ -8668,7 +8811,7 @@ void ContinueMoving(int x, int y) if (pushed_by_player) // special case: moving object pushed by player { - MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos)); + MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x, y)->MovPos)); } else if (use_step_delay) // special case: moving object has step delay { @@ -8930,7 +9073,7 @@ void ContinueMoving(int x, int y) CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER, player->index_bit, push_side); - CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X, + CheckTriggeredElementChangeByPlayer(newx, newy, element, CE_PLAYER_PUSHES_X, player->index_bit, push_side); } @@ -9354,7 +9497,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; @@ -9363,7 +9506,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; @@ -9656,7 +9799,7 @@ static void DrawTwinkleOnField(int x, int y) } } -static void MauerWaechst(int x, int y) +static void WallGrowing(int x, int y) { int delay = 6; @@ -9706,15 +9849,59 @@ static void MauerWaechst(int x, int y) } } -static void MauerAbleger(int ax, int ay) +static void CheckWallGrowing(int ax, int ay) { int element = Tile[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; + boolean free_top = FALSE; + boolean free_bottom = FALSE; + boolean free_left = FALSE; + boolean free_right = FALSE; + boolean stop_top = FALSE; + boolean stop_bottom = FALSE; + boolean stop_left = FALSE; + boolean stop_right = FALSE; + boolean new_wall = FALSE; + + boolean is_steelwall = (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL || + element == EL_EXPANDABLE_STEELWALL_VERTICAL || + element == EL_EXPANDABLE_STEELWALL_ANY); + + boolean grow_vertical = (element == EL_EXPANDABLE_WALL_VERTICAL || + element == EL_EXPANDABLE_WALL_ANY || + element == EL_EXPANDABLE_STEELWALL_VERTICAL || + element == EL_EXPANDABLE_STEELWALL_ANY); + + boolean grow_horizontal = (element == EL_EXPANDABLE_WALL_HORIZONTAL || + element == EL_EXPANDABLE_WALL_ANY || + element == EL_EXPANDABLE_WALL || + element == EL_BD_EXPANDABLE_WALL || + element == EL_EXPANDABLE_STEELWALL_HORIZONTAL || + element == EL_EXPANDABLE_STEELWALL_ANY); + + boolean stop_vertical = (element == EL_EXPANDABLE_WALL_VERTICAL || + element == EL_EXPANDABLE_STEELWALL_VERTICAL); + + boolean stop_horizontal = (element == EL_EXPANDABLE_WALL_HORIZONTAL || + element == EL_EXPANDABLE_WALL || + element == EL_EXPANDABLE_STEELWALL_HORIZONTAL); + + int wall_growing = (is_steelwall ? + EL_EXPANDABLE_STEELWALL_GROWING : + EL_EXPANDABLE_WALL_GROWING); + + int gfx_wall_growing_up = (is_steelwall ? + IMG_EXPANDABLE_STEELWALL_GROWING_UP : + IMG_EXPANDABLE_WALL_GROWING_UP); + int gfx_wall_growing_down = (is_steelwall ? + IMG_EXPANDABLE_STEELWALL_GROWING_DOWN : + IMG_EXPANDABLE_WALL_GROWING_DOWN); + int gfx_wall_growing_left = (is_steelwall ? + IMG_EXPANDABLE_STEELWALL_GROWING_LEFT : + IMG_EXPANDABLE_WALL_GROWING_LEFT); + int gfx_wall_growing_right = (is_steelwall ? + IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT : + IMG_EXPANDABLE_WALL_GROWING_RIGHT); if (IS_ANIMATED(graphic)) DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic); @@ -9730,179 +9917,83 @@ static void MauerAbleger(int ax, int ay) } if (IN_LEV_FIELD(ax, ay - 1) && IS_FREE(ax, ay - 1)) - oben_frei = TRUE; + free_top = TRUE; if (IN_LEV_FIELD(ax, ay + 1) && IS_FREE(ax, ay + 1)) - unten_frei = TRUE; + free_bottom = TRUE; if (IN_LEV_FIELD(ax - 1, ay) && IS_FREE(ax - 1, ay)) - links_frei = TRUE; + free_left = TRUE; if (IN_LEV_FIELD(ax + 1, ay) && IS_FREE(ax + 1, ay)) - rechts_frei = TRUE; + free_right = TRUE; - if (element == EL_EXPANDABLE_WALL_VERTICAL || - element == EL_EXPANDABLE_WALL_ANY) + if (grow_vertical) { - if (oben_frei) + if (free_top) { - Tile[ax][ay - 1] = EL_EXPANDABLE_WALL_GROWING; + Tile[ax][ay - 1] = wall_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))) - DrawLevelGraphic(ax, ay - 1, IMG_EXPANDABLE_WALL_GROWING_UP, 0); - new_wall = TRUE; - } - if (unten_frei) - { - Tile[ax][ay + 1] = EL_EXPANDABLE_WALL_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))) - DrawLevelGraphic(ax, ay + 1, IMG_EXPANDABLE_WALL_GROWING_DOWN, 0); - new_wall = TRUE; - } - } - if (element == EL_EXPANDABLE_WALL_HORIZONTAL || - element == EL_EXPANDABLE_WALL_ANY || - element == EL_EXPANDABLE_WALL || - element == EL_BD_EXPANDABLE_WALL) - { - if (links_frei) - { - Tile[ax - 1][ay] = EL_EXPANDABLE_WALL_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))) - DrawLevelGraphic(ax - 1, ay, IMG_EXPANDABLE_WALL_GROWING_LEFT, 0); - new_wall = TRUE; - } + if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay - 1))) + DrawLevelGraphic(ax, ay - 1, gfx_wall_growing_up, 0); - if (rechts_frei) - { - Tile[ax + 1][ay] = EL_EXPANDABLE_WALL_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))) - DrawLevelGraphic(ax + 1, ay, IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0); new_wall = TRUE; } - } - - if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei)) - TEST_DrawLevelField(ax, ay); - - if (!IN_LEV_FIELD(ax, ay - 1) || IS_WALL(Tile[ax][ay - 1])) - oben_massiv = TRUE; - if (!IN_LEV_FIELD(ax, ay + 1) || IS_WALL(Tile[ax][ay + 1])) - unten_massiv = TRUE; - if (!IN_LEV_FIELD(ax - 1, ay) || IS_WALL(Tile[ax - 1][ay])) - links_massiv = TRUE; - if (!IN_LEV_FIELD(ax + 1, ay) || IS_WALL(Tile[ax + 1][ay])) - rechts_massiv = TRUE; - - if (((oben_massiv && unten_massiv) || - element == EL_EXPANDABLE_WALL_HORIZONTAL || - element == EL_EXPANDABLE_WALL) && - ((links_massiv && rechts_massiv) || - element == EL_EXPANDABLE_WALL_VERTICAL)) - Tile[ax][ay] = EL_WALL; - - if (new_wall) - PlayLevelSoundAction(ax, ay, ACTION_GROWING); -} - -static void MauerAblegerStahl(int ax, int ay) -{ - int element = Tile[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) + if (free_bottom) { - Tile[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))) - DrawLevelGraphic(ax, ay - 1, IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0); - new_wall = TRUE; - } - if (unten_frei) - { - Tile[ax][ay + 1] = EL_EXPANDABLE_STEELWALL_GROWING; + Tile[ax][ay + 1] = wall_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))) - DrawLevelGraphic(ax, ay + 1, IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0); + DrawLevelGraphic(ax, ay + 1, gfx_wall_growing_down, 0); + new_wall = TRUE; } } - if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL || - element == EL_EXPANDABLE_STEELWALL_ANY) + if (grow_horizontal) { - if (links_frei) + if (free_left) { - Tile[ax - 1][ay] = EL_EXPANDABLE_STEELWALL_GROWING; + Tile[ax - 1][ay] = wall_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))) - DrawLevelGraphic(ax - 1, ay, IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0); + DrawLevelGraphic(ax - 1, ay, gfx_wall_growing_left, 0); + new_wall = TRUE; } - if (rechts_frei) + if (free_right) { - Tile[ax + 1][ay] = EL_EXPANDABLE_STEELWALL_GROWING; + Tile[ax + 1][ay] = wall_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))) - DrawLevelGraphic(ax + 1, ay, IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0); + DrawLevelGraphic(ax + 1, ay, gfx_wall_growing_right, 0); + new_wall = TRUE; } } + if (element == EL_EXPANDABLE_WALL && (free_left || free_right)) + TEST_DrawLevelField(ax, ay); + if (!IN_LEV_FIELD(ax, ay - 1) || IS_WALL(Tile[ax][ay - 1])) - oben_massiv = TRUE; + stop_top = TRUE; if (!IN_LEV_FIELD(ax, ay + 1) || IS_WALL(Tile[ax][ay + 1])) - unten_massiv = TRUE; + stop_bottom = TRUE; if (!IN_LEV_FIELD(ax - 1, ay) || IS_WALL(Tile[ax - 1][ay])) - links_massiv = TRUE; + stop_left = TRUE; if (!IN_LEV_FIELD(ax + 1, ay) || IS_WALL(Tile[ax + 1][ay])) - rechts_massiv = TRUE; + stop_right = TRUE; - if (((oben_massiv && unten_massiv) || - element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) && - ((links_massiv && rechts_massiv) || - element == EL_EXPANDABLE_STEELWALL_VERTICAL)) - Tile[ax][ay] = EL_STEELWALL; + if (((stop_top && stop_bottom) || stop_horizontal) && + ((stop_left && stop_right) || stop_vertical)) + Tile[ax][ay] = (is_steelwall ? EL_STEELWALL : EL_WALL); if (new_wall) PlayLevelSoundAction(ax, ay, ACTION_GROWING); @@ -10650,17 +10741,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 @@ -10720,6 +10820,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 @@ -10728,6 +10830,11 @@ 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, page, x, y, + change->actual_trigger_x, + change->actual_trigger_y); + if (change->explode) { Bang(x, y); @@ -10962,13 +11069,14 @@ static void HandleElementChange(int x, int y, int page) if (ChangeDelay[x][y] != 0) // continue element change { - if (change->can_change) - { - int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]); + int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]); - if (IS_ANIMATED(graphic)) - DrawLevelGraphicAnimationIfNeeded(x, y, graphic); + // also needed if CE can not change, but has CE delay with CE action + if (IS_ANIMATED(graphic)) + DrawLevelGraphicAnimationIfNeeded(x, y, graphic); + if (change->can_change) + { if (change->change_function) change->change_function(x, y); } @@ -11055,6 +11163,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) { @@ -11169,6 +11279,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) @@ -11192,6 +11304,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) @@ -11531,7 +11645,22 @@ static void SetTapeActionFromMouseAction(byte *tape_action, static void CheckLevelSolved(void) { - if (level.game_engine_type == GAME_ENGINE_TYPE_EM) + if (level.game_engine_type == GAME_ENGINE_TYPE_BD) + { + if (game_bd.level_solved && + !game_bd.game_over) // game won + { + LevelSolved(); + + game_bd.game_over = TRUE; + + game.all_players_gone = TRUE; + } + + if (game_bd.game_over) // game lost + game.all_players_gone = TRUE; + } + else if (level.game_engine_type == GAME_ENGINE_TYPE_EM) { if (game_em.level_solved && !game_em.game_over) // game won @@ -11578,6 +11707,27 @@ static void CheckLevelSolved(void) } } +static void PlayTimeoutSound(int seconds_left) +{ + // will be played directly by BD engine (for classic bonus time sounds) + if (level.game_engine_type == GAME_ENGINE_TYPE_BD && checkBonusTime_BD()) + return; + + // try to use individual "running out of time" sound for each second left + int sound = SND_GAME_RUNNING_OUT_OF_TIME_0 - seconds_left; + + // if special sound per second not defined, use default sound + if (getSoundInfoEntryFilename(sound) == NULL) + sound = SND_GAME_RUNNING_OUT_OF_TIME; + + // if out of time, but player still alive, play special "timeout" sound, if defined + if (seconds_left == 0 && !checkGameFailed()) + if (getSoundInfoEntryFilename(SND_GAME_TIMEOUT) != NULL) + sound = SND_GAME_TIMEOUT; + + PlaySound(sound); +} + static void CheckLevelTime_StepCounter(void) { int i; @@ -11589,7 +11739,7 @@ static void CheckLevelTime_StepCounter(void) TimeLeft--; if (TimeLeft <= 10 && game.time_limit && !game.LevelSolved) - PlaySound(SND_GAME_RUNNING_OUT_OF_TIME); + PlayTimeoutSound(TimeLeft); game_panel_controls[GAME_PANEL_TIME].value = TimeLeft; @@ -11609,12 +11759,26 @@ static void CheckLevelTime_StepCounter(void) static void CheckLevelTime(void) { + int frames_per_second = FRAMES_PER_SECOND; int i; - if (TimeFrames >= FRAMES_PER_SECOND) + if (level.game_engine_type == GAME_ENGINE_TYPE_BD) + { + // level time may be running slower in native BD engine + frames_per_second = getFramesPerSecond_BD(); + + // if native engine time changed, force main engine time change + if (getTimeLeft_BD() < TimeLeft) + TimeFrames = frames_per_second; + + // if last second running, wait for native engine time to exactly reach zero + if (getTimeLeft_BD() == 1 && TimeLeft == 1) + TimeFrames = frames_per_second - 1; + } + + if (TimeFrames >= frames_per_second) { TimeFrames = 0; - TapeTime++; for (i = 0; i < MAX_PLAYERS; i++) { @@ -11638,7 +11802,7 @@ static void CheckLevelTime(void) TimeLeft--; if (TimeLeft <= 10 && game.time_limit) - PlaySound(SND_GAME_RUNNING_OUT_OF_TIME); + PlayTimeoutSound(TimeLeft); /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value is reset from other values in UpdateGameDoorValues() -- FIX THIS */ @@ -11647,11 +11811,20 @@ static void CheckLevelTime(void) if (!TimeLeft && game.time_limit) { - if (level.game_engine_type == GAME_ENGINE_TYPE_EM) + if (level.game_engine_type == GAME_ENGINE_TYPE_BD) + { + if (game_bd.game->cave->player_state == GD_PL_LIVING) + game_bd.game->cave->player_state = GD_PL_TIMEOUT; + } + else if (level.game_engine_type == GAME_ENGINE_TYPE_EM) + { game_em.lev->killed_out_of_time = TRUE; + } else + { for (i = 0; i < MAX_PLAYERS; i++) KillPlayer(&stored_player[i]); + } } } else if (game.no_level_time_limit && !game.all_players_gone) @@ -11661,6 +11834,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); @@ -11676,8 +11855,21 @@ void AdvanceFrameAndPlayerCounters(int player_nr) { int i; - // advance frame counters (global frame counter and time frame counter) + // handle game and tape time differently for native BD game engine + + // tape time is running in native BD engine even if player is not hatched yet + if (!checkGameRunning()) + return; + + // advance frame counters (global frame counter and tape time frame counter) FrameCounter++; + TapeTimeFrames++; + + // level time is running in native BD engine after player is being hatched + if (!checkGamePlaying()) + return; + + // advance time frame counter (used to control available time to solve level) TimeFrames++; // advance player counters (counters for move delay, move animation etc.) @@ -11721,6 +11913,64 @@ void AdvanceFrameAndPlayerCounters(int player_nr) } } +void AdvanceFrameCounter(void) +{ + FrameCounter++; +} + +void AdvanceGfxFrame(void) +{ + int x, y; + + SCAN_PLAYFIELD(x, y) + { + GfxFrame[x][y]++; + } +} + +static void HandleMouseAction(struct MouseActionInfo *mouse_action, + struct MouseActionInfo *mouse_action_last) +{ + if (mouse_action->button) + { + int new_button = (mouse_action->button && mouse_action_last->button == 0); + int ch_button = CH_SIDE_FROM_BUTTON(mouse_action->button); + int x = mouse_action->lx; + int y = mouse_action->ly; + int element = Tile[x][y]; + + if (new_button) + { + CheckElementChangeByMouse(x, y, element, CE_CLICKED_BY_MOUSE, ch_button); + CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_CLICKED_ON_X, + ch_button); + } + + CheckElementChangeByMouse(x, y, element, CE_PRESSED_BY_MOUSE, ch_button); + CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_PRESSED_ON_X, + ch_button); + + if (level.use_step_counter) + { + boolean counted_click = FALSE; + + // element clicked that can change when clicked/pressed + if (CAN_CHANGE_OR_HAS_ACTION(element) && + (HAS_ANY_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) || + HAS_ANY_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE))) + counted_click = TRUE; + + // element clicked that can trigger change when clicked/pressed + if (trigger_events[element][CE_MOUSE_CLICKED_ON_X] || + trigger_events[element][CE_MOUSE_PRESSED_ON_X]) + counted_click = TRUE; + + if (new_button && counted_click) + CheckLevelTime_StepCounter(); + } + } +} + void StartGameActions(boolean init_network_game, boolean record_tape, int random_seed) { @@ -11916,7 +12166,7 @@ static void GameActionsExt(void) TapeRecordAction(tape_action); // remember if game was played (especially after tape stopped playing) - if (!tape.playing && summarized_player_action) + if (!tape.playing && summarized_player_action && !checkGameFailed()) game.GamePlayed = TRUE; #if USE_NEW_PLAYER_ASSIGNMENTS @@ -11969,7 +12219,11 @@ static void GameActionsExt(void) game.snapshot.last_action[i] = stored_player[i].effective_action; } - if (level.game_engine_type == GAME_ENGINE_TYPE_EM) + if (level.game_engine_type == GAME_ENGINE_TYPE_BD) + { + GameActions_BD_Main(); + } + else if (level.game_engine_type == GAME_ENGINE_TYPE_EM) { GameActions_EM_Main(); } @@ -12036,28 +12290,37 @@ void GameActions(void) GameActions_CheckSaveEngineSnapshot(); } +void GameActions_BD_Main(void) +{ + byte effective_action[MAX_PLAYERS]; + int i; + + for (i = 0; i < MAX_PLAYERS; i++) + effective_action[i] = stored_player[i].effective_action; + + GameActions_BD(effective_action); +} + void GameActions_EM_Main(void) { byte effective_action[MAX_PLAYERS]; - boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing); int i; for (i = 0; i < MAX_PLAYERS; i++) effective_action[i] = stored_player[i].effective_action; - GameActions_EM(effective_action, warp_mode); + GameActions_EM(effective_action); } void GameActions_SP_Main(void) { byte effective_action[MAX_PLAYERS]; - boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing); int i; for (i = 0; i < MAX_PLAYERS; i++) effective_action[i] = stored_player[i].effective_action; - GameActions_SP(effective_action, warp_mode); + GameActions_SP(effective_action); for (i = 0; i < MAX_PLAYERS; i++) { @@ -12070,9 +12333,9 @@ void GameActions_SP_Main(void) void GameActions_MM_Main(void) { - boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing); + AdvanceGfxFrame(); - GameActions_MM(local_player->effective_mouse_action, warp_mode); + GameActions_MM(local_player->effective_mouse_action); } void GameActions_RND_Main(void) @@ -12136,7 +12399,7 @@ void GameActions_RND(void) game.centered_player_nr = game.centered_player_nr_next; game.set_centered_player = FALSE; - DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch); + DrawRelocateScreen(0, 0, sx, sy, TRUE, setup.quick_switch); DrawGameDoorValues(); } @@ -12289,45 +12552,7 @@ void GameActions_RND(void) #endif } - if (mouse_action.button) - { - int new_button = (mouse_action.button && mouse_action_last.button == 0); - int ch_button = CH_SIDE_FROM_BUTTON(mouse_action.button); - - x = mouse_action.lx; - y = mouse_action.ly; - element = Tile[x][y]; - - if (new_button) - { - CheckElementChangeByMouse(x, y, element, CE_CLICKED_BY_MOUSE, ch_button); - CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_CLICKED_ON_X, - ch_button); - } - - CheckElementChangeByMouse(x, y, element, CE_PRESSED_BY_MOUSE, ch_button); - CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_PRESSED_ON_X, - ch_button); - - if (level.use_step_counter) - { - boolean counted_click = FALSE; - - // element clicked that can change when clicked/pressed - if (CAN_CHANGE_OR_HAS_ACTION(element) && - (HAS_ANY_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) || - HAS_ANY_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE))) - counted_click = TRUE; - - // element clicked that can trigger change when clicked/pressed - if (trigger_events[element][CE_MOUSE_CLICKED_ON_X] || - trigger_events[element][CE_MOUSE_PRESSED_ON_X]) - counted_click = TRUE; - - if (new_button && counted_click) - CheckLevelTime_StepCounter(); - } - } + HandleMouseAction(&mouse_action, &mouse_action_last); SCAN_PLAYFIELD(x, y) { @@ -12433,17 +12658,16 @@ void GameActions_RND(void) CheckExitSP(x, y); else if (element == EL_EXPANDABLE_WALL_GROWING || element == EL_EXPANDABLE_STEELWALL_GROWING) - MauerWaechst(x, y); + WallGrowing(x, y); else if (element == EL_EXPANDABLE_WALL || element == EL_EXPANDABLE_WALL_HORIZONTAL || element == EL_EXPANDABLE_WALL_VERTICAL || element == EL_EXPANDABLE_WALL_ANY || - element == EL_BD_EXPANDABLE_WALL) - MauerAbleger(x, y); - else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL || + element == EL_BD_EXPANDABLE_WALL || + element == EL_EXPANDABLE_STEELWALL_HORIZONTAL || element == EL_EXPANDABLE_STEELWALL_VERTICAL || element == EL_EXPANDABLE_STEELWALL_ANY) - MauerAblegerStahl(x, y); + CheckWallGrowing(x, y); else if (element == EL_FLAMES) CheckForDragon(x, y); else if (element == EL_EXPLOSION) @@ -12452,7 +12676,7 @@ void GameActions_RND(void) element == EL_DIAGONAL_SHRINKING || element == EL_DIAGONAL_GROWING) { - graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]); + graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y], GfxDir[x][y]); DrawLevelGraphicAnimationIfNeeded(x, y, graphic); } @@ -12497,7 +12721,7 @@ void GameActions_RND(void) y = RND(lev_fieldy); element = Tile[x][y]; - if (!IS_PLAYER(x,y) && + if (!IS_PLAYER(x, y) && (element == EL_EMPTY || CAN_GROW_INTO(element) || element == EL_QUICKSAND_EMPTY || @@ -12873,7 +13097,7 @@ boolean MovePlayerOneStep(struct PlayerInfo *player, !AllPlayersInSight(player, new_jx, new_jy)) return MP_NO_ACTION; - can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG); + can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx, real_dy, DF_DIG); if (can_move != MP_MOVING) return can_move; @@ -13243,13 +13467,18 @@ void ScrollPlayer(struct PlayerInfo *player, int mode) CE_PLAYER_LEAVES_X, player->index_bit, leave_side); - if (IS_CUSTOM_ELEMENT(new_element)) - CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER, - player->index_bit, enter_side); + // needed because pushed element has not yet reached its destination, + // so it would trigger a change event at its previous field location + if (!player->is_pushing) + { + if (IS_CUSTOM_ELEMENT(new_element)) + CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER, + player->index_bit, enter_side); - CheckTriggeredElementChangeByPlayer(jx, jy, new_element, - CE_PLAYER_ENTERS_X, - player->index_bit, enter_side); + CheckTriggeredElementChangeByPlayer(jx, jy, new_element, + CE_PLAYER_ENTERS_X, + player->index_bit, enter_side); + } CheckTriggeredElementChangeBySide(jx, jy, player->initial_element, CE_MOVE_OF_X, move_direction); @@ -13260,8 +13489,8 @@ void ScrollPlayer(struct PlayerInfo *player, int mode) TestIfPlayerTouchesBadThing(jx, jy); TestIfPlayerTouchesCustomElement(jx, jy); - /* needed because pushed element has not yet reached its destination, - so it would trigger a change event at its previous field location */ + // needed because pushed element has not yet reached its destination, + // so it would trigger a change event at its previous field location if (!player->is_pushing) TestIfElementTouchesCustomElement(jx, jy); // for empty space @@ -13448,8 +13677,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 @@ -13475,8 +13705,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; @@ -13583,7 +13814,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++) @@ -13610,6 +13841,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); } @@ -13967,7 +14199,7 @@ void KillPlayer(struct PlayerInfo *player) player->killed = TRUE; // remove accessible field at the player's position - Tile[jx][jy] = EL_EMPTY; + RemoveField(jx, jy); // deactivate shield (else Bang()/Explode() would not work right) player->shield_normal_time_left = 0; @@ -14013,7 +14245,6 @@ void BuryPlayer(struct PlayerInfo *player) return; PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING); - PlayLevelSound(jx, jy, SND_GAME_LOSING); RemovePlayer(player); @@ -14192,7 +14423,7 @@ static int DigField(struct PlayerInfo *player, if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction)) return MP_NO_ACTION; // field has no opening in this direction - if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction)) + if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element, opposite_direction)) return MP_NO_ACTION; // field has no opening in this direction if (player_can_move && element == EL_ACID && move_direction == MV_DOWN) @@ -14671,7 +14902,7 @@ static int DigField(struct PlayerInfo *player, LevelSolved(); - PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING); + PlaySound(SND_GAME_SOKOBAN_SOLVING); } } else @@ -14756,7 +14987,7 @@ static int DigField(struct PlayerInfo *player, element == EL_DC_SWITCHGATE_SWITCH_UP || element == EL_DC_SWITCHGATE_SWITCH_DOWN) { - ToggleSwitchgateSwitch(x, y); + ToggleSwitchgateSwitch(); } else if (element == EL_LIGHT_SWITCH || element == EL_LIGHT_SWITCH_ACTIVE) @@ -15164,15 +15395,15 @@ void InitPlayLevelSound(void) loop_sound_volume = checked_calloc(num_sounds * sizeof(int)); } -static void PlayLevelSound(int x, int y, int nr) +static void PlayLevelSoundExt(int x, int y, int nr, boolean is_loop_sound) { int sx = SCREENX(x), sy = SCREENY(y); int volume, stereo_position; int max_distance = 8; - int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND); + int type = (is_loop_sound ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND); - if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) || - (!setup.sound_loops && IS_LOOP_SOUND(nr))) + if ((!setup.sound_simple && !is_loop_sound) || + (!setup.sound_loops && is_loop_sound)) return; if (!IN_LEV_FIELD(x, y) || @@ -15194,7 +15425,7 @@ static void PlayLevelSound(int x, int y, int nr) (sx + max_distance) * SOUND_MAX_LEFT2RIGHT / (SCR_FIELDX + 2 * max_distance)); - if (IS_LOOP_SOUND(nr)) + if (is_loop_sound) { /* This assures that quieter loop sounds do not overwrite louder ones, while restarting sound volume comparison with each new game frame. */ @@ -15209,6 +15440,11 @@ static void PlayLevelSound(int x, int y, int nr) PlaySoundExt(nr, volume, stereo_position, type); } +static void PlayLevelSound(int x, int y, int nr) +{ + PlayLevelSoundExt(x, y, nr, IS_LOOP_SOUND(nr)); +} + static void PlayLevelSoundNearest(int x, int y, int sound_action) { PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) : @@ -15258,10 +15494,12 @@ static void StopLevelSoundActionIfLoop(int x, int y, int action) static int getLevelMusicNr(void) { + int level_pos = level_nr - leveldir_current->first_level; + if (levelset.music[level_nr] != MUS_UNDEFINED) return levelset.music[level_nr]; // from config file else - return MAP_NOCONF_MUSIC(level_nr); // from music dir + return MAP_NOCONF_MUSIC(level_pos); // from music dir } static void FadeLevelSounds(void) @@ -15295,6 +15533,226 @@ static void PlayLevelMusic(void) PlayMusicLoop(music_nr); } +static int getSoundAction_BD(int sample) +{ + switch (sample) + { + case GD_S_STONE: + case GD_S_NUT: + case GD_S_DIRT_BALL: + case GD_S_NITRO: + case GD_S_FALLING_WALL: + return ACTION_IMPACT; + + case GD_S_NUT_CRACK: + return ACTION_BREAKING; + + case GD_S_EXPANDING_WALL: + case GD_S_WALL_REAPPEAR: + case GD_S_SLIME: + case GD_S_LAVA: + case GD_S_ACID_SPREAD: + return ACTION_GROWING; + + case GD_S_DIAMOND_COLLECT: + case GD_S_SKELETON_COLLECT: + case GD_S_PNEUMATIC_COLLECT: + case GD_S_BOMB_COLLECT: + case GD_S_CLOCK_COLLECT: + case GD_S_SWEET_COLLECT: + case GD_S_KEY_COLLECT: + case GD_S_DIAMOND_KEY_COLLECT: + return ACTION_COLLECTING; + + case GD_S_BOMB_PLACE: + case GD_S_REPLICATOR: + return ACTION_DROPPING; + + case GD_S_BLADDER_MOVE: + return ACTION_MOVING; + + case GD_S_BLADDER_SPENDER: + case GD_S_BLADDER_CONVERT: + case GD_S_GRAVITY_CHANGE: + return ACTION_CHANGING; + + case GD_S_BITER_EAT: + return ACTION_EATING; + + case GD_S_DOOR_OPEN: + case GD_S_CRACK: + return ACTION_OPENING; + + case GD_S_WALK_EARTH: + return ACTION_DIGGING; + + case GD_S_WALK_EMPTY: + return ACTION_WALKING; + + case GD_S_SWITCH_BITER: + case GD_S_SWITCH_CREATURES: + case GD_S_SWITCH_GRAVITY: + case GD_S_SWITCH_EXPANDING: + case GD_S_SWITCH_CONVEYOR: + case GD_S_SWITCH_REPLICATOR: + case GD_S_STIRRING: + return ACTION_ACTIVATING; + + case GD_S_BOX_PUSH: + return ACTION_PUSHING; + + case GD_S_TELEPORTER: + return ACTION_PASSING; + + case GD_S_EXPLOSION: + case GD_S_BOMB_EXPLOSION: + case GD_S_GHOST_EXPLOSION: + case GD_S_VOODOO_EXPLOSION: + case GD_S_NITRO_EXPLOSION: + return ACTION_EXPLODING; + + case GD_S_COVER: + case GD_S_AMOEBA: + case GD_S_AMOEBA_MAGIC: + case GD_S_MAGIC_WALL: + case GD_S_PNEUMATIC_HAMMER: + case GD_S_WATER: + return ACTION_ACTIVE; + + case GD_S_DIAMOND_RANDOM: + case GD_S_DIAMOND_1: + case GD_S_DIAMOND_2: + case GD_S_DIAMOND_3: + case GD_S_DIAMOND_4: + case GD_S_DIAMOND_5: + case GD_S_DIAMOND_6: + case GD_S_DIAMOND_7: + case GD_S_DIAMOND_8: + case GD_S_TIMEOUT_0: + case GD_S_TIMEOUT_1: + case GD_S_TIMEOUT_2: + case GD_S_TIMEOUT_3: + case GD_S_TIMEOUT_4: + case GD_S_TIMEOUT_5: + case GD_S_TIMEOUT_6: + case GD_S_TIMEOUT_7: + case GD_S_TIMEOUT_8: + case GD_S_TIMEOUT_9: + case GD_S_TIMEOUT_10: + case GD_S_BONUS_LIFE: + // kludge to prevent playing as loop sound + return ACTION_OTHER; + + case GD_S_FINISHED: + return ACTION_DEFAULT; + + default: + return ACTION_DEFAULT; + } +} + +static int getSoundEffect_BD(int element_bd, int sample) +{ + int sound_action = getSoundAction_BD(sample); + int sound_effect = element_info[SND_ELEMENT(element_bd)].sound[sound_action]; + int nr; + + // standard sounds + if (sound_action != ACTION_OTHER && + sound_action != ACTION_DEFAULT) + return sound_effect; + + // special sounds + switch (sample) + { + case GD_S_DIAMOND_RANDOM: + nr = GetSimpleRandom(8); + sound_effect = SND_BD_DIAMOND_IMPACT_RANDOM_1 + nr; + break; + + case GD_S_DIAMOND_1: + case GD_S_DIAMOND_2: + case GD_S_DIAMOND_3: + case GD_S_DIAMOND_4: + case GD_S_DIAMOND_5: + case GD_S_DIAMOND_6: + case GD_S_DIAMOND_7: + case GD_S_DIAMOND_8: + nr = sample - GD_S_DIAMOND_1; + sound_effect = SND_BD_DIAMOND_IMPACT_RANDOM_1 + nr; + break; + + case GD_S_TIMEOUT_0: + case GD_S_TIMEOUT_1: + case GD_S_TIMEOUT_2: + case GD_S_TIMEOUT_3: + case GD_S_TIMEOUT_4: + case GD_S_TIMEOUT_5: + case GD_S_TIMEOUT_6: + case GD_S_TIMEOUT_7: + case GD_S_TIMEOUT_8: + case GD_S_TIMEOUT_9: + case GD_S_TIMEOUT_10: + nr = sample - GD_S_TIMEOUT_0; + sound_effect = SND_GAME_RUNNING_OUT_OF_TIME_0 + nr; + + if (getSoundInfoEntryFilename(sound_effect) == NULL && sample != GD_S_TIMEOUT_0) + sound_effect = SND_GAME_RUNNING_OUT_OF_TIME; + break; + + case GD_S_FINISHED: + sound_effect = SND_GAME_LEVELTIME_BONUS; + break; + + case GD_S_BONUS_LIFE: + sound_effect = SND_GAME_HEALTH_BONUS; + break; + + default: + sound_effect = SND_UNDEFINED; + break; + } + + return sound_effect; +} + +void PlayLevelSound_BD(int xx, int yy, int element_bd, int sample) +{ + int element = (element_bd > -1 ? map_element_BD_to_RND(element_bd) : 0); + int sound_effect = getSoundEffect_BD(element, sample); + int sound_action = getSoundAction_BD(sample); + boolean is_loop_sound = IS_LOOP_SOUND(sound_effect); + int offset = 0; + int x = xx - offset; + int y = yy - offset; + + if (sound_action == ACTION_OTHER) + is_loop_sound = FALSE; + + if (sound_effect != SND_UNDEFINED) + PlayLevelSoundExt(x, y, sound_effect, is_loop_sound); +} + +void StopSound_BD(int element_bd, int sample) +{ + int element = (element_bd > -1 ? map_element_BD_to_RND(element_bd) : 0); + int sound_effect = getSoundEffect_BD(element, sample); + + if (sound_effect != SND_UNDEFINED) + StopSound(sound_effect); +} + +boolean isSoundPlaying_BD(int element_bd, int sample) +{ + int element = (element_bd > -1 ? map_element_BD_to_RND(element_bd) : 0); + int sound_effect = getSoundEffect_BD(element, sample); + + if (sound_effect != SND_UNDEFINED) + return isSoundPlaying(sound_effect); + + return FALSE; +} + void PlayLevelSound_EM(int xx, int yy, int element_em, int sample) { int element = (element_em > -1 ? map_element_EM_to_RND_game(element_em) : 0); @@ -15614,9 +16072,15 @@ void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message) } if (network.enabled) + { SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER); + } else { + // when using BD game engine, cover screen before fading out + if (!quick_quit && level.game_engine_type == GAME_ENGINE_TYPE_BD) + game_bd.cover_screen = TRUE; + if (quick_quit) FadeSkipNextFadeIn(); @@ -15649,21 +16113,43 @@ void RequestQuitGame(boolean escape_key_pressed) "Do you really want to quit the game?"); } -void RequestRestartGame(char *message) +static char *getRestartGameMessage(void) { - game.restart_game_message = NULL; + boolean play_again = hasStartedNetworkGame(); + static char message[MAX_OUTPUT_LINESIZE]; + char *game_over_text = "Game over!"; + char *play_again_text = " Play it again?"; + + if (level.game_engine_type == GAME_ENGINE_TYPE_MM && + game_mm.game_over_message != NULL) + game_over_text = game_mm.game_over_message; + + snprintf(message, MAX_OUTPUT_LINESIZE, "%s%s", game_over_text, + (play_again ? play_again_text : "")); + return message; +} + +static void RequestRestartGame(void) +{ + char *message = getRestartGameMessage(); boolean has_started_game = hasStartedNetworkGame(); int request_mode = (has_started_game ? REQ_ASK : REQ_CONFIRM); + int door_state = DOOR_CLOSE_1; - if (Request(message, request_mode | REQ_STAY_CLOSED) && has_started_game) + if (Request(message, request_mode | REQ_STAY_OPEN) && has_started_game) { + CloseDoor(door_state); + StartGameActions(network.enabled, setup.autorecord, level.random_seed); } else { - // needed in case of envelope request to close game panel - CloseDoor(DOOR_CLOSE_1); + // if game was invoked from level editor, also close tape recorder door + if (level_editor_test_game) + door_state = DOOR_CLOSE_ALL; + + CloseDoor(door_state); SetGameStatus(GAME_MODE_MAIN); @@ -15671,42 +16157,72 @@ void RequestRestartGame(char *message) } } -void CheckGameOver(void) +boolean CheckRestartGame(void) { - static boolean last_game_over = FALSE; static int game_over_delay = 0; int game_over_delay_value = 50; boolean game_over = checkGameFailed(); - // do not handle game over if request dialog is already active - if (game.request_active) - return; - - // do not ask to play again if game was never actually played - if (!game.GamePlayed) - return; - if (!game_over) { - last_game_over = FALSE; game_over_delay = game_over_delay_value; - return; + return FALSE; } if (game_over_delay > 0) { + if (game_over_delay == game_over_delay_value / 2) + PlaySound(SND_GAME_LOSING); + game_over_delay--; - return; + return FALSE; } - if (last_game_over != game_over) - game.restart_game_message = (hasStartedNetworkGame() ? - "Game over! Play it again?" : - "Game over!"); + // do not ask to play again if request dialog is already active + if (game.request_active) + return FALSE; - last_game_over = game_over; + // 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; + + // do not ask to play again if this was disabled in setup menu + if (!setup.ask_on_game_over) + return FALSE; + + game.RestartGameRequested = TRUE; + + RequestRestartGame(); + + return TRUE; +} + +boolean checkGameRunning(void) +{ + if (game_status != GAME_MODE_PLAYING) + return FALSE; + + if (level.game_engine_type == GAME_ENGINE_TYPE_BD && !checkGameRunning_BD()) + return FALSE; + + return TRUE; +} + +boolean checkGamePlaying(void) +{ + if (game_status != GAME_MODE_PLAYING) + return FALSE; + + if (level.game_engine_type == GAME_ENGINE_TYPE_BD && !checkGamePlaying_BD()) + return FALSE; + + return TRUE; } boolean checkGameSolved(void) @@ -15717,7 +16233,9 @@ boolean checkGameSolved(void) boolean checkGameFailed(void) { - if (level.game_engine_type == GAME_ENGINE_TYPE_EM) + if (level.game_engine_type == GAME_ENGINE_TYPE_BD) + return (game_bd.game_over && !game_bd.level_solved); + else if (level.game_engine_type == GAME_ENGINE_TYPE_EM) return (game_em.game_over && !game_em.level_solved); else if (level.game_engine_type == GAME_ENGINE_TYPE_SP) return (game_sp.game_over && !game_sp.level_solved); @@ -15890,7 +16408,7 @@ static ListNode *SaveEngineSnapshotBuffers(void) if (level.game_engine_type == GAME_ENGINE_TYPE_SP) SaveEngineSnapshotValues_SP(&buffers); if (level.game_engine_type == GAME_ENGINE_TYPE_MM) - SaveEngineSnapshotValues_MM(&buffers); + SaveEngineSnapshotValues_MM(); // save values stored in special snapshot structure @@ -15913,6 +16431,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)); @@ -16128,6 +16647,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, @@ -16143,6 +16667,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, @@ -16153,6 +16682,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, @@ -16232,7 +16766,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; @@ -16355,6 +16892,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); } @@ -16576,6 +17117,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)