1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back! *
3 *----------------------------------------------------------*
4 * (c) 1995-2002 Artsoft Entertainment *
6 * Detmolder Strasse 189 *
9 * e-mail: info@artsoft.org *
10 *----------------------------------------------------------*
12 ***********************************************************/
14 #include "libgame/libgame.h"
24 /* EXPERIMENTAL STUFF */
25 #define USE_NEW_AMOEBA_CODE FALSE
27 /* EXPERIMENTAL STUFF */
28 #define USE_NEW_STUFF ( 1)
30 #define USE_NEW_SP_SLIPPERY (USE_NEW_STUFF * 1)
31 #define USE_NEW_CUSTOM_VALUE (USE_NEW_STUFF * 1)
32 #define USE_NEW_PLAYER_ANIM (USE_NEW_STUFF * 1)
33 #define USE_NEW_ALL_SLIPPERY (USE_NEW_STUFF * 1)
34 #define USE_NEW_PLAYER_SPEED (USE_NEW_STUFF * 1)
35 #define USE_NEW_DELAYED_ACTION (USE_NEW_STUFF * 1)
36 #define USE_NEW_SNAP_DELAY (USE_NEW_STUFF * 1)
37 #define USE_ONLY_ONE_CHANGE_PER_FRAME (USE_NEW_STUFF * 1)
38 #define USE_ONE_MORE_CHANGE_PER_FRAME (USE_NEW_STUFF * 1)
39 #define USE_FIXED_DONT_RUN_INTO (USE_NEW_STUFF * 1)
40 #define USE_NEW_SPRING_BUMPER (USE_NEW_STUFF * 1)
41 #define USE_STOP_CHANGED_ELEMENTS (USE_NEW_STUFF * 1)
42 #define USE_ELEMENT_TOUCHING_BUGFIX (USE_NEW_STUFF * 1)
43 #define USE_NEW_CONTINUOUS_SNAPPING (USE_NEW_STUFF * 1)
44 #define USE_GFX_RESET_GFX_ANIMATION (USE_NEW_STUFF * 1)
45 #define USE_BOTH_SWITCHGATE_SWITCHES (USE_NEW_STUFF * 1)
46 #define USE_PLAYER_GRAVITY (USE_NEW_STUFF * 1)
47 #define USE_FIXED_BORDER_RUNNING_GFX (USE_NEW_STUFF * 1)
48 #define USE_QUICKSAND_BD_ROCK_BUGFIX (USE_NEW_STUFF * 0)
50 #define USE_QUICKSAND_IMPACT_BUGFIX (USE_NEW_STUFF * 0)
52 #define USE_CODE_THAT_BREAKS_SNAKE_BITE (USE_NEW_STUFF * 1)
54 #define USE_UFAST_PLAYER_EXIT_BUGFIX (USE_NEW_STUFF * 1)
55 #define USE_NEW_GAME_WON (USE_NEW_STUFF * 1)
63 /* for MovePlayer() */
64 #define MP_NO_ACTION 0
67 #define MP_DONT_RUN_INTO (MP_MOVING | MP_ACTION)
69 /* for ScrollPlayer() */
71 #define SCROLL_GO_ON 1
73 /* for Bang()/Explode() */
74 #define EX_PHASE_START 0
75 #define EX_TYPE_NONE 0
76 #define EX_TYPE_NORMAL (1 << 0)
77 #define EX_TYPE_CENTER (1 << 1)
78 #define EX_TYPE_BORDER (1 << 2)
79 #define EX_TYPE_CROSS (1 << 3)
80 #define EX_TYPE_DYNA (1 << 4)
81 #define EX_TYPE_SINGLE_TILE (EX_TYPE_CENTER | EX_TYPE_BORDER)
83 #define PANEL_DEACTIVATED(p) ((p).x < 0 || (p).y < 0)
85 /* special positions in the game control window (relative to control window) */
86 #define XX_LEVEL1 (game.panel.level.x)
87 #define XX_LEVEL2 (game.panel.level.x - 1)
88 #define YY_LEVEL (game.panel.level.y)
89 #define XX_EMERALDS (game.panel.gems.x)
90 #define YY_EMERALDS (game.panel.gems.y)
91 #define XX_DYNAMITE (game.panel.inventory.x)
92 #define YY_DYNAMITE (game.panel.inventory.y)
93 #define XX_KEYS (game.panel.keys.x)
94 #define YY_KEYS (game.panel.keys.y)
95 #define XX_SCORE (game.panel.score.x)
96 #define YY_SCORE (game.panel.score.y)
97 #define XX_TIME1 (game.panel.time.x)
98 #define XX_TIME2 (game.panel.time.x + 1)
99 #define YY_TIME (game.panel.time.y)
101 /* special positions in the game control window (relative to main window) */
102 #define DX_LEVEL1 (DX + XX_LEVEL1)
103 #define DX_LEVEL2 (DX + XX_LEVEL2)
104 #define DY_LEVEL (DY + YY_LEVEL)
105 #define DX_EMERALDS (DX + XX_EMERALDS)
106 #define DY_EMERALDS (DY + YY_EMERALDS)
107 #define DX_DYNAMITE (DX + XX_DYNAMITE)
108 #define DY_DYNAMITE (DY + YY_DYNAMITE)
109 #define DX_KEYS (DX + XX_KEYS)
110 #define DY_KEYS (DY + YY_KEYS)
111 #define DX_SCORE (DX + XX_SCORE)
112 #define DY_SCORE (DY + YY_SCORE)
113 #define DX_TIME1 (DX + XX_TIME1)
114 #define DX_TIME2 (DX + XX_TIME2)
115 #define DY_TIME (DY + YY_TIME)
117 /* values for delayed check of falling and moving elements and for collision */
118 #define CHECK_DELAY_MOVING 3
119 #define CHECK_DELAY_FALLING 3
120 #define CHECK_DELAY_COLLISION 2
122 /* values for initial player move delay (initial delay counter value) */
123 #define INITIAL_MOVE_DELAY_OFF -1
124 #define INITIAL_MOVE_DELAY_ON 0
126 /* values for player movement speed (which is in fact a delay value) */
127 #define MOVE_DELAY_MIN_SPEED 32
128 #define MOVE_DELAY_NORMAL_SPEED 8
129 #define MOVE_DELAY_HIGH_SPEED 4
130 #define MOVE_DELAY_MAX_SPEED 1
132 #define DOUBLE_MOVE_DELAY(x) (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
133 #define HALVE_MOVE_DELAY(x) (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
135 #define DOUBLE_PLAYER_SPEED(p) (HALVE_MOVE_DELAY( (p)->move_delay_value))
136 #define HALVE_PLAYER_SPEED(p) (DOUBLE_MOVE_DELAY((p)->move_delay_value))
138 /* values for other actions */
139 #define MOVE_STEPSIZE_NORMAL (TILEX / MOVE_DELAY_NORMAL_SPEED)
140 #define MOVE_STEPSIZE_MIN (1)
141 #define MOVE_STEPSIZE_MAX (TILEX)
143 #define GET_DX_FROM_DIR(d) ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
144 #define GET_DY_FROM_DIR(d) ((d) == MV_UP ? -1 : (d) == MV_DOWN ? 1 : 0)
146 #define INIT_GFX_RANDOM() (SimpleRND(1000000))
148 #define GET_NEW_PUSH_DELAY(e) ( (element_info[e].push_delay_fixed) + \
149 RND(element_info[e].push_delay_random))
150 #define GET_NEW_DROP_DELAY(e) ( (element_info[e].drop_delay_fixed) + \
151 RND(element_info[e].drop_delay_random))
152 #define GET_NEW_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
153 RND(element_info[e].move_delay_random))
154 #define GET_MAX_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
155 (element_info[e].move_delay_random))
156 #define GET_NEW_CE_VALUE(e) ( (element_info[e].ce_value_fixed_initial) +\
157 RND(element_info[e].ce_value_random_initial))
158 #define GET_CE_SCORE(e) ( (element_info[e].collect_score))
159 #define GET_CHANGE_DELAY(c) ( ((c)->delay_fixed * (c)->delay_frames) + \
160 RND((c)->delay_random * (c)->delay_frames))
161 #define GET_CE_DELAY_VALUE(c) ( ((c)->delay_fixed) + \
162 RND((c)->delay_random))
165 #define GET_VALID_RUNTIME_ELEMENT(e) \
166 ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
168 #define RESOLVED_REFERENCE_ELEMENT(be, e) \
169 ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START : \
170 (be) + (e) - EL_SELF > EL_CUSTOM_END ? EL_CUSTOM_END : \
171 (be) + (e) - EL_SELF)
173 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs) \
174 ((e) == EL_TRIGGER_PLAYER ? (ch)->actual_trigger_player : \
175 (e) == EL_TRIGGER_ELEMENT ? (ch)->actual_trigger_element : \
176 (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value : \
177 (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score : \
178 (e) == EL_CURRENT_CE_VALUE ? (cv) : \
179 (e) == EL_CURRENT_CE_SCORE ? (cs) : \
180 (e) >= EL_LAST_CE_8 && (e) <= EL_NEXT_CE_8 ? \
181 RESOLVED_REFERENCE_ELEMENT(be, e) : \
184 #define CAN_GROW_INTO(e) \
185 ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
187 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition) \
188 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
191 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition) \
192 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
193 (CAN_MOVE_INTO_ACID(e) && \
194 Feld[x][y] == EL_ACID) || \
197 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition) \
198 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
199 (CAN_MOVE_INTO_ACID(e) && \
200 Feld[x][y] == EL_ACID) || \
203 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition) \
204 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
206 (CAN_MOVE_INTO_ACID(e) && \
207 Feld[x][y] == EL_ACID) || \
208 (DONT_COLLIDE_WITH(e) && \
210 !PLAYER_ENEMY_PROTECTED(x, y))))
212 #define ELEMENT_CAN_ENTER_FIELD(e, x, y) \
213 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
215 #define SATELLITE_CAN_ENTER_FIELD(x, y) \
216 ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
218 #define ANDROID_CAN_ENTER_FIELD(e, x, y) \
219 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
221 #define ANDROID_CAN_CLONE_FIELD(x, y) \
222 (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
223 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
225 #define ENEMY_CAN_ENTER_FIELD(e, x, y) \
226 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
228 #define YAMYAM_CAN_ENTER_FIELD(e, x, y) \
229 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
231 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y) \
232 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
234 #define PACMAN_CAN_ENTER_FIELD(e, x, y) \
235 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
237 #define PIG_CAN_ENTER_FIELD(e, x, y) \
238 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
240 #define PENGUIN_CAN_ENTER_FIELD(e, x, y) \
241 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN ||\
242 IS_FOOD_PENGUIN(Feld[x][y])))
243 #define DRAGON_CAN_ENTER_FIELD(e, x, y) \
244 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
246 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition) \
247 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
249 #define SPRING_CAN_ENTER_FIELD(e, x, y) \
250 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
252 #define SPRING_CAN_BUMP_FROM_FIELD(x, y) \
253 (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER || \
254 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
256 #define MOVE_ENTER_EL(e) (element_info[e].move_enter_element)
258 #define CE_ENTER_FIELD_COND(e, x, y) \
259 (!IS_PLAYER(x, y) && \
260 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
262 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y) \
263 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
265 #define IN_LEV_FIELD_AND_IS_FREE(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
266 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
268 #define ACCESS_FROM(e, d) (element_info[e].access_direction &(d))
269 #define IS_WALKABLE_FROM(e, d) (IS_WALKABLE(e) && ACCESS_FROM(e, d))
270 #define IS_PASSABLE_FROM(e, d) (IS_PASSABLE(e) && ACCESS_FROM(e, d))
271 #define IS_ACCESSIBLE_FROM(e, d) (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
273 /* game button identifiers */
274 #define GAME_CTRL_ID_STOP 0
275 #define GAME_CTRL_ID_PAUSE 1
276 #define GAME_CTRL_ID_PLAY 2
277 #define SOUND_CTRL_ID_MUSIC 3
278 #define SOUND_CTRL_ID_LOOPS 4
279 #define SOUND_CTRL_ID_SIMPLE 5
281 #define NUM_GAME_BUTTONS 6
284 /* forward declaration for internal use */
286 static void CreateField(int, int, int);
288 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
289 static void AdvanceFrameAndPlayerCounters(int);
291 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
292 static boolean MovePlayer(struct PlayerInfo *, int, int);
293 static void ScrollPlayer(struct PlayerInfo *, int);
294 static void ScrollScreen(struct PlayerInfo *, int);
296 int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
298 static void InitBeltMovement(void);
299 static void CloseAllOpenTimegates(void);
300 static void CheckGravityMovement(struct PlayerInfo *);
301 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
302 static void KillPlayerUnlessEnemyProtected(int, int);
303 static void KillPlayerUnlessExplosionProtected(int, int);
305 static void TestIfPlayerTouchesCustomElement(int, int);
306 static void TestIfElementTouchesCustomElement(int, int);
307 static void TestIfElementHitsCustomElement(int, int, int);
309 static void TestIfElementSmashesCustomElement(int, int, int);
312 static void HandleElementChange(int, int, int);
313 static void ExecuteCustomElementAction(int, int, int, int);
314 static boolean ChangeElement(int, int, int, int);
316 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
317 #define CheckTriggeredElementChange(x, y, e, ev) \
318 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
319 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s) \
320 CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
321 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s) \
322 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
323 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p) \
324 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
326 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
327 #define CheckElementChange(x, y, e, te, ev) \
328 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
329 #define CheckElementChangeByPlayer(x, y, e, ev, p, s) \
330 CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
331 #define CheckElementChangeBySide(x, y, e, te, ev, s) \
332 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
334 static void PlayLevelSound(int, int, int);
335 static void PlayLevelSoundNearest(int, int, int);
336 static void PlayLevelSoundAction(int, int, int);
337 static void PlayLevelSoundElementAction(int, int, int, int);
338 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
339 static void PlayLevelSoundActionIfLoop(int, int, int);
340 static void StopLevelSoundActionIfLoop(int, int, int);
341 static void PlayLevelMusic();
343 static void MapGameButtons();
344 static void HandleGameButtons(struct GadgetInfo *);
346 int AmoebeNachbarNr(int, int);
347 void AmoebeUmwandeln(int, int);
348 void ContinueMoving(int, int);
350 void InitMovDir(int, int);
351 void InitAmoebaNr(int, int);
352 int NewHiScore(void);
354 void TestIfGoodThingHitsBadThing(int, int, int);
355 void TestIfBadThingHitsGoodThing(int, int, int);
356 void TestIfPlayerTouchesBadThing(int, int);
357 void TestIfPlayerRunsIntoBadThing(int, int, int);
358 void TestIfBadThingTouchesPlayer(int, int);
359 void TestIfBadThingRunsIntoPlayer(int, int, int);
360 void TestIfFriendTouchesBadThing(int, int);
361 void TestIfBadThingTouchesFriend(int, int);
362 void TestIfBadThingTouchesOtherBadThing(int, int);
364 void KillPlayer(struct PlayerInfo *);
365 void BuryPlayer(struct PlayerInfo *);
366 void RemovePlayer(struct PlayerInfo *);
368 boolean SnapField(struct PlayerInfo *, int, int);
369 boolean DropElement(struct PlayerInfo *);
371 static int getInvisibleActiveFromInvisibleElement(int);
372 static int getInvisibleFromInvisibleActiveElement(int);
374 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
377 /* ------------------------------------------------------------------------- */
378 /* definition of elements that automatically change to other elements after */
379 /* a specified time, eventually calling a function when changing */
380 /* ------------------------------------------------------------------------- */
382 /* forward declaration for changer functions */
383 static void InitBuggyBase(int, int);
384 static void WarnBuggyBase(int, int);
386 static void InitTrap(int, int);
387 static void ActivateTrap(int, int);
388 static void ChangeActiveTrap(int, int);
390 static void InitRobotWheel(int, int);
391 static void RunRobotWheel(int, int);
392 static void StopRobotWheel(int, int);
394 static void InitTimegateWheel(int, int);
395 static void RunTimegateWheel(int, int);
397 static void InitMagicBallDelay(int, int);
398 static void ActivateMagicBall(int, int);
400 struct ChangingElementInfo
405 void (*pre_change_function)(int x, int y);
406 void (*change_function)(int x, int y);
407 void (*post_change_function)(int x, int y);
410 static struct ChangingElementInfo change_delay_list[] =
461 EL_SWITCHGATE_OPENING,
469 EL_SWITCHGATE_CLOSING,
470 EL_SWITCHGATE_CLOSED,
502 EL_ACID_SPLASH_RIGHT,
511 EL_SP_BUGGY_BASE_ACTIVATING,
518 EL_SP_BUGGY_BASE_ACTIVATING,
519 EL_SP_BUGGY_BASE_ACTIVE,
526 EL_SP_BUGGY_BASE_ACTIVE,
550 EL_ROBOT_WHEEL_ACTIVE,
558 EL_TIMEGATE_SWITCH_ACTIVE,
566 EL_EMC_MAGIC_BALL_ACTIVE,
567 EL_EMC_MAGIC_BALL_ACTIVE,
574 EL_EMC_SPRING_BUMPER_ACTIVE,
575 EL_EMC_SPRING_BUMPER,
582 EL_DIAGONAL_SHRINKING,
611 int push_delay_fixed, push_delay_random;
616 { EL_BALLOON, 0, 0 },
618 { EL_SOKOBAN_OBJECT, 2, 0 },
619 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
620 { EL_SATELLITE, 2, 0 },
621 { EL_SP_DISK_YELLOW, 2, 0 },
623 { EL_UNDEFINED, 0, 0 },
631 move_stepsize_list[] =
633 { EL_AMOEBA_DROP, 2 },
634 { EL_AMOEBA_DROPPING, 2 },
635 { EL_QUICKSAND_FILLING, 1 },
636 { EL_QUICKSAND_EMPTYING, 1 },
637 { EL_MAGIC_WALL_FILLING, 2 },
638 { EL_BD_MAGIC_WALL_FILLING, 2 },
639 { EL_MAGIC_WALL_EMPTYING, 2 },
640 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
650 collect_count_list[] =
653 { EL_BD_DIAMOND, 1 },
654 { EL_EMERALD_YELLOW, 1 },
655 { EL_EMERALD_RED, 1 },
656 { EL_EMERALD_PURPLE, 1 },
658 { EL_SP_INFOTRON, 1 },
670 access_direction_list[] =
672 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
673 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
674 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
675 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
676 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
677 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
678 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
679 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
680 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
681 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
682 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
684 { EL_SP_PORT_LEFT, MV_RIGHT },
685 { EL_SP_PORT_RIGHT, MV_LEFT },
686 { EL_SP_PORT_UP, MV_DOWN },
687 { EL_SP_PORT_DOWN, MV_UP },
688 { EL_SP_PORT_HORIZONTAL, MV_LEFT | MV_RIGHT },
689 { EL_SP_PORT_VERTICAL, MV_UP | MV_DOWN },
690 { EL_SP_PORT_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
691 { EL_SP_GRAVITY_PORT_LEFT, MV_RIGHT },
692 { EL_SP_GRAVITY_PORT_RIGHT, MV_LEFT },
693 { EL_SP_GRAVITY_PORT_UP, MV_DOWN },
694 { EL_SP_GRAVITY_PORT_DOWN, MV_UP },
695 { EL_SP_GRAVITY_ON_PORT_LEFT, MV_RIGHT },
696 { EL_SP_GRAVITY_ON_PORT_RIGHT, MV_LEFT },
697 { EL_SP_GRAVITY_ON_PORT_UP, MV_DOWN },
698 { EL_SP_GRAVITY_ON_PORT_DOWN, MV_UP },
699 { EL_SP_GRAVITY_OFF_PORT_LEFT, MV_RIGHT },
700 { EL_SP_GRAVITY_OFF_PORT_RIGHT, MV_LEFT },
701 { EL_SP_GRAVITY_OFF_PORT_UP, MV_DOWN },
702 { EL_SP_GRAVITY_OFF_PORT_DOWN, MV_UP },
704 { EL_UNDEFINED, MV_NONE }
707 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
709 #define IS_AUTO_CHANGING(e) (element_info[e].has_change_event[CE_DELAY])
710 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
711 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
712 IS_JUST_CHANGING(x, y))
714 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
716 /* static variables for playfield scan mode (scanning forward or backward) */
717 static int playfield_scan_start_x = 0;
718 static int playfield_scan_start_y = 0;
719 static int playfield_scan_delta_x = 1;
720 static int playfield_scan_delta_y = 1;
722 #define SCAN_PLAYFIELD(x, y) for ((y) = playfield_scan_start_y; \
723 (y) >= 0 && (y) <= lev_fieldy - 1; \
724 (y) += playfield_scan_delta_y) \
725 for ((x) = playfield_scan_start_x; \
726 (x) >= 0 && (x) <= lev_fieldx - 1; \
727 (x) += playfield_scan_delta_x) \
730 void DEBUG_SetMaximumDynamite()
734 for (i = 0; i < MAX_INVENTORY_SIZE; i++)
735 if (local_player->inventory_size < MAX_INVENTORY_SIZE)
736 local_player->inventory_element[local_player->inventory_size++] =
741 static void InitPlayfieldScanModeVars()
743 if (game.use_reverse_scan_direction)
745 playfield_scan_start_x = lev_fieldx - 1;
746 playfield_scan_start_y = lev_fieldy - 1;
748 playfield_scan_delta_x = -1;
749 playfield_scan_delta_y = -1;
753 playfield_scan_start_x = 0;
754 playfield_scan_start_y = 0;
756 playfield_scan_delta_x = 1;
757 playfield_scan_delta_y = 1;
761 static void InitPlayfieldScanMode(int mode)
763 game.use_reverse_scan_direction =
764 (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
766 InitPlayfieldScanModeVars();
769 static int get_move_delay_from_stepsize(int move_stepsize)
772 MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
774 /* make sure that stepsize value is always a power of 2 */
775 move_stepsize = (1 << log_2(move_stepsize));
777 return TILEX / move_stepsize;
780 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
783 int player_nr = player->index_nr;
784 int move_delay = get_move_delay_from_stepsize(move_stepsize);
785 boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
787 /* do no immediately change move delay -- the player might just be moving */
788 player->move_delay_value_next = move_delay;
790 /* information if player can move must be set separately */
791 player->cannot_move = cannot_move;
795 player->move_delay = game.initial_move_delay[player_nr];
796 player->move_delay_value = game.initial_move_delay_value[player_nr];
798 player->move_delay_value_next = -1;
800 player->move_delay_reset_counter = 0;
804 void GetPlayerConfig()
806 if (!audio.sound_available)
807 setup.sound_simple = FALSE;
809 if (!audio.loops_available)
810 setup.sound_loops = FALSE;
812 if (!audio.music_available)
813 setup.sound_music = FALSE;
815 if (!video.fullscreen_available)
816 setup.fullscreen = FALSE;
818 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
820 SetAudioMode(setup.sound);
824 static int getBeltNrFromBeltElement(int element)
826 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
827 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
828 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
831 static int getBeltNrFromBeltActiveElement(int element)
833 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
834 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
835 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
838 static int getBeltNrFromBeltSwitchElement(int element)
840 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
841 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
842 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
845 static int getBeltDirNrFromBeltSwitchElement(int element)
847 static int belt_base_element[4] =
849 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
850 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
851 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
852 EL_CONVEYOR_BELT_4_SWITCH_LEFT
855 int belt_nr = getBeltNrFromBeltSwitchElement(element);
856 int belt_dir_nr = element - belt_base_element[belt_nr];
858 return (belt_dir_nr % 3);
861 static int getBeltDirFromBeltSwitchElement(int element)
863 static int belt_move_dir[3] =
870 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
872 return belt_move_dir[belt_dir_nr];
875 static int get_element_from_group_element(int element)
877 if (IS_GROUP_ELEMENT(element))
879 struct ElementGroupInfo *group = element_info[element].group;
880 int last_anim_random_frame = gfx.anim_random_frame;
883 if (group->choice_mode == ANIM_RANDOM)
884 gfx.anim_random_frame = RND(group->num_elements_resolved);
886 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
887 group->choice_mode, 0,
890 if (group->choice_mode == ANIM_RANDOM)
891 gfx.anim_random_frame = last_anim_random_frame;
895 element = group->element_resolved[element_pos];
901 static void InitPlayerField(int x, int y, int element, boolean init_game)
903 if (element == EL_SP_MURPHY)
907 if (stored_player[0].present)
909 Feld[x][y] = EL_SP_MURPHY_CLONE;
915 stored_player[0].use_murphy = TRUE;
917 if (!level.use_artwork_element[0])
918 stored_player[0].artwork_element = EL_SP_MURPHY;
921 Feld[x][y] = EL_PLAYER_1;
927 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
928 int jx = player->jx, jy = player->jy;
930 player->present = TRUE;
932 player->block_last_field = (element == EL_SP_MURPHY ?
933 level.sp_block_last_field :
934 level.block_last_field);
936 /* ---------- initialize player's last field block delay --------------- */
938 /* always start with reliable default value (no adjustment needed) */
939 player->block_delay_adjustment = 0;
941 /* special case 1: in Supaplex, Murphy blocks last field one more frame */
942 if (player->block_last_field && element == EL_SP_MURPHY)
943 player->block_delay_adjustment = 1;
945 /* special case 2: in game engines before 3.1.1, blocking was different */
946 if (game.use_block_last_field_bug)
947 player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
949 if (!options.network || player->connected)
951 player->active = TRUE;
953 /* remove potentially duplicate players */
954 if (StorePlayer[jx][jy] == Feld[x][y])
955 StorePlayer[jx][jy] = 0;
957 StorePlayer[x][y] = Feld[x][y];
961 printf("Player %d activated.\n", player->element_nr);
962 printf("[Local player is %d and currently %s.]\n",
963 local_player->element_nr,
964 local_player->active ? "active" : "not active");
968 Feld[x][y] = EL_EMPTY;
970 player->jx = player->last_jx = x;
971 player->jy = player->last_jy = y;
975 static void InitField(int x, int y, boolean init_game)
977 int element = Feld[x][y];
986 InitPlayerField(x, y, element, init_game);
989 case EL_SOKOBAN_FIELD_PLAYER:
990 element = Feld[x][y] = EL_PLAYER_1;
991 InitField(x, y, init_game);
993 element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
994 InitField(x, y, init_game);
997 case EL_SOKOBAN_FIELD_EMPTY:
998 local_player->sokobanfields_still_needed++;
1002 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1003 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1004 else if (x > 0 && Feld[x-1][y] == EL_ACID)
1005 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1006 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1007 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1008 else if (y > 0 && Feld[x][y-1] == EL_ACID)
1009 Feld[x][y] = EL_ACID_POOL_BOTTOM;
1010 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1011 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1020 case EL_SPACESHIP_RIGHT:
1021 case EL_SPACESHIP_UP:
1022 case EL_SPACESHIP_LEFT:
1023 case EL_SPACESHIP_DOWN:
1024 case EL_BD_BUTTERFLY:
1025 case EL_BD_BUTTERFLY_RIGHT:
1026 case EL_BD_BUTTERFLY_UP:
1027 case EL_BD_BUTTERFLY_LEFT:
1028 case EL_BD_BUTTERFLY_DOWN:
1030 case EL_BD_FIREFLY_RIGHT:
1031 case EL_BD_FIREFLY_UP:
1032 case EL_BD_FIREFLY_LEFT:
1033 case EL_BD_FIREFLY_DOWN:
1034 case EL_PACMAN_RIGHT:
1036 case EL_PACMAN_LEFT:
1037 case EL_PACMAN_DOWN:
1039 case EL_YAMYAM_LEFT:
1040 case EL_YAMYAM_RIGHT:
1042 case EL_YAMYAM_DOWN:
1043 case EL_DARK_YAMYAM:
1046 case EL_SP_SNIKSNAK:
1047 case EL_SP_ELECTRON:
1056 case EL_AMOEBA_FULL:
1061 case EL_AMOEBA_DROP:
1062 if (y == lev_fieldy - 1)
1064 Feld[x][y] = EL_AMOEBA_GROWING;
1065 Store[x][y] = EL_AMOEBA_WET;
1069 case EL_DYNAMITE_ACTIVE:
1070 case EL_SP_DISK_RED_ACTIVE:
1071 case EL_DYNABOMB_PLAYER_1_ACTIVE:
1072 case EL_DYNABOMB_PLAYER_2_ACTIVE:
1073 case EL_DYNABOMB_PLAYER_3_ACTIVE:
1074 case EL_DYNABOMB_PLAYER_4_ACTIVE:
1075 MovDelay[x][y] = 96;
1078 case EL_EM_DYNAMITE_ACTIVE:
1079 MovDelay[x][y] = 32;
1083 local_player->lights_still_needed++;
1087 local_player->friends_still_needed++;
1092 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1095 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1096 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1097 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1098 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1099 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1100 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1101 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1102 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1103 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1104 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1105 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1106 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1109 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1110 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1111 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1113 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
1115 game.belt_dir[belt_nr] = belt_dir;
1116 game.belt_dir_nr[belt_nr] = belt_dir_nr;
1118 else /* more than one switch -- set it like the first switch */
1120 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1125 #if !USE_BOTH_SWITCHGATE_SWITCHES
1126 case EL_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
1128 Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
1132 case EL_LIGHT_SWITCH_ACTIVE:
1134 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1137 case EL_INVISIBLE_STEELWALL:
1138 case EL_INVISIBLE_WALL:
1139 case EL_INVISIBLE_SAND:
1140 if (game.light_time_left > 0 ||
1141 game.lenses_time_left > 0)
1142 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1145 case EL_EMC_MAGIC_BALL:
1146 if (game.ball_state)
1147 Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1150 case EL_EMC_MAGIC_BALL_SWITCH:
1151 if (game.ball_state)
1152 Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1156 if (IS_CUSTOM_ELEMENT(element))
1158 if (CAN_MOVE(element))
1161 #if USE_NEW_CUSTOM_VALUE
1162 if (!element_info[element].use_last_ce_value || init_game)
1163 CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1166 else if (IS_GROUP_ELEMENT(element))
1168 Feld[x][y] = get_element_from_group_element(element);
1170 InitField(x, y, init_game);
1177 CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1180 static inline void InitField_WithBug1(int x, int y, boolean init_game)
1182 InitField(x, y, init_game);
1184 /* not needed to call InitMovDir() -- already done by InitField()! */
1185 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1186 CAN_MOVE(Feld[x][y]))
1190 static inline void InitField_WithBug2(int x, int y, boolean init_game)
1192 int old_element = Feld[x][y];
1194 InitField(x, y, init_game);
1196 /* not needed to call InitMovDir() -- already done by InitField()! */
1197 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1198 CAN_MOVE(old_element) &&
1199 (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1202 /* this case is in fact a combination of not less than three bugs:
1203 first, it calls InitMovDir() for elements that can move, although this is
1204 already done by InitField(); then, it checks the element that was at this
1205 field _before_ the call to InitField() (which can change it); lastly, it
1206 was not called for "mole with direction" elements, which were treated as
1207 "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1211 inline void DrawGameValue_Emeralds(int value)
1213 int xpos = (3 * 14 - 3 * getFontWidth(FONT_TEXT_2)) / 2;
1215 if (PANEL_DEACTIVATED(game.panel.gems))
1218 DrawText(DX_EMERALDS + xpos, DY_EMERALDS, int2str(value, 3), FONT_TEXT_2);
1221 inline void DrawGameValue_Dynamite(int value)
1223 int xpos = (3 * 14 - 3 * getFontWidth(FONT_TEXT_2)) / 2;
1225 if (PANEL_DEACTIVATED(game.panel.inventory))
1228 DrawText(DX_DYNAMITE + xpos, DY_DYNAMITE, int2str(value, 3), FONT_TEXT_2);
1231 inline void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
1233 int base_key_graphic = EL_KEY_1;
1236 if (PANEL_DEACTIVATED(game.panel.keys))
1239 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
1240 base_key_graphic = EL_EM_KEY_1;
1242 /* currently only 4 of 8 possible keys are displayed */
1243 for (i = 0; i < STD_NUM_KEYS; i++)
1245 int x = XX_KEYS + i * MINI_TILEX;
1249 DrawMiniGraphicExt(drawto, DX + x,DY + y, el2edimg(base_key_graphic + i));
1251 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
1252 DOOR_GFX_PAGEX5 + x, y, MINI_TILEX, MINI_TILEY, DX + x,DY + y);
1256 inline void DrawGameValue_Score(int value)
1258 int xpos = (5 * 14 - 5 * getFontWidth(FONT_TEXT_2)) / 2;
1260 if (PANEL_DEACTIVATED(game.panel.score))
1263 DrawText(DX_SCORE + xpos, DY_SCORE, int2str(value, 5), FONT_TEXT_2);
1266 inline void DrawGameValue_Time(int value)
1268 int xpos3 = (3 * 14 - 3 * getFontWidth(FONT_TEXT_2)) / 2;
1269 int xpos4 = (4 * 10 - 4 * getFontWidth(FONT_LEVEL_NUMBER)) / 2;
1271 if (PANEL_DEACTIVATED(game.panel.time))
1274 /* clear background if value just changed its size */
1275 if (value == 999 || value == 1000)
1276 ClearRectangleOnBackground(drawto, DX_TIME1, DY_TIME, 14 * 3, 14);
1279 DrawText(DX_TIME1 + xpos3, DY_TIME, int2str(value, 3), FONT_TEXT_2);
1281 DrawText(DX_TIME2 + xpos4, DY_TIME, int2str(value, 4), FONT_LEVEL_NUMBER);
1284 inline void DrawGameValue_Level(int value)
1286 if (PANEL_DEACTIVATED(game.panel.level))
1290 DrawText(DX_LEVEL1, DY_LEVEL, int2str(value, 2), FONT_TEXT_2);
1292 DrawText(DX_LEVEL2, DY_LEVEL, int2str(value, 3), FONT_LEVEL_NUMBER);
1295 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
1298 int key[MAX_NUM_KEYS];
1301 for (i = 0; i < MAX_NUM_KEYS; i++)
1302 key[i] = key_bits & (1 << i);
1304 DrawGameValue_Level(level_nr);
1306 DrawGameValue_Emeralds(emeralds);
1307 DrawGameValue_Dynamite(dynamite);
1308 DrawGameValue_Score(score);
1309 DrawGameValue_Time(time);
1311 DrawGameValue_Keys(key);
1313 redraw_mask |= REDRAW_DOOR_1;
1316 void DrawGameDoorValues()
1318 int time_value = (level.time == 0 ? TimePlayed : TimeLeft);
1319 int dynamite_state = 0;
1323 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
1325 DrawGameDoorValues_EM();
1330 if (game.centered_player_nr == -1)
1332 for (i = 0; i < MAX_PLAYERS; i++)
1334 for (j = 0; j < MAX_NUM_KEYS; j++)
1335 if (stored_player[i].key[j])
1336 key_bits |= (1 << j);
1338 dynamite_state += stored_player[i].inventory_size;
1343 int player_nr = game.centered_player_nr;
1345 for (i = 0; i < MAX_NUM_KEYS; i++)
1346 if (stored_player[player_nr].key[i])
1347 key_bits |= (1 << i);
1349 dynamite_state = stored_player[player_nr].inventory_size;
1352 DrawAllGameValues(local_player->gems_still_needed, dynamite_state,
1353 local_player->score, time_value, key_bits);
1358 =============================================================================
1360 -----------------------------------------------------------------------------
1361 initialize game engine due to level / tape version number
1362 =============================================================================
1365 static void InitGameEngine()
1367 int i, j, k, l, x, y;
1369 /* set game engine from tape file when re-playing, else from level file */
1370 game.engine_version = (tape.playing ? tape.engine_version :
1371 level.game_version);
1373 /* ---------------------------------------------------------------------- */
1374 /* set flags for bugs and changes according to active game engine version */
1375 /* ---------------------------------------------------------------------- */
1378 Summary of bugfix/change:
1379 Fixed handling for custom elements that change when pushed by the player.
1381 Fixed/changed in version:
1385 Before 3.1.0, custom elements that "change when pushing" changed directly
1386 after the player started pushing them (until then handled in "DigField()").
1387 Since 3.1.0, these custom elements are not changed until the "pushing"
1388 move of the element is finished (now handled in "ContinueMoving()").
1390 Affected levels/tapes:
1391 The first condition is generally needed for all levels/tapes before version
1392 3.1.0, which might use the old behaviour before it was changed; known tapes
1393 that are affected are some tapes from the level set "Walpurgis Gardens" by
1395 The second condition is an exception from the above case and is needed for
1396 the special case of tapes recorded with game (not engine!) version 3.1.0 or
1397 above (including some development versions of 3.1.0), but before it was
1398 known that this change would break tapes like the above and was fixed in
1399 3.1.1, so that the changed behaviour was active although the engine version
1400 while recording maybe was before 3.1.0. There is at least one tape that is
1401 affected by this exception, which is the tape for the one-level set "Bug
1402 Machine" by Juergen Bonhagen.
1405 game.use_change_when_pushing_bug =
1406 (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1408 tape.game_version >= VERSION_IDENT(3,1,0,0) &&
1409 tape.game_version < VERSION_IDENT(3,1,1,0)));
1412 Summary of bugfix/change:
1413 Fixed handling for blocking the field the player leaves when moving.
1415 Fixed/changed in version:
1419 Before 3.1.1, when "block last field when moving" was enabled, the field
1420 the player is leaving when moving was blocked for the time of the move,
1421 and was directly unblocked afterwards. This resulted in the last field
1422 being blocked for exactly one less than the number of frames of one player
1423 move. Additionally, even when blocking was disabled, the last field was
1424 blocked for exactly one frame.
1425 Since 3.1.1, due to changes in player movement handling, the last field
1426 is not blocked at all when blocking is disabled. When blocking is enabled,
1427 the last field is blocked for exactly the number of frames of one player
1428 move. Additionally, if the player is Murphy, the hero of Supaplex, the
1429 last field is blocked for exactly one more than the number of frames of
1432 Affected levels/tapes:
1433 (!!! yet to be determined -- probably many !!!)
1436 game.use_block_last_field_bug =
1437 (game.engine_version < VERSION_IDENT(3,1,1,0));
1440 Summary of bugfix/change:
1441 Changed behaviour of CE changes with multiple changes per single frame.
1443 Fixed/changed in version:
1447 Before 3.2.0-6, only one single CE change was allowed in each engine frame.
1448 This resulted in race conditions where CEs seem to behave strange in some
1449 situations (where triggered CE changes were just skipped because there was
1450 already a CE change on that tile in the playfield in that engine frame).
1451 Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
1452 (The number of changes per frame must be limited in any case, because else
1453 it is easily possible to define CE changes that would result in an infinite
1454 loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
1455 should be set large enough so that it would only be reached in cases where
1456 the corresponding CE change conditions run into a loop. Therefore, it seems
1457 to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
1458 maximal number of change pages for custom elements.)
1460 Affected levels/tapes:
1464 #if USE_ONLY_ONE_CHANGE_PER_FRAME
1465 game.max_num_changes_per_frame = 1;
1467 game.max_num_changes_per_frame =
1468 (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
1471 /* ---------------------------------------------------------------------- */
1473 /* default scan direction: scan playfield from top/left to bottom/right */
1474 InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
1476 /* dynamically adjust element properties according to game engine version */
1477 InitElementPropertiesEngine(game.engine_version);
1480 printf("level %d: level version == %06d\n", level_nr, level.game_version);
1481 printf(" tape version == %06d [%s] [file: %06d]\n",
1482 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
1484 printf(" => game.engine_version == %06d\n", game.engine_version);
1487 /* ---------- initialize player's initial move delay --------------------- */
1489 /* dynamically adjust player properties according to level information */
1490 for (i = 0; i < MAX_PLAYERS; i++)
1491 game.initial_move_delay_value[i] =
1492 get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
1494 /* dynamically adjust player properties according to game engine version */
1495 for (i = 0; i < MAX_PLAYERS; i++)
1496 game.initial_move_delay[i] =
1497 (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
1498 game.initial_move_delay_value[i] : 0);
1500 /* ---------- initialize player's initial push delay --------------------- */
1502 /* dynamically adjust player properties according to game engine version */
1503 game.initial_push_delay_value =
1504 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
1506 /* ---------- initialize changing elements ------------------------------- */
1508 /* initialize changing elements information */
1509 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1511 struct ElementInfo *ei = &element_info[i];
1513 /* this pointer might have been changed in the level editor */
1514 ei->change = &ei->change_page[0];
1516 if (!IS_CUSTOM_ELEMENT(i))
1518 ei->change->target_element = EL_EMPTY_SPACE;
1519 ei->change->delay_fixed = 0;
1520 ei->change->delay_random = 0;
1521 ei->change->delay_frames = 1;
1524 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1526 ei->has_change_event[j] = FALSE;
1528 ei->event_page_nr[j] = 0;
1529 ei->event_page[j] = &ei->change_page[0];
1533 /* add changing elements from pre-defined list */
1534 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
1536 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
1537 struct ElementInfo *ei = &element_info[ch_delay->element];
1539 ei->change->target_element = ch_delay->target_element;
1540 ei->change->delay_fixed = ch_delay->change_delay;
1542 ei->change->pre_change_function = ch_delay->pre_change_function;
1543 ei->change->change_function = ch_delay->change_function;
1544 ei->change->post_change_function = ch_delay->post_change_function;
1546 ei->change->can_change = TRUE;
1547 ei->change->can_change_or_has_action = TRUE;
1549 ei->has_change_event[CE_DELAY] = TRUE;
1551 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
1552 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
1555 /* ---------- initialize internal run-time variables ------------- */
1557 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1559 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1561 for (j = 0; j < ei->num_change_pages; j++)
1563 ei->change_page[j].can_change_or_has_action =
1564 (ei->change_page[j].can_change |
1565 ei->change_page[j].has_action);
1569 /* add change events from custom element configuration */
1570 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1572 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1574 for (j = 0; j < ei->num_change_pages; j++)
1576 if (!ei->change_page[j].can_change_or_has_action)
1579 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1581 /* only add event page for the first page found with this event */
1582 if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
1584 ei->has_change_event[k] = TRUE;
1586 ei->event_page_nr[k] = j;
1587 ei->event_page[k] = &ei->change_page[j];
1593 /* ---------- initialize run-time trigger player and element ------------- */
1595 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1597 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1599 for (j = 0; j < ei->num_change_pages; j++)
1601 ei->change_page[j].actual_trigger_element = EL_EMPTY;
1602 ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
1603 ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
1604 ei->change_page[j].actual_trigger_ce_value = 0;
1605 ei->change_page[j].actual_trigger_ce_score = 0;
1609 /* ---------- initialize trigger events ---------------------------------- */
1611 /* initialize trigger events information */
1612 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1613 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1614 trigger_events[i][j] = FALSE;
1616 /* add trigger events from element change event properties */
1617 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1619 struct ElementInfo *ei = &element_info[i];
1621 for (j = 0; j < ei->num_change_pages; j++)
1623 if (!ei->change_page[j].can_change_or_has_action)
1626 if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
1628 int trigger_element = ei->change_page[j].trigger_element;
1630 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1632 if (ei->change_page[j].has_event[k])
1634 if (IS_GROUP_ELEMENT(trigger_element))
1636 struct ElementGroupInfo *group =
1637 element_info[trigger_element].group;
1639 for (l = 0; l < group->num_elements_resolved; l++)
1640 trigger_events[group->element_resolved[l]][k] = TRUE;
1642 else if (trigger_element == EL_ANY_ELEMENT)
1643 for (l = 0; l < MAX_NUM_ELEMENTS; l++)
1644 trigger_events[l][k] = TRUE;
1646 trigger_events[trigger_element][k] = TRUE;
1653 /* ---------- initialize push delay -------------------------------------- */
1655 /* initialize push delay values to default */
1656 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1658 if (!IS_CUSTOM_ELEMENT(i))
1660 /* set default push delay values (corrected since version 3.0.7-1) */
1661 if (game.engine_version < VERSION_IDENT(3,0,7,1))
1663 element_info[i].push_delay_fixed = 2;
1664 element_info[i].push_delay_random = 8;
1668 element_info[i].push_delay_fixed = 8;
1669 element_info[i].push_delay_random = 8;
1674 /* set push delay value for certain elements from pre-defined list */
1675 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
1677 int e = push_delay_list[i].element;
1679 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
1680 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
1683 /* set push delay value for Supaplex elements for newer engine versions */
1684 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
1686 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1688 if (IS_SP_ELEMENT(i))
1690 /* set SP push delay to just enough to push under a falling zonk */
1691 int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
1693 element_info[i].push_delay_fixed = delay;
1694 element_info[i].push_delay_random = 0;
1699 /* ---------- initialize move stepsize ----------------------------------- */
1701 /* initialize move stepsize values to default */
1702 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1703 if (!IS_CUSTOM_ELEMENT(i))
1704 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
1706 /* set move stepsize value for certain elements from pre-defined list */
1707 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
1709 int e = move_stepsize_list[i].element;
1711 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
1714 /* ---------- initialize collect score ----------------------------------- */
1716 /* initialize collect score values for custom elements from initial value */
1717 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1718 if (IS_CUSTOM_ELEMENT(i))
1719 element_info[i].collect_score = element_info[i].collect_score_initial;
1721 /* ---------- initialize collect count ----------------------------------- */
1723 /* initialize collect count values for non-custom elements */
1724 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1725 if (!IS_CUSTOM_ELEMENT(i))
1726 element_info[i].collect_count_initial = 0;
1728 /* add collect count values for all elements from pre-defined list */
1729 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
1730 element_info[collect_count_list[i].element].collect_count_initial =
1731 collect_count_list[i].count;
1733 /* ---------- initialize access direction -------------------------------- */
1735 /* initialize access direction values to default (access from every side) */
1736 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1737 if (!IS_CUSTOM_ELEMENT(i))
1738 element_info[i].access_direction = MV_ALL_DIRECTIONS;
1740 /* set access direction value for certain elements from pre-defined list */
1741 for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
1742 element_info[access_direction_list[i].element].access_direction =
1743 access_direction_list[i].direction;
1745 /* ---------- initialize explosion content ------------------------------- */
1746 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1748 if (IS_CUSTOM_ELEMENT(i))
1751 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
1753 /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
1755 element_info[i].content.e[x][y] =
1756 (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
1757 i == EL_PLAYER_2 ? EL_EMERALD_RED :
1758 i == EL_PLAYER_3 ? EL_EMERALD :
1759 i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
1760 i == EL_MOLE ? EL_EMERALD_RED :
1761 i == EL_PENGUIN ? EL_EMERALD_PURPLE :
1762 i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
1763 i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
1764 i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
1765 i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
1766 i == EL_WALL_EMERALD ? EL_EMERALD :
1767 i == EL_WALL_DIAMOND ? EL_DIAMOND :
1768 i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
1769 i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
1770 i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
1771 i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
1772 i == EL_WALL_PEARL ? EL_PEARL :
1773 i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
1779 int get_num_special_action(int element, int action_first, int action_last)
1781 int num_special_action = 0;
1784 for (i = action_first; i <= action_last; i++)
1786 boolean found = FALSE;
1788 for (j = 0; j < NUM_DIRECTIONS; j++)
1789 if (el_act_dir2img(element, i, j) !=
1790 el_act_dir2img(element, ACTION_DEFAULT, j))
1794 num_special_action++;
1799 return num_special_action;
1804 =============================================================================
1806 -----------------------------------------------------------------------------
1807 initialize and start new game
1808 =============================================================================
1813 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
1814 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
1815 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
1816 boolean do_fading = (game_status == GAME_MODE_MAIN);
1819 game_status = GAME_MODE_PLAYING;
1823 /* don't play tapes over network */
1824 network_playing = (options.network && !tape.playing);
1826 for (i = 0; i < MAX_PLAYERS; i++)
1828 struct PlayerInfo *player = &stored_player[i];
1830 player->index_nr = i;
1831 player->index_bit = (1 << i);
1832 player->element_nr = EL_PLAYER_1 + i;
1834 player->present = FALSE;
1835 player->active = FALSE;
1838 player->effective_action = 0;
1839 player->programmed_action = 0;
1842 player->gems_still_needed = level.gems_needed;
1843 player->sokobanfields_still_needed = 0;
1844 player->lights_still_needed = 0;
1845 player->friends_still_needed = 0;
1847 for (j = 0; j < MAX_NUM_KEYS; j++)
1848 player->key[j] = FALSE;
1850 player->dynabomb_count = 0;
1851 player->dynabomb_size = 1;
1852 player->dynabombs_left = 0;
1853 player->dynabomb_xl = FALSE;
1855 player->MovDir = MV_NONE;
1858 player->GfxDir = MV_NONE;
1859 player->GfxAction = ACTION_DEFAULT;
1861 player->StepFrame = 0;
1863 player->use_murphy = FALSE;
1864 player->artwork_element =
1865 (level.use_artwork_element[i] ? level.artwork_element[i] :
1866 player->element_nr);
1868 player->block_last_field = FALSE; /* initialized in InitPlayerField() */
1869 player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
1871 player->gravity = level.initial_player_gravity[i];
1873 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
1875 player->actual_frame_counter = 0;
1877 player->step_counter = 0;
1879 player->last_move_dir = MV_NONE;
1881 player->is_active = FALSE;
1883 player->is_waiting = FALSE;
1884 player->is_moving = FALSE;
1885 player->is_auto_moving = FALSE;
1886 player->is_digging = FALSE;
1887 player->is_snapping = FALSE;
1888 player->is_collecting = FALSE;
1889 player->is_pushing = FALSE;
1890 player->is_switching = FALSE;
1891 player->is_dropping = FALSE;
1892 player->is_dropping_pressed = FALSE;
1894 player->is_bored = FALSE;
1895 player->is_sleeping = FALSE;
1897 player->frame_counter_bored = -1;
1898 player->frame_counter_sleeping = -1;
1900 player->anim_delay_counter = 0;
1901 player->post_delay_counter = 0;
1903 player->dir_waiting = MV_NONE;
1904 player->action_waiting = ACTION_DEFAULT;
1905 player->last_action_waiting = ACTION_DEFAULT;
1906 player->special_action_bored = ACTION_DEFAULT;
1907 player->special_action_sleeping = ACTION_DEFAULT;
1909 player->switch_x = -1;
1910 player->switch_y = -1;
1912 player->drop_x = -1;
1913 player->drop_y = -1;
1915 player->show_envelope = 0;
1917 SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
1919 player->push_delay = -1; /* initialized when pushing starts */
1920 player->push_delay_value = game.initial_push_delay_value;
1922 player->drop_delay = 0;
1923 player->drop_pressed_delay = 0;
1925 player->last_jx = player->last_jy = 0;
1926 player->jx = player->jy = 0;
1928 player->shield_normal_time_left = 0;
1929 player->shield_deadly_time_left = 0;
1931 player->inventory_infinite_element = EL_UNDEFINED;
1932 player->inventory_size = 0;
1934 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
1935 SnapField(player, 0, 0);
1937 player->LevelSolved = FALSE;
1938 player->GameOver = FALSE;
1940 player->LevelSolved_GameEnd = FALSE;
1941 player->LevelSolved_SaveTape = FALSE;
1942 player->LevelSolved_SaveScore = FALSE;
1945 network_player_action_received = FALSE;
1947 #if defined(NETWORK_AVALIABLE)
1948 /* initial null action */
1949 if (network_playing)
1950 SendToServer_MovePlayer(MV_NONE);
1959 TimeLeft = level.time;
1962 ScreenMovDir = MV_NONE;
1966 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
1968 AllPlayersGone = FALSE;
1970 game.yamyam_content_nr = 0;
1971 game.magic_wall_active = FALSE;
1972 game.magic_wall_time_left = 0;
1973 game.light_time_left = 0;
1974 game.timegate_time_left = 0;
1975 game.switchgate_pos = 0;
1976 game.wind_direction = level.wind_direction_initial;
1978 #if !USE_PLAYER_GRAVITY
1979 game.gravity = FALSE;
1980 game.explosions_delayed = TRUE;
1983 game.lenses_time_left = 0;
1984 game.magnify_time_left = 0;
1986 game.ball_state = level.ball_state_initial;
1987 game.ball_content_nr = 0;
1989 game.envelope_active = FALSE;
1991 /* set focus to local player for network games, else to all players */
1992 game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
1993 game.centered_player_nr_next = game.centered_player_nr;
1994 game.set_centered_player = FALSE;
1996 if (network_playing && tape.recording)
1998 /* store client dependent player focus when recording network games */
1999 tape.centered_player_nr_next = game.centered_player_nr_next;
2000 tape.set_centered_player = TRUE;
2003 for (i = 0; i < NUM_BELTS; i++)
2005 game.belt_dir[i] = MV_NONE;
2006 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
2009 for (i = 0; i < MAX_NUM_AMOEBA; i++)
2010 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
2012 SCAN_PLAYFIELD(x, y)
2014 Feld[x][y] = level.field[x][y];
2015 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
2016 ChangeDelay[x][y] = 0;
2017 ChangePage[x][y] = -1;
2018 #if USE_NEW_CUSTOM_VALUE
2019 CustomValue[x][y] = 0; /* initialized in InitField() */
2021 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
2023 WasJustMoving[x][y] = 0;
2024 WasJustFalling[x][y] = 0;
2025 CheckCollision[x][y] = 0;
2027 Pushed[x][y] = FALSE;
2029 ChangeCount[x][y] = 0;
2030 ChangeEvent[x][y] = -1;
2032 ExplodePhase[x][y] = 0;
2033 ExplodeDelay[x][y] = 0;
2034 ExplodeField[x][y] = EX_TYPE_NONE;
2036 RunnerVisit[x][y] = 0;
2037 PlayerVisit[x][y] = 0;
2040 GfxRandom[x][y] = INIT_GFX_RANDOM();
2041 GfxElement[x][y] = EL_UNDEFINED;
2042 GfxAction[x][y] = ACTION_DEFAULT;
2043 GfxDir[x][y] = MV_NONE;
2046 SCAN_PLAYFIELD(x, y)
2048 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
2050 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
2052 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
2055 InitField(x, y, TRUE);
2060 for (i = 0; i < MAX_PLAYERS; i++)
2062 struct PlayerInfo *player = &stored_player[i];
2064 /* set number of special actions for bored and sleeping animation */
2065 player->num_special_action_bored =
2066 get_num_special_action(player->artwork_element,
2067 ACTION_BORING_1, ACTION_BORING_LAST);
2068 player->num_special_action_sleeping =
2069 get_num_special_action(player->artwork_element,
2070 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
2073 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
2074 emulate_sb ? EMU_SOKOBAN :
2075 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
2077 #if USE_NEW_ALL_SLIPPERY
2078 /* initialize type of slippery elements */
2079 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2081 if (!IS_CUSTOM_ELEMENT(i))
2083 /* default: elements slip down either to the left or right randomly */
2084 element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
2086 /* SP style elements prefer to slip down on the left side */
2087 if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
2088 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
2090 /* BD style elements prefer to slip down on the left side */
2091 if (game.emulation == EMU_BOULDERDASH)
2092 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
2097 /* initialize explosion and ignition delay */
2098 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2100 if (!IS_CUSTOM_ELEMENT(i))
2103 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
2104 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
2105 game.emulation == EMU_SUPAPLEX ? 3 : 2);
2106 int last_phase = (num_phase + 1) * delay;
2107 int half_phase = (num_phase / 2) * delay;
2109 element_info[i].explosion_delay = last_phase - 1;
2110 element_info[i].ignition_delay = half_phase;
2112 if (i == EL_BLACK_ORB)
2113 element_info[i].ignition_delay = 1;
2117 if (element_info[i].explosion_delay < 1) /* !!! check again !!! */
2118 element_info[i].explosion_delay = 1;
2120 if (element_info[i].ignition_delay < 1) /* !!! check again !!! */
2121 element_info[i].ignition_delay = 1;
2125 /* correct non-moving belts to start moving left */
2126 for (i = 0; i < NUM_BELTS; i++)
2127 if (game.belt_dir[i] == MV_NONE)
2128 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
2130 /* check if any connected player was not found in playfield */
2131 for (i = 0; i < MAX_PLAYERS; i++)
2133 struct PlayerInfo *player = &stored_player[i];
2135 if (player->connected && !player->present)
2137 for (j = 0; j < MAX_PLAYERS; j++)
2139 struct PlayerInfo *some_player = &stored_player[j];
2140 int jx = some_player->jx, jy = some_player->jy;
2142 /* assign first free player found that is present in the playfield */
2143 if (some_player->present && !some_player->connected)
2145 player->present = TRUE;
2146 player->active = TRUE;
2148 some_player->present = FALSE;
2149 some_player->active = FALSE;
2151 player->artwork_element = some_player->artwork_element;
2153 player->block_last_field = some_player->block_last_field;
2154 player->block_delay_adjustment = some_player->block_delay_adjustment;
2156 StorePlayer[jx][jy] = player->element_nr;
2157 player->jx = player->last_jx = jx;
2158 player->jy = player->last_jy = jy;
2168 /* when playing a tape, eliminate all players who do not participate */
2170 for (i = 0; i < MAX_PLAYERS; i++)
2172 if (stored_player[i].active && !tape.player_participates[i])
2174 struct PlayerInfo *player = &stored_player[i];
2175 int jx = player->jx, jy = player->jy;
2177 player->active = FALSE;
2178 StorePlayer[jx][jy] = 0;
2179 Feld[jx][jy] = EL_EMPTY;
2183 else if (!options.network && !setup.team_mode) /* && !tape.playing */
2185 /* when in single player mode, eliminate all but the first active player */
2187 for (i = 0; i < MAX_PLAYERS; i++)
2189 if (stored_player[i].active)
2191 for (j = i + 1; j < MAX_PLAYERS; j++)
2193 if (stored_player[j].active)
2195 struct PlayerInfo *player = &stored_player[j];
2196 int jx = player->jx, jy = player->jy;
2198 player->active = FALSE;
2199 player->present = FALSE;
2201 StorePlayer[jx][jy] = 0;
2202 Feld[jx][jy] = EL_EMPTY;
2209 /* when recording the game, store which players take part in the game */
2212 for (i = 0; i < MAX_PLAYERS; i++)
2213 if (stored_player[i].active)
2214 tape.player_participates[i] = TRUE;
2219 for (i = 0; i < MAX_PLAYERS; i++)
2221 struct PlayerInfo *player = &stored_player[i];
2223 printf("Player %d: present == %d, connected == %d, active == %d.\n",
2228 if (local_player == player)
2229 printf("Player %d is local player.\n", i+1);
2233 if (BorderElement == EL_EMPTY)
2236 SBX_Right = lev_fieldx - SCR_FIELDX;
2238 SBY_Lower = lev_fieldy - SCR_FIELDY;
2243 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
2245 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
2248 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
2249 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
2251 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
2252 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
2254 /* if local player not found, look for custom element that might create
2255 the player (make some assumptions about the right custom element) */
2256 if (!local_player->present)
2258 int start_x = 0, start_y = 0;
2259 int found_rating = 0;
2260 int found_element = EL_UNDEFINED;
2261 int player_nr = local_player->index_nr;
2263 SCAN_PLAYFIELD(x, y)
2265 int element = Feld[x][y];
2270 if (level.use_start_element[player_nr] &&
2271 level.start_element[player_nr] == element &&
2278 found_element = element;
2281 if (!IS_CUSTOM_ELEMENT(element))
2284 if (CAN_CHANGE(element))
2286 for (i = 0; i < element_info[element].num_change_pages; i++)
2288 /* check for player created from custom element as single target */
2289 content = element_info[element].change_page[i].target_element;
2290 is_player = ELEM_IS_PLAYER(content);
2292 if (is_player && (found_rating < 3 || element < found_element))
2298 found_element = element;
2303 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
2305 /* check for player created from custom element as explosion content */
2306 content = element_info[element].content.e[xx][yy];
2307 is_player = ELEM_IS_PLAYER(content);
2309 if (is_player && (found_rating < 2 || element < found_element))
2311 start_x = x + xx - 1;
2312 start_y = y + yy - 1;
2315 found_element = element;
2318 if (!CAN_CHANGE(element))
2321 for (i = 0; i < element_info[element].num_change_pages; i++)
2323 /* check for player created from custom element as extended target */
2325 element_info[element].change_page[i].target_content.e[xx][yy];
2327 is_player = ELEM_IS_PLAYER(content);
2329 if (is_player && (found_rating < 1 || element < found_element))
2331 start_x = x + xx - 1;
2332 start_y = y + yy - 1;
2335 found_element = element;
2341 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
2342 start_x > SBX_Right + MIDPOSX ? SBX_Right :
2345 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
2346 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
2351 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2352 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2353 local_player->jx - MIDPOSX);
2355 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2356 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2357 local_player->jy - MIDPOSY);
2362 if (!game.restart_level)
2363 CloseDoor(DOOR_CLOSE_1);
2366 FadeOut(REDRAW_FIELD);
2368 /* !!! FIX THIS (START) !!! */
2369 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2371 InitGameEngine_EM();
2373 /* blit playfield from scroll buffer to normal back buffer for fading in */
2374 BlitScreenToBitmap_EM(backbuffer);
2381 /* after drawing the level, correct some elements */
2382 if (game.timegate_time_left == 0)
2383 CloseAllOpenTimegates();
2385 /* blit playfield from scroll buffer to normal back buffer for fading in */
2386 if (setup.soft_scrolling)
2387 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
2389 redraw_mask |= REDRAW_FROM_BACKBUFFER;
2391 /* !!! FIX THIS (END) !!! */
2394 FadeIn(REDRAW_FIELD);
2398 if (!game.restart_level)
2400 /* copy default game door content to main double buffer */
2401 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2402 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2405 SetPanelBackground();
2406 SetDrawBackgroundMask(REDRAW_DOOR_1);
2408 DrawGameDoorValues();
2410 if (!game.restart_level)
2414 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
2415 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
2416 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
2420 /* copy actual game door content to door double buffer for OpenDoor() */
2421 BlitBitmap(drawto, bitmap_db_door,
2422 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
2424 OpenDoor(DOOR_OPEN_ALL);
2426 PlaySoundStereo(SND_GAME_STARTING, SOUND_MIDDLE);
2428 if (setup.sound_music)
2431 KeyboardAutoRepeatOffUnlessAutoplay();
2435 for (i = 0; i < MAX_PLAYERS; i++)
2436 printf("Player %d %sactive.\n",
2437 i + 1, (stored_player[i].active ? "" : "not "));
2441 game.restart_level = FALSE;
2444 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
2446 /* this is used for non-R'n'D game engines to update certain engine values */
2448 /* needed to determine if sounds are played within the visible screen area */
2449 scroll_x = actual_scroll_x;
2450 scroll_y = actual_scroll_y;
2453 void InitMovDir(int x, int y)
2455 int i, element = Feld[x][y];
2456 static int xy[4][2] =
2463 static int direction[3][4] =
2465 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
2466 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
2467 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
2476 Feld[x][y] = EL_BUG;
2477 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
2480 case EL_SPACESHIP_RIGHT:
2481 case EL_SPACESHIP_UP:
2482 case EL_SPACESHIP_LEFT:
2483 case EL_SPACESHIP_DOWN:
2484 Feld[x][y] = EL_SPACESHIP;
2485 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
2488 case EL_BD_BUTTERFLY_RIGHT:
2489 case EL_BD_BUTTERFLY_UP:
2490 case EL_BD_BUTTERFLY_LEFT:
2491 case EL_BD_BUTTERFLY_DOWN:
2492 Feld[x][y] = EL_BD_BUTTERFLY;
2493 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
2496 case EL_BD_FIREFLY_RIGHT:
2497 case EL_BD_FIREFLY_UP:
2498 case EL_BD_FIREFLY_LEFT:
2499 case EL_BD_FIREFLY_DOWN:
2500 Feld[x][y] = EL_BD_FIREFLY;
2501 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
2504 case EL_PACMAN_RIGHT:
2506 case EL_PACMAN_LEFT:
2507 case EL_PACMAN_DOWN:
2508 Feld[x][y] = EL_PACMAN;
2509 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
2512 case EL_YAMYAM_LEFT:
2513 case EL_YAMYAM_RIGHT:
2515 case EL_YAMYAM_DOWN:
2516 Feld[x][y] = EL_YAMYAM;
2517 MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
2520 case EL_SP_SNIKSNAK:
2521 MovDir[x][y] = MV_UP;
2524 case EL_SP_ELECTRON:
2525 MovDir[x][y] = MV_LEFT;
2532 Feld[x][y] = EL_MOLE;
2533 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
2537 if (IS_CUSTOM_ELEMENT(element))
2539 struct ElementInfo *ei = &element_info[element];
2540 int move_direction_initial = ei->move_direction_initial;
2541 int move_pattern = ei->move_pattern;
2543 if (move_direction_initial == MV_START_PREVIOUS)
2545 if (MovDir[x][y] != MV_NONE)
2548 move_direction_initial = MV_START_AUTOMATIC;
2551 if (move_direction_initial == MV_START_RANDOM)
2552 MovDir[x][y] = 1 << RND(4);
2553 else if (move_direction_initial & MV_ANY_DIRECTION)
2554 MovDir[x][y] = move_direction_initial;
2555 else if (move_pattern == MV_ALL_DIRECTIONS ||
2556 move_pattern == MV_TURNING_LEFT ||
2557 move_pattern == MV_TURNING_RIGHT ||
2558 move_pattern == MV_TURNING_LEFT_RIGHT ||
2559 move_pattern == MV_TURNING_RIGHT_LEFT ||
2560 move_pattern == MV_TURNING_RANDOM)
2561 MovDir[x][y] = 1 << RND(4);
2562 else if (move_pattern == MV_HORIZONTAL)
2563 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
2564 else if (move_pattern == MV_VERTICAL)
2565 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
2566 else if (move_pattern & MV_ANY_DIRECTION)
2567 MovDir[x][y] = element_info[element].move_pattern;
2568 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
2569 move_pattern == MV_ALONG_RIGHT_SIDE)
2571 /* use random direction as default start direction */
2572 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2573 MovDir[x][y] = 1 << RND(4);
2575 for (i = 0; i < NUM_DIRECTIONS; i++)
2577 int x1 = x + xy[i][0];
2578 int y1 = y + xy[i][1];
2580 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2582 if (move_pattern == MV_ALONG_RIGHT_SIDE)
2583 MovDir[x][y] = direction[0][i];
2585 MovDir[x][y] = direction[1][i];
2594 MovDir[x][y] = 1 << RND(4);
2596 if (element != EL_BUG &&
2597 element != EL_SPACESHIP &&
2598 element != EL_BD_BUTTERFLY &&
2599 element != EL_BD_FIREFLY)
2602 for (i = 0; i < NUM_DIRECTIONS; i++)
2604 int x1 = x + xy[i][0];
2605 int y1 = y + xy[i][1];
2607 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2609 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
2611 MovDir[x][y] = direction[0][i];
2614 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
2615 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
2617 MovDir[x][y] = direction[1][i];
2626 GfxDir[x][y] = MovDir[x][y];
2629 void InitAmoebaNr(int x, int y)
2632 int group_nr = AmoebeNachbarNr(x, y);
2636 for (i = 1; i < MAX_NUM_AMOEBA; i++)
2638 if (AmoebaCnt[i] == 0)
2646 AmoebaNr[x][y] = group_nr;
2647 AmoebaCnt[group_nr]++;
2648 AmoebaCnt2[group_nr]++;
2651 #if USE_NEW_GAME_WON
2655 static boolean score_done = FALSE;
2656 static boolean player_done = FALSE;
2657 static int game_over_delay = 0;
2658 int game_over_delay_value = 50;
2660 /* do not start end game actions before the player stops moving (to exit) */
2661 if (local_player->MovPos)
2664 if (tape.auto_play) /* tape might already be stopped here */
2665 tape.auto_play_level_solved = TRUE;
2667 if (!local_player->LevelSolved_GameEnd)
2669 local_player->LevelSolved_GameEnd = TRUE;
2670 local_player->LevelSolved_SaveTape = tape.recording;
2671 local_player->LevelSolved_SaveScore = !tape.playing;
2674 player_done = FALSE;
2675 game_over_delay = 0;
2678 PlaySoundStereo(SND_GAME_WINNING, SOUND_MIDDLE);
2684 if (setup.sound_loops)
2685 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2686 SND_CTRL_PLAY_LOOP);
2688 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2691 if (TimeLeft > 100 && TimeLeft % 10 == 0)
2694 RaiseScore(level.score[SC_TIME_BONUS] * 10);
2699 RaiseScore(level.score[SC_TIME_BONUS]);
2702 DrawGameValue_Time(TimeLeft);
2704 if (TimeLeft <= 0 && !tape.playing && setup.sound_loops)
2705 StopSound(SND_GAME_LEVELTIME_BONUS);
2707 else if (level.time == 0 && TimePlayed < 999) /* level without time limit */
2711 if (setup.sound_loops)
2712 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2713 SND_CTRL_PLAY_LOOP);
2715 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2718 if (TimePlayed < 900 && TimePlayed % 10 == 0)
2721 RaiseScore(level.score[SC_TIME_BONUS] * 10);
2726 RaiseScore(level.score[SC_TIME_BONUS]);
2729 DrawGameValue_Time(TimePlayed);
2731 if (TimePlayed >= 999 && !tape.playing && setup.sound_loops)
2732 StopSound(SND_GAME_LEVELTIME_BONUS);
2739 /* close exit door after last player */
2740 if (AllPlayersGone && ExitX >= 0 && ExitY >= 0 &&
2741 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
2742 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN))
2744 int element = Feld[ExitX][ExitY];
2746 Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
2747 EL_SP_EXIT_CLOSING);
2749 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
2752 /* player disappears */
2753 if (ExitX >= 0 && ExitY >= 0 && !player_done)
2755 DrawLevelField(ExitX, ExitY);
2762 if (game_over_delay < game_over_delay_value || !score_done)
2769 boolean raise_level = FALSE;
2771 CloseDoor(DOOR_CLOSE_1);
2773 if (local_player->LevelSolved_SaveTape)
2777 SaveTape(tape.level_nr); /* Ask to save tape */
2780 if (!local_player->LevelSolved_SaveScore)
2782 FadeOut(REDRAW_FIELD);
2784 game_status = GAME_MODE_MAIN;
2786 DrawAndFadeInMainMenu(REDRAW_FIELD);
2791 if (level_nr == leveldir_current->handicap_level)
2793 leveldir_current->handicap_level++;
2794 SaveLevelSetup_SeriesInfo();
2797 if (level_editor_test_game)
2798 local_player->score = -1; /* no highscore when playing from editor */
2799 else if (level_nr < leveldir_current->last_level)
2800 raise_level = TRUE; /* advance to next level */
2802 if ((hi_pos = NewHiScore()) >= 0)
2804 game_status = GAME_MODE_SCORES;
2806 DrawHallOfFame(hi_pos);
2816 FadeOut(REDRAW_FIELD);
2818 game_status = GAME_MODE_MAIN;
2826 DrawAndFadeInMainMenu(REDRAW_FIELD);
2829 local_player->LevelSolved_SaveScore = FALSE;
2837 boolean raise_level = FALSE;
2839 if (local_player->MovPos)
2842 if (tape.auto_play) /* tape might already be stopped here */
2843 tape.auto_play_level_solved = TRUE;
2845 local_player->LevelSolved = FALSE;
2847 PlaySoundStereo(SND_GAME_WINNING, SOUND_MIDDLE);
2851 if (!tape.playing && setup.sound_loops)
2852 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2853 SND_CTRL_PLAY_LOOP);
2855 while (TimeLeft > 0)
2857 if (!tape.playing && !setup.sound_loops)
2858 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2860 if (TimeLeft > 100 && TimeLeft % 10 == 0)
2863 RaiseScore(level.score[SC_TIME_BONUS] * 10);
2868 RaiseScore(level.score[SC_TIME_BONUS]);
2871 DrawGameValue_Time(TimeLeft);
2879 if (!tape.playing && setup.sound_loops)
2880 StopSound(SND_GAME_LEVELTIME_BONUS);
2882 else if (level.time == 0) /* level without time limit */
2884 if (!tape.playing && setup.sound_loops)
2885 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2886 SND_CTRL_PLAY_LOOP);
2888 while (TimePlayed < 999)
2890 if (!tape.playing && !setup.sound_loops)
2891 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2893 if (TimePlayed < 900 && TimePlayed % 10 == 0)
2896 RaiseScore(level.score[SC_TIME_BONUS] * 10);
2901 RaiseScore(level.score[SC_TIME_BONUS]);
2904 DrawGameValue_Time(TimePlayed);
2912 if (!tape.playing && setup.sound_loops)
2913 StopSound(SND_GAME_LEVELTIME_BONUS);
2916 /* close exit door after last player */
2917 if (AllPlayersGone && ExitX >= 0 && ExitY >= 0 &&
2918 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
2919 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN))
2921 int element = Feld[ExitX][ExitY];
2923 Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
2924 EL_SP_EXIT_CLOSING);
2926 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
2929 /* player disappears */
2930 if (ExitX >= 0 && ExitY >= 0)
2931 DrawLevelField(ExitX, ExitY);
2938 CloseDoor(DOOR_CLOSE_1);
2943 SaveTape(tape.level_nr); /* Ask to save tape */
2946 if (level_nr == leveldir_current->handicap_level)
2948 leveldir_current->handicap_level++;
2949 SaveLevelSetup_SeriesInfo();
2952 if (level_editor_test_game)
2953 local_player->score = -1; /* no highscore when playing from editor */
2954 else if (level_nr < leveldir_current->last_level)
2955 raise_level = TRUE; /* advance to next level */
2957 if ((hi_pos = NewHiScore()) >= 0)
2959 game_status = GAME_MODE_SCORES;
2960 DrawHallOfFame(hi_pos);
2969 game_status = GAME_MODE_MAIN;
2988 LoadScore(level_nr);
2990 if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
2991 local_player->score < highscore[MAX_SCORE_ENTRIES - 1].Score)
2994 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
2996 if (local_player->score > highscore[k].Score)
2998 /* player has made it to the hall of fame */
3000 if (k < MAX_SCORE_ENTRIES - 1)
3002 int m = MAX_SCORE_ENTRIES - 1;
3005 for (l = k; l < MAX_SCORE_ENTRIES; l++)
3006 if (strEqual(setup.player_name, highscore[l].Name))
3008 if (m == k) /* player's new highscore overwrites his old one */
3012 for (l = m; l > k; l--)
3014 strcpy(highscore[l].Name, highscore[l - 1].Name);
3015 highscore[l].Score = highscore[l - 1].Score;
3022 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
3023 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
3024 highscore[k].Score = local_player->score;
3030 else if (!strncmp(setup.player_name, highscore[k].Name,
3031 MAX_PLAYER_NAME_LEN))
3032 break; /* player already there with a higher score */
3038 SaveScore(level_nr);
3043 inline static int getElementMoveStepsize(int x, int y)
3045 int element = Feld[x][y];
3046 int direction = MovDir[x][y];
3047 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3048 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
3049 int horiz_move = (dx != 0);
3050 int sign = (horiz_move ? dx : dy);
3051 int step = sign * element_info[element].move_stepsize;
3053 /* special values for move stepsize for spring and things on conveyor belt */
3056 if (CAN_FALL(element) &&
3057 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
3058 step = sign * MOVE_STEPSIZE_NORMAL / 2;
3059 else if (element == EL_SPRING)
3060 step = sign * MOVE_STEPSIZE_NORMAL * 2;
3066 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
3068 if (player->GfxAction != action || player->GfxDir != dir)
3071 printf("Player frame reset! (%d => %d, %d => %d)\n",
3072 player->GfxAction, action, player->GfxDir, dir);
3075 player->GfxAction = action;
3076 player->GfxDir = dir;
3078 player->StepFrame = 0;
3082 #if USE_GFX_RESET_GFX_ANIMATION
3083 static void ResetGfxFrame(int x, int y, boolean redraw)
3085 int element = Feld[x][y];
3086 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3087 int last_gfx_frame = GfxFrame[x][y];
3089 if (graphic_info[graphic].anim_global_sync)
3090 GfxFrame[x][y] = FrameCounter;
3091 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
3092 GfxFrame[x][y] = CustomValue[x][y];
3093 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
3094 GfxFrame[x][y] = element_info[element].collect_score;
3095 else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
3096 GfxFrame[x][y] = ChangeDelay[x][y];
3098 if (redraw && GfxFrame[x][y] != last_gfx_frame)
3099 DrawLevelGraphicAnimation(x, y, graphic);
3103 static void ResetGfxAnimation(int x, int y)
3105 GfxAction[x][y] = ACTION_DEFAULT;
3106 GfxDir[x][y] = MovDir[x][y];
3109 #if USE_GFX_RESET_GFX_ANIMATION
3110 ResetGfxFrame(x, y, FALSE);
3114 static void ResetRandomAnimationValue(int x, int y)
3116 GfxRandom[x][y] = INIT_GFX_RANDOM();
3119 void InitMovingField(int x, int y, int direction)
3121 int element = Feld[x][y];
3122 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3123 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
3127 if (!WasJustMoving[x][y] || direction != MovDir[x][y])
3128 ResetGfxAnimation(x, y);
3130 MovDir[x][y] = direction;
3131 GfxDir[x][y] = direction;
3132 GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
3133 ACTION_FALLING : ACTION_MOVING);
3135 /* this is needed for CEs with property "can move" / "not moving" */
3137 if (getElementMoveStepsize(x, y) != 0) /* moving or being moved */
3139 if (Feld[newx][newy] == EL_EMPTY)
3140 Feld[newx][newy] = EL_BLOCKED;
3142 MovDir[newx][newy] = MovDir[x][y];
3144 #if USE_NEW_CUSTOM_VALUE
3145 CustomValue[newx][newy] = CustomValue[x][y];
3148 GfxFrame[newx][newy] = GfxFrame[x][y];
3149 GfxRandom[newx][newy] = GfxRandom[x][y];
3150 GfxAction[newx][newy] = GfxAction[x][y];
3151 GfxDir[newx][newy] = GfxDir[x][y];
3155 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
3157 int direction = MovDir[x][y];
3158 int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
3159 int newy = y + (direction & MV_UP ? -1 : direction & MV_DOWN ? +1 : 0);
3165 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
3167 int oldx = x, oldy = y;
3168 int direction = MovDir[x][y];
3170 if (direction == MV_LEFT)
3172 else if (direction == MV_RIGHT)
3174 else if (direction == MV_UP)
3176 else if (direction == MV_DOWN)
3179 *comes_from_x = oldx;
3180 *comes_from_y = oldy;
3183 int MovingOrBlocked2Element(int x, int y)
3185 int element = Feld[x][y];
3187 if (element == EL_BLOCKED)
3191 Blocked2Moving(x, y, &oldx, &oldy);
3192 return Feld[oldx][oldy];
3198 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
3200 /* like MovingOrBlocked2Element(), but if element is moving
3201 and (x,y) is the field the moving element is just leaving,
3202 return EL_BLOCKED instead of the element value */
3203 int element = Feld[x][y];
3205 if (IS_MOVING(x, y))
3207 if (element == EL_BLOCKED)
3211 Blocked2Moving(x, y, &oldx, &oldy);
3212 return Feld[oldx][oldy];
3221 static void RemoveField(int x, int y)
3223 Feld[x][y] = EL_EMPTY;
3229 #if USE_NEW_CUSTOM_VALUE
3230 CustomValue[x][y] = 0;
3234 ChangeDelay[x][y] = 0;
3235 ChangePage[x][y] = -1;
3236 Pushed[x][y] = FALSE;
3239 ExplodeField[x][y] = EX_TYPE_NONE;
3242 GfxElement[x][y] = EL_UNDEFINED;
3243 GfxAction[x][y] = ACTION_DEFAULT;
3244 GfxDir[x][y] = MV_NONE;
3247 void RemoveMovingField(int x, int y)
3249 int oldx = x, oldy = y, newx = x, newy = y;
3250 int element = Feld[x][y];
3251 int next_element = EL_UNDEFINED;
3253 if (element != EL_BLOCKED && !IS_MOVING(x, y))
3256 if (IS_MOVING(x, y))
3258 Moving2Blocked(x, y, &newx, &newy);
3260 if (Feld[newx][newy] != EL_BLOCKED)
3262 /* element is moving, but target field is not free (blocked), but
3263 already occupied by something different (example: acid pool);
3264 in this case, only remove the moving field, but not the target */
3266 RemoveField(oldx, oldy);
3268 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
3270 DrawLevelField(oldx, oldy);
3275 else if (element == EL_BLOCKED)
3277 Blocked2Moving(x, y, &oldx, &oldy);
3278 if (!IS_MOVING(oldx, oldy))
3282 if (element == EL_BLOCKED &&
3283 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
3284 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
3285 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
3286 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
3287 next_element = get_next_element(Feld[oldx][oldy]);
3289 RemoveField(oldx, oldy);
3290 RemoveField(newx, newy);
3292 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
3294 if (next_element != EL_UNDEFINED)
3295 Feld[oldx][oldy] = next_element;
3297 DrawLevelField(oldx, oldy);
3298 DrawLevelField(newx, newy);
3301 void DrawDynamite(int x, int y)
3303 int sx = SCREENX(x), sy = SCREENY(y);
3304 int graphic = el2img(Feld[x][y]);
3307 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
3310 if (IS_WALKABLE_INSIDE(Back[x][y]))
3314 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
3315 else if (Store[x][y])
3316 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
3318 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
3320 if (Back[x][y] || Store[x][y])
3321 DrawGraphicThruMask(sx, sy, graphic, frame);
3323 DrawGraphic(sx, sy, graphic, frame);
3326 void CheckDynamite(int x, int y)
3328 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
3332 if (MovDelay[x][y] != 0)
3335 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
3341 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
3346 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
3348 boolean num_checked_players = 0;
3351 for (i = 0; i < MAX_PLAYERS; i++)
3353 if (stored_player[i].active)
3355 int sx = stored_player[i].jx;
3356 int sy = stored_player[i].jy;
3358 if (num_checked_players == 0)
3365 *sx1 = MIN(*sx1, sx);
3366 *sy1 = MIN(*sy1, sy);
3367 *sx2 = MAX(*sx2, sx);
3368 *sy2 = MAX(*sy2, sy);
3371 num_checked_players++;
3376 static boolean checkIfAllPlayersFitToScreen_RND()
3378 int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
3380 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
3382 return (sx2 - sx1 < SCR_FIELDX &&
3383 sy2 - sy1 < SCR_FIELDY);
3386 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
3388 int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
3390 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
3392 *sx = (sx1 + sx2) / 2;
3393 *sy = (sy1 + sy2) / 2;
3396 void DrawRelocateScreen(int x, int y, int move_dir, boolean center_screen,
3397 boolean quick_relocation)
3399 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3400 boolean no_delay = (tape.warp_forward);
3401 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
3402 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
3404 if (quick_relocation)
3406 int offset = (setup.scroll_delay ? 3 : 0);
3408 if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
3410 scroll_x = (x < SBX_Left + MIDPOSX ? SBX_Left :
3411 x > SBX_Right + MIDPOSX ? SBX_Right :
3414 scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
3415 y > SBY_Lower + MIDPOSY ? SBY_Lower :
3420 if ((move_dir == MV_LEFT && scroll_x > x - MIDPOSX + offset) ||
3421 (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
3422 scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
3424 if ((move_dir == MV_UP && scroll_y > y - MIDPOSY + offset) ||
3425 (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
3426 scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
3428 /* don't scroll over playfield boundaries */
3429 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
3430 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
3432 /* don't scroll over playfield boundaries */
3433 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
3434 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
3437 RedrawPlayfield(TRUE, 0,0,0,0);
3441 int scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left :
3442 x > SBX_Right + MIDPOSX ? SBX_Right :
3445 int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
3446 y > SBY_Lower + MIDPOSY ? SBY_Lower :
3449 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
3451 while (scroll_x != scroll_xx || scroll_y != scroll_yy)
3454 int fx = FX, fy = FY;
3456 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
3457 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
3459 if (dx == 0 && dy == 0) /* no scrolling needed at all */
3465 fx += dx * TILEX / 2;
3466 fy += dy * TILEY / 2;
3468 ScrollLevel(dx, dy);
3471 /* scroll in two steps of half tile size to make things smoother */
3472 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
3474 Delay(wait_delay_value);
3476 /* scroll second step to align at full tile size */
3478 Delay(wait_delay_value);
3483 Delay(wait_delay_value);
3487 void RelocatePlayer(int jx, int jy, int el_player_raw)
3489 int el_player = GET_PLAYER_ELEMENT(el_player_raw);
3490 int player_nr = GET_PLAYER_NR(el_player);
3491 struct PlayerInfo *player = &stored_player[player_nr];
3492 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3493 boolean no_delay = (tape.warp_forward);
3494 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
3495 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
3496 int old_jx = player->jx;
3497 int old_jy = player->jy;
3498 int old_element = Feld[old_jx][old_jy];
3499 int element = Feld[jx][jy];
3500 boolean player_relocated = (old_jx != jx || old_jy != jy);
3502 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
3503 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
3504 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
3505 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
3506 int leave_side_horiz = move_dir_horiz;
3507 int leave_side_vert = move_dir_vert;
3508 int enter_side = enter_side_horiz | enter_side_vert;
3509 int leave_side = leave_side_horiz | leave_side_vert;
3511 if (player->GameOver) /* do not reanimate dead player */
3514 if (!player_relocated) /* no need to relocate the player */
3517 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
3519 RemoveField(jx, jy); /* temporarily remove newly placed player */
3520 DrawLevelField(jx, jy);
3523 if (player->present)
3525 while (player->MovPos)
3527 ScrollPlayer(player, SCROLL_GO_ON);
3528 ScrollScreen(NULL, SCROLL_GO_ON);
3530 AdvanceFrameAndPlayerCounters(player->index_nr);
3535 Delay(wait_delay_value);
3538 DrawPlayer(player); /* needed here only to cleanup last field */
3539 DrawLevelField(player->jx, player->jy); /* remove player graphic */
3541 player->is_moving = FALSE;
3544 if (IS_CUSTOM_ELEMENT(old_element))
3545 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
3547 player->index_bit, leave_side);
3549 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
3551 player->index_bit, leave_side);
3553 Feld[jx][jy] = el_player;
3554 InitPlayerField(jx, jy, el_player, TRUE);
3556 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
3558 Feld[jx][jy] = element;
3559 InitField(jx, jy, FALSE);
3562 /* only visually relocate centered player */
3563 DrawRelocateScreen(player->jx, player->jy, player->MovDir, FALSE,
3564 level.instant_relocation);
3566 TestIfPlayerTouchesBadThing(jx, jy);
3567 TestIfPlayerTouchesCustomElement(jx, jy);
3569 if (IS_CUSTOM_ELEMENT(element))
3570 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
3571 player->index_bit, enter_side);
3573 CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
3574 player->index_bit, enter_side);
3577 void Explode(int ex, int ey, int phase, int mode)
3583 /* !!! eliminate this variable !!! */
3584 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
3586 if (game.explosions_delayed)
3588 ExplodeField[ex][ey] = mode;
3592 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
3594 int center_element = Feld[ex][ey];
3595 int artwork_element, explosion_element; /* set these values later */
3598 /* --- This is only really needed (and now handled) in "Impact()". --- */
3599 /* do not explode moving elements that left the explode field in time */
3600 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
3601 center_element == EL_EMPTY &&
3602 (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
3607 /* !!! at this place, the center element may be EL_BLOCKED !!! */
3608 if (mode == EX_TYPE_NORMAL ||
3609 mode == EX_TYPE_CENTER ||
3610 mode == EX_TYPE_CROSS)
3611 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
3614 /* remove things displayed in background while burning dynamite */
3615 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
3618 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
3620 /* put moving element to center field (and let it explode there) */
3621 center_element = MovingOrBlocked2Element(ex, ey);
3622 RemoveMovingField(ex, ey);
3623 Feld[ex][ey] = center_element;
3626 /* now "center_element" is finally determined -- set related values now */
3627 artwork_element = center_element; /* for custom player artwork */
3628 explosion_element = center_element; /* for custom player artwork */
3630 if (IS_PLAYER(ex, ey))
3632 int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
3634 artwork_element = stored_player[player_nr].artwork_element;
3636 if (level.use_explosion_element[player_nr])
3638 explosion_element = level.explosion_element[player_nr];
3639 artwork_element = explosion_element;
3644 if (mode == EX_TYPE_NORMAL ||
3645 mode == EX_TYPE_CENTER ||
3646 mode == EX_TYPE_CROSS)
3647 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
3650 last_phase = element_info[explosion_element].explosion_delay + 1;
3652 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
3654 int xx = x - ex + 1;
3655 int yy = y - ey + 1;
3658 if (!IN_LEV_FIELD(x, y) ||
3659 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
3660 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
3663 element = Feld[x][y];
3665 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
3667 element = MovingOrBlocked2Element(x, y);
3669 if (!IS_EXPLOSION_PROOF(element))
3670 RemoveMovingField(x, y);
3673 /* indestructible elements can only explode in center (but not flames) */
3674 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
3675 mode == EX_TYPE_BORDER)) ||
3676 element == EL_FLAMES)
3679 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
3680 behaviour, for example when touching a yamyam that explodes to rocks
3681 with active deadly shield, a rock is created under the player !!! */
3682 /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
3684 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
3685 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
3686 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
3688 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
3691 if (IS_ACTIVE_BOMB(element))
3693 /* re-activate things under the bomb like gate or penguin */
3694 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
3701 /* save walkable background elements while explosion on same tile */
3702 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
3703 (x != ex || y != ey || mode == EX_TYPE_BORDER))
3704 Back[x][y] = element;
3706 /* ignite explodable elements reached by other explosion */
3707 if (element == EL_EXPLOSION)
3708 element = Store2[x][y];
3710 if (AmoebaNr[x][y] &&
3711 (element == EL_AMOEBA_FULL ||
3712 element == EL_BD_AMOEBA ||
3713 element == EL_AMOEBA_GROWING))
3715 AmoebaCnt[AmoebaNr[x][y]]--;
3716 AmoebaCnt2[AmoebaNr[x][y]]--;
3721 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
3723 int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
3725 Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
3727 if (PLAYERINFO(ex, ey)->use_murphy)
3728 Store[x][y] = EL_EMPTY;
3731 /* !!! check this case -- currently needed for rnd_rado_negundo_v,
3732 !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
3733 else if (ELEM_IS_PLAYER(center_element))
3734 Store[x][y] = EL_EMPTY;
3735 else if (center_element == EL_YAMYAM)
3736 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
3737 else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
3738 Store[x][y] = element_info[center_element].content.e[xx][yy];
3740 /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
3741 (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
3742 otherwise) -- FIX THIS !!! */
3743 else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
3744 Store[x][y] = element_info[element].content.e[1][1];
3746 else if (!CAN_EXPLODE(element))
3747 Store[x][y] = element_info[element].content.e[1][1];
3750 Store[x][y] = EL_EMPTY;
3752 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
3753 center_element == EL_AMOEBA_TO_DIAMOND)
3754 Store2[x][y] = element;
3756 Feld[x][y] = EL_EXPLOSION;
3757 GfxElement[x][y] = artwork_element;
3759 ExplodePhase[x][y] = 1;
3760 ExplodeDelay[x][y] = last_phase;
3765 if (center_element == EL_YAMYAM)
3766 game.yamyam_content_nr =
3767 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
3779 GfxFrame[x][y] = 0; /* restart explosion animation */
3781 last_phase = ExplodeDelay[x][y];
3783 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
3787 /* activate this even in non-DEBUG version until cause for crash in
3788 getGraphicAnimationFrame() (see below) is found and eliminated */
3794 /* this can happen if the player leaves an explosion just in time */
3795 if (GfxElement[x][y] == EL_UNDEFINED)
3796 GfxElement[x][y] = EL_EMPTY;
3798 if (GfxElement[x][y] == EL_UNDEFINED)
3801 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
3802 printf("Explode(): This should never happen!\n");
3805 GfxElement[x][y] = EL_EMPTY;
3811 border_element = Store2[x][y];
3812 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3813 border_element = StorePlayer[x][y];
3815 if (phase == element_info[border_element].ignition_delay ||
3816 phase == last_phase)
3818 boolean border_explosion = FALSE;
3820 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
3821 !PLAYER_EXPLOSION_PROTECTED(x, y))
3823 KillPlayerUnlessExplosionProtected(x, y);
3824 border_explosion = TRUE;
3826 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
3828 Feld[x][y] = Store2[x][y];
3831 border_explosion = TRUE;
3833 else if (border_element == EL_AMOEBA_TO_DIAMOND)
3835 AmoebeUmwandeln(x, y);
3837 border_explosion = TRUE;
3840 /* if an element just explodes due to another explosion (chain-reaction),
3841 do not immediately end the new explosion when it was the last frame of
3842 the explosion (as it would be done in the following "if"-statement!) */
3843 if (border_explosion && phase == last_phase)
3847 if (phase == last_phase)
3851 element = Feld[x][y] = Store[x][y];
3852 Store[x][y] = Store2[x][y] = 0;
3853 GfxElement[x][y] = EL_UNDEFINED;
3855 /* player can escape from explosions and might therefore be still alive */
3856 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
3857 element <= EL_PLAYER_IS_EXPLODING_4)
3859 int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
3860 int explosion_element = EL_PLAYER_1 + player_nr;
3861 int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
3862 int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
3864 if (level.use_explosion_element[player_nr])
3865 explosion_element = level.explosion_element[player_nr];
3867 Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
3868 element_info[explosion_element].content.e[xx][yy]);
3871 /* restore probably existing indestructible background element */
3872 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
3873 element = Feld[x][y] = Back[x][y];
3876 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
3877 GfxDir[x][y] = MV_NONE;
3878 ChangeDelay[x][y] = 0;
3879 ChangePage[x][y] = -1;
3881 #if USE_NEW_CUSTOM_VALUE
3882 CustomValue[x][y] = 0;
3885 InitField_WithBug2(x, y, FALSE);
3887 DrawLevelField(x, y);
3889 TestIfElementTouchesCustomElement(x, y);
3891 if (GFX_CRUMBLED(element))
3892 DrawLevelFieldCrumbledSandNeighbours(x, y);
3894 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
3895 StorePlayer[x][y] = 0;
3897 if (ELEM_IS_PLAYER(element))
3898 RelocatePlayer(x, y, element);
3900 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3902 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
3903 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
3906 DrawLevelFieldCrumbledSand(x, y);
3908 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
3910 DrawLevelElement(x, y, Back[x][y]);
3911 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
3913 else if (IS_WALKABLE_UNDER(Back[x][y]))
3915 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3916 DrawLevelElementThruMask(x, y, Back[x][y]);
3918 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
3919 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3923 void DynaExplode(int ex, int ey)
3926 int dynabomb_element = Feld[ex][ey];
3927 int dynabomb_size = 1;
3928 boolean dynabomb_xl = FALSE;
3929 struct PlayerInfo *player;
3930 static int xy[4][2] =
3938 if (IS_ACTIVE_BOMB(dynabomb_element))
3940 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
3941 dynabomb_size = player->dynabomb_size;
3942 dynabomb_xl = player->dynabomb_xl;
3943 player->dynabombs_left++;
3946 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
3948 for (i = 0; i < NUM_DIRECTIONS; i++)
3950 for (j = 1; j <= dynabomb_size; j++)
3952 int x = ex + j * xy[i][0];
3953 int y = ey + j * xy[i][1];
3956 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
3959 element = Feld[x][y];
3961 /* do not restart explosions of fields with active bombs */
3962 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
3965 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
3967 if (element != EL_EMPTY && element != EL_EXPLOSION &&
3968 !IS_DIGGABLE(element) && !dynabomb_xl)
3974 void Bang(int x, int y)
3976 int element = MovingOrBlocked2Element(x, y);
3977 int explosion_type = EX_TYPE_NORMAL;
3979 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3981 struct PlayerInfo *player = PLAYERINFO(x, y);
3983 element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
3984 player->element_nr);
3986 if (level.use_explosion_element[player->index_nr])
3988 int explosion_element = level.explosion_element[player->index_nr];
3990 if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
3991 explosion_type = EX_TYPE_CROSS;
3992 else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
3993 explosion_type = EX_TYPE_CENTER;
4001 case EL_BD_BUTTERFLY:
4004 case EL_DARK_YAMYAM:
4008 RaiseScoreElement(element);
4011 case EL_DYNABOMB_PLAYER_1_ACTIVE:
4012 case EL_DYNABOMB_PLAYER_2_ACTIVE:
4013 case EL_DYNABOMB_PLAYER_3_ACTIVE:
4014 case EL_DYNABOMB_PLAYER_4_ACTIVE:
4015 case EL_DYNABOMB_INCREASE_NUMBER:
4016 case EL_DYNABOMB_INCREASE_SIZE:
4017 case EL_DYNABOMB_INCREASE_POWER:
4018 explosion_type = EX_TYPE_DYNA;
4023 case EL_LAMP_ACTIVE:
4024 case EL_AMOEBA_TO_DIAMOND:
4025 if (!IS_PLAYER(x, y)) /* penguin and player may be at same field */
4026 explosion_type = EX_TYPE_CENTER;
4030 if (element_info[element].explosion_type == EXPLODES_CROSS)
4031 explosion_type = EX_TYPE_CROSS;
4032 else if (element_info[element].explosion_type == EXPLODES_1X1)
4033 explosion_type = EX_TYPE_CENTER;
4037 if (explosion_type == EX_TYPE_DYNA)
4040 Explode(x, y, EX_PHASE_START, explosion_type);
4042 CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
4045 void SplashAcid(int x, int y)
4047 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
4048 (!IN_LEV_FIELD(x - 1, y - 2) ||
4049 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
4050 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
4052 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
4053 (!IN_LEV_FIELD(x + 1, y - 2) ||
4054 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
4055 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
4057 PlayLevelSound(x, y, SND_ACID_SPLASHING);
4060 static void InitBeltMovement()
4062 static int belt_base_element[4] =
4064 EL_CONVEYOR_BELT_1_LEFT,
4065 EL_CONVEYOR_BELT_2_LEFT,
4066 EL_CONVEYOR_BELT_3_LEFT,
4067 EL_CONVEYOR_BELT_4_LEFT
4069 static int belt_base_active_element[4] =
4071 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
4072 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
4073 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
4074 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
4079 /* set frame order for belt animation graphic according to belt direction */
4080 for (i = 0; i < NUM_BELTS; i++)
4084 for (j = 0; j < NUM_BELT_PARTS; j++)
4086 int element = belt_base_active_element[belt_nr] + j;
4087 int graphic = el2img(element);
4089 if (game.belt_dir[i] == MV_LEFT)
4090 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
4092 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
4096 SCAN_PLAYFIELD(x, y)
4098 int element = Feld[x][y];
4100 for (i = 0; i < NUM_BELTS; i++)
4102 if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
4104 int e_belt_nr = getBeltNrFromBeltElement(element);
4107 if (e_belt_nr == belt_nr)
4109 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
4111 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
4118 static void ToggleBeltSwitch(int x, int y)
4120 static int belt_base_element[4] =
4122 EL_CONVEYOR_BELT_1_LEFT,
4123 EL_CONVEYOR_BELT_2_LEFT,
4124 EL_CONVEYOR_BELT_3_LEFT,
4125 EL_CONVEYOR_BELT_4_LEFT
4127 static int belt_base_active_element[4] =
4129 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
4130 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
4131 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
4132 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
4134 static int belt_base_switch_element[4] =
4136 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
4137 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
4138 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
4139 EL_CONVEYOR_BELT_4_SWITCH_LEFT
4141 static int belt_move_dir[4] =
4149 int element = Feld[x][y];
4150 int belt_nr = getBeltNrFromBeltSwitchElement(element);
4151 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
4152 int belt_dir = belt_move_dir[belt_dir_nr];
4155 if (!IS_BELT_SWITCH(element))
4158 game.belt_dir_nr[belt_nr] = belt_dir_nr;
4159 game.belt_dir[belt_nr] = belt_dir;
4161 if (belt_dir_nr == 3)
4164 /* set frame order for belt animation graphic according to belt direction */
4165 for (i = 0; i < NUM_BELT_PARTS; i++)
4167 int element = belt_base_active_element[belt_nr] + i;
4168 int graphic = el2img(element);
4170 if (belt_dir == MV_LEFT)
4171 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
4173 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
4176 SCAN_PLAYFIELD(xx, yy)
4178 int element = Feld[xx][yy];
4180 if (IS_BELT_SWITCH(element))
4182 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
4184 if (e_belt_nr == belt_nr)
4186 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
4187 DrawLevelField(xx, yy);
4190 else if (IS_BELT(element) && belt_dir != MV_NONE)
4192 int e_belt_nr = getBeltNrFromBeltElement(element);
4194 if (e_belt_nr == belt_nr)
4196 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
4198 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
4199 DrawLevelField(xx, yy);
4202 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
4204 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
4206 if (e_belt_nr == belt_nr)
4208 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
4210 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
4211 DrawLevelField(xx, yy);
4217 static void ToggleSwitchgateSwitch(int x, int y)
4221 game.switchgate_pos = !game.switchgate_pos;
4223 SCAN_PLAYFIELD(xx, yy)
4225 int element = Feld[xx][yy];
4227 #if !USE_BOTH_SWITCHGATE_SWITCHES
4228 if (element == EL_SWITCHGATE_SWITCH_UP ||
4229 element == EL_SWITCHGATE_SWITCH_DOWN)
4231 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
4232 DrawLevelField(xx, yy);
4235 if (element == EL_SWITCHGATE_SWITCH_UP)
4237 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
4238 DrawLevelField(xx, yy);
4240 else if (element == EL_SWITCHGATE_SWITCH_DOWN)
4242 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
4243 DrawLevelField(xx, yy);
4246 else if (element == EL_SWITCHGATE_OPEN ||
4247 element == EL_SWITCHGATE_OPENING)
4249 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
4251 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
4253 else if (element == EL_SWITCHGATE_CLOSED ||
4254 element == EL_SWITCHGATE_CLOSING)
4256 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
4258 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
4263 static int getInvisibleActiveFromInvisibleElement(int element)
4265 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
4266 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
4267 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
4271 static int getInvisibleFromInvisibleActiveElement(int element)
4273 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
4274 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
4275 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
4279 static void RedrawAllLightSwitchesAndInvisibleElements()
4283 SCAN_PLAYFIELD(x, y)
4285 int element = Feld[x][y];
4287 if (element == EL_LIGHT_SWITCH &&
4288 game.light_time_left > 0)
4290 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
4291 DrawLevelField(x, y);
4293 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
4294 game.light_time_left == 0)
4296 Feld[x][y] = EL_LIGHT_SWITCH;
4297 DrawLevelField(x, y);
4299 else if (element == EL_EMC_DRIPPER &&
4300 game.light_time_left > 0)
4302 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
4303 DrawLevelField(x, y);
4305 else if (element == EL_EMC_DRIPPER_ACTIVE &&
4306 game.light_time_left == 0)
4308 Feld[x][y] = EL_EMC_DRIPPER;
4309 DrawLevelField(x, y);
4311 else if (element == EL_INVISIBLE_STEELWALL ||
4312 element == EL_INVISIBLE_WALL ||
4313 element == EL_INVISIBLE_SAND)
4315 if (game.light_time_left > 0)
4316 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
4318 DrawLevelField(x, y);
4320 /* uncrumble neighbour fields, if needed */
4321 if (element == EL_INVISIBLE_SAND)
4322 DrawLevelFieldCrumbledSandNeighbours(x, y);
4324 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
4325 element == EL_INVISIBLE_WALL_ACTIVE ||
4326 element == EL_INVISIBLE_SAND_ACTIVE)
4328 if (game.light_time_left == 0)
4329 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
4331 DrawLevelField(x, y);
4333 /* re-crumble neighbour fields, if needed */
4334 if (element == EL_INVISIBLE_SAND)
4335 DrawLevelFieldCrumbledSandNeighbours(x, y);
4340 static void RedrawAllInvisibleElementsForLenses()
4344 SCAN_PLAYFIELD(x, y)
4346 int element = Feld[x][y];
4348 if (element == EL_EMC_DRIPPER &&
4349 game.lenses_time_left > 0)
4351 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
4352 DrawLevelField(x, y);
4354 else if (element == EL_EMC_DRIPPER_ACTIVE &&
4355 game.lenses_time_left == 0)
4357 Feld[x][y] = EL_EMC_DRIPPER;
4358 DrawLevelField(x, y);
4360 else if (element == EL_INVISIBLE_STEELWALL ||
4361 element == EL_INVISIBLE_WALL ||
4362 element == EL_INVISIBLE_SAND)
4364 if (game.lenses_time_left > 0)
4365 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
4367 DrawLevelField(x, y);
4369 /* uncrumble neighbour fields, if needed */
4370 if (element == EL_INVISIBLE_SAND)
4371 DrawLevelFieldCrumbledSandNeighbours(x, y);
4373 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
4374 element == EL_INVISIBLE_WALL_ACTIVE ||
4375 element == EL_INVISIBLE_SAND_ACTIVE)
4377 if (game.lenses_time_left == 0)
4378 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
4380 DrawLevelField(x, y);
4382 /* re-crumble neighbour fields, if needed */
4383 if (element == EL_INVISIBLE_SAND)
4384 DrawLevelFieldCrumbledSandNeighbours(x, y);
4389 static void RedrawAllInvisibleElementsForMagnifier()
4393 SCAN_PLAYFIELD(x, y)
4395 int element = Feld[x][y];
4397 if (element == EL_EMC_FAKE_GRASS &&
4398 game.magnify_time_left > 0)
4400 Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
4401 DrawLevelField(x, y);
4403 else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
4404 game.magnify_time_left == 0)
4406 Feld[x][y] = EL_EMC_FAKE_GRASS;
4407 DrawLevelField(x, y);
4409 else if (IS_GATE_GRAY(element) &&
4410 game.magnify_time_left > 0)
4412 Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
4413 element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
4414 IS_EM_GATE_GRAY(element) ?
4415 element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
4416 IS_EMC_GATE_GRAY(element) ?
4417 element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
4419 DrawLevelField(x, y);
4421 else if (IS_GATE_GRAY_ACTIVE(element) &&
4422 game.magnify_time_left == 0)
4424 Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
4425 element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
4426 IS_EM_GATE_GRAY_ACTIVE(element) ?
4427 element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
4428 IS_EMC_GATE_GRAY_ACTIVE(element) ?
4429 element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
4431 DrawLevelField(x, y);
4436 static void ToggleLightSwitch(int x, int y)
4438 int element = Feld[x][y];
4440 game.light_time_left =
4441 (element == EL_LIGHT_SWITCH ?
4442 level.time_light * FRAMES_PER_SECOND : 0);
4444 RedrawAllLightSwitchesAndInvisibleElements();
4447 static void ActivateTimegateSwitch(int x, int y)
4451 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
4453 SCAN_PLAYFIELD(xx, yy)
4455 int element = Feld[xx][yy];
4457 if (element == EL_TIMEGATE_CLOSED ||
4458 element == EL_TIMEGATE_CLOSING)
4460 Feld[xx][yy] = EL_TIMEGATE_OPENING;
4461 PlayLevelSound(xx, yy, SND_TIMEGATE_OPENING);
4465 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
4467 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
4468 DrawLevelField(xx, yy);
4474 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
4477 void Impact(int x, int y)
4479 boolean last_line = (y == lev_fieldy - 1);
4480 boolean object_hit = FALSE;
4481 boolean impact = (last_line || object_hit);
4482 int element = Feld[x][y];
4483 int smashed = EL_STEELWALL;
4485 if (!last_line) /* check if element below was hit */
4487 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
4490 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
4491 MovDir[x][y + 1] != MV_DOWN ||
4492 MovPos[x][y + 1] <= TILEY / 2));
4494 /* do not smash moving elements that left the smashed field in time */
4495 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
4496 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
4499 #if USE_QUICKSAND_IMPACT_BUGFIX
4500 if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
4502 RemoveMovingField(x, y + 1);
4503 Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
4504 Feld[x][y + 2] = EL_ROCK;
4505 DrawLevelField(x, y + 2);
4512 smashed = MovingOrBlocked2Element(x, y + 1);
4514 impact = (last_line || object_hit);
4517 if (!last_line && smashed == EL_ACID) /* element falls into acid */
4519 SplashAcid(x, y + 1);
4523 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
4524 /* only reset graphic animation if graphic really changes after impact */
4526 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
4528 ResetGfxAnimation(x, y);
4529 DrawLevelField(x, y);
4532 if (impact && CAN_EXPLODE_IMPACT(element))
4537 else if (impact && element == EL_PEARL)
4539 ResetGfxAnimation(x, y);
4541 Feld[x][y] = EL_PEARL_BREAKING;
4542 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4545 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
4547 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4552 if (impact && element == EL_AMOEBA_DROP)
4554 if (object_hit && IS_PLAYER(x, y + 1))
4555 KillPlayerUnlessEnemyProtected(x, y + 1);
4556 else if (object_hit && smashed == EL_PENGUIN)
4560 Feld[x][y] = EL_AMOEBA_GROWING;
4561 Store[x][y] = EL_AMOEBA_WET;
4563 ResetRandomAnimationValue(x, y);
4568 if (object_hit) /* check which object was hit */
4570 if (CAN_PASS_MAGIC_WALL(element) &&
4571 (smashed == EL_MAGIC_WALL ||
4572 smashed == EL_BD_MAGIC_WALL))
4575 int activated_magic_wall =
4576 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
4577 EL_BD_MAGIC_WALL_ACTIVE);
4579 /* activate magic wall / mill */
4580 SCAN_PLAYFIELD(xx, yy)
4581 if (Feld[xx][yy] == smashed)
4582 Feld[xx][yy] = activated_magic_wall;
4584 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
4585 game.magic_wall_active = TRUE;
4587 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
4588 SND_MAGIC_WALL_ACTIVATING :
4589 SND_BD_MAGIC_WALL_ACTIVATING));
4592 if (IS_PLAYER(x, y + 1))
4594 if (CAN_SMASH_PLAYER(element))
4596 KillPlayerUnlessEnemyProtected(x, y + 1);
4600 else if (smashed == EL_PENGUIN)
4602 if (CAN_SMASH_PLAYER(element))
4608 else if (element == EL_BD_DIAMOND)
4610 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
4616 else if (((element == EL_SP_INFOTRON ||
4617 element == EL_SP_ZONK) &&
4618 (smashed == EL_SP_SNIKSNAK ||
4619 smashed == EL_SP_ELECTRON ||
4620 smashed == EL_SP_DISK_ORANGE)) ||
4621 (element == EL_SP_INFOTRON &&
4622 smashed == EL_SP_DISK_YELLOW))
4627 else if (CAN_SMASH_EVERYTHING(element))
4629 if (IS_CLASSIC_ENEMY(smashed) ||
4630 CAN_EXPLODE_SMASHED(smashed))
4635 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
4637 if (smashed == EL_LAMP ||
4638 smashed == EL_LAMP_ACTIVE)
4643 else if (smashed == EL_NUT)
4645 Feld[x][y + 1] = EL_NUT_BREAKING;
4646 PlayLevelSound(x, y, SND_NUT_BREAKING);
4647 RaiseScoreElement(EL_NUT);
4650 else if (smashed == EL_PEARL)
4652 ResetGfxAnimation(x, y);
4654 Feld[x][y + 1] = EL_PEARL_BREAKING;
4655 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4658 else if (smashed == EL_DIAMOND)
4660 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
4661 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
4664 else if (IS_BELT_SWITCH(smashed))
4666 ToggleBeltSwitch(x, y + 1);
4668 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
4669 smashed == EL_SWITCHGATE_SWITCH_DOWN)
4671 ToggleSwitchgateSwitch(x, y + 1);
4673 else if (smashed == EL_LIGHT_SWITCH ||
4674 smashed == EL_LIGHT_SWITCH_ACTIVE)
4676 ToggleLightSwitch(x, y + 1);
4681 TestIfElementSmashesCustomElement(x, y, MV_DOWN);
4684 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4686 CheckElementChangeBySide(x, y + 1, smashed, element,
4687 CE_SWITCHED, CH_SIDE_TOP);
4688 CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
4694 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4699 /* play sound of magic wall / mill */
4701 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
4702 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
4704 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
4705 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
4706 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
4707 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
4712 /* play sound of object that hits the ground */
4713 if (last_line || object_hit)
4714 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4717 inline static void TurnRoundExt(int x, int y)
4729 { 0, 0 }, { 0, 0 }, { 0, 0 },
4734 int left, right, back;
4738 { MV_DOWN, MV_UP, MV_RIGHT },
4739 { MV_UP, MV_DOWN, MV_LEFT },
4741 { MV_LEFT, MV_RIGHT, MV_DOWN },
4745 { MV_RIGHT, MV_LEFT, MV_UP }
4748 int element = Feld[x][y];
4749 int move_pattern = element_info[element].move_pattern;
4751 int old_move_dir = MovDir[x][y];
4752 int left_dir = turn[old_move_dir].left;
4753 int right_dir = turn[old_move_dir].right;
4754 int back_dir = turn[old_move_dir].back;
4756 int left_dx = move_xy[left_dir].dx, left_dy = move_xy[left_dir].dy;
4757 int right_dx = move_xy[right_dir].dx, right_dy = move_xy[right_dir].dy;
4758 int move_dx = move_xy[old_move_dir].dx, move_dy = move_xy[old_move_dir].dy;
4759 int back_dx = move_xy[back_dir].dx, back_dy = move_xy[back_dir].dy;
4761 int left_x = x + left_dx, left_y = y + left_dy;
4762 int right_x = x + right_dx, right_y = y + right_dy;
4763 int move_x = x + move_dx, move_y = y + move_dy;
4767 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4769 TestIfBadThingTouchesOtherBadThing(x, y);
4771 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
4772 MovDir[x][y] = right_dir;
4773 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4774 MovDir[x][y] = left_dir;
4776 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
4778 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
4781 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
4783 TestIfBadThingTouchesOtherBadThing(x, y);
4785 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
4786 MovDir[x][y] = left_dir;
4787 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4788 MovDir[x][y] = right_dir;
4790 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
4792 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
4795 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4797 TestIfBadThingTouchesOtherBadThing(x, y);
4799 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
4800 MovDir[x][y] = left_dir;
4801 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
4802 MovDir[x][y] = right_dir;
4804 if (MovDir[x][y] != old_move_dir)
4807 else if (element == EL_YAMYAM)
4809 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
4810 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
4812 if (can_turn_left && can_turn_right)
4813 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4814 else if (can_turn_left)
4815 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4816 else if (can_turn_right)
4817 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4819 MovDir[x][y] = back_dir;
4821 MovDelay[x][y] = 16 + 16 * RND(3);
4823 else if (element == EL_DARK_YAMYAM)
4825 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4827 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4830 if (can_turn_left && can_turn_right)
4831 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4832 else if (can_turn_left)
4833 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4834 else if (can_turn_right)
4835 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4837 MovDir[x][y] = back_dir;
4839 MovDelay[x][y] = 16 + 16 * RND(3);
4841 else if (element == EL_PACMAN)
4843 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
4844 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
4846 if (can_turn_left && can_turn_right)
4847 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4848 else if (can_turn_left)
4849 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4850 else if (can_turn_right)
4851 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4853 MovDir[x][y] = back_dir;
4855 MovDelay[x][y] = 6 + RND(40);
4857 else if (element == EL_PIG)
4859 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
4860 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
4861 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
4862 boolean should_turn_left, should_turn_right, should_move_on;
4864 int rnd = RND(rnd_value);
4866 should_turn_left = (can_turn_left &&
4868 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
4869 y + back_dy + left_dy)));
4870 should_turn_right = (can_turn_right &&
4872 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
4873 y + back_dy + right_dy)));
4874 should_move_on = (can_move_on &&
4877 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
4878 y + move_dy + left_dy) ||
4879 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
4880 y + move_dy + right_dy)));
4882 if (should_turn_left || should_turn_right || should_move_on)
4884 if (should_turn_left && should_turn_right && should_move_on)
4885 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
4886 rnd < 2 * rnd_value / 3 ? right_dir :
4888 else if (should_turn_left && should_turn_right)
4889 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4890 else if (should_turn_left && should_move_on)
4891 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
4892 else if (should_turn_right && should_move_on)
4893 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
4894 else if (should_turn_left)
4895 MovDir[x][y] = left_dir;
4896 else if (should_turn_right)
4897 MovDir[x][y] = right_dir;
4898 else if (should_move_on)
4899 MovDir[x][y] = old_move_dir;
4901 else if (can_move_on && rnd > rnd_value / 8)
4902 MovDir[x][y] = old_move_dir;
4903 else if (can_turn_left && can_turn_right)
4904 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4905 else if (can_turn_left && rnd > rnd_value / 8)
4906 MovDir[x][y] = left_dir;
4907 else if (can_turn_right && rnd > rnd_value/8)
4908 MovDir[x][y] = right_dir;
4910 MovDir[x][y] = back_dir;
4912 xx = x + move_xy[MovDir[x][y]].dx;
4913 yy = y + move_xy[MovDir[x][y]].dy;
4915 if (!IN_LEV_FIELD(xx, yy) ||
4916 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
4917 MovDir[x][y] = old_move_dir;
4921 else if (element == EL_DRAGON)
4923 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
4924 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
4925 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
4927 int rnd = RND(rnd_value);
4929 if (can_move_on && rnd > rnd_value / 8)
4930 MovDir[x][y] = old_move_dir;
4931 else if (can_turn_left && can_turn_right)
4932 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4933 else if (can_turn_left && rnd > rnd_value / 8)
4934 MovDir[x][y] = left_dir;
4935 else if (can_turn_right && rnd > rnd_value / 8)
4936 MovDir[x][y] = right_dir;
4938 MovDir[x][y] = back_dir;
4940 xx = x + move_xy[MovDir[x][y]].dx;
4941 yy = y + move_xy[MovDir[x][y]].dy;
4943 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
4944 MovDir[x][y] = old_move_dir;
4948 else if (element == EL_MOLE)
4950 boolean can_move_on =
4951 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
4952 IS_AMOEBOID(Feld[move_x][move_y]) ||
4953 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
4956 boolean can_turn_left =
4957 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
4958 IS_AMOEBOID(Feld[left_x][left_y])));
4960 boolean can_turn_right =
4961 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
4962 IS_AMOEBOID(Feld[right_x][right_y])));
4964 if (can_turn_left && can_turn_right)
4965 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
4966 else if (can_turn_left)
4967 MovDir[x][y] = left_dir;
4969 MovDir[x][y] = right_dir;
4972 if (MovDir[x][y] != old_move_dir)
4975 else if (element == EL_BALLOON)
4977 MovDir[x][y] = game.wind_direction;
4980 else if (element == EL_SPRING)
4982 #if USE_NEW_SPRING_BUMPER
4983 if (MovDir[x][y] & MV_HORIZONTAL)
4985 if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
4986 !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
4988 Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
4989 ResetGfxAnimation(move_x, move_y);
4990 DrawLevelField(move_x, move_y);
4992 MovDir[x][y] = back_dir;
4994 else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
4995 SPRING_CAN_ENTER_FIELD(element, x, y + 1))
4996 MovDir[x][y] = MV_NONE;
4999 if (MovDir[x][y] & MV_HORIZONTAL &&
5000 (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
5001 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
5002 MovDir[x][y] = MV_NONE;
5007 else if (element == EL_ROBOT ||
5008 element == EL_SATELLITE ||
5009 element == EL_PENGUIN ||
5010 element == EL_EMC_ANDROID)
5012 int attr_x = -1, attr_y = -1;
5023 for (i = 0; i < MAX_PLAYERS; i++)
5025 struct PlayerInfo *player = &stored_player[i];
5026 int jx = player->jx, jy = player->jy;
5028 if (!player->active)
5032 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
5040 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
5041 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
5042 game.engine_version < VERSION_IDENT(3,1,0,0)))
5048 if (element == EL_PENGUIN)
5051 static int xy[4][2] =
5059 for (i = 0; i < NUM_DIRECTIONS; i++)
5061 int ex = x + xy[i][0];
5062 int ey = y + xy[i][1];
5064 if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
5073 MovDir[x][y] = MV_NONE;
5075 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
5076 else if (attr_x > x)
5077 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
5079 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
5080 else if (attr_y > y)
5081 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
5083 if (element == EL_ROBOT)
5087 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5088 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
5089 Moving2Blocked(x, y, &newx, &newy);
5091 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
5092 MovDelay[x][y] = 8 + 8 * !RND(3);
5094 MovDelay[x][y] = 16;
5096 else if (element == EL_PENGUIN)
5102 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5104 boolean first_horiz = RND(2);
5105 int new_move_dir = MovDir[x][y];
5108 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5109 Moving2Blocked(x, y, &newx, &newy);
5111 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
5115 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5116 Moving2Blocked(x, y, &newx, &newy);
5118 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
5121 MovDir[x][y] = old_move_dir;
5125 else if (element == EL_SATELLITE)
5131 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5133 boolean first_horiz = RND(2);
5134 int new_move_dir = MovDir[x][y];
5137 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5138 Moving2Blocked(x, y, &newx, &newy);
5140 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
5144 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5145 Moving2Blocked(x, y, &newx, &newy);
5147 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
5150 MovDir[x][y] = old_move_dir;
5154 else if (element == EL_EMC_ANDROID)
5156 static int check_pos[16] =
5158 -1, /* 0 => (invalid) */
5159 7, /* 1 => MV_LEFT */
5160 3, /* 2 => MV_RIGHT */
5161 -1, /* 3 => (invalid) */
5163 0, /* 5 => MV_LEFT | MV_UP */
5164 2, /* 6 => MV_RIGHT | MV_UP */
5165 -1, /* 7 => (invalid) */
5166 5, /* 8 => MV_DOWN */
5167 6, /* 9 => MV_LEFT | MV_DOWN */
5168 4, /* 10 => MV_RIGHT | MV_DOWN */
5169 -1, /* 11 => (invalid) */
5170 -1, /* 12 => (invalid) */
5171 -1, /* 13 => (invalid) */
5172 -1, /* 14 => (invalid) */
5173 -1, /* 15 => (invalid) */
5181 { -1, -1, MV_LEFT | MV_UP },
5183 { +1, -1, MV_RIGHT | MV_UP },
5184 { +1, 0, MV_RIGHT },
5185 { +1, +1, MV_RIGHT | MV_DOWN },
5187 { -1, +1, MV_LEFT | MV_DOWN },
5190 int start_pos, check_order;
5191 boolean can_clone = FALSE;
5194 /* check if there is any free field around current position */
5195 for (i = 0; i < 8; i++)
5197 int newx = x + check_xy[i].dx;
5198 int newy = y + check_xy[i].dy;
5200 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5208 if (can_clone) /* randomly find an element to clone */
5212 start_pos = check_pos[RND(8)];
5213 check_order = (RND(2) ? -1 : +1);
5215 for (i = 0; i < 8; i++)
5217 int pos_raw = start_pos + i * check_order;
5218 int pos = (pos_raw + 8) % 8;
5219 int newx = x + check_xy[pos].dx;
5220 int newy = y + check_xy[pos].dy;
5222 if (ANDROID_CAN_CLONE_FIELD(newx, newy))
5224 element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
5225 element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
5227 Store[x][y] = Feld[newx][newy];
5236 if (can_clone) /* randomly find a direction to move */
5240 start_pos = check_pos[RND(8)];
5241 check_order = (RND(2) ? -1 : +1);
5243 for (i = 0; i < 8; i++)
5245 int pos_raw = start_pos + i * check_order;
5246 int pos = (pos_raw + 8) % 8;
5247 int newx = x + check_xy[pos].dx;
5248 int newy = y + check_xy[pos].dy;
5249 int new_move_dir = check_xy[pos].dir;
5251 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5253 MovDir[x][y] = new_move_dir;
5254 MovDelay[x][y] = level.android_clone_time * 8 + 1;
5263 if (can_clone) /* cloning and moving successful */
5266 /* cannot clone -- try to move towards player */
5268 start_pos = check_pos[MovDir[x][y] & 0x0f];
5269 check_order = (RND(2) ? -1 : +1);
5271 for (i = 0; i < 3; i++)
5273 /* first check start_pos, then previous/next or (next/previous) pos */
5274 int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
5275 int pos = (pos_raw + 8) % 8;
5276 int newx = x + check_xy[pos].dx;
5277 int newy = y + check_xy[pos].dy;
5278 int new_move_dir = check_xy[pos].dir;
5280 if (IS_PLAYER(newx, newy))
5283 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
5285 MovDir[x][y] = new_move_dir;
5286 MovDelay[x][y] = level.android_move_time * 8 + 1;
5293 else if (move_pattern == MV_TURNING_LEFT ||
5294 move_pattern == MV_TURNING_RIGHT ||
5295 move_pattern == MV_TURNING_LEFT_RIGHT ||
5296 move_pattern == MV_TURNING_RIGHT_LEFT ||
5297 move_pattern == MV_TURNING_RANDOM ||
5298 move_pattern == MV_ALL_DIRECTIONS)
5300 boolean can_turn_left =
5301 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
5302 boolean can_turn_right =
5303 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
5305 if (element_info[element].move_stepsize == 0) /* "not moving" */
5308 if (move_pattern == MV_TURNING_LEFT)
5309 MovDir[x][y] = left_dir;
5310 else if (move_pattern == MV_TURNING_RIGHT)
5311 MovDir[x][y] = right_dir;
5312 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
5313 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
5314 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
5315 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
5316 else if (move_pattern == MV_TURNING_RANDOM)
5317 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
5318 can_turn_right && !can_turn_left ? right_dir :
5319 RND(2) ? left_dir : right_dir);
5320 else if (can_turn_left && can_turn_right)
5321 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5322 else if (can_turn_left)
5323 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5324 else if (can_turn_right)
5325 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5327 MovDir[x][y] = back_dir;
5329 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5331 else if (move_pattern == MV_HORIZONTAL ||
5332 move_pattern == MV_VERTICAL)
5334 if (move_pattern & old_move_dir)
5335 MovDir[x][y] = back_dir;
5336 else if (move_pattern == MV_HORIZONTAL)
5337 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
5338 else if (move_pattern == MV_VERTICAL)
5339 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
5341 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5343 else if (move_pattern & MV_ANY_DIRECTION)
5345 MovDir[x][y] = move_pattern;
5346 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5348 else if (move_pattern & MV_WIND_DIRECTION)
5350 MovDir[x][y] = game.wind_direction;
5351 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5353 else if (move_pattern == MV_ALONG_LEFT_SIDE)
5355 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
5356 MovDir[x][y] = left_dir;
5357 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5358 MovDir[x][y] = right_dir;
5360 if (MovDir[x][y] != old_move_dir)
5361 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5363 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
5365 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
5366 MovDir[x][y] = right_dir;
5367 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5368 MovDir[x][y] = left_dir;
5370 if (MovDir[x][y] != old_move_dir)
5371 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5373 else if (move_pattern == MV_TOWARDS_PLAYER ||
5374 move_pattern == MV_AWAY_FROM_PLAYER)
5376 int attr_x = -1, attr_y = -1;
5378 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
5389 for (i = 0; i < MAX_PLAYERS; i++)
5391 struct PlayerInfo *player = &stored_player[i];
5392 int jx = player->jx, jy = player->jy;
5394 if (!player->active)
5398 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
5406 MovDir[x][y] = MV_NONE;
5408 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
5409 else if (attr_x > x)
5410 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
5412 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
5413 else if (attr_y > y)
5414 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
5416 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5418 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5420 boolean first_horiz = RND(2);
5421 int new_move_dir = MovDir[x][y];
5423 if (element_info[element].move_stepsize == 0) /* "not moving" */
5425 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
5426 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5432 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5433 Moving2Blocked(x, y, &newx, &newy);
5435 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5439 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5440 Moving2Blocked(x, y, &newx, &newy);
5442 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5445 MovDir[x][y] = old_move_dir;
5448 else if (move_pattern == MV_WHEN_PUSHED ||
5449 move_pattern == MV_WHEN_DROPPED)
5451 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5452 MovDir[x][y] = MV_NONE;
5456 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
5458 static int test_xy[7][2] =
5468 static int test_dir[7] =
5478 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
5479 int move_preference = -1000000; /* start with very low preference */
5480 int new_move_dir = MV_NONE;
5481 int start_test = RND(4);
5484 for (i = 0; i < NUM_DIRECTIONS; i++)
5486 int move_dir = test_dir[start_test + i];
5487 int move_dir_preference;
5489 xx = x + test_xy[start_test + i][0];
5490 yy = y + test_xy[start_test + i][1];
5492 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
5493 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
5495 new_move_dir = move_dir;
5500 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
5503 move_dir_preference = -1 * RunnerVisit[xx][yy];
5504 if (hunter_mode && PlayerVisit[xx][yy] > 0)
5505 move_dir_preference = PlayerVisit[xx][yy];
5507 if (move_dir_preference > move_preference)
5509 /* prefer field that has not been visited for the longest time */
5510 move_preference = move_dir_preference;
5511 new_move_dir = move_dir;
5513 else if (move_dir_preference == move_preference &&
5514 move_dir == old_move_dir)
5516 /* prefer last direction when all directions are preferred equally */
5517 move_preference = move_dir_preference;
5518 new_move_dir = move_dir;
5522 MovDir[x][y] = new_move_dir;
5523 if (old_move_dir != new_move_dir)
5524 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5528 static void TurnRound(int x, int y)
5530 int direction = MovDir[x][y];
5534 GfxDir[x][y] = MovDir[x][y];
5536 if (direction != MovDir[x][y])
5540 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
5542 ResetGfxFrame(x, y, FALSE);
5545 static boolean JustBeingPushed(int x, int y)
5549 for (i = 0; i < MAX_PLAYERS; i++)
5551 struct PlayerInfo *player = &stored_player[i];
5553 if (player->active && player->is_pushing && player->MovPos)
5555 int next_jx = player->jx + (player->jx - player->last_jx);
5556 int next_jy = player->jy + (player->jy - player->last_jy);
5558 if (x == next_jx && y == next_jy)
5566 void StartMoving(int x, int y)
5568 boolean started_moving = FALSE; /* some elements can fall _and_ move */
5569 int element = Feld[x][y];
5574 if (MovDelay[x][y] == 0)
5575 GfxAction[x][y] = ACTION_DEFAULT;
5577 if (CAN_FALL(element) && y < lev_fieldy - 1)
5579 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
5580 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
5581 if (JustBeingPushed(x, y))
5584 if (element == EL_QUICKSAND_FULL)
5586 if (IS_FREE(x, y + 1))
5588 InitMovingField(x, y, MV_DOWN);
5589 started_moving = TRUE;
5591 Feld[x][y] = EL_QUICKSAND_EMPTYING;
5592 #if USE_QUICKSAND_BD_ROCK_BUGFIX
5593 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
5594 Store[x][y] = EL_ROCK;
5596 Store[x][y] = EL_ROCK;
5599 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
5601 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5603 if (!MovDelay[x][y])
5604 MovDelay[x][y] = TILEY + 1;
5613 Feld[x][y] = EL_QUICKSAND_EMPTY;
5614 Feld[x][y + 1] = EL_QUICKSAND_FULL;
5615 Store[x][y + 1] = Store[x][y];
5618 PlayLevelSoundAction(x, y, ACTION_FILLING);
5621 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
5622 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5624 InitMovingField(x, y, MV_DOWN);
5625 started_moving = TRUE;
5627 Feld[x][y] = EL_QUICKSAND_FILLING;
5628 Store[x][y] = element;
5630 PlayLevelSoundAction(x, y, ACTION_FILLING);
5632 else if (element == EL_MAGIC_WALL_FULL)
5634 if (IS_FREE(x, y + 1))
5636 InitMovingField(x, y, MV_DOWN);
5637 started_moving = TRUE;
5639 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
5640 Store[x][y] = EL_CHANGED(Store[x][y]);
5642 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
5644 if (!MovDelay[x][y])
5645 MovDelay[x][y] = TILEY/4 + 1;
5654 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
5655 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
5656 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
5660 else if (element == EL_BD_MAGIC_WALL_FULL)
5662 if (IS_FREE(x, y + 1))
5664 InitMovingField(x, y, MV_DOWN);
5665 started_moving = TRUE;
5667 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
5668 Store[x][y] = EL_CHANGED2(Store[x][y]);
5670 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
5672 if (!MovDelay[x][y])
5673 MovDelay[x][y] = TILEY/4 + 1;
5682 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
5683 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
5684 Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
5688 else if (CAN_PASS_MAGIC_WALL(element) &&
5689 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
5690 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
5692 InitMovingField(x, y, MV_DOWN);
5693 started_moving = TRUE;
5696 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
5697 EL_BD_MAGIC_WALL_FILLING);
5698 Store[x][y] = element;
5700 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
5702 SplashAcid(x, y + 1);
5704 InitMovingField(x, y, MV_DOWN);
5705 started_moving = TRUE;
5707 Store[x][y] = EL_ACID;
5709 else if ((game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5710 CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
5712 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
5713 CAN_FALL(element) && WasJustFalling[x][y] &&
5714 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
5716 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
5717 CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
5718 (Feld[x][y + 1] == EL_BLOCKED)))
5720 /* this is needed for a special case not covered by calling "Impact()"
5721 from "ContinueMoving()": if an element moves to a tile directly below
5722 another element which was just falling on that tile (which was empty
5723 in the previous frame), the falling element above would just stop
5724 instead of smashing the element below (in previous version, the above
5725 element was just checked for "moving" instead of "falling", resulting
5726 in incorrect smashes caused by horizontal movement of the above
5727 element; also, the case of the player being the element to smash was
5728 simply not covered here... :-/ ) */
5730 CheckCollision[x][y] = 0;
5734 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
5736 if (MovDir[x][y] == MV_NONE)
5738 InitMovingField(x, y, MV_DOWN);
5739 started_moving = TRUE;
5742 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
5744 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
5745 MovDir[x][y] = MV_DOWN;
5747 InitMovingField(x, y, MV_DOWN);
5748 started_moving = TRUE;
5750 else if (element == EL_AMOEBA_DROP)
5752 Feld[x][y] = EL_AMOEBA_GROWING;
5753 Store[x][y] = EL_AMOEBA_WET;
5755 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
5756 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
5757 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
5758 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
5760 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
5761 (IS_FREE(x - 1, y + 1) ||
5762 Feld[x - 1][y + 1] == EL_ACID));
5763 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
5764 (IS_FREE(x + 1, y + 1) ||
5765 Feld[x + 1][y + 1] == EL_ACID));
5766 boolean can_fall_any = (can_fall_left || can_fall_right);
5767 boolean can_fall_both = (can_fall_left && can_fall_right);
5768 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
5770 #if USE_NEW_ALL_SLIPPERY
5771 if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
5773 if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
5774 can_fall_right = FALSE;
5775 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
5776 can_fall_left = FALSE;
5777 else if (slippery_type == SLIPPERY_ONLY_LEFT)
5778 can_fall_right = FALSE;
5779 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
5780 can_fall_left = FALSE;
5782 can_fall_any = (can_fall_left || can_fall_right);
5783 can_fall_both = FALSE;
5786 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
5788 if (slippery_type == SLIPPERY_ONLY_LEFT)
5789 can_fall_right = FALSE;
5790 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
5791 can_fall_left = FALSE;
5792 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
5793 can_fall_right = FALSE;
5794 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
5795 can_fall_left = FALSE;
5797 can_fall_any = (can_fall_left || can_fall_right);
5798 can_fall_both = (can_fall_left && can_fall_right);
5802 #if USE_NEW_ALL_SLIPPERY
5804 #if USE_NEW_SP_SLIPPERY
5805 /* !!! better use the same properties as for custom elements here !!! */
5806 else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
5807 can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
5809 can_fall_right = FALSE; /* slip down on left side */
5810 can_fall_both = FALSE;
5815 #if USE_NEW_ALL_SLIPPERY
5818 if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
5819 can_fall_right = FALSE; /* slip down on left side */
5821 can_fall_left = !(can_fall_right = RND(2));
5823 can_fall_both = FALSE;
5828 if (game.emulation == EMU_BOULDERDASH ||
5829 element == EL_BD_ROCK || element == EL_BD_DIAMOND)
5830 can_fall_right = FALSE; /* slip down on left side */
5832 can_fall_left = !(can_fall_right = RND(2));
5834 can_fall_both = FALSE;
5840 /* if not determined otherwise, prefer left side for slipping down */
5841 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
5842 started_moving = TRUE;
5846 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
5848 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
5851 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
5852 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
5853 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
5854 int belt_dir = game.belt_dir[belt_nr];
5856 if ((belt_dir == MV_LEFT && left_is_free) ||
5857 (belt_dir == MV_RIGHT && right_is_free))
5859 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
5861 InitMovingField(x, y, belt_dir);
5862 started_moving = TRUE;
5864 Pushed[x][y] = TRUE;
5865 Pushed[nextx][y] = TRUE;
5867 GfxAction[x][y] = ACTION_DEFAULT;
5871 MovDir[x][y] = 0; /* if element was moving, stop it */
5876 /* not "else if" because of elements that can fall and move (EL_SPRING) */
5878 if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
5880 if (CAN_MOVE(element) && !started_moving)
5883 int move_pattern = element_info[element].move_pattern;
5888 if (MovDir[x][y] == MV_NONE)
5890 printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
5891 x, y, element, element_info[element].token_name);
5892 printf("StartMoving(): This should never happen!\n");
5897 Moving2Blocked(x, y, &newx, &newy);
5899 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
5902 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5903 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5905 WasJustMoving[x][y] = 0;
5906 CheckCollision[x][y] = 0;
5908 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
5910 if (Feld[x][y] != element) /* element has changed */
5914 if (!MovDelay[x][y]) /* start new movement phase */
5916 /* all objects that can change their move direction after each step
5917 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
5919 if (element != EL_YAMYAM &&
5920 element != EL_DARK_YAMYAM &&
5921 element != EL_PACMAN &&
5922 !(move_pattern & MV_ANY_DIRECTION) &&
5923 move_pattern != MV_TURNING_LEFT &&
5924 move_pattern != MV_TURNING_RIGHT &&
5925 move_pattern != MV_TURNING_LEFT_RIGHT &&
5926 move_pattern != MV_TURNING_RIGHT_LEFT &&
5927 move_pattern != MV_TURNING_RANDOM)
5931 if (MovDelay[x][y] && (element == EL_BUG ||
5932 element == EL_SPACESHIP ||
5933 element == EL_SP_SNIKSNAK ||
5934 element == EL_SP_ELECTRON ||
5935 element == EL_MOLE))
5936 DrawLevelField(x, y);
5940 if (MovDelay[x][y]) /* wait some time before next movement */
5944 if (element == EL_ROBOT ||
5945 element == EL_YAMYAM ||
5946 element == EL_DARK_YAMYAM)
5948 DrawLevelElementAnimationIfNeeded(x, y, element);
5949 PlayLevelSoundAction(x, y, ACTION_WAITING);
5951 else if (element == EL_SP_ELECTRON)
5952 DrawLevelElementAnimationIfNeeded(x, y, element);
5953 else if (element == EL_DRAGON)
5956 int dir = MovDir[x][y];
5957 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
5958 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
5959 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
5960 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
5961 dir == MV_UP ? IMG_FLAMES_1_UP :
5962 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
5963 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5965 GfxAction[x][y] = ACTION_ATTACKING;
5967 if (IS_PLAYER(x, y))
5968 DrawPlayerField(x, y);
5970 DrawLevelField(x, y);
5972 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
5974 for (i = 1; i <= 3; i++)
5976 int xx = x + i * dx;
5977 int yy = y + i * dy;
5978 int sx = SCREENX(xx);
5979 int sy = SCREENY(yy);
5980 int flame_graphic = graphic + (i - 1);
5982 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
5987 int flamed = MovingOrBlocked2Element(xx, yy);
5991 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
5993 else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
5994 RemoveMovingField(xx, yy);
5996 RemoveField(xx, yy);
5998 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
6001 RemoveMovingField(xx, yy);
6004 ChangeDelay[xx][yy] = 0;
6006 Feld[xx][yy] = EL_FLAMES;
6008 if (IN_SCR_FIELD(sx, sy))
6010 DrawLevelFieldCrumbledSand(xx, yy);
6011 DrawGraphic(sx, sy, flame_graphic, frame);
6016 if (Feld[xx][yy] == EL_FLAMES)
6017 Feld[xx][yy] = EL_EMPTY;
6018 DrawLevelField(xx, yy);
6023 if (MovDelay[x][y]) /* element still has to wait some time */
6025 PlayLevelSoundAction(x, y, ACTION_WAITING);
6031 /* now make next step */
6033 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
6035 if (DONT_COLLIDE_WITH(element) &&
6036 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
6037 !PLAYER_ENEMY_PROTECTED(newx, newy))
6039 TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
6044 else if (CAN_MOVE_INTO_ACID(element) &&
6045 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
6046 !IS_MV_DIAGONAL(MovDir[x][y]) &&
6047 (MovDir[x][y] == MV_DOWN ||
6048 game.engine_version >= VERSION_IDENT(3,1,0,0)))
6050 SplashAcid(newx, newy);
6051 Store[x][y] = EL_ACID;
6053 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
6055 if (Feld[newx][newy] == EL_EXIT_OPEN)
6058 DrawLevelField(x, y);
6060 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
6061 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
6062 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
6064 local_player->friends_still_needed--;
6065 if (!local_player->friends_still_needed &&
6066 !local_player->GameOver && AllPlayersGone)
6067 local_player->LevelSolved = local_player->GameOver = TRUE;
6071 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
6073 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
6074 DrawLevelField(newx, newy);
6076 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
6078 else if (!IS_FREE(newx, newy))
6080 GfxAction[x][y] = ACTION_WAITING;
6082 if (IS_PLAYER(x, y))
6083 DrawPlayerField(x, y);
6085 DrawLevelField(x, y);
6090 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
6092 if (IS_FOOD_PIG(Feld[newx][newy]))
6094 if (IS_MOVING(newx, newy))
6095 RemoveMovingField(newx, newy);
6098 Feld[newx][newy] = EL_EMPTY;
6099 DrawLevelField(newx, newy);
6102 PlayLevelSound(x, y, SND_PIG_DIGGING);
6104 else if (!IS_FREE(newx, newy))
6106 if (IS_PLAYER(x, y))
6107 DrawPlayerField(x, y);
6109 DrawLevelField(x, y);
6114 else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
6116 if (Store[x][y] != EL_EMPTY)
6118 boolean can_clone = FALSE;
6121 /* check if element to clone is still there */
6122 for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
6124 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
6132 /* cannot clone or target field not free anymore -- do not clone */
6133 if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6134 Store[x][y] = EL_EMPTY;
6137 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6139 if (IS_MV_DIAGONAL(MovDir[x][y]))
6141 int diagonal_move_dir = MovDir[x][y];
6142 int stored = Store[x][y];
6143 int change_delay = 8;
6146 /* android is moving diagonally */
6148 CreateField(x, y, EL_DIAGONAL_SHRINKING);
6150 Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
6151 GfxElement[x][y] = EL_EMC_ANDROID;
6152 GfxAction[x][y] = ACTION_SHRINKING;
6153 GfxDir[x][y] = diagonal_move_dir;
6154 ChangeDelay[x][y] = change_delay;
6156 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
6159 DrawLevelGraphicAnimation(x, y, graphic);
6160 PlayLevelSoundAction(x, y, ACTION_SHRINKING);
6162 if (Feld[newx][newy] == EL_ACID)
6164 SplashAcid(newx, newy);
6169 CreateField(newx, newy, EL_DIAGONAL_GROWING);
6171 Store[newx][newy] = EL_EMC_ANDROID;
6172 GfxElement[newx][newy] = EL_EMC_ANDROID;
6173 GfxAction[newx][newy] = ACTION_GROWING;
6174 GfxDir[newx][newy] = diagonal_move_dir;
6175 ChangeDelay[newx][newy] = change_delay;
6177 graphic = el_act_dir2img(GfxElement[newx][newy],
6178 GfxAction[newx][newy], GfxDir[newx][newy]);
6180 DrawLevelGraphicAnimation(newx, newy, graphic);
6181 PlayLevelSoundAction(newx, newy, ACTION_GROWING);
6187 Feld[newx][newy] = EL_EMPTY;
6188 DrawLevelField(newx, newy);
6190 PlayLevelSoundAction(x, y, ACTION_DIGGING);
6193 else if (!IS_FREE(newx, newy))
6196 if (IS_PLAYER(x, y))
6197 DrawPlayerField(x, y);
6199 DrawLevelField(x, y);
6205 else if (IS_CUSTOM_ELEMENT(element) &&
6206 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6208 int new_element = Feld[newx][newy];
6210 if (!IS_FREE(newx, newy))
6212 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
6213 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
6216 /* no element can dig solid indestructible elements */
6217 if (IS_INDESTRUCTIBLE(new_element) &&
6218 !IS_DIGGABLE(new_element) &&
6219 !IS_COLLECTIBLE(new_element))
6222 if (AmoebaNr[newx][newy] &&
6223 (new_element == EL_AMOEBA_FULL ||
6224 new_element == EL_BD_AMOEBA ||
6225 new_element == EL_AMOEBA_GROWING))
6227 AmoebaCnt[AmoebaNr[newx][newy]]--;
6228 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6231 if (IS_MOVING(newx, newy))
6232 RemoveMovingField(newx, newy);
6235 RemoveField(newx, newy);
6236 DrawLevelField(newx, newy);
6239 /* if digged element was about to explode, prevent the explosion */
6240 ExplodeField[newx][newy] = EX_TYPE_NONE;
6242 PlayLevelSoundAction(x, y, action);
6245 Store[newx][newy] = EL_EMPTY;
6247 /* this makes it possible to leave the removed element again */
6248 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
6249 Store[newx][newy] = new_element;
6251 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
6253 int move_leave_element = element_info[element].move_leave_element;
6255 /* this makes it possible to leave the removed element again */
6256 Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
6257 new_element : move_leave_element);
6261 if (move_pattern & MV_MAZE_RUNNER_STYLE)
6263 RunnerVisit[x][y] = FrameCounter;
6264 PlayerVisit[x][y] /= 8; /* expire player visit path */
6267 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
6269 if (!IS_FREE(newx, newy))
6271 if (IS_PLAYER(x, y))
6272 DrawPlayerField(x, y);
6274 DrawLevelField(x, y);
6280 boolean wanna_flame = !RND(10);
6281 int dx = newx - x, dy = newy - y;
6282 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
6283 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
6284 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
6285 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
6286 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
6287 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
6290 IS_CLASSIC_ENEMY(element1) ||
6291 IS_CLASSIC_ENEMY(element2)) &&
6292 element1 != EL_DRAGON && element2 != EL_DRAGON &&
6293 element1 != EL_FLAMES && element2 != EL_FLAMES)
6295 ResetGfxAnimation(x, y);
6296 GfxAction[x][y] = ACTION_ATTACKING;
6298 if (IS_PLAYER(x, y))
6299 DrawPlayerField(x, y);
6301 DrawLevelField(x, y);
6303 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
6305 MovDelay[x][y] = 50;
6309 RemoveField(newx, newy);
6311 Feld[newx][newy] = EL_FLAMES;
6312 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
6315 RemoveField(newx1, newy1);
6317 Feld[newx1][newy1] = EL_FLAMES;
6319 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
6322 RemoveField(newx2, newy2);
6324 Feld[newx2][newy2] = EL_FLAMES;
6331 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6332 Feld[newx][newy] == EL_DIAMOND)
6334 if (IS_MOVING(newx, newy))
6335 RemoveMovingField(newx, newy);
6338 Feld[newx][newy] = EL_EMPTY;
6339 DrawLevelField(newx, newy);
6342 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
6344 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6345 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
6347 if (AmoebaNr[newx][newy])
6349 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6350 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
6351 Feld[newx][newy] == EL_BD_AMOEBA)
6352 AmoebaCnt[AmoebaNr[newx][newy]]--;
6357 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
6359 RemoveMovingField(newx, newy);
6362 if (IS_MOVING(newx, newy))
6364 RemoveMovingField(newx, newy);
6369 Feld[newx][newy] = EL_EMPTY;
6370 DrawLevelField(newx, newy);
6373 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
6375 else if ((element == EL_PACMAN || element == EL_MOLE)
6376 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
6378 if (AmoebaNr[newx][newy])
6380 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6381 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
6382 Feld[newx][newy] == EL_BD_AMOEBA)
6383 AmoebaCnt[AmoebaNr[newx][newy]]--;
6386 if (element == EL_MOLE)
6388 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
6389 PlayLevelSound(x, y, SND_MOLE_DIGGING);
6391 ResetGfxAnimation(x, y);
6392 GfxAction[x][y] = ACTION_DIGGING;
6393 DrawLevelField(x, y);
6395 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
6397 return; /* wait for shrinking amoeba */
6399 else /* element == EL_PACMAN */
6401 Feld[newx][newy] = EL_EMPTY;
6402 DrawLevelField(newx, newy);
6403 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
6406 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
6407 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
6408 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
6410 /* wait for shrinking amoeba to completely disappear */
6413 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
6415 /* object was running against a wall */
6420 /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
6421 if (move_pattern & MV_ANY_DIRECTION &&
6422 move_pattern == MovDir[x][y])
6424 int blocking_element =
6425 (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
6427 CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
6430 element = Feld[x][y]; /* element might have changed */
6434 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
6435 DrawLevelElementAnimation(x, y, element);
6437 if (DONT_TOUCH(element))
6438 TestIfBadThingTouchesPlayer(x, y);
6443 InitMovingField(x, y, MovDir[x][y]);
6445 PlayLevelSoundAction(x, y, ACTION_MOVING);
6449 ContinueMoving(x, y);
6452 void ContinueMoving(int x, int y)
6454 int element = Feld[x][y];
6455 struct ElementInfo *ei = &element_info[element];
6456 int direction = MovDir[x][y];
6457 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
6458 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
6459 int newx = x + dx, newy = y + dy;
6460 int stored = Store[x][y];
6461 int stored_new = Store[newx][newy];
6462 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
6463 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
6464 boolean last_line = (newy == lev_fieldy - 1);
6466 MovPos[x][y] += getElementMoveStepsize(x, y);
6468 if (pushed_by_player) /* special case: moving object pushed by player */
6469 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
6471 if (ABS(MovPos[x][y]) < TILEX)
6473 DrawLevelField(x, y);
6475 return; /* element is still moving */
6478 /* element reached destination field */
6480 Feld[x][y] = EL_EMPTY;
6481 Feld[newx][newy] = element;
6482 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
6484 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
6486 element = Feld[newx][newy] = EL_ACID;
6488 else if (element == EL_MOLE)
6490 Feld[x][y] = EL_SAND;
6492 DrawLevelFieldCrumbledSandNeighbours(x, y);
6494 else if (element == EL_QUICKSAND_FILLING)
6496 element = Feld[newx][newy] = get_next_element(element);
6497 Store[newx][newy] = Store[x][y];
6499 else if (element == EL_QUICKSAND_EMPTYING)
6501 Feld[x][y] = get_next_element(element);
6502 element = Feld[newx][newy] = Store[x][y];
6504 else if (element == EL_MAGIC_WALL_FILLING)
6506 element = Feld[newx][newy] = get_next_element(element);
6507 if (!game.magic_wall_active)
6508 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
6509 Store[newx][newy] = Store[x][y];
6511 else if (element == EL_MAGIC_WALL_EMPTYING)
6513 Feld[x][y] = get_next_element(element);
6514 if (!game.magic_wall_active)
6515 Feld[x][y] = EL_MAGIC_WALL_DEAD;
6516 element = Feld[newx][newy] = Store[x][y];
6518 #if USE_NEW_CUSTOM_VALUE
6519 InitField(newx, newy, FALSE);
6522 else if (element == EL_BD_MAGIC_WALL_FILLING)
6524 element = Feld[newx][newy] = get_next_element(element);
6525 if (!game.magic_wall_active)
6526 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
6527 Store[newx][newy] = Store[x][y];
6529 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
6531 Feld[x][y] = get_next_element(element);
6532 if (!game.magic_wall_active)
6533 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
6534 element = Feld[newx][newy] = Store[x][y];
6536 #if USE_NEW_CUSTOM_VALUE
6537 InitField(newx, newy, FALSE);
6540 else if (element == EL_AMOEBA_DROPPING)
6542 Feld[x][y] = get_next_element(element);
6543 element = Feld[newx][newy] = Store[x][y];
6545 else if (element == EL_SOKOBAN_OBJECT)
6548 Feld[x][y] = Back[x][y];
6550 if (Back[newx][newy])
6551 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
6553 Back[x][y] = Back[newx][newy] = 0;
6556 Store[x][y] = EL_EMPTY;
6561 MovDelay[newx][newy] = 0;
6563 if (CAN_CHANGE_OR_HAS_ACTION(element))
6565 /* copy element change control values to new field */
6566 ChangeDelay[newx][newy] = ChangeDelay[x][y];
6567 ChangePage[newx][newy] = ChangePage[x][y];
6568 ChangeCount[newx][newy] = ChangeCount[x][y];
6569 ChangeEvent[newx][newy] = ChangeEvent[x][y];
6572 #if USE_NEW_CUSTOM_VALUE
6573 CustomValue[newx][newy] = CustomValue[x][y];
6576 ChangeDelay[x][y] = 0;
6577 ChangePage[x][y] = -1;
6578 ChangeCount[x][y] = 0;
6579 ChangeEvent[x][y] = -1;
6581 #if USE_NEW_CUSTOM_VALUE
6582 CustomValue[x][y] = 0;
6585 /* copy animation control values to new field */
6586 GfxFrame[newx][newy] = GfxFrame[x][y];
6587 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
6588 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
6589 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
6591 Pushed[x][y] = Pushed[newx][newy] = FALSE;
6593 /* some elements can leave other elements behind after moving */
6595 if (ei->move_leave_element != EL_EMPTY &&
6596 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
6597 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
6599 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
6600 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
6601 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
6604 int move_leave_element = ei->move_leave_element;
6608 /* this makes it possible to leave the removed element again */
6609 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
6610 move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
6612 /* this makes it possible to leave the removed element again */
6613 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
6614 move_leave_element = stored;
6617 /* this makes it possible to leave the removed element again */
6618 if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
6619 ei->move_leave_element == EL_TRIGGER_ELEMENT)
6620 move_leave_element = stored;
6623 Feld[x][y] = move_leave_element;
6625 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
6626 MovDir[x][y] = direction;
6628 InitField(x, y, FALSE);
6630 if (GFX_CRUMBLED(Feld[x][y]))
6631 DrawLevelFieldCrumbledSandNeighbours(x, y);
6633 if (ELEM_IS_PLAYER(move_leave_element))
6634 RelocatePlayer(x, y, move_leave_element);
6637 /* do this after checking for left-behind element */
6638 ResetGfxAnimation(x, y); /* reset animation values for old field */
6640 if (!CAN_MOVE(element) ||
6641 (CAN_FALL(element) && direction == MV_DOWN &&
6642 (element == EL_SPRING ||
6643 element_info[element].move_pattern == MV_WHEN_PUSHED ||
6644 element_info[element].move_pattern == MV_WHEN_DROPPED)))
6645 GfxDir[x][y] = MovDir[newx][newy] = 0;
6647 DrawLevelField(x, y);
6648 DrawLevelField(newx, newy);
6650 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
6652 /* prevent pushed element from moving on in pushed direction */
6653 if (pushed_by_player && CAN_MOVE(element) &&
6654 element_info[element].move_pattern & MV_ANY_DIRECTION &&
6655 !(element_info[element].move_pattern & direction))
6656 TurnRound(newx, newy);
6658 /* prevent elements on conveyor belt from moving on in last direction */
6659 if (pushed_by_conveyor && CAN_FALL(element) &&
6660 direction & MV_HORIZONTAL)
6661 MovDir[newx][newy] = 0;
6663 if (!pushed_by_player)
6665 int nextx = newx + dx, nexty = newy + dy;
6666 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
6668 WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
6670 if (CAN_FALL(element) && direction == MV_DOWN)
6671 WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
6673 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
6674 CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
6677 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
6679 TestIfBadThingTouchesPlayer(newx, newy);
6680 TestIfBadThingTouchesFriend(newx, newy);
6682 if (!IS_CUSTOM_ELEMENT(element))
6683 TestIfBadThingTouchesOtherBadThing(newx, newy);
6685 else if (element == EL_PENGUIN)
6686 TestIfFriendTouchesBadThing(newx, newy);
6688 /* give the player one last chance (one more frame) to move away */
6689 if (CAN_FALL(element) && direction == MV_DOWN &&
6690 (last_line || (!IS_FREE(x, newy + 1) &&
6691 (!IS_PLAYER(x, newy + 1) ||
6692 game.engine_version < VERSION_IDENT(3,1,1,0)))))
6695 if (pushed_by_player && !game.use_change_when_pushing_bug)
6697 int push_side = MV_DIR_OPPOSITE(direction);
6698 struct PlayerInfo *player = PLAYERINFO(x, y);
6700 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
6701 player->index_bit, push_side);
6702 CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
6703 player->index_bit, push_side);
6706 if (element == EL_EMC_ANDROID && pushed_by_player) /* make another move */
6707 MovDelay[newx][newy] = 1;
6709 CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
6711 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
6714 if (ChangePage[newx][newy] != -1) /* delayed change */
6716 int page = ChangePage[newx][newy];
6717 struct ElementChangeInfo *change = &ei->change_page[page];
6719 ChangePage[newx][newy] = -1;
6721 if (change->can_change)
6723 if (ChangeElement(newx, newy, element, page))
6725 if (change->post_change_function)
6726 change->post_change_function(newx, newy);
6730 if (change->has_action)
6731 ExecuteCustomElementAction(newx, newy, element, page);
6735 TestIfElementHitsCustomElement(newx, newy, direction);
6736 TestIfPlayerTouchesCustomElement(newx, newy);
6737 TestIfElementTouchesCustomElement(newx, newy);
6739 if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
6740 IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
6741 CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
6742 MV_DIR_OPPOSITE(direction));
6745 int AmoebeNachbarNr(int ax, int ay)
6748 int element = Feld[ax][ay];
6750 static int xy[4][2] =
6758 for (i = 0; i < NUM_DIRECTIONS; i++)
6760 int x = ax + xy[i][0];
6761 int y = ay + xy[i][1];
6763 if (!IN_LEV_FIELD(x, y))
6766 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
6767 group_nr = AmoebaNr[x][y];
6773 void AmoebenVereinigen(int ax, int ay)
6775 int i, x, y, xx, yy;
6776 int new_group_nr = AmoebaNr[ax][ay];
6777 static int xy[4][2] =
6785 if (new_group_nr == 0)
6788 for (i = 0; i < NUM_DIRECTIONS; i++)
6793 if (!IN_LEV_FIELD(x, y))
6796 if ((Feld[x][y] == EL_AMOEBA_FULL ||
6797 Feld[x][y] == EL_BD_AMOEBA ||
6798 Feld[x][y] == EL_AMOEBA_DEAD) &&
6799 AmoebaNr[x][y] != new_group_nr)
6801 int old_group_nr = AmoebaNr[x][y];
6803 if (old_group_nr == 0)
6806 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
6807 AmoebaCnt[old_group_nr] = 0;
6808 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
6809 AmoebaCnt2[old_group_nr] = 0;
6811 SCAN_PLAYFIELD(xx, yy)
6813 if (AmoebaNr[xx][yy] == old_group_nr)
6814 AmoebaNr[xx][yy] = new_group_nr;
6820 void AmoebeUmwandeln(int ax, int ay)
6824 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
6826 int group_nr = AmoebaNr[ax][ay];
6831 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
6832 printf("AmoebeUmwandeln(): This should never happen!\n");
6837 SCAN_PLAYFIELD(x, y)
6839 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
6842 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
6846 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
6847 SND_AMOEBA_TURNING_TO_GEM :
6848 SND_AMOEBA_TURNING_TO_ROCK));
6853 static int xy[4][2] =
6861 for (i = 0; i < NUM_DIRECTIONS; i++)
6866 if (!IN_LEV_FIELD(x, y))
6869 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
6871 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
6872 SND_AMOEBA_TURNING_TO_GEM :
6873 SND_AMOEBA_TURNING_TO_ROCK));
6880 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
6883 int group_nr = AmoebaNr[ax][ay];
6884 boolean done = FALSE;
6889 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
6890 printf("AmoebeUmwandelnBD(): This should never happen!\n");
6895 SCAN_PLAYFIELD(x, y)
6897 if (AmoebaNr[x][y] == group_nr &&
6898 (Feld[x][y] == EL_AMOEBA_DEAD ||
6899 Feld[x][y] == EL_BD_AMOEBA ||
6900 Feld[x][y] == EL_AMOEBA_GROWING))
6903 Feld[x][y] = new_element;
6904 InitField(x, y, FALSE);
6905 DrawLevelField(x, y);
6911 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
6912 SND_BD_AMOEBA_TURNING_TO_ROCK :
6913 SND_BD_AMOEBA_TURNING_TO_GEM));
6916 void AmoebeWaechst(int x, int y)
6918 static unsigned long sound_delay = 0;
6919 static unsigned long sound_delay_value = 0;
6921 if (!MovDelay[x][y]) /* start new growing cycle */
6925 if (DelayReached(&sound_delay, sound_delay_value))
6927 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
6928 sound_delay_value = 30;
6932 if (MovDelay[x][y]) /* wait some time before growing bigger */
6935 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6937 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
6938 6 - MovDelay[x][y]);
6940 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
6943 if (!MovDelay[x][y])
6945 Feld[x][y] = Store[x][y];
6947 DrawLevelField(x, y);
6952 void AmoebaDisappearing(int x, int y)
6954 static unsigned long sound_delay = 0;
6955 static unsigned long sound_delay_value = 0;
6957 if (!MovDelay[x][y]) /* start new shrinking cycle */
6961 if (DelayReached(&sound_delay, sound_delay_value))
6962 sound_delay_value = 30;
6965 if (MovDelay[x][y]) /* wait some time before shrinking */
6968 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6970 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
6971 6 - MovDelay[x][y]);
6973 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
6976 if (!MovDelay[x][y])
6978 Feld[x][y] = EL_EMPTY;
6979 DrawLevelField(x, y);
6981 /* don't let mole enter this field in this cycle;
6982 (give priority to objects falling to this field from above) */
6988 void AmoebeAbleger(int ax, int ay)
6991 int element = Feld[ax][ay];
6992 int graphic = el2img(element);
6993 int newax = ax, neway = ay;
6994 boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
6995 static int xy[4][2] =
7003 if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
7005 Feld[ax][ay] = EL_AMOEBA_DEAD;
7006 DrawLevelField(ax, ay);
7010 if (IS_ANIMATED(graphic))
7011 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7013 if (!MovDelay[ax][ay]) /* start making new amoeba field */
7014 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
7016 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
7019 if (MovDelay[ax][ay])
7023 if (can_drop) /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
7026 int x = ax + xy[start][0];
7027 int y = ay + xy[start][1];
7029 if (!IN_LEV_FIELD(x, y))
7032 if (IS_FREE(x, y) ||
7033 CAN_GROW_INTO(Feld[x][y]) ||
7034 Feld[x][y] == EL_QUICKSAND_EMPTY)
7040 if (newax == ax && neway == ay)
7043 else /* normal or "filled" (BD style) amoeba */
7046 boolean waiting_for_player = FALSE;
7048 for (i = 0; i < NUM_DIRECTIONS; i++)
7050 int j = (start + i) % 4;
7051 int x = ax + xy[j][0];
7052 int y = ay + xy[j][1];
7054 if (!IN_LEV_FIELD(x, y))
7057 if (IS_FREE(x, y) ||
7058 CAN_GROW_INTO(Feld[x][y]) ||
7059 Feld[x][y] == EL_QUICKSAND_EMPTY)
7065 else if (IS_PLAYER(x, y))
7066 waiting_for_player = TRUE;
7069 if (newax == ax && neway == ay) /* amoeba cannot grow */
7071 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
7073 Feld[ax][ay] = EL_AMOEBA_DEAD;
7074 DrawLevelField(ax, ay);
7075 AmoebaCnt[AmoebaNr[ax][ay]]--;
7077 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
7079 if (element == EL_AMOEBA_FULL)
7080 AmoebeUmwandeln(ax, ay);
7081 else if (element == EL_BD_AMOEBA)
7082 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
7087 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
7089 /* amoeba gets larger by growing in some direction */
7091 int new_group_nr = AmoebaNr[ax][ay];
7094 if (new_group_nr == 0)
7096 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
7097 printf("AmoebeAbleger(): This should never happen!\n");
7102 AmoebaNr[newax][neway] = new_group_nr;
7103 AmoebaCnt[new_group_nr]++;
7104 AmoebaCnt2[new_group_nr]++;
7106 /* if amoeba touches other amoeba(s) after growing, unify them */
7107 AmoebenVereinigen(newax, neway);
7109 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
7111 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
7117 if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
7118 (neway == lev_fieldy - 1 && newax != ax))
7120 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
7121 Store[newax][neway] = element;
7123 else if (neway == ay || element == EL_EMC_DRIPPER)
7125 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
7127 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
7131 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
7132 Feld[ax][ay] = EL_AMOEBA_DROPPING;
7133 Store[ax][ay] = EL_AMOEBA_DROP;
7134 ContinueMoving(ax, ay);
7138 DrawLevelField(newax, neway);
7141 void Life(int ax, int ay)
7145 int element = Feld[ax][ay];
7146 int graphic = el2img(element);
7147 int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
7149 boolean changed = FALSE;
7151 if (IS_ANIMATED(graphic))
7152 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7157 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
7158 MovDelay[ax][ay] = life_time;
7160 if (MovDelay[ax][ay]) /* wait some time before next cycle */
7163 if (MovDelay[ax][ay])
7167 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
7169 int xx = ax+x1, yy = ay+y1;
7172 if (!IN_LEV_FIELD(xx, yy))
7175 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
7177 int x = xx+x2, y = yy+y2;
7179 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
7182 if (((Feld[x][y] == element ||
7183 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
7185 (IS_FREE(x, y) && Stop[x][y]))
7189 if (xx == ax && yy == ay) /* field in the middle */
7191 if (nachbarn < life_parameter[0] ||
7192 nachbarn > life_parameter[1])
7194 Feld[xx][yy] = EL_EMPTY;
7196 DrawLevelField(xx, yy);
7197 Stop[xx][yy] = TRUE;
7201 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
7202 { /* free border field */
7203 if (nachbarn >= life_parameter[2] &&
7204 nachbarn <= life_parameter[3])
7206 Feld[xx][yy] = element;
7207 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
7209 DrawLevelField(xx, yy);
7210 Stop[xx][yy] = TRUE;
7217 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
7218 SND_GAME_OF_LIFE_GROWING);
7221 static void InitRobotWheel(int x, int y)
7223 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
7226 static void RunRobotWheel(int x, int y)
7228 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
7231 static void StopRobotWheel(int x, int y)
7233 if (ZX == x && ZY == y)
7237 static void InitTimegateWheel(int x, int y)
7239 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
7242 static void RunTimegateWheel(int x, int y)
7244 PlayLevelSound(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
7247 static void InitMagicBallDelay(int x, int y)
7250 ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
7252 ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
7256 static void ActivateMagicBall(int bx, int by)
7260 if (level.ball_random)
7262 int pos_border = RND(8); /* select one of the eight border elements */
7263 int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
7264 int xx = pos_content % 3;
7265 int yy = pos_content / 3;
7270 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
7271 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
7275 for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
7277 int xx = x - bx + 1;
7278 int yy = y - by + 1;
7280 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
7281 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
7285 game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
7288 void CheckExit(int x, int y)
7290 if (local_player->gems_still_needed > 0 ||
7291 local_player->sokobanfields_still_needed > 0 ||
7292 local_player->lights_still_needed > 0)
7294 int element = Feld[x][y];
7295 int graphic = el2img(element);
7297 if (IS_ANIMATED(graphic))
7298 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7303 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7306 Feld[x][y] = EL_EXIT_OPENING;
7308 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
7311 void CheckExitSP(int x, int y)
7313 if (local_player->gems_still_needed > 0)
7315 int element = Feld[x][y];
7316 int graphic = el2img(element);
7318 if (IS_ANIMATED(graphic))
7319 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7324 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7327 Feld[x][y] = EL_SP_EXIT_OPENING;
7329 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
7332 static void CloseAllOpenTimegates()
7336 SCAN_PLAYFIELD(x, y)
7338 int element = Feld[x][y];
7340 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
7342 Feld[x][y] = EL_TIMEGATE_CLOSING;
7344 PlayLevelSoundAction(x, y, ACTION_CLOSING);
7349 void EdelsteinFunkeln(int x, int y)
7351 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
7354 if (Feld[x][y] == EL_BD_DIAMOND)
7357 if (MovDelay[x][y] == 0) /* next animation frame */
7358 MovDelay[x][y] = 11 * !SimpleRND(500);
7360 if (MovDelay[x][y] != 0) /* wait some time before next frame */
7364 if (setup.direct_draw && MovDelay[x][y])
7365 SetDrawtoField(DRAW_BUFFERED);
7367 DrawLevelElementAnimation(x, y, Feld[x][y]);
7369 if (MovDelay[x][y] != 0)
7371 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
7372 10 - MovDelay[x][y]);
7374 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
7376 if (setup.direct_draw)
7380 dest_x = FX + SCREENX(x) * TILEX;
7381 dest_y = FY + SCREENY(y) * TILEY;
7383 BlitBitmap(drawto_field, window,
7384 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
7385 SetDrawtoField(DRAW_DIRECT);
7391 void MauerWaechst(int x, int y)
7395 if (!MovDelay[x][y]) /* next animation frame */
7396 MovDelay[x][y] = 3 * delay;
7398 if (MovDelay[x][y]) /* wait some time before next frame */
7402 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7404 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
7405 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
7407 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
7410 if (!MovDelay[x][y])
7412 if (MovDir[x][y] == MV_LEFT)
7414 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
7415 DrawLevelField(x - 1, y);
7417 else if (MovDir[x][y] == MV_RIGHT)
7419 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
7420 DrawLevelField(x + 1, y);
7422 else if (MovDir[x][y] == MV_UP)
7424 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
7425 DrawLevelField(x, y - 1);
7429 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
7430 DrawLevelField(x, y + 1);
7433 Feld[x][y] = Store[x][y];
7435 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7436 DrawLevelField(x, y);
7441 void MauerAbleger(int ax, int ay)
7443 int element = Feld[ax][ay];
7444 int graphic = el2img(element);
7445 boolean oben_frei = FALSE, unten_frei = FALSE;
7446 boolean links_frei = FALSE, rechts_frei = FALSE;
7447 boolean oben_massiv = FALSE, unten_massiv = FALSE;
7448 boolean links_massiv = FALSE, rechts_massiv = FALSE;
7449 boolean new_wall = FALSE;
7451 if (IS_ANIMATED(graphic))
7452 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7454 if (!MovDelay[ax][ay]) /* start building new wall */
7455 MovDelay[ax][ay] = 6;
7457 if (MovDelay[ax][ay]) /* wait some time before building new wall */
7460 if (MovDelay[ax][ay])
7464 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
7466 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
7468 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
7470 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
7473 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
7474 element == EL_EXPANDABLE_WALL_ANY)
7478 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
7479 Store[ax][ay-1] = element;
7480 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
7481 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
7482 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
7483 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
7488 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
7489 Store[ax][ay+1] = element;
7490 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
7491 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
7492 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
7493 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
7498 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7499 element == EL_EXPANDABLE_WALL_ANY ||
7500 element == EL_EXPANDABLE_WALL ||
7501 element == EL_BD_EXPANDABLE_WALL)
7505 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
7506 Store[ax-1][ay] = element;
7507 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
7508 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
7509 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
7510 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
7516 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
7517 Store[ax+1][ay] = element;
7518 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
7519 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
7520 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
7521 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
7526 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
7527 DrawLevelField(ax, ay);
7529 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
7531 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
7532 unten_massiv = TRUE;
7533 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
7534 links_massiv = TRUE;
7535 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
7536 rechts_massiv = TRUE;
7538 if (((oben_massiv && unten_massiv) ||
7539 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7540 element == EL_EXPANDABLE_WALL) &&
7541 ((links_massiv && rechts_massiv) ||
7542 element == EL_EXPANDABLE_WALL_VERTICAL))
7543 Feld[ax][ay] = EL_WALL;
7546 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
7549 void CheckForDragon(int x, int y)
7552 boolean dragon_found = FALSE;
7553 static int xy[4][2] =
7561 for (i = 0; i < NUM_DIRECTIONS; i++)
7563 for (j = 0; j < 4; j++)
7565 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7567 if (IN_LEV_FIELD(xx, yy) &&
7568 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
7570 if (Feld[xx][yy] == EL_DRAGON)
7571 dragon_found = TRUE;
7580 for (i = 0; i < NUM_DIRECTIONS; i++)
7582 for (j = 0; j < 3; j++)
7584 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7586 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
7588 Feld[xx][yy] = EL_EMPTY;
7589 DrawLevelField(xx, yy);
7598 static void InitBuggyBase(int x, int y)
7600 int element = Feld[x][y];
7601 int activating_delay = FRAMES_PER_SECOND / 4;
7604 (element == EL_SP_BUGGY_BASE ?
7605 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
7606 element == EL_SP_BUGGY_BASE_ACTIVATING ?
7608 element == EL_SP_BUGGY_BASE_ACTIVE ?
7609 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
7612 static void WarnBuggyBase(int x, int y)
7615 static int xy[4][2] =
7623 for (i = 0; i < NUM_DIRECTIONS; i++)
7625 int xx = x + xy[i][0];
7626 int yy = y + xy[i][1];
7628 if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
7630 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
7637 static void InitTrap(int x, int y)
7639 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
7642 static void ActivateTrap(int x, int y)
7644 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
7647 static void ChangeActiveTrap(int x, int y)
7649 int graphic = IMG_TRAP_ACTIVE;
7651 /* if new animation frame was drawn, correct crumbled sand border */
7652 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
7653 DrawLevelFieldCrumbledSand(x, y);
7656 static int getSpecialActionElement(int element, int number, int base_element)
7658 return (element != EL_EMPTY ? element :
7659 number != -1 ? base_element + number - 1 :
7663 static int getModifiedActionNumber(int value_old, int operator, int operand,
7664 int value_min, int value_max)
7666 int value_new = (operator == CA_MODE_SET ? operand :
7667 operator == CA_MODE_ADD ? value_old + operand :
7668 operator == CA_MODE_SUBTRACT ? value_old - operand :
7669 operator == CA_MODE_MULTIPLY ? value_old * operand :
7670 operator == CA_MODE_DIVIDE ? value_old / MAX(1, operand) :
7671 operator == CA_MODE_MODULO ? value_old % MAX(1, operand) :
7674 return (value_new < value_min ? value_min :
7675 value_new > value_max ? value_max :
7679 static void ExecuteCustomElementAction(int x, int y, int element, int page)
7681 struct ElementInfo *ei = &element_info[element];
7682 struct ElementChangeInfo *change = &ei->change_page[page];
7683 int target_element = change->target_element;
7684 int action_type = change->action_type;
7685 int action_mode = change->action_mode;
7686 int action_arg = change->action_arg;
7689 if (!change->has_action)
7692 /* ---------- determine action paramater values -------------------------- */
7694 int level_time_value =
7695 (level.time > 0 ? TimeLeft :
7698 int action_arg_element =
7699 (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player :
7700 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
7701 action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element :
7704 int action_arg_direction =
7705 (action_arg >= CA_ARG_DIRECTION_LEFT &&
7706 action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
7707 action_arg == CA_ARG_DIRECTION_TRIGGER ?
7708 change->actual_trigger_side :
7709 action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
7710 MV_DIR_OPPOSITE(change->actual_trigger_side) :
7713 int action_arg_number_min =
7714 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
7717 int action_arg_number_max =
7718 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
7719 action_type == CA_SET_LEVEL_GEMS ? 999 :
7720 action_type == CA_SET_LEVEL_TIME ? 9999 :
7721 action_type == CA_SET_LEVEL_SCORE ? 99999 :
7722 action_type == CA_SET_CE_VALUE ? 9999 :
7723 action_type == CA_SET_CE_SCORE ? 9999 :
7726 int action_arg_number_reset =
7727 (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
7728 action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
7729 action_type == CA_SET_LEVEL_TIME ? level.time :
7730 action_type == CA_SET_LEVEL_SCORE ? 0 :
7731 #if USE_NEW_CUSTOM_VALUE
7732 action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
7734 action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
7736 action_type == CA_SET_CE_SCORE ? 0 :
7739 int action_arg_number =
7740 (action_arg <= CA_ARG_MAX ? action_arg :
7741 action_arg >= CA_ARG_SPEED_NOT_MOVING &&
7742 action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
7743 action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
7744 action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
7745 action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
7746 action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
7747 #if USE_NEW_CUSTOM_VALUE
7748 action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
7750 action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
7752 action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
7753 action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
7754 action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
7755 action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
7756 action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
7757 action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
7758 action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
7759 action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
7760 action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
7761 action_arg == CA_ARG_ELEMENT_NR_TARGET ? change->target_element :
7762 action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
7765 int action_arg_number_old =
7766 (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
7767 action_type == CA_SET_LEVEL_TIME ? TimeLeft :
7768 action_type == CA_SET_LEVEL_SCORE ? local_player->score :
7769 action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
7770 action_type == CA_SET_CE_SCORE ? ei->collect_score :
7773 int action_arg_number_new =
7774 getModifiedActionNumber(action_arg_number_old,
7775 action_mode, action_arg_number,
7776 action_arg_number_min, action_arg_number_max);
7778 int trigger_player_bits =
7779 (change->actual_trigger_player >= EL_PLAYER_1 &&
7780 change->actual_trigger_player <= EL_PLAYER_4 ?
7781 (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
7784 int action_arg_player_bits =
7785 (action_arg >= CA_ARG_PLAYER_1 &&
7786 action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
7787 action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
7790 /* ---------- execute action -------------------------------------------- */
7799 /* ---------- level actions ------------------------------------------- */
7801 case CA_RESTART_LEVEL:
7803 game.restart_level = TRUE;
7808 case CA_SHOW_ENVELOPE:
7810 int element = getSpecialActionElement(action_arg_element,
7811 action_arg_number, EL_ENVELOPE_1);
7813 if (IS_ENVELOPE(element))
7814 local_player->show_envelope = element;
7819 case CA_SET_LEVEL_TIME:
7821 if (level.time > 0) /* only modify limited time value */
7823 TimeLeft = action_arg_number_new;
7825 DrawGameValue_Time(TimeLeft);
7827 if (!TimeLeft && setup.time_limit)
7828 for (i = 0; i < MAX_PLAYERS; i++)
7829 KillPlayer(&stored_player[i]);
7835 case CA_SET_LEVEL_SCORE:
7837 local_player->score = action_arg_number_new;
7839 DrawGameValue_Score(local_player->score);
7844 case CA_SET_LEVEL_GEMS:
7846 local_player->gems_still_needed = action_arg_number_new;
7848 DrawGameValue_Emeralds(local_player->gems_still_needed);
7853 #if !USE_PLAYER_GRAVITY
7854 case CA_SET_LEVEL_GRAVITY:
7856 game.gravity = (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
7857 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
7858 action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
7864 case CA_SET_LEVEL_WIND:
7866 game.wind_direction = action_arg_direction;
7871 /* ---------- player actions ------------------------------------------ */
7873 case CA_MOVE_PLAYER:
7875 /* automatically move to the next field in specified direction */
7876 for (i = 0; i < MAX_PLAYERS; i++)
7877 if (trigger_player_bits & (1 << i))
7878 stored_player[i].programmed_action = action_arg_direction;
7883 case CA_EXIT_PLAYER:
7885 for (i = 0; i < MAX_PLAYERS; i++)
7886 if (action_arg_player_bits & (1 << i))
7887 stored_player[i].LevelSolved = stored_player[i].GameOver = TRUE;
7892 case CA_KILL_PLAYER:
7894 for (i = 0; i < MAX_PLAYERS; i++)
7895 if (action_arg_player_bits & (1 << i))
7896 KillPlayer(&stored_player[i]);
7901 case CA_SET_PLAYER_KEYS:
7903 int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
7904 int element = getSpecialActionElement(action_arg_element,
7905 action_arg_number, EL_KEY_1);
7907 if (IS_KEY(element))
7909 for (i = 0; i < MAX_PLAYERS; i++)
7911 if (trigger_player_bits & (1 << i))
7913 stored_player[i].key[KEY_NR(element)] = key_state;
7915 DrawGameDoorValues();
7923 case CA_SET_PLAYER_SPEED:
7925 for (i = 0; i < MAX_PLAYERS; i++)
7927 if (trigger_player_bits & (1 << i))
7929 int move_stepsize = TILEX / stored_player[i].move_delay_value;
7931 if (action_arg == CA_ARG_SPEED_FASTER &&
7932 stored_player[i].cannot_move)
7934 action_arg_number = STEPSIZE_VERY_SLOW;
7936 else if (action_arg == CA_ARG_SPEED_SLOWER ||
7937 action_arg == CA_ARG_SPEED_FASTER)
7939 action_arg_number = 2;
7940 action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
7943 else if (action_arg == CA_ARG_NUMBER_RESET)
7945 action_arg_number = level.initial_player_stepsize[i];
7949 getModifiedActionNumber(move_stepsize,
7952 action_arg_number_min,
7953 action_arg_number_max);
7955 SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
7962 case CA_SET_PLAYER_SHIELD:
7964 for (i = 0; i < MAX_PLAYERS; i++)
7966 if (trigger_player_bits & (1 << i))
7968 if (action_arg == CA_ARG_SHIELD_OFF)
7970 stored_player[i].shield_normal_time_left = 0;
7971 stored_player[i].shield_deadly_time_left = 0;
7973 else if (action_arg == CA_ARG_SHIELD_NORMAL)
7975 stored_player[i].shield_normal_time_left = 999999;
7977 else if (action_arg == CA_ARG_SHIELD_DEADLY)
7979 stored_player[i].shield_normal_time_left = 999999;
7980 stored_player[i].shield_deadly_time_left = 999999;
7988 #if USE_PLAYER_GRAVITY
7989 case CA_SET_PLAYER_GRAVITY:
7991 for (i = 0; i < MAX_PLAYERS; i++)
7993 if (trigger_player_bits & (1 << i))
7995 stored_player[i].gravity =
7996 (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
7997 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
7998 action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
7999 stored_player[i].gravity);
8007 case CA_SET_PLAYER_ARTWORK:
8009 for (i = 0; i < MAX_PLAYERS; i++)
8011 if (trigger_player_bits & (1 << i))
8013 int artwork_element = action_arg_element;
8015 if (action_arg == CA_ARG_ELEMENT_RESET)
8017 (level.use_artwork_element[i] ? level.artwork_element[i] :
8018 stored_player[i].element_nr);
8020 stored_player[i].artwork_element = artwork_element;
8022 SetPlayerWaiting(&stored_player[i], FALSE);
8024 /* set number of special actions for bored and sleeping animation */
8025 stored_player[i].num_special_action_bored =
8026 get_num_special_action(artwork_element,
8027 ACTION_BORING_1, ACTION_BORING_LAST);
8028 stored_player[i].num_special_action_sleeping =
8029 get_num_special_action(artwork_element,
8030 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
8037 /* ---------- CE actions ---------------------------------------------- */
8039 case CA_SET_CE_VALUE:
8041 #if USE_NEW_CUSTOM_VALUE
8042 int last_ce_value = CustomValue[x][y];
8044 CustomValue[x][y] = action_arg_number_new;
8046 if (CustomValue[x][y] != last_ce_value)
8048 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
8049 CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
8051 if (CustomValue[x][y] == 0)
8053 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
8054 CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
8062 case CA_SET_CE_SCORE:
8064 #if USE_NEW_CUSTOM_VALUE
8065 int last_ce_score = ei->collect_score;
8067 ei->collect_score = action_arg_number_new;
8069 if (ei->collect_score != last_ce_score)
8071 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
8072 CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
8074 if (ei->collect_score == 0)
8078 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
8079 CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
8082 This is a very special case that seems to be a mixture between
8083 CheckElementChange() and CheckTriggeredElementChange(): while
8084 the first one only affects single elements that are triggered
8085 directly, the second one affects multiple elements in the playfield
8086 that are triggered indirectly by another element. This is a third
8087 case: Changing the CE score always affects multiple identical CEs,
8088 so every affected CE must be checked, not only the single CE for
8089 which the CE score was changed in the first place (as every instance
8090 of that CE shares the same CE score, and therefore also can change)!
8092 SCAN_PLAYFIELD(xx, yy)
8094 if (Feld[xx][yy] == element)
8095 CheckElementChange(xx, yy, element, EL_UNDEFINED,
8096 CE_SCORE_GETS_ZERO);
8105 /* ---------- engine actions ------------------------------------------ */
8107 case CA_SET_ENGINE_SCAN_MODE:
8109 InitPlayfieldScanMode(action_arg);
8119 static void CreateFieldExt(int x, int y, int element, boolean is_change)
8121 int old_element = Feld[x][y];
8122 int new_element = get_element_from_group_element(element);
8123 int previous_move_direction = MovDir[x][y];
8124 #if USE_NEW_CUSTOM_VALUE
8125 int last_ce_value = CustomValue[x][y];
8127 boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
8128 boolean add_player_onto_element = (new_element_is_player &&
8129 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
8130 /* this breaks SnakeBite when a snake is
8131 halfway through a door that closes */
8132 /* NOW FIXED AT LEVEL INIT IN files.c */
8133 new_element != EL_SOKOBAN_FIELD_PLAYER &&
8135 IS_WALKABLE(old_element));
8138 /* check if element under the player changes from accessible to unaccessible
8139 (needed for special case of dropping element which then changes) */
8140 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
8141 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
8149 if (!add_player_onto_element)
8151 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
8152 RemoveMovingField(x, y);
8156 Feld[x][y] = new_element;
8158 #if !USE_GFX_RESET_GFX_ANIMATION
8159 ResetGfxAnimation(x, y);
8160 ResetRandomAnimationValue(x, y);
8163 if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
8164 MovDir[x][y] = previous_move_direction;
8166 #if USE_NEW_CUSTOM_VALUE
8167 if (element_info[new_element].use_last_ce_value)
8168 CustomValue[x][y] = last_ce_value;
8171 InitField_WithBug1(x, y, FALSE);
8173 new_element = Feld[x][y]; /* element may have changed */
8175 #if USE_GFX_RESET_GFX_ANIMATION
8176 ResetGfxAnimation(x, y);
8177 ResetRandomAnimationValue(x, y);
8180 DrawLevelField(x, y);
8182 if (GFX_CRUMBLED(new_element))
8183 DrawLevelFieldCrumbledSandNeighbours(x, y);
8187 /* check if element under the player changes from accessible to unaccessible
8188 (needed for special case of dropping element which then changes) */
8189 /* (must be checked after creating new element for walkable group elements) */
8190 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
8191 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
8199 /* "ChangeCount" not set yet to allow "entered by player" change one time */
8200 if (new_element_is_player)
8201 RelocatePlayer(x, y, new_element);
8204 ChangeCount[x][y]++; /* count number of changes in the same frame */
8206 TestIfBadThingTouchesPlayer(x, y);
8207 TestIfPlayerTouchesCustomElement(x, y);
8208 TestIfElementTouchesCustomElement(x, y);
8211 static void CreateField(int x, int y, int element)
8213 CreateFieldExt(x, y, element, FALSE);
8216 static void CreateElementFromChange(int x, int y, int element)
8218 element = GET_VALID_RUNTIME_ELEMENT(element);
8220 #if USE_STOP_CHANGED_ELEMENTS
8221 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
8223 int old_element = Feld[x][y];
8225 /* prevent changed element from moving in same engine frame
8226 unless both old and new element can either fall or move */
8227 if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
8228 (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
8233 CreateFieldExt(x, y, element, TRUE);
8236 static boolean ChangeElement(int x, int y, int element, int page)
8238 struct ElementInfo *ei = &element_info[element];
8239 struct ElementChangeInfo *change = &ei->change_page[page];
8240 int ce_value = CustomValue[x][y];
8241 int ce_score = ei->collect_score;
8243 int old_element = Feld[x][y];
8245 /* always use default change event to prevent running into a loop */
8246 if (ChangeEvent[x][y] == -1)
8247 ChangeEvent[x][y] = CE_DELAY;
8249 if (ChangeEvent[x][y] == CE_DELAY)
8251 /* reset actual trigger element, trigger player and action element */
8252 change->actual_trigger_element = EL_EMPTY;
8253 change->actual_trigger_player = EL_PLAYER_1;
8254 change->actual_trigger_side = CH_SIDE_NONE;
8255 change->actual_trigger_ce_value = 0;
8256 change->actual_trigger_ce_score = 0;
8259 /* do not change elements more than a specified maximum number of changes */
8260 if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
8263 ChangeCount[x][y]++; /* count number of changes in the same frame */
8265 if (change->explode)
8272 if (change->use_target_content)
8274 boolean complete_replace = TRUE;
8275 boolean can_replace[3][3];
8278 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
8281 boolean is_walkable;
8282 boolean is_diggable;
8283 boolean is_collectible;
8284 boolean is_removable;
8285 boolean is_destructible;
8286 int ex = x + xx - 1;
8287 int ey = y + yy - 1;
8288 int content_element = change->target_content.e[xx][yy];
8291 can_replace[xx][yy] = TRUE;
8293 if (ex == x && ey == y) /* do not check changing element itself */
8296 if (content_element == EL_EMPTY_SPACE)
8298 can_replace[xx][yy] = FALSE; /* do not replace border with space */
8303 if (!IN_LEV_FIELD(ex, ey))
8305 can_replace[xx][yy] = FALSE;
8306 complete_replace = FALSE;
8313 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
8314 e = MovingOrBlocked2Element(ex, ey);
8316 is_empty = (IS_FREE(ex, ey) ||
8317 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
8319 is_walkable = (is_empty || IS_WALKABLE(e));
8320 is_diggable = (is_empty || IS_DIGGABLE(e));
8321 is_collectible = (is_empty || IS_COLLECTIBLE(e));
8322 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
8323 is_removable = (is_diggable || is_collectible);
8325 can_replace[xx][yy] =
8326 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
8327 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
8328 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
8329 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
8330 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
8331 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
8332 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
8334 if (!can_replace[xx][yy])
8335 complete_replace = FALSE;
8338 if (!change->only_if_complete || complete_replace)
8340 boolean something_has_changed = FALSE;
8342 if (change->only_if_complete && change->use_random_replace &&
8343 RND(100) < change->random_percentage)
8346 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
8348 int ex = x + xx - 1;
8349 int ey = y + yy - 1;
8350 int content_element;
8352 if (can_replace[xx][yy] && (!change->use_random_replace ||
8353 RND(100) < change->random_percentage))
8355 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
8356 RemoveMovingField(ex, ey);
8358 ChangeEvent[ex][ey] = ChangeEvent[x][y];
8360 content_element = change->target_content.e[xx][yy];
8361 target_element = GET_TARGET_ELEMENT(element, content_element, change,
8362 ce_value, ce_score);
8364 CreateElementFromChange(ex, ey, target_element);
8366 something_has_changed = TRUE;
8368 /* for symmetry reasons, freeze newly created border elements */
8369 if (ex != x || ey != y)
8370 Stop[ex][ey] = TRUE; /* no more moving in this frame */
8374 if (something_has_changed)
8376 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
8377 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
8383 target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
8384 ce_value, ce_score);
8386 if (element == EL_DIAGONAL_GROWING ||
8387 element == EL_DIAGONAL_SHRINKING)
8389 target_element = Store[x][y];
8391 Store[x][y] = EL_EMPTY;
8394 CreateElementFromChange(x, y, target_element);
8396 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
8397 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
8400 /* this uses direct change before indirect change */
8401 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
8406 #if USE_NEW_DELAYED_ACTION
8408 static void HandleElementChange(int x, int y, int page)
8410 int element = MovingOrBlocked2Element(x, y);
8411 struct ElementInfo *ei = &element_info[element];
8412 struct ElementChangeInfo *change = &ei->change_page[page];
8415 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
8416 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
8419 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
8420 x, y, element, element_info[element].token_name);
8421 printf("HandleElementChange(): This should never happen!\n");
8426 /* this can happen with classic bombs on walkable, changing elements */
8427 if (!CAN_CHANGE_OR_HAS_ACTION(element))
8430 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
8431 ChangeDelay[x][y] = 0;
8437 if (ChangeDelay[x][y] == 0) /* initialize element change */
8439 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
8441 if (change->can_change)
8443 ResetGfxAnimation(x, y);
8444 ResetRandomAnimationValue(x, y);
8446 if (change->pre_change_function)
8447 change->pre_change_function(x, y);
8451 ChangeDelay[x][y]--;
8453 if (ChangeDelay[x][y] != 0) /* continue element change */
8455 if (change->can_change)
8457 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8459 if (IS_ANIMATED(graphic))
8460 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8462 if (change->change_function)
8463 change->change_function(x, y);
8466 else /* finish element change */
8468 if (ChangePage[x][y] != -1) /* remember page from delayed change */
8470 page = ChangePage[x][y];
8471 ChangePage[x][y] = -1;
8473 change = &ei->change_page[page];
8476 if (IS_MOVING(x, y)) /* never change a running system ;-) */
8478 ChangeDelay[x][y] = 1; /* try change after next move step */
8479 ChangePage[x][y] = page; /* remember page to use for change */
8484 if (change->can_change)
8486 if (ChangeElement(x, y, element, page))
8488 if (change->post_change_function)
8489 change->post_change_function(x, y);
8493 if (change->has_action)
8494 ExecuteCustomElementAction(x, y, element, page);
8500 static void HandleElementChange(int x, int y, int page)
8502 int element = MovingOrBlocked2Element(x, y);
8503 struct ElementInfo *ei = &element_info[element];
8504 struct ElementChangeInfo *change = &ei->change_page[page];
8507 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
8510 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
8511 x, y, element, element_info[element].token_name);
8512 printf("HandleElementChange(): This should never happen!\n");
8517 /* this can happen with classic bombs on walkable, changing elements */
8518 if (!CAN_CHANGE(element))
8521 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
8522 ChangeDelay[x][y] = 0;
8528 if (ChangeDelay[x][y] == 0) /* initialize element change */
8530 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
8532 ResetGfxAnimation(x, y);
8533 ResetRandomAnimationValue(x, y);
8535 if (change->pre_change_function)
8536 change->pre_change_function(x, y);
8539 ChangeDelay[x][y]--;
8541 if (ChangeDelay[x][y] != 0) /* continue element change */
8543 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8545 if (IS_ANIMATED(graphic))
8546 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8548 if (change->change_function)
8549 change->change_function(x, y);
8551 else /* finish element change */
8553 if (ChangePage[x][y] != -1) /* remember page from delayed change */
8555 page = ChangePage[x][y];
8556 ChangePage[x][y] = -1;
8558 change = &ei->change_page[page];
8561 if (IS_MOVING(x, y)) /* never change a running system ;-) */
8563 ChangeDelay[x][y] = 1; /* try change after next move step */
8564 ChangePage[x][y] = page; /* remember page to use for change */
8569 if (ChangeElement(x, y, element, page))
8571 if (change->post_change_function)
8572 change->post_change_function(x, y);
8579 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
8580 int trigger_element,
8586 boolean change_done_any = FALSE;
8587 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
8590 if (!(trigger_events[trigger_element][trigger_event]))
8593 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8595 int element = EL_CUSTOM_START + i;
8596 boolean change_done = FALSE;
8599 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
8600 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
8603 for (p = 0; p < element_info[element].num_change_pages; p++)
8605 struct ElementChangeInfo *change = &element_info[element].change_page[p];
8607 if (change->can_change_or_has_action &&
8608 change->has_event[trigger_event] &&
8609 change->trigger_side & trigger_side &&
8610 change->trigger_player & trigger_player &&
8611 change->trigger_page & trigger_page_bits &&
8612 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
8614 change->actual_trigger_element = trigger_element;
8615 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
8616 change->actual_trigger_side = trigger_side;
8617 change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
8618 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
8620 if ((change->can_change && !change_done) || change->has_action)
8624 SCAN_PLAYFIELD(x, y)
8626 if (Feld[x][y] == element)
8628 if (change->can_change && !change_done)
8630 ChangeDelay[x][y] = 1;
8631 ChangeEvent[x][y] = trigger_event;
8633 HandleElementChange(x, y, p);
8635 #if USE_NEW_DELAYED_ACTION
8636 else if (change->has_action)
8638 ExecuteCustomElementAction(x, y, element, p);
8639 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8642 if (change->has_action)
8644 ExecuteCustomElementAction(x, y, element, p);
8645 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8651 if (change->can_change)
8654 change_done_any = TRUE;
8661 return change_done_any;
8664 static boolean CheckElementChangeExt(int x, int y,
8666 int trigger_element,
8671 boolean change_done = FALSE;
8674 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
8675 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
8678 if (Feld[x][y] == EL_BLOCKED)
8680 Blocked2Moving(x, y, &x, &y);
8681 element = Feld[x][y];
8685 /* check if element has already changed */
8686 if (Feld[x][y] != element)
8689 /* check if element has already changed or is about to change after moving */
8690 if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
8691 Feld[x][y] != element) ||
8693 (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
8694 (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
8695 ChangePage[x][y] != -1)))
8699 for (p = 0; p < element_info[element].num_change_pages; p++)
8701 struct ElementChangeInfo *change = &element_info[element].change_page[p];
8703 boolean check_trigger_element =
8704 (trigger_event == CE_TOUCHING_X ||
8705 trigger_event == CE_HITTING_X ||
8706 trigger_event == CE_HIT_BY_X);
8708 if (change->can_change_or_has_action &&
8709 change->has_event[trigger_event] &&
8710 change->trigger_side & trigger_side &&
8711 change->trigger_player & trigger_player &&
8712 (!check_trigger_element ||
8713 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
8715 change->actual_trigger_element = trigger_element;
8716 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
8717 change->actual_trigger_side = trigger_side;
8718 change->actual_trigger_ce_value = CustomValue[x][y];
8719 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
8721 /* special case: trigger element not at (x,y) position for some events */
8722 if (check_trigger_element)
8734 { 0, 0 }, { 0, 0 }, { 0, 0 },
8738 int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
8739 int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
8741 change->actual_trigger_ce_value = CustomValue[xx][yy];
8742 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
8745 if (change->can_change && !change_done)
8747 ChangeDelay[x][y] = 1;
8748 ChangeEvent[x][y] = trigger_event;
8750 HandleElementChange(x, y, p);
8754 #if USE_NEW_DELAYED_ACTION
8755 else if (change->has_action)
8757 ExecuteCustomElementAction(x, y, element, p);
8758 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8761 if (change->has_action)
8763 ExecuteCustomElementAction(x, y, element, p);
8764 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8773 static void PlayPlayerSound(struct PlayerInfo *player)
8775 int jx = player->jx, jy = player->jy;
8776 int sound_element = player->artwork_element;
8777 int last_action = player->last_action_waiting;
8778 int action = player->action_waiting;
8780 if (player->is_waiting)
8782 if (action != last_action)
8783 PlayLevelSoundElementAction(jx, jy, sound_element, action);
8785 PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
8789 if (action != last_action)
8790 StopSound(element_info[sound_element].sound[last_action]);
8792 if (last_action == ACTION_SLEEPING)
8793 PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
8797 static void PlayAllPlayersSound()
8801 for (i = 0; i < MAX_PLAYERS; i++)
8802 if (stored_player[i].active)
8803 PlayPlayerSound(&stored_player[i]);
8806 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
8808 boolean last_waiting = player->is_waiting;
8809 int move_dir = player->MovDir;
8811 player->dir_waiting = move_dir;
8812 player->last_action_waiting = player->action_waiting;
8816 if (!last_waiting) /* not waiting -> waiting */
8818 player->is_waiting = TRUE;
8820 player->frame_counter_bored =
8822 game.player_boring_delay_fixed +
8823 SimpleRND(game.player_boring_delay_random);
8824 player->frame_counter_sleeping =
8826 game.player_sleeping_delay_fixed +
8827 SimpleRND(game.player_sleeping_delay_random);
8829 InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
8832 if (game.player_sleeping_delay_fixed +
8833 game.player_sleeping_delay_random > 0 &&
8834 player->anim_delay_counter == 0 &&
8835 player->post_delay_counter == 0 &&
8836 FrameCounter >= player->frame_counter_sleeping)
8837 player->is_sleeping = TRUE;
8838 else if (game.player_boring_delay_fixed +
8839 game.player_boring_delay_random > 0 &&
8840 FrameCounter >= player->frame_counter_bored)
8841 player->is_bored = TRUE;
8843 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
8844 player->is_bored ? ACTION_BORING :
8847 if (player->is_sleeping && player->use_murphy)
8849 /* special case for sleeping Murphy when leaning against non-free tile */
8851 if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
8852 (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
8853 !IS_MOVING(player->jx - 1, player->jy)))
8855 else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
8856 (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
8857 !IS_MOVING(player->jx + 1, player->jy)))
8858 move_dir = MV_RIGHT;
8860 player->is_sleeping = FALSE;
8862 player->dir_waiting = move_dir;
8865 if (player->is_sleeping)
8867 if (player->num_special_action_sleeping > 0)
8869 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
8871 int last_special_action = player->special_action_sleeping;
8872 int num_special_action = player->num_special_action_sleeping;
8873 int special_action =
8874 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
8875 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
8876 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
8877 last_special_action + 1 : ACTION_SLEEPING);
8878 int special_graphic =
8879 el_act_dir2img(player->artwork_element, special_action, move_dir);
8881 player->anim_delay_counter =
8882 graphic_info[special_graphic].anim_delay_fixed +
8883 SimpleRND(graphic_info[special_graphic].anim_delay_random);
8884 player->post_delay_counter =
8885 graphic_info[special_graphic].post_delay_fixed +
8886 SimpleRND(graphic_info[special_graphic].post_delay_random);
8888 player->special_action_sleeping = special_action;
8891 if (player->anim_delay_counter > 0)
8893 player->action_waiting = player->special_action_sleeping;
8894 player->anim_delay_counter--;
8896 else if (player->post_delay_counter > 0)
8898 player->post_delay_counter--;
8902 else if (player->is_bored)
8904 if (player->num_special_action_bored > 0)
8906 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
8908 int special_action =
8909 ACTION_BORING_1 + SimpleRND(player->num_special_action_bored);
8910 int special_graphic =
8911 el_act_dir2img(player->artwork_element, special_action, move_dir);
8913 player->anim_delay_counter =
8914 graphic_info[special_graphic].anim_delay_fixed +
8915 SimpleRND(graphic_info[special_graphic].anim_delay_random);
8916 player->post_delay_counter =
8917 graphic_info[special_graphic].post_delay_fixed +
8918 SimpleRND(graphic_info[special_graphic].post_delay_random);
8920 player->special_action_bored = special_action;
8923 if (player->anim_delay_counter > 0)
8925 player->action_waiting = player->special_action_bored;
8926 player->anim_delay_counter--;
8928 else if (player->post_delay_counter > 0)
8930 player->post_delay_counter--;
8935 else if (last_waiting) /* waiting -> not waiting */
8937 player->is_waiting = FALSE;
8938 player->is_bored = FALSE;
8939 player->is_sleeping = FALSE;
8941 player->frame_counter_bored = -1;
8942 player->frame_counter_sleeping = -1;
8944 player->anim_delay_counter = 0;
8945 player->post_delay_counter = 0;
8947 player->dir_waiting = player->MovDir;
8948 player->action_waiting = ACTION_DEFAULT;
8950 player->special_action_bored = ACTION_DEFAULT;
8951 player->special_action_sleeping = ACTION_DEFAULT;
8955 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
8957 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
8958 int left = player_action & JOY_LEFT;
8959 int right = player_action & JOY_RIGHT;
8960 int up = player_action & JOY_UP;
8961 int down = player_action & JOY_DOWN;
8962 int button1 = player_action & JOY_BUTTON_1;
8963 int button2 = player_action & JOY_BUTTON_2;
8964 int dx = (left ? -1 : right ? 1 : 0);
8965 int dy = (up ? -1 : down ? 1 : 0);
8967 if (!player->active || tape.pausing)
8973 snapped = SnapField(player, dx, dy);
8977 dropped = DropElement(player);
8979 moved = MovePlayer(player, dx, dy);
8982 if (tape.single_step && tape.recording && !tape.pausing)
8984 if (button1 || (dropped && !moved))
8986 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8987 SnapField(player, 0, 0); /* stop snapping */
8991 SetPlayerWaiting(player, FALSE);
8993 return player_action;
8997 /* no actions for this player (no input at player's configured device) */
8999 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
9000 SnapField(player, 0, 0);
9001 CheckGravityMovementWhenNotMoving(player);
9003 if (player->MovPos == 0)
9004 SetPlayerWaiting(player, TRUE);
9006 if (player->MovPos == 0) /* needed for tape.playing */
9007 player->is_moving = FALSE;
9009 player->is_dropping = FALSE;
9010 player->is_dropping_pressed = FALSE;
9011 player->drop_pressed_delay = 0;
9017 static void CheckLevelTime()
9021 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9023 if (level.native_em_level->lev->home == 0) /* all players at home */
9025 local_player->LevelSolved = TRUE;
9026 AllPlayersGone = TRUE;
9028 level.native_em_level->lev->home = -1;
9031 if (level.native_em_level->ply[0]->alive == 0 &&
9032 level.native_em_level->ply[1]->alive == 0 &&
9033 level.native_em_level->ply[2]->alive == 0 &&
9034 level.native_em_level->ply[3]->alive == 0) /* all dead */
9035 AllPlayersGone = TRUE;
9038 if (TimeFrames >= FRAMES_PER_SECOND)
9043 for (i = 0; i < MAX_PLAYERS; i++)
9045 struct PlayerInfo *player = &stored_player[i];
9047 if (SHIELD_ON(player))
9049 player->shield_normal_time_left--;
9051 if (player->shield_deadly_time_left > 0)
9052 player->shield_deadly_time_left--;
9056 if (!level.use_step_counter)
9064 if (TimeLeft <= 10 && setup.time_limit)
9065 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
9067 DrawGameValue_Time(TimeLeft);
9069 if (!TimeLeft && setup.time_limit)
9071 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9072 level.native_em_level->lev->killed_out_of_time = TRUE;
9074 for (i = 0; i < MAX_PLAYERS; i++)
9075 KillPlayer(&stored_player[i]);
9078 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
9079 DrawGameValue_Time(TimePlayed);
9081 level.native_em_level->lev->time =
9082 (level.time == 0 ? TimePlayed : TimeLeft);
9085 if (tape.recording || tape.playing)
9086 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
9090 void AdvanceFrameAndPlayerCounters(int player_nr)
9094 /* advance frame counters (global frame counter and time frame counter) */
9098 /* advance player counters (counters for move delay, move animation etc.) */
9099 for (i = 0; i < MAX_PLAYERS; i++)
9101 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
9102 int move_delay_value = stored_player[i].move_delay_value;
9103 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
9105 if (!advance_player_counters) /* not all players may be affected */
9108 #if USE_NEW_PLAYER_ANIM
9109 if (move_frames == 0) /* less than one move per game frame */
9111 int stepsize = TILEX / move_delay_value;
9112 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
9113 int count = (stored_player[i].is_moving ?
9114 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
9116 if (count % delay == 0)
9121 stored_player[i].Frame += move_frames;
9123 if (stored_player[i].MovPos != 0)
9124 stored_player[i].StepFrame += move_frames;
9126 if (stored_player[i].move_delay > 0)
9127 stored_player[i].move_delay--;
9129 /* due to bugs in previous versions, counter must count up, not down */
9130 if (stored_player[i].push_delay != -1)
9131 stored_player[i].push_delay++;
9133 if (stored_player[i].drop_delay > 0)
9134 stored_player[i].drop_delay--;
9136 if (stored_player[i].is_dropping_pressed)
9137 stored_player[i].drop_pressed_delay++;
9141 void StartGameActions(boolean init_network_game, boolean record_tape,
9144 unsigned long new_random_seed = InitRND(random_seed);
9147 TapeStartRecording(new_random_seed);
9149 #if defined(NETWORK_AVALIABLE)
9150 if (init_network_game)
9152 SendToServer_StartPlaying();
9163 static unsigned long game_frame_delay = 0;
9164 unsigned long game_frame_delay_value;
9165 byte *recorded_player_action;
9166 byte summarized_player_action = 0;
9167 byte tape_action[MAX_PLAYERS];
9170 if (game.restart_level)
9171 StartGameActions(options.network, setup.autorecord, NEW_RANDOMIZE);
9173 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9175 if (level.native_em_level->lev->home == 0) /* all players at home */
9177 local_player->LevelSolved = TRUE;
9178 AllPlayersGone = TRUE;
9180 level.native_em_level->lev->home = -1;
9183 if (level.native_em_level->ply[0]->alive == 0 &&
9184 level.native_em_level->ply[1]->alive == 0 &&
9185 level.native_em_level->ply[2]->alive == 0 &&
9186 level.native_em_level->ply[3]->alive == 0) /* all dead */
9187 AllPlayersGone = TRUE;
9190 if (local_player->LevelSolved)
9193 if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
9196 if (game_status != GAME_MODE_PLAYING) /* status might have changed */
9199 game_frame_delay_value =
9200 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
9202 if (tape.playing && tape.warp_forward && !tape.pausing)
9203 game_frame_delay_value = 0;
9205 /* ---------- main game synchronization point ---------- */
9207 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
9209 if (network_playing && !network_player_action_received)
9211 /* try to get network player actions in time */
9213 #if defined(NETWORK_AVALIABLE)
9214 /* last chance to get network player actions without main loop delay */
9218 /* game was quit by network peer */
9219 if (game_status != GAME_MODE_PLAYING)
9222 if (!network_player_action_received)
9223 return; /* failed to get network player actions in time */
9225 /* do not yet reset "network_player_action_received" (for tape.pausing) */
9231 /* at this point we know that we really continue executing the game */
9233 network_player_action_received = FALSE;
9235 /* when playing tape, read previously recorded player input from tape data */
9236 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
9239 /* TapePlayAction() may return NULL when toggling to "pause before death" */
9244 if (tape.set_centered_player)
9246 game.centered_player_nr_next = tape.centered_player_nr_next;
9247 game.set_centered_player = TRUE;
9250 for (i = 0; i < MAX_PLAYERS; i++)
9252 summarized_player_action |= stored_player[i].action;
9254 if (!network_playing)
9255 stored_player[i].effective_action = stored_player[i].action;
9258 #if defined(NETWORK_AVALIABLE)
9259 if (network_playing)
9260 SendToServer_MovePlayer(summarized_player_action);
9263 if (!options.network && !setup.team_mode)
9264 local_player->effective_action = summarized_player_action;
9266 if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
9268 for (i = 0; i < MAX_PLAYERS; i++)
9269 stored_player[i].effective_action =
9270 (i == game.centered_player_nr ? summarized_player_action : 0);
9273 if (recorded_player_action != NULL)
9274 for (i = 0; i < MAX_PLAYERS; i++)
9275 stored_player[i].effective_action = recorded_player_action[i];
9277 for (i = 0; i < MAX_PLAYERS; i++)
9279 tape_action[i] = stored_player[i].effective_action;
9281 /* (this can only happen in the R'n'D game engine) */
9282 if (tape.recording && tape_action[i] && !tape.player_participates[i])
9283 tape.player_participates[i] = TRUE; /* player just appeared from CE */
9286 /* only record actions from input devices, but not programmed actions */
9288 TapeRecordAction(tape_action);
9290 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9292 GameActions_EM_Main();
9300 void GameActions_EM_Main()
9302 byte effective_action[MAX_PLAYERS];
9303 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
9306 for (i = 0; i < MAX_PLAYERS; i++)
9307 effective_action[i] = stored_player[i].effective_action;
9309 GameActions_EM(effective_action, warp_mode);
9313 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
9316 void GameActions_RND()
9318 int magic_wall_x = 0, magic_wall_y = 0;
9319 int i, x, y, element, graphic;
9321 InitPlayfieldScanModeVars();
9323 #if USE_ONE_MORE_CHANGE_PER_FRAME
9324 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
9326 SCAN_PLAYFIELD(x, y)
9328 ChangeCount[x][y] = 0;
9329 ChangeEvent[x][y] = -1;
9334 if (game.set_centered_player)
9336 boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
9338 /* switching to "all players" only possible if all players fit to screen */
9339 if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
9341 game.centered_player_nr_next = game.centered_player_nr;
9342 game.set_centered_player = FALSE;
9345 /* do not switch focus to non-existing (or non-active) player */
9346 if (game.centered_player_nr_next >= 0 &&
9347 !stored_player[game.centered_player_nr_next].active)
9349 game.centered_player_nr_next = game.centered_player_nr;
9350 game.set_centered_player = FALSE;
9354 if (game.set_centered_player &&
9355 ScreenMovPos == 0) /* screen currently aligned at tile position */
9359 if (game.centered_player_nr_next == -1)
9361 setScreenCenteredToAllPlayers(&sx, &sy);
9365 sx = stored_player[game.centered_player_nr_next].jx;
9366 sy = stored_player[game.centered_player_nr_next].jy;
9369 game.centered_player_nr = game.centered_player_nr_next;
9370 game.set_centered_player = FALSE;
9372 DrawRelocateScreen(sx, sy, MV_NONE, TRUE, setup.quick_switch);
9373 DrawGameDoorValues();
9376 for (i = 0; i < MAX_PLAYERS; i++)
9378 int actual_player_action = stored_player[i].effective_action;
9381 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
9382 - rnd_equinox_tetrachloride 048
9383 - rnd_equinox_tetrachloride_ii 096
9384 - rnd_emanuel_schmieg 002
9385 - doctor_sloan_ww 001, 020
9387 if (stored_player[i].MovPos == 0)
9388 CheckGravityMovement(&stored_player[i]);
9391 /* overwrite programmed action with tape action */
9392 if (stored_player[i].programmed_action)
9393 actual_player_action = stored_player[i].programmed_action;
9395 PlayerActions(&stored_player[i], actual_player_action);
9397 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
9400 ScrollScreen(NULL, SCROLL_GO_ON);
9402 /* for backwards compatibility, the following code emulates a fixed bug that
9403 occured when pushing elements (causing elements that just made their last
9404 pushing step to already (if possible) make their first falling step in the
9405 same game frame, which is bad); this code is also needed to use the famous
9406 "spring push bug" which is used in older levels and might be wanted to be
9407 used also in newer levels, but in this case the buggy pushing code is only
9408 affecting the "spring" element and no other elements */
9410 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
9412 for (i = 0; i < MAX_PLAYERS; i++)
9414 struct PlayerInfo *player = &stored_player[i];
9418 if (player->active && player->is_pushing && player->is_moving &&
9420 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
9421 Feld[x][y] == EL_SPRING))
9423 ContinueMoving(x, y);
9425 /* continue moving after pushing (this is actually a bug) */
9426 if (!IS_MOVING(x, y))
9434 SCAN_PLAYFIELD(x, y)
9436 ChangeCount[x][y] = 0;
9437 ChangeEvent[x][y] = -1;
9439 /* this must be handled before main playfield loop */
9440 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
9443 if (MovDelay[x][y] <= 0)
9447 #if USE_NEW_SNAP_DELAY
9448 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
9451 if (MovDelay[x][y] <= 0)
9454 DrawLevelField(x, y);
9456 TestIfElementTouchesCustomElement(x, y); /* for empty space */
9462 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
9464 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
9465 printf("GameActions(): This should never happen!\n");
9467 ChangePage[x][y] = -1;
9472 if (WasJustMoving[x][y] > 0)
9473 WasJustMoving[x][y]--;
9474 if (WasJustFalling[x][y] > 0)
9475 WasJustFalling[x][y]--;
9476 if (CheckCollision[x][y] > 0)
9477 CheckCollision[x][y]--;
9481 /* reset finished pushing action (not done in ContinueMoving() to allow
9482 continuous pushing animation for elements with zero push delay) */
9483 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
9485 ResetGfxAnimation(x, y);
9486 DrawLevelField(x, y);
9490 if (IS_BLOCKED(x, y))
9494 Blocked2Moving(x, y, &oldx, &oldy);
9495 if (!IS_MOVING(oldx, oldy))
9497 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
9498 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
9499 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
9500 printf("GameActions(): This should never happen!\n");
9506 SCAN_PLAYFIELD(x, y)
9508 element = Feld[x][y];
9509 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9511 ResetGfxFrame(x, y, TRUE);
9513 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
9514 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
9515 ResetRandomAnimationValue(x, y);
9517 SetRandomAnimationValue(x, y);
9519 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
9521 if (IS_INACTIVE(element))
9523 if (IS_ANIMATED(graphic))
9524 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9529 /* this may take place after moving, so 'element' may have changed */
9530 if (IS_CHANGING(x, y) &&
9531 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
9533 int page = element_info[element].event_page_nr[CE_DELAY];
9536 HandleElementChange(x, y, page);
9538 if (CAN_CHANGE(element))
9539 HandleElementChange(x, y, page);
9541 if (HAS_ACTION(element))
9542 ExecuteCustomElementAction(x, y, element, page);
9545 element = Feld[x][y];
9546 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9549 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
9553 element = Feld[x][y];
9554 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9556 if (IS_ANIMATED(graphic) &&
9559 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9561 if (IS_GEM(element) || element == EL_SP_INFOTRON)
9562 EdelsteinFunkeln(x, y);
9564 else if ((element == EL_ACID ||
9565 element == EL_EXIT_OPEN ||
9566 element == EL_SP_EXIT_OPEN ||
9567 element == EL_SP_TERMINAL ||
9568 element == EL_SP_TERMINAL_ACTIVE ||
9569 element == EL_EXTRA_TIME ||
9570 element == EL_SHIELD_NORMAL ||
9571 element == EL_SHIELD_DEADLY) &&
9572 IS_ANIMATED(graphic))
9573 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9574 else if (IS_MOVING(x, y))
9575 ContinueMoving(x, y);
9576 else if (IS_ACTIVE_BOMB(element))
9577 CheckDynamite(x, y);
9578 else if (element == EL_AMOEBA_GROWING)
9579 AmoebeWaechst(x, y);
9580 else if (element == EL_AMOEBA_SHRINKING)
9581 AmoebaDisappearing(x, y);
9583 #if !USE_NEW_AMOEBA_CODE
9584 else if (IS_AMOEBALIVE(element))
9585 AmoebeAbleger(x, y);
9588 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
9590 else if (element == EL_EXIT_CLOSED)
9592 else if (element == EL_SP_EXIT_CLOSED)
9594 else if (element == EL_EXPANDABLE_WALL_GROWING)
9596 else if (element == EL_EXPANDABLE_WALL ||
9597 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9598 element == EL_EXPANDABLE_WALL_VERTICAL ||
9599 element == EL_EXPANDABLE_WALL_ANY ||
9600 element == EL_BD_EXPANDABLE_WALL)
9602 else if (element == EL_FLAMES)
9603 CheckForDragon(x, y);
9604 else if (element == EL_EXPLOSION)
9605 ; /* drawing of correct explosion animation is handled separately */
9606 else if (element == EL_ELEMENT_SNAPPING ||
9607 element == EL_DIAGONAL_SHRINKING ||
9608 element == EL_DIAGONAL_GROWING)
9610 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
9612 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9614 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
9615 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9617 if (IS_BELT_ACTIVE(element))
9618 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
9620 if (game.magic_wall_active)
9622 int jx = local_player->jx, jy = local_player->jy;
9624 /* play the element sound at the position nearest to the player */
9625 if ((element == EL_MAGIC_WALL_FULL ||
9626 element == EL_MAGIC_WALL_ACTIVE ||
9627 element == EL_MAGIC_WALL_EMPTYING ||
9628 element == EL_BD_MAGIC_WALL_FULL ||
9629 element == EL_BD_MAGIC_WALL_ACTIVE ||
9630 element == EL_BD_MAGIC_WALL_EMPTYING) &&
9631 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
9639 #if USE_NEW_AMOEBA_CODE
9640 /* new experimental amoeba growth stuff */
9641 if (!(FrameCounter % 8))
9643 static unsigned long random = 1684108901;
9645 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
9647 x = RND(lev_fieldx);
9648 y = RND(lev_fieldy);
9649 element = Feld[x][y];
9651 if (!IS_PLAYER(x,y) &&
9652 (element == EL_EMPTY ||
9653 CAN_GROW_INTO(element) ||
9654 element == EL_QUICKSAND_EMPTY ||
9655 element == EL_ACID_SPLASH_LEFT ||
9656 element == EL_ACID_SPLASH_RIGHT))
9658 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
9659 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
9660 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
9661 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
9662 Feld[x][y] = EL_AMOEBA_DROP;
9665 random = random * 129 + 1;
9671 if (game.explosions_delayed)
9674 game.explosions_delayed = FALSE;
9676 SCAN_PLAYFIELD(x, y)
9678 element = Feld[x][y];
9680 if (ExplodeField[x][y])
9681 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
9682 else if (element == EL_EXPLOSION)
9683 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
9685 ExplodeField[x][y] = EX_TYPE_NONE;
9688 game.explosions_delayed = TRUE;
9691 if (game.magic_wall_active)
9693 if (!(game.magic_wall_time_left % 4))
9695 int element = Feld[magic_wall_x][magic_wall_y];
9697 if (element == EL_BD_MAGIC_WALL_FULL ||
9698 element == EL_BD_MAGIC_WALL_ACTIVE ||
9699 element == EL_BD_MAGIC_WALL_EMPTYING)
9700 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
9702 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
9705 if (game.magic_wall_time_left > 0)
9707 game.magic_wall_time_left--;
9708 if (!game.magic_wall_time_left)
9710 SCAN_PLAYFIELD(x, y)
9712 element = Feld[x][y];
9714 if (element == EL_MAGIC_WALL_ACTIVE ||
9715 element == EL_MAGIC_WALL_FULL)
9717 Feld[x][y] = EL_MAGIC_WALL_DEAD;
9718 DrawLevelField(x, y);
9720 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
9721 element == EL_BD_MAGIC_WALL_FULL)
9723 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
9724 DrawLevelField(x, y);
9728 game.magic_wall_active = FALSE;
9733 if (game.light_time_left > 0)
9735 game.light_time_left--;
9737 if (game.light_time_left == 0)
9738 RedrawAllLightSwitchesAndInvisibleElements();
9741 if (game.timegate_time_left > 0)
9743 game.timegate_time_left--;
9745 if (game.timegate_time_left == 0)
9746 CloseAllOpenTimegates();
9749 if (game.lenses_time_left > 0)
9751 game.lenses_time_left--;
9753 if (game.lenses_time_left == 0)
9754 RedrawAllInvisibleElementsForLenses();
9757 if (game.magnify_time_left > 0)
9759 game.magnify_time_left--;
9761 if (game.magnify_time_left == 0)
9762 RedrawAllInvisibleElementsForMagnifier();
9765 for (i = 0; i < MAX_PLAYERS; i++)
9767 struct PlayerInfo *player = &stored_player[i];
9769 if (SHIELD_ON(player))
9771 if (player->shield_deadly_time_left)
9772 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
9773 else if (player->shield_normal_time_left)
9774 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
9781 PlayAllPlayersSound();
9783 if (options.debug) /* calculate frames per second */
9785 static unsigned long fps_counter = 0;
9786 static int fps_frames = 0;
9787 unsigned long fps_delay_ms = Counter() - fps_counter;
9791 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
9793 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
9796 fps_counter = Counter();
9799 redraw_mask |= REDRAW_FPS;
9802 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
9804 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
9806 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
9808 local_player->show_envelope = 0;
9811 /* use random number generator in every frame to make it less predictable */
9812 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
9816 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
9818 int min_x = x, min_y = y, max_x = x, max_y = y;
9821 for (i = 0; i < MAX_PLAYERS; i++)
9823 int jx = stored_player[i].jx, jy = stored_player[i].jy;
9825 if (!stored_player[i].active || &stored_player[i] == player)
9828 min_x = MIN(min_x, jx);
9829 min_y = MIN(min_y, jy);
9830 max_x = MAX(max_x, jx);
9831 max_y = MAX(max_y, jy);
9834 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
9837 static boolean AllPlayersInVisibleScreen()
9841 for (i = 0; i < MAX_PLAYERS; i++)
9843 int jx = stored_player[i].jx, jy = stored_player[i].jy;
9845 if (!stored_player[i].active)
9848 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
9855 void ScrollLevel(int dx, int dy)
9857 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
9860 BlitBitmap(drawto_field, drawto_field,
9861 FX + TILEX * (dx == -1) - softscroll_offset,
9862 FY + TILEY * (dy == -1) - softscroll_offset,
9863 SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
9864 SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
9865 FX + TILEX * (dx == 1) - softscroll_offset,
9866 FY + TILEY * (dy == 1) - softscroll_offset);
9870 x = (dx == 1 ? BX1 : BX2);
9871 for (y = BY1; y <= BY2; y++)
9872 DrawScreenField(x, y);
9877 y = (dy == 1 ? BY1 : BY2);
9878 for (x = BX1; x <= BX2; x++)
9879 DrawScreenField(x, y);
9882 redraw_mask |= REDRAW_FIELD;
9885 static boolean canFallDown(struct PlayerInfo *player)
9887 int jx = player->jx, jy = player->jy;
9889 return (IN_LEV_FIELD(jx, jy + 1) &&
9890 (IS_FREE(jx, jy + 1) ||
9891 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
9892 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
9893 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
9896 static boolean canPassField(int x, int y, int move_dir)
9898 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9899 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9900 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
9903 int element = Feld[x][y];
9905 return (IS_PASSABLE_FROM(element, opposite_dir) &&
9906 !CAN_MOVE(element) &&
9907 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
9908 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
9909 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
9912 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
9914 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9915 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9916 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
9920 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9921 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
9922 (IS_DIGGABLE(Feld[newx][newy]) ||
9923 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9924 canPassField(newx, newy, move_dir)));
9927 static void CheckGravityMovement(struct PlayerInfo *player)
9929 #if USE_PLAYER_GRAVITY
9930 if (player->gravity && !player->programmed_action)
9932 if (game.gravity && !player->programmed_action)
9935 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
9936 int move_dir_vertical = player->effective_action & MV_VERTICAL;
9937 boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
9938 int jx = player->jx, jy = player->jy;
9939 boolean player_is_moving_to_valid_field =
9940 (!player_is_snapping &&
9941 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
9942 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
9943 boolean player_can_fall_down = canFallDown(player);
9945 if (player_can_fall_down &&
9946 !player_is_moving_to_valid_field)
9947 player->programmed_action = MV_DOWN;
9951 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
9953 return CheckGravityMovement(player);
9955 #if USE_PLAYER_GRAVITY
9956 if (player->gravity && !player->programmed_action)
9958 if (game.gravity && !player->programmed_action)
9961 int jx = player->jx, jy = player->jy;
9962 boolean field_under_player_is_free =
9963 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
9964 boolean player_is_standing_on_valid_field =
9965 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
9966 (IS_WALKABLE(Feld[jx][jy]) &&
9967 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
9969 if (field_under_player_is_free && !player_is_standing_on_valid_field)
9970 player->programmed_action = MV_DOWN;
9976 -----------------------------------------------------------------------------
9977 dx, dy: direction (non-diagonal) to try to move the player to
9978 real_dx, real_dy: direction as read from input device (can be diagonal)
9981 boolean MovePlayerOneStep(struct PlayerInfo *player,
9982 int dx, int dy, int real_dx, int real_dy)
9984 int jx = player->jx, jy = player->jy;
9985 int new_jx = jx + dx, new_jy = jy + dy;
9986 #if !USE_FIXED_DONT_RUN_INTO
9990 boolean player_can_move = !player->cannot_move;
9992 if (!player->active || (!dx && !dy))
9993 return MP_NO_ACTION;
9995 player->MovDir = (dx < 0 ? MV_LEFT :
9998 dy > 0 ? MV_DOWN : MV_NONE);
10000 if (!IN_LEV_FIELD(new_jx, new_jy))
10001 return MP_NO_ACTION;
10003 if (!player_can_move)
10005 if (player->MovPos == 0)
10007 player->is_moving = FALSE;
10008 player->is_digging = FALSE;
10009 player->is_collecting = FALSE;
10010 player->is_snapping = FALSE;
10011 player->is_pushing = FALSE;
10016 if (!options.network && game.centered_player_nr == -1 &&
10017 !AllPlayersInSight(player, new_jx, new_jy))
10018 return MP_NO_ACTION;
10020 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
10021 return MP_NO_ACTION;
10024 #if !USE_FIXED_DONT_RUN_INTO
10025 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
10027 /* (moved to DigField()) */
10028 if (player_can_move && DONT_RUN_INTO(element))
10030 if (element == EL_ACID && dx == 0 && dy == 1)
10032 SplashAcid(new_jx, new_jy);
10033 Feld[jx][jy] = EL_PLAYER_1;
10034 InitMovingField(jx, jy, MV_DOWN);
10035 Store[jx][jy] = EL_ACID;
10036 ContinueMoving(jx, jy);
10037 BuryPlayer(player);
10040 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
10046 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
10047 if (can_move != MP_MOVING)
10050 /* check if DigField() has caused relocation of the player */
10051 if (player->jx != jx || player->jy != jy)
10052 return MP_NO_ACTION; /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
10054 StorePlayer[jx][jy] = 0;
10055 player->last_jx = jx;
10056 player->last_jy = jy;
10057 player->jx = new_jx;
10058 player->jy = new_jy;
10059 StorePlayer[new_jx][new_jy] = player->element_nr;
10061 if (player->move_delay_value_next != -1)
10063 player->move_delay_value = player->move_delay_value_next;
10064 player->move_delay_value_next = -1;
10068 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
10070 player->step_counter++;
10072 PlayerVisit[jx][jy] = FrameCounter;
10074 #if USE_UFAST_PLAYER_EXIT_BUGFIX
10075 player->is_moving = TRUE;
10079 /* should better be called in MovePlayer(), but this breaks some tapes */
10080 ScrollPlayer(player, SCROLL_INIT);
10086 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
10088 int jx = player->jx, jy = player->jy;
10089 int old_jx = jx, old_jy = jy;
10090 int moved = MP_NO_ACTION;
10092 if (!player->active)
10097 if (player->MovPos == 0)
10099 player->is_moving = FALSE;
10100 player->is_digging = FALSE;
10101 player->is_collecting = FALSE;
10102 player->is_snapping = FALSE;
10103 player->is_pushing = FALSE;
10109 if (player->move_delay > 0)
10112 player->move_delay = -1; /* set to "uninitialized" value */
10114 /* store if player is automatically moved to next field */
10115 player->is_auto_moving = (player->programmed_action != MV_NONE);
10117 /* remove the last programmed player action */
10118 player->programmed_action = 0;
10120 if (player->MovPos)
10122 /* should only happen if pre-1.2 tape recordings are played */
10123 /* this is only for backward compatibility */
10125 int original_move_delay_value = player->move_delay_value;
10128 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
10132 /* scroll remaining steps with finest movement resolution */
10133 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
10135 while (player->MovPos)
10137 ScrollPlayer(player, SCROLL_GO_ON);
10138 ScrollScreen(NULL, SCROLL_GO_ON);
10140 AdvanceFrameAndPlayerCounters(player->index_nr);
10146 player->move_delay_value = original_move_delay_value;
10149 player->is_active = FALSE;
10151 if (player->last_move_dir & MV_HORIZONTAL)
10153 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
10154 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
10158 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
10159 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
10162 #if USE_FIXED_BORDER_RUNNING_GFX
10163 if (!moved && !player->is_active)
10165 player->is_moving = FALSE;
10166 player->is_digging = FALSE;
10167 player->is_collecting = FALSE;
10168 player->is_snapping = FALSE;
10169 player->is_pushing = FALSE;
10177 if (moved & MP_MOVING && !ScreenMovPos &&
10178 (player->index_nr == game.centered_player_nr ||
10179 game.centered_player_nr == -1))
10181 if (moved & MP_MOVING && !ScreenMovPos &&
10182 (player == local_player || !options.network))
10185 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
10186 int offset = (setup.scroll_delay ? 3 : 0);
10188 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
10190 /* actual player has left the screen -- scroll in that direction */
10191 if (jx != old_jx) /* player has moved horizontally */
10192 scroll_x += (jx - old_jx);
10193 else /* player has moved vertically */
10194 scroll_y += (jy - old_jy);
10198 if (jx != old_jx) /* player has moved horizontally */
10200 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
10201 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
10202 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
10204 /* don't scroll over playfield boundaries */
10205 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
10206 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
10208 /* don't scroll more than one field at a time */
10209 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
10211 /* don't scroll against the player's moving direction */
10212 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
10213 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
10214 scroll_x = old_scroll_x;
10216 else /* player has moved vertically */
10218 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
10219 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
10220 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
10222 /* don't scroll over playfield boundaries */
10223 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
10224 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
10226 /* don't scroll more than one field at a time */
10227 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
10229 /* don't scroll against the player's moving direction */
10230 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
10231 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
10232 scroll_y = old_scroll_y;
10236 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
10239 if (!options.network && game.centered_player_nr == -1 &&
10240 !AllPlayersInVisibleScreen())
10242 scroll_x = old_scroll_x;
10243 scroll_y = old_scroll_y;
10247 if (!options.network && !AllPlayersInVisibleScreen())
10249 scroll_x = old_scroll_x;
10250 scroll_y = old_scroll_y;
10255 ScrollScreen(player, SCROLL_INIT);
10256 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
10261 player->StepFrame = 0;
10263 if (moved & MP_MOVING)
10265 if (old_jx != jx && old_jy == jy)
10266 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
10267 else if (old_jx == jx && old_jy != jy)
10268 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
10270 DrawLevelField(jx, jy); /* for "crumbled sand" */
10272 player->last_move_dir = player->MovDir;
10273 player->is_moving = TRUE;
10274 player->is_snapping = FALSE;
10275 player->is_switching = FALSE;
10276 player->is_dropping = FALSE;
10277 player->is_dropping_pressed = FALSE;
10278 player->drop_pressed_delay = 0;
10281 /* should better be called here than above, but this breaks some tapes */
10282 ScrollPlayer(player, SCROLL_INIT);
10287 CheckGravityMovementWhenNotMoving(player);
10289 player->is_moving = FALSE;
10291 /* at this point, the player is allowed to move, but cannot move right now
10292 (e.g. because of something blocking the way) -- ensure that the player
10293 is also allowed to move in the next frame (in old versions before 3.1.1,
10294 the player was forced to wait again for eight frames before next try) */
10296 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
10297 player->move_delay = 0; /* allow direct movement in the next frame */
10300 if (player->move_delay == -1) /* not yet initialized by DigField() */
10301 player->move_delay = player->move_delay_value;
10303 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10305 TestIfPlayerTouchesBadThing(jx, jy);
10306 TestIfPlayerTouchesCustomElement(jx, jy);
10309 if (!player->active)
10310 RemovePlayer(player);
10315 void ScrollPlayer(struct PlayerInfo *player, int mode)
10317 int jx = player->jx, jy = player->jy;
10318 int last_jx = player->last_jx, last_jy = player->last_jy;
10319 int move_stepsize = TILEX / player->move_delay_value;
10321 #if USE_NEW_PLAYER_SPEED
10322 if (!player->active)
10325 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
10328 if (!player->active || player->MovPos == 0)
10332 if (mode == SCROLL_INIT)
10334 player->actual_frame_counter = FrameCounter;
10335 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10337 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
10338 Feld[last_jx][last_jy] == EL_EMPTY)
10340 int last_field_block_delay = 0; /* start with no blocking at all */
10341 int block_delay_adjustment = player->block_delay_adjustment;
10343 /* if player blocks last field, add delay for exactly one move */
10344 if (player->block_last_field)
10346 last_field_block_delay += player->move_delay_value;
10348 /* when blocking enabled, prevent moving up despite gravity */
10349 #if USE_PLAYER_GRAVITY
10350 if (player->gravity && player->MovDir == MV_UP)
10351 block_delay_adjustment = -1;
10353 if (game.gravity && player->MovDir == MV_UP)
10354 block_delay_adjustment = -1;
10358 /* add block delay adjustment (also possible when not blocking) */
10359 last_field_block_delay += block_delay_adjustment;
10361 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
10362 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
10365 #if USE_NEW_PLAYER_SPEED
10366 if (player->MovPos != 0) /* player has not yet reached destination */
10372 else if (!FrameReached(&player->actual_frame_counter, 1))
10375 #if USE_NEW_PLAYER_SPEED
10376 if (player->MovPos != 0)
10378 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
10379 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10381 /* before DrawPlayer() to draw correct player graphic for this case */
10382 if (player->MovPos == 0)
10383 CheckGravityMovement(player);
10386 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
10387 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10389 /* before DrawPlayer() to draw correct player graphic for this case */
10390 if (player->MovPos == 0)
10391 CheckGravityMovement(player);
10394 if (player->MovPos == 0) /* player reached destination field */
10396 if (player->move_delay_reset_counter > 0)
10398 player->move_delay_reset_counter--;
10400 if (player->move_delay_reset_counter == 0)
10402 /* continue with normal speed after quickly moving through gate */
10403 HALVE_PLAYER_SPEED(player);
10405 /* be able to make the next move without delay */
10406 player->move_delay = 0;
10410 player->last_jx = jx;
10411 player->last_jy = jy;
10413 if (Feld[jx][jy] == EL_EXIT_OPEN ||
10414 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
10415 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
10417 DrawPlayer(player); /* needed here only to cleanup last field */
10418 RemovePlayer(player);
10420 if (local_player->friends_still_needed == 0 ||
10421 IS_SP_ELEMENT(Feld[jx][jy]))
10422 player->LevelSolved = player->GameOver = TRUE;
10425 /* this breaks one level: "machine", level 000 */
10427 int move_direction = player->MovDir;
10428 int enter_side = MV_DIR_OPPOSITE(move_direction);
10429 int leave_side = move_direction;
10430 int old_jx = last_jx;
10431 int old_jy = last_jy;
10432 int old_element = Feld[old_jx][old_jy];
10433 int new_element = Feld[jx][jy];
10435 if (IS_CUSTOM_ELEMENT(old_element))
10436 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
10438 player->index_bit, leave_side);
10440 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
10441 CE_PLAYER_LEAVES_X,
10442 player->index_bit, leave_side);
10444 if (IS_CUSTOM_ELEMENT(new_element))
10445 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
10446 player->index_bit, enter_side);
10448 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
10449 CE_PLAYER_ENTERS_X,
10450 player->index_bit, enter_side);
10452 CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
10453 CE_MOVE_OF_X, move_direction);
10456 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10458 TestIfPlayerTouchesBadThing(jx, jy);
10459 TestIfPlayerTouchesCustomElement(jx, jy);
10461 /* needed because pushed element has not yet reached its destination,
10462 so it would trigger a change event at its previous field location */
10463 if (!player->is_pushing)
10464 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
10466 if (!player->active)
10467 RemovePlayer(player);
10470 if (level.use_step_counter)
10480 if (TimeLeft <= 10 && setup.time_limit)
10481 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
10483 DrawGameValue_Time(TimeLeft);
10485 if (!TimeLeft && setup.time_limit)
10486 for (i = 0; i < MAX_PLAYERS; i++)
10487 KillPlayer(&stored_player[i]);
10489 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
10490 DrawGameValue_Time(TimePlayed);
10493 if (tape.single_step && tape.recording && !tape.pausing &&
10494 !player->programmed_action)
10495 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10499 void ScrollScreen(struct PlayerInfo *player, int mode)
10501 static unsigned long screen_frame_counter = 0;
10503 if (mode == SCROLL_INIT)
10505 /* set scrolling step size according to actual player's moving speed */
10506 ScrollStepSize = TILEX / player->move_delay_value;
10508 screen_frame_counter = FrameCounter;
10509 ScreenMovDir = player->MovDir;
10510 ScreenMovPos = player->MovPos;
10511 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
10514 else if (!FrameReached(&screen_frame_counter, 1))
10519 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
10520 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
10521 redraw_mask |= REDRAW_FIELD;
10524 ScreenMovDir = MV_NONE;
10527 void TestIfPlayerTouchesCustomElement(int x, int y)
10529 static int xy[4][2] =
10536 static int trigger_sides[4][2] =
10538 /* center side border side */
10539 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
10540 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
10541 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
10542 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
10544 static int touch_dir[4] =
10546 MV_LEFT | MV_RIGHT,
10551 int center_element = Feld[x][y]; /* should always be non-moving! */
10554 for (i = 0; i < NUM_DIRECTIONS; i++)
10556 int xx = x + xy[i][0];
10557 int yy = y + xy[i][1];
10558 int center_side = trigger_sides[i][0];
10559 int border_side = trigger_sides[i][1];
10560 int border_element;
10562 if (!IN_LEV_FIELD(xx, yy))
10565 if (IS_PLAYER(x, y))
10567 struct PlayerInfo *player = PLAYERINFO(x, y);
10569 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10570 border_element = Feld[xx][yy]; /* may be moving! */
10571 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
10572 border_element = Feld[xx][yy];
10573 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
10574 border_element = MovingOrBlocked2Element(xx, yy);
10576 continue; /* center and border element do not touch */
10578 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
10579 player->index_bit, border_side);
10580 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
10581 CE_PLAYER_TOUCHES_X,
10582 player->index_bit, border_side);
10584 else if (IS_PLAYER(xx, yy))
10586 struct PlayerInfo *player = PLAYERINFO(xx, yy);
10588 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10590 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
10591 continue; /* center and border element do not touch */
10594 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
10595 player->index_bit, center_side);
10596 CheckTriggeredElementChangeByPlayer(x, y, center_element,
10597 CE_PLAYER_TOUCHES_X,
10598 player->index_bit, center_side);
10604 #if USE_ELEMENT_TOUCHING_BUGFIX
10606 void TestIfElementTouchesCustomElement(int x, int y)
10608 static int xy[4][2] =
10615 static int trigger_sides[4][2] =
10617 /* center side border side */
10618 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
10619 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
10620 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
10621 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
10623 static int touch_dir[4] =
10625 MV_LEFT | MV_RIGHT,
10630 boolean change_center_element = FALSE;
10631 int center_element = Feld[x][y]; /* should always be non-moving! */
10632 int border_element_old[NUM_DIRECTIONS];
10635 for (i = 0; i < NUM_DIRECTIONS; i++)
10637 int xx = x + xy[i][0];
10638 int yy = y + xy[i][1];
10639 int border_element;
10641 border_element_old[i] = -1;
10643 if (!IN_LEV_FIELD(xx, yy))
10646 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10647 border_element = Feld[xx][yy]; /* may be moving! */
10648 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
10649 border_element = Feld[xx][yy];
10650 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
10651 border_element = MovingOrBlocked2Element(xx, yy);
10653 continue; /* center and border element do not touch */
10655 border_element_old[i] = border_element;
10658 for (i = 0; i < NUM_DIRECTIONS; i++)
10660 int xx = x + xy[i][0];
10661 int yy = y + xy[i][1];
10662 int center_side = trigger_sides[i][0];
10663 int border_element = border_element_old[i];
10665 if (border_element == -1)
10668 /* check for change of border element */
10669 CheckElementChangeBySide(xx, yy, border_element, center_element,
10670 CE_TOUCHING_X, center_side);
10673 for (i = 0; i < NUM_DIRECTIONS; i++)
10675 int border_side = trigger_sides[i][1];
10676 int border_element = border_element_old[i];
10678 if (border_element == -1)
10681 /* check for change of center element (but change it only once) */
10682 if (!change_center_element)
10683 change_center_element =
10684 CheckElementChangeBySide(x, y, center_element, border_element,
10685 CE_TOUCHING_X, border_side);
10691 void TestIfElementTouchesCustomElement_OLD(int x, int y)
10693 static int xy[4][2] =
10700 static int trigger_sides[4][2] =
10702 /* center side border side */
10703 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
10704 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
10705 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
10706 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
10708 static int touch_dir[4] =
10710 MV_LEFT | MV_RIGHT,
10715 boolean change_center_element = FALSE;
10716 int center_element = Feld[x][y]; /* should always be non-moving! */
10719 for (i = 0; i < NUM_DIRECTIONS; i++)
10721 int xx = x + xy[i][0];
10722 int yy = y + xy[i][1];
10723 int center_side = trigger_sides[i][0];
10724 int border_side = trigger_sides[i][1];
10725 int border_element;
10727 if (!IN_LEV_FIELD(xx, yy))
10730 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10731 border_element = Feld[xx][yy]; /* may be moving! */
10732 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
10733 border_element = Feld[xx][yy];
10734 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
10735 border_element = MovingOrBlocked2Element(xx, yy);
10737 continue; /* center and border element do not touch */
10739 /* check for change of center element (but change it only once) */
10740 if (!change_center_element)
10741 change_center_element =
10742 CheckElementChangeBySide(x, y, center_element, border_element,
10743 CE_TOUCHING_X, border_side);
10745 /* check for change of border element */
10746 CheckElementChangeBySide(xx, yy, border_element, center_element,
10747 CE_TOUCHING_X, center_side);
10753 void TestIfElementHitsCustomElement(int x, int y, int direction)
10755 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
10756 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
10757 int hitx = x + dx, hity = y + dy;
10758 int hitting_element = Feld[x][y];
10759 int touched_element;
10761 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
10764 touched_element = (IN_LEV_FIELD(hitx, hity) ?
10765 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
10767 if (IN_LEV_FIELD(hitx, hity))
10769 int opposite_direction = MV_DIR_OPPOSITE(direction);
10770 int hitting_side = direction;
10771 int touched_side = opposite_direction;
10772 boolean object_hit = (!IS_MOVING(hitx, hity) ||
10773 MovDir[hitx][hity] != direction ||
10774 ABS(MovPos[hitx][hity]) <= TILEY / 2);
10780 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10781 CE_HITTING_X, touched_side);
10783 CheckElementChangeBySide(hitx, hity, touched_element,
10784 hitting_element, CE_HIT_BY_X, hitting_side);
10786 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10787 CE_HIT_BY_SOMETHING, opposite_direction);
10791 /* "hitting something" is also true when hitting the playfield border */
10792 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10793 CE_HITTING_SOMETHING, direction);
10797 void TestIfElementSmashesCustomElement(int x, int y, int direction)
10799 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
10800 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
10801 int hitx = x + dx, hity = y + dy;
10802 int hitting_element = Feld[x][y];
10803 int touched_element;
10805 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
10806 !IS_FREE(hitx, hity) &&
10807 (!IS_MOVING(hitx, hity) ||
10808 MovDir[hitx][hity] != direction ||
10809 ABS(MovPos[hitx][hity]) <= TILEY / 2));
10812 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
10816 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
10820 touched_element = (IN_LEV_FIELD(hitx, hity) ?
10821 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
10823 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10824 EP_CAN_SMASH_EVERYTHING, direction);
10826 if (IN_LEV_FIELD(hitx, hity))
10828 int opposite_direction = MV_DIR_OPPOSITE(direction);
10829 int hitting_side = direction;
10830 int touched_side = opposite_direction;
10832 int touched_element = MovingOrBlocked2Element(hitx, hity);
10835 boolean object_hit = (!IS_MOVING(hitx, hity) ||
10836 MovDir[hitx][hity] != direction ||
10837 ABS(MovPos[hitx][hity]) <= TILEY / 2);
10846 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10847 CE_SMASHED_BY_SOMETHING, opposite_direction);
10849 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10850 CE_OTHER_IS_SMASHING, touched_side);
10852 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10853 CE_OTHER_GETS_SMASHED, hitting_side);
10859 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
10861 int i, kill_x = -1, kill_y = -1;
10863 int bad_element = -1;
10864 static int test_xy[4][2] =
10871 static int test_dir[4] =
10879 for (i = 0; i < NUM_DIRECTIONS; i++)
10881 int test_x, test_y, test_move_dir, test_element;
10883 test_x = good_x + test_xy[i][0];
10884 test_y = good_y + test_xy[i][1];
10886 if (!IN_LEV_FIELD(test_x, test_y))
10890 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
10892 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
10894 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
10895 2nd case: DONT_TOUCH style bad thing does not move away from good thing
10897 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
10898 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
10902 bad_element = test_element;
10908 if (kill_x != -1 || kill_y != -1)
10910 if (IS_PLAYER(good_x, good_y))
10912 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
10914 if (player->shield_deadly_time_left > 0 &&
10915 !IS_INDESTRUCTIBLE(bad_element))
10916 Bang(kill_x, kill_y);
10917 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
10918 KillPlayer(player);
10921 Bang(good_x, good_y);
10925 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
10927 int i, kill_x = -1, kill_y = -1;
10928 int bad_element = Feld[bad_x][bad_y];
10929 static int test_xy[4][2] =
10936 static int touch_dir[4] =
10938 MV_LEFT | MV_RIGHT,
10943 static int test_dir[4] =
10951 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
10954 for (i = 0; i < NUM_DIRECTIONS; i++)
10956 int test_x, test_y, test_move_dir, test_element;
10958 test_x = bad_x + test_xy[i][0];
10959 test_y = bad_y + test_xy[i][1];
10960 if (!IN_LEV_FIELD(test_x, test_y))
10964 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
10966 test_element = Feld[test_x][test_y];
10968 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
10969 2nd case: DONT_TOUCH style bad thing does not move away from good thing
10971 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
10972 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
10974 /* good thing is player or penguin that does not move away */
10975 if (IS_PLAYER(test_x, test_y))
10977 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
10979 if (bad_element == EL_ROBOT && player->is_moving)
10980 continue; /* robot does not kill player if he is moving */
10982 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10984 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
10985 continue; /* center and border element do not touch */
10992 else if (test_element == EL_PENGUIN)
11001 if (kill_x != -1 || kill_y != -1)
11003 if (IS_PLAYER(kill_x, kill_y))
11005 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
11007 if (player->shield_deadly_time_left > 0 &&
11008 !IS_INDESTRUCTIBLE(bad_element))
11009 Bang(bad_x, bad_y);
11010 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
11011 KillPlayer(player);
11014 Bang(kill_x, kill_y);
11018 void TestIfPlayerTouchesBadThing(int x, int y)
11020 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
11023 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
11025 TestIfGoodThingHitsBadThing(x, y, move_dir);
11028 void TestIfBadThingTouchesPlayer(int x, int y)
11030 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
11033 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
11035 TestIfBadThingHitsGoodThing(x, y, move_dir);
11038 void TestIfFriendTouchesBadThing(int x, int y)
11040 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
11043 void TestIfBadThingTouchesFriend(int x, int y)
11045 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
11048 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
11050 int i, kill_x = bad_x, kill_y = bad_y;
11051 static int xy[4][2] =
11059 for (i = 0; i < NUM_DIRECTIONS; i++)
11063 x = bad_x + xy[i][0];
11064 y = bad_y + xy[i][1];
11065 if (!IN_LEV_FIELD(x, y))
11068 element = Feld[x][y];
11069 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
11070 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
11078 if (kill_x != bad_x || kill_y != bad_y)
11079 Bang(bad_x, bad_y);
11082 void KillPlayer(struct PlayerInfo *player)
11084 int jx = player->jx, jy = player->jy;
11086 if (!player->active)
11089 /* remove accessible field at the player's position */
11090 Feld[jx][jy] = EL_EMPTY;
11092 /* deactivate shield (else Bang()/Explode() would not work right) */
11093 player->shield_normal_time_left = 0;
11094 player->shield_deadly_time_left = 0;
11097 BuryPlayer(player);
11100 static void KillPlayerUnlessEnemyProtected(int x, int y)
11102 if (!PLAYER_ENEMY_PROTECTED(x, y))
11103 KillPlayer(PLAYERINFO(x, y));
11106 static void KillPlayerUnlessExplosionProtected(int x, int y)
11108 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
11109 KillPlayer(PLAYERINFO(x, y));
11112 void BuryPlayer(struct PlayerInfo *player)
11114 int jx = player->jx, jy = player->jy;
11116 if (!player->active)
11119 PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
11120 PlayLevelSound(jx, jy, SND_GAME_LOSING);
11122 player->GameOver = TRUE;
11123 RemovePlayer(player);
11126 void RemovePlayer(struct PlayerInfo *player)
11128 int jx = player->jx, jy = player->jy;
11129 int i, found = FALSE;
11131 player->present = FALSE;
11132 player->active = FALSE;
11134 if (!ExplodeField[jx][jy])
11135 StorePlayer[jx][jy] = 0;
11137 if (player->is_moving)
11138 DrawLevelField(player->last_jx, player->last_jy);
11140 for (i = 0; i < MAX_PLAYERS; i++)
11141 if (stored_player[i].active)
11145 AllPlayersGone = TRUE;
11151 #if USE_NEW_SNAP_DELAY
11152 static void setFieldForSnapping(int x, int y, int element, int direction)
11154 struct ElementInfo *ei = &element_info[element];
11155 int direction_bit = MV_DIR_TO_BIT(direction);
11156 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
11157 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
11158 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
11160 Feld[x][y] = EL_ELEMENT_SNAPPING;
11161 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
11163 ResetGfxAnimation(x, y);
11165 GfxElement[x][y] = element;
11166 GfxAction[x][y] = action;
11167 GfxDir[x][y] = direction;
11168 GfxFrame[x][y] = -1;
11173 =============================================================================
11174 checkDiagonalPushing()
11175 -----------------------------------------------------------------------------
11176 check if diagonal input device direction results in pushing of object
11177 (by checking if the alternative direction is walkable, diggable, ...)
11178 =============================================================================
11181 static boolean checkDiagonalPushing(struct PlayerInfo *player,
11182 int x, int y, int real_dx, int real_dy)
11184 int jx, jy, dx, dy, xx, yy;
11186 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
11189 /* diagonal direction: check alternative direction */
11194 xx = jx + (dx == 0 ? real_dx : 0);
11195 yy = jy + (dy == 0 ? real_dy : 0);
11197 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
11201 =============================================================================
11203 -----------------------------------------------------------------------------
11204 x, y: field next to player (non-diagonal) to try to dig to
11205 real_dx, real_dy: direction as read from input device (can be diagonal)
11206 =============================================================================
11209 int DigField(struct PlayerInfo *player,
11210 int oldx, int oldy, int x, int y,
11211 int real_dx, int real_dy, int mode)
11213 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
11214 boolean player_was_pushing = player->is_pushing;
11215 boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
11216 boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
11217 int jx = oldx, jy = oldy;
11218 int dx = x - jx, dy = y - jy;
11219 int nextx = x + dx, nexty = y + dy;
11220 int move_direction = (dx == -1 ? MV_LEFT :
11221 dx == +1 ? MV_RIGHT :
11223 dy == +1 ? MV_DOWN : MV_NONE);
11224 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
11225 int dig_side = MV_DIR_OPPOSITE(move_direction);
11226 int old_element = Feld[jx][jy];
11227 #if USE_FIXED_DONT_RUN_INTO
11228 int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
11234 if (is_player) /* function can also be called by EL_PENGUIN */
11236 if (player->MovPos == 0)
11238 player->is_digging = FALSE;
11239 player->is_collecting = FALSE;
11242 if (player->MovPos == 0) /* last pushing move finished */
11243 player->is_pushing = FALSE;
11245 if (mode == DF_NO_PUSH) /* player just stopped pushing */
11247 player->is_switching = FALSE;
11248 player->push_delay = -1;
11250 return MP_NO_ACTION;
11254 #if !USE_FIXED_DONT_RUN_INTO
11255 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
11256 return MP_NO_ACTION;
11259 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
11260 old_element = Back[jx][jy];
11262 /* in case of element dropped at player position, check background */
11263 else if (Back[jx][jy] != EL_EMPTY &&
11264 game.engine_version >= VERSION_IDENT(2,2,0,0))
11265 old_element = Back[jx][jy];
11267 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
11268 return MP_NO_ACTION; /* field has no opening in this direction */
11270 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
11271 return MP_NO_ACTION; /* field has no opening in this direction */
11273 #if USE_FIXED_DONT_RUN_INTO
11274 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
11278 Feld[jx][jy] = player->artwork_element;
11279 InitMovingField(jx, jy, MV_DOWN);
11280 Store[jx][jy] = EL_ACID;
11281 ContinueMoving(jx, jy);
11282 BuryPlayer(player);
11284 return MP_DONT_RUN_INTO;
11288 #if USE_FIXED_DONT_RUN_INTO
11289 if (player_can_move && DONT_RUN_INTO(element))
11291 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
11293 return MP_DONT_RUN_INTO;
11297 #if USE_FIXED_DONT_RUN_INTO
11298 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
11299 return MP_NO_ACTION;
11302 #if !USE_FIXED_DONT_RUN_INTO
11303 element = Feld[x][y];
11306 collect_count = element_info[element].collect_count_initial;
11308 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
11309 return MP_NO_ACTION;
11311 if (game.engine_version < VERSION_IDENT(2,2,0,0))
11312 player_can_move = player_can_move_or_snap;
11314 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
11315 game.engine_version >= VERSION_IDENT(2,2,0,0))
11317 CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
11318 player->index_bit, dig_side);
11319 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
11320 player->index_bit, dig_side);
11322 if (Feld[x][y] != element) /* field changed by snapping */
11325 return MP_NO_ACTION;
11328 #if USE_PLAYER_GRAVITY
11329 if (player->gravity && is_player && !player->is_auto_moving &&
11330 canFallDown(player) && move_direction != MV_DOWN &&
11331 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
11332 return MP_NO_ACTION; /* player cannot walk here due to gravity */
11334 if (game.gravity && is_player && !player->is_auto_moving &&
11335 canFallDown(player) && move_direction != MV_DOWN &&
11336 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
11337 return MP_NO_ACTION; /* player cannot walk here due to gravity */
11340 if (player_can_move &&
11341 IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
11343 int sound_element = SND_ELEMENT(element);
11344 int sound_action = ACTION_WALKING;
11346 if (IS_RND_GATE(element))
11348 if (!player->key[RND_GATE_NR(element)])
11349 return MP_NO_ACTION;
11351 else if (IS_RND_GATE_GRAY(element))
11353 if (!player->key[RND_GATE_GRAY_NR(element)])
11354 return MP_NO_ACTION;
11356 else if (IS_RND_GATE_GRAY_ACTIVE(element))
11358 if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
11359 return MP_NO_ACTION;
11361 else if (element == EL_EXIT_OPEN ||
11362 element == EL_SP_EXIT_OPEN ||
11363 element == EL_SP_EXIT_OPENING)
11365 sound_action = ACTION_PASSING; /* player is passing exit */
11367 else if (element == EL_EMPTY)
11369 sound_action = ACTION_MOVING; /* nothing to walk on */
11372 /* play sound from background or player, whatever is available */
11373 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
11374 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
11376 PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
11378 else if (player_can_move &&
11379 IS_PASSABLE(element) && canPassField(x, y, move_direction))
11381 if (!ACCESS_FROM(element, opposite_direction))
11382 return MP_NO_ACTION; /* field not accessible from this direction */
11384 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
11385 return MP_NO_ACTION;
11387 if (IS_EM_GATE(element))
11389 if (!player->key[EM_GATE_NR(element)])
11390 return MP_NO_ACTION;
11392 else if (IS_EM_GATE_GRAY(element))
11394 if (!player->key[EM_GATE_GRAY_NR(element)])
11395 return MP_NO_ACTION;
11397 else if (IS_EM_GATE_GRAY_ACTIVE(element))
11399 if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
11400 return MP_NO_ACTION;
11402 else if (IS_EMC_GATE(element))
11404 if (!player->key[EMC_GATE_NR(element)])
11405 return MP_NO_ACTION;
11407 else if (IS_EMC_GATE_GRAY(element))
11409 if (!player->key[EMC_GATE_GRAY_NR(element)])
11410 return MP_NO_ACTION;
11412 else if (IS_EMC_GATE_GRAY_ACTIVE(element))
11414 if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
11415 return MP_NO_ACTION;
11417 else if (IS_SP_PORT(element))
11419 if (element == EL_SP_GRAVITY_PORT_LEFT ||
11420 element == EL_SP_GRAVITY_PORT_RIGHT ||
11421 element == EL_SP_GRAVITY_PORT_UP ||
11422 element == EL_SP_GRAVITY_PORT_DOWN)
11423 #if USE_PLAYER_GRAVITY
11424 player->gravity = !player->gravity;
11426 game.gravity = !game.gravity;
11428 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
11429 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
11430 element == EL_SP_GRAVITY_ON_PORT_UP ||
11431 element == EL_SP_GRAVITY_ON_PORT_DOWN)
11432 #if USE_PLAYER_GRAVITY
11433 player->gravity = TRUE;
11435 game.gravity = TRUE;
11437 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
11438 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
11439 element == EL_SP_GRAVITY_OFF_PORT_UP ||
11440 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
11441 #if USE_PLAYER_GRAVITY
11442 player->gravity = FALSE;
11444 game.gravity = FALSE;
11448 /* automatically move to the next field with double speed */
11449 player->programmed_action = move_direction;
11451 if (player->move_delay_reset_counter == 0)
11453 player->move_delay_reset_counter = 2; /* two double speed steps */
11455 DOUBLE_PLAYER_SPEED(player);
11458 PlayLevelSoundAction(x, y, ACTION_PASSING);
11460 else if (player_can_move_or_snap && IS_DIGGABLE(element))
11464 if (mode != DF_SNAP)
11466 GfxElement[x][y] = GFX_ELEMENT(element);
11467 player->is_digging = TRUE;
11470 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
11472 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
11473 player->index_bit, dig_side);
11475 if (mode == DF_SNAP)
11477 #if USE_NEW_SNAP_DELAY
11478 if (level.block_snap_field)
11479 setFieldForSnapping(x, y, element, move_direction);
11481 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11483 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11486 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
11487 player->index_bit, dig_side);
11490 else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
11494 if (is_player && mode != DF_SNAP)
11496 GfxElement[x][y] = element;
11497 player->is_collecting = TRUE;
11500 if (element == EL_SPEED_PILL)
11502 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
11504 else if (element == EL_EXTRA_TIME && level.time > 0)
11506 TimeLeft += level.extra_time;
11507 DrawGameValue_Time(TimeLeft);
11509 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
11511 player->shield_normal_time_left += level.shield_normal_time;
11512 if (element == EL_SHIELD_DEADLY)
11513 player->shield_deadly_time_left += level.shield_deadly_time;
11515 else if (element == EL_DYNAMITE ||
11516 element == EL_EM_DYNAMITE ||
11517 element == EL_SP_DISK_RED)
11519 if (player->inventory_size < MAX_INVENTORY_SIZE)
11520 player->inventory_element[player->inventory_size++] = element;
11522 DrawGameDoorValues();
11524 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
11526 player->dynabomb_count++;
11527 player->dynabombs_left++;
11529 else if (element == EL_DYNABOMB_INCREASE_SIZE)
11531 player->dynabomb_size++;
11533 else if (element == EL_DYNABOMB_INCREASE_POWER)
11535 player->dynabomb_xl = TRUE;
11537 else if (IS_KEY(element))
11539 player->key[KEY_NR(element)] = TRUE;
11541 DrawGameDoorValues();
11543 else if (IS_ENVELOPE(element))
11545 player->show_envelope = element;
11547 else if (element == EL_EMC_LENSES)
11549 game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
11551 RedrawAllInvisibleElementsForLenses();
11553 else if (element == EL_EMC_MAGNIFIER)
11555 game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
11557 RedrawAllInvisibleElementsForMagnifier();
11559 else if (IS_DROPPABLE(element) ||
11560 IS_THROWABLE(element)) /* can be collected and dropped */
11564 if (collect_count == 0)
11565 player->inventory_infinite_element = element;
11567 for (i = 0; i < collect_count; i++)
11568 if (player->inventory_size < MAX_INVENTORY_SIZE)
11569 player->inventory_element[player->inventory_size++] = element;
11571 DrawGameDoorValues();
11573 else if (collect_count > 0)
11575 local_player->gems_still_needed -= collect_count;
11576 if (local_player->gems_still_needed < 0)
11577 local_player->gems_still_needed = 0;
11579 DrawGameValue_Emeralds(local_player->gems_still_needed);
11582 RaiseScoreElement(element);
11583 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
11586 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
11587 player->index_bit, dig_side);
11589 if (mode == DF_SNAP)
11591 #if USE_NEW_SNAP_DELAY
11592 if (level.block_snap_field)
11593 setFieldForSnapping(x, y, element, move_direction);
11595 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11597 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11600 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
11601 player->index_bit, dig_side);
11604 else if (player_can_move_or_snap && IS_PUSHABLE(element))
11606 if (mode == DF_SNAP && element != EL_BD_ROCK)
11607 return MP_NO_ACTION;
11609 if (CAN_FALL(element) && dy)
11610 return MP_NO_ACTION;
11612 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
11613 !(element == EL_SPRING && level.use_spring_bug))
11614 return MP_NO_ACTION;
11616 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
11617 ((move_direction & MV_VERTICAL &&
11618 ((element_info[element].move_pattern & MV_LEFT &&
11619 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
11620 (element_info[element].move_pattern & MV_RIGHT &&
11621 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
11622 (move_direction & MV_HORIZONTAL &&
11623 ((element_info[element].move_pattern & MV_UP &&
11624 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
11625 (element_info[element].move_pattern & MV_DOWN &&
11626 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
11627 return MP_NO_ACTION;
11629 /* do not push elements already moving away faster than player */
11630 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
11631 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
11632 return MP_NO_ACTION;
11634 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
11636 if (player->push_delay_value == -1 || !player_was_pushing)
11637 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11639 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11641 if (player->push_delay_value == -1)
11642 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11644 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
11646 if (!player->is_pushing)
11647 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11650 player->is_pushing = TRUE;
11651 player->is_active = TRUE;
11653 if (!(IN_LEV_FIELD(nextx, nexty) &&
11654 (IS_FREE(nextx, nexty) ||
11655 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
11656 IS_SB_ELEMENT(element)))))
11657 return MP_NO_ACTION;
11659 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
11660 return MP_NO_ACTION;
11662 if (player->push_delay == -1) /* new pushing; restart delay */
11663 player->push_delay = 0;
11665 if (player->push_delay < player->push_delay_value &&
11666 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
11667 element != EL_SPRING && element != EL_BALLOON)
11669 /* make sure that there is no move delay before next try to push */
11670 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11671 player->move_delay = 0;
11673 return MP_NO_ACTION;
11676 if (IS_SB_ELEMENT(element))
11678 if (element == EL_SOKOBAN_FIELD_FULL)
11680 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
11681 local_player->sokobanfields_still_needed++;
11684 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
11686 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
11687 local_player->sokobanfields_still_needed--;
11690 Feld[x][y] = EL_SOKOBAN_OBJECT;
11692 if (Back[x][y] == Back[nextx][nexty])
11693 PlayLevelSoundAction(x, y, ACTION_PUSHING);
11694 else if (Back[x][y] != 0)
11695 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
11698 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
11701 if (local_player->sokobanfields_still_needed == 0 &&
11702 game.emulation == EMU_SOKOBAN)
11704 player->LevelSolved = player->GameOver = TRUE;
11705 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
11709 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
11711 InitMovingField(x, y, move_direction);
11712 GfxAction[x][y] = ACTION_PUSHING;
11714 if (mode == DF_SNAP)
11715 ContinueMoving(x, y);
11717 MovPos[x][y] = (dx != 0 ? dx : dy);
11719 Pushed[x][y] = TRUE;
11720 Pushed[nextx][nexty] = TRUE;
11722 if (game.engine_version < VERSION_IDENT(2,2,0,7))
11723 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11725 player->push_delay_value = -1; /* get new value later */
11727 /* check for element change _after_ element has been pushed */
11728 if (game.use_change_when_pushing_bug)
11730 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
11731 player->index_bit, dig_side);
11732 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
11733 player->index_bit, dig_side);
11736 else if (IS_SWITCHABLE(element))
11738 if (PLAYER_SWITCHING(player, x, y))
11740 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
11741 player->index_bit, dig_side);
11746 player->is_switching = TRUE;
11747 player->switch_x = x;
11748 player->switch_y = y;
11750 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
11752 if (element == EL_ROBOT_WHEEL)
11754 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
11758 DrawLevelField(x, y);
11760 else if (element == EL_SP_TERMINAL)
11764 SCAN_PLAYFIELD(xx, yy)
11766 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
11768 else if (Feld[xx][yy] == EL_SP_TERMINAL)
11769 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
11772 else if (IS_BELT_SWITCH(element))
11774 ToggleBeltSwitch(x, y);
11776 else if (element == EL_SWITCHGATE_SWITCH_UP ||
11777 element == EL_SWITCHGATE_SWITCH_DOWN)
11779 ToggleSwitchgateSwitch(x, y);
11781 else if (element == EL_LIGHT_SWITCH ||
11782 element == EL_LIGHT_SWITCH_ACTIVE)
11784 ToggleLightSwitch(x, y);
11786 else if (element == EL_TIMEGATE_SWITCH)
11788 ActivateTimegateSwitch(x, y);
11790 else if (element == EL_BALLOON_SWITCH_LEFT ||
11791 element == EL_BALLOON_SWITCH_RIGHT ||
11792 element == EL_BALLOON_SWITCH_UP ||
11793 element == EL_BALLOON_SWITCH_DOWN ||
11794 element == EL_BALLOON_SWITCH_NONE ||
11795 element == EL_BALLOON_SWITCH_ANY)
11797 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
11798 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
11799 element == EL_BALLOON_SWITCH_UP ? MV_UP :
11800 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
11801 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
11804 else if (element == EL_LAMP)
11806 Feld[x][y] = EL_LAMP_ACTIVE;
11807 local_player->lights_still_needed--;
11809 ResetGfxAnimation(x, y);
11810 DrawLevelField(x, y);
11812 else if (element == EL_TIME_ORB_FULL)
11814 Feld[x][y] = EL_TIME_ORB_EMPTY;
11816 if (level.time > 0 || level.use_time_orb_bug)
11818 TimeLeft += level.time_orb_time;
11819 DrawGameValue_Time(TimeLeft);
11822 ResetGfxAnimation(x, y);
11823 DrawLevelField(x, y);
11825 else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
11826 element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
11830 game.ball_state = !game.ball_state;
11832 SCAN_PLAYFIELD(xx, yy)
11834 int e = Feld[xx][yy];
11836 if (game.ball_state)
11838 if (e == EL_EMC_MAGIC_BALL)
11839 CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
11840 else if (e == EL_EMC_MAGIC_BALL_SWITCH)
11841 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
11845 if (e == EL_EMC_MAGIC_BALL_ACTIVE)
11846 CreateField(xx, yy, EL_EMC_MAGIC_BALL);
11847 else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
11848 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
11853 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
11854 player->index_bit, dig_side);
11856 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
11857 player->index_bit, dig_side);
11859 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
11860 player->index_bit, dig_side);
11866 if (!PLAYER_SWITCHING(player, x, y))
11868 player->is_switching = TRUE;
11869 player->switch_x = x;
11870 player->switch_y = y;
11872 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
11873 player->index_bit, dig_side);
11874 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
11875 player->index_bit, dig_side);
11877 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
11878 player->index_bit, dig_side);
11879 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
11880 player->index_bit, dig_side);
11883 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
11884 player->index_bit, dig_side);
11885 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
11886 player->index_bit, dig_side);
11888 return MP_NO_ACTION;
11891 player->push_delay = -1;
11893 if (is_player) /* function can also be called by EL_PENGUIN */
11895 if (Feld[x][y] != element) /* really digged/collected something */
11897 player->is_collecting = !player->is_digging;
11898 player->is_active = TRUE;
11905 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
11907 int jx = player->jx, jy = player->jy;
11908 int x = jx + dx, y = jy + dy;
11909 int snap_direction = (dx == -1 ? MV_LEFT :
11910 dx == +1 ? MV_RIGHT :
11912 dy == +1 ? MV_DOWN : MV_NONE);
11913 boolean can_continue_snapping = (level.continuous_snapping &&
11914 WasJustFalling[x][y] < CHECK_DELAY_FALLING);
11916 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
11919 if (!player->active || !IN_LEV_FIELD(x, y))
11927 if (player->MovPos == 0)
11928 player->is_pushing = FALSE;
11930 player->is_snapping = FALSE;
11932 if (player->MovPos == 0)
11934 player->is_moving = FALSE;
11935 player->is_digging = FALSE;
11936 player->is_collecting = FALSE;
11942 #if USE_NEW_CONTINUOUS_SNAPPING
11943 /* prevent snapping with already pressed snap key when not allowed */
11944 if (player->is_snapping && !can_continue_snapping)
11947 if (player->is_snapping)
11951 player->MovDir = snap_direction;
11953 if (player->MovPos == 0)
11955 player->is_moving = FALSE;
11956 player->is_digging = FALSE;
11957 player->is_collecting = FALSE;
11960 player->is_dropping = FALSE;
11961 player->is_dropping_pressed = FALSE;
11962 player->drop_pressed_delay = 0;
11964 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
11967 player->is_snapping = TRUE;
11968 player->is_active = TRUE;
11970 if (player->MovPos == 0)
11972 player->is_moving = FALSE;
11973 player->is_digging = FALSE;
11974 player->is_collecting = FALSE;
11977 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
11978 DrawLevelField(player->last_jx, player->last_jy);
11980 DrawLevelField(x, y);
11985 boolean DropElement(struct PlayerInfo *player)
11987 int old_element, new_element;
11988 int dropx = player->jx, dropy = player->jy;
11989 int drop_direction = player->MovDir;
11990 int drop_side = drop_direction;
11991 int drop_element = (player->inventory_size > 0 ?
11992 player->inventory_element[player->inventory_size - 1] :
11993 player->inventory_infinite_element != EL_UNDEFINED ?
11994 player->inventory_infinite_element :
11995 player->dynabombs_left > 0 ?
11996 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
11999 player->is_dropping_pressed = TRUE;
12001 /* do not drop an element on top of another element; when holding drop key
12002 pressed without moving, dropped element must move away before the next
12003 element can be dropped (this is especially important if the next element
12004 is dynamite, which can be placed on background for historical reasons) */
12005 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
12008 if (IS_THROWABLE(drop_element))
12010 dropx += GET_DX_FROM_DIR(drop_direction);
12011 dropy += GET_DY_FROM_DIR(drop_direction);
12013 if (!IN_LEV_FIELD(dropx, dropy))
12017 old_element = Feld[dropx][dropy]; /* old element at dropping position */
12018 new_element = drop_element; /* default: no change when dropping */
12020 /* check if player is active, not moving and ready to drop */
12021 if (!player->active || player->MovPos || player->drop_delay > 0)
12024 /* check if player has anything that can be dropped */
12025 if (new_element == EL_UNDEFINED)
12028 /* check if drop key was pressed long enough for EM style dynamite */
12029 if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
12032 /* check if anything can be dropped at the current position */
12033 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
12036 /* collected custom elements can only be dropped on empty fields */
12037 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
12040 if (old_element != EL_EMPTY)
12041 Back[dropx][dropy] = old_element; /* store old element on this field */
12043 ResetGfxAnimation(dropx, dropy);
12044 ResetRandomAnimationValue(dropx, dropy);
12046 if (player->inventory_size > 0 ||
12047 player->inventory_infinite_element != EL_UNDEFINED)
12049 if (player->inventory_size > 0)
12051 player->inventory_size--;
12053 DrawGameDoorValues();
12055 if (new_element == EL_DYNAMITE)
12056 new_element = EL_DYNAMITE_ACTIVE;
12057 else if (new_element == EL_EM_DYNAMITE)
12058 new_element = EL_EM_DYNAMITE_ACTIVE;
12059 else if (new_element == EL_SP_DISK_RED)
12060 new_element = EL_SP_DISK_RED_ACTIVE;
12063 Feld[dropx][dropy] = new_element;
12065 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
12066 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
12067 el2img(Feld[dropx][dropy]), 0);
12069 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
12071 /* needed if previous element just changed to "empty" in the last frame */
12072 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
12074 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
12075 player->index_bit, drop_side);
12076 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
12078 player->index_bit, drop_side);
12080 TestIfElementTouchesCustomElement(dropx, dropy);
12082 else /* player is dropping a dyna bomb */
12084 player->dynabombs_left--;
12086 Feld[dropx][dropy] = new_element;
12088 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
12089 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
12090 el2img(Feld[dropx][dropy]), 0);
12092 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
12095 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
12096 InitField_WithBug1(dropx, dropy, FALSE);
12098 new_element = Feld[dropx][dropy]; /* element might have changed */
12100 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
12101 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
12103 int move_direction, nextx, nexty;
12105 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
12106 MovDir[dropx][dropy] = drop_direction;
12108 move_direction = MovDir[dropx][dropy];
12109 nextx = dropx + GET_DX_FROM_DIR(move_direction);
12110 nexty = dropy + GET_DY_FROM_DIR(move_direction);
12112 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
12113 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
12116 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
12117 player->is_dropping = TRUE;
12119 player->drop_pressed_delay = 0;
12120 player->is_dropping_pressed = FALSE;
12122 player->drop_x = dropx;
12123 player->drop_y = dropy;
12128 /* ------------------------------------------------------------------------- */
12129 /* game sound playing functions */
12130 /* ------------------------------------------------------------------------- */
12132 static int *loop_sound_frame = NULL;
12133 static int *loop_sound_volume = NULL;
12135 void InitPlayLevelSound()
12137 int num_sounds = getSoundListSize();
12139 checked_free(loop_sound_frame);
12140 checked_free(loop_sound_volume);
12142 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
12143 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
12146 static void PlayLevelSound(int x, int y, int nr)
12148 int sx = SCREENX(x), sy = SCREENY(y);
12149 int volume, stereo_position;
12150 int max_distance = 8;
12151 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
12153 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
12154 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
12157 if (!IN_LEV_FIELD(x, y) ||
12158 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
12159 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
12162 volume = SOUND_MAX_VOLUME;
12164 if (!IN_SCR_FIELD(sx, sy))
12166 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
12167 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
12169 volume -= volume * (dx > dy ? dx : dy) / max_distance;
12172 stereo_position = (SOUND_MAX_LEFT +
12173 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
12174 (SCR_FIELDX + 2 * max_distance));
12176 if (IS_LOOP_SOUND(nr))
12178 /* This assures that quieter loop sounds do not overwrite louder ones,
12179 while restarting sound volume comparison with each new game frame. */
12181 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
12184 loop_sound_volume[nr] = volume;
12185 loop_sound_frame[nr] = FrameCounter;
12188 PlaySoundExt(nr, volume, stereo_position, type);
12191 static void PlayLevelSoundNearest(int x, int y, int sound_action)
12193 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
12194 x > LEVELX(BX2) ? LEVELX(BX2) : x,
12195 y < LEVELY(BY1) ? LEVELY(BY1) :
12196 y > LEVELY(BY2) ? LEVELY(BY2) : y,
12200 static void PlayLevelSoundAction(int x, int y, int action)
12202 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
12205 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
12207 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
12209 if (sound_effect != SND_UNDEFINED)
12210 PlayLevelSound(x, y, sound_effect);
12213 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
12216 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
12218 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12219 PlayLevelSound(x, y, sound_effect);
12222 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
12224 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
12226 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12227 PlayLevelSound(x, y, sound_effect);
12230 static void StopLevelSoundActionIfLoop(int x, int y, int action)
12232 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
12234 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12235 StopSound(sound_effect);
12238 static void PlayLevelMusic()
12240 if (levelset.music[level_nr] != MUS_UNDEFINED)
12241 PlayMusic(levelset.music[level_nr]); /* from config file */
12243 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
12246 void PlayLevelSound_EM(int x, int y, int element_em, int sample)
12248 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
12253 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
12257 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12261 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12265 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12269 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
12273 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12277 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12280 case SAMPLE_android_clone:
12281 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12284 case SAMPLE_android_move:
12285 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12288 case SAMPLE_spring:
12289 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12293 PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
12297 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
12300 case SAMPLE_eater_eat:
12301 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
12305 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12308 case SAMPLE_collect:
12309 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
12312 case SAMPLE_diamond:
12313 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12316 case SAMPLE_squash:
12317 /* !!! CHECK THIS !!! */
12319 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
12321 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
12325 case SAMPLE_wonderfall:
12326 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
12330 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12334 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12338 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
12342 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
12346 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12350 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
12353 case SAMPLE_wonder:
12354 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12358 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
12361 case SAMPLE_exit_open:
12362 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
12365 case SAMPLE_exit_leave:
12366 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
12369 case SAMPLE_dynamite:
12370 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12374 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12378 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
12382 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12386 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
12390 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
12394 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
12398 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
12403 void RaiseScore(int value)
12405 local_player->score += value;
12407 DrawGameValue_Score(local_player->score);
12410 void RaiseScoreElement(int element)
12415 case EL_BD_DIAMOND:
12416 case EL_EMERALD_YELLOW:
12417 case EL_EMERALD_RED:
12418 case EL_EMERALD_PURPLE:
12419 case EL_SP_INFOTRON:
12420 RaiseScore(level.score[SC_EMERALD]);
12423 RaiseScore(level.score[SC_DIAMOND]);
12426 RaiseScore(level.score[SC_CRYSTAL]);
12429 RaiseScore(level.score[SC_PEARL]);
12432 case EL_BD_BUTTERFLY:
12433 case EL_SP_ELECTRON:
12434 RaiseScore(level.score[SC_BUG]);
12437 case EL_BD_FIREFLY:
12438 case EL_SP_SNIKSNAK:
12439 RaiseScore(level.score[SC_SPACESHIP]);
12442 case EL_DARK_YAMYAM:
12443 RaiseScore(level.score[SC_YAMYAM]);
12446 RaiseScore(level.score[SC_ROBOT]);
12449 RaiseScore(level.score[SC_PACMAN]);
12452 RaiseScore(level.score[SC_NUT]);
12455 case EL_EM_DYNAMITE:
12456 case EL_SP_DISK_RED:
12457 case EL_DYNABOMB_INCREASE_NUMBER:
12458 case EL_DYNABOMB_INCREASE_SIZE:
12459 case EL_DYNABOMB_INCREASE_POWER:
12460 RaiseScore(level.score[SC_DYNAMITE]);
12462 case EL_SHIELD_NORMAL:
12463 case EL_SHIELD_DEADLY:
12464 RaiseScore(level.score[SC_SHIELD]);
12466 case EL_EXTRA_TIME:
12467 RaiseScore(level.extra_time_score);
12481 RaiseScore(level.score[SC_KEY]);
12484 RaiseScore(element_info[element].collect_score);
12489 void RequestQuitGame(boolean ask_if_really_quit)
12491 if (AllPlayersGone ||
12492 !ask_if_really_quit ||
12493 level_editor_test_game ||
12494 Request("Do you really want to quit the game ?",
12495 REQ_ASK | REQ_STAY_CLOSED))
12497 #if defined(NETWORK_AVALIABLE)
12498 if (options.network)
12499 SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
12503 if (!ask_if_really_quit || level_editor_test_game)
12505 game_status = GAME_MODE_MAIN;
12511 FadeOut(REDRAW_FIELD);
12513 game_status = GAME_MODE_MAIN;
12515 DrawAndFadeInMainMenu(REDRAW_FIELD);
12521 if (tape.playing && tape.deactivate_display)
12522 TapeDeactivateDisplayOff(TRUE);
12524 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
12526 if (tape.playing && tape.deactivate_display)
12527 TapeDeactivateDisplayOn();
12532 /* ---------- new game button stuff ---------------------------------------- */
12534 /* graphic position values for game buttons */
12535 #define GAME_BUTTON_XSIZE 30
12536 #define GAME_BUTTON_YSIZE 30
12537 #define GAME_BUTTON_XPOS 5
12538 #define GAME_BUTTON_YPOS 215
12539 #define SOUND_BUTTON_XPOS 5
12540 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
12542 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
12543 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
12544 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
12545 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
12546 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
12547 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
12554 } gamebutton_info[NUM_GAME_BUTTONS] =
12557 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
12562 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
12563 GAME_CTRL_ID_PAUSE,
12567 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
12572 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
12573 SOUND_CTRL_ID_MUSIC,
12574 "background music on/off"
12577 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
12578 SOUND_CTRL_ID_LOOPS,
12579 "sound loops on/off"
12582 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
12583 SOUND_CTRL_ID_SIMPLE,
12584 "normal sounds on/off"
12588 void CreateGameButtons()
12592 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12594 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
12595 struct GadgetInfo *gi;
12598 unsigned long event_mask;
12599 int gd_xoffset, gd_yoffset;
12600 int gd_x1, gd_x2, gd_y1, gd_y2;
12603 gd_xoffset = gamebutton_info[i].x;
12604 gd_yoffset = gamebutton_info[i].y;
12605 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
12606 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
12608 if (id == GAME_CTRL_ID_STOP ||
12609 id == GAME_CTRL_ID_PAUSE ||
12610 id == GAME_CTRL_ID_PLAY)
12612 button_type = GD_TYPE_NORMAL_BUTTON;
12614 event_mask = GD_EVENT_RELEASED;
12615 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12616 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12620 button_type = GD_TYPE_CHECK_BUTTON;
12622 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
12623 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
12624 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
12625 event_mask = GD_EVENT_PRESSED;
12626 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
12627 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12630 gi = CreateGadget(GDI_CUSTOM_ID, id,
12631 GDI_INFO_TEXT, gamebutton_info[i].infotext,
12632 GDI_X, DX + gd_xoffset,
12633 GDI_Y, DY + gd_yoffset,
12634 GDI_WIDTH, GAME_BUTTON_XSIZE,
12635 GDI_HEIGHT, GAME_BUTTON_YSIZE,
12636 GDI_TYPE, button_type,
12637 GDI_STATE, GD_BUTTON_UNPRESSED,
12638 GDI_CHECKED, checked,
12639 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
12640 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
12641 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
12642 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
12643 GDI_EVENT_MASK, event_mask,
12644 GDI_CALLBACK_ACTION, HandleGameButtons,
12648 Error(ERR_EXIT, "cannot create gadget");
12650 game_gadget[id] = gi;
12654 void FreeGameButtons()
12658 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12659 FreeGadget(game_gadget[i]);
12662 static void MapGameButtons()
12666 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12667 MapGadget(game_gadget[i]);
12670 void UnmapGameButtons()
12674 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12675 UnmapGadget(game_gadget[i]);
12678 static void HandleGameButtons(struct GadgetInfo *gi)
12680 int id = gi->custom_id;
12682 if (game_status != GAME_MODE_PLAYING)
12687 case GAME_CTRL_ID_STOP:
12691 RequestQuitGame(TRUE);
12694 case GAME_CTRL_ID_PAUSE:
12695 if (options.network)
12697 #if defined(NETWORK_AVALIABLE)
12699 SendToServer_ContinuePlaying();
12701 SendToServer_PausePlaying();
12705 TapeTogglePause(TAPE_TOGGLE_MANUAL);
12708 case GAME_CTRL_ID_PLAY:
12711 #if defined(NETWORK_AVALIABLE)
12712 if (options.network)
12713 SendToServer_ContinuePlaying();
12717 tape.pausing = FALSE;
12718 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
12723 case SOUND_CTRL_ID_MUSIC:
12724 if (setup.sound_music)
12726 setup.sound_music = FALSE;
12729 else if (audio.music_available)
12731 setup.sound = setup.sound_music = TRUE;
12733 SetAudioMode(setup.sound);
12739 case SOUND_CTRL_ID_LOOPS:
12740 if (setup.sound_loops)
12741 setup.sound_loops = FALSE;
12742 else if (audio.loops_available)
12744 setup.sound = setup.sound_loops = TRUE;
12745 SetAudioMode(setup.sound);
12749 case SOUND_CTRL_ID_SIMPLE:
12750 if (setup.sound_simple)
12751 setup.sound_simple = FALSE;
12752 else if (audio.sound_available)
12754 setup.sound = setup.sound_simple = TRUE;
12755 SetAudioMode(setup.sound);