#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)
+
+#define USE_GFX_RESET_WHEN_NOT_MOVING (USE_NEW_STUFF * 1)
+
/* for DigField() */
#define DF_NO_PUSH 0
#define EX_TYPE_DYNA (1 << 4)
#define EX_TYPE_SINGLE_TILE (EX_TYPE_CENTER | EX_TYPE_BORDER)
+#if 1
+#define PANEL_OFF() (local_player->LevelSolved_PanelOff)
+#define PANEL_DEACTIVATED(p) ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
+#define PANEL_XPOS(p) (DX + ALIGNED_MENU_XPOS(p))
+#define PANEL_YPOS(p) (DY + ALIGNED_MENU_YPOS(p))
+#else
#define PANEL_DEACTIVATED(p) ((p).x < 0 || (p).y < 0)
+#define PANEL_XPOS(p) (ALIGNED_XPOS((p).x, (p).width, (p).align))
+#define PANEL_YPOS(p) ((p).y)
+#endif
/* special positions in the game control window (relative to control window) */
-#define XX_LEVEL1 (game.panel.level.x)
-#define XX_LEVEL2 (game.panel.level.x - 1)
-#define YY_LEVEL (game.panel.level.y)
-#define XX_EMERALDS (game.panel.gems.x)
-#define YY_EMERALDS (game.panel.gems.y)
-#define XX_DYNAMITE (game.panel.inventory.x)
-#define YY_DYNAMITE (game.panel.inventory.y)
-#define XX_KEYS (game.panel.keys.x)
-#define YY_KEYS (game.panel.keys.y)
-#define XX_SCORE (game.panel.score.x)
-#define YY_SCORE (game.panel.score.y)
-#define XX_TIME1 (game.panel.time.x)
-#define XX_TIME2 (game.panel.time.x + 1)
-#define YY_TIME (game.panel.time.y)
+#define XX_LEVEL1 (PANEL_XPOS(game.panel.level))
+#define XX_LEVEL2 (PANEL_XPOS(game.panel.level) - 1)
+#define XX_LEVEL (PANEL_XPOS(game.panel.level))
+#define YY_LEVEL (PANEL_YPOS(game.panel.level))
+#define XX_EMERALDS (PANEL_XPOS(game.panel.gems))
+#define YY_EMERALDS (PANEL_YPOS(game.panel.gems))
+#define XX_DYNAMITE (PANEL_XPOS(game.panel.inventory))
+#define YY_DYNAMITE (PANEL_YPOS(game.panel.inventory))
+#define XX_KEYS (PANEL_XPOS(game.panel.keys))
+#define YY_KEYS (PANEL_YPOS(game.panel.keys))
+#define XX_SCORE (PANEL_XPOS(game.panel.score))
+#define YY_SCORE (PANEL_YPOS(game.panel.score))
+#define XX_TIME1 (PANEL_XPOS(game.panel.time))
+#define XX_TIME2 (PANEL_XPOS(game.panel.time) + 1)
+#define XX_TIME (PANEL_XPOS(game.panel.time))
+#define YY_TIME (PANEL_YPOS(game.panel.time))
/* special positions in the game control window (relative to main window) */
#define DX_LEVEL1 (DX + XX_LEVEL1)
#define DX_LEVEL2 (DX + XX_LEVEL2)
+#define DX_LEVEL (DX + XX_LEVEL)
#define DY_LEVEL (DY + YY_LEVEL)
#define DX_EMERALDS (DX + XX_EMERALDS)
#define DY_EMERALDS (DY + YY_EMERALDS)
#define DY_SCORE (DY + YY_SCORE)
#define DX_TIME1 (DX + XX_TIME1)
#define DX_TIME2 (DX + XX_TIME2)
+#define DX_TIME (DX + XX_TIME)
#define DY_TIME (DY + YY_TIME)
+#if 0
+/* game panel display and control definitions */
+
+#define GAME_CONTROL_LEVEL 0
+#define GAME_CONTROL_GEMS 1
+#define GAME_CONTROL_INVENTORY 2
+#define GAME_CONTROL_KEY_1 3
+#define GAME_CONTROL_KEY_2 4
+#define GAME_CONTROL_KEY_3 5
+#define GAME_CONTROL_KEY_4 6
+#define GAME_CONTROL_KEY_5 7
+#define GAME_CONTROL_KEY_6 8
+#define GAME_CONTROL_KEY_7 9
+#define GAME_CONTROL_KEY_8 10
+#define GAME_CONTROL_KEY_WHITE 11
+#define GAME_CONTROL_KEY_WHITE_COUNT 12
+#define GAME_CONTROL_SCORE 13
+#define GAME_CONTROL_TIME 14
+#define GAME_CONTROL_TIME_HH 15
+#define GAME_CONTROL_TIME_MM 16
+#define GAME_CONTROL_TIME_SS 17
+#define GAME_CONTROL_DROP_NEXT_1 18
+#define GAME_CONTROL_DROP_NEXT_2 19
+#define GAME_CONTROL_DROP_NEXT_3 20
+#define GAME_CONTROL_DROP_NEXT_4 21
+#define GAME_CONTROL_DROP_NEXT_5 22
+#define GAME_CONTROL_DROP_NEXT_6 23
+#define GAME_CONTROL_DROP_NEXT_7 24
+#define GAME_CONTROL_DROP_NEXT_8 25
+#define GAME_CONTROL_SHIELD_NORMAL 26
+#define GAME_CONTROL_SHIELD_NORMAL_TIME 27
+#define GAME_CONTROL_SHIELD_DEADLY 28
+#define GAME_CONTROL_SHIELD_DEADLY_TIME 29
+#define GAME_CONTROL_EXIT 30
+#define GAME_CONTROL_EM_EXIT 31
+#define GAME_CONTROL_SP_EXIT 32
+#define GAME_CONTROL_STEEL_EXIT 33
+#define GAME_CONTROL_EM_STEEL_EXIT 34
+#define GAME_CONTROL_EMC_MAGIC_BALL 35
+#define GAME_CONTROL_EMC_MAGIC_BALL_TIME 36
+#define GAME_CONTROL_LIGHT_SWITCH 37
+#define GAME_CONTROL_LIGHT_SWITCH_TIME 38
+#define GAME_CONTROL_TIMEGATE_SWITCH 39
+#define GAME_CONTROL_TIMEGATE_SWITCH_TIME 40
+#define GAME_CONTROL_SWITCHGATE_SWITCH 41
+#define GAME_CONTROL_EMC_LENSES 42
+#define GAME_CONTROL_EMC_LENSES_TIME 43
+#define GAME_CONTROL_EMC_MAGNIFIER 44
+#define GAME_CONTROL_EMC_MAGNIFIER_TIME 45
+#define GAME_CONTROL_BALLOON_SWITCH 46
+#define GAME_CONTROL_DYNABOMB_NUMBER 47
+#define GAME_CONTROL_DYNABOMB_SIZE 48
+#define GAME_CONTROL_DYNABOMB_POWER 49
+#define GAME_CONTROL_PENGUINS 50
+#define GAME_CONTROL_SOKOBAN_OBJECTS 51
+#define GAME_CONTROL_SOKOBAN_FIELDS 52
+#define GAME_CONTROL_ROBOT_WHEEL 53
+#define GAME_CONTROL_CONVEYOR_BELT_1 54
+#define GAME_CONTROL_CONVEYOR_BELT_1_SWITCH 55
+#define GAME_CONTROL_CONVEYOR_BELT_2 56
+#define GAME_CONTROL_CONVEYOR_BELT_2_SWITCH 57
+#define GAME_CONTROL_CONVEYOR_BELT_3 58
+#define GAME_CONTROL_CONVEYOR_BELT_3_SWITCH 59
+#define GAME_CONTROL_CONVEYOR_BELT_4 60
+#define GAME_CONTROL_CONVEYOR_BELT_4_SWITCH 61
+#define GAME_CONTROL_MAGIC_WALL 62
+#define GAME_CONTROL_MAGIC_WALL_TIME 63
+#define GAME_CONTROL_BD_MAGIC_WALL 64
+#define GAME_CONTROL_DC_MAGIC_WALL 65
+#define GAME_CONTROL_PLAYER_NAME 66
+#define GAME_CONTROL_LEVEL_NAME 67
+#define GAME_CONTROL_LEVEL_AUTHOR 68
+
+struct GameControlInfo
+{
+ int nr;
+
+ struct TextPosInfo *pos_text;
+ int type;
+ void *ptr;
+};
+
+static struct GameControlInfo game_controls[] =
+{
+ {
+ GAME_CONTROL_LEVEL,
+ &game.panel.level,
+ TYPE_INTEGER,
+ },
+ {
+ GAME_CONTROL_GEMS,
+ &game.panel.gems,
+ TYPE_INTEGER,
+ },
+ {
+ GAME_CONTROL_INVENTORY,
+ &game.panel.inventory,
+ TYPE_INTEGER,
+ },
+ {
+ GAME_CONTROL_KEYS,
+ &game.panel.keys,
+ TYPE_INTEGER,
+ },
+ {
+ GAME_CONTROL_SCORE,
+ &game.panel.score,
+ TYPE_INTEGER,
+ },
+ {
+ GAME_CONTROL_TIME,
+ &game.panel.time,
+ TYPE_INTEGER,
+ },
+ {
+ GAME_CONTROL_TIME_HH,
+ &game.panel.time_hh,
+ TYPE_INTEGER,
+ },
+ {
+ GAME_CONTROL_TIME_MM,
+ &game.panel.time_mm,
+ TYPE_INTEGER,
+ },
+ {
+ GAME_CONTROL_TIME_SS,
+ &game.panel.time_ss,
+ TYPE_INTEGER,
+ },
+ {
+ GAME_CONTROL_DROP_NEXT_1,
+ &game.panel.drop_next_1,
+ TYPE_INTEGER,
+ },
+ {
+ GAME_CONTROL_DROP_NEXT_2,
+ &game.panel.drop_next_2,
+ TYPE_INTEGER,
+ },
+ {
+ GAME_CONTROL_DROP_NEXT_3,
+ &game.panel.drop_next_3,
+ TYPE_INTEGER,
+ },
+ {
+ GAME_CONTROL_DROP_NEXT_4,
+ &game.panel.drop_next_4,
+ TYPE_INTEGER,
+ },
+ {
+ GAME_CONTROL_DROP_NEXT_5,
+ &game.panel.drop_next_5,
+ TYPE_INTEGER,
+ },
+ {
+ GAME_CONTROL_DROP_NEXT_6,
+ &game.panel.drop_next_6,
+ TYPE_INTEGER,
+ },
+ {
+ GAME_CONTROL_DROP_NEXT_7,
+ &game.panel.drop_next_7,
+ TYPE_INTEGER,
+ },
+ {
+ GAME_CONTROL_DROP_NEXT_8,
+ &game.panel.drop_next_8,
+ TYPE_INTEGER,
+ },
+ {
+ GAME_CONTROL_EMC_KEYS,
+ &game.panel.emc_keys,
+ TYPE_INTEGER,
+ },
+ {
+ GAME_CONTROL_KEY_1,
+ &game.panel.key_1,
+ TYPE_INTEGER,
+ },
+ {
+ GAME_CONTROL_KEY_2,
+ &game.panel.key_2,
+ TYPE_INTEGER,
+ },
+ {
+ GAME_CONTROL_KEY_3,
+ &game.panel.key_3,
+ TYPE_INTEGER,
+ },
+ {
+ GAME_CONTROL_KEY_4,
+ &game.panel.key_4,
+ TYPE_INTEGER,
+ },
+ {
+ GAME_CONTROL_KEY_5,
+ &game.panel.key_5,
+ TYPE_INTEGER,
+ },
+ {
+ GAME_CONTROL_KEY_6,
+ &game.panel.key_6,
+ TYPE_INTEGER,
+ },
+ {
+ GAME_CONTROL_KEY_7,
+ &game.panel.key_7,
+ TYPE_INTEGER,
+ },
+ {
+ GAME_CONTROL_KEY_8,
+ &game.panel.key_8,
+ TYPE_INTEGER,
+ },
+ {
+ GAME_CONTROL_KEY_WHITE,
+ &game.panel.key_white,
+ TYPE_INTEGER,
+ },
+ {
+ GAME_CONTROL_KEY_WHITE_COUNT,
+ &game.panel.key_white_count,
+ TYPE_INTEGER,
+ },
+ {
+ GAME_CONTROL_SHIELD_NORMAL,
+ &game.panel.shield_normal,
+ TYPE_INTEGER,
+ },
+ {
+ GAME_CONTROL_SHIELD_NORMAL_TIME,
+ &game.panel.shield_normal_time,
+ TYPE_INTEGER,
+ },
+ {
+ GAME_CONTROL_SHIELD_DEADLY,
+ &game.panel.shield_deadly,
+ TYPE_INTEGER,
+ },
+ {
+ GAME_CONTROL_SHIELD_DEADLY_TIME,
+ &game.panel.shield_deadly_time,
+ TYPE_INTEGER,
+ },
+ {
+ GAME_CONTROL_EXIT,
+ &game.panel.exit,
+ TYPE_INTEGER,
+ },
+ {
+ GAME_CONTROL_EM_EXIT,
+ &game.panel.em_exit,
+ TYPE_INTEGER,
+ },
+ {
+ GAME_CONTROL_SP_EXIT,
+ &game.panel.sp_exit,
+ TYPE_INTEGER,
+ },
+ {
+ GAME_CONTROL_STEEL_EXIT,
+ &game.panel.steel_exit,
+ TYPE_INTEGER,
+ },
+ {
+ GAME_CONTROL_EM_STEEL_EXIT,
+ &game.panel.em_steel_exit,
+ TYPE_INTEGER,
+ },
+ {
+ GAME_CONTROL_EMC_MAGIC_BALL,
+ &game.panel.emc_magic_ball,
+ TYPE_INTEGER,
+ },
+ {
+ GAME_CONTROL_EMC_MAGIC_BALL_TIME,
+ &game.panel.emc_magic_ball_time,
+ TYPE_INTEGER,
+ },
+ {
+ GAME_CONTROL_LIGHT_SWITCH,
+ &game.panel.light_switch,
+ TYPE_INTEGER,
+ },
+ {
+ GAME_CONTROL_LIGHT_SWITCH_TIME,
+ &game.panel.light_switch_time,
+ TYPE_INTEGER,
+ },
+ {
+ GAME_CONTROL_TIMEGATE_SWITCH,
+ &game.panel.timegate_switch,
+ TYPE_INTEGER,
+ },
+ {
+ GAME_CONTROL_TIMEGATE_SWITCH_TIME,
+ &game.panel.timegate_switch_time,
+ TYPE_INTEGER,
+ },
+ {
+ GAME_CONTROL_SWITCHGATE_SWITCH,
+ &game.panel.switchgate_switch,
+ TYPE_INTEGER,
+ },
+ {
+ GAME_CONTROL_EMC_LENSES,
+ &game.panel.emc_lenses,
+ TYPE_INTEGER,
+ },
+ {
+ GAME_CONTROL_EMC_LENSES_TIME,
+ &game.panel.emc_lenses_time,
+ TYPE_INTEGER,
+ },
+ {
+ GAME_CONTROL_EMC_MAGNIFIER,
+ &game.panel.emc_magnifier,
+ TYPE_INTEGER,
+ },
+ {
+ GAME_CONTROL_EMC_MAGNIFIER_TIME,
+ &game.panel.emc_magnifier_time,
+ TYPE_INTEGER,
+ },
+ {
+ GAME_CONTROL_BALLOON_SWITCH,
+ &game.panel.balloon_switch,
+ TYPE_INTEGER,
+ },
+ {
+ GAME_CONTROL_DYNABOMB_NUMBER,
+ &game.panel.dynabomb_number,
+ TYPE_INTEGER,
+ },
+ {
+ GAME_CONTROL_DYNABOMB_SIZE,
+ &game.panel.dynabomb_size,
+ TYPE_INTEGER,
+ },
+ {
+ GAME_CONTROL_DYNABOMB_POWER,
+ &game.panel.dynabomb_power,
+ TYPE_INTEGER,
+ },
+ {
+ GAME_CONTROL_PENGUINS,
+ &game.panel.penguins,
+ TYPE_INTEGER,
+ },
+ {
+ GAME_CONTROL_SOKOBAN_OBJECTS,
+ &game.panel.sokoban_objects,
+ TYPE_INTEGER,
+ },
+ {
+ GAME_CONTROL_SOKOBAN_FIELDS,
+ &game.panel.sokoban_fields,
+ TYPE_INTEGER,
+ },
+ {
+ GAME_CONTROL_ROBOT_WHEEL,
+ &game.panel.robot_wheel,
+ TYPE_INTEGER,
+ },
+ {
+ GAME_CONTROL_CONVEYOR_BELT_1,
+ &game.panel.conveyor_belt_1,
+ TYPE_INTEGER,
+ },
+ {
+ GAME_CONTROL_CONVEYOR_BELT_1_SWITCH,
+ &game.panel.conveyor_belt_1_switch,
+ TYPE_INTEGER,
+ },
+ {
+ GAME_CONTROL_CONVEYOR_BELT_2,
+ &game.panel.conveyor_belt_2,
+ TYPE_INTEGER,
+ },
+ {
+ GAME_CONTROL_CONVEYOR_BELT_2_SWITCH,
+ &game.panel.conveyor_belt_2_switch,
+ TYPE_INTEGER,
+ },
+ {
+ GAME_CONTROL_CONVEYOR_BELT_3,
+ &game.panel.conveyor_belt_3,
+ TYPE_INTEGER,
+ },
+ {
+ GAME_CONTROL_CONVEYOR_BELT_3_SWITCH,
+ &game.panel.conveyor_belt_3_switch,
+ TYPE_INTEGER,
+ },
+ {
+ GAME_CONTROL_CONVEYOR_BELT_4,
+ &game.panel.conveyor_belt_4,
+ TYPE_INTEGER,
+ },
+ {
+ GAME_CONTROL_CONVEYOR_BELT_4_SWITCH,
+ &game.panel.conveyor_belt_4_switch,
+ TYPE_INTEGER,
+ },
+ {
+ GAME_CONTROL_MAGIC_WALL,
+ &game.panel.magic_wall,
+ TYPE_INTEGER,
+ },
+ {
+ GAME_CONTROL_MAGIC_WALL_TIME,
+ &game.panel.magic_wall_time,
+ TYPE_INTEGER,
+ },
+ {
+ GAME_CONTROL_BD_MAGIC_WALL,
+ &game.panel.bd_magic_wall,
+ TYPE_INTEGER,
+ },
+ {
+ GAME_CONTROL_DC_MAGIC_WALL,
+ &game.panel.dc_magic_wall,
+ TYPE_INTEGER,
+ },
+ {
+ GAME_CONTROL_PLAYER_NAME,
+ &game.panel.player_name,
+ TYPE_INTEGER,
+ },
+ {
+ GAME_CONTROL_LEVEL_NAME,
+ &game.panel.level_name,
+ TYPE_INTEGER,
+ },
+ {
+ GAME_CONTROL_LEVEL_AUTHOR,
+ &game.panel.level_author,
+ TYPE_INTEGER,
+ },
+
+ {
+ -1,
+ NULL,
+ -1,
+ NULL
+ }
+};
+#endif
+
+
/* 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
(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))
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_EM_EXIT_OPEN || \
+ Feld[x][y] == EL_STEEL_EXIT_OPEN || \
+ Feld[x][y] == EL_EM_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 maximal playfield width x 10 is just a rough approximation) */
+#define MAX_ELEMENT_CHANGE_RECURSION_DEPTH (MAX_PLAYFIELD_WIDTH * 10)
+
+#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_EM_EXIT_OPENING,
+ EL_EM_EXIT_OPEN,
+ 29,
+ NULL,
+ NULL,
+ NULL
+ },
+ {
+ EL_EM_EXIT_CLOSING,
+#if 1
+ EL_EMPTY,
+#else
+ EL_EM_EXIT_CLOSED,
+#endif
+ 29,
+ NULL,
+ NULL,
+ NULL
+ },
+ {
+ EL_EM_STEEL_EXIT_OPENING,
+ EL_EM_STEEL_EXIT_OPEN,
+ 29,
+ NULL,
+ NULL,
+ NULL
+ },
+ {
+ EL_EM_STEEL_EXIT_CLOSING,
+#if 1
+ EL_STEELWALL,
+#else
+ EL_EM_STEEL_EXIT_CLOSED,
+#endif
+ 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,
{ EL_AMOEBA_DROPPING, 2 },
{ EL_QUICKSAND_FILLING, 1 },
{ EL_QUICKSAND_EMPTYING, 1 },
+ { EL_QUICKSAND_FAST_FILLING, 2 },
+ { EL_QUICKSAND_FAST_EMPTYING, 2 },
{ EL_MAGIC_WALL_FILLING, 2 },
- { EL_BD_MAGIC_WALL_FILLING, 2 },
{ EL_MAGIC_WALL_EMPTYING, 2 },
+ { EL_BD_MAGIC_WALL_FILLING, 2 },
{ EL_BD_MAGIC_WALL_EMPTYING, 2 },
+ { EL_DC_MAGIC_WALL_FILLING, 2 },
+ { EL_DC_MAGIC_WALL_EMPTYING, 2 },
{ EL_UNDEFINED, 0 },
};
(y) += playfield_scan_delta_y) \
for ((x) = playfield_scan_start_x; \
(x) >= 0 && (x) <= lev_fieldx - 1; \
- (x) += playfield_scan_delta_x) \
+ (x) += playfield_scan_delta_x)
#ifdef DEBUG
void DEBUG_SetMaximumDynamite()
void GetPlayerConfig()
{
+ GameFrameDelay = setup.game_frame_delay;
+
if (!audio.sound_available)
setup.sound_simple = FALSE;
InitJoysticks();
}
-static int getBeltNrFromBeltElement(int element)
-{
- return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
- element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
- element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
-}
-
-static int getBeltNrFromBeltActiveElement(int element)
-{
- return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
- element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
- element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
-}
-
-static int getBeltNrFromBeltSwitchElement(int element)
-{
- return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
- element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
- element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
-}
-
-static int getBeltDirNrFromBeltSwitchElement(int element)
-{
- static int belt_base_element[4] =
- {
- EL_CONVEYOR_BELT_1_SWITCH_LEFT,
- EL_CONVEYOR_BELT_2_SWITCH_LEFT,
- EL_CONVEYOR_BELT_3_SWITCH_LEFT,
- EL_CONVEYOR_BELT_4_SWITCH_LEFT
- };
-
- int belt_nr = getBeltNrFromBeltSwitchElement(element);
- int belt_dir_nr = element - belt_base_element[belt_nr];
-
- return (belt_dir_nr % 3);
-}
-
-static int getBeltDirFromBeltSwitchElement(int element)
-{
- static int belt_move_dir[3] =
- {
- MV_LEFT,
- MV_NONE,
- MV_RIGHT
- };
-
- int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
-
- return belt_move_dir[belt_dir_nr];
-}
-
-static int get_element_from_group_element(int element)
+int GetElementFromGroupElement(int element)
{
if (IS_GROUP_ELEMENT(element))
{
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:
}
else if (IS_GROUP_ELEMENT(element))
{
- Feld[x][y] = get_element_from_group_element(element);
+ Feld[x][y] = GetElementFromGroupElement(element);
InitField(x, y, init_game);
}
*/
}
-inline void DrawGameValue_Emeralds(int value)
+#if 1
+
+void DrawGameValue_Emeralds(int value)
{
- int xpos = (3 * 14 - 3 * getFontWidth(FONT_TEXT_2)) / 2;
+ struct TextPosInfo *pos = &game.panel.gems;
+#if 1
+ int font_nr = pos->font;
+#else
+ int font_nr = FONT_TEXT_2;
+#endif
+ int font_width = getFontWidth(font_nr);
+ int chars = pos->chars;
- if (PANEL_DEACTIVATED(game.panel.gems))
+ if (PANEL_DEACTIVATED(pos))
return;
- DrawText(DX_EMERALDS + xpos, DY_EMERALDS, int2str(value, 3), FONT_TEXT_2);
+ pos->width = chars * font_width;
+
+ DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
}
-inline void DrawGameValue_Dynamite(int value)
+void DrawGameValue_Dynamite(int value)
{
- int xpos = (3 * 14 - 3 * getFontWidth(FONT_TEXT_2)) / 2;
+ struct TextPosInfo *pos = &game.panel.inventory;
+#if 1
+ int font_nr = pos->font;
+#else
+ int font_nr = FONT_TEXT_2;
+#endif
+ int font_width = getFontWidth(font_nr);
+ int chars = pos->chars;
- if (PANEL_DEACTIVATED(game.panel.inventory))
+ if (PANEL_DEACTIVATED(pos))
+ return;
+
+ pos->width = chars * font_width;
+
+ DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
+}
+
+void DrawGameValue_Score(int value)
+{
+ struct TextPosInfo *pos = &game.panel.score;
+#if 1
+ int font_nr = pos->font;
+#else
+ int font_nr = FONT_TEXT_2;
+#endif
+ int font_width = getFontWidth(font_nr);
+ int chars = pos->chars;
+
+ if (PANEL_DEACTIVATED(pos))
return;
- DrawText(DX_DYNAMITE + xpos, DY_DYNAMITE, int2str(value, 3), FONT_TEXT_2);
+ pos->width = chars * font_width;
+
+ DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
}
-inline void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
+void DrawGameValue_Time(int value)
{
+ struct TextPosInfo *pos = &game.panel.time;
+ static int last_value = -1;
+ int chars1 = 3;
+ int chars2 = 4;
+ int chars = pos->chars;
+#if 1
+ int font1_nr = pos->font;
+ int font2_nr = pos->font_alt;
+#else
+ int font1_nr = FONT_TEXT_2;
+ int font2_nr = FONT_TEXT_1;
+#endif
+ int font_nr = font1_nr;
+ boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
+
+ if (PANEL_DEACTIVATED(pos))
+ return;
+
+ if (use_dynamic_chars) /* use dynamic number of chars */
+ {
+ chars = (value < 1000 ? chars1 : chars2);
+ font_nr = (value < 1000 ? font1_nr : font2_nr);
+ }
+
+ /* clear background if value just changed its size (dynamic chars only) */
+ if (use_dynamic_chars && (last_value < 1000) != (value < 1000))
+ {
+ int width1 = chars1 * getFontWidth(font1_nr);
+ int width2 = chars2 * getFontWidth(font2_nr);
+ int max_width = MAX(width1, width2);
+ int max_height = MAX(getFontHeight(font1_nr), getFontHeight(font2_nr));
+
+ pos->width = max_width;
+
+ ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
+ max_width, max_height);
+ }
+
+ pos->width = chars * getFontWidth(font_nr);
+
+ DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
+
+ last_value = value;
+}
+
+void DrawGameValue_Level(int value)
+{
+ struct TextPosInfo *pos = &game.panel.level_number;
+ int chars1 = 2;
+ int chars2 = 3;
+ int chars = pos->chars;
+#if 1
+ int font1_nr = pos->font;
+ int font2_nr = pos->font_alt;
+#else
+ int font1_nr = FONT_TEXT_2;
+ int font2_nr = FONT_TEXT_1;
+#endif
+ int font_nr = font1_nr;
+ boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
+
+ if (PANEL_DEACTIVATED(pos))
+ return;
+
+ if (use_dynamic_chars) /* use dynamic number of chars */
+ {
+ chars = (level_nr < 100 ? chars1 : chars2);
+ font_nr = (level_nr < 100 ? font1_nr : font2_nr);
+ }
+
+ pos->width = chars * getFontWidth(font_nr);
+
+ DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
+}
+
+void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
+{
+#if 0
+ struct TextPosInfo *pos = &game.panel.keys;
+#endif
+#if 0
int base_key_graphic = EL_KEY_1;
+#endif
int i;
- if (PANEL_DEACTIVATED(game.panel.keys))
+#if 0
+ if (PANEL_DEACTIVATED(pos))
return;
+#endif
+#if 0
if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
base_key_graphic = EL_EM_KEY_1;
+#endif
+
+#if 0
+ pos->width = 4 * MINI_TILEX;
+#endif
+#if 1
+ for (i = 0; i < MAX_NUM_KEYS; i++)
+#else
/* currently only 4 of 8 possible keys are displayed */
for (i = 0; i < STD_NUM_KEYS; i++)
+#endif
{
- int x = XX_KEYS + i * MINI_TILEX;
- int y = YY_KEYS;
+#if 1
+ struct TextPosInfo *pos = &game.panel.key[i];
+#endif
+ int src_x = DOOR_GFX_PAGEX5 + 18;
+ int src_y = DOOR_GFX_PAGEY1 + 123;
+#if 1
+ int dst_x = PANEL_XPOS(pos);
+ int dst_y = PANEL_YPOS(pos);
+#else
+ int dst_x = PANEL_XPOS(pos) + i * MINI_TILEX;
+ int dst_y = PANEL_YPOS(pos);
+#endif
+
+#if 1
+ int element = (i >= STD_NUM_KEYS ? EL_EMC_KEY_5 - 4 :
+ level.game_engine_type == GAME_ENGINE_TYPE_EM ? EL_EM_KEY_1 :
+ EL_KEY_1) + i;
+ int graphic = el2edimg(element);
+#endif
+
+#if 1
+ if (PANEL_DEACTIVATED(pos))
+ continue;
+#endif
+
+#if 0
+ /* masked blit with tiles from half-size scaled bitmap does not work yet
+ (no mask bitmap created for these sizes after loading and scaling) --
+ solution: load without creating mask, scale, then create final mask */
+
+ BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
+ MINI_TILEX, MINI_TILEY, dst_x, dst_y);
if (key[i])
- DrawMiniGraphicExt(drawto, DX + x,DY + y, el2edimg(base_key_graphic + i));
+ {
+#if 0
+ int graphic = el2edimg(base_key_graphic + i);
+#endif
+ Bitmap *src_bitmap;
+ int src_x, src_y;
+
+ getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
+
+ SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
+ dst_x - src_x, dst_y - src_y);
+ BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, MINI_TILEX, MINI_TILEY,
+ dst_x, dst_y);
+ }
+#else
+#if 1
+ if (key[i])
+ DrawMiniGraphicExt(drawto, dst_x, dst_y, graphic);
else
- BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
- DOOR_GFX_PAGEX5 + x, y, MINI_TILEX, MINI_TILEY, DX + x,DY + y);
+ BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
+ MINI_TILEX, MINI_TILEY, dst_x, dst_y);
+#else
+ if (key[i])
+ DrawMiniGraphicExt(drawto, dst_x, dst_y, el2edimg(base_key_graphic + i));
+ else
+ BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
+ MINI_TILEX, MINI_TILEY, dst_x, dst_y);
+#endif
+#endif
}
}
-inline void DrawGameValue_Score(int value)
+#else
+
+void DrawGameValue_Emeralds(int value)
{
- int xpos = (5 * 14 - 5 * getFontWidth(FONT_TEXT_2)) / 2;
+ int font_nr = FONT_TEXT_2;
+ int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
+
+ if (PANEL_DEACTIVATED(game.panel.gems))
+ return;
+
+ DrawText(DX_EMERALDS + xpos, DY_EMERALDS, int2str(value, 3), font_nr);
+}
+
+void DrawGameValue_Dynamite(int value)
+{
+ int font_nr = FONT_TEXT_2;
+ int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
+
+ if (PANEL_DEACTIVATED(game.panel.inventory))
+ return;
+
+ DrawText(DX_DYNAMITE + xpos, DY_DYNAMITE, int2str(value, 3), font_nr);
+}
+
+void DrawGameValue_Score(int value)
+{
+ int font_nr = FONT_TEXT_2;
+ int xpos = (5 * 14 - 5 * getFontWidth(font_nr)) / 2;
if (PANEL_DEACTIVATED(game.panel.score))
return;
- DrawText(DX_SCORE + xpos, DY_SCORE, int2str(value, 5), FONT_TEXT_2);
+ DrawText(DX_SCORE + xpos, DY_SCORE, int2str(value, 5), font_nr);
}
-inline void DrawGameValue_Time(int value)
+void DrawGameValue_Time(int value)
{
- int xpos3 = (3 * 14 - 3 * getFontWidth(FONT_TEXT_2)) / 2;
- int xpos4 = (4 * 10 - 4 * getFontWidth(FONT_LEVEL_NUMBER)) / 2;
+ int font1_nr = FONT_TEXT_2;
+#if 1
+ int font2_nr = FONT_TEXT_1;
+#else
+ int font2_nr = FONT_LEVEL_NUMBER;
+#endif
+ int xpos3 = (3 * 14 - 3 * getFontWidth(font1_nr)) / 2;
+ int xpos4 = (4 * 10 - 4 * getFontWidth(font2_nr)) / 2;
if (PANEL_DEACTIVATED(game.panel.time))
return;
ClearRectangleOnBackground(drawto, DX_TIME1, DY_TIME, 14 * 3, 14);
if (value < 1000)
- DrawText(DX_TIME1 + xpos3, DY_TIME, int2str(value, 3), FONT_TEXT_2);
+ DrawText(DX_TIME1 + xpos3, DY_TIME, int2str(value, 3), font1_nr);
else
- DrawText(DX_TIME2 + xpos4, DY_TIME, int2str(value, 4), FONT_LEVEL_NUMBER);
+ DrawText(DX_TIME2 + xpos4, DY_TIME, int2str(value, 4), font2_nr);
}
-inline void DrawGameValue_Level(int value)
+void DrawGameValue_Level(int value)
{
+ int font1_nr = FONT_TEXT_2;
+#if 1
+ int font2_nr = FONT_TEXT_1;
+#else
+ int font2_nr = FONT_LEVEL_NUMBER;
+#endif
+
if (PANEL_DEACTIVATED(game.panel.level))
return;
if (level_nr < 100)
- DrawText(DX_LEVEL1, DY_LEVEL, int2str(value, 2), FONT_TEXT_2);
+ DrawText(DX_LEVEL1, DY_LEVEL, int2str(value, 2), font1_nr);
else
- DrawText(DX_LEVEL2, DY_LEVEL, int2str(value, 3), FONT_LEVEL_NUMBER);
+ DrawText(DX_LEVEL2, DY_LEVEL, int2str(value, 3), font2_nr);
+}
+
+void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
+{
+ int base_key_graphic = EL_KEY_1;
+ int i;
+
+ if (PANEL_DEACTIVATED(game.panel.keys))
+ return;
+
+ if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
+ base_key_graphic = EL_EM_KEY_1;
+
+ /* currently only 4 of 8 possible keys are displayed */
+ for (i = 0; i < STD_NUM_KEYS; i++)
+ {
+ int x = XX_KEYS + i * MINI_TILEX;
+ int y = YY_KEYS;
+
+ if (key[i])
+ DrawMiniGraphicExt(drawto, DX + x,DY + y, el2edimg(base_key_graphic + i));
+ else
+ BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
+ DOOR_GFX_PAGEX5 + x, y, MINI_TILEX, MINI_TILEY, DX + x,DY + y);
+ }
}
+#endif
+
void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
int key_bits)
{
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;
for (j = 0; j < MAX_NUM_KEYS; j++)
player->key[j] = FALSE;
+ player->num_white_keys = 0;
+
player->dynabomb_count = 0;
player->dynabomb_size = 1;
player->dynabombs_left = 0;
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;
player->LevelSolved = FALSE;
player->GameOver = FALSE;
+ player->LevelSolved_GameWon = FALSE;
player->LevelSolved_GameEnd = FALSE;
+ player->LevelSolved_PanelOff = FALSE;
player->LevelSolved_SaveTape = FALSE;
player->LevelSolved_SaveScore = FALSE;
}
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 time, time_final;
static int score, score_final;
- static int game_over_delay = 0;
- int game_over_delay_value = 50;
+ static int game_over_delay_1 = 0;
+ static int game_over_delay_2 = 0;
+ int game_over_delay_value_1 = 50;
+ int game_over_delay_value_2 = 50;
- if (!local_player->LevelSolved_GameEnd)
+ if (!local_player->LevelSolved_GameWon)
{
int i;
if (local_player->MovPos)
return;
- local_player->LevelSolved_GameEnd = TRUE;
+ local_player->LevelSolved_GameWon = TRUE;
local_player->LevelSolved_SaveTape = tape.recording;
local_player->LevelSolved_SaveScore = !tape.playing;
TapeStop();
#endif
- game_over_delay = game_over_delay_value;
+ game_over_delay_1 = game_over_delay_value_1;
+ game_over_delay_2 = game_over_delay_value_2;
time = time_final = (level.time == 0 ? TimePlayed : TimeLeft);
score = score_final = local_player->score_final;
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 ||
+ Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
+ Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
+ Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
+ {
+ int element = Feld[ExitX][ExitY];
- Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
- EL_SP_EXIT_CLOSING);
+#if 0
+ if (element == EL_EM_EXIT_OPEN ||
+ element == EL_EM_STEEL_EXIT_OPEN)
+ {
+ Bang(ExitX, ExitY);
+ }
+ else
+#endif
+ {
+ Feld[ExitX][ExitY] =
+ (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
+ element == EL_EM_EXIT_OPEN ? EL_EM_EXIT_CLOSING :
+ element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING:
+ element == EL_STEEL_EXIT_OPEN ? EL_STEEL_EXIT_CLOSING:
+ EL_EM_STEEL_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)
+ 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);
+ }
}
}
PlaySound(SND_GAME_WINNING);
}
- if (game_over_delay > 0)
+ if (game_over_delay_1 > 0)
{
- game_over_delay--;
+ game_over_delay_1--;
return;
}
PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
else
PlaySound(SND_GAME_LEVELTIME_BONUS);
+
+ return;
+ }
+
+ local_player->LevelSolved_PanelOff = TRUE;
+
+ if (game_over_delay_2 > 0)
+ {
+ game_over_delay_2--;
+
+ return;
}
+
+#if 1
+ GameEnd();
+#endif
}
void GameEnd()
int hi_pos;
boolean raise_level = FALSE;
+ local_player->LevelSolved_GameEnd = TRUE;
+
CloseDoor(DOOR_CLOSE_1);
if (local_player->LevelSolved_SaveTape)
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
+#if 1
+ is_moving_before = (WasJustMoving[x][y] != 0);
+#else
+ /* (!!! this does not work -- WasJustMoving is NOT a boolean value !!!) */
+ is_moving_before = WasJustMoving[x][y];
+#endif
+#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;
if (element == EL_BLOCKED &&
(Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
+ Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
+ Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
next_element = get_next_element(Feld[oldx][oldy]);
*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 */
+
+ 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);
- scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
- y > SBY_Lower + MIDPOSY ? SBY_Lower :
- 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:
+#if 0
+ case EL_EM_EXIT_OPEN:
+ case EL_EM_STEEL_EXIT_OPEN:
+#endif
+ 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)
object_hit = TRUE;
}
+
+ if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
+ {
+ RemoveMovingField(x, y + 1);
+ Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
+ Feld[x][y + 2] = EL_ROCK;
+ DrawLevelField(x, y + 2);
+
+ object_hit = TRUE;
+ }
#endif
if (object_hit)
Bang(x, y);
return;
}
- else if (impact && element == EL_PEARL)
+ else if (impact && element == EL_PEARL &&
+ smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
{
ResetGfxAnimation(x, y);
if (object_hit) /* check which object was hit */
{
- if (CAN_PASS_MAGIC_WALL(element) &&
- (smashed == EL_MAGIC_WALL ||
- smashed == EL_BD_MAGIC_WALL))
+ if ((CAN_PASS_MAGIC_WALL(element) &&
+ (smashed == EL_MAGIC_WALL ||
+ smashed == EL_BD_MAGIC_WALL)) ||
+ (CAN_PASS_DC_MAGIC_WALL(element) &&
+ smashed == EL_DC_MAGIC_WALL))
{
int xx, yy;
int activated_magic_wall =
(smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
- EL_BD_MAGIC_WALL_ACTIVE);
+ smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
+ EL_DC_MAGIC_WALL_ACTIVE);
/* activate magic wall / mill */
SCAN_PLAYFIELD(xx, yy)
+ {
if (Feld[xx][yy] == smashed)
Feld[xx][yy] = activated_magic_wall;
+ }
game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
game.magic_wall_active = TRUE;
PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
SND_MAGIC_WALL_ACTIVATING :
- SND_BD_MAGIC_WALL_ACTIVATING));
+ smashed == EL_BD_MAGIC_WALL ?
+ SND_BD_MAGIC_WALL_ACTIVATING :
+ SND_DC_MAGIC_WALL_ACTIVATING));
}
if (IS_PLAYER(x, y + 1))
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);
}
/* play sound of magic wall / mill */
if (!last_line &&
(Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
- Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
+ Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
+ Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
{
if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
+ else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
+ PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
return;
}
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_EM_EXIT_OPEN ||
+ Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
+ Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
{
attr_x = ex;
attr_y = ey;
PlayLevelSoundAction(x, y, ACTION_FILLING);
}
}
+ else if (element == EL_QUICKSAND_FAST_FULL)
+ {
+ if (IS_FREE(x, y + 1))
+ {
+ InitMovingField(x, y, MV_DOWN);
+ started_moving = TRUE;
+
+ Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
+#if USE_QUICKSAND_BD_ROCK_BUGFIX
+ if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
+ Store[x][y] = EL_ROCK;
+#else
+ Store[x][y] = EL_ROCK;
+#endif
+
+ PlayLevelSoundAction(x, y, ACTION_EMPTYING);
+ }
+ else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
+ {
+ if (!MovDelay[x][y])
+ MovDelay[x][y] = TILEY + 1;
+
+ if (MovDelay[x][y])
+ {
+ MovDelay[x][y]--;
+ if (MovDelay[x][y])
+ return;
+ }
+
+ Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
+ Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
+ Store[x][y + 1] = Store[x][y];
+ Store[x][y] = 0;
+
+ PlayLevelSoundAction(x, y, ACTION_FILLING);
+ }
+ }
else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
{
PlayLevelSoundAction(x, y, ACTION_FILLING);
}
+ else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
+ Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
+ {
+ InitMovingField(x, y, MV_DOWN);
+ started_moving = TRUE;
+
+ Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
+ Store[x][y] = element;
+
+ PlayLevelSoundAction(x, y, ACTION_FILLING);
+ }
else if (element == EL_MAGIC_WALL_FULL)
{
if (IS_FREE(x, y + 1))
started_moving = TRUE;
Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
- Store[x][y] = EL_CHANGED2(Store[x][y]);
+ Store[x][y] = EL_CHANGED_BD(Store[x][y]);
}
else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
{
Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
- Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
+ Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
+ Store[x][y] = 0;
+ }
+ }
+ else if (element == EL_DC_MAGIC_WALL_FULL)
+ {
+ if (IS_FREE(x, y + 1))
+ {
+ InitMovingField(x, y, MV_DOWN);
+ started_moving = TRUE;
+
+ Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
+ Store[x][y] = EL_CHANGED_DC(Store[x][y]);
+ }
+ else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
+ {
+ if (!MovDelay[x][y])
+ MovDelay[x][y] = TILEY/4 + 1;
+
+ if (MovDelay[x][y])
+ {
+ MovDelay[x][y]--;
+ if (MovDelay[x][y])
+ return;
+ }
+
+ Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
+ Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
+ Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
Store[x][y] = 0;
}
}
- else if (CAN_PASS_MAGIC_WALL(element) &&
- (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
- Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
+ else if ((CAN_PASS_MAGIC_WALL(element) &&
+ (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
+ Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
+ (CAN_PASS_DC_MAGIC_WALL(element) &&
+ (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
+
{
InitMovingField(x, y, MV_DOWN);
started_moving = TRUE;
Feld[x][y] =
(Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
- EL_BD_MAGIC_WALL_FILLING);
+ Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
+ EL_DC_MAGIC_WALL_FILLING);
Store[x][y] = element;
}
else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
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_EM_EXIT_OPEN ||
+ Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
+ Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
{
RemoveField(x, y);
DrawLevelField(x, y);
if (ABS(MovPos[x][y]) < TILEX)
{
+#if 0
+ int ee = Feld[x][y];
+ int gg = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
+ int ff = getGraphicAnimationFrame(gg, GfxFrame[x][y]);
+
+ printf("::: %d.%d: moving %d ... [%d, %d, %d] [%d, %d, %d]\n",
+ x, y, ABS(MovPos[x][y]),
+ ee, gg, ff,
+ GfxAction[x][y], GfxDir[x][y], GfxFrame[x][y]);
+#endif
+
DrawLevelField(x, y);
return; /* element is still moving */
Feld[x][y] = get_next_element(element);
element = Feld[newx][newy] = Store[x][y];
}
+ else if (element == EL_QUICKSAND_FAST_FILLING)
+ {
+ element = Feld[newx][newy] = get_next_element(element);
+ Store[newx][newy] = Store[x][y];
+ }
+ else if (element == EL_QUICKSAND_FAST_EMPTYING)
+ {
+ Feld[x][y] = get_next_element(element);
+ element = Feld[newx][newy] = Store[x][y];
+ }
else if (element == EL_MAGIC_WALL_FILLING)
{
element = Feld[newx][newy] = get_next_element(element);
Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
element = Feld[newx][newy] = Store[x][y];
+#if USE_NEW_CUSTOM_VALUE
+ InitField(newx, newy, FALSE);
+#endif
+ }
+ else if (element == EL_DC_MAGIC_WALL_FILLING)
+ {
+ element = Feld[newx][newy] = get_next_element(element);
+ if (!game.magic_wall_active)
+ element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
+ Store[newx][newy] = Store[x][y];
+ }
+ else if (element == EL_DC_MAGIC_WALL_EMPTYING)
+ {
+ Feld[x][y] = get_next_element(element);
+ if (!game.magic_wall_active)
+ Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
+ element = Feld[newx][newy] = Store[x][y];
+
#if USE_NEW_CUSTOM_VALUE
InitField(newx, newy, FALSE);
#endif
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)
if (IS_FREE(x, y) ||
CAN_GROW_INTO(Feld[x][y]) ||
- Feld[x][y] == EL_QUICKSAND_EMPTY)
+ Feld[x][y] == EL_QUICKSAND_EMPTY ||
+ Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
{
newax = x;
neway = y;
if (IS_FREE(x, y) ||
CAN_GROW_INTO(Feld[x][y]) ||
- Feld[x][y] == EL_QUICKSAND_EMPTY)
+ Feld[x][y] == EL_QUICKSAND_EMPTY ||
+ Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
{
newax = x;
neway = y;
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 CheckExitEM(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_EM_EXIT_OPENING;
+
+ PlayLevelSoundNearest(x, y, SND_CLASS_EM_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 CheckExitSteelEM(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_EM_STEEL_EXIT_OPENING;
+
+ PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
+}
+
void CheckExitSP(int x, int y)
{
if (local_player->gems_still_needed > 0)
}
}
-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;
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:
{
(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);
static void CreateFieldExt(int x, int y, int element, boolean is_change)
{
int old_element = Feld[x][y];
- int new_element = get_element_from_group_element(element);
+ int new_element = GetElementFromGroupElement(element);
int previous_move_direction = MovDir[x][y];
#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 (change->can_change)
{
- ResetGfxAnimation(x, y);
- ResetRandomAnimationValue(x, y);
+#if 1
+ /* !!! not clear why graphic animation should be reset at all here !!! */
+ /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
+#if USE_GFX_RESET_WHEN_NOT_MOVING
+ /* when a custom element is about to change (for example by change delay),
+ do not reset graphic animation when the custom element is moving */
+ if (!IS_MOVING(x, y))
+#endif
+ {
+ ResetGfxAnimation(x, y);
+ ResetRandomAnimationValue(x, y);
+ }
+#endif
if (change->pre_change_function)
change->pre_change_function(x, y);
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);
AllPlayersGone = TRUE;
}
- if (local_player->LevelSolved)
+ if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
GameWon();
if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
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();
}
/* continue moving after pushing (this is actually a bug) */
if (!IS_MOVING(x, y))
- {
Stop[x][y] = FALSE;
- }
}
}
}
+#if 0
+ debug_print_timestamp(0, "start main loop profiling");
+#endif
+
SCAN_PLAYFIELD(x, y)
{
ChangeCount[x][y] = 0;
WasJustFalling[x][y]--;
if (CheckCollision[x][y] > 0)
CheckCollision[x][y]--;
+ if (CheckImpact[x][y] > 0)
+ CheckImpact[x][y]--;
GfxFrame[x][y]++;
#endif
}
+#if 0
+ debug_print_timestamp(0, "- time for pre-main loop:");
+#endif
+
+#if 0 // -------------------- !!! TEST ONLY !!! --------------------
+ SCAN_PLAYFIELD(x, y)
+ {
+ element = Feld[x][y];
+ graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
+
+#if 1
+ {
+#if 1
+ int element2 = element;
+ int graphic2 = graphic;
+#else
+ int element2 = Feld[x][y];
+ int graphic2 = el_act_dir2img(element2, GfxAction[x][y], GfxDir[x][y]);
+#endif
+ int last_gfx_frame = GfxFrame[x][y];
+
+ if (graphic_info[graphic2].anim_global_sync)
+ GfxFrame[x][y] = FrameCounter;
+ else if (ANIM_MODE(graphic2) == ANIM_CE_VALUE)
+ GfxFrame[x][y] = CustomValue[x][y];
+ else if (ANIM_MODE(graphic2) == ANIM_CE_SCORE)
+ GfxFrame[x][y] = element_info[element2].collect_score;
+ else if (ANIM_MODE(graphic2) == ANIM_CE_DELAY)
+ GfxFrame[x][y] = ChangeDelay[x][y];
+
+ if (redraw && GfxFrame[x][y] != last_gfx_frame)
+ DrawLevelGraphicAnimation(x, y, graphic2);
+ }
+#else
+ ResetGfxFrame(x, y, TRUE);
+#endif
+
+#if 1
+ if (ANIM_MODE(graphic) == ANIM_RANDOM &&
+ IS_NEXT_FRAME(GfxFrame[x][y], graphic))
+ ResetRandomAnimationValue(x, y);
+#endif
+
+#if 1
+ SetRandomAnimationValue(x, y);
+#endif
+
+#if 1
+ PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
+#endif
+ }
+#endif // -------------------- !!! TEST ONLY !!! --------------------
+
+#if 0
+ debug_print_timestamp(0, "- time for TEST loop: -->");
+#endif
+
SCAN_PLAYFIELD(x, y)
{
element = Feld[x][y];
graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
}
+#if 0 // ---------------------------------------------------------------------
+
+ if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
+ {
+ StartMoving(x, y);
+
+ element = Feld[x][y];
+ graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
+
+ if (IS_ANIMATED(graphic) &&
+ !IS_MOVING(x, y) &&
+ !Stop[x][y])
+ DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
+
+ if (IS_GEM(element) || element == EL_SP_INFOTRON)
+ DrawTwinkleOnField(x, y);
+ }
+ else if (IS_MOVING(x, y))
+ ContinueMoving(x, y);
+ else
+ {
+ switch (element)
+ {
+ case EL_ACID:
+ case EL_EXIT_OPEN:
+ case EL_EM_EXIT_OPEN:
+ case EL_SP_EXIT_OPEN:
+ case EL_STEEL_EXIT_OPEN:
+ case EL_EM_STEEL_EXIT_OPEN:
+ case EL_SP_TERMINAL:
+ case EL_SP_TERMINAL_ACTIVE:
+ case EL_EXTRA_TIME:
+ case EL_SHIELD_NORMAL:
+ case EL_SHIELD_DEADLY:
+ if (IS_ANIMATED(graphic))
+ DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
+ break;
+
+ case EL_DYNAMITE_ACTIVE:
+ case EL_EM_DYNAMITE_ACTIVE:
+ case EL_DYNABOMB_PLAYER_1_ACTIVE:
+ case EL_DYNABOMB_PLAYER_2_ACTIVE:
+ case EL_DYNABOMB_PLAYER_3_ACTIVE:
+ case EL_DYNABOMB_PLAYER_4_ACTIVE:
+ case EL_SP_DISK_RED_ACTIVE:
+ CheckDynamite(x, y);
+ break;
+
+ case EL_AMOEBA_GROWING:
+ AmoebeWaechst(x, y);
+ break;
+
+ case EL_AMOEBA_SHRINKING:
+ AmoebaDisappearing(x, y);
+ break;
+
+#if !USE_NEW_AMOEBA_CODE
+ case EL_AMOEBA_WET:
+ case EL_AMOEBA_DRY:
+ case EL_AMOEBA_FULL:
+ case EL_BD_AMOEBA:
+ case EL_EMC_DRIPPER:
+ AmoebeAbleger(x, y);
+ break;
+#endif
+
+ case EL_GAME_OF_LIFE:
+ case EL_BIOMAZE:
+ Life(x, y);
+ break;
+
+ case EL_EXIT_CLOSED:
+ CheckExit(x, y);
+ break;
+
+ case EL_EM_EXIT_CLOSED:
+ CheckExitEM(x, y);
+ break;
+
+ case EL_STEEL_EXIT_CLOSED:
+ CheckExitSteel(x, y);
+ break;
+
+ case EL_EM_STEEL_EXIT_CLOSED:
+ CheckExitSteelEM(x, y);
+ break;
+
+ case EL_SP_EXIT_CLOSED:
+ CheckExitSP(x, y);
+ break;
+
+ case EL_EXPANDABLE_WALL_GROWING:
+ case EL_EXPANDABLE_STEELWALL_GROWING:
+ MauerWaechst(x, y);
+ break;
+
+ case EL_EXPANDABLE_WALL:
+ case EL_EXPANDABLE_WALL_HORIZONTAL:
+ case EL_EXPANDABLE_WALL_VERTICAL:
+ case EL_EXPANDABLE_WALL_ANY:
+ case EL_BD_EXPANDABLE_WALL:
+ MauerAbleger(x, y);
+ break;
+
+ case EL_EXPANDABLE_STEELWALL_HORIZONTAL:
+ case EL_EXPANDABLE_STEELWALL_VERTICAL:
+ case EL_EXPANDABLE_STEELWALL_ANY:
+ MauerAblegerStahl(x, y);
+ break;
+
+ case EL_FLAMES:
+ CheckForDragon(x, y);
+ break;
+
+ case EL_EXPLOSION:
+ break;
+
+ case EL_ELEMENT_SNAPPING:
+ case EL_DIAGONAL_SHRINKING:
+ case EL_DIAGONAL_GROWING:
+ {
+ graphic =
+ el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
+
+ DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
+ break;
+ }
+
+ default:
+ if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
+ DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
+ break;
+ }
+ }
+
+#else // ---------------------------------------------------------------------
+
if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
{
StartMoving(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 ||
+ element == EL_EM_EXIT_OPEN ||
element == EL_SP_EXIT_OPEN ||
+ element == EL_STEEL_EXIT_OPEN ||
+ element == EL_EM_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_EM_EXIT_CLOSED)
+ CheckExitEM(x, y);
+ else if (element == EL_STEEL_EXIT_CLOSED)
+ CheckExitSteel(x, y);
+ else if (element == EL_EM_STEEL_EXIT_CLOSED)
+ CheckExitSteelEM(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)
else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
+#endif // ---------------------------------------------------------------------
+
if (IS_BELT_ACTIVE(element))
PlayLevelSoundAction(x, y, ACTION_ACTIVE);
element == EL_MAGIC_WALL_EMPTYING ||
element == EL_BD_MAGIC_WALL_FULL ||
element == EL_BD_MAGIC_WALL_ACTIVE ||
- element == EL_BD_MAGIC_WALL_EMPTYING) &&
+ element == EL_BD_MAGIC_WALL_EMPTYING ||
+ element == EL_DC_MAGIC_WALL_FULL ||
+ element == EL_DC_MAGIC_WALL_ACTIVE ||
+ element == EL_DC_MAGIC_WALL_EMPTYING) &&
ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
{
magic_wall_x = x;
}
}
+#if 0
+ debug_print_timestamp(0, "- time for MAIN loop: -->");
+#endif
+
#if USE_NEW_AMOEBA_CODE
/* new experimental amoeba growth stuff */
if (!(FrameCounter % 8))
(element == EL_EMPTY ||
CAN_GROW_INTO(element) ||
element == EL_QUICKSAND_EMPTY ||
+ element == EL_QUICKSAND_FAST_EMPTY ||
element == EL_ACID_SPLASH_LEFT ||
element == EL_ACID_SPLASH_RIGHT))
{
element == EL_BD_MAGIC_WALL_ACTIVE ||
element == EL_BD_MAGIC_WALL_EMPTYING)
PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
+ else if (element == EL_DC_MAGIC_WALL_FULL ||
+ element == EL_DC_MAGIC_WALL_ACTIVE ||
+ element == EL_DC_MAGIC_WALL_EMPTYING)
+ PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
else
PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
}
Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
DrawLevelField(x, y);
}
+ else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
+ element == EL_DC_MAGIC_WALL_FULL)
+ {
+ Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
+ DrawLevelField(x, y);
+ }
}
game.magic_wall_active = FALSE;
local_player->show_envelope = 0;
}
+#if 0
+ debug_print_timestamp(0, "stop main loop profiling ");
+ printf("----------------------------------------------------------\n");
+#endif
+
/* use random number generator in every frame to make it less predictable */
if (game.engine_version >= VERSION_IDENT(3,1,1,0))
RND(1);
void ScrollLevel(int dx, int dy)
{
+#if 1
+ static Bitmap *bitmap_db_field2 = NULL;
int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
int x, y;
+#else
+ int i, x, y;
+#endif
+
+#if 0
+ /* !!! THIS IS APPARENTLY WRONG FOR PLAYER RELOCATION !!! */
+ /* only horizontal XOR vertical scroll direction allowed */
+ if ((dx == 0 && dy == 0) || (dx != 0 && dy != 0))
+ return;
+#endif
+
+#if 1
+ if (bitmap_db_field2 == NULL)
+ bitmap_db_field2 = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH);
+
+ /* needed when blitting directly to same bitmap -- should not be needed with
+ recent SDL libraries, but apparently does not work in 1.2.11 directly */
+ BlitBitmap(drawto_field, bitmap_db_field2,
+ FX + TILEX * (dx == -1) - softscroll_offset,
+ FY + TILEY * (dy == -1) - softscroll_offset,
+ SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
+ SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
+ FX + TILEX * (dx == 1) - softscroll_offset,
+ FY + TILEY * (dy == 1) - softscroll_offset);
+ BlitBitmap(bitmap_db_field2, drawto_field,
+ FX + TILEX * (dx == 1) - softscroll_offset,
+ FY + TILEY * (dy == 1) - softscroll_offset,
+ SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
+ SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
+ FX + TILEX * (dx == 1) - softscroll_offset,
+ FY + TILEY * (dy == 1) - softscroll_offset);
+
+#else
+
+#if 1
+ /* !!! DOES NOT WORK FOR DIAGONAL PLAYER RELOCATION !!! */
+ int xsize = (BX2 - BX1 + 1);
+ int ysize = (BY2 - BY1 + 1);
+ int start = (dx != 0 ? (dx == -1 ? BX1 : BX2) : (dy == -1 ? BY1 : BY2));
+ int end = (dx != 0 ? (dx == -1 ? BX2 : BX1) : (dy == -1 ? BY2 : BY1));
+ int step = (start < end ? +1 : -1);
+
+ for (i = start; i != end; i += step)
+ {
+ BlitBitmap(drawto_field, drawto_field,
+ FX + TILEX * (dx != 0 ? i + step : 0),
+ FY + TILEY * (dy != 0 ? i + step : 0),
+ TILEX * (dx != 0 ? 1 : xsize),
+ TILEY * (dy != 0 ? 1 : ysize),
+ FX + TILEX * (dx != 0 ? i : 0),
+ FY + TILEY * (dy != 0 ? i : 0));
+ }
+
+#else
+
+ int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
BlitBitmap(drawto_field, drawto_field,
FX + TILEX * (dx == -1) - softscroll_offset,
FY + TILEY * (dy == -1) - softscroll_offset,
- SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
- SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
+ SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
+ SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
FX + TILEX * (dx == 1) - softscroll_offset,
FY + TILEY * (dy == 1) - softscroll_offset);
+#endif
+#endif
- if (dx)
+ if (dx != 0)
{
x = (dx == 1 ? BX1 : BX2);
for (y = BY1; y <= BY2; y++)
DrawScreenField(x, y);
}
- if (dy)
+ if (dy != 0)
{
y = (dy == 1 ? BY1 : BY2);
for (x = BX1; x <= BX2; x++)
player->last_jy = jy;
if (Feld[jx][jy] == EL_EXIT_OPEN ||
+ Feld[jx][jy] == EL_EM_EXIT_OPEN ||
+ Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
+ Feld[jx][jy] == EL_EM_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_EM_EXIT_OPEN ||
+ element == EL_STEEL_EXIT_OPEN ||
+ element == EL_EM_STEEL_EXIT_OPEN ||
element == EL_SP_EXIT_OPEN ||
element == EL_SP_EXIT_OPENING)
{
if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
return MP_NO_ACTION;
}
+ else if (element == EL_DC_GATE_WHITE ||
+ element == EL_DC_GATE_WHITE_GRAY ||
+ element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
+ {
+ if (player->num_white_keys == 0)
+ return MP_NO_ACTION;
+
+ player->num_white_keys--;
+ }
else if (IS_SP_PORT(element))
{
if (element == EL_SP_GRAVITY_PORT_LEFT ||
DrawGameDoorValues();
}
+ else if (element == EL_DC_KEY_WHITE)
+ {
+ player->num_white_keys++;
+
+ /* display white keys? */
+ /* DrawGameDoorValues(); */
+ }
else if (IS_ENVELOPE(element))
{
player->show_envelope = element;
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:
case EL_EMC_KEY_6:
case EL_EMC_KEY_7:
case EL_EMC_KEY_8:
+ case EL_DC_KEY_WHITE:
RaiseScore(level.score[SC_KEY]);
break;
default:
}
}
-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 */
engine_snapshot_rnd.belt_anim_mode[i * 4 + j] = anim_mode;
}
}
-
-#if 0
- printf("::: SAVE: %d RNDs\n", game.num_random_calls);
-#endif
}
static void LoadEngineSnapshotValues_RND()
}
}
-#if 0
- printf("::: LOADING ... : %d RNDs\n", num_random_calls);
-#endif
-
if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
{
InitRND(tape.random_seed);
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_INFO, "number of random calls out of sync");
+ Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
+ Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
Error(ERR_EXIT, "this should not happen -- please debug");
}
-
-#if 0
- printf("::: LOADED: %d RNDs\n", num_random_calls);
-#endif
}
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));
static struct
{
- int x, y;
+ int *x, *y;
+ int gd_x, gd_y;
int gadget_id;
char *infotext;
} gamebutton_info[NUM_GAME_BUTTONS] =
{
+#if 1
+ {
+ &game.button.stop.x, &game.button.stop.y,
+ GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
+ GAME_CTRL_ID_STOP,
+ "stop game"
+ },
+ {
+ &game.button.pause.x, &game.button.pause.y,
+ GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
+ GAME_CTRL_ID_PAUSE,
+ "pause game"
+ },
+ {
+ &game.button.play.x, &game.button.play.y,
+ GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
+ GAME_CTRL_ID_PLAY,
+ "play game"
+ },
+ {
+ &game.button.sound_music.x, &game.button.sound_music.y,
+ SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
+ SOUND_CTRL_ID_MUSIC,
+ "background music on/off"
+ },
+ {
+ &game.button.sound_loops.x, &game.button.sound_loops.y,
+ SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
+ SOUND_CTRL_ID_LOOPS,
+ "sound loops on/off"
+ },
+ {
+ &game.button.sound_simple.x,&game.button.sound_simple.y,
+ SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
+ SOUND_CTRL_ID_SIMPLE,
+ "normal sounds on/off"
+ }
+#else
{
GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
GAME_CTRL_ID_STOP,
SOUND_CTRL_ID_SIMPLE,
"normal sounds on/off"
}
+#endif
};
void CreateGameButtons()
int button_type;
boolean checked;
unsigned long event_mask;
+ int x, y;
int gd_xoffset, gd_yoffset;
int gd_x1, gd_x2, gd_y1, gd_y2;
int id = i;
- gd_xoffset = gamebutton_info[i].x;
- gd_yoffset = gamebutton_info[i].y;
+ x = DX + *gamebutton_info[i].x;
+ y = DY + *gamebutton_info[i].y;
+ gd_xoffset = gamebutton_info[i].gd_x;
+ gd_yoffset = gamebutton_info[i].gd_y;
gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
gi = CreateGadget(GDI_CUSTOM_ID, id,
GDI_INFO_TEXT, gamebutton_info[i].infotext,
+#if 1
+ GDI_X, x,
+ GDI_Y, y,
+#else
GDI_X, DX + gd_xoffset,
GDI_Y, DY + gd_yoffset,
+#endif
GDI_WIDTH, GAME_BUTTON_XSIZE,
GDI_HEIGHT, GAME_BUTTON_YSIZE,
GDI_TYPE, button_type,