(IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
(condition) || \
(DONT_COLLIDE_WITH(e) && \
- IS_FREE_OR_PLAYER(x, y))))
+ IS_PLAYER(x, y) && \
+ !PLAYER_ENEMY_PROTECTED(x, y))))
+
+#define ELEMENT_CAN_ENTER_FIELD_GENERIC_X(e, x, y, condition) \
+ (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
+ (condition) || \
+ (CAN_MOVE_INTO_ACID(e) && \
+ Feld[x][y] == EL_ACID) || \
+ (DONT_COLLIDE_WITH(e) && \
+ IS_PLAYER(x, y) && \
+ !PLAYER_ENEMY_PROTECTED(x, y))))
#define ELEMENT_CAN_ENTER_FIELD_GENERIC_2(x, y, condition) \
(IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
Feld[x][y] == EL_EXIT_OPEN || \
Feld[x][y] == EL_ACID))
-#if 1
-#define MAZE_RUNNER_CAN_ENTER_FIELD(x, y) \
- (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
-#else
-#define MAZE_RUNNER_CAN_ENTER_FIELD(x, y) \
- (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
- IS_FOOD_DARK_YAMYAM(Feld[x][y])))
-#endif
+#define GROUP_NR(e) ((e) - EL_GROUP_START)
+#define MOVE_ENTER_EL(e) (element_info[e].move_enter_element)
+#define IS_IN_GROUP(e, nr) (element_info[e].in_group[nr] == TRUE)
+#define IS_IN_GROUP_EL(e, ge) (IS_IN_GROUP(e, (ge) - EL_GROUP_START))
+
+#define IS_EQUAL_OR_IN_GROUP(e, ge) \
+ (IS_GROUP_ELEMENT(ge) ? IS_IN_GROUP(e, GROUP_NR(ge)) : (e) == (ge))
+
+#define CE_ENTER_FIELD_COND(e, x, y) \
+ (!IS_PLAYER(x, y) && \
+ (Feld[x][y] == EL_ACID || \
+ IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e))))
+
+#define CE_ENTER_FIELD_COND_X(e, x, y) \
+ (!IS_PLAYER(x, y) && \
+ IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
+
+#define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y) \
+ ELEMENT_CAN_ENTER_FIELD_GENERIC_X(e, x, y, CE_ENTER_FIELD_COND_X(e, x, y))
#define MOLE_CAN_ENTER_FIELD(x, y, condition) \
(IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || (condition)))
static void ScrollPlayer(struct PlayerInfo *, int);
static void ScrollScreen(struct PlayerInfo *, int);
+int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
+
static void InitBeltMovement(void);
static void CloseAllOpenTimegates(void);
static void CheckGravityMovement(struct PlayerInfo *);
-static void KillHeroUnlessProtected(int, int);
+static void KillHeroUnlessEnemyProtected(int, int);
+static void KillHeroUnlessExplosionProtected(int, int);
static void TestIfPlayerTouchesCustomElement(int, int);
static void TestIfElementTouchesCustomElement(int, int);
{ EL_UNDEFINED, 0 },
};
+struct
+{
+ int element;
+ int direction;
+}
+tube_access[] =
+{
+ { 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_UNDEFINED, 0 }
+};
+
static unsigned long trigger_events[MAX_NUM_ELEMENTS];
#define IS_AUTO_CHANGING(e) (element_info[e].change_events & \
player->present = TRUE;
+ player->block_last_field = (element == EL_SP_MURPHY ?
+ level.sp_block_last_field :
+ level.block_last_field);
+
if (!options.network || player->connected)
{
player->active = TRUE;
InitPlayerField(x, y, element, init_game);
break;
+ case EL_SOKOBAN_FIELD_PLAYER:
+ element = Feld[x][y] = EL_PLAYER_1;
+ InitField(x, y, init_game);
+
+ element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
+ InitField(x, y, init_game);
+ break;
+
+ case EL_SOKOBAN_FIELD_EMPTY:
+ local_player->sokobanfields_still_needed++;
+ break;
+
case EL_STONEBLOCK:
if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
Feld[x][y] = EL_ACID_POOL_TOPLEFT;
local_player->lights_still_needed++;
break;
- case EL_SOKOBAN_FIELD_EMPTY:
- local_player->sokobanfields_still_needed++;
- break;
-
case EL_PENGUIN:
local_player->friends_still_needed++;
break;
default:
if (IS_CUSTOM_ELEMENT(element) && CAN_MOVE(element))
InitMovDir(x, y);
+ else if (IS_GROUP_ELEMENT(element))
+ {
+ struct ElementGroupInfo *group = element_info[element].group;
+ int last_anim_random_frame = gfx.anim_random_frame;
+ int element_pos;
+
+ if (group->choice_mode == ANIM_RANDOM)
+ gfx.anim_random_frame = RND(group->num_elements_resolved);
+
+ element_pos = getAnimationFrame(group->num_elements_resolved, 1,
+ group->choice_mode, 0,
+ group->choice_pos);
+
+ if (group->choice_mode == ANIM_RANDOM)
+ gfx.anim_random_frame = last_anim_random_frame;
+
+ group->choice_pos++;
+
+ Feld[x][y] = group->element_resolved[element_pos];
+
+ InitField(x, y, init_game);
+ }
break;
}
}
+static inline void InitField_WithBug1(int x, int y, boolean init_game)
+{
+ InitField(x, y, init_game);
+
+ /* not needed to call InitMovDir() -- already done by InitField()! */
+ if (game.engine_version < VERSION_IDENT(3,0,9,0) &&
+ CAN_MOVE(Feld[x][y]))
+ InitMovDir(x, y);
+}
+
+static inline void InitField_WithBug2(int x, int y, boolean init_game)
+{
+ int old_element = Feld[x][y];
+
+ InitField(x, y, init_game);
+
+ /* not needed to call InitMovDir() -- already done by InitField()! */
+ if (game.engine_version < VERSION_IDENT(3,0,9,0) &&
+ CAN_MOVE(old_element) &&
+ (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
+ InitMovDir(x, y);
+
+ /* this case is in fact a combination of not less than three bugs:
+ first, it calls InitMovDir() for elements that can move, although this is
+ already done by InitField(); then, it checks the element that was at this
+ field _before_ the call to InitField() (which can change it)
+
+ */
+}
+
void DrawGameDoorValues()
{
int i, j;
int2str(TimeLeft, 3), FONT_TEXT_2);
}
+static void resolve_group_element(int group_element, int recursion_depth)
+{
+ static int group_nr;
+ static struct ElementGroupInfo *group;
+ struct ElementGroupInfo *actual_group = element_info[group_element].group;
+ int i;
+
+ if (recursion_depth > NUM_GROUP_ELEMENTS) /* recursion too deep */
+ {
+ Error(ERR_WARN, "recursion too deep when resolving group element %d",
+ group_element - EL_GROUP_START + 1);
+
+ /* replace element which caused too deep recursion by question mark */
+ group->element_resolved[group->num_elements_resolved++] = EL_UNKNOWN;
+
+ return;
+ }
+
+ if (recursion_depth == 0) /* initialization */
+ {
+ group = element_info[group_element].group;
+ group_nr = group_element - EL_GROUP_START;
+
+ group->num_elements_resolved = 0;
+ group->choice_pos = 0;
+ }
+
+ for (i = 0; i < actual_group->num_elements; i++)
+ {
+ int element = actual_group->element[i];
+
+ if (group->num_elements_resolved == NUM_FILE_ELEMENTS)
+ break;
+
+ if (IS_GROUP_ELEMENT(element))
+ resolve_group_element(element, recursion_depth + 1);
+ else
+ {
+ group->element_resolved[group->num_elements_resolved++] = element;
+ element_info[element].in_group[group_nr] = TRUE;
+ }
+ }
+
+#if 0
+ if (recursion_depth == 0 && group_element <= EL_GROUP_4)
+ {
+ printf("::: group %d: %d resolved elements\n",
+ group_element - EL_GROUP_START, group->num_elements_resolved);
+ for (i = 0; i < group->num_elements_resolved; i++)
+ printf("::: - %d ['%s']\n", group->element_resolved[i],
+ element_info[group->element_resolved[i]].token_name);
+ }
+#endif
+}
+
/*
=============================================================================
printf(" => game.engine_version == %06d\n", game.engine_version);
#endif
+ /* ---------- recursively resolve group elements ------------------------- */
+
+ for (i = 0; i < MAX_NUM_ELEMENTS; i++)
+ for (j = 0; j < NUM_GROUP_ELEMENTS; j++)
+ element_info[i].in_group[j] = FALSE;
+
+ for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
+ resolve_group_element(EL_GROUP_START + i, 0);
+
/* ---------- initialize player's initial move delay --------------------- */
/* dynamically adjust player properties according to game engine version */
{
int trigger_element = ei->change_page[j].trigger_element;
- trigger_events[trigger_element] |= ei->change_page[j].events;
+ if (IS_GROUP_ELEMENT(trigger_element))
+ {
+ struct ElementGroupInfo *group = element_info[trigger_element].group;
+
+ for (k = 0; k < group->num_elements_resolved; k++)
+ trigger_events[group->element_resolved[k]]
+ |= ei->change_page[j].events;
+ }
+ else
+ trigger_events[trigger_element] |= ei->change_page[j].events;
}
}
}
element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
}
+ /* set push delay value for Supaplex elements for newer engine versions */
+ if (game.engine_version >= VERSION_IDENT(3,0,9,0))
+ {
+ for (i = 0; i < MAX_NUM_ELEMENTS; i++)
+ {
+ if (IS_SP_ELEMENT(i))
+ {
+ element_info[i].push_delay_fixed = 6;
+ element_info[i].push_delay_random = 0;
+ }
+ }
+ }
+
/* ---------- initialize move stepsize ----------------------------------- */
/* initialize move stepsize values to default */
element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
}
+ /* ---------- initialize move dig/leave ---------------------------------- */
+
+ for (i = 0; i < MAX_NUM_ELEMENTS; i++)
+ {
+ element_info[i].can_leave_element = FALSE;
+ element_info[i].can_leave_element_last = FALSE;
+ }
+
/* ---------- initialize gem count --------------------------------------- */
/* initialize gem count values for each element */
for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
element_info[collect_count_list[i].element].collect_count =
collect_count_list[i].count;
+
+ /* ---------- initialize access direction -------------------------------- */
+
+ /* initialize access direction values to default */
+ for (i = 0; i < MAX_NUM_ELEMENTS; i++)
+ if (!IS_CUSTOM_ELEMENT(i))
+ element_info[i].access_direction = MV_ALL_DIRECTIONS;
+
+ /* set access direction value for certain elements from pre-defined list */
+ for (i = 0; tube_access[i].element != EL_UNDEFINED; i++)
+ element_info[tube_access[i].element].access_direction =
+ tube_access[i].direction;
}
player->use_murphy_graphic = FALSE;
+ player->block_last_field = FALSE;
+
player->actual_frame_counter = 0;
player->step_counter = 0;
player->inventory_size = 0;
- DigField(player, 0, 0, 0, 0, DF_NO_PUSH);
+ DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
SnapField(player, 0, 0);
player->LevelSolved = FALSE;
ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
ExplodePhase[x][y] = 0;
+ ExplodeDelay[x][y] = 0;
ExplodeField[x][y] = EX_NO_EXPLOSION;
RunnerVisit[x][y] = 0;
emulate_sb ? EMU_SOKOBAN :
emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
+ /* initialize explosion and ignition delay */
+ for (i = 0; i < MAX_NUM_ELEMENTS; i++)
+ {
+ if (!IS_CUSTOM_ELEMENT(i))
+ {
+ int num_phase = 9;
+ int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
+ int last_phase = num_phase * delay;
+ int half_phase = (num_phase / 2) * delay;
+
+ element_info[i].explosion_delay = last_phase;
+ element_info[i].ignition_delay = half_phase;
+
+ if (i == EL_BLACK_ORB)
+ element_info[i].ignition_delay = 1;
+ }
+
+ if (element_info[i].explosion_delay < 2) /* !!! check again !!! */
+ element_info[i].explosion_delay = 2;
+
+ if (element_info[i].ignition_delay < 1) /* !!! check again !!! */
+ element_info[i].ignition_delay = 1;
+ }
+
/* correct non-moving belts to start moving left */
for (i = 0; i < 4; i++)
if (game.belt_dir[i] == MV_NO_MOVING)
{
player->present = TRUE;
player->active = TRUE;
+
some_player->present = FALSE;
+ some_player->active = FALSE;
StorePlayer[jx][jy] = player->element_nr;
player->jx = player->last_jx = jx;
if (tape.playing)
{
- /* when playing a tape, eliminate all players who do not participate */
+ /* when playing a tape, eliminate all players which do not participate */
for (i = 0; i < MAX_PLAYERS; i++)
{
int jx = player->jx, jy = player->jy;
player->active = FALSE;
+ player->present = FALSE;
+
StorePlayer[jx][jy] = 0;
Feld[jx][jy] = EL_EMPTY;
}
default:
if (IS_CUSTOM_ELEMENT(element))
{
- if (element_info[element].move_direction_initial != MV_NO_MOVING)
- MovDir[x][y] = element_info[element].move_direction_initial;
- else if (element_info[element].move_pattern == MV_ALL_DIRECTIONS ||
- element_info[element].move_pattern == MV_TURNING_LEFT ||
- element_info[element].move_pattern == MV_TURNING_RIGHT ||
- element_info[element].move_pattern == MV_TURNING_LEFT_RIGHT ||
- element_info[element].move_pattern == MV_TURNING_RIGHT_LEFT ||
- element_info[element].move_pattern == MV_TURNING_RANDOM)
+ struct ElementInfo *ei = &element_info[element];
+ int move_direction_initial = ei->move_direction_initial;
+ int move_pattern = ei->move_pattern;
+
+ if (move_direction_initial == MV_START_PREVIOUS)
+ {
+ if (MovDir[x][y] != MV_NO_MOVING)
+ return;
+
+ move_direction_initial = MV_START_AUTOMATIC;
+ }
+
+ if (move_direction_initial == MV_START_RANDOM)
+ MovDir[x][y] = 1 << RND(4);
+ else if (move_direction_initial & MV_ANY_DIRECTION)
+ MovDir[x][y] = move_direction_initial;
+ else if (move_pattern == MV_ALL_DIRECTIONS ||
+ move_pattern == MV_TURNING_LEFT ||
+ move_pattern == MV_TURNING_RIGHT ||
+ move_pattern == MV_TURNING_LEFT_RIGHT ||
+ move_pattern == MV_TURNING_RIGHT_LEFT ||
+ move_pattern == MV_TURNING_RANDOM)
MovDir[x][y] = 1 << RND(4);
- else if (element_info[element].move_pattern == MV_HORIZONTAL)
+ else if (move_pattern == MV_HORIZONTAL)
MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
- else if (element_info[element].move_pattern == MV_VERTICAL)
+ else if (move_pattern == MV_VERTICAL)
MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
- else if (element_info[element].move_pattern & MV_ANY_DIRECTION)
+ else if (move_pattern & MV_ANY_DIRECTION)
MovDir[x][y] = element_info[element].move_pattern;
- else if (element_info[element].move_pattern == MV_ALONG_LEFT_SIDE ||
- element_info[element].move_pattern == MV_ALONG_RIGHT_SIDE)
+ else if (move_pattern == MV_ALONG_LEFT_SIDE ||
+ move_pattern == MV_ALONG_RIGHT_SIDE)
{
for (i = 0; i < 4; i++)
{
if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
{
- if (element_info[element].move_pattern == MV_ALONG_RIGHT_SIDE)
+ if (move_pattern == MV_ALONG_RIGHT_SIDE)
MovDir[x][y] = direction[0][i];
else
MovDir[x][y] = direction[1][i];
Bang(x, y);
}
-void RelocatePlayer(int x, int y, int element)
+void RelocatePlayer(int x, int y, int element_raw)
{
+ 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.index_search);
+ int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
+ int wait_delay_value = (no_delay ? 0 : frame_delay_value);
if (player->GameOver) /* do not reanimate dead player */
return;
DrawPlayer(player);
BackToFront();
- Delay(GAME_FRAME_DELAY);
+ Delay(wait_delay_value);
}
DrawPlayer(player); /* needed here only to cleanup last field */
/* scroll in two steps of half tile size to make things smoother */
BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
FlushDisplay();
- Delay(GAME_FRAME_DELAY);
+ Delay(wait_delay_value);
/* scroll second step to align at full tile size */
BackToFront();
- Delay(GAME_FRAME_DELAY);
+ Delay(wait_delay_value);
}
}
}
void Explode(int ex, int ey, int phase, int mode)
{
int x, y;
+#if 0
int num_phase = 9;
+#endif
+
+ /* !!! eliminate this variable !!! */
int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
+
+#if 1
+ int last_phase;
+#else
int last_phase = num_phase * delay;
int half_phase = (num_phase / 2) * delay;
int first_phase_after_start = EX_PHASE_START + 1;
+#endif
+ int border_element;
if (game.explosions_delayed)
{
Feld[ex][ey] = center_element;
}
+#if 1
+ last_phase = element_info[center_element].explosion_delay;
+#endif
+
for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
{
int xx = x - ex + 1;
RemoveField(x, y);
#endif
- if (IS_PLAYER(ex, ey) && !PLAYER_PROTECTED(ex, ey))
+ if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
{
switch(StorePlayer[ex][ey])
{
case EL_PLAYER_2:
- Store[x][y] = EL_EMERALD_RED;
+ Store[x][y] = EL_PLAYER_IS_EXPLODING_2;
break;
case EL_PLAYER_3:
- Store[x][y] = EL_EMERALD;
+ Store[x][y] = EL_PLAYER_IS_EXPLODING_3;
break;
case EL_PLAYER_4:
- Store[x][y] = EL_EMERALD_PURPLE;
+ Store[x][y] = EL_PLAYER_IS_EXPLODING_4;
break;
case EL_PLAYER_1:
default:
- Store[x][y] = EL_EMERALD_YELLOW;
+ Store[x][y] = EL_PLAYER_IS_EXPLODING_1;
break;
}
#endif
ExplodePhase[x][y] = 1;
+#if 1
+ ExplodeDelay[x][y] = last_phase;
+#endif
Stop[x][y] = TRUE;
}
x = ex;
y = ey;
+#if 1
+ last_phase = ExplodeDelay[x][y];
+#endif
+
ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
#ifdef DEBUG
}
#endif
+#if 1
+
+ border_element = Store2[x][y];
+ if (IS_PLAYER(x, y))
+ border_element = StorePlayer[x][y];
+
+ if (phase == element_info[border_element].ignition_delay ||
+ phase == last_phase)
+ {
+ boolean border_explosion = FALSE;
+
+#if 1
+ if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present)
+#else
+ if (IS_PLAYER(x, y))
+#endif
+ {
+ KillHeroUnlessExplosionProtected(x, y);
+ border_explosion = TRUE;
+
+#if 0
+ if (phase == last_phase)
+ printf("::: IS_PLAYER\n");
+#endif
+ }
+ else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
+ {
+ Feld[x][y] = Store2[x][y];
+ Store2[x][y] = 0;
+ Bang(x, y);
+ border_explosion = TRUE;
+
+#if 0
+ if (phase == last_phase)
+ printf("::: CAN_EXPLODE_BY_EXPLOSION\n");
+#endif
+ }
+ else if (border_element == EL_AMOEBA_TO_DIAMOND)
+ {
+ AmoebeUmwandeln(x, y);
+ Store2[x][y] = 0;
+ border_explosion = TRUE;
+
+#if 0
+ if (phase == last_phase)
+ printf("::: EL_AMOEBA_TO_DIAMOND [%d, %d] [%d]\n",
+ element_info[border_element].explosion_delay,
+ element_info[border_element].ignition_delay,
+ phase);
+#endif
+ }
+
+#if 1
+ /* if an element just explodes due to another explosion (chain-reaction),
+ do not immediately end the new explosion when it was the last frame of
+ the explosion (as it would be done in the following "if"-statement!) */
+ if (border_explosion && phase == last_phase)
+ return;
+#endif
+ }
+
+#else
+
if (phase == first_phase_after_start)
{
int element = Store2[x][y];
int element = Store2[x][y];
if (IS_PLAYER(x, y))
- KillHeroUnlessProtected(x, y);
- else if (CAN_EXPLODE_BY_FIRE(element))
+ KillHeroUnlessExplosionProtected(x, y);
+ else if (CAN_EXPLODE_BY_EXPLOSION(element))
{
Feld[x][y] = Store2[x][y];
Store2[x][y] = 0;
else if (element == EL_AMOEBA_TO_DIAMOND)
AmoebeUmwandeln(x, y);
}
+#endif
if (phase == last_phase)
{
Store[x][y] = Store2[x][y] = 0;
GfxElement[x][y] = EL_UNDEFINED;
+ /* player can escape from explosions and might therefore be still alive */
+ if (element >= EL_PLAYER_IS_EXPLODING_1 &&
+ element <= EL_PLAYER_IS_EXPLODING_4)
+ Feld[x][y] = (stored_player[element - EL_PLAYER_IS_EXPLODING_1].active ?
+ EL_EMPTY :
+ element == EL_PLAYER_IS_EXPLODING_1 ? EL_EMERALD_YELLOW :
+ element == EL_PLAYER_IS_EXPLODING_2 ? EL_EMERALD_RED :
+ element == EL_PLAYER_IS_EXPLODING_3 ? EL_EMERALD :
+ EL_EMERALD_PURPLE);
+
+ /* restore probably existing indestructible background element */
if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
element = Feld[x][y] = Back[x][y];
Back[x][y] = 0;
ChangeDelay[x][y] = 0;
ChangePage[x][y] = -1;
+#if 1
+ InitField_WithBug2(x, y, FALSE);
+#else
InitField(x, y, FALSE);
+#if 1
+ /* !!! not needed !!! */
+#if 1
+ if (game.engine_version < VERSION_IDENT(3,0,9,0) &&
+ CAN_MOVE(Feld[x][y]) && Feld[x][y] != EL_MOLE)
+ InitMovDir(x, y);
+#else
if (CAN_MOVE(element))
InitMovDir(x, y);
+#endif
+#endif
+#endif
DrawLevelField(x, y);
TestIfElementTouchesCustomElement(x, y);
if (GFX_CRUMBLED(element))
DrawLevelFieldCrumbledSandNeighbours(x, y);
- if (IS_PLAYER(x, y) && !PLAYERINFO(x,y)->present)
+ if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
StorePlayer[x][y] = 0;
if (ELEM_IS_PLAYER(element))
RelocatePlayer(x, y, element);
}
+#if 1
+ else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
+#else
else if (phase >= delay && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
+#endif
{
#if 1
int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
#endif
#if 1
- if (IS_PLAYER(x, y) && !PLAYER_PROTECTED(x, y))
+ if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
#else
if (IS_PLAYER(x, y))
#endif
/* 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;
if (impact && element == EL_AMOEBA_DROP)
{
if (object_hit && IS_PLAYER(x, y + 1))
- KillHeroUnlessProtected(x, y + 1);
+ KillHeroUnlessEnemyProtected(x, y + 1);
else if (object_hit && smashed == EL_PENGUIN)
Bang(x, y + 1);
else
{
if (CAN_SMASH_PLAYER(element))
{
- KillHeroUnlessProtected(x, y + 1);
+ KillHeroUnlessEnemyProtected(x, y + 1);
return;
}
}
return;
}
}
- else if ((element == EL_SP_INFOTRON ||
- element == EL_SP_ZONK) &&
- (smashed == EL_SP_SNIKSNAK ||
- smashed == EL_SP_ELECTRON ||
- smashed == EL_SP_DISK_ORANGE))
+ else if (((element == EL_SP_INFOTRON ||
+ element == EL_SP_ZONK) &&
+ (smashed == EL_SP_SNIKSNAK ||
+ smashed == EL_SP_ELECTRON ||
+ smashed == EL_SP_DISK_ORANGE)) ||
+ (element == EL_SP_INFOTRON &&
+ smashed == EL_SP_DISK_YELLOW))
{
Bang(x, y + 1);
return;
move_pattern == MV_TURNING_RANDOM ||
move_pattern == MV_ALL_DIRECTIONS)
{
- boolean can_turn_left = ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
- boolean can_turn_right = ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
+ boolean can_turn_left =
+ CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
+ boolean can_turn_right =
+ CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
if (move_pattern == MV_TURNING_LEFT)
MovDir[x][y] = left_dir;
}
else if (move_pattern == MV_ALONG_LEFT_SIDE)
{
- if (ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
+ if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
MovDir[x][y] = left_dir;
- else if (!ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
+ else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
MovDir[x][y] = right_dir;
if (MovDir[x][y] != old_move_dir)
}
else if (move_pattern == MV_ALONG_RIGHT_SIDE)
{
- if (ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
+ if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
MovDir[x][y] = right_dir;
- else if (!ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
+ else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
MovDir[x][y] = left_dir;
if (MovDir[x][y] != old_move_dir)
new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
Moving2Blocked(x, y, &newx, &newy);
- if (ELEMENT_CAN_ENTER_FIELD_OR_ACID(element, newx, newy))
+ if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
return;
MovDir[x][y] =
new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
Moving2Blocked(x, y, &newx, &newy);
- if (ELEMENT_CAN_ENTER_FIELD_OR_ACID(element, newx, newy))
+ if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
return;
MovDir[x][y] = old_move_dir;
MovDelay[x][y] = 0;
}
- else if (move_pattern & MV_MAZE_RUNNER_STYLE ||
- element == EL_MAZE_RUNNER)
+ else if (move_pattern & MV_MAZE_RUNNER_STYLE)
{
static int test_xy[7][2] =
{
break;
}
- if (!MAZE_RUNNER_CAN_ENTER_FIELD(xx, yy))
+ if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
continue;
move_dir_preference = -1 * RunnerVisit[xx][yy];
void StartMoving(int x, int y)
{
+#if 0
boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
+#endif
boolean started_moving = FALSE; /* some elements can fall _and_ move */
int element = Feld[x][y];
if (CAN_FALL(element) && y < lev_fieldy - 1)
{
- if ((x > 0 && IS_PLAYER(x - 1, y)) ||
- (x < lev_fieldx-1 && IS_PLAYER(x + 1, y)))
+ if ((x > 0 && IS_PLAYER(x - 1, y)) ||
+ (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
if (JustBeingPushed(x, y))
return;
Impact(x, y);
}
- else if (IS_FREE(x, y + 1) && element == EL_SPRING && use_spring_bug)
+ else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
{
if (MovDir[x][y] == MV_NO_MOVING)
{
started_moving = TRUE;
}
}
+#if 0
+ else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
+#else
else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
+#endif
{
boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
if ((belt_dir == MV_LEFT && left_is_free) ||
(belt_dir == MV_RIGHT && right_is_free))
{
+#if 1
+ int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
+#endif
+
InitMovingField(x, y, belt_dir);
started_moving = TRUE;
+#if 1
+ Pushed[x][y] = TRUE;
+ Pushed[nextx][y] = TRUE;
+#endif
+
GfxAction[x][y] = ACTION_DEFAULT;
}
+ else
+ {
+ MovDir[x][y] = 0; /* if element was moving, stop it */
+ }
}
}
{
int flamed = MovingOrBlocked2Element(xx, yy);
- if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_FIRE(flamed))
+ if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
Bang(xx, yy);
else
RemoveMovingField(xx, yy);
if (DONT_COLLIDE_WITH(element) &&
IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
- !PLAYER_PROTECTED(newx, newy))
+ !PLAYER_ENEMY_PROTECTED(newx, newy))
{
#if 1
TestIfBadThingRunsIntoHero(x, y, MovDir[x][y]);
+
return;
#else
/* player killed by element which is deadly when colliding with */
#endif
}
+#if 1
+ else if (CAN_MOVE_INTO_ACID(element) && MovDir[x][y] == MV_DOWN &&
+ IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID)
+
+#else
+
else if ((element == EL_PENGUIN ||
element == EL_ROBOT ||
element == EL_SATELLITE ||
IS_CUSTOM_ELEMENT(element)) &&
IN_LEV_FIELD(newx, newy) &&
MovDir[x][y] == MV_DOWN && Feld[newx][newy] == EL_ACID)
+#endif
{
SplashAcid(x, y);
Store[x][y] = EL_ACID;
}
else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
{
- if (DigField(local_player, newx, newy, 0, 0, DF_DIG) == MF_MOVING)
+ if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MF_MOVING)
DrawLevelField(newx, newy);
else
GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
DrawPlayerField(x, y);
else
DrawLevelField(x, y);
+
return;
}
}
DrawPlayerField(x, y);
else
DrawLevelField(x, y);
+
return;
}
}
- else if ((move_pattern & MV_MAZE_RUNNER_STYLE ||
- element == EL_MAZE_RUNNER) && IN_LEV_FIELD(newx, newy))
- {
+
#if 1
- if (IS_FREE(newx, newy))
-#else
- if (IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
+
+ /*
+ else if (move_pattern & MV_MAZE_RUNNER_STYLE && IN_LEV_FIELD(newx, newy))
+ */
+
+ else if (IS_CUSTOM_ELEMENT(element) &&
+ CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy)
+
+#if 0
+ &&
+ !IS_FREE(newx, newy)
+#endif
+
+)
+ {
+ int new_element = Feld[newx][newy];
+
+#if 0
+ printf("::: '%s' digs '%s' [%d]\n",
+ element_info[element].token_name,
+ element_info[Feld[newx][newy]].token_name,
+ StorePlayer[newx][newy]);
#endif
+
+ if (!IS_FREE(newx, newy))
{
+ int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
+ IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
+ ACTION_BREAKING);
+
+ /* no element can dig solid indestructible elements */
+ if (IS_INDESTRUCTIBLE(new_element) &&
+ !IS_DIGGABLE(new_element) &&
+ !IS_COLLECTIBLE(new_element))
+ return;
+
+ if (AmoebaNr[newx][newy] &&
+ (new_element == EL_AMOEBA_FULL ||
+ new_element == EL_BD_AMOEBA ||
+ new_element == EL_AMOEBA_GROWING))
+ {
+ AmoebaCnt[AmoebaNr[newx][newy]]--;
+ AmoebaCnt2[AmoebaNr[newx][newy]]--;
+ }
+
if (IS_MOVING(newx, newy))
RemoveMovingField(newx, newy);
else
{
- Feld[newx][newy] = EL_EMPTY;
+ RemoveField(newx, newy);
DrawLevelField(newx, newy);
}
- PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
+ PlayLevelSoundAction(x, y, action);
}
- else if (!IS_FREE(newx, newy))
+
+ if (new_element == element_info[element].move_enter_element)
+ element_info[element].can_leave_element = TRUE;
+
+ if (move_pattern & MV_MAZE_RUNNER_STYLE)
{
-#if 0
- if (IS_PLAYER(x, y))
- DrawPlayerField(x, y);
- else
- DrawLevelField(x, y);
-#endif
- return;
+ RunnerVisit[x][y] = FrameCounter;
+ PlayerVisit[x][y] /= 8; /* expire player visit path */
}
-
- RunnerVisit[x][y] = FrameCounter;
- PlayerVisit[x][y] /= 8; /* expire player visit path */
}
+
+#endif
+
else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
{
if (!IS_FREE(newx, newy))
DrawLevelField(x, y);
MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
+
return; /* wait for shrinking amoeba */
}
else /* element == EL_PACMAN */
void ContinueMoving(int x, int y)
{
int element = Feld[x][y];
+ struct ElementInfo *ei = &element_info[element];
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);
#if 0
int nextx = newx + dx, nexty = newy + dy;
#endif
- boolean pushed = Pushed[x][y];
+#if 1
+ boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
+ boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
+#else
+ boolean pushed_by_player = Pushed[x][y];
+#endif
MovPos[x][y] += getElementMoveStepsize(x, y);
- if (pushed) /* special case: moving object pushed by player */
+#if 0
+ if (pushed_by_player && IS_PLAYER(x, y))
+ {
+ /* special case: moving object pushed by player */
+ MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
+ }
+#else
+ if (pushed_by_player) /* special case: moving object pushed by player */
MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
+#endif
if (ABS(MovPos[x][y]) < TILEX)
{
ResetGfxAnimation(x, y); /* reset animation values for old field */
+#if 1
+ /* some elements can leave other elements behind after moving */
+ if (IS_CUSTOM_ELEMENT(element) && !IS_PLAYER(x, y) &&
+ ei->move_leave_element != EL_EMPTY &&
+ (ei->move_leave_type == LEAVE_TYPE_UNLIMITED ||
+ ei->can_leave_element_last))
+ {
+ Feld[x][y] = ei->move_leave_element;
+ InitField(x, y, FALSE);
+
+ if (GFX_CRUMBLED(Feld[x][y]))
+ DrawLevelFieldCrumbledSandNeighbours(x, y);
+ }
+
+ ei->can_leave_element_last = ei->can_leave_element;
+ ei->can_leave_element = FALSE;
+#endif
+
#if 0
/* 2.1.1 (does not work correctly for spring) */
if (!CAN_MOVE(element))
Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
/* prevent pushed element from moving on in pushed direction */
- if (pushed && CAN_MOVE(element) &&
+ if (pushed_by_player && CAN_MOVE(element) &&
element_info[element].move_pattern & MV_ANY_DIRECTION &&
!(element_info[element].move_pattern & direction))
TurnRound(newx, newy);
- if (!pushed) /* special case: moving object pushed by player */
+#if 1
+ /* prevent elements on conveyor belt from moving on in last direction */
+ if (pushed_by_conveyor && CAN_FALL(element) &&
+ direction & MV_HORIZONTAL)
+ MovDir[newx][newy] = 0;
+#endif
+
+ if (!pushed_by_player)
{
WasJustMoving[newx][newy] = 3;
Impact(x, newy);
#if 1
- TestIfElementTouchesCustomElement(x, y); /* for empty space */
+ TestIfElementTouchesCustomElement(x, y); /* empty or new element */
#endif
#if 0
#if 0
if (IN_LEV_FIELD(nextx, nexty))
{
- static int opposite_directions[] =
- {
- MV_RIGHT,
- MV_LEFT,
- MV_DOWN,
- MV_UP
- };
- int move_dir_bit = MV_DIR_BIT(direction);
- int opposite_direction = opposite_directions[move_dir_bit];
+ int opposite_direction = MV_DIR_OPPOSITE(direction);
int hitting_side = direction;
int touched_side = opposite_direction;
int touched_element = MovingOrBlocked2Element(nextx, nexty);
static void ChangeElementNowExt(int x, int y, int target_element)
{
+ int previous_move_direction = MovDir[x][y];
+
/* 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_PROTECTED(x, y) &&
+ if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
IS_ACCESSIBLE(Feld[x][y]) && !IS_ACCESSIBLE(target_element))
{
Bang(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 1
+ InitField_WithBug1(x, y, FALSE);
+#else
InitField(x, y, FALSE);
if (CAN_MOVE(Feld[x][y]))
InitMovDir(x, y);
+#endif
DrawLevelField(x, y);
change->events & CH_EVENT_BIT(trigger_event) &&
#endif
change->sides & trigger_side &&
- change->trigger_element == trigger_element)
+#if 1
+ IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)
+#else
+ change->trigger_element == trigger_element
+#endif
+ )
{
#if 0
if (!(change->events & CH_EVENT_BIT(trigger_event)))
element = Feld[x][y];
}
+#if 1
+ if (page < 0)
+ {
+ boolean change_element = FALSE;
+ int i;
+
+ for (i = 0; i < element_info[element].num_change_pages; i++)
+ {
+ struct ElementChangeInfo *change = &element_info[element].change_page[i];
+
+ if (change->can_change &&
+ change->events & CH_EVENT_BIT(trigger_event) &&
+ change->sides & side)
+ {
+ change_element = TRUE;
+ page = i;
+
+ break;
+ }
+ }
+
+ if (!change_element)
+ return FALSE;
+ }
+
+#else
+
+ /* !!! this check misses pages with same event, but different side !!! */
+
if (page < 0)
page = element_info[element].event_page_nr[trigger_event];
if (!(element_info[element].change_page[page].sides & side))
return FALSE;
+#endif
ChangeDelay[x][y] = 1;
ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
/* no actions for this player (no input at player's configured device) */
- DigField(player, 0, 0, 0, 0, DF_NO_PUSH);
+ DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
SnapField(player, 0, 0);
CheckGravityMovement(player);
/* no actions for this player (no input at player's configured device) */
- DigField(player, 0, 0, 0, 0, DF_NO_PUSH);
+ DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
SnapField(player, 0, 0);
CheckGravityMovement(player);
#endif
#if 1
+ /* for downwards 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
+ "spring push bug" which is used in older levels and might be wanted to be
+ used also in newer levels, but in this case the buggy pushing code is only
+ affecting the "spring" element and no other elements */
+
+#if 1
+ if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
+#else
if (game.engine_version < VERSION_IDENT(2,2,0,7))
+#endif
{
for (i = 0; i < MAX_PLAYERS; i++)
{
int x = player->jx;
int y = player->jy;
+#if 1
+ if (player->active && player->is_pushing && player->is_moving &&
+ IS_MOVING(x, y) &&
+ (game.engine_version < VERSION_IDENT(2,2,0,7) ||
+ Feld[x][y] == EL_SPRING))
+#else
if (player->active && player->is_pushing && player->is_moving &&
IS_MOVING(x, y))
+#endif
{
ContinueMoving(x, y);
redraw_mask |= REDRAW_FIELD;
}
+static boolean canEnterSupaplexPort(int x, int y, int dx, int dy)
+{
+ int nextx = x + dx, nexty = y + dy;
+ int element = Feld[x][y];
+
+ if ((dx == -1 &&
+ element != EL_SP_PORT_LEFT &&
+ element != EL_SP_GRAVITY_PORT_LEFT &&
+ element != EL_SP_PORT_HORIZONTAL &&
+ element != EL_SP_PORT_ANY) ||
+ (dx == +1 &&
+ element != EL_SP_PORT_RIGHT &&
+ element != EL_SP_GRAVITY_PORT_RIGHT &&
+ element != EL_SP_PORT_HORIZONTAL &&
+ element != EL_SP_PORT_ANY) ||
+ (dy == -1 &&
+ element != EL_SP_PORT_UP &&
+ element != EL_SP_GRAVITY_PORT_UP &&
+ element != EL_SP_PORT_VERTICAL &&
+ element != EL_SP_PORT_ANY) ||
+ (dy == +1 &&
+ element != EL_SP_PORT_DOWN &&
+ element != EL_SP_GRAVITY_PORT_DOWN &&
+ element != EL_SP_PORT_VERTICAL &&
+ element != EL_SP_PORT_ANY) ||
+ !IN_LEV_FIELD(nextx, nexty) ||
+ !IS_FREE(nextx, nexty))
+ return FALSE;
+
+ return TRUE;
+}
+
static void CheckGravityMovement(struct PlayerInfo *player)
{
if (game.gravity && !player->programmed_action)
boolean player_is_moving_to_valid_field =
(IN_LEV_FIELD(new_jx, new_jy) &&
(Feld[new_jx][new_jy] == EL_SP_BASE ||
- Feld[new_jx][new_jy] == EL_SAND));
+ Feld[new_jx][new_jy] == EL_SAND ||
+ (IS_SP_PORT(Feld[new_jx][new_jy]) &&
+ canEnterSupaplexPort(new_jx, new_jy, dx, dy))));
/* !!! extend EL_SAND to anything diggable !!! */
+ boolean player_is_standing_on_valid_field =
+ (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
+ (IS_WALKABLE(Feld[jx][jy]) &&
+ !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
+
if (field_under_player_is_free &&
- !player_is_moving_to_valid_field &&
- !IS_WALKABLE_INSIDE(Feld[jx][jy]))
+ !player_is_standing_on_valid_field &&
+ !player_is_moving_to_valid_field)
player->programmed_action = MV_DOWN;
}
}
return MF_MOVING;
}
- can_move = DigField(player, new_jx, new_jy, real_dx, real_dy, DF_DIG);
+ can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
if (can_move != MF_MOVING)
return can_move;
#if 0
DrawPlayer(player);
#endif
+
return;
}
else if (!FrameReached(&player->actual_frame_counter, 1))
player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
- if (Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
+ if (!player->block_last_field &&
+ Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
Feld[last_jx][last_jy] = EL_EMPTY;
/* before DrawPlayer() to draw correct player graphic for this case */
}
#endif
+ if (player->block_last_field &&
+ Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
+ Feld[last_jx][last_jy] = EL_EMPTY;
+
player->last_jx = jx;
player->last_jy = jy;
if (change->can_change &&
change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
change->sides & border_side &&
- change->trigger_element == border_element)
+#if 1
+ IS_EQUAL_OR_IN_GROUP(border_element, change->trigger_element)
+#else
+ change->trigger_element == border_element
+#endif
+ )
{
change_center_element = TRUE;
center_element_change_page = j;
if (change->can_change &&
change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
change->sides & center_side &&
- change->trigger_element == center_element)
+#if 1
+ IS_EQUAL_OR_IN_GROUP(center_element, change->trigger_element)
+#else
+ change->trigger_element == center_element
+#endif
+ )
{
CheckElementSideChange(xx, yy, border_element, CH_SIDE_ANY,
CE_OTHER_IS_TOUCHING, j);
if (IN_LEV_FIELD(hitx, hity))
{
- static int opposite_directions[] =
- {
- MV_RIGHT,
- MV_LEFT,
- MV_DOWN,
- MV_UP
- };
- int move_dir_bit = MV_DIR_BIT(direction);
- int opposite_direction = opposite_directions[move_dir_bit];
+ int opposite_direction = MV_DIR_OPPOSITE(direction);
int hitting_side = direction;
int touched_side = opposite_direction;
int touched_element = MovingOrBlocked2Element(hitx, hity);
if (change->can_change &&
change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) &&
change->sides & touched_side &&
- change->trigger_element == touched_element)
+
+#if 1
+ IS_EQUAL_OR_IN_GROUP(touched_element, change->trigger_element)
+#else
+ change->trigger_element == touched_element
+#endif
+ )
{
CheckElementSideChange(x, y, hitting_element,
CH_SIDE_ANY, CE_OTHER_IS_HITTING, i);
if (change->can_change &&
change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) &&
change->sides & hitting_side &&
- change->trigger_element == hitting_element)
+#if 1
+ IS_EQUAL_OR_IN_GROUP(hitting_element, change->trigger_element)
+#else
+ change->trigger_element == hitting_element
+#endif
+ )
{
CheckElementSideChange(hitx, hity, touched_element,
CH_SIDE_ANY, CE_OTHER_GETS_HIT, i);
if (player->shield_deadly_time_left > 0)
Bang(kill_x, kill_y);
- else if (!PLAYER_PROTECTED(good_x, good_y))
+ else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
KillHero(player);
}
else
if (player->shield_deadly_time_left > 0)
Bang(bad_x, bad_y);
- else if (!PLAYER_PROTECTED(kill_x, kill_y))
+ else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
KillHero(player);
}
else
BuryHero(player);
}
-static void KillHeroUnlessProtected(int x, int y)
+static void KillHeroUnlessEnemyProtected(int x, int y)
{
- if (!PLAYER_PROTECTED(x, y))
+ if (!PLAYER_ENEMY_PROTECTED(x, y))
+ KillHero(PLAYERINFO(x, y));
+}
+
+static void KillHeroUnlessExplosionProtected(int x, int y)
+{
+ if (!PLAYER_EXPLOSION_PROTECTED(x, y))
KillHero(PLAYERINFO(x, y));
}
*/
int DigField(struct PlayerInfo *player,
- int x, int y, int real_dx, int real_dy, int mode)
+ int oldx, int oldy, int x, int y,
+ int real_dx, int real_dy, int mode)
{
static int change_sides[4] =
{
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));
- int jx = player->jx, jy = player->jy;
+#endif
+ int jx = oldx, jy = oldy;
int dx = x - jx, dy = y - jy;
int nextx = x + dx, nexty = y + dy;
int move_direction = (dx == -1 ? MV_LEFT :
dx == +1 ? MV_RIGHT :
dy == -1 ? MV_UP :
dy == +1 ? MV_DOWN : MV_NO_MOVING);
+ int opposite_direction = MV_DIR_OPPOSITE(move_direction);
int dig_side = change_sides[MV_DIR_BIT(move_direction)];
+ int old_element = Feld[jx][jy];
int element;
if (player->MovPos == 0)
if (IS_MOVING(x, y) || IS_PLAYER(x, y))
return MF_NO_ACTION;
+#if 0
+
#if 0
if (IS_TUBE(Feld[jx][jy]) || IS_TUBE(Back[jx][jy]))
#else
return MF_NO_ACTION; /* tube has no opening in this direction */
}
+#else
+
+ if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
+ old_element = Back[jx][jy];
+
+#endif
+
+ if (IS_WALKABLE(old_element) &&
+ !(element_info[old_element].access_direction & move_direction))
+ return MF_NO_ACTION; /* field has no opening in this direction */
+
element = Feld[x][y];
if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
case EL_SP_GRAVITY_PORT_RIGHT:
case EL_SP_GRAVITY_PORT_UP:
case EL_SP_GRAVITY_PORT_DOWN:
+#if 1
+ if (!canEnterSupaplexPort(x, y, dx, dy))
+ return MF_NO_ACTION;
+#else
if ((dx == -1 &&
element != EL_SP_PORT_LEFT &&
element != EL_SP_GRAVITY_PORT_LEFT &&
!IN_LEV_FIELD(nextx, nexty) ||
!IS_FREE(nextx, nexty))
return MF_NO_ACTION;
+#endif
if (element == EL_SP_GRAVITY_PORT_LEFT ||
element == EL_SP_GRAVITY_PORT_RIGHT ||
PlayLevelSound(x, y, SND_CLASS_SP_PORT_PASSING);
break;
+#if 0
case EL_TUBE_ANY:
case EL_TUBE_VERTICAL:
case EL_TUBE_HORIZONTAL:
PlayLevelSound(x, y, SND_CLASS_TUBE_WALKING);
}
break;
+#endif
default:
{
int sound_action = ACTION_WALKING;
+ if (!(element_info[element].access_direction & opposite_direction))
+ return MF_NO_ACTION; /* field not accessible from this direction */
+
if (element >= EL_GATE_1 && element <= EL_GATE_4)
{
if (!player->key[element - EL_GATE_1])
if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
return MF_NO_ACTION;
+ if (IS_CUSTOM_ELEMENT(element) &&
+ !(element_info[element].access_direction & opposite_direction))
+ return MF_NO_ACTION; /* field not accessible from this direction */
+
#if 1
if (CAN_MOVE(element)) /* only fixed elements can be passed! */
return MF_NO_ACTION;
return MF_NO_ACTION;
if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
- !(element == EL_SPRING && use_spring_bug))
+ !(element == EL_SPRING && level.use_spring_bug))
return MF_NO_ACTION;
#if 1
player->is_dropping = FALSE;
- if (DigField(player, x, y, 0, 0, DF_SNAP) == MF_NO_ACTION)
+ if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MF_NO_ACTION)
return FALSE;
player->is_snapping = TRUE;
PlayLevelSoundAction(jx, jy, ACTION_DROPPING);
+#if 1
+ /* needed if previous element just changed to "empty" in the last frame */
+ Changed[jx][jy] = 0; /* allow another change */
+#endif
+
CheckTriggeredElementChange(jx, jy, new_element, CE_OTHER_GETS_DROPPED);
CheckElementChange(jx, jy, new_element, CE_DROPPED_BY_PLAYER);
if (Feld[jx][jy] == new_element) /* uninitialized unless CE change */
{
+#if 1
+ InitField_WithBug1(jx, jy, FALSE);
+#else
InitField(jx, jy, FALSE);
if (CAN_MOVE(Feld[jx][jy]))
InitMovDir(jx, jy);
+#endif
}
new_element = Feld[jx][jy];
int move_stepsize = element_info[new_element].move_stepsize;
int direction, dx, dy, nextx, nexty;
- if (element_info[new_element].move_direction_initial == MV_NO_MOVING)
+ if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
MovDir[jx][jy] = player->MovDir;
direction = MovDir[jx][jy];
}
else
{
- Changed[jx][jy] = 0; /* allow another change */
+ Changed[jx][jy] = 0; /* allow another change */
#if 1
TestIfElementHitsCustomElement(jx, jy, direction);