(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);
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;
group_element - EL_GROUP_START + 1);
/* replace element which caused too deep recursion by question mark */
- group->element_resolved[group->num_elements_resolved++] = EL_CHAR_QUESTION;
+ group->element_resolved[group->num_elements_resolved++] = EL_UNKNOWN;
return;
}
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 */
player->use_murphy_graphic = FALSE;
+ player->block_last_field = FALSE;
+
player->actual_frame_counter = 0;
player->step_counter = 0;
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;
void RelocatePlayer(int x, int y, int element)
{
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);
}
}
}
int last_phase = num_phase * delay;
int half_phase = (num_phase / 2) * delay;
int first_phase_after_start = EX_PHASE_START + 1;
+ int border_element;
+
+ int last_phase_TEST = last_phase;
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 (IS_PLAYER(x, y))
+ {
+ KillHeroUnlessExplosionProtected(x, y);
+ border_explosion = TRUE;
+
+ if (phase == last_phase)
+ printf("::: IS_PLAYER\n");
+ }
+ else if (CAN_EXPLODE_BY_FIRE(border_element))
+ {
+ Feld[x][y] = Store2[x][y];
+ Store2[x][y] = 0;
+ Bang(x, y);
+ border_explosion = TRUE;
+
+ if (phase == last_phase)
+ printf("::: CAN_EXPLODE_BY_FIRE\n");
+ }
+ else if (border_element == EL_AMOEBA_TO_DIAMOND)
+ {
+ AmoebeUmwandeln(x, y);
+ border_explosion = TRUE;
+
+ if (phase == last_phase)
+ printf("::: EL_AMOEBA_TO_DIAMOND\n");
+ }
+
+#if 0
+ 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);
+ KillHeroUnlessExplosionProtected(x, y);
else if (CAN_EXPLODE_BY_FIRE(element))
{
Feld[x][y] = Store2[x][y];
else if (element == EL_AMOEBA_TO_DIAMOND)
AmoebeUmwandeln(x, y);
}
+#endif
if (phase == last_phase)
{
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
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 (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);
redraw_mask |= REDRAW_FIELD;
}
-static boolean canEnterSupaplexPort(int x, int y, int move_dir)
+static boolean canEnterSupaplexPort(int x, int y, int dx, int dy)
{
- int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
- int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
int nextx = x + dx, nexty = y + dy;
int element = Feld[x][y];
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 !!! */
if (field_under_player_is_free &&
#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 (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_ENEMY_PROTECTED(x, y))
+ KillHero(PLAYERINFO(x, y));
+}
+
+static void KillHeroUnlessExplosionProtected(int x, int y)
{
- if (!PLAYER_PROTECTED(x, y))
+ if (!PLAYER_EXPLOSION_PROTECTED(x, y))
KillHero(PLAYERINFO(x, y));
}
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 ||
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);
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];
}
else
{
- Changed[jx][jy] = 0; /* allow another change */
+ Changed[jx][jy] = 0; /* allow another change */
#if 1
TestIfElementHitsCustomElement(jx, jy, direction);