#define USE_BOTH_SWITCHGATE_SWITCHES (USE_NEW_STUFF * 1)
#define USE_PLAYER_GRAVITY (USE_NEW_STUFF * 1)
#define USE_FIXED_BORDER_RUNNING_GFX (USE_NEW_STUFF * 1)
+#define USE_QUICKSAND_BD_ROCK_BUGFIX (USE_NEW_STUFF * 0)
#define USE_QUICKSAND_IMPACT_BUGFIX (USE_NEW_STUFF * 0)
+#define USE_CODE_THAT_BREAKS_SNAKE_BITE (USE_NEW_STUFF * 1)
+
+#define USE_UFAST_PLAYER_EXIT_BUGFIX (USE_NEW_STUFF * 1)
+#define USE_NEW_GAME_WON (USE_NEW_STUFF * 1)
+
+
/* for DigField() */
#define DF_NO_PUSH 0
#define DF_DIG 1
#define EX_TYPE_DYNA (1 << 4)
#define EX_TYPE_SINGLE_TILE (EX_TYPE_CENTER | EX_TYPE_BORDER)
+#if 1
+
+#define PANEL_DEACTIVATED(p) ((p).x < 0 || (p).y < 0)
+
+/* special positions in the game control window (relative to control window) */
+#define XX_LEVEL1 (game.panel.level.x)
+#define XX_LEVEL2 (game.panel.level.x - 1)
+#define YY_LEVEL (game.panel.level.y)
+#define XX_EMERALDS (game.panel.gems.x)
+#define YY_EMERALDS (game.panel.gems.y)
+#define XX_DYNAMITE (game.panel.inventory.x)
+#define YY_DYNAMITE (game.panel.inventory.y)
+#define XX_KEYS (game.panel.keys.x)
+#define YY_KEYS (game.panel.keys.y)
+#define XX_SCORE (game.panel.score.x)
+#define YY_SCORE (game.panel.score.y)
+#define XX_TIME1 (game.panel.time.x)
+#define XX_TIME2 (game.panel.time.x + 1)
+#define YY_TIME (game.panel.time.y)
+
+#else
+
/* special positions in the game control window (relative to control window) */
#define XX_LEVEL 37
#define YY_LEVEL 20
#define XX_TIME2 30
#define YY_TIME 194
+#endif
+
/* special positions in the game control window (relative to main window) */
-#define DX_LEVEL (DX + XX_LEVEL)
+#define DX_LEVEL1 (DX + XX_LEVEL1)
+#define DX_LEVEL2 (DX + XX_LEVEL2)
#define DY_LEVEL (DY + YY_LEVEL)
#define DX_EMERALDS (DX + XX_EMERALDS)
#define DY_EMERALDS (DY + YY_EMERALDS)
((e) >= NUM_FILE_ELEMENTS ? EL_UNKNOWN : (e))
#endif
-#define GET_TARGET_ELEMENT(e, ch, cv, cs) \
+#define RESOLVED_REFERENCE_ELEMENT(be, e) \
+ ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START : \
+ (be) + (e) - EL_SELF > EL_CUSTOM_END ? EL_CUSTOM_END : \
+ (be) + (e) - EL_SELF)
+
+#define GET_TARGET_ELEMENT(be, e, ch, cv, cs) \
((e) == EL_TRIGGER_PLAYER ? (ch)->actual_trigger_player : \
(e) == EL_TRIGGER_ELEMENT ? (ch)->actual_trigger_element : \
(e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value : \
(e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score : \
(e) == EL_CURRENT_CE_VALUE ? (cv) : \
- (e) == EL_CURRENT_CE_SCORE ? (cs) : (e))
+ (e) == EL_CURRENT_CE_SCORE ? (cs) : \
+ (e) >= EL_LAST_CE_8 && (e) <= EL_NEXT_CE_8 ? \
+ RESOLVED_REFERENCE_ELEMENT(be, e) : \
+ (e))
#define CAN_GROW_INTO(e) \
((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
InitField(x, y, init_game);
#endif
}
+
break;
}
{
int xpos = (3 * 14 - 3 * getFontWidth(FONT_TEXT_2)) / 2;
+ if (PANEL_DEACTIVATED(game.panel.gems))
+ return;
+
DrawText(DX_EMERALDS + xpos, DY_EMERALDS, int2str(value, 3), FONT_TEXT_2);
}
{
int xpos = (3 * 14 - 3 * getFontWidth(FONT_TEXT_2)) / 2;
+ if (PANEL_DEACTIVATED(game.panel.inventory))
+ return;
+
DrawText(DX_DYNAMITE + xpos, DY_DYNAMITE, int2str(value, 3), FONT_TEXT_2);
}
int base_key_graphic = EL_KEY_1;
int i;
+ if (PANEL_DEACTIVATED(game.panel.keys))
+ return;
+
if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
base_key_graphic = EL_EM_KEY_1;
/* currently only 4 of 8 possible keys are displayed */
for (i = 0; i < STD_NUM_KEYS; i++)
{
+ int x = XX_KEYS + i * MINI_TILEX;
+ int y = YY_KEYS;
+
if (key[i])
- DrawMiniGraphicExt(drawto, DX_KEYS + i * MINI_TILEX, DY_KEYS,
- el2edimg(base_key_graphic + i));
+ DrawMiniGraphicExt(drawto, DX + x,DY + y, el2edimg(base_key_graphic + i));
else
BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
- DOOR_GFX_PAGEX5 + XX_KEYS + i * MINI_TILEX, YY_KEYS,
- MINI_TILEX, MINI_TILEY, DX_KEYS + i * MINI_TILEX, DY_KEYS);
+ DOOR_GFX_PAGEX5 + x, y, MINI_TILEX, MINI_TILEY, DX + x,DY + y);
}
}
{
int xpos = (5 * 14 - 5 * getFontWidth(FONT_TEXT_2)) / 2;
+ if (PANEL_DEACTIVATED(game.panel.score))
+ return;
+
DrawText(DX_SCORE + xpos, DY_SCORE, int2str(value, 5), FONT_TEXT_2);
}
int xpos3 = (3 * 14 - 3 * getFontWidth(FONT_TEXT_2)) / 2;
int xpos4 = (4 * 10 - 4 * getFontWidth(FONT_LEVEL_NUMBER)) / 2;
+ if (PANEL_DEACTIVATED(game.panel.time))
+ return;
+
/* clear background if value just changed its size */
if (value == 999 || value == 1000)
- ClearRectangle(drawto, DX_TIME1, DY_TIME, 14 * 3, 14);
+ ClearRectangleOnBackground(drawto, DX_TIME1, DY_TIME, 14 * 3, 14);
if (value < 1000)
DrawText(DX_TIME1 + xpos3, DY_TIME, int2str(value, 3), FONT_TEXT_2);
inline void DrawGameValue_Level(int value)
{
+ if (PANEL_DEACTIVATED(game.panel.level))
+ return;
+
if (level_nr < 100)
- DrawText(DX_LEVEL, DY_LEVEL, int2str(value, 2), FONT_TEXT_2);
+ DrawText(DX_LEVEL1, DY_LEVEL, int2str(value, 2), FONT_TEXT_2);
else
+#if 1
+ DrawText(DX_LEVEL2, DY_LEVEL, int2str(value, 3), FONT_LEVEL_NUMBER);
+#else
{
/* misuse area for displaying emeralds to draw bigger level number */
DrawTextExt(drawto, DX_EMERALDS, DY_EMERALDS,
/* yes, this is all really ugly :-) */
}
+#endif
}
void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
void DrawGameDoorValues()
{
+ int time_value = (level.time == 0 ? TimePlayed : TimeLeft);
int dynamite_state = 0;
int key_bits = 0;
int i, j;
}
DrawAllGameValues(local_player->gems_still_needed, dynamite_state,
- local_player->score, TimeLeft, key_bits);
+ local_player->score, time_value, key_bits);
#endif
}
}
#endif
+#if 0
+static void replace_reference_element(int base_element, int *element)
+{
+ if (*element >= EL_LAST_CE_8 && *element <= EL_NEXT_CE_8)
+ {
+ *element = base_element + *element - EL_SELF;
+ *element = (*element < EL_CUSTOM_START ? EL_CUSTOM_START :
+ *element > EL_CUSTOM_END ? EL_CUSTOM_END : *element);
+ }
+}
+#endif
+
/*
=============================================================================
InitGameEngine()
for (l = 0; l < group->num_elements_resolved; l++)
trigger_events[group->element_resolved[l]][k] = TRUE;
}
+#if 1
+ else if (trigger_element == EL_ANY_ELEMENT)
+ for (l = 0; l < MAX_NUM_ELEMENTS; l++)
+ trigger_events[l][k] = TRUE;
+#endif
else
trigger_events[trigger_element][k] = TRUE;
}
EL_EMPTY);
}
}
+
+#if 0
+ /* ---------- initialize reference elements ------------------------------- */
+ for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
+ {
+ int element = EL_CUSTOM_START + i;
+ struct ElementInfo *ei = &element_info[element];
+
+ for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
+ replace_reference_element(element, &ei->content.e[x][y]);
+
+ for (j = 0; j < ei->num_change_pages; j++)
+ {
+ struct ElementChangeInfo *change = &ei->change_page[j];
+
+ replace_reference_element(element, &change->target_element);
+ replace_reference_element(element, &change->trigger_element);
+
+ for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
+ replace_reference_element(element, &change->target_content.e[x][y]);
+ }
+ }
+#endif
}
int get_num_special_action(int element, int action_first, int action_last)
boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
+ boolean do_fading = (game_status == GAME_MODE_MAIN);
int i, j, x, y;
+ game_status = GAME_MODE_PLAYING;
+
InitGameEngine();
/* don't play tapes over network */
player->LevelSolved = FALSE;
player->GameOver = FALSE;
+
+ player->LevelSolved_GameEnd = FALSE;
+ player->LevelSolved_SaveTape = FALSE;
+ player->LevelSolved_SaveScore = FALSE;
}
network_player_action_received = FALSE;
local_player->jy - MIDPOSY);
}
+ StopAnimation();
+
if (!game.restart_level)
CloseDoor(DOOR_CLOSE_1);
+ if (do_fading)
+ FadeOut(REDRAW_FIELD);
+
/* !!! FIX THIS (START) !!! */
if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
{
InitGameEngine_EM();
+
+#if 1
+ /* blit playfield from scroll buffer to normal back buffer for fading in */
+ BlitScreenToBitmap_EM(backbuffer);
+#endif
}
else
{
if (game.timegate_time_left == 0)
CloseAllOpenTimegates();
+ /* blit playfield from scroll buffer to normal back buffer for fading in */
if (setup.soft_scrolling)
BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
redraw_mask |= REDRAW_FROM_BACKBUFFER;
+
+#if 0
FadeToFront();
+#endif
}
/* !!! FIX THIS (END) !!! */
+ if (do_fading)
+ FadeIn(REDRAW_FIELD);
+
+ BackToFront();
+
if (!game.restart_level)
{
/* copy default game door content to main double buffer */
DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
}
+#if 1
+ SetPanelBackground();
+ SetDrawBackgroundMask(REDRAW_DOOR_1);
+#endif
+
DrawGameDoorValues();
if (!game.restart_level)
AmoebaCnt2[group_nr]++;
}
+#if USE_NEW_GAME_WON
+
+void GameWon()
+{
+ static boolean score_done = FALSE;
+ static boolean player_done = FALSE;
+ static int game_over_delay = 0;
+ int game_over_delay_value = 50;
+
+ /* do not start end game actions before the player stops moving (to exit) */
+ if (local_player->MovPos)
+ return;
+
+ if (tape.auto_play) /* tape might already be stopped here */
+ tape.auto_play_level_solved = TRUE;
+
+ if (!local_player->LevelSolved_GameEnd)
+ {
+ local_player->LevelSolved_GameEnd = TRUE;
+ local_player->LevelSolved_SaveTape = tape.recording;
+ local_player->LevelSolved_SaveScore = !tape.playing;
+
+ score_done = FALSE;
+ player_done = FALSE;
+ game_over_delay = 0;
+ }
+
+ PlaySoundStereo(SND_GAME_WINNING, SOUND_MIDDLE);
+
+ if (TimeLeft > 0)
+ {
+ if (!tape.playing)
+ {
+ if (setup.sound_loops)
+ PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
+ SND_CTRL_PLAY_LOOP);
+ else
+ PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
+ }
+
+ if (TimeLeft > 100 && TimeLeft % 10 == 0)
+ {
+ TimeLeft -= 10;
+ RaiseScore(level.score[SC_TIME_BONUS] * 10);
+ }
+ else
+ {
+ TimeLeft--;
+ RaiseScore(level.score[SC_TIME_BONUS]);
+ }
+
+ DrawGameValue_Time(TimeLeft);
+
+#if 0
+ if (!tape.playing)
+ Delay(10);
+#endif
+
+ if (TimeLeft <= 0 && !tape.playing && setup.sound_loops)
+ StopSound(SND_GAME_LEVELTIME_BONUS);
+ }
+ else if (level.time == 0 && TimePlayed < 999) /* level without time limit */
+ {
+ if (!tape.playing)
+ {
+ if (setup.sound_loops)
+ PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
+ SND_CTRL_PLAY_LOOP);
+ else
+ PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
+ }
+
+ if (TimePlayed < 900 && TimePlayed % 10 == 0)
+ {
+ TimePlayed += 10;
+ RaiseScore(level.score[SC_TIME_BONUS] * 10);
+ }
+ else
+ {
+ TimePlayed++;
+ RaiseScore(level.score[SC_TIME_BONUS]);
+ }
+
+ DrawGameValue_Time(TimePlayed);
+
+ if (TimePlayed >= 999 && !tape.playing && setup.sound_loops)
+ StopSound(SND_GAME_LEVELTIME_BONUS);
+ }
+ else
+ {
+ score_done = TRUE;
+ }
+
+ /* close exit door after last player */
+ if (AllPlayersGone && ExitX >= 0 && ExitY >= 0 &&
+ (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
+ Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN))
+ {
+ int element = Feld[ExitX][ExitY];
+
+ Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
+ EL_SP_EXIT_CLOSING);
+
+ PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
+ }
+
+ /* player disappears */
+ if (ExitX >= 0 && ExitY >= 0 && !player_done)
+ {
+ DrawLevelField(ExitX, ExitY);
+
+ player_done = TRUE;
+ }
+
+ game_over_delay++;
+
+ if (game_over_delay < game_over_delay_value || !score_done)
+ return;
+}
+
+void GameEnd()
+{
+ int hi_pos;
+ boolean raise_level = FALSE;
+
+ CloseDoor(DOOR_CLOSE_1);
+
+ if (local_player->LevelSolved_SaveTape)
+ {
+ TapeStop();
+
+ SaveTape(tape.level_nr); /* Ask to save tape */
+ }
+
+ if (!local_player->LevelSolved_SaveScore)
+ {
+ FadeOut(REDRAW_FIELD);
+
+ game_status = GAME_MODE_MAIN;
+
+ DrawAndFadeInMainMenu(REDRAW_FIELD);
+
+ return;
+ }
+
+ if (level_nr == leveldir_current->handicap_level)
+ {
+ leveldir_current->handicap_level++;
+ SaveLevelSetup_SeriesInfo();
+ }
+
+ if (level_editor_test_game)
+ local_player->score = -1; /* no highscore when playing from editor */
+ else if (level_nr < leveldir_current->last_level)
+ raise_level = TRUE; /* advance to next level */
+
+ if ((hi_pos = NewHiScore()) >= 0)
+ {
+ game_status = GAME_MODE_SCORES;
+
+ DrawHallOfFame(hi_pos);
+
+ if (raise_level)
+ {
+ level_nr++;
+ TapeErase();
+ }
+ }
+ else
+ {
+ FadeOut(REDRAW_FIELD);
+
+ game_status = GAME_MODE_MAIN;
+
+ if (raise_level)
+ {
+ level_nr++;
+ TapeErase();
+ }
+
+ DrawAndFadeInMainMenu(REDRAW_FIELD);
+ }
+
+ local_player->LevelSolved_SaveScore = FALSE;
+}
+
+#else
+
void GameWon()
{
int hi_pos;
BackToFront();
}
+#endif
+
int NewHiScore()
{
int k, l;
started_moving = TRUE;
Feld[x][y] = EL_QUICKSAND_EMPTYING;
+#if USE_QUICKSAND_BD_ROCK_BUGFIX
+ if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
+ Store[x][y] = EL_ROCK;
+#else
Store[x][y] = EL_ROCK;
+#endif
PlayLevelSoundAction(x, y, ACTION_EMPTYING);
}
if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
element == EL_EXPANDABLE_WALL_ANY ||
- element == EL_EXPANDABLE_WALL)
+ element == EL_EXPANDABLE_WALL ||
+ element == EL_BD_EXPANDABLE_WALL)
{
if (links_frei)
{
if (ei->collect_score == 0)
{
+ int xx, yy;
+
#if 0
printf("::: CE_SCORE_GETS_ZERO\n");
#endif
#if 0
printf("::: RESULT: %d, %d\n", Feld[x][y], ChangePage[x][y]);
#endif
+
+#if 1
+ /*
+ This is a very special case that seems to be a mixture between
+ CheckElementChange() and CheckTriggeredElementChange(): while
+ the first one only affects single elements that are triggered
+ directly, the second one affects multiple elements in the playfield
+ that are triggered indirectly by another element. This is a third
+ case: Changing the CE score always affects multiple identical CEs,
+ so every affected CE must be checked, not only the single CE for
+ which the CE score was changed in the first place (as every instance
+ of that CE shares the same CE score, and therefore also can change)!
+ */
+ SCAN_PLAYFIELD(xx, yy)
+ {
+ if (Feld[xx][yy] == element)
+ CheckElementChange(xx, yy, element, EL_UNDEFINED,
+ CE_SCORE_GETS_ZERO);
+ }
+#endif
}
}
#endif
boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
boolean add_player_onto_element = (new_element_is_player &&
+#if USE_CODE_THAT_BREAKS_SNAKE_BITE
+ /* this breaks SnakeBite when a snake is
+ halfway through a door that closes */
+ /* NOW FIXED AT LEVEL INIT IN files.c */
new_element != EL_SOKOBAN_FIELD_PLAYER &&
+#endif
IS_WALKABLE(old_element));
#if 0
ChangeEvent[ex][ey] = ChangeEvent[x][y];
content_element = change->target_content.e[xx][yy];
- target_element = GET_TARGET_ELEMENT(content_element, change,
+ target_element = GET_TARGET_ELEMENT(element, content_element, change,
ce_value, ce_score);
CreateElementFromChange(ex, ey, target_element);
}
else
{
- target_element = GET_TARGET_ELEMENT(change->target_element, change,
+ target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
ce_value, ce_score);
if (element == EL_DIAGONAL_GROWING ||
}
#endif
- StopAnimation();
-
- game_status = GAME_MODE_PLAYING;
-
InitGame();
}
network_player_action_received = FALSE;
#endif
+ /* when playing tape, read previously recorded player input from tape data */
recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
+#if 1
+ /* TapePlayAction() may return NULL when toggling to "pause before death" */
+ if (tape.pausing)
+ return;
+#endif
+
+#if 0
+ if (tape.playing)
+ {
+ if (recorded_player_action == NULL)
+ printf("!!! THIS SHOULD NOT HAPPEN !!!\n");
+ else
+ printf("::: %05d: TAPE PLAYING: %08x\n",
+ FrameCounter, recorded_player_action[0]);
+ }
+#endif
+
if (tape.set_centered_player)
{
game.centered_player_nr_next = tape.centered_player_nr_next;
if (tape.recording)
TapeRecordAction(tape_action);
+#if 0
+ if (tape.recording)
+ printf("::: %05d: TAPE RECORDING: %08x\n",
+ FrameCounter, tape_action[0]);
+#endif
+
if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
{
GameActions_EM_Main();
for (i = 0; i < MAX_PLAYERS; i++)
effective_action[i] = stored_player[i].effective_action;
+#if 0
+ printf("::: %04d: %08x\n", FrameCounter, effective_action[0]);
+#endif
+
GameActions_EM(effective_action, warp_mode);
CheckLevelTime();
else if (element == EL_EXPANDABLE_WALL ||
element == EL_EXPANDABLE_WALL_HORIZONTAL ||
element == EL_EXPANDABLE_WALL_VERTICAL ||
- element == EL_EXPANDABLE_WALL_ANY)
+ element == EL_EXPANDABLE_WALL_ANY ||
+ element == EL_BD_EXPANDABLE_WALL)
MauerAbleger(x, y);
else if (element == EL_FLAMES)
CheckForDragon(x, y);
{
int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
int move_dir_vertical = player->effective_action & MV_VERTICAL;
- boolean player_is_snapping = player->effective_action & JOY_BUTTON_1;
+ boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
int jx = player->jx, jy = player->jy;
boolean player_is_moving_to_valid_field =
(!player_is_snapping &&
PlayerVisit[jx][jy] = FrameCounter;
+#if USE_UFAST_PLAYER_EXIT_BUGFIX
+ player->is_moving = TRUE;
+#endif
+
+#if 1
+ /* should better be called in MovePlayer(), but this breaks some tapes */
ScrollPlayer(player, SCROLL_INIT);
+#endif
return MP_MOVING;
}
player->is_dropping = FALSE;
player->is_dropping_pressed = FALSE;
player->drop_pressed_delay = 0;
+
+#if 0
+ /* should better be called here than above, but this breaks some tapes */
+ ScrollPlayer(player, SCROLL_INIT);
+#endif
}
else
{
else
#endif
{
- game_status = GAME_MODE_MAIN;
- DrawMainMenu();
+ if (!ask_if_really_quit || level_editor_test_game)
+ {
+ game_status = GAME_MODE_MAIN;
+
+ DrawMainMenu();
+ }
+ else
+ {
+ FadeOut(REDRAW_FIELD);
+
+ game_status = GAME_MODE_MAIN;
+
+ DrawAndFadeInMainMenu(REDRAW_FIELD);
+ }
}
}
else