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 IS_FOOD_PENGUIN(Feld[x][y])))
249 #define DRAGON_CAN_ENTER_FIELD(e, x, y) \
250 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
252 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition) \
253 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
255 #define SPRING_CAN_ENTER_FIELD(e, x, y) \
256 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
258 #define SPRING_CAN_BUMP_FROM_FIELD(x, y) \
259 (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER || \
260 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
262 #define MOVE_ENTER_EL(e) (element_info[e].move_enter_element)
264 #define CE_ENTER_FIELD_COND(e, x, y) \
265 (!IS_PLAYER(x, y) && \
266 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
268 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y) \
269 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
271 #define IN_LEV_FIELD_AND_IS_FREE(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
272 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
274 #define ACCESS_FROM(e, d) (element_info[e].access_direction &(d))
275 #define IS_WALKABLE_FROM(e, d) (IS_WALKABLE(e) && ACCESS_FROM(e, d))
276 #define IS_PASSABLE_FROM(e, d) (IS_PASSABLE(e) && ACCESS_FROM(e, d))
277 #define IS_ACCESSIBLE_FROM(e, d) (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
279 /* game button identifiers */
280 #define GAME_CTRL_ID_STOP 0
281 #define GAME_CTRL_ID_PAUSE 1
282 #define GAME_CTRL_ID_PLAY 2
283 #define SOUND_CTRL_ID_MUSIC 3
284 #define SOUND_CTRL_ID_LOOPS 4
285 #define SOUND_CTRL_ID_SIMPLE 5
287 #define NUM_GAME_BUTTONS 6
290 /* forward declaration for internal use */
292 static void CreateField(int, int, int);
294 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
295 static void AdvanceFrameAndPlayerCounters(int);
297 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
298 static boolean MovePlayer(struct PlayerInfo *, int, int);
299 static void ScrollPlayer(struct PlayerInfo *, int);
300 static void ScrollScreen(struct PlayerInfo *, int);
302 int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
304 static void InitBeltMovement(void);
305 static void CloseAllOpenTimegates(void);
306 static void CheckGravityMovement(struct PlayerInfo *);
307 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
308 static void KillPlayerUnlessEnemyProtected(int, int);
309 static void KillPlayerUnlessExplosionProtected(int, int);
311 static void TestIfPlayerTouchesCustomElement(int, int);
312 static void TestIfElementTouchesCustomElement(int, int);
313 static void TestIfElementHitsCustomElement(int, int, int);
315 static void TestIfElementSmashesCustomElement(int, int, int);
318 static void HandleElementChange(int, int, int);
319 static void ExecuteCustomElementAction(int, int, int, int);
320 static boolean ChangeElement(int, int, int, int);
322 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
323 #define CheckTriggeredElementChange(x, y, e, ev) \
324 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
325 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s) \
326 CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
327 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s) \
328 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
329 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p) \
330 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
332 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
333 #define CheckElementChange(x, y, e, te, ev) \
334 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
335 #define CheckElementChangeByPlayer(x, y, e, ev, p, s) \
336 CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
337 #define CheckElementChangeBySide(x, y, e, te, ev, s) \
338 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
340 static void PlayLevelSound(int, int, int);
341 static void PlayLevelSoundNearest(int, int, int);
342 static void PlayLevelSoundAction(int, int, int);
343 static void PlayLevelSoundElementAction(int, int, int, int);
344 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
345 static void PlayLevelSoundActionIfLoop(int, int, int);
346 static void StopLevelSoundActionIfLoop(int, int, int);
347 static void PlayLevelMusic();
349 static void MapGameButtons();
350 static void HandleGameButtons(struct GadgetInfo *);
352 int AmoebeNachbarNr(int, int);
353 void AmoebeUmwandeln(int, int);
354 void ContinueMoving(int, int);
356 void InitMovDir(int, int);
357 void InitAmoebaNr(int, int);
358 int NewHiScore(void);
360 void TestIfGoodThingHitsBadThing(int, int, int);
361 void TestIfBadThingHitsGoodThing(int, int, int);
362 void TestIfPlayerTouchesBadThing(int, int);
363 void TestIfPlayerRunsIntoBadThing(int, int, int);
364 void TestIfBadThingTouchesPlayer(int, int);
365 void TestIfBadThingRunsIntoPlayer(int, int, int);
366 void TestIfFriendTouchesBadThing(int, int);
367 void TestIfBadThingTouchesFriend(int, int);
368 void TestIfBadThingTouchesOtherBadThing(int, int);
370 void KillPlayer(struct PlayerInfo *);
371 void BuryPlayer(struct PlayerInfo *);
372 void RemovePlayer(struct PlayerInfo *);
374 boolean SnapField(struct PlayerInfo *, int, int);
375 boolean DropElement(struct PlayerInfo *);
377 static int getInvisibleActiveFromInvisibleElement(int);
378 static int getInvisibleFromInvisibleActiveElement(int);
380 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
382 /* for detection of endless loops, caused by custom element programming */
383 /* (using "MAX_PLAYFIELD_WIDTH" here is just a rough approximation...) */
384 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH (MAX_PLAYFIELD_WIDTH)
386 #define RECURSION_LOOP_DETECTION_START(e, rc) \
388 if (recursion_loop_detected) \
391 if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH) \
393 recursion_loop_detected = TRUE; \
394 recursion_loop_element = (e); \
397 recursion_loop_depth++; \
400 #define RECURSION_LOOP_DETECTION_END() \
402 recursion_loop_depth--; \
405 static int recursion_loop_depth;
406 static boolean recursion_loop_detected;
407 static boolean recursion_loop_element;
410 /* ------------------------------------------------------------------------- */
411 /* definition of elements that automatically change to other elements after */
412 /* a specified time, eventually calling a function when changing */
413 /* ------------------------------------------------------------------------- */
415 /* forward declaration for changer functions */
416 static void InitBuggyBase(int, int);
417 static void WarnBuggyBase(int, int);
419 static void InitTrap(int, int);
420 static void ActivateTrap(int, int);
421 static void ChangeActiveTrap(int, int);
423 static void InitRobotWheel(int, int);
424 static void RunRobotWheel(int, int);
425 static void StopRobotWheel(int, int);
427 static void InitTimegateWheel(int, int);
428 static void RunTimegateWheel(int, int);
430 static void InitMagicBallDelay(int, int);
431 static void ActivateMagicBall(int, int);
433 struct ChangingElementInfo
438 void (*pre_change_function)(int x, int y);
439 void (*change_function)(int x, int y);
440 void (*post_change_function)(int x, int y);
443 static struct ChangingElementInfo change_delay_list[] =
494 EL_SWITCHGATE_OPENING,
502 EL_SWITCHGATE_CLOSING,
503 EL_SWITCHGATE_CLOSED,
535 EL_ACID_SPLASH_RIGHT,
544 EL_SP_BUGGY_BASE_ACTIVATING,
551 EL_SP_BUGGY_BASE_ACTIVATING,
552 EL_SP_BUGGY_BASE_ACTIVE,
559 EL_SP_BUGGY_BASE_ACTIVE,
583 EL_ROBOT_WHEEL_ACTIVE,
591 EL_TIMEGATE_SWITCH_ACTIVE,
599 EL_EMC_MAGIC_BALL_ACTIVE,
600 EL_EMC_MAGIC_BALL_ACTIVE,
607 EL_EMC_SPRING_BUMPER_ACTIVE,
608 EL_EMC_SPRING_BUMPER,
615 EL_DIAGONAL_SHRINKING,
644 int push_delay_fixed, push_delay_random;
649 { EL_BALLOON, 0, 0 },
651 { EL_SOKOBAN_OBJECT, 2, 0 },
652 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
653 { EL_SATELLITE, 2, 0 },
654 { EL_SP_DISK_YELLOW, 2, 0 },
656 { EL_UNDEFINED, 0, 0 },
664 move_stepsize_list[] =
666 { EL_AMOEBA_DROP, 2 },
667 { EL_AMOEBA_DROPPING, 2 },
668 { EL_QUICKSAND_FILLING, 1 },
669 { EL_QUICKSAND_EMPTYING, 1 },
670 { EL_MAGIC_WALL_FILLING, 2 },
671 { EL_BD_MAGIC_WALL_FILLING, 2 },
672 { EL_MAGIC_WALL_EMPTYING, 2 },
673 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
683 collect_count_list[] =
686 { EL_BD_DIAMOND, 1 },
687 { EL_EMERALD_YELLOW, 1 },
688 { EL_EMERALD_RED, 1 },
689 { EL_EMERALD_PURPLE, 1 },
691 { EL_SP_INFOTRON, 1 },
703 access_direction_list[] =
705 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
706 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
707 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
708 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
709 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
710 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
711 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
712 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
713 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
714 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
715 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
717 { EL_SP_PORT_LEFT, MV_RIGHT },
718 { EL_SP_PORT_RIGHT, MV_LEFT },
719 { EL_SP_PORT_UP, MV_DOWN },
720 { EL_SP_PORT_DOWN, MV_UP },
721 { EL_SP_PORT_HORIZONTAL, MV_LEFT | MV_RIGHT },
722 { EL_SP_PORT_VERTICAL, MV_UP | MV_DOWN },
723 { EL_SP_PORT_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
724 { EL_SP_GRAVITY_PORT_LEFT, MV_RIGHT },
725 { EL_SP_GRAVITY_PORT_RIGHT, MV_LEFT },
726 { EL_SP_GRAVITY_PORT_UP, MV_DOWN },
727 { EL_SP_GRAVITY_PORT_DOWN, MV_UP },
728 { EL_SP_GRAVITY_ON_PORT_LEFT, MV_RIGHT },
729 { EL_SP_GRAVITY_ON_PORT_RIGHT, MV_LEFT },
730 { EL_SP_GRAVITY_ON_PORT_UP, MV_DOWN },
731 { EL_SP_GRAVITY_ON_PORT_DOWN, MV_UP },
732 { EL_SP_GRAVITY_OFF_PORT_LEFT, MV_RIGHT },
733 { EL_SP_GRAVITY_OFF_PORT_RIGHT, MV_LEFT },
734 { EL_SP_GRAVITY_OFF_PORT_UP, MV_DOWN },
735 { EL_SP_GRAVITY_OFF_PORT_DOWN, MV_UP },
737 { EL_UNDEFINED, MV_NONE }
740 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
742 #define IS_AUTO_CHANGING(e) (element_info[e].has_change_event[CE_DELAY])
743 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
744 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
745 IS_JUST_CHANGING(x, y))
747 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
749 /* static variables for playfield scan mode (scanning forward or backward) */
750 static int playfield_scan_start_x = 0;
751 static int playfield_scan_start_y = 0;
752 static int playfield_scan_delta_x = 1;
753 static int playfield_scan_delta_y = 1;
755 #define SCAN_PLAYFIELD(x, y) for ((y) = playfield_scan_start_y; \
756 (y) >= 0 && (y) <= lev_fieldy - 1; \
757 (y) += playfield_scan_delta_y) \
758 for ((x) = playfield_scan_start_x; \
759 (x) >= 0 && (x) <= lev_fieldx - 1; \
760 (x) += playfield_scan_delta_x) \
763 void DEBUG_SetMaximumDynamite()
767 for (i = 0; i < MAX_INVENTORY_SIZE; i++)
768 if (local_player->inventory_size < MAX_INVENTORY_SIZE)
769 local_player->inventory_element[local_player->inventory_size++] =
774 static void InitPlayfieldScanModeVars()
776 if (game.use_reverse_scan_direction)
778 playfield_scan_start_x = lev_fieldx - 1;
779 playfield_scan_start_y = lev_fieldy - 1;
781 playfield_scan_delta_x = -1;
782 playfield_scan_delta_y = -1;
786 playfield_scan_start_x = 0;
787 playfield_scan_start_y = 0;
789 playfield_scan_delta_x = 1;
790 playfield_scan_delta_y = 1;
794 static void InitPlayfieldScanMode(int mode)
796 game.use_reverse_scan_direction =
797 (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
799 InitPlayfieldScanModeVars();
802 static int get_move_delay_from_stepsize(int move_stepsize)
805 MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
807 /* make sure that stepsize value is always a power of 2 */
808 move_stepsize = (1 << log_2(move_stepsize));
810 return TILEX / move_stepsize;
813 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
816 int player_nr = player->index_nr;
817 int move_delay = get_move_delay_from_stepsize(move_stepsize);
818 boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
820 /* do no immediately change move delay -- the player might just be moving */
821 player->move_delay_value_next = move_delay;
823 /* information if player can move must be set separately */
824 player->cannot_move = cannot_move;
828 player->move_delay = game.initial_move_delay[player_nr];
829 player->move_delay_value = game.initial_move_delay_value[player_nr];
831 player->move_delay_value_next = -1;
833 player->move_delay_reset_counter = 0;
837 void GetPlayerConfig()
839 if (!audio.sound_available)
840 setup.sound_simple = FALSE;
842 if (!audio.loops_available)
843 setup.sound_loops = FALSE;
845 if (!audio.music_available)
846 setup.sound_music = FALSE;
848 if (!video.fullscreen_available)
849 setup.fullscreen = FALSE;
851 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
853 SetAudioMode(setup.sound);
857 static int getBeltNrFromBeltElement(int element)
859 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
860 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
861 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
864 static int getBeltNrFromBeltActiveElement(int element)
866 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
867 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
868 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
871 static int getBeltNrFromBeltSwitchElement(int element)
873 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
874 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
875 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
878 static int getBeltDirNrFromBeltSwitchElement(int element)
880 static int belt_base_element[4] =
882 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
883 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
884 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
885 EL_CONVEYOR_BELT_4_SWITCH_LEFT
888 int belt_nr = getBeltNrFromBeltSwitchElement(element);
889 int belt_dir_nr = element - belt_base_element[belt_nr];
891 return (belt_dir_nr % 3);
894 static int getBeltDirFromBeltSwitchElement(int element)
896 static int belt_move_dir[3] =
903 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
905 return belt_move_dir[belt_dir_nr];
908 static int get_element_from_group_element(int element)
910 if (IS_GROUP_ELEMENT(element))
912 struct ElementGroupInfo *group = element_info[element].group;
913 int last_anim_random_frame = gfx.anim_random_frame;
916 if (group->choice_mode == ANIM_RANDOM)
917 gfx.anim_random_frame = RND(group->num_elements_resolved);
919 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
920 group->choice_mode, 0,
923 if (group->choice_mode == ANIM_RANDOM)
924 gfx.anim_random_frame = last_anim_random_frame;
928 element = group->element_resolved[element_pos];
934 static void InitPlayerField(int x, int y, int element, boolean init_game)
936 if (element == EL_SP_MURPHY)
940 if (stored_player[0].present)
942 Feld[x][y] = EL_SP_MURPHY_CLONE;
948 stored_player[0].use_murphy = TRUE;
950 if (!level.use_artwork_element[0])
951 stored_player[0].artwork_element = EL_SP_MURPHY;
954 Feld[x][y] = EL_PLAYER_1;
960 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
961 int jx = player->jx, jy = player->jy;
963 player->present = TRUE;
965 player->block_last_field = (element == EL_SP_MURPHY ?
966 level.sp_block_last_field :
967 level.block_last_field);
969 /* ---------- initialize player's last field block delay --------------- */
971 /* always start with reliable default value (no adjustment needed) */
972 player->block_delay_adjustment = 0;
974 /* special case 1: in Supaplex, Murphy blocks last field one more frame */
975 if (player->block_last_field && element == EL_SP_MURPHY)
976 player->block_delay_adjustment = 1;
978 /* special case 2: in game engines before 3.1.1, blocking was different */
979 if (game.use_block_last_field_bug)
980 player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
982 if (!options.network || player->connected)
984 player->active = TRUE;
986 /* remove potentially duplicate players */
987 if (StorePlayer[jx][jy] == Feld[x][y])
988 StorePlayer[jx][jy] = 0;
990 StorePlayer[x][y] = Feld[x][y];
994 printf("Player %d activated.\n", player->element_nr);
995 printf("[Local player is %d and currently %s.]\n",
996 local_player->element_nr,
997 local_player->active ? "active" : "not active");
1001 Feld[x][y] = EL_EMPTY;
1003 player->jx = player->last_jx = x;
1004 player->jy = player->last_jy = y;
1008 static void InitField(int x, int y, boolean init_game)
1010 int element = Feld[x][y];
1019 InitPlayerField(x, y, element, init_game);
1022 case EL_SOKOBAN_FIELD_PLAYER:
1023 element = Feld[x][y] = EL_PLAYER_1;
1024 InitField(x, y, init_game);
1026 element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1027 InitField(x, y, init_game);
1030 case EL_SOKOBAN_FIELD_EMPTY:
1031 local_player->sokobanfields_still_needed++;
1035 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1036 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1037 else if (x > 0 && Feld[x-1][y] == EL_ACID)
1038 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1039 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1040 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1041 else if (y > 0 && Feld[x][y-1] == EL_ACID)
1042 Feld[x][y] = EL_ACID_POOL_BOTTOM;
1043 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1044 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1053 case EL_SPACESHIP_RIGHT:
1054 case EL_SPACESHIP_UP:
1055 case EL_SPACESHIP_LEFT:
1056 case EL_SPACESHIP_DOWN:
1057 case EL_BD_BUTTERFLY:
1058 case EL_BD_BUTTERFLY_RIGHT:
1059 case EL_BD_BUTTERFLY_UP:
1060 case EL_BD_BUTTERFLY_LEFT:
1061 case EL_BD_BUTTERFLY_DOWN:
1063 case EL_BD_FIREFLY_RIGHT:
1064 case EL_BD_FIREFLY_UP:
1065 case EL_BD_FIREFLY_LEFT:
1066 case EL_BD_FIREFLY_DOWN:
1067 case EL_PACMAN_RIGHT:
1069 case EL_PACMAN_LEFT:
1070 case EL_PACMAN_DOWN:
1072 case EL_YAMYAM_LEFT:
1073 case EL_YAMYAM_RIGHT:
1075 case EL_YAMYAM_DOWN:
1076 case EL_DARK_YAMYAM:
1079 case EL_SP_SNIKSNAK:
1080 case EL_SP_ELECTRON:
1089 case EL_AMOEBA_FULL:
1094 case EL_AMOEBA_DROP:
1095 if (y == lev_fieldy - 1)
1097 Feld[x][y] = EL_AMOEBA_GROWING;
1098 Store[x][y] = EL_AMOEBA_WET;
1102 case EL_DYNAMITE_ACTIVE:
1103 case EL_SP_DISK_RED_ACTIVE:
1104 case EL_DYNABOMB_PLAYER_1_ACTIVE:
1105 case EL_DYNABOMB_PLAYER_2_ACTIVE:
1106 case EL_DYNABOMB_PLAYER_3_ACTIVE:
1107 case EL_DYNABOMB_PLAYER_4_ACTIVE:
1108 MovDelay[x][y] = 96;
1111 case EL_EM_DYNAMITE_ACTIVE:
1112 MovDelay[x][y] = 32;
1116 local_player->lights_still_needed++;
1120 local_player->friends_still_needed++;
1125 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1128 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1129 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1130 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1131 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1132 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1133 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1134 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1135 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1136 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1137 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1138 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1139 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1142 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1143 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1144 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1146 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
1148 game.belt_dir[belt_nr] = belt_dir;
1149 game.belt_dir_nr[belt_nr] = belt_dir_nr;
1151 else /* more than one switch -- set it like the first switch */
1153 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1158 #if !USE_BOTH_SWITCHGATE_SWITCHES
1159 case EL_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
1161 Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
1165 case EL_LIGHT_SWITCH_ACTIVE:
1167 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1170 case EL_INVISIBLE_STEELWALL:
1171 case EL_INVISIBLE_WALL:
1172 case EL_INVISIBLE_SAND:
1173 if (game.light_time_left > 0 ||
1174 game.lenses_time_left > 0)
1175 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1178 case EL_EMC_MAGIC_BALL:
1179 if (game.ball_state)
1180 Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1183 case EL_EMC_MAGIC_BALL_SWITCH:
1184 if (game.ball_state)
1185 Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1189 if (IS_CUSTOM_ELEMENT(element))
1191 if (CAN_MOVE(element))
1194 #if USE_NEW_CUSTOM_VALUE
1195 if (!element_info[element].use_last_ce_value || init_game)
1196 CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1199 else if (IS_GROUP_ELEMENT(element))
1201 Feld[x][y] = get_element_from_group_element(element);
1203 InitField(x, y, init_game);
1210 CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1213 static inline void InitField_WithBug1(int x, int y, boolean init_game)
1215 InitField(x, y, init_game);
1217 /* not needed to call InitMovDir() -- already done by InitField()! */
1218 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1219 CAN_MOVE(Feld[x][y]))
1223 static inline void InitField_WithBug2(int x, int y, boolean init_game)
1225 int old_element = Feld[x][y];
1227 InitField(x, y, init_game);
1229 /* not needed to call InitMovDir() -- already done by InitField()! */
1230 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1231 CAN_MOVE(old_element) &&
1232 (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1235 /* this case is in fact a combination of not less than three bugs:
1236 first, it calls InitMovDir() for elements that can move, although this is
1237 already done by InitField(); then, it checks the element that was at this
1238 field _before_ the call to InitField() (which can change it); lastly, it
1239 was not called for "mole with direction" elements, which were treated as
1240 "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1244 inline void DrawGameValue_Emeralds(int value)
1246 int xpos = (3 * 14 - 3 * getFontWidth(FONT_TEXT_2)) / 2;
1248 if (PANEL_DEACTIVATED(game.panel.gems))
1251 DrawText(DX_EMERALDS + xpos, DY_EMERALDS, int2str(value, 3), FONT_TEXT_2);
1254 inline void DrawGameValue_Dynamite(int value)
1256 int xpos = (3 * 14 - 3 * getFontWidth(FONT_TEXT_2)) / 2;
1258 if (PANEL_DEACTIVATED(game.panel.inventory))
1261 DrawText(DX_DYNAMITE + xpos, DY_DYNAMITE, int2str(value, 3), FONT_TEXT_2);
1264 inline void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
1266 int base_key_graphic = EL_KEY_1;
1269 if (PANEL_DEACTIVATED(game.panel.keys))
1272 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
1273 base_key_graphic = EL_EM_KEY_1;
1275 /* currently only 4 of 8 possible keys are displayed */
1276 for (i = 0; i < STD_NUM_KEYS; i++)
1278 int x = XX_KEYS + i * MINI_TILEX;
1282 DrawMiniGraphicExt(drawto, DX + x,DY + y, el2edimg(base_key_graphic + i));
1284 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
1285 DOOR_GFX_PAGEX5 + x, y, MINI_TILEX, MINI_TILEY, DX + x,DY + y);
1289 inline void DrawGameValue_Score(int value)
1291 int xpos = (5 * 14 - 5 * getFontWidth(FONT_TEXT_2)) / 2;
1293 if (PANEL_DEACTIVATED(game.panel.score))
1296 DrawText(DX_SCORE + xpos, DY_SCORE, int2str(value, 5), FONT_TEXT_2);
1299 inline void DrawGameValue_Time(int value)
1301 int xpos3 = (3 * 14 - 3 * getFontWidth(FONT_TEXT_2)) / 2;
1302 int xpos4 = (4 * 10 - 4 * getFontWidth(FONT_LEVEL_NUMBER)) / 2;
1304 if (PANEL_DEACTIVATED(game.panel.time))
1307 /* clear background if value just changed its size */
1308 if (value == 999 || value == 1000)
1309 ClearRectangleOnBackground(drawto, DX_TIME1, DY_TIME, 14 * 3, 14);
1312 DrawText(DX_TIME1 + xpos3, DY_TIME, int2str(value, 3), FONT_TEXT_2);
1314 DrawText(DX_TIME2 + xpos4, DY_TIME, int2str(value, 4), FONT_LEVEL_NUMBER);
1317 inline void DrawGameValue_Level(int value)
1319 if (PANEL_DEACTIVATED(game.panel.level))
1323 DrawText(DX_LEVEL1, DY_LEVEL, int2str(value, 2), FONT_TEXT_2);
1325 DrawText(DX_LEVEL2, DY_LEVEL, int2str(value, 3), FONT_LEVEL_NUMBER);
1328 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
1331 int key[MAX_NUM_KEYS];
1334 /* prevent EM engine from updating time/score values parallel to GameWon() */
1335 if (level.game_engine_type == GAME_ENGINE_TYPE_EM &&
1336 local_player->LevelSolved)
1339 for (i = 0; i < MAX_NUM_KEYS; i++)
1340 key[i] = key_bits & (1 << i);
1342 DrawGameValue_Level(level_nr);
1344 DrawGameValue_Emeralds(emeralds);
1345 DrawGameValue_Dynamite(dynamite);
1346 DrawGameValue_Score(score);
1347 DrawGameValue_Time(time);
1349 DrawGameValue_Keys(key);
1352 void DrawGameDoorValues()
1354 int time_value = (level.time == 0 ? TimePlayed : TimeLeft);
1355 int dynamite_value = 0;
1356 int score_value = (local_player->LevelSolved ? local_player->score_final :
1357 local_player->score);
1358 int gems_value = local_player->gems_still_needed;
1362 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
1364 DrawGameDoorValues_EM();
1369 if (game.centered_player_nr == -1)
1371 for (i = 0; i < MAX_PLAYERS; i++)
1373 for (j = 0; j < MAX_NUM_KEYS; j++)
1374 if (stored_player[i].key[j])
1375 key_bits |= (1 << j);
1377 dynamite_value += stored_player[i].inventory_size;
1382 int player_nr = game.centered_player_nr;
1384 for (i = 0; i < MAX_NUM_KEYS; i++)
1385 if (stored_player[player_nr].key[i])
1386 key_bits |= (1 << i);
1388 dynamite_value = stored_player[player_nr].inventory_size;
1391 DrawAllGameValues(gems_value, dynamite_value, score_value, time_value,
1397 =============================================================================
1399 -----------------------------------------------------------------------------
1400 initialize game engine due to level / tape version number
1401 =============================================================================
1404 static void InitGameEngine()
1406 int i, j, k, l, x, y;
1408 /* set game engine from tape file when re-playing, else from level file */
1409 game.engine_version = (tape.playing ? tape.engine_version :
1410 level.game_version);
1412 /* ---------------------------------------------------------------------- */
1413 /* set flags for bugs and changes according to active game engine version */
1414 /* ---------------------------------------------------------------------- */
1417 Summary of bugfix/change:
1418 Fixed handling for custom elements that change when pushed by the player.
1420 Fixed/changed in version:
1424 Before 3.1.0, custom elements that "change when pushing" changed directly
1425 after the player started pushing them (until then handled in "DigField()").
1426 Since 3.1.0, these custom elements are not changed until the "pushing"
1427 move of the element is finished (now handled in "ContinueMoving()").
1429 Affected levels/tapes:
1430 The first condition is generally needed for all levels/tapes before version
1431 3.1.0, which might use the old behaviour before it was changed; known tapes
1432 that are affected are some tapes from the level set "Walpurgis Gardens" by
1434 The second condition is an exception from the above case and is needed for
1435 the special case of tapes recorded with game (not engine!) version 3.1.0 or
1436 above (including some development versions of 3.1.0), but before it was
1437 known that this change would break tapes like the above and was fixed in
1438 3.1.1, so that the changed behaviour was active although the engine version
1439 while recording maybe was before 3.1.0. There is at least one tape that is
1440 affected by this exception, which is the tape for the one-level set "Bug
1441 Machine" by Juergen Bonhagen.
1444 game.use_change_when_pushing_bug =
1445 (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1447 tape.game_version >= VERSION_IDENT(3,1,0,0) &&
1448 tape.game_version < VERSION_IDENT(3,1,1,0)));
1451 Summary of bugfix/change:
1452 Fixed handling for blocking the field the player leaves when moving.
1454 Fixed/changed in version:
1458 Before 3.1.1, when "block last field when moving" was enabled, the field
1459 the player is leaving when moving was blocked for the time of the move,
1460 and was directly unblocked afterwards. This resulted in the last field
1461 being blocked for exactly one less than the number of frames of one player
1462 move. Additionally, even when blocking was disabled, the last field was
1463 blocked for exactly one frame.
1464 Since 3.1.1, due to changes in player movement handling, the last field
1465 is not blocked at all when blocking is disabled. When blocking is enabled,
1466 the last field is blocked for exactly the number of frames of one player
1467 move. Additionally, if the player is Murphy, the hero of Supaplex, the
1468 last field is blocked for exactly one more than the number of frames of
1471 Affected levels/tapes:
1472 (!!! yet to be determined -- probably many !!!)
1475 game.use_block_last_field_bug =
1476 (game.engine_version < VERSION_IDENT(3,1,1,0));
1479 Summary of bugfix/change:
1480 Changed behaviour of CE changes with multiple changes per single frame.
1482 Fixed/changed in version:
1486 Before 3.2.0-6, only one single CE change was allowed in each engine frame.
1487 This resulted in race conditions where CEs seem to behave strange in some
1488 situations (where triggered CE changes were just skipped because there was
1489 already a CE change on that tile in the playfield in that engine frame).
1490 Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
1491 (The number of changes per frame must be limited in any case, because else
1492 it is easily possible to define CE changes that would result in an infinite
1493 loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
1494 should be set large enough so that it would only be reached in cases where
1495 the corresponding CE change conditions run into a loop. Therefore, it seems
1496 to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
1497 maximal number of change pages for custom elements.)
1499 Affected levels/tapes:
1503 #if USE_ONLY_ONE_CHANGE_PER_FRAME
1504 game.max_num_changes_per_frame = 1;
1506 game.max_num_changes_per_frame =
1507 (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
1510 /* ---------------------------------------------------------------------- */
1512 /* default scan direction: scan playfield from top/left to bottom/right */
1513 InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
1515 /* dynamically adjust element properties according to game engine version */
1516 InitElementPropertiesEngine(game.engine_version);
1519 printf("level %d: level version == %06d\n", level_nr, level.game_version);
1520 printf(" tape version == %06d [%s] [file: %06d]\n",
1521 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
1523 printf(" => game.engine_version == %06d\n", game.engine_version);
1526 /* ---------- initialize player's initial move delay --------------------- */
1528 /* dynamically adjust player properties according to level information */
1529 for (i = 0; i < MAX_PLAYERS; i++)
1530 game.initial_move_delay_value[i] =
1531 get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
1533 /* dynamically adjust player properties according to game engine version */
1534 for (i = 0; i < MAX_PLAYERS; i++)
1535 game.initial_move_delay[i] =
1536 (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
1537 game.initial_move_delay_value[i] : 0);
1539 /* ---------- initialize player's initial push delay --------------------- */
1541 /* dynamically adjust player properties according to game engine version */
1542 game.initial_push_delay_value =
1543 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
1545 /* ---------- initialize changing elements ------------------------------- */
1547 /* initialize changing elements information */
1548 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1550 struct ElementInfo *ei = &element_info[i];
1552 /* this pointer might have been changed in the level editor */
1553 ei->change = &ei->change_page[0];
1555 if (!IS_CUSTOM_ELEMENT(i))
1557 ei->change->target_element = EL_EMPTY_SPACE;
1558 ei->change->delay_fixed = 0;
1559 ei->change->delay_random = 0;
1560 ei->change->delay_frames = 1;
1563 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1565 ei->has_change_event[j] = FALSE;
1567 ei->event_page_nr[j] = 0;
1568 ei->event_page[j] = &ei->change_page[0];
1572 /* add changing elements from pre-defined list */
1573 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
1575 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
1576 struct ElementInfo *ei = &element_info[ch_delay->element];
1578 ei->change->target_element = ch_delay->target_element;
1579 ei->change->delay_fixed = ch_delay->change_delay;
1581 ei->change->pre_change_function = ch_delay->pre_change_function;
1582 ei->change->change_function = ch_delay->change_function;
1583 ei->change->post_change_function = ch_delay->post_change_function;
1585 ei->change->can_change = TRUE;
1586 ei->change->can_change_or_has_action = TRUE;
1588 ei->has_change_event[CE_DELAY] = TRUE;
1590 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
1591 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
1594 /* ---------- initialize internal run-time variables ------------- */
1596 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1598 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1600 for (j = 0; j < ei->num_change_pages; j++)
1602 ei->change_page[j].can_change_or_has_action =
1603 (ei->change_page[j].can_change |
1604 ei->change_page[j].has_action);
1608 /* add change events from custom element configuration */
1609 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1611 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1613 for (j = 0; j < ei->num_change_pages; j++)
1615 if (!ei->change_page[j].can_change_or_has_action)
1618 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1620 /* only add event page for the first page found with this event */
1621 if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
1623 ei->has_change_event[k] = TRUE;
1625 ei->event_page_nr[k] = j;
1626 ei->event_page[k] = &ei->change_page[j];
1632 /* ---------- initialize run-time trigger player and element ------------- */
1634 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1636 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1638 for (j = 0; j < ei->num_change_pages; j++)
1640 ei->change_page[j].actual_trigger_element = EL_EMPTY;
1641 ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
1642 ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
1643 ei->change_page[j].actual_trigger_ce_value = 0;
1644 ei->change_page[j].actual_trigger_ce_score = 0;
1648 /* ---------- initialize trigger events ---------------------------------- */
1650 /* initialize trigger events information */
1651 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1652 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1653 trigger_events[i][j] = FALSE;
1655 /* add trigger events from element change event properties */
1656 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1658 struct ElementInfo *ei = &element_info[i];
1660 for (j = 0; j < ei->num_change_pages; j++)
1662 if (!ei->change_page[j].can_change_or_has_action)
1665 if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
1667 int trigger_element = ei->change_page[j].trigger_element;
1669 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1671 if (ei->change_page[j].has_event[k])
1673 if (IS_GROUP_ELEMENT(trigger_element))
1675 struct ElementGroupInfo *group =
1676 element_info[trigger_element].group;
1678 for (l = 0; l < group->num_elements_resolved; l++)
1679 trigger_events[group->element_resolved[l]][k] = TRUE;
1681 else if (trigger_element == EL_ANY_ELEMENT)
1682 for (l = 0; l < MAX_NUM_ELEMENTS; l++)
1683 trigger_events[l][k] = TRUE;
1685 trigger_events[trigger_element][k] = TRUE;
1692 /* ---------- initialize push delay -------------------------------------- */
1694 /* initialize push delay values to default */
1695 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1697 if (!IS_CUSTOM_ELEMENT(i))
1699 /* set default push delay values (corrected since version 3.0.7-1) */
1700 if (game.engine_version < VERSION_IDENT(3,0,7,1))
1702 element_info[i].push_delay_fixed = 2;
1703 element_info[i].push_delay_random = 8;
1707 element_info[i].push_delay_fixed = 8;
1708 element_info[i].push_delay_random = 8;
1713 /* set push delay value for certain elements from pre-defined list */
1714 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
1716 int e = push_delay_list[i].element;
1718 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
1719 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
1722 /* set push delay value for Supaplex elements for newer engine versions */
1723 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
1725 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1727 if (IS_SP_ELEMENT(i))
1729 /* set SP push delay to just enough to push under a falling zonk */
1730 int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
1732 element_info[i].push_delay_fixed = delay;
1733 element_info[i].push_delay_random = 0;
1738 /* ---------- initialize move stepsize ----------------------------------- */
1740 /* initialize move stepsize values to default */
1741 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1742 if (!IS_CUSTOM_ELEMENT(i))
1743 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
1745 /* set move stepsize value for certain elements from pre-defined list */
1746 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
1748 int e = move_stepsize_list[i].element;
1750 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
1753 /* ---------- initialize collect score ----------------------------------- */
1755 /* initialize collect score values for custom elements from initial value */
1756 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1757 if (IS_CUSTOM_ELEMENT(i))
1758 element_info[i].collect_score = element_info[i].collect_score_initial;
1760 /* ---------- initialize collect count ----------------------------------- */
1762 /* initialize collect count values for non-custom elements */
1763 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1764 if (!IS_CUSTOM_ELEMENT(i))
1765 element_info[i].collect_count_initial = 0;
1767 /* add collect count values for all elements from pre-defined list */
1768 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
1769 element_info[collect_count_list[i].element].collect_count_initial =
1770 collect_count_list[i].count;
1772 /* ---------- initialize access direction -------------------------------- */
1774 /* initialize access direction values to default (access from every side) */
1775 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1776 if (!IS_CUSTOM_ELEMENT(i))
1777 element_info[i].access_direction = MV_ALL_DIRECTIONS;
1779 /* set access direction value for certain elements from pre-defined list */
1780 for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
1781 element_info[access_direction_list[i].element].access_direction =
1782 access_direction_list[i].direction;
1784 /* ---------- initialize explosion content ------------------------------- */
1785 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1787 if (IS_CUSTOM_ELEMENT(i))
1790 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
1792 /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
1794 element_info[i].content.e[x][y] =
1795 (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
1796 i == EL_PLAYER_2 ? EL_EMERALD_RED :
1797 i == EL_PLAYER_3 ? EL_EMERALD :
1798 i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
1799 i == EL_MOLE ? EL_EMERALD_RED :
1800 i == EL_PENGUIN ? EL_EMERALD_PURPLE :
1801 i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
1802 i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
1803 i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
1804 i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
1805 i == EL_WALL_EMERALD ? EL_EMERALD :
1806 i == EL_WALL_DIAMOND ? EL_DIAMOND :
1807 i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
1808 i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
1809 i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
1810 i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
1811 i == EL_WALL_PEARL ? EL_PEARL :
1812 i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
1817 /* ---------- initialize recursion detection ------------------------------ */
1818 recursion_loop_depth = 0;
1819 recursion_loop_detected = FALSE;
1820 recursion_loop_element = EL_UNDEFINED;
1823 int get_num_special_action(int element, int action_first, int action_last)
1825 int num_special_action = 0;
1828 for (i = action_first; i <= action_last; i++)
1830 boolean found = FALSE;
1832 for (j = 0; j < NUM_DIRECTIONS; j++)
1833 if (el_act_dir2img(element, i, j) !=
1834 el_act_dir2img(element, ACTION_DEFAULT, j))
1838 num_special_action++;
1843 return num_special_action;
1848 =============================================================================
1850 -----------------------------------------------------------------------------
1851 initialize and start new game
1852 =============================================================================
1857 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
1858 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
1859 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
1860 boolean do_fading = (game_status == GAME_MODE_MAIN);
1863 game_status = GAME_MODE_PLAYING;
1867 /* don't play tapes over network */
1868 network_playing = (options.network && !tape.playing);
1870 for (i = 0; i < MAX_PLAYERS; i++)
1872 struct PlayerInfo *player = &stored_player[i];
1874 player->index_nr = i;
1875 player->index_bit = (1 << i);
1876 player->element_nr = EL_PLAYER_1 + i;
1878 player->present = FALSE;
1879 player->active = FALSE;
1880 player->killed = FALSE;
1883 player->effective_action = 0;
1884 player->programmed_action = 0;
1887 player->score_final = 0;
1889 player->gems_still_needed = level.gems_needed;
1890 player->sokobanfields_still_needed = 0;
1891 player->lights_still_needed = 0;
1892 player->friends_still_needed = 0;
1894 for (j = 0; j < MAX_NUM_KEYS; j++)
1895 player->key[j] = FALSE;
1897 player->dynabomb_count = 0;
1898 player->dynabomb_size = 1;
1899 player->dynabombs_left = 0;
1900 player->dynabomb_xl = FALSE;
1902 player->MovDir = MV_NONE;
1905 player->GfxDir = MV_NONE;
1906 player->GfxAction = ACTION_DEFAULT;
1908 player->StepFrame = 0;
1910 player->use_murphy = FALSE;
1911 player->artwork_element =
1912 (level.use_artwork_element[i] ? level.artwork_element[i] :
1913 player->element_nr);
1915 player->block_last_field = FALSE; /* initialized in InitPlayerField() */
1916 player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
1918 player->gravity = level.initial_player_gravity[i];
1920 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
1922 player->actual_frame_counter = 0;
1924 player->step_counter = 0;
1926 player->last_move_dir = MV_NONE;
1928 player->is_active = FALSE;
1930 player->is_waiting = FALSE;
1931 player->is_moving = FALSE;
1932 player->is_auto_moving = FALSE;
1933 player->is_digging = FALSE;
1934 player->is_snapping = FALSE;
1935 player->is_collecting = FALSE;
1936 player->is_pushing = FALSE;
1937 player->is_switching = FALSE;
1938 player->is_dropping = FALSE;
1939 player->is_dropping_pressed = FALSE;
1941 player->is_bored = FALSE;
1942 player->is_sleeping = FALSE;
1944 player->frame_counter_bored = -1;
1945 player->frame_counter_sleeping = -1;
1947 player->anim_delay_counter = 0;
1948 player->post_delay_counter = 0;
1950 player->dir_waiting = MV_NONE;
1951 player->action_waiting = ACTION_DEFAULT;
1952 player->last_action_waiting = ACTION_DEFAULT;
1953 player->special_action_bored = ACTION_DEFAULT;
1954 player->special_action_sleeping = ACTION_DEFAULT;
1956 player->switch_x = -1;
1957 player->switch_y = -1;
1959 player->drop_x = -1;
1960 player->drop_y = -1;
1962 player->show_envelope = 0;
1964 SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
1966 player->push_delay = -1; /* initialized when pushing starts */
1967 player->push_delay_value = game.initial_push_delay_value;
1969 player->drop_delay = 0;
1970 player->drop_pressed_delay = 0;
1972 player->last_jx = -1;
1973 player->last_jy = -1;
1977 player->shield_normal_time_left = 0;
1978 player->shield_deadly_time_left = 0;
1980 player->inventory_infinite_element = EL_UNDEFINED;
1981 player->inventory_size = 0;
1983 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
1984 SnapField(player, 0, 0);
1986 player->LevelSolved = FALSE;
1987 player->GameOver = FALSE;
1989 player->LevelSolved_GameEnd = FALSE;
1990 player->LevelSolved_SaveTape = FALSE;
1991 player->LevelSolved_SaveScore = FALSE;
1994 network_player_action_received = FALSE;
1996 #if defined(NETWORK_AVALIABLE)
1997 /* initial null action */
1998 if (network_playing)
1999 SendToServer_MovePlayer(MV_NONE);
2008 TimeLeft = level.time;
2011 ScreenMovDir = MV_NONE;
2015 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
2017 AllPlayersGone = FALSE;
2019 game.yamyam_content_nr = 0;
2020 game.magic_wall_active = FALSE;
2021 game.magic_wall_time_left = 0;
2022 game.light_time_left = 0;
2023 game.timegate_time_left = 0;
2024 game.switchgate_pos = 0;
2025 game.wind_direction = level.wind_direction_initial;
2027 #if !USE_PLAYER_GRAVITY
2028 game.gravity = FALSE;
2029 game.explosions_delayed = TRUE;
2032 game.lenses_time_left = 0;
2033 game.magnify_time_left = 0;
2035 game.ball_state = level.ball_state_initial;
2036 game.ball_content_nr = 0;
2038 game.envelope_active = FALSE;
2040 /* set focus to local player for network games, else to all players */
2041 game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
2042 game.centered_player_nr_next = game.centered_player_nr;
2043 game.set_centered_player = FALSE;
2045 if (network_playing && tape.recording)
2047 /* store client dependent player focus when recording network games */
2048 tape.centered_player_nr_next = game.centered_player_nr_next;
2049 tape.set_centered_player = TRUE;
2052 for (i = 0; i < NUM_BELTS; i++)
2054 game.belt_dir[i] = MV_NONE;
2055 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
2058 for (i = 0; i < MAX_NUM_AMOEBA; i++)
2059 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
2061 SCAN_PLAYFIELD(x, y)
2063 Feld[x][y] = level.field[x][y];
2064 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
2065 ChangeDelay[x][y] = 0;
2066 ChangePage[x][y] = -1;
2067 #if USE_NEW_CUSTOM_VALUE
2068 CustomValue[x][y] = 0; /* initialized in InitField() */
2070 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
2072 WasJustMoving[x][y] = 0;
2073 WasJustFalling[x][y] = 0;
2074 CheckCollision[x][y] = 0;
2075 CheckImpact[x][y] = 0;
2077 Pushed[x][y] = FALSE;
2079 ChangeCount[x][y] = 0;
2080 ChangeEvent[x][y] = -1;
2082 ExplodePhase[x][y] = 0;
2083 ExplodeDelay[x][y] = 0;
2084 ExplodeField[x][y] = EX_TYPE_NONE;
2086 RunnerVisit[x][y] = 0;
2087 PlayerVisit[x][y] = 0;
2090 GfxRandom[x][y] = INIT_GFX_RANDOM();
2091 GfxElement[x][y] = EL_UNDEFINED;
2092 GfxAction[x][y] = ACTION_DEFAULT;
2093 GfxDir[x][y] = MV_NONE;
2096 SCAN_PLAYFIELD(x, y)
2098 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
2100 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
2102 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
2105 InitField(x, y, TRUE);
2110 for (i = 0; i < MAX_PLAYERS; i++)
2112 struct PlayerInfo *player = &stored_player[i];
2114 /* set number of special actions for bored and sleeping animation */
2115 player->num_special_action_bored =
2116 get_num_special_action(player->artwork_element,
2117 ACTION_BORING_1, ACTION_BORING_LAST);
2118 player->num_special_action_sleeping =
2119 get_num_special_action(player->artwork_element,
2120 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
2123 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
2124 emulate_sb ? EMU_SOKOBAN :
2125 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
2127 #if USE_NEW_ALL_SLIPPERY
2128 /* initialize type of slippery elements */
2129 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2131 if (!IS_CUSTOM_ELEMENT(i))
2133 /* default: elements slip down either to the left or right randomly */
2134 element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
2136 /* SP style elements prefer to slip down on the left side */
2137 if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
2138 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
2140 /* BD style elements prefer to slip down on the left side */
2141 if (game.emulation == EMU_BOULDERDASH)
2142 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
2147 /* initialize explosion and ignition delay */
2148 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2150 if (!IS_CUSTOM_ELEMENT(i))
2153 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
2154 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
2155 game.emulation == EMU_SUPAPLEX ? 3 : 2);
2156 int last_phase = (num_phase + 1) * delay;
2157 int half_phase = (num_phase / 2) * delay;
2159 element_info[i].explosion_delay = last_phase - 1;
2160 element_info[i].ignition_delay = half_phase;
2162 if (i == EL_BLACK_ORB)
2163 element_info[i].ignition_delay = 1;
2167 if (element_info[i].explosion_delay < 1) /* !!! check again !!! */
2168 element_info[i].explosion_delay = 1;
2170 if (element_info[i].ignition_delay < 1) /* !!! check again !!! */
2171 element_info[i].ignition_delay = 1;
2175 /* correct non-moving belts to start moving left */
2176 for (i = 0; i < NUM_BELTS; i++)
2177 if (game.belt_dir[i] == MV_NONE)
2178 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
2180 /* check if any connected player was not found in playfield */
2181 for (i = 0; i < MAX_PLAYERS; i++)
2183 struct PlayerInfo *player = &stored_player[i];
2185 if (player->connected && !player->present)
2187 for (j = 0; j < MAX_PLAYERS; j++)
2189 struct PlayerInfo *some_player = &stored_player[j];
2190 int jx = some_player->jx, jy = some_player->jy;
2192 /* assign first free player found that is present in the playfield */
2193 if (some_player->present && !some_player->connected)
2195 player->present = TRUE;
2196 player->active = TRUE;
2198 some_player->present = FALSE;
2199 some_player->active = FALSE;
2201 player->artwork_element = some_player->artwork_element;
2203 player->block_last_field = some_player->block_last_field;
2204 player->block_delay_adjustment = some_player->block_delay_adjustment;
2206 StorePlayer[jx][jy] = player->element_nr;
2207 player->jx = player->last_jx = jx;
2208 player->jy = player->last_jy = jy;
2218 /* when playing a tape, eliminate all players who do not participate */
2220 for (i = 0; i < MAX_PLAYERS; i++)
2222 if (stored_player[i].active && !tape.player_participates[i])
2224 struct PlayerInfo *player = &stored_player[i];
2225 int jx = player->jx, jy = player->jy;
2227 player->active = FALSE;
2228 StorePlayer[jx][jy] = 0;
2229 Feld[jx][jy] = EL_EMPTY;
2233 else if (!options.network && !setup.team_mode) /* && !tape.playing */
2235 /* when in single player mode, eliminate all but the first active player */
2237 for (i = 0; i < MAX_PLAYERS; i++)
2239 if (stored_player[i].active)
2241 for (j = i + 1; j < MAX_PLAYERS; j++)
2243 if (stored_player[j].active)
2245 struct PlayerInfo *player = &stored_player[j];
2246 int jx = player->jx, jy = player->jy;
2248 player->active = FALSE;
2249 player->present = FALSE;
2251 StorePlayer[jx][jy] = 0;
2252 Feld[jx][jy] = EL_EMPTY;
2259 /* when recording the game, store which players take part in the game */
2262 for (i = 0; i < MAX_PLAYERS; i++)
2263 if (stored_player[i].active)
2264 tape.player_participates[i] = TRUE;
2269 for (i = 0; i < MAX_PLAYERS; i++)
2271 struct PlayerInfo *player = &stored_player[i];
2273 printf("Player %d: present == %d, connected == %d, active == %d.\n",
2278 if (local_player == player)
2279 printf("Player %d is local player.\n", i+1);
2283 if (BorderElement == EL_EMPTY)
2286 SBX_Right = lev_fieldx - SCR_FIELDX;
2288 SBY_Lower = lev_fieldy - SCR_FIELDY;
2293 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
2295 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
2298 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
2299 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
2301 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
2302 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
2304 /* if local player not found, look for custom element that might create
2305 the player (make some assumptions about the right custom element) */
2306 if (!local_player->present)
2308 int start_x = 0, start_y = 0;
2309 int found_rating = 0;
2310 int found_element = EL_UNDEFINED;
2311 int player_nr = local_player->index_nr;
2313 SCAN_PLAYFIELD(x, y)
2315 int element = Feld[x][y];
2320 if (level.use_start_element[player_nr] &&
2321 level.start_element[player_nr] == element &&
2328 found_element = element;
2331 if (!IS_CUSTOM_ELEMENT(element))
2334 if (CAN_CHANGE(element))
2336 for (i = 0; i < element_info[element].num_change_pages; i++)
2338 /* check for player created from custom element as single target */
2339 content = element_info[element].change_page[i].target_element;
2340 is_player = ELEM_IS_PLAYER(content);
2342 if (is_player && (found_rating < 3 || element < found_element))
2348 found_element = element;
2353 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
2355 /* check for player created from custom element as explosion content */
2356 content = element_info[element].content.e[xx][yy];
2357 is_player = ELEM_IS_PLAYER(content);
2359 if (is_player && (found_rating < 2 || element < found_element))
2361 start_x = x + xx - 1;
2362 start_y = y + yy - 1;
2365 found_element = element;
2368 if (!CAN_CHANGE(element))
2371 for (i = 0; i < element_info[element].num_change_pages; i++)
2373 /* check for player created from custom element as extended target */
2375 element_info[element].change_page[i].target_content.e[xx][yy];
2377 is_player = ELEM_IS_PLAYER(content);
2379 if (is_player && (found_rating < 1 || element < found_element))
2381 start_x = x + xx - 1;
2382 start_y = y + yy - 1;
2385 found_element = element;
2391 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
2392 start_x > SBX_Right + MIDPOSX ? SBX_Right :
2395 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
2396 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
2401 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2402 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2403 local_player->jx - MIDPOSX);
2405 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2406 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2407 local_player->jy - MIDPOSY);
2412 if (!game.restart_level)
2413 CloseDoor(DOOR_CLOSE_1);
2416 FadeOut(REDRAW_FIELD);
2418 /* !!! FIX THIS (START) !!! */
2419 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2421 InitGameEngine_EM();
2423 /* blit playfield from scroll buffer to normal back buffer for fading in */
2424 BlitScreenToBitmap_EM(backbuffer);
2431 /* after drawing the level, correct some elements */
2432 if (game.timegate_time_left == 0)
2433 CloseAllOpenTimegates();
2435 /* blit playfield from scroll buffer to normal back buffer for fading in */
2436 if (setup.soft_scrolling)
2437 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
2439 redraw_mask |= REDRAW_FROM_BACKBUFFER;
2441 /* !!! FIX THIS (END) !!! */
2444 FadeIn(REDRAW_FIELD);
2448 if (!game.restart_level)
2450 /* copy default game door content to main double buffer */
2451 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2452 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2455 SetPanelBackground();
2456 SetDrawBackgroundMask(REDRAW_DOOR_1);
2458 DrawGameDoorValues();
2460 if (!game.restart_level)
2464 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
2465 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
2466 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
2470 /* copy actual game door content to door double buffer for OpenDoor() */
2471 BlitBitmap(drawto, bitmap_db_door,
2472 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
2474 OpenDoor(DOOR_OPEN_ALL);
2476 PlaySound(SND_GAME_STARTING);
2478 if (setup.sound_music)
2481 KeyboardAutoRepeatOffUnlessAutoplay();
2485 for (i = 0; i < MAX_PLAYERS; i++)
2486 printf("Player %d %sactive.\n",
2487 i + 1, (stored_player[i].active ? "" : "not "));
2498 game.restart_level = FALSE;
2501 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
2503 /* this is used for non-R'n'D game engines to update certain engine values */
2505 /* needed to determine if sounds are played within the visible screen area */
2506 scroll_x = actual_scroll_x;
2507 scroll_y = actual_scroll_y;
2510 void InitMovDir(int x, int y)
2512 int i, element = Feld[x][y];
2513 static int xy[4][2] =
2520 static int direction[3][4] =
2522 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
2523 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
2524 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
2533 Feld[x][y] = EL_BUG;
2534 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
2537 case EL_SPACESHIP_RIGHT:
2538 case EL_SPACESHIP_UP:
2539 case EL_SPACESHIP_LEFT:
2540 case EL_SPACESHIP_DOWN:
2541 Feld[x][y] = EL_SPACESHIP;
2542 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
2545 case EL_BD_BUTTERFLY_RIGHT:
2546 case EL_BD_BUTTERFLY_UP:
2547 case EL_BD_BUTTERFLY_LEFT:
2548 case EL_BD_BUTTERFLY_DOWN:
2549 Feld[x][y] = EL_BD_BUTTERFLY;
2550 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
2553 case EL_BD_FIREFLY_RIGHT:
2554 case EL_BD_FIREFLY_UP:
2555 case EL_BD_FIREFLY_LEFT:
2556 case EL_BD_FIREFLY_DOWN:
2557 Feld[x][y] = EL_BD_FIREFLY;
2558 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
2561 case EL_PACMAN_RIGHT:
2563 case EL_PACMAN_LEFT:
2564 case EL_PACMAN_DOWN:
2565 Feld[x][y] = EL_PACMAN;
2566 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
2569 case EL_YAMYAM_LEFT:
2570 case EL_YAMYAM_RIGHT:
2572 case EL_YAMYAM_DOWN:
2573 Feld[x][y] = EL_YAMYAM;
2574 MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
2577 case EL_SP_SNIKSNAK:
2578 MovDir[x][y] = MV_UP;
2581 case EL_SP_ELECTRON:
2582 MovDir[x][y] = MV_LEFT;
2589 Feld[x][y] = EL_MOLE;
2590 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
2594 if (IS_CUSTOM_ELEMENT(element))
2596 struct ElementInfo *ei = &element_info[element];
2597 int move_direction_initial = ei->move_direction_initial;
2598 int move_pattern = ei->move_pattern;
2600 if (move_direction_initial == MV_START_PREVIOUS)
2602 if (MovDir[x][y] != MV_NONE)
2605 move_direction_initial = MV_START_AUTOMATIC;
2608 if (move_direction_initial == MV_START_RANDOM)
2609 MovDir[x][y] = 1 << RND(4);
2610 else if (move_direction_initial & MV_ANY_DIRECTION)
2611 MovDir[x][y] = move_direction_initial;
2612 else if (move_pattern == MV_ALL_DIRECTIONS ||
2613 move_pattern == MV_TURNING_LEFT ||
2614 move_pattern == MV_TURNING_RIGHT ||
2615 move_pattern == MV_TURNING_LEFT_RIGHT ||
2616 move_pattern == MV_TURNING_RIGHT_LEFT ||
2617 move_pattern == MV_TURNING_RANDOM)
2618 MovDir[x][y] = 1 << RND(4);
2619 else if (move_pattern == MV_HORIZONTAL)
2620 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
2621 else if (move_pattern == MV_VERTICAL)
2622 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
2623 else if (move_pattern & MV_ANY_DIRECTION)
2624 MovDir[x][y] = element_info[element].move_pattern;
2625 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
2626 move_pattern == MV_ALONG_RIGHT_SIDE)
2628 /* use random direction as default start direction */
2629 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2630 MovDir[x][y] = 1 << RND(4);
2632 for (i = 0; i < NUM_DIRECTIONS; i++)
2634 int x1 = x + xy[i][0];
2635 int y1 = y + xy[i][1];
2637 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2639 if (move_pattern == MV_ALONG_RIGHT_SIDE)
2640 MovDir[x][y] = direction[0][i];
2642 MovDir[x][y] = direction[1][i];
2651 MovDir[x][y] = 1 << RND(4);
2653 if (element != EL_BUG &&
2654 element != EL_SPACESHIP &&
2655 element != EL_BD_BUTTERFLY &&
2656 element != EL_BD_FIREFLY)
2659 for (i = 0; i < NUM_DIRECTIONS; i++)
2661 int x1 = x + xy[i][0];
2662 int y1 = y + xy[i][1];
2664 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2666 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
2668 MovDir[x][y] = direction[0][i];
2671 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
2672 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
2674 MovDir[x][y] = direction[1][i];
2683 GfxDir[x][y] = MovDir[x][y];
2686 void InitAmoebaNr(int x, int y)
2689 int group_nr = AmoebeNachbarNr(x, y);
2693 for (i = 1; i < MAX_NUM_AMOEBA; i++)
2695 if (AmoebaCnt[i] == 0)
2703 AmoebaNr[x][y] = group_nr;
2704 AmoebaCnt[group_nr]++;
2705 AmoebaCnt2[group_nr]++;
2708 static void PlayerWins(struct PlayerInfo *player)
2710 player->LevelSolved = TRUE;
2711 player->GameOver = TRUE;
2713 player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2714 level.native_em_level->lev->score : player->score);
2719 static int time, time_final;
2720 static int score, score_final;
2721 static int game_over_delay = 0;
2722 int game_over_delay_value = 50;
2724 if (!local_player->LevelSolved_GameEnd)
2728 /* do not start end game actions before the player stops moving (to exit) */
2729 if (local_player->MovPos)
2732 local_player->LevelSolved_GameEnd = TRUE;
2733 local_player->LevelSolved_SaveTape = tape.recording;
2734 local_player->LevelSolved_SaveScore = !tape.playing;
2736 if (tape.auto_play) /* tape might already be stopped here */
2737 tape.auto_play_level_solved = TRUE;
2743 game_over_delay = game_over_delay_value;
2745 time = time_final = (level.time == 0 ? TimePlayed : TimeLeft);
2746 score = score_final = local_player->score_final;
2751 score_final += TimeLeft * level.score[SC_TIME_BONUS];
2753 else if (level.time == 0 && TimePlayed < 999)
2756 score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
2759 local_player->score_final = score_final;
2761 if (level_editor_test_game)
2764 score = score_final;
2766 DrawGameValue_Time(time);
2767 DrawGameValue_Score(score);
2770 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
2772 if (ExitX >= 0 && ExitY >= 0) /* local player has left the level */
2774 /* close exit door after last player */
2775 if (AllPlayersGone &&
2776 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
2777 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN))
2779 int element = Feld[ExitX][ExitY];
2781 Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
2782 EL_SP_EXIT_CLOSING);
2784 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
2787 /* player disappears */
2788 DrawLevelField(ExitX, ExitY);
2791 for (i = 0; i < MAX_PLAYERS; i++)
2793 struct PlayerInfo *player = &stored_player[i];
2795 if (player->present)
2797 RemovePlayer(player);
2799 /* player disappears */
2800 DrawLevelField(player->jx, player->jy);
2805 PlaySound(SND_GAME_WINNING);
2808 if (game_over_delay > 0)
2815 if (time != time_final)
2817 int time_to_go = ABS(time_final - time);
2818 int time_count_dir = (time < time_final ? +1 : -1);
2819 int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
2821 time += time_count_steps * time_count_dir;
2822 score += time_count_steps * level.score[SC_TIME_BONUS];
2824 DrawGameValue_Time(time);
2825 DrawGameValue_Score(score);
2827 if (time == time_final)
2828 StopSound(SND_GAME_LEVELTIME_BONUS);
2829 else if (setup.sound_loops)
2830 PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
2832 PlaySound(SND_GAME_LEVELTIME_BONUS);
2839 boolean raise_level = FALSE;
2841 CloseDoor(DOOR_CLOSE_1);
2843 if (local_player->LevelSolved_SaveTape)
2850 SaveTapeChecked(tape.level_nr); /* ask to save tape */
2852 SaveTape(tape.level_nr); /* ask to save tape */
2856 if (level_editor_test_game)
2858 game_status = GAME_MODE_MAIN;
2865 if (!local_player->LevelSolved_SaveScore)
2867 FadeOut(REDRAW_FIELD);
2869 game_status = GAME_MODE_MAIN;
2871 DrawAndFadeInMainMenu(REDRAW_FIELD);
2876 if (level_nr == leveldir_current->handicap_level)
2878 leveldir_current->handicap_level++;
2879 SaveLevelSetup_SeriesInfo();
2882 if (level_nr < leveldir_current->last_level)
2883 raise_level = TRUE; /* advance to next level */
2885 if ((hi_pos = NewHiScore()) >= 0)
2887 game_status = GAME_MODE_SCORES;
2889 DrawHallOfFame(hi_pos);
2899 FadeOut(REDRAW_FIELD);
2901 game_status = GAME_MODE_MAIN;
2909 DrawAndFadeInMainMenu(REDRAW_FIELD);
2918 LoadScore(level_nr);
2920 if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
2921 local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score)
2924 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
2926 if (local_player->score_final > highscore[k].Score)
2928 /* player has made it to the hall of fame */
2930 if (k < MAX_SCORE_ENTRIES - 1)
2932 int m = MAX_SCORE_ENTRIES - 1;
2935 for (l = k; l < MAX_SCORE_ENTRIES; l++)
2936 if (strEqual(setup.player_name, highscore[l].Name))
2938 if (m == k) /* player's new highscore overwrites his old one */
2942 for (l = m; l > k; l--)
2944 strcpy(highscore[l].Name, highscore[l - 1].Name);
2945 highscore[l].Score = highscore[l - 1].Score;
2952 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
2953 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
2954 highscore[k].Score = local_player->score_final;
2960 else if (!strncmp(setup.player_name, highscore[k].Name,
2961 MAX_PLAYER_NAME_LEN))
2962 break; /* player already there with a higher score */
2968 SaveScore(level_nr);
2973 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
2975 int element = Feld[x][y];
2976 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2977 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2978 int horiz_move = (dx != 0);
2979 int sign = (horiz_move ? dx : dy);
2980 int step = sign * element_info[element].move_stepsize;
2982 /* special values for move stepsize for spring and things on conveyor belt */
2985 if (CAN_FALL(element) &&
2986 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
2987 step = sign * MOVE_STEPSIZE_NORMAL / 2;
2988 else if (element == EL_SPRING)
2989 step = sign * MOVE_STEPSIZE_NORMAL * 2;
2995 inline static int getElementMoveStepsize(int x, int y)
2997 return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
3000 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
3002 if (player->GfxAction != action || player->GfxDir != dir)
3005 printf("Player frame reset! (%d => %d, %d => %d)\n",
3006 player->GfxAction, action, player->GfxDir, dir);
3009 player->GfxAction = action;
3010 player->GfxDir = dir;
3012 player->StepFrame = 0;
3016 #if USE_GFX_RESET_GFX_ANIMATION
3017 static void ResetGfxFrame(int x, int y, boolean redraw)
3019 int element = Feld[x][y];
3020 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3021 int last_gfx_frame = GfxFrame[x][y];
3023 if (graphic_info[graphic].anim_global_sync)
3024 GfxFrame[x][y] = FrameCounter;
3025 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
3026 GfxFrame[x][y] = CustomValue[x][y];
3027 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
3028 GfxFrame[x][y] = element_info[element].collect_score;
3029 else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
3030 GfxFrame[x][y] = ChangeDelay[x][y];
3032 if (redraw && GfxFrame[x][y] != last_gfx_frame)
3033 DrawLevelGraphicAnimation(x, y, graphic);
3037 static void ResetGfxAnimation(int x, int y)
3039 GfxAction[x][y] = ACTION_DEFAULT;
3040 GfxDir[x][y] = MovDir[x][y];
3043 #if USE_GFX_RESET_GFX_ANIMATION
3044 ResetGfxFrame(x, y, FALSE);
3048 static void ResetRandomAnimationValue(int x, int y)
3050 GfxRandom[x][y] = INIT_GFX_RANDOM();
3053 void InitMovingField(int x, int y, int direction)
3055 int element = Feld[x][y];
3056 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3057 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
3060 boolean is_moving_before, is_moving_after;
3062 boolean continues_moving = (WasJustMoving[x][y] && direction == MovDir[x][y]);
3065 /* check if element was/is moving or being moved before/after mode change */
3067 is_moving_before = WasJustMoving[x][y];
3069 is_moving_before = (getElementMoveStepsizeExt(x, y, MovDir[x][y]) != 0);
3071 is_moving_after = (getElementMoveStepsizeExt(x, y, direction) != 0);
3073 /* reset animation only for moving elements which change direction of moving
3074 or which just started or stopped moving
3075 (else CEs with property "can move" / "not moving" are reset each frame) */
3076 #if USE_GFX_RESET_ONLY_WHEN_MOVING
3078 if (is_moving_before != is_moving_after ||
3079 direction != MovDir[x][y])
3080 ResetGfxAnimation(x, y);
3082 if ((is_moving_before || is_moving_after) && !continues_moving)
3083 ResetGfxAnimation(x, y);
3086 if (!continues_moving)
3087 ResetGfxAnimation(x, y);
3090 MovDir[x][y] = direction;
3091 GfxDir[x][y] = direction;
3093 #if USE_GFX_RESET_ONLY_WHEN_MOVING
3094 GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
3095 direction == MV_DOWN && CAN_FALL(element) ?
3096 ACTION_FALLING : ACTION_MOVING);
3098 GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
3099 ACTION_FALLING : ACTION_MOVING);
3102 /* this is needed for CEs with property "can move" / "not moving" */
3104 if (is_moving_after)
3106 if (Feld[newx][newy] == EL_EMPTY)
3107 Feld[newx][newy] = EL_BLOCKED;
3109 MovDir[newx][newy] = MovDir[x][y];
3111 #if USE_NEW_CUSTOM_VALUE
3112 CustomValue[newx][newy] = CustomValue[x][y];
3115 GfxFrame[newx][newy] = GfxFrame[x][y];
3116 GfxRandom[newx][newy] = GfxRandom[x][y];
3117 GfxAction[newx][newy] = GfxAction[x][y];
3118 GfxDir[newx][newy] = GfxDir[x][y];
3122 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
3124 int direction = MovDir[x][y];
3125 int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
3126 int newy = y + (direction & MV_UP ? -1 : direction & MV_DOWN ? +1 : 0);
3132 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
3134 int oldx = x, oldy = y;
3135 int direction = MovDir[x][y];
3137 if (direction == MV_LEFT)
3139 else if (direction == MV_RIGHT)
3141 else if (direction == MV_UP)
3143 else if (direction == MV_DOWN)
3146 *comes_from_x = oldx;
3147 *comes_from_y = oldy;
3150 int MovingOrBlocked2Element(int x, int y)
3152 int element = Feld[x][y];
3154 if (element == EL_BLOCKED)
3158 Blocked2Moving(x, y, &oldx, &oldy);
3159 return Feld[oldx][oldy];
3165 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
3167 /* like MovingOrBlocked2Element(), but if element is moving
3168 and (x,y) is the field the moving element is just leaving,
3169 return EL_BLOCKED instead of the element value */
3170 int element = Feld[x][y];
3172 if (IS_MOVING(x, y))
3174 if (element == EL_BLOCKED)
3178 Blocked2Moving(x, y, &oldx, &oldy);
3179 return Feld[oldx][oldy];
3188 static void RemoveField(int x, int y)
3190 Feld[x][y] = EL_EMPTY;
3196 #if USE_NEW_CUSTOM_VALUE
3197 CustomValue[x][y] = 0;
3201 ChangeDelay[x][y] = 0;
3202 ChangePage[x][y] = -1;
3203 Pushed[x][y] = FALSE;
3206 ExplodeField[x][y] = EX_TYPE_NONE;
3209 GfxElement[x][y] = EL_UNDEFINED;
3210 GfxAction[x][y] = ACTION_DEFAULT;
3211 GfxDir[x][y] = MV_NONE;
3214 void RemoveMovingField(int x, int y)
3216 int oldx = x, oldy = y, newx = x, newy = y;
3217 int element = Feld[x][y];
3218 int next_element = EL_UNDEFINED;
3220 if (element != EL_BLOCKED && !IS_MOVING(x, y))
3223 if (IS_MOVING(x, y))
3225 Moving2Blocked(x, y, &newx, &newy);
3227 if (Feld[newx][newy] != EL_BLOCKED)
3229 /* element is moving, but target field is not free (blocked), but
3230 already occupied by something different (example: acid pool);
3231 in this case, only remove the moving field, but not the target */
3233 RemoveField(oldx, oldy);
3235 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
3237 DrawLevelField(oldx, oldy);
3242 else if (element == EL_BLOCKED)
3244 Blocked2Moving(x, y, &oldx, &oldy);
3245 if (!IS_MOVING(oldx, oldy))
3249 if (element == EL_BLOCKED &&
3250 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
3251 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
3252 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
3253 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
3254 next_element = get_next_element(Feld[oldx][oldy]);
3256 RemoveField(oldx, oldy);
3257 RemoveField(newx, newy);
3259 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
3261 if (next_element != EL_UNDEFINED)
3262 Feld[oldx][oldy] = next_element;
3264 DrawLevelField(oldx, oldy);
3265 DrawLevelField(newx, newy);
3268 void DrawDynamite(int x, int y)
3270 int sx = SCREENX(x), sy = SCREENY(y);
3271 int graphic = el2img(Feld[x][y]);
3274 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
3277 if (IS_WALKABLE_INSIDE(Back[x][y]))
3281 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
3282 else if (Store[x][y])
3283 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
3285 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
3287 if (Back[x][y] || Store[x][y])
3288 DrawGraphicThruMask(sx, sy, graphic, frame);
3290 DrawGraphic(sx, sy, graphic, frame);
3293 void CheckDynamite(int x, int y)
3295 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
3299 if (MovDelay[x][y] != 0)
3302 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
3308 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
3313 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
3315 boolean num_checked_players = 0;
3318 for (i = 0; i < MAX_PLAYERS; i++)
3320 if (stored_player[i].active)
3322 int sx = stored_player[i].jx;
3323 int sy = stored_player[i].jy;
3325 if (num_checked_players == 0)
3332 *sx1 = MIN(*sx1, sx);
3333 *sy1 = MIN(*sy1, sy);
3334 *sx2 = MAX(*sx2, sx);
3335 *sy2 = MAX(*sy2, sy);
3338 num_checked_players++;
3343 static boolean checkIfAllPlayersFitToScreen_RND()
3345 int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
3347 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
3349 return (sx2 - sx1 < SCR_FIELDX &&
3350 sy2 - sy1 < SCR_FIELDY);
3353 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
3355 int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
3357 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
3359 *sx = (sx1 + sx2) / 2;
3360 *sy = (sy1 + sy2) / 2;
3363 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
3364 boolean center_screen, boolean quick_relocation)
3366 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3367 boolean no_delay = (tape.warp_forward);
3368 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
3369 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
3371 if (quick_relocation)
3373 int offset = (setup.scroll_delay ? 3 : 0);
3375 if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
3379 scroll_x = (x < SBX_Left + MIDPOSX ? SBX_Left :
3380 x > SBX_Right + MIDPOSX ? SBX_Right :
3383 scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
3384 y > SBY_Lower + MIDPOSY ? SBY_Lower :
3389 /* quick relocation (without scrolling), but do not center screen */
3391 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
3392 old_x > SBX_Right + MIDPOSX ? SBX_Right :
3395 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
3396 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
3399 int offset_x = x + (scroll_x - center_scroll_x);
3400 int offset_y = y + (scroll_y - center_scroll_y);
3402 scroll_x = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
3403 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
3404 offset_x - MIDPOSX);
3406 scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
3407 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
3408 offset_y - MIDPOSY);
3413 if ((move_dir == MV_LEFT && scroll_x > x - MIDPOSX + offset) ||
3414 (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
3415 scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
3417 if ((move_dir == MV_UP && scroll_y > y - MIDPOSY + offset) ||
3418 (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
3419 scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
3421 /* don't scroll over playfield boundaries */
3422 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
3423 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
3425 /* don't scroll over playfield boundaries */
3426 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
3427 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
3430 RedrawPlayfield(TRUE, 0,0,0,0);
3434 int scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left :
3435 x > SBX_Right + MIDPOSX ? SBX_Right :
3438 int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
3439 y > SBY_Lower + MIDPOSY ? SBY_Lower :
3442 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
3444 while (scroll_x != scroll_xx || scroll_y != scroll_yy)
3447 int fx = FX, fy = FY;
3449 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
3450 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
3452 if (dx == 0 && dy == 0) /* no scrolling needed at all */
3458 fx += dx * TILEX / 2;
3459 fy += dy * TILEY / 2;
3461 ScrollLevel(dx, dy);
3464 /* scroll in two steps of half tile size to make things smoother */
3465 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
3467 Delay(wait_delay_value);
3469 /* scroll second step to align at full tile size */
3471 Delay(wait_delay_value);
3476 Delay(wait_delay_value);
3480 void RelocatePlayer(int jx, int jy, int el_player_raw)
3482 int el_player = GET_PLAYER_ELEMENT(el_player_raw);
3483 int player_nr = GET_PLAYER_NR(el_player);
3484 struct PlayerInfo *player = &stored_player[player_nr];
3485 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3486 boolean no_delay = (tape.warp_forward);
3487 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
3488 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
3489 int old_jx = player->jx;
3490 int old_jy = player->jy;
3491 int old_element = Feld[old_jx][old_jy];
3492 int element = Feld[jx][jy];
3493 boolean player_relocated = (old_jx != jx || old_jy != jy);
3495 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
3496 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
3497 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
3498 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
3499 int leave_side_horiz = move_dir_horiz;
3500 int leave_side_vert = move_dir_vert;
3501 int enter_side = enter_side_horiz | enter_side_vert;
3502 int leave_side = leave_side_horiz | leave_side_vert;
3504 if (player->GameOver) /* do not reanimate dead player */
3507 if (!player_relocated) /* no need to relocate the player */
3510 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
3512 RemoveField(jx, jy); /* temporarily remove newly placed player */
3513 DrawLevelField(jx, jy);
3516 if (player->present)
3518 while (player->MovPos)
3520 ScrollPlayer(player, SCROLL_GO_ON);
3521 ScrollScreen(NULL, SCROLL_GO_ON);
3523 AdvanceFrameAndPlayerCounters(player->index_nr);
3528 Delay(wait_delay_value);
3531 DrawPlayer(player); /* needed here only to cleanup last field */
3532 DrawLevelField(player->jx, player->jy); /* remove player graphic */
3534 player->is_moving = FALSE;
3537 if (IS_CUSTOM_ELEMENT(old_element))
3538 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
3540 player->index_bit, leave_side);
3542 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
3544 player->index_bit, leave_side);
3546 Feld[jx][jy] = el_player;
3547 InitPlayerField(jx, jy, el_player, TRUE);
3549 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
3551 Feld[jx][jy] = element;
3552 InitField(jx, jy, FALSE);
3555 /* only visually relocate centered player */
3556 DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
3557 FALSE, level.instant_relocation);
3559 TestIfPlayerTouchesBadThing(jx, jy);
3560 TestIfPlayerTouchesCustomElement(jx, jy);
3562 if (IS_CUSTOM_ELEMENT(element))
3563 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
3564 player->index_bit, enter_side);
3566 CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
3567 player->index_bit, enter_side);
3570 void Explode(int ex, int ey, int phase, int mode)
3576 /* !!! eliminate this variable !!! */
3577 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
3579 if (game.explosions_delayed)
3581 ExplodeField[ex][ey] = mode;
3585 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
3587 int center_element = Feld[ex][ey];
3588 int artwork_element, explosion_element; /* set these values later */
3591 /* --- This is only really needed (and now handled) in "Impact()". --- */
3592 /* do not explode moving elements that left the explode field in time */
3593 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
3594 center_element == EL_EMPTY &&
3595 (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
3600 /* !!! at this place, the center element may be EL_BLOCKED !!! */
3601 if (mode == EX_TYPE_NORMAL ||
3602 mode == EX_TYPE_CENTER ||
3603 mode == EX_TYPE_CROSS)
3604 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
3607 /* remove things displayed in background while burning dynamite */
3608 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
3611 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
3613 /* put moving element to center field (and let it explode there) */
3614 center_element = MovingOrBlocked2Element(ex, ey);
3615 RemoveMovingField(ex, ey);
3616 Feld[ex][ey] = center_element;
3619 /* now "center_element" is finally determined -- set related values now */
3620 artwork_element = center_element; /* for custom player artwork */
3621 explosion_element = center_element; /* for custom player artwork */
3623 if (IS_PLAYER(ex, ey))
3625 int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
3627 artwork_element = stored_player[player_nr].artwork_element;
3629 if (level.use_explosion_element[player_nr])
3631 explosion_element = level.explosion_element[player_nr];
3632 artwork_element = explosion_element;
3637 if (mode == EX_TYPE_NORMAL ||
3638 mode == EX_TYPE_CENTER ||
3639 mode == EX_TYPE_CROSS)
3640 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
3643 last_phase = element_info[explosion_element].explosion_delay + 1;
3645 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
3647 int xx = x - ex + 1;
3648 int yy = y - ey + 1;
3651 if (!IN_LEV_FIELD(x, y) ||
3652 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
3653 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
3656 element = Feld[x][y];
3658 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
3660 element = MovingOrBlocked2Element(x, y);
3662 if (!IS_EXPLOSION_PROOF(element))
3663 RemoveMovingField(x, y);
3666 /* indestructible elements can only explode in center (but not flames) */
3667 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
3668 mode == EX_TYPE_BORDER)) ||
3669 element == EL_FLAMES)
3672 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
3673 behaviour, for example when touching a yamyam that explodes to rocks
3674 with active deadly shield, a rock is created under the player !!! */
3675 /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
3677 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
3678 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
3679 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
3681 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
3684 if (IS_ACTIVE_BOMB(element))
3686 /* re-activate things under the bomb like gate or penguin */
3687 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
3694 /* save walkable background elements while explosion on same tile */
3695 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
3696 (x != ex || y != ey || mode == EX_TYPE_BORDER))
3697 Back[x][y] = element;
3699 /* ignite explodable elements reached by other explosion */
3700 if (element == EL_EXPLOSION)
3701 element = Store2[x][y];
3703 if (AmoebaNr[x][y] &&
3704 (element == EL_AMOEBA_FULL ||
3705 element == EL_BD_AMOEBA ||
3706 element == EL_AMOEBA_GROWING))
3708 AmoebaCnt[AmoebaNr[x][y]]--;
3709 AmoebaCnt2[AmoebaNr[x][y]]--;
3714 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
3716 int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
3718 Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
3720 if (PLAYERINFO(ex, ey)->use_murphy)
3721 Store[x][y] = EL_EMPTY;
3724 /* !!! check this case -- currently needed for rnd_rado_negundo_v,
3725 !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
3726 else if (ELEM_IS_PLAYER(center_element))
3727 Store[x][y] = EL_EMPTY;
3728 else if (center_element == EL_YAMYAM)
3729 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
3730 else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
3731 Store[x][y] = element_info[center_element].content.e[xx][yy];
3733 /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
3734 (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
3735 otherwise) -- FIX THIS !!! */
3736 else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
3737 Store[x][y] = element_info[element].content.e[1][1];
3739 else if (!CAN_EXPLODE(element))
3740 Store[x][y] = element_info[element].content.e[1][1];
3743 Store[x][y] = EL_EMPTY;
3745 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
3746 center_element == EL_AMOEBA_TO_DIAMOND)
3747 Store2[x][y] = element;
3749 Feld[x][y] = EL_EXPLOSION;
3750 GfxElement[x][y] = artwork_element;
3752 ExplodePhase[x][y] = 1;
3753 ExplodeDelay[x][y] = last_phase;
3758 if (center_element == EL_YAMYAM)
3759 game.yamyam_content_nr =
3760 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
3772 GfxFrame[x][y] = 0; /* restart explosion animation */
3774 last_phase = ExplodeDelay[x][y];
3776 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
3780 /* activate this even in non-DEBUG version until cause for crash in
3781 getGraphicAnimationFrame() (see below) is found and eliminated */
3787 /* this can happen if the player leaves an explosion just in time */
3788 if (GfxElement[x][y] == EL_UNDEFINED)
3789 GfxElement[x][y] = EL_EMPTY;
3791 if (GfxElement[x][y] == EL_UNDEFINED)
3794 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
3795 printf("Explode(): This should never happen!\n");
3798 GfxElement[x][y] = EL_EMPTY;
3804 border_element = Store2[x][y];
3805 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3806 border_element = StorePlayer[x][y];
3808 if (phase == element_info[border_element].ignition_delay ||
3809 phase == last_phase)
3811 boolean border_explosion = FALSE;
3813 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
3814 !PLAYER_EXPLOSION_PROTECTED(x, y))
3816 KillPlayerUnlessExplosionProtected(x, y);
3817 border_explosion = TRUE;
3819 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
3821 Feld[x][y] = Store2[x][y];
3824 border_explosion = TRUE;
3826 else if (border_element == EL_AMOEBA_TO_DIAMOND)
3828 AmoebeUmwandeln(x, y);
3830 border_explosion = TRUE;
3833 /* if an element just explodes due to another explosion (chain-reaction),
3834 do not immediately end the new explosion when it was the last frame of
3835 the explosion (as it would be done in the following "if"-statement!) */
3836 if (border_explosion && phase == last_phase)
3840 if (phase == last_phase)
3844 element = Feld[x][y] = Store[x][y];
3845 Store[x][y] = Store2[x][y] = 0;
3846 GfxElement[x][y] = EL_UNDEFINED;
3848 /* player can escape from explosions and might therefore be still alive */
3849 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
3850 element <= EL_PLAYER_IS_EXPLODING_4)
3852 int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
3853 int explosion_element = EL_PLAYER_1 + player_nr;
3854 int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
3855 int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
3857 if (level.use_explosion_element[player_nr])
3858 explosion_element = level.explosion_element[player_nr];
3860 Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
3861 element_info[explosion_element].content.e[xx][yy]);
3864 /* restore probably existing indestructible background element */
3865 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
3866 element = Feld[x][y] = Back[x][y];
3869 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
3870 GfxDir[x][y] = MV_NONE;
3871 ChangeDelay[x][y] = 0;
3872 ChangePage[x][y] = -1;
3874 #if USE_NEW_CUSTOM_VALUE
3875 CustomValue[x][y] = 0;
3878 InitField_WithBug2(x, y, FALSE);
3880 DrawLevelField(x, y);
3882 TestIfElementTouchesCustomElement(x, y);
3884 if (GFX_CRUMBLED(element))
3885 DrawLevelFieldCrumbledSandNeighbours(x, y);
3887 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
3888 StorePlayer[x][y] = 0;
3890 if (ELEM_IS_PLAYER(element))
3891 RelocatePlayer(x, y, element);
3893 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3895 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
3896 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
3899 DrawLevelFieldCrumbledSand(x, y);
3901 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
3903 DrawLevelElement(x, y, Back[x][y]);
3904 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
3906 else if (IS_WALKABLE_UNDER(Back[x][y]))
3908 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3909 DrawLevelElementThruMask(x, y, Back[x][y]);
3911 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
3912 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3916 void DynaExplode(int ex, int ey)
3919 int dynabomb_element = Feld[ex][ey];
3920 int dynabomb_size = 1;
3921 boolean dynabomb_xl = FALSE;
3922 struct PlayerInfo *player;
3923 static int xy[4][2] =
3931 if (IS_ACTIVE_BOMB(dynabomb_element))
3933 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
3934 dynabomb_size = player->dynabomb_size;
3935 dynabomb_xl = player->dynabomb_xl;
3936 player->dynabombs_left++;
3939 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
3941 for (i = 0; i < NUM_DIRECTIONS; i++)
3943 for (j = 1; j <= dynabomb_size; j++)
3945 int x = ex + j * xy[i][0];
3946 int y = ey + j * xy[i][1];
3949 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
3952 element = Feld[x][y];
3954 /* do not restart explosions of fields with active bombs */
3955 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
3958 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
3960 if (element != EL_EMPTY && element != EL_EXPLOSION &&
3961 !IS_DIGGABLE(element) && !dynabomb_xl)
3967 void Bang(int x, int y)
3969 int element = MovingOrBlocked2Element(x, y);
3970 int explosion_type = EX_TYPE_NORMAL;
3972 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3974 struct PlayerInfo *player = PLAYERINFO(x, y);
3976 element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
3977 player->element_nr);
3979 if (level.use_explosion_element[player->index_nr])
3981 int explosion_element = level.explosion_element[player->index_nr];
3983 if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
3984 explosion_type = EX_TYPE_CROSS;
3985 else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
3986 explosion_type = EX_TYPE_CENTER;
3994 case EL_BD_BUTTERFLY:
3997 case EL_DARK_YAMYAM:
4001 RaiseScoreElement(element);
4004 case EL_DYNABOMB_PLAYER_1_ACTIVE:
4005 case EL_DYNABOMB_PLAYER_2_ACTIVE:
4006 case EL_DYNABOMB_PLAYER_3_ACTIVE:
4007 case EL_DYNABOMB_PLAYER_4_ACTIVE:
4008 case EL_DYNABOMB_INCREASE_NUMBER:
4009 case EL_DYNABOMB_INCREASE_SIZE:
4010 case EL_DYNABOMB_INCREASE_POWER:
4011 explosion_type = EX_TYPE_DYNA;
4016 case EL_LAMP_ACTIVE:
4017 case EL_AMOEBA_TO_DIAMOND:
4018 if (!IS_PLAYER(x, y)) /* penguin and player may be at same field */
4019 explosion_type = EX_TYPE_CENTER;
4023 if (element_info[element].explosion_type == EXPLODES_CROSS)
4024 explosion_type = EX_TYPE_CROSS;
4025 else if (element_info[element].explosion_type == EXPLODES_1X1)
4026 explosion_type = EX_TYPE_CENTER;
4030 if (explosion_type == EX_TYPE_DYNA)
4033 Explode(x, y, EX_PHASE_START, explosion_type);
4035 CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
4038 void SplashAcid(int x, int y)
4040 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
4041 (!IN_LEV_FIELD(x - 1, y - 2) ||
4042 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
4043 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
4045 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
4046 (!IN_LEV_FIELD(x + 1, y - 2) ||
4047 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
4048 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
4050 PlayLevelSound(x, y, SND_ACID_SPLASHING);
4053 static void InitBeltMovement()
4055 static int belt_base_element[4] =
4057 EL_CONVEYOR_BELT_1_LEFT,
4058 EL_CONVEYOR_BELT_2_LEFT,
4059 EL_CONVEYOR_BELT_3_LEFT,
4060 EL_CONVEYOR_BELT_4_LEFT
4062 static int belt_base_active_element[4] =
4064 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
4065 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
4066 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
4067 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
4072 /* set frame order for belt animation graphic according to belt direction */
4073 for (i = 0; i < NUM_BELTS; i++)
4077 for (j = 0; j < NUM_BELT_PARTS; j++)
4079 int element = belt_base_active_element[belt_nr] + j;
4080 int graphic = el2img(element);
4082 if (game.belt_dir[i] == MV_LEFT)
4083 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
4085 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
4089 SCAN_PLAYFIELD(x, y)
4091 int element = Feld[x][y];
4093 for (i = 0; i < NUM_BELTS; i++)
4095 if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
4097 int e_belt_nr = getBeltNrFromBeltElement(element);
4100 if (e_belt_nr == belt_nr)
4102 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
4104 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
4111 static void ToggleBeltSwitch(int x, int y)
4113 static int belt_base_element[4] =
4115 EL_CONVEYOR_BELT_1_LEFT,
4116 EL_CONVEYOR_BELT_2_LEFT,
4117 EL_CONVEYOR_BELT_3_LEFT,
4118 EL_CONVEYOR_BELT_4_LEFT
4120 static int belt_base_active_element[4] =
4122 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
4123 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
4124 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
4125 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
4127 static int belt_base_switch_element[4] =
4129 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
4130 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
4131 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
4132 EL_CONVEYOR_BELT_4_SWITCH_LEFT
4134 static int belt_move_dir[4] =
4142 int element = Feld[x][y];
4143 int belt_nr = getBeltNrFromBeltSwitchElement(element);
4144 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
4145 int belt_dir = belt_move_dir[belt_dir_nr];
4148 if (!IS_BELT_SWITCH(element))
4151 game.belt_dir_nr[belt_nr] = belt_dir_nr;
4152 game.belt_dir[belt_nr] = belt_dir;
4154 if (belt_dir_nr == 3)
4157 /* set frame order for belt animation graphic according to belt direction */
4158 for (i = 0; i < NUM_BELT_PARTS; i++)
4160 int element = belt_base_active_element[belt_nr] + i;
4161 int graphic = el2img(element);
4163 if (belt_dir == MV_LEFT)
4164 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
4166 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
4169 SCAN_PLAYFIELD(xx, yy)
4171 int element = Feld[xx][yy];
4173 if (IS_BELT_SWITCH(element))
4175 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
4177 if (e_belt_nr == belt_nr)
4179 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
4180 DrawLevelField(xx, yy);
4183 else if (IS_BELT(element) && belt_dir != MV_NONE)
4185 int e_belt_nr = getBeltNrFromBeltElement(element);
4187 if (e_belt_nr == belt_nr)
4189 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
4191 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
4192 DrawLevelField(xx, yy);
4195 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
4197 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
4199 if (e_belt_nr == belt_nr)
4201 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
4203 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
4204 DrawLevelField(xx, yy);
4210 static void ToggleSwitchgateSwitch(int x, int y)
4214 game.switchgate_pos = !game.switchgate_pos;
4216 SCAN_PLAYFIELD(xx, yy)
4218 int element = Feld[xx][yy];
4220 #if !USE_BOTH_SWITCHGATE_SWITCHES
4221 if (element == EL_SWITCHGATE_SWITCH_UP ||
4222 element == EL_SWITCHGATE_SWITCH_DOWN)
4224 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
4225 DrawLevelField(xx, yy);
4228 if (element == EL_SWITCHGATE_SWITCH_UP)
4230 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
4231 DrawLevelField(xx, yy);
4233 else if (element == EL_SWITCHGATE_SWITCH_DOWN)
4235 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
4236 DrawLevelField(xx, yy);
4239 else if (element == EL_SWITCHGATE_OPEN ||
4240 element == EL_SWITCHGATE_OPENING)
4242 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
4244 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
4246 else if (element == EL_SWITCHGATE_CLOSED ||
4247 element == EL_SWITCHGATE_CLOSING)
4249 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
4251 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
4256 static int getInvisibleActiveFromInvisibleElement(int element)
4258 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
4259 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
4260 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
4264 static int getInvisibleFromInvisibleActiveElement(int element)
4266 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
4267 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
4268 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
4272 static void RedrawAllLightSwitchesAndInvisibleElements()
4276 SCAN_PLAYFIELD(x, y)
4278 int element = Feld[x][y];
4280 if (element == EL_LIGHT_SWITCH &&
4281 game.light_time_left > 0)
4283 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
4284 DrawLevelField(x, y);
4286 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
4287 game.light_time_left == 0)
4289 Feld[x][y] = EL_LIGHT_SWITCH;
4290 DrawLevelField(x, y);
4292 else if (element == EL_EMC_DRIPPER &&
4293 game.light_time_left > 0)
4295 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
4296 DrawLevelField(x, y);
4298 else if (element == EL_EMC_DRIPPER_ACTIVE &&
4299 game.light_time_left == 0)
4301 Feld[x][y] = EL_EMC_DRIPPER;
4302 DrawLevelField(x, y);
4304 else if (element == EL_INVISIBLE_STEELWALL ||
4305 element == EL_INVISIBLE_WALL ||
4306 element == EL_INVISIBLE_SAND)
4308 if (game.light_time_left > 0)
4309 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
4311 DrawLevelField(x, y);
4313 /* uncrumble neighbour fields, if needed */
4314 if (element == EL_INVISIBLE_SAND)
4315 DrawLevelFieldCrumbledSandNeighbours(x, y);
4317 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
4318 element == EL_INVISIBLE_WALL_ACTIVE ||
4319 element == EL_INVISIBLE_SAND_ACTIVE)
4321 if (game.light_time_left == 0)
4322 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
4324 DrawLevelField(x, y);
4326 /* re-crumble neighbour fields, if needed */
4327 if (element == EL_INVISIBLE_SAND)
4328 DrawLevelFieldCrumbledSandNeighbours(x, y);
4333 static void RedrawAllInvisibleElementsForLenses()
4337 SCAN_PLAYFIELD(x, y)
4339 int element = Feld[x][y];
4341 if (element == EL_EMC_DRIPPER &&
4342 game.lenses_time_left > 0)
4344 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
4345 DrawLevelField(x, y);
4347 else if (element == EL_EMC_DRIPPER_ACTIVE &&
4348 game.lenses_time_left == 0)
4350 Feld[x][y] = EL_EMC_DRIPPER;
4351 DrawLevelField(x, y);
4353 else if (element == EL_INVISIBLE_STEELWALL ||
4354 element == EL_INVISIBLE_WALL ||
4355 element == EL_INVISIBLE_SAND)
4357 if (game.lenses_time_left > 0)
4358 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
4360 DrawLevelField(x, y);
4362 /* uncrumble neighbour fields, if needed */
4363 if (element == EL_INVISIBLE_SAND)
4364 DrawLevelFieldCrumbledSandNeighbours(x, y);
4366 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
4367 element == EL_INVISIBLE_WALL_ACTIVE ||
4368 element == EL_INVISIBLE_SAND_ACTIVE)
4370 if (game.lenses_time_left == 0)
4371 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
4373 DrawLevelField(x, y);
4375 /* re-crumble neighbour fields, if needed */
4376 if (element == EL_INVISIBLE_SAND)
4377 DrawLevelFieldCrumbledSandNeighbours(x, y);
4382 static void RedrawAllInvisibleElementsForMagnifier()
4386 SCAN_PLAYFIELD(x, y)
4388 int element = Feld[x][y];
4390 if (element == EL_EMC_FAKE_GRASS &&
4391 game.magnify_time_left > 0)
4393 Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
4394 DrawLevelField(x, y);
4396 else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
4397 game.magnify_time_left == 0)
4399 Feld[x][y] = EL_EMC_FAKE_GRASS;
4400 DrawLevelField(x, y);
4402 else if (IS_GATE_GRAY(element) &&
4403 game.magnify_time_left > 0)
4405 Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
4406 element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
4407 IS_EM_GATE_GRAY(element) ?
4408 element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
4409 IS_EMC_GATE_GRAY(element) ?
4410 element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
4412 DrawLevelField(x, y);
4414 else if (IS_GATE_GRAY_ACTIVE(element) &&
4415 game.magnify_time_left == 0)
4417 Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
4418 element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
4419 IS_EM_GATE_GRAY_ACTIVE(element) ?
4420 element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
4421 IS_EMC_GATE_GRAY_ACTIVE(element) ?
4422 element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
4424 DrawLevelField(x, y);
4429 static void ToggleLightSwitch(int x, int y)
4431 int element = Feld[x][y];
4433 game.light_time_left =
4434 (element == EL_LIGHT_SWITCH ?
4435 level.time_light * FRAMES_PER_SECOND : 0);
4437 RedrawAllLightSwitchesAndInvisibleElements();
4440 static void ActivateTimegateSwitch(int x, int y)
4444 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
4446 SCAN_PLAYFIELD(xx, yy)
4448 int element = Feld[xx][yy];
4450 if (element == EL_TIMEGATE_CLOSED ||
4451 element == EL_TIMEGATE_CLOSING)
4453 Feld[xx][yy] = EL_TIMEGATE_OPENING;
4454 PlayLevelSound(xx, yy, SND_TIMEGATE_OPENING);
4458 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
4460 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
4461 DrawLevelField(xx, yy);
4467 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
4470 void Impact(int x, int y)
4472 boolean last_line = (y == lev_fieldy - 1);
4473 boolean object_hit = FALSE;
4474 boolean impact = (last_line || object_hit);
4475 int element = Feld[x][y];
4476 int smashed = EL_STEELWALL;
4478 if (!last_line) /* check if element below was hit */
4480 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
4483 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
4484 MovDir[x][y + 1] != MV_DOWN ||
4485 MovPos[x][y + 1] <= TILEY / 2));
4487 /* do not smash moving elements that left the smashed field in time */
4488 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
4489 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
4492 #if USE_QUICKSAND_IMPACT_BUGFIX
4493 if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
4495 RemoveMovingField(x, y + 1);
4496 Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
4497 Feld[x][y + 2] = EL_ROCK;
4498 DrawLevelField(x, y + 2);
4505 smashed = MovingOrBlocked2Element(x, y + 1);
4507 impact = (last_line || object_hit);
4510 if (!last_line && smashed == EL_ACID) /* element falls into acid */
4512 SplashAcid(x, y + 1);
4516 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
4517 /* only reset graphic animation if graphic really changes after impact */
4519 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
4521 ResetGfxAnimation(x, y);
4522 DrawLevelField(x, y);
4525 if (impact && CAN_EXPLODE_IMPACT(element))
4530 else if (impact && element == EL_PEARL)
4532 ResetGfxAnimation(x, y);
4534 Feld[x][y] = EL_PEARL_BREAKING;
4535 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4538 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
4540 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4545 if (impact && element == EL_AMOEBA_DROP)
4547 if (object_hit && IS_PLAYER(x, y + 1))
4548 KillPlayerUnlessEnemyProtected(x, y + 1);
4549 else if (object_hit && smashed == EL_PENGUIN)
4553 Feld[x][y] = EL_AMOEBA_GROWING;
4554 Store[x][y] = EL_AMOEBA_WET;
4556 ResetRandomAnimationValue(x, y);
4561 if (object_hit) /* check which object was hit */
4563 if (CAN_PASS_MAGIC_WALL(element) &&
4564 (smashed == EL_MAGIC_WALL ||
4565 smashed == EL_BD_MAGIC_WALL))
4568 int activated_magic_wall =
4569 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
4570 EL_BD_MAGIC_WALL_ACTIVE);
4572 /* activate magic wall / mill */
4573 SCAN_PLAYFIELD(xx, yy)
4574 if (Feld[xx][yy] == smashed)
4575 Feld[xx][yy] = activated_magic_wall;
4577 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
4578 game.magic_wall_active = TRUE;
4580 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
4581 SND_MAGIC_WALL_ACTIVATING :
4582 SND_BD_MAGIC_WALL_ACTIVATING));
4585 if (IS_PLAYER(x, y + 1))
4587 if (CAN_SMASH_PLAYER(element))
4589 KillPlayerUnlessEnemyProtected(x, y + 1);
4593 else if (smashed == EL_PENGUIN)
4595 if (CAN_SMASH_PLAYER(element))
4601 else if (element == EL_BD_DIAMOND)
4603 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
4609 else if (((element == EL_SP_INFOTRON ||
4610 element == EL_SP_ZONK) &&
4611 (smashed == EL_SP_SNIKSNAK ||
4612 smashed == EL_SP_ELECTRON ||
4613 smashed == EL_SP_DISK_ORANGE)) ||
4614 (element == EL_SP_INFOTRON &&
4615 smashed == EL_SP_DISK_YELLOW))
4620 else if (CAN_SMASH_EVERYTHING(element))
4622 if (IS_CLASSIC_ENEMY(smashed) ||
4623 CAN_EXPLODE_SMASHED(smashed))
4628 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
4630 if (smashed == EL_LAMP ||
4631 smashed == EL_LAMP_ACTIVE)
4636 else if (smashed == EL_NUT)
4638 Feld[x][y + 1] = EL_NUT_BREAKING;
4639 PlayLevelSound(x, y, SND_NUT_BREAKING);
4640 RaiseScoreElement(EL_NUT);
4643 else if (smashed == EL_PEARL)
4645 ResetGfxAnimation(x, y);
4647 Feld[x][y + 1] = EL_PEARL_BREAKING;
4648 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4651 else if (smashed == EL_DIAMOND)
4653 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
4654 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
4657 else if (IS_BELT_SWITCH(smashed))
4659 ToggleBeltSwitch(x, y + 1);
4661 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
4662 smashed == EL_SWITCHGATE_SWITCH_DOWN)
4664 ToggleSwitchgateSwitch(x, y + 1);
4666 else if (smashed == EL_LIGHT_SWITCH ||
4667 smashed == EL_LIGHT_SWITCH_ACTIVE)
4669 ToggleLightSwitch(x, y + 1);
4674 TestIfElementSmashesCustomElement(x, y, MV_DOWN);
4677 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4679 CheckElementChangeBySide(x, y + 1, smashed, element,
4680 CE_SWITCHED, CH_SIDE_TOP);
4681 CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
4687 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4692 /* play sound of magic wall / mill */
4694 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
4695 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
4697 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
4698 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
4699 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
4700 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
4705 /* play sound of object that hits the ground */
4706 if (last_line || object_hit)
4707 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4710 inline static void TurnRoundExt(int x, int y)
4722 { 0, 0 }, { 0, 0 }, { 0, 0 },
4727 int left, right, back;
4731 { MV_DOWN, MV_UP, MV_RIGHT },
4732 { MV_UP, MV_DOWN, MV_LEFT },
4734 { MV_LEFT, MV_RIGHT, MV_DOWN },
4738 { MV_RIGHT, MV_LEFT, MV_UP }
4741 int element = Feld[x][y];
4742 int move_pattern = element_info[element].move_pattern;
4744 int old_move_dir = MovDir[x][y];
4745 int left_dir = turn[old_move_dir].left;
4746 int right_dir = turn[old_move_dir].right;
4747 int back_dir = turn[old_move_dir].back;
4749 int left_dx = move_xy[left_dir].dx, left_dy = move_xy[left_dir].dy;
4750 int right_dx = move_xy[right_dir].dx, right_dy = move_xy[right_dir].dy;
4751 int move_dx = move_xy[old_move_dir].dx, move_dy = move_xy[old_move_dir].dy;
4752 int back_dx = move_xy[back_dir].dx, back_dy = move_xy[back_dir].dy;
4754 int left_x = x + left_dx, left_y = y + left_dy;
4755 int right_x = x + right_dx, right_y = y + right_dy;
4756 int move_x = x + move_dx, move_y = y + move_dy;
4760 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4762 TestIfBadThingTouchesOtherBadThing(x, y);
4764 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
4765 MovDir[x][y] = right_dir;
4766 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4767 MovDir[x][y] = left_dir;
4769 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
4771 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
4774 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
4776 TestIfBadThingTouchesOtherBadThing(x, y);
4778 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
4779 MovDir[x][y] = left_dir;
4780 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4781 MovDir[x][y] = right_dir;
4783 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
4785 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
4788 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4790 TestIfBadThingTouchesOtherBadThing(x, y);
4792 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
4793 MovDir[x][y] = left_dir;
4794 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
4795 MovDir[x][y] = right_dir;
4797 if (MovDir[x][y] != old_move_dir)
4800 else if (element == EL_YAMYAM)
4802 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
4803 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
4805 if (can_turn_left && can_turn_right)
4806 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4807 else if (can_turn_left)
4808 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4809 else if (can_turn_right)
4810 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4812 MovDir[x][y] = back_dir;
4814 MovDelay[x][y] = 16 + 16 * RND(3);
4816 else if (element == EL_DARK_YAMYAM)
4818 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4820 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4823 if (can_turn_left && can_turn_right)
4824 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4825 else if (can_turn_left)
4826 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4827 else if (can_turn_right)
4828 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4830 MovDir[x][y] = back_dir;
4832 MovDelay[x][y] = 16 + 16 * RND(3);
4834 else if (element == EL_PACMAN)
4836 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
4837 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
4839 if (can_turn_left && can_turn_right)
4840 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4841 else if (can_turn_left)
4842 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4843 else if (can_turn_right)
4844 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4846 MovDir[x][y] = back_dir;
4848 MovDelay[x][y] = 6 + RND(40);
4850 else if (element == EL_PIG)
4852 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
4853 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
4854 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
4855 boolean should_turn_left, should_turn_right, should_move_on;
4857 int rnd = RND(rnd_value);
4859 should_turn_left = (can_turn_left &&
4861 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
4862 y + back_dy + left_dy)));
4863 should_turn_right = (can_turn_right &&
4865 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
4866 y + back_dy + right_dy)));
4867 should_move_on = (can_move_on &&
4870 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
4871 y + move_dy + left_dy) ||
4872 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
4873 y + move_dy + right_dy)));
4875 if (should_turn_left || should_turn_right || should_move_on)
4877 if (should_turn_left && should_turn_right && should_move_on)
4878 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
4879 rnd < 2 * rnd_value / 3 ? right_dir :
4881 else if (should_turn_left && should_turn_right)
4882 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4883 else if (should_turn_left && should_move_on)
4884 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
4885 else if (should_turn_right && should_move_on)
4886 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
4887 else if (should_turn_left)
4888 MovDir[x][y] = left_dir;
4889 else if (should_turn_right)
4890 MovDir[x][y] = right_dir;
4891 else if (should_move_on)
4892 MovDir[x][y] = old_move_dir;
4894 else if (can_move_on && rnd > rnd_value / 8)
4895 MovDir[x][y] = old_move_dir;
4896 else if (can_turn_left && can_turn_right)
4897 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4898 else if (can_turn_left && rnd > rnd_value / 8)
4899 MovDir[x][y] = left_dir;
4900 else if (can_turn_right && rnd > rnd_value/8)
4901 MovDir[x][y] = right_dir;
4903 MovDir[x][y] = back_dir;
4905 xx = x + move_xy[MovDir[x][y]].dx;
4906 yy = y + move_xy[MovDir[x][y]].dy;
4908 if (!IN_LEV_FIELD(xx, yy) ||
4909 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
4910 MovDir[x][y] = old_move_dir;
4914 else if (element == EL_DRAGON)
4916 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
4917 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
4918 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
4920 int rnd = RND(rnd_value);
4922 if (can_move_on && rnd > rnd_value / 8)
4923 MovDir[x][y] = old_move_dir;
4924 else if (can_turn_left && can_turn_right)
4925 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4926 else if (can_turn_left && rnd > rnd_value / 8)
4927 MovDir[x][y] = left_dir;
4928 else if (can_turn_right && rnd > rnd_value / 8)
4929 MovDir[x][y] = right_dir;
4931 MovDir[x][y] = back_dir;
4933 xx = x + move_xy[MovDir[x][y]].dx;
4934 yy = y + move_xy[MovDir[x][y]].dy;
4936 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
4937 MovDir[x][y] = old_move_dir;
4941 else if (element == EL_MOLE)
4943 boolean can_move_on =
4944 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
4945 IS_AMOEBOID(Feld[move_x][move_y]) ||
4946 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
4949 boolean can_turn_left =
4950 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
4951 IS_AMOEBOID(Feld[left_x][left_y])));
4953 boolean can_turn_right =
4954 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
4955 IS_AMOEBOID(Feld[right_x][right_y])));
4957 if (can_turn_left && can_turn_right)
4958 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
4959 else if (can_turn_left)
4960 MovDir[x][y] = left_dir;
4962 MovDir[x][y] = right_dir;
4965 if (MovDir[x][y] != old_move_dir)
4968 else if (element == EL_BALLOON)
4970 MovDir[x][y] = game.wind_direction;
4973 else if (element == EL_SPRING)
4975 #if USE_NEW_SPRING_BUMPER
4976 if (MovDir[x][y] & MV_HORIZONTAL)
4978 if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
4979 !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
4981 Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
4982 ResetGfxAnimation(move_x, move_y);
4983 DrawLevelField(move_x, move_y);
4985 MovDir[x][y] = back_dir;
4987 else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
4988 SPRING_CAN_ENTER_FIELD(element, x, y + 1))
4989 MovDir[x][y] = MV_NONE;
4992 if (MovDir[x][y] & MV_HORIZONTAL &&
4993 (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
4994 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
4995 MovDir[x][y] = MV_NONE;
5000 else if (element == EL_ROBOT ||
5001 element == EL_SATELLITE ||
5002 element == EL_PENGUIN ||
5003 element == EL_EMC_ANDROID)
5005 int attr_x = -1, attr_y = -1;
5016 for (i = 0; i < MAX_PLAYERS; i++)
5018 struct PlayerInfo *player = &stored_player[i];
5019 int jx = player->jx, jy = player->jy;
5021 if (!player->active)
5025 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
5033 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
5034 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
5035 game.engine_version < VERSION_IDENT(3,1,0,0)))
5041 if (element == EL_PENGUIN)
5044 static int xy[4][2] =
5052 for (i = 0; i < NUM_DIRECTIONS; i++)
5054 int ex = x + xy[i][0];
5055 int ey = y + xy[i][1];
5057 if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
5066 MovDir[x][y] = MV_NONE;
5068 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
5069 else if (attr_x > x)
5070 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
5072 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
5073 else if (attr_y > y)
5074 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
5076 if (element == EL_ROBOT)
5080 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5081 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
5082 Moving2Blocked(x, y, &newx, &newy);
5084 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
5085 MovDelay[x][y] = 8 + 8 * !RND(3);
5087 MovDelay[x][y] = 16;
5089 else if (element == EL_PENGUIN)
5095 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5097 boolean first_horiz = RND(2);
5098 int new_move_dir = MovDir[x][y];
5101 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5102 Moving2Blocked(x, y, &newx, &newy);
5104 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
5108 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5109 Moving2Blocked(x, y, &newx, &newy);
5111 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
5114 MovDir[x][y] = old_move_dir;
5118 else if (element == EL_SATELLITE)
5124 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5126 boolean first_horiz = RND(2);
5127 int new_move_dir = MovDir[x][y];
5130 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5131 Moving2Blocked(x, y, &newx, &newy);
5133 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
5137 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5138 Moving2Blocked(x, y, &newx, &newy);
5140 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
5143 MovDir[x][y] = old_move_dir;
5147 else if (element == EL_EMC_ANDROID)
5149 static int check_pos[16] =
5151 -1, /* 0 => (invalid) */
5152 7, /* 1 => MV_LEFT */
5153 3, /* 2 => MV_RIGHT */
5154 -1, /* 3 => (invalid) */
5156 0, /* 5 => MV_LEFT | MV_UP */
5157 2, /* 6 => MV_RIGHT | MV_UP */
5158 -1, /* 7 => (invalid) */
5159 5, /* 8 => MV_DOWN */
5160 6, /* 9 => MV_LEFT | MV_DOWN */
5161 4, /* 10 => MV_RIGHT | MV_DOWN */
5162 -1, /* 11 => (invalid) */
5163 -1, /* 12 => (invalid) */
5164 -1, /* 13 => (invalid) */
5165 -1, /* 14 => (invalid) */
5166 -1, /* 15 => (invalid) */
5174 { -1, -1, MV_LEFT | MV_UP },
5176 { +1, -1, MV_RIGHT | MV_UP },
5177 { +1, 0, MV_RIGHT },
5178 { +1, +1, MV_RIGHT | MV_DOWN },
5180 { -1, +1, MV_LEFT | MV_DOWN },
5183 int start_pos, check_order;
5184 boolean can_clone = FALSE;
5187 /* check if there is any free field around current position */
5188 for (i = 0; i < 8; i++)
5190 int newx = x + check_xy[i].dx;
5191 int newy = y + check_xy[i].dy;
5193 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5201 if (can_clone) /* randomly find an element to clone */
5205 start_pos = check_pos[RND(8)];
5206 check_order = (RND(2) ? -1 : +1);
5208 for (i = 0; i < 8; i++)
5210 int pos_raw = start_pos + i * check_order;
5211 int pos = (pos_raw + 8) % 8;
5212 int newx = x + check_xy[pos].dx;
5213 int newy = y + check_xy[pos].dy;
5215 if (ANDROID_CAN_CLONE_FIELD(newx, newy))
5217 element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
5218 element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
5220 Store[x][y] = Feld[newx][newy];
5229 if (can_clone) /* randomly find a direction to move */
5233 start_pos = check_pos[RND(8)];
5234 check_order = (RND(2) ? -1 : +1);
5236 for (i = 0; i < 8; i++)
5238 int pos_raw = start_pos + i * check_order;
5239 int pos = (pos_raw + 8) % 8;
5240 int newx = x + check_xy[pos].dx;
5241 int newy = y + check_xy[pos].dy;
5242 int new_move_dir = check_xy[pos].dir;
5244 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5246 MovDir[x][y] = new_move_dir;
5247 MovDelay[x][y] = level.android_clone_time * 8 + 1;
5256 if (can_clone) /* cloning and moving successful */
5259 /* cannot clone -- try to move towards player */
5261 start_pos = check_pos[MovDir[x][y] & 0x0f];
5262 check_order = (RND(2) ? -1 : +1);
5264 for (i = 0; i < 3; i++)
5266 /* first check start_pos, then previous/next or (next/previous) pos */
5267 int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
5268 int pos = (pos_raw + 8) % 8;
5269 int newx = x + check_xy[pos].dx;
5270 int newy = y + check_xy[pos].dy;
5271 int new_move_dir = check_xy[pos].dir;
5273 if (IS_PLAYER(newx, newy))
5276 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
5278 MovDir[x][y] = new_move_dir;
5279 MovDelay[x][y] = level.android_move_time * 8 + 1;
5286 else if (move_pattern == MV_TURNING_LEFT ||
5287 move_pattern == MV_TURNING_RIGHT ||
5288 move_pattern == MV_TURNING_LEFT_RIGHT ||
5289 move_pattern == MV_TURNING_RIGHT_LEFT ||
5290 move_pattern == MV_TURNING_RANDOM ||
5291 move_pattern == MV_ALL_DIRECTIONS)
5293 boolean can_turn_left =
5294 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
5295 boolean can_turn_right =
5296 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
5298 if (element_info[element].move_stepsize == 0) /* "not moving" */
5301 if (move_pattern == MV_TURNING_LEFT)
5302 MovDir[x][y] = left_dir;
5303 else if (move_pattern == MV_TURNING_RIGHT)
5304 MovDir[x][y] = right_dir;
5305 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
5306 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
5307 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
5308 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
5309 else if (move_pattern == MV_TURNING_RANDOM)
5310 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
5311 can_turn_right && !can_turn_left ? right_dir :
5312 RND(2) ? left_dir : right_dir);
5313 else if (can_turn_left && can_turn_right)
5314 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5315 else if (can_turn_left)
5316 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5317 else if (can_turn_right)
5318 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5320 MovDir[x][y] = back_dir;
5322 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5324 else if (move_pattern == MV_HORIZONTAL ||
5325 move_pattern == MV_VERTICAL)
5327 if (move_pattern & old_move_dir)
5328 MovDir[x][y] = back_dir;
5329 else if (move_pattern == MV_HORIZONTAL)
5330 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
5331 else if (move_pattern == MV_VERTICAL)
5332 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
5334 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5336 else if (move_pattern & MV_ANY_DIRECTION)
5338 MovDir[x][y] = move_pattern;
5339 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5341 else if (move_pattern & MV_WIND_DIRECTION)
5343 MovDir[x][y] = game.wind_direction;
5344 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5346 else if (move_pattern == MV_ALONG_LEFT_SIDE)
5348 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
5349 MovDir[x][y] = left_dir;
5350 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5351 MovDir[x][y] = right_dir;
5353 if (MovDir[x][y] != old_move_dir)
5354 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5356 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
5358 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
5359 MovDir[x][y] = right_dir;
5360 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5361 MovDir[x][y] = left_dir;
5363 if (MovDir[x][y] != old_move_dir)
5364 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5366 else if (move_pattern == MV_TOWARDS_PLAYER ||
5367 move_pattern == MV_AWAY_FROM_PLAYER)
5369 int attr_x = -1, attr_y = -1;
5371 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
5382 for (i = 0; i < MAX_PLAYERS; i++)
5384 struct PlayerInfo *player = &stored_player[i];
5385 int jx = player->jx, jy = player->jy;
5387 if (!player->active)
5391 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
5399 MovDir[x][y] = MV_NONE;
5401 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
5402 else if (attr_x > x)
5403 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
5405 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
5406 else if (attr_y > y)
5407 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
5409 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5411 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5413 boolean first_horiz = RND(2);
5414 int new_move_dir = MovDir[x][y];
5416 if (element_info[element].move_stepsize == 0) /* "not moving" */
5418 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
5419 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5425 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5426 Moving2Blocked(x, y, &newx, &newy);
5428 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5432 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5433 Moving2Blocked(x, y, &newx, &newy);
5435 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5438 MovDir[x][y] = old_move_dir;
5441 else if (move_pattern == MV_WHEN_PUSHED ||
5442 move_pattern == MV_WHEN_DROPPED)
5444 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5445 MovDir[x][y] = MV_NONE;
5449 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
5451 static int test_xy[7][2] =
5461 static int test_dir[7] =
5471 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
5472 int move_preference = -1000000; /* start with very low preference */
5473 int new_move_dir = MV_NONE;
5474 int start_test = RND(4);
5477 for (i = 0; i < NUM_DIRECTIONS; i++)
5479 int move_dir = test_dir[start_test + i];
5480 int move_dir_preference;
5482 xx = x + test_xy[start_test + i][0];
5483 yy = y + test_xy[start_test + i][1];
5485 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
5486 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
5488 new_move_dir = move_dir;
5493 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
5496 move_dir_preference = -1 * RunnerVisit[xx][yy];
5497 if (hunter_mode && PlayerVisit[xx][yy] > 0)
5498 move_dir_preference = PlayerVisit[xx][yy];
5500 if (move_dir_preference > move_preference)
5502 /* prefer field that has not been visited for the longest time */
5503 move_preference = move_dir_preference;
5504 new_move_dir = move_dir;
5506 else if (move_dir_preference == move_preference &&
5507 move_dir == old_move_dir)
5509 /* prefer last direction when all directions are preferred equally */
5510 move_preference = move_dir_preference;
5511 new_move_dir = move_dir;
5515 MovDir[x][y] = new_move_dir;
5516 if (old_move_dir != new_move_dir)
5517 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5521 static void TurnRound(int x, int y)
5523 int direction = MovDir[x][y];
5527 GfxDir[x][y] = MovDir[x][y];
5529 if (direction != MovDir[x][y])
5533 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
5535 ResetGfxFrame(x, y, FALSE);
5538 static boolean JustBeingPushed(int x, int y)
5542 for (i = 0; i < MAX_PLAYERS; i++)
5544 struct PlayerInfo *player = &stored_player[i];
5546 if (player->active && player->is_pushing && player->MovPos)
5548 int next_jx = player->jx + (player->jx - player->last_jx);
5549 int next_jy = player->jy + (player->jy - player->last_jy);
5551 if (x == next_jx && y == next_jy)
5559 void StartMoving(int x, int y)
5561 boolean started_moving = FALSE; /* some elements can fall _and_ move */
5562 int element = Feld[x][y];
5567 if (MovDelay[x][y] == 0)
5568 GfxAction[x][y] = ACTION_DEFAULT;
5570 if (CAN_FALL(element) && y < lev_fieldy - 1)
5572 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
5573 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
5574 if (JustBeingPushed(x, y))
5577 if (element == EL_QUICKSAND_FULL)
5579 if (IS_FREE(x, y + 1))
5581 InitMovingField(x, y, MV_DOWN);
5582 started_moving = TRUE;
5584 Feld[x][y] = EL_QUICKSAND_EMPTYING;
5585 #if USE_QUICKSAND_BD_ROCK_BUGFIX
5586 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
5587 Store[x][y] = EL_ROCK;
5589 Store[x][y] = EL_ROCK;
5592 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
5594 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5596 if (!MovDelay[x][y])
5597 MovDelay[x][y] = TILEY + 1;
5606 Feld[x][y] = EL_QUICKSAND_EMPTY;
5607 Feld[x][y + 1] = EL_QUICKSAND_FULL;
5608 Store[x][y + 1] = Store[x][y];
5611 PlayLevelSoundAction(x, y, ACTION_FILLING);
5614 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
5615 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5617 InitMovingField(x, y, MV_DOWN);
5618 started_moving = TRUE;
5620 Feld[x][y] = EL_QUICKSAND_FILLING;
5621 Store[x][y] = element;
5623 PlayLevelSoundAction(x, y, ACTION_FILLING);
5625 else if (element == EL_MAGIC_WALL_FULL)
5627 if (IS_FREE(x, y + 1))
5629 InitMovingField(x, y, MV_DOWN);
5630 started_moving = TRUE;
5632 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
5633 Store[x][y] = EL_CHANGED(Store[x][y]);
5635 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
5637 if (!MovDelay[x][y])
5638 MovDelay[x][y] = TILEY/4 + 1;
5647 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
5648 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
5649 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
5653 else if (element == EL_BD_MAGIC_WALL_FULL)
5655 if (IS_FREE(x, y + 1))
5657 InitMovingField(x, y, MV_DOWN);
5658 started_moving = TRUE;
5660 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
5661 Store[x][y] = EL_CHANGED2(Store[x][y]);
5663 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
5665 if (!MovDelay[x][y])
5666 MovDelay[x][y] = TILEY/4 + 1;
5675 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
5676 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
5677 Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
5681 else if (CAN_PASS_MAGIC_WALL(element) &&
5682 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
5683 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
5685 InitMovingField(x, y, MV_DOWN);
5686 started_moving = TRUE;
5689 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
5690 EL_BD_MAGIC_WALL_FILLING);
5691 Store[x][y] = element;
5693 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
5695 SplashAcid(x, y + 1);
5697 InitMovingField(x, y, MV_DOWN);
5698 started_moving = TRUE;
5700 Store[x][y] = EL_ACID;
5703 #if USE_FIX_IMPACT_COLLISION
5704 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5705 CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
5707 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5708 CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
5710 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
5711 CAN_FALL(element) && WasJustFalling[x][y] &&
5712 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
5714 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
5715 CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
5716 (Feld[x][y + 1] == EL_BLOCKED)))
5718 /* this is needed for a special case not covered by calling "Impact()"
5719 from "ContinueMoving()": if an element moves to a tile directly below
5720 another element which was just falling on that tile (which was empty
5721 in the previous frame), the falling element above would just stop
5722 instead of smashing the element below (in previous version, the above
5723 element was just checked for "moving" instead of "falling", resulting
5724 in incorrect smashes caused by horizontal movement of the above
5725 element; also, the case of the player being the element to smash was
5726 simply not covered here... :-/ ) */
5728 CheckCollision[x][y] = 0;
5729 CheckImpact[x][y] = 0;
5733 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
5735 if (MovDir[x][y] == MV_NONE)
5737 InitMovingField(x, y, MV_DOWN);
5738 started_moving = TRUE;
5741 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
5743 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
5744 MovDir[x][y] = MV_DOWN;
5746 InitMovingField(x, y, MV_DOWN);
5747 started_moving = TRUE;
5749 else if (element == EL_AMOEBA_DROP)
5751 Feld[x][y] = EL_AMOEBA_GROWING;
5752 Store[x][y] = EL_AMOEBA_WET;
5754 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
5755 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
5756 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
5757 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
5759 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
5760 (IS_FREE(x - 1, y + 1) ||
5761 Feld[x - 1][y + 1] == EL_ACID));
5762 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
5763 (IS_FREE(x + 1, y + 1) ||
5764 Feld[x + 1][y + 1] == EL_ACID));
5765 boolean can_fall_any = (can_fall_left || can_fall_right);
5766 boolean can_fall_both = (can_fall_left && can_fall_right);
5767 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
5769 #if USE_NEW_ALL_SLIPPERY
5770 if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
5772 if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
5773 can_fall_right = FALSE;
5774 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
5775 can_fall_left = FALSE;
5776 else if (slippery_type == SLIPPERY_ONLY_LEFT)
5777 can_fall_right = FALSE;
5778 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
5779 can_fall_left = FALSE;
5781 can_fall_any = (can_fall_left || can_fall_right);
5782 can_fall_both = FALSE;
5785 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
5787 if (slippery_type == SLIPPERY_ONLY_LEFT)
5788 can_fall_right = FALSE;
5789 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
5790 can_fall_left = FALSE;
5791 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
5792 can_fall_right = FALSE;
5793 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
5794 can_fall_left = FALSE;
5796 can_fall_any = (can_fall_left || can_fall_right);
5797 can_fall_both = (can_fall_left && can_fall_right);
5801 #if USE_NEW_ALL_SLIPPERY
5803 #if USE_NEW_SP_SLIPPERY
5804 /* !!! better use the same properties as for custom elements here !!! */
5805 else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
5806 can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
5808 can_fall_right = FALSE; /* slip down on left side */
5809 can_fall_both = FALSE;
5814 #if USE_NEW_ALL_SLIPPERY
5817 if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
5818 can_fall_right = FALSE; /* slip down on left side */
5820 can_fall_left = !(can_fall_right = RND(2));
5822 can_fall_both = FALSE;
5827 if (game.emulation == EMU_BOULDERDASH ||
5828 element == EL_BD_ROCK || element == EL_BD_DIAMOND)
5829 can_fall_right = FALSE; /* slip down on left side */
5831 can_fall_left = !(can_fall_right = RND(2));
5833 can_fall_both = FALSE;
5839 /* if not determined otherwise, prefer left side for slipping down */
5840 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
5841 started_moving = TRUE;
5845 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
5847 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
5850 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
5851 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
5852 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
5853 int belt_dir = game.belt_dir[belt_nr];
5855 if ((belt_dir == MV_LEFT && left_is_free) ||
5856 (belt_dir == MV_RIGHT && right_is_free))
5858 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
5860 InitMovingField(x, y, belt_dir);
5861 started_moving = TRUE;
5863 Pushed[x][y] = TRUE;
5864 Pushed[nextx][y] = TRUE;
5866 GfxAction[x][y] = ACTION_DEFAULT;
5870 MovDir[x][y] = 0; /* if element was moving, stop it */
5875 /* not "else if" because of elements that can fall and move (EL_SPRING) */
5877 if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
5879 if (CAN_MOVE(element) && !started_moving)
5882 int move_pattern = element_info[element].move_pattern;
5887 if (MovDir[x][y] == MV_NONE)
5889 printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
5890 x, y, element, element_info[element].token_name);
5891 printf("StartMoving(): This should never happen!\n");
5896 Moving2Blocked(x, y, &newx, &newy);
5898 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
5901 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5902 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5904 WasJustMoving[x][y] = 0;
5905 CheckCollision[x][y] = 0;
5907 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
5909 if (Feld[x][y] != element) /* element has changed */
5913 if (!MovDelay[x][y]) /* start new movement phase */
5915 /* all objects that can change their move direction after each step
5916 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
5918 if (element != EL_YAMYAM &&
5919 element != EL_DARK_YAMYAM &&
5920 element != EL_PACMAN &&
5921 !(move_pattern & MV_ANY_DIRECTION) &&
5922 move_pattern != MV_TURNING_LEFT &&
5923 move_pattern != MV_TURNING_RIGHT &&
5924 move_pattern != MV_TURNING_LEFT_RIGHT &&
5925 move_pattern != MV_TURNING_RIGHT_LEFT &&
5926 move_pattern != MV_TURNING_RANDOM)
5930 if (MovDelay[x][y] && (element == EL_BUG ||
5931 element == EL_SPACESHIP ||
5932 element == EL_SP_SNIKSNAK ||
5933 element == EL_SP_ELECTRON ||
5934 element == EL_MOLE))
5935 DrawLevelField(x, y);
5939 if (MovDelay[x][y]) /* wait some time before next movement */
5943 if (element == EL_ROBOT ||
5944 element == EL_YAMYAM ||
5945 element == EL_DARK_YAMYAM)
5947 DrawLevelElementAnimationIfNeeded(x, y, element);
5948 PlayLevelSoundAction(x, y, ACTION_WAITING);
5950 else if (element == EL_SP_ELECTRON)
5951 DrawLevelElementAnimationIfNeeded(x, y, element);
5952 else if (element == EL_DRAGON)
5955 int dir = MovDir[x][y];
5956 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
5957 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
5958 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
5959 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
5960 dir == MV_UP ? IMG_FLAMES_1_UP :
5961 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
5962 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5964 GfxAction[x][y] = ACTION_ATTACKING;
5966 if (IS_PLAYER(x, y))
5967 DrawPlayerField(x, y);
5969 DrawLevelField(x, y);
5971 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
5973 for (i = 1; i <= 3; i++)
5975 int xx = x + i * dx;
5976 int yy = y + i * dy;
5977 int sx = SCREENX(xx);
5978 int sy = SCREENY(yy);
5979 int flame_graphic = graphic + (i - 1);
5981 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
5986 int flamed = MovingOrBlocked2Element(xx, yy);
5990 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
5992 else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
5993 RemoveMovingField(xx, yy);
5995 RemoveField(xx, yy);
5997 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
6000 RemoveMovingField(xx, yy);
6003 ChangeDelay[xx][yy] = 0;
6005 Feld[xx][yy] = EL_FLAMES;
6007 if (IN_SCR_FIELD(sx, sy))
6009 DrawLevelFieldCrumbledSand(xx, yy);
6010 DrawGraphic(sx, sy, flame_graphic, frame);
6015 if (Feld[xx][yy] == EL_FLAMES)
6016 Feld[xx][yy] = EL_EMPTY;
6017 DrawLevelField(xx, yy);
6022 if (MovDelay[x][y]) /* element still has to wait some time */
6024 PlayLevelSoundAction(x, y, ACTION_WAITING);
6030 /* now make next step */
6032 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
6034 if (DONT_COLLIDE_WITH(element) &&
6035 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
6036 !PLAYER_ENEMY_PROTECTED(newx, newy))
6038 TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
6043 else if (CAN_MOVE_INTO_ACID(element) &&
6044 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
6045 !IS_MV_DIAGONAL(MovDir[x][y]) &&
6046 (MovDir[x][y] == MV_DOWN ||
6047 game.engine_version >= VERSION_IDENT(3,1,0,0)))
6049 SplashAcid(newx, newy);
6050 Store[x][y] = EL_ACID;
6052 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
6054 if (Feld[newx][newy] == EL_EXIT_OPEN)
6057 DrawLevelField(x, y);
6059 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
6060 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
6061 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
6063 local_player->friends_still_needed--;
6064 if (!local_player->friends_still_needed &&
6065 !local_player->GameOver && AllPlayersGone)
6066 PlayerWins(local_player);
6070 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
6072 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
6073 DrawLevelField(newx, newy);
6075 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
6077 else if (!IS_FREE(newx, newy))
6079 GfxAction[x][y] = ACTION_WAITING;
6081 if (IS_PLAYER(x, y))
6082 DrawPlayerField(x, y);
6084 DrawLevelField(x, y);
6089 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
6091 if (IS_FOOD_PIG(Feld[newx][newy]))
6093 if (IS_MOVING(newx, newy))
6094 RemoveMovingField(newx, newy);
6097 Feld[newx][newy] = EL_EMPTY;
6098 DrawLevelField(newx, newy);
6101 PlayLevelSound(x, y, SND_PIG_DIGGING);
6103 else if (!IS_FREE(newx, newy))
6105 if (IS_PLAYER(x, y))
6106 DrawPlayerField(x, y);
6108 DrawLevelField(x, y);
6113 else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
6115 if (Store[x][y] != EL_EMPTY)
6117 boolean can_clone = FALSE;
6120 /* check if element to clone is still there */
6121 for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
6123 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
6131 /* cannot clone or target field not free anymore -- do not clone */
6132 if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6133 Store[x][y] = EL_EMPTY;
6136 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6138 if (IS_MV_DIAGONAL(MovDir[x][y]))
6140 int diagonal_move_dir = MovDir[x][y];
6141 int stored = Store[x][y];
6142 int change_delay = 8;
6145 /* android is moving diagonally */
6147 CreateField(x, y, EL_DIAGONAL_SHRINKING);
6149 Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
6150 GfxElement[x][y] = EL_EMC_ANDROID;
6151 GfxAction[x][y] = ACTION_SHRINKING;
6152 GfxDir[x][y] = diagonal_move_dir;
6153 ChangeDelay[x][y] = change_delay;
6155 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
6158 DrawLevelGraphicAnimation(x, y, graphic);
6159 PlayLevelSoundAction(x, y, ACTION_SHRINKING);
6161 if (Feld[newx][newy] == EL_ACID)
6163 SplashAcid(newx, newy);
6168 CreateField(newx, newy, EL_DIAGONAL_GROWING);
6170 Store[newx][newy] = EL_EMC_ANDROID;
6171 GfxElement[newx][newy] = EL_EMC_ANDROID;
6172 GfxAction[newx][newy] = ACTION_GROWING;
6173 GfxDir[newx][newy] = diagonal_move_dir;
6174 ChangeDelay[newx][newy] = change_delay;
6176 graphic = el_act_dir2img(GfxElement[newx][newy],
6177 GfxAction[newx][newy], GfxDir[newx][newy]);
6179 DrawLevelGraphicAnimation(newx, newy, graphic);
6180 PlayLevelSoundAction(newx, newy, ACTION_GROWING);
6186 Feld[newx][newy] = EL_EMPTY;
6187 DrawLevelField(newx, newy);
6189 PlayLevelSoundAction(x, y, ACTION_DIGGING);
6192 else if (!IS_FREE(newx, newy))
6195 if (IS_PLAYER(x, y))
6196 DrawPlayerField(x, y);
6198 DrawLevelField(x, y);
6204 else if (IS_CUSTOM_ELEMENT(element) &&
6205 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6207 int new_element = Feld[newx][newy];
6209 if (!IS_FREE(newx, newy))
6211 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
6212 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
6215 /* no element can dig solid indestructible elements */
6216 if (IS_INDESTRUCTIBLE(new_element) &&
6217 !IS_DIGGABLE(new_element) &&
6218 !IS_COLLECTIBLE(new_element))
6221 if (AmoebaNr[newx][newy] &&
6222 (new_element == EL_AMOEBA_FULL ||
6223 new_element == EL_BD_AMOEBA ||
6224 new_element == EL_AMOEBA_GROWING))
6226 AmoebaCnt[AmoebaNr[newx][newy]]--;
6227 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6230 if (IS_MOVING(newx, newy))
6231 RemoveMovingField(newx, newy);
6234 RemoveField(newx, newy);
6235 DrawLevelField(newx, newy);
6238 /* if digged element was about to explode, prevent the explosion */
6239 ExplodeField[newx][newy] = EX_TYPE_NONE;
6241 PlayLevelSoundAction(x, y, action);
6244 Store[newx][newy] = EL_EMPTY;
6246 /* this makes it possible to leave the removed element again */
6247 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
6248 Store[newx][newy] = new_element;
6250 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
6252 int move_leave_element = element_info[element].move_leave_element;
6254 /* this makes it possible to leave the removed element again */
6255 Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
6256 new_element : move_leave_element);
6260 if (move_pattern & MV_MAZE_RUNNER_STYLE)
6262 RunnerVisit[x][y] = FrameCounter;
6263 PlayerVisit[x][y] /= 8; /* expire player visit path */
6266 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
6268 if (!IS_FREE(newx, newy))
6270 if (IS_PLAYER(x, y))
6271 DrawPlayerField(x, y);
6273 DrawLevelField(x, y);
6279 boolean wanna_flame = !RND(10);
6280 int dx = newx - x, dy = newy - y;
6281 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
6282 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
6283 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
6284 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
6285 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
6286 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
6289 IS_CLASSIC_ENEMY(element1) ||
6290 IS_CLASSIC_ENEMY(element2)) &&
6291 element1 != EL_DRAGON && element2 != EL_DRAGON &&
6292 element1 != EL_FLAMES && element2 != EL_FLAMES)
6294 ResetGfxAnimation(x, y);
6295 GfxAction[x][y] = ACTION_ATTACKING;
6297 if (IS_PLAYER(x, y))
6298 DrawPlayerField(x, y);
6300 DrawLevelField(x, y);
6302 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
6304 MovDelay[x][y] = 50;
6308 RemoveField(newx, newy);
6310 Feld[newx][newy] = EL_FLAMES;
6311 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
6314 RemoveField(newx1, newy1);
6316 Feld[newx1][newy1] = EL_FLAMES;
6318 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
6321 RemoveField(newx2, newy2);
6323 Feld[newx2][newy2] = EL_FLAMES;
6330 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6331 Feld[newx][newy] == EL_DIAMOND)
6333 if (IS_MOVING(newx, newy))
6334 RemoveMovingField(newx, newy);
6337 Feld[newx][newy] = EL_EMPTY;
6338 DrawLevelField(newx, newy);
6341 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
6343 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6344 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
6346 if (AmoebaNr[newx][newy])
6348 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6349 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
6350 Feld[newx][newy] == EL_BD_AMOEBA)
6351 AmoebaCnt[AmoebaNr[newx][newy]]--;
6356 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
6358 RemoveMovingField(newx, newy);
6361 if (IS_MOVING(newx, newy))
6363 RemoveMovingField(newx, newy);
6368 Feld[newx][newy] = EL_EMPTY;
6369 DrawLevelField(newx, newy);
6372 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
6374 else if ((element == EL_PACMAN || element == EL_MOLE)
6375 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
6377 if (AmoebaNr[newx][newy])
6379 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6380 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
6381 Feld[newx][newy] == EL_BD_AMOEBA)
6382 AmoebaCnt[AmoebaNr[newx][newy]]--;
6385 if (element == EL_MOLE)
6387 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
6388 PlayLevelSound(x, y, SND_MOLE_DIGGING);
6390 ResetGfxAnimation(x, y);
6391 GfxAction[x][y] = ACTION_DIGGING;
6392 DrawLevelField(x, y);
6394 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
6396 return; /* wait for shrinking amoeba */
6398 else /* element == EL_PACMAN */
6400 Feld[newx][newy] = EL_EMPTY;
6401 DrawLevelField(newx, newy);
6402 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
6405 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
6406 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
6407 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
6409 /* wait for shrinking amoeba to completely disappear */
6412 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
6414 /* object was running against a wall */
6419 /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
6420 if (move_pattern & MV_ANY_DIRECTION &&
6421 move_pattern == MovDir[x][y])
6423 int blocking_element =
6424 (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
6426 CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
6429 element = Feld[x][y]; /* element might have changed */
6433 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
6434 DrawLevelElementAnimation(x, y, element);
6436 if (DONT_TOUCH(element))
6437 TestIfBadThingTouchesPlayer(x, y);
6442 InitMovingField(x, y, MovDir[x][y]);
6444 PlayLevelSoundAction(x, y, ACTION_MOVING);
6448 ContinueMoving(x, y);
6451 void ContinueMoving(int x, int y)
6453 int element = Feld[x][y];
6454 struct ElementInfo *ei = &element_info[element];
6455 int direction = MovDir[x][y];
6456 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
6457 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
6458 int newx = x + dx, newy = y + dy;
6459 int stored = Store[x][y];
6460 int stored_new = Store[newx][newy];
6461 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
6462 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
6463 boolean last_line = (newy == lev_fieldy - 1);
6465 MovPos[x][y] += getElementMoveStepsize(x, y);
6467 if (pushed_by_player) /* special case: moving object pushed by player */
6468 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
6470 if (ABS(MovPos[x][y]) < TILEX)
6472 DrawLevelField(x, y);
6474 return; /* element is still moving */
6477 /* element reached destination field */
6479 Feld[x][y] = EL_EMPTY;
6480 Feld[newx][newy] = element;
6481 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
6483 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
6485 element = Feld[newx][newy] = EL_ACID;
6487 else if (element == EL_MOLE)
6489 Feld[x][y] = EL_SAND;
6491 DrawLevelFieldCrumbledSandNeighbours(x, y);
6493 else if (element == EL_QUICKSAND_FILLING)
6495 element = Feld[newx][newy] = get_next_element(element);
6496 Store[newx][newy] = Store[x][y];
6498 else if (element == EL_QUICKSAND_EMPTYING)
6500 Feld[x][y] = get_next_element(element);
6501 element = Feld[newx][newy] = Store[x][y];
6503 else if (element == EL_MAGIC_WALL_FILLING)
6505 element = Feld[newx][newy] = get_next_element(element);
6506 if (!game.magic_wall_active)
6507 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
6508 Store[newx][newy] = Store[x][y];
6510 else if (element == EL_MAGIC_WALL_EMPTYING)
6512 Feld[x][y] = get_next_element(element);
6513 if (!game.magic_wall_active)
6514 Feld[x][y] = EL_MAGIC_WALL_DEAD;
6515 element = Feld[newx][newy] = Store[x][y];
6517 #if USE_NEW_CUSTOM_VALUE
6518 InitField(newx, newy, FALSE);
6521 else if (element == EL_BD_MAGIC_WALL_FILLING)
6523 element = Feld[newx][newy] = get_next_element(element);
6524 if (!game.magic_wall_active)
6525 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
6526 Store[newx][newy] = Store[x][y];
6528 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
6530 Feld[x][y] = get_next_element(element);
6531 if (!game.magic_wall_active)
6532 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
6533 element = Feld[newx][newy] = Store[x][y];
6535 #if USE_NEW_CUSTOM_VALUE
6536 InitField(newx, newy, FALSE);
6539 else if (element == EL_AMOEBA_DROPPING)
6541 Feld[x][y] = get_next_element(element);
6542 element = Feld[newx][newy] = Store[x][y];
6544 else if (element == EL_SOKOBAN_OBJECT)
6547 Feld[x][y] = Back[x][y];
6549 if (Back[newx][newy])
6550 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
6552 Back[x][y] = Back[newx][newy] = 0;
6555 Store[x][y] = EL_EMPTY;
6560 MovDelay[newx][newy] = 0;
6562 if (CAN_CHANGE_OR_HAS_ACTION(element))
6564 /* copy element change control values to new field */
6565 ChangeDelay[newx][newy] = ChangeDelay[x][y];
6566 ChangePage[newx][newy] = ChangePage[x][y];
6567 ChangeCount[newx][newy] = ChangeCount[x][y];
6568 ChangeEvent[newx][newy] = ChangeEvent[x][y];
6571 #if USE_NEW_CUSTOM_VALUE
6572 CustomValue[newx][newy] = CustomValue[x][y];
6575 ChangeDelay[x][y] = 0;
6576 ChangePage[x][y] = -1;
6577 ChangeCount[x][y] = 0;
6578 ChangeEvent[x][y] = -1;
6580 #if USE_NEW_CUSTOM_VALUE
6581 CustomValue[x][y] = 0;
6584 /* copy animation control values to new field */
6585 GfxFrame[newx][newy] = GfxFrame[x][y];
6586 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
6587 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
6588 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
6590 Pushed[x][y] = Pushed[newx][newy] = FALSE;
6592 /* some elements can leave other elements behind after moving */
6594 if (ei->move_leave_element != EL_EMPTY &&
6595 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
6596 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
6598 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
6599 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
6600 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
6603 int move_leave_element = ei->move_leave_element;
6607 /* this makes it possible to leave the removed element again */
6608 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
6609 move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
6611 /* this makes it possible to leave the removed element again */
6612 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
6613 move_leave_element = stored;
6616 /* this makes it possible to leave the removed element again */
6617 if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
6618 ei->move_leave_element == EL_TRIGGER_ELEMENT)
6619 move_leave_element = stored;
6622 Feld[x][y] = move_leave_element;
6624 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
6625 MovDir[x][y] = direction;
6627 InitField(x, y, FALSE);
6629 if (GFX_CRUMBLED(Feld[x][y]))
6630 DrawLevelFieldCrumbledSandNeighbours(x, y);
6632 if (ELEM_IS_PLAYER(move_leave_element))
6633 RelocatePlayer(x, y, move_leave_element);
6636 /* do this after checking for left-behind element */
6637 ResetGfxAnimation(x, y); /* reset animation values for old field */
6639 if (!CAN_MOVE(element) ||
6640 (CAN_FALL(element) && direction == MV_DOWN &&
6641 (element == EL_SPRING ||
6642 element_info[element].move_pattern == MV_WHEN_PUSHED ||
6643 element_info[element].move_pattern == MV_WHEN_DROPPED)))
6644 GfxDir[x][y] = MovDir[newx][newy] = 0;
6646 DrawLevelField(x, y);
6647 DrawLevelField(newx, newy);
6649 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
6651 /* prevent pushed element from moving on in pushed direction */
6652 if (pushed_by_player && CAN_MOVE(element) &&
6653 element_info[element].move_pattern & MV_ANY_DIRECTION &&
6654 !(element_info[element].move_pattern & direction))
6655 TurnRound(newx, newy);
6657 /* prevent elements on conveyor belt from moving on in last direction */
6658 if (pushed_by_conveyor && CAN_FALL(element) &&
6659 direction & MV_HORIZONTAL)
6660 MovDir[newx][newy] = 0;
6662 if (!pushed_by_player)
6664 int nextx = newx + dx, nexty = newy + dy;
6665 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
6667 WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
6669 if (CAN_FALL(element) && direction == MV_DOWN)
6670 WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
6672 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
6673 CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
6675 #if USE_FIX_IMPACT_COLLISION
6676 if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
6677 CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
6681 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
6683 TestIfBadThingTouchesPlayer(newx, newy);
6684 TestIfBadThingTouchesFriend(newx, newy);
6686 if (!IS_CUSTOM_ELEMENT(element))
6687 TestIfBadThingTouchesOtherBadThing(newx, newy);
6689 else if (element == EL_PENGUIN)
6690 TestIfFriendTouchesBadThing(newx, newy);
6692 /* give the player one last chance (one more frame) to move away */
6693 if (CAN_FALL(element) && direction == MV_DOWN &&
6694 (last_line || (!IS_FREE(x, newy + 1) &&
6695 (!IS_PLAYER(x, newy + 1) ||
6696 game.engine_version < VERSION_IDENT(3,1,1,0)))))
6699 if (pushed_by_player && !game.use_change_when_pushing_bug)
6701 int push_side = MV_DIR_OPPOSITE(direction);
6702 struct PlayerInfo *player = PLAYERINFO(x, y);
6704 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
6705 player->index_bit, push_side);
6706 CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
6707 player->index_bit, push_side);
6710 if (element == EL_EMC_ANDROID && pushed_by_player) /* make another move */
6711 MovDelay[newx][newy] = 1;
6713 CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
6715 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
6718 if (ChangePage[newx][newy] != -1) /* delayed change */
6720 int page = ChangePage[newx][newy];
6721 struct ElementChangeInfo *change = &ei->change_page[page];
6723 ChangePage[newx][newy] = -1;
6725 if (change->can_change)
6727 if (ChangeElement(newx, newy, element, page))
6729 if (change->post_change_function)
6730 change->post_change_function(newx, newy);
6734 if (change->has_action)
6735 ExecuteCustomElementAction(newx, newy, element, page);
6739 TestIfElementHitsCustomElement(newx, newy, direction);
6740 TestIfPlayerTouchesCustomElement(newx, newy);
6741 TestIfElementTouchesCustomElement(newx, newy);
6743 if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
6744 IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
6745 CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
6746 MV_DIR_OPPOSITE(direction));
6749 int AmoebeNachbarNr(int ax, int ay)
6752 int element = Feld[ax][ay];
6754 static int xy[4][2] =
6762 for (i = 0; i < NUM_DIRECTIONS; i++)
6764 int x = ax + xy[i][0];
6765 int y = ay + xy[i][1];
6767 if (!IN_LEV_FIELD(x, y))
6770 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
6771 group_nr = AmoebaNr[x][y];
6777 void AmoebenVereinigen(int ax, int ay)
6779 int i, x, y, xx, yy;
6780 int new_group_nr = AmoebaNr[ax][ay];
6781 static int xy[4][2] =
6789 if (new_group_nr == 0)
6792 for (i = 0; i < NUM_DIRECTIONS; i++)
6797 if (!IN_LEV_FIELD(x, y))
6800 if ((Feld[x][y] == EL_AMOEBA_FULL ||
6801 Feld[x][y] == EL_BD_AMOEBA ||
6802 Feld[x][y] == EL_AMOEBA_DEAD) &&
6803 AmoebaNr[x][y] != new_group_nr)
6805 int old_group_nr = AmoebaNr[x][y];
6807 if (old_group_nr == 0)
6810 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
6811 AmoebaCnt[old_group_nr] = 0;
6812 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
6813 AmoebaCnt2[old_group_nr] = 0;
6815 SCAN_PLAYFIELD(xx, yy)
6817 if (AmoebaNr[xx][yy] == old_group_nr)
6818 AmoebaNr[xx][yy] = new_group_nr;
6824 void AmoebeUmwandeln(int ax, int ay)
6828 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
6830 int group_nr = AmoebaNr[ax][ay];
6835 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
6836 printf("AmoebeUmwandeln(): This should never happen!\n");
6841 SCAN_PLAYFIELD(x, y)
6843 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
6846 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
6850 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
6851 SND_AMOEBA_TURNING_TO_GEM :
6852 SND_AMOEBA_TURNING_TO_ROCK));
6857 static int xy[4][2] =
6865 for (i = 0; i < NUM_DIRECTIONS; i++)
6870 if (!IN_LEV_FIELD(x, y))
6873 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
6875 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
6876 SND_AMOEBA_TURNING_TO_GEM :
6877 SND_AMOEBA_TURNING_TO_ROCK));
6884 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
6887 int group_nr = AmoebaNr[ax][ay];
6888 boolean done = FALSE;
6893 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
6894 printf("AmoebeUmwandelnBD(): This should never happen!\n");
6899 SCAN_PLAYFIELD(x, y)
6901 if (AmoebaNr[x][y] == group_nr &&
6902 (Feld[x][y] == EL_AMOEBA_DEAD ||
6903 Feld[x][y] == EL_BD_AMOEBA ||
6904 Feld[x][y] == EL_AMOEBA_GROWING))
6907 Feld[x][y] = new_element;
6908 InitField(x, y, FALSE);
6909 DrawLevelField(x, y);
6915 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
6916 SND_BD_AMOEBA_TURNING_TO_ROCK :
6917 SND_BD_AMOEBA_TURNING_TO_GEM));
6920 void AmoebeWaechst(int x, int y)
6922 static unsigned long sound_delay = 0;
6923 static unsigned long sound_delay_value = 0;
6925 if (!MovDelay[x][y]) /* start new growing cycle */
6929 if (DelayReached(&sound_delay, sound_delay_value))
6931 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
6932 sound_delay_value = 30;
6936 if (MovDelay[x][y]) /* wait some time before growing bigger */
6939 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6941 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
6942 6 - MovDelay[x][y]);
6944 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
6947 if (!MovDelay[x][y])
6949 Feld[x][y] = Store[x][y];
6951 DrawLevelField(x, y);
6956 void AmoebaDisappearing(int x, int y)
6958 static unsigned long sound_delay = 0;
6959 static unsigned long sound_delay_value = 0;
6961 if (!MovDelay[x][y]) /* start new shrinking cycle */
6965 if (DelayReached(&sound_delay, sound_delay_value))
6966 sound_delay_value = 30;
6969 if (MovDelay[x][y]) /* wait some time before shrinking */
6972 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6974 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
6975 6 - MovDelay[x][y]);
6977 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
6980 if (!MovDelay[x][y])
6982 Feld[x][y] = EL_EMPTY;
6983 DrawLevelField(x, y);
6985 /* don't let mole enter this field in this cycle;
6986 (give priority to objects falling to this field from above) */
6992 void AmoebeAbleger(int ax, int ay)
6995 int element = Feld[ax][ay];
6996 int graphic = el2img(element);
6997 int newax = ax, neway = ay;
6998 boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
6999 static int xy[4][2] =
7007 if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
7009 Feld[ax][ay] = EL_AMOEBA_DEAD;
7010 DrawLevelField(ax, ay);
7014 if (IS_ANIMATED(graphic))
7015 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7017 if (!MovDelay[ax][ay]) /* start making new amoeba field */
7018 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
7020 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
7023 if (MovDelay[ax][ay])
7027 if (can_drop) /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
7030 int x = ax + xy[start][0];
7031 int y = ay + xy[start][1];
7033 if (!IN_LEV_FIELD(x, y))
7036 if (IS_FREE(x, y) ||
7037 CAN_GROW_INTO(Feld[x][y]) ||
7038 Feld[x][y] == EL_QUICKSAND_EMPTY)
7044 if (newax == ax && neway == ay)
7047 else /* normal or "filled" (BD style) amoeba */
7050 boolean waiting_for_player = FALSE;
7052 for (i = 0; i < NUM_DIRECTIONS; i++)
7054 int j = (start + i) % 4;
7055 int x = ax + xy[j][0];
7056 int y = ay + xy[j][1];
7058 if (!IN_LEV_FIELD(x, y))
7061 if (IS_FREE(x, y) ||
7062 CAN_GROW_INTO(Feld[x][y]) ||
7063 Feld[x][y] == EL_QUICKSAND_EMPTY)
7069 else if (IS_PLAYER(x, y))
7070 waiting_for_player = TRUE;
7073 if (newax == ax && neway == ay) /* amoeba cannot grow */
7075 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
7077 Feld[ax][ay] = EL_AMOEBA_DEAD;
7078 DrawLevelField(ax, ay);
7079 AmoebaCnt[AmoebaNr[ax][ay]]--;
7081 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
7083 if (element == EL_AMOEBA_FULL)
7084 AmoebeUmwandeln(ax, ay);
7085 else if (element == EL_BD_AMOEBA)
7086 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
7091 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
7093 /* amoeba gets larger by growing in some direction */
7095 int new_group_nr = AmoebaNr[ax][ay];
7098 if (new_group_nr == 0)
7100 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
7101 printf("AmoebeAbleger(): This should never happen!\n");
7106 AmoebaNr[newax][neway] = new_group_nr;
7107 AmoebaCnt[new_group_nr]++;
7108 AmoebaCnt2[new_group_nr]++;
7110 /* if amoeba touches other amoeba(s) after growing, unify them */
7111 AmoebenVereinigen(newax, neway);
7113 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
7115 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
7121 if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
7122 (neway == lev_fieldy - 1 && newax != ax))
7124 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
7125 Store[newax][neway] = element;
7127 else if (neway == ay || element == EL_EMC_DRIPPER)
7129 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
7131 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
7135 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
7136 Feld[ax][ay] = EL_AMOEBA_DROPPING;
7137 Store[ax][ay] = EL_AMOEBA_DROP;
7138 ContinueMoving(ax, ay);
7142 DrawLevelField(newax, neway);
7145 void Life(int ax, int ay)
7149 int element = Feld[ax][ay];
7150 int graphic = el2img(element);
7151 int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
7153 boolean changed = FALSE;
7155 if (IS_ANIMATED(graphic))
7156 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7161 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
7162 MovDelay[ax][ay] = life_time;
7164 if (MovDelay[ax][ay]) /* wait some time before next cycle */
7167 if (MovDelay[ax][ay])
7171 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
7173 int xx = ax+x1, yy = ay+y1;
7176 if (!IN_LEV_FIELD(xx, yy))
7179 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
7181 int x = xx+x2, y = yy+y2;
7183 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
7186 if (((Feld[x][y] == element ||
7187 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
7189 (IS_FREE(x, y) && Stop[x][y]))
7193 if (xx == ax && yy == ay) /* field in the middle */
7195 if (nachbarn < life_parameter[0] ||
7196 nachbarn > life_parameter[1])
7198 Feld[xx][yy] = EL_EMPTY;
7200 DrawLevelField(xx, yy);
7201 Stop[xx][yy] = TRUE;
7205 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
7206 { /* free border field */
7207 if (nachbarn >= life_parameter[2] &&
7208 nachbarn <= life_parameter[3])
7210 Feld[xx][yy] = element;
7211 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
7213 DrawLevelField(xx, yy);
7214 Stop[xx][yy] = TRUE;
7221 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
7222 SND_GAME_OF_LIFE_GROWING);
7225 static void InitRobotWheel(int x, int y)
7227 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
7230 static void RunRobotWheel(int x, int y)
7232 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
7235 static void StopRobotWheel(int x, int y)
7237 if (ZX == x && ZY == y)
7241 static void InitTimegateWheel(int x, int y)
7243 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
7246 static void RunTimegateWheel(int x, int y)
7248 PlayLevelSound(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
7251 static void InitMagicBallDelay(int x, int y)
7254 ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
7256 ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
7260 static void ActivateMagicBall(int bx, int by)
7264 if (level.ball_random)
7266 int pos_border = RND(8); /* select one of the eight border elements */
7267 int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
7268 int xx = pos_content % 3;
7269 int yy = pos_content / 3;
7274 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
7275 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
7279 for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
7281 int xx = x - bx + 1;
7282 int yy = y - by + 1;
7284 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
7285 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
7289 game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
7292 void CheckExit(int x, int y)
7294 if (local_player->gems_still_needed > 0 ||
7295 local_player->sokobanfields_still_needed > 0 ||
7296 local_player->lights_still_needed > 0)
7298 int element = Feld[x][y];
7299 int graphic = el2img(element);
7301 if (IS_ANIMATED(graphic))
7302 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7307 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7310 Feld[x][y] = EL_EXIT_OPENING;
7312 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
7315 void CheckExitSP(int x, int y)
7317 if (local_player->gems_still_needed > 0)
7319 int element = Feld[x][y];
7320 int graphic = el2img(element);
7322 if (IS_ANIMATED(graphic))
7323 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7328 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7331 Feld[x][y] = EL_SP_EXIT_OPENING;
7333 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
7336 static void CloseAllOpenTimegates()
7340 SCAN_PLAYFIELD(x, y)
7342 int element = Feld[x][y];
7344 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
7346 Feld[x][y] = EL_TIMEGATE_CLOSING;
7348 PlayLevelSoundAction(x, y, ACTION_CLOSING);
7353 void DrawTwinkleOnField(int x, int y)
7355 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
7358 if (Feld[x][y] == EL_BD_DIAMOND)
7361 if (MovDelay[x][y] == 0) /* next animation frame */
7362 MovDelay[x][y] = 11 * !GetSimpleRandom(500);
7364 if (MovDelay[x][y] != 0) /* wait some time before next frame */
7368 if (setup.direct_draw && MovDelay[x][y])
7369 SetDrawtoField(DRAW_BUFFERED);
7371 DrawLevelElementAnimation(x, y, Feld[x][y]);
7373 if (MovDelay[x][y] != 0)
7375 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
7376 10 - MovDelay[x][y]);
7378 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
7380 if (setup.direct_draw)
7384 dest_x = FX + SCREENX(x) * TILEX;
7385 dest_y = FY + SCREENY(y) * TILEY;
7387 BlitBitmap(drawto_field, window,
7388 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
7389 SetDrawtoField(DRAW_DIRECT);
7395 void MauerWaechst(int x, int y)
7399 if (!MovDelay[x][y]) /* next animation frame */
7400 MovDelay[x][y] = 3 * delay;
7402 if (MovDelay[x][y]) /* wait some time before next frame */
7406 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7408 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
7409 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
7411 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
7414 if (!MovDelay[x][y])
7416 if (MovDir[x][y] == MV_LEFT)
7418 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
7419 DrawLevelField(x - 1, y);
7421 else if (MovDir[x][y] == MV_RIGHT)
7423 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
7424 DrawLevelField(x + 1, y);
7426 else if (MovDir[x][y] == MV_UP)
7428 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
7429 DrawLevelField(x, y - 1);
7433 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
7434 DrawLevelField(x, y + 1);
7437 Feld[x][y] = Store[x][y];
7439 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7440 DrawLevelField(x, y);
7445 void MauerAbleger(int ax, int ay)
7447 int element = Feld[ax][ay];
7448 int graphic = el2img(element);
7449 boolean oben_frei = FALSE, unten_frei = FALSE;
7450 boolean links_frei = FALSE, rechts_frei = FALSE;
7451 boolean oben_massiv = FALSE, unten_massiv = FALSE;
7452 boolean links_massiv = FALSE, rechts_massiv = FALSE;
7453 boolean new_wall = FALSE;
7455 if (IS_ANIMATED(graphic))
7456 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7458 if (!MovDelay[ax][ay]) /* start building new wall */
7459 MovDelay[ax][ay] = 6;
7461 if (MovDelay[ax][ay]) /* wait some time before building new wall */
7464 if (MovDelay[ax][ay])
7468 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
7470 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
7472 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
7474 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
7477 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
7478 element == EL_EXPANDABLE_WALL_ANY)
7482 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
7483 Store[ax][ay-1] = element;
7484 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
7485 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
7486 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
7487 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
7492 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
7493 Store[ax][ay+1] = element;
7494 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
7495 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
7496 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
7497 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
7502 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7503 element == EL_EXPANDABLE_WALL_ANY ||
7504 element == EL_EXPANDABLE_WALL ||
7505 element == EL_BD_EXPANDABLE_WALL)
7509 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
7510 Store[ax-1][ay] = element;
7511 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
7512 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
7513 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
7514 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
7520 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
7521 Store[ax+1][ay] = element;
7522 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
7523 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
7524 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
7525 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
7530 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
7531 DrawLevelField(ax, ay);
7533 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
7535 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
7536 unten_massiv = TRUE;
7537 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
7538 links_massiv = TRUE;
7539 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
7540 rechts_massiv = TRUE;
7542 if (((oben_massiv && unten_massiv) ||
7543 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7544 element == EL_EXPANDABLE_WALL) &&
7545 ((links_massiv && rechts_massiv) ||
7546 element == EL_EXPANDABLE_WALL_VERTICAL))
7547 Feld[ax][ay] = EL_WALL;
7550 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
7553 void CheckForDragon(int x, int y)
7556 boolean dragon_found = FALSE;
7557 static int xy[4][2] =
7565 for (i = 0; i < NUM_DIRECTIONS; i++)
7567 for (j = 0; j < 4; j++)
7569 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7571 if (IN_LEV_FIELD(xx, yy) &&
7572 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
7574 if (Feld[xx][yy] == EL_DRAGON)
7575 dragon_found = TRUE;
7584 for (i = 0; i < NUM_DIRECTIONS; i++)
7586 for (j = 0; j < 3; j++)
7588 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7590 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
7592 Feld[xx][yy] = EL_EMPTY;
7593 DrawLevelField(xx, yy);
7602 static void InitBuggyBase(int x, int y)
7604 int element = Feld[x][y];
7605 int activating_delay = FRAMES_PER_SECOND / 4;
7608 (element == EL_SP_BUGGY_BASE ?
7609 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
7610 element == EL_SP_BUGGY_BASE_ACTIVATING ?
7612 element == EL_SP_BUGGY_BASE_ACTIVE ?
7613 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
7616 static void WarnBuggyBase(int x, int y)
7619 static int xy[4][2] =
7627 for (i = 0; i < NUM_DIRECTIONS; i++)
7629 int xx = x + xy[i][0];
7630 int yy = y + xy[i][1];
7632 if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
7634 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
7641 static void InitTrap(int x, int y)
7643 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
7646 static void ActivateTrap(int x, int y)
7648 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
7651 static void ChangeActiveTrap(int x, int y)
7653 int graphic = IMG_TRAP_ACTIVE;
7655 /* if new animation frame was drawn, correct crumbled sand border */
7656 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
7657 DrawLevelFieldCrumbledSand(x, y);
7660 static int getSpecialActionElement(int element, int number, int base_element)
7662 return (element != EL_EMPTY ? element :
7663 number != -1 ? base_element + number - 1 :
7667 static int getModifiedActionNumber(int value_old, int operator, int operand,
7668 int value_min, int value_max)
7670 int value_new = (operator == CA_MODE_SET ? operand :
7671 operator == CA_MODE_ADD ? value_old + operand :
7672 operator == CA_MODE_SUBTRACT ? value_old - operand :
7673 operator == CA_MODE_MULTIPLY ? value_old * operand :
7674 operator == CA_MODE_DIVIDE ? value_old / MAX(1, operand) :
7675 operator == CA_MODE_MODULO ? value_old % MAX(1, operand) :
7678 return (value_new < value_min ? value_min :
7679 value_new > value_max ? value_max :
7683 static void ExecuteCustomElementAction(int x, int y, int element, int page)
7685 struct ElementInfo *ei = &element_info[element];
7686 struct ElementChangeInfo *change = &ei->change_page[page];
7687 int target_element = change->target_element;
7688 int action_type = change->action_type;
7689 int action_mode = change->action_mode;
7690 int action_arg = change->action_arg;
7693 if (!change->has_action)
7696 /* ---------- determine action paramater values -------------------------- */
7698 int level_time_value =
7699 (level.time > 0 ? TimeLeft :
7702 int action_arg_element =
7703 (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player :
7704 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
7705 action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element :
7708 int action_arg_direction =
7709 (action_arg >= CA_ARG_DIRECTION_LEFT &&
7710 action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
7711 action_arg == CA_ARG_DIRECTION_TRIGGER ?
7712 change->actual_trigger_side :
7713 action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
7714 MV_DIR_OPPOSITE(change->actual_trigger_side) :
7717 int action_arg_number_min =
7718 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
7721 int action_arg_number_max =
7722 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
7723 action_type == CA_SET_LEVEL_GEMS ? 999 :
7724 action_type == CA_SET_LEVEL_TIME ? 9999 :
7725 action_type == CA_SET_LEVEL_SCORE ? 99999 :
7726 action_type == CA_SET_CE_VALUE ? 9999 :
7727 action_type == CA_SET_CE_SCORE ? 9999 :
7730 int action_arg_number_reset =
7731 (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
7732 action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
7733 action_type == CA_SET_LEVEL_TIME ? level.time :
7734 action_type == CA_SET_LEVEL_SCORE ? 0 :
7735 #if USE_NEW_CUSTOM_VALUE
7736 action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
7738 action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
7740 action_type == CA_SET_CE_SCORE ? 0 :
7743 int action_arg_number =
7744 (action_arg <= CA_ARG_MAX ? action_arg :
7745 action_arg >= CA_ARG_SPEED_NOT_MOVING &&
7746 action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
7747 action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
7748 action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
7749 action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
7750 action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
7751 #if USE_NEW_CUSTOM_VALUE
7752 action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
7754 action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
7756 action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
7757 action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
7758 action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
7759 action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
7760 action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
7761 action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
7762 action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
7763 action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
7764 action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
7765 action_arg == CA_ARG_ELEMENT_NR_TARGET ? change->target_element :
7766 action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
7769 int action_arg_number_old =
7770 (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
7771 action_type == CA_SET_LEVEL_TIME ? TimeLeft :
7772 action_type == CA_SET_LEVEL_SCORE ? local_player->score :
7773 action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
7774 action_type == CA_SET_CE_SCORE ? ei->collect_score :
7777 int action_arg_number_new =
7778 getModifiedActionNumber(action_arg_number_old,
7779 action_mode, action_arg_number,
7780 action_arg_number_min, action_arg_number_max);
7782 int trigger_player_bits =
7783 (change->actual_trigger_player >= EL_PLAYER_1 &&
7784 change->actual_trigger_player <= EL_PLAYER_4 ?
7785 (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
7788 int action_arg_player_bits =
7789 (action_arg >= CA_ARG_PLAYER_1 &&
7790 action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
7791 action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
7794 /* ---------- execute action -------------------------------------------- */
7796 switch (action_type)
7803 /* ---------- level actions ------------------------------------------- */
7805 case CA_RESTART_LEVEL:
7807 game.restart_level = TRUE;
7812 case CA_SHOW_ENVELOPE:
7814 int element = getSpecialActionElement(action_arg_element,
7815 action_arg_number, EL_ENVELOPE_1);
7817 if (IS_ENVELOPE(element))
7818 local_player->show_envelope = element;
7823 case CA_SET_LEVEL_TIME:
7825 if (level.time > 0) /* only modify limited time value */
7827 TimeLeft = action_arg_number_new;
7829 DrawGameValue_Time(TimeLeft);
7831 if (!TimeLeft && setup.time_limit)
7832 for (i = 0; i < MAX_PLAYERS; i++)
7833 KillPlayer(&stored_player[i]);
7839 case CA_SET_LEVEL_SCORE:
7841 local_player->score = action_arg_number_new;
7843 DrawGameValue_Score(local_player->score);
7848 case CA_SET_LEVEL_GEMS:
7850 local_player->gems_still_needed = action_arg_number_new;
7852 DrawGameValue_Emeralds(local_player->gems_still_needed);
7857 #if !USE_PLAYER_GRAVITY
7858 case CA_SET_LEVEL_GRAVITY:
7860 game.gravity = (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
7861 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
7862 action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
7868 case CA_SET_LEVEL_WIND:
7870 game.wind_direction = action_arg_direction;
7875 /* ---------- player actions ------------------------------------------ */
7877 case CA_MOVE_PLAYER:
7879 /* automatically move to the next field in specified direction */
7880 for (i = 0; i < MAX_PLAYERS; i++)
7881 if (trigger_player_bits & (1 << i))
7882 stored_player[i].programmed_action = action_arg_direction;
7887 case CA_EXIT_PLAYER:
7889 for (i = 0; i < MAX_PLAYERS; i++)
7890 if (action_arg_player_bits & (1 << i))
7891 PlayerWins(&stored_player[i]);
7896 case CA_KILL_PLAYER:
7898 for (i = 0; i < MAX_PLAYERS; i++)
7899 if (action_arg_player_bits & (1 << i))
7900 KillPlayer(&stored_player[i]);
7905 case CA_SET_PLAYER_KEYS:
7907 int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
7908 int element = getSpecialActionElement(action_arg_element,
7909 action_arg_number, EL_KEY_1);
7911 if (IS_KEY(element))
7913 for (i = 0; i < MAX_PLAYERS; i++)
7915 if (trigger_player_bits & (1 << i))
7917 stored_player[i].key[KEY_NR(element)] = key_state;
7919 DrawGameDoorValues();
7927 case CA_SET_PLAYER_SPEED:
7929 for (i = 0; i < MAX_PLAYERS; i++)
7931 if (trigger_player_bits & (1 << i))
7933 int move_stepsize = TILEX / stored_player[i].move_delay_value;
7935 if (action_arg == CA_ARG_SPEED_FASTER &&
7936 stored_player[i].cannot_move)
7938 action_arg_number = STEPSIZE_VERY_SLOW;
7940 else if (action_arg == CA_ARG_SPEED_SLOWER ||
7941 action_arg == CA_ARG_SPEED_FASTER)
7943 action_arg_number = 2;
7944 action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
7947 else if (action_arg == CA_ARG_NUMBER_RESET)
7949 action_arg_number = level.initial_player_stepsize[i];
7953 getModifiedActionNumber(move_stepsize,
7956 action_arg_number_min,
7957 action_arg_number_max);
7959 SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
7966 case CA_SET_PLAYER_SHIELD:
7968 for (i = 0; i < MAX_PLAYERS; i++)
7970 if (trigger_player_bits & (1 << i))
7972 if (action_arg == CA_ARG_SHIELD_OFF)
7974 stored_player[i].shield_normal_time_left = 0;
7975 stored_player[i].shield_deadly_time_left = 0;
7977 else if (action_arg == CA_ARG_SHIELD_NORMAL)
7979 stored_player[i].shield_normal_time_left = 999999;
7981 else if (action_arg == CA_ARG_SHIELD_DEADLY)
7983 stored_player[i].shield_normal_time_left = 999999;
7984 stored_player[i].shield_deadly_time_left = 999999;
7992 #if USE_PLAYER_GRAVITY
7993 case CA_SET_PLAYER_GRAVITY:
7995 for (i = 0; i < MAX_PLAYERS; i++)
7997 if (trigger_player_bits & (1 << i))
7999 stored_player[i].gravity =
8000 (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
8001 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
8002 action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
8003 stored_player[i].gravity);
8011 case CA_SET_PLAYER_ARTWORK:
8013 for (i = 0; i < MAX_PLAYERS; i++)
8015 if (trigger_player_bits & (1 << i))
8017 int artwork_element = action_arg_element;
8019 if (action_arg == CA_ARG_ELEMENT_RESET)
8021 (level.use_artwork_element[i] ? level.artwork_element[i] :
8022 stored_player[i].element_nr);
8024 #if USE_GFX_RESET_PLAYER_ARTWORK
8025 if (stored_player[i].artwork_element != artwork_element)
8026 stored_player[i].Frame = 0;
8029 stored_player[i].artwork_element = artwork_element;
8031 SetPlayerWaiting(&stored_player[i], FALSE);
8033 /* set number of special actions for bored and sleeping animation */
8034 stored_player[i].num_special_action_bored =
8035 get_num_special_action(artwork_element,
8036 ACTION_BORING_1, ACTION_BORING_LAST);
8037 stored_player[i].num_special_action_sleeping =
8038 get_num_special_action(artwork_element,
8039 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
8046 /* ---------- CE actions ---------------------------------------------- */
8048 case CA_SET_CE_VALUE:
8050 #if USE_NEW_CUSTOM_VALUE
8051 int last_ce_value = CustomValue[x][y];
8053 CustomValue[x][y] = action_arg_number_new;
8055 if (CustomValue[x][y] != last_ce_value)
8057 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
8058 CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
8060 if (CustomValue[x][y] == 0)
8062 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
8063 CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
8071 case CA_SET_CE_SCORE:
8073 #if USE_NEW_CUSTOM_VALUE
8074 int last_ce_score = ei->collect_score;
8076 ei->collect_score = action_arg_number_new;
8078 if (ei->collect_score != last_ce_score)
8080 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
8081 CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
8083 if (ei->collect_score == 0)
8087 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
8088 CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
8091 This is a very special case that seems to be a mixture between
8092 CheckElementChange() and CheckTriggeredElementChange(): while
8093 the first one only affects single elements that are triggered
8094 directly, the second one affects multiple elements in the playfield
8095 that are triggered indirectly by another element. This is a third
8096 case: Changing the CE score always affects multiple identical CEs,
8097 so every affected CE must be checked, not only the single CE for
8098 which the CE score was changed in the first place (as every instance
8099 of that CE shares the same CE score, and therefore also can change)!
8101 SCAN_PLAYFIELD(xx, yy)
8103 if (Feld[xx][yy] == element)
8104 CheckElementChange(xx, yy, element, EL_UNDEFINED,
8105 CE_SCORE_GETS_ZERO);
8114 /* ---------- engine actions ------------------------------------------ */
8116 case CA_SET_ENGINE_SCAN_MODE:
8118 InitPlayfieldScanMode(action_arg);
8128 static void CreateFieldExt(int x, int y, int element, boolean is_change)
8130 int old_element = Feld[x][y];
8131 int new_element = get_element_from_group_element(element);
8132 int previous_move_direction = MovDir[x][y];
8133 #if USE_NEW_CUSTOM_VALUE
8134 int last_ce_value = CustomValue[x][y];
8136 boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
8137 boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
8138 boolean add_player_onto_element = (new_element_is_player &&
8139 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
8140 /* this breaks SnakeBite when a snake is
8141 halfway through a door that closes */
8142 /* NOW FIXED AT LEVEL INIT IN files.c */
8143 new_element != EL_SOKOBAN_FIELD_PLAYER &&
8145 IS_WALKABLE(old_element));
8148 /* check if element under the player changes from accessible to unaccessible
8149 (needed for special case of dropping element which then changes) */
8150 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
8151 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
8159 if (!add_player_onto_element)
8161 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
8162 RemoveMovingField(x, y);
8166 Feld[x][y] = new_element;
8168 #if !USE_GFX_RESET_GFX_ANIMATION
8169 ResetGfxAnimation(x, y);
8170 ResetRandomAnimationValue(x, y);
8173 if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
8174 MovDir[x][y] = previous_move_direction;
8176 #if USE_NEW_CUSTOM_VALUE
8177 if (element_info[new_element].use_last_ce_value)
8178 CustomValue[x][y] = last_ce_value;
8181 InitField_WithBug1(x, y, FALSE);
8183 new_element = Feld[x][y]; /* element may have changed */
8185 #if USE_GFX_RESET_GFX_ANIMATION
8186 ResetGfxAnimation(x, y);
8187 ResetRandomAnimationValue(x, y);
8190 DrawLevelField(x, y);
8192 if (GFX_CRUMBLED(new_element))
8193 DrawLevelFieldCrumbledSandNeighbours(x, y);
8197 /* check if element under the player changes from accessible to unaccessible
8198 (needed for special case of dropping element which then changes) */
8199 /* (must be checked after creating new element for walkable group elements) */
8200 #if USE_FIX_KILLED_BY_NON_WALKABLE
8201 if (IS_PLAYER(x, y) && !player_explosion_protected &&
8202 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
8209 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
8210 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
8219 /* "ChangeCount" not set yet to allow "entered by player" change one time */
8220 if (new_element_is_player)
8221 RelocatePlayer(x, y, new_element);
8224 ChangeCount[x][y]++; /* count number of changes in the same frame */
8226 TestIfBadThingTouchesPlayer(x, y);
8227 TestIfPlayerTouchesCustomElement(x, y);
8228 TestIfElementTouchesCustomElement(x, y);
8231 static void CreateField(int x, int y, int element)
8233 CreateFieldExt(x, y, element, FALSE);
8236 static void CreateElementFromChange(int x, int y, int element)
8238 element = GET_VALID_RUNTIME_ELEMENT(element);
8240 #if USE_STOP_CHANGED_ELEMENTS
8241 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
8243 int old_element = Feld[x][y];
8245 /* prevent changed element from moving in same engine frame
8246 unless both old and new element can either fall or move */
8247 if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
8248 (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
8253 CreateFieldExt(x, y, element, TRUE);
8256 static boolean ChangeElement(int x, int y, int element, int page)
8258 struct ElementInfo *ei = &element_info[element];
8259 struct ElementChangeInfo *change = &ei->change_page[page];
8260 int ce_value = CustomValue[x][y];
8261 int ce_score = ei->collect_score;
8263 int old_element = Feld[x][y];
8265 /* always use default change event to prevent running into a loop */
8266 if (ChangeEvent[x][y] == -1)
8267 ChangeEvent[x][y] = CE_DELAY;
8269 if (ChangeEvent[x][y] == CE_DELAY)
8271 /* reset actual trigger element, trigger player and action element */
8272 change->actual_trigger_element = EL_EMPTY;
8273 change->actual_trigger_player = EL_PLAYER_1;
8274 change->actual_trigger_side = CH_SIDE_NONE;
8275 change->actual_trigger_ce_value = 0;
8276 change->actual_trigger_ce_score = 0;
8279 /* do not change elements more than a specified maximum number of changes */
8280 if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
8283 ChangeCount[x][y]++; /* count number of changes in the same frame */
8285 if (change->explode)
8292 if (change->use_target_content)
8294 boolean complete_replace = TRUE;
8295 boolean can_replace[3][3];
8298 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
8301 boolean is_walkable;
8302 boolean is_diggable;
8303 boolean is_collectible;
8304 boolean is_removable;
8305 boolean is_destructible;
8306 int ex = x + xx - 1;
8307 int ey = y + yy - 1;
8308 int content_element = change->target_content.e[xx][yy];
8311 can_replace[xx][yy] = TRUE;
8313 if (ex == x && ey == y) /* do not check changing element itself */
8316 if (content_element == EL_EMPTY_SPACE)
8318 can_replace[xx][yy] = FALSE; /* do not replace border with space */
8323 if (!IN_LEV_FIELD(ex, ey))
8325 can_replace[xx][yy] = FALSE;
8326 complete_replace = FALSE;
8333 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
8334 e = MovingOrBlocked2Element(ex, ey);
8336 is_empty = (IS_FREE(ex, ey) ||
8337 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
8339 is_walkable = (is_empty || IS_WALKABLE(e));
8340 is_diggable = (is_empty || IS_DIGGABLE(e));
8341 is_collectible = (is_empty || IS_COLLECTIBLE(e));
8342 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
8343 is_removable = (is_diggable || is_collectible);
8345 can_replace[xx][yy] =
8346 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
8347 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
8348 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
8349 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
8350 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
8351 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
8352 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
8354 if (!can_replace[xx][yy])
8355 complete_replace = FALSE;
8358 if (!change->only_if_complete || complete_replace)
8360 boolean something_has_changed = FALSE;
8362 if (change->only_if_complete && change->use_random_replace &&
8363 RND(100) < change->random_percentage)
8366 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
8368 int ex = x + xx - 1;
8369 int ey = y + yy - 1;
8370 int content_element;
8372 if (can_replace[xx][yy] && (!change->use_random_replace ||
8373 RND(100) < change->random_percentage))
8375 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
8376 RemoveMovingField(ex, ey);
8378 ChangeEvent[ex][ey] = ChangeEvent[x][y];
8380 content_element = change->target_content.e[xx][yy];
8381 target_element = GET_TARGET_ELEMENT(element, content_element, change,
8382 ce_value, ce_score);
8384 CreateElementFromChange(ex, ey, target_element);
8386 something_has_changed = TRUE;
8388 /* for symmetry reasons, freeze newly created border elements */
8389 if (ex != x || ey != y)
8390 Stop[ex][ey] = TRUE; /* no more moving in this frame */
8394 if (something_has_changed)
8396 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
8397 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
8403 target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
8404 ce_value, ce_score);
8406 if (element == EL_DIAGONAL_GROWING ||
8407 element == EL_DIAGONAL_SHRINKING)
8409 target_element = Store[x][y];
8411 Store[x][y] = EL_EMPTY;
8414 CreateElementFromChange(x, y, target_element);
8416 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
8417 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
8420 /* this uses direct change before indirect change */
8421 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
8426 #if USE_NEW_DELAYED_ACTION
8428 static void HandleElementChange(int x, int y, int page)
8430 int element = MovingOrBlocked2Element(x, y);
8431 struct ElementInfo *ei = &element_info[element];
8432 struct ElementChangeInfo *change = &ei->change_page[page];
8435 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
8436 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
8439 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
8440 x, y, element, element_info[element].token_name);
8441 printf("HandleElementChange(): This should never happen!\n");
8446 /* this can happen with classic bombs on walkable, changing elements */
8447 if (!CAN_CHANGE_OR_HAS_ACTION(element))
8450 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
8451 ChangeDelay[x][y] = 0;
8457 if (ChangeDelay[x][y] == 0) /* initialize element change */
8459 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
8461 if (change->can_change)
8463 ResetGfxAnimation(x, y);
8464 ResetRandomAnimationValue(x, y);
8466 if (change->pre_change_function)
8467 change->pre_change_function(x, y);
8471 ChangeDelay[x][y]--;
8473 if (ChangeDelay[x][y] != 0) /* continue element change */
8475 if (change->can_change)
8477 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8479 if (IS_ANIMATED(graphic))
8480 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8482 if (change->change_function)
8483 change->change_function(x, y);
8486 else /* finish element change */
8488 if (ChangePage[x][y] != -1) /* remember page from delayed change */
8490 page = ChangePage[x][y];
8491 ChangePage[x][y] = -1;
8493 change = &ei->change_page[page];
8496 if (IS_MOVING(x, y)) /* never change a running system ;-) */
8498 ChangeDelay[x][y] = 1; /* try change after next move step */
8499 ChangePage[x][y] = page; /* remember page to use for change */
8504 if (change->can_change)
8506 if (ChangeElement(x, y, element, page))
8508 if (change->post_change_function)
8509 change->post_change_function(x, y);
8513 if (change->has_action)
8514 ExecuteCustomElementAction(x, y, element, page);
8520 static void HandleElementChange(int x, int y, int page)
8522 int element = MovingOrBlocked2Element(x, y);
8523 struct ElementInfo *ei = &element_info[element];
8524 struct ElementChangeInfo *change = &ei->change_page[page];
8527 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
8530 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
8531 x, y, element, element_info[element].token_name);
8532 printf("HandleElementChange(): This should never happen!\n");
8537 /* this can happen with classic bombs on walkable, changing elements */
8538 if (!CAN_CHANGE(element))
8541 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
8542 ChangeDelay[x][y] = 0;
8548 if (ChangeDelay[x][y] == 0) /* initialize element change */
8550 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
8552 ResetGfxAnimation(x, y);
8553 ResetRandomAnimationValue(x, y);
8555 if (change->pre_change_function)
8556 change->pre_change_function(x, y);
8559 ChangeDelay[x][y]--;
8561 if (ChangeDelay[x][y] != 0) /* continue element change */
8563 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8565 if (IS_ANIMATED(graphic))
8566 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8568 if (change->change_function)
8569 change->change_function(x, y);
8571 else /* finish element change */
8573 if (ChangePage[x][y] != -1) /* remember page from delayed change */
8575 page = ChangePage[x][y];
8576 ChangePage[x][y] = -1;
8578 change = &ei->change_page[page];
8581 if (IS_MOVING(x, y)) /* never change a running system ;-) */
8583 ChangeDelay[x][y] = 1; /* try change after next move step */
8584 ChangePage[x][y] = page; /* remember page to use for change */
8589 if (ChangeElement(x, y, element, page))
8591 if (change->post_change_function)
8592 change->post_change_function(x, y);
8599 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
8600 int trigger_element,
8606 boolean change_done_any = FALSE;
8607 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
8610 if (!(trigger_events[trigger_element][trigger_event]))
8614 printf("::: CheckTriggeredElementChangeExt %d ... [%d, %d, %d, '%s']\n",
8615 trigger_event, recursion_loop_depth, recursion_loop_detected,
8616 recursion_loop_element, EL_NAME(recursion_loop_element));
8619 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
8621 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8623 int element = EL_CUSTOM_START + i;
8624 boolean change_done = FALSE;
8627 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
8628 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
8631 for (p = 0; p < element_info[element].num_change_pages; p++)
8633 struct ElementChangeInfo *change = &element_info[element].change_page[p];
8635 if (change->can_change_or_has_action &&
8636 change->has_event[trigger_event] &&
8637 change->trigger_side & trigger_side &&
8638 change->trigger_player & trigger_player &&
8639 change->trigger_page & trigger_page_bits &&
8640 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
8642 change->actual_trigger_element = trigger_element;
8643 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
8644 change->actual_trigger_side = trigger_side;
8645 change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
8646 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
8648 if ((change->can_change && !change_done) || change->has_action)
8652 SCAN_PLAYFIELD(x, y)
8654 if (Feld[x][y] == element)
8656 if (change->can_change && !change_done)
8658 ChangeDelay[x][y] = 1;
8659 ChangeEvent[x][y] = trigger_event;
8661 HandleElementChange(x, y, p);
8663 #if USE_NEW_DELAYED_ACTION
8664 else if (change->has_action)
8666 ExecuteCustomElementAction(x, y, element, p);
8667 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8670 if (change->has_action)
8672 ExecuteCustomElementAction(x, y, element, p);
8673 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8679 if (change->can_change)
8682 change_done_any = TRUE;
8689 RECURSION_LOOP_DETECTION_END();
8691 return change_done_any;
8694 static boolean CheckElementChangeExt(int x, int y,
8696 int trigger_element,
8701 boolean change_done = FALSE;
8704 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
8705 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
8708 if (Feld[x][y] == EL_BLOCKED)
8710 Blocked2Moving(x, y, &x, &y);
8711 element = Feld[x][y];
8715 /* check if element has already changed */
8716 if (Feld[x][y] != element)
8719 /* check if element has already changed or is about to change after moving */
8720 if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
8721 Feld[x][y] != element) ||
8723 (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
8724 (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
8725 ChangePage[x][y] != -1)))
8730 printf("::: CheckElementChangeExt %d ... [%d, %d, %d, '%s']\n",
8731 trigger_event, recursion_loop_depth, recursion_loop_detected,
8732 recursion_loop_element, EL_NAME(recursion_loop_element));
8735 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
8737 for (p = 0; p < element_info[element].num_change_pages; p++)
8739 struct ElementChangeInfo *change = &element_info[element].change_page[p];
8741 /* check trigger element for all events where the element that is checked
8742 for changing interacts with a directly adjacent element -- this is
8743 different to element changes that affect other elements to change on the
8744 whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
8745 boolean check_trigger_element =
8746 (trigger_event == CE_TOUCHING_X ||
8747 trigger_event == CE_HITTING_X ||
8748 trigger_event == CE_HIT_BY_X ||
8750 /* this one was forgotten until 3.2.3 */
8751 trigger_event == CE_DIGGING_X);
8754 if (change->can_change_or_has_action &&
8755 change->has_event[trigger_event] &&
8756 change->trigger_side & trigger_side &&
8757 change->trigger_player & trigger_player &&
8758 (!check_trigger_element ||
8759 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
8761 change->actual_trigger_element = trigger_element;
8762 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
8763 change->actual_trigger_side = trigger_side;
8764 change->actual_trigger_ce_value = CustomValue[x][y];
8765 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
8767 /* special case: trigger element not at (x,y) position for some events */
8768 if (check_trigger_element)
8780 { 0, 0 }, { 0, 0 }, { 0, 0 },
8784 int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
8785 int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
8787 change->actual_trigger_ce_value = CustomValue[xx][yy];
8788 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
8791 if (change->can_change && !change_done)
8793 ChangeDelay[x][y] = 1;
8794 ChangeEvent[x][y] = trigger_event;
8796 HandleElementChange(x, y, p);
8800 #if USE_NEW_DELAYED_ACTION
8801 else if (change->has_action)
8803 ExecuteCustomElementAction(x, y, element, p);
8804 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8807 if (change->has_action)
8809 ExecuteCustomElementAction(x, y, element, p);
8810 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8816 RECURSION_LOOP_DETECTION_END();
8821 static void PlayPlayerSound(struct PlayerInfo *player)
8823 int jx = player->jx, jy = player->jy;
8824 int sound_element = player->artwork_element;
8825 int last_action = player->last_action_waiting;
8826 int action = player->action_waiting;
8828 if (player->is_waiting)
8830 if (action != last_action)
8831 PlayLevelSoundElementAction(jx, jy, sound_element, action);
8833 PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
8837 if (action != last_action)
8838 StopSound(element_info[sound_element].sound[last_action]);
8840 if (last_action == ACTION_SLEEPING)
8841 PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
8845 static void PlayAllPlayersSound()
8849 for (i = 0; i < MAX_PLAYERS; i++)
8850 if (stored_player[i].active)
8851 PlayPlayerSound(&stored_player[i]);
8854 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
8856 boolean last_waiting = player->is_waiting;
8857 int move_dir = player->MovDir;
8859 player->dir_waiting = move_dir;
8860 player->last_action_waiting = player->action_waiting;
8864 if (!last_waiting) /* not waiting -> waiting */
8866 player->is_waiting = TRUE;
8868 player->frame_counter_bored =
8870 game.player_boring_delay_fixed +
8871 GetSimpleRandom(game.player_boring_delay_random);
8872 player->frame_counter_sleeping =
8874 game.player_sleeping_delay_fixed +
8875 GetSimpleRandom(game.player_sleeping_delay_random);
8877 InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
8880 if (game.player_sleeping_delay_fixed +
8881 game.player_sleeping_delay_random > 0 &&
8882 player->anim_delay_counter == 0 &&
8883 player->post_delay_counter == 0 &&
8884 FrameCounter >= player->frame_counter_sleeping)
8885 player->is_sleeping = TRUE;
8886 else if (game.player_boring_delay_fixed +
8887 game.player_boring_delay_random > 0 &&
8888 FrameCounter >= player->frame_counter_bored)
8889 player->is_bored = TRUE;
8891 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
8892 player->is_bored ? ACTION_BORING :
8895 if (player->is_sleeping && player->use_murphy)
8897 /* special case for sleeping Murphy when leaning against non-free tile */
8899 if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
8900 (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
8901 !IS_MOVING(player->jx - 1, player->jy)))
8903 else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
8904 (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
8905 !IS_MOVING(player->jx + 1, player->jy)))
8906 move_dir = MV_RIGHT;
8908 player->is_sleeping = FALSE;
8910 player->dir_waiting = move_dir;
8913 if (player->is_sleeping)
8915 if (player->num_special_action_sleeping > 0)
8917 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
8919 int last_special_action = player->special_action_sleeping;
8920 int num_special_action = player->num_special_action_sleeping;
8921 int special_action =
8922 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
8923 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
8924 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
8925 last_special_action + 1 : ACTION_SLEEPING);
8926 int special_graphic =
8927 el_act_dir2img(player->artwork_element, special_action, move_dir);
8929 player->anim_delay_counter =
8930 graphic_info[special_graphic].anim_delay_fixed +
8931 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
8932 player->post_delay_counter =
8933 graphic_info[special_graphic].post_delay_fixed +
8934 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
8936 player->special_action_sleeping = special_action;
8939 if (player->anim_delay_counter > 0)
8941 player->action_waiting = player->special_action_sleeping;
8942 player->anim_delay_counter--;
8944 else if (player->post_delay_counter > 0)
8946 player->post_delay_counter--;
8950 else if (player->is_bored)
8952 if (player->num_special_action_bored > 0)
8954 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
8956 int special_action =
8957 ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
8958 int special_graphic =
8959 el_act_dir2img(player->artwork_element, special_action, move_dir);
8961 player->anim_delay_counter =
8962 graphic_info[special_graphic].anim_delay_fixed +
8963 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
8964 player->post_delay_counter =
8965 graphic_info[special_graphic].post_delay_fixed +
8966 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
8968 player->special_action_bored = special_action;
8971 if (player->anim_delay_counter > 0)
8973 player->action_waiting = player->special_action_bored;
8974 player->anim_delay_counter--;
8976 else if (player->post_delay_counter > 0)
8978 player->post_delay_counter--;
8983 else if (last_waiting) /* waiting -> not waiting */
8985 player->is_waiting = FALSE;
8986 player->is_bored = FALSE;
8987 player->is_sleeping = FALSE;
8989 player->frame_counter_bored = -1;
8990 player->frame_counter_sleeping = -1;
8992 player->anim_delay_counter = 0;
8993 player->post_delay_counter = 0;
8995 player->dir_waiting = player->MovDir;
8996 player->action_waiting = ACTION_DEFAULT;
8998 player->special_action_bored = ACTION_DEFAULT;
8999 player->special_action_sleeping = ACTION_DEFAULT;
9003 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
9005 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
9006 int left = player_action & JOY_LEFT;
9007 int right = player_action & JOY_RIGHT;
9008 int up = player_action & JOY_UP;
9009 int down = player_action & JOY_DOWN;
9010 int button1 = player_action & JOY_BUTTON_1;
9011 int button2 = player_action & JOY_BUTTON_2;
9012 int dx = (left ? -1 : right ? 1 : 0);
9013 int dy = (up ? -1 : down ? 1 : 0);
9015 if (!player->active || tape.pausing)
9021 snapped = SnapField(player, dx, dy);
9025 dropped = DropElement(player);
9027 moved = MovePlayer(player, dx, dy);
9030 if (tape.single_step && tape.recording && !tape.pausing)
9032 if (button1 || (dropped && !moved))
9034 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9035 SnapField(player, 0, 0); /* stop snapping */
9039 SetPlayerWaiting(player, FALSE);
9041 return player_action;
9045 /* no actions for this player (no input at player's configured device) */
9047 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
9048 SnapField(player, 0, 0);
9049 CheckGravityMovementWhenNotMoving(player);
9051 if (player->MovPos == 0)
9052 SetPlayerWaiting(player, TRUE);
9054 if (player->MovPos == 0) /* needed for tape.playing */
9055 player->is_moving = FALSE;
9057 player->is_dropping = FALSE;
9058 player->is_dropping_pressed = FALSE;
9059 player->drop_pressed_delay = 0;
9065 static void CheckLevelTime()
9069 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9071 if (level.native_em_level->lev->home == 0) /* all players at home */
9073 PlayerWins(local_player);
9075 AllPlayersGone = TRUE;
9077 level.native_em_level->lev->home = -1;
9080 if (level.native_em_level->ply[0]->alive == 0 &&
9081 level.native_em_level->ply[1]->alive == 0 &&
9082 level.native_em_level->ply[2]->alive == 0 &&
9083 level.native_em_level->ply[3]->alive == 0) /* all dead */
9084 AllPlayersGone = TRUE;
9087 if (TimeFrames >= FRAMES_PER_SECOND)
9092 for (i = 0; i < MAX_PLAYERS; i++)
9094 struct PlayerInfo *player = &stored_player[i];
9096 if (SHIELD_ON(player))
9098 player->shield_normal_time_left--;
9100 if (player->shield_deadly_time_left > 0)
9101 player->shield_deadly_time_left--;
9105 if (!local_player->LevelSolved && !level.use_step_counter)
9113 if (TimeLeft <= 10 && setup.time_limit)
9114 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
9116 DrawGameValue_Time(TimeLeft);
9118 if (!TimeLeft && setup.time_limit)
9120 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9121 level.native_em_level->lev->killed_out_of_time = TRUE;
9123 for (i = 0; i < MAX_PLAYERS; i++)
9124 KillPlayer(&stored_player[i]);
9127 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
9128 DrawGameValue_Time(TimePlayed);
9130 level.native_em_level->lev->time =
9131 (level.time == 0 ? TimePlayed : TimeLeft);
9134 if (tape.recording || tape.playing)
9135 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
9139 void AdvanceFrameAndPlayerCounters(int player_nr)
9143 /* advance frame counters (global frame counter and time frame counter) */
9147 /* advance player counters (counters for move delay, move animation etc.) */
9148 for (i = 0; i < MAX_PLAYERS; i++)
9150 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
9151 int move_delay_value = stored_player[i].move_delay_value;
9152 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
9154 if (!advance_player_counters) /* not all players may be affected */
9157 #if USE_NEW_PLAYER_ANIM
9158 if (move_frames == 0) /* less than one move per game frame */
9160 int stepsize = TILEX / move_delay_value;
9161 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
9162 int count = (stored_player[i].is_moving ?
9163 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
9165 if (count % delay == 0)
9170 stored_player[i].Frame += move_frames;
9172 if (stored_player[i].MovPos != 0)
9173 stored_player[i].StepFrame += move_frames;
9175 if (stored_player[i].move_delay > 0)
9176 stored_player[i].move_delay--;
9178 /* due to bugs in previous versions, counter must count up, not down */
9179 if (stored_player[i].push_delay != -1)
9180 stored_player[i].push_delay++;
9182 if (stored_player[i].drop_delay > 0)
9183 stored_player[i].drop_delay--;
9185 if (stored_player[i].is_dropping_pressed)
9186 stored_player[i].drop_pressed_delay++;
9190 void StartGameActions(boolean init_network_game, boolean record_tape,
9193 unsigned long new_random_seed = InitRND(random_seed);
9196 TapeStartRecording(new_random_seed);
9198 #if defined(NETWORK_AVALIABLE)
9199 if (init_network_game)
9201 SendToServer_StartPlaying();
9212 static unsigned long game_frame_delay = 0;
9213 unsigned long game_frame_delay_value;
9214 byte *recorded_player_action;
9215 byte summarized_player_action = 0;
9216 byte tape_action[MAX_PLAYERS];
9219 /* detect endless loops, caused by custom element programming */
9220 if (recursion_loop_detected && recursion_loop_depth == 0)
9222 char *message = getStringCat3("Internal Error ! Element ",
9223 EL_NAME(recursion_loop_element),
9224 " caused endless loop ! Quit the game ?");
9226 Error(ERR_WARN, "element '%s' caused endless loop in game engine",
9227 EL_NAME(recursion_loop_element));
9229 RequestQuitGameExt(FALSE, level_editor_test_game, message);
9231 recursion_loop_detected = FALSE; /* if game should be continued */
9238 if (game.restart_level)
9239 StartGameActions(options.network, setup.autorecord, NEW_RANDOMIZE);
9241 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9243 if (level.native_em_level->lev->home == 0) /* all players at home */
9245 PlayerWins(local_player);
9247 AllPlayersGone = TRUE;
9249 level.native_em_level->lev->home = -1;
9252 if (level.native_em_level->ply[0]->alive == 0 &&
9253 level.native_em_level->ply[1]->alive == 0 &&
9254 level.native_em_level->ply[2]->alive == 0 &&
9255 level.native_em_level->ply[3]->alive == 0) /* all dead */
9256 AllPlayersGone = TRUE;
9259 if (local_player->LevelSolved)
9262 if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
9265 if (game_status != GAME_MODE_PLAYING) /* status might have changed */
9268 game_frame_delay_value =
9269 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
9271 if (tape.playing && tape.warp_forward && !tape.pausing)
9272 game_frame_delay_value = 0;
9274 /* ---------- main game synchronization point ---------- */
9276 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
9278 if (network_playing && !network_player_action_received)
9280 /* try to get network player actions in time */
9282 #if defined(NETWORK_AVALIABLE)
9283 /* last chance to get network player actions without main loop delay */
9287 /* game was quit by network peer */
9288 if (game_status != GAME_MODE_PLAYING)
9291 if (!network_player_action_received)
9292 return; /* failed to get network player actions in time */
9294 /* do not yet reset "network_player_action_received" (for tape.pausing) */
9300 /* at this point we know that we really continue executing the game */
9302 network_player_action_received = FALSE;
9304 /* when playing tape, read previously recorded player input from tape data */
9305 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
9308 /* TapePlayAction() may return NULL when toggling to "pause before death" */
9313 if (tape.set_centered_player)
9315 game.centered_player_nr_next = tape.centered_player_nr_next;
9316 game.set_centered_player = TRUE;
9319 for (i = 0; i < MAX_PLAYERS; i++)
9321 summarized_player_action |= stored_player[i].action;
9323 if (!network_playing)
9324 stored_player[i].effective_action = stored_player[i].action;
9327 #if defined(NETWORK_AVALIABLE)
9328 if (network_playing)
9329 SendToServer_MovePlayer(summarized_player_action);
9332 if (!options.network && !setup.team_mode)
9333 local_player->effective_action = summarized_player_action;
9335 if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
9337 for (i = 0; i < MAX_PLAYERS; i++)
9338 stored_player[i].effective_action =
9339 (i == game.centered_player_nr ? summarized_player_action : 0);
9342 if (recorded_player_action != NULL)
9343 for (i = 0; i < MAX_PLAYERS; i++)
9344 stored_player[i].effective_action = recorded_player_action[i];
9346 for (i = 0; i < MAX_PLAYERS; i++)
9348 tape_action[i] = stored_player[i].effective_action;
9350 /* (this can only happen in the R'n'D game engine) */
9351 if (tape.recording && tape_action[i] && !tape.player_participates[i])
9352 tape.player_participates[i] = TRUE; /* player just appeared from CE */
9355 /* only record actions from input devices, but not programmed actions */
9357 TapeRecordAction(tape_action);
9359 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9361 GameActions_EM_Main();
9369 void GameActions_EM_Main()
9371 byte effective_action[MAX_PLAYERS];
9372 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
9375 for (i = 0; i < MAX_PLAYERS; i++)
9376 effective_action[i] = stored_player[i].effective_action;
9378 GameActions_EM(effective_action, warp_mode);
9382 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
9385 void GameActions_RND()
9387 int magic_wall_x = 0, magic_wall_y = 0;
9388 int i, x, y, element, graphic;
9390 InitPlayfieldScanModeVars();
9392 #if USE_ONE_MORE_CHANGE_PER_FRAME
9393 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
9395 SCAN_PLAYFIELD(x, y)
9397 ChangeCount[x][y] = 0;
9398 ChangeEvent[x][y] = -1;
9403 if (game.set_centered_player)
9405 boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
9407 /* switching to "all players" only possible if all players fit to screen */
9408 if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
9410 game.centered_player_nr_next = game.centered_player_nr;
9411 game.set_centered_player = FALSE;
9414 /* do not switch focus to non-existing (or non-active) player */
9415 if (game.centered_player_nr_next >= 0 &&
9416 !stored_player[game.centered_player_nr_next].active)
9418 game.centered_player_nr_next = game.centered_player_nr;
9419 game.set_centered_player = FALSE;
9423 if (game.set_centered_player &&
9424 ScreenMovPos == 0) /* screen currently aligned at tile position */
9428 if (game.centered_player_nr_next == -1)
9430 setScreenCenteredToAllPlayers(&sx, &sy);
9434 sx = stored_player[game.centered_player_nr_next].jx;
9435 sy = stored_player[game.centered_player_nr_next].jy;
9438 game.centered_player_nr = game.centered_player_nr_next;
9439 game.set_centered_player = FALSE;
9441 DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
9442 DrawGameDoorValues();
9445 for (i = 0; i < MAX_PLAYERS; i++)
9447 int actual_player_action = stored_player[i].effective_action;
9450 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
9451 - rnd_equinox_tetrachloride 048
9452 - rnd_equinox_tetrachloride_ii 096
9453 - rnd_emanuel_schmieg 002
9454 - doctor_sloan_ww 001, 020
9456 if (stored_player[i].MovPos == 0)
9457 CheckGravityMovement(&stored_player[i]);
9460 /* overwrite programmed action with tape action */
9461 if (stored_player[i].programmed_action)
9462 actual_player_action = stored_player[i].programmed_action;
9464 PlayerActions(&stored_player[i], actual_player_action);
9466 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
9469 ScrollScreen(NULL, SCROLL_GO_ON);
9471 /* for backwards compatibility, the following code emulates a fixed bug that
9472 occured when pushing elements (causing elements that just made their last
9473 pushing step to already (if possible) make their first falling step in the
9474 same game frame, which is bad); this code is also needed to use the famous
9475 "spring push bug" which is used in older levels and might be wanted to be
9476 used also in newer levels, but in this case the buggy pushing code is only
9477 affecting the "spring" element and no other elements */
9479 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
9481 for (i = 0; i < MAX_PLAYERS; i++)
9483 struct PlayerInfo *player = &stored_player[i];
9487 if (player->active && player->is_pushing && player->is_moving &&
9489 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
9490 Feld[x][y] == EL_SPRING))
9492 ContinueMoving(x, y);
9494 /* continue moving after pushing (this is actually a bug) */
9495 if (!IS_MOVING(x, y))
9503 SCAN_PLAYFIELD(x, y)
9505 ChangeCount[x][y] = 0;
9506 ChangeEvent[x][y] = -1;
9508 /* this must be handled before main playfield loop */
9509 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
9512 if (MovDelay[x][y] <= 0)
9516 #if USE_NEW_SNAP_DELAY
9517 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
9520 if (MovDelay[x][y] <= 0)
9523 DrawLevelField(x, y);
9525 TestIfElementTouchesCustomElement(x, y); /* for empty space */
9531 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
9533 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
9534 printf("GameActions(): This should never happen!\n");
9536 ChangePage[x][y] = -1;
9541 if (WasJustMoving[x][y] > 0)
9542 WasJustMoving[x][y]--;
9543 if (WasJustFalling[x][y] > 0)
9544 WasJustFalling[x][y]--;
9545 if (CheckCollision[x][y] > 0)
9546 CheckCollision[x][y]--;
9547 if (CheckImpact[x][y] > 0)
9548 CheckImpact[x][y]--;
9552 /* reset finished pushing action (not done in ContinueMoving() to allow
9553 continuous pushing animation for elements with zero push delay) */
9554 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
9556 ResetGfxAnimation(x, y);
9557 DrawLevelField(x, y);
9561 if (IS_BLOCKED(x, y))
9565 Blocked2Moving(x, y, &oldx, &oldy);
9566 if (!IS_MOVING(oldx, oldy))
9568 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
9569 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
9570 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
9571 printf("GameActions(): This should never happen!\n");
9577 SCAN_PLAYFIELD(x, y)
9579 element = Feld[x][y];
9580 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9582 ResetGfxFrame(x, y, TRUE);
9584 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
9585 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
9586 ResetRandomAnimationValue(x, y);
9588 SetRandomAnimationValue(x, y);
9590 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
9592 if (IS_INACTIVE(element))
9594 if (IS_ANIMATED(graphic))
9595 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9600 /* this may take place after moving, so 'element' may have changed */
9601 if (IS_CHANGING(x, y) &&
9602 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
9604 int page = element_info[element].event_page_nr[CE_DELAY];
9607 HandleElementChange(x, y, page);
9609 if (CAN_CHANGE(element))
9610 HandleElementChange(x, y, page);
9612 if (HAS_ACTION(element))
9613 ExecuteCustomElementAction(x, y, element, page);
9616 element = Feld[x][y];
9617 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9620 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
9624 element = Feld[x][y];
9625 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9627 if (IS_ANIMATED(graphic) &&
9630 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9632 if (IS_GEM(element) || element == EL_SP_INFOTRON)
9633 DrawTwinkleOnField(x, y);
9635 else if ((element == EL_ACID ||
9636 element == EL_EXIT_OPEN ||
9637 element == EL_SP_EXIT_OPEN ||
9638 element == EL_SP_TERMINAL ||
9639 element == EL_SP_TERMINAL_ACTIVE ||
9640 element == EL_EXTRA_TIME ||
9641 element == EL_SHIELD_NORMAL ||
9642 element == EL_SHIELD_DEADLY) &&
9643 IS_ANIMATED(graphic))
9644 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9645 else if (IS_MOVING(x, y))
9646 ContinueMoving(x, y);
9647 else if (IS_ACTIVE_BOMB(element))
9648 CheckDynamite(x, y);
9649 else if (element == EL_AMOEBA_GROWING)
9650 AmoebeWaechst(x, y);
9651 else if (element == EL_AMOEBA_SHRINKING)
9652 AmoebaDisappearing(x, y);
9654 #if !USE_NEW_AMOEBA_CODE
9655 else if (IS_AMOEBALIVE(element))
9656 AmoebeAbleger(x, y);
9659 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
9661 else if (element == EL_EXIT_CLOSED)
9663 else if (element == EL_SP_EXIT_CLOSED)
9665 else if (element == EL_EXPANDABLE_WALL_GROWING)
9667 else if (element == EL_EXPANDABLE_WALL ||
9668 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9669 element == EL_EXPANDABLE_WALL_VERTICAL ||
9670 element == EL_EXPANDABLE_WALL_ANY ||
9671 element == EL_BD_EXPANDABLE_WALL)
9673 else if (element == EL_FLAMES)
9674 CheckForDragon(x, y);
9675 else if (element == EL_EXPLOSION)
9676 ; /* drawing of correct explosion animation is handled separately */
9677 else if (element == EL_ELEMENT_SNAPPING ||
9678 element == EL_DIAGONAL_SHRINKING ||
9679 element == EL_DIAGONAL_GROWING)
9681 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
9683 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9685 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
9686 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9688 if (IS_BELT_ACTIVE(element))
9689 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
9691 if (game.magic_wall_active)
9693 int jx = local_player->jx, jy = local_player->jy;
9695 /* play the element sound at the position nearest to the player */
9696 if ((element == EL_MAGIC_WALL_FULL ||
9697 element == EL_MAGIC_WALL_ACTIVE ||
9698 element == EL_MAGIC_WALL_EMPTYING ||
9699 element == EL_BD_MAGIC_WALL_FULL ||
9700 element == EL_BD_MAGIC_WALL_ACTIVE ||
9701 element == EL_BD_MAGIC_WALL_EMPTYING) &&
9702 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
9710 #if USE_NEW_AMOEBA_CODE
9711 /* new experimental amoeba growth stuff */
9712 if (!(FrameCounter % 8))
9714 static unsigned long random = 1684108901;
9716 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
9718 x = RND(lev_fieldx);
9719 y = RND(lev_fieldy);
9720 element = Feld[x][y];
9722 if (!IS_PLAYER(x,y) &&
9723 (element == EL_EMPTY ||
9724 CAN_GROW_INTO(element) ||
9725 element == EL_QUICKSAND_EMPTY ||
9726 element == EL_ACID_SPLASH_LEFT ||
9727 element == EL_ACID_SPLASH_RIGHT))
9729 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
9730 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
9731 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
9732 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
9733 Feld[x][y] = EL_AMOEBA_DROP;
9736 random = random * 129 + 1;
9742 if (game.explosions_delayed)
9745 game.explosions_delayed = FALSE;
9747 SCAN_PLAYFIELD(x, y)
9749 element = Feld[x][y];
9751 if (ExplodeField[x][y])
9752 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
9753 else if (element == EL_EXPLOSION)
9754 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
9756 ExplodeField[x][y] = EX_TYPE_NONE;
9759 game.explosions_delayed = TRUE;
9762 if (game.magic_wall_active)
9764 if (!(game.magic_wall_time_left % 4))
9766 int element = Feld[magic_wall_x][magic_wall_y];
9768 if (element == EL_BD_MAGIC_WALL_FULL ||
9769 element == EL_BD_MAGIC_WALL_ACTIVE ||
9770 element == EL_BD_MAGIC_WALL_EMPTYING)
9771 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
9773 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
9776 if (game.magic_wall_time_left > 0)
9778 game.magic_wall_time_left--;
9779 if (!game.magic_wall_time_left)
9781 SCAN_PLAYFIELD(x, y)
9783 element = Feld[x][y];
9785 if (element == EL_MAGIC_WALL_ACTIVE ||
9786 element == EL_MAGIC_WALL_FULL)
9788 Feld[x][y] = EL_MAGIC_WALL_DEAD;
9789 DrawLevelField(x, y);
9791 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
9792 element == EL_BD_MAGIC_WALL_FULL)
9794 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
9795 DrawLevelField(x, y);
9799 game.magic_wall_active = FALSE;
9804 if (game.light_time_left > 0)
9806 game.light_time_left--;
9808 if (game.light_time_left == 0)
9809 RedrawAllLightSwitchesAndInvisibleElements();
9812 if (game.timegate_time_left > 0)
9814 game.timegate_time_left--;
9816 if (game.timegate_time_left == 0)
9817 CloseAllOpenTimegates();
9820 if (game.lenses_time_left > 0)
9822 game.lenses_time_left--;
9824 if (game.lenses_time_left == 0)
9825 RedrawAllInvisibleElementsForLenses();
9828 if (game.magnify_time_left > 0)
9830 game.magnify_time_left--;
9832 if (game.magnify_time_left == 0)
9833 RedrawAllInvisibleElementsForMagnifier();
9836 for (i = 0; i < MAX_PLAYERS; i++)
9838 struct PlayerInfo *player = &stored_player[i];
9840 if (SHIELD_ON(player))
9842 if (player->shield_deadly_time_left)
9843 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
9844 else if (player->shield_normal_time_left)
9845 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
9852 PlayAllPlayersSound();
9854 if (options.debug) /* calculate frames per second */
9856 static unsigned long fps_counter = 0;
9857 static int fps_frames = 0;
9858 unsigned long fps_delay_ms = Counter() - fps_counter;
9862 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
9864 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
9867 fps_counter = Counter();
9870 redraw_mask |= REDRAW_FPS;
9873 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
9875 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
9877 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
9879 local_player->show_envelope = 0;
9882 /* use random number generator in every frame to make it less predictable */
9883 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
9887 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
9889 int min_x = x, min_y = y, max_x = x, max_y = y;
9892 for (i = 0; i < MAX_PLAYERS; i++)
9894 int jx = stored_player[i].jx, jy = stored_player[i].jy;
9896 if (!stored_player[i].active || &stored_player[i] == player)
9899 min_x = MIN(min_x, jx);
9900 min_y = MIN(min_y, jy);
9901 max_x = MAX(max_x, jx);
9902 max_y = MAX(max_y, jy);
9905 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
9908 static boolean AllPlayersInVisibleScreen()
9912 for (i = 0; i < MAX_PLAYERS; i++)
9914 int jx = stored_player[i].jx, jy = stored_player[i].jy;
9916 if (!stored_player[i].active)
9919 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
9926 void ScrollLevel(int dx, int dy)
9928 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
9931 BlitBitmap(drawto_field, drawto_field,
9932 FX + TILEX * (dx == -1) - softscroll_offset,
9933 FY + TILEY * (dy == -1) - softscroll_offset,
9934 SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
9935 SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
9936 FX + TILEX * (dx == 1) - softscroll_offset,
9937 FY + TILEY * (dy == 1) - softscroll_offset);
9941 x = (dx == 1 ? BX1 : BX2);
9942 for (y = BY1; y <= BY2; y++)
9943 DrawScreenField(x, y);
9948 y = (dy == 1 ? BY1 : BY2);
9949 for (x = BX1; x <= BX2; x++)
9950 DrawScreenField(x, y);
9953 redraw_mask |= REDRAW_FIELD;
9956 static boolean canFallDown(struct PlayerInfo *player)
9958 int jx = player->jx, jy = player->jy;
9960 return (IN_LEV_FIELD(jx, jy + 1) &&
9961 (IS_FREE(jx, jy + 1) ||
9962 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
9963 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
9964 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
9967 static boolean canPassField(int x, int y, int move_dir)
9969 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9970 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9971 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
9974 int element = Feld[x][y];
9976 return (IS_PASSABLE_FROM(element, opposite_dir) &&
9977 !CAN_MOVE(element) &&
9978 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
9979 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
9980 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
9983 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
9985 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9986 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9987 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
9991 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9992 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
9993 (IS_DIGGABLE(Feld[newx][newy]) ||
9994 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9995 canPassField(newx, newy, move_dir)));
9998 static void CheckGravityMovement(struct PlayerInfo *player)
10000 #if USE_PLAYER_GRAVITY
10001 if (player->gravity && !player->programmed_action)
10003 if (game.gravity && !player->programmed_action)
10006 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
10007 int move_dir_vertical = player->effective_action & MV_VERTICAL;
10008 boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
10009 int jx = player->jx, jy = player->jy;
10010 boolean player_is_moving_to_valid_field =
10011 (!player_is_snapping &&
10012 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
10013 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
10014 boolean player_can_fall_down = canFallDown(player);
10016 if (player_can_fall_down &&
10017 !player_is_moving_to_valid_field)
10018 player->programmed_action = MV_DOWN;
10022 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
10024 return CheckGravityMovement(player);
10026 #if USE_PLAYER_GRAVITY
10027 if (player->gravity && !player->programmed_action)
10029 if (game.gravity && !player->programmed_action)
10032 int jx = player->jx, jy = player->jy;
10033 boolean field_under_player_is_free =
10034 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
10035 boolean player_is_standing_on_valid_field =
10036 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
10037 (IS_WALKABLE(Feld[jx][jy]) &&
10038 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
10040 if (field_under_player_is_free && !player_is_standing_on_valid_field)
10041 player->programmed_action = MV_DOWN;
10046 MovePlayerOneStep()
10047 -----------------------------------------------------------------------------
10048 dx, dy: direction (non-diagonal) to try to move the player to
10049 real_dx, real_dy: direction as read from input device (can be diagonal)
10052 boolean MovePlayerOneStep(struct PlayerInfo *player,
10053 int dx, int dy, int real_dx, int real_dy)
10055 int jx = player->jx, jy = player->jy;
10056 int new_jx = jx + dx, new_jy = jy + dy;
10057 #if !USE_FIXED_DONT_RUN_INTO
10061 boolean player_can_move = !player->cannot_move;
10063 if (!player->active || (!dx && !dy))
10064 return MP_NO_ACTION;
10066 player->MovDir = (dx < 0 ? MV_LEFT :
10067 dx > 0 ? MV_RIGHT :
10069 dy > 0 ? MV_DOWN : MV_NONE);
10071 if (!IN_LEV_FIELD(new_jx, new_jy))
10072 return MP_NO_ACTION;
10074 if (!player_can_move)
10076 if (player->MovPos == 0)
10078 player->is_moving = FALSE;
10079 player->is_digging = FALSE;
10080 player->is_collecting = FALSE;
10081 player->is_snapping = FALSE;
10082 player->is_pushing = FALSE;
10087 if (!options.network && game.centered_player_nr == -1 &&
10088 !AllPlayersInSight(player, new_jx, new_jy))
10089 return MP_NO_ACTION;
10091 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
10092 return MP_NO_ACTION;
10095 #if !USE_FIXED_DONT_RUN_INTO
10096 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
10098 /* (moved to DigField()) */
10099 if (player_can_move && DONT_RUN_INTO(element))
10101 if (element == EL_ACID && dx == 0 && dy == 1)
10103 SplashAcid(new_jx, new_jy);
10104 Feld[jx][jy] = EL_PLAYER_1;
10105 InitMovingField(jx, jy, MV_DOWN);
10106 Store[jx][jy] = EL_ACID;
10107 ContinueMoving(jx, jy);
10108 BuryPlayer(player);
10111 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
10117 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
10118 if (can_move != MP_MOVING)
10121 /* check if DigField() has caused relocation of the player */
10122 if (player->jx != jx || player->jy != jy)
10123 return MP_NO_ACTION; /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
10125 StorePlayer[jx][jy] = 0;
10126 player->last_jx = jx;
10127 player->last_jy = jy;
10128 player->jx = new_jx;
10129 player->jy = new_jy;
10130 StorePlayer[new_jx][new_jy] = player->element_nr;
10132 if (player->move_delay_value_next != -1)
10134 player->move_delay_value = player->move_delay_value_next;
10135 player->move_delay_value_next = -1;
10139 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
10141 player->step_counter++;
10143 PlayerVisit[jx][jy] = FrameCounter;
10145 #if USE_UFAST_PLAYER_EXIT_BUGFIX
10146 player->is_moving = TRUE;
10150 /* should better be called in MovePlayer(), but this breaks some tapes */
10151 ScrollPlayer(player, SCROLL_INIT);
10157 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
10159 int jx = player->jx, jy = player->jy;
10160 int old_jx = jx, old_jy = jy;
10161 int moved = MP_NO_ACTION;
10163 if (!player->active)
10168 if (player->MovPos == 0)
10170 player->is_moving = FALSE;
10171 player->is_digging = FALSE;
10172 player->is_collecting = FALSE;
10173 player->is_snapping = FALSE;
10174 player->is_pushing = FALSE;
10180 if (player->move_delay > 0)
10183 player->move_delay = -1; /* set to "uninitialized" value */
10185 /* store if player is automatically moved to next field */
10186 player->is_auto_moving = (player->programmed_action != MV_NONE);
10188 /* remove the last programmed player action */
10189 player->programmed_action = 0;
10191 if (player->MovPos)
10193 /* should only happen if pre-1.2 tape recordings are played */
10194 /* this is only for backward compatibility */
10196 int original_move_delay_value = player->move_delay_value;
10199 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
10203 /* scroll remaining steps with finest movement resolution */
10204 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
10206 while (player->MovPos)
10208 ScrollPlayer(player, SCROLL_GO_ON);
10209 ScrollScreen(NULL, SCROLL_GO_ON);
10211 AdvanceFrameAndPlayerCounters(player->index_nr);
10217 player->move_delay_value = original_move_delay_value;
10220 player->is_active = FALSE;
10222 if (player->last_move_dir & MV_HORIZONTAL)
10224 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
10225 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
10229 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
10230 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
10233 #if USE_FIXED_BORDER_RUNNING_GFX
10234 if (!moved && !player->is_active)
10236 player->is_moving = FALSE;
10237 player->is_digging = FALSE;
10238 player->is_collecting = FALSE;
10239 player->is_snapping = FALSE;
10240 player->is_pushing = FALSE;
10248 if (moved & MP_MOVING && !ScreenMovPos &&
10249 (player->index_nr == game.centered_player_nr ||
10250 game.centered_player_nr == -1))
10252 if (moved & MP_MOVING && !ScreenMovPos &&
10253 (player == local_player || !options.network))
10256 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
10257 int offset = (setup.scroll_delay ? 3 : 0);
10259 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
10261 /* actual player has left the screen -- scroll in that direction */
10262 if (jx != old_jx) /* player has moved horizontally */
10263 scroll_x += (jx - old_jx);
10264 else /* player has moved vertically */
10265 scroll_y += (jy - old_jy);
10269 if (jx != old_jx) /* player has moved horizontally */
10271 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
10272 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
10273 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
10275 /* don't scroll over playfield boundaries */
10276 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
10277 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
10279 /* don't scroll more than one field at a time */
10280 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
10282 /* don't scroll against the player's moving direction */
10283 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
10284 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
10285 scroll_x = old_scroll_x;
10287 else /* player has moved vertically */
10289 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
10290 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
10291 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
10293 /* don't scroll over playfield boundaries */
10294 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
10295 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
10297 /* don't scroll more than one field at a time */
10298 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
10300 /* don't scroll against the player's moving direction */
10301 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
10302 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
10303 scroll_y = old_scroll_y;
10307 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
10310 if (!options.network && game.centered_player_nr == -1 &&
10311 !AllPlayersInVisibleScreen())
10313 scroll_x = old_scroll_x;
10314 scroll_y = old_scroll_y;
10318 if (!options.network && !AllPlayersInVisibleScreen())
10320 scroll_x = old_scroll_x;
10321 scroll_y = old_scroll_y;
10326 ScrollScreen(player, SCROLL_INIT);
10327 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
10332 player->StepFrame = 0;
10334 if (moved & MP_MOVING)
10336 if (old_jx != jx && old_jy == jy)
10337 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
10338 else if (old_jx == jx && old_jy != jy)
10339 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
10341 DrawLevelField(jx, jy); /* for "crumbled sand" */
10343 player->last_move_dir = player->MovDir;
10344 player->is_moving = TRUE;
10345 player->is_snapping = FALSE;
10346 player->is_switching = FALSE;
10347 player->is_dropping = FALSE;
10348 player->is_dropping_pressed = FALSE;
10349 player->drop_pressed_delay = 0;
10352 /* should better be called here than above, but this breaks some tapes */
10353 ScrollPlayer(player, SCROLL_INIT);
10358 CheckGravityMovementWhenNotMoving(player);
10360 player->is_moving = FALSE;
10362 /* at this point, the player is allowed to move, but cannot move right now
10363 (e.g. because of something blocking the way) -- ensure that the player
10364 is also allowed to move in the next frame (in old versions before 3.1.1,
10365 the player was forced to wait again for eight frames before next try) */
10367 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
10368 player->move_delay = 0; /* allow direct movement in the next frame */
10371 if (player->move_delay == -1) /* not yet initialized by DigField() */
10372 player->move_delay = player->move_delay_value;
10374 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10376 TestIfPlayerTouchesBadThing(jx, jy);
10377 TestIfPlayerTouchesCustomElement(jx, jy);
10380 if (!player->active)
10381 RemovePlayer(player);
10386 void ScrollPlayer(struct PlayerInfo *player, int mode)
10388 int jx = player->jx, jy = player->jy;
10389 int last_jx = player->last_jx, last_jy = player->last_jy;
10390 int move_stepsize = TILEX / player->move_delay_value;
10392 #if USE_NEW_PLAYER_SPEED
10393 if (!player->active)
10396 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
10399 if (!player->active || player->MovPos == 0)
10403 if (mode == SCROLL_INIT)
10405 player->actual_frame_counter = FrameCounter;
10406 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10408 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
10409 Feld[last_jx][last_jy] == EL_EMPTY)
10411 int last_field_block_delay = 0; /* start with no blocking at all */
10412 int block_delay_adjustment = player->block_delay_adjustment;
10414 /* if player blocks last field, add delay for exactly one move */
10415 if (player->block_last_field)
10417 last_field_block_delay += player->move_delay_value;
10419 /* when blocking enabled, prevent moving up despite gravity */
10420 #if USE_PLAYER_GRAVITY
10421 if (player->gravity && player->MovDir == MV_UP)
10422 block_delay_adjustment = -1;
10424 if (game.gravity && player->MovDir == MV_UP)
10425 block_delay_adjustment = -1;
10429 /* add block delay adjustment (also possible when not blocking) */
10430 last_field_block_delay += block_delay_adjustment;
10432 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
10433 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
10436 #if USE_NEW_PLAYER_SPEED
10437 if (player->MovPos != 0) /* player has not yet reached destination */
10443 else if (!FrameReached(&player->actual_frame_counter, 1))
10446 #if USE_NEW_PLAYER_SPEED
10447 if (player->MovPos != 0)
10449 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
10450 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10452 /* before DrawPlayer() to draw correct player graphic for this case */
10453 if (player->MovPos == 0)
10454 CheckGravityMovement(player);
10457 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
10458 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10460 /* before DrawPlayer() to draw correct player graphic for this case */
10461 if (player->MovPos == 0)
10462 CheckGravityMovement(player);
10465 if (player->MovPos == 0) /* player reached destination field */
10467 if (player->move_delay_reset_counter > 0)
10469 player->move_delay_reset_counter--;
10471 if (player->move_delay_reset_counter == 0)
10473 /* continue with normal speed after quickly moving through gate */
10474 HALVE_PLAYER_SPEED(player);
10476 /* be able to make the next move without delay */
10477 player->move_delay = 0;
10481 player->last_jx = jx;
10482 player->last_jy = jy;
10484 if (Feld[jx][jy] == EL_EXIT_OPEN ||
10485 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
10486 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
10488 DrawPlayer(player); /* needed here only to cleanup last field */
10489 RemovePlayer(player);
10491 if (local_player->friends_still_needed == 0 ||
10492 IS_SP_ELEMENT(Feld[jx][jy]))
10493 PlayerWins(player);
10496 /* this breaks one level: "machine", level 000 */
10498 int move_direction = player->MovDir;
10499 int enter_side = MV_DIR_OPPOSITE(move_direction);
10500 int leave_side = move_direction;
10501 int old_jx = last_jx;
10502 int old_jy = last_jy;
10503 int old_element = Feld[old_jx][old_jy];
10504 int new_element = Feld[jx][jy];
10506 if (IS_CUSTOM_ELEMENT(old_element))
10507 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
10509 player->index_bit, leave_side);
10511 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
10512 CE_PLAYER_LEAVES_X,
10513 player->index_bit, leave_side);
10515 if (IS_CUSTOM_ELEMENT(new_element))
10516 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
10517 player->index_bit, enter_side);
10519 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
10520 CE_PLAYER_ENTERS_X,
10521 player->index_bit, enter_side);
10523 CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
10524 CE_MOVE_OF_X, move_direction);
10527 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10529 TestIfPlayerTouchesBadThing(jx, jy);
10530 TestIfPlayerTouchesCustomElement(jx, jy);
10532 /* needed because pushed element has not yet reached its destination,
10533 so it would trigger a change event at its previous field location */
10534 if (!player->is_pushing)
10535 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
10537 if (!player->active)
10538 RemovePlayer(player);
10541 if (!local_player->LevelSolved && level.use_step_counter)
10551 if (TimeLeft <= 10 && setup.time_limit)
10552 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
10554 DrawGameValue_Time(TimeLeft);
10556 if (!TimeLeft && setup.time_limit)
10557 for (i = 0; i < MAX_PLAYERS; i++)
10558 KillPlayer(&stored_player[i]);
10560 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
10561 DrawGameValue_Time(TimePlayed);
10564 if (tape.single_step && tape.recording && !tape.pausing &&
10565 !player->programmed_action)
10566 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10570 void ScrollScreen(struct PlayerInfo *player, int mode)
10572 static unsigned long screen_frame_counter = 0;
10574 if (mode == SCROLL_INIT)
10576 /* set scrolling step size according to actual player's moving speed */
10577 ScrollStepSize = TILEX / player->move_delay_value;
10579 screen_frame_counter = FrameCounter;
10580 ScreenMovDir = player->MovDir;
10581 ScreenMovPos = player->MovPos;
10582 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
10585 else if (!FrameReached(&screen_frame_counter, 1))
10590 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
10591 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
10592 redraw_mask |= REDRAW_FIELD;
10595 ScreenMovDir = MV_NONE;
10598 void TestIfPlayerTouchesCustomElement(int x, int y)
10600 static int xy[4][2] =
10607 static int trigger_sides[4][2] =
10609 /* center side border side */
10610 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
10611 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
10612 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
10613 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
10615 static int touch_dir[4] =
10617 MV_LEFT | MV_RIGHT,
10622 int center_element = Feld[x][y]; /* should always be non-moving! */
10625 for (i = 0; i < NUM_DIRECTIONS; i++)
10627 int xx = x + xy[i][0];
10628 int yy = y + xy[i][1];
10629 int center_side = trigger_sides[i][0];
10630 int border_side = trigger_sides[i][1];
10631 int border_element;
10633 if (!IN_LEV_FIELD(xx, yy))
10636 if (IS_PLAYER(x, y))
10638 struct PlayerInfo *player = PLAYERINFO(x, y);
10640 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10641 border_element = Feld[xx][yy]; /* may be moving! */
10642 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
10643 border_element = Feld[xx][yy];
10644 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
10645 border_element = MovingOrBlocked2Element(xx, yy);
10647 continue; /* center and border element do not touch */
10649 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
10650 player->index_bit, border_side);
10651 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
10652 CE_PLAYER_TOUCHES_X,
10653 player->index_bit, border_side);
10655 else if (IS_PLAYER(xx, yy))
10657 struct PlayerInfo *player = PLAYERINFO(xx, yy);
10659 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10661 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
10662 continue; /* center and border element do not touch */
10665 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
10666 player->index_bit, center_side);
10667 CheckTriggeredElementChangeByPlayer(x, y, center_element,
10668 CE_PLAYER_TOUCHES_X,
10669 player->index_bit, center_side);
10675 #if USE_ELEMENT_TOUCHING_BUGFIX
10677 void TestIfElementTouchesCustomElement(int x, int y)
10679 static int xy[4][2] =
10686 static int trigger_sides[4][2] =
10688 /* center side border side */
10689 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
10690 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
10691 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
10692 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
10694 static int touch_dir[4] =
10696 MV_LEFT | MV_RIGHT,
10701 boolean change_center_element = FALSE;
10702 int center_element = Feld[x][y]; /* should always be non-moving! */
10703 int border_element_old[NUM_DIRECTIONS];
10706 for (i = 0; i < NUM_DIRECTIONS; i++)
10708 int xx = x + xy[i][0];
10709 int yy = y + xy[i][1];
10710 int border_element;
10712 border_element_old[i] = -1;
10714 if (!IN_LEV_FIELD(xx, yy))
10717 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10718 border_element = Feld[xx][yy]; /* may be moving! */
10719 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
10720 border_element = Feld[xx][yy];
10721 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
10722 border_element = MovingOrBlocked2Element(xx, yy);
10724 continue; /* center and border element do not touch */
10726 border_element_old[i] = border_element;
10729 for (i = 0; i < NUM_DIRECTIONS; i++)
10731 int xx = x + xy[i][0];
10732 int yy = y + xy[i][1];
10733 int center_side = trigger_sides[i][0];
10734 int border_element = border_element_old[i];
10736 if (border_element == -1)
10739 /* check for change of border element */
10740 CheckElementChangeBySide(xx, yy, border_element, center_element,
10741 CE_TOUCHING_X, center_side);
10744 for (i = 0; i < NUM_DIRECTIONS; i++)
10746 int border_side = trigger_sides[i][1];
10747 int border_element = border_element_old[i];
10749 if (border_element == -1)
10752 /* check for change of center element (but change it only once) */
10753 if (!change_center_element)
10754 change_center_element =
10755 CheckElementChangeBySide(x, y, center_element, border_element,
10756 CE_TOUCHING_X, border_side);
10762 void TestIfElementTouchesCustomElement_OLD(int x, int y)
10764 static int xy[4][2] =
10771 static int trigger_sides[4][2] =
10773 /* center side border side */
10774 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
10775 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
10776 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
10777 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
10779 static int touch_dir[4] =
10781 MV_LEFT | MV_RIGHT,
10786 boolean change_center_element = FALSE;
10787 int center_element = Feld[x][y]; /* should always be non-moving! */
10790 for (i = 0; i < NUM_DIRECTIONS; i++)
10792 int xx = x + xy[i][0];
10793 int yy = y + xy[i][1];
10794 int center_side = trigger_sides[i][0];
10795 int border_side = trigger_sides[i][1];
10796 int border_element;
10798 if (!IN_LEV_FIELD(xx, yy))
10801 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10802 border_element = Feld[xx][yy]; /* may be moving! */
10803 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
10804 border_element = Feld[xx][yy];
10805 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
10806 border_element = MovingOrBlocked2Element(xx, yy);
10808 continue; /* center and border element do not touch */
10810 /* check for change of center element (but change it only once) */
10811 if (!change_center_element)
10812 change_center_element =
10813 CheckElementChangeBySide(x, y, center_element, border_element,
10814 CE_TOUCHING_X, border_side);
10816 /* check for change of border element */
10817 CheckElementChangeBySide(xx, yy, border_element, center_element,
10818 CE_TOUCHING_X, center_side);
10824 void TestIfElementHitsCustomElement(int x, int y, int direction)
10826 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
10827 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
10828 int hitx = x + dx, hity = y + dy;
10829 int hitting_element = Feld[x][y];
10830 int touched_element;
10832 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
10835 touched_element = (IN_LEV_FIELD(hitx, hity) ?
10836 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
10838 if (IN_LEV_FIELD(hitx, hity))
10840 int opposite_direction = MV_DIR_OPPOSITE(direction);
10841 int hitting_side = direction;
10842 int touched_side = opposite_direction;
10843 boolean object_hit = (!IS_MOVING(hitx, hity) ||
10844 MovDir[hitx][hity] != direction ||
10845 ABS(MovPos[hitx][hity]) <= TILEY / 2);
10851 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10852 CE_HITTING_X, touched_side);
10854 CheckElementChangeBySide(hitx, hity, touched_element,
10855 hitting_element, CE_HIT_BY_X, hitting_side);
10857 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10858 CE_HIT_BY_SOMETHING, opposite_direction);
10862 /* "hitting something" is also true when hitting the playfield border */
10863 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10864 CE_HITTING_SOMETHING, direction);
10868 void TestIfElementSmashesCustomElement(int x, int y, int direction)
10870 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
10871 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
10872 int hitx = x + dx, hity = y + dy;
10873 int hitting_element = Feld[x][y];
10874 int touched_element;
10876 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
10877 !IS_FREE(hitx, hity) &&
10878 (!IS_MOVING(hitx, hity) ||
10879 MovDir[hitx][hity] != direction ||
10880 ABS(MovPos[hitx][hity]) <= TILEY / 2));
10883 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
10887 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
10891 touched_element = (IN_LEV_FIELD(hitx, hity) ?
10892 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
10894 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10895 EP_CAN_SMASH_EVERYTHING, direction);
10897 if (IN_LEV_FIELD(hitx, hity))
10899 int opposite_direction = MV_DIR_OPPOSITE(direction);
10900 int hitting_side = direction;
10901 int touched_side = opposite_direction;
10903 int touched_element = MovingOrBlocked2Element(hitx, hity);
10906 boolean object_hit = (!IS_MOVING(hitx, hity) ||
10907 MovDir[hitx][hity] != direction ||
10908 ABS(MovPos[hitx][hity]) <= TILEY / 2);
10917 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10918 CE_SMASHED_BY_SOMETHING, opposite_direction);
10920 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10921 CE_OTHER_IS_SMASHING, touched_side);
10923 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10924 CE_OTHER_GETS_SMASHED, hitting_side);
10930 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
10932 int i, kill_x = -1, kill_y = -1;
10934 int bad_element = -1;
10935 static int test_xy[4][2] =
10942 static int test_dir[4] =
10950 for (i = 0; i < NUM_DIRECTIONS; i++)
10952 int test_x, test_y, test_move_dir, test_element;
10954 test_x = good_x + test_xy[i][0];
10955 test_y = good_y + test_xy[i][1];
10957 if (!IN_LEV_FIELD(test_x, test_y))
10961 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
10963 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
10965 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
10966 2nd case: DONT_TOUCH style bad thing does not move away from good thing
10968 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
10969 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
10973 bad_element = test_element;
10979 if (kill_x != -1 || kill_y != -1)
10981 if (IS_PLAYER(good_x, good_y))
10983 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
10985 if (player->shield_deadly_time_left > 0 &&
10986 !IS_INDESTRUCTIBLE(bad_element))
10987 Bang(kill_x, kill_y);
10988 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
10989 KillPlayer(player);
10992 Bang(good_x, good_y);
10996 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
10998 int i, kill_x = -1, kill_y = -1;
10999 int bad_element = Feld[bad_x][bad_y];
11000 static int test_xy[4][2] =
11007 static int touch_dir[4] =
11009 MV_LEFT | MV_RIGHT,
11014 static int test_dir[4] =
11022 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
11025 for (i = 0; i < NUM_DIRECTIONS; i++)
11027 int test_x, test_y, test_move_dir, test_element;
11029 test_x = bad_x + test_xy[i][0];
11030 test_y = bad_y + test_xy[i][1];
11031 if (!IN_LEV_FIELD(test_x, test_y))
11035 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
11037 test_element = Feld[test_x][test_y];
11039 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
11040 2nd case: DONT_TOUCH style bad thing does not move away from good thing
11042 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
11043 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
11045 /* good thing is player or penguin that does not move away */
11046 if (IS_PLAYER(test_x, test_y))
11048 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
11050 if (bad_element == EL_ROBOT && player->is_moving)
11051 continue; /* robot does not kill player if he is moving */
11053 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
11055 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
11056 continue; /* center and border element do not touch */
11063 else if (test_element == EL_PENGUIN)
11072 if (kill_x != -1 || kill_y != -1)
11074 if (IS_PLAYER(kill_x, kill_y))
11076 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
11078 if (player->shield_deadly_time_left > 0 &&
11079 !IS_INDESTRUCTIBLE(bad_element))
11080 Bang(bad_x, bad_y);
11081 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
11082 KillPlayer(player);
11085 Bang(kill_x, kill_y);
11089 void TestIfPlayerTouchesBadThing(int x, int y)
11091 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
11094 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
11096 TestIfGoodThingHitsBadThing(x, y, move_dir);
11099 void TestIfBadThingTouchesPlayer(int x, int y)
11101 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
11104 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
11106 TestIfBadThingHitsGoodThing(x, y, move_dir);
11109 void TestIfFriendTouchesBadThing(int x, int y)
11111 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
11114 void TestIfBadThingTouchesFriend(int x, int y)
11116 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
11119 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
11121 int i, kill_x = bad_x, kill_y = bad_y;
11122 static int xy[4][2] =
11130 for (i = 0; i < NUM_DIRECTIONS; i++)
11134 x = bad_x + xy[i][0];
11135 y = bad_y + xy[i][1];
11136 if (!IN_LEV_FIELD(x, y))
11139 element = Feld[x][y];
11140 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
11141 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
11149 if (kill_x != bad_x || kill_y != bad_y)
11150 Bang(bad_x, bad_y);
11153 void KillPlayer(struct PlayerInfo *player)
11155 int jx = player->jx, jy = player->jy;
11157 if (!player->active)
11160 /* the following code was introduced to prevent an infinite loop when calling
11162 -> CheckTriggeredElementChangeExt()
11163 -> ExecuteCustomElementAction()
11165 -> (infinitely repeating the above sequence of function calls)
11166 which occurs when killing the player while having a CE with the setting
11167 "kill player X when explosion of <player X>"; the solution using a new
11168 field "player->killed" was chosen for backwards compatibility, although
11169 clever use of the fields "player->active" etc. would probably also work */
11171 if (player->killed)
11175 player->killed = TRUE;
11177 /* remove accessible field at the player's position */
11178 Feld[jx][jy] = EL_EMPTY;
11180 /* deactivate shield (else Bang()/Explode() would not work right) */
11181 player->shield_normal_time_left = 0;
11182 player->shield_deadly_time_left = 0;
11185 BuryPlayer(player);
11188 static void KillPlayerUnlessEnemyProtected(int x, int y)
11190 if (!PLAYER_ENEMY_PROTECTED(x, y))
11191 KillPlayer(PLAYERINFO(x, y));
11194 static void KillPlayerUnlessExplosionProtected(int x, int y)
11196 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
11197 KillPlayer(PLAYERINFO(x, y));
11200 void BuryPlayer(struct PlayerInfo *player)
11202 int jx = player->jx, jy = player->jy;
11204 if (!player->active)
11207 PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
11208 PlayLevelSound(jx, jy, SND_GAME_LOSING);
11210 player->GameOver = TRUE;
11211 RemovePlayer(player);
11214 void RemovePlayer(struct PlayerInfo *player)
11216 int jx = player->jx, jy = player->jy;
11217 int i, found = FALSE;
11219 player->present = FALSE;
11220 player->active = FALSE;
11222 if (!ExplodeField[jx][jy])
11223 StorePlayer[jx][jy] = 0;
11225 if (player->is_moving)
11226 DrawLevelField(player->last_jx, player->last_jy);
11228 for (i = 0; i < MAX_PLAYERS; i++)
11229 if (stored_player[i].active)
11233 AllPlayersGone = TRUE;
11239 #if USE_NEW_SNAP_DELAY
11240 static void setFieldForSnapping(int x, int y, int element, int direction)
11242 struct ElementInfo *ei = &element_info[element];
11243 int direction_bit = MV_DIR_TO_BIT(direction);
11244 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
11245 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
11246 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
11248 Feld[x][y] = EL_ELEMENT_SNAPPING;
11249 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
11251 ResetGfxAnimation(x, y);
11253 GfxElement[x][y] = element;
11254 GfxAction[x][y] = action;
11255 GfxDir[x][y] = direction;
11256 GfxFrame[x][y] = -1;
11261 =============================================================================
11262 checkDiagonalPushing()
11263 -----------------------------------------------------------------------------
11264 check if diagonal input device direction results in pushing of object
11265 (by checking if the alternative direction is walkable, diggable, ...)
11266 =============================================================================
11269 static boolean checkDiagonalPushing(struct PlayerInfo *player,
11270 int x, int y, int real_dx, int real_dy)
11272 int jx, jy, dx, dy, xx, yy;
11274 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
11277 /* diagonal direction: check alternative direction */
11282 xx = jx + (dx == 0 ? real_dx : 0);
11283 yy = jy + (dy == 0 ? real_dy : 0);
11285 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
11289 =============================================================================
11291 -----------------------------------------------------------------------------
11292 x, y: field next to player (non-diagonal) to try to dig to
11293 real_dx, real_dy: direction as read from input device (can be diagonal)
11294 =============================================================================
11297 int DigField(struct PlayerInfo *player,
11298 int oldx, int oldy, int x, int y,
11299 int real_dx, int real_dy, int mode)
11301 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
11302 boolean player_was_pushing = player->is_pushing;
11303 boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
11304 boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
11305 int jx = oldx, jy = oldy;
11306 int dx = x - jx, dy = y - jy;
11307 int nextx = x + dx, nexty = y + dy;
11308 int move_direction = (dx == -1 ? MV_LEFT :
11309 dx == +1 ? MV_RIGHT :
11311 dy == +1 ? MV_DOWN : MV_NONE);
11312 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
11313 int dig_side = MV_DIR_OPPOSITE(move_direction);
11314 int old_element = Feld[jx][jy];
11315 #if USE_FIXED_DONT_RUN_INTO
11316 int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
11322 if (is_player) /* function can also be called by EL_PENGUIN */
11324 if (player->MovPos == 0)
11326 player->is_digging = FALSE;
11327 player->is_collecting = FALSE;
11330 if (player->MovPos == 0) /* last pushing move finished */
11331 player->is_pushing = FALSE;
11333 if (mode == DF_NO_PUSH) /* player just stopped pushing */
11335 player->is_switching = FALSE;
11336 player->push_delay = -1;
11338 return MP_NO_ACTION;
11342 #if !USE_FIXED_DONT_RUN_INTO
11343 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
11344 return MP_NO_ACTION;
11347 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
11348 old_element = Back[jx][jy];
11350 /* in case of element dropped at player position, check background */
11351 else if (Back[jx][jy] != EL_EMPTY &&
11352 game.engine_version >= VERSION_IDENT(2,2,0,0))
11353 old_element = Back[jx][jy];
11355 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
11356 return MP_NO_ACTION; /* field has no opening in this direction */
11358 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
11359 return MP_NO_ACTION; /* field has no opening in this direction */
11361 #if USE_FIXED_DONT_RUN_INTO
11362 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
11366 Feld[jx][jy] = player->artwork_element;
11367 InitMovingField(jx, jy, MV_DOWN);
11368 Store[jx][jy] = EL_ACID;
11369 ContinueMoving(jx, jy);
11370 BuryPlayer(player);
11372 return MP_DONT_RUN_INTO;
11376 #if USE_FIXED_DONT_RUN_INTO
11377 if (player_can_move && DONT_RUN_INTO(element))
11379 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
11381 return MP_DONT_RUN_INTO;
11385 #if USE_FIXED_DONT_RUN_INTO
11386 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
11387 return MP_NO_ACTION;
11390 #if !USE_FIXED_DONT_RUN_INTO
11391 element = Feld[x][y];
11394 collect_count = element_info[element].collect_count_initial;
11396 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
11397 return MP_NO_ACTION;
11399 if (game.engine_version < VERSION_IDENT(2,2,0,0))
11400 player_can_move = player_can_move_or_snap;
11402 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
11403 game.engine_version >= VERSION_IDENT(2,2,0,0))
11405 CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
11406 player->index_bit, dig_side);
11407 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
11408 player->index_bit, dig_side);
11410 if (Feld[x][y] != element) /* field changed by snapping */
11413 return MP_NO_ACTION;
11416 #if USE_PLAYER_GRAVITY
11417 if (player->gravity && is_player && !player->is_auto_moving &&
11418 canFallDown(player) && move_direction != MV_DOWN &&
11419 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
11420 return MP_NO_ACTION; /* player cannot walk here due to gravity */
11422 if (game.gravity && is_player && !player->is_auto_moving &&
11423 canFallDown(player) && move_direction != MV_DOWN &&
11424 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
11425 return MP_NO_ACTION; /* player cannot walk here due to gravity */
11428 if (player_can_move &&
11429 IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
11431 int sound_element = SND_ELEMENT(element);
11432 int sound_action = ACTION_WALKING;
11434 if (IS_RND_GATE(element))
11436 if (!player->key[RND_GATE_NR(element)])
11437 return MP_NO_ACTION;
11439 else if (IS_RND_GATE_GRAY(element))
11441 if (!player->key[RND_GATE_GRAY_NR(element)])
11442 return MP_NO_ACTION;
11444 else if (IS_RND_GATE_GRAY_ACTIVE(element))
11446 if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
11447 return MP_NO_ACTION;
11449 else if (element == EL_EXIT_OPEN ||
11450 element == EL_SP_EXIT_OPEN ||
11451 element == EL_SP_EXIT_OPENING)
11453 sound_action = ACTION_PASSING; /* player is passing exit */
11455 else if (element == EL_EMPTY)
11457 sound_action = ACTION_MOVING; /* nothing to walk on */
11460 /* play sound from background or player, whatever is available */
11461 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
11462 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
11464 PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
11466 else if (player_can_move &&
11467 IS_PASSABLE(element) && canPassField(x, y, move_direction))
11469 if (!ACCESS_FROM(element, opposite_direction))
11470 return MP_NO_ACTION; /* field not accessible from this direction */
11472 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
11473 return MP_NO_ACTION;
11475 if (IS_EM_GATE(element))
11477 if (!player->key[EM_GATE_NR(element)])
11478 return MP_NO_ACTION;
11480 else if (IS_EM_GATE_GRAY(element))
11482 if (!player->key[EM_GATE_GRAY_NR(element)])
11483 return MP_NO_ACTION;
11485 else if (IS_EM_GATE_GRAY_ACTIVE(element))
11487 if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
11488 return MP_NO_ACTION;
11490 else if (IS_EMC_GATE(element))
11492 if (!player->key[EMC_GATE_NR(element)])
11493 return MP_NO_ACTION;
11495 else if (IS_EMC_GATE_GRAY(element))
11497 if (!player->key[EMC_GATE_GRAY_NR(element)])
11498 return MP_NO_ACTION;
11500 else if (IS_EMC_GATE_GRAY_ACTIVE(element))
11502 if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
11503 return MP_NO_ACTION;
11505 else if (IS_SP_PORT(element))
11507 if (element == EL_SP_GRAVITY_PORT_LEFT ||
11508 element == EL_SP_GRAVITY_PORT_RIGHT ||
11509 element == EL_SP_GRAVITY_PORT_UP ||
11510 element == EL_SP_GRAVITY_PORT_DOWN)
11511 #if USE_PLAYER_GRAVITY
11512 player->gravity = !player->gravity;
11514 game.gravity = !game.gravity;
11516 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
11517 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
11518 element == EL_SP_GRAVITY_ON_PORT_UP ||
11519 element == EL_SP_GRAVITY_ON_PORT_DOWN)
11520 #if USE_PLAYER_GRAVITY
11521 player->gravity = TRUE;
11523 game.gravity = TRUE;
11525 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
11526 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
11527 element == EL_SP_GRAVITY_OFF_PORT_UP ||
11528 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
11529 #if USE_PLAYER_GRAVITY
11530 player->gravity = FALSE;
11532 game.gravity = FALSE;
11536 /* automatically move to the next field with double speed */
11537 player->programmed_action = move_direction;
11539 if (player->move_delay_reset_counter == 0)
11541 player->move_delay_reset_counter = 2; /* two double speed steps */
11543 DOUBLE_PLAYER_SPEED(player);
11546 PlayLevelSoundAction(x, y, ACTION_PASSING);
11548 else if (player_can_move_or_snap && IS_DIGGABLE(element))
11552 if (mode != DF_SNAP)
11554 GfxElement[x][y] = GFX_ELEMENT(element);
11555 player->is_digging = TRUE;
11558 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
11560 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
11561 player->index_bit, dig_side);
11563 if (mode == DF_SNAP)
11565 #if USE_NEW_SNAP_DELAY
11566 if (level.block_snap_field)
11567 setFieldForSnapping(x, y, element, move_direction);
11569 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11571 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11574 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
11575 player->index_bit, dig_side);
11578 else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
11582 if (is_player && mode != DF_SNAP)
11584 GfxElement[x][y] = element;
11585 player->is_collecting = TRUE;
11588 if (element == EL_SPEED_PILL)
11590 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
11592 else if (element == EL_EXTRA_TIME && level.time > 0)
11594 TimeLeft += level.extra_time;
11595 DrawGameValue_Time(TimeLeft);
11597 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
11599 player->shield_normal_time_left += level.shield_normal_time;
11600 if (element == EL_SHIELD_DEADLY)
11601 player->shield_deadly_time_left += level.shield_deadly_time;
11603 else if (element == EL_DYNAMITE ||
11604 element == EL_EM_DYNAMITE ||
11605 element == EL_SP_DISK_RED)
11607 if (player->inventory_size < MAX_INVENTORY_SIZE)
11608 player->inventory_element[player->inventory_size++] = element;
11610 DrawGameDoorValues();
11612 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
11614 player->dynabomb_count++;
11615 player->dynabombs_left++;
11617 else if (element == EL_DYNABOMB_INCREASE_SIZE)
11619 player->dynabomb_size++;
11621 else if (element == EL_DYNABOMB_INCREASE_POWER)
11623 player->dynabomb_xl = TRUE;
11625 else if (IS_KEY(element))
11627 player->key[KEY_NR(element)] = TRUE;
11629 DrawGameDoorValues();
11631 else if (IS_ENVELOPE(element))
11633 player->show_envelope = element;
11635 else if (element == EL_EMC_LENSES)
11637 game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
11639 RedrawAllInvisibleElementsForLenses();
11641 else if (element == EL_EMC_MAGNIFIER)
11643 game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
11645 RedrawAllInvisibleElementsForMagnifier();
11647 else if (IS_DROPPABLE(element) ||
11648 IS_THROWABLE(element)) /* can be collected and dropped */
11652 if (collect_count == 0)
11653 player->inventory_infinite_element = element;
11655 for (i = 0; i < collect_count; i++)
11656 if (player->inventory_size < MAX_INVENTORY_SIZE)
11657 player->inventory_element[player->inventory_size++] = element;
11659 DrawGameDoorValues();
11661 else if (collect_count > 0)
11663 local_player->gems_still_needed -= collect_count;
11664 if (local_player->gems_still_needed < 0)
11665 local_player->gems_still_needed = 0;
11667 DrawGameValue_Emeralds(local_player->gems_still_needed);
11670 RaiseScoreElement(element);
11671 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
11674 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
11675 player->index_bit, dig_side);
11677 if (mode == DF_SNAP)
11679 #if USE_NEW_SNAP_DELAY
11680 if (level.block_snap_field)
11681 setFieldForSnapping(x, y, element, move_direction);
11683 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11685 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11688 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
11689 player->index_bit, dig_side);
11692 else if (player_can_move_or_snap && IS_PUSHABLE(element))
11694 if (mode == DF_SNAP && element != EL_BD_ROCK)
11695 return MP_NO_ACTION;
11697 if (CAN_FALL(element) && dy)
11698 return MP_NO_ACTION;
11700 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
11701 !(element == EL_SPRING && level.use_spring_bug))
11702 return MP_NO_ACTION;
11704 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
11705 ((move_direction & MV_VERTICAL &&
11706 ((element_info[element].move_pattern & MV_LEFT &&
11707 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
11708 (element_info[element].move_pattern & MV_RIGHT &&
11709 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
11710 (move_direction & MV_HORIZONTAL &&
11711 ((element_info[element].move_pattern & MV_UP &&
11712 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
11713 (element_info[element].move_pattern & MV_DOWN &&
11714 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
11715 return MP_NO_ACTION;
11717 /* do not push elements already moving away faster than player */
11718 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
11719 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
11720 return MP_NO_ACTION;
11722 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
11724 if (player->push_delay_value == -1 || !player_was_pushing)
11725 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11727 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11729 if (player->push_delay_value == -1)
11730 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11732 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
11734 if (!player->is_pushing)
11735 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11738 player->is_pushing = TRUE;
11739 player->is_active = TRUE;
11741 if (!(IN_LEV_FIELD(nextx, nexty) &&
11742 (IS_FREE(nextx, nexty) ||
11743 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
11744 IS_SB_ELEMENT(element)))))
11745 return MP_NO_ACTION;
11747 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
11748 return MP_NO_ACTION;
11750 if (player->push_delay == -1) /* new pushing; restart delay */
11751 player->push_delay = 0;
11753 if (player->push_delay < player->push_delay_value &&
11754 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
11755 element != EL_SPRING && element != EL_BALLOON)
11757 /* make sure that there is no move delay before next try to push */
11758 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11759 player->move_delay = 0;
11761 return MP_NO_ACTION;
11764 if (IS_SB_ELEMENT(element))
11766 if (element == EL_SOKOBAN_FIELD_FULL)
11768 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
11769 local_player->sokobanfields_still_needed++;
11772 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
11774 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
11775 local_player->sokobanfields_still_needed--;
11778 Feld[x][y] = EL_SOKOBAN_OBJECT;
11780 if (Back[x][y] == Back[nextx][nexty])
11781 PlayLevelSoundAction(x, y, ACTION_PUSHING);
11782 else if (Back[x][y] != 0)
11783 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
11786 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
11789 if (local_player->sokobanfields_still_needed == 0 &&
11790 game.emulation == EMU_SOKOBAN)
11792 PlayerWins(player);
11794 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
11798 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
11800 InitMovingField(x, y, move_direction);
11801 GfxAction[x][y] = ACTION_PUSHING;
11803 if (mode == DF_SNAP)
11804 ContinueMoving(x, y);
11806 MovPos[x][y] = (dx != 0 ? dx : dy);
11808 Pushed[x][y] = TRUE;
11809 Pushed[nextx][nexty] = TRUE;
11811 if (game.engine_version < VERSION_IDENT(2,2,0,7))
11812 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11814 player->push_delay_value = -1; /* get new value later */
11816 /* check for element change _after_ element has been pushed */
11817 if (game.use_change_when_pushing_bug)
11819 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
11820 player->index_bit, dig_side);
11821 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
11822 player->index_bit, dig_side);
11825 else if (IS_SWITCHABLE(element))
11827 if (PLAYER_SWITCHING(player, x, y))
11829 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
11830 player->index_bit, dig_side);
11835 player->is_switching = TRUE;
11836 player->switch_x = x;
11837 player->switch_y = y;
11839 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
11841 if (element == EL_ROBOT_WHEEL)
11843 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
11847 DrawLevelField(x, y);
11849 else if (element == EL_SP_TERMINAL)
11853 SCAN_PLAYFIELD(xx, yy)
11855 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
11857 else if (Feld[xx][yy] == EL_SP_TERMINAL)
11858 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
11861 else if (IS_BELT_SWITCH(element))
11863 ToggleBeltSwitch(x, y);
11865 else if (element == EL_SWITCHGATE_SWITCH_UP ||
11866 element == EL_SWITCHGATE_SWITCH_DOWN)
11868 ToggleSwitchgateSwitch(x, y);
11870 else if (element == EL_LIGHT_SWITCH ||
11871 element == EL_LIGHT_SWITCH_ACTIVE)
11873 ToggleLightSwitch(x, y);
11875 else if (element == EL_TIMEGATE_SWITCH)
11877 ActivateTimegateSwitch(x, y);
11879 else if (element == EL_BALLOON_SWITCH_LEFT ||
11880 element == EL_BALLOON_SWITCH_RIGHT ||
11881 element == EL_BALLOON_SWITCH_UP ||
11882 element == EL_BALLOON_SWITCH_DOWN ||
11883 element == EL_BALLOON_SWITCH_NONE ||
11884 element == EL_BALLOON_SWITCH_ANY)
11886 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
11887 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
11888 element == EL_BALLOON_SWITCH_UP ? MV_UP :
11889 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
11890 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
11893 else if (element == EL_LAMP)
11895 Feld[x][y] = EL_LAMP_ACTIVE;
11896 local_player->lights_still_needed--;
11898 ResetGfxAnimation(x, y);
11899 DrawLevelField(x, y);
11901 else if (element == EL_TIME_ORB_FULL)
11903 Feld[x][y] = EL_TIME_ORB_EMPTY;
11905 if (level.time > 0 || level.use_time_orb_bug)
11907 TimeLeft += level.time_orb_time;
11908 DrawGameValue_Time(TimeLeft);
11911 ResetGfxAnimation(x, y);
11912 DrawLevelField(x, y);
11914 else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
11915 element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
11919 game.ball_state = !game.ball_state;
11921 SCAN_PLAYFIELD(xx, yy)
11923 int e = Feld[xx][yy];
11925 if (game.ball_state)
11927 if (e == EL_EMC_MAGIC_BALL)
11928 CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
11929 else if (e == EL_EMC_MAGIC_BALL_SWITCH)
11930 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
11934 if (e == EL_EMC_MAGIC_BALL_ACTIVE)
11935 CreateField(xx, yy, EL_EMC_MAGIC_BALL);
11936 else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
11937 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
11942 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
11943 player->index_bit, dig_side);
11945 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
11946 player->index_bit, dig_side);
11948 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
11949 player->index_bit, dig_side);
11955 if (!PLAYER_SWITCHING(player, x, y))
11957 player->is_switching = TRUE;
11958 player->switch_x = x;
11959 player->switch_y = y;
11961 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
11962 player->index_bit, dig_side);
11963 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
11964 player->index_bit, dig_side);
11966 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
11967 player->index_bit, dig_side);
11968 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
11969 player->index_bit, dig_side);
11972 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
11973 player->index_bit, dig_side);
11974 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
11975 player->index_bit, dig_side);
11977 return MP_NO_ACTION;
11980 player->push_delay = -1;
11982 if (is_player) /* function can also be called by EL_PENGUIN */
11984 if (Feld[x][y] != element) /* really digged/collected something */
11986 player->is_collecting = !player->is_digging;
11987 player->is_active = TRUE;
11994 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
11996 int jx = player->jx, jy = player->jy;
11997 int x = jx + dx, y = jy + dy;
11998 int snap_direction = (dx == -1 ? MV_LEFT :
11999 dx == +1 ? MV_RIGHT :
12001 dy == +1 ? MV_DOWN : MV_NONE);
12002 boolean can_continue_snapping = (level.continuous_snapping &&
12003 WasJustFalling[x][y] < CHECK_DELAY_FALLING);
12005 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
12008 if (!player->active || !IN_LEV_FIELD(x, y))
12016 if (player->MovPos == 0)
12017 player->is_pushing = FALSE;
12019 player->is_snapping = FALSE;
12021 if (player->MovPos == 0)
12023 player->is_moving = FALSE;
12024 player->is_digging = FALSE;
12025 player->is_collecting = FALSE;
12031 #if USE_NEW_CONTINUOUS_SNAPPING
12032 /* prevent snapping with already pressed snap key when not allowed */
12033 if (player->is_snapping && !can_continue_snapping)
12036 if (player->is_snapping)
12040 player->MovDir = snap_direction;
12042 if (player->MovPos == 0)
12044 player->is_moving = FALSE;
12045 player->is_digging = FALSE;
12046 player->is_collecting = FALSE;
12049 player->is_dropping = FALSE;
12050 player->is_dropping_pressed = FALSE;
12051 player->drop_pressed_delay = 0;
12053 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
12056 player->is_snapping = TRUE;
12057 player->is_active = TRUE;
12059 if (player->MovPos == 0)
12061 player->is_moving = FALSE;
12062 player->is_digging = FALSE;
12063 player->is_collecting = FALSE;
12066 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
12067 DrawLevelField(player->last_jx, player->last_jy);
12069 DrawLevelField(x, y);
12074 boolean DropElement(struct PlayerInfo *player)
12076 int old_element, new_element;
12077 int dropx = player->jx, dropy = player->jy;
12078 int drop_direction = player->MovDir;
12079 int drop_side = drop_direction;
12080 int drop_element = (player->inventory_size > 0 ?
12081 player->inventory_element[player->inventory_size - 1] :
12082 player->inventory_infinite_element != EL_UNDEFINED ?
12083 player->inventory_infinite_element :
12084 player->dynabombs_left > 0 ?
12085 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
12088 player->is_dropping_pressed = TRUE;
12090 /* do not drop an element on top of another element; when holding drop key
12091 pressed without moving, dropped element must move away before the next
12092 element can be dropped (this is especially important if the next element
12093 is dynamite, which can be placed on background for historical reasons) */
12094 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
12097 if (IS_THROWABLE(drop_element))
12099 dropx += GET_DX_FROM_DIR(drop_direction);
12100 dropy += GET_DY_FROM_DIR(drop_direction);
12102 if (!IN_LEV_FIELD(dropx, dropy))
12106 old_element = Feld[dropx][dropy]; /* old element at dropping position */
12107 new_element = drop_element; /* default: no change when dropping */
12109 /* check if player is active, not moving and ready to drop */
12110 if (!player->active || player->MovPos || player->drop_delay > 0)
12113 /* check if player has anything that can be dropped */
12114 if (new_element == EL_UNDEFINED)
12117 /* check if drop key was pressed long enough for EM style dynamite */
12118 if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
12121 /* check if anything can be dropped at the current position */
12122 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
12125 /* collected custom elements can only be dropped on empty fields */
12126 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
12129 if (old_element != EL_EMPTY)
12130 Back[dropx][dropy] = old_element; /* store old element on this field */
12132 ResetGfxAnimation(dropx, dropy);
12133 ResetRandomAnimationValue(dropx, dropy);
12135 if (player->inventory_size > 0 ||
12136 player->inventory_infinite_element != EL_UNDEFINED)
12138 if (player->inventory_size > 0)
12140 player->inventory_size--;
12142 DrawGameDoorValues();
12144 if (new_element == EL_DYNAMITE)
12145 new_element = EL_DYNAMITE_ACTIVE;
12146 else if (new_element == EL_EM_DYNAMITE)
12147 new_element = EL_EM_DYNAMITE_ACTIVE;
12148 else if (new_element == EL_SP_DISK_RED)
12149 new_element = EL_SP_DISK_RED_ACTIVE;
12152 Feld[dropx][dropy] = new_element;
12154 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
12155 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
12156 el2img(Feld[dropx][dropy]), 0);
12158 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
12160 /* needed if previous element just changed to "empty" in the last frame */
12161 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
12163 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
12164 player->index_bit, drop_side);
12165 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
12167 player->index_bit, drop_side);
12169 TestIfElementTouchesCustomElement(dropx, dropy);
12171 else /* player is dropping a dyna bomb */
12173 player->dynabombs_left--;
12175 Feld[dropx][dropy] = new_element;
12177 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
12178 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
12179 el2img(Feld[dropx][dropy]), 0);
12181 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
12184 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
12185 InitField_WithBug1(dropx, dropy, FALSE);
12187 new_element = Feld[dropx][dropy]; /* element might have changed */
12189 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
12190 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
12192 int move_direction, nextx, nexty;
12194 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
12195 MovDir[dropx][dropy] = drop_direction;
12197 move_direction = MovDir[dropx][dropy];
12198 nextx = dropx + GET_DX_FROM_DIR(move_direction);
12199 nexty = dropy + GET_DY_FROM_DIR(move_direction);
12201 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
12203 #if USE_FIX_IMPACT_COLLISION
12204 /* do not cause impact style collision by dropping elements that can fall */
12205 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
12207 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
12211 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
12212 player->is_dropping = TRUE;
12214 player->drop_pressed_delay = 0;
12215 player->is_dropping_pressed = FALSE;
12217 player->drop_x = dropx;
12218 player->drop_y = dropy;
12223 /* ------------------------------------------------------------------------- */
12224 /* game sound playing functions */
12225 /* ------------------------------------------------------------------------- */
12227 static int *loop_sound_frame = NULL;
12228 static int *loop_sound_volume = NULL;
12230 void InitPlayLevelSound()
12232 int num_sounds = getSoundListSize();
12234 checked_free(loop_sound_frame);
12235 checked_free(loop_sound_volume);
12237 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
12238 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
12241 static void PlayLevelSound(int x, int y, int nr)
12243 int sx = SCREENX(x), sy = SCREENY(y);
12244 int volume, stereo_position;
12245 int max_distance = 8;
12246 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
12248 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
12249 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
12252 if (!IN_LEV_FIELD(x, y) ||
12253 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
12254 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
12257 volume = SOUND_MAX_VOLUME;
12259 if (!IN_SCR_FIELD(sx, sy))
12261 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
12262 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
12264 volume -= volume * (dx > dy ? dx : dy) / max_distance;
12267 stereo_position = (SOUND_MAX_LEFT +
12268 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
12269 (SCR_FIELDX + 2 * max_distance));
12271 if (IS_LOOP_SOUND(nr))
12273 /* This assures that quieter loop sounds do not overwrite louder ones,
12274 while restarting sound volume comparison with each new game frame. */
12276 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
12279 loop_sound_volume[nr] = volume;
12280 loop_sound_frame[nr] = FrameCounter;
12283 PlaySoundExt(nr, volume, stereo_position, type);
12286 static void PlayLevelSoundNearest(int x, int y, int sound_action)
12288 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
12289 x > LEVELX(BX2) ? LEVELX(BX2) : x,
12290 y < LEVELY(BY1) ? LEVELY(BY1) :
12291 y > LEVELY(BY2) ? LEVELY(BY2) : y,
12295 static void PlayLevelSoundAction(int x, int y, int action)
12297 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
12300 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
12302 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
12304 if (sound_effect != SND_UNDEFINED)
12305 PlayLevelSound(x, y, sound_effect);
12308 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
12311 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
12313 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12314 PlayLevelSound(x, y, sound_effect);
12317 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
12319 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
12321 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12322 PlayLevelSound(x, y, sound_effect);
12325 static void StopLevelSoundActionIfLoop(int x, int y, int action)
12327 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
12329 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12330 StopSound(sound_effect);
12333 static void PlayLevelMusic()
12335 if (levelset.music[level_nr] != MUS_UNDEFINED)
12336 PlayMusic(levelset.music[level_nr]); /* from config file */
12338 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
12341 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
12343 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
12344 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
12345 int x = xx - 1 - offset;
12346 int y = yy - 1 - offset;
12351 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
12355 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12359 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12363 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12367 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
12371 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12375 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12378 case SAMPLE_android_clone:
12379 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12382 case SAMPLE_android_move:
12383 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12386 case SAMPLE_spring:
12387 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12391 PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
12395 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
12398 case SAMPLE_eater_eat:
12399 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
12403 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12406 case SAMPLE_collect:
12407 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
12410 case SAMPLE_diamond:
12411 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12414 case SAMPLE_squash:
12415 /* !!! CHECK THIS !!! */
12417 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
12419 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
12423 case SAMPLE_wonderfall:
12424 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
12428 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12432 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12436 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
12440 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
12444 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12448 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
12451 case SAMPLE_wonder:
12452 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12456 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
12459 case SAMPLE_exit_open:
12460 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
12463 case SAMPLE_exit_leave:
12464 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
12467 case SAMPLE_dynamite:
12468 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12472 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12476 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
12480 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12484 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
12488 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
12492 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12496 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
12502 void ChangeTime(int value)
12504 int *time = (level.time == 0 ? &TimePlayed : &TimeLeft);
12508 /* EMC game engine uses value from time counter of RND game engine */
12509 level.native_em_level->lev->time = *time;
12511 DrawGameValue_Time(*time);
12514 void RaiseScore(int value)
12516 /* EMC game engine and RND game engine have separate score counters */
12517 int *score = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
12518 &level.native_em_level->lev->score : &local_player->score);
12522 DrawGameValue_Score(*score);
12526 void RaiseScore(int value)
12528 local_player->score += value;
12530 DrawGameValue_Score(local_player->score);
12533 void RaiseScoreElement(int element)
12538 case EL_BD_DIAMOND:
12539 case EL_EMERALD_YELLOW:
12540 case EL_EMERALD_RED:
12541 case EL_EMERALD_PURPLE:
12542 case EL_SP_INFOTRON:
12543 RaiseScore(level.score[SC_EMERALD]);
12546 RaiseScore(level.score[SC_DIAMOND]);
12549 RaiseScore(level.score[SC_CRYSTAL]);
12552 RaiseScore(level.score[SC_PEARL]);
12555 case EL_BD_BUTTERFLY:
12556 case EL_SP_ELECTRON:
12557 RaiseScore(level.score[SC_BUG]);
12560 case EL_BD_FIREFLY:
12561 case EL_SP_SNIKSNAK:
12562 RaiseScore(level.score[SC_SPACESHIP]);
12565 case EL_DARK_YAMYAM:
12566 RaiseScore(level.score[SC_YAMYAM]);
12569 RaiseScore(level.score[SC_ROBOT]);
12572 RaiseScore(level.score[SC_PACMAN]);
12575 RaiseScore(level.score[SC_NUT]);
12578 case EL_EM_DYNAMITE:
12579 case EL_SP_DISK_RED:
12580 case EL_DYNABOMB_INCREASE_NUMBER:
12581 case EL_DYNABOMB_INCREASE_SIZE:
12582 case EL_DYNABOMB_INCREASE_POWER:
12583 RaiseScore(level.score[SC_DYNAMITE]);
12585 case EL_SHIELD_NORMAL:
12586 case EL_SHIELD_DEADLY:
12587 RaiseScore(level.score[SC_SHIELD]);
12589 case EL_EXTRA_TIME:
12590 RaiseScore(level.extra_time_score);
12604 RaiseScore(level.score[SC_KEY]);
12607 RaiseScore(element_info[element].collect_score);
12612 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
12614 if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
12616 #if defined(NETWORK_AVALIABLE)
12617 if (options.network)
12618 SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
12624 game_status = GAME_MODE_MAIN;
12630 FadeOut(REDRAW_FIELD);
12632 game_status = GAME_MODE_MAIN;
12634 DrawAndFadeInMainMenu(REDRAW_FIELD);
12638 else /* continue playing the game */
12640 if (tape.playing && tape.deactivate_display)
12641 TapeDeactivateDisplayOff(TRUE);
12643 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
12645 if (tape.playing && tape.deactivate_display)
12646 TapeDeactivateDisplayOn();
12650 void RequestQuitGame(boolean ask_if_really_quit)
12652 boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
12653 boolean skip_request = AllPlayersGone || quick_quit;
12655 RequestQuitGameExt(skip_request, quick_quit,
12656 "Do you really want to quit the game ?");
12660 /* ------------------------------------------------------------------------- */
12661 /* random generator functions */
12662 /* ------------------------------------------------------------------------- */
12664 unsigned int InitEngineRandom_RND(long seed)
12666 game.num_random_calls = 0;
12669 unsigned int rnd_seed = InitEngineRandom(seed);
12671 printf("::: START RND: %d\n", rnd_seed);
12676 return InitEngineRandom(seed);
12682 unsigned int RND(int max)
12686 game.num_random_calls++;
12688 return GetEngineRandom(max);
12695 /* ------------------------------------------------------------------------- */
12696 /* game engine snapshot handling functions */
12697 /* ------------------------------------------------------------------------- */
12699 #define ARGS_ADDRESS_AND_SIZEOF(x) (&(x)), (sizeof(x))
12701 struct EngineSnapshotInfo
12703 /* runtime values for custom element collect score */
12704 int collect_score[NUM_CUSTOM_ELEMENTS];
12706 /* runtime values for group element choice position */
12707 int choice_pos[NUM_GROUP_ELEMENTS];
12709 /* runtime values for belt position animations */
12710 int belt_graphic[4 * NUM_BELT_PARTS];
12711 int belt_anim_mode[4 * NUM_BELT_PARTS];
12714 struct EngineSnapshotNodeInfo
12721 static struct EngineSnapshotInfo engine_snapshot_rnd;
12722 static ListNode *engine_snapshot_list = NULL;
12723 static char *snapshot_level_identifier = NULL;
12724 static int snapshot_level_nr = -1;
12726 void FreeEngineSnapshot()
12728 while (engine_snapshot_list != NULL)
12729 deleteNodeFromList(&engine_snapshot_list, engine_snapshot_list->key,
12732 setString(&snapshot_level_identifier, NULL);
12733 snapshot_level_nr = -1;
12736 static void SaveEngineSnapshotValues_RND()
12738 static int belt_base_active_element[4] =
12740 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
12741 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
12742 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
12743 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
12747 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
12749 int element = EL_CUSTOM_START + i;
12751 engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
12754 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
12756 int element = EL_GROUP_START + i;
12758 engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
12761 for (i = 0; i < 4; i++)
12763 for (j = 0; j < NUM_BELT_PARTS; j++)
12765 int element = belt_base_active_element[i] + j;
12766 int graphic = el2img(element);
12767 int anim_mode = graphic_info[graphic].anim_mode;
12769 engine_snapshot_rnd.belt_graphic[i * 4 + j] = graphic;
12770 engine_snapshot_rnd.belt_anim_mode[i * 4 + j] = anim_mode;
12775 static void LoadEngineSnapshotValues_RND()
12777 unsigned long num_random_calls = game.num_random_calls;
12780 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
12782 int element = EL_CUSTOM_START + i;
12784 element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
12787 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
12789 int element = EL_GROUP_START + i;
12791 element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
12794 for (i = 0; i < 4; i++)
12796 for (j = 0; j < NUM_BELT_PARTS; j++)
12798 int graphic = engine_snapshot_rnd.belt_graphic[i * 4 + j];
12799 int anim_mode = engine_snapshot_rnd.belt_anim_mode[i * 4 + j];
12801 graphic_info[graphic].anim_mode = anim_mode;
12805 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
12807 InitRND(tape.random_seed);
12808 for (i = 0; i < num_random_calls; i++)
12812 if (game.num_random_calls != num_random_calls)
12814 Error(ERR_RETURN, "number of random calls out of sync");
12815 Error(ERR_RETURN, "number of random calls should be %d", num_random_calls);
12816 Error(ERR_RETURN, "number of random calls is %d", game.num_random_calls);
12817 Error(ERR_EXIT, "this should not happen -- please debug");
12821 static void SaveEngineSnapshotBuffer(void *buffer, int size)
12823 struct EngineSnapshotNodeInfo *bi =
12824 checked_calloc(sizeof(struct EngineSnapshotNodeInfo));
12826 bi->buffer_orig = buffer;
12827 bi->buffer_copy = checked_malloc(size);
12830 memcpy(bi->buffer_copy, buffer, size);
12832 addNodeToList(&engine_snapshot_list, NULL, bi);
12835 void SaveEngineSnapshot()
12837 FreeEngineSnapshot(); /* free previous snapshot, if needed */
12839 if (level_editor_test_game) /* do not save snapshots from editor */
12842 /* copy some special values to a structure better suited for the snapshot */
12844 SaveEngineSnapshotValues_RND();
12845 SaveEngineSnapshotValues_EM();
12847 /* save values stored in special snapshot structure */
12849 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
12850 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
12852 /* save further RND engine values */
12854 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
12855 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
12856 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
12858 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
12859 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
12860 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
12861 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
12863 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
12864 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
12865 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
12866 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
12867 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
12869 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
12870 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
12871 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
12873 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
12875 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
12877 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
12878 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
12880 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
12881 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
12882 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
12883 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
12884 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
12885 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
12886 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
12887 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
12888 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
12889 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
12890 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
12891 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
12892 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
12893 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
12894 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
12895 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
12896 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
12897 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
12899 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
12900 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
12902 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
12903 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
12904 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
12906 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
12907 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
12909 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
12910 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
12911 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
12912 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
12913 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
12915 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
12916 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
12918 /* save level identification information */
12920 setString(&snapshot_level_identifier, leveldir_current->identifier);
12921 snapshot_level_nr = level_nr;
12924 ListNode *node = engine_snapshot_list;
12927 while (node != NULL)
12929 num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
12934 printf("::: size of engine snapshot: %d bytes\n", num_bytes);
12938 static void LoadEngineSnapshotBuffer(struct EngineSnapshotNodeInfo *bi)
12940 memcpy(bi->buffer_orig, bi->buffer_copy, bi->size);
12943 void LoadEngineSnapshot()
12945 ListNode *node = engine_snapshot_list;
12947 if (engine_snapshot_list == NULL)
12950 while (node != NULL)
12952 LoadEngineSnapshotBuffer((struct EngineSnapshotNodeInfo *)node->content);
12957 /* restore special values from snapshot structure */
12959 LoadEngineSnapshotValues_RND();
12960 LoadEngineSnapshotValues_EM();
12963 boolean CheckEngineSnapshot()
12965 return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
12966 snapshot_level_nr == level_nr);
12970 /* ---------- new game button stuff ---------------------------------------- */
12972 /* graphic position values for game buttons */
12973 #define GAME_BUTTON_XSIZE 30
12974 #define GAME_BUTTON_YSIZE 30
12975 #define GAME_BUTTON_XPOS 5
12976 #define GAME_BUTTON_YPOS 215
12977 #define SOUND_BUTTON_XPOS 5
12978 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
12980 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
12981 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
12982 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
12983 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
12984 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
12985 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
12992 } gamebutton_info[NUM_GAME_BUTTONS] =
12995 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
13000 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
13001 GAME_CTRL_ID_PAUSE,
13005 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
13010 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
13011 SOUND_CTRL_ID_MUSIC,
13012 "background music on/off"
13015 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
13016 SOUND_CTRL_ID_LOOPS,
13017 "sound loops on/off"
13020 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
13021 SOUND_CTRL_ID_SIMPLE,
13022 "normal sounds on/off"
13026 void CreateGameButtons()
13030 for (i = 0; i < NUM_GAME_BUTTONS; i++)
13032 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
13033 struct GadgetInfo *gi;
13036 unsigned long event_mask;
13037 int gd_xoffset, gd_yoffset;
13038 int gd_x1, gd_x2, gd_y1, gd_y2;
13041 gd_xoffset = gamebutton_info[i].x;
13042 gd_yoffset = gamebutton_info[i].y;
13043 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
13044 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
13046 if (id == GAME_CTRL_ID_STOP ||
13047 id == GAME_CTRL_ID_PAUSE ||
13048 id == GAME_CTRL_ID_PLAY)
13050 button_type = GD_TYPE_NORMAL_BUTTON;
13052 event_mask = GD_EVENT_RELEASED;
13053 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
13054 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
13058 button_type = GD_TYPE_CHECK_BUTTON;
13060 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
13061 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
13062 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
13063 event_mask = GD_EVENT_PRESSED;
13064 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
13065 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
13068 gi = CreateGadget(GDI_CUSTOM_ID, id,
13069 GDI_INFO_TEXT, gamebutton_info[i].infotext,
13070 GDI_X, DX + gd_xoffset,
13071 GDI_Y, DY + gd_yoffset,
13072 GDI_WIDTH, GAME_BUTTON_XSIZE,
13073 GDI_HEIGHT, GAME_BUTTON_YSIZE,
13074 GDI_TYPE, button_type,
13075 GDI_STATE, GD_BUTTON_UNPRESSED,
13076 GDI_CHECKED, checked,
13077 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
13078 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
13079 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
13080 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
13081 GDI_EVENT_MASK, event_mask,
13082 GDI_CALLBACK_ACTION, HandleGameButtons,
13086 Error(ERR_EXIT, "cannot create gadget");
13088 game_gadget[id] = gi;
13092 void FreeGameButtons()
13096 for (i = 0; i < NUM_GAME_BUTTONS; i++)
13097 FreeGadget(game_gadget[i]);
13100 static void MapGameButtons()
13104 for (i = 0; i < NUM_GAME_BUTTONS; i++)
13105 MapGadget(game_gadget[i]);
13108 void UnmapGameButtons()
13112 for (i = 0; i < NUM_GAME_BUTTONS; i++)
13113 UnmapGadget(game_gadget[i]);
13116 static void HandleGameButtons(struct GadgetInfo *gi)
13118 int id = gi->custom_id;
13120 if (game_status != GAME_MODE_PLAYING)
13125 case GAME_CTRL_ID_STOP:
13129 RequestQuitGame(TRUE);
13132 case GAME_CTRL_ID_PAUSE:
13133 if (options.network)
13135 #if defined(NETWORK_AVALIABLE)
13137 SendToServer_ContinuePlaying();
13139 SendToServer_PausePlaying();
13143 TapeTogglePause(TAPE_TOGGLE_MANUAL);
13146 case GAME_CTRL_ID_PLAY:
13149 #if defined(NETWORK_AVALIABLE)
13150 if (options.network)
13151 SendToServer_ContinuePlaying();
13155 tape.pausing = FALSE;
13156 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
13161 case SOUND_CTRL_ID_MUSIC:
13162 if (setup.sound_music)
13164 setup.sound_music = FALSE;
13167 else if (audio.music_available)
13169 setup.sound = setup.sound_music = TRUE;
13171 SetAudioMode(setup.sound);
13177 case SOUND_CTRL_ID_LOOPS:
13178 if (setup.sound_loops)
13179 setup.sound_loops = FALSE;
13180 else if (audio.loops_available)
13182 setup.sound = setup.sound_loops = TRUE;
13183 SetAudioMode(setup.sound);
13187 case SOUND_CTRL_ID_SIMPLE:
13188 if (setup.sound_simple)
13189 setup.sound_simple = FALSE;
13190 else if (audio.sound_available)
13192 setup.sound = setup.sound_simple = TRUE;
13193 SetAudioMode(setup.sound);