/* EXPERIMENTAL STUFF */
#define USE_NEW_AMOEBA_CODE FALSE
+/* EXPERIMENTAL STUFF */
+#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 * 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
+
/* for DigField() */
#define DF_NO_PUSH 0
#define DF_DIG 1
/* values for other actions */
#define MOVE_STEPSIZE_NORMAL (TILEX / MOVE_DELAY_NORMAL_SPEED)
+#define GET_DX_FROM_DIR(d) ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
+#define GET_DY_FROM_DIR(d) ((d) == MV_UP ? -1 : (d) == MV_DOWN ? 1 : 0)
+
#define INIT_GFX_RANDOM() (SimpleRND(1000000))
#define GET_NEW_PUSH_DELAY(e) ( (element_info[e].push_delay_fixed) + \
((e) == EL_TRIGGER_ELEMENT ? (ch)->actual_trigger_element : \
(e) == EL_TRIGGER_PLAYER ? (ch)->actual_trigger_player : (e))
+#define GET_VALID_PLAYER_ELEMENT(e) \
+ ((e) >= EL_PLAYER_1 && (e) <= EL_PLAYER_4 ? (e) : EL_PLAYER_1)
+
#define CAN_GROW_INTO(e) \
- (e == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
+ ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
#define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition) \
(IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
/* 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);
}
access_direction_list[] =
{
- { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
- { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
- { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
- { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
- { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
- { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
- { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
- { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
- { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
- { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
- { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
-
- { EL_SP_PORT_LEFT, MV_RIGHT },
- { EL_SP_PORT_RIGHT, MV_LEFT },
- { EL_SP_PORT_UP, MV_DOWN },
- { EL_SP_PORT_DOWN, MV_UP },
- { EL_SP_PORT_HORIZONTAL, MV_LEFT | MV_RIGHT },
- { EL_SP_PORT_VERTICAL, MV_UP | MV_DOWN },
- { EL_SP_PORT_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
- { EL_SP_GRAVITY_PORT_LEFT, MV_RIGHT },
- { EL_SP_GRAVITY_PORT_RIGHT, MV_LEFT },
- { EL_SP_GRAVITY_PORT_UP, MV_DOWN },
- { EL_SP_GRAVITY_PORT_DOWN, MV_UP },
-
- { EL_UNDEFINED, MV_NO_MOVING }
+ { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
+ { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
+ { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
+ { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
+ { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
+ { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
+ { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
+ { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
+ { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
+ { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
+ { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
+
+ { EL_SP_PORT_LEFT, MV_RIGHT },
+ { EL_SP_PORT_RIGHT, MV_LEFT },
+ { EL_SP_PORT_UP, MV_DOWN },
+ { EL_SP_PORT_DOWN, MV_UP },
+ { EL_SP_PORT_HORIZONTAL, MV_LEFT | MV_RIGHT },
+ { EL_SP_PORT_VERTICAL, MV_UP | MV_DOWN },
+ { EL_SP_PORT_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
+ { EL_SP_GRAVITY_PORT_LEFT, MV_RIGHT },
+ { EL_SP_GRAVITY_PORT_RIGHT, MV_LEFT },
+ { EL_SP_GRAVITY_PORT_UP, MV_DOWN },
+ { EL_SP_GRAVITY_PORT_DOWN, MV_UP },
+ { EL_SP_GRAVITY_ON_PORT_LEFT, MV_RIGHT },
+ { EL_SP_GRAVITY_ON_PORT_RIGHT, MV_LEFT },
+ { EL_SP_GRAVITY_ON_PORT_UP, MV_DOWN },
+ { EL_SP_GRAVITY_ON_PORT_DOWN, MV_UP },
+ { EL_SP_GRAVITY_OFF_PORT_LEFT, MV_RIGHT },
+ { EL_SP_GRAVITY_OFF_PORT_RIGHT, MV_LEFT },
+ { EL_SP_GRAVITY_OFF_PORT_UP, MV_DOWN },
+ { EL_SP_GRAVITY_OFF_PORT_DOWN, MV_UP },
+
+ { EL_UNDEFINED, MV_NO_MOVING }
};
static unsigned long trigger_events[MAX_NUM_ELEMENTS];
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;
}
Feld[x][y] = EL_EMPTY;
+
player->jx = player->last_jx = x;
player->jy = player->last_jy = y;
}
DrawGameValue_Time(TimeLeft);
}
+void DrawGameDoorValues_EM(int emeralds, int dynamite, int score, int time)
+{
+ DrawGameValue_Emeralds(emeralds);
+ DrawGameValue_Dynamite(dynamite);
+ DrawGameValue_Score(score);
+ DrawGameValue_Time(time);
+}
+
static void resolve_group_element(int group_element, int recursion_depth)
{
static int group_nr;
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->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;
network_player_action_received = FALSE;
-#if defined(PLATFORM_UNIX)
+#if defined(NETWORK_AVALIABLE)
/* initial null action */
if (network_playing)
SendToServer_MovePlayer(MV_NO_MOVING);
AmoebaNr[x][y] = 0;
WasJustMoving[x][y] = 0;
WasJustFalling[x][y] = 0;
+ CheckCollision[x][y] = 0;
Stop[x][y] = FALSE;
Pushed[x][y] = FALSE;
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];
{
#if 1
/* use random direction as default start direction */
- if (game.engine_version >= VERSION_IDENT(3,1,0,2))
+ if (game.engine_version >= VERSION_IDENT(3,1,0,0))
MovDir[x][y] = 1 << RND(4);
#endif
ChangePage[x][y] = -1;
Pushed[x][y] = FALSE;
+#if 0
+ ExplodeField[x][y] = EX_TYPE_NONE;
+#endif
+
GfxElement[x][y] = EL_UNDEFINED;
GfxAction[x][y] = ACTION_DEFAULT;
GfxDir[x][y] = MV_NO_MOVING;
Bang(x, y);
}
-void RelocatePlayer(int x, int y, int element_raw)
+void DrawRelocatePlayer(struct PlayerInfo *player)
{
- int element = (element_raw == EL_SP_MURPHY ? EL_PLAYER_1 : element_raw);
- struct PlayerInfo *player = &stored_player[element - EL_PLAYER_1];
boolean ffwd_delay = (tape.playing && tape.fast_forward);
boolean no_delay = (tape.warp_forward);
int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
int wait_delay_value = (no_delay ? 0 : frame_delay_value);
- int old_jx, old_jy;
-
- if (player->GameOver) /* do not reanimate dead player */
- return;
-
- RemoveField(x, y); /* temporarily remove newly placed player */
- DrawLevelField(x, y);
-
- if (player->present)
- {
- while (player->MovPos)
- {
- ScrollPlayer(player, SCROLL_GO_ON);
- ScrollScreen(NULL, SCROLL_GO_ON);
- FrameCounter++;
-
- DrawPlayer(player);
-
- BackToFront();
- Delay(wait_delay_value);
- }
-
- DrawPlayer(player); /* needed here only to cleanup last field */
- DrawLevelField(player->jx, player->jy); /* remove player graphic */
-
- player->is_moving = FALSE;
- }
-
- old_jx = player->jx;
- old_jy = player->jy;
-
- Feld[x][y] = element;
- InitPlayerField(x, y, element, TRUE);
-
- if (player != local_player) /* do not visually relocate other players */
- return;
+ int jx = player->jx;
+ int jy = player->jy;
if (level.instant_relocation)
{
#if 1
int offset = (setup.scroll_delay ? 3 : 0);
- int jx = local_player->jx;
- int jy = local_player->jy;
if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
{
#if 1
#if 0
int offset = (setup.scroll_delay ? 3 : 0);
- int jx = local_player->jx;
- int jy = local_player->jy;
#endif
int scroll_xx = -999, scroll_yy = -999;
Delay(wait_delay_value);
}
#endif
+
+ DrawPlayer(player);
+ BackToFront();
+ Delay(wait_delay_value);
+ }
+}
+
+void RelocatePlayer(int jx, int jy, int el_player_raw)
+{
+#if 1
+ int el_player = GET_VALID_PLAYER_ELEMENT(el_player_raw);
+#else
+ int el_player = (el_player_raw == EL_SP_MURPHY ? EL_PLAYER_1 :el_player_raw);
+#endif
+ struct PlayerInfo *player = &stored_player[el_player - EL_PLAYER_1];
+ boolean ffwd_delay = (tape.playing && tape.fast_forward);
+ boolean no_delay = (tape.warp_forward);
+ int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
+ int wait_delay_value = (no_delay ? 0 : frame_delay_value);
+ int old_jx = player->jx;
+ int old_jy = player->jy;
+ int old_element = Feld[old_jx][old_jy];
+ int element = Feld[jx][jy];
+ boolean player_relocated = (old_jx != jx || old_jy != jy);
+
+ int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
+ int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
+#if 1
+ int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
+ int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
+ int leave_side_horiz = move_dir_horiz;
+ int leave_side_vert = move_dir_vert;
+#else
+ static int trigger_sides[4][2] =
+ {
+ /* enter side leave side */
+ { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
+ { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
+ { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
+ { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
+ };
+ int enter_side_horiz = trigger_sides[MV_DIR_BIT(move_dir_horiz)][0];
+ int enter_side_vert = trigger_sides[MV_DIR_BIT(move_dir_vert)][0];
+ int leave_side_horiz = trigger_sides[MV_DIR_BIT(move_dir_horiz)][1];
+ int leave_side_vert = trigger_sides[MV_DIR_BIT(move_dir_vert)][1];
+#endif
+ int enter_side = enter_side_horiz | enter_side_vert;
+ int leave_side = leave_side_horiz | leave_side_vert;
+
+ if (player->GameOver) /* do not reanimate dead player */
+ return;
+
+ if (!player_relocated) /* no need to relocate the player */
+ return;
+
+ if (IS_PLAYER(jx, jy)) /* player already placed at new position */
+ {
+ RemoveField(jx, jy); /* temporarily remove newly placed player */
+ DrawLevelField(jx, jy);
+ }
+
+ if (player->present)
+ {
+ while (player->MovPos)
+ {
+ ScrollPlayer(player, SCROLL_GO_ON);
+ ScrollScreen(NULL, SCROLL_GO_ON);
+
+#if USE_NEW_MOVE_DELAY
+ AdvanceFrameAndPlayerCounters(player->index_nr);
+#else
+ FrameCounter++;
+#endif
+
+ DrawPlayer(player);
+
+ BackToFront();
+ Delay(wait_delay_value);
+ }
+
+ DrawPlayer(player); /* needed here only to cleanup last field */
+ DrawLevelField(player->jx, player->jy); /* remove player graphic */
+
+ player->is_moving = FALSE;
+ }
+
+#if 1
+ if (IS_CUSTOM_ELEMENT(old_element))
+ CheckElementChangeByPlayer(old_jx, old_jy, old_element,
+ CE_LEFT_BY_PLAYER,
+ player->index_bit, leave_side);
+
+ CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
+ CE_OTHER_GETS_LEFT,
+ player->index_bit, leave_side);
+#endif
+
+ Feld[jx][jy] = el_player;
+ InitPlayerField(jx, jy, el_player, TRUE);
+
+ if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
+ {
+ Feld[jx][jy] = element;
+ InitField(jx, jy, FALSE);
}
+
+#if 1
+ if (player == local_player) /* only visually relocate local player */
+ DrawRelocatePlayer(player);
+#endif
+
+#if 1
+ TestIfHeroTouchesBadThing(jx, jy);
+ TestIfPlayerTouchesCustomElement(jx, jy);
+#endif
+
+#if 0
+ printf("::: %d,%d: %d\n", jx, jy-1, Changed[jx][jy-1]);
+#endif
+
+#if 0
+#if 0
+ /* needed to allow change of walkable custom element by entering player */
+ if (!(Changed[jx][jy] & CH_EVENT_BIT(CE_ENTERED_BY_PLAYER)))
+ Changed[jx][jy] = 0; /* allow another change (but prevent loop) */
+#else
+ /* needed to allow change of walkable custom element by entering player */
+ Changed[jx][jy] = 0; /* allow another change */
+#endif
+#endif
+
+#if 0
+ printf("::: player entering %d, %d from %s ...\n", jx, jy,
+ enter_side == MV_LEFT ? "left" :
+ enter_side == MV_RIGHT ? "right" :
+ enter_side == MV_UP ? "top" :
+ enter_side == MV_DOWN ? "bottom" : "oops! no idea!");
+#endif
+
+#if 1
+ if (IS_CUSTOM_ELEMENT(element))
+ CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
+ player->index_bit, enter_side);
+
+ CheckTriggeredElementChangeByPlayer(jx, jy, element,
+ CE_OTHER_GETS_ENTERED,
+ player->index_bit, enter_side);
+#endif
}
void Explode(int ex, int ey, int phase, int mode)
continue;
#else
/* indestructible elements can only explode in center (but not flames) */
+#if 1
+ if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
+ mode == EX_TYPE_BORDER)) ||
+ element == EL_FLAMES)
+ continue;
+#else
if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey)) ||
element == EL_FLAMES)
continue;
#endif
+#endif
#else
if ((IS_INDESTRUCTIBLE(element) &&
continue;
#endif
+#if 1
+ if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
+ (game.engine_version < VERSION_IDENT(3,1,0,0) ||
+ (x == ex && y == ey && mode != EX_TYPE_BORDER)))
+#else
if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
+#endif
{
if (IS_ACTIVE_BOMB(element))
{
/* re-activate things under the bomb like gate or penguin */
+#if 1
+ Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
+ Back[x][y] = 0;
+#else
Feld[x][y] = (Store[x][y] ? Store[x][y] : EL_EMPTY);
Store[x][y] = 0;
+#endif
+
+#if 0
+ printf("::: %d,%d: %d %s [%d, %d]\n", x, y, Feld[x][y],
+ element_info[Feld[x][y]].token_name,
+ Store[x][y], Store2[x][y]);
+#endif
}
continue;
#if 0
if (IS_INDESTRUCTIBLE(element))
Back[x][y] = element;
+#else
+#if 1
+#if 1
+ if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
+ (x != ex || y != ey || mode == EX_TYPE_BORDER))
+ Back[x][y] = element;
+#else
+ if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
+ (x != ex || y != ey))
+ Back[x][y] = element;
+#endif
#else
if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element))
Back[x][y] = element;
+#endif
#endif
/* ignite explodable elements reached by other explosion */
else
Store[x][y] = EL_EMPTY;
- if (x != ex || y != ey ||
- center_element == EL_AMOEBA_TO_DIAMOND || mode == EX_TYPE_BORDER)
+ if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
+ center_element == EL_AMOEBA_TO_DIAMOND)
Store2[x][y] = element;
+#if 0
+ printf("::: %d,%d: %d %s\n", x, y, Store2[x][y],
+ element_info[Store2[x][y]].token_name);
+#endif
+
#if 0
if (AmoebaNr[x][y] &&
(element == EL_AMOEBA_FULL ||
game.yamyam_content_nr =
(game.yamyam_content_nr + 1) % level.num_yamyam_contents;
+#if 0
+ printf("::: %d,%d: %d %s [%d]\n", ex + 1, ey, Feld[ex + 1][ey],
+ element_info[Feld[ex + 1][ey]].token_name, Store2[ex + 1][ey]);
+#endif
+
return;
}
#if 1
border_element = Store2[x][y];
+#if 1
+ if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
+ border_element = StorePlayer[x][y];
+#else
if (IS_PLAYER(x, y))
border_element = StorePlayer[x][y];
+#endif
+
+#if 0
+ printf("::: %d,%d: %d %s [%d]\n", x, y, border_element,
+ element_info[border_element].token_name, Store2[x][y]);
+#endif
#if 0
printf("::: phase == %d\n", phase);
boolean border_explosion = FALSE;
#if 1
+#if 1
+ if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
+ !PLAYER_EXPLOSION_PROTECTED(x, y))
+#else
if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present)
+#endif
#else
if (IS_PLAYER(x, y))
#endif
}
else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
{
+#if 0
+ printf("::: %d,%d: %d %s\n", x, y, border_element,
+ element_info[border_element].token_name);
+#endif
+
Feld[x][y] = Store2[x][y];
Store2[x][y] = 0;
Bang(x, y);
Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
#if 1
+#if 1
+ if (element != EL_EMPTY && element != EL_EXPLOSION &&
+ !IS_DIGGABLE(element) && !dynabomb_xl)
+ break;
+#else
if (element != EL_EMPTY && element != EL_EXPLOSION &&
!CAN_GROW_INTO(element) && !dynabomb_xl)
break;
+#endif
#else
/* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
if (element != EL_EMPTY && element != EL_EXPLOSION &&
Explode(x, y, EX_PHASE_START, EX_TYPE_CENTER);
break;
default:
+#if 1
+ if (element_info[element].explosion_type == EXPLODES_CROSS)
+#else
if (CAN_EXPLODE_CROSS(element))
+#endif
#if 1
Explode(x, y, EX_PHASE_START, EX_TYPE_CROSS);
#else
DynaExplode(x, y);
#endif
+#if 1
+ else if (element_info[element].explosion_type == EXPLODES_1X1)
+#else
else if (CAN_EXPLODE_1X1(element))
+#endif
Explode(x, y, EX_PHASE_START, EX_TYPE_CENTER);
else
Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
return;
}
+ /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
/* only reset graphic animation if graphic really changes after impact */
if (impact &&
el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
}
else if (impact && element == EL_PEARL)
{
+ ResetGfxAnimation(x, y);
+
Feld[x][y] = EL_PEARL_BREAKING;
PlayLevelSound(x, y, SND_PEARL_BREAKING);
return;
}
else if (smashed == EL_PEARL)
{
+ ResetGfxAnimation(x, y);
+
Feld[x][y + 1] = EL_PEARL_BREAKING;
PlayLevelSound(x, y, SND_PEARL_BREAKING);
return;
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;
}
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
+ }
}
}
#endif
}
#if 1
- else if ((game.engine_version < VERSION_IDENT(2,2,0,7) &&
- CAN_SMASH(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
- (Feld[x][y + 1] == EL_BLOCKED)) ||
+ else if ((game.engine_version >= VERSION_IDENT(3,1,0,0) &&
+ CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
+
(game.engine_version >= VERSION_IDENT(3,0,7,0) &&
CAN_SMASH(element) && WasJustFalling[x][y] &&
- (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))))
+ (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
+
+ (game.engine_version < VERSION_IDENT(2,2,0,7) &&
+ CAN_SMASH(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
+ (Feld[x][y + 1] == EL_BLOCKED)))
#else
#if 1
WasJustFalling[x][y] = 0;
#endif
+ CheckCollision[x][y] = 0;
+
+#if 0
+ if (IS_PLAYER(x, y + 1))
+ printf("::: we ARE now killing the player [%d]\n", FrameCounter);
+#endif
+
Impact(x, y);
}
else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
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;
}
}
/* not "else if" because of elements that can fall and move (EL_SPRING) */
- if (CAN_MOVE(element) && !started_moving)
+#if 0
+ if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NO_MOVING)
+#else
+ if (CAN_MOVE(element) && !started_moving)
+#endif
{
int move_pattern = element_info[element].move_pattern;
int newx, newy;
+#if 0
+#if DEBUG
+ if (MovDir[x][y] == MV_NO_MOVING)
+ {
+ printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
+ x, y, element, element_info[element].token_name);
+ printf("StartMoving(): This should never happen!\n");
+ }
+#endif
+#endif
+
Moving2Blocked(x, y, &newx, &newy);
#if 1
#endif
#if 1
+
+#if 1
+ if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
+ CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
+#else
if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
WasJustMoving[x][y] && IN_LEV_FIELD(newx, newy) &&
(Feld[newx][newy] == EL_BLOCKED || IS_PLAYER(newx, newy)))
+#endif
{
#if 0
printf("::: element %d '%s' WasJustMoving %d [%d, %d, %d, %d]\n",
WasJustMoving[x][y] = 0;
#endif
+ CheckCollision[x][y] = 0;
+
TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
#if 0
DrawLevelField(newx, newy);
}
+ /* if digged element was about to explode, prevent the explosion */
+ ExplodeField[newx][newy] = EX_TYPE_NONE;
+
PlayLevelSoundAction(x, y, action);
}
TurnRound(x, y);
+#if 0
+ if (move_pattern & MV_ANY_DIRECTION &&
+ move_pattern == MovDir[x][y])
+ {
+ int blocking_element =
+ (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
+
+#if 0
+ printf("::: '%s' is blocked by '%s'! [%d,%d -> %d,%d]\n",
+ element_info[element].token_name,
+ element_info[blocking_element].token_name,
+ x, y, newx, newy);
+#endif
+
+ CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
+ MovDir[x][y]);
+
+ element = Feld[x][y]; /* element might have changed */
+ }
+#endif
+
#if 1
if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
DrawLevelElementAnimation(x, y, element);
Feld[newx][newy] = element;
MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
- if (element == EL_MOLE)
+#if 1
+ if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
+ {
+ element = Feld[newx][newy] = EL_ACID;
+ }
+#endif
+ else if (element == EL_MOLE)
{
Feld[x][y] = EL_SAND;
Back[x][y] = Back[newx][newy] = 0;
}
+#if 0
else if (Store[x][y] == EL_ACID)
{
element = Feld[newx][newy] = EL_ACID;
}
+#endif
#if 0
else if (IS_CUSTOM_ELEMENT(element) && !IS_PLAYER(x, y) &&
ei->move_leave_element != EL_EMPTY &&
ResetGfxAnimation(x, y); /* reset animation values for old field */
#if 1
- if (IS_CUSTOM_ELEMENT(element) && !IS_PLAYER(x, y) &&
- ei->move_leave_element != EL_EMPTY &&
- (ei->move_leave_type == LEAVE_TYPE_UNLIMITED ||
- stored != EL_EMPTY))
+ /* some elements can leave other elements behind after moving */
+#if 1
+ if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
+ (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
+ (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
+#else
+ if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
+ (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
+ !IS_PLAYER(x, y))
+#endif
{
- /* some elements can leave other elements behind after moving */
+ int move_leave_element = ei->move_leave_element;
- Feld[x][y] = ei->move_leave_element;
+ Feld[x][y] = move_leave_element;
InitField(x, y, FALSE);
if (GFX_CRUMBLED(Feld[x][y]))
DrawLevelFieldCrumbledSandNeighbours(x, y);
+
+ if (ELEM_IS_PLAYER(move_leave_element))
+ RelocatePlayer(x, y, move_leave_element);
}
#endif
/* prevent elements on conveyor belt from moving on in last direction */
if (pushed_by_conveyor && CAN_FALL(element) &&
direction & MV_HORIZONTAL)
+ {
+#if 0
+ if (CAN_MOVE(element))
+ InitMovDir(newx, newy);
+ else
+ MovDir[newx][newy] = 0;
+#else
MovDir[newx][newy] = 0;
#endif
+ }
+#endif
if (!pushed_by_player)
{
+ int nextx = newx + dx, nexty = newy + dy;
+ boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
+
WasJustMoving[newx][newy] = 3;
if (CAN_FALL(element) && direction == MV_DOWN)
WasJustFalling[newx][newy] = 3;
+
+ if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
+ CheckCollision[newx][newy] = 2;
}
if (DONT_TOUCH(element)) /* object may be nasty to player or others */
else if (element == EL_PENGUIN)
TestIfFriendTouchesBadThing(newx, newy);
+#if USE_NEW_MOVE_STYLE
+#if 0
+ if (CAN_FALL(element) && direction == MV_DOWN &&
+ (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)) &&
+ IS_PLAYER(x, newy + 1))
+ printf("::: we would now kill the player [%d]\n", FrameCounter);
+#endif
+
+ /* 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) ||
+ game.engine_version < VERSION_IDENT(3,1,1,0)))
+ Impact(x, newy);
+#else
if (CAN_FALL(element) && direction == MV_DOWN &&
(newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)))
Impact(x, newy);
+#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);
+#else
+ static int trigger_sides[4] =
+ {
+ CH_SIDE_RIGHT, /* moving left */
+ CH_SIDE_LEFT, /* moving right */
+ CH_SIDE_BOTTOM, /* moving up */
+ CH_SIDE_TOP, /* moving down */
+ };
+ int dig_side = trigger_sides[MV_DIR_BIT(direction)];
+#endif
+ struct PlayerInfo *player = PLAYERINFO(x, y);
+
+ CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
+ player->index_bit, dig_side);
+ CheckTriggeredElementChangeByPlayer(newx,newy,element,CE_OTHER_GETS_PUSHED,
+ player->index_bit, dig_side);
+ }
+#endif
#if 1
TestIfElementTouchesCustomElement(x, y); /* empty or new element */
static void ChangeElementNowExt(int x, int y, int target_element)
{
int previous_move_direction = MovDir[x][y];
+#if 1
+ boolean add_player = (ELEM_IS_PLAYER(target_element) &&
+ IS_WALKABLE(Feld[x][y]));
+#else
+ boolean add_player = (ELEM_IS_PLAYER(target_element) &&
+ IS_WALKABLE(Feld[x][y]) &&
+ !IS_MOVING(x, y));
+#endif
/* check if element under player changes from accessible to unaccessible
(needed for special case of dropping element which then changes) */
if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
IS_ACCESSIBLE(Feld[x][y]) && !IS_ACCESSIBLE(target_element))
{
+#if 0
+ printf("::: BOOOM! [%d, '%s']\n", target_element,
+ element_info[target_element].token_name);
+#endif
+
Bang(x, y);
return;
}
- RemoveField(x, y);
- Feld[x][y] = target_element;
+#if 1
+ if (!add_player)
+#endif
+ {
+#if 1
+ if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
+ RemoveMovingField(x, y);
+ else
+ RemoveField(x, y);
- Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
+ Feld[x][y] = target_element;
+#else
+ RemoveField(x, y);
+ Feld[x][y] = target_element;
+#endif
- ResetGfxAnimation(x, y);
- ResetRandomAnimationValue(x, y);
+ ResetGfxAnimation(x, y);
+ ResetRandomAnimationValue(x, y);
- if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
- MovDir[x][y] = previous_move_direction;
+ if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
+ MovDir[x][y] = previous_move_direction;
#if 1
- InitField_WithBug1(x, y, FALSE);
+ InitField_WithBug1(x, y, FALSE);
#else
- InitField(x, y, FALSE);
- if (CAN_MOVE(Feld[x][y]))
- InitMovDir(x, y);
+ InitField(x, y, FALSE);
+ if (CAN_MOVE(Feld[x][y]))
+ InitMovDir(x, y);
#endif
- DrawLevelField(x, y);
+ DrawLevelField(x, y);
- if (GFX_CRUMBLED(Feld[x][y]))
- DrawLevelFieldCrumbledSandNeighbours(x, y);
+ if (GFX_CRUMBLED(Feld[x][y]))
+ DrawLevelFieldCrumbledSandNeighbours(x, y);
+ }
+
+#if 0
+ Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
+#endif
+#if 0
TestIfBadThingTouchesHero(x, y);
TestIfPlayerTouchesCustomElement(x, y);
TestIfElementTouchesCustomElement(x, y);
+#endif
+ /* "Changed[][]" not set yet to allow "entered by player" change one time */
if (ELEM_IS_PLAYER(target_element))
RelocatePlayer(x, y, target_element);
+
+#if 1
+ Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
+#endif
+
+#if 1
+ TestIfBadThingTouchesHero(x, y);
+ TestIfPlayerTouchesCustomElement(x, y);
+ TestIfElementTouchesCustomElement(x, y);
+#endif
}
static boolean ChangeElementNow(int x, int y, int element, int page)
{
struct ElementChangeInfo *change = &element_info[element].change_page[page];
int target_element;
+ int old_element = Feld[x][y];
/* always use default change event to prevent running into a loop */
if (ChangeEvent[x][y] == CE_BITMASK_DEFAULT)
Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
+#if 0
+ /* !!! indirect change before direct change !!! */
CheckTriggeredElementChangeByPage(x,y,Feld[x][y], CE_OTHER_IS_CHANGING,page);
+#endif
if (change->explode)
{
for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
{
boolean is_empty;
+ boolean is_walkable;
boolean is_diggable;
+ boolean is_collectible;
+ boolean is_removable;
boolean is_destructible;
int ex = x + xx - 1;
int ey = y + yy - 1;
continue;
}
+#if 0
+ if (Changed[ex][ey]) /* do not change already changed elements */
+ {
+ can_replace[xx][yy] = FALSE;
+ complete_replace = FALSE;
+
+ continue;
+ }
+#endif
+
e = Feld[ex][ey];
if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
#if 1
-#if 1
- is_empty = (IS_FREE(ex, ey) || (IS_FREE_OR_PLAYER(ex, ey) &&
- IS_WALKABLE(content_element)));
+#if 0
+ is_empty = (IS_FREE(ex, ey) ||
+ (IS_PLAYER(ex, ey) && IS_WALKABLE(content_element)) ||
+ (IS_WALKABLE(e) && ELEM_IS_PLAYER(content_element) &&
+ !IS_MOVING(ex, ey) && !IS_BLOCKED(ex, ey)));
+#else
+
+#if 0
+ is_empty = (IS_FREE(ex, ey) ||
+ (IS_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
#else
- is_empty = (IS_FREE(ex, ey) || (IS_PLAYER(ex, ey) &&
- IS_WALKABLE(content_element)));
+ is_empty = (IS_FREE(ex, ey) ||
+ (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
+#endif
+
#endif
- is_diggable = (is_empty || IS_DIGGABLE(e));
+
+ is_walkable = (is_empty || IS_WALKABLE(e));
+ is_diggable = (is_empty || IS_DIGGABLE(e));
+ is_collectible = (is_empty || IS_COLLECTIBLE(e));
is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
+ is_removable = (is_diggable || is_collectible);
can_replace[xx][yy] =
- ((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
- (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
- (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible));
+ (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
+ (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
+ (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
+ (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
+ (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
+ (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
+ !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
if (!can_replace[xx][yy])
complete_replace = FALSE;
PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
}
+#if 1
+ /* this uses direct change before indirect change */
+ CheckTriggeredElementChangeByPage(x,y,old_element,CE_OTHER_IS_CHANGING,page);
+#endif
+
return TRUE;
}
}
#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)
{
#endif
*/
-#if defined(PLATFORM_UNIX)
+#if defined(NETWORK_AVALIABLE)
/* last chance to get network player actions without main loop delay */
HandleNetworking();
#endif
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
stored_player[i].effective_action = stored_player[i].action;
}
-#if defined(PLATFORM_UNIX)
+#if defined(NETWORK_AVALIABLE)
if (network_playing)
SendToServer_MovePlayer(summarized_player_action);
#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)
{
WasJustMoving[x][y]--;
if (WasJustFalling[x][y] > 0)
WasJustFalling[x][y]--;
+ if (CheckCollision[x][y] > 0)
+ CheckCollision[x][y]--;
GfxFrame[x][y]++;
TimeFrames = 0;
TapeTime++;
- if (!level.use_step_counter)
+ for (i = 0; i < MAX_PLAYERS; i++)
{
- TimePlayed++;
+ struct PlayerInfo *player = &stored_player[i];
- for (i = 0; i < MAX_PLAYERS; i++)
+ if (SHIELD_ON(player))
{
- struct PlayerInfo *player = &stored_player[i];
-
- if (SHIELD_ON(player))
- {
- player->shield_normal_time_left--;
+ player->shield_normal_time_left--;
- if (player->shield_deadly_time_left > 0)
- player->shield_deadly_time_left--;
- }
+ if (player->shield_deadly_time_left > 0)
+ player->shield_deadly_time_left--;
}
+ }
+
+ if (!level.use_step_counter)
+ {
+ TimePlayed++;
if (TimeLeft > 0)
{
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)
#if 1
return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
+ IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
+#if 0
+ (!IS_SP_PORT(Feld[newx][newy]) || move_dir == MV_UP) &&
+#endif
+ (IS_DIGGABLE(Feld[newx][newy]) ||
+ IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
+ canPassField(newx, newy, move_dir)));
+#else
+#if 1
+ return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
+ IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
(IS_DIGGABLE(Feld[newx][newy]) ||
IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
canPassField(newx, newy, move_dir)));
+#else
+#if 1
+ return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
+ (IS_DIGGABLE_WITH_GRAVITY(Feld[newx][newy]) ||
+ IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
+ canPassField(newx, newy, move_dir)));
#else
return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
(IS_DIGGABLE(Feld[newx][newy]) ||
IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
(level.can_pass_to_walkable || IS_FREE(nextx, nexty)))));
#endif
+#endif
+#endif
}
static void CheckGravityMovement(struct PlayerInfo *player)
#else
#if 1
+
+#if 0
+ printf("::: %d <= %d < %d ?\n", player->move_delay, FrameCounter,
+ 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");
+#endif
+
return FALSE;
+ }
#else
if (!FrameReached(&player->move_delay, player->move_delay_value) &&
!(tape.playing && tape.file_version < FILE_VERSION_2_0))
return FALSE;
#endif
+#endif
+
+#if 0
+ 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 */
{
ScrollPlayer(player, SCROLL_GO_ON);
ScrollScreen(NULL, SCROLL_GO_ON);
+
+#if USE_NEW_MOVE_DELAY
+ AdvanceFrameAndPlayerCounters(player->index_nr);
+#else
FrameCounter++;
+#endif
+
DrawAllPlayers();
BackToFront();
}
if (moved & MF_MOVING)
{
+#if 0
+ printf("::: REALLY moves now\n");
+#endif
+
if (old_jx != jx && old_jy == jy)
player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
else if (old_jx == jx && old_jy != jy)
if (game.engine_version < VERSION_IDENT(3,1,0,0))
#endif
{
+ int move_direction = player->MovDir;
+#if 1
+ int enter_side = MV_DIR_OPPOSITE(move_direction);
+ int leave_side = move_direction;
+#else
static int trigger_sides[4][2] =
{
/* enter side leave side */
{ CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
{ CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
};
- int move_direction = player->MovDir;
int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
+#endif
+ int old_element = Feld[old_jx][old_jy];
+ int new_element = Feld[jx][jy];
#if 1
/* !!! TEST ONLY !!! */
- if (IS_CUSTOM_ELEMENT(Feld[old_jx][old_jy]))
- CheckElementChangeByPlayer(old_jx, old_jy, Feld[old_jx][old_jy],
+ if (IS_CUSTOM_ELEMENT(old_element))
+ CheckElementChangeByPlayer(old_jx, old_jy, old_element,
CE_LEFT_BY_PLAYER,
player->index_bit, leave_side);
- CheckTriggeredElementChangeByPlayer(old_jx, old_jy, Feld[old_jx][old_jy],
+ CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
CE_OTHER_GETS_LEFT,
player->index_bit, leave_side);
- if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
- CheckElementChangeByPlayer(jx, jy, Feld[jx][jy], CE_ENTERED_BY_PLAYER,
+ if (IS_CUSTOM_ELEMENT(new_element))
+ CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
player->index_bit, enter_side);
- CheckTriggeredElementChangeByPlayer(jx, jy, Feld[jx][jy],
+ CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
CE_OTHER_GETS_ENTERED,
player->index_bit, enter_side);
#endif
player->last_move_dir = MV_NO_MOVING;
*/
player->is_moving = FALSE;
+
+#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!!!) */
+
+ 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 (Feld[last_jx][last_jy] == EL_EMPTY)
- Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
-
#if 0
- DrawPlayer(player);
+ printf("::: %06d: %d,%d: %d (%d) [%d]\n",
+ FrameCounter,
+ last_jx, last_jy, Feld[last_jx][last_jy], EL_EXPLOSION,
+ player->block_delay);
#endif
- return;
- }
- else if (!FrameReached(&player->actual_frame_counter, 1))
- return;
+#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);
+#endif
+
+ return;
+ }
+ else if (!FrameReached(&player->actual_frame_counter, 1))
+ return;
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 (game.engine_version >= VERSION_IDENT(3,1,0,0))
#endif
{
+ int move_direction = player->MovDir;
+#if 1
+ int enter_side = MV_DIR_OPPOSITE(move_direction);
+ int leave_side = move_direction;
+#else
static int trigger_sides[4][2] =
{
/* enter side leave side */
{ CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
{ CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
};
- int move_direction = player->MovDir;
int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
+#endif
int old_jx = last_jx;
int old_jy = last_jy;
+ int old_element = Feld[old_jx][old_jy];
+ int new_element = Feld[jx][jy];
#if 1
/* !!! TEST ONLY !!! */
- if (IS_CUSTOM_ELEMENT(Feld[old_jx][old_jy]))
- CheckElementChangeByPlayer(old_jx, old_jy, Feld[old_jx][old_jy],
+ if (IS_CUSTOM_ELEMENT(old_element))
+ CheckElementChangeByPlayer(old_jx, old_jy, old_element,
CE_LEFT_BY_PLAYER,
player->index_bit, leave_side);
- CheckTriggeredElementChangeByPlayer(old_jx, old_jy, Feld[old_jx][old_jy],
+ CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
CE_OTHER_GETS_LEFT,
player->index_bit, leave_side);
- if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
- CheckElementChangeByPlayer(jx, jy, Feld[jx][jy], CE_ENTERED_BY_PLAYER,
+ if (IS_CUSTOM_ELEMENT(new_element))
+ CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
player->index_bit, enter_side);
- CheckTriggeredElementChangeByPlayer(jx, jy, Feld[jx][jy],
+ CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
CE_OTHER_GETS_ENTERED,
player->index_bit, enter_side);
#endif
TestIfHeroTouchesBadThing(jx, jy);
TestIfPlayerTouchesCustomElement(jx, jy);
#if 1
- TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
+#if 1
+ /* 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)
+#endif
+ TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
#endif
if (!player->active)
TimePlayed++;
- for (i = 0; i < MAX_PLAYERS; i++)
- {
- struct PlayerInfo *player = &stored_player[i];
-
- if (SHIELD_ON(player))
- {
- player->shield_normal_time_left--;
-
- if (player->shield_deadly_time_left > 0)
- player->shield_deadly_time_left--;
- }
- }
-
if (TimeLeft > 0)
{
TimeLeft--;
boolean change_center_element = FALSE;
int center_element_change_page = 0;
int center_element = Feld[x][y]; /* should always be non-moving! */
- int border_trigger_element;
+ int border_trigger_element = EL_UNDEFINED;
int i, j;
for (i = 0; i < NUM_DIRECTIONS; i++)
int oldx, int oldy, int x, int y,
int real_dx, int real_dy, int mode)
{
- static int trigger_sides[4] =
- {
- CH_SIDE_RIGHT, /* moving left */
- CH_SIDE_LEFT, /* moving right */
- CH_SIDE_BOTTOM, /* moving up */
- CH_SIDE_TOP, /* moving down */
- };
#if 0
boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
#endif
+ boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
+ boolean player_was_pushing = player->is_pushing;
int jx = oldx, jy = oldy;
int dx = x - jx, dy = y - jy;
int nextx = x + dx, nexty = y + dy;
dy == -1 ? MV_UP :
dy == +1 ? MV_DOWN : MV_NO_MOVING);
int opposite_direction = MV_DIR_OPPOSITE(move_direction);
+#if 1
+ int dig_side = MV_DIR_OPPOSITE(move_direction);
+#else
+ static int trigger_sides[4] =
+ {
+ CH_SIDE_RIGHT, /* moving left */
+ CH_SIDE_LEFT, /* moving right */
+ CH_SIDE_BOTTOM, /* moving up */
+ CH_SIDE_TOP, /* moving down */
+ };
int dig_side = trigger_sides[MV_DIR_BIT(move_direction)];
+#endif
int old_element = Feld[jx][jy];
int element;
- if (player->MovPos == 0)
+ if (is_player) /* function can also be called by EL_PENGUIN */
{
- player->is_digging = FALSE;
- player->is_collecting = FALSE;
- }
+ if (player->MovPos == 0)
+ {
+ player->is_digging = FALSE;
+ player->is_collecting = FALSE;
+ }
- if (player->MovPos == 0) /* last pushing move finished */
- player->is_pushing = FALSE;
+ if (player->MovPos == 0) /* last pushing move finished */
+ player->is_pushing = FALSE;
- if (mode == DF_NO_PUSH) /* player just stopped pushing */
- {
- player->is_switching = FALSE;
- player->push_delay = 0;
+ 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;
+ }
}
if (IS_MOVING(x, y) || IS_PLAYER(x, y))
element = Feld[x][y];
+ if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
+ return MF_NO_ACTION;
+
if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
game.engine_version >= VERSION_IDENT(2,2,0,0))
return MF_NO_ACTION;
+#if 1
+ if (game.gravity && is_player && !player->is_auto_moving &&
+ canFallDown(player) && move_direction != MV_DOWN &&
+ !canMoveToValidFieldWithGravity(jx, jy, move_direction))
+ return MF_NO_ACTION; /* player cannot walk here due to gravity */
+#endif
+
+#if 0
+ if (element == EL_EMPTY_SPACE &&
+ game.gravity && !player->is_auto_moving &&
+ canFallDown(player) && move_direction != MV_DOWN)
+ return MF_NO_ACTION; /* player cannot walk here due to gravity */
+#endif
+
switch (element)
{
#if 0
default:
+#if 1
+ if (IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
+#else
if (IS_WALKABLE(element))
+#endif
{
+ int sound_element = SND_ELEMENT(element);
int sound_action = ACTION_WALKING;
+#if 0
if (!ACCESS_FROM(element, opposite_direction))
return MF_NO_ACTION; /* field not accessible from this direction */
+#endif
-#if 1
+#if 0
if (element == EL_EMPTY_SPACE &&
game.gravity && !player->is_auto_moving &&
canFallDown(player) && move_direction != MV_DOWN)
}
/* play sound from background or player, whatever is available */
- if (element_info[element].sound[sound_action] != SND_UNDEFINED)
- PlayLevelSoundElementAction(x, y, element, sound_action);
+ if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
+ PlayLevelSoundElementAction(x, y, sound_element, sound_action);
else
PlayLevelSoundElementAction(x, y, player->element_nr, sound_action);
break;
}
+#if 1
+ else if (IS_PASSABLE(element) && canPassField(x, y, move_direction))
+#else
else if (IS_PASSABLE(element))
+#endif
{
-#if 1
+#if 0
if (!canPassField(x, y, move_direction))
return MF_NO_ACTION;
#else
+#if 0
#if 1
if (!IN_LEV_FIELD(nextx, nexty) || IS_PLAYER(nextx, nexty) ||
!IS_WALKABLE_FROM(Feld[nextx][nexty], move_direction) ||
if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
return MF_NO_ACTION;
#endif
+#endif
#if 1
if (!ACCESS_FROM(element, opposite_direction))
element == EL_SP_GRAVITY_PORT_UP ||
element == EL_SP_GRAVITY_PORT_DOWN)
game.gravity = !game.gravity;
+ else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
+ element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
+ element == EL_SP_GRAVITY_ON_PORT_UP ||
+ element == EL_SP_GRAVITY_ON_PORT_DOWN)
+ game.gravity = TRUE;
+ else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
+ element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
+ element == EL_SP_GRAVITY_OFF_PORT_UP ||
+ element == EL_SP_GRAVITY_OFF_PORT_DOWN)
+ game.gravity = FALSE;
}
/* automatically move to the next field with double speed */
{
RemoveField(x, y);
- if (mode != DF_SNAP)
+ if (is_player && mode != DF_SNAP)
{
GfxElement[x][y] = element;
player->is_collecting = TRUE;
ShowEnvelope(element - EL_ENVELOPE_1);
#endif
}
- else if (IS_DROPPABLE(element)) /* can be collected and dropped */
+ else if (IS_DROPPABLE(element) ||
+ IS_THROWABLE(element)) /* can be collected and dropped */
{
int i;
RaiseScoreElement(element);
PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
- CheckTriggeredElementChangeByPlayer(x, y, element,
- CE_OTHER_GETS_COLLECTED,
- player->index_bit, dig_side);
+ if (is_player)
+ CheckTriggeredElementChangeByPlayer(x, y, element,
+ CE_OTHER_GETS_COLLECTED,
+ player->index_bit, dig_side);
#if 1
if (mode == DF_SNAP)
#endif
#if 1
- if (game.engine_version >= VERSION_IDENT(3,0,7,1))
+
+#if 1
+ if (game.engine_version >= VERSION_IDENT(3,1,0,0))
+ {
+ if (player->push_delay_value == -1 || !player_was_pushing)
+ player->push_delay_value = GET_NEW_PUSH_DELAY(element);
+ }
+ else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
{
if (player->push_delay_value == -1)
player->push_delay_value = GET_NEW_PUSH_DELAY(element);
}
+#else
+ if (game.engine_version >= VERSION_IDENT(3,0,7,1))
+ {
+ if (player->push_delay_value == -1 || !player_was_pushing)
+ player->push_delay_value = GET_NEW_PUSH_DELAY(element);
+ }
+#endif
else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
{
if (!player->is_pushing)
#endif
#if 0
- printf("::: push delay: %ld [%d, %d] [%d]\n",
- player->push_delay_value, FrameCounter, game.engine_version,
- player->is_pushing);
+ printf("::: push delay: %ld -> %ld [%d, %d] [%d / %d] [%d '%s': %d]\n",
+ player->push_delay, player->push_delay_value,
+ FrameCounter, game.engine_version,
+ player_was_pushing, player->is_pushing,
+ element, element_info[element].token_name,
+ GET_NEW_PUSH_DELAY(element));
#endif
player->is_pushing = TRUE;
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
- /* !!! TEST ONLY !!! */
+ 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 !!! */
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);
CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
player->index_bit, dig_side);
+#endif
+#endif
+
#endif
break;
else if (IS_SWITCHABLE(element))
{
if (PLAYER_SWITCHING(player, x, y))
+ {
+ CheckTriggeredElementChangeByPlayer(x,y, element,
+ CE_OTHER_GETS_PRESSED,
+ player->index_bit, dig_side);
+
return MF_ACTION;
+ }
player->is_switching = TRUE;
player->switch_x = x;
#endif
}
+ CheckTriggeredElementChangeByPlayer(x, y, element,
+ CE_OTHER_IS_SWITCHING,
+ player->index_bit, dig_side);
+
+ CheckTriggeredElementChangeByPlayer(x,y, element,CE_OTHER_GETS_PRESSED,
+ player->index_bit, dig_side);
+
return MF_ACTION;
}
else
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;
dy == +1 ? MV_DOWN : MV_NO_MOVING);
#if 0
- if (player->MovPos)
+ if (player->MovPos != 0)
return FALSE;
#else
- if (player->MovPos && game.engine_version >= VERSION_IDENT(2,2,0,0))
+ if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
return FALSE;
#endif
player->is_collecting = FALSE;
}
+#if 1
+ if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
+ DrawLevelField(player->last_jx, player->last_jy);
+#endif
+
DrawLevelField(x, y);
+
+#if 0
BackToFront();
+#endif
return TRUE;
}
boolean DropElement(struct PlayerInfo *player)
{
+ int old_element, new_element;
+ int dropx = player->jx, dropy = player->jy;
+ int drop_direction = player->MovDir;
+#if 1
+ int drop_side = drop_direction;
+#else
static int trigger_sides[4] =
{
CH_SIDE_LEFT, /* dropping left */
CH_SIDE_TOP, /* dropping up */
CH_SIDE_BOTTOM, /* dropping down */
};
- int jx = player->jx, jy = player->jy;
- int drop_direction = player->MovDir;
int drop_side = trigger_sides[MV_DIR_BIT(drop_direction)];
- int old_element = Feld[jx][jy];
+#endif
int drop_element = (player->inventory_size > 0 ?
player->inventory_element[player->inventory_size - 1] :
player->inventory_infinite_element != EL_UNDEFINED ?
player->dynabombs_left > 0 ?
EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
EL_UNDEFINED);
- int new_element = drop_element; /* default: element does not change */
+
+ if (IS_THROWABLE(drop_element))
+ {
+ dropx += GET_DX_FROM_DIR(drop_direction);
+ dropy += GET_DY_FROM_DIR(drop_direction);
+
+ if (!IN_LEV_FIELD(dropx, dropy))
+ return FALSE;
+ }
+
+ old_element = Feld[dropx][dropy]; /* old element at dropping position */
+ new_element = drop_element; /* default: no change when dropping */
/* check if player is active, not moving and ready to drop */
if (!player->active || player->MovPos || player->drop_delay > 0)
#endif
if (old_element != EL_EMPTY)
- Back[jx][jy] = old_element; /* store old element on this field */
+ Back[dropx][dropy] = old_element; /* store old element on this field */
- ResetGfxAnimation(jx, jy);
- ResetRandomAnimationValue(jx, jy);
+ ResetGfxAnimation(dropx, dropy);
+ ResetRandomAnimationValue(dropx, dropy);
if (player->inventory_size > 0 ||
player->inventory_infinite_element != EL_UNDEFINED)
new_element = EL_SP_DISK_RED_ACTIVE;
}
- Feld[jx][jy] = new_element;
+ Feld[dropx][dropy] = new_element;
- if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
- DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0);
+ if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
+ DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
+ el2img(Feld[dropx][dropy]), 0);
- PlayLevelSoundAction(jx, jy, ACTION_DROPPING);
+ PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
#if 1
/* needed if previous element just changed to "empty" in the last frame */
- Changed[jx][jy] = 0; /* allow another change */
+ Changed[dropx][dropy] = 0; /* allow another change */
#endif
#if 1
/* !!! TEST ONLY !!! */
- CheckElementChangeByPlayer(jx, jy, new_element, CE_DROPPED_BY_PLAYER,
+ CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
player->index_bit, drop_side);
- CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
+ CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
CE_OTHER_GETS_DROPPED,
player->index_bit, drop_side);
#else
- CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
+ CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
CE_OTHER_GETS_DROPPED,
player->index_bit, drop_side);
- CheckElementChangeByPlayer(jx, jy, new_element, CE_DROPPED_BY_PLAYER,
+ CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
player->index_bit, drop_side);
#endif
- TestIfElementTouchesCustomElement(jx, jy);
+ TestIfElementTouchesCustomElement(dropx, dropy);
}
else /* player is dropping a dyna bomb */
{
new_element = EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr;
#endif
- Feld[jx][jy] = new_element;
+ Feld[dropx][dropy] = new_element;
- if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
- DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0);
+ if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
+ DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
+ el2img(Feld[dropx][dropy]), 0);
- PlayLevelSoundAction(jx, jy, ACTION_DROPPING);
+ PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
}
#if 1
- if (Feld[jx][jy] == new_element) /* uninitialized unless CE change */
+ if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
{
#if 1
- InitField_WithBug1(jx, jy, FALSE);
+ InitField_WithBug1(dropx, dropy, FALSE);
#else
- InitField(jx, jy, FALSE);
- if (CAN_MOVE(Feld[jx][jy]))
- InitMovDir(jx, jy);
+ InitField(dropx, dropy, FALSE);
+ if (CAN_MOVE(Feld[dropx][dropy]))
+ InitMovDir(dropx, dropy);
#endif
}
- new_element = Feld[jx][jy]; /* element might have changed */
+ new_element = Feld[dropx][dropy]; /* element might have changed */
if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
element_info[new_element].move_pattern == MV_WHEN_DROPPED)
#if 0
int move_stepsize = element_info[new_element].move_stepsize;
#endif
- int direction, dx, dy, nextx, nexty;
+ int move_direction, nextx, nexty;
if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
- MovDir[jx][jy] = player->MovDir;
+ MovDir[dropx][dropy] = drop_direction;
+
+ move_direction = MovDir[dropx][dropy];
+ nextx = dropx + GET_DX_FROM_DIR(move_direction);
+ nexty = dropy + GET_DY_FROM_DIR(move_direction);
- direction = MovDir[jx][jy];
- dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
- dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
- nextx = jx + dx;
- nexty = jy + dy;
+#if 1
+ Changed[dropx][dropy] = 0; /* allow another change */
+ CheckCollision[dropx][dropy] = 2;
+#else
- if (IN_LEV_FIELD(nextx, nexty) && IS_FREE(nextx, nexty))
+ if (IN_LEV_FIELD_AND_IS_FREE(nextx, nexty))
{
#if 0
- WasJustMoving[jx][jy] = 3;
+ WasJustMoving[dropx][dropy] = 3;
#else
- InitMovingField(jx, jy, direction);
- ContinueMoving(jx, jy);
+#if 1
+ InitMovingField(dropx, dropy, move_direction);
+ ContinueMoving(dropx, dropy);
+#endif
#endif
}
+#if 0
+ /* !!! commented out from 3.1.0-4 to 3.1.0-5 !!! */
else
{
- Changed[jx][jy] = 0; /* allow another change */
+ Changed[dropx][dropy] = 0; /* allow another change */
#if 1
- TestIfElementHitsCustomElement(jx, jy, direction);
+ TestIfElementHitsCustomElement(dropx, dropy, move_direction);
#else
- CheckElementChangeBySide(jx, jy, new_element, touched_element,
- CE_HITTING_SOMETHING, direction);
+ CheckElementChangeBySide(dropx, dropy, new_element, touched_element,
+ CE_HITTING_SOMETHING, move_direction);
#endif
}
+#endif
+
+#endif
#if 0
player->drop_delay = 2 * TILEX / move_stepsize + 1;
static void PlayLevelSoundElementAction(int x, int y, int element, int action)
{
- int sound_effect = element_info[element].sound[action];
+ int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
if (sound_effect != SND_UNDEFINED)
PlayLevelSound(x, y, sound_effect);
static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
int action)
{
- int sound_effect = element_info[element].sound[action];
+ int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
PlayLevelSound(x, y, sound_effect);
static void PlayLevelSoundActionIfLoop(int x, int y, int action)
{
- int sound_effect = element_info[Feld[x][y]].sound[action];
+ int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
PlayLevelSound(x, y, sound_effect);
static void StopLevelSoundActionIfLoop(int x, int y, int action)
{
- int sound_effect = element_info[Feld[x][y]].sound[action];
+ int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
StopSound(sound_effect);
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, EL_BUG, ACTION_MOVING);
+ break;
+
+ case SAMPLE_tank:
+ PlayLevelSoundElementAction(x, y, EL_SPACESHIP, ACTION_MOVING);
+ break;
+
+ case SAMPLE_android:
+ PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
+ break;
+
+ case SAMPLE_spring:
+ PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
+ break;
+
+ case SAMPLE_slurp:
+ PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
+ break;
+
+ case SAMPLE_eater:
+ PlayLevelSoundElementAction(x, y, EL_YAMYAM, ACTION_WAITING);
+ 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:
+ PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
+ break;
+
+ case SAMPLE_wonderfall:
+ PlayLevelSoundElementAction(x, y, EL_MAGIC_WALL, 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:
+ PlayLevelSound(x, y, SND_ACID_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:
+ 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:
+ PlaySoundStereo(SND_MAGIC_WALL_ACTIVE, SOUND_MIDDLE);
+ break;
+
+ case SAMPLE_boom:
+ PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
+ break;
+
+ case SAMPLE_time:
+ PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
+ break;
+
+ case SAMPLE_die:
+ PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
+ break;
+
+ default:
+ PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
+ break;
+ }
+}
+
void RaiseScore(int value)
{
local_player->score += value;
Request("Do you really want to quit the game ?",
REQ_ASK | REQ_STAY_CLOSED))
{
-#if defined(PLATFORM_UNIX)
+#if defined(NETWORK_AVALIABLE)
if (options.network)
SendToServer_StopPlaying();
else
case GAME_CTRL_ID_PAUSE:
if (options.network)
{
-#if defined(PLATFORM_UNIX)
+#if defined(NETWORK_AVALIABLE)
if (tape.pausing)
SendToServer_ContinuePlaying();
else
case GAME_CTRL_ID_PLAY:
if (tape.pausing)
{
-#if defined(PLATFORM_UNIX)
+#if defined(NETWORK_AVALIABLE)
if (options.network)
SendToServer_ContinuePlaying();
else