1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back! *
3 *----------------------------------------------------------*
4 * (c) 1995-2006 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)
56 #define USE_GFX_RESET_ONLY_WHEN_MOVING (USE_NEW_STUFF * 1)
57 #define USE_GFX_RESET_PLAYER_ARTWORK (USE_NEW_STUFF * 1)
59 #define USE_FIX_KILLED_BY_NON_WALKABLE (USE_NEW_STUFF * 1)
60 #define USE_FIX_IMPACT_COLLISION (USE_NEW_STUFF * 1)
68 /* for MovePlayer() */
69 #define MP_NO_ACTION 0
72 #define MP_DONT_RUN_INTO (MP_MOVING | MP_ACTION)
74 /* for ScrollPlayer() */
76 #define SCROLL_GO_ON 1
78 /* for Bang()/Explode() */
79 #define EX_PHASE_START 0
80 #define EX_TYPE_NONE 0
81 #define EX_TYPE_NORMAL (1 << 0)
82 #define EX_TYPE_CENTER (1 << 1)
83 #define EX_TYPE_BORDER (1 << 2)
84 #define EX_TYPE_CROSS (1 << 3)
85 #define EX_TYPE_DYNA (1 << 4)
86 #define EX_TYPE_SINGLE_TILE (EX_TYPE_CENTER | EX_TYPE_BORDER)
88 #define PANEL_DEACTIVATED(p) ((p).x < 0 || (p).y < 0)
90 /* special positions in the game control window (relative to control window) */
91 #define XX_LEVEL1 (game.panel.level.x)
92 #define XX_LEVEL2 (game.panel.level.x - 1)
93 #define YY_LEVEL (game.panel.level.y)
94 #define XX_EMERALDS (game.panel.gems.x)
95 #define YY_EMERALDS (game.panel.gems.y)
96 #define XX_DYNAMITE (game.panel.inventory.x)
97 #define YY_DYNAMITE (game.panel.inventory.y)
98 #define XX_KEYS (game.panel.keys.x)
99 #define YY_KEYS (game.panel.keys.y)
100 #define XX_SCORE (game.panel.score.x)
101 #define YY_SCORE (game.panel.score.y)
102 #define XX_TIME1 (game.panel.time.x)
103 #define XX_TIME2 (game.panel.time.x + 1)
104 #define YY_TIME (game.panel.time.y)
106 /* special positions in the game control window (relative to main window) */
107 #define DX_LEVEL1 (DX + XX_LEVEL1)
108 #define DX_LEVEL2 (DX + XX_LEVEL2)
109 #define DY_LEVEL (DY + YY_LEVEL)
110 #define DX_EMERALDS (DX + XX_EMERALDS)
111 #define DY_EMERALDS (DY + YY_EMERALDS)
112 #define DX_DYNAMITE (DX + XX_DYNAMITE)
113 #define DY_DYNAMITE (DY + YY_DYNAMITE)
114 #define DX_KEYS (DX + XX_KEYS)
115 #define DY_KEYS (DY + YY_KEYS)
116 #define DX_SCORE (DX + XX_SCORE)
117 #define DY_SCORE (DY + YY_SCORE)
118 #define DX_TIME1 (DX + XX_TIME1)
119 #define DX_TIME2 (DX + XX_TIME2)
120 #define DY_TIME (DY + YY_TIME)
122 /* values for delayed check of falling and moving elements and for collision */
123 #define CHECK_DELAY_MOVING 3
124 #define CHECK_DELAY_FALLING CHECK_DELAY_MOVING
125 #define CHECK_DELAY_COLLISION 2
126 #define CHECK_DELAY_IMPACT CHECK_DELAY_COLLISION
128 /* values for initial player move delay (initial delay counter value) */
129 #define INITIAL_MOVE_DELAY_OFF -1
130 #define INITIAL_MOVE_DELAY_ON 0
132 /* values for player movement speed (which is in fact a delay value) */
133 #define MOVE_DELAY_MIN_SPEED 32
134 #define MOVE_DELAY_NORMAL_SPEED 8
135 #define MOVE_DELAY_HIGH_SPEED 4
136 #define MOVE_DELAY_MAX_SPEED 1
138 #define DOUBLE_MOVE_DELAY(x) (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
139 #define HALVE_MOVE_DELAY(x) (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
141 #define DOUBLE_PLAYER_SPEED(p) (HALVE_MOVE_DELAY( (p)->move_delay_value))
142 #define HALVE_PLAYER_SPEED(p) (DOUBLE_MOVE_DELAY((p)->move_delay_value))
144 /* values for other actions */
145 #define MOVE_STEPSIZE_NORMAL (TILEX / MOVE_DELAY_NORMAL_SPEED)
146 #define MOVE_STEPSIZE_MIN (1)
147 #define MOVE_STEPSIZE_MAX (TILEX)
149 #define GET_DX_FROM_DIR(d) ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
150 #define GET_DY_FROM_DIR(d) ((d) == MV_UP ? -1 : (d) == MV_DOWN ? 1 : 0)
152 #define INIT_GFX_RANDOM() (GetSimpleRandom(1000000))
154 #define GET_NEW_PUSH_DELAY(e) ( (element_info[e].push_delay_fixed) + \
155 RND(element_info[e].push_delay_random))
156 #define GET_NEW_DROP_DELAY(e) ( (element_info[e].drop_delay_fixed) + \
157 RND(element_info[e].drop_delay_random))
158 #define GET_NEW_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
159 RND(element_info[e].move_delay_random))
160 #define GET_MAX_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
161 (element_info[e].move_delay_random))
162 #define GET_NEW_CE_VALUE(e) ( (element_info[e].ce_value_fixed_initial) +\
163 RND(element_info[e].ce_value_random_initial))
164 #define GET_CE_SCORE(e) ( (element_info[e].collect_score))
165 #define GET_CHANGE_DELAY(c) ( ((c)->delay_fixed * (c)->delay_frames) + \
166 RND((c)->delay_random * (c)->delay_frames))
167 #define GET_CE_DELAY_VALUE(c) ( ((c)->delay_fixed) + \
168 RND((c)->delay_random))
171 #define GET_VALID_RUNTIME_ELEMENT(e) \
172 ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
174 #define RESOLVED_REFERENCE_ELEMENT(be, e) \
175 ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START : \
176 (be) + (e) - EL_SELF > EL_CUSTOM_END ? EL_CUSTOM_END : \
177 (be) + (e) - EL_SELF)
179 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs) \
180 ((e) == EL_TRIGGER_PLAYER ? (ch)->actual_trigger_player : \
181 (e) == EL_TRIGGER_ELEMENT ? (ch)->actual_trigger_element : \
182 (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value : \
183 (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score : \
184 (e) == EL_CURRENT_CE_VALUE ? (cv) : \
185 (e) == EL_CURRENT_CE_SCORE ? (cs) : \
186 (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ? \
187 RESOLVED_REFERENCE_ELEMENT(be, e) : \
190 #define CAN_GROW_INTO(e) \
191 ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
193 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition) \
194 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
197 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition) \
198 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
199 (CAN_MOVE_INTO_ACID(e) && \
200 Feld[x][y] == EL_ACID) || \
203 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition) \
204 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
205 (CAN_MOVE_INTO_ACID(e) && \
206 Feld[x][y] == EL_ACID) || \
209 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition) \
210 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
212 (CAN_MOVE_INTO_ACID(e) && \
213 Feld[x][y] == EL_ACID) || \
214 (DONT_COLLIDE_WITH(e) && \
216 !PLAYER_ENEMY_PROTECTED(x, y))))
218 #define ELEMENT_CAN_ENTER_FIELD(e, x, y) \
219 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
221 #define SATELLITE_CAN_ENTER_FIELD(x, y) \
222 ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
224 #define ANDROID_CAN_ENTER_FIELD(e, x, y) \
225 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
227 #define ANDROID_CAN_CLONE_FIELD(x, y) \
228 (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
229 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
231 #define ENEMY_CAN_ENTER_FIELD(e, x, y) \
232 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
234 #define YAMYAM_CAN_ENTER_FIELD(e, x, y) \
235 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
237 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y) \
238 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
240 #define PACMAN_CAN_ENTER_FIELD(e, x, y) \
241 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
243 #define PIG_CAN_ENTER_FIELD(e, x, y) \
244 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
246 #define PENGUIN_CAN_ENTER_FIELD(e, x, y) \
247 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
248 Feld[x][y] == EL_EM_EXIT_OPEN || \
249 Feld[x][y] == EL_STEEL_EXIT_OPEN || \
250 Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
251 IS_FOOD_PENGUIN(Feld[x][y])))
252 #define DRAGON_CAN_ENTER_FIELD(e, x, y) \
253 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
255 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition) \
256 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
258 #define SPRING_CAN_ENTER_FIELD(e, x, y) \
259 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
261 #define SPRING_CAN_BUMP_FROM_FIELD(x, y) \
262 (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER || \
263 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
265 #define MOVE_ENTER_EL(e) (element_info[e].move_enter_element)
267 #define CE_ENTER_FIELD_COND(e, x, y) \
268 (!IS_PLAYER(x, y) && \
269 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
271 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y) \
272 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
274 #define IN_LEV_FIELD_AND_IS_FREE(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
275 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
277 #define ACCESS_FROM(e, d) (element_info[e].access_direction &(d))
278 #define IS_WALKABLE_FROM(e, d) (IS_WALKABLE(e) && ACCESS_FROM(e, d))
279 #define IS_PASSABLE_FROM(e, d) (IS_PASSABLE(e) && ACCESS_FROM(e, d))
280 #define IS_ACCESSIBLE_FROM(e, d) (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
282 /* game button identifiers */
283 #define GAME_CTRL_ID_STOP 0
284 #define GAME_CTRL_ID_PAUSE 1
285 #define GAME_CTRL_ID_PLAY 2
286 #define SOUND_CTRL_ID_MUSIC 3
287 #define SOUND_CTRL_ID_LOOPS 4
288 #define SOUND_CTRL_ID_SIMPLE 5
290 #define NUM_GAME_BUTTONS 6
293 /* forward declaration for internal use */
295 static void CreateField(int, int, int);
297 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
298 static void AdvanceFrameAndPlayerCounters(int);
300 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
301 static boolean MovePlayer(struct PlayerInfo *, int, int);
302 static void ScrollPlayer(struct PlayerInfo *, int);
303 static void ScrollScreen(struct PlayerInfo *, int);
305 int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
307 static void InitBeltMovement(void);
308 static void CloseAllOpenTimegates(void);
309 static void CheckGravityMovement(struct PlayerInfo *);
310 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
311 static void KillPlayerUnlessEnemyProtected(int, int);
312 static void KillPlayerUnlessExplosionProtected(int, int);
314 static void TestIfPlayerTouchesCustomElement(int, int);
315 static void TestIfElementTouchesCustomElement(int, int);
316 static void TestIfElementHitsCustomElement(int, int, int);
318 static void TestIfElementSmashesCustomElement(int, int, int);
321 static void HandleElementChange(int, int, int);
322 static void ExecuteCustomElementAction(int, int, int, int);
323 static boolean ChangeElement(int, int, int, int);
325 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
326 #define CheckTriggeredElementChange(x, y, e, ev) \
327 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
328 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s) \
329 CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
330 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s) \
331 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
332 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p) \
333 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
335 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
336 #define CheckElementChange(x, y, e, te, ev) \
337 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
338 #define CheckElementChangeByPlayer(x, y, e, ev, p, s) \
339 CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
340 #define CheckElementChangeBySide(x, y, e, te, ev, s) \
341 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
343 static void PlayLevelSound(int, int, int);
344 static void PlayLevelSoundNearest(int, int, int);
345 static void PlayLevelSoundAction(int, int, int);
346 static void PlayLevelSoundElementAction(int, int, int, int);
347 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
348 static void PlayLevelSoundActionIfLoop(int, int, int);
349 static void StopLevelSoundActionIfLoop(int, int, int);
350 static void PlayLevelMusic();
352 static void MapGameButtons();
353 static void HandleGameButtons(struct GadgetInfo *);
355 int AmoebeNachbarNr(int, int);
356 void AmoebeUmwandeln(int, int);
357 void ContinueMoving(int, int);
359 void InitMovDir(int, int);
360 void InitAmoebaNr(int, int);
361 int NewHiScore(void);
363 void TestIfGoodThingHitsBadThing(int, int, int);
364 void TestIfBadThingHitsGoodThing(int, int, int);
365 void TestIfPlayerTouchesBadThing(int, int);
366 void TestIfPlayerRunsIntoBadThing(int, int, int);
367 void TestIfBadThingTouchesPlayer(int, int);
368 void TestIfBadThingRunsIntoPlayer(int, int, int);
369 void TestIfFriendTouchesBadThing(int, int);
370 void TestIfBadThingTouchesFriend(int, int);
371 void TestIfBadThingTouchesOtherBadThing(int, int);
373 void KillPlayer(struct PlayerInfo *);
374 void BuryPlayer(struct PlayerInfo *);
375 void RemovePlayer(struct PlayerInfo *);
377 boolean SnapField(struct PlayerInfo *, int, int);
378 boolean DropElement(struct PlayerInfo *);
380 static int getInvisibleActiveFromInvisibleElement(int);
381 static int getInvisibleFromInvisibleActiveElement(int);
383 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
385 /* for detection of endless loops, caused by custom element programming */
386 /* (using maximal playfield width x 10 is just a rough approximation) */
387 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH (MAX_PLAYFIELD_WIDTH * 10)
389 #define RECURSION_LOOP_DETECTION_START(e, rc) \
391 if (recursion_loop_detected) \
394 if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH) \
396 recursion_loop_detected = TRUE; \
397 recursion_loop_element = (e); \
400 recursion_loop_depth++; \
403 #define RECURSION_LOOP_DETECTION_END() \
405 recursion_loop_depth--; \
408 static int recursion_loop_depth;
409 static boolean recursion_loop_detected;
410 static boolean recursion_loop_element;
413 /* ------------------------------------------------------------------------- */
414 /* definition of elements that automatically change to other elements after */
415 /* a specified time, eventually calling a function when changing */
416 /* ------------------------------------------------------------------------- */
418 /* forward declaration for changer functions */
419 static void InitBuggyBase(int, int);
420 static void WarnBuggyBase(int, int);
422 static void InitTrap(int, int);
423 static void ActivateTrap(int, int);
424 static void ChangeActiveTrap(int, int);
426 static void InitRobotWheel(int, int);
427 static void RunRobotWheel(int, int);
428 static void StopRobotWheel(int, int);
430 static void InitTimegateWheel(int, int);
431 static void RunTimegateWheel(int, int);
433 static void InitMagicBallDelay(int, int);
434 static void ActivateMagicBall(int, int);
436 struct ChangingElementInfo
441 void (*pre_change_function)(int x, int y);
442 void (*change_function)(int x, int y);
443 void (*post_change_function)(int x, int y);
446 static struct ChangingElementInfo change_delay_list[] =
481 EL_STEEL_EXIT_OPENING,
489 EL_STEEL_EXIT_CLOSING,
490 EL_STEEL_EXIT_CLOSED,
517 EL_EM_STEEL_EXIT_OPENING,
518 EL_EM_STEEL_EXIT_OPEN,
525 EL_EM_STEEL_EXIT_CLOSING,
529 EL_EM_STEEL_EXIT_CLOSED,
553 EL_SWITCHGATE_OPENING,
561 EL_SWITCHGATE_CLOSING,
562 EL_SWITCHGATE_CLOSED,
594 EL_ACID_SPLASH_RIGHT,
603 EL_SP_BUGGY_BASE_ACTIVATING,
610 EL_SP_BUGGY_BASE_ACTIVATING,
611 EL_SP_BUGGY_BASE_ACTIVE,
618 EL_SP_BUGGY_BASE_ACTIVE,
642 EL_ROBOT_WHEEL_ACTIVE,
650 EL_TIMEGATE_SWITCH_ACTIVE,
658 EL_DC_TIMEGATE_SWITCH_ACTIVE,
659 EL_DC_TIMEGATE_SWITCH,
666 EL_EMC_MAGIC_BALL_ACTIVE,
667 EL_EMC_MAGIC_BALL_ACTIVE,
674 EL_EMC_SPRING_BUMPER_ACTIVE,
675 EL_EMC_SPRING_BUMPER,
682 EL_DIAGONAL_SHRINKING,
711 int push_delay_fixed, push_delay_random;
716 { EL_BALLOON, 0, 0 },
718 { EL_SOKOBAN_OBJECT, 2, 0 },
719 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
720 { EL_SATELLITE, 2, 0 },
721 { EL_SP_DISK_YELLOW, 2, 0 },
723 { EL_UNDEFINED, 0, 0 },
731 move_stepsize_list[] =
733 { EL_AMOEBA_DROP, 2 },
734 { EL_AMOEBA_DROPPING, 2 },
735 { EL_QUICKSAND_FILLING, 1 },
736 { EL_QUICKSAND_EMPTYING, 1 },
737 { EL_MAGIC_WALL_FILLING, 2 },
738 { EL_BD_MAGIC_WALL_FILLING, 2 },
739 { EL_MAGIC_WALL_EMPTYING, 2 },
740 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
750 collect_count_list[] =
753 { EL_BD_DIAMOND, 1 },
754 { EL_EMERALD_YELLOW, 1 },
755 { EL_EMERALD_RED, 1 },
756 { EL_EMERALD_PURPLE, 1 },
758 { EL_SP_INFOTRON, 1 },
770 access_direction_list[] =
772 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
773 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
774 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
775 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
776 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
777 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
778 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
779 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
780 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
781 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
782 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
784 { EL_SP_PORT_LEFT, MV_RIGHT },
785 { EL_SP_PORT_RIGHT, MV_LEFT },
786 { EL_SP_PORT_UP, MV_DOWN },
787 { EL_SP_PORT_DOWN, MV_UP },
788 { EL_SP_PORT_HORIZONTAL, MV_LEFT | MV_RIGHT },
789 { EL_SP_PORT_VERTICAL, MV_UP | MV_DOWN },
790 { EL_SP_PORT_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
791 { EL_SP_GRAVITY_PORT_LEFT, MV_RIGHT },
792 { EL_SP_GRAVITY_PORT_RIGHT, MV_LEFT },
793 { EL_SP_GRAVITY_PORT_UP, MV_DOWN },
794 { EL_SP_GRAVITY_PORT_DOWN, MV_UP },
795 { EL_SP_GRAVITY_ON_PORT_LEFT, MV_RIGHT },
796 { EL_SP_GRAVITY_ON_PORT_RIGHT, MV_LEFT },
797 { EL_SP_GRAVITY_ON_PORT_UP, MV_DOWN },
798 { EL_SP_GRAVITY_ON_PORT_DOWN, MV_UP },
799 { EL_SP_GRAVITY_OFF_PORT_LEFT, MV_RIGHT },
800 { EL_SP_GRAVITY_OFF_PORT_RIGHT, MV_LEFT },
801 { EL_SP_GRAVITY_OFF_PORT_UP, MV_DOWN },
802 { EL_SP_GRAVITY_OFF_PORT_DOWN, MV_UP },
804 { EL_UNDEFINED, MV_NONE }
807 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
809 #define IS_AUTO_CHANGING(e) (element_info[e].has_change_event[CE_DELAY])
810 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
811 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
812 IS_JUST_CHANGING(x, y))
814 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
816 /* static variables for playfield scan mode (scanning forward or backward) */
817 static int playfield_scan_start_x = 0;
818 static int playfield_scan_start_y = 0;
819 static int playfield_scan_delta_x = 1;
820 static int playfield_scan_delta_y = 1;
822 #define SCAN_PLAYFIELD(x, y) for ((y) = playfield_scan_start_y; \
823 (y) >= 0 && (y) <= lev_fieldy - 1; \
824 (y) += playfield_scan_delta_y) \
825 for ((x) = playfield_scan_start_x; \
826 (x) >= 0 && (x) <= lev_fieldx - 1; \
827 (x) += playfield_scan_delta_x) \
830 void DEBUG_SetMaximumDynamite()
834 for (i = 0; i < MAX_INVENTORY_SIZE; i++)
835 if (local_player->inventory_size < MAX_INVENTORY_SIZE)
836 local_player->inventory_element[local_player->inventory_size++] =
841 static void InitPlayfieldScanModeVars()
843 if (game.use_reverse_scan_direction)
845 playfield_scan_start_x = lev_fieldx - 1;
846 playfield_scan_start_y = lev_fieldy - 1;
848 playfield_scan_delta_x = -1;
849 playfield_scan_delta_y = -1;
853 playfield_scan_start_x = 0;
854 playfield_scan_start_y = 0;
856 playfield_scan_delta_x = 1;
857 playfield_scan_delta_y = 1;
861 static void InitPlayfieldScanMode(int mode)
863 game.use_reverse_scan_direction =
864 (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
866 InitPlayfieldScanModeVars();
869 static int get_move_delay_from_stepsize(int move_stepsize)
872 MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
874 /* make sure that stepsize value is always a power of 2 */
875 move_stepsize = (1 << log_2(move_stepsize));
877 return TILEX / move_stepsize;
880 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
883 int player_nr = player->index_nr;
884 int move_delay = get_move_delay_from_stepsize(move_stepsize);
885 boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
887 /* do no immediately change move delay -- the player might just be moving */
888 player->move_delay_value_next = move_delay;
890 /* information if player can move must be set separately */
891 player->cannot_move = cannot_move;
895 player->move_delay = game.initial_move_delay[player_nr];
896 player->move_delay_value = game.initial_move_delay_value[player_nr];
898 player->move_delay_value_next = -1;
900 player->move_delay_reset_counter = 0;
904 void GetPlayerConfig()
906 if (!audio.sound_available)
907 setup.sound_simple = FALSE;
909 if (!audio.loops_available)
910 setup.sound_loops = FALSE;
912 if (!audio.music_available)
913 setup.sound_music = FALSE;
915 if (!video.fullscreen_available)
916 setup.fullscreen = FALSE;
918 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
920 SetAudioMode(setup.sound);
924 static int getBeltNrFromBeltElement(int element)
926 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
927 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
928 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
931 static int getBeltNrFromBeltActiveElement(int element)
933 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
934 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
935 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
938 static int getBeltNrFromBeltSwitchElement(int element)
940 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
941 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
942 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
945 static int getBeltDirNrFromBeltSwitchElement(int element)
947 static int belt_base_element[4] =
949 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
950 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
951 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
952 EL_CONVEYOR_BELT_4_SWITCH_LEFT
955 int belt_nr = getBeltNrFromBeltSwitchElement(element);
956 int belt_dir_nr = element - belt_base_element[belt_nr];
958 return (belt_dir_nr % 3);
961 static int getBeltDirFromBeltSwitchElement(int element)
963 static int belt_move_dir[3] =
970 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
972 return belt_move_dir[belt_dir_nr];
975 static int get_element_from_group_element(int element)
977 if (IS_GROUP_ELEMENT(element))
979 struct ElementGroupInfo *group = element_info[element].group;
980 int last_anim_random_frame = gfx.anim_random_frame;
983 if (group->choice_mode == ANIM_RANDOM)
984 gfx.anim_random_frame = RND(group->num_elements_resolved);
986 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
987 group->choice_mode, 0,
990 if (group->choice_mode == ANIM_RANDOM)
991 gfx.anim_random_frame = last_anim_random_frame;
995 element = group->element_resolved[element_pos];
1001 static void InitPlayerField(int x, int y, int element, boolean init_game)
1003 if (element == EL_SP_MURPHY)
1007 if (stored_player[0].present)
1009 Feld[x][y] = EL_SP_MURPHY_CLONE;
1015 stored_player[0].use_murphy = TRUE;
1017 if (!level.use_artwork_element[0])
1018 stored_player[0].artwork_element = EL_SP_MURPHY;
1021 Feld[x][y] = EL_PLAYER_1;
1027 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1028 int jx = player->jx, jy = player->jy;
1030 player->present = TRUE;
1032 player->block_last_field = (element == EL_SP_MURPHY ?
1033 level.sp_block_last_field :
1034 level.block_last_field);
1036 /* ---------- initialize player's last field block delay --------------- */
1038 /* always start with reliable default value (no adjustment needed) */
1039 player->block_delay_adjustment = 0;
1041 /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1042 if (player->block_last_field && element == EL_SP_MURPHY)
1043 player->block_delay_adjustment = 1;
1045 /* special case 2: in game engines before 3.1.1, blocking was different */
1046 if (game.use_block_last_field_bug)
1047 player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1049 if (!options.network || player->connected)
1051 player->active = TRUE;
1053 /* remove potentially duplicate players */
1054 if (StorePlayer[jx][jy] == Feld[x][y])
1055 StorePlayer[jx][jy] = 0;
1057 StorePlayer[x][y] = Feld[x][y];
1061 printf("Player %d activated.\n", player->element_nr);
1062 printf("[Local player is %d and currently %s.]\n",
1063 local_player->element_nr,
1064 local_player->active ? "active" : "not active");
1068 Feld[x][y] = EL_EMPTY;
1070 player->jx = player->last_jx = x;
1071 player->jy = player->last_jy = y;
1075 static void InitField(int x, int y, boolean init_game)
1077 int element = Feld[x][y];
1086 InitPlayerField(x, y, element, init_game);
1089 case EL_SOKOBAN_FIELD_PLAYER:
1090 element = Feld[x][y] = EL_PLAYER_1;
1091 InitField(x, y, init_game);
1093 element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1094 InitField(x, y, init_game);
1097 case EL_SOKOBAN_FIELD_EMPTY:
1098 local_player->sokobanfields_still_needed++;
1102 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1103 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1104 else if (x > 0 && Feld[x-1][y] == EL_ACID)
1105 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1106 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1107 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1108 else if (y > 0 && Feld[x][y-1] == EL_ACID)
1109 Feld[x][y] = EL_ACID_POOL_BOTTOM;
1110 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1111 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1120 case EL_SPACESHIP_RIGHT:
1121 case EL_SPACESHIP_UP:
1122 case EL_SPACESHIP_LEFT:
1123 case EL_SPACESHIP_DOWN:
1124 case EL_BD_BUTTERFLY:
1125 case EL_BD_BUTTERFLY_RIGHT:
1126 case EL_BD_BUTTERFLY_UP:
1127 case EL_BD_BUTTERFLY_LEFT:
1128 case EL_BD_BUTTERFLY_DOWN:
1130 case EL_BD_FIREFLY_RIGHT:
1131 case EL_BD_FIREFLY_UP:
1132 case EL_BD_FIREFLY_LEFT:
1133 case EL_BD_FIREFLY_DOWN:
1134 case EL_PACMAN_RIGHT:
1136 case EL_PACMAN_LEFT:
1137 case EL_PACMAN_DOWN:
1139 case EL_YAMYAM_LEFT:
1140 case EL_YAMYAM_RIGHT:
1142 case EL_YAMYAM_DOWN:
1143 case EL_DARK_YAMYAM:
1146 case EL_SP_SNIKSNAK:
1147 case EL_SP_ELECTRON:
1156 case EL_AMOEBA_FULL:
1161 case EL_AMOEBA_DROP:
1162 if (y == lev_fieldy - 1)
1164 Feld[x][y] = EL_AMOEBA_GROWING;
1165 Store[x][y] = EL_AMOEBA_WET;
1169 case EL_DYNAMITE_ACTIVE:
1170 case EL_SP_DISK_RED_ACTIVE:
1171 case EL_DYNABOMB_PLAYER_1_ACTIVE:
1172 case EL_DYNABOMB_PLAYER_2_ACTIVE:
1173 case EL_DYNABOMB_PLAYER_3_ACTIVE:
1174 case EL_DYNABOMB_PLAYER_4_ACTIVE:
1175 MovDelay[x][y] = 96;
1178 case EL_EM_DYNAMITE_ACTIVE:
1179 MovDelay[x][y] = 32;
1183 local_player->lights_still_needed++;
1187 local_player->friends_still_needed++;
1192 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1195 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1196 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1197 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1198 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1199 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1200 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1201 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1202 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1203 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1204 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1205 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1206 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1209 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1210 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1211 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1213 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
1215 game.belt_dir[belt_nr] = belt_dir;
1216 game.belt_dir_nr[belt_nr] = belt_dir_nr;
1218 else /* more than one switch -- set it like the first switch */
1220 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1225 #if !USE_BOTH_SWITCHGATE_SWITCHES
1226 case EL_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
1228 Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
1231 case EL_DC_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
1233 Feld[x][y] = EL_DC_SWITCHGATE_SWITCH_UP;
1237 case EL_LIGHT_SWITCH_ACTIVE:
1239 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1242 case EL_INVISIBLE_STEELWALL:
1243 case EL_INVISIBLE_WALL:
1244 case EL_INVISIBLE_SAND:
1245 if (game.light_time_left > 0 ||
1246 game.lenses_time_left > 0)
1247 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1250 case EL_EMC_MAGIC_BALL:
1251 if (game.ball_state)
1252 Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1255 case EL_EMC_MAGIC_BALL_SWITCH:
1256 if (game.ball_state)
1257 Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1261 if (IS_CUSTOM_ELEMENT(element))
1263 if (CAN_MOVE(element))
1266 #if USE_NEW_CUSTOM_VALUE
1267 if (!element_info[element].use_last_ce_value || init_game)
1268 CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1271 else if (IS_GROUP_ELEMENT(element))
1273 Feld[x][y] = get_element_from_group_element(element);
1275 InitField(x, y, init_game);
1282 CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1285 static inline void InitField_WithBug1(int x, int y, boolean init_game)
1287 InitField(x, y, init_game);
1289 /* not needed to call InitMovDir() -- already done by InitField()! */
1290 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1291 CAN_MOVE(Feld[x][y]))
1295 static inline void InitField_WithBug2(int x, int y, boolean init_game)
1297 int old_element = Feld[x][y];
1299 InitField(x, y, init_game);
1301 /* not needed to call InitMovDir() -- already done by InitField()! */
1302 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1303 CAN_MOVE(old_element) &&
1304 (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1307 /* this case is in fact a combination of not less than three bugs:
1308 first, it calls InitMovDir() for elements that can move, although this is
1309 already done by InitField(); then, it checks the element that was at this
1310 field _before_ the call to InitField() (which can change it); lastly, it
1311 was not called for "mole with direction" elements, which were treated as
1312 "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1316 inline void DrawGameValue_Emeralds(int value)
1318 int xpos = (3 * 14 - 3 * getFontWidth(FONT_TEXT_2)) / 2;
1320 if (PANEL_DEACTIVATED(game.panel.gems))
1323 DrawText(DX_EMERALDS + xpos, DY_EMERALDS, int2str(value, 3), FONT_TEXT_2);
1326 inline void DrawGameValue_Dynamite(int value)
1328 int xpos = (3 * 14 - 3 * getFontWidth(FONT_TEXT_2)) / 2;
1330 if (PANEL_DEACTIVATED(game.panel.inventory))
1333 DrawText(DX_DYNAMITE + xpos, DY_DYNAMITE, int2str(value, 3), FONT_TEXT_2);
1336 inline void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
1338 int base_key_graphic = EL_KEY_1;
1341 if (PANEL_DEACTIVATED(game.panel.keys))
1344 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
1345 base_key_graphic = EL_EM_KEY_1;
1347 /* currently only 4 of 8 possible keys are displayed */
1348 for (i = 0; i < STD_NUM_KEYS; i++)
1350 int x = XX_KEYS + i * MINI_TILEX;
1354 DrawMiniGraphicExt(drawto, DX + x,DY + y, el2edimg(base_key_graphic + i));
1356 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
1357 DOOR_GFX_PAGEX5 + x, y, MINI_TILEX, MINI_TILEY, DX + x,DY + y);
1361 inline void DrawGameValue_Score(int value)
1363 int xpos = (5 * 14 - 5 * getFontWidth(FONT_TEXT_2)) / 2;
1365 if (PANEL_DEACTIVATED(game.panel.score))
1368 DrawText(DX_SCORE + xpos, DY_SCORE, int2str(value, 5), FONT_TEXT_2);
1371 inline void DrawGameValue_Time(int value)
1373 int xpos3 = (3 * 14 - 3 * getFontWidth(FONT_TEXT_2)) / 2;
1374 int xpos4 = (4 * 10 - 4 * getFontWidth(FONT_LEVEL_NUMBER)) / 2;
1376 if (PANEL_DEACTIVATED(game.panel.time))
1379 /* clear background if value just changed its size */
1380 if (value == 999 || value == 1000)
1381 ClearRectangleOnBackground(drawto, DX_TIME1, DY_TIME, 14 * 3, 14);
1384 DrawText(DX_TIME1 + xpos3, DY_TIME, int2str(value, 3), FONT_TEXT_2);
1386 DrawText(DX_TIME2 + xpos4, DY_TIME, int2str(value, 4), FONT_LEVEL_NUMBER);
1389 inline void DrawGameValue_Level(int value)
1391 if (PANEL_DEACTIVATED(game.panel.level))
1395 DrawText(DX_LEVEL1, DY_LEVEL, int2str(value, 2), FONT_TEXT_2);
1397 DrawText(DX_LEVEL2, DY_LEVEL, int2str(value, 3), FONT_LEVEL_NUMBER);
1400 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
1403 int key[MAX_NUM_KEYS];
1406 /* prevent EM engine from updating time/score values parallel to GameWon() */
1407 if (level.game_engine_type == GAME_ENGINE_TYPE_EM &&
1408 local_player->LevelSolved)
1411 for (i = 0; i < MAX_NUM_KEYS; i++)
1412 key[i] = key_bits & (1 << i);
1414 DrawGameValue_Level(level_nr);
1416 DrawGameValue_Emeralds(emeralds);
1417 DrawGameValue_Dynamite(dynamite);
1418 DrawGameValue_Score(score);
1419 DrawGameValue_Time(time);
1421 DrawGameValue_Keys(key);
1424 void DrawGameDoorValues()
1426 int time_value = (level.time == 0 ? TimePlayed : TimeLeft);
1427 int dynamite_value = 0;
1428 int score_value = (local_player->LevelSolved ? local_player->score_final :
1429 local_player->score);
1430 int gems_value = local_player->gems_still_needed;
1434 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
1436 DrawGameDoorValues_EM();
1441 if (game.centered_player_nr == -1)
1443 for (i = 0; i < MAX_PLAYERS; i++)
1445 for (j = 0; j < MAX_NUM_KEYS; j++)
1446 if (stored_player[i].key[j])
1447 key_bits |= (1 << j);
1449 dynamite_value += stored_player[i].inventory_size;
1454 int player_nr = game.centered_player_nr;
1456 for (i = 0; i < MAX_NUM_KEYS; i++)
1457 if (stored_player[player_nr].key[i])
1458 key_bits |= (1 << i);
1460 dynamite_value = stored_player[player_nr].inventory_size;
1463 DrawAllGameValues(gems_value, dynamite_value, score_value, time_value,
1469 =============================================================================
1471 -----------------------------------------------------------------------------
1472 initialize game engine due to level / tape version number
1473 =============================================================================
1476 static void InitGameEngine()
1478 int i, j, k, l, x, y;
1480 /* set game engine from tape file when re-playing, else from level file */
1481 game.engine_version = (tape.playing ? tape.engine_version :
1482 level.game_version);
1484 /* ---------------------------------------------------------------------- */
1485 /* set flags for bugs and changes according to active game engine version */
1486 /* ---------------------------------------------------------------------- */
1489 Summary of bugfix/change:
1490 Fixed handling for custom elements that change when pushed by the player.
1492 Fixed/changed in version:
1496 Before 3.1.0, custom elements that "change when pushing" changed directly
1497 after the player started pushing them (until then handled in "DigField()").
1498 Since 3.1.0, these custom elements are not changed until the "pushing"
1499 move of the element is finished (now handled in "ContinueMoving()").
1501 Affected levels/tapes:
1502 The first condition is generally needed for all levels/tapes before version
1503 3.1.0, which might use the old behaviour before it was changed; known tapes
1504 that are affected are some tapes from the level set "Walpurgis Gardens" by
1506 The second condition is an exception from the above case and is needed for
1507 the special case of tapes recorded with game (not engine!) version 3.1.0 or
1508 above (including some development versions of 3.1.0), but before it was
1509 known that this change would break tapes like the above and was fixed in
1510 3.1.1, so that the changed behaviour was active although the engine version
1511 while recording maybe was before 3.1.0. There is at least one tape that is
1512 affected by this exception, which is the tape for the one-level set "Bug
1513 Machine" by Juergen Bonhagen.
1516 game.use_change_when_pushing_bug =
1517 (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1519 tape.game_version >= VERSION_IDENT(3,1,0,0) &&
1520 tape.game_version < VERSION_IDENT(3,1,1,0)));
1523 Summary of bugfix/change:
1524 Fixed handling for blocking the field the player leaves when moving.
1526 Fixed/changed in version:
1530 Before 3.1.1, when "block last field when moving" was enabled, the field
1531 the player is leaving when moving was blocked for the time of the move,
1532 and was directly unblocked afterwards. This resulted in the last field
1533 being blocked for exactly one less than the number of frames of one player
1534 move. Additionally, even when blocking was disabled, the last field was
1535 blocked for exactly one frame.
1536 Since 3.1.1, due to changes in player movement handling, the last field
1537 is not blocked at all when blocking is disabled. When blocking is enabled,
1538 the last field is blocked for exactly the number of frames of one player
1539 move. Additionally, if the player is Murphy, the hero of Supaplex, the
1540 last field is blocked for exactly one more than the number of frames of
1543 Affected levels/tapes:
1544 (!!! yet to be determined -- probably many !!!)
1547 game.use_block_last_field_bug =
1548 (game.engine_version < VERSION_IDENT(3,1,1,0));
1551 Summary of bugfix/change:
1552 Changed behaviour of CE changes with multiple changes per single frame.
1554 Fixed/changed in version:
1558 Before 3.2.0-6, only one single CE change was allowed in each engine frame.
1559 This resulted in race conditions where CEs seem to behave strange in some
1560 situations (where triggered CE changes were just skipped because there was
1561 already a CE change on that tile in the playfield in that engine frame).
1562 Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
1563 (The number of changes per frame must be limited in any case, because else
1564 it is easily possible to define CE changes that would result in an infinite
1565 loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
1566 should be set large enough so that it would only be reached in cases where
1567 the corresponding CE change conditions run into a loop. Therefore, it seems
1568 to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
1569 maximal number of change pages for custom elements.)
1571 Affected levels/tapes:
1575 #if USE_ONLY_ONE_CHANGE_PER_FRAME
1576 game.max_num_changes_per_frame = 1;
1578 game.max_num_changes_per_frame =
1579 (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
1582 /* ---------------------------------------------------------------------- */
1584 /* default scan direction: scan playfield from top/left to bottom/right */
1585 InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
1587 /* dynamically adjust element properties according to game engine version */
1588 InitElementPropertiesEngine(game.engine_version);
1591 printf("level %d: level version == %06d\n", level_nr, level.game_version);
1592 printf(" tape version == %06d [%s] [file: %06d]\n",
1593 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
1595 printf(" => game.engine_version == %06d\n", game.engine_version);
1598 /* ---------- initialize player's initial move delay --------------------- */
1600 /* dynamically adjust player properties according to level information */
1601 for (i = 0; i < MAX_PLAYERS; i++)
1602 game.initial_move_delay_value[i] =
1603 get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
1605 /* dynamically adjust player properties according to game engine version */
1606 for (i = 0; i < MAX_PLAYERS; i++)
1607 game.initial_move_delay[i] =
1608 (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
1609 game.initial_move_delay_value[i] : 0);
1611 /* ---------- initialize player's initial push delay --------------------- */
1613 /* dynamically adjust player properties according to game engine version */
1614 game.initial_push_delay_value =
1615 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
1617 /* ---------- initialize changing elements ------------------------------- */
1619 /* initialize changing elements information */
1620 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1622 struct ElementInfo *ei = &element_info[i];
1624 /* this pointer might have been changed in the level editor */
1625 ei->change = &ei->change_page[0];
1627 if (!IS_CUSTOM_ELEMENT(i))
1629 ei->change->target_element = EL_EMPTY_SPACE;
1630 ei->change->delay_fixed = 0;
1631 ei->change->delay_random = 0;
1632 ei->change->delay_frames = 1;
1635 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1637 ei->has_change_event[j] = FALSE;
1639 ei->event_page_nr[j] = 0;
1640 ei->event_page[j] = &ei->change_page[0];
1644 /* add changing elements from pre-defined list */
1645 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
1647 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
1648 struct ElementInfo *ei = &element_info[ch_delay->element];
1650 ei->change->target_element = ch_delay->target_element;
1651 ei->change->delay_fixed = ch_delay->change_delay;
1653 ei->change->pre_change_function = ch_delay->pre_change_function;
1654 ei->change->change_function = ch_delay->change_function;
1655 ei->change->post_change_function = ch_delay->post_change_function;
1657 ei->change->can_change = TRUE;
1658 ei->change->can_change_or_has_action = TRUE;
1660 ei->has_change_event[CE_DELAY] = TRUE;
1662 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
1663 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
1666 /* ---------- initialize internal run-time variables ------------- */
1668 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1670 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1672 for (j = 0; j < ei->num_change_pages; j++)
1674 ei->change_page[j].can_change_or_has_action =
1675 (ei->change_page[j].can_change |
1676 ei->change_page[j].has_action);
1680 /* add change events from custom element configuration */
1681 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1683 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1685 for (j = 0; j < ei->num_change_pages; j++)
1687 if (!ei->change_page[j].can_change_or_has_action)
1690 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1692 /* only add event page for the first page found with this event */
1693 if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
1695 ei->has_change_event[k] = TRUE;
1697 ei->event_page_nr[k] = j;
1698 ei->event_page[k] = &ei->change_page[j];
1704 /* ---------- initialize run-time trigger player and element ------------- */
1706 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1708 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1710 for (j = 0; j < ei->num_change_pages; j++)
1712 ei->change_page[j].actual_trigger_element = EL_EMPTY;
1713 ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
1714 ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
1715 ei->change_page[j].actual_trigger_ce_value = 0;
1716 ei->change_page[j].actual_trigger_ce_score = 0;
1720 /* ---------- initialize trigger events ---------------------------------- */
1722 /* initialize trigger events information */
1723 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1724 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1725 trigger_events[i][j] = FALSE;
1727 /* add trigger events from element change event properties */
1728 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1730 struct ElementInfo *ei = &element_info[i];
1732 for (j = 0; j < ei->num_change_pages; j++)
1734 if (!ei->change_page[j].can_change_or_has_action)
1737 if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
1739 int trigger_element = ei->change_page[j].trigger_element;
1741 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1743 if (ei->change_page[j].has_event[k])
1745 if (IS_GROUP_ELEMENT(trigger_element))
1747 struct ElementGroupInfo *group =
1748 element_info[trigger_element].group;
1750 for (l = 0; l < group->num_elements_resolved; l++)
1751 trigger_events[group->element_resolved[l]][k] = TRUE;
1753 else if (trigger_element == EL_ANY_ELEMENT)
1754 for (l = 0; l < MAX_NUM_ELEMENTS; l++)
1755 trigger_events[l][k] = TRUE;
1757 trigger_events[trigger_element][k] = TRUE;
1764 /* ---------- initialize push delay -------------------------------------- */
1766 /* initialize push delay values to default */
1767 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1769 if (!IS_CUSTOM_ELEMENT(i))
1771 /* set default push delay values (corrected since version 3.0.7-1) */
1772 if (game.engine_version < VERSION_IDENT(3,0,7,1))
1774 element_info[i].push_delay_fixed = 2;
1775 element_info[i].push_delay_random = 8;
1779 element_info[i].push_delay_fixed = 8;
1780 element_info[i].push_delay_random = 8;
1785 /* set push delay value for certain elements from pre-defined list */
1786 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
1788 int e = push_delay_list[i].element;
1790 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
1791 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
1794 /* set push delay value for Supaplex elements for newer engine versions */
1795 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
1797 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1799 if (IS_SP_ELEMENT(i))
1801 /* set SP push delay to just enough to push under a falling zonk */
1802 int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
1804 element_info[i].push_delay_fixed = delay;
1805 element_info[i].push_delay_random = 0;
1810 /* ---------- initialize move stepsize ----------------------------------- */
1812 /* initialize move stepsize values to default */
1813 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1814 if (!IS_CUSTOM_ELEMENT(i))
1815 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
1817 /* set move stepsize value for certain elements from pre-defined list */
1818 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
1820 int e = move_stepsize_list[i].element;
1822 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
1825 /* ---------- initialize collect score ----------------------------------- */
1827 /* initialize collect score values for custom elements from initial value */
1828 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1829 if (IS_CUSTOM_ELEMENT(i))
1830 element_info[i].collect_score = element_info[i].collect_score_initial;
1832 /* ---------- initialize collect count ----------------------------------- */
1834 /* initialize collect count values for non-custom elements */
1835 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1836 if (!IS_CUSTOM_ELEMENT(i))
1837 element_info[i].collect_count_initial = 0;
1839 /* add collect count values for all elements from pre-defined list */
1840 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
1841 element_info[collect_count_list[i].element].collect_count_initial =
1842 collect_count_list[i].count;
1844 /* ---------- initialize access direction -------------------------------- */
1846 /* initialize access direction values to default (access from every side) */
1847 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1848 if (!IS_CUSTOM_ELEMENT(i))
1849 element_info[i].access_direction = MV_ALL_DIRECTIONS;
1851 /* set access direction value for certain elements from pre-defined list */
1852 for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
1853 element_info[access_direction_list[i].element].access_direction =
1854 access_direction_list[i].direction;
1856 /* ---------- initialize explosion content ------------------------------- */
1857 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1859 if (IS_CUSTOM_ELEMENT(i))
1862 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
1864 /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
1866 element_info[i].content.e[x][y] =
1867 (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
1868 i == EL_PLAYER_2 ? EL_EMERALD_RED :
1869 i == EL_PLAYER_3 ? EL_EMERALD :
1870 i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
1871 i == EL_MOLE ? EL_EMERALD_RED :
1872 i == EL_PENGUIN ? EL_EMERALD_PURPLE :
1873 i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
1874 i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
1875 i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
1876 i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
1877 i == EL_WALL_EMERALD ? EL_EMERALD :
1878 i == EL_WALL_DIAMOND ? EL_DIAMOND :
1879 i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
1880 i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
1881 i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
1882 i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
1883 i == EL_WALL_PEARL ? EL_PEARL :
1884 i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
1889 /* ---------- initialize recursion detection ------------------------------ */
1890 recursion_loop_depth = 0;
1891 recursion_loop_detected = FALSE;
1892 recursion_loop_element = EL_UNDEFINED;
1895 int get_num_special_action(int element, int action_first, int action_last)
1897 int num_special_action = 0;
1900 for (i = action_first; i <= action_last; i++)
1902 boolean found = FALSE;
1904 for (j = 0; j < NUM_DIRECTIONS; j++)
1905 if (el_act_dir2img(element, i, j) !=
1906 el_act_dir2img(element, ACTION_DEFAULT, j))
1910 num_special_action++;
1915 return num_special_action;
1920 =============================================================================
1922 -----------------------------------------------------------------------------
1923 initialize and start new game
1924 =============================================================================
1929 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
1930 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
1931 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
1932 boolean do_fading = (game_status == GAME_MODE_MAIN);
1935 game_status = GAME_MODE_PLAYING;
1939 /* don't play tapes over network */
1940 network_playing = (options.network && !tape.playing);
1942 for (i = 0; i < MAX_PLAYERS; i++)
1944 struct PlayerInfo *player = &stored_player[i];
1946 player->index_nr = i;
1947 player->index_bit = (1 << i);
1948 player->element_nr = EL_PLAYER_1 + i;
1950 player->present = FALSE;
1951 player->active = FALSE;
1952 player->killed = FALSE;
1955 player->effective_action = 0;
1956 player->programmed_action = 0;
1959 player->score_final = 0;
1961 player->gems_still_needed = level.gems_needed;
1962 player->sokobanfields_still_needed = 0;
1963 player->lights_still_needed = 0;
1964 player->friends_still_needed = 0;
1966 for (j = 0; j < MAX_NUM_KEYS; j++)
1967 player->key[j] = FALSE;
1969 player->dynabomb_count = 0;
1970 player->dynabomb_size = 1;
1971 player->dynabombs_left = 0;
1972 player->dynabomb_xl = FALSE;
1974 player->MovDir = MV_NONE;
1977 player->GfxDir = MV_NONE;
1978 player->GfxAction = ACTION_DEFAULT;
1980 player->StepFrame = 0;
1982 player->use_murphy = FALSE;
1983 player->artwork_element =
1984 (level.use_artwork_element[i] ? level.artwork_element[i] :
1985 player->element_nr);
1987 player->block_last_field = FALSE; /* initialized in InitPlayerField() */
1988 player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
1990 player->gravity = level.initial_player_gravity[i];
1992 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
1994 player->actual_frame_counter = 0;
1996 player->step_counter = 0;
1998 player->last_move_dir = MV_NONE;
2000 player->is_active = FALSE;
2002 player->is_waiting = FALSE;
2003 player->is_moving = FALSE;
2004 player->is_auto_moving = FALSE;
2005 player->is_digging = FALSE;
2006 player->is_snapping = FALSE;
2007 player->is_collecting = FALSE;
2008 player->is_pushing = FALSE;
2009 player->is_switching = FALSE;
2010 player->is_dropping = FALSE;
2011 player->is_dropping_pressed = FALSE;
2013 player->is_bored = FALSE;
2014 player->is_sleeping = FALSE;
2016 player->frame_counter_bored = -1;
2017 player->frame_counter_sleeping = -1;
2019 player->anim_delay_counter = 0;
2020 player->post_delay_counter = 0;
2022 player->dir_waiting = MV_NONE;
2023 player->action_waiting = ACTION_DEFAULT;
2024 player->last_action_waiting = ACTION_DEFAULT;
2025 player->special_action_bored = ACTION_DEFAULT;
2026 player->special_action_sleeping = ACTION_DEFAULT;
2028 player->switch_x = -1;
2029 player->switch_y = -1;
2031 player->drop_x = -1;
2032 player->drop_y = -1;
2034 player->show_envelope = 0;
2036 SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
2038 player->push_delay = -1; /* initialized when pushing starts */
2039 player->push_delay_value = game.initial_push_delay_value;
2041 player->drop_delay = 0;
2042 player->drop_pressed_delay = 0;
2044 player->last_jx = -1;
2045 player->last_jy = -1;
2049 player->shield_normal_time_left = 0;
2050 player->shield_deadly_time_left = 0;
2052 player->inventory_infinite_element = EL_UNDEFINED;
2053 player->inventory_size = 0;
2055 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
2056 SnapField(player, 0, 0);
2058 player->LevelSolved = FALSE;
2059 player->GameOver = FALSE;
2061 player->LevelSolved_GameEnd = FALSE;
2062 player->LevelSolved_SaveTape = FALSE;
2063 player->LevelSolved_SaveScore = FALSE;
2066 network_player_action_received = FALSE;
2068 #if defined(NETWORK_AVALIABLE)
2069 /* initial null action */
2070 if (network_playing)
2071 SendToServer_MovePlayer(MV_NONE);
2080 TimeLeft = level.time;
2083 ScreenMovDir = MV_NONE;
2087 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
2089 AllPlayersGone = FALSE;
2091 game.yamyam_content_nr = 0;
2092 game.magic_wall_active = FALSE;
2093 game.magic_wall_time_left = 0;
2094 game.light_time_left = 0;
2095 game.timegate_time_left = 0;
2096 game.switchgate_pos = 0;
2097 game.wind_direction = level.wind_direction_initial;
2099 #if !USE_PLAYER_GRAVITY
2100 game.gravity = FALSE;
2101 game.explosions_delayed = TRUE;
2104 game.lenses_time_left = 0;
2105 game.magnify_time_left = 0;
2107 game.ball_state = level.ball_state_initial;
2108 game.ball_content_nr = 0;
2110 game.envelope_active = FALSE;
2112 /* set focus to local player for network games, else to all players */
2113 game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
2114 game.centered_player_nr_next = game.centered_player_nr;
2115 game.set_centered_player = FALSE;
2117 if (network_playing && tape.recording)
2119 /* store client dependent player focus when recording network games */
2120 tape.centered_player_nr_next = game.centered_player_nr_next;
2121 tape.set_centered_player = TRUE;
2124 for (i = 0; i < NUM_BELTS; i++)
2126 game.belt_dir[i] = MV_NONE;
2127 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
2130 for (i = 0; i < MAX_NUM_AMOEBA; i++)
2131 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
2133 SCAN_PLAYFIELD(x, y)
2135 Feld[x][y] = level.field[x][y];
2136 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
2137 ChangeDelay[x][y] = 0;
2138 ChangePage[x][y] = -1;
2139 #if USE_NEW_CUSTOM_VALUE
2140 CustomValue[x][y] = 0; /* initialized in InitField() */
2142 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
2144 WasJustMoving[x][y] = 0;
2145 WasJustFalling[x][y] = 0;
2146 CheckCollision[x][y] = 0;
2147 CheckImpact[x][y] = 0;
2149 Pushed[x][y] = FALSE;
2151 ChangeCount[x][y] = 0;
2152 ChangeEvent[x][y] = -1;
2154 ExplodePhase[x][y] = 0;
2155 ExplodeDelay[x][y] = 0;
2156 ExplodeField[x][y] = EX_TYPE_NONE;
2158 RunnerVisit[x][y] = 0;
2159 PlayerVisit[x][y] = 0;
2162 GfxRandom[x][y] = INIT_GFX_RANDOM();
2163 GfxElement[x][y] = EL_UNDEFINED;
2164 GfxAction[x][y] = ACTION_DEFAULT;
2165 GfxDir[x][y] = MV_NONE;
2168 SCAN_PLAYFIELD(x, y)
2170 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
2172 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
2174 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
2177 InitField(x, y, TRUE);
2182 for (i = 0; i < MAX_PLAYERS; i++)
2184 struct PlayerInfo *player = &stored_player[i];
2186 /* set number of special actions for bored and sleeping animation */
2187 player->num_special_action_bored =
2188 get_num_special_action(player->artwork_element,
2189 ACTION_BORING_1, ACTION_BORING_LAST);
2190 player->num_special_action_sleeping =
2191 get_num_special_action(player->artwork_element,
2192 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
2195 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
2196 emulate_sb ? EMU_SOKOBAN :
2197 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
2199 #if USE_NEW_ALL_SLIPPERY
2200 /* initialize type of slippery elements */
2201 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2203 if (!IS_CUSTOM_ELEMENT(i))
2205 /* default: elements slip down either to the left or right randomly */
2206 element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
2208 /* SP style elements prefer to slip down on the left side */
2209 if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
2210 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
2212 /* BD style elements prefer to slip down on the left side */
2213 if (game.emulation == EMU_BOULDERDASH)
2214 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
2219 /* initialize explosion and ignition delay */
2220 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2222 if (!IS_CUSTOM_ELEMENT(i))
2225 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
2226 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
2227 game.emulation == EMU_SUPAPLEX ? 3 : 2);
2228 int last_phase = (num_phase + 1) * delay;
2229 int half_phase = (num_phase / 2) * delay;
2231 element_info[i].explosion_delay = last_phase - 1;
2232 element_info[i].ignition_delay = half_phase;
2234 if (i == EL_BLACK_ORB)
2235 element_info[i].ignition_delay = 1;
2239 if (element_info[i].explosion_delay < 1) /* !!! check again !!! */
2240 element_info[i].explosion_delay = 1;
2242 if (element_info[i].ignition_delay < 1) /* !!! check again !!! */
2243 element_info[i].ignition_delay = 1;
2247 /* correct non-moving belts to start moving left */
2248 for (i = 0; i < NUM_BELTS; i++)
2249 if (game.belt_dir[i] == MV_NONE)
2250 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
2252 /* check if any connected player was not found in playfield */
2253 for (i = 0; i < MAX_PLAYERS; i++)
2255 struct PlayerInfo *player = &stored_player[i];
2257 if (player->connected && !player->present)
2259 for (j = 0; j < MAX_PLAYERS; j++)
2261 struct PlayerInfo *some_player = &stored_player[j];
2262 int jx = some_player->jx, jy = some_player->jy;
2264 /* assign first free player found that is present in the playfield */
2265 if (some_player->present && !some_player->connected)
2267 player->present = TRUE;
2268 player->active = TRUE;
2270 some_player->present = FALSE;
2271 some_player->active = FALSE;
2273 player->artwork_element = some_player->artwork_element;
2275 player->block_last_field = some_player->block_last_field;
2276 player->block_delay_adjustment = some_player->block_delay_adjustment;
2278 StorePlayer[jx][jy] = player->element_nr;
2279 player->jx = player->last_jx = jx;
2280 player->jy = player->last_jy = jy;
2290 /* when playing a tape, eliminate all players who do not participate */
2292 for (i = 0; i < MAX_PLAYERS; i++)
2294 if (stored_player[i].active && !tape.player_participates[i])
2296 struct PlayerInfo *player = &stored_player[i];
2297 int jx = player->jx, jy = player->jy;
2299 player->active = FALSE;
2300 StorePlayer[jx][jy] = 0;
2301 Feld[jx][jy] = EL_EMPTY;
2305 else if (!options.network && !setup.team_mode) /* && !tape.playing */
2307 /* when in single player mode, eliminate all but the first active player */
2309 for (i = 0; i < MAX_PLAYERS; i++)
2311 if (stored_player[i].active)
2313 for (j = i + 1; j < MAX_PLAYERS; j++)
2315 if (stored_player[j].active)
2317 struct PlayerInfo *player = &stored_player[j];
2318 int jx = player->jx, jy = player->jy;
2320 player->active = FALSE;
2321 player->present = FALSE;
2323 StorePlayer[jx][jy] = 0;
2324 Feld[jx][jy] = EL_EMPTY;
2331 /* when recording the game, store which players take part in the game */
2334 for (i = 0; i < MAX_PLAYERS; i++)
2335 if (stored_player[i].active)
2336 tape.player_participates[i] = TRUE;
2341 for (i = 0; i < MAX_PLAYERS; i++)
2343 struct PlayerInfo *player = &stored_player[i];
2345 printf("Player %d: present == %d, connected == %d, active == %d.\n",
2350 if (local_player == player)
2351 printf("Player %d is local player.\n", i+1);
2355 if (BorderElement == EL_EMPTY)
2358 SBX_Right = lev_fieldx - SCR_FIELDX;
2360 SBY_Lower = lev_fieldy - SCR_FIELDY;
2365 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
2367 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
2370 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
2371 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
2373 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
2374 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
2376 /* if local player not found, look for custom element that might create
2377 the player (make some assumptions about the right custom element) */
2378 if (!local_player->present)
2380 int start_x = 0, start_y = 0;
2381 int found_rating = 0;
2382 int found_element = EL_UNDEFINED;
2383 int player_nr = local_player->index_nr;
2385 SCAN_PLAYFIELD(x, y)
2387 int element = Feld[x][y];
2392 if (level.use_start_element[player_nr] &&
2393 level.start_element[player_nr] == element &&
2400 found_element = element;
2403 if (!IS_CUSTOM_ELEMENT(element))
2406 if (CAN_CHANGE(element))
2408 for (i = 0; i < element_info[element].num_change_pages; i++)
2410 /* check for player created from custom element as single target */
2411 content = element_info[element].change_page[i].target_element;
2412 is_player = ELEM_IS_PLAYER(content);
2414 if (is_player && (found_rating < 3 || element < found_element))
2420 found_element = element;
2425 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
2427 /* check for player created from custom element as explosion content */
2428 content = element_info[element].content.e[xx][yy];
2429 is_player = ELEM_IS_PLAYER(content);
2431 if (is_player && (found_rating < 2 || element < found_element))
2433 start_x = x + xx - 1;
2434 start_y = y + yy - 1;
2437 found_element = element;
2440 if (!CAN_CHANGE(element))
2443 for (i = 0; i < element_info[element].num_change_pages; i++)
2445 /* check for player created from custom element as extended target */
2447 element_info[element].change_page[i].target_content.e[xx][yy];
2449 is_player = ELEM_IS_PLAYER(content);
2451 if (is_player && (found_rating < 1 || element < found_element))
2453 start_x = x + xx - 1;
2454 start_y = y + yy - 1;
2457 found_element = element;
2463 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
2464 start_x > SBX_Right + MIDPOSX ? SBX_Right :
2467 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
2468 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
2473 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2474 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2475 local_player->jx - MIDPOSX);
2477 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2478 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2479 local_player->jy - MIDPOSY);
2484 if (!game.restart_level)
2485 CloseDoor(DOOR_CLOSE_1);
2488 FadeOut(REDRAW_FIELD);
2490 /* !!! FIX THIS (START) !!! */
2491 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2493 InitGameEngine_EM();
2495 /* blit playfield from scroll buffer to normal back buffer for fading in */
2496 BlitScreenToBitmap_EM(backbuffer);
2503 /* after drawing the level, correct some elements */
2504 if (game.timegate_time_left == 0)
2505 CloseAllOpenTimegates();
2507 /* blit playfield from scroll buffer to normal back buffer for fading in */
2508 if (setup.soft_scrolling)
2509 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
2511 redraw_mask |= REDRAW_FROM_BACKBUFFER;
2513 /* !!! FIX THIS (END) !!! */
2516 FadeIn(REDRAW_FIELD);
2520 if (!game.restart_level)
2522 /* copy default game door content to main double buffer */
2523 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2524 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2527 SetPanelBackground();
2528 SetDrawBackgroundMask(REDRAW_DOOR_1);
2530 DrawGameDoorValues();
2532 if (!game.restart_level)
2536 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
2537 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
2538 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
2542 /* copy actual game door content to door double buffer for OpenDoor() */
2543 BlitBitmap(drawto, bitmap_db_door,
2544 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
2546 OpenDoor(DOOR_OPEN_ALL);
2548 PlaySound(SND_GAME_STARTING);
2550 if (setup.sound_music)
2553 KeyboardAutoRepeatOffUnlessAutoplay();
2557 for (i = 0; i < MAX_PLAYERS; i++)
2558 printf("Player %d %sactive.\n",
2559 i + 1, (stored_player[i].active ? "" : "not "));
2570 game.restart_level = FALSE;
2573 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
2575 /* this is used for non-R'n'D game engines to update certain engine values */
2577 /* needed to determine if sounds are played within the visible screen area */
2578 scroll_x = actual_scroll_x;
2579 scroll_y = actual_scroll_y;
2582 void InitMovDir(int x, int y)
2584 int i, element = Feld[x][y];
2585 static int xy[4][2] =
2592 static int direction[3][4] =
2594 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
2595 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
2596 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
2605 Feld[x][y] = EL_BUG;
2606 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
2609 case EL_SPACESHIP_RIGHT:
2610 case EL_SPACESHIP_UP:
2611 case EL_SPACESHIP_LEFT:
2612 case EL_SPACESHIP_DOWN:
2613 Feld[x][y] = EL_SPACESHIP;
2614 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
2617 case EL_BD_BUTTERFLY_RIGHT:
2618 case EL_BD_BUTTERFLY_UP:
2619 case EL_BD_BUTTERFLY_LEFT:
2620 case EL_BD_BUTTERFLY_DOWN:
2621 Feld[x][y] = EL_BD_BUTTERFLY;
2622 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
2625 case EL_BD_FIREFLY_RIGHT:
2626 case EL_BD_FIREFLY_UP:
2627 case EL_BD_FIREFLY_LEFT:
2628 case EL_BD_FIREFLY_DOWN:
2629 Feld[x][y] = EL_BD_FIREFLY;
2630 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
2633 case EL_PACMAN_RIGHT:
2635 case EL_PACMAN_LEFT:
2636 case EL_PACMAN_DOWN:
2637 Feld[x][y] = EL_PACMAN;
2638 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
2641 case EL_YAMYAM_LEFT:
2642 case EL_YAMYAM_RIGHT:
2644 case EL_YAMYAM_DOWN:
2645 Feld[x][y] = EL_YAMYAM;
2646 MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
2649 case EL_SP_SNIKSNAK:
2650 MovDir[x][y] = MV_UP;
2653 case EL_SP_ELECTRON:
2654 MovDir[x][y] = MV_LEFT;
2661 Feld[x][y] = EL_MOLE;
2662 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
2666 if (IS_CUSTOM_ELEMENT(element))
2668 struct ElementInfo *ei = &element_info[element];
2669 int move_direction_initial = ei->move_direction_initial;
2670 int move_pattern = ei->move_pattern;
2672 if (move_direction_initial == MV_START_PREVIOUS)
2674 if (MovDir[x][y] != MV_NONE)
2677 move_direction_initial = MV_START_AUTOMATIC;
2680 if (move_direction_initial == MV_START_RANDOM)
2681 MovDir[x][y] = 1 << RND(4);
2682 else if (move_direction_initial & MV_ANY_DIRECTION)
2683 MovDir[x][y] = move_direction_initial;
2684 else if (move_pattern == MV_ALL_DIRECTIONS ||
2685 move_pattern == MV_TURNING_LEFT ||
2686 move_pattern == MV_TURNING_RIGHT ||
2687 move_pattern == MV_TURNING_LEFT_RIGHT ||
2688 move_pattern == MV_TURNING_RIGHT_LEFT ||
2689 move_pattern == MV_TURNING_RANDOM)
2690 MovDir[x][y] = 1 << RND(4);
2691 else if (move_pattern == MV_HORIZONTAL)
2692 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
2693 else if (move_pattern == MV_VERTICAL)
2694 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
2695 else if (move_pattern & MV_ANY_DIRECTION)
2696 MovDir[x][y] = element_info[element].move_pattern;
2697 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
2698 move_pattern == MV_ALONG_RIGHT_SIDE)
2700 /* use random direction as default start direction */
2701 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2702 MovDir[x][y] = 1 << RND(4);
2704 for (i = 0; i < NUM_DIRECTIONS; i++)
2706 int x1 = x + xy[i][0];
2707 int y1 = y + xy[i][1];
2709 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2711 if (move_pattern == MV_ALONG_RIGHT_SIDE)
2712 MovDir[x][y] = direction[0][i];
2714 MovDir[x][y] = direction[1][i];
2723 MovDir[x][y] = 1 << RND(4);
2725 if (element != EL_BUG &&
2726 element != EL_SPACESHIP &&
2727 element != EL_BD_BUTTERFLY &&
2728 element != EL_BD_FIREFLY)
2731 for (i = 0; i < NUM_DIRECTIONS; i++)
2733 int x1 = x + xy[i][0];
2734 int y1 = y + xy[i][1];
2736 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2738 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
2740 MovDir[x][y] = direction[0][i];
2743 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
2744 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
2746 MovDir[x][y] = direction[1][i];
2755 GfxDir[x][y] = MovDir[x][y];
2758 void InitAmoebaNr(int x, int y)
2761 int group_nr = AmoebeNachbarNr(x, y);
2765 for (i = 1; i < MAX_NUM_AMOEBA; i++)
2767 if (AmoebaCnt[i] == 0)
2775 AmoebaNr[x][y] = group_nr;
2776 AmoebaCnt[group_nr]++;
2777 AmoebaCnt2[group_nr]++;
2780 static void PlayerWins(struct PlayerInfo *player)
2782 player->LevelSolved = TRUE;
2783 player->GameOver = TRUE;
2785 player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2786 level.native_em_level->lev->score : player->score);
2791 static int time, time_final;
2792 static int score, score_final;
2793 static int game_over_delay = 0;
2794 int game_over_delay_value = 50;
2796 if (!local_player->LevelSolved_GameEnd)
2800 /* do not start end game actions before the player stops moving (to exit) */
2801 if (local_player->MovPos)
2804 local_player->LevelSolved_GameEnd = TRUE;
2805 local_player->LevelSolved_SaveTape = tape.recording;
2806 local_player->LevelSolved_SaveScore = !tape.playing;
2808 if (tape.auto_play) /* tape might already be stopped here */
2809 tape.auto_play_level_solved = TRUE;
2815 game_over_delay = game_over_delay_value;
2817 time = time_final = (level.time == 0 ? TimePlayed : TimeLeft);
2818 score = score_final = local_player->score_final;
2823 score_final += TimeLeft * level.score[SC_TIME_BONUS];
2825 else if (level.time == 0 && TimePlayed < 999)
2828 score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
2831 local_player->score_final = score_final;
2833 if (level_editor_test_game)
2836 score = score_final;
2838 DrawGameValue_Time(time);
2839 DrawGameValue_Score(score);
2842 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
2844 if (ExitX >= 0 && ExitY >= 0) /* local player has left the level */
2846 /* close exit door after last player */
2847 if ((AllPlayersGone &&
2848 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
2849 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
2850 Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
2851 Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
2852 Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
2854 int element = Feld[ExitX][ExitY];
2857 if (element == EL_EM_EXIT_OPEN ||
2858 element == EL_EM_STEEL_EXIT_OPEN)
2865 Feld[ExitX][ExitY] =
2866 (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
2867 element == EL_EM_EXIT_OPEN ? EL_EM_EXIT_CLOSING :
2868 element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING:
2869 element == EL_STEEL_EXIT_OPEN ? EL_STEEL_EXIT_CLOSING:
2870 EL_EM_STEEL_EXIT_CLOSING);
2872 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
2876 /* player disappears */
2877 DrawLevelField(ExitX, ExitY);
2880 for (i = 0; i < MAX_PLAYERS; i++)
2882 struct PlayerInfo *player = &stored_player[i];
2884 if (player->present)
2886 RemovePlayer(player);
2888 /* player disappears */
2889 DrawLevelField(player->jx, player->jy);
2894 PlaySound(SND_GAME_WINNING);
2897 if (game_over_delay > 0)
2904 if (time != time_final)
2906 int time_to_go = ABS(time_final - time);
2907 int time_count_dir = (time < time_final ? +1 : -1);
2908 int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
2910 time += time_count_steps * time_count_dir;
2911 score += time_count_steps * level.score[SC_TIME_BONUS];
2913 DrawGameValue_Time(time);
2914 DrawGameValue_Score(score);
2916 if (time == time_final)
2917 StopSound(SND_GAME_LEVELTIME_BONUS);
2918 else if (setup.sound_loops)
2919 PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
2921 PlaySound(SND_GAME_LEVELTIME_BONUS);
2928 boolean raise_level = FALSE;
2930 CloseDoor(DOOR_CLOSE_1);
2932 if (local_player->LevelSolved_SaveTape)
2939 SaveTapeChecked(tape.level_nr); /* ask to save tape */
2941 SaveTape(tape.level_nr); /* ask to save tape */
2945 if (level_editor_test_game)
2947 game_status = GAME_MODE_MAIN;
2954 if (!local_player->LevelSolved_SaveScore)
2956 FadeOut(REDRAW_FIELD);
2958 game_status = GAME_MODE_MAIN;
2960 DrawAndFadeInMainMenu(REDRAW_FIELD);
2965 if (level_nr == leveldir_current->handicap_level)
2967 leveldir_current->handicap_level++;
2968 SaveLevelSetup_SeriesInfo();
2971 if (level_nr < leveldir_current->last_level)
2972 raise_level = TRUE; /* advance to next level */
2974 if ((hi_pos = NewHiScore()) >= 0)
2976 game_status = GAME_MODE_SCORES;
2978 DrawHallOfFame(hi_pos);
2988 FadeOut(REDRAW_FIELD);
2990 game_status = GAME_MODE_MAIN;
2998 DrawAndFadeInMainMenu(REDRAW_FIELD);
3007 LoadScore(level_nr);
3009 if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
3010 local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score)
3013 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
3015 if (local_player->score_final > highscore[k].Score)
3017 /* player has made it to the hall of fame */
3019 if (k < MAX_SCORE_ENTRIES - 1)
3021 int m = MAX_SCORE_ENTRIES - 1;
3024 for (l = k; l < MAX_SCORE_ENTRIES; l++)
3025 if (strEqual(setup.player_name, highscore[l].Name))
3027 if (m == k) /* player's new highscore overwrites his old one */
3031 for (l = m; l > k; l--)
3033 strcpy(highscore[l].Name, highscore[l - 1].Name);
3034 highscore[l].Score = highscore[l - 1].Score;
3041 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
3042 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
3043 highscore[k].Score = local_player->score_final;
3049 else if (!strncmp(setup.player_name, highscore[k].Name,
3050 MAX_PLAYER_NAME_LEN))
3051 break; /* player already there with a higher score */
3057 SaveScore(level_nr);
3062 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
3064 int element = Feld[x][y];
3065 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3066 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
3067 int horiz_move = (dx != 0);
3068 int sign = (horiz_move ? dx : dy);
3069 int step = sign * element_info[element].move_stepsize;
3071 /* special values for move stepsize for spring and things on conveyor belt */
3074 if (CAN_FALL(element) &&
3075 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
3076 step = sign * MOVE_STEPSIZE_NORMAL / 2;
3077 else if (element == EL_SPRING)
3078 step = sign * MOVE_STEPSIZE_NORMAL * 2;
3084 inline static int getElementMoveStepsize(int x, int y)
3086 return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
3089 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
3091 if (player->GfxAction != action || player->GfxDir != dir)
3094 printf("Player frame reset! (%d => %d, %d => %d)\n",
3095 player->GfxAction, action, player->GfxDir, dir);
3098 player->GfxAction = action;
3099 player->GfxDir = dir;
3101 player->StepFrame = 0;
3105 #if USE_GFX_RESET_GFX_ANIMATION
3106 static void ResetGfxFrame(int x, int y, boolean redraw)
3108 int element = Feld[x][y];
3109 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3110 int last_gfx_frame = GfxFrame[x][y];
3112 if (graphic_info[graphic].anim_global_sync)
3113 GfxFrame[x][y] = FrameCounter;
3114 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
3115 GfxFrame[x][y] = CustomValue[x][y];
3116 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
3117 GfxFrame[x][y] = element_info[element].collect_score;
3118 else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
3119 GfxFrame[x][y] = ChangeDelay[x][y];
3121 if (redraw && GfxFrame[x][y] != last_gfx_frame)
3122 DrawLevelGraphicAnimation(x, y, graphic);
3126 static void ResetGfxAnimation(int x, int y)
3128 GfxAction[x][y] = ACTION_DEFAULT;
3129 GfxDir[x][y] = MovDir[x][y];
3132 #if USE_GFX_RESET_GFX_ANIMATION
3133 ResetGfxFrame(x, y, FALSE);
3137 static void ResetRandomAnimationValue(int x, int y)
3139 GfxRandom[x][y] = INIT_GFX_RANDOM();
3142 void InitMovingField(int x, int y, int direction)
3144 int element = Feld[x][y];
3145 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3146 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
3149 boolean is_moving_before, is_moving_after;
3151 boolean continues_moving = (WasJustMoving[x][y] && direction == MovDir[x][y]);
3154 /* check if element was/is moving or being moved before/after mode change */
3156 is_moving_before = WasJustMoving[x][y];
3158 is_moving_before = (getElementMoveStepsizeExt(x, y, MovDir[x][y]) != 0);
3160 is_moving_after = (getElementMoveStepsizeExt(x, y, direction) != 0);
3162 /* reset animation only for moving elements which change direction of moving
3163 or which just started or stopped moving
3164 (else CEs with property "can move" / "not moving" are reset each frame) */
3165 #if USE_GFX_RESET_ONLY_WHEN_MOVING
3167 if (is_moving_before != is_moving_after ||
3168 direction != MovDir[x][y])
3169 ResetGfxAnimation(x, y);
3171 if ((is_moving_before || is_moving_after) && !continues_moving)
3172 ResetGfxAnimation(x, y);
3175 if (!continues_moving)
3176 ResetGfxAnimation(x, y);
3179 MovDir[x][y] = direction;
3180 GfxDir[x][y] = direction;
3182 #if USE_GFX_RESET_ONLY_WHEN_MOVING
3183 GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
3184 direction == MV_DOWN && CAN_FALL(element) ?
3185 ACTION_FALLING : ACTION_MOVING);
3187 GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
3188 ACTION_FALLING : ACTION_MOVING);
3191 /* this is needed for CEs with property "can move" / "not moving" */
3193 if (is_moving_after)
3195 if (Feld[newx][newy] == EL_EMPTY)
3196 Feld[newx][newy] = EL_BLOCKED;
3198 MovDir[newx][newy] = MovDir[x][y];
3200 #if USE_NEW_CUSTOM_VALUE
3201 CustomValue[newx][newy] = CustomValue[x][y];
3204 GfxFrame[newx][newy] = GfxFrame[x][y];
3205 GfxRandom[newx][newy] = GfxRandom[x][y];
3206 GfxAction[newx][newy] = GfxAction[x][y];
3207 GfxDir[newx][newy] = GfxDir[x][y];
3211 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
3213 int direction = MovDir[x][y];
3214 int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
3215 int newy = y + (direction & MV_UP ? -1 : direction & MV_DOWN ? +1 : 0);
3221 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
3223 int oldx = x, oldy = y;
3224 int direction = MovDir[x][y];
3226 if (direction == MV_LEFT)
3228 else if (direction == MV_RIGHT)
3230 else if (direction == MV_UP)
3232 else if (direction == MV_DOWN)
3235 *comes_from_x = oldx;
3236 *comes_from_y = oldy;
3239 int MovingOrBlocked2Element(int x, int y)
3241 int element = Feld[x][y];
3243 if (element == EL_BLOCKED)
3247 Blocked2Moving(x, y, &oldx, &oldy);
3248 return Feld[oldx][oldy];
3254 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
3256 /* like MovingOrBlocked2Element(), but if element is moving
3257 and (x,y) is the field the moving element is just leaving,
3258 return EL_BLOCKED instead of the element value */
3259 int element = Feld[x][y];
3261 if (IS_MOVING(x, y))
3263 if (element == EL_BLOCKED)
3267 Blocked2Moving(x, y, &oldx, &oldy);
3268 return Feld[oldx][oldy];
3277 static void RemoveField(int x, int y)
3279 Feld[x][y] = EL_EMPTY;
3285 #if USE_NEW_CUSTOM_VALUE
3286 CustomValue[x][y] = 0;
3290 ChangeDelay[x][y] = 0;
3291 ChangePage[x][y] = -1;
3292 Pushed[x][y] = FALSE;
3295 ExplodeField[x][y] = EX_TYPE_NONE;
3298 GfxElement[x][y] = EL_UNDEFINED;
3299 GfxAction[x][y] = ACTION_DEFAULT;
3300 GfxDir[x][y] = MV_NONE;
3303 void RemoveMovingField(int x, int y)
3305 int oldx = x, oldy = y, newx = x, newy = y;
3306 int element = Feld[x][y];
3307 int next_element = EL_UNDEFINED;
3309 if (element != EL_BLOCKED && !IS_MOVING(x, y))
3312 if (IS_MOVING(x, y))
3314 Moving2Blocked(x, y, &newx, &newy);
3316 if (Feld[newx][newy] != EL_BLOCKED)
3318 /* element is moving, but target field is not free (blocked), but
3319 already occupied by something different (example: acid pool);
3320 in this case, only remove the moving field, but not the target */
3322 RemoveField(oldx, oldy);
3324 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
3326 DrawLevelField(oldx, oldy);
3331 else if (element == EL_BLOCKED)
3333 Blocked2Moving(x, y, &oldx, &oldy);
3334 if (!IS_MOVING(oldx, oldy))
3338 if (element == EL_BLOCKED &&
3339 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
3340 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
3341 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
3342 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
3343 next_element = get_next_element(Feld[oldx][oldy]);
3345 RemoveField(oldx, oldy);
3346 RemoveField(newx, newy);
3348 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
3350 if (next_element != EL_UNDEFINED)
3351 Feld[oldx][oldy] = next_element;
3353 DrawLevelField(oldx, oldy);
3354 DrawLevelField(newx, newy);
3357 void DrawDynamite(int x, int y)
3359 int sx = SCREENX(x), sy = SCREENY(y);
3360 int graphic = el2img(Feld[x][y]);
3363 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
3366 if (IS_WALKABLE_INSIDE(Back[x][y]))
3370 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
3371 else if (Store[x][y])
3372 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
3374 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
3376 if (Back[x][y] || Store[x][y])
3377 DrawGraphicThruMask(sx, sy, graphic, frame);
3379 DrawGraphic(sx, sy, graphic, frame);
3382 void CheckDynamite(int x, int y)
3384 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
3388 if (MovDelay[x][y] != 0)
3391 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
3397 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
3402 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
3404 boolean num_checked_players = 0;
3407 for (i = 0; i < MAX_PLAYERS; i++)
3409 if (stored_player[i].active)
3411 int sx = stored_player[i].jx;
3412 int sy = stored_player[i].jy;
3414 if (num_checked_players == 0)
3421 *sx1 = MIN(*sx1, sx);
3422 *sy1 = MIN(*sy1, sy);
3423 *sx2 = MAX(*sx2, sx);
3424 *sy2 = MAX(*sy2, sy);
3427 num_checked_players++;
3432 static boolean checkIfAllPlayersFitToScreen_RND()
3434 int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
3436 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
3438 return (sx2 - sx1 < SCR_FIELDX &&
3439 sy2 - sy1 < SCR_FIELDY);
3442 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
3444 int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
3446 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
3448 *sx = (sx1 + sx2) / 2;
3449 *sy = (sy1 + sy2) / 2;
3452 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
3453 boolean center_screen, boolean quick_relocation)
3455 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3456 boolean no_delay = (tape.warp_forward);
3457 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
3458 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
3460 if (quick_relocation)
3462 int offset = (setup.scroll_delay ? 3 : 0);
3464 if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
3468 scroll_x = (x < SBX_Left + MIDPOSX ? SBX_Left :
3469 x > SBX_Right + MIDPOSX ? SBX_Right :
3472 scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
3473 y > SBY_Lower + MIDPOSY ? SBY_Lower :
3478 /* quick relocation (without scrolling), but do not center screen */
3480 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
3481 old_x > SBX_Right + MIDPOSX ? SBX_Right :
3484 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
3485 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
3488 int offset_x = x + (scroll_x - center_scroll_x);
3489 int offset_y = y + (scroll_y - center_scroll_y);
3491 scroll_x = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
3492 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
3493 offset_x - MIDPOSX);
3495 scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
3496 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
3497 offset_y - MIDPOSY);
3502 if ((move_dir == MV_LEFT && scroll_x > x - MIDPOSX + offset) ||
3503 (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
3504 scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
3506 if ((move_dir == MV_UP && scroll_y > y - MIDPOSY + offset) ||
3507 (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
3508 scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
3510 /* don't scroll over playfield boundaries */
3511 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
3512 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
3514 /* don't scroll over playfield boundaries */
3515 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
3516 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
3519 RedrawPlayfield(TRUE, 0,0,0,0);
3523 int scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left :
3524 x > SBX_Right + MIDPOSX ? SBX_Right :
3527 int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
3528 y > SBY_Lower + MIDPOSY ? SBY_Lower :
3531 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
3533 while (scroll_x != scroll_xx || scroll_y != scroll_yy)
3536 int fx = FX, fy = FY;
3538 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
3539 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
3541 if (dx == 0 && dy == 0) /* no scrolling needed at all */
3547 fx += dx * TILEX / 2;
3548 fy += dy * TILEY / 2;
3550 ScrollLevel(dx, dy);
3553 /* scroll in two steps of half tile size to make things smoother */
3554 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
3556 Delay(wait_delay_value);
3558 /* scroll second step to align at full tile size */
3560 Delay(wait_delay_value);
3565 Delay(wait_delay_value);
3569 void RelocatePlayer(int jx, int jy, int el_player_raw)
3571 int el_player = GET_PLAYER_ELEMENT(el_player_raw);
3572 int player_nr = GET_PLAYER_NR(el_player);
3573 struct PlayerInfo *player = &stored_player[player_nr];
3574 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3575 boolean no_delay = (tape.warp_forward);
3576 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
3577 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
3578 int old_jx = player->jx;
3579 int old_jy = player->jy;
3580 int old_element = Feld[old_jx][old_jy];
3581 int element = Feld[jx][jy];
3582 boolean player_relocated = (old_jx != jx || old_jy != jy);
3584 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
3585 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
3586 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
3587 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
3588 int leave_side_horiz = move_dir_horiz;
3589 int leave_side_vert = move_dir_vert;
3590 int enter_side = enter_side_horiz | enter_side_vert;
3591 int leave_side = leave_side_horiz | leave_side_vert;
3593 if (player->GameOver) /* do not reanimate dead player */
3596 if (!player_relocated) /* no need to relocate the player */
3599 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
3601 RemoveField(jx, jy); /* temporarily remove newly placed player */
3602 DrawLevelField(jx, jy);
3605 if (player->present)
3607 while (player->MovPos)
3609 ScrollPlayer(player, SCROLL_GO_ON);
3610 ScrollScreen(NULL, SCROLL_GO_ON);
3612 AdvanceFrameAndPlayerCounters(player->index_nr);
3617 Delay(wait_delay_value);
3620 DrawPlayer(player); /* needed here only to cleanup last field */
3621 DrawLevelField(player->jx, player->jy); /* remove player graphic */
3623 player->is_moving = FALSE;
3626 if (IS_CUSTOM_ELEMENT(old_element))
3627 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
3629 player->index_bit, leave_side);
3631 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
3633 player->index_bit, leave_side);
3635 Feld[jx][jy] = el_player;
3636 InitPlayerField(jx, jy, el_player, TRUE);
3638 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
3640 Feld[jx][jy] = element;
3641 InitField(jx, jy, FALSE);
3644 /* only visually relocate centered player */
3645 DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
3646 FALSE, level.instant_relocation);
3648 TestIfPlayerTouchesBadThing(jx, jy);
3649 TestIfPlayerTouchesCustomElement(jx, jy);
3651 if (IS_CUSTOM_ELEMENT(element))
3652 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
3653 player->index_bit, enter_side);
3655 CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
3656 player->index_bit, enter_side);
3659 void Explode(int ex, int ey, int phase, int mode)
3665 /* !!! eliminate this variable !!! */
3666 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
3668 if (game.explosions_delayed)
3670 ExplodeField[ex][ey] = mode;
3674 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
3676 int center_element = Feld[ex][ey];
3677 int artwork_element, explosion_element; /* set these values later */
3680 /* --- This is only really needed (and now handled) in "Impact()". --- */
3681 /* do not explode moving elements that left the explode field in time */
3682 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
3683 center_element == EL_EMPTY &&
3684 (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
3689 /* !!! at this place, the center element may be EL_BLOCKED !!! */
3690 if (mode == EX_TYPE_NORMAL ||
3691 mode == EX_TYPE_CENTER ||
3692 mode == EX_TYPE_CROSS)
3693 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
3696 /* remove things displayed in background while burning dynamite */
3697 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
3700 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
3702 /* put moving element to center field (and let it explode there) */
3703 center_element = MovingOrBlocked2Element(ex, ey);
3704 RemoveMovingField(ex, ey);
3705 Feld[ex][ey] = center_element;
3708 /* now "center_element" is finally determined -- set related values now */
3709 artwork_element = center_element; /* for custom player artwork */
3710 explosion_element = center_element; /* for custom player artwork */
3712 if (IS_PLAYER(ex, ey))
3714 int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
3716 artwork_element = stored_player[player_nr].artwork_element;
3718 if (level.use_explosion_element[player_nr])
3720 explosion_element = level.explosion_element[player_nr];
3721 artwork_element = explosion_element;
3726 if (mode == EX_TYPE_NORMAL ||
3727 mode == EX_TYPE_CENTER ||
3728 mode == EX_TYPE_CROSS)
3729 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
3732 last_phase = element_info[explosion_element].explosion_delay + 1;
3734 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
3736 int xx = x - ex + 1;
3737 int yy = y - ey + 1;
3740 if (!IN_LEV_FIELD(x, y) ||
3741 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
3742 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
3745 element = Feld[x][y];
3747 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
3749 element = MovingOrBlocked2Element(x, y);
3751 if (!IS_EXPLOSION_PROOF(element))
3752 RemoveMovingField(x, y);
3755 /* indestructible elements can only explode in center (but not flames) */
3756 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
3757 mode == EX_TYPE_BORDER)) ||
3758 element == EL_FLAMES)
3761 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
3762 behaviour, for example when touching a yamyam that explodes to rocks
3763 with active deadly shield, a rock is created under the player !!! */
3764 /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
3766 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
3767 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
3768 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
3770 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
3773 if (IS_ACTIVE_BOMB(element))
3775 /* re-activate things under the bomb like gate or penguin */
3776 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
3783 /* save walkable background elements while explosion on same tile */
3784 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
3785 (x != ex || y != ey || mode == EX_TYPE_BORDER))
3786 Back[x][y] = element;
3788 /* ignite explodable elements reached by other explosion */
3789 if (element == EL_EXPLOSION)
3790 element = Store2[x][y];
3792 if (AmoebaNr[x][y] &&
3793 (element == EL_AMOEBA_FULL ||
3794 element == EL_BD_AMOEBA ||
3795 element == EL_AMOEBA_GROWING))
3797 AmoebaCnt[AmoebaNr[x][y]]--;
3798 AmoebaCnt2[AmoebaNr[x][y]]--;
3803 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
3805 int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
3807 Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
3809 if (PLAYERINFO(ex, ey)->use_murphy)
3810 Store[x][y] = EL_EMPTY;
3813 /* !!! check this case -- currently needed for rnd_rado_negundo_v,
3814 !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
3815 else if (ELEM_IS_PLAYER(center_element))
3816 Store[x][y] = EL_EMPTY;
3817 else if (center_element == EL_YAMYAM)
3818 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
3819 else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
3820 Store[x][y] = element_info[center_element].content.e[xx][yy];
3822 /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
3823 (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
3824 otherwise) -- FIX THIS !!! */
3825 else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
3826 Store[x][y] = element_info[element].content.e[1][1];
3828 else if (!CAN_EXPLODE(element))
3829 Store[x][y] = element_info[element].content.e[1][1];
3832 Store[x][y] = EL_EMPTY;
3834 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
3835 center_element == EL_AMOEBA_TO_DIAMOND)
3836 Store2[x][y] = element;
3838 Feld[x][y] = EL_EXPLOSION;
3839 GfxElement[x][y] = artwork_element;
3841 ExplodePhase[x][y] = 1;
3842 ExplodeDelay[x][y] = last_phase;
3847 if (center_element == EL_YAMYAM)
3848 game.yamyam_content_nr =
3849 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
3861 GfxFrame[x][y] = 0; /* restart explosion animation */
3863 last_phase = ExplodeDelay[x][y];
3865 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
3869 /* activate this even in non-DEBUG version until cause for crash in
3870 getGraphicAnimationFrame() (see below) is found and eliminated */
3876 /* this can happen if the player leaves an explosion just in time */
3877 if (GfxElement[x][y] == EL_UNDEFINED)
3878 GfxElement[x][y] = EL_EMPTY;
3880 if (GfxElement[x][y] == EL_UNDEFINED)
3883 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
3884 printf("Explode(): This should never happen!\n");
3887 GfxElement[x][y] = EL_EMPTY;
3893 border_element = Store2[x][y];
3894 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3895 border_element = StorePlayer[x][y];
3897 if (phase == element_info[border_element].ignition_delay ||
3898 phase == last_phase)
3900 boolean border_explosion = FALSE;
3902 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
3903 !PLAYER_EXPLOSION_PROTECTED(x, y))
3905 KillPlayerUnlessExplosionProtected(x, y);
3906 border_explosion = TRUE;
3908 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
3910 Feld[x][y] = Store2[x][y];
3913 border_explosion = TRUE;
3915 else if (border_element == EL_AMOEBA_TO_DIAMOND)
3917 AmoebeUmwandeln(x, y);
3919 border_explosion = TRUE;
3922 /* if an element just explodes due to another explosion (chain-reaction),
3923 do not immediately end the new explosion when it was the last frame of
3924 the explosion (as it would be done in the following "if"-statement!) */
3925 if (border_explosion && phase == last_phase)
3929 if (phase == last_phase)
3933 element = Feld[x][y] = Store[x][y];
3934 Store[x][y] = Store2[x][y] = 0;
3935 GfxElement[x][y] = EL_UNDEFINED;
3937 /* player can escape from explosions and might therefore be still alive */
3938 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
3939 element <= EL_PLAYER_IS_EXPLODING_4)
3941 int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
3942 int explosion_element = EL_PLAYER_1 + player_nr;
3943 int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
3944 int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
3946 if (level.use_explosion_element[player_nr])
3947 explosion_element = level.explosion_element[player_nr];
3949 Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
3950 element_info[explosion_element].content.e[xx][yy]);
3953 /* restore probably existing indestructible background element */
3954 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
3955 element = Feld[x][y] = Back[x][y];
3958 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
3959 GfxDir[x][y] = MV_NONE;
3960 ChangeDelay[x][y] = 0;
3961 ChangePage[x][y] = -1;
3963 #if USE_NEW_CUSTOM_VALUE
3964 CustomValue[x][y] = 0;
3967 InitField_WithBug2(x, y, FALSE);
3969 DrawLevelField(x, y);
3971 TestIfElementTouchesCustomElement(x, y);
3973 if (GFX_CRUMBLED(element))
3974 DrawLevelFieldCrumbledSandNeighbours(x, y);
3976 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
3977 StorePlayer[x][y] = 0;
3979 if (ELEM_IS_PLAYER(element))
3980 RelocatePlayer(x, y, element);
3982 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3984 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
3985 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
3988 DrawLevelFieldCrumbledSand(x, y);
3990 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
3992 DrawLevelElement(x, y, Back[x][y]);
3993 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
3995 else if (IS_WALKABLE_UNDER(Back[x][y]))
3997 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3998 DrawLevelElementThruMask(x, y, Back[x][y]);
4000 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
4001 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
4005 void DynaExplode(int ex, int ey)
4008 int dynabomb_element = Feld[ex][ey];
4009 int dynabomb_size = 1;
4010 boolean dynabomb_xl = FALSE;
4011 struct PlayerInfo *player;
4012 static int xy[4][2] =
4020 if (IS_ACTIVE_BOMB(dynabomb_element))
4022 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
4023 dynabomb_size = player->dynabomb_size;
4024 dynabomb_xl = player->dynabomb_xl;
4025 player->dynabombs_left++;
4028 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
4030 for (i = 0; i < NUM_DIRECTIONS; i++)
4032 for (j = 1; j <= dynabomb_size; j++)
4034 int x = ex + j * xy[i][0];
4035 int y = ey + j * xy[i][1];
4038 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
4041 element = Feld[x][y];
4043 /* do not restart explosions of fields with active bombs */
4044 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
4047 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
4049 if (element != EL_EMPTY && element != EL_EXPLOSION &&
4050 !IS_DIGGABLE(element) && !dynabomb_xl)
4056 void Bang(int x, int y)
4058 int element = MovingOrBlocked2Element(x, y);
4059 int explosion_type = EX_TYPE_NORMAL;
4061 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
4063 struct PlayerInfo *player = PLAYERINFO(x, y);
4065 element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
4066 player->element_nr);
4068 if (level.use_explosion_element[player->index_nr])
4070 int explosion_element = level.explosion_element[player->index_nr];
4072 if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
4073 explosion_type = EX_TYPE_CROSS;
4074 else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
4075 explosion_type = EX_TYPE_CENTER;
4083 case EL_BD_BUTTERFLY:
4086 case EL_DARK_YAMYAM:
4090 RaiseScoreElement(element);
4093 case EL_DYNABOMB_PLAYER_1_ACTIVE:
4094 case EL_DYNABOMB_PLAYER_2_ACTIVE:
4095 case EL_DYNABOMB_PLAYER_3_ACTIVE:
4096 case EL_DYNABOMB_PLAYER_4_ACTIVE:
4097 case EL_DYNABOMB_INCREASE_NUMBER:
4098 case EL_DYNABOMB_INCREASE_SIZE:
4099 case EL_DYNABOMB_INCREASE_POWER:
4100 explosion_type = EX_TYPE_DYNA;
4103 case EL_DC_LANDMINE:
4105 case EL_EM_EXIT_OPEN:
4106 case EL_EM_STEEL_EXIT_OPEN:
4108 explosion_type = EX_TYPE_CENTER;
4113 case EL_LAMP_ACTIVE:
4114 case EL_AMOEBA_TO_DIAMOND:
4115 if (!IS_PLAYER(x, y)) /* penguin and player may be at same field */
4116 explosion_type = EX_TYPE_CENTER;
4120 if (element_info[element].explosion_type == EXPLODES_CROSS)
4121 explosion_type = EX_TYPE_CROSS;
4122 else if (element_info[element].explosion_type == EXPLODES_1X1)
4123 explosion_type = EX_TYPE_CENTER;
4127 if (explosion_type == EX_TYPE_DYNA)
4130 Explode(x, y, EX_PHASE_START, explosion_type);
4132 CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
4135 void SplashAcid(int x, int y)
4137 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
4138 (!IN_LEV_FIELD(x - 1, y - 2) ||
4139 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
4140 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
4142 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
4143 (!IN_LEV_FIELD(x + 1, y - 2) ||
4144 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
4145 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
4147 PlayLevelSound(x, y, SND_ACID_SPLASHING);
4150 static void InitBeltMovement()
4152 static int belt_base_element[4] =
4154 EL_CONVEYOR_BELT_1_LEFT,
4155 EL_CONVEYOR_BELT_2_LEFT,
4156 EL_CONVEYOR_BELT_3_LEFT,
4157 EL_CONVEYOR_BELT_4_LEFT
4159 static int belt_base_active_element[4] =
4161 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
4162 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
4163 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
4164 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
4169 /* set frame order for belt animation graphic according to belt direction */
4170 for (i = 0; i < NUM_BELTS; i++)
4174 for (j = 0; j < NUM_BELT_PARTS; j++)
4176 int element = belt_base_active_element[belt_nr] + j;
4177 int graphic = el2img(element);
4179 if (game.belt_dir[i] == MV_LEFT)
4180 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
4182 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
4186 SCAN_PLAYFIELD(x, y)
4188 int element = Feld[x][y];
4190 for (i = 0; i < NUM_BELTS; i++)
4192 if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
4194 int e_belt_nr = getBeltNrFromBeltElement(element);
4197 if (e_belt_nr == belt_nr)
4199 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
4201 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
4208 static void ToggleBeltSwitch(int x, int y)
4210 static int belt_base_element[4] =
4212 EL_CONVEYOR_BELT_1_LEFT,
4213 EL_CONVEYOR_BELT_2_LEFT,
4214 EL_CONVEYOR_BELT_3_LEFT,
4215 EL_CONVEYOR_BELT_4_LEFT
4217 static int belt_base_active_element[4] =
4219 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
4220 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
4221 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
4222 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
4224 static int belt_base_switch_element[4] =
4226 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
4227 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
4228 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
4229 EL_CONVEYOR_BELT_4_SWITCH_LEFT
4231 static int belt_move_dir[4] =
4239 int element = Feld[x][y];
4240 int belt_nr = getBeltNrFromBeltSwitchElement(element);
4241 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
4242 int belt_dir = belt_move_dir[belt_dir_nr];
4245 if (!IS_BELT_SWITCH(element))
4248 game.belt_dir_nr[belt_nr] = belt_dir_nr;
4249 game.belt_dir[belt_nr] = belt_dir;
4251 if (belt_dir_nr == 3)
4254 /* set frame order for belt animation graphic according to belt direction */
4255 for (i = 0; i < NUM_BELT_PARTS; i++)
4257 int element = belt_base_active_element[belt_nr] + i;
4258 int graphic = el2img(element);
4260 if (belt_dir == MV_LEFT)
4261 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
4263 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
4266 SCAN_PLAYFIELD(xx, yy)
4268 int element = Feld[xx][yy];
4270 if (IS_BELT_SWITCH(element))
4272 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
4274 if (e_belt_nr == belt_nr)
4276 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
4277 DrawLevelField(xx, yy);
4280 else if (IS_BELT(element) && belt_dir != MV_NONE)
4282 int e_belt_nr = getBeltNrFromBeltElement(element);
4284 if (e_belt_nr == belt_nr)
4286 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
4288 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
4289 DrawLevelField(xx, yy);
4292 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
4294 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
4296 if (e_belt_nr == belt_nr)
4298 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
4300 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
4301 DrawLevelField(xx, yy);
4307 static void ToggleSwitchgateSwitch(int x, int y)
4311 game.switchgate_pos = !game.switchgate_pos;
4313 SCAN_PLAYFIELD(xx, yy)
4315 int element = Feld[xx][yy];
4317 #if !USE_BOTH_SWITCHGATE_SWITCHES
4318 if (element == EL_SWITCHGATE_SWITCH_UP ||
4319 element == EL_SWITCHGATE_SWITCH_DOWN)
4321 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
4322 DrawLevelField(xx, yy);
4324 else if (element == EL_DC_SWITCHGATE_SWITCH_UP ||
4325 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
4327 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
4328 DrawLevelField(xx, yy);
4331 if (element == EL_SWITCHGATE_SWITCH_UP)
4333 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
4334 DrawLevelField(xx, yy);
4336 else if (element == EL_SWITCHGATE_SWITCH_DOWN)
4338 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
4339 DrawLevelField(xx, yy);
4341 else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
4343 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
4344 DrawLevelField(xx, yy);
4346 else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
4348 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
4349 DrawLevelField(xx, yy);
4352 else if (element == EL_SWITCHGATE_OPEN ||
4353 element == EL_SWITCHGATE_OPENING)
4355 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
4357 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
4359 else if (element == EL_SWITCHGATE_CLOSED ||
4360 element == EL_SWITCHGATE_CLOSING)
4362 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
4364 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
4369 static int getInvisibleActiveFromInvisibleElement(int element)
4371 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
4372 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
4373 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
4377 static int getInvisibleFromInvisibleActiveElement(int element)
4379 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
4380 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
4381 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
4385 static void RedrawAllLightSwitchesAndInvisibleElements()
4389 SCAN_PLAYFIELD(x, y)
4391 int element = Feld[x][y];
4393 if (element == EL_LIGHT_SWITCH &&
4394 game.light_time_left > 0)
4396 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
4397 DrawLevelField(x, y);
4399 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
4400 game.light_time_left == 0)
4402 Feld[x][y] = EL_LIGHT_SWITCH;
4403 DrawLevelField(x, y);
4405 else if (element == EL_EMC_DRIPPER &&
4406 game.light_time_left > 0)
4408 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
4409 DrawLevelField(x, y);
4411 else if (element == EL_EMC_DRIPPER_ACTIVE &&
4412 game.light_time_left == 0)
4414 Feld[x][y] = EL_EMC_DRIPPER;
4415 DrawLevelField(x, y);
4417 else if (element == EL_INVISIBLE_STEELWALL ||
4418 element == EL_INVISIBLE_WALL ||
4419 element == EL_INVISIBLE_SAND)
4421 if (game.light_time_left > 0)
4422 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
4424 DrawLevelField(x, y);
4426 /* uncrumble neighbour fields, if needed */
4427 if (element == EL_INVISIBLE_SAND)
4428 DrawLevelFieldCrumbledSandNeighbours(x, y);
4430 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
4431 element == EL_INVISIBLE_WALL_ACTIVE ||
4432 element == EL_INVISIBLE_SAND_ACTIVE)
4434 if (game.light_time_left == 0)
4435 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
4437 DrawLevelField(x, y);
4439 /* re-crumble neighbour fields, if needed */
4440 if (element == EL_INVISIBLE_SAND)
4441 DrawLevelFieldCrumbledSandNeighbours(x, y);
4446 static void RedrawAllInvisibleElementsForLenses()
4450 SCAN_PLAYFIELD(x, y)
4452 int element = Feld[x][y];
4454 if (element == EL_EMC_DRIPPER &&
4455 game.lenses_time_left > 0)
4457 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
4458 DrawLevelField(x, y);
4460 else if (element == EL_EMC_DRIPPER_ACTIVE &&
4461 game.lenses_time_left == 0)
4463 Feld[x][y] = EL_EMC_DRIPPER;
4464 DrawLevelField(x, y);
4466 else if (element == EL_INVISIBLE_STEELWALL ||
4467 element == EL_INVISIBLE_WALL ||
4468 element == EL_INVISIBLE_SAND)
4470 if (game.lenses_time_left > 0)
4471 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
4473 DrawLevelField(x, y);
4475 /* uncrumble neighbour fields, if needed */
4476 if (element == EL_INVISIBLE_SAND)
4477 DrawLevelFieldCrumbledSandNeighbours(x, y);
4479 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
4480 element == EL_INVISIBLE_WALL_ACTIVE ||
4481 element == EL_INVISIBLE_SAND_ACTIVE)
4483 if (game.lenses_time_left == 0)
4484 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
4486 DrawLevelField(x, y);
4488 /* re-crumble neighbour fields, if needed */
4489 if (element == EL_INVISIBLE_SAND)
4490 DrawLevelFieldCrumbledSandNeighbours(x, y);
4495 static void RedrawAllInvisibleElementsForMagnifier()
4499 SCAN_PLAYFIELD(x, y)
4501 int element = Feld[x][y];
4503 if (element == EL_EMC_FAKE_GRASS &&
4504 game.magnify_time_left > 0)
4506 Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
4507 DrawLevelField(x, y);
4509 else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
4510 game.magnify_time_left == 0)
4512 Feld[x][y] = EL_EMC_FAKE_GRASS;
4513 DrawLevelField(x, y);
4515 else if (IS_GATE_GRAY(element) &&
4516 game.magnify_time_left > 0)
4518 Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
4519 element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
4520 IS_EM_GATE_GRAY(element) ?
4521 element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
4522 IS_EMC_GATE_GRAY(element) ?
4523 element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
4525 DrawLevelField(x, y);
4527 else if (IS_GATE_GRAY_ACTIVE(element) &&
4528 game.magnify_time_left == 0)
4530 Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
4531 element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
4532 IS_EM_GATE_GRAY_ACTIVE(element) ?
4533 element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
4534 IS_EMC_GATE_GRAY_ACTIVE(element) ?
4535 element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
4537 DrawLevelField(x, y);
4542 static void ToggleLightSwitch(int x, int y)
4544 int element = Feld[x][y];
4546 game.light_time_left =
4547 (element == EL_LIGHT_SWITCH ?
4548 level.time_light * FRAMES_PER_SECOND : 0);
4550 RedrawAllLightSwitchesAndInvisibleElements();
4553 static void ActivateTimegateSwitch(int x, int y)
4557 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
4559 SCAN_PLAYFIELD(xx, yy)
4561 int element = Feld[xx][yy];
4563 if (element == EL_TIMEGATE_CLOSED ||
4564 element == EL_TIMEGATE_CLOSING)
4566 Feld[xx][yy] = EL_TIMEGATE_OPENING;
4567 PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
4571 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
4573 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
4574 DrawLevelField(xx, yy);
4581 Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
4582 EL_DC_TIMEGATE_SWITCH_ACTIVE);
4584 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
4588 void Impact(int x, int y)
4590 boolean last_line = (y == lev_fieldy - 1);
4591 boolean object_hit = FALSE;
4592 boolean impact = (last_line || object_hit);
4593 int element = Feld[x][y];
4594 int smashed = EL_STEELWALL;
4596 if (!last_line) /* check if element below was hit */
4598 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
4601 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
4602 MovDir[x][y + 1] != MV_DOWN ||
4603 MovPos[x][y + 1] <= TILEY / 2));
4605 /* do not smash moving elements that left the smashed field in time */
4606 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
4607 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
4610 #if USE_QUICKSAND_IMPACT_BUGFIX
4611 if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
4613 RemoveMovingField(x, y + 1);
4614 Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
4615 Feld[x][y + 2] = EL_ROCK;
4616 DrawLevelField(x, y + 2);
4623 smashed = MovingOrBlocked2Element(x, y + 1);
4625 impact = (last_line || object_hit);
4628 if (!last_line && smashed == EL_ACID) /* element falls into acid */
4630 SplashAcid(x, y + 1);
4634 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
4635 /* only reset graphic animation if graphic really changes after impact */
4637 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
4639 ResetGfxAnimation(x, y);
4640 DrawLevelField(x, y);
4643 if (impact && CAN_EXPLODE_IMPACT(element))
4648 else if (impact && element == EL_PEARL)
4650 ResetGfxAnimation(x, y);
4652 Feld[x][y] = EL_PEARL_BREAKING;
4653 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4656 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
4658 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4663 if (impact && element == EL_AMOEBA_DROP)
4665 if (object_hit && IS_PLAYER(x, y + 1))
4666 KillPlayerUnlessEnemyProtected(x, y + 1);
4667 else if (object_hit && smashed == EL_PENGUIN)
4671 Feld[x][y] = EL_AMOEBA_GROWING;
4672 Store[x][y] = EL_AMOEBA_WET;
4674 ResetRandomAnimationValue(x, y);
4679 if (object_hit) /* check which object was hit */
4681 if (CAN_PASS_MAGIC_WALL(element) &&
4682 (smashed == EL_MAGIC_WALL ||
4683 smashed == EL_BD_MAGIC_WALL))
4686 int activated_magic_wall =
4687 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
4688 EL_BD_MAGIC_WALL_ACTIVE);
4690 /* activate magic wall / mill */
4691 SCAN_PLAYFIELD(xx, yy)
4692 if (Feld[xx][yy] == smashed)
4693 Feld[xx][yy] = activated_magic_wall;
4695 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
4696 game.magic_wall_active = TRUE;
4698 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
4699 SND_MAGIC_WALL_ACTIVATING :
4700 SND_BD_MAGIC_WALL_ACTIVATING));
4703 if (IS_PLAYER(x, y + 1))
4705 if (CAN_SMASH_PLAYER(element))
4707 KillPlayerUnlessEnemyProtected(x, y + 1);
4711 else if (smashed == EL_PENGUIN)
4713 if (CAN_SMASH_PLAYER(element))
4719 else if (element == EL_BD_DIAMOND)
4721 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
4727 else if (((element == EL_SP_INFOTRON ||
4728 element == EL_SP_ZONK) &&
4729 (smashed == EL_SP_SNIKSNAK ||
4730 smashed == EL_SP_ELECTRON ||
4731 smashed == EL_SP_DISK_ORANGE)) ||
4732 (element == EL_SP_INFOTRON &&
4733 smashed == EL_SP_DISK_YELLOW))
4738 else if (CAN_SMASH_EVERYTHING(element))
4740 if (IS_CLASSIC_ENEMY(smashed) ||
4741 CAN_EXPLODE_SMASHED(smashed))
4746 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
4748 if (smashed == EL_LAMP ||
4749 smashed == EL_LAMP_ACTIVE)
4754 else if (smashed == EL_NUT)
4756 Feld[x][y + 1] = EL_NUT_BREAKING;
4757 PlayLevelSound(x, y, SND_NUT_BREAKING);
4758 RaiseScoreElement(EL_NUT);
4761 else if (smashed == EL_PEARL)
4763 ResetGfxAnimation(x, y);
4765 Feld[x][y + 1] = EL_PEARL_BREAKING;
4766 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4769 else if (smashed == EL_DIAMOND)
4771 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
4772 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
4775 else if (IS_BELT_SWITCH(smashed))
4777 ToggleBeltSwitch(x, y + 1);
4779 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
4780 smashed == EL_SWITCHGATE_SWITCH_DOWN ||
4781 smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
4782 smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
4784 ToggleSwitchgateSwitch(x, y + 1);
4786 else if (smashed == EL_LIGHT_SWITCH ||
4787 smashed == EL_LIGHT_SWITCH_ACTIVE)
4789 ToggleLightSwitch(x, y + 1);
4794 TestIfElementSmashesCustomElement(x, y, MV_DOWN);
4797 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4799 CheckElementChangeBySide(x, y + 1, smashed, element,
4800 CE_SWITCHED, CH_SIDE_TOP);
4801 CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
4807 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4812 /* play sound of magic wall / mill */
4814 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
4815 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
4817 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
4818 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
4819 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
4820 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
4825 /* play sound of object that hits the ground */
4826 if (last_line || object_hit)
4827 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4830 inline static void TurnRoundExt(int x, int y)
4842 { 0, 0 }, { 0, 0 }, { 0, 0 },
4847 int left, right, back;
4851 { MV_DOWN, MV_UP, MV_RIGHT },
4852 { MV_UP, MV_DOWN, MV_LEFT },
4854 { MV_LEFT, MV_RIGHT, MV_DOWN },
4858 { MV_RIGHT, MV_LEFT, MV_UP }
4861 int element = Feld[x][y];
4862 int move_pattern = element_info[element].move_pattern;
4864 int old_move_dir = MovDir[x][y];
4865 int left_dir = turn[old_move_dir].left;
4866 int right_dir = turn[old_move_dir].right;
4867 int back_dir = turn[old_move_dir].back;
4869 int left_dx = move_xy[left_dir].dx, left_dy = move_xy[left_dir].dy;
4870 int right_dx = move_xy[right_dir].dx, right_dy = move_xy[right_dir].dy;
4871 int move_dx = move_xy[old_move_dir].dx, move_dy = move_xy[old_move_dir].dy;
4872 int back_dx = move_xy[back_dir].dx, back_dy = move_xy[back_dir].dy;
4874 int left_x = x + left_dx, left_y = y + left_dy;
4875 int right_x = x + right_dx, right_y = y + right_dy;
4876 int move_x = x + move_dx, move_y = y + move_dy;
4880 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4882 TestIfBadThingTouchesOtherBadThing(x, y);
4884 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
4885 MovDir[x][y] = right_dir;
4886 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4887 MovDir[x][y] = left_dir;
4889 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
4891 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
4894 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
4896 TestIfBadThingTouchesOtherBadThing(x, y);
4898 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
4899 MovDir[x][y] = left_dir;
4900 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4901 MovDir[x][y] = right_dir;
4903 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
4905 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
4908 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4910 TestIfBadThingTouchesOtherBadThing(x, y);
4912 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
4913 MovDir[x][y] = left_dir;
4914 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
4915 MovDir[x][y] = right_dir;
4917 if (MovDir[x][y] != old_move_dir)
4920 else if (element == EL_YAMYAM)
4922 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
4923 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
4925 if (can_turn_left && can_turn_right)
4926 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4927 else if (can_turn_left)
4928 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4929 else if (can_turn_right)
4930 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4932 MovDir[x][y] = back_dir;
4934 MovDelay[x][y] = 16 + 16 * RND(3);
4936 else if (element == EL_DARK_YAMYAM)
4938 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4940 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4943 if (can_turn_left && can_turn_right)
4944 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4945 else if (can_turn_left)
4946 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4947 else if (can_turn_right)
4948 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4950 MovDir[x][y] = back_dir;
4952 MovDelay[x][y] = 16 + 16 * RND(3);
4954 else if (element == EL_PACMAN)
4956 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
4957 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
4959 if (can_turn_left && can_turn_right)
4960 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4961 else if (can_turn_left)
4962 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4963 else if (can_turn_right)
4964 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4966 MovDir[x][y] = back_dir;
4968 MovDelay[x][y] = 6 + RND(40);
4970 else if (element == EL_PIG)
4972 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
4973 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
4974 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
4975 boolean should_turn_left, should_turn_right, should_move_on;
4977 int rnd = RND(rnd_value);
4979 should_turn_left = (can_turn_left &&
4981 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
4982 y + back_dy + left_dy)));
4983 should_turn_right = (can_turn_right &&
4985 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
4986 y + back_dy + right_dy)));
4987 should_move_on = (can_move_on &&
4990 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
4991 y + move_dy + left_dy) ||
4992 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
4993 y + move_dy + right_dy)));
4995 if (should_turn_left || should_turn_right || should_move_on)
4997 if (should_turn_left && should_turn_right && should_move_on)
4998 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
4999 rnd < 2 * rnd_value / 3 ? right_dir :
5001 else if (should_turn_left && should_turn_right)
5002 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
5003 else if (should_turn_left && should_move_on)
5004 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
5005 else if (should_turn_right && should_move_on)
5006 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
5007 else if (should_turn_left)
5008 MovDir[x][y] = left_dir;
5009 else if (should_turn_right)
5010 MovDir[x][y] = right_dir;
5011 else if (should_move_on)
5012 MovDir[x][y] = old_move_dir;
5014 else if (can_move_on && rnd > rnd_value / 8)
5015 MovDir[x][y] = old_move_dir;
5016 else if (can_turn_left && can_turn_right)
5017 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
5018 else if (can_turn_left && rnd > rnd_value / 8)
5019 MovDir[x][y] = left_dir;
5020 else if (can_turn_right && rnd > rnd_value/8)
5021 MovDir[x][y] = right_dir;
5023 MovDir[x][y] = back_dir;
5025 xx = x + move_xy[MovDir[x][y]].dx;
5026 yy = y + move_xy[MovDir[x][y]].dy;
5028 if (!IN_LEV_FIELD(xx, yy) ||
5029 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
5030 MovDir[x][y] = old_move_dir;
5034 else if (element == EL_DRAGON)
5036 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
5037 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
5038 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
5040 int rnd = RND(rnd_value);
5042 if (can_move_on && rnd > rnd_value / 8)
5043 MovDir[x][y] = old_move_dir;
5044 else if (can_turn_left && can_turn_right)
5045 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
5046 else if (can_turn_left && rnd > rnd_value / 8)
5047 MovDir[x][y] = left_dir;
5048 else if (can_turn_right && rnd > rnd_value / 8)
5049 MovDir[x][y] = right_dir;
5051 MovDir[x][y] = back_dir;
5053 xx = x + move_xy[MovDir[x][y]].dx;
5054 yy = y + move_xy[MovDir[x][y]].dy;
5056 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
5057 MovDir[x][y] = old_move_dir;
5061 else if (element == EL_MOLE)
5063 boolean can_move_on =
5064 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
5065 IS_AMOEBOID(Feld[move_x][move_y]) ||
5066 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
5069 boolean can_turn_left =
5070 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
5071 IS_AMOEBOID(Feld[left_x][left_y])));
5073 boolean can_turn_right =
5074 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
5075 IS_AMOEBOID(Feld[right_x][right_y])));
5077 if (can_turn_left && can_turn_right)
5078 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
5079 else if (can_turn_left)
5080 MovDir[x][y] = left_dir;
5082 MovDir[x][y] = right_dir;
5085 if (MovDir[x][y] != old_move_dir)
5088 else if (element == EL_BALLOON)
5090 MovDir[x][y] = game.wind_direction;
5093 else if (element == EL_SPRING)
5095 #if USE_NEW_SPRING_BUMPER
5096 if (MovDir[x][y] & MV_HORIZONTAL)
5098 if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
5099 !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
5101 Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
5102 ResetGfxAnimation(move_x, move_y);
5103 DrawLevelField(move_x, move_y);
5105 MovDir[x][y] = back_dir;
5107 else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
5108 SPRING_CAN_ENTER_FIELD(element, x, y + 1))
5109 MovDir[x][y] = MV_NONE;
5112 if (MovDir[x][y] & MV_HORIZONTAL &&
5113 (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
5114 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
5115 MovDir[x][y] = MV_NONE;
5120 else if (element == EL_ROBOT ||
5121 element == EL_SATELLITE ||
5122 element == EL_PENGUIN ||
5123 element == EL_EMC_ANDROID)
5125 int attr_x = -1, attr_y = -1;
5136 for (i = 0; i < MAX_PLAYERS; i++)
5138 struct PlayerInfo *player = &stored_player[i];
5139 int jx = player->jx, jy = player->jy;
5141 if (!player->active)
5145 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
5153 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
5154 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
5155 game.engine_version < VERSION_IDENT(3,1,0,0)))
5161 if (element == EL_PENGUIN)
5164 static int xy[4][2] =
5172 for (i = 0; i < NUM_DIRECTIONS; i++)
5174 int ex = x + xy[i][0];
5175 int ey = y + xy[i][1];
5177 if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
5178 Feld[ex][ey] == EL_EM_EXIT_OPEN ||
5179 Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
5180 Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
5189 MovDir[x][y] = MV_NONE;
5191 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
5192 else if (attr_x > x)
5193 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
5195 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
5196 else if (attr_y > y)
5197 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
5199 if (element == EL_ROBOT)
5203 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5204 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
5205 Moving2Blocked(x, y, &newx, &newy);
5207 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
5208 MovDelay[x][y] = 8 + 8 * !RND(3);
5210 MovDelay[x][y] = 16;
5212 else if (element == EL_PENGUIN)
5218 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5220 boolean first_horiz = RND(2);
5221 int new_move_dir = MovDir[x][y];
5224 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5225 Moving2Blocked(x, y, &newx, &newy);
5227 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
5231 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5232 Moving2Blocked(x, y, &newx, &newy);
5234 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
5237 MovDir[x][y] = old_move_dir;
5241 else if (element == EL_SATELLITE)
5247 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5249 boolean first_horiz = RND(2);
5250 int new_move_dir = MovDir[x][y];
5253 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5254 Moving2Blocked(x, y, &newx, &newy);
5256 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
5260 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5261 Moving2Blocked(x, y, &newx, &newy);
5263 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
5266 MovDir[x][y] = old_move_dir;
5270 else if (element == EL_EMC_ANDROID)
5272 static int check_pos[16] =
5274 -1, /* 0 => (invalid) */
5275 7, /* 1 => MV_LEFT */
5276 3, /* 2 => MV_RIGHT */
5277 -1, /* 3 => (invalid) */
5279 0, /* 5 => MV_LEFT | MV_UP */
5280 2, /* 6 => MV_RIGHT | MV_UP */
5281 -1, /* 7 => (invalid) */
5282 5, /* 8 => MV_DOWN */
5283 6, /* 9 => MV_LEFT | MV_DOWN */
5284 4, /* 10 => MV_RIGHT | MV_DOWN */
5285 -1, /* 11 => (invalid) */
5286 -1, /* 12 => (invalid) */
5287 -1, /* 13 => (invalid) */
5288 -1, /* 14 => (invalid) */
5289 -1, /* 15 => (invalid) */
5297 { -1, -1, MV_LEFT | MV_UP },
5299 { +1, -1, MV_RIGHT | MV_UP },
5300 { +1, 0, MV_RIGHT },
5301 { +1, +1, MV_RIGHT | MV_DOWN },
5303 { -1, +1, MV_LEFT | MV_DOWN },
5306 int start_pos, check_order;
5307 boolean can_clone = FALSE;
5310 /* check if there is any free field around current position */
5311 for (i = 0; i < 8; i++)
5313 int newx = x + check_xy[i].dx;
5314 int newy = y + check_xy[i].dy;
5316 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5324 if (can_clone) /* randomly find an element to clone */
5328 start_pos = check_pos[RND(8)];
5329 check_order = (RND(2) ? -1 : +1);
5331 for (i = 0; i < 8; i++)
5333 int pos_raw = start_pos + i * check_order;
5334 int pos = (pos_raw + 8) % 8;
5335 int newx = x + check_xy[pos].dx;
5336 int newy = y + check_xy[pos].dy;
5338 if (ANDROID_CAN_CLONE_FIELD(newx, newy))
5340 element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
5341 element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
5343 Store[x][y] = Feld[newx][newy];
5352 if (can_clone) /* randomly find a direction to move */
5356 start_pos = check_pos[RND(8)];
5357 check_order = (RND(2) ? -1 : +1);
5359 for (i = 0; i < 8; i++)
5361 int pos_raw = start_pos + i * check_order;
5362 int pos = (pos_raw + 8) % 8;
5363 int newx = x + check_xy[pos].dx;
5364 int newy = y + check_xy[pos].dy;
5365 int new_move_dir = check_xy[pos].dir;
5367 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5369 MovDir[x][y] = new_move_dir;
5370 MovDelay[x][y] = level.android_clone_time * 8 + 1;
5379 if (can_clone) /* cloning and moving successful */
5382 /* cannot clone -- try to move towards player */
5384 start_pos = check_pos[MovDir[x][y] & 0x0f];
5385 check_order = (RND(2) ? -1 : +1);
5387 for (i = 0; i < 3; i++)
5389 /* first check start_pos, then previous/next or (next/previous) pos */
5390 int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
5391 int pos = (pos_raw + 8) % 8;
5392 int newx = x + check_xy[pos].dx;
5393 int newy = y + check_xy[pos].dy;
5394 int new_move_dir = check_xy[pos].dir;
5396 if (IS_PLAYER(newx, newy))
5399 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
5401 MovDir[x][y] = new_move_dir;
5402 MovDelay[x][y] = level.android_move_time * 8 + 1;
5409 else if (move_pattern == MV_TURNING_LEFT ||
5410 move_pattern == MV_TURNING_RIGHT ||
5411 move_pattern == MV_TURNING_LEFT_RIGHT ||
5412 move_pattern == MV_TURNING_RIGHT_LEFT ||
5413 move_pattern == MV_TURNING_RANDOM ||
5414 move_pattern == MV_ALL_DIRECTIONS)
5416 boolean can_turn_left =
5417 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
5418 boolean can_turn_right =
5419 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
5421 if (element_info[element].move_stepsize == 0) /* "not moving" */
5424 if (move_pattern == MV_TURNING_LEFT)
5425 MovDir[x][y] = left_dir;
5426 else if (move_pattern == MV_TURNING_RIGHT)
5427 MovDir[x][y] = right_dir;
5428 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
5429 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
5430 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
5431 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
5432 else if (move_pattern == MV_TURNING_RANDOM)
5433 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
5434 can_turn_right && !can_turn_left ? right_dir :
5435 RND(2) ? left_dir : right_dir);
5436 else if (can_turn_left && can_turn_right)
5437 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5438 else if (can_turn_left)
5439 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5440 else if (can_turn_right)
5441 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5443 MovDir[x][y] = back_dir;
5445 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5447 else if (move_pattern == MV_HORIZONTAL ||
5448 move_pattern == MV_VERTICAL)
5450 if (move_pattern & old_move_dir)
5451 MovDir[x][y] = back_dir;
5452 else if (move_pattern == MV_HORIZONTAL)
5453 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
5454 else if (move_pattern == MV_VERTICAL)
5455 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
5457 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5459 else if (move_pattern & MV_ANY_DIRECTION)
5461 MovDir[x][y] = move_pattern;
5462 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5464 else if (move_pattern & MV_WIND_DIRECTION)
5466 MovDir[x][y] = game.wind_direction;
5467 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5469 else if (move_pattern == MV_ALONG_LEFT_SIDE)
5471 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
5472 MovDir[x][y] = left_dir;
5473 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5474 MovDir[x][y] = right_dir;
5476 if (MovDir[x][y] != old_move_dir)
5477 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5479 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
5481 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
5482 MovDir[x][y] = right_dir;
5483 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5484 MovDir[x][y] = left_dir;
5486 if (MovDir[x][y] != old_move_dir)
5487 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5489 else if (move_pattern == MV_TOWARDS_PLAYER ||
5490 move_pattern == MV_AWAY_FROM_PLAYER)
5492 int attr_x = -1, attr_y = -1;
5494 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
5505 for (i = 0; i < MAX_PLAYERS; i++)
5507 struct PlayerInfo *player = &stored_player[i];
5508 int jx = player->jx, jy = player->jy;
5510 if (!player->active)
5514 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
5522 MovDir[x][y] = MV_NONE;
5524 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
5525 else if (attr_x > x)
5526 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
5528 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
5529 else if (attr_y > y)
5530 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
5532 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5534 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5536 boolean first_horiz = RND(2);
5537 int new_move_dir = MovDir[x][y];
5539 if (element_info[element].move_stepsize == 0) /* "not moving" */
5541 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
5542 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5548 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5549 Moving2Blocked(x, y, &newx, &newy);
5551 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5555 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5556 Moving2Blocked(x, y, &newx, &newy);
5558 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5561 MovDir[x][y] = old_move_dir;
5564 else if (move_pattern == MV_WHEN_PUSHED ||
5565 move_pattern == MV_WHEN_DROPPED)
5567 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5568 MovDir[x][y] = MV_NONE;
5572 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
5574 static int test_xy[7][2] =
5584 static int test_dir[7] =
5594 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
5595 int move_preference = -1000000; /* start with very low preference */
5596 int new_move_dir = MV_NONE;
5597 int start_test = RND(4);
5600 for (i = 0; i < NUM_DIRECTIONS; i++)
5602 int move_dir = test_dir[start_test + i];
5603 int move_dir_preference;
5605 xx = x + test_xy[start_test + i][0];
5606 yy = y + test_xy[start_test + i][1];
5608 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
5609 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
5611 new_move_dir = move_dir;
5616 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
5619 move_dir_preference = -1 * RunnerVisit[xx][yy];
5620 if (hunter_mode && PlayerVisit[xx][yy] > 0)
5621 move_dir_preference = PlayerVisit[xx][yy];
5623 if (move_dir_preference > move_preference)
5625 /* prefer field that has not been visited for the longest time */
5626 move_preference = move_dir_preference;
5627 new_move_dir = move_dir;
5629 else if (move_dir_preference == move_preference &&
5630 move_dir == old_move_dir)
5632 /* prefer last direction when all directions are preferred equally */
5633 move_preference = move_dir_preference;
5634 new_move_dir = move_dir;
5638 MovDir[x][y] = new_move_dir;
5639 if (old_move_dir != new_move_dir)
5640 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5644 static void TurnRound(int x, int y)
5646 int direction = MovDir[x][y];
5650 GfxDir[x][y] = MovDir[x][y];
5652 if (direction != MovDir[x][y])
5656 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
5658 ResetGfxFrame(x, y, FALSE);
5661 static boolean JustBeingPushed(int x, int y)
5665 for (i = 0; i < MAX_PLAYERS; i++)
5667 struct PlayerInfo *player = &stored_player[i];
5669 if (player->active && player->is_pushing && player->MovPos)
5671 int next_jx = player->jx + (player->jx - player->last_jx);
5672 int next_jy = player->jy + (player->jy - player->last_jy);
5674 if (x == next_jx && y == next_jy)
5682 void StartMoving(int x, int y)
5684 boolean started_moving = FALSE; /* some elements can fall _and_ move */
5685 int element = Feld[x][y];
5690 if (MovDelay[x][y] == 0)
5691 GfxAction[x][y] = ACTION_DEFAULT;
5693 if (CAN_FALL(element) && y < lev_fieldy - 1)
5695 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
5696 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
5697 if (JustBeingPushed(x, y))
5700 if (element == EL_QUICKSAND_FULL)
5702 if (IS_FREE(x, y + 1))
5704 InitMovingField(x, y, MV_DOWN);
5705 started_moving = TRUE;
5707 Feld[x][y] = EL_QUICKSAND_EMPTYING;
5708 #if USE_QUICKSAND_BD_ROCK_BUGFIX
5709 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
5710 Store[x][y] = EL_ROCK;
5712 Store[x][y] = EL_ROCK;
5715 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
5717 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5719 if (!MovDelay[x][y])
5720 MovDelay[x][y] = TILEY + 1;
5729 Feld[x][y] = EL_QUICKSAND_EMPTY;
5730 Feld[x][y + 1] = EL_QUICKSAND_FULL;
5731 Store[x][y + 1] = Store[x][y];
5734 PlayLevelSoundAction(x, y, ACTION_FILLING);
5737 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
5738 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5740 InitMovingField(x, y, MV_DOWN);
5741 started_moving = TRUE;
5743 Feld[x][y] = EL_QUICKSAND_FILLING;
5744 Store[x][y] = element;
5746 PlayLevelSoundAction(x, y, ACTION_FILLING);
5748 else if (element == EL_MAGIC_WALL_FULL)
5750 if (IS_FREE(x, y + 1))
5752 InitMovingField(x, y, MV_DOWN);
5753 started_moving = TRUE;
5755 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
5756 Store[x][y] = EL_CHANGED(Store[x][y]);
5758 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
5760 if (!MovDelay[x][y])
5761 MovDelay[x][y] = TILEY/4 + 1;
5770 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
5771 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
5772 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
5776 else if (element == EL_BD_MAGIC_WALL_FULL)
5778 if (IS_FREE(x, y + 1))
5780 InitMovingField(x, y, MV_DOWN);
5781 started_moving = TRUE;
5783 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
5784 Store[x][y] = EL_CHANGED2(Store[x][y]);
5786 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
5788 if (!MovDelay[x][y])
5789 MovDelay[x][y] = TILEY/4 + 1;
5798 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
5799 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
5800 Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
5804 else if (CAN_PASS_MAGIC_WALL(element) &&
5805 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
5806 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
5808 InitMovingField(x, y, MV_DOWN);
5809 started_moving = TRUE;
5812 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
5813 EL_BD_MAGIC_WALL_FILLING);
5814 Store[x][y] = element;
5816 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
5818 SplashAcid(x, y + 1);
5820 InitMovingField(x, y, MV_DOWN);
5821 started_moving = TRUE;
5823 Store[x][y] = EL_ACID;
5826 #if USE_FIX_IMPACT_COLLISION
5827 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5828 CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
5830 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5831 CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
5833 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
5834 CAN_FALL(element) && WasJustFalling[x][y] &&
5835 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
5837 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
5838 CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
5839 (Feld[x][y + 1] == EL_BLOCKED)))
5841 /* this is needed for a special case not covered by calling "Impact()"
5842 from "ContinueMoving()": if an element moves to a tile directly below
5843 another element which was just falling on that tile (which was empty
5844 in the previous frame), the falling element above would just stop
5845 instead of smashing the element below (in previous version, the above
5846 element was just checked for "moving" instead of "falling", resulting
5847 in incorrect smashes caused by horizontal movement of the above
5848 element; also, the case of the player being the element to smash was
5849 simply not covered here... :-/ ) */
5851 CheckCollision[x][y] = 0;
5852 CheckImpact[x][y] = 0;
5856 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
5858 if (MovDir[x][y] == MV_NONE)
5860 InitMovingField(x, y, MV_DOWN);
5861 started_moving = TRUE;
5864 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
5866 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
5867 MovDir[x][y] = MV_DOWN;
5869 InitMovingField(x, y, MV_DOWN);
5870 started_moving = TRUE;
5872 else if (element == EL_AMOEBA_DROP)
5874 Feld[x][y] = EL_AMOEBA_GROWING;
5875 Store[x][y] = EL_AMOEBA_WET;
5877 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
5878 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
5879 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
5880 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
5882 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
5883 (IS_FREE(x - 1, y + 1) ||
5884 Feld[x - 1][y + 1] == EL_ACID));
5885 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
5886 (IS_FREE(x + 1, y + 1) ||
5887 Feld[x + 1][y + 1] == EL_ACID));
5888 boolean can_fall_any = (can_fall_left || can_fall_right);
5889 boolean can_fall_both = (can_fall_left && can_fall_right);
5890 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
5892 #if USE_NEW_ALL_SLIPPERY
5893 if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
5895 if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
5896 can_fall_right = FALSE;
5897 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
5898 can_fall_left = FALSE;
5899 else if (slippery_type == SLIPPERY_ONLY_LEFT)
5900 can_fall_right = FALSE;
5901 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
5902 can_fall_left = FALSE;
5904 can_fall_any = (can_fall_left || can_fall_right);
5905 can_fall_both = FALSE;
5908 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
5910 if (slippery_type == SLIPPERY_ONLY_LEFT)
5911 can_fall_right = FALSE;
5912 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
5913 can_fall_left = FALSE;
5914 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
5915 can_fall_right = FALSE;
5916 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
5917 can_fall_left = FALSE;
5919 can_fall_any = (can_fall_left || can_fall_right);
5920 can_fall_both = (can_fall_left && can_fall_right);
5924 #if USE_NEW_ALL_SLIPPERY
5926 #if USE_NEW_SP_SLIPPERY
5927 /* !!! better use the same properties as for custom elements here !!! */
5928 else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
5929 can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
5931 can_fall_right = FALSE; /* slip down on left side */
5932 can_fall_both = FALSE;
5937 #if USE_NEW_ALL_SLIPPERY
5940 if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
5941 can_fall_right = FALSE; /* slip down on left side */
5943 can_fall_left = !(can_fall_right = RND(2));
5945 can_fall_both = FALSE;
5950 if (game.emulation == EMU_BOULDERDASH ||
5951 element == EL_BD_ROCK || element == EL_BD_DIAMOND)
5952 can_fall_right = FALSE; /* slip down on left side */
5954 can_fall_left = !(can_fall_right = RND(2));
5956 can_fall_both = FALSE;
5962 /* if not determined otherwise, prefer left side for slipping down */
5963 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
5964 started_moving = TRUE;
5968 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
5970 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
5973 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
5974 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
5975 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
5976 int belt_dir = game.belt_dir[belt_nr];
5978 if ((belt_dir == MV_LEFT && left_is_free) ||
5979 (belt_dir == MV_RIGHT && right_is_free))
5981 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
5983 InitMovingField(x, y, belt_dir);
5984 started_moving = TRUE;
5986 Pushed[x][y] = TRUE;
5987 Pushed[nextx][y] = TRUE;
5989 GfxAction[x][y] = ACTION_DEFAULT;
5993 MovDir[x][y] = 0; /* if element was moving, stop it */
5998 /* not "else if" because of elements that can fall and move (EL_SPRING) */
6000 if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
6002 if (CAN_MOVE(element) && !started_moving)
6005 int move_pattern = element_info[element].move_pattern;
6010 if (MovDir[x][y] == MV_NONE)
6012 printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
6013 x, y, element, element_info[element].token_name);
6014 printf("StartMoving(): This should never happen!\n");
6019 Moving2Blocked(x, y, &newx, &newy);
6021 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
6024 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
6025 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6027 WasJustMoving[x][y] = 0;
6028 CheckCollision[x][y] = 0;
6030 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
6032 if (Feld[x][y] != element) /* element has changed */
6036 if (!MovDelay[x][y]) /* start new movement phase */
6038 /* all objects that can change their move direction after each step
6039 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
6041 if (element != EL_YAMYAM &&
6042 element != EL_DARK_YAMYAM &&
6043 element != EL_PACMAN &&
6044 !(move_pattern & MV_ANY_DIRECTION) &&
6045 move_pattern != MV_TURNING_LEFT &&
6046 move_pattern != MV_TURNING_RIGHT &&
6047 move_pattern != MV_TURNING_LEFT_RIGHT &&
6048 move_pattern != MV_TURNING_RIGHT_LEFT &&
6049 move_pattern != MV_TURNING_RANDOM)
6053 if (MovDelay[x][y] && (element == EL_BUG ||
6054 element == EL_SPACESHIP ||
6055 element == EL_SP_SNIKSNAK ||
6056 element == EL_SP_ELECTRON ||
6057 element == EL_MOLE))
6058 DrawLevelField(x, y);
6062 if (MovDelay[x][y]) /* wait some time before next movement */
6066 if (element == EL_ROBOT ||
6067 element == EL_YAMYAM ||
6068 element == EL_DARK_YAMYAM)
6070 DrawLevelElementAnimationIfNeeded(x, y, element);
6071 PlayLevelSoundAction(x, y, ACTION_WAITING);
6073 else if (element == EL_SP_ELECTRON)
6074 DrawLevelElementAnimationIfNeeded(x, y, element);
6075 else if (element == EL_DRAGON)
6078 int dir = MovDir[x][y];
6079 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
6080 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
6081 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
6082 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
6083 dir == MV_UP ? IMG_FLAMES_1_UP :
6084 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
6085 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
6087 GfxAction[x][y] = ACTION_ATTACKING;
6089 if (IS_PLAYER(x, y))
6090 DrawPlayerField(x, y);
6092 DrawLevelField(x, y);
6094 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
6096 for (i = 1; i <= 3; i++)
6098 int xx = x + i * dx;
6099 int yy = y + i * dy;
6100 int sx = SCREENX(xx);
6101 int sy = SCREENY(yy);
6102 int flame_graphic = graphic + (i - 1);
6104 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
6109 int flamed = MovingOrBlocked2Element(xx, yy);
6113 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
6115 else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
6116 RemoveMovingField(xx, yy);
6118 RemoveField(xx, yy);
6120 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
6123 RemoveMovingField(xx, yy);
6126 ChangeDelay[xx][yy] = 0;
6128 Feld[xx][yy] = EL_FLAMES;
6130 if (IN_SCR_FIELD(sx, sy))
6132 DrawLevelFieldCrumbledSand(xx, yy);
6133 DrawGraphic(sx, sy, flame_graphic, frame);
6138 if (Feld[xx][yy] == EL_FLAMES)
6139 Feld[xx][yy] = EL_EMPTY;
6140 DrawLevelField(xx, yy);
6145 if (MovDelay[x][y]) /* element still has to wait some time */
6147 PlayLevelSoundAction(x, y, ACTION_WAITING);
6153 /* now make next step */
6155 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
6157 if (DONT_COLLIDE_WITH(element) &&
6158 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
6159 !PLAYER_ENEMY_PROTECTED(newx, newy))
6161 TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
6166 else if (CAN_MOVE_INTO_ACID(element) &&
6167 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
6168 !IS_MV_DIAGONAL(MovDir[x][y]) &&
6169 (MovDir[x][y] == MV_DOWN ||
6170 game.engine_version >= VERSION_IDENT(3,1,0,0)))
6172 SplashAcid(newx, newy);
6173 Store[x][y] = EL_ACID;
6175 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
6177 if (Feld[newx][newy] == EL_EXIT_OPEN ||
6178 Feld[newx][newy] == EL_EM_EXIT_OPEN ||
6179 Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
6180 Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
6183 DrawLevelField(x, y);
6185 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
6186 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
6187 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
6189 local_player->friends_still_needed--;
6190 if (!local_player->friends_still_needed &&
6191 !local_player->GameOver && AllPlayersGone)
6192 PlayerWins(local_player);
6196 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
6198 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
6199 DrawLevelField(newx, newy);
6201 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
6203 else if (!IS_FREE(newx, newy))
6205 GfxAction[x][y] = ACTION_WAITING;
6207 if (IS_PLAYER(x, y))
6208 DrawPlayerField(x, y);
6210 DrawLevelField(x, y);
6215 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
6217 if (IS_FOOD_PIG(Feld[newx][newy]))
6219 if (IS_MOVING(newx, newy))
6220 RemoveMovingField(newx, newy);
6223 Feld[newx][newy] = EL_EMPTY;
6224 DrawLevelField(newx, newy);
6227 PlayLevelSound(x, y, SND_PIG_DIGGING);
6229 else if (!IS_FREE(newx, newy))
6231 if (IS_PLAYER(x, y))
6232 DrawPlayerField(x, y);
6234 DrawLevelField(x, y);
6239 else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
6241 if (Store[x][y] != EL_EMPTY)
6243 boolean can_clone = FALSE;
6246 /* check if element to clone is still there */
6247 for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
6249 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
6257 /* cannot clone or target field not free anymore -- do not clone */
6258 if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6259 Store[x][y] = EL_EMPTY;
6262 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6264 if (IS_MV_DIAGONAL(MovDir[x][y]))
6266 int diagonal_move_dir = MovDir[x][y];
6267 int stored = Store[x][y];
6268 int change_delay = 8;
6271 /* android is moving diagonally */
6273 CreateField(x, y, EL_DIAGONAL_SHRINKING);
6275 Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
6276 GfxElement[x][y] = EL_EMC_ANDROID;
6277 GfxAction[x][y] = ACTION_SHRINKING;
6278 GfxDir[x][y] = diagonal_move_dir;
6279 ChangeDelay[x][y] = change_delay;
6281 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
6284 DrawLevelGraphicAnimation(x, y, graphic);
6285 PlayLevelSoundAction(x, y, ACTION_SHRINKING);
6287 if (Feld[newx][newy] == EL_ACID)
6289 SplashAcid(newx, newy);
6294 CreateField(newx, newy, EL_DIAGONAL_GROWING);
6296 Store[newx][newy] = EL_EMC_ANDROID;
6297 GfxElement[newx][newy] = EL_EMC_ANDROID;
6298 GfxAction[newx][newy] = ACTION_GROWING;
6299 GfxDir[newx][newy] = diagonal_move_dir;
6300 ChangeDelay[newx][newy] = change_delay;
6302 graphic = el_act_dir2img(GfxElement[newx][newy],
6303 GfxAction[newx][newy], GfxDir[newx][newy]);
6305 DrawLevelGraphicAnimation(newx, newy, graphic);
6306 PlayLevelSoundAction(newx, newy, ACTION_GROWING);
6312 Feld[newx][newy] = EL_EMPTY;
6313 DrawLevelField(newx, newy);
6315 PlayLevelSoundAction(x, y, ACTION_DIGGING);
6318 else if (!IS_FREE(newx, newy))
6321 if (IS_PLAYER(x, y))
6322 DrawPlayerField(x, y);
6324 DrawLevelField(x, y);
6330 else if (IS_CUSTOM_ELEMENT(element) &&
6331 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6333 int new_element = Feld[newx][newy];
6335 if (!IS_FREE(newx, newy))
6337 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
6338 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
6341 /* no element can dig solid indestructible elements */
6342 if (IS_INDESTRUCTIBLE(new_element) &&
6343 !IS_DIGGABLE(new_element) &&
6344 !IS_COLLECTIBLE(new_element))
6347 if (AmoebaNr[newx][newy] &&
6348 (new_element == EL_AMOEBA_FULL ||
6349 new_element == EL_BD_AMOEBA ||
6350 new_element == EL_AMOEBA_GROWING))
6352 AmoebaCnt[AmoebaNr[newx][newy]]--;
6353 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6356 if (IS_MOVING(newx, newy))
6357 RemoveMovingField(newx, newy);
6360 RemoveField(newx, newy);
6361 DrawLevelField(newx, newy);
6364 /* if digged element was about to explode, prevent the explosion */
6365 ExplodeField[newx][newy] = EX_TYPE_NONE;
6367 PlayLevelSoundAction(x, y, action);
6370 Store[newx][newy] = EL_EMPTY;
6372 /* this makes it possible to leave the removed element again */
6373 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
6374 Store[newx][newy] = new_element;
6376 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
6378 int move_leave_element = element_info[element].move_leave_element;
6380 /* this makes it possible to leave the removed element again */
6381 Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
6382 new_element : move_leave_element);
6386 if (move_pattern & MV_MAZE_RUNNER_STYLE)
6388 RunnerVisit[x][y] = FrameCounter;
6389 PlayerVisit[x][y] /= 8; /* expire player visit path */
6392 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
6394 if (!IS_FREE(newx, newy))
6396 if (IS_PLAYER(x, y))
6397 DrawPlayerField(x, y);
6399 DrawLevelField(x, y);
6405 boolean wanna_flame = !RND(10);
6406 int dx = newx - x, dy = newy - y;
6407 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
6408 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
6409 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
6410 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
6411 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
6412 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
6415 IS_CLASSIC_ENEMY(element1) ||
6416 IS_CLASSIC_ENEMY(element2)) &&
6417 element1 != EL_DRAGON && element2 != EL_DRAGON &&
6418 element1 != EL_FLAMES && element2 != EL_FLAMES)
6420 ResetGfxAnimation(x, y);
6421 GfxAction[x][y] = ACTION_ATTACKING;
6423 if (IS_PLAYER(x, y))
6424 DrawPlayerField(x, y);
6426 DrawLevelField(x, y);
6428 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
6430 MovDelay[x][y] = 50;
6434 RemoveField(newx, newy);
6436 Feld[newx][newy] = EL_FLAMES;
6437 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
6440 RemoveField(newx1, newy1);
6442 Feld[newx1][newy1] = EL_FLAMES;
6444 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
6447 RemoveField(newx2, newy2);
6449 Feld[newx2][newy2] = EL_FLAMES;
6456 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6457 Feld[newx][newy] == EL_DIAMOND)
6459 if (IS_MOVING(newx, newy))
6460 RemoveMovingField(newx, newy);
6463 Feld[newx][newy] = EL_EMPTY;
6464 DrawLevelField(newx, newy);
6467 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
6469 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6470 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
6472 if (AmoebaNr[newx][newy])
6474 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6475 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
6476 Feld[newx][newy] == EL_BD_AMOEBA)
6477 AmoebaCnt[AmoebaNr[newx][newy]]--;
6482 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
6484 RemoveMovingField(newx, newy);
6487 if (IS_MOVING(newx, newy))
6489 RemoveMovingField(newx, newy);
6494 Feld[newx][newy] = EL_EMPTY;
6495 DrawLevelField(newx, newy);
6498 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
6500 else if ((element == EL_PACMAN || element == EL_MOLE)
6501 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
6503 if (AmoebaNr[newx][newy])
6505 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6506 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
6507 Feld[newx][newy] == EL_BD_AMOEBA)
6508 AmoebaCnt[AmoebaNr[newx][newy]]--;
6511 if (element == EL_MOLE)
6513 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
6514 PlayLevelSound(x, y, SND_MOLE_DIGGING);
6516 ResetGfxAnimation(x, y);
6517 GfxAction[x][y] = ACTION_DIGGING;
6518 DrawLevelField(x, y);
6520 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
6522 return; /* wait for shrinking amoeba */
6524 else /* element == EL_PACMAN */
6526 Feld[newx][newy] = EL_EMPTY;
6527 DrawLevelField(newx, newy);
6528 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
6531 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
6532 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
6533 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
6535 /* wait for shrinking amoeba to completely disappear */
6538 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
6540 /* object was running against a wall */
6545 /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
6546 if (move_pattern & MV_ANY_DIRECTION &&
6547 move_pattern == MovDir[x][y])
6549 int blocking_element =
6550 (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
6552 CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
6555 element = Feld[x][y]; /* element might have changed */
6559 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
6560 DrawLevelElementAnimation(x, y, element);
6562 if (DONT_TOUCH(element))
6563 TestIfBadThingTouchesPlayer(x, y);
6568 InitMovingField(x, y, MovDir[x][y]);
6570 PlayLevelSoundAction(x, y, ACTION_MOVING);
6574 ContinueMoving(x, y);
6577 void ContinueMoving(int x, int y)
6579 int element = Feld[x][y];
6580 struct ElementInfo *ei = &element_info[element];
6581 int direction = MovDir[x][y];
6582 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
6583 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
6584 int newx = x + dx, newy = y + dy;
6585 int stored = Store[x][y];
6586 int stored_new = Store[newx][newy];
6587 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
6588 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
6589 boolean last_line = (newy == lev_fieldy - 1);
6591 MovPos[x][y] += getElementMoveStepsize(x, y);
6593 if (pushed_by_player) /* special case: moving object pushed by player */
6594 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
6596 if (ABS(MovPos[x][y]) < TILEX)
6598 DrawLevelField(x, y);
6600 return; /* element is still moving */
6603 /* element reached destination field */
6605 Feld[x][y] = EL_EMPTY;
6606 Feld[newx][newy] = element;
6607 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
6609 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
6611 element = Feld[newx][newy] = EL_ACID;
6613 else if (element == EL_MOLE)
6615 Feld[x][y] = EL_SAND;
6617 DrawLevelFieldCrumbledSandNeighbours(x, y);
6619 else if (element == EL_QUICKSAND_FILLING)
6621 element = Feld[newx][newy] = get_next_element(element);
6622 Store[newx][newy] = Store[x][y];
6624 else if (element == EL_QUICKSAND_EMPTYING)
6626 Feld[x][y] = get_next_element(element);
6627 element = Feld[newx][newy] = Store[x][y];
6629 else if (element == EL_MAGIC_WALL_FILLING)
6631 element = Feld[newx][newy] = get_next_element(element);
6632 if (!game.magic_wall_active)
6633 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
6634 Store[newx][newy] = Store[x][y];
6636 else if (element == EL_MAGIC_WALL_EMPTYING)
6638 Feld[x][y] = get_next_element(element);
6639 if (!game.magic_wall_active)
6640 Feld[x][y] = EL_MAGIC_WALL_DEAD;
6641 element = Feld[newx][newy] = Store[x][y];
6643 #if USE_NEW_CUSTOM_VALUE
6644 InitField(newx, newy, FALSE);
6647 else if (element == EL_BD_MAGIC_WALL_FILLING)
6649 element = Feld[newx][newy] = get_next_element(element);
6650 if (!game.magic_wall_active)
6651 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
6652 Store[newx][newy] = Store[x][y];
6654 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
6656 Feld[x][y] = get_next_element(element);
6657 if (!game.magic_wall_active)
6658 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
6659 element = Feld[newx][newy] = Store[x][y];
6661 #if USE_NEW_CUSTOM_VALUE
6662 InitField(newx, newy, FALSE);
6665 else if (element == EL_AMOEBA_DROPPING)
6667 Feld[x][y] = get_next_element(element);
6668 element = Feld[newx][newy] = Store[x][y];
6670 else if (element == EL_SOKOBAN_OBJECT)
6673 Feld[x][y] = Back[x][y];
6675 if (Back[newx][newy])
6676 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
6678 Back[x][y] = Back[newx][newy] = 0;
6681 Store[x][y] = EL_EMPTY;
6686 MovDelay[newx][newy] = 0;
6688 if (CAN_CHANGE_OR_HAS_ACTION(element))
6690 /* copy element change control values to new field */
6691 ChangeDelay[newx][newy] = ChangeDelay[x][y];
6692 ChangePage[newx][newy] = ChangePage[x][y];
6693 ChangeCount[newx][newy] = ChangeCount[x][y];
6694 ChangeEvent[newx][newy] = ChangeEvent[x][y];
6697 #if USE_NEW_CUSTOM_VALUE
6698 CustomValue[newx][newy] = CustomValue[x][y];
6701 ChangeDelay[x][y] = 0;
6702 ChangePage[x][y] = -1;
6703 ChangeCount[x][y] = 0;
6704 ChangeEvent[x][y] = -1;
6706 #if USE_NEW_CUSTOM_VALUE
6707 CustomValue[x][y] = 0;
6710 /* copy animation control values to new field */
6711 GfxFrame[newx][newy] = GfxFrame[x][y];
6712 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
6713 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
6714 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
6716 Pushed[x][y] = Pushed[newx][newy] = FALSE;
6718 /* some elements can leave other elements behind after moving */
6720 if (ei->move_leave_element != EL_EMPTY &&
6721 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
6722 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
6724 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
6725 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
6726 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
6729 int move_leave_element = ei->move_leave_element;
6733 /* this makes it possible to leave the removed element again */
6734 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
6735 move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
6737 /* this makes it possible to leave the removed element again */
6738 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
6739 move_leave_element = stored;
6742 /* this makes it possible to leave the removed element again */
6743 if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
6744 ei->move_leave_element == EL_TRIGGER_ELEMENT)
6745 move_leave_element = stored;
6748 Feld[x][y] = move_leave_element;
6750 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
6751 MovDir[x][y] = direction;
6753 InitField(x, y, FALSE);
6755 if (GFX_CRUMBLED(Feld[x][y]))
6756 DrawLevelFieldCrumbledSandNeighbours(x, y);
6758 if (ELEM_IS_PLAYER(move_leave_element))
6759 RelocatePlayer(x, y, move_leave_element);
6762 /* do this after checking for left-behind element */
6763 ResetGfxAnimation(x, y); /* reset animation values for old field */
6765 if (!CAN_MOVE(element) ||
6766 (CAN_FALL(element) && direction == MV_DOWN &&
6767 (element == EL_SPRING ||
6768 element_info[element].move_pattern == MV_WHEN_PUSHED ||
6769 element_info[element].move_pattern == MV_WHEN_DROPPED)))
6770 GfxDir[x][y] = MovDir[newx][newy] = 0;
6772 DrawLevelField(x, y);
6773 DrawLevelField(newx, newy);
6775 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
6777 /* prevent pushed element from moving on in pushed direction */
6778 if (pushed_by_player && CAN_MOVE(element) &&
6779 element_info[element].move_pattern & MV_ANY_DIRECTION &&
6780 !(element_info[element].move_pattern & direction))
6781 TurnRound(newx, newy);
6783 /* prevent elements on conveyor belt from moving on in last direction */
6784 if (pushed_by_conveyor && CAN_FALL(element) &&
6785 direction & MV_HORIZONTAL)
6786 MovDir[newx][newy] = 0;
6788 if (!pushed_by_player)
6790 int nextx = newx + dx, nexty = newy + dy;
6791 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
6793 WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
6795 if (CAN_FALL(element) && direction == MV_DOWN)
6796 WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
6798 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
6799 CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
6801 #if USE_FIX_IMPACT_COLLISION
6802 if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
6803 CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
6807 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
6809 TestIfBadThingTouchesPlayer(newx, newy);
6810 TestIfBadThingTouchesFriend(newx, newy);
6812 if (!IS_CUSTOM_ELEMENT(element))
6813 TestIfBadThingTouchesOtherBadThing(newx, newy);
6815 else if (element == EL_PENGUIN)
6816 TestIfFriendTouchesBadThing(newx, newy);
6818 /* give the player one last chance (one more frame) to move away */
6819 if (CAN_FALL(element) && direction == MV_DOWN &&
6820 (last_line || (!IS_FREE(x, newy + 1) &&
6821 (!IS_PLAYER(x, newy + 1) ||
6822 game.engine_version < VERSION_IDENT(3,1,1,0)))))
6825 if (pushed_by_player && !game.use_change_when_pushing_bug)
6827 int push_side = MV_DIR_OPPOSITE(direction);
6828 struct PlayerInfo *player = PLAYERINFO(x, y);
6830 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
6831 player->index_bit, push_side);
6832 CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
6833 player->index_bit, push_side);
6836 if (element == EL_EMC_ANDROID && pushed_by_player) /* make another move */
6837 MovDelay[newx][newy] = 1;
6839 CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
6841 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
6844 if (ChangePage[newx][newy] != -1) /* delayed change */
6846 int page = ChangePage[newx][newy];
6847 struct ElementChangeInfo *change = &ei->change_page[page];
6849 ChangePage[newx][newy] = -1;
6851 if (change->can_change)
6853 if (ChangeElement(newx, newy, element, page))
6855 if (change->post_change_function)
6856 change->post_change_function(newx, newy);
6860 if (change->has_action)
6861 ExecuteCustomElementAction(newx, newy, element, page);
6865 TestIfElementHitsCustomElement(newx, newy, direction);
6866 TestIfPlayerTouchesCustomElement(newx, newy);
6867 TestIfElementTouchesCustomElement(newx, newy);
6869 if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
6870 IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
6871 CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
6872 MV_DIR_OPPOSITE(direction));
6875 int AmoebeNachbarNr(int ax, int ay)
6878 int element = Feld[ax][ay];
6880 static int xy[4][2] =
6888 for (i = 0; i < NUM_DIRECTIONS; i++)
6890 int x = ax + xy[i][0];
6891 int y = ay + xy[i][1];
6893 if (!IN_LEV_FIELD(x, y))
6896 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
6897 group_nr = AmoebaNr[x][y];
6903 void AmoebenVereinigen(int ax, int ay)
6905 int i, x, y, xx, yy;
6906 int new_group_nr = AmoebaNr[ax][ay];
6907 static int xy[4][2] =
6915 if (new_group_nr == 0)
6918 for (i = 0; i < NUM_DIRECTIONS; i++)
6923 if (!IN_LEV_FIELD(x, y))
6926 if ((Feld[x][y] == EL_AMOEBA_FULL ||
6927 Feld[x][y] == EL_BD_AMOEBA ||
6928 Feld[x][y] == EL_AMOEBA_DEAD) &&
6929 AmoebaNr[x][y] != new_group_nr)
6931 int old_group_nr = AmoebaNr[x][y];
6933 if (old_group_nr == 0)
6936 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
6937 AmoebaCnt[old_group_nr] = 0;
6938 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
6939 AmoebaCnt2[old_group_nr] = 0;
6941 SCAN_PLAYFIELD(xx, yy)
6943 if (AmoebaNr[xx][yy] == old_group_nr)
6944 AmoebaNr[xx][yy] = new_group_nr;
6950 void AmoebeUmwandeln(int ax, int ay)
6954 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
6956 int group_nr = AmoebaNr[ax][ay];
6961 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
6962 printf("AmoebeUmwandeln(): This should never happen!\n");
6967 SCAN_PLAYFIELD(x, y)
6969 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
6972 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
6976 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
6977 SND_AMOEBA_TURNING_TO_GEM :
6978 SND_AMOEBA_TURNING_TO_ROCK));
6983 static int xy[4][2] =
6991 for (i = 0; i < NUM_DIRECTIONS; i++)
6996 if (!IN_LEV_FIELD(x, y))
6999 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
7001 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
7002 SND_AMOEBA_TURNING_TO_GEM :
7003 SND_AMOEBA_TURNING_TO_ROCK));
7010 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
7013 int group_nr = AmoebaNr[ax][ay];
7014 boolean done = FALSE;
7019 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
7020 printf("AmoebeUmwandelnBD(): This should never happen!\n");
7025 SCAN_PLAYFIELD(x, y)
7027 if (AmoebaNr[x][y] == group_nr &&
7028 (Feld[x][y] == EL_AMOEBA_DEAD ||
7029 Feld[x][y] == EL_BD_AMOEBA ||
7030 Feld[x][y] == EL_AMOEBA_GROWING))
7033 Feld[x][y] = new_element;
7034 InitField(x, y, FALSE);
7035 DrawLevelField(x, y);
7041 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
7042 SND_BD_AMOEBA_TURNING_TO_ROCK :
7043 SND_BD_AMOEBA_TURNING_TO_GEM));
7046 void AmoebeWaechst(int x, int y)
7048 static unsigned long sound_delay = 0;
7049 static unsigned long sound_delay_value = 0;
7051 if (!MovDelay[x][y]) /* start new growing cycle */
7055 if (DelayReached(&sound_delay, sound_delay_value))
7057 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
7058 sound_delay_value = 30;
7062 if (MovDelay[x][y]) /* wait some time before growing bigger */
7065 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7067 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
7068 6 - MovDelay[x][y]);
7070 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
7073 if (!MovDelay[x][y])
7075 Feld[x][y] = Store[x][y];
7077 DrawLevelField(x, y);
7082 void AmoebaDisappearing(int x, int y)
7084 static unsigned long sound_delay = 0;
7085 static unsigned long sound_delay_value = 0;
7087 if (!MovDelay[x][y]) /* start new shrinking cycle */
7091 if (DelayReached(&sound_delay, sound_delay_value))
7092 sound_delay_value = 30;
7095 if (MovDelay[x][y]) /* wait some time before shrinking */
7098 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7100 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
7101 6 - MovDelay[x][y]);
7103 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
7106 if (!MovDelay[x][y])
7108 Feld[x][y] = EL_EMPTY;
7109 DrawLevelField(x, y);
7111 /* don't let mole enter this field in this cycle;
7112 (give priority to objects falling to this field from above) */
7118 void AmoebeAbleger(int ax, int ay)
7121 int element = Feld[ax][ay];
7122 int graphic = el2img(element);
7123 int newax = ax, neway = ay;
7124 boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
7125 static int xy[4][2] =
7133 if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
7135 Feld[ax][ay] = EL_AMOEBA_DEAD;
7136 DrawLevelField(ax, ay);
7140 if (IS_ANIMATED(graphic))
7141 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7143 if (!MovDelay[ax][ay]) /* start making new amoeba field */
7144 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
7146 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
7149 if (MovDelay[ax][ay])
7153 if (can_drop) /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
7156 int x = ax + xy[start][0];
7157 int y = ay + xy[start][1];
7159 if (!IN_LEV_FIELD(x, y))
7162 if (IS_FREE(x, y) ||
7163 CAN_GROW_INTO(Feld[x][y]) ||
7164 Feld[x][y] == EL_QUICKSAND_EMPTY)
7170 if (newax == ax && neway == ay)
7173 else /* normal or "filled" (BD style) amoeba */
7176 boolean waiting_for_player = FALSE;
7178 for (i = 0; i < NUM_DIRECTIONS; i++)
7180 int j = (start + i) % 4;
7181 int x = ax + xy[j][0];
7182 int y = ay + xy[j][1];
7184 if (!IN_LEV_FIELD(x, y))
7187 if (IS_FREE(x, y) ||
7188 CAN_GROW_INTO(Feld[x][y]) ||
7189 Feld[x][y] == EL_QUICKSAND_EMPTY)
7195 else if (IS_PLAYER(x, y))
7196 waiting_for_player = TRUE;
7199 if (newax == ax && neway == ay) /* amoeba cannot grow */
7201 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
7203 Feld[ax][ay] = EL_AMOEBA_DEAD;
7204 DrawLevelField(ax, ay);
7205 AmoebaCnt[AmoebaNr[ax][ay]]--;
7207 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
7209 if (element == EL_AMOEBA_FULL)
7210 AmoebeUmwandeln(ax, ay);
7211 else if (element == EL_BD_AMOEBA)
7212 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
7217 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
7219 /* amoeba gets larger by growing in some direction */
7221 int new_group_nr = AmoebaNr[ax][ay];
7224 if (new_group_nr == 0)
7226 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
7227 printf("AmoebeAbleger(): This should never happen!\n");
7232 AmoebaNr[newax][neway] = new_group_nr;
7233 AmoebaCnt[new_group_nr]++;
7234 AmoebaCnt2[new_group_nr]++;
7236 /* if amoeba touches other amoeba(s) after growing, unify them */
7237 AmoebenVereinigen(newax, neway);
7239 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
7241 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
7247 if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
7248 (neway == lev_fieldy - 1 && newax != ax))
7250 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
7251 Store[newax][neway] = element;
7253 else if (neway == ay || element == EL_EMC_DRIPPER)
7255 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
7257 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
7261 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
7262 Feld[ax][ay] = EL_AMOEBA_DROPPING;
7263 Store[ax][ay] = EL_AMOEBA_DROP;
7264 ContinueMoving(ax, ay);
7268 DrawLevelField(newax, neway);
7271 void Life(int ax, int ay)
7275 int element = Feld[ax][ay];
7276 int graphic = el2img(element);
7277 int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
7279 boolean changed = FALSE;
7281 if (IS_ANIMATED(graphic))
7282 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7287 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
7288 MovDelay[ax][ay] = life_time;
7290 if (MovDelay[ax][ay]) /* wait some time before next cycle */
7293 if (MovDelay[ax][ay])
7297 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
7299 int xx = ax+x1, yy = ay+y1;
7302 if (!IN_LEV_FIELD(xx, yy))
7305 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
7307 int x = xx+x2, y = yy+y2;
7309 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
7312 if (((Feld[x][y] == element ||
7313 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
7315 (IS_FREE(x, y) && Stop[x][y]))
7319 if (xx == ax && yy == ay) /* field in the middle */
7321 if (nachbarn < life_parameter[0] ||
7322 nachbarn > life_parameter[1])
7324 Feld[xx][yy] = EL_EMPTY;
7326 DrawLevelField(xx, yy);
7327 Stop[xx][yy] = TRUE;
7331 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
7332 { /* free border field */
7333 if (nachbarn >= life_parameter[2] &&
7334 nachbarn <= life_parameter[3])
7336 Feld[xx][yy] = element;
7337 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
7339 DrawLevelField(xx, yy);
7340 Stop[xx][yy] = TRUE;
7347 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
7348 SND_GAME_OF_LIFE_GROWING);
7351 static void InitRobotWheel(int x, int y)
7353 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
7356 static void RunRobotWheel(int x, int y)
7358 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
7361 static void StopRobotWheel(int x, int y)
7363 if (ZX == x && ZY == y)
7367 static void InitTimegateWheel(int x, int y)
7369 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
7372 static void RunTimegateWheel(int x, int y)
7374 PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
7377 static void InitMagicBallDelay(int x, int y)
7380 ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
7382 ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
7386 static void ActivateMagicBall(int bx, int by)
7390 if (level.ball_random)
7392 int pos_border = RND(8); /* select one of the eight border elements */
7393 int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
7394 int xx = pos_content % 3;
7395 int yy = pos_content / 3;
7400 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
7401 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
7405 for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
7407 int xx = x - bx + 1;
7408 int yy = y - by + 1;
7410 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
7411 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
7415 game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
7418 void CheckExit(int x, int y)
7420 if (local_player->gems_still_needed > 0 ||
7421 local_player->sokobanfields_still_needed > 0 ||
7422 local_player->lights_still_needed > 0)
7424 int element = Feld[x][y];
7425 int graphic = el2img(element);
7427 if (IS_ANIMATED(graphic))
7428 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7433 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7436 Feld[x][y] = EL_EXIT_OPENING;
7438 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
7441 void CheckExitEM(int x, int y)
7443 if (local_player->gems_still_needed > 0 ||
7444 local_player->sokobanfields_still_needed > 0 ||
7445 local_player->lights_still_needed > 0)
7447 int element = Feld[x][y];
7448 int graphic = el2img(element);
7450 if (IS_ANIMATED(graphic))
7451 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7456 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7459 Feld[x][y] = EL_EM_EXIT_OPENING;
7461 PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
7464 void CheckExitSteel(int x, int y)
7466 if (local_player->gems_still_needed > 0 ||
7467 local_player->sokobanfields_still_needed > 0 ||
7468 local_player->lights_still_needed > 0)
7470 int element = Feld[x][y];
7471 int graphic = el2img(element);
7473 if (IS_ANIMATED(graphic))
7474 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7479 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7482 Feld[x][y] = EL_STEEL_EXIT_OPENING;
7484 PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
7487 void CheckExitSteelEM(int x, int y)
7489 if (local_player->gems_still_needed > 0 ||
7490 local_player->sokobanfields_still_needed > 0 ||
7491 local_player->lights_still_needed > 0)
7493 int element = Feld[x][y];
7494 int graphic = el2img(element);
7496 if (IS_ANIMATED(graphic))
7497 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7502 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7505 Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
7507 PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
7510 void CheckExitSP(int x, int y)
7512 if (local_player->gems_still_needed > 0)
7514 int element = Feld[x][y];
7515 int graphic = el2img(element);
7517 if (IS_ANIMATED(graphic))
7518 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7523 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7526 Feld[x][y] = EL_SP_EXIT_OPENING;
7528 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
7531 static void CloseAllOpenTimegates()
7535 SCAN_PLAYFIELD(x, y)
7537 int element = Feld[x][y];
7539 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
7541 Feld[x][y] = EL_TIMEGATE_CLOSING;
7543 PlayLevelSoundAction(x, y, ACTION_CLOSING);
7548 void DrawTwinkleOnField(int x, int y)
7550 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
7553 if (Feld[x][y] == EL_BD_DIAMOND)
7556 if (MovDelay[x][y] == 0) /* next animation frame */
7557 MovDelay[x][y] = 11 * !GetSimpleRandom(500);
7559 if (MovDelay[x][y] != 0) /* wait some time before next frame */
7563 if (setup.direct_draw && MovDelay[x][y])
7564 SetDrawtoField(DRAW_BUFFERED);
7566 DrawLevelElementAnimation(x, y, Feld[x][y]);
7568 if (MovDelay[x][y] != 0)
7570 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
7571 10 - MovDelay[x][y]);
7573 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
7575 if (setup.direct_draw)
7579 dest_x = FX + SCREENX(x) * TILEX;
7580 dest_y = FY + SCREENY(y) * TILEY;
7582 BlitBitmap(drawto_field, window,
7583 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
7584 SetDrawtoField(DRAW_DIRECT);
7590 void MauerWaechst(int x, int y)
7594 if (!MovDelay[x][y]) /* next animation frame */
7595 MovDelay[x][y] = 3 * delay;
7597 if (MovDelay[x][y]) /* wait some time before next frame */
7601 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7603 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
7604 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
7606 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
7609 if (!MovDelay[x][y])
7611 if (MovDir[x][y] == MV_LEFT)
7613 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
7614 DrawLevelField(x - 1, y);
7616 else if (MovDir[x][y] == MV_RIGHT)
7618 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
7619 DrawLevelField(x + 1, y);
7621 else if (MovDir[x][y] == MV_UP)
7623 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
7624 DrawLevelField(x, y - 1);
7628 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
7629 DrawLevelField(x, y + 1);
7632 Feld[x][y] = Store[x][y];
7634 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7635 DrawLevelField(x, y);
7640 void MauerAbleger(int ax, int ay)
7642 int element = Feld[ax][ay];
7643 int graphic = el2img(element);
7644 boolean oben_frei = FALSE, unten_frei = FALSE;
7645 boolean links_frei = FALSE, rechts_frei = FALSE;
7646 boolean oben_massiv = FALSE, unten_massiv = FALSE;
7647 boolean links_massiv = FALSE, rechts_massiv = FALSE;
7648 boolean new_wall = FALSE;
7650 if (IS_ANIMATED(graphic))
7651 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7653 if (!MovDelay[ax][ay]) /* start building new wall */
7654 MovDelay[ax][ay] = 6;
7656 if (MovDelay[ax][ay]) /* wait some time before building new wall */
7659 if (MovDelay[ax][ay])
7663 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
7665 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
7667 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
7669 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
7672 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
7673 element == EL_EXPANDABLE_WALL_ANY)
7677 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
7678 Store[ax][ay-1] = element;
7679 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
7680 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
7681 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
7682 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
7687 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
7688 Store[ax][ay+1] = element;
7689 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
7690 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
7691 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
7692 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
7697 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7698 element == EL_EXPANDABLE_WALL_ANY ||
7699 element == EL_EXPANDABLE_WALL ||
7700 element == EL_BD_EXPANDABLE_WALL)
7704 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
7705 Store[ax-1][ay] = element;
7706 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
7707 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
7708 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
7709 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
7715 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
7716 Store[ax+1][ay] = element;
7717 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
7718 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
7719 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
7720 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
7725 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
7726 DrawLevelField(ax, ay);
7728 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
7730 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
7731 unten_massiv = TRUE;
7732 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
7733 links_massiv = TRUE;
7734 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
7735 rechts_massiv = TRUE;
7737 if (((oben_massiv && unten_massiv) ||
7738 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7739 element == EL_EXPANDABLE_WALL) &&
7740 ((links_massiv && rechts_massiv) ||
7741 element == EL_EXPANDABLE_WALL_VERTICAL))
7742 Feld[ax][ay] = EL_WALL;
7745 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
7748 void MauerAblegerStahl(int ax, int ay)
7750 int element = Feld[ax][ay];
7751 int graphic = el2img(element);
7752 boolean oben_frei = FALSE, unten_frei = FALSE;
7753 boolean links_frei = FALSE, rechts_frei = FALSE;
7754 boolean oben_massiv = FALSE, unten_massiv = FALSE;
7755 boolean links_massiv = FALSE, rechts_massiv = FALSE;
7756 boolean new_wall = FALSE;
7758 if (IS_ANIMATED(graphic))
7759 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7761 if (!MovDelay[ax][ay]) /* start building new wall */
7762 MovDelay[ax][ay] = 6;
7764 if (MovDelay[ax][ay]) /* wait some time before building new wall */
7767 if (MovDelay[ax][ay])
7771 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
7773 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
7775 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
7777 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
7780 if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
7781 element == EL_EXPANDABLE_STEELWALL_ANY)
7785 Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
7786 Store[ax][ay-1] = element;
7787 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
7788 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
7789 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
7790 IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
7795 Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
7796 Store[ax][ay+1] = element;
7797 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
7798 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
7799 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
7800 IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
7805 if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
7806 element == EL_EXPANDABLE_STEELWALL_ANY)
7810 Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
7811 Store[ax-1][ay] = element;
7812 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
7813 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
7814 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
7815 IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
7821 Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
7822 Store[ax+1][ay] = element;
7823 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
7824 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
7825 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
7826 IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
7831 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
7833 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
7834 unten_massiv = TRUE;
7835 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
7836 links_massiv = TRUE;
7837 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
7838 rechts_massiv = TRUE;
7840 if (((oben_massiv && unten_massiv) ||
7841 element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
7842 ((links_massiv && rechts_massiv) ||
7843 element == EL_EXPANDABLE_STEELWALL_VERTICAL))
7844 Feld[ax][ay] = EL_WALL;
7847 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
7850 void CheckForDragon(int x, int y)
7853 boolean dragon_found = FALSE;
7854 static int xy[4][2] =
7862 for (i = 0; i < NUM_DIRECTIONS; i++)
7864 for (j = 0; j < 4; j++)
7866 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7868 if (IN_LEV_FIELD(xx, yy) &&
7869 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
7871 if (Feld[xx][yy] == EL_DRAGON)
7872 dragon_found = TRUE;
7881 for (i = 0; i < NUM_DIRECTIONS; i++)
7883 for (j = 0; j < 3; j++)
7885 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7887 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
7889 Feld[xx][yy] = EL_EMPTY;
7890 DrawLevelField(xx, yy);
7899 static void InitBuggyBase(int x, int y)
7901 int element = Feld[x][y];
7902 int activating_delay = FRAMES_PER_SECOND / 4;
7905 (element == EL_SP_BUGGY_BASE ?
7906 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
7907 element == EL_SP_BUGGY_BASE_ACTIVATING ?
7909 element == EL_SP_BUGGY_BASE_ACTIVE ?
7910 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
7913 static void WarnBuggyBase(int x, int y)
7916 static int xy[4][2] =
7924 for (i = 0; i < NUM_DIRECTIONS; i++)
7926 int xx = x + xy[i][0];
7927 int yy = y + xy[i][1];
7929 if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
7931 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
7938 static void InitTrap(int x, int y)
7940 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
7943 static void ActivateTrap(int x, int y)
7945 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
7948 static void ChangeActiveTrap(int x, int y)
7950 int graphic = IMG_TRAP_ACTIVE;
7952 /* if new animation frame was drawn, correct crumbled sand border */
7953 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
7954 DrawLevelFieldCrumbledSand(x, y);
7957 static int getSpecialActionElement(int element, int number, int base_element)
7959 return (element != EL_EMPTY ? element :
7960 number != -1 ? base_element + number - 1 :
7964 static int getModifiedActionNumber(int value_old, int operator, int operand,
7965 int value_min, int value_max)
7967 int value_new = (operator == CA_MODE_SET ? operand :
7968 operator == CA_MODE_ADD ? value_old + operand :
7969 operator == CA_MODE_SUBTRACT ? value_old - operand :
7970 operator == CA_MODE_MULTIPLY ? value_old * operand :
7971 operator == CA_MODE_DIVIDE ? value_old / MAX(1, operand) :
7972 operator == CA_MODE_MODULO ? value_old % MAX(1, operand) :
7975 return (value_new < value_min ? value_min :
7976 value_new > value_max ? value_max :
7980 static void ExecuteCustomElementAction(int x, int y, int element, int page)
7982 struct ElementInfo *ei = &element_info[element];
7983 struct ElementChangeInfo *change = &ei->change_page[page];
7984 int target_element = change->target_element;
7985 int action_type = change->action_type;
7986 int action_mode = change->action_mode;
7987 int action_arg = change->action_arg;
7990 if (!change->has_action)
7993 /* ---------- determine action paramater values -------------------------- */
7995 int level_time_value =
7996 (level.time > 0 ? TimeLeft :
7999 int action_arg_element =
8000 (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player :
8001 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
8002 action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element :
8005 int action_arg_direction =
8006 (action_arg >= CA_ARG_DIRECTION_LEFT &&
8007 action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
8008 action_arg == CA_ARG_DIRECTION_TRIGGER ?
8009 change->actual_trigger_side :
8010 action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
8011 MV_DIR_OPPOSITE(change->actual_trigger_side) :
8014 int action_arg_number_min =
8015 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
8018 int action_arg_number_max =
8019 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
8020 action_type == CA_SET_LEVEL_GEMS ? 999 :
8021 action_type == CA_SET_LEVEL_TIME ? 9999 :
8022 action_type == CA_SET_LEVEL_SCORE ? 99999 :
8023 action_type == CA_SET_CE_VALUE ? 9999 :
8024 action_type == CA_SET_CE_SCORE ? 9999 :
8027 int action_arg_number_reset =
8028 (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
8029 action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
8030 action_type == CA_SET_LEVEL_TIME ? level.time :
8031 action_type == CA_SET_LEVEL_SCORE ? 0 :
8032 #if USE_NEW_CUSTOM_VALUE
8033 action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
8035 action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
8037 action_type == CA_SET_CE_SCORE ? 0 :
8040 int action_arg_number =
8041 (action_arg <= CA_ARG_MAX ? action_arg :
8042 action_arg >= CA_ARG_SPEED_NOT_MOVING &&
8043 action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
8044 action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
8045 action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
8046 action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
8047 action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
8048 #if USE_NEW_CUSTOM_VALUE
8049 action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
8051 action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
8053 action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
8054 action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
8055 action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
8056 action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
8057 action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
8058 action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
8059 action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
8060 action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
8061 action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
8062 action_arg == CA_ARG_ELEMENT_NR_TARGET ? change->target_element :
8063 action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
8066 int action_arg_number_old =
8067 (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
8068 action_type == CA_SET_LEVEL_TIME ? TimeLeft :
8069 action_type == CA_SET_LEVEL_SCORE ? local_player->score :
8070 action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
8071 action_type == CA_SET_CE_SCORE ? ei->collect_score :
8074 int action_arg_number_new =
8075 getModifiedActionNumber(action_arg_number_old,
8076 action_mode, action_arg_number,
8077 action_arg_number_min, action_arg_number_max);
8079 int trigger_player_bits =
8080 (change->actual_trigger_player >= EL_PLAYER_1 &&
8081 change->actual_trigger_player <= EL_PLAYER_4 ?
8082 (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
8085 int action_arg_player_bits =
8086 (action_arg >= CA_ARG_PLAYER_1 &&
8087 action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
8088 action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
8091 /* ---------- execute action -------------------------------------------- */
8093 switch (action_type)
8100 /* ---------- level actions ------------------------------------------- */
8102 case CA_RESTART_LEVEL:
8104 game.restart_level = TRUE;
8109 case CA_SHOW_ENVELOPE:
8111 int element = getSpecialActionElement(action_arg_element,
8112 action_arg_number, EL_ENVELOPE_1);
8114 if (IS_ENVELOPE(element))
8115 local_player->show_envelope = element;
8120 case CA_SET_LEVEL_TIME:
8122 if (level.time > 0) /* only modify limited time value */
8124 TimeLeft = action_arg_number_new;
8126 DrawGameValue_Time(TimeLeft);
8128 if (!TimeLeft && setup.time_limit)
8129 for (i = 0; i < MAX_PLAYERS; i++)
8130 KillPlayer(&stored_player[i]);
8136 case CA_SET_LEVEL_SCORE:
8138 local_player->score = action_arg_number_new;
8140 DrawGameValue_Score(local_player->score);
8145 case CA_SET_LEVEL_GEMS:
8147 local_player->gems_still_needed = action_arg_number_new;
8149 DrawGameValue_Emeralds(local_player->gems_still_needed);
8154 #if !USE_PLAYER_GRAVITY
8155 case CA_SET_LEVEL_GRAVITY:
8157 game.gravity = (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
8158 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
8159 action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
8165 case CA_SET_LEVEL_WIND:
8167 game.wind_direction = action_arg_direction;
8172 /* ---------- player actions ------------------------------------------ */
8174 case CA_MOVE_PLAYER:
8176 /* automatically move to the next field in specified direction */
8177 for (i = 0; i < MAX_PLAYERS; i++)
8178 if (trigger_player_bits & (1 << i))
8179 stored_player[i].programmed_action = action_arg_direction;
8184 case CA_EXIT_PLAYER:
8186 for (i = 0; i < MAX_PLAYERS; i++)
8187 if (action_arg_player_bits & (1 << i))
8188 PlayerWins(&stored_player[i]);
8193 case CA_KILL_PLAYER:
8195 for (i = 0; i < MAX_PLAYERS; i++)
8196 if (action_arg_player_bits & (1 << i))
8197 KillPlayer(&stored_player[i]);
8202 case CA_SET_PLAYER_KEYS:
8204 int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
8205 int element = getSpecialActionElement(action_arg_element,
8206 action_arg_number, EL_KEY_1);
8208 if (IS_KEY(element))
8210 for (i = 0; i < MAX_PLAYERS; i++)
8212 if (trigger_player_bits & (1 << i))
8214 stored_player[i].key[KEY_NR(element)] = key_state;
8216 DrawGameDoorValues();
8224 case CA_SET_PLAYER_SPEED:
8226 for (i = 0; i < MAX_PLAYERS; i++)
8228 if (trigger_player_bits & (1 << i))
8230 int move_stepsize = TILEX / stored_player[i].move_delay_value;
8232 if (action_arg == CA_ARG_SPEED_FASTER &&
8233 stored_player[i].cannot_move)
8235 action_arg_number = STEPSIZE_VERY_SLOW;
8237 else if (action_arg == CA_ARG_SPEED_SLOWER ||
8238 action_arg == CA_ARG_SPEED_FASTER)
8240 action_arg_number = 2;
8241 action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
8244 else if (action_arg == CA_ARG_NUMBER_RESET)
8246 action_arg_number = level.initial_player_stepsize[i];
8250 getModifiedActionNumber(move_stepsize,
8253 action_arg_number_min,
8254 action_arg_number_max);
8256 SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
8263 case CA_SET_PLAYER_SHIELD:
8265 for (i = 0; i < MAX_PLAYERS; i++)
8267 if (trigger_player_bits & (1 << i))
8269 if (action_arg == CA_ARG_SHIELD_OFF)
8271 stored_player[i].shield_normal_time_left = 0;
8272 stored_player[i].shield_deadly_time_left = 0;
8274 else if (action_arg == CA_ARG_SHIELD_NORMAL)
8276 stored_player[i].shield_normal_time_left = 999999;
8278 else if (action_arg == CA_ARG_SHIELD_DEADLY)
8280 stored_player[i].shield_normal_time_left = 999999;
8281 stored_player[i].shield_deadly_time_left = 999999;
8289 #if USE_PLAYER_GRAVITY
8290 case CA_SET_PLAYER_GRAVITY:
8292 for (i = 0; i < MAX_PLAYERS; i++)
8294 if (trigger_player_bits & (1 << i))
8296 stored_player[i].gravity =
8297 (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
8298 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
8299 action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
8300 stored_player[i].gravity);
8308 case CA_SET_PLAYER_ARTWORK:
8310 for (i = 0; i < MAX_PLAYERS; i++)
8312 if (trigger_player_bits & (1 << i))
8314 int artwork_element = action_arg_element;
8316 if (action_arg == CA_ARG_ELEMENT_RESET)
8318 (level.use_artwork_element[i] ? level.artwork_element[i] :
8319 stored_player[i].element_nr);
8321 #if USE_GFX_RESET_PLAYER_ARTWORK
8322 if (stored_player[i].artwork_element != artwork_element)
8323 stored_player[i].Frame = 0;
8326 stored_player[i].artwork_element = artwork_element;
8328 SetPlayerWaiting(&stored_player[i], FALSE);
8330 /* set number of special actions for bored and sleeping animation */
8331 stored_player[i].num_special_action_bored =
8332 get_num_special_action(artwork_element,
8333 ACTION_BORING_1, ACTION_BORING_LAST);
8334 stored_player[i].num_special_action_sleeping =
8335 get_num_special_action(artwork_element,
8336 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
8343 /* ---------- CE actions ---------------------------------------------- */
8345 case CA_SET_CE_VALUE:
8347 #if USE_NEW_CUSTOM_VALUE
8348 int last_ce_value = CustomValue[x][y];
8350 CustomValue[x][y] = action_arg_number_new;
8352 if (CustomValue[x][y] != last_ce_value)
8354 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
8355 CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
8357 if (CustomValue[x][y] == 0)
8359 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
8360 CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
8368 case CA_SET_CE_SCORE:
8370 #if USE_NEW_CUSTOM_VALUE
8371 int last_ce_score = ei->collect_score;
8373 ei->collect_score = action_arg_number_new;
8375 if (ei->collect_score != last_ce_score)
8377 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
8378 CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
8380 if (ei->collect_score == 0)
8384 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
8385 CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
8388 This is a very special case that seems to be a mixture between
8389 CheckElementChange() and CheckTriggeredElementChange(): while
8390 the first one only affects single elements that are triggered
8391 directly, the second one affects multiple elements in the playfield
8392 that are triggered indirectly by another element. This is a third
8393 case: Changing the CE score always affects multiple identical CEs,
8394 so every affected CE must be checked, not only the single CE for
8395 which the CE score was changed in the first place (as every instance
8396 of that CE shares the same CE score, and therefore also can change)!
8398 SCAN_PLAYFIELD(xx, yy)
8400 if (Feld[xx][yy] == element)
8401 CheckElementChange(xx, yy, element, EL_UNDEFINED,
8402 CE_SCORE_GETS_ZERO);
8411 /* ---------- engine actions ------------------------------------------ */
8413 case CA_SET_ENGINE_SCAN_MODE:
8415 InitPlayfieldScanMode(action_arg);
8425 static void CreateFieldExt(int x, int y, int element, boolean is_change)
8427 int old_element = Feld[x][y];
8428 int new_element = get_element_from_group_element(element);
8429 int previous_move_direction = MovDir[x][y];
8430 #if USE_NEW_CUSTOM_VALUE
8431 int last_ce_value = CustomValue[x][y];
8433 boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
8434 boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
8435 boolean add_player_onto_element = (new_element_is_player &&
8436 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
8437 /* this breaks SnakeBite when a snake is
8438 halfway through a door that closes */
8439 /* NOW FIXED AT LEVEL INIT IN files.c */
8440 new_element != EL_SOKOBAN_FIELD_PLAYER &&
8442 IS_WALKABLE(old_element));
8445 /* check if element under the player changes from accessible to unaccessible
8446 (needed for special case of dropping element which then changes) */
8447 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
8448 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
8456 if (!add_player_onto_element)
8458 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
8459 RemoveMovingField(x, y);
8463 Feld[x][y] = new_element;
8465 #if !USE_GFX_RESET_GFX_ANIMATION
8466 ResetGfxAnimation(x, y);
8467 ResetRandomAnimationValue(x, y);
8470 if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
8471 MovDir[x][y] = previous_move_direction;
8473 #if USE_NEW_CUSTOM_VALUE
8474 if (element_info[new_element].use_last_ce_value)
8475 CustomValue[x][y] = last_ce_value;
8478 InitField_WithBug1(x, y, FALSE);
8480 new_element = Feld[x][y]; /* element may have changed */
8482 #if USE_GFX_RESET_GFX_ANIMATION
8483 ResetGfxAnimation(x, y);
8484 ResetRandomAnimationValue(x, y);
8487 DrawLevelField(x, y);
8489 if (GFX_CRUMBLED(new_element))
8490 DrawLevelFieldCrumbledSandNeighbours(x, y);
8494 /* check if element under the player changes from accessible to unaccessible
8495 (needed for special case of dropping element which then changes) */
8496 /* (must be checked after creating new element for walkable group elements) */
8497 #if USE_FIX_KILLED_BY_NON_WALKABLE
8498 if (IS_PLAYER(x, y) && !player_explosion_protected &&
8499 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
8506 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
8507 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
8516 /* "ChangeCount" not set yet to allow "entered by player" change one time */
8517 if (new_element_is_player)
8518 RelocatePlayer(x, y, new_element);
8521 ChangeCount[x][y]++; /* count number of changes in the same frame */
8523 TestIfBadThingTouchesPlayer(x, y);
8524 TestIfPlayerTouchesCustomElement(x, y);
8525 TestIfElementTouchesCustomElement(x, y);
8528 static void CreateField(int x, int y, int element)
8530 CreateFieldExt(x, y, element, FALSE);
8533 static void CreateElementFromChange(int x, int y, int element)
8535 element = GET_VALID_RUNTIME_ELEMENT(element);
8537 #if USE_STOP_CHANGED_ELEMENTS
8538 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
8540 int old_element = Feld[x][y];
8542 /* prevent changed element from moving in same engine frame
8543 unless both old and new element can either fall or move */
8544 if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
8545 (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
8550 CreateFieldExt(x, y, element, TRUE);
8553 static boolean ChangeElement(int x, int y, int element, int page)
8555 struct ElementInfo *ei = &element_info[element];
8556 struct ElementChangeInfo *change = &ei->change_page[page];
8557 int ce_value = CustomValue[x][y];
8558 int ce_score = ei->collect_score;
8560 int old_element = Feld[x][y];
8562 /* always use default change event to prevent running into a loop */
8563 if (ChangeEvent[x][y] == -1)
8564 ChangeEvent[x][y] = CE_DELAY;
8566 if (ChangeEvent[x][y] == CE_DELAY)
8568 /* reset actual trigger element, trigger player and action element */
8569 change->actual_trigger_element = EL_EMPTY;
8570 change->actual_trigger_player = EL_PLAYER_1;
8571 change->actual_trigger_side = CH_SIDE_NONE;
8572 change->actual_trigger_ce_value = 0;
8573 change->actual_trigger_ce_score = 0;
8576 /* do not change elements more than a specified maximum number of changes */
8577 if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
8580 ChangeCount[x][y]++; /* count number of changes in the same frame */
8582 if (change->explode)
8589 if (change->use_target_content)
8591 boolean complete_replace = TRUE;
8592 boolean can_replace[3][3];
8595 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
8598 boolean is_walkable;
8599 boolean is_diggable;
8600 boolean is_collectible;
8601 boolean is_removable;
8602 boolean is_destructible;
8603 int ex = x + xx - 1;
8604 int ey = y + yy - 1;
8605 int content_element = change->target_content.e[xx][yy];
8608 can_replace[xx][yy] = TRUE;
8610 if (ex == x && ey == y) /* do not check changing element itself */
8613 if (content_element == EL_EMPTY_SPACE)
8615 can_replace[xx][yy] = FALSE; /* do not replace border with space */
8620 if (!IN_LEV_FIELD(ex, ey))
8622 can_replace[xx][yy] = FALSE;
8623 complete_replace = FALSE;
8630 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
8631 e = MovingOrBlocked2Element(ex, ey);
8633 is_empty = (IS_FREE(ex, ey) ||
8634 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
8636 is_walkable = (is_empty || IS_WALKABLE(e));
8637 is_diggable = (is_empty || IS_DIGGABLE(e));
8638 is_collectible = (is_empty || IS_COLLECTIBLE(e));
8639 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
8640 is_removable = (is_diggable || is_collectible);
8642 can_replace[xx][yy] =
8643 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
8644 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
8645 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
8646 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
8647 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
8648 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
8649 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
8651 if (!can_replace[xx][yy])
8652 complete_replace = FALSE;
8655 if (!change->only_if_complete || complete_replace)
8657 boolean something_has_changed = FALSE;
8659 if (change->only_if_complete && change->use_random_replace &&
8660 RND(100) < change->random_percentage)
8663 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
8665 int ex = x + xx - 1;
8666 int ey = y + yy - 1;
8667 int content_element;
8669 if (can_replace[xx][yy] && (!change->use_random_replace ||
8670 RND(100) < change->random_percentage))
8672 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
8673 RemoveMovingField(ex, ey);
8675 ChangeEvent[ex][ey] = ChangeEvent[x][y];
8677 content_element = change->target_content.e[xx][yy];
8678 target_element = GET_TARGET_ELEMENT(element, content_element, change,
8679 ce_value, ce_score);
8681 CreateElementFromChange(ex, ey, target_element);
8683 something_has_changed = TRUE;
8685 /* for symmetry reasons, freeze newly created border elements */
8686 if (ex != x || ey != y)
8687 Stop[ex][ey] = TRUE; /* no more moving in this frame */
8691 if (something_has_changed)
8693 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
8694 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
8700 target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
8701 ce_value, ce_score);
8703 if (element == EL_DIAGONAL_GROWING ||
8704 element == EL_DIAGONAL_SHRINKING)
8706 target_element = Store[x][y];
8708 Store[x][y] = EL_EMPTY;
8711 CreateElementFromChange(x, y, target_element);
8713 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
8714 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
8717 /* this uses direct change before indirect change */
8718 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
8723 #if USE_NEW_DELAYED_ACTION
8725 static void HandleElementChange(int x, int y, int page)
8727 int element = MovingOrBlocked2Element(x, y);
8728 struct ElementInfo *ei = &element_info[element];
8729 struct ElementChangeInfo *change = &ei->change_page[page];
8732 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
8733 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
8736 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
8737 x, y, element, element_info[element].token_name);
8738 printf("HandleElementChange(): This should never happen!\n");
8743 /* this can happen with classic bombs on walkable, changing elements */
8744 if (!CAN_CHANGE_OR_HAS_ACTION(element))
8747 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
8748 ChangeDelay[x][y] = 0;
8754 if (ChangeDelay[x][y] == 0) /* initialize element change */
8756 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
8758 if (change->can_change)
8760 ResetGfxAnimation(x, y);
8761 ResetRandomAnimationValue(x, y);
8763 if (change->pre_change_function)
8764 change->pre_change_function(x, y);
8768 ChangeDelay[x][y]--;
8770 if (ChangeDelay[x][y] != 0) /* continue element change */
8772 if (change->can_change)
8774 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8776 if (IS_ANIMATED(graphic))
8777 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8779 if (change->change_function)
8780 change->change_function(x, y);
8783 else /* finish element change */
8785 if (ChangePage[x][y] != -1) /* remember page from delayed change */
8787 page = ChangePage[x][y];
8788 ChangePage[x][y] = -1;
8790 change = &ei->change_page[page];
8793 if (IS_MOVING(x, y)) /* never change a running system ;-) */
8795 ChangeDelay[x][y] = 1; /* try change after next move step */
8796 ChangePage[x][y] = page; /* remember page to use for change */
8801 if (change->can_change)
8803 if (ChangeElement(x, y, element, page))
8805 if (change->post_change_function)
8806 change->post_change_function(x, y);
8810 if (change->has_action)
8811 ExecuteCustomElementAction(x, y, element, page);
8817 static void HandleElementChange(int x, int y, int page)
8819 int element = MovingOrBlocked2Element(x, y);
8820 struct ElementInfo *ei = &element_info[element];
8821 struct ElementChangeInfo *change = &ei->change_page[page];
8824 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
8827 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
8828 x, y, element, element_info[element].token_name);
8829 printf("HandleElementChange(): This should never happen!\n");
8834 /* this can happen with classic bombs on walkable, changing elements */
8835 if (!CAN_CHANGE(element))
8838 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
8839 ChangeDelay[x][y] = 0;
8845 if (ChangeDelay[x][y] == 0) /* initialize element change */
8847 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
8849 ResetGfxAnimation(x, y);
8850 ResetRandomAnimationValue(x, y);
8852 if (change->pre_change_function)
8853 change->pre_change_function(x, y);
8856 ChangeDelay[x][y]--;
8858 if (ChangeDelay[x][y] != 0) /* continue element change */
8860 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8862 if (IS_ANIMATED(graphic))
8863 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8865 if (change->change_function)
8866 change->change_function(x, y);
8868 else /* finish element change */
8870 if (ChangePage[x][y] != -1) /* remember page from delayed change */
8872 page = ChangePage[x][y];
8873 ChangePage[x][y] = -1;
8875 change = &ei->change_page[page];
8878 if (IS_MOVING(x, y)) /* never change a running system ;-) */
8880 ChangeDelay[x][y] = 1; /* try change after next move step */
8881 ChangePage[x][y] = page; /* remember page to use for change */
8886 if (ChangeElement(x, y, element, page))
8888 if (change->post_change_function)
8889 change->post_change_function(x, y);
8896 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
8897 int trigger_element,
8903 boolean change_done_any = FALSE;
8904 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
8907 if (!(trigger_events[trigger_element][trigger_event]))
8911 printf("::: CheckTriggeredElementChangeExt %d ... [%d, %d, %d, '%s']\n",
8912 trigger_event, recursion_loop_depth, recursion_loop_detected,
8913 recursion_loop_element, EL_NAME(recursion_loop_element));
8916 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
8918 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8920 int element = EL_CUSTOM_START + i;
8921 boolean change_done = FALSE;
8924 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
8925 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
8928 for (p = 0; p < element_info[element].num_change_pages; p++)
8930 struct ElementChangeInfo *change = &element_info[element].change_page[p];
8932 if (change->can_change_or_has_action &&
8933 change->has_event[trigger_event] &&
8934 change->trigger_side & trigger_side &&
8935 change->trigger_player & trigger_player &&
8936 change->trigger_page & trigger_page_bits &&
8937 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
8939 change->actual_trigger_element = trigger_element;
8940 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
8941 change->actual_trigger_side = trigger_side;
8942 change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
8943 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
8945 if ((change->can_change && !change_done) || change->has_action)
8949 SCAN_PLAYFIELD(x, y)
8951 if (Feld[x][y] == element)
8953 if (change->can_change && !change_done)
8955 ChangeDelay[x][y] = 1;
8956 ChangeEvent[x][y] = trigger_event;
8958 HandleElementChange(x, y, p);
8960 #if USE_NEW_DELAYED_ACTION
8961 else if (change->has_action)
8963 ExecuteCustomElementAction(x, y, element, p);
8964 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8967 if (change->has_action)
8969 ExecuteCustomElementAction(x, y, element, p);
8970 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8976 if (change->can_change)
8979 change_done_any = TRUE;
8986 RECURSION_LOOP_DETECTION_END();
8988 return change_done_any;
8991 static boolean CheckElementChangeExt(int x, int y,
8993 int trigger_element,
8998 boolean change_done = FALSE;
9001 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
9002 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
9005 if (Feld[x][y] == EL_BLOCKED)
9007 Blocked2Moving(x, y, &x, &y);
9008 element = Feld[x][y];
9012 /* check if element has already changed */
9013 if (Feld[x][y] != element)
9016 /* check if element has already changed or is about to change after moving */
9017 if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
9018 Feld[x][y] != element) ||
9020 (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
9021 (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
9022 ChangePage[x][y] != -1)))
9027 printf("::: CheckElementChangeExt %d ... [%d, %d, %d, '%s']\n",
9028 trigger_event, recursion_loop_depth, recursion_loop_detected,
9029 recursion_loop_element, EL_NAME(recursion_loop_element));
9032 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
9034 for (p = 0; p < element_info[element].num_change_pages; p++)
9036 struct ElementChangeInfo *change = &element_info[element].change_page[p];
9038 /* check trigger element for all events where the element that is checked
9039 for changing interacts with a directly adjacent element -- this is
9040 different to element changes that affect other elements to change on the
9041 whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
9042 boolean check_trigger_element =
9043 (trigger_event == CE_TOUCHING_X ||
9044 trigger_event == CE_HITTING_X ||
9045 trigger_event == CE_HIT_BY_X ||
9047 /* this one was forgotten until 3.2.3 */
9048 trigger_event == CE_DIGGING_X);
9051 if (change->can_change_or_has_action &&
9052 change->has_event[trigger_event] &&
9053 change->trigger_side & trigger_side &&
9054 change->trigger_player & trigger_player &&
9055 (!check_trigger_element ||
9056 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
9058 change->actual_trigger_element = trigger_element;
9059 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
9060 change->actual_trigger_side = trigger_side;
9061 change->actual_trigger_ce_value = CustomValue[x][y];
9062 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
9064 /* special case: trigger element not at (x,y) position for some events */
9065 if (check_trigger_element)
9077 { 0, 0 }, { 0, 0 }, { 0, 0 },
9081 int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
9082 int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
9084 change->actual_trigger_ce_value = CustomValue[xx][yy];
9085 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
9088 if (change->can_change && !change_done)
9090 ChangeDelay[x][y] = 1;
9091 ChangeEvent[x][y] = trigger_event;
9093 HandleElementChange(x, y, p);
9097 #if USE_NEW_DELAYED_ACTION
9098 else if (change->has_action)
9100 ExecuteCustomElementAction(x, y, element, p);
9101 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
9104 if (change->has_action)
9106 ExecuteCustomElementAction(x, y, element, p);
9107 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
9113 RECURSION_LOOP_DETECTION_END();
9118 static void PlayPlayerSound(struct PlayerInfo *player)
9120 int jx = player->jx, jy = player->jy;
9121 int sound_element = player->artwork_element;
9122 int last_action = player->last_action_waiting;
9123 int action = player->action_waiting;
9125 if (player->is_waiting)
9127 if (action != last_action)
9128 PlayLevelSoundElementAction(jx, jy, sound_element, action);
9130 PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
9134 if (action != last_action)
9135 StopSound(element_info[sound_element].sound[last_action]);
9137 if (last_action == ACTION_SLEEPING)
9138 PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
9142 static void PlayAllPlayersSound()
9146 for (i = 0; i < MAX_PLAYERS; i++)
9147 if (stored_player[i].active)
9148 PlayPlayerSound(&stored_player[i]);
9151 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
9153 boolean last_waiting = player->is_waiting;
9154 int move_dir = player->MovDir;
9156 player->dir_waiting = move_dir;
9157 player->last_action_waiting = player->action_waiting;
9161 if (!last_waiting) /* not waiting -> waiting */
9163 player->is_waiting = TRUE;
9165 player->frame_counter_bored =
9167 game.player_boring_delay_fixed +
9168 GetSimpleRandom(game.player_boring_delay_random);
9169 player->frame_counter_sleeping =
9171 game.player_sleeping_delay_fixed +
9172 GetSimpleRandom(game.player_sleeping_delay_random);
9174 InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
9177 if (game.player_sleeping_delay_fixed +
9178 game.player_sleeping_delay_random > 0 &&
9179 player->anim_delay_counter == 0 &&
9180 player->post_delay_counter == 0 &&
9181 FrameCounter >= player->frame_counter_sleeping)
9182 player->is_sleeping = TRUE;
9183 else if (game.player_boring_delay_fixed +
9184 game.player_boring_delay_random > 0 &&
9185 FrameCounter >= player->frame_counter_bored)
9186 player->is_bored = TRUE;
9188 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
9189 player->is_bored ? ACTION_BORING :
9192 if (player->is_sleeping && player->use_murphy)
9194 /* special case for sleeping Murphy when leaning against non-free tile */
9196 if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
9197 (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
9198 !IS_MOVING(player->jx - 1, player->jy)))
9200 else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
9201 (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
9202 !IS_MOVING(player->jx + 1, player->jy)))
9203 move_dir = MV_RIGHT;
9205 player->is_sleeping = FALSE;
9207 player->dir_waiting = move_dir;
9210 if (player->is_sleeping)
9212 if (player->num_special_action_sleeping > 0)
9214 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
9216 int last_special_action = player->special_action_sleeping;
9217 int num_special_action = player->num_special_action_sleeping;
9218 int special_action =
9219 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
9220 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
9221 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
9222 last_special_action + 1 : ACTION_SLEEPING);
9223 int special_graphic =
9224 el_act_dir2img(player->artwork_element, special_action, move_dir);
9226 player->anim_delay_counter =
9227 graphic_info[special_graphic].anim_delay_fixed +
9228 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
9229 player->post_delay_counter =
9230 graphic_info[special_graphic].post_delay_fixed +
9231 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
9233 player->special_action_sleeping = special_action;
9236 if (player->anim_delay_counter > 0)
9238 player->action_waiting = player->special_action_sleeping;
9239 player->anim_delay_counter--;
9241 else if (player->post_delay_counter > 0)
9243 player->post_delay_counter--;
9247 else if (player->is_bored)
9249 if (player->num_special_action_bored > 0)
9251 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
9253 int special_action =
9254 ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
9255 int special_graphic =
9256 el_act_dir2img(player->artwork_element, special_action, move_dir);
9258 player->anim_delay_counter =
9259 graphic_info[special_graphic].anim_delay_fixed +
9260 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
9261 player->post_delay_counter =
9262 graphic_info[special_graphic].post_delay_fixed +
9263 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
9265 player->special_action_bored = special_action;
9268 if (player->anim_delay_counter > 0)
9270 player->action_waiting = player->special_action_bored;
9271 player->anim_delay_counter--;
9273 else if (player->post_delay_counter > 0)
9275 player->post_delay_counter--;
9280 else if (last_waiting) /* waiting -> not waiting */
9282 player->is_waiting = FALSE;
9283 player->is_bored = FALSE;
9284 player->is_sleeping = FALSE;
9286 player->frame_counter_bored = -1;
9287 player->frame_counter_sleeping = -1;
9289 player->anim_delay_counter = 0;
9290 player->post_delay_counter = 0;
9292 player->dir_waiting = player->MovDir;
9293 player->action_waiting = ACTION_DEFAULT;
9295 player->special_action_bored = ACTION_DEFAULT;
9296 player->special_action_sleeping = ACTION_DEFAULT;
9300 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
9302 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
9303 int left = player_action & JOY_LEFT;
9304 int right = player_action & JOY_RIGHT;
9305 int up = player_action & JOY_UP;
9306 int down = player_action & JOY_DOWN;
9307 int button1 = player_action & JOY_BUTTON_1;
9308 int button2 = player_action & JOY_BUTTON_2;
9309 int dx = (left ? -1 : right ? 1 : 0);
9310 int dy = (up ? -1 : down ? 1 : 0);
9312 if (!player->active || tape.pausing)
9318 snapped = SnapField(player, dx, dy);
9322 dropped = DropElement(player);
9324 moved = MovePlayer(player, dx, dy);
9327 if (tape.single_step && tape.recording && !tape.pausing)
9329 if (button1 || (dropped && !moved))
9331 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9332 SnapField(player, 0, 0); /* stop snapping */
9336 SetPlayerWaiting(player, FALSE);
9338 return player_action;
9342 /* no actions for this player (no input at player's configured device) */
9344 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
9345 SnapField(player, 0, 0);
9346 CheckGravityMovementWhenNotMoving(player);
9348 if (player->MovPos == 0)
9349 SetPlayerWaiting(player, TRUE);
9351 if (player->MovPos == 0) /* needed for tape.playing */
9352 player->is_moving = FALSE;
9354 player->is_dropping = FALSE;
9355 player->is_dropping_pressed = FALSE;
9356 player->drop_pressed_delay = 0;
9362 static void CheckLevelTime()
9366 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9368 if (level.native_em_level->lev->home == 0) /* all players at home */
9370 PlayerWins(local_player);
9372 AllPlayersGone = TRUE;
9374 level.native_em_level->lev->home = -1;
9377 if (level.native_em_level->ply[0]->alive == 0 &&
9378 level.native_em_level->ply[1]->alive == 0 &&
9379 level.native_em_level->ply[2]->alive == 0 &&
9380 level.native_em_level->ply[3]->alive == 0) /* all dead */
9381 AllPlayersGone = TRUE;
9384 if (TimeFrames >= FRAMES_PER_SECOND)
9389 for (i = 0; i < MAX_PLAYERS; i++)
9391 struct PlayerInfo *player = &stored_player[i];
9393 if (SHIELD_ON(player))
9395 player->shield_normal_time_left--;
9397 if (player->shield_deadly_time_left > 0)
9398 player->shield_deadly_time_left--;
9402 if (!local_player->LevelSolved && !level.use_step_counter)
9410 if (TimeLeft <= 10 && setup.time_limit)
9411 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
9413 DrawGameValue_Time(TimeLeft);
9415 if (!TimeLeft && setup.time_limit)
9417 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9418 level.native_em_level->lev->killed_out_of_time = TRUE;
9420 for (i = 0; i < MAX_PLAYERS; i++)
9421 KillPlayer(&stored_player[i]);
9424 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
9425 DrawGameValue_Time(TimePlayed);
9427 level.native_em_level->lev->time =
9428 (level.time == 0 ? TimePlayed : TimeLeft);
9431 if (tape.recording || tape.playing)
9432 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
9436 void AdvanceFrameAndPlayerCounters(int player_nr)
9440 /* advance frame counters (global frame counter and time frame counter) */
9444 /* advance player counters (counters for move delay, move animation etc.) */
9445 for (i = 0; i < MAX_PLAYERS; i++)
9447 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
9448 int move_delay_value = stored_player[i].move_delay_value;
9449 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
9451 if (!advance_player_counters) /* not all players may be affected */
9454 #if USE_NEW_PLAYER_ANIM
9455 if (move_frames == 0) /* less than one move per game frame */
9457 int stepsize = TILEX / move_delay_value;
9458 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
9459 int count = (stored_player[i].is_moving ?
9460 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
9462 if (count % delay == 0)
9467 stored_player[i].Frame += move_frames;
9469 if (stored_player[i].MovPos != 0)
9470 stored_player[i].StepFrame += move_frames;
9472 if (stored_player[i].move_delay > 0)
9473 stored_player[i].move_delay--;
9475 /* due to bugs in previous versions, counter must count up, not down */
9476 if (stored_player[i].push_delay != -1)
9477 stored_player[i].push_delay++;
9479 if (stored_player[i].drop_delay > 0)
9480 stored_player[i].drop_delay--;
9482 if (stored_player[i].is_dropping_pressed)
9483 stored_player[i].drop_pressed_delay++;
9487 void StartGameActions(boolean init_network_game, boolean record_tape,
9490 unsigned long new_random_seed = InitRND(random_seed);
9493 TapeStartRecording(new_random_seed);
9495 #if defined(NETWORK_AVALIABLE)
9496 if (init_network_game)
9498 SendToServer_StartPlaying();
9509 static unsigned long game_frame_delay = 0;
9510 unsigned long game_frame_delay_value;
9511 byte *recorded_player_action;
9512 byte summarized_player_action = 0;
9513 byte tape_action[MAX_PLAYERS];
9516 /* detect endless loops, caused by custom element programming */
9517 if (recursion_loop_detected && recursion_loop_depth == 0)
9519 char *message = getStringCat3("Internal Error ! Element ",
9520 EL_NAME(recursion_loop_element),
9521 " caused endless loop ! Quit the game ?");
9523 Error(ERR_WARN, "element '%s' caused endless loop in game engine",
9524 EL_NAME(recursion_loop_element));
9526 RequestQuitGameExt(FALSE, level_editor_test_game, message);
9528 recursion_loop_detected = FALSE; /* if game should be continued */
9535 if (game.restart_level)
9536 StartGameActions(options.network, setup.autorecord, NEW_RANDOMIZE);
9538 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9540 if (level.native_em_level->lev->home == 0) /* all players at home */
9542 PlayerWins(local_player);
9544 AllPlayersGone = TRUE;
9546 level.native_em_level->lev->home = -1;
9549 if (level.native_em_level->ply[0]->alive == 0 &&
9550 level.native_em_level->ply[1]->alive == 0 &&
9551 level.native_em_level->ply[2]->alive == 0 &&
9552 level.native_em_level->ply[3]->alive == 0) /* all dead */
9553 AllPlayersGone = TRUE;
9556 if (local_player->LevelSolved)
9559 if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
9562 if (game_status != GAME_MODE_PLAYING) /* status might have changed */
9565 game_frame_delay_value =
9566 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
9568 if (tape.playing && tape.warp_forward && !tape.pausing)
9569 game_frame_delay_value = 0;
9571 /* ---------- main game synchronization point ---------- */
9573 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
9575 if (network_playing && !network_player_action_received)
9577 /* try to get network player actions in time */
9579 #if defined(NETWORK_AVALIABLE)
9580 /* last chance to get network player actions without main loop delay */
9584 /* game was quit by network peer */
9585 if (game_status != GAME_MODE_PLAYING)
9588 if (!network_player_action_received)
9589 return; /* failed to get network player actions in time */
9591 /* do not yet reset "network_player_action_received" (for tape.pausing) */
9597 /* at this point we know that we really continue executing the game */
9599 network_player_action_received = FALSE;
9601 /* when playing tape, read previously recorded player input from tape data */
9602 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
9605 /* TapePlayAction() may return NULL when toggling to "pause before death" */
9610 if (tape.set_centered_player)
9612 game.centered_player_nr_next = tape.centered_player_nr_next;
9613 game.set_centered_player = TRUE;
9616 for (i = 0; i < MAX_PLAYERS; i++)
9618 summarized_player_action |= stored_player[i].action;
9620 if (!network_playing)
9621 stored_player[i].effective_action = stored_player[i].action;
9624 #if defined(NETWORK_AVALIABLE)
9625 if (network_playing)
9626 SendToServer_MovePlayer(summarized_player_action);
9629 if (!options.network && !setup.team_mode)
9630 local_player->effective_action = summarized_player_action;
9632 if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
9634 for (i = 0; i < MAX_PLAYERS; i++)
9635 stored_player[i].effective_action =
9636 (i == game.centered_player_nr ? summarized_player_action : 0);
9639 if (recorded_player_action != NULL)
9640 for (i = 0; i < MAX_PLAYERS; i++)
9641 stored_player[i].effective_action = recorded_player_action[i];
9643 for (i = 0; i < MAX_PLAYERS; i++)
9645 tape_action[i] = stored_player[i].effective_action;
9647 /* (this can only happen in the R'n'D game engine) */
9648 if (tape.recording && tape_action[i] && !tape.player_participates[i])
9649 tape.player_participates[i] = TRUE; /* player just appeared from CE */
9652 /* only record actions from input devices, but not programmed actions */
9654 TapeRecordAction(tape_action);
9656 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9658 GameActions_EM_Main();
9666 void GameActions_EM_Main()
9668 byte effective_action[MAX_PLAYERS];
9669 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
9672 for (i = 0; i < MAX_PLAYERS; i++)
9673 effective_action[i] = stored_player[i].effective_action;
9675 GameActions_EM(effective_action, warp_mode);
9679 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
9682 void GameActions_RND()
9684 int magic_wall_x = 0, magic_wall_y = 0;
9685 int i, x, y, element, graphic;
9687 InitPlayfieldScanModeVars();
9689 #if USE_ONE_MORE_CHANGE_PER_FRAME
9690 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
9692 SCAN_PLAYFIELD(x, y)
9694 ChangeCount[x][y] = 0;
9695 ChangeEvent[x][y] = -1;
9700 if (game.set_centered_player)
9702 boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
9704 /* switching to "all players" only possible if all players fit to screen */
9705 if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
9707 game.centered_player_nr_next = game.centered_player_nr;
9708 game.set_centered_player = FALSE;
9711 /* do not switch focus to non-existing (or non-active) player */
9712 if (game.centered_player_nr_next >= 0 &&
9713 !stored_player[game.centered_player_nr_next].active)
9715 game.centered_player_nr_next = game.centered_player_nr;
9716 game.set_centered_player = FALSE;
9720 if (game.set_centered_player &&
9721 ScreenMovPos == 0) /* screen currently aligned at tile position */
9725 if (game.centered_player_nr_next == -1)
9727 setScreenCenteredToAllPlayers(&sx, &sy);
9731 sx = stored_player[game.centered_player_nr_next].jx;
9732 sy = stored_player[game.centered_player_nr_next].jy;
9735 game.centered_player_nr = game.centered_player_nr_next;
9736 game.set_centered_player = FALSE;
9738 DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
9739 DrawGameDoorValues();
9742 for (i = 0; i < MAX_PLAYERS; i++)
9744 int actual_player_action = stored_player[i].effective_action;
9747 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
9748 - rnd_equinox_tetrachloride 048
9749 - rnd_equinox_tetrachloride_ii 096
9750 - rnd_emanuel_schmieg 002
9751 - doctor_sloan_ww 001, 020
9753 if (stored_player[i].MovPos == 0)
9754 CheckGravityMovement(&stored_player[i]);
9757 /* overwrite programmed action with tape action */
9758 if (stored_player[i].programmed_action)
9759 actual_player_action = stored_player[i].programmed_action;
9761 PlayerActions(&stored_player[i], actual_player_action);
9763 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
9766 ScrollScreen(NULL, SCROLL_GO_ON);
9768 /* for backwards compatibility, the following code emulates a fixed bug that
9769 occured when pushing elements (causing elements that just made their last
9770 pushing step to already (if possible) make their first falling step in the
9771 same game frame, which is bad); this code is also needed to use the famous
9772 "spring push bug" which is used in older levels and might be wanted to be
9773 used also in newer levels, but in this case the buggy pushing code is only
9774 affecting the "spring" element and no other elements */
9776 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
9778 for (i = 0; i < MAX_PLAYERS; i++)
9780 struct PlayerInfo *player = &stored_player[i];
9784 if (player->active && player->is_pushing && player->is_moving &&
9786 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
9787 Feld[x][y] == EL_SPRING))
9789 ContinueMoving(x, y);
9791 /* continue moving after pushing (this is actually a bug) */
9792 if (!IS_MOVING(x, y))
9800 SCAN_PLAYFIELD(x, y)
9802 ChangeCount[x][y] = 0;
9803 ChangeEvent[x][y] = -1;
9805 /* this must be handled before main playfield loop */
9806 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
9809 if (MovDelay[x][y] <= 0)
9813 #if USE_NEW_SNAP_DELAY
9814 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
9817 if (MovDelay[x][y] <= 0)
9820 DrawLevelField(x, y);
9822 TestIfElementTouchesCustomElement(x, y); /* for empty space */
9828 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
9830 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
9831 printf("GameActions(): This should never happen!\n");
9833 ChangePage[x][y] = -1;
9838 if (WasJustMoving[x][y] > 0)
9839 WasJustMoving[x][y]--;
9840 if (WasJustFalling[x][y] > 0)
9841 WasJustFalling[x][y]--;
9842 if (CheckCollision[x][y] > 0)
9843 CheckCollision[x][y]--;
9844 if (CheckImpact[x][y] > 0)
9845 CheckImpact[x][y]--;
9849 /* reset finished pushing action (not done in ContinueMoving() to allow
9850 continuous pushing animation for elements with zero push delay) */
9851 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
9853 ResetGfxAnimation(x, y);
9854 DrawLevelField(x, y);
9858 if (IS_BLOCKED(x, y))
9862 Blocked2Moving(x, y, &oldx, &oldy);
9863 if (!IS_MOVING(oldx, oldy))
9865 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
9866 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
9867 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
9868 printf("GameActions(): This should never happen!\n");
9874 SCAN_PLAYFIELD(x, y)
9876 element = Feld[x][y];
9877 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9879 ResetGfxFrame(x, y, TRUE);
9881 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
9882 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
9883 ResetRandomAnimationValue(x, y);
9885 SetRandomAnimationValue(x, y);
9887 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
9889 if (IS_INACTIVE(element))
9891 if (IS_ANIMATED(graphic))
9892 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9897 /* this may take place after moving, so 'element' may have changed */
9898 if (IS_CHANGING(x, y) &&
9899 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
9901 int page = element_info[element].event_page_nr[CE_DELAY];
9904 HandleElementChange(x, y, page);
9906 if (CAN_CHANGE(element))
9907 HandleElementChange(x, y, page);
9909 if (HAS_ACTION(element))
9910 ExecuteCustomElementAction(x, y, element, page);
9913 element = Feld[x][y];
9914 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9917 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
9921 element = Feld[x][y];
9922 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9924 if (IS_ANIMATED(graphic) &&
9927 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9929 if (IS_GEM(element) || element == EL_SP_INFOTRON)
9930 DrawTwinkleOnField(x, y);
9932 else if ((element == EL_ACID ||
9933 element == EL_EXIT_OPEN ||
9934 element == EL_EM_EXIT_OPEN ||
9935 element == EL_SP_EXIT_OPEN ||
9936 element == EL_STEEL_EXIT_OPEN ||
9937 element == EL_EM_STEEL_EXIT_OPEN ||
9938 element == EL_SP_TERMINAL ||
9939 element == EL_SP_TERMINAL_ACTIVE ||
9940 element == EL_EXTRA_TIME ||
9941 element == EL_SHIELD_NORMAL ||
9942 element == EL_SHIELD_DEADLY) &&
9943 IS_ANIMATED(graphic))
9944 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9945 else if (IS_MOVING(x, y))
9946 ContinueMoving(x, y);
9947 else if (IS_ACTIVE_BOMB(element))
9948 CheckDynamite(x, y);
9949 else if (element == EL_AMOEBA_GROWING)
9950 AmoebeWaechst(x, y);
9951 else if (element == EL_AMOEBA_SHRINKING)
9952 AmoebaDisappearing(x, y);
9954 #if !USE_NEW_AMOEBA_CODE
9955 else if (IS_AMOEBALIVE(element))
9956 AmoebeAbleger(x, y);
9959 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
9961 else if (element == EL_EXIT_CLOSED)
9963 else if (element == EL_EM_EXIT_CLOSED)
9965 else if (element == EL_STEEL_EXIT_CLOSED)
9966 CheckExitSteel(x, y);
9967 else if (element == EL_EM_STEEL_EXIT_CLOSED)
9968 CheckExitSteelEM(x, y);
9969 else if (element == EL_SP_EXIT_CLOSED)
9971 else if (element == EL_EXPANDABLE_WALL_GROWING ||
9972 element == EL_EXPANDABLE_STEELWALL_GROWING)
9974 else if (element == EL_EXPANDABLE_WALL ||
9975 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9976 element == EL_EXPANDABLE_WALL_VERTICAL ||
9977 element == EL_EXPANDABLE_WALL_ANY ||
9978 element == EL_BD_EXPANDABLE_WALL)
9980 else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9981 element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9982 element == EL_EXPANDABLE_STEELWALL_ANY)
9983 MauerAblegerStahl(x, y);
9984 else if (element == EL_FLAMES)
9985 CheckForDragon(x, y);
9986 else if (element == EL_EXPLOSION)
9987 ; /* drawing of correct explosion animation is handled separately */
9988 else if (element == EL_ELEMENT_SNAPPING ||
9989 element == EL_DIAGONAL_SHRINKING ||
9990 element == EL_DIAGONAL_GROWING)
9992 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
9994 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9996 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
9997 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9999 if (IS_BELT_ACTIVE(element))
10000 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
10002 if (game.magic_wall_active)
10004 int jx = local_player->jx, jy = local_player->jy;
10006 /* play the element sound at the position nearest to the player */
10007 if ((element == EL_MAGIC_WALL_FULL ||
10008 element == EL_MAGIC_WALL_ACTIVE ||
10009 element == EL_MAGIC_WALL_EMPTYING ||
10010 element == EL_BD_MAGIC_WALL_FULL ||
10011 element == EL_BD_MAGIC_WALL_ACTIVE ||
10012 element == EL_BD_MAGIC_WALL_EMPTYING) &&
10013 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
10021 #if USE_NEW_AMOEBA_CODE
10022 /* new experimental amoeba growth stuff */
10023 if (!(FrameCounter % 8))
10025 static unsigned long random = 1684108901;
10027 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
10029 x = RND(lev_fieldx);
10030 y = RND(lev_fieldy);
10031 element = Feld[x][y];
10033 if (!IS_PLAYER(x,y) &&
10034 (element == EL_EMPTY ||
10035 CAN_GROW_INTO(element) ||
10036 element == EL_QUICKSAND_EMPTY ||
10037 element == EL_ACID_SPLASH_LEFT ||
10038 element == EL_ACID_SPLASH_RIGHT))
10040 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
10041 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
10042 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
10043 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
10044 Feld[x][y] = EL_AMOEBA_DROP;
10047 random = random * 129 + 1;
10053 if (game.explosions_delayed)
10056 game.explosions_delayed = FALSE;
10058 SCAN_PLAYFIELD(x, y)
10060 element = Feld[x][y];
10062 if (ExplodeField[x][y])
10063 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
10064 else if (element == EL_EXPLOSION)
10065 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
10067 ExplodeField[x][y] = EX_TYPE_NONE;
10070 game.explosions_delayed = TRUE;
10073 if (game.magic_wall_active)
10075 if (!(game.magic_wall_time_left % 4))
10077 int element = Feld[magic_wall_x][magic_wall_y];
10079 if (element == EL_BD_MAGIC_WALL_FULL ||
10080 element == EL_BD_MAGIC_WALL_ACTIVE ||
10081 element == EL_BD_MAGIC_WALL_EMPTYING)
10082 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
10084 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
10087 if (game.magic_wall_time_left > 0)
10089 game.magic_wall_time_left--;
10090 if (!game.magic_wall_time_left)
10092 SCAN_PLAYFIELD(x, y)
10094 element = Feld[x][y];
10096 if (element == EL_MAGIC_WALL_ACTIVE ||
10097 element == EL_MAGIC_WALL_FULL)
10099 Feld[x][y] = EL_MAGIC_WALL_DEAD;
10100 DrawLevelField(x, y);
10102 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
10103 element == EL_BD_MAGIC_WALL_FULL)
10105 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
10106 DrawLevelField(x, y);
10110 game.magic_wall_active = FALSE;
10115 if (game.light_time_left > 0)
10117 game.light_time_left--;
10119 if (game.light_time_left == 0)
10120 RedrawAllLightSwitchesAndInvisibleElements();
10123 if (game.timegate_time_left > 0)
10125 game.timegate_time_left--;
10127 if (game.timegate_time_left == 0)
10128 CloseAllOpenTimegates();
10131 if (game.lenses_time_left > 0)
10133 game.lenses_time_left--;
10135 if (game.lenses_time_left == 0)
10136 RedrawAllInvisibleElementsForLenses();
10139 if (game.magnify_time_left > 0)
10141 game.magnify_time_left--;
10143 if (game.magnify_time_left == 0)
10144 RedrawAllInvisibleElementsForMagnifier();
10147 for (i = 0; i < MAX_PLAYERS; i++)
10149 struct PlayerInfo *player = &stored_player[i];
10151 if (SHIELD_ON(player))
10153 if (player->shield_deadly_time_left)
10154 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
10155 else if (player->shield_normal_time_left)
10156 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
10163 PlayAllPlayersSound();
10165 if (options.debug) /* calculate frames per second */
10167 static unsigned long fps_counter = 0;
10168 static int fps_frames = 0;
10169 unsigned long fps_delay_ms = Counter() - fps_counter;
10173 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
10175 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
10178 fps_counter = Counter();
10181 redraw_mask |= REDRAW_FPS;
10184 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
10186 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
10188 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
10190 local_player->show_envelope = 0;
10193 /* use random number generator in every frame to make it less predictable */
10194 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
10198 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
10200 int min_x = x, min_y = y, max_x = x, max_y = y;
10203 for (i = 0; i < MAX_PLAYERS; i++)
10205 int jx = stored_player[i].jx, jy = stored_player[i].jy;
10207 if (!stored_player[i].active || &stored_player[i] == player)
10210 min_x = MIN(min_x, jx);
10211 min_y = MIN(min_y, jy);
10212 max_x = MAX(max_x, jx);
10213 max_y = MAX(max_y, jy);
10216 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
10219 static boolean AllPlayersInVisibleScreen()
10223 for (i = 0; i < MAX_PLAYERS; i++)
10225 int jx = stored_player[i].jx, jy = stored_player[i].jy;
10227 if (!stored_player[i].active)
10230 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
10237 void ScrollLevel(int dx, int dy)
10239 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
10242 BlitBitmap(drawto_field, drawto_field,
10243 FX + TILEX * (dx == -1) - softscroll_offset,
10244 FY + TILEY * (dy == -1) - softscroll_offset,
10245 SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
10246 SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
10247 FX + TILEX * (dx == 1) - softscroll_offset,
10248 FY + TILEY * (dy == 1) - softscroll_offset);
10252 x = (dx == 1 ? BX1 : BX2);
10253 for (y = BY1; y <= BY2; y++)
10254 DrawScreenField(x, y);
10259 y = (dy == 1 ? BY1 : BY2);
10260 for (x = BX1; x <= BX2; x++)
10261 DrawScreenField(x, y);
10264 redraw_mask |= REDRAW_FIELD;
10267 static boolean canFallDown(struct PlayerInfo *player)
10269 int jx = player->jx, jy = player->jy;
10271 return (IN_LEV_FIELD(jx, jy + 1) &&
10272 (IS_FREE(jx, jy + 1) ||
10273 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
10274 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
10275 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
10278 static boolean canPassField(int x, int y, int move_dir)
10280 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
10281 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
10282 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
10283 int nextx = x + dx;
10284 int nexty = y + dy;
10285 int element = Feld[x][y];
10287 return (IS_PASSABLE_FROM(element, opposite_dir) &&
10288 !CAN_MOVE(element) &&
10289 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
10290 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
10291 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
10294 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
10296 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
10297 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
10298 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
10302 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
10303 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
10304 (IS_DIGGABLE(Feld[newx][newy]) ||
10305 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
10306 canPassField(newx, newy, move_dir)));
10309 static void CheckGravityMovement(struct PlayerInfo *player)
10311 #if USE_PLAYER_GRAVITY
10312 if (player->gravity && !player->programmed_action)
10314 if (game.gravity && !player->programmed_action)
10317 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
10318 int move_dir_vertical = player->effective_action & MV_VERTICAL;
10319 boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
10320 int jx = player->jx, jy = player->jy;
10321 boolean player_is_moving_to_valid_field =
10322 (!player_is_snapping &&
10323 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
10324 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
10325 boolean player_can_fall_down = canFallDown(player);
10327 if (player_can_fall_down &&
10328 !player_is_moving_to_valid_field)
10329 player->programmed_action = MV_DOWN;
10333 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
10335 return CheckGravityMovement(player);
10337 #if USE_PLAYER_GRAVITY
10338 if (player->gravity && !player->programmed_action)
10340 if (game.gravity && !player->programmed_action)
10343 int jx = player->jx, jy = player->jy;
10344 boolean field_under_player_is_free =
10345 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
10346 boolean player_is_standing_on_valid_field =
10347 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
10348 (IS_WALKABLE(Feld[jx][jy]) &&
10349 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
10351 if (field_under_player_is_free && !player_is_standing_on_valid_field)
10352 player->programmed_action = MV_DOWN;
10357 MovePlayerOneStep()
10358 -----------------------------------------------------------------------------
10359 dx, dy: direction (non-diagonal) to try to move the player to
10360 real_dx, real_dy: direction as read from input device (can be diagonal)
10363 boolean MovePlayerOneStep(struct PlayerInfo *player,
10364 int dx, int dy, int real_dx, int real_dy)
10366 int jx = player->jx, jy = player->jy;
10367 int new_jx = jx + dx, new_jy = jy + dy;
10368 #if !USE_FIXED_DONT_RUN_INTO
10372 boolean player_can_move = !player->cannot_move;
10374 if (!player->active || (!dx && !dy))
10375 return MP_NO_ACTION;
10377 player->MovDir = (dx < 0 ? MV_LEFT :
10378 dx > 0 ? MV_RIGHT :
10380 dy > 0 ? MV_DOWN : MV_NONE);
10382 if (!IN_LEV_FIELD(new_jx, new_jy))
10383 return MP_NO_ACTION;
10385 if (!player_can_move)
10387 if (player->MovPos == 0)
10389 player->is_moving = FALSE;
10390 player->is_digging = FALSE;
10391 player->is_collecting = FALSE;
10392 player->is_snapping = FALSE;
10393 player->is_pushing = FALSE;
10398 if (!options.network && game.centered_player_nr == -1 &&
10399 !AllPlayersInSight(player, new_jx, new_jy))
10400 return MP_NO_ACTION;
10402 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
10403 return MP_NO_ACTION;
10406 #if !USE_FIXED_DONT_RUN_INTO
10407 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
10409 /* (moved to DigField()) */
10410 if (player_can_move && DONT_RUN_INTO(element))
10412 if (element == EL_ACID && dx == 0 && dy == 1)
10414 SplashAcid(new_jx, new_jy);
10415 Feld[jx][jy] = EL_PLAYER_1;
10416 InitMovingField(jx, jy, MV_DOWN);
10417 Store[jx][jy] = EL_ACID;
10418 ContinueMoving(jx, jy);
10419 BuryPlayer(player);
10422 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
10428 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
10429 if (can_move != MP_MOVING)
10432 /* check if DigField() has caused relocation of the player */
10433 if (player->jx != jx || player->jy != jy)
10434 return MP_NO_ACTION; /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
10436 StorePlayer[jx][jy] = 0;
10437 player->last_jx = jx;
10438 player->last_jy = jy;
10439 player->jx = new_jx;
10440 player->jy = new_jy;
10441 StorePlayer[new_jx][new_jy] = player->element_nr;
10443 if (player->move_delay_value_next != -1)
10445 player->move_delay_value = player->move_delay_value_next;
10446 player->move_delay_value_next = -1;
10450 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
10452 player->step_counter++;
10454 PlayerVisit[jx][jy] = FrameCounter;
10456 #if USE_UFAST_PLAYER_EXIT_BUGFIX
10457 player->is_moving = TRUE;
10461 /* should better be called in MovePlayer(), but this breaks some tapes */
10462 ScrollPlayer(player, SCROLL_INIT);
10468 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
10470 int jx = player->jx, jy = player->jy;
10471 int old_jx = jx, old_jy = jy;
10472 int moved = MP_NO_ACTION;
10474 if (!player->active)
10479 if (player->MovPos == 0)
10481 player->is_moving = FALSE;
10482 player->is_digging = FALSE;
10483 player->is_collecting = FALSE;
10484 player->is_snapping = FALSE;
10485 player->is_pushing = FALSE;
10491 if (player->move_delay > 0)
10494 player->move_delay = -1; /* set to "uninitialized" value */
10496 /* store if player is automatically moved to next field */
10497 player->is_auto_moving = (player->programmed_action != MV_NONE);
10499 /* remove the last programmed player action */
10500 player->programmed_action = 0;
10502 if (player->MovPos)
10504 /* should only happen if pre-1.2 tape recordings are played */
10505 /* this is only for backward compatibility */
10507 int original_move_delay_value = player->move_delay_value;
10510 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
10514 /* scroll remaining steps with finest movement resolution */
10515 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
10517 while (player->MovPos)
10519 ScrollPlayer(player, SCROLL_GO_ON);
10520 ScrollScreen(NULL, SCROLL_GO_ON);
10522 AdvanceFrameAndPlayerCounters(player->index_nr);
10528 player->move_delay_value = original_move_delay_value;
10531 player->is_active = FALSE;
10533 if (player->last_move_dir & MV_HORIZONTAL)
10535 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
10536 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
10540 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
10541 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
10544 #if USE_FIXED_BORDER_RUNNING_GFX
10545 if (!moved && !player->is_active)
10547 player->is_moving = FALSE;
10548 player->is_digging = FALSE;
10549 player->is_collecting = FALSE;
10550 player->is_snapping = FALSE;
10551 player->is_pushing = FALSE;
10559 if (moved & MP_MOVING && !ScreenMovPos &&
10560 (player->index_nr == game.centered_player_nr ||
10561 game.centered_player_nr == -1))
10563 if (moved & MP_MOVING && !ScreenMovPos &&
10564 (player == local_player || !options.network))
10567 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
10568 int offset = (setup.scroll_delay ? 3 : 0);
10570 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
10572 /* actual player has left the screen -- scroll in that direction */
10573 if (jx != old_jx) /* player has moved horizontally */
10574 scroll_x += (jx - old_jx);
10575 else /* player has moved vertically */
10576 scroll_y += (jy - old_jy);
10580 if (jx != old_jx) /* player has moved horizontally */
10582 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
10583 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
10584 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
10586 /* don't scroll over playfield boundaries */
10587 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
10588 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
10590 /* don't scroll more than one field at a time */
10591 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
10593 /* don't scroll against the player's moving direction */
10594 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
10595 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
10596 scroll_x = old_scroll_x;
10598 else /* player has moved vertically */
10600 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
10601 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
10602 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
10604 /* don't scroll over playfield boundaries */
10605 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
10606 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
10608 /* don't scroll more than one field at a time */
10609 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
10611 /* don't scroll against the player's moving direction */
10612 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
10613 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
10614 scroll_y = old_scroll_y;
10618 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
10621 if (!options.network && game.centered_player_nr == -1 &&
10622 !AllPlayersInVisibleScreen())
10624 scroll_x = old_scroll_x;
10625 scroll_y = old_scroll_y;
10629 if (!options.network && !AllPlayersInVisibleScreen())
10631 scroll_x = old_scroll_x;
10632 scroll_y = old_scroll_y;
10637 ScrollScreen(player, SCROLL_INIT);
10638 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
10643 player->StepFrame = 0;
10645 if (moved & MP_MOVING)
10647 if (old_jx != jx && old_jy == jy)
10648 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
10649 else if (old_jx == jx && old_jy != jy)
10650 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
10652 DrawLevelField(jx, jy); /* for "crumbled sand" */
10654 player->last_move_dir = player->MovDir;
10655 player->is_moving = TRUE;
10656 player->is_snapping = FALSE;
10657 player->is_switching = FALSE;
10658 player->is_dropping = FALSE;
10659 player->is_dropping_pressed = FALSE;
10660 player->drop_pressed_delay = 0;
10663 /* should better be called here than above, but this breaks some tapes */
10664 ScrollPlayer(player, SCROLL_INIT);
10669 CheckGravityMovementWhenNotMoving(player);
10671 player->is_moving = FALSE;
10673 /* at this point, the player is allowed to move, but cannot move right now
10674 (e.g. because of something blocking the way) -- ensure that the player
10675 is also allowed to move in the next frame (in old versions before 3.1.1,
10676 the player was forced to wait again for eight frames before next try) */
10678 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
10679 player->move_delay = 0; /* allow direct movement in the next frame */
10682 if (player->move_delay == -1) /* not yet initialized by DigField() */
10683 player->move_delay = player->move_delay_value;
10685 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10687 TestIfPlayerTouchesBadThing(jx, jy);
10688 TestIfPlayerTouchesCustomElement(jx, jy);
10691 if (!player->active)
10692 RemovePlayer(player);
10697 void ScrollPlayer(struct PlayerInfo *player, int mode)
10699 int jx = player->jx, jy = player->jy;
10700 int last_jx = player->last_jx, last_jy = player->last_jy;
10701 int move_stepsize = TILEX / player->move_delay_value;
10703 #if USE_NEW_PLAYER_SPEED
10704 if (!player->active)
10707 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
10710 if (!player->active || player->MovPos == 0)
10714 if (mode == SCROLL_INIT)
10716 player->actual_frame_counter = FrameCounter;
10717 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10719 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
10720 Feld[last_jx][last_jy] == EL_EMPTY)
10722 int last_field_block_delay = 0; /* start with no blocking at all */
10723 int block_delay_adjustment = player->block_delay_adjustment;
10725 /* if player blocks last field, add delay for exactly one move */
10726 if (player->block_last_field)
10728 last_field_block_delay += player->move_delay_value;
10730 /* when blocking enabled, prevent moving up despite gravity */
10731 #if USE_PLAYER_GRAVITY
10732 if (player->gravity && player->MovDir == MV_UP)
10733 block_delay_adjustment = -1;
10735 if (game.gravity && player->MovDir == MV_UP)
10736 block_delay_adjustment = -1;
10740 /* add block delay adjustment (also possible when not blocking) */
10741 last_field_block_delay += block_delay_adjustment;
10743 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
10744 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
10747 #if USE_NEW_PLAYER_SPEED
10748 if (player->MovPos != 0) /* player has not yet reached destination */
10754 else if (!FrameReached(&player->actual_frame_counter, 1))
10757 #if USE_NEW_PLAYER_SPEED
10758 if (player->MovPos != 0)
10760 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
10761 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10763 /* before DrawPlayer() to draw correct player graphic for this case */
10764 if (player->MovPos == 0)
10765 CheckGravityMovement(player);
10768 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
10769 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10771 /* before DrawPlayer() to draw correct player graphic for this case */
10772 if (player->MovPos == 0)
10773 CheckGravityMovement(player);
10776 if (player->MovPos == 0) /* player reached destination field */
10778 if (player->move_delay_reset_counter > 0)
10780 player->move_delay_reset_counter--;
10782 if (player->move_delay_reset_counter == 0)
10784 /* continue with normal speed after quickly moving through gate */
10785 HALVE_PLAYER_SPEED(player);
10787 /* be able to make the next move without delay */
10788 player->move_delay = 0;
10792 player->last_jx = jx;
10793 player->last_jy = jy;
10795 if (Feld[jx][jy] == EL_EXIT_OPEN ||
10796 Feld[jx][jy] == EL_EM_EXIT_OPEN ||
10797 Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
10798 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
10799 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
10800 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
10802 DrawPlayer(player); /* needed here only to cleanup last field */
10803 RemovePlayer(player);
10805 if (local_player->friends_still_needed == 0 ||
10806 IS_SP_ELEMENT(Feld[jx][jy]))
10807 PlayerWins(player);
10810 /* this breaks one level: "machine", level 000 */
10812 int move_direction = player->MovDir;
10813 int enter_side = MV_DIR_OPPOSITE(move_direction);
10814 int leave_side = move_direction;
10815 int old_jx = last_jx;
10816 int old_jy = last_jy;
10817 int old_element = Feld[old_jx][old_jy];
10818 int new_element = Feld[jx][jy];
10820 if (IS_CUSTOM_ELEMENT(old_element))
10821 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
10823 player->index_bit, leave_side);
10825 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
10826 CE_PLAYER_LEAVES_X,
10827 player->index_bit, leave_side);
10829 if (IS_CUSTOM_ELEMENT(new_element))
10830 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
10831 player->index_bit, enter_side);
10833 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
10834 CE_PLAYER_ENTERS_X,
10835 player->index_bit, enter_side);
10837 CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
10838 CE_MOVE_OF_X, move_direction);
10841 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10843 TestIfPlayerTouchesBadThing(jx, jy);
10844 TestIfPlayerTouchesCustomElement(jx, jy);
10846 /* needed because pushed element has not yet reached its destination,
10847 so it would trigger a change event at its previous field location */
10848 if (!player->is_pushing)
10849 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
10851 if (!player->active)
10852 RemovePlayer(player);
10855 if (!local_player->LevelSolved && level.use_step_counter)
10865 if (TimeLeft <= 10 && setup.time_limit)
10866 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
10868 DrawGameValue_Time(TimeLeft);
10870 if (!TimeLeft && setup.time_limit)
10871 for (i = 0; i < MAX_PLAYERS; i++)
10872 KillPlayer(&stored_player[i]);
10874 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
10875 DrawGameValue_Time(TimePlayed);
10878 if (tape.single_step && tape.recording && !tape.pausing &&
10879 !player->programmed_action)
10880 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10884 void ScrollScreen(struct PlayerInfo *player, int mode)
10886 static unsigned long screen_frame_counter = 0;
10888 if (mode == SCROLL_INIT)
10890 /* set scrolling step size according to actual player's moving speed */
10891 ScrollStepSize = TILEX / player->move_delay_value;
10893 screen_frame_counter = FrameCounter;
10894 ScreenMovDir = player->MovDir;
10895 ScreenMovPos = player->MovPos;
10896 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
10899 else if (!FrameReached(&screen_frame_counter, 1))
10904 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
10905 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
10906 redraw_mask |= REDRAW_FIELD;
10909 ScreenMovDir = MV_NONE;
10912 void TestIfPlayerTouchesCustomElement(int x, int y)
10914 static int xy[4][2] =
10921 static int trigger_sides[4][2] =
10923 /* center side border side */
10924 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
10925 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
10926 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
10927 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
10929 static int touch_dir[4] =
10931 MV_LEFT | MV_RIGHT,
10936 int center_element = Feld[x][y]; /* should always be non-moving! */
10939 for (i = 0; i < NUM_DIRECTIONS; i++)
10941 int xx = x + xy[i][0];
10942 int yy = y + xy[i][1];
10943 int center_side = trigger_sides[i][0];
10944 int border_side = trigger_sides[i][1];
10945 int border_element;
10947 if (!IN_LEV_FIELD(xx, yy))
10950 if (IS_PLAYER(x, y))
10952 struct PlayerInfo *player = PLAYERINFO(x, y);
10954 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10955 border_element = Feld[xx][yy]; /* may be moving! */
10956 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
10957 border_element = Feld[xx][yy];
10958 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
10959 border_element = MovingOrBlocked2Element(xx, yy);
10961 continue; /* center and border element do not touch */
10963 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
10964 player->index_bit, border_side);
10965 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
10966 CE_PLAYER_TOUCHES_X,
10967 player->index_bit, border_side);
10969 else if (IS_PLAYER(xx, yy))
10971 struct PlayerInfo *player = PLAYERINFO(xx, yy);
10973 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10975 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
10976 continue; /* center and border element do not touch */
10979 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
10980 player->index_bit, center_side);
10981 CheckTriggeredElementChangeByPlayer(x, y, center_element,
10982 CE_PLAYER_TOUCHES_X,
10983 player->index_bit, center_side);
10989 #if USE_ELEMENT_TOUCHING_BUGFIX
10991 void TestIfElementTouchesCustomElement(int x, int y)
10993 static int xy[4][2] =
11000 static int trigger_sides[4][2] =
11002 /* center side border side */
11003 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
11004 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
11005 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
11006 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
11008 static int touch_dir[4] =
11010 MV_LEFT | MV_RIGHT,
11015 boolean change_center_element = FALSE;
11016 int center_element = Feld[x][y]; /* should always be non-moving! */
11017 int border_element_old[NUM_DIRECTIONS];
11020 for (i = 0; i < NUM_DIRECTIONS; i++)
11022 int xx = x + xy[i][0];
11023 int yy = y + xy[i][1];
11024 int border_element;
11026 border_element_old[i] = -1;
11028 if (!IN_LEV_FIELD(xx, yy))
11031 if (game.engine_version < VERSION_IDENT(3,0,7,0))
11032 border_element = Feld[xx][yy]; /* may be moving! */
11033 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
11034 border_element = Feld[xx][yy];
11035 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
11036 border_element = MovingOrBlocked2Element(xx, yy);
11038 continue; /* center and border element do not touch */
11040 border_element_old[i] = border_element;
11043 for (i = 0; i < NUM_DIRECTIONS; i++)
11045 int xx = x + xy[i][0];
11046 int yy = y + xy[i][1];
11047 int center_side = trigger_sides[i][0];
11048 int border_element = border_element_old[i];
11050 if (border_element == -1)
11053 /* check for change of border element */
11054 CheckElementChangeBySide(xx, yy, border_element, center_element,
11055 CE_TOUCHING_X, center_side);
11058 for (i = 0; i < NUM_DIRECTIONS; i++)
11060 int border_side = trigger_sides[i][1];
11061 int border_element = border_element_old[i];
11063 if (border_element == -1)
11066 /* check for change of center element (but change it only once) */
11067 if (!change_center_element)
11068 change_center_element =
11069 CheckElementChangeBySide(x, y, center_element, border_element,
11070 CE_TOUCHING_X, border_side);
11076 void TestIfElementTouchesCustomElement_OLD(int x, int y)
11078 static int xy[4][2] =
11085 static int trigger_sides[4][2] =
11087 /* center side border side */
11088 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
11089 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
11090 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
11091 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
11093 static int touch_dir[4] =
11095 MV_LEFT | MV_RIGHT,
11100 boolean change_center_element = FALSE;
11101 int center_element = Feld[x][y]; /* should always be non-moving! */
11104 for (i = 0; i < NUM_DIRECTIONS; i++)
11106 int xx = x + xy[i][0];
11107 int yy = y + xy[i][1];
11108 int center_side = trigger_sides[i][0];
11109 int border_side = trigger_sides[i][1];
11110 int border_element;
11112 if (!IN_LEV_FIELD(xx, yy))
11115 if (game.engine_version < VERSION_IDENT(3,0,7,0))
11116 border_element = Feld[xx][yy]; /* may be moving! */
11117 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
11118 border_element = Feld[xx][yy];
11119 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
11120 border_element = MovingOrBlocked2Element(xx, yy);
11122 continue; /* center and border element do not touch */
11124 /* check for change of center element (but change it only once) */
11125 if (!change_center_element)
11126 change_center_element =
11127 CheckElementChangeBySide(x, y, center_element, border_element,
11128 CE_TOUCHING_X, border_side);
11130 /* check for change of border element */
11131 CheckElementChangeBySide(xx, yy, border_element, center_element,
11132 CE_TOUCHING_X, center_side);
11138 void TestIfElementHitsCustomElement(int x, int y, int direction)
11140 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
11141 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
11142 int hitx = x + dx, hity = y + dy;
11143 int hitting_element = Feld[x][y];
11144 int touched_element;
11146 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
11149 touched_element = (IN_LEV_FIELD(hitx, hity) ?
11150 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
11152 if (IN_LEV_FIELD(hitx, hity))
11154 int opposite_direction = MV_DIR_OPPOSITE(direction);
11155 int hitting_side = direction;
11156 int touched_side = opposite_direction;
11157 boolean object_hit = (!IS_MOVING(hitx, hity) ||
11158 MovDir[hitx][hity] != direction ||
11159 ABS(MovPos[hitx][hity]) <= TILEY / 2);
11165 CheckElementChangeBySide(x, y, hitting_element, touched_element,
11166 CE_HITTING_X, touched_side);
11168 CheckElementChangeBySide(hitx, hity, touched_element,
11169 hitting_element, CE_HIT_BY_X, hitting_side);
11171 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
11172 CE_HIT_BY_SOMETHING, opposite_direction);
11176 /* "hitting something" is also true when hitting the playfield border */
11177 CheckElementChangeBySide(x, y, hitting_element, touched_element,
11178 CE_HITTING_SOMETHING, direction);
11182 void TestIfElementSmashesCustomElement(int x, int y, int direction)
11184 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
11185 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
11186 int hitx = x + dx, hity = y + dy;
11187 int hitting_element = Feld[x][y];
11188 int touched_element;
11190 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
11191 !IS_FREE(hitx, hity) &&
11192 (!IS_MOVING(hitx, hity) ||
11193 MovDir[hitx][hity] != direction ||
11194 ABS(MovPos[hitx][hity]) <= TILEY / 2));
11197 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
11201 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
11205 touched_element = (IN_LEV_FIELD(hitx, hity) ?
11206 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
11208 CheckElementChangeBySide(x, y, hitting_element, touched_element,
11209 EP_CAN_SMASH_EVERYTHING, direction);
11211 if (IN_LEV_FIELD(hitx, hity))
11213 int opposite_direction = MV_DIR_OPPOSITE(direction);
11214 int hitting_side = direction;
11215 int touched_side = opposite_direction;
11217 int touched_element = MovingOrBlocked2Element(hitx, hity);
11220 boolean object_hit = (!IS_MOVING(hitx, hity) ||
11221 MovDir[hitx][hity] != direction ||
11222 ABS(MovPos[hitx][hity]) <= TILEY / 2);
11231 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
11232 CE_SMASHED_BY_SOMETHING, opposite_direction);
11234 CheckElementChangeBySide(x, y, hitting_element, touched_element,
11235 CE_OTHER_IS_SMASHING, touched_side);
11237 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
11238 CE_OTHER_GETS_SMASHED, hitting_side);
11244 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
11246 int i, kill_x = -1, kill_y = -1;
11248 int bad_element = -1;
11249 static int test_xy[4][2] =
11256 static int test_dir[4] =
11264 for (i = 0; i < NUM_DIRECTIONS; i++)
11266 int test_x, test_y, test_move_dir, test_element;
11268 test_x = good_x + test_xy[i][0];
11269 test_y = good_y + test_xy[i][1];
11271 if (!IN_LEV_FIELD(test_x, test_y))
11275 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
11277 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
11279 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
11280 2nd case: DONT_TOUCH style bad thing does not move away from good thing
11282 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
11283 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
11287 bad_element = test_element;
11293 if (kill_x != -1 || kill_y != -1)
11295 if (IS_PLAYER(good_x, good_y))
11297 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
11299 if (player->shield_deadly_time_left > 0 &&
11300 !IS_INDESTRUCTIBLE(bad_element))
11301 Bang(kill_x, kill_y);
11302 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
11303 KillPlayer(player);
11306 Bang(good_x, good_y);
11310 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
11312 int i, kill_x = -1, kill_y = -1;
11313 int bad_element = Feld[bad_x][bad_y];
11314 static int test_xy[4][2] =
11321 static int touch_dir[4] =
11323 MV_LEFT | MV_RIGHT,
11328 static int test_dir[4] =
11336 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
11339 for (i = 0; i < NUM_DIRECTIONS; i++)
11341 int test_x, test_y, test_move_dir, test_element;
11343 test_x = bad_x + test_xy[i][0];
11344 test_y = bad_y + test_xy[i][1];
11345 if (!IN_LEV_FIELD(test_x, test_y))
11349 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
11351 test_element = Feld[test_x][test_y];
11353 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
11354 2nd case: DONT_TOUCH style bad thing does not move away from good thing
11356 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
11357 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
11359 /* good thing is player or penguin that does not move away */
11360 if (IS_PLAYER(test_x, test_y))
11362 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
11364 if (bad_element == EL_ROBOT && player->is_moving)
11365 continue; /* robot does not kill player if he is moving */
11367 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
11369 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
11370 continue; /* center and border element do not touch */
11377 else if (test_element == EL_PENGUIN)
11386 if (kill_x != -1 || kill_y != -1)
11388 if (IS_PLAYER(kill_x, kill_y))
11390 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
11392 if (player->shield_deadly_time_left > 0 &&
11393 !IS_INDESTRUCTIBLE(bad_element))
11394 Bang(bad_x, bad_y);
11395 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
11396 KillPlayer(player);
11399 Bang(kill_x, kill_y);
11403 void TestIfPlayerTouchesBadThing(int x, int y)
11405 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
11408 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
11410 TestIfGoodThingHitsBadThing(x, y, move_dir);
11413 void TestIfBadThingTouchesPlayer(int x, int y)
11415 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
11418 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
11420 TestIfBadThingHitsGoodThing(x, y, move_dir);
11423 void TestIfFriendTouchesBadThing(int x, int y)
11425 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
11428 void TestIfBadThingTouchesFriend(int x, int y)
11430 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
11433 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
11435 int i, kill_x = bad_x, kill_y = bad_y;
11436 static int xy[4][2] =
11444 for (i = 0; i < NUM_DIRECTIONS; i++)
11448 x = bad_x + xy[i][0];
11449 y = bad_y + xy[i][1];
11450 if (!IN_LEV_FIELD(x, y))
11453 element = Feld[x][y];
11454 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
11455 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
11463 if (kill_x != bad_x || kill_y != bad_y)
11464 Bang(bad_x, bad_y);
11467 void KillPlayer(struct PlayerInfo *player)
11469 int jx = player->jx, jy = player->jy;
11471 if (!player->active)
11474 /* the following code was introduced to prevent an infinite loop when calling
11476 -> CheckTriggeredElementChangeExt()
11477 -> ExecuteCustomElementAction()
11479 -> (infinitely repeating the above sequence of function calls)
11480 which occurs when killing the player while having a CE with the setting
11481 "kill player X when explosion of <player X>"; the solution using a new
11482 field "player->killed" was chosen for backwards compatibility, although
11483 clever use of the fields "player->active" etc. would probably also work */
11485 if (player->killed)
11489 player->killed = TRUE;
11491 /* remove accessible field at the player's position */
11492 Feld[jx][jy] = EL_EMPTY;
11494 /* deactivate shield (else Bang()/Explode() would not work right) */
11495 player->shield_normal_time_left = 0;
11496 player->shield_deadly_time_left = 0;
11499 BuryPlayer(player);
11502 static void KillPlayerUnlessEnemyProtected(int x, int y)
11504 if (!PLAYER_ENEMY_PROTECTED(x, y))
11505 KillPlayer(PLAYERINFO(x, y));
11508 static void KillPlayerUnlessExplosionProtected(int x, int y)
11510 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
11511 KillPlayer(PLAYERINFO(x, y));
11514 void BuryPlayer(struct PlayerInfo *player)
11516 int jx = player->jx, jy = player->jy;
11518 if (!player->active)
11521 PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
11522 PlayLevelSound(jx, jy, SND_GAME_LOSING);
11524 player->GameOver = TRUE;
11525 RemovePlayer(player);
11528 void RemovePlayer(struct PlayerInfo *player)
11530 int jx = player->jx, jy = player->jy;
11531 int i, found = FALSE;
11533 player->present = FALSE;
11534 player->active = FALSE;
11536 if (!ExplodeField[jx][jy])
11537 StorePlayer[jx][jy] = 0;
11539 if (player->is_moving)
11540 DrawLevelField(player->last_jx, player->last_jy);
11542 for (i = 0; i < MAX_PLAYERS; i++)
11543 if (stored_player[i].active)
11547 AllPlayersGone = TRUE;
11553 #if USE_NEW_SNAP_DELAY
11554 static void setFieldForSnapping(int x, int y, int element, int direction)
11556 struct ElementInfo *ei = &element_info[element];
11557 int direction_bit = MV_DIR_TO_BIT(direction);
11558 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
11559 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
11560 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
11562 Feld[x][y] = EL_ELEMENT_SNAPPING;
11563 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
11565 ResetGfxAnimation(x, y);
11567 GfxElement[x][y] = element;
11568 GfxAction[x][y] = action;
11569 GfxDir[x][y] = direction;
11570 GfxFrame[x][y] = -1;
11575 =============================================================================
11576 checkDiagonalPushing()
11577 -----------------------------------------------------------------------------
11578 check if diagonal input device direction results in pushing of object
11579 (by checking if the alternative direction is walkable, diggable, ...)
11580 =============================================================================
11583 static boolean checkDiagonalPushing(struct PlayerInfo *player,
11584 int x, int y, int real_dx, int real_dy)
11586 int jx, jy, dx, dy, xx, yy;
11588 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
11591 /* diagonal direction: check alternative direction */
11596 xx = jx + (dx == 0 ? real_dx : 0);
11597 yy = jy + (dy == 0 ? real_dy : 0);
11599 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
11603 =============================================================================
11605 -----------------------------------------------------------------------------
11606 x, y: field next to player (non-diagonal) to try to dig to
11607 real_dx, real_dy: direction as read from input device (can be diagonal)
11608 =============================================================================
11611 int DigField(struct PlayerInfo *player,
11612 int oldx, int oldy, int x, int y,
11613 int real_dx, int real_dy, int mode)
11615 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
11616 boolean player_was_pushing = player->is_pushing;
11617 boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
11618 boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
11619 int jx = oldx, jy = oldy;
11620 int dx = x - jx, dy = y - jy;
11621 int nextx = x + dx, nexty = y + dy;
11622 int move_direction = (dx == -1 ? MV_LEFT :
11623 dx == +1 ? MV_RIGHT :
11625 dy == +1 ? MV_DOWN : MV_NONE);
11626 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
11627 int dig_side = MV_DIR_OPPOSITE(move_direction);
11628 int old_element = Feld[jx][jy];
11629 #if USE_FIXED_DONT_RUN_INTO
11630 int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
11636 if (is_player) /* function can also be called by EL_PENGUIN */
11638 if (player->MovPos == 0)
11640 player->is_digging = FALSE;
11641 player->is_collecting = FALSE;
11644 if (player->MovPos == 0) /* last pushing move finished */
11645 player->is_pushing = FALSE;
11647 if (mode == DF_NO_PUSH) /* player just stopped pushing */
11649 player->is_switching = FALSE;
11650 player->push_delay = -1;
11652 return MP_NO_ACTION;
11656 #if !USE_FIXED_DONT_RUN_INTO
11657 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
11658 return MP_NO_ACTION;
11661 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
11662 old_element = Back[jx][jy];
11664 /* in case of element dropped at player position, check background */
11665 else if (Back[jx][jy] != EL_EMPTY &&
11666 game.engine_version >= VERSION_IDENT(2,2,0,0))
11667 old_element = Back[jx][jy];
11669 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
11670 return MP_NO_ACTION; /* field has no opening in this direction */
11672 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
11673 return MP_NO_ACTION; /* field has no opening in this direction */
11675 #if USE_FIXED_DONT_RUN_INTO
11676 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
11680 Feld[jx][jy] = player->artwork_element;
11681 InitMovingField(jx, jy, MV_DOWN);
11682 Store[jx][jy] = EL_ACID;
11683 ContinueMoving(jx, jy);
11684 BuryPlayer(player);
11686 return MP_DONT_RUN_INTO;
11690 #if USE_FIXED_DONT_RUN_INTO
11691 if (player_can_move && DONT_RUN_INTO(element))
11693 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
11695 return MP_DONT_RUN_INTO;
11699 #if USE_FIXED_DONT_RUN_INTO
11700 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
11701 return MP_NO_ACTION;
11704 #if !USE_FIXED_DONT_RUN_INTO
11705 element = Feld[x][y];
11708 collect_count = element_info[element].collect_count_initial;
11710 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
11711 return MP_NO_ACTION;
11713 if (game.engine_version < VERSION_IDENT(2,2,0,0))
11714 player_can_move = player_can_move_or_snap;
11716 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
11717 game.engine_version >= VERSION_IDENT(2,2,0,0))
11719 CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
11720 player->index_bit, dig_side);
11721 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
11722 player->index_bit, dig_side);
11724 if (element == EL_DC_LANDMINE)
11727 if (Feld[x][y] != element) /* field changed by snapping */
11730 return MP_NO_ACTION;
11733 #if USE_PLAYER_GRAVITY
11734 if (player->gravity && is_player && !player->is_auto_moving &&
11735 canFallDown(player) && move_direction != MV_DOWN &&
11736 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
11737 return MP_NO_ACTION; /* player cannot walk here due to gravity */
11739 if (game.gravity && is_player && !player->is_auto_moving &&
11740 canFallDown(player) && move_direction != MV_DOWN &&
11741 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
11742 return MP_NO_ACTION; /* player cannot walk here due to gravity */
11745 if (player_can_move &&
11746 IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
11748 int sound_element = SND_ELEMENT(element);
11749 int sound_action = ACTION_WALKING;
11751 if (IS_RND_GATE(element))
11753 if (!player->key[RND_GATE_NR(element)])
11754 return MP_NO_ACTION;
11756 else if (IS_RND_GATE_GRAY(element))
11758 if (!player->key[RND_GATE_GRAY_NR(element)])
11759 return MP_NO_ACTION;
11761 else if (IS_RND_GATE_GRAY_ACTIVE(element))
11763 if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
11764 return MP_NO_ACTION;
11766 else if (element == EL_EXIT_OPEN ||
11767 element == EL_EM_EXIT_OPEN ||
11768 element == EL_STEEL_EXIT_OPEN ||
11769 element == EL_EM_STEEL_EXIT_OPEN ||
11770 element == EL_SP_EXIT_OPEN ||
11771 element == EL_SP_EXIT_OPENING)
11773 sound_action = ACTION_PASSING; /* player is passing exit */
11775 else if (element == EL_EMPTY)
11777 sound_action = ACTION_MOVING; /* nothing to walk on */
11780 /* play sound from background or player, whatever is available */
11781 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
11782 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
11784 PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
11786 else if (player_can_move &&
11787 IS_PASSABLE(element) && canPassField(x, y, move_direction))
11789 if (!ACCESS_FROM(element, opposite_direction))
11790 return MP_NO_ACTION; /* field not accessible from this direction */
11792 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
11793 return MP_NO_ACTION;
11795 if (IS_EM_GATE(element))
11797 if (!player->key[EM_GATE_NR(element)])
11798 return MP_NO_ACTION;
11800 else if (IS_EM_GATE_GRAY(element))
11802 if (!player->key[EM_GATE_GRAY_NR(element)])
11803 return MP_NO_ACTION;
11805 else if (IS_EM_GATE_GRAY_ACTIVE(element))
11807 if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
11808 return MP_NO_ACTION;
11810 else if (IS_EMC_GATE(element))
11812 if (!player->key[EMC_GATE_NR(element)])
11813 return MP_NO_ACTION;
11815 else if (IS_EMC_GATE_GRAY(element))
11817 if (!player->key[EMC_GATE_GRAY_NR(element)])
11818 return MP_NO_ACTION;
11820 else if (IS_EMC_GATE_GRAY_ACTIVE(element))
11822 if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
11823 return MP_NO_ACTION;
11825 else if (IS_SP_PORT(element))
11827 if (element == EL_SP_GRAVITY_PORT_LEFT ||
11828 element == EL_SP_GRAVITY_PORT_RIGHT ||
11829 element == EL_SP_GRAVITY_PORT_UP ||
11830 element == EL_SP_GRAVITY_PORT_DOWN)
11831 #if USE_PLAYER_GRAVITY
11832 player->gravity = !player->gravity;
11834 game.gravity = !game.gravity;
11836 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
11837 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
11838 element == EL_SP_GRAVITY_ON_PORT_UP ||
11839 element == EL_SP_GRAVITY_ON_PORT_DOWN)
11840 #if USE_PLAYER_GRAVITY
11841 player->gravity = TRUE;
11843 game.gravity = TRUE;
11845 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
11846 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
11847 element == EL_SP_GRAVITY_OFF_PORT_UP ||
11848 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
11849 #if USE_PLAYER_GRAVITY
11850 player->gravity = FALSE;
11852 game.gravity = FALSE;
11856 /* automatically move to the next field with double speed */
11857 player->programmed_action = move_direction;
11859 if (player->move_delay_reset_counter == 0)
11861 player->move_delay_reset_counter = 2; /* two double speed steps */
11863 DOUBLE_PLAYER_SPEED(player);
11866 PlayLevelSoundAction(x, y, ACTION_PASSING);
11868 else if (player_can_move_or_snap && IS_DIGGABLE(element))
11872 if (mode != DF_SNAP)
11874 GfxElement[x][y] = GFX_ELEMENT(element);
11875 player->is_digging = TRUE;
11878 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
11880 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
11881 player->index_bit, dig_side);
11883 if (mode == DF_SNAP)
11885 #if USE_NEW_SNAP_DELAY
11886 if (level.block_snap_field)
11887 setFieldForSnapping(x, y, element, move_direction);
11889 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11891 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11894 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
11895 player->index_bit, dig_side);
11898 else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
11902 if (is_player && mode != DF_SNAP)
11904 GfxElement[x][y] = element;
11905 player->is_collecting = TRUE;
11908 if (element == EL_SPEED_PILL)
11910 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
11912 else if (element == EL_EXTRA_TIME && level.time > 0)
11914 TimeLeft += level.extra_time;
11915 DrawGameValue_Time(TimeLeft);
11917 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
11919 player->shield_normal_time_left += level.shield_normal_time;
11920 if (element == EL_SHIELD_DEADLY)
11921 player->shield_deadly_time_left += level.shield_deadly_time;
11923 else if (element == EL_DYNAMITE ||
11924 element == EL_EM_DYNAMITE ||
11925 element == EL_SP_DISK_RED)
11927 if (player->inventory_size < MAX_INVENTORY_SIZE)
11928 player->inventory_element[player->inventory_size++] = element;
11930 DrawGameDoorValues();
11932 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
11934 player->dynabomb_count++;
11935 player->dynabombs_left++;
11937 else if (element == EL_DYNABOMB_INCREASE_SIZE)
11939 player->dynabomb_size++;
11941 else if (element == EL_DYNABOMB_INCREASE_POWER)
11943 player->dynabomb_xl = TRUE;
11945 else if (IS_KEY(element))
11947 player->key[KEY_NR(element)] = TRUE;
11949 DrawGameDoorValues();
11951 else if (IS_ENVELOPE(element))
11953 player->show_envelope = element;
11955 else if (element == EL_EMC_LENSES)
11957 game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
11959 RedrawAllInvisibleElementsForLenses();
11961 else if (element == EL_EMC_MAGNIFIER)
11963 game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
11965 RedrawAllInvisibleElementsForMagnifier();
11967 else if (IS_DROPPABLE(element) ||
11968 IS_THROWABLE(element)) /* can be collected and dropped */
11972 if (collect_count == 0)
11973 player->inventory_infinite_element = element;
11975 for (i = 0; i < collect_count; i++)
11976 if (player->inventory_size < MAX_INVENTORY_SIZE)
11977 player->inventory_element[player->inventory_size++] = element;
11979 DrawGameDoorValues();
11981 else if (collect_count > 0)
11983 local_player->gems_still_needed -= collect_count;
11984 if (local_player->gems_still_needed < 0)
11985 local_player->gems_still_needed = 0;
11987 DrawGameValue_Emeralds(local_player->gems_still_needed);
11990 RaiseScoreElement(element);
11991 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
11994 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
11995 player->index_bit, dig_side);
11997 if (mode == DF_SNAP)
11999 #if USE_NEW_SNAP_DELAY
12000 if (level.block_snap_field)
12001 setFieldForSnapping(x, y, element, move_direction);
12003 TestIfElementTouchesCustomElement(x, y); /* for empty space */
12005 TestIfElementTouchesCustomElement(x, y); /* for empty space */
12008 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
12009 player->index_bit, dig_side);
12012 else if (player_can_move_or_snap && IS_PUSHABLE(element))
12014 if (mode == DF_SNAP && element != EL_BD_ROCK)
12015 return MP_NO_ACTION;
12017 if (CAN_FALL(element) && dy)
12018 return MP_NO_ACTION;
12020 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
12021 !(element == EL_SPRING && level.use_spring_bug))
12022 return MP_NO_ACTION;
12024 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
12025 ((move_direction & MV_VERTICAL &&
12026 ((element_info[element].move_pattern & MV_LEFT &&
12027 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
12028 (element_info[element].move_pattern & MV_RIGHT &&
12029 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
12030 (move_direction & MV_HORIZONTAL &&
12031 ((element_info[element].move_pattern & MV_UP &&
12032 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
12033 (element_info[element].move_pattern & MV_DOWN &&
12034 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
12035 return MP_NO_ACTION;
12037 /* do not push elements already moving away faster than player */
12038 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
12039 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
12040 return MP_NO_ACTION;
12042 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
12044 if (player->push_delay_value == -1 || !player_was_pushing)
12045 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12047 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
12049 if (player->push_delay_value == -1)
12050 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12052 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
12054 if (!player->is_pushing)
12055 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12058 player->is_pushing = TRUE;
12059 player->is_active = TRUE;
12061 if (!(IN_LEV_FIELD(nextx, nexty) &&
12062 (IS_FREE(nextx, nexty) ||
12063 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
12064 IS_SB_ELEMENT(element)))))
12065 return MP_NO_ACTION;
12067 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
12068 return MP_NO_ACTION;
12070 if (player->push_delay == -1) /* new pushing; restart delay */
12071 player->push_delay = 0;
12073 if (player->push_delay < player->push_delay_value &&
12074 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
12075 element != EL_SPRING && element != EL_BALLOON)
12077 /* make sure that there is no move delay before next try to push */
12078 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
12079 player->move_delay = 0;
12081 return MP_NO_ACTION;
12084 if (IS_SB_ELEMENT(element))
12086 if (element == EL_SOKOBAN_FIELD_FULL)
12088 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
12089 local_player->sokobanfields_still_needed++;
12092 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
12094 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
12095 local_player->sokobanfields_still_needed--;
12098 Feld[x][y] = EL_SOKOBAN_OBJECT;
12100 if (Back[x][y] == Back[nextx][nexty])
12101 PlayLevelSoundAction(x, y, ACTION_PUSHING);
12102 else if (Back[x][y] != 0)
12103 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
12106 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
12109 if (local_player->sokobanfields_still_needed == 0 &&
12110 game.emulation == EMU_SOKOBAN)
12112 PlayerWins(player);
12114 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
12118 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12120 InitMovingField(x, y, move_direction);
12121 GfxAction[x][y] = ACTION_PUSHING;
12123 if (mode == DF_SNAP)
12124 ContinueMoving(x, y);
12126 MovPos[x][y] = (dx != 0 ? dx : dy);
12128 Pushed[x][y] = TRUE;
12129 Pushed[nextx][nexty] = TRUE;
12131 if (game.engine_version < VERSION_IDENT(2,2,0,7))
12132 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12134 player->push_delay_value = -1; /* get new value later */
12136 /* check for element change _after_ element has been pushed */
12137 if (game.use_change_when_pushing_bug)
12139 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
12140 player->index_bit, dig_side);
12141 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
12142 player->index_bit, dig_side);
12145 else if (IS_SWITCHABLE(element))
12147 if (PLAYER_SWITCHING(player, x, y))
12149 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
12150 player->index_bit, dig_side);
12155 player->is_switching = TRUE;
12156 player->switch_x = x;
12157 player->switch_y = y;
12159 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
12161 if (element == EL_ROBOT_WHEEL)
12163 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
12167 DrawLevelField(x, y);
12169 else if (element == EL_SP_TERMINAL)
12173 SCAN_PLAYFIELD(xx, yy)
12175 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
12177 else if (Feld[xx][yy] == EL_SP_TERMINAL)
12178 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
12181 else if (IS_BELT_SWITCH(element))
12183 ToggleBeltSwitch(x, y);
12185 else if (element == EL_SWITCHGATE_SWITCH_UP ||
12186 element == EL_SWITCHGATE_SWITCH_DOWN ||
12187 element == EL_DC_SWITCHGATE_SWITCH_UP ||
12188 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
12190 ToggleSwitchgateSwitch(x, y);
12192 else if (element == EL_LIGHT_SWITCH ||
12193 element == EL_LIGHT_SWITCH_ACTIVE)
12195 ToggleLightSwitch(x, y);
12197 else if (element == EL_TIMEGATE_SWITCH ||
12198 element == EL_DC_TIMEGATE_SWITCH)
12200 ActivateTimegateSwitch(x, y);
12202 else if (element == EL_BALLOON_SWITCH_LEFT ||
12203 element == EL_BALLOON_SWITCH_RIGHT ||
12204 element == EL_BALLOON_SWITCH_UP ||
12205 element == EL_BALLOON_SWITCH_DOWN ||
12206 element == EL_BALLOON_SWITCH_NONE ||
12207 element == EL_BALLOON_SWITCH_ANY)
12209 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
12210 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
12211 element == EL_BALLOON_SWITCH_UP ? MV_UP :
12212 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
12213 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
12216 else if (element == EL_LAMP)
12218 Feld[x][y] = EL_LAMP_ACTIVE;
12219 local_player->lights_still_needed--;
12221 ResetGfxAnimation(x, y);
12222 DrawLevelField(x, y);
12224 else if (element == EL_TIME_ORB_FULL)
12226 Feld[x][y] = EL_TIME_ORB_EMPTY;
12228 if (level.time > 0 || level.use_time_orb_bug)
12230 TimeLeft += level.time_orb_time;
12231 DrawGameValue_Time(TimeLeft);
12234 ResetGfxAnimation(x, y);
12235 DrawLevelField(x, y);
12237 else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
12238 element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
12242 game.ball_state = !game.ball_state;
12244 SCAN_PLAYFIELD(xx, yy)
12246 int e = Feld[xx][yy];
12248 if (game.ball_state)
12250 if (e == EL_EMC_MAGIC_BALL)
12251 CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
12252 else if (e == EL_EMC_MAGIC_BALL_SWITCH)
12253 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
12257 if (e == EL_EMC_MAGIC_BALL_ACTIVE)
12258 CreateField(xx, yy, EL_EMC_MAGIC_BALL);
12259 else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
12260 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
12265 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
12266 player->index_bit, dig_side);
12268 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
12269 player->index_bit, dig_side);
12271 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
12272 player->index_bit, dig_side);
12278 if (!PLAYER_SWITCHING(player, x, y))
12280 player->is_switching = TRUE;
12281 player->switch_x = x;
12282 player->switch_y = y;
12284 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
12285 player->index_bit, dig_side);
12286 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
12287 player->index_bit, dig_side);
12289 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
12290 player->index_bit, dig_side);
12291 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
12292 player->index_bit, dig_side);
12295 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
12296 player->index_bit, dig_side);
12297 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
12298 player->index_bit, dig_side);
12300 return MP_NO_ACTION;
12303 player->push_delay = -1;
12305 if (is_player) /* function can also be called by EL_PENGUIN */
12307 if (Feld[x][y] != element) /* really digged/collected something */
12309 player->is_collecting = !player->is_digging;
12310 player->is_active = TRUE;
12317 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
12319 int jx = player->jx, jy = player->jy;
12320 int x = jx + dx, y = jy + dy;
12321 int snap_direction = (dx == -1 ? MV_LEFT :
12322 dx == +1 ? MV_RIGHT :
12324 dy == +1 ? MV_DOWN : MV_NONE);
12325 boolean can_continue_snapping = (level.continuous_snapping &&
12326 WasJustFalling[x][y] < CHECK_DELAY_FALLING);
12328 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
12331 if (!player->active || !IN_LEV_FIELD(x, y))
12339 if (player->MovPos == 0)
12340 player->is_pushing = FALSE;
12342 player->is_snapping = FALSE;
12344 if (player->MovPos == 0)
12346 player->is_moving = FALSE;
12347 player->is_digging = FALSE;
12348 player->is_collecting = FALSE;
12354 #if USE_NEW_CONTINUOUS_SNAPPING
12355 /* prevent snapping with already pressed snap key when not allowed */
12356 if (player->is_snapping && !can_continue_snapping)
12359 if (player->is_snapping)
12363 player->MovDir = snap_direction;
12365 if (player->MovPos == 0)
12367 player->is_moving = FALSE;
12368 player->is_digging = FALSE;
12369 player->is_collecting = FALSE;
12372 player->is_dropping = FALSE;
12373 player->is_dropping_pressed = FALSE;
12374 player->drop_pressed_delay = 0;
12376 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
12379 player->is_snapping = TRUE;
12380 player->is_active = TRUE;
12382 if (player->MovPos == 0)
12384 player->is_moving = FALSE;
12385 player->is_digging = FALSE;
12386 player->is_collecting = FALSE;
12389 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
12390 DrawLevelField(player->last_jx, player->last_jy);
12392 DrawLevelField(x, y);
12397 boolean DropElement(struct PlayerInfo *player)
12399 int old_element, new_element;
12400 int dropx = player->jx, dropy = player->jy;
12401 int drop_direction = player->MovDir;
12402 int drop_side = drop_direction;
12403 int drop_element = (player->inventory_size > 0 ?
12404 player->inventory_element[player->inventory_size - 1] :
12405 player->inventory_infinite_element != EL_UNDEFINED ?
12406 player->inventory_infinite_element :
12407 player->dynabombs_left > 0 ?
12408 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
12411 player->is_dropping_pressed = TRUE;
12413 /* do not drop an element on top of another element; when holding drop key
12414 pressed without moving, dropped element must move away before the next
12415 element can be dropped (this is especially important if the next element
12416 is dynamite, which can be placed on background for historical reasons) */
12417 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
12420 if (IS_THROWABLE(drop_element))
12422 dropx += GET_DX_FROM_DIR(drop_direction);
12423 dropy += GET_DY_FROM_DIR(drop_direction);
12425 if (!IN_LEV_FIELD(dropx, dropy))
12429 old_element = Feld[dropx][dropy]; /* old element at dropping position */
12430 new_element = drop_element; /* default: no change when dropping */
12432 /* check if player is active, not moving and ready to drop */
12433 if (!player->active || player->MovPos || player->drop_delay > 0)
12436 /* check if player has anything that can be dropped */
12437 if (new_element == EL_UNDEFINED)
12440 /* check if drop key was pressed long enough for EM style dynamite */
12441 if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
12444 /* check if anything can be dropped at the current position */
12445 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
12448 /* collected custom elements can only be dropped on empty fields */
12449 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
12452 if (old_element != EL_EMPTY)
12453 Back[dropx][dropy] = old_element; /* store old element on this field */
12455 ResetGfxAnimation(dropx, dropy);
12456 ResetRandomAnimationValue(dropx, dropy);
12458 if (player->inventory_size > 0 ||
12459 player->inventory_infinite_element != EL_UNDEFINED)
12461 if (player->inventory_size > 0)
12463 player->inventory_size--;
12465 DrawGameDoorValues();
12467 if (new_element == EL_DYNAMITE)
12468 new_element = EL_DYNAMITE_ACTIVE;
12469 else if (new_element == EL_EM_DYNAMITE)
12470 new_element = EL_EM_DYNAMITE_ACTIVE;
12471 else if (new_element == EL_SP_DISK_RED)
12472 new_element = EL_SP_DISK_RED_ACTIVE;
12475 Feld[dropx][dropy] = new_element;
12477 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
12478 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
12479 el2img(Feld[dropx][dropy]), 0);
12481 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
12483 /* needed if previous element just changed to "empty" in the last frame */
12484 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
12486 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
12487 player->index_bit, drop_side);
12488 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
12490 player->index_bit, drop_side);
12492 TestIfElementTouchesCustomElement(dropx, dropy);
12494 else /* player is dropping a dyna bomb */
12496 player->dynabombs_left--;
12498 Feld[dropx][dropy] = new_element;
12500 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
12501 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
12502 el2img(Feld[dropx][dropy]), 0);
12504 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
12507 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
12508 InitField_WithBug1(dropx, dropy, FALSE);
12510 new_element = Feld[dropx][dropy]; /* element might have changed */
12512 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
12513 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
12515 int move_direction, nextx, nexty;
12517 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
12518 MovDir[dropx][dropy] = drop_direction;
12520 move_direction = MovDir[dropx][dropy];
12521 nextx = dropx + GET_DX_FROM_DIR(move_direction);
12522 nexty = dropy + GET_DY_FROM_DIR(move_direction);
12524 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
12526 #if USE_FIX_IMPACT_COLLISION
12527 /* do not cause impact style collision by dropping elements that can fall */
12528 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
12530 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
12534 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
12535 player->is_dropping = TRUE;
12537 player->drop_pressed_delay = 0;
12538 player->is_dropping_pressed = FALSE;
12540 player->drop_x = dropx;
12541 player->drop_y = dropy;
12546 /* ------------------------------------------------------------------------- */
12547 /* game sound playing functions */
12548 /* ------------------------------------------------------------------------- */
12550 static int *loop_sound_frame = NULL;
12551 static int *loop_sound_volume = NULL;
12553 void InitPlayLevelSound()
12555 int num_sounds = getSoundListSize();
12557 checked_free(loop_sound_frame);
12558 checked_free(loop_sound_volume);
12560 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
12561 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
12564 static void PlayLevelSound(int x, int y, int nr)
12566 int sx = SCREENX(x), sy = SCREENY(y);
12567 int volume, stereo_position;
12568 int max_distance = 8;
12569 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
12571 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
12572 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
12575 if (!IN_LEV_FIELD(x, y) ||
12576 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
12577 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
12580 volume = SOUND_MAX_VOLUME;
12582 if (!IN_SCR_FIELD(sx, sy))
12584 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
12585 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
12587 volume -= volume * (dx > dy ? dx : dy) / max_distance;
12590 stereo_position = (SOUND_MAX_LEFT +
12591 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
12592 (SCR_FIELDX + 2 * max_distance));
12594 if (IS_LOOP_SOUND(nr))
12596 /* This assures that quieter loop sounds do not overwrite louder ones,
12597 while restarting sound volume comparison with each new game frame. */
12599 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
12602 loop_sound_volume[nr] = volume;
12603 loop_sound_frame[nr] = FrameCounter;
12606 PlaySoundExt(nr, volume, stereo_position, type);
12609 static void PlayLevelSoundNearest(int x, int y, int sound_action)
12611 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
12612 x > LEVELX(BX2) ? LEVELX(BX2) : x,
12613 y < LEVELY(BY1) ? LEVELY(BY1) :
12614 y > LEVELY(BY2) ? LEVELY(BY2) : y,
12618 static void PlayLevelSoundAction(int x, int y, int action)
12620 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
12623 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
12625 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
12627 if (sound_effect != SND_UNDEFINED)
12628 PlayLevelSound(x, y, sound_effect);
12631 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
12634 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
12636 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12637 PlayLevelSound(x, y, sound_effect);
12640 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
12642 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
12644 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12645 PlayLevelSound(x, y, sound_effect);
12648 static void StopLevelSoundActionIfLoop(int x, int y, int action)
12650 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
12652 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12653 StopSound(sound_effect);
12656 static void PlayLevelMusic()
12658 if (levelset.music[level_nr] != MUS_UNDEFINED)
12659 PlayMusic(levelset.music[level_nr]); /* from config file */
12661 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
12664 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
12666 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
12667 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
12668 int x = xx - 1 - offset;
12669 int y = yy - 1 - offset;
12674 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
12678 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12682 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12686 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12690 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
12694 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12698 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12701 case SAMPLE_android_clone:
12702 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12705 case SAMPLE_android_move:
12706 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12709 case SAMPLE_spring:
12710 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12714 PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
12718 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
12721 case SAMPLE_eater_eat:
12722 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
12726 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12729 case SAMPLE_collect:
12730 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
12733 case SAMPLE_diamond:
12734 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12737 case SAMPLE_squash:
12738 /* !!! CHECK THIS !!! */
12740 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
12742 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
12746 case SAMPLE_wonderfall:
12747 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
12751 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12755 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12759 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
12763 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
12767 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12771 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
12774 case SAMPLE_wonder:
12775 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12779 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
12782 case SAMPLE_exit_open:
12783 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
12786 case SAMPLE_exit_leave:
12787 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
12790 case SAMPLE_dynamite:
12791 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12795 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12799 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
12803 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12807 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
12811 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
12815 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12819 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
12825 void ChangeTime(int value)
12827 int *time = (level.time == 0 ? &TimePlayed : &TimeLeft);
12831 /* EMC game engine uses value from time counter of RND game engine */
12832 level.native_em_level->lev->time = *time;
12834 DrawGameValue_Time(*time);
12837 void RaiseScore(int value)
12839 /* EMC game engine and RND game engine have separate score counters */
12840 int *score = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
12841 &level.native_em_level->lev->score : &local_player->score);
12845 DrawGameValue_Score(*score);
12849 void RaiseScore(int value)
12851 local_player->score += value;
12853 DrawGameValue_Score(local_player->score);
12856 void RaiseScoreElement(int element)
12861 case EL_BD_DIAMOND:
12862 case EL_EMERALD_YELLOW:
12863 case EL_EMERALD_RED:
12864 case EL_EMERALD_PURPLE:
12865 case EL_SP_INFOTRON:
12866 RaiseScore(level.score[SC_EMERALD]);
12869 RaiseScore(level.score[SC_DIAMOND]);
12872 RaiseScore(level.score[SC_CRYSTAL]);
12875 RaiseScore(level.score[SC_PEARL]);
12878 case EL_BD_BUTTERFLY:
12879 case EL_SP_ELECTRON:
12880 RaiseScore(level.score[SC_BUG]);
12883 case EL_BD_FIREFLY:
12884 case EL_SP_SNIKSNAK:
12885 RaiseScore(level.score[SC_SPACESHIP]);
12888 case EL_DARK_YAMYAM:
12889 RaiseScore(level.score[SC_YAMYAM]);
12892 RaiseScore(level.score[SC_ROBOT]);
12895 RaiseScore(level.score[SC_PACMAN]);
12898 RaiseScore(level.score[SC_NUT]);
12901 case EL_EM_DYNAMITE:
12902 case EL_SP_DISK_RED:
12903 case EL_DYNABOMB_INCREASE_NUMBER:
12904 case EL_DYNABOMB_INCREASE_SIZE:
12905 case EL_DYNABOMB_INCREASE_POWER:
12906 RaiseScore(level.score[SC_DYNAMITE]);
12908 case EL_SHIELD_NORMAL:
12909 case EL_SHIELD_DEADLY:
12910 RaiseScore(level.score[SC_SHIELD]);
12912 case EL_EXTRA_TIME:
12913 RaiseScore(level.extra_time_score);
12927 RaiseScore(level.score[SC_KEY]);
12930 RaiseScore(element_info[element].collect_score);
12935 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
12937 if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
12939 #if defined(NETWORK_AVALIABLE)
12940 if (options.network)
12941 SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
12947 game_status = GAME_MODE_MAIN;
12953 FadeOut(REDRAW_FIELD);
12955 game_status = GAME_MODE_MAIN;
12957 DrawAndFadeInMainMenu(REDRAW_FIELD);
12961 else /* continue playing the game */
12963 if (tape.playing && tape.deactivate_display)
12964 TapeDeactivateDisplayOff(TRUE);
12966 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
12968 if (tape.playing && tape.deactivate_display)
12969 TapeDeactivateDisplayOn();
12973 void RequestQuitGame(boolean ask_if_really_quit)
12975 boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
12976 boolean skip_request = AllPlayersGone || quick_quit;
12978 RequestQuitGameExt(skip_request, quick_quit,
12979 "Do you really want to quit the game ?");
12983 /* ------------------------------------------------------------------------- */
12984 /* random generator functions */
12985 /* ------------------------------------------------------------------------- */
12987 unsigned int InitEngineRandom_RND(long seed)
12989 game.num_random_calls = 0;
12992 unsigned int rnd_seed = InitEngineRandom(seed);
12994 printf("::: START RND: %d\n", rnd_seed);
12999 return InitEngineRandom(seed);
13005 unsigned int RND(int max)
13009 game.num_random_calls++;
13011 return GetEngineRandom(max);
13018 /* ------------------------------------------------------------------------- */
13019 /* game engine snapshot handling functions */
13020 /* ------------------------------------------------------------------------- */
13022 #define ARGS_ADDRESS_AND_SIZEOF(x) (&(x)), (sizeof(x))
13024 struct EngineSnapshotInfo
13026 /* runtime values for custom element collect score */
13027 int collect_score[NUM_CUSTOM_ELEMENTS];
13029 /* runtime values for group element choice position */
13030 int choice_pos[NUM_GROUP_ELEMENTS];
13032 /* runtime values for belt position animations */
13033 int belt_graphic[4 * NUM_BELT_PARTS];
13034 int belt_anim_mode[4 * NUM_BELT_PARTS];
13037 struct EngineSnapshotNodeInfo
13044 static struct EngineSnapshotInfo engine_snapshot_rnd;
13045 static ListNode *engine_snapshot_list = NULL;
13046 static char *snapshot_level_identifier = NULL;
13047 static int snapshot_level_nr = -1;
13049 void FreeEngineSnapshot()
13051 while (engine_snapshot_list != NULL)
13052 deleteNodeFromList(&engine_snapshot_list, engine_snapshot_list->key,
13055 setString(&snapshot_level_identifier, NULL);
13056 snapshot_level_nr = -1;
13059 static void SaveEngineSnapshotValues_RND()
13061 static int belt_base_active_element[4] =
13063 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
13064 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
13065 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
13066 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
13070 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
13072 int element = EL_CUSTOM_START + i;
13074 engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
13077 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
13079 int element = EL_GROUP_START + i;
13081 engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
13084 for (i = 0; i < 4; i++)
13086 for (j = 0; j < NUM_BELT_PARTS; j++)
13088 int element = belt_base_active_element[i] + j;
13089 int graphic = el2img(element);
13090 int anim_mode = graphic_info[graphic].anim_mode;
13092 engine_snapshot_rnd.belt_graphic[i * 4 + j] = graphic;
13093 engine_snapshot_rnd.belt_anim_mode[i * 4 + j] = anim_mode;
13098 static void LoadEngineSnapshotValues_RND()
13100 unsigned long num_random_calls = game.num_random_calls;
13103 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
13105 int element = EL_CUSTOM_START + i;
13107 element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
13110 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
13112 int element = EL_GROUP_START + i;
13114 element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
13117 for (i = 0; i < 4; i++)
13119 for (j = 0; j < NUM_BELT_PARTS; j++)
13121 int graphic = engine_snapshot_rnd.belt_graphic[i * 4 + j];
13122 int anim_mode = engine_snapshot_rnd.belt_anim_mode[i * 4 + j];
13124 graphic_info[graphic].anim_mode = anim_mode;
13128 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
13130 InitRND(tape.random_seed);
13131 for (i = 0; i < num_random_calls; i++)
13135 if (game.num_random_calls != num_random_calls)
13137 Error(ERR_RETURN, "number of random calls out of sync");
13138 Error(ERR_RETURN, "number of random calls should be %d", num_random_calls);
13139 Error(ERR_RETURN, "number of random calls is %d", game.num_random_calls);
13140 Error(ERR_EXIT, "this should not happen -- please debug");
13144 static void SaveEngineSnapshotBuffer(void *buffer, int size)
13146 struct EngineSnapshotNodeInfo *bi =
13147 checked_calloc(sizeof(struct EngineSnapshotNodeInfo));
13149 bi->buffer_orig = buffer;
13150 bi->buffer_copy = checked_malloc(size);
13153 memcpy(bi->buffer_copy, buffer, size);
13155 addNodeToList(&engine_snapshot_list, NULL, bi);
13158 void SaveEngineSnapshot()
13160 FreeEngineSnapshot(); /* free previous snapshot, if needed */
13162 if (level_editor_test_game) /* do not save snapshots from editor */
13165 /* copy some special values to a structure better suited for the snapshot */
13167 SaveEngineSnapshotValues_RND();
13168 SaveEngineSnapshotValues_EM();
13170 /* save values stored in special snapshot structure */
13172 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
13173 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
13175 /* save further RND engine values */
13177 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
13178 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
13179 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
13181 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
13182 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
13183 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
13184 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
13186 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
13187 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
13188 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
13189 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
13190 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
13192 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
13193 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
13194 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
13196 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
13198 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
13200 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
13201 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
13203 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
13204 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
13205 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
13206 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
13207 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
13208 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
13209 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
13210 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
13211 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
13212 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
13213 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
13214 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
13215 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
13216 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
13217 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
13218 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
13219 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
13220 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
13222 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
13223 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
13225 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
13226 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
13227 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
13229 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
13230 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
13232 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
13233 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
13234 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
13235 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
13236 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
13238 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
13239 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
13241 /* save level identification information */
13243 setString(&snapshot_level_identifier, leveldir_current->identifier);
13244 snapshot_level_nr = level_nr;
13247 ListNode *node = engine_snapshot_list;
13250 while (node != NULL)
13252 num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
13257 printf("::: size of engine snapshot: %d bytes\n", num_bytes);
13261 static void LoadEngineSnapshotBuffer(struct EngineSnapshotNodeInfo *bi)
13263 memcpy(bi->buffer_orig, bi->buffer_copy, bi->size);
13266 void LoadEngineSnapshot()
13268 ListNode *node = engine_snapshot_list;
13270 if (engine_snapshot_list == NULL)
13273 while (node != NULL)
13275 LoadEngineSnapshotBuffer((struct EngineSnapshotNodeInfo *)node->content);
13280 /* restore special values from snapshot structure */
13282 LoadEngineSnapshotValues_RND();
13283 LoadEngineSnapshotValues_EM();
13286 boolean CheckEngineSnapshot()
13288 return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
13289 snapshot_level_nr == level_nr);
13293 /* ---------- new game button stuff ---------------------------------------- */
13295 /* graphic position values for game buttons */
13296 #define GAME_BUTTON_XSIZE 30
13297 #define GAME_BUTTON_YSIZE 30
13298 #define GAME_BUTTON_XPOS 5
13299 #define GAME_BUTTON_YPOS 215
13300 #define SOUND_BUTTON_XPOS 5
13301 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
13303 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
13304 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
13305 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
13306 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
13307 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
13308 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
13315 } gamebutton_info[NUM_GAME_BUTTONS] =
13318 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
13323 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
13324 GAME_CTRL_ID_PAUSE,
13328 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
13333 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
13334 SOUND_CTRL_ID_MUSIC,
13335 "background music on/off"
13338 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
13339 SOUND_CTRL_ID_LOOPS,
13340 "sound loops on/off"
13343 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
13344 SOUND_CTRL_ID_SIMPLE,
13345 "normal sounds on/off"
13349 void CreateGameButtons()
13353 for (i = 0; i < NUM_GAME_BUTTONS; i++)
13355 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
13356 struct GadgetInfo *gi;
13359 unsigned long event_mask;
13360 int gd_xoffset, gd_yoffset;
13361 int gd_x1, gd_x2, gd_y1, gd_y2;
13364 gd_xoffset = gamebutton_info[i].x;
13365 gd_yoffset = gamebutton_info[i].y;
13366 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
13367 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
13369 if (id == GAME_CTRL_ID_STOP ||
13370 id == GAME_CTRL_ID_PAUSE ||
13371 id == GAME_CTRL_ID_PLAY)
13373 button_type = GD_TYPE_NORMAL_BUTTON;
13375 event_mask = GD_EVENT_RELEASED;
13376 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
13377 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
13381 button_type = GD_TYPE_CHECK_BUTTON;
13383 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
13384 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
13385 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
13386 event_mask = GD_EVENT_PRESSED;
13387 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
13388 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
13391 gi = CreateGadget(GDI_CUSTOM_ID, id,
13392 GDI_INFO_TEXT, gamebutton_info[i].infotext,
13393 GDI_X, DX + gd_xoffset,
13394 GDI_Y, DY + gd_yoffset,
13395 GDI_WIDTH, GAME_BUTTON_XSIZE,
13396 GDI_HEIGHT, GAME_BUTTON_YSIZE,
13397 GDI_TYPE, button_type,
13398 GDI_STATE, GD_BUTTON_UNPRESSED,
13399 GDI_CHECKED, checked,
13400 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
13401 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
13402 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
13403 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
13404 GDI_EVENT_MASK, event_mask,
13405 GDI_CALLBACK_ACTION, HandleGameButtons,
13409 Error(ERR_EXIT, "cannot create gadget");
13411 game_gadget[id] = gi;
13415 void FreeGameButtons()
13419 for (i = 0; i < NUM_GAME_BUTTONS; i++)
13420 FreeGadget(game_gadget[i]);
13423 static void MapGameButtons()
13427 for (i = 0; i < NUM_GAME_BUTTONS; i++)
13428 MapGadget(game_gadget[i]);
13431 void UnmapGameButtons()
13435 for (i = 0; i < NUM_GAME_BUTTONS; i++)
13436 UnmapGadget(game_gadget[i]);
13439 static void HandleGameButtons(struct GadgetInfo *gi)
13441 int id = gi->custom_id;
13443 if (game_status != GAME_MODE_PLAYING)
13448 case GAME_CTRL_ID_STOP:
13452 RequestQuitGame(TRUE);
13455 case GAME_CTRL_ID_PAUSE:
13456 if (options.network)
13458 #if defined(NETWORK_AVALIABLE)
13460 SendToServer_ContinuePlaying();
13462 SendToServer_PausePlaying();
13466 TapeTogglePause(TAPE_TOGGLE_MANUAL);
13469 case GAME_CTRL_ID_PLAY:
13472 #if defined(NETWORK_AVALIABLE)
13473 if (options.network)
13474 SendToServer_ContinuePlaying();
13478 tape.pausing = FALSE;
13479 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
13484 case SOUND_CTRL_ID_MUSIC:
13485 if (setup.sound_music)
13487 setup.sound_music = FALSE;
13490 else if (audio.music_available)
13492 setup.sound = setup.sound_music = TRUE;
13494 SetAudioMode(setup.sound);
13500 case SOUND_CTRL_ID_LOOPS:
13501 if (setup.sound_loops)
13502 setup.sound_loops = FALSE;
13503 else if (audio.loops_available)
13505 setup.sound = setup.sound_loops = TRUE;
13506 SetAudioMode(setup.sound);
13510 case SOUND_CTRL_ID_SIMPLE:
13511 if (setup.sound_simple)
13512 setup.sound_simple = FALSE;
13513 else if (audio.sound_available)
13515 setup.sound = setup.sound_simple = TRUE;
13516 SetAudioMode(setup.sound);