// 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_NEEDED 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
{
&game.panel.gems,
TYPE_INTEGER,
},
+ {
+ GAME_PANEL_GEMS_NEEDED,
+ &game.panel.gems_needed,
+ 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,
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]))
#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
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) \
}
}
+static void InitFieldForEngine_RND(int x, int y)
+{
+ int element = Tile[x][y];
+
+ // convert BD engine elements to corresponding R'n'D engine elements
+ element = (element == EL_BD_EMPTY ? EL_EMPTY :
+ element == EL_BD_PLAYER ? EL_PLAYER_1 :
+ element == EL_BD_INBOX ? EL_PLAYER_1 :
+ element == EL_BD_SAND ? EL_SAND :
+ element == EL_BD_STEELWALL ? EL_STEELWALL :
+ element == EL_BD_EXIT_CLOSED ? EL_EXIT_CLOSED :
+ element == EL_BD_EXIT_OPEN ? EL_EXIT_OPEN :
+ element);
+
+ Tile[x][y] = element;
+}
+
+static void InitFieldForEngine(int x, int y)
+{
+ if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
+ InitFieldForEngine_RND(x, y);
+}
+
static void InitField(int x, int y, boolean init_game)
{
int element = Tile[x][y];
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 ?
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 ?
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_needed = level.gems_needed;
+ int gems_collected = (level.game_engine_type == GAME_ENGINE_TYPE_BD ?
+ game_bd.game->cave->diamonds_collected :
+ gems_needed - 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 :
// 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_NEEDED].value = gems_needed;
+ 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++)
if (PANEL_DEACTIVATED(pos))
continue;
- if (pos->class == get_hash_from_key("extra_panel_items") &&
+ if (pos->class == get_hash_from_string("extra_panel_items") &&
!setup.prefer_extra_panel_items)
continue;
int width, height;
int dst_x = PANEL_XPOS(pos);
int dst_y = PANEL_YPOS(pos);
- boolean skip = (pos->class == get_hash_from_key("mm_engine_only") &&
+ boolean skip = (pos->class == get_hash_from_string("mm_engine_only") &&
level.game_engine_type != GAME_ENGINE_TYPE_MM);
if (graphic != IMG_UNDEFINED && !skip)
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));
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++)
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;
}
}
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))
{
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++)
{
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;
}
}
// 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);
}
TimeFrames = 0;
TimePlayed = 0;
TimeLeft = level.time;
+
+ TapeTimeFrames = 0;
TapeTime = 0;
ScreenMovDir = MV_NONE;
game.LevelSolved_CountingScore = 0;
game.LevelSolved_CountingHealth = 0;
+ game.RestartGameRequested = FALSE;
+
game.panel.active = TRUE;
game.no_level_time_limit = (level.time == 0);
SCAN_PLAYFIELD(x, y)
{
+ InitFieldForEngine(x, y);
+
if (emulate_bd && !IS_BD_ELEMENT(Tile[x][y]))
emulate_bd = FALSE;
if (emulate_sp && !IS_SP_ELEMENT(Tile[x][y]))
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];
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();
}
}
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();
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 ?
LevelSolved_SetFinalGameValues();
}
+static boolean AdvanceToNextLevel(void)
+{
+ if (setup.increment_levels &&
+ level_nr < leveldir_current->last_level &&
+ !network_playing)
+ {
+ level_nr++; // advance to next level
+ TapeErase(); // start with empty tape
+
+ if (setup.auto_play_next_level)
+ {
+ scores.continue_playing = TRUE;
+ scores.next_level_nr = level_nr;
+
+ LoadLevel(level_nr);
+
+ SaveLevelSetup_SeriesInfo();
+ }
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
void GameWon(void)
{
static int time_count_steps;
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;
}
// 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;
int last_level_nr = levelset.level_nr;
boolean tape_saved = FALSE;
- game.LevelSolved_GameEnd = TRUE;
+ // Important note: This function is not only called after "GameWon()", but also after
+ // "game over" (if automatically asking for restarting the game is disabled in setup)
+
+ if (game.LevelSolved)
+ game.LevelSolved_GameEnd = TRUE;
if (game.LevelSolved_SaveTape && !score_info_tape_play)
{
return;
}
- if (!game.LevelSolved_SaveScore)
+ if (!game.GamePlayed || (!game.LevelSolved_SaveScore && !level.bd_intermission))
{
SetGameStatus(GAME_MODE_MAIN);
}
// save score and score tape before potentially erasing tape below
- NewHighScore(last_level_nr, tape_saved);
+ if (game.LevelSolved_SaveScore)
+ NewHighScore(last_level_nr, tape_saved);
- if (setup.increment_levels &&
- level_nr < leveldir_current->last_level &&
- !network_playing)
- {
- level_nr++; // advance to next level
- TapeErase(); // start with empty tape
-
- if (setup.auto_play_next_level)
- {
- scores.continue_playing = TRUE;
- scores.next_level_nr = level_nr;
-
- LoadLevel(level_nr);
-
- SaveLevelSetup_SeriesInfo();
- }
- }
+ // increment and load next level (if possible and not configured otherwise)
+ AdvanceToNextLevel();
- if (scores.last_added >= 0 && setup.show_scores_after_game)
+ if (game.LevelSolved_SaveScore && scores.last_added >= 0 && setup.show_scores_after_game)
{
SetGameStatus(GAME_MODE_SCORES);
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;
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];
{
// 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)
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;
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
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;
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)
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;
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 &&
}
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;
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
{
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);
}
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;
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;
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
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
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);
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);
}
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)
{
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)
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)
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
}
}
+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;
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;
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++)
{
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 */
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)
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);
{
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.)
}
}
+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)
{
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();
}
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];
#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)
{
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);
}
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 ||
!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;
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);
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
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
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;
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++)
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);
}
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;
return;
PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
- PlayLevelSound(jx, jy, SND_GAME_LOSING);
RemovePlayer(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)
LevelSolved();
- PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
+ PlaySound(SND_GAME_SOKOBAN_SOLVING);
}
}
else
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) ||
(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. */
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) :
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)
PlayMusicLoop(music_nr);
}
+static int getSoundAction_BD(int sample)
+{
+ switch (sample)
+ {
+ case GD_S_STONE_PUSHING:
+ case GD_S_MEGA_STONE_PUSHING:
+ case GD_S_FLYING_STONE_PUSHING:
+ case GD_S_WAITING_STONE_PUSHING:
+ case GD_S_CHASING_STONE_PUSHING:
+ case GD_S_NUT_PUSHING:
+ case GD_S_NITRO_PACK_PUSHING:
+ case GD_S_BLADDER_PUSHING:
+ case GD_S_BOX_PUSHING:
+ return ACTION_PUSHING;
+
+ case GD_S_STONE_FALLING:
+ case GD_S_MEGA_STONE_FALLING:
+ case GD_S_FLYING_STONE_FALLING:
+ case GD_S_NUT_FALLING:
+ case GD_S_DIRT_BALL_FALLING:
+ case GD_S_DIRT_LOOSE_FALLING:
+ case GD_S_NITRO_PACK_FALLING:
+ case GD_S_FALLING_WALL_FALLING:
+ return ACTION_FALLING;
+
+ case GD_S_STONE_IMPACT:
+ case GD_S_MEGA_STONE_IMPACT:
+ case GD_S_FLYING_STONE_IMPACT:
+ case GD_S_NUT_IMPACT:
+ case GD_S_DIRT_BALL_IMPACT:
+ case GD_S_DIRT_LOOSE_IMPACT:
+ case GD_S_NITRO_PACK_IMPACT:
+ case GD_S_FALLING_WALL_IMPACT:
+ return ACTION_IMPACT;
+
+ case GD_S_NUT_CRACKING:
+ return ACTION_BREAKING;
+
+ case GD_S_EXPANDING_WALL:
+ case GD_S_WALL_REAPPEARING:
+ case GD_S_SLIME:
+ case GD_S_LAVA:
+ case GD_S_ACID_SPREADING:
+ return ACTION_GROWING;
+
+ case GD_S_DIAMOND_COLLECTING:
+ case GD_S_FLYING_DIAMOND_COLLECTING:
+ case GD_S_SKELETON_COLLECTING:
+ case GD_S_PNEUMATIC_COLLECTING:
+ case GD_S_BOMB_COLLECTING:
+ case GD_S_CLOCK_COLLECTING:
+ case GD_S_SWEET_COLLECTING:
+ case GD_S_KEY_COLLECTING:
+ case GD_S_DIAMOND_KEY_COLLECTING:
+ return ACTION_COLLECTING;
+
+ case GD_S_BOMB_PLACING:
+ case GD_S_REPLICATOR:
+ return ACTION_DROPPING;
+
+ case GD_S_BLADDER_MOVING:
+ return ACTION_MOVING;
+
+ case GD_S_BLADDER_SPENDER:
+ case GD_S_BLADDER_CONVERTING:
+ case GD_S_GRAVITY_CHANGING:
+ return ACTION_CHANGING;
+
+ case GD_S_BITER_EATING:
+ return ACTION_EATING;
+
+ case GD_S_DOOR_OPENING:
+ case GD_S_CRACKING:
+ return ACTION_OPENING;
+
+ case GD_S_DIRT_WALKING:
+ return ACTION_DIGGING;
+
+ case GD_S_EMPTY_WALKING:
+ 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_TELEPORTER:
+ return ACTION_PASSING;
+
+ case GD_S_EXPLODING:
+ case GD_S_BOMB_EXPLODING:
+ case GD_S_GHOST_EXPLODING:
+ case GD_S_VOODOO_EXPLODING:
+ case GD_S_NITRO_PACK_EXPLODING:
+ return ACTION_EXPLODING;
+
+ case GD_S_COVERING:
+ case GD_S_AMOEBA:
+ case GD_S_MAGIC_WALL:
+ case GD_S_PNEUMATIC_HAMMER:
+ case GD_S_WATER:
+ return ACTION_ACTIVE;
+
+ case GD_S_DIAMOND_FALLING_RANDOM:
+ case GD_S_DIAMOND_FALLING_1:
+ case GD_S_DIAMOND_FALLING_2:
+ case GD_S_DIAMOND_FALLING_3:
+ case GD_S_DIAMOND_FALLING_4:
+ case GD_S_DIAMOND_FALLING_5:
+ case GD_S_DIAMOND_FALLING_6:
+ case GD_S_DIAMOND_FALLING_7:
+ case GD_S_DIAMOND_FALLING_8:
+ case GD_S_DIAMOND_IMPACT_RANDOM:
+ case GD_S_DIAMOND_IMPACT_1:
+ case GD_S_DIAMOND_IMPACT_2:
+ case GD_S_DIAMOND_IMPACT_3:
+ case GD_S_DIAMOND_IMPACT_4:
+ case GD_S_DIAMOND_IMPACT_5:
+ case GD_S_DIAMOND_IMPACT_6:
+ case GD_S_DIAMOND_IMPACT_7:
+ case GD_S_DIAMOND_IMPACT_8:
+ case GD_S_FLYING_DIAMOND_FALLING_RANDOM:
+ case GD_S_FLYING_DIAMOND_FALLING_1:
+ case GD_S_FLYING_DIAMOND_FALLING_2:
+ case GD_S_FLYING_DIAMOND_FALLING_3:
+ case GD_S_FLYING_DIAMOND_FALLING_4:
+ case GD_S_FLYING_DIAMOND_FALLING_5:
+ case GD_S_FLYING_DIAMOND_FALLING_6:
+ case GD_S_FLYING_DIAMOND_FALLING_7:
+ case GD_S_FLYING_DIAMOND_FALLING_8:
+ case GD_S_FLYING_DIAMOND_IMPACT_RANDOM:
+ case GD_S_FLYING_DIAMOND_IMPACT_1:
+ case GD_S_FLYING_DIAMOND_IMPACT_2:
+ case GD_S_FLYING_DIAMOND_IMPACT_3:
+ case GD_S_FLYING_DIAMOND_IMPACT_4:
+ case GD_S_FLYING_DIAMOND_IMPACT_5:
+ case GD_S_FLYING_DIAMOND_IMPACT_6:
+ case GD_S_FLYING_DIAMOND_IMPACT_7:
+ case GD_S_FLYING_DIAMOND_IMPACT_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:
+ // trigger special post-processing (and force sound to be non-looping)
+ return ACTION_OTHER;
+
+ case GD_S_AMOEBA_MAGIC:
+ case GD_S_FINISHED:
+ // trigger special post-processing (and force sound to be looping)
+ 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 post-processing for some sounds
+ switch (sample)
+ {
+ case GD_S_DIAMOND_FALLING_RANDOM:
+ case GD_S_DIAMOND_FALLING_1:
+ case GD_S_DIAMOND_FALLING_2:
+ case GD_S_DIAMOND_FALLING_3:
+ case GD_S_DIAMOND_FALLING_4:
+ case GD_S_DIAMOND_FALLING_5:
+ case GD_S_DIAMOND_FALLING_6:
+ case GD_S_DIAMOND_FALLING_7:
+ case GD_S_DIAMOND_FALLING_8:
+ nr = (sample == GD_S_DIAMOND_FALLING_RANDOM ? GetSimpleRandom(8) :
+ sample - GD_S_DIAMOND_FALLING_1);
+ sound_effect = SND_BD_DIAMOND_FALLING_RANDOM_1 + nr;
+
+ if (getSoundInfoEntryFilename(sound_effect) == NULL)
+ sound_effect = SND_BD_DIAMOND_FALLING;
+ break;
+
+ case GD_S_DIAMOND_IMPACT_RANDOM:
+ case GD_S_DIAMOND_IMPACT_1:
+ case GD_S_DIAMOND_IMPACT_2:
+ case GD_S_DIAMOND_IMPACT_3:
+ case GD_S_DIAMOND_IMPACT_4:
+ case GD_S_DIAMOND_IMPACT_5:
+ case GD_S_DIAMOND_IMPACT_6:
+ case GD_S_DIAMOND_IMPACT_7:
+ case GD_S_DIAMOND_IMPACT_8:
+ nr = (sample == GD_S_DIAMOND_IMPACT_RANDOM ? GetSimpleRandom(8) :
+ sample - GD_S_DIAMOND_IMPACT_1);
+ sound_effect = SND_BD_DIAMOND_IMPACT_RANDOM_1 + nr;
+
+ if (getSoundInfoEntryFilename(sound_effect) == NULL)
+ sound_effect = SND_BD_DIAMOND_IMPACT;
+ break;
+
+ case GD_S_FLYING_DIAMOND_FALLING_RANDOM:
+ case GD_S_FLYING_DIAMOND_FALLING_1:
+ case GD_S_FLYING_DIAMOND_FALLING_2:
+ case GD_S_FLYING_DIAMOND_FALLING_3:
+ case GD_S_FLYING_DIAMOND_FALLING_4:
+ case GD_S_FLYING_DIAMOND_FALLING_5:
+ case GD_S_FLYING_DIAMOND_FALLING_6:
+ case GD_S_FLYING_DIAMOND_FALLING_7:
+ case GD_S_FLYING_DIAMOND_FALLING_8:
+ nr = (sample == GD_S_FLYING_DIAMOND_FALLING_RANDOM ? GetSimpleRandom(8) :
+ sample - GD_S_FLYING_DIAMOND_FALLING_1);
+ sound_effect = SND_BD_FLYING_DIAMOND_FALLING_RANDOM_1 + nr;
+
+ if (getSoundInfoEntryFilename(sound_effect) == NULL)
+ sound_effect = SND_BD_FLYING_DIAMOND_FALLING;
+ break;
+
+ case GD_S_FLYING_DIAMOND_IMPACT_RANDOM:
+ case GD_S_FLYING_DIAMOND_IMPACT_1:
+ case GD_S_FLYING_DIAMOND_IMPACT_2:
+ case GD_S_FLYING_DIAMOND_IMPACT_3:
+ case GD_S_FLYING_DIAMOND_IMPACT_4:
+ case GD_S_FLYING_DIAMOND_IMPACT_5:
+ case GD_S_FLYING_DIAMOND_IMPACT_6:
+ case GD_S_FLYING_DIAMOND_IMPACT_7:
+ case GD_S_FLYING_DIAMOND_IMPACT_8:
+ nr = (sample == GD_S_FLYING_DIAMOND_IMPACT_RANDOM ? GetSimpleRandom(8) :
+ sample - GD_S_FLYING_DIAMOND_IMPACT_1);
+ sound_effect = SND_BD_FLYING_DIAMOND_IMPACT_RANDOM_1 + nr;
+
+ if (getSoundInfoEntryFilename(sound_effect) == NULL)
+ sound_effect = SND_BD_FLYING_DIAMOND_IMPACT;
+ 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_BONUS_LIFE:
+ sound_effect = SND_GAME_HEALTH_BONUS;
+ break;
+
+ case GD_S_AMOEBA_MAGIC:
+ sound_effect = SND_BD_AMOEBA_OTHER;
+ break;
+
+ case GD_S_FINISHED:
+ sound_effect = SND_GAME_LEVELTIME_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;
+
+ // some sound actions are always looping in native BD game engine
+ if (sound_action == ACTION_DEFAULT)
+ is_loop_sound = TRUE;
+
+ // some sound actions are always non-looping in native BD game engine
+ if (sound_action == ACTION_FALLING ||
+ sound_action == ACTION_MOVING ||
+ 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);
}
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();
"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;
+
+ boolean restart_wanted = (Request(message, request_mode | REQ_STAY_OPEN) && has_started_game);
- if (Request(message, request_mode | REQ_STAY_CLOSED) && has_started_game)
+ // if no restart wanted, continue with next level for BD style intermission levels
+ if (!restart_wanted && !level_editor_test_game && level.bd_intermission)
{
+ boolean success = AdvanceToNextLevel();
+
+ restart_wanted = (success && setup.auto_play_next_level);
+ }
+
+ if (restart_wanted)
+ {
+ 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);
}
}
-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;
+
+ // 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;
- last_game_over = game_over;
+ return TRUE;
}
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);
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));
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,
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,
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,
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;
};
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);
}
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)