/***********************************************************
* Rocks'n'Diamonds -- McDuffin Strikes Back! *
*----------------------------------------------------------*
-* (c) 1995-2002 Artsoft Entertainment *
+* (c) 1995-2006 Artsoft Entertainment *
* Holger Schemel *
* Detmolder Strasse 189 *
* 33604 Bielefeld *
#include "tape.h"
#include "network.h"
-/* this switch controls how rocks move horizontally */
-#define OLD_GAME_BEHAVIOUR FALSE
-
/* EXPERIMENTAL STUFF */
#define USE_NEW_AMOEBA_CODE FALSE
/* EXPERIMENTAL STUFF */
-#define USE_NEW_STUFF TRUE * 1
-
-#define USE_NEW_MOVE_STYLE TRUE * USE_NEW_STUFF * 1
-#define USE_NEW_MOVE_DELAY TRUE * USE_NEW_STUFF * 1
-#define USE_NEW_PUSH_DELAY TRUE * USE_NEW_STUFF * 1
-#define USE_NEW_BLOCK_STYLE TRUE * USE_NEW_STUFF * 1 * 1
-#define USE_NEW_SP_SLIPPERY TRUE * USE_NEW_STUFF * 1
-#define USE_NEW_RANDOMIZE TRUE * USE_NEW_STUFF * 1
+#define USE_NEW_STUFF ( 1)
+
+#define USE_NEW_SP_SLIPPERY (USE_NEW_STUFF * 1)
+#define USE_NEW_CUSTOM_VALUE (USE_NEW_STUFF * 1)
+#define USE_NEW_PLAYER_ANIM (USE_NEW_STUFF * 1)
+#define USE_NEW_ALL_SLIPPERY (USE_NEW_STUFF * 1)
+#define USE_NEW_PLAYER_SPEED (USE_NEW_STUFF * 1)
+#define USE_NEW_DELAYED_ACTION (USE_NEW_STUFF * 1)
+#define USE_NEW_SNAP_DELAY (USE_NEW_STUFF * 1)
+#define USE_ONLY_ONE_CHANGE_PER_FRAME (USE_NEW_STUFF * 1)
+#define USE_ONE_MORE_CHANGE_PER_FRAME (USE_NEW_STUFF * 1)
+#define USE_FIXED_DONT_RUN_INTO (USE_NEW_STUFF * 1)
+#define USE_NEW_SPRING_BUMPER (USE_NEW_STUFF * 1)
+#define USE_STOP_CHANGED_ELEMENTS (USE_NEW_STUFF * 1)
+#define USE_ELEMENT_TOUCHING_BUGFIX (USE_NEW_STUFF * 1)
+#define USE_NEW_CONTINUOUS_SNAPPING (USE_NEW_STUFF * 1)
+#define USE_GFX_RESET_GFX_ANIMATION (USE_NEW_STUFF * 1)
+#define USE_BOTH_SWITCHGATE_SWITCHES (USE_NEW_STUFF * 1)
+#define USE_PLAYER_GRAVITY (USE_NEW_STUFF * 1)
+#define USE_FIXED_BORDER_RUNNING_GFX (USE_NEW_STUFF * 1)
+#define USE_QUICKSAND_BD_ROCK_BUGFIX (USE_NEW_STUFF * 0)
+
+#define USE_QUICKSAND_IMPACT_BUGFIX (USE_NEW_STUFF * 0)
+
+#define USE_CODE_THAT_BREAKS_SNAKE_BITE (USE_NEW_STUFF * 1)
+
+#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_FIX_CE_ACTION_WITH_PLAYER (USE_NEW_STUFF * 1)
+#define USE_FIX_NO_ACTION_AFTER_CHANGE (USE_NEW_STUFF * 1)
+
+#define USE_PLAYER_REANIMATION (USE_NEW_STUFF * 1)
+
+#define USE_GFX_RESET_WHEN_NOT_MOVING (USE_NEW_STUFF * 1)
+
+#define USE_NEW_PLAYER_ASSIGNMENTS (USE_NEW_STUFF * 1)
+
+#define USE_DELAYED_GFX_REDRAW (USE_NEW_STUFF * 0)
+
+#if USE_DELAYED_GFX_REDRAW
+#define TEST_DrawLevelField(x, y) \
+ GfxRedraw[x][y] |= GFX_REDRAW_TILE
+#define TEST_DrawLevelFieldCrumbled(x, y) \
+ GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED
+#define TEST_DrawLevelFieldCrumbledNeighbours(x, y) \
+ GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS
+#define TEST_DrawTwinkleOnField(x, y) \
+ GfxRedraw[x][y] |= GFX_REDRAW_TILE_TWINKLED
+#else
+#define TEST_DrawLevelField(x, y) \
+ DrawLevelField(x, y)
+#define TEST_DrawLevelFieldCrumbled(x, y) \
+ DrawLevelFieldCrumbled(x, y)
+#define TEST_DrawLevelFieldCrumbledNeighbours(x, y) \
+ DrawLevelFieldCrumbledNeighbours(x, y)
+#define TEST_DrawTwinkleOnField(x, y) \
+ DrawTwinkleOnField(x, y)
+#endif
-#define USE_PUSH_BUGFIX TRUE * 1
/* for DigField() */
#define DF_NO_PUSH 0
#define DF_SNAP 2
/* for MovePlayer() */
-#define MF_NO_ACTION 0
-#define MF_MOVING 1
-#define MF_ACTION 2
+#define MP_NO_ACTION 0
+#define MP_MOVING 1
+#define MP_ACTION 2
+#define MP_DONT_RUN_INTO (MP_MOVING | MP_ACTION)
/* for ScrollPlayer() */
#define SCROLL_INIT 0
#define SCROLL_GO_ON 1
-/* for Explode() */
+/* for Bang()/Explode() */
#define EX_PHASE_START 0
#define EX_TYPE_NONE 0
#define EX_TYPE_NORMAL (1 << 0)
#define EX_TYPE_CENTER (1 << 1)
#define EX_TYPE_BORDER (1 << 2)
#define EX_TYPE_CROSS (1 << 3)
+#define EX_TYPE_DYNA (1 << 4)
#define EX_TYPE_SINGLE_TILE (EX_TYPE_CENTER | EX_TYPE_BORDER)
+#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_TEXT_XPOS(p))
+#define PANEL_YPOS(p) (DY + ALIGNED_TEXT_YPOS(p))
+
/* special positions in the game control window (relative to control window) */
-#define XX_LEVEL 37
-#define YY_LEVEL 20
-#define XX_EMERALDS 29
-#define YY_EMERALDS 54
-#define XX_DYNAMITE 29
-#define YY_DYNAMITE 89
-#define XX_KEYS 18
-#define YY_KEYS 123
-#define XX_SCORE 15
-#define YY_SCORE 159
-#define XX_TIME1 29
-#define XX_TIME2 30
-#define YY_TIME 194
+#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_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 1
+/* game panel display and control definitions */
+
+#define GAME_PANEL_LEVEL_NUMBER 0
+#define GAME_PANEL_GEMS 1
+#define GAME_PANEL_INVENTORY_COUNT 2
+#define GAME_PANEL_INVENTORY_FIRST_1 3
+#define GAME_PANEL_INVENTORY_FIRST_2 4
+#define GAME_PANEL_INVENTORY_FIRST_3 5
+#define GAME_PANEL_INVENTORY_FIRST_4 6
+#define GAME_PANEL_INVENTORY_FIRST_5 7
+#define GAME_PANEL_INVENTORY_FIRST_6 8
+#define GAME_PANEL_INVENTORY_FIRST_7 9
+#define GAME_PANEL_INVENTORY_FIRST_8 10
+#define GAME_PANEL_INVENTORY_LAST_1 11
+#define GAME_PANEL_INVENTORY_LAST_2 12
+#define GAME_PANEL_INVENTORY_LAST_3 13
+#define GAME_PANEL_INVENTORY_LAST_4 14
+#define GAME_PANEL_INVENTORY_LAST_5 15
+#define GAME_PANEL_INVENTORY_LAST_6 16
+#define GAME_PANEL_INVENTORY_LAST_7 17
+#define GAME_PANEL_INVENTORY_LAST_8 18
+#define GAME_PANEL_KEY_1 19
+#define GAME_PANEL_KEY_2 20
+#define GAME_PANEL_KEY_3 21
+#define GAME_PANEL_KEY_4 22
+#define GAME_PANEL_KEY_5 23
+#define GAME_PANEL_KEY_6 24
+#define GAME_PANEL_KEY_7 25
+#define GAME_PANEL_KEY_8 26
+#define GAME_PANEL_KEY_WHITE 27
+#define GAME_PANEL_KEY_WHITE_COUNT 28
+#define GAME_PANEL_SCORE 29
+#define GAME_PANEL_HIGHSCORE 30
+#define GAME_PANEL_TIME 31
+#define GAME_PANEL_TIME_HH 32
+#define GAME_PANEL_TIME_MM 33
+#define GAME_PANEL_TIME_SS 34
+#define GAME_PANEL_FRAME 35
+#define GAME_PANEL_SHIELD_NORMAL 36
+#define GAME_PANEL_SHIELD_NORMAL_TIME 37
+#define GAME_PANEL_SHIELD_DEADLY 38
+#define GAME_PANEL_SHIELD_DEADLY_TIME 39
+#define GAME_PANEL_EXIT 40
+#define GAME_PANEL_EMC_MAGIC_BALL 41
+#define GAME_PANEL_EMC_MAGIC_BALL_SWITCH 42
+#define GAME_PANEL_LIGHT_SWITCH 43
+#define GAME_PANEL_LIGHT_SWITCH_TIME 44
+#define GAME_PANEL_TIMEGATE_SWITCH 45
+#define GAME_PANEL_TIMEGATE_SWITCH_TIME 46
+#define GAME_PANEL_SWITCHGATE_SWITCH 47
+#define GAME_PANEL_EMC_LENSES 48
+#define GAME_PANEL_EMC_LENSES_TIME 49
+#define GAME_PANEL_EMC_MAGNIFIER 50
+#define GAME_PANEL_EMC_MAGNIFIER_TIME 51
+#define GAME_PANEL_BALLOON_SWITCH 52
+#define GAME_PANEL_DYNABOMB_NUMBER 53
+#define GAME_PANEL_DYNABOMB_SIZE 54
+#define GAME_PANEL_DYNABOMB_POWER 55
+#define GAME_PANEL_PENGUINS 56
+#define GAME_PANEL_SOKOBAN_OBJECTS 57
+#define GAME_PANEL_SOKOBAN_FIELDS 58
+#define GAME_PANEL_ROBOT_WHEEL 59
+#define GAME_PANEL_CONVEYOR_BELT_1 60
+#define GAME_PANEL_CONVEYOR_BELT_2 61
+#define GAME_PANEL_CONVEYOR_BELT_3 62
+#define GAME_PANEL_CONVEYOR_BELT_4 63
+#define GAME_PANEL_CONVEYOR_BELT_1_SWITCH 64
+#define GAME_PANEL_CONVEYOR_BELT_2_SWITCH 65
+#define GAME_PANEL_CONVEYOR_BELT_3_SWITCH 66
+#define GAME_PANEL_CONVEYOR_BELT_4_SWITCH 67
+#define GAME_PANEL_MAGIC_WALL 68
+#define GAME_PANEL_MAGIC_WALL_TIME 69
+#define GAME_PANEL_GRAVITY_STATE 70
+#define GAME_PANEL_GRAPHIC_1 71
+#define GAME_PANEL_GRAPHIC_2 72
+#define GAME_PANEL_GRAPHIC_3 73
+#define GAME_PANEL_GRAPHIC_4 74
+#define GAME_PANEL_GRAPHIC_5 75
+#define GAME_PANEL_GRAPHIC_6 76
+#define GAME_PANEL_GRAPHIC_7 77
+#define GAME_PANEL_GRAPHIC_8 78
+#define GAME_PANEL_ELEMENT_1 79
+#define GAME_PANEL_ELEMENT_2 80
+#define GAME_PANEL_ELEMENT_3 81
+#define GAME_PANEL_ELEMENT_4 82
+#define GAME_PANEL_ELEMENT_5 83
+#define GAME_PANEL_ELEMENT_6 84
+#define GAME_PANEL_ELEMENT_7 85
+#define GAME_PANEL_ELEMENT_8 86
+#define GAME_PANEL_ELEMENT_COUNT_1 87
+#define GAME_PANEL_ELEMENT_COUNT_2 88
+#define GAME_PANEL_ELEMENT_COUNT_3 89
+#define GAME_PANEL_ELEMENT_COUNT_4 90
+#define GAME_PANEL_ELEMENT_COUNT_5 91
+#define GAME_PANEL_ELEMENT_COUNT_6 92
+#define GAME_PANEL_ELEMENT_COUNT_7 93
+#define GAME_PANEL_ELEMENT_COUNT_8 94
+#define GAME_PANEL_CE_SCORE_1 95
+#define GAME_PANEL_CE_SCORE_2 96
+#define GAME_PANEL_CE_SCORE_3 97
+#define GAME_PANEL_CE_SCORE_4 98
+#define GAME_PANEL_CE_SCORE_5 99
+#define GAME_PANEL_CE_SCORE_6 100
+#define GAME_PANEL_CE_SCORE_7 101
+#define GAME_PANEL_CE_SCORE_8 102
+#define GAME_PANEL_CE_SCORE_1_ELEMENT 103
+#define GAME_PANEL_CE_SCORE_2_ELEMENT 104
+#define GAME_PANEL_CE_SCORE_3_ELEMENT 105
+#define GAME_PANEL_CE_SCORE_4_ELEMENT 106
+#define GAME_PANEL_CE_SCORE_5_ELEMENT 107
+#define GAME_PANEL_CE_SCORE_6_ELEMENT 108
+#define GAME_PANEL_CE_SCORE_7_ELEMENT 109
+#define GAME_PANEL_CE_SCORE_8_ELEMENT 110
+#define GAME_PANEL_PLAYER_NAME 111
+#define GAME_PANEL_LEVEL_NAME 112
+#define GAME_PANEL_LEVEL_AUTHOR 113
+
+#define NUM_GAME_PANEL_CONTROLS 114
+
+struct GamePanelOrderInfo
+{
+ int nr;
+ int sort_priority;
+};
+
+static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
+
+struct GamePanelControlInfo
+{
+ int nr;
+
+ struct TextPosInfo *pos;
+ int type;
+
+ int value, last_value;
+ int frame, last_frame;
+ int gfx_frame;
+ int gfx_random;
+};
+
+static struct GamePanelControlInfo game_panel_controls[] =
+{
+ {
+ GAME_PANEL_LEVEL_NUMBER,
+ &game.panel.level_number,
+ TYPE_INTEGER,
+ },
+ {
+ GAME_PANEL_GEMS,
+ &game.panel.gems,
+ TYPE_INTEGER,
+ },
+ {
+ GAME_PANEL_INVENTORY_COUNT,
+ &game.panel.inventory_count,
+ TYPE_INTEGER,
+ },
+ {
+ GAME_PANEL_INVENTORY_FIRST_1,
+ &game.panel.inventory_first[0],
+ TYPE_ELEMENT,
+ },
+ {
+ GAME_PANEL_INVENTORY_FIRST_2,
+ &game.panel.inventory_first[1],
+ TYPE_ELEMENT,
+ },
+ {
+ GAME_PANEL_INVENTORY_FIRST_3,
+ &game.panel.inventory_first[2],
+ TYPE_ELEMENT,
+ },
+ {
+ GAME_PANEL_INVENTORY_FIRST_4,
+ &game.panel.inventory_first[3],
+ TYPE_ELEMENT,
+ },
+ {
+ GAME_PANEL_INVENTORY_FIRST_5,
+ &game.panel.inventory_first[4],
+ TYPE_ELEMENT,
+ },
+ {
+ GAME_PANEL_INVENTORY_FIRST_6,
+ &game.panel.inventory_first[5],
+ TYPE_ELEMENT,
+ },
+ {
+ GAME_PANEL_INVENTORY_FIRST_7,
+ &game.panel.inventory_first[6],
+ TYPE_ELEMENT,
+ },
+ {
+ GAME_PANEL_INVENTORY_FIRST_8,
+ &game.panel.inventory_first[7],
+ TYPE_ELEMENT,
+ },
+ {
+ GAME_PANEL_INVENTORY_LAST_1,
+ &game.panel.inventory_last[0],
+ TYPE_ELEMENT,
+ },
+ {
+ GAME_PANEL_INVENTORY_LAST_2,
+ &game.panel.inventory_last[1],
+ TYPE_ELEMENT,
+ },
+ {
+ GAME_PANEL_INVENTORY_LAST_3,
+ &game.panel.inventory_last[2],
+ TYPE_ELEMENT,
+ },
+ {
+ GAME_PANEL_INVENTORY_LAST_4,
+ &game.panel.inventory_last[3],
+ TYPE_ELEMENT,
+ },
+ {
+ GAME_PANEL_INVENTORY_LAST_5,
+ &game.panel.inventory_last[4],
+ TYPE_ELEMENT,
+ },
+ {
+ GAME_PANEL_INVENTORY_LAST_6,
+ &game.panel.inventory_last[5],
+ TYPE_ELEMENT,
+ },
+ {
+ GAME_PANEL_INVENTORY_LAST_7,
+ &game.panel.inventory_last[6],
+ TYPE_ELEMENT,
+ },
+ {
+ GAME_PANEL_INVENTORY_LAST_8,
+ &game.panel.inventory_last[7],
+ TYPE_ELEMENT,
+ },
+ {
+ GAME_PANEL_KEY_1,
+ &game.panel.key[0],
+ TYPE_ELEMENT,
+ },
+ {
+ GAME_PANEL_KEY_2,
+ &game.panel.key[1],
+ TYPE_ELEMENT,
+ },
+ {
+ GAME_PANEL_KEY_3,
+ &game.panel.key[2],
+ TYPE_ELEMENT,
+ },
+ {
+ GAME_PANEL_KEY_4,
+ &game.panel.key[3],
+ TYPE_ELEMENT,
+ },
+ {
+ GAME_PANEL_KEY_5,
+ &game.panel.key[4],
+ TYPE_ELEMENT,
+ },
+ {
+ GAME_PANEL_KEY_6,
+ &game.panel.key[5],
+ TYPE_ELEMENT,
+ },
+ {
+ GAME_PANEL_KEY_7,
+ &game.panel.key[6],
+ TYPE_ELEMENT,
+ },
+ {
+ GAME_PANEL_KEY_8,
+ &game.panel.key[7],
+ TYPE_ELEMENT,
+ },
+ {
+ GAME_PANEL_KEY_WHITE,
+ &game.panel.key_white,
+ TYPE_ELEMENT,
+ },
+ {
+ GAME_PANEL_KEY_WHITE_COUNT,
+ &game.panel.key_white_count,
+ TYPE_INTEGER,
+ },
+ {
+ GAME_PANEL_SCORE,
+ &game.panel.score,
+ TYPE_INTEGER,
+ },
+ {
+ GAME_PANEL_HIGHSCORE,
+ &game.panel.highscore,
+ TYPE_INTEGER,
+ },
+ {
+ GAME_PANEL_TIME,
+ &game.panel.time,
+ TYPE_INTEGER,
+ },
+ {
+ GAME_PANEL_TIME_HH,
+ &game.panel.time_hh,
+ TYPE_INTEGER,
+ },
+ {
+ GAME_PANEL_TIME_MM,
+ &game.panel.time_mm,
+ TYPE_INTEGER,
+ },
+ {
+ GAME_PANEL_TIME_SS,
+ &game.panel.time_ss,
+ TYPE_INTEGER,
+ },
+ {
+ GAME_PANEL_FRAME,
+ &game.panel.frame,
+ TYPE_INTEGER,
+ },
+ {
+ GAME_PANEL_SHIELD_NORMAL,
+ &game.panel.shield_normal,
+ TYPE_ELEMENT,
+ },
+ {
+ GAME_PANEL_SHIELD_NORMAL_TIME,
+ &game.panel.shield_normal_time,
+ TYPE_INTEGER,
+ },
+ {
+ GAME_PANEL_SHIELD_DEADLY,
+ &game.panel.shield_deadly,
+ TYPE_ELEMENT,
+ },
+ {
+ GAME_PANEL_SHIELD_DEADLY_TIME,
+ &game.panel.shield_deadly_time,
+ TYPE_INTEGER,
+ },
+ {
+ GAME_PANEL_EXIT,
+ &game.panel.exit,
+ TYPE_ELEMENT,
+ },
+ {
+ GAME_PANEL_EMC_MAGIC_BALL,
+ &game.panel.emc_magic_ball,
+ TYPE_ELEMENT,
+ },
+ {
+ GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
+ &game.panel.emc_magic_ball_switch,
+ TYPE_ELEMENT,
+ },
+ {
+ GAME_PANEL_LIGHT_SWITCH,
+ &game.panel.light_switch,
+ TYPE_ELEMENT,
+ },
+ {
+ GAME_PANEL_LIGHT_SWITCH_TIME,
+ &game.panel.light_switch_time,
+ TYPE_INTEGER,
+ },
+ {
+ GAME_PANEL_TIMEGATE_SWITCH,
+ &game.panel.timegate_switch,
+ TYPE_ELEMENT,
+ },
+ {
+ GAME_PANEL_TIMEGATE_SWITCH_TIME,
+ &game.panel.timegate_switch_time,
+ TYPE_INTEGER,
+ },
+ {
+ GAME_PANEL_SWITCHGATE_SWITCH,
+ &game.panel.switchgate_switch,
+ TYPE_ELEMENT,
+ },
+ {
+ GAME_PANEL_EMC_LENSES,
+ &game.panel.emc_lenses,
+ TYPE_ELEMENT,
+ },
+ {
+ GAME_PANEL_EMC_LENSES_TIME,
+ &game.panel.emc_lenses_time,
+ TYPE_INTEGER,
+ },
+ {
+ GAME_PANEL_EMC_MAGNIFIER,
+ &game.panel.emc_magnifier,
+ TYPE_ELEMENT,
+ },
+ {
+ GAME_PANEL_EMC_MAGNIFIER_TIME,
+ &game.panel.emc_magnifier_time,
+ TYPE_INTEGER,
+ },
+ {
+ GAME_PANEL_BALLOON_SWITCH,
+ &game.panel.balloon_switch,
+ TYPE_ELEMENT,
+ },
+ {
+ GAME_PANEL_DYNABOMB_NUMBER,
+ &game.panel.dynabomb_number,
+ TYPE_INTEGER,
+ },
+ {
+ GAME_PANEL_DYNABOMB_SIZE,
+ &game.panel.dynabomb_size,
+ TYPE_INTEGER,
+ },
+ {
+ GAME_PANEL_DYNABOMB_POWER,
+ &game.panel.dynabomb_power,
+ TYPE_ELEMENT,
+ },
+ {
+ GAME_PANEL_PENGUINS,
+ &game.panel.penguins,
+ TYPE_INTEGER,
+ },
+ {
+ GAME_PANEL_SOKOBAN_OBJECTS,
+ &game.panel.sokoban_objects,
+ TYPE_INTEGER,
+ },
+ {
+ GAME_PANEL_SOKOBAN_FIELDS,
+ &game.panel.sokoban_fields,
+ TYPE_INTEGER,
+ },
+ {
+ GAME_PANEL_ROBOT_WHEEL,
+ &game.panel.robot_wheel,
+ TYPE_ELEMENT,
+ },
+ {
+ GAME_PANEL_CONVEYOR_BELT_1,
+ &game.panel.conveyor_belt[0],
+ TYPE_ELEMENT,
+ },
+ {
+ GAME_PANEL_CONVEYOR_BELT_2,
+ &game.panel.conveyor_belt[1],
+ TYPE_ELEMENT,
+ },
+ {
+ GAME_PANEL_CONVEYOR_BELT_3,
+ &game.panel.conveyor_belt[2],
+ TYPE_ELEMENT,
+ },
+ {
+ GAME_PANEL_CONVEYOR_BELT_4,
+ &game.panel.conveyor_belt[3],
+ TYPE_ELEMENT,
+ },
+ {
+ GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
+ &game.panel.conveyor_belt_switch[0],
+ TYPE_ELEMENT,
+ },
+ {
+ GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
+ &game.panel.conveyor_belt_switch[1],
+ TYPE_ELEMENT,
+ },
+ {
+ GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
+ &game.panel.conveyor_belt_switch[2],
+ TYPE_ELEMENT,
+ },
+ {
+ GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
+ &game.panel.conveyor_belt_switch[3],
+ TYPE_ELEMENT,
+ },
+ {
+ GAME_PANEL_MAGIC_WALL,
+ &game.panel.magic_wall,
+ TYPE_ELEMENT,
+ },
+ {
+ GAME_PANEL_MAGIC_WALL_TIME,
+ &game.panel.magic_wall_time,
+ TYPE_INTEGER,
+ },
+ {
+ GAME_PANEL_GRAVITY_STATE,
+ &game.panel.gravity_state,
+ TYPE_STRING,
+ },
+ {
+ GAME_PANEL_GRAPHIC_1,
+ &game.panel.graphic[0],
+ TYPE_ELEMENT,
+ },
+ {
+ GAME_PANEL_GRAPHIC_2,
+ &game.panel.graphic[1],
+ TYPE_ELEMENT,
+ },
+ {
+ GAME_PANEL_GRAPHIC_3,
+ &game.panel.graphic[2],
+ TYPE_ELEMENT,
+ },
+ {
+ GAME_PANEL_GRAPHIC_4,
+ &game.panel.graphic[3],
+ TYPE_ELEMENT,
+ },
+ {
+ GAME_PANEL_GRAPHIC_5,
+ &game.panel.graphic[4],
+ TYPE_ELEMENT,
+ },
+ {
+ GAME_PANEL_GRAPHIC_6,
+ &game.panel.graphic[5],
+ TYPE_ELEMENT,
+ },
+ {
+ GAME_PANEL_GRAPHIC_7,
+ &game.panel.graphic[6],
+ TYPE_ELEMENT,
+ },
+ {
+ GAME_PANEL_GRAPHIC_8,
+ &game.panel.graphic[7],
+ TYPE_ELEMENT,
+ },
+ {
+ GAME_PANEL_ELEMENT_1,
+ &game.panel.element[0],
+ TYPE_ELEMENT,
+ },
+ {
+ GAME_PANEL_ELEMENT_2,
+ &game.panel.element[1],
+ TYPE_ELEMENT,
+ },
+ {
+ GAME_PANEL_ELEMENT_3,
+ &game.panel.element[2],
+ TYPE_ELEMENT,
+ },
+ {
+ GAME_PANEL_ELEMENT_4,
+ &game.panel.element[3],
+ TYPE_ELEMENT,
+ },
+ {
+ GAME_PANEL_ELEMENT_5,
+ &game.panel.element[4],
+ TYPE_ELEMENT,
+ },
+ {
+ GAME_PANEL_ELEMENT_6,
+ &game.panel.element[5],
+ TYPE_ELEMENT,
+ },
+ {
+ GAME_PANEL_ELEMENT_7,
+ &game.panel.element[6],
+ TYPE_ELEMENT,
+ },
+ {
+ GAME_PANEL_ELEMENT_8,
+ &game.panel.element[7],
+ TYPE_ELEMENT,
+ },
+ {
+ GAME_PANEL_ELEMENT_COUNT_1,
+ &game.panel.element_count[0],
+ TYPE_INTEGER,
+ },
+ {
+ GAME_PANEL_ELEMENT_COUNT_2,
+ &game.panel.element_count[1],
+ TYPE_INTEGER,
+ },
+ {
+ GAME_PANEL_ELEMENT_COUNT_3,
+ &game.panel.element_count[2],
+ TYPE_INTEGER,
+ },
+ {
+ GAME_PANEL_ELEMENT_COUNT_4,
+ &game.panel.element_count[3],
+ TYPE_INTEGER,
+ },
+ {
+ GAME_PANEL_ELEMENT_COUNT_5,
+ &game.panel.element_count[4],
+ TYPE_INTEGER,
+ },
+ {
+ GAME_PANEL_ELEMENT_COUNT_6,
+ &game.panel.element_count[5],
+ TYPE_INTEGER,
+ },
+ {
+ GAME_PANEL_ELEMENT_COUNT_7,
+ &game.panel.element_count[6],
+ TYPE_INTEGER,
+ },
+ {
+ GAME_PANEL_ELEMENT_COUNT_8,
+ &game.panel.element_count[7],
+ TYPE_INTEGER,
+ },
+ {
+ GAME_PANEL_CE_SCORE_1,
+ &game.panel.ce_score[0],
+ TYPE_INTEGER,
+ },
+ {
+ GAME_PANEL_CE_SCORE_2,
+ &game.panel.ce_score[1],
+ TYPE_INTEGER,
+ },
+ {
+ GAME_PANEL_CE_SCORE_3,
+ &game.panel.ce_score[2],
+ TYPE_INTEGER,
+ },
+ {
+ GAME_PANEL_CE_SCORE_4,
+ &game.panel.ce_score[3],
+ TYPE_INTEGER,
+ },
+ {
+ GAME_PANEL_CE_SCORE_5,
+ &game.panel.ce_score[4],
+ TYPE_INTEGER,
+ },
+ {
+ GAME_PANEL_CE_SCORE_6,
+ &game.panel.ce_score[5],
+ TYPE_INTEGER,
+ },
+ {
+ GAME_PANEL_CE_SCORE_7,
+ &game.panel.ce_score[6],
+ TYPE_INTEGER,
+ },
+ {
+ GAME_PANEL_CE_SCORE_8,
+ &game.panel.ce_score[7],
+ TYPE_INTEGER,
+ },
+ {
+ GAME_PANEL_CE_SCORE_1_ELEMENT,
+ &game.panel.ce_score_element[0],
+ TYPE_ELEMENT,
+ },
+ {
+ GAME_PANEL_CE_SCORE_2_ELEMENT,
+ &game.panel.ce_score_element[1],
+ TYPE_ELEMENT,
+ },
+ {
+ GAME_PANEL_CE_SCORE_3_ELEMENT,
+ &game.panel.ce_score_element[2],
+ TYPE_ELEMENT,
+ },
+ {
+ GAME_PANEL_CE_SCORE_4_ELEMENT,
+ &game.panel.ce_score_element[3],
+ TYPE_ELEMENT,
+ },
+ {
+ GAME_PANEL_CE_SCORE_5_ELEMENT,
+ &game.panel.ce_score_element[4],
+ TYPE_ELEMENT,
+ },
+ {
+ GAME_PANEL_CE_SCORE_6_ELEMENT,
+ &game.panel.ce_score_element[5],
+ TYPE_ELEMENT,
+ },
+ {
+ GAME_PANEL_CE_SCORE_7_ELEMENT,
+ &game.panel.ce_score_element[6],
+ TYPE_ELEMENT,
+ },
+ {
+ GAME_PANEL_CE_SCORE_8_ELEMENT,
+ &game.panel.ce_score_element[7],
+ TYPE_ELEMENT,
+ },
+ {
+ GAME_PANEL_PLAYER_NAME,
+ &game.panel.player_name,
+ TYPE_STRING,
+ },
+ {
+ GAME_PANEL_LEVEL_NAME,
+ &game.panel.level_name,
+ TYPE_STRING,
+ },
+ {
+ GAME_PANEL_LEVEL_AUTHOR,
+ &game.panel.level_author,
+ TYPE_STRING,
+ },
+
+ {
+ -1,
+ NULL,
+ -1,
+ }
+};
+#endif
+
+
+/* values for delayed check of falling and moving elements and for collision */
+#define CHECK_DELAY_MOVING 3
+#define CHECK_DELAY_FALLING CHECK_DELAY_MOVING
+#define CHECK_DELAY_COLLISION 2
+#define CHECK_DELAY_IMPACT CHECK_DELAY_COLLISION
+
/* values for initial player move delay (initial delay counter value) */
#define INITIAL_MOVE_DELAY_OFF -1
#define INITIAL_MOVE_DELAY_ON 0
/* values for player movement speed (which is in fact a delay value) */
+#define MOVE_DELAY_MIN_SPEED 32
#define MOVE_DELAY_NORMAL_SPEED 8
#define MOVE_DELAY_HIGH_SPEED 4
+#define MOVE_DELAY_MAX_SPEED 1
+
+#define DOUBLE_MOVE_DELAY(x) (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
+#define HALVE_MOVE_DELAY(x) (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
-#define DOUBLE_MOVE_DELAY(x) (x = (x <= MOVE_DELAY_HIGH_SPEED ? x * 2 : x))
-#define HALVE_MOVE_DELAY(x) (x = (x >= MOVE_DELAY_HIGH_SPEED ? x / 2 : x))
-#define DOUBLE_PLAYER_SPEED(p) (HALVE_MOVE_DELAY((p)->move_delay_value))
+#define DOUBLE_PLAYER_SPEED(p) (HALVE_MOVE_DELAY( (p)->move_delay_value))
#define HALVE_PLAYER_SPEED(p) (DOUBLE_MOVE_DELAY((p)->move_delay_value))
/* values for other actions */
#define MOVE_STEPSIZE_NORMAL (TILEX / MOVE_DELAY_NORMAL_SPEED)
+#define MOVE_STEPSIZE_MIN (1)
+#define MOVE_STEPSIZE_MAX (TILEX)
#define GET_DX_FROM_DIR(d) ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
#define GET_DY_FROM_DIR(d) ((d) == MV_UP ? -1 : (d) == MV_DOWN ? 1 : 0)
-#define INIT_GFX_RANDOM() (SimpleRND(1000000))
+#define INIT_GFX_RANDOM() (GetSimpleRandom(1000000))
#define GET_NEW_PUSH_DELAY(e) ( (element_info[e].push_delay_fixed) + \
RND(element_info[e].push_delay_random))
RND(element_info[e].move_delay_random))
#define GET_MAX_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
(element_info[e].move_delay_random))
-
-#define GET_TARGET_ELEMENT(e, ch) \
- ((e) == EL_TRIGGER_ELEMENT ? (ch)->actual_trigger_element : \
- (e) == EL_TRIGGER_PLAYER ? (ch)->actual_trigger_player : (e))
-
-#define GET_VALID_PLAYER_ELEMENT(e) \
- ((e) >= EL_PLAYER_1 && (e) <= EL_PLAYER_4 ? (e) : EL_PLAYER_1)
+#define GET_NEW_CE_VALUE(e) ( (element_info[e].ce_value_fixed_initial) +\
+ RND(element_info[e].ce_value_random_initial))
+#define GET_CE_SCORE(e) ( (element_info[e].collect_score))
+#define GET_CHANGE_DELAY(c) ( ((c)->delay_fixed * (c)->delay_frames) + \
+ RND((c)->delay_random * (c)->delay_frames))
+#define GET_CE_DELAY_VALUE(c) ( ((c)->delay_fixed) + \
+ RND((c)->delay_random))
+
+
+#define GET_VALID_RUNTIME_ELEMENT(e) \
+ ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
+
+#define RESOLVED_REFERENCE_ELEMENT(be, e) \
+ ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START : \
+ (be) + (e) - EL_SELF > EL_CUSTOM_END ? EL_CUSTOM_END : \
+ (be) + (e) - EL_SELF)
+
+#define GET_PLAYER_FROM_BITS(p) \
+ (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
+
+#define GET_TARGET_ELEMENT(be, e, ch, cv, cs) \
+ ((e) == EL_TRIGGER_PLAYER ? (ch)->actual_trigger_player : \
+ (e) == EL_TRIGGER_ELEMENT ? (ch)->actual_trigger_element : \
+ (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value : \
+ (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score : \
+ (e) == EL_CURRENT_CE_VALUE ? (cv) : \
+ (e) == EL_CURRENT_CE_SCORE ? (cs) : \
+ (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ? \
+ RESOLVED_REFERENCE_ELEMENT(be, e) : \
+ (e))
#define CAN_GROW_INTO(e) \
((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
IS_PLAYER(x, y) && \
!PLAYER_ENEMY_PROTECTED(x, y))))
-#if 0
-#define ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, condition) \
- (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
- (condition) || \
- (DONT_COLLIDE_WITH(e) && \
- IS_PLAYER(x, y) && \
- !PLAYER_ENEMY_PROTECTED(x, y))))
-#endif
-
#define ELEMENT_CAN_ENTER_FIELD(e, x, y) \
ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
-#if 1
#define SATELLITE_CAN_ENTER_FIELD(x, y) \
ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
-#else
-#define SATELLITE_CAN_ENTER_FIELD(x, y) \
- ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, Feld[x][y] == EL_ACID)
-#endif
-#if 0
-#define ENEMY_CAN_ENTER_FIELD(e, x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
-#endif
+#define ANDROID_CAN_ENTER_FIELD(e, x, y) \
+ ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
+
+#define ANDROID_CAN_CLONE_FIELD(x, y) \
+ (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
+ CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
#define ENEMY_CAN_ENTER_FIELD(e, x, y) \
ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
-#if 1
-
#define YAMYAM_CAN_ENTER_FIELD(e, x, y) \
ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
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)
#define SPRING_CAN_ENTER_FIELD(e, x, y) \
ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
-#else
-
-#define YAMYAM_CAN_ENTER_FIELD(e, x, y) \
- (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
- (CAN_MOVE_INTO_ACID(e) && \
- Feld[x][y] == EL_ACID) || \
- Feld[x][y] == EL_DIAMOND))
-
-#define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y) \
- (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
- (CAN_MOVE_INTO_ACID(e) && \
- Feld[x][y] == EL_ACID) || \
- IS_FOOD_DARK_YAMYAM(Feld[x][y])))
-
-#define PACMAN_CAN_ENTER_FIELD(e, x, y) \
- (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
- (CAN_MOVE_INTO_ACID(e) && \
- Feld[x][y] == EL_ACID) || \
- IS_AMOEBOID(Feld[x][y])))
-
-#define PIG_CAN_ENTER_FIELD(e, x, y) \
- (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
- (CAN_MOVE_INTO_ACID(e) && \
- Feld[x][y] == EL_ACID) || \
- IS_FOOD_PIG(Feld[x][y])))
-
-#define PENGUIN_CAN_ENTER_FIELD(e, x, y) \
- (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
- (CAN_MOVE_INTO_ACID(e) && \
- Feld[x][y] == EL_ACID) || \
- IS_FOOD_PENGUIN(Feld[x][y]) || \
- Feld[x][y] == EL_EXIT_OPEN))
-
-#define DRAGON_CAN_ENTER_FIELD(e, x, y) \
- (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
- (CAN_MOVE_INTO_ACID(e) && \
- Feld[x][y] == EL_ACID)))
-
-#define MOLE_CAN_ENTER_FIELD(e, x, y, condition) \
- (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
- (CAN_MOVE_INTO_ACID(e) && \
- Feld[x][y] == EL_ACID) || \
- (condition)))
-
-#define SPRING_CAN_ENTER_FIELD(e, x, y) \
- (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
- (CAN_MOVE_INTO_ACID(e) && \
- Feld[x][y] == EL_ACID)))
-
-#endif
+#define SPRING_CAN_BUMP_FROM_FIELD(x, y) \
+ (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER || \
+ Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
-#define GROUP_NR(e) ((e) - EL_GROUP_START)
#define MOVE_ENTER_EL(e) (element_info[e].move_enter_element)
-#define IS_IN_GROUP(e, nr) (element_info[e].in_group[nr] == TRUE)
-#define IS_IN_GROUP_EL(e, ge) (IS_IN_GROUP(e, (ge) - EL_GROUP_START))
-
-#define IS_EQUAL_OR_IN_GROUP(e, ge) \
- (IS_GROUP_ELEMENT(ge) ? IS_IN_GROUP(e, GROUP_NR(ge)) : (e) == (ge))
-#if 0
-#define CE_ENTER_FIELD_COND(e, x, y) \
- (!IS_PLAYER(x, y) && \
- (Feld[x][y] == EL_ACID || \
- IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e))))
-#else
#define CE_ENTER_FIELD_COND(e, x, y) \
(!IS_PLAYER(x, y) && \
IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
-#endif
#define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y) \
ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
/* forward declaration for internal use */
+static void CreateField(int, int, int);
+
+static void ResetGfxAnimation(int, int);
+
+static void SetPlayerWaiting(struct PlayerInfo *, boolean);
static void AdvanceFrameAndPlayerCounters(int);
static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
static void ScrollPlayer(struct PlayerInfo *, int);
static void ScrollScreen(struct PlayerInfo *, int);
-int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
+static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
+static boolean DigFieldByCE(int, int, int);
+static boolean SnapField(struct PlayerInfo *, int, int);
+static boolean DropElement(struct PlayerInfo *);
static void InitBeltMovement(void);
static void CloseAllOpenTimegates(void);
static void CheckGravityMovement(struct PlayerInfo *);
static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
-static void KillHeroUnlessEnemyProtected(int, int);
-static void KillHeroUnlessExplosionProtected(int, int);
+static void KillPlayerUnlessEnemyProtected(int, int);
+static void KillPlayerUnlessExplosionProtected(int, int);
static void TestIfPlayerTouchesCustomElement(int, int);
static void TestIfElementTouchesCustomElement(int, int);
static void TestIfElementSmashesCustomElement(int, int, int);
#endif
-static void ChangeElement(int, int, int);
+static void HandleElementChange(int, int, int);
+static void ExecuteCustomElementAction(int, int, int, int);
+static boolean ChangeElement(int, int, int, int);
static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
#define CheckTriggeredElementChange(x, y, e, ev) \
- CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, \
- CH_SIDE_ANY, -1)
+ CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
#define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s) \
CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
#define CheckTriggeredElementChangeBySide(x, y, e, ev, s) \
CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
#define CheckTriggeredElementChangeByPage(x, y, e, ev, p) \
- CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, \
- CH_SIDE_ANY, p)
+ CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
-static boolean CheckElementChangeExt(int, int, int, int, int, int, int, int);
+static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
#define CheckElementChange(x, y, e, te, ev) \
- CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY, -1)
+ CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
#define CheckElementChangeByPlayer(x, y, e, ev, p, s) \
- CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s, CH_PAGE_ANY)
+ CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
#define CheckElementChangeBySide(x, y, e, te, ev, s) \
- CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s, CH_PAGE_ANY)
-#define CheckElementChangeByPage(x, y, e, te, ev, p) \
- CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
+ CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
static void PlayLevelSound(int, int, int);
static void PlayLevelSoundNearest(int, int, int);
static void MapGameButtons();
static void HandleGameButtons(struct GadgetInfo *);
+int AmoebeNachbarNr(int, int);
+void AmoebeUmwandeln(int, int);
+void ContinueMoving(int, int);
+void Bang(int, int);
+void InitMovDir(int, int);
+void InitAmoebaNr(int, int);
+int NewHiScore(void);
+
+void TestIfGoodThingHitsBadThing(int, int, int);
+void TestIfBadThingHitsGoodThing(int, int, int);
+void TestIfPlayerTouchesBadThing(int, int);
+void TestIfPlayerRunsIntoBadThing(int, int, int);
+void TestIfBadThingTouchesPlayer(int, int);
+void TestIfBadThingRunsIntoPlayer(int, int, int);
+void TestIfFriendTouchesBadThing(int, int);
+void TestIfBadThingTouchesFriend(int, int);
+void TestIfBadThingTouchesOtherBadThing(int, int);
+void TestIfGoodThingGetsHitByBadThing(int, int, int);
+
+void KillPlayer(struct PlayerInfo *);
+void BuryPlayer(struct PlayerInfo *);
+void RemovePlayer(struct PlayerInfo *);
+
+static int getInvisibleActiveFromInvisibleElement(int);
+static int getInvisibleFromInvisibleActiveElement(int);
+
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;
+
+static int map_player_action[MAX_PLAYERS];
+
/* ------------------------------------------------------------------------- */
/* definition of elements that automatically change to other elements after */
/* ------------------------------------------------------------------------- */
/* forward declaration for changer functions */
-static void InitBuggyBase(int x, int y);
-static void WarnBuggyBase(int x, int y);
+static void InitBuggyBase(int, int);
+static void WarnBuggyBase(int, int);
-static void InitTrap(int x, int y);
-static void ActivateTrap(int x, int y);
-static void ChangeActiveTrap(int x, int y);
+static void InitTrap(int, int);
+static void ActivateTrap(int, int);
+static void ChangeActiveTrap(int, int);
-static void InitRobotWheel(int x, int y);
-static void RunRobotWheel(int x, int y);
-static void StopRobotWheel(int x, int y);
+static void InitRobotWheel(int, int);
+static void RunRobotWheel(int, int);
+static void StopRobotWheel(int, int);
-static void InitTimegateWheel(int x, int y);
-static void RunTimegateWheel(int x, int y);
+static void InitTimegateWheel(int, int);
+static void RunTimegateWheel(int, int);
+
+static void InitMagicBallDelay(int, int);
+static void ActivateMagicBall(int, int);
struct ChangingElementInfo
{
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,
+ 0,
+ InitMagicBallDelay,
+ NULL,
+ ActivateMagicBall
+ },
+ {
+ EL_EMC_SPRING_BUMPER_ACTIVE,
+ EL_EMC_SPRING_BUMPER,
+ 8,
+ NULL,
+ NULL,
+ NULL
+ },
+ {
+ EL_DIAGONAL_SHRINKING,
+ EL_UNDEFINED,
+ 0,
+ NULL,
+ NULL,
+ NULL
+ },
+ {
+ EL_DIAGONAL_GROWING,
+ EL_UNDEFINED,
+ 0,
+ NULL,
+ NULL,
+ NULL,
+ },
{
EL_UNDEFINED,
{ 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 },
};
{ EL_SP_GRAVITY_OFF_PORT_UP, MV_DOWN },
{ EL_SP_GRAVITY_OFF_PORT_DOWN, MV_UP },
- { EL_UNDEFINED, MV_NO_MOVING }
+ { EL_UNDEFINED, MV_NONE }
};
-static unsigned long trigger_events[MAX_NUM_ELEMENTS];
+static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
-#define IS_AUTO_CHANGING(e) (element_info[e].change_events & \
- CH_EVENT_BIT(CE_DELAY))
+#define IS_AUTO_CHANGING(e) (element_info[e].has_change_event[CE_DELAY])
#define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
#define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
IS_JUST_CHANGING(x, y))
#define CE_PAGE(e, ce) (element_info[e].event_page[ce])
+/* static variables for playfield scan mode (scanning forward or backward) */
+static int playfield_scan_start_x = 0;
+static int playfield_scan_start_y = 0;
+static int playfield_scan_delta_x = 1;
+static int playfield_scan_delta_y = 1;
+
+#define SCAN_PLAYFIELD(x, y) for ((y) = playfield_scan_start_y; \
+ (y) >= 0 && (y) <= lev_fieldy - 1; \
+ (y) += playfield_scan_delta_y) \
+ for ((x) = playfield_scan_start_x; \
+ (x) >= 0 && (x) <= lev_fieldx - 1; \
+ (x) += playfield_scan_delta_x)
+
+#ifdef DEBUG
+void DEBUG_SetMaximumDynamite()
+{
+ int i;
+
+ for (i = 0; i < MAX_INVENTORY_SIZE; i++)
+ if (local_player->inventory_size < MAX_INVENTORY_SIZE)
+ local_player->inventory_element[local_player->inventory_size++] =
+ EL_DYNAMITE;
+}
+#endif
+
+static void InitPlayfieldScanModeVars()
+{
+ if (game.use_reverse_scan_direction)
+ {
+ playfield_scan_start_x = lev_fieldx - 1;
+ playfield_scan_start_y = lev_fieldy - 1;
+
+ playfield_scan_delta_x = -1;
+ playfield_scan_delta_y = -1;
+ }
+ else
+ {
+ playfield_scan_start_x = 0;
+ playfield_scan_start_y = 0;
+
+ playfield_scan_delta_x = 1;
+ playfield_scan_delta_y = 1;
+ }
+}
+
+static void InitPlayfieldScanMode(int mode)
+{
+ game.use_reverse_scan_direction =
+ (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
+
+ InitPlayfieldScanModeVars();
+}
+
+static int get_move_delay_from_stepsize(int move_stepsize)
+{
+ move_stepsize =
+ MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
+
+ /* make sure that stepsize value is always a power of 2 */
+ move_stepsize = (1 << log_2(move_stepsize));
+
+ return TILEX / move_stepsize;
+}
+
+static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
+ boolean init_game)
+{
+ int player_nr = player->index_nr;
+ int move_delay = get_move_delay_from_stepsize(move_stepsize);
+ boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
+
+ /* do no immediately change move delay -- the player might just be moving */
+ player->move_delay_value_next = move_delay;
+
+ /* information if player can move must be set separately */
+ player->cannot_move = cannot_move;
+
+ if (init_game)
+ {
+ player->move_delay = game.initial_move_delay[player_nr];
+ player->move_delay_value = game.initial_move_delay_value[player_nr];
+
+ player->move_delay_value_next = -1;
+
+ player->move_delay_reset_counter = 0;
+ }
+}
void GetPlayerConfig()
{
+ GameFrameDelay = setup.game_frame_delay;
+
if (!audio.sound_available)
setup.sound_simple = FALSE;
InitJoysticks();
}
-static int getBeltNrFromBeltElement(int element)
+int GetElementFromGroupElement(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] =
+ if (IS_GROUP_ELEMENT(element))
{
- EL_CONVEYOR_BELT_1_SWITCH_LEFT,
- EL_CONVEYOR_BELT_2_SWITCH_LEFT,
- EL_CONVEYOR_BELT_3_SWITCH_LEFT,
- EL_CONVEYOR_BELT_4_SWITCH_LEFT
- };
+ struct ElementGroupInfo *group = element_info[element].group;
+ int last_anim_random_frame = gfx.anim_random_frame;
+ int element_pos;
- int belt_nr = getBeltNrFromBeltSwitchElement(element);
- int belt_dir_nr = element - belt_base_element[belt_nr];
+ if (group->choice_mode == ANIM_RANDOM)
+ gfx.anim_random_frame = RND(group->num_elements_resolved);
- return (belt_dir_nr % 3);
-}
+ element_pos = getAnimationFrame(group->num_elements_resolved, 1,
+ group->choice_mode, 0,
+ group->choice_pos);
-static int getBeltDirFromBeltSwitchElement(int element)
-{
- static int belt_move_dir[3] =
- {
- MV_LEFT,
- MV_NO_MOVING,
- MV_RIGHT
- };
+ if (group->choice_mode == ANIM_RANDOM)
+ gfx.anim_random_frame = last_anim_random_frame;
+
+ group->choice_pos++;
- int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
+ element = group->element_resolved[element_pos];
+ }
- return belt_move_dir[belt_dir_nr];
+ return element;
}
static void InitPlayerField(int x, int y, int element, boolean init_game)
}
else
{
- stored_player[0].use_murphy_graphic = TRUE;
+ stored_player[0].initial_element = element;
+ stored_player[0].use_murphy = TRUE;
+
+ if (!level.use_artwork_element[0])
+ stored_player[0].artwork_element = EL_SP_MURPHY;
}
Feld[x][y] = EL_PLAYER_1;
level.sp_block_last_field :
level.block_last_field);
-#if USE_NEW_BLOCK_STYLE
-#if 1
- player->block_delay = (player->block_last_field ?
- (element == EL_SP_MURPHY ?
- level.sp_block_delay :
- level.block_delay) : 0);
-#else
- player->block_delay = (element == EL_SP_MURPHY ?
- (player->block_last_field ? 7 : 1) :
- (player->block_last_field ? 7 : 1));
-#endif
+ /* ---------- initialize player's last field block delay --------------- */
-#if 0
- printf("::: block_last_field == %d, block_delay = %d\n",
- player->block_last_field, player->block_delay);
-#endif
-#endif
+ /* always start with reliable default value (no adjustment needed) */
+ player->block_delay_adjustment = 0;
+
+ /* special case 1: in Supaplex, Murphy blocks last field one more frame */
+ if (player->block_last_field && element == EL_SP_MURPHY)
+ player->block_delay_adjustment = 1;
+
+ /* special case 2: in game engines before 3.1.1, blocking was different */
+ if (game.use_block_last_field_bug)
+ player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
if (!options.network || player->connected)
{
player->jx = player->last_jx = x;
player->jy = player->last_jy = y;
}
+
+#if USE_PLAYER_REANIMATION
+ if (!init_game)
+ {
+ int player_nr = GET_PLAYER_NR(element);
+ struct PlayerInfo *player = &stored_player[player_nr];
+
+ if (player->active && player->killed)
+ player->reanimated = TRUE; /* if player was just killed, reanimate him */
+ }
+#endif
}
static void InitField(int x, int y, boolean init_game)
Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
break;
+ case EL_BUG:
case EL_BUG_RIGHT:
case EL_BUG_UP:
case EL_BUG_LEFT:
case EL_BUG_DOWN:
- case EL_BUG:
+ case EL_SPACESHIP:
case EL_SPACESHIP_RIGHT:
case EL_SPACESHIP_UP:
case EL_SPACESHIP_LEFT:
case EL_SPACESHIP_DOWN:
- case EL_SPACESHIP:
+ case EL_BD_BUTTERFLY:
case EL_BD_BUTTERFLY_RIGHT:
case EL_BD_BUTTERFLY_UP:
case EL_BD_BUTTERFLY_LEFT:
case EL_BD_BUTTERFLY_DOWN:
- case EL_BD_BUTTERFLY:
+ case EL_BD_FIREFLY:
case EL_BD_FIREFLY_RIGHT:
case EL_BD_FIREFLY_UP:
case EL_BD_FIREFLY_LEFT:
case EL_BD_FIREFLY_DOWN:
- case EL_BD_FIREFLY:
case EL_PACMAN_RIGHT:
case EL_PACMAN_UP:
case EL_PACMAN_LEFT:
case EL_PACMAN_DOWN:
case EL_YAMYAM:
+ case EL_YAMYAM_LEFT:
+ case EL_YAMYAM_RIGHT:
+ case EL_YAMYAM_UP:
+ case EL_YAMYAM_DOWN:
case EL_DARK_YAMYAM:
case EL_ROBOT:
case EL_PACMAN:
case EL_SP_SNIKSNAK:
case EL_SP_ELECTRON:
+ case EL_MOLE:
case EL_MOLE_LEFT:
case EL_MOLE_RIGHT:
case EL_MOLE_UP:
case EL_MOLE_DOWN:
- case EL_MOLE:
InitMovDir(x, y);
break;
MovDelay[x][y] = 96;
break;
+ case EL_EM_DYNAMITE_ACTIVE:
+ MovDelay[x][y] = 32;
+ break;
+
case EL_LAMP:
local_player->lights_still_needed++;
break;
GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
break;
-#if 0
- case EL_SP_EMPTY:
- Feld[x][y] = EL_EMPTY;
- break;
-#endif
-
-#if 0
- case EL_EM_KEY_1_FILE:
- Feld[x][y] = EL_EM_KEY_1;
- break;
- case EL_EM_KEY_2_FILE:
- Feld[x][y] = EL_EM_KEY_2;
- break;
- case EL_EM_KEY_3_FILE:
- Feld[x][y] = EL_EM_KEY_3;
- break;
- case EL_EM_KEY_4_FILE:
- Feld[x][y] = EL_EM_KEY_4;
- break;
-#endif
-
case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
}
break;
+#if !USE_BOTH_SWITCHGATE_SWITCHES
case EL_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
if (init_game)
Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
break;
- case EL_LIGHT_SWITCH_ACTIVE:
+ case EL_DC_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
if (init_game)
- game.light_time_left = level.time_light * FRAMES_PER_SECOND;
+ Feld[x][y] = EL_DC_SWITCHGATE_SWITCH_UP;
break;
+#endif
- default:
- if (IS_CUSTOM_ELEMENT(element) && CAN_MOVE(element))
- InitMovDir(x, y);
- else if (IS_GROUP_ELEMENT(element))
- {
- struct ElementGroupInfo *group = element_info[element].group;
- int last_anim_random_frame = gfx.anim_random_frame;
- int element_pos;
+ case EL_LIGHT_SWITCH_ACTIVE:
+ if (init_game)
+ game.light_time_left = level.time_light * FRAMES_PER_SECOND;
+ break;
- if (group->choice_mode == ANIM_RANDOM)
- gfx.anim_random_frame = RND(group->num_elements_resolved);
+ case EL_INVISIBLE_STEELWALL:
+ case EL_INVISIBLE_WALL:
+ case EL_INVISIBLE_SAND:
+ if (game.light_time_left > 0 ||
+ game.lenses_time_left > 0)
+ Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
+ break;
- element_pos = getAnimationFrame(group->num_elements_resolved, 1,
- group->choice_mode, 0,
- group->choice_pos);
+ case EL_EMC_MAGIC_BALL:
+ if (game.ball_state)
+ Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
+ break;
- if (group->choice_mode == ANIM_RANDOM)
- gfx.anim_random_frame = last_anim_random_frame;
+ case EL_EMC_MAGIC_BALL_SWITCH:
+ if (game.ball_state)
+ Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
+ break;
- group->choice_pos++;
+ case EL_TRIGGER_PLAYER:
+ case EL_TRIGGER_ELEMENT:
+ case EL_TRIGGER_CE_VALUE:
+ case EL_TRIGGER_CE_SCORE:
+ case EL_SELF:
+ case EL_ANY_ELEMENT:
+ case EL_CURRENT_CE_VALUE:
+ case EL_CURRENT_CE_SCORE:
+ case EL_PREV_CE_1:
+ case EL_PREV_CE_2:
+ case EL_PREV_CE_3:
+ case EL_PREV_CE_4:
+ case EL_PREV_CE_5:
+ case EL_PREV_CE_6:
+ case EL_PREV_CE_7:
+ case EL_PREV_CE_8:
+ case EL_NEXT_CE_1:
+ case EL_NEXT_CE_2:
+ case EL_NEXT_CE_3:
+ case EL_NEXT_CE_4:
+ case EL_NEXT_CE_5:
+ case EL_NEXT_CE_6:
+ case EL_NEXT_CE_7:
+ case EL_NEXT_CE_8:
+ /* reference elements should not be used on the playfield */
+ Feld[x][y] = EL_EMPTY;
+ break;
+
+ default:
+ if (IS_CUSTOM_ELEMENT(element))
+ {
+ if (CAN_MOVE(element))
+ InitMovDir(x, y);
- Feld[x][y] = group->element_resolved[element_pos];
+#if USE_NEW_CUSTOM_VALUE
+ if (!element_info[element].use_last_ce_value || init_game)
+ CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
+#endif
+ }
+ else if (IS_GROUP_ELEMENT(element))
+ {
+ Feld[x][y] = GetElementFromGroupElement(element);
InitField(x, y, init_game);
}
+
break;
}
+
+ if (!init_game)
+ CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
}
static inline void InitField_WithBug1(int x, int y, boolean init_game)
*/
}
-inline void DrawGameValue_Emeralds(int value)
+#if 1
+
+static int get_key_element_from_nr(int key_nr)
{
- DrawText(DX_EMERALDS, DY_EMERALDS, int2str(value, 3), FONT_TEXT_2);
+ int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
+ level.game_engine_type == GAME_ENGINE_TYPE_EM ?
+ EL_EM_KEY_1 : EL_KEY_1);
+
+ return key_base_element + key_nr;
}
-inline void DrawGameValue_Dynamite(int value)
+static int get_next_dropped_element(struct PlayerInfo *player)
{
- DrawText(DX_DYNAMITE, DY_DYNAMITE, int2str(value, 3), FONT_TEXT_2);
+ return (player->inventory_size > 0 ?
+ player->inventory_element[player->inventory_size - 1] :
+ player->inventory_infinite_element != EL_UNDEFINED ?
+ player->inventory_infinite_element :
+ player->dynabombs_left > 0 ?
+ EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
+ EL_UNDEFINED);
}
-inline void DrawGameValue_Keys(struct PlayerInfo *player)
+static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
{
- int i;
+ /* pos >= 0: get element from bottom of the stack;
+ pos < 0: get element from top of the stack */
- for (i = 0; i < MAX_KEYS; i++)
- if (player->key[i])
- DrawMiniGraphicExt(drawto, DX_KEYS + i * MINI_TILEX, DY_KEYS,
- el2edimg(EL_KEY_1 + i));
-}
+ if (pos < 0)
+ {
+ int min_inventory_size = -pos;
+ int inventory_pos = player->inventory_size - min_inventory_size;
+ int min_dynabombs_left = min_inventory_size - player->inventory_size;
-inline void DrawGameValue_Score(int value)
-{
- DrawText(DX_SCORE, DY_SCORE, int2str(value, 5), FONT_TEXT_2);
+ return (player->inventory_size >= min_inventory_size ?
+ player->inventory_element[inventory_pos] :
+ player->inventory_infinite_element != EL_UNDEFINED ?
+ player->inventory_infinite_element :
+ player->dynabombs_left >= min_dynabombs_left ?
+ EL_DYNABOMB_PLAYER_1 + player->index_nr :
+ EL_UNDEFINED);
+ }
+ else
+ {
+ int min_dynabombs_left = pos + 1;
+ int min_inventory_size = pos + 1 - player->dynabombs_left;
+ int inventory_pos = pos - player->dynabombs_left;
+
+ return (player->inventory_infinite_element != EL_UNDEFINED ?
+ player->inventory_infinite_element :
+ player->dynabombs_left >= min_dynabombs_left ?
+ EL_DYNABOMB_PLAYER_1 + player->index_nr :
+ player->inventory_size >= min_inventory_size ?
+ player->inventory_element[inventory_pos] :
+ EL_UNDEFINED);
+ }
}
-inline void DrawGameValue_Time(int value)
+static int compareGamePanelOrderInfo(const void *object1, const void *object2)
{
- if (value < 1000)
- DrawText(DX_TIME1, DY_TIME, int2str(value, 3), FONT_TEXT_2);
+ const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
+ const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
+ int compare_result;
+
+ if (gpo1->sort_priority != gpo2->sort_priority)
+ compare_result = gpo1->sort_priority - gpo2->sort_priority;
else
- DrawText(DX_TIME2, DY_TIME, int2str(value, 4), FONT_LEVEL_NUMBER);
+ compare_result = gpo1->nr - gpo2->nr;
+
+ return compare_result;
}
-inline void DrawGameValue_Level(int value)
+void InitGameControlValues()
{
- if (level_nr < 100)
- DrawText(DX_LEVEL, DY_LEVEL, int2str(value, 2), FONT_TEXT_2);
- else
+ int i;
+
+ for (i = 0; game_panel_controls[i].nr != -1; i++)
{
- /* misuse area for displaying emeralds to draw bigger level number */
- DrawTextExt(drawto, DX_EMERALDS, DY_EMERALDS,
- int2str(value, 3), FONT_LEVEL_NUMBER, BLIT_OPAQUE);
+ struct GamePanelControlInfo *gpc = &game_panel_controls[i];
+ struct GamePanelOrderInfo *gpo = &game_panel_order[i];
+ struct TextPosInfo *pos = gpc->pos;
+ int nr = gpc->nr;
+ int type = gpc->type;
+
+ if (nr != i)
+ {
+ Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
+ Error(ERR_EXIT, "this should not happen -- please debug");
+ }
- /* now copy it to the area for displaying level number */
- BlitBitmap(drawto, drawto,
- DX_EMERALDS, DY_EMERALDS + 1,
- getFontWidth(FONT_LEVEL_NUMBER) * 3,
- getFontHeight(FONT_LEVEL_NUMBER) - 1,
- DX_LEVEL - 1, DY_LEVEL + 1);
+ /* force update of game controls after initialization */
+ gpc->value = gpc->last_value = -1;
+ gpc->frame = gpc->last_frame = -1;
+ gpc->gfx_frame = -1;
- /* restore the area for displaying emeralds */
- DrawGameValue_Emeralds(local_player->gems_still_needed);
+ /* determine panel value width for later calculation of alignment */
+ if (type == TYPE_INTEGER || type == TYPE_STRING)
+ {
+ pos->width = pos->size * getFontWidth(pos->font);
+ pos->height = getFontHeight(pos->font);
+ }
+ else if (type == TYPE_ELEMENT)
+ {
+ pos->width = pos->size;
+ pos->height = pos->size;
+ }
- /* yes, this is all really ugly :-) */
+ /* fill structure for game panel draw order */
+ gpo->nr = gpc->nr;
+ gpo->sort_priority = pos->sort_priority;
}
+
+ /* sort game panel controls according to sort_priority and control number */
+ qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
+ sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
}
-void DrawGameDoorValues()
+void UpdatePlayfieldElementCount()
{
- int i;
+ boolean use_element_count = FALSE;
+ int i, j, x, y;
- DrawGameValue_Level(level_nr);
+ /* first check if it is needed at all to calculate playfield element count */
+ for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
+ if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
+ use_element_count = TRUE;
- for (i = 0; i < MAX_PLAYERS; i++)
- DrawGameValue_Keys(&stored_player[i]);
+ if (!use_element_count)
+ return;
- DrawGameValue_Emeralds(local_player->gems_still_needed);
- DrawGameValue_Dynamite(local_player->inventory_size);
- DrawGameValue_Score(local_player->score);
- DrawGameValue_Time(TimeLeft);
+ for (i = 0; i < MAX_NUM_ELEMENTS; i++)
+ element_info[i].element_count = 0;
+
+ SCAN_PLAYFIELD(x, y)
+ {
+ element_info[Feld[x][y]].element_count++;
+ }
+
+ for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
+ for (j = 0; j < MAX_NUM_ELEMENTS; j++)
+ if (IS_IN_GROUP(j, i))
+ element_info[EL_GROUP_START + i].element_count +=
+ element_info[j].element_count;
}
-static void resolve_group_element(int group_element, int recursion_depth)
+void UpdateGameControlValues()
{
- static int group_nr;
- static struct ElementGroupInfo *group;
- struct ElementGroupInfo *actual_group = element_info[group_element].group;
- int i;
-
- if (recursion_depth > NUM_GROUP_ELEMENTS) /* recursion too deep */
+ int i, k;
+ int time = (local_player->LevelSolved ?
+ local_player->LevelSolved_CountingTime :
+ level.game_engine_type == GAME_ENGINE_TYPE_EM ?
+ level.native_em_level->lev->time :
+ level.game_engine_type == GAME_ENGINE_TYPE_SP ?
+ level.native_sp_level->game_sp->time_played :
+ level.time == 0 ? TimePlayed : TimeLeft);
+ int score = (local_player->LevelSolved ?
+ local_player->LevelSolved_CountingScore :
+ level.game_engine_type == GAME_ENGINE_TYPE_EM ?
+ level.native_em_level->lev->score :
+ level.game_engine_type == GAME_ENGINE_TYPE_SP ?
+ level.native_sp_level->game_sp->score :
+ local_player->score);
+ int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
+ level.native_em_level->lev->required :
+ level.game_engine_type == GAME_ENGINE_TYPE_SP ?
+ level.native_sp_level->game_sp->infotrons_still_needed :
+ local_player->gems_still_needed);
+ int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
+ level.native_em_level->lev->required > 0 :
+ level.game_engine_type == GAME_ENGINE_TYPE_SP ?
+ level.native_sp_level->game_sp->infotrons_still_needed > 0 :
+ local_player->gems_still_needed > 0 ||
+ local_player->sokobanfields_still_needed > 0 ||
+ local_player->lights_still_needed > 0);
+
+ UpdatePlayfieldElementCount();
+
+ /* update game panel control values */
+
+ game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
+ game_panel_controls[GAME_PANEL_GEMS].value = gems;
+
+ game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
+ for (i = 0; i < MAX_NUM_KEYS; i++)
+ game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
+ game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
+ game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
+
+ if (game.centered_player_nr == -1)
{
- Error(ERR_WARN, "recursion too deep when resolving group element %d",
- group_element - EL_GROUP_START + 1);
+ for (i = 0; i < MAX_PLAYERS; i++)
+ {
+ /* only one player in Supaplex game engine */
+ if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
+ break;
- /* replace element which caused too deep recursion by question mark */
- group->element_resolved[group->num_elements_resolved++] = EL_UNKNOWN;
+ for (k = 0; k < MAX_NUM_KEYS; k++)
+ {
+ if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
+ {
+ if (level.native_em_level->ply[i]->keys & (1 << k))
+ game_panel_controls[GAME_PANEL_KEY_1 + k].value =
+ get_key_element_from_nr(k);
+ }
+ else if (stored_player[i].key[k])
+ game_panel_controls[GAME_PANEL_KEY_1 + k].value =
+ get_key_element_from_nr(k);
+ }
- return;
- }
+ if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
+ game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
+ level.native_em_level->ply[i]->dynamite;
+ else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
+ game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
+ level.native_sp_level->game_sp->red_disk_count;
+ else
+ game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
+ stored_player[i].inventory_size;
- if (recursion_depth == 0) /* initialization */
- {
- group = element_info[group_element].group;
- group_nr = group_element - EL_GROUP_START;
+ if (stored_player[i].num_white_keys > 0)
+ game_panel_controls[GAME_PANEL_KEY_WHITE].value =
+ EL_DC_KEY_WHITE;
- group->num_elements_resolved = 0;
- group->choice_pos = 0;
+ game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
+ stored_player[i].num_white_keys;
+ }
}
-
- for (i = 0; i < actual_group->num_elements; i++)
+ else
{
- int element = actual_group->element[i];
+ int player_nr = game.centered_player_nr;
- if (group->num_elements_resolved == NUM_FILE_ELEMENTS)
- break;
-
- if (IS_GROUP_ELEMENT(element))
- resolve_group_element(element, recursion_depth + 1);
- else
+ for (k = 0; k < MAX_NUM_KEYS; k++)
{
- group->element_resolved[group->num_elements_resolved++] = element;
- element_info[element].in_group[group_nr] = TRUE;
+ if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
+ {
+ if (level.native_em_level->ply[player_nr]->keys & (1 << k))
+ game_panel_controls[GAME_PANEL_KEY_1 + k].value =
+ get_key_element_from_nr(k);
+ }
+ else if (stored_player[player_nr].key[k])
+ game_panel_controls[GAME_PANEL_KEY_1 + k].value =
+ get_key_element_from_nr(k);
}
+
+ if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
+ game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
+ level.native_em_level->ply[player_nr]->dynamite;
+ else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
+ game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
+ level.native_sp_level->game_sp->red_disk_count;
+ else
+ game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
+ stored_player[player_nr].inventory_size;
+
+ if (stored_player[player_nr].num_white_keys > 0)
+ game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
+
+ game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
+ stored_player[player_nr].num_white_keys;
}
-#if 0
- if (recursion_depth == 0 && group_element <= EL_GROUP_4)
+ for (i = 0; i < NUM_PANEL_INVENTORY; i++)
{
- printf("::: group %d: %d resolved elements\n",
- group_element - EL_GROUP_START, group->num_elements_resolved);
- for (i = 0; i < group->num_elements_resolved; i++)
- printf("::: - %d ['%s']\n", group->element_resolved[i],
- element_info[group->element_resolved[i]].token_name);
+ game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
+ get_inventory_element_from_pos(local_player, i);
+ game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
+ get_inventory_element_from_pos(local_player, -i - 1);
}
-#endif
-}
+ game_panel_controls[GAME_PANEL_SCORE].value = score;
+ game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
-/*
- =============================================================================
- InitGameEngine()
- -----------------------------------------------------------------------------
- initialize game engine due to level / tape version number
- =============================================================================
-*/
+ game_panel_controls[GAME_PANEL_TIME].value = time;
-static void InitGameEngine()
-{
- int i, j, k;
+ game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
+ game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
+ game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
- /* set game engine from tape file when re-playing, else from level file */
- game.engine_version = (tape.playing ? tape.engine_version :
- level.game_version);
+ game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
- /* ---------------------------------------------------------------------- */
- /* set flags for bugs and changes according to active game engine version */
- /* ---------------------------------------------------------------------- */
+ game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
+ (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
+ EL_EMPTY);
+ game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
+ local_player->shield_normal_time_left;
+ game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
+ (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
+ EL_EMPTY);
+ game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
+ local_player->shield_deadly_time_left;
- /*
- Type of bug/change:
- Before 3.1.0, custom elements that "change when pushing" changed directly
- after the player started pushing them (until then handled in "DigField()").
- Since 3.1.0, these custom elements are not changed until the "pushing"
- move of the element is finished (now handled in "ContinueMoving()").
+ game_panel_controls[GAME_PANEL_EXIT].value =
+ (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
- Affected levels/tapes:
- The first condition is generally needed for all levels/tapes before version
- 3.1.0, which might use the old behaviour before it was changed; known tapes
- that are affected are some tapes from the level set "Walpurgis Gardens" by
- Jamie Cullen.
- The second condition is an exception from the above case and is needed for
- the special case of tapes recorded with game (not engine!) version 3.1.0 or
- above (including some development versions of 3.1.0), but before it was
- known that this change would break tapes like the above and was fixed in
- 3.1.1, so that the changed behaviour was active although the engine version
- while recording maybe was before 3.1.0. There is at least one tape that is
- affected by this exception, which is the tape for the one-level set "Bug
- Machine" by Juergen Bonhagen.
- */
+ game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
+ (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
+ game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
+ (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
+ EL_EMC_MAGIC_BALL_SWITCH);
- game.use_bug_change_when_pushing =
- (game.engine_version < VERSION_IDENT(3,1,0,0) &&
- !(tape.playing &&
- tape.game_version >= VERSION_IDENT(3,1,0,0) &&
- tape.game_version < VERSION_IDENT(3,1,1,0)));
+ game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
+ (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
+ game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
+ game.light_time_left;
- /* ---------------------------------------------------------------------- */
+ game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
+ (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
+ game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
+ game.timegate_time_left;
- /* dynamically adjust element properties according to game engine version */
- InitElementPropertiesEngine(game.engine_version);
+ game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
+ EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
-#if 0
- printf("level %d: level version == %06d\n", level_nr, level.game_version);
- printf(" tape version == %06d [%s] [file: %06d]\n",
- tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
- tape.file_version);
- printf(" => game.engine_version == %06d\n", game.engine_version);
-#endif
+ game_panel_controls[GAME_PANEL_EMC_LENSES].value =
+ (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
+ game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
+ game.lenses_time_left;
- /* ---------- recursively resolve group elements ------------------------- */
+ game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
+ (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
+ game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
+ game.magnify_time_left;
- for (i = 0; i < MAX_NUM_ELEMENTS; i++)
- for (j = 0; j < NUM_GROUP_ELEMENTS; j++)
- element_info[i].in_group[j] = FALSE;
+ game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
+ (game.wind_direction == MV_LEFT ? EL_BALLOON_SWITCH_LEFT :
+ game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
+ game.wind_direction == MV_UP ? EL_BALLOON_SWITCH_UP :
+ game.wind_direction == MV_DOWN ? EL_BALLOON_SWITCH_DOWN :
+ EL_BALLOON_SWITCH_NONE);
- for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
- resolve_group_element(EL_GROUP_START + i, 0);
+ game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
+ local_player->dynabomb_count;
+ game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
+ local_player->dynabomb_size;
+ game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
+ (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
- /* ---------- initialize player's initial move delay --------------------- */
+ game_panel_controls[GAME_PANEL_PENGUINS].value =
+ local_player->friends_still_needed;
-#if USE_NEW_MOVE_DELAY
- /* dynamically adjust player properties according to level information */
- game.initial_move_delay_value =
- (level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED);
+ game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
+ local_player->sokobanfields_still_needed;
+ game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
+ local_player->sokobanfields_still_needed;
- /* dynamically adjust player properties according to game engine version */
- game.initial_move_delay = (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
- game.initial_move_delay_value : 0);
-#else
- /* dynamically adjust player properties according to game engine version */
- game.initial_move_delay =
- (game.engine_version <= VERSION_IDENT(2,0,1,0) ? INITIAL_MOVE_DELAY_ON :
- INITIAL_MOVE_DELAY_OFF);
+ game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
+ (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
- /* dynamically adjust player properties according to level information */
- game.initial_move_delay_value =
- (level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED);
+ for (i = 0; i < NUM_BELTS; i++)
+ {
+ game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
+ (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
+ EL_CONVEYOR_BELT_1_MIDDLE) + i;
+ game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
+ getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
+ }
+
+ game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
+ (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
+ game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
+ game.magic_wall_time_left;
+
+#if USE_PLAYER_GRAVITY
+ game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
+ local_player->gravity;
+#else
+ game_panel_controls[GAME_PANEL_GRAVITY_STATE].value = game.gravity;
#endif
- /* ---------- initialize player's initial push delay --------------------- */
+ for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
+ game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
- /* dynamically adjust player properties according to game engine version */
- game.initial_push_delay_value =
- (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
+ for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
+ game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
+ (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
+ game.panel.element[i].id : EL_UNDEFINED);
- /* ---------- initialize changing elements ------------------------------- */
+ for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
+ game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
+ (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
+ element_info[game.panel.element_count[i].id].element_count : 0);
- /* initialize changing elements information */
- for (i = 0; i < MAX_NUM_ELEMENTS; i++)
- {
- struct ElementInfo *ei = &element_info[i];
+ for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
+ game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
+ (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
+ element_info[game.panel.ce_score[i].id].collect_score : 0);
- /* this pointer might have been changed in the level editor */
- ei->change = &ei->change_page[0];
+ for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
+ game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
+ (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
+ element_info[game.panel.ce_score_element[i].id].collect_score :
+ EL_UNDEFINED);
- if (!IS_CUSTOM_ELEMENT(i))
- {
- ei->change->target_element = EL_EMPTY_SPACE;
- ei->change->delay_fixed = 0;
- ei->change->delay_random = 0;
- ei->change->delay_frames = 1;
- }
+ game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
+ game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
+ game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
- ei->change_events = CE_BITMASK_DEFAULT;
- for (j = 0; j < NUM_CHANGE_EVENTS; j++)
- {
- ei->event_page_nr[j] = 0;
- ei->event_page[j] = &ei->change_page[0];
- }
- }
+ /* update game panel control frames */
- /* add changing elements from pre-defined list */
- for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
+ for (i = 0; game_panel_controls[i].nr != -1; i++)
{
- struct ChangingElementInfo *ch_delay = &change_delay_list[i];
- struct ElementInfo *ei = &element_info[ch_delay->element];
+ struct GamePanelControlInfo *gpc = &game_panel_controls[i];
- ei->change->target_element = ch_delay->target_element;
- ei->change->delay_fixed = ch_delay->change_delay;
+ if (gpc->type == TYPE_ELEMENT)
+ {
+ if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
+ {
+ int last_anim_random_frame = gfx.anim_random_frame;
+ int element = gpc->value;
+ int graphic = el2panelimg(element);
- ei->change->pre_change_function = ch_delay->pre_change_function;
- ei->change->change_function = ch_delay->change_function;
- ei->change->post_change_function = ch_delay->post_change_function;
+ if (gpc->value != gpc->last_value)
+ {
+ gpc->gfx_frame = 0;
+ gpc->gfx_random = INIT_GFX_RANDOM();
+ }
+ else
+ {
+ gpc->gfx_frame++;
- ei->change_events |= CH_EVENT_BIT(CE_DELAY);
+ if (ANIM_MODE(graphic) == ANIM_RANDOM &&
+ IS_NEXT_FRAME(gpc->gfx_frame, graphic))
+ gpc->gfx_random = INIT_GFX_RANDOM();
+ }
-#if 1
- SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
-#endif
- }
+ if (ANIM_MODE(graphic) == ANIM_RANDOM)
+ gfx.anim_random_frame = gpc->gfx_random;
-#if 1
- /* add change events from custom element configuration */
- for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
- {
- struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
+ if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
+ gpc->gfx_frame = element_info[element].collect_score;
- for (j = 0; j < ei->num_change_pages; j++)
- {
- if (!ei->change_page[j].can_change)
- continue;
+ gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
+ gpc->gfx_frame);
- for (k = 0; k < NUM_CHANGE_EVENTS; k++)
- {
- /* only add event page for the first page found with this event */
- if (ei->change_page[j].events & CH_EVENT_BIT(k) &&
- !(ei->change_events & CH_EVENT_BIT(k)))
- {
- ei->change_events |= CH_EVENT_BIT(k);
- ei->event_page_nr[k] = j;
- ei->event_page[k] = &ei->change_page[j];
- }
+ if (ANIM_MODE(graphic) == ANIM_RANDOM)
+ gfx.anim_random_frame = last_anim_random_frame;
}
}
}
+}
-#else
+void DisplayGameControlValues()
+{
+ boolean redraw_panel = FALSE;
+ int i;
- /* add change events from custom element configuration */
- for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
+ for (i = 0; game_panel_controls[i].nr != -1; i++)
{
- int element = EL_CUSTOM_START + i;
-
- /* only add custom elements that change after fixed/random frame delay */
- if (CAN_CHANGE(element) && HAS_CHANGE_EVENT(element, CE_DELAY))
- element_info[element].change_events |= CH_EVENT_BIT(CE_DELAY);
- }
-#endif
+ struct GamePanelControlInfo *gpc = &game_panel_controls[i];
- /* ---------- initialize run-time trigger player and element ------------- */
+ if (PANEL_DEACTIVATED(gpc->pos))
+ continue;
- for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
- {
- struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
+ if (gpc->value == gpc->last_value &&
+ gpc->frame == gpc->last_frame)
+ continue;
- for (j = 0; j < ei->num_change_pages; j++)
- {
- ei->change_page[j].actual_trigger_element = EL_EMPTY;
- ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
- }
+ redraw_panel = TRUE;
}
- /* ---------- initialize trigger events ---------------------------------- */
+ if (!redraw_panel)
+ return;
- /* initialize trigger events information */
- for (i = 0; i < MAX_NUM_ELEMENTS; i++)
- trigger_events[i] = EP_BITMASK_DEFAULT;
+ /* copy default game door content to main double buffer */
+#if 1
+ /* !!! CHECK AGAIN !!! */
+ SetPanelBackground();
+ // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
+ DrawBackground(DX, DY, DXSIZE, DYSIZE);
+#else
+ BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
+ DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
+#endif
+ /* redraw game control buttons */
#if 1
- /* add trigger events from element change event properties */
- for (i = 0; i < MAX_NUM_ELEMENTS; i++)
- {
- struct ElementInfo *ei = &element_info[i];
+ RedrawGameButtons();
+#else
+ UnmapGameButtons();
+ MapGameButtons();
+#endif
- for (j = 0; j < ei->num_change_pages; j++)
- {
- if (!ei->change_page[j].can_change)
- continue;
+ game_status = GAME_MODE_PSEUDO_PANEL;
- if (ei->change_page[j].events & CH_EVENT_BIT(CE_BY_OTHER_ACTION))
- {
- int trigger_element = ei->change_page[j].trigger_element;
-
- if (IS_GROUP_ELEMENT(trigger_element))
- {
- struct ElementGroupInfo *group = element_info[trigger_element].group;
-
- for (k = 0; k < group->num_elements_resolved; k++)
- trigger_events[group->element_resolved[k]]
- |= ei->change_page[j].events;
- }
- else
- trigger_events[trigger_element] |= ei->change_page[j].events;
- }
- }
- }
+#if 1
+ for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
#else
- /* add trigger events from element change event properties */
- for (i = 0; i < MAX_NUM_ELEMENTS; i++)
- if (HAS_CHANGE_EVENT(i, CE_BY_OTHER_ACTION))
- trigger_events[element_info[i].change->trigger_element] |=
- element_info[i].change->events;
+ for (i = 0; game_panel_controls[i].nr != -1; i++)
+#endif
+ {
+#if 1
+ int nr = game_panel_order[i].nr;
+ struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
+#else
+ struct GamePanelControlInfo *gpc = &game_panel_controls[i];
+ int nr = gpc->nr;
+#endif
+ struct TextPosInfo *pos = gpc->pos;
+ int type = gpc->type;
+ int value = gpc->value;
+ int frame = gpc->frame;
+#if 0
+ int last_value = gpc->last_value;
+ int last_frame = gpc->last_frame;
#endif
+ int size = pos->size;
+ int font = pos->font;
+ boolean draw_masked = pos->draw_masked;
+ int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
- /* ---------- initialize push delay -------------------------------------- */
+ if (PANEL_DEACTIVATED(pos))
+ continue;
- /* initialize push delay values to default */
- for (i = 0; i < MAX_NUM_ELEMENTS; i++)
- {
- if (!IS_CUSTOM_ELEMENT(i))
- {
- element_info[i].push_delay_fixed = game.default_push_delay_fixed;
- element_info[i].push_delay_random = game.default_push_delay_random;
- }
- }
+#if 0
+ if (value == last_value && frame == last_frame)
+ continue;
+#endif
- /* set push delay value for certain elements from pre-defined list */
- for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
- {
- int e = push_delay_list[i].element;
+ gpc->last_value = value;
+ gpc->last_frame = frame;
- element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
- element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
- }
+#if 0
+ printf("::: value %d changed from %d to %d\n", nr, last_value, value);
+#endif
- /* set push delay value for Supaplex elements for newer engine versions */
- if (game.engine_version >= VERSION_IDENT(3,1,0,0))
- {
- for (i = 0; i < MAX_NUM_ELEMENTS; i++)
+ if (type == TYPE_INTEGER)
{
- if (IS_SP_ELEMENT(i))
+ if (nr == GAME_PANEL_LEVEL_NUMBER ||
+ nr == GAME_PANEL_TIME)
{
-#if USE_NEW_MOVE_STYLE
- /* set SP push delay to just enough to push under a falling zonk */
- int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
+ boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
- element_info[i].push_delay_fixed = delay;
- element_info[i].push_delay_random = 0;
-#else
- element_info[i].push_delay_fixed = 6; /* just enough to escape ... */
- element_info[i].push_delay_random = 0; /* ... from falling zonk */
-#endif
- }
- }
- }
+ if (use_dynamic_size) /* use dynamic number of digits */
+ {
+ int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
+ int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
+ int size2 = size1 + 1;
+ int font1 = pos->font;
+ int font2 = pos->font_alt;
- /* ---------- initialize move stepsize ----------------------------------- */
+ size = (value < value_change ? size1 : size2);
+ font = (value < value_change ? font1 : font2);
- /* initialize move stepsize values to default */
- for (i = 0; i < MAX_NUM_ELEMENTS; i++)
- if (!IS_CUSTOM_ELEMENT(i))
- element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
+#if 0
+ /* clear background if value just changed its size (dynamic digits) */
+ if ((last_value < value_change) != (value < value_change))
+ {
+ int width1 = size1 * getFontWidth(font1);
+ int width2 = size2 * getFontWidth(font2);
+ int max_width = MAX(width1, width2);
+ int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
- /* set move stepsize value for certain elements from pre-defined list */
- for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
- {
- int e = move_stepsize_list[i].element;
+ pos->width = max_width;
- element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
- }
+ ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
+ max_width, max_height);
+ }
+#endif
+ }
+ }
-#if 0
- /* ---------- initialize move dig/leave ---------------------------------- */
+#if 1
+ /* correct text size if "digits" is zero or less */
+ if (size <= 0)
+ size = strlen(int2str(value, size));
- for (i = 0; i < MAX_NUM_ELEMENTS; i++)
- {
- element_info[i].can_leave_element = FALSE;
- element_info[i].can_leave_element_last = FALSE;
- }
+ /* dynamically correct text alignment */
+ pos->width = size * getFontWidth(font);
#endif
- /* ---------- initialize gem count --------------------------------------- */
+ DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
+ int2str(value, size), font, mask_mode);
+ }
+ else if (type == TYPE_ELEMENT)
+ {
+ int element, graphic;
+ Bitmap *src_bitmap;
+ int src_x, src_y;
+ int width, height;
+ int dst_x = PANEL_XPOS(pos);
+ int dst_y = PANEL_YPOS(pos);
- /* initialize gem count values for each element */
- for (i = 0; i < MAX_NUM_ELEMENTS; i++)
- if (!IS_CUSTOM_ELEMENT(i))
- element_info[i].collect_count = 0;
+#if 1
+ if (value != EL_UNDEFINED && value != EL_EMPTY)
+ {
+ element = value;
+ graphic = el2panelimg(value);
- /* add gem count values for all elements from pre-defined list */
- for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
- element_info[collect_count_list[i].element].collect_count =
- collect_count_list[i].count;
+ // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
- /* ---------- initialize access direction -------------------------------- */
+#if 1
+ if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
+ size = TILESIZE;
+#endif
- /* initialize access direction values to default (access from every side) */
- for (i = 0; i < MAX_NUM_ELEMENTS; i++)
- if (!IS_CUSTOM_ELEMENT(i))
- element_info[i].access_direction = MV_ALL_DIRECTIONS;
+ getSizedGraphicSource(graphic, frame, size, &src_bitmap,
+ &src_x, &src_y);
- /* set access direction value for certain elements from pre-defined list */
- for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
- element_info[access_direction_list[i].element].access_direction =
- access_direction_list[i].direction;
-}
+ width = graphic_info[graphic].width * size / TILESIZE;
+ height = graphic_info[graphic].height * size / TILESIZE;
+ if (draw_masked)
+ {
+ SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
+ dst_x - src_x, dst_y - src_y);
+ BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
+ dst_x, dst_y);
+ }
+ else
+ {
+ BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
+ dst_x, dst_y);
+ }
+ }
+#else
+ if (value == EL_UNDEFINED || value == EL_EMPTY)
+ {
+ element = (last_value == EL_UNDEFINED ? EL_EMPTY : last_value);
+ graphic = el2panelimg(element);
-/*
- =============================================================================
- InitGame()
- -----------------------------------------------------------------------------
- initialize and start new game
- =============================================================================
-*/
+ src_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
+ src_x = DOOR_GFX_PAGEX5 + ALIGNED_TEXT_XPOS(pos);
+ src_y = DOOR_GFX_PAGEY1 + ALIGNED_TEXT_YPOS(pos);
+ }
+ else
+ {
+ element = value;
+ graphic = el2panelimg(value);
-void InitGame()
-{
- boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
- boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
- boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
- int i, j, k, x, y;
+ getSizedGraphicSource(graphic, frame, size, &src_bitmap, &src_x,&src_y);
+ }
- InitGameEngine();
+ width = graphic_info[graphic].width * size / TILESIZE;
+ height = graphic_info[graphic].height * size / TILESIZE;
-#if 0
-#if DEBUG
-#if USE_NEW_AMOEBA_CODE
- printf("Using new amoeba code.\n");
-#else
- printf("Using old amoeba code.\n");
-#endif
-#endif
+ BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height, dst_x, dst_y);
#endif
+ }
+ else if (type == TYPE_STRING)
+ {
+ boolean active = (value != 0);
+ char *state_normal = "off";
+ char *state_active = "on";
+ char *state = (active ? state_active : state_normal);
+ char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
+ nr == GAME_PANEL_PLAYER_NAME ? setup.player_name :
+ nr == GAME_PANEL_LEVEL_NAME ? level.name :
+ nr == GAME_PANEL_LEVEL_AUTHOR ? level.author : NULL);
+
+ if (nr == GAME_PANEL_GRAVITY_STATE)
+ {
+ int font1 = pos->font; /* (used for normal state) */
+ int font2 = pos->font_alt; /* (used for active state) */
+#if 0
+ int size1 = strlen(state_normal);
+ int size2 = strlen(state_active);
+ int width1 = size1 * getFontWidth(font1);
+ int width2 = size2 * getFontWidth(font2);
+ int max_width = MAX(width1, width2);
+ int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
- /* don't play tapes over network */
- network_playing = (options.network && !tape.playing);
-
- for (i = 0; i < MAX_PLAYERS; i++)
- {
- struct PlayerInfo *player = &stored_player[i];
-
- player->index_nr = i;
- player->index_bit = (1 << i);
- player->element_nr = EL_PLAYER_1 + i;
+ pos->width = max_width;
- player->present = FALSE;
- player->active = FALSE;
+ /* clear background for values that may have changed its size */
+ ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
+ max_width, max_height);
+#endif
- player->action = 0;
- player->effective_action = 0;
- player->programmed_action = 0;
+ font = (active ? font2 : font1);
+ }
- player->score = 0;
- player->gems_still_needed = level.gems_needed;
- player->sokobanfields_still_needed = 0;
- player->lights_still_needed = 0;
- player->friends_still_needed = 0;
+ if (s != NULL)
+ {
+ char *s_cut;
- for (j = 0; j < MAX_KEYS; j++)
- player->key[j] = FALSE;
+#if 1
+ if (size <= 0)
+ {
+ /* don't truncate output if "chars" is zero or less */
+ size = strlen(s);
- player->dynabomb_count = 0;
- player->dynabomb_size = 1;
- player->dynabombs_left = 0;
- player->dynabomb_xl = FALSE;
+ /* dynamically correct text alignment */
+ pos->width = size * getFontWidth(font);
+ }
+#endif
- player->MovDir = MV_NO_MOVING;
- player->MovPos = 0;
- player->GfxPos = 0;
- player->GfxDir = MV_NO_MOVING;
- player->GfxAction = ACTION_DEFAULT;
- player->Frame = 0;
- player->StepFrame = 0;
+ s_cut = getStringCopyN(s, size);
- player->use_murphy_graphic = FALSE;
+ DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
+ s_cut, font, mask_mode);
- player->block_last_field = FALSE; /* initialized in InitPlayerField() */
- player->block_delay = -1; /* initialized in InitPlayerField() */
+ free(s_cut);
+ }
+ }
- player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
+ redraw_mask |= REDRAW_DOOR_1;
+ }
- player->actual_frame_counter = 0;
+ game_status = GAME_MODE_PLAYING;
+}
- player->step_counter = 0;
+void UpdateAndDisplayGameControlValues()
+{
+ if (tape.warp_forward)
+ return;
- player->last_move_dir = MV_NO_MOVING;
+ UpdateGameControlValues();
+ DisplayGameControlValues();
+}
- player->is_waiting = FALSE;
- player->is_moving = FALSE;
- player->is_auto_moving = FALSE;
- player->is_digging = FALSE;
- player->is_snapping = FALSE;
- player->is_collecting = FALSE;
- player->is_pushing = FALSE;
- player->is_switching = FALSE;
- player->is_dropping = FALSE;
+void DrawGameValue_Emeralds(int value)
+{
+ 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->size;
- player->is_bored = FALSE;
- player->is_sleeping = FALSE;
+#if 1
+ return; /* !!! USE NEW STUFF !!! */
+#endif
- player->frame_counter_bored = -1;
- player->frame_counter_sleeping = -1;
+ if (PANEL_DEACTIVATED(pos))
+ return;
- player->anim_delay_counter = 0;
- player->post_delay_counter = 0;
+ pos->width = chars * font_width;
- player->action_waiting = ACTION_DEFAULT;
- player->last_action_waiting = ACTION_DEFAULT;
- player->special_action_bored = ACTION_DEFAULT;
- player->special_action_sleeping = ACTION_DEFAULT;
+ DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
+}
- player->num_special_action_bored = 0;
- player->num_special_action_sleeping = 0;
+void DrawGameValue_Dynamite(int value)
+{
+ struct TextPosInfo *pos = &game.panel.inventory_count;
+#if 1
+ int font_nr = pos->font;
+#else
+ int font_nr = FONT_TEXT_2;
+#endif
+ int font_width = getFontWidth(font_nr);
+ int chars = pos->size;
- /* determine number of special actions for bored and sleeping animation */
- for (j = ACTION_BORING_1; j <= ACTION_BORING_LAST; j++)
- {
- boolean found = FALSE;
+#if 1
+ return; /* !!! USE NEW STUFF !!! */
+#endif
- for (k = 0; k < NUM_DIRECTIONS; k++)
- if (el_act_dir2img(player->element_nr, j, k) !=
- el_act_dir2img(player->element_nr, ACTION_DEFAULT, k))
- found = TRUE;
+ if (PANEL_DEACTIVATED(pos))
+ return;
- if (found)
- player->num_special_action_bored++;
- else
- break;
- }
- for (j = ACTION_SLEEPING_1; j <= ACTION_SLEEPING_LAST; j++)
- {
- boolean found = FALSE;
+ pos->width = chars * font_width;
- for (k = 0; k < NUM_DIRECTIONS; k++)
- if (el_act_dir2img(player->element_nr, j, k) !=
- el_act_dir2img(player->element_nr, ACTION_DEFAULT, k))
- found = TRUE;
+ DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
+}
- if (found)
- player->num_special_action_sleeping++;
- else
- break;
- }
+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->size;
- player->switch_x = -1;
- player->switch_y = -1;
+#if 1
+ return; /* !!! USE NEW STUFF !!! */
+#endif
- player->show_envelope = 0;
+ if (PANEL_DEACTIVATED(pos))
+ return;
- player->move_delay = game.initial_move_delay;
- player->move_delay_value = game.initial_move_delay_value;
+ pos->width = chars * font_width;
- player->move_delay_reset_counter = 0;
+ DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
+}
-#if USE_NEW_PUSH_DELAY
- player->push_delay = -1; /* initialized when pushing starts */
- player->push_delay_value = game.initial_push_delay_value;
+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->size;
+#if 1
+ int font1_nr = pos->font;
+ int font2_nr = pos->font_alt;
#else
- player->push_delay = 0;
- player->push_delay_value = game.initial_push_delay_value;
+ 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);
- player->drop_delay = 0;
+#if 1
+ return; /* !!! USE NEW STUFF !!! */
+#endif
- player->last_jx = player->last_jy = 0;
- player->jx = player->jy = 0;
+ if (PANEL_DEACTIVATED(pos))
+ return;
- player->shield_normal_time_left = 0;
- player->shield_deadly_time_left = 0;
+ if (use_dynamic_chars) /* use dynamic number of chars */
+ {
+ chars = (value < 1000 ? chars1 : chars2);
+ font_nr = (value < 1000 ? font1_nr : font2_nr);
+ }
- player->inventory_infinite_element = EL_UNDEFINED;
- player->inventory_size = 0;
+ /* 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));
- DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
- SnapField(player, 0, 0);
+ pos->width = max_width;
- player->LevelSolved = FALSE;
- player->GameOver = FALSE;
+ ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
+ max_width, max_height);
}
- network_player_action_received = FALSE;
+ pos->width = chars * getFontWidth(font_nr);
-#if defined(NETWORK_AVALIABLE)
- /* initial null action */
- if (network_playing)
- SendToServer_MovePlayer(MV_NO_MOVING);
+ 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->size;
+#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);
- ZX = ZY = -1;
+#if 1
+ return; /* !!! USE NEW STUFF !!! */
+#endif
- FrameCounter = 0;
- TimeFrames = 0;
- TimePlayed = 0;
- TimeLeft = level.time;
- TapeTime = 0;
+ if (PANEL_DEACTIVATED(pos))
+ return;
- ScreenMovDir = MV_NO_MOVING;
- ScreenMovPos = 0;
- ScreenGfxPos = 0;
+ if (use_dynamic_chars) /* use dynamic number of chars */
+ {
+ chars = (level_nr < 100 ? chars1 : chars2);
+ font_nr = (level_nr < 100 ? font1_nr : font2_nr);
+ }
- ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
+ pos->width = chars * getFontWidth(font_nr);
- AllPlayersGone = FALSE;
+ DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
+}
- game.yamyam_content_nr = 0;
- game.magic_wall_active = FALSE;
- game.magic_wall_time_left = 0;
- game.light_time_left = 0;
- game.timegate_time_left = 0;
- game.switchgate_pos = 0;
- game.balloon_dir = MV_NO_MOVING;
- game.gravity = level.initial_gravity;
- game.explosions_delayed = TRUE;
+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;
- game.envelope_active = FALSE;
+#if 1
+ return; /* !!! USE NEW STUFF !!! */
+#endif
- for (i = 0; i < NUM_BELTS; i++)
- {
- game.belt_dir[i] = MV_NO_MOVING;
- game.belt_dir_nr[i] = 3; /* not moving, next moving left */
- }
+#if 0
+ if (PANEL_DEACTIVATED(pos))
+ return;
+#endif
- for (i = 0; i < MAX_NUM_AMOEBA; i++)
- AmoebaCnt[i] = AmoebaCnt2[i] = 0;
+#if 0
+ if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
+ base_key_graphic = EL_EM_KEY_1;
+#endif
- for (x = 0; x < lev_fieldx; x++)
+#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
{
- for (y = 0; y < lev_fieldy; y++)
- {
- Feld[x][y] = level.field[x][y];
- MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
- ChangeDelay[x][y] = 0;
- ChangePage[x][y] = -1;
- Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
- AmoebaNr[x][y] = 0;
- WasJustMoving[x][y] = 0;
- WasJustFalling[x][y] = 0;
- CheckCollision[x][y] = 0;
- Stop[x][y] = FALSE;
- Pushed[x][y] = FALSE;
+#if 1
+ struct TextPosInfo *pos = &game.panel.key[i];
+#endif
+ int src_x = DOOR_GFX_PAGEX5 + 18 + (i % 4) * MINI_TILEX;
+ 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
- Changed[x][y] = CE_BITMASK_DEFAULT;
- ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
+#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
- ExplodePhase[x][y] = 0;
- ExplodeDelay[x][y] = 0;
- ExplodeField[x][y] = EX_TYPE_NONE;
+#if 1
+ if (PANEL_DEACTIVATED(pos))
+ continue;
+#endif
- RunnerVisit[x][y] = 0;
- PlayerVisit[x][y] = 0;
+#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 */
- GfxFrame[x][y] = 0;
- GfxRandom[x][y] = INIT_GFX_RANDOM();
- GfxElement[x][y] = EL_UNDEFINED;
- GfxAction[x][y] = ACTION_DEFAULT;
- GfxDir[x][y] = MV_NO_MOVING;
- }
- }
+ BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
+ MINI_TILEX, MINI_TILEY, dst_x, dst_y);
- for (y = 0; y < lev_fieldy; y++)
- {
- for (x = 0; x < lev_fieldx; x++)
+ if (key[i])
{
- if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
- emulate_bd = FALSE;
- if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
- emulate_sb = FALSE;
- if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
- emulate_sp = FALSE;
+#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);
- InitField(x, y, TRUE);
+ 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, 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
}
+}
- InitBeltMovement();
+#else
- game.emulation = (emulate_bd ? EMU_BOULDERDASH :
- emulate_sb ? EMU_SOKOBAN :
- emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
+void DrawGameValue_Emeralds(int value)
+{
+ int font_nr = FONT_TEXT_2;
+ int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
- /* initialize explosion and ignition delay */
- for (i = 0; i < MAX_NUM_ELEMENTS; i++)
- {
- if (!IS_CUSTOM_ELEMENT(i))
- {
- int num_phase = 8;
- int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
- game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
- game.emulation == EMU_SUPAPLEX ? 3 : 2);
- int last_phase = (num_phase + 1) * delay;
- int half_phase = (num_phase / 2) * delay;
+ if (PANEL_DEACTIVATED(game.panel.gems))
+ return;
- element_info[i].explosion_delay = last_phase - 1;
- element_info[i].ignition_delay = half_phase;
+ DrawText(DX_EMERALDS + xpos, DY_EMERALDS, int2str(value, 3), font_nr);
+}
-#if 0
- if (i == EL_BLACK_ORB)
- element_info[i].ignition_delay = 0;
+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_count))
+ 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_nr);
+}
+
+void DrawGameValue_Time(int value)
+{
+ int font1_nr = FONT_TEXT_2;
+#if 1
+ int font2_nr = FONT_TEXT_1;
#else
- if (i == EL_BLACK_ORB)
- element_info[i].ignition_delay = 1;
+ 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 0
- if (element_info[i].explosion_delay < 1) /* !!! check again !!! */
- element_info[i].explosion_delay = 1;
+ if (PANEL_DEACTIVATED(game.panel.time))
+ return;
- if (element_info[i].ignition_delay < 1) /* !!! check again !!! */
- element_info[i].ignition_delay = 1;
+ /* clear background if value just changed its size */
+ if (value == 999 || value == 1000)
+ ClearRectangleOnBackground(drawto, DX_TIME1, DY_TIME, 14 * 3, 14);
+
+ if (value < 1000)
+ DrawText(DX_TIME1 + xpos3, DY_TIME, int2str(value, 3), font1_nr);
+ else
+ DrawText(DX_TIME2 + xpos4, DY_TIME, int2str(value, 4), font2_nr);
+}
+
+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
- }
- /* correct non-moving belts to start moving left */
- for (i = 0; i < NUM_BELTS; i++)
- if (game.belt_dir[i] == MV_NO_MOVING)
- game.belt_dir_nr[i] = 3; /* not moving, next moving left */
+ if (PANEL_DEACTIVATED(game.panel.level))
+ return;
- /* check if any connected player was not found in playfield */
- for (i = 0; i < MAX_PLAYERS; i++)
- {
- struct PlayerInfo *player = &stored_player[i];
+ if (level_nr < 100)
+ DrawText(DX_LEVEL1, DY_LEVEL, int2str(value, 2), font1_nr);
+ else
+ DrawText(DX_LEVEL2, DY_LEVEL, int2str(value, 3), font2_nr);
+}
- if (player->connected && !player->present)
- {
- for (j = 0; j < MAX_PLAYERS; j++)
- {
- struct PlayerInfo *some_player = &stored_player[j];
- int jx = some_player->jx, jy = some_player->jy;
+void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
+{
+ int base_key_graphic = EL_KEY_1;
+ int i;
- /* assign first free player found that is present in the playfield */
- if (some_player->present && !some_player->connected)
- {
- player->present = TRUE;
- player->active = TRUE;
+ if (PANEL_DEACTIVATED(game.panel.keys))
+ return;
- some_player->present = FALSE;
- some_player->active = FALSE;
+ if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
+ base_key_graphic = EL_EM_KEY_1;
-#if 0
- player->element_nr = some_player->element_nr;
-#endif
+ /* 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);
+ }
+}
-#if USE_NEW_BLOCK_STYLE
- player->block_last_field = some_player->block_last_field;
- player->block_delay = some_player->block_delay;
#endif
- StorePlayer[jx][jy] = player->element_nr;
- player->jx = player->last_jx = jx;
- player->jy = player->last_jy = jy;
+void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
+ int key_bits)
+{
+ int key[MAX_NUM_KEYS];
+ int i;
- break;
- }
- }
- }
- }
+ /* prevent EM engine from updating time/score values parallel to GameWon() */
+ if (level.game_engine_type == GAME_ENGINE_TYPE_EM &&
+ local_player->LevelSolved)
+ return;
- if (tape.playing)
- {
- /* when playing a tape, eliminate all players which do not participate */
+ for (i = 0; i < MAX_NUM_KEYS; i++)
+ key[i] = key_bits & (1 << i);
- for (i = 0; i < MAX_PLAYERS; i++)
- {
- if (stored_player[i].active && !tape.player_participates[i])
- {
- struct PlayerInfo *player = &stored_player[i];
- int jx = player->jx, jy = player->jy;
+ DrawGameValue_Level(level_nr);
- player->active = FALSE;
- StorePlayer[jx][jy] = 0;
- Feld[jx][jy] = EL_EMPTY;
- }
- }
- }
- else if (!options.network && !setup.team_mode) /* && !tape.playing */
- {
- /* when in single player mode, eliminate all but the first active player */
+ DrawGameValue_Emeralds(emeralds);
+ DrawGameValue_Dynamite(dynamite);
+ DrawGameValue_Score(score);
+ DrawGameValue_Time(time);
- for (i = 0; i < MAX_PLAYERS; i++)
- {
- if (stored_player[i].active)
- {
- for (j = i + 1; j < MAX_PLAYERS; j++)
- {
- if (stored_player[j].active)
- {
- struct PlayerInfo *player = &stored_player[j];
- int jx = player->jx, jy = player->jy;
+ DrawGameValue_Keys(key);
+}
- player->active = FALSE;
- player->present = FALSE;
+void UpdateGameDoorValues()
+{
+ UpdateGameControlValues();
+}
- StorePlayer[jx][jy] = 0;
- Feld[jx][jy] = EL_EMPTY;
- }
- }
- }
- }
- }
+void DrawGameDoorValues()
+{
+ DisplayGameControlValues();
+}
- /* when recording the game, store which players take part in the game */
- if (tape.recording)
+void DrawGameDoorValues_OLD()
+{
+ int time_value = (level.time == 0 ? TimePlayed : TimeLeft);
+ int dynamite_value = 0;
+ int score_value = (local_player->LevelSolved ? local_player->score_final :
+ local_player->score);
+ int gems_value = local_player->gems_still_needed;
+ int key_bits = 0;
+ int i, j;
+
+ if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
{
- for (i = 0; i < MAX_PLAYERS; i++)
- if (stored_player[i].active)
- tape.player_participates[i] = TRUE;
+ DrawGameDoorValues_EM();
+
+ return;
}
- if (options.debug)
+ if (game.centered_player_nr == -1)
{
for (i = 0; i < MAX_PLAYERS; i++)
{
- struct PlayerInfo *player = &stored_player[i];
+ for (j = 0; j < MAX_NUM_KEYS; j++)
+ if (stored_player[i].key[j])
+ key_bits |= (1 << j);
- printf("Player %d: present == %d, connected == %d, active == %d.\n",
- i+1,
- player->present,
- player->connected,
- player->active);
- if (local_player == player)
- printf("Player %d is local player.\n", i+1);
+ dynamite_value += stored_player[i].inventory_size;
}
}
-
- if (BorderElement == EL_EMPTY)
- {
- SBX_Left = 0;
- SBX_Right = lev_fieldx - SCR_FIELDX;
- SBY_Upper = 0;
- SBY_Lower = lev_fieldy - SCR_FIELDY;
- }
else
{
- SBX_Left = -1;
- SBX_Right = lev_fieldx - SCR_FIELDX + 1;
- SBY_Upper = -1;
- SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
- }
+ int player_nr = game.centered_player_nr;
- if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
- SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
+ for (i = 0; i < MAX_NUM_KEYS; i++)
+ if (stored_player[player_nr].key[i])
+ key_bits |= (1 << i);
- if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
- SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
+ dynamite_value = stored_player[player_nr].inventory_size;
+ }
- /* if local player not found, look for custom element that might create
- the player (make some assumptions about the right custom element) */
- if (!local_player->present)
- {
- int start_x = 0, start_y = 0;
- int found_rating = 0;
- int found_element = EL_UNDEFINED;
+ DrawAllGameValues(gems_value, dynamite_value, score_value, time_value,
+ key_bits);
+}
- for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
- {
- int element = Feld[x][y];
- int content;
- int xx, yy;
- boolean is_player;
- if (!IS_CUSTOM_ELEMENT(element))
- continue;
+/*
+ =============================================================================
+ InitGameEngine()
+ -----------------------------------------------------------------------------
+ initialize game engine due to level / tape version number
+ =============================================================================
+*/
- if (CAN_CHANGE(element))
- {
- for (i = 0; i < element_info[element].num_change_pages; i++)
- {
- content = element_info[element].change_page[i].target_element;
- is_player = ELEM_IS_PLAYER(content);
+static void InitGameEngine()
+{
+ int i, j, k, l, x, y;
- if (is_player && (found_rating < 3 || element < found_element))
- {
- start_x = x;
- start_y = y;
+ /* set game engine from tape file when re-playing, else from level file */
+ game.engine_version = (tape.playing ? tape.engine_version :
+ level.game_version);
- found_rating = 3;
- found_element = element;
- }
- }
- }
+ /* ---------------------------------------------------------------------- */
+ /* set flags for bugs and changes according to active game engine version */
+ /* ---------------------------------------------------------------------- */
- for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
- {
- content = element_info[element].content[xx][yy];
- is_player = ELEM_IS_PLAYER(content);
+ /*
+ Summary of bugfix/change:
+ Fixed handling for custom elements that change when pushed by the player.
- if (is_player && (found_rating < 2 || element < found_element))
- {
- start_x = x + xx - 1;
- start_y = y + yy - 1;
+ Fixed/changed in version:
+ 3.1.0
- found_rating = 2;
- found_element = element;
- }
+ Description:
+ Before 3.1.0, custom elements that "change when pushing" changed directly
+ after the player started pushing them (until then handled in "DigField()").
+ Since 3.1.0, these custom elements are not changed until the "pushing"
+ move of the element is finished (now handled in "ContinueMoving()").
- if (!CAN_CHANGE(element))
- continue;
+ Affected levels/tapes:
+ The first condition is generally needed for all levels/tapes before version
+ 3.1.0, which might use the old behaviour before it was changed; known tapes
+ that are affected are some tapes from the level set "Walpurgis Gardens" by
+ Jamie Cullen.
+ The second condition is an exception from the above case and is needed for
+ the special case of tapes recorded with game (not engine!) version 3.1.0 or
+ above (including some development versions of 3.1.0), but before it was
+ known that this change would break tapes like the above and was fixed in
+ 3.1.1, so that the changed behaviour was active although the engine version
+ while recording maybe was before 3.1.0. There is at least one tape that is
+ affected by this exception, which is the tape for the one-level set "Bug
+ Machine" by Juergen Bonhagen.
+ */
- for (i = 0; i < element_info[element].num_change_pages; i++)
- {
- content= element_info[element].change_page[i].target_content[xx][yy];
- is_player = ELEM_IS_PLAYER(content);
+ game.use_change_when_pushing_bug =
+ (game.engine_version < VERSION_IDENT(3,1,0,0) &&
+ !(tape.playing &&
+ tape.game_version >= VERSION_IDENT(3,1,0,0) &&
+ tape.game_version < VERSION_IDENT(3,1,1,0)));
- if (is_player && (found_rating < 1 || element < found_element))
- {
- start_x = x + xx - 1;
- start_y = y + yy - 1;
+ /*
+ Summary of bugfix/change:
+ Fixed handling for blocking the field the player leaves when moving.
+
+ Fixed/changed in version:
+ 3.1.1
+
+ Description:
+ Before 3.1.1, when "block last field when moving" was enabled, the field
+ the player is leaving when moving was blocked for the time of the move,
+ and was directly unblocked afterwards. This resulted in the last field
+ being blocked for exactly one less than the number of frames of one player
+ move. Additionally, even when blocking was disabled, the last field was
+ blocked for exactly one frame.
+ Since 3.1.1, due to changes in player movement handling, the last field
+ is not blocked at all when blocking is disabled. When blocking is enabled,
+ the last field is blocked for exactly the number of frames of one player
+ move. Additionally, if the player is Murphy, the hero of Supaplex, the
+ last field is blocked for exactly one more than the number of frames of
+ one player move.
- found_rating = 1;
- found_element = element;
- }
- }
- }
- }
+ Affected levels/tapes:
+ (!!! yet to be determined -- probably many !!!)
+ */
- scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
- start_x > SBX_Right + MIDPOSX ? SBX_Right :
- start_x - MIDPOSX);
+ game.use_block_last_field_bug =
+ (game.engine_version < VERSION_IDENT(3,1,1,0));
- scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
- start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
- start_y - MIDPOSY);
- }
- else
- {
-#if 1
- scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
- local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
- local_player->jx - MIDPOSX);
+ /*
+ Summary of bugfix/change:
+ Changed behaviour of CE changes with multiple changes per single frame.
+
+ Fixed/changed in version:
+ 3.2.0-6
+
+ Description:
+ Before 3.2.0-6, only one single CE change was allowed in each engine frame.
+ This resulted in race conditions where CEs seem to behave strange in some
+ situations (where triggered CE changes were just skipped because there was
+ already a CE change on that tile in the playfield in that engine frame).
+ Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
+ (The number of changes per frame must be limited in any case, because else
+ it is easily possible to define CE changes that would result in an infinite
+ loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
+ should be set large enough so that it would only be reached in cases where
+ the corresponding CE change conditions run into a loop. Therefore, it seems
+ to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
+ maximal number of change pages for custom elements.)
- scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
- local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
- local_player->jy - MIDPOSY);
+ Affected levels/tapes:
+ Probably many.
+ */
+
+#if USE_ONLY_ONE_CHANGE_PER_FRAME
+ game.max_num_changes_per_frame = 1;
#else
- scroll_x = SBX_Left;
- scroll_y = SBY_Upper;
- if (local_player->jx >= SBX_Left + MIDPOSX)
- scroll_x = (local_player->jx <= SBX_Right + MIDPOSX ?
- local_player->jx - MIDPOSX :
- SBX_Right);
- if (local_player->jy >= SBY_Upper + MIDPOSY)
- scroll_y = (local_player->jy <= SBY_Lower + MIDPOSY ?
- local_player->jy - MIDPOSY :
- SBY_Lower);
+ game.max_num_changes_per_frame =
+ (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
#endif
- }
-
- CloseDoor(DOOR_CLOSE_1);
- DrawLevel();
- DrawAllPlayers();
+ /* ---------------------------------------------------------------------- */
- /* after drawing the level, correct some elements */
- if (game.timegate_time_left == 0)
- CloseAllOpenTimegates();
+ /* default scan direction: scan playfield from top/left to bottom/right */
+ InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
- if (setup.soft_scrolling)
- BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
+ /* dynamically adjust element properties according to game engine version */
+ InitElementPropertiesEngine(game.engine_version);
- redraw_mask |= REDRAW_FROM_BACKBUFFER;
- FadeToFront();
+#if 0
+ printf("level %d: level version == %06d\n", level_nr, level.game_version);
+ printf(" tape version == %06d [%s] [file: %06d]\n",
+ tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
+ tape.file_version);
+ printf(" => game.engine_version == %06d\n", game.engine_version);
+#endif
- /* copy default game door content to main double buffer */
- BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
- DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
+ /* ---------- initialize player's initial move delay --------------------- */
- DrawGameDoorValues();
+ /* dynamically adjust player properties according to level information */
+ for (i = 0; i < MAX_PLAYERS; i++)
+ game.initial_move_delay_value[i] =
+ get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
- UnmapGameButtons();
- UnmapTapeButtons();
- game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
- game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
- game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
- MapGameButtons();
- MapTapeButtons();
+ /* dynamically adjust player properties according to game engine version */
+ for (i = 0; i < MAX_PLAYERS; i++)
+ game.initial_move_delay[i] =
+ (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
+ game.initial_move_delay_value[i] : 0);
- /* copy actual game door content to door double buffer for OpenDoor() */
- BlitBitmap(drawto, bitmap_db_door,
- DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
+ /* ---------- initialize player's initial push delay --------------------- */
- OpenDoor(DOOR_OPEN_ALL);
+ /* dynamically adjust player properties according to game engine version */
+ game.initial_push_delay_value =
+ (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
- PlaySoundStereo(SND_GAME_STARTING, SOUND_MIDDLE);
+ /* ---------- initialize changing elements ------------------------------- */
- if (setup.sound_music)
- PlayLevelMusic();
+ /* initialize changing elements information */
+ for (i = 0; i < MAX_NUM_ELEMENTS; i++)
+ {
+ struct ElementInfo *ei = &element_info[i];
- KeyboardAutoRepeatOffUnlessAutoplay();
+ /* this pointer might have been changed in the level editor */
+ ei->change = &ei->change_page[0];
- if (options.debug)
- {
- for (i = 0; i < MAX_PLAYERS; i++)
- printf("Player %d %sactive.\n",
- i + 1, (stored_player[i].active ? "" : "not "));
- }
+ if (!IS_CUSTOM_ELEMENT(i))
+ {
+ ei->change->target_element = EL_EMPTY_SPACE;
+ ei->change->delay_fixed = 0;
+ ei->change->delay_random = 0;
+ ei->change->delay_frames = 1;
+ }
-#if 0
- printf("::: starting game [%d]\n", FrameCounter);
-#endif
-}
+ for (j = 0; j < NUM_CHANGE_EVENTS; j++)
+ {
+ ei->has_change_event[j] = FALSE;
-void InitMovDir(int x, int y)
-{
- int i, element = Feld[x][y];
- static int xy[4][2] =
- {
- { 0, +1 },
- { +1, 0 },
- { 0, -1 },
- { -1, 0 }
- };
- static int direction[3][4] =
- {
- { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
- { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
- { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
- };
+ ei->event_page_nr[j] = 0;
+ ei->event_page[j] = &ei->change_page[0];
+ }
+ }
- switch(element)
+ /* add changing elements from pre-defined list */
+ for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
{
- case EL_BUG_RIGHT:
- case EL_BUG_UP:
- case EL_BUG_LEFT:
- case EL_BUG_DOWN:
- Feld[x][y] = EL_BUG;
- MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
- break;
+ struct ChangingElementInfo *ch_delay = &change_delay_list[i];
+ struct ElementInfo *ei = &element_info[ch_delay->element];
- case EL_SPACESHIP_RIGHT:
- case EL_SPACESHIP_UP:
- case EL_SPACESHIP_LEFT:
- case EL_SPACESHIP_DOWN:
- Feld[x][y] = EL_SPACESHIP;
- MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
- break;
+ ei->change->target_element = ch_delay->target_element;
+ ei->change->delay_fixed = ch_delay->change_delay;
- case EL_BD_BUTTERFLY_RIGHT:
- case EL_BD_BUTTERFLY_UP:
- case EL_BD_BUTTERFLY_LEFT:
- case EL_BD_BUTTERFLY_DOWN:
- Feld[x][y] = EL_BD_BUTTERFLY;
- MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
- break;
+ ei->change->pre_change_function = ch_delay->pre_change_function;
+ ei->change->change_function = ch_delay->change_function;
+ ei->change->post_change_function = ch_delay->post_change_function;
- case EL_BD_FIREFLY_RIGHT:
- case EL_BD_FIREFLY_UP:
- case EL_BD_FIREFLY_LEFT:
- case EL_BD_FIREFLY_DOWN:
- Feld[x][y] = EL_BD_FIREFLY;
- MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
- break;
+ ei->change->can_change = TRUE;
+ ei->change->can_change_or_has_action = TRUE;
- case EL_PACMAN_RIGHT:
- case EL_PACMAN_UP:
- case EL_PACMAN_LEFT:
- case EL_PACMAN_DOWN:
- Feld[x][y] = EL_PACMAN;
- MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
- break;
+ ei->has_change_event[CE_DELAY] = TRUE;
- case EL_SP_SNIKSNAK:
- MovDir[x][y] = MV_UP;
- break;
+ SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
+ SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
+ }
- case EL_SP_ELECTRON:
- MovDir[x][y] = MV_LEFT;
- break;
+ /* ---------- initialize internal run-time variables --------------------- */
- case EL_MOLE_LEFT:
- case EL_MOLE_RIGHT:
- case EL_MOLE_UP:
- case EL_MOLE_DOWN:
- Feld[x][y] = EL_MOLE;
- MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
- break;
+ for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
+ {
+ struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
- default:
- if (IS_CUSTOM_ELEMENT(element))
- {
- struct ElementInfo *ei = &element_info[element];
- int move_direction_initial = ei->move_direction_initial;
- int move_pattern = ei->move_pattern;
+ for (j = 0; j < ei->num_change_pages; j++)
+ {
+ ei->change_page[j].can_change_or_has_action =
+ (ei->change_page[j].can_change |
+ ei->change_page[j].has_action);
+ }
+ }
- if (move_direction_initial == MV_START_PREVIOUS)
+ /* add change events from custom element configuration */
+ for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
+ {
+ struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
+
+ for (j = 0; j < ei->num_change_pages; j++)
+ {
+ if (!ei->change_page[j].can_change_or_has_action)
+ continue;
+
+ for (k = 0; k < NUM_CHANGE_EVENTS; k++)
+ {
+ /* only add event page for the first page found with this event */
+ if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
{
- if (MovDir[x][y] != MV_NO_MOVING)
- return;
+ ei->has_change_event[k] = TRUE;
- move_direction_initial = MV_START_AUTOMATIC;
+ ei->event_page_nr[k] = j;
+ ei->event_page[k] = &ei->change_page[j];
}
+ }
+ }
+ }
- if (move_direction_initial == MV_START_RANDOM)
- MovDir[x][y] = 1 << RND(4);
- else if (move_direction_initial & MV_ANY_DIRECTION)
- MovDir[x][y] = move_direction_initial;
- else if (move_pattern == MV_ALL_DIRECTIONS ||
- move_pattern == MV_TURNING_LEFT ||
- move_pattern == MV_TURNING_RIGHT ||
- move_pattern == MV_TURNING_LEFT_RIGHT ||
- move_pattern == MV_TURNING_RIGHT_LEFT ||
- move_pattern == MV_TURNING_RANDOM)
- MovDir[x][y] = 1 << RND(4);
- else if (move_pattern == MV_HORIZONTAL)
- MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
- else if (move_pattern == MV_VERTICAL)
- MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
- else if (move_pattern & MV_ANY_DIRECTION)
- MovDir[x][y] = element_info[element].move_pattern;
- else if (move_pattern == MV_ALONG_LEFT_SIDE ||
- move_pattern == MV_ALONG_RIGHT_SIDE)
- {
#if 1
- /* use random direction as default start direction */
- if (game.engine_version >= VERSION_IDENT(3,1,0,0))
- MovDir[x][y] = 1 << RND(4);
+ /* ---------- initialize reference elements in change conditions --------- */
+
+ for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
+ {
+ int element = EL_CUSTOM_START + i;
+ struct ElementInfo *ei = &element_info[element];
+
+ for (j = 0; j < ei->num_change_pages; j++)
+ {
+ int trigger_element = ei->change_page[j].initial_trigger_element;
+
+ if (trigger_element >= EL_PREV_CE_8 &&
+ trigger_element <= EL_NEXT_CE_8)
+ trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
+
+ ei->change_page[j].trigger_element = trigger_element;
+ }
+ }
#endif
- for (i = 0; i < NUM_DIRECTIONS; i++)
- {
- int x1 = x + xy[i][0];
- int y1 = y + xy[i][1];
+ /* ---------- initialize run-time trigger player and element ------------- */
- if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
- {
- if (move_pattern == MV_ALONG_RIGHT_SIDE)
- MovDir[x][y] = direction[0][i];
- else
- MovDir[x][y] = direction[1][i];
+ for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
+ {
+ struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
- break;
- }
- }
- }
- }
- else
- {
- MovDir[x][y] = 1 << RND(4);
+ for (j = 0; j < ei->num_change_pages; j++)
+ {
+ ei->change_page[j].actual_trigger_element = EL_EMPTY;
+ ei->change_page[j].actual_trigger_player = EL_EMPTY;
+ ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
+ ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
+ ei->change_page[j].actual_trigger_ce_value = 0;
+ ei->change_page[j].actual_trigger_ce_score = 0;
+ }
+ }
- if (element != EL_BUG &&
- element != EL_SPACESHIP &&
- element != EL_BD_BUTTERFLY &&
- element != EL_BD_FIREFLY)
- break;
+ /* ---------- initialize trigger events ---------------------------------- */
- for (i = 0; i < NUM_DIRECTIONS; i++)
- {
- int x1 = x + xy[i][0];
- int y1 = y + xy[i][1];
+ /* initialize trigger events information */
+ for (i = 0; i < MAX_NUM_ELEMENTS; i++)
+ for (j = 0; j < NUM_CHANGE_EVENTS; j++)
+ trigger_events[i][j] = FALSE;
- if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
+ /* add trigger events from element change event properties */
+ for (i = 0; i < MAX_NUM_ELEMENTS; i++)
+ {
+ struct ElementInfo *ei = &element_info[i];
+
+ for (j = 0; j < ei->num_change_pages; j++)
+ {
+ if (!ei->change_page[j].can_change_or_has_action)
+ continue;
+
+ if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
+ {
+ int trigger_element = ei->change_page[j].trigger_element;
+
+ for (k = 0; k < NUM_CHANGE_EVENTS; k++)
+ {
+ if (ei->change_page[j].has_event[k])
{
- if (element == EL_BUG || element == EL_BD_BUTTERFLY)
+ if (IS_GROUP_ELEMENT(trigger_element))
{
- MovDir[x][y] = direction[0][i];
- break;
- }
- else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
- element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
- {
- MovDir[x][y] = direction[1][i];
- break;
+ struct ElementGroupInfo *group =
+ element_info[trigger_element].group;
+
+ for (l = 0; l < group->num_elements_resolved; l++)
+ trigger_events[group->element_resolved[l]][k] = TRUE;
}
+ else if (trigger_element == EL_ANY_ELEMENT)
+ for (l = 0; l < MAX_NUM_ELEMENTS; l++)
+ trigger_events[l][k] = TRUE;
+ else
+ trigger_events[trigger_element][k] = TRUE;
}
}
}
- break;
+ }
}
- GfxDir[x][y] = MovDir[x][y];
-}
-
-void InitAmoebaNr(int x, int y)
-{
- int i;
- int group_nr = AmoebeNachbarNr(x, y);
+ /* ---------- initialize push delay -------------------------------------- */
- if (group_nr == 0)
+ /* initialize push delay values to default */
+ for (i = 0; i < MAX_NUM_ELEMENTS; i++)
{
- for (i = 1; i < MAX_NUM_AMOEBA; i++)
+ if (!IS_CUSTOM_ELEMENT(i))
{
- if (AmoebaCnt[i] == 0)
+ /* set default push delay values (corrected since version 3.0.7-1) */
+ if (game.engine_version < VERSION_IDENT(3,0,7,1))
{
- group_nr = i;
- break;
+ element_info[i].push_delay_fixed = 2;
+ element_info[i].push_delay_random = 8;
+ }
+ else
+ {
+ element_info[i].push_delay_fixed = 8;
+ element_info[i].push_delay_random = 8;
}
}
}
- AmoebaNr[x][y] = group_nr;
- AmoebaCnt[group_nr]++;
- AmoebaCnt2[group_nr]++;
-}
+ /* set push delay value for certain elements from pre-defined list */
+ for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
+ {
+ int e = push_delay_list[i].element;
-void GameWon()
-{
- int hi_pos;
- boolean raise_level = FALSE;
+ element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
+ element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
+ }
- if (local_player->MovPos)
- return;
+ /* set push delay value for Supaplex elements for newer engine versions */
+ if (game.engine_version >= VERSION_IDENT(3,1,0,0))
+ {
+ for (i = 0; i < MAX_NUM_ELEMENTS; i++)
+ {
+ if (IS_SP_ELEMENT(i))
+ {
+ /* set SP push delay to just enough to push under a falling zonk */
+ int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
-#if 1
- if (tape.auto_play) /* tape might already be stopped here */
- tape.auto_play_level_solved = TRUE;
-#else
- if (tape.playing && tape.auto_play)
- tape.auto_play_level_solved = TRUE;
-#endif
+ element_info[i].push_delay_fixed = delay;
+ element_info[i].push_delay_random = 0;
+ }
+ }
+ }
- local_player->LevelSolved = FALSE;
+ /* ---------- initialize move stepsize ----------------------------------- */
- PlaySoundStereo(SND_GAME_WINNING, SOUND_MIDDLE);
+ /* initialize move stepsize values to default */
+ for (i = 0; i < MAX_NUM_ELEMENTS; i++)
+ if (!IS_CUSTOM_ELEMENT(i))
+ element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
- if (TimeLeft)
+ /* set move stepsize value for certain elements from pre-defined list */
+ for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
{
- if (!tape.playing && setup.sound_loops)
- PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
- SND_CTRL_PLAY_LOOP);
-
- while (TimeLeft > 0)
- {
- if (!tape.playing && !setup.sound_loops)
- PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
- if (TimeLeft > 0 && !(TimeLeft % 10))
- RaiseScore(level.score[SC_TIME_BONUS]);
- if (TimeLeft > 100 && !(TimeLeft % 10))
- TimeLeft -= 10;
- else
- TimeLeft--;
+ int e = move_stepsize_list[i].element;
- DrawGameValue_Time(TimeLeft);
+ element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
+ }
- BackToFront();
+ /* ---------- initialize collect score ----------------------------------- */
- if (!tape.playing)
- Delay(10);
- }
+ /* initialize collect score values for custom elements from initial value */
+ for (i = 0; i < MAX_NUM_ELEMENTS; i++)
+ if (IS_CUSTOM_ELEMENT(i))
+ element_info[i].collect_score = element_info[i].collect_score_initial;
- if (!tape.playing && setup.sound_loops)
- StopSound(SND_GAME_LEVELTIME_BONUS);
- }
- else if (level.time == 0) /* level without time limit */
- {
- if (!tape.playing && setup.sound_loops)
- PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
- SND_CTRL_PLAY_LOOP);
+ /* ---------- initialize collect count ----------------------------------- */
- while (TimePlayed < 999)
- {
- if (!tape.playing && !setup.sound_loops)
- PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
- if (TimePlayed < 999 && !(TimePlayed % 10))
- RaiseScore(level.score[SC_TIME_BONUS]);
- if (TimePlayed < 900 && !(TimePlayed % 10))
- TimePlayed += 10;
- else
- TimePlayed++;
+ /* initialize collect count values for non-custom elements */
+ for (i = 0; i < MAX_NUM_ELEMENTS; i++)
+ if (!IS_CUSTOM_ELEMENT(i))
+ element_info[i].collect_count_initial = 0;
- DrawGameValue_Time(TimePlayed);
+ /* add collect count values for all elements from pre-defined list */
+ for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
+ element_info[collect_count_list[i].element].collect_count_initial =
+ collect_count_list[i].count;
- BackToFront();
+ /* ---------- initialize access direction -------------------------------- */
- if (!tape.playing)
- Delay(10);
- }
+ /* initialize access direction values to default (access from every side) */
+ for (i = 0; i < MAX_NUM_ELEMENTS; i++)
+ if (!IS_CUSTOM_ELEMENT(i))
+ element_info[i].access_direction = MV_ALL_DIRECTIONS;
- if (!tape.playing && setup.sound_loops)
- StopSound(SND_GAME_LEVELTIME_BONUS);
- }
+ /* set access direction value for certain elements from pre-defined list */
+ for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
+ element_info[access_direction_list[i].element].access_direction =
+ access_direction_list[i].direction;
- /* close exit door after last player */
- if ((Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
- Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN) && AllPlayersGone)
+ /* ---------- initialize explosion content ------------------------------- */
+ for (i = 0; i < MAX_NUM_ELEMENTS; i++)
{
- int element = Feld[ExitX][ExitY];
-
- Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
- EL_SP_EXIT_CLOSING);
+ if (IS_CUSTOM_ELEMENT(i))
+ continue;
- PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
+ for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
+ {
+ /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
+
+ element_info[i].content.e[x][y] =
+ (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
+ i == EL_PLAYER_2 ? EL_EMERALD_RED :
+ i == EL_PLAYER_3 ? EL_EMERALD :
+ i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
+ i == EL_MOLE ? EL_EMERALD_RED :
+ i == EL_PENGUIN ? EL_EMERALD_PURPLE :
+ i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
+ i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
+ i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
+ i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
+ i == EL_WALL_EMERALD ? EL_EMERALD :
+ i == EL_WALL_DIAMOND ? EL_DIAMOND :
+ i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
+ i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
+ i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
+ i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
+ i == EL_WALL_PEARL ? EL_PEARL :
+ i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
+ EL_EMPTY);
+ }
}
- /* Hero disappears */
- DrawLevelField(ExitX, ExitY);
- BackToFront();
+ /* ---------- initialize recursion detection ------------------------------ */
+ recursion_loop_depth = 0;
+ recursion_loop_detected = FALSE;
+ recursion_loop_element = EL_UNDEFINED;
- if (tape.playing)
- return;
+ /* ---------- initialize graphics engine ---------------------------------- */
+ game.scroll_delay_value =
+ (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
+ setup.scroll_delay ? setup.scroll_delay_value : 0);
+ game.scroll_delay_value =
+ MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
+}
- CloseDoor(DOOR_CLOSE_1);
+int get_num_special_action(int element, int action_first, int action_last)
+{
+ int num_special_action = 0;
+ int i, j;
- if (tape.recording)
+ for (i = action_first; i <= action_last; i++)
{
- TapeStop();
- SaveTape(tape.level_nr); /* Ask to save tape */
- }
+ boolean found = FALSE;
- if (level_nr == leveldir_current->handicap_level)
- {
- leveldir_current->handicap_level++;
- SaveLevelSetup_SeriesInfo();
+ for (j = 0; j < NUM_DIRECTIONS; j++)
+ if (el_act_dir2img(element, i, j) !=
+ el_act_dir2img(element, ACTION_DEFAULT, j))
+ found = TRUE;
+
+ if (found)
+ num_special_action++;
+ else
+ break;
}
- if (level_editor_test_game)
- local_player->score = -1; /* no highscore when playing from editor */
- else if (level_nr < leveldir_current->last_level)
- raise_level = TRUE; /* advance to next level */
+ return num_special_action;
+}
- if ((hi_pos = NewHiScore()) >= 0)
- {
- game_status = GAME_MODE_SCORES;
- DrawHallOfFame(hi_pos);
- if (raise_level)
- {
- level_nr++;
- TapeErase();
- }
- }
- else
- {
- game_status = GAME_MODE_MAIN;
- if (raise_level)
- {
- level_nr++;
- TapeErase();
- }
- DrawMainMenu();
- }
- BackToFront();
-}
+/*
+ =============================================================================
+ InitGame()
+ -----------------------------------------------------------------------------
+ initialize and start new game
+ =============================================================================
+*/
-int NewHiScore()
+void InitGame()
{
- int k, l;
- int position = -1;
+ boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
+ boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
+ boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
+#if 0
+ boolean do_fading = (game_status == GAME_MODE_MAIN);
+#endif
+#if 1
+ int initial_move_dir = MV_DOWN;
+#else
+ int initial_move_dir = MV_NONE;
+#endif
+ int i, j, x, y;
- LoadScore(level_nr);
+ game_status = GAME_MODE_PLAYING;
- if (strcmp(setup.player_name, EMPTY_PLAYER_NAME) == 0 ||
- local_player->score < highscore[MAX_SCORE_ENTRIES - 1].Score)
- return -1;
+#if 1
+ /* needed if different viewport properties defined for playing */
+ ChangeViewportPropertiesIfNeeded();
+#endif
- for (k = 0; k < MAX_SCORE_ENTRIES; k++)
+#if 1
+ DrawCompleteVideoDisplay();
+#endif
+
+ InitGameEngine();
+ InitGameControlValues();
+
+ /* don't play tapes over network */
+ network_playing = (options.network && !tape.playing);
+
+ for (i = 0; i < MAX_PLAYERS; i++)
{
- if (local_player->score > highscore[k].Score)
- {
- /* player has made it to the hall of fame */
+ struct PlayerInfo *player = &stored_player[i];
- if (k < MAX_SCORE_ENTRIES - 1)
- {
- int m = MAX_SCORE_ENTRIES - 1;
+ player->index_nr = i;
+ player->index_bit = (1 << i);
+ player->element_nr = EL_PLAYER_1 + i;
-#ifdef ONE_PER_NAME
- for (l = k; l < MAX_SCORE_ENTRIES; l++)
- if (!strcmp(setup.player_name, highscore[l].Name))
- m = l;
- if (m == k) /* player's new highscore overwrites his old one */
- goto put_into_list;
-#endif
+ player->present = FALSE;
+ player->active = FALSE;
+ player->mapped = FALSE;
- for (l = m; l > k; l--)
- {
- strcpy(highscore[l].Name, highscore[l - 1].Name);
- highscore[l].Score = highscore[l - 1].Score;
- }
- }
+ player->killed = FALSE;
+ player->reanimated = FALSE;
-#ifdef ONE_PER_NAME
- put_into_list:
-#endif
- strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
- highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
- highscore[k].Score = local_player->score;
- position = k;
- break;
- }
+ player->action = 0;
+ player->effective_action = 0;
+ player->programmed_action = 0;
-#ifdef ONE_PER_NAME
- else if (!strncmp(setup.player_name, highscore[k].Name,
- MAX_PLAYER_NAME_LEN))
- break; /* player already there with a higher score */
-#endif
+ player->score = 0;
+ player->score_final = 0;
- }
+ player->gems_still_needed = level.gems_needed;
+ player->sokobanfields_still_needed = 0;
+ player->lights_still_needed = 0;
+ player->friends_still_needed = 0;
- if (position >= 0)
- SaveScore(level_nr);
+ for (j = 0; j < MAX_NUM_KEYS; j++)
+ player->key[j] = FALSE;
- return position;
-}
+ player->num_white_keys = 0;
-void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
-{
- if (player->GfxAction != action || player->GfxDir != dir)
- {
-#if 0
- printf("Player frame reset! (%d => %d, %d => %d)\n",
- player->GfxAction, action, player->GfxDir, dir);
-#endif
+ player->dynabomb_count = 0;
+ player->dynabomb_size = 1;
+ player->dynabombs_left = 0;
+ player->dynabomb_xl = FALSE;
- player->GfxAction = action;
- player->GfxDir = dir;
+ player->MovDir = initial_move_dir;
+ player->MovPos = 0;
+ player->GfxPos = 0;
+ player->GfxDir = initial_move_dir;
+ player->GfxAction = ACTION_DEFAULT;
player->Frame = 0;
player->StepFrame = 0;
- }
-}
-static void ResetRandomAnimationValue(int x, int y)
-{
- GfxRandom[x][y] = INIT_GFX_RANDOM();
-}
+ player->initial_element = player->element_nr;
+ player->artwork_element =
+ (level.use_artwork_element[i] ? level.artwork_element[i] :
+ player->element_nr);
+ player->use_murphy = FALSE;
-static void ResetGfxAnimation(int x, int y)
-{
- GfxFrame[x][y] = 0;
- GfxAction[x][y] = ACTION_DEFAULT;
- GfxDir[x][y] = MovDir[x][y];
-}
+ player->block_last_field = FALSE; /* initialized in InitPlayerField() */
+ player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
-void InitMovingField(int x, int y, int direction)
-{
- int element = Feld[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 newx = x + dx;
- int newy = y + dy;
+ player->gravity = level.initial_player_gravity[i];
- if (!WasJustMoving[x][y] || direction != MovDir[x][y])
- ResetGfxAnimation(x, y);
+ player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
- MovDir[newx][newy] = MovDir[x][y] = direction;
- GfxDir[x][y] = direction;
+ player->actual_frame_counter = 0;
- if (Feld[newx][newy] == EL_EMPTY)
- Feld[newx][newy] = EL_BLOCKED;
+ player->step_counter = 0;
- if (direction == MV_DOWN && CAN_FALL(element))
- GfxAction[x][y] = ACTION_FALLING;
- else
- GfxAction[x][y] = ACTION_MOVING;
+ player->last_move_dir = initial_move_dir;
- GfxFrame[newx][newy] = GfxFrame[x][y];
- GfxRandom[newx][newy] = GfxRandom[x][y];
- GfxAction[newx][newy] = GfxAction[x][y];
- GfxDir[newx][newy] = GfxDir[x][y];
-}
+ player->is_active = FALSE;
-void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
-{
- int direction = MovDir[x][y];
- int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
- int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
+ player->is_waiting = FALSE;
+ player->is_moving = FALSE;
+ player->is_auto_moving = FALSE;
+ player->is_digging = FALSE;
+ player->is_snapping = FALSE;
+ player->is_collecting = FALSE;
+ player->is_pushing = FALSE;
+ player->is_switching = FALSE;
+ player->is_dropping = FALSE;
+ player->is_dropping_pressed = FALSE;
- *goes_to_x = newx;
- *goes_to_y = newy;
-}
+ player->is_bored = FALSE;
+ player->is_sleeping = FALSE;
-void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
-{
- int oldx = x, oldy = y;
- int direction = MovDir[x][y];
+ player->frame_counter_bored = -1;
+ player->frame_counter_sleeping = -1;
- if (direction == MV_LEFT)
- oldx++;
- else if (direction == MV_RIGHT)
- oldx--;
- else if (direction == MV_UP)
- oldy++;
- else if (direction == MV_DOWN)
- oldy--;
+ player->anim_delay_counter = 0;
+ player->post_delay_counter = 0;
- *comes_from_x = oldx;
- *comes_from_y = oldy;
-}
+ player->dir_waiting = initial_move_dir;
+ player->action_waiting = ACTION_DEFAULT;
+ player->last_action_waiting = ACTION_DEFAULT;
+ player->special_action_bored = ACTION_DEFAULT;
+ player->special_action_sleeping = ACTION_DEFAULT;
-int MovingOrBlocked2Element(int x, int y)
-{
- int element = Feld[x][y];
+ player->switch_x = -1;
+ player->switch_y = -1;
- if (element == EL_BLOCKED)
- {
- int oldx, oldy;
+ player->drop_x = -1;
+ player->drop_y = -1;
- Blocked2Moving(x, y, &oldx, &oldy);
- return Feld[oldx][oldy];
- }
- else
- return element;
-}
+ player->show_envelope = 0;
-static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
-{
- /* like MovingOrBlocked2Element(), but if element is moving
- and (x,y) is the field the moving element is just leaving,
- return EL_BLOCKED instead of the element value */
- int element = Feld[x][y];
+ SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
- if (IS_MOVING(x, y))
- {
- if (element == EL_BLOCKED)
- {
- int oldx, oldy;
+ player->push_delay = -1; /* initialized when pushing starts */
+ player->push_delay_value = game.initial_push_delay_value;
- Blocked2Moving(x, y, &oldx, &oldy);
- return Feld[oldx][oldy];
- }
- else
- return EL_BLOCKED;
- }
- else
- return element;
-}
+ player->drop_delay = 0;
+ player->drop_pressed_delay = 0;
-static void RemoveField(int x, int y)
-{
- Feld[x][y] = EL_EMPTY;
+ player->last_jx = -1;
+ player->last_jy = -1;
+ player->jx = -1;
+ player->jy = -1;
- MovPos[x][y] = 0;
- MovDir[x][y] = 0;
- MovDelay[x][y] = 0;
+ player->shield_normal_time_left = 0;
+ player->shield_deadly_time_left = 0;
- AmoebaNr[x][y] = 0;
- ChangeDelay[x][y] = 0;
- ChangePage[x][y] = -1;
- Pushed[x][y] = FALSE;
+ player->inventory_infinite_element = EL_UNDEFINED;
+ player->inventory_size = 0;
-#if 0
- ExplodeField[x][y] = EX_TYPE_NONE;
-#endif
+ if (level.use_initial_inventory[i])
+ {
+ for (j = 0; j < level.initial_inventory_size[i]; j++)
+ {
+ int element = level.initial_inventory_content[i][j];
+ int collect_count = element_info[element].collect_count_initial;
+ int k;
- GfxElement[x][y] = EL_UNDEFINED;
- GfxAction[x][y] = ACTION_DEFAULT;
- GfxDir[x][y] = MV_NO_MOVING;
-}
+ if (!IS_CUSTOM_ELEMENT(element))
+ collect_count = 1;
-void RemoveMovingField(int x, int y)
-{
- int oldx = x, oldy = y, newx = x, newy = y;
- int element = Feld[x][y];
- int next_element = EL_UNDEFINED;
+ if (collect_count == 0)
+ player->inventory_infinite_element = element;
+ else
+ for (k = 0; k < collect_count; k++)
+ if (player->inventory_size < MAX_INVENTORY_SIZE)
+ player->inventory_element[player->inventory_size++] = element;
+ }
+ }
- if (element != EL_BLOCKED && !IS_MOVING(x, y))
- return;
+ DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
+ SnapField(player, 0, 0);
- if (IS_MOVING(x, y))
- {
- Moving2Blocked(x, y, &newx, &newy);
-#if 0
- if (Feld[newx][newy] != EL_BLOCKED)
- return;
-#else
- if (Feld[newx][newy] != EL_BLOCKED)
- {
- /* element is moving, but target field is not free (blocked), but
- already occupied by something different (example: acid pool);
- in this case, only remove the moving field, but not the target */
+ player->LevelSolved = FALSE;
+ player->GameOver = FALSE;
- RemoveField(oldx, oldy);
+ player->LevelSolved_GameWon = FALSE;
+ player->LevelSolved_GameEnd = FALSE;
+ player->LevelSolved_PanelOff = FALSE;
+ player->LevelSolved_SaveTape = FALSE;
+ player->LevelSolved_SaveScore = FALSE;
+ player->LevelSolved_CountingTime = 0;
+ player->LevelSolved_CountingScore = 0;
- Store[oldx][oldy] = Store2[oldx][oldy] = 0;
+ map_player_action[i] = i;
+ }
- DrawLevelField(oldx, oldy);
+ network_player_action_received = FALSE;
- return;
- }
+#if defined(NETWORK_AVALIABLE)
+ /* initial null action */
+ if (network_playing)
+ SendToServer_MovePlayer(MV_NONE);
#endif
- }
- else if (element == EL_BLOCKED)
- {
- Blocked2Moving(x, y, &oldx, &oldy);
- if (!IS_MOVING(oldx, oldy))
- return;
- }
- if (element == EL_BLOCKED &&
- (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
- Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
- Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
- Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
- next_element = get_next_element(Feld[oldx][oldy]);
+ ZX = ZY = -1;
+ ExitX = ExitY = -1;
- RemoveField(oldx, oldy);
- RemoveField(newx, newy);
+ FrameCounter = 0;
+ TimeFrames = 0;
+ TimePlayed = 0;
+ TimeLeft = level.time;
+ TapeTime = 0;
- Store[oldx][oldy] = Store2[oldx][oldy] = 0;
+ ScreenMovDir = MV_NONE;
+ ScreenMovPos = 0;
+ ScreenGfxPos = 0;
- if (next_element != EL_UNDEFINED)
- Feld[oldx][oldy] = next_element;
+ ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
- DrawLevelField(oldx, oldy);
- DrawLevelField(newx, newy);
-}
+ AllPlayersGone = FALSE;
-void DrawDynamite(int x, int y)
-{
- int sx = SCREENX(x), sy = SCREENY(y);
- int graphic = el2img(Feld[x][y]);
- int frame;
+ game.yamyam_content_nr = 0;
+ game.robot_wheel_active = FALSE;
+ game.magic_wall_active = FALSE;
+ game.magic_wall_time_left = 0;
+ game.light_time_left = 0;
+ game.timegate_time_left = 0;
+ game.switchgate_pos = 0;
+ game.wind_direction = level.wind_direction_initial;
- if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
- return;
+#if !USE_PLAYER_GRAVITY
+ game.gravity = FALSE;
+ game.explosions_delayed = TRUE;
+#endif
- if (IS_WALKABLE_INSIDE(Back[x][y]))
- return;
+ game.lenses_time_left = 0;
+ game.magnify_time_left = 0;
- if (Back[x][y])
- DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
- else if (Store[x][y])
- DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
+ game.ball_state = level.ball_state_initial;
+ game.ball_content_nr = 0;
- frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
+ game.envelope_active = FALSE;
-#if 1
- if (Back[x][y] || Store[x][y])
- DrawGraphicThruMask(sx, sy, graphic, frame);
- else
- DrawGraphic(sx, sy, graphic, frame);
-#else
- if (game.emulation == EMU_SUPAPLEX)
- DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
- else if (Store[x][y])
- DrawGraphicThruMask(sx, sy, graphic, frame);
- else
- DrawGraphic(sx, sy, graphic, frame);
-#endif
-}
+ /* set focus to local player for network games, else to all players */
+ game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
+ game.centered_player_nr_next = game.centered_player_nr;
+ game.set_centered_player = FALSE;
-void CheckDynamite(int x, int y)
-{
- if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
+ if (network_playing && tape.recording)
{
- MovDelay[x][y]--;
+ /* store client dependent player focus when recording network games */
+ tape.centered_player_nr_next = game.centered_player_nr_next;
+ tape.set_centered_player = TRUE;
+ }
- if (MovDelay[x][y] != 0)
- {
- DrawDynamite(x, y);
- PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
+ for (i = 0; i < NUM_BELTS; i++)
+ {
+ game.belt_dir[i] = MV_NONE;
+ game.belt_dir_nr[i] = 3; /* not moving, next moving left */
+ }
- return;
- }
+ for (i = 0; i < MAX_NUM_AMOEBA; i++)
+ AmoebaCnt[i] = AmoebaCnt2[i] = 0;
+
+ SCAN_PLAYFIELD(x, y)
+ {
+ Feld[x][y] = level.field[x][y];
+ MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
+ ChangeDelay[x][y] = 0;
+ ChangePage[x][y] = -1;
+#if USE_NEW_CUSTOM_VALUE
+ CustomValue[x][y] = 0; /* initialized in InitField() */
+#endif
+ Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
+ AmoebaNr[x][y] = 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;
+
+ ChangeCount[x][y] = 0;
+ ChangeEvent[x][y] = -1;
+
+ ExplodePhase[x][y] = 0;
+ ExplodeDelay[x][y] = 0;
+ ExplodeField[x][y] = EX_TYPE_NONE;
+
+ RunnerVisit[x][y] = 0;
+ PlayerVisit[x][y] = 0;
+
+ GfxFrame[x][y] = 0;
+ GfxRandom[x][y] = INIT_GFX_RANDOM();
+ GfxElement[x][y] = EL_UNDEFINED;
+ GfxAction[x][y] = ACTION_DEFAULT;
+ GfxDir[x][y] = MV_NONE;
+ GfxRedraw[x][y] = GFX_REDRAW_NONE;
}
-#if 1
- StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
-#else
- if (Feld[x][y] == EL_DYNAMITE_ACTIVE ||
- Feld[x][y] == EL_SP_DISK_RED_ACTIVE)
- StopSound(SND_DYNAMITE_ACTIVE);
- else
- StopSound(SND_DYNABOMB_ACTIVE);
-#endif
+ SCAN_PLAYFIELD(x, y)
+ {
+ if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
+ emulate_bd = FALSE;
+ if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
+ emulate_sb = FALSE;
+ if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
+ emulate_sp = FALSE;
- Bang(x, y);
-}
+ InitField(x, y, TRUE);
-void DrawRelocatePlayer(struct PlayerInfo *player)
-{
- boolean ffwd_delay = (tape.playing && tape.fast_forward);
- boolean no_delay = (tape.warp_forward);
- int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
- int wait_delay_value = (no_delay ? 0 : frame_delay_value);
- int jx = player->jx;
- int jy = player->jy;
+ ResetGfxAnimation(x, y);
+ }
+
+ InitBeltMovement();
- if (level.instant_relocation)
+ for (i = 0; i < MAX_PLAYERS; i++)
{
-#if 1
- int offset = (setup.scroll_delay ? 3 : 0);
+ struct PlayerInfo *player = &stored_player[i];
- if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
+ /* set number of special actions for bored and sleeping animation */
+ player->num_special_action_bored =
+ get_num_special_action(player->artwork_element,
+ ACTION_BORING_1, ACTION_BORING_LAST);
+ player->num_special_action_sleeping =
+ get_num_special_action(player->artwork_element,
+ ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
+ }
+
+ game.emulation = (emulate_bd ? EMU_BOULDERDASH :
+ emulate_sb ? EMU_SOKOBAN :
+ emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
+
+#if USE_NEW_ALL_SLIPPERY
+ /* initialize type of slippery elements */
+ for (i = 0; i < MAX_NUM_ELEMENTS; i++)
+ {
+ if (!IS_CUSTOM_ELEMENT(i))
{
- scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
- local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
- local_player->jx - MIDPOSX);
+ /* default: elements slip down either to the left or right randomly */
+ element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
- scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
- local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
- local_player->jy - MIDPOSY);
+ /* SP style elements prefer to slip down on the left side */
+ if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
+ element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
+
+ /* BD style elements prefer to slip down on the left side */
+ if (game.emulation == EMU_BOULDERDASH)
+ element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
}
- else
- {
- if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
- (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
- scroll_x = jx - MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
+ }
+#endif
- if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
- (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
- scroll_y = jy - MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
+ /* initialize explosion and ignition delay */
+ for (i = 0; i < MAX_NUM_ELEMENTS; i++)
+ {
+ if (!IS_CUSTOM_ELEMENT(i))
+ {
+ int num_phase = 8;
+ int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
+ game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
+ game.emulation == EMU_SUPAPLEX ? 3 : 2);
+ int last_phase = (num_phase + 1) * delay;
+ int half_phase = (num_phase / 2) * delay;
- /* don't scroll over playfield boundaries */
- if (scroll_x < SBX_Left || scroll_x > SBX_Right)
- scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
+ element_info[i].explosion_delay = last_phase - 1;
+ element_info[i].ignition_delay = half_phase;
- /* don't scroll over playfield boundaries */
- if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
- scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
+ if (i == EL_BLACK_ORB)
+ element_info[i].ignition_delay = 1;
}
-#else
- scroll_x += (local_player->jx - old_jx);
- scroll_y += (local_player->jy - old_jy);
- /* don't scroll over playfield boundaries */
- if (scroll_x < SBX_Left || scroll_x > SBX_Right)
- scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
+#if 0
+ if (element_info[i].explosion_delay < 1) /* !!! check again !!! */
+ element_info[i].explosion_delay = 1;
- /* don't scroll over playfield boundaries */
- if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
- scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
+ if (element_info[i].ignition_delay < 1) /* !!! check again !!! */
+ element_info[i].ignition_delay = 1;
#endif
+ }
- RedrawPlayfield(TRUE, 0,0,0,0);
+ /* correct non-moving belts to start moving left */
+ for (i = 0; i < NUM_BELTS; i++)
+ if (game.belt_dir[i] == MV_NONE)
+ game.belt_dir_nr[i] = 3; /* not moving, next moving left */
+
+#if USE_NEW_PLAYER_ASSIGNMENTS
+ /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
+ /* choose default local player */
+ local_player = &stored_player[0];
+
+ for (i = 0; i < MAX_PLAYERS; i++)
+ stored_player[i].connected = FALSE;
+
+ local_player->connected = TRUE;
+ /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
+
+ if (tape.playing)
+ {
+ /* try to guess locally connected team mode players (needed for correct
+ assignment of player figures from level to locally playing players) */
+
+ for (i = 0; i < MAX_PLAYERS; i++)
+ if (tape.player_participates[i])
+ stored_player[i].connected = TRUE;
}
- else
+ else if (setup.team_mode && !options.network)
{
-#if 1
+ /* try to guess locally connected team mode players (needed for correct
+ assignment of player figures from level to locally playing players) */
+
+ for (i = 0; i < MAX_PLAYERS; i++)
+ if (setup.input[i].use_joystick ||
+ setup.input[i].key.left != KSYM_UNDEFINED)
+ stored_player[i].connected = TRUE;
+ }
+
#if 0
- int offset = (setup.scroll_delay ? 3 : 0);
+ for (i = 0; i < MAX_PLAYERS; i++)
+ printf("::: player %d: %s\n", i,
+ (stored_player[i].connected ? "connected" : "not connected"));
+
+ for (i = 0; i < MAX_PLAYERS; i++)
+ printf("::: player %d: %s\n", i,
+ (stored_player[i].present ? "present" : "not present"));
#endif
- int scroll_xx = -999, scroll_yy = -999;
- ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
+ /* check if any connected player was not found in playfield */
+ for (i = 0; i < MAX_PLAYERS; i++)
+ {
+ struct PlayerInfo *player = &stored_player[i];
- while (scroll_xx != scroll_x || scroll_yy != scroll_y)
+ if (player->connected && !player->present)
{
- int dx = 0, dy = 0;
- int fx = FX, fy = FY;
+ struct PlayerInfo *field_player = NULL;
- scroll_xx = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
- local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
- local_player->jx - MIDPOSX);
+#if 0
+ printf("::: looking for field player for player %d ...\n", i);
+#endif
- scroll_yy = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
- local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
- local_player->jy - MIDPOSY);
+ /* assign first free player found that is present in the playfield */
- dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
- dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
+ /* first try: look for unmapped playfield player that is not connected */
+ if (field_player == NULL)
+ for (j = 0; j < MAX_PLAYERS; j++)
+ if (stored_player[j].present &&
+ !stored_player[j].mapped &&
+ !stored_player[j].connected)
+ field_player = &stored_player[j];
-#if 1
- if (dx == 0 && dy == 0) /* no scrolling needed at all */
- break;
-#else
- if (scroll_xx == scroll_x && scroll_yy == scroll_y)
- break;
-#endif
+ /* second try: look for *any* unmapped playfield player */
+ if (field_player == NULL)
+ for (j = 0; j < MAX_PLAYERS; j++)
+ if (stored_player[j].present &&
+ !stored_player[j].mapped)
+ field_player = &stored_player[j];
- scroll_x -= dx;
- scroll_y -= dy;
+ if (field_player != NULL)
+ {
+ int jx = field_player->jx, jy = field_player->jy;
- fx += dx * TILEX / 2;
- fy += dy * TILEY / 2;
+#if 0
+ printf("::: found player figure %d\n", field_player->index_nr);
+#endif
- ScrollLevel(dx, dy);
- DrawAllPlayers();
+ player->present = FALSE;
+ player->active = FALSE;
- /* scroll in two steps of half tile size to make things smoother */
- BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
- FlushDisplay();
- Delay(wait_delay_value);
+ field_player->present = TRUE;
+ field_player->active = TRUE;
- /* scroll second step to align at full tile size */
- BackToFront();
- Delay(wait_delay_value);
- }
-#else
- int scroll_xx = -999, scroll_yy = -999;
+ /*
+ player->initial_element = field_player->initial_element;
+ player->artwork_element = field_player->artwork_element;
- ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
+ player->block_last_field = field_player->block_last_field;
+ player->block_delay_adjustment = field_player->block_delay_adjustment;
+ */
- while (scroll_xx != scroll_x || scroll_yy != scroll_y)
- {
- int dx = 0, dy = 0;
- int fx = FX, fy = FY;
+ StorePlayer[jx][jy] = field_player->element_nr;
- scroll_xx = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
- local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
- local_player->jx - MIDPOSX);
+ field_player->jx = field_player->last_jx = jx;
+ field_player->jy = field_player->last_jy = jy;
- scroll_yy = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
- local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
- local_player->jy - MIDPOSY);
+ if (local_player == player)
+ local_player = field_player;
- dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
- dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
+ map_player_action[field_player->index_nr] = i;
-#if 1
- if (dx == 0 && dy == 0) /* no scrolling needed at all */
- break;
-#else
- if (scroll_xx == scroll_x && scroll_yy == scroll_y)
- break;
+ field_player->mapped = TRUE;
+
+#if 0
+ printf("::: map_player_action[%d] == %d\n",
+ field_player->index_nr, i);
#endif
+ }
+ }
- scroll_x -= dx;
- scroll_y -= dy;
+ if (player->connected && player->present)
+ player->mapped = TRUE;
+ }
- fx += dx * TILEX / 2;
- fy += dy * TILEY / 2;
+#else
- ScrollLevel(dx, dy);
- DrawAllPlayers();
+ /* check if any connected player was not found in playfield */
+ for (i = 0; i < MAX_PLAYERS; i++)
+ {
+ struct PlayerInfo *player = &stored_player[i];
- /* scroll in two steps of half tile size to make things smoother */
- BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
- FlushDisplay();
- Delay(wait_delay_value);
+ if (player->connected && !player->present)
+ {
+ for (j = 0; j < MAX_PLAYERS; j++)
+ {
+ struct PlayerInfo *field_player = &stored_player[j];
+ int jx = field_player->jx, jy = field_player->jy;
- /* scroll second step to align at full tile size */
- BackToFront();
- Delay(wait_delay_value);
- }
-#endif
+ /* assign first free player found that is present in the playfield */
+ if (field_player->present && !field_player->connected)
+ {
+ player->present = TRUE;
+ player->active = TRUE;
- DrawPlayer(player);
- BackToFront();
- Delay(wait_delay_value);
- }
-}
+ field_player->present = FALSE;
+ field_player->active = FALSE;
-void RelocatePlayer(int jx, int jy, int el_player_raw)
-{
-#if 1
- int el_player = GET_VALID_PLAYER_ELEMENT(el_player_raw);
-#else
- int el_player = (el_player_raw == EL_SP_MURPHY ? EL_PLAYER_1 :el_player_raw);
-#endif
- struct PlayerInfo *player = &stored_player[el_player - EL_PLAYER_1];
- boolean ffwd_delay = (tape.playing && tape.fast_forward);
- boolean no_delay = (tape.warp_forward);
- int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
- int wait_delay_value = (no_delay ? 0 : frame_delay_value);
- int old_jx = player->jx;
- int old_jy = player->jy;
- int old_element = Feld[old_jx][old_jy];
- int element = Feld[jx][jy];
- boolean player_relocated = (old_jx != jx || old_jy != jy);
+ player->initial_element = field_player->initial_element;
+ player->artwork_element = field_player->artwork_element;
- int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
- int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
-#if 1
- int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
- int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
- int leave_side_horiz = move_dir_horiz;
- int leave_side_vert = move_dir_vert;
-#else
- static int trigger_sides[4][2] =
- {
- /* enter side leave side */
- { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
- { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
- { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
- { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
- };
- int enter_side_horiz = trigger_sides[MV_DIR_BIT(move_dir_horiz)][0];
- int enter_side_vert = trigger_sides[MV_DIR_BIT(move_dir_vert)][0];
- int leave_side_horiz = trigger_sides[MV_DIR_BIT(move_dir_horiz)][1];
- int leave_side_vert = trigger_sides[MV_DIR_BIT(move_dir_vert)][1];
-#endif
- int enter_side = enter_side_horiz | enter_side_vert;
- int leave_side = leave_side_horiz | leave_side_vert;
+ player->block_last_field = field_player->block_last_field;
+ player->block_delay_adjustment = field_player->block_delay_adjustment;
- if (player->GameOver) /* do not reanimate dead player */
- return;
+ StorePlayer[jx][jy] = player->element_nr;
- if (!player_relocated) /* no need to relocate the player */
- return;
+ player->jx = player->last_jx = jx;
+ player->jy = player->last_jy = jy;
- if (IS_PLAYER(jx, jy)) /* player already placed at new position */
- {
- RemoveField(jx, jy); /* temporarily remove newly placed player */
- DrawLevelField(jx, jy);
+ break;
+ }
+ }
+ }
}
+#endif
- if (player->present)
+#if 0
+ printf("::: local_player->present == %d\n", local_player->present);
+#endif
+
+ if (tape.playing)
{
- while (player->MovPos)
+ /* when playing a tape, eliminate all players who do not participate */
+
+#if USE_NEW_PLAYER_ASSIGNMENTS
+ for (i = 0; i < MAX_PLAYERS; i++)
{
- ScrollPlayer(player, SCROLL_GO_ON);
- ScrollScreen(NULL, SCROLL_GO_ON);
+ if (stored_player[i].active &&
+ !tape.player_participates[map_player_action[i]])
+ {
+ struct PlayerInfo *player = &stored_player[i];
+ int jx = player->jx, jy = player->jy;
-#if USE_NEW_MOVE_DELAY
- AdvanceFrameAndPlayerCounters(player->index_nr);
+ player->active = FALSE;
+ StorePlayer[jx][jy] = 0;
+ Feld[jx][jy] = EL_EMPTY;
+ }
+ }
#else
- FrameCounter++;
-#endif
-
- DrawPlayer(player);
+ for (i = 0; i < MAX_PLAYERS; i++)
+ {
+ if (stored_player[i].active &&
+ !tape.player_participates[i])
+ {
+ struct PlayerInfo *player = &stored_player[i];
+ int jx = player->jx, jy = player->jy;
- BackToFront();
- Delay(wait_delay_value);
+ player->active = FALSE;
+ StorePlayer[jx][jy] = 0;
+ Feld[jx][jy] = EL_EMPTY;
+ }
}
-
- DrawPlayer(player); /* needed here only to cleanup last field */
- DrawLevelField(player->jx, player->jy); /* remove player graphic */
-
- player->is_moving = FALSE;
+#endif
}
+ else if (!options.network && !setup.team_mode) /* && !tape.playing */
+ {
+ /* when in single player mode, eliminate all but the first active player */
-#if 1
- if (IS_CUSTOM_ELEMENT(old_element))
- CheckElementChangeByPlayer(old_jx, old_jy, old_element,
- CE_LEFT_BY_PLAYER,
- player->index_bit, leave_side);
+ for (i = 0; i < MAX_PLAYERS; i++)
+ {
+ if (stored_player[i].active)
+ {
+ for (j = i + 1; j < MAX_PLAYERS; j++)
+ {
+ if (stored_player[j].active)
+ {
+ struct PlayerInfo *player = &stored_player[j];
+ int jx = player->jx, jy = player->jy;
- CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
- CE_OTHER_GETS_LEFT,
- player->index_bit, leave_side);
-#endif
+ player->active = FALSE;
+ player->present = FALSE;
- Feld[jx][jy] = el_player;
- InitPlayerField(jx, jy, el_player, TRUE);
+ StorePlayer[jx][jy] = 0;
+ Feld[jx][jy] = EL_EMPTY;
+ }
+ }
+ }
+ }
+ }
- if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
+ /* when recording the game, store which players take part in the game */
+ if (tape.recording)
{
- Feld[jx][jy] = element;
- InitField(jx, jy, FALSE);
+#if USE_NEW_PLAYER_ASSIGNMENTS
+ for (i = 0; i < MAX_PLAYERS; i++)
+ if (stored_player[i].connected)
+ tape.player_participates[i] = TRUE;
+#else
+ for (i = 0; i < MAX_PLAYERS; i++)
+ if (stored_player[i].active)
+ tape.player_participates[i] = TRUE;
+#endif
}
-#if 1
- if (player == local_player) /* only visually relocate local player */
- DrawRelocatePlayer(player);
-#endif
+ if (options.debug)
+ {
+ for (i = 0; i < MAX_PLAYERS; i++)
+ {
+ struct PlayerInfo *player = &stored_player[i];
-#if 1
- TestIfHeroTouchesBadThing(jx, jy);
- TestIfPlayerTouchesCustomElement(jx, jy);
-#endif
+ printf("Player %d: present == %d, connected == %d, active == %d.\n",
+ i+1,
+ player->present,
+ player->connected,
+ player->active);
+ if (local_player == player)
+ printf("Player %d is local player.\n", i+1);
+ }
+ }
-#if 0
- printf("::: %d,%d: %d\n", jx, jy-1, Changed[jx][jy-1]);
-#endif
+ if (BorderElement == EL_EMPTY)
+ {
+ SBX_Left = 0;
+ SBX_Right = lev_fieldx - SCR_FIELDX;
+ SBY_Upper = 0;
+ SBY_Lower = lev_fieldy - SCR_FIELDY;
+ }
+ else
+ {
+ SBX_Left = -1;
+ SBX_Right = lev_fieldx - SCR_FIELDX + 1;
+ SBY_Upper = -1;
+ SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
+ }
-#if 0
-#if 0
- /* needed to allow change of walkable custom element by entering player */
- if (!(Changed[jx][jy] & CH_EVENT_BIT(CE_ENTERED_BY_PLAYER)))
- Changed[jx][jy] = 0; /* allow another change (but prevent loop) */
-#else
- /* needed to allow change of walkable custom element by entering player */
- Changed[jx][jy] = 0; /* allow another change */
-#endif
-#endif
+#if NEW_TILESIZE
-#if 0
- printf("::: player entering %d, %d from %s ...\n", jx, jy,
- enter_side == MV_LEFT ? "left" :
- enter_side == MV_RIGHT ? "right" :
- enter_side == MV_UP ? "top" :
- enter_side == MV_DOWN ? "bottom" : "oops! no idea!");
-#endif
+ if (lev_fieldx + (SBX_Left < 0 ? 2 : 0) <= SCR_FIELDX)
+ SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
-#if 1
- if (IS_CUSTOM_ELEMENT(element))
- CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
- player->index_bit, enter_side);
+ if (lev_fieldy + (SBY_Upper < 0 ? 2 : 0) <= SCR_FIELDY)
+ SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
- CheckTriggeredElementChangeByPlayer(jx, jy, element,
- CE_OTHER_GETS_ENTERED,
- player->index_bit, enter_side);
-#endif
-}
+ if (EVEN(SCR_FIELDX))
+ SBX_Left--;
+ if (EVEN(SCR_FIELDY))
+ SBY_Upper--;
-void Explode(int ex, int ey, int phase, int mode)
-{
- int x, y;
-#if 0
- int num_phase = 9;
-#endif
+#else
- /* !!! eliminate this variable !!! */
- int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
+ if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
+ SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
-#if 1
- int last_phase;
-#else
- int last_phase = num_phase * delay;
- int half_phase = (num_phase / 2) * delay;
- int first_phase_after_start = EX_PHASE_START + 1;
+ if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
+ SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
#endif
- int border_element;
- if (game.explosions_delayed)
+ /* if local player not found, look for custom element that might create
+ the player (make some assumptions about the right custom element) */
+ if (!local_player->present)
{
- ExplodeField[ex][ey] = mode;
- return;
- }
+ int start_x = 0, start_y = 0;
+ int found_rating = 0;
+ int found_element = EL_UNDEFINED;
+ int player_nr = local_player->index_nr;
- if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
- {
- int center_element = Feld[ex][ey];
+ SCAN_PLAYFIELD(x, y)
+ {
+ int element = Feld[x][y];
+ int content;
+ int xx, yy;
+ boolean is_player;
-#if 0
- printf("::: start explosion %d,%d [%d]\n", ex, ey, FrameCounter);
-#endif
+ if (level.use_start_element[player_nr] &&
+ level.start_element[player_nr] == element &&
+ found_rating < 4)
+ {
+ start_x = x;
+ start_y = y;
-#if 0
- /* --- This is only really needed (and now handled) in "Impact()". --- */
- /* do not explode moving elements that left the explode field in time */
- if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
- center_element == EL_EMPTY &&
- (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
- return;
-#endif
+ found_rating = 4;
+ found_element = element;
+ }
- if (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER)
- PlayLevelSoundAction(ex, ey, ACTION_EXPLODING);
+ if (!IS_CUSTOM_ELEMENT(element))
+ continue;
- /* remove things displayed in background while burning dynamite */
- if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
- Back[ex][ey] = 0;
+ if (CAN_CHANGE(element))
+ {
+ for (i = 0; i < element_info[element].num_change_pages; i++)
+ {
+ /* check for player created from custom element as single target */
+ content = element_info[element].change_page[i].target_element;
+ is_player = ELEM_IS_PLAYER(content);
- if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
- {
- /* put moving element to center field (and let it explode there) */
- center_element = MovingOrBlocked2Element(ex, ey);
- RemoveMovingField(ex, ey);
- Feld[ex][ey] = center_element;
- }
+ if (is_player && (found_rating < 3 ||
+ (found_rating == 3 && element < found_element)))
+ {
+ start_x = x;
+ start_y = y;
-#if 1
+ found_rating = 3;
+ found_element = element;
+ }
+ }
+ }
-#if 1
- last_phase = element_info[center_element].explosion_delay + 1;
-#else
- last_phase = element_info[center_element].explosion_delay;
-#endif
+ for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
+ {
+ /* check for player created from custom element as explosion content */
+ content = element_info[element].content.e[xx][yy];
+ is_player = ELEM_IS_PLAYER(content);
-#if 0
- printf("::: %d -> %d\n", center_element, last_phase);
-#endif
-#endif
+ if (is_player && (found_rating < 2 ||
+ (found_rating == 2 && element < found_element)))
+ {
+ start_x = x + xx - 1;
+ start_y = y + yy - 1;
- for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
- {
- int xx = x - ex + 1;
- int yy = y - ey + 1;
- int element;
+ found_rating = 2;
+ found_element = element;
+ }
-#if 1
-#if 1
- if (!IN_LEV_FIELD(x, y) ||
- (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
- (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
- continue;
-#else
- if (!IN_LEV_FIELD(x, y) ||
- (mode != EX_TYPE_NORMAL && (x != ex || y != ey)))
- continue;
-#endif
-#else
- if (!IN_LEV_FIELD(x, y) ||
- ((mode != EX_TYPE_NORMAL ||
- center_element == EL_AMOEBA_TO_DIAMOND) &&
- (x != ex || y != ey)))
- continue;
-#endif
+ if (!CAN_CHANGE(element))
+ continue;
- element = Feld[x][y];
+ for (i = 0; i < element_info[element].num_change_pages; i++)
+ {
+ /* check for player created from custom element as extended target */
+ content =
+ element_info[element].change_page[i].target_content.e[xx][yy];
- if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
- {
- element = MovingOrBlocked2Element(x, y);
+ is_player = ELEM_IS_PLAYER(content);
- if (!IS_EXPLOSION_PROOF(element))
- RemoveMovingField(x, y);
+ if (is_player && (found_rating < 1 ||
+ (found_rating == 1 && element < found_element)))
+ {
+ start_x = x + xx - 1;
+ start_y = y + yy - 1;
+
+ found_rating = 1;
+ found_element = element;
+ }
+ }
}
+ }
-#if 1
+ scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
+ start_x > SBX_Right + MIDPOSX ? SBX_Right :
+ start_x - MIDPOSX);
+
+ scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
+ start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
+ start_y - MIDPOSY);
+ }
+ else
+ {
+ scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
+ local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
+ local_player->jx - MIDPOSX);
+
+ scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
+ local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
+ local_player->jy - MIDPOSY);
+ }
#if 0
- if (IS_EXPLOSION_PROOF(element))
- continue;
-#else
- /* indestructible elements can only explode in center (but not flames) */
-#if 1
- if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
- mode == EX_TYPE_BORDER)) ||
- element == EL_FLAMES)
- continue;
-#else
- if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey)) ||
- element == EL_FLAMES)
- continue;
-#endif
+ printf("::: %d, %d (initial)\n", scroll_x, scroll_y);
#endif
-#else
- if ((IS_INDESTRUCTIBLE(element) &&
- (game.engine_version < VERSION_IDENT(2,2,0,0) ||
- (!IS_WALKABLE_OVER(element) && !IS_WALKABLE_UNDER(element)))) ||
- element == EL_FLAMES)
- continue;
+#if 0
+ /* do not use PLAYING mask for fading out from main screen */
+ game_status = GAME_MODE_MAIN;
#endif
+ StopAnimation();
+
+ if (!game.restart_level)
+ CloseDoor(DOOR_CLOSE_1);
+
#if 1
- if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
- (game.engine_version < VERSION_IDENT(3,1,0,0) ||
- (x == ex && y == ey && mode != EX_TYPE_BORDER)))
+ if (level_editor_test_game)
+ FadeSkipNextFadeIn();
+ else
+ FadeSetEnterScreen();
#else
- if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
+ if (level_editor_test_game)
+ fading = fading_none;
+ else
+ fading = menu.destination;
#endif
- {
- if (IS_ACTIVE_BOMB(element))
- {
- /* re-activate things under the bomb like gate or penguin */
+
#if 1
- Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
- Back[x][y] = 0;
+ FadeOut(REDRAW_FIELD);
#else
- Feld[x][y] = (Store[x][y] ? Store[x][y] : EL_EMPTY);
- Store[x][y] = 0;
+ if (do_fading)
+ FadeOut(REDRAW_FIELD);
#endif
#if 0
- printf("::: %d,%d: %d %s [%d, %d]\n", x, y, Feld[x][y],
- element_info[Feld[x][y]].token_name,
- Store[x][y], Store2[x][y]);
+ game_status = GAME_MODE_PLAYING;
#endif
- }
- continue;
- }
+ /* !!! FIX THIS (START) !!! */
+ if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
+ {
+ InitGameEngine_EM();
- /* save walkable background elements while explosion on same tile */
-#if 0
- if (IS_INDESTRUCTIBLE(element))
- Back[x][y] = element;
-#else
-#if 1
-#if 1
- if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
- (x != ex || y != ey || mode == EX_TYPE_BORDER))
- Back[x][y] = element;
-#else
- if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
- (x != ex || y != ey))
- Back[x][y] = element;
-#endif
-#else
- if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element))
- Back[x][y] = element;
-#endif
-#endif
+ /* blit playfield from scroll buffer to normal back buffer for fading in */
+ BlitScreenToBitmap_EM(backbuffer);
+ }
+ else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
+ {
+ InitGameEngine_SP();
- /* ignite explodable elements reached by other explosion */
- if (element == EL_EXPLOSION)
- element = Store2[x][y];
+ /* blit playfield from scroll buffer to normal back buffer for fading in */
+ BlitScreenToBitmap_SP(backbuffer);
+ }
+ else
+ {
+ DrawLevel();
+ DrawAllPlayers();
-#if 1
- if (AmoebaNr[x][y] &&
- (element == EL_AMOEBA_FULL ||
- element == EL_BD_AMOEBA ||
- element == EL_AMOEBA_GROWING))
- {
- AmoebaCnt[AmoebaNr[x][y]]--;
- AmoebaCnt2[AmoebaNr[x][y]]--;
- }
+ /* after drawing the level, correct some elements */
+ if (game.timegate_time_left == 0)
+ CloseAllOpenTimegates();
- RemoveField(x, y);
+#if NEW_TILESIZE
+ BlitScreenToBitmap(backbuffer);
+#else
+ /* blit playfield from scroll buffer to normal back buffer for fading in */
+ if (setup.soft_scrolling)
+ BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
#endif
- if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
- {
- switch(StorePlayer[ex][ey])
- {
- case EL_PLAYER_2:
- Store[x][y] = EL_PLAYER_IS_EXPLODING_2;
- break;
- case EL_PLAYER_3:
- Store[x][y] = EL_PLAYER_IS_EXPLODING_3;
- break;
- case EL_PLAYER_4:
- Store[x][y] = EL_PLAYER_IS_EXPLODING_4;
- break;
- case EL_PLAYER_1:
- default:
- Store[x][y] = EL_PLAYER_IS_EXPLODING_1;
- break;
- }
+ redraw_mask |= REDRAW_FROM_BACKBUFFER;
+ }
+ /* !!! FIX THIS (END) !!! */
#if 1
- if (PLAYERINFO(ex, ey)->use_murphy_graphic)
- Store[x][y] = EL_EMPTY;
+ FadeIn(REDRAW_FIELD);
#else
- if (game.emulation == EMU_SUPAPLEX)
- Store[x][y] = EL_EMPTY;
-#endif
- }
- else if (center_element == EL_MOLE)
- Store[x][y] = EL_EMERALD_RED;
- else if (center_element == EL_PENGUIN)
- Store[x][y] = EL_EMERALD_PURPLE;
- else if (center_element == EL_BUG)
- Store[x][y] = ((x == ex && y == ey) ? EL_DIAMOND : EL_EMERALD);
- else if (center_element == EL_BD_BUTTERFLY)
- Store[x][y] = EL_BD_DIAMOND;
- else if (center_element == EL_SP_ELECTRON)
- Store[x][y] = EL_SP_INFOTRON;
- else if (center_element == EL_AMOEBA_TO_DIAMOND)
- Store[x][y] = level.amoeba_content;
- else if (center_element == EL_YAMYAM)
- Store[x][y] = level.yamyam_content[game.yamyam_content_nr][xx][yy];
- else if (IS_CUSTOM_ELEMENT(center_element) &&
- element_info[center_element].content[xx][yy] != EL_EMPTY)
- Store[x][y] = element_info[center_element].content[xx][yy];
- else if (element == EL_WALL_EMERALD)
- Store[x][y] = EL_EMERALD;
- else if (element == EL_WALL_DIAMOND)
- Store[x][y] = EL_DIAMOND;
- else if (element == EL_WALL_BD_DIAMOND)
- Store[x][y] = EL_BD_DIAMOND;
- else if (element == EL_WALL_EMERALD_YELLOW)
- Store[x][y] = EL_EMERALD_YELLOW;
- else if (element == EL_WALL_EMERALD_RED)
- Store[x][y] = EL_EMERALD_RED;
- else if (element == EL_WALL_EMERALD_PURPLE)
- Store[x][y] = EL_EMERALD_PURPLE;
- else if (element == EL_WALL_PEARL)
- Store[x][y] = EL_PEARL;
- else if (element == EL_WALL_CRYSTAL)
- Store[x][y] = EL_CRYSTAL;
- else if (IS_CUSTOM_ELEMENT(element) && !CAN_EXPLODE(element))
- Store[x][y] = element_info[element].content[1][1];
- else
- Store[x][y] = EL_EMPTY;
-
- if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
- center_element == EL_AMOEBA_TO_DIAMOND)
- Store2[x][y] = element;
+ if (do_fading)
+ FadeIn(REDRAW_FIELD);
-#if 0
- printf("::: %d,%d: %d %s\n", x, y, Store2[x][y],
- element_info[Store2[x][y]].token_name);
+ BackToFront();
#endif
-#if 0
- if (AmoebaNr[x][y] &&
- (element == EL_AMOEBA_FULL ||
- element == EL_BD_AMOEBA ||
- element == EL_AMOEBA_GROWING))
- {
- AmoebaCnt[AmoebaNr[x][y]]--;
- AmoebaCnt2[AmoebaNr[x][y]]--;
- }
-
+ if (!game.restart_level)
+ {
+ /* copy default game door content to main double buffer */
#if 1
- RemoveField(x, y);
+#if 1
+ /* !!! CHECK AGAIN !!! */
+ SetPanelBackground();
+ // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
+ DrawBackground(DX, DY, DXSIZE, DYSIZE);
#else
- MovDir[x][y] = MovPos[x][y] = 0;
- GfxDir[x][y] = MovDir[x][y];
- AmoebaNr[x][y] = 0;
-#endif
-#endif
+ struct GraphicInfo *gfx = &graphic_info[IMG_BACKGROUND_PANEL];
- Feld[x][y] = EL_EXPLOSION;
-#if 1
- GfxElement[x][y] = center_element;
+ /* (ClearRectangle() only needed if panel bitmap is smaller than panel) */
+ ClearRectangle(drawto, DX, DY, DXSIZE, DYSIZE);
+ BlitBitmap(gfx->bitmap, drawto, gfx->src_x, gfx->src_y,
+ MIN(gfx->width, DXSIZE), MIN(gfx->height, DYSIZE), DX, DY);
+#endif
#else
- GfxElement[x][y] = EL_UNDEFINED;
+ BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
+ DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
#endif
+ }
- ExplodePhase[x][y] = 1;
-#if 1
- ExplodeDelay[x][y] = last_phase;
-#endif
+ SetPanelBackground();
+ SetDrawBackgroundMask(REDRAW_DOOR_1);
-#if 0
#if 1
- GfxFrame[x][y] = 0; /* animation does not start until next frame */
+ UpdateAndDisplayGameControlValues();
#else
- GfxFrame[x][y] = -1; /* animation does not start until next frame */
-#endif
+ UpdateGameDoorValues();
+ DrawGameDoorValues();
#endif
- Stop[x][y] = TRUE;
- }
+ if (!game.restart_level)
+ {
+ UnmapGameButtons();
+ UnmapTapeButtons();
+ game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
+ game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
+ game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
+ MapGameButtons();
+ MapTapeButtons();
- if (center_element == EL_YAMYAM)
- game.yamyam_content_nr =
- (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
+ /* copy actual game door content to door double buffer for OpenDoor() */
+ BlitBitmap(drawto, bitmap_db_door,
+ DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
-#if 0
- printf("::: %d,%d: %d %s [%d]\n", ex + 1, ey, Feld[ex + 1][ey],
- element_info[Feld[ex + 1][ey]].token_name, Store2[ex + 1][ey]);
-#endif
+ OpenDoor(DOOR_OPEN_ALL);
- return;
- }
+ PlaySound(SND_GAME_STARTING);
- if (Stop[ex][ey])
- return;
+ if (setup.sound_music)
+ PlayLevelMusic();
- x = ex;
- y = ey;
+ KeyboardAutoRepeatOffUnlessAutoplay();
+
+ if (options.debug)
+ {
+ for (i = 0; i < MAX_PLAYERS; i++)
+ printf("Player %d %sactive.\n",
+ i + 1, (stored_player[i].active ? "" : "not "));
+ }
+ }
#if 1
- if (phase == 1)
- GfxFrame[x][y] = 0; /* restart explosion animation */
+ UnmapAllGadgets();
+
+ MapGameButtons();
+ MapTapeButtons();
#endif
+ if (!game.restart_level && !tape.playing)
+ {
+ LevelStats_incPlayed(level_nr);
+
+ SaveLevelSetup_SeriesInfo();
+
#if 0
- printf(":X: phase == %d [%d]\n", phase, GfxFrame[x][y]);
+ printf("::: PLAYING LEVEL (%d)\n", LevelStats_getPlayed(level_nr));
#endif
+ }
-#if 1
- last_phase = ExplodeDelay[x][y];
-#endif
+ game.restart_level = FALSE;
+}
- ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
+void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
+{
+ /* this is used for non-R'n'D game engines to update certain engine values */
-#ifdef DEBUG
+ /* needed to determine if sounds are played within the visible screen area */
+ scroll_x = actual_scroll_x;
+ scroll_y = actual_scroll_y;
+}
- /* activate this even in non-DEBUG version until cause for crash in
- getGraphicAnimationFrame() (see below) is found and eliminated */
-#endif
-#if 1
+void InitMovDir(int x, int y)
+{
+ int i, element = Feld[x][y];
+ static int xy[4][2] =
+ {
+ { 0, +1 },
+ { +1, 0 },
+ { 0, -1 },
+ { -1, 0 }
+ };
+ static int direction[3][4] =
+ {
+ { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
+ { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
+ { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
+ };
- if (GfxElement[x][y] == EL_UNDEFINED)
+ switch (element)
{
- printf("\n\n");
- printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
- printf("Explode(): This should never happen!\n");
- printf("\n\n");
+ case EL_BUG_RIGHT:
+ case EL_BUG_UP:
+ case EL_BUG_LEFT:
+ case EL_BUG_DOWN:
+ Feld[x][y] = EL_BUG;
+ MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
+ break;
- GfxElement[x][y] = EL_EMPTY;
- }
-#endif
+ case EL_SPACESHIP_RIGHT:
+ case EL_SPACESHIP_UP:
+ case EL_SPACESHIP_LEFT:
+ case EL_SPACESHIP_DOWN:
+ Feld[x][y] = EL_SPACESHIP;
+ MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
+ break;
-#if 1
+ case EL_BD_BUTTERFLY_RIGHT:
+ case EL_BD_BUTTERFLY_UP:
+ case EL_BD_BUTTERFLY_LEFT:
+ case EL_BD_BUTTERFLY_DOWN:
+ Feld[x][y] = EL_BD_BUTTERFLY;
+ MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
+ break;
- border_element = Store2[x][y];
-#if 1
- if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
- border_element = StorePlayer[x][y];
-#else
- if (IS_PLAYER(x, y))
- border_element = StorePlayer[x][y];
-#endif
+ case EL_BD_FIREFLY_RIGHT:
+ case EL_BD_FIREFLY_UP:
+ case EL_BD_FIREFLY_LEFT:
+ case EL_BD_FIREFLY_DOWN:
+ Feld[x][y] = EL_BD_FIREFLY;
+ MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
+ break;
-#if 0
- printf("::: %d,%d: %d %s [%d]\n", x, y, border_element,
- element_info[border_element].token_name, Store2[x][y]);
-#endif
+ case EL_PACMAN_RIGHT:
+ case EL_PACMAN_UP:
+ case EL_PACMAN_LEFT:
+ case EL_PACMAN_DOWN:
+ Feld[x][y] = EL_PACMAN;
+ MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
+ break;
-#if 0
- printf("::: phase == %d\n", phase);
-#endif
+ case EL_YAMYAM_LEFT:
+ case EL_YAMYAM_RIGHT:
+ case EL_YAMYAM_UP:
+ case EL_YAMYAM_DOWN:
+ Feld[x][y] = EL_YAMYAM;
+ MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
+ break;
- if (phase == element_info[border_element].ignition_delay ||
- phase == last_phase)
- {
- boolean border_explosion = FALSE;
+ case EL_SP_SNIKSNAK:
+ MovDir[x][y] = MV_UP;
+ break;
-#if 1
-#if 1
- if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
- !PLAYER_EXPLOSION_PROTECTED(x, y))
-#else
- if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present)
-#endif
-#else
- if (IS_PLAYER(x, y))
-#endif
- {
- KillHeroUnlessExplosionProtected(x, y);
- border_explosion = TRUE;
+ case EL_SP_ELECTRON:
+ MovDir[x][y] = MV_LEFT;
+ break;
-#if 0
- if (phase == last_phase)
- printf("::: IS_PLAYER\n");
-#endif
- }
- else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
- {
-#if 0
- printf("::: %d,%d: %d %s\n", x, y, border_element,
- element_info[border_element].token_name);
-#endif
+ case EL_MOLE_LEFT:
+ case EL_MOLE_RIGHT:
+ case EL_MOLE_UP:
+ case EL_MOLE_DOWN:
+ Feld[x][y] = EL_MOLE;
+ MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
+ break;
- Feld[x][y] = Store2[x][y];
- Store2[x][y] = 0;
- Bang(x, y);
- border_explosion = TRUE;
+ default:
+ if (IS_CUSTOM_ELEMENT(element))
+ {
+ struct ElementInfo *ei = &element_info[element];
+ int move_direction_initial = ei->move_direction_initial;
+ int move_pattern = ei->move_pattern;
-#if 0
- if (phase == last_phase)
- printf("::: CAN_EXPLODE_BY_EXPLOSION\n");
-#endif
- }
- else if (border_element == EL_AMOEBA_TO_DIAMOND)
- {
- AmoebeUmwandeln(x, y);
- Store2[x][y] = 0;
- border_explosion = TRUE;
+ if (move_direction_initial == MV_START_PREVIOUS)
+ {
+ if (MovDir[x][y] != MV_NONE)
+ return;
-#if 0
- if (phase == last_phase)
- printf("::: EL_AMOEBA_TO_DIAMOND [%d, %d] [%d]\n",
- element_info[border_element].explosion_delay,
- element_info[border_element].ignition_delay,
- phase);
-#endif
- }
+ move_direction_initial = MV_START_AUTOMATIC;
+ }
-#if 1
- /* if an element just explodes due to another explosion (chain-reaction),
- do not immediately end the new explosion when it was the last frame of
- the explosion (as it would be done in the following "if"-statement!) */
- if (border_explosion && phase == last_phase)
- return;
-#endif
- }
+ if (move_direction_initial == MV_START_RANDOM)
+ MovDir[x][y] = 1 << RND(4);
+ else if (move_direction_initial & MV_ANY_DIRECTION)
+ MovDir[x][y] = move_direction_initial;
+ else if (move_pattern == MV_ALL_DIRECTIONS ||
+ move_pattern == MV_TURNING_LEFT ||
+ move_pattern == MV_TURNING_RIGHT ||
+ move_pattern == MV_TURNING_LEFT_RIGHT ||
+ move_pattern == MV_TURNING_RIGHT_LEFT ||
+ move_pattern == MV_TURNING_RANDOM)
+ MovDir[x][y] = 1 << RND(4);
+ else if (move_pattern == MV_HORIZONTAL)
+ MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
+ else if (move_pattern == MV_VERTICAL)
+ MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
+ else if (move_pattern & MV_ANY_DIRECTION)
+ MovDir[x][y] = element_info[element].move_pattern;
+ else if (move_pattern == MV_ALONG_LEFT_SIDE ||
+ move_pattern == MV_ALONG_RIGHT_SIDE)
+ {
+ /* use random direction as default start direction */
+ if (game.engine_version >= VERSION_IDENT(3,1,0,0))
+ MovDir[x][y] = 1 << RND(4);
-#else
+ for (i = 0; i < NUM_DIRECTIONS; i++)
+ {
+ int x1 = x + xy[i][0];
+ int y1 = y + xy[i][1];
- if (phase == first_phase_after_start)
- {
- int element = Store2[x][y];
+ if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
+ {
+ if (move_pattern == MV_ALONG_RIGHT_SIDE)
+ MovDir[x][y] = direction[0][i];
+ else
+ MovDir[x][y] = direction[1][i];
- if (element == EL_BLACK_ORB)
- {
- Feld[x][y] = Store2[x][y];
- Store2[x][y] = 0;
- Bang(x, y);
- }
+ break;
+ }
+ }
+ }
+ }
+ else
+ {
+ MovDir[x][y] = 1 << RND(4);
+
+ if (element != EL_BUG &&
+ element != EL_SPACESHIP &&
+ element != EL_BD_BUTTERFLY &&
+ element != EL_BD_FIREFLY)
+ break;
+
+ for (i = 0; i < NUM_DIRECTIONS; i++)
+ {
+ int x1 = x + xy[i][0];
+ int y1 = y + xy[i][1];
+
+ if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
+ {
+ if (element == EL_BUG || element == EL_BD_BUTTERFLY)
+ {
+ MovDir[x][y] = direction[0][i];
+ break;
+ }
+ else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
+ element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
+ {
+ MovDir[x][y] = direction[1][i];
+ break;
+ }
+ }
+ }
+ }
+ break;
}
- else if (phase == half_phase)
- {
- int element = Store2[x][y];
- if (IS_PLAYER(x, y))
- KillHeroUnlessExplosionProtected(x, y);
- else if (CAN_EXPLODE_BY_EXPLOSION(element))
+ GfxDir[x][y] = MovDir[x][y];
+}
+
+void InitAmoebaNr(int x, int y)
+{
+ int i;
+ int group_nr = AmoebeNachbarNr(x, y);
+
+ if (group_nr == 0)
+ {
+ for (i = 1; i < MAX_NUM_AMOEBA; i++)
{
- Feld[x][y] = Store2[x][y];
- Store2[x][y] = 0;
- Bang(x, y);
+ if (AmoebaCnt[i] == 0)
+ {
+ group_nr = i;
+ break;
+ }
}
- else if (element == EL_AMOEBA_TO_DIAMOND)
- AmoebeUmwandeln(x, y);
}
-#endif
-
- if (phase == last_phase)
- {
- int element;
-#if 0
- printf("::: done: phase == %d\n", phase);
-#endif
+ AmoebaNr[x][y] = group_nr;
+ AmoebaCnt[group_nr]++;
+ AmoebaCnt2[group_nr]++;
+}
-#if 0
- printf("::: explosion %d,%d done [%d]\n", x, y, FrameCounter);
-#endif
+static void PlayerWins(struct PlayerInfo *player)
+{
+ player->LevelSolved = TRUE;
+ player->GameOver = TRUE;
- element = Feld[x][y] = Store[x][y];
- Store[x][y] = Store2[x][y] = 0;
- GfxElement[x][y] = EL_UNDEFINED;
+ player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
+ level.native_em_level->lev->score : player->score);
- /* player can escape from explosions and might therefore be still alive */
- if (element >= EL_PLAYER_IS_EXPLODING_1 &&
- element <= EL_PLAYER_IS_EXPLODING_4)
- Feld[x][y] = (stored_player[element - EL_PLAYER_IS_EXPLODING_1].active ?
- EL_EMPTY :
- element == EL_PLAYER_IS_EXPLODING_1 ? EL_EMERALD_YELLOW :
- element == EL_PLAYER_IS_EXPLODING_2 ? EL_EMERALD_RED :
- element == EL_PLAYER_IS_EXPLODING_3 ? EL_EMERALD :
- EL_EMERALD_PURPLE);
+ player->LevelSolved_CountingTime = (level.time == 0 ? TimePlayed : TimeLeft);
+ player->LevelSolved_CountingScore = player->score_final;
+}
- /* restore probably existing indestructible background element */
- if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
- element = Feld[x][y] = Back[x][y];
- Back[x][y] = 0;
+void GameWon()
+{
+ static int time, time_final;
+ static int score, score_final;
+ 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;
- MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
- GfxDir[x][y] = MV_NO_MOVING;
- ChangeDelay[x][y] = 0;
- ChangePage[x][y] = -1;
+ if (!local_player->LevelSolved_GameWon)
+ {
+ int i;
-#if 1
- InitField_WithBug2(x, y, FALSE);
-#else
- InitField(x, y, FALSE);
-#if 1
- /* !!! not needed !!! */
-#if 1
- if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
- CAN_MOVE(Feld[x][y]) && Feld[x][y] != EL_MOLE)
- InitMovDir(x, y);
-#else
- if (CAN_MOVE(element))
- InitMovDir(x, y);
-#endif
-#endif
-#endif
- DrawLevelField(x, y);
+ /* do not start end game actions before the player stops moving (to exit) */
+ if (local_player->MovPos)
+ return;
- TestIfElementTouchesCustomElement(x, y);
+ local_player->LevelSolved_GameWon = TRUE;
+ local_player->LevelSolved_SaveTape = tape.recording;
+ local_player->LevelSolved_SaveScore = !tape.playing;
- if (GFX_CRUMBLED(element))
- DrawLevelFieldCrumbledSandNeighbours(x, y);
+ if (!tape.playing)
+ {
+ LevelStats_incSolved(level_nr);
- if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
- StorePlayer[x][y] = 0;
+ SaveLevelSetup_SeriesInfo();
- if (ELEM_IS_PLAYER(element))
- RelocatePlayer(x, y, element);
- }
-#if 1
- else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
-#else
- else if (phase >= delay && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
+#if 0
+ printf("::: LEVEL SOLVED (%d)\n", LevelStats_getSolved(level_nr));
#endif
- {
+ }
+
+ if (tape.auto_play) /* tape might already be stopped here */
+ tape.auto_play_level_solved = TRUE;
+
#if 1
- int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
-#else
- int stored = Store[x][y];
- int graphic = (game.emulation != EMU_SUPAPLEX ? IMG_EXPLOSION :
- stored == EL_SP_INFOTRON ? IMG_SP_EXPLOSION_INFOTRON :
- IMG_SP_EXPLOSION);
+ TapeStop();
#endif
+
+ 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;
+
+ if (TimeLeft > 0)
+ {
+ time_final = 0;
+ score_final += TimeLeft * level.score[SC_TIME_BONUS];
+ }
+ else if (level.time == 0 && TimePlayed < 999)
+ {
+ time_final = 999;
+ score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
+ }
+
+ local_player->score_final = score_final;
+
+ if (level_editor_test_game)
+ {
+ time = time_final;
+ score = score_final;
+
#if 1
- int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
+ local_player->LevelSolved_CountingTime = time;
+ local_player->LevelSolved_CountingScore = score;
+
+ game_panel_controls[GAME_PANEL_TIME].value = time;
+ game_panel_controls[GAME_PANEL_SCORE].value = score;
+
+ DisplayGameControlValues();
#else
- int frame = getGraphicAnimationFrame(graphic, phase - delay);
+ DrawGameValue_Time(time);
+ DrawGameValue_Score(score);
#endif
+ }
-#if 0
- printf("::: phase == %d [%d]\n", phase, GfxFrame[x][y]);
-#endif
+ if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
+ {
+ if (ExitX >= 0 && ExitY >= 0) /* local player has left the level */
+ {
+ /* 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];
#if 0
- printf("::: %d / %d [%d - %d]\n",
- GfxFrame[x][y], phase - delay, phase, delay);
+ 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);
+ }
+ }
-#if 0
- printf("::: %d ['%s'] -> %d\n", GfxElement[x][y],
- element_info[GfxElement[x][y]].token_name,
- graphic);
-#endif
+ /* player disappears */
+ DrawLevelField(ExitX, ExitY);
+ }
- if (phase == delay)
- DrawLevelFieldCrumbledSand(x, y);
+ for (i = 0; i < MAX_PLAYERS; i++)
+ {
+ struct PlayerInfo *player = &stored_player[i];
- if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
- {
- DrawLevelElement(x, y, Back[x][y]);
- DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
- }
- else if (IS_WALKABLE_UNDER(Back[x][y]))
- {
- DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
- DrawLevelElementThruMask(x, y, Back[x][y]);
+ if (player->present)
+ {
+ RemovePlayer(player);
+
+ /* player disappears */
+ DrawLevelField(player->jx, player->jy);
+ }
+ }
}
- else if (!IS_WALKABLE_INSIDE(Back[x][y]))
- DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
+
+ PlaySound(SND_GAME_WINNING);
}
-}
-void DynaExplode(int ex, int ey)
-{
- int i, j;
- int dynabomb_element = Feld[ex][ey];
- int dynabomb_size = 1;
- boolean dynabomb_xl = FALSE;
- struct PlayerInfo *player;
- static int xy[4][2] =
+ if (game_over_delay_1 > 0)
{
- { 0, -1 },
- { -1, 0 },
- { +1, 0 },
- { 0, +1 }
- };
+ game_over_delay_1--;
- if (IS_ACTIVE_BOMB(dynabomb_element))
- {
- player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
- dynabomb_size = player->dynabomb_size;
- dynabomb_xl = player->dynabomb_xl;
- player->dynabombs_left++;
+ return;
}
- Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
-
- for (i = 0; i < NUM_DIRECTIONS; i++)
+ if (time != time_final)
{
- for (j = 1; j <= dynabomb_size; j++)
- {
- int x = ex + j * xy[i][0];
- int y = ey + j * xy[i][1];
- int element;
+ int time_to_go = ABS(time_final - time);
+ int time_count_dir = (time < time_final ? +1 : -1);
+ int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
- if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
- break;
-
- element = Feld[x][y];
+ time += time_count_steps * time_count_dir;
+ score += time_count_steps * level.score[SC_TIME_BONUS];
- /* do not restart explosions of fields with active bombs */
- if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
- continue;
+#if 1
+ local_player->LevelSolved_CountingTime = time;
+ local_player->LevelSolved_CountingScore = score;
- Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
+ game_panel_controls[GAME_PANEL_TIME].value = time;
+ game_panel_controls[GAME_PANEL_SCORE].value = score;
-#if 1
-#if 1
- if (element != EL_EMPTY && element != EL_EXPLOSION &&
- !IS_DIGGABLE(element) && !dynabomb_xl)
- break;
-#else
- if (element != EL_EMPTY && element != EL_EXPLOSION &&
- !CAN_GROW_INTO(element) && !dynabomb_xl)
- break;
-#endif
+ DisplayGameControlValues();
#else
- /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
- if (element != EL_EMPTY && element != EL_EXPLOSION &&
- element != EL_SAND && !dynabomb_xl)
- break;
+ DrawGameValue_Time(time);
+ DrawGameValue_Score(score);
#endif
- }
+
+ if (time == time_final)
+ StopSound(SND_GAME_LEVELTIME_BONUS);
+ else if (setup.sound_loops)
+ PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
+ else
+ PlaySound(SND_GAME_LEVELTIME_BONUS);
+
+ return;
}
-}
-void Bang(int x, int y)
-{
-#if 1
- int element = MovingOrBlocked2Element(x, y);
-#else
- int element = Feld[x][y];
-#endif
+ local_player->LevelSolved_PanelOff = TRUE;
-#if 1
- if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
-#else
- if (IS_PLAYER(x, y))
-#endif
+ if (game_over_delay_2 > 0)
{
- struct PlayerInfo *player = PLAYERINFO(x, y);
+ game_over_delay_2--;
- element = Feld[x][y] = (player->use_murphy_graphic ? EL_SP_MURPHY :
- player->element_nr);
+ return;
}
-#if 0
#if 1
- PlayLevelSoundAction(x, y, ACTION_EXPLODING);
-#else
- if (game.emulation == EMU_SUPAPLEX)
- PlayLevelSound(x, y, SND_SP_ELEMENT_EXPLODING);
- else
- PlayLevelSound(x, y, SND_ELEMENT_EXPLODING);
-#endif
+ GameEnd();
#endif
+}
-#if 0
- if (IS_PLAYER(x, y)) /* remove objects that might cause smaller explosion */
- element = EL_EMPTY;
-#endif
+void GameEnd()
+{
+ int hi_pos;
+ boolean raise_level = FALSE;
+
+ local_player->LevelSolved_GameEnd = TRUE;
- switch(element)
+ CloseDoor(DOOR_CLOSE_1);
+
+ if (local_player->LevelSolved_SaveTape)
{
- case EL_BUG:
- case EL_SPACESHIP:
- case EL_BD_BUTTERFLY:
- case EL_BD_FIREFLY:
- case EL_YAMYAM:
- case EL_DARK_YAMYAM:
- case EL_ROBOT:
- case EL_PACMAN:
- case EL_MOLE:
- RaiseScoreElement(element);
- Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
- break;
- 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_DYNABOMB_INCREASE_NUMBER:
- case EL_DYNABOMB_INCREASE_SIZE:
- case EL_DYNABOMB_INCREASE_POWER:
- DynaExplode(x, y);
- break;
- case EL_PENGUIN:
- case EL_LAMP:
- case EL_LAMP_ACTIVE:
-#if 1
- case EL_AMOEBA_TO_DIAMOND:
+#if 0
+ TapeStop();
#endif
- if (IS_PLAYER(x, y))
- Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
- else
- Explode(x, y, EX_PHASE_START, EX_TYPE_CENTER);
- break;
- default:
+
#if 1
- if (element_info[element].explosion_type == EXPLODES_CROSS)
+ SaveTapeChecked(tape.level_nr); /* ask to save tape */
#else
- if (CAN_EXPLODE_CROSS(element))
+ SaveTape(tape.level_nr); /* ask to save tape */
#endif
+ }
+
+ if (level_editor_test_game)
+ {
+ game_status = GAME_MODE_MAIN;
+
#if 1
- Explode(x, y, EX_PHASE_START, EX_TYPE_CROSS);
+ DrawAndFadeInMainMenu(REDRAW_FIELD);
#else
- DynaExplode(x, y);
+ DrawMainMenu();
#endif
+
+ return;
+ }
+
+ if (!local_player->LevelSolved_SaveScore)
+ {
#if 1
- else if (element_info[element].explosion_type == EXPLODES_1X1)
-#else
- else if (CAN_EXPLODE_1X1(element))
+ FadeOut(REDRAW_FIELD);
#endif
- Explode(x, y, EX_PHASE_START, EX_TYPE_CENTER);
- else
- Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
- break;
- }
- CheckTriggeredElementChange(x, y, element, CE_OTHER_IS_EXPLODING);
-}
+ game_status = GAME_MODE_MAIN;
-void SplashAcid(int x, int y)
-{
-#if 1
- if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
- (!IN_LEV_FIELD(x - 1, y - 2) ||
- !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
- Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
+ DrawAndFadeInMainMenu(REDRAW_FIELD);
- if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
- (!IN_LEV_FIELD(x + 1, y - 2) ||
- !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
- Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
+ return;
+ }
- PlayLevelSound(x, y, SND_ACID_SPLASHING);
-#else
- /* input: position of element entering acid (obsolete) */
+ if (level_nr == leveldir_current->handicap_level)
+ {
+ leveldir_current->handicap_level++;
- int element = Feld[x][y];
+ SaveLevelSetup_SeriesInfo();
+ }
+
+ if (level_nr < leveldir_current->last_level)
+ raise_level = TRUE; /* advance to next level */
+
+ if ((hi_pos = NewHiScore()) >= 0)
+ {
+ game_status = GAME_MODE_SCORES;
+
+ DrawHallOfFame(hi_pos);
+
+ if (raise_level)
+ {
+ level_nr++;
+ TapeErase();
+ }
+ }
+ else
+ {
+#if 1
+ FadeOut(REDRAW_FIELD);
+#endif
+
+ game_status = GAME_MODE_MAIN;
+
+ if (raise_level)
+ {
+ level_nr++;
+ TapeErase();
+ }
+
+ DrawAndFadeInMainMenu(REDRAW_FIELD);
+ }
+}
+
+int NewHiScore()
+{
+ int k, l;
+ int position = -1;
+
+ LoadScore(level_nr);
+
+ if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
+ local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score)
+ return -1;
+
+ for (k = 0; k < MAX_SCORE_ENTRIES; k++)
+ {
+ if (local_player->score_final > highscore[k].Score)
+ {
+ /* player has made it to the hall of fame */
+
+ if (k < MAX_SCORE_ENTRIES - 1)
+ {
+ int m = MAX_SCORE_ENTRIES - 1;
+
+#ifdef ONE_PER_NAME
+ for (l = k; l < MAX_SCORE_ENTRIES; l++)
+ if (strEqual(setup.player_name, highscore[l].Name))
+ m = l;
+ if (m == k) /* player's new highscore overwrites his old one */
+ goto put_into_list;
+#endif
+
+ for (l = m; l > k; l--)
+ {
+ strcpy(highscore[l].Name, highscore[l - 1].Name);
+ highscore[l].Score = highscore[l - 1].Score;
+ }
+ }
+
+#ifdef ONE_PER_NAME
+ put_into_list:
+#endif
+ strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
+ highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
+ highscore[k].Score = local_player->score_final;
+ position = k;
+ break;
+ }
+
+#ifdef ONE_PER_NAME
+ else if (!strncmp(setup.player_name, highscore[k].Name,
+ MAX_PLAYER_NAME_LEN))
+ break; /* player already there with a higher score */
+#endif
+
+ }
+
+ if (position >= 0)
+ SaveScore(level_nr);
+
+ return position;
+}
+
+inline static int getElementMoveStepsizeExt(int x, int y, int direction)
+{
+ int element = Feld[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);
+ int sign = (horiz_move ? dx : dy);
+ int step = sign * element_info[element].move_stepsize;
+
+ /* special values for move stepsize for spring and things on conveyor belt */
+ if (horiz_move)
+ {
+ if (CAN_FALL(element) &&
+ y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
+ step = sign * MOVE_STEPSIZE_NORMAL / 2;
+ else if (element == EL_SPRING)
+ step = sign * MOVE_STEPSIZE_NORMAL * 2;
+ }
+
+ 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)
+ {
+#if 0
+ printf("Player frame reset! (%d => %d, %d => %d)\n",
+ player->GfxAction, action, player->GfxDir, dir);
+#endif
+
+ player->GfxAction = action;
+ player->GfxDir = dir;
+ player->Frame = 0;
+ player->StepFrame = 0;
+ }
+}
+
+#if USE_GFX_RESET_GFX_ANIMATION
+static void ResetGfxFrame(int x, int y, boolean redraw)
+{
+ int element = Feld[x][y];
+ int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
+ int last_gfx_frame = GfxFrame[x][y];
+
+ if (graphic_info[graphic].anim_global_sync)
+ GfxFrame[x][y] = FrameCounter;
+ else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
+ GfxFrame[x][y] = CustomValue[x][y];
+ else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
+ GfxFrame[x][y] = element_info[element].collect_score;
+ else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
+ GfxFrame[x][y] = ChangeDelay[x][y];
+
+ if (redraw && GfxFrame[x][y] != last_gfx_frame)
+ DrawLevelGraphicAnimation(x, y, graphic);
+}
+#endif
+
+static void ResetGfxAnimation(int x, int y)
+{
+ GfxAction[x][y] = ACTION_DEFAULT;
+ GfxDir[x][y] = MovDir[x][y];
+ GfxFrame[x][y] = 0;
+
+#if USE_GFX_RESET_GFX_ANIMATION
+ ResetGfxFrame(x, y, FALSE);
+#endif
+}
+
+static void ResetRandomAnimationValue(int x, int y)
+{
+ GfxRandom[x][y] = INIT_GFX_RANDOM();
+}
+
+void InitMovingField(int x, int y, int direction)
+{
+ int element = Feld[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 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);
+
+ /* 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 (is_moving_after)
+ {
+ if (Feld[newx][newy] == EL_EMPTY)
+ Feld[newx][newy] = EL_BLOCKED;
+
+ MovDir[newx][newy] = MovDir[x][y];
+
+#if USE_NEW_CUSTOM_VALUE
+ CustomValue[newx][newy] = CustomValue[x][y];
+#endif
+
+ GfxFrame[newx][newy] = GfxFrame[x][y];
+ GfxRandom[newx][newy] = GfxRandom[x][y];
+ GfxAction[newx][newy] = GfxAction[x][y];
+ GfxDir[newx][newy] = GfxDir[x][y];
+ }
+}
+
+void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
+{
+ int direction = MovDir[x][y];
+ int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
+ int newy = y + (direction & MV_UP ? -1 : direction & MV_DOWN ? +1 : 0);
+
+ *goes_to_x = newx;
+ *goes_to_y = newy;
+}
+
+void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
+{
+ int oldx = x, oldy = y;
+ int direction = MovDir[x][y];
+
+ if (direction == MV_LEFT)
+ oldx++;
+ else if (direction == MV_RIGHT)
+ oldx--;
+ else if (direction == MV_UP)
+ oldy++;
+ else if (direction == MV_DOWN)
+ oldy--;
+
+ *comes_from_x = oldx;
+ *comes_from_y = oldy;
+}
+
+int MovingOrBlocked2Element(int x, int y)
+{
+ int element = Feld[x][y];
+
+ if (element == EL_BLOCKED)
+ {
+ int oldx, oldy;
+
+ Blocked2Moving(x, y, &oldx, &oldy);
+ return Feld[oldx][oldy];
+ }
+ else
+ return element;
+}
+
+static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
+{
+ /* like MovingOrBlocked2Element(), but if element is moving
+ and (x,y) is the field the moving element is just leaving,
+ return EL_BLOCKED instead of the element value */
+ int element = Feld[x][y];
+
+ if (IS_MOVING(x, y))
+ {
+ if (element == EL_BLOCKED)
+ {
+ int oldx, oldy;
+
+ Blocked2Moving(x, y, &oldx, &oldy);
+ return Feld[oldx][oldy];
+ }
+ else
+ return EL_BLOCKED;
+ }
+ else
+ return element;
+}
+
+static void RemoveField(int x, int y)
+{
+ Feld[x][y] = EL_EMPTY;
+
+ MovPos[x][y] = 0;
+ MovDir[x][y] = 0;
+ MovDelay[x][y] = 0;
+
+#if USE_NEW_CUSTOM_VALUE
+ CustomValue[x][y] = 0;
+#endif
+
+ AmoebaNr[x][y] = 0;
+ ChangeDelay[x][y] = 0;
+ ChangePage[x][y] = -1;
+ Pushed[x][y] = FALSE;
+
+#if 0
+ ExplodeField[x][y] = EX_TYPE_NONE;
+#endif
+
+ GfxElement[x][y] = EL_UNDEFINED;
+ GfxAction[x][y] = ACTION_DEFAULT;
+ GfxDir[x][y] = MV_NONE;
+#if 0
+ /* !!! this would prevent the removed tile from being redrawn !!! */
+ GfxRedraw[x][y] = GFX_REDRAW_NONE;
+#endif
+}
+
+void RemoveMovingField(int x, int y)
+{
+ int oldx = x, oldy = y, newx = x, newy = y;
+ int element = Feld[x][y];
+ int next_element = EL_UNDEFINED;
+
+ if (element != EL_BLOCKED && !IS_MOVING(x, y))
+ return;
+
+ if (IS_MOVING(x, y))
+ {
+ Moving2Blocked(x, y, &newx, &newy);
+
+ if (Feld[newx][newy] != EL_BLOCKED)
+ {
+ /* element is moving, but target field is not free (blocked), but
+ already occupied by something different (example: acid pool);
+ in this case, only remove the moving field, but not the target */
+
+ RemoveField(oldx, oldy);
+
+ Store[oldx][oldy] = Store2[oldx][oldy] = 0;
+
+ TEST_DrawLevelField(oldx, oldy);
+
+ return;
+ }
+ }
+ else if (element == EL_BLOCKED)
+ {
+ Blocked2Moving(x, y, &oldx, &oldy);
+ if (!IS_MOVING(oldx, oldy))
+ return;
+ }
+
+ 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]);
+
+ RemoveField(oldx, oldy);
+ RemoveField(newx, newy);
+
+ Store[oldx][oldy] = Store2[oldx][oldy] = 0;
+
+ if (next_element != EL_UNDEFINED)
+ Feld[oldx][oldy] = next_element;
+
+ TEST_DrawLevelField(oldx, oldy);
+ TEST_DrawLevelField(newx, newy);
+}
+
+void DrawDynamite(int x, int y)
+{
+ int sx = SCREENX(x), sy = SCREENY(y);
+ int graphic = el2img(Feld[x][y]);
+ int frame;
+
+ if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
+ return;
+
+ if (IS_WALKABLE_INSIDE(Back[x][y]))
+ return;
+
+ if (Back[x][y])
+ DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
+ else if (Store[x][y])
+ DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
+
+ frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
+
+ if (Back[x][y] || Store[x][y])
+ DrawGraphicThruMask(sx, sy, graphic, frame);
+ else
+ DrawGraphic(sx, sy, graphic, frame);
+}
+
+void CheckDynamite(int x, int y)
+{
+ if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
+ {
+ MovDelay[x][y]--;
+
+ if (MovDelay[x][y] != 0)
+ {
+ DrawDynamite(x, y);
+ PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
+
+ return;
+ }
+ }
+
+ StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
+
+ Bang(x, y);
+}
+
+static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
+{
+ boolean num_checked_players = 0;
+ int i;
+
+ for (i = 0; i < MAX_PLAYERS; i++)
+ {
+ if (stored_player[i].active)
+ {
+ int sx = stored_player[i].jx;
+ int sy = stored_player[i].jy;
+
+ if (num_checked_players == 0)
+ {
+ *sx1 = *sx2 = sx;
+ *sy1 = *sy2 = sy;
+ }
+ else
+ {
+ *sx1 = MIN(*sx1, sx);
+ *sy1 = MIN(*sy1, sy);
+ *sx2 = MAX(*sx2, sx);
+ *sy2 = MAX(*sy2, sy);
+ }
+
+ num_checked_players++;
+ }
+ }
+}
+
+static boolean checkIfAllPlayersFitToScreen_RND()
+{
+ int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
+
+ setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
+
+ return (sx2 - sx1 < SCR_FIELDX &&
+ sy2 - sy1 < SCR_FIELDY);
+}
+
+static void setScreenCenteredToAllPlayers(int *sx, int *sy)
+{
+ int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
+
+ setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
+
+ *sx = (sx1 + sx2) / 2;
+ *sy = (sy1 + sy2) / 2;
+}
+
+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);
+ int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
+ int wait_delay_value = (no_delay ? 0 : frame_delay_value);
+
+ if (quick_relocation)
+ {
+ if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
+ {
+ if (!level.shifted_relocation || center_screen)
+ {
+ /* quick relocation (without scrolling), with centering of 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);
+
+ 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
+ {
+#if 1
+ if (!level.shifted_relocation || center_screen)
+ {
+ /* quick relocation (without scrolling), with centering of 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);
+
+ 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
+ /* quick relocation (without scrolling), inside visible screen area */
+
+ int offset = game.scroll_delay_value;
+
+ if ((move_dir == MV_LEFT && scroll_x > x - MIDPOSX + offset) ||
+ (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
+ scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
+
+ if ((move_dir == MV_UP && scroll_y > y - MIDPOSY + offset) ||
+ (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
+ scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
+
+ /* don't scroll over playfield boundaries */
+ if (scroll_x < SBX_Left || scroll_x > SBX_Right)
+ scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
+
+ /* don't scroll over playfield boundaries */
+ if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
+ scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
+#endif
+ }
+
+ RedrawPlayfield(TRUE, 0,0,0,0);
+ }
+ else
+ {
+#if 1
+ int scroll_xx, scroll_yy;
+
+ if (!level.shifted_relocation || center_screen)
+ {
+ /* visible relocation (with scrolling), with centering of screen */
+
+ scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left :
+ x > SBX_Right + MIDPOSX ? SBX_Right :
+ x - MIDPOSX);
+
+ scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
+ y > SBY_Lower + MIDPOSY ? SBY_Lower :
+ y - MIDPOSY);
+ }
+ else
+ {
+ /* visible relocation (with 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);
+
+ int offset_x = x + (scroll_x - center_scroll_x);
+ int offset_y = y + (scroll_y - center_scroll_y);
+
+ scroll_xx = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
+ offset_x > SBX_Right + MIDPOSX ? SBX_Right :
+ offset_x - MIDPOSX);
+
+ scroll_yy = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
+ offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
+ offset_y - MIDPOSY);
+ }
+
+#else
+
+ /* visible relocation (with scrolling), with centering of screen */
+
+ int scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left :
+ x > SBX_Right + MIDPOSX ? SBX_Right :
+ x - MIDPOSX);
+
+ int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
+ y > SBY_Lower + MIDPOSY ? SBY_Lower :
+ y - MIDPOSY);
+#endif
+
+ ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
+
+ while (scroll_x != scroll_xx || scroll_y != scroll_yy)
+ {
+ int dx = 0, dy = 0;
+ int fx = FX, fy = FY;
+
+ dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
+ dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
+
+ if (dx == 0 && dy == 0) /* no scrolling needed at all */
+ break;
+
+ scroll_x -= dx;
+ scroll_y -= dy;
+
+ fx += dx * TILEX / 2;
+ fy += dy * TILEY / 2;
+
+ ScrollLevel(dx, dy);
+ DrawAllPlayers();
+
+ /* scroll in two steps of half tile size to make things smoother */
+ BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
+ FlushDisplay();
+ Delay(wait_delay_value);
+
+ /* scroll second step to align at full tile size */
+ BackToFront();
+ Delay(wait_delay_value);
+ }
+
+ DrawAllPlayers();
+ BackToFront();
+ Delay(wait_delay_value);
+ }
+}
+
+void RelocatePlayer(int jx, int jy, int el_player_raw)
+{
+ int el_player = GET_PLAYER_ELEMENT(el_player_raw);
+ int player_nr = GET_PLAYER_NR(el_player);
+ struct PlayerInfo *player = &stored_player[player_nr];
+ boolean ffwd_delay = (tape.playing && tape.fast_forward);
+ boolean no_delay = (tape.warp_forward);
+ int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
+ int wait_delay_value = (no_delay ? 0 : frame_delay_value);
+ int old_jx = player->jx;
+ int old_jy = player->jy;
+ int old_element = Feld[old_jx][old_jy];
+ int element = Feld[jx][jy];
+ boolean player_relocated = (old_jx != jx || old_jy != jy);
+
+ int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
+ int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
+ int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
+ int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
+ int leave_side_horiz = move_dir_horiz;
+ int leave_side_vert = move_dir_vert;
+ int enter_side = enter_side_horiz | enter_side_vert;
+ int leave_side = leave_side_horiz | leave_side_vert;
+
+ if (player->GameOver) /* do not reanimate dead player */
+ return;
+
+ if (!player_relocated) /* no need to relocate the player */
+ return;
+
+ if (IS_PLAYER(jx, jy)) /* player already placed at new position */
+ {
+ RemoveField(jx, jy); /* temporarily remove newly placed player */
+ DrawLevelField(jx, jy);
+ }
+
+ if (player->present)
+ {
+ while (player->MovPos)
+ {
+ ScrollPlayer(player, SCROLL_GO_ON);
+ ScrollScreen(NULL, SCROLL_GO_ON);
+
+ AdvanceFrameAndPlayerCounters(player->index_nr);
+
+ DrawPlayer(player);
+
+ BackToFront();
+ Delay(wait_delay_value);
+ }
+
+ DrawPlayer(player); /* needed here only to cleanup last field */
+ DrawLevelField(player->jx, player->jy); /* remove player graphic */
+
+ player->is_moving = FALSE;
+ }
+
+ if (IS_CUSTOM_ELEMENT(old_element))
+ CheckElementChangeByPlayer(old_jx, old_jy, old_element,
+ CE_LEFT_BY_PLAYER,
+ player->index_bit, leave_side);
+
+ CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
+ CE_PLAYER_LEAVES_X,
+ player->index_bit, leave_side);
+
+ Feld[jx][jy] = el_player;
+ InitPlayerField(jx, jy, el_player, TRUE);
+
+ /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
+ possible that the relocation target field did not contain a player element,
+ but a walkable element, to which the new player was relocated -- in this
+ case, restore that (already initialized!) element on the player field */
+ if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
+ {
+ Feld[jx][jy] = element; /* restore previously existing element */
+#if 0
+ /* !!! do not initialize already initialized element a second time !!! */
+ /* (this causes at least problems with "element creation" CE trigger for
+ already existing elements, and existing Sokoban fields counted twice) */
+ InitField(jx, jy, FALSE);
+#endif
+ }
+
+ /* only visually relocate centered player */
+ DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
+ FALSE, level.instant_relocation);
+
+ TestIfPlayerTouchesBadThing(jx, jy);
+ TestIfPlayerTouchesCustomElement(jx, jy);
+
+ if (IS_CUSTOM_ELEMENT(element))
+ CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
+ player->index_bit, enter_side);
+
+ CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
+ player->index_bit, enter_side);
+
+#if 1
+ if (player->is_switching)
+ {
+ /* ensure that relocation while still switching an element does not cause
+ a new element to be treated as also switched directly after relocation
+ (this is important for teleporter switches that teleport the player to
+ a place where another teleporter switch is in the same direction, which
+ would then incorrectly be treated as immediately switched before the
+ direction key that caused the switch was released) */
+
+ player->switch_x += jx - old_jx;
+ player->switch_y += jy - old_jy;
+ }
+#endif
+}
+
+void Explode(int ex, int ey, int phase, int mode)
+{
+ int x, y;
+ int last_phase;
+ int border_element;
+
+ /* !!! eliminate this variable !!! */
+ int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
+
+ if (game.explosions_delayed)
+ {
+ ExplodeField[ex][ey] = mode;
+ return;
+ }
+
+ if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
+ {
+ int center_element = Feld[ex][ey];
+ int artwork_element, explosion_element; /* set these values later */
+
+#if 0
+ /* --- This is only really needed (and now handled) in "Impact()". --- */
+ /* do not explode moving elements that left the explode field in time */
+ if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
+ center_element == EL_EMPTY &&
+ (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
+ return;
+#endif
+
+#if 0
+ /* !!! at this place, the center element may be EL_BLOCKED !!! */
+ if (mode == EX_TYPE_NORMAL ||
+ mode == EX_TYPE_CENTER ||
+ mode == EX_TYPE_CROSS)
+ PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
+#endif
+
+ /* remove things displayed in background while burning dynamite */
+ if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
+ Back[ex][ey] = 0;
+
+ if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
+ {
+ /* put moving element to center field (and let it explode there) */
+ center_element = MovingOrBlocked2Element(ex, ey);
+ RemoveMovingField(ex, ey);
+ Feld[ex][ey] = center_element;
+ }
+
+ /* now "center_element" is finally determined -- set related values now */
+ artwork_element = center_element; /* for custom player artwork */
+ explosion_element = center_element; /* for custom player artwork */
+
+ if (IS_PLAYER(ex, ey))
+ {
+ int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
+
+ artwork_element = stored_player[player_nr].artwork_element;
+
+ if (level.use_explosion_element[player_nr])
+ {
+ explosion_element = level.explosion_element[player_nr];
+ artwork_element = explosion_element;
+ }
+ }
+
+#if 1
+ if (mode == EX_TYPE_NORMAL ||
+ mode == EX_TYPE_CENTER ||
+ mode == EX_TYPE_CROSS)
+ PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
+#endif
+
+ last_phase = element_info[explosion_element].explosion_delay + 1;
+
+ for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
+ {
+ int xx = x - ex + 1;
+ int yy = y - ey + 1;
+ int element;
+
+ if (!IN_LEV_FIELD(x, y) ||
+ (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
+ (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
+ continue;
+
+ element = Feld[x][y];
+
+ if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
+ {
+ element = MovingOrBlocked2Element(x, y);
+
+ if (!IS_EXPLOSION_PROOF(element))
+ RemoveMovingField(x, y);
+ }
+
+ /* indestructible elements can only explode in center (but not flames) */
+ if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
+ mode == EX_TYPE_BORDER)) ||
+ element == EL_FLAMES)
+ continue;
+
+ /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
+ behaviour, for example when touching a yamyam that explodes to rocks
+ with active deadly shield, a rock is created under the player !!! */
+ /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
+#if 0
+ if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
+ (game.engine_version < VERSION_IDENT(3,1,0,0) ||
+ (x == ex && y == ey && mode != EX_TYPE_BORDER)))
+#else
+ if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
+#endif
+ {
+ if (IS_ACTIVE_BOMB(element))
+ {
+ /* re-activate things under the bomb like gate or penguin */
+ Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
+ Back[x][y] = 0;
+ }
+
+ continue;
+ }
+
+ /* save walkable background elements while explosion on same tile */
+ if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
+ (x != ex || y != ey || mode == EX_TYPE_BORDER))
+ Back[x][y] = element;
+
+ /* ignite explodable elements reached by other explosion */
+ if (element == EL_EXPLOSION)
+ element = Store2[x][y];
+
+ if (AmoebaNr[x][y] &&
+ (element == EL_AMOEBA_FULL ||
+ element == EL_BD_AMOEBA ||
+ element == EL_AMOEBA_GROWING))
+ {
+ AmoebaCnt[AmoebaNr[x][y]]--;
+ AmoebaCnt2[AmoebaNr[x][y]]--;
+ }
+
+ RemoveField(x, y);
+
+ if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
+ {
+ int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
+
+ Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
+
+ if (PLAYERINFO(ex, ey)->use_murphy)
+ Store[x][y] = EL_EMPTY;
+ }
+
+ /* !!! check this case -- currently needed for rnd_rado_negundo_v,
+ !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
+ else if (ELEM_IS_PLAYER(center_element))
+ Store[x][y] = EL_EMPTY;
+ else if (center_element == EL_YAMYAM)
+ Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
+ else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
+ Store[x][y] = element_info[center_element].content.e[xx][yy];
+#if 1
+ /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
+ (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
+ otherwise) -- FIX THIS !!! */
+ else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
+ Store[x][y] = element_info[element].content.e[1][1];
+#else
+ else if (!CAN_EXPLODE(element))
+ Store[x][y] = element_info[element].content.e[1][1];
+#endif
+ else
+ Store[x][y] = EL_EMPTY;
+
+ if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
+ center_element == EL_AMOEBA_TO_DIAMOND)
+ Store2[x][y] = element;
+
+ Feld[x][y] = EL_EXPLOSION;
+ GfxElement[x][y] = artwork_element;
+
+ ExplodePhase[x][y] = 1;
+ ExplodeDelay[x][y] = last_phase;
+
+ Stop[x][y] = TRUE;
+ }
+
+ if (center_element == EL_YAMYAM)
+ game.yamyam_content_nr =
+ (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
+
+ return;
+ }
+
+ if (Stop[ex][ey])
+ return;
+
+ x = ex;
+ y = ey;
+
+ if (phase == 1)
+ GfxFrame[x][y] = 0; /* restart explosion animation */
+
+ last_phase = ExplodeDelay[x][y];
+
+ ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
+
+#ifdef DEBUG
+
+ /* activate this even in non-DEBUG version until cause for crash in
+ getGraphicAnimationFrame() (see below) is found and eliminated */
+
+#endif
+#if 1
+
+#if 1
+ /* this can happen if the player leaves an explosion just in time */
+ if (GfxElement[x][y] == EL_UNDEFINED)
+ GfxElement[x][y] = EL_EMPTY;
+#else
+ if (GfxElement[x][y] == EL_UNDEFINED)
+ {
+ printf("\n\n");
+ printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
+ printf("Explode(): This should never happen!\n");
+ printf("\n\n");
+
+ GfxElement[x][y] = EL_EMPTY;
+ }
+#endif
+
+#endif
+
+ border_element = Store2[x][y];
+ if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
+ border_element = StorePlayer[x][y];
+
+ if (phase == element_info[border_element].ignition_delay ||
+ phase == last_phase)
+ {
+ boolean border_explosion = FALSE;
+
+ if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
+ !PLAYER_EXPLOSION_PROTECTED(x, y))
+ {
+ KillPlayerUnlessExplosionProtected(x, y);
+ border_explosion = TRUE;
+ }
+ else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
+ {
+ Feld[x][y] = Store2[x][y];
+ Store2[x][y] = 0;
+ Bang(x, y);
+ border_explosion = TRUE;
+ }
+ else if (border_element == EL_AMOEBA_TO_DIAMOND)
+ {
+ AmoebeUmwandeln(x, y);
+ Store2[x][y] = 0;
+ border_explosion = TRUE;
+ }
+
+ /* if an element just explodes due to another explosion (chain-reaction),
+ do not immediately end the new explosion when it was the last frame of
+ the explosion (as it would be done in the following "if"-statement!) */
+ if (border_explosion && phase == last_phase)
+ return;
+ }
+
+ if (phase == last_phase)
+ {
+ int element;
+
+ element = Feld[x][y] = Store[x][y];
+ Store[x][y] = Store2[x][y] = 0;
+ GfxElement[x][y] = EL_UNDEFINED;
+
+ /* player can escape from explosions and might therefore be still alive */
+ if (element >= EL_PLAYER_IS_EXPLODING_1 &&
+ element <= EL_PLAYER_IS_EXPLODING_4)
+ {
+ int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
+ int explosion_element = EL_PLAYER_1 + player_nr;
+ int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
+ int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
+
+ if (level.use_explosion_element[player_nr])
+ explosion_element = level.explosion_element[player_nr];
+
+ Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
+ element_info[explosion_element].content.e[xx][yy]);
+ }
+
+ /* restore probably existing indestructible background element */
+ if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
+ element = Feld[x][y] = Back[x][y];
+ Back[x][y] = 0;
+
+ MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
+ GfxDir[x][y] = MV_NONE;
+ ChangeDelay[x][y] = 0;
+ ChangePage[x][y] = -1;
+
+#if USE_NEW_CUSTOM_VALUE
+ CustomValue[x][y] = 0;
+#endif
+
+ InitField_WithBug2(x, y, FALSE);
+
+ TEST_DrawLevelField(x, y);
+
+ TestIfElementTouchesCustomElement(x, y);
+
+ if (GFX_CRUMBLED(element))
+ TEST_DrawLevelFieldCrumbledNeighbours(x, y);
+
+ if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
+ StorePlayer[x][y] = 0;
+
+ if (ELEM_IS_PLAYER(element))
+ RelocatePlayer(x, y, element);
+ }
+ else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
+ {
+ int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
+ int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
+
+ if (phase == delay)
+ TEST_DrawLevelFieldCrumbled(x, y);
+
+ if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
+ {
+ DrawLevelElement(x, y, Back[x][y]);
+ DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
+ }
+ else if (IS_WALKABLE_UNDER(Back[x][y]))
+ {
+ DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
+ DrawLevelElementThruMask(x, y, Back[x][y]);
+ }
+ else if (!IS_WALKABLE_INSIDE(Back[x][y]))
+ DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
+ }
+}
+
+void DynaExplode(int ex, int ey)
+{
+ int i, j;
+ int dynabomb_element = Feld[ex][ey];
+ int dynabomb_size = 1;
+ boolean dynabomb_xl = FALSE;
+ struct PlayerInfo *player;
+ static int xy[4][2] =
+ {
+ { 0, -1 },
+ { -1, 0 },
+ { +1, 0 },
+ { 0, +1 }
+ };
+
+ if (IS_ACTIVE_BOMB(dynabomb_element))
+ {
+ player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
+ dynabomb_size = player->dynabomb_size;
+ dynabomb_xl = player->dynabomb_xl;
+ player->dynabombs_left++;
+ }
+
+ Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
+
+ for (i = 0; i < NUM_DIRECTIONS; i++)
+ {
+ for (j = 1; j <= dynabomb_size; j++)
+ {
+ int x = ex + j * xy[i][0];
+ int y = ey + j * xy[i][1];
+ int element;
+
+ if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
+ break;
+
+ element = Feld[x][y];
+
+ /* do not restart explosions of fields with active bombs */
+ if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
+ continue;
+
+ Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
+
+ if (element != EL_EMPTY && element != EL_EXPLOSION &&
+ !IS_DIGGABLE(element) && !dynabomb_xl)
+ break;
+ }
+ }
+}
+
+void Bang(int x, int y)
+{
+ int element = MovingOrBlocked2Element(x, y);
+ int explosion_type = EX_TYPE_NORMAL;
+
+ if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
+ {
+ struct PlayerInfo *player = PLAYERINFO(x, y);
+
+#if USE_FIX_CE_ACTION_WITH_PLAYER
+ element = Feld[x][y] = player->initial_element;
+#else
+ element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
+ player->element_nr);
+#endif
+
+ if (level.use_explosion_element[player->index_nr])
+ {
+ int explosion_element = level.explosion_element[player->index_nr];
+
+ if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
+ explosion_type = EX_TYPE_CROSS;
+ else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
+ explosion_type = EX_TYPE_CENTER;
+ }
+ }
+
+ switch (element)
+ {
+ case EL_BUG:
+ case EL_SPACESHIP:
+ case EL_BD_BUTTERFLY:
+ case EL_BD_FIREFLY:
+ case EL_YAMYAM:
+ case EL_DARK_YAMYAM:
+ case EL_ROBOT:
+ case EL_PACMAN:
+ case EL_MOLE:
+ RaiseScoreElement(element);
+ break;
+
+ 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_DYNABOMB_INCREASE_NUMBER:
+ case EL_DYNABOMB_INCREASE_SIZE:
+ case EL_DYNABOMB_INCREASE_POWER:
+ 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:
+ case EL_AMOEBA_TO_DIAMOND:
+ if (!IS_PLAYER(x, y)) /* penguin and player may be at same field */
+ explosion_type = EX_TYPE_CENTER;
+ break;
+
+ default:
+ if (element_info[element].explosion_type == EXPLODES_CROSS)
+ explosion_type = EX_TYPE_CROSS;
+ else if (element_info[element].explosion_type == EXPLODES_1X1)
+ explosion_type = EX_TYPE_CENTER;
+ break;
+ }
+
+ if (explosion_type == EX_TYPE_DYNA)
+ DynaExplode(x, y);
+ else
+ Explode(x, y, EX_PHASE_START, explosion_type);
+
+ CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
+}
+
+void SplashAcid(int x, int y)
+{
+ if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
+ (!IN_LEV_FIELD(x - 1, y - 2) ||
+ !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
+ Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
+
+ if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
+ (!IN_LEV_FIELD(x + 1, y - 2) ||
+ !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
+ Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
+
+ PlayLevelSound(x, y, SND_ACID_SPLASHING);
+}
+
+static void InitBeltMovement()
+{
+ static int belt_base_element[4] =
+ {
+ EL_CONVEYOR_BELT_1_LEFT,
+ EL_CONVEYOR_BELT_2_LEFT,
+ EL_CONVEYOR_BELT_3_LEFT,
+ EL_CONVEYOR_BELT_4_LEFT
+ };
+ static int belt_base_active_element[4] =
+ {
+ EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
+ EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
+ EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
+ EL_CONVEYOR_BELT_4_LEFT_ACTIVE
+ };
+
+ int x, y, i, j;
+
+ /* set frame order for belt animation graphic according to belt direction */
+ for (i = 0; i < NUM_BELTS; i++)
+ {
+ int belt_nr = i;
+
+ for (j = 0; j < NUM_BELT_PARTS; j++)
+ {
+ int element = belt_base_active_element[belt_nr] + j;
+ int graphic_1 = el2img(element);
+ int graphic_2 = el2panelimg(element);
+
+ if (game.belt_dir[i] == MV_LEFT)
+ {
+ graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
+ graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
+ }
+ else
+ {
+ graphic_info[graphic_1].anim_mode |= ANIM_REVERSE;
+ graphic_info[graphic_2].anim_mode |= ANIM_REVERSE;
+ }
+ }
+ }
+
+ SCAN_PLAYFIELD(x, y)
+ {
+ int element = Feld[x][y];
+
+ for (i = 0; i < NUM_BELTS; i++)
+ {
+ if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
+ {
+ int e_belt_nr = getBeltNrFromBeltElement(element);
+ int belt_nr = i;
+
+ if (e_belt_nr == belt_nr)
+ {
+ int belt_part = Feld[x][y] - belt_base_element[belt_nr];
+
+ Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
+ }
+ }
+ }
+ }
+}
+
+static void ToggleBeltSwitch(int x, int y)
+{
+ static int belt_base_element[4] =
+ {
+ EL_CONVEYOR_BELT_1_LEFT,
+ EL_CONVEYOR_BELT_2_LEFT,
+ EL_CONVEYOR_BELT_3_LEFT,
+ EL_CONVEYOR_BELT_4_LEFT
+ };
+ static int belt_base_active_element[4] =
+ {
+ EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
+ EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
+ EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
+ EL_CONVEYOR_BELT_4_LEFT_ACTIVE
+ };
+ static int belt_base_switch_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
+ };
+ static int belt_move_dir[4] =
+ {
+ MV_LEFT,
+ MV_NONE,
+ MV_RIGHT,
+ MV_NONE,
+ };
+
+ int element = Feld[x][y];
+ int belt_nr = getBeltNrFromBeltSwitchElement(element);
+ int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
+ int belt_dir = belt_move_dir[belt_dir_nr];
+ int xx, yy, i;
+
+ if (!IS_BELT_SWITCH(element))
+ return;
+
+ game.belt_dir_nr[belt_nr] = belt_dir_nr;
+ game.belt_dir[belt_nr] = belt_dir;
+
+ if (belt_dir_nr == 3)
+ belt_dir_nr = 1;
+
+ /* set frame order for belt animation graphic according to belt direction */
+ for (i = 0; i < NUM_BELT_PARTS; i++)
+ {
+ int element = belt_base_active_element[belt_nr] + i;
+ int graphic_1 = el2img(element);
+ int graphic_2 = el2panelimg(element);
+
+ if (belt_dir == MV_LEFT)
+ {
+ graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
+ graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
+ }
+ else
+ {
+ graphic_info[graphic_1].anim_mode |= ANIM_REVERSE;
+ graphic_info[graphic_2].anim_mode |= ANIM_REVERSE;
+ }
+ }
+
+ SCAN_PLAYFIELD(xx, yy)
+ {
+ int element = Feld[xx][yy];
+
+ if (IS_BELT_SWITCH(element))
+ {
+ int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
+
+ if (e_belt_nr == belt_nr)
+ {
+ Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
+ TEST_DrawLevelField(xx, yy);
+ }
+ }
+ else if (IS_BELT(element) && belt_dir != MV_NONE)
+ {
+ int e_belt_nr = getBeltNrFromBeltElement(element);
+
+ if (e_belt_nr == belt_nr)
+ {
+ int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
+
+ Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
+ TEST_DrawLevelField(xx, yy);
+ }
+ }
+ else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
+ {
+ int e_belt_nr = getBeltNrFromBeltActiveElement(element);
+
+ if (e_belt_nr == belt_nr)
+ {
+ int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
+
+ Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
+ TEST_DrawLevelField(xx, yy);
+ }
+ }
+ }
+}
+
+static void ToggleSwitchgateSwitch(int x, int y)
+{
+ int xx, yy;
+
+ game.switchgate_pos = !game.switchgate_pos;
+
+ SCAN_PLAYFIELD(xx, yy)
+ {
+ int element = Feld[xx][yy];
+
+#if !USE_BOTH_SWITCHGATE_SWITCHES
+ if (element == EL_SWITCHGATE_SWITCH_UP ||
+ element == EL_SWITCHGATE_SWITCH_DOWN)
+ {
+ Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
+ TEST_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;
+ TEST_DrawLevelField(xx, yy);
+ }
+#else
+ if (element == EL_SWITCHGATE_SWITCH_UP)
+ {
+ Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
+ TEST_DrawLevelField(xx, yy);
+ }
+ else if (element == EL_SWITCHGATE_SWITCH_DOWN)
+ {
+ Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
+ TEST_DrawLevelField(xx, yy);
+ }
+ else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
+ {
+ Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
+ TEST_DrawLevelField(xx, yy);
+ }
+ else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
+ {
+ Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
+ TEST_DrawLevelField(xx, yy);
+ }
+#endif
+ else if (element == EL_SWITCHGATE_OPEN ||
+ element == EL_SWITCHGATE_OPENING)
+ {
+ Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
+
+ PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
+ }
+ else if (element == EL_SWITCHGATE_CLOSED ||
+ element == EL_SWITCHGATE_CLOSING)
+ {
+ Feld[xx][yy] = EL_SWITCHGATE_OPENING;
+
+ PlayLevelSoundAction(xx, yy, ACTION_OPENING);
+ }
+ }
+}
+
+static int getInvisibleActiveFromInvisibleElement(int element)
+{
+ return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
+ element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
+ element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
+ element);
+}
+
+static int getInvisibleFromInvisibleActiveElement(int element)
+{
+ return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
+ element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
+ element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
+ element);
+}
+
+static void RedrawAllLightSwitchesAndInvisibleElements()
+{
+ int x, y;
+
+ SCAN_PLAYFIELD(x, y)
+ {
+ int element = Feld[x][y];
+
+ if (element == EL_LIGHT_SWITCH &&
+ game.light_time_left > 0)
+ {
+ Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
+ TEST_DrawLevelField(x, y);
+ }
+ else if (element == EL_LIGHT_SWITCH_ACTIVE &&
+ game.light_time_left == 0)
+ {
+ Feld[x][y] = EL_LIGHT_SWITCH;
+ TEST_DrawLevelField(x, y);
+ }
+ else if (element == EL_EMC_DRIPPER &&
+ game.light_time_left > 0)
+ {
+ Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
+ TEST_DrawLevelField(x, y);
+ }
+ else if (element == EL_EMC_DRIPPER_ACTIVE &&
+ game.light_time_left == 0)
+ {
+ Feld[x][y] = EL_EMC_DRIPPER;
+ TEST_DrawLevelField(x, y);
+ }
+ else if (element == EL_INVISIBLE_STEELWALL ||
+ element == EL_INVISIBLE_WALL ||
+ element == EL_INVISIBLE_SAND)
+ {
+ if (game.light_time_left > 0)
+ Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
+
+ TEST_DrawLevelField(x, y);
+
+ /* uncrumble neighbour fields, if needed */
+ if (element == EL_INVISIBLE_SAND)
+ TEST_DrawLevelFieldCrumbledNeighbours(x, y);
+ }
+ else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
+ element == EL_INVISIBLE_WALL_ACTIVE ||
+ element == EL_INVISIBLE_SAND_ACTIVE)
+ {
+ if (game.light_time_left == 0)
+ Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
+
+ TEST_DrawLevelField(x, y);
+
+ /* re-crumble neighbour fields, if needed */
+ if (element == EL_INVISIBLE_SAND)
+ TEST_DrawLevelFieldCrumbledNeighbours(x, y);
+ }
+ }
+}
+
+static void RedrawAllInvisibleElementsForLenses()
+{
+ int x, y;
+
+ SCAN_PLAYFIELD(x, y)
+ {
+ int element = Feld[x][y];
+
+ if (element == EL_EMC_DRIPPER &&
+ game.lenses_time_left > 0)
+ {
+ Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
+ TEST_DrawLevelField(x, y);
+ }
+ else if (element == EL_EMC_DRIPPER_ACTIVE &&
+ game.lenses_time_left == 0)
+ {
+ Feld[x][y] = EL_EMC_DRIPPER;
+ TEST_DrawLevelField(x, y);
+ }
+ else if (element == EL_INVISIBLE_STEELWALL ||
+ element == EL_INVISIBLE_WALL ||
+ element == EL_INVISIBLE_SAND)
+ {
+ if (game.lenses_time_left > 0)
+ Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
+
+ TEST_DrawLevelField(x, y);
+
+ /* uncrumble neighbour fields, if needed */
+ if (element == EL_INVISIBLE_SAND)
+ TEST_DrawLevelFieldCrumbledNeighbours(x, y);
+ }
+ else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
+ element == EL_INVISIBLE_WALL_ACTIVE ||
+ element == EL_INVISIBLE_SAND_ACTIVE)
+ {
+ if (game.lenses_time_left == 0)
+ Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
+
+ TEST_DrawLevelField(x, y);
+
+ /* re-crumble neighbour fields, if needed */
+ if (element == EL_INVISIBLE_SAND)
+ TEST_DrawLevelFieldCrumbledNeighbours(x, y);
+ }
+ }
+}
+
+static void RedrawAllInvisibleElementsForMagnifier()
+{
+ int x, y;
+
+ SCAN_PLAYFIELD(x, y)
+ {
+ int element = Feld[x][y];
+
+ if (element == EL_EMC_FAKE_GRASS &&
+ game.magnify_time_left > 0)
+ {
+ Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
+ TEST_DrawLevelField(x, y);
+ }
+ else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
+ game.magnify_time_left == 0)
+ {
+ Feld[x][y] = EL_EMC_FAKE_GRASS;
+ TEST_DrawLevelField(x, y);
+ }
+ else if (IS_GATE_GRAY(element) &&
+ game.magnify_time_left > 0)
+ {
+ Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
+ element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
+ IS_EM_GATE_GRAY(element) ?
+ element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
+ IS_EMC_GATE_GRAY(element) ?
+ element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
+ IS_DC_GATE_GRAY(element) ?
+ EL_DC_GATE_WHITE_GRAY_ACTIVE :
+ element);
+ TEST_DrawLevelField(x, y);
+ }
+ else if (IS_GATE_GRAY_ACTIVE(element) &&
+ game.magnify_time_left == 0)
+ {
+ Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
+ element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
+ IS_EM_GATE_GRAY_ACTIVE(element) ?
+ element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
+ IS_EMC_GATE_GRAY_ACTIVE(element) ?
+ element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
+ IS_DC_GATE_GRAY_ACTIVE(element) ?
+ EL_DC_GATE_WHITE_GRAY :
+ element);
+ TEST_DrawLevelField(x, y);
+ }
+ }
+}
+
+static void ToggleLightSwitch(int x, int y)
+{
+ int element = Feld[x][y];
+
+ game.light_time_left =
+ (element == EL_LIGHT_SWITCH ?
+ level.time_light * FRAMES_PER_SECOND : 0);
+
+ RedrawAllLightSwitchesAndInvisibleElements();
+}
+
+static void ActivateTimegateSwitch(int x, int y)
+{
+ int xx, yy;
+
+ game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
+
+ SCAN_PLAYFIELD(xx, yy)
+ {
+ int element = Feld[xx][yy];
+
+ if (element == EL_TIMEGATE_CLOSED ||
+ element == EL_TIMEGATE_CLOSING)
+ {
+ Feld[xx][yy] = EL_TIMEGATE_OPENING;
+ PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
+ }
+
+ /*
+ else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
+ {
+ Feld[xx][yy] = EL_TIMEGATE_SWITCH;
+ TEST_DrawLevelField(xx, yy);
+ }
+ */
+
+ }
+
+#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)
+{
+ boolean last_line = (y == lev_fieldy - 1);
+ boolean object_hit = FALSE;
+ boolean impact = (last_line || object_hit);
+ int element = Feld[x][y];
+ int smashed = EL_STEELWALL;
+
+ if (!last_line) /* check if element below was hit */
+ {
+ if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
+ return;
+
+ object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
+ MovDir[x][y + 1] != MV_DOWN ||
+ MovPos[x][y + 1] <= TILEY / 2));
+
+ /* do not smash moving elements that left the smashed field in time */
+ if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
+ ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
+ object_hit = FALSE;
+
+#if USE_QUICKSAND_IMPACT_BUGFIX
+ if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
+ {
+ RemoveMovingField(x, y + 1);
+ Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
+ Feld[x][y + 2] = EL_ROCK;
+ TEST_DrawLevelField(x, y + 2);
+
+ 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;
+ TEST_DrawLevelField(x, y + 2);
+
+ object_hit = TRUE;
+ }
+#endif
+
+ if (object_hit)
+ smashed = MovingOrBlocked2Element(x, y + 1);
+
+ impact = (last_line || object_hit);
+ }
+
+ if (!last_line && smashed == EL_ACID) /* element falls into acid */
+ {
+ SplashAcid(x, y + 1);
+ return;
+ }
+
+ /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
+ /* only reset graphic animation if graphic really changes after impact */
+ if (impact &&
+ el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
+ {
+ ResetGfxAnimation(x, y);
+ TEST_DrawLevelField(x, y);
+ }
+
+ if (impact && CAN_EXPLODE_IMPACT(element))
+ {
+ Bang(x, y);
+ return;
+ }
+ else if (impact && element == EL_PEARL &&
+ smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
+ {
+ ResetGfxAnimation(x, y);
+
+ Feld[x][y] = EL_PEARL_BREAKING;
+ PlayLevelSound(x, y, SND_PEARL_BREAKING);
+ return;
+ }
+ else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
+ {
+ PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
+
+ return;
+ }
+
+ if (impact && element == EL_AMOEBA_DROP)
+ {
+ if (object_hit && IS_PLAYER(x, y + 1))
+ KillPlayerUnlessEnemyProtected(x, y + 1);
+ else if (object_hit && smashed == EL_PENGUIN)
+ Bang(x, y + 1);
+ else
+ {
+ Feld[x][y] = EL_AMOEBA_GROWING;
+ Store[x][y] = EL_AMOEBA_WET;
+
+ ResetRandomAnimationValue(x, y);
+ }
+ return;
+ }
+
+ if (object_hit) /* check which object was hit */
+ {
+ 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 :
+ 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 :
+ smashed == EL_BD_MAGIC_WALL ?
+ SND_BD_MAGIC_WALL_ACTIVATING :
+ SND_DC_MAGIC_WALL_ACTIVATING));
+ }
+
+ if (IS_PLAYER(x, y + 1))
+ {
+ if (CAN_SMASH_PLAYER(element))
+ {
+ KillPlayerUnlessEnemyProtected(x, y + 1);
+ return;
+ }
+ }
+ else if (smashed == EL_PENGUIN)
+ {
+ if (CAN_SMASH_PLAYER(element))
+ {
+ Bang(x, y + 1);
+ return;
+ }
+ }
+ else if (element == EL_BD_DIAMOND)
+ {
+ if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
+ {
+ Bang(x, y + 1);
+ return;
+ }
+ }
+ else if (((element == EL_SP_INFOTRON ||
+ element == EL_SP_ZONK) &&
+ (smashed == EL_SP_SNIKSNAK ||
+ smashed == EL_SP_ELECTRON ||
+ smashed == EL_SP_DISK_ORANGE)) ||
+ (element == EL_SP_INFOTRON &&
+ smashed == EL_SP_DISK_YELLOW))
+ {
+ Bang(x, y + 1);
+ return;
+ }
+ else if (CAN_SMASH_EVERYTHING(element))
+ {
+ if (IS_CLASSIC_ENEMY(smashed) ||
+ CAN_EXPLODE_SMASHED(smashed))
+ {
+ Bang(x, y + 1);
+ return;
+ }
+ else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
+ {
+ if (smashed == EL_LAMP ||
+ smashed == EL_LAMP_ACTIVE)
+ {
+ Bang(x, y + 1);
+ return;
+ }
+ else if (smashed == EL_NUT)
+ {
+ Feld[x][y + 1] = EL_NUT_BREAKING;
+ PlayLevelSound(x, y, SND_NUT_BREAKING);
+ RaiseScoreElement(EL_NUT);
+ return;
+ }
+ else if (smashed == EL_PEARL)
+ {
+ ResetGfxAnimation(x, y);
+
+ Feld[x][y + 1] = EL_PEARL_BREAKING;
+ PlayLevelSound(x, y, SND_PEARL_BREAKING);
+ return;
+ }
+ else if (smashed == EL_DIAMOND)
+ {
+ Feld[x][y + 1] = EL_DIAMOND_BREAKING;
+ PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
+ return;
+ }
+ else if (IS_BELT_SWITCH(smashed))
+ {
+ ToggleBeltSwitch(x, y + 1);
+ }
+ else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
+ smashed == EL_SWITCHGATE_SWITCH_DOWN ||
+ smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
+ smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
+ {
+ ToggleSwitchgateSwitch(x, y + 1);
+ }
+ else if (smashed == EL_LIGHT_SWITCH ||
+ smashed == EL_LIGHT_SWITCH_ACTIVE)
+ {
+ ToggleLightSwitch(x, y + 1);
+ }
+ else
+ {
+#if 0
+ TestIfElementSmashesCustomElement(x, y, MV_DOWN);
+#endif
+
+ CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
+
+ CheckElementChangeBySide(x, y + 1, smashed, element,
+ CE_SWITCHED, CH_SIDE_TOP);
+ CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
+ CH_SIDE_TOP);
+ }
+ }
+ else
+ {
+ CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
+ }
+ }
+ }
+
+ /* 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_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;
+ }
+
+ /* play sound of object that hits the ground */
+ if (last_line || object_hit)
+ PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
+}
+
+inline static void TurnRoundExt(int x, int y)
+{
+ static struct
+ {
+ int dx, dy;
+ } move_xy[] =
+ {
+ { 0, 0 },
+ { -1, 0 },
+ { +1, 0 },
+ { 0, 0 },
+ { 0, -1 },
+ { 0, 0 }, { 0, 0 }, { 0, 0 },
+ { 0, +1 }
+ };
+ static struct
+ {
+ int left, right, back;
+ } turn[] =
+ {
+ { 0, 0, 0 },
+ { MV_DOWN, MV_UP, MV_RIGHT },
+ { MV_UP, MV_DOWN, MV_LEFT },
+ { 0, 0, 0 },
+ { MV_LEFT, MV_RIGHT, MV_DOWN },
+ { 0, 0, 0 },
+ { 0, 0, 0 },
+ { 0, 0, 0 },
+ { MV_RIGHT, MV_LEFT, MV_UP }
+ };
+
+ int element = Feld[x][y];
+ int move_pattern = element_info[element].move_pattern;
+
+ int old_move_dir = MovDir[x][y];
+ int left_dir = turn[old_move_dir].left;
+ int right_dir = turn[old_move_dir].right;
+ int back_dir = turn[old_move_dir].back;
+
+ int left_dx = move_xy[left_dir].dx, left_dy = move_xy[left_dir].dy;
+ int right_dx = move_xy[right_dir].dx, right_dy = move_xy[right_dir].dy;
+ int move_dx = move_xy[old_move_dir].dx, move_dy = move_xy[old_move_dir].dy;
+ int back_dx = move_xy[back_dir].dx, back_dy = move_xy[back_dir].dy;
+
+ int left_x = x + left_dx, left_y = y + left_dy;
+ int right_x = x + right_dx, right_y = y + right_dy;
+ int move_x = x + move_dx, move_y = y + move_dy;
- if (!IN_LEV_FIELD(x, y + 1) || Feld[x][y + 1] != EL_ACID)
- return;
+ int xx, yy;
- if (element != EL_ACID_SPLASH_LEFT &&
- element != EL_ACID_SPLASH_RIGHT)
+ if (element == EL_BUG || element == EL_BD_BUTTERFLY)
{
- PlayLevelSound(x, y, SND_ACID_SPLASHING);
+ TestIfBadThingTouchesOtherBadThing(x, y);
- if (IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y) &&
- (!IN_LEV_FIELD(x - 1, y - 1) ||
- !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 1))))
- Feld[x - 1][y] = EL_ACID_SPLASH_LEFT;
+ if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
+ MovDir[x][y] = right_dir;
+ else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
+ MovDir[x][y] = left_dir;
- if (IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y) &&
- (!IN_LEV_FIELD(x + 1, y - 1) ||
- !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 1))))
- Feld[x + 1][y] = EL_ACID_SPLASH_RIGHT;
+ if (element == EL_BUG && MovDir[x][y] != old_move_dir)
+ MovDelay[x][y] = 9;
+ else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
+ MovDelay[x][y] = 1;
}
-#endif
-}
+ else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
+ {
+ TestIfBadThingTouchesOtherBadThing(x, y);
-static void InitBeltMovement()
-{
- static int belt_base_element[4] =
+ if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
+ MovDir[x][y] = left_dir;
+ else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
+ MovDir[x][y] = right_dir;
+
+ if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
+ MovDelay[x][y] = 9;
+ else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
+ MovDelay[x][y] = 1;
+ }
+ else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
{
- EL_CONVEYOR_BELT_1_LEFT,
- EL_CONVEYOR_BELT_2_LEFT,
- EL_CONVEYOR_BELT_3_LEFT,
- EL_CONVEYOR_BELT_4_LEFT
- };
- static int belt_base_active_element[4] =
+ TestIfBadThingTouchesOtherBadThing(x, y);
+
+ if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
+ MovDir[x][y] = left_dir;
+ else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
+ MovDir[x][y] = right_dir;
+
+ if (MovDir[x][y] != old_move_dir)
+ MovDelay[x][y] = 9;
+ }
+ else if (element == EL_YAMYAM)
{
- EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
- EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
- EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
- EL_CONVEYOR_BELT_4_LEFT_ACTIVE
- };
+ boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
+ boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
- int x, y, i, j;
+ if (can_turn_left && can_turn_right)
+ MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
+ else if (can_turn_left)
+ MovDir[x][y] = (RND(2) ? left_dir : back_dir);
+ else if (can_turn_right)
+ MovDir[x][y] = (RND(2) ? right_dir : back_dir);
+ else
+ MovDir[x][y] = back_dir;
- /* set frame order for belt animation graphic according to belt direction */
- for (i = 0; i < NUM_BELTS; i++)
+ MovDelay[x][y] = 16 + 16 * RND(3);
+ }
+ else if (element == EL_DARK_YAMYAM)
{
- int belt_nr = i;
+ boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
+ left_x, left_y);
+ boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
+ right_x, right_y);
- for (j = 0; j < NUM_BELT_PARTS; j++)
- {
- int element = belt_base_active_element[belt_nr] + j;
- int graphic = el2img(element);
+ if (can_turn_left && can_turn_right)
+ MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
+ else if (can_turn_left)
+ MovDir[x][y] = (RND(2) ? left_dir : back_dir);
+ else if (can_turn_right)
+ MovDir[x][y] = (RND(2) ? right_dir : back_dir);
+ else
+ MovDir[x][y] = back_dir;
- if (game.belt_dir[i] == MV_LEFT)
- graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
- else
- graphic_info[graphic].anim_mode |= ANIM_REVERSE;
- }
+ MovDelay[x][y] = 16 + 16 * RND(3);
}
+ else if (element == EL_PACMAN)
+ {
+ boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
+ boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
+
+ if (can_turn_left && can_turn_right)
+ MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
+ else if (can_turn_left)
+ MovDir[x][y] = (RND(2) ? left_dir : back_dir);
+ else if (can_turn_right)
+ MovDir[x][y] = (RND(2) ? right_dir : back_dir);
+ else
+ MovDir[x][y] = back_dir;
- for (y = 0; y < lev_fieldy; y++)
+ MovDelay[x][y] = 6 + RND(40);
+ }
+ else if (element == EL_PIG)
{
- for (x = 0; x < lev_fieldx; x++)
+ boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
+ boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
+ boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
+ boolean should_turn_left, should_turn_right, should_move_on;
+ int rnd_value = 24;
+ int rnd = RND(rnd_value);
+
+ should_turn_left = (can_turn_left &&
+ (!can_move_on ||
+ IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
+ y + back_dy + left_dy)));
+ should_turn_right = (can_turn_right &&
+ (!can_move_on ||
+ IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
+ y + back_dy + right_dy)));
+ should_move_on = (can_move_on &&
+ (!can_turn_left ||
+ !can_turn_right ||
+ IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
+ y + move_dy + left_dy) ||
+ IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
+ y + move_dy + right_dy)));
+
+ if (should_turn_left || should_turn_right || should_move_on)
{
- int element = Feld[x][y];
+ if (should_turn_left && should_turn_right && should_move_on)
+ MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
+ rnd < 2 * rnd_value / 3 ? right_dir :
+ old_move_dir);
+ else if (should_turn_left && should_turn_right)
+ MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
+ else if (should_turn_left && should_move_on)
+ MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
+ else if (should_turn_right && should_move_on)
+ MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
+ else if (should_turn_left)
+ MovDir[x][y] = left_dir;
+ else if (should_turn_right)
+ MovDir[x][y] = right_dir;
+ else if (should_move_on)
+ MovDir[x][y] = old_move_dir;
+ }
+ else if (can_move_on && rnd > rnd_value / 8)
+ MovDir[x][y] = old_move_dir;
+ else if (can_turn_left && can_turn_right)
+ MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
+ else if (can_turn_left && rnd > rnd_value / 8)
+ MovDir[x][y] = left_dir;
+ else if (can_turn_right && rnd > rnd_value/8)
+ MovDir[x][y] = right_dir;
+ else
+ MovDir[x][y] = back_dir;
- for (i = 0; i < NUM_BELTS; i++)
- {
- if (IS_BELT(element) && game.belt_dir[i] != MV_NO_MOVING)
- {
- int e_belt_nr = getBeltNrFromBeltElement(element);
- int belt_nr = i;
+ xx = x + move_xy[MovDir[x][y]].dx;
+ yy = y + move_xy[MovDir[x][y]].dy;
- if (e_belt_nr == belt_nr)
- {
- int belt_part = Feld[x][y] - belt_base_element[belt_nr];
+ if (!IN_LEV_FIELD(xx, yy) ||
+ (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
+ MovDir[x][y] = old_move_dir;
- Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
- }
- }
- }
- }
+ MovDelay[x][y] = 0;
}
-}
-
-static void ToggleBeltSwitch(int x, int y)
-{
- static int belt_base_element[4] =
- {
- EL_CONVEYOR_BELT_1_LEFT,
- EL_CONVEYOR_BELT_2_LEFT,
- EL_CONVEYOR_BELT_3_LEFT,
- EL_CONVEYOR_BELT_4_LEFT
- };
- static int belt_base_active_element[4] =
- {
- EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
- EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
- EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
- EL_CONVEYOR_BELT_4_LEFT_ACTIVE
- };
- static int belt_base_switch_element[4] =
+ else if (element == EL_DRAGON)
{
- EL_CONVEYOR_BELT_1_SWITCH_LEFT,
- EL_CONVEYOR_BELT_2_SWITCH_LEFT,
- EL_CONVEYOR_BELT_3_SWITCH_LEFT,
- EL_CONVEYOR_BELT_4_SWITCH_LEFT
- };
- static int belt_move_dir[4] =
+ boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
+ boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
+ boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
+ int rnd_value = 24;
+ int rnd = RND(rnd_value);
+
+ if (can_move_on && rnd > rnd_value / 8)
+ MovDir[x][y] = old_move_dir;
+ else if (can_turn_left && can_turn_right)
+ MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
+ else if (can_turn_left && rnd > rnd_value / 8)
+ MovDir[x][y] = left_dir;
+ else if (can_turn_right && rnd > rnd_value / 8)
+ MovDir[x][y] = right_dir;
+ else
+ MovDir[x][y] = back_dir;
+
+ xx = x + move_xy[MovDir[x][y]].dx;
+ yy = y + move_xy[MovDir[x][y]].dy;
+
+ if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
+ MovDir[x][y] = old_move_dir;
+
+ MovDelay[x][y] = 0;
+ }
+ else if (element == EL_MOLE)
{
- MV_LEFT,
- MV_NO_MOVING,
- MV_RIGHT,
- MV_NO_MOVING,
- };
+ boolean can_move_on =
+ (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
+ IS_AMOEBOID(Feld[move_x][move_y]) ||
+ Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
+ if (!can_move_on)
+ {
+ boolean can_turn_left =
+ (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
+ IS_AMOEBOID(Feld[left_x][left_y])));
- int element = Feld[x][y];
- int belt_nr = getBeltNrFromBeltSwitchElement(element);
- int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
- int belt_dir = belt_move_dir[belt_dir_nr];
- int xx, yy, i;
+ boolean can_turn_right =
+ (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
+ IS_AMOEBOID(Feld[right_x][right_y])));
- if (!IS_BELT_SWITCH(element))
- return;
+ if (can_turn_left && can_turn_right)
+ MovDir[x][y] = (RND(2) ? left_dir : right_dir);
+ else if (can_turn_left)
+ MovDir[x][y] = left_dir;
+ else
+ MovDir[x][y] = right_dir;
+ }
- game.belt_dir_nr[belt_nr] = belt_dir_nr;
- game.belt_dir[belt_nr] = belt_dir;
+ if (MovDir[x][y] != old_move_dir)
+ MovDelay[x][y] = 9;
+ }
+ else if (element == EL_BALLOON)
+ {
+ MovDir[x][y] = game.wind_direction;
+ MovDelay[x][y] = 0;
+ }
+ else if (element == EL_SPRING)
+ {
+#if USE_NEW_SPRING_BUMPER
+ if (MovDir[x][y] & MV_HORIZONTAL)
+ {
+ if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
+ !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
+ {
+ Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
+ ResetGfxAnimation(move_x, move_y);
+ TEST_DrawLevelField(move_x, move_y);
- if (belt_dir_nr == 3)
- belt_dir_nr = 1;
+ MovDir[x][y] = back_dir;
+ }
+ else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
+ SPRING_CAN_ENTER_FIELD(element, x, y + 1))
+ MovDir[x][y] = MV_NONE;
+ }
+#else
+ if (MovDir[x][y] & MV_HORIZONTAL &&
+ (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
+ SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
+ MovDir[x][y] = MV_NONE;
+#endif
- /* set frame order for belt animation graphic according to belt direction */
- for (i = 0; i < NUM_BELT_PARTS; i++)
+ MovDelay[x][y] = 0;
+ }
+ else if (element == EL_ROBOT ||
+ element == EL_SATELLITE ||
+ element == EL_PENGUIN ||
+ element == EL_EMC_ANDROID)
{
- int element = belt_base_active_element[belt_nr] + i;
- int graphic = el2img(element);
+ int attr_x = -1, attr_y = -1;
- if (belt_dir == MV_LEFT)
- graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
+ if (AllPlayersGone)
+ {
+ attr_x = ExitX;
+ attr_y = ExitY;
+ }
else
- graphic_info[graphic].anim_mode |= ANIM_REVERSE;
- }
-
- for (yy = 0; yy < lev_fieldy; yy++)
- {
- for (xx = 0; xx < lev_fieldx; xx++)
{
- int element = Feld[xx][yy];
+ int i;
- if (IS_BELT_SWITCH(element))
+ for (i = 0; i < MAX_PLAYERS; i++)
{
- int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
+ struct PlayerInfo *player = &stored_player[i];
+ int jx = player->jx, jy = player->jy;
- if (e_belt_nr == belt_nr)
+ if (!player->active)
+ continue;
+
+ if (attr_x == -1 ||
+ ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
{
- Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
- DrawLevelField(xx, yy);
+ attr_x = jx;
+ attr_y = jy;
}
}
- else if (IS_BELT(element) && belt_dir != MV_NO_MOVING)
- {
- int e_belt_nr = getBeltNrFromBeltElement(element);
+ }
- if (e_belt_nr == belt_nr)
- {
- int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
+ if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
+ (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
+ game.engine_version < VERSION_IDENT(3,1,0,0)))
+ {
+ attr_x = ZX;
+ attr_y = ZY;
+ }
- Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
- DrawLevelField(xx, yy);
- }
- }
- else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NO_MOVING)
+ if (element == EL_PENGUIN)
+ {
+ int i;
+ static int xy[4][2] =
+ {
+ { 0, -1 },
+ { -1, 0 },
+ { +1, 0 },
+ { 0, +1 }
+ };
+
+ for (i = 0; i < NUM_DIRECTIONS; i++)
{
- int e_belt_nr = getBeltNrFromBeltActiveElement(element);
+ int ex = x + xy[i][0];
+ int ey = y + xy[i][1];
- if (e_belt_nr == belt_nr)
+ 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))
{
- int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
-
- Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
- DrawLevelField(xx, yy);
+ attr_x = ex;
+ attr_y = ey;
+ break;
}
}
}
- }
-}
-static void ToggleSwitchgateSwitch(int x, int y)
-{
- int xx, yy;
+ MovDir[x][y] = MV_NONE;
+ if (attr_x < x)
+ MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
+ else if (attr_x > x)
+ MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
+ if (attr_y < y)
+ MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
+ else if (attr_y > y)
+ MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
- game.switchgate_pos = !game.switchgate_pos;
+ if (element == EL_ROBOT)
+ {
+ int newx, newy;
- for (yy = 0; yy < lev_fieldy; yy++)
- {
- for (xx = 0; xx < lev_fieldx; xx++)
+ if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
+ MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
+ Moving2Blocked(x, y, &newx, &newy);
+
+ if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
+ MovDelay[x][y] = 8 + 8 * !RND(3);
+ else
+ MovDelay[x][y] = 16;
+ }
+ else if (element == EL_PENGUIN)
{
- int element = Feld[xx][yy];
+ int newx, newy;
- if (element == EL_SWITCHGATE_SWITCH_UP ||
- element == EL_SWITCHGATE_SWITCH_DOWN)
- {
- Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
- DrawLevelField(xx, yy);
- }
- else if (element == EL_SWITCHGATE_OPEN ||
- element == EL_SWITCHGATE_OPENING)
- {
- Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
-#if 1
- PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
-#else
- PlayLevelSound(xx, yy, SND_SWITCHGATE_CLOSING);
-#endif
- }
- else if (element == EL_SWITCHGATE_CLOSED ||
- element == EL_SWITCHGATE_CLOSING)
+ MovDelay[x][y] = 1;
+
+ if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
{
- Feld[xx][yy] = EL_SWITCHGATE_OPENING;
-#if 1
- PlayLevelSoundAction(xx, yy, ACTION_OPENING);
-#else
- PlayLevelSound(xx, yy, SND_SWITCHGATE_OPENING);
-#endif
- }
- }
- }
-}
+ boolean first_horiz = RND(2);
+ int new_move_dir = MovDir[x][y];
-static int getInvisibleActiveFromInvisibleElement(int element)
-{
- return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
- element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
- element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
- element);
-}
+ MovDir[x][y] =
+ new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
+ Moving2Blocked(x, y, &newx, &newy);
-static int getInvisibleFromInvisibleActiveElement(int element)
-{
- return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
- element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
- element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
- element);
-}
+ if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
+ return;
-static void RedrawAllLightSwitchesAndInvisibleElements()
-{
- int x, y;
+ MovDir[x][y] =
+ new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
+ Moving2Blocked(x, y, &newx, &newy);
- for (y = 0; y < lev_fieldy; y++)
- {
- for (x = 0; x < lev_fieldx; x++)
+ if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
+ return;
+
+ MovDir[x][y] = old_move_dir;
+ return;
+ }
+ }
+ else if (element == EL_SATELLITE)
{
- int element = Feld[x][y];
+ int newx, newy;
+
+ MovDelay[x][y] = 1;
- if (element == EL_LIGHT_SWITCH &&
- game.light_time_left > 0)
+ if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
{
- Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
- DrawLevelField(x, y);
+ boolean first_horiz = RND(2);
+ int new_move_dir = MovDir[x][y];
+
+ MovDir[x][y] =
+ new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
+ Moving2Blocked(x, y, &newx, &newy);
+
+ if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
+ return;
+
+ MovDir[x][y] =
+ new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
+ Moving2Blocked(x, y, &newx, &newy);
+
+ if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
+ return;
+
+ MovDir[x][y] = old_move_dir;
+ return;
}
- else if (element == EL_LIGHT_SWITCH_ACTIVE &&
- game.light_time_left == 0)
+ }
+ else if (element == EL_EMC_ANDROID)
+ {
+ static int check_pos[16] =
{
- Feld[x][y] = EL_LIGHT_SWITCH;
- DrawLevelField(x, y);
- }
- else if (element == EL_INVISIBLE_STEELWALL ||
- element == EL_INVISIBLE_WALL ||
- element == EL_INVISIBLE_SAND)
+ -1, /* 0 => (invalid) */
+ 7, /* 1 => MV_LEFT */
+ 3, /* 2 => MV_RIGHT */
+ -1, /* 3 => (invalid) */
+ 1, /* 4 => MV_UP */
+ 0, /* 5 => MV_LEFT | MV_UP */
+ 2, /* 6 => MV_RIGHT | MV_UP */
+ -1, /* 7 => (invalid) */
+ 5, /* 8 => MV_DOWN */
+ 6, /* 9 => MV_LEFT | MV_DOWN */
+ 4, /* 10 => MV_RIGHT | MV_DOWN */
+ -1, /* 11 => (invalid) */
+ -1, /* 12 => (invalid) */
+ -1, /* 13 => (invalid) */
+ -1, /* 14 => (invalid) */
+ -1, /* 15 => (invalid) */
+ };
+ static struct
+ {
+ int dx, dy;
+ int dir;
+ } check_xy[8] =
{
- if (game.light_time_left > 0)
- Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
+ { -1, -1, MV_LEFT | MV_UP },
+ { 0, -1, MV_UP },
+ { +1, -1, MV_RIGHT | MV_UP },
+ { +1, 0, MV_RIGHT },
+ { +1, +1, MV_RIGHT | MV_DOWN },
+ { 0, +1, MV_DOWN },
+ { -1, +1, MV_LEFT | MV_DOWN },
+ { -1, 0, MV_LEFT },
+ };
+ int start_pos, check_order;
+ boolean can_clone = FALSE;
+ int i;
- DrawLevelField(x, y);
- }
- else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
- element == EL_INVISIBLE_WALL_ACTIVE ||
- element == EL_INVISIBLE_SAND_ACTIVE)
+ /* check if there is any free field around current position */
+ for (i = 0; i < 8; i++)
{
- if (game.light_time_left == 0)
- Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
+ int newx = x + check_xy[i].dx;
+ int newy = y + check_xy[i].dy;
- DrawLevelField(x, y);
+ if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
+ {
+ can_clone = TRUE;
+
+ break;
+ }
}
- }
- }
-}
-static void ToggleLightSwitch(int x, int y)
-{
- int element = Feld[x][y];
+ if (can_clone) /* randomly find an element to clone */
+ {
+ can_clone = FALSE;
- game.light_time_left =
- (element == EL_LIGHT_SWITCH ?
- level.time_light * FRAMES_PER_SECOND : 0);
+ start_pos = check_pos[RND(8)];
+ check_order = (RND(2) ? -1 : +1);
- RedrawAllLightSwitchesAndInvisibleElements();
-}
+ for (i = 0; i < 8; i++)
+ {
+ int pos_raw = start_pos + i * check_order;
+ int pos = (pos_raw + 8) % 8;
+ int newx = x + check_xy[pos].dx;
+ int newy = y + check_xy[pos].dy;
-static void ActivateTimegateSwitch(int x, int y)
-{
- int xx, yy;
+ if (ANDROID_CAN_CLONE_FIELD(newx, newy))
+ {
+ element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
+ element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
- game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
+ Store[x][y] = Feld[newx][newy];
- for (yy = 0; yy < lev_fieldy; yy++)
- {
- for (xx = 0; xx < lev_fieldx; xx++)
- {
- int element = Feld[xx][yy];
+ can_clone = TRUE;
- if (element == EL_TIMEGATE_CLOSED ||
- element == EL_TIMEGATE_CLOSING)
- {
- Feld[xx][yy] = EL_TIMEGATE_OPENING;
- PlayLevelSound(xx, yy, SND_TIMEGATE_OPENING);
+ break;
+ }
+ }
}
- /*
- else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
+ if (can_clone) /* randomly find a direction to move */
{
- Feld[xx][yy] = EL_TIMEGATE_SWITCH;
- DrawLevelField(xx, yy);
- }
- */
+ can_clone = FALSE;
- }
- }
+ start_pos = check_pos[RND(8)];
+ check_order = (RND(2) ? -1 : +1);
- Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
-}
+ for (i = 0; i < 8; i++)
+ {
+ int pos_raw = start_pos + i * check_order;
+ int pos = (pos_raw + 8) % 8;
+ int newx = x + check_xy[pos].dx;
+ int newy = y + check_xy[pos].dy;
+ int new_move_dir = check_xy[pos].dir;
-inline static int getElementMoveStepsize(int x, int y)
-{
- 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);
- int sign = (horiz_move ? dx : dy);
- int step = sign * element_info[element].move_stepsize;
+ if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
+ {
+ MovDir[x][y] = new_move_dir;
+ MovDelay[x][y] = level.android_clone_time * 8 + 1;
- /* special values for move stepsize for spring and things on conveyor belt */
- if (horiz_move)
- {
-#if 0
- if (element == EL_SPRING)
- step = sign * MOVE_STEPSIZE_NORMAL * 2;
- else if (CAN_FALL(element) && !CAN_MOVE(element) &&
- y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
- step = sign * MOVE_STEPSIZE_NORMAL / 2;
-#else
- if (CAN_FALL(element) &&
- y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
- step = sign * MOVE_STEPSIZE_NORMAL / 2;
- else if (element == EL_SPRING)
- step = sign * MOVE_STEPSIZE_NORMAL * 2;
-#endif
- }
+ can_clone = TRUE;
+
+ break;
+ }
+ }
+ }
- return step;
-}
+ if (can_clone) /* cloning and moving successful */
+ return;
-void Impact(int x, int y)
-{
- boolean lastline = (y == lev_fieldy-1);
- boolean object_hit = FALSE;
- boolean impact = (lastline || object_hit);
- int element = Feld[x][y];
- int smashed = EL_STEELWALL;
+ /* cannot clone -- try to move towards player */
- if (!lastline) /* check if element below was hit */
- {
- if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
- return;
+ start_pos = check_pos[MovDir[x][y] & 0x0f];
+ check_order = (RND(2) ? -1 : +1);
- object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
- MovDir[x][y + 1] != MV_DOWN ||
- MovPos[x][y + 1] <= TILEY / 2));
+ for (i = 0; i < 3; i++)
+ {
+ /* first check start_pos, then previous/next or (next/previous) pos */
+ int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
+ int pos = (pos_raw + 8) % 8;
+ int newx = x + check_xy[pos].dx;
+ int newy = y + check_xy[pos].dy;
+ int new_move_dir = check_xy[pos].dir;
+
+ if (IS_PLAYER(newx, newy))
+ break;
-#if 0
- object_hit = !IS_FREE(x, y + 1);
-#endif
+ if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
+ {
+ MovDir[x][y] = new_move_dir;
+ MovDelay[x][y] = level.android_move_time * 8 + 1;
- /* do not smash moving elements that left the smashed field in time */
- if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
- ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
- object_hit = FALSE;
+ break;
+ }
+ }
+ }
+ }
+ else if (move_pattern == MV_TURNING_LEFT ||
+ move_pattern == MV_TURNING_RIGHT ||
+ move_pattern == MV_TURNING_LEFT_RIGHT ||
+ move_pattern == MV_TURNING_RIGHT_LEFT ||
+ move_pattern == MV_TURNING_RANDOM ||
+ move_pattern == MV_ALL_DIRECTIONS)
+ {
+ boolean can_turn_left =
+ CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
+ boolean can_turn_right =
+ CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
- if (object_hit)
- smashed = MovingOrBlocked2Element(x, y + 1);
+ if (element_info[element].move_stepsize == 0) /* "not moving" */
+ return;
- impact = (lastline || object_hit);
- }
+ if (move_pattern == MV_TURNING_LEFT)
+ MovDir[x][y] = left_dir;
+ else if (move_pattern == MV_TURNING_RIGHT)
+ MovDir[x][y] = right_dir;
+ else if (move_pattern == MV_TURNING_LEFT_RIGHT)
+ MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
+ else if (move_pattern == MV_TURNING_RIGHT_LEFT)
+ MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
+ else if (move_pattern == MV_TURNING_RANDOM)
+ MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
+ can_turn_right && !can_turn_left ? right_dir :
+ RND(2) ? left_dir : right_dir);
+ else if (can_turn_left && can_turn_right)
+ MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
+ else if (can_turn_left)
+ MovDir[x][y] = (RND(2) ? left_dir : back_dir);
+ else if (can_turn_right)
+ MovDir[x][y] = (RND(2) ? right_dir : back_dir);
+ else
+ MovDir[x][y] = back_dir;
- if (!lastline && smashed == EL_ACID) /* element falls into acid */
- {
- SplashAcid(x, y + 1);
- return;
+ MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
}
-
- /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
- /* only reset graphic animation if graphic really changes after impact */
- if (impact &&
- el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
+ else if (move_pattern == MV_HORIZONTAL ||
+ move_pattern == MV_VERTICAL)
{
- ResetGfxAnimation(x, y);
- DrawLevelField(x, y);
- }
+ if (move_pattern & old_move_dir)
+ MovDir[x][y] = back_dir;
+ else if (move_pattern == MV_HORIZONTAL)
+ MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
+ else if (move_pattern == MV_VERTICAL)
+ MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
- if (impact && CAN_EXPLODE_IMPACT(element))
+ MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
+ }
+ else if (move_pattern & MV_ANY_DIRECTION)
{
- Bang(x, y);
- return;
+ MovDir[x][y] = move_pattern;
+ MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
}
- else if (impact && element == EL_PEARL)
+ else if (move_pattern & MV_WIND_DIRECTION)
{
- ResetGfxAnimation(x, y);
-
- Feld[x][y] = EL_PEARL_BREAKING;
- PlayLevelSound(x, y, SND_PEARL_BREAKING);
- return;
+ MovDir[x][y] = game.wind_direction;
+ MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
}
- else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
+ else if (move_pattern == MV_ALONG_LEFT_SIDE)
{
- PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
+ if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
+ MovDir[x][y] = left_dir;
+ else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
+ MovDir[x][y] = right_dir;
- return;
+ if (MovDir[x][y] != old_move_dir)
+ MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
}
-
- if (impact && element == EL_AMOEBA_DROP)
+ else if (move_pattern == MV_ALONG_RIGHT_SIDE)
{
- if (object_hit && IS_PLAYER(x, y + 1))
- KillHeroUnlessEnemyProtected(x, y + 1);
- else if (object_hit && smashed == EL_PENGUIN)
- Bang(x, y + 1);
- else
- {
- Feld[x][y] = EL_AMOEBA_GROWING;
- Store[x][y] = EL_AMOEBA_WET;
+ if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
+ MovDir[x][y] = right_dir;
+ else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
+ MovDir[x][y] = left_dir;
- ResetRandomAnimationValue(x, y);
- }
- return;
+ if (MovDir[x][y] != old_move_dir)
+ MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
}
-
- if (object_hit) /* check which object was hit */
+ else if (move_pattern == MV_TOWARDS_PLAYER ||
+ move_pattern == MV_AWAY_FROM_PLAYER)
{
- if (CAN_PASS_MAGIC_WALL(element) &&
- (smashed == EL_MAGIC_WALL ||
- smashed == EL_BD_MAGIC_WALL))
- {
- int xx, yy;
- int activated_magic_wall =
- (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
- EL_BD_MAGIC_WALL_ACTIVE);
-
- /* activate magic wall / mill */
- for (yy = 0; yy < lev_fieldy; yy++)
- for (xx = 0; xx < lev_fieldx; xx++)
- 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));
- }
+ int attr_x = -1, attr_y = -1;
+ int newx, newy;
+ boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
- if (IS_PLAYER(x, y + 1))
- {
- if (CAN_SMASH_PLAYER(element))
- {
- KillHeroUnlessEnemyProtected(x, y + 1);
- return;
- }
- }
- else if (smashed == EL_PENGUIN)
- {
- if (CAN_SMASH_PLAYER(element))
- {
- Bang(x, y + 1);
- return;
- }
- }
- else if (element == EL_BD_DIAMOND)
- {
- if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
- {
- Bang(x, y + 1);
- return;
- }
- }
- else if (((element == EL_SP_INFOTRON ||
- element == EL_SP_ZONK) &&
- (smashed == EL_SP_SNIKSNAK ||
- smashed == EL_SP_ELECTRON ||
- smashed == EL_SP_DISK_ORANGE)) ||
- (element == EL_SP_INFOTRON &&
- smashed == EL_SP_DISK_YELLOW))
- {
- Bang(x, y + 1);
- return;
- }
-#if 0
- else if (CAN_SMASH_ENEMIES(element) && IS_CLASSIC_ENEMY(smashed))
+ if (AllPlayersGone)
{
- Bang(x, y + 1);
- return;
+ attr_x = ExitX;
+ attr_y = ExitY;
}
-#endif
- else if (CAN_SMASH_EVERYTHING(element))
+ else
{
- if (IS_CLASSIC_ENEMY(smashed) ||
- CAN_EXPLODE_SMASHED(smashed))
- {
- Bang(x, y + 1);
- return;
- }
- else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
- {
- if (smashed == EL_LAMP ||
- smashed == EL_LAMP_ACTIVE)
- {
- Bang(x, y + 1);
- return;
- }
- else if (smashed == EL_NUT)
- {
- Feld[x][y + 1] = EL_NUT_BREAKING;
- PlayLevelSound(x, y, SND_NUT_BREAKING);
- RaiseScoreElement(EL_NUT);
- return;
- }
- else if (smashed == EL_PEARL)
- {
- ResetGfxAnimation(x, y);
+ int i;
- Feld[x][y + 1] = EL_PEARL_BREAKING;
- PlayLevelSound(x, y, SND_PEARL_BREAKING);
- return;
- }
- else if (smashed == EL_DIAMOND)
- {
- Feld[x][y + 1] = EL_DIAMOND_BREAKING;
- PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
- return;
- }
- else if (IS_BELT_SWITCH(smashed))
- {
- ToggleBeltSwitch(x, y + 1);
- }
- else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
- smashed == EL_SWITCHGATE_SWITCH_DOWN)
- {
- ToggleSwitchgateSwitch(x, y + 1);
- }
- else if (smashed == EL_LIGHT_SWITCH ||
- smashed == EL_LIGHT_SWITCH_ACTIVE)
- {
- ToggleLightSwitch(x, y + 1);
- }
- else
- {
-#if 0
- TestIfElementSmashesCustomElement(x, y, MV_DOWN);
-#endif
+ for (i = 0; i < MAX_PLAYERS; i++)
+ {
+ struct PlayerInfo *player = &stored_player[i];
+ int jx = player->jx, jy = player->jy;
- CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
+ if (!player->active)
+ continue;
-#if 1
- /* !!! TEST ONLY !!! */
- CheckElementChangeBySide(x, y + 1, smashed, element,
- CE_SWITCHED, CH_SIDE_TOP);
- CheckTriggeredElementChangeBySide(x, y + 1, smashed,
- CE_OTHER_IS_SWITCHING,CH_SIDE_TOP);
-#else
- CheckTriggeredElementChangeBySide(x, y + 1, smashed,
- CE_OTHER_IS_SWITCHING,CH_SIDE_TOP);
- CheckElementChangeBySide(x, y + 1, smashed, element,
- CE_SWITCHED, CH_SIDE_TOP);
-#endif
+ if (attr_x == -1 ||
+ ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
+ {
+ attr_x = jx;
+ attr_y = jy;
}
}
- else
+ }
+
+ MovDir[x][y] = MV_NONE;
+ if (attr_x < x)
+ MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
+ else if (attr_x > x)
+ MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
+ if (attr_y < y)
+ MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
+ else if (attr_y > y)
+ MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
+
+ MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
+
+ if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
+ {
+ boolean first_horiz = RND(2);
+ int new_move_dir = MovDir[x][y];
+
+ if (element_info[element].move_stepsize == 0) /* "not moving" */
{
- CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
+ first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
+ MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
+
+ return;
}
- }
- }
- /* play sound of magic wall / mill */
- if (!lastline &&
- (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
- Feld[x][y + 1] == EL_BD_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);
+ MovDir[x][y] =
+ new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
+ Moving2Blocked(x, y, &newx, &newy);
- return;
- }
+ if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
+ return;
- /* play sound of object that hits the ground */
- if (lastline || object_hit)
- PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
-}
+ MovDir[x][y] =
+ new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
+ Moving2Blocked(x, y, &newx, &newy);
-inline static void TurnRoundExt(int x, int y)
-{
- static struct
- {
- int x, y;
- } move_xy[] =
- {
- { 0, 0 },
- { -1, 0 },
- { +1, 0 },
- { 0, 0 },
- { 0, -1 },
- { 0, 0 }, { 0, 0 }, { 0, 0 },
- { 0, +1 }
- };
- static struct
+ if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
+ return;
+
+ MovDir[x][y] = old_move_dir;
+ }
+ }
+ else if (move_pattern == MV_WHEN_PUSHED ||
+ move_pattern == MV_WHEN_DROPPED)
{
- int left, right, back;
- } turn[] =
+ if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
+ MovDir[x][y] = MV_NONE;
+
+ MovDelay[x][y] = 0;
+ }
+ else if (move_pattern & MV_MAZE_RUNNER_STYLE)
{
- { 0, 0, 0 },
- { MV_DOWN, MV_UP, MV_RIGHT },
- { MV_UP, MV_DOWN, MV_LEFT },
- { 0, 0, 0 },
- { MV_LEFT, MV_RIGHT, MV_DOWN },
- { 0, 0, 0 },
- { 0, 0, 0 },
- { 0, 0, 0 },
- { MV_RIGHT, MV_LEFT, MV_UP }
- };
+ static int test_xy[7][2] =
+ {
+ { 0, -1 },
+ { -1, 0 },
+ { +1, 0 },
+ { 0, +1 },
+ { 0, -1 },
+ { -1, 0 },
+ { +1, 0 },
+ };
+ static int test_dir[7] =
+ {
+ MV_UP,
+ MV_LEFT,
+ MV_RIGHT,
+ MV_DOWN,
+ MV_UP,
+ MV_LEFT,
+ MV_RIGHT,
+ };
+ boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
+ int move_preference = -1000000; /* start with very low preference */
+ int new_move_dir = MV_NONE;
+ int start_test = RND(4);
+ int i;
- int element = Feld[x][y];
- int move_pattern = element_info[element].move_pattern;
+ for (i = 0; i < NUM_DIRECTIONS; i++)
+ {
+ int move_dir = test_dir[start_test + i];
+ int move_dir_preference;
- int old_move_dir = MovDir[x][y];
- int left_dir = turn[old_move_dir].left;
- int right_dir = turn[old_move_dir].right;
- int back_dir = turn[old_move_dir].back;
+ xx = x + test_xy[start_test + i][0];
+ yy = y + test_xy[start_test + i][1];
- int left_dx = move_xy[left_dir].x, left_dy = move_xy[left_dir].y;
- int right_dx = move_xy[right_dir].x, right_dy = move_xy[right_dir].y;
- int move_dx = move_xy[old_move_dir].x, move_dy = move_xy[old_move_dir].y;
- int back_dx = move_xy[back_dir].x, back_dy = move_xy[back_dir].y;
+ if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
+ (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
+ {
+ new_move_dir = move_dir;
- int left_x = x + left_dx, left_y = y + left_dy;
- int right_x = x + right_dx, right_y = y + right_dy;
- int move_x = x + move_dx, move_y = y + move_dy;
+ break;
+ }
- int xx, yy;
+ if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
+ continue;
- if (element == EL_BUG || element == EL_BD_BUTTERFLY)
- {
- TestIfBadThingTouchesOtherBadThing(x, y);
+ move_dir_preference = -1 * RunnerVisit[xx][yy];
+ if (hunter_mode && PlayerVisit[xx][yy] > 0)
+ move_dir_preference = PlayerVisit[xx][yy];
- if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
- MovDir[x][y] = right_dir;
- else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
- MovDir[x][y] = left_dir;
+ if (move_dir_preference > move_preference)
+ {
+ /* prefer field that has not been visited for the longest time */
+ move_preference = move_dir_preference;
+ new_move_dir = move_dir;
+ }
+ else if (move_dir_preference == move_preference &&
+ move_dir == old_move_dir)
+ {
+ /* prefer last direction when all directions are preferred equally */
+ move_preference = move_dir_preference;
+ new_move_dir = move_dir;
+ }
+ }
- if (element == EL_BUG && MovDir[x][y] != old_move_dir)
- MovDelay[x][y] = 9;
- else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
- MovDelay[x][y] = 1;
+ MovDir[x][y] = new_move_dir;
+ if (old_move_dir != new_move_dir)
+ MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
}
-#if 0
- else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
- element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
- {
- TestIfBadThingTouchesOtherBadThing(x, y);
+}
- if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
- MovDir[x][y] = left_dir;
- else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
- MovDir[x][y] = right_dir;
+static void TurnRound(int x, int y)
+{
+ int direction = MovDir[x][y];
- if ((element == EL_SPACESHIP ||
- element == EL_SP_SNIKSNAK ||
- element == EL_SP_ELECTRON)
- && MovDir[x][y] != old_move_dir)
- MovDelay[x][y] = 9;
- else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
- MovDelay[x][y] = 1;
- }
-#else
- else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
- {
- TestIfBadThingTouchesOtherBadThing(x, y);
+ TurnRoundExt(x, y);
- if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
- MovDir[x][y] = left_dir;
- else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
- MovDir[x][y] = right_dir;
+ GfxDir[x][y] = MovDir[x][y];
- if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
- MovDelay[x][y] = 9;
- else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
- MovDelay[x][y] = 1;
- }
- else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
- {
- TestIfBadThingTouchesOtherBadThing(x, y);
+ if (direction != MovDir[x][y])
+ GfxFrame[x][y] = 0;
- if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
- MovDir[x][y] = left_dir;
- else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
- MovDir[x][y] = right_dir;
+ if (MovDelay[x][y])
+ GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
- if (MovDir[x][y] != old_move_dir)
- MovDelay[x][y] = 9;
- }
-#endif
- else if (element == EL_YAMYAM)
+ ResetGfxFrame(x, y, FALSE);
+}
+
+static boolean JustBeingPushed(int x, int y)
+{
+ int i;
+
+ for (i = 0; i < MAX_PLAYERS; i++)
{
- boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
- boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
+ struct PlayerInfo *player = &stored_player[i];
- if (can_turn_left && can_turn_right)
- MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
- else if (can_turn_left)
- MovDir[x][y] = (RND(2) ? left_dir : back_dir);
- else if (can_turn_right)
- MovDir[x][y] = (RND(2) ? right_dir : back_dir);
- else
- MovDir[x][y] = back_dir;
+ if (player->active && player->is_pushing && player->MovPos)
+ {
+ int next_jx = player->jx + (player->jx - player->last_jx);
+ int next_jy = player->jy + (player->jy - player->last_jy);
- MovDelay[x][y] = 16 + 16 * RND(3);
+ if (x == next_jx && y == next_jy)
+ return TRUE;
+ }
}
- else if (element == EL_DARK_YAMYAM)
- {
- boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
- left_x, left_y);
- boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
- right_x, right_y);
- if (can_turn_left && can_turn_right)
- MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
- else if (can_turn_left)
- MovDir[x][y] = (RND(2) ? left_dir : back_dir);
- else if (can_turn_right)
- MovDir[x][y] = (RND(2) ? right_dir : back_dir);
- else
- MovDir[x][y] = back_dir;
+ return FALSE;
+}
+
+void StartMoving(int x, int y)
+{
+ boolean started_moving = FALSE; /* some elements can fall _and_ move */
+ int element = Feld[x][y];
- MovDelay[x][y] = 16 + 16 * RND(3);
- }
- else if (element == EL_PACMAN)
- {
- boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
- boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
+ if (Stop[x][y])
+ return;
- if (can_turn_left && can_turn_right)
- MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
- else if (can_turn_left)
- MovDir[x][y] = (RND(2) ? left_dir : back_dir);
- else if (can_turn_right)
- MovDir[x][y] = (RND(2) ? right_dir : back_dir);
- else
- MovDir[x][y] = back_dir;
+ if (MovDelay[x][y] == 0)
+ GfxAction[x][y] = ACTION_DEFAULT;
- MovDelay[x][y] = 6 + RND(40);
- }
- else if (element == EL_PIG)
+ if (CAN_FALL(element) && y < lev_fieldy - 1)
{
- boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
- boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
- boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
- boolean should_turn_left, should_turn_right, should_move_on;
- int rnd_value = 24;
- int rnd = RND(rnd_value);
-
- should_turn_left = (can_turn_left &&
- (!can_move_on ||
- IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
- y + back_dy + left_dy)));
- should_turn_right = (can_turn_right &&
- (!can_move_on ||
- IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
- y + back_dy + right_dy)));
- should_move_on = (can_move_on &&
- (!can_turn_left ||
- !can_turn_right ||
- IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
- y + move_dy + left_dy) ||
- IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
- y + move_dy + right_dy)));
+ if ((x > 0 && IS_PLAYER(x - 1, y)) ||
+ (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
+ if (JustBeingPushed(x, y))
+ return;
- if (should_turn_left || should_turn_right || should_move_on)
+ if (element == EL_QUICKSAND_FULL)
{
- if (should_turn_left && should_turn_right && should_move_on)
- MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
- rnd < 2 * rnd_value / 3 ? right_dir :
- old_move_dir);
- else if (should_turn_left && should_turn_right)
- MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
- else if (should_turn_left && should_move_on)
- MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
- else if (should_turn_right && should_move_on)
- MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
- else if (should_turn_left)
- MovDir[x][y] = left_dir;
- else if (should_turn_right)
- MovDir[x][y] = right_dir;
- else if (should_move_on)
- MovDir[x][y] = old_move_dir;
- }
- else if (can_move_on && rnd > rnd_value / 8)
- MovDir[x][y] = old_move_dir;
- else if (can_turn_left && can_turn_right)
- MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
- else if (can_turn_left && rnd > rnd_value / 8)
- MovDir[x][y] = left_dir;
- else if (can_turn_right && rnd > rnd_value/8)
- MovDir[x][y] = right_dir;
- else
- MovDir[x][y] = back_dir;
-
- xx = x + move_xy[MovDir[x][y]].x;
- yy = y + move_xy[MovDir[x][y]].y;
+ if (IS_FREE(x, y + 1))
+ {
+ InitMovingField(x, y, MV_DOWN);
+ started_moving = TRUE;
-#if 1
- /* !!! this bugfix breaks at least BD2K3, level 010 !!! [re-recorded] */
- if (!IN_LEV_FIELD(xx, yy) ||
- (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
- MovDir[x][y] = old_move_dir;
+ Feld[x][y] = EL_QUICKSAND_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
- if (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy]))
- MovDir[x][y] = old_move_dir;
+ Store[x][y] = EL_ROCK;
#endif
- MovDelay[x][y] = 0;
- }
- else if (element == EL_DRAGON)
- {
- boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
- boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
- boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
- int rnd_value = 24;
- int rnd = RND(rnd_value);
+ PlayLevelSoundAction(x, y, ACTION_EMPTYING);
+ }
+ else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
+ {
+ if (!MovDelay[x][y])
+ {
+ MovDelay[x][y] = TILEY + 1;
-#if 0
- if (FrameCounter < 1 && x == 0 && y == 29)
- printf(":2: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
-#endif
+ ResetGfxAnimation(x, y);
+ ResetGfxAnimation(x, y + 1);
+ }
- if (can_move_on && rnd > rnd_value / 8)
- MovDir[x][y] = old_move_dir;
- else if (can_turn_left && can_turn_right)
- MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
- else if (can_turn_left && rnd > rnd_value / 8)
- MovDir[x][y] = left_dir;
- else if (can_turn_right && rnd > rnd_value / 8)
- MovDir[x][y] = right_dir;
- else
- MovDir[x][y] = back_dir;
+ if (MovDelay[x][y])
+ {
+ DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
+ DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
+
+ MovDelay[x][y]--;
+ if (MovDelay[x][y])
+ return;
+ }
- xx = x + move_xy[MovDir[x][y]].x;
- yy = y + move_xy[MovDir[x][y]].y;
+ Feld[x][y] = EL_QUICKSAND_EMPTY;
+ Feld[x][y + 1] = EL_QUICKSAND_FULL;
+ Store[x][y + 1] = Store[x][y];
+ Store[x][y] = 0;
-#if 0
- if (FrameCounter < 1 && x == 0 && y == 29)
- printf(":3: %d/%d: %d (%d/%d: %d) [%d]\n", x, y, MovDir[x][y],
- xx, yy, Feld[xx][yy],
- FrameCounter);
-#endif
+ PlayLevelSoundAction(x, y, ACTION_FILLING);
+ }
+ else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
+ {
+ if (!MovDelay[x][y])
+ {
+ MovDelay[x][y] = TILEY + 1;
-#if 1
- if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
- MovDir[x][y] = old_move_dir;
-#else
- if (!IS_FREE(xx, yy))
- MovDir[x][y] = old_move_dir;
-#endif
+ ResetGfxAnimation(x, y);
+ ResetGfxAnimation(x, y + 1);
+ }
-#if 0
- if (FrameCounter < 1 && x == 0 && y == 29)
- printf(":4: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
-#endif
+ if (MovDelay[x][y])
+ {
+ DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
+ DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
- MovDelay[x][y] = 0;
- }
- else if (element == EL_MOLE)
- {
- boolean can_move_on =
- (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
- IS_AMOEBOID(Feld[move_x][move_y]) ||
- Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
- if (!can_move_on)
- {
- boolean can_turn_left =
- (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
- IS_AMOEBOID(Feld[left_x][left_y])));
+ MovDelay[x][y]--;
+ if (MovDelay[x][y])
+ return;
+ }
- boolean can_turn_right =
- (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
- IS_AMOEBOID(Feld[right_x][right_y])));
+ Feld[x][y] = EL_QUICKSAND_EMPTY;
+ Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
+ Store[x][y + 1] = Store[x][y];
+ Store[x][y] = 0;
- if (can_turn_left && can_turn_right)
- MovDir[x][y] = (RND(2) ? left_dir : right_dir);
- else if (can_turn_left)
- MovDir[x][y] = left_dir;
- else
- MovDir[x][y] = right_dir;
+ 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;
- if (MovDir[x][y] != old_move_dir)
- MovDelay[x][y] = 9;
- }
- else if (element == EL_BALLOON)
- {
- MovDir[x][y] = game.balloon_dir;
- MovDelay[x][y] = 0;
- }
- else if (element == EL_SPRING)
- {
-#if 0
- if (MovDir[x][y] & MV_HORIZONTAL &&
- !SPRING_CAN_ENTER_FIELD(element, move_x, move_y))
- MovDir[x][y] = MV_NO_MOVING;
+ 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
- if (MovDir[x][y] & MV_HORIZONTAL &&
- (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
- SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
- MovDir[x][y] = MV_NO_MOVING;
+ Store[x][y] = EL_ROCK;
#endif
- MovDelay[x][y] = 0;
- }
- else if (element == EL_ROBOT ||
- element == EL_SATELLITE ||
- element == EL_PENGUIN)
- {
- int attr_x = -1, attr_y = -1;
+ 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 (AllPlayersGone)
+ ResetGfxAnimation(x, y);
+ ResetGfxAnimation(x, y + 1);
+ }
+
+ if (MovDelay[x][y])
+ {
+ DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
+ DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
+
+ 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 (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
+ {
+ if (!MovDelay[x][y])
+ {
+ MovDelay[x][y] = TILEY + 1;
+
+ ResetGfxAnimation(x, y);
+ ResetGfxAnimation(x, y + 1);
+ }
+
+ if (MovDelay[x][y])
+ {
+ DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
+ DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
+
+ MovDelay[x][y]--;
+ if (MovDelay[x][y])
+ return;
+ }
+
+ Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
+ Feld[x][y + 1] = EL_QUICKSAND_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)
{
- attr_x = ExitX;
- attr_y = ExitY;
+ InitMovingField(x, y, MV_DOWN);
+ started_moving = TRUE;
+
+ Feld[x][y] = EL_QUICKSAND_FILLING;
+ Store[x][y] = element;
+
+ PlayLevelSoundAction(x, y, ACTION_FILLING);
}
- else
+ else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
+ Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
{
- int i;
+ InitMovingField(x, y, MV_DOWN);
+ started_moving = TRUE;
- for (i = 0; i < MAX_PLAYERS; i++)
+ 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))
{
- struct PlayerInfo *player = &stored_player[i];
- int jx = player->jx, jy = player->jy;
+ InitMovingField(x, y, MV_DOWN);
+ started_moving = TRUE;
- if (!player->active)
- continue;
+ Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
+ Store[x][y] = EL_CHANGED(Store[x][y]);
+ }
+ else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
+ {
+ if (!MovDelay[x][y])
+ MovDelay[x][y] = TILEY / 4 + 1;
- if (attr_x == -1 ||
- ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
+ if (MovDelay[x][y])
{
- attr_x = jx;
- attr_y = jy;
+ MovDelay[x][y]--;
+ if (MovDelay[x][y])
+ return;
}
+
+ Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
+ Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
+ Store[x][y + 1] = EL_CHANGED(Store[x][y]);
+ Store[x][y] = 0;
}
}
-
-#if 1
- if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
- (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
- game.engine_version < VERSION_IDENT(3,1,0,0)))
-#else
- if (element == EL_ROBOT && ZX >= 0 && ZY >= 0)
-#endif
+ else if (element == EL_BD_MAGIC_WALL_FULL)
{
- attr_x = ZX;
- attr_y = ZY;
- }
+ if (IS_FREE(x, y + 1))
+ {
+ InitMovingField(x, y, MV_DOWN);
+ started_moving = TRUE;
- if (element == EL_PENGUIN)
+ Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
+ Store[x][y] = EL_CHANGED_BD(Store[x][y]);
+ }
+ else if (Feld[x][y + 1] == EL_BD_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_BD_MAGIC_WALL_ACTIVE;
+ Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
+ Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
+ Store[x][y] = 0;
+ }
+ }
+ else if (element == EL_DC_MAGIC_WALL_FULL)
{
- int i;
- static int xy[4][2] =
+ if (IS_FREE(x, y + 1))
{
- { 0, -1 },
- { -1, 0 },
- { +1, 0 },
- { 0, +1 }
- };
+ InitMovingField(x, y, MV_DOWN);
+ started_moving = TRUE;
- for (i = 0; i < NUM_DIRECTIONS; i++)
+ 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)
{
- int ex = x + xy[i][0];
- int ey = y + xy[i][1];
+ if (!MovDelay[x][y])
+ MovDelay[x][y] = TILEY / 4 + 1;
- if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
+ if (MovDelay[x][y])
{
- attr_x = ex;
- attr_y = ey;
- break;
+ 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)) ||
+ (CAN_PASS_DC_MAGIC_WALL(element) &&
+ (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
- MovDir[x][y] = MV_NO_MOVING;
- if (attr_x < x)
- MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
- else if (attr_x > x)
- MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
- if (attr_y < y)
- MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
- else if (attr_y > y)
- MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
+ {
+ InitMovingField(x, y, MV_DOWN);
+ started_moving = TRUE;
- if (element == EL_ROBOT)
+ Feld[x][y] =
+ (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_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)
{
- int newx, newy;
+ SplashAcid(x, y + 1);
- if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
- MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
- Moving2Blocked(x, y, &newx, &newy);
+ InitMovingField(x, y, MV_DOWN);
+ started_moving = TRUE;
- if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
- MovDelay[x][y] = 8 + 8 * !RND(3);
- else
- MovDelay[x][y] = 16;
+ Store[x][y] = EL_ACID;
}
- else if (element == EL_PENGUIN)
+ 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))) ||
+
+ (game.engine_version < VERSION_IDENT(2,2,0,7) &&
+ CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
+ (Feld[x][y + 1] == EL_BLOCKED)))
{
- int newx, newy;
+ /* this is needed for a special case not covered by calling "Impact()"
+ from "ContinueMoving()": if an element moves to a tile directly below
+ another element which was just falling on that tile (which was empty
+ in the previous frame), the falling element above would just stop
+ instead of smashing the element below (in previous version, the above
+ element was just checked for "moving" instead of "falling", resulting
+ in incorrect smashes caused by horizontal movement of the above
+ element; also, the case of the player being the element to smash was
+ simply not covered here... :-/ ) */
- MovDelay[x][y] = 1;
+ CheckCollision[x][y] = 0;
+ CheckImpact[x][y] = 0;
- if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
+ Impact(x, y);
+ }
+ else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
+ {
+ if (MovDir[x][y] == MV_NONE)
{
- boolean first_horiz = RND(2);
- int new_move_dir = MovDir[x][y];
+ InitMovingField(x, y, MV_DOWN);
+ started_moving = TRUE;
+ }
+ }
+ else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
+ {
+ if (WasJustFalling[x][y]) /* prevent animation from being restarted */
+ MovDir[x][y] = MV_DOWN;
- MovDir[x][y] =
- new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
- Moving2Blocked(x, y, &newx, &newy);
+ InitMovingField(x, y, MV_DOWN);
+ started_moving = TRUE;
+ }
+ else if (element == EL_AMOEBA_DROP)
+ {
+ Feld[x][y] = EL_AMOEBA_GROWING;
+ Store[x][y] = EL_AMOEBA_WET;
+ }
+ else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
+ (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
+ !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
+ element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
+ {
+ boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
+ (IS_FREE(x - 1, y + 1) ||
+ Feld[x - 1][y + 1] == EL_ACID));
+ boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
+ (IS_FREE(x + 1, y + 1) ||
+ Feld[x + 1][y + 1] == EL_ACID));
+ boolean can_fall_any = (can_fall_left || can_fall_right);
+ boolean can_fall_both = (can_fall_left && can_fall_right);
+ int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
- if (PENGUIN_CAN_ENTER_FIELD(EL_PENGUIN, newx, newy))
- return;
+#if USE_NEW_ALL_SLIPPERY
+ if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
+ {
+ if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
+ can_fall_right = FALSE;
+ else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
+ can_fall_left = FALSE;
+ else if (slippery_type == SLIPPERY_ONLY_LEFT)
+ can_fall_right = FALSE;
+ else if (slippery_type == SLIPPERY_ONLY_RIGHT)
+ can_fall_left = FALSE;
- MovDir[x][y] =
- new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
- Moving2Blocked(x, y, &newx, &newy);
+ can_fall_any = (can_fall_left || can_fall_right);
+ can_fall_both = FALSE;
+ }
+#else
+ if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
+ {
+ if (slippery_type == SLIPPERY_ONLY_LEFT)
+ can_fall_right = FALSE;
+ else if (slippery_type == SLIPPERY_ONLY_RIGHT)
+ can_fall_left = FALSE;
+ else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
+ can_fall_right = FALSE;
+ else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
+ can_fall_left = FALSE;
+
+ can_fall_any = (can_fall_left || can_fall_right);
+ can_fall_both = (can_fall_left && can_fall_right);
+ }
+#endif
+
+#if USE_NEW_ALL_SLIPPERY
+#else
+#if USE_NEW_SP_SLIPPERY
+ /* !!! better use the same properties as for custom elements here !!! */
+ else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
+ can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
+ {
+ can_fall_right = FALSE; /* slip down on left side */
+ can_fall_both = FALSE;
+ }
+#endif
+#endif
+
+#if USE_NEW_ALL_SLIPPERY
+ if (can_fall_both)
+ {
+ if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
+ can_fall_right = FALSE; /* slip down on left side */
+ else
+ can_fall_left = !(can_fall_right = RND(2));
+
+ can_fall_both = FALSE;
+ }
+#else
+ if (can_fall_both)
+ {
+ if (game.emulation == EMU_BOULDERDASH ||
+ element == EL_BD_ROCK || element == EL_BD_DIAMOND)
+ can_fall_right = FALSE; /* slip down on left side */
+ else
+ can_fall_left = !(can_fall_right = RND(2));
- if (PENGUIN_CAN_ENTER_FIELD(EL_PENGUIN, newx, newy))
- return;
+ can_fall_both = FALSE;
+ }
+#endif
- MovDir[x][y] = old_move_dir;
- return;
+ if (can_fall_any)
+ {
+ /* if not determined otherwise, prefer left side for slipping down */
+ InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
+ started_moving = TRUE;
}
}
- else /* (element == EL_SATELLITE) */
+#if 0
+ else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
+#else
+ else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
+#endif
{
- int newx, newy;
-
- MovDelay[x][y] = 1;
+ boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
+ boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
+ int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
+ int belt_dir = game.belt_dir[belt_nr];
- if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
+ if ((belt_dir == MV_LEFT && left_is_free) ||
+ (belt_dir == MV_RIGHT && right_is_free))
{
- boolean first_horiz = RND(2);
- int new_move_dir = MovDir[x][y];
-
- MovDir[x][y] =
- new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
- Moving2Blocked(x, y, &newx, &newy);
-
- if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
- return;
+ int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
- MovDir[x][y] =
- new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
- Moving2Blocked(x, y, &newx, &newy);
+ InitMovingField(x, y, belt_dir);
+ started_moving = TRUE;
- if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
- return;
+ Pushed[x][y] = TRUE;
+ Pushed[nextx][y] = TRUE;
- MovDir[x][y] = old_move_dir;
- return;
+ GfxAction[x][y] = ACTION_DEFAULT;
+ }
+ else
+ {
+ MovDir[x][y] = 0; /* if element was moving, stop it */
}
}
}
- else if (move_pattern == MV_TURNING_LEFT ||
- move_pattern == MV_TURNING_RIGHT ||
- move_pattern == MV_TURNING_LEFT_RIGHT ||
- move_pattern == MV_TURNING_RIGHT_LEFT ||
- move_pattern == MV_TURNING_RANDOM ||
- move_pattern == MV_ALL_DIRECTIONS)
+
+ /* not "else if" because of elements that can fall and move (EL_SPRING) */
+#if 0
+ if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
+#else
+ if (CAN_MOVE(element) && !started_moving)
+#endif
{
- boolean can_turn_left =
- CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
- boolean can_turn_right =
- CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
+ int move_pattern = element_info[element].move_pattern;
+ int newx, newy;
- if (move_pattern == MV_TURNING_LEFT)
- MovDir[x][y] = left_dir;
- else if (move_pattern == MV_TURNING_RIGHT)
- MovDir[x][y] = right_dir;
- else if (move_pattern == MV_TURNING_LEFT_RIGHT)
- MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
- else if (move_pattern == MV_TURNING_RIGHT_LEFT)
- MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
- else if (move_pattern == MV_TURNING_RANDOM)
- MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
- can_turn_right && !can_turn_left ? right_dir :
- RND(2) ? left_dir : right_dir);
- else if (can_turn_left && can_turn_right)
- MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
- else if (can_turn_left)
- MovDir[x][y] = (RND(2) ? left_dir : back_dir);
- else if (can_turn_right)
- MovDir[x][y] = (RND(2) ? right_dir : back_dir);
- else
- MovDir[x][y] = back_dir;
+#if 0
+#if DEBUG
+ if (MovDir[x][y] == MV_NONE)
+ {
+ printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
+ x, y, element, element_info[element].token_name);
+ printf("StartMoving(): This should never happen!\n");
+ }
+#endif
+#endif
- MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
- }
- else if (move_pattern == MV_HORIZONTAL ||
- move_pattern == MV_VERTICAL)
- {
- if (move_pattern & old_move_dir)
- MovDir[x][y] = back_dir;
- else if (move_pattern == MV_HORIZONTAL)
- MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
- else if (move_pattern == MV_VERTICAL)
- MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
+ Moving2Blocked(x, y, &newx, &newy);
- MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
- }
- else if (move_pattern & MV_ANY_DIRECTION)
- {
- MovDir[x][y] = move_pattern;
- MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
- }
- else if (move_pattern == MV_ALONG_LEFT_SIDE)
- {
- if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
- MovDir[x][y] = left_dir;
- else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
- MovDir[x][y] = right_dir;
+ if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
+ return;
- if (MovDir[x][y] != old_move_dir)
- MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
- }
- else if (move_pattern == MV_ALONG_RIGHT_SIDE)
- {
- if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
- MovDir[x][y] = right_dir;
- else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
- MovDir[x][y] = left_dir;
+ if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
+ CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
+ {
+ WasJustMoving[x][y] = 0;
+ CheckCollision[x][y] = 0;
- if (MovDir[x][y] != old_move_dir)
- MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
- }
- else if (move_pattern == MV_TOWARDS_PLAYER ||
- move_pattern == MV_AWAY_FROM_PLAYER)
- {
- int attr_x = -1, attr_y = -1;
- int newx, newy;
- boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
+ TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
- if (AllPlayersGone)
+ if (Feld[x][y] != element) /* element has changed */
+ return;
+ }
+
+ if (!MovDelay[x][y]) /* start new movement phase */
{
- attr_x = ExitX;
- attr_y = ExitY;
+ /* all objects that can change their move direction after each step
+ (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
+
+ if (element != EL_YAMYAM &&
+ element != EL_DARK_YAMYAM &&
+ element != EL_PACMAN &&
+ !(move_pattern & MV_ANY_DIRECTION) &&
+ move_pattern != MV_TURNING_LEFT &&
+ move_pattern != MV_TURNING_RIGHT &&
+ move_pattern != MV_TURNING_LEFT_RIGHT &&
+ move_pattern != MV_TURNING_RIGHT_LEFT &&
+ move_pattern != MV_TURNING_RANDOM)
+ {
+ TurnRound(x, y);
+
+ if (MovDelay[x][y] && (element == EL_BUG ||
+ element == EL_SPACESHIP ||
+ element == EL_SP_SNIKSNAK ||
+ element == EL_SP_ELECTRON ||
+ element == EL_MOLE))
+ TEST_DrawLevelField(x, y);
+ }
}
- else
+
+ if (MovDelay[x][y]) /* wait some time before next movement */
{
- int i;
+ MovDelay[x][y]--;
- for (i = 0; i < MAX_PLAYERS; i++)
+ if (element == EL_ROBOT ||
+ element == EL_YAMYAM ||
+ element == EL_DARK_YAMYAM)
{
- struct PlayerInfo *player = &stored_player[i];
- int jx = player->jx, jy = player->jy;
+ DrawLevelElementAnimationIfNeeded(x, y, element);
+ PlayLevelSoundAction(x, y, ACTION_WAITING);
+ }
+ else if (element == EL_SP_ELECTRON)
+ DrawLevelElementAnimationIfNeeded(x, y, element);
+ else if (element == EL_DRAGON)
+ {
+ int i;
+ int dir = MovDir[x][y];
+ int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
+ int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
+ int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
+ dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
+ dir == MV_UP ? IMG_FLAMES_1_UP :
+ dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
+ int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
- if (!player->active)
- continue;
+ GfxAction[x][y] = ACTION_ATTACKING;
- if (attr_x == -1 ||
- ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
+ if (IS_PLAYER(x, y))
+ DrawPlayerField(x, y);
+ else
+ TEST_DrawLevelField(x, y);
+
+ PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
+
+ for (i = 1; i <= 3; i++)
{
- attr_x = jx;
- attr_y = jy;
- }
- }
- }
+ int xx = x + i * dx;
+ int yy = y + i * dy;
+ int sx = SCREENX(xx);
+ int sy = SCREENY(yy);
+ int flame_graphic = graphic + (i - 1);
- MovDir[x][y] = MV_NO_MOVING;
- if (attr_x < x)
- MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
- else if (attr_x > x)
- MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
- if (attr_y < y)
- MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
- else if (attr_y > y)
- MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
+ if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
+ break;
+
+ if (MovDelay[x][y])
+ {
+ int flamed = MovingOrBlocked2Element(xx, yy);
+
+ /* !!! */
+#if 0
+ if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
+ Bang(xx, yy);
+ else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
+ RemoveMovingField(xx, yy);
+ else
+ RemoveField(xx, yy);
+#else
+ if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
+ Bang(xx, yy);
+ else
+ RemoveMovingField(xx, yy);
+#endif
- MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
+ ChangeDelay[xx][yy] = 0;
- if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
- {
- boolean first_horiz = RND(2);
- int new_move_dir = MovDir[x][y];
+ Feld[xx][yy] = EL_FLAMES;
- MovDir[x][y] =
- new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
- Moving2Blocked(x, y, &newx, &newy);
+ if (IN_SCR_FIELD(sx, sy))
+ {
+ TEST_DrawLevelFieldCrumbled(xx, yy);
+ DrawGraphic(sx, sy, flame_graphic, frame);
+ }
+ }
+ else
+ {
+ if (Feld[xx][yy] == EL_FLAMES)
+ Feld[xx][yy] = EL_EMPTY;
+ TEST_DrawLevelField(xx, yy);
+ }
+ }
+ }
+
+ if (MovDelay[x][y]) /* element still has to wait some time */
+ {
+ PlayLevelSoundAction(x, y, ACTION_WAITING);
- if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
return;
+ }
+ }
- MovDir[x][y] =
- new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
- Moving2Blocked(x, y, &newx, &newy);
+ /* now make next step */
- if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
- return;
+ Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
- MovDir[x][y] = old_move_dir;
+ if (DONT_COLLIDE_WITH(element) &&
+ IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
+ !PLAYER_ENEMY_PROTECTED(newx, newy))
+ {
+ TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
+
+ return;
}
- }
- else if (move_pattern == MV_WHEN_PUSHED ||
- move_pattern == MV_WHEN_DROPPED)
- {
- if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
- MovDir[x][y] = MV_NO_MOVING;
- MovDelay[x][y] = 0;
- }
- else if (move_pattern & MV_MAZE_RUNNER_STYLE)
- {
- static int test_xy[7][2] =
+ else if (CAN_MOVE_INTO_ACID(element) &&
+ IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
+ !IS_MV_DIAGONAL(MovDir[x][y]) &&
+ (MovDir[x][y] == MV_DOWN ||
+ game.engine_version >= VERSION_IDENT(3,1,0,0)))
{
- { 0, -1 },
- { -1, 0 },
- { +1, 0 },
- { 0, +1 },
- { 0, -1 },
- { -1, 0 },
- { +1, 0 },
- };
- static int test_dir[7] =
+ SplashAcid(newx, newy);
+ Store[x][y] = EL_ACID;
+ }
+ else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
{
- MV_UP,
- MV_LEFT,
- MV_RIGHT,
- MV_DOWN,
- MV_UP,
- MV_LEFT,
- MV_RIGHT,
- };
- boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
- int move_preference = -1000000; /* start with very low preference */
- int new_move_dir = MV_NO_MOVING;
- int start_test = RND(4);
- int i;
+ 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);
+ TEST_DrawLevelField(x, y);
- for (i = 0; i < NUM_DIRECTIONS; i++)
- {
- int move_dir = test_dir[start_test + i];
- int move_dir_preference;
+ PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
+ if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
+ DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
- xx = x + test_xy[start_test + i][0];
- yy = y + test_xy[start_test + i][1];
+ local_player->friends_still_needed--;
+ if (!local_player->friends_still_needed &&
+ !local_player->GameOver && AllPlayersGone)
+ PlayerWins(local_player);
- if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
- (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
+ return;
+ }
+ else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
{
- new_move_dir = move_dir;
-
- break;
+ if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
+ TEST_DrawLevelField(newx, newy);
+ else
+ GfxDir[x][y] = MovDir[x][y] = MV_NONE;
}
+ else if (!IS_FREE(newx, newy))
+ {
+ GfxAction[x][y] = ACTION_WAITING;
- if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
- continue;
-
- move_dir_preference = -1 * RunnerVisit[xx][yy];
- if (hunter_mode && PlayerVisit[xx][yy] > 0)
- move_dir_preference = PlayerVisit[xx][yy];
+ if (IS_PLAYER(x, y))
+ DrawPlayerField(x, y);
+ else
+ TEST_DrawLevelField(x, y);
- if (move_dir_preference > move_preference)
+ return;
+ }
+ }
+ else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
+ {
+ if (IS_FOOD_PIG(Feld[newx][newy]))
{
- /* prefer field that has not been visited for the longest time */
- move_preference = move_dir_preference;
- new_move_dir = move_dir;
+ if (IS_MOVING(newx, newy))
+ RemoveMovingField(newx, newy);
+ else
+ {
+ Feld[newx][newy] = EL_EMPTY;
+ TEST_DrawLevelField(newx, newy);
+ }
+
+ PlayLevelSound(x, y, SND_PIG_DIGGING);
}
- else if (move_dir_preference == move_preference &&
- move_dir == old_move_dir)
+ else if (!IS_FREE(newx, newy))
{
- /* prefer last direction when all directions are preferred equally */
- move_preference = move_dir_preference;
- new_move_dir = move_dir;
+ if (IS_PLAYER(x, y))
+ DrawPlayerField(x, y);
+ else
+ TEST_DrawLevelField(x, y);
+
+ return;
}
}
+ else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
+ {
+ if (Store[x][y] != EL_EMPTY)
+ {
+ boolean can_clone = FALSE;
+ int xx, yy;
- MovDir[x][y] = new_move_dir;
- if (old_move_dir != new_move_dir)
- MovDelay[x][y] = 9;
- }
-}
+ /* check if element to clone is still there */
+ for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
+ {
+ if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
+ {
+ can_clone = TRUE;
-static void TurnRound(int x, int y)
-{
- int direction = MovDir[x][y];
+ break;
+ }
+ }
-#if 0
- GfxDir[x][y] = MovDir[x][y];
-#endif
+ /* cannot clone or target field not free anymore -- do not clone */
+ if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
+ Store[x][y] = EL_EMPTY;
+ }
- TurnRoundExt(x, y);
+ if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
+ {
+ if (IS_MV_DIAGONAL(MovDir[x][y]))
+ {
+ int diagonal_move_dir = MovDir[x][y];
+ int stored = Store[x][y];
+ int change_delay = 8;
+ int graphic;
-#if 1
- GfxDir[x][y] = MovDir[x][y];
-#endif
+ /* android is moving diagonally */
- if (direction != MovDir[x][y])
- GfxFrame[x][y] = 0;
+ CreateField(x, y, EL_DIAGONAL_SHRINKING);
-#if 1
- if (MovDelay[x][y])
- GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_BIT(direction);
-#else
- if (MovDelay[x][y])
- GfxAction[x][y] = ACTION_WAITING;
-#endif
-}
+ Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
+ GfxElement[x][y] = EL_EMC_ANDROID;
+ GfxAction[x][y] = ACTION_SHRINKING;
+ GfxDir[x][y] = diagonal_move_dir;
+ ChangeDelay[x][y] = change_delay;
-static boolean JustBeingPushed(int x, int y)
-{
- int i;
+ graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
+ GfxDir[x][y]);
- for (i = 0; i < MAX_PLAYERS; i++)
- {
- struct PlayerInfo *player = &stored_player[i];
+ DrawLevelGraphicAnimation(x, y, graphic);
+ PlayLevelSoundAction(x, y, ACTION_SHRINKING);
- if (player->active && player->is_pushing && player->MovPos)
- {
- int next_jx = player->jx + (player->jx - player->last_jx);
- int next_jy = player->jy + (player->jy - player->last_jy);
+ if (Feld[newx][newy] == EL_ACID)
+ {
+ SplashAcid(newx, newy);
- if (x == next_jx && y == next_jy)
- return TRUE;
- }
- }
+ return;
+ }
- return FALSE;
-}
+ CreateField(newx, newy, EL_DIAGONAL_GROWING);
-void StartMoving(int x, int y)
-{
-#if 0
- boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
-#endif
- boolean started_moving = FALSE; /* some elements can fall _and_ move */
- int element = Feld[x][y];
+ Store[newx][newy] = EL_EMC_ANDROID;
+ GfxElement[newx][newy] = EL_EMC_ANDROID;
+ GfxAction[newx][newy] = ACTION_GROWING;
+ GfxDir[newx][newy] = diagonal_move_dir;
+ ChangeDelay[newx][newy] = change_delay;
- if (Stop[x][y])
- return;
+ graphic = el_act_dir2img(GfxElement[newx][newy],
+ GfxAction[newx][newy], GfxDir[newx][newy]);
-#if 1
- if (MovDelay[x][y] == 0)
- GfxAction[x][y] = ACTION_DEFAULT;
-#else
- /* !!! this should be handled more generic (not only for mole) !!! */
- if (element != EL_MOLE && GfxAction[x][y] != ACTION_DIGGING)
- GfxAction[x][y] = ACTION_DEFAULT;
+ DrawLevelGraphicAnimation(newx, newy, graphic);
+ PlayLevelSoundAction(newx, newy, ACTION_GROWING);
+
+ return;
+ }
+ else
+ {
+ Feld[newx][newy] = EL_EMPTY;
+ TEST_DrawLevelField(newx, newy);
+
+ PlayLevelSoundAction(x, y, ACTION_DIGGING);
+ }
+ }
+ else if (!IS_FREE(newx, newy))
+ {
+#if 0
+ if (IS_PLAYER(x, y))
+ DrawPlayerField(x, y);
+ else
+ TEST_DrawLevelField(x, y);
#endif
- if (CAN_FALL(element) && y < lev_fieldy - 1)
- {
- if ((x > 0 && IS_PLAYER(x - 1, y)) ||
- (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
- if (JustBeingPushed(x, y))
return;
-
- if (element == EL_QUICKSAND_FULL)
+ }
+ }
+ else if (IS_CUSTOM_ELEMENT(element) &&
+ CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
{
- if (IS_FREE(x, y + 1))
+#if 1
+ if (!DigFieldByCE(newx, newy, element))
+ return;
+#else
+ int new_element = Feld[newx][newy];
+
+ if (!IS_FREE(newx, newy))
{
- InitMovingField(x, y, MV_DOWN);
- started_moving = TRUE;
+ int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
+ IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
+ ACTION_BREAKING);
+
+ /* no element can dig solid indestructible elements */
+ if (IS_INDESTRUCTIBLE(new_element) &&
+ !IS_DIGGABLE(new_element) &&
+ !IS_COLLECTIBLE(new_element))
+ return;
- Feld[x][y] = EL_QUICKSAND_EMPTYING;
- Store[x][y] = EL_ROCK;
-#if 1
- PlayLevelSoundAction(x, y, ACTION_EMPTYING);
-#else
- PlayLevelSound(x, y, SND_QUICKSAND_EMPTYING);
-#endif
- }
- else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
- {
- if (!MovDelay[x][y])
- MovDelay[x][y] = TILEY + 1;
+ if (AmoebaNr[newx][newy] &&
+ (new_element == EL_AMOEBA_FULL ||
+ new_element == EL_BD_AMOEBA ||
+ new_element == EL_AMOEBA_GROWING))
+ {
+ AmoebaCnt[AmoebaNr[newx][newy]]--;
+ AmoebaCnt2[AmoebaNr[newx][newy]]--;
+ }
- if (MovDelay[x][y])
+ if (IS_MOVING(newx, newy))
+ RemoveMovingField(newx, newy);
+ else
{
- MovDelay[x][y]--;
- if (MovDelay[x][y])
- return;
+ RemoveField(newx, newy);
+ TEST_DrawLevelField(newx, newy);
}
- Feld[x][y] = EL_QUICKSAND_EMPTY;
- Feld[x][y + 1] = EL_QUICKSAND_FULL;
- Store[x][y + 1] = Store[x][y];
- Store[x][y] = 0;
-#if 1
- PlayLevelSoundAction(x, y, ACTION_FILLING);
-#else
- PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
-#endif
+ /* if digged element was about to explode, prevent the explosion */
+ ExplodeField[newx][newy] = EX_TYPE_NONE;
+
+ PlayLevelSoundAction(x, y, action);
}
- }
- else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
- Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
- {
- InitMovingField(x, y, MV_DOWN);
- started_moving = TRUE;
- Feld[x][y] = EL_QUICKSAND_FILLING;
- Store[x][y] = element;
+ Store[newx][newy] = EL_EMPTY;
+
#if 1
- PlayLevelSoundAction(x, y, ACTION_FILLING);
+ /* this makes it possible to leave the removed element again */
+ if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
+ Store[newx][newy] = new_element;
#else
- PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
-#endif
- }
- else if (element == EL_MAGIC_WALL_FULL)
- {
- if (IS_FREE(x, y + 1))
+ if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
{
- InitMovingField(x, y, MV_DOWN);
- started_moving = TRUE;
+ int move_leave_element = element_info[element].move_leave_element;
- Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
- Store[x][y] = EL_CHANGED(Store[x][y]);
+ /* this makes it possible to leave the removed element again */
+ Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
+ new_element : move_leave_element);
}
- else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
- {
- if (!MovDelay[x][y])
- MovDelay[x][y] = TILEY/4 + 1;
+#endif
- if (MovDelay[x][y])
- {
- MovDelay[x][y]--;
- if (MovDelay[x][y])
- return;
- }
+#endif
- Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
- Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
- Store[x][y + 1] = EL_CHANGED(Store[x][y]);
- Store[x][y] = 0;
+ if (move_pattern & MV_MAZE_RUNNER_STYLE)
+ {
+ RunnerVisit[x][y] = FrameCounter;
+ PlayerVisit[x][y] /= 8; /* expire player visit path */
}
}
- else if (element == EL_BD_MAGIC_WALL_FULL)
+ else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
{
- if (IS_FREE(x, y + 1))
+ if (!IS_FREE(newx, newy))
{
- InitMovingField(x, y, MV_DOWN);
- started_moving = TRUE;
+ if (IS_PLAYER(x, y))
+ DrawPlayerField(x, y);
+ else
+ TEST_DrawLevelField(x, y);
- Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
- Store[x][y] = EL_CHANGED2(Store[x][y]);
+ return;
}
- else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
+ else
{
- if (!MovDelay[x][y])
- MovDelay[x][y] = TILEY/4 + 1;
+ boolean wanna_flame = !RND(10);
+ int dx = newx - x, dy = newy - y;
+ int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
+ int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
+ int element1 = (IN_LEV_FIELD(newx1, newy1) ?
+ MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
+ int element2 = (IN_LEV_FIELD(newx2, newy2) ?
+ MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
- if (MovDelay[x][y])
+ if ((wanna_flame ||
+ IS_CLASSIC_ENEMY(element1) ||
+ IS_CLASSIC_ENEMY(element2)) &&
+ element1 != EL_DRAGON && element2 != EL_DRAGON &&
+ element1 != EL_FLAMES && element2 != EL_FLAMES)
{
- MovDelay[x][y]--;
- if (MovDelay[x][y])
- return;
- }
+ ResetGfxAnimation(x, y);
+ GfxAction[x][y] = ACTION_ATTACKING;
- 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] = 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))
- {
- InitMovingField(x, y, MV_DOWN);
- started_moving = TRUE;
+ if (IS_PLAYER(x, y))
+ DrawPlayerField(x, y);
+ else
+ TEST_DrawLevelField(x, y);
- Feld[x][y] =
- (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
- EL_BD_MAGIC_WALL_FILLING);
- Store[x][y] = element;
- }
-#if 0
- else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_ACID)
-#else
- else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
-#endif
- {
- SplashAcid(x, y + 1);
+ PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
- InitMovingField(x, y, MV_DOWN);
- started_moving = TRUE;
+ MovDelay[x][y] = 50;
- Store[x][y] = EL_ACID;
+ /* !!! */
#if 0
- /* !!! TEST !!! better use "_FALLING" etc. !!! */
- GfxAction[x][y + 1] = ACTION_ACTIVE;
-#endif
- }
-#if 1
- else if ((game.engine_version >= VERSION_IDENT(3,1,0,0) &&
- CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
-
- (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
- CAN_SMASH(element) && WasJustFalling[x][y] &&
- (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
-
- (game.engine_version < VERSION_IDENT(2,2,0,7) &&
- CAN_SMASH(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
- (Feld[x][y + 1] == EL_BLOCKED)))
-
-#else
-#if 1
- else if (game.engine_version < VERSION_IDENT(2,2,0,7) &&
- CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
- WasJustMoving[x][y] && !Pushed[x][y + 1])
-#else
- else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
- WasJustMoving[x][y])
-#endif
+ RemoveField(newx, newy);
#endif
-
- {
- /* this is needed for a special case not covered by calling "Impact()"
- from "ContinueMoving()": if an element moves to a tile directly below
- another element which was just falling on that tile (which was empty
- in the previous frame), the falling element above would just stop
- instead of smashing the element below (in previous version, the above
- element was just checked for "moving" instead of "falling", resulting
- in incorrect smashes caused by horizontal movement of the above
- element; also, the case of the player being the element to smash was
- simply not covered here... :-/ ) */
-
+ Feld[newx][newy] = EL_FLAMES;
+ if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
+ {
#if 0
- WasJustMoving[x][y] = 0;
- WasJustFalling[x][y] = 0;
+ RemoveField(newx1, newy1);
#endif
-
- CheckCollision[x][y] = 0;
-
+ Feld[newx1][newy1] = EL_FLAMES;
+ }
+ if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
+ {
#if 0
- if (IS_PLAYER(x, y + 1))
- printf("::: we ARE now killing the player [%d]\n", FrameCounter);
+ RemoveField(newx2, newy2);
#endif
+ Feld[newx2][newy2] = EL_FLAMES;
+ }
- Impact(x, y);
+ return;
+ }
+ }
}
- else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
+ else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
+ Feld[newx][newy] == EL_DIAMOND)
{
- if (MovDir[x][y] == MV_NO_MOVING)
+ if (IS_MOVING(newx, newy))
+ RemoveMovingField(newx, newy);
+ else
{
- InitMovingField(x, y, MV_DOWN);
- started_moving = TRUE;
+ Feld[newx][newy] = EL_EMPTY;
+ TEST_DrawLevelField(newx, newy);
}
- }
- else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
- {
- if (WasJustFalling[x][y]) /* prevent animation from being restarted */
- MovDir[x][y] = MV_DOWN;
- InitMovingField(x, y, MV_DOWN);
- started_moving = TRUE;
- }
- else if (element == EL_AMOEBA_DROP)
- {
- Feld[x][y] = EL_AMOEBA_GROWING;
- Store[x][y] = EL_AMOEBA_WET;
+ PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
}
- /* Store[x][y + 1] must be zero, because:
- (EL_QUICKSAND_FULL -> EL_ROCK): Store[x][y + 1] == EL_QUICKSAND_EMPTY
- */
-#if 0
-#if OLD_GAME_BEHAVIOUR
- else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1])
-#else
- else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1] &&
- !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
- element != EL_DX_SUPABOMB)
-#endif
-#else
- else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
- (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
- !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
- element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
-#endif
+ else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
+ IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
{
- boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
- (IS_FREE(x - 1, y + 1) ||
- Feld[x - 1][y + 1] == EL_ACID));
- boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
- (IS_FREE(x + 1, y + 1) ||
- Feld[x + 1][y + 1] == EL_ACID));
- boolean can_fall_any = (can_fall_left || can_fall_right);
- boolean can_fall_both = (can_fall_left && can_fall_right);
-
- if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
+ if (AmoebaNr[newx][newy])
{
- int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
-
- if (slippery_type == SLIPPERY_ONLY_LEFT)
- can_fall_right = FALSE;
- else if (slippery_type == SLIPPERY_ONLY_RIGHT)
- can_fall_left = FALSE;
- else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
- can_fall_right = FALSE;
- else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
- can_fall_left = FALSE;
-
- can_fall_any = (can_fall_left || can_fall_right);
- can_fall_both = (can_fall_left && can_fall_right);
+ AmoebaCnt2[AmoebaNr[newx][newy]]--;
+ if (Feld[newx][newy] == EL_AMOEBA_FULL ||
+ Feld[newx][newy] == EL_BD_AMOEBA)
+ AmoebaCnt[AmoebaNr[newx][newy]]--;
}
-#if USE_NEW_SP_SLIPPERY
- /* !!! better use the same properties as for custom elements here !!! */
- else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
- can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
+#if 0
+ /* !!! test !!! */
+ if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
{
- can_fall_right = FALSE; /* slip down on left side */
- can_fall_both = FALSE;
+ RemoveMovingField(newx, newy);
}
-#endif
-
-#if 1
- if (can_fall_both)
+#else
+ if (IS_MOVING(newx, newy))
{
- if (game.emulation == EMU_BOULDERDASH ||
- element == EL_BD_ROCK || element == EL_BD_DIAMOND)
- can_fall_right = FALSE; /* slip down on left side */
- else
- can_fall_left = !(can_fall_right = RND(2));
-
- can_fall_both = FALSE;
+ RemoveMovingField(newx, newy);
}
#endif
-
- if (can_fall_any)
+ else
{
-#if 0
- if (can_fall_both &&
- (game.emulation != EMU_BOULDERDASH &&
- element != EL_BD_ROCK && element != EL_BD_DIAMOND))
- can_fall_left = !(can_fall_right = RND(2));
-#endif
-
- /* if not determined otherwise, prefer left side for slipping down */
- InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
- started_moving = TRUE;
+ Feld[newx][newy] = EL_EMPTY;
+ TEST_DrawLevelField(newx, newy);
}
+
+ PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
}
-#if 0
- else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
-#else
- else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
-#endif
+ else if ((element == EL_PACMAN || element == EL_MOLE)
+ && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
{
- boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
- boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
- int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
- int belt_dir = game.belt_dir[belt_nr];
+ if (AmoebaNr[newx][newy])
+ {
+ AmoebaCnt2[AmoebaNr[newx][newy]]--;
+ if (Feld[newx][newy] == EL_AMOEBA_FULL ||
+ Feld[newx][newy] == EL_BD_AMOEBA)
+ AmoebaCnt[AmoebaNr[newx][newy]]--;
+ }
- if ((belt_dir == MV_LEFT && left_is_free) ||
- (belt_dir == MV_RIGHT && right_is_free))
+ if (element == EL_MOLE)
{
-#if 1
- int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
-#endif
+ Feld[newx][newy] = EL_AMOEBA_SHRINKING;
+ PlayLevelSound(x, y, SND_MOLE_DIGGING);
- InitMovingField(x, y, belt_dir);
- started_moving = TRUE;
+ ResetGfxAnimation(x, y);
+ GfxAction[x][y] = ACTION_DIGGING;
+ TEST_DrawLevelField(x, y);
-#if 1
- Pushed[x][y] = TRUE;
- Pushed[nextx][y] = TRUE;
-#endif
+ MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
- GfxAction[x][y] = ACTION_DEFAULT;
+ return; /* wait for shrinking amoeba */
}
- else
+ else /* element == EL_PACMAN */
{
- MovDir[x][y] = 0; /* if element was moving, stop it */
+ Feld[newx][newy] = EL_EMPTY;
+ TEST_DrawLevelField(newx, newy);
+ PlayLevelSound(x, y, SND_PACMAN_DIGGING);
}
}
- }
+ else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
+ (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
+ (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
+ {
+ /* wait for shrinking amoeba to completely disappear */
+ return;
+ }
+ else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
+ {
+ /* object was running against a wall */
- /* not "else if" because of elements that can fall and move (EL_SPRING) */
-#if 0
- if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NO_MOVING)
-#else
- if (CAN_MOVE(element) && !started_moving)
-#endif
- {
- int move_pattern = element_info[element].move_pattern;
- int newx, newy;
+ TurnRound(x, y);
#if 0
-#if DEBUG
- if (MovDir[x][y] == MV_NO_MOVING)
- {
- printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
- x, y, element, element_info[element].token_name);
- printf("StartMoving(): This should never happen!\n");
- }
-#endif
+ /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
+ if (move_pattern & MV_ANY_DIRECTION &&
+ move_pattern == MovDir[x][y])
+ {
+ int blocking_element =
+ (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
+
+ CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
+ MovDir[x][y]);
+
+ element = Feld[x][y]; /* element might have changed */
+ }
#endif
- Moving2Blocked(x, y, &newx, &newy);
+ if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
+ DrawLevelElementAnimation(x, y, element);
+
+ if (DONT_TOUCH(element))
+ TestIfBadThingTouchesPlayer(x, y);
-#if 1
- if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
- return;
-#else
- if ((element == EL_SATELLITE ||
- element == EL_BALLOON ||
- element == EL_SPRING)
- && JustBeingPushed(x, y))
return;
-#endif
+ }
-#if 1
+ InitMovingField(x, y, MovDir[x][y]);
-#if 1
- if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
- CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
-#else
- if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
- WasJustMoving[x][y] && IN_LEV_FIELD(newx, newy) &&
- (Feld[newx][newy] == EL_BLOCKED || IS_PLAYER(newx, newy)))
-#endif
- {
-#if 0
- printf("::: element %d '%s' WasJustMoving %d [%d, %d, %d, %d]\n",
- element, element_info[element].token_name,
- WasJustMoving[x][y],
- HAS_ANY_CHANGE_EVENT(element, CE_HITTING_SOMETHING),
- HAS_ANY_CHANGE_EVENT(element, CE_HIT_BY_SOMETHING),
- HAS_ANY_CHANGE_EVENT(element, CE_OTHER_IS_HITTING),
- HAS_ANY_CHANGE_EVENT(element, CE_OTHER_GETS_HIT));
-#endif
+ PlayLevelSoundAction(x, y, ACTION_MOVING);
+ }
-#if 1
- WasJustMoving[x][y] = 0;
-#endif
+ if (MovDir[x][y])
+ ContinueMoving(x, y);
+}
- CheckCollision[x][y] = 0;
+void ContinueMoving(int x, int y)
+{
+ int element = Feld[x][y];
+ struct ElementInfo *ei = &element_info[element];
+ 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 newx = x + dx, newy = y + dy;
+ int stored = Store[x][y];
+ int stored_new = Store[newx][newy];
+ boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
+ boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
+ boolean last_line = (newy == lev_fieldy - 1);
- TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
+ MovPos[x][y] += getElementMoveStepsize(x, y);
+
+ if (pushed_by_player) /* special case: moving object pushed by player */
+ MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
+ if (ABS(MovPos[x][y]) < TILEX)
+ {
#if 0
- if (Feld[x][y] != element) /* element has changed */
- {
- element = Feld[x][y];
- move_pattern = element_info[element].move_pattern;
+ int ee = Feld[x][y];
+ int gg = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
+ int ff = getGraphicAnimationFrame(gg, GfxFrame[x][y]);
- if (!CAN_MOVE(element))
- return;
- }
-#else
- if (Feld[x][y] != element) /* element has changed */
- return;
-#endif
- }
+ 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
-#if 0
-#if 0
- if (element == EL_SPRING && MovDir[x][y] == MV_DOWN)
- Feld[x][y + 1] = EL_EMPTY; /* was set to EL_BLOCKED above */
-#else
- if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
- {
- Moving2Blocked(x, y, &newx, &newy);
- if (Feld[newx][newy] == EL_BLOCKED)
- Feld[newx][newy] = EL_EMPTY; /* was set to EL_BLOCKED above */
- }
+ TEST_DrawLevelField(x, y);
+
+ return; /* element is still moving */
+ }
+
+ /* element reached destination field */
+
+ Feld[x][y] = EL_EMPTY;
+ Feld[newx][newy] = element;
+ MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
+
+ if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
+ {
+ element = Feld[newx][newy] = EL_ACID;
+ }
+ else if (element == EL_MOLE)
+ {
+ Feld[x][y] = EL_SAND;
+
+ TEST_DrawLevelFieldCrumbledNeighbours(x, y);
+ }
+ else if (element == EL_QUICKSAND_FILLING)
+ {
+ element = Feld[newx][newy] = get_next_element(element);
+ Store[newx][newy] = Store[x][y];
+ }
+ else if (element == EL_QUICKSAND_EMPTYING)
+ {
+ 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);
+ if (!game.magic_wall_active)
+ element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
+ Store[newx][newy] = Store[x][y];
+ }
+ else if (element == EL_MAGIC_WALL_EMPTYING)
+ {
+ Feld[x][y] = get_next_element(element);
+ if (!game.magic_wall_active)
+ Feld[x][y] = EL_MAGIC_WALL_DEAD;
+ element = Feld[newx][newy] = Store[x][y];
+
+#if USE_NEW_CUSTOM_VALUE
+ InitField(newx, newy, FALSE);
#endif
+ }
+ else if (element == EL_BD_MAGIC_WALL_FILLING)
+ {
+ element = Feld[newx][newy] = get_next_element(element);
+ if (!game.magic_wall_active)
+ element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
+ Store[newx][newy] = Store[x][y];
+ }
+ else if (element == EL_BD_MAGIC_WALL_EMPTYING)
+ {
+ Feld[x][y] = get_next_element(element);
+ if (!game.magic_wall_active)
+ 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 0
- if (FrameCounter < 1 && x == 0 && y == 29)
- printf(":1: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
+#if USE_NEW_CUSTOM_VALUE
+ InitField(newx, newy, FALSE);
#endif
+ }
+ else if (element == EL_AMOEBA_DROPPING)
+ {
+ Feld[x][y] = get_next_element(element);
+ element = Feld[newx][newy] = Store[x][y];
+ }
+ else if (element == EL_SOKOBAN_OBJECT)
+ {
+ if (Back[x][y])
+ Feld[x][y] = Back[x][y];
- if (!MovDelay[x][y]) /* start new movement phase */
- {
- /* all objects that can change their move direction after each step
- (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
+ if (Back[newx][newy])
+ Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
- if (element != EL_YAMYAM &&
- element != EL_DARK_YAMYAM &&
- element != EL_PACMAN &&
- !(move_pattern & MV_ANY_DIRECTION) &&
- move_pattern != MV_TURNING_LEFT &&
- move_pattern != MV_TURNING_RIGHT &&
- move_pattern != MV_TURNING_LEFT_RIGHT &&
- move_pattern != MV_TURNING_RIGHT_LEFT &&
- move_pattern != MV_TURNING_RANDOM)
- {
- TurnRound(x, y);
+ Back[x][y] = Back[newx][newy] = 0;
+ }
-#if 0
- if (FrameCounter < 1 && x == 0 && y == 29)
- printf(":9: %d: %d [%d]\n", y, MovDir[x][y], FrameCounter);
-#endif
+ Store[x][y] = EL_EMPTY;
+ MovPos[x][y] = 0;
+ MovDir[x][y] = 0;
+ MovDelay[x][y] = 0;
- if (MovDelay[x][y] && (element == EL_BUG ||
- element == EL_SPACESHIP ||
- element == EL_SP_SNIKSNAK ||
- element == EL_SP_ELECTRON ||
- element == EL_MOLE))
- DrawLevelField(x, y);
- }
- }
+ MovDelay[newx][newy] = 0;
- if (MovDelay[x][y]) /* wait some time before next movement */
- {
- MovDelay[x][y]--;
+ if (CAN_CHANGE_OR_HAS_ACTION(element))
+ {
+ /* copy element change control values to new field */
+ ChangeDelay[newx][newy] = ChangeDelay[x][y];
+ ChangePage[newx][newy] = ChangePage[x][y];
+ ChangeCount[newx][newy] = ChangeCount[x][y];
+ ChangeEvent[newx][newy] = ChangeEvent[x][y];
+ }
-#if 0
- if (element == EL_YAMYAM)
- {
- printf("::: %d\n",
- el_act_dir2img(EL_YAMYAM, ACTION_WAITING, MV_LEFT));
- DrawLevelElementAnimation(x, y, element);
- }
+#if USE_NEW_CUSTOM_VALUE
+ CustomValue[newx][newy] = CustomValue[x][y];
#endif
- if (MovDelay[x][y]) /* element still has to wait some time */
- {
-#if 0
- /* !!! PLACE THIS SOMEWHERE AFTER "TurnRound()" !!! */
- ResetGfxAnimation(x, y);
+ ChangeDelay[x][y] = 0;
+ ChangePage[x][y] = -1;
+ ChangeCount[x][y] = 0;
+ ChangeEvent[x][y] = -1;
+
+#if USE_NEW_CUSTOM_VALUE
+ CustomValue[x][y] = 0;
#endif
-#if 0
- if (GfxAction[x][y] != ACTION_WAITING)
- printf("::: %d: %d != ACTION_WAITING\n", element, GfxAction[x][y]);
+ /* copy animation control values to new field */
+ GfxFrame[newx][newy] = GfxFrame[x][y];
+ GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
+ GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
+ GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
- GfxAction[x][y] = ACTION_WAITING;
+ Pushed[x][y] = Pushed[newx][newy] = FALSE;
+
+ /* some elements can leave other elements behind after moving */
+#if 1
+ if (ei->move_leave_element != EL_EMPTY &&
+ (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
+ (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
+#else
+ if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
+ (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
+ (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
#endif
- }
+ {
+ int move_leave_element = ei->move_leave_element;
- if (element == EL_ROBOT ||
-#if 0
- element == EL_PACMAN ||
+#if 1
+#if 1
+ /* this makes it possible to leave the removed element again */
+ if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
+ move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
+#else
+ /* this makes it possible to leave the removed element again */
+ if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
+ move_leave_element = stored;
#endif
- element == EL_YAMYAM ||
- element == EL_DARK_YAMYAM)
- {
-#if 0
- DrawLevelElementAnimation(x, y, element);
#else
- DrawLevelElementAnimationIfNeeded(x, y, element);
+ /* this makes it possible to leave the removed element again */
+ if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
+ ei->move_leave_element == EL_TRIGGER_ELEMENT)
+ move_leave_element = stored;
#endif
- PlayLevelSoundAction(x, y, ACTION_WAITING);
- }
- else if (element == EL_SP_ELECTRON)
- DrawLevelElementAnimationIfNeeded(x, y, element);
- else if (element == EL_DRAGON)
- {
- int i;
- int dir = MovDir[x][y];
- int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
- int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
- int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
- dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
- dir == MV_UP ? IMG_FLAMES_1_UP :
- dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
- int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
-#if 0
- printf("::: %d, %d\n", GfxAction[x][y], GfxFrame[x][y]);
-#endif
+ Feld[x][y] = move_leave_element;
- GfxAction[x][y] = ACTION_ATTACKING;
+ if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
+ MovDir[x][y] = direction;
- if (IS_PLAYER(x, y))
- DrawPlayerField(x, y);
- else
- DrawLevelField(x, y);
+ InitField(x, y, FALSE);
- PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
+ if (GFX_CRUMBLED(Feld[x][y]))
+ TEST_DrawLevelFieldCrumbledNeighbours(x, y);
- for (i = 1; i <= 3; i++)
- {
- int xx = x + i * dx;
- int yy = y + i * dy;
- int sx = SCREENX(xx);
- int sy = SCREENY(yy);
- int flame_graphic = graphic + (i - 1);
+ if (ELEM_IS_PLAYER(move_leave_element))
+ RelocatePlayer(x, y, move_leave_element);
+ }
- if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
- break;
+ /* do this after checking for left-behind element */
+ ResetGfxAnimation(x, y); /* reset animation values for old field */
- if (MovDelay[x][y])
- {
- int flamed = MovingOrBlocked2Element(xx, yy);
+ if (!CAN_MOVE(element) ||
+ (CAN_FALL(element) && direction == MV_DOWN &&
+ (element == EL_SPRING ||
+ element_info[element].move_pattern == MV_WHEN_PUSHED ||
+ element_info[element].move_pattern == MV_WHEN_DROPPED)))
+ GfxDir[x][y] = MovDir[newx][newy] = 0;
- /* !!! */
-#if 0
- if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
- Bang(xx, yy);
- else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
- RemoveMovingField(xx, yy);
- else
- RemoveField(xx, yy);
-#else
- if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
- Bang(xx, yy);
- else
- RemoveMovingField(xx, yy);
-#endif
+ TEST_DrawLevelField(x, y);
+ TEST_DrawLevelField(newx, newy);
-#if 0
- if (ChangeDelay[xx][yy])
- printf("::: !!! [%d]\n", (IS_MOVING(xx, yy) ||
- Feld[xx][yy] == EL_BLOCKED));
-#endif
+ Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
-#if 1
- ChangeDelay[xx][yy] = 0;
-#endif
- Feld[xx][yy] = EL_FLAMES;
- if (IN_SCR_FIELD(sx, sy))
- {
- DrawLevelFieldCrumbledSand(xx, yy);
- DrawGraphic(sx, sy, flame_graphic, frame);
- }
- }
- else
- {
- if (Feld[xx][yy] == EL_FLAMES)
- Feld[xx][yy] = EL_EMPTY;
- DrawLevelField(xx, yy);
- }
- }
- }
+ /* prevent pushed element from moving on in pushed direction */
+ if (pushed_by_player && CAN_MOVE(element) &&
+ element_info[element].move_pattern & MV_ANY_DIRECTION &&
+ !(element_info[element].move_pattern & direction))
+ TurnRound(newx, newy);
- if (MovDelay[x][y]) /* element still has to wait some time */
- {
- PlayLevelSoundAction(x, y, ACTION_WAITING);
+ /* prevent elements on conveyor belt from moving on in last direction */
+ if (pushed_by_conveyor && CAN_FALL(element) &&
+ direction & MV_HORIZONTAL)
+ MovDir[newx][newy] = 0;
- return;
- }
+ if (!pushed_by_player)
+ {
+ int nextx = newx + dx, nexty = newy + dy;
+ boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
-#if 0
- /* special case of "moving" animation of waiting elements (FIX THIS !!!);
- for all other elements GfxAction will be set by InitMovingField() */
- if (element == EL_BD_BUTTERFLY || element == EL_BD_FIREFLY)
- GfxAction[x][y] = ACTION_MOVING;
+ WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
+
+ if (CAN_FALL(element) && direction == MV_DOWN)
+ WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
+
+ 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
- }
+ }
- /* now make next step */
+ if (DONT_TOUCH(element)) /* object may be nasty to player or others */
+ {
+ TestIfBadThingTouchesPlayer(newx, newy);
+ TestIfBadThingTouchesFriend(newx, newy);
- Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
+ if (!IS_CUSTOM_ELEMENT(element))
+ TestIfBadThingTouchesOtherBadThing(newx, newy);
+ }
+ else if (element == EL_PENGUIN)
+ TestIfFriendTouchesBadThing(newx, newy);
- if (DONT_COLLIDE_WITH(element) &&
- IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
- !PLAYER_ENEMY_PROTECTED(newx, newy))
- {
-#if 1
- TestIfBadThingRunsIntoHero(x, y, MovDir[x][y]);
+ if (DONT_GET_HIT_BY(element))
+ {
+ TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
+ }
- return;
-#else
- /* player killed by element which is deadly when colliding with */
- MovDir[x][y] = 0;
- KillHero(PLAYERINFO(newx, newy));
- return;
-#endif
+ /* give the player one last chance (one more frame) to move away */
+ if (CAN_FALL(element) && direction == MV_DOWN &&
+ (last_line || (!IS_FREE(x, newy + 1) &&
+ (!IS_PLAYER(x, newy + 1) ||
+ game.engine_version < VERSION_IDENT(3,1,1,0)))))
+ Impact(x, newy);
- }
-#if 1
-#if 1
- else if (CAN_MOVE_INTO_ACID(element) &&
- IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
- (MovDir[x][y] == MV_DOWN ||
- game.engine_version >= VERSION_IDENT(3,1,0,0)))
-#else
- else if (CAN_MOVE_INTO_ACID(element) && MovDir[x][y] == MV_DOWN &&
- IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID)
-#endif
-#else
+ if (pushed_by_player && !game.use_change_when_pushing_bug)
+ {
+ int push_side = MV_DIR_OPPOSITE(direction);
+ struct PlayerInfo *player = PLAYERINFO(x, y);
- else if ((element == EL_PENGUIN ||
- element == EL_ROBOT ||
- element == EL_SATELLITE ||
- element == EL_BALLOON ||
- IS_CUSTOM_ELEMENT(element)) &&
- IN_LEV_FIELD(newx, newy) &&
- MovDir[x][y] == MV_DOWN && Feld[newx][newy] == EL_ACID)
-#endif
- {
- SplashAcid(newx, newy);
- Store[x][y] = EL_ACID;
- }
- else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
- {
- if (Feld[newx][newy] == EL_EXIT_OPEN)
- {
-#if 1
- RemoveField(x, y);
- DrawLevelField(x, y);
-#else
- Feld[x][y] = EL_EMPTY;
- DrawLevelField(x, y);
-#endif
+ CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
+ player->index_bit, push_side);
+ CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
+ player->index_bit, push_side);
+ }
- PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
- if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
- DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
+ if (element == EL_EMC_ANDROID && pushed_by_player) /* make another move */
+ MovDelay[newx][newy] = 1;
- local_player->friends_still_needed--;
- if (!local_player->friends_still_needed &&
- !local_player->GameOver && AllPlayersGone)
- local_player->LevelSolved = local_player->GameOver = TRUE;
+ CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
+
+ TestIfElementTouchesCustomElement(x, y); /* empty or new element */
- return;
- }
- else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
- {
- if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MF_MOVING)
- DrawLevelField(newx, newy);
- else
- GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
- }
- else if (!IS_FREE(newx, newy))
- {
- GfxAction[x][y] = ACTION_WAITING;
+#if 0
+ if (ChangePage[newx][newy] != -1) /* delayed change */
+ {
+ int page = ChangePage[newx][newy];
+ struct ElementChangeInfo *change = &ei->change_page[page];
- if (IS_PLAYER(x, y))
- DrawPlayerField(x, y);
- else
- DrawLevelField(x, y);
+ ChangePage[newx][newy] = -1;
- return;
- }
- }
- else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
+ if (change->can_change)
{
- if (IS_FOOD_PIG(Feld[newx][newy]))
- {
- if (IS_MOVING(newx, newy))
- RemoveMovingField(newx, newy);
- else
- {
- Feld[newx][newy] = EL_EMPTY;
- DrawLevelField(newx, newy);
- }
-
- PlayLevelSound(x, y, SND_PIG_DIGGING);
- }
- else if (!IS_FREE(newx, newy))
+ if (ChangeElement(newx, newy, element, page))
{
- if (IS_PLAYER(x, y))
- DrawPlayerField(x, y);
- else
- DrawLevelField(x, y);
-
- return;
+ if (change->post_change_function)
+ change->post_change_function(newx, newy);
}
}
-#if 1
+ if (change->has_action)
+ ExecuteCustomElementAction(newx, newy, element, page);
+ }
+#endif
- /*
- else if (move_pattern & MV_MAZE_RUNNER_STYLE && IN_LEV_FIELD(newx, newy))
- */
+ TestIfElementHitsCustomElement(newx, newy, direction);
+ TestIfPlayerTouchesCustomElement(newx, newy);
+ TestIfElementTouchesCustomElement(newx, newy);
- else if (IS_CUSTOM_ELEMENT(element) &&
- CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy)
+ 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));
+}
-#if 0
- &&
- !IS_FREE(newx, newy)
-#endif
+int AmoebeNachbarNr(int ax, int ay)
+{
+ int i;
+ int element = Feld[ax][ay];
+ int group_nr = 0;
+ static int xy[4][2] =
+ {
+ { 0, -1 },
+ { -1, 0 },
+ { +1, 0 },
+ { 0, +1 }
+ };
-)
- {
- int new_element = Feld[newx][newy];
+ for (i = 0; i < NUM_DIRECTIONS; i++)
+ {
+ int x = ax + xy[i][0];
+ int y = ay + xy[i][1];
-#if 0
- printf("::: '%s' digs '%s' [%d]\n",
- element_info[element].token_name,
- element_info[Feld[newx][newy]].token_name,
- StorePlayer[newx][newy]);
-#endif
+ if (!IN_LEV_FIELD(x, y))
+ continue;
- if (!IS_FREE(newx, newy))
- {
- int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
- IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
- ACTION_BREAKING);
+ if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
+ group_nr = AmoebaNr[x][y];
+ }
- /* no element can dig solid indestructible elements */
- if (IS_INDESTRUCTIBLE(new_element) &&
- !IS_DIGGABLE(new_element) &&
- !IS_COLLECTIBLE(new_element))
- return;
+ return group_nr;
+}
- if (AmoebaNr[newx][newy] &&
- (new_element == EL_AMOEBA_FULL ||
- new_element == EL_BD_AMOEBA ||
- new_element == EL_AMOEBA_GROWING))
- {
- AmoebaCnt[AmoebaNr[newx][newy]]--;
- AmoebaCnt2[AmoebaNr[newx][newy]]--;
- }
+void AmoebenVereinigen(int ax, int ay)
+{
+ int i, x, y, xx, yy;
+ int new_group_nr = AmoebaNr[ax][ay];
+ static int xy[4][2] =
+ {
+ { 0, -1 },
+ { -1, 0 },
+ { +1, 0 },
+ { 0, +1 }
+ };
- if (IS_MOVING(newx, newy))
- RemoveMovingField(newx, newy);
- else
- {
- RemoveField(newx, newy);
- DrawLevelField(newx, newy);
- }
+ if (new_group_nr == 0)
+ return;
- /* if digged element was about to explode, prevent the explosion */
- ExplodeField[newx][newy] = EX_TYPE_NONE;
+ for (i = 0; i < NUM_DIRECTIONS; i++)
+ {
+ x = ax + xy[i][0];
+ y = ay + xy[i][1];
- PlayLevelSoundAction(x, y, action);
- }
+ if (!IN_LEV_FIELD(x, y))
+ continue;
-#if 1
-#if 1
- Store[newx][newy] = EL_EMPTY;
- if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
- Store[newx][newy] = element_info[element].move_leave_element;
-#else
- Store[newx][newy] = EL_EMPTY;
- if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)) ||
- element_info[element].move_leave_type == LEAVE_TYPE_UNLIMITED)
- Store[newx][newy] = element_info[element].move_leave_element;
-#endif
-#else
- if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
- element_info[element].can_leave_element = TRUE;
-#endif
+ if ((Feld[x][y] == EL_AMOEBA_FULL ||
+ Feld[x][y] == EL_BD_AMOEBA ||
+ Feld[x][y] == EL_AMOEBA_DEAD) &&
+ AmoebaNr[x][y] != new_group_nr)
+ {
+ int old_group_nr = AmoebaNr[x][y];
- if (move_pattern & MV_MAZE_RUNNER_STYLE)
+ if (old_group_nr == 0)
+ return;
+
+ AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
+ AmoebaCnt[old_group_nr] = 0;
+ AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
+ AmoebaCnt2[old_group_nr] = 0;
+
+ SCAN_PLAYFIELD(xx, yy)
{
- RunnerVisit[x][y] = FrameCounter;
- PlayerVisit[x][y] /= 8; /* expire player visit path */
+ if (AmoebaNr[xx][yy] == old_group_nr)
+ AmoebaNr[xx][yy] = new_group_nr;
}
}
+ }
+}
+
+void AmoebeUmwandeln(int ax, int ay)
+{
+ int i, x, y;
+
+ if (Feld[ax][ay] == EL_AMOEBA_DEAD)
+ {
+ int group_nr = AmoebaNr[ax][ay];
+#ifdef DEBUG
+ if (group_nr == 0)
+ {
+ printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
+ printf("AmoebeUmwandeln(): This should never happen!\n");
+ return;
+ }
#endif
- else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
+ SCAN_PLAYFIELD(x, y)
{
- if (!IS_FREE(newx, newy))
+ if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
{
- if (IS_PLAYER(x, y))
- DrawPlayerField(x, y);
- else
- DrawLevelField(x, y);
-
- return;
+ AmoebaNr[x][y] = 0;
+ Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
}
- else
- {
- boolean wanna_flame = !RND(10);
- int dx = newx - x, dy = newy - y;
- int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
- int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
- int element1 = (IN_LEV_FIELD(newx1, newy1) ?
- MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
- int element2 = (IN_LEV_FIELD(newx2, newy2) ?
- MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
+ }
- if ((wanna_flame ||
- IS_CLASSIC_ENEMY(element1) ||
- IS_CLASSIC_ENEMY(element2)) &&
- element1 != EL_DRAGON && element2 != EL_DRAGON &&
- element1 != EL_FLAMES && element2 != EL_FLAMES)
- {
-#if 1
- ResetGfxAnimation(x, y);
- GfxAction[x][y] = ACTION_ATTACKING;
-#endif
+ PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
+ SND_AMOEBA_TURNING_TO_GEM :
+ SND_AMOEBA_TURNING_TO_ROCK));
+ Bang(ax, ay);
+ }
+ else
+ {
+ static int xy[4][2] =
+ {
+ { 0, -1 },
+ { -1, 0 },
+ { +1, 0 },
+ { 0, +1 }
+ };
- if (IS_PLAYER(x, y))
- DrawPlayerField(x, y);
- else
- DrawLevelField(x, y);
+ for (i = 0; i < NUM_DIRECTIONS; i++)
+ {
+ x = ax + xy[i][0];
+ y = ay + xy[i][1];
- PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
+ if (!IN_LEV_FIELD(x, y))
+ continue;
- MovDelay[x][y] = 50;
+ if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
+ {
+ PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
+ SND_AMOEBA_TURNING_TO_GEM :
+ SND_AMOEBA_TURNING_TO_ROCK));
+ Bang(x, y);
+ }
+ }
+ }
+}
- /* !!! */
-#if 0
- RemoveField(newx, newy);
-#endif
- Feld[newx][newy] = EL_FLAMES;
- if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
- {
-#if 0
- RemoveField(newx1, newy1);
-#endif
- Feld[newx1][newy1] = EL_FLAMES;
- }
- if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
- {
-#if 0
- RemoveField(newx2, newy2);
+void AmoebeUmwandelnBD(int ax, int ay, int new_element)
+{
+ int x, y;
+ int group_nr = AmoebaNr[ax][ay];
+ boolean done = FALSE;
+
+#ifdef DEBUG
+ if (group_nr == 0)
+ {
+ printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
+ printf("AmoebeUmwandelnBD(): This should never happen!\n");
+ return;
+ }
#endif
- Feld[newx2][newy2] = EL_FLAMES;
- }
- return;
- }
- }
- }
- else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
- Feld[newx][newy] == EL_DIAMOND)
+ SCAN_PLAYFIELD(x, y)
+ {
+ if (AmoebaNr[x][y] == group_nr &&
+ (Feld[x][y] == EL_AMOEBA_DEAD ||
+ Feld[x][y] == EL_BD_AMOEBA ||
+ Feld[x][y] == EL_AMOEBA_GROWING))
{
- if (IS_MOVING(newx, newy))
- RemoveMovingField(newx, newy);
- else
- {
- Feld[newx][newy] = EL_EMPTY;
- DrawLevelField(newx, newy);
- }
-
- PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
+ AmoebaNr[x][y] = 0;
+ Feld[x][y] = new_element;
+ InitField(x, y, FALSE);
+ TEST_DrawLevelField(x, y);
+ done = TRUE;
}
- else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
- IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
+ }
+
+ if (done)
+ PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
+ SND_BD_AMOEBA_TURNING_TO_ROCK :
+ SND_BD_AMOEBA_TURNING_TO_GEM));
+}
+
+void AmoebeWaechst(int x, int y)
+{
+ static unsigned long sound_delay = 0;
+ static unsigned long sound_delay_value = 0;
+
+ if (!MovDelay[x][y]) /* start new growing cycle */
+ {
+ MovDelay[x][y] = 7;
+
+ if (DelayReached(&sound_delay, sound_delay_value))
{
- if (AmoebaNr[newx][newy])
- {
- AmoebaCnt2[AmoebaNr[newx][newy]]--;
- if (Feld[newx][newy] == EL_AMOEBA_FULL ||
- Feld[newx][newy] == EL_BD_AMOEBA)
- AmoebaCnt[AmoebaNr[newx][newy]]--;
- }
+ PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
+ sound_delay_value = 30;
+ }
+ }
-#if 0
- /* !!! test !!! */
- if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
-#else
- if (IS_MOVING(newx, newy))
-#endif
- {
- RemoveMovingField(newx, newy);
- }
- else
- {
- Feld[newx][newy] = EL_EMPTY;
- DrawLevelField(newx, newy);
- }
+ if (MovDelay[x][y]) /* wait some time before growing bigger */
+ {
+ MovDelay[x][y]--;
+ if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
+ {
+ int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
+ 6 - MovDelay[x][y]);
- PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
+ DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
}
- else if ((element == EL_PACMAN || element == EL_MOLE)
- && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
+
+ if (!MovDelay[x][y])
{
- if (AmoebaNr[newx][newy])
- {
- AmoebaCnt2[AmoebaNr[newx][newy]]--;
- if (Feld[newx][newy] == EL_AMOEBA_FULL ||
- Feld[newx][newy] == EL_BD_AMOEBA)
- AmoebaCnt[AmoebaNr[newx][newy]]--;
- }
+ Feld[x][y] = Store[x][y];
+ Store[x][y] = 0;
+ TEST_DrawLevelField(x, y);
+ }
+ }
+}
- if (element == EL_MOLE)
- {
- Feld[newx][newy] = EL_AMOEBA_SHRINKING;
- PlayLevelSound(x, y, SND_MOLE_DIGGING);
+void AmoebaDisappearing(int x, int y)
+{
+ static unsigned long sound_delay = 0;
+ static unsigned long sound_delay_value = 0;
- ResetGfxAnimation(x, y);
- GfxAction[x][y] = ACTION_DIGGING;
- DrawLevelField(x, y);
+ if (!MovDelay[x][y]) /* start new shrinking cycle */
+ {
+ MovDelay[x][y] = 7;
- MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
+ if (DelayReached(&sound_delay, sound_delay_value))
+ sound_delay_value = 30;
+ }
- return; /* wait for shrinking amoeba */
- }
- else /* element == EL_PACMAN */
- {
- Feld[newx][newy] = EL_EMPTY;
- DrawLevelField(newx, newy);
- PlayLevelSound(x, y, SND_PACMAN_DIGGING);
- }
- }
- else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
- (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
- (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
+ if (MovDelay[x][y]) /* wait some time before shrinking */
+ {
+ MovDelay[x][y]--;
+ if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
{
- /* wait for shrinking amoeba to completely disappear */
- return;
+ int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
+ 6 - MovDelay[x][y]);
+
+ DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
}
- else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
- {
- /* object was running against a wall */
- TurnRound(x, y);
+ if (!MovDelay[x][y])
+ {
+ Feld[x][y] = EL_EMPTY;
+ TEST_DrawLevelField(x, y);
-#if 0
- if (move_pattern & MV_ANY_DIRECTION &&
- move_pattern == MovDir[x][y])
- {
- int blocking_element =
- (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
+ /* don't let mole enter this field in this cycle;
+ (give priority to objects falling to this field from above) */
+ Stop[x][y] = TRUE;
+ }
+ }
+}
-#if 0
- printf("::: '%s' is blocked by '%s'! [%d,%d -> %d,%d]\n",
- element_info[element].token_name,
- element_info[blocking_element].token_name,
- x, y, newx, newy);
-#endif
+void AmoebeAbleger(int ax, int ay)
+{
+ int i;
+ int element = Feld[ax][ay];
+ int graphic = el2img(element);
+ int newax = ax, neway = ay;
+ boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
+ static int xy[4][2] =
+ {
+ { 0, -1 },
+ { -1, 0 },
+ { +1, 0 },
+ { 0, +1 }
+ };
- CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
- MovDir[x][y]);
+ if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
+ {
+ Feld[ax][ay] = EL_AMOEBA_DEAD;
+ TEST_DrawLevelField(ax, ay);
+ return;
+ }
- element = Feld[x][y]; /* element might have changed */
- }
-#endif
+ if (IS_ANIMATED(graphic))
+ DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
-#if 1
- if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
- DrawLevelElementAnimation(x, y, element);
-#else
- if (element == EL_BUG ||
- element == EL_SPACESHIP ||
- element == EL_SP_SNIKSNAK)
- DrawLevelField(x, y);
- else if (element == EL_MOLE)
- DrawLevelField(x, y);
- else if (element == EL_BD_BUTTERFLY ||
- element == EL_BD_FIREFLY)
- DrawLevelElementAnimationIfNeeded(x, y, element);
- else if (element == EL_SATELLITE)
- DrawLevelElementAnimationIfNeeded(x, y, element);
- else if (element == EL_SP_ELECTRON)
- DrawLevelElementAnimationIfNeeded(x, y, element);
-#endif
+ if (!MovDelay[ax][ay]) /* start making new amoeba field */
+ MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
- if (DONT_TOUCH(element))
- TestIfBadThingTouchesHero(x, y);
+ if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
+ {
+ MovDelay[ax][ay]--;
+ if (MovDelay[ax][ay])
+ return;
+ }
-#if 0
- PlayLevelSoundAction(x, y, ACTION_WAITING);
-#endif
+ if (can_drop) /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
+ {
+ int start = RND(4);
+ int x = ax + xy[start][0];
+ int y = ay + xy[start][1];
+ if (!IN_LEV_FIELD(x, y))
return;
- }
- InitMovingField(x, y, MovDir[x][y]);
+ if (IS_FREE(x, y) ||
+ CAN_GROW_INTO(Feld[x][y]) ||
+ Feld[x][y] == EL_QUICKSAND_EMPTY ||
+ Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
+ {
+ newax = x;
+ neway = y;
+ }
- PlayLevelSoundAction(x, y, ACTION_MOVING);
+ if (newax == ax && neway == ay)
+ return;
}
+ else /* normal or "filled" (BD style) amoeba */
+ {
+ int start = RND(4);
+ boolean waiting_for_player = FALSE;
- if (MovDir[x][y])
- ContinueMoving(x, y);
-}
+ for (i = 0; i < NUM_DIRECTIONS; i++)
+ {
+ int j = (start + i) % 4;
+ int x = ax + xy[j][0];
+ int y = ay + xy[j][1];
-void ContinueMoving(int x, int y)
-{
- int element = Feld[x][y];
- int stored = Store[x][y];
- struct ElementInfo *ei = &element_info[element];
- 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 newx = x + dx, newy = y + dy;
-#if 0
- int nextx = newx + dx, nexty = newy + dy;
-#endif
-#if 1
- boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
- boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
-#else
- boolean pushed_by_player = Pushed[x][y];
-#endif
+ if (!IN_LEV_FIELD(x, y))
+ continue;
+
+ if (IS_FREE(x, y) ||
+ CAN_GROW_INTO(Feld[x][y]) ||
+ Feld[x][y] == EL_QUICKSAND_EMPTY ||
+ Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
+ {
+ newax = x;
+ neway = y;
+ break;
+ }
+ else if (IS_PLAYER(x, y))
+ waiting_for_player = TRUE;
+ }
+
+ if (newax == ax && neway == ay) /* amoeba cannot grow */
+ {
+ if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
+ {
+ Feld[ax][ay] = EL_AMOEBA_DEAD;
+ TEST_DrawLevelField(ax, ay);
+ AmoebaCnt[AmoebaNr[ax][ay]]--;
- MovPos[x][y] += getElementMoveStepsize(x, y);
+ if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
+ {
+ if (element == EL_AMOEBA_FULL)
+ AmoebeUmwandeln(ax, ay);
+ else if (element == EL_BD_AMOEBA)
+ AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
+ }
+ }
+ return;
+ }
+ else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
+ {
+ /* amoeba gets larger by growing in some direction */
-#if 0
- if (pushed_by_player && IS_PLAYER(x, y))
- {
- /* special case: moving object pushed by player */
- MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
- }
-#else
- if (pushed_by_player) /* special case: moving object pushed by player */
- MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
-#endif
+ int new_group_nr = AmoebaNr[ax][ay];
- if (ABS(MovPos[x][y]) < TILEX)
+#ifdef DEBUG
+ if (new_group_nr == 0)
{
- DrawLevelField(x, y);
-
- return; /* element is still moving */
+ printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
+ printf("AmoebeAbleger(): This should never happen!\n");
+ return;
}
+#endif
- /* element reached destination field */
+ AmoebaNr[newax][neway] = new_group_nr;
+ AmoebaCnt[new_group_nr]++;
+ AmoebaCnt2[new_group_nr]++;
- Feld[x][y] = EL_EMPTY;
- Feld[newx][newy] = element;
- MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
+ /* if amoeba touches other amoeba(s) after growing, unify them */
+ AmoebenVereinigen(newax, neway);
-#if 1
- if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
- {
- element = Feld[newx][newy] = EL_ACID;
+ if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
+ {
+ AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
+ return;
+ }
+ }
}
-#endif
- else if (element == EL_MOLE)
- {
- Feld[x][y] = EL_SAND;
- DrawLevelFieldCrumbledSandNeighbours(x, y);
- }
- else if (element == EL_QUICKSAND_FILLING)
- {
- element = Feld[newx][newy] = get_next_element(element);
- Store[newx][newy] = Store[x][y];
- }
- else if (element == EL_QUICKSAND_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);
- if (!game.magic_wall_active)
- element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
- Store[newx][newy] = Store[x][y];
- }
- else if (element == EL_MAGIC_WALL_EMPTYING)
- {
- Feld[x][y] = get_next_element(element);
- if (!game.magic_wall_active)
- Feld[x][y] = EL_MAGIC_WALL_DEAD;
- element = Feld[newx][newy] = Store[x][y];
- }
- else if (element == EL_BD_MAGIC_WALL_FILLING)
+ if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
+ (neway == lev_fieldy - 1 && newax != ax))
{
- element = Feld[newx][newy] = get_next_element(element);
- if (!game.magic_wall_active)
- element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
- Store[newx][newy] = Store[x][y];
+ Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
+ Store[newax][neway] = element;
}
- else if (element == EL_BD_MAGIC_WALL_EMPTYING)
+ else if (neway == ay || element == EL_EMC_DRIPPER)
{
- Feld[x][y] = get_next_element(element);
- if (!game.magic_wall_active)
- Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
- element = Feld[newx][newy] = Store[x][y];
+ Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
+
+ PlayLevelSoundAction(newax, neway, ACTION_GROWING);
}
- else if (element == EL_AMOEBA_DROPPING)
+ else
{
- Feld[x][y] = get_next_element(element);
- element = Feld[newx][newy] = Store[x][y];
+ InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
+ Feld[ax][ay] = EL_AMOEBA_DROPPING;
+ Store[ax][ay] = EL_AMOEBA_DROP;
+ ContinueMoving(ax, ay);
+ return;
}
- else if (element == EL_SOKOBAN_OBJECT)
- {
- if (Back[x][y])
- Feld[x][y] = Back[x][y];
- if (Back[newx][newy])
- Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
+ TEST_DrawLevelField(newax, neway);
+}
- Back[x][y] = Back[newx][newy] = 0;
- }
-#if 0
- else if (Store[x][y] == EL_ACID)
- {
- element = Feld[newx][newy] = EL_ACID;
- }
-#endif
-#if 0
- else if (IS_CUSTOM_ELEMENT(element) && !IS_PLAYER(x, y) &&
- ei->move_leave_element != EL_EMPTY &&
- (ei->move_leave_type == LEAVE_TYPE_UNLIMITED ||
- Store[x][y] != EL_EMPTY))
- {
- /* some elements can leave other elements behind after moving */
+void Life(int ax, int ay)
+{
+ int x1, y1, x2, y2;
+ int life_time = 40;
+ int element = Feld[ax][ay];
+ int graphic = el2img(element);
+ int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
+ level.biomaze);
+ boolean changed = FALSE;
- Feld[x][y] = ei->move_leave_element;
- InitField(x, y, FALSE);
+ if (IS_ANIMATED(graphic))
+ DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
- if (GFX_CRUMBLED(Feld[x][y]))
- DrawLevelFieldCrumbledSandNeighbours(x, y);
- }
-#endif
+ if (Stop[ax][ay])
+ return;
- Store[x][y] = EL_EMPTY;
- MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
- MovDelay[newx][newy] = 0;
+ if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
+ MovDelay[ax][ay] = life_time;
- if (CAN_CHANGE(element))
+ if (MovDelay[ax][ay]) /* wait some time before next cycle */
{
- /* copy element change control values to new field */
- ChangeDelay[newx][newy] = ChangeDelay[x][y];
- ChangePage[newx][newy] = ChangePage[x][y];
- Changed[newx][newy] = Changed[x][y];
- ChangeEvent[newx][newy] = ChangeEvent[x][y];
+ MovDelay[ax][ay]--;
+ if (MovDelay[ax][ay])
+ return;
}
- ChangeDelay[x][y] = 0;
- ChangePage[x][y] = -1;
- Changed[x][y] = CE_BITMASK_DEFAULT;
- ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
-
- /* copy animation control values to new field */
- GfxFrame[newx][newy] = GfxFrame[x][y];
- GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
- GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
- GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
-
- Pushed[x][y] = Pushed[newx][newy] = FALSE;
-
- ResetGfxAnimation(x, y); /* reset animation values for old field */
-
-#if 1
- /* some elements can leave other elements behind after moving */
-#if 1
- if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
- (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
- (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
-#else
- if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
- (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
- !IS_PLAYER(x, y))
-#endif
+ for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
{
- int move_leave_element = ei->move_leave_element;
+ int xx = ax+x1, yy = ay+y1;
+ int nachbarn = 0;
- Feld[x][y] = move_leave_element;
- InitField(x, y, FALSE);
+ if (!IN_LEV_FIELD(xx, yy))
+ continue;
- if (GFX_CRUMBLED(Feld[x][y]))
- DrawLevelFieldCrumbledSandNeighbours(x, y);
+ for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
+ {
+ int x = xx+x2, y = yy+y2;
- if (ELEM_IS_PLAYER(move_leave_element))
- RelocatePlayer(x, y, move_leave_element);
- }
-#endif
+ if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
+ continue;
-#if 0
- /* some elements can leave other elements behind after moving */
- if (IS_CUSTOM_ELEMENT(element) && !IS_PLAYER(x, y) &&
- ei->move_leave_element != EL_EMPTY &&
- (ei->move_leave_type == LEAVE_TYPE_UNLIMITED ||
- ei->can_leave_element_last))
- {
- Feld[x][y] = ei->move_leave_element;
- InitField(x, y, FALSE);
+ if (((Feld[x][y] == element ||
+ (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
+ !Stop[x][y]) ||
+ (IS_FREE(x, y) && Stop[x][y]))
+ nachbarn++;
+ }
- if (GFX_CRUMBLED(Feld[x][y]))
- DrawLevelFieldCrumbledSandNeighbours(x, y);
+ if (xx == ax && yy == ay) /* field in the middle */
+ {
+ if (nachbarn < life_parameter[0] ||
+ nachbarn > life_parameter[1])
+ {
+ Feld[xx][yy] = EL_EMPTY;
+ if (!Stop[xx][yy])
+ TEST_DrawLevelField(xx, yy);
+ Stop[xx][yy] = TRUE;
+ changed = TRUE;
+ }
+ }
+ else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
+ { /* free border field */
+ if (nachbarn >= life_parameter[2] &&
+ nachbarn <= life_parameter[3])
+ {
+ Feld[xx][yy] = element;
+ MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
+ if (!Stop[xx][yy])
+ TEST_DrawLevelField(xx, yy);
+ Stop[xx][yy] = TRUE;
+ changed = TRUE;
+ }
+ }
}
- ei->can_leave_element_last = ei->can_leave_element;
- ei->can_leave_element = FALSE;
-#endif
-
-#if 0
- /* 2.1.1 (does not work correctly for spring) */
- if (!CAN_MOVE(element))
- MovDir[newx][newy] = 0;
-#else
-
-#if 0
- /* (does not work for falling objects that slide horizontally) */
- if (CAN_FALL(element) && MovDir[newx][newy] == MV_DOWN)
- MovDir[newx][newy] = 0;
-#else
- /*
- if (!CAN_MOVE(element) ||
- (element == EL_SPRING && MovDir[newx][newy] == MV_DOWN))
- MovDir[newx][newy] = 0;
- */
+ if (changed)
+ PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
+ SND_GAME_OF_LIFE_GROWING);
+}
-#if 0
- if (!CAN_MOVE(element) ||
- (CAN_FALL(element) && direction == MV_DOWN))
- GfxDir[x][y] = MovDir[newx][newy] = 0;
-#else
- if (!CAN_MOVE(element) ||
- (CAN_FALL(element) && direction == MV_DOWN &&
- (element == EL_SPRING ||
- element_info[element].move_pattern == MV_WHEN_PUSHED ||
- element_info[element].move_pattern == MV_WHEN_DROPPED)))
- GfxDir[x][y] = MovDir[newx][newy] = 0;
-#endif
+static void InitRobotWheel(int x, int y)
+{
+ ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
+}
-#endif
-#endif
+static void RunRobotWheel(int x, int y)
+{
+ PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
+}
- DrawLevelField(x, y);
- DrawLevelField(newx, newy);
+static void StopRobotWheel(int x, int y)
+{
+ if (ZX == x && ZY == y)
+ {
+ ZX = ZY = -1;
- Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
+ game.robot_wheel_active = FALSE;
+ }
+}
- /* prevent pushed element from moving on in pushed direction */
- if (pushed_by_player && CAN_MOVE(element) &&
- element_info[element].move_pattern & MV_ANY_DIRECTION &&
- !(element_info[element].move_pattern & direction))
- TurnRound(newx, newy);
+static void InitTimegateWheel(int x, int y)
+{
+ ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
+}
+
+static void RunTimegateWheel(int x, int y)
+{
+ PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
+}
+static void InitMagicBallDelay(int x, int y)
+{
#if 1
- /* prevent elements on conveyor belt from moving on in last direction */
- if (pushed_by_conveyor && CAN_FALL(element) &&
- direction & MV_HORIZONTAL)
- {
-#if 0
- if (CAN_MOVE(element))
- InitMovDir(newx, newy);
- else
- MovDir[newx][newy] = 0;
+ ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
#else
- MovDir[newx][newy] = 0;
-#endif
- }
+ ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
#endif
+}
- if (!pushed_by_player)
+static void ActivateMagicBall(int bx, int by)
+{
+ int x, y;
+
+ if (level.ball_random)
{
- int nextx = newx + dx, nexty = newy + dy;
- boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
+ int pos_border = RND(8); /* select one of the eight border elements */
+ int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
+ int xx = pos_content % 3;
+ int yy = pos_content / 3;
- WasJustMoving[newx][newy] = 3;
+ x = bx - 1 + xx;
+ y = by - 1 + yy;
- if (CAN_FALL(element) && direction == MV_DOWN)
- WasJustFalling[newx][newy] = 3;
+ if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
+ CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
+ }
+ else
+ {
+ for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
+ {
+ int xx = x - bx + 1;
+ int yy = y - by + 1;
- if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
- CheckCollision[newx][newy] = 2;
+ if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
+ CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
+ }
}
- if (DONT_TOUCH(element)) /* object may be nasty to player or others */
+ game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
+}
+
+void CheckExit(int x, int y)
+{
+ if (local_player->gems_still_needed > 0 ||
+ local_player->sokobanfields_still_needed > 0 ||
+ local_player->lights_still_needed > 0)
{
- TestIfBadThingTouchesHero(newx, newy);
- TestIfBadThingTouchesFriend(newx, newy);
+ int element = Feld[x][y];
+ int graphic = el2img(element);
- if (!IS_CUSTOM_ELEMENT(element))
- TestIfBadThingTouchesOtherBadThing(newx, newy);
- }
- else if (element == EL_PENGUIN)
- TestIfFriendTouchesBadThing(newx, newy);
+ if (IS_ANIMATED(graphic))
+ DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
-#if USE_NEW_MOVE_STYLE
-#if 0
- if (CAN_FALL(element) && direction == MV_DOWN &&
- (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)) &&
- IS_PLAYER(x, newy + 1))
- printf("::: we would now kill the player [%d]\n", FrameCounter);
-#endif
+ return;
+ }
- /* give the player one last chance (one more frame) to move away */
- if (CAN_FALL(element) && direction == MV_DOWN &&
- (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)) &&
- (!IS_PLAYER(x, newy + 1) ||
- game.engine_version < VERSION_IDENT(3,1,1,0)))
- Impact(x, newy);
-#else
- if (CAN_FALL(element) && direction == MV_DOWN &&
- (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)))
- Impact(x, newy);
-#endif
+ if (AllPlayersGone) /* do not re-open exit door closed after last player */
+ return;
-#if 1
+ Feld[x][y] = EL_EXIT_OPENING;
-#if USE_PUSH_BUGFIX
-#if 1
- if (pushed_by_player && !game.use_bug_change_when_pushing)
-#else
- if (pushed_by_player && game.engine_version >= VERSION_IDENT(3,1,0,0))
-#endif
-#else
- if (pushed_by_player)
-#endif
+ 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)
{
-#if 1
- int dig_side = MV_DIR_OPPOSITE(direction);
-#else
- static int trigger_sides[4] =
- {
- CH_SIDE_RIGHT, /* moving left */
- CH_SIDE_LEFT, /* moving right */
- CH_SIDE_BOTTOM, /* moving up */
- CH_SIDE_TOP, /* moving down */
- };
- int dig_side = trigger_sides[MV_DIR_BIT(direction)];
-#endif
- struct PlayerInfo *player = PLAYERINFO(x, y);
-
- CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
- player->index_bit, dig_side);
- CheckTriggeredElementChangeByPlayer(newx,newy,element,CE_OTHER_GETS_PUSHED,
- player->index_bit, dig_side);
- }
-#endif
+ int element = Feld[x][y];
+ int graphic = el2img(element);
-#if 1
- TestIfElementTouchesCustomElement(x, y); /* empty or new element */
-#endif
+ if (IS_ANIMATED(graphic))
+ DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
-#if 0
- if (ChangePage[newx][newy] != -1) /* delayed change */
- ChangeElement(newx, newy, ChangePage[newx][newy]);
-#endif
+ return;
+ }
-#if 1
+ if (AllPlayersGone) /* do not re-open exit door closed after last player */
+ return;
- TestIfElementHitsCustomElement(newx, newy, direction);
+ Feld[x][y] = EL_EM_EXIT_OPENING;
-#else
+ PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
+}
- if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
+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 hitting_element = Feld[newx][newy];
+ int element = Feld[x][y];
+ int graphic = el2img(element);
- /* !!! fix side (direction) orientation here and elsewhere !!! */
- CheckElementChangeBySide(newx, newy, hitting_element, CE_HITTING_SOMETHING,
- direction);
+ if (IS_ANIMATED(graphic))
+ DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
-#if 0
- if (IN_LEV_FIELD(nextx, nexty))
- {
- int opposite_direction = MV_DIR_OPPOSITE(direction);
- int hitting_side = direction;
- int touched_side = opposite_direction;
- int touched_element = MovingOrBlocked2Element(nextx, nexty);
- boolean object_hit = (!IS_MOVING(nextx, nexty) ||
- MovDir[nextx][nexty] != direction ||
- ABS(MovPos[nextx][nexty]) <= TILEY / 2);
+ return;
+ }
- if (object_hit)
- {
- int i;
+ if (AllPlayersGone) /* do not re-open exit door closed after last player */
+ return;
- CheckElementChangeBySide(nextx, nexty, touched_element,
- CE_HIT_BY_SOMETHING, opposite_direction);
+ Feld[x][y] = EL_STEEL_EXIT_OPENING;
- if (IS_CUSTOM_ELEMENT(hitting_element) &&
- HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING))
- {
- for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
- {
- struct ElementChangeInfo *change =
- &element_info[hitting_element].change_page[i];
+ PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
+}
- if (change->can_change &&
- change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) &&
- change->trigger_side & touched_side &&
- change->trigger_element == touched_element)
- {
- CheckElementChangeByPage(newx, newy, hitting_element,
- touched_element, CE_OTHER_IS_HITTING,i);
- break;
- }
- }
- }
+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_CUSTOM_ELEMENT(touched_element) &&
- HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_HIT))
- {
- for (i = 0; i < element_info[touched_element].num_change_pages; i++)
- {
- struct ElementChangeInfo *change =
- &element_info[touched_element].change_page[i];
+ if (IS_ANIMATED(graphic))
+ DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
- if (change->can_change &&
- change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) &&
- change->trigger_side & hitting_side &&
- change->trigger_element == hitting_element)
- {
- CheckElementChangeByPage(nextx, nexty, touched_element,
- hitting_element, CE_OTHER_GETS_HIT, i);
- break;
- }
- }
- }
- }
- }
-#endif
+ return;
}
-#endif
- TestIfPlayerTouchesCustomElement(newx, newy);
- TestIfElementTouchesCustomElement(newx, newy);
+ 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);
}
-int AmoebeNachbarNr(int ax, int ay)
+void CheckExitSP(int x, int y)
{
- int i;
- int element = Feld[ax][ay];
- int group_nr = 0;
- static int xy[4][2] =
- {
- { 0, -1 },
- { -1, 0 },
- { +1, 0 },
- { 0, +1 }
- };
-
- for (i = 0; i < NUM_DIRECTIONS; i++)
+ if (local_player->gems_still_needed > 0)
{
- int x = ax + xy[i][0];
- int y = ay + xy[i][1];
+ int element = Feld[x][y];
+ int graphic = el2img(element);
- if (!IN_LEV_FIELD(x, y))
- continue;
+ if (IS_ANIMATED(graphic))
+ DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
- if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
- group_nr = AmoebaNr[x][y];
+ return;
}
- return group_nr;
+ if (AllPlayersGone) /* do not re-open exit door closed after last player */
+ return;
+
+ Feld[x][y] = EL_SP_EXIT_OPENING;
+
+ PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
}
-void AmoebenVereinigen(int ax, int ay)
+static void CloseAllOpenTimegates()
{
- int i, x, y, xx, yy;
- int new_group_nr = AmoebaNr[ax][ay];
- static int xy[4][2] =
+ int x, y;
+
+ SCAN_PLAYFIELD(x, y)
{
- { 0, -1 },
- { -1, 0 },
- { +1, 0 },
- { 0, +1 }
- };
+ int element = Feld[x][y];
- if (new_group_nr == 0)
- return;
+ if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
+ {
+ Feld[x][y] = EL_TIMEGATE_CLOSING;
- for (i = 0; i < NUM_DIRECTIONS; i++)
- {
- x = ax + xy[i][0];
- y = ay + xy[i][1];
+ PlayLevelSoundAction(x, y, ACTION_CLOSING);
+ }
+ }
+}
- if (!IN_LEV_FIELD(x, y))
- continue;
+void DrawTwinkleOnField(int x, int y)
+{
+ if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
+ return;
- if ((Feld[x][y] == EL_AMOEBA_FULL ||
- Feld[x][y] == EL_BD_AMOEBA ||
- Feld[x][y] == EL_AMOEBA_DEAD) &&
- AmoebaNr[x][y] != new_group_nr)
- {
- int old_group_nr = AmoebaNr[x][y];
+ if (Feld[x][y] == EL_BD_DIAMOND)
+ return;
- if (old_group_nr == 0)
- return;
+ if (MovDelay[x][y] == 0) /* next animation frame */
+ MovDelay[x][y] = 11 * !GetSimpleRandom(500);
- AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
- AmoebaCnt[old_group_nr] = 0;
- AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
- AmoebaCnt2[old_group_nr] = 0;
+ if (MovDelay[x][y] != 0) /* wait some time before next frame */
+ {
+ MovDelay[x][y]--;
+
+ DrawLevelElementAnimation(x, y, Feld[x][y]);
- for (yy = 0; yy < lev_fieldy; yy++)
- {
- for (xx = 0; xx < lev_fieldx; xx++)
- {
- if (AmoebaNr[xx][yy] == old_group_nr)
- AmoebaNr[xx][yy] = new_group_nr;
- }
- }
+ if (MovDelay[x][y] != 0)
+ {
+ int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
+ 10 - MovDelay[x][y]);
+
+ DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
}
}
}
-void AmoebeUmwandeln(int ax, int ay)
+void MauerWaechst(int x, int y)
{
- int i, x, y;
+ int delay = 6;
- if (Feld[ax][ay] == EL_AMOEBA_DEAD)
+ if (!MovDelay[x][y]) /* next animation frame */
+ MovDelay[x][y] = 3 * delay;
+
+ if (MovDelay[x][y]) /* wait some time before next frame */
{
- int group_nr = AmoebaNr[ax][ay];
+ MovDelay[x][y]--;
-#ifdef DEBUG
- if (group_nr == 0)
+ if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
{
- printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
- printf("AmoebeUmwandeln(): This should never happen!\n");
- return;
+ int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
+ int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
+
+ DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
}
-#endif
- for (y = 0; y < lev_fieldy; y++)
+ if (!MovDelay[x][y])
{
- for (x = 0; x < lev_fieldx; x++)
+ if (MovDir[x][y] == MV_LEFT)
{
- if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
- {
- AmoebaNr[x][y] = 0;
- Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
- }
+ if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
+ TEST_DrawLevelField(x - 1, y);
}
- }
- PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
- SND_AMOEBA_TURNING_TO_GEM :
- SND_AMOEBA_TURNING_TO_ROCK));
- Bang(ax, ay);
- }
- else
- {
- static int xy[4][2] =
- {
- { 0, -1 },
- { -1, 0 },
- { +1, 0 },
- { 0, +1 }
- };
-
- for (i = 0; i < NUM_DIRECTIONS; i++)
- {
- x = ax + xy[i][0];
- y = ay + xy[i][1];
-
- if (!IN_LEV_FIELD(x, y))
- continue;
-
- if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
+ else if (MovDir[x][y] == MV_RIGHT)
{
- PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
- SND_AMOEBA_TURNING_TO_GEM :
- SND_AMOEBA_TURNING_TO_ROCK));
- Bang(x, y);
+ if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
+ TEST_DrawLevelField(x + 1, y);
+ }
+ else if (MovDir[x][y] == MV_UP)
+ {
+ if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
+ TEST_DrawLevelField(x, y - 1);
+ }
+ else
+ {
+ if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
+ TEST_DrawLevelField(x, y + 1);
}
+
+ Feld[x][y] = Store[x][y];
+ Store[x][y] = 0;
+ GfxDir[x][y] = MovDir[x][y] = MV_NONE;
+ TEST_DrawLevelField(x, y);
}
}
}
-void AmoebeUmwandelnBD(int ax, int ay, int new_element)
+void MauerAbleger(int ax, int ay)
{
- int x, y;
- int group_nr = AmoebaNr[ax][ay];
- boolean done = FALSE;
+ 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;
-#ifdef DEBUG
- if (group_nr == 0)
- {
- printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
- printf("AmoebeUmwandelnBD(): This should never happen!\n");
- return;
- }
-#endif
+ if (IS_ANIMATED(graphic))
+ DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
+
+ if (!MovDelay[ax][ay]) /* start building new wall */
+ MovDelay[ax][ay] = 6;
- for (y = 0; y < lev_fieldy; y++)
+ if (MovDelay[ax][ay]) /* wait some time before building new wall */
{
- for (x = 0; x < lev_fieldx; x++)
- {
- if (AmoebaNr[x][y] == group_nr &&
- (Feld[x][y] == EL_AMOEBA_DEAD ||
- Feld[x][y] == EL_BD_AMOEBA ||
- Feld[x][y] == EL_AMOEBA_GROWING))
- {
- AmoebaNr[x][y] = 0;
- Feld[x][y] = new_element;
- InitField(x, y, FALSE);
- DrawLevelField(x, y);
- done = TRUE;
- }
- }
+ MovDelay[ax][ay]--;
+ if (MovDelay[ax][ay])
+ return;
}
- if (done)
- PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
- SND_BD_AMOEBA_TURNING_TO_ROCK :
- SND_BD_AMOEBA_TURNING_TO_GEM));
-}
-
-void AmoebeWaechst(int x, int y)
-{
- static unsigned long sound_delay = 0;
- static unsigned long sound_delay_value = 0;
+ 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 (!MovDelay[x][y]) /* start new growing cycle */
+ if (element == EL_EXPANDABLE_WALL_VERTICAL ||
+ element == EL_EXPANDABLE_WALL_ANY)
{
- MovDelay[x][y] = 7;
-
- if (DelayReached(&sound_delay, sound_delay_value))
+ if (oben_frei)
{
-#if 1
- PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
-#else
- if (Store[x][y] == EL_BD_AMOEBA)
- PlayLevelSound(x, y, SND_BD_AMOEBA_GROWING);
- else
- PlayLevelSound(x, y, SND_AMOEBA_GROWING);
-#endif
- sound_delay_value = 30;
+ Feld[ax][ay-1] = EL_EXPANDABLE_WALL_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_WALL_GROWING_UP, 0);
+ new_wall = TRUE;
+ }
+ if (unten_frei)
+ {
+ Feld[ax][ay+1] = EL_EXPANDABLE_WALL_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_WALL_GROWING_DOWN, 0);
+ new_wall = TRUE;
}
}
- if (MovDelay[x][y]) /* wait some time before growing bigger */
+ if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
+ element == EL_EXPANDABLE_WALL_ANY ||
+ element == EL_EXPANDABLE_WALL ||
+ element == EL_BD_EXPANDABLE_WALL)
{
- MovDelay[x][y]--;
- if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
+ if (links_frei)
{
- int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
- 6 - MovDelay[x][y]);
-
- DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
+ Feld[ax-1][ay] = EL_EXPANDABLE_WALL_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_WALL_GROWING_LEFT, 0);
+ new_wall = TRUE;
}
- if (!MovDelay[x][y])
+ if (rechts_frei)
{
- Feld[x][y] = Store[x][y];
- Store[x][y] = 0;
- DrawLevelField(x, y);
+ Feld[ax+1][ay] = EL_EXPANDABLE_WALL_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_WALL_GROWING_RIGHT, 0);
+ new_wall = TRUE;
}
}
+
+ if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
+ TEST_DrawLevelField(ax, ay);
+
+ 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_WALL_HORIZONTAL ||
+ element == EL_EXPANDABLE_WALL) &&
+ ((links_massiv && rechts_massiv) ||
+ element == EL_EXPANDABLE_WALL_VERTICAL))
+ Feld[ax][ay] = EL_WALL;
+
+ if (new_wall)
+ PlayLevelSoundAction(ax, ay, ACTION_GROWING);
}
-void AmoebaDisappearing(int x, int y)
+void MauerAblegerStahl(int ax, int ay)
{
- static unsigned long sound_delay = 0;
- static unsigned long sound_delay_value = 0;
+ 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 (!MovDelay[x][y]) /* start new shrinking cycle */
+ 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[x][y] = 7;
+ MovDelay[ax][ay]--;
+ if (MovDelay[ax][ay])
+ return;
+ }
- if (DelayReached(&sound_delay, sound_delay_value))
- sound_delay_value = 30;
+ 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 (MovDelay[x][y]) /* wait some time before shrinking */
+ if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
+ element == EL_EXPANDABLE_STEELWALL_ANY)
{
- MovDelay[x][y]--;
- if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
+ if (links_frei)
{
- int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
- 6 - MovDelay[x][y]);
-
- DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
+ 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 (!MovDelay[x][y])
+ if (rechts_frei)
{
- Feld[x][y] = EL_EMPTY;
- DrawLevelField(x, y);
-
- /* don't let mole enter this field in this cycle;
- (give priority to objects falling to this field from above) */
- Stop[x][y] = TRUE;
+ 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_STEELWALL;
+
+ if (new_wall)
+ PlayLevelSoundAction(ax, ay, ACTION_GROWING);
}
-void AmoebeAbleger(int ax, int ay)
+void CheckForDragon(int x, int y)
{
- int i;
- int element = Feld[ax][ay];
- int graphic = el2img(element);
- int newax = ax, neway = ay;
+ int i, j;
+ boolean dragon_found = FALSE;
static int xy[4][2] =
{
{ 0, -1 },
{ 0, +1 }
};
- if (!level.amoeba_speed)
- {
- Feld[ax][ay] = EL_AMOEBA_DEAD;
- DrawLevelField(ax, ay);
- return;
- }
-
- if (IS_ANIMATED(graphic))
- DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
-
- if (!MovDelay[ax][ay]) /* start making new amoeba field */
- MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
-
- if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
- {
- MovDelay[ax][ay]--;
- if (MovDelay[ax][ay])
- return;
- }
-
- if (element == EL_AMOEBA_WET) /* object is an acid / amoeba drop */
- {
- int start = RND(4);
- int x = ax + xy[start][0];
- int y = ay + xy[start][1];
-
- if (!IN_LEV_FIELD(x, y))
- return;
-
-#if 1
- if (IS_FREE(x, y) ||
- CAN_GROW_INTO(Feld[x][y]) ||
- Feld[x][y] == EL_QUICKSAND_EMPTY)
- {
- newax = x;
- neway = y;
- }
-#else
- /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
- if (IS_FREE(x, y) ||
- Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
- {
- newax = x;
- neway = y;
- }
-#endif
-
- if (newax == ax && neway == ay)
- return;
- }
- else /* normal or "filled" (BD style) amoeba */
+ for (i = 0; i < NUM_DIRECTIONS; i++)
{
- int start = RND(4);
- boolean waiting_for_player = FALSE;
-
- for (i = 0; i < NUM_DIRECTIONS; i++)
+ for (j = 0; j < 4; j++)
{
- int j = (start + i) % 4;
- int x = ax + xy[j][0];
- int y = ay + xy[j][1];
-
- if (!IN_LEV_FIELD(x, y))
- continue;
+ int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
-#if 1
- if (IS_FREE(x, y) ||
- CAN_GROW_INTO(Feld[x][y]) ||
- Feld[x][y] == EL_QUICKSAND_EMPTY)
+ if (IN_LEV_FIELD(xx, yy) &&
+ (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
{
- newax = x;
- neway = y;
- break;
+ if (Feld[xx][yy] == EL_DRAGON)
+ dragon_found = TRUE;
}
-#else
- /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
- if (IS_FREE(x, y) ||
- Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
- {
- newax = x;
- neway = y;
+ else
break;
- }
-#endif
- else if (IS_PLAYER(x, y))
- waiting_for_player = TRUE;
}
+ }
- if (newax == ax && neway == ay) /* amoeba cannot grow */
+ if (!dragon_found)
+ {
+ for (i = 0; i < NUM_DIRECTIONS; i++)
{
-#if 1
- if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
-#else
- if (i == 4 && (!waiting_for_player || game.emulation == EMU_BOULDERDASH))
-#endif
+ for (j = 0; j < 3; j++)
{
- Feld[ax][ay] = EL_AMOEBA_DEAD;
- DrawLevelField(ax, ay);
- AmoebaCnt[AmoebaNr[ax][ay]]--;
-
- if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
- {
- if (element == EL_AMOEBA_FULL)
- AmoebeUmwandeln(ax, ay);
- else if (element == EL_BD_AMOEBA)
- AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
- }
+ int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
+
+ if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
+ {
+ Feld[xx][yy] = EL_EMPTY;
+ TEST_DrawLevelField(xx, yy);
+ }
+ else
+ break;
}
- return;
}
- else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
- {
- /* amoeba gets larger by growing in some direction */
+ }
+}
- int new_group_nr = AmoebaNr[ax][ay];
+static void InitBuggyBase(int x, int y)
+{
+ int element = Feld[x][y];
+ int activating_delay = FRAMES_PER_SECOND / 4;
-#ifdef DEBUG
- if (new_group_nr == 0)
+ ChangeDelay[x][y] =
+ (element == EL_SP_BUGGY_BASE ?
+ 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
+ element == EL_SP_BUGGY_BASE_ACTIVATING ?
+ activating_delay :
+ element == EL_SP_BUGGY_BASE_ACTIVE ?
+ 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
+}
+
+static void WarnBuggyBase(int x, int y)
+{
+ int i;
+ static int xy[4][2] =
{
- printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
- printf("AmoebeAbleger(): This should never happen!\n");
- return;
- }
-#endif
+ { 0, -1 },
+ { -1, 0 },
+ { +1, 0 },
+ { 0, +1 }
+ };
- AmoebaNr[newax][neway] = new_group_nr;
- AmoebaCnt[new_group_nr]++;
- AmoebaCnt2[new_group_nr]++;
+ for (i = 0; i < NUM_DIRECTIONS; i++)
+ {
+ int xx = x + xy[i][0];
+ int yy = y + xy[i][1];
- /* if amoeba touches other amoeba(s) after growing, unify them */
- AmoebenVereinigen(newax, neway);
+ if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
+ {
+ PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
- if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
- {
- AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
- return;
- }
+ break;
}
}
+}
- if (element != EL_AMOEBA_WET || neway < ay || !IS_FREE(newax, neway) ||
- (neway == lev_fieldy - 1 && newax != ax))
- {
- Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
- Store[newax][neway] = element;
- }
- else if (neway == ay)
- {
- Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
-#if 1
- PlayLevelSoundAction(newax, neway, ACTION_GROWING);
-#else
- PlayLevelSound(newax, neway, SND_AMOEBA_GROWING);
-#endif
- }
- else
- {
- InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
- Feld[ax][ay] = EL_AMOEBA_DROPPING;
- Store[ax][ay] = EL_AMOEBA_DROP;
- ContinueMoving(ax, ay);
- return;
- }
+static void InitTrap(int x, int y)
+{
+ ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
+}
+
+static void ActivateTrap(int x, int y)
+{
+ PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
+}
+
+static void ChangeActiveTrap(int x, int y)
+{
+ int graphic = IMG_TRAP_ACTIVE;
- DrawLevelField(newax, neway);
+ /* if new animation frame was drawn, correct crumbled sand border */
+ if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
+ TEST_DrawLevelFieldCrumbled(x, y);
+}
+
+static int getSpecialActionElement(int element, int number, int base_element)
+{
+ return (element != EL_EMPTY ? element :
+ number != -1 ? base_element + number - 1 :
+ EL_EMPTY);
+}
+
+static int getModifiedActionNumber(int value_old, int operator, int operand,
+ int value_min, int value_max)
+{
+ int value_new = (operator == CA_MODE_SET ? operand :
+ operator == CA_MODE_ADD ? value_old + operand :
+ operator == CA_MODE_SUBTRACT ? value_old - operand :
+ operator == CA_MODE_MULTIPLY ? value_old * operand :
+ operator == CA_MODE_DIVIDE ? value_old / MAX(1, operand) :
+ operator == CA_MODE_MODULO ? value_old % MAX(1, operand) :
+ value_old);
+
+ return (value_new < value_min ? value_min :
+ value_new > value_max ? value_max :
+ value_new);
}
-void Life(int ax, int ay)
+static void ExecuteCustomElementAction(int x, int y, int element, int page)
{
- int x1, y1, x2, y2;
- static int life[4] = { 2, 3, 3, 3 }; /* parameters for "game of life" */
- int life_time = 40;
- int element = Feld[ax][ay];
- int graphic = el2img(element);
- boolean changed = FALSE;
-
- if (IS_ANIMATED(graphic))
- DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
+ struct ElementInfo *ei = &element_info[element];
+ struct ElementChangeInfo *change = &ei->change_page[page];
+ int target_element = change->target_element;
+ int action_type = change->action_type;
+ int action_mode = change->action_mode;
+ int action_arg = change->action_arg;
+ int action_element = change->action_element;
+ int i;
- if (Stop[ax][ay])
+ if (!change->has_action)
return;
- if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
- MovDelay[ax][ay] = life_time;
+ /* ---------- determine action paramater values -------------------------- */
- if (MovDelay[ax][ay]) /* wait some time before next cycle */
+ int level_time_value =
+ (level.time > 0 ? TimeLeft :
+ TimePlayed);
+
+ int action_arg_element_raw =
+ (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player :
+ action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
+ action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element :
+ action_arg == CA_ARG_ELEMENT_ACTION ? change->action_element :
+ action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
+ action_arg == CA_ARG_INVENTORY_RM_TARGET ? change->target_element :
+ action_arg == CA_ARG_INVENTORY_RM_ACTION ? change->action_element :
+ EL_EMPTY);
+ int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
+
+#if 0
+ if (action_arg_element_raw == EL_GROUP_START)
+ printf("::: %d,%d: %d ('%s')\n", x, y, element, EL_NAME(element));
+#endif
+
+ int action_arg_direction =
+ (action_arg >= CA_ARG_DIRECTION_LEFT &&
+ action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
+ action_arg == CA_ARG_DIRECTION_TRIGGER ?
+ change->actual_trigger_side :
+ action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
+ MV_DIR_OPPOSITE(change->actual_trigger_side) :
+ MV_NONE);
+
+ int action_arg_number_min =
+ (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
+ CA_ARG_MIN);
+
+ int action_arg_number_max =
+ (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
+ action_type == CA_SET_LEVEL_GEMS ? 999 :
+ action_type == CA_SET_LEVEL_TIME ? 9999 :
+ action_type == CA_SET_LEVEL_SCORE ? 99999 :
+ action_type == CA_SET_CE_VALUE ? 9999 :
+ action_type == CA_SET_CE_SCORE ? 9999 :
+ CA_ARG_MAX);
+
+ int action_arg_number_reset =
+ (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
+ action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
+ action_type == CA_SET_LEVEL_TIME ? level.time :
+ action_type == CA_SET_LEVEL_SCORE ? 0 :
+#if USE_NEW_CUSTOM_VALUE
+ action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
+#else
+ action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
+#endif
+ action_type == CA_SET_CE_SCORE ? 0 :
+ 0);
+
+ int action_arg_number =
+ (action_arg <= CA_ARG_MAX ? action_arg :
+ action_arg >= CA_ARG_SPEED_NOT_MOVING &&
+ action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
+ action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
+ action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
+ action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
+ action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
+#if USE_NEW_CUSTOM_VALUE
+ action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
+#else
+ action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
+#endif
+ action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
+ action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
+ action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
+ action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
+ action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
+ action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
+ action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
+ action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
+ action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
+ action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
+ action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
+ action_arg == CA_ARG_ELEMENT_NR_TARGET ? change->target_element :
+ action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
+ action_arg == CA_ARG_ELEMENT_NR_ACTION ? change->action_element :
+ -1);
+
+ int action_arg_number_old =
+ (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
+ action_type == CA_SET_LEVEL_TIME ? TimeLeft :
+ action_type == CA_SET_LEVEL_SCORE ? local_player->score :
+ action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
+ action_type == CA_SET_CE_SCORE ? ei->collect_score :
+ 0);
+
+ int action_arg_number_new =
+ getModifiedActionNumber(action_arg_number_old,
+ action_mode, action_arg_number,
+ action_arg_number_min, action_arg_number_max);
+
+#if 1
+ int trigger_player_bits =
+ (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
+ change->actual_trigger_player_bits : change->trigger_player);
+#else
+ int trigger_player_bits =
+ (change->actual_trigger_player >= EL_PLAYER_1 &&
+ change->actual_trigger_player <= EL_PLAYER_4 ?
+ (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
+ PLAYER_BITS_ANY);
+#endif
+
+ int action_arg_player_bits =
+ (action_arg >= CA_ARG_PLAYER_1 &&
+ action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
+ action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
+ action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
+ PLAYER_BITS_ANY);
+
+ /* ---------- execute action -------------------------------------------- */
+
+ switch (action_type)
{
- MovDelay[ax][ay]--;
- if (MovDelay[ax][ay])
+ case CA_NO_ACTION:
+ {
return;
- }
+ }
- for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
- {
- int xx = ax+x1, yy = ay+y1;
- int nachbarn = 0;
+ /* ---------- level actions ------------------------------------------- */
- if (!IN_LEV_FIELD(xx, yy))
- continue;
+ case CA_RESTART_LEVEL:
+ {
+ game.restart_level = TRUE;
- for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
+ break;
+ }
+
+ case CA_SHOW_ENVELOPE:
{
- int x = xx+x2, y = yy+y2;
+ int element = getSpecialActionElement(action_arg_element,
+ action_arg_number, EL_ENVELOPE_1);
- if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
- continue;
+ if (IS_ENVELOPE(element))
+ local_player->show_envelope = element;
- if (((Feld[x][y] == element ||
- (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
- !Stop[x][y]) ||
- (IS_FREE(x, y) && Stop[x][y]))
- nachbarn++;
+ break;
}
- if (xx == ax && yy == ay) /* field in the middle */
+ case CA_SET_LEVEL_TIME:
{
- if (nachbarn < life[0] || nachbarn > life[1])
+ if (level.time > 0) /* only modify limited time value */
{
- Feld[xx][yy] = EL_EMPTY;
- if (!Stop[xx][yy])
- DrawLevelField(xx, yy);
- Stop[xx][yy] = TRUE;
- changed = TRUE;
- }
- }
+ TimeLeft = action_arg_number_new;
+
#if 1
- else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
- { /* free border field */
- if (nachbarn >= life[2] && nachbarn <= life[3])
- {
- Feld[xx][yy] = element;
- MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
- if (!Stop[xx][yy])
- DrawLevelField(xx, yy);
- Stop[xx][yy] = TRUE;
- changed = TRUE;
- }
- }
+ game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
+
+ DisplayGameControlValues();
#else
- /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
- else if (IS_FREE(xx, yy) || Feld[xx][yy] == EL_SAND)
- { /* free border field */
- if (nachbarn >= life[2] && nachbarn <= life[3])
- {
- Feld[xx][yy] = element;
- MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
- if (!Stop[xx][yy])
- DrawLevelField(xx, yy);
- Stop[xx][yy] = TRUE;
- changed = TRUE;
+ DrawGameValue_Time(TimeLeft);
+#endif
+
+ if (!TimeLeft && setup.time_limit)
+ for (i = 0; i < MAX_PLAYERS; i++)
+ KillPlayer(&stored_player[i]);
}
+
+ break;
}
-#endif
- }
- if (changed)
- PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
- SND_GAME_OF_LIFE_GROWING);
-}
+ case CA_SET_LEVEL_SCORE:
+ {
+ local_player->score = action_arg_number_new;
-static void InitRobotWheel(int x, int y)
-{
- ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
-}
+#if 1
+ game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
-static void RunRobotWheel(int x, int y)
-{
- PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
-}
+ DisplayGameControlValues();
+#else
+ DrawGameValue_Score(local_player->score);
+#endif
-static void StopRobotWheel(int x, int y)
-{
- if (ZX == x && ZY == y)
- ZX = ZY = -1;
-}
+ break;
+ }
+
+ case CA_SET_LEVEL_GEMS:
+ {
+ local_player->gems_still_needed = action_arg_number_new;
-static void InitTimegateWheel(int x, int y)
-{
#if 1
- ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
+ game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
+
+ DisplayGameControlValues();
#else
- /* another brainless, "type style" bug ... :-( */
- ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
+ DrawGameValue_Emeralds(local_player->gems_still_needed);
#endif
-}
-static void RunTimegateWheel(int x, int y)
-{
- PlayLevelSound(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
-}
+ break;
+ }
-void CheckExit(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 !USE_PLAYER_GRAVITY
+ case CA_SET_LEVEL_GRAVITY:
+ {
+ game.gravity = (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
+ action_arg == CA_ARG_GRAVITY_ON ? TRUE :
+ action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
+ game.gravity);
+ break;
+ }
+#endif
- if (IS_ANIMATED(graphic))
- DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
+ case CA_SET_LEVEL_WIND:
+ {
+ game.wind_direction = action_arg_direction;
- return;
- }
+ break;
+ }
- if (AllPlayersGone) /* do not re-open exit door closed after last player */
- return;
+ case CA_SET_LEVEL_RANDOM_SEED:
+ {
+#if 1
+ /* ensure that setting a new random seed while playing is predictable */
+ InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
+#else
+ InitRND(action_arg_number_new);
+#endif
- Feld[x][y] = EL_EXIT_OPENING;
+#if 0
+ printf("::: %d -> %d\n", action_arg_number_new, RND(10));
+#endif
- PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
-}
+#if 0
+ {
+ int i;
-void CheckExitSP(int x, int y)
-{
- if (local_player->gems_still_needed > 0)
- {
- int element = Feld[x][y];
- int graphic = el2img(element);
+ printf("::: ");
+ for (i = 0; i < 9; i++)
+ printf("%d, ", RND(2));
+ printf("\n");
+ }
+#endif
- if (IS_ANIMATED(graphic))
- DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
+ break;
+ }
- return;
- }
+ /* ---------- player actions ------------------------------------------ */
- if (AllPlayersGone) /* do not re-open exit door closed after last player */
- return;
+ case CA_MOVE_PLAYER:
+ {
+ /* automatically move to the next field in specified direction */
+ for (i = 0; i < MAX_PLAYERS; i++)
+ if (trigger_player_bits & (1 << i))
+ stored_player[i].programmed_action = action_arg_direction;
- Feld[x][y] = EL_SP_EXIT_OPENING;
+ break;
+ }
- PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
-}
+ case CA_EXIT_PLAYER:
+ {
+ for (i = 0; i < MAX_PLAYERS; i++)
+ if (action_arg_player_bits & (1 << i))
+ PlayerWins(&stored_player[i]);
-static void CloseAllOpenTimegates()
-{
- int x, y;
+ break;
+ }
- for (y = 0; y < lev_fieldy; y++)
- {
- for (x = 0; x < lev_fieldx; x++)
+ case CA_KILL_PLAYER:
{
- int element = Feld[x][y];
+ for (i = 0; i < MAX_PLAYERS; i++)
+ if (action_arg_player_bits & (1 << i))
+ KillPlayer(&stored_player[i]);
+
+ break;
+ }
+
+ case CA_SET_PLAYER_KEYS:
+ {
+ int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
+ int element = getSpecialActionElement(action_arg_element,
+ action_arg_number, EL_KEY_1);
- if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
+ if (IS_KEY(element))
{
- Feld[x][y] = EL_TIMEGATE_CLOSING;
-#if 1
- PlayLevelSoundAction(x, y, ACTION_CLOSING);
-#else
- PlayLevelSound(x, y, SND_TIMEGATE_CLOSING);
-#endif
+ for (i = 0; i < MAX_PLAYERS; i++)
+ {
+ if (trigger_player_bits & (1 << i))
+ {
+ stored_player[i].key[KEY_NR(element)] = key_state;
+
+ DrawGameDoorValues();
+ }
+ }
}
+
+ break;
}
- }
-}
-void EdelsteinFunkeln(int x, int y)
-{
- if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
- return;
+ case CA_SET_PLAYER_SPEED:
+ {
+#if 0
+ printf("::: trigger_player_bits == %d\n", trigger_player_bits);
+#endif
- if (Feld[x][y] == EL_BD_DIAMOND)
- return;
+ for (i = 0; i < MAX_PLAYERS; i++)
+ {
+ if (trigger_player_bits & (1 << i))
+ {
+ int move_stepsize = TILEX / stored_player[i].move_delay_value;
- if (MovDelay[x][y] == 0) /* next animation frame */
- MovDelay[x][y] = 11 * !SimpleRND(500);
+ if (action_arg == CA_ARG_SPEED_FASTER &&
+ stored_player[i].cannot_move)
+ {
+ action_arg_number = STEPSIZE_VERY_SLOW;
+ }
+ else if (action_arg == CA_ARG_SPEED_SLOWER ||
+ action_arg == CA_ARG_SPEED_FASTER)
+ {
+ action_arg_number = 2;
+ action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
+ CA_MODE_MULTIPLY);
+ }
+ else if (action_arg == CA_ARG_NUMBER_RESET)
+ {
+ action_arg_number = level.initial_player_stepsize[i];
+ }
- if (MovDelay[x][y] != 0) /* wait some time before next frame */
- {
- MovDelay[x][y]--;
+ move_stepsize =
+ getModifiedActionNumber(move_stepsize,
+ action_mode,
+ action_arg_number,
+ action_arg_number_min,
+ action_arg_number_max);
- if (setup.direct_draw && MovDelay[x][y])
- SetDrawtoField(DRAW_BUFFERED);
+ SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
+ }
+ }
- DrawLevelElementAnimation(x, y, Feld[x][y]);
+ break;
+ }
- if (MovDelay[x][y] != 0)
+ case CA_SET_PLAYER_SHIELD:
{
- int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
- 10 - MovDelay[x][y]);
+ for (i = 0; i < MAX_PLAYERS; i++)
+ {
+ if (trigger_player_bits & (1 << i))
+ {
+ if (action_arg == CA_ARG_SHIELD_OFF)
+ {
+ stored_player[i].shield_normal_time_left = 0;
+ stored_player[i].shield_deadly_time_left = 0;
+ }
+ else if (action_arg == CA_ARG_SHIELD_NORMAL)
+ {
+ stored_player[i].shield_normal_time_left = 999999;
+ }
+ else if (action_arg == CA_ARG_SHIELD_DEADLY)
+ {
+ stored_player[i].shield_normal_time_left = 999999;
+ stored_player[i].shield_deadly_time_left = 999999;
+ }
+ }
+ }
+
+ break;
+ }
+
+#if USE_PLAYER_GRAVITY
+ case CA_SET_PLAYER_GRAVITY:
+ {
+ for (i = 0; i < MAX_PLAYERS; i++)
+ {
+ if (trigger_player_bits & (1 << i))
+ {
+ stored_player[i].gravity =
+ (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
+ action_arg == CA_ARG_GRAVITY_ON ? TRUE :
+ action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
+ stored_player[i].gravity);
+ }
+ }
- DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
+ break;
+ }
+#endif
- if (setup.direct_draw)
+ case CA_SET_PLAYER_ARTWORK:
+ {
+ for (i = 0; i < MAX_PLAYERS; i++)
{
- int dest_x, dest_y;
-
- dest_x = FX + SCREENX(x) * TILEX;
- dest_y = FY + SCREENY(y) * TILEY;
+ if (trigger_player_bits & (1 << i))
+ {
+ int artwork_element = action_arg_element;
- BlitBitmap(drawto_field, window,
- dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
- SetDrawtoField(DRAW_DIRECT);
- }
- }
- }
-}
+ if (action_arg == CA_ARG_ELEMENT_RESET)
+ artwork_element =
+ (level.use_artwork_element[i] ? level.artwork_element[i] :
+ stored_player[i].element_nr);
-void MauerWaechst(int x, int y)
-{
- int delay = 6;
+#if USE_GFX_RESET_PLAYER_ARTWORK
+ if (stored_player[i].artwork_element != artwork_element)
+ stored_player[i].Frame = 0;
+#endif
- if (!MovDelay[x][y]) /* next animation frame */
- MovDelay[x][y] = 3 * delay;
+ stored_player[i].artwork_element = artwork_element;
- if (MovDelay[x][y]) /* wait some time before next frame */
- {
- MovDelay[x][y]--;
+ SetPlayerWaiting(&stored_player[i], FALSE);
- if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
- {
- int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
- int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
+ /* set number of special actions for bored and sleeping animation */
+ stored_player[i].num_special_action_bored =
+ get_num_special_action(artwork_element,
+ ACTION_BORING_1, ACTION_BORING_LAST);
+ stored_player[i].num_special_action_sleeping =
+ get_num_special_action(artwork_element,
+ ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
+ }
+ }
- DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
+ break;
}
- if (!MovDelay[x][y])
+ case CA_SET_PLAYER_INVENTORY:
{
- if (MovDir[x][y] == MV_LEFT)
- {
- if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
- DrawLevelField(x - 1, y);
- }
- else if (MovDir[x][y] == MV_RIGHT)
- {
- if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
- DrawLevelField(x + 1, y);
- }
- else if (MovDir[x][y] == MV_UP)
- {
- if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
- DrawLevelField(x, y - 1);
- }
- else
+ for (i = 0; i < MAX_PLAYERS; i++)
{
- if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
- DrawLevelField(x, y + 1);
- }
+ struct PlayerInfo *player = &stored_player[i];
+ int j, k;
- Feld[x][y] = Store[x][y];
- Store[x][y] = 0;
- GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
- DrawLevelField(x, y);
- }
- }
-}
+ if (trigger_player_bits & (1 << i))
+ {
+ int inventory_element = action_arg_element;
-void MauerAbleger(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 (action_arg == CA_ARG_ELEMENT_TARGET ||
+ action_arg == CA_ARG_ELEMENT_TRIGGER ||
+ action_arg == CA_ARG_ELEMENT_ACTION)
+ {
+ int element = inventory_element;
+ int collect_count = element_info[element].collect_count_initial;
- if (IS_ANIMATED(graphic))
- DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
+ if (!IS_CUSTOM_ELEMENT(element))
+ collect_count = 1;
- if (!MovDelay[ax][ay]) /* start building new wall */
- MovDelay[ax][ay] = 6;
+ if (collect_count == 0)
+ player->inventory_infinite_element = element;
+ else
+ for (k = 0; k < collect_count; k++)
+ if (player->inventory_size < MAX_INVENTORY_SIZE)
+ player->inventory_element[player->inventory_size++] =
+ element;
+ }
+ else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
+ action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
+ action_arg == CA_ARG_INVENTORY_RM_ACTION)
+ {
+ if (player->inventory_infinite_element != EL_UNDEFINED &&
+ IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
+ action_arg_element_raw))
+ player->inventory_infinite_element = EL_UNDEFINED;
- if (MovDelay[ax][ay]) /* wait some time before building new wall */
- {
- MovDelay[ax][ay]--;
- if (MovDelay[ax][ay])
- return;
- }
+ for (k = 0, j = 0; j < player->inventory_size; j++)
+ {
+ if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
+ action_arg_element_raw))
+ player->inventory_element[k++] = player->inventory_element[j];
+ }
- 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;
+ player->inventory_size = k;
+ }
+ else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
+ {
+ if (player->inventory_size > 0)
+ {
+ for (j = 0; j < player->inventory_size - 1; j++)
+ player->inventory_element[j] = player->inventory_element[j + 1];
- if (element == EL_EXPANDABLE_WALL_VERTICAL ||
- element == EL_EXPANDABLE_WALL_ANY)
- {
- if (oben_frei)
- {
- Feld[ax][ay-1] = EL_EXPANDABLE_WALL_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_WALL_GROWING_UP, 0);
- new_wall = TRUE;
- }
- if (unten_frei)
- {
- Feld[ax][ay+1] = EL_EXPANDABLE_WALL_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_WALL_GROWING_DOWN, 0);
- new_wall = TRUE;
- }
- }
+ player->inventory_size--;
+ }
+ }
+ else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
+ {
+ if (player->inventory_size > 0)
+ player->inventory_size--;
+ }
+ else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
+ {
+ player->inventory_infinite_element = EL_UNDEFINED;
+ player->inventory_size = 0;
+ }
+ else if (action_arg == CA_ARG_INVENTORY_RESET)
+ {
+ player->inventory_infinite_element = EL_UNDEFINED;
+ player->inventory_size = 0;
- if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
- element == EL_EXPANDABLE_WALL_ANY ||
- element == EL_EXPANDABLE_WALL)
- {
- if (links_frei)
- {
- Feld[ax-1][ay] = EL_EXPANDABLE_WALL_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_WALL_GROWING_LEFT, 0);
- new_wall = TRUE;
- }
+ if (level.use_initial_inventory[i])
+ {
+ for (j = 0; j < level.initial_inventory_size[i]; j++)
+ {
+ int element = level.initial_inventory_content[i][j];
+ int collect_count = element_info[element].collect_count_initial;
+
+ if (!IS_CUSTOM_ELEMENT(element))
+ collect_count = 1;
+
+ if (collect_count == 0)
+ player->inventory_infinite_element = element;
+ else
+ for (k = 0; k < collect_count; k++)
+ if (player->inventory_size < MAX_INVENTORY_SIZE)
+ player->inventory_element[player->inventory_size++] =
+ element;
+ }
+ }
+ }
+ }
+ }
- if (rechts_frei)
- {
- Feld[ax+1][ay] = EL_EXPANDABLE_WALL_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_WALL_GROWING_RIGHT, 0);
- new_wall = TRUE;
+ break;
}
- }
- if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
- DrawLevelField(ax, ay);
+ /* ---------- CE actions ---------------------------------------------- */
- 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;
+ case CA_SET_CE_VALUE:
+ {
+#if USE_NEW_CUSTOM_VALUE
+ int last_ce_value = CustomValue[x][y];
- if (((oben_massiv && unten_massiv) ||
- element == EL_EXPANDABLE_WALL_HORIZONTAL ||
- element == EL_EXPANDABLE_WALL) &&
- ((links_massiv && rechts_massiv) ||
- element == EL_EXPANDABLE_WALL_VERTICAL))
- Feld[ax][ay] = EL_WALL;
+ CustomValue[x][y] = action_arg_number_new;
- if (new_wall)
-#if 1
- PlayLevelSoundAction(ax, ay, ACTION_GROWING);
-#else
- PlayLevelSound(ax, ay, SND_EXPANDABLE_WALL_GROWING);
+ if (CustomValue[x][y] != last_ce_value)
+ {
+ CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
+ CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
+
+ if (CustomValue[x][y] == 0)
+ {
+ CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
+ CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
+ }
+ }
#endif
-}
-void CheckForDragon(int x, int y)
-{
- int i, j;
- boolean dragon_found = FALSE;
- static int xy[4][2] =
- {
- { 0, -1 },
- { -1, 0 },
- { +1, 0 },
- { 0, +1 }
- };
+ break;
+ }
- for (i = 0; i < NUM_DIRECTIONS; i++)
- {
- for (j = 0; j < 4; j++)
+ case CA_SET_CE_SCORE:
{
- int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
+#if USE_NEW_CUSTOM_VALUE
+ int last_ce_score = ei->collect_score;
- if (IN_LEV_FIELD(xx, yy) &&
- (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
+ ei->collect_score = action_arg_number_new;
+
+ if (ei->collect_score != last_ce_score)
{
- if (Feld[xx][yy] == EL_DRAGON)
- dragon_found = TRUE;
+ CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
+ CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
+
+ if (ei->collect_score == 0)
+ {
+ int xx, yy;
+
+ CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
+ CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
+
+ /*
+ This is a very special case that seems to be a mixture between
+ CheckElementChange() and CheckTriggeredElementChange(): while
+ the first one only affects single elements that are triggered
+ directly, the second one affects multiple elements in the playfield
+ that are triggered indirectly by another element. This is a third
+ case: Changing the CE score always affects multiple identical CEs,
+ so every affected CE must be checked, not only the single CE for
+ which the CE score was changed in the first place (as every instance
+ of that CE shares the same CE score, and therefore also can change)!
+ */
+ SCAN_PLAYFIELD(xx, yy)
+ {
+ if (Feld[xx][yy] == element)
+ CheckElementChange(xx, yy, element, EL_UNDEFINED,
+ CE_SCORE_GETS_ZERO);
+ }
+ }
}
- else
- break;
+#endif
+
+ break;
}
- }
- if (!dragon_found)
- {
- for (i = 0; i < NUM_DIRECTIONS; i++)
+ case CA_SET_CE_ARTWORK:
{
- for (j = 0; j < 3; j++)
- {
- int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
-
- if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
- {
- Feld[xx][yy] = EL_EMPTY;
- DrawLevelField(xx, yy);
- }
- else
- break;
- }
- }
- }
-}
+ int artwork_element = action_arg_element;
+ boolean reset_frame = FALSE;
+ int xx, yy;
-static void InitBuggyBase(int x, int y)
-{
- int element = Feld[x][y];
- int activating_delay = FRAMES_PER_SECOND / 4;
+ if (action_arg == CA_ARG_ELEMENT_RESET)
+ artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
+ element);
- ChangeDelay[x][y] =
- (element == EL_SP_BUGGY_BASE ?
- 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
- element == EL_SP_BUGGY_BASE_ACTIVATING ?
- activating_delay :
- element == EL_SP_BUGGY_BASE_ACTIVE ?
- 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
-}
+ if (ei->gfx_element != artwork_element)
+ reset_frame = TRUE;
-static void WarnBuggyBase(int x, int y)
-{
- int i;
- static int xy[4][2] =
- {
- { 0, -1 },
- { -1, 0 },
- { +1, 0 },
- { 0, +1 }
- };
+ ei->gfx_element = artwork_element;
- for (i = 0; i < NUM_DIRECTIONS; i++)
- {
- int xx = x + xy[i][0], yy = y + xy[i][1];
+ SCAN_PLAYFIELD(xx, yy)
+ {
+ if (Feld[xx][yy] == element)
+ {
+ if (reset_frame)
+ {
+ ResetGfxAnimation(xx, yy);
+ ResetRandomAnimationValue(xx, yy);
+ }
- if (IS_PLAYER(xx, yy))
- {
- PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
+ TEST_DrawLevelField(xx, yy);
+ }
+ }
break;
}
- }
-}
-static void InitTrap(int x, int y)
-{
- ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
-}
+ /* ---------- engine actions ------------------------------------------ */
-static void ActivateTrap(int x, int y)
-{
- PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
-}
+ case CA_SET_ENGINE_SCAN_MODE:
+ {
+ InitPlayfieldScanMode(action_arg);
-static void ChangeActiveTrap(int x, int y)
-{
- int graphic = IMG_TRAP_ACTIVE;
+ break;
+ }
- /* if new animation frame was drawn, correct crumbled sand border */
- if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
- DrawLevelFieldCrumbledSand(x, y);
+ default:
+ break;
+ }
}
-static void ChangeElementNowExt(int x, int y, int target_element)
+static void CreateFieldExt(int x, int y, int element, boolean is_change)
{
+ int old_element = Feld[x][y];
+ int new_element = GetElementFromGroupElement(element);
int previous_move_direction = MovDir[x][y];
-#if 1
- boolean add_player = (ELEM_IS_PLAYER(target_element) &&
- IS_WALKABLE(Feld[x][y]));
-#else
- boolean add_player = (ELEM_IS_PLAYER(target_element) &&
- IS_WALKABLE(Feld[x][y]) &&
- !IS_MOVING(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
+ /* this breaks SnakeBite when a snake is
+ halfway through a door that closes */
+ /* NOW FIXED AT LEVEL INIT IN files.c */
+ new_element != EL_SOKOBAN_FIELD_PLAYER &&
+#endif
+ IS_WALKABLE(old_element));
- /* check if element under player changes from accessible to unaccessible
+#if 0
+ /* check if element under the player changes from accessible to unaccessible
(needed for special case of dropping element which then changes) */
if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
- IS_ACCESSIBLE(Feld[x][y]) && !IS_ACCESSIBLE(target_element))
+ IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
{
-#if 0
- printf("::: BOOOM! [%d, '%s']\n", target_element,
- element_info[target_element].token_name);
-#endif
-
Bang(x, y);
+
return;
}
-
-#if 1
- if (!add_player)
#endif
+
+ if (!add_player_onto_element)
{
-#if 1
if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
RemoveMovingField(x, y);
else
RemoveField(x, y);
- Feld[x][y] = target_element;
-#else
- RemoveField(x, y);
- Feld[x][y] = target_element;
-#endif
+ Feld[x][y] = new_element;
+#if !USE_GFX_RESET_GFX_ANIMATION
ResetGfxAnimation(x, y);
ResetRandomAnimationValue(x, y);
+#endif
- if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
+ if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
MovDir[x][y] = previous_move_direction;
-#if 1
- InitField_WithBug1(x, y, FALSE);
-#else
- InitField(x, y, FALSE);
- if (CAN_MOVE(Feld[x][y]))
- InitMovDir(x, y);
+#if USE_NEW_CUSTOM_VALUE
+ if (element_info[new_element].use_last_ce_value)
+ CustomValue[x][y] = last_ce_value;
#endif
- DrawLevelField(x, y);
+ InitField_WithBug1(x, y, FALSE);
- if (GFX_CRUMBLED(Feld[x][y]))
- DrawLevelFieldCrumbledSandNeighbours(x, y);
- }
+ new_element = Feld[x][y]; /* element may have changed */
-#if 0
- Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
+#if USE_GFX_RESET_GFX_ANIMATION
+ ResetGfxAnimation(x, y);
+ ResetRandomAnimationValue(x, y);
#endif
-#if 0
- TestIfBadThingTouchesHero(x, y);
- TestIfPlayerTouchesCustomElement(x, y);
- TestIfElementTouchesCustomElement(x, y);
-#endif
+ TEST_DrawLevelField(x, y);
- /* "Changed[][]" not set yet to allow "entered by player" change one time */
- if (ELEM_IS_PLAYER(target_element))
- RelocatePlayer(x, y, target_element);
+ if (GFX_CRUMBLED(new_element))
+ TEST_DrawLevelFieldCrumbledNeighbours(x, y);
+ }
#if 1
- Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
+ /* 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))
+ {
+ Bang(x, y);
+
+ return;
+ }
+#endif
#endif
-#if 1
- TestIfBadThingTouchesHero(x, y);
+ /* "ChangeCount" not set yet to allow "entered by player" change one time */
+ if (new_element_is_player)
+ RelocatePlayer(x, y, new_element);
+
+ if (is_change)
+ ChangeCount[x][y]++; /* count number of changes in the same frame */
+
+ TestIfBadThingTouchesPlayer(x, y);
TestIfPlayerTouchesCustomElement(x, y);
TestIfElementTouchesCustomElement(x, y);
+}
+
+static void CreateField(int x, int y, int element)
+{
+ CreateFieldExt(x, y, element, FALSE);
+}
+
+static void CreateElementFromChange(int x, int y, int element)
+{
+ element = GET_VALID_RUNTIME_ELEMENT(element);
+
+#if USE_STOP_CHANGED_ELEMENTS
+ if (game.engine_version >= VERSION_IDENT(3,2,0,7))
+ {
+ int old_element = Feld[x][y];
+
+ /* prevent changed element from moving in same engine frame
+ unless both old and new element can either fall or move */
+ if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
+ (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
+ Stop[x][y] = TRUE;
+ }
#endif
+
+ CreateFieldExt(x, y, element, TRUE);
}
-static boolean ChangeElementNow(int x, int y, int element, int page)
+static boolean ChangeElement(int x, int y, int element, int page)
{
- struct ElementChangeInfo *change = &element_info[element].change_page[page];
+ struct ElementInfo *ei = &element_info[element];
+ struct ElementChangeInfo *change = &ei->change_page[page];
+ int ce_value = CustomValue[x][y];
+ int ce_score = ei->collect_score;
int target_element;
int old_element = Feld[x][y];
/* always use default change event to prevent running into a loop */
- if (ChangeEvent[x][y] == CE_BITMASK_DEFAULT)
- ChangeEvent[x][y] = CH_EVENT_BIT(CE_DELAY);
+ if (ChangeEvent[x][y] == -1)
+ ChangeEvent[x][y] = CE_DELAY;
- if (ChangeEvent[x][y] == CH_EVENT_BIT(CE_DELAY))
+ if (ChangeEvent[x][y] == CE_DELAY)
{
- /* reset actual trigger element and player */
+ /* reset actual trigger element, trigger player and action element */
change->actual_trigger_element = EL_EMPTY;
- change->actual_trigger_player = EL_PLAYER_1;
+ change->actual_trigger_player = EL_EMPTY;
+ change->actual_trigger_player_bits = CH_PLAYER_NONE;
+ change->actual_trigger_side = CH_SIDE_NONE;
+ change->actual_trigger_ce_value = 0;
+ change->actual_trigger_ce_score = 0;
}
- /* do not change already changed elements with same change event */
-#if 0
- if (Changed[x][y] & ChangeEvent[x][y])
- return FALSE;
-#else
- if (Changed[x][y])
+ /* do not change elements more than a specified maximum number of changes */
+ if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
return FALSE;
-#endif
- Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
-
-#if 0
- /* !!! indirect change before direct change !!! */
- CheckTriggeredElementChangeByPage(x,y,Feld[x][y], CE_OTHER_IS_CHANGING,page);
-#endif
+ ChangeCount[x][y]++; /* count number of changes in the same frame */
if (change->explode)
{
boolean is_destructible;
int ex = x + xx - 1;
int ey = y + yy - 1;
- int content_element = change->target_content[xx][yy];
+ int content_element = change->target_content.e[xx][yy];
int e;
can_replace[xx][yy] = TRUE;
continue;
}
-#if 0
- if (Changed[ex][ey]) /* do not change already changed elements */
- {
- can_replace[xx][yy] = FALSE;
- complete_replace = FALSE;
-
- continue;
- }
-#endif
-
e = Feld[ex][ey];
if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
e = MovingOrBlocked2Element(ex, ey);
-#if 1
-
-#if 0
- is_empty = (IS_FREE(ex, ey) ||
- (IS_PLAYER(ex, ey) && IS_WALKABLE(content_element)) ||
- (IS_WALKABLE(e) && ELEM_IS_PLAYER(content_element) &&
- !IS_MOVING(ex, ey) && !IS_BLOCKED(ex, ey)));
-#else
-
-#if 0
- is_empty = (IS_FREE(ex, ey) ||
- (IS_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
-#else
is_empty = (IS_FREE(ex, ey) ||
(IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
-#endif
-
-#endif
is_walkable = (is_empty || IS_WALKABLE(e));
is_diggable = (is_empty || IS_DIGGABLE(e));
if (!can_replace[xx][yy])
complete_replace = FALSE;
-#else
- empty_for_element = (IS_FREE(ex, ey) || (IS_FREE_OR_PLAYER(ex, ey) &&
- IS_WALKABLE(content_element)));
-#if 1
- half_destructible = (empty_for_element || IS_DIGGABLE(e));
-#else
- half_destructible = (IS_FREE(ex, ey) || IS_DIGGABLE(e));
-#endif
-
- if ((change->replace_when <= CP_WHEN_EMPTY && !empty_for_element) ||
- (change->replace_when <= CP_WHEN_DIGGABLE && !half_destructible) ||
- (change->replace_when <= CP_WHEN_DESTRUCTIBLE && IS_INDESTRUCTIBLE(e)))
- {
- can_replace[xx][yy] = FALSE;
- complete_replace = FALSE;
- }
-#endif
}
if (!change->only_if_complete || complete_replace)
ChangeEvent[ex][ey] = ChangeEvent[x][y];
- content_element = change->target_content[xx][yy];
- target_element = GET_TARGET_ELEMENT(content_element, change);
+ content_element = change->target_content.e[xx][yy];
+ target_element = GET_TARGET_ELEMENT(element, content_element, change,
+ ce_value, ce_score);
- ChangeElementNowExt(ex, ey, target_element);
+ CreateElementFromChange(ex, ey, target_element);
something_has_changed = TRUE;
}
if (something_has_changed)
+ {
PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
+ PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
+ }
}
}
else
{
- target_element = GET_TARGET_ELEMENT(change->target_element, change);
+ target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
+ ce_value, ce_score);
+
+ if (element == EL_DIAGONAL_GROWING ||
+ element == EL_DIAGONAL_SHRINKING)
+ {
+ target_element = Store[x][y];
+
+ Store[x][y] = EL_EMPTY;
+ }
- ChangeElementNowExt(x, y, target_element);
+ CreateElementFromChange(x, y, target_element);
PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
+ PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
}
-#if 1
/* this uses direct change before indirect change */
- CheckTriggeredElementChangeByPage(x,y,old_element,CE_OTHER_IS_CHANGING,page);
-#endif
+ CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
return TRUE;
}
-static void ChangeElement(int x, int y, int page)
+#if USE_NEW_DELAYED_ACTION
+
+static void HandleElementChange(int x, int y, int page)
+{
+ int element = MovingOrBlocked2Element(x, y);
+ struct ElementInfo *ei = &element_info[element];
+ struct ElementChangeInfo *change = &ei->change_page[page];
+ boolean handle_action_before_change = FALSE;
+
+#ifdef DEBUG
+ if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
+ !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
+ {
+ printf("\n\n");
+ printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
+ x, y, element, element_info[element].token_name);
+ printf("HandleElementChange(): This should never happen!\n");
+ printf("\n\n");
+ }
+#endif
+
+ /* this can happen with classic bombs on walkable, changing elements */
+ if (!CAN_CHANGE_OR_HAS_ACTION(element))
+ {
+#if 0
+ if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
+ ChangeDelay[x][y] = 0;
+#endif
+
+ return;
+ }
+
+ if (ChangeDelay[x][y] == 0) /* initialize element change */
+ {
+ ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
+
+ if (change->can_change)
+ {
+#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);
+ }
+ }
+
+ ChangeDelay[x][y]--;
+
+ if (ChangeDelay[x][y] != 0) /* continue element change */
+ {
+ if (change->can_change)
+ {
+ int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
+
+ if (IS_ANIMATED(graphic))
+ DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
+
+ if (change->change_function)
+ change->change_function(x, y);
+ }
+ }
+ else /* finish element change */
+ {
+ if (ChangePage[x][y] != -1) /* remember page from delayed change */
+ {
+ page = ChangePage[x][y];
+ ChangePage[x][y] = -1;
+
+ change = &ei->change_page[page];
+ }
+
+ if (IS_MOVING(x, y)) /* never change a running system ;-) */
+ {
+ ChangeDelay[x][y] = 1; /* try change after next move step */
+ ChangePage[x][y] = page; /* remember page to use for change */
+
+ return;
+ }
+
+#if 1
+ /* special case: set new level random seed before changing element */
+ if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
+ handle_action_before_change = TRUE;
+
+ if (change->has_action && handle_action_before_change)
+ ExecuteCustomElementAction(x, y, element, page);
+#endif
+
+ if (change->can_change)
+ {
+ if (ChangeElement(x, y, element, page))
+ {
+ if (change->post_change_function)
+ change->post_change_function(x, y);
+ }
+ }
+
+ if (change->has_action && !handle_action_before_change)
+ ExecuteCustomElementAction(x, y, element, page);
+ }
+}
+
+#else
+
+static void HandleElementChange(int x, int y, int page)
{
int element = MovingOrBlocked2Element(x, y);
struct ElementInfo *ei = &element_info[element];
if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
{
printf("\n\n");
- printf("ChangeElement(): %d,%d: element = %d ('%s')\n",
+ printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
x, y, element, element_info[element].token_name);
- printf("ChangeElement(): This should never happen!\n");
+ printf("HandleElementChange(): This should never happen!\n");
printf("\n\n");
}
#endif
if (ChangeDelay[x][y] == 0) /* initialize element change */
{
- ChangeDelay[x][y] = ( change->delay_fixed * change->delay_frames +
- RND(change->delay_random * change->delay_frames)) + 1;
+ ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
ResetGfxAnimation(x, y);
ResetRandomAnimationValue(x, y);
change = &ei->change_page[page];
}
-#if 0
- if (IS_MOVING(x, y) && !change->explode)
-#else
if (IS_MOVING(x, y)) /* never change a running system ;-) */
-#endif
{
ChangeDelay[x][y] = 1; /* try change after next move step */
ChangePage[x][y] = page; /* remember page to use for change */
return;
}
- if (ChangeElementNow(x, y, element, page))
+ if (ChangeElement(x, y, element, page))
{
if (change->post_change_function)
change->post_change_function(x, y);
}
}
-static boolean CheckTriggeredElementChangeExt(int lx, int ly,
+#endif
+
+static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
int trigger_element,
int trigger_event,
int trigger_player,
int trigger_side,
int trigger_page)
{
- int i, j, x, y;
+ boolean change_done_any = FALSE;
int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
+ int i;
- if (!(trigger_events[trigger_element] & CH_EVENT_BIT(trigger_event)))
+ 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;
+ boolean change_done = FALSE;
+ int p;
- boolean change_element = FALSE;
- int page = 0;
-
- if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
+ if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
+ !HAS_ANY_CHANGE_EVENT(element, trigger_event))
continue;
- for (j = 0; j < element_info[element].num_change_pages; j++)
+ for (p = 0; p < element_info[element].num_change_pages; p++)
{
- struct ElementChangeInfo *change = &element_info[element].change_page[j];
+ struct ElementChangeInfo *change = &element_info[element].change_page[p];
- if (change->can_change &&
- change->events & CH_EVENT_BIT(trigger_event) &&
+ if (change->can_change_or_has_action &&
+ change->has_event[trigger_event] &&
change->trigger_side & trigger_side &&
change->trigger_player & trigger_player &&
change->trigger_page & trigger_page_bits &&
IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
{
+ change->actual_trigger_element = trigger_element;
+ change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
+ change->actual_trigger_player_bits = trigger_player;
+ change->actual_trigger_side = trigger_side;
+ change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
+ change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
+
#if 0
- if (!(change->events & CH_EVENT_BIT(trigger_event)))
- printf("::: !!! %d triggers %d: using wrong page %d [event %d]\n",
- trigger_element-EL_CUSTOM_START+1, i+1, j, trigger_event);
+ printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d\n",
+ element, EL_NAME(element), p);
#endif
- change_element = TRUE;
- page = j;
+ if ((change->can_change && !change_done) || change->has_action)
+ {
+ int x, y;
- change->actual_trigger_element = trigger_element;
- change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
+ SCAN_PLAYFIELD(x, y)
+ {
+ if (Feld[x][y] == element)
+ {
+ if (change->can_change && !change_done)
+ {
+#if USE_FIX_NO_ACTION_AFTER_CHANGE
+ /* if element already changed in this frame, not only prevent
+ another element change (checked in ChangeElement()), but
+ also prevent additional element actions for this element */
- break;
- }
- }
+ if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
+ !level.use_action_after_change_bug)
+ continue;
+#endif
+
+#if 0
+ printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- CHANGE\n",
+ element, EL_NAME(element), p);
+#endif
+
+ ChangeDelay[x][y] = 1;
+ ChangeEvent[x][y] = trigger_event;
+
+ HandleElementChange(x, y, p);
+ }
+#if USE_NEW_DELAYED_ACTION
+ else if (change->has_action)
+ {
+#if USE_FIX_NO_ACTION_AFTER_CHANGE
+ /* if element already changed in this frame, not only prevent
+ another element change (checked in ChangeElement()), but
+ also prevent additional element actions for this element */
+
+ if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
+ !level.use_action_after_change_bug)
+ continue;
+#endif
+
+
+#if 0
+ printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- ACTION\n",
+ element, EL_NAME(element), p);
+#endif
+
+ ExecuteCustomElementAction(x, y, element, p);
+ PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
+ }
+#else
+ if (change->has_action)
+ {
+ ExecuteCustomElementAction(x, y, element, p);
+ PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
+ }
+#endif
+ }
+ }
- if (!change_element)
- continue;
+ if (change->can_change)
+ {
+ change_done = TRUE;
+ change_done_any = TRUE;
- for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
- {
#if 0
- if (x == lx && y == ly) /* do not change trigger element itself */
- continue;
+ printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- DONE\n",
+ element, EL_NAME(element), p);
#endif
- if (Feld[x][y] == element)
- {
- ChangeDelay[x][y] = 1;
- ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
- ChangeElement(x, y, page);
+ }
+ }
}
}
}
- return TRUE;
+ RECURSION_LOOP_DETECTION_END();
+
+ return change_done_any;
}
static boolean CheckElementChangeExt(int x, int y,
int trigger_element,
int trigger_event,
int trigger_player,
- int trigger_side,
- int trigger_page)
+ int trigger_side)
{
- if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
+ boolean change_done = FALSE;
+ int p;
+
+ if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
+ !HAS_ANY_CHANGE_EVENT(element, trigger_event))
return FALSE;
if (Feld[x][y] == EL_BLOCKED)
element = Feld[x][y];
}
-#if 1
- if (Feld[x][y] != element) /* check if element has already changed */
- {
#if 0
- printf("::: %d ('%s') != %d ('%s') [%d]\n",
- Feld[x][y], element_info[Feld[x][y]].token_name,
- element, element_info[element].token_name,
- trigger_event);
-#endif
+ /* check if element has already changed */
+ if (Feld[x][y] != element)
+ return FALSE;
+#else
+ /* check if element has already changed or is about to change after moving */
+ if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
+ Feld[x][y] != element) ||
+ (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
+ (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
+ ChangePage[x][y] != -1)))
return FALSE;
- }
#endif
-#if 1
- if (trigger_page < 0)
- {
- boolean change_element = FALSE;
- int i;
-
- for (i = 0; i < element_info[element].num_change_pages; i++)
- {
- struct ElementChangeInfo *change = &element_info[element].change_page[i];
-
- if (change->can_change &&
- change->events & CH_EVENT_BIT(trigger_event) &&
- change->trigger_side & trigger_side &&
- change->trigger_player & trigger_player)
- {
- change_element = TRUE;
- trigger_page = i;
+#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
- change->actual_trigger_element = trigger_element;
- change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
+ RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
- break;
- }
- }
+#if 0
+ printf("::: X: trigger_player_bits == %d\n", trigger_player);
+#endif
- if (!change_element)
- return FALSE;
- }
- else
+ for (p = 0; p < element_info[element].num_change_pages; p++)
{
- struct ElementInfo *ei = &element_info[element];
- struct ElementChangeInfo *change = &ei->change_page[trigger_page];
+ struct ElementChangeInfo *change = &element_info[element].change_page[p];
- change->actual_trigger_element = trigger_element;
- change->actual_trigger_player = EL_PLAYER_1; /* unused */
- }
+ /* 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 ||
+#if 1
+ /* this one was forgotten until 3.2.3 */
+ trigger_event == CE_DIGGING_X);
+#endif
-#else
+ if (change->can_change_or_has_action &&
+ change->has_event[trigger_event] &&
+ change->trigger_side & trigger_side &&
+ change->trigger_player & trigger_player &&
+ (!check_trigger_element ||
+ IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
+ {
+ change->actual_trigger_element = trigger_element;
+ change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
+ change->actual_trigger_player_bits = trigger_player;
+ change->actual_trigger_side = trigger_side;
+ change->actual_trigger_ce_value = CustomValue[x][y];
+ change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
+
+ /* special case: trigger element not at (x,y) position for some events */
+ if (check_trigger_element)
+ {
+ static struct
+ {
+ int dx, dy;
+ } move_xy[] =
+ {
+ { 0, 0 },
+ { -1, 0 },
+ { +1, 0 },
+ { 0, 0 },
+ { 0, -1 },
+ { 0, 0 }, { 0, 0 }, { 0, 0 },
+ { 0, +1 }
+ };
+
+ int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
+ int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
+
+ change->actual_trigger_ce_value = CustomValue[xx][yy];
+ change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
+ }
- /* !!! this check misses pages with same event, but different side !!! */
+ if (change->can_change && !change_done)
+ {
+ ChangeDelay[x][y] = 1;
+ ChangeEvent[x][y] = trigger_event;
- if (trigger_page < 0)
- trigger_page = element_info[element].event_page_nr[trigger_event];
+ HandleElementChange(x, y, p);
- if (!(element_info[element].change_page[trigger_page].trigger_side & trigger_side))
- return FALSE;
+ change_done = TRUE;
+ }
+#if USE_NEW_DELAYED_ACTION
+ else if (change->has_action)
+ {
+ ExecuteCustomElementAction(x, y, element, p);
+ PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
+ }
+#else
+ if (change->has_action)
+ {
+ ExecuteCustomElementAction(x, y, element, p);
+ PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
+ }
#endif
+ }
+ }
- ChangeDelay[x][y] = 1;
- ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
- ChangeElement(x, y, trigger_page);
+ RECURSION_LOOP_DETECTION_END();
- return TRUE;
+ return change_done;
}
static void PlayPlayerSound(struct PlayerInfo *player)
{
int jx = player->jx, jy = player->jy;
- int element = player->element_nr;
+ int sound_element = player->artwork_element;
int last_action = player->last_action_waiting;
int action = player->action_waiting;
if (player->is_waiting)
{
if (action != last_action)
- PlayLevelSoundElementAction(jx, jy, element, action);
+ PlayLevelSoundElementAction(jx, jy, sound_element, action);
else
- PlayLevelSoundElementActionIfLoop(jx, jy, element, action);
+ PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
}
else
{
if (action != last_action)
- StopSound(element_info[element].sound[last_action]);
+ StopSound(element_info[sound_element].sound[last_action]);
if (last_action == ACTION_SLEEPING)
- PlayLevelSoundElementAction(jx, jy, element, ACTION_AWAKENING);
+ PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
}
}
boolean last_waiting = player->is_waiting;
int move_dir = player->MovDir;
+ player->dir_waiting = move_dir;
player->last_action_waiting = player->action_waiting;
if (is_waiting)
player->frame_counter_bored =
FrameCounter +
game.player_boring_delay_fixed +
- SimpleRND(game.player_boring_delay_random);
+ GetSimpleRandom(game.player_boring_delay_random);
player->frame_counter_sleeping =
FrameCounter +
game.player_sleeping_delay_fixed +
- SimpleRND(game.player_sleeping_delay_random);
+ GetSimpleRandom(game.player_sleeping_delay_random);
- InitPlayerGfxAnimation(player, ACTION_WAITING, player->MovDir);
+ InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
}
if (game.player_sleeping_delay_fixed +
player->is_bored ? ACTION_BORING :
ACTION_WAITING);
+ if (player->is_sleeping && player->use_murphy)
+ {
+ /* special case for sleeping Murphy when leaning against non-free tile */
+
+ if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
+ (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
+ !IS_MOVING(player->jx - 1, player->jy)))
+ move_dir = MV_LEFT;
+ else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
+ (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
+ !IS_MOVING(player->jx + 1, player->jy)))
+ move_dir = MV_RIGHT;
+ else
+ player->is_sleeping = FALSE;
+
+ player->dir_waiting = move_dir;
+ }
+
if (player->is_sleeping)
{
if (player->num_special_action_sleeping > 0)
last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
last_special_action + 1 : ACTION_SLEEPING);
int special_graphic =
- el_act_dir2img(player->element_nr, special_action, move_dir);
+ el_act_dir2img(player->artwork_element, special_action, move_dir);
player->anim_delay_counter =
graphic_info[special_graphic].anim_delay_fixed +
- SimpleRND(graphic_info[special_graphic].anim_delay_random);
+ GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
player->post_delay_counter =
graphic_info[special_graphic].post_delay_fixed +
- SimpleRND(graphic_info[special_graphic].post_delay_random);
+ GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
player->special_action_sleeping = special_action;
}
if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
{
int special_action =
- ACTION_BORING_1 + SimpleRND(player->num_special_action_bored);
+ ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
int special_graphic =
- el_act_dir2img(player->element_nr, special_action, move_dir);
+ el_act_dir2img(player->artwork_element, special_action, move_dir);
player->anim_delay_counter =
graphic_info[special_graphic].anim_delay_fixed +
- SimpleRND(graphic_info[special_graphic].anim_delay_random);
+ GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
player->post_delay_counter =
graphic_info[special_graphic].post_delay_fixed +
- SimpleRND(graphic_info[special_graphic].post_delay_random);
+ GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
player->special_action_bored = special_action;
}
player->anim_delay_counter = 0;
player->post_delay_counter = 0;
+ player->dir_waiting = player->MovDir;
player->action_waiting = ACTION_DEFAULT;
player->special_action_bored = ACTION_DEFAULT;
}
}
-#if 1
+static void CheckSingleStepMode(struct PlayerInfo *player)
+{
+ if (tape.single_step && tape.recording && !tape.pausing)
+ {
+ /* as it is called "single step mode", just return to pause mode when the
+ player stopped moving after one tile (or never starts moving at all) */
+ if (!player->is_moving && !player->is_pushing)
+ {
+ TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
+ SnapField(player, 0, 0); /* stop snapping */
+ }
+ }
+}
+
static byte PlayerActions(struct PlayerInfo *player, byte player_action)
{
-#if 0
- static byte stored_player_action[MAX_PLAYERS];
- static int num_stored_actions = 0;
-#endif
boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
int left = player_action & JOY_LEFT;
int right = player_action & JOY_RIGHT;
int down = player_action & JOY_DOWN;
int button1 = player_action & JOY_BUTTON_1;
int button2 = player_action & JOY_BUTTON_2;
- int dx = (left ? -1 : right ? 1 : 0);
- int dy = (up ? -1 : down ? 1 : 0);
-
-#if 0
- stored_player_action[player->index_nr] = 0;
- num_stored_actions++;
-#endif
-
-#if 0
- printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
-#endif
+ int dx = (left ? -1 : right ? 1 : 0);
+ int dy = (up ? -1 : down ? 1 : 0);
if (!player->active || tape.pausing)
return 0;
-#if 0
- printf("::: [%d %d %d %d] [%d %d]\n",
- left, right, up, down, button1, button2);
-#endif
-
if (player_action)
{
-#if 0
- printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
-#endif
-
-#if 0
- /* !!! TEST !!! */
- if (player->MovPos == 0)
- CheckGravityMovement(player);
-#endif
if (button1)
snapped = SnapField(player, dx, dy);
else
moved = MovePlayer(player, dx, dy);
}
- if (tape.single_step && tape.recording && !tape.pausing)
- {
- if (button1 || (dropped && !moved))
- {
- TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
- SnapField(player, 0, 0); /* stop snapping */
- }
- }
+ CheckSingleStepMode(player);
SetPlayerWaiting(player, FALSE);
-#if 1
return player_action;
-#else
- stored_player_action[player->index_nr] = player_action;
-#endif
}
else
{
-#if 0
- printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
-#endif
-
/* no actions for this player (no input at player's configured device) */
DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
player->is_moving = FALSE;
player->is_dropping = FALSE;
+ player->is_dropping_pressed = FALSE;
+ player->drop_pressed_delay = 0;
+
+ CheckSingleStepMode(player);
return 0;
}
+}
-#if 0
- if (tape.recording && num_stored_actions >= MAX_PLAYERS)
+static void CheckLevelTime()
+{
+ int i;
+
+ /* !!! SAME CODE AS IN "GameActions()" -- FIX THIS !!! */
+ if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
{
- printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
+ if (level.native_em_level->lev->home == 0) /* all players at home */
+ {
+ PlayerWins(local_player);
- TapeRecordAction(stored_player_action);
- num_stored_actions = 0;
- }
-#endif
-}
+ AllPlayersGone = TRUE;
-#else
+ level.native_em_level->lev->home = -1;
+ }
-static void PlayerActions(struct PlayerInfo *player, byte player_action)
-{
- static byte stored_player_action[MAX_PLAYERS];
- static int num_stored_actions = 0;
- boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
- int left = player_action & JOY_LEFT;
- int right = player_action & JOY_RIGHT;
- int up = player_action & JOY_UP;
- int down = player_action & JOY_DOWN;
- int button1 = player_action & JOY_BUTTON_1;
- int button2 = player_action & JOY_BUTTON_2;
- int dx = (left ? -1 : right ? 1 : 0);
- int dy = (up ? -1 : down ? 1 : 0);
+ if (level.native_em_level->ply[0]->alive == 0 &&
+ level.native_em_level->ply[1]->alive == 0 &&
+ level.native_em_level->ply[2]->alive == 0 &&
+ level.native_em_level->ply[3]->alive == 0) /* all dead */
+ AllPlayersGone = TRUE;
+ }
+ else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
+ {
+ if (game_sp.LevelSolved &&
+ !game_sp.GameOver) /* game won */
+ {
+ PlayerWins(local_player);
- stored_player_action[player->index_nr] = 0;
- num_stored_actions++;
+ game_sp.GameOver = TRUE;
- printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
+ AllPlayersGone = TRUE;
+ }
- if (!player->active || tape.pausing)
- return;
+ if (game_sp.GameOver) /* game lost */
+ AllPlayersGone = TRUE;
+ }
- if (player_action)
+ if (TimeFrames >= FRAMES_PER_SECOND)
{
- printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
+ TimeFrames = 0;
+ TapeTime++;
- if (button1)
- snapped = SnapField(player, dx, dy);
- else
+ for (i = 0; i < MAX_PLAYERS; i++)
{
- if (button2)
- dropped = DropElement(player);
+ struct PlayerInfo *player = &stored_player[i];
- moved = MovePlayer(player, dx, dy);
+ if (SHIELD_ON(player))
+ {
+ player->shield_normal_time_left--;
+
+ if (player->shield_deadly_time_left > 0)
+ player->shield_deadly_time_left--;
+ }
}
- if (tape.single_step && tape.recording && !tape.pausing)
+ if (!local_player->LevelSolved && !level.use_step_counter)
{
- if (button1 || (dropped && !moved))
+ TimePlayed++;
+
+ if (TimeLeft > 0)
{
- TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
- SnapField(player, 0, 0); /* stop snapping */
- }
- }
+ TimeLeft--;
- stored_player_action[player->index_nr] = player_action;
- }
- else
- {
- printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
+ if (TimeLeft <= 10 && setup.time_limit)
+ PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
- /* no actions for this player (no input at player's configured device) */
+#if 1
+ game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
- DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
- SnapField(player, 0, 0);
- CheckGravityMovementWhenNotMoving(player);
+ DisplayGameControlValues();
+#else
+ DrawGameValue_Time(TimeLeft);
+#endif
- if (player->MovPos == 0)
- InitPlayerGfxAnimation(player, ACTION_DEFAULT, player->MovDir);
+ if (!TimeLeft && setup.time_limit)
+ {
+ if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
+ level.native_em_level->lev->killed_out_of_time = TRUE;
+ else
+ for (i = 0; i < MAX_PLAYERS; i++)
+ KillPlayer(&stored_player[i]);
+ }
+ }
+#if 1
+ else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
+ {
+ game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
- if (player->MovPos == 0) /* needed for tape.playing */
- player->is_moving = FALSE;
- }
+ DisplayGameControlValues();
+ }
+#else
+ else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
+ DrawGameValue_Time(TimePlayed);
+#endif
- if (tape.recording && num_stored_actions >= MAX_PLAYERS)
- {
- printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
+ level.native_em_level->lev->time =
+ (level.time == 0 ? TimePlayed : TimeLeft);
+ }
- TapeRecordAction(stored_player_action);
- num_stored_actions = 0;
+ if (tape.recording || tape.playing)
+ DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
}
-}
+
+#if 1
+ UpdateAndDisplayGameControlValues();
+#else
+ UpdateGameDoorValues();
+ DrawGameDoorValues();
#endif
+}
void AdvanceFrameAndPlayerCounters(int player_nr)
{
for (i = 0; i < MAX_PLAYERS; i++)
{
boolean advance_player_counters = (player_nr == -1 || player_nr == i);
- int move_frames =
- MOVE_DELAY_NORMAL_SPEED / stored_player[i].move_delay_value;
+ int move_delay_value = stored_player[i].move_delay_value;
+ int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
if (!advance_player_counters) /* not all players may be affected */
continue;
+#if USE_NEW_PLAYER_ANIM
+ if (move_frames == 0) /* less than one move per game frame */
+ {
+ int stepsize = TILEX / move_delay_value;
+ int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
+ int count = (stored_player[i].is_moving ?
+ ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
+
+ if (count % delay == 0)
+ move_frames = 1;
+ }
+#endif
+
stored_player[i].Frame += move_frames;
if (stored_player[i].MovPos != 0)
stored_player[i].StepFrame += move_frames;
-#if USE_NEW_MOVE_DELAY
if (stored_player[i].move_delay > 0)
stored_player[i].move_delay--;
-#endif
-#if USE_NEW_PUSH_DELAY
/* due to bugs in previous versions, counter must count up, not down */
if (stored_player[i].push_delay != -1)
stored_player[i].push_delay++;
-#endif
if (stored_player[i].drop_delay > 0)
stored_player[i].drop_delay--;
+
+ if (stored_player[i].is_dropping_pressed)
+ stored_player[i].drop_pressed_delay++;
+ }
+}
+
+void StartGameActions(boolean init_network_game, boolean record_tape,
+ long random_seed)
+{
+ unsigned long new_random_seed = InitRND(random_seed);
+
+ if (record_tape)
+ TapeStartRecording(new_random_seed);
+
+#if defined(NETWORK_AVALIABLE)
+ if (init_network_game)
+ {
+ SendToServer_StartPlaying();
+
+ return;
}
+#endif
+
+ InitGame();
}
void GameActions()
{
- static unsigned long action_delay = 0;
- unsigned long action_delay_value;
- int magic_wall_x = 0, magic_wall_y = 0;
- int i, x, y, element, graphic;
+ static unsigned long game_frame_delay = 0;
+ unsigned long game_frame_delay_value;
byte *recorded_player_action;
byte summarized_player_action = 0;
-#if 1
byte tape_action[MAX_PLAYERS];
-#endif
+ 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);
- if (game_status != GAME_MODE_PLAYING)
return;
+ }
+
+ if (game.restart_level)
+ StartGameActions(options.network, setup.autorecord, level.random_seed);
+
+ /* !!! SAME CODE AS IN "CheckLevelTime()" -- FIX THIS !!! */
+ if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
+ {
+ if (level.native_em_level->lev->home == 0) /* all players at home */
+ {
+ PlayerWins(local_player);
+
+ AllPlayersGone = TRUE;
+
+ level.native_em_level->lev->home = -1;
+ }
+
+ if (level.native_em_level->ply[0]->alive == 0 &&
+ level.native_em_level->ply[1]->alive == 0 &&
+ level.native_em_level->ply[2]->alive == 0 &&
+ level.native_em_level->ply[3]->alive == 0) /* all dead */
+ AllPlayersGone = TRUE;
+ }
+ else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
+ {
+ if (game_sp.LevelSolved &&
+ !game_sp.GameOver) /* game won */
+ {
+ PlayerWins(local_player);
+
+ game_sp.GameOver = TRUE;
+
+ AllPlayersGone = TRUE;
+ }
+
+ if (game_sp.GameOver) /* game lost */
+ AllPlayersGone = TRUE;
+ }
+
+ if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
+ GameWon();
+
+ if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
+ TapeStop();
- action_delay_value =
+ if (game_status != GAME_MODE_PLAYING) /* status might have changed */
+ return;
+
+ game_frame_delay_value =
(tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
if (tape.playing && tape.warp_forward && !tape.pausing)
- action_delay_value = 0;
+ game_frame_delay_value = 0;
/* ---------- main game synchronization point ---------- */
- WaitUntilDelayReached(&action_delay, action_delay_value);
+ WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
if (network_playing && !network_player_action_received)
{
- /*
-#ifdef DEBUG
- printf("DEBUG: try to get network player actions in time\n");
-#endif
- */
+ /* try to get network player actions in time */
#if defined(NETWORK_AVALIABLE)
/* last chance to get network player actions without main loop delay */
HandleNetworking();
#endif
+ /* game was quit by network peer */
if (game_status != GAME_MODE_PLAYING)
return;
if (!network_player_action_received)
- {
- /*
-#ifdef DEBUG
- printf("DEBUG: failed to get network player actions in time\n");
-#endif
- */
- return;
- }
+ return; /* failed to get network player actions in time */
+
+ /* do not yet reset "network_player_action_received" (for tape.pausing) */
}
if (tape.pausing)
return;
-#if 0
- printf("::: getting new tape action [%d]\n", FrameCounter);
-#endif
+ /* at this point we know that we really continue executing the game */
+
+ network_player_action_received = FALSE;
+ /* when playing tape, read previously recorded player input from tape data */
recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
#if 1
- if (recorded_player_action == NULL && tape.pausing)
+ /* TapePlayAction() may return NULL when toggling to "pause before death" */
+ if (tape.pausing)
return;
#endif
-#if 0
- printf("::: %d\n", stored_player[0].action);
-#endif
-
-#if 0
- if (recorded_player_action != NULL)
- for (i = 0; i < MAX_PLAYERS; i++)
- stored_player[i].action = recorded_player_action[i];
-#endif
+ if (tape.set_centered_player)
+ {
+ game.centered_player_nr_next = tape.centered_player_nr_next;
+ game.set_centered_player = TRUE;
+ }
for (i = 0; i < MAX_PLAYERS; i++)
{
if (!options.network && !setup.team_mode)
local_player->effective_action = summarized_player_action;
-#if 1
+ if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
+ {
+ for (i = 0; i < MAX_PLAYERS; i++)
+ stored_player[i].effective_action =
+ (i == game.centered_player_nr ? summarized_player_action : 0);
+ }
+
if (recorded_player_action != NULL)
for (i = 0; i < MAX_PLAYERS; i++)
stored_player[i].effective_action = recorded_player_action[i];
-#endif
-#if 1
for (i = 0; i < MAX_PLAYERS; i++)
{
tape_action[i] = stored_player[i].effective_action;
+ /* (this can only happen in the R'n'D game engine) */
if (tape.recording && tape_action[i] && !tape.player_participates[i])
tape.player_participates[i] = TRUE; /* player just appeared from CE */
}
- /* only save actions from input devices, but not programmed actions */
+ /* only record actions from input devices, but not programmed actions */
if (tape.recording)
TapeRecordAction(tape_action);
+
+#if USE_NEW_PLAYER_ASSIGNMENTS
+ {
+ byte mapped_action[MAX_PLAYERS];
+
+ for (i = 0; i < MAX_PLAYERS; i++)
+ mapped_action[i] = stored_player[map_player_action[i]].effective_action;
+
+ for (i = 0; i < MAX_PLAYERS; i++)
+ stored_player[i].effective_action = mapped_action[i];
+ }
+#endif
+
+ if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
+ {
+ GameActions_EM_Main();
+ }
+ else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
+ {
+ GameActions_SP_Main();
+ }
+ else
+ {
+ GameActions_RND();
+ }
+}
+
+void GameActions_EM_Main()
+{
+ byte effective_action[MAX_PLAYERS];
+ boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
+ int i;
+
+ for (i = 0; i < MAX_PLAYERS; i++)
+ effective_action[i] = stored_player[i].effective_action;
+
+ GameActions_EM(effective_action, warp_mode);
+
+ CheckLevelTime();
+
+ AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
+}
+
+void GameActions_SP_Main()
+{
+ byte effective_action[MAX_PLAYERS];
+ boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
+ int i;
+
+ for (i = 0; i < MAX_PLAYERS; i++)
+ effective_action[i] = stored_player[i].effective_action;
+
+ GameActions_SP(effective_action, warp_mode);
+
+ CheckLevelTime();
+
+ AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
+}
+
+void GameActions_RND()
+{
+ int magic_wall_x = 0, magic_wall_y = 0;
+ int i, x, y, element, graphic;
+
+ InitPlayfieldScanModeVars();
+
+#if USE_ONE_MORE_CHANGE_PER_FRAME
+ if (game.engine_version >= VERSION_IDENT(3,2,0,7))
+ {
+ SCAN_PLAYFIELD(x, y)
+ {
+ ChangeCount[x][y] = 0;
+ ChangeEvent[x][y] = -1;
+ }
+ }
#endif
+ if (game.set_centered_player)
+ {
+ boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
+
+ /* switching to "all players" only possible if all players fit to screen */
+ if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
+ {
+ game.centered_player_nr_next = game.centered_player_nr;
+ game.set_centered_player = FALSE;
+ }
+
+ /* do not switch focus to non-existing (or non-active) player */
+ if (game.centered_player_nr_next >= 0 &&
+ !stored_player[game.centered_player_nr_next].active)
+ {
+ game.centered_player_nr_next = game.centered_player_nr;
+ game.set_centered_player = FALSE;
+ }
+ }
+
+ if (game.set_centered_player &&
+ ScreenMovPos == 0) /* screen currently aligned at tile position */
+ {
+ int sx, sy;
+
+ if (game.centered_player_nr_next == -1)
+ {
+ setScreenCenteredToAllPlayers(&sx, &sy);
+ }
+ else
+ {
+ sx = stored_player[game.centered_player_nr_next].jx;
+ sy = stored_player[game.centered_player_nr_next].jy;
+ }
+
+ game.centered_player_nr = game.centered_player_nr_next;
+ game.set_centered_player = FALSE;
+
+ DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
+ DrawGameDoorValues();
+ }
+
for (i = 0; i < MAX_PLAYERS; i++)
{
int actual_player_action = stored_player[i].effective_action;
CheckGravityMovement(&stored_player[i]);
#endif
-#if 1
/* overwrite programmed action with tape action */
if (stored_player[i].programmed_action)
actual_player_action = stored_player[i].programmed_action;
-#endif
-
-#if 0
- if (stored_player[i].programmed_action)
- printf("::: %d\n", stored_player[i].programmed_action);
-#endif
-
- if (recorded_player_action)
- {
-#if 0
- if (stored_player[i].programmed_action &&
- stored_player[i].programmed_action != recorded_player_action[i])
- printf("::: %d: %d <-> %d\n", i,
- stored_player[i].programmed_action, recorded_player_action[i]);
-#endif
-
-#if 0
- actual_player_action = recorded_player_action[i];
-#endif
- }
-
-#if 0
- /* overwrite tape action with programmed action */
- if (stored_player[i].programmed_action)
- actual_player_action = stored_player[i].programmed_action;
-#endif
-
-#if 0
- if (i == 0)
- printf("::: action: %d: %x [%d]\n",
- stored_player[i].MovPos, actual_player_action, FrameCounter);
-#endif
-#if 1
PlayerActions(&stored_player[i], actual_player_action);
-#else
- tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
-
- if (tape.recording && tape_action[i] && !tape.player_participates[i])
- tape.player_participates[i] = TRUE; /* player just appeared from CE */
-#endif
ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
}
-#if 0
- if (tape.recording)
- TapeRecordAction(tape_action);
-#endif
-
- network_player_action_received = FALSE;
-
ScrollScreen(NULL, SCROLL_GO_ON);
-#if 0
- FrameCounter++;
- TimeFrames++;
-
- for (i = 0; i < MAX_PLAYERS; i++)
- stored_player[i].Frame++;
-#endif
-
-#if 1
/* for backwards compatibility, the following code emulates a fixed bug that
occured when pushing elements (causing elements that just made their last
pushing step to already (if possible) make their first falling step in the
used also in newer levels, but in this case the buggy pushing code is only
affecting the "spring" element and no other elements */
-#if 1
if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
-#else
- if (game.engine_version < VERSION_IDENT(2,2,0,7))
-#endif
{
for (i = 0; i < MAX_PLAYERS; i++)
{
int x = player->jx;
int y = player->jy;
-#if 1
if (player->active && player->is_pushing && player->is_moving &&
IS_MOVING(x, y) &&
(game.engine_version < VERSION_IDENT(2,2,0,7) ||
Feld[x][y] == EL_SPRING))
-#else
- if (player->active && player->is_pushing && player->is_moving &&
- IS_MOVING(x, y))
-#endif
{
ContinueMoving(x, y);
/* 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
- for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
+ SCAN_PLAYFIELD(x, y)
{
- Changed[x][y] = CE_BITMASK_DEFAULT;
- ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
+ ChangeCount[x][y] = 0;
+ ChangeEvent[x][y] = -1;
-#if USE_NEW_BLOCK_STYLE
/* this must be handled before main playfield loop */
if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
{
if (MovDelay[x][y] <= 0)
RemoveField(x, y);
}
+
+#if USE_NEW_SNAP_DELAY
+ if (Feld[x][y] == EL_ELEMENT_SNAPPING)
+ {
+ MovDelay[x][y]--;
+ if (MovDelay[x][y] <= 0)
+ {
+ RemoveField(x, y);
+ TEST_DrawLevelField(x, y);
+
+ TestIfElementTouchesCustomElement(x, y); /* for empty space */
+ }
+ }
#endif
#if DEBUG
WasJustFalling[x][y]--;
if (CheckCollision[x][y] > 0)
CheckCollision[x][y]--;
+ if (CheckImpact[x][y] > 0)
+ CheckImpact[x][y]--;
GfxFrame[x][y]++;
-#if 1
/* reset finished pushing action (not done in ContinueMoving() to allow
- continous pushing animation for elements with zero push delay) */
+ continuous pushing animation for elements with zero push delay) */
if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
{
ResetGfxAnimation(x, y);
- DrawLevelField(x, y);
+ TEST_DrawLevelField(x, y);
}
-#endif
#if DEBUG
if (IS_BLOCKED(x, y))
#endif
}
- for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
+#if 0
+ debug_print_timestamp(0, "- time for pre-main loop:");
+#endif
+
+#if 0 // -------------------- !!! TEST ONLY !!! --------------------
+ SCAN_PLAYFIELD(x, y)
{
element = Feld[x][y];
-#if 1
graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
-#else
- graphic = el2img(element);
-#endif
-#if 0
- if (element == -1)
+#if 1
{
- printf("::: %d,%d: %d [%d]\n", x, y, element, FrameCounter);
-
- element = graphic = 0;
+#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 (graphic_info[graphic].anim_global_sync)
- GfxFrame[x][y] = FrameCounter;
-
+#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]);
+
+ ResetGfxFrame(x, y, TRUE);
+
+ if (ANIM_MODE(graphic) == ANIM_RANDOM &&
+ IS_NEXT_FRAME(GfxFrame[x][y], graphic))
+ ResetRandomAnimationValue(x, y);
+
+ SetRandomAnimationValue(x, y);
+
+ PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
if (IS_INACTIVE(element))
{
continue;
}
-#if 1
/* this may take place after moving, so 'element' may have changed */
-#if 0
- if (IS_CHANGING(x, y))
-#else
if (IS_CHANGING(x, y) &&
(game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
-#endif
{
-#if 0
- ChangeElement(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] :
- element_info[element].event_page_nr[CE_DELAY]);
+ int page = element_info[element].event_page_nr[CE_DELAY];
+
+#if 1
+ HandleElementChange(x, y, page);
#else
- ChangeElement(x, y, element_info[element].event_page_nr[CE_DELAY]);
+ if (CAN_CHANGE(element))
+ HandleElementChange(x, y, page);
+
+ if (HAS_ACTION(element))
+ ExecuteCustomElementAction(x, y, element, page);
#endif
element = Feld[x][y];
graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
}
-#endif
+
+#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)
+ TEST_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);
-#if 1
element = Feld[x][y];
graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
-#if 0
- if (element == EL_MOLE)
- printf("::: %d, %d, %d [%d]\n",
- IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y],
- GfxAction[x][y]);
-#endif
-#if 0
- if (element == EL_YAMYAM)
- printf("::: %d, %d, %d\n",
- IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y]);
-#endif
-#endif
if (IS_ANIMATED(graphic) &&
!IS_MOVING(x, y) &&
!Stop[x][y])
- {
DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
-#if 0
- if (element == EL_BUG)
- printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
-#endif
-
-#if 0
- if (element == EL_MOLE)
- printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
-#endif
- }
-
if (IS_GEM(element) || element == EL_SP_INFOTRON)
- EdelsteinFunkeln(x, y);
+ TEST_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 ||
ContinueMoving(x, y);
else if (IS_ACTIVE_BOMB(element))
CheckDynamite(x, y);
-#if 0
- else if (element == EL_EXPLOSION && !game.explosions_delayed)
- Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
-#endif
else if (element == EL_AMOEBA_GROWING)
AmoebeWaechst(x, y);
else if (element == EL_AMOEBA_SHRINKING)
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_VERTICAL ||
- element == EL_EXPANDABLE_WALL_ANY)
+ 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);
-#if 0
- else if (IS_AUTO_CHANGING(element))
- ChangeElement(x, y);
-#endif
else if (element == EL_EXPLOSION)
; /* drawing of correct explosion animation is handled separately */
+ else if (element == EL_ELEMENT_SNAPPING ||
+ element == EL_DIAGONAL_SHRINKING ||
+ element == EL_DIAGONAL_GROWING)
+ {
+ graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
+
+ DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
+ }
else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
-#if 0
- /* this may take place after moving, so 'element' may have changed */
- if (IS_AUTO_CHANGING(Feld[x][y]))
- ChangeElement(x, y);
-#endif
+#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 1
if (!(FrameCounter % 8))
-#endif
{
static unsigned long random = 1684108901;
for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
{
-#if 0
- x = (random >> 10) % lev_fieldx;
- y = (random >> 20) % lev_fieldy;
-#else
x = RND(lev_fieldx);
y = RND(lev_fieldy);
-#endif
element = Feld[x][y];
-#if 1
if (!IS_PLAYER(x,y) &&
(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))
{
(IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
Feld[x][y] = EL_AMOEBA_DROP;
}
-#else
- /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
- if (!IS_PLAYER(x,y) &&
- (element == EL_EMPTY ||
- element == EL_SAND ||
- element == EL_QUICKSAND_EMPTY ||
- element == EL_ACID_SPLASH_LEFT ||
- element == EL_ACID_SPLASH_RIGHT))
- {
- if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
- (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
- (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
- (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
- Feld[x][y] = EL_AMOEBA_DROP;
- }
-#endif
random = random * 129 + 1;
}
{
game.explosions_delayed = FALSE;
- for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
+ SCAN_PLAYFIELD(x, y)
{
element = Feld[x][y];
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);
}
if (game.magic_wall_time_left > 0)
{
game.magic_wall_time_left--;
+
if (!game.magic_wall_time_left)
{
- for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
+ SCAN_PLAYFIELD(x, y)
{
element = Feld[x][y];
element == EL_MAGIC_WALL_FULL)
{
Feld[x][y] = EL_MAGIC_WALL_DEAD;
- DrawLevelField(x, y);
+ TEST_DrawLevelField(x, y);
}
else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
element == EL_BD_MAGIC_WALL_FULL)
{
Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
- DrawLevelField(x, y);
+ TEST_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;
+ TEST_DrawLevelField(x, y);
}
}
CloseAllOpenTimegates();
}
+ if (game.lenses_time_left > 0)
+ {
+ game.lenses_time_left--;
+
+ if (game.lenses_time_left == 0)
+ RedrawAllInvisibleElementsForLenses();
+ }
+
+ if (game.magnify_time_left > 0)
+ {
+ game.magnify_time_left--;
+
+ if (game.magnify_time_left == 0)
+ RedrawAllInvisibleElementsForMagnifier();
+ }
+
for (i = 0; i < MAX_PLAYERS; i++)
{
struct PlayerInfo *player = &stored_player[i];
}
}
- if (TimeFrames >= FRAMES_PER_SECOND)
+#if USE_DELAYED_GFX_REDRAW
+ SCAN_PLAYFIELD(x, y)
{
- TimeFrames = 0;
- TapeTime++;
-
- for (i = 0; i < MAX_PLAYERS; i++)
- {
- struct PlayerInfo *player = &stored_player[i];
-
- if (SHIELD_ON(player))
- {
- player->shield_normal_time_left--;
-
- if (player->shield_deadly_time_left > 0)
- player->shield_deadly_time_left--;
- }
- }
-
- if (!level.use_step_counter)
+#if 1
+ if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
+#else
+ if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)) &&
+ GfxRedraw[x][y] != GFX_REDRAW_NONE)
+#endif
{
- TimePlayed++;
+ /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
+ !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
- if (TimeLeft > 0)
- {
- TimeLeft--;
+ if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
+ DrawLevelField(x, y);
- if (TimeLeft <= 10 && setup.time_limit)
- PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
+ if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
+ DrawLevelFieldCrumbled(x, y);
- DrawGameValue_Time(TimeLeft);
+ if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
+ DrawLevelFieldCrumbledNeighbours(x, y);
- if (!TimeLeft && setup.time_limit)
- for (i = 0; i < MAX_PLAYERS; i++)
- KillHero(&stored_player[i]);
- }
- else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
- DrawGameValue_Time(TimePlayed);
+ if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
+ DrawTwinkleOnField(x, y);
}
- if (tape.recording || tape.playing)
- DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
+ GfxRedraw[x][y] = GFX_REDRAW_NONE;
}
+#endif
+
+ CheckLevelTime();
DrawAllPlayers();
PlayAllPlayersSound();
redraw_mask |= REDRAW_FPS;
}
-#if 0
- if (stored_player[0].jx != stored_player[0].last_jx ||
- stored_player[0].jy != stored_player[0].last_jy)
- printf("::: %d, %d, %d, %d, %d\n",
- stored_player[0].MovDir,
- stored_player[0].MovPos,
- stored_player[0].GfxPos,
- stored_player[0].Frame,
- stored_player[0].StepFrame);
-#endif
-
-#if USE_NEW_MOVE_DELAY
AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
-#else
- FrameCounter++;
- TimeFrames++;
-
- for (i = 0; i < MAX_PLAYERS; i++)
- {
- int move_frames =
- MOVE_DELAY_NORMAL_SPEED / stored_player[i].move_delay_value;
-
- stored_player[i].Frame += move_frames;
- if (stored_player[i].MovPos != 0)
- stored_player[i].StepFrame += move_frames;
-
-#if USE_NEW_MOVE_DELAY
- if (stored_player[i].move_delay > 0)
- stored_player[i].move_delay--;
-#endif
-
- if (stored_player[i].drop_delay > 0)
- stored_player[i].drop_delay--;
- }
-#endif
-
-#if 1
if (local_player->show_envelope != 0 && local_player->MovPos == 0)
{
ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
local_player->show_envelope = 0;
}
+
+#if 0
+ debug_print_timestamp(0, "stop main loop profiling ");
+ printf("----------------------------------------------------------\n");
#endif
-#if USE_NEW_RANDOMIZE
/* use random number generator in every frame to make it less predictable */
if (game.engine_version >= VERSION_IDENT(3,1,1,0))
RND(1);
-#endif
}
static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
void ScrollLevel(int dx, int dy)
{
+#if 0
+ /* (directly solved in BlitBitmap() now) */
+ static Bitmap *bitmap_db_field2 = NULL;
int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
int x, y;
+#else
+ int 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 0
+ /* (directly solved in BlitBitmap() now) */
+ 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 0
+ /* !!! 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
+
+#if NEW_TILESIZE
+#if NEW_SCROLL
+ int softscroll_offset = (setup.soft_scrolling ? 2 * TILEX_VAR : 0);
+#else
+ int softscroll_offset = (setup.soft_scrolling ? TILEX_VAR : 0);
+#endif
+#else
+#if NEW_SCROLL
+ int softscroll_offset = (setup.soft_scrolling ? 2 * TILEX : 0);
+#else
+ int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
+#endif
+#endif
+#if NEW_TILESIZE
+ BlitBitmap(drawto_field, drawto_field,
+ FX + TILEX_VAR * (dx == -1) - softscroll_offset,
+ FY + TILEY_VAR * (dy == -1) - softscroll_offset,
+ SXSIZE - TILEX_VAR * (dx != 0) + 2 * softscroll_offset,
+ SYSIZE - TILEY_VAR * (dy != 0) + 2 * softscroll_offset,
+ FX + TILEX_VAR * (dx == 1) - softscroll_offset,
+ FY + TILEY_VAR * (dy == 1) - softscroll_offset);
+#else
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
+#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++)
redraw_mask |= REDRAW_FIELD;
}
-#if 0
-static boolean canEnterSupaplexPort(int x, int y, int dx, int dy)
-{
- int nextx = x + dx, nexty = y + dy;
- int element = Feld[x][y];
-
- if ((dx == -1 &&
- element != EL_SP_PORT_LEFT &&
- element != EL_SP_GRAVITY_PORT_LEFT &&
- element != EL_SP_PORT_HORIZONTAL &&
- element != EL_SP_PORT_ANY) ||
- (dx == +1 &&
- element != EL_SP_PORT_RIGHT &&
- element != EL_SP_GRAVITY_PORT_RIGHT &&
- element != EL_SP_PORT_HORIZONTAL &&
- element != EL_SP_PORT_ANY) ||
- (dy == -1 &&
- element != EL_SP_PORT_UP &&
- element != EL_SP_GRAVITY_PORT_UP &&
- element != EL_SP_PORT_VERTICAL &&
- element != EL_SP_PORT_ANY) ||
- (dy == +1 &&
- element != EL_SP_PORT_DOWN &&
- element != EL_SP_GRAVITY_PORT_DOWN &&
- element != EL_SP_PORT_VERTICAL &&
- element != EL_SP_PORT_ANY) ||
- !IN_LEV_FIELD(nextx, nexty) ||
- !IS_FREE(nextx, nexty))
- return FALSE;
-
- return TRUE;
-}
-#endif
-
static boolean canFallDown(struct PlayerInfo *player)
{
int jx = player->jx, jy = player->jy;
int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
int newx = x + dx;
int newy = y + dy;
-#if 0
- int nextx = newx + dx;
- int nexty = newy + dy;
-#endif
-#if 1
- return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
- IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
-#if 0
- (!IS_SP_PORT(Feld[newx][newy]) || move_dir == MV_UP) &&
-#endif
- (IS_DIGGABLE(Feld[newx][newy]) ||
- IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
- canPassField(newx, newy, move_dir)));
-#else
-#if 1
return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
(IS_DIGGABLE(Feld[newx][newy]) ||
IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
canPassField(newx, newy, move_dir)));
-#else
-#if 1
- return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
- (IS_DIGGABLE_WITH_GRAVITY(Feld[newx][newy]) ||
- IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
- canPassField(newx, newy, move_dir)));
-#else
- return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
- (IS_DIGGABLE(Feld[newx][newy]) ||
- IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
- (IS_PASSABLE_FROM(Feld[newx][newy], opposite_dir) &&
- !CAN_MOVE(Feld[newx][newy]) &&
- IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
- IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
- (level.can_pass_to_walkable || IS_FREE(nextx, nexty)))));
-#endif
-#endif
-#endif
}
-static void CheckGravityMovement(struct PlayerInfo *player)
-{
- if (game.gravity && !player->programmed_action)
- {
-#if 1
- int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
- int move_dir_vertical = player->effective_action & MV_VERTICAL;
-#else
- int move_dir_horizontal = player->action & MV_HORIZONTAL;
- int move_dir_vertical = player->action & MV_VERTICAL;
-#endif
-
-#if 1
- boolean player_is_snapping = player->effective_action & JOY_BUTTON_1;
-#else
- boolean player_is_snapping = player->action & JOY_BUTTON_1;
-#endif
-
- int jx = player->jx, jy = player->jy;
-
- boolean player_is_moving_to_valid_field =
- (!player_is_snapping &&
- (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
- canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
-
-#if 0
- int move_dir =
- (player->last_move_dir & MV_HORIZONTAL ?
- (move_dir_vertical ? move_dir_vertical : move_dir_horizontal) :
- (move_dir_horizontal ? move_dir_horizontal : move_dir_vertical));
-#endif
-
-#if 0
- int opposite_dir = MV_DIR_OPPOSITE(move_dir);
- int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
- int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
- int new_jx = jx + dx, new_jy = jy + dy;
- int nextx = new_jx + dx, nexty = new_jy + dy;
-#endif
-
-#if 1
-
-#if 1
- boolean player_can_fall_down = canFallDown(player);
-#else
- boolean player_can_fall_down =
- (IN_LEV_FIELD(jx, jy + 1) &&
- (IS_FREE(jx, jy + 1) ||
- (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)));
-#endif
-
-#else
- boolean player_can_fall_down =
- (IN_LEV_FIELD(jx, jy + 1) &&
- (IS_FREE(jx, jy + 1)));
-#endif
-
-#if 0
- boolean player_is_moving_to_valid_field =
- (
-#if 1
- !player_is_snapping &&
-#endif
-
-#if 1
- IN_LEV_FIELD(new_jx, new_jy) &&
- (IS_DIGGABLE(Feld[new_jx][new_jy]) ||
- (IS_SP_PORT(Feld[new_jx][new_jy]) &&
- element_info[Feld[new_jx][new_jy]].access_direction & opposite_dir &&
- IN_LEV_FIELD(nextx, nexty) &&
- element_info[Feld[nextx][nexty]].access_direction & move_dir))
-#else
- IN_LEV_FIELD(new_jx, new_jy) &&
- (Feld[new_jx][new_jy] == EL_SP_BASE ||
- Feld[new_jx][new_jy] == EL_SAND ||
- (IS_SP_PORT(Feld[new_jx][new_jy]) &&
- canEnterSupaplexPort(new_jx, new_jy, dx, dy)))
- /* !!! extend EL_SAND to anything diggable !!! */
-#endif
- );
-#endif
-
-#if 0
- boolean player_is_standing_on_valid_field =
- (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
- (IS_WALKABLE(Feld[jx][jy]) && !ACCESS_FROM(Feld[jx][jy], MV_DOWN)));
-#endif
-
-#if 0
- printf("::: checking gravity NOW [%d, %d, %d] [%d] [%d / %d] ...\n",
- player_can_fall_down,
- player_is_standing_on_valid_field,
- player_is_moving_to_valid_field,
- (player_is_moving_to_valid_field ? Feld[new_jx][new_jy] : -1),
- player->effective_action,
- player->can_fall_into_acid);
+static void CheckGravityMovement(struct PlayerInfo *player)
+{
+#if USE_PLAYER_GRAVITY
+ if (player->gravity && !player->programmed_action)
+#else
+ if (game.gravity && !player->programmed_action)
#endif
+ {
+ int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
+ int move_dir_vertical = player->effective_action & MV_VERTICAL;
+ boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
+ int jx = player->jx, jy = player->jy;
+ boolean player_is_moving_to_valid_field =
+ (!player_is_snapping &&
+ (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
+ canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
+ boolean player_can_fall_down = canFallDown(player);
if (player_can_fall_down &&
-#if 0
- !player_is_standing_on_valid_field &&
-#endif
!player_is_moving_to_valid_field)
- {
-#if 0
- printf("::: setting programmed_action to MV_DOWN [%d,%d - %d] ...\n",
- jx, jy, FrameCounter);
-#endif
-
player->programmed_action = MV_DOWN;
- }
}
}
static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
{
-#if 1
return CheckGravityMovement(player);
-#endif
+#if USE_PLAYER_GRAVITY
+ if (player->gravity && !player->programmed_action)
+#else
if (game.gravity && !player->programmed_action)
+#endif
{
int jx = player->jx, jy = player->jy;
boolean field_under_player_is_free =
boolean MovePlayerOneStep(struct PlayerInfo *player,
int dx, int dy, int real_dx, int real_dy)
{
-#if 0
- static int trigger_sides[4][2] =
- {
- /* enter side leave side */
- { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
- { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
- { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
- { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
- };
- int move_direction = (dx == -1 ? MV_LEFT :
- dx == +1 ? MV_RIGHT :
- dy == -1 ? MV_UP :
- dy == +1 ? MV_DOWN : MV_NO_MOVING);
- int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
- int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
-#endif
int jx = player->jx, jy = player->jy;
int new_jx = jx + dx, new_jy = jy + dy;
+#if !USE_FIXED_DONT_RUN_INTO
int element;
+#endif
int can_move;
+ boolean player_can_move = !player->cannot_move;
if (!player->active || (!dx && !dy))
- return MF_NO_ACTION;
+ return MP_NO_ACTION;
player->MovDir = (dx < 0 ? MV_LEFT :
dx > 0 ? MV_RIGHT :
dy < 0 ? MV_UP :
- dy > 0 ? MV_DOWN : MV_NO_MOVING);
+ dy > 0 ? MV_DOWN : MV_NONE);
if (!IN_LEV_FIELD(new_jx, new_jy))
- return MF_NO_ACTION;
+ return MP_NO_ACTION;
- if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
- return MF_NO_ACTION;
+ if (!player_can_move)
+ {
+ if (player->MovPos == 0)
+ {
+ player->is_moving = FALSE;
+ player->is_digging = FALSE;
+ player->is_collecting = FALSE;
+ player->is_snapping = FALSE;
+ player->is_pushing = FALSE;
+ }
+ }
-#if 0
- element = MovingOrBlocked2Element(new_jx, new_jy);
+#if 1
+ if (!options.network && game.centered_player_nr == -1 &&
+ !AllPlayersInSight(player, new_jx, new_jy))
+ return MP_NO_ACTION;
#else
- element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
+ if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
+ return MP_NO_ACTION;
#endif
- if (DONT_RUN_INTO(element))
+#if !USE_FIXED_DONT_RUN_INTO
+ element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
+
+ /* (moved to DigField()) */
+ if (player_can_move && DONT_RUN_INTO(element))
{
if (element == EL_ACID && dx == 0 && dy == 1)
{
InitMovingField(jx, jy, MV_DOWN);
Store[jx][jy] = EL_ACID;
ContinueMoving(jx, jy);
- BuryHero(player);
+ BuryPlayer(player);
}
else
- TestIfHeroRunsIntoBadThing(jx, jy, player->MovDir);
+ TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
- return MF_MOVING;
+ return MP_MOVING;
}
+#endif
can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
- if (can_move != MF_MOVING)
+ if (can_move != MP_MOVING)
return can_move;
/* check if DigField() has caused relocation of the player */
if (player->jx != jx || player->jy != jy)
- return MF_NO_ACTION;
+ return MP_NO_ACTION; /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
StorePlayer[jx][jy] = 0;
player->last_jx = jx;
player->jy = new_jy;
StorePlayer[new_jx][new_jy] = player->element_nr;
+ if (player->move_delay_value_next != -1)
+ {
+ player->move_delay_value = player->move_delay_value_next;
+ player->move_delay_value_next = -1;
+ }
+
player->MovPos =
(dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
player->step_counter++;
-#if 0
- player->drop_delay = 0;
-#endif
-
PlayerVisit[jx][jy] = FrameCounter;
- ScrollPlayer(player, SCROLL_INIT);
-
-#if 0
- if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
- {
- CheckTriggeredElementChangeBySide(jx, jy, Feld[jx][jy], CE_OTHER_GETS_LEFT,
- leave_side);
- CheckElementChangeBySide(jx,jy, Feld[jx][jy],CE_LEFT_BY_PLAYER,leave_side);
- }
+#if USE_UFAST_PLAYER_EXIT_BUGFIX
+ player->is_moving = TRUE;
+#endif
- if (IS_CUSTOM_ELEMENT(Feld[new_jx][new_jy]))
- {
- CheckTriggeredElementChangeBySide(new_jx, new_jy, Feld[new_jx][new_jy],
- CE_OTHER_GETS_ENTERED, enter_side);
- CheckElementChangeBySide(new_jx, new_jy, Feld[new_jx][new_jy],
- CE_ENTERED_BY_PLAYER, enter_side);
- }
+#if 1
+ /* should better be called in MovePlayer(), but this breaks some tapes */
+ ScrollPlayer(player, SCROLL_INIT);
#endif
- return MF_MOVING;
+ return MP_MOVING;
}
boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
{
int jx = player->jx, jy = player->jy;
int old_jx = jx, old_jy = jy;
- int moved = MF_NO_ACTION;
+ int moved = MP_NO_ACTION;
-#if 1
if (!player->active)
return FALSE;
return FALSE;
}
-#else
- if (!player->active || (!dx && !dy))
- return FALSE;
-#endif
-
-#if 0
- if (!FrameReached(&player->move_delay, player->move_delay_value) &&
- !tape.playing)
- return FALSE;
-#else
-
-#if 1
-
-#if 0
- printf("::: %d <= %d < %d ?\n", player->move_delay, FrameCounter,
- player->move_delay + player->move_delay_value);
-#endif
-#if USE_NEW_MOVE_DELAY
if (player->move_delay > 0)
-#else
- if (!FrameReached(&player->move_delay, player->move_delay_value))
-#endif
- {
-#if 0
- printf("::: can NOT move\n");
-#endif
-
- return FALSE;
- }
-#else
- if (!FrameReached(&player->move_delay, player->move_delay_value) &&
- !(tape.playing && tape.file_version < FILE_VERSION_2_0))
return FALSE;
-#endif
-
-#endif
-#if 0
- printf("::: COULD move now\n");
-#endif
-
-#if USE_NEW_MOVE_DELAY
player->move_delay = -1; /* set to "uninitialized" value */
-#endif
/* store if player is automatically moved to next field */
- player->is_auto_moving = (player->programmed_action != MV_NO_MOVING);
+ player->is_auto_moving = (player->programmed_action != MV_NONE);
/* remove the last programmed player action */
player->programmed_action = 0;
ScrollPlayer(player, SCROLL_GO_ON);
ScrollScreen(NULL, SCROLL_GO_ON);
-#if USE_NEW_MOVE_DELAY
AdvanceFrameAndPlayerCounters(player->index_nr);
-#else
- FrameCounter++;
-#endif
DrawAllPlayers();
BackToFront();
player->move_delay_value = original_move_delay_value;
}
+ player->is_active = FALSE;
+
if (player->last_move_dir & MV_HORIZONTAL)
{
if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
}
+#if USE_FIXED_BORDER_RUNNING_GFX
+ if (!moved && !player->is_active)
+ {
+ player->is_moving = FALSE;
+ player->is_digging = FALSE;
+ player->is_collecting = FALSE;
+ player->is_snapping = FALSE;
+ player->is_pushing = FALSE;
+ }
+#endif
+
jx = player->jx;
jy = player->jy;
- if (moved & MF_MOVING && !ScreenMovPos &&
+#if 1
+ if (moved & MP_MOVING && !ScreenMovPos &&
+ (player->index_nr == game.centered_player_nr ||
+ game.centered_player_nr == -1))
+#else
+ if (moved & MP_MOVING && !ScreenMovPos &&
(player == local_player || !options.network))
+#endif
{
int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
- int offset = (setup.scroll_delay ? 3 : 0);
+ int offset = game.scroll_delay_value;
if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
{
if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
{
+#if 1
+ if (!options.network && game.centered_player_nr == -1 &&
+ !AllPlayersInVisibleScreen())
+ {
+ scroll_x = old_scroll_x;
+ scroll_y = old_scroll_y;
+ }
+ else
+#else
if (!options.network && !AllPlayersInVisibleScreen())
{
scroll_x = old_scroll_x;
scroll_y = old_scroll_y;
}
else
+#endif
{
ScrollScreen(player, SCROLL_INIT);
ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
}
}
-#if 0
-#if 1
- InitPlayerGfxAnimation(player, ACTION_DEFAULT);
-#else
- if (!(moved & MF_MOVING) && !player->is_pushing)
- player->Frame = 0;
-#endif
-#endif
-
player->StepFrame = 0;
- if (moved & MF_MOVING)
+ if (moved & MP_MOVING)
{
-#if 0
- printf("::: REALLY moves now\n");
-#endif
-
if (old_jx != jx && old_jy == jy)
player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
else if (old_jx == jx && old_jy != jy)
player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
- DrawLevelField(jx, jy); /* for "crumbled sand" */
+ TEST_DrawLevelField(jx, jy); /* for "crumbled sand" */
player->last_move_dir = player->MovDir;
player->is_moving = TRUE;
-#if 1
player->is_snapping = FALSE;
-#endif
-
-#if 1
player->is_switching = FALSE;
-#endif
-
player->is_dropping = FALSE;
-
+ player->is_dropping_pressed = FALSE;
+ player->drop_pressed_delay = 0;
#if 0
- /* !!! ENABLE THIS FOR OLD VERSIONS !!! */
-
-#if 1
- if (game.engine_version < VERSION_IDENT(3,1,0,0))
-#endif
- {
- int move_direction = player->MovDir;
-#if 1
- int enter_side = MV_DIR_OPPOSITE(move_direction);
- int leave_side = move_direction;
-#else
- static int trigger_sides[4][2] =
- {
- /* enter side leave side */
- { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
- { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
- { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
- { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
- };
- int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
- int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
-#endif
- int old_element = Feld[old_jx][old_jy];
- int new_element = Feld[jx][jy];
-
-#if 1
- /* !!! TEST ONLY !!! */
- if (IS_CUSTOM_ELEMENT(old_element))
- CheckElementChangeByPlayer(old_jx, old_jy, old_element,
- CE_LEFT_BY_PLAYER,
- player->index_bit, leave_side);
-
- CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
- CE_OTHER_GETS_LEFT,
- player->index_bit, leave_side);
-
- if (IS_CUSTOM_ELEMENT(new_element))
- CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
- player->index_bit, enter_side);
-
- CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
- CE_OTHER_GETS_ENTERED,
- player->index_bit, enter_side);
-#endif
-
- }
+ /* should better be called here than above, but this breaks some tapes */
+ ScrollPlayer(player, SCROLL_INIT);
#endif
-
-
}
else
{
CheckGravityMovementWhenNotMoving(player);
- /*
- player->last_move_dir = MV_NO_MOVING;
- */
player->is_moving = FALSE;
-#if USE_NEW_MOVE_STYLE
- /* player is ALLOWED to move, but CANNOT move (something blocks his way) */
- /* ensure that the player is also allowed to move in the next frame */
- /* (currently, the player is forced to wait eight frames before he can try
- again!!!) */
+ /* at this point, the player is allowed to move, but cannot move right now
+ (e.g. because of something blocking the way) -- ensure that the player
+ is also allowed to move in the next frame (in old versions before 3.1.1,
+ the player was forced to wait again for eight frames before next try) */
if (game.engine_version >= VERSION_IDENT(3,1,1,0))
player->move_delay = 0; /* allow direct movement in the next frame */
-#endif
}
-#if USE_NEW_MOVE_DELAY
if (player->move_delay == -1) /* not yet initialized by DigField() */
player->move_delay = player->move_delay_value;
-#endif
if (game.engine_version < VERSION_IDENT(3,0,7,0))
{
- TestIfHeroTouchesBadThing(jx, jy);
+ TestIfPlayerTouchesBadThing(jx, jy);
TestIfPlayerTouchesCustomElement(jx, jy);
}
if (!player->active)
- RemoveHero(player);
+ RemovePlayer(player);
return moved;
}
int last_jx = player->last_jx, last_jy = player->last_jy;
int move_stepsize = TILEX / player->move_delay_value;
- if (!player->active || !player->MovPos)
+#if USE_NEW_PLAYER_SPEED
+ if (!player->active)
+ return;
+
+ if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
+ return;
+#else
+ if (!player->active || player->MovPos == 0)
return;
+#endif
if (mode == SCROLL_INIT)
{
player->actual_frame_counter = FrameCounter;
player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
-#if 0
- printf("::: %06d: %d,%d: %d (%d) [%d]\n",
- FrameCounter,
- last_jx, last_jy, Feld[last_jx][last_jy], EL_EXPLOSION,
- player->block_delay);
-#endif
+ if ((player->block_last_field || player->block_delay_adjustment > 0) &&
+ Feld[last_jx][last_jy] == EL_EMPTY)
+ {
+ int last_field_block_delay = 0; /* start with no blocking at all */
+ int block_delay_adjustment = player->block_delay_adjustment;
-#if USE_NEW_BLOCK_STYLE
+ /* if player blocks last field, add delay for exactly one move */
+ if (player->block_last_field)
+ {
+ last_field_block_delay += player->move_delay_value;
-#if 0
- if (player->block_delay <= 0)
- printf("::: ALERT! block_delay == %d\n", player->block_delay);
+ /* when blocking enabled, prevent moving up despite gravity */
+#if USE_PLAYER_GRAVITY
+ if (player->gravity && player->MovDir == MV_UP)
+ block_delay_adjustment = -1;
+#else
+ if (game.gravity && player->MovDir == MV_UP)
+ block_delay_adjustment = -1;
#endif
+ }
+
+ /* add block delay adjustment (also possible when not blocking) */
+ last_field_block_delay += block_delay_adjustment;
- if (player->block_delay > 0 &&
- Feld[last_jx][last_jy] == EL_EMPTY)
- {
Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
- MovDelay[last_jx][last_jy] = player->block_delay + 1;
+ MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
}
-#else
-#if USE_NEW_MOVE_STYLE
- if ((game.engine_version < VERSION_IDENT(3,1,1,0) ||
- player->block_last_field) &&
- Feld[last_jx][last_jy] == EL_EMPTY)
- Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
-#else
- if (Feld[last_jx][last_jy] == EL_EMPTY)
- Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
-#endif
-#endif
-
-#if 0
- DrawPlayer(player);
-#endif
+#if USE_NEW_PLAYER_SPEED
+ if (player->MovPos != 0) /* player has not yet reached destination */
+ return;
+#else
return;
+#endif
}
else if (!FrameReached(&player->actual_frame_counter, 1))
return;
- player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
- player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
+#if USE_NEW_PLAYER_SPEED
+ if (player->MovPos != 0)
+ {
+ player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
+ player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
-#if USE_NEW_BLOCK_STYLE
-#else
- if (!player->block_last_field &&
- Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
-#if 1
- RemoveField(last_jx, last_jy);
+ /* before DrawPlayer() to draw correct player graphic for this case */
+ if (player->MovPos == 0)
+ CheckGravityMovement(player);
+ }
#else
- Feld[last_jx][last_jy] = EL_EMPTY;
-#endif
-#endif
+ player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
+ player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
/* before DrawPlayer() to draw correct player graphic for this case */
if (player->MovPos == 0)
CheckGravityMovement(player);
-
-#if 0
- DrawPlayer(player); /* needed here only to cleanup last field */
#endif
if (player->MovPos == 0) /* player reached destination field */
{
-#if 1
if (player->move_delay_reset_counter > 0)
{
player->move_delay_reset_counter--;
player->move_delay = 0;
}
}
-#else
- if (IS_PASSABLE(Feld[last_jx][last_jy]))
- {
- /* continue with normal speed after quickly moving through gate */
- HALVE_PLAYER_SPEED(player);
-
- /* be able to make the next move without delay */
- player->move_delay = 0;
- }
-#endif
-
-#if USE_NEW_BLOCK_STYLE
-#else
- if (player->block_last_field &&
- Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
-#if 1
- RemoveField(last_jx, last_jy);
-#else
- Feld[last_jx][last_jy] = EL_EMPTY;
-#endif
-#endif
player->last_jx = jx;
player->last_jy = jy;
if (Feld[jx][jy] == EL_EXIT_OPEN ||
+ Feld[jx][jy] == EL_EM_EXIT_OPEN ||
+#if 1
+ Feld[jx][jy] == EL_EM_EXIT_OPENING ||
+#endif
+ Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
+ Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
+#if 1
+ Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
+#endif
Feld[jx][jy] == EL_SP_EXIT_OPEN ||
Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
{
DrawPlayer(player); /* needed here only to cleanup last field */
- RemoveHero(player);
+ RemovePlayer(player);
if (local_player->friends_still_needed == 0 ||
IS_SP_ELEMENT(Feld[jx][jy]))
- player->LevelSolved = player->GameOver = TRUE;
+ PlayerWins(player);
}
-#if 1
- /* !!! ENABLE THIS FOR NEW VERSIONS !!! */
/* this breaks one level: "machine", level 000 */
-#if 0
- if (game.engine_version >= VERSION_IDENT(3,1,0,0))
-#endif
{
int move_direction = player->MovDir;
-#if 1
int enter_side = MV_DIR_OPPOSITE(move_direction);
int leave_side = move_direction;
-#else
- static int trigger_sides[4][2] =
- {
- /* enter side leave side */
- { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
- { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
- { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
- { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
- };
- int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
- int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
-#endif
int old_jx = last_jx;
int old_jy = last_jy;
int old_element = Feld[old_jx][old_jy];
int new_element = Feld[jx][jy];
-#if 1
- /* !!! TEST ONLY !!! */
if (IS_CUSTOM_ELEMENT(old_element))
CheckElementChangeByPlayer(old_jx, old_jy, old_element,
CE_LEFT_BY_PLAYER,
player->index_bit, leave_side);
CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
- CE_OTHER_GETS_LEFT,
+ CE_PLAYER_LEAVES_X,
player->index_bit, leave_side);
if (IS_CUSTOM_ELEMENT(new_element))
player->index_bit, enter_side);
CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
- CE_OTHER_GETS_ENTERED,
+ CE_PLAYER_ENTERS_X,
player->index_bit, enter_side);
-#endif
- }
+#if USE_FIX_CE_ACTION_WITH_PLAYER
+ CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
+ CE_MOVE_OF_X, move_direction);
+#else
+ CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
+ CE_MOVE_OF_X, move_direction);
#endif
+ }
if (game.engine_version >= VERSION_IDENT(3,0,7,0))
{
- TestIfHeroTouchesBadThing(jx, jy);
+ TestIfPlayerTouchesBadThing(jx, jy);
TestIfPlayerTouchesCustomElement(jx, jy);
-#if 1
-#if 1
+
/* needed because pushed element has not yet reached its destination,
so it would trigger a change event at its previous field location */
if (!player->is_pushing)
-#endif
TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
-#endif
if (!player->active)
- RemoveHero(player);
+ RemovePlayer(player);
}
- if (level.use_step_counter)
+ if (!local_player->LevelSolved && level.use_step_counter)
{
int i;
TimeLeft--;
if (TimeLeft <= 10 && setup.time_limit)
- PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
+ PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
+#if 1
+ game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
+
+ DisplayGameControlValues();
+#else
DrawGameValue_Time(TimeLeft);
+#endif
if (!TimeLeft && setup.time_limit)
for (i = 0; i < MAX_PLAYERS; i++)
- KillHero(&stored_player[i]);
+ KillPlayer(&stored_player[i]);
+ }
+#if 1
+ else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
+ {
+ game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
+
+ DisplayGameControlValues();
}
+#else
else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
DrawGameValue_Time(TimePlayed);
+#endif
}
if (tape.single_step && tape.recording && !tape.pausing &&
redraw_mask |= REDRAW_FIELD;
}
else
- ScreenMovDir = MV_NO_MOVING;
+ ScreenMovDir = MV_NONE;
}
void TestIfPlayerTouchesCustomElement(int x, int y)
if (!IN_LEV_FIELD(xx, yy))
continue;
- if (IS_PLAYER(x, y))
+ if (IS_PLAYER(x, y)) /* player found at center element */
{
struct PlayerInfo *player = PLAYERINFO(x, y);
else
continue; /* center and border element do not touch */
-#if 1
- /* !!! TEST ONLY !!! */
CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
player->index_bit, border_side);
CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
- CE_OTHER_GETS_TOUCHED,
+ CE_PLAYER_TOUCHES_X,
player->index_bit, border_side);
-#else
- CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
- CE_OTHER_GETS_TOUCHED,
- player->index_bit, border_side);
- CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
- player->index_bit, border_side);
+
+#if USE_FIX_CE_ACTION_WITH_PLAYER
+ {
+ /* use player element that is initially defined in the level playfield,
+ not the player element that corresponds to the runtime player number
+ (example: a level that contains EL_PLAYER_3 as the only player would
+ incorrectly give EL_PLAYER_1 for "player->element_nr") */
+ int player_element = PLAYERINFO(x, y)->initial_element;
+
+ CheckElementChangeBySide(xx, yy, border_element, player_element,
+ CE_TOUCHING_X, border_side);
+ }
#endif
}
- else if (IS_PLAYER(xx, yy))
+ else if (IS_PLAYER(xx, yy)) /* player found at border element */
{
struct PlayerInfo *player = PLAYERINFO(xx, yy);
continue; /* center and border element do not touch */
}
-#if 1
- /* !!! TEST ONLY !!! */
CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
player->index_bit, center_side);
CheckTriggeredElementChangeByPlayer(x, y, center_element,
- CE_OTHER_GETS_TOUCHED,
- player->index_bit, center_side);
-#else
- CheckTriggeredElementChangeByPlayer(x, y, center_element,
- CE_OTHER_GETS_TOUCHED,
+ CE_PLAYER_TOUCHES_X,
player->index_bit, center_side);
- CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
- player->index_bit, center_side);
+
+#if USE_FIX_CE_ACTION_WITH_PLAYER
+ {
+ /* use player element that is initially defined in the level playfield,
+ not the player element that corresponds to the runtime player number
+ (example: a level that contains EL_PLAYER_3 as the only player would
+ incorrectly give EL_PLAYER_1 for "player->element_nr") */
+ int player_element = PLAYERINFO(xx, yy)->initial_element;
+
+ CheckElementChangeBySide(x, y, center_element, player_element,
+ CE_TOUCHING_X, center_side);
+ }
#endif
break;
}
}
+#if USE_ELEMENT_TOUCHING_BUGFIX
+
void TestIfElementTouchesCustomElement(int x, int y)
{
static int xy[4][2] =
MV_LEFT | MV_RIGHT
};
boolean change_center_element = FALSE;
- int center_element_change_page = 0;
int center_element = Feld[x][y]; /* should always be non-moving! */
- int border_trigger_element = EL_UNDEFINED;
- int i, j;
+ int border_element_old[NUM_DIRECTIONS];
+ int i;
for (i = 0; i < NUM_DIRECTIONS; i++)
{
int xx = x + xy[i][0];
int yy = y + xy[i][1];
- int center_side = trigger_sides[i][0];
- int border_side = trigger_sides[i][1];
int border_element;
+ border_element_old[i] = -1;
+
if (!IN_LEV_FIELD(xx, yy))
continue;
else
continue; /* center and border element do not touch */
- /* check for change of center element (but change it only once) */
- if (IS_CUSTOM_ELEMENT(center_element) &&
- HAS_ANY_CHANGE_EVENT(center_element, CE_OTHER_IS_TOUCHING) &&
- !change_center_element)
- {
- for (j = 0; j < element_info[center_element].num_change_pages; j++)
- {
- struct ElementChangeInfo *change =
- &element_info[center_element].change_page[j];
+ border_element_old[i] = border_element;
+ }
- if (change->can_change &&
- change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
- change->trigger_side & border_side &&
-#if 1
- IS_EQUAL_OR_IN_GROUP(border_element, change->trigger_element)
-#else
- change->trigger_element == border_element
-#endif
- )
- {
- change_center_element = TRUE;
- center_element_change_page = j;
- border_trigger_element = border_element;
+ for (i = 0; i < NUM_DIRECTIONS; i++)
+ {
+ int xx = x + xy[i][0];
+ int yy = y + xy[i][1];
+ int center_side = trigger_sides[i][0];
+ int border_element = border_element_old[i];
- break;
- }
- }
- }
+ if (border_element == -1)
+ continue;
/* check for change of border element */
- if (IS_CUSTOM_ELEMENT(border_element) &&
- HAS_ANY_CHANGE_EVENT(border_element, CE_OTHER_IS_TOUCHING))
- {
- for (j = 0; j < element_info[border_element].num_change_pages; j++)
- {
- struct ElementChangeInfo *change =
- &element_info[border_element].change_page[j];
+ CheckElementChangeBySide(xx, yy, border_element, center_element,
+ CE_TOUCHING_X, center_side);
- if (change->can_change &&
- change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
- change->trigger_side & center_side &&
-#if 1
- IS_EQUAL_OR_IN_GROUP(center_element, change->trigger_element)
-#else
- change->trigger_element == center_element
-#endif
- )
- {
-#if 0
- printf("::: border_element %d, %d\n", x, y);
-#endif
+ /* (center element cannot be player, so we dont have to check this here) */
+ }
- CheckElementChangeByPage(xx, yy, border_element, center_element,
- CE_OTHER_IS_TOUCHING, j);
- break;
- }
- }
+ for (i = 0; i < NUM_DIRECTIONS; i++)
+ {
+ int xx = x + xy[i][0];
+ int yy = y + xy[i][1];
+ int border_side = trigger_sides[i][1];
+ int border_element = border_element_old[i];
+
+ if (border_element == -1)
+ continue;
+
+ /* check for change of center element (but change it only once) */
+ if (!change_center_element)
+ change_center_element =
+ CheckElementChangeBySide(x, y, center_element, border_element,
+ CE_TOUCHING_X, border_side);
+
+#if USE_FIX_CE_ACTION_WITH_PLAYER
+ if (IS_PLAYER(xx, yy))
+ {
+ /* use player element that is initially defined in the level playfield,
+ not the player element that corresponds to the runtime player number
+ (example: a level that contains EL_PLAYER_3 as the only player would
+ incorrectly give EL_PLAYER_1 for "player->element_nr") */
+ int player_element = PLAYERINFO(xx, yy)->initial_element;
+
+ CheckElementChangeBySide(x, y, center_element, player_element,
+ CE_TOUCHING_X, border_side);
}
+#endif
}
+}
+
+#else
- if (change_center_element)
+void TestIfElementTouchesCustomElement_OLD(int x, int y)
+{
+ static int xy[4][2] =
{
-#if 0
- printf("::: center_element %d, %d\n", x, y);
-#endif
+ { 0, -1 },
+ { -1, 0 },
+ { +1, 0 },
+ { 0, +1 }
+ };
+ static int trigger_sides[4][2] =
+ {
+ /* center side border side */
+ { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
+ { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
+ { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
+ { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
+ };
+ static int touch_dir[4] =
+ {
+ MV_LEFT | MV_RIGHT,
+ MV_UP | MV_DOWN,
+ MV_UP | MV_DOWN,
+ MV_LEFT | MV_RIGHT
+ };
+ boolean change_center_element = FALSE;
+ int center_element = Feld[x][y]; /* should always be non-moving! */
+ int i;
+
+ for (i = 0; i < NUM_DIRECTIONS; i++)
+ {
+ int xx = x + xy[i][0];
+ int yy = y + xy[i][1];
+ int center_side = trigger_sides[i][0];
+ int border_side = trigger_sides[i][1];
+ int border_element;
+
+ if (!IN_LEV_FIELD(xx, yy))
+ continue;
+
+ if (game.engine_version < VERSION_IDENT(3,0,7,0))
+ border_element = Feld[xx][yy]; /* may be moving! */
+ else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
+ border_element = Feld[xx][yy];
+ else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
+ border_element = MovingOrBlocked2Element(xx, yy);
+ else
+ continue; /* center and border element do not touch */
- CheckElementChangeByPage(x, y, center_element, border_trigger_element,
- CE_OTHER_IS_TOUCHING, center_element_change_page);
+ /* check for change of center element (but change it only once) */
+ if (!change_center_element)
+ change_center_element =
+ CheckElementChangeBySide(x, y, center_element, border_element,
+ CE_TOUCHING_X, border_side);
+
+ /* check for change of border element */
+ CheckElementChangeBySide(xx, yy, border_element, center_element,
+ CE_TOUCHING_X, center_side);
}
}
+#endif
+
void TestIfElementHitsCustomElement(int x, int y, int direction)
{
int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
int hitx = x + dx, hity = y + dy;
int hitting_element = Feld[x][y];
int touched_element;
-#if 0
- boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
- !IS_FREE(hitx, hity) &&
- (!IS_MOVING(hitx, hity) ||
- MovDir[hitx][hity] != direction ||
- ABS(MovPos[hitx][hity]) <= TILEY / 2));
-#endif
if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
return;
-#if 0
- if (IN_LEV_FIELD(hitx, hity) && !object_hit)
- return;
-#endif
-
touched_element = (IN_LEV_FIELD(hitx, hity) ?
MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
- CheckElementChangeBySide(x, y, hitting_element, touched_element,
- CE_HITTING_SOMETHING, direction);
-
if (IN_LEV_FIELD(hitx, hity))
{
int opposite_direction = MV_DIR_OPPOSITE(direction);
int hitting_side = direction;
int touched_side = opposite_direction;
-#if 0
- int touched_element = MovingOrBlocked2Element(hitx, hity);
-#endif
-#if 1
boolean object_hit = (!IS_MOVING(hitx, hity) ||
MovDir[hitx][hity] != direction ||
ABS(MovPos[hitx][hity]) <= TILEY / 2);
object_hit = TRUE;
-#endif
if (object_hit)
{
- int i;
+ CheckElementChangeBySide(x, y, hitting_element, touched_element,
+ CE_HITTING_X, touched_side);
+
+ CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
+ CE_HIT_BY_X, hitting_side);
CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
CE_HIT_BY_SOMETHING, opposite_direction);
- if (IS_CUSTOM_ELEMENT(hitting_element) &&
- HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING))
+#if USE_FIX_CE_ACTION_WITH_PLAYER
+ if (IS_PLAYER(hitx, hity))
{
- for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
- {
- struct ElementChangeInfo *change =
- &element_info[hitting_element].change_page[i];
-
- if (change->can_change &&
- change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) &&
- change->trigger_side & touched_side &&
-
-#if 1
- IS_EQUAL_OR_IN_GROUP(touched_element, change->trigger_element)
-#else
- change->trigger_element == touched_element
-#endif
- )
- {
- CheckElementChangeByPage(x, y, hitting_element, touched_element,
- CE_OTHER_IS_HITTING, i);
- break;
- }
- }
+ /* use player element that is initially defined in the level playfield,
+ not the player element that corresponds to the runtime player number
+ (example: a level that contains EL_PLAYER_3 as the only player would
+ incorrectly give EL_PLAYER_1 for "player->element_nr") */
+ int player_element = PLAYERINFO(hitx, hity)->initial_element;
+
+ CheckElementChangeBySide(x, y, hitting_element, player_element,
+ CE_HITTING_X, touched_side);
}
-
- if (IS_CUSTOM_ELEMENT(touched_element) &&
- HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_HIT))
- {
- for (i = 0; i < element_info[touched_element].num_change_pages; i++)
- {
- struct ElementChangeInfo *change =
- &element_info[touched_element].change_page[i];
-
- if (change->can_change &&
- change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) &&
- change->trigger_side & hitting_side &&
-#if 1
- IS_EQUAL_OR_IN_GROUP(hitting_element, change->trigger_element)
-#else
- change->trigger_element == hitting_element
#endif
- )
- {
- CheckElementChangeByPage(hitx, hity, touched_element,
- hitting_element, CE_OTHER_GETS_HIT, i);
- break;
- }
- }
- }
}
}
+
+ /* "hitting something" is also true when hitting the playfield border */
+ CheckElementChangeBySide(x, y, hitting_element, touched_element,
+ CE_HITTING_SOMETHING, direction);
}
#if 0
CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
CE_SMASHED_BY_SOMETHING, opposite_direction);
- if (IS_CUSTOM_ELEMENT(hitting_element) &&
- HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_SMASHING))
- {
- for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
- {
- struct ElementChangeInfo *change =
- &element_info[hitting_element].change_page[i];
-
- if (change->can_change &&
- change->events & CH_EVENT_BIT(CE_OTHER_IS_SMASHING) &&
- change->trigger_side & touched_side &&
-
-#if 1
- IS_EQUAL_OR_IN_GROUP(touched_element, change->trigger_element)
-#else
- change->trigger_element == touched_element
-#endif
- )
- {
- CheckElementChangeByPage(x, y, hitting_element, touched_element,
- CE_OTHER_IS_SMASHING, i);
- break;
- }
- }
- }
-
- if (IS_CUSTOM_ELEMENT(touched_element) &&
- HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_SMASHED))
- {
- for (i = 0; i < element_info[touched_element].num_change_pages; i++)
- {
- struct ElementChangeInfo *change =
- &element_info[touched_element].change_page[i];
+ CheckElementChangeBySide(x, y, hitting_element, touched_element,
+ CE_OTHER_IS_SMASHING, touched_side);
- if (change->can_change &&
- change->events & CH_EVENT_BIT(CE_OTHER_GETS_SMASHED) &&
- change->trigger_side & hitting_side &&
-#if 1
- IS_EQUAL_OR_IN_GROUP(hitting_element, change->trigger_element)
-#else
- change->trigger_element == hitting_element
-#endif
- )
- {
- CheckElementChangeByPage(hitx, hity, touched_element,
- hitting_element, CE_OTHER_GETS_SMASHED,i);
- break;
- }
- }
- }
+ CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
+ CE_OTHER_GETS_SMASHED, hitting_side);
}
}
}
void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
{
int i, kill_x = -1, kill_y = -1;
+
int bad_element = -1;
static int test_xy[4][2] =
{
continue;
test_move_dir =
- (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
+ (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
-#if 0
- test_element = Feld[test_x][test_y];
-#else
test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
-#endif
/* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
2nd case: DONT_TOUCH style bad thing does not move away from good thing
{
struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
-#if 1
if (player->shield_deadly_time_left > 0 &&
!IS_INDESTRUCTIBLE(bad_element))
Bang(kill_x, kill_y);
else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
- KillHero(player);
-#else
- if (player->shield_deadly_time_left > 0)
- Bang(kill_x, kill_y);
- else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
- KillHero(player);
-#endif
+ KillPlayer(player);
}
else
Bang(good_x, good_y);
test_x = bad_x + test_xy[i][0];
test_y = bad_y + test_xy[i][1];
+
if (!IN_LEV_FIELD(test_x, test_y))
continue;
test_move_dir =
- (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
+ (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
test_element = Feld[test_x][test_y];
{
struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
- if (bad_element == EL_ROBOT && player->is_moving)
- continue; /* robot does not kill player if he is moving */
+ if (bad_element == EL_ROBOT && player->is_moving)
+ continue; /* robot does not kill player if he is moving */
+
+ if (game.engine_version >= VERSION_IDENT(3,0,7,0))
+ {
+ if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
+ continue; /* center and border element do not touch */
+ }
+
+ kill_x = test_x;
+ kill_y = test_y;
+
+ break;
+ }
+ else if (test_element == EL_PENGUIN)
+ {
+ kill_x = test_x;
+ kill_y = test_y;
+
+ break;
+ }
+ }
+ }
+
+ if (kill_x != -1 || kill_y != -1)
+ {
+ if (IS_PLAYER(kill_x, kill_y))
+ {
+ struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
+
+ if (player->shield_deadly_time_left > 0 &&
+ !IS_INDESTRUCTIBLE(bad_element))
+ Bang(bad_x, bad_y);
+ else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
+ KillPlayer(player);
+ }
+ else
+ Bang(kill_x, kill_y);
+ }
+}
+
+void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
+{
+ int bad_element = Feld[bad_x][bad_y];
+ int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
+ int dy = (bad_move_dir == MV_UP ? -1 : bad_move_dir == MV_DOWN ? +1 : 0);
+ int test_x = bad_x + dx, test_y = bad_y + dy;
+ int test_move_dir, test_element;
+ int kill_x = -1, kill_y = -1;
+
+ if (!IN_LEV_FIELD(test_x, test_y))
+ return;
- if (game.engine_version >= VERSION_IDENT(3,0,7,0))
- {
- if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
- continue; /* center and border element do not touch */
- }
+ test_move_dir =
+ (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
- kill_x = test_x;
- kill_y = test_y;
- break;
- }
- else if (test_element == EL_PENGUIN)
- {
- kill_x = test_x;
- kill_y = test_y;
- break;
- }
+ test_element = Feld[test_x][test_y];
+
+ if (test_move_dir != bad_move_dir)
+ {
+ /* good thing can be player or penguin that does not move away */
+ if (IS_PLAYER(test_x, test_y))
+ {
+ struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
+
+ /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
+ player as being hit when he is moving towards the bad thing, because
+ the "get hit by" condition would be lost after the player stops) */
+ if (player->MovPos != 0 && player->MovDir == bad_move_dir)
+ return; /* player moves away from bad thing */
+
+ kill_x = test_x;
+ kill_y = test_y;
+ }
+ else if (test_element == EL_PENGUIN)
+ {
+ kill_x = test_x;
+ kill_y = test_y;
}
}
{
struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
-#if 1
if (player->shield_deadly_time_left > 0 &&
!IS_INDESTRUCTIBLE(bad_element))
Bang(bad_x, bad_y);
else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
- KillHero(player);
-#else
- if (player->shield_deadly_time_left > 0)
- Bang(bad_x, bad_y);
- else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
- KillHero(player);
-#endif
+ KillPlayer(player);
}
else
Bang(kill_x, kill_y);
}
}
-void TestIfHeroTouchesBadThing(int x, int y)
+void TestIfPlayerTouchesBadThing(int x, int y)
{
- TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
+ TestIfGoodThingHitsBadThing(x, y, MV_NONE);
}
-void TestIfHeroRunsIntoBadThing(int x, int y, int move_dir)
+void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
{
TestIfGoodThingHitsBadThing(x, y, move_dir);
}
-void TestIfBadThingTouchesHero(int x, int y)
+void TestIfBadThingTouchesPlayer(int x, int y)
{
- TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
+ TestIfBadThingHitsGoodThing(x, y, MV_NONE);
}
-void TestIfBadThingRunsIntoHero(int x, int y, int move_dir)
+void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
{
TestIfBadThingHitsGoodThing(x, y, move_dir);
}
void TestIfFriendTouchesBadThing(int x, int y)
{
- TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
+ TestIfGoodThingHitsBadThing(x, y, MV_NONE);
}
void TestIfBadThingTouchesFriend(int x, int y)
{
- TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
+ TestIfBadThingHitsGoodThing(x, y, MV_NONE);
}
void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
Bang(bad_x, bad_y);
}
-void KillHero(struct PlayerInfo *player)
+void KillPlayer(struct PlayerInfo *player)
{
int jx = player->jx, jy = player->jy;
if (!player->active)
return;
+#if 0
+ printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
+ player->killed, player->active, player->reanimated);
+#endif
+
+ /* 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;
player->shield_normal_time_left = 0;
player->shield_deadly_time_left = 0;
+#if 0
+ printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
+ player->killed, player->active, player->reanimated);
+#endif
+
Bang(jx, jy);
- BuryHero(player);
+
+#if 0
+ printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
+ player->killed, player->active, player->reanimated);
+#endif
+
+#if USE_PLAYER_REANIMATION
+#if 1
+ if (player->reanimated) /* killed player may have been reanimated */
+ player->killed = player->reanimated = FALSE;
+ else
+ BuryPlayer(player);
+#else
+ if (player->killed) /* player may have been reanimated */
+ BuryPlayer(player);
+#endif
+#else
+ BuryPlayer(player);
+#endif
}
-static void KillHeroUnlessEnemyProtected(int x, int y)
+static void KillPlayerUnlessEnemyProtected(int x, int y)
{
if (!PLAYER_ENEMY_PROTECTED(x, y))
- KillHero(PLAYERINFO(x, y));
+ KillPlayer(PLAYERINFO(x, y));
}
-static void KillHeroUnlessExplosionProtected(int x, int y)
+static void KillPlayerUnlessExplosionProtected(int x, int y)
{
if (!PLAYER_EXPLOSION_PROTECTED(x, y))
- KillHero(PLAYERINFO(x, y));
+ KillPlayer(PLAYERINFO(x, y));
}
-void BuryHero(struct PlayerInfo *player)
+void BuryPlayer(struct PlayerInfo *player)
{
int jx = player->jx, jy = player->jy;
if (!player->active)
return;
-#if 1
- PlayLevelSoundElementAction(jx, jy, player->element_nr, ACTION_DYING);
-#else
- PlayLevelSound(jx, jy, SND_CLASS_PLAYER_DYING);
-#endif
+ PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
PlayLevelSound(jx, jy, SND_GAME_LOSING);
player->GameOver = TRUE;
- RemoveHero(player);
+ RemovePlayer(player);
}
-void RemoveHero(struct PlayerInfo *player)
+void RemovePlayer(struct PlayerInfo *player)
{
int jx = player->jx, jy = player->jy;
int i, found = FALSE;
if (!ExplodeField[jx][jy])
StorePlayer[jx][jy] = 0;
+ if (player->is_moving)
+ TEST_DrawLevelField(player->last_jx, player->last_jy);
+
for (i = 0; i < MAX_PLAYERS; i++)
if (stored_player[i].active)
found = TRUE;
ExitY = ZY = jy;
}
+#if USE_NEW_SNAP_DELAY
+static void setFieldForSnapping(int x, int y, int element, int direction)
+{
+ struct ElementInfo *ei = &element_info[element];
+ int direction_bit = MV_DIR_TO_BIT(direction);
+ int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
+ int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
+ IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
+
+ Feld[x][y] = EL_ELEMENT_SNAPPING;
+ MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
+
+ ResetGfxAnimation(x, y);
+
+ GfxElement[x][y] = element;
+ GfxAction[x][y] = action;
+ GfxDir[x][y] = direction;
+ GfxFrame[x][y] = -1;
+}
+#endif
+
/*
=============================================================================
checkDiagonalPushing()
=============================================================================
*/
-int DigField(struct PlayerInfo *player,
- int oldx, int oldy, int x, int y,
- int real_dx, int real_dy, int mode)
+static int DigField(struct PlayerInfo *player,
+ int oldx, int oldy, int x, int y,
+ int real_dx, int real_dy, int mode)
{
-#if 0
- boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
-#endif
boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
boolean player_was_pushing = player->is_pushing;
+ boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
+ boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
int jx = oldx, jy = oldy;
int dx = x - jx, dy = y - jy;
int nextx = x + dx, nexty = y + dy;
- int move_direction = (dx == -1 ? MV_LEFT :
+ int move_direction = (dx == -1 ? MV_LEFT :
dx == +1 ? MV_RIGHT :
- dy == -1 ? MV_UP :
- dy == +1 ? MV_DOWN : MV_NO_MOVING);
+ dy == -1 ? MV_UP :
+ dy == +1 ? MV_DOWN : MV_NONE);
int opposite_direction = MV_DIR_OPPOSITE(move_direction);
-#if 1
int dig_side = MV_DIR_OPPOSITE(move_direction);
-#else
- static int trigger_sides[4] =
- {
- CH_SIDE_RIGHT, /* moving left */
- CH_SIDE_LEFT, /* moving right */
- CH_SIDE_BOTTOM, /* moving up */
- CH_SIDE_TOP, /* moving down */
- };
- int dig_side = trigger_sides[MV_DIR_BIT(move_direction)];
-#endif
int old_element = Feld[jx][jy];
+#if USE_FIXED_DONT_RUN_INTO
+ int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
+#else
int element;
+#endif
+ int collect_count;
if (is_player) /* function can also be called by EL_PENGUIN */
{
if (mode == DF_NO_PUSH) /* player just stopped pushing */
{
player->is_switching = FALSE;
-#if USE_NEW_PUSH_DELAY
player->push_delay = -1;
-#else
- player->push_delay = 0;
-#endif
- return MF_NO_ACTION;
+ return MP_NO_ACTION;
}
}
+#if !USE_FIXED_DONT_RUN_INTO
if (IS_MOVING(x, y) || IS_PLAYER(x, y))
- return MF_NO_ACTION;
-
-#if 0
-
-#if 0
- if (IS_TUBE(Feld[jx][jy]) || IS_TUBE(Back[jx][jy]))
-#else
- if (IS_TUBE(Feld[jx][jy]) ||
- (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0)))
-#endif
- {
- int i = 0;
- int tube_element = (IS_TUBE(Feld[jx][jy]) ? Feld[jx][jy] : Back[jx][jy]);
- int tube_leave_directions[][2] =
- {
- { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
- { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
- { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
- { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
- { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
- { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
- { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
- { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
- { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
- { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
- { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
- { -1, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN }
- };
-
- while (tube_leave_directions[i][0] != tube_element)
- {
- i++;
- if (tube_leave_directions[i][0] == -1) /* should not happen */
- break;
- }
-
- if (!(tube_leave_directions[i][1] & move_direction))
- return MF_NO_ACTION; /* tube has no opening in this direction */
- }
-
-#else
+ return MP_NO_ACTION;
+#endif
if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
old_element = Back[jx][jy];
-#endif
+ /* in case of element dropped at player position, check background */
+ else if (Back[jx][jy] != EL_EMPTY &&
+ game.engine_version >= VERSION_IDENT(2,2,0,0))
+ old_element = Back[jx][jy];
if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
- return MF_NO_ACTION; /* field has no opening in this direction */
+ return MP_NO_ACTION; /* field has no opening in this direction */
if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
- return MF_NO_ACTION; /* field has no opening in this direction */
-
- element = Feld[x][y];
+ return MP_NO_ACTION; /* field has no opening in this direction */
- if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
- return MF_NO_ACTION;
+#if USE_FIXED_DONT_RUN_INTO
+ if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
+ {
+ SplashAcid(x, y);
- if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
- game.engine_version >= VERSION_IDENT(2,2,0,0))
- return MF_NO_ACTION;
+ Feld[jx][jy] = player->artwork_element;
+ InitMovingField(jx, jy, MV_DOWN);
+ Store[jx][jy] = EL_ACID;
+ ContinueMoving(jx, jy);
+ BuryPlayer(player);
-#if 1
- if (game.gravity && is_player && !player->is_auto_moving &&
- canFallDown(player) && move_direction != MV_DOWN &&
- !canMoveToValidFieldWithGravity(jx, jy, move_direction))
- return MF_NO_ACTION; /* player cannot walk here due to gravity */
+ return MP_DONT_RUN_INTO;
+ }
#endif
-#if 0
- if (element == EL_EMPTY_SPACE &&
- game.gravity && !player->is_auto_moving &&
- canFallDown(player) && move_direction != MV_DOWN)
- return MF_NO_ACTION; /* player cannot walk here due to gravity */
+#if USE_FIXED_DONT_RUN_INTO
+ if (player_can_move && DONT_RUN_INTO(element))
+ {
+ TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
+
+ return MP_DONT_RUN_INTO;
+ }
#endif
- switch (element)
- {
-#if 0
- case EL_SP_PORT_LEFT:
- case EL_SP_PORT_RIGHT:
- case EL_SP_PORT_UP:
- case EL_SP_PORT_DOWN:
- case EL_SP_PORT_HORIZONTAL:
- case EL_SP_PORT_VERTICAL:
- case EL_SP_PORT_ANY:
- case EL_SP_GRAVITY_PORT_LEFT:
- case EL_SP_GRAVITY_PORT_RIGHT:
- case EL_SP_GRAVITY_PORT_UP:
- case EL_SP_GRAVITY_PORT_DOWN:
-#if 1
- if (!canEnterSupaplexPort(x, y, dx, dy))
- return MF_NO_ACTION;
-#else
- if ((dx == -1 &&
- element != EL_SP_PORT_LEFT &&
- element != EL_SP_GRAVITY_PORT_LEFT &&
- element != EL_SP_PORT_HORIZONTAL &&
- element != EL_SP_PORT_ANY) ||
- (dx == +1 &&
- element != EL_SP_PORT_RIGHT &&
- element != EL_SP_GRAVITY_PORT_RIGHT &&
- element != EL_SP_PORT_HORIZONTAL &&
- element != EL_SP_PORT_ANY) ||
- (dy == -1 &&
- element != EL_SP_PORT_UP &&
- element != EL_SP_GRAVITY_PORT_UP &&
- element != EL_SP_PORT_VERTICAL &&
- element != EL_SP_PORT_ANY) ||
- (dy == +1 &&
- element != EL_SP_PORT_DOWN &&
- element != EL_SP_GRAVITY_PORT_DOWN &&
- element != EL_SP_PORT_VERTICAL &&
- element != EL_SP_PORT_ANY) ||
- !IN_LEV_FIELD(nextx, nexty) ||
- !IS_FREE(nextx, nexty))
- return MF_NO_ACTION;
+#if USE_FIXED_DONT_RUN_INTO
+ if (IS_MOVING(x, y) || IS_PLAYER(x, y))
+ return MP_NO_ACTION;
#endif
- if (element == EL_SP_GRAVITY_PORT_LEFT ||
- element == EL_SP_GRAVITY_PORT_RIGHT ||
- element == EL_SP_GRAVITY_PORT_UP ||
- element == EL_SP_GRAVITY_PORT_DOWN)
- game.gravity = !game.gravity;
+#if !USE_FIXED_DONT_RUN_INTO
+ element = Feld[x][y];
+#endif
- /* automatically move to the next field with double speed */
- player->programmed_action = move_direction;
-#if 1
- if (player->move_delay_reset_counter == 0)
- {
- player->move_delay_reset_counter = 2; /* two double speed steps */
+ collect_count = element_info[element].collect_count_initial;
- DOUBLE_PLAYER_SPEED(player);
- }
-#else
- player->move_delay_reset_counter = 2;
+ if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
+ return MP_NO_ACTION;
- DOUBLE_PLAYER_SPEED(player);
-#endif
+ if (game.engine_version < VERSION_IDENT(2,2,0,0))
+ player_can_move = player_can_move_or_snap;
-#if 0
- printf("::: passing port %d,%d [%d]\n", x, y, FrameCounter);
-#endif
+ if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
+ game.engine_version >= VERSION_IDENT(2,2,0,0))
+ {
+ CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
+ player->index_bit, dig_side);
+ CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
+ player->index_bit, dig_side);
- PlayLevelSound(x, y, SND_CLASS_SP_PORT_PASSING);
- break;
-#endif
+ if (element == EL_DC_LANDMINE)
+ Bang(x, y);
-#if 0
- case EL_TUBE_ANY:
- case EL_TUBE_VERTICAL:
- case EL_TUBE_HORIZONTAL:
- case EL_TUBE_VERTICAL_LEFT:
- case EL_TUBE_VERTICAL_RIGHT:
- case EL_TUBE_HORIZONTAL_UP:
- case EL_TUBE_HORIZONTAL_DOWN:
- case EL_TUBE_LEFT_UP:
- case EL_TUBE_LEFT_DOWN:
- case EL_TUBE_RIGHT_UP:
- case EL_TUBE_RIGHT_DOWN:
- {
- int i = 0;
- int tube_enter_directions[][2] =
- {
- { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
- { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
- { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
- { EL_TUBE_VERTICAL_LEFT, MV_RIGHT | MV_UP | MV_DOWN },
- { EL_TUBE_VERTICAL_RIGHT, MV_LEFT | MV_UP | MV_DOWN },
- { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_DOWN },
- { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_UP },
- { EL_TUBE_LEFT_UP, MV_RIGHT | MV_DOWN },
- { EL_TUBE_LEFT_DOWN, MV_RIGHT | MV_UP },
- { EL_TUBE_RIGHT_UP, MV_LEFT | MV_DOWN },
- { EL_TUBE_RIGHT_DOWN, MV_LEFT | MV_UP },
- { -1, MV_NO_MOVING }
- };
-
- while (tube_enter_directions[i][0] != element)
- {
- i++;
- if (tube_enter_directions[i][0] == -1) /* should not happen */
- break;
- }
+ if (Feld[x][y] != element) /* field changed by snapping */
+ return MP_ACTION;
- if (!(tube_enter_directions[i][1] & move_direction))
- return MF_NO_ACTION; /* tube has no opening in this direction */
+ return MP_NO_ACTION;
+ }
- PlayLevelSound(x, y, SND_CLASS_TUBE_WALKING);
- }
- break;
+#if USE_PLAYER_GRAVITY
+ if (player->gravity && is_player && !player->is_auto_moving &&
+ canFallDown(player) && move_direction != MV_DOWN &&
+ !canMoveToValidFieldWithGravity(jx, jy, move_direction))
+ return MP_NO_ACTION; /* player cannot walk here due to gravity */
+#else
+ if (game.gravity && is_player && !player->is_auto_moving &&
+ canFallDown(player) && move_direction != MV_DOWN &&
+ !canMoveToValidFieldWithGravity(jx, jy, move_direction))
+ return MP_NO_ACTION; /* player cannot walk here due to gravity */
#endif
- default:
+ if (player_can_move &&
+ IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
+ {
+ int sound_element = SND_ELEMENT(element);
+ int sound_action = ACTION_WALKING;
+ if (IS_RND_GATE(element))
+ {
+ if (!player->key[RND_GATE_NR(element)])
+ return MP_NO_ACTION;
+ }
+ else if (IS_RND_GATE_GRAY(element))
+ {
+ if (!player->key[RND_GATE_GRAY_NR(element)])
+ return MP_NO_ACTION;
+ }
+ else if (IS_RND_GATE_GRAY_ACTIVE(element))
+ {
+ if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
+ return MP_NO_ACTION;
+ }
+ else if (element == EL_EXIT_OPEN ||
+ element == EL_EM_EXIT_OPEN ||
#if 1
- if (IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
-#else
- if (IS_WALKABLE(element))
+ element == EL_EM_EXIT_OPENING ||
#endif
- {
- int sound_element = SND_ELEMENT(element);
- int sound_action = ACTION_WALKING;
-
-#if 0
- if (!ACCESS_FROM(element, opposite_direction))
- return MF_NO_ACTION; /* field not accessible from this direction */
+ element == EL_STEEL_EXIT_OPEN ||
+ element == EL_EM_STEEL_EXIT_OPEN ||
+#if 1
+ element == EL_EM_STEEL_EXIT_OPENING ||
#endif
+ element == EL_SP_EXIT_OPEN ||
+ element == EL_SP_EXIT_OPENING)
+ {
+ sound_action = ACTION_PASSING; /* player is passing exit */
+ }
+ else if (element == EL_EMPTY)
+ {
+ sound_action = ACTION_MOVING; /* nothing to walk on */
+ }
-#if 0
- if (element == EL_EMPTY_SPACE &&
- game.gravity && !player->is_auto_moving &&
- canFallDown(player) && move_direction != MV_DOWN)
- return MF_NO_ACTION; /* player cannot walk here due to gravity */
-#endif
+ /* play sound from background or player, whatever is available */
+ if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
+ PlayLevelSoundElementAction(x, y, sound_element, sound_action);
+ else
+ PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
+ }
+ else if (player_can_move &&
+ IS_PASSABLE(element) && canPassField(x, y, move_direction))
+ {
+ if (!ACCESS_FROM(element, opposite_direction))
+ return MP_NO_ACTION; /* field not accessible from this direction */
- if (IS_GATE(element))
- {
- if (!player->key[element - EL_GATE_1])
- return MF_NO_ACTION;
- }
- else if (IS_GATE_GRAY(element))
- {
- if (!player->key[element - EL_GATE_1_GRAY])
- return MF_NO_ACTION;
- }
- else if (element == EL_EXIT_OPEN ||
- element == EL_SP_EXIT_OPEN ||
- element == EL_SP_EXIT_OPENING)
- {
- sound_action = ACTION_PASSING; /* player is passing exit */
- }
- else if (element == EL_EMPTY)
- {
- sound_action = ACTION_MOVING; /* nothing to walk on */
- }
+ if (CAN_MOVE(element)) /* only fixed elements can be passed! */
+ return MP_NO_ACTION;
- /* play sound from background or player, whatever is available */
- if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
- PlayLevelSoundElementAction(x, y, sound_element, sound_action);
- else
- PlayLevelSoundElementAction(x, y, player->element_nr, sound_action);
+ if (IS_EM_GATE(element))
+ {
+ if (!player->key[EM_GATE_NR(element)])
+ return MP_NO_ACTION;
+ }
+ else if (IS_EM_GATE_GRAY(element))
+ {
+ if (!player->key[EM_GATE_GRAY_NR(element)])
+ return MP_NO_ACTION;
+ }
+ else if (IS_EM_GATE_GRAY_ACTIVE(element))
+ {
+ if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
+ return MP_NO_ACTION;
+ }
+ else if (IS_EMC_GATE(element))
+ {
+ if (!player->key[EMC_GATE_NR(element)])
+ return MP_NO_ACTION;
+ }
+ else if (IS_EMC_GATE_GRAY(element))
+ {
+ if (!player->key[EMC_GATE_GRAY_NR(element)])
+ return MP_NO_ACTION;
+ }
+ else if (IS_EMC_GATE_GRAY_ACTIVE(element))
+ {
+ 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;
- break;
- }
-#if 1
- else if (IS_PASSABLE(element) && canPassField(x, y, move_direction))
+ player->num_white_keys--;
+ }
+ else if (IS_SP_PORT(element))
+ {
+ if (element == EL_SP_GRAVITY_PORT_LEFT ||
+ element == EL_SP_GRAVITY_PORT_RIGHT ||
+ element == EL_SP_GRAVITY_PORT_UP ||
+ element == EL_SP_GRAVITY_PORT_DOWN)
+#if USE_PLAYER_GRAVITY
+ player->gravity = !player->gravity;
#else
- else if (IS_PASSABLE(element))
+ game.gravity = !game.gravity;
#endif
- {
-#if 0
- if (!canPassField(x, y, move_direction))
- return MF_NO_ACTION;
+ else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
+ element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
+ element == EL_SP_GRAVITY_ON_PORT_UP ||
+ element == EL_SP_GRAVITY_ON_PORT_DOWN)
+#if USE_PLAYER_GRAVITY
+ player->gravity = TRUE;
#else
-
-#if 0
-#if 1
- if (!IN_LEV_FIELD(nextx, nexty) || IS_PLAYER(nextx, nexty) ||
- !IS_WALKABLE_FROM(Feld[nextx][nexty], move_direction) ||
- (!level.can_pass_to_walkable && !IS_FREE(nextx, nexty)))
- return MF_NO_ACTION;
+ game.gravity = TRUE;
+#endif
+ else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
+ element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
+ element == EL_SP_GRAVITY_OFF_PORT_UP ||
+ element == EL_SP_GRAVITY_OFF_PORT_DOWN)
+#if USE_PLAYER_GRAVITY
+ player->gravity = FALSE;
#else
- if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
- return MF_NO_ACTION;
-#endif
+ game.gravity = FALSE;
#endif
+ }
-#if 1
- if (!ACCESS_FROM(element, opposite_direction))
- return MF_NO_ACTION; /* field not accessible from this direction */
-#else
- if (IS_CUSTOM_ELEMENT(element) &&
- !ACCESS_FROM(element, opposite_direction))
- return MF_NO_ACTION; /* field not accessible from this direction */
-#endif
+ /* automatically move to the next field with double speed */
+ player->programmed_action = move_direction;
-#if 1
- if (CAN_MOVE(element)) /* only fixed elements can be passed! */
- return MF_NO_ACTION;
-#endif
+ if (player->move_delay_reset_counter == 0)
+ {
+ player->move_delay_reset_counter = 2; /* two double speed steps */
-#endif
+ DOUBLE_PLAYER_SPEED(player);
+ }
- if (IS_EM_GATE(element))
- {
- if (!player->key[element - EL_EM_GATE_1])
- return MF_NO_ACTION;
- }
- else if (IS_EM_GATE_GRAY(element))
- {
- if (!player->key[element - EL_EM_GATE_1_GRAY])
- return MF_NO_ACTION;
- }
- else if (IS_SP_PORT(element))
- {
- if (element == EL_SP_GRAVITY_PORT_LEFT ||
- element == EL_SP_GRAVITY_PORT_RIGHT ||
- element == EL_SP_GRAVITY_PORT_UP ||
- element == EL_SP_GRAVITY_PORT_DOWN)
- game.gravity = !game.gravity;
- else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
- element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
- element == EL_SP_GRAVITY_ON_PORT_UP ||
- element == EL_SP_GRAVITY_ON_PORT_DOWN)
- game.gravity = TRUE;
- else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
- element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
- element == EL_SP_GRAVITY_OFF_PORT_UP ||
- element == EL_SP_GRAVITY_OFF_PORT_DOWN)
- game.gravity = FALSE;
- }
+ PlayLevelSoundAction(x, y, ACTION_PASSING);
+ }
+ else if (player_can_move_or_snap && IS_DIGGABLE(element))
+ {
+ RemoveField(x, y);
- /* automatically move to the next field with double speed */
- player->programmed_action = move_direction;
-#if 1
- if (player->move_delay_reset_counter == 0)
- {
- player->move_delay_reset_counter = 2; /* two double speed steps */
+ if (mode != DF_SNAP)
+ {
+ GfxElement[x][y] = GFX_ELEMENT(element);
+ player->is_digging = TRUE;
+ }
- DOUBLE_PLAYER_SPEED(player);
- }
-#else
- player->move_delay_reset_counter = 2;
+ PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
+
+ CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
+ player->index_bit, dig_side);
- DOUBLE_PLAYER_SPEED(player);
+ if (mode == DF_SNAP)
+ {
+#if USE_NEW_SNAP_DELAY
+ if (level.block_snap_field)
+ setFieldForSnapping(x, y, element, move_direction);
+ else
+ TestIfElementTouchesCustomElement(x, y); /* for empty space */
+#else
+ TestIfElementTouchesCustomElement(x, y); /* for empty space */
#endif
- PlayLevelSoundAction(x, y, ACTION_PASSING);
+ CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
+ player->index_bit, dig_side);
+ }
+ }
+ else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
+ {
+ RemoveField(x, y);
- break;
- }
- else if (IS_DIGGABLE(element))
- {
- RemoveField(x, y);
+ if (is_player && mode != DF_SNAP)
+ {
+ GfxElement[x][y] = element;
+ player->is_collecting = TRUE;
+ }
+
+ if (element == EL_SPEED_PILL)
+ {
+ player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
+ }
+ else if (element == EL_EXTRA_TIME && level.time > 0)
+ {
+ TimeLeft += level.extra_time;
- if (mode != DF_SNAP)
- {
#if 1
- GfxElement[x][y] = GFX_ELEMENT(element);
+ game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
+
+ DisplayGameControlValues();
#else
- GfxElement[x][y] =
- (GFX_CRUMBLED(element) ? EL_SAND : GFX_ELEMENT(element));
+ DrawGameValue_Time(TimeLeft);
#endif
- player->is_digging = TRUE;
- }
+ }
+ else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
+ {
+ player->shield_normal_time_left += level.shield_normal_time;
+ if (element == EL_SHIELD_DEADLY)
+ player->shield_deadly_time_left += level.shield_deadly_time;
+ }
+ else if (element == EL_DYNAMITE ||
+ element == EL_EM_DYNAMITE ||
+ element == EL_SP_DISK_RED)
+ {
+ if (player->inventory_size < MAX_INVENTORY_SIZE)
+ player->inventory_element[player->inventory_size++] = element;
- PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
+ DrawGameDoorValues();
+ }
+ else if (element == EL_DYNABOMB_INCREASE_NUMBER)
+ {
+ player->dynabomb_count++;
+ player->dynabombs_left++;
+ }
+ else if (element == EL_DYNABOMB_INCREASE_SIZE)
+ {
+ player->dynabomb_size++;
+ }
+ else if (element == EL_DYNABOMB_INCREASE_POWER)
+ {
+ player->dynabomb_xl = TRUE;
+ }
+ else if (IS_KEY(element))
+ {
+ player->key[KEY_NR(element)] = TRUE;
- CheckTriggeredElementChangeByPlayer(x, y, element,CE_OTHER_GETS_DIGGED,
- player->index_bit, dig_side);
+ DrawGameDoorValues();
+ }
+ else if (element == EL_DC_KEY_WHITE)
+ {
+ player->num_white_keys++;
-#if 1
- if (mode == DF_SNAP)
- TestIfElementTouchesCustomElement(x, y); /* for empty space */
-#endif
+ /* display white keys? */
+ /* DrawGameDoorValues(); */
+ }
+ else if (IS_ENVELOPE(element))
+ {
+ player->show_envelope = element;
+ }
+ else if (element == EL_EMC_LENSES)
+ {
+ game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
- break;
- }
- else if (IS_COLLECTIBLE(element))
- {
- RemoveField(x, y);
+ RedrawAllInvisibleElementsForLenses();
+ }
+ else if (element == EL_EMC_MAGNIFIER)
+ {
+ game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
- if (is_player && mode != DF_SNAP)
- {
- GfxElement[x][y] = element;
- player->is_collecting = TRUE;
- }
+ RedrawAllInvisibleElementsForMagnifier();
+ }
+ else if (IS_DROPPABLE(element) ||
+ IS_THROWABLE(element)) /* can be collected and dropped */
+ {
+ int i;
- if (element == EL_SPEED_PILL)
- player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
- else if (element == EL_EXTRA_TIME && level.time > 0)
- {
- TimeLeft += 10;
- DrawGameValue_Time(TimeLeft);
- }
- else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
- {
- player->shield_normal_time_left += 10;
- if (element == EL_SHIELD_DEADLY)
- player->shield_deadly_time_left += 10;
- }
- else if (element == EL_DYNAMITE || element == EL_SP_DISK_RED)
- {
+ if (collect_count == 0)
+ player->inventory_infinite_element = element;
+ else
+ for (i = 0; i < collect_count; i++)
if (player->inventory_size < MAX_INVENTORY_SIZE)
player->inventory_element[player->inventory_size++] = element;
- DrawGameValue_Dynamite(local_player->inventory_size);
- }
- else if (element == EL_DYNABOMB_INCREASE_NUMBER)
- {
- player->dynabomb_count++;
- player->dynabombs_left++;
- }
- else if (element == EL_DYNABOMB_INCREASE_SIZE)
- {
- player->dynabomb_size++;
- }
- else if (element == EL_DYNABOMB_INCREASE_POWER)
- {
- player->dynabomb_xl = TRUE;
- }
- else if ((element >= EL_KEY_1 && element <= EL_KEY_4) ||
- (element >= EL_EM_KEY_1 && element <= EL_EM_KEY_4))
- {
- int key_nr = (element >= EL_KEY_1 && element <= EL_KEY_4 ?
- element - EL_KEY_1 : element - EL_EM_KEY_1);
-
- player->key[key_nr] = TRUE;
-
- DrawGameValue_Keys(player);
+ DrawGameDoorValues();
+ }
+ else if (collect_count > 0)
+ {
+ local_player->gems_still_needed -= collect_count;
+ if (local_player->gems_still_needed < 0)
+ local_player->gems_still_needed = 0;
- redraw_mask |= REDRAW_DOOR_1;
- }
- else if (IS_ENVELOPE(element))
- {
#if 1
- player->show_envelope = element;
+ game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
+
+ DisplayGameControlValues();
#else
- ShowEnvelope(element - EL_ENVELOPE_1);
+ DrawGameValue_Emeralds(local_player->gems_still_needed);
#endif
- }
- else if (IS_DROPPABLE(element) ||
- IS_THROWABLE(element)) /* can be collected and dropped */
- {
- int i;
+ }
- if (element_info[element].collect_count == 0)
- player->inventory_infinite_element = element;
- else
- for (i = 0; i < element_info[element].collect_count; i++)
- if (player->inventory_size < MAX_INVENTORY_SIZE)
- player->inventory_element[player->inventory_size++] = element;
+ RaiseScoreElement(element);
+ PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
- DrawGameValue_Dynamite(local_player->inventory_size);
- }
- else if (element_info[element].collect_count > 0)
- {
- local_player->gems_still_needed -=
- element_info[element].collect_count;
- if (local_player->gems_still_needed < 0)
- local_player->gems_still_needed = 0;
+ if (is_player)
+ CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
+ player->index_bit, dig_side);
- DrawGameValue_Emeralds(local_player->gems_still_needed);
- }
+ if (mode == DF_SNAP)
+ {
+#if USE_NEW_SNAP_DELAY
+ if (level.block_snap_field)
+ setFieldForSnapping(x, y, element, move_direction);
+ else
+ TestIfElementTouchesCustomElement(x, y); /* for empty space */
+#else
+ TestIfElementTouchesCustomElement(x, y); /* for empty space */
+#endif
- RaiseScoreElement(element);
- PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
+ CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
+ player->index_bit, dig_side);
+ }
+ }
+ else if (player_can_move_or_snap && IS_PUSHABLE(element))
+ {
+ if (mode == DF_SNAP && element != EL_BD_ROCK)
+ return MP_NO_ACTION;
- if (is_player)
- CheckTriggeredElementChangeByPlayer(x, y, element,
- CE_OTHER_GETS_COLLECTED,
- player->index_bit, dig_side);
+ if (CAN_FALL(element) && dy)
+ return MP_NO_ACTION;
-#if 1
- if (mode == DF_SNAP)
- TestIfElementTouchesCustomElement(x, y); /* for empty space */
-#endif
+ if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
+ !(element == EL_SPRING && level.use_spring_bug))
+ return MP_NO_ACTION;
- break;
- }
- else if (IS_PUSHABLE(element))
- {
- if (mode == DF_SNAP && element != EL_BD_ROCK)
- return MF_NO_ACTION;
+ if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
+ ((move_direction & MV_VERTICAL &&
+ ((element_info[element].move_pattern & MV_LEFT &&
+ IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
+ (element_info[element].move_pattern & MV_RIGHT &&
+ IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
+ (move_direction & MV_HORIZONTAL &&
+ ((element_info[element].move_pattern & MV_UP &&
+ IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
+ (element_info[element].move_pattern & MV_DOWN &&
+ IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
+ return MP_NO_ACTION;
- if (CAN_FALL(element) && dy)
- return MF_NO_ACTION;
+ /* do not push elements already moving away faster than player */
+ if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
+ ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
+ return MP_NO_ACTION;
- if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
- !(element == EL_SPRING && level.use_spring_bug))
- return MF_NO_ACTION;
+ if (game.engine_version >= VERSION_IDENT(3,1,0,0))
+ {
+ if (player->push_delay_value == -1 || !player_was_pushing)
+ player->push_delay_value = GET_NEW_PUSH_DELAY(element);
+ }
+ else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
+ {
+ if (player->push_delay_value == -1)
+ player->push_delay_value = GET_NEW_PUSH_DELAY(element);
+ }
+ else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
+ {
+ if (!player->is_pushing)
+ player->push_delay_value = GET_NEW_PUSH_DELAY(element);
+ }
-#if 1
- if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
- ((move_direction & MV_VERTICAL &&
- ((element_info[element].move_pattern & MV_LEFT &&
- IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
- (element_info[element].move_pattern & MV_RIGHT &&
- IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
- (move_direction & MV_HORIZONTAL &&
- ((element_info[element].move_pattern & MV_UP &&
- IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
- (element_info[element].move_pattern & MV_DOWN &&
- IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
- return MF_NO_ACTION;
-#endif
+ player->is_pushing = TRUE;
+ player->is_active = TRUE;
-#if 1
- /* do not push elements already moving away faster than player */
- if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
- ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
- return MF_NO_ACTION;
-#else
- if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
- return MF_NO_ACTION;
-#endif
+ if (!(IN_LEV_FIELD(nextx, nexty) &&
+ (IS_FREE(nextx, nexty) ||
+ (IS_SB_ELEMENT(element) &&
+ Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
+ (IS_CUSTOM_ELEMENT(element) &&
+ CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
+ return MP_NO_ACTION;
-#if 1
+ if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
+ return MP_NO_ACTION;
-#if 1
- if (game.engine_version >= VERSION_IDENT(3,1,0,0))
- {
- if (player->push_delay_value == -1 || !player_was_pushing)
- player->push_delay_value = GET_NEW_PUSH_DELAY(element);
- }
- else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
- {
- if (player->push_delay_value == -1)
- player->push_delay_value = GET_NEW_PUSH_DELAY(element);
- }
-#else
- if (game.engine_version >= VERSION_IDENT(3,0,7,1))
- {
- if (player->push_delay_value == -1 || !player_was_pushing)
- player->push_delay_value = GET_NEW_PUSH_DELAY(element);
- }
-#endif
- else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
- {
- if (!player->is_pushing)
- player->push_delay_value = GET_NEW_PUSH_DELAY(element);
- }
+ if (player->push_delay == -1) /* new pushing; restart delay */
+ player->push_delay = 0;
- /*
- if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
- (game.engine_version < VERSION_IDENT(3,0,7,1) ||
- !player_is_pushing))
- player->push_delay_value = GET_NEW_PUSH_DELAY(element);
- */
-#else
- if (!player->is_pushing &&
- game.engine_version >= VERSION_IDENT(2,2,0,7))
- player->push_delay_value = GET_NEW_PUSH_DELAY(element);
-#endif
+ if (player->push_delay < player->push_delay_value &&
+ !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
+ element != EL_SPRING && element != EL_BALLOON)
+ {
+ /* make sure that there is no move delay before next try to push */
+ if (game.engine_version >= VERSION_IDENT(3,0,7,1))
+ player->move_delay = 0;
-#if 0
- printf("::: push delay: %ld -> %ld [%d, %d] [%d / %d] [%d '%s': %d]\n",
- player->push_delay, player->push_delay_value,
- FrameCounter, game.engine_version,
- player_was_pushing, player->is_pushing,
- element, element_info[element].token_name,
- GET_NEW_PUSH_DELAY(element));
-#endif
+ return MP_NO_ACTION;
+ }
- player->is_pushing = TRUE;
+ if (IS_CUSTOM_ELEMENT(element) &&
+ CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
+ {
+ if (!DigFieldByCE(nextx, nexty, element))
+ return MP_NO_ACTION;
+ }
- if (!(IN_LEV_FIELD(nextx, nexty) &&
- (IS_FREE(nextx, nexty) ||
- (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
- IS_SB_ELEMENT(element)))))
- return MF_NO_ACTION;
+ if (IS_SB_ELEMENT(element))
+ {
+ if (element == EL_SOKOBAN_FIELD_FULL)
+ {
+ Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
+ local_player->sokobanfields_still_needed++;
+ }
- if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
- return MF_NO_ACTION;
+ if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
+ {
+ Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
+ local_player->sokobanfields_still_needed--;
+ }
-#if USE_NEW_PUSH_DELAY
+ Feld[x][y] = EL_SOKOBAN_OBJECT;
-#if 0
- if ( (player->push_delay == -1) != (player->push_delay2 == 0) )
- printf("::: ALERT: %d, %d [%d / %d]\n",
- player->push_delay, player->push_delay2,
- FrameCounter, FrameCounter / 50);
-#endif
+ if (Back[x][y] == Back[nextx][nexty])
+ PlayLevelSoundAction(x, y, ACTION_PUSHING);
+ else if (Back[x][y] != 0)
+ PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
+ ACTION_EMPTYING);
+ else
+ PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
+ ACTION_FILLING);
- if (player->push_delay == -1) /* new pushing; restart delay */
- player->push_delay = 0;
+#if 1
+ if (local_player->sokobanfields_still_needed == 0 &&
+ (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
#else
- if (player->push_delay == 0) /* new pushing; restart delay */
- player->push_delay = FrameCounter;
-#endif
-
-#if USE_NEW_PUSH_DELAY
-#if 0
- if ( (player->push_delay > 0) != (!xxx_fr) )
- printf("::: PUSH BUG! %d, (%d -> %d) %d [%d / %d]\n",
- player->push_delay,
- xxx_pdv2, player->push_delay2, player->push_delay_value,
- FrameCounter, FrameCounter / 50);
+ if (local_player->sokobanfields_still_needed == 0 &&
+ game.emulation == EMU_SOKOBAN)
#endif
+ {
+ PlayerWins(player);
-#if 0
- if (player->push_delay > 0 &&
- !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
- element != EL_SPRING && element != EL_BALLOON)
-#else
- /* !!! */
- if (player->push_delay < player->push_delay_value &&
- !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
- element != EL_SPRING && element != EL_BALLOON)
-#endif
+ PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
+ }
+ }
+ else
+ PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
-#else
- if (!FrameReached(&player->push_delay, player->push_delay_value) &&
- !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
- element != EL_SPRING && element != EL_BALLOON)
-#endif
- {
- /* make sure that there is no move delay before next try to push */
-#if USE_NEW_MOVE_DELAY
- if (game.engine_version >= VERSION_IDENT(3,0,7,1))
- player->move_delay = 0;
-#else
- if (game.engine_version >= VERSION_IDENT(3,0,7,1))
- player->move_delay = INITIAL_MOVE_DELAY_OFF;
-#endif
+ InitMovingField(x, y, move_direction);
+ GfxAction[x][y] = ACTION_PUSHING;
- return MF_NO_ACTION;
- }
+ if (mode == DF_SNAP)
+ ContinueMoving(x, y);
+ else
+ MovPos[x][y] = (dx != 0 ? dx : dy);
-#if 0
- printf("::: NOW PUSHING... [%d]\n", FrameCounter);
-#endif
+ Pushed[x][y] = TRUE;
+ Pushed[nextx][nexty] = TRUE;
- if (IS_SB_ELEMENT(element))
- {
- if (element == EL_SOKOBAN_FIELD_FULL)
- {
- Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
- local_player->sokobanfields_still_needed++;
- }
+ if (game.engine_version < VERSION_IDENT(2,2,0,7))
+ player->push_delay_value = GET_NEW_PUSH_DELAY(element);
+ else
+ player->push_delay_value = -1; /* get new value later */
- if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
- {
- Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
- local_player->sokobanfields_still_needed--;
- }
+ /* check for element change _after_ element has been pushed */
+ if (game.use_change_when_pushing_bug)
+ {
+ CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
+ player->index_bit, dig_side);
+ CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
+ player->index_bit, dig_side);
+ }
+ }
+ else if (IS_SWITCHABLE(element))
+ {
+ if (PLAYER_SWITCHING(player, x, y))
+ {
+ CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
+ player->index_bit, dig_side);
- Feld[x][y] = EL_SOKOBAN_OBJECT;
+ return MP_ACTION;
+ }
- if (Back[x][y] == Back[nextx][nexty])
- PlayLevelSoundAction(x, y, ACTION_PUSHING);
- else if (Back[x][y] != 0)
- PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
- ACTION_EMPTYING);
- else
- PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
- ACTION_FILLING);
+ player->is_switching = TRUE;
+ player->switch_x = x;
+ player->switch_y = y;
- if (local_player->sokobanfields_still_needed == 0 &&
- game.emulation == EMU_SOKOBAN)
- {
- player->LevelSolved = player->GameOver = TRUE;
- PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
- }
- }
- else
- PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
+ PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
- InitMovingField(x, y, move_direction);
- GfxAction[x][y] = ACTION_PUSHING;
+ if (element == EL_ROBOT_WHEEL)
+ {
+ Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
+ ZX = x;
+ ZY = y;
- if (mode == DF_SNAP)
- ContinueMoving(x, y);
- else
- MovPos[x][y] = (dx != 0 ? dx : dy);
+ game.robot_wheel_active = TRUE;
- Pushed[x][y] = TRUE;
- Pushed[nextx][nexty] = TRUE;
+ TEST_DrawLevelField(x, y);
+ }
+ else if (element == EL_SP_TERMINAL)
+ {
+ int xx, yy;
- if (game.engine_version < VERSION_IDENT(2,2,0,7))
- player->push_delay_value = GET_NEW_PUSH_DELAY(element);
- else
- player->push_delay_value = -1; /* get new value later */
+ SCAN_PLAYFIELD(xx, yy)
+ {
+ if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
+ Bang(xx, yy);
+ else if (Feld[xx][yy] == EL_SP_TERMINAL)
+ Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
+ }
+ }
+ else if (IS_BELT_SWITCH(element))
+ {
+ ToggleBeltSwitch(x, y);
+ }
+ else if (element == EL_SWITCHGATE_SWITCH_UP ||
+ element == EL_SWITCHGATE_SWITCH_DOWN ||
+ element == EL_DC_SWITCHGATE_SWITCH_UP ||
+ element == EL_DC_SWITCHGATE_SWITCH_DOWN)
+ {
+ ToggleSwitchgateSwitch(x, y);
+ }
+ else if (element == EL_LIGHT_SWITCH ||
+ element == EL_LIGHT_SWITCH_ACTIVE)
+ {
+ ToggleLightSwitch(x, y);
+ }
+ else if (element == EL_TIMEGATE_SWITCH ||
+ element == EL_DC_TIMEGATE_SWITCH)
+ {
+ ActivateTimegateSwitch(x, y);
+ }
+ else if (element == EL_BALLOON_SWITCH_LEFT ||
+ element == EL_BALLOON_SWITCH_RIGHT ||
+ element == EL_BALLOON_SWITCH_UP ||
+ element == EL_BALLOON_SWITCH_DOWN ||
+ element == EL_BALLOON_SWITCH_NONE ||
+ element == EL_BALLOON_SWITCH_ANY)
+ {
+ game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
+ element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
+ element == EL_BALLOON_SWITCH_UP ? MV_UP :
+ element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
+ element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
+ move_direction);
+ }
+ else if (element == EL_LAMP)
+ {
+ Feld[x][y] = EL_LAMP_ACTIVE;
+ local_player->lights_still_needed--;
-#if USE_PUSH_BUGFIX
- /* now: check for element change _after_ element has been pushed! */
-#if 1
- if (game.use_bug_change_when_pushing)
-#else
- if (game.engine_version < VERSION_IDENT(3,1,0,0))
-#endif
- {
- CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
- player->index_bit, dig_side);
- CheckTriggeredElementChangeByPlayer(x,y,element,CE_OTHER_GETS_PUSHED,
- player->index_bit, dig_side);
- }
+ ResetGfxAnimation(x, y);
+ TEST_DrawLevelField(x, y);
+ }
+ else if (element == EL_TIME_ORB_FULL)
+ {
+ Feld[x][y] = EL_TIME_ORB_EMPTY;
-#else
+ if (level.time > 0 || level.use_time_orb_bug)
+ {
+ TimeLeft += level.time_orb_time;
#if 1
- /* check for element change _after_ element has been pushed! */
-#else
+ game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
-#if 1
- /* !!! TEST ONLY !!! */
- CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
- player->index_bit, dig_side);
- CheckTriggeredElementChangeByPlayer(x, y, element,CE_OTHER_GETS_PUSHED,
- player->index_bit, dig_side);
+ DisplayGameControlValues();
#else
- CheckTriggeredElementChangeByPlayer(x, y, element,CE_OTHER_GETS_PUSHED,
- player->index_bit, dig_side);
- CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
- player->index_bit, dig_side);
-#endif
+ DrawGameValue_Time(TimeLeft);
#endif
+ }
-#endif
+ ResetGfxAnimation(x, y);
+ TEST_DrawLevelField(x, y);
+ }
+ else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
+ element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
+ {
+ int xx, yy;
- break;
- }
- else if (IS_SWITCHABLE(element))
+ game.ball_state = !game.ball_state;
+
+ SCAN_PLAYFIELD(xx, yy)
{
- if (PLAYER_SWITCHING(player, x, y))
- {
- CheckTriggeredElementChangeByPlayer(x,y, element,
- CE_OTHER_GETS_PRESSED,
- player->index_bit, dig_side);
+ int e = Feld[xx][yy];
- return MF_ACTION;
+ if (game.ball_state)
+ {
+ if (e == EL_EMC_MAGIC_BALL)
+ CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
+ else if (e == EL_EMC_MAGIC_BALL_SWITCH)
+ CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
+ }
+ else
+ {
+ if (e == EL_EMC_MAGIC_BALL_ACTIVE)
+ CreateField(xx, yy, EL_EMC_MAGIC_BALL);
+ else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
+ CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
}
+ }
+ }
- player->is_switching = TRUE;
- player->switch_x = x;
- player->switch_y = y;
+ CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
+ player->index_bit, dig_side);
- PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
+ CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
+ player->index_bit, dig_side);
- if (element == EL_ROBOT_WHEEL)
- {
- Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
- ZX = x;
- ZY = y;
+ CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
+ player->index_bit, dig_side);
- DrawLevelField(x, y);
- }
- else if (element == EL_SP_TERMINAL)
- {
- int xx, yy;
+ return MP_ACTION;
+ }
+ else
+ {
+ if (!PLAYER_SWITCHING(player, x, y))
+ {
+ player->is_switching = TRUE;
+ player->switch_x = x;
+ player->switch_y = y;
+
+ CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
+ player->index_bit, dig_side);
+ CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
+ player->index_bit, dig_side);
+
+ CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
+ player->index_bit, dig_side);
+ CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
+ player->index_bit, dig_side);
+ }
- for (yy = 0; yy < lev_fieldy; yy++) for (xx=0; xx < lev_fieldx; xx++)
- {
- if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
- Bang(xx, yy);
- else if (Feld[xx][yy] == EL_SP_TERMINAL)
- Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
- }
- }
- else if (IS_BELT_SWITCH(element))
- {
- ToggleBeltSwitch(x, y);
- }
- else if (element == EL_SWITCHGATE_SWITCH_UP ||
- element == EL_SWITCHGATE_SWITCH_DOWN)
- {
- ToggleSwitchgateSwitch(x, y);
- }
- else if (element == EL_LIGHT_SWITCH ||
- element == EL_LIGHT_SWITCH_ACTIVE)
- {
- ToggleLightSwitch(x, y);
+ CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
+ player->index_bit, dig_side);
+ CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
+ player->index_bit, dig_side);
-#if 0
- PlayLevelSound(x, y, element == EL_LIGHT_SWITCH ?
- SND_LIGHT_SWITCH_ACTIVATING :
- SND_LIGHT_SWITCH_DEACTIVATING);
-#endif
- }
- else if (element == EL_TIMEGATE_SWITCH)
- {
- ActivateTimegateSwitch(x, y);
- }
- else if (element == EL_BALLOON_SWITCH_LEFT ||
- element == EL_BALLOON_SWITCH_RIGHT ||
- element == EL_BALLOON_SWITCH_UP ||
- element == EL_BALLOON_SWITCH_DOWN ||
- element == EL_BALLOON_SWITCH_ANY)
- {
- if (element == EL_BALLOON_SWITCH_ANY)
- game.balloon_dir = move_direction;
- else
- game.balloon_dir = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
- element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
- element == EL_BALLOON_SWITCH_UP ? MV_UP :
- element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
- MV_NO_MOVING);
- }
- else if (element == EL_LAMP)
- {
- Feld[x][y] = EL_LAMP_ACTIVE;
- local_player->lights_still_needed--;
+ return MP_NO_ACTION;
+ }
+
+ player->push_delay = -1;
+
+ if (is_player) /* function can also be called by EL_PENGUIN */
+ {
+ if (Feld[x][y] != element) /* really digged/collected something */
+ {
+ player->is_collecting = !player->is_digging;
+ player->is_active = TRUE;
+ }
+ }
+
+ return MP_MOVING;
+}
+
+static boolean DigFieldByCE(int x, int y, int digging_element)
+{
+ int element = Feld[x][y];
- DrawLevelField(x, y);
- }
- else if (element == EL_TIME_ORB_FULL)
- {
- Feld[x][y] = EL_TIME_ORB_EMPTY;
- TimeLeft += 10;
- DrawGameValue_Time(TimeLeft);
+ if (!IS_FREE(x, y))
+ {
+ int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
+ IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
+ ACTION_BREAKING);
- DrawLevelField(x, y);
+ /* no element can dig solid indestructible elements */
+ if (IS_INDESTRUCTIBLE(element) &&
+ !IS_DIGGABLE(element) &&
+ !IS_COLLECTIBLE(element))
+ return FALSE;
-#if 0
- PlaySoundStereo(SND_TIME_ORB_FULL_COLLECTING, SOUND_MIDDLE);
-#endif
- }
+ if (AmoebaNr[x][y] &&
+ (element == EL_AMOEBA_FULL ||
+ element == EL_BD_AMOEBA ||
+ element == EL_AMOEBA_GROWING))
+ {
+ AmoebaCnt[AmoebaNr[x][y]]--;
+ AmoebaCnt2[AmoebaNr[x][y]]--;
+ }
- CheckTriggeredElementChangeByPlayer(x, y, element,
- CE_OTHER_IS_SWITCHING,
- player->index_bit, dig_side);
+ if (IS_MOVING(x, y))
+ RemoveMovingField(x, y);
+ else
+ {
+ RemoveField(x, y);
+ TEST_DrawLevelField(x, y);
+ }
- CheckTriggeredElementChangeByPlayer(x,y, element,CE_OTHER_GETS_PRESSED,
- player->index_bit, dig_side);
+ /* if digged element was about to explode, prevent the explosion */
+ ExplodeField[x][y] = EX_TYPE_NONE;
- return MF_ACTION;
- }
- else
- {
- if (!PLAYER_SWITCHING(player, x, y))
- {
- player->is_switching = TRUE;
- player->switch_x = x;
- player->switch_y = y;
+ PlayLevelSoundAction(x, y, action);
+ }
-#if 1
- /* !!! TEST ONLY !!! */
- CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
- player->index_bit, dig_side);
- CheckTriggeredElementChangeByPlayer(x, y, element,
- CE_OTHER_IS_SWITCHING,
- player->index_bit, dig_side);
-#else
- CheckTriggeredElementChangeByPlayer(x, y, element,
- CE_OTHER_IS_SWITCHING,
- player->index_bit, dig_side);
- CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
- player->index_bit, dig_side);
-#endif
- }
+ Store[x][y] = EL_EMPTY;
#if 1
- /* !!! TEST ONLY !!! (this breaks "machine", level 000) */
- CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
- player->index_bit, dig_side);
- CheckTriggeredElementChangeByPlayer(x,y, element,CE_OTHER_GETS_PRESSED,
- player->index_bit, dig_side);
+ /* this makes it possible to leave the removed element again */
+ if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
+ Store[x][y] = element;
#else
- CheckTriggeredElementChangeByPlayer(x,y, element,CE_OTHER_GETS_PRESSED,
- player->index_bit, dig_side);
- CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
- player->index_bit, dig_side);
-#endif
- }
+ if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
+ {
+ int move_leave_element = element_info[digging_element].move_leave_element;
- return MF_NO_ACTION;
+ /* this makes it possible to leave the removed element again */
+ Store[x][y] = (move_leave_element == EL_TRIGGER_ELEMENT ?
+ element : move_leave_element);
}
-
-#if USE_NEW_PUSH_DELAY
- player->push_delay = -1;
-#else
- player->push_delay = 0;
#endif
- if (Feld[x][y] != element) /* really digged/collected something */
- player->is_collecting = !player->is_digging;
-
- return MF_MOVING;
+ return TRUE;
}
-boolean SnapField(struct PlayerInfo *player, int dx, int dy)
+static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
{
int jx = player->jx, jy = player->jy;
int x = jx + dx, y = jy + dy;
- int snap_direction = (dx == -1 ? MV_LEFT :
+ int snap_direction = (dx == -1 ? MV_LEFT :
dx == +1 ? MV_RIGHT :
- dy == -1 ? MV_UP :
- dy == +1 ? MV_DOWN : MV_NO_MOVING);
+ dy == -1 ? MV_UP :
+ dy == +1 ? MV_DOWN : MV_NONE);
+ boolean can_continue_snapping = (level.continuous_snapping &&
+ WasJustFalling[x][y] < CHECK_DELAY_FALLING);
-#if 0
- if (player->MovPos != 0)
- return FALSE;
-#else
if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
return FALSE;
-#endif
if (!player->active || !IN_LEV_FIELD(x, y))
return FALSE;
return FALSE;
}
+#if USE_NEW_CONTINUOUS_SNAPPING
+ /* prevent snapping with already pressed snap key when not allowed */
+ if (player->is_snapping && !can_continue_snapping)
+ return FALSE;
+#else
if (player->is_snapping)
return FALSE;
+#endif
player->MovDir = snap_direction;
-#if 1
if (player->MovPos == 0)
-#endif
{
player->is_moving = FALSE;
player->is_digging = FALSE;
}
player->is_dropping = FALSE;
+ player->is_dropping_pressed = FALSE;
+ player->drop_pressed_delay = 0;
- if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MF_NO_ACTION)
+ if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
return FALSE;
player->is_snapping = TRUE;
+ player->is_active = TRUE;
-#if 1
if (player->MovPos == 0)
-#endif
{
player->is_moving = FALSE;
player->is_digging = FALSE;
player->is_collecting = FALSE;
}
-#if 1
if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
- DrawLevelField(player->last_jx, player->last_jy);
-#endif
-
- DrawLevelField(x, y);
+ TEST_DrawLevelField(player->last_jx, player->last_jy);
-#if 0
- BackToFront();
-#endif
+ TEST_DrawLevelField(x, y);
return TRUE;
}
-boolean DropElement(struct PlayerInfo *player)
+static boolean DropElement(struct PlayerInfo *player)
{
int old_element, new_element;
int dropx = player->jx, dropy = player->jy;
int drop_direction = player->MovDir;
-#if 1
int drop_side = drop_direction;
+#if 1
+ int drop_element = get_next_dropped_element(player);
#else
- static int trigger_sides[4] =
- {
- CH_SIDE_LEFT, /* dropping left */
- CH_SIDE_RIGHT, /* dropping right */
- CH_SIDE_TOP, /* dropping up */
- CH_SIDE_BOTTOM, /* dropping down */
- };
- int drop_side = trigger_sides[MV_DIR_BIT(drop_direction)];
-#endif
int drop_element = (player->inventory_size > 0 ?
player->inventory_element[player->inventory_size - 1] :
player->inventory_infinite_element != EL_UNDEFINED ?
player->dynabombs_left > 0 ?
EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
EL_UNDEFINED);
+#endif
+
+ player->is_dropping_pressed = TRUE;
+
+ /* do not drop an element on top of another element; when holding drop key
+ pressed without moving, dropped element must move away before the next
+ element can be dropped (this is especially important if the next element
+ is dynamite, which can be placed on background for historical reasons) */
+ if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
+ return MP_ACTION;
if (IS_THROWABLE(drop_element))
{
return FALSE;
/* check if player has anything that can be dropped */
-#if 1
if (new_element == EL_UNDEFINED)
return FALSE;
-#else
- if (player->inventory_size == 0 &&
- player->inventory_infinite_element == EL_UNDEFINED &&
- player->dynabombs_left == 0)
+
+ /* check if drop key was pressed long enough for EM style dynamite */
+ if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
return FALSE;
-#endif
/* check if anything can be dropped at the current position */
if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
return FALSE;
/* collected custom elements can only be dropped on empty fields */
-#if 1
if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
return FALSE;
-#else
- if (player->inventory_size > 0 &&
- IS_CUSTOM_ELEMENT(player->inventory_element[player->inventory_size - 1])
- && old_element != EL_EMPTY)
- return FALSE;
-#endif
if (old_element != EL_EMPTY)
Back[dropx][dropy] = old_element; /* store old element on this field */
{
player->inventory_size--;
-#if 0
- new_element = player->inventory_element[player->inventory_size];
-#endif
-
- DrawGameValue_Dynamite(local_player->inventory_size);
+ DrawGameDoorValues();
if (new_element == EL_DYNAMITE)
new_element = EL_DYNAMITE_ACTIVE;
+ else if (new_element == EL_EM_DYNAMITE)
+ new_element = EL_EM_DYNAMITE_ACTIVE;
else if (new_element == EL_SP_DISK_RED)
new_element = EL_SP_DISK_RED_ACTIVE;
}
PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
-#if 1
/* needed if previous element just changed to "empty" in the last frame */
- Changed[dropx][dropy] = 0; /* allow another change */
-#endif
+ ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
-#if 1
- /* !!! TEST ONLY !!! */
CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
player->index_bit, drop_side);
CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
- CE_OTHER_GETS_DROPPED,
- player->index_bit, drop_side);
-#else
- CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
- CE_OTHER_GETS_DROPPED,
+ CE_PLAYER_DROPS_X,
player->index_bit, drop_side);
- CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
- player->index_bit, drop_side);
-#endif
TestIfElementTouchesCustomElement(dropx, dropy);
}
{
player->dynabombs_left--;
-#if 0
- new_element = EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr;
-#endif
-
Feld[dropx][dropy] = new_element;
if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
}
-
-
-#if 1
-
if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
- {
-#if 1
InitField_WithBug1(dropx, dropy, FALSE);
-#else
- InitField(dropx, dropy, FALSE);
- if (CAN_MOVE(Feld[dropx][dropy]))
- InitMovDir(dropx, dropy);
-#endif
- }
new_element = Feld[dropx][dropy]; /* element might have changed */
if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
element_info[new_element].move_pattern == MV_WHEN_DROPPED)
{
-#if 0
- int move_stepsize = element_info[new_element].move_stepsize;
-#endif
int move_direction, nextx, nexty;
if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
nextx = dropx + GET_DX_FROM_DIR(move_direction);
nexty = dropy + GET_DY_FROM_DIR(move_direction);
-#if 1
- Changed[dropx][dropy] = 0; /* allow another change */
- CheckCollision[dropx][dropy] = 2;
-#else
-
- if (IN_LEV_FIELD_AND_IS_FREE(nextx, nexty))
- {
-#if 0
- WasJustMoving[dropx][dropy] = 3;
-#else
-#if 1
- InitMovingField(dropx, dropy, move_direction);
- ContinueMoving(dropx, dropy);
-#endif
-#endif
- }
-#if 0
- /* !!! commented out from 3.1.0-4 to 3.1.0-5 !!! */
- else
- {
- Changed[dropx][dropy] = 0; /* allow another change */
+ ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
-#if 1
- TestIfElementHitsCustomElement(dropx, dropy, move_direction);
+#if USE_FIX_IMPACT_COLLISION
+ /* do not cause impact style collision by dropping elements that can fall */
+ CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
#else
- CheckElementChangeBySide(dropx, dropy, new_element, touched_element,
- CE_HITTING_SOMETHING, move_direction);
-#endif
- }
-#endif
-
-#endif
-
-#if 0
- player->drop_delay = 2 * TILEX / move_stepsize + 1;
+ CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
#endif
}
-#if 0
- player->drop_delay = 8 + 8 + 8;
-#endif
-
-#if 1
player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
-#endif
-
-#endif
-
player->is_dropping = TRUE;
+ player->drop_pressed_delay = 0;
+ player->is_dropping_pressed = FALSE;
+
+ player->drop_x = dropx;
+ player->drop_y = dropy;
return TRUE;
}
(sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
(SCR_FIELDX + 2 * max_distance));
- if (IS_LOOP_SOUND(nr))
- {
- /* This assures that quieter loop sounds do not overwrite louder ones,
- while restarting sound volume comparison with each new game frame. */
+ if (IS_LOOP_SOUND(nr))
+ {
+ /* This assures that quieter loop sounds do not overwrite louder ones,
+ while restarting sound volume comparison with each new game frame. */
+
+ if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
+ return;
+
+ loop_sound_volume[nr] = volume;
+ loop_sound_frame[nr] = FrameCounter;
+ }
+
+ PlaySoundExt(nr, volume, stereo_position, type);
+}
+
+static void PlayLevelSoundNearest(int x, int y, int sound_action)
+{
+ PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
+ x > LEVELX(BX2) ? LEVELX(BX2) : x,
+ y < LEVELY(BY1) ? LEVELY(BY1) :
+ y > LEVELY(BY2) ? LEVELY(BY2) : y,
+ sound_action);
+}
+
+static void PlayLevelSoundAction(int x, int y, int action)
+{
+ PlayLevelSoundElementAction(x, y, Feld[x][y], action);
+}
+
+static void PlayLevelSoundElementAction(int x, int y, int element, int action)
+{
+ int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
+
+ if (sound_effect != SND_UNDEFINED)
+ PlayLevelSound(x, y, sound_effect);
+}
+
+static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
+ int action)
+{
+ int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
+
+ if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
+ PlayLevelSound(x, y, sound_effect);
+}
+
+static void PlayLevelSoundActionIfLoop(int x, int y, int action)
+{
+ int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
+
+ if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
+ PlayLevelSound(x, y, sound_effect);
+}
+
+static void StopLevelSoundActionIfLoop(int x, int y, int action)
+{
+ int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
+
+ if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
+ StopSound(sound_effect);
+}
+
+static void PlayLevelMusic()
+{
+ if (levelset.music[level_nr] != MUS_UNDEFINED)
+ PlayMusic(levelset.music[level_nr]); /* from config file */
+ else
+ PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
+}
+
+void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
+{
+ int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
+ int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
+ int x = xx - 1 - offset;
+ int y = yy - 1 - offset;
+
+ switch (sample)
+ {
+ case SAMPLE_blank:
+ PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
+ break;
+
+ case SAMPLE_roll:
+ PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
+ break;
+
+ case SAMPLE_stone:
+ PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
+ break;
+
+ case SAMPLE_nut:
+ PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
+ break;
+
+ case SAMPLE_crack:
+ PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
+ break;
+
+ case SAMPLE_bug:
+ PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
+ break;
+
+ case SAMPLE_tank:
+ PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
+ break;
+
+ case SAMPLE_android_clone:
+ PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
+ break;
+
+ case SAMPLE_android_move:
+ PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
+ break;
+
+ case SAMPLE_spring:
+ PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
+ break;
+
+ case SAMPLE_slurp:
+ PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
+ break;
+
+ case SAMPLE_eater:
+ PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
+ break;
+
+ case SAMPLE_eater_eat:
+ PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
+ break;
+
+ case SAMPLE_alien:
+ PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
+ break;
+
+ case SAMPLE_collect:
+ PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
+ break;
+
+ case SAMPLE_diamond:
+ PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
+ break;
+
+ case SAMPLE_squash:
+ /* !!! CHECK THIS !!! */
+#if 1
+ PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
+#else
+ PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
+#endif
+ break;
+
+ case SAMPLE_wonderfall:
+ PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
+ break;
+
+ case SAMPLE_drip:
+ PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
+ break;
+
+ case SAMPLE_push:
+ PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
+ break;
+
+ case SAMPLE_dirt:
+ PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
+ break;
+
+ case SAMPLE_acid:
+ PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
+ break;
+
+ case SAMPLE_ball:
+ PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
+ break;
+
+ case SAMPLE_grow:
+ PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
+ break;
+
+ case SAMPLE_wonder:
+ PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
+ break;
+
+ case SAMPLE_door:
+ PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
+ break;
+
+ case SAMPLE_exit_open:
+ PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
+ break;
+
+ case SAMPLE_exit_leave:
+ PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
+ break;
+
+ case SAMPLE_dynamite:
+ PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
+ break;
+
+ case SAMPLE_tick:
+ PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
+ break;
+
+ case SAMPLE_press:
+ PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
+ break;
- if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
- return;
+ case SAMPLE_wheel:
+ PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
+ break;
- loop_sound_volume[nr] = volume;
- loop_sound_frame[nr] = FrameCounter;
- }
+ case SAMPLE_boom:
+ PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
+ break;
- PlaySoundExt(nr, volume, stereo_position, type);
-}
+ case SAMPLE_die:
+ PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
+ break;
-static void PlayLevelSoundNearest(int x, int y, int sound_action)
-{
- PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
- x > LEVELX(BX2) ? LEVELX(BX2) : x,
- y < LEVELY(BY1) ? LEVELY(BY1) :
- y > LEVELY(BY2) ? LEVELY(BY2) : y,
- sound_action);
-}
+ case SAMPLE_time:
+ PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
+ break;
-static void PlayLevelSoundAction(int x, int y, int action)
-{
- PlayLevelSoundElementAction(x, y, Feld[x][y], action);
+ default:
+ PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
+ break;
+ }
}
-static void PlayLevelSoundElementAction(int x, int y, int element, int action)
+void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
{
- int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
+ int element = map_element_SP_to_RND(element_sp);
+ int action = map_action_SP_to_RND(action_sp);
+ int offset = (setup.sp_show_border_elements ? 0 : 1);
+ int x = xx - offset;
+ int y = yy - offset;
- if (sound_effect != SND_UNDEFINED)
- PlayLevelSound(x, y, sound_effect);
+#if 0
+ printf("::: %d -> %d\n", element_sp, action_sp);
+#endif
+
+ PlayLevelSoundElementAction(x, y, element, action);
}
-static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
- int action)
+#if 0
+void ChangeTime(int value)
{
- int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
+ int *time = (level.time == 0 ? &TimePlayed : &TimeLeft);
- if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
- PlayLevelSound(x, y, sound_effect);
-}
+ *time += value;
-static void PlayLevelSoundActionIfLoop(int x, int y, int action)
-{
- int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
+ /* EMC game engine uses value from time counter of RND game engine */
+ level.native_em_level->lev->time = *time;
- if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
- PlayLevelSound(x, y, sound_effect);
+ DrawGameValue_Time(*time);
}
-static void StopLevelSoundActionIfLoop(int x, int y, int action)
+void RaiseScore(int value)
{
- int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
+ /* EMC game engine and RND game engine have separate score counters */
+ int *score = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
+ &level.native_em_level->lev->score : &local_player->score);
- if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
- StopSound(sound_effect);
-}
+ *score += value;
-static void PlayLevelMusic()
-{
- if (levelset.music[level_nr] != MUS_UNDEFINED)
- PlayMusic(levelset.music[level_nr]); /* from config file */
- else
- PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
+ DrawGameValue_Score(*score);
}
+#endif
void RaiseScore(int value)
{
local_player->score += value;
+#if 1
+ game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
+
+ DisplayGameControlValues();
+#else
DrawGameValue_Score(local_player->score);
+#endif
}
void RaiseScoreElement(int element)
{
- switch(element)
+ switch (element)
{
case EL_EMERALD:
case EL_BD_DIAMOND:
RaiseScore(level.score[SC_NUT]);
break;
case EL_DYNAMITE:
+ case EL_EM_DYNAMITE:
case EL_SP_DISK_RED:
case EL_DYNABOMB_INCREASE_NUMBER:
case EL_DYNABOMB_INCREASE_SIZE:
RaiseScore(level.score[SC_SHIELD]);
break;
case EL_EXTRA_TIME:
- RaiseScore(level.score[SC_TIME_BONUS]);
+ RaiseScore(level.extra_time_score);
break;
case EL_KEY_1:
case EL_KEY_2:
case EL_KEY_3:
case EL_KEY_4:
+ case EL_EM_KEY_1:
+ case EL_EM_KEY_2:
+ case EL_EM_KEY_3:
+ case EL_EM_KEY_4:
+ case EL_EMC_KEY_5:
+ 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)
- SendToServer_StopPlaying();
+ SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
else
#endif
{
- game_status = GAME_MODE_MAIN;
- DrawMainMenu();
+ if (quick_quit)
+ {
+#if 1
+
+#if 1
+ FadeSkipNextFadeIn();
+#else
+ fading = fading_none;
+#endif
+
+#else
+ OpenDoor(DOOR_CLOSE_1);
+#endif
+
+ game_status = GAME_MODE_MAIN;
+
+#if 1
+ DrawAndFadeInMainMenu(REDRAW_FIELD);
+#else
+ DrawMainMenu();
+#endif
+ }
+ else
+ {
+#if 0
+ FadeOut(REDRAW_FIELD);
+#endif
+
+ game_status = GAME_MODE_MAIN;
+
+ DrawAndFadeInMainMenu(REDRAW_FIELD);
+ }
}
}
- else
+ else /* continue playing the game */
{
-
-#if 1
if (tape.playing && tape.deactivate_display)
TapeDeactivateDisplayOff(TRUE);
-#endif
OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
-#if 1
if (tape.playing && tape.deactivate_display)
TapeDeactivateDisplayOn();
+ }
+}
+
+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 */
+/* ------------------------------------------------------------------------- */
+
+unsigned int InitEngineRandom_RND(long seed)
+{
+ game.num_random_calls = 0;
+
+#if 0
+ unsigned int rnd_seed = InitEngineRandom(seed);
+
+ printf("::: START RND: %d\n", rnd_seed);
+
+ return rnd_seed;
+#else
+
+ return InitEngineRandom(seed);
+
#endif
+}
+
+unsigned int RND(int max)
+{
+ if (max > 0)
+ {
+ game.num_random_calls++;
+
+ return GetEngineRandom(max);
}
+
+ return 0;
}
-/* ---------- new game button stuff ---------------------------------------- */
+/* ------------------------------------------------------------------------- */
+/* game engine snapshot handling functions */
+/* ------------------------------------------------------------------------- */
+
+struct EngineSnapshotInfo
+{
+ /* runtime values for custom element collect score */
+ int collect_score[NUM_CUSTOM_ELEMENTS];
+
+ /* runtime values for group element choice position */
+ int choice_pos[NUM_GROUP_ELEMENTS];
-/* graphic position values for game buttons */
-#define GAME_BUTTON_XSIZE 30
-#define GAME_BUTTON_YSIZE 30
-#define GAME_BUTTON_XPOS 5
-#define GAME_BUTTON_YPOS 215
-#define SOUND_BUTTON_XPOS 5
-#define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
-
-#define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
-#define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
-#define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
-#define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
-#define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
-#define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
+ /* runtime values for belt position animations */
+ int belt_graphic[4][NUM_BELT_PARTS];
+ int belt_anim_mode[4][NUM_BELT_PARTS];
+};
+
+static struct EngineSnapshotInfo engine_snapshot_rnd;
+static char *snapshot_level_identifier = NULL;
+static int snapshot_level_nr = -1;
+
+static void SaveEngineSnapshotValues_RND()
+{
+ static int belt_base_active_element[4] =
+ {
+ EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
+ EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
+ EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
+ EL_CONVEYOR_BELT_4_LEFT_ACTIVE
+ };
+ int i, j;
+
+ for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
+ {
+ int element = EL_CUSTOM_START + i;
+
+ engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
+ }
+
+ for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
+ {
+ int element = EL_GROUP_START + i;
+
+ engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
+ }
+
+ for (i = 0; i < 4; i++)
+ {
+ for (j = 0; j < NUM_BELT_PARTS; j++)
+ {
+ int element = belt_base_active_element[i] + j;
+ int graphic = el2img(element);
+ int anim_mode = graphic_info[graphic].anim_mode;
+
+ engine_snapshot_rnd.belt_graphic[i][j] = graphic;
+ engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
+ }
+ }
+}
+
+static void LoadEngineSnapshotValues_RND()
+{
+ unsigned long num_random_calls = game.num_random_calls;
+ int i, j;
+
+ for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
+ {
+ int element = EL_CUSTOM_START + i;
+
+ element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
+ }
+
+ for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
+ {
+ int element = EL_GROUP_START + i;
+
+ element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
+ }
+
+ for (i = 0; i < 4; i++)
+ {
+ for (j = 0; j < NUM_BELT_PARTS; j++)
+ {
+ int graphic = engine_snapshot_rnd.belt_graphic[i][j];
+ int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
+
+ graphic_info[graphic].anim_mode = anim_mode;
+ }
+ }
+
+ if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
+ {
+ InitRND(tape.random_seed);
+ for (i = 0; i < num_random_calls; i++)
+ RND(1);
+ }
+
+ if (game.num_random_calls != num_random_calls)
+ {
+ Error(ERR_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");
+ }
+}
+
+void SaveEngineSnapshot()
+{
+ /* do not save snapshots from editor */
+ if (level_editor_test_game)
+ return;
+
+ /* free previous snapshot buffers, if needed */
+ FreeEngineSnapshotBuffers();
+
+ /* copy some special values to a structure better suited for the snapshot */
+
+ SaveEngineSnapshotValues_RND();
+ SaveEngineSnapshotValues_EM();
+ SaveEngineSnapshotValues_SP();
+
+ /* save values stored in special snapshot structure */
+
+ SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
+ SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
+ SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
+
+ /* save further RND engine values */
+
+ SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
+ SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
+ SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
+
+ SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
+ SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
+ SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
+ SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
+
+ SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
+ SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
+ SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
+ SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
+ SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
+
+ SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
+ SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
+ SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
+
+ SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
+
+ SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
+
+ SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
+ SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
+
+ SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
+ SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
+ SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
+ SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
+ SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
+ SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
+ SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
+ SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
+ SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
+ SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
+ SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
+ SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
+ 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));
+
+ SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
+ SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
+
+ SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
+ SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
+ SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
+
+ SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
+ SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
+
+ SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
+ SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
+ SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
+ SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
+ SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
+
+ SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
+ SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
+
+ /* save level identification information */
+
+ setString(&snapshot_level_identifier, leveldir_current->identifier);
+ snapshot_level_nr = level_nr;
+
+#if 0
+ ListNode *node = engine_snapshot_list_rnd;
+ int num_bytes = 0;
+
+ while (node != NULL)
+ {
+ num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
+
+ node = node->next;
+ }
+
+ printf("::: size of engine snapshot: %d bytes\n", num_bytes);
+#endif
+}
+
+void LoadEngineSnapshot()
+{
+ /* restore generically stored snapshot buffers */
+
+ LoadEngineSnapshotBuffers();
+
+ /* restore special values from snapshot structure */
+
+ LoadEngineSnapshotValues_RND();
+ LoadEngineSnapshotValues_EM();
+ LoadEngineSnapshotValues_SP();
+}
+
+boolean CheckEngineSnapshot()
+{
+ return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
+ snapshot_level_nr == level_nr);
+}
+
+
+/* ---------- new game button stuff ---------------------------------------- */
static struct
{
- int x, y;
+ int graphic;
+ struct Rect *pos;
int gadget_id;
char *infotext;
} gamebutton_info[NUM_GAME_BUTTONS] =
{
{
- GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
- GAME_CTRL_ID_STOP,
- "stop game"
+ IMG_GAME_BUTTON_GFX_STOP, &game.button.stop,
+ GAME_CTRL_ID_STOP, "stop game"
},
{
- GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
- GAME_CTRL_ID_PAUSE,
- "pause game"
+ IMG_GAME_BUTTON_GFX_PAUSE, &game.button.pause,
+ GAME_CTRL_ID_PAUSE, "pause game"
},
{
- GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
- GAME_CTRL_ID_PLAY,
- "play game"
+ IMG_GAME_BUTTON_GFX_PLAY, &game.button.play,
+ GAME_CTRL_ID_PLAY, "play game"
},
{
- SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
- SOUND_CTRL_ID_MUSIC,
- "background music on/off"
+ IMG_GAME_BUTTON_GFX_SOUND_MUSIC, &game.button.sound_music,
+ SOUND_CTRL_ID_MUSIC, "background music on/off"
},
{
- SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
- SOUND_CTRL_ID_LOOPS,
- "sound loops on/off"
+ IMG_GAME_BUTTON_GFX_SOUND_LOOPS, &game.button.sound_loops,
+ SOUND_CTRL_ID_LOOPS, "sound loops on/off"
},
{
- SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
- SOUND_CTRL_ID_SIMPLE,
- "normal sounds on/off"
+ IMG_GAME_BUTTON_GFX_SOUND_SIMPLE, &game.button.sound_simple,
+ SOUND_CTRL_ID_SIMPLE, "normal sounds on/off"
}
};
for (i = 0; i < NUM_GAME_BUTTONS; i++)
{
- Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
+ struct GraphicInfo *gfx = &graphic_info[gamebutton_info[i].graphic];
+ struct Rect *pos = gamebutton_info[i].pos;
struct GadgetInfo *gi;
int button_type;
boolean checked;
unsigned long event_mask;
- int gd_xoffset, gd_yoffset;
- int gd_x1, gd_x2, gd_y1, gd_y2;
+ int gd_x = gfx->src_x;
+ int gd_y = gfx->src_y;
+ int gd_xp = gfx->src_x + gfx->pressed_xoffset;
+ int gd_yp = gfx->src_y + gfx->pressed_yoffset;
+ int gd_xa = gfx->src_x + gfx->active_xoffset;
+ int gd_ya = gfx->src_y + gfx->active_yoffset;
+ int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
+ int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
int id = i;
- gd_xoffset = gamebutton_info[i].x;
- gd_yoffset = gamebutton_info[i].y;
- gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
- gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
-
if (id == GAME_CTRL_ID_STOP ||
id == GAME_CTRL_ID_PAUSE ||
id == GAME_CTRL_ID_PLAY)
button_type = GD_TYPE_NORMAL_BUTTON;
checked = FALSE;
event_mask = GD_EVENT_RELEASED;
- gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
- gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
}
else
{
(id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
(id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
event_mask = GD_EVENT_PRESSED;
- gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
- gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
}
gi = CreateGadget(GDI_CUSTOM_ID, id,
GDI_INFO_TEXT, gamebutton_info[i].infotext,
- GDI_X, DX + gd_xoffset,
- GDI_Y, DY + gd_yoffset,
- GDI_WIDTH, GAME_BUTTON_XSIZE,
- GDI_HEIGHT, GAME_BUTTON_YSIZE,
+ GDI_X, DX + pos->x,
+ GDI_Y, DY + pos->y,
+ GDI_WIDTH, gfx->width,
+ GDI_HEIGHT, gfx->height,
GDI_TYPE, button_type,
GDI_STATE, GD_BUTTON_UNPRESSED,
GDI_CHECKED, checked,
- GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
- GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
- GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
- GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
+ GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
+ GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
+ GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
+ GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
+ GDI_DIRECT_DRAW, FALSE,
GDI_EVENT_MASK, event_mask,
GDI_CALLBACK_ACTION, HandleGameButtons,
GDI_END);
UnmapGadget(game_gadget[i]);
}
-static void HandleGameButtons(struct GadgetInfo *gi)
+void RedrawGameButtons()
{
- int id = gi->custom_id;
+ int i;
+
+ for (i = 0; i < NUM_GAME_BUTTONS; i++)
+ RedrawGadget(game_gadget[i]);
+}
+static void HandleGameButtonsExt(int id)
+{
if (game_status != GAME_MODE_PLAYING)
return;
switch (id)
{
case GAME_CTRL_ID_STOP:
- RequestQuitGame(TRUE);
+ if (tape.playing)
+ TapeStop();
+ else
+ RequestQuitGame(TRUE);
break;
case GAME_CTRL_ID_PAUSE:
#endif
{
tape.pausing = FALSE;
- DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
+ DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
}
}
break;
if (setup.sound_music)
{
setup.sound_music = FALSE;
+
FadeMusic();
}
else if (audio.music_available)
else if (audio.loops_available)
{
setup.sound = setup.sound_loops = TRUE;
+
SetAudioMode(setup.sound);
}
break;
else if (audio.sound_available)
{
setup.sound = setup.sound_simple = TRUE;
+
SetAudioMode(setup.sound);
}
break;
break;
}
}
+
+static void HandleGameButtons(struct GadgetInfo *gi)
+{
+ HandleGameButtonsExt(gi->custom_id);
+}
+
+void HandleSoundButtonKeys(Key key)
+{
+#if 1
+ if (key == setup.shortcut.sound_simple)
+ ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
+ else if (key == setup.shortcut.sound_loops)
+ ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
+ else if (key == setup.shortcut.sound_music)
+ ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
+#else
+ if (key == setup.shortcut.sound_simple)
+ HandleGameButtonsExt(SOUND_CTRL_ID_SIMPLE);
+ else if (key == setup.shortcut.sound_loops)
+ HandleGameButtonsExt(SOUND_CTRL_ID_LOOPS);
+ else if (key == setup.shortcut.sound_music)
+ HandleGameButtonsExt(SOUND_CTRL_ID_MUSIC);
+#endif
+}