/* EXPERIMENTAL STUFF */
#define USE_NEW_AMOEBA_CODE FALSE
+/* EXPERIMENTAL STUFF */
+#define USE_NEW_MOVE_STYLE TRUE *1
+#define USE_NEW_MOVE_DELAY TRUE *1
+#define USE_NEW_PUSH_DELAY TRUE *1
+#define USE_NEW_BLOCK_STYLE TRUE *1
+
/* for DigField() */
#define DF_NO_PUSH 0
#define DF_DIG 1
/* 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);
static struct ChangingElementInfo change_delay_list[] =
{
+#if USE_NEW_BLOCK_STYLE
+#if 0
+ {
+ EL_PLAYER_IS_LEAVING,
+ EL_EMPTY,
+ -1, /* delay for blocking field left by player set at runtime */
+ NULL,
+ NULL,
+ NULL
+ },
+#endif
+#endif
{
EL_NUT_BREAKING,
EL_EMERALD,
}
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
+#endif
+
if (!options.network || player->connected)
{
player->active = TRUE;
/*
=============================================================================
- InitGameEngine()
+ InitGameEngine()
-----------------------------------------------------------------------------
initialize game engine due to level / tape version number
=============================================================================
/* ---------- 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;
{
ScrollPlayer(player, SCROLL_GO_ON);
ScrollScreen(NULL, SCROLL_GO_ON);
+
+#if USE_NEW_MOVE_DELAY
+ AdvanceFrameAndPlayerCounters(player->index_nr);
+#else
FrameCounter++;
+#endif
DrawPlayer(player);
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)
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 (pushed_by_player)
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;
}
#endif
#endif
+
is_walkable = (is_empty || IS_WALKABLE(e));
is_diggable = (is_empty || IS_DIGGABLE(e));
is_collectible = (is_empty || IS_COLLECTIBLE(e));
is_removable = (is_diggable || is_collectible);
can_replace[xx][yy] =
- ((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));
+ (((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;
}
#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;
Changed[x][y] = CE_BITMASK_DEFAULT;
ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
+#if USE_NEW_BLOCK_STYLE
+ /* this must be handled before main playfield loop */
+ if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
+ {
+ MovDelay[x][y]--;
+ if (MovDelay[x][y] <= 0)
+ RemoveField(x, y);
+ }
+#endif
+
#if DEBUG
if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
{
stored_player[0].StepFrame);
#endif
-#if 1
+#if USE_NEW_MOVE_DELAY
+ AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
+#else
FrameCounter++;
TimeFrames++;
if (stored_player[i].MovPos != 0)
stored_player[i].StepFrame += move_frames;
+#if USE_NEW_MOVE_DELAY
+ if (stored_player[i].move_delay > 0)
+ stored_player[i].move_delay--;
+#endif
+
if (stored_player[i].drop_delay > 0)
stored_player[i].drop_delay--;
}
#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)
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 USE_NEW_BLOCK_STYLE
+ if (player->block_delay > 0 &&
+ Feld[last_jx][last_jy] == EL_EMPTY)
+ {
+ Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
+#if 1
+ MovDelay[last_jx][last_jy] = player->block_delay + 1;
+#else
+ ChangeDelay[last_jx][last_jy] = player->block_last_field_delay;
+#endif
+ }
+#else
+#if USE_NEW_MOVE_STYLE
+ if (player->block_last_field &&
+ Feld[last_jx][last_jy] == EL_EMPTY)
+ Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
+#else
if (Feld[last_jx][last_jy] == EL_EMPTY)
Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
+#endif
+#endif
#if 0
DrawPlayer(player);
player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
+#if USE_NEW_BLOCK_STYLE
+#else
if (!player->block_last_field &&
Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
+#if 1
+ RemoveField(last_jx, last_jy);
+#else
Feld[last_jx][last_jy] = EL_EMPTY;
+#endif
+#endif
/* before DrawPlayer() to draw correct player graphic for this case */
if (player->MovPos == 0)
}
#endif
+#if USE_NEW_BLOCK_STYLE
+#else
if (player->block_last_field &&
Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
+#if 1
+ RemoveField(last_jx, last_jy);
+#else
Feld[last_jx][last_jy] = EL_EMPTY;
+#endif
+#endif
player->last_jx = jx;
player->last_jy = jy;
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++)
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;
}
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 */
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;
}
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;