#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
ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
#define PENGUIN_CAN_ENTER_FIELD(e, x, y) \
- ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN ||\
+ ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
+ Feld[x][y] == EL_STEEL_EXIT_OPEN || \
IS_FOOD_PENGUIN(Feld[x][y])))
#define DRAGON_CAN_ENTER_FIELD(e, x, y) \
ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
+/* for detection of endless loops, caused by custom element programming */
+/* (using "MAX_PLAYFIELD_WIDTH" here is just a rough approximation...) */
+#define MAX_ELEMENT_CHANGE_RECURSION_DEPTH (MAX_PLAYFIELD_WIDTH)
+
+#define RECURSION_LOOP_DETECTION_START(e, rc) \
+{ \
+ if (recursion_loop_detected) \
+ return (rc); \
+ \
+ if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH) \
+ { \
+ recursion_loop_detected = TRUE; \
+ recursion_loop_element = (e); \
+ } \
+ \
+ recursion_loop_depth++; \
+}
+
+#define RECURSION_LOOP_DETECTION_END() \
+{ \
+ recursion_loop_depth--; \
+}
+
+static int recursion_loop_depth;
+static boolean recursion_loop_detected;
+static boolean recursion_loop_element;
+
/* ------------------------------------------------------------------------- */
/* definition of elements that automatically change to other elements after */
NULL,
NULL
},
+ {
+ EL_STEEL_EXIT_OPENING,
+ EL_STEEL_EXIT_OPEN,
+ 29,
+ NULL,
+ NULL,
+ NULL
+ },
+ {
+ EL_STEEL_EXIT_CLOSING,
+ EL_STEEL_EXIT_CLOSED,
+ 29,
+ NULL,
+ NULL,
+ NULL
+ },
{
EL_SP_EXIT_OPENING,
EL_SP_EXIT_OPEN,
RunTimegateWheel,
NULL
},
+ {
+ EL_DC_TIMEGATE_SWITCH_ACTIVE,
+ EL_DC_TIMEGATE_SWITCH,
+ 0,
+ InitTimegateWheel,
+ RunTimegateWheel,
+ NULL
+ },
{
EL_EMC_MAGIC_BALL_ACTIVE,
EL_EMC_MAGIC_BALL_ACTIVE,
if (init_game)
Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
break;
+
+ case EL_DC_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
+ if (init_game)
+ Feld[x][y] = EL_DC_SWITCHGATE_SWITCH_UP;
+ break;
#endif
case EL_LIGHT_SWITCH_ACTIVE:
EL_EMPTY);
}
}
+
+ /* ---------- initialize recursion detection ------------------------------ */
+ recursion_loop_depth = 0;
+ recursion_loop_detected = FALSE;
+ recursion_loop_element = EL_UNDEFINED;
}
int get_num_special_action(int element, int action_first, int action_last)
player->present = FALSE;
player->active = FALSE;
+ player->killed = FALSE;
player->action = 0;
player->effective_action = 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;
{ MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
};
- switch(element)
+ switch (element)
{
case EL_BUG_RIGHT:
case EL_BUG_UP:
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];
-
- Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
- EL_SP_EXIT_CLOSING);
+ /* close exit door after last player */
+ if (AllPlayersGone &&
+ (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
+ Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
+ Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN))
+ {
+ int element = Feld[ExitX][ExitY];
- PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
- }
+ Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
+ element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING:
+ EL_STEEL_EXIT_CLOSING);
- /* player disappears */
- DrawLevelField(ExitX, ExitY);
- }
+ PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
+ }
- for (i = 0; i < MAX_PLAYERS; i++)
- {
- struct PlayerInfo *player = &stored_player[i];
+ /* player disappears */
+ DrawLevelField(ExitX, ExitY);
+ }
- if (player->present)
+ for (i = 0; i < MAX_PLAYERS; i++)
{
- RemovePlayer(player);
+ struct PlayerInfo *player = &stored_player[i];
- /* player disappears */
- DrawLevelField(player->jx, player->jy);
+ if (player->present)
+ {
+ RemovePlayer(player);
+
+ /* player disappears */
+ DrawLevelField(player->jx, player->jy);
+ }
}
}
*sy = (sy1 + sy2) / 2;
}
-void DrawRelocateScreen(int x, int y, int move_dir, boolean center_screen,
- boolean quick_relocation)
+void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
+ boolean center_screen, boolean quick_relocation)
{
boolean ffwd_delay = (tape.playing && tape.fast_forward);
boolean no_delay = (tape.warp_forward);
if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
{
- scroll_x = (x < SBX_Left + MIDPOSX ? SBX_Left :
- x > SBX_Right + MIDPOSX ? SBX_Right :
- x - MIDPOSX);
+ if (center_screen)
+ {
+ scroll_x = (x < SBX_Left + MIDPOSX ? SBX_Left :
+ x > SBX_Right + MIDPOSX ? SBX_Right :
+ x - MIDPOSX);
+
+ scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
+ y > SBY_Lower + MIDPOSY ? SBY_Lower :
+ y - MIDPOSY);
+ }
+ else
+ {
+ /* quick relocation (without scrolling), but do not center screen */
- scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
- y > SBY_Lower + MIDPOSY ? SBY_Lower :
- y - MIDPOSY);
+ int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
+ old_x > SBX_Right + MIDPOSX ? SBX_Right :
+ old_x - MIDPOSX);
+
+ int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
+ old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
+ old_y - MIDPOSY);
+
+ int offset_x = x + (scroll_x - center_scroll_x);
+ int offset_y = y + (scroll_y - center_scroll_y);
+
+ scroll_x = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
+ offset_x > SBX_Right + MIDPOSX ? SBX_Right :
+ offset_x - MIDPOSX);
+
+ scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
+ offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
+ offset_y - MIDPOSY);
+ }
}
else
{
}
/* only visually relocate centered player */
- DrawRelocateScreen(player->jx, player->jy, player->MovDir, FALSE,
- level.instant_relocation);
+ DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
+ FALSE, level.instant_relocation);
TestIfPlayerTouchesBadThing(jx, jy);
TestIfPlayerTouchesCustomElement(jx, jy);
}
}
- switch(element)
+ switch (element)
{
case EL_BUG:
case EL_SPACESHIP:
explosion_type = EX_TYPE_DYNA;
break;
+ case EL_DC_LANDMINE:
+ explosion_type = EX_TYPE_CENTER;
+ break;
+
case EL_PENGUIN:
case EL_LAMP:
case EL_LAMP_ACTIVE:
Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
DrawLevelField(xx, yy);
}
+ else if (element == EL_DC_SWITCHGATE_SWITCH_UP ||
+ element == EL_DC_SWITCHGATE_SWITCH_DOWN)
+ {
+ Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
+ DrawLevelField(xx, yy);
+ }
#else
if (element == EL_SWITCHGATE_SWITCH_UP)
{
Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
DrawLevelField(xx, yy);
}
+ else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
+ {
+ Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
+ DrawLevelField(xx, yy);
+ }
+ else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
+ {
+ Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
+ DrawLevelField(xx, yy);
+ }
#endif
else if (element == EL_SWITCHGATE_OPEN ||
element == EL_SWITCHGATE_OPENING)
element == EL_TIMEGATE_CLOSING)
{
Feld[xx][yy] = EL_TIMEGATE_OPENING;
- PlayLevelSound(xx, yy, SND_TIMEGATE_OPENING);
+ PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
}
/*
}
+#if 1
+ Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
+ EL_DC_TIMEGATE_SWITCH_ACTIVE);
+#else
Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
+#endif
}
void Impact(int x, int y)
ToggleBeltSwitch(x, y + 1);
}
else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
- smashed == EL_SWITCHGATE_SWITCH_DOWN)
+ smashed == EL_SWITCHGATE_SWITCH_DOWN ||
+ smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
+ smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
{
ToggleSwitchgateSwitch(x, y + 1);
}
int ex = x + xy[i][0];
int ey = y + xy[i][1];
- if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
+ if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
+ Feld[ex][ey] == EL_STEEL_EXIT_OPEN))
{
attr_x = ex;
attr_y = ey;
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);
}
}
else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
{
- if (Feld[newx][newy] == EL_EXIT_OPEN)
+ if (Feld[newx][newy] == EL_EXIT_OPEN ||
+ Feld[newx][newy] == EL_STEEL_EXIT_OPEN)
{
RemoveField(x, y);
DrawLevelField(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)
static void RunTimegateWheel(int x, int y)
{
- PlayLevelSound(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
+ PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
}
static void InitMagicBallDelay(int x, int y)
PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
}
+void CheckExitSteel(int x, int y)
+{
+ if (local_player->gems_still_needed > 0 ||
+ local_player->sokobanfields_still_needed > 0 ||
+ local_player->lights_still_needed > 0)
+ {
+ int element = Feld[x][y];
+ int graphic = el2img(element);
+
+ if (IS_ANIMATED(graphic))
+ DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
+
+ return;
+ }
+
+ if (AllPlayersGone) /* do not re-open exit door closed after last player */
+ return;
+
+ Feld[x][y] = EL_STEEL_EXIT_OPENING;
+
+ PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
+}
+
void CheckExitSP(int x, int y)
{
if (local_player->gems_still_needed > 0)
PlayLevelSoundAction(ax, ay, ACTION_GROWING);
}
+void MauerAblegerStahl(int ax, int ay)
+{
+ int element = Feld[ax][ay];
+ int graphic = el2img(element);
+ boolean oben_frei = FALSE, unten_frei = FALSE;
+ boolean links_frei = FALSE, rechts_frei = FALSE;
+ boolean oben_massiv = FALSE, unten_massiv = FALSE;
+ boolean links_massiv = FALSE, rechts_massiv = FALSE;
+ boolean new_wall = FALSE;
+
+ if (IS_ANIMATED(graphic))
+ DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
+
+ if (!MovDelay[ax][ay]) /* start building new wall */
+ MovDelay[ax][ay] = 6;
+
+ if (MovDelay[ax][ay]) /* wait some time before building new wall */
+ {
+ MovDelay[ax][ay]--;
+ if (MovDelay[ax][ay])
+ return;
+ }
+
+ if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
+ oben_frei = TRUE;
+ if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
+ unten_frei = TRUE;
+ if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
+ links_frei = TRUE;
+ if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
+ rechts_frei = TRUE;
+
+ if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
+ element == EL_EXPANDABLE_STEELWALL_ANY)
+ {
+ if (oben_frei)
+ {
+ Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
+ Store[ax][ay-1] = element;
+ GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
+ if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
+ DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
+ IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
+ new_wall = TRUE;
+ }
+ if (unten_frei)
+ {
+ Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
+ Store[ax][ay+1] = element;
+ GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
+ if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
+ DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
+ IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
+ new_wall = TRUE;
+ }
+ }
+
+ if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
+ element == EL_EXPANDABLE_STEELWALL_ANY)
+ {
+ if (links_frei)
+ {
+ Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
+ Store[ax-1][ay] = element;
+ GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
+ if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
+ DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
+ IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
+ new_wall = TRUE;
+ }
+
+ if (rechts_frei)
+ {
+ Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
+ Store[ax+1][ay] = element;
+ GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
+ if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
+ DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
+ IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
+ new_wall = TRUE;
+ }
+ }
+
+ if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
+ oben_massiv = TRUE;
+ if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
+ unten_massiv = TRUE;
+ if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
+ links_massiv = TRUE;
+ if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
+ rechts_massiv = TRUE;
+
+ if (((oben_massiv && unten_massiv) ||
+ element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
+ ((links_massiv && rechts_massiv) ||
+ element == EL_EXPANDABLE_STEELWALL_VERTICAL))
+ Feld[ax][ay] = EL_WALL;
+
+ if (new_wall)
+ PlayLevelSoundAction(ax, ay, ACTION_GROWING);
+}
+
void CheckForDragon(int x, int y)
{
int i, j;
/* ---------- execute action -------------------------------------------- */
- switch(action_type)
+ switch (action_type)
{
case CA_NO_ACTION:
{
#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 */
if (!(trigger_events[trigger_element][trigger_event]))
return FALSE;
+#if 0
+ printf("::: CheckTriggeredElementChangeExt %d ... [%d, %d, %d, '%s']\n",
+ trigger_event, recursion_loop_depth, recursion_loop_detected,
+ recursion_loop_element, EL_NAME(recursion_loop_element));
+#endif
+
+ RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
+
for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
{
int element = EL_CUSTOM_START + i;
}
}
+ RECURSION_LOOP_DETECTION_END();
+
return change_done_any;
}
return FALSE;
#endif
+#if 0
+ printf("::: CheckElementChangeExt %d ... [%d, %d, %d, '%s']\n",
+ trigger_event, recursion_loop_depth, recursion_loop_detected,
+ recursion_loop_element, EL_NAME(recursion_loop_element));
+#endif
+
+ RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
+
for (p = 0; p < element_info[element].num_change_pages; p++)
{
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] &&
}
}
+ RECURSION_LOOP_DETECTION_END();
+
return change_done;
}
byte tape_action[MAX_PLAYERS];
int i;
+ /* detect endless loops, caused by custom element programming */
+ if (recursion_loop_detected && recursion_loop_depth == 0)
+ {
+ char *message = getStringCat3("Internal Error ! Element ",
+ EL_NAME(recursion_loop_element),
+ " caused endless loop ! Quit the game ?");
+
+ Error(ERR_WARN, "element '%s' caused endless loop in game engine",
+ EL_NAME(recursion_loop_element));
+
+ RequestQuitGameExt(FALSE, level_editor_test_game, message);
+
+ recursion_loop_detected = FALSE; /* if game should be continued */
+
+ free(message);
+
+ return;
+ }
+
if (game.restart_level)
StartGameActions(options.network, setup.autorecord, NEW_RANDOMIZE);
game.centered_player_nr = game.centered_player_nr_next;
game.set_centered_player = FALSE;
- DrawRelocateScreen(sx, sy, MV_NONE, TRUE, setup.quick_switch);
+ DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
DrawGameDoorValues();
}
WasJustFalling[x][y]--;
if (CheckCollision[x][y] > 0)
CheckCollision[x][y]--;
+ if (CheckImpact[x][y] > 0)
+ CheckImpact[x][y]--;
GfxFrame[x][y]++;
else if ((element == EL_ACID ||
element == EL_EXIT_OPEN ||
element == EL_SP_EXIT_OPEN ||
+ element == EL_STEEL_EXIT_OPEN ||
element == EL_SP_TERMINAL ||
element == EL_SP_TERMINAL_ACTIVE ||
element == EL_EXTRA_TIME ||
Life(x, y);
else if (element == EL_EXIT_CLOSED)
CheckExit(x, y);
+ else if (element == EL_STEEL_EXIT_CLOSED)
+ CheckExitSteel(x, y);
else if (element == EL_SP_EXIT_CLOSED)
CheckExitSP(x, y);
- else if (element == EL_EXPANDABLE_WALL_GROWING)
+ else if (element == EL_EXPANDABLE_WALL_GROWING ||
+ element == EL_EXPANDABLE_STEELWALL_GROWING)
MauerWaechst(x, y);
else if (element == EL_EXPANDABLE_WALL ||
element == EL_EXPANDABLE_WALL_HORIZONTAL ||
element == EL_EXPANDABLE_WALL_ANY ||
element == EL_BD_EXPANDABLE_WALL)
MauerAbleger(x, y);
+ else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
+ element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
+ element == EL_EXPANDABLE_STEELWALL_ANY)
+ MauerAblegerStahl(x, y);
else if (element == EL_FLAMES)
CheckForDragon(x, y);
else if (element == EL_EXPLOSION)
player->last_jy = jy;
if (Feld[jx][jy] == EL_EXIT_OPEN ||
+ Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
Feld[jx][jy] == EL_SP_EXIT_OPEN ||
Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
{
if (!player->active)
return;
+ /* the following code was introduced to prevent an infinite loop when calling
+ -> Bang()
+ -> CheckTriggeredElementChangeExt()
+ -> ExecuteCustomElementAction()
+ -> KillPlayer()
+ -> (infinitely repeating the above sequence of function calls)
+ which occurs when killing the player while having a CE with the setting
+ "kill player X when explosion of <player X>"; the solution using a new
+ field "player->killed" was chosen for backwards compatibility, although
+ clever use of the fields "player->active" etc. would probably also work */
+#if 1
+ if (player->killed)
+ return;
+#endif
+
+ player->killed = TRUE;
+
/* remove accessible field at the player's position */
Feld[jx][jy] = EL_EMPTY;
CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
player->index_bit, dig_side);
+ if (element == EL_DC_LANDMINE)
+ Bang(x, y);
+
if (Feld[x][y] != element) /* field changed by snapping */
return MP_ACTION;
return MP_NO_ACTION;
}
else if (element == EL_EXIT_OPEN ||
+ element == EL_STEEL_EXIT_OPEN ||
element == EL_SP_EXIT_OPEN ||
element == EL_SP_EXIT_OPENING)
{
ToggleBeltSwitch(x, y);
}
else if (element == EL_SWITCHGATE_SWITCH_UP ||
- element == EL_SWITCHGATE_SWITCH_DOWN)
+ element == EL_SWITCHGATE_SWITCH_DOWN ||
+ element == EL_DC_SWITCHGATE_SWITCH_UP ||
+ element == EL_DC_SWITCHGATE_SWITCH_DOWN)
{
ToggleSwitchgateSwitch(x, y);
}
{
ToggleLightSwitch(x, y);
}
- else if (element == EL_TIMEGATE_SWITCH)
+ else if (element == EL_TIMEGATE_SWITCH ||
+ element == EL_DC_TIMEGATE_SWITCH)
{
ActivateTimegateSwitch(x, y);
}
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:
}
}
-void RequestQuitGame(boolean ask_if_really_quit)
+void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
{
- if (AllPlayersGone ||
- !ask_if_really_quit ||
- level_editor_test_game ||
- Request("Do you really want to quit the game ?",
- REQ_ASK | REQ_STAY_CLOSED))
+ if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
{
#if defined(NETWORK_AVALIABLE)
if (options.network)
else
#endif
{
- if (!ask_if_really_quit || level_editor_test_game)
+ if (quick_quit)
{
game_status = GAME_MODE_MAIN;
}
}
}
- else
+ else /* continue playing the game */
{
if (tape.playing && tape.deactivate_display)
TapeDeactivateDisplayOff(TRUE);
}
}
+void RequestQuitGame(boolean ask_if_really_quit)
+{
+ boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
+ boolean skip_request = AllPlayersGone || quick_quit;
+
+ RequestQuitGameExt(skip_request, quick_quit,
+ "Do you really want to quit the game ?");
+}
+
/* ------------------------------------------------------------------------- */
/* random generator functions */
{
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));