#define USE_UFAST_PLAYER_EXIT_BUGFIX (USE_NEW_STUFF * 1)
+#define USE_GFX_RESET_ONLY_WHEN_MOVING (USE_NEW_STUFF * 1)
+#define USE_GFX_RESET_PLAYER_ARTWORK (USE_NEW_STUFF * 1)
+
+#define USE_FIX_KILLED_BY_NON_WALKABLE (USE_NEW_STUFF * 1)
+#define USE_FIX_IMPACT_COLLISION (USE_NEW_STUFF * 1)
+
/* for DigField() */
#define DF_NO_PUSH 0
/* values for delayed check of falling and moving elements and for collision */
#define CHECK_DELAY_MOVING 3
-#define CHECK_DELAY_FALLING 3
+#define CHECK_DELAY_FALLING CHECK_DELAY_MOVING
#define CHECK_DELAY_COLLISION 2
+#define CHECK_DELAY_IMPACT CHECK_DELAY_COLLISION
/* values for initial player move delay (initial delay counter value) */
#define INITIAL_MOVE_DELAY_OFF -1
#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 INIT_GFX_RANDOM() (GetSimpleRandom(1000000))
#define GET_NEW_PUSH_DELAY(e) ( (element_info[e].push_delay_fixed) + \
RND(element_info[e].push_delay_random))
(e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score : \
(e) == EL_CURRENT_CE_VALUE ? (cv) : \
(e) == EL_CURRENT_CE_SCORE ? (cs) : \
- (e) >= EL_LAST_CE_8 && (e) <= EL_NEXT_CE_8 ? \
+ (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ? \
RESOLVED_REFERENCE_ELEMENT(be, e) : \
(e))
player->drop_delay = 0;
player->drop_pressed_delay = 0;
- player->last_jx = player->last_jy = 0;
- player->jx = player->jy = 0;
+ player->last_jx = -1;
+ player->last_jy = -1;
+ player->jx = -1;
+ player->jy = -1;
player->shield_normal_time_left = 0;
player->shield_deadly_time_left = 0;
WasJustMoving[x][y] = 0;
WasJustFalling[x][y] = 0;
CheckCollision[x][y] = 0;
+ CheckImpact[x][y] = 0;
Stop[x][y] = FALSE;
Pushed[x][y] = FALSE;
}
}
+#if 1
+ UnmapAllGadgets();
+
+ MapGameButtons();
+ MapTapeButtons();
+#endif
+
game.restart_level = FALSE;
}
{ MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
};
- switch(element)
+ switch (element)
{
case EL_BUG_RIGHT:
case EL_BUG_UP:
static int game_over_delay = 0;
int game_over_delay_value = 50;
- /* do not start end game actions before the player stops moving (to exit) */
- if (local_player->MovPos)
- return;
-
if (!local_player->LevelSolved_GameEnd)
{
+ int i;
+
+ /* do not start end game actions before the player stops moving (to exit) */
+ if (local_player->MovPos)
+ return;
+
local_player->LevelSolved_GameEnd = TRUE;
local_player->LevelSolved_SaveTape = tape.recording;
local_player->LevelSolved_SaveScore = !tape.playing;
if (tape.auto_play) /* tape might already be stopped here */
tape.auto_play_level_solved = TRUE;
+#if 1
+ TapeStop();
+#endif
+
game_over_delay = game_over_delay_value;
time = time_final = (level.time == 0 ? TimePlayed : TimeLeft);
DrawGameValue_Score(score);
}
- if (ExitX >= 0 && ExitY >= 0) /* local player has left the level */
+ if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
{
- /* close exit door after last player */
- if (AllPlayersGone &&
- (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
- Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN))
+ if (ExitX >= 0 && ExitY >= 0) /* local player has left the level */
{
- int element = Feld[ExitX][ExitY];
+ /* close exit door after last player */
+ if (AllPlayersGone &&
+ (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
+ Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN))
+ {
+ int element = Feld[ExitX][ExitY];
- Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
- EL_SP_EXIT_CLOSING);
+ Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
+ EL_SP_EXIT_CLOSING);
+
+ PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
+ }
- PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
+ /* player disappears */
+ DrawLevelField(ExitX, ExitY);
}
- /* player disappears */
- DrawLevelField(ExitX, ExitY);
+ for (i = 0; i < MAX_PLAYERS; i++)
+ {
+ struct PlayerInfo *player = &stored_player[i];
+
+ if (player->present)
+ {
+ RemovePlayer(player);
+
+ /* player disappears */
+ DrawLevelField(player->jx, player->jy);
+ }
+ }
}
PlaySound(SND_GAME_WINNING);
if (local_player->LevelSolved_SaveTape)
{
+#if 0
TapeStop();
+#endif
+#if 1
+ SaveTapeChecked(tape.level_nr); /* ask to save tape */
+#else
SaveTape(tape.level_nr); /* ask to save tape */
+#endif
}
if (level_editor_test_game)
return position;
}
-inline static int getElementMoveStepsize(int x, int y)
+inline static int getElementMoveStepsizeExt(int x, int y, int direction)
{
int element = Feld[x][y];
- 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);
int horiz_move = (dx != 0);
return step;
}
+inline static int getElementMoveStepsize(int x, int y)
+{
+ return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
+}
+
void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
{
if (player->GfxAction != action || player->GfxDir != dir)
int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
int newx = x + dx;
int newy = y + dy;
+ boolean is_moving_before, is_moving_after;
+#if 0
+ boolean continues_moving = (WasJustMoving[x][y] && direction == MovDir[x][y]);
+#endif
+
+ /* check if element was/is moving or being moved before/after mode change */
+#if 1
+ is_moving_before = WasJustMoving[x][y];
+#else
+ is_moving_before = (getElementMoveStepsizeExt(x, y, MovDir[x][y]) != 0);
+#endif
+ is_moving_after = (getElementMoveStepsizeExt(x, y, direction) != 0);
- if (!WasJustMoving[x][y] || direction != MovDir[x][y])
+ /* reset animation only for moving elements which change direction of moving
+ or which just started or stopped moving
+ (else CEs with property "can move" / "not moving" are reset each frame) */
+#if USE_GFX_RESET_ONLY_WHEN_MOVING
+#if 1
+ if (is_moving_before != is_moving_after ||
+ direction != MovDir[x][y])
+ ResetGfxAnimation(x, y);
+#else
+ if ((is_moving_before || is_moving_after) && !continues_moving)
ResetGfxAnimation(x, y);
+#endif
+#else
+ if (!continues_moving)
+ ResetGfxAnimation(x, y);
+#endif
MovDir[x][y] = direction;
GfxDir[x][y] = direction;
+
+#if USE_GFX_RESET_ONLY_WHEN_MOVING
+ GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
+ direction == MV_DOWN && CAN_FALL(element) ?
+ ACTION_FALLING : ACTION_MOVING);
+#else
GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
ACTION_FALLING : ACTION_MOVING);
+#endif
/* this is needed for CEs with property "can move" / "not moving" */
- if (getElementMoveStepsize(x, y) != 0) /* moving or being moved */
+ if (is_moving_after)
{
if (Feld[newx][newy] == EL_EMPTY)
Feld[newx][newy] = EL_BLOCKED;
}
}
- switch(element)
+ switch (element)
{
case EL_BUG:
case EL_SPACESHIP:
Store[x][y] = EL_ACID;
}
- else if ((game.engine_version >= VERSION_IDENT(3,1,0,0) &&
+ else if (
+#if USE_FIX_IMPACT_COLLISION
+ (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
+ CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
+#else
+ (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
-
+#endif
(game.engine_version >= VERSION_IDENT(3,0,7,0) &&
CAN_FALL(element) && WasJustFalling[x][y] &&
(Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
simply not covered here... :-/ ) */
CheckCollision[x][y] = 0;
+ CheckImpact[x][y] = 0;
Impact(x, y);
}
if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
+
+#if USE_FIX_IMPACT_COLLISION
+ if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
+ CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
+#endif
}
if (DONT_TOUCH(element)) /* object may be nasty to player or others */
if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
- MV_DIR_OPPOSITE(direction));
+ MV_DIR_OPPOSITE(direction));
}
int AmoebeNachbarNr(int ax, int ay)
}
}
-void EdelsteinFunkeln(int x, int y)
+void DrawTwinkleOnField(int x, int y)
{
if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
return;
return;
if (MovDelay[x][y] == 0) /* next animation frame */
- MovDelay[x][y] = 11 * !SimpleRND(500);
+ MovDelay[x][y] = 11 * !GetSimpleRandom(500);
if (MovDelay[x][y] != 0) /* wait some time before next frame */
{
/* ---------- execute action -------------------------------------------- */
- switch(action_type)
+ switch (action_type)
{
case CA_NO_ACTION:
{
(level.use_artwork_element[i] ? level.artwork_element[i] :
stored_player[i].element_nr);
+#if USE_GFX_RESET_PLAYER_ARTWORK
+ if (stored_player[i].artwork_element != artwork_element)
+ stored_player[i].Frame = 0;
+#endif
+
stored_player[i].artwork_element = artwork_element;
SetPlayerWaiting(&stored_player[i], FALSE);
#if USE_NEW_CUSTOM_VALUE
int last_ce_value = CustomValue[x][y];
#endif
+ boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
boolean add_player_onto_element = (new_element_is_player &&
#if USE_CODE_THAT_BREAKS_SNAKE_BITE
/* check if element under the player changes from accessible to unaccessible
(needed for special case of dropping element which then changes) */
/* (must be checked after creating new element for walkable group elements) */
+#if USE_FIX_KILLED_BY_NON_WALKABLE
+ if (IS_PLAYER(x, y) && !player_explosion_protected &&
+ IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
+ {
+ Bang(x, y);
+
+ return;
+ }
+#else
if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
{
return;
}
+#endif
#endif
/* "ChangeCount" not set yet to allow "entered by player" change one time */
{
struct ElementChangeInfo *change = &element_info[element].change_page[p];
+ /* check trigger element for all events where the element that is checked
+ for changing interacts with a directly adjacent element -- this is
+ different to element changes that affect other elements to change on the
+ whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
boolean check_trigger_element =
(trigger_event == CE_TOUCHING_X ||
trigger_event == CE_HITTING_X ||
- trigger_event == CE_HIT_BY_X);
+ trigger_event == CE_HIT_BY_X ||
+#if 1
+ /* this one was forgotten until 3.2.3 */
+ trigger_event == CE_DIGGING_X);
+#endif
if (change->can_change_or_has_action &&
change->has_event[trigger_event] &&
player->frame_counter_bored =
FrameCounter +
game.player_boring_delay_fixed +
- SimpleRND(game.player_boring_delay_random);
+ GetSimpleRandom(game.player_boring_delay_random);
player->frame_counter_sleeping =
FrameCounter +
game.player_sleeping_delay_fixed +
- SimpleRND(game.player_sleeping_delay_random);
+ GetSimpleRandom(game.player_sleeping_delay_random);
InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
}
player->anim_delay_counter =
graphic_info[special_graphic].anim_delay_fixed +
- SimpleRND(graphic_info[special_graphic].anim_delay_random);
+ GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
player->post_delay_counter =
graphic_info[special_graphic].post_delay_fixed +
- SimpleRND(graphic_info[special_graphic].post_delay_random);
+ GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
player->special_action_sleeping = special_action;
}
if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
{
int special_action =
- ACTION_BORING_1 + SimpleRND(player->num_special_action_bored);
+ ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
int special_graphic =
el_act_dir2img(player->artwork_element, special_action, move_dir);
player->anim_delay_counter =
graphic_info[special_graphic].anim_delay_fixed +
- SimpleRND(graphic_info[special_graphic].anim_delay_random);
+ GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
player->post_delay_counter =
graphic_info[special_graphic].post_delay_fixed +
- SimpleRND(graphic_info[special_graphic].post_delay_random);
+ GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
player->special_action_bored = special_action;
}
WasJustFalling[x][y]--;
if (CheckCollision[x][y] > 0)
CheckCollision[x][y]--;
+ if (CheckImpact[x][y] > 0)
+ CheckImpact[x][y]--;
GfxFrame[x][y]++;
DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
if (IS_GEM(element) || element == EL_SP_INFOTRON)
- EdelsteinFunkeln(x, y);
+ DrawTwinkleOnField(x, y);
}
else if ((element == EL_ACID ||
element == EL_EXIT_OPEN ||
nexty = dropy + GET_DY_FROM_DIR(move_direction);
ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
+
+#if USE_FIX_IMPACT_COLLISION
+ /* do not cause impact style collision by dropping elements that can fall */
+ CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
+#else
CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
+#endif
}
player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
void RaiseScoreElement(int element)
{
- switch(element)
+ switch (element)
{
case EL_EMERALD:
case EL_BD_DIAMOND:
/* ------------------------------------------------------------------------- */
-/* game engine snapshot handling */
+/* random generator functions */
+/* ------------------------------------------------------------------------- */
+
+unsigned int InitEngineRandom_RND(long seed)
+{
+ game.num_random_calls = 0;
+
+#if 0
+ unsigned int rnd_seed = InitEngineRandom(seed);
+
+ printf("::: START RND: %d\n", rnd_seed);
+
+ return rnd_seed;
+#else
+
+ return InitEngineRandom(seed);
+
+#endif
+
+}
+
+unsigned int RND(int max)
+{
+ if (max > 0)
+ {
+ game.num_random_calls++;
+
+ return GetEngineRandom(max);
+ }
+
+ return 0;
+}
+
+
+/* ------------------------------------------------------------------------- */
+/* game engine snapshot handling functions */
/* ------------------------------------------------------------------------- */
#define ARGS_ADDRESS_AND_SIZEOF(x) (&(x)), (sizeof(x))
struct EngineSnapshotInfo
{
+ /* runtime values for custom element collect score */
int collect_score[NUM_CUSTOM_ELEMENTS];
+
+ /* runtime values for group element choice position */
+ int choice_pos[NUM_GROUP_ELEMENTS];
+
+ /* runtime values for belt position animations */
int belt_graphic[4 * NUM_BELT_PARTS];
int belt_anim_mode[4 * NUM_BELT_PARTS];
};
int i, j;
for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
- engine_snapshot_rnd.collect_score[i] = element_info[i].collect_score;
+ {
+ int element = EL_CUSTOM_START + i;
+
+ engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
+ }
+
+ for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
+ {
+ int element = EL_GROUP_START + i;
+
+ engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
+ }
for (i = 0; i < 4; i++)
{
static void LoadEngineSnapshotValues_RND()
{
+ unsigned long num_random_calls = game.num_random_calls;
int i, j;
for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
- element_info[i].collect_score = engine_snapshot_rnd.collect_score[i];
+ {
+ int element = EL_CUSTOM_START + i;
+
+ element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
+ }
+
+ for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
+ {
+ int element = EL_GROUP_START + i;
+
+ element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
+ }
for (i = 0; i < 4; i++)
{
graphic_info[graphic].anim_mode = anim_mode;
}
}
+
+ if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
+ {
+ InitRND(tape.random_seed);
+ for (i = 0; i < num_random_calls; i++)
+ RND(1);
+ }
+
+ if (game.num_random_calls != num_random_calls)
+ {
+ Error(ERR_RETURN, "number of random calls out of sync");
+ Error(ERR_RETURN, "number of random calls should be %d", num_random_calls);
+ Error(ERR_RETURN, "number of random calls is %d", game.num_random_calls);
+ Error(ERR_EXIT, "this should not happen -- please debug");
+ }
}
static void SaveEngineSnapshotBuffer(void *buffer, int size)
{
FreeEngineSnapshot(); /* free previous snapshot, if needed */
+ if (level_editor_test_game) /* do not save snapshots from editor */
+ return;
+
/* copy some special values to a structure better suited for the snapshot */
SaveEngineSnapshotValues_RND();
SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
+ SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));