(condition) || \
(DONT_COLLIDE_WITH(e) && \
IS_PLAYER(x, y) && \
- !PLAYER_PROTECTED(x, y))))
+ !PLAYER_ENEMY_PROTECTED(x, y))))
#else
#define ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, condition) \
(IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
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 & \
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;
}
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)
int move_direction_initial = ei->move_direction_initial;
int move_pattern = ei->move_pattern;
- if (move_direction_initial == MV_PREVIOUS)
+ if (move_direction_initial == MV_START_PREVIOUS)
{
if (MovDir[x][y] != MV_NO_MOVING)
return;
- move_direction_initial = MV_AUTOMATIC;
+ move_direction_initial = MV_START_AUTOMATIC;
}
- if (move_direction_initial == MV_RANDOM)
+ 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;
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);
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])
{
#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)
{
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 1
+ 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;
}
}
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;
started_moving = TRUE;
}
}
+#if 1
+ 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));
{
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]);
#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);
/* 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_PREVIOUS)
+ if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
MovDir[x][y] = previous_move_direction;
InitField(x, y, FALSE);
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);
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;
}
}
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 (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));
}
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) &&
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;
int move_stepsize = element_info[new_element].move_stepsize;
int direction, dx, dy, nextx, nexty;
- if (element_info[new_element].move_direction_initial == MV_AUTOMATIC)
+ if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
MovDir[jx][jy] = player->MovDir;
direction = MovDir[jx][jy];