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)
62 #define USE_GFX_RESET_WHEN_NOT_MOVING (USE_NEW_STUFF * 1)
70 /* for MovePlayer() */
71 #define MP_NO_ACTION 0
74 #define MP_DONT_RUN_INTO (MP_MOVING | MP_ACTION)
76 /* for ScrollPlayer() */
78 #define SCROLL_GO_ON 1
80 /* for Bang()/Explode() */
81 #define EX_PHASE_START 0
82 #define EX_TYPE_NONE 0
83 #define EX_TYPE_NORMAL (1 << 0)
84 #define EX_TYPE_CENTER (1 << 1)
85 #define EX_TYPE_BORDER (1 << 2)
86 #define EX_TYPE_CROSS (1 << 3)
87 #define EX_TYPE_DYNA (1 << 4)
88 #define EX_TYPE_SINGLE_TILE (EX_TYPE_CENTER | EX_TYPE_BORDER)
91 #define PANEL_OFF() (local_player->LevelSolved_PanelOff)
92 #define PANEL_DEACTIVATED(p) ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
93 #define PANEL_XPOS(p) (DX + ALIGNED_MENU_XPOS(p))
94 #define PANEL_YPOS(p) (DY + ALIGNED_MENU_YPOS(p))
96 #define PANEL_DEACTIVATED(p) ((p).x < 0 || (p).y < 0)
97 #define PANEL_XPOS(p) (ALIGNED_XPOS((p).x, (p).width, (p).align))
98 #define PANEL_YPOS(p) ((p).y)
101 /* special positions in the game control window (relative to control window) */
102 #define XX_LEVEL1 (PANEL_XPOS(game.panel.level))
103 #define XX_LEVEL2 (PANEL_XPOS(game.panel.level) - 1)
104 #define XX_LEVEL (PANEL_XPOS(game.panel.level))
105 #define YY_LEVEL (PANEL_YPOS(game.panel.level))
106 #define XX_EMERALDS (PANEL_XPOS(game.panel.gems))
107 #define YY_EMERALDS (PANEL_YPOS(game.panel.gems))
108 #define XX_DYNAMITE (PANEL_XPOS(game.panel.inventory))
109 #define YY_DYNAMITE (PANEL_YPOS(game.panel.inventory))
110 #define XX_KEYS (PANEL_XPOS(game.panel.keys))
111 #define YY_KEYS (PANEL_YPOS(game.panel.keys))
112 #define XX_SCORE (PANEL_XPOS(game.panel.score))
113 #define YY_SCORE (PANEL_YPOS(game.panel.score))
114 #define XX_TIME1 (PANEL_XPOS(game.panel.time))
115 #define XX_TIME2 (PANEL_XPOS(game.panel.time) + 1)
116 #define XX_TIME (PANEL_XPOS(game.panel.time))
117 #define YY_TIME (PANEL_YPOS(game.panel.time))
119 /* special positions in the game control window (relative to main window) */
120 #define DX_LEVEL1 (DX + XX_LEVEL1)
121 #define DX_LEVEL2 (DX + XX_LEVEL2)
122 #define DX_LEVEL (DX + XX_LEVEL)
123 #define DY_LEVEL (DY + YY_LEVEL)
124 #define DX_EMERALDS (DX + XX_EMERALDS)
125 #define DY_EMERALDS (DY + YY_EMERALDS)
126 #define DX_DYNAMITE (DX + XX_DYNAMITE)
127 #define DY_DYNAMITE (DY + YY_DYNAMITE)
128 #define DX_KEYS (DX + XX_KEYS)
129 #define DY_KEYS (DY + YY_KEYS)
130 #define DX_SCORE (DX + XX_SCORE)
131 #define DY_SCORE (DY + YY_SCORE)
132 #define DX_TIME1 (DX + XX_TIME1)
133 #define DX_TIME2 (DX + XX_TIME2)
134 #define DX_TIME (DX + XX_TIME)
135 #define DY_TIME (DY + YY_TIME)
137 /* values for delayed check of falling and moving elements and for collision */
138 #define CHECK_DELAY_MOVING 3
139 #define CHECK_DELAY_FALLING CHECK_DELAY_MOVING
140 #define CHECK_DELAY_COLLISION 2
141 #define CHECK_DELAY_IMPACT CHECK_DELAY_COLLISION
143 /* values for initial player move delay (initial delay counter value) */
144 #define INITIAL_MOVE_DELAY_OFF -1
145 #define INITIAL_MOVE_DELAY_ON 0
147 /* values for player movement speed (which is in fact a delay value) */
148 #define MOVE_DELAY_MIN_SPEED 32
149 #define MOVE_DELAY_NORMAL_SPEED 8
150 #define MOVE_DELAY_HIGH_SPEED 4
151 #define MOVE_DELAY_MAX_SPEED 1
153 #define DOUBLE_MOVE_DELAY(x) (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
154 #define HALVE_MOVE_DELAY(x) (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
156 #define DOUBLE_PLAYER_SPEED(p) (HALVE_MOVE_DELAY( (p)->move_delay_value))
157 #define HALVE_PLAYER_SPEED(p) (DOUBLE_MOVE_DELAY((p)->move_delay_value))
159 /* values for other actions */
160 #define MOVE_STEPSIZE_NORMAL (TILEX / MOVE_DELAY_NORMAL_SPEED)
161 #define MOVE_STEPSIZE_MIN (1)
162 #define MOVE_STEPSIZE_MAX (TILEX)
164 #define GET_DX_FROM_DIR(d) ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
165 #define GET_DY_FROM_DIR(d) ((d) == MV_UP ? -1 : (d) == MV_DOWN ? 1 : 0)
167 #define INIT_GFX_RANDOM() (GetSimpleRandom(1000000))
169 #define GET_NEW_PUSH_DELAY(e) ( (element_info[e].push_delay_fixed) + \
170 RND(element_info[e].push_delay_random))
171 #define GET_NEW_DROP_DELAY(e) ( (element_info[e].drop_delay_fixed) + \
172 RND(element_info[e].drop_delay_random))
173 #define GET_NEW_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
174 RND(element_info[e].move_delay_random))
175 #define GET_MAX_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
176 (element_info[e].move_delay_random))
177 #define GET_NEW_CE_VALUE(e) ( (element_info[e].ce_value_fixed_initial) +\
178 RND(element_info[e].ce_value_random_initial))
179 #define GET_CE_SCORE(e) ( (element_info[e].collect_score))
180 #define GET_CHANGE_DELAY(c) ( ((c)->delay_fixed * (c)->delay_frames) + \
181 RND((c)->delay_random * (c)->delay_frames))
182 #define GET_CE_DELAY_VALUE(c) ( ((c)->delay_fixed) + \
183 RND((c)->delay_random))
186 #define GET_VALID_RUNTIME_ELEMENT(e) \
187 ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
189 #define RESOLVED_REFERENCE_ELEMENT(be, e) \
190 ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START : \
191 (be) + (e) - EL_SELF > EL_CUSTOM_END ? EL_CUSTOM_END : \
192 (be) + (e) - EL_SELF)
194 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs) \
195 ((e) == EL_TRIGGER_PLAYER ? (ch)->actual_trigger_player : \
196 (e) == EL_TRIGGER_ELEMENT ? (ch)->actual_trigger_element : \
197 (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value : \
198 (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score : \
199 (e) == EL_CURRENT_CE_VALUE ? (cv) : \
200 (e) == EL_CURRENT_CE_SCORE ? (cs) : \
201 (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ? \
202 RESOLVED_REFERENCE_ELEMENT(be, e) : \
205 #define CAN_GROW_INTO(e) \
206 ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
208 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition) \
209 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
212 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition) \
213 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
214 (CAN_MOVE_INTO_ACID(e) && \
215 Feld[x][y] == EL_ACID) || \
218 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition) \
219 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
220 (CAN_MOVE_INTO_ACID(e) && \
221 Feld[x][y] == EL_ACID) || \
224 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition) \
225 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
227 (CAN_MOVE_INTO_ACID(e) && \
228 Feld[x][y] == EL_ACID) || \
229 (DONT_COLLIDE_WITH(e) && \
231 !PLAYER_ENEMY_PROTECTED(x, y))))
233 #define ELEMENT_CAN_ENTER_FIELD(e, x, y) \
234 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
236 #define SATELLITE_CAN_ENTER_FIELD(x, y) \
237 ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
239 #define ANDROID_CAN_ENTER_FIELD(e, x, y) \
240 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
242 #define ANDROID_CAN_CLONE_FIELD(x, y) \
243 (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
244 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
246 #define ENEMY_CAN_ENTER_FIELD(e, x, y) \
247 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
249 #define YAMYAM_CAN_ENTER_FIELD(e, x, y) \
250 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
252 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y) \
253 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
255 #define PACMAN_CAN_ENTER_FIELD(e, x, y) \
256 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
258 #define PIG_CAN_ENTER_FIELD(e, x, y) \
259 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
261 #define PENGUIN_CAN_ENTER_FIELD(e, x, y) \
262 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
263 Feld[x][y] == EL_EM_EXIT_OPEN || \
264 Feld[x][y] == EL_STEEL_EXIT_OPEN || \
265 Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
266 IS_FOOD_PENGUIN(Feld[x][y])))
267 #define DRAGON_CAN_ENTER_FIELD(e, x, y) \
268 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
270 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition) \
271 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
273 #define SPRING_CAN_ENTER_FIELD(e, x, y) \
274 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
276 #define SPRING_CAN_BUMP_FROM_FIELD(x, y) \
277 (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER || \
278 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
280 #define MOVE_ENTER_EL(e) (element_info[e].move_enter_element)
282 #define CE_ENTER_FIELD_COND(e, x, y) \
283 (!IS_PLAYER(x, y) && \
284 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
286 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y) \
287 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
289 #define IN_LEV_FIELD_AND_IS_FREE(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
290 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
292 #define ACCESS_FROM(e, d) (element_info[e].access_direction &(d))
293 #define IS_WALKABLE_FROM(e, d) (IS_WALKABLE(e) && ACCESS_FROM(e, d))
294 #define IS_PASSABLE_FROM(e, d) (IS_PASSABLE(e) && ACCESS_FROM(e, d))
295 #define IS_ACCESSIBLE_FROM(e, d) (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
297 /* game button identifiers */
298 #define GAME_CTRL_ID_STOP 0
299 #define GAME_CTRL_ID_PAUSE 1
300 #define GAME_CTRL_ID_PLAY 2
301 #define SOUND_CTRL_ID_MUSIC 3
302 #define SOUND_CTRL_ID_LOOPS 4
303 #define SOUND_CTRL_ID_SIMPLE 5
305 #define NUM_GAME_BUTTONS 6
308 /* forward declaration for internal use */
310 static void CreateField(int, int, int);
312 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
313 static void AdvanceFrameAndPlayerCounters(int);
315 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
316 static boolean MovePlayer(struct PlayerInfo *, int, int);
317 static void ScrollPlayer(struct PlayerInfo *, int);
318 static void ScrollScreen(struct PlayerInfo *, int);
320 int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
322 static void InitBeltMovement(void);
323 static void CloseAllOpenTimegates(void);
324 static void CheckGravityMovement(struct PlayerInfo *);
325 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
326 static void KillPlayerUnlessEnemyProtected(int, int);
327 static void KillPlayerUnlessExplosionProtected(int, int);
329 static void TestIfPlayerTouchesCustomElement(int, int);
330 static void TestIfElementTouchesCustomElement(int, int);
331 static void TestIfElementHitsCustomElement(int, int, int);
333 static void TestIfElementSmashesCustomElement(int, int, int);
336 static void HandleElementChange(int, int, int);
337 static void ExecuteCustomElementAction(int, int, int, int);
338 static boolean ChangeElement(int, int, int, int);
340 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
341 #define CheckTriggeredElementChange(x, y, e, ev) \
342 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
343 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s) \
344 CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
345 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s) \
346 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
347 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p) \
348 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
350 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
351 #define CheckElementChange(x, y, e, te, ev) \
352 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
353 #define CheckElementChangeByPlayer(x, y, e, ev, p, s) \
354 CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
355 #define CheckElementChangeBySide(x, y, e, te, ev, s) \
356 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
358 static void PlayLevelSound(int, int, int);
359 static void PlayLevelSoundNearest(int, int, int);
360 static void PlayLevelSoundAction(int, int, int);
361 static void PlayLevelSoundElementAction(int, int, int, int);
362 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
363 static void PlayLevelSoundActionIfLoop(int, int, int);
364 static void StopLevelSoundActionIfLoop(int, int, int);
365 static void PlayLevelMusic();
367 static void MapGameButtons();
368 static void HandleGameButtons(struct GadgetInfo *);
370 int AmoebeNachbarNr(int, int);
371 void AmoebeUmwandeln(int, int);
372 void ContinueMoving(int, int);
374 void InitMovDir(int, int);
375 void InitAmoebaNr(int, int);
376 int NewHiScore(void);
378 void TestIfGoodThingHitsBadThing(int, int, int);
379 void TestIfBadThingHitsGoodThing(int, int, int);
380 void TestIfPlayerTouchesBadThing(int, int);
381 void TestIfPlayerRunsIntoBadThing(int, int, int);
382 void TestIfBadThingTouchesPlayer(int, int);
383 void TestIfBadThingRunsIntoPlayer(int, int, int);
384 void TestIfFriendTouchesBadThing(int, int);
385 void TestIfBadThingTouchesFriend(int, int);
386 void TestIfBadThingTouchesOtherBadThing(int, int);
388 void KillPlayer(struct PlayerInfo *);
389 void BuryPlayer(struct PlayerInfo *);
390 void RemovePlayer(struct PlayerInfo *);
392 boolean SnapField(struct PlayerInfo *, int, int);
393 boolean DropElement(struct PlayerInfo *);
395 static int getInvisibleActiveFromInvisibleElement(int);
396 static int getInvisibleFromInvisibleActiveElement(int);
398 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
400 /* for detection of endless loops, caused by custom element programming */
401 /* (using maximal playfield width x 10 is just a rough approximation) */
402 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH (MAX_PLAYFIELD_WIDTH * 10)
404 #define RECURSION_LOOP_DETECTION_START(e, rc) \
406 if (recursion_loop_detected) \
409 if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH) \
411 recursion_loop_detected = TRUE; \
412 recursion_loop_element = (e); \
415 recursion_loop_depth++; \
418 #define RECURSION_LOOP_DETECTION_END() \
420 recursion_loop_depth--; \
423 static int recursion_loop_depth;
424 static boolean recursion_loop_detected;
425 static boolean recursion_loop_element;
428 /* ------------------------------------------------------------------------- */
429 /* definition of elements that automatically change to other elements after */
430 /* a specified time, eventually calling a function when changing */
431 /* ------------------------------------------------------------------------- */
433 /* forward declaration for changer functions */
434 static void InitBuggyBase(int, int);
435 static void WarnBuggyBase(int, int);
437 static void InitTrap(int, int);
438 static void ActivateTrap(int, int);
439 static void ChangeActiveTrap(int, int);
441 static void InitRobotWheel(int, int);
442 static void RunRobotWheel(int, int);
443 static void StopRobotWheel(int, int);
445 static void InitTimegateWheel(int, int);
446 static void RunTimegateWheel(int, int);
448 static void InitMagicBallDelay(int, int);
449 static void ActivateMagicBall(int, int);
451 struct ChangingElementInfo
456 void (*pre_change_function)(int x, int y);
457 void (*change_function)(int x, int y);
458 void (*post_change_function)(int x, int y);
461 static struct ChangingElementInfo change_delay_list[] =
496 EL_STEEL_EXIT_OPENING,
504 EL_STEEL_EXIT_CLOSING,
505 EL_STEEL_EXIT_CLOSED,
532 EL_EM_STEEL_EXIT_OPENING,
533 EL_EM_STEEL_EXIT_OPEN,
540 EL_EM_STEEL_EXIT_CLOSING,
544 EL_EM_STEEL_EXIT_CLOSED,
568 EL_SWITCHGATE_OPENING,
576 EL_SWITCHGATE_CLOSING,
577 EL_SWITCHGATE_CLOSED,
609 EL_ACID_SPLASH_RIGHT,
618 EL_SP_BUGGY_BASE_ACTIVATING,
625 EL_SP_BUGGY_BASE_ACTIVATING,
626 EL_SP_BUGGY_BASE_ACTIVE,
633 EL_SP_BUGGY_BASE_ACTIVE,
657 EL_ROBOT_WHEEL_ACTIVE,
665 EL_TIMEGATE_SWITCH_ACTIVE,
673 EL_DC_TIMEGATE_SWITCH_ACTIVE,
674 EL_DC_TIMEGATE_SWITCH,
681 EL_EMC_MAGIC_BALL_ACTIVE,
682 EL_EMC_MAGIC_BALL_ACTIVE,
689 EL_EMC_SPRING_BUMPER_ACTIVE,
690 EL_EMC_SPRING_BUMPER,
697 EL_DIAGONAL_SHRINKING,
726 int push_delay_fixed, push_delay_random;
731 { EL_BALLOON, 0, 0 },
733 { EL_SOKOBAN_OBJECT, 2, 0 },
734 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
735 { EL_SATELLITE, 2, 0 },
736 { EL_SP_DISK_YELLOW, 2, 0 },
738 { EL_UNDEFINED, 0, 0 },
746 move_stepsize_list[] =
748 { EL_AMOEBA_DROP, 2 },
749 { EL_AMOEBA_DROPPING, 2 },
750 { EL_QUICKSAND_FILLING, 1 },
751 { EL_QUICKSAND_EMPTYING, 1 },
752 { EL_QUICKSAND_FAST_FILLING, 2 },
753 { EL_QUICKSAND_FAST_EMPTYING, 2 },
754 { EL_MAGIC_WALL_FILLING, 2 },
755 { EL_MAGIC_WALL_EMPTYING, 2 },
756 { EL_BD_MAGIC_WALL_FILLING, 2 },
757 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
758 { EL_DC_MAGIC_WALL_FILLING, 2 },
759 { EL_DC_MAGIC_WALL_EMPTYING, 2 },
769 collect_count_list[] =
772 { EL_BD_DIAMOND, 1 },
773 { EL_EMERALD_YELLOW, 1 },
774 { EL_EMERALD_RED, 1 },
775 { EL_EMERALD_PURPLE, 1 },
777 { EL_SP_INFOTRON, 1 },
789 access_direction_list[] =
791 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
792 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
793 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
794 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
795 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
796 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
797 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
798 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
799 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
800 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
801 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
803 { EL_SP_PORT_LEFT, MV_RIGHT },
804 { EL_SP_PORT_RIGHT, MV_LEFT },
805 { EL_SP_PORT_UP, MV_DOWN },
806 { EL_SP_PORT_DOWN, MV_UP },
807 { EL_SP_PORT_HORIZONTAL, MV_LEFT | MV_RIGHT },
808 { EL_SP_PORT_VERTICAL, MV_UP | MV_DOWN },
809 { EL_SP_PORT_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
810 { EL_SP_GRAVITY_PORT_LEFT, MV_RIGHT },
811 { EL_SP_GRAVITY_PORT_RIGHT, MV_LEFT },
812 { EL_SP_GRAVITY_PORT_UP, MV_DOWN },
813 { EL_SP_GRAVITY_PORT_DOWN, MV_UP },
814 { EL_SP_GRAVITY_ON_PORT_LEFT, MV_RIGHT },
815 { EL_SP_GRAVITY_ON_PORT_RIGHT, MV_LEFT },
816 { EL_SP_GRAVITY_ON_PORT_UP, MV_DOWN },
817 { EL_SP_GRAVITY_ON_PORT_DOWN, MV_UP },
818 { EL_SP_GRAVITY_OFF_PORT_LEFT, MV_RIGHT },
819 { EL_SP_GRAVITY_OFF_PORT_RIGHT, MV_LEFT },
820 { EL_SP_GRAVITY_OFF_PORT_UP, MV_DOWN },
821 { EL_SP_GRAVITY_OFF_PORT_DOWN, MV_UP },
823 { EL_UNDEFINED, MV_NONE }
826 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
828 #define IS_AUTO_CHANGING(e) (element_info[e].has_change_event[CE_DELAY])
829 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
830 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
831 IS_JUST_CHANGING(x, y))
833 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
835 /* static variables for playfield scan mode (scanning forward or backward) */
836 static int playfield_scan_start_x = 0;
837 static int playfield_scan_start_y = 0;
838 static int playfield_scan_delta_x = 1;
839 static int playfield_scan_delta_y = 1;
841 #define SCAN_PLAYFIELD(x, y) for ((y) = playfield_scan_start_y; \
842 (y) >= 0 && (y) <= lev_fieldy - 1; \
843 (y) += playfield_scan_delta_y) \
844 for ((x) = playfield_scan_start_x; \
845 (x) >= 0 && (x) <= lev_fieldx - 1; \
846 (x) += playfield_scan_delta_x) \
849 void DEBUG_SetMaximumDynamite()
853 for (i = 0; i < MAX_INVENTORY_SIZE; i++)
854 if (local_player->inventory_size < MAX_INVENTORY_SIZE)
855 local_player->inventory_element[local_player->inventory_size++] =
860 static void InitPlayfieldScanModeVars()
862 if (game.use_reverse_scan_direction)
864 playfield_scan_start_x = lev_fieldx - 1;
865 playfield_scan_start_y = lev_fieldy - 1;
867 playfield_scan_delta_x = -1;
868 playfield_scan_delta_y = -1;
872 playfield_scan_start_x = 0;
873 playfield_scan_start_y = 0;
875 playfield_scan_delta_x = 1;
876 playfield_scan_delta_y = 1;
880 static void InitPlayfieldScanMode(int mode)
882 game.use_reverse_scan_direction =
883 (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
885 InitPlayfieldScanModeVars();
888 static int get_move_delay_from_stepsize(int move_stepsize)
891 MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
893 /* make sure that stepsize value is always a power of 2 */
894 move_stepsize = (1 << log_2(move_stepsize));
896 return TILEX / move_stepsize;
899 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
902 int player_nr = player->index_nr;
903 int move_delay = get_move_delay_from_stepsize(move_stepsize);
904 boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
906 /* do no immediately change move delay -- the player might just be moving */
907 player->move_delay_value_next = move_delay;
909 /* information if player can move must be set separately */
910 player->cannot_move = cannot_move;
914 player->move_delay = game.initial_move_delay[player_nr];
915 player->move_delay_value = game.initial_move_delay_value[player_nr];
917 player->move_delay_value_next = -1;
919 player->move_delay_reset_counter = 0;
923 void GetPlayerConfig()
925 GameFrameDelay = setup.game_frame_delay;
927 if (!audio.sound_available)
928 setup.sound_simple = FALSE;
930 if (!audio.loops_available)
931 setup.sound_loops = FALSE;
933 if (!audio.music_available)
934 setup.sound_music = FALSE;
936 if (!video.fullscreen_available)
937 setup.fullscreen = FALSE;
939 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
941 SetAudioMode(setup.sound);
945 static int get_element_from_group_element(int element)
947 if (IS_GROUP_ELEMENT(element))
949 struct ElementGroupInfo *group = element_info[element].group;
950 int last_anim_random_frame = gfx.anim_random_frame;
953 if (group->choice_mode == ANIM_RANDOM)
954 gfx.anim_random_frame = RND(group->num_elements_resolved);
956 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
957 group->choice_mode, 0,
960 if (group->choice_mode == ANIM_RANDOM)
961 gfx.anim_random_frame = last_anim_random_frame;
965 element = group->element_resolved[element_pos];
971 static void InitPlayerField(int x, int y, int element, boolean init_game)
973 if (element == EL_SP_MURPHY)
977 if (stored_player[0].present)
979 Feld[x][y] = EL_SP_MURPHY_CLONE;
985 stored_player[0].use_murphy = TRUE;
987 if (!level.use_artwork_element[0])
988 stored_player[0].artwork_element = EL_SP_MURPHY;
991 Feld[x][y] = EL_PLAYER_1;
997 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
998 int jx = player->jx, jy = player->jy;
1000 player->present = TRUE;
1002 player->block_last_field = (element == EL_SP_MURPHY ?
1003 level.sp_block_last_field :
1004 level.block_last_field);
1006 /* ---------- initialize player's last field block delay --------------- */
1008 /* always start with reliable default value (no adjustment needed) */
1009 player->block_delay_adjustment = 0;
1011 /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1012 if (player->block_last_field && element == EL_SP_MURPHY)
1013 player->block_delay_adjustment = 1;
1015 /* special case 2: in game engines before 3.1.1, blocking was different */
1016 if (game.use_block_last_field_bug)
1017 player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1019 if (!options.network || player->connected)
1021 player->active = TRUE;
1023 /* remove potentially duplicate players */
1024 if (StorePlayer[jx][jy] == Feld[x][y])
1025 StorePlayer[jx][jy] = 0;
1027 StorePlayer[x][y] = Feld[x][y];
1031 printf("Player %d activated.\n", player->element_nr);
1032 printf("[Local player is %d and currently %s.]\n",
1033 local_player->element_nr,
1034 local_player->active ? "active" : "not active");
1038 Feld[x][y] = EL_EMPTY;
1040 player->jx = player->last_jx = x;
1041 player->jy = player->last_jy = y;
1045 static void InitField(int x, int y, boolean init_game)
1047 int element = Feld[x][y];
1056 InitPlayerField(x, y, element, init_game);
1059 case EL_SOKOBAN_FIELD_PLAYER:
1060 element = Feld[x][y] = EL_PLAYER_1;
1061 InitField(x, y, init_game);
1063 element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1064 InitField(x, y, init_game);
1067 case EL_SOKOBAN_FIELD_EMPTY:
1068 local_player->sokobanfields_still_needed++;
1072 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1073 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1074 else if (x > 0 && Feld[x-1][y] == EL_ACID)
1075 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1076 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1077 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1078 else if (y > 0 && Feld[x][y-1] == EL_ACID)
1079 Feld[x][y] = EL_ACID_POOL_BOTTOM;
1080 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1081 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1090 case EL_SPACESHIP_RIGHT:
1091 case EL_SPACESHIP_UP:
1092 case EL_SPACESHIP_LEFT:
1093 case EL_SPACESHIP_DOWN:
1094 case EL_BD_BUTTERFLY:
1095 case EL_BD_BUTTERFLY_RIGHT:
1096 case EL_BD_BUTTERFLY_UP:
1097 case EL_BD_BUTTERFLY_LEFT:
1098 case EL_BD_BUTTERFLY_DOWN:
1100 case EL_BD_FIREFLY_RIGHT:
1101 case EL_BD_FIREFLY_UP:
1102 case EL_BD_FIREFLY_LEFT:
1103 case EL_BD_FIREFLY_DOWN:
1104 case EL_PACMAN_RIGHT:
1106 case EL_PACMAN_LEFT:
1107 case EL_PACMAN_DOWN:
1109 case EL_YAMYAM_LEFT:
1110 case EL_YAMYAM_RIGHT:
1112 case EL_YAMYAM_DOWN:
1113 case EL_DARK_YAMYAM:
1116 case EL_SP_SNIKSNAK:
1117 case EL_SP_ELECTRON:
1126 case EL_AMOEBA_FULL:
1131 case EL_AMOEBA_DROP:
1132 if (y == lev_fieldy - 1)
1134 Feld[x][y] = EL_AMOEBA_GROWING;
1135 Store[x][y] = EL_AMOEBA_WET;
1139 case EL_DYNAMITE_ACTIVE:
1140 case EL_SP_DISK_RED_ACTIVE:
1141 case EL_DYNABOMB_PLAYER_1_ACTIVE:
1142 case EL_DYNABOMB_PLAYER_2_ACTIVE:
1143 case EL_DYNABOMB_PLAYER_3_ACTIVE:
1144 case EL_DYNABOMB_PLAYER_4_ACTIVE:
1145 MovDelay[x][y] = 96;
1148 case EL_EM_DYNAMITE_ACTIVE:
1149 MovDelay[x][y] = 32;
1153 local_player->lights_still_needed++;
1157 local_player->friends_still_needed++;
1162 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1165 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1166 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1167 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1168 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1169 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1170 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1171 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1172 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1173 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1174 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1175 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1176 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1179 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1180 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1181 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1183 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
1185 game.belt_dir[belt_nr] = belt_dir;
1186 game.belt_dir_nr[belt_nr] = belt_dir_nr;
1188 else /* more than one switch -- set it like the first switch */
1190 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1195 #if !USE_BOTH_SWITCHGATE_SWITCHES
1196 case EL_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
1198 Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
1201 case EL_DC_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
1203 Feld[x][y] = EL_DC_SWITCHGATE_SWITCH_UP;
1207 case EL_LIGHT_SWITCH_ACTIVE:
1209 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1212 case EL_INVISIBLE_STEELWALL:
1213 case EL_INVISIBLE_WALL:
1214 case EL_INVISIBLE_SAND:
1215 if (game.light_time_left > 0 ||
1216 game.lenses_time_left > 0)
1217 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1220 case EL_EMC_MAGIC_BALL:
1221 if (game.ball_state)
1222 Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1225 case EL_EMC_MAGIC_BALL_SWITCH:
1226 if (game.ball_state)
1227 Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1231 if (IS_CUSTOM_ELEMENT(element))
1233 if (CAN_MOVE(element))
1236 #if USE_NEW_CUSTOM_VALUE
1237 if (!element_info[element].use_last_ce_value || init_game)
1238 CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1241 else if (IS_GROUP_ELEMENT(element))
1243 Feld[x][y] = get_element_from_group_element(element);
1245 InitField(x, y, init_game);
1252 CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1255 static inline void InitField_WithBug1(int x, int y, boolean init_game)
1257 InitField(x, y, init_game);
1259 /* not needed to call InitMovDir() -- already done by InitField()! */
1260 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1261 CAN_MOVE(Feld[x][y]))
1265 static inline void InitField_WithBug2(int x, int y, boolean init_game)
1267 int old_element = Feld[x][y];
1269 InitField(x, y, init_game);
1271 /* not needed to call InitMovDir() -- already done by InitField()! */
1272 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1273 CAN_MOVE(old_element) &&
1274 (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1277 /* this case is in fact a combination of not less than three bugs:
1278 first, it calls InitMovDir() for elements that can move, although this is
1279 already done by InitField(); then, it checks the element that was at this
1280 field _before_ the call to InitField() (which can change it); lastly, it
1281 was not called for "mole with direction" elements, which were treated as
1282 "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1288 void DrawGameValue_Emeralds(int value)
1290 struct TextPosInfo *pos = &game.panel.gems;
1291 int font_nr = FONT_TEXT_2;
1292 int font_width = getFontWidth(font_nr);
1293 int digits = pos->chars;
1295 if (PANEL_DEACTIVATED(pos))
1298 pos->width = digits * font_width;
1300 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, digits), font_nr);
1303 void DrawGameValue_Dynamite(int value)
1305 struct TextPosInfo *pos = &game.panel.inventory;
1306 int font_nr = FONT_TEXT_2;
1307 int font_width = getFontWidth(font_nr);
1308 int digits = pos->chars;
1310 if (PANEL_DEACTIVATED(pos))
1313 pos->width = digits * font_width;
1315 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, digits), font_nr);
1318 void DrawGameValue_Score(int value)
1320 struct TextPosInfo *pos = &game.panel.score;
1321 int font_nr = FONT_TEXT_2;
1322 int font_width = getFontWidth(font_nr);
1323 int digits = pos->chars;
1325 if (PANEL_DEACTIVATED(pos))
1328 pos->width = digits * font_width;
1330 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, digits), font_nr);
1333 void DrawGameValue_Time(int value)
1335 struct TextPosInfo *pos = &game.panel.time;
1336 static int last_value = -1;
1339 int digits = pos->chars;
1340 int font1_nr = FONT_TEXT_2;
1341 int font2_nr = FONT_TEXT_1;
1342 int font_nr = font1_nr;
1343 boolean use_dynamic_digits = (digits == -1 ? TRUE : FALSE);
1345 if (PANEL_DEACTIVATED(pos))
1348 if (use_dynamic_digits) /* use dynamic number of digits */
1350 digits = (value < 1000 ? digits1 : digits2);
1351 font_nr = (value < 1000 ? font1_nr : font2_nr);
1354 /* clear background if value just changed its size (dynamic digits only) */
1355 if (use_dynamic_digits && (last_value < 1000) != (value < 1000))
1357 int width1 = digits1 * getFontWidth(font1_nr);
1358 int width2 = digits2 * getFontWidth(font2_nr);
1359 int max_width = MAX(width1, width2);
1360 int max_height = MAX(getFontHeight(font1_nr), getFontHeight(font2_nr));
1362 pos->width = max_width;
1364 ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
1365 max_width, max_height);
1368 pos->width = digits * getFontWidth(font_nr);
1370 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, digits), font_nr);
1375 void DrawGameValue_Level(int value)
1377 struct TextPosInfo *pos = &game.panel.level;
1380 int digits = pos->chars;
1381 int font1_nr = FONT_TEXT_2;
1382 int font2_nr = FONT_TEXT_1;
1383 int font_nr = font1_nr;
1384 boolean use_dynamic_digits = (digits == -1 ? TRUE : FALSE);
1386 if (PANEL_DEACTIVATED(pos))
1389 if (use_dynamic_digits) /* use dynamic number of digits */
1391 digits = (level_nr < 100 ? digits1 : digits2);
1392 font_nr = (level_nr < 100 ? font1_nr : font2_nr);
1395 pos->width = digits * getFontWidth(font_nr);
1397 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, digits), font_nr);
1400 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
1402 struct TextPosInfo *pos = &game.panel.keys;
1403 int base_key_graphic = EL_KEY_1;
1406 if (PANEL_DEACTIVATED(pos))
1409 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
1410 base_key_graphic = EL_EM_KEY_1;
1412 pos->width = 4 * MINI_TILEX;
1414 /* currently only 4 of 8 possible keys are displayed */
1415 for (i = 0; i < STD_NUM_KEYS; i++)
1417 int src_x = DOOR_GFX_PAGEX5 + 18;
1418 int src_y = DOOR_GFX_PAGEY1 + 123;
1419 int dst_x = PANEL_XPOS(pos) + i * MINI_TILEX;
1420 int dst_y = PANEL_YPOS(pos);
1423 DrawMiniGraphicExt(drawto, dst_x, dst_y, el2edimg(base_key_graphic + i));
1425 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
1426 MINI_TILEX, MINI_TILEY, dst_x, dst_y);
1432 void DrawGameValue_Emeralds(int value)
1434 int font_nr = FONT_TEXT_2;
1435 int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
1437 if (PANEL_DEACTIVATED(game.panel.gems))
1440 DrawText(DX_EMERALDS + xpos, DY_EMERALDS, int2str(value, 3), font_nr);
1443 void DrawGameValue_Dynamite(int value)
1445 int font_nr = FONT_TEXT_2;
1446 int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
1448 if (PANEL_DEACTIVATED(game.panel.inventory))
1451 DrawText(DX_DYNAMITE + xpos, DY_DYNAMITE, int2str(value, 3), font_nr);
1454 void DrawGameValue_Score(int value)
1456 int font_nr = FONT_TEXT_2;
1457 int xpos = (5 * 14 - 5 * getFontWidth(font_nr)) / 2;
1459 if (PANEL_DEACTIVATED(game.panel.score))
1462 DrawText(DX_SCORE + xpos, DY_SCORE, int2str(value, 5), font_nr);
1465 void DrawGameValue_Time(int value)
1467 int font1_nr = FONT_TEXT_2;
1469 int font2_nr = FONT_TEXT_1;
1471 int font2_nr = FONT_LEVEL_NUMBER;
1473 int xpos3 = (3 * 14 - 3 * getFontWidth(font1_nr)) / 2;
1474 int xpos4 = (4 * 10 - 4 * getFontWidth(font2_nr)) / 2;
1476 if (PANEL_DEACTIVATED(game.panel.time))
1479 /* clear background if value just changed its size */
1480 if (value == 999 || value == 1000)
1481 ClearRectangleOnBackground(drawto, DX_TIME1, DY_TIME, 14 * 3, 14);
1484 DrawText(DX_TIME1 + xpos3, DY_TIME, int2str(value, 3), font1_nr);
1486 DrawText(DX_TIME2 + xpos4, DY_TIME, int2str(value, 4), font2_nr);
1489 void DrawGameValue_Level(int value)
1491 int font1_nr = FONT_TEXT_2;
1493 int font2_nr = FONT_TEXT_1;
1495 int font2_nr = FONT_LEVEL_NUMBER;
1498 if (PANEL_DEACTIVATED(game.panel.level))
1502 DrawText(DX_LEVEL1, DY_LEVEL, int2str(value, 2), font1_nr);
1504 DrawText(DX_LEVEL2, DY_LEVEL, int2str(value, 3), font2_nr);
1507 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
1509 int base_key_graphic = EL_KEY_1;
1512 if (PANEL_DEACTIVATED(game.panel.keys))
1515 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
1516 base_key_graphic = EL_EM_KEY_1;
1518 /* currently only 4 of 8 possible keys are displayed */
1519 for (i = 0; i < STD_NUM_KEYS; i++)
1521 int x = XX_KEYS + i * MINI_TILEX;
1525 DrawMiniGraphicExt(drawto, DX + x,DY + y, el2edimg(base_key_graphic + i));
1527 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
1528 DOOR_GFX_PAGEX5 + x, y, MINI_TILEX, MINI_TILEY, DX + x,DY + y);
1534 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
1537 int key[MAX_NUM_KEYS];
1540 /* prevent EM engine from updating time/score values parallel to GameWon() */
1541 if (level.game_engine_type == GAME_ENGINE_TYPE_EM &&
1542 local_player->LevelSolved)
1545 for (i = 0; i < MAX_NUM_KEYS; i++)
1546 key[i] = key_bits & (1 << i);
1548 DrawGameValue_Level(level_nr);
1550 DrawGameValue_Emeralds(emeralds);
1551 DrawGameValue_Dynamite(dynamite);
1552 DrawGameValue_Score(score);
1553 DrawGameValue_Time(time);
1555 DrawGameValue_Keys(key);
1558 void DrawGameDoorValues()
1560 int time_value = (level.time == 0 ? TimePlayed : TimeLeft);
1561 int dynamite_value = 0;
1562 int score_value = (local_player->LevelSolved ? local_player->score_final :
1563 local_player->score);
1564 int gems_value = local_player->gems_still_needed;
1568 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
1570 DrawGameDoorValues_EM();
1575 if (game.centered_player_nr == -1)
1577 for (i = 0; i < MAX_PLAYERS; i++)
1579 for (j = 0; j < MAX_NUM_KEYS; j++)
1580 if (stored_player[i].key[j])
1581 key_bits |= (1 << j);
1583 dynamite_value += stored_player[i].inventory_size;
1588 int player_nr = game.centered_player_nr;
1590 for (i = 0; i < MAX_NUM_KEYS; i++)
1591 if (stored_player[player_nr].key[i])
1592 key_bits |= (1 << i);
1594 dynamite_value = stored_player[player_nr].inventory_size;
1597 DrawAllGameValues(gems_value, dynamite_value, score_value, time_value,
1603 =============================================================================
1605 -----------------------------------------------------------------------------
1606 initialize game engine due to level / tape version number
1607 =============================================================================
1610 static void InitGameEngine()
1612 int i, j, k, l, x, y;
1614 /* set game engine from tape file when re-playing, else from level file */
1615 game.engine_version = (tape.playing ? tape.engine_version :
1616 level.game_version);
1618 /* ---------------------------------------------------------------------- */
1619 /* set flags for bugs and changes according to active game engine version */
1620 /* ---------------------------------------------------------------------- */
1623 Summary of bugfix/change:
1624 Fixed handling for custom elements that change when pushed by the player.
1626 Fixed/changed in version:
1630 Before 3.1.0, custom elements that "change when pushing" changed directly
1631 after the player started pushing them (until then handled in "DigField()").
1632 Since 3.1.0, these custom elements are not changed until the "pushing"
1633 move of the element is finished (now handled in "ContinueMoving()").
1635 Affected levels/tapes:
1636 The first condition is generally needed for all levels/tapes before version
1637 3.1.0, which might use the old behaviour before it was changed; known tapes
1638 that are affected are some tapes from the level set "Walpurgis Gardens" by
1640 The second condition is an exception from the above case and is needed for
1641 the special case of tapes recorded with game (not engine!) version 3.1.0 or
1642 above (including some development versions of 3.1.0), but before it was
1643 known that this change would break tapes like the above and was fixed in
1644 3.1.1, so that the changed behaviour was active although the engine version
1645 while recording maybe was before 3.1.0. There is at least one tape that is
1646 affected by this exception, which is the tape for the one-level set "Bug
1647 Machine" by Juergen Bonhagen.
1650 game.use_change_when_pushing_bug =
1651 (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1653 tape.game_version >= VERSION_IDENT(3,1,0,0) &&
1654 tape.game_version < VERSION_IDENT(3,1,1,0)));
1657 Summary of bugfix/change:
1658 Fixed handling for blocking the field the player leaves when moving.
1660 Fixed/changed in version:
1664 Before 3.1.1, when "block last field when moving" was enabled, the field
1665 the player is leaving when moving was blocked for the time of the move,
1666 and was directly unblocked afterwards. This resulted in the last field
1667 being blocked for exactly one less than the number of frames of one player
1668 move. Additionally, even when blocking was disabled, the last field was
1669 blocked for exactly one frame.
1670 Since 3.1.1, due to changes in player movement handling, the last field
1671 is not blocked at all when blocking is disabled. When blocking is enabled,
1672 the last field is blocked for exactly the number of frames of one player
1673 move. Additionally, if the player is Murphy, the hero of Supaplex, the
1674 last field is blocked for exactly one more than the number of frames of
1677 Affected levels/tapes:
1678 (!!! yet to be determined -- probably many !!!)
1681 game.use_block_last_field_bug =
1682 (game.engine_version < VERSION_IDENT(3,1,1,0));
1685 Summary of bugfix/change:
1686 Changed behaviour of CE changes with multiple changes per single frame.
1688 Fixed/changed in version:
1692 Before 3.2.0-6, only one single CE change was allowed in each engine frame.
1693 This resulted in race conditions where CEs seem to behave strange in some
1694 situations (where triggered CE changes were just skipped because there was
1695 already a CE change on that tile in the playfield in that engine frame).
1696 Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
1697 (The number of changes per frame must be limited in any case, because else
1698 it is easily possible to define CE changes that would result in an infinite
1699 loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
1700 should be set large enough so that it would only be reached in cases where
1701 the corresponding CE change conditions run into a loop. Therefore, it seems
1702 to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
1703 maximal number of change pages for custom elements.)
1705 Affected levels/tapes:
1709 #if USE_ONLY_ONE_CHANGE_PER_FRAME
1710 game.max_num_changes_per_frame = 1;
1712 game.max_num_changes_per_frame =
1713 (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
1716 /* ---------------------------------------------------------------------- */
1718 /* default scan direction: scan playfield from top/left to bottom/right */
1719 InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
1721 /* dynamically adjust element properties according to game engine version */
1722 InitElementPropertiesEngine(game.engine_version);
1725 printf("level %d: level version == %06d\n", level_nr, level.game_version);
1726 printf(" tape version == %06d [%s] [file: %06d]\n",
1727 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
1729 printf(" => game.engine_version == %06d\n", game.engine_version);
1732 /* ---------- initialize player's initial move delay --------------------- */
1734 /* dynamically adjust player properties according to level information */
1735 for (i = 0; i < MAX_PLAYERS; i++)
1736 game.initial_move_delay_value[i] =
1737 get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
1739 /* dynamically adjust player properties according to game engine version */
1740 for (i = 0; i < MAX_PLAYERS; i++)
1741 game.initial_move_delay[i] =
1742 (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
1743 game.initial_move_delay_value[i] : 0);
1745 /* ---------- initialize player's initial push delay --------------------- */
1747 /* dynamically adjust player properties according to game engine version */
1748 game.initial_push_delay_value =
1749 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
1751 /* ---------- initialize changing elements ------------------------------- */
1753 /* initialize changing elements information */
1754 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1756 struct ElementInfo *ei = &element_info[i];
1758 /* this pointer might have been changed in the level editor */
1759 ei->change = &ei->change_page[0];
1761 if (!IS_CUSTOM_ELEMENT(i))
1763 ei->change->target_element = EL_EMPTY_SPACE;
1764 ei->change->delay_fixed = 0;
1765 ei->change->delay_random = 0;
1766 ei->change->delay_frames = 1;
1769 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1771 ei->has_change_event[j] = FALSE;
1773 ei->event_page_nr[j] = 0;
1774 ei->event_page[j] = &ei->change_page[0];
1778 /* add changing elements from pre-defined list */
1779 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
1781 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
1782 struct ElementInfo *ei = &element_info[ch_delay->element];
1784 ei->change->target_element = ch_delay->target_element;
1785 ei->change->delay_fixed = ch_delay->change_delay;
1787 ei->change->pre_change_function = ch_delay->pre_change_function;
1788 ei->change->change_function = ch_delay->change_function;
1789 ei->change->post_change_function = ch_delay->post_change_function;
1791 ei->change->can_change = TRUE;
1792 ei->change->can_change_or_has_action = TRUE;
1794 ei->has_change_event[CE_DELAY] = TRUE;
1796 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
1797 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
1800 /* ---------- initialize internal run-time variables ------------- */
1802 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1804 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1806 for (j = 0; j < ei->num_change_pages; j++)
1808 ei->change_page[j].can_change_or_has_action =
1809 (ei->change_page[j].can_change |
1810 ei->change_page[j].has_action);
1814 /* add change events from custom element configuration */
1815 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1817 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1819 for (j = 0; j < ei->num_change_pages; j++)
1821 if (!ei->change_page[j].can_change_or_has_action)
1824 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1826 /* only add event page for the first page found with this event */
1827 if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
1829 ei->has_change_event[k] = TRUE;
1831 ei->event_page_nr[k] = j;
1832 ei->event_page[k] = &ei->change_page[j];
1838 /* ---------- initialize run-time trigger player and element ------------- */
1840 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1842 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1844 for (j = 0; j < ei->num_change_pages; j++)
1846 ei->change_page[j].actual_trigger_element = EL_EMPTY;
1847 ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
1848 ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
1849 ei->change_page[j].actual_trigger_ce_value = 0;
1850 ei->change_page[j].actual_trigger_ce_score = 0;
1854 /* ---------- initialize trigger events ---------------------------------- */
1856 /* initialize trigger events information */
1857 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1858 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1859 trigger_events[i][j] = FALSE;
1861 /* add trigger events from element change event properties */
1862 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1864 struct ElementInfo *ei = &element_info[i];
1866 for (j = 0; j < ei->num_change_pages; j++)
1868 if (!ei->change_page[j].can_change_or_has_action)
1871 if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
1873 int trigger_element = ei->change_page[j].trigger_element;
1875 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1877 if (ei->change_page[j].has_event[k])
1879 if (IS_GROUP_ELEMENT(trigger_element))
1881 struct ElementGroupInfo *group =
1882 element_info[trigger_element].group;
1884 for (l = 0; l < group->num_elements_resolved; l++)
1885 trigger_events[group->element_resolved[l]][k] = TRUE;
1887 else if (trigger_element == EL_ANY_ELEMENT)
1888 for (l = 0; l < MAX_NUM_ELEMENTS; l++)
1889 trigger_events[l][k] = TRUE;
1891 trigger_events[trigger_element][k] = TRUE;
1898 /* ---------- initialize push delay -------------------------------------- */
1900 /* initialize push delay values to default */
1901 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1903 if (!IS_CUSTOM_ELEMENT(i))
1905 /* set default push delay values (corrected since version 3.0.7-1) */
1906 if (game.engine_version < VERSION_IDENT(3,0,7,1))
1908 element_info[i].push_delay_fixed = 2;
1909 element_info[i].push_delay_random = 8;
1913 element_info[i].push_delay_fixed = 8;
1914 element_info[i].push_delay_random = 8;
1919 /* set push delay value for certain elements from pre-defined list */
1920 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
1922 int e = push_delay_list[i].element;
1924 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
1925 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
1928 /* set push delay value for Supaplex elements for newer engine versions */
1929 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
1931 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1933 if (IS_SP_ELEMENT(i))
1935 /* set SP push delay to just enough to push under a falling zonk */
1936 int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
1938 element_info[i].push_delay_fixed = delay;
1939 element_info[i].push_delay_random = 0;
1944 /* ---------- initialize move stepsize ----------------------------------- */
1946 /* initialize move stepsize values to default */
1947 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1948 if (!IS_CUSTOM_ELEMENT(i))
1949 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
1951 /* set move stepsize value for certain elements from pre-defined list */
1952 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
1954 int e = move_stepsize_list[i].element;
1956 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
1959 /* ---------- initialize collect score ----------------------------------- */
1961 /* initialize collect score values for custom elements from initial value */
1962 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1963 if (IS_CUSTOM_ELEMENT(i))
1964 element_info[i].collect_score = element_info[i].collect_score_initial;
1966 /* ---------- initialize collect count ----------------------------------- */
1968 /* initialize collect count values for non-custom elements */
1969 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1970 if (!IS_CUSTOM_ELEMENT(i))
1971 element_info[i].collect_count_initial = 0;
1973 /* add collect count values for all elements from pre-defined list */
1974 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
1975 element_info[collect_count_list[i].element].collect_count_initial =
1976 collect_count_list[i].count;
1978 /* ---------- initialize access direction -------------------------------- */
1980 /* initialize access direction values to default (access from every side) */
1981 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1982 if (!IS_CUSTOM_ELEMENT(i))
1983 element_info[i].access_direction = MV_ALL_DIRECTIONS;
1985 /* set access direction value for certain elements from pre-defined list */
1986 for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
1987 element_info[access_direction_list[i].element].access_direction =
1988 access_direction_list[i].direction;
1990 /* ---------- initialize explosion content ------------------------------- */
1991 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1993 if (IS_CUSTOM_ELEMENT(i))
1996 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
1998 /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
2000 element_info[i].content.e[x][y] =
2001 (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
2002 i == EL_PLAYER_2 ? EL_EMERALD_RED :
2003 i == EL_PLAYER_3 ? EL_EMERALD :
2004 i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
2005 i == EL_MOLE ? EL_EMERALD_RED :
2006 i == EL_PENGUIN ? EL_EMERALD_PURPLE :
2007 i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
2008 i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
2009 i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
2010 i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
2011 i == EL_WALL_EMERALD ? EL_EMERALD :
2012 i == EL_WALL_DIAMOND ? EL_DIAMOND :
2013 i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
2014 i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
2015 i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
2016 i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
2017 i == EL_WALL_PEARL ? EL_PEARL :
2018 i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
2023 /* ---------- initialize recursion detection ------------------------------ */
2024 recursion_loop_depth = 0;
2025 recursion_loop_detected = FALSE;
2026 recursion_loop_element = EL_UNDEFINED;
2029 int get_num_special_action(int element, int action_first, int action_last)
2031 int num_special_action = 0;
2034 for (i = action_first; i <= action_last; i++)
2036 boolean found = FALSE;
2038 for (j = 0; j < NUM_DIRECTIONS; j++)
2039 if (el_act_dir2img(element, i, j) !=
2040 el_act_dir2img(element, ACTION_DEFAULT, j))
2044 num_special_action++;
2049 return num_special_action;
2054 =============================================================================
2056 -----------------------------------------------------------------------------
2057 initialize and start new game
2058 =============================================================================
2063 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
2064 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
2065 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
2066 boolean do_fading = (game_status == GAME_MODE_MAIN);
2069 game_status = GAME_MODE_PLAYING;
2073 /* don't play tapes over network */
2074 network_playing = (options.network && !tape.playing);
2076 for (i = 0; i < MAX_PLAYERS; i++)
2078 struct PlayerInfo *player = &stored_player[i];
2080 player->index_nr = i;
2081 player->index_bit = (1 << i);
2082 player->element_nr = EL_PLAYER_1 + i;
2084 player->present = FALSE;
2085 player->active = FALSE;
2086 player->killed = FALSE;
2089 player->effective_action = 0;
2090 player->programmed_action = 0;
2093 player->score_final = 0;
2095 player->gems_still_needed = level.gems_needed;
2096 player->sokobanfields_still_needed = 0;
2097 player->lights_still_needed = 0;
2098 player->friends_still_needed = 0;
2100 for (j = 0; j < MAX_NUM_KEYS; j++)
2101 player->key[j] = FALSE;
2103 player->num_white_keys = 0;
2105 player->dynabomb_count = 0;
2106 player->dynabomb_size = 1;
2107 player->dynabombs_left = 0;
2108 player->dynabomb_xl = FALSE;
2110 player->MovDir = MV_NONE;
2113 player->GfxDir = MV_NONE;
2114 player->GfxAction = ACTION_DEFAULT;
2116 player->StepFrame = 0;
2118 player->use_murphy = FALSE;
2119 player->artwork_element =
2120 (level.use_artwork_element[i] ? level.artwork_element[i] :
2121 player->element_nr);
2123 player->block_last_field = FALSE; /* initialized in InitPlayerField() */
2124 player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
2126 player->gravity = level.initial_player_gravity[i];
2128 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
2130 player->actual_frame_counter = 0;
2132 player->step_counter = 0;
2134 player->last_move_dir = MV_NONE;
2136 player->is_active = FALSE;
2138 player->is_waiting = FALSE;
2139 player->is_moving = FALSE;
2140 player->is_auto_moving = FALSE;
2141 player->is_digging = FALSE;
2142 player->is_snapping = FALSE;
2143 player->is_collecting = FALSE;
2144 player->is_pushing = FALSE;
2145 player->is_switching = FALSE;
2146 player->is_dropping = FALSE;
2147 player->is_dropping_pressed = FALSE;
2149 player->is_bored = FALSE;
2150 player->is_sleeping = FALSE;
2152 player->frame_counter_bored = -1;
2153 player->frame_counter_sleeping = -1;
2155 player->anim_delay_counter = 0;
2156 player->post_delay_counter = 0;
2158 player->dir_waiting = MV_NONE;
2159 player->action_waiting = ACTION_DEFAULT;
2160 player->last_action_waiting = ACTION_DEFAULT;
2161 player->special_action_bored = ACTION_DEFAULT;
2162 player->special_action_sleeping = ACTION_DEFAULT;
2164 player->switch_x = -1;
2165 player->switch_y = -1;
2167 player->drop_x = -1;
2168 player->drop_y = -1;
2170 player->show_envelope = 0;
2172 SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
2174 player->push_delay = -1; /* initialized when pushing starts */
2175 player->push_delay_value = game.initial_push_delay_value;
2177 player->drop_delay = 0;
2178 player->drop_pressed_delay = 0;
2180 player->last_jx = -1;
2181 player->last_jy = -1;
2185 player->shield_normal_time_left = 0;
2186 player->shield_deadly_time_left = 0;
2188 player->inventory_infinite_element = EL_UNDEFINED;
2189 player->inventory_size = 0;
2191 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
2192 SnapField(player, 0, 0);
2194 player->LevelSolved = FALSE;
2195 player->GameOver = FALSE;
2197 player->LevelSolved_GameWon = FALSE;
2198 player->LevelSolved_GameEnd = FALSE;
2199 player->LevelSolved_PanelOff = FALSE;
2200 player->LevelSolved_SaveTape = FALSE;
2201 player->LevelSolved_SaveScore = FALSE;
2204 network_player_action_received = FALSE;
2206 #if defined(NETWORK_AVALIABLE)
2207 /* initial null action */
2208 if (network_playing)
2209 SendToServer_MovePlayer(MV_NONE);
2218 TimeLeft = level.time;
2221 ScreenMovDir = MV_NONE;
2225 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
2227 AllPlayersGone = FALSE;
2229 game.yamyam_content_nr = 0;
2230 game.magic_wall_active = FALSE;
2231 game.magic_wall_time_left = 0;
2232 game.light_time_left = 0;
2233 game.timegate_time_left = 0;
2234 game.switchgate_pos = 0;
2235 game.wind_direction = level.wind_direction_initial;
2237 #if !USE_PLAYER_GRAVITY
2238 game.gravity = FALSE;
2239 game.explosions_delayed = TRUE;
2242 game.lenses_time_left = 0;
2243 game.magnify_time_left = 0;
2245 game.ball_state = level.ball_state_initial;
2246 game.ball_content_nr = 0;
2248 game.envelope_active = FALSE;
2250 /* set focus to local player for network games, else to all players */
2251 game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
2252 game.centered_player_nr_next = game.centered_player_nr;
2253 game.set_centered_player = FALSE;
2255 if (network_playing && tape.recording)
2257 /* store client dependent player focus when recording network games */
2258 tape.centered_player_nr_next = game.centered_player_nr_next;
2259 tape.set_centered_player = TRUE;
2262 for (i = 0; i < NUM_BELTS; i++)
2264 game.belt_dir[i] = MV_NONE;
2265 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
2268 for (i = 0; i < MAX_NUM_AMOEBA; i++)
2269 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
2271 SCAN_PLAYFIELD(x, y)
2273 Feld[x][y] = level.field[x][y];
2274 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
2275 ChangeDelay[x][y] = 0;
2276 ChangePage[x][y] = -1;
2277 #if USE_NEW_CUSTOM_VALUE
2278 CustomValue[x][y] = 0; /* initialized in InitField() */
2280 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
2282 WasJustMoving[x][y] = 0;
2283 WasJustFalling[x][y] = 0;
2284 CheckCollision[x][y] = 0;
2285 CheckImpact[x][y] = 0;
2287 Pushed[x][y] = FALSE;
2289 ChangeCount[x][y] = 0;
2290 ChangeEvent[x][y] = -1;
2292 ExplodePhase[x][y] = 0;
2293 ExplodeDelay[x][y] = 0;
2294 ExplodeField[x][y] = EX_TYPE_NONE;
2296 RunnerVisit[x][y] = 0;
2297 PlayerVisit[x][y] = 0;
2300 GfxRandom[x][y] = INIT_GFX_RANDOM();
2301 GfxElement[x][y] = EL_UNDEFINED;
2302 GfxAction[x][y] = ACTION_DEFAULT;
2303 GfxDir[x][y] = MV_NONE;
2306 SCAN_PLAYFIELD(x, y)
2308 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
2310 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
2312 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
2315 InitField(x, y, TRUE);
2320 for (i = 0; i < MAX_PLAYERS; i++)
2322 struct PlayerInfo *player = &stored_player[i];
2324 /* set number of special actions for bored and sleeping animation */
2325 player->num_special_action_bored =
2326 get_num_special_action(player->artwork_element,
2327 ACTION_BORING_1, ACTION_BORING_LAST);
2328 player->num_special_action_sleeping =
2329 get_num_special_action(player->artwork_element,
2330 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
2333 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
2334 emulate_sb ? EMU_SOKOBAN :
2335 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
2337 #if USE_NEW_ALL_SLIPPERY
2338 /* initialize type of slippery elements */
2339 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2341 if (!IS_CUSTOM_ELEMENT(i))
2343 /* default: elements slip down either to the left or right randomly */
2344 element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
2346 /* SP style elements prefer to slip down on the left side */
2347 if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
2348 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
2350 /* BD style elements prefer to slip down on the left side */
2351 if (game.emulation == EMU_BOULDERDASH)
2352 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
2357 /* initialize explosion and ignition delay */
2358 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2360 if (!IS_CUSTOM_ELEMENT(i))
2363 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
2364 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
2365 game.emulation == EMU_SUPAPLEX ? 3 : 2);
2366 int last_phase = (num_phase + 1) * delay;
2367 int half_phase = (num_phase / 2) * delay;
2369 element_info[i].explosion_delay = last_phase - 1;
2370 element_info[i].ignition_delay = half_phase;
2372 if (i == EL_BLACK_ORB)
2373 element_info[i].ignition_delay = 1;
2377 if (element_info[i].explosion_delay < 1) /* !!! check again !!! */
2378 element_info[i].explosion_delay = 1;
2380 if (element_info[i].ignition_delay < 1) /* !!! check again !!! */
2381 element_info[i].ignition_delay = 1;
2385 /* correct non-moving belts to start moving left */
2386 for (i = 0; i < NUM_BELTS; i++)
2387 if (game.belt_dir[i] == MV_NONE)
2388 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
2390 /* check if any connected player was not found in playfield */
2391 for (i = 0; i < MAX_PLAYERS; i++)
2393 struct PlayerInfo *player = &stored_player[i];
2395 if (player->connected && !player->present)
2397 for (j = 0; j < MAX_PLAYERS; j++)
2399 struct PlayerInfo *some_player = &stored_player[j];
2400 int jx = some_player->jx, jy = some_player->jy;
2402 /* assign first free player found that is present in the playfield */
2403 if (some_player->present && !some_player->connected)
2405 player->present = TRUE;
2406 player->active = TRUE;
2408 some_player->present = FALSE;
2409 some_player->active = FALSE;
2411 player->artwork_element = some_player->artwork_element;
2413 player->block_last_field = some_player->block_last_field;
2414 player->block_delay_adjustment = some_player->block_delay_adjustment;
2416 StorePlayer[jx][jy] = player->element_nr;
2417 player->jx = player->last_jx = jx;
2418 player->jy = player->last_jy = jy;
2428 /* when playing a tape, eliminate all players who do not participate */
2430 for (i = 0; i < MAX_PLAYERS; i++)
2432 if (stored_player[i].active && !tape.player_participates[i])
2434 struct PlayerInfo *player = &stored_player[i];
2435 int jx = player->jx, jy = player->jy;
2437 player->active = FALSE;
2438 StorePlayer[jx][jy] = 0;
2439 Feld[jx][jy] = EL_EMPTY;
2443 else if (!options.network && !setup.team_mode) /* && !tape.playing */
2445 /* when in single player mode, eliminate all but the first active player */
2447 for (i = 0; i < MAX_PLAYERS; i++)
2449 if (stored_player[i].active)
2451 for (j = i + 1; j < MAX_PLAYERS; j++)
2453 if (stored_player[j].active)
2455 struct PlayerInfo *player = &stored_player[j];
2456 int jx = player->jx, jy = player->jy;
2458 player->active = FALSE;
2459 player->present = FALSE;
2461 StorePlayer[jx][jy] = 0;
2462 Feld[jx][jy] = EL_EMPTY;
2469 /* when recording the game, store which players take part in the game */
2472 for (i = 0; i < MAX_PLAYERS; i++)
2473 if (stored_player[i].active)
2474 tape.player_participates[i] = TRUE;
2479 for (i = 0; i < MAX_PLAYERS; i++)
2481 struct PlayerInfo *player = &stored_player[i];
2483 printf("Player %d: present == %d, connected == %d, active == %d.\n",
2488 if (local_player == player)
2489 printf("Player %d is local player.\n", i+1);
2493 if (BorderElement == EL_EMPTY)
2496 SBX_Right = lev_fieldx - SCR_FIELDX;
2498 SBY_Lower = lev_fieldy - SCR_FIELDY;
2503 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
2505 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
2508 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
2509 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
2511 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
2512 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
2514 /* if local player not found, look for custom element that might create
2515 the player (make some assumptions about the right custom element) */
2516 if (!local_player->present)
2518 int start_x = 0, start_y = 0;
2519 int found_rating = 0;
2520 int found_element = EL_UNDEFINED;
2521 int player_nr = local_player->index_nr;
2523 SCAN_PLAYFIELD(x, y)
2525 int element = Feld[x][y];
2530 if (level.use_start_element[player_nr] &&
2531 level.start_element[player_nr] == element &&
2538 found_element = element;
2541 if (!IS_CUSTOM_ELEMENT(element))
2544 if (CAN_CHANGE(element))
2546 for (i = 0; i < element_info[element].num_change_pages; i++)
2548 /* check for player created from custom element as single target */
2549 content = element_info[element].change_page[i].target_element;
2550 is_player = ELEM_IS_PLAYER(content);
2552 if (is_player && (found_rating < 3 || element < found_element))
2558 found_element = element;
2563 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
2565 /* check for player created from custom element as explosion content */
2566 content = element_info[element].content.e[xx][yy];
2567 is_player = ELEM_IS_PLAYER(content);
2569 if (is_player && (found_rating < 2 || element < found_element))
2571 start_x = x + xx - 1;
2572 start_y = y + yy - 1;
2575 found_element = element;
2578 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 extended target */
2585 element_info[element].change_page[i].target_content.e[xx][yy];
2587 is_player = ELEM_IS_PLAYER(content);
2589 if (is_player && (found_rating < 1 || element < found_element))
2591 start_x = x + xx - 1;
2592 start_y = y + yy - 1;
2595 found_element = element;
2601 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
2602 start_x > SBX_Right + MIDPOSX ? SBX_Right :
2605 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
2606 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
2611 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2612 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2613 local_player->jx - MIDPOSX);
2615 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2616 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2617 local_player->jy - MIDPOSY);
2622 if (!game.restart_level)
2623 CloseDoor(DOOR_CLOSE_1);
2626 FadeOut(REDRAW_FIELD);
2628 /* !!! FIX THIS (START) !!! */
2629 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2631 InitGameEngine_EM();
2633 /* blit playfield from scroll buffer to normal back buffer for fading in */
2634 BlitScreenToBitmap_EM(backbuffer);
2641 /* after drawing the level, correct some elements */
2642 if (game.timegate_time_left == 0)
2643 CloseAllOpenTimegates();
2645 /* blit playfield from scroll buffer to normal back buffer for fading in */
2646 if (setup.soft_scrolling)
2647 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
2649 redraw_mask |= REDRAW_FROM_BACKBUFFER;
2651 /* !!! FIX THIS (END) !!! */
2654 FadeIn(REDRAW_FIELD);
2658 if (!game.restart_level)
2660 /* copy default game door content to main double buffer */
2661 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2662 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2665 SetPanelBackground();
2666 SetDrawBackgroundMask(REDRAW_DOOR_1);
2668 DrawGameDoorValues();
2670 if (!game.restart_level)
2674 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
2675 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
2676 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
2680 /* copy actual game door content to door double buffer for OpenDoor() */
2681 BlitBitmap(drawto, bitmap_db_door,
2682 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
2684 OpenDoor(DOOR_OPEN_ALL);
2686 PlaySound(SND_GAME_STARTING);
2688 if (setup.sound_music)
2691 KeyboardAutoRepeatOffUnlessAutoplay();
2695 for (i = 0; i < MAX_PLAYERS; i++)
2696 printf("Player %d %sactive.\n",
2697 i + 1, (stored_player[i].active ? "" : "not "));
2708 game.restart_level = FALSE;
2711 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
2713 /* this is used for non-R'n'D game engines to update certain engine values */
2715 /* needed to determine if sounds are played within the visible screen area */
2716 scroll_x = actual_scroll_x;
2717 scroll_y = actual_scroll_y;
2720 void InitMovDir(int x, int y)
2722 int i, element = Feld[x][y];
2723 static int xy[4][2] =
2730 static int direction[3][4] =
2732 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
2733 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
2734 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
2743 Feld[x][y] = EL_BUG;
2744 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
2747 case EL_SPACESHIP_RIGHT:
2748 case EL_SPACESHIP_UP:
2749 case EL_SPACESHIP_LEFT:
2750 case EL_SPACESHIP_DOWN:
2751 Feld[x][y] = EL_SPACESHIP;
2752 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
2755 case EL_BD_BUTTERFLY_RIGHT:
2756 case EL_BD_BUTTERFLY_UP:
2757 case EL_BD_BUTTERFLY_LEFT:
2758 case EL_BD_BUTTERFLY_DOWN:
2759 Feld[x][y] = EL_BD_BUTTERFLY;
2760 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
2763 case EL_BD_FIREFLY_RIGHT:
2764 case EL_BD_FIREFLY_UP:
2765 case EL_BD_FIREFLY_LEFT:
2766 case EL_BD_FIREFLY_DOWN:
2767 Feld[x][y] = EL_BD_FIREFLY;
2768 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
2771 case EL_PACMAN_RIGHT:
2773 case EL_PACMAN_LEFT:
2774 case EL_PACMAN_DOWN:
2775 Feld[x][y] = EL_PACMAN;
2776 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
2779 case EL_YAMYAM_LEFT:
2780 case EL_YAMYAM_RIGHT:
2782 case EL_YAMYAM_DOWN:
2783 Feld[x][y] = EL_YAMYAM;
2784 MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
2787 case EL_SP_SNIKSNAK:
2788 MovDir[x][y] = MV_UP;
2791 case EL_SP_ELECTRON:
2792 MovDir[x][y] = MV_LEFT;
2799 Feld[x][y] = EL_MOLE;
2800 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
2804 if (IS_CUSTOM_ELEMENT(element))
2806 struct ElementInfo *ei = &element_info[element];
2807 int move_direction_initial = ei->move_direction_initial;
2808 int move_pattern = ei->move_pattern;
2810 if (move_direction_initial == MV_START_PREVIOUS)
2812 if (MovDir[x][y] != MV_NONE)
2815 move_direction_initial = MV_START_AUTOMATIC;
2818 if (move_direction_initial == MV_START_RANDOM)
2819 MovDir[x][y] = 1 << RND(4);
2820 else if (move_direction_initial & MV_ANY_DIRECTION)
2821 MovDir[x][y] = move_direction_initial;
2822 else if (move_pattern == MV_ALL_DIRECTIONS ||
2823 move_pattern == MV_TURNING_LEFT ||
2824 move_pattern == MV_TURNING_RIGHT ||
2825 move_pattern == MV_TURNING_LEFT_RIGHT ||
2826 move_pattern == MV_TURNING_RIGHT_LEFT ||
2827 move_pattern == MV_TURNING_RANDOM)
2828 MovDir[x][y] = 1 << RND(4);
2829 else if (move_pattern == MV_HORIZONTAL)
2830 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
2831 else if (move_pattern == MV_VERTICAL)
2832 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
2833 else if (move_pattern & MV_ANY_DIRECTION)
2834 MovDir[x][y] = element_info[element].move_pattern;
2835 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
2836 move_pattern == MV_ALONG_RIGHT_SIDE)
2838 /* use random direction as default start direction */
2839 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2840 MovDir[x][y] = 1 << RND(4);
2842 for (i = 0; i < NUM_DIRECTIONS; i++)
2844 int x1 = x + xy[i][0];
2845 int y1 = y + xy[i][1];
2847 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2849 if (move_pattern == MV_ALONG_RIGHT_SIDE)
2850 MovDir[x][y] = direction[0][i];
2852 MovDir[x][y] = direction[1][i];
2861 MovDir[x][y] = 1 << RND(4);
2863 if (element != EL_BUG &&
2864 element != EL_SPACESHIP &&
2865 element != EL_BD_BUTTERFLY &&
2866 element != EL_BD_FIREFLY)
2869 for (i = 0; i < NUM_DIRECTIONS; i++)
2871 int x1 = x + xy[i][0];
2872 int y1 = y + xy[i][1];
2874 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2876 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
2878 MovDir[x][y] = direction[0][i];
2881 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
2882 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
2884 MovDir[x][y] = direction[1][i];
2893 GfxDir[x][y] = MovDir[x][y];
2896 void InitAmoebaNr(int x, int y)
2899 int group_nr = AmoebeNachbarNr(x, y);
2903 for (i = 1; i < MAX_NUM_AMOEBA; i++)
2905 if (AmoebaCnt[i] == 0)
2913 AmoebaNr[x][y] = group_nr;
2914 AmoebaCnt[group_nr]++;
2915 AmoebaCnt2[group_nr]++;
2918 static void PlayerWins(struct PlayerInfo *player)
2920 player->LevelSolved = TRUE;
2921 player->GameOver = TRUE;
2923 player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2924 level.native_em_level->lev->score : player->score);
2929 static int time, time_final;
2930 static int score, score_final;
2931 static int game_over_delay_1 = 0;
2932 static int game_over_delay_2 = 0;
2933 int game_over_delay_value_1 = 50;
2934 int game_over_delay_value_2 = 50;
2936 if (!local_player->LevelSolved_GameWon)
2940 /* do not start end game actions before the player stops moving (to exit) */
2941 if (local_player->MovPos)
2944 local_player->LevelSolved_GameWon = TRUE;
2945 local_player->LevelSolved_SaveTape = tape.recording;
2946 local_player->LevelSolved_SaveScore = !tape.playing;
2948 if (tape.auto_play) /* tape might already be stopped here */
2949 tape.auto_play_level_solved = TRUE;
2955 game_over_delay_1 = game_over_delay_value_1;
2956 game_over_delay_2 = game_over_delay_value_2;
2958 time = time_final = (level.time == 0 ? TimePlayed : TimeLeft);
2959 score = score_final = local_player->score_final;
2964 score_final += TimeLeft * level.score[SC_TIME_BONUS];
2966 else if (level.time == 0 && TimePlayed < 999)
2969 score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
2972 local_player->score_final = score_final;
2974 if (level_editor_test_game)
2977 score = score_final;
2979 DrawGameValue_Time(time);
2980 DrawGameValue_Score(score);
2983 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
2985 if (ExitX >= 0 && ExitY >= 0) /* local player has left the level */
2987 /* close exit door after last player */
2988 if ((AllPlayersGone &&
2989 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
2990 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
2991 Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
2992 Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
2993 Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
2995 int element = Feld[ExitX][ExitY];
2998 if (element == EL_EM_EXIT_OPEN ||
2999 element == EL_EM_STEEL_EXIT_OPEN)
3006 Feld[ExitX][ExitY] =
3007 (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
3008 element == EL_EM_EXIT_OPEN ? EL_EM_EXIT_CLOSING :
3009 element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING:
3010 element == EL_STEEL_EXIT_OPEN ? EL_STEEL_EXIT_CLOSING:
3011 EL_EM_STEEL_EXIT_CLOSING);
3013 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
3017 /* player disappears */
3018 DrawLevelField(ExitX, ExitY);
3021 for (i = 0; i < MAX_PLAYERS; i++)
3023 struct PlayerInfo *player = &stored_player[i];
3025 if (player->present)
3027 RemovePlayer(player);
3029 /* player disappears */
3030 DrawLevelField(player->jx, player->jy);
3035 PlaySound(SND_GAME_WINNING);
3038 if (game_over_delay_1 > 0)
3040 game_over_delay_1--;
3045 if (time != time_final)
3047 int time_to_go = ABS(time_final - time);
3048 int time_count_dir = (time < time_final ? +1 : -1);
3049 int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
3051 time += time_count_steps * time_count_dir;
3052 score += time_count_steps * level.score[SC_TIME_BONUS];
3054 DrawGameValue_Time(time);
3055 DrawGameValue_Score(score);
3057 if (time == time_final)
3058 StopSound(SND_GAME_LEVELTIME_BONUS);
3059 else if (setup.sound_loops)
3060 PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
3062 PlaySound(SND_GAME_LEVELTIME_BONUS);
3067 local_player->LevelSolved_PanelOff = TRUE;
3069 if (game_over_delay_2 > 0)
3071 game_over_delay_2--;
3084 boolean raise_level = FALSE;
3086 local_player->LevelSolved_GameEnd = TRUE;
3088 CloseDoor(DOOR_CLOSE_1);
3090 if (local_player->LevelSolved_SaveTape)
3097 SaveTapeChecked(tape.level_nr); /* ask to save tape */
3099 SaveTape(tape.level_nr); /* ask to save tape */
3103 if (level_editor_test_game)
3105 game_status = GAME_MODE_MAIN;
3112 if (!local_player->LevelSolved_SaveScore)
3114 FadeOut(REDRAW_FIELD);
3116 game_status = GAME_MODE_MAIN;
3118 DrawAndFadeInMainMenu(REDRAW_FIELD);
3123 if (level_nr == leveldir_current->handicap_level)
3125 leveldir_current->handicap_level++;
3126 SaveLevelSetup_SeriesInfo();
3129 if (level_nr < leveldir_current->last_level)
3130 raise_level = TRUE; /* advance to next level */
3132 if ((hi_pos = NewHiScore()) >= 0)
3134 game_status = GAME_MODE_SCORES;
3136 DrawHallOfFame(hi_pos);
3146 FadeOut(REDRAW_FIELD);
3148 game_status = GAME_MODE_MAIN;
3156 DrawAndFadeInMainMenu(REDRAW_FIELD);
3165 LoadScore(level_nr);
3167 if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
3168 local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score)
3171 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
3173 if (local_player->score_final > highscore[k].Score)
3175 /* player has made it to the hall of fame */
3177 if (k < MAX_SCORE_ENTRIES - 1)
3179 int m = MAX_SCORE_ENTRIES - 1;
3182 for (l = k; l < MAX_SCORE_ENTRIES; l++)
3183 if (strEqual(setup.player_name, highscore[l].Name))
3185 if (m == k) /* player's new highscore overwrites his old one */
3189 for (l = m; l > k; l--)
3191 strcpy(highscore[l].Name, highscore[l - 1].Name);
3192 highscore[l].Score = highscore[l - 1].Score;
3199 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
3200 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
3201 highscore[k].Score = local_player->score_final;
3207 else if (!strncmp(setup.player_name, highscore[k].Name,
3208 MAX_PLAYER_NAME_LEN))
3209 break; /* player already there with a higher score */
3215 SaveScore(level_nr);
3220 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
3222 int element = Feld[x][y];
3223 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3224 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
3225 int horiz_move = (dx != 0);
3226 int sign = (horiz_move ? dx : dy);
3227 int step = sign * element_info[element].move_stepsize;
3229 /* special values for move stepsize for spring and things on conveyor belt */
3232 if (CAN_FALL(element) &&
3233 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
3234 step = sign * MOVE_STEPSIZE_NORMAL / 2;
3235 else if (element == EL_SPRING)
3236 step = sign * MOVE_STEPSIZE_NORMAL * 2;
3242 inline static int getElementMoveStepsize(int x, int y)
3244 return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
3247 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
3249 if (player->GfxAction != action || player->GfxDir != dir)
3252 printf("Player frame reset! (%d => %d, %d => %d)\n",
3253 player->GfxAction, action, player->GfxDir, dir);
3256 player->GfxAction = action;
3257 player->GfxDir = dir;
3259 player->StepFrame = 0;
3263 #if USE_GFX_RESET_GFX_ANIMATION
3264 static void ResetGfxFrame(int x, int y, boolean redraw)
3266 int element = Feld[x][y];
3267 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3268 int last_gfx_frame = GfxFrame[x][y];
3270 if (graphic_info[graphic].anim_global_sync)
3271 GfxFrame[x][y] = FrameCounter;
3272 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
3273 GfxFrame[x][y] = CustomValue[x][y];
3274 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
3275 GfxFrame[x][y] = element_info[element].collect_score;
3276 else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
3277 GfxFrame[x][y] = ChangeDelay[x][y];
3279 if (redraw && GfxFrame[x][y] != last_gfx_frame)
3280 DrawLevelGraphicAnimation(x, y, graphic);
3284 static void ResetGfxAnimation(int x, int y)
3286 GfxAction[x][y] = ACTION_DEFAULT;
3287 GfxDir[x][y] = MovDir[x][y];
3290 #if USE_GFX_RESET_GFX_ANIMATION
3291 ResetGfxFrame(x, y, FALSE);
3295 static void ResetRandomAnimationValue(int x, int y)
3297 GfxRandom[x][y] = INIT_GFX_RANDOM();
3300 void InitMovingField(int x, int y, int direction)
3302 int element = Feld[x][y];
3303 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3304 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
3307 boolean is_moving_before, is_moving_after;
3309 boolean continues_moving = (WasJustMoving[x][y] && direction == MovDir[x][y]);
3312 /* check if element was/is moving or being moved before/after mode change */
3315 is_moving_before = (WasJustMoving[x][y] != 0);
3317 /* (!!! this does not work -- WasJustMoving is NOT a boolean value !!!) */
3318 is_moving_before = WasJustMoving[x][y];
3321 is_moving_before = (getElementMoveStepsizeExt(x, y, MovDir[x][y]) != 0);
3323 is_moving_after = (getElementMoveStepsizeExt(x, y, direction) != 0);
3325 /* reset animation only for moving elements which change direction of moving
3326 or which just started or stopped moving
3327 (else CEs with property "can move" / "not moving" are reset each frame) */
3328 #if USE_GFX_RESET_ONLY_WHEN_MOVING
3330 if (is_moving_before != is_moving_after ||
3331 direction != MovDir[x][y])
3332 ResetGfxAnimation(x, y);
3334 if ((is_moving_before || is_moving_after) && !continues_moving)
3335 ResetGfxAnimation(x, y);
3338 if (!continues_moving)
3339 ResetGfxAnimation(x, y);
3342 MovDir[x][y] = direction;
3343 GfxDir[x][y] = direction;
3345 #if USE_GFX_RESET_ONLY_WHEN_MOVING
3346 GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
3347 direction == MV_DOWN && CAN_FALL(element) ?
3348 ACTION_FALLING : ACTION_MOVING);
3350 GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
3351 ACTION_FALLING : ACTION_MOVING);
3354 /* this is needed for CEs with property "can move" / "not moving" */
3356 if (is_moving_after)
3358 if (Feld[newx][newy] == EL_EMPTY)
3359 Feld[newx][newy] = EL_BLOCKED;
3361 MovDir[newx][newy] = MovDir[x][y];
3363 #if USE_NEW_CUSTOM_VALUE
3364 CustomValue[newx][newy] = CustomValue[x][y];
3367 GfxFrame[newx][newy] = GfxFrame[x][y];
3368 GfxRandom[newx][newy] = GfxRandom[x][y];
3369 GfxAction[newx][newy] = GfxAction[x][y];
3370 GfxDir[newx][newy] = GfxDir[x][y];
3374 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
3376 int direction = MovDir[x][y];
3377 int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
3378 int newy = y + (direction & MV_UP ? -1 : direction & MV_DOWN ? +1 : 0);
3384 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
3386 int oldx = x, oldy = y;
3387 int direction = MovDir[x][y];
3389 if (direction == MV_LEFT)
3391 else if (direction == MV_RIGHT)
3393 else if (direction == MV_UP)
3395 else if (direction == MV_DOWN)
3398 *comes_from_x = oldx;
3399 *comes_from_y = oldy;
3402 int MovingOrBlocked2Element(int x, int y)
3404 int element = Feld[x][y];
3406 if (element == EL_BLOCKED)
3410 Blocked2Moving(x, y, &oldx, &oldy);
3411 return Feld[oldx][oldy];
3417 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
3419 /* like MovingOrBlocked2Element(), but if element is moving
3420 and (x,y) is the field the moving element is just leaving,
3421 return EL_BLOCKED instead of the element value */
3422 int element = Feld[x][y];
3424 if (IS_MOVING(x, y))
3426 if (element == EL_BLOCKED)
3430 Blocked2Moving(x, y, &oldx, &oldy);
3431 return Feld[oldx][oldy];
3440 static void RemoveField(int x, int y)
3442 Feld[x][y] = EL_EMPTY;
3448 #if USE_NEW_CUSTOM_VALUE
3449 CustomValue[x][y] = 0;
3453 ChangeDelay[x][y] = 0;
3454 ChangePage[x][y] = -1;
3455 Pushed[x][y] = FALSE;
3458 ExplodeField[x][y] = EX_TYPE_NONE;
3461 GfxElement[x][y] = EL_UNDEFINED;
3462 GfxAction[x][y] = ACTION_DEFAULT;
3463 GfxDir[x][y] = MV_NONE;
3466 void RemoveMovingField(int x, int y)
3468 int oldx = x, oldy = y, newx = x, newy = y;
3469 int element = Feld[x][y];
3470 int next_element = EL_UNDEFINED;
3472 if (element != EL_BLOCKED && !IS_MOVING(x, y))
3475 if (IS_MOVING(x, y))
3477 Moving2Blocked(x, y, &newx, &newy);
3479 if (Feld[newx][newy] != EL_BLOCKED)
3481 /* element is moving, but target field is not free (blocked), but
3482 already occupied by something different (example: acid pool);
3483 in this case, only remove the moving field, but not the target */
3485 RemoveField(oldx, oldy);
3487 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
3489 DrawLevelField(oldx, oldy);
3494 else if (element == EL_BLOCKED)
3496 Blocked2Moving(x, y, &oldx, &oldy);
3497 if (!IS_MOVING(oldx, oldy))
3501 if (element == EL_BLOCKED &&
3502 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
3503 Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
3504 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
3505 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
3506 Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
3507 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
3508 next_element = get_next_element(Feld[oldx][oldy]);
3510 RemoveField(oldx, oldy);
3511 RemoveField(newx, newy);
3513 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
3515 if (next_element != EL_UNDEFINED)
3516 Feld[oldx][oldy] = next_element;
3518 DrawLevelField(oldx, oldy);
3519 DrawLevelField(newx, newy);
3522 void DrawDynamite(int x, int y)
3524 int sx = SCREENX(x), sy = SCREENY(y);
3525 int graphic = el2img(Feld[x][y]);
3528 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
3531 if (IS_WALKABLE_INSIDE(Back[x][y]))
3535 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
3536 else if (Store[x][y])
3537 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
3539 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
3541 if (Back[x][y] || Store[x][y])
3542 DrawGraphicThruMask(sx, sy, graphic, frame);
3544 DrawGraphic(sx, sy, graphic, frame);
3547 void CheckDynamite(int x, int y)
3549 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
3553 if (MovDelay[x][y] != 0)
3556 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
3562 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
3567 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
3569 boolean num_checked_players = 0;
3572 for (i = 0; i < MAX_PLAYERS; i++)
3574 if (stored_player[i].active)
3576 int sx = stored_player[i].jx;
3577 int sy = stored_player[i].jy;
3579 if (num_checked_players == 0)
3586 *sx1 = MIN(*sx1, sx);
3587 *sy1 = MIN(*sy1, sy);
3588 *sx2 = MAX(*sx2, sx);
3589 *sy2 = MAX(*sy2, sy);
3592 num_checked_players++;
3597 static boolean checkIfAllPlayersFitToScreen_RND()
3599 int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
3601 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
3603 return (sx2 - sx1 < SCR_FIELDX &&
3604 sy2 - sy1 < SCR_FIELDY);
3607 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
3609 int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
3611 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
3613 *sx = (sx1 + sx2) / 2;
3614 *sy = (sy1 + sy2) / 2;
3617 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
3618 boolean center_screen, boolean quick_relocation)
3620 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3621 boolean no_delay = (tape.warp_forward);
3622 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
3623 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
3625 if (quick_relocation)
3627 int offset = (setup.scroll_delay ? 3 : 0);
3629 if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
3633 scroll_x = (x < SBX_Left + MIDPOSX ? SBX_Left :
3634 x > SBX_Right + MIDPOSX ? SBX_Right :
3637 scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
3638 y > SBY_Lower + MIDPOSY ? SBY_Lower :
3643 /* quick relocation (without scrolling), but do not center screen */
3645 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
3646 old_x > SBX_Right + MIDPOSX ? SBX_Right :
3649 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
3650 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
3653 int offset_x = x + (scroll_x - center_scroll_x);
3654 int offset_y = y + (scroll_y - center_scroll_y);
3656 scroll_x = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
3657 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
3658 offset_x - MIDPOSX);
3660 scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
3661 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
3662 offset_y - MIDPOSY);
3667 if ((move_dir == MV_LEFT && scroll_x > x - MIDPOSX + offset) ||
3668 (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
3669 scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
3671 if ((move_dir == MV_UP && scroll_y > y - MIDPOSY + offset) ||
3672 (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
3673 scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
3675 /* don't scroll over playfield boundaries */
3676 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
3677 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
3679 /* don't scroll over playfield boundaries */
3680 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
3681 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
3684 RedrawPlayfield(TRUE, 0,0,0,0);
3688 int scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left :
3689 x > SBX_Right + MIDPOSX ? SBX_Right :
3692 int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
3693 y > SBY_Lower + MIDPOSY ? SBY_Lower :
3696 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
3698 while (scroll_x != scroll_xx || scroll_y != scroll_yy)
3701 int fx = FX, fy = FY;
3703 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
3704 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
3706 if (dx == 0 && dy == 0) /* no scrolling needed at all */
3712 fx += dx * TILEX / 2;
3713 fy += dy * TILEY / 2;
3715 ScrollLevel(dx, dy);
3718 /* scroll in two steps of half tile size to make things smoother */
3719 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
3721 Delay(wait_delay_value);
3723 /* scroll second step to align at full tile size */
3725 Delay(wait_delay_value);
3730 Delay(wait_delay_value);
3734 void RelocatePlayer(int jx, int jy, int el_player_raw)
3736 int el_player = GET_PLAYER_ELEMENT(el_player_raw);
3737 int player_nr = GET_PLAYER_NR(el_player);
3738 struct PlayerInfo *player = &stored_player[player_nr];
3739 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3740 boolean no_delay = (tape.warp_forward);
3741 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
3742 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
3743 int old_jx = player->jx;
3744 int old_jy = player->jy;
3745 int old_element = Feld[old_jx][old_jy];
3746 int element = Feld[jx][jy];
3747 boolean player_relocated = (old_jx != jx || old_jy != jy);
3749 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
3750 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
3751 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
3752 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
3753 int leave_side_horiz = move_dir_horiz;
3754 int leave_side_vert = move_dir_vert;
3755 int enter_side = enter_side_horiz | enter_side_vert;
3756 int leave_side = leave_side_horiz | leave_side_vert;
3758 if (player->GameOver) /* do not reanimate dead player */
3761 if (!player_relocated) /* no need to relocate the player */
3764 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
3766 RemoveField(jx, jy); /* temporarily remove newly placed player */
3767 DrawLevelField(jx, jy);
3770 if (player->present)
3772 while (player->MovPos)
3774 ScrollPlayer(player, SCROLL_GO_ON);
3775 ScrollScreen(NULL, SCROLL_GO_ON);
3777 AdvanceFrameAndPlayerCounters(player->index_nr);
3782 Delay(wait_delay_value);
3785 DrawPlayer(player); /* needed here only to cleanup last field */
3786 DrawLevelField(player->jx, player->jy); /* remove player graphic */
3788 player->is_moving = FALSE;
3791 if (IS_CUSTOM_ELEMENT(old_element))
3792 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
3794 player->index_bit, leave_side);
3796 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
3798 player->index_bit, leave_side);
3800 Feld[jx][jy] = el_player;
3801 InitPlayerField(jx, jy, el_player, TRUE);
3803 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
3805 Feld[jx][jy] = element;
3806 InitField(jx, jy, FALSE);
3809 /* only visually relocate centered player */
3810 DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
3811 FALSE, level.instant_relocation);
3813 TestIfPlayerTouchesBadThing(jx, jy);
3814 TestIfPlayerTouchesCustomElement(jx, jy);
3816 if (IS_CUSTOM_ELEMENT(element))
3817 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
3818 player->index_bit, enter_side);
3820 CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
3821 player->index_bit, enter_side);
3824 void Explode(int ex, int ey, int phase, int mode)
3830 /* !!! eliminate this variable !!! */
3831 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
3833 if (game.explosions_delayed)
3835 ExplodeField[ex][ey] = mode;
3839 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
3841 int center_element = Feld[ex][ey];
3842 int artwork_element, explosion_element; /* set these values later */
3845 /* --- This is only really needed (and now handled) in "Impact()". --- */
3846 /* do not explode moving elements that left the explode field in time */
3847 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
3848 center_element == EL_EMPTY &&
3849 (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
3854 /* !!! at this place, the center element may be EL_BLOCKED !!! */
3855 if (mode == EX_TYPE_NORMAL ||
3856 mode == EX_TYPE_CENTER ||
3857 mode == EX_TYPE_CROSS)
3858 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
3861 /* remove things displayed in background while burning dynamite */
3862 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
3865 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
3867 /* put moving element to center field (and let it explode there) */
3868 center_element = MovingOrBlocked2Element(ex, ey);
3869 RemoveMovingField(ex, ey);
3870 Feld[ex][ey] = center_element;
3873 /* now "center_element" is finally determined -- set related values now */
3874 artwork_element = center_element; /* for custom player artwork */
3875 explosion_element = center_element; /* for custom player artwork */
3877 if (IS_PLAYER(ex, ey))
3879 int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
3881 artwork_element = stored_player[player_nr].artwork_element;
3883 if (level.use_explosion_element[player_nr])
3885 explosion_element = level.explosion_element[player_nr];
3886 artwork_element = explosion_element;
3891 if (mode == EX_TYPE_NORMAL ||
3892 mode == EX_TYPE_CENTER ||
3893 mode == EX_TYPE_CROSS)
3894 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
3897 last_phase = element_info[explosion_element].explosion_delay + 1;
3899 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
3901 int xx = x - ex + 1;
3902 int yy = y - ey + 1;
3905 if (!IN_LEV_FIELD(x, y) ||
3906 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
3907 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
3910 element = Feld[x][y];
3912 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
3914 element = MovingOrBlocked2Element(x, y);
3916 if (!IS_EXPLOSION_PROOF(element))
3917 RemoveMovingField(x, y);
3920 /* indestructible elements can only explode in center (but not flames) */
3921 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
3922 mode == EX_TYPE_BORDER)) ||
3923 element == EL_FLAMES)
3926 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
3927 behaviour, for example when touching a yamyam that explodes to rocks
3928 with active deadly shield, a rock is created under the player !!! */
3929 /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
3931 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
3932 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
3933 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
3935 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
3938 if (IS_ACTIVE_BOMB(element))
3940 /* re-activate things under the bomb like gate or penguin */
3941 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
3948 /* save walkable background elements while explosion on same tile */
3949 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
3950 (x != ex || y != ey || mode == EX_TYPE_BORDER))
3951 Back[x][y] = element;
3953 /* ignite explodable elements reached by other explosion */
3954 if (element == EL_EXPLOSION)
3955 element = Store2[x][y];
3957 if (AmoebaNr[x][y] &&
3958 (element == EL_AMOEBA_FULL ||
3959 element == EL_BD_AMOEBA ||
3960 element == EL_AMOEBA_GROWING))
3962 AmoebaCnt[AmoebaNr[x][y]]--;
3963 AmoebaCnt2[AmoebaNr[x][y]]--;
3968 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
3970 int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
3972 Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
3974 if (PLAYERINFO(ex, ey)->use_murphy)
3975 Store[x][y] = EL_EMPTY;
3978 /* !!! check this case -- currently needed for rnd_rado_negundo_v,
3979 !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
3980 else if (ELEM_IS_PLAYER(center_element))
3981 Store[x][y] = EL_EMPTY;
3982 else if (center_element == EL_YAMYAM)
3983 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
3984 else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
3985 Store[x][y] = element_info[center_element].content.e[xx][yy];
3987 /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
3988 (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
3989 otherwise) -- FIX THIS !!! */
3990 else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
3991 Store[x][y] = element_info[element].content.e[1][1];
3993 else if (!CAN_EXPLODE(element))
3994 Store[x][y] = element_info[element].content.e[1][1];
3997 Store[x][y] = EL_EMPTY;
3999 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
4000 center_element == EL_AMOEBA_TO_DIAMOND)
4001 Store2[x][y] = element;
4003 Feld[x][y] = EL_EXPLOSION;
4004 GfxElement[x][y] = artwork_element;
4006 ExplodePhase[x][y] = 1;
4007 ExplodeDelay[x][y] = last_phase;
4012 if (center_element == EL_YAMYAM)
4013 game.yamyam_content_nr =
4014 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
4026 GfxFrame[x][y] = 0; /* restart explosion animation */
4028 last_phase = ExplodeDelay[x][y];
4030 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
4034 /* activate this even in non-DEBUG version until cause for crash in
4035 getGraphicAnimationFrame() (see below) is found and eliminated */
4041 /* this can happen if the player leaves an explosion just in time */
4042 if (GfxElement[x][y] == EL_UNDEFINED)
4043 GfxElement[x][y] = EL_EMPTY;
4045 if (GfxElement[x][y] == EL_UNDEFINED)
4048 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
4049 printf("Explode(): This should never happen!\n");
4052 GfxElement[x][y] = EL_EMPTY;
4058 border_element = Store2[x][y];
4059 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
4060 border_element = StorePlayer[x][y];
4062 if (phase == element_info[border_element].ignition_delay ||
4063 phase == last_phase)
4065 boolean border_explosion = FALSE;
4067 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
4068 !PLAYER_EXPLOSION_PROTECTED(x, y))
4070 KillPlayerUnlessExplosionProtected(x, y);
4071 border_explosion = TRUE;
4073 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
4075 Feld[x][y] = Store2[x][y];
4078 border_explosion = TRUE;
4080 else if (border_element == EL_AMOEBA_TO_DIAMOND)
4082 AmoebeUmwandeln(x, y);
4084 border_explosion = TRUE;
4087 /* if an element just explodes due to another explosion (chain-reaction),
4088 do not immediately end the new explosion when it was the last frame of
4089 the explosion (as it would be done in the following "if"-statement!) */
4090 if (border_explosion && phase == last_phase)
4094 if (phase == last_phase)
4098 element = Feld[x][y] = Store[x][y];
4099 Store[x][y] = Store2[x][y] = 0;
4100 GfxElement[x][y] = EL_UNDEFINED;
4102 /* player can escape from explosions and might therefore be still alive */
4103 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
4104 element <= EL_PLAYER_IS_EXPLODING_4)
4106 int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
4107 int explosion_element = EL_PLAYER_1 + player_nr;
4108 int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
4109 int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
4111 if (level.use_explosion_element[player_nr])
4112 explosion_element = level.explosion_element[player_nr];
4114 Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
4115 element_info[explosion_element].content.e[xx][yy]);
4118 /* restore probably existing indestructible background element */
4119 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
4120 element = Feld[x][y] = Back[x][y];
4123 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
4124 GfxDir[x][y] = MV_NONE;
4125 ChangeDelay[x][y] = 0;
4126 ChangePage[x][y] = -1;
4128 #if USE_NEW_CUSTOM_VALUE
4129 CustomValue[x][y] = 0;
4132 InitField_WithBug2(x, y, FALSE);
4134 DrawLevelField(x, y);
4136 TestIfElementTouchesCustomElement(x, y);
4138 if (GFX_CRUMBLED(element))
4139 DrawLevelFieldCrumbledSandNeighbours(x, y);
4141 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
4142 StorePlayer[x][y] = 0;
4144 if (ELEM_IS_PLAYER(element))
4145 RelocatePlayer(x, y, element);
4147 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
4149 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
4150 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4153 DrawLevelFieldCrumbledSand(x, y);
4155 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
4157 DrawLevelElement(x, y, Back[x][y]);
4158 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
4160 else if (IS_WALKABLE_UNDER(Back[x][y]))
4162 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
4163 DrawLevelElementThruMask(x, y, Back[x][y]);
4165 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
4166 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
4170 void DynaExplode(int ex, int ey)
4173 int dynabomb_element = Feld[ex][ey];
4174 int dynabomb_size = 1;
4175 boolean dynabomb_xl = FALSE;
4176 struct PlayerInfo *player;
4177 static int xy[4][2] =
4185 if (IS_ACTIVE_BOMB(dynabomb_element))
4187 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
4188 dynabomb_size = player->dynabomb_size;
4189 dynabomb_xl = player->dynabomb_xl;
4190 player->dynabombs_left++;
4193 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
4195 for (i = 0; i < NUM_DIRECTIONS; i++)
4197 for (j = 1; j <= dynabomb_size; j++)
4199 int x = ex + j * xy[i][0];
4200 int y = ey + j * xy[i][1];
4203 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
4206 element = Feld[x][y];
4208 /* do not restart explosions of fields with active bombs */
4209 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
4212 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
4214 if (element != EL_EMPTY && element != EL_EXPLOSION &&
4215 !IS_DIGGABLE(element) && !dynabomb_xl)
4221 void Bang(int x, int y)
4223 int element = MovingOrBlocked2Element(x, y);
4224 int explosion_type = EX_TYPE_NORMAL;
4226 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
4228 struct PlayerInfo *player = PLAYERINFO(x, y);
4230 element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
4231 player->element_nr);
4233 if (level.use_explosion_element[player->index_nr])
4235 int explosion_element = level.explosion_element[player->index_nr];
4237 if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
4238 explosion_type = EX_TYPE_CROSS;
4239 else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
4240 explosion_type = EX_TYPE_CENTER;
4248 case EL_BD_BUTTERFLY:
4251 case EL_DARK_YAMYAM:
4255 RaiseScoreElement(element);
4258 case EL_DYNABOMB_PLAYER_1_ACTIVE:
4259 case EL_DYNABOMB_PLAYER_2_ACTIVE:
4260 case EL_DYNABOMB_PLAYER_3_ACTIVE:
4261 case EL_DYNABOMB_PLAYER_4_ACTIVE:
4262 case EL_DYNABOMB_INCREASE_NUMBER:
4263 case EL_DYNABOMB_INCREASE_SIZE:
4264 case EL_DYNABOMB_INCREASE_POWER:
4265 explosion_type = EX_TYPE_DYNA;
4268 case EL_DC_LANDMINE:
4270 case EL_EM_EXIT_OPEN:
4271 case EL_EM_STEEL_EXIT_OPEN:
4273 explosion_type = EX_TYPE_CENTER;
4278 case EL_LAMP_ACTIVE:
4279 case EL_AMOEBA_TO_DIAMOND:
4280 if (!IS_PLAYER(x, y)) /* penguin and player may be at same field */
4281 explosion_type = EX_TYPE_CENTER;
4285 if (element_info[element].explosion_type == EXPLODES_CROSS)
4286 explosion_type = EX_TYPE_CROSS;
4287 else if (element_info[element].explosion_type == EXPLODES_1X1)
4288 explosion_type = EX_TYPE_CENTER;
4292 if (explosion_type == EX_TYPE_DYNA)
4295 Explode(x, y, EX_PHASE_START, explosion_type);
4297 CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
4300 void SplashAcid(int x, int y)
4302 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
4303 (!IN_LEV_FIELD(x - 1, y - 2) ||
4304 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
4305 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
4307 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
4308 (!IN_LEV_FIELD(x + 1, y - 2) ||
4309 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
4310 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
4312 PlayLevelSound(x, y, SND_ACID_SPLASHING);
4315 static void InitBeltMovement()
4317 static int belt_base_element[4] =
4319 EL_CONVEYOR_BELT_1_LEFT,
4320 EL_CONVEYOR_BELT_2_LEFT,
4321 EL_CONVEYOR_BELT_3_LEFT,
4322 EL_CONVEYOR_BELT_4_LEFT
4324 static int belt_base_active_element[4] =
4326 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
4327 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
4328 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
4329 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
4334 /* set frame order for belt animation graphic according to belt direction */
4335 for (i = 0; i < NUM_BELTS; i++)
4339 for (j = 0; j < NUM_BELT_PARTS; j++)
4341 int element = belt_base_active_element[belt_nr] + j;
4342 int graphic = el2img(element);
4344 if (game.belt_dir[i] == MV_LEFT)
4345 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
4347 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
4351 SCAN_PLAYFIELD(x, y)
4353 int element = Feld[x][y];
4355 for (i = 0; i < NUM_BELTS; i++)
4357 if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
4359 int e_belt_nr = getBeltNrFromBeltElement(element);
4362 if (e_belt_nr == belt_nr)
4364 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
4366 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
4373 static void ToggleBeltSwitch(int x, int y)
4375 static int belt_base_element[4] =
4377 EL_CONVEYOR_BELT_1_LEFT,
4378 EL_CONVEYOR_BELT_2_LEFT,
4379 EL_CONVEYOR_BELT_3_LEFT,
4380 EL_CONVEYOR_BELT_4_LEFT
4382 static int belt_base_active_element[4] =
4384 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
4385 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
4386 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
4387 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
4389 static int belt_base_switch_element[4] =
4391 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
4392 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
4393 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
4394 EL_CONVEYOR_BELT_4_SWITCH_LEFT
4396 static int belt_move_dir[4] =
4404 int element = Feld[x][y];
4405 int belt_nr = getBeltNrFromBeltSwitchElement(element);
4406 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
4407 int belt_dir = belt_move_dir[belt_dir_nr];
4410 if (!IS_BELT_SWITCH(element))
4413 game.belt_dir_nr[belt_nr] = belt_dir_nr;
4414 game.belt_dir[belt_nr] = belt_dir;
4416 if (belt_dir_nr == 3)
4419 /* set frame order for belt animation graphic according to belt direction */
4420 for (i = 0; i < NUM_BELT_PARTS; i++)
4422 int element = belt_base_active_element[belt_nr] + i;
4423 int graphic = el2img(element);
4425 if (belt_dir == MV_LEFT)
4426 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
4428 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
4431 SCAN_PLAYFIELD(xx, yy)
4433 int element = Feld[xx][yy];
4435 if (IS_BELT_SWITCH(element))
4437 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
4439 if (e_belt_nr == belt_nr)
4441 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
4442 DrawLevelField(xx, yy);
4445 else if (IS_BELT(element) && belt_dir != MV_NONE)
4447 int e_belt_nr = getBeltNrFromBeltElement(element);
4449 if (e_belt_nr == belt_nr)
4451 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
4453 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
4454 DrawLevelField(xx, yy);
4457 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
4459 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
4461 if (e_belt_nr == belt_nr)
4463 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
4465 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
4466 DrawLevelField(xx, yy);
4472 static void ToggleSwitchgateSwitch(int x, int y)
4476 game.switchgate_pos = !game.switchgate_pos;
4478 SCAN_PLAYFIELD(xx, yy)
4480 int element = Feld[xx][yy];
4482 #if !USE_BOTH_SWITCHGATE_SWITCHES
4483 if (element == EL_SWITCHGATE_SWITCH_UP ||
4484 element == EL_SWITCHGATE_SWITCH_DOWN)
4486 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
4487 DrawLevelField(xx, yy);
4489 else if (element == EL_DC_SWITCHGATE_SWITCH_UP ||
4490 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
4492 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
4493 DrawLevelField(xx, yy);
4496 if (element == EL_SWITCHGATE_SWITCH_UP)
4498 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
4499 DrawLevelField(xx, yy);
4501 else if (element == EL_SWITCHGATE_SWITCH_DOWN)
4503 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
4504 DrawLevelField(xx, yy);
4506 else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
4508 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
4509 DrawLevelField(xx, yy);
4511 else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
4513 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
4514 DrawLevelField(xx, yy);
4517 else if (element == EL_SWITCHGATE_OPEN ||
4518 element == EL_SWITCHGATE_OPENING)
4520 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
4522 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
4524 else if (element == EL_SWITCHGATE_CLOSED ||
4525 element == EL_SWITCHGATE_CLOSING)
4527 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
4529 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
4534 static int getInvisibleActiveFromInvisibleElement(int element)
4536 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
4537 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
4538 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
4542 static int getInvisibleFromInvisibleActiveElement(int element)
4544 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
4545 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
4546 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
4550 static void RedrawAllLightSwitchesAndInvisibleElements()
4554 SCAN_PLAYFIELD(x, y)
4556 int element = Feld[x][y];
4558 if (element == EL_LIGHT_SWITCH &&
4559 game.light_time_left > 0)
4561 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
4562 DrawLevelField(x, y);
4564 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
4565 game.light_time_left == 0)
4567 Feld[x][y] = EL_LIGHT_SWITCH;
4568 DrawLevelField(x, y);
4570 else if (element == EL_EMC_DRIPPER &&
4571 game.light_time_left > 0)
4573 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
4574 DrawLevelField(x, y);
4576 else if (element == EL_EMC_DRIPPER_ACTIVE &&
4577 game.light_time_left == 0)
4579 Feld[x][y] = EL_EMC_DRIPPER;
4580 DrawLevelField(x, y);
4582 else if (element == EL_INVISIBLE_STEELWALL ||
4583 element == EL_INVISIBLE_WALL ||
4584 element == EL_INVISIBLE_SAND)
4586 if (game.light_time_left > 0)
4587 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
4589 DrawLevelField(x, y);
4591 /* uncrumble neighbour fields, if needed */
4592 if (element == EL_INVISIBLE_SAND)
4593 DrawLevelFieldCrumbledSandNeighbours(x, y);
4595 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
4596 element == EL_INVISIBLE_WALL_ACTIVE ||
4597 element == EL_INVISIBLE_SAND_ACTIVE)
4599 if (game.light_time_left == 0)
4600 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
4602 DrawLevelField(x, y);
4604 /* re-crumble neighbour fields, if needed */
4605 if (element == EL_INVISIBLE_SAND)
4606 DrawLevelFieldCrumbledSandNeighbours(x, y);
4611 static void RedrawAllInvisibleElementsForLenses()
4615 SCAN_PLAYFIELD(x, y)
4617 int element = Feld[x][y];
4619 if (element == EL_EMC_DRIPPER &&
4620 game.lenses_time_left > 0)
4622 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
4623 DrawLevelField(x, y);
4625 else if (element == EL_EMC_DRIPPER_ACTIVE &&
4626 game.lenses_time_left == 0)
4628 Feld[x][y] = EL_EMC_DRIPPER;
4629 DrawLevelField(x, y);
4631 else if (element == EL_INVISIBLE_STEELWALL ||
4632 element == EL_INVISIBLE_WALL ||
4633 element == EL_INVISIBLE_SAND)
4635 if (game.lenses_time_left > 0)
4636 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
4638 DrawLevelField(x, y);
4640 /* uncrumble neighbour fields, if needed */
4641 if (element == EL_INVISIBLE_SAND)
4642 DrawLevelFieldCrumbledSandNeighbours(x, y);
4644 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
4645 element == EL_INVISIBLE_WALL_ACTIVE ||
4646 element == EL_INVISIBLE_SAND_ACTIVE)
4648 if (game.lenses_time_left == 0)
4649 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
4651 DrawLevelField(x, y);
4653 /* re-crumble neighbour fields, if needed */
4654 if (element == EL_INVISIBLE_SAND)
4655 DrawLevelFieldCrumbledSandNeighbours(x, y);
4660 static void RedrawAllInvisibleElementsForMagnifier()
4664 SCAN_PLAYFIELD(x, y)
4666 int element = Feld[x][y];
4668 if (element == EL_EMC_FAKE_GRASS &&
4669 game.magnify_time_left > 0)
4671 Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
4672 DrawLevelField(x, y);
4674 else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
4675 game.magnify_time_left == 0)
4677 Feld[x][y] = EL_EMC_FAKE_GRASS;
4678 DrawLevelField(x, y);
4680 else if (IS_GATE_GRAY(element) &&
4681 game.magnify_time_left > 0)
4683 Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
4684 element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
4685 IS_EM_GATE_GRAY(element) ?
4686 element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
4687 IS_EMC_GATE_GRAY(element) ?
4688 element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
4690 DrawLevelField(x, y);
4692 else if (IS_GATE_GRAY_ACTIVE(element) &&
4693 game.magnify_time_left == 0)
4695 Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
4696 element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
4697 IS_EM_GATE_GRAY_ACTIVE(element) ?
4698 element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
4699 IS_EMC_GATE_GRAY_ACTIVE(element) ?
4700 element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
4702 DrawLevelField(x, y);
4707 static void ToggleLightSwitch(int x, int y)
4709 int element = Feld[x][y];
4711 game.light_time_left =
4712 (element == EL_LIGHT_SWITCH ?
4713 level.time_light * FRAMES_PER_SECOND : 0);
4715 RedrawAllLightSwitchesAndInvisibleElements();
4718 static void ActivateTimegateSwitch(int x, int y)
4722 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
4724 SCAN_PLAYFIELD(xx, yy)
4726 int element = Feld[xx][yy];
4728 if (element == EL_TIMEGATE_CLOSED ||
4729 element == EL_TIMEGATE_CLOSING)
4731 Feld[xx][yy] = EL_TIMEGATE_OPENING;
4732 PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
4736 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
4738 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
4739 DrawLevelField(xx, yy);
4746 Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
4747 EL_DC_TIMEGATE_SWITCH_ACTIVE);
4749 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
4753 void Impact(int x, int y)
4755 boolean last_line = (y == lev_fieldy - 1);
4756 boolean object_hit = FALSE;
4757 boolean impact = (last_line || object_hit);
4758 int element = Feld[x][y];
4759 int smashed = EL_STEELWALL;
4761 if (!last_line) /* check if element below was hit */
4763 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
4766 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
4767 MovDir[x][y + 1] != MV_DOWN ||
4768 MovPos[x][y + 1] <= TILEY / 2));
4770 /* do not smash moving elements that left the smashed field in time */
4771 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
4772 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
4775 #if USE_QUICKSAND_IMPACT_BUGFIX
4776 if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
4778 RemoveMovingField(x, y + 1);
4779 Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
4780 Feld[x][y + 2] = EL_ROCK;
4781 DrawLevelField(x, y + 2);
4786 if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
4788 RemoveMovingField(x, y + 1);
4789 Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
4790 Feld[x][y + 2] = EL_ROCK;
4791 DrawLevelField(x, y + 2);
4798 smashed = MovingOrBlocked2Element(x, y + 1);
4800 impact = (last_line || object_hit);
4803 if (!last_line && smashed == EL_ACID) /* element falls into acid */
4805 SplashAcid(x, y + 1);
4809 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
4810 /* only reset graphic animation if graphic really changes after impact */
4812 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
4814 ResetGfxAnimation(x, y);
4815 DrawLevelField(x, y);
4818 if (impact && CAN_EXPLODE_IMPACT(element))
4823 else if (impact && element == EL_PEARL &&
4824 smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
4826 ResetGfxAnimation(x, y);
4828 Feld[x][y] = EL_PEARL_BREAKING;
4829 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4832 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
4834 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4839 if (impact && element == EL_AMOEBA_DROP)
4841 if (object_hit && IS_PLAYER(x, y + 1))
4842 KillPlayerUnlessEnemyProtected(x, y + 1);
4843 else if (object_hit && smashed == EL_PENGUIN)
4847 Feld[x][y] = EL_AMOEBA_GROWING;
4848 Store[x][y] = EL_AMOEBA_WET;
4850 ResetRandomAnimationValue(x, y);
4855 if (object_hit) /* check which object was hit */
4857 if ((CAN_PASS_MAGIC_WALL(element) &&
4858 (smashed == EL_MAGIC_WALL ||
4859 smashed == EL_BD_MAGIC_WALL)) ||
4860 (CAN_PASS_DC_MAGIC_WALL(element) &&
4861 smashed == EL_DC_MAGIC_WALL))
4864 int activated_magic_wall =
4865 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
4866 smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
4867 EL_DC_MAGIC_WALL_ACTIVE);
4869 /* activate magic wall / mill */
4870 SCAN_PLAYFIELD(xx, yy)
4872 if (Feld[xx][yy] == smashed)
4873 Feld[xx][yy] = activated_magic_wall;
4876 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
4877 game.magic_wall_active = TRUE;
4879 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
4880 SND_MAGIC_WALL_ACTIVATING :
4881 smashed == EL_BD_MAGIC_WALL ?
4882 SND_BD_MAGIC_WALL_ACTIVATING :
4883 SND_DC_MAGIC_WALL_ACTIVATING));
4886 if (IS_PLAYER(x, y + 1))
4888 if (CAN_SMASH_PLAYER(element))
4890 KillPlayerUnlessEnemyProtected(x, y + 1);
4894 else if (smashed == EL_PENGUIN)
4896 if (CAN_SMASH_PLAYER(element))
4902 else if (element == EL_BD_DIAMOND)
4904 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
4910 else if (((element == EL_SP_INFOTRON ||
4911 element == EL_SP_ZONK) &&
4912 (smashed == EL_SP_SNIKSNAK ||
4913 smashed == EL_SP_ELECTRON ||
4914 smashed == EL_SP_DISK_ORANGE)) ||
4915 (element == EL_SP_INFOTRON &&
4916 smashed == EL_SP_DISK_YELLOW))
4921 else if (CAN_SMASH_EVERYTHING(element))
4923 if (IS_CLASSIC_ENEMY(smashed) ||
4924 CAN_EXPLODE_SMASHED(smashed))
4929 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
4931 if (smashed == EL_LAMP ||
4932 smashed == EL_LAMP_ACTIVE)
4937 else if (smashed == EL_NUT)
4939 Feld[x][y + 1] = EL_NUT_BREAKING;
4940 PlayLevelSound(x, y, SND_NUT_BREAKING);
4941 RaiseScoreElement(EL_NUT);
4944 else if (smashed == EL_PEARL)
4946 ResetGfxAnimation(x, y);
4948 Feld[x][y + 1] = EL_PEARL_BREAKING;
4949 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4952 else if (smashed == EL_DIAMOND)
4954 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
4955 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
4958 else if (IS_BELT_SWITCH(smashed))
4960 ToggleBeltSwitch(x, y + 1);
4962 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
4963 smashed == EL_SWITCHGATE_SWITCH_DOWN ||
4964 smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
4965 smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
4967 ToggleSwitchgateSwitch(x, y + 1);
4969 else if (smashed == EL_LIGHT_SWITCH ||
4970 smashed == EL_LIGHT_SWITCH_ACTIVE)
4972 ToggleLightSwitch(x, y + 1);
4977 TestIfElementSmashesCustomElement(x, y, MV_DOWN);
4980 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4982 CheckElementChangeBySide(x, y + 1, smashed, element,
4983 CE_SWITCHED, CH_SIDE_TOP);
4984 CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
4990 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4995 /* play sound of magic wall / mill */
4997 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
4998 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
4999 Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
5001 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
5002 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
5003 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
5004 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
5005 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
5006 PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
5011 /* play sound of object that hits the ground */
5012 if (last_line || object_hit)
5013 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
5016 inline static void TurnRoundExt(int x, int y)
5028 { 0, 0 }, { 0, 0 }, { 0, 0 },
5033 int left, right, back;
5037 { MV_DOWN, MV_UP, MV_RIGHT },
5038 { MV_UP, MV_DOWN, MV_LEFT },
5040 { MV_LEFT, MV_RIGHT, MV_DOWN },
5044 { MV_RIGHT, MV_LEFT, MV_UP }
5047 int element = Feld[x][y];
5048 int move_pattern = element_info[element].move_pattern;
5050 int old_move_dir = MovDir[x][y];
5051 int left_dir = turn[old_move_dir].left;
5052 int right_dir = turn[old_move_dir].right;
5053 int back_dir = turn[old_move_dir].back;
5055 int left_dx = move_xy[left_dir].dx, left_dy = move_xy[left_dir].dy;
5056 int right_dx = move_xy[right_dir].dx, right_dy = move_xy[right_dir].dy;
5057 int move_dx = move_xy[old_move_dir].dx, move_dy = move_xy[old_move_dir].dy;
5058 int back_dx = move_xy[back_dir].dx, back_dy = move_xy[back_dir].dy;
5060 int left_x = x + left_dx, left_y = y + left_dy;
5061 int right_x = x + right_dx, right_y = y + right_dy;
5062 int move_x = x + move_dx, move_y = y + move_dy;
5066 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
5068 TestIfBadThingTouchesOtherBadThing(x, y);
5070 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
5071 MovDir[x][y] = right_dir;
5072 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
5073 MovDir[x][y] = left_dir;
5075 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
5077 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
5080 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
5082 TestIfBadThingTouchesOtherBadThing(x, y);
5084 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
5085 MovDir[x][y] = left_dir;
5086 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
5087 MovDir[x][y] = right_dir;
5089 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
5091 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
5094 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
5096 TestIfBadThingTouchesOtherBadThing(x, y);
5098 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
5099 MovDir[x][y] = left_dir;
5100 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
5101 MovDir[x][y] = right_dir;
5103 if (MovDir[x][y] != old_move_dir)
5106 else if (element == EL_YAMYAM)
5108 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
5109 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
5111 if (can_turn_left && can_turn_right)
5112 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5113 else if (can_turn_left)
5114 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5115 else if (can_turn_right)
5116 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5118 MovDir[x][y] = back_dir;
5120 MovDelay[x][y] = 16 + 16 * RND(3);
5122 else if (element == EL_DARK_YAMYAM)
5124 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
5126 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
5129 if (can_turn_left && can_turn_right)
5130 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5131 else if (can_turn_left)
5132 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5133 else if (can_turn_right)
5134 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5136 MovDir[x][y] = back_dir;
5138 MovDelay[x][y] = 16 + 16 * RND(3);
5140 else if (element == EL_PACMAN)
5142 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
5143 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
5145 if (can_turn_left && can_turn_right)
5146 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5147 else if (can_turn_left)
5148 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5149 else if (can_turn_right)
5150 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5152 MovDir[x][y] = back_dir;
5154 MovDelay[x][y] = 6 + RND(40);
5156 else if (element == EL_PIG)
5158 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
5159 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
5160 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
5161 boolean should_turn_left, should_turn_right, should_move_on;
5163 int rnd = RND(rnd_value);
5165 should_turn_left = (can_turn_left &&
5167 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
5168 y + back_dy + left_dy)));
5169 should_turn_right = (can_turn_right &&
5171 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
5172 y + back_dy + right_dy)));
5173 should_move_on = (can_move_on &&
5176 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
5177 y + move_dy + left_dy) ||
5178 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
5179 y + move_dy + right_dy)));
5181 if (should_turn_left || should_turn_right || should_move_on)
5183 if (should_turn_left && should_turn_right && should_move_on)
5184 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
5185 rnd < 2 * rnd_value / 3 ? right_dir :
5187 else if (should_turn_left && should_turn_right)
5188 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
5189 else if (should_turn_left && should_move_on)
5190 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
5191 else if (should_turn_right && should_move_on)
5192 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
5193 else if (should_turn_left)
5194 MovDir[x][y] = left_dir;
5195 else if (should_turn_right)
5196 MovDir[x][y] = right_dir;
5197 else if (should_move_on)
5198 MovDir[x][y] = old_move_dir;
5200 else if (can_move_on && rnd > rnd_value / 8)
5201 MovDir[x][y] = old_move_dir;
5202 else if (can_turn_left && can_turn_right)
5203 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
5204 else if (can_turn_left && rnd > rnd_value / 8)
5205 MovDir[x][y] = left_dir;
5206 else if (can_turn_right && rnd > rnd_value/8)
5207 MovDir[x][y] = right_dir;
5209 MovDir[x][y] = back_dir;
5211 xx = x + move_xy[MovDir[x][y]].dx;
5212 yy = y + move_xy[MovDir[x][y]].dy;
5214 if (!IN_LEV_FIELD(xx, yy) ||
5215 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
5216 MovDir[x][y] = old_move_dir;
5220 else if (element == EL_DRAGON)
5222 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
5223 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
5224 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
5226 int rnd = RND(rnd_value);
5228 if (can_move_on && rnd > rnd_value / 8)
5229 MovDir[x][y] = old_move_dir;
5230 else if (can_turn_left && can_turn_right)
5231 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
5232 else if (can_turn_left && rnd > rnd_value / 8)
5233 MovDir[x][y] = left_dir;
5234 else if (can_turn_right && rnd > rnd_value / 8)
5235 MovDir[x][y] = right_dir;
5237 MovDir[x][y] = back_dir;
5239 xx = x + move_xy[MovDir[x][y]].dx;
5240 yy = y + move_xy[MovDir[x][y]].dy;
5242 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
5243 MovDir[x][y] = old_move_dir;
5247 else if (element == EL_MOLE)
5249 boolean can_move_on =
5250 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
5251 IS_AMOEBOID(Feld[move_x][move_y]) ||
5252 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
5255 boolean can_turn_left =
5256 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
5257 IS_AMOEBOID(Feld[left_x][left_y])));
5259 boolean can_turn_right =
5260 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
5261 IS_AMOEBOID(Feld[right_x][right_y])));
5263 if (can_turn_left && can_turn_right)
5264 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
5265 else if (can_turn_left)
5266 MovDir[x][y] = left_dir;
5268 MovDir[x][y] = right_dir;
5271 if (MovDir[x][y] != old_move_dir)
5274 else if (element == EL_BALLOON)
5276 MovDir[x][y] = game.wind_direction;
5279 else if (element == EL_SPRING)
5281 #if USE_NEW_SPRING_BUMPER
5282 if (MovDir[x][y] & MV_HORIZONTAL)
5284 if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
5285 !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
5287 Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
5288 ResetGfxAnimation(move_x, move_y);
5289 DrawLevelField(move_x, move_y);
5291 MovDir[x][y] = back_dir;
5293 else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
5294 SPRING_CAN_ENTER_FIELD(element, x, y + 1))
5295 MovDir[x][y] = MV_NONE;
5298 if (MovDir[x][y] & MV_HORIZONTAL &&
5299 (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
5300 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
5301 MovDir[x][y] = MV_NONE;
5306 else if (element == EL_ROBOT ||
5307 element == EL_SATELLITE ||
5308 element == EL_PENGUIN ||
5309 element == EL_EMC_ANDROID)
5311 int attr_x = -1, attr_y = -1;
5322 for (i = 0; i < MAX_PLAYERS; i++)
5324 struct PlayerInfo *player = &stored_player[i];
5325 int jx = player->jx, jy = player->jy;
5327 if (!player->active)
5331 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
5339 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
5340 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
5341 game.engine_version < VERSION_IDENT(3,1,0,0)))
5347 if (element == EL_PENGUIN)
5350 static int xy[4][2] =
5358 for (i = 0; i < NUM_DIRECTIONS; i++)
5360 int ex = x + xy[i][0];
5361 int ey = y + xy[i][1];
5363 if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
5364 Feld[ex][ey] == EL_EM_EXIT_OPEN ||
5365 Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
5366 Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
5375 MovDir[x][y] = MV_NONE;
5377 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
5378 else if (attr_x > x)
5379 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
5381 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
5382 else if (attr_y > y)
5383 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
5385 if (element == EL_ROBOT)
5389 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5390 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
5391 Moving2Blocked(x, y, &newx, &newy);
5393 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
5394 MovDelay[x][y] = 8 + 8 * !RND(3);
5396 MovDelay[x][y] = 16;
5398 else if (element == EL_PENGUIN)
5404 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5406 boolean first_horiz = RND(2);
5407 int new_move_dir = MovDir[x][y];
5410 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5411 Moving2Blocked(x, y, &newx, &newy);
5413 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
5417 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5418 Moving2Blocked(x, y, &newx, &newy);
5420 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
5423 MovDir[x][y] = old_move_dir;
5427 else if (element == EL_SATELLITE)
5433 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5435 boolean first_horiz = RND(2);
5436 int new_move_dir = MovDir[x][y];
5439 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5440 Moving2Blocked(x, y, &newx, &newy);
5442 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
5446 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5447 Moving2Blocked(x, y, &newx, &newy);
5449 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
5452 MovDir[x][y] = old_move_dir;
5456 else if (element == EL_EMC_ANDROID)
5458 static int check_pos[16] =
5460 -1, /* 0 => (invalid) */
5461 7, /* 1 => MV_LEFT */
5462 3, /* 2 => MV_RIGHT */
5463 -1, /* 3 => (invalid) */
5465 0, /* 5 => MV_LEFT | MV_UP */
5466 2, /* 6 => MV_RIGHT | MV_UP */
5467 -1, /* 7 => (invalid) */
5468 5, /* 8 => MV_DOWN */
5469 6, /* 9 => MV_LEFT | MV_DOWN */
5470 4, /* 10 => MV_RIGHT | MV_DOWN */
5471 -1, /* 11 => (invalid) */
5472 -1, /* 12 => (invalid) */
5473 -1, /* 13 => (invalid) */
5474 -1, /* 14 => (invalid) */
5475 -1, /* 15 => (invalid) */
5483 { -1, -1, MV_LEFT | MV_UP },
5485 { +1, -1, MV_RIGHT | MV_UP },
5486 { +1, 0, MV_RIGHT },
5487 { +1, +1, MV_RIGHT | MV_DOWN },
5489 { -1, +1, MV_LEFT | MV_DOWN },
5492 int start_pos, check_order;
5493 boolean can_clone = FALSE;
5496 /* check if there is any free field around current position */
5497 for (i = 0; i < 8; i++)
5499 int newx = x + check_xy[i].dx;
5500 int newy = y + check_xy[i].dy;
5502 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5510 if (can_clone) /* randomly find an element to clone */
5514 start_pos = check_pos[RND(8)];
5515 check_order = (RND(2) ? -1 : +1);
5517 for (i = 0; i < 8; i++)
5519 int pos_raw = start_pos + i * check_order;
5520 int pos = (pos_raw + 8) % 8;
5521 int newx = x + check_xy[pos].dx;
5522 int newy = y + check_xy[pos].dy;
5524 if (ANDROID_CAN_CLONE_FIELD(newx, newy))
5526 element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
5527 element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
5529 Store[x][y] = Feld[newx][newy];
5538 if (can_clone) /* randomly find a direction to move */
5542 start_pos = check_pos[RND(8)];
5543 check_order = (RND(2) ? -1 : +1);
5545 for (i = 0; i < 8; i++)
5547 int pos_raw = start_pos + i * check_order;
5548 int pos = (pos_raw + 8) % 8;
5549 int newx = x + check_xy[pos].dx;
5550 int newy = y + check_xy[pos].dy;
5551 int new_move_dir = check_xy[pos].dir;
5553 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5555 MovDir[x][y] = new_move_dir;
5556 MovDelay[x][y] = level.android_clone_time * 8 + 1;
5565 if (can_clone) /* cloning and moving successful */
5568 /* cannot clone -- try to move towards player */
5570 start_pos = check_pos[MovDir[x][y] & 0x0f];
5571 check_order = (RND(2) ? -1 : +1);
5573 for (i = 0; i < 3; i++)
5575 /* first check start_pos, then previous/next or (next/previous) pos */
5576 int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
5577 int pos = (pos_raw + 8) % 8;
5578 int newx = x + check_xy[pos].dx;
5579 int newy = y + check_xy[pos].dy;
5580 int new_move_dir = check_xy[pos].dir;
5582 if (IS_PLAYER(newx, newy))
5585 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
5587 MovDir[x][y] = new_move_dir;
5588 MovDelay[x][y] = level.android_move_time * 8 + 1;
5595 else if (move_pattern == MV_TURNING_LEFT ||
5596 move_pattern == MV_TURNING_RIGHT ||
5597 move_pattern == MV_TURNING_LEFT_RIGHT ||
5598 move_pattern == MV_TURNING_RIGHT_LEFT ||
5599 move_pattern == MV_TURNING_RANDOM ||
5600 move_pattern == MV_ALL_DIRECTIONS)
5602 boolean can_turn_left =
5603 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
5604 boolean can_turn_right =
5605 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
5607 if (element_info[element].move_stepsize == 0) /* "not moving" */
5610 if (move_pattern == MV_TURNING_LEFT)
5611 MovDir[x][y] = left_dir;
5612 else if (move_pattern == MV_TURNING_RIGHT)
5613 MovDir[x][y] = right_dir;
5614 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
5615 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
5616 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
5617 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
5618 else if (move_pattern == MV_TURNING_RANDOM)
5619 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
5620 can_turn_right && !can_turn_left ? right_dir :
5621 RND(2) ? left_dir : right_dir);
5622 else if (can_turn_left && can_turn_right)
5623 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5624 else if (can_turn_left)
5625 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5626 else if (can_turn_right)
5627 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5629 MovDir[x][y] = back_dir;
5631 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5633 else if (move_pattern == MV_HORIZONTAL ||
5634 move_pattern == MV_VERTICAL)
5636 if (move_pattern & old_move_dir)
5637 MovDir[x][y] = back_dir;
5638 else if (move_pattern == MV_HORIZONTAL)
5639 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
5640 else if (move_pattern == MV_VERTICAL)
5641 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
5643 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5645 else if (move_pattern & MV_ANY_DIRECTION)
5647 MovDir[x][y] = move_pattern;
5648 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5650 else if (move_pattern & MV_WIND_DIRECTION)
5652 MovDir[x][y] = game.wind_direction;
5653 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5655 else if (move_pattern == MV_ALONG_LEFT_SIDE)
5657 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
5658 MovDir[x][y] = left_dir;
5659 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5660 MovDir[x][y] = right_dir;
5662 if (MovDir[x][y] != old_move_dir)
5663 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5665 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
5667 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
5668 MovDir[x][y] = right_dir;
5669 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5670 MovDir[x][y] = left_dir;
5672 if (MovDir[x][y] != old_move_dir)
5673 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5675 else if (move_pattern == MV_TOWARDS_PLAYER ||
5676 move_pattern == MV_AWAY_FROM_PLAYER)
5678 int attr_x = -1, attr_y = -1;
5680 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
5691 for (i = 0; i < MAX_PLAYERS; i++)
5693 struct PlayerInfo *player = &stored_player[i];
5694 int jx = player->jx, jy = player->jy;
5696 if (!player->active)
5700 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
5708 MovDir[x][y] = MV_NONE;
5710 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
5711 else if (attr_x > x)
5712 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
5714 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
5715 else if (attr_y > y)
5716 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
5718 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5720 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5722 boolean first_horiz = RND(2);
5723 int new_move_dir = MovDir[x][y];
5725 if (element_info[element].move_stepsize == 0) /* "not moving" */
5727 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
5728 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5734 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5735 Moving2Blocked(x, y, &newx, &newy);
5737 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5741 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5742 Moving2Blocked(x, y, &newx, &newy);
5744 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5747 MovDir[x][y] = old_move_dir;
5750 else if (move_pattern == MV_WHEN_PUSHED ||
5751 move_pattern == MV_WHEN_DROPPED)
5753 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5754 MovDir[x][y] = MV_NONE;
5758 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
5760 static int test_xy[7][2] =
5770 static int test_dir[7] =
5780 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
5781 int move_preference = -1000000; /* start with very low preference */
5782 int new_move_dir = MV_NONE;
5783 int start_test = RND(4);
5786 for (i = 0; i < NUM_DIRECTIONS; i++)
5788 int move_dir = test_dir[start_test + i];
5789 int move_dir_preference;
5791 xx = x + test_xy[start_test + i][0];
5792 yy = y + test_xy[start_test + i][1];
5794 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
5795 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
5797 new_move_dir = move_dir;
5802 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
5805 move_dir_preference = -1 * RunnerVisit[xx][yy];
5806 if (hunter_mode && PlayerVisit[xx][yy] > 0)
5807 move_dir_preference = PlayerVisit[xx][yy];
5809 if (move_dir_preference > move_preference)
5811 /* prefer field that has not been visited for the longest time */
5812 move_preference = move_dir_preference;
5813 new_move_dir = move_dir;
5815 else if (move_dir_preference == move_preference &&
5816 move_dir == old_move_dir)
5818 /* prefer last direction when all directions are preferred equally */
5819 move_preference = move_dir_preference;
5820 new_move_dir = move_dir;
5824 MovDir[x][y] = new_move_dir;
5825 if (old_move_dir != new_move_dir)
5826 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5830 static void TurnRound(int x, int y)
5832 int direction = MovDir[x][y];
5836 GfxDir[x][y] = MovDir[x][y];
5838 if (direction != MovDir[x][y])
5842 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
5844 ResetGfxFrame(x, y, FALSE);
5847 static boolean JustBeingPushed(int x, int y)
5851 for (i = 0; i < MAX_PLAYERS; i++)
5853 struct PlayerInfo *player = &stored_player[i];
5855 if (player->active && player->is_pushing && player->MovPos)
5857 int next_jx = player->jx + (player->jx - player->last_jx);
5858 int next_jy = player->jy + (player->jy - player->last_jy);
5860 if (x == next_jx && y == next_jy)
5868 void StartMoving(int x, int y)
5870 boolean started_moving = FALSE; /* some elements can fall _and_ move */
5871 int element = Feld[x][y];
5876 if (MovDelay[x][y] == 0)
5877 GfxAction[x][y] = ACTION_DEFAULT;
5879 if (CAN_FALL(element) && y < lev_fieldy - 1)
5881 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
5882 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
5883 if (JustBeingPushed(x, y))
5886 if (element == EL_QUICKSAND_FULL)
5888 if (IS_FREE(x, y + 1))
5890 InitMovingField(x, y, MV_DOWN);
5891 started_moving = TRUE;
5893 Feld[x][y] = EL_QUICKSAND_EMPTYING;
5894 #if USE_QUICKSAND_BD_ROCK_BUGFIX
5895 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
5896 Store[x][y] = EL_ROCK;
5898 Store[x][y] = EL_ROCK;
5901 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
5903 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5905 if (!MovDelay[x][y])
5906 MovDelay[x][y] = TILEY + 1;
5915 Feld[x][y] = EL_QUICKSAND_EMPTY;
5916 Feld[x][y + 1] = EL_QUICKSAND_FULL;
5917 Store[x][y + 1] = Store[x][y];
5920 PlayLevelSoundAction(x, y, ACTION_FILLING);
5923 else if (element == EL_QUICKSAND_FAST_FULL)
5925 if (IS_FREE(x, y + 1))
5927 InitMovingField(x, y, MV_DOWN);
5928 started_moving = TRUE;
5930 Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
5931 #if USE_QUICKSAND_BD_ROCK_BUGFIX
5932 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
5933 Store[x][y] = EL_ROCK;
5935 Store[x][y] = EL_ROCK;
5938 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
5940 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
5942 if (!MovDelay[x][y])
5943 MovDelay[x][y] = TILEY + 1;
5952 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
5953 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
5954 Store[x][y + 1] = Store[x][y];
5957 PlayLevelSoundAction(x, y, ACTION_FILLING);
5960 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
5961 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5963 InitMovingField(x, y, MV_DOWN);
5964 started_moving = TRUE;
5966 Feld[x][y] = EL_QUICKSAND_FILLING;
5967 Store[x][y] = element;
5969 PlayLevelSoundAction(x, y, ACTION_FILLING);
5971 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
5972 Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
5974 InitMovingField(x, y, MV_DOWN);
5975 started_moving = TRUE;
5977 Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
5978 Store[x][y] = element;
5980 PlayLevelSoundAction(x, y, ACTION_FILLING);
5982 else if (element == EL_MAGIC_WALL_FULL)
5984 if (IS_FREE(x, y + 1))
5986 InitMovingField(x, y, MV_DOWN);
5987 started_moving = TRUE;
5989 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
5990 Store[x][y] = EL_CHANGED(Store[x][y]);
5992 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
5994 if (!MovDelay[x][y])
5995 MovDelay[x][y] = TILEY/4 + 1;
6004 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
6005 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
6006 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
6010 else if (element == EL_BD_MAGIC_WALL_FULL)
6012 if (IS_FREE(x, y + 1))
6014 InitMovingField(x, y, MV_DOWN);
6015 started_moving = TRUE;
6017 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
6018 Store[x][y] = EL_CHANGED_BD(Store[x][y]);
6020 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6022 if (!MovDelay[x][y])
6023 MovDelay[x][y] = TILEY/4 + 1;
6032 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
6033 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
6034 Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
6038 else if (element == EL_DC_MAGIC_WALL_FULL)
6040 if (IS_FREE(x, y + 1))
6042 InitMovingField(x, y, MV_DOWN);
6043 started_moving = TRUE;
6045 Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
6046 Store[x][y] = EL_CHANGED_DC(Store[x][y]);
6048 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6050 if (!MovDelay[x][y])
6051 MovDelay[x][y] = TILEY/4 + 1;
6060 Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
6061 Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
6062 Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
6066 else if ((CAN_PASS_MAGIC_WALL(element) &&
6067 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6068 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
6069 (CAN_PASS_DC_MAGIC_WALL(element) &&
6070 (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
6073 InitMovingField(x, y, MV_DOWN);
6074 started_moving = TRUE;
6077 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
6078 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
6079 EL_DC_MAGIC_WALL_FILLING);
6080 Store[x][y] = element;
6082 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
6084 SplashAcid(x, y + 1);
6086 InitMovingField(x, y, MV_DOWN);
6087 started_moving = TRUE;
6089 Store[x][y] = EL_ACID;
6092 #if USE_FIX_IMPACT_COLLISION
6093 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
6094 CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
6096 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
6097 CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
6099 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
6100 CAN_FALL(element) && WasJustFalling[x][y] &&
6101 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
6103 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
6104 CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
6105 (Feld[x][y + 1] == EL_BLOCKED)))
6107 /* this is needed for a special case not covered by calling "Impact()"
6108 from "ContinueMoving()": if an element moves to a tile directly below
6109 another element which was just falling on that tile (which was empty
6110 in the previous frame), the falling element above would just stop
6111 instead of smashing the element below (in previous version, the above
6112 element was just checked for "moving" instead of "falling", resulting
6113 in incorrect smashes caused by horizontal movement of the above
6114 element; also, the case of the player being the element to smash was
6115 simply not covered here... :-/ ) */
6117 CheckCollision[x][y] = 0;
6118 CheckImpact[x][y] = 0;
6122 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
6124 if (MovDir[x][y] == MV_NONE)
6126 InitMovingField(x, y, MV_DOWN);
6127 started_moving = TRUE;
6130 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
6132 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
6133 MovDir[x][y] = MV_DOWN;
6135 InitMovingField(x, y, MV_DOWN);
6136 started_moving = TRUE;
6138 else if (element == EL_AMOEBA_DROP)
6140 Feld[x][y] = EL_AMOEBA_GROWING;
6141 Store[x][y] = EL_AMOEBA_WET;
6143 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
6144 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
6145 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
6146 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
6148 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
6149 (IS_FREE(x - 1, y + 1) ||
6150 Feld[x - 1][y + 1] == EL_ACID));
6151 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
6152 (IS_FREE(x + 1, y + 1) ||
6153 Feld[x + 1][y + 1] == EL_ACID));
6154 boolean can_fall_any = (can_fall_left || can_fall_right);
6155 boolean can_fall_both = (can_fall_left && can_fall_right);
6156 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
6158 #if USE_NEW_ALL_SLIPPERY
6159 if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
6161 if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
6162 can_fall_right = FALSE;
6163 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
6164 can_fall_left = FALSE;
6165 else if (slippery_type == SLIPPERY_ONLY_LEFT)
6166 can_fall_right = FALSE;
6167 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
6168 can_fall_left = FALSE;
6170 can_fall_any = (can_fall_left || can_fall_right);
6171 can_fall_both = FALSE;
6174 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
6176 if (slippery_type == SLIPPERY_ONLY_LEFT)
6177 can_fall_right = FALSE;
6178 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
6179 can_fall_left = FALSE;
6180 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
6181 can_fall_right = FALSE;
6182 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
6183 can_fall_left = FALSE;
6185 can_fall_any = (can_fall_left || can_fall_right);
6186 can_fall_both = (can_fall_left && can_fall_right);
6190 #if USE_NEW_ALL_SLIPPERY
6192 #if USE_NEW_SP_SLIPPERY
6193 /* !!! better use the same properties as for custom elements here !!! */
6194 else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
6195 can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
6197 can_fall_right = FALSE; /* slip down on left side */
6198 can_fall_both = FALSE;
6203 #if USE_NEW_ALL_SLIPPERY
6206 if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
6207 can_fall_right = FALSE; /* slip down on left side */
6209 can_fall_left = !(can_fall_right = RND(2));
6211 can_fall_both = FALSE;
6216 if (game.emulation == EMU_BOULDERDASH ||
6217 element == EL_BD_ROCK || element == EL_BD_DIAMOND)
6218 can_fall_right = FALSE; /* slip down on left side */
6220 can_fall_left = !(can_fall_right = RND(2));
6222 can_fall_both = FALSE;
6228 /* if not determined otherwise, prefer left side for slipping down */
6229 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
6230 started_moving = TRUE;
6234 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
6236 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
6239 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
6240 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
6241 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
6242 int belt_dir = game.belt_dir[belt_nr];
6244 if ((belt_dir == MV_LEFT && left_is_free) ||
6245 (belt_dir == MV_RIGHT && right_is_free))
6247 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
6249 InitMovingField(x, y, belt_dir);
6250 started_moving = TRUE;
6252 Pushed[x][y] = TRUE;
6253 Pushed[nextx][y] = TRUE;
6255 GfxAction[x][y] = ACTION_DEFAULT;
6259 MovDir[x][y] = 0; /* if element was moving, stop it */
6264 /* not "else if" because of elements that can fall and move (EL_SPRING) */
6266 if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
6268 if (CAN_MOVE(element) && !started_moving)
6271 int move_pattern = element_info[element].move_pattern;
6276 if (MovDir[x][y] == MV_NONE)
6278 printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
6279 x, y, element, element_info[element].token_name);
6280 printf("StartMoving(): This should never happen!\n");
6285 Moving2Blocked(x, y, &newx, &newy);
6287 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
6290 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
6291 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6293 WasJustMoving[x][y] = 0;
6294 CheckCollision[x][y] = 0;
6296 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
6298 if (Feld[x][y] != element) /* element has changed */
6302 if (!MovDelay[x][y]) /* start new movement phase */
6304 /* all objects that can change their move direction after each step
6305 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
6307 if (element != EL_YAMYAM &&
6308 element != EL_DARK_YAMYAM &&
6309 element != EL_PACMAN &&
6310 !(move_pattern & MV_ANY_DIRECTION) &&
6311 move_pattern != MV_TURNING_LEFT &&
6312 move_pattern != MV_TURNING_RIGHT &&
6313 move_pattern != MV_TURNING_LEFT_RIGHT &&
6314 move_pattern != MV_TURNING_RIGHT_LEFT &&
6315 move_pattern != MV_TURNING_RANDOM)
6319 if (MovDelay[x][y] && (element == EL_BUG ||
6320 element == EL_SPACESHIP ||
6321 element == EL_SP_SNIKSNAK ||
6322 element == EL_SP_ELECTRON ||
6323 element == EL_MOLE))
6324 DrawLevelField(x, y);
6328 if (MovDelay[x][y]) /* wait some time before next movement */
6332 if (element == EL_ROBOT ||
6333 element == EL_YAMYAM ||
6334 element == EL_DARK_YAMYAM)
6336 DrawLevelElementAnimationIfNeeded(x, y, element);
6337 PlayLevelSoundAction(x, y, ACTION_WAITING);
6339 else if (element == EL_SP_ELECTRON)
6340 DrawLevelElementAnimationIfNeeded(x, y, element);
6341 else if (element == EL_DRAGON)
6344 int dir = MovDir[x][y];
6345 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
6346 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
6347 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
6348 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
6349 dir == MV_UP ? IMG_FLAMES_1_UP :
6350 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
6351 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
6353 GfxAction[x][y] = ACTION_ATTACKING;
6355 if (IS_PLAYER(x, y))
6356 DrawPlayerField(x, y);
6358 DrawLevelField(x, y);
6360 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
6362 for (i = 1; i <= 3; i++)
6364 int xx = x + i * dx;
6365 int yy = y + i * dy;
6366 int sx = SCREENX(xx);
6367 int sy = SCREENY(yy);
6368 int flame_graphic = graphic + (i - 1);
6370 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
6375 int flamed = MovingOrBlocked2Element(xx, yy);
6379 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
6381 else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
6382 RemoveMovingField(xx, yy);
6384 RemoveField(xx, yy);
6386 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
6389 RemoveMovingField(xx, yy);
6392 ChangeDelay[xx][yy] = 0;
6394 Feld[xx][yy] = EL_FLAMES;
6396 if (IN_SCR_FIELD(sx, sy))
6398 DrawLevelFieldCrumbledSand(xx, yy);
6399 DrawGraphic(sx, sy, flame_graphic, frame);
6404 if (Feld[xx][yy] == EL_FLAMES)
6405 Feld[xx][yy] = EL_EMPTY;
6406 DrawLevelField(xx, yy);
6411 if (MovDelay[x][y]) /* element still has to wait some time */
6413 PlayLevelSoundAction(x, y, ACTION_WAITING);
6419 /* now make next step */
6421 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
6423 if (DONT_COLLIDE_WITH(element) &&
6424 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
6425 !PLAYER_ENEMY_PROTECTED(newx, newy))
6427 TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
6432 else if (CAN_MOVE_INTO_ACID(element) &&
6433 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
6434 !IS_MV_DIAGONAL(MovDir[x][y]) &&
6435 (MovDir[x][y] == MV_DOWN ||
6436 game.engine_version >= VERSION_IDENT(3,1,0,0)))
6438 SplashAcid(newx, newy);
6439 Store[x][y] = EL_ACID;
6441 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
6443 if (Feld[newx][newy] == EL_EXIT_OPEN ||
6444 Feld[newx][newy] == EL_EM_EXIT_OPEN ||
6445 Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
6446 Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
6449 DrawLevelField(x, y);
6451 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
6452 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
6453 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
6455 local_player->friends_still_needed--;
6456 if (!local_player->friends_still_needed &&
6457 !local_player->GameOver && AllPlayersGone)
6458 PlayerWins(local_player);
6462 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
6464 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
6465 DrawLevelField(newx, newy);
6467 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
6469 else if (!IS_FREE(newx, newy))
6471 GfxAction[x][y] = ACTION_WAITING;
6473 if (IS_PLAYER(x, y))
6474 DrawPlayerField(x, y);
6476 DrawLevelField(x, y);
6481 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
6483 if (IS_FOOD_PIG(Feld[newx][newy]))
6485 if (IS_MOVING(newx, newy))
6486 RemoveMovingField(newx, newy);
6489 Feld[newx][newy] = EL_EMPTY;
6490 DrawLevelField(newx, newy);
6493 PlayLevelSound(x, y, SND_PIG_DIGGING);
6495 else if (!IS_FREE(newx, newy))
6497 if (IS_PLAYER(x, y))
6498 DrawPlayerField(x, y);
6500 DrawLevelField(x, y);
6505 else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
6507 if (Store[x][y] != EL_EMPTY)
6509 boolean can_clone = FALSE;
6512 /* check if element to clone is still there */
6513 for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
6515 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
6523 /* cannot clone or target field not free anymore -- do not clone */
6524 if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6525 Store[x][y] = EL_EMPTY;
6528 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6530 if (IS_MV_DIAGONAL(MovDir[x][y]))
6532 int diagonal_move_dir = MovDir[x][y];
6533 int stored = Store[x][y];
6534 int change_delay = 8;
6537 /* android is moving diagonally */
6539 CreateField(x, y, EL_DIAGONAL_SHRINKING);
6541 Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
6542 GfxElement[x][y] = EL_EMC_ANDROID;
6543 GfxAction[x][y] = ACTION_SHRINKING;
6544 GfxDir[x][y] = diagonal_move_dir;
6545 ChangeDelay[x][y] = change_delay;
6547 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
6550 DrawLevelGraphicAnimation(x, y, graphic);
6551 PlayLevelSoundAction(x, y, ACTION_SHRINKING);
6553 if (Feld[newx][newy] == EL_ACID)
6555 SplashAcid(newx, newy);
6560 CreateField(newx, newy, EL_DIAGONAL_GROWING);
6562 Store[newx][newy] = EL_EMC_ANDROID;
6563 GfxElement[newx][newy] = EL_EMC_ANDROID;
6564 GfxAction[newx][newy] = ACTION_GROWING;
6565 GfxDir[newx][newy] = diagonal_move_dir;
6566 ChangeDelay[newx][newy] = change_delay;
6568 graphic = el_act_dir2img(GfxElement[newx][newy],
6569 GfxAction[newx][newy], GfxDir[newx][newy]);
6571 DrawLevelGraphicAnimation(newx, newy, graphic);
6572 PlayLevelSoundAction(newx, newy, ACTION_GROWING);
6578 Feld[newx][newy] = EL_EMPTY;
6579 DrawLevelField(newx, newy);
6581 PlayLevelSoundAction(x, y, ACTION_DIGGING);
6584 else if (!IS_FREE(newx, newy))
6587 if (IS_PLAYER(x, y))
6588 DrawPlayerField(x, y);
6590 DrawLevelField(x, y);
6596 else if (IS_CUSTOM_ELEMENT(element) &&
6597 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6599 int new_element = Feld[newx][newy];
6601 if (!IS_FREE(newx, newy))
6603 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
6604 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
6607 /* no element can dig solid indestructible elements */
6608 if (IS_INDESTRUCTIBLE(new_element) &&
6609 !IS_DIGGABLE(new_element) &&
6610 !IS_COLLECTIBLE(new_element))
6613 if (AmoebaNr[newx][newy] &&
6614 (new_element == EL_AMOEBA_FULL ||
6615 new_element == EL_BD_AMOEBA ||
6616 new_element == EL_AMOEBA_GROWING))
6618 AmoebaCnt[AmoebaNr[newx][newy]]--;
6619 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6622 if (IS_MOVING(newx, newy))
6623 RemoveMovingField(newx, newy);
6626 RemoveField(newx, newy);
6627 DrawLevelField(newx, newy);
6630 /* if digged element was about to explode, prevent the explosion */
6631 ExplodeField[newx][newy] = EX_TYPE_NONE;
6633 PlayLevelSoundAction(x, y, action);
6636 Store[newx][newy] = EL_EMPTY;
6638 /* this makes it possible to leave the removed element again */
6639 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
6640 Store[newx][newy] = new_element;
6642 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
6644 int move_leave_element = element_info[element].move_leave_element;
6646 /* this makes it possible to leave the removed element again */
6647 Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
6648 new_element : move_leave_element);
6652 if (move_pattern & MV_MAZE_RUNNER_STYLE)
6654 RunnerVisit[x][y] = FrameCounter;
6655 PlayerVisit[x][y] /= 8; /* expire player visit path */
6658 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
6660 if (!IS_FREE(newx, newy))
6662 if (IS_PLAYER(x, y))
6663 DrawPlayerField(x, y);
6665 DrawLevelField(x, y);
6671 boolean wanna_flame = !RND(10);
6672 int dx = newx - x, dy = newy - y;
6673 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
6674 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
6675 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
6676 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
6677 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
6678 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
6681 IS_CLASSIC_ENEMY(element1) ||
6682 IS_CLASSIC_ENEMY(element2)) &&
6683 element1 != EL_DRAGON && element2 != EL_DRAGON &&
6684 element1 != EL_FLAMES && element2 != EL_FLAMES)
6686 ResetGfxAnimation(x, y);
6687 GfxAction[x][y] = ACTION_ATTACKING;
6689 if (IS_PLAYER(x, y))
6690 DrawPlayerField(x, y);
6692 DrawLevelField(x, y);
6694 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
6696 MovDelay[x][y] = 50;
6700 RemoveField(newx, newy);
6702 Feld[newx][newy] = EL_FLAMES;
6703 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
6706 RemoveField(newx1, newy1);
6708 Feld[newx1][newy1] = EL_FLAMES;
6710 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
6713 RemoveField(newx2, newy2);
6715 Feld[newx2][newy2] = EL_FLAMES;
6722 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6723 Feld[newx][newy] == EL_DIAMOND)
6725 if (IS_MOVING(newx, newy))
6726 RemoveMovingField(newx, newy);
6729 Feld[newx][newy] = EL_EMPTY;
6730 DrawLevelField(newx, newy);
6733 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
6735 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6736 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
6738 if (AmoebaNr[newx][newy])
6740 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6741 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
6742 Feld[newx][newy] == EL_BD_AMOEBA)
6743 AmoebaCnt[AmoebaNr[newx][newy]]--;
6748 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
6750 RemoveMovingField(newx, newy);
6753 if (IS_MOVING(newx, newy))
6755 RemoveMovingField(newx, newy);
6760 Feld[newx][newy] = EL_EMPTY;
6761 DrawLevelField(newx, newy);
6764 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
6766 else if ((element == EL_PACMAN || element == EL_MOLE)
6767 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
6769 if (AmoebaNr[newx][newy])
6771 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6772 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
6773 Feld[newx][newy] == EL_BD_AMOEBA)
6774 AmoebaCnt[AmoebaNr[newx][newy]]--;
6777 if (element == EL_MOLE)
6779 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
6780 PlayLevelSound(x, y, SND_MOLE_DIGGING);
6782 ResetGfxAnimation(x, y);
6783 GfxAction[x][y] = ACTION_DIGGING;
6784 DrawLevelField(x, y);
6786 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
6788 return; /* wait for shrinking amoeba */
6790 else /* element == EL_PACMAN */
6792 Feld[newx][newy] = EL_EMPTY;
6793 DrawLevelField(newx, newy);
6794 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
6797 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
6798 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
6799 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
6801 /* wait for shrinking amoeba to completely disappear */
6804 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
6806 /* object was running against a wall */
6811 /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
6812 if (move_pattern & MV_ANY_DIRECTION &&
6813 move_pattern == MovDir[x][y])
6815 int blocking_element =
6816 (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
6818 CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
6821 element = Feld[x][y]; /* element might have changed */
6825 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
6826 DrawLevelElementAnimation(x, y, element);
6828 if (DONT_TOUCH(element))
6829 TestIfBadThingTouchesPlayer(x, y);
6834 InitMovingField(x, y, MovDir[x][y]);
6836 PlayLevelSoundAction(x, y, ACTION_MOVING);
6840 ContinueMoving(x, y);
6843 void ContinueMoving(int x, int y)
6845 int element = Feld[x][y];
6846 struct ElementInfo *ei = &element_info[element];
6847 int direction = MovDir[x][y];
6848 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
6849 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
6850 int newx = x + dx, newy = y + dy;
6851 int stored = Store[x][y];
6852 int stored_new = Store[newx][newy];
6853 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
6854 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
6855 boolean last_line = (newy == lev_fieldy - 1);
6857 MovPos[x][y] += getElementMoveStepsize(x, y);
6859 if (pushed_by_player) /* special case: moving object pushed by player */
6860 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
6862 if (ABS(MovPos[x][y]) < TILEX)
6865 int ee = Feld[x][y];
6866 int gg = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
6867 int ff = getGraphicAnimationFrame(gg, GfxFrame[x][y]);
6869 printf("::: %d.%d: moving %d ... [%d, %d, %d] [%d, %d, %d]\n",
6870 x, y, ABS(MovPos[x][y]),
6872 GfxAction[x][y], GfxDir[x][y], GfxFrame[x][y]);
6875 DrawLevelField(x, y);
6877 return; /* element is still moving */
6880 /* element reached destination field */
6882 Feld[x][y] = EL_EMPTY;
6883 Feld[newx][newy] = element;
6884 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
6886 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
6888 element = Feld[newx][newy] = EL_ACID;
6890 else if (element == EL_MOLE)
6892 Feld[x][y] = EL_SAND;
6894 DrawLevelFieldCrumbledSandNeighbours(x, y);
6896 else if (element == EL_QUICKSAND_FILLING)
6898 element = Feld[newx][newy] = get_next_element(element);
6899 Store[newx][newy] = Store[x][y];
6901 else if (element == EL_QUICKSAND_EMPTYING)
6903 Feld[x][y] = get_next_element(element);
6904 element = Feld[newx][newy] = Store[x][y];
6906 else if (element == EL_QUICKSAND_FAST_FILLING)
6908 element = Feld[newx][newy] = get_next_element(element);
6909 Store[newx][newy] = Store[x][y];
6911 else if (element == EL_QUICKSAND_FAST_EMPTYING)
6913 Feld[x][y] = get_next_element(element);
6914 element = Feld[newx][newy] = Store[x][y];
6916 else if (element == EL_MAGIC_WALL_FILLING)
6918 element = Feld[newx][newy] = get_next_element(element);
6919 if (!game.magic_wall_active)
6920 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
6921 Store[newx][newy] = Store[x][y];
6923 else if (element == EL_MAGIC_WALL_EMPTYING)
6925 Feld[x][y] = get_next_element(element);
6926 if (!game.magic_wall_active)
6927 Feld[x][y] = EL_MAGIC_WALL_DEAD;
6928 element = Feld[newx][newy] = Store[x][y];
6930 #if USE_NEW_CUSTOM_VALUE
6931 InitField(newx, newy, FALSE);
6934 else if (element == EL_BD_MAGIC_WALL_FILLING)
6936 element = Feld[newx][newy] = get_next_element(element);
6937 if (!game.magic_wall_active)
6938 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
6939 Store[newx][newy] = Store[x][y];
6941 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
6943 Feld[x][y] = get_next_element(element);
6944 if (!game.magic_wall_active)
6945 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
6946 element = Feld[newx][newy] = Store[x][y];
6948 #if USE_NEW_CUSTOM_VALUE
6949 InitField(newx, newy, FALSE);
6952 else if (element == EL_DC_MAGIC_WALL_FILLING)
6954 element = Feld[newx][newy] = get_next_element(element);
6955 if (!game.magic_wall_active)
6956 element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
6957 Store[newx][newy] = Store[x][y];
6959 else if (element == EL_DC_MAGIC_WALL_EMPTYING)
6961 Feld[x][y] = get_next_element(element);
6962 if (!game.magic_wall_active)
6963 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
6964 element = Feld[newx][newy] = Store[x][y];
6966 #if USE_NEW_CUSTOM_VALUE
6967 InitField(newx, newy, FALSE);
6970 else if (element == EL_AMOEBA_DROPPING)
6972 Feld[x][y] = get_next_element(element);
6973 element = Feld[newx][newy] = Store[x][y];
6975 else if (element == EL_SOKOBAN_OBJECT)
6978 Feld[x][y] = Back[x][y];
6980 if (Back[newx][newy])
6981 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
6983 Back[x][y] = Back[newx][newy] = 0;
6986 Store[x][y] = EL_EMPTY;
6991 MovDelay[newx][newy] = 0;
6993 if (CAN_CHANGE_OR_HAS_ACTION(element))
6995 /* copy element change control values to new field */
6996 ChangeDelay[newx][newy] = ChangeDelay[x][y];
6997 ChangePage[newx][newy] = ChangePage[x][y];
6998 ChangeCount[newx][newy] = ChangeCount[x][y];
6999 ChangeEvent[newx][newy] = ChangeEvent[x][y];
7002 #if USE_NEW_CUSTOM_VALUE
7003 CustomValue[newx][newy] = CustomValue[x][y];
7006 ChangeDelay[x][y] = 0;
7007 ChangePage[x][y] = -1;
7008 ChangeCount[x][y] = 0;
7009 ChangeEvent[x][y] = -1;
7011 #if USE_NEW_CUSTOM_VALUE
7012 CustomValue[x][y] = 0;
7015 /* copy animation control values to new field */
7016 GfxFrame[newx][newy] = GfxFrame[x][y];
7017 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
7018 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
7019 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
7021 Pushed[x][y] = Pushed[newx][newy] = FALSE;
7023 /* some elements can leave other elements behind after moving */
7025 if (ei->move_leave_element != EL_EMPTY &&
7026 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
7027 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
7029 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
7030 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
7031 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
7034 int move_leave_element = ei->move_leave_element;
7038 /* this makes it possible to leave the removed element again */
7039 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
7040 move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
7042 /* this makes it possible to leave the removed element again */
7043 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
7044 move_leave_element = stored;
7047 /* this makes it possible to leave the removed element again */
7048 if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
7049 ei->move_leave_element == EL_TRIGGER_ELEMENT)
7050 move_leave_element = stored;
7053 Feld[x][y] = move_leave_element;
7055 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
7056 MovDir[x][y] = direction;
7058 InitField(x, y, FALSE);
7060 if (GFX_CRUMBLED(Feld[x][y]))
7061 DrawLevelFieldCrumbledSandNeighbours(x, y);
7063 if (ELEM_IS_PLAYER(move_leave_element))
7064 RelocatePlayer(x, y, move_leave_element);
7067 /* do this after checking for left-behind element */
7068 ResetGfxAnimation(x, y); /* reset animation values for old field */
7070 if (!CAN_MOVE(element) ||
7071 (CAN_FALL(element) && direction == MV_DOWN &&
7072 (element == EL_SPRING ||
7073 element_info[element].move_pattern == MV_WHEN_PUSHED ||
7074 element_info[element].move_pattern == MV_WHEN_DROPPED)))
7075 GfxDir[x][y] = MovDir[newx][newy] = 0;
7077 DrawLevelField(x, y);
7078 DrawLevelField(newx, newy);
7080 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
7082 /* prevent pushed element from moving on in pushed direction */
7083 if (pushed_by_player && CAN_MOVE(element) &&
7084 element_info[element].move_pattern & MV_ANY_DIRECTION &&
7085 !(element_info[element].move_pattern & direction))
7086 TurnRound(newx, newy);
7088 /* prevent elements on conveyor belt from moving on in last direction */
7089 if (pushed_by_conveyor && CAN_FALL(element) &&
7090 direction & MV_HORIZONTAL)
7091 MovDir[newx][newy] = 0;
7093 if (!pushed_by_player)
7095 int nextx = newx + dx, nexty = newy + dy;
7096 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
7098 WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
7100 if (CAN_FALL(element) && direction == MV_DOWN)
7101 WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
7103 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
7104 CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
7106 #if USE_FIX_IMPACT_COLLISION
7107 if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
7108 CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
7112 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
7114 TestIfBadThingTouchesPlayer(newx, newy);
7115 TestIfBadThingTouchesFriend(newx, newy);
7117 if (!IS_CUSTOM_ELEMENT(element))
7118 TestIfBadThingTouchesOtherBadThing(newx, newy);
7120 else if (element == EL_PENGUIN)
7121 TestIfFriendTouchesBadThing(newx, newy);
7123 /* give the player one last chance (one more frame) to move away */
7124 if (CAN_FALL(element) && direction == MV_DOWN &&
7125 (last_line || (!IS_FREE(x, newy + 1) &&
7126 (!IS_PLAYER(x, newy + 1) ||
7127 game.engine_version < VERSION_IDENT(3,1,1,0)))))
7130 if (pushed_by_player && !game.use_change_when_pushing_bug)
7132 int push_side = MV_DIR_OPPOSITE(direction);
7133 struct PlayerInfo *player = PLAYERINFO(x, y);
7135 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
7136 player->index_bit, push_side);
7137 CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
7138 player->index_bit, push_side);
7141 if (element == EL_EMC_ANDROID && pushed_by_player) /* make another move */
7142 MovDelay[newx][newy] = 1;
7144 CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
7146 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
7149 if (ChangePage[newx][newy] != -1) /* delayed change */
7151 int page = ChangePage[newx][newy];
7152 struct ElementChangeInfo *change = &ei->change_page[page];
7154 ChangePage[newx][newy] = -1;
7156 if (change->can_change)
7158 if (ChangeElement(newx, newy, element, page))
7160 if (change->post_change_function)
7161 change->post_change_function(newx, newy);
7165 if (change->has_action)
7166 ExecuteCustomElementAction(newx, newy, element, page);
7170 TestIfElementHitsCustomElement(newx, newy, direction);
7171 TestIfPlayerTouchesCustomElement(newx, newy);
7172 TestIfElementTouchesCustomElement(newx, newy);
7174 if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
7175 IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
7176 CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
7177 MV_DIR_OPPOSITE(direction));
7180 int AmoebeNachbarNr(int ax, int ay)
7183 int element = Feld[ax][ay];
7185 static int xy[4][2] =
7193 for (i = 0; i < NUM_DIRECTIONS; i++)
7195 int x = ax + xy[i][0];
7196 int y = ay + xy[i][1];
7198 if (!IN_LEV_FIELD(x, y))
7201 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
7202 group_nr = AmoebaNr[x][y];
7208 void AmoebenVereinigen(int ax, int ay)
7210 int i, x, y, xx, yy;
7211 int new_group_nr = AmoebaNr[ax][ay];
7212 static int xy[4][2] =
7220 if (new_group_nr == 0)
7223 for (i = 0; i < NUM_DIRECTIONS; i++)
7228 if (!IN_LEV_FIELD(x, y))
7231 if ((Feld[x][y] == EL_AMOEBA_FULL ||
7232 Feld[x][y] == EL_BD_AMOEBA ||
7233 Feld[x][y] == EL_AMOEBA_DEAD) &&
7234 AmoebaNr[x][y] != new_group_nr)
7236 int old_group_nr = AmoebaNr[x][y];
7238 if (old_group_nr == 0)
7241 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
7242 AmoebaCnt[old_group_nr] = 0;
7243 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
7244 AmoebaCnt2[old_group_nr] = 0;
7246 SCAN_PLAYFIELD(xx, yy)
7248 if (AmoebaNr[xx][yy] == old_group_nr)
7249 AmoebaNr[xx][yy] = new_group_nr;
7255 void AmoebeUmwandeln(int ax, int ay)
7259 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
7261 int group_nr = AmoebaNr[ax][ay];
7266 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
7267 printf("AmoebeUmwandeln(): This should never happen!\n");
7272 SCAN_PLAYFIELD(x, y)
7274 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
7277 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
7281 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
7282 SND_AMOEBA_TURNING_TO_GEM :
7283 SND_AMOEBA_TURNING_TO_ROCK));
7288 static int xy[4][2] =
7296 for (i = 0; i < NUM_DIRECTIONS; i++)
7301 if (!IN_LEV_FIELD(x, y))
7304 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
7306 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
7307 SND_AMOEBA_TURNING_TO_GEM :
7308 SND_AMOEBA_TURNING_TO_ROCK));
7315 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
7318 int group_nr = AmoebaNr[ax][ay];
7319 boolean done = FALSE;
7324 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
7325 printf("AmoebeUmwandelnBD(): This should never happen!\n");
7330 SCAN_PLAYFIELD(x, y)
7332 if (AmoebaNr[x][y] == group_nr &&
7333 (Feld[x][y] == EL_AMOEBA_DEAD ||
7334 Feld[x][y] == EL_BD_AMOEBA ||
7335 Feld[x][y] == EL_AMOEBA_GROWING))
7338 Feld[x][y] = new_element;
7339 InitField(x, y, FALSE);
7340 DrawLevelField(x, y);
7346 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
7347 SND_BD_AMOEBA_TURNING_TO_ROCK :
7348 SND_BD_AMOEBA_TURNING_TO_GEM));
7351 void AmoebeWaechst(int x, int y)
7353 static unsigned long sound_delay = 0;
7354 static unsigned long sound_delay_value = 0;
7356 if (!MovDelay[x][y]) /* start new growing cycle */
7360 if (DelayReached(&sound_delay, sound_delay_value))
7362 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
7363 sound_delay_value = 30;
7367 if (MovDelay[x][y]) /* wait some time before growing bigger */
7370 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7372 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
7373 6 - MovDelay[x][y]);
7375 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
7378 if (!MovDelay[x][y])
7380 Feld[x][y] = Store[x][y];
7382 DrawLevelField(x, y);
7387 void AmoebaDisappearing(int x, int y)
7389 static unsigned long sound_delay = 0;
7390 static unsigned long sound_delay_value = 0;
7392 if (!MovDelay[x][y]) /* start new shrinking cycle */
7396 if (DelayReached(&sound_delay, sound_delay_value))
7397 sound_delay_value = 30;
7400 if (MovDelay[x][y]) /* wait some time before shrinking */
7403 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7405 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
7406 6 - MovDelay[x][y]);
7408 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
7411 if (!MovDelay[x][y])
7413 Feld[x][y] = EL_EMPTY;
7414 DrawLevelField(x, y);
7416 /* don't let mole enter this field in this cycle;
7417 (give priority to objects falling to this field from above) */
7423 void AmoebeAbleger(int ax, int ay)
7426 int element = Feld[ax][ay];
7427 int graphic = el2img(element);
7428 int newax = ax, neway = ay;
7429 boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
7430 static int xy[4][2] =
7438 if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
7440 Feld[ax][ay] = EL_AMOEBA_DEAD;
7441 DrawLevelField(ax, ay);
7445 if (IS_ANIMATED(graphic))
7446 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7448 if (!MovDelay[ax][ay]) /* start making new amoeba field */
7449 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
7451 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
7454 if (MovDelay[ax][ay])
7458 if (can_drop) /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
7461 int x = ax + xy[start][0];
7462 int y = ay + xy[start][1];
7464 if (!IN_LEV_FIELD(x, y))
7467 if (IS_FREE(x, y) ||
7468 CAN_GROW_INTO(Feld[x][y]) ||
7469 Feld[x][y] == EL_QUICKSAND_EMPTY ||
7470 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
7476 if (newax == ax && neway == ay)
7479 else /* normal or "filled" (BD style) amoeba */
7482 boolean waiting_for_player = FALSE;
7484 for (i = 0; i < NUM_DIRECTIONS; i++)
7486 int j = (start + i) % 4;
7487 int x = ax + xy[j][0];
7488 int y = ay + xy[j][1];
7490 if (!IN_LEV_FIELD(x, y))
7493 if (IS_FREE(x, y) ||
7494 CAN_GROW_INTO(Feld[x][y]) ||
7495 Feld[x][y] == EL_QUICKSAND_EMPTY ||
7496 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
7502 else if (IS_PLAYER(x, y))
7503 waiting_for_player = TRUE;
7506 if (newax == ax && neway == ay) /* amoeba cannot grow */
7508 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
7510 Feld[ax][ay] = EL_AMOEBA_DEAD;
7511 DrawLevelField(ax, ay);
7512 AmoebaCnt[AmoebaNr[ax][ay]]--;
7514 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
7516 if (element == EL_AMOEBA_FULL)
7517 AmoebeUmwandeln(ax, ay);
7518 else if (element == EL_BD_AMOEBA)
7519 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
7524 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
7526 /* amoeba gets larger by growing in some direction */
7528 int new_group_nr = AmoebaNr[ax][ay];
7531 if (new_group_nr == 0)
7533 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
7534 printf("AmoebeAbleger(): This should never happen!\n");
7539 AmoebaNr[newax][neway] = new_group_nr;
7540 AmoebaCnt[new_group_nr]++;
7541 AmoebaCnt2[new_group_nr]++;
7543 /* if amoeba touches other amoeba(s) after growing, unify them */
7544 AmoebenVereinigen(newax, neway);
7546 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
7548 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
7554 if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
7555 (neway == lev_fieldy - 1 && newax != ax))
7557 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
7558 Store[newax][neway] = element;
7560 else if (neway == ay || element == EL_EMC_DRIPPER)
7562 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
7564 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
7568 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
7569 Feld[ax][ay] = EL_AMOEBA_DROPPING;
7570 Store[ax][ay] = EL_AMOEBA_DROP;
7571 ContinueMoving(ax, ay);
7575 DrawLevelField(newax, neway);
7578 void Life(int ax, int ay)
7582 int element = Feld[ax][ay];
7583 int graphic = el2img(element);
7584 int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
7586 boolean changed = FALSE;
7588 if (IS_ANIMATED(graphic))
7589 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7594 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
7595 MovDelay[ax][ay] = life_time;
7597 if (MovDelay[ax][ay]) /* wait some time before next cycle */
7600 if (MovDelay[ax][ay])
7604 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
7606 int xx = ax+x1, yy = ay+y1;
7609 if (!IN_LEV_FIELD(xx, yy))
7612 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
7614 int x = xx+x2, y = yy+y2;
7616 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
7619 if (((Feld[x][y] == element ||
7620 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
7622 (IS_FREE(x, y) && Stop[x][y]))
7626 if (xx == ax && yy == ay) /* field in the middle */
7628 if (nachbarn < life_parameter[0] ||
7629 nachbarn > life_parameter[1])
7631 Feld[xx][yy] = EL_EMPTY;
7633 DrawLevelField(xx, yy);
7634 Stop[xx][yy] = TRUE;
7638 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
7639 { /* free border field */
7640 if (nachbarn >= life_parameter[2] &&
7641 nachbarn <= life_parameter[3])
7643 Feld[xx][yy] = element;
7644 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
7646 DrawLevelField(xx, yy);
7647 Stop[xx][yy] = TRUE;
7654 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
7655 SND_GAME_OF_LIFE_GROWING);
7658 static void InitRobotWheel(int x, int y)
7660 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
7663 static void RunRobotWheel(int x, int y)
7665 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
7668 static void StopRobotWheel(int x, int y)
7670 if (ZX == x && ZY == y)
7674 static void InitTimegateWheel(int x, int y)
7676 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
7679 static void RunTimegateWheel(int x, int y)
7681 PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
7684 static void InitMagicBallDelay(int x, int y)
7687 ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
7689 ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
7693 static void ActivateMagicBall(int bx, int by)
7697 if (level.ball_random)
7699 int pos_border = RND(8); /* select one of the eight border elements */
7700 int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
7701 int xx = pos_content % 3;
7702 int yy = pos_content / 3;
7707 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
7708 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
7712 for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
7714 int xx = x - bx + 1;
7715 int yy = y - by + 1;
7717 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
7718 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
7722 game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
7725 void CheckExit(int x, int y)
7727 if (local_player->gems_still_needed > 0 ||
7728 local_player->sokobanfields_still_needed > 0 ||
7729 local_player->lights_still_needed > 0)
7731 int element = Feld[x][y];
7732 int graphic = el2img(element);
7734 if (IS_ANIMATED(graphic))
7735 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7740 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7743 Feld[x][y] = EL_EXIT_OPENING;
7745 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
7748 void CheckExitEM(int x, int y)
7750 if (local_player->gems_still_needed > 0 ||
7751 local_player->sokobanfields_still_needed > 0 ||
7752 local_player->lights_still_needed > 0)
7754 int element = Feld[x][y];
7755 int graphic = el2img(element);
7757 if (IS_ANIMATED(graphic))
7758 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7763 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7766 Feld[x][y] = EL_EM_EXIT_OPENING;
7768 PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
7771 void CheckExitSteel(int x, int y)
7773 if (local_player->gems_still_needed > 0 ||
7774 local_player->sokobanfields_still_needed > 0 ||
7775 local_player->lights_still_needed > 0)
7777 int element = Feld[x][y];
7778 int graphic = el2img(element);
7780 if (IS_ANIMATED(graphic))
7781 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7786 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7789 Feld[x][y] = EL_STEEL_EXIT_OPENING;
7791 PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
7794 void CheckExitSteelEM(int x, int y)
7796 if (local_player->gems_still_needed > 0 ||
7797 local_player->sokobanfields_still_needed > 0 ||
7798 local_player->lights_still_needed > 0)
7800 int element = Feld[x][y];
7801 int graphic = el2img(element);
7803 if (IS_ANIMATED(graphic))
7804 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7809 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7812 Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
7814 PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
7817 void CheckExitSP(int x, int y)
7819 if (local_player->gems_still_needed > 0)
7821 int element = Feld[x][y];
7822 int graphic = el2img(element);
7824 if (IS_ANIMATED(graphic))
7825 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7830 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7833 Feld[x][y] = EL_SP_EXIT_OPENING;
7835 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
7838 static void CloseAllOpenTimegates()
7842 SCAN_PLAYFIELD(x, y)
7844 int element = Feld[x][y];
7846 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
7848 Feld[x][y] = EL_TIMEGATE_CLOSING;
7850 PlayLevelSoundAction(x, y, ACTION_CLOSING);
7855 void DrawTwinkleOnField(int x, int y)
7857 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
7860 if (Feld[x][y] == EL_BD_DIAMOND)
7863 if (MovDelay[x][y] == 0) /* next animation frame */
7864 MovDelay[x][y] = 11 * !GetSimpleRandom(500);
7866 if (MovDelay[x][y] != 0) /* wait some time before next frame */
7870 if (setup.direct_draw && MovDelay[x][y])
7871 SetDrawtoField(DRAW_BUFFERED);
7873 DrawLevelElementAnimation(x, y, Feld[x][y]);
7875 if (MovDelay[x][y] != 0)
7877 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
7878 10 - MovDelay[x][y]);
7880 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
7882 if (setup.direct_draw)
7886 dest_x = FX + SCREENX(x) * TILEX;
7887 dest_y = FY + SCREENY(y) * TILEY;
7889 BlitBitmap(drawto_field, window,
7890 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
7891 SetDrawtoField(DRAW_DIRECT);
7897 void MauerWaechst(int x, int y)
7901 if (!MovDelay[x][y]) /* next animation frame */
7902 MovDelay[x][y] = 3 * delay;
7904 if (MovDelay[x][y]) /* wait some time before next frame */
7908 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7910 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
7911 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
7913 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
7916 if (!MovDelay[x][y])
7918 if (MovDir[x][y] == MV_LEFT)
7920 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
7921 DrawLevelField(x - 1, y);
7923 else if (MovDir[x][y] == MV_RIGHT)
7925 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
7926 DrawLevelField(x + 1, y);
7928 else if (MovDir[x][y] == MV_UP)
7930 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
7931 DrawLevelField(x, y - 1);
7935 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
7936 DrawLevelField(x, y + 1);
7939 Feld[x][y] = Store[x][y];
7941 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7942 DrawLevelField(x, y);
7947 void MauerAbleger(int ax, int ay)
7949 int element = Feld[ax][ay];
7950 int graphic = el2img(element);
7951 boolean oben_frei = FALSE, unten_frei = FALSE;
7952 boolean links_frei = FALSE, rechts_frei = FALSE;
7953 boolean oben_massiv = FALSE, unten_massiv = FALSE;
7954 boolean links_massiv = FALSE, rechts_massiv = FALSE;
7955 boolean new_wall = FALSE;
7957 if (IS_ANIMATED(graphic))
7958 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7960 if (!MovDelay[ax][ay]) /* start building new wall */
7961 MovDelay[ax][ay] = 6;
7963 if (MovDelay[ax][ay]) /* wait some time before building new wall */
7966 if (MovDelay[ax][ay])
7970 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
7972 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
7974 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
7976 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
7979 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
7980 element == EL_EXPANDABLE_WALL_ANY)
7984 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
7985 Store[ax][ay-1] = element;
7986 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
7987 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
7988 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
7989 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
7994 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
7995 Store[ax][ay+1] = element;
7996 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
7997 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
7998 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
7999 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
8004 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
8005 element == EL_EXPANDABLE_WALL_ANY ||
8006 element == EL_EXPANDABLE_WALL ||
8007 element == EL_BD_EXPANDABLE_WALL)
8011 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
8012 Store[ax-1][ay] = element;
8013 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
8014 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
8015 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
8016 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
8022 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
8023 Store[ax+1][ay] = element;
8024 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
8025 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
8026 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
8027 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
8032 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
8033 DrawLevelField(ax, ay);
8035 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
8037 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
8038 unten_massiv = TRUE;
8039 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
8040 links_massiv = TRUE;
8041 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
8042 rechts_massiv = TRUE;
8044 if (((oben_massiv && unten_massiv) ||
8045 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
8046 element == EL_EXPANDABLE_WALL) &&
8047 ((links_massiv && rechts_massiv) ||
8048 element == EL_EXPANDABLE_WALL_VERTICAL))
8049 Feld[ax][ay] = EL_WALL;
8052 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
8055 void MauerAblegerStahl(int ax, int ay)
8057 int element = Feld[ax][ay];
8058 int graphic = el2img(element);
8059 boolean oben_frei = FALSE, unten_frei = FALSE;
8060 boolean links_frei = FALSE, rechts_frei = FALSE;
8061 boolean oben_massiv = FALSE, unten_massiv = FALSE;
8062 boolean links_massiv = FALSE, rechts_massiv = FALSE;
8063 boolean new_wall = FALSE;
8065 if (IS_ANIMATED(graphic))
8066 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8068 if (!MovDelay[ax][ay]) /* start building new wall */
8069 MovDelay[ax][ay] = 6;
8071 if (MovDelay[ax][ay]) /* wait some time before building new wall */
8074 if (MovDelay[ax][ay])
8078 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
8080 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
8082 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
8084 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
8087 if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
8088 element == EL_EXPANDABLE_STEELWALL_ANY)
8092 Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
8093 Store[ax][ay-1] = element;
8094 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
8095 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
8096 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
8097 IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
8102 Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
8103 Store[ax][ay+1] = element;
8104 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
8105 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
8106 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
8107 IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
8112 if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
8113 element == EL_EXPANDABLE_STEELWALL_ANY)
8117 Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
8118 Store[ax-1][ay] = element;
8119 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
8120 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
8121 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
8122 IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
8128 Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
8129 Store[ax+1][ay] = element;
8130 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
8131 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
8132 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
8133 IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
8138 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
8140 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
8141 unten_massiv = TRUE;
8142 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
8143 links_massiv = TRUE;
8144 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
8145 rechts_massiv = TRUE;
8147 if (((oben_massiv && unten_massiv) ||
8148 element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
8149 ((links_massiv && rechts_massiv) ||
8150 element == EL_EXPANDABLE_STEELWALL_VERTICAL))
8151 Feld[ax][ay] = EL_WALL;
8154 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
8157 void CheckForDragon(int x, int y)
8160 boolean dragon_found = FALSE;
8161 static int xy[4][2] =
8169 for (i = 0; i < NUM_DIRECTIONS; i++)
8171 for (j = 0; j < 4; j++)
8173 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
8175 if (IN_LEV_FIELD(xx, yy) &&
8176 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
8178 if (Feld[xx][yy] == EL_DRAGON)
8179 dragon_found = TRUE;
8188 for (i = 0; i < NUM_DIRECTIONS; i++)
8190 for (j = 0; j < 3; j++)
8192 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
8194 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
8196 Feld[xx][yy] = EL_EMPTY;
8197 DrawLevelField(xx, yy);
8206 static void InitBuggyBase(int x, int y)
8208 int element = Feld[x][y];
8209 int activating_delay = FRAMES_PER_SECOND / 4;
8212 (element == EL_SP_BUGGY_BASE ?
8213 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
8214 element == EL_SP_BUGGY_BASE_ACTIVATING ?
8216 element == EL_SP_BUGGY_BASE_ACTIVE ?
8217 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
8220 static void WarnBuggyBase(int x, int y)
8223 static int xy[4][2] =
8231 for (i = 0; i < NUM_DIRECTIONS; i++)
8233 int xx = x + xy[i][0];
8234 int yy = y + xy[i][1];
8236 if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
8238 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
8245 static void InitTrap(int x, int y)
8247 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
8250 static void ActivateTrap(int x, int y)
8252 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
8255 static void ChangeActiveTrap(int x, int y)
8257 int graphic = IMG_TRAP_ACTIVE;
8259 /* if new animation frame was drawn, correct crumbled sand border */
8260 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
8261 DrawLevelFieldCrumbledSand(x, y);
8264 static int getSpecialActionElement(int element, int number, int base_element)
8266 return (element != EL_EMPTY ? element :
8267 number != -1 ? base_element + number - 1 :
8271 static int getModifiedActionNumber(int value_old, int operator, int operand,
8272 int value_min, int value_max)
8274 int value_new = (operator == CA_MODE_SET ? operand :
8275 operator == CA_MODE_ADD ? value_old + operand :
8276 operator == CA_MODE_SUBTRACT ? value_old - operand :
8277 operator == CA_MODE_MULTIPLY ? value_old * operand :
8278 operator == CA_MODE_DIVIDE ? value_old / MAX(1, operand) :
8279 operator == CA_MODE_MODULO ? value_old % MAX(1, operand) :
8282 return (value_new < value_min ? value_min :
8283 value_new > value_max ? value_max :
8287 static void ExecuteCustomElementAction(int x, int y, int element, int page)
8289 struct ElementInfo *ei = &element_info[element];
8290 struct ElementChangeInfo *change = &ei->change_page[page];
8291 int target_element = change->target_element;
8292 int action_type = change->action_type;
8293 int action_mode = change->action_mode;
8294 int action_arg = change->action_arg;
8297 if (!change->has_action)
8300 /* ---------- determine action paramater values -------------------------- */
8302 int level_time_value =
8303 (level.time > 0 ? TimeLeft :
8306 int action_arg_element =
8307 (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player :
8308 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
8309 action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element :
8312 int action_arg_direction =
8313 (action_arg >= CA_ARG_DIRECTION_LEFT &&
8314 action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
8315 action_arg == CA_ARG_DIRECTION_TRIGGER ?
8316 change->actual_trigger_side :
8317 action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
8318 MV_DIR_OPPOSITE(change->actual_trigger_side) :
8321 int action_arg_number_min =
8322 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
8325 int action_arg_number_max =
8326 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
8327 action_type == CA_SET_LEVEL_GEMS ? 999 :
8328 action_type == CA_SET_LEVEL_TIME ? 9999 :
8329 action_type == CA_SET_LEVEL_SCORE ? 99999 :
8330 action_type == CA_SET_CE_VALUE ? 9999 :
8331 action_type == CA_SET_CE_SCORE ? 9999 :
8334 int action_arg_number_reset =
8335 (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
8336 action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
8337 action_type == CA_SET_LEVEL_TIME ? level.time :
8338 action_type == CA_SET_LEVEL_SCORE ? 0 :
8339 #if USE_NEW_CUSTOM_VALUE
8340 action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
8342 action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
8344 action_type == CA_SET_CE_SCORE ? 0 :
8347 int action_arg_number =
8348 (action_arg <= CA_ARG_MAX ? action_arg :
8349 action_arg >= CA_ARG_SPEED_NOT_MOVING &&
8350 action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
8351 action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
8352 action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
8353 action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
8354 action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
8355 #if USE_NEW_CUSTOM_VALUE
8356 action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
8358 action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
8360 action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
8361 action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
8362 action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
8363 action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
8364 action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
8365 action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
8366 action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
8367 action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
8368 action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
8369 action_arg == CA_ARG_ELEMENT_NR_TARGET ? change->target_element :
8370 action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
8373 int action_arg_number_old =
8374 (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
8375 action_type == CA_SET_LEVEL_TIME ? TimeLeft :
8376 action_type == CA_SET_LEVEL_SCORE ? local_player->score :
8377 action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
8378 action_type == CA_SET_CE_SCORE ? ei->collect_score :
8381 int action_arg_number_new =
8382 getModifiedActionNumber(action_arg_number_old,
8383 action_mode, action_arg_number,
8384 action_arg_number_min, action_arg_number_max);
8386 int trigger_player_bits =
8387 (change->actual_trigger_player >= EL_PLAYER_1 &&
8388 change->actual_trigger_player <= EL_PLAYER_4 ?
8389 (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
8392 int action_arg_player_bits =
8393 (action_arg >= CA_ARG_PLAYER_1 &&
8394 action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
8395 action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
8398 /* ---------- execute action -------------------------------------------- */
8400 switch (action_type)
8407 /* ---------- level actions ------------------------------------------- */
8409 case CA_RESTART_LEVEL:
8411 game.restart_level = TRUE;
8416 case CA_SHOW_ENVELOPE:
8418 int element = getSpecialActionElement(action_arg_element,
8419 action_arg_number, EL_ENVELOPE_1);
8421 if (IS_ENVELOPE(element))
8422 local_player->show_envelope = element;
8427 case CA_SET_LEVEL_TIME:
8429 if (level.time > 0) /* only modify limited time value */
8431 TimeLeft = action_arg_number_new;
8433 DrawGameValue_Time(TimeLeft);
8435 if (!TimeLeft && setup.time_limit)
8436 for (i = 0; i < MAX_PLAYERS; i++)
8437 KillPlayer(&stored_player[i]);
8443 case CA_SET_LEVEL_SCORE:
8445 local_player->score = action_arg_number_new;
8447 DrawGameValue_Score(local_player->score);
8452 case CA_SET_LEVEL_GEMS:
8454 local_player->gems_still_needed = action_arg_number_new;
8456 DrawGameValue_Emeralds(local_player->gems_still_needed);
8461 #if !USE_PLAYER_GRAVITY
8462 case CA_SET_LEVEL_GRAVITY:
8464 game.gravity = (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
8465 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
8466 action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
8472 case CA_SET_LEVEL_WIND:
8474 game.wind_direction = action_arg_direction;
8479 /* ---------- player actions ------------------------------------------ */
8481 case CA_MOVE_PLAYER:
8483 /* automatically move to the next field in specified direction */
8484 for (i = 0; i < MAX_PLAYERS; i++)
8485 if (trigger_player_bits & (1 << i))
8486 stored_player[i].programmed_action = action_arg_direction;
8491 case CA_EXIT_PLAYER:
8493 for (i = 0; i < MAX_PLAYERS; i++)
8494 if (action_arg_player_bits & (1 << i))
8495 PlayerWins(&stored_player[i]);
8500 case CA_KILL_PLAYER:
8502 for (i = 0; i < MAX_PLAYERS; i++)
8503 if (action_arg_player_bits & (1 << i))
8504 KillPlayer(&stored_player[i]);
8509 case CA_SET_PLAYER_KEYS:
8511 int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
8512 int element = getSpecialActionElement(action_arg_element,
8513 action_arg_number, EL_KEY_1);
8515 if (IS_KEY(element))
8517 for (i = 0; i < MAX_PLAYERS; i++)
8519 if (trigger_player_bits & (1 << i))
8521 stored_player[i].key[KEY_NR(element)] = key_state;
8523 DrawGameDoorValues();
8531 case CA_SET_PLAYER_SPEED:
8533 for (i = 0; i < MAX_PLAYERS; i++)
8535 if (trigger_player_bits & (1 << i))
8537 int move_stepsize = TILEX / stored_player[i].move_delay_value;
8539 if (action_arg == CA_ARG_SPEED_FASTER &&
8540 stored_player[i].cannot_move)
8542 action_arg_number = STEPSIZE_VERY_SLOW;
8544 else if (action_arg == CA_ARG_SPEED_SLOWER ||
8545 action_arg == CA_ARG_SPEED_FASTER)
8547 action_arg_number = 2;
8548 action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
8551 else if (action_arg == CA_ARG_NUMBER_RESET)
8553 action_arg_number = level.initial_player_stepsize[i];
8557 getModifiedActionNumber(move_stepsize,
8560 action_arg_number_min,
8561 action_arg_number_max);
8563 SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
8570 case CA_SET_PLAYER_SHIELD:
8572 for (i = 0; i < MAX_PLAYERS; i++)
8574 if (trigger_player_bits & (1 << i))
8576 if (action_arg == CA_ARG_SHIELD_OFF)
8578 stored_player[i].shield_normal_time_left = 0;
8579 stored_player[i].shield_deadly_time_left = 0;
8581 else if (action_arg == CA_ARG_SHIELD_NORMAL)
8583 stored_player[i].shield_normal_time_left = 999999;
8585 else if (action_arg == CA_ARG_SHIELD_DEADLY)
8587 stored_player[i].shield_normal_time_left = 999999;
8588 stored_player[i].shield_deadly_time_left = 999999;
8596 #if USE_PLAYER_GRAVITY
8597 case CA_SET_PLAYER_GRAVITY:
8599 for (i = 0; i < MAX_PLAYERS; i++)
8601 if (trigger_player_bits & (1 << i))
8603 stored_player[i].gravity =
8604 (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
8605 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
8606 action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
8607 stored_player[i].gravity);
8615 case CA_SET_PLAYER_ARTWORK:
8617 for (i = 0; i < MAX_PLAYERS; i++)
8619 if (trigger_player_bits & (1 << i))
8621 int artwork_element = action_arg_element;
8623 if (action_arg == CA_ARG_ELEMENT_RESET)
8625 (level.use_artwork_element[i] ? level.artwork_element[i] :
8626 stored_player[i].element_nr);
8628 #if USE_GFX_RESET_PLAYER_ARTWORK
8629 if (stored_player[i].artwork_element != artwork_element)
8630 stored_player[i].Frame = 0;
8633 stored_player[i].artwork_element = artwork_element;
8635 SetPlayerWaiting(&stored_player[i], FALSE);
8637 /* set number of special actions for bored and sleeping animation */
8638 stored_player[i].num_special_action_bored =
8639 get_num_special_action(artwork_element,
8640 ACTION_BORING_1, ACTION_BORING_LAST);
8641 stored_player[i].num_special_action_sleeping =
8642 get_num_special_action(artwork_element,
8643 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
8650 /* ---------- CE actions ---------------------------------------------- */
8652 case CA_SET_CE_VALUE:
8654 #if USE_NEW_CUSTOM_VALUE
8655 int last_ce_value = CustomValue[x][y];
8657 CustomValue[x][y] = action_arg_number_new;
8659 if (CustomValue[x][y] != last_ce_value)
8661 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
8662 CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
8664 if (CustomValue[x][y] == 0)
8666 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
8667 CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
8675 case CA_SET_CE_SCORE:
8677 #if USE_NEW_CUSTOM_VALUE
8678 int last_ce_score = ei->collect_score;
8680 ei->collect_score = action_arg_number_new;
8682 if (ei->collect_score != last_ce_score)
8684 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
8685 CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
8687 if (ei->collect_score == 0)
8691 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
8692 CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
8695 This is a very special case that seems to be a mixture between
8696 CheckElementChange() and CheckTriggeredElementChange(): while
8697 the first one only affects single elements that are triggered
8698 directly, the second one affects multiple elements in the playfield
8699 that are triggered indirectly by another element. This is a third
8700 case: Changing the CE score always affects multiple identical CEs,
8701 so every affected CE must be checked, not only the single CE for
8702 which the CE score was changed in the first place (as every instance
8703 of that CE shares the same CE score, and therefore also can change)!
8705 SCAN_PLAYFIELD(xx, yy)
8707 if (Feld[xx][yy] == element)
8708 CheckElementChange(xx, yy, element, EL_UNDEFINED,
8709 CE_SCORE_GETS_ZERO);
8718 /* ---------- engine actions ------------------------------------------ */
8720 case CA_SET_ENGINE_SCAN_MODE:
8722 InitPlayfieldScanMode(action_arg);
8732 static void CreateFieldExt(int x, int y, int element, boolean is_change)
8734 int old_element = Feld[x][y];
8735 int new_element = get_element_from_group_element(element);
8736 int previous_move_direction = MovDir[x][y];
8737 #if USE_NEW_CUSTOM_VALUE
8738 int last_ce_value = CustomValue[x][y];
8740 boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
8741 boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
8742 boolean add_player_onto_element = (new_element_is_player &&
8743 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
8744 /* this breaks SnakeBite when a snake is
8745 halfway through a door that closes */
8746 /* NOW FIXED AT LEVEL INIT IN files.c */
8747 new_element != EL_SOKOBAN_FIELD_PLAYER &&
8749 IS_WALKABLE(old_element));
8752 /* check if element under the player changes from accessible to unaccessible
8753 (needed for special case of dropping element which then changes) */
8754 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
8755 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
8763 if (!add_player_onto_element)
8765 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
8766 RemoveMovingField(x, y);
8770 Feld[x][y] = new_element;
8772 #if !USE_GFX_RESET_GFX_ANIMATION
8773 ResetGfxAnimation(x, y);
8774 ResetRandomAnimationValue(x, y);
8777 if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
8778 MovDir[x][y] = previous_move_direction;
8780 #if USE_NEW_CUSTOM_VALUE
8781 if (element_info[new_element].use_last_ce_value)
8782 CustomValue[x][y] = last_ce_value;
8785 InitField_WithBug1(x, y, FALSE);
8787 new_element = Feld[x][y]; /* element may have changed */
8789 #if USE_GFX_RESET_GFX_ANIMATION
8790 ResetGfxAnimation(x, y);
8791 ResetRandomAnimationValue(x, y);
8794 DrawLevelField(x, y);
8796 if (GFX_CRUMBLED(new_element))
8797 DrawLevelFieldCrumbledSandNeighbours(x, y);
8801 /* check if element under the player changes from accessible to unaccessible
8802 (needed for special case of dropping element which then changes) */
8803 /* (must be checked after creating new element for walkable group elements) */
8804 #if USE_FIX_KILLED_BY_NON_WALKABLE
8805 if (IS_PLAYER(x, y) && !player_explosion_protected &&
8806 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
8813 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
8814 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
8823 /* "ChangeCount" not set yet to allow "entered by player" change one time */
8824 if (new_element_is_player)
8825 RelocatePlayer(x, y, new_element);
8828 ChangeCount[x][y]++; /* count number of changes in the same frame */
8830 TestIfBadThingTouchesPlayer(x, y);
8831 TestIfPlayerTouchesCustomElement(x, y);
8832 TestIfElementTouchesCustomElement(x, y);
8835 static void CreateField(int x, int y, int element)
8837 CreateFieldExt(x, y, element, FALSE);
8840 static void CreateElementFromChange(int x, int y, int element)
8842 element = GET_VALID_RUNTIME_ELEMENT(element);
8844 #if USE_STOP_CHANGED_ELEMENTS
8845 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
8847 int old_element = Feld[x][y];
8849 /* prevent changed element from moving in same engine frame
8850 unless both old and new element can either fall or move */
8851 if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
8852 (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
8857 CreateFieldExt(x, y, element, TRUE);
8860 static boolean ChangeElement(int x, int y, int element, int page)
8862 struct ElementInfo *ei = &element_info[element];
8863 struct ElementChangeInfo *change = &ei->change_page[page];
8864 int ce_value = CustomValue[x][y];
8865 int ce_score = ei->collect_score;
8867 int old_element = Feld[x][y];
8869 /* always use default change event to prevent running into a loop */
8870 if (ChangeEvent[x][y] == -1)
8871 ChangeEvent[x][y] = CE_DELAY;
8873 if (ChangeEvent[x][y] == CE_DELAY)
8875 /* reset actual trigger element, trigger player and action element */
8876 change->actual_trigger_element = EL_EMPTY;
8877 change->actual_trigger_player = EL_PLAYER_1;
8878 change->actual_trigger_side = CH_SIDE_NONE;
8879 change->actual_trigger_ce_value = 0;
8880 change->actual_trigger_ce_score = 0;
8883 /* do not change elements more than a specified maximum number of changes */
8884 if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
8887 ChangeCount[x][y]++; /* count number of changes in the same frame */
8889 if (change->explode)
8896 if (change->use_target_content)
8898 boolean complete_replace = TRUE;
8899 boolean can_replace[3][3];
8902 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
8905 boolean is_walkable;
8906 boolean is_diggable;
8907 boolean is_collectible;
8908 boolean is_removable;
8909 boolean is_destructible;
8910 int ex = x + xx - 1;
8911 int ey = y + yy - 1;
8912 int content_element = change->target_content.e[xx][yy];
8915 can_replace[xx][yy] = TRUE;
8917 if (ex == x && ey == y) /* do not check changing element itself */
8920 if (content_element == EL_EMPTY_SPACE)
8922 can_replace[xx][yy] = FALSE; /* do not replace border with space */
8927 if (!IN_LEV_FIELD(ex, ey))
8929 can_replace[xx][yy] = FALSE;
8930 complete_replace = FALSE;
8937 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
8938 e = MovingOrBlocked2Element(ex, ey);
8940 is_empty = (IS_FREE(ex, ey) ||
8941 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
8943 is_walkable = (is_empty || IS_WALKABLE(e));
8944 is_diggable = (is_empty || IS_DIGGABLE(e));
8945 is_collectible = (is_empty || IS_COLLECTIBLE(e));
8946 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
8947 is_removable = (is_diggable || is_collectible);
8949 can_replace[xx][yy] =
8950 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
8951 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
8952 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
8953 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
8954 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
8955 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
8956 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
8958 if (!can_replace[xx][yy])
8959 complete_replace = FALSE;
8962 if (!change->only_if_complete || complete_replace)
8964 boolean something_has_changed = FALSE;
8966 if (change->only_if_complete && change->use_random_replace &&
8967 RND(100) < change->random_percentage)
8970 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
8972 int ex = x + xx - 1;
8973 int ey = y + yy - 1;
8974 int content_element;
8976 if (can_replace[xx][yy] && (!change->use_random_replace ||
8977 RND(100) < change->random_percentage))
8979 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
8980 RemoveMovingField(ex, ey);
8982 ChangeEvent[ex][ey] = ChangeEvent[x][y];
8984 content_element = change->target_content.e[xx][yy];
8985 target_element = GET_TARGET_ELEMENT(element, content_element, change,
8986 ce_value, ce_score);
8988 CreateElementFromChange(ex, ey, target_element);
8990 something_has_changed = TRUE;
8992 /* for symmetry reasons, freeze newly created border elements */
8993 if (ex != x || ey != y)
8994 Stop[ex][ey] = TRUE; /* no more moving in this frame */
8998 if (something_has_changed)
9000 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
9001 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
9007 target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
9008 ce_value, ce_score);
9010 if (element == EL_DIAGONAL_GROWING ||
9011 element == EL_DIAGONAL_SHRINKING)
9013 target_element = Store[x][y];
9015 Store[x][y] = EL_EMPTY;
9018 CreateElementFromChange(x, y, target_element);
9020 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
9021 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
9024 /* this uses direct change before indirect change */
9025 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
9030 #if USE_NEW_DELAYED_ACTION
9032 static void HandleElementChange(int x, int y, int page)
9034 int element = MovingOrBlocked2Element(x, y);
9035 struct ElementInfo *ei = &element_info[element];
9036 struct ElementChangeInfo *change = &ei->change_page[page];
9039 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
9040 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
9043 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
9044 x, y, element, element_info[element].token_name);
9045 printf("HandleElementChange(): This should never happen!\n");
9050 /* this can happen with classic bombs on walkable, changing elements */
9051 if (!CAN_CHANGE_OR_HAS_ACTION(element))
9054 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
9055 ChangeDelay[x][y] = 0;
9061 if (ChangeDelay[x][y] == 0) /* initialize element change */
9063 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
9065 if (change->can_change)
9068 /* !!! not clear why graphic animation should be reset at all here !!! */
9069 #if USE_GFX_RESET_WHEN_NOT_MOVING
9070 /* when a custom element is about to change (for example by change delay),
9071 do not reset graphic animation when the custom element is moving */
9072 if (IS_MOVING(x, y))
9075 ResetGfxAnimation(x, y);
9076 ResetRandomAnimationValue(x, y);
9080 if (change->pre_change_function)
9081 change->pre_change_function(x, y);
9085 ChangeDelay[x][y]--;
9087 if (ChangeDelay[x][y] != 0) /* continue element change */
9089 if (change->can_change)
9091 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9093 if (IS_ANIMATED(graphic))
9094 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9096 if (change->change_function)
9097 change->change_function(x, y);
9100 else /* finish element change */
9102 if (ChangePage[x][y] != -1) /* remember page from delayed change */
9104 page = ChangePage[x][y];
9105 ChangePage[x][y] = -1;
9107 change = &ei->change_page[page];
9110 if (IS_MOVING(x, y)) /* never change a running system ;-) */
9112 ChangeDelay[x][y] = 1; /* try change after next move step */
9113 ChangePage[x][y] = page; /* remember page to use for change */
9118 if (change->can_change)
9120 if (ChangeElement(x, y, element, page))
9122 if (change->post_change_function)
9123 change->post_change_function(x, y);
9127 if (change->has_action)
9128 ExecuteCustomElementAction(x, y, element, page);
9134 static void HandleElementChange(int x, int y, int page)
9136 int element = MovingOrBlocked2Element(x, y);
9137 struct ElementInfo *ei = &element_info[element];
9138 struct ElementChangeInfo *change = &ei->change_page[page];
9141 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
9144 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
9145 x, y, element, element_info[element].token_name);
9146 printf("HandleElementChange(): This should never happen!\n");
9151 /* this can happen with classic bombs on walkable, changing elements */
9152 if (!CAN_CHANGE(element))
9155 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
9156 ChangeDelay[x][y] = 0;
9162 if (ChangeDelay[x][y] == 0) /* initialize element change */
9164 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
9166 ResetGfxAnimation(x, y);
9167 ResetRandomAnimationValue(x, y);
9169 if (change->pre_change_function)
9170 change->pre_change_function(x, y);
9173 ChangeDelay[x][y]--;
9175 if (ChangeDelay[x][y] != 0) /* continue element change */
9177 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9179 if (IS_ANIMATED(graphic))
9180 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9182 if (change->change_function)
9183 change->change_function(x, y);
9185 else /* finish element change */
9187 if (ChangePage[x][y] != -1) /* remember page from delayed change */
9189 page = ChangePage[x][y];
9190 ChangePage[x][y] = -1;
9192 change = &ei->change_page[page];
9195 if (IS_MOVING(x, y)) /* never change a running system ;-) */
9197 ChangeDelay[x][y] = 1; /* try change after next move step */
9198 ChangePage[x][y] = page; /* remember page to use for change */
9203 if (ChangeElement(x, y, element, page))
9205 if (change->post_change_function)
9206 change->post_change_function(x, y);
9213 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
9214 int trigger_element,
9220 boolean change_done_any = FALSE;
9221 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
9224 if (!(trigger_events[trigger_element][trigger_event]))
9228 printf("::: CheckTriggeredElementChangeExt %d ... [%d, %d, %d, '%s']\n",
9229 trigger_event, recursion_loop_depth, recursion_loop_detected,
9230 recursion_loop_element, EL_NAME(recursion_loop_element));
9233 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
9235 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
9237 int element = EL_CUSTOM_START + i;
9238 boolean change_done = FALSE;
9241 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
9242 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
9245 for (p = 0; p < element_info[element].num_change_pages; p++)
9247 struct ElementChangeInfo *change = &element_info[element].change_page[p];
9249 if (change->can_change_or_has_action &&
9250 change->has_event[trigger_event] &&
9251 change->trigger_side & trigger_side &&
9252 change->trigger_player & trigger_player &&
9253 change->trigger_page & trigger_page_bits &&
9254 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
9256 change->actual_trigger_element = trigger_element;
9257 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
9258 change->actual_trigger_side = trigger_side;
9259 change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
9260 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
9262 if ((change->can_change && !change_done) || change->has_action)
9266 SCAN_PLAYFIELD(x, y)
9268 if (Feld[x][y] == element)
9270 if (change->can_change && !change_done)
9272 ChangeDelay[x][y] = 1;
9273 ChangeEvent[x][y] = trigger_event;
9275 HandleElementChange(x, y, p);
9277 #if USE_NEW_DELAYED_ACTION
9278 else if (change->has_action)
9280 ExecuteCustomElementAction(x, y, element, p);
9281 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
9284 if (change->has_action)
9286 ExecuteCustomElementAction(x, y, element, p);
9287 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
9293 if (change->can_change)
9296 change_done_any = TRUE;
9303 RECURSION_LOOP_DETECTION_END();
9305 return change_done_any;
9308 static boolean CheckElementChangeExt(int x, int y,
9310 int trigger_element,
9315 boolean change_done = FALSE;
9318 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
9319 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
9322 if (Feld[x][y] == EL_BLOCKED)
9324 Blocked2Moving(x, y, &x, &y);
9325 element = Feld[x][y];
9329 /* check if element has already changed */
9330 if (Feld[x][y] != element)
9333 /* check if element has already changed or is about to change after moving */
9334 if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
9335 Feld[x][y] != element) ||
9337 (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
9338 (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
9339 ChangePage[x][y] != -1)))
9344 printf("::: CheckElementChangeExt %d ... [%d, %d, %d, '%s']\n",
9345 trigger_event, recursion_loop_depth, recursion_loop_detected,
9346 recursion_loop_element, EL_NAME(recursion_loop_element));
9349 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
9351 for (p = 0; p < element_info[element].num_change_pages; p++)
9353 struct ElementChangeInfo *change = &element_info[element].change_page[p];
9355 /* check trigger element for all events where the element that is checked
9356 for changing interacts with a directly adjacent element -- this is
9357 different to element changes that affect other elements to change on the
9358 whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
9359 boolean check_trigger_element =
9360 (trigger_event == CE_TOUCHING_X ||
9361 trigger_event == CE_HITTING_X ||
9362 trigger_event == CE_HIT_BY_X ||
9364 /* this one was forgotten until 3.2.3 */
9365 trigger_event == CE_DIGGING_X);
9368 if (change->can_change_or_has_action &&
9369 change->has_event[trigger_event] &&
9370 change->trigger_side & trigger_side &&
9371 change->trigger_player & trigger_player &&
9372 (!check_trigger_element ||
9373 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
9375 change->actual_trigger_element = trigger_element;
9376 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
9377 change->actual_trigger_side = trigger_side;
9378 change->actual_trigger_ce_value = CustomValue[x][y];
9379 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
9381 /* special case: trigger element not at (x,y) position for some events */
9382 if (check_trigger_element)
9394 { 0, 0 }, { 0, 0 }, { 0, 0 },
9398 int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
9399 int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
9401 change->actual_trigger_ce_value = CustomValue[xx][yy];
9402 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
9405 if (change->can_change && !change_done)
9407 ChangeDelay[x][y] = 1;
9408 ChangeEvent[x][y] = trigger_event;
9410 HandleElementChange(x, y, p);
9414 #if USE_NEW_DELAYED_ACTION
9415 else if (change->has_action)
9417 ExecuteCustomElementAction(x, y, element, p);
9418 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
9421 if (change->has_action)
9423 ExecuteCustomElementAction(x, y, element, p);
9424 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
9430 RECURSION_LOOP_DETECTION_END();
9435 static void PlayPlayerSound(struct PlayerInfo *player)
9437 int jx = player->jx, jy = player->jy;
9438 int sound_element = player->artwork_element;
9439 int last_action = player->last_action_waiting;
9440 int action = player->action_waiting;
9442 if (player->is_waiting)
9444 if (action != last_action)
9445 PlayLevelSoundElementAction(jx, jy, sound_element, action);
9447 PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
9451 if (action != last_action)
9452 StopSound(element_info[sound_element].sound[last_action]);
9454 if (last_action == ACTION_SLEEPING)
9455 PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
9459 static void PlayAllPlayersSound()
9463 for (i = 0; i < MAX_PLAYERS; i++)
9464 if (stored_player[i].active)
9465 PlayPlayerSound(&stored_player[i]);
9468 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
9470 boolean last_waiting = player->is_waiting;
9471 int move_dir = player->MovDir;
9473 player->dir_waiting = move_dir;
9474 player->last_action_waiting = player->action_waiting;
9478 if (!last_waiting) /* not waiting -> waiting */
9480 player->is_waiting = TRUE;
9482 player->frame_counter_bored =
9484 game.player_boring_delay_fixed +
9485 GetSimpleRandom(game.player_boring_delay_random);
9486 player->frame_counter_sleeping =
9488 game.player_sleeping_delay_fixed +
9489 GetSimpleRandom(game.player_sleeping_delay_random);
9491 InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
9494 if (game.player_sleeping_delay_fixed +
9495 game.player_sleeping_delay_random > 0 &&
9496 player->anim_delay_counter == 0 &&
9497 player->post_delay_counter == 0 &&
9498 FrameCounter >= player->frame_counter_sleeping)
9499 player->is_sleeping = TRUE;
9500 else if (game.player_boring_delay_fixed +
9501 game.player_boring_delay_random > 0 &&
9502 FrameCounter >= player->frame_counter_bored)
9503 player->is_bored = TRUE;
9505 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
9506 player->is_bored ? ACTION_BORING :
9509 if (player->is_sleeping && player->use_murphy)
9511 /* special case for sleeping Murphy when leaning against non-free tile */
9513 if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
9514 (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
9515 !IS_MOVING(player->jx - 1, player->jy)))
9517 else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
9518 (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
9519 !IS_MOVING(player->jx + 1, player->jy)))
9520 move_dir = MV_RIGHT;
9522 player->is_sleeping = FALSE;
9524 player->dir_waiting = move_dir;
9527 if (player->is_sleeping)
9529 if (player->num_special_action_sleeping > 0)
9531 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
9533 int last_special_action = player->special_action_sleeping;
9534 int num_special_action = player->num_special_action_sleeping;
9535 int special_action =
9536 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
9537 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
9538 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
9539 last_special_action + 1 : ACTION_SLEEPING);
9540 int special_graphic =
9541 el_act_dir2img(player->artwork_element, special_action, move_dir);
9543 player->anim_delay_counter =
9544 graphic_info[special_graphic].anim_delay_fixed +
9545 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
9546 player->post_delay_counter =
9547 graphic_info[special_graphic].post_delay_fixed +
9548 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
9550 player->special_action_sleeping = special_action;
9553 if (player->anim_delay_counter > 0)
9555 player->action_waiting = player->special_action_sleeping;
9556 player->anim_delay_counter--;
9558 else if (player->post_delay_counter > 0)
9560 player->post_delay_counter--;
9564 else if (player->is_bored)
9566 if (player->num_special_action_bored > 0)
9568 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
9570 int special_action =
9571 ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
9572 int special_graphic =
9573 el_act_dir2img(player->artwork_element, special_action, move_dir);
9575 player->anim_delay_counter =
9576 graphic_info[special_graphic].anim_delay_fixed +
9577 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
9578 player->post_delay_counter =
9579 graphic_info[special_graphic].post_delay_fixed +
9580 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
9582 player->special_action_bored = special_action;
9585 if (player->anim_delay_counter > 0)
9587 player->action_waiting = player->special_action_bored;
9588 player->anim_delay_counter--;
9590 else if (player->post_delay_counter > 0)
9592 player->post_delay_counter--;
9597 else if (last_waiting) /* waiting -> not waiting */
9599 player->is_waiting = FALSE;
9600 player->is_bored = FALSE;
9601 player->is_sleeping = FALSE;
9603 player->frame_counter_bored = -1;
9604 player->frame_counter_sleeping = -1;
9606 player->anim_delay_counter = 0;
9607 player->post_delay_counter = 0;
9609 player->dir_waiting = player->MovDir;
9610 player->action_waiting = ACTION_DEFAULT;
9612 player->special_action_bored = ACTION_DEFAULT;
9613 player->special_action_sleeping = ACTION_DEFAULT;
9617 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
9619 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
9620 int left = player_action & JOY_LEFT;
9621 int right = player_action & JOY_RIGHT;
9622 int up = player_action & JOY_UP;
9623 int down = player_action & JOY_DOWN;
9624 int button1 = player_action & JOY_BUTTON_1;
9625 int button2 = player_action & JOY_BUTTON_2;
9626 int dx = (left ? -1 : right ? 1 : 0);
9627 int dy = (up ? -1 : down ? 1 : 0);
9629 if (!player->active || tape.pausing)
9635 snapped = SnapField(player, dx, dy);
9639 dropped = DropElement(player);
9641 moved = MovePlayer(player, dx, dy);
9644 if (tape.single_step && tape.recording && !tape.pausing)
9646 if (button1 || (dropped && !moved))
9648 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9649 SnapField(player, 0, 0); /* stop snapping */
9653 SetPlayerWaiting(player, FALSE);
9655 return player_action;
9659 /* no actions for this player (no input at player's configured device) */
9661 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
9662 SnapField(player, 0, 0);
9663 CheckGravityMovementWhenNotMoving(player);
9665 if (player->MovPos == 0)
9666 SetPlayerWaiting(player, TRUE);
9668 if (player->MovPos == 0) /* needed for tape.playing */
9669 player->is_moving = FALSE;
9671 player->is_dropping = FALSE;
9672 player->is_dropping_pressed = FALSE;
9673 player->drop_pressed_delay = 0;
9679 static void CheckLevelTime()
9683 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9685 if (level.native_em_level->lev->home == 0) /* all players at home */
9687 PlayerWins(local_player);
9689 AllPlayersGone = TRUE;
9691 level.native_em_level->lev->home = -1;
9694 if (level.native_em_level->ply[0]->alive == 0 &&
9695 level.native_em_level->ply[1]->alive == 0 &&
9696 level.native_em_level->ply[2]->alive == 0 &&
9697 level.native_em_level->ply[3]->alive == 0) /* all dead */
9698 AllPlayersGone = TRUE;
9701 if (TimeFrames >= FRAMES_PER_SECOND)
9706 for (i = 0; i < MAX_PLAYERS; i++)
9708 struct PlayerInfo *player = &stored_player[i];
9710 if (SHIELD_ON(player))
9712 player->shield_normal_time_left--;
9714 if (player->shield_deadly_time_left > 0)
9715 player->shield_deadly_time_left--;
9719 if (!local_player->LevelSolved && !level.use_step_counter)
9727 if (TimeLeft <= 10 && setup.time_limit)
9728 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
9730 DrawGameValue_Time(TimeLeft);
9732 if (!TimeLeft && setup.time_limit)
9734 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9735 level.native_em_level->lev->killed_out_of_time = TRUE;
9737 for (i = 0; i < MAX_PLAYERS; i++)
9738 KillPlayer(&stored_player[i]);
9741 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
9742 DrawGameValue_Time(TimePlayed);
9744 level.native_em_level->lev->time =
9745 (level.time == 0 ? TimePlayed : TimeLeft);
9748 if (tape.recording || tape.playing)
9749 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
9753 void AdvanceFrameAndPlayerCounters(int player_nr)
9757 /* advance frame counters (global frame counter and time frame counter) */
9761 /* advance player counters (counters for move delay, move animation etc.) */
9762 for (i = 0; i < MAX_PLAYERS; i++)
9764 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
9765 int move_delay_value = stored_player[i].move_delay_value;
9766 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
9768 if (!advance_player_counters) /* not all players may be affected */
9771 #if USE_NEW_PLAYER_ANIM
9772 if (move_frames == 0) /* less than one move per game frame */
9774 int stepsize = TILEX / move_delay_value;
9775 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
9776 int count = (stored_player[i].is_moving ?
9777 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
9779 if (count % delay == 0)
9784 stored_player[i].Frame += move_frames;
9786 if (stored_player[i].MovPos != 0)
9787 stored_player[i].StepFrame += move_frames;
9789 if (stored_player[i].move_delay > 0)
9790 stored_player[i].move_delay--;
9792 /* due to bugs in previous versions, counter must count up, not down */
9793 if (stored_player[i].push_delay != -1)
9794 stored_player[i].push_delay++;
9796 if (stored_player[i].drop_delay > 0)
9797 stored_player[i].drop_delay--;
9799 if (stored_player[i].is_dropping_pressed)
9800 stored_player[i].drop_pressed_delay++;
9804 void StartGameActions(boolean init_network_game, boolean record_tape,
9807 unsigned long new_random_seed = InitRND(random_seed);
9810 TapeStartRecording(new_random_seed);
9812 #if defined(NETWORK_AVALIABLE)
9813 if (init_network_game)
9815 SendToServer_StartPlaying();
9826 static unsigned long game_frame_delay = 0;
9827 unsigned long game_frame_delay_value;
9828 byte *recorded_player_action;
9829 byte summarized_player_action = 0;
9830 byte tape_action[MAX_PLAYERS];
9833 /* detect endless loops, caused by custom element programming */
9834 if (recursion_loop_detected && recursion_loop_depth == 0)
9836 char *message = getStringCat3("Internal Error ! Element ",
9837 EL_NAME(recursion_loop_element),
9838 " caused endless loop ! Quit the game ?");
9840 Error(ERR_WARN, "element '%s' caused endless loop in game engine",
9841 EL_NAME(recursion_loop_element));
9843 RequestQuitGameExt(FALSE, level_editor_test_game, message);
9845 recursion_loop_detected = FALSE; /* if game should be continued */
9852 if (game.restart_level)
9853 StartGameActions(options.network, setup.autorecord, NEW_RANDOMIZE);
9855 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9857 if (level.native_em_level->lev->home == 0) /* all players at home */
9859 PlayerWins(local_player);
9861 AllPlayersGone = TRUE;
9863 level.native_em_level->lev->home = -1;
9866 if (level.native_em_level->ply[0]->alive == 0 &&
9867 level.native_em_level->ply[1]->alive == 0 &&
9868 level.native_em_level->ply[2]->alive == 0 &&
9869 level.native_em_level->ply[3]->alive == 0) /* all dead */
9870 AllPlayersGone = TRUE;
9873 if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
9876 if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
9879 if (game_status != GAME_MODE_PLAYING) /* status might have changed */
9882 game_frame_delay_value =
9883 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
9885 if (tape.playing && tape.warp_forward && !tape.pausing)
9886 game_frame_delay_value = 0;
9888 /* ---------- main game synchronization point ---------- */
9890 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
9892 if (network_playing && !network_player_action_received)
9894 /* try to get network player actions in time */
9896 #if defined(NETWORK_AVALIABLE)
9897 /* last chance to get network player actions without main loop delay */
9901 /* game was quit by network peer */
9902 if (game_status != GAME_MODE_PLAYING)
9905 if (!network_player_action_received)
9906 return; /* failed to get network player actions in time */
9908 /* do not yet reset "network_player_action_received" (for tape.pausing) */
9914 /* at this point we know that we really continue executing the game */
9916 network_player_action_received = FALSE;
9918 /* when playing tape, read previously recorded player input from tape data */
9919 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
9922 /* TapePlayAction() may return NULL when toggling to "pause before death" */
9927 if (tape.set_centered_player)
9929 game.centered_player_nr_next = tape.centered_player_nr_next;
9930 game.set_centered_player = TRUE;
9933 for (i = 0; i < MAX_PLAYERS; i++)
9935 summarized_player_action |= stored_player[i].action;
9937 if (!network_playing)
9938 stored_player[i].effective_action = stored_player[i].action;
9941 #if defined(NETWORK_AVALIABLE)
9942 if (network_playing)
9943 SendToServer_MovePlayer(summarized_player_action);
9946 if (!options.network && !setup.team_mode)
9947 local_player->effective_action = summarized_player_action;
9949 if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
9951 for (i = 0; i < MAX_PLAYERS; i++)
9952 stored_player[i].effective_action =
9953 (i == game.centered_player_nr ? summarized_player_action : 0);
9956 if (recorded_player_action != NULL)
9957 for (i = 0; i < MAX_PLAYERS; i++)
9958 stored_player[i].effective_action = recorded_player_action[i];
9960 for (i = 0; i < MAX_PLAYERS; i++)
9962 tape_action[i] = stored_player[i].effective_action;
9964 /* (this can only happen in the R'n'D game engine) */
9965 if (tape.recording && tape_action[i] && !tape.player_participates[i])
9966 tape.player_participates[i] = TRUE; /* player just appeared from CE */
9969 /* only record actions from input devices, but not programmed actions */
9971 TapeRecordAction(tape_action);
9973 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9975 GameActions_EM_Main();
9983 void GameActions_EM_Main()
9985 byte effective_action[MAX_PLAYERS];
9986 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
9989 for (i = 0; i < MAX_PLAYERS; i++)
9990 effective_action[i] = stored_player[i].effective_action;
9992 GameActions_EM(effective_action, warp_mode);
9996 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
9999 void GameActions_RND()
10001 int magic_wall_x = 0, magic_wall_y = 0;
10002 int i, x, y, element, graphic;
10004 InitPlayfieldScanModeVars();
10006 #if USE_ONE_MORE_CHANGE_PER_FRAME
10007 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10009 SCAN_PLAYFIELD(x, y)
10011 ChangeCount[x][y] = 0;
10012 ChangeEvent[x][y] = -1;
10017 if (game.set_centered_player)
10019 boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
10021 /* switching to "all players" only possible if all players fit to screen */
10022 if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
10024 game.centered_player_nr_next = game.centered_player_nr;
10025 game.set_centered_player = FALSE;
10028 /* do not switch focus to non-existing (or non-active) player */
10029 if (game.centered_player_nr_next >= 0 &&
10030 !stored_player[game.centered_player_nr_next].active)
10032 game.centered_player_nr_next = game.centered_player_nr;
10033 game.set_centered_player = FALSE;
10037 if (game.set_centered_player &&
10038 ScreenMovPos == 0) /* screen currently aligned at tile position */
10042 if (game.centered_player_nr_next == -1)
10044 setScreenCenteredToAllPlayers(&sx, &sy);
10048 sx = stored_player[game.centered_player_nr_next].jx;
10049 sy = stored_player[game.centered_player_nr_next].jy;
10052 game.centered_player_nr = game.centered_player_nr_next;
10053 game.set_centered_player = FALSE;
10055 DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
10056 DrawGameDoorValues();
10059 for (i = 0; i < MAX_PLAYERS; i++)
10061 int actual_player_action = stored_player[i].effective_action;
10064 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
10065 - rnd_equinox_tetrachloride 048
10066 - rnd_equinox_tetrachloride_ii 096
10067 - rnd_emanuel_schmieg 002
10068 - doctor_sloan_ww 001, 020
10070 if (stored_player[i].MovPos == 0)
10071 CheckGravityMovement(&stored_player[i]);
10074 /* overwrite programmed action with tape action */
10075 if (stored_player[i].programmed_action)
10076 actual_player_action = stored_player[i].programmed_action;
10078 PlayerActions(&stored_player[i], actual_player_action);
10080 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
10083 ScrollScreen(NULL, SCROLL_GO_ON);
10085 /* for backwards compatibility, the following code emulates a fixed bug that
10086 occured when pushing elements (causing elements that just made their last
10087 pushing step to already (if possible) make their first falling step in the
10088 same game frame, which is bad); this code is also needed to use the famous
10089 "spring push bug" which is used in older levels and might be wanted to be
10090 used also in newer levels, but in this case the buggy pushing code is only
10091 affecting the "spring" element and no other elements */
10093 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
10095 for (i = 0; i < MAX_PLAYERS; i++)
10097 struct PlayerInfo *player = &stored_player[i];
10098 int x = player->jx;
10099 int y = player->jy;
10101 if (player->active && player->is_pushing && player->is_moving &&
10103 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
10104 Feld[x][y] == EL_SPRING))
10106 ContinueMoving(x, y);
10108 /* continue moving after pushing (this is actually a bug) */
10109 if (!IS_MOVING(x, y))
10110 Stop[x][y] = FALSE;
10116 debug_print_timestamp(0, "start main loop profiling");
10119 SCAN_PLAYFIELD(x, y)
10121 ChangeCount[x][y] = 0;
10122 ChangeEvent[x][y] = -1;
10124 /* this must be handled before main playfield loop */
10125 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
10128 if (MovDelay[x][y] <= 0)
10132 #if USE_NEW_SNAP_DELAY
10133 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
10136 if (MovDelay[x][y] <= 0)
10139 DrawLevelField(x, y);
10141 TestIfElementTouchesCustomElement(x, y); /* for empty space */
10147 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
10149 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
10150 printf("GameActions(): This should never happen!\n");
10152 ChangePage[x][y] = -1;
10156 Stop[x][y] = FALSE;
10157 if (WasJustMoving[x][y] > 0)
10158 WasJustMoving[x][y]--;
10159 if (WasJustFalling[x][y] > 0)
10160 WasJustFalling[x][y]--;
10161 if (CheckCollision[x][y] > 0)
10162 CheckCollision[x][y]--;
10163 if (CheckImpact[x][y] > 0)
10164 CheckImpact[x][y]--;
10168 /* reset finished pushing action (not done in ContinueMoving() to allow
10169 continuous pushing animation for elements with zero push delay) */
10170 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
10172 ResetGfxAnimation(x, y);
10173 DrawLevelField(x, y);
10177 if (IS_BLOCKED(x, y))
10181 Blocked2Moving(x, y, &oldx, &oldy);
10182 if (!IS_MOVING(oldx, oldy))
10184 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
10185 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
10186 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
10187 printf("GameActions(): This should never happen!\n");
10194 debug_print_timestamp(0, "- time for pre-main loop:");
10197 #if 0 // -------------------- !!! TEST ONLY !!! --------------------
10198 SCAN_PLAYFIELD(x, y)
10200 element = Feld[x][y];
10201 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10206 int element2 = element;
10207 int graphic2 = graphic;
10209 int element2 = Feld[x][y];
10210 int graphic2 = el_act_dir2img(element2, GfxAction[x][y], GfxDir[x][y]);
10212 int last_gfx_frame = GfxFrame[x][y];
10214 if (graphic_info[graphic2].anim_global_sync)
10215 GfxFrame[x][y] = FrameCounter;
10216 else if (ANIM_MODE(graphic2) == ANIM_CE_VALUE)
10217 GfxFrame[x][y] = CustomValue[x][y];
10218 else if (ANIM_MODE(graphic2) == ANIM_CE_SCORE)
10219 GfxFrame[x][y] = element_info[element2].collect_score;
10220 else if (ANIM_MODE(graphic2) == ANIM_CE_DELAY)
10221 GfxFrame[x][y] = ChangeDelay[x][y];
10223 if (redraw && GfxFrame[x][y] != last_gfx_frame)
10224 DrawLevelGraphicAnimation(x, y, graphic2);
10227 ResetGfxFrame(x, y, TRUE);
10231 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
10232 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
10233 ResetRandomAnimationValue(x, y);
10237 SetRandomAnimationValue(x, y);
10241 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
10244 #endif // -------------------- !!! TEST ONLY !!! --------------------
10247 debug_print_timestamp(0, "- time for TEST loop: -->");
10250 SCAN_PLAYFIELD(x, y)
10252 element = Feld[x][y];
10253 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10255 ResetGfxFrame(x, y, TRUE);
10257 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
10258 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
10259 ResetRandomAnimationValue(x, y);
10261 SetRandomAnimationValue(x, y);
10263 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
10265 if (IS_INACTIVE(element))
10267 if (IS_ANIMATED(graphic))
10268 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10273 /* this may take place after moving, so 'element' may have changed */
10274 if (IS_CHANGING(x, y) &&
10275 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
10277 int page = element_info[element].event_page_nr[CE_DELAY];
10280 HandleElementChange(x, y, page);
10282 if (CAN_CHANGE(element))
10283 HandleElementChange(x, y, page);
10285 if (HAS_ACTION(element))
10286 ExecuteCustomElementAction(x, y, element, page);
10289 element = Feld[x][y];
10290 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10293 #if 0 // ---------------------------------------------------------------------
10295 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
10299 element = Feld[x][y];
10300 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10302 if (IS_ANIMATED(graphic) &&
10303 !IS_MOVING(x, y) &&
10305 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10307 if (IS_GEM(element) || element == EL_SP_INFOTRON)
10308 DrawTwinkleOnField(x, y);
10310 else if (IS_MOVING(x, y))
10311 ContinueMoving(x, y);
10318 case EL_EM_EXIT_OPEN:
10319 case EL_SP_EXIT_OPEN:
10320 case EL_STEEL_EXIT_OPEN:
10321 case EL_EM_STEEL_EXIT_OPEN:
10322 case EL_SP_TERMINAL:
10323 case EL_SP_TERMINAL_ACTIVE:
10324 case EL_EXTRA_TIME:
10325 case EL_SHIELD_NORMAL:
10326 case EL_SHIELD_DEADLY:
10327 if (IS_ANIMATED(graphic))
10328 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10331 case EL_DYNAMITE_ACTIVE:
10332 case EL_EM_DYNAMITE_ACTIVE:
10333 case EL_DYNABOMB_PLAYER_1_ACTIVE:
10334 case EL_DYNABOMB_PLAYER_2_ACTIVE:
10335 case EL_DYNABOMB_PLAYER_3_ACTIVE:
10336 case EL_DYNABOMB_PLAYER_4_ACTIVE:
10337 case EL_SP_DISK_RED_ACTIVE:
10338 CheckDynamite(x, y);
10341 case EL_AMOEBA_GROWING:
10342 AmoebeWaechst(x, y);
10345 case EL_AMOEBA_SHRINKING:
10346 AmoebaDisappearing(x, y);
10349 #if !USE_NEW_AMOEBA_CODE
10350 case EL_AMOEBA_WET:
10351 case EL_AMOEBA_DRY:
10352 case EL_AMOEBA_FULL:
10354 case EL_EMC_DRIPPER:
10355 AmoebeAbleger(x, y);
10359 case EL_GAME_OF_LIFE:
10364 case EL_EXIT_CLOSED:
10368 case EL_EM_EXIT_CLOSED:
10372 case EL_STEEL_EXIT_CLOSED:
10373 CheckExitSteel(x, y);
10376 case EL_EM_STEEL_EXIT_CLOSED:
10377 CheckExitSteelEM(x, y);
10380 case EL_SP_EXIT_CLOSED:
10384 case EL_EXPANDABLE_WALL_GROWING:
10385 case EL_EXPANDABLE_STEELWALL_GROWING:
10386 MauerWaechst(x, y);
10389 case EL_EXPANDABLE_WALL:
10390 case EL_EXPANDABLE_WALL_HORIZONTAL:
10391 case EL_EXPANDABLE_WALL_VERTICAL:
10392 case EL_EXPANDABLE_WALL_ANY:
10393 case EL_BD_EXPANDABLE_WALL:
10394 MauerAbleger(x, y);
10397 case EL_EXPANDABLE_STEELWALL_HORIZONTAL:
10398 case EL_EXPANDABLE_STEELWALL_VERTICAL:
10399 case EL_EXPANDABLE_STEELWALL_ANY:
10400 MauerAblegerStahl(x, y);
10404 CheckForDragon(x, y);
10410 case EL_ELEMENT_SNAPPING:
10411 case EL_DIAGONAL_SHRINKING:
10412 case EL_DIAGONAL_GROWING:
10415 el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
10417 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10422 if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
10423 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10428 #else // ---------------------------------------------------------------------
10430 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
10434 element = Feld[x][y];
10435 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10437 if (IS_ANIMATED(graphic) &&
10438 !IS_MOVING(x, y) &&
10440 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10442 if (IS_GEM(element) || element == EL_SP_INFOTRON)
10443 DrawTwinkleOnField(x, y);
10445 else if ((element == EL_ACID ||
10446 element == EL_EXIT_OPEN ||
10447 element == EL_EM_EXIT_OPEN ||
10448 element == EL_SP_EXIT_OPEN ||
10449 element == EL_STEEL_EXIT_OPEN ||
10450 element == EL_EM_STEEL_EXIT_OPEN ||
10451 element == EL_SP_TERMINAL ||
10452 element == EL_SP_TERMINAL_ACTIVE ||
10453 element == EL_EXTRA_TIME ||
10454 element == EL_SHIELD_NORMAL ||
10455 element == EL_SHIELD_DEADLY) &&
10456 IS_ANIMATED(graphic))
10457 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10458 else if (IS_MOVING(x, y))
10459 ContinueMoving(x, y);
10460 else if (IS_ACTIVE_BOMB(element))
10461 CheckDynamite(x, y);
10462 else if (element == EL_AMOEBA_GROWING)
10463 AmoebeWaechst(x, y);
10464 else if (element == EL_AMOEBA_SHRINKING)
10465 AmoebaDisappearing(x, y);
10467 #if !USE_NEW_AMOEBA_CODE
10468 else if (IS_AMOEBALIVE(element))
10469 AmoebeAbleger(x, y);
10472 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
10474 else if (element == EL_EXIT_CLOSED)
10476 else if (element == EL_EM_EXIT_CLOSED)
10478 else if (element == EL_STEEL_EXIT_CLOSED)
10479 CheckExitSteel(x, y);
10480 else if (element == EL_EM_STEEL_EXIT_CLOSED)
10481 CheckExitSteelEM(x, y);
10482 else if (element == EL_SP_EXIT_CLOSED)
10484 else if (element == EL_EXPANDABLE_WALL_GROWING ||
10485 element == EL_EXPANDABLE_STEELWALL_GROWING)
10486 MauerWaechst(x, y);
10487 else if (element == EL_EXPANDABLE_WALL ||
10488 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
10489 element == EL_EXPANDABLE_WALL_VERTICAL ||
10490 element == EL_EXPANDABLE_WALL_ANY ||
10491 element == EL_BD_EXPANDABLE_WALL)
10492 MauerAbleger(x, y);
10493 else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
10494 element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
10495 element == EL_EXPANDABLE_STEELWALL_ANY)
10496 MauerAblegerStahl(x, y);
10497 else if (element == EL_FLAMES)
10498 CheckForDragon(x, y);
10499 else if (element == EL_EXPLOSION)
10500 ; /* drawing of correct explosion animation is handled separately */
10501 else if (element == EL_ELEMENT_SNAPPING ||
10502 element == EL_DIAGONAL_SHRINKING ||
10503 element == EL_DIAGONAL_GROWING)
10505 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
10507 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10509 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
10510 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10512 #endif // ---------------------------------------------------------------------
10514 if (IS_BELT_ACTIVE(element))
10515 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
10517 if (game.magic_wall_active)
10519 int jx = local_player->jx, jy = local_player->jy;
10521 /* play the element sound at the position nearest to the player */
10522 if ((element == EL_MAGIC_WALL_FULL ||
10523 element == EL_MAGIC_WALL_ACTIVE ||
10524 element == EL_MAGIC_WALL_EMPTYING ||
10525 element == EL_BD_MAGIC_WALL_FULL ||
10526 element == EL_BD_MAGIC_WALL_ACTIVE ||
10527 element == EL_BD_MAGIC_WALL_EMPTYING ||
10528 element == EL_DC_MAGIC_WALL_FULL ||
10529 element == EL_DC_MAGIC_WALL_ACTIVE ||
10530 element == EL_DC_MAGIC_WALL_EMPTYING) &&
10531 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
10540 debug_print_timestamp(0, "- time for MAIN loop: -->");
10543 #if USE_NEW_AMOEBA_CODE
10544 /* new experimental amoeba growth stuff */
10545 if (!(FrameCounter % 8))
10547 static unsigned long random = 1684108901;
10549 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
10551 x = RND(lev_fieldx);
10552 y = RND(lev_fieldy);
10553 element = Feld[x][y];
10555 if (!IS_PLAYER(x,y) &&
10556 (element == EL_EMPTY ||
10557 CAN_GROW_INTO(element) ||
10558 element == EL_QUICKSAND_EMPTY ||
10559 element == EL_QUICKSAND_FAST_EMPTY ||
10560 element == EL_ACID_SPLASH_LEFT ||
10561 element == EL_ACID_SPLASH_RIGHT))
10563 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
10564 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
10565 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
10566 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
10567 Feld[x][y] = EL_AMOEBA_DROP;
10570 random = random * 129 + 1;
10576 if (game.explosions_delayed)
10579 game.explosions_delayed = FALSE;
10581 SCAN_PLAYFIELD(x, y)
10583 element = Feld[x][y];
10585 if (ExplodeField[x][y])
10586 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
10587 else if (element == EL_EXPLOSION)
10588 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
10590 ExplodeField[x][y] = EX_TYPE_NONE;
10593 game.explosions_delayed = TRUE;
10596 if (game.magic_wall_active)
10598 if (!(game.magic_wall_time_left % 4))
10600 int element = Feld[magic_wall_x][magic_wall_y];
10602 if (element == EL_BD_MAGIC_WALL_FULL ||
10603 element == EL_BD_MAGIC_WALL_ACTIVE ||
10604 element == EL_BD_MAGIC_WALL_EMPTYING)
10605 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
10606 else if (element == EL_DC_MAGIC_WALL_FULL ||
10607 element == EL_DC_MAGIC_WALL_ACTIVE ||
10608 element == EL_DC_MAGIC_WALL_EMPTYING)
10609 PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
10611 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
10614 if (game.magic_wall_time_left > 0)
10616 game.magic_wall_time_left--;
10617 if (!game.magic_wall_time_left)
10619 SCAN_PLAYFIELD(x, y)
10621 element = Feld[x][y];
10623 if (element == EL_MAGIC_WALL_ACTIVE ||
10624 element == EL_MAGIC_WALL_FULL)
10626 Feld[x][y] = EL_MAGIC_WALL_DEAD;
10627 DrawLevelField(x, y);
10629 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
10630 element == EL_BD_MAGIC_WALL_FULL)
10632 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
10633 DrawLevelField(x, y);
10635 else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
10636 element == EL_DC_MAGIC_WALL_FULL)
10638 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
10639 DrawLevelField(x, y);
10643 game.magic_wall_active = FALSE;
10648 if (game.light_time_left > 0)
10650 game.light_time_left--;
10652 if (game.light_time_left == 0)
10653 RedrawAllLightSwitchesAndInvisibleElements();
10656 if (game.timegate_time_left > 0)
10658 game.timegate_time_left--;
10660 if (game.timegate_time_left == 0)
10661 CloseAllOpenTimegates();
10664 if (game.lenses_time_left > 0)
10666 game.lenses_time_left--;
10668 if (game.lenses_time_left == 0)
10669 RedrawAllInvisibleElementsForLenses();
10672 if (game.magnify_time_left > 0)
10674 game.magnify_time_left--;
10676 if (game.magnify_time_left == 0)
10677 RedrawAllInvisibleElementsForMagnifier();
10680 for (i = 0; i < MAX_PLAYERS; i++)
10682 struct PlayerInfo *player = &stored_player[i];
10684 if (SHIELD_ON(player))
10686 if (player->shield_deadly_time_left)
10687 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
10688 else if (player->shield_normal_time_left)
10689 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
10696 PlayAllPlayersSound();
10698 if (options.debug) /* calculate frames per second */
10700 static unsigned long fps_counter = 0;
10701 static int fps_frames = 0;
10702 unsigned long fps_delay_ms = Counter() - fps_counter;
10706 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
10708 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
10711 fps_counter = Counter();
10714 redraw_mask |= REDRAW_FPS;
10717 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
10719 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
10721 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
10723 local_player->show_envelope = 0;
10727 debug_print_timestamp(0, "stop main loop profiling ");
10728 printf("----------------------------------------------------------\n");
10731 /* use random number generator in every frame to make it less predictable */
10732 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
10736 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
10738 int min_x = x, min_y = y, max_x = x, max_y = y;
10741 for (i = 0; i < MAX_PLAYERS; i++)
10743 int jx = stored_player[i].jx, jy = stored_player[i].jy;
10745 if (!stored_player[i].active || &stored_player[i] == player)
10748 min_x = MIN(min_x, jx);
10749 min_y = MIN(min_y, jy);
10750 max_x = MAX(max_x, jx);
10751 max_y = MAX(max_y, jy);
10754 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
10757 static boolean AllPlayersInVisibleScreen()
10761 for (i = 0; i < MAX_PLAYERS; i++)
10763 int jx = stored_player[i].jx, jy = stored_player[i].jy;
10765 if (!stored_player[i].active)
10768 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
10775 void ScrollLevel(int dx, int dy)
10778 static Bitmap *bitmap_db_field2 = NULL;
10779 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
10785 /* only horizontal XOR vertical scroll direction allowed */
10786 if ((dx == 0 && dy == 0) || (dx != 0 && dy != 0))
10790 if (bitmap_db_field2 == NULL)
10791 bitmap_db_field2 = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH);
10793 BlitBitmap(drawto_field, bitmap_db_field2,
10794 FX + TILEX * (dx == -1) - softscroll_offset,
10795 FY + TILEY * (dy == -1) - softscroll_offset,
10796 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
10797 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
10798 FX + TILEX * (dx == 1) - softscroll_offset,
10799 FY + TILEY * (dy == 1) - softscroll_offset);
10800 BlitBitmap(bitmap_db_field2, drawto_field,
10801 FX + TILEX * (dx == 1) - softscroll_offset,
10802 FY + TILEY * (dy == 1) - softscroll_offset,
10803 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
10804 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
10805 FX + TILEX * (dx == 1) - softscroll_offset,
10806 FY + TILEY * (dy == 1) - softscroll_offset);
10811 int xsize = (BX2 - BX1 + 1);
10812 int ysize = (BY2 - BY1 + 1);
10813 int start = (dx != 0 ? (dx == -1 ? BX1 : BX2) : (dy == -1 ? BY1 : BY2));
10814 int end = (dx != 0 ? (dx == -1 ? BX2 : BX1) : (dy == -1 ? BY2 : BY1));
10815 int step = (start < end ? +1 : -1);
10817 for (i = start; i != end; i += step)
10819 BlitBitmap(drawto_field, drawto_field,
10820 FX + TILEX * (dx != 0 ? i + step : 0),
10821 FY + TILEY * (dy != 0 ? i + step : 0),
10822 TILEX * (dx != 0 ? 1 : xsize),
10823 TILEY * (dy != 0 ? 1 : ysize),
10824 FX + TILEX * (dx != 0 ? i : 0),
10825 FY + TILEY * (dy != 0 ? i : 0));
10830 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
10832 BlitBitmap(drawto_field, drawto_field,
10833 FX + TILEX * (dx == -1) - softscroll_offset,
10834 FY + TILEY * (dy == -1) - softscroll_offset,
10835 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
10836 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
10837 FX + TILEX * (dx == 1) - softscroll_offset,
10838 FY + TILEY * (dy == 1) - softscroll_offset);
10844 x = (dx == 1 ? BX1 : BX2);
10845 for (y = BY1; y <= BY2; y++)
10846 DrawScreenField(x, y);
10851 y = (dy == 1 ? BY1 : BY2);
10852 for (x = BX1; x <= BX2; x++)
10853 DrawScreenField(x, y);
10856 redraw_mask |= REDRAW_FIELD;
10859 static boolean canFallDown(struct PlayerInfo *player)
10861 int jx = player->jx, jy = player->jy;
10863 return (IN_LEV_FIELD(jx, jy + 1) &&
10864 (IS_FREE(jx, jy + 1) ||
10865 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
10866 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
10867 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
10870 static boolean canPassField(int x, int y, int move_dir)
10872 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
10873 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
10874 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
10875 int nextx = x + dx;
10876 int nexty = y + dy;
10877 int element = Feld[x][y];
10879 return (IS_PASSABLE_FROM(element, opposite_dir) &&
10880 !CAN_MOVE(element) &&
10881 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
10882 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
10883 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
10886 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
10888 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
10889 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
10890 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
10894 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
10895 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
10896 (IS_DIGGABLE(Feld[newx][newy]) ||
10897 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
10898 canPassField(newx, newy, move_dir)));
10901 static void CheckGravityMovement(struct PlayerInfo *player)
10903 #if USE_PLAYER_GRAVITY
10904 if (player->gravity && !player->programmed_action)
10906 if (game.gravity && !player->programmed_action)
10909 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
10910 int move_dir_vertical = player->effective_action & MV_VERTICAL;
10911 boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
10912 int jx = player->jx, jy = player->jy;
10913 boolean player_is_moving_to_valid_field =
10914 (!player_is_snapping &&
10915 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
10916 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
10917 boolean player_can_fall_down = canFallDown(player);
10919 if (player_can_fall_down &&
10920 !player_is_moving_to_valid_field)
10921 player->programmed_action = MV_DOWN;
10925 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
10927 return CheckGravityMovement(player);
10929 #if USE_PLAYER_GRAVITY
10930 if (player->gravity && !player->programmed_action)
10932 if (game.gravity && !player->programmed_action)
10935 int jx = player->jx, jy = player->jy;
10936 boolean field_under_player_is_free =
10937 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
10938 boolean player_is_standing_on_valid_field =
10939 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
10940 (IS_WALKABLE(Feld[jx][jy]) &&
10941 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
10943 if (field_under_player_is_free && !player_is_standing_on_valid_field)
10944 player->programmed_action = MV_DOWN;
10949 MovePlayerOneStep()
10950 -----------------------------------------------------------------------------
10951 dx, dy: direction (non-diagonal) to try to move the player to
10952 real_dx, real_dy: direction as read from input device (can be diagonal)
10955 boolean MovePlayerOneStep(struct PlayerInfo *player,
10956 int dx, int dy, int real_dx, int real_dy)
10958 int jx = player->jx, jy = player->jy;
10959 int new_jx = jx + dx, new_jy = jy + dy;
10960 #if !USE_FIXED_DONT_RUN_INTO
10964 boolean player_can_move = !player->cannot_move;
10966 if (!player->active || (!dx && !dy))
10967 return MP_NO_ACTION;
10969 player->MovDir = (dx < 0 ? MV_LEFT :
10970 dx > 0 ? MV_RIGHT :
10972 dy > 0 ? MV_DOWN : MV_NONE);
10974 if (!IN_LEV_FIELD(new_jx, new_jy))
10975 return MP_NO_ACTION;
10977 if (!player_can_move)
10979 if (player->MovPos == 0)
10981 player->is_moving = FALSE;
10982 player->is_digging = FALSE;
10983 player->is_collecting = FALSE;
10984 player->is_snapping = FALSE;
10985 player->is_pushing = FALSE;
10990 if (!options.network && game.centered_player_nr == -1 &&
10991 !AllPlayersInSight(player, new_jx, new_jy))
10992 return MP_NO_ACTION;
10994 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
10995 return MP_NO_ACTION;
10998 #if !USE_FIXED_DONT_RUN_INTO
10999 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
11001 /* (moved to DigField()) */
11002 if (player_can_move && DONT_RUN_INTO(element))
11004 if (element == EL_ACID && dx == 0 && dy == 1)
11006 SplashAcid(new_jx, new_jy);
11007 Feld[jx][jy] = EL_PLAYER_1;
11008 InitMovingField(jx, jy, MV_DOWN);
11009 Store[jx][jy] = EL_ACID;
11010 ContinueMoving(jx, jy);
11011 BuryPlayer(player);
11014 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
11020 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
11021 if (can_move != MP_MOVING)
11024 /* check if DigField() has caused relocation of the player */
11025 if (player->jx != jx || player->jy != jy)
11026 return MP_NO_ACTION; /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
11028 StorePlayer[jx][jy] = 0;
11029 player->last_jx = jx;
11030 player->last_jy = jy;
11031 player->jx = new_jx;
11032 player->jy = new_jy;
11033 StorePlayer[new_jx][new_jy] = player->element_nr;
11035 if (player->move_delay_value_next != -1)
11037 player->move_delay_value = player->move_delay_value_next;
11038 player->move_delay_value_next = -1;
11042 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
11044 player->step_counter++;
11046 PlayerVisit[jx][jy] = FrameCounter;
11048 #if USE_UFAST_PLAYER_EXIT_BUGFIX
11049 player->is_moving = TRUE;
11053 /* should better be called in MovePlayer(), but this breaks some tapes */
11054 ScrollPlayer(player, SCROLL_INIT);
11060 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
11062 int jx = player->jx, jy = player->jy;
11063 int old_jx = jx, old_jy = jy;
11064 int moved = MP_NO_ACTION;
11066 if (!player->active)
11071 if (player->MovPos == 0)
11073 player->is_moving = FALSE;
11074 player->is_digging = FALSE;
11075 player->is_collecting = FALSE;
11076 player->is_snapping = FALSE;
11077 player->is_pushing = FALSE;
11083 if (player->move_delay > 0)
11086 player->move_delay = -1; /* set to "uninitialized" value */
11088 /* store if player is automatically moved to next field */
11089 player->is_auto_moving = (player->programmed_action != MV_NONE);
11091 /* remove the last programmed player action */
11092 player->programmed_action = 0;
11094 if (player->MovPos)
11096 /* should only happen if pre-1.2 tape recordings are played */
11097 /* this is only for backward compatibility */
11099 int original_move_delay_value = player->move_delay_value;
11102 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
11106 /* scroll remaining steps with finest movement resolution */
11107 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
11109 while (player->MovPos)
11111 ScrollPlayer(player, SCROLL_GO_ON);
11112 ScrollScreen(NULL, SCROLL_GO_ON);
11114 AdvanceFrameAndPlayerCounters(player->index_nr);
11120 player->move_delay_value = original_move_delay_value;
11123 player->is_active = FALSE;
11125 if (player->last_move_dir & MV_HORIZONTAL)
11127 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
11128 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
11132 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
11133 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
11136 #if USE_FIXED_BORDER_RUNNING_GFX
11137 if (!moved && !player->is_active)
11139 player->is_moving = FALSE;
11140 player->is_digging = FALSE;
11141 player->is_collecting = FALSE;
11142 player->is_snapping = FALSE;
11143 player->is_pushing = FALSE;
11151 if (moved & MP_MOVING && !ScreenMovPos &&
11152 (player->index_nr == game.centered_player_nr ||
11153 game.centered_player_nr == -1))
11155 if (moved & MP_MOVING && !ScreenMovPos &&
11156 (player == local_player || !options.network))
11159 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
11160 int offset = (setup.scroll_delay ? 3 : 0);
11162 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
11164 /* actual player has left the screen -- scroll in that direction */
11165 if (jx != old_jx) /* player has moved horizontally */
11166 scroll_x += (jx - old_jx);
11167 else /* player has moved vertically */
11168 scroll_y += (jy - old_jy);
11172 if (jx != old_jx) /* player has moved horizontally */
11174 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
11175 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
11176 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
11178 /* don't scroll over playfield boundaries */
11179 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
11180 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
11182 /* don't scroll more than one field at a time */
11183 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
11185 /* don't scroll against the player's moving direction */
11186 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
11187 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
11188 scroll_x = old_scroll_x;
11190 else /* player has moved vertically */
11192 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
11193 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
11194 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
11196 /* don't scroll over playfield boundaries */
11197 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
11198 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
11200 /* don't scroll more than one field at a time */
11201 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
11203 /* don't scroll against the player's moving direction */
11204 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
11205 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
11206 scroll_y = old_scroll_y;
11210 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
11213 if (!options.network && game.centered_player_nr == -1 &&
11214 !AllPlayersInVisibleScreen())
11216 scroll_x = old_scroll_x;
11217 scroll_y = old_scroll_y;
11221 if (!options.network && !AllPlayersInVisibleScreen())
11223 scroll_x = old_scroll_x;
11224 scroll_y = old_scroll_y;
11229 ScrollScreen(player, SCROLL_INIT);
11230 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
11235 player->StepFrame = 0;
11237 if (moved & MP_MOVING)
11239 if (old_jx != jx && old_jy == jy)
11240 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
11241 else if (old_jx == jx && old_jy != jy)
11242 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
11244 DrawLevelField(jx, jy); /* for "crumbled sand" */
11246 player->last_move_dir = player->MovDir;
11247 player->is_moving = TRUE;
11248 player->is_snapping = FALSE;
11249 player->is_switching = FALSE;
11250 player->is_dropping = FALSE;
11251 player->is_dropping_pressed = FALSE;
11252 player->drop_pressed_delay = 0;
11255 /* should better be called here than above, but this breaks some tapes */
11256 ScrollPlayer(player, SCROLL_INIT);
11261 CheckGravityMovementWhenNotMoving(player);
11263 player->is_moving = FALSE;
11265 /* at this point, the player is allowed to move, but cannot move right now
11266 (e.g. because of something blocking the way) -- ensure that the player
11267 is also allowed to move in the next frame (in old versions before 3.1.1,
11268 the player was forced to wait again for eight frames before next try) */
11270 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
11271 player->move_delay = 0; /* allow direct movement in the next frame */
11274 if (player->move_delay == -1) /* not yet initialized by DigField() */
11275 player->move_delay = player->move_delay_value;
11277 if (game.engine_version < VERSION_IDENT(3,0,7,0))
11279 TestIfPlayerTouchesBadThing(jx, jy);
11280 TestIfPlayerTouchesCustomElement(jx, jy);
11283 if (!player->active)
11284 RemovePlayer(player);
11289 void ScrollPlayer(struct PlayerInfo *player, int mode)
11291 int jx = player->jx, jy = player->jy;
11292 int last_jx = player->last_jx, last_jy = player->last_jy;
11293 int move_stepsize = TILEX / player->move_delay_value;
11295 #if USE_NEW_PLAYER_SPEED
11296 if (!player->active)
11299 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
11302 if (!player->active || player->MovPos == 0)
11306 if (mode == SCROLL_INIT)
11308 player->actual_frame_counter = FrameCounter;
11309 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
11311 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
11312 Feld[last_jx][last_jy] == EL_EMPTY)
11314 int last_field_block_delay = 0; /* start with no blocking at all */
11315 int block_delay_adjustment = player->block_delay_adjustment;
11317 /* if player blocks last field, add delay for exactly one move */
11318 if (player->block_last_field)
11320 last_field_block_delay += player->move_delay_value;
11322 /* when blocking enabled, prevent moving up despite gravity */
11323 #if USE_PLAYER_GRAVITY
11324 if (player->gravity && player->MovDir == MV_UP)
11325 block_delay_adjustment = -1;
11327 if (game.gravity && player->MovDir == MV_UP)
11328 block_delay_adjustment = -1;
11332 /* add block delay adjustment (also possible when not blocking) */
11333 last_field_block_delay += block_delay_adjustment;
11335 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
11336 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
11339 #if USE_NEW_PLAYER_SPEED
11340 if (player->MovPos != 0) /* player has not yet reached destination */
11346 else if (!FrameReached(&player->actual_frame_counter, 1))
11349 #if USE_NEW_PLAYER_SPEED
11350 if (player->MovPos != 0)
11352 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
11353 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
11355 /* before DrawPlayer() to draw correct player graphic for this case */
11356 if (player->MovPos == 0)
11357 CheckGravityMovement(player);
11360 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
11361 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
11363 /* before DrawPlayer() to draw correct player graphic for this case */
11364 if (player->MovPos == 0)
11365 CheckGravityMovement(player);
11368 if (player->MovPos == 0) /* player reached destination field */
11370 if (player->move_delay_reset_counter > 0)
11372 player->move_delay_reset_counter--;
11374 if (player->move_delay_reset_counter == 0)
11376 /* continue with normal speed after quickly moving through gate */
11377 HALVE_PLAYER_SPEED(player);
11379 /* be able to make the next move without delay */
11380 player->move_delay = 0;
11384 player->last_jx = jx;
11385 player->last_jy = jy;
11387 if (Feld[jx][jy] == EL_EXIT_OPEN ||
11388 Feld[jx][jy] == EL_EM_EXIT_OPEN ||
11389 Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
11390 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
11391 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
11392 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
11394 DrawPlayer(player); /* needed here only to cleanup last field */
11395 RemovePlayer(player);
11397 if (local_player->friends_still_needed == 0 ||
11398 IS_SP_ELEMENT(Feld[jx][jy]))
11399 PlayerWins(player);
11402 /* this breaks one level: "machine", level 000 */
11404 int move_direction = player->MovDir;
11405 int enter_side = MV_DIR_OPPOSITE(move_direction);
11406 int leave_side = move_direction;
11407 int old_jx = last_jx;
11408 int old_jy = last_jy;
11409 int old_element = Feld[old_jx][old_jy];
11410 int new_element = Feld[jx][jy];
11412 if (IS_CUSTOM_ELEMENT(old_element))
11413 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
11415 player->index_bit, leave_side);
11417 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
11418 CE_PLAYER_LEAVES_X,
11419 player->index_bit, leave_side);
11421 if (IS_CUSTOM_ELEMENT(new_element))
11422 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
11423 player->index_bit, enter_side);
11425 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
11426 CE_PLAYER_ENTERS_X,
11427 player->index_bit, enter_side);
11429 CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
11430 CE_MOVE_OF_X, move_direction);
11433 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
11435 TestIfPlayerTouchesBadThing(jx, jy);
11436 TestIfPlayerTouchesCustomElement(jx, jy);
11438 /* needed because pushed element has not yet reached its destination,
11439 so it would trigger a change event at its previous field location */
11440 if (!player->is_pushing)
11441 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
11443 if (!player->active)
11444 RemovePlayer(player);
11447 if (!local_player->LevelSolved && level.use_step_counter)
11457 if (TimeLeft <= 10 && setup.time_limit)
11458 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11460 DrawGameValue_Time(TimeLeft);
11462 if (!TimeLeft && setup.time_limit)
11463 for (i = 0; i < MAX_PLAYERS; i++)
11464 KillPlayer(&stored_player[i]);
11466 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
11467 DrawGameValue_Time(TimePlayed);
11470 if (tape.single_step && tape.recording && !tape.pausing &&
11471 !player->programmed_action)
11472 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
11476 void ScrollScreen(struct PlayerInfo *player, int mode)
11478 static unsigned long screen_frame_counter = 0;
11480 if (mode == SCROLL_INIT)
11482 /* set scrolling step size according to actual player's moving speed */
11483 ScrollStepSize = TILEX / player->move_delay_value;
11485 screen_frame_counter = FrameCounter;
11486 ScreenMovDir = player->MovDir;
11487 ScreenMovPos = player->MovPos;
11488 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
11491 else if (!FrameReached(&screen_frame_counter, 1))
11496 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
11497 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
11498 redraw_mask |= REDRAW_FIELD;
11501 ScreenMovDir = MV_NONE;
11504 void TestIfPlayerTouchesCustomElement(int x, int y)
11506 static int xy[4][2] =
11513 static int trigger_sides[4][2] =
11515 /* center side border side */
11516 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
11517 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
11518 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
11519 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
11521 static int touch_dir[4] =
11523 MV_LEFT | MV_RIGHT,
11528 int center_element = Feld[x][y]; /* should always be non-moving! */
11531 for (i = 0; i < NUM_DIRECTIONS; i++)
11533 int xx = x + xy[i][0];
11534 int yy = y + xy[i][1];
11535 int center_side = trigger_sides[i][0];
11536 int border_side = trigger_sides[i][1];
11537 int border_element;
11539 if (!IN_LEV_FIELD(xx, yy))
11542 if (IS_PLAYER(x, y))
11544 struct PlayerInfo *player = PLAYERINFO(x, y);
11546 if (game.engine_version < VERSION_IDENT(3,0,7,0))
11547 border_element = Feld[xx][yy]; /* may be moving! */
11548 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
11549 border_element = Feld[xx][yy];
11550 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
11551 border_element = MovingOrBlocked2Element(xx, yy);
11553 continue; /* center and border element do not touch */
11555 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
11556 player->index_bit, border_side);
11557 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
11558 CE_PLAYER_TOUCHES_X,
11559 player->index_bit, border_side);
11561 else if (IS_PLAYER(xx, yy))
11563 struct PlayerInfo *player = PLAYERINFO(xx, yy);
11565 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
11567 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
11568 continue; /* center and border element do not touch */
11571 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
11572 player->index_bit, center_side);
11573 CheckTriggeredElementChangeByPlayer(x, y, center_element,
11574 CE_PLAYER_TOUCHES_X,
11575 player->index_bit, center_side);
11581 #if USE_ELEMENT_TOUCHING_BUGFIX
11583 void TestIfElementTouchesCustomElement(int x, int y)
11585 static int xy[4][2] =
11592 static int trigger_sides[4][2] =
11594 /* center side border side */
11595 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
11596 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
11597 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
11598 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
11600 static int touch_dir[4] =
11602 MV_LEFT | MV_RIGHT,
11607 boolean change_center_element = FALSE;
11608 int center_element = Feld[x][y]; /* should always be non-moving! */
11609 int border_element_old[NUM_DIRECTIONS];
11612 for (i = 0; i < NUM_DIRECTIONS; i++)
11614 int xx = x + xy[i][0];
11615 int yy = y + xy[i][1];
11616 int border_element;
11618 border_element_old[i] = -1;
11620 if (!IN_LEV_FIELD(xx, yy))
11623 if (game.engine_version < VERSION_IDENT(3,0,7,0))
11624 border_element = Feld[xx][yy]; /* may be moving! */
11625 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
11626 border_element = Feld[xx][yy];
11627 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
11628 border_element = MovingOrBlocked2Element(xx, yy);
11630 continue; /* center and border element do not touch */
11632 border_element_old[i] = border_element;
11635 for (i = 0; i < NUM_DIRECTIONS; i++)
11637 int xx = x + xy[i][0];
11638 int yy = y + xy[i][1];
11639 int center_side = trigger_sides[i][0];
11640 int border_element = border_element_old[i];
11642 if (border_element == -1)
11645 /* check for change of border element */
11646 CheckElementChangeBySide(xx, yy, border_element, center_element,
11647 CE_TOUCHING_X, center_side);
11650 for (i = 0; i < NUM_DIRECTIONS; i++)
11652 int border_side = trigger_sides[i][1];
11653 int border_element = border_element_old[i];
11655 if (border_element == -1)
11658 /* check for change of center element (but change it only once) */
11659 if (!change_center_element)
11660 change_center_element =
11661 CheckElementChangeBySide(x, y, center_element, border_element,
11662 CE_TOUCHING_X, border_side);
11668 void TestIfElementTouchesCustomElement_OLD(int x, int y)
11670 static int xy[4][2] =
11677 static int trigger_sides[4][2] =
11679 /* center side border side */
11680 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
11681 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
11682 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
11683 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
11685 static int touch_dir[4] =
11687 MV_LEFT | MV_RIGHT,
11692 boolean change_center_element = FALSE;
11693 int center_element = Feld[x][y]; /* should always be non-moving! */
11696 for (i = 0; i < NUM_DIRECTIONS; i++)
11698 int xx = x + xy[i][0];
11699 int yy = y + xy[i][1];
11700 int center_side = trigger_sides[i][0];
11701 int border_side = trigger_sides[i][1];
11702 int border_element;
11704 if (!IN_LEV_FIELD(xx, yy))
11707 if (game.engine_version < VERSION_IDENT(3,0,7,0))
11708 border_element = Feld[xx][yy]; /* may be moving! */
11709 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
11710 border_element = Feld[xx][yy];
11711 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
11712 border_element = MovingOrBlocked2Element(xx, yy);
11714 continue; /* center and border element do not touch */
11716 /* check for change of center element (but change it only once) */
11717 if (!change_center_element)
11718 change_center_element =
11719 CheckElementChangeBySide(x, y, center_element, border_element,
11720 CE_TOUCHING_X, border_side);
11722 /* check for change of border element */
11723 CheckElementChangeBySide(xx, yy, border_element, center_element,
11724 CE_TOUCHING_X, center_side);
11730 void TestIfElementHitsCustomElement(int x, int y, int direction)
11732 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
11733 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
11734 int hitx = x + dx, hity = y + dy;
11735 int hitting_element = Feld[x][y];
11736 int touched_element;
11738 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
11741 touched_element = (IN_LEV_FIELD(hitx, hity) ?
11742 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
11744 if (IN_LEV_FIELD(hitx, hity))
11746 int opposite_direction = MV_DIR_OPPOSITE(direction);
11747 int hitting_side = direction;
11748 int touched_side = opposite_direction;
11749 boolean object_hit = (!IS_MOVING(hitx, hity) ||
11750 MovDir[hitx][hity] != direction ||
11751 ABS(MovPos[hitx][hity]) <= TILEY / 2);
11757 CheckElementChangeBySide(x, y, hitting_element, touched_element,
11758 CE_HITTING_X, touched_side);
11760 CheckElementChangeBySide(hitx, hity, touched_element,
11761 hitting_element, CE_HIT_BY_X, hitting_side);
11763 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
11764 CE_HIT_BY_SOMETHING, opposite_direction);
11768 /* "hitting something" is also true when hitting the playfield border */
11769 CheckElementChangeBySide(x, y, hitting_element, touched_element,
11770 CE_HITTING_SOMETHING, direction);
11774 void TestIfElementSmashesCustomElement(int x, int y, int direction)
11776 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
11777 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
11778 int hitx = x + dx, hity = y + dy;
11779 int hitting_element = Feld[x][y];
11780 int touched_element;
11782 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
11783 !IS_FREE(hitx, hity) &&
11784 (!IS_MOVING(hitx, hity) ||
11785 MovDir[hitx][hity] != direction ||
11786 ABS(MovPos[hitx][hity]) <= TILEY / 2));
11789 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
11793 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
11797 touched_element = (IN_LEV_FIELD(hitx, hity) ?
11798 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
11800 CheckElementChangeBySide(x, y, hitting_element, touched_element,
11801 EP_CAN_SMASH_EVERYTHING, direction);
11803 if (IN_LEV_FIELD(hitx, hity))
11805 int opposite_direction = MV_DIR_OPPOSITE(direction);
11806 int hitting_side = direction;
11807 int touched_side = opposite_direction;
11809 int touched_element = MovingOrBlocked2Element(hitx, hity);
11812 boolean object_hit = (!IS_MOVING(hitx, hity) ||
11813 MovDir[hitx][hity] != direction ||
11814 ABS(MovPos[hitx][hity]) <= TILEY / 2);
11823 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
11824 CE_SMASHED_BY_SOMETHING, opposite_direction);
11826 CheckElementChangeBySide(x, y, hitting_element, touched_element,
11827 CE_OTHER_IS_SMASHING, touched_side);
11829 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
11830 CE_OTHER_GETS_SMASHED, hitting_side);
11836 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
11838 int i, kill_x = -1, kill_y = -1;
11840 int bad_element = -1;
11841 static int test_xy[4][2] =
11848 static int test_dir[4] =
11856 for (i = 0; i < NUM_DIRECTIONS; i++)
11858 int test_x, test_y, test_move_dir, test_element;
11860 test_x = good_x + test_xy[i][0];
11861 test_y = good_y + test_xy[i][1];
11863 if (!IN_LEV_FIELD(test_x, test_y))
11867 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
11869 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
11871 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
11872 2nd case: DONT_TOUCH style bad thing does not move away from good thing
11874 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
11875 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
11879 bad_element = test_element;
11885 if (kill_x != -1 || kill_y != -1)
11887 if (IS_PLAYER(good_x, good_y))
11889 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
11891 if (player->shield_deadly_time_left > 0 &&
11892 !IS_INDESTRUCTIBLE(bad_element))
11893 Bang(kill_x, kill_y);
11894 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
11895 KillPlayer(player);
11898 Bang(good_x, good_y);
11902 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
11904 int i, kill_x = -1, kill_y = -1;
11905 int bad_element = Feld[bad_x][bad_y];
11906 static int test_xy[4][2] =
11913 static int touch_dir[4] =
11915 MV_LEFT | MV_RIGHT,
11920 static int test_dir[4] =
11928 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
11931 for (i = 0; i < NUM_DIRECTIONS; i++)
11933 int test_x, test_y, test_move_dir, test_element;
11935 test_x = bad_x + test_xy[i][0];
11936 test_y = bad_y + test_xy[i][1];
11937 if (!IN_LEV_FIELD(test_x, test_y))
11941 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
11943 test_element = Feld[test_x][test_y];
11945 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
11946 2nd case: DONT_TOUCH style bad thing does not move away from good thing
11948 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
11949 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
11951 /* good thing is player or penguin that does not move away */
11952 if (IS_PLAYER(test_x, test_y))
11954 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
11956 if (bad_element == EL_ROBOT && player->is_moving)
11957 continue; /* robot does not kill player if he is moving */
11959 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
11961 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
11962 continue; /* center and border element do not touch */
11969 else if (test_element == EL_PENGUIN)
11978 if (kill_x != -1 || kill_y != -1)
11980 if (IS_PLAYER(kill_x, kill_y))
11982 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
11984 if (player->shield_deadly_time_left > 0 &&
11985 !IS_INDESTRUCTIBLE(bad_element))
11986 Bang(bad_x, bad_y);
11987 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
11988 KillPlayer(player);
11991 Bang(kill_x, kill_y);
11995 void TestIfPlayerTouchesBadThing(int x, int y)
11997 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
12000 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
12002 TestIfGoodThingHitsBadThing(x, y, move_dir);
12005 void TestIfBadThingTouchesPlayer(int x, int y)
12007 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
12010 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
12012 TestIfBadThingHitsGoodThing(x, y, move_dir);
12015 void TestIfFriendTouchesBadThing(int x, int y)
12017 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
12020 void TestIfBadThingTouchesFriend(int x, int y)
12022 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
12025 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
12027 int i, kill_x = bad_x, kill_y = bad_y;
12028 static int xy[4][2] =
12036 for (i = 0; i < NUM_DIRECTIONS; i++)
12040 x = bad_x + xy[i][0];
12041 y = bad_y + xy[i][1];
12042 if (!IN_LEV_FIELD(x, y))
12045 element = Feld[x][y];
12046 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
12047 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
12055 if (kill_x != bad_x || kill_y != bad_y)
12056 Bang(bad_x, bad_y);
12059 void KillPlayer(struct PlayerInfo *player)
12061 int jx = player->jx, jy = player->jy;
12063 if (!player->active)
12066 /* the following code was introduced to prevent an infinite loop when calling
12068 -> CheckTriggeredElementChangeExt()
12069 -> ExecuteCustomElementAction()
12071 -> (infinitely repeating the above sequence of function calls)
12072 which occurs when killing the player while having a CE with the setting
12073 "kill player X when explosion of <player X>"; the solution using a new
12074 field "player->killed" was chosen for backwards compatibility, although
12075 clever use of the fields "player->active" etc. would probably also work */
12077 if (player->killed)
12081 player->killed = TRUE;
12083 /* remove accessible field at the player's position */
12084 Feld[jx][jy] = EL_EMPTY;
12086 /* deactivate shield (else Bang()/Explode() would not work right) */
12087 player->shield_normal_time_left = 0;
12088 player->shield_deadly_time_left = 0;
12091 BuryPlayer(player);
12094 static void KillPlayerUnlessEnemyProtected(int x, int y)
12096 if (!PLAYER_ENEMY_PROTECTED(x, y))
12097 KillPlayer(PLAYERINFO(x, y));
12100 static void KillPlayerUnlessExplosionProtected(int x, int y)
12102 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
12103 KillPlayer(PLAYERINFO(x, y));
12106 void BuryPlayer(struct PlayerInfo *player)
12108 int jx = player->jx, jy = player->jy;
12110 if (!player->active)
12113 PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
12114 PlayLevelSound(jx, jy, SND_GAME_LOSING);
12116 player->GameOver = TRUE;
12117 RemovePlayer(player);
12120 void RemovePlayer(struct PlayerInfo *player)
12122 int jx = player->jx, jy = player->jy;
12123 int i, found = FALSE;
12125 player->present = FALSE;
12126 player->active = FALSE;
12128 if (!ExplodeField[jx][jy])
12129 StorePlayer[jx][jy] = 0;
12131 if (player->is_moving)
12132 DrawLevelField(player->last_jx, player->last_jy);
12134 for (i = 0; i < MAX_PLAYERS; i++)
12135 if (stored_player[i].active)
12139 AllPlayersGone = TRUE;
12145 #if USE_NEW_SNAP_DELAY
12146 static void setFieldForSnapping(int x, int y, int element, int direction)
12148 struct ElementInfo *ei = &element_info[element];
12149 int direction_bit = MV_DIR_TO_BIT(direction);
12150 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
12151 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
12152 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
12154 Feld[x][y] = EL_ELEMENT_SNAPPING;
12155 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
12157 ResetGfxAnimation(x, y);
12159 GfxElement[x][y] = element;
12160 GfxAction[x][y] = action;
12161 GfxDir[x][y] = direction;
12162 GfxFrame[x][y] = -1;
12167 =============================================================================
12168 checkDiagonalPushing()
12169 -----------------------------------------------------------------------------
12170 check if diagonal input device direction results in pushing of object
12171 (by checking if the alternative direction is walkable, diggable, ...)
12172 =============================================================================
12175 static boolean checkDiagonalPushing(struct PlayerInfo *player,
12176 int x, int y, int real_dx, int real_dy)
12178 int jx, jy, dx, dy, xx, yy;
12180 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
12183 /* diagonal direction: check alternative direction */
12188 xx = jx + (dx == 0 ? real_dx : 0);
12189 yy = jy + (dy == 0 ? real_dy : 0);
12191 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
12195 =============================================================================
12197 -----------------------------------------------------------------------------
12198 x, y: field next to player (non-diagonal) to try to dig to
12199 real_dx, real_dy: direction as read from input device (can be diagonal)
12200 =============================================================================
12203 int DigField(struct PlayerInfo *player,
12204 int oldx, int oldy, int x, int y,
12205 int real_dx, int real_dy, int mode)
12207 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
12208 boolean player_was_pushing = player->is_pushing;
12209 boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
12210 boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
12211 int jx = oldx, jy = oldy;
12212 int dx = x - jx, dy = y - jy;
12213 int nextx = x + dx, nexty = y + dy;
12214 int move_direction = (dx == -1 ? MV_LEFT :
12215 dx == +1 ? MV_RIGHT :
12217 dy == +1 ? MV_DOWN : MV_NONE);
12218 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
12219 int dig_side = MV_DIR_OPPOSITE(move_direction);
12220 int old_element = Feld[jx][jy];
12221 #if USE_FIXED_DONT_RUN_INTO
12222 int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
12228 if (is_player) /* function can also be called by EL_PENGUIN */
12230 if (player->MovPos == 0)
12232 player->is_digging = FALSE;
12233 player->is_collecting = FALSE;
12236 if (player->MovPos == 0) /* last pushing move finished */
12237 player->is_pushing = FALSE;
12239 if (mode == DF_NO_PUSH) /* player just stopped pushing */
12241 player->is_switching = FALSE;
12242 player->push_delay = -1;
12244 return MP_NO_ACTION;
12248 #if !USE_FIXED_DONT_RUN_INTO
12249 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
12250 return MP_NO_ACTION;
12253 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
12254 old_element = Back[jx][jy];
12256 /* in case of element dropped at player position, check background */
12257 else if (Back[jx][jy] != EL_EMPTY &&
12258 game.engine_version >= VERSION_IDENT(2,2,0,0))
12259 old_element = Back[jx][jy];
12261 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
12262 return MP_NO_ACTION; /* field has no opening in this direction */
12264 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
12265 return MP_NO_ACTION; /* field has no opening in this direction */
12267 #if USE_FIXED_DONT_RUN_INTO
12268 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
12272 Feld[jx][jy] = player->artwork_element;
12273 InitMovingField(jx, jy, MV_DOWN);
12274 Store[jx][jy] = EL_ACID;
12275 ContinueMoving(jx, jy);
12276 BuryPlayer(player);
12278 return MP_DONT_RUN_INTO;
12282 #if USE_FIXED_DONT_RUN_INTO
12283 if (player_can_move && DONT_RUN_INTO(element))
12285 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
12287 return MP_DONT_RUN_INTO;
12291 #if USE_FIXED_DONT_RUN_INTO
12292 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
12293 return MP_NO_ACTION;
12296 #if !USE_FIXED_DONT_RUN_INTO
12297 element = Feld[x][y];
12300 collect_count = element_info[element].collect_count_initial;
12302 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
12303 return MP_NO_ACTION;
12305 if (game.engine_version < VERSION_IDENT(2,2,0,0))
12306 player_can_move = player_can_move_or_snap;
12308 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
12309 game.engine_version >= VERSION_IDENT(2,2,0,0))
12311 CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
12312 player->index_bit, dig_side);
12313 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
12314 player->index_bit, dig_side);
12316 if (element == EL_DC_LANDMINE)
12319 if (Feld[x][y] != element) /* field changed by snapping */
12322 return MP_NO_ACTION;
12325 #if USE_PLAYER_GRAVITY
12326 if (player->gravity && is_player && !player->is_auto_moving &&
12327 canFallDown(player) && move_direction != MV_DOWN &&
12328 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
12329 return MP_NO_ACTION; /* player cannot walk here due to gravity */
12331 if (game.gravity && is_player && !player->is_auto_moving &&
12332 canFallDown(player) && move_direction != MV_DOWN &&
12333 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
12334 return MP_NO_ACTION; /* player cannot walk here due to gravity */
12337 if (player_can_move &&
12338 IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
12340 int sound_element = SND_ELEMENT(element);
12341 int sound_action = ACTION_WALKING;
12343 if (IS_RND_GATE(element))
12345 if (!player->key[RND_GATE_NR(element)])
12346 return MP_NO_ACTION;
12348 else if (IS_RND_GATE_GRAY(element))
12350 if (!player->key[RND_GATE_GRAY_NR(element)])
12351 return MP_NO_ACTION;
12353 else if (IS_RND_GATE_GRAY_ACTIVE(element))
12355 if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
12356 return MP_NO_ACTION;
12358 else if (element == EL_EXIT_OPEN ||
12359 element == EL_EM_EXIT_OPEN ||
12360 element == EL_STEEL_EXIT_OPEN ||
12361 element == EL_EM_STEEL_EXIT_OPEN ||
12362 element == EL_SP_EXIT_OPEN ||
12363 element == EL_SP_EXIT_OPENING)
12365 sound_action = ACTION_PASSING; /* player is passing exit */
12367 else if (element == EL_EMPTY)
12369 sound_action = ACTION_MOVING; /* nothing to walk on */
12372 /* play sound from background or player, whatever is available */
12373 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
12374 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
12376 PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
12378 else if (player_can_move &&
12379 IS_PASSABLE(element) && canPassField(x, y, move_direction))
12381 if (!ACCESS_FROM(element, opposite_direction))
12382 return MP_NO_ACTION; /* field not accessible from this direction */
12384 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
12385 return MP_NO_ACTION;
12387 if (IS_EM_GATE(element))
12389 if (!player->key[EM_GATE_NR(element)])
12390 return MP_NO_ACTION;
12392 else if (IS_EM_GATE_GRAY(element))
12394 if (!player->key[EM_GATE_GRAY_NR(element)])
12395 return MP_NO_ACTION;
12397 else if (IS_EM_GATE_GRAY_ACTIVE(element))
12399 if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
12400 return MP_NO_ACTION;
12402 else if (IS_EMC_GATE(element))
12404 if (!player->key[EMC_GATE_NR(element)])
12405 return MP_NO_ACTION;
12407 else if (IS_EMC_GATE_GRAY(element))
12409 if (!player->key[EMC_GATE_GRAY_NR(element)])
12410 return MP_NO_ACTION;
12412 else if (IS_EMC_GATE_GRAY_ACTIVE(element))
12414 if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
12415 return MP_NO_ACTION;
12417 else if (element == EL_DC_GATE_WHITE ||
12418 element == EL_DC_GATE_WHITE_GRAY ||
12419 element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
12421 if (player->num_white_keys == 0)
12422 return MP_NO_ACTION;
12424 player->num_white_keys--;
12426 else if (IS_SP_PORT(element))
12428 if (element == EL_SP_GRAVITY_PORT_LEFT ||
12429 element == EL_SP_GRAVITY_PORT_RIGHT ||
12430 element == EL_SP_GRAVITY_PORT_UP ||
12431 element == EL_SP_GRAVITY_PORT_DOWN)
12432 #if USE_PLAYER_GRAVITY
12433 player->gravity = !player->gravity;
12435 game.gravity = !game.gravity;
12437 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
12438 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
12439 element == EL_SP_GRAVITY_ON_PORT_UP ||
12440 element == EL_SP_GRAVITY_ON_PORT_DOWN)
12441 #if USE_PLAYER_GRAVITY
12442 player->gravity = TRUE;
12444 game.gravity = TRUE;
12446 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
12447 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
12448 element == EL_SP_GRAVITY_OFF_PORT_UP ||
12449 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
12450 #if USE_PLAYER_GRAVITY
12451 player->gravity = FALSE;
12453 game.gravity = FALSE;
12457 /* automatically move to the next field with double speed */
12458 player->programmed_action = move_direction;
12460 if (player->move_delay_reset_counter == 0)
12462 player->move_delay_reset_counter = 2; /* two double speed steps */
12464 DOUBLE_PLAYER_SPEED(player);
12467 PlayLevelSoundAction(x, y, ACTION_PASSING);
12469 else if (player_can_move_or_snap && IS_DIGGABLE(element))
12473 if (mode != DF_SNAP)
12475 GfxElement[x][y] = GFX_ELEMENT(element);
12476 player->is_digging = TRUE;
12479 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
12481 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
12482 player->index_bit, dig_side);
12484 if (mode == DF_SNAP)
12486 #if USE_NEW_SNAP_DELAY
12487 if (level.block_snap_field)
12488 setFieldForSnapping(x, y, element, move_direction);
12490 TestIfElementTouchesCustomElement(x, y); /* for empty space */
12492 TestIfElementTouchesCustomElement(x, y); /* for empty space */
12495 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
12496 player->index_bit, dig_side);
12499 else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
12503 if (is_player && mode != DF_SNAP)
12505 GfxElement[x][y] = element;
12506 player->is_collecting = TRUE;
12509 if (element == EL_SPEED_PILL)
12511 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
12513 else if (element == EL_EXTRA_TIME && level.time > 0)
12515 TimeLeft += level.extra_time;
12516 DrawGameValue_Time(TimeLeft);
12518 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
12520 player->shield_normal_time_left += level.shield_normal_time;
12521 if (element == EL_SHIELD_DEADLY)
12522 player->shield_deadly_time_left += level.shield_deadly_time;
12524 else if (element == EL_DYNAMITE ||
12525 element == EL_EM_DYNAMITE ||
12526 element == EL_SP_DISK_RED)
12528 if (player->inventory_size < MAX_INVENTORY_SIZE)
12529 player->inventory_element[player->inventory_size++] = element;
12531 DrawGameDoorValues();
12533 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
12535 player->dynabomb_count++;
12536 player->dynabombs_left++;
12538 else if (element == EL_DYNABOMB_INCREASE_SIZE)
12540 player->dynabomb_size++;
12542 else if (element == EL_DYNABOMB_INCREASE_POWER)
12544 player->dynabomb_xl = TRUE;
12546 else if (IS_KEY(element))
12548 player->key[KEY_NR(element)] = TRUE;
12550 DrawGameDoorValues();
12552 else if (element == EL_DC_KEY_WHITE)
12554 player->num_white_keys++;
12556 /* display white keys? */
12557 /* DrawGameDoorValues(); */
12559 else if (IS_ENVELOPE(element))
12561 player->show_envelope = element;
12563 else if (element == EL_EMC_LENSES)
12565 game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
12567 RedrawAllInvisibleElementsForLenses();
12569 else if (element == EL_EMC_MAGNIFIER)
12571 game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
12573 RedrawAllInvisibleElementsForMagnifier();
12575 else if (IS_DROPPABLE(element) ||
12576 IS_THROWABLE(element)) /* can be collected and dropped */
12580 if (collect_count == 0)
12581 player->inventory_infinite_element = element;
12583 for (i = 0; i < collect_count; i++)
12584 if (player->inventory_size < MAX_INVENTORY_SIZE)
12585 player->inventory_element[player->inventory_size++] = element;
12587 DrawGameDoorValues();
12589 else if (collect_count > 0)
12591 local_player->gems_still_needed -= collect_count;
12592 if (local_player->gems_still_needed < 0)
12593 local_player->gems_still_needed = 0;
12595 DrawGameValue_Emeralds(local_player->gems_still_needed);
12598 RaiseScoreElement(element);
12599 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
12602 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
12603 player->index_bit, dig_side);
12605 if (mode == DF_SNAP)
12607 #if USE_NEW_SNAP_DELAY
12608 if (level.block_snap_field)
12609 setFieldForSnapping(x, y, element, move_direction);
12611 TestIfElementTouchesCustomElement(x, y); /* for empty space */
12613 TestIfElementTouchesCustomElement(x, y); /* for empty space */
12616 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
12617 player->index_bit, dig_side);
12620 else if (player_can_move_or_snap && IS_PUSHABLE(element))
12622 if (mode == DF_SNAP && element != EL_BD_ROCK)
12623 return MP_NO_ACTION;
12625 if (CAN_FALL(element) && dy)
12626 return MP_NO_ACTION;
12628 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
12629 !(element == EL_SPRING && level.use_spring_bug))
12630 return MP_NO_ACTION;
12632 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
12633 ((move_direction & MV_VERTICAL &&
12634 ((element_info[element].move_pattern & MV_LEFT &&
12635 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
12636 (element_info[element].move_pattern & MV_RIGHT &&
12637 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
12638 (move_direction & MV_HORIZONTAL &&
12639 ((element_info[element].move_pattern & MV_UP &&
12640 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
12641 (element_info[element].move_pattern & MV_DOWN &&
12642 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
12643 return MP_NO_ACTION;
12645 /* do not push elements already moving away faster than player */
12646 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
12647 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
12648 return MP_NO_ACTION;
12650 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
12652 if (player->push_delay_value == -1 || !player_was_pushing)
12653 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12655 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
12657 if (player->push_delay_value == -1)
12658 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12660 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
12662 if (!player->is_pushing)
12663 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12666 player->is_pushing = TRUE;
12667 player->is_active = TRUE;
12669 if (!(IN_LEV_FIELD(nextx, nexty) &&
12670 (IS_FREE(nextx, nexty) ||
12671 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
12672 IS_SB_ELEMENT(element)))))
12673 return MP_NO_ACTION;
12675 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
12676 return MP_NO_ACTION;
12678 if (player->push_delay == -1) /* new pushing; restart delay */
12679 player->push_delay = 0;
12681 if (player->push_delay < player->push_delay_value &&
12682 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
12683 element != EL_SPRING && element != EL_BALLOON)
12685 /* make sure that there is no move delay before next try to push */
12686 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
12687 player->move_delay = 0;
12689 return MP_NO_ACTION;
12692 if (IS_SB_ELEMENT(element))
12694 if (element == EL_SOKOBAN_FIELD_FULL)
12696 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
12697 local_player->sokobanfields_still_needed++;
12700 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
12702 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
12703 local_player->sokobanfields_still_needed--;
12706 Feld[x][y] = EL_SOKOBAN_OBJECT;
12708 if (Back[x][y] == Back[nextx][nexty])
12709 PlayLevelSoundAction(x, y, ACTION_PUSHING);
12710 else if (Back[x][y] != 0)
12711 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
12714 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
12717 if (local_player->sokobanfields_still_needed == 0 &&
12718 game.emulation == EMU_SOKOBAN)
12720 PlayerWins(player);
12722 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
12726 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12728 InitMovingField(x, y, move_direction);
12729 GfxAction[x][y] = ACTION_PUSHING;
12731 if (mode == DF_SNAP)
12732 ContinueMoving(x, y);
12734 MovPos[x][y] = (dx != 0 ? dx : dy);
12736 Pushed[x][y] = TRUE;
12737 Pushed[nextx][nexty] = TRUE;
12739 if (game.engine_version < VERSION_IDENT(2,2,0,7))
12740 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12742 player->push_delay_value = -1; /* get new value later */
12744 /* check for element change _after_ element has been pushed */
12745 if (game.use_change_when_pushing_bug)
12747 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
12748 player->index_bit, dig_side);
12749 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
12750 player->index_bit, dig_side);
12753 else if (IS_SWITCHABLE(element))
12755 if (PLAYER_SWITCHING(player, x, y))
12757 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
12758 player->index_bit, dig_side);
12763 player->is_switching = TRUE;
12764 player->switch_x = x;
12765 player->switch_y = y;
12767 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
12769 if (element == EL_ROBOT_WHEEL)
12771 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
12775 DrawLevelField(x, y);
12777 else if (element == EL_SP_TERMINAL)
12781 SCAN_PLAYFIELD(xx, yy)
12783 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
12785 else if (Feld[xx][yy] == EL_SP_TERMINAL)
12786 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
12789 else if (IS_BELT_SWITCH(element))
12791 ToggleBeltSwitch(x, y);
12793 else if (element == EL_SWITCHGATE_SWITCH_UP ||
12794 element == EL_SWITCHGATE_SWITCH_DOWN ||
12795 element == EL_DC_SWITCHGATE_SWITCH_UP ||
12796 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
12798 ToggleSwitchgateSwitch(x, y);
12800 else if (element == EL_LIGHT_SWITCH ||
12801 element == EL_LIGHT_SWITCH_ACTIVE)
12803 ToggleLightSwitch(x, y);
12805 else if (element == EL_TIMEGATE_SWITCH ||
12806 element == EL_DC_TIMEGATE_SWITCH)
12808 ActivateTimegateSwitch(x, y);
12810 else if (element == EL_BALLOON_SWITCH_LEFT ||
12811 element == EL_BALLOON_SWITCH_RIGHT ||
12812 element == EL_BALLOON_SWITCH_UP ||
12813 element == EL_BALLOON_SWITCH_DOWN ||
12814 element == EL_BALLOON_SWITCH_NONE ||
12815 element == EL_BALLOON_SWITCH_ANY)
12817 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
12818 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
12819 element == EL_BALLOON_SWITCH_UP ? MV_UP :
12820 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
12821 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
12824 else if (element == EL_LAMP)
12826 Feld[x][y] = EL_LAMP_ACTIVE;
12827 local_player->lights_still_needed--;
12829 ResetGfxAnimation(x, y);
12830 DrawLevelField(x, y);
12832 else if (element == EL_TIME_ORB_FULL)
12834 Feld[x][y] = EL_TIME_ORB_EMPTY;
12836 if (level.time > 0 || level.use_time_orb_bug)
12838 TimeLeft += level.time_orb_time;
12839 DrawGameValue_Time(TimeLeft);
12842 ResetGfxAnimation(x, y);
12843 DrawLevelField(x, y);
12845 else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
12846 element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
12850 game.ball_state = !game.ball_state;
12852 SCAN_PLAYFIELD(xx, yy)
12854 int e = Feld[xx][yy];
12856 if (game.ball_state)
12858 if (e == EL_EMC_MAGIC_BALL)
12859 CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
12860 else if (e == EL_EMC_MAGIC_BALL_SWITCH)
12861 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
12865 if (e == EL_EMC_MAGIC_BALL_ACTIVE)
12866 CreateField(xx, yy, EL_EMC_MAGIC_BALL);
12867 else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
12868 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
12873 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
12874 player->index_bit, dig_side);
12876 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
12877 player->index_bit, dig_side);
12879 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
12880 player->index_bit, dig_side);
12886 if (!PLAYER_SWITCHING(player, x, y))
12888 player->is_switching = TRUE;
12889 player->switch_x = x;
12890 player->switch_y = y;
12892 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
12893 player->index_bit, dig_side);
12894 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
12895 player->index_bit, dig_side);
12897 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
12898 player->index_bit, dig_side);
12899 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
12900 player->index_bit, dig_side);
12903 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
12904 player->index_bit, dig_side);
12905 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
12906 player->index_bit, dig_side);
12908 return MP_NO_ACTION;
12911 player->push_delay = -1;
12913 if (is_player) /* function can also be called by EL_PENGUIN */
12915 if (Feld[x][y] != element) /* really digged/collected something */
12917 player->is_collecting = !player->is_digging;
12918 player->is_active = TRUE;
12925 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
12927 int jx = player->jx, jy = player->jy;
12928 int x = jx + dx, y = jy + dy;
12929 int snap_direction = (dx == -1 ? MV_LEFT :
12930 dx == +1 ? MV_RIGHT :
12932 dy == +1 ? MV_DOWN : MV_NONE);
12933 boolean can_continue_snapping = (level.continuous_snapping &&
12934 WasJustFalling[x][y] < CHECK_DELAY_FALLING);
12936 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
12939 if (!player->active || !IN_LEV_FIELD(x, y))
12947 if (player->MovPos == 0)
12948 player->is_pushing = FALSE;
12950 player->is_snapping = FALSE;
12952 if (player->MovPos == 0)
12954 player->is_moving = FALSE;
12955 player->is_digging = FALSE;
12956 player->is_collecting = FALSE;
12962 #if USE_NEW_CONTINUOUS_SNAPPING
12963 /* prevent snapping with already pressed snap key when not allowed */
12964 if (player->is_snapping && !can_continue_snapping)
12967 if (player->is_snapping)
12971 player->MovDir = snap_direction;
12973 if (player->MovPos == 0)
12975 player->is_moving = FALSE;
12976 player->is_digging = FALSE;
12977 player->is_collecting = FALSE;
12980 player->is_dropping = FALSE;
12981 player->is_dropping_pressed = FALSE;
12982 player->drop_pressed_delay = 0;
12984 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
12987 player->is_snapping = TRUE;
12988 player->is_active = TRUE;
12990 if (player->MovPos == 0)
12992 player->is_moving = FALSE;
12993 player->is_digging = FALSE;
12994 player->is_collecting = FALSE;
12997 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
12998 DrawLevelField(player->last_jx, player->last_jy);
13000 DrawLevelField(x, y);
13005 boolean DropElement(struct PlayerInfo *player)
13007 int old_element, new_element;
13008 int dropx = player->jx, dropy = player->jy;
13009 int drop_direction = player->MovDir;
13010 int drop_side = drop_direction;
13011 int drop_element = (player->inventory_size > 0 ?
13012 player->inventory_element[player->inventory_size - 1] :
13013 player->inventory_infinite_element != EL_UNDEFINED ?
13014 player->inventory_infinite_element :
13015 player->dynabombs_left > 0 ?
13016 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
13019 player->is_dropping_pressed = TRUE;
13021 /* do not drop an element on top of another element; when holding drop key
13022 pressed without moving, dropped element must move away before the next
13023 element can be dropped (this is especially important if the next element
13024 is dynamite, which can be placed on background for historical reasons) */
13025 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
13028 if (IS_THROWABLE(drop_element))
13030 dropx += GET_DX_FROM_DIR(drop_direction);
13031 dropy += GET_DY_FROM_DIR(drop_direction);
13033 if (!IN_LEV_FIELD(dropx, dropy))
13037 old_element = Feld[dropx][dropy]; /* old element at dropping position */
13038 new_element = drop_element; /* default: no change when dropping */
13040 /* check if player is active, not moving and ready to drop */
13041 if (!player->active || player->MovPos || player->drop_delay > 0)
13044 /* check if player has anything that can be dropped */
13045 if (new_element == EL_UNDEFINED)
13048 /* check if drop key was pressed long enough for EM style dynamite */
13049 if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
13052 /* check if anything can be dropped at the current position */
13053 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
13056 /* collected custom elements can only be dropped on empty fields */
13057 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
13060 if (old_element != EL_EMPTY)
13061 Back[dropx][dropy] = old_element; /* store old element on this field */
13063 ResetGfxAnimation(dropx, dropy);
13064 ResetRandomAnimationValue(dropx, dropy);
13066 if (player->inventory_size > 0 ||
13067 player->inventory_infinite_element != EL_UNDEFINED)
13069 if (player->inventory_size > 0)
13071 player->inventory_size--;
13073 DrawGameDoorValues();
13075 if (new_element == EL_DYNAMITE)
13076 new_element = EL_DYNAMITE_ACTIVE;
13077 else if (new_element == EL_EM_DYNAMITE)
13078 new_element = EL_EM_DYNAMITE_ACTIVE;
13079 else if (new_element == EL_SP_DISK_RED)
13080 new_element = EL_SP_DISK_RED_ACTIVE;
13083 Feld[dropx][dropy] = new_element;
13085 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
13086 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
13087 el2img(Feld[dropx][dropy]), 0);
13089 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
13091 /* needed if previous element just changed to "empty" in the last frame */
13092 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
13094 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
13095 player->index_bit, drop_side);
13096 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
13098 player->index_bit, drop_side);
13100 TestIfElementTouchesCustomElement(dropx, dropy);
13102 else /* player is dropping a dyna bomb */
13104 player->dynabombs_left--;
13106 Feld[dropx][dropy] = new_element;
13108 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
13109 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
13110 el2img(Feld[dropx][dropy]), 0);
13112 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
13115 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
13116 InitField_WithBug1(dropx, dropy, FALSE);
13118 new_element = Feld[dropx][dropy]; /* element might have changed */
13120 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
13121 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
13123 int move_direction, nextx, nexty;
13125 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
13126 MovDir[dropx][dropy] = drop_direction;
13128 move_direction = MovDir[dropx][dropy];
13129 nextx = dropx + GET_DX_FROM_DIR(move_direction);
13130 nexty = dropy + GET_DY_FROM_DIR(move_direction);
13132 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
13134 #if USE_FIX_IMPACT_COLLISION
13135 /* do not cause impact style collision by dropping elements that can fall */
13136 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
13138 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
13142 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
13143 player->is_dropping = TRUE;
13145 player->drop_pressed_delay = 0;
13146 player->is_dropping_pressed = FALSE;
13148 player->drop_x = dropx;
13149 player->drop_y = dropy;
13154 /* ------------------------------------------------------------------------- */
13155 /* game sound playing functions */
13156 /* ------------------------------------------------------------------------- */
13158 static int *loop_sound_frame = NULL;
13159 static int *loop_sound_volume = NULL;
13161 void InitPlayLevelSound()
13163 int num_sounds = getSoundListSize();
13165 checked_free(loop_sound_frame);
13166 checked_free(loop_sound_volume);
13168 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
13169 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
13172 static void PlayLevelSound(int x, int y, int nr)
13174 int sx = SCREENX(x), sy = SCREENY(y);
13175 int volume, stereo_position;
13176 int max_distance = 8;
13177 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
13179 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
13180 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
13183 if (!IN_LEV_FIELD(x, y) ||
13184 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
13185 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
13188 volume = SOUND_MAX_VOLUME;
13190 if (!IN_SCR_FIELD(sx, sy))
13192 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
13193 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
13195 volume -= volume * (dx > dy ? dx : dy) / max_distance;
13198 stereo_position = (SOUND_MAX_LEFT +
13199 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
13200 (SCR_FIELDX + 2 * max_distance));
13202 if (IS_LOOP_SOUND(nr))
13204 /* This assures that quieter loop sounds do not overwrite louder ones,
13205 while restarting sound volume comparison with each new game frame. */
13207 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
13210 loop_sound_volume[nr] = volume;
13211 loop_sound_frame[nr] = FrameCounter;
13214 PlaySoundExt(nr, volume, stereo_position, type);
13217 static void PlayLevelSoundNearest(int x, int y, int sound_action)
13219 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
13220 x > LEVELX(BX2) ? LEVELX(BX2) : x,
13221 y < LEVELY(BY1) ? LEVELY(BY1) :
13222 y > LEVELY(BY2) ? LEVELY(BY2) : y,
13226 static void PlayLevelSoundAction(int x, int y, int action)
13228 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
13231 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
13233 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
13235 if (sound_effect != SND_UNDEFINED)
13236 PlayLevelSound(x, y, sound_effect);
13239 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
13242 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
13244 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
13245 PlayLevelSound(x, y, sound_effect);
13248 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
13250 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
13252 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
13253 PlayLevelSound(x, y, sound_effect);
13256 static void StopLevelSoundActionIfLoop(int x, int y, int action)
13258 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
13260 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
13261 StopSound(sound_effect);
13264 static void PlayLevelMusic()
13266 if (levelset.music[level_nr] != MUS_UNDEFINED)
13267 PlayMusic(levelset.music[level_nr]); /* from config file */
13269 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
13272 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
13274 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
13275 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
13276 int x = xx - 1 - offset;
13277 int y = yy - 1 - offset;
13282 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
13286 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13290 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
13294 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
13298 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
13302 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
13306 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
13309 case SAMPLE_android_clone:
13310 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
13313 case SAMPLE_android_move:
13314 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
13317 case SAMPLE_spring:
13318 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
13322 PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
13326 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
13329 case SAMPLE_eater_eat:
13330 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13334 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
13337 case SAMPLE_collect:
13338 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13341 case SAMPLE_diamond:
13342 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
13345 case SAMPLE_squash:
13346 /* !!! CHECK THIS !!! */
13348 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
13350 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
13354 case SAMPLE_wonderfall:
13355 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
13359 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
13363 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13367 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13371 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
13375 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
13379 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
13382 case SAMPLE_wonder:
13383 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
13387 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
13390 case SAMPLE_exit_open:
13391 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
13394 case SAMPLE_exit_leave:
13395 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
13398 case SAMPLE_dynamite:
13399 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
13403 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
13407 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
13411 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
13415 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
13419 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
13423 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
13427 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
13433 void ChangeTime(int value)
13435 int *time = (level.time == 0 ? &TimePlayed : &TimeLeft);
13439 /* EMC game engine uses value from time counter of RND game engine */
13440 level.native_em_level->lev->time = *time;
13442 DrawGameValue_Time(*time);
13445 void RaiseScore(int value)
13447 /* EMC game engine and RND game engine have separate score counters */
13448 int *score = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
13449 &level.native_em_level->lev->score : &local_player->score);
13453 DrawGameValue_Score(*score);
13457 void RaiseScore(int value)
13459 local_player->score += value;
13461 DrawGameValue_Score(local_player->score);
13464 void RaiseScoreElement(int element)
13469 case EL_BD_DIAMOND:
13470 case EL_EMERALD_YELLOW:
13471 case EL_EMERALD_RED:
13472 case EL_EMERALD_PURPLE:
13473 case EL_SP_INFOTRON:
13474 RaiseScore(level.score[SC_EMERALD]);
13477 RaiseScore(level.score[SC_DIAMOND]);
13480 RaiseScore(level.score[SC_CRYSTAL]);
13483 RaiseScore(level.score[SC_PEARL]);
13486 case EL_BD_BUTTERFLY:
13487 case EL_SP_ELECTRON:
13488 RaiseScore(level.score[SC_BUG]);
13491 case EL_BD_FIREFLY:
13492 case EL_SP_SNIKSNAK:
13493 RaiseScore(level.score[SC_SPACESHIP]);
13496 case EL_DARK_YAMYAM:
13497 RaiseScore(level.score[SC_YAMYAM]);
13500 RaiseScore(level.score[SC_ROBOT]);
13503 RaiseScore(level.score[SC_PACMAN]);
13506 RaiseScore(level.score[SC_NUT]);
13509 case EL_EM_DYNAMITE:
13510 case EL_SP_DISK_RED:
13511 case EL_DYNABOMB_INCREASE_NUMBER:
13512 case EL_DYNABOMB_INCREASE_SIZE:
13513 case EL_DYNABOMB_INCREASE_POWER:
13514 RaiseScore(level.score[SC_DYNAMITE]);
13516 case EL_SHIELD_NORMAL:
13517 case EL_SHIELD_DEADLY:
13518 RaiseScore(level.score[SC_SHIELD]);
13520 case EL_EXTRA_TIME:
13521 RaiseScore(level.extra_time_score);
13535 case EL_DC_KEY_WHITE:
13536 RaiseScore(level.score[SC_KEY]);
13539 RaiseScore(element_info[element].collect_score);
13544 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
13546 if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
13548 #if defined(NETWORK_AVALIABLE)
13549 if (options.network)
13550 SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
13556 game_status = GAME_MODE_MAIN;
13562 FadeOut(REDRAW_FIELD);
13564 game_status = GAME_MODE_MAIN;
13566 DrawAndFadeInMainMenu(REDRAW_FIELD);
13570 else /* continue playing the game */
13572 if (tape.playing && tape.deactivate_display)
13573 TapeDeactivateDisplayOff(TRUE);
13575 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
13577 if (tape.playing && tape.deactivate_display)
13578 TapeDeactivateDisplayOn();
13582 void RequestQuitGame(boolean ask_if_really_quit)
13584 boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
13585 boolean skip_request = AllPlayersGone || quick_quit;
13587 RequestQuitGameExt(skip_request, quick_quit,
13588 "Do you really want to quit the game ?");
13592 /* ------------------------------------------------------------------------- */
13593 /* random generator functions */
13594 /* ------------------------------------------------------------------------- */
13596 unsigned int InitEngineRandom_RND(long seed)
13598 game.num_random_calls = 0;
13601 unsigned int rnd_seed = InitEngineRandom(seed);
13603 printf("::: START RND: %d\n", rnd_seed);
13608 return InitEngineRandom(seed);
13614 unsigned int RND(int max)
13618 game.num_random_calls++;
13620 return GetEngineRandom(max);
13627 /* ------------------------------------------------------------------------- */
13628 /* game engine snapshot handling functions */
13629 /* ------------------------------------------------------------------------- */
13631 #define ARGS_ADDRESS_AND_SIZEOF(x) (&(x)), (sizeof(x))
13633 struct EngineSnapshotInfo
13635 /* runtime values for custom element collect score */
13636 int collect_score[NUM_CUSTOM_ELEMENTS];
13638 /* runtime values for group element choice position */
13639 int choice_pos[NUM_GROUP_ELEMENTS];
13641 /* runtime values for belt position animations */
13642 int belt_graphic[4 * NUM_BELT_PARTS];
13643 int belt_anim_mode[4 * NUM_BELT_PARTS];
13646 struct EngineSnapshotNodeInfo
13653 static struct EngineSnapshotInfo engine_snapshot_rnd;
13654 static ListNode *engine_snapshot_list = NULL;
13655 static char *snapshot_level_identifier = NULL;
13656 static int snapshot_level_nr = -1;
13658 void FreeEngineSnapshot()
13660 while (engine_snapshot_list != NULL)
13661 deleteNodeFromList(&engine_snapshot_list, engine_snapshot_list->key,
13664 setString(&snapshot_level_identifier, NULL);
13665 snapshot_level_nr = -1;
13668 static void SaveEngineSnapshotValues_RND()
13670 static int belt_base_active_element[4] =
13672 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
13673 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
13674 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
13675 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
13679 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
13681 int element = EL_CUSTOM_START + i;
13683 engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
13686 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
13688 int element = EL_GROUP_START + i;
13690 engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
13693 for (i = 0; i < 4; i++)
13695 for (j = 0; j < NUM_BELT_PARTS; j++)
13697 int element = belt_base_active_element[i] + j;
13698 int graphic = el2img(element);
13699 int anim_mode = graphic_info[graphic].anim_mode;
13701 engine_snapshot_rnd.belt_graphic[i * 4 + j] = graphic;
13702 engine_snapshot_rnd.belt_anim_mode[i * 4 + j] = anim_mode;
13707 static void LoadEngineSnapshotValues_RND()
13709 unsigned long num_random_calls = game.num_random_calls;
13712 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
13714 int element = EL_CUSTOM_START + i;
13716 element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
13719 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
13721 int element = EL_GROUP_START + i;
13723 element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
13726 for (i = 0; i < 4; i++)
13728 for (j = 0; j < NUM_BELT_PARTS; j++)
13730 int graphic = engine_snapshot_rnd.belt_graphic[i * 4 + j];
13731 int anim_mode = engine_snapshot_rnd.belt_anim_mode[i * 4 + j];
13733 graphic_info[graphic].anim_mode = anim_mode;
13737 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
13739 InitRND(tape.random_seed);
13740 for (i = 0; i < num_random_calls; i++)
13744 if (game.num_random_calls != num_random_calls)
13746 Error(ERR_INFO, "number of random calls out of sync");
13747 Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
13748 Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
13749 Error(ERR_EXIT, "this should not happen -- please debug");
13753 static void SaveEngineSnapshotBuffer(void *buffer, int size)
13755 struct EngineSnapshotNodeInfo *bi =
13756 checked_calloc(sizeof(struct EngineSnapshotNodeInfo));
13758 bi->buffer_orig = buffer;
13759 bi->buffer_copy = checked_malloc(size);
13762 memcpy(bi->buffer_copy, buffer, size);
13764 addNodeToList(&engine_snapshot_list, NULL, bi);
13767 void SaveEngineSnapshot()
13769 FreeEngineSnapshot(); /* free previous snapshot, if needed */
13771 if (level_editor_test_game) /* do not save snapshots from editor */
13774 /* copy some special values to a structure better suited for the snapshot */
13776 SaveEngineSnapshotValues_RND();
13777 SaveEngineSnapshotValues_EM();
13779 /* save values stored in special snapshot structure */
13781 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
13782 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
13784 /* save further RND engine values */
13786 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
13787 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
13788 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
13790 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
13791 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
13792 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
13793 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
13795 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
13796 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
13797 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
13798 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
13799 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
13801 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
13802 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
13803 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
13805 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
13807 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
13809 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
13810 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
13812 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
13813 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
13814 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
13815 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
13816 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
13817 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
13818 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
13819 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
13820 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
13821 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
13822 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
13823 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
13824 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
13825 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
13826 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
13827 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
13828 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
13829 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
13831 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
13832 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
13834 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
13835 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
13836 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
13838 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
13839 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
13841 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
13842 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
13843 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
13844 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
13845 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
13847 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
13848 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
13850 /* save level identification information */
13852 setString(&snapshot_level_identifier, leveldir_current->identifier);
13853 snapshot_level_nr = level_nr;
13856 ListNode *node = engine_snapshot_list;
13859 while (node != NULL)
13861 num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
13866 printf("::: size of engine snapshot: %d bytes\n", num_bytes);
13870 static void LoadEngineSnapshotBuffer(struct EngineSnapshotNodeInfo *bi)
13872 memcpy(bi->buffer_orig, bi->buffer_copy, bi->size);
13875 void LoadEngineSnapshot()
13877 ListNode *node = engine_snapshot_list;
13879 if (engine_snapshot_list == NULL)
13882 while (node != NULL)
13884 LoadEngineSnapshotBuffer((struct EngineSnapshotNodeInfo *)node->content);
13889 /* restore special values from snapshot structure */
13891 LoadEngineSnapshotValues_RND();
13892 LoadEngineSnapshotValues_EM();
13895 boolean CheckEngineSnapshot()
13897 return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
13898 snapshot_level_nr == level_nr);
13902 /* ---------- new game button stuff ---------------------------------------- */
13904 /* graphic position values for game buttons */
13905 #define GAME_BUTTON_XSIZE 30
13906 #define GAME_BUTTON_YSIZE 30
13907 #define GAME_BUTTON_XPOS 5
13908 #define GAME_BUTTON_YPOS 215
13909 #define SOUND_BUTTON_XPOS 5
13910 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
13912 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
13913 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
13914 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
13915 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
13916 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
13917 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
13924 } gamebutton_info[NUM_GAME_BUTTONS] =
13927 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
13932 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
13933 GAME_CTRL_ID_PAUSE,
13937 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
13942 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
13943 SOUND_CTRL_ID_MUSIC,
13944 "background music on/off"
13947 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
13948 SOUND_CTRL_ID_LOOPS,
13949 "sound loops on/off"
13952 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
13953 SOUND_CTRL_ID_SIMPLE,
13954 "normal sounds on/off"
13958 void CreateGameButtons()
13962 for (i = 0; i < NUM_GAME_BUTTONS; i++)
13964 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
13965 struct GadgetInfo *gi;
13968 unsigned long event_mask;
13969 int gd_xoffset, gd_yoffset;
13970 int gd_x1, gd_x2, gd_y1, gd_y2;
13973 gd_xoffset = gamebutton_info[i].x;
13974 gd_yoffset = gamebutton_info[i].y;
13975 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
13976 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
13978 if (id == GAME_CTRL_ID_STOP ||
13979 id == GAME_CTRL_ID_PAUSE ||
13980 id == GAME_CTRL_ID_PLAY)
13982 button_type = GD_TYPE_NORMAL_BUTTON;
13984 event_mask = GD_EVENT_RELEASED;
13985 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
13986 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
13990 button_type = GD_TYPE_CHECK_BUTTON;
13992 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
13993 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
13994 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
13995 event_mask = GD_EVENT_PRESSED;
13996 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
13997 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
14000 gi = CreateGadget(GDI_CUSTOM_ID, id,
14001 GDI_INFO_TEXT, gamebutton_info[i].infotext,
14002 GDI_X, DX + gd_xoffset,
14003 GDI_Y, DY + gd_yoffset,
14004 GDI_WIDTH, GAME_BUTTON_XSIZE,
14005 GDI_HEIGHT, GAME_BUTTON_YSIZE,
14006 GDI_TYPE, button_type,
14007 GDI_STATE, GD_BUTTON_UNPRESSED,
14008 GDI_CHECKED, checked,
14009 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
14010 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
14011 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
14012 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
14013 GDI_EVENT_MASK, event_mask,
14014 GDI_CALLBACK_ACTION, HandleGameButtons,
14018 Error(ERR_EXIT, "cannot create gadget");
14020 game_gadget[id] = gi;
14024 void FreeGameButtons()
14028 for (i = 0; i < NUM_GAME_BUTTONS; i++)
14029 FreeGadget(game_gadget[i]);
14032 static void MapGameButtons()
14036 for (i = 0; i < NUM_GAME_BUTTONS; i++)
14037 MapGadget(game_gadget[i]);
14040 void UnmapGameButtons()
14044 for (i = 0; i < NUM_GAME_BUTTONS; i++)
14045 UnmapGadget(game_gadget[i]);
14048 static void HandleGameButtons(struct GadgetInfo *gi)
14050 int id = gi->custom_id;
14052 if (game_status != GAME_MODE_PLAYING)
14057 case GAME_CTRL_ID_STOP:
14061 RequestQuitGame(TRUE);
14064 case GAME_CTRL_ID_PAUSE:
14065 if (options.network)
14067 #if defined(NETWORK_AVALIABLE)
14069 SendToServer_ContinuePlaying();
14071 SendToServer_PausePlaying();
14075 TapeTogglePause(TAPE_TOGGLE_MANUAL);
14078 case GAME_CTRL_ID_PLAY:
14081 #if defined(NETWORK_AVALIABLE)
14082 if (options.network)
14083 SendToServer_ContinuePlaying();
14087 tape.pausing = FALSE;
14088 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
14093 case SOUND_CTRL_ID_MUSIC:
14094 if (setup.sound_music)
14096 setup.sound_music = FALSE;
14099 else if (audio.music_available)
14101 setup.sound = setup.sound_music = TRUE;
14103 SetAudioMode(setup.sound);
14109 case SOUND_CTRL_ID_LOOPS:
14110 if (setup.sound_loops)
14111 setup.sound_loops = FALSE;
14112 else if (audio.loops_available)
14114 setup.sound = setup.sound_loops = TRUE;
14115 SetAudioMode(setup.sound);
14119 case SOUND_CTRL_ID_SIMPLE:
14120 if (setup.sound_simple)
14121 setup.sound_simple = FALSE;
14122 else if (audio.sound_available)
14124 setup.sound = setup.sound_simple = TRUE;
14125 SetAudioMode(setup.sound);