#define USE_NEW_AMOEBA_CODE FALSE
/* EXPERIMENTAL STUFF */
-#define USE_NEW_MOVEMENT FALSE
+#define USE_NEW_STUFF (TRUE * 1)
+
+#define USE_NEW_MOVE_STYLE (TRUE * USE_NEW_STUFF * 1)
+#define USE_NEW_MOVE_DELAY (TRUE * USE_NEW_STUFF * 1)
+#define USE_NEW_PUSH_DELAY (TRUE * USE_NEW_STUFF * 1)
+#define USE_NEW_BLOCK_STYLE (TRUE * USE_NEW_STUFF * 1)
+#define USE_NEW_SP_SLIPPERY (TRUE * USE_NEW_STUFF * 1)
+#define USE_NEW_RANDOMIZE (TRUE * USE_NEW_STUFF * 1)
+
+#define USE_PUSH_BUGFIX (TRUE * USE_NEW_STUFF * 1)
+
+#define USE_CAN_MOVE_NOT_MOVING (TRUE * USE_NEW_STUFF * 1)
+#define USE_PREVIOUS_MOVE_DIR (TRUE * USE_NEW_STUFF * 1)
+
/* for DigField() */
#define DF_NO_PUSH 0
/* forward declaration for internal use */
+static void AdvanceFrameAndPlayerCounters(int);
+
static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
static boolean MovePlayer(struct PlayerInfo *, int, int);
static void ScrollPlayer(struct PlayerInfo *, int);
level.sp_block_last_field :
level.block_last_field);
+#if USE_NEW_BLOCK_STYLE
+#if 1
+ player->block_delay = (player->block_last_field ?
+ (element == EL_SP_MURPHY ?
+ level.sp_block_delay :
+ level.block_delay) : 0);
+#else
+ player->block_delay = (element == EL_SP_MURPHY ?
+ (player->block_last_field ? 7 : 1) :
+ (player->block_last_field ? 7 : 1));
+#endif
+
+#if 0
+ printf("::: block_last_field == %d, block_delay = %d\n",
+ player->block_last_field, player->block_delay);
+#endif
+#endif
+
if (!options.network || player->connected)
{
player->active = TRUE;
DrawText(DX_DYNAMITE, DY_DYNAMITE, int2str(value, 3), FONT_TEXT_2);
}
-inline void DrawGameValue_Keys(struct PlayerInfo *player)
+inline void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
{
int i;
- for (i = 0; i < MAX_KEYS; i++)
- if (player->key[i])
+ /* currently only 4 of 8 possible keys are displayed */
+ for (i = 0; i < STD_NUM_KEYS; i++)
+ if (key[i])
DrawMiniGraphicExt(drawto, DX_KEYS + i * MINI_TILEX, DY_KEYS,
el2edimg(EL_KEY_1 + i));
}
}
}
-void DrawGameDoorValues()
+void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
+ int key_bits)
{
+ int key[MAX_NUM_KEYS];
int i;
+ for (i = 0; i < MAX_NUM_KEYS; i++)
+ key[i] = key_bits & (1 << i);
+
DrawGameValue_Level(level_nr);
- for (i = 0; i < MAX_PLAYERS; i++)
- DrawGameValue_Keys(&stored_player[i]);
+ DrawGameValue_Emeralds(emeralds);
+ DrawGameValue_Dynamite(dynamite);
+ DrawGameValue_Score(score);
+ DrawGameValue_Time(time);
+
+ DrawGameValue_Keys(key);
+}
+
+void DrawGameDoorValues()
+{
+ int i;
+
+ if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
+ {
+ DrawGameDoorValues_EM();
+
+ return;
+ }
+
+ DrawGameValue_Level(level_nr);
DrawGameValue_Emeralds(local_player->gems_still_needed);
DrawGameValue_Dynamite(local_player->inventory_size);
DrawGameValue_Score(local_player->score);
DrawGameValue_Time(TimeLeft);
+
+ for (i = 0; i < MAX_PLAYERS; i++)
+ DrawGameValue_Keys(stored_player[i].key);
}
static void resolve_group_element(int group_element, int recursion_depth)
game.engine_version = (tape.playing ? tape.engine_version :
level.game_version);
+ /* ---------------------------------------------------------------------- */
+ /* set flags for bugs and changes according to active game engine version */
+ /* ---------------------------------------------------------------------- */
+
+ /*
+ Type of bug/change:
+ Before 3.1.0, custom elements that "change when pushing" changed directly
+ after the player started pushing them (until then handled in "DigField()").
+ Since 3.1.0, these custom elements are not changed until the "pushing"
+ move of the element is finished (now handled in "ContinueMoving()").
+
+ Affected levels/tapes:
+ The first condition is generally needed for all levels/tapes before version
+ 3.1.0, which might use the old behaviour before it was changed; known tapes
+ that are affected are some tapes from the level set "Walpurgis Gardens" by
+ Jamie Cullen.
+ The second condition is an exception from the above case and is needed for
+ the special case of tapes recorded with game (not engine!) version 3.1.0 or
+ above (including some development versions of 3.1.0), but before it was
+ known that this change would break tapes like the above and was fixed in
+ 3.1.1, so that the changed behaviour was active although the engine version
+ while recording maybe was before 3.1.0. There is at least one tape that is
+ affected by this exception, which is the tape for the one-level set "Bug
+ Machine" by Juergen Bonhagen.
+ */
+
+ game.use_bug_change_when_pushing =
+ (game.engine_version < VERSION_IDENT(3,1,0,0) &&
+ !(tape.playing &&
+ tape.game_version >= VERSION_IDENT(3,1,0,0) &&
+ tape.game_version < VERSION_IDENT(3,1,1,0)));
+
+ /* ---------------------------------------------------------------------- */
+
/* dynamically adjust element properties according to game engine version */
InitElementPropertiesEngine(game.engine_version);
/* ---------- initialize player's initial move delay --------------------- */
+#if USE_NEW_MOVE_DELAY
+ /* dynamically adjust player properties according to level information */
+ game.initial_move_delay_value =
+ (level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED);
+
+ /* dynamically adjust player properties according to game engine version */
+ game.initial_move_delay = (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
+ game.initial_move_delay_value : 0);
+#else
/* dynamically adjust player properties according to game engine version */
game.initial_move_delay =
(game.engine_version <= VERSION_IDENT(2,0,1,0) ? INITIAL_MOVE_DELAY_ON :
/* dynamically adjust player properties according to level information */
game.initial_move_delay_value =
(level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED);
+#endif
/* ---------- initialize player's initial push delay --------------------- */
{
if (IS_SP_ELEMENT(i))
{
+#if USE_NEW_MOVE_STYLE
+ /* set SP push delay to just enough to push under a falling zonk */
+ int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
+
+ element_info[i].push_delay_fixed = delay;
+ element_info[i].push_delay_random = 0;
+#else
element_info[i].push_delay_fixed = 6; /* just enough to escape ... */
element_info[i].push_delay_random = 0; /* ... from falling zonk */
+#endif
}
}
}
player->lights_still_needed = 0;
player->friends_still_needed = 0;
- for (j = 0; j < MAX_KEYS; j++)
+ for (j = 0; j < MAX_NUM_KEYS; j++)
player->key[j] = FALSE;
player->dynabomb_count = 0;
player->use_murphy_graphic = FALSE;
- player->block_last_field = FALSE;
+ player->block_last_field = FALSE; /* initialized in InitPlayerField() */
+ player->block_delay = -1; /* initialized in InitPlayerField() */
+
player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
player->actual_frame_counter = 0;
player->move_delay_reset_counter = 0;
- player->push_delay = 0;
+#if USE_NEW_PUSH_DELAY
+ player->push_delay = -1; /* initialized when pushing starts */
+ player->push_delay_value = game.initial_push_delay_value;
+#else
+ player->push_delay = 0;
player->push_delay_value = game.initial_push_delay_value;
+#endif
player->drop_delay = 0;
#endif
ZX = ZY = -1;
+ ExitX = ExitY = -1;
FrameCounter = 0;
TimeFrames = 0;
player->element_nr = some_player->element_nr;
#endif
+#if USE_NEW_BLOCK_STYLE
+ player->block_last_field = some_player->block_last_field;
+ player->block_delay = some_player->block_delay;
+#endif
+
StorePlayer[jx][jy] = player->element_nr;
player->jx = player->last_jx = jx;
player->jy = player->last_jy = jy;
CloseDoor(DOOR_CLOSE_1);
- DrawLevel();
- DrawAllPlayers();
+ /* !!! FIX THIS (START) !!! */
+ if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
+ {
+ InitGameEngine_EM();
+ }
+ else
+ {
+ DrawLevel();
+ DrawAllPlayers();
- /* after drawing the level, correct some elements */
- if (game.timegate_time_left == 0)
- CloseAllOpenTimegates();
+ /* after drawing the level, correct some elements */
+ if (game.timegate_time_left == 0)
+ CloseAllOpenTimegates();
- if (setup.soft_scrolling)
- BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
+ if (setup.soft_scrolling)
+ BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
- redraw_mask |= REDRAW_FROM_BACKBUFFER;
- FadeToFront();
+ redraw_mask |= REDRAW_FROM_BACKBUFFER;
+ FadeToFront();
+ }
+ /* !!! FIX THIS (END) !!! */
/* copy default game door content to main double buffer */
BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
#endif
}
+void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
+{
+ /* this is used for non-R'n'D game engines to update certain engine values */
+
+ /* needed to determine if sounds are played within the visible screen area */
+ scroll_x = actual_scroll_x;
+ scroll_y = actual_scroll_y;
+}
+
void InitMovDir(int x, int y)
{
int i, element = Feld[x][y];
}
/* close exit door after last player */
- if ((Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
- Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN) && AllPlayersGone)
+ if (AllPlayersGone && ExitX >= 0 && ExitY >= 0 &&
+ (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
+ Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN))
{
int element = Feld[ExitX][ExitY];
}
/* Hero disappears */
- DrawLevelField(ExitX, ExitY);
+ if (ExitX >= 0 && ExitY >= 0)
+ DrawLevelField(ExitX, ExitY);
+
BackToFront();
if (tape.playing)
return position;
}
+inline static int getElementMoveStepsize(int x, int y)
+{
+ int element = Feld[x][y];
+ int direction = MovDir[x][y];
+ int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
+ int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
+ int horiz_move = (dx != 0);
+ int sign = (horiz_move ? dx : dy);
+ int step = sign * element_info[element].move_stepsize;
+
+ /* special values for move stepsize for spring and things on conveyor belt */
+ if (horiz_move)
+ {
+#if 0
+ if (element == EL_SPRING)
+ step = sign * MOVE_STEPSIZE_NORMAL * 2;
+ else if (CAN_FALL(element) && !CAN_MOVE(element) &&
+ y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
+ step = sign * MOVE_STEPSIZE_NORMAL / 2;
+#else
+ if (CAN_FALL(element) &&
+ y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
+ step = sign * MOVE_STEPSIZE_NORMAL / 2;
+ else if (element == EL_SPRING)
+ step = sign * MOVE_STEPSIZE_NORMAL * 2;
+#endif
+ }
+
+ return step;
+}
+
void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
{
if (player->GfxAction != action || player->GfxDir != dir)
if (!WasJustMoving[x][y] || direction != MovDir[x][y])
ResetGfxAnimation(x, y);
+#if USE_CAN_MOVE_NOT_MOVING
+
+ MovDir[x][y] = direction;
+ GfxDir[x][y] = direction;
+ GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
+ ACTION_FALLING : ACTION_MOVING);
+
+ if (getElementMoveStepsize(x, y) != 0) /* moving or being moved */
+ {
+ if (Feld[newx][newy] == EL_EMPTY)
+ Feld[newx][newy] = EL_BLOCKED;
+
+ MovDir[newx][newy] = MovDir[x][y];
+ GfxFrame[newx][newy] = GfxFrame[x][y];
+ GfxRandom[newx][newy] = GfxRandom[x][y];
+ GfxAction[newx][newy] = GfxAction[x][y];
+ GfxDir[newx][newy] = GfxDir[x][y];
+ }
+
+#else
+
MovDir[newx][newy] = MovDir[x][y] = direction;
GfxDir[x][y] = direction;
GfxRandom[newx][newy] = GfxRandom[x][y];
GfxAction[newx][newy] = GfxAction[x][y];
GfxDir[newx][newy] = GfxDir[x][y];
+#endif
}
void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
{
ScrollPlayer(player, SCROLL_GO_ON);
ScrollScreen(NULL, SCROLL_GO_ON);
+
+#if USE_NEW_MOVE_DELAY
+ AdvanceFrameAndPlayerCounters(player->index_nr);
+#else
FrameCounter++;
+#endif
DrawPlayer(player);
return;
#endif
+#if 1
+ if (mode == EX_TYPE_NORMAL ||
+ mode == EX_TYPE_CENTER ||
+ mode == EX_TYPE_CROSS)
+ PlayLevelSoundAction(ex, ey, ACTION_EXPLODING);
+#else
if (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER)
PlayLevelSoundAction(ex, ey, ACTION_EXPLODING);
+#endif
/* remove things displayed in background while burning dynamite */
if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
DrawLevelField(x, y);
+
+ /* uncrumble neighbour fields, if needed */
+ if (element == EL_INVISIBLE_SAND)
+ DrawLevelFieldCrumbledSandNeighbours(x, y);
}
else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
element == EL_INVISIBLE_WALL_ACTIVE ||
Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
DrawLevelField(x, y);
+
+ /* re-crumble neighbour fields, if needed */
+ if (element == EL_INVISIBLE_SAND)
+ DrawLevelFieldCrumbledSandNeighbours(x, y);
}
}
}
Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
}
-inline static int getElementMoveStepsize(int x, int y)
-{
- int element = Feld[x][y];
- int direction = MovDir[x][y];
- int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
- int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
- int horiz_move = (dx != 0);
- int sign = (horiz_move ? dx : dy);
- int step = sign * element_info[element].move_stepsize;
-
- /* special values for move stepsize for spring and things on conveyor belt */
- if (horiz_move)
- {
-#if 0
- if (element == EL_SPRING)
- step = sign * MOVE_STEPSIZE_NORMAL * 2;
- else if (CAN_FALL(element) && !CAN_MOVE(element) &&
- y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
- step = sign * MOVE_STEPSIZE_NORMAL / 2;
-#else
- if (CAN_FALL(element) &&
- y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
- step = sign * MOVE_STEPSIZE_NORMAL / 2;
- else if (element == EL_SPRING)
- step = sign * MOVE_STEPSIZE_NORMAL * 2;
-#endif
- }
-
- return step;
-}
-
void Impact(int x, int y)
{
boolean lastline = (y == lev_fieldy-1);
xx = x + move_xy[MovDir[x][y]].x;
yy = y + move_xy[MovDir[x][y]].y;
+#if 1
+ /* !!! this bugfix breaks at least BD2K3, level 010 !!! [re-recorded] */
+ if (!IN_LEV_FIELD(xx, yy) ||
+ (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
+ MovDir[x][y] = old_move_dir;
+#else
if (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy]))
MovDir[x][y] = old_move_dir;
+#endif
MovDelay[x][y] = 0;
}
boolean can_turn_right =
CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
+#if USE_CAN_MOVE_NOT_MOVING
+ if (element_info[element].move_stepsize == 0) /* not moving */
+ return;
+#endif
+
if (move_pattern == MV_TURNING_LEFT)
MovDir[x][y] = left_dir;
else if (move_pattern == MV_TURNING_RIGHT)
boolean first_horiz = RND(2);
int new_move_dir = MovDir[x][y];
+#if USE_CAN_MOVE_NOT_MOVING
+ if (element_info[element].move_stepsize == 0) /* not moving */
+ {
+ first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
+ MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
+
+ return;
+ }
+#endif
+
MovDir[x][y] =
new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
Moving2Blocked(x, y, &newx, &newy);
MovDir[x][y] = new_move_dir;
if (old_move_dir != new_move_dir)
+ {
+#if 1
+ MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
+#else
MovDelay[x][y] = 9;
+#endif
+ }
}
}
can_fall_both = (can_fall_left && can_fall_right);
}
+#if USE_NEW_SP_SLIPPERY
+ /* !!! better use the same properties as for custom elements here !!! */
+ else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
+ can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
+ {
+ can_fall_right = FALSE; /* slip down on left side */
+ can_fall_both = FALSE;
+ }
+#endif
+
+#if 1
+ if (can_fall_both)
+ {
+ if (game.emulation == EMU_BOULDERDASH ||
+ element == EL_BD_ROCK || element == EL_BD_DIAMOND)
+ can_fall_right = FALSE; /* slip down on left side */
+ else
+ can_fall_left = !(can_fall_right = RND(2));
+
+ can_fall_both = FALSE;
+ }
+#endif
+
if (can_fall_any)
{
+#if 0
if (can_fall_both &&
(game.emulation != EMU_BOULDERDASH &&
element != EL_BD_ROCK && element != EL_BD_DIAMOND))
can_fall_left = !(can_fall_right = RND(2));
+#endif
+ /* if not determined otherwise, prefer left side for slipping down */
InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
started_moving = TRUE;
}
#endif
Store[x][y] = EL_EMPTY;
- MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
+ MovPos[x][y] = 0;
+ MovDir[x][y] = 0;
+ MovDelay[x][y] = 0;
MovDelay[newx][newy] = 0;
if (CAN_CHANGE(element))
Pushed[x][y] = Pushed[newx][newy] = FALSE;
+#if 0
+ /* do this after checking for left-behind element */
ResetGfxAnimation(x, y); /* reset animation values for old field */
+#endif
#if 1
/* some elements can leave other elements behind after moving */
int move_leave_element = ei->move_leave_element;
Feld[x][y] = move_leave_element;
+
+#if USE_PREVIOUS_MOVE_DIR
+ if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
+ MovDir[x][y] = direction;
+#endif
+
InitField(x, y, FALSE);
if (GFX_CRUMBLED(Feld[x][y]))
ei->can_leave_element = FALSE;
#endif
+#if 1
+ /* do this after checking for left-behind element */
+ ResetGfxAnimation(x, y); /* reset animation values for old field */
+#endif
+
#if 0
/* 2.1.1 (does not work correctly for spring) */
if (!CAN_MOVE(element))
else if (element == EL_PENGUIN)
TestIfFriendTouchesBadThing(newx, newy);
-#if USE_NEW_MOVEMENT
+#if USE_NEW_MOVE_STYLE
#if 0
if (CAN_FALL(element) && direction == MV_DOWN &&
(newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)) &&
/* give the player one last chance (one more frame) to move away */
if (CAN_FALL(element) && direction == MV_DOWN &&
(newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)) &&
- !IS_PLAYER(x, newy + 1))
+ (!IS_PLAYER(x, newy + 1) ||
+ game.engine_version < VERSION_IDENT(3,1,1,0)))
Impact(x, newy);
#else
if (CAN_FALL(element) && direction == MV_DOWN &&
#endif
#if 1
+
+#if USE_PUSH_BUGFIX
+#if 1
+ if (pushed_by_player && !game.use_bug_change_when_pushing)
+#else
+ if (pushed_by_player && game.engine_version >= VERSION_IDENT(3,1,0,0))
+#endif
+#else
if (pushed_by_player)
+#endif
+
{
#if 1
int dig_side = MV_DIR_OPPOSITE(direction);
}
#endif
+void AdvanceFrameAndPlayerCounters(int player_nr)
+{
+ int i;
+
+ /* advance frame counters (global frame counter and time frame counter) */
+ FrameCounter++;
+ TimeFrames++;
+
+ /* advance player counters (counters for move delay, move animation etc.) */
+ for (i = 0; i < MAX_PLAYERS; i++)
+ {
+ boolean advance_player_counters = (player_nr == -1 || player_nr == i);
+ int move_frames =
+ MOVE_DELAY_NORMAL_SPEED / stored_player[i].move_delay_value;
+
+ if (!advance_player_counters) /* not all players may be affected */
+ continue;
+
+ stored_player[i].Frame += move_frames;
+
+ if (stored_player[i].MovPos != 0)
+ stored_player[i].StepFrame += move_frames;
+
+#if USE_NEW_MOVE_DELAY
+ if (stored_player[i].move_delay > 0)
+ stored_player[i].move_delay--;
+#endif
+
+#if USE_NEW_PUSH_DELAY
+ /* due to bugs in previous versions, counter must count up, not down */
+ if (stored_player[i].push_delay != -1)
+ stored_player[i].push_delay++;
+#endif
+
+ if (stored_player[i].drop_delay > 0)
+ stored_player[i].drop_delay--;
+ }
+}
+
void GameActions()
{
- static unsigned long action_delay = 0;
- unsigned long action_delay_value;
+ static unsigned long game_frame_delay = 0;
+ unsigned long game_frame_delay_value;
int magic_wall_x = 0, magic_wall_y = 0;
int i, x, y, element, graphic;
byte *recorded_player_action;
if (game_status != GAME_MODE_PLAYING)
return;
- action_delay_value =
+ game_frame_delay_value =
(tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
if (tape.playing && tape.warp_forward && !tape.pausing)
- action_delay_value = 0;
+ game_frame_delay_value = 0;
/* ---------- main game synchronization point ---------- */
- WaitUntilDelayReached(&action_delay, action_delay_value);
+ WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
if (network_playing && !network_player_action_received)
{
recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
#if 1
+ /* !!! CHECK THIS (tape.pausing is always FALSE here!) !!! */
if (recorded_player_action == NULL && tape.pausing)
return;
#endif
#endif
#if 1
- /* for downwards compatibility, the following code emulates a fixed bug that
+ /* for backwards compatibility, the following code emulates a fixed bug that
occured when pushing elements (causing elements that just made their last
pushing step to already (if possible) make their first falling step in the
same game frame, which is bad); this code is also needed to use the famous
Changed[x][y] = CE_BITMASK_DEFAULT;
ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
+#if USE_NEW_BLOCK_STYLE
+ /* this must be handled before main playfield loop */
+ if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
+ {
+ MovDelay[x][y]--;
+ if (MovDelay[x][y] <= 0)
+ RemoveField(x, y);
+ }
+#endif
+
#if DEBUG
if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
{
stored_player[0].StepFrame);
#endif
-#if 1
+#if USE_NEW_MOVE_DELAY
+ AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
+#else
FrameCounter++;
TimeFrames++;
if (stored_player[i].MovPos != 0)
stored_player[i].StepFrame += move_frames;
+#if USE_NEW_MOVE_DELAY
+ if (stored_player[i].move_delay > 0)
+ stored_player[i].move_delay--;
+#endif
+
if (stored_player[i].drop_delay > 0)
stored_player[i].drop_delay--;
}
local_player->show_envelope = 0;
}
#endif
+
+#if USE_NEW_RANDOMIZE
+ /* use random number generator in every frame to make it less predictable */
+ if (game.engine_version >= VERSION_IDENT(3,1,1,0))
+ RND(1);
+#endif
}
static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
return (IN_LEV_FIELD(jx, jy + 1) &&
(IS_FREE(jx, jy + 1) ||
+#if USE_NEW_BLOCK_STYLE
+ Feld[jx][jy + 1] == EL_PLAYER_IS_LEAVING ||
+#endif
(Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
!IS_WALKABLE_INSIDE(Feld[jx][jy]));
/* check if DigField() has caused relocation of the player */
if (player->jx != jx || player->jy != jy)
- return MF_NO_ACTION;
+ return MF_NO_ACTION; /* <-- !!! CHECK THIS [-> MF_ACTION ?] !!! */
StorePlayer[jx][jy] = 0;
player->last_jx = jx;
player->move_delay + player->move_delay_value);
#endif
+#if USE_NEW_MOVE_DELAY
+ if (player->move_delay > 0)
+#else
if (!FrameReached(&player->move_delay, player->move_delay_value))
+#endif
{
#if 0
printf("::: can NOT move\n");
printf("::: COULD move now\n");
#endif
+#if USE_NEW_MOVE_DELAY
+ player->move_delay = -1; /* set to "uninitialized" value */
+#endif
+
/* store if player is automatically moved to next field */
player->is_auto_moving = (player->programmed_action != MV_NO_MOVING);
{
ScrollPlayer(player, SCROLL_GO_ON);
ScrollScreen(NULL, SCROLL_GO_ON);
+
+#if USE_NEW_MOVE_DELAY
+ AdvanceFrameAndPlayerCounters(player->index_nr);
+#else
FrameCounter++;
+#endif
+
DrawAllPlayers();
BackToFront();
}
*/
player->is_moving = FALSE;
-#if USE_NEW_MOVEMENT
+#if USE_NEW_MOVE_STYLE
/* player is ALLOWED to move, but CANNOT move (something blocks his way) */
/* ensure that the player is also allowed to move in the next frame */
/* (currently, the player is forced to wait eight frames before he can try
again!!!) */
- player->move_delay = -1; /* allow direct movement in the next frame */
+ if (game.engine_version >= VERSION_IDENT(3,1,1,0))
+ player->move_delay = 0; /* allow direct movement in the next frame */
#endif
}
+#if USE_NEW_MOVE_DELAY
+ if (player->move_delay == -1) /* not yet initialized by DigField() */
+ player->move_delay = player->move_delay_value;
+#endif
+
if (game.engine_version < VERSION_IDENT(3,0,7,0))
{
TestIfHeroTouchesBadThing(jx, jy);
player->actual_frame_counter = FrameCounter;
player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
-#if USE_NEW_MOVEMENT
- if (player->block_last_field &&
+#if 0
+ printf("::: %06d: %d,%d: %d (%d) [%d]\n",
+ FrameCounter,
+ last_jx, last_jy, Feld[last_jx][last_jy], EL_EXPLOSION,
+ player->block_delay);
+#endif
+
+#if USE_NEW_BLOCK_STYLE
+
+#if 0
+ if (player->block_delay <= 0)
+ printf("::: ALERT! block_delay == %d\n", player->block_delay);
+#endif
+
+ if (player->block_delay > 0 &&
+ Feld[last_jx][last_jy] == EL_EMPTY)
+ {
+ Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
+ MovDelay[last_jx][last_jy] = player->block_delay + 1;
+ }
+#else
+#if USE_NEW_MOVE_STYLE
+ if ((game.engine_version < VERSION_IDENT(3,1,1,0) ||
+ player->block_last_field) &&
Feld[last_jx][last_jy] == EL_EMPTY)
Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
#else
if (Feld[last_jx][last_jy] == EL_EMPTY)
Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
#endif
+#endif
#if 0
DrawPlayer(player);
player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
+#if USE_NEW_BLOCK_STYLE
+#else
if (!player->block_last_field &&
Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
+#if 1
+ RemoveField(last_jx, last_jy);
+#else
Feld[last_jx][last_jy] = EL_EMPTY;
+#endif
+#endif
/* before DrawPlayer() to draw correct player graphic for this case */
if (player->MovPos == 0)
}
#endif
+#if USE_NEW_BLOCK_STYLE
+#else
if (player->block_last_field &&
Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
+#if 1
+ RemoveField(last_jx, last_jy);
+#else
Feld[last_jx][last_jy] = EL_EMPTY;
+#endif
+#endif
player->last_jx = jx;
player->last_jy = jy;
if (mode == DF_NO_PUSH) /* player just stopped pushing */
{
player->is_switching = FALSE;
+#if USE_NEW_PUSH_DELAY
+ player->push_delay = -1;
+#else
player->push_delay = 0;
+#endif
return MF_NO_ACTION;
}
return MF_NO_ACTION; /* player cannot walk here due to gravity */
#endif
- if (IS_GATE(element))
+ if (IS_RND_GATE(element))
{
- if (!player->key[element - EL_GATE_1])
+ if (!player->key[RND_GATE_NR(element)])
return MF_NO_ACTION;
}
- else if (IS_GATE_GRAY(element))
+ else if (IS_RND_GATE_GRAY(element))
{
- if (!player->key[element - EL_GATE_1_GRAY])
+ if (!player->key[RND_GATE_GRAY_NR(element)])
return MF_NO_ACTION;
}
else if (element == EL_EXIT_OPEN ||
if (IS_EM_GATE(element))
{
- if (!player->key[element - EL_EM_GATE_1])
+ if (!player->key[EM_GATE_NR(element)])
return MF_NO_ACTION;
}
else if (IS_EM_GATE_GRAY(element))
{
- if (!player->key[element - EL_EM_GATE_1_GRAY])
+ if (!player->key[EM_GATE_GRAY_NR(element)])
return MF_NO_ACTION;
}
else if (IS_SP_PORT(element))
{
player->dynabomb_xl = TRUE;
}
- else if ((element >= EL_KEY_1 && element <= EL_KEY_4) ||
- (element >= EL_EM_KEY_1 && element <= EL_EM_KEY_4))
+ else if (IS_KEY(element))
{
- int key_nr = (element >= EL_KEY_1 && element <= EL_KEY_4 ?
- element - EL_KEY_1 : element - EL_EM_KEY_1);
+ player->key[KEY_NR(element)] = TRUE;
- player->key[key_nr] = TRUE;
-
- DrawGameValue_Keys(player);
+ DrawGameValue_Keys(player->key);
redraw_mask |= REDRAW_DOOR_1;
}
if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
return MF_NO_ACTION;
+#if USE_NEW_PUSH_DELAY
+
+#if 0
+ if ( (player->push_delay == -1) != (player->push_delay2 == 0) )
+ printf("::: ALERT: %d, %d [%d / %d]\n",
+ player->push_delay, player->push_delay2,
+ FrameCounter, FrameCounter / 50);
+#endif
+
+ if (player->push_delay == -1) /* new pushing; restart delay */
+ player->push_delay = 0;
+#else
if (player->push_delay == 0) /* new pushing; restart delay */
player->push_delay = FrameCounter;
+#endif
+
+#if USE_NEW_PUSH_DELAY
+#if 0
+ if ( (player->push_delay > 0) != (!xxx_fr) )
+ printf("::: PUSH BUG! %d, (%d -> %d) %d [%d / %d]\n",
+ player->push_delay,
+ xxx_pdv2, player->push_delay2, player->push_delay_value,
+ FrameCounter, FrameCounter / 50);
+#endif
+#if 0
+ if (player->push_delay > 0 &&
+ !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
+ element != EL_SPRING && element != EL_BALLOON)
+#else
+ /* !!! */
+ if (player->push_delay < player->push_delay_value &&
+ !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
+ element != EL_SPRING && element != EL_BALLOON)
+#endif
+
+#else
if (!FrameReached(&player->push_delay, player->push_delay_value) &&
!(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
element != EL_SPRING && element != EL_BALLOON)
+#endif
{
/* make sure that there is no move delay before next try to push */
+#if USE_NEW_MOVE_DELAY
+ if (game.engine_version >= VERSION_IDENT(3,0,7,1))
+ player->move_delay = 0;
+#else
if (game.engine_version >= VERSION_IDENT(3,0,7,1))
player->move_delay = INITIAL_MOVE_DELAY_OFF;
+#endif
return MF_NO_ACTION;
}
else
player->push_delay_value = -1; /* get new value later */
+#if USE_PUSH_BUGFIX
+ /* now: check for element change _after_ element has been pushed! */
+#if 1
+ if (game.use_bug_change_when_pushing)
+#else
+ if (game.engine_version < VERSION_IDENT(3,1,0,0))
+#endif
+ {
+ CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
+ player->index_bit, dig_side);
+ CheckTriggeredElementChangeByPlayer(x,y,element,CE_OTHER_GETS_PUSHED,
+ player->index_bit, dig_side);
+ }
+
+#else
+
#if 1
/* check for element change _after_ element has been pushed! */
#else
#if 1
- /* !!! TEST ONLY !!! */
+ /* !!! TEST ONLY !!! */
CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
player->index_bit, dig_side);
CheckTriggeredElementChangeByPlayer(x, y, element,CE_OTHER_GETS_PUSHED,
CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
player->index_bit, dig_side);
#endif
+#endif
+
#endif
break;
return MF_NO_ACTION;
}
+#if USE_NEW_PUSH_DELAY
+ player->push_delay = -1;
+#else
player->push_delay = 0;
+#endif
if (Feld[x][y] != element) /* really digged/collected something */
player->is_collecting = !player->is_digging;
PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
}
+void PlayLevelSound_EM(int x, int y, int element_em, int sample)
+{
+ int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
+
+#if 0
+ if (sample == SAMPLE_bug)
+ printf("::: PlayLevelSound_EM: %d, %d: %d\n", x, y, sample);
+#endif
+
+ switch (sample)
+ {
+ case SAMPLE_blank:
+ PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
+ break;
+
+ case SAMPLE_roll:
+ PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
+ break;
+
+ case SAMPLE_stone:
+ PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
+ break;
+
+ case SAMPLE_nut:
+ PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
+ break;
+
+ case SAMPLE_crack:
+ PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
+ break;
+
+ case SAMPLE_bug:
+ PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
+ break;
+
+ case SAMPLE_tank:
+ PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
+ break;
+
+ case SAMPLE_android_clone:
+ PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
+ break;
+
+ case SAMPLE_android_move:
+ PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
+ break;
+
+ case SAMPLE_spring:
+ PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
+ break;
+
+ case SAMPLE_slurp:
+ PlayLevelSoundElementAction(x, y, element, ACTION_SLURPED_BY_SPRING);
+ break;
+
+ case SAMPLE_eater:
+ PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
+ break;
+
+ case SAMPLE_eater_eat:
+ PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
+ break;
+
+ case SAMPLE_alien:
+ PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
+ break;
+
+ case SAMPLE_collect:
+ PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
+ break;
+
+ case SAMPLE_diamond:
+ PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
+ break;
+
+ case SAMPLE_squash:
+ /* !!! CHECK THIS !!! */
+#if 1
+ PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
+#else
+ PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
+#endif
+ break;
+
+ case SAMPLE_wonderfall:
+ PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
+ break;
+
+ case SAMPLE_drip:
+ PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
+ break;
+
+ case SAMPLE_push:
+ PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
+ break;
+
+ case SAMPLE_dirt:
+ PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
+ break;
+
+ case SAMPLE_acid:
+ PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
+ break;
+
+ case SAMPLE_ball:
+ PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
+ break;
+
+ case SAMPLE_grow:
+ PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
+ break;
+
+ case SAMPLE_wonder:
+ PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
+ break;
+
+ case SAMPLE_door:
+ PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
+ break;
+
+ case SAMPLE_exit_open:
+ PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
+ break;
+
+ case SAMPLE_exit_leave:
+ PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
+ break;
+
+ case SAMPLE_dynamite:
+ PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
+ break;
+
+ case SAMPLE_tick:
+ PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
+ break;
+
+ case SAMPLE_press:
+ PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
+ break;
+
+ case SAMPLE_wheel:
+ PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
+ break;
+
+ case SAMPLE_boom:
+ PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
+ break;
+
+ case SAMPLE_die:
+ PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
+ break;
+
+ case SAMPLE_time:
+ PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
+ break;
+
+ default:
+ PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
+ break;
+ }
+}
+
void RaiseScore(int value)
{
local_player->score += value;
case EL_KEY_2:
case EL_KEY_3:
case EL_KEY_4:
+ case EL_EM_KEY_1:
+ case EL_EM_KEY_2:
+ case EL_EM_KEY_3:
+ case EL_EM_KEY_4:
+ case EL_EMC_KEY_5:
+ case EL_EMC_KEY_6:
+ case EL_EMC_KEY_7:
+ case EL_EMC_KEY_8:
RaiseScore(level.score[SC_KEY]);
break;
default: