// game panel display and control definitions
#define GAME_PANEL_LEVEL_NUMBER 0
#define GAME_PANEL_GEMS 1
-#define GAME_PANEL_GEMS_TOTAL 2
+#define GAME_PANEL_GEMS_NEEDED 2
#define GAME_PANEL_GEMS_COLLECTED 3
#define GAME_PANEL_GEMS_SCORE 4
#define GAME_PANEL_INVENTORY_COUNT 5
TYPE_INTEGER,
},
{
- GAME_PANEL_GEMS_TOTAL,
- &game.panel.gems_total,
+ GAME_PANEL_GEMS_NEEDED,
+ &game.panel.gems_needed,
TYPE_INTEGER,
},
{
}
}
+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];
level.game_engine_type == GAME_ENGINE_TYPE_MM ?
game_mm.kettles_still_needed :
game.gems_still_needed);
- int gems_total = level.gems_needed;
+ int gems_needed = level.gems_needed;
int gems_collected = (level.game_engine_type == GAME_ENGINE_TYPE_BD ?
game_bd.game->cave->diamonds_collected :
- gems_total - gems);
+ gems_needed - gems);
int gems_score = (level.game_engine_type == GAME_ENGINE_TYPE_BD ?
game_bd.game->cave->diamond_value :
level.score[SC_EMERALD]);
// 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_NEEDED].value = gems_needed;
game_panel_controls[GAME_PANEL_GEMS_COLLECTED].value = gems_collected;
game_panel_controls[GAME_PANEL_GEMS_SCORE].value = gems_score;
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)
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]))
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();
}
}
// 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;
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;
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;
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)
{
int i;
+ // 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++;
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];
{
switch (sample)
{
- case GD_S_STONE:
- case GD_S_NUT:
- case GD_S_DIRT_BALL:
- case GD_S_NITRO:
- case GD_S_FALLING_WALL:
+ 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_CRACK:
+ case GD_S_NUT_CRACKING:
return ACTION_BREAKING;
case GD_S_EXPANDING_WALL:
- case GD_S_WALL_REAPPEAR:
+ case GD_S_WALL_REAPPEARING:
case GD_S_SLIME:
case GD_S_LAVA:
- case GD_S_ACID_SPREAD:
+ case GD_S_ACID_SPREADING:
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:
+ 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_PLACE:
+ case GD_S_BOMB_PLACING:
case GD_S_REPLICATOR:
return ACTION_DROPPING;
- case GD_S_BLADDER_MOVE:
+ case GD_S_BLADDER_MOVING:
return ACTION_MOVING;
case GD_S_BLADDER_SPENDER:
- case GD_S_BLADDER_CONVERT:
- case GD_S_GRAVITY_CHANGE:
+ case GD_S_BLADDER_CONVERTING:
+ case GD_S_GRAVITY_CHANGING:
return ACTION_CHANGING;
- case GD_S_BITER_EAT:
+ case GD_S_BITER_EATING:
return ACTION_EATING;
- case GD_S_DOOR_OPEN:
- case GD_S_CRACK:
+ case GD_S_DOOR_OPENING:
+ case GD_S_CRACKING:
return ACTION_OPENING;
- case GD_S_WALK_EARTH:
+ case GD_S_DIRT_WALKING:
return ACTION_DIGGING;
- case GD_S_WALK_EMPTY:
+ case GD_S_EMPTY_WALKING:
return ACTION_WALKING;
case GD_S_SWITCH_BITER:
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:
+ 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_COVER:
+ case GD_S_COVERING:
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_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_9:
case GD_S_TIMEOUT_10:
case GD_S_BONUS_LIFE:
- // kludge to prevent playing as loop sound
+ // 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:
sound_action != ACTION_DEFAULT)
return sound_effect;
- // special sounds
+ // special post-processing for some sounds
switch (sample)
{
- case GD_S_DIAMOND_RANDOM:
- nr = GetSimpleRandom(8);
- sound_effect = SND_BD_DIAMOND_IMPACT_RANDOM_1 + nr;
+ 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_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;
+ 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:
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;
+ 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;
int x = xx - offset;
int y = yy - offset;
- if (sound_action == ACTION_OTHER)
+ // 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)
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)
{
// set for all game engines if level was solved