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)
89 #define PANEL_XPOS(p) (ALIGNED_XPOS((p).x, (p).width, (p).align))
90 #define PANEL_YPOS(p) ((p).y)
92 /* special positions in the game control window (relative to control window) */
93 #define XX_LEVEL1 (PANEL_XPOS(game.panel.level))
94 #define XX_LEVEL2 (PANEL_XPOS(game.panel.level) - 1)
95 #define XX_LEVEL (PANEL_XPOS(game.panel.level))
96 #define YY_LEVEL (PANEL_YPOS(game.panel.level))
97 #define XX_EMERALDS (PANEL_XPOS(game.panel.gems))
98 #define YY_EMERALDS (PANEL_YPOS(game.panel.gems))
99 #define XX_DYNAMITE (PANEL_XPOS(game.panel.inventory))
100 #define YY_DYNAMITE (PANEL_YPOS(game.panel.inventory))
101 #define XX_KEYS (PANEL_XPOS(game.panel.keys))
102 #define YY_KEYS (PANEL_YPOS(game.panel.keys))
103 #define XX_SCORE (PANEL_XPOS(game.panel.score))
104 #define YY_SCORE (PANEL_YPOS(game.panel.score))
105 #define XX_TIME1 (PANEL_XPOS(game.panel.time))
106 #define XX_TIME2 (PANEL_XPOS(game.panel.time) + 1)
107 #define XX_TIME (PANEL_XPOS(game.panel.time))
108 #define YY_TIME (PANEL_YPOS(game.panel.time))
110 /* special positions in the game control window (relative to main window) */
111 #define DX_LEVEL1 (DX + XX_LEVEL1)
112 #define DX_LEVEL2 (DX + XX_LEVEL2)
113 #define DX_LEVEL (DX + XX_LEVEL)
114 #define DY_LEVEL (DY + YY_LEVEL)
115 #define DX_EMERALDS (DX + XX_EMERALDS)
116 #define DY_EMERALDS (DY + YY_EMERALDS)
117 #define DX_DYNAMITE (DX + XX_DYNAMITE)
118 #define DY_DYNAMITE (DY + YY_DYNAMITE)
119 #define DX_KEYS (DX + XX_KEYS)
120 #define DY_KEYS (DY + YY_KEYS)
121 #define DX_SCORE (DX + XX_SCORE)
122 #define DY_SCORE (DY + YY_SCORE)
123 #define DX_TIME1 (DX + XX_TIME1)
124 #define DX_TIME2 (DX + XX_TIME2)
125 #define DX_TIME (DX + XX_TIME)
126 #define DY_TIME (DY + YY_TIME)
128 /* values for delayed check of falling and moving elements and for collision */
129 #define CHECK_DELAY_MOVING 3
130 #define CHECK_DELAY_FALLING CHECK_DELAY_MOVING
131 #define CHECK_DELAY_COLLISION 2
132 #define CHECK_DELAY_IMPACT CHECK_DELAY_COLLISION
134 /* values for initial player move delay (initial delay counter value) */
135 #define INITIAL_MOVE_DELAY_OFF -1
136 #define INITIAL_MOVE_DELAY_ON 0
138 /* values for player movement speed (which is in fact a delay value) */
139 #define MOVE_DELAY_MIN_SPEED 32
140 #define MOVE_DELAY_NORMAL_SPEED 8
141 #define MOVE_DELAY_HIGH_SPEED 4
142 #define MOVE_DELAY_MAX_SPEED 1
144 #define DOUBLE_MOVE_DELAY(x) (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
145 #define HALVE_MOVE_DELAY(x) (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
147 #define DOUBLE_PLAYER_SPEED(p) (HALVE_MOVE_DELAY( (p)->move_delay_value))
148 #define HALVE_PLAYER_SPEED(p) (DOUBLE_MOVE_DELAY((p)->move_delay_value))
150 /* values for other actions */
151 #define MOVE_STEPSIZE_NORMAL (TILEX / MOVE_DELAY_NORMAL_SPEED)
152 #define MOVE_STEPSIZE_MIN (1)
153 #define MOVE_STEPSIZE_MAX (TILEX)
155 #define GET_DX_FROM_DIR(d) ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
156 #define GET_DY_FROM_DIR(d) ((d) == MV_UP ? -1 : (d) == MV_DOWN ? 1 : 0)
158 #define INIT_GFX_RANDOM() (GetSimpleRandom(1000000))
160 #define GET_NEW_PUSH_DELAY(e) ( (element_info[e].push_delay_fixed) + \
161 RND(element_info[e].push_delay_random))
162 #define GET_NEW_DROP_DELAY(e) ( (element_info[e].drop_delay_fixed) + \
163 RND(element_info[e].drop_delay_random))
164 #define GET_NEW_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
165 RND(element_info[e].move_delay_random))
166 #define GET_MAX_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
167 (element_info[e].move_delay_random))
168 #define GET_NEW_CE_VALUE(e) ( (element_info[e].ce_value_fixed_initial) +\
169 RND(element_info[e].ce_value_random_initial))
170 #define GET_CE_SCORE(e) ( (element_info[e].collect_score))
171 #define GET_CHANGE_DELAY(c) ( ((c)->delay_fixed * (c)->delay_frames) + \
172 RND((c)->delay_random * (c)->delay_frames))
173 #define GET_CE_DELAY_VALUE(c) ( ((c)->delay_fixed) + \
174 RND((c)->delay_random))
177 #define GET_VALID_RUNTIME_ELEMENT(e) \
178 ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
180 #define RESOLVED_REFERENCE_ELEMENT(be, e) \
181 ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START : \
182 (be) + (e) - EL_SELF > EL_CUSTOM_END ? EL_CUSTOM_END : \
183 (be) + (e) - EL_SELF)
185 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs) \
186 ((e) == EL_TRIGGER_PLAYER ? (ch)->actual_trigger_player : \
187 (e) == EL_TRIGGER_ELEMENT ? (ch)->actual_trigger_element : \
188 (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value : \
189 (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score : \
190 (e) == EL_CURRENT_CE_VALUE ? (cv) : \
191 (e) == EL_CURRENT_CE_SCORE ? (cs) : \
192 (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ? \
193 RESOLVED_REFERENCE_ELEMENT(be, e) : \
196 #define CAN_GROW_INTO(e) \
197 ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
199 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition) \
200 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
203 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition) \
204 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
205 (CAN_MOVE_INTO_ACID(e) && \
206 Feld[x][y] == EL_ACID) || \
209 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition) \
210 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
211 (CAN_MOVE_INTO_ACID(e) && \
212 Feld[x][y] == EL_ACID) || \
215 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition) \
216 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
218 (CAN_MOVE_INTO_ACID(e) && \
219 Feld[x][y] == EL_ACID) || \
220 (DONT_COLLIDE_WITH(e) && \
222 !PLAYER_ENEMY_PROTECTED(x, y))))
224 #define ELEMENT_CAN_ENTER_FIELD(e, x, y) \
225 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
227 #define SATELLITE_CAN_ENTER_FIELD(x, y) \
228 ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
230 #define ANDROID_CAN_ENTER_FIELD(e, x, y) \
231 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
233 #define ANDROID_CAN_CLONE_FIELD(x, y) \
234 (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
235 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
237 #define ENEMY_CAN_ENTER_FIELD(e, x, y) \
238 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
240 #define YAMYAM_CAN_ENTER_FIELD(e, x, y) \
241 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
243 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y) \
244 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
246 #define PACMAN_CAN_ENTER_FIELD(e, x, y) \
247 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
249 #define PIG_CAN_ENTER_FIELD(e, x, y) \
250 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
252 #define PENGUIN_CAN_ENTER_FIELD(e, x, y) \
253 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
254 Feld[x][y] == EL_EM_EXIT_OPEN || \
255 Feld[x][y] == EL_STEEL_EXIT_OPEN || \
256 Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
257 IS_FOOD_PENGUIN(Feld[x][y])))
258 #define DRAGON_CAN_ENTER_FIELD(e, x, y) \
259 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
261 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition) \
262 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
264 #define SPRING_CAN_ENTER_FIELD(e, x, y) \
265 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
267 #define SPRING_CAN_BUMP_FROM_FIELD(x, y) \
268 (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER || \
269 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
271 #define MOVE_ENTER_EL(e) (element_info[e].move_enter_element)
273 #define CE_ENTER_FIELD_COND(e, x, y) \
274 (!IS_PLAYER(x, y) && \
275 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
277 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y) \
278 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
280 #define IN_LEV_FIELD_AND_IS_FREE(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
281 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
283 #define ACCESS_FROM(e, d) (element_info[e].access_direction &(d))
284 #define IS_WALKABLE_FROM(e, d) (IS_WALKABLE(e) && ACCESS_FROM(e, d))
285 #define IS_PASSABLE_FROM(e, d) (IS_PASSABLE(e) && ACCESS_FROM(e, d))
286 #define IS_ACCESSIBLE_FROM(e, d) (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
288 /* game button identifiers */
289 #define GAME_CTRL_ID_STOP 0
290 #define GAME_CTRL_ID_PAUSE 1
291 #define GAME_CTRL_ID_PLAY 2
292 #define SOUND_CTRL_ID_MUSIC 3
293 #define SOUND_CTRL_ID_LOOPS 4
294 #define SOUND_CTRL_ID_SIMPLE 5
296 #define NUM_GAME_BUTTONS 6
299 /* forward declaration for internal use */
301 static void CreateField(int, int, int);
303 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
304 static void AdvanceFrameAndPlayerCounters(int);
306 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
307 static boolean MovePlayer(struct PlayerInfo *, int, int);
308 static void ScrollPlayer(struct PlayerInfo *, int);
309 static void ScrollScreen(struct PlayerInfo *, int);
311 int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
313 static void InitBeltMovement(void);
314 static void CloseAllOpenTimegates(void);
315 static void CheckGravityMovement(struct PlayerInfo *);
316 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
317 static void KillPlayerUnlessEnemyProtected(int, int);
318 static void KillPlayerUnlessExplosionProtected(int, int);
320 static void TestIfPlayerTouchesCustomElement(int, int);
321 static void TestIfElementTouchesCustomElement(int, int);
322 static void TestIfElementHitsCustomElement(int, int, int);
324 static void TestIfElementSmashesCustomElement(int, int, int);
327 static void HandleElementChange(int, int, int);
328 static void ExecuteCustomElementAction(int, int, int, int);
329 static boolean ChangeElement(int, int, int, int);
331 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
332 #define CheckTriggeredElementChange(x, y, e, ev) \
333 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
334 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s) \
335 CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
336 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s) \
337 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
338 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p) \
339 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
341 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
342 #define CheckElementChange(x, y, e, te, ev) \
343 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
344 #define CheckElementChangeByPlayer(x, y, e, ev, p, s) \
345 CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
346 #define CheckElementChangeBySide(x, y, e, te, ev, s) \
347 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
349 static void PlayLevelSound(int, int, int);
350 static void PlayLevelSoundNearest(int, int, int);
351 static void PlayLevelSoundAction(int, int, int);
352 static void PlayLevelSoundElementAction(int, int, int, int);
353 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
354 static void PlayLevelSoundActionIfLoop(int, int, int);
355 static void StopLevelSoundActionIfLoop(int, int, int);
356 static void PlayLevelMusic();
358 static void MapGameButtons();
359 static void HandleGameButtons(struct GadgetInfo *);
361 int AmoebeNachbarNr(int, int);
362 void AmoebeUmwandeln(int, int);
363 void ContinueMoving(int, int);
365 void InitMovDir(int, int);
366 void InitAmoebaNr(int, int);
367 int NewHiScore(void);
369 void TestIfGoodThingHitsBadThing(int, int, int);
370 void TestIfBadThingHitsGoodThing(int, int, int);
371 void TestIfPlayerTouchesBadThing(int, int);
372 void TestIfPlayerRunsIntoBadThing(int, int, int);
373 void TestIfBadThingTouchesPlayer(int, int);
374 void TestIfBadThingRunsIntoPlayer(int, int, int);
375 void TestIfFriendTouchesBadThing(int, int);
376 void TestIfBadThingTouchesFriend(int, int);
377 void TestIfBadThingTouchesOtherBadThing(int, int);
379 void KillPlayer(struct PlayerInfo *);
380 void BuryPlayer(struct PlayerInfo *);
381 void RemovePlayer(struct PlayerInfo *);
383 boolean SnapField(struct PlayerInfo *, int, int);
384 boolean DropElement(struct PlayerInfo *);
386 static int getInvisibleActiveFromInvisibleElement(int);
387 static int getInvisibleFromInvisibleActiveElement(int);
389 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
391 /* for detection of endless loops, caused by custom element programming */
392 /* (using maximal playfield width x 10 is just a rough approximation) */
393 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH (MAX_PLAYFIELD_WIDTH * 10)
395 #define RECURSION_LOOP_DETECTION_START(e, rc) \
397 if (recursion_loop_detected) \
400 if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH) \
402 recursion_loop_detected = TRUE; \
403 recursion_loop_element = (e); \
406 recursion_loop_depth++; \
409 #define RECURSION_LOOP_DETECTION_END() \
411 recursion_loop_depth--; \
414 static int recursion_loop_depth;
415 static boolean recursion_loop_detected;
416 static boolean recursion_loop_element;
419 /* ------------------------------------------------------------------------- */
420 /* definition of elements that automatically change to other elements after */
421 /* a specified time, eventually calling a function when changing */
422 /* ------------------------------------------------------------------------- */
424 /* forward declaration for changer functions */
425 static void InitBuggyBase(int, int);
426 static void WarnBuggyBase(int, int);
428 static void InitTrap(int, int);
429 static void ActivateTrap(int, int);
430 static void ChangeActiveTrap(int, int);
432 static void InitRobotWheel(int, int);
433 static void RunRobotWheel(int, int);
434 static void StopRobotWheel(int, int);
436 static void InitTimegateWheel(int, int);
437 static void RunTimegateWheel(int, int);
439 static void InitMagicBallDelay(int, int);
440 static void ActivateMagicBall(int, int);
442 struct ChangingElementInfo
447 void (*pre_change_function)(int x, int y);
448 void (*change_function)(int x, int y);
449 void (*post_change_function)(int x, int y);
452 static struct ChangingElementInfo change_delay_list[] =
487 EL_STEEL_EXIT_OPENING,
495 EL_STEEL_EXIT_CLOSING,
496 EL_STEEL_EXIT_CLOSED,
523 EL_EM_STEEL_EXIT_OPENING,
524 EL_EM_STEEL_EXIT_OPEN,
531 EL_EM_STEEL_EXIT_CLOSING,
535 EL_EM_STEEL_EXIT_CLOSED,
559 EL_SWITCHGATE_OPENING,
567 EL_SWITCHGATE_CLOSING,
568 EL_SWITCHGATE_CLOSED,
600 EL_ACID_SPLASH_RIGHT,
609 EL_SP_BUGGY_BASE_ACTIVATING,
616 EL_SP_BUGGY_BASE_ACTIVATING,
617 EL_SP_BUGGY_BASE_ACTIVE,
624 EL_SP_BUGGY_BASE_ACTIVE,
648 EL_ROBOT_WHEEL_ACTIVE,
656 EL_TIMEGATE_SWITCH_ACTIVE,
664 EL_DC_TIMEGATE_SWITCH_ACTIVE,
665 EL_DC_TIMEGATE_SWITCH,
672 EL_EMC_MAGIC_BALL_ACTIVE,
673 EL_EMC_MAGIC_BALL_ACTIVE,
680 EL_EMC_SPRING_BUMPER_ACTIVE,
681 EL_EMC_SPRING_BUMPER,
688 EL_DIAGONAL_SHRINKING,
717 int push_delay_fixed, push_delay_random;
722 { EL_BALLOON, 0, 0 },
724 { EL_SOKOBAN_OBJECT, 2, 0 },
725 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
726 { EL_SATELLITE, 2, 0 },
727 { EL_SP_DISK_YELLOW, 2, 0 },
729 { EL_UNDEFINED, 0, 0 },
737 move_stepsize_list[] =
739 { EL_AMOEBA_DROP, 2 },
740 { EL_AMOEBA_DROPPING, 2 },
741 { EL_QUICKSAND_FILLING, 1 },
742 { EL_QUICKSAND_EMPTYING, 1 },
743 { EL_QUICKSAND_FAST_FILLING, 2 },
744 { EL_QUICKSAND_FAST_EMPTYING, 2 },
745 { EL_MAGIC_WALL_FILLING, 2 },
746 { EL_MAGIC_WALL_EMPTYING, 2 },
747 { EL_BD_MAGIC_WALL_FILLING, 2 },
748 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
749 { EL_DC_MAGIC_WALL_FILLING, 2 },
750 { EL_DC_MAGIC_WALL_EMPTYING, 2 },
760 collect_count_list[] =
763 { EL_BD_DIAMOND, 1 },
764 { EL_EMERALD_YELLOW, 1 },
765 { EL_EMERALD_RED, 1 },
766 { EL_EMERALD_PURPLE, 1 },
768 { EL_SP_INFOTRON, 1 },
780 access_direction_list[] =
782 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
783 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
784 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
785 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
786 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
787 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
788 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
789 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
790 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
791 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
792 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
794 { EL_SP_PORT_LEFT, MV_RIGHT },
795 { EL_SP_PORT_RIGHT, MV_LEFT },
796 { EL_SP_PORT_UP, MV_DOWN },
797 { EL_SP_PORT_DOWN, MV_UP },
798 { EL_SP_PORT_HORIZONTAL, MV_LEFT | MV_RIGHT },
799 { EL_SP_PORT_VERTICAL, MV_UP | MV_DOWN },
800 { EL_SP_PORT_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
801 { EL_SP_GRAVITY_PORT_LEFT, MV_RIGHT },
802 { EL_SP_GRAVITY_PORT_RIGHT, MV_LEFT },
803 { EL_SP_GRAVITY_PORT_UP, MV_DOWN },
804 { EL_SP_GRAVITY_PORT_DOWN, MV_UP },
805 { EL_SP_GRAVITY_ON_PORT_LEFT, MV_RIGHT },
806 { EL_SP_GRAVITY_ON_PORT_RIGHT, MV_LEFT },
807 { EL_SP_GRAVITY_ON_PORT_UP, MV_DOWN },
808 { EL_SP_GRAVITY_ON_PORT_DOWN, MV_UP },
809 { EL_SP_GRAVITY_OFF_PORT_LEFT, MV_RIGHT },
810 { EL_SP_GRAVITY_OFF_PORT_RIGHT, MV_LEFT },
811 { EL_SP_GRAVITY_OFF_PORT_UP, MV_DOWN },
812 { EL_SP_GRAVITY_OFF_PORT_DOWN, MV_UP },
814 { EL_UNDEFINED, MV_NONE }
817 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
819 #define IS_AUTO_CHANGING(e) (element_info[e].has_change_event[CE_DELAY])
820 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
821 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
822 IS_JUST_CHANGING(x, y))
824 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
826 /* static variables for playfield scan mode (scanning forward or backward) */
827 static int playfield_scan_start_x = 0;
828 static int playfield_scan_start_y = 0;
829 static int playfield_scan_delta_x = 1;
830 static int playfield_scan_delta_y = 1;
832 #define SCAN_PLAYFIELD(x, y) for ((y) = playfield_scan_start_y; \
833 (y) >= 0 && (y) <= lev_fieldy - 1; \
834 (y) += playfield_scan_delta_y) \
835 for ((x) = playfield_scan_start_x; \
836 (x) >= 0 && (x) <= lev_fieldx - 1; \
837 (x) += playfield_scan_delta_x) \
840 void DEBUG_SetMaximumDynamite()
844 for (i = 0; i < MAX_INVENTORY_SIZE; i++)
845 if (local_player->inventory_size < MAX_INVENTORY_SIZE)
846 local_player->inventory_element[local_player->inventory_size++] =
851 static void InitPlayfieldScanModeVars()
853 if (game.use_reverse_scan_direction)
855 playfield_scan_start_x = lev_fieldx - 1;
856 playfield_scan_start_y = lev_fieldy - 1;
858 playfield_scan_delta_x = -1;
859 playfield_scan_delta_y = -1;
863 playfield_scan_start_x = 0;
864 playfield_scan_start_y = 0;
866 playfield_scan_delta_x = 1;
867 playfield_scan_delta_y = 1;
871 static void InitPlayfieldScanMode(int mode)
873 game.use_reverse_scan_direction =
874 (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
876 InitPlayfieldScanModeVars();
879 static int get_move_delay_from_stepsize(int move_stepsize)
882 MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
884 /* make sure that stepsize value is always a power of 2 */
885 move_stepsize = (1 << log_2(move_stepsize));
887 return TILEX / move_stepsize;
890 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
893 int player_nr = player->index_nr;
894 int move_delay = get_move_delay_from_stepsize(move_stepsize);
895 boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
897 /* do no immediately change move delay -- the player might just be moving */
898 player->move_delay_value_next = move_delay;
900 /* information if player can move must be set separately */
901 player->cannot_move = cannot_move;
905 player->move_delay = game.initial_move_delay[player_nr];
906 player->move_delay_value = game.initial_move_delay_value[player_nr];
908 player->move_delay_value_next = -1;
910 player->move_delay_reset_counter = 0;
914 void GetPlayerConfig()
916 if (!audio.sound_available)
917 setup.sound_simple = FALSE;
919 if (!audio.loops_available)
920 setup.sound_loops = FALSE;
922 if (!audio.music_available)
923 setup.sound_music = FALSE;
925 if (!video.fullscreen_available)
926 setup.fullscreen = FALSE;
928 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
930 SetAudioMode(setup.sound);
934 static int getBeltNrFromBeltElement(int element)
936 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
937 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
938 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
941 static int getBeltNrFromBeltActiveElement(int element)
943 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
944 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
945 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
948 static int getBeltNrFromBeltSwitchElement(int element)
950 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
951 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
952 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
955 static int getBeltDirNrFromBeltSwitchElement(int element)
957 static int belt_base_element[4] =
959 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
960 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
961 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
962 EL_CONVEYOR_BELT_4_SWITCH_LEFT
965 int belt_nr = getBeltNrFromBeltSwitchElement(element);
966 int belt_dir_nr = element - belt_base_element[belt_nr];
968 return (belt_dir_nr % 3);
971 static int getBeltDirFromBeltSwitchElement(int element)
973 static int belt_move_dir[3] =
980 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
982 return belt_move_dir[belt_dir_nr];
985 static int get_element_from_group_element(int element)
987 if (IS_GROUP_ELEMENT(element))
989 struct ElementGroupInfo *group = element_info[element].group;
990 int last_anim_random_frame = gfx.anim_random_frame;
993 if (group->choice_mode == ANIM_RANDOM)
994 gfx.anim_random_frame = RND(group->num_elements_resolved);
996 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
997 group->choice_mode, 0,
1000 if (group->choice_mode == ANIM_RANDOM)
1001 gfx.anim_random_frame = last_anim_random_frame;
1003 group->choice_pos++;
1005 element = group->element_resolved[element_pos];
1011 static void InitPlayerField(int x, int y, int element, boolean init_game)
1013 if (element == EL_SP_MURPHY)
1017 if (stored_player[0].present)
1019 Feld[x][y] = EL_SP_MURPHY_CLONE;
1025 stored_player[0].use_murphy = TRUE;
1027 if (!level.use_artwork_element[0])
1028 stored_player[0].artwork_element = EL_SP_MURPHY;
1031 Feld[x][y] = EL_PLAYER_1;
1037 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1038 int jx = player->jx, jy = player->jy;
1040 player->present = TRUE;
1042 player->block_last_field = (element == EL_SP_MURPHY ?
1043 level.sp_block_last_field :
1044 level.block_last_field);
1046 /* ---------- initialize player's last field block delay --------------- */
1048 /* always start with reliable default value (no adjustment needed) */
1049 player->block_delay_adjustment = 0;
1051 /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1052 if (player->block_last_field && element == EL_SP_MURPHY)
1053 player->block_delay_adjustment = 1;
1055 /* special case 2: in game engines before 3.1.1, blocking was different */
1056 if (game.use_block_last_field_bug)
1057 player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1059 if (!options.network || player->connected)
1061 player->active = TRUE;
1063 /* remove potentially duplicate players */
1064 if (StorePlayer[jx][jy] == Feld[x][y])
1065 StorePlayer[jx][jy] = 0;
1067 StorePlayer[x][y] = Feld[x][y];
1071 printf("Player %d activated.\n", player->element_nr);
1072 printf("[Local player is %d and currently %s.]\n",
1073 local_player->element_nr,
1074 local_player->active ? "active" : "not active");
1078 Feld[x][y] = EL_EMPTY;
1080 player->jx = player->last_jx = x;
1081 player->jy = player->last_jy = y;
1085 static void InitField(int x, int y, boolean init_game)
1087 int element = Feld[x][y];
1096 InitPlayerField(x, y, element, init_game);
1099 case EL_SOKOBAN_FIELD_PLAYER:
1100 element = Feld[x][y] = EL_PLAYER_1;
1101 InitField(x, y, init_game);
1103 element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1104 InitField(x, y, init_game);
1107 case EL_SOKOBAN_FIELD_EMPTY:
1108 local_player->sokobanfields_still_needed++;
1112 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1113 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1114 else if (x > 0 && Feld[x-1][y] == EL_ACID)
1115 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1116 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1117 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1118 else if (y > 0 && Feld[x][y-1] == EL_ACID)
1119 Feld[x][y] = EL_ACID_POOL_BOTTOM;
1120 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1121 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1130 case EL_SPACESHIP_RIGHT:
1131 case EL_SPACESHIP_UP:
1132 case EL_SPACESHIP_LEFT:
1133 case EL_SPACESHIP_DOWN:
1134 case EL_BD_BUTTERFLY:
1135 case EL_BD_BUTTERFLY_RIGHT:
1136 case EL_BD_BUTTERFLY_UP:
1137 case EL_BD_BUTTERFLY_LEFT:
1138 case EL_BD_BUTTERFLY_DOWN:
1140 case EL_BD_FIREFLY_RIGHT:
1141 case EL_BD_FIREFLY_UP:
1142 case EL_BD_FIREFLY_LEFT:
1143 case EL_BD_FIREFLY_DOWN:
1144 case EL_PACMAN_RIGHT:
1146 case EL_PACMAN_LEFT:
1147 case EL_PACMAN_DOWN:
1149 case EL_YAMYAM_LEFT:
1150 case EL_YAMYAM_RIGHT:
1152 case EL_YAMYAM_DOWN:
1153 case EL_DARK_YAMYAM:
1156 case EL_SP_SNIKSNAK:
1157 case EL_SP_ELECTRON:
1166 case EL_AMOEBA_FULL:
1171 case EL_AMOEBA_DROP:
1172 if (y == lev_fieldy - 1)
1174 Feld[x][y] = EL_AMOEBA_GROWING;
1175 Store[x][y] = EL_AMOEBA_WET;
1179 case EL_DYNAMITE_ACTIVE:
1180 case EL_SP_DISK_RED_ACTIVE:
1181 case EL_DYNABOMB_PLAYER_1_ACTIVE:
1182 case EL_DYNABOMB_PLAYER_2_ACTIVE:
1183 case EL_DYNABOMB_PLAYER_3_ACTIVE:
1184 case EL_DYNABOMB_PLAYER_4_ACTIVE:
1185 MovDelay[x][y] = 96;
1188 case EL_EM_DYNAMITE_ACTIVE:
1189 MovDelay[x][y] = 32;
1193 local_player->lights_still_needed++;
1197 local_player->friends_still_needed++;
1202 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1205 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1206 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1207 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1208 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1209 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1210 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1211 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1212 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1213 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1214 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1215 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1216 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1219 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1220 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1221 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1223 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
1225 game.belt_dir[belt_nr] = belt_dir;
1226 game.belt_dir_nr[belt_nr] = belt_dir_nr;
1228 else /* more than one switch -- set it like the first switch */
1230 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1235 #if !USE_BOTH_SWITCHGATE_SWITCHES
1236 case EL_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
1238 Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
1241 case EL_DC_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
1243 Feld[x][y] = EL_DC_SWITCHGATE_SWITCH_UP;
1247 case EL_LIGHT_SWITCH_ACTIVE:
1249 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1252 case EL_INVISIBLE_STEELWALL:
1253 case EL_INVISIBLE_WALL:
1254 case EL_INVISIBLE_SAND:
1255 if (game.light_time_left > 0 ||
1256 game.lenses_time_left > 0)
1257 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1260 case EL_EMC_MAGIC_BALL:
1261 if (game.ball_state)
1262 Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1265 case EL_EMC_MAGIC_BALL_SWITCH:
1266 if (game.ball_state)
1267 Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1271 if (IS_CUSTOM_ELEMENT(element))
1273 if (CAN_MOVE(element))
1276 #if USE_NEW_CUSTOM_VALUE
1277 if (!element_info[element].use_last_ce_value || init_game)
1278 CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1281 else if (IS_GROUP_ELEMENT(element))
1283 Feld[x][y] = get_element_from_group_element(element);
1285 InitField(x, y, init_game);
1292 CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1295 static inline void InitField_WithBug1(int x, int y, boolean init_game)
1297 InitField(x, y, init_game);
1299 /* not needed to call InitMovDir() -- already done by InitField()! */
1300 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1301 CAN_MOVE(Feld[x][y]))
1305 static inline void InitField_WithBug2(int x, int y, boolean init_game)
1307 int old_element = Feld[x][y];
1309 InitField(x, y, init_game);
1311 /* not needed to call InitMovDir() -- already done by InitField()! */
1312 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1313 CAN_MOVE(old_element) &&
1314 (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1317 /* this case is in fact a combination of not less than three bugs:
1318 first, it calls InitMovDir() for elements that can move, although this is
1319 already done by InitField(); then, it checks the element that was at this
1320 field _before_ the call to InitField() (which can change it); lastly, it
1321 was not called for "mole with direction" elements, which were treated as
1322 "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1328 inline void DrawGameValue_Emeralds(int value)
1330 int font_nr = FONT_TEXT_2;
1331 int font_width = getFontWidth(font_nr);
1332 int xpos = (3 * 14 - 3 * font_width) / 2;
1334 if (PANEL_DEACTIVATED(game.panel.gems))
1337 game.panel.gems.width = game.panel.gems.chars * font_width;
1340 DrawText(DX_EMERALDS + xpos, DY_EMERALDS, int2str(value, 3), font_nr);
1343 inline void DrawGameValue_Dynamite(int value)
1345 int font_nr = FONT_TEXT_2;
1346 int font_width = getFontWidth(font_nr);
1347 int xpos = (3 * 14 - 3 * font_width) / 2;
1349 if (PANEL_DEACTIVATED(game.panel.inventory))
1352 game.panel.inventory.width = game.panel.inventory.chars * font_width;
1355 DrawText(DX_DYNAMITE + xpos, DY_DYNAMITE, int2str(value, 3), font_nr);
1358 inline void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
1360 int base_key_graphic = EL_KEY_1;
1363 if (PANEL_DEACTIVATED(game.panel.keys))
1366 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
1367 base_key_graphic = EL_EM_KEY_1;
1369 /* currently only 4 of 8 possible keys are displayed */
1370 for (i = 0; i < STD_NUM_KEYS; i++)
1372 int x = XX_KEYS + i * MINI_TILEX;
1376 DrawMiniGraphicExt(drawto, DX + x,DY + y, el2edimg(base_key_graphic + i));
1378 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
1379 DOOR_GFX_PAGEX5 + x, y, MINI_TILEX, MINI_TILEY, DX + x,DY + y);
1383 inline void DrawGameValue_Score(int value)
1385 int font_nr = FONT_TEXT_2;
1386 int font_width = getFontWidth(font_nr);
1387 int xpos = (5 * 14 - 5 * font_width) / 2;
1389 if (PANEL_DEACTIVATED(game.panel.score))
1392 game.panel.score.width = game.panel.score.chars * font_width;
1395 DrawText(DX_SCORE + xpos, DY_SCORE, int2str(value, 5), font_nr);
1398 inline void DrawGameValue_Time(int value)
1400 static int last_value = -1;
1401 int num_digits1 = 3;
1402 int num_digits2 = 4;
1403 int num_digits = game.panel.time.chars;
1404 int font1_nr = FONT_TEXT_2;
1405 int font2_nr = FONT_TEXT_1;
1406 int font_nr = font1_nr;
1407 int font1_width = getFontWidth(font1_nr);
1408 int font2_width = getFontWidth(font2_nr);
1409 int xpos3 = (3 * 14 - 3 * font1_width) / 2;
1410 int xpos4 = (4 * 10 - 4 * font2_width) / 2;
1412 if (PANEL_DEACTIVATED(game.panel.time))
1415 if (num_digits == -1) /* use dynamic number of digits */
1417 num_digits = (value < 1000 ? num_digits1 : num_digits2);
1418 font_nr = (value < 1000 ? font1_nr : font2_nr);
1424 /* clear background if value just changed its size (dynamic digits only) */
1425 if (game.panel.time.chars == -1 && (last_value < 1000) != (value < 1000))
1427 int width1 = num_digits1 * getFontWidth(font1_nr);
1428 int width2 = num_digits2 * getFontWidth(font2_nr);
1429 int max_width = MAX(width1, width2);
1430 int max_height = MAX(getFontHeight(font1_nr), getFontHeight(font2_nr));
1432 game.panel.time.width = max_width;
1434 ClearRectangleOnBackground(drawto, DX_TIME, DY_TIME, max_width, max_height);
1437 game.panel.time.width = num_digits * getFontWidth(font_nr);
1439 DrawText(DX_TIME, DY_TIME, int2str(value, num_digits), font_nr);
1444 inline void DrawGameValue_Level(int value)
1446 int num_digits1 = 2;
1447 int num_digits2 = 3;
1448 int num_digits = game.panel.level.chars;
1449 int font1_nr = FONT_TEXT_2;
1450 int font2_nr = FONT_TEXT_1;
1451 int font_nr = font1_nr;
1453 if (PANEL_DEACTIVATED(game.panel.level))
1456 if (num_digits == -1) /* use dynamic number of digits */
1458 num_digits = (level_nr < 100 ? num_digits1 : num_digits2);
1459 font_nr = (level_nr < 100 ? font1_nr : font2_nr);
1462 game.panel.level.width = num_digits * getFontWidth(font_nr);
1464 DrawText(DX_LEVEL, DY_LEVEL, int2str(value, num_digits), font_nr);
1469 inline void DrawGameValue_Emeralds(int value)
1471 int font_nr = FONT_TEXT_2;
1472 int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
1474 if (PANEL_DEACTIVATED(game.panel.gems))
1477 DrawText(DX_EMERALDS + xpos, DY_EMERALDS, int2str(value, 3), font_nr);
1480 inline void DrawGameValue_Dynamite(int value)
1482 int font_nr = FONT_TEXT_2;
1483 int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
1485 if (PANEL_DEACTIVATED(game.panel.inventory))
1488 DrawText(DX_DYNAMITE + xpos, DY_DYNAMITE, int2str(value, 3), font_nr);
1491 inline void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
1493 int base_key_graphic = EL_KEY_1;
1496 if (PANEL_DEACTIVATED(game.panel.keys))
1499 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
1500 base_key_graphic = EL_EM_KEY_1;
1502 /* currently only 4 of 8 possible keys are displayed */
1503 for (i = 0; i < STD_NUM_KEYS; i++)
1505 int x = XX_KEYS + i * MINI_TILEX;
1509 DrawMiniGraphicExt(drawto, DX + x,DY + y, el2edimg(base_key_graphic + i));
1511 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
1512 DOOR_GFX_PAGEX5 + x, y, MINI_TILEX, MINI_TILEY, DX + x,DY + y);
1516 inline void DrawGameValue_Score(int value)
1518 int font_nr = FONT_TEXT_2;
1519 int xpos = (5 * 14 - 5 * getFontWidth(font_nr)) / 2;
1521 if (PANEL_DEACTIVATED(game.panel.score))
1524 DrawText(DX_SCORE + xpos, DY_SCORE, int2str(value, 5), font_nr);
1527 inline void DrawGameValue_Time(int value)
1529 int font1_nr = FONT_TEXT_2;
1531 int font2_nr = FONT_TEXT_1;
1533 int font2_nr = FONT_LEVEL_NUMBER;
1535 int xpos3 = (3 * 14 - 3 * getFontWidth(font1_nr)) / 2;
1536 int xpos4 = (4 * 10 - 4 * getFontWidth(font2_nr)) / 2;
1538 if (PANEL_DEACTIVATED(game.panel.time))
1541 /* clear background if value just changed its size */
1542 if (value == 999 || value == 1000)
1543 ClearRectangleOnBackground(drawto, DX_TIME1, DY_TIME, 14 * 3, 14);
1546 DrawText(DX_TIME1 + xpos3, DY_TIME, int2str(value, 3), font1_nr);
1548 DrawText(DX_TIME2 + xpos4, DY_TIME, int2str(value, 4), font2_nr);
1551 inline void DrawGameValue_Level(int value)
1553 int font1_nr = FONT_TEXT_2;
1555 int font2_nr = FONT_TEXT_1;
1557 int font2_nr = FONT_LEVEL_NUMBER;
1560 if (PANEL_DEACTIVATED(game.panel.level))
1564 DrawText(DX_LEVEL1, DY_LEVEL, int2str(value, 2), font1_nr);
1566 DrawText(DX_LEVEL2, DY_LEVEL, int2str(value, 3), font2_nr);
1571 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
1574 int key[MAX_NUM_KEYS];
1577 /* prevent EM engine from updating time/score values parallel to GameWon() */
1578 if (level.game_engine_type == GAME_ENGINE_TYPE_EM &&
1579 local_player->LevelSolved)
1582 for (i = 0; i < MAX_NUM_KEYS; i++)
1583 key[i] = key_bits & (1 << i);
1585 DrawGameValue_Level(level_nr);
1587 DrawGameValue_Emeralds(emeralds);
1588 DrawGameValue_Dynamite(dynamite);
1589 DrawGameValue_Score(score);
1590 DrawGameValue_Time(time);
1592 DrawGameValue_Keys(key);
1595 void DrawGameDoorValues()
1597 int time_value = (level.time == 0 ? TimePlayed : TimeLeft);
1598 int dynamite_value = 0;
1599 int score_value = (local_player->LevelSolved ? local_player->score_final :
1600 local_player->score);
1601 int gems_value = local_player->gems_still_needed;
1605 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
1607 DrawGameDoorValues_EM();
1612 if (game.centered_player_nr == -1)
1614 for (i = 0; i < MAX_PLAYERS; i++)
1616 for (j = 0; j < MAX_NUM_KEYS; j++)
1617 if (stored_player[i].key[j])
1618 key_bits |= (1 << j);
1620 dynamite_value += stored_player[i].inventory_size;
1625 int player_nr = game.centered_player_nr;
1627 for (i = 0; i < MAX_NUM_KEYS; i++)
1628 if (stored_player[player_nr].key[i])
1629 key_bits |= (1 << i);
1631 dynamite_value = stored_player[player_nr].inventory_size;
1634 DrawAllGameValues(gems_value, dynamite_value, score_value, time_value,
1640 =============================================================================
1642 -----------------------------------------------------------------------------
1643 initialize game engine due to level / tape version number
1644 =============================================================================
1647 static void InitGameEngine()
1649 int i, j, k, l, x, y;
1651 /* set game engine from tape file when re-playing, else from level file */
1652 game.engine_version = (tape.playing ? tape.engine_version :
1653 level.game_version);
1655 /* ---------------------------------------------------------------------- */
1656 /* set flags for bugs and changes according to active game engine version */
1657 /* ---------------------------------------------------------------------- */
1660 Summary of bugfix/change:
1661 Fixed handling for custom elements that change when pushed by the player.
1663 Fixed/changed in version:
1667 Before 3.1.0, custom elements that "change when pushing" changed directly
1668 after the player started pushing them (until then handled in "DigField()").
1669 Since 3.1.0, these custom elements are not changed until the "pushing"
1670 move of the element is finished (now handled in "ContinueMoving()").
1672 Affected levels/tapes:
1673 The first condition is generally needed for all levels/tapes before version
1674 3.1.0, which might use the old behaviour before it was changed; known tapes
1675 that are affected are some tapes from the level set "Walpurgis Gardens" by
1677 The second condition is an exception from the above case and is needed for
1678 the special case of tapes recorded with game (not engine!) version 3.1.0 or
1679 above (including some development versions of 3.1.0), but before it was
1680 known that this change would break tapes like the above and was fixed in
1681 3.1.1, so that the changed behaviour was active although the engine version
1682 while recording maybe was before 3.1.0. There is at least one tape that is
1683 affected by this exception, which is the tape for the one-level set "Bug
1684 Machine" by Juergen Bonhagen.
1687 game.use_change_when_pushing_bug =
1688 (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1690 tape.game_version >= VERSION_IDENT(3,1,0,0) &&
1691 tape.game_version < VERSION_IDENT(3,1,1,0)));
1694 Summary of bugfix/change:
1695 Fixed handling for blocking the field the player leaves when moving.
1697 Fixed/changed in version:
1701 Before 3.1.1, when "block last field when moving" was enabled, the field
1702 the player is leaving when moving was blocked for the time of the move,
1703 and was directly unblocked afterwards. This resulted in the last field
1704 being blocked for exactly one less than the number of frames of one player
1705 move. Additionally, even when blocking was disabled, the last field was
1706 blocked for exactly one frame.
1707 Since 3.1.1, due to changes in player movement handling, the last field
1708 is not blocked at all when blocking is disabled. When blocking is enabled,
1709 the last field is blocked for exactly the number of frames of one player
1710 move. Additionally, if the player is Murphy, the hero of Supaplex, the
1711 last field is blocked for exactly one more than the number of frames of
1714 Affected levels/tapes:
1715 (!!! yet to be determined -- probably many !!!)
1718 game.use_block_last_field_bug =
1719 (game.engine_version < VERSION_IDENT(3,1,1,0));
1722 Summary of bugfix/change:
1723 Changed behaviour of CE changes with multiple changes per single frame.
1725 Fixed/changed in version:
1729 Before 3.2.0-6, only one single CE change was allowed in each engine frame.
1730 This resulted in race conditions where CEs seem to behave strange in some
1731 situations (where triggered CE changes were just skipped because there was
1732 already a CE change on that tile in the playfield in that engine frame).
1733 Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
1734 (The number of changes per frame must be limited in any case, because else
1735 it is easily possible to define CE changes that would result in an infinite
1736 loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
1737 should be set large enough so that it would only be reached in cases where
1738 the corresponding CE change conditions run into a loop. Therefore, it seems
1739 to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
1740 maximal number of change pages for custom elements.)
1742 Affected levels/tapes:
1746 #if USE_ONLY_ONE_CHANGE_PER_FRAME
1747 game.max_num_changes_per_frame = 1;
1749 game.max_num_changes_per_frame =
1750 (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
1753 /* ---------------------------------------------------------------------- */
1755 /* default scan direction: scan playfield from top/left to bottom/right */
1756 InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
1758 /* dynamically adjust element properties according to game engine version */
1759 InitElementPropertiesEngine(game.engine_version);
1762 printf("level %d: level version == %06d\n", level_nr, level.game_version);
1763 printf(" tape version == %06d [%s] [file: %06d]\n",
1764 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
1766 printf(" => game.engine_version == %06d\n", game.engine_version);
1769 /* ---------- initialize player's initial move delay --------------------- */
1771 /* dynamically adjust player properties according to level information */
1772 for (i = 0; i < MAX_PLAYERS; i++)
1773 game.initial_move_delay_value[i] =
1774 get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
1776 /* dynamically adjust player properties according to game engine version */
1777 for (i = 0; i < MAX_PLAYERS; i++)
1778 game.initial_move_delay[i] =
1779 (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
1780 game.initial_move_delay_value[i] : 0);
1782 /* ---------- initialize player's initial push delay --------------------- */
1784 /* dynamically adjust player properties according to game engine version */
1785 game.initial_push_delay_value =
1786 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
1788 /* ---------- initialize changing elements ------------------------------- */
1790 /* initialize changing elements information */
1791 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1793 struct ElementInfo *ei = &element_info[i];
1795 /* this pointer might have been changed in the level editor */
1796 ei->change = &ei->change_page[0];
1798 if (!IS_CUSTOM_ELEMENT(i))
1800 ei->change->target_element = EL_EMPTY_SPACE;
1801 ei->change->delay_fixed = 0;
1802 ei->change->delay_random = 0;
1803 ei->change->delay_frames = 1;
1806 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1808 ei->has_change_event[j] = FALSE;
1810 ei->event_page_nr[j] = 0;
1811 ei->event_page[j] = &ei->change_page[0];
1815 /* add changing elements from pre-defined list */
1816 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
1818 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
1819 struct ElementInfo *ei = &element_info[ch_delay->element];
1821 ei->change->target_element = ch_delay->target_element;
1822 ei->change->delay_fixed = ch_delay->change_delay;
1824 ei->change->pre_change_function = ch_delay->pre_change_function;
1825 ei->change->change_function = ch_delay->change_function;
1826 ei->change->post_change_function = ch_delay->post_change_function;
1828 ei->change->can_change = TRUE;
1829 ei->change->can_change_or_has_action = TRUE;
1831 ei->has_change_event[CE_DELAY] = TRUE;
1833 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
1834 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
1837 /* ---------- initialize internal run-time variables ------------- */
1839 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1841 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1843 for (j = 0; j < ei->num_change_pages; j++)
1845 ei->change_page[j].can_change_or_has_action =
1846 (ei->change_page[j].can_change |
1847 ei->change_page[j].has_action);
1851 /* add change events from custom element configuration */
1852 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1854 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1856 for (j = 0; j < ei->num_change_pages; j++)
1858 if (!ei->change_page[j].can_change_or_has_action)
1861 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1863 /* only add event page for the first page found with this event */
1864 if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
1866 ei->has_change_event[k] = TRUE;
1868 ei->event_page_nr[k] = j;
1869 ei->event_page[k] = &ei->change_page[j];
1875 /* ---------- initialize run-time trigger player and element ------------- */
1877 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1879 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1881 for (j = 0; j < ei->num_change_pages; j++)
1883 ei->change_page[j].actual_trigger_element = EL_EMPTY;
1884 ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
1885 ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
1886 ei->change_page[j].actual_trigger_ce_value = 0;
1887 ei->change_page[j].actual_trigger_ce_score = 0;
1891 /* ---------- initialize trigger events ---------------------------------- */
1893 /* initialize trigger events information */
1894 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1895 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1896 trigger_events[i][j] = FALSE;
1898 /* add trigger events from element change event properties */
1899 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1901 struct ElementInfo *ei = &element_info[i];
1903 for (j = 0; j < ei->num_change_pages; j++)
1905 if (!ei->change_page[j].can_change_or_has_action)
1908 if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
1910 int trigger_element = ei->change_page[j].trigger_element;
1912 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1914 if (ei->change_page[j].has_event[k])
1916 if (IS_GROUP_ELEMENT(trigger_element))
1918 struct ElementGroupInfo *group =
1919 element_info[trigger_element].group;
1921 for (l = 0; l < group->num_elements_resolved; l++)
1922 trigger_events[group->element_resolved[l]][k] = TRUE;
1924 else if (trigger_element == EL_ANY_ELEMENT)
1925 for (l = 0; l < MAX_NUM_ELEMENTS; l++)
1926 trigger_events[l][k] = TRUE;
1928 trigger_events[trigger_element][k] = TRUE;
1935 /* ---------- initialize push delay -------------------------------------- */
1937 /* initialize push delay values to default */
1938 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1940 if (!IS_CUSTOM_ELEMENT(i))
1942 /* set default push delay values (corrected since version 3.0.7-1) */
1943 if (game.engine_version < VERSION_IDENT(3,0,7,1))
1945 element_info[i].push_delay_fixed = 2;
1946 element_info[i].push_delay_random = 8;
1950 element_info[i].push_delay_fixed = 8;
1951 element_info[i].push_delay_random = 8;
1956 /* set push delay value for certain elements from pre-defined list */
1957 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
1959 int e = push_delay_list[i].element;
1961 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
1962 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
1965 /* set push delay value for Supaplex elements for newer engine versions */
1966 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
1968 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1970 if (IS_SP_ELEMENT(i))
1972 /* set SP push delay to just enough to push under a falling zonk */
1973 int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
1975 element_info[i].push_delay_fixed = delay;
1976 element_info[i].push_delay_random = 0;
1981 /* ---------- initialize move stepsize ----------------------------------- */
1983 /* initialize move stepsize values to default */
1984 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1985 if (!IS_CUSTOM_ELEMENT(i))
1986 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
1988 /* set move stepsize value for certain elements from pre-defined list */
1989 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
1991 int e = move_stepsize_list[i].element;
1993 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
1996 /* ---------- initialize collect score ----------------------------------- */
1998 /* initialize collect score values for custom elements from initial value */
1999 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2000 if (IS_CUSTOM_ELEMENT(i))
2001 element_info[i].collect_score = element_info[i].collect_score_initial;
2003 /* ---------- initialize collect count ----------------------------------- */
2005 /* initialize collect count values for non-custom elements */
2006 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2007 if (!IS_CUSTOM_ELEMENT(i))
2008 element_info[i].collect_count_initial = 0;
2010 /* add collect count values for all elements from pre-defined list */
2011 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
2012 element_info[collect_count_list[i].element].collect_count_initial =
2013 collect_count_list[i].count;
2015 /* ---------- initialize access direction -------------------------------- */
2017 /* initialize access direction values to default (access from every side) */
2018 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2019 if (!IS_CUSTOM_ELEMENT(i))
2020 element_info[i].access_direction = MV_ALL_DIRECTIONS;
2022 /* set access direction value for certain elements from pre-defined list */
2023 for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
2024 element_info[access_direction_list[i].element].access_direction =
2025 access_direction_list[i].direction;
2027 /* ---------- initialize explosion content ------------------------------- */
2028 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2030 if (IS_CUSTOM_ELEMENT(i))
2033 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
2035 /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
2037 element_info[i].content.e[x][y] =
2038 (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
2039 i == EL_PLAYER_2 ? EL_EMERALD_RED :
2040 i == EL_PLAYER_3 ? EL_EMERALD :
2041 i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
2042 i == EL_MOLE ? EL_EMERALD_RED :
2043 i == EL_PENGUIN ? EL_EMERALD_PURPLE :
2044 i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
2045 i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
2046 i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
2047 i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
2048 i == EL_WALL_EMERALD ? EL_EMERALD :
2049 i == EL_WALL_DIAMOND ? EL_DIAMOND :
2050 i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
2051 i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
2052 i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
2053 i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
2054 i == EL_WALL_PEARL ? EL_PEARL :
2055 i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
2060 /* ---------- initialize recursion detection ------------------------------ */
2061 recursion_loop_depth = 0;
2062 recursion_loop_detected = FALSE;
2063 recursion_loop_element = EL_UNDEFINED;
2066 int get_num_special_action(int element, int action_first, int action_last)
2068 int num_special_action = 0;
2071 for (i = action_first; i <= action_last; i++)
2073 boolean found = FALSE;
2075 for (j = 0; j < NUM_DIRECTIONS; j++)
2076 if (el_act_dir2img(element, i, j) !=
2077 el_act_dir2img(element, ACTION_DEFAULT, j))
2081 num_special_action++;
2086 return num_special_action;
2091 =============================================================================
2093 -----------------------------------------------------------------------------
2094 initialize and start new game
2095 =============================================================================
2100 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
2101 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
2102 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
2103 boolean do_fading = (game_status == GAME_MODE_MAIN);
2106 game_status = GAME_MODE_PLAYING;
2110 /* don't play tapes over network */
2111 network_playing = (options.network && !tape.playing);
2113 for (i = 0; i < MAX_PLAYERS; i++)
2115 struct PlayerInfo *player = &stored_player[i];
2117 player->index_nr = i;
2118 player->index_bit = (1 << i);
2119 player->element_nr = EL_PLAYER_1 + i;
2121 player->present = FALSE;
2122 player->active = FALSE;
2123 player->killed = FALSE;
2126 player->effective_action = 0;
2127 player->programmed_action = 0;
2130 player->score_final = 0;
2132 player->gems_still_needed = level.gems_needed;
2133 player->sokobanfields_still_needed = 0;
2134 player->lights_still_needed = 0;
2135 player->friends_still_needed = 0;
2137 for (j = 0; j < MAX_NUM_KEYS; j++)
2138 player->key[j] = FALSE;
2140 player->num_white_keys = 0;
2142 player->dynabomb_count = 0;
2143 player->dynabomb_size = 1;
2144 player->dynabombs_left = 0;
2145 player->dynabomb_xl = FALSE;
2147 player->MovDir = MV_NONE;
2150 player->GfxDir = MV_NONE;
2151 player->GfxAction = ACTION_DEFAULT;
2153 player->StepFrame = 0;
2155 player->use_murphy = FALSE;
2156 player->artwork_element =
2157 (level.use_artwork_element[i] ? level.artwork_element[i] :
2158 player->element_nr);
2160 player->block_last_field = FALSE; /* initialized in InitPlayerField() */
2161 player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
2163 player->gravity = level.initial_player_gravity[i];
2165 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
2167 player->actual_frame_counter = 0;
2169 player->step_counter = 0;
2171 player->last_move_dir = MV_NONE;
2173 player->is_active = FALSE;
2175 player->is_waiting = FALSE;
2176 player->is_moving = FALSE;
2177 player->is_auto_moving = FALSE;
2178 player->is_digging = FALSE;
2179 player->is_snapping = FALSE;
2180 player->is_collecting = FALSE;
2181 player->is_pushing = FALSE;
2182 player->is_switching = FALSE;
2183 player->is_dropping = FALSE;
2184 player->is_dropping_pressed = FALSE;
2186 player->is_bored = FALSE;
2187 player->is_sleeping = FALSE;
2189 player->frame_counter_bored = -1;
2190 player->frame_counter_sleeping = -1;
2192 player->anim_delay_counter = 0;
2193 player->post_delay_counter = 0;
2195 player->dir_waiting = MV_NONE;
2196 player->action_waiting = ACTION_DEFAULT;
2197 player->last_action_waiting = ACTION_DEFAULT;
2198 player->special_action_bored = ACTION_DEFAULT;
2199 player->special_action_sleeping = ACTION_DEFAULT;
2201 player->switch_x = -1;
2202 player->switch_y = -1;
2204 player->drop_x = -1;
2205 player->drop_y = -1;
2207 player->show_envelope = 0;
2209 SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
2211 player->push_delay = -1; /* initialized when pushing starts */
2212 player->push_delay_value = game.initial_push_delay_value;
2214 player->drop_delay = 0;
2215 player->drop_pressed_delay = 0;
2217 player->last_jx = -1;
2218 player->last_jy = -1;
2222 player->shield_normal_time_left = 0;
2223 player->shield_deadly_time_left = 0;
2225 player->inventory_infinite_element = EL_UNDEFINED;
2226 player->inventory_size = 0;
2228 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
2229 SnapField(player, 0, 0);
2231 player->LevelSolved = FALSE;
2232 player->GameOver = FALSE;
2234 player->LevelSolved_GameEnd = FALSE;
2235 player->LevelSolved_SaveTape = FALSE;
2236 player->LevelSolved_SaveScore = FALSE;
2239 network_player_action_received = FALSE;
2241 #if defined(NETWORK_AVALIABLE)
2242 /* initial null action */
2243 if (network_playing)
2244 SendToServer_MovePlayer(MV_NONE);
2253 TimeLeft = level.time;
2256 ScreenMovDir = MV_NONE;
2260 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
2262 AllPlayersGone = FALSE;
2264 game.yamyam_content_nr = 0;
2265 game.magic_wall_active = FALSE;
2266 game.magic_wall_time_left = 0;
2267 game.light_time_left = 0;
2268 game.timegate_time_left = 0;
2269 game.switchgate_pos = 0;
2270 game.wind_direction = level.wind_direction_initial;
2272 #if !USE_PLAYER_GRAVITY
2273 game.gravity = FALSE;
2274 game.explosions_delayed = TRUE;
2277 game.lenses_time_left = 0;
2278 game.magnify_time_left = 0;
2280 game.ball_state = level.ball_state_initial;
2281 game.ball_content_nr = 0;
2283 game.envelope_active = FALSE;
2285 /* set focus to local player for network games, else to all players */
2286 game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
2287 game.centered_player_nr_next = game.centered_player_nr;
2288 game.set_centered_player = FALSE;
2290 if (network_playing && tape.recording)
2292 /* store client dependent player focus when recording network games */
2293 tape.centered_player_nr_next = game.centered_player_nr_next;
2294 tape.set_centered_player = TRUE;
2297 for (i = 0; i < NUM_BELTS; i++)
2299 game.belt_dir[i] = MV_NONE;
2300 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
2303 for (i = 0; i < MAX_NUM_AMOEBA; i++)
2304 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
2306 SCAN_PLAYFIELD(x, y)
2308 Feld[x][y] = level.field[x][y];
2309 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
2310 ChangeDelay[x][y] = 0;
2311 ChangePage[x][y] = -1;
2312 #if USE_NEW_CUSTOM_VALUE
2313 CustomValue[x][y] = 0; /* initialized in InitField() */
2315 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
2317 WasJustMoving[x][y] = 0;
2318 WasJustFalling[x][y] = 0;
2319 CheckCollision[x][y] = 0;
2320 CheckImpact[x][y] = 0;
2322 Pushed[x][y] = FALSE;
2324 ChangeCount[x][y] = 0;
2325 ChangeEvent[x][y] = -1;
2327 ExplodePhase[x][y] = 0;
2328 ExplodeDelay[x][y] = 0;
2329 ExplodeField[x][y] = EX_TYPE_NONE;
2331 RunnerVisit[x][y] = 0;
2332 PlayerVisit[x][y] = 0;
2335 GfxRandom[x][y] = INIT_GFX_RANDOM();
2336 GfxElement[x][y] = EL_UNDEFINED;
2337 GfxAction[x][y] = ACTION_DEFAULT;
2338 GfxDir[x][y] = MV_NONE;
2341 SCAN_PLAYFIELD(x, y)
2343 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
2345 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
2347 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
2350 InitField(x, y, TRUE);
2355 for (i = 0; i < MAX_PLAYERS; i++)
2357 struct PlayerInfo *player = &stored_player[i];
2359 /* set number of special actions for bored and sleeping animation */
2360 player->num_special_action_bored =
2361 get_num_special_action(player->artwork_element,
2362 ACTION_BORING_1, ACTION_BORING_LAST);
2363 player->num_special_action_sleeping =
2364 get_num_special_action(player->artwork_element,
2365 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
2368 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
2369 emulate_sb ? EMU_SOKOBAN :
2370 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
2372 #if USE_NEW_ALL_SLIPPERY
2373 /* initialize type of slippery elements */
2374 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2376 if (!IS_CUSTOM_ELEMENT(i))
2378 /* default: elements slip down either to the left or right randomly */
2379 element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
2381 /* SP style elements prefer to slip down on the left side */
2382 if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
2383 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
2385 /* BD style elements prefer to slip down on the left side */
2386 if (game.emulation == EMU_BOULDERDASH)
2387 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
2392 /* initialize explosion and ignition delay */
2393 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2395 if (!IS_CUSTOM_ELEMENT(i))
2398 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
2399 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
2400 game.emulation == EMU_SUPAPLEX ? 3 : 2);
2401 int last_phase = (num_phase + 1) * delay;
2402 int half_phase = (num_phase / 2) * delay;
2404 element_info[i].explosion_delay = last_phase - 1;
2405 element_info[i].ignition_delay = half_phase;
2407 if (i == EL_BLACK_ORB)
2408 element_info[i].ignition_delay = 1;
2412 if (element_info[i].explosion_delay < 1) /* !!! check again !!! */
2413 element_info[i].explosion_delay = 1;
2415 if (element_info[i].ignition_delay < 1) /* !!! check again !!! */
2416 element_info[i].ignition_delay = 1;
2420 /* correct non-moving belts to start moving left */
2421 for (i = 0; i < NUM_BELTS; i++)
2422 if (game.belt_dir[i] == MV_NONE)
2423 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
2425 /* check if any connected player was not found in playfield */
2426 for (i = 0; i < MAX_PLAYERS; i++)
2428 struct PlayerInfo *player = &stored_player[i];
2430 if (player->connected && !player->present)
2432 for (j = 0; j < MAX_PLAYERS; j++)
2434 struct PlayerInfo *some_player = &stored_player[j];
2435 int jx = some_player->jx, jy = some_player->jy;
2437 /* assign first free player found that is present in the playfield */
2438 if (some_player->present && !some_player->connected)
2440 player->present = TRUE;
2441 player->active = TRUE;
2443 some_player->present = FALSE;
2444 some_player->active = FALSE;
2446 player->artwork_element = some_player->artwork_element;
2448 player->block_last_field = some_player->block_last_field;
2449 player->block_delay_adjustment = some_player->block_delay_adjustment;
2451 StorePlayer[jx][jy] = player->element_nr;
2452 player->jx = player->last_jx = jx;
2453 player->jy = player->last_jy = jy;
2463 /* when playing a tape, eliminate all players who do not participate */
2465 for (i = 0; i < MAX_PLAYERS; i++)
2467 if (stored_player[i].active && !tape.player_participates[i])
2469 struct PlayerInfo *player = &stored_player[i];
2470 int jx = player->jx, jy = player->jy;
2472 player->active = FALSE;
2473 StorePlayer[jx][jy] = 0;
2474 Feld[jx][jy] = EL_EMPTY;
2478 else if (!options.network && !setup.team_mode) /* && !tape.playing */
2480 /* when in single player mode, eliminate all but the first active player */
2482 for (i = 0; i < MAX_PLAYERS; i++)
2484 if (stored_player[i].active)
2486 for (j = i + 1; j < MAX_PLAYERS; j++)
2488 if (stored_player[j].active)
2490 struct PlayerInfo *player = &stored_player[j];
2491 int jx = player->jx, jy = player->jy;
2493 player->active = FALSE;
2494 player->present = FALSE;
2496 StorePlayer[jx][jy] = 0;
2497 Feld[jx][jy] = EL_EMPTY;
2504 /* when recording the game, store which players take part in the game */
2507 for (i = 0; i < MAX_PLAYERS; i++)
2508 if (stored_player[i].active)
2509 tape.player_participates[i] = TRUE;
2514 for (i = 0; i < MAX_PLAYERS; i++)
2516 struct PlayerInfo *player = &stored_player[i];
2518 printf("Player %d: present == %d, connected == %d, active == %d.\n",
2523 if (local_player == player)
2524 printf("Player %d is local player.\n", i+1);
2528 if (BorderElement == EL_EMPTY)
2531 SBX_Right = lev_fieldx - SCR_FIELDX;
2533 SBY_Lower = lev_fieldy - SCR_FIELDY;
2538 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
2540 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
2543 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
2544 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
2546 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
2547 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
2549 /* if local player not found, look for custom element that might create
2550 the player (make some assumptions about the right custom element) */
2551 if (!local_player->present)
2553 int start_x = 0, start_y = 0;
2554 int found_rating = 0;
2555 int found_element = EL_UNDEFINED;
2556 int player_nr = local_player->index_nr;
2558 SCAN_PLAYFIELD(x, y)
2560 int element = Feld[x][y];
2565 if (level.use_start_element[player_nr] &&
2566 level.start_element[player_nr] == element &&
2573 found_element = element;
2576 if (!IS_CUSTOM_ELEMENT(element))
2579 if (CAN_CHANGE(element))
2581 for (i = 0; i < element_info[element].num_change_pages; i++)
2583 /* check for player created from custom element as single target */
2584 content = element_info[element].change_page[i].target_element;
2585 is_player = ELEM_IS_PLAYER(content);
2587 if (is_player && (found_rating < 3 || element < found_element))
2593 found_element = element;
2598 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
2600 /* check for player created from custom element as explosion content */
2601 content = element_info[element].content.e[xx][yy];
2602 is_player = ELEM_IS_PLAYER(content);
2604 if (is_player && (found_rating < 2 || element < found_element))
2606 start_x = x + xx - 1;
2607 start_y = y + yy - 1;
2610 found_element = element;
2613 if (!CAN_CHANGE(element))
2616 for (i = 0; i < element_info[element].num_change_pages; i++)
2618 /* check for player created from custom element as extended target */
2620 element_info[element].change_page[i].target_content.e[xx][yy];
2622 is_player = ELEM_IS_PLAYER(content);
2624 if (is_player && (found_rating < 1 || element < found_element))
2626 start_x = x + xx - 1;
2627 start_y = y + yy - 1;
2630 found_element = element;
2636 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
2637 start_x > SBX_Right + MIDPOSX ? SBX_Right :
2640 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
2641 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
2646 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2647 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2648 local_player->jx - MIDPOSX);
2650 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2651 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2652 local_player->jy - MIDPOSY);
2657 if (!game.restart_level)
2658 CloseDoor(DOOR_CLOSE_1);
2661 FadeOut(REDRAW_FIELD);
2663 /* !!! FIX THIS (START) !!! */
2664 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2666 InitGameEngine_EM();
2668 /* blit playfield from scroll buffer to normal back buffer for fading in */
2669 BlitScreenToBitmap_EM(backbuffer);
2676 /* after drawing the level, correct some elements */
2677 if (game.timegate_time_left == 0)
2678 CloseAllOpenTimegates();
2680 /* blit playfield from scroll buffer to normal back buffer for fading in */
2681 if (setup.soft_scrolling)
2682 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
2684 redraw_mask |= REDRAW_FROM_BACKBUFFER;
2686 /* !!! FIX THIS (END) !!! */
2689 FadeIn(REDRAW_FIELD);
2693 if (!game.restart_level)
2695 /* copy default game door content to main double buffer */
2696 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2697 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2700 SetPanelBackground();
2701 SetDrawBackgroundMask(REDRAW_DOOR_1);
2703 DrawGameDoorValues();
2705 if (!game.restart_level)
2709 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
2710 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
2711 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
2715 /* copy actual game door content to door double buffer for OpenDoor() */
2716 BlitBitmap(drawto, bitmap_db_door,
2717 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
2719 OpenDoor(DOOR_OPEN_ALL);
2721 PlaySound(SND_GAME_STARTING);
2723 if (setup.sound_music)
2726 KeyboardAutoRepeatOffUnlessAutoplay();
2730 for (i = 0; i < MAX_PLAYERS; i++)
2731 printf("Player %d %sactive.\n",
2732 i + 1, (stored_player[i].active ? "" : "not "));
2743 game.restart_level = FALSE;
2746 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
2748 /* this is used for non-R'n'D game engines to update certain engine values */
2750 /* needed to determine if sounds are played within the visible screen area */
2751 scroll_x = actual_scroll_x;
2752 scroll_y = actual_scroll_y;
2755 void InitMovDir(int x, int y)
2757 int i, element = Feld[x][y];
2758 static int xy[4][2] =
2765 static int direction[3][4] =
2767 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
2768 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
2769 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
2778 Feld[x][y] = EL_BUG;
2779 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
2782 case EL_SPACESHIP_RIGHT:
2783 case EL_SPACESHIP_UP:
2784 case EL_SPACESHIP_LEFT:
2785 case EL_SPACESHIP_DOWN:
2786 Feld[x][y] = EL_SPACESHIP;
2787 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
2790 case EL_BD_BUTTERFLY_RIGHT:
2791 case EL_BD_BUTTERFLY_UP:
2792 case EL_BD_BUTTERFLY_LEFT:
2793 case EL_BD_BUTTERFLY_DOWN:
2794 Feld[x][y] = EL_BD_BUTTERFLY;
2795 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
2798 case EL_BD_FIREFLY_RIGHT:
2799 case EL_BD_FIREFLY_UP:
2800 case EL_BD_FIREFLY_LEFT:
2801 case EL_BD_FIREFLY_DOWN:
2802 Feld[x][y] = EL_BD_FIREFLY;
2803 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
2806 case EL_PACMAN_RIGHT:
2808 case EL_PACMAN_LEFT:
2809 case EL_PACMAN_DOWN:
2810 Feld[x][y] = EL_PACMAN;
2811 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
2814 case EL_YAMYAM_LEFT:
2815 case EL_YAMYAM_RIGHT:
2817 case EL_YAMYAM_DOWN:
2818 Feld[x][y] = EL_YAMYAM;
2819 MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
2822 case EL_SP_SNIKSNAK:
2823 MovDir[x][y] = MV_UP;
2826 case EL_SP_ELECTRON:
2827 MovDir[x][y] = MV_LEFT;
2834 Feld[x][y] = EL_MOLE;
2835 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
2839 if (IS_CUSTOM_ELEMENT(element))
2841 struct ElementInfo *ei = &element_info[element];
2842 int move_direction_initial = ei->move_direction_initial;
2843 int move_pattern = ei->move_pattern;
2845 if (move_direction_initial == MV_START_PREVIOUS)
2847 if (MovDir[x][y] != MV_NONE)
2850 move_direction_initial = MV_START_AUTOMATIC;
2853 if (move_direction_initial == MV_START_RANDOM)
2854 MovDir[x][y] = 1 << RND(4);
2855 else if (move_direction_initial & MV_ANY_DIRECTION)
2856 MovDir[x][y] = move_direction_initial;
2857 else if (move_pattern == MV_ALL_DIRECTIONS ||
2858 move_pattern == MV_TURNING_LEFT ||
2859 move_pattern == MV_TURNING_RIGHT ||
2860 move_pattern == MV_TURNING_LEFT_RIGHT ||
2861 move_pattern == MV_TURNING_RIGHT_LEFT ||
2862 move_pattern == MV_TURNING_RANDOM)
2863 MovDir[x][y] = 1 << RND(4);
2864 else if (move_pattern == MV_HORIZONTAL)
2865 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
2866 else if (move_pattern == MV_VERTICAL)
2867 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
2868 else if (move_pattern & MV_ANY_DIRECTION)
2869 MovDir[x][y] = element_info[element].move_pattern;
2870 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
2871 move_pattern == MV_ALONG_RIGHT_SIDE)
2873 /* use random direction as default start direction */
2874 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2875 MovDir[x][y] = 1 << RND(4);
2877 for (i = 0; i < NUM_DIRECTIONS; i++)
2879 int x1 = x + xy[i][0];
2880 int y1 = y + xy[i][1];
2882 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2884 if (move_pattern == MV_ALONG_RIGHT_SIDE)
2885 MovDir[x][y] = direction[0][i];
2887 MovDir[x][y] = direction[1][i];
2896 MovDir[x][y] = 1 << RND(4);
2898 if (element != EL_BUG &&
2899 element != EL_SPACESHIP &&
2900 element != EL_BD_BUTTERFLY &&
2901 element != EL_BD_FIREFLY)
2904 for (i = 0; i < NUM_DIRECTIONS; i++)
2906 int x1 = x + xy[i][0];
2907 int y1 = y + xy[i][1];
2909 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2911 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
2913 MovDir[x][y] = direction[0][i];
2916 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
2917 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
2919 MovDir[x][y] = direction[1][i];
2928 GfxDir[x][y] = MovDir[x][y];
2931 void InitAmoebaNr(int x, int y)
2934 int group_nr = AmoebeNachbarNr(x, y);
2938 for (i = 1; i < MAX_NUM_AMOEBA; i++)
2940 if (AmoebaCnt[i] == 0)
2948 AmoebaNr[x][y] = group_nr;
2949 AmoebaCnt[group_nr]++;
2950 AmoebaCnt2[group_nr]++;
2953 static void PlayerWins(struct PlayerInfo *player)
2955 player->LevelSolved = TRUE;
2956 player->GameOver = TRUE;
2958 player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2959 level.native_em_level->lev->score : player->score);
2964 static int time, time_final;
2965 static int score, score_final;
2966 static int game_over_delay = 0;
2967 int game_over_delay_value = 50;
2969 if (!local_player->LevelSolved_GameEnd)
2973 /* do not start end game actions before the player stops moving (to exit) */
2974 if (local_player->MovPos)
2977 local_player->LevelSolved_GameEnd = TRUE;
2978 local_player->LevelSolved_SaveTape = tape.recording;
2979 local_player->LevelSolved_SaveScore = !tape.playing;
2981 if (tape.auto_play) /* tape might already be stopped here */
2982 tape.auto_play_level_solved = TRUE;
2988 game_over_delay = game_over_delay_value;
2990 time = time_final = (level.time == 0 ? TimePlayed : TimeLeft);
2991 score = score_final = local_player->score_final;
2996 score_final += TimeLeft * level.score[SC_TIME_BONUS];
2998 else if (level.time == 0 && TimePlayed < 999)
3001 score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
3004 local_player->score_final = score_final;
3006 if (level_editor_test_game)
3009 score = score_final;
3011 DrawGameValue_Time(time);
3012 DrawGameValue_Score(score);
3015 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
3017 if (ExitX >= 0 && ExitY >= 0) /* local player has left the level */
3019 /* close exit door after last player */
3020 if ((AllPlayersGone &&
3021 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
3022 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
3023 Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
3024 Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
3025 Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
3027 int element = Feld[ExitX][ExitY];
3030 if (element == EL_EM_EXIT_OPEN ||
3031 element == EL_EM_STEEL_EXIT_OPEN)
3038 Feld[ExitX][ExitY] =
3039 (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
3040 element == EL_EM_EXIT_OPEN ? EL_EM_EXIT_CLOSING :
3041 element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING:
3042 element == EL_STEEL_EXIT_OPEN ? EL_STEEL_EXIT_CLOSING:
3043 EL_EM_STEEL_EXIT_CLOSING);
3045 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
3049 /* player disappears */
3050 DrawLevelField(ExitX, ExitY);
3053 for (i = 0; i < MAX_PLAYERS; i++)
3055 struct PlayerInfo *player = &stored_player[i];
3057 if (player->present)
3059 RemovePlayer(player);
3061 /* player disappears */
3062 DrawLevelField(player->jx, player->jy);
3067 PlaySound(SND_GAME_WINNING);
3070 if (game_over_delay > 0)
3077 if (time != time_final)
3079 int time_to_go = ABS(time_final - time);
3080 int time_count_dir = (time < time_final ? +1 : -1);
3081 int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
3083 time += time_count_steps * time_count_dir;
3084 score += time_count_steps * level.score[SC_TIME_BONUS];
3086 DrawGameValue_Time(time);
3087 DrawGameValue_Score(score);
3089 if (time == time_final)
3090 StopSound(SND_GAME_LEVELTIME_BONUS);
3091 else if (setup.sound_loops)
3092 PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
3094 PlaySound(SND_GAME_LEVELTIME_BONUS);
3101 boolean raise_level = FALSE;
3103 CloseDoor(DOOR_CLOSE_1);
3105 if (local_player->LevelSolved_SaveTape)
3112 SaveTapeChecked(tape.level_nr); /* ask to save tape */
3114 SaveTape(tape.level_nr); /* ask to save tape */
3118 if (level_editor_test_game)
3120 game_status = GAME_MODE_MAIN;
3127 if (!local_player->LevelSolved_SaveScore)
3129 FadeOut(REDRAW_FIELD);
3131 game_status = GAME_MODE_MAIN;
3133 DrawAndFadeInMainMenu(REDRAW_FIELD);
3138 if (level_nr == leveldir_current->handicap_level)
3140 leveldir_current->handicap_level++;
3141 SaveLevelSetup_SeriesInfo();
3144 if (level_nr < leveldir_current->last_level)
3145 raise_level = TRUE; /* advance to next level */
3147 if ((hi_pos = NewHiScore()) >= 0)
3149 game_status = GAME_MODE_SCORES;
3151 DrawHallOfFame(hi_pos);
3161 FadeOut(REDRAW_FIELD);
3163 game_status = GAME_MODE_MAIN;
3171 DrawAndFadeInMainMenu(REDRAW_FIELD);
3180 LoadScore(level_nr);
3182 if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
3183 local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score)
3186 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
3188 if (local_player->score_final > highscore[k].Score)
3190 /* player has made it to the hall of fame */
3192 if (k < MAX_SCORE_ENTRIES - 1)
3194 int m = MAX_SCORE_ENTRIES - 1;
3197 for (l = k; l < MAX_SCORE_ENTRIES; l++)
3198 if (strEqual(setup.player_name, highscore[l].Name))
3200 if (m == k) /* player's new highscore overwrites his old one */
3204 for (l = m; l > k; l--)
3206 strcpy(highscore[l].Name, highscore[l - 1].Name);
3207 highscore[l].Score = highscore[l - 1].Score;
3214 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
3215 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
3216 highscore[k].Score = local_player->score_final;
3222 else if (!strncmp(setup.player_name, highscore[k].Name,
3223 MAX_PLAYER_NAME_LEN))
3224 break; /* player already there with a higher score */
3230 SaveScore(level_nr);
3235 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
3237 int element = Feld[x][y];
3238 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3239 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
3240 int horiz_move = (dx != 0);
3241 int sign = (horiz_move ? dx : dy);
3242 int step = sign * element_info[element].move_stepsize;
3244 /* special values for move stepsize for spring and things on conveyor belt */
3247 if (CAN_FALL(element) &&
3248 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
3249 step = sign * MOVE_STEPSIZE_NORMAL / 2;
3250 else if (element == EL_SPRING)
3251 step = sign * MOVE_STEPSIZE_NORMAL * 2;
3257 inline static int getElementMoveStepsize(int x, int y)
3259 return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
3262 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
3264 if (player->GfxAction != action || player->GfxDir != dir)
3267 printf("Player frame reset! (%d => %d, %d => %d)\n",
3268 player->GfxAction, action, player->GfxDir, dir);
3271 player->GfxAction = action;
3272 player->GfxDir = dir;
3274 player->StepFrame = 0;
3278 #if USE_GFX_RESET_GFX_ANIMATION
3279 static void ResetGfxFrame(int x, int y, boolean redraw)
3281 int element = Feld[x][y];
3282 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3283 int last_gfx_frame = GfxFrame[x][y];
3285 if (graphic_info[graphic].anim_global_sync)
3286 GfxFrame[x][y] = FrameCounter;
3287 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
3288 GfxFrame[x][y] = CustomValue[x][y];
3289 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
3290 GfxFrame[x][y] = element_info[element].collect_score;
3291 else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
3292 GfxFrame[x][y] = ChangeDelay[x][y];
3294 if (redraw && GfxFrame[x][y] != last_gfx_frame)
3295 DrawLevelGraphicAnimation(x, y, graphic);
3299 static void ResetGfxAnimation(int x, int y)
3301 GfxAction[x][y] = ACTION_DEFAULT;
3302 GfxDir[x][y] = MovDir[x][y];
3305 #if USE_GFX_RESET_GFX_ANIMATION
3306 ResetGfxFrame(x, y, FALSE);
3310 static void ResetRandomAnimationValue(int x, int y)
3312 GfxRandom[x][y] = INIT_GFX_RANDOM();
3315 void InitMovingField(int x, int y, int direction)
3317 int element = Feld[x][y];
3318 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3319 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
3322 boolean is_moving_before, is_moving_after;
3324 boolean continues_moving = (WasJustMoving[x][y] && direction == MovDir[x][y]);
3327 /* check if element was/is moving or being moved before/after mode change */
3329 is_moving_before = WasJustMoving[x][y];
3331 is_moving_before = (getElementMoveStepsizeExt(x, y, MovDir[x][y]) != 0);
3333 is_moving_after = (getElementMoveStepsizeExt(x, y, direction) != 0);
3335 /* reset animation only for moving elements which change direction of moving
3336 or which just started or stopped moving
3337 (else CEs with property "can move" / "not moving" are reset each frame) */
3338 #if USE_GFX_RESET_ONLY_WHEN_MOVING
3340 if (is_moving_before != is_moving_after ||
3341 direction != MovDir[x][y])
3342 ResetGfxAnimation(x, y);
3344 if ((is_moving_before || is_moving_after) && !continues_moving)
3345 ResetGfxAnimation(x, y);
3348 if (!continues_moving)
3349 ResetGfxAnimation(x, y);
3352 MovDir[x][y] = direction;
3353 GfxDir[x][y] = direction;
3355 #if USE_GFX_RESET_ONLY_WHEN_MOVING
3356 GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
3357 direction == MV_DOWN && CAN_FALL(element) ?
3358 ACTION_FALLING : ACTION_MOVING);
3360 GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
3361 ACTION_FALLING : ACTION_MOVING);
3364 /* this is needed for CEs with property "can move" / "not moving" */
3366 if (is_moving_after)
3368 if (Feld[newx][newy] == EL_EMPTY)
3369 Feld[newx][newy] = EL_BLOCKED;
3371 MovDir[newx][newy] = MovDir[x][y];
3373 #if USE_NEW_CUSTOM_VALUE
3374 CustomValue[newx][newy] = CustomValue[x][y];
3377 GfxFrame[newx][newy] = GfxFrame[x][y];
3378 GfxRandom[newx][newy] = GfxRandom[x][y];
3379 GfxAction[newx][newy] = GfxAction[x][y];
3380 GfxDir[newx][newy] = GfxDir[x][y];
3384 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
3386 int direction = MovDir[x][y];
3387 int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
3388 int newy = y + (direction & MV_UP ? -1 : direction & MV_DOWN ? +1 : 0);
3394 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
3396 int oldx = x, oldy = y;
3397 int direction = MovDir[x][y];
3399 if (direction == MV_LEFT)
3401 else if (direction == MV_RIGHT)
3403 else if (direction == MV_UP)
3405 else if (direction == MV_DOWN)
3408 *comes_from_x = oldx;
3409 *comes_from_y = oldy;
3412 int MovingOrBlocked2Element(int x, int y)
3414 int element = Feld[x][y];
3416 if (element == EL_BLOCKED)
3420 Blocked2Moving(x, y, &oldx, &oldy);
3421 return Feld[oldx][oldy];
3427 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
3429 /* like MovingOrBlocked2Element(), but if element is moving
3430 and (x,y) is the field the moving element is just leaving,
3431 return EL_BLOCKED instead of the element value */
3432 int element = Feld[x][y];
3434 if (IS_MOVING(x, y))
3436 if (element == EL_BLOCKED)
3440 Blocked2Moving(x, y, &oldx, &oldy);
3441 return Feld[oldx][oldy];
3450 static void RemoveField(int x, int y)
3452 Feld[x][y] = EL_EMPTY;
3458 #if USE_NEW_CUSTOM_VALUE
3459 CustomValue[x][y] = 0;
3463 ChangeDelay[x][y] = 0;
3464 ChangePage[x][y] = -1;
3465 Pushed[x][y] = FALSE;
3468 ExplodeField[x][y] = EX_TYPE_NONE;
3471 GfxElement[x][y] = EL_UNDEFINED;
3472 GfxAction[x][y] = ACTION_DEFAULT;
3473 GfxDir[x][y] = MV_NONE;
3476 void RemoveMovingField(int x, int y)
3478 int oldx = x, oldy = y, newx = x, newy = y;
3479 int element = Feld[x][y];
3480 int next_element = EL_UNDEFINED;
3482 if (element != EL_BLOCKED && !IS_MOVING(x, y))
3485 if (IS_MOVING(x, y))
3487 Moving2Blocked(x, y, &newx, &newy);
3489 if (Feld[newx][newy] != EL_BLOCKED)
3491 /* element is moving, but target field is not free (blocked), but
3492 already occupied by something different (example: acid pool);
3493 in this case, only remove the moving field, but not the target */
3495 RemoveField(oldx, oldy);
3497 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
3499 DrawLevelField(oldx, oldy);
3504 else if (element == EL_BLOCKED)
3506 Blocked2Moving(x, y, &oldx, &oldy);
3507 if (!IS_MOVING(oldx, oldy))
3511 if (element == EL_BLOCKED &&
3512 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
3513 Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
3514 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
3515 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
3516 Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
3517 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
3518 next_element = get_next_element(Feld[oldx][oldy]);
3520 RemoveField(oldx, oldy);
3521 RemoveField(newx, newy);
3523 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
3525 if (next_element != EL_UNDEFINED)
3526 Feld[oldx][oldy] = next_element;
3528 DrawLevelField(oldx, oldy);
3529 DrawLevelField(newx, newy);
3532 void DrawDynamite(int x, int y)
3534 int sx = SCREENX(x), sy = SCREENY(y);
3535 int graphic = el2img(Feld[x][y]);
3538 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
3541 if (IS_WALKABLE_INSIDE(Back[x][y]))
3545 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
3546 else if (Store[x][y])
3547 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
3549 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
3551 if (Back[x][y] || Store[x][y])
3552 DrawGraphicThruMask(sx, sy, graphic, frame);
3554 DrawGraphic(sx, sy, graphic, frame);
3557 void CheckDynamite(int x, int y)
3559 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
3563 if (MovDelay[x][y] != 0)
3566 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
3572 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
3577 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
3579 boolean num_checked_players = 0;
3582 for (i = 0; i < MAX_PLAYERS; i++)
3584 if (stored_player[i].active)
3586 int sx = stored_player[i].jx;
3587 int sy = stored_player[i].jy;
3589 if (num_checked_players == 0)
3596 *sx1 = MIN(*sx1, sx);
3597 *sy1 = MIN(*sy1, sy);
3598 *sx2 = MAX(*sx2, sx);
3599 *sy2 = MAX(*sy2, sy);
3602 num_checked_players++;
3607 static boolean checkIfAllPlayersFitToScreen_RND()
3609 int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
3611 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
3613 return (sx2 - sx1 < SCR_FIELDX &&
3614 sy2 - sy1 < SCR_FIELDY);
3617 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
3619 int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
3621 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
3623 *sx = (sx1 + sx2) / 2;
3624 *sy = (sy1 + sy2) / 2;
3627 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
3628 boolean center_screen, boolean quick_relocation)
3630 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3631 boolean no_delay = (tape.warp_forward);
3632 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
3633 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
3635 if (quick_relocation)
3637 int offset = (setup.scroll_delay ? 3 : 0);
3639 if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
3643 scroll_x = (x < SBX_Left + MIDPOSX ? SBX_Left :
3644 x > SBX_Right + MIDPOSX ? SBX_Right :
3647 scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
3648 y > SBY_Lower + MIDPOSY ? SBY_Lower :
3653 /* quick relocation (without scrolling), but do not center screen */
3655 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
3656 old_x > SBX_Right + MIDPOSX ? SBX_Right :
3659 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
3660 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
3663 int offset_x = x + (scroll_x - center_scroll_x);
3664 int offset_y = y + (scroll_y - center_scroll_y);
3666 scroll_x = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
3667 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
3668 offset_x - MIDPOSX);
3670 scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
3671 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
3672 offset_y - MIDPOSY);
3677 if ((move_dir == MV_LEFT && scroll_x > x - MIDPOSX + offset) ||
3678 (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
3679 scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
3681 if ((move_dir == MV_UP && scroll_y > y - MIDPOSY + offset) ||
3682 (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
3683 scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
3685 /* don't scroll over playfield boundaries */
3686 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
3687 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
3689 /* don't scroll over playfield boundaries */
3690 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
3691 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
3694 RedrawPlayfield(TRUE, 0,0,0,0);
3698 int scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left :
3699 x > SBX_Right + MIDPOSX ? SBX_Right :
3702 int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
3703 y > SBY_Lower + MIDPOSY ? SBY_Lower :
3706 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
3708 while (scroll_x != scroll_xx || scroll_y != scroll_yy)
3711 int fx = FX, fy = FY;
3713 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
3714 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
3716 if (dx == 0 && dy == 0) /* no scrolling needed at all */
3722 fx += dx * TILEX / 2;
3723 fy += dy * TILEY / 2;
3725 ScrollLevel(dx, dy);
3728 /* scroll in two steps of half tile size to make things smoother */
3729 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
3731 Delay(wait_delay_value);
3733 /* scroll second step to align at full tile size */
3735 Delay(wait_delay_value);
3740 Delay(wait_delay_value);
3744 void RelocatePlayer(int jx, int jy, int el_player_raw)
3746 int el_player = GET_PLAYER_ELEMENT(el_player_raw);
3747 int player_nr = GET_PLAYER_NR(el_player);
3748 struct PlayerInfo *player = &stored_player[player_nr];
3749 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3750 boolean no_delay = (tape.warp_forward);
3751 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
3752 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
3753 int old_jx = player->jx;
3754 int old_jy = player->jy;
3755 int old_element = Feld[old_jx][old_jy];
3756 int element = Feld[jx][jy];
3757 boolean player_relocated = (old_jx != jx || old_jy != jy);
3759 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
3760 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
3761 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
3762 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
3763 int leave_side_horiz = move_dir_horiz;
3764 int leave_side_vert = move_dir_vert;
3765 int enter_side = enter_side_horiz | enter_side_vert;
3766 int leave_side = leave_side_horiz | leave_side_vert;
3768 if (player->GameOver) /* do not reanimate dead player */
3771 if (!player_relocated) /* no need to relocate the player */
3774 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
3776 RemoveField(jx, jy); /* temporarily remove newly placed player */
3777 DrawLevelField(jx, jy);
3780 if (player->present)
3782 while (player->MovPos)
3784 ScrollPlayer(player, SCROLL_GO_ON);
3785 ScrollScreen(NULL, SCROLL_GO_ON);
3787 AdvanceFrameAndPlayerCounters(player->index_nr);
3792 Delay(wait_delay_value);
3795 DrawPlayer(player); /* needed here only to cleanup last field */
3796 DrawLevelField(player->jx, player->jy); /* remove player graphic */
3798 player->is_moving = FALSE;
3801 if (IS_CUSTOM_ELEMENT(old_element))
3802 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
3804 player->index_bit, leave_side);
3806 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
3808 player->index_bit, leave_side);
3810 Feld[jx][jy] = el_player;
3811 InitPlayerField(jx, jy, el_player, TRUE);
3813 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
3815 Feld[jx][jy] = element;
3816 InitField(jx, jy, FALSE);
3819 /* only visually relocate centered player */
3820 DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
3821 FALSE, level.instant_relocation);
3823 TestIfPlayerTouchesBadThing(jx, jy);
3824 TestIfPlayerTouchesCustomElement(jx, jy);
3826 if (IS_CUSTOM_ELEMENT(element))
3827 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
3828 player->index_bit, enter_side);
3830 CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
3831 player->index_bit, enter_side);
3834 void Explode(int ex, int ey, int phase, int mode)
3840 /* !!! eliminate this variable !!! */
3841 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
3843 if (game.explosions_delayed)
3845 ExplodeField[ex][ey] = mode;
3849 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
3851 int center_element = Feld[ex][ey];
3852 int artwork_element, explosion_element; /* set these values later */
3855 /* --- This is only really needed (and now handled) in "Impact()". --- */
3856 /* do not explode moving elements that left the explode field in time */
3857 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
3858 center_element == EL_EMPTY &&
3859 (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
3864 /* !!! at this place, the center element may be EL_BLOCKED !!! */
3865 if (mode == EX_TYPE_NORMAL ||
3866 mode == EX_TYPE_CENTER ||
3867 mode == EX_TYPE_CROSS)
3868 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
3871 /* remove things displayed in background while burning dynamite */
3872 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
3875 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
3877 /* put moving element to center field (and let it explode there) */
3878 center_element = MovingOrBlocked2Element(ex, ey);
3879 RemoveMovingField(ex, ey);
3880 Feld[ex][ey] = center_element;
3883 /* now "center_element" is finally determined -- set related values now */
3884 artwork_element = center_element; /* for custom player artwork */
3885 explosion_element = center_element; /* for custom player artwork */
3887 if (IS_PLAYER(ex, ey))
3889 int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
3891 artwork_element = stored_player[player_nr].artwork_element;
3893 if (level.use_explosion_element[player_nr])
3895 explosion_element = level.explosion_element[player_nr];
3896 artwork_element = explosion_element;
3901 if (mode == EX_TYPE_NORMAL ||
3902 mode == EX_TYPE_CENTER ||
3903 mode == EX_TYPE_CROSS)
3904 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
3907 last_phase = element_info[explosion_element].explosion_delay + 1;
3909 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
3911 int xx = x - ex + 1;
3912 int yy = y - ey + 1;
3915 if (!IN_LEV_FIELD(x, y) ||
3916 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
3917 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
3920 element = Feld[x][y];
3922 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
3924 element = MovingOrBlocked2Element(x, y);
3926 if (!IS_EXPLOSION_PROOF(element))
3927 RemoveMovingField(x, y);
3930 /* indestructible elements can only explode in center (but not flames) */
3931 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
3932 mode == EX_TYPE_BORDER)) ||
3933 element == EL_FLAMES)
3936 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
3937 behaviour, for example when touching a yamyam that explodes to rocks
3938 with active deadly shield, a rock is created under the player !!! */
3939 /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
3941 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
3942 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
3943 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
3945 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
3948 if (IS_ACTIVE_BOMB(element))
3950 /* re-activate things under the bomb like gate or penguin */
3951 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
3958 /* save walkable background elements while explosion on same tile */
3959 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
3960 (x != ex || y != ey || mode == EX_TYPE_BORDER))
3961 Back[x][y] = element;
3963 /* ignite explodable elements reached by other explosion */
3964 if (element == EL_EXPLOSION)
3965 element = Store2[x][y];
3967 if (AmoebaNr[x][y] &&
3968 (element == EL_AMOEBA_FULL ||
3969 element == EL_BD_AMOEBA ||
3970 element == EL_AMOEBA_GROWING))
3972 AmoebaCnt[AmoebaNr[x][y]]--;
3973 AmoebaCnt2[AmoebaNr[x][y]]--;
3978 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
3980 int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
3982 Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
3984 if (PLAYERINFO(ex, ey)->use_murphy)
3985 Store[x][y] = EL_EMPTY;
3988 /* !!! check this case -- currently needed for rnd_rado_negundo_v,
3989 !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
3990 else if (ELEM_IS_PLAYER(center_element))
3991 Store[x][y] = EL_EMPTY;
3992 else if (center_element == EL_YAMYAM)
3993 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
3994 else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
3995 Store[x][y] = element_info[center_element].content.e[xx][yy];
3997 /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
3998 (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
3999 otherwise) -- FIX THIS !!! */
4000 else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
4001 Store[x][y] = element_info[element].content.e[1][1];
4003 else if (!CAN_EXPLODE(element))
4004 Store[x][y] = element_info[element].content.e[1][1];
4007 Store[x][y] = EL_EMPTY;
4009 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
4010 center_element == EL_AMOEBA_TO_DIAMOND)
4011 Store2[x][y] = element;
4013 Feld[x][y] = EL_EXPLOSION;
4014 GfxElement[x][y] = artwork_element;
4016 ExplodePhase[x][y] = 1;
4017 ExplodeDelay[x][y] = last_phase;
4022 if (center_element == EL_YAMYAM)
4023 game.yamyam_content_nr =
4024 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
4036 GfxFrame[x][y] = 0; /* restart explosion animation */
4038 last_phase = ExplodeDelay[x][y];
4040 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
4044 /* activate this even in non-DEBUG version until cause for crash in
4045 getGraphicAnimationFrame() (see below) is found and eliminated */
4051 /* this can happen if the player leaves an explosion just in time */
4052 if (GfxElement[x][y] == EL_UNDEFINED)
4053 GfxElement[x][y] = EL_EMPTY;
4055 if (GfxElement[x][y] == EL_UNDEFINED)
4058 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
4059 printf("Explode(): This should never happen!\n");
4062 GfxElement[x][y] = EL_EMPTY;
4068 border_element = Store2[x][y];
4069 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
4070 border_element = StorePlayer[x][y];
4072 if (phase == element_info[border_element].ignition_delay ||
4073 phase == last_phase)
4075 boolean border_explosion = FALSE;
4077 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
4078 !PLAYER_EXPLOSION_PROTECTED(x, y))
4080 KillPlayerUnlessExplosionProtected(x, y);
4081 border_explosion = TRUE;
4083 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
4085 Feld[x][y] = Store2[x][y];
4088 border_explosion = TRUE;
4090 else if (border_element == EL_AMOEBA_TO_DIAMOND)
4092 AmoebeUmwandeln(x, y);
4094 border_explosion = TRUE;
4097 /* if an element just explodes due to another explosion (chain-reaction),
4098 do not immediately end the new explosion when it was the last frame of
4099 the explosion (as it would be done in the following "if"-statement!) */
4100 if (border_explosion && phase == last_phase)
4104 if (phase == last_phase)
4108 element = Feld[x][y] = Store[x][y];
4109 Store[x][y] = Store2[x][y] = 0;
4110 GfxElement[x][y] = EL_UNDEFINED;
4112 /* player can escape from explosions and might therefore be still alive */
4113 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
4114 element <= EL_PLAYER_IS_EXPLODING_4)
4116 int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
4117 int explosion_element = EL_PLAYER_1 + player_nr;
4118 int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
4119 int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
4121 if (level.use_explosion_element[player_nr])
4122 explosion_element = level.explosion_element[player_nr];
4124 Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
4125 element_info[explosion_element].content.e[xx][yy]);
4128 /* restore probably existing indestructible background element */
4129 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
4130 element = Feld[x][y] = Back[x][y];
4133 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
4134 GfxDir[x][y] = MV_NONE;
4135 ChangeDelay[x][y] = 0;
4136 ChangePage[x][y] = -1;
4138 #if USE_NEW_CUSTOM_VALUE
4139 CustomValue[x][y] = 0;
4142 InitField_WithBug2(x, y, FALSE);
4144 DrawLevelField(x, y);
4146 TestIfElementTouchesCustomElement(x, y);
4148 if (GFX_CRUMBLED(element))
4149 DrawLevelFieldCrumbledSandNeighbours(x, y);
4151 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
4152 StorePlayer[x][y] = 0;
4154 if (ELEM_IS_PLAYER(element))
4155 RelocatePlayer(x, y, element);
4157 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
4159 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
4160 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4163 DrawLevelFieldCrumbledSand(x, y);
4165 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
4167 DrawLevelElement(x, y, Back[x][y]);
4168 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
4170 else if (IS_WALKABLE_UNDER(Back[x][y]))
4172 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
4173 DrawLevelElementThruMask(x, y, Back[x][y]);
4175 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
4176 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
4180 void DynaExplode(int ex, int ey)
4183 int dynabomb_element = Feld[ex][ey];
4184 int dynabomb_size = 1;
4185 boolean dynabomb_xl = FALSE;
4186 struct PlayerInfo *player;
4187 static int xy[4][2] =
4195 if (IS_ACTIVE_BOMB(dynabomb_element))
4197 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
4198 dynabomb_size = player->dynabomb_size;
4199 dynabomb_xl = player->dynabomb_xl;
4200 player->dynabombs_left++;
4203 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
4205 for (i = 0; i < NUM_DIRECTIONS; i++)
4207 for (j = 1; j <= dynabomb_size; j++)
4209 int x = ex + j * xy[i][0];
4210 int y = ey + j * xy[i][1];
4213 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
4216 element = Feld[x][y];
4218 /* do not restart explosions of fields with active bombs */
4219 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
4222 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
4224 if (element != EL_EMPTY && element != EL_EXPLOSION &&
4225 !IS_DIGGABLE(element) && !dynabomb_xl)
4231 void Bang(int x, int y)
4233 int element = MovingOrBlocked2Element(x, y);
4234 int explosion_type = EX_TYPE_NORMAL;
4236 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
4238 struct PlayerInfo *player = PLAYERINFO(x, y);
4240 element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
4241 player->element_nr);
4243 if (level.use_explosion_element[player->index_nr])
4245 int explosion_element = level.explosion_element[player->index_nr];
4247 if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
4248 explosion_type = EX_TYPE_CROSS;
4249 else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
4250 explosion_type = EX_TYPE_CENTER;
4258 case EL_BD_BUTTERFLY:
4261 case EL_DARK_YAMYAM:
4265 RaiseScoreElement(element);
4268 case EL_DYNABOMB_PLAYER_1_ACTIVE:
4269 case EL_DYNABOMB_PLAYER_2_ACTIVE:
4270 case EL_DYNABOMB_PLAYER_3_ACTIVE:
4271 case EL_DYNABOMB_PLAYER_4_ACTIVE:
4272 case EL_DYNABOMB_INCREASE_NUMBER:
4273 case EL_DYNABOMB_INCREASE_SIZE:
4274 case EL_DYNABOMB_INCREASE_POWER:
4275 explosion_type = EX_TYPE_DYNA;
4278 case EL_DC_LANDMINE:
4280 case EL_EM_EXIT_OPEN:
4281 case EL_EM_STEEL_EXIT_OPEN:
4283 explosion_type = EX_TYPE_CENTER;
4288 case EL_LAMP_ACTIVE:
4289 case EL_AMOEBA_TO_DIAMOND:
4290 if (!IS_PLAYER(x, y)) /* penguin and player may be at same field */
4291 explosion_type = EX_TYPE_CENTER;
4295 if (element_info[element].explosion_type == EXPLODES_CROSS)
4296 explosion_type = EX_TYPE_CROSS;
4297 else if (element_info[element].explosion_type == EXPLODES_1X1)
4298 explosion_type = EX_TYPE_CENTER;
4302 if (explosion_type == EX_TYPE_DYNA)
4305 Explode(x, y, EX_PHASE_START, explosion_type);
4307 CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
4310 void SplashAcid(int x, int y)
4312 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
4313 (!IN_LEV_FIELD(x - 1, y - 2) ||
4314 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
4315 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
4317 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
4318 (!IN_LEV_FIELD(x + 1, y - 2) ||
4319 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
4320 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
4322 PlayLevelSound(x, y, SND_ACID_SPLASHING);
4325 static void InitBeltMovement()
4327 static int belt_base_element[4] =
4329 EL_CONVEYOR_BELT_1_LEFT,
4330 EL_CONVEYOR_BELT_2_LEFT,
4331 EL_CONVEYOR_BELT_3_LEFT,
4332 EL_CONVEYOR_BELT_4_LEFT
4334 static int belt_base_active_element[4] =
4336 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
4337 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
4338 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
4339 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
4344 /* set frame order for belt animation graphic according to belt direction */
4345 for (i = 0; i < NUM_BELTS; i++)
4349 for (j = 0; j < NUM_BELT_PARTS; j++)
4351 int element = belt_base_active_element[belt_nr] + j;
4352 int graphic = el2img(element);
4354 if (game.belt_dir[i] == MV_LEFT)
4355 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
4357 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
4361 SCAN_PLAYFIELD(x, y)
4363 int element = Feld[x][y];
4365 for (i = 0; i < NUM_BELTS; i++)
4367 if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
4369 int e_belt_nr = getBeltNrFromBeltElement(element);
4372 if (e_belt_nr == belt_nr)
4374 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
4376 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
4383 static void ToggleBeltSwitch(int x, int y)
4385 static int belt_base_element[4] =
4387 EL_CONVEYOR_BELT_1_LEFT,
4388 EL_CONVEYOR_BELT_2_LEFT,
4389 EL_CONVEYOR_BELT_3_LEFT,
4390 EL_CONVEYOR_BELT_4_LEFT
4392 static int belt_base_active_element[4] =
4394 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
4395 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
4396 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
4397 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
4399 static int belt_base_switch_element[4] =
4401 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
4402 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
4403 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
4404 EL_CONVEYOR_BELT_4_SWITCH_LEFT
4406 static int belt_move_dir[4] =
4414 int element = Feld[x][y];
4415 int belt_nr = getBeltNrFromBeltSwitchElement(element);
4416 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
4417 int belt_dir = belt_move_dir[belt_dir_nr];
4420 if (!IS_BELT_SWITCH(element))
4423 game.belt_dir_nr[belt_nr] = belt_dir_nr;
4424 game.belt_dir[belt_nr] = belt_dir;
4426 if (belt_dir_nr == 3)
4429 /* set frame order for belt animation graphic according to belt direction */
4430 for (i = 0; i < NUM_BELT_PARTS; i++)
4432 int element = belt_base_active_element[belt_nr] + i;
4433 int graphic = el2img(element);
4435 if (belt_dir == MV_LEFT)
4436 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
4438 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
4441 SCAN_PLAYFIELD(xx, yy)
4443 int element = Feld[xx][yy];
4445 if (IS_BELT_SWITCH(element))
4447 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
4449 if (e_belt_nr == belt_nr)
4451 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
4452 DrawLevelField(xx, yy);
4455 else if (IS_BELT(element) && belt_dir != MV_NONE)
4457 int e_belt_nr = getBeltNrFromBeltElement(element);
4459 if (e_belt_nr == belt_nr)
4461 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
4463 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
4464 DrawLevelField(xx, yy);
4467 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
4469 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
4471 if (e_belt_nr == belt_nr)
4473 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
4475 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
4476 DrawLevelField(xx, yy);
4482 static void ToggleSwitchgateSwitch(int x, int y)
4486 game.switchgate_pos = !game.switchgate_pos;
4488 SCAN_PLAYFIELD(xx, yy)
4490 int element = Feld[xx][yy];
4492 #if !USE_BOTH_SWITCHGATE_SWITCHES
4493 if (element == EL_SWITCHGATE_SWITCH_UP ||
4494 element == EL_SWITCHGATE_SWITCH_DOWN)
4496 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
4497 DrawLevelField(xx, yy);
4499 else if (element == EL_DC_SWITCHGATE_SWITCH_UP ||
4500 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
4502 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
4503 DrawLevelField(xx, yy);
4506 if (element == EL_SWITCHGATE_SWITCH_UP)
4508 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
4509 DrawLevelField(xx, yy);
4511 else if (element == EL_SWITCHGATE_SWITCH_DOWN)
4513 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
4514 DrawLevelField(xx, yy);
4516 else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
4518 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
4519 DrawLevelField(xx, yy);
4521 else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
4523 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
4524 DrawLevelField(xx, yy);
4527 else if (element == EL_SWITCHGATE_OPEN ||
4528 element == EL_SWITCHGATE_OPENING)
4530 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
4532 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
4534 else if (element == EL_SWITCHGATE_CLOSED ||
4535 element == EL_SWITCHGATE_CLOSING)
4537 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
4539 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
4544 static int getInvisibleActiveFromInvisibleElement(int element)
4546 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
4547 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
4548 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
4552 static int getInvisibleFromInvisibleActiveElement(int element)
4554 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
4555 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
4556 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
4560 static void RedrawAllLightSwitchesAndInvisibleElements()
4564 SCAN_PLAYFIELD(x, y)
4566 int element = Feld[x][y];
4568 if (element == EL_LIGHT_SWITCH &&
4569 game.light_time_left > 0)
4571 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
4572 DrawLevelField(x, y);
4574 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
4575 game.light_time_left == 0)
4577 Feld[x][y] = EL_LIGHT_SWITCH;
4578 DrawLevelField(x, y);
4580 else if (element == EL_EMC_DRIPPER &&
4581 game.light_time_left > 0)
4583 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
4584 DrawLevelField(x, y);
4586 else if (element == EL_EMC_DRIPPER_ACTIVE &&
4587 game.light_time_left == 0)
4589 Feld[x][y] = EL_EMC_DRIPPER;
4590 DrawLevelField(x, y);
4592 else if (element == EL_INVISIBLE_STEELWALL ||
4593 element == EL_INVISIBLE_WALL ||
4594 element == EL_INVISIBLE_SAND)
4596 if (game.light_time_left > 0)
4597 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
4599 DrawLevelField(x, y);
4601 /* uncrumble neighbour fields, if needed */
4602 if (element == EL_INVISIBLE_SAND)
4603 DrawLevelFieldCrumbledSandNeighbours(x, y);
4605 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
4606 element == EL_INVISIBLE_WALL_ACTIVE ||
4607 element == EL_INVISIBLE_SAND_ACTIVE)
4609 if (game.light_time_left == 0)
4610 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
4612 DrawLevelField(x, y);
4614 /* re-crumble neighbour fields, if needed */
4615 if (element == EL_INVISIBLE_SAND)
4616 DrawLevelFieldCrumbledSandNeighbours(x, y);
4621 static void RedrawAllInvisibleElementsForLenses()
4625 SCAN_PLAYFIELD(x, y)
4627 int element = Feld[x][y];
4629 if (element == EL_EMC_DRIPPER &&
4630 game.lenses_time_left > 0)
4632 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
4633 DrawLevelField(x, y);
4635 else if (element == EL_EMC_DRIPPER_ACTIVE &&
4636 game.lenses_time_left == 0)
4638 Feld[x][y] = EL_EMC_DRIPPER;
4639 DrawLevelField(x, y);
4641 else if (element == EL_INVISIBLE_STEELWALL ||
4642 element == EL_INVISIBLE_WALL ||
4643 element == EL_INVISIBLE_SAND)
4645 if (game.lenses_time_left > 0)
4646 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
4648 DrawLevelField(x, y);
4650 /* uncrumble neighbour fields, if needed */
4651 if (element == EL_INVISIBLE_SAND)
4652 DrawLevelFieldCrumbledSandNeighbours(x, y);
4654 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
4655 element == EL_INVISIBLE_WALL_ACTIVE ||
4656 element == EL_INVISIBLE_SAND_ACTIVE)
4658 if (game.lenses_time_left == 0)
4659 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
4661 DrawLevelField(x, y);
4663 /* re-crumble neighbour fields, if needed */
4664 if (element == EL_INVISIBLE_SAND)
4665 DrawLevelFieldCrumbledSandNeighbours(x, y);
4670 static void RedrawAllInvisibleElementsForMagnifier()
4674 SCAN_PLAYFIELD(x, y)
4676 int element = Feld[x][y];
4678 if (element == EL_EMC_FAKE_GRASS &&
4679 game.magnify_time_left > 0)
4681 Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
4682 DrawLevelField(x, y);
4684 else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
4685 game.magnify_time_left == 0)
4687 Feld[x][y] = EL_EMC_FAKE_GRASS;
4688 DrawLevelField(x, y);
4690 else if (IS_GATE_GRAY(element) &&
4691 game.magnify_time_left > 0)
4693 Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
4694 element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
4695 IS_EM_GATE_GRAY(element) ?
4696 element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
4697 IS_EMC_GATE_GRAY(element) ?
4698 element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
4700 DrawLevelField(x, y);
4702 else if (IS_GATE_GRAY_ACTIVE(element) &&
4703 game.magnify_time_left == 0)
4705 Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
4706 element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
4707 IS_EM_GATE_GRAY_ACTIVE(element) ?
4708 element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
4709 IS_EMC_GATE_GRAY_ACTIVE(element) ?
4710 element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
4712 DrawLevelField(x, y);
4717 static void ToggleLightSwitch(int x, int y)
4719 int element = Feld[x][y];
4721 game.light_time_left =
4722 (element == EL_LIGHT_SWITCH ?
4723 level.time_light * FRAMES_PER_SECOND : 0);
4725 RedrawAllLightSwitchesAndInvisibleElements();
4728 static void ActivateTimegateSwitch(int x, int y)
4732 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
4734 SCAN_PLAYFIELD(xx, yy)
4736 int element = Feld[xx][yy];
4738 if (element == EL_TIMEGATE_CLOSED ||
4739 element == EL_TIMEGATE_CLOSING)
4741 Feld[xx][yy] = EL_TIMEGATE_OPENING;
4742 PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
4746 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
4748 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
4749 DrawLevelField(xx, yy);
4756 Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
4757 EL_DC_TIMEGATE_SWITCH_ACTIVE);
4759 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
4763 void Impact(int x, int y)
4765 boolean last_line = (y == lev_fieldy - 1);
4766 boolean object_hit = FALSE;
4767 boolean impact = (last_line || object_hit);
4768 int element = Feld[x][y];
4769 int smashed = EL_STEELWALL;
4771 if (!last_line) /* check if element below was hit */
4773 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
4776 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
4777 MovDir[x][y + 1] != MV_DOWN ||
4778 MovPos[x][y + 1] <= TILEY / 2));
4780 /* do not smash moving elements that left the smashed field in time */
4781 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
4782 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
4785 #if USE_QUICKSAND_IMPACT_BUGFIX
4786 if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
4788 RemoveMovingField(x, y + 1);
4789 Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
4790 Feld[x][y + 2] = EL_ROCK;
4791 DrawLevelField(x, y + 2);
4796 if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
4798 RemoveMovingField(x, y + 1);
4799 Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
4800 Feld[x][y + 2] = EL_ROCK;
4801 DrawLevelField(x, y + 2);
4808 smashed = MovingOrBlocked2Element(x, y + 1);
4810 impact = (last_line || object_hit);
4813 if (!last_line && smashed == EL_ACID) /* element falls into acid */
4815 SplashAcid(x, y + 1);
4819 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
4820 /* only reset graphic animation if graphic really changes after impact */
4822 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
4824 ResetGfxAnimation(x, y);
4825 DrawLevelField(x, y);
4828 if (impact && CAN_EXPLODE_IMPACT(element))
4833 else if (impact && element == EL_PEARL &&
4834 smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
4836 ResetGfxAnimation(x, y);
4838 Feld[x][y] = EL_PEARL_BREAKING;
4839 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4842 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
4844 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4849 if (impact && element == EL_AMOEBA_DROP)
4851 if (object_hit && IS_PLAYER(x, y + 1))
4852 KillPlayerUnlessEnemyProtected(x, y + 1);
4853 else if (object_hit && smashed == EL_PENGUIN)
4857 Feld[x][y] = EL_AMOEBA_GROWING;
4858 Store[x][y] = EL_AMOEBA_WET;
4860 ResetRandomAnimationValue(x, y);
4865 if (object_hit) /* check which object was hit */
4867 if ((CAN_PASS_MAGIC_WALL(element) &&
4868 (smashed == EL_MAGIC_WALL ||
4869 smashed == EL_BD_MAGIC_WALL)) ||
4870 (CAN_PASS_DC_MAGIC_WALL(element) &&
4871 smashed == EL_DC_MAGIC_WALL))
4874 int activated_magic_wall =
4875 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
4876 smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
4877 EL_DC_MAGIC_WALL_ACTIVE);
4879 /* activate magic wall / mill */
4880 SCAN_PLAYFIELD(xx, yy)
4882 if (Feld[xx][yy] == smashed)
4883 Feld[xx][yy] = activated_magic_wall;
4886 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
4887 game.magic_wall_active = TRUE;
4889 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
4890 SND_MAGIC_WALL_ACTIVATING :
4891 smashed == EL_BD_MAGIC_WALL ?
4892 SND_BD_MAGIC_WALL_ACTIVATING :
4893 SND_DC_MAGIC_WALL_ACTIVATING));
4896 if (IS_PLAYER(x, y + 1))
4898 if (CAN_SMASH_PLAYER(element))
4900 KillPlayerUnlessEnemyProtected(x, y + 1);
4904 else if (smashed == EL_PENGUIN)
4906 if (CAN_SMASH_PLAYER(element))
4912 else if (element == EL_BD_DIAMOND)
4914 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
4920 else if (((element == EL_SP_INFOTRON ||
4921 element == EL_SP_ZONK) &&
4922 (smashed == EL_SP_SNIKSNAK ||
4923 smashed == EL_SP_ELECTRON ||
4924 smashed == EL_SP_DISK_ORANGE)) ||
4925 (element == EL_SP_INFOTRON &&
4926 smashed == EL_SP_DISK_YELLOW))
4931 else if (CAN_SMASH_EVERYTHING(element))
4933 if (IS_CLASSIC_ENEMY(smashed) ||
4934 CAN_EXPLODE_SMASHED(smashed))
4939 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
4941 if (smashed == EL_LAMP ||
4942 smashed == EL_LAMP_ACTIVE)
4947 else if (smashed == EL_NUT)
4949 Feld[x][y + 1] = EL_NUT_BREAKING;
4950 PlayLevelSound(x, y, SND_NUT_BREAKING);
4951 RaiseScoreElement(EL_NUT);
4954 else if (smashed == EL_PEARL)
4956 ResetGfxAnimation(x, y);
4958 Feld[x][y + 1] = EL_PEARL_BREAKING;
4959 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4962 else if (smashed == EL_DIAMOND)
4964 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
4965 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
4968 else if (IS_BELT_SWITCH(smashed))
4970 ToggleBeltSwitch(x, y + 1);
4972 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
4973 smashed == EL_SWITCHGATE_SWITCH_DOWN ||
4974 smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
4975 smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
4977 ToggleSwitchgateSwitch(x, y + 1);
4979 else if (smashed == EL_LIGHT_SWITCH ||
4980 smashed == EL_LIGHT_SWITCH_ACTIVE)
4982 ToggleLightSwitch(x, y + 1);
4987 TestIfElementSmashesCustomElement(x, y, MV_DOWN);
4990 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4992 CheckElementChangeBySide(x, y + 1, smashed, element,
4993 CE_SWITCHED, CH_SIDE_TOP);
4994 CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
5000 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
5005 /* play sound of magic wall / mill */
5007 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
5008 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
5009 Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
5011 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
5012 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
5013 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
5014 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
5015 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
5016 PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
5021 /* play sound of object that hits the ground */
5022 if (last_line || object_hit)
5023 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
5026 inline static void TurnRoundExt(int x, int y)
5038 { 0, 0 }, { 0, 0 }, { 0, 0 },
5043 int left, right, back;
5047 { MV_DOWN, MV_UP, MV_RIGHT },
5048 { MV_UP, MV_DOWN, MV_LEFT },
5050 { MV_LEFT, MV_RIGHT, MV_DOWN },
5054 { MV_RIGHT, MV_LEFT, MV_UP }
5057 int element = Feld[x][y];
5058 int move_pattern = element_info[element].move_pattern;
5060 int old_move_dir = MovDir[x][y];
5061 int left_dir = turn[old_move_dir].left;
5062 int right_dir = turn[old_move_dir].right;
5063 int back_dir = turn[old_move_dir].back;
5065 int left_dx = move_xy[left_dir].dx, left_dy = move_xy[left_dir].dy;
5066 int right_dx = move_xy[right_dir].dx, right_dy = move_xy[right_dir].dy;
5067 int move_dx = move_xy[old_move_dir].dx, move_dy = move_xy[old_move_dir].dy;
5068 int back_dx = move_xy[back_dir].dx, back_dy = move_xy[back_dir].dy;
5070 int left_x = x + left_dx, left_y = y + left_dy;
5071 int right_x = x + right_dx, right_y = y + right_dy;
5072 int move_x = x + move_dx, move_y = y + move_dy;
5076 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
5078 TestIfBadThingTouchesOtherBadThing(x, y);
5080 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
5081 MovDir[x][y] = right_dir;
5082 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
5083 MovDir[x][y] = left_dir;
5085 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
5087 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
5090 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
5092 TestIfBadThingTouchesOtherBadThing(x, y);
5094 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
5095 MovDir[x][y] = left_dir;
5096 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
5097 MovDir[x][y] = right_dir;
5099 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
5101 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
5104 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
5106 TestIfBadThingTouchesOtherBadThing(x, y);
5108 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
5109 MovDir[x][y] = left_dir;
5110 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
5111 MovDir[x][y] = right_dir;
5113 if (MovDir[x][y] != old_move_dir)
5116 else if (element == EL_YAMYAM)
5118 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
5119 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
5121 if (can_turn_left && can_turn_right)
5122 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5123 else if (can_turn_left)
5124 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5125 else if (can_turn_right)
5126 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5128 MovDir[x][y] = back_dir;
5130 MovDelay[x][y] = 16 + 16 * RND(3);
5132 else if (element == EL_DARK_YAMYAM)
5134 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
5136 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
5139 if (can_turn_left && can_turn_right)
5140 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5141 else if (can_turn_left)
5142 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5143 else if (can_turn_right)
5144 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5146 MovDir[x][y] = back_dir;
5148 MovDelay[x][y] = 16 + 16 * RND(3);
5150 else if (element == EL_PACMAN)
5152 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
5153 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
5155 if (can_turn_left && can_turn_right)
5156 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5157 else if (can_turn_left)
5158 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5159 else if (can_turn_right)
5160 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5162 MovDir[x][y] = back_dir;
5164 MovDelay[x][y] = 6 + RND(40);
5166 else if (element == EL_PIG)
5168 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
5169 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
5170 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
5171 boolean should_turn_left, should_turn_right, should_move_on;
5173 int rnd = RND(rnd_value);
5175 should_turn_left = (can_turn_left &&
5177 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
5178 y + back_dy + left_dy)));
5179 should_turn_right = (can_turn_right &&
5181 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
5182 y + back_dy + right_dy)));
5183 should_move_on = (can_move_on &&
5186 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
5187 y + move_dy + left_dy) ||
5188 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
5189 y + move_dy + right_dy)));
5191 if (should_turn_left || should_turn_right || should_move_on)
5193 if (should_turn_left && should_turn_right && should_move_on)
5194 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
5195 rnd < 2 * rnd_value / 3 ? right_dir :
5197 else if (should_turn_left && should_turn_right)
5198 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
5199 else if (should_turn_left && should_move_on)
5200 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
5201 else if (should_turn_right && should_move_on)
5202 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
5203 else if (should_turn_left)
5204 MovDir[x][y] = left_dir;
5205 else if (should_turn_right)
5206 MovDir[x][y] = right_dir;
5207 else if (should_move_on)
5208 MovDir[x][y] = old_move_dir;
5210 else if (can_move_on && rnd > rnd_value / 8)
5211 MovDir[x][y] = old_move_dir;
5212 else if (can_turn_left && can_turn_right)
5213 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
5214 else if (can_turn_left && rnd > rnd_value / 8)
5215 MovDir[x][y] = left_dir;
5216 else if (can_turn_right && rnd > rnd_value/8)
5217 MovDir[x][y] = right_dir;
5219 MovDir[x][y] = back_dir;
5221 xx = x + move_xy[MovDir[x][y]].dx;
5222 yy = y + move_xy[MovDir[x][y]].dy;
5224 if (!IN_LEV_FIELD(xx, yy) ||
5225 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
5226 MovDir[x][y] = old_move_dir;
5230 else if (element == EL_DRAGON)
5232 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
5233 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
5234 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
5236 int rnd = RND(rnd_value);
5238 if (can_move_on && rnd > rnd_value / 8)
5239 MovDir[x][y] = old_move_dir;
5240 else if (can_turn_left && can_turn_right)
5241 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
5242 else if (can_turn_left && rnd > rnd_value / 8)
5243 MovDir[x][y] = left_dir;
5244 else if (can_turn_right && rnd > rnd_value / 8)
5245 MovDir[x][y] = right_dir;
5247 MovDir[x][y] = back_dir;
5249 xx = x + move_xy[MovDir[x][y]].dx;
5250 yy = y + move_xy[MovDir[x][y]].dy;
5252 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
5253 MovDir[x][y] = old_move_dir;
5257 else if (element == EL_MOLE)
5259 boolean can_move_on =
5260 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
5261 IS_AMOEBOID(Feld[move_x][move_y]) ||
5262 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
5265 boolean can_turn_left =
5266 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
5267 IS_AMOEBOID(Feld[left_x][left_y])));
5269 boolean can_turn_right =
5270 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
5271 IS_AMOEBOID(Feld[right_x][right_y])));
5273 if (can_turn_left && can_turn_right)
5274 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
5275 else if (can_turn_left)
5276 MovDir[x][y] = left_dir;
5278 MovDir[x][y] = right_dir;
5281 if (MovDir[x][y] != old_move_dir)
5284 else if (element == EL_BALLOON)
5286 MovDir[x][y] = game.wind_direction;
5289 else if (element == EL_SPRING)
5291 #if USE_NEW_SPRING_BUMPER
5292 if (MovDir[x][y] & MV_HORIZONTAL)
5294 if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
5295 !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
5297 Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
5298 ResetGfxAnimation(move_x, move_y);
5299 DrawLevelField(move_x, move_y);
5301 MovDir[x][y] = back_dir;
5303 else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
5304 SPRING_CAN_ENTER_FIELD(element, x, y + 1))
5305 MovDir[x][y] = MV_NONE;
5308 if (MovDir[x][y] & MV_HORIZONTAL &&
5309 (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
5310 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
5311 MovDir[x][y] = MV_NONE;
5316 else if (element == EL_ROBOT ||
5317 element == EL_SATELLITE ||
5318 element == EL_PENGUIN ||
5319 element == EL_EMC_ANDROID)
5321 int attr_x = -1, attr_y = -1;
5332 for (i = 0; i < MAX_PLAYERS; i++)
5334 struct PlayerInfo *player = &stored_player[i];
5335 int jx = player->jx, jy = player->jy;
5337 if (!player->active)
5341 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
5349 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
5350 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
5351 game.engine_version < VERSION_IDENT(3,1,0,0)))
5357 if (element == EL_PENGUIN)
5360 static int xy[4][2] =
5368 for (i = 0; i < NUM_DIRECTIONS; i++)
5370 int ex = x + xy[i][0];
5371 int ey = y + xy[i][1];
5373 if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
5374 Feld[ex][ey] == EL_EM_EXIT_OPEN ||
5375 Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
5376 Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
5385 MovDir[x][y] = MV_NONE;
5387 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
5388 else if (attr_x > x)
5389 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
5391 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
5392 else if (attr_y > y)
5393 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
5395 if (element == EL_ROBOT)
5399 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5400 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
5401 Moving2Blocked(x, y, &newx, &newy);
5403 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
5404 MovDelay[x][y] = 8 + 8 * !RND(3);
5406 MovDelay[x][y] = 16;
5408 else if (element == EL_PENGUIN)
5414 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5416 boolean first_horiz = RND(2);
5417 int new_move_dir = MovDir[x][y];
5420 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5421 Moving2Blocked(x, y, &newx, &newy);
5423 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
5427 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5428 Moving2Blocked(x, y, &newx, &newy);
5430 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
5433 MovDir[x][y] = old_move_dir;
5437 else if (element == EL_SATELLITE)
5443 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5445 boolean first_horiz = RND(2);
5446 int new_move_dir = MovDir[x][y];
5449 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5450 Moving2Blocked(x, y, &newx, &newy);
5452 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
5456 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5457 Moving2Blocked(x, y, &newx, &newy);
5459 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
5462 MovDir[x][y] = old_move_dir;
5466 else if (element == EL_EMC_ANDROID)
5468 static int check_pos[16] =
5470 -1, /* 0 => (invalid) */
5471 7, /* 1 => MV_LEFT */
5472 3, /* 2 => MV_RIGHT */
5473 -1, /* 3 => (invalid) */
5475 0, /* 5 => MV_LEFT | MV_UP */
5476 2, /* 6 => MV_RIGHT | MV_UP */
5477 -1, /* 7 => (invalid) */
5478 5, /* 8 => MV_DOWN */
5479 6, /* 9 => MV_LEFT | MV_DOWN */
5480 4, /* 10 => MV_RIGHT | MV_DOWN */
5481 -1, /* 11 => (invalid) */
5482 -1, /* 12 => (invalid) */
5483 -1, /* 13 => (invalid) */
5484 -1, /* 14 => (invalid) */
5485 -1, /* 15 => (invalid) */
5493 { -1, -1, MV_LEFT | MV_UP },
5495 { +1, -1, MV_RIGHT | MV_UP },
5496 { +1, 0, MV_RIGHT },
5497 { +1, +1, MV_RIGHT | MV_DOWN },
5499 { -1, +1, MV_LEFT | MV_DOWN },
5502 int start_pos, check_order;
5503 boolean can_clone = FALSE;
5506 /* check if there is any free field around current position */
5507 for (i = 0; i < 8; i++)
5509 int newx = x + check_xy[i].dx;
5510 int newy = y + check_xy[i].dy;
5512 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5520 if (can_clone) /* randomly find an element to clone */
5524 start_pos = check_pos[RND(8)];
5525 check_order = (RND(2) ? -1 : +1);
5527 for (i = 0; i < 8; i++)
5529 int pos_raw = start_pos + i * check_order;
5530 int pos = (pos_raw + 8) % 8;
5531 int newx = x + check_xy[pos].dx;
5532 int newy = y + check_xy[pos].dy;
5534 if (ANDROID_CAN_CLONE_FIELD(newx, newy))
5536 element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
5537 element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
5539 Store[x][y] = Feld[newx][newy];
5548 if (can_clone) /* randomly find a direction to move */
5552 start_pos = check_pos[RND(8)];
5553 check_order = (RND(2) ? -1 : +1);
5555 for (i = 0; i < 8; i++)
5557 int pos_raw = start_pos + i * check_order;
5558 int pos = (pos_raw + 8) % 8;
5559 int newx = x + check_xy[pos].dx;
5560 int newy = y + check_xy[pos].dy;
5561 int new_move_dir = check_xy[pos].dir;
5563 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5565 MovDir[x][y] = new_move_dir;
5566 MovDelay[x][y] = level.android_clone_time * 8 + 1;
5575 if (can_clone) /* cloning and moving successful */
5578 /* cannot clone -- try to move towards player */
5580 start_pos = check_pos[MovDir[x][y] & 0x0f];
5581 check_order = (RND(2) ? -1 : +1);
5583 for (i = 0; i < 3; i++)
5585 /* first check start_pos, then previous/next or (next/previous) pos */
5586 int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
5587 int pos = (pos_raw + 8) % 8;
5588 int newx = x + check_xy[pos].dx;
5589 int newy = y + check_xy[pos].dy;
5590 int new_move_dir = check_xy[pos].dir;
5592 if (IS_PLAYER(newx, newy))
5595 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
5597 MovDir[x][y] = new_move_dir;
5598 MovDelay[x][y] = level.android_move_time * 8 + 1;
5605 else if (move_pattern == MV_TURNING_LEFT ||
5606 move_pattern == MV_TURNING_RIGHT ||
5607 move_pattern == MV_TURNING_LEFT_RIGHT ||
5608 move_pattern == MV_TURNING_RIGHT_LEFT ||
5609 move_pattern == MV_TURNING_RANDOM ||
5610 move_pattern == MV_ALL_DIRECTIONS)
5612 boolean can_turn_left =
5613 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
5614 boolean can_turn_right =
5615 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
5617 if (element_info[element].move_stepsize == 0) /* "not moving" */
5620 if (move_pattern == MV_TURNING_LEFT)
5621 MovDir[x][y] = left_dir;
5622 else if (move_pattern == MV_TURNING_RIGHT)
5623 MovDir[x][y] = right_dir;
5624 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
5625 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
5626 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
5627 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
5628 else if (move_pattern == MV_TURNING_RANDOM)
5629 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
5630 can_turn_right && !can_turn_left ? right_dir :
5631 RND(2) ? left_dir : right_dir);
5632 else if (can_turn_left && can_turn_right)
5633 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5634 else if (can_turn_left)
5635 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5636 else if (can_turn_right)
5637 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5639 MovDir[x][y] = back_dir;
5641 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5643 else if (move_pattern == MV_HORIZONTAL ||
5644 move_pattern == MV_VERTICAL)
5646 if (move_pattern & old_move_dir)
5647 MovDir[x][y] = back_dir;
5648 else if (move_pattern == MV_HORIZONTAL)
5649 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
5650 else if (move_pattern == MV_VERTICAL)
5651 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
5653 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5655 else if (move_pattern & MV_ANY_DIRECTION)
5657 MovDir[x][y] = move_pattern;
5658 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5660 else if (move_pattern & MV_WIND_DIRECTION)
5662 MovDir[x][y] = game.wind_direction;
5663 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5665 else if (move_pattern == MV_ALONG_LEFT_SIDE)
5667 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
5668 MovDir[x][y] = left_dir;
5669 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5670 MovDir[x][y] = right_dir;
5672 if (MovDir[x][y] != old_move_dir)
5673 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5675 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
5677 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
5678 MovDir[x][y] = right_dir;
5679 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5680 MovDir[x][y] = left_dir;
5682 if (MovDir[x][y] != old_move_dir)
5683 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5685 else if (move_pattern == MV_TOWARDS_PLAYER ||
5686 move_pattern == MV_AWAY_FROM_PLAYER)
5688 int attr_x = -1, attr_y = -1;
5690 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
5701 for (i = 0; i < MAX_PLAYERS; i++)
5703 struct PlayerInfo *player = &stored_player[i];
5704 int jx = player->jx, jy = player->jy;
5706 if (!player->active)
5710 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
5718 MovDir[x][y] = MV_NONE;
5720 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
5721 else if (attr_x > x)
5722 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
5724 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
5725 else if (attr_y > y)
5726 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
5728 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5730 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5732 boolean first_horiz = RND(2);
5733 int new_move_dir = MovDir[x][y];
5735 if (element_info[element].move_stepsize == 0) /* "not moving" */
5737 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
5738 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5744 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5745 Moving2Blocked(x, y, &newx, &newy);
5747 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5751 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5752 Moving2Blocked(x, y, &newx, &newy);
5754 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5757 MovDir[x][y] = old_move_dir;
5760 else if (move_pattern == MV_WHEN_PUSHED ||
5761 move_pattern == MV_WHEN_DROPPED)
5763 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5764 MovDir[x][y] = MV_NONE;
5768 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
5770 static int test_xy[7][2] =
5780 static int test_dir[7] =
5790 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
5791 int move_preference = -1000000; /* start with very low preference */
5792 int new_move_dir = MV_NONE;
5793 int start_test = RND(4);
5796 for (i = 0; i < NUM_DIRECTIONS; i++)
5798 int move_dir = test_dir[start_test + i];
5799 int move_dir_preference;
5801 xx = x + test_xy[start_test + i][0];
5802 yy = y + test_xy[start_test + i][1];
5804 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
5805 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
5807 new_move_dir = move_dir;
5812 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
5815 move_dir_preference = -1 * RunnerVisit[xx][yy];
5816 if (hunter_mode && PlayerVisit[xx][yy] > 0)
5817 move_dir_preference = PlayerVisit[xx][yy];
5819 if (move_dir_preference > move_preference)
5821 /* prefer field that has not been visited for the longest time */
5822 move_preference = move_dir_preference;
5823 new_move_dir = move_dir;
5825 else if (move_dir_preference == move_preference &&
5826 move_dir == old_move_dir)
5828 /* prefer last direction when all directions are preferred equally */
5829 move_preference = move_dir_preference;
5830 new_move_dir = move_dir;
5834 MovDir[x][y] = new_move_dir;
5835 if (old_move_dir != new_move_dir)
5836 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5840 static void TurnRound(int x, int y)
5842 int direction = MovDir[x][y];
5846 GfxDir[x][y] = MovDir[x][y];
5848 if (direction != MovDir[x][y])
5852 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
5854 ResetGfxFrame(x, y, FALSE);
5857 static boolean JustBeingPushed(int x, int y)
5861 for (i = 0; i < MAX_PLAYERS; i++)
5863 struct PlayerInfo *player = &stored_player[i];
5865 if (player->active && player->is_pushing && player->MovPos)
5867 int next_jx = player->jx + (player->jx - player->last_jx);
5868 int next_jy = player->jy + (player->jy - player->last_jy);
5870 if (x == next_jx && y == next_jy)
5878 void StartMoving(int x, int y)
5880 boolean started_moving = FALSE; /* some elements can fall _and_ move */
5881 int element = Feld[x][y];
5886 if (MovDelay[x][y] == 0)
5887 GfxAction[x][y] = ACTION_DEFAULT;
5889 if (CAN_FALL(element) && y < lev_fieldy - 1)
5891 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
5892 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
5893 if (JustBeingPushed(x, y))
5896 if (element == EL_QUICKSAND_FULL)
5898 if (IS_FREE(x, y + 1))
5900 InitMovingField(x, y, MV_DOWN);
5901 started_moving = TRUE;
5903 Feld[x][y] = EL_QUICKSAND_EMPTYING;
5904 #if USE_QUICKSAND_BD_ROCK_BUGFIX
5905 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
5906 Store[x][y] = EL_ROCK;
5908 Store[x][y] = EL_ROCK;
5911 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
5913 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5915 if (!MovDelay[x][y])
5916 MovDelay[x][y] = TILEY + 1;
5925 Feld[x][y] = EL_QUICKSAND_EMPTY;
5926 Feld[x][y + 1] = EL_QUICKSAND_FULL;
5927 Store[x][y + 1] = Store[x][y];
5930 PlayLevelSoundAction(x, y, ACTION_FILLING);
5933 else if (element == EL_QUICKSAND_FAST_FULL)
5935 if (IS_FREE(x, y + 1))
5937 InitMovingField(x, y, MV_DOWN);
5938 started_moving = TRUE;
5940 Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
5941 #if USE_QUICKSAND_BD_ROCK_BUGFIX
5942 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
5943 Store[x][y] = EL_ROCK;
5945 Store[x][y] = EL_ROCK;
5948 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
5950 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
5952 if (!MovDelay[x][y])
5953 MovDelay[x][y] = TILEY + 1;
5962 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
5963 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
5964 Store[x][y + 1] = Store[x][y];
5967 PlayLevelSoundAction(x, y, ACTION_FILLING);
5970 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
5971 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5973 InitMovingField(x, y, MV_DOWN);
5974 started_moving = TRUE;
5976 Feld[x][y] = EL_QUICKSAND_FILLING;
5977 Store[x][y] = element;
5979 PlayLevelSoundAction(x, y, ACTION_FILLING);
5981 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
5982 Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
5984 InitMovingField(x, y, MV_DOWN);
5985 started_moving = TRUE;
5987 Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
5988 Store[x][y] = element;
5990 PlayLevelSoundAction(x, y, ACTION_FILLING);
5992 else if (element == EL_MAGIC_WALL_FULL)
5994 if (IS_FREE(x, y + 1))
5996 InitMovingField(x, y, MV_DOWN);
5997 started_moving = TRUE;
5999 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
6000 Store[x][y] = EL_CHANGED(Store[x][y]);
6002 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6004 if (!MovDelay[x][y])
6005 MovDelay[x][y] = TILEY/4 + 1;
6014 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
6015 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
6016 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
6020 else if (element == EL_BD_MAGIC_WALL_FULL)
6022 if (IS_FREE(x, y + 1))
6024 InitMovingField(x, y, MV_DOWN);
6025 started_moving = TRUE;
6027 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
6028 Store[x][y] = EL_CHANGED_BD(Store[x][y]);
6030 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6032 if (!MovDelay[x][y])
6033 MovDelay[x][y] = TILEY/4 + 1;
6042 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
6043 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
6044 Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
6048 else if (element == EL_DC_MAGIC_WALL_FULL)
6050 if (IS_FREE(x, y + 1))
6052 InitMovingField(x, y, MV_DOWN);
6053 started_moving = TRUE;
6055 Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
6056 Store[x][y] = EL_CHANGED_DC(Store[x][y]);
6058 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6060 if (!MovDelay[x][y])
6061 MovDelay[x][y] = TILEY/4 + 1;
6070 Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
6071 Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
6072 Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
6076 else if ((CAN_PASS_MAGIC_WALL(element) &&
6077 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6078 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
6079 (CAN_PASS_DC_MAGIC_WALL(element) &&
6080 (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
6083 InitMovingField(x, y, MV_DOWN);
6084 started_moving = TRUE;
6087 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
6088 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
6089 EL_DC_MAGIC_WALL_FILLING);
6090 Store[x][y] = element;
6092 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
6094 SplashAcid(x, y + 1);
6096 InitMovingField(x, y, MV_DOWN);
6097 started_moving = TRUE;
6099 Store[x][y] = EL_ACID;
6102 #if USE_FIX_IMPACT_COLLISION
6103 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
6104 CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
6106 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
6107 CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
6109 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
6110 CAN_FALL(element) && WasJustFalling[x][y] &&
6111 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
6113 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
6114 CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
6115 (Feld[x][y + 1] == EL_BLOCKED)))
6117 /* this is needed for a special case not covered by calling "Impact()"
6118 from "ContinueMoving()": if an element moves to a tile directly below
6119 another element which was just falling on that tile (which was empty
6120 in the previous frame), the falling element above would just stop
6121 instead of smashing the element below (in previous version, the above
6122 element was just checked for "moving" instead of "falling", resulting
6123 in incorrect smashes caused by horizontal movement of the above
6124 element; also, the case of the player being the element to smash was
6125 simply not covered here... :-/ ) */
6127 CheckCollision[x][y] = 0;
6128 CheckImpact[x][y] = 0;
6132 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
6134 if (MovDir[x][y] == MV_NONE)
6136 InitMovingField(x, y, MV_DOWN);
6137 started_moving = TRUE;
6140 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
6142 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
6143 MovDir[x][y] = MV_DOWN;
6145 InitMovingField(x, y, MV_DOWN);
6146 started_moving = TRUE;
6148 else if (element == EL_AMOEBA_DROP)
6150 Feld[x][y] = EL_AMOEBA_GROWING;
6151 Store[x][y] = EL_AMOEBA_WET;
6153 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
6154 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
6155 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
6156 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
6158 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
6159 (IS_FREE(x - 1, y + 1) ||
6160 Feld[x - 1][y + 1] == EL_ACID));
6161 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
6162 (IS_FREE(x + 1, y + 1) ||
6163 Feld[x + 1][y + 1] == EL_ACID));
6164 boolean can_fall_any = (can_fall_left || can_fall_right);
6165 boolean can_fall_both = (can_fall_left && can_fall_right);
6166 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
6168 #if USE_NEW_ALL_SLIPPERY
6169 if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
6171 if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
6172 can_fall_right = FALSE;
6173 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
6174 can_fall_left = FALSE;
6175 else if (slippery_type == SLIPPERY_ONLY_LEFT)
6176 can_fall_right = FALSE;
6177 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
6178 can_fall_left = FALSE;
6180 can_fall_any = (can_fall_left || can_fall_right);
6181 can_fall_both = FALSE;
6184 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
6186 if (slippery_type == SLIPPERY_ONLY_LEFT)
6187 can_fall_right = FALSE;
6188 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
6189 can_fall_left = FALSE;
6190 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
6191 can_fall_right = FALSE;
6192 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
6193 can_fall_left = FALSE;
6195 can_fall_any = (can_fall_left || can_fall_right);
6196 can_fall_both = (can_fall_left && can_fall_right);
6200 #if USE_NEW_ALL_SLIPPERY
6202 #if USE_NEW_SP_SLIPPERY
6203 /* !!! better use the same properties as for custom elements here !!! */
6204 else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
6205 can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
6207 can_fall_right = FALSE; /* slip down on left side */
6208 can_fall_both = FALSE;
6213 #if USE_NEW_ALL_SLIPPERY
6216 if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
6217 can_fall_right = FALSE; /* slip down on left side */
6219 can_fall_left = !(can_fall_right = RND(2));
6221 can_fall_both = FALSE;
6226 if (game.emulation == EMU_BOULDERDASH ||
6227 element == EL_BD_ROCK || element == EL_BD_DIAMOND)
6228 can_fall_right = FALSE; /* slip down on left side */
6230 can_fall_left = !(can_fall_right = RND(2));
6232 can_fall_both = FALSE;
6238 /* if not determined otherwise, prefer left side for slipping down */
6239 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
6240 started_moving = TRUE;
6244 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
6246 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
6249 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
6250 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
6251 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
6252 int belt_dir = game.belt_dir[belt_nr];
6254 if ((belt_dir == MV_LEFT && left_is_free) ||
6255 (belt_dir == MV_RIGHT && right_is_free))
6257 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
6259 InitMovingField(x, y, belt_dir);
6260 started_moving = TRUE;
6262 Pushed[x][y] = TRUE;
6263 Pushed[nextx][y] = TRUE;
6265 GfxAction[x][y] = ACTION_DEFAULT;
6269 MovDir[x][y] = 0; /* if element was moving, stop it */
6274 /* not "else if" because of elements that can fall and move (EL_SPRING) */
6276 if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
6278 if (CAN_MOVE(element) && !started_moving)
6281 int move_pattern = element_info[element].move_pattern;
6286 if (MovDir[x][y] == MV_NONE)
6288 printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
6289 x, y, element, element_info[element].token_name);
6290 printf("StartMoving(): This should never happen!\n");
6295 Moving2Blocked(x, y, &newx, &newy);
6297 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
6300 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
6301 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6303 WasJustMoving[x][y] = 0;
6304 CheckCollision[x][y] = 0;
6306 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
6308 if (Feld[x][y] != element) /* element has changed */
6312 if (!MovDelay[x][y]) /* start new movement phase */
6314 /* all objects that can change their move direction after each step
6315 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
6317 if (element != EL_YAMYAM &&
6318 element != EL_DARK_YAMYAM &&
6319 element != EL_PACMAN &&
6320 !(move_pattern & MV_ANY_DIRECTION) &&
6321 move_pattern != MV_TURNING_LEFT &&
6322 move_pattern != MV_TURNING_RIGHT &&
6323 move_pattern != MV_TURNING_LEFT_RIGHT &&
6324 move_pattern != MV_TURNING_RIGHT_LEFT &&
6325 move_pattern != MV_TURNING_RANDOM)
6329 if (MovDelay[x][y] && (element == EL_BUG ||
6330 element == EL_SPACESHIP ||
6331 element == EL_SP_SNIKSNAK ||
6332 element == EL_SP_ELECTRON ||
6333 element == EL_MOLE))
6334 DrawLevelField(x, y);
6338 if (MovDelay[x][y]) /* wait some time before next movement */
6342 if (element == EL_ROBOT ||
6343 element == EL_YAMYAM ||
6344 element == EL_DARK_YAMYAM)
6346 DrawLevelElementAnimationIfNeeded(x, y, element);
6347 PlayLevelSoundAction(x, y, ACTION_WAITING);
6349 else if (element == EL_SP_ELECTRON)
6350 DrawLevelElementAnimationIfNeeded(x, y, element);
6351 else if (element == EL_DRAGON)
6354 int dir = MovDir[x][y];
6355 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
6356 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
6357 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
6358 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
6359 dir == MV_UP ? IMG_FLAMES_1_UP :
6360 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
6361 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
6363 GfxAction[x][y] = ACTION_ATTACKING;
6365 if (IS_PLAYER(x, y))
6366 DrawPlayerField(x, y);
6368 DrawLevelField(x, y);
6370 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
6372 for (i = 1; i <= 3; i++)
6374 int xx = x + i * dx;
6375 int yy = y + i * dy;
6376 int sx = SCREENX(xx);
6377 int sy = SCREENY(yy);
6378 int flame_graphic = graphic + (i - 1);
6380 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
6385 int flamed = MovingOrBlocked2Element(xx, yy);
6389 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
6391 else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
6392 RemoveMovingField(xx, yy);
6394 RemoveField(xx, yy);
6396 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
6399 RemoveMovingField(xx, yy);
6402 ChangeDelay[xx][yy] = 0;
6404 Feld[xx][yy] = EL_FLAMES;
6406 if (IN_SCR_FIELD(sx, sy))
6408 DrawLevelFieldCrumbledSand(xx, yy);
6409 DrawGraphic(sx, sy, flame_graphic, frame);
6414 if (Feld[xx][yy] == EL_FLAMES)
6415 Feld[xx][yy] = EL_EMPTY;
6416 DrawLevelField(xx, yy);
6421 if (MovDelay[x][y]) /* element still has to wait some time */
6423 PlayLevelSoundAction(x, y, ACTION_WAITING);
6429 /* now make next step */
6431 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
6433 if (DONT_COLLIDE_WITH(element) &&
6434 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
6435 !PLAYER_ENEMY_PROTECTED(newx, newy))
6437 TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
6442 else if (CAN_MOVE_INTO_ACID(element) &&
6443 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
6444 !IS_MV_DIAGONAL(MovDir[x][y]) &&
6445 (MovDir[x][y] == MV_DOWN ||
6446 game.engine_version >= VERSION_IDENT(3,1,0,0)))
6448 SplashAcid(newx, newy);
6449 Store[x][y] = EL_ACID;
6451 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
6453 if (Feld[newx][newy] == EL_EXIT_OPEN ||
6454 Feld[newx][newy] == EL_EM_EXIT_OPEN ||
6455 Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
6456 Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
6459 DrawLevelField(x, y);
6461 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
6462 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
6463 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
6465 local_player->friends_still_needed--;
6466 if (!local_player->friends_still_needed &&
6467 !local_player->GameOver && AllPlayersGone)
6468 PlayerWins(local_player);
6472 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
6474 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
6475 DrawLevelField(newx, newy);
6477 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
6479 else if (!IS_FREE(newx, newy))
6481 GfxAction[x][y] = ACTION_WAITING;
6483 if (IS_PLAYER(x, y))
6484 DrawPlayerField(x, y);
6486 DrawLevelField(x, y);
6491 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
6493 if (IS_FOOD_PIG(Feld[newx][newy]))
6495 if (IS_MOVING(newx, newy))
6496 RemoveMovingField(newx, newy);
6499 Feld[newx][newy] = EL_EMPTY;
6500 DrawLevelField(newx, newy);
6503 PlayLevelSound(x, y, SND_PIG_DIGGING);
6505 else if (!IS_FREE(newx, newy))
6507 if (IS_PLAYER(x, y))
6508 DrawPlayerField(x, y);
6510 DrawLevelField(x, y);
6515 else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
6517 if (Store[x][y] != EL_EMPTY)
6519 boolean can_clone = FALSE;
6522 /* check if element to clone is still there */
6523 for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
6525 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
6533 /* cannot clone or target field not free anymore -- do not clone */
6534 if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6535 Store[x][y] = EL_EMPTY;
6538 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6540 if (IS_MV_DIAGONAL(MovDir[x][y]))
6542 int diagonal_move_dir = MovDir[x][y];
6543 int stored = Store[x][y];
6544 int change_delay = 8;
6547 /* android is moving diagonally */
6549 CreateField(x, y, EL_DIAGONAL_SHRINKING);
6551 Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
6552 GfxElement[x][y] = EL_EMC_ANDROID;
6553 GfxAction[x][y] = ACTION_SHRINKING;
6554 GfxDir[x][y] = diagonal_move_dir;
6555 ChangeDelay[x][y] = change_delay;
6557 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
6560 DrawLevelGraphicAnimation(x, y, graphic);
6561 PlayLevelSoundAction(x, y, ACTION_SHRINKING);
6563 if (Feld[newx][newy] == EL_ACID)
6565 SplashAcid(newx, newy);
6570 CreateField(newx, newy, EL_DIAGONAL_GROWING);
6572 Store[newx][newy] = EL_EMC_ANDROID;
6573 GfxElement[newx][newy] = EL_EMC_ANDROID;
6574 GfxAction[newx][newy] = ACTION_GROWING;
6575 GfxDir[newx][newy] = diagonal_move_dir;
6576 ChangeDelay[newx][newy] = change_delay;
6578 graphic = el_act_dir2img(GfxElement[newx][newy],
6579 GfxAction[newx][newy], GfxDir[newx][newy]);
6581 DrawLevelGraphicAnimation(newx, newy, graphic);
6582 PlayLevelSoundAction(newx, newy, ACTION_GROWING);
6588 Feld[newx][newy] = EL_EMPTY;
6589 DrawLevelField(newx, newy);
6591 PlayLevelSoundAction(x, y, ACTION_DIGGING);
6594 else if (!IS_FREE(newx, newy))
6597 if (IS_PLAYER(x, y))
6598 DrawPlayerField(x, y);
6600 DrawLevelField(x, y);
6606 else if (IS_CUSTOM_ELEMENT(element) &&
6607 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6609 int new_element = Feld[newx][newy];
6611 if (!IS_FREE(newx, newy))
6613 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
6614 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
6617 /* no element can dig solid indestructible elements */
6618 if (IS_INDESTRUCTIBLE(new_element) &&
6619 !IS_DIGGABLE(new_element) &&
6620 !IS_COLLECTIBLE(new_element))
6623 if (AmoebaNr[newx][newy] &&
6624 (new_element == EL_AMOEBA_FULL ||
6625 new_element == EL_BD_AMOEBA ||
6626 new_element == EL_AMOEBA_GROWING))
6628 AmoebaCnt[AmoebaNr[newx][newy]]--;
6629 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6632 if (IS_MOVING(newx, newy))
6633 RemoveMovingField(newx, newy);
6636 RemoveField(newx, newy);
6637 DrawLevelField(newx, newy);
6640 /* if digged element was about to explode, prevent the explosion */
6641 ExplodeField[newx][newy] = EX_TYPE_NONE;
6643 PlayLevelSoundAction(x, y, action);
6646 Store[newx][newy] = EL_EMPTY;
6648 /* this makes it possible to leave the removed element again */
6649 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
6650 Store[newx][newy] = new_element;
6652 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
6654 int move_leave_element = element_info[element].move_leave_element;
6656 /* this makes it possible to leave the removed element again */
6657 Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
6658 new_element : move_leave_element);
6662 if (move_pattern & MV_MAZE_RUNNER_STYLE)
6664 RunnerVisit[x][y] = FrameCounter;
6665 PlayerVisit[x][y] /= 8; /* expire player visit path */
6668 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
6670 if (!IS_FREE(newx, newy))
6672 if (IS_PLAYER(x, y))
6673 DrawPlayerField(x, y);
6675 DrawLevelField(x, y);
6681 boolean wanna_flame = !RND(10);
6682 int dx = newx - x, dy = newy - y;
6683 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
6684 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
6685 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
6686 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
6687 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
6688 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
6691 IS_CLASSIC_ENEMY(element1) ||
6692 IS_CLASSIC_ENEMY(element2)) &&
6693 element1 != EL_DRAGON && element2 != EL_DRAGON &&
6694 element1 != EL_FLAMES && element2 != EL_FLAMES)
6696 ResetGfxAnimation(x, y);
6697 GfxAction[x][y] = ACTION_ATTACKING;
6699 if (IS_PLAYER(x, y))
6700 DrawPlayerField(x, y);
6702 DrawLevelField(x, y);
6704 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
6706 MovDelay[x][y] = 50;
6710 RemoveField(newx, newy);
6712 Feld[newx][newy] = EL_FLAMES;
6713 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
6716 RemoveField(newx1, newy1);
6718 Feld[newx1][newy1] = EL_FLAMES;
6720 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
6723 RemoveField(newx2, newy2);
6725 Feld[newx2][newy2] = EL_FLAMES;
6732 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6733 Feld[newx][newy] == EL_DIAMOND)
6735 if (IS_MOVING(newx, newy))
6736 RemoveMovingField(newx, newy);
6739 Feld[newx][newy] = EL_EMPTY;
6740 DrawLevelField(newx, newy);
6743 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
6745 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6746 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
6748 if (AmoebaNr[newx][newy])
6750 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6751 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
6752 Feld[newx][newy] == EL_BD_AMOEBA)
6753 AmoebaCnt[AmoebaNr[newx][newy]]--;
6758 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
6760 RemoveMovingField(newx, newy);
6763 if (IS_MOVING(newx, newy))
6765 RemoveMovingField(newx, newy);
6770 Feld[newx][newy] = EL_EMPTY;
6771 DrawLevelField(newx, newy);
6774 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
6776 else if ((element == EL_PACMAN || element == EL_MOLE)
6777 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
6779 if (AmoebaNr[newx][newy])
6781 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6782 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
6783 Feld[newx][newy] == EL_BD_AMOEBA)
6784 AmoebaCnt[AmoebaNr[newx][newy]]--;
6787 if (element == EL_MOLE)
6789 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
6790 PlayLevelSound(x, y, SND_MOLE_DIGGING);
6792 ResetGfxAnimation(x, y);
6793 GfxAction[x][y] = ACTION_DIGGING;
6794 DrawLevelField(x, y);
6796 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
6798 return; /* wait for shrinking amoeba */
6800 else /* element == EL_PACMAN */
6802 Feld[newx][newy] = EL_EMPTY;
6803 DrawLevelField(newx, newy);
6804 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
6807 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
6808 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
6809 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
6811 /* wait for shrinking amoeba to completely disappear */
6814 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
6816 /* object was running against a wall */
6821 /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
6822 if (move_pattern & MV_ANY_DIRECTION &&
6823 move_pattern == MovDir[x][y])
6825 int blocking_element =
6826 (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
6828 CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
6831 element = Feld[x][y]; /* element might have changed */
6835 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
6836 DrawLevelElementAnimation(x, y, element);
6838 if (DONT_TOUCH(element))
6839 TestIfBadThingTouchesPlayer(x, y);
6844 InitMovingField(x, y, MovDir[x][y]);
6846 PlayLevelSoundAction(x, y, ACTION_MOVING);
6850 ContinueMoving(x, y);
6853 void ContinueMoving(int x, int y)
6855 int element = Feld[x][y];
6856 struct ElementInfo *ei = &element_info[element];
6857 int direction = MovDir[x][y];
6858 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
6859 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
6860 int newx = x + dx, newy = y + dy;
6861 int stored = Store[x][y];
6862 int stored_new = Store[newx][newy];
6863 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
6864 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
6865 boolean last_line = (newy == lev_fieldy - 1);
6867 MovPos[x][y] += getElementMoveStepsize(x, y);
6869 if (pushed_by_player) /* special case: moving object pushed by player */
6870 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
6872 if (ABS(MovPos[x][y]) < TILEX)
6874 DrawLevelField(x, y);
6876 return; /* element is still moving */
6879 /* element reached destination field */
6881 Feld[x][y] = EL_EMPTY;
6882 Feld[newx][newy] = element;
6883 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
6885 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
6887 element = Feld[newx][newy] = EL_ACID;
6889 else if (element == EL_MOLE)
6891 Feld[x][y] = EL_SAND;
6893 DrawLevelFieldCrumbledSandNeighbours(x, y);
6895 else if (element == EL_QUICKSAND_FILLING)
6897 element = Feld[newx][newy] = get_next_element(element);
6898 Store[newx][newy] = Store[x][y];
6900 else if (element == EL_QUICKSAND_EMPTYING)
6902 Feld[x][y] = get_next_element(element);
6903 element = Feld[newx][newy] = Store[x][y];
6905 else if (element == EL_QUICKSAND_FAST_FILLING)
6907 element = Feld[newx][newy] = get_next_element(element);
6908 Store[newx][newy] = Store[x][y];
6910 else if (element == EL_QUICKSAND_FAST_EMPTYING)
6912 Feld[x][y] = get_next_element(element);
6913 element = Feld[newx][newy] = Store[x][y];
6915 else if (element == EL_MAGIC_WALL_FILLING)
6917 element = Feld[newx][newy] = get_next_element(element);
6918 if (!game.magic_wall_active)
6919 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
6920 Store[newx][newy] = Store[x][y];
6922 else if (element == EL_MAGIC_WALL_EMPTYING)
6924 Feld[x][y] = get_next_element(element);
6925 if (!game.magic_wall_active)
6926 Feld[x][y] = EL_MAGIC_WALL_DEAD;
6927 element = Feld[newx][newy] = Store[x][y];
6929 #if USE_NEW_CUSTOM_VALUE
6930 InitField(newx, newy, FALSE);
6933 else if (element == EL_BD_MAGIC_WALL_FILLING)
6935 element = Feld[newx][newy] = get_next_element(element);
6936 if (!game.magic_wall_active)
6937 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
6938 Store[newx][newy] = Store[x][y];
6940 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
6942 Feld[x][y] = get_next_element(element);
6943 if (!game.magic_wall_active)
6944 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
6945 element = Feld[newx][newy] = Store[x][y];
6947 #if USE_NEW_CUSTOM_VALUE
6948 InitField(newx, newy, FALSE);
6951 else if (element == EL_DC_MAGIC_WALL_FILLING)
6953 element = Feld[newx][newy] = get_next_element(element);
6954 if (!game.magic_wall_active)
6955 element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
6956 Store[newx][newy] = Store[x][y];
6958 else if (element == EL_DC_MAGIC_WALL_EMPTYING)
6960 Feld[x][y] = get_next_element(element);
6961 if (!game.magic_wall_active)
6962 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
6963 element = Feld[newx][newy] = Store[x][y];
6965 #if USE_NEW_CUSTOM_VALUE
6966 InitField(newx, newy, FALSE);
6969 else if (element == EL_AMOEBA_DROPPING)
6971 Feld[x][y] = get_next_element(element);
6972 element = Feld[newx][newy] = Store[x][y];
6974 else if (element == EL_SOKOBAN_OBJECT)
6977 Feld[x][y] = Back[x][y];
6979 if (Back[newx][newy])
6980 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
6982 Back[x][y] = Back[newx][newy] = 0;
6985 Store[x][y] = EL_EMPTY;
6990 MovDelay[newx][newy] = 0;
6992 if (CAN_CHANGE_OR_HAS_ACTION(element))
6994 /* copy element change control values to new field */
6995 ChangeDelay[newx][newy] = ChangeDelay[x][y];
6996 ChangePage[newx][newy] = ChangePage[x][y];
6997 ChangeCount[newx][newy] = ChangeCount[x][y];
6998 ChangeEvent[newx][newy] = ChangeEvent[x][y];
7001 #if USE_NEW_CUSTOM_VALUE
7002 CustomValue[newx][newy] = CustomValue[x][y];
7005 ChangeDelay[x][y] = 0;
7006 ChangePage[x][y] = -1;
7007 ChangeCount[x][y] = 0;
7008 ChangeEvent[x][y] = -1;
7010 #if USE_NEW_CUSTOM_VALUE
7011 CustomValue[x][y] = 0;
7014 /* copy animation control values to new field */
7015 GfxFrame[newx][newy] = GfxFrame[x][y];
7016 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
7017 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
7018 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
7020 Pushed[x][y] = Pushed[newx][newy] = FALSE;
7022 /* some elements can leave other elements behind after moving */
7024 if (ei->move_leave_element != EL_EMPTY &&
7025 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
7026 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
7028 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
7029 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
7030 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
7033 int move_leave_element = ei->move_leave_element;
7037 /* this makes it possible to leave the removed element again */
7038 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
7039 move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
7041 /* this makes it possible to leave the removed element again */
7042 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
7043 move_leave_element = stored;
7046 /* this makes it possible to leave the removed element again */
7047 if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
7048 ei->move_leave_element == EL_TRIGGER_ELEMENT)
7049 move_leave_element = stored;
7052 Feld[x][y] = move_leave_element;
7054 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
7055 MovDir[x][y] = direction;
7057 InitField(x, y, FALSE);
7059 if (GFX_CRUMBLED(Feld[x][y]))
7060 DrawLevelFieldCrumbledSandNeighbours(x, y);
7062 if (ELEM_IS_PLAYER(move_leave_element))
7063 RelocatePlayer(x, y, move_leave_element);
7066 /* do this after checking for left-behind element */
7067 ResetGfxAnimation(x, y); /* reset animation values for old field */
7069 if (!CAN_MOVE(element) ||
7070 (CAN_FALL(element) && direction == MV_DOWN &&
7071 (element == EL_SPRING ||
7072 element_info[element].move_pattern == MV_WHEN_PUSHED ||
7073 element_info[element].move_pattern == MV_WHEN_DROPPED)))
7074 GfxDir[x][y] = MovDir[newx][newy] = 0;
7076 DrawLevelField(x, y);
7077 DrawLevelField(newx, newy);
7079 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
7081 /* prevent pushed element from moving on in pushed direction */
7082 if (pushed_by_player && CAN_MOVE(element) &&
7083 element_info[element].move_pattern & MV_ANY_DIRECTION &&
7084 !(element_info[element].move_pattern & direction))
7085 TurnRound(newx, newy);
7087 /* prevent elements on conveyor belt from moving on in last direction */
7088 if (pushed_by_conveyor && CAN_FALL(element) &&
7089 direction & MV_HORIZONTAL)
7090 MovDir[newx][newy] = 0;
7092 if (!pushed_by_player)
7094 int nextx = newx + dx, nexty = newy + dy;
7095 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
7097 WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
7099 if (CAN_FALL(element) && direction == MV_DOWN)
7100 WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
7102 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
7103 CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
7105 #if USE_FIX_IMPACT_COLLISION
7106 if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
7107 CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
7111 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
7113 TestIfBadThingTouchesPlayer(newx, newy);
7114 TestIfBadThingTouchesFriend(newx, newy);
7116 if (!IS_CUSTOM_ELEMENT(element))
7117 TestIfBadThingTouchesOtherBadThing(newx, newy);
7119 else if (element == EL_PENGUIN)
7120 TestIfFriendTouchesBadThing(newx, newy);
7122 /* give the player one last chance (one more frame) to move away */
7123 if (CAN_FALL(element) && direction == MV_DOWN &&
7124 (last_line || (!IS_FREE(x, newy + 1) &&
7125 (!IS_PLAYER(x, newy + 1) ||
7126 game.engine_version < VERSION_IDENT(3,1,1,0)))))
7129 if (pushed_by_player && !game.use_change_when_pushing_bug)
7131 int push_side = MV_DIR_OPPOSITE(direction);
7132 struct PlayerInfo *player = PLAYERINFO(x, y);
7134 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
7135 player->index_bit, push_side);
7136 CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
7137 player->index_bit, push_side);
7140 if (element == EL_EMC_ANDROID && pushed_by_player) /* make another move */
7141 MovDelay[newx][newy] = 1;
7143 CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
7145 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
7148 if (ChangePage[newx][newy] != -1) /* delayed change */
7150 int page = ChangePage[newx][newy];
7151 struct ElementChangeInfo *change = &ei->change_page[page];
7153 ChangePage[newx][newy] = -1;
7155 if (change->can_change)
7157 if (ChangeElement(newx, newy, element, page))
7159 if (change->post_change_function)
7160 change->post_change_function(newx, newy);
7164 if (change->has_action)
7165 ExecuteCustomElementAction(newx, newy, element, page);
7169 TestIfElementHitsCustomElement(newx, newy, direction);
7170 TestIfPlayerTouchesCustomElement(newx, newy);
7171 TestIfElementTouchesCustomElement(newx, newy);
7173 if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
7174 IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
7175 CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
7176 MV_DIR_OPPOSITE(direction));
7179 int AmoebeNachbarNr(int ax, int ay)
7182 int element = Feld[ax][ay];
7184 static int xy[4][2] =
7192 for (i = 0; i < NUM_DIRECTIONS; i++)
7194 int x = ax + xy[i][0];
7195 int y = ay + xy[i][1];
7197 if (!IN_LEV_FIELD(x, y))
7200 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
7201 group_nr = AmoebaNr[x][y];
7207 void AmoebenVereinigen(int ax, int ay)
7209 int i, x, y, xx, yy;
7210 int new_group_nr = AmoebaNr[ax][ay];
7211 static int xy[4][2] =
7219 if (new_group_nr == 0)
7222 for (i = 0; i < NUM_DIRECTIONS; i++)
7227 if (!IN_LEV_FIELD(x, y))
7230 if ((Feld[x][y] == EL_AMOEBA_FULL ||
7231 Feld[x][y] == EL_BD_AMOEBA ||
7232 Feld[x][y] == EL_AMOEBA_DEAD) &&
7233 AmoebaNr[x][y] != new_group_nr)
7235 int old_group_nr = AmoebaNr[x][y];
7237 if (old_group_nr == 0)
7240 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
7241 AmoebaCnt[old_group_nr] = 0;
7242 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
7243 AmoebaCnt2[old_group_nr] = 0;
7245 SCAN_PLAYFIELD(xx, yy)
7247 if (AmoebaNr[xx][yy] == old_group_nr)
7248 AmoebaNr[xx][yy] = new_group_nr;
7254 void AmoebeUmwandeln(int ax, int ay)
7258 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
7260 int group_nr = AmoebaNr[ax][ay];
7265 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
7266 printf("AmoebeUmwandeln(): This should never happen!\n");
7271 SCAN_PLAYFIELD(x, y)
7273 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
7276 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
7280 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
7281 SND_AMOEBA_TURNING_TO_GEM :
7282 SND_AMOEBA_TURNING_TO_ROCK));
7287 static int xy[4][2] =
7295 for (i = 0; i < NUM_DIRECTIONS; i++)
7300 if (!IN_LEV_FIELD(x, y))
7303 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
7305 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
7306 SND_AMOEBA_TURNING_TO_GEM :
7307 SND_AMOEBA_TURNING_TO_ROCK));
7314 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
7317 int group_nr = AmoebaNr[ax][ay];
7318 boolean done = FALSE;
7323 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
7324 printf("AmoebeUmwandelnBD(): This should never happen!\n");
7329 SCAN_PLAYFIELD(x, y)
7331 if (AmoebaNr[x][y] == group_nr &&
7332 (Feld[x][y] == EL_AMOEBA_DEAD ||
7333 Feld[x][y] == EL_BD_AMOEBA ||
7334 Feld[x][y] == EL_AMOEBA_GROWING))
7337 Feld[x][y] = new_element;
7338 InitField(x, y, FALSE);
7339 DrawLevelField(x, y);
7345 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
7346 SND_BD_AMOEBA_TURNING_TO_ROCK :
7347 SND_BD_AMOEBA_TURNING_TO_GEM));
7350 void AmoebeWaechst(int x, int y)
7352 static unsigned long sound_delay = 0;
7353 static unsigned long sound_delay_value = 0;
7355 if (!MovDelay[x][y]) /* start new growing cycle */
7359 if (DelayReached(&sound_delay, sound_delay_value))
7361 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
7362 sound_delay_value = 30;
7366 if (MovDelay[x][y]) /* wait some time before growing bigger */
7369 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7371 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
7372 6 - MovDelay[x][y]);
7374 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
7377 if (!MovDelay[x][y])
7379 Feld[x][y] = Store[x][y];
7381 DrawLevelField(x, y);
7386 void AmoebaDisappearing(int x, int y)
7388 static unsigned long sound_delay = 0;
7389 static unsigned long sound_delay_value = 0;
7391 if (!MovDelay[x][y]) /* start new shrinking cycle */
7395 if (DelayReached(&sound_delay, sound_delay_value))
7396 sound_delay_value = 30;
7399 if (MovDelay[x][y]) /* wait some time before shrinking */
7402 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7404 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
7405 6 - MovDelay[x][y]);
7407 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
7410 if (!MovDelay[x][y])
7412 Feld[x][y] = EL_EMPTY;
7413 DrawLevelField(x, y);
7415 /* don't let mole enter this field in this cycle;
7416 (give priority to objects falling to this field from above) */
7422 void AmoebeAbleger(int ax, int ay)
7425 int element = Feld[ax][ay];
7426 int graphic = el2img(element);
7427 int newax = ax, neway = ay;
7428 boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
7429 static int xy[4][2] =
7437 if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
7439 Feld[ax][ay] = EL_AMOEBA_DEAD;
7440 DrawLevelField(ax, ay);
7444 if (IS_ANIMATED(graphic))
7445 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7447 if (!MovDelay[ax][ay]) /* start making new amoeba field */
7448 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
7450 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
7453 if (MovDelay[ax][ay])
7457 if (can_drop) /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
7460 int x = ax + xy[start][0];
7461 int y = ay + xy[start][1];
7463 if (!IN_LEV_FIELD(x, y))
7466 if (IS_FREE(x, y) ||
7467 CAN_GROW_INTO(Feld[x][y]) ||
7468 Feld[x][y] == EL_QUICKSAND_EMPTY ||
7469 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
7475 if (newax == ax && neway == ay)
7478 else /* normal or "filled" (BD style) amoeba */
7481 boolean waiting_for_player = FALSE;
7483 for (i = 0; i < NUM_DIRECTIONS; i++)
7485 int j = (start + i) % 4;
7486 int x = ax + xy[j][0];
7487 int y = ay + xy[j][1];
7489 if (!IN_LEV_FIELD(x, y))
7492 if (IS_FREE(x, y) ||
7493 CAN_GROW_INTO(Feld[x][y]) ||
7494 Feld[x][y] == EL_QUICKSAND_EMPTY ||
7495 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
7501 else if (IS_PLAYER(x, y))
7502 waiting_for_player = TRUE;
7505 if (newax == ax && neway == ay) /* amoeba cannot grow */
7507 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
7509 Feld[ax][ay] = EL_AMOEBA_DEAD;
7510 DrawLevelField(ax, ay);
7511 AmoebaCnt[AmoebaNr[ax][ay]]--;
7513 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
7515 if (element == EL_AMOEBA_FULL)
7516 AmoebeUmwandeln(ax, ay);
7517 else if (element == EL_BD_AMOEBA)
7518 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
7523 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
7525 /* amoeba gets larger by growing in some direction */
7527 int new_group_nr = AmoebaNr[ax][ay];
7530 if (new_group_nr == 0)
7532 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
7533 printf("AmoebeAbleger(): This should never happen!\n");
7538 AmoebaNr[newax][neway] = new_group_nr;
7539 AmoebaCnt[new_group_nr]++;
7540 AmoebaCnt2[new_group_nr]++;
7542 /* if amoeba touches other amoeba(s) after growing, unify them */
7543 AmoebenVereinigen(newax, neway);
7545 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
7547 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
7553 if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
7554 (neway == lev_fieldy - 1 && newax != ax))
7556 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
7557 Store[newax][neway] = element;
7559 else if (neway == ay || element == EL_EMC_DRIPPER)
7561 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
7563 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
7567 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
7568 Feld[ax][ay] = EL_AMOEBA_DROPPING;
7569 Store[ax][ay] = EL_AMOEBA_DROP;
7570 ContinueMoving(ax, ay);
7574 DrawLevelField(newax, neway);
7577 void Life(int ax, int ay)
7581 int element = Feld[ax][ay];
7582 int graphic = el2img(element);
7583 int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
7585 boolean changed = FALSE;
7587 if (IS_ANIMATED(graphic))
7588 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7593 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
7594 MovDelay[ax][ay] = life_time;
7596 if (MovDelay[ax][ay]) /* wait some time before next cycle */
7599 if (MovDelay[ax][ay])
7603 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
7605 int xx = ax+x1, yy = ay+y1;
7608 if (!IN_LEV_FIELD(xx, yy))
7611 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
7613 int x = xx+x2, y = yy+y2;
7615 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
7618 if (((Feld[x][y] == element ||
7619 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
7621 (IS_FREE(x, y) && Stop[x][y]))
7625 if (xx == ax && yy == ay) /* field in the middle */
7627 if (nachbarn < life_parameter[0] ||
7628 nachbarn > life_parameter[1])
7630 Feld[xx][yy] = EL_EMPTY;
7632 DrawLevelField(xx, yy);
7633 Stop[xx][yy] = TRUE;
7637 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
7638 { /* free border field */
7639 if (nachbarn >= life_parameter[2] &&
7640 nachbarn <= life_parameter[3])
7642 Feld[xx][yy] = element;
7643 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
7645 DrawLevelField(xx, yy);
7646 Stop[xx][yy] = TRUE;
7653 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
7654 SND_GAME_OF_LIFE_GROWING);
7657 static void InitRobotWheel(int x, int y)
7659 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
7662 static void RunRobotWheel(int x, int y)
7664 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
7667 static void StopRobotWheel(int x, int y)
7669 if (ZX == x && ZY == y)
7673 static void InitTimegateWheel(int x, int y)
7675 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
7678 static void RunTimegateWheel(int x, int y)
7680 PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
7683 static void InitMagicBallDelay(int x, int y)
7686 ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
7688 ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
7692 static void ActivateMagicBall(int bx, int by)
7696 if (level.ball_random)
7698 int pos_border = RND(8); /* select one of the eight border elements */
7699 int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
7700 int xx = pos_content % 3;
7701 int yy = pos_content / 3;
7706 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
7707 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
7711 for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
7713 int xx = x - bx + 1;
7714 int yy = y - by + 1;
7716 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
7717 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
7721 game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
7724 void CheckExit(int x, int y)
7726 if (local_player->gems_still_needed > 0 ||
7727 local_player->sokobanfields_still_needed > 0 ||
7728 local_player->lights_still_needed > 0)
7730 int element = Feld[x][y];
7731 int graphic = el2img(element);
7733 if (IS_ANIMATED(graphic))
7734 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7739 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7742 Feld[x][y] = EL_EXIT_OPENING;
7744 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
7747 void CheckExitEM(int x, int y)
7749 if (local_player->gems_still_needed > 0 ||
7750 local_player->sokobanfields_still_needed > 0 ||
7751 local_player->lights_still_needed > 0)
7753 int element = Feld[x][y];
7754 int graphic = el2img(element);
7756 if (IS_ANIMATED(graphic))
7757 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7762 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7765 Feld[x][y] = EL_EM_EXIT_OPENING;
7767 PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
7770 void CheckExitSteel(int x, int y)
7772 if (local_player->gems_still_needed > 0 ||
7773 local_player->sokobanfields_still_needed > 0 ||
7774 local_player->lights_still_needed > 0)
7776 int element = Feld[x][y];
7777 int graphic = el2img(element);
7779 if (IS_ANIMATED(graphic))
7780 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7785 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7788 Feld[x][y] = EL_STEEL_EXIT_OPENING;
7790 PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
7793 void CheckExitSteelEM(int x, int y)
7795 if (local_player->gems_still_needed > 0 ||
7796 local_player->sokobanfields_still_needed > 0 ||
7797 local_player->lights_still_needed > 0)
7799 int element = Feld[x][y];
7800 int graphic = el2img(element);
7802 if (IS_ANIMATED(graphic))
7803 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7808 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7811 Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
7813 PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
7816 void CheckExitSP(int x, int y)
7818 if (local_player->gems_still_needed > 0)
7820 int element = Feld[x][y];
7821 int graphic = el2img(element);
7823 if (IS_ANIMATED(graphic))
7824 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7829 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7832 Feld[x][y] = EL_SP_EXIT_OPENING;
7834 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
7837 static void CloseAllOpenTimegates()
7841 SCAN_PLAYFIELD(x, y)
7843 int element = Feld[x][y];
7845 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
7847 Feld[x][y] = EL_TIMEGATE_CLOSING;
7849 PlayLevelSoundAction(x, y, ACTION_CLOSING);
7854 void DrawTwinkleOnField(int x, int y)
7856 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
7859 if (Feld[x][y] == EL_BD_DIAMOND)
7862 if (MovDelay[x][y] == 0) /* next animation frame */
7863 MovDelay[x][y] = 11 * !GetSimpleRandom(500);
7865 if (MovDelay[x][y] != 0) /* wait some time before next frame */
7869 if (setup.direct_draw && MovDelay[x][y])
7870 SetDrawtoField(DRAW_BUFFERED);
7872 DrawLevelElementAnimation(x, y, Feld[x][y]);
7874 if (MovDelay[x][y] != 0)
7876 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
7877 10 - MovDelay[x][y]);
7879 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
7881 if (setup.direct_draw)
7885 dest_x = FX + SCREENX(x) * TILEX;
7886 dest_y = FY + SCREENY(y) * TILEY;
7888 BlitBitmap(drawto_field, window,
7889 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
7890 SetDrawtoField(DRAW_DIRECT);
7896 void MauerWaechst(int x, int y)
7900 if (!MovDelay[x][y]) /* next animation frame */
7901 MovDelay[x][y] = 3 * delay;
7903 if (MovDelay[x][y]) /* wait some time before next frame */
7907 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7909 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
7910 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
7912 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
7915 if (!MovDelay[x][y])
7917 if (MovDir[x][y] == MV_LEFT)
7919 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
7920 DrawLevelField(x - 1, y);
7922 else if (MovDir[x][y] == MV_RIGHT)
7924 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
7925 DrawLevelField(x + 1, y);
7927 else if (MovDir[x][y] == MV_UP)
7929 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
7930 DrawLevelField(x, y - 1);
7934 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
7935 DrawLevelField(x, y + 1);
7938 Feld[x][y] = Store[x][y];
7940 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7941 DrawLevelField(x, y);
7946 void MauerAbleger(int ax, int ay)
7948 int element = Feld[ax][ay];
7949 int graphic = el2img(element);
7950 boolean oben_frei = FALSE, unten_frei = FALSE;
7951 boolean links_frei = FALSE, rechts_frei = FALSE;
7952 boolean oben_massiv = FALSE, unten_massiv = FALSE;
7953 boolean links_massiv = FALSE, rechts_massiv = FALSE;
7954 boolean new_wall = FALSE;
7956 if (IS_ANIMATED(graphic))
7957 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7959 if (!MovDelay[ax][ay]) /* start building new wall */
7960 MovDelay[ax][ay] = 6;
7962 if (MovDelay[ax][ay]) /* wait some time before building new wall */
7965 if (MovDelay[ax][ay])
7969 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
7971 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
7973 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
7975 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
7978 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
7979 element == EL_EXPANDABLE_WALL_ANY)
7983 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
7984 Store[ax][ay-1] = element;
7985 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
7986 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
7987 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
7988 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
7993 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
7994 Store[ax][ay+1] = element;
7995 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
7996 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
7997 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
7998 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
8003 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
8004 element == EL_EXPANDABLE_WALL_ANY ||
8005 element == EL_EXPANDABLE_WALL ||
8006 element == EL_BD_EXPANDABLE_WALL)
8010 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
8011 Store[ax-1][ay] = element;
8012 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
8013 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
8014 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
8015 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
8021 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
8022 Store[ax+1][ay] = element;
8023 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
8024 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
8025 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
8026 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
8031 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
8032 DrawLevelField(ax, ay);
8034 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
8036 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
8037 unten_massiv = TRUE;
8038 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
8039 links_massiv = TRUE;
8040 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
8041 rechts_massiv = TRUE;
8043 if (((oben_massiv && unten_massiv) ||
8044 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
8045 element == EL_EXPANDABLE_WALL) &&
8046 ((links_massiv && rechts_massiv) ||
8047 element == EL_EXPANDABLE_WALL_VERTICAL))
8048 Feld[ax][ay] = EL_WALL;
8051 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
8054 void MauerAblegerStahl(int ax, int ay)
8056 int element = Feld[ax][ay];
8057 int graphic = el2img(element);
8058 boolean oben_frei = FALSE, unten_frei = FALSE;
8059 boolean links_frei = FALSE, rechts_frei = FALSE;
8060 boolean oben_massiv = FALSE, unten_massiv = FALSE;
8061 boolean links_massiv = FALSE, rechts_massiv = FALSE;
8062 boolean new_wall = FALSE;
8064 if (IS_ANIMATED(graphic))
8065 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8067 if (!MovDelay[ax][ay]) /* start building new wall */
8068 MovDelay[ax][ay] = 6;
8070 if (MovDelay[ax][ay]) /* wait some time before building new wall */
8073 if (MovDelay[ax][ay])
8077 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
8079 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
8081 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
8083 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
8086 if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
8087 element == EL_EXPANDABLE_STEELWALL_ANY)
8091 Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
8092 Store[ax][ay-1] = element;
8093 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
8094 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
8095 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
8096 IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
8101 Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
8102 Store[ax][ay+1] = element;
8103 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
8104 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
8105 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
8106 IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
8111 if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
8112 element == EL_EXPANDABLE_STEELWALL_ANY)
8116 Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
8117 Store[ax-1][ay] = element;
8118 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
8119 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
8120 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
8121 IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
8127 Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
8128 Store[ax+1][ay] = element;
8129 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
8130 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
8131 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
8132 IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
8137 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
8139 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
8140 unten_massiv = TRUE;
8141 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
8142 links_massiv = TRUE;
8143 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
8144 rechts_massiv = TRUE;
8146 if (((oben_massiv && unten_massiv) ||
8147 element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
8148 ((links_massiv && rechts_massiv) ||
8149 element == EL_EXPANDABLE_STEELWALL_VERTICAL))
8150 Feld[ax][ay] = EL_WALL;
8153 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
8156 void CheckForDragon(int x, int y)
8159 boolean dragon_found = FALSE;
8160 static int xy[4][2] =
8168 for (i = 0; i < NUM_DIRECTIONS; i++)
8170 for (j = 0; j < 4; j++)
8172 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
8174 if (IN_LEV_FIELD(xx, yy) &&
8175 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
8177 if (Feld[xx][yy] == EL_DRAGON)
8178 dragon_found = TRUE;
8187 for (i = 0; i < NUM_DIRECTIONS; i++)
8189 for (j = 0; j < 3; j++)
8191 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
8193 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
8195 Feld[xx][yy] = EL_EMPTY;
8196 DrawLevelField(xx, yy);
8205 static void InitBuggyBase(int x, int y)
8207 int element = Feld[x][y];
8208 int activating_delay = FRAMES_PER_SECOND / 4;
8211 (element == EL_SP_BUGGY_BASE ?
8212 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
8213 element == EL_SP_BUGGY_BASE_ACTIVATING ?
8215 element == EL_SP_BUGGY_BASE_ACTIVE ?
8216 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
8219 static void WarnBuggyBase(int x, int y)
8222 static int xy[4][2] =
8230 for (i = 0; i < NUM_DIRECTIONS; i++)
8232 int xx = x + xy[i][0];
8233 int yy = y + xy[i][1];
8235 if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
8237 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
8244 static void InitTrap(int x, int y)
8246 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
8249 static void ActivateTrap(int x, int y)
8251 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
8254 static void ChangeActiveTrap(int x, int y)
8256 int graphic = IMG_TRAP_ACTIVE;
8258 /* if new animation frame was drawn, correct crumbled sand border */
8259 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
8260 DrawLevelFieldCrumbledSand(x, y);
8263 static int getSpecialActionElement(int element, int number, int base_element)
8265 return (element != EL_EMPTY ? element :
8266 number != -1 ? base_element + number - 1 :
8270 static int getModifiedActionNumber(int value_old, int operator, int operand,
8271 int value_min, int value_max)
8273 int value_new = (operator == CA_MODE_SET ? operand :
8274 operator == CA_MODE_ADD ? value_old + operand :
8275 operator == CA_MODE_SUBTRACT ? value_old - operand :
8276 operator == CA_MODE_MULTIPLY ? value_old * operand :
8277 operator == CA_MODE_DIVIDE ? value_old / MAX(1, operand) :
8278 operator == CA_MODE_MODULO ? value_old % MAX(1, operand) :
8281 return (value_new < value_min ? value_min :
8282 value_new > value_max ? value_max :
8286 static void ExecuteCustomElementAction(int x, int y, int element, int page)
8288 struct ElementInfo *ei = &element_info[element];
8289 struct ElementChangeInfo *change = &ei->change_page[page];
8290 int target_element = change->target_element;
8291 int action_type = change->action_type;
8292 int action_mode = change->action_mode;
8293 int action_arg = change->action_arg;
8296 if (!change->has_action)
8299 /* ---------- determine action paramater values -------------------------- */
8301 int level_time_value =
8302 (level.time > 0 ? TimeLeft :
8305 int action_arg_element =
8306 (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player :
8307 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
8308 action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element :
8311 int action_arg_direction =
8312 (action_arg >= CA_ARG_DIRECTION_LEFT &&
8313 action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
8314 action_arg == CA_ARG_DIRECTION_TRIGGER ?
8315 change->actual_trigger_side :
8316 action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
8317 MV_DIR_OPPOSITE(change->actual_trigger_side) :
8320 int action_arg_number_min =
8321 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
8324 int action_arg_number_max =
8325 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
8326 action_type == CA_SET_LEVEL_GEMS ? 999 :
8327 action_type == CA_SET_LEVEL_TIME ? 9999 :
8328 action_type == CA_SET_LEVEL_SCORE ? 99999 :
8329 action_type == CA_SET_CE_VALUE ? 9999 :
8330 action_type == CA_SET_CE_SCORE ? 9999 :
8333 int action_arg_number_reset =
8334 (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
8335 action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
8336 action_type == CA_SET_LEVEL_TIME ? level.time :
8337 action_type == CA_SET_LEVEL_SCORE ? 0 :
8338 #if USE_NEW_CUSTOM_VALUE
8339 action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
8341 action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
8343 action_type == CA_SET_CE_SCORE ? 0 :
8346 int action_arg_number =
8347 (action_arg <= CA_ARG_MAX ? action_arg :
8348 action_arg >= CA_ARG_SPEED_NOT_MOVING &&
8349 action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
8350 action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
8351 action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
8352 action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
8353 action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
8354 #if USE_NEW_CUSTOM_VALUE
8355 action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
8357 action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
8359 action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
8360 action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
8361 action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
8362 action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
8363 action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
8364 action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
8365 action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
8366 action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
8367 action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
8368 action_arg == CA_ARG_ELEMENT_NR_TARGET ? change->target_element :
8369 action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
8372 int action_arg_number_old =
8373 (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
8374 action_type == CA_SET_LEVEL_TIME ? TimeLeft :
8375 action_type == CA_SET_LEVEL_SCORE ? local_player->score :
8376 action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
8377 action_type == CA_SET_CE_SCORE ? ei->collect_score :
8380 int action_arg_number_new =
8381 getModifiedActionNumber(action_arg_number_old,
8382 action_mode, action_arg_number,
8383 action_arg_number_min, action_arg_number_max);
8385 int trigger_player_bits =
8386 (change->actual_trigger_player >= EL_PLAYER_1 &&
8387 change->actual_trigger_player <= EL_PLAYER_4 ?
8388 (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
8391 int action_arg_player_bits =
8392 (action_arg >= CA_ARG_PLAYER_1 &&
8393 action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
8394 action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
8397 /* ---------- execute action -------------------------------------------- */
8399 switch (action_type)
8406 /* ---------- level actions ------------------------------------------- */
8408 case CA_RESTART_LEVEL:
8410 game.restart_level = TRUE;
8415 case CA_SHOW_ENVELOPE:
8417 int element = getSpecialActionElement(action_arg_element,
8418 action_arg_number, EL_ENVELOPE_1);
8420 if (IS_ENVELOPE(element))
8421 local_player->show_envelope = element;
8426 case CA_SET_LEVEL_TIME:
8428 if (level.time > 0) /* only modify limited time value */
8430 TimeLeft = action_arg_number_new;
8432 DrawGameValue_Time(TimeLeft);
8434 if (!TimeLeft && setup.time_limit)
8435 for (i = 0; i < MAX_PLAYERS; i++)
8436 KillPlayer(&stored_player[i]);
8442 case CA_SET_LEVEL_SCORE:
8444 local_player->score = action_arg_number_new;
8446 DrawGameValue_Score(local_player->score);
8451 case CA_SET_LEVEL_GEMS:
8453 local_player->gems_still_needed = action_arg_number_new;
8455 DrawGameValue_Emeralds(local_player->gems_still_needed);
8460 #if !USE_PLAYER_GRAVITY
8461 case CA_SET_LEVEL_GRAVITY:
8463 game.gravity = (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
8464 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
8465 action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
8471 case CA_SET_LEVEL_WIND:
8473 game.wind_direction = action_arg_direction;
8478 /* ---------- player actions ------------------------------------------ */
8480 case CA_MOVE_PLAYER:
8482 /* automatically move to the next field in specified direction */
8483 for (i = 0; i < MAX_PLAYERS; i++)
8484 if (trigger_player_bits & (1 << i))
8485 stored_player[i].programmed_action = action_arg_direction;
8490 case CA_EXIT_PLAYER:
8492 for (i = 0; i < MAX_PLAYERS; i++)
8493 if (action_arg_player_bits & (1 << i))
8494 PlayerWins(&stored_player[i]);
8499 case CA_KILL_PLAYER:
8501 for (i = 0; i < MAX_PLAYERS; i++)
8502 if (action_arg_player_bits & (1 << i))
8503 KillPlayer(&stored_player[i]);
8508 case CA_SET_PLAYER_KEYS:
8510 int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
8511 int element = getSpecialActionElement(action_arg_element,
8512 action_arg_number, EL_KEY_1);
8514 if (IS_KEY(element))
8516 for (i = 0; i < MAX_PLAYERS; i++)
8518 if (trigger_player_bits & (1 << i))
8520 stored_player[i].key[KEY_NR(element)] = key_state;
8522 DrawGameDoorValues();
8530 case CA_SET_PLAYER_SPEED:
8532 for (i = 0; i < MAX_PLAYERS; i++)
8534 if (trigger_player_bits & (1 << i))
8536 int move_stepsize = TILEX / stored_player[i].move_delay_value;
8538 if (action_arg == CA_ARG_SPEED_FASTER &&
8539 stored_player[i].cannot_move)
8541 action_arg_number = STEPSIZE_VERY_SLOW;
8543 else if (action_arg == CA_ARG_SPEED_SLOWER ||
8544 action_arg == CA_ARG_SPEED_FASTER)
8546 action_arg_number = 2;
8547 action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
8550 else if (action_arg == CA_ARG_NUMBER_RESET)
8552 action_arg_number = level.initial_player_stepsize[i];
8556 getModifiedActionNumber(move_stepsize,
8559 action_arg_number_min,
8560 action_arg_number_max);
8562 SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
8569 case CA_SET_PLAYER_SHIELD:
8571 for (i = 0; i < MAX_PLAYERS; i++)
8573 if (trigger_player_bits & (1 << i))
8575 if (action_arg == CA_ARG_SHIELD_OFF)
8577 stored_player[i].shield_normal_time_left = 0;
8578 stored_player[i].shield_deadly_time_left = 0;
8580 else if (action_arg == CA_ARG_SHIELD_NORMAL)
8582 stored_player[i].shield_normal_time_left = 999999;
8584 else if (action_arg == CA_ARG_SHIELD_DEADLY)
8586 stored_player[i].shield_normal_time_left = 999999;
8587 stored_player[i].shield_deadly_time_left = 999999;
8595 #if USE_PLAYER_GRAVITY
8596 case CA_SET_PLAYER_GRAVITY:
8598 for (i = 0; i < MAX_PLAYERS; i++)
8600 if (trigger_player_bits & (1 << i))
8602 stored_player[i].gravity =
8603 (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
8604 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
8605 action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
8606 stored_player[i].gravity);
8614 case CA_SET_PLAYER_ARTWORK:
8616 for (i = 0; i < MAX_PLAYERS; i++)
8618 if (trigger_player_bits & (1 << i))
8620 int artwork_element = action_arg_element;
8622 if (action_arg == CA_ARG_ELEMENT_RESET)
8624 (level.use_artwork_element[i] ? level.artwork_element[i] :
8625 stored_player[i].element_nr);
8627 #if USE_GFX_RESET_PLAYER_ARTWORK
8628 if (stored_player[i].artwork_element != artwork_element)
8629 stored_player[i].Frame = 0;
8632 stored_player[i].artwork_element = artwork_element;
8634 SetPlayerWaiting(&stored_player[i], FALSE);
8636 /* set number of special actions for bored and sleeping animation */
8637 stored_player[i].num_special_action_bored =
8638 get_num_special_action(artwork_element,
8639 ACTION_BORING_1, ACTION_BORING_LAST);
8640 stored_player[i].num_special_action_sleeping =
8641 get_num_special_action(artwork_element,
8642 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
8649 /* ---------- CE actions ---------------------------------------------- */
8651 case CA_SET_CE_VALUE:
8653 #if USE_NEW_CUSTOM_VALUE
8654 int last_ce_value = CustomValue[x][y];
8656 CustomValue[x][y] = action_arg_number_new;
8658 if (CustomValue[x][y] != last_ce_value)
8660 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
8661 CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
8663 if (CustomValue[x][y] == 0)
8665 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
8666 CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
8674 case CA_SET_CE_SCORE:
8676 #if USE_NEW_CUSTOM_VALUE
8677 int last_ce_score = ei->collect_score;
8679 ei->collect_score = action_arg_number_new;
8681 if (ei->collect_score != last_ce_score)
8683 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
8684 CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
8686 if (ei->collect_score == 0)
8690 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
8691 CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
8694 This is a very special case that seems to be a mixture between
8695 CheckElementChange() and CheckTriggeredElementChange(): while
8696 the first one only affects single elements that are triggered
8697 directly, the second one affects multiple elements in the playfield
8698 that are triggered indirectly by another element. This is a third
8699 case: Changing the CE score always affects multiple identical CEs,
8700 so every affected CE must be checked, not only the single CE for
8701 which the CE score was changed in the first place (as every instance
8702 of that CE shares the same CE score, and therefore also can change)!
8704 SCAN_PLAYFIELD(xx, yy)
8706 if (Feld[xx][yy] == element)
8707 CheckElementChange(xx, yy, element, EL_UNDEFINED,
8708 CE_SCORE_GETS_ZERO);
8717 /* ---------- engine actions ------------------------------------------ */
8719 case CA_SET_ENGINE_SCAN_MODE:
8721 InitPlayfieldScanMode(action_arg);
8731 static void CreateFieldExt(int x, int y, int element, boolean is_change)
8733 int old_element = Feld[x][y];
8734 int new_element = get_element_from_group_element(element);
8735 int previous_move_direction = MovDir[x][y];
8736 #if USE_NEW_CUSTOM_VALUE
8737 int last_ce_value = CustomValue[x][y];
8739 boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
8740 boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
8741 boolean add_player_onto_element = (new_element_is_player &&
8742 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
8743 /* this breaks SnakeBite when a snake is
8744 halfway through a door that closes */
8745 /* NOW FIXED AT LEVEL INIT IN files.c */
8746 new_element != EL_SOKOBAN_FIELD_PLAYER &&
8748 IS_WALKABLE(old_element));
8751 /* check if element under the player changes from accessible to unaccessible
8752 (needed for special case of dropping element which then changes) */
8753 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
8754 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
8762 if (!add_player_onto_element)
8764 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
8765 RemoveMovingField(x, y);
8769 Feld[x][y] = new_element;
8771 #if !USE_GFX_RESET_GFX_ANIMATION
8772 ResetGfxAnimation(x, y);
8773 ResetRandomAnimationValue(x, y);
8776 if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
8777 MovDir[x][y] = previous_move_direction;
8779 #if USE_NEW_CUSTOM_VALUE
8780 if (element_info[new_element].use_last_ce_value)
8781 CustomValue[x][y] = last_ce_value;
8784 InitField_WithBug1(x, y, FALSE);
8786 new_element = Feld[x][y]; /* element may have changed */
8788 #if USE_GFX_RESET_GFX_ANIMATION
8789 ResetGfxAnimation(x, y);
8790 ResetRandomAnimationValue(x, y);
8793 DrawLevelField(x, y);
8795 if (GFX_CRUMBLED(new_element))
8796 DrawLevelFieldCrumbledSandNeighbours(x, y);
8800 /* check if element under the player changes from accessible to unaccessible
8801 (needed for special case of dropping element which then changes) */
8802 /* (must be checked after creating new element for walkable group elements) */
8803 #if USE_FIX_KILLED_BY_NON_WALKABLE
8804 if (IS_PLAYER(x, y) && !player_explosion_protected &&
8805 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
8812 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
8813 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
8822 /* "ChangeCount" not set yet to allow "entered by player" change one time */
8823 if (new_element_is_player)
8824 RelocatePlayer(x, y, new_element);
8827 ChangeCount[x][y]++; /* count number of changes in the same frame */
8829 TestIfBadThingTouchesPlayer(x, y);
8830 TestIfPlayerTouchesCustomElement(x, y);
8831 TestIfElementTouchesCustomElement(x, y);
8834 static void CreateField(int x, int y, int element)
8836 CreateFieldExt(x, y, element, FALSE);
8839 static void CreateElementFromChange(int x, int y, int element)
8841 element = GET_VALID_RUNTIME_ELEMENT(element);
8843 #if USE_STOP_CHANGED_ELEMENTS
8844 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
8846 int old_element = Feld[x][y];
8848 /* prevent changed element from moving in same engine frame
8849 unless both old and new element can either fall or move */
8850 if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
8851 (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
8856 CreateFieldExt(x, y, element, TRUE);
8859 static boolean ChangeElement(int x, int y, int element, int page)
8861 struct ElementInfo *ei = &element_info[element];
8862 struct ElementChangeInfo *change = &ei->change_page[page];
8863 int ce_value = CustomValue[x][y];
8864 int ce_score = ei->collect_score;
8866 int old_element = Feld[x][y];
8868 /* always use default change event to prevent running into a loop */
8869 if (ChangeEvent[x][y] == -1)
8870 ChangeEvent[x][y] = CE_DELAY;
8872 if (ChangeEvent[x][y] == CE_DELAY)
8874 /* reset actual trigger element, trigger player and action element */
8875 change->actual_trigger_element = EL_EMPTY;
8876 change->actual_trigger_player = EL_PLAYER_1;
8877 change->actual_trigger_side = CH_SIDE_NONE;
8878 change->actual_trigger_ce_value = 0;
8879 change->actual_trigger_ce_score = 0;
8882 /* do not change elements more than a specified maximum number of changes */
8883 if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
8886 ChangeCount[x][y]++; /* count number of changes in the same frame */
8888 if (change->explode)
8895 if (change->use_target_content)
8897 boolean complete_replace = TRUE;
8898 boolean can_replace[3][3];
8901 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
8904 boolean is_walkable;
8905 boolean is_diggable;
8906 boolean is_collectible;
8907 boolean is_removable;
8908 boolean is_destructible;
8909 int ex = x + xx - 1;
8910 int ey = y + yy - 1;
8911 int content_element = change->target_content.e[xx][yy];
8914 can_replace[xx][yy] = TRUE;
8916 if (ex == x && ey == y) /* do not check changing element itself */
8919 if (content_element == EL_EMPTY_SPACE)
8921 can_replace[xx][yy] = FALSE; /* do not replace border with space */
8926 if (!IN_LEV_FIELD(ex, ey))
8928 can_replace[xx][yy] = FALSE;
8929 complete_replace = FALSE;
8936 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
8937 e = MovingOrBlocked2Element(ex, ey);
8939 is_empty = (IS_FREE(ex, ey) ||
8940 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
8942 is_walkable = (is_empty || IS_WALKABLE(e));
8943 is_diggable = (is_empty || IS_DIGGABLE(e));
8944 is_collectible = (is_empty || IS_COLLECTIBLE(e));
8945 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
8946 is_removable = (is_diggable || is_collectible);
8948 can_replace[xx][yy] =
8949 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
8950 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
8951 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
8952 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
8953 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
8954 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
8955 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
8957 if (!can_replace[xx][yy])
8958 complete_replace = FALSE;
8961 if (!change->only_if_complete || complete_replace)
8963 boolean something_has_changed = FALSE;
8965 if (change->only_if_complete && change->use_random_replace &&
8966 RND(100) < change->random_percentage)
8969 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
8971 int ex = x + xx - 1;
8972 int ey = y + yy - 1;
8973 int content_element;
8975 if (can_replace[xx][yy] && (!change->use_random_replace ||
8976 RND(100) < change->random_percentage))
8978 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
8979 RemoveMovingField(ex, ey);
8981 ChangeEvent[ex][ey] = ChangeEvent[x][y];
8983 content_element = change->target_content.e[xx][yy];
8984 target_element = GET_TARGET_ELEMENT(element, content_element, change,
8985 ce_value, ce_score);
8987 CreateElementFromChange(ex, ey, target_element);
8989 something_has_changed = TRUE;
8991 /* for symmetry reasons, freeze newly created border elements */
8992 if (ex != x || ey != y)
8993 Stop[ex][ey] = TRUE; /* no more moving in this frame */
8997 if (something_has_changed)
8999 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
9000 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
9006 target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
9007 ce_value, ce_score);
9009 if (element == EL_DIAGONAL_GROWING ||
9010 element == EL_DIAGONAL_SHRINKING)
9012 target_element = Store[x][y];
9014 Store[x][y] = EL_EMPTY;
9017 CreateElementFromChange(x, y, target_element);
9019 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
9020 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
9023 /* this uses direct change before indirect change */
9024 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
9029 #if USE_NEW_DELAYED_ACTION
9031 static void HandleElementChange(int x, int y, int page)
9033 int element = MovingOrBlocked2Element(x, y);
9034 struct ElementInfo *ei = &element_info[element];
9035 struct ElementChangeInfo *change = &ei->change_page[page];
9038 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
9039 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
9042 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
9043 x, y, element, element_info[element].token_name);
9044 printf("HandleElementChange(): This should never happen!\n");
9049 /* this can happen with classic bombs on walkable, changing elements */
9050 if (!CAN_CHANGE_OR_HAS_ACTION(element))
9053 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
9054 ChangeDelay[x][y] = 0;
9060 if (ChangeDelay[x][y] == 0) /* initialize element change */
9062 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
9064 if (change->can_change)
9066 ResetGfxAnimation(x, y);
9067 ResetRandomAnimationValue(x, y);
9069 if (change->pre_change_function)
9070 change->pre_change_function(x, y);
9074 ChangeDelay[x][y]--;
9076 if (ChangeDelay[x][y] != 0) /* continue element change */
9078 if (change->can_change)
9080 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9082 if (IS_ANIMATED(graphic))
9083 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9085 if (change->change_function)
9086 change->change_function(x, y);
9089 else /* finish element change */
9091 if (ChangePage[x][y] != -1) /* remember page from delayed change */
9093 page = ChangePage[x][y];
9094 ChangePage[x][y] = -1;
9096 change = &ei->change_page[page];
9099 if (IS_MOVING(x, y)) /* never change a running system ;-) */
9101 ChangeDelay[x][y] = 1; /* try change after next move step */
9102 ChangePage[x][y] = page; /* remember page to use for change */
9107 if (change->can_change)
9109 if (ChangeElement(x, y, element, page))
9111 if (change->post_change_function)
9112 change->post_change_function(x, y);
9116 if (change->has_action)
9117 ExecuteCustomElementAction(x, y, element, page);
9123 static void HandleElementChange(int x, int y, int page)
9125 int element = MovingOrBlocked2Element(x, y);
9126 struct ElementInfo *ei = &element_info[element];
9127 struct ElementChangeInfo *change = &ei->change_page[page];
9130 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
9133 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
9134 x, y, element, element_info[element].token_name);
9135 printf("HandleElementChange(): This should never happen!\n");
9140 /* this can happen with classic bombs on walkable, changing elements */
9141 if (!CAN_CHANGE(element))
9144 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
9145 ChangeDelay[x][y] = 0;
9151 if (ChangeDelay[x][y] == 0) /* initialize element change */
9153 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
9155 ResetGfxAnimation(x, y);
9156 ResetRandomAnimationValue(x, y);
9158 if (change->pre_change_function)
9159 change->pre_change_function(x, y);
9162 ChangeDelay[x][y]--;
9164 if (ChangeDelay[x][y] != 0) /* continue element change */
9166 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9168 if (IS_ANIMATED(graphic))
9169 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9171 if (change->change_function)
9172 change->change_function(x, y);
9174 else /* finish element change */
9176 if (ChangePage[x][y] != -1) /* remember page from delayed change */
9178 page = ChangePage[x][y];
9179 ChangePage[x][y] = -1;
9181 change = &ei->change_page[page];
9184 if (IS_MOVING(x, y)) /* never change a running system ;-) */
9186 ChangeDelay[x][y] = 1; /* try change after next move step */
9187 ChangePage[x][y] = page; /* remember page to use for change */
9192 if (ChangeElement(x, y, element, page))
9194 if (change->post_change_function)
9195 change->post_change_function(x, y);
9202 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
9203 int trigger_element,
9209 boolean change_done_any = FALSE;
9210 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
9213 if (!(trigger_events[trigger_element][trigger_event]))
9217 printf("::: CheckTriggeredElementChangeExt %d ... [%d, %d, %d, '%s']\n",
9218 trigger_event, recursion_loop_depth, recursion_loop_detected,
9219 recursion_loop_element, EL_NAME(recursion_loop_element));
9222 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
9224 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
9226 int element = EL_CUSTOM_START + i;
9227 boolean change_done = FALSE;
9230 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
9231 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
9234 for (p = 0; p < element_info[element].num_change_pages; p++)
9236 struct ElementChangeInfo *change = &element_info[element].change_page[p];
9238 if (change->can_change_or_has_action &&
9239 change->has_event[trigger_event] &&
9240 change->trigger_side & trigger_side &&
9241 change->trigger_player & trigger_player &&
9242 change->trigger_page & trigger_page_bits &&
9243 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
9245 change->actual_trigger_element = trigger_element;
9246 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
9247 change->actual_trigger_side = trigger_side;
9248 change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
9249 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
9251 if ((change->can_change && !change_done) || change->has_action)
9255 SCAN_PLAYFIELD(x, y)
9257 if (Feld[x][y] == element)
9259 if (change->can_change && !change_done)
9261 ChangeDelay[x][y] = 1;
9262 ChangeEvent[x][y] = trigger_event;
9264 HandleElementChange(x, y, p);
9266 #if USE_NEW_DELAYED_ACTION
9267 else if (change->has_action)
9269 ExecuteCustomElementAction(x, y, element, p);
9270 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
9273 if (change->has_action)
9275 ExecuteCustomElementAction(x, y, element, p);
9276 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
9282 if (change->can_change)
9285 change_done_any = TRUE;
9292 RECURSION_LOOP_DETECTION_END();
9294 return change_done_any;
9297 static boolean CheckElementChangeExt(int x, int y,
9299 int trigger_element,
9304 boolean change_done = FALSE;
9307 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
9308 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
9311 if (Feld[x][y] == EL_BLOCKED)
9313 Blocked2Moving(x, y, &x, &y);
9314 element = Feld[x][y];
9318 /* check if element has already changed */
9319 if (Feld[x][y] != element)
9322 /* check if element has already changed or is about to change after moving */
9323 if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
9324 Feld[x][y] != element) ||
9326 (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
9327 (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
9328 ChangePage[x][y] != -1)))
9333 printf("::: CheckElementChangeExt %d ... [%d, %d, %d, '%s']\n",
9334 trigger_event, recursion_loop_depth, recursion_loop_detected,
9335 recursion_loop_element, EL_NAME(recursion_loop_element));
9338 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
9340 for (p = 0; p < element_info[element].num_change_pages; p++)
9342 struct ElementChangeInfo *change = &element_info[element].change_page[p];
9344 /* check trigger element for all events where the element that is checked
9345 for changing interacts with a directly adjacent element -- this is
9346 different to element changes that affect other elements to change on the
9347 whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
9348 boolean check_trigger_element =
9349 (trigger_event == CE_TOUCHING_X ||
9350 trigger_event == CE_HITTING_X ||
9351 trigger_event == CE_HIT_BY_X ||
9353 /* this one was forgotten until 3.2.3 */
9354 trigger_event == CE_DIGGING_X);
9357 if (change->can_change_or_has_action &&
9358 change->has_event[trigger_event] &&
9359 change->trigger_side & trigger_side &&
9360 change->trigger_player & trigger_player &&
9361 (!check_trigger_element ||
9362 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
9364 change->actual_trigger_element = trigger_element;
9365 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
9366 change->actual_trigger_side = trigger_side;
9367 change->actual_trigger_ce_value = CustomValue[x][y];
9368 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
9370 /* special case: trigger element not at (x,y) position for some events */
9371 if (check_trigger_element)
9383 { 0, 0 }, { 0, 0 }, { 0, 0 },
9387 int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
9388 int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
9390 change->actual_trigger_ce_value = CustomValue[xx][yy];
9391 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
9394 if (change->can_change && !change_done)
9396 ChangeDelay[x][y] = 1;
9397 ChangeEvent[x][y] = trigger_event;
9399 HandleElementChange(x, y, p);
9403 #if USE_NEW_DELAYED_ACTION
9404 else if (change->has_action)
9406 ExecuteCustomElementAction(x, y, element, p);
9407 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
9410 if (change->has_action)
9412 ExecuteCustomElementAction(x, y, element, p);
9413 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
9419 RECURSION_LOOP_DETECTION_END();
9424 static void PlayPlayerSound(struct PlayerInfo *player)
9426 int jx = player->jx, jy = player->jy;
9427 int sound_element = player->artwork_element;
9428 int last_action = player->last_action_waiting;
9429 int action = player->action_waiting;
9431 if (player->is_waiting)
9433 if (action != last_action)
9434 PlayLevelSoundElementAction(jx, jy, sound_element, action);
9436 PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
9440 if (action != last_action)
9441 StopSound(element_info[sound_element].sound[last_action]);
9443 if (last_action == ACTION_SLEEPING)
9444 PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
9448 static void PlayAllPlayersSound()
9452 for (i = 0; i < MAX_PLAYERS; i++)
9453 if (stored_player[i].active)
9454 PlayPlayerSound(&stored_player[i]);
9457 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
9459 boolean last_waiting = player->is_waiting;
9460 int move_dir = player->MovDir;
9462 player->dir_waiting = move_dir;
9463 player->last_action_waiting = player->action_waiting;
9467 if (!last_waiting) /* not waiting -> waiting */
9469 player->is_waiting = TRUE;
9471 player->frame_counter_bored =
9473 game.player_boring_delay_fixed +
9474 GetSimpleRandom(game.player_boring_delay_random);
9475 player->frame_counter_sleeping =
9477 game.player_sleeping_delay_fixed +
9478 GetSimpleRandom(game.player_sleeping_delay_random);
9480 InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
9483 if (game.player_sleeping_delay_fixed +
9484 game.player_sleeping_delay_random > 0 &&
9485 player->anim_delay_counter == 0 &&
9486 player->post_delay_counter == 0 &&
9487 FrameCounter >= player->frame_counter_sleeping)
9488 player->is_sleeping = TRUE;
9489 else if (game.player_boring_delay_fixed +
9490 game.player_boring_delay_random > 0 &&
9491 FrameCounter >= player->frame_counter_bored)
9492 player->is_bored = TRUE;
9494 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
9495 player->is_bored ? ACTION_BORING :
9498 if (player->is_sleeping && player->use_murphy)
9500 /* special case for sleeping Murphy when leaning against non-free tile */
9502 if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
9503 (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
9504 !IS_MOVING(player->jx - 1, player->jy)))
9506 else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
9507 (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
9508 !IS_MOVING(player->jx + 1, player->jy)))
9509 move_dir = MV_RIGHT;
9511 player->is_sleeping = FALSE;
9513 player->dir_waiting = move_dir;
9516 if (player->is_sleeping)
9518 if (player->num_special_action_sleeping > 0)
9520 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
9522 int last_special_action = player->special_action_sleeping;
9523 int num_special_action = player->num_special_action_sleeping;
9524 int special_action =
9525 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
9526 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
9527 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
9528 last_special_action + 1 : ACTION_SLEEPING);
9529 int special_graphic =
9530 el_act_dir2img(player->artwork_element, special_action, move_dir);
9532 player->anim_delay_counter =
9533 graphic_info[special_graphic].anim_delay_fixed +
9534 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
9535 player->post_delay_counter =
9536 graphic_info[special_graphic].post_delay_fixed +
9537 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
9539 player->special_action_sleeping = special_action;
9542 if (player->anim_delay_counter > 0)
9544 player->action_waiting = player->special_action_sleeping;
9545 player->anim_delay_counter--;
9547 else if (player->post_delay_counter > 0)
9549 player->post_delay_counter--;
9553 else if (player->is_bored)
9555 if (player->num_special_action_bored > 0)
9557 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
9559 int special_action =
9560 ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
9561 int special_graphic =
9562 el_act_dir2img(player->artwork_element, special_action, move_dir);
9564 player->anim_delay_counter =
9565 graphic_info[special_graphic].anim_delay_fixed +
9566 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
9567 player->post_delay_counter =
9568 graphic_info[special_graphic].post_delay_fixed +
9569 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
9571 player->special_action_bored = special_action;
9574 if (player->anim_delay_counter > 0)
9576 player->action_waiting = player->special_action_bored;
9577 player->anim_delay_counter--;
9579 else if (player->post_delay_counter > 0)
9581 player->post_delay_counter--;
9586 else if (last_waiting) /* waiting -> not waiting */
9588 player->is_waiting = FALSE;
9589 player->is_bored = FALSE;
9590 player->is_sleeping = FALSE;
9592 player->frame_counter_bored = -1;
9593 player->frame_counter_sleeping = -1;
9595 player->anim_delay_counter = 0;
9596 player->post_delay_counter = 0;
9598 player->dir_waiting = player->MovDir;
9599 player->action_waiting = ACTION_DEFAULT;
9601 player->special_action_bored = ACTION_DEFAULT;
9602 player->special_action_sleeping = ACTION_DEFAULT;
9606 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
9608 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
9609 int left = player_action & JOY_LEFT;
9610 int right = player_action & JOY_RIGHT;
9611 int up = player_action & JOY_UP;
9612 int down = player_action & JOY_DOWN;
9613 int button1 = player_action & JOY_BUTTON_1;
9614 int button2 = player_action & JOY_BUTTON_2;
9615 int dx = (left ? -1 : right ? 1 : 0);
9616 int dy = (up ? -1 : down ? 1 : 0);
9618 if (!player->active || tape.pausing)
9624 snapped = SnapField(player, dx, dy);
9628 dropped = DropElement(player);
9630 moved = MovePlayer(player, dx, dy);
9633 if (tape.single_step && tape.recording && !tape.pausing)
9635 if (button1 || (dropped && !moved))
9637 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9638 SnapField(player, 0, 0); /* stop snapping */
9642 SetPlayerWaiting(player, FALSE);
9644 return player_action;
9648 /* no actions for this player (no input at player's configured device) */
9650 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
9651 SnapField(player, 0, 0);
9652 CheckGravityMovementWhenNotMoving(player);
9654 if (player->MovPos == 0)
9655 SetPlayerWaiting(player, TRUE);
9657 if (player->MovPos == 0) /* needed for tape.playing */
9658 player->is_moving = FALSE;
9660 player->is_dropping = FALSE;
9661 player->is_dropping_pressed = FALSE;
9662 player->drop_pressed_delay = 0;
9668 static void CheckLevelTime()
9672 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9674 if (level.native_em_level->lev->home == 0) /* all players at home */
9676 PlayerWins(local_player);
9678 AllPlayersGone = TRUE;
9680 level.native_em_level->lev->home = -1;
9683 if (level.native_em_level->ply[0]->alive == 0 &&
9684 level.native_em_level->ply[1]->alive == 0 &&
9685 level.native_em_level->ply[2]->alive == 0 &&
9686 level.native_em_level->ply[3]->alive == 0) /* all dead */
9687 AllPlayersGone = TRUE;
9690 if (TimeFrames >= FRAMES_PER_SECOND)
9695 for (i = 0; i < MAX_PLAYERS; i++)
9697 struct PlayerInfo *player = &stored_player[i];
9699 if (SHIELD_ON(player))
9701 player->shield_normal_time_left--;
9703 if (player->shield_deadly_time_left > 0)
9704 player->shield_deadly_time_left--;
9708 if (!local_player->LevelSolved && !level.use_step_counter)
9716 if (TimeLeft <= 10 && setup.time_limit)
9717 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
9719 DrawGameValue_Time(TimeLeft);
9721 if (!TimeLeft && setup.time_limit)
9723 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9724 level.native_em_level->lev->killed_out_of_time = TRUE;
9726 for (i = 0; i < MAX_PLAYERS; i++)
9727 KillPlayer(&stored_player[i]);
9730 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
9731 DrawGameValue_Time(TimePlayed);
9733 level.native_em_level->lev->time =
9734 (level.time == 0 ? TimePlayed : TimeLeft);
9737 if (tape.recording || tape.playing)
9738 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
9742 void AdvanceFrameAndPlayerCounters(int player_nr)
9746 /* advance frame counters (global frame counter and time frame counter) */
9750 /* advance player counters (counters for move delay, move animation etc.) */
9751 for (i = 0; i < MAX_PLAYERS; i++)
9753 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
9754 int move_delay_value = stored_player[i].move_delay_value;
9755 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
9757 if (!advance_player_counters) /* not all players may be affected */
9760 #if USE_NEW_PLAYER_ANIM
9761 if (move_frames == 0) /* less than one move per game frame */
9763 int stepsize = TILEX / move_delay_value;
9764 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
9765 int count = (stored_player[i].is_moving ?
9766 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
9768 if (count % delay == 0)
9773 stored_player[i].Frame += move_frames;
9775 if (stored_player[i].MovPos != 0)
9776 stored_player[i].StepFrame += move_frames;
9778 if (stored_player[i].move_delay > 0)
9779 stored_player[i].move_delay--;
9781 /* due to bugs in previous versions, counter must count up, not down */
9782 if (stored_player[i].push_delay != -1)
9783 stored_player[i].push_delay++;
9785 if (stored_player[i].drop_delay > 0)
9786 stored_player[i].drop_delay--;
9788 if (stored_player[i].is_dropping_pressed)
9789 stored_player[i].drop_pressed_delay++;
9793 void StartGameActions(boolean init_network_game, boolean record_tape,
9796 unsigned long new_random_seed = InitRND(random_seed);
9799 TapeStartRecording(new_random_seed);
9801 #if defined(NETWORK_AVALIABLE)
9802 if (init_network_game)
9804 SendToServer_StartPlaying();
9815 static unsigned long game_frame_delay = 0;
9816 unsigned long game_frame_delay_value;
9817 byte *recorded_player_action;
9818 byte summarized_player_action = 0;
9819 byte tape_action[MAX_PLAYERS];
9822 /* detect endless loops, caused by custom element programming */
9823 if (recursion_loop_detected && recursion_loop_depth == 0)
9825 char *message = getStringCat3("Internal Error ! Element ",
9826 EL_NAME(recursion_loop_element),
9827 " caused endless loop ! Quit the game ?");
9829 Error(ERR_WARN, "element '%s' caused endless loop in game engine",
9830 EL_NAME(recursion_loop_element));
9832 RequestQuitGameExt(FALSE, level_editor_test_game, message);
9834 recursion_loop_detected = FALSE; /* if game should be continued */
9841 if (game.restart_level)
9842 StartGameActions(options.network, setup.autorecord, NEW_RANDOMIZE);
9844 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9846 if (level.native_em_level->lev->home == 0) /* all players at home */
9848 PlayerWins(local_player);
9850 AllPlayersGone = TRUE;
9852 level.native_em_level->lev->home = -1;
9855 if (level.native_em_level->ply[0]->alive == 0 &&
9856 level.native_em_level->ply[1]->alive == 0 &&
9857 level.native_em_level->ply[2]->alive == 0 &&
9858 level.native_em_level->ply[3]->alive == 0) /* all dead */
9859 AllPlayersGone = TRUE;
9862 if (local_player->LevelSolved)
9865 if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
9868 if (game_status != GAME_MODE_PLAYING) /* status might have changed */
9871 game_frame_delay_value =
9872 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
9874 if (tape.playing && tape.warp_forward && !tape.pausing)
9875 game_frame_delay_value = 0;
9877 /* ---------- main game synchronization point ---------- */
9879 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
9881 if (network_playing && !network_player_action_received)
9883 /* try to get network player actions in time */
9885 #if defined(NETWORK_AVALIABLE)
9886 /* last chance to get network player actions without main loop delay */
9890 /* game was quit by network peer */
9891 if (game_status != GAME_MODE_PLAYING)
9894 if (!network_player_action_received)
9895 return; /* failed to get network player actions in time */
9897 /* do not yet reset "network_player_action_received" (for tape.pausing) */
9903 /* at this point we know that we really continue executing the game */
9905 network_player_action_received = FALSE;
9907 /* when playing tape, read previously recorded player input from tape data */
9908 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
9911 /* TapePlayAction() may return NULL when toggling to "pause before death" */
9916 if (tape.set_centered_player)
9918 game.centered_player_nr_next = tape.centered_player_nr_next;
9919 game.set_centered_player = TRUE;
9922 for (i = 0; i < MAX_PLAYERS; i++)
9924 summarized_player_action |= stored_player[i].action;
9926 if (!network_playing)
9927 stored_player[i].effective_action = stored_player[i].action;
9930 #if defined(NETWORK_AVALIABLE)
9931 if (network_playing)
9932 SendToServer_MovePlayer(summarized_player_action);
9935 if (!options.network && !setup.team_mode)
9936 local_player->effective_action = summarized_player_action;
9938 if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
9940 for (i = 0; i < MAX_PLAYERS; i++)
9941 stored_player[i].effective_action =
9942 (i == game.centered_player_nr ? summarized_player_action : 0);
9945 if (recorded_player_action != NULL)
9946 for (i = 0; i < MAX_PLAYERS; i++)
9947 stored_player[i].effective_action = recorded_player_action[i];
9949 for (i = 0; i < MAX_PLAYERS; i++)
9951 tape_action[i] = stored_player[i].effective_action;
9953 /* (this can only happen in the R'n'D game engine) */
9954 if (tape.recording && tape_action[i] && !tape.player_participates[i])
9955 tape.player_participates[i] = TRUE; /* player just appeared from CE */
9958 /* only record actions from input devices, but not programmed actions */
9960 TapeRecordAction(tape_action);
9962 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9964 GameActions_EM_Main();
9972 void GameActions_EM_Main()
9974 byte effective_action[MAX_PLAYERS];
9975 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
9978 for (i = 0; i < MAX_PLAYERS; i++)
9979 effective_action[i] = stored_player[i].effective_action;
9981 GameActions_EM(effective_action, warp_mode);
9985 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
9988 void GameActions_RND()
9990 int magic_wall_x = 0, magic_wall_y = 0;
9991 int i, x, y, element, graphic;
9993 InitPlayfieldScanModeVars();
9995 #if USE_ONE_MORE_CHANGE_PER_FRAME
9996 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
9998 SCAN_PLAYFIELD(x, y)
10000 ChangeCount[x][y] = 0;
10001 ChangeEvent[x][y] = -1;
10006 if (game.set_centered_player)
10008 boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
10010 /* switching to "all players" only possible if all players fit to screen */
10011 if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
10013 game.centered_player_nr_next = game.centered_player_nr;
10014 game.set_centered_player = FALSE;
10017 /* do not switch focus to non-existing (or non-active) player */
10018 if (game.centered_player_nr_next >= 0 &&
10019 !stored_player[game.centered_player_nr_next].active)
10021 game.centered_player_nr_next = game.centered_player_nr;
10022 game.set_centered_player = FALSE;
10026 if (game.set_centered_player &&
10027 ScreenMovPos == 0) /* screen currently aligned at tile position */
10031 if (game.centered_player_nr_next == -1)
10033 setScreenCenteredToAllPlayers(&sx, &sy);
10037 sx = stored_player[game.centered_player_nr_next].jx;
10038 sy = stored_player[game.centered_player_nr_next].jy;
10041 game.centered_player_nr = game.centered_player_nr_next;
10042 game.set_centered_player = FALSE;
10044 DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
10045 DrawGameDoorValues();
10048 for (i = 0; i < MAX_PLAYERS; i++)
10050 int actual_player_action = stored_player[i].effective_action;
10053 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
10054 - rnd_equinox_tetrachloride 048
10055 - rnd_equinox_tetrachloride_ii 096
10056 - rnd_emanuel_schmieg 002
10057 - doctor_sloan_ww 001, 020
10059 if (stored_player[i].MovPos == 0)
10060 CheckGravityMovement(&stored_player[i]);
10063 /* overwrite programmed action with tape action */
10064 if (stored_player[i].programmed_action)
10065 actual_player_action = stored_player[i].programmed_action;
10067 PlayerActions(&stored_player[i], actual_player_action);
10069 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
10072 ScrollScreen(NULL, SCROLL_GO_ON);
10074 /* for backwards compatibility, the following code emulates a fixed bug that
10075 occured when pushing elements (causing elements that just made their last
10076 pushing step to already (if possible) make their first falling step in the
10077 same game frame, which is bad); this code is also needed to use the famous
10078 "spring push bug" which is used in older levels and might be wanted to be
10079 used also in newer levels, but in this case the buggy pushing code is only
10080 affecting the "spring" element and no other elements */
10082 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
10084 for (i = 0; i < MAX_PLAYERS; i++)
10086 struct PlayerInfo *player = &stored_player[i];
10087 int x = player->jx;
10088 int y = player->jy;
10090 if (player->active && player->is_pushing && player->is_moving &&
10092 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
10093 Feld[x][y] == EL_SPRING))
10095 ContinueMoving(x, y);
10097 /* continue moving after pushing (this is actually a bug) */
10098 if (!IS_MOVING(x, y))
10100 Stop[x][y] = FALSE;
10106 SCAN_PLAYFIELD(x, y)
10108 ChangeCount[x][y] = 0;
10109 ChangeEvent[x][y] = -1;
10111 /* this must be handled before main playfield loop */
10112 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
10115 if (MovDelay[x][y] <= 0)
10119 #if USE_NEW_SNAP_DELAY
10120 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
10123 if (MovDelay[x][y] <= 0)
10126 DrawLevelField(x, y);
10128 TestIfElementTouchesCustomElement(x, y); /* for empty space */
10134 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
10136 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
10137 printf("GameActions(): This should never happen!\n");
10139 ChangePage[x][y] = -1;
10143 Stop[x][y] = FALSE;
10144 if (WasJustMoving[x][y] > 0)
10145 WasJustMoving[x][y]--;
10146 if (WasJustFalling[x][y] > 0)
10147 WasJustFalling[x][y]--;
10148 if (CheckCollision[x][y] > 0)
10149 CheckCollision[x][y]--;
10150 if (CheckImpact[x][y] > 0)
10151 CheckImpact[x][y]--;
10155 /* reset finished pushing action (not done in ContinueMoving() to allow
10156 continuous pushing animation for elements with zero push delay) */
10157 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
10159 ResetGfxAnimation(x, y);
10160 DrawLevelField(x, y);
10164 if (IS_BLOCKED(x, y))
10168 Blocked2Moving(x, y, &oldx, &oldy);
10169 if (!IS_MOVING(oldx, oldy))
10171 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
10172 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
10173 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
10174 printf("GameActions(): This should never happen!\n");
10180 SCAN_PLAYFIELD(x, y)
10182 element = Feld[x][y];
10183 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10185 ResetGfxFrame(x, y, TRUE);
10187 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
10188 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
10189 ResetRandomAnimationValue(x, y);
10191 SetRandomAnimationValue(x, y);
10193 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
10195 if (IS_INACTIVE(element))
10197 if (IS_ANIMATED(graphic))
10198 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10203 /* this may take place after moving, so 'element' may have changed */
10204 if (IS_CHANGING(x, y) &&
10205 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
10207 int page = element_info[element].event_page_nr[CE_DELAY];
10210 HandleElementChange(x, y, page);
10212 if (CAN_CHANGE(element))
10213 HandleElementChange(x, y, page);
10215 if (HAS_ACTION(element))
10216 ExecuteCustomElementAction(x, y, element, page);
10219 element = Feld[x][y];
10220 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10223 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
10227 element = Feld[x][y];
10228 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10230 if (IS_ANIMATED(graphic) &&
10231 !IS_MOVING(x, y) &&
10233 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10235 if (IS_GEM(element) || element == EL_SP_INFOTRON)
10236 DrawTwinkleOnField(x, y);
10238 else if ((element == EL_ACID ||
10239 element == EL_EXIT_OPEN ||
10240 element == EL_EM_EXIT_OPEN ||
10241 element == EL_SP_EXIT_OPEN ||
10242 element == EL_STEEL_EXIT_OPEN ||
10243 element == EL_EM_STEEL_EXIT_OPEN ||
10244 element == EL_SP_TERMINAL ||
10245 element == EL_SP_TERMINAL_ACTIVE ||
10246 element == EL_EXTRA_TIME ||
10247 element == EL_SHIELD_NORMAL ||
10248 element == EL_SHIELD_DEADLY) &&
10249 IS_ANIMATED(graphic))
10250 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10251 else if (IS_MOVING(x, y))
10252 ContinueMoving(x, y);
10253 else if (IS_ACTIVE_BOMB(element))
10254 CheckDynamite(x, y);
10255 else if (element == EL_AMOEBA_GROWING)
10256 AmoebeWaechst(x, y);
10257 else if (element == EL_AMOEBA_SHRINKING)
10258 AmoebaDisappearing(x, y);
10260 #if !USE_NEW_AMOEBA_CODE
10261 else if (IS_AMOEBALIVE(element))
10262 AmoebeAbleger(x, y);
10265 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
10267 else if (element == EL_EXIT_CLOSED)
10269 else if (element == EL_EM_EXIT_CLOSED)
10271 else if (element == EL_STEEL_EXIT_CLOSED)
10272 CheckExitSteel(x, y);
10273 else if (element == EL_EM_STEEL_EXIT_CLOSED)
10274 CheckExitSteelEM(x, y);
10275 else if (element == EL_SP_EXIT_CLOSED)
10277 else if (element == EL_EXPANDABLE_WALL_GROWING ||
10278 element == EL_EXPANDABLE_STEELWALL_GROWING)
10279 MauerWaechst(x, y);
10280 else if (element == EL_EXPANDABLE_WALL ||
10281 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
10282 element == EL_EXPANDABLE_WALL_VERTICAL ||
10283 element == EL_EXPANDABLE_WALL_ANY ||
10284 element == EL_BD_EXPANDABLE_WALL)
10285 MauerAbleger(x, y);
10286 else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
10287 element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
10288 element == EL_EXPANDABLE_STEELWALL_ANY)
10289 MauerAblegerStahl(x, y);
10290 else if (element == EL_FLAMES)
10291 CheckForDragon(x, y);
10292 else if (element == EL_EXPLOSION)
10293 ; /* drawing of correct explosion animation is handled separately */
10294 else if (element == EL_ELEMENT_SNAPPING ||
10295 element == EL_DIAGONAL_SHRINKING ||
10296 element == EL_DIAGONAL_GROWING)
10298 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
10300 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10302 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
10303 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10305 if (IS_BELT_ACTIVE(element))
10306 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
10308 if (game.magic_wall_active)
10310 int jx = local_player->jx, jy = local_player->jy;
10312 /* play the element sound at the position nearest to the player */
10313 if ((element == EL_MAGIC_WALL_FULL ||
10314 element == EL_MAGIC_WALL_ACTIVE ||
10315 element == EL_MAGIC_WALL_EMPTYING ||
10316 element == EL_BD_MAGIC_WALL_FULL ||
10317 element == EL_BD_MAGIC_WALL_ACTIVE ||
10318 element == EL_BD_MAGIC_WALL_EMPTYING ||
10319 element == EL_DC_MAGIC_WALL_FULL ||
10320 element == EL_DC_MAGIC_WALL_ACTIVE ||
10321 element == EL_DC_MAGIC_WALL_EMPTYING) &&
10322 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
10330 #if USE_NEW_AMOEBA_CODE
10331 /* new experimental amoeba growth stuff */
10332 if (!(FrameCounter % 8))
10334 static unsigned long random = 1684108901;
10336 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
10338 x = RND(lev_fieldx);
10339 y = RND(lev_fieldy);
10340 element = Feld[x][y];
10342 if (!IS_PLAYER(x,y) &&
10343 (element == EL_EMPTY ||
10344 CAN_GROW_INTO(element) ||
10345 element == EL_QUICKSAND_EMPTY ||
10346 element == EL_QUICKSAND_FAST_EMPTY ||
10347 element == EL_ACID_SPLASH_LEFT ||
10348 element == EL_ACID_SPLASH_RIGHT))
10350 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
10351 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
10352 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
10353 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
10354 Feld[x][y] = EL_AMOEBA_DROP;
10357 random = random * 129 + 1;
10363 if (game.explosions_delayed)
10366 game.explosions_delayed = FALSE;
10368 SCAN_PLAYFIELD(x, y)
10370 element = Feld[x][y];
10372 if (ExplodeField[x][y])
10373 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
10374 else if (element == EL_EXPLOSION)
10375 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
10377 ExplodeField[x][y] = EX_TYPE_NONE;
10380 game.explosions_delayed = TRUE;
10383 if (game.magic_wall_active)
10385 if (!(game.magic_wall_time_left % 4))
10387 int element = Feld[magic_wall_x][magic_wall_y];
10389 if (element == EL_BD_MAGIC_WALL_FULL ||
10390 element == EL_BD_MAGIC_WALL_ACTIVE ||
10391 element == EL_BD_MAGIC_WALL_EMPTYING)
10392 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
10393 else if (element == EL_DC_MAGIC_WALL_FULL ||
10394 element == EL_DC_MAGIC_WALL_ACTIVE ||
10395 element == EL_DC_MAGIC_WALL_EMPTYING)
10396 PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
10398 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
10401 if (game.magic_wall_time_left > 0)
10403 game.magic_wall_time_left--;
10404 if (!game.magic_wall_time_left)
10406 SCAN_PLAYFIELD(x, y)
10408 element = Feld[x][y];
10410 if (element == EL_MAGIC_WALL_ACTIVE ||
10411 element == EL_MAGIC_WALL_FULL)
10413 Feld[x][y] = EL_MAGIC_WALL_DEAD;
10414 DrawLevelField(x, y);
10416 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
10417 element == EL_BD_MAGIC_WALL_FULL)
10419 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
10420 DrawLevelField(x, y);
10422 else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
10423 element == EL_DC_MAGIC_WALL_FULL)
10425 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
10426 DrawLevelField(x, y);
10430 game.magic_wall_active = FALSE;
10435 if (game.light_time_left > 0)
10437 game.light_time_left--;
10439 if (game.light_time_left == 0)
10440 RedrawAllLightSwitchesAndInvisibleElements();
10443 if (game.timegate_time_left > 0)
10445 game.timegate_time_left--;
10447 if (game.timegate_time_left == 0)
10448 CloseAllOpenTimegates();
10451 if (game.lenses_time_left > 0)
10453 game.lenses_time_left--;
10455 if (game.lenses_time_left == 0)
10456 RedrawAllInvisibleElementsForLenses();
10459 if (game.magnify_time_left > 0)
10461 game.magnify_time_left--;
10463 if (game.magnify_time_left == 0)
10464 RedrawAllInvisibleElementsForMagnifier();
10467 for (i = 0; i < MAX_PLAYERS; i++)
10469 struct PlayerInfo *player = &stored_player[i];
10471 if (SHIELD_ON(player))
10473 if (player->shield_deadly_time_left)
10474 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
10475 else if (player->shield_normal_time_left)
10476 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
10483 PlayAllPlayersSound();
10485 if (options.debug) /* calculate frames per second */
10487 static unsigned long fps_counter = 0;
10488 static int fps_frames = 0;
10489 unsigned long fps_delay_ms = Counter() - fps_counter;
10493 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
10495 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
10498 fps_counter = Counter();
10501 redraw_mask |= REDRAW_FPS;
10504 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
10506 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
10508 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
10510 local_player->show_envelope = 0;
10513 /* use random number generator in every frame to make it less predictable */
10514 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
10518 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
10520 int min_x = x, min_y = y, max_x = x, max_y = y;
10523 for (i = 0; i < MAX_PLAYERS; i++)
10525 int jx = stored_player[i].jx, jy = stored_player[i].jy;
10527 if (!stored_player[i].active || &stored_player[i] == player)
10530 min_x = MIN(min_x, jx);
10531 min_y = MIN(min_y, jy);
10532 max_x = MAX(max_x, jx);
10533 max_y = MAX(max_y, jy);
10536 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
10539 static boolean AllPlayersInVisibleScreen()
10543 for (i = 0; i < MAX_PLAYERS; i++)
10545 int jx = stored_player[i].jx, jy = stored_player[i].jy;
10547 if (!stored_player[i].active)
10550 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
10557 void ScrollLevel(int dx, int dy)
10559 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
10562 BlitBitmap(drawto_field, drawto_field,
10563 FX + TILEX * (dx == -1) - softscroll_offset,
10564 FY + TILEY * (dy == -1) - softscroll_offset,
10565 SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
10566 SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
10567 FX + TILEX * (dx == 1) - softscroll_offset,
10568 FY + TILEY * (dy == 1) - softscroll_offset);
10572 x = (dx == 1 ? BX1 : BX2);
10573 for (y = BY1; y <= BY2; y++)
10574 DrawScreenField(x, y);
10579 y = (dy == 1 ? BY1 : BY2);
10580 for (x = BX1; x <= BX2; x++)
10581 DrawScreenField(x, y);
10584 redraw_mask |= REDRAW_FIELD;
10587 static boolean canFallDown(struct PlayerInfo *player)
10589 int jx = player->jx, jy = player->jy;
10591 return (IN_LEV_FIELD(jx, jy + 1) &&
10592 (IS_FREE(jx, jy + 1) ||
10593 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
10594 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
10595 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
10598 static boolean canPassField(int x, int y, int move_dir)
10600 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
10601 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
10602 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
10603 int nextx = x + dx;
10604 int nexty = y + dy;
10605 int element = Feld[x][y];
10607 return (IS_PASSABLE_FROM(element, opposite_dir) &&
10608 !CAN_MOVE(element) &&
10609 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
10610 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
10611 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
10614 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
10616 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
10617 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
10618 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
10622 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
10623 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
10624 (IS_DIGGABLE(Feld[newx][newy]) ||
10625 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
10626 canPassField(newx, newy, move_dir)));
10629 static void CheckGravityMovement(struct PlayerInfo *player)
10631 #if USE_PLAYER_GRAVITY
10632 if (player->gravity && !player->programmed_action)
10634 if (game.gravity && !player->programmed_action)
10637 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
10638 int move_dir_vertical = player->effective_action & MV_VERTICAL;
10639 boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
10640 int jx = player->jx, jy = player->jy;
10641 boolean player_is_moving_to_valid_field =
10642 (!player_is_snapping &&
10643 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
10644 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
10645 boolean player_can_fall_down = canFallDown(player);
10647 if (player_can_fall_down &&
10648 !player_is_moving_to_valid_field)
10649 player->programmed_action = MV_DOWN;
10653 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
10655 return CheckGravityMovement(player);
10657 #if USE_PLAYER_GRAVITY
10658 if (player->gravity && !player->programmed_action)
10660 if (game.gravity && !player->programmed_action)
10663 int jx = player->jx, jy = player->jy;
10664 boolean field_under_player_is_free =
10665 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
10666 boolean player_is_standing_on_valid_field =
10667 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
10668 (IS_WALKABLE(Feld[jx][jy]) &&
10669 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
10671 if (field_under_player_is_free && !player_is_standing_on_valid_field)
10672 player->programmed_action = MV_DOWN;
10677 MovePlayerOneStep()
10678 -----------------------------------------------------------------------------
10679 dx, dy: direction (non-diagonal) to try to move the player to
10680 real_dx, real_dy: direction as read from input device (can be diagonal)
10683 boolean MovePlayerOneStep(struct PlayerInfo *player,
10684 int dx, int dy, int real_dx, int real_dy)
10686 int jx = player->jx, jy = player->jy;
10687 int new_jx = jx + dx, new_jy = jy + dy;
10688 #if !USE_FIXED_DONT_RUN_INTO
10692 boolean player_can_move = !player->cannot_move;
10694 if (!player->active || (!dx && !dy))
10695 return MP_NO_ACTION;
10697 player->MovDir = (dx < 0 ? MV_LEFT :
10698 dx > 0 ? MV_RIGHT :
10700 dy > 0 ? MV_DOWN : MV_NONE);
10702 if (!IN_LEV_FIELD(new_jx, new_jy))
10703 return MP_NO_ACTION;
10705 if (!player_can_move)
10707 if (player->MovPos == 0)
10709 player->is_moving = FALSE;
10710 player->is_digging = FALSE;
10711 player->is_collecting = FALSE;
10712 player->is_snapping = FALSE;
10713 player->is_pushing = FALSE;
10718 if (!options.network && game.centered_player_nr == -1 &&
10719 !AllPlayersInSight(player, new_jx, new_jy))
10720 return MP_NO_ACTION;
10722 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
10723 return MP_NO_ACTION;
10726 #if !USE_FIXED_DONT_RUN_INTO
10727 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
10729 /* (moved to DigField()) */
10730 if (player_can_move && DONT_RUN_INTO(element))
10732 if (element == EL_ACID && dx == 0 && dy == 1)
10734 SplashAcid(new_jx, new_jy);
10735 Feld[jx][jy] = EL_PLAYER_1;
10736 InitMovingField(jx, jy, MV_DOWN);
10737 Store[jx][jy] = EL_ACID;
10738 ContinueMoving(jx, jy);
10739 BuryPlayer(player);
10742 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
10748 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
10749 if (can_move != MP_MOVING)
10752 /* check if DigField() has caused relocation of the player */
10753 if (player->jx != jx || player->jy != jy)
10754 return MP_NO_ACTION; /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
10756 StorePlayer[jx][jy] = 0;
10757 player->last_jx = jx;
10758 player->last_jy = jy;
10759 player->jx = new_jx;
10760 player->jy = new_jy;
10761 StorePlayer[new_jx][new_jy] = player->element_nr;
10763 if (player->move_delay_value_next != -1)
10765 player->move_delay_value = player->move_delay_value_next;
10766 player->move_delay_value_next = -1;
10770 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
10772 player->step_counter++;
10774 PlayerVisit[jx][jy] = FrameCounter;
10776 #if USE_UFAST_PLAYER_EXIT_BUGFIX
10777 player->is_moving = TRUE;
10781 /* should better be called in MovePlayer(), but this breaks some tapes */
10782 ScrollPlayer(player, SCROLL_INIT);
10788 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
10790 int jx = player->jx, jy = player->jy;
10791 int old_jx = jx, old_jy = jy;
10792 int moved = MP_NO_ACTION;
10794 if (!player->active)
10799 if (player->MovPos == 0)
10801 player->is_moving = FALSE;
10802 player->is_digging = FALSE;
10803 player->is_collecting = FALSE;
10804 player->is_snapping = FALSE;
10805 player->is_pushing = FALSE;
10811 if (player->move_delay > 0)
10814 player->move_delay = -1; /* set to "uninitialized" value */
10816 /* store if player is automatically moved to next field */
10817 player->is_auto_moving = (player->programmed_action != MV_NONE);
10819 /* remove the last programmed player action */
10820 player->programmed_action = 0;
10822 if (player->MovPos)
10824 /* should only happen if pre-1.2 tape recordings are played */
10825 /* this is only for backward compatibility */
10827 int original_move_delay_value = player->move_delay_value;
10830 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
10834 /* scroll remaining steps with finest movement resolution */
10835 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
10837 while (player->MovPos)
10839 ScrollPlayer(player, SCROLL_GO_ON);
10840 ScrollScreen(NULL, SCROLL_GO_ON);
10842 AdvanceFrameAndPlayerCounters(player->index_nr);
10848 player->move_delay_value = original_move_delay_value;
10851 player->is_active = FALSE;
10853 if (player->last_move_dir & MV_HORIZONTAL)
10855 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
10856 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
10860 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
10861 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
10864 #if USE_FIXED_BORDER_RUNNING_GFX
10865 if (!moved && !player->is_active)
10867 player->is_moving = FALSE;
10868 player->is_digging = FALSE;
10869 player->is_collecting = FALSE;
10870 player->is_snapping = FALSE;
10871 player->is_pushing = FALSE;
10879 if (moved & MP_MOVING && !ScreenMovPos &&
10880 (player->index_nr == game.centered_player_nr ||
10881 game.centered_player_nr == -1))
10883 if (moved & MP_MOVING && !ScreenMovPos &&
10884 (player == local_player || !options.network))
10887 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
10888 int offset = (setup.scroll_delay ? 3 : 0);
10890 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
10892 /* actual player has left the screen -- scroll in that direction */
10893 if (jx != old_jx) /* player has moved horizontally */
10894 scroll_x += (jx - old_jx);
10895 else /* player has moved vertically */
10896 scroll_y += (jy - old_jy);
10900 if (jx != old_jx) /* player has moved horizontally */
10902 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
10903 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
10904 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
10906 /* don't scroll over playfield boundaries */
10907 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
10908 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
10910 /* don't scroll more than one field at a time */
10911 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
10913 /* don't scroll against the player's moving direction */
10914 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
10915 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
10916 scroll_x = old_scroll_x;
10918 else /* player has moved vertically */
10920 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
10921 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
10922 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
10924 /* don't scroll over playfield boundaries */
10925 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
10926 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
10928 /* don't scroll more than one field at a time */
10929 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
10931 /* don't scroll against the player's moving direction */
10932 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
10933 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
10934 scroll_y = old_scroll_y;
10938 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
10941 if (!options.network && game.centered_player_nr == -1 &&
10942 !AllPlayersInVisibleScreen())
10944 scroll_x = old_scroll_x;
10945 scroll_y = old_scroll_y;
10949 if (!options.network && !AllPlayersInVisibleScreen())
10951 scroll_x = old_scroll_x;
10952 scroll_y = old_scroll_y;
10957 ScrollScreen(player, SCROLL_INIT);
10958 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
10963 player->StepFrame = 0;
10965 if (moved & MP_MOVING)
10967 if (old_jx != jx && old_jy == jy)
10968 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
10969 else if (old_jx == jx && old_jy != jy)
10970 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
10972 DrawLevelField(jx, jy); /* for "crumbled sand" */
10974 player->last_move_dir = player->MovDir;
10975 player->is_moving = TRUE;
10976 player->is_snapping = FALSE;
10977 player->is_switching = FALSE;
10978 player->is_dropping = FALSE;
10979 player->is_dropping_pressed = FALSE;
10980 player->drop_pressed_delay = 0;
10983 /* should better be called here than above, but this breaks some tapes */
10984 ScrollPlayer(player, SCROLL_INIT);
10989 CheckGravityMovementWhenNotMoving(player);
10991 player->is_moving = FALSE;
10993 /* at this point, the player is allowed to move, but cannot move right now
10994 (e.g. because of something blocking the way) -- ensure that the player
10995 is also allowed to move in the next frame (in old versions before 3.1.1,
10996 the player was forced to wait again for eight frames before next try) */
10998 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
10999 player->move_delay = 0; /* allow direct movement in the next frame */
11002 if (player->move_delay == -1) /* not yet initialized by DigField() */
11003 player->move_delay = player->move_delay_value;
11005 if (game.engine_version < VERSION_IDENT(3,0,7,0))
11007 TestIfPlayerTouchesBadThing(jx, jy);
11008 TestIfPlayerTouchesCustomElement(jx, jy);
11011 if (!player->active)
11012 RemovePlayer(player);
11017 void ScrollPlayer(struct PlayerInfo *player, int mode)
11019 int jx = player->jx, jy = player->jy;
11020 int last_jx = player->last_jx, last_jy = player->last_jy;
11021 int move_stepsize = TILEX / player->move_delay_value;
11023 #if USE_NEW_PLAYER_SPEED
11024 if (!player->active)
11027 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
11030 if (!player->active || player->MovPos == 0)
11034 if (mode == SCROLL_INIT)
11036 player->actual_frame_counter = FrameCounter;
11037 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
11039 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
11040 Feld[last_jx][last_jy] == EL_EMPTY)
11042 int last_field_block_delay = 0; /* start with no blocking at all */
11043 int block_delay_adjustment = player->block_delay_adjustment;
11045 /* if player blocks last field, add delay for exactly one move */
11046 if (player->block_last_field)
11048 last_field_block_delay += player->move_delay_value;
11050 /* when blocking enabled, prevent moving up despite gravity */
11051 #if USE_PLAYER_GRAVITY
11052 if (player->gravity && player->MovDir == MV_UP)
11053 block_delay_adjustment = -1;
11055 if (game.gravity && player->MovDir == MV_UP)
11056 block_delay_adjustment = -1;
11060 /* add block delay adjustment (also possible when not blocking) */
11061 last_field_block_delay += block_delay_adjustment;
11063 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
11064 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
11067 #if USE_NEW_PLAYER_SPEED
11068 if (player->MovPos != 0) /* player has not yet reached destination */
11074 else if (!FrameReached(&player->actual_frame_counter, 1))
11077 #if USE_NEW_PLAYER_SPEED
11078 if (player->MovPos != 0)
11080 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
11081 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
11083 /* before DrawPlayer() to draw correct player graphic for this case */
11084 if (player->MovPos == 0)
11085 CheckGravityMovement(player);
11088 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
11089 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
11091 /* before DrawPlayer() to draw correct player graphic for this case */
11092 if (player->MovPos == 0)
11093 CheckGravityMovement(player);
11096 if (player->MovPos == 0) /* player reached destination field */
11098 if (player->move_delay_reset_counter > 0)
11100 player->move_delay_reset_counter--;
11102 if (player->move_delay_reset_counter == 0)
11104 /* continue with normal speed after quickly moving through gate */
11105 HALVE_PLAYER_SPEED(player);
11107 /* be able to make the next move without delay */
11108 player->move_delay = 0;
11112 player->last_jx = jx;
11113 player->last_jy = jy;
11115 if (Feld[jx][jy] == EL_EXIT_OPEN ||
11116 Feld[jx][jy] == EL_EM_EXIT_OPEN ||
11117 Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
11118 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
11119 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
11120 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
11122 DrawPlayer(player); /* needed here only to cleanup last field */
11123 RemovePlayer(player);
11125 if (local_player->friends_still_needed == 0 ||
11126 IS_SP_ELEMENT(Feld[jx][jy]))
11127 PlayerWins(player);
11130 /* this breaks one level: "machine", level 000 */
11132 int move_direction = player->MovDir;
11133 int enter_side = MV_DIR_OPPOSITE(move_direction);
11134 int leave_side = move_direction;
11135 int old_jx = last_jx;
11136 int old_jy = last_jy;
11137 int old_element = Feld[old_jx][old_jy];
11138 int new_element = Feld[jx][jy];
11140 if (IS_CUSTOM_ELEMENT(old_element))
11141 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
11143 player->index_bit, leave_side);
11145 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
11146 CE_PLAYER_LEAVES_X,
11147 player->index_bit, leave_side);
11149 if (IS_CUSTOM_ELEMENT(new_element))
11150 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
11151 player->index_bit, enter_side);
11153 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
11154 CE_PLAYER_ENTERS_X,
11155 player->index_bit, enter_side);
11157 CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
11158 CE_MOVE_OF_X, move_direction);
11161 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
11163 TestIfPlayerTouchesBadThing(jx, jy);
11164 TestIfPlayerTouchesCustomElement(jx, jy);
11166 /* needed because pushed element has not yet reached its destination,
11167 so it would trigger a change event at its previous field location */
11168 if (!player->is_pushing)
11169 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
11171 if (!player->active)
11172 RemovePlayer(player);
11175 if (!local_player->LevelSolved && level.use_step_counter)
11185 if (TimeLeft <= 10 && setup.time_limit)
11186 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11188 DrawGameValue_Time(TimeLeft);
11190 if (!TimeLeft && setup.time_limit)
11191 for (i = 0; i < MAX_PLAYERS; i++)
11192 KillPlayer(&stored_player[i]);
11194 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
11195 DrawGameValue_Time(TimePlayed);
11198 if (tape.single_step && tape.recording && !tape.pausing &&
11199 !player->programmed_action)
11200 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
11204 void ScrollScreen(struct PlayerInfo *player, int mode)
11206 static unsigned long screen_frame_counter = 0;
11208 if (mode == SCROLL_INIT)
11210 /* set scrolling step size according to actual player's moving speed */
11211 ScrollStepSize = TILEX / player->move_delay_value;
11213 screen_frame_counter = FrameCounter;
11214 ScreenMovDir = player->MovDir;
11215 ScreenMovPos = player->MovPos;
11216 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
11219 else if (!FrameReached(&screen_frame_counter, 1))
11224 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
11225 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
11226 redraw_mask |= REDRAW_FIELD;
11229 ScreenMovDir = MV_NONE;
11232 void TestIfPlayerTouchesCustomElement(int x, int y)
11234 static int xy[4][2] =
11241 static int trigger_sides[4][2] =
11243 /* center side border side */
11244 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
11245 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
11246 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
11247 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
11249 static int touch_dir[4] =
11251 MV_LEFT | MV_RIGHT,
11256 int center_element = Feld[x][y]; /* should always be non-moving! */
11259 for (i = 0; i < NUM_DIRECTIONS; i++)
11261 int xx = x + xy[i][0];
11262 int yy = y + xy[i][1];
11263 int center_side = trigger_sides[i][0];
11264 int border_side = trigger_sides[i][1];
11265 int border_element;
11267 if (!IN_LEV_FIELD(xx, yy))
11270 if (IS_PLAYER(x, y))
11272 struct PlayerInfo *player = PLAYERINFO(x, y);
11274 if (game.engine_version < VERSION_IDENT(3,0,7,0))
11275 border_element = Feld[xx][yy]; /* may be moving! */
11276 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
11277 border_element = Feld[xx][yy];
11278 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
11279 border_element = MovingOrBlocked2Element(xx, yy);
11281 continue; /* center and border element do not touch */
11283 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
11284 player->index_bit, border_side);
11285 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
11286 CE_PLAYER_TOUCHES_X,
11287 player->index_bit, border_side);
11289 else if (IS_PLAYER(xx, yy))
11291 struct PlayerInfo *player = PLAYERINFO(xx, yy);
11293 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
11295 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
11296 continue; /* center and border element do not touch */
11299 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
11300 player->index_bit, center_side);
11301 CheckTriggeredElementChangeByPlayer(x, y, center_element,
11302 CE_PLAYER_TOUCHES_X,
11303 player->index_bit, center_side);
11309 #if USE_ELEMENT_TOUCHING_BUGFIX
11311 void TestIfElementTouchesCustomElement(int x, int y)
11313 static int xy[4][2] =
11320 static int trigger_sides[4][2] =
11322 /* center side border side */
11323 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
11324 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
11325 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
11326 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
11328 static int touch_dir[4] =
11330 MV_LEFT | MV_RIGHT,
11335 boolean change_center_element = FALSE;
11336 int center_element = Feld[x][y]; /* should always be non-moving! */
11337 int border_element_old[NUM_DIRECTIONS];
11340 for (i = 0; i < NUM_DIRECTIONS; i++)
11342 int xx = x + xy[i][0];
11343 int yy = y + xy[i][1];
11344 int border_element;
11346 border_element_old[i] = -1;
11348 if (!IN_LEV_FIELD(xx, yy))
11351 if (game.engine_version < VERSION_IDENT(3,0,7,0))
11352 border_element = Feld[xx][yy]; /* may be moving! */
11353 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
11354 border_element = Feld[xx][yy];
11355 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
11356 border_element = MovingOrBlocked2Element(xx, yy);
11358 continue; /* center and border element do not touch */
11360 border_element_old[i] = border_element;
11363 for (i = 0; i < NUM_DIRECTIONS; i++)
11365 int xx = x + xy[i][0];
11366 int yy = y + xy[i][1];
11367 int center_side = trigger_sides[i][0];
11368 int border_element = border_element_old[i];
11370 if (border_element == -1)
11373 /* check for change of border element */
11374 CheckElementChangeBySide(xx, yy, border_element, center_element,
11375 CE_TOUCHING_X, center_side);
11378 for (i = 0; i < NUM_DIRECTIONS; i++)
11380 int border_side = trigger_sides[i][1];
11381 int border_element = border_element_old[i];
11383 if (border_element == -1)
11386 /* check for change of center element (but change it only once) */
11387 if (!change_center_element)
11388 change_center_element =
11389 CheckElementChangeBySide(x, y, center_element, border_element,
11390 CE_TOUCHING_X, border_side);
11396 void TestIfElementTouchesCustomElement_OLD(int x, int y)
11398 static int xy[4][2] =
11405 static int trigger_sides[4][2] =
11407 /* center side border side */
11408 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
11409 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
11410 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
11411 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
11413 static int touch_dir[4] =
11415 MV_LEFT | MV_RIGHT,
11420 boolean change_center_element = FALSE;
11421 int center_element = Feld[x][y]; /* should always be non-moving! */
11424 for (i = 0; i < NUM_DIRECTIONS; i++)
11426 int xx = x + xy[i][0];
11427 int yy = y + xy[i][1];
11428 int center_side = trigger_sides[i][0];
11429 int border_side = trigger_sides[i][1];
11430 int border_element;
11432 if (!IN_LEV_FIELD(xx, yy))
11435 if (game.engine_version < VERSION_IDENT(3,0,7,0))
11436 border_element = Feld[xx][yy]; /* may be moving! */
11437 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
11438 border_element = Feld[xx][yy];
11439 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
11440 border_element = MovingOrBlocked2Element(xx, yy);
11442 continue; /* center and border element do not touch */
11444 /* check for change of center element (but change it only once) */
11445 if (!change_center_element)
11446 change_center_element =
11447 CheckElementChangeBySide(x, y, center_element, border_element,
11448 CE_TOUCHING_X, border_side);
11450 /* check for change of border element */
11451 CheckElementChangeBySide(xx, yy, border_element, center_element,
11452 CE_TOUCHING_X, center_side);
11458 void TestIfElementHitsCustomElement(int x, int y, int direction)
11460 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
11461 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
11462 int hitx = x + dx, hity = y + dy;
11463 int hitting_element = Feld[x][y];
11464 int touched_element;
11466 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
11469 touched_element = (IN_LEV_FIELD(hitx, hity) ?
11470 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
11472 if (IN_LEV_FIELD(hitx, hity))
11474 int opposite_direction = MV_DIR_OPPOSITE(direction);
11475 int hitting_side = direction;
11476 int touched_side = opposite_direction;
11477 boolean object_hit = (!IS_MOVING(hitx, hity) ||
11478 MovDir[hitx][hity] != direction ||
11479 ABS(MovPos[hitx][hity]) <= TILEY / 2);
11485 CheckElementChangeBySide(x, y, hitting_element, touched_element,
11486 CE_HITTING_X, touched_side);
11488 CheckElementChangeBySide(hitx, hity, touched_element,
11489 hitting_element, CE_HIT_BY_X, hitting_side);
11491 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
11492 CE_HIT_BY_SOMETHING, opposite_direction);
11496 /* "hitting something" is also true when hitting the playfield border */
11497 CheckElementChangeBySide(x, y, hitting_element, touched_element,
11498 CE_HITTING_SOMETHING, direction);
11502 void TestIfElementSmashesCustomElement(int x, int y, int direction)
11504 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
11505 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
11506 int hitx = x + dx, hity = y + dy;
11507 int hitting_element = Feld[x][y];
11508 int touched_element;
11510 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
11511 !IS_FREE(hitx, hity) &&
11512 (!IS_MOVING(hitx, hity) ||
11513 MovDir[hitx][hity] != direction ||
11514 ABS(MovPos[hitx][hity]) <= TILEY / 2));
11517 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
11521 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
11525 touched_element = (IN_LEV_FIELD(hitx, hity) ?
11526 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
11528 CheckElementChangeBySide(x, y, hitting_element, touched_element,
11529 EP_CAN_SMASH_EVERYTHING, direction);
11531 if (IN_LEV_FIELD(hitx, hity))
11533 int opposite_direction = MV_DIR_OPPOSITE(direction);
11534 int hitting_side = direction;
11535 int touched_side = opposite_direction;
11537 int touched_element = MovingOrBlocked2Element(hitx, hity);
11540 boolean object_hit = (!IS_MOVING(hitx, hity) ||
11541 MovDir[hitx][hity] != direction ||
11542 ABS(MovPos[hitx][hity]) <= TILEY / 2);
11551 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
11552 CE_SMASHED_BY_SOMETHING, opposite_direction);
11554 CheckElementChangeBySide(x, y, hitting_element, touched_element,
11555 CE_OTHER_IS_SMASHING, touched_side);
11557 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
11558 CE_OTHER_GETS_SMASHED, hitting_side);
11564 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
11566 int i, kill_x = -1, kill_y = -1;
11568 int bad_element = -1;
11569 static int test_xy[4][2] =
11576 static int test_dir[4] =
11584 for (i = 0; i < NUM_DIRECTIONS; i++)
11586 int test_x, test_y, test_move_dir, test_element;
11588 test_x = good_x + test_xy[i][0];
11589 test_y = good_y + test_xy[i][1];
11591 if (!IN_LEV_FIELD(test_x, test_y))
11595 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
11597 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
11599 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
11600 2nd case: DONT_TOUCH style bad thing does not move away from good thing
11602 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
11603 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
11607 bad_element = test_element;
11613 if (kill_x != -1 || kill_y != -1)
11615 if (IS_PLAYER(good_x, good_y))
11617 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
11619 if (player->shield_deadly_time_left > 0 &&
11620 !IS_INDESTRUCTIBLE(bad_element))
11621 Bang(kill_x, kill_y);
11622 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
11623 KillPlayer(player);
11626 Bang(good_x, good_y);
11630 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
11632 int i, kill_x = -1, kill_y = -1;
11633 int bad_element = Feld[bad_x][bad_y];
11634 static int test_xy[4][2] =
11641 static int touch_dir[4] =
11643 MV_LEFT | MV_RIGHT,
11648 static int test_dir[4] =
11656 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
11659 for (i = 0; i < NUM_DIRECTIONS; i++)
11661 int test_x, test_y, test_move_dir, test_element;
11663 test_x = bad_x + test_xy[i][0];
11664 test_y = bad_y + test_xy[i][1];
11665 if (!IN_LEV_FIELD(test_x, test_y))
11669 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
11671 test_element = Feld[test_x][test_y];
11673 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
11674 2nd case: DONT_TOUCH style bad thing does not move away from good thing
11676 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
11677 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
11679 /* good thing is player or penguin that does not move away */
11680 if (IS_PLAYER(test_x, test_y))
11682 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
11684 if (bad_element == EL_ROBOT && player->is_moving)
11685 continue; /* robot does not kill player if he is moving */
11687 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
11689 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
11690 continue; /* center and border element do not touch */
11697 else if (test_element == EL_PENGUIN)
11706 if (kill_x != -1 || kill_y != -1)
11708 if (IS_PLAYER(kill_x, kill_y))
11710 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
11712 if (player->shield_deadly_time_left > 0 &&
11713 !IS_INDESTRUCTIBLE(bad_element))
11714 Bang(bad_x, bad_y);
11715 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
11716 KillPlayer(player);
11719 Bang(kill_x, kill_y);
11723 void TestIfPlayerTouchesBadThing(int x, int y)
11725 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
11728 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
11730 TestIfGoodThingHitsBadThing(x, y, move_dir);
11733 void TestIfBadThingTouchesPlayer(int x, int y)
11735 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
11738 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
11740 TestIfBadThingHitsGoodThing(x, y, move_dir);
11743 void TestIfFriendTouchesBadThing(int x, int y)
11745 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
11748 void TestIfBadThingTouchesFriend(int x, int y)
11750 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
11753 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
11755 int i, kill_x = bad_x, kill_y = bad_y;
11756 static int xy[4][2] =
11764 for (i = 0; i < NUM_DIRECTIONS; i++)
11768 x = bad_x + xy[i][0];
11769 y = bad_y + xy[i][1];
11770 if (!IN_LEV_FIELD(x, y))
11773 element = Feld[x][y];
11774 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
11775 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
11783 if (kill_x != bad_x || kill_y != bad_y)
11784 Bang(bad_x, bad_y);
11787 void KillPlayer(struct PlayerInfo *player)
11789 int jx = player->jx, jy = player->jy;
11791 if (!player->active)
11794 /* the following code was introduced to prevent an infinite loop when calling
11796 -> CheckTriggeredElementChangeExt()
11797 -> ExecuteCustomElementAction()
11799 -> (infinitely repeating the above sequence of function calls)
11800 which occurs when killing the player while having a CE with the setting
11801 "kill player X when explosion of <player X>"; the solution using a new
11802 field "player->killed" was chosen for backwards compatibility, although
11803 clever use of the fields "player->active" etc. would probably also work */
11805 if (player->killed)
11809 player->killed = TRUE;
11811 /* remove accessible field at the player's position */
11812 Feld[jx][jy] = EL_EMPTY;
11814 /* deactivate shield (else Bang()/Explode() would not work right) */
11815 player->shield_normal_time_left = 0;
11816 player->shield_deadly_time_left = 0;
11819 BuryPlayer(player);
11822 static void KillPlayerUnlessEnemyProtected(int x, int y)
11824 if (!PLAYER_ENEMY_PROTECTED(x, y))
11825 KillPlayer(PLAYERINFO(x, y));
11828 static void KillPlayerUnlessExplosionProtected(int x, int y)
11830 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
11831 KillPlayer(PLAYERINFO(x, y));
11834 void BuryPlayer(struct PlayerInfo *player)
11836 int jx = player->jx, jy = player->jy;
11838 if (!player->active)
11841 PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
11842 PlayLevelSound(jx, jy, SND_GAME_LOSING);
11844 player->GameOver = TRUE;
11845 RemovePlayer(player);
11848 void RemovePlayer(struct PlayerInfo *player)
11850 int jx = player->jx, jy = player->jy;
11851 int i, found = FALSE;
11853 player->present = FALSE;
11854 player->active = FALSE;
11856 if (!ExplodeField[jx][jy])
11857 StorePlayer[jx][jy] = 0;
11859 if (player->is_moving)
11860 DrawLevelField(player->last_jx, player->last_jy);
11862 for (i = 0; i < MAX_PLAYERS; i++)
11863 if (stored_player[i].active)
11867 AllPlayersGone = TRUE;
11873 #if USE_NEW_SNAP_DELAY
11874 static void setFieldForSnapping(int x, int y, int element, int direction)
11876 struct ElementInfo *ei = &element_info[element];
11877 int direction_bit = MV_DIR_TO_BIT(direction);
11878 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
11879 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
11880 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
11882 Feld[x][y] = EL_ELEMENT_SNAPPING;
11883 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
11885 ResetGfxAnimation(x, y);
11887 GfxElement[x][y] = element;
11888 GfxAction[x][y] = action;
11889 GfxDir[x][y] = direction;
11890 GfxFrame[x][y] = -1;
11895 =============================================================================
11896 checkDiagonalPushing()
11897 -----------------------------------------------------------------------------
11898 check if diagonal input device direction results in pushing of object
11899 (by checking if the alternative direction is walkable, diggable, ...)
11900 =============================================================================
11903 static boolean checkDiagonalPushing(struct PlayerInfo *player,
11904 int x, int y, int real_dx, int real_dy)
11906 int jx, jy, dx, dy, xx, yy;
11908 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
11911 /* diagonal direction: check alternative direction */
11916 xx = jx + (dx == 0 ? real_dx : 0);
11917 yy = jy + (dy == 0 ? real_dy : 0);
11919 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
11923 =============================================================================
11925 -----------------------------------------------------------------------------
11926 x, y: field next to player (non-diagonal) to try to dig to
11927 real_dx, real_dy: direction as read from input device (can be diagonal)
11928 =============================================================================
11931 int DigField(struct PlayerInfo *player,
11932 int oldx, int oldy, int x, int y,
11933 int real_dx, int real_dy, int mode)
11935 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
11936 boolean player_was_pushing = player->is_pushing;
11937 boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
11938 boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
11939 int jx = oldx, jy = oldy;
11940 int dx = x - jx, dy = y - jy;
11941 int nextx = x + dx, nexty = y + dy;
11942 int move_direction = (dx == -1 ? MV_LEFT :
11943 dx == +1 ? MV_RIGHT :
11945 dy == +1 ? MV_DOWN : MV_NONE);
11946 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
11947 int dig_side = MV_DIR_OPPOSITE(move_direction);
11948 int old_element = Feld[jx][jy];
11949 #if USE_FIXED_DONT_RUN_INTO
11950 int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
11956 if (is_player) /* function can also be called by EL_PENGUIN */
11958 if (player->MovPos == 0)
11960 player->is_digging = FALSE;
11961 player->is_collecting = FALSE;
11964 if (player->MovPos == 0) /* last pushing move finished */
11965 player->is_pushing = FALSE;
11967 if (mode == DF_NO_PUSH) /* player just stopped pushing */
11969 player->is_switching = FALSE;
11970 player->push_delay = -1;
11972 return MP_NO_ACTION;
11976 #if !USE_FIXED_DONT_RUN_INTO
11977 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
11978 return MP_NO_ACTION;
11981 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
11982 old_element = Back[jx][jy];
11984 /* in case of element dropped at player position, check background */
11985 else if (Back[jx][jy] != EL_EMPTY &&
11986 game.engine_version >= VERSION_IDENT(2,2,0,0))
11987 old_element = Back[jx][jy];
11989 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
11990 return MP_NO_ACTION; /* field has no opening in this direction */
11992 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
11993 return MP_NO_ACTION; /* field has no opening in this direction */
11995 #if USE_FIXED_DONT_RUN_INTO
11996 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
12000 Feld[jx][jy] = player->artwork_element;
12001 InitMovingField(jx, jy, MV_DOWN);
12002 Store[jx][jy] = EL_ACID;
12003 ContinueMoving(jx, jy);
12004 BuryPlayer(player);
12006 return MP_DONT_RUN_INTO;
12010 #if USE_FIXED_DONT_RUN_INTO
12011 if (player_can_move && DONT_RUN_INTO(element))
12013 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
12015 return MP_DONT_RUN_INTO;
12019 #if USE_FIXED_DONT_RUN_INTO
12020 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
12021 return MP_NO_ACTION;
12024 #if !USE_FIXED_DONT_RUN_INTO
12025 element = Feld[x][y];
12028 collect_count = element_info[element].collect_count_initial;
12030 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
12031 return MP_NO_ACTION;
12033 if (game.engine_version < VERSION_IDENT(2,2,0,0))
12034 player_can_move = player_can_move_or_snap;
12036 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
12037 game.engine_version >= VERSION_IDENT(2,2,0,0))
12039 CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
12040 player->index_bit, dig_side);
12041 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
12042 player->index_bit, dig_side);
12044 if (element == EL_DC_LANDMINE)
12047 if (Feld[x][y] != element) /* field changed by snapping */
12050 return MP_NO_ACTION;
12053 #if USE_PLAYER_GRAVITY
12054 if (player->gravity && is_player && !player->is_auto_moving &&
12055 canFallDown(player) && move_direction != MV_DOWN &&
12056 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
12057 return MP_NO_ACTION; /* player cannot walk here due to gravity */
12059 if (game.gravity && is_player && !player->is_auto_moving &&
12060 canFallDown(player) && move_direction != MV_DOWN &&
12061 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
12062 return MP_NO_ACTION; /* player cannot walk here due to gravity */
12065 if (player_can_move &&
12066 IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
12068 int sound_element = SND_ELEMENT(element);
12069 int sound_action = ACTION_WALKING;
12071 if (IS_RND_GATE(element))
12073 if (!player->key[RND_GATE_NR(element)])
12074 return MP_NO_ACTION;
12076 else if (IS_RND_GATE_GRAY(element))
12078 if (!player->key[RND_GATE_GRAY_NR(element)])
12079 return MP_NO_ACTION;
12081 else if (IS_RND_GATE_GRAY_ACTIVE(element))
12083 if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
12084 return MP_NO_ACTION;
12086 else if (element == EL_EXIT_OPEN ||
12087 element == EL_EM_EXIT_OPEN ||
12088 element == EL_STEEL_EXIT_OPEN ||
12089 element == EL_EM_STEEL_EXIT_OPEN ||
12090 element == EL_SP_EXIT_OPEN ||
12091 element == EL_SP_EXIT_OPENING)
12093 sound_action = ACTION_PASSING; /* player is passing exit */
12095 else if (element == EL_EMPTY)
12097 sound_action = ACTION_MOVING; /* nothing to walk on */
12100 /* play sound from background or player, whatever is available */
12101 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
12102 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
12104 PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
12106 else if (player_can_move &&
12107 IS_PASSABLE(element) && canPassField(x, y, move_direction))
12109 if (!ACCESS_FROM(element, opposite_direction))
12110 return MP_NO_ACTION; /* field not accessible from this direction */
12112 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
12113 return MP_NO_ACTION;
12115 if (IS_EM_GATE(element))
12117 if (!player->key[EM_GATE_NR(element)])
12118 return MP_NO_ACTION;
12120 else if (IS_EM_GATE_GRAY(element))
12122 if (!player->key[EM_GATE_GRAY_NR(element)])
12123 return MP_NO_ACTION;
12125 else if (IS_EM_GATE_GRAY_ACTIVE(element))
12127 if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
12128 return MP_NO_ACTION;
12130 else if (IS_EMC_GATE(element))
12132 if (!player->key[EMC_GATE_NR(element)])
12133 return MP_NO_ACTION;
12135 else if (IS_EMC_GATE_GRAY(element))
12137 if (!player->key[EMC_GATE_GRAY_NR(element)])
12138 return MP_NO_ACTION;
12140 else if (IS_EMC_GATE_GRAY_ACTIVE(element))
12142 if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
12143 return MP_NO_ACTION;
12145 else if (element == EL_DC_GATE_WHITE ||
12146 element == EL_DC_GATE_WHITE_GRAY ||
12147 element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
12149 if (player->num_white_keys == 0)
12150 return MP_NO_ACTION;
12152 player->num_white_keys--;
12154 else if (IS_SP_PORT(element))
12156 if (element == EL_SP_GRAVITY_PORT_LEFT ||
12157 element == EL_SP_GRAVITY_PORT_RIGHT ||
12158 element == EL_SP_GRAVITY_PORT_UP ||
12159 element == EL_SP_GRAVITY_PORT_DOWN)
12160 #if USE_PLAYER_GRAVITY
12161 player->gravity = !player->gravity;
12163 game.gravity = !game.gravity;
12165 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
12166 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
12167 element == EL_SP_GRAVITY_ON_PORT_UP ||
12168 element == EL_SP_GRAVITY_ON_PORT_DOWN)
12169 #if USE_PLAYER_GRAVITY
12170 player->gravity = TRUE;
12172 game.gravity = TRUE;
12174 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
12175 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
12176 element == EL_SP_GRAVITY_OFF_PORT_UP ||
12177 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
12178 #if USE_PLAYER_GRAVITY
12179 player->gravity = FALSE;
12181 game.gravity = FALSE;
12185 /* automatically move to the next field with double speed */
12186 player->programmed_action = move_direction;
12188 if (player->move_delay_reset_counter == 0)
12190 player->move_delay_reset_counter = 2; /* two double speed steps */
12192 DOUBLE_PLAYER_SPEED(player);
12195 PlayLevelSoundAction(x, y, ACTION_PASSING);
12197 else if (player_can_move_or_snap && IS_DIGGABLE(element))
12201 if (mode != DF_SNAP)
12203 GfxElement[x][y] = GFX_ELEMENT(element);
12204 player->is_digging = TRUE;
12207 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
12209 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
12210 player->index_bit, dig_side);
12212 if (mode == DF_SNAP)
12214 #if USE_NEW_SNAP_DELAY
12215 if (level.block_snap_field)
12216 setFieldForSnapping(x, y, element, move_direction);
12218 TestIfElementTouchesCustomElement(x, y); /* for empty space */
12220 TestIfElementTouchesCustomElement(x, y); /* for empty space */
12223 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
12224 player->index_bit, dig_side);
12227 else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
12231 if (is_player && mode != DF_SNAP)
12233 GfxElement[x][y] = element;
12234 player->is_collecting = TRUE;
12237 if (element == EL_SPEED_PILL)
12239 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
12241 else if (element == EL_EXTRA_TIME && level.time > 0)
12243 TimeLeft += level.extra_time;
12244 DrawGameValue_Time(TimeLeft);
12246 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
12248 player->shield_normal_time_left += level.shield_normal_time;
12249 if (element == EL_SHIELD_DEADLY)
12250 player->shield_deadly_time_left += level.shield_deadly_time;
12252 else if (element == EL_DYNAMITE ||
12253 element == EL_EM_DYNAMITE ||
12254 element == EL_SP_DISK_RED)
12256 if (player->inventory_size < MAX_INVENTORY_SIZE)
12257 player->inventory_element[player->inventory_size++] = element;
12259 DrawGameDoorValues();
12261 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
12263 player->dynabomb_count++;
12264 player->dynabombs_left++;
12266 else if (element == EL_DYNABOMB_INCREASE_SIZE)
12268 player->dynabomb_size++;
12270 else if (element == EL_DYNABOMB_INCREASE_POWER)
12272 player->dynabomb_xl = TRUE;
12274 else if (IS_KEY(element))
12276 player->key[KEY_NR(element)] = TRUE;
12278 DrawGameDoorValues();
12280 else if (element == EL_DC_KEY_WHITE)
12282 player->num_white_keys++;
12284 /* display white keys? */
12285 /* DrawGameDoorValues(); */
12287 else if (IS_ENVELOPE(element))
12289 player->show_envelope = element;
12291 else if (element == EL_EMC_LENSES)
12293 game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
12295 RedrawAllInvisibleElementsForLenses();
12297 else if (element == EL_EMC_MAGNIFIER)
12299 game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
12301 RedrawAllInvisibleElementsForMagnifier();
12303 else if (IS_DROPPABLE(element) ||
12304 IS_THROWABLE(element)) /* can be collected and dropped */
12308 if (collect_count == 0)
12309 player->inventory_infinite_element = element;
12311 for (i = 0; i < collect_count; i++)
12312 if (player->inventory_size < MAX_INVENTORY_SIZE)
12313 player->inventory_element[player->inventory_size++] = element;
12315 DrawGameDoorValues();
12317 else if (collect_count > 0)
12319 local_player->gems_still_needed -= collect_count;
12320 if (local_player->gems_still_needed < 0)
12321 local_player->gems_still_needed = 0;
12323 DrawGameValue_Emeralds(local_player->gems_still_needed);
12326 RaiseScoreElement(element);
12327 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
12330 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
12331 player->index_bit, dig_side);
12333 if (mode == DF_SNAP)
12335 #if USE_NEW_SNAP_DELAY
12336 if (level.block_snap_field)
12337 setFieldForSnapping(x, y, element, move_direction);
12339 TestIfElementTouchesCustomElement(x, y); /* for empty space */
12341 TestIfElementTouchesCustomElement(x, y); /* for empty space */
12344 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
12345 player->index_bit, dig_side);
12348 else if (player_can_move_or_snap && IS_PUSHABLE(element))
12350 if (mode == DF_SNAP && element != EL_BD_ROCK)
12351 return MP_NO_ACTION;
12353 if (CAN_FALL(element) && dy)
12354 return MP_NO_ACTION;
12356 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
12357 !(element == EL_SPRING && level.use_spring_bug))
12358 return MP_NO_ACTION;
12360 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
12361 ((move_direction & MV_VERTICAL &&
12362 ((element_info[element].move_pattern & MV_LEFT &&
12363 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
12364 (element_info[element].move_pattern & MV_RIGHT &&
12365 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
12366 (move_direction & MV_HORIZONTAL &&
12367 ((element_info[element].move_pattern & MV_UP &&
12368 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
12369 (element_info[element].move_pattern & MV_DOWN &&
12370 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
12371 return MP_NO_ACTION;
12373 /* do not push elements already moving away faster than player */
12374 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
12375 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
12376 return MP_NO_ACTION;
12378 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
12380 if (player->push_delay_value == -1 || !player_was_pushing)
12381 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12383 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
12385 if (player->push_delay_value == -1)
12386 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12388 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
12390 if (!player->is_pushing)
12391 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12394 player->is_pushing = TRUE;
12395 player->is_active = TRUE;
12397 if (!(IN_LEV_FIELD(nextx, nexty) &&
12398 (IS_FREE(nextx, nexty) ||
12399 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
12400 IS_SB_ELEMENT(element)))))
12401 return MP_NO_ACTION;
12403 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
12404 return MP_NO_ACTION;
12406 if (player->push_delay == -1) /* new pushing; restart delay */
12407 player->push_delay = 0;
12409 if (player->push_delay < player->push_delay_value &&
12410 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
12411 element != EL_SPRING && element != EL_BALLOON)
12413 /* make sure that there is no move delay before next try to push */
12414 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
12415 player->move_delay = 0;
12417 return MP_NO_ACTION;
12420 if (IS_SB_ELEMENT(element))
12422 if (element == EL_SOKOBAN_FIELD_FULL)
12424 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
12425 local_player->sokobanfields_still_needed++;
12428 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
12430 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
12431 local_player->sokobanfields_still_needed--;
12434 Feld[x][y] = EL_SOKOBAN_OBJECT;
12436 if (Back[x][y] == Back[nextx][nexty])
12437 PlayLevelSoundAction(x, y, ACTION_PUSHING);
12438 else if (Back[x][y] != 0)
12439 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
12442 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
12445 if (local_player->sokobanfields_still_needed == 0 &&
12446 game.emulation == EMU_SOKOBAN)
12448 PlayerWins(player);
12450 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
12454 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12456 InitMovingField(x, y, move_direction);
12457 GfxAction[x][y] = ACTION_PUSHING;
12459 if (mode == DF_SNAP)
12460 ContinueMoving(x, y);
12462 MovPos[x][y] = (dx != 0 ? dx : dy);
12464 Pushed[x][y] = TRUE;
12465 Pushed[nextx][nexty] = TRUE;
12467 if (game.engine_version < VERSION_IDENT(2,2,0,7))
12468 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12470 player->push_delay_value = -1; /* get new value later */
12472 /* check for element change _after_ element has been pushed */
12473 if (game.use_change_when_pushing_bug)
12475 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
12476 player->index_bit, dig_side);
12477 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
12478 player->index_bit, dig_side);
12481 else if (IS_SWITCHABLE(element))
12483 if (PLAYER_SWITCHING(player, x, y))
12485 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
12486 player->index_bit, dig_side);
12491 player->is_switching = TRUE;
12492 player->switch_x = x;
12493 player->switch_y = y;
12495 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
12497 if (element == EL_ROBOT_WHEEL)
12499 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
12503 DrawLevelField(x, y);
12505 else if (element == EL_SP_TERMINAL)
12509 SCAN_PLAYFIELD(xx, yy)
12511 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
12513 else if (Feld[xx][yy] == EL_SP_TERMINAL)
12514 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
12517 else if (IS_BELT_SWITCH(element))
12519 ToggleBeltSwitch(x, y);
12521 else if (element == EL_SWITCHGATE_SWITCH_UP ||
12522 element == EL_SWITCHGATE_SWITCH_DOWN ||
12523 element == EL_DC_SWITCHGATE_SWITCH_UP ||
12524 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
12526 ToggleSwitchgateSwitch(x, y);
12528 else if (element == EL_LIGHT_SWITCH ||
12529 element == EL_LIGHT_SWITCH_ACTIVE)
12531 ToggleLightSwitch(x, y);
12533 else if (element == EL_TIMEGATE_SWITCH ||
12534 element == EL_DC_TIMEGATE_SWITCH)
12536 ActivateTimegateSwitch(x, y);
12538 else if (element == EL_BALLOON_SWITCH_LEFT ||
12539 element == EL_BALLOON_SWITCH_RIGHT ||
12540 element == EL_BALLOON_SWITCH_UP ||
12541 element == EL_BALLOON_SWITCH_DOWN ||
12542 element == EL_BALLOON_SWITCH_NONE ||
12543 element == EL_BALLOON_SWITCH_ANY)
12545 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
12546 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
12547 element == EL_BALLOON_SWITCH_UP ? MV_UP :
12548 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
12549 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
12552 else if (element == EL_LAMP)
12554 Feld[x][y] = EL_LAMP_ACTIVE;
12555 local_player->lights_still_needed--;
12557 ResetGfxAnimation(x, y);
12558 DrawLevelField(x, y);
12560 else if (element == EL_TIME_ORB_FULL)
12562 Feld[x][y] = EL_TIME_ORB_EMPTY;
12564 if (level.time > 0 || level.use_time_orb_bug)
12566 TimeLeft += level.time_orb_time;
12567 DrawGameValue_Time(TimeLeft);
12570 ResetGfxAnimation(x, y);
12571 DrawLevelField(x, y);
12573 else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
12574 element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
12578 game.ball_state = !game.ball_state;
12580 SCAN_PLAYFIELD(xx, yy)
12582 int e = Feld[xx][yy];
12584 if (game.ball_state)
12586 if (e == EL_EMC_MAGIC_BALL)
12587 CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
12588 else if (e == EL_EMC_MAGIC_BALL_SWITCH)
12589 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
12593 if (e == EL_EMC_MAGIC_BALL_ACTIVE)
12594 CreateField(xx, yy, EL_EMC_MAGIC_BALL);
12595 else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
12596 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
12601 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
12602 player->index_bit, dig_side);
12604 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
12605 player->index_bit, dig_side);
12607 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
12608 player->index_bit, dig_side);
12614 if (!PLAYER_SWITCHING(player, x, y))
12616 player->is_switching = TRUE;
12617 player->switch_x = x;
12618 player->switch_y = y;
12620 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
12621 player->index_bit, dig_side);
12622 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
12623 player->index_bit, dig_side);
12625 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
12626 player->index_bit, dig_side);
12627 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
12628 player->index_bit, dig_side);
12631 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
12632 player->index_bit, dig_side);
12633 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
12634 player->index_bit, dig_side);
12636 return MP_NO_ACTION;
12639 player->push_delay = -1;
12641 if (is_player) /* function can also be called by EL_PENGUIN */
12643 if (Feld[x][y] != element) /* really digged/collected something */
12645 player->is_collecting = !player->is_digging;
12646 player->is_active = TRUE;
12653 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
12655 int jx = player->jx, jy = player->jy;
12656 int x = jx + dx, y = jy + dy;
12657 int snap_direction = (dx == -1 ? MV_LEFT :
12658 dx == +1 ? MV_RIGHT :
12660 dy == +1 ? MV_DOWN : MV_NONE);
12661 boolean can_continue_snapping = (level.continuous_snapping &&
12662 WasJustFalling[x][y] < CHECK_DELAY_FALLING);
12664 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
12667 if (!player->active || !IN_LEV_FIELD(x, y))
12675 if (player->MovPos == 0)
12676 player->is_pushing = FALSE;
12678 player->is_snapping = FALSE;
12680 if (player->MovPos == 0)
12682 player->is_moving = FALSE;
12683 player->is_digging = FALSE;
12684 player->is_collecting = FALSE;
12690 #if USE_NEW_CONTINUOUS_SNAPPING
12691 /* prevent snapping with already pressed snap key when not allowed */
12692 if (player->is_snapping && !can_continue_snapping)
12695 if (player->is_snapping)
12699 player->MovDir = snap_direction;
12701 if (player->MovPos == 0)
12703 player->is_moving = FALSE;
12704 player->is_digging = FALSE;
12705 player->is_collecting = FALSE;
12708 player->is_dropping = FALSE;
12709 player->is_dropping_pressed = FALSE;
12710 player->drop_pressed_delay = 0;
12712 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
12715 player->is_snapping = TRUE;
12716 player->is_active = TRUE;
12718 if (player->MovPos == 0)
12720 player->is_moving = FALSE;
12721 player->is_digging = FALSE;
12722 player->is_collecting = FALSE;
12725 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
12726 DrawLevelField(player->last_jx, player->last_jy);
12728 DrawLevelField(x, y);
12733 boolean DropElement(struct PlayerInfo *player)
12735 int old_element, new_element;
12736 int dropx = player->jx, dropy = player->jy;
12737 int drop_direction = player->MovDir;
12738 int drop_side = drop_direction;
12739 int drop_element = (player->inventory_size > 0 ?
12740 player->inventory_element[player->inventory_size - 1] :
12741 player->inventory_infinite_element != EL_UNDEFINED ?
12742 player->inventory_infinite_element :
12743 player->dynabombs_left > 0 ?
12744 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
12747 player->is_dropping_pressed = TRUE;
12749 /* do not drop an element on top of another element; when holding drop key
12750 pressed without moving, dropped element must move away before the next
12751 element can be dropped (this is especially important if the next element
12752 is dynamite, which can be placed on background for historical reasons) */
12753 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
12756 if (IS_THROWABLE(drop_element))
12758 dropx += GET_DX_FROM_DIR(drop_direction);
12759 dropy += GET_DY_FROM_DIR(drop_direction);
12761 if (!IN_LEV_FIELD(dropx, dropy))
12765 old_element = Feld[dropx][dropy]; /* old element at dropping position */
12766 new_element = drop_element; /* default: no change when dropping */
12768 /* check if player is active, not moving and ready to drop */
12769 if (!player->active || player->MovPos || player->drop_delay > 0)
12772 /* check if player has anything that can be dropped */
12773 if (new_element == EL_UNDEFINED)
12776 /* check if drop key was pressed long enough for EM style dynamite */
12777 if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
12780 /* check if anything can be dropped at the current position */
12781 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
12784 /* collected custom elements can only be dropped on empty fields */
12785 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
12788 if (old_element != EL_EMPTY)
12789 Back[dropx][dropy] = old_element; /* store old element on this field */
12791 ResetGfxAnimation(dropx, dropy);
12792 ResetRandomAnimationValue(dropx, dropy);
12794 if (player->inventory_size > 0 ||
12795 player->inventory_infinite_element != EL_UNDEFINED)
12797 if (player->inventory_size > 0)
12799 player->inventory_size--;
12801 DrawGameDoorValues();
12803 if (new_element == EL_DYNAMITE)
12804 new_element = EL_DYNAMITE_ACTIVE;
12805 else if (new_element == EL_EM_DYNAMITE)
12806 new_element = EL_EM_DYNAMITE_ACTIVE;
12807 else if (new_element == EL_SP_DISK_RED)
12808 new_element = EL_SP_DISK_RED_ACTIVE;
12811 Feld[dropx][dropy] = new_element;
12813 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
12814 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
12815 el2img(Feld[dropx][dropy]), 0);
12817 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
12819 /* needed if previous element just changed to "empty" in the last frame */
12820 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
12822 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
12823 player->index_bit, drop_side);
12824 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
12826 player->index_bit, drop_side);
12828 TestIfElementTouchesCustomElement(dropx, dropy);
12830 else /* player is dropping a dyna bomb */
12832 player->dynabombs_left--;
12834 Feld[dropx][dropy] = new_element;
12836 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
12837 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
12838 el2img(Feld[dropx][dropy]), 0);
12840 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
12843 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
12844 InitField_WithBug1(dropx, dropy, FALSE);
12846 new_element = Feld[dropx][dropy]; /* element might have changed */
12848 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
12849 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
12851 int move_direction, nextx, nexty;
12853 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
12854 MovDir[dropx][dropy] = drop_direction;
12856 move_direction = MovDir[dropx][dropy];
12857 nextx = dropx + GET_DX_FROM_DIR(move_direction);
12858 nexty = dropy + GET_DY_FROM_DIR(move_direction);
12860 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
12862 #if USE_FIX_IMPACT_COLLISION
12863 /* do not cause impact style collision by dropping elements that can fall */
12864 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
12866 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
12870 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
12871 player->is_dropping = TRUE;
12873 player->drop_pressed_delay = 0;
12874 player->is_dropping_pressed = FALSE;
12876 player->drop_x = dropx;
12877 player->drop_y = dropy;
12882 /* ------------------------------------------------------------------------- */
12883 /* game sound playing functions */
12884 /* ------------------------------------------------------------------------- */
12886 static int *loop_sound_frame = NULL;
12887 static int *loop_sound_volume = NULL;
12889 void InitPlayLevelSound()
12891 int num_sounds = getSoundListSize();
12893 checked_free(loop_sound_frame);
12894 checked_free(loop_sound_volume);
12896 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
12897 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
12900 static void PlayLevelSound(int x, int y, int nr)
12902 int sx = SCREENX(x), sy = SCREENY(y);
12903 int volume, stereo_position;
12904 int max_distance = 8;
12905 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
12907 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
12908 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
12911 if (!IN_LEV_FIELD(x, y) ||
12912 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
12913 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
12916 volume = SOUND_MAX_VOLUME;
12918 if (!IN_SCR_FIELD(sx, sy))
12920 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
12921 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
12923 volume -= volume * (dx > dy ? dx : dy) / max_distance;
12926 stereo_position = (SOUND_MAX_LEFT +
12927 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
12928 (SCR_FIELDX + 2 * max_distance));
12930 if (IS_LOOP_SOUND(nr))
12932 /* This assures that quieter loop sounds do not overwrite louder ones,
12933 while restarting sound volume comparison with each new game frame. */
12935 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
12938 loop_sound_volume[nr] = volume;
12939 loop_sound_frame[nr] = FrameCounter;
12942 PlaySoundExt(nr, volume, stereo_position, type);
12945 static void PlayLevelSoundNearest(int x, int y, int sound_action)
12947 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
12948 x > LEVELX(BX2) ? LEVELX(BX2) : x,
12949 y < LEVELY(BY1) ? LEVELY(BY1) :
12950 y > LEVELY(BY2) ? LEVELY(BY2) : y,
12954 static void PlayLevelSoundAction(int x, int y, int action)
12956 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
12959 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
12961 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
12963 if (sound_effect != SND_UNDEFINED)
12964 PlayLevelSound(x, y, sound_effect);
12967 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
12970 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
12972 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12973 PlayLevelSound(x, y, sound_effect);
12976 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
12978 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
12980 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12981 PlayLevelSound(x, y, sound_effect);
12984 static void StopLevelSoundActionIfLoop(int x, int y, int action)
12986 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
12988 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12989 StopSound(sound_effect);
12992 static void PlayLevelMusic()
12994 if (levelset.music[level_nr] != MUS_UNDEFINED)
12995 PlayMusic(levelset.music[level_nr]); /* from config file */
12997 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
13000 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
13002 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
13003 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
13004 int x = xx - 1 - offset;
13005 int y = yy - 1 - offset;
13010 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
13014 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13018 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
13022 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
13026 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
13030 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
13034 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
13037 case SAMPLE_android_clone:
13038 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
13041 case SAMPLE_android_move:
13042 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
13045 case SAMPLE_spring:
13046 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
13050 PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
13054 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
13057 case SAMPLE_eater_eat:
13058 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13062 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
13065 case SAMPLE_collect:
13066 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13069 case SAMPLE_diamond:
13070 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
13073 case SAMPLE_squash:
13074 /* !!! CHECK THIS !!! */
13076 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
13078 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
13082 case SAMPLE_wonderfall:
13083 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
13087 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
13091 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13095 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13099 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
13103 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
13107 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
13110 case SAMPLE_wonder:
13111 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
13115 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
13118 case SAMPLE_exit_open:
13119 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
13122 case SAMPLE_exit_leave:
13123 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
13126 case SAMPLE_dynamite:
13127 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
13131 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
13135 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
13139 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
13143 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
13147 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
13151 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
13155 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
13161 void ChangeTime(int value)
13163 int *time = (level.time == 0 ? &TimePlayed : &TimeLeft);
13167 /* EMC game engine uses value from time counter of RND game engine */
13168 level.native_em_level->lev->time = *time;
13170 DrawGameValue_Time(*time);
13173 void RaiseScore(int value)
13175 /* EMC game engine and RND game engine have separate score counters */
13176 int *score = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
13177 &level.native_em_level->lev->score : &local_player->score);
13181 DrawGameValue_Score(*score);
13185 void RaiseScore(int value)
13187 local_player->score += value;
13189 DrawGameValue_Score(local_player->score);
13192 void RaiseScoreElement(int element)
13197 case EL_BD_DIAMOND:
13198 case EL_EMERALD_YELLOW:
13199 case EL_EMERALD_RED:
13200 case EL_EMERALD_PURPLE:
13201 case EL_SP_INFOTRON:
13202 RaiseScore(level.score[SC_EMERALD]);
13205 RaiseScore(level.score[SC_DIAMOND]);
13208 RaiseScore(level.score[SC_CRYSTAL]);
13211 RaiseScore(level.score[SC_PEARL]);
13214 case EL_BD_BUTTERFLY:
13215 case EL_SP_ELECTRON:
13216 RaiseScore(level.score[SC_BUG]);
13219 case EL_BD_FIREFLY:
13220 case EL_SP_SNIKSNAK:
13221 RaiseScore(level.score[SC_SPACESHIP]);
13224 case EL_DARK_YAMYAM:
13225 RaiseScore(level.score[SC_YAMYAM]);
13228 RaiseScore(level.score[SC_ROBOT]);
13231 RaiseScore(level.score[SC_PACMAN]);
13234 RaiseScore(level.score[SC_NUT]);
13237 case EL_EM_DYNAMITE:
13238 case EL_SP_DISK_RED:
13239 case EL_DYNABOMB_INCREASE_NUMBER:
13240 case EL_DYNABOMB_INCREASE_SIZE:
13241 case EL_DYNABOMB_INCREASE_POWER:
13242 RaiseScore(level.score[SC_DYNAMITE]);
13244 case EL_SHIELD_NORMAL:
13245 case EL_SHIELD_DEADLY:
13246 RaiseScore(level.score[SC_SHIELD]);
13248 case EL_EXTRA_TIME:
13249 RaiseScore(level.extra_time_score);
13263 case EL_DC_KEY_WHITE:
13264 RaiseScore(level.score[SC_KEY]);
13267 RaiseScore(element_info[element].collect_score);
13272 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
13274 if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
13276 #if defined(NETWORK_AVALIABLE)
13277 if (options.network)
13278 SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
13284 game_status = GAME_MODE_MAIN;
13290 FadeOut(REDRAW_FIELD);
13292 game_status = GAME_MODE_MAIN;
13294 DrawAndFadeInMainMenu(REDRAW_FIELD);
13298 else /* continue playing the game */
13300 if (tape.playing && tape.deactivate_display)
13301 TapeDeactivateDisplayOff(TRUE);
13303 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
13305 if (tape.playing && tape.deactivate_display)
13306 TapeDeactivateDisplayOn();
13310 void RequestQuitGame(boolean ask_if_really_quit)
13312 boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
13313 boolean skip_request = AllPlayersGone || quick_quit;
13315 RequestQuitGameExt(skip_request, quick_quit,
13316 "Do you really want to quit the game ?");
13320 /* ------------------------------------------------------------------------- */
13321 /* random generator functions */
13322 /* ------------------------------------------------------------------------- */
13324 unsigned int InitEngineRandom_RND(long seed)
13326 game.num_random_calls = 0;
13329 unsigned int rnd_seed = InitEngineRandom(seed);
13331 printf("::: START RND: %d\n", rnd_seed);
13336 return InitEngineRandom(seed);
13342 unsigned int RND(int max)
13346 game.num_random_calls++;
13348 return GetEngineRandom(max);
13355 /* ------------------------------------------------------------------------- */
13356 /* game engine snapshot handling functions */
13357 /* ------------------------------------------------------------------------- */
13359 #define ARGS_ADDRESS_AND_SIZEOF(x) (&(x)), (sizeof(x))
13361 struct EngineSnapshotInfo
13363 /* runtime values for custom element collect score */
13364 int collect_score[NUM_CUSTOM_ELEMENTS];
13366 /* runtime values for group element choice position */
13367 int choice_pos[NUM_GROUP_ELEMENTS];
13369 /* runtime values for belt position animations */
13370 int belt_graphic[4 * NUM_BELT_PARTS];
13371 int belt_anim_mode[4 * NUM_BELT_PARTS];
13374 struct EngineSnapshotNodeInfo
13381 static struct EngineSnapshotInfo engine_snapshot_rnd;
13382 static ListNode *engine_snapshot_list = NULL;
13383 static char *snapshot_level_identifier = NULL;
13384 static int snapshot_level_nr = -1;
13386 void FreeEngineSnapshot()
13388 while (engine_snapshot_list != NULL)
13389 deleteNodeFromList(&engine_snapshot_list, engine_snapshot_list->key,
13392 setString(&snapshot_level_identifier, NULL);
13393 snapshot_level_nr = -1;
13396 static void SaveEngineSnapshotValues_RND()
13398 static int belt_base_active_element[4] =
13400 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
13401 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
13402 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
13403 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
13407 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
13409 int element = EL_CUSTOM_START + i;
13411 engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
13414 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
13416 int element = EL_GROUP_START + i;
13418 engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
13421 for (i = 0; i < 4; i++)
13423 for (j = 0; j < NUM_BELT_PARTS; j++)
13425 int element = belt_base_active_element[i] + j;
13426 int graphic = el2img(element);
13427 int anim_mode = graphic_info[graphic].anim_mode;
13429 engine_snapshot_rnd.belt_graphic[i * 4 + j] = graphic;
13430 engine_snapshot_rnd.belt_anim_mode[i * 4 + j] = anim_mode;
13435 static void LoadEngineSnapshotValues_RND()
13437 unsigned long num_random_calls = game.num_random_calls;
13440 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
13442 int element = EL_CUSTOM_START + i;
13444 element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
13447 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
13449 int element = EL_GROUP_START + i;
13451 element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
13454 for (i = 0; i < 4; i++)
13456 for (j = 0; j < NUM_BELT_PARTS; j++)
13458 int graphic = engine_snapshot_rnd.belt_graphic[i * 4 + j];
13459 int anim_mode = engine_snapshot_rnd.belt_anim_mode[i * 4 + j];
13461 graphic_info[graphic].anim_mode = anim_mode;
13465 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
13467 InitRND(tape.random_seed);
13468 for (i = 0; i < num_random_calls; i++)
13472 if (game.num_random_calls != num_random_calls)
13474 Error(ERR_RETURN, "number of random calls out of sync");
13475 Error(ERR_RETURN, "number of random calls should be %d", num_random_calls);
13476 Error(ERR_RETURN, "number of random calls is %d", game.num_random_calls);
13477 Error(ERR_EXIT, "this should not happen -- please debug");
13481 static void SaveEngineSnapshotBuffer(void *buffer, int size)
13483 struct EngineSnapshotNodeInfo *bi =
13484 checked_calloc(sizeof(struct EngineSnapshotNodeInfo));
13486 bi->buffer_orig = buffer;
13487 bi->buffer_copy = checked_malloc(size);
13490 memcpy(bi->buffer_copy, buffer, size);
13492 addNodeToList(&engine_snapshot_list, NULL, bi);
13495 void SaveEngineSnapshot()
13497 FreeEngineSnapshot(); /* free previous snapshot, if needed */
13499 if (level_editor_test_game) /* do not save snapshots from editor */
13502 /* copy some special values to a structure better suited for the snapshot */
13504 SaveEngineSnapshotValues_RND();
13505 SaveEngineSnapshotValues_EM();
13507 /* save values stored in special snapshot structure */
13509 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
13510 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
13512 /* save further RND engine values */
13514 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
13515 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
13516 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
13518 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
13519 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
13520 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
13521 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
13523 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
13524 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
13525 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
13526 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
13527 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
13529 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
13530 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
13531 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
13533 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
13535 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
13537 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
13538 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
13540 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
13541 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
13542 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
13543 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
13544 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
13545 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
13546 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
13547 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
13548 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
13549 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
13550 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
13551 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
13552 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
13553 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
13554 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
13555 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
13556 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
13557 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
13559 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
13560 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
13562 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
13563 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
13564 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
13566 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
13567 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
13569 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
13570 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
13571 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
13572 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
13573 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
13575 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
13576 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
13578 /* save level identification information */
13580 setString(&snapshot_level_identifier, leveldir_current->identifier);
13581 snapshot_level_nr = level_nr;
13584 ListNode *node = engine_snapshot_list;
13587 while (node != NULL)
13589 num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
13594 printf("::: size of engine snapshot: %d bytes\n", num_bytes);
13598 static void LoadEngineSnapshotBuffer(struct EngineSnapshotNodeInfo *bi)
13600 memcpy(bi->buffer_orig, bi->buffer_copy, bi->size);
13603 void LoadEngineSnapshot()
13605 ListNode *node = engine_snapshot_list;
13607 if (engine_snapshot_list == NULL)
13610 while (node != NULL)
13612 LoadEngineSnapshotBuffer((struct EngineSnapshotNodeInfo *)node->content);
13617 /* restore special values from snapshot structure */
13619 LoadEngineSnapshotValues_RND();
13620 LoadEngineSnapshotValues_EM();
13623 boolean CheckEngineSnapshot()
13625 return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
13626 snapshot_level_nr == level_nr);
13630 /* ---------- new game button stuff ---------------------------------------- */
13632 /* graphic position values for game buttons */
13633 #define GAME_BUTTON_XSIZE 30
13634 #define GAME_BUTTON_YSIZE 30
13635 #define GAME_BUTTON_XPOS 5
13636 #define GAME_BUTTON_YPOS 215
13637 #define SOUND_BUTTON_XPOS 5
13638 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
13640 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
13641 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
13642 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
13643 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
13644 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
13645 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
13652 } gamebutton_info[NUM_GAME_BUTTONS] =
13655 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
13660 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
13661 GAME_CTRL_ID_PAUSE,
13665 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
13670 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
13671 SOUND_CTRL_ID_MUSIC,
13672 "background music on/off"
13675 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
13676 SOUND_CTRL_ID_LOOPS,
13677 "sound loops on/off"
13680 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
13681 SOUND_CTRL_ID_SIMPLE,
13682 "normal sounds on/off"
13686 void CreateGameButtons()
13690 for (i = 0; i < NUM_GAME_BUTTONS; i++)
13692 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
13693 struct GadgetInfo *gi;
13696 unsigned long event_mask;
13697 int gd_xoffset, gd_yoffset;
13698 int gd_x1, gd_x2, gd_y1, gd_y2;
13701 gd_xoffset = gamebutton_info[i].x;
13702 gd_yoffset = gamebutton_info[i].y;
13703 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
13704 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
13706 if (id == GAME_CTRL_ID_STOP ||
13707 id == GAME_CTRL_ID_PAUSE ||
13708 id == GAME_CTRL_ID_PLAY)
13710 button_type = GD_TYPE_NORMAL_BUTTON;
13712 event_mask = GD_EVENT_RELEASED;
13713 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
13714 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
13718 button_type = GD_TYPE_CHECK_BUTTON;
13720 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
13721 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
13722 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
13723 event_mask = GD_EVENT_PRESSED;
13724 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
13725 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
13728 gi = CreateGadget(GDI_CUSTOM_ID, id,
13729 GDI_INFO_TEXT, gamebutton_info[i].infotext,
13730 GDI_X, DX + gd_xoffset,
13731 GDI_Y, DY + gd_yoffset,
13732 GDI_WIDTH, GAME_BUTTON_XSIZE,
13733 GDI_HEIGHT, GAME_BUTTON_YSIZE,
13734 GDI_TYPE, button_type,
13735 GDI_STATE, GD_BUTTON_UNPRESSED,
13736 GDI_CHECKED, checked,
13737 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
13738 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
13739 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
13740 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
13741 GDI_EVENT_MASK, event_mask,
13742 GDI_CALLBACK_ACTION, HandleGameButtons,
13746 Error(ERR_EXIT, "cannot create gadget");
13748 game_gadget[id] = gi;
13752 void FreeGameButtons()
13756 for (i = 0; i < NUM_GAME_BUTTONS; i++)
13757 FreeGadget(game_gadget[i]);
13760 static void MapGameButtons()
13764 for (i = 0; i < NUM_GAME_BUTTONS; i++)
13765 MapGadget(game_gadget[i]);
13768 void UnmapGameButtons()
13772 for (i = 0; i < NUM_GAME_BUTTONS; i++)
13773 UnmapGadget(game_gadget[i]);
13776 static void HandleGameButtons(struct GadgetInfo *gi)
13778 int id = gi->custom_id;
13780 if (game_status != GAME_MODE_PLAYING)
13785 case GAME_CTRL_ID_STOP:
13789 RequestQuitGame(TRUE);
13792 case GAME_CTRL_ID_PAUSE:
13793 if (options.network)
13795 #if defined(NETWORK_AVALIABLE)
13797 SendToServer_ContinuePlaying();
13799 SendToServer_PausePlaying();
13803 TapeTogglePause(TAPE_TOGGLE_MANUAL);
13806 case GAME_CTRL_ID_PLAY:
13809 #if defined(NETWORK_AVALIABLE)
13810 if (options.network)
13811 SendToServer_ContinuePlaying();
13815 tape.pausing = FALSE;
13816 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
13821 case SOUND_CTRL_ID_MUSIC:
13822 if (setup.sound_music)
13824 setup.sound_music = FALSE;
13827 else if (audio.music_available)
13829 setup.sound = setup.sound_music = TRUE;
13831 SetAudioMode(setup.sound);
13837 case SOUND_CTRL_ID_LOOPS:
13838 if (setup.sound_loops)
13839 setup.sound_loops = FALSE;
13840 else if (audio.loops_available)
13842 setup.sound = setup.sound_loops = TRUE;
13843 SetAudioMode(setup.sound);
13847 case SOUND_CTRL_ID_SIMPLE:
13848 if (setup.sound_simple)
13849 setup.sound_simple = FALSE;
13850 else if (audio.sound_available)
13852 setup.sound = setup.sound_simple = TRUE;
13853 SetAudioMode(setup.sound);