1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back! *
3 *----------------------------------------------------------*
4 * (c) 1995-2006 Artsoft Entertainment *
6 * Detmolder Strasse 189 *
9 * e-mail: info@artsoft.org *
10 *----------------------------------------------------------*
12 ***********************************************************/
14 #include "libgame/libgame.h"
24 /* EXPERIMENTAL STUFF */
25 #define USE_NEW_AMOEBA_CODE FALSE
27 /* EXPERIMENTAL STUFF */
28 #define USE_NEW_STUFF ( 1)
30 #define USE_NEW_SP_SLIPPERY (USE_NEW_STUFF * 1)
31 #define USE_NEW_CUSTOM_VALUE (USE_NEW_STUFF * 1)
32 #define USE_NEW_PLAYER_ANIM (USE_NEW_STUFF * 1)
33 #define USE_NEW_ALL_SLIPPERY (USE_NEW_STUFF * 1)
34 #define USE_NEW_PLAYER_SPEED (USE_NEW_STUFF * 1)
35 #define USE_NEW_DELAYED_ACTION (USE_NEW_STUFF * 1)
36 #define USE_NEW_SNAP_DELAY (USE_NEW_STUFF * 1)
37 #define USE_ONLY_ONE_CHANGE_PER_FRAME (USE_NEW_STUFF * 1)
38 #define USE_ONE_MORE_CHANGE_PER_FRAME (USE_NEW_STUFF * 1)
39 #define USE_FIXED_DONT_RUN_INTO (USE_NEW_STUFF * 1)
40 #define USE_NEW_SPRING_BUMPER (USE_NEW_STUFF * 1)
41 #define USE_STOP_CHANGED_ELEMENTS (USE_NEW_STUFF * 1)
42 #define USE_ELEMENT_TOUCHING_BUGFIX (USE_NEW_STUFF * 1)
43 #define USE_NEW_CONTINUOUS_SNAPPING (USE_NEW_STUFF * 1)
44 #define USE_GFX_RESET_GFX_ANIMATION (USE_NEW_STUFF * 1)
45 #define USE_BOTH_SWITCHGATE_SWITCHES (USE_NEW_STUFF * 1)
46 #define USE_PLAYER_GRAVITY (USE_NEW_STUFF * 1)
47 #define USE_FIXED_BORDER_RUNNING_GFX (USE_NEW_STUFF * 1)
48 #define USE_QUICKSAND_BD_ROCK_BUGFIX (USE_NEW_STUFF * 0)
50 #define USE_QUICKSAND_IMPACT_BUGFIX (USE_NEW_STUFF * 0)
52 #define USE_CODE_THAT_BREAKS_SNAKE_BITE (USE_NEW_STUFF * 1)
54 #define USE_UFAST_PLAYER_EXIT_BUGFIX (USE_NEW_STUFF * 1)
56 #define USE_GFX_RESET_ONLY_WHEN_MOVING (USE_NEW_STUFF * 1)
57 #define USE_GFX_RESET_PLAYER_ARTWORK (USE_NEW_STUFF * 1)
59 #define USE_FIX_KILLED_BY_NON_WALKABLE (USE_NEW_STUFF * 1)
60 #define USE_FIX_IMPACT_COLLISION (USE_NEW_STUFF * 1)
68 /* for MovePlayer() */
69 #define MP_NO_ACTION 0
72 #define MP_DONT_RUN_INTO (MP_MOVING | MP_ACTION)
74 /* for ScrollPlayer() */
76 #define SCROLL_GO_ON 1
78 /* for Bang()/Explode() */
79 #define EX_PHASE_START 0
80 #define EX_TYPE_NONE 0
81 #define EX_TYPE_NORMAL (1 << 0)
82 #define EX_TYPE_CENTER (1 << 1)
83 #define EX_TYPE_BORDER (1 << 2)
84 #define EX_TYPE_CROSS (1 << 3)
85 #define EX_TYPE_DYNA (1 << 4)
86 #define EX_TYPE_SINGLE_TILE (EX_TYPE_CENTER | EX_TYPE_BORDER)
89 #define PANEL_OFF() (local_player->LevelSolved_PanelOff)
90 #define PANEL_DEACTIVATED(p) ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
91 #define PANEL_XPOS(p) (DX + ALIGNED_MENU_XPOS(p))
92 #define PANEL_YPOS(p) (DY + ALIGNED_MENU_YPOS(p))
94 #define PANEL_DEACTIVATED(p) ((p).x < 0 || (p).y < 0)
95 #define PANEL_XPOS(p) (ALIGNED_XPOS((p).x, (p).width, (p).align))
96 #define PANEL_YPOS(p) ((p).y)
99 /* special positions in the game control window (relative to control window) */
100 #define XX_LEVEL1 (PANEL_XPOS(game.panel.level))
101 #define XX_LEVEL2 (PANEL_XPOS(game.panel.level) - 1)
102 #define XX_LEVEL (PANEL_XPOS(game.panel.level))
103 #define YY_LEVEL (PANEL_YPOS(game.panel.level))
104 #define XX_EMERALDS (PANEL_XPOS(game.panel.gems))
105 #define YY_EMERALDS (PANEL_YPOS(game.panel.gems))
106 #define XX_DYNAMITE (PANEL_XPOS(game.panel.inventory))
107 #define YY_DYNAMITE (PANEL_YPOS(game.panel.inventory))
108 #define XX_KEYS (PANEL_XPOS(game.panel.keys))
109 #define YY_KEYS (PANEL_YPOS(game.panel.keys))
110 #define XX_SCORE (PANEL_XPOS(game.panel.score))
111 #define YY_SCORE (PANEL_YPOS(game.panel.score))
112 #define XX_TIME1 (PANEL_XPOS(game.panel.time))
113 #define XX_TIME2 (PANEL_XPOS(game.panel.time) + 1)
114 #define XX_TIME (PANEL_XPOS(game.panel.time))
115 #define YY_TIME (PANEL_YPOS(game.panel.time))
117 /* special positions in the game control window (relative to main window) */
118 #define DX_LEVEL1 (DX + XX_LEVEL1)
119 #define DX_LEVEL2 (DX + XX_LEVEL2)
120 #define DX_LEVEL (DX + XX_LEVEL)
121 #define DY_LEVEL (DY + YY_LEVEL)
122 #define DX_EMERALDS (DX + XX_EMERALDS)
123 #define DY_EMERALDS (DY + YY_EMERALDS)
124 #define DX_DYNAMITE (DX + XX_DYNAMITE)
125 #define DY_DYNAMITE (DY + YY_DYNAMITE)
126 #define DX_KEYS (DX + XX_KEYS)
127 #define DY_KEYS (DY + YY_KEYS)
128 #define DX_SCORE (DX + XX_SCORE)
129 #define DY_SCORE (DY + YY_SCORE)
130 #define DX_TIME1 (DX + XX_TIME1)
131 #define DX_TIME2 (DX + XX_TIME2)
132 #define DX_TIME (DX + XX_TIME)
133 #define DY_TIME (DY + YY_TIME)
135 /* values for delayed check of falling and moving elements and for collision */
136 #define CHECK_DELAY_MOVING 3
137 #define CHECK_DELAY_FALLING CHECK_DELAY_MOVING
138 #define CHECK_DELAY_COLLISION 2
139 #define CHECK_DELAY_IMPACT CHECK_DELAY_COLLISION
141 /* values for initial player move delay (initial delay counter value) */
142 #define INITIAL_MOVE_DELAY_OFF -1
143 #define INITIAL_MOVE_DELAY_ON 0
145 /* values for player movement speed (which is in fact a delay value) */
146 #define MOVE_DELAY_MIN_SPEED 32
147 #define MOVE_DELAY_NORMAL_SPEED 8
148 #define MOVE_DELAY_HIGH_SPEED 4
149 #define MOVE_DELAY_MAX_SPEED 1
151 #define DOUBLE_MOVE_DELAY(x) (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
152 #define HALVE_MOVE_DELAY(x) (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
154 #define DOUBLE_PLAYER_SPEED(p) (HALVE_MOVE_DELAY( (p)->move_delay_value))
155 #define HALVE_PLAYER_SPEED(p) (DOUBLE_MOVE_DELAY((p)->move_delay_value))
157 /* values for other actions */
158 #define MOVE_STEPSIZE_NORMAL (TILEX / MOVE_DELAY_NORMAL_SPEED)
159 #define MOVE_STEPSIZE_MIN (1)
160 #define MOVE_STEPSIZE_MAX (TILEX)
162 #define GET_DX_FROM_DIR(d) ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
163 #define GET_DY_FROM_DIR(d) ((d) == MV_UP ? -1 : (d) == MV_DOWN ? 1 : 0)
165 #define INIT_GFX_RANDOM() (GetSimpleRandom(1000000))
167 #define GET_NEW_PUSH_DELAY(e) ( (element_info[e].push_delay_fixed) + \
168 RND(element_info[e].push_delay_random))
169 #define GET_NEW_DROP_DELAY(e) ( (element_info[e].drop_delay_fixed) + \
170 RND(element_info[e].drop_delay_random))
171 #define GET_NEW_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
172 RND(element_info[e].move_delay_random))
173 #define GET_MAX_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
174 (element_info[e].move_delay_random))
175 #define GET_NEW_CE_VALUE(e) ( (element_info[e].ce_value_fixed_initial) +\
176 RND(element_info[e].ce_value_random_initial))
177 #define GET_CE_SCORE(e) ( (element_info[e].collect_score))
178 #define GET_CHANGE_DELAY(c) ( ((c)->delay_fixed * (c)->delay_frames) + \
179 RND((c)->delay_random * (c)->delay_frames))
180 #define GET_CE_DELAY_VALUE(c) ( ((c)->delay_fixed) + \
181 RND((c)->delay_random))
184 #define GET_VALID_RUNTIME_ELEMENT(e) \
185 ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
187 #define RESOLVED_REFERENCE_ELEMENT(be, e) \
188 ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START : \
189 (be) + (e) - EL_SELF > EL_CUSTOM_END ? EL_CUSTOM_END : \
190 (be) + (e) - EL_SELF)
192 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs) \
193 ((e) == EL_TRIGGER_PLAYER ? (ch)->actual_trigger_player : \
194 (e) == EL_TRIGGER_ELEMENT ? (ch)->actual_trigger_element : \
195 (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value : \
196 (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score : \
197 (e) == EL_CURRENT_CE_VALUE ? (cv) : \
198 (e) == EL_CURRENT_CE_SCORE ? (cs) : \
199 (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ? \
200 RESOLVED_REFERENCE_ELEMENT(be, e) : \
203 #define CAN_GROW_INTO(e) \
204 ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
206 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition) \
207 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
210 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition) \
211 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
212 (CAN_MOVE_INTO_ACID(e) && \
213 Feld[x][y] == EL_ACID) || \
216 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition) \
217 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
218 (CAN_MOVE_INTO_ACID(e) && \
219 Feld[x][y] == EL_ACID) || \
222 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition) \
223 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
225 (CAN_MOVE_INTO_ACID(e) && \
226 Feld[x][y] == EL_ACID) || \
227 (DONT_COLLIDE_WITH(e) && \
229 !PLAYER_ENEMY_PROTECTED(x, y))))
231 #define ELEMENT_CAN_ENTER_FIELD(e, x, y) \
232 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
234 #define SATELLITE_CAN_ENTER_FIELD(x, y) \
235 ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
237 #define ANDROID_CAN_ENTER_FIELD(e, x, y) \
238 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
240 #define ANDROID_CAN_CLONE_FIELD(x, y) \
241 (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
242 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
244 #define ENEMY_CAN_ENTER_FIELD(e, x, y) \
245 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
247 #define YAMYAM_CAN_ENTER_FIELD(e, x, y) \
248 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
250 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y) \
251 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
253 #define PACMAN_CAN_ENTER_FIELD(e, x, y) \
254 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
256 #define PIG_CAN_ENTER_FIELD(e, x, y) \
257 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
259 #define PENGUIN_CAN_ENTER_FIELD(e, x, y) \
260 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
261 Feld[x][y] == EL_EM_EXIT_OPEN || \
262 Feld[x][y] == EL_STEEL_EXIT_OPEN || \
263 Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
264 IS_FOOD_PENGUIN(Feld[x][y])))
265 #define DRAGON_CAN_ENTER_FIELD(e, x, y) \
266 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
268 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition) \
269 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
271 #define SPRING_CAN_ENTER_FIELD(e, x, y) \
272 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
274 #define SPRING_CAN_BUMP_FROM_FIELD(x, y) \
275 (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER || \
276 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
278 #define MOVE_ENTER_EL(e) (element_info[e].move_enter_element)
280 #define CE_ENTER_FIELD_COND(e, x, y) \
281 (!IS_PLAYER(x, y) && \
282 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
284 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y) \
285 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
287 #define IN_LEV_FIELD_AND_IS_FREE(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
288 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
290 #define ACCESS_FROM(e, d) (element_info[e].access_direction &(d))
291 #define IS_WALKABLE_FROM(e, d) (IS_WALKABLE(e) && ACCESS_FROM(e, d))
292 #define IS_PASSABLE_FROM(e, d) (IS_PASSABLE(e) && ACCESS_FROM(e, d))
293 #define IS_ACCESSIBLE_FROM(e, d) (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
295 /* game button identifiers */
296 #define GAME_CTRL_ID_STOP 0
297 #define GAME_CTRL_ID_PAUSE 1
298 #define GAME_CTRL_ID_PLAY 2
299 #define SOUND_CTRL_ID_MUSIC 3
300 #define SOUND_CTRL_ID_LOOPS 4
301 #define SOUND_CTRL_ID_SIMPLE 5
303 #define NUM_GAME_BUTTONS 6
306 /* forward declaration for internal use */
308 static void CreateField(int, int, int);
310 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
311 static void AdvanceFrameAndPlayerCounters(int);
313 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
314 static boolean MovePlayer(struct PlayerInfo *, int, int);
315 static void ScrollPlayer(struct PlayerInfo *, int);
316 static void ScrollScreen(struct PlayerInfo *, int);
318 int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
320 static void InitBeltMovement(void);
321 static void CloseAllOpenTimegates(void);
322 static void CheckGravityMovement(struct PlayerInfo *);
323 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
324 static void KillPlayerUnlessEnemyProtected(int, int);
325 static void KillPlayerUnlessExplosionProtected(int, int);
327 static void TestIfPlayerTouchesCustomElement(int, int);
328 static void TestIfElementTouchesCustomElement(int, int);
329 static void TestIfElementHitsCustomElement(int, int, int);
331 static void TestIfElementSmashesCustomElement(int, int, int);
334 static void HandleElementChange(int, int, int);
335 static void ExecuteCustomElementAction(int, int, int, int);
336 static boolean ChangeElement(int, int, int, int);
338 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
339 #define CheckTriggeredElementChange(x, y, e, ev) \
340 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
341 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s) \
342 CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
343 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s) \
344 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
345 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p) \
346 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
348 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
349 #define CheckElementChange(x, y, e, te, ev) \
350 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
351 #define CheckElementChangeByPlayer(x, y, e, ev, p, s) \
352 CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
353 #define CheckElementChangeBySide(x, y, e, te, ev, s) \
354 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
356 static void PlayLevelSound(int, int, int);
357 static void PlayLevelSoundNearest(int, int, int);
358 static void PlayLevelSoundAction(int, int, int);
359 static void PlayLevelSoundElementAction(int, int, int, int);
360 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
361 static void PlayLevelSoundActionIfLoop(int, int, int);
362 static void StopLevelSoundActionIfLoop(int, int, int);
363 static void PlayLevelMusic();
365 static void MapGameButtons();
366 static void HandleGameButtons(struct GadgetInfo *);
368 int AmoebeNachbarNr(int, int);
369 void AmoebeUmwandeln(int, int);
370 void ContinueMoving(int, int);
372 void InitMovDir(int, int);
373 void InitAmoebaNr(int, int);
374 int NewHiScore(void);
376 void TestIfGoodThingHitsBadThing(int, int, int);
377 void TestIfBadThingHitsGoodThing(int, int, int);
378 void TestIfPlayerTouchesBadThing(int, int);
379 void TestIfPlayerRunsIntoBadThing(int, int, int);
380 void TestIfBadThingTouchesPlayer(int, int);
381 void TestIfBadThingRunsIntoPlayer(int, int, int);
382 void TestIfFriendTouchesBadThing(int, int);
383 void TestIfBadThingTouchesFriend(int, int);
384 void TestIfBadThingTouchesOtherBadThing(int, int);
386 void KillPlayer(struct PlayerInfo *);
387 void BuryPlayer(struct PlayerInfo *);
388 void RemovePlayer(struct PlayerInfo *);
390 boolean SnapField(struct PlayerInfo *, int, int);
391 boolean DropElement(struct PlayerInfo *);
393 static int getInvisibleActiveFromInvisibleElement(int);
394 static int getInvisibleFromInvisibleActiveElement(int);
396 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
398 /* for detection of endless loops, caused by custom element programming */
399 /* (using maximal playfield width x 10 is just a rough approximation) */
400 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH (MAX_PLAYFIELD_WIDTH * 10)
402 #define RECURSION_LOOP_DETECTION_START(e, rc) \
404 if (recursion_loop_detected) \
407 if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH) \
409 recursion_loop_detected = TRUE; \
410 recursion_loop_element = (e); \
413 recursion_loop_depth++; \
416 #define RECURSION_LOOP_DETECTION_END() \
418 recursion_loop_depth--; \
421 static int recursion_loop_depth;
422 static boolean recursion_loop_detected;
423 static boolean recursion_loop_element;
426 /* ------------------------------------------------------------------------- */
427 /* definition of elements that automatically change to other elements after */
428 /* a specified time, eventually calling a function when changing */
429 /* ------------------------------------------------------------------------- */
431 /* forward declaration for changer functions */
432 static void InitBuggyBase(int, int);
433 static void WarnBuggyBase(int, int);
435 static void InitTrap(int, int);
436 static void ActivateTrap(int, int);
437 static void ChangeActiveTrap(int, int);
439 static void InitRobotWheel(int, int);
440 static void RunRobotWheel(int, int);
441 static void StopRobotWheel(int, int);
443 static void InitTimegateWheel(int, int);
444 static void RunTimegateWheel(int, int);
446 static void InitMagicBallDelay(int, int);
447 static void ActivateMagicBall(int, int);
449 struct ChangingElementInfo
454 void (*pre_change_function)(int x, int y);
455 void (*change_function)(int x, int y);
456 void (*post_change_function)(int x, int y);
459 static struct ChangingElementInfo change_delay_list[] =
494 EL_STEEL_EXIT_OPENING,
502 EL_STEEL_EXIT_CLOSING,
503 EL_STEEL_EXIT_CLOSED,
530 EL_EM_STEEL_EXIT_OPENING,
531 EL_EM_STEEL_EXIT_OPEN,
538 EL_EM_STEEL_EXIT_CLOSING,
542 EL_EM_STEEL_EXIT_CLOSED,
566 EL_SWITCHGATE_OPENING,
574 EL_SWITCHGATE_CLOSING,
575 EL_SWITCHGATE_CLOSED,
607 EL_ACID_SPLASH_RIGHT,
616 EL_SP_BUGGY_BASE_ACTIVATING,
623 EL_SP_BUGGY_BASE_ACTIVATING,
624 EL_SP_BUGGY_BASE_ACTIVE,
631 EL_SP_BUGGY_BASE_ACTIVE,
655 EL_ROBOT_WHEEL_ACTIVE,
663 EL_TIMEGATE_SWITCH_ACTIVE,
671 EL_DC_TIMEGATE_SWITCH_ACTIVE,
672 EL_DC_TIMEGATE_SWITCH,
679 EL_EMC_MAGIC_BALL_ACTIVE,
680 EL_EMC_MAGIC_BALL_ACTIVE,
687 EL_EMC_SPRING_BUMPER_ACTIVE,
688 EL_EMC_SPRING_BUMPER,
695 EL_DIAGONAL_SHRINKING,
724 int push_delay_fixed, push_delay_random;
729 { EL_BALLOON, 0, 0 },
731 { EL_SOKOBAN_OBJECT, 2, 0 },
732 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
733 { EL_SATELLITE, 2, 0 },
734 { EL_SP_DISK_YELLOW, 2, 0 },
736 { EL_UNDEFINED, 0, 0 },
744 move_stepsize_list[] =
746 { EL_AMOEBA_DROP, 2 },
747 { EL_AMOEBA_DROPPING, 2 },
748 { EL_QUICKSAND_FILLING, 1 },
749 { EL_QUICKSAND_EMPTYING, 1 },
750 { EL_QUICKSAND_FAST_FILLING, 2 },
751 { EL_QUICKSAND_FAST_EMPTYING, 2 },
752 { EL_MAGIC_WALL_FILLING, 2 },
753 { EL_MAGIC_WALL_EMPTYING, 2 },
754 { EL_BD_MAGIC_WALL_FILLING, 2 },
755 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
756 { EL_DC_MAGIC_WALL_FILLING, 2 },
757 { EL_DC_MAGIC_WALL_EMPTYING, 2 },
767 collect_count_list[] =
770 { EL_BD_DIAMOND, 1 },
771 { EL_EMERALD_YELLOW, 1 },
772 { EL_EMERALD_RED, 1 },
773 { EL_EMERALD_PURPLE, 1 },
775 { EL_SP_INFOTRON, 1 },
787 access_direction_list[] =
789 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
790 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
791 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
792 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
793 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
794 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
795 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
796 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
797 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
798 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
799 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
801 { EL_SP_PORT_LEFT, MV_RIGHT },
802 { EL_SP_PORT_RIGHT, MV_LEFT },
803 { EL_SP_PORT_UP, MV_DOWN },
804 { EL_SP_PORT_DOWN, MV_UP },
805 { EL_SP_PORT_HORIZONTAL, MV_LEFT | MV_RIGHT },
806 { EL_SP_PORT_VERTICAL, MV_UP | MV_DOWN },
807 { EL_SP_PORT_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
808 { EL_SP_GRAVITY_PORT_LEFT, MV_RIGHT },
809 { EL_SP_GRAVITY_PORT_RIGHT, MV_LEFT },
810 { EL_SP_GRAVITY_PORT_UP, MV_DOWN },
811 { EL_SP_GRAVITY_PORT_DOWN, MV_UP },
812 { EL_SP_GRAVITY_ON_PORT_LEFT, MV_RIGHT },
813 { EL_SP_GRAVITY_ON_PORT_RIGHT, MV_LEFT },
814 { EL_SP_GRAVITY_ON_PORT_UP, MV_DOWN },
815 { EL_SP_GRAVITY_ON_PORT_DOWN, MV_UP },
816 { EL_SP_GRAVITY_OFF_PORT_LEFT, MV_RIGHT },
817 { EL_SP_GRAVITY_OFF_PORT_RIGHT, MV_LEFT },
818 { EL_SP_GRAVITY_OFF_PORT_UP, MV_DOWN },
819 { EL_SP_GRAVITY_OFF_PORT_DOWN, MV_UP },
821 { EL_UNDEFINED, MV_NONE }
824 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
826 #define IS_AUTO_CHANGING(e) (element_info[e].has_change_event[CE_DELAY])
827 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
828 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
829 IS_JUST_CHANGING(x, y))
831 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
833 /* static variables for playfield scan mode (scanning forward or backward) */
834 static int playfield_scan_start_x = 0;
835 static int playfield_scan_start_y = 0;
836 static int playfield_scan_delta_x = 1;
837 static int playfield_scan_delta_y = 1;
839 #define SCAN_PLAYFIELD(x, y) for ((y) = playfield_scan_start_y; \
840 (y) >= 0 && (y) <= lev_fieldy - 1; \
841 (y) += playfield_scan_delta_y) \
842 for ((x) = playfield_scan_start_x; \
843 (x) >= 0 && (x) <= lev_fieldx - 1; \
844 (x) += playfield_scan_delta_x) \
847 void DEBUG_SetMaximumDynamite()
851 for (i = 0; i < MAX_INVENTORY_SIZE; i++)
852 if (local_player->inventory_size < MAX_INVENTORY_SIZE)
853 local_player->inventory_element[local_player->inventory_size++] =
858 static void InitPlayfieldScanModeVars()
860 if (game.use_reverse_scan_direction)
862 playfield_scan_start_x = lev_fieldx - 1;
863 playfield_scan_start_y = lev_fieldy - 1;
865 playfield_scan_delta_x = -1;
866 playfield_scan_delta_y = -1;
870 playfield_scan_start_x = 0;
871 playfield_scan_start_y = 0;
873 playfield_scan_delta_x = 1;
874 playfield_scan_delta_y = 1;
878 static void InitPlayfieldScanMode(int mode)
880 game.use_reverse_scan_direction =
881 (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
883 InitPlayfieldScanModeVars();
886 static int get_move_delay_from_stepsize(int move_stepsize)
889 MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
891 /* make sure that stepsize value is always a power of 2 */
892 move_stepsize = (1 << log_2(move_stepsize));
894 return TILEX / move_stepsize;
897 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
900 int player_nr = player->index_nr;
901 int move_delay = get_move_delay_from_stepsize(move_stepsize);
902 boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
904 /* do no immediately change move delay -- the player might just be moving */
905 player->move_delay_value_next = move_delay;
907 /* information if player can move must be set separately */
908 player->cannot_move = cannot_move;
912 player->move_delay = game.initial_move_delay[player_nr];
913 player->move_delay_value = game.initial_move_delay_value[player_nr];
915 player->move_delay_value_next = -1;
917 player->move_delay_reset_counter = 0;
921 void GetPlayerConfig()
923 GameFrameDelay = setup.game_frame_delay;
925 if (!audio.sound_available)
926 setup.sound_simple = FALSE;
928 if (!audio.loops_available)
929 setup.sound_loops = FALSE;
931 if (!audio.music_available)
932 setup.sound_music = FALSE;
934 if (!video.fullscreen_available)
935 setup.fullscreen = FALSE;
937 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
939 SetAudioMode(setup.sound);
943 static int getBeltNrFromBeltElement(int element)
945 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
946 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
947 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
950 static int getBeltNrFromBeltActiveElement(int element)
952 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
953 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
954 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
957 static int getBeltNrFromBeltSwitchElement(int element)
959 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
960 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
961 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
964 static int getBeltDirNrFromBeltSwitchElement(int element)
966 static int belt_base_element[4] =
968 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
969 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
970 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
971 EL_CONVEYOR_BELT_4_SWITCH_LEFT
974 int belt_nr = getBeltNrFromBeltSwitchElement(element);
975 int belt_dir_nr = element - belt_base_element[belt_nr];
977 return (belt_dir_nr % 3);
980 static int getBeltDirFromBeltSwitchElement(int element)
982 static int belt_move_dir[3] =
989 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
991 return belt_move_dir[belt_dir_nr];
994 static int get_element_from_group_element(int element)
996 if (IS_GROUP_ELEMENT(element))
998 struct ElementGroupInfo *group = element_info[element].group;
999 int last_anim_random_frame = gfx.anim_random_frame;
1002 if (group->choice_mode == ANIM_RANDOM)
1003 gfx.anim_random_frame = RND(group->num_elements_resolved);
1005 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1006 group->choice_mode, 0,
1009 if (group->choice_mode == ANIM_RANDOM)
1010 gfx.anim_random_frame = last_anim_random_frame;
1012 group->choice_pos++;
1014 element = group->element_resolved[element_pos];
1020 static void InitPlayerField(int x, int y, int element, boolean init_game)
1022 if (element == EL_SP_MURPHY)
1026 if (stored_player[0].present)
1028 Feld[x][y] = EL_SP_MURPHY_CLONE;
1034 stored_player[0].use_murphy = TRUE;
1036 if (!level.use_artwork_element[0])
1037 stored_player[0].artwork_element = EL_SP_MURPHY;
1040 Feld[x][y] = EL_PLAYER_1;
1046 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1047 int jx = player->jx, jy = player->jy;
1049 player->present = TRUE;
1051 player->block_last_field = (element == EL_SP_MURPHY ?
1052 level.sp_block_last_field :
1053 level.block_last_field);
1055 /* ---------- initialize player's last field block delay --------------- */
1057 /* always start with reliable default value (no adjustment needed) */
1058 player->block_delay_adjustment = 0;
1060 /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1061 if (player->block_last_field && element == EL_SP_MURPHY)
1062 player->block_delay_adjustment = 1;
1064 /* special case 2: in game engines before 3.1.1, blocking was different */
1065 if (game.use_block_last_field_bug)
1066 player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1068 if (!options.network || player->connected)
1070 player->active = TRUE;
1072 /* remove potentially duplicate players */
1073 if (StorePlayer[jx][jy] == Feld[x][y])
1074 StorePlayer[jx][jy] = 0;
1076 StorePlayer[x][y] = Feld[x][y];
1080 printf("Player %d activated.\n", player->element_nr);
1081 printf("[Local player is %d and currently %s.]\n",
1082 local_player->element_nr,
1083 local_player->active ? "active" : "not active");
1087 Feld[x][y] = EL_EMPTY;
1089 player->jx = player->last_jx = x;
1090 player->jy = player->last_jy = y;
1094 static void InitField(int x, int y, boolean init_game)
1096 int element = Feld[x][y];
1105 InitPlayerField(x, y, element, init_game);
1108 case EL_SOKOBAN_FIELD_PLAYER:
1109 element = Feld[x][y] = EL_PLAYER_1;
1110 InitField(x, y, init_game);
1112 element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1113 InitField(x, y, init_game);
1116 case EL_SOKOBAN_FIELD_EMPTY:
1117 local_player->sokobanfields_still_needed++;
1121 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1122 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1123 else if (x > 0 && Feld[x-1][y] == EL_ACID)
1124 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1125 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1126 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1127 else if (y > 0 && Feld[x][y-1] == EL_ACID)
1128 Feld[x][y] = EL_ACID_POOL_BOTTOM;
1129 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1130 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1139 case EL_SPACESHIP_RIGHT:
1140 case EL_SPACESHIP_UP:
1141 case EL_SPACESHIP_LEFT:
1142 case EL_SPACESHIP_DOWN:
1143 case EL_BD_BUTTERFLY:
1144 case EL_BD_BUTTERFLY_RIGHT:
1145 case EL_BD_BUTTERFLY_UP:
1146 case EL_BD_BUTTERFLY_LEFT:
1147 case EL_BD_BUTTERFLY_DOWN:
1149 case EL_BD_FIREFLY_RIGHT:
1150 case EL_BD_FIREFLY_UP:
1151 case EL_BD_FIREFLY_LEFT:
1152 case EL_BD_FIREFLY_DOWN:
1153 case EL_PACMAN_RIGHT:
1155 case EL_PACMAN_LEFT:
1156 case EL_PACMAN_DOWN:
1158 case EL_YAMYAM_LEFT:
1159 case EL_YAMYAM_RIGHT:
1161 case EL_YAMYAM_DOWN:
1162 case EL_DARK_YAMYAM:
1165 case EL_SP_SNIKSNAK:
1166 case EL_SP_ELECTRON:
1175 case EL_AMOEBA_FULL:
1180 case EL_AMOEBA_DROP:
1181 if (y == lev_fieldy - 1)
1183 Feld[x][y] = EL_AMOEBA_GROWING;
1184 Store[x][y] = EL_AMOEBA_WET;
1188 case EL_DYNAMITE_ACTIVE:
1189 case EL_SP_DISK_RED_ACTIVE:
1190 case EL_DYNABOMB_PLAYER_1_ACTIVE:
1191 case EL_DYNABOMB_PLAYER_2_ACTIVE:
1192 case EL_DYNABOMB_PLAYER_3_ACTIVE:
1193 case EL_DYNABOMB_PLAYER_4_ACTIVE:
1194 MovDelay[x][y] = 96;
1197 case EL_EM_DYNAMITE_ACTIVE:
1198 MovDelay[x][y] = 32;
1202 local_player->lights_still_needed++;
1206 local_player->friends_still_needed++;
1211 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1214 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1215 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1216 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1217 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1218 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1219 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1220 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1221 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1222 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1223 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1224 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1225 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1228 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1229 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1230 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1232 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
1234 game.belt_dir[belt_nr] = belt_dir;
1235 game.belt_dir_nr[belt_nr] = belt_dir_nr;
1237 else /* more than one switch -- set it like the first switch */
1239 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1244 #if !USE_BOTH_SWITCHGATE_SWITCHES
1245 case EL_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
1247 Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
1250 case EL_DC_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
1252 Feld[x][y] = EL_DC_SWITCHGATE_SWITCH_UP;
1256 case EL_LIGHT_SWITCH_ACTIVE:
1258 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1261 case EL_INVISIBLE_STEELWALL:
1262 case EL_INVISIBLE_WALL:
1263 case EL_INVISIBLE_SAND:
1264 if (game.light_time_left > 0 ||
1265 game.lenses_time_left > 0)
1266 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1269 case EL_EMC_MAGIC_BALL:
1270 if (game.ball_state)
1271 Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1274 case EL_EMC_MAGIC_BALL_SWITCH:
1275 if (game.ball_state)
1276 Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1280 if (IS_CUSTOM_ELEMENT(element))
1282 if (CAN_MOVE(element))
1285 #if USE_NEW_CUSTOM_VALUE
1286 if (!element_info[element].use_last_ce_value || init_game)
1287 CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1290 else if (IS_GROUP_ELEMENT(element))
1292 Feld[x][y] = get_element_from_group_element(element);
1294 InitField(x, y, init_game);
1301 CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1304 static inline void InitField_WithBug1(int x, int y, boolean init_game)
1306 InitField(x, y, init_game);
1308 /* not needed to call InitMovDir() -- already done by InitField()! */
1309 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1310 CAN_MOVE(Feld[x][y]))
1314 static inline void InitField_WithBug2(int x, int y, boolean init_game)
1316 int old_element = Feld[x][y];
1318 InitField(x, y, init_game);
1320 /* not needed to call InitMovDir() -- already done by InitField()! */
1321 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1322 CAN_MOVE(old_element) &&
1323 (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1326 /* this case is in fact a combination of not less than three bugs:
1327 first, it calls InitMovDir() for elements that can move, although this is
1328 already done by InitField(); then, it checks the element that was at this
1329 field _before_ the call to InitField() (which can change it); lastly, it
1330 was not called for "mole with direction" elements, which were treated as
1331 "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1337 void DrawGameValue_Emeralds(int value)
1339 struct TextPosInfo *pos = &game.panel.gems;
1340 int font_nr = FONT_TEXT_2;
1341 int font_width = getFontWidth(font_nr);
1342 int digits = pos->chars;
1344 if (PANEL_DEACTIVATED(pos))
1347 pos->width = digits * font_width;
1349 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, digits), font_nr);
1352 void DrawGameValue_Dynamite(int value)
1354 struct TextPosInfo *pos = &game.panel.inventory;
1355 int font_nr = FONT_TEXT_2;
1356 int font_width = getFontWidth(font_nr);
1357 int digits = pos->chars;
1359 if (PANEL_DEACTIVATED(pos))
1362 pos->width = digits * font_width;
1364 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, digits), font_nr);
1367 void DrawGameValue_Score(int value)
1369 struct TextPosInfo *pos = &game.panel.score;
1370 int font_nr = FONT_TEXT_2;
1371 int font_width = getFontWidth(font_nr);
1372 int digits = pos->chars;
1374 if (PANEL_DEACTIVATED(pos))
1377 pos->width = digits * font_width;
1379 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, digits), font_nr);
1382 void DrawGameValue_Time(int value)
1384 struct TextPosInfo *pos = &game.panel.time;
1385 static int last_value = -1;
1388 int digits = pos->chars;
1389 int font1_nr = FONT_TEXT_2;
1390 int font2_nr = FONT_TEXT_1;
1391 int font_nr = font1_nr;
1392 boolean use_dynamic_digits = (digits == -1 ? TRUE : FALSE);
1394 if (PANEL_DEACTIVATED(pos))
1397 if (use_dynamic_digits) /* use dynamic number of digits */
1399 digits = (value < 1000 ? digits1 : digits2);
1400 font_nr = (value < 1000 ? font1_nr : font2_nr);
1403 /* clear background if value just changed its size (dynamic digits only) */
1404 if (use_dynamic_digits && (last_value < 1000) != (value < 1000))
1406 int width1 = digits1 * getFontWidth(font1_nr);
1407 int width2 = digits2 * getFontWidth(font2_nr);
1408 int max_width = MAX(width1, width2);
1409 int max_height = MAX(getFontHeight(font1_nr), getFontHeight(font2_nr));
1411 pos->width = max_width;
1413 ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
1414 max_width, max_height);
1417 pos->width = digits * getFontWidth(font_nr);
1419 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, digits), font_nr);
1424 void DrawGameValue_Level(int value)
1426 struct TextPosInfo *pos = &game.panel.level;
1429 int digits = pos->chars;
1430 int font1_nr = FONT_TEXT_2;
1431 int font2_nr = FONT_TEXT_1;
1432 int font_nr = font1_nr;
1433 boolean use_dynamic_digits = (digits == -1 ? TRUE : FALSE);
1435 if (PANEL_DEACTIVATED(pos))
1438 if (use_dynamic_digits) /* use dynamic number of digits */
1440 digits = (level_nr < 100 ? digits1 : digits2);
1441 font_nr = (level_nr < 100 ? font1_nr : font2_nr);
1444 pos->width = digits * getFontWidth(font_nr);
1446 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, digits), font_nr);
1449 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
1451 struct TextPosInfo *pos = &game.panel.keys;
1452 int base_key_graphic = EL_KEY_1;
1455 if (PANEL_DEACTIVATED(pos))
1458 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
1459 base_key_graphic = EL_EM_KEY_1;
1461 pos->width = 4 * MINI_TILEX;
1463 /* currently only 4 of 8 possible keys are displayed */
1464 for (i = 0; i < STD_NUM_KEYS; i++)
1466 int src_x = DOOR_GFX_PAGEX5 + 18;
1467 int src_y = DOOR_GFX_PAGEY1 + 123;
1468 int dst_x = PANEL_XPOS(pos) + i * MINI_TILEX;
1469 int dst_y = PANEL_YPOS(pos);
1472 DrawMiniGraphicExt(drawto, dst_x, dst_y, el2edimg(base_key_graphic + i));
1474 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
1475 MINI_TILEX, MINI_TILEY, dst_x, dst_y);
1481 void DrawGameValue_Emeralds(int value)
1483 int font_nr = FONT_TEXT_2;
1484 int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
1486 if (PANEL_DEACTIVATED(game.panel.gems))
1489 DrawText(DX_EMERALDS + xpos, DY_EMERALDS, int2str(value, 3), font_nr);
1492 void DrawGameValue_Dynamite(int value)
1494 int font_nr = FONT_TEXT_2;
1495 int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
1497 if (PANEL_DEACTIVATED(game.panel.inventory))
1500 DrawText(DX_DYNAMITE + xpos, DY_DYNAMITE, int2str(value, 3), font_nr);
1503 void DrawGameValue_Score(int value)
1505 int font_nr = FONT_TEXT_2;
1506 int xpos = (5 * 14 - 5 * getFontWidth(font_nr)) / 2;
1508 if (PANEL_DEACTIVATED(game.panel.score))
1511 DrawText(DX_SCORE + xpos, DY_SCORE, int2str(value, 5), font_nr);
1514 void DrawGameValue_Time(int value)
1516 int font1_nr = FONT_TEXT_2;
1518 int font2_nr = FONT_TEXT_1;
1520 int font2_nr = FONT_LEVEL_NUMBER;
1522 int xpos3 = (3 * 14 - 3 * getFontWidth(font1_nr)) / 2;
1523 int xpos4 = (4 * 10 - 4 * getFontWidth(font2_nr)) / 2;
1525 if (PANEL_DEACTIVATED(game.panel.time))
1528 /* clear background if value just changed its size */
1529 if (value == 999 || value == 1000)
1530 ClearRectangleOnBackground(drawto, DX_TIME1, DY_TIME, 14 * 3, 14);
1533 DrawText(DX_TIME1 + xpos3, DY_TIME, int2str(value, 3), font1_nr);
1535 DrawText(DX_TIME2 + xpos4, DY_TIME, int2str(value, 4), font2_nr);
1538 void DrawGameValue_Level(int value)
1540 int font1_nr = FONT_TEXT_2;
1542 int font2_nr = FONT_TEXT_1;
1544 int font2_nr = FONT_LEVEL_NUMBER;
1547 if (PANEL_DEACTIVATED(game.panel.level))
1551 DrawText(DX_LEVEL1, DY_LEVEL, int2str(value, 2), font1_nr);
1553 DrawText(DX_LEVEL2, DY_LEVEL, int2str(value, 3), font2_nr);
1556 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
1558 int base_key_graphic = EL_KEY_1;
1561 if (PANEL_DEACTIVATED(game.panel.keys))
1564 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
1565 base_key_graphic = EL_EM_KEY_1;
1567 /* currently only 4 of 8 possible keys are displayed */
1568 for (i = 0; i < STD_NUM_KEYS; i++)
1570 int x = XX_KEYS + i * MINI_TILEX;
1574 DrawMiniGraphicExt(drawto, DX + x,DY + y, el2edimg(base_key_graphic + i));
1576 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
1577 DOOR_GFX_PAGEX5 + x, y, MINI_TILEX, MINI_TILEY, DX + x,DY + y);
1583 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
1586 int key[MAX_NUM_KEYS];
1589 /* prevent EM engine from updating time/score values parallel to GameWon() */
1590 if (level.game_engine_type == GAME_ENGINE_TYPE_EM &&
1591 local_player->LevelSolved)
1594 for (i = 0; i < MAX_NUM_KEYS; i++)
1595 key[i] = key_bits & (1 << i);
1597 DrawGameValue_Level(level_nr);
1599 DrawGameValue_Emeralds(emeralds);
1600 DrawGameValue_Dynamite(dynamite);
1601 DrawGameValue_Score(score);
1602 DrawGameValue_Time(time);
1604 DrawGameValue_Keys(key);
1607 void DrawGameDoorValues()
1609 int time_value = (level.time == 0 ? TimePlayed : TimeLeft);
1610 int dynamite_value = 0;
1611 int score_value = (local_player->LevelSolved ? local_player->score_final :
1612 local_player->score);
1613 int gems_value = local_player->gems_still_needed;
1617 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
1619 DrawGameDoorValues_EM();
1624 if (game.centered_player_nr == -1)
1626 for (i = 0; i < MAX_PLAYERS; i++)
1628 for (j = 0; j < MAX_NUM_KEYS; j++)
1629 if (stored_player[i].key[j])
1630 key_bits |= (1 << j);
1632 dynamite_value += stored_player[i].inventory_size;
1637 int player_nr = game.centered_player_nr;
1639 for (i = 0; i < MAX_NUM_KEYS; i++)
1640 if (stored_player[player_nr].key[i])
1641 key_bits |= (1 << i);
1643 dynamite_value = stored_player[player_nr].inventory_size;
1646 DrawAllGameValues(gems_value, dynamite_value, score_value, time_value,
1652 =============================================================================
1654 -----------------------------------------------------------------------------
1655 initialize game engine due to level / tape version number
1656 =============================================================================
1659 static void InitGameEngine()
1661 int i, j, k, l, x, y;
1663 /* set game engine from tape file when re-playing, else from level file */
1664 game.engine_version = (tape.playing ? tape.engine_version :
1665 level.game_version);
1667 /* ---------------------------------------------------------------------- */
1668 /* set flags for bugs and changes according to active game engine version */
1669 /* ---------------------------------------------------------------------- */
1672 Summary of bugfix/change:
1673 Fixed handling for custom elements that change when pushed by the player.
1675 Fixed/changed in version:
1679 Before 3.1.0, custom elements that "change when pushing" changed directly
1680 after the player started pushing them (until then handled in "DigField()").
1681 Since 3.1.0, these custom elements are not changed until the "pushing"
1682 move of the element is finished (now handled in "ContinueMoving()").
1684 Affected levels/tapes:
1685 The first condition is generally needed for all levels/tapes before version
1686 3.1.0, which might use the old behaviour before it was changed; known tapes
1687 that are affected are some tapes from the level set "Walpurgis Gardens" by
1689 The second condition is an exception from the above case and is needed for
1690 the special case of tapes recorded with game (not engine!) version 3.1.0 or
1691 above (including some development versions of 3.1.0), but before it was
1692 known that this change would break tapes like the above and was fixed in
1693 3.1.1, so that the changed behaviour was active although the engine version
1694 while recording maybe was before 3.1.0. There is at least one tape that is
1695 affected by this exception, which is the tape for the one-level set "Bug
1696 Machine" by Juergen Bonhagen.
1699 game.use_change_when_pushing_bug =
1700 (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1702 tape.game_version >= VERSION_IDENT(3,1,0,0) &&
1703 tape.game_version < VERSION_IDENT(3,1,1,0)));
1706 Summary of bugfix/change:
1707 Fixed handling for blocking the field the player leaves when moving.
1709 Fixed/changed in version:
1713 Before 3.1.1, when "block last field when moving" was enabled, the field
1714 the player is leaving when moving was blocked for the time of the move,
1715 and was directly unblocked afterwards. This resulted in the last field
1716 being blocked for exactly one less than the number of frames of one player
1717 move. Additionally, even when blocking was disabled, the last field was
1718 blocked for exactly one frame.
1719 Since 3.1.1, due to changes in player movement handling, the last field
1720 is not blocked at all when blocking is disabled. When blocking is enabled,
1721 the last field is blocked for exactly the number of frames of one player
1722 move. Additionally, if the player is Murphy, the hero of Supaplex, the
1723 last field is blocked for exactly one more than the number of frames of
1726 Affected levels/tapes:
1727 (!!! yet to be determined -- probably many !!!)
1730 game.use_block_last_field_bug =
1731 (game.engine_version < VERSION_IDENT(3,1,1,0));
1734 Summary of bugfix/change:
1735 Changed behaviour of CE changes with multiple changes per single frame.
1737 Fixed/changed in version:
1741 Before 3.2.0-6, only one single CE change was allowed in each engine frame.
1742 This resulted in race conditions where CEs seem to behave strange in some
1743 situations (where triggered CE changes were just skipped because there was
1744 already a CE change on that tile in the playfield in that engine frame).
1745 Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
1746 (The number of changes per frame must be limited in any case, because else
1747 it is easily possible to define CE changes that would result in an infinite
1748 loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
1749 should be set large enough so that it would only be reached in cases where
1750 the corresponding CE change conditions run into a loop. Therefore, it seems
1751 to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
1752 maximal number of change pages for custom elements.)
1754 Affected levels/tapes:
1758 #if USE_ONLY_ONE_CHANGE_PER_FRAME
1759 game.max_num_changes_per_frame = 1;
1761 game.max_num_changes_per_frame =
1762 (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
1765 /* ---------------------------------------------------------------------- */
1767 /* default scan direction: scan playfield from top/left to bottom/right */
1768 InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
1770 /* dynamically adjust element properties according to game engine version */
1771 InitElementPropertiesEngine(game.engine_version);
1774 printf("level %d: level version == %06d\n", level_nr, level.game_version);
1775 printf(" tape version == %06d [%s] [file: %06d]\n",
1776 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
1778 printf(" => game.engine_version == %06d\n", game.engine_version);
1781 /* ---------- initialize player's initial move delay --------------------- */
1783 /* dynamically adjust player properties according to level information */
1784 for (i = 0; i < MAX_PLAYERS; i++)
1785 game.initial_move_delay_value[i] =
1786 get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
1788 /* dynamically adjust player properties according to game engine version */
1789 for (i = 0; i < MAX_PLAYERS; i++)
1790 game.initial_move_delay[i] =
1791 (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
1792 game.initial_move_delay_value[i] : 0);
1794 /* ---------- initialize player's initial push delay --------------------- */
1796 /* dynamically adjust player properties according to game engine version */
1797 game.initial_push_delay_value =
1798 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
1800 /* ---------- initialize changing elements ------------------------------- */
1802 /* initialize changing elements information */
1803 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1805 struct ElementInfo *ei = &element_info[i];
1807 /* this pointer might have been changed in the level editor */
1808 ei->change = &ei->change_page[0];
1810 if (!IS_CUSTOM_ELEMENT(i))
1812 ei->change->target_element = EL_EMPTY_SPACE;
1813 ei->change->delay_fixed = 0;
1814 ei->change->delay_random = 0;
1815 ei->change->delay_frames = 1;
1818 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1820 ei->has_change_event[j] = FALSE;
1822 ei->event_page_nr[j] = 0;
1823 ei->event_page[j] = &ei->change_page[0];
1827 /* add changing elements from pre-defined list */
1828 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
1830 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
1831 struct ElementInfo *ei = &element_info[ch_delay->element];
1833 ei->change->target_element = ch_delay->target_element;
1834 ei->change->delay_fixed = ch_delay->change_delay;
1836 ei->change->pre_change_function = ch_delay->pre_change_function;
1837 ei->change->change_function = ch_delay->change_function;
1838 ei->change->post_change_function = ch_delay->post_change_function;
1840 ei->change->can_change = TRUE;
1841 ei->change->can_change_or_has_action = TRUE;
1843 ei->has_change_event[CE_DELAY] = TRUE;
1845 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
1846 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
1849 /* ---------- initialize internal run-time variables ------------- */
1851 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1853 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1855 for (j = 0; j < ei->num_change_pages; j++)
1857 ei->change_page[j].can_change_or_has_action =
1858 (ei->change_page[j].can_change |
1859 ei->change_page[j].has_action);
1863 /* add change events from custom element configuration */
1864 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1866 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1868 for (j = 0; j < ei->num_change_pages; j++)
1870 if (!ei->change_page[j].can_change_or_has_action)
1873 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1875 /* only add event page for the first page found with this event */
1876 if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
1878 ei->has_change_event[k] = TRUE;
1880 ei->event_page_nr[k] = j;
1881 ei->event_page[k] = &ei->change_page[j];
1887 /* ---------- initialize run-time trigger player and element ------------- */
1889 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1891 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1893 for (j = 0; j < ei->num_change_pages; j++)
1895 ei->change_page[j].actual_trigger_element = EL_EMPTY;
1896 ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
1897 ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
1898 ei->change_page[j].actual_trigger_ce_value = 0;
1899 ei->change_page[j].actual_trigger_ce_score = 0;
1903 /* ---------- initialize trigger events ---------------------------------- */
1905 /* initialize trigger events information */
1906 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1907 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1908 trigger_events[i][j] = FALSE;
1910 /* add trigger events from element change event properties */
1911 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1913 struct ElementInfo *ei = &element_info[i];
1915 for (j = 0; j < ei->num_change_pages; j++)
1917 if (!ei->change_page[j].can_change_or_has_action)
1920 if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
1922 int trigger_element = ei->change_page[j].trigger_element;
1924 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1926 if (ei->change_page[j].has_event[k])
1928 if (IS_GROUP_ELEMENT(trigger_element))
1930 struct ElementGroupInfo *group =
1931 element_info[trigger_element].group;
1933 for (l = 0; l < group->num_elements_resolved; l++)
1934 trigger_events[group->element_resolved[l]][k] = TRUE;
1936 else if (trigger_element == EL_ANY_ELEMENT)
1937 for (l = 0; l < MAX_NUM_ELEMENTS; l++)
1938 trigger_events[l][k] = TRUE;
1940 trigger_events[trigger_element][k] = TRUE;
1947 /* ---------- initialize push delay -------------------------------------- */
1949 /* initialize push delay values to default */
1950 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1952 if (!IS_CUSTOM_ELEMENT(i))
1954 /* set default push delay values (corrected since version 3.0.7-1) */
1955 if (game.engine_version < VERSION_IDENT(3,0,7,1))
1957 element_info[i].push_delay_fixed = 2;
1958 element_info[i].push_delay_random = 8;
1962 element_info[i].push_delay_fixed = 8;
1963 element_info[i].push_delay_random = 8;
1968 /* set push delay value for certain elements from pre-defined list */
1969 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
1971 int e = push_delay_list[i].element;
1973 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
1974 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
1977 /* set push delay value for Supaplex elements for newer engine versions */
1978 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
1980 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1982 if (IS_SP_ELEMENT(i))
1984 /* set SP push delay to just enough to push under a falling zonk */
1985 int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
1987 element_info[i].push_delay_fixed = delay;
1988 element_info[i].push_delay_random = 0;
1993 /* ---------- initialize move stepsize ----------------------------------- */
1995 /* initialize move stepsize values to default */
1996 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1997 if (!IS_CUSTOM_ELEMENT(i))
1998 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
2000 /* set move stepsize value for certain elements from pre-defined list */
2001 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
2003 int e = move_stepsize_list[i].element;
2005 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
2008 /* ---------- initialize collect score ----------------------------------- */
2010 /* initialize collect score values for custom elements from initial value */
2011 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2012 if (IS_CUSTOM_ELEMENT(i))
2013 element_info[i].collect_score = element_info[i].collect_score_initial;
2015 /* ---------- initialize collect count ----------------------------------- */
2017 /* initialize collect count values for non-custom elements */
2018 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2019 if (!IS_CUSTOM_ELEMENT(i))
2020 element_info[i].collect_count_initial = 0;
2022 /* add collect count values for all elements from pre-defined list */
2023 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
2024 element_info[collect_count_list[i].element].collect_count_initial =
2025 collect_count_list[i].count;
2027 /* ---------- initialize access direction -------------------------------- */
2029 /* initialize access direction values to default (access from every side) */
2030 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2031 if (!IS_CUSTOM_ELEMENT(i))
2032 element_info[i].access_direction = MV_ALL_DIRECTIONS;
2034 /* set access direction value for certain elements from pre-defined list */
2035 for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
2036 element_info[access_direction_list[i].element].access_direction =
2037 access_direction_list[i].direction;
2039 /* ---------- initialize explosion content ------------------------------- */
2040 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2042 if (IS_CUSTOM_ELEMENT(i))
2045 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
2047 /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
2049 element_info[i].content.e[x][y] =
2050 (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
2051 i == EL_PLAYER_2 ? EL_EMERALD_RED :
2052 i == EL_PLAYER_3 ? EL_EMERALD :
2053 i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
2054 i == EL_MOLE ? EL_EMERALD_RED :
2055 i == EL_PENGUIN ? EL_EMERALD_PURPLE :
2056 i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
2057 i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
2058 i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
2059 i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
2060 i == EL_WALL_EMERALD ? EL_EMERALD :
2061 i == EL_WALL_DIAMOND ? EL_DIAMOND :
2062 i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
2063 i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
2064 i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
2065 i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
2066 i == EL_WALL_PEARL ? EL_PEARL :
2067 i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
2072 /* ---------- initialize recursion detection ------------------------------ */
2073 recursion_loop_depth = 0;
2074 recursion_loop_detected = FALSE;
2075 recursion_loop_element = EL_UNDEFINED;
2078 int get_num_special_action(int element, int action_first, int action_last)
2080 int num_special_action = 0;
2083 for (i = action_first; i <= action_last; i++)
2085 boolean found = FALSE;
2087 for (j = 0; j < NUM_DIRECTIONS; j++)
2088 if (el_act_dir2img(element, i, j) !=
2089 el_act_dir2img(element, ACTION_DEFAULT, j))
2093 num_special_action++;
2098 return num_special_action;
2103 =============================================================================
2105 -----------------------------------------------------------------------------
2106 initialize and start new game
2107 =============================================================================
2112 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
2113 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
2114 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
2115 boolean do_fading = (game_status == GAME_MODE_MAIN);
2118 game_status = GAME_MODE_PLAYING;
2122 /* don't play tapes over network */
2123 network_playing = (options.network && !tape.playing);
2125 for (i = 0; i < MAX_PLAYERS; i++)
2127 struct PlayerInfo *player = &stored_player[i];
2129 player->index_nr = i;
2130 player->index_bit = (1 << i);
2131 player->element_nr = EL_PLAYER_1 + i;
2133 player->present = FALSE;
2134 player->active = FALSE;
2135 player->killed = FALSE;
2138 player->effective_action = 0;
2139 player->programmed_action = 0;
2142 player->score_final = 0;
2144 player->gems_still_needed = level.gems_needed;
2145 player->sokobanfields_still_needed = 0;
2146 player->lights_still_needed = 0;
2147 player->friends_still_needed = 0;
2149 for (j = 0; j < MAX_NUM_KEYS; j++)
2150 player->key[j] = FALSE;
2152 player->num_white_keys = 0;
2154 player->dynabomb_count = 0;
2155 player->dynabomb_size = 1;
2156 player->dynabombs_left = 0;
2157 player->dynabomb_xl = FALSE;
2159 player->MovDir = MV_NONE;
2162 player->GfxDir = MV_NONE;
2163 player->GfxAction = ACTION_DEFAULT;
2165 player->StepFrame = 0;
2167 player->use_murphy = FALSE;
2168 player->artwork_element =
2169 (level.use_artwork_element[i] ? level.artwork_element[i] :
2170 player->element_nr);
2172 player->block_last_field = FALSE; /* initialized in InitPlayerField() */
2173 player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
2175 player->gravity = level.initial_player_gravity[i];
2177 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
2179 player->actual_frame_counter = 0;
2181 player->step_counter = 0;
2183 player->last_move_dir = MV_NONE;
2185 player->is_active = FALSE;
2187 player->is_waiting = FALSE;
2188 player->is_moving = FALSE;
2189 player->is_auto_moving = FALSE;
2190 player->is_digging = FALSE;
2191 player->is_snapping = FALSE;
2192 player->is_collecting = FALSE;
2193 player->is_pushing = FALSE;
2194 player->is_switching = FALSE;
2195 player->is_dropping = FALSE;
2196 player->is_dropping_pressed = FALSE;
2198 player->is_bored = FALSE;
2199 player->is_sleeping = FALSE;
2201 player->frame_counter_bored = -1;
2202 player->frame_counter_sleeping = -1;
2204 player->anim_delay_counter = 0;
2205 player->post_delay_counter = 0;
2207 player->dir_waiting = MV_NONE;
2208 player->action_waiting = ACTION_DEFAULT;
2209 player->last_action_waiting = ACTION_DEFAULT;
2210 player->special_action_bored = ACTION_DEFAULT;
2211 player->special_action_sleeping = ACTION_DEFAULT;
2213 player->switch_x = -1;
2214 player->switch_y = -1;
2216 player->drop_x = -1;
2217 player->drop_y = -1;
2219 player->show_envelope = 0;
2221 SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
2223 player->push_delay = -1; /* initialized when pushing starts */
2224 player->push_delay_value = game.initial_push_delay_value;
2226 player->drop_delay = 0;
2227 player->drop_pressed_delay = 0;
2229 player->last_jx = -1;
2230 player->last_jy = -1;
2234 player->shield_normal_time_left = 0;
2235 player->shield_deadly_time_left = 0;
2237 player->inventory_infinite_element = EL_UNDEFINED;
2238 player->inventory_size = 0;
2240 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
2241 SnapField(player, 0, 0);
2243 player->LevelSolved = FALSE;
2244 player->GameOver = FALSE;
2246 player->LevelSolved_GameWon = FALSE;
2247 player->LevelSolved_GameEnd = FALSE;
2248 player->LevelSolved_PanelOff = FALSE;
2249 player->LevelSolved_SaveTape = FALSE;
2250 player->LevelSolved_SaveScore = FALSE;
2253 network_player_action_received = FALSE;
2255 #if defined(NETWORK_AVALIABLE)
2256 /* initial null action */
2257 if (network_playing)
2258 SendToServer_MovePlayer(MV_NONE);
2267 TimeLeft = level.time;
2270 ScreenMovDir = MV_NONE;
2274 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
2276 AllPlayersGone = FALSE;
2278 game.yamyam_content_nr = 0;
2279 game.magic_wall_active = FALSE;
2280 game.magic_wall_time_left = 0;
2281 game.light_time_left = 0;
2282 game.timegate_time_left = 0;
2283 game.switchgate_pos = 0;
2284 game.wind_direction = level.wind_direction_initial;
2286 #if !USE_PLAYER_GRAVITY
2287 game.gravity = FALSE;
2288 game.explosions_delayed = TRUE;
2291 game.lenses_time_left = 0;
2292 game.magnify_time_left = 0;
2294 game.ball_state = level.ball_state_initial;
2295 game.ball_content_nr = 0;
2297 game.envelope_active = FALSE;
2299 /* set focus to local player for network games, else to all players */
2300 game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
2301 game.centered_player_nr_next = game.centered_player_nr;
2302 game.set_centered_player = FALSE;
2304 if (network_playing && tape.recording)
2306 /* store client dependent player focus when recording network games */
2307 tape.centered_player_nr_next = game.centered_player_nr_next;
2308 tape.set_centered_player = TRUE;
2311 for (i = 0; i < NUM_BELTS; i++)
2313 game.belt_dir[i] = MV_NONE;
2314 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
2317 for (i = 0; i < MAX_NUM_AMOEBA; i++)
2318 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
2320 SCAN_PLAYFIELD(x, y)
2322 Feld[x][y] = level.field[x][y];
2323 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
2324 ChangeDelay[x][y] = 0;
2325 ChangePage[x][y] = -1;
2326 #if USE_NEW_CUSTOM_VALUE
2327 CustomValue[x][y] = 0; /* initialized in InitField() */
2329 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
2331 WasJustMoving[x][y] = 0;
2332 WasJustFalling[x][y] = 0;
2333 CheckCollision[x][y] = 0;
2334 CheckImpact[x][y] = 0;
2336 Pushed[x][y] = FALSE;
2338 ChangeCount[x][y] = 0;
2339 ChangeEvent[x][y] = -1;
2341 ExplodePhase[x][y] = 0;
2342 ExplodeDelay[x][y] = 0;
2343 ExplodeField[x][y] = EX_TYPE_NONE;
2345 RunnerVisit[x][y] = 0;
2346 PlayerVisit[x][y] = 0;
2349 GfxRandom[x][y] = INIT_GFX_RANDOM();
2350 GfxElement[x][y] = EL_UNDEFINED;
2351 GfxAction[x][y] = ACTION_DEFAULT;
2352 GfxDir[x][y] = MV_NONE;
2355 SCAN_PLAYFIELD(x, y)
2357 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
2359 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
2361 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
2364 InitField(x, y, TRUE);
2369 for (i = 0; i < MAX_PLAYERS; i++)
2371 struct PlayerInfo *player = &stored_player[i];
2373 /* set number of special actions for bored and sleeping animation */
2374 player->num_special_action_bored =
2375 get_num_special_action(player->artwork_element,
2376 ACTION_BORING_1, ACTION_BORING_LAST);
2377 player->num_special_action_sleeping =
2378 get_num_special_action(player->artwork_element,
2379 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
2382 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
2383 emulate_sb ? EMU_SOKOBAN :
2384 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
2386 #if USE_NEW_ALL_SLIPPERY
2387 /* initialize type of slippery elements */
2388 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2390 if (!IS_CUSTOM_ELEMENT(i))
2392 /* default: elements slip down either to the left or right randomly */
2393 element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
2395 /* SP style elements prefer to slip down on the left side */
2396 if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
2397 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
2399 /* BD style elements prefer to slip down on the left side */
2400 if (game.emulation == EMU_BOULDERDASH)
2401 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
2406 /* initialize explosion and ignition delay */
2407 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2409 if (!IS_CUSTOM_ELEMENT(i))
2412 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
2413 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
2414 game.emulation == EMU_SUPAPLEX ? 3 : 2);
2415 int last_phase = (num_phase + 1) * delay;
2416 int half_phase = (num_phase / 2) * delay;
2418 element_info[i].explosion_delay = last_phase - 1;
2419 element_info[i].ignition_delay = half_phase;
2421 if (i == EL_BLACK_ORB)
2422 element_info[i].ignition_delay = 1;
2426 if (element_info[i].explosion_delay < 1) /* !!! check again !!! */
2427 element_info[i].explosion_delay = 1;
2429 if (element_info[i].ignition_delay < 1) /* !!! check again !!! */
2430 element_info[i].ignition_delay = 1;
2434 /* correct non-moving belts to start moving left */
2435 for (i = 0; i < NUM_BELTS; i++)
2436 if (game.belt_dir[i] == MV_NONE)
2437 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
2439 /* check if any connected player was not found in playfield */
2440 for (i = 0; i < MAX_PLAYERS; i++)
2442 struct PlayerInfo *player = &stored_player[i];
2444 if (player->connected && !player->present)
2446 for (j = 0; j < MAX_PLAYERS; j++)
2448 struct PlayerInfo *some_player = &stored_player[j];
2449 int jx = some_player->jx, jy = some_player->jy;
2451 /* assign first free player found that is present in the playfield */
2452 if (some_player->present && !some_player->connected)
2454 player->present = TRUE;
2455 player->active = TRUE;
2457 some_player->present = FALSE;
2458 some_player->active = FALSE;
2460 player->artwork_element = some_player->artwork_element;
2462 player->block_last_field = some_player->block_last_field;
2463 player->block_delay_adjustment = some_player->block_delay_adjustment;
2465 StorePlayer[jx][jy] = player->element_nr;
2466 player->jx = player->last_jx = jx;
2467 player->jy = player->last_jy = jy;
2477 /* when playing a tape, eliminate all players who do not participate */
2479 for (i = 0; i < MAX_PLAYERS; i++)
2481 if (stored_player[i].active && !tape.player_participates[i])
2483 struct PlayerInfo *player = &stored_player[i];
2484 int jx = player->jx, jy = player->jy;
2486 player->active = FALSE;
2487 StorePlayer[jx][jy] = 0;
2488 Feld[jx][jy] = EL_EMPTY;
2492 else if (!options.network && !setup.team_mode) /* && !tape.playing */
2494 /* when in single player mode, eliminate all but the first active player */
2496 for (i = 0; i < MAX_PLAYERS; i++)
2498 if (stored_player[i].active)
2500 for (j = i + 1; j < MAX_PLAYERS; j++)
2502 if (stored_player[j].active)
2504 struct PlayerInfo *player = &stored_player[j];
2505 int jx = player->jx, jy = player->jy;
2507 player->active = FALSE;
2508 player->present = FALSE;
2510 StorePlayer[jx][jy] = 0;
2511 Feld[jx][jy] = EL_EMPTY;
2518 /* when recording the game, store which players take part in the game */
2521 for (i = 0; i < MAX_PLAYERS; i++)
2522 if (stored_player[i].active)
2523 tape.player_participates[i] = TRUE;
2528 for (i = 0; i < MAX_PLAYERS; i++)
2530 struct PlayerInfo *player = &stored_player[i];
2532 printf("Player %d: present == %d, connected == %d, active == %d.\n",
2537 if (local_player == player)
2538 printf("Player %d is local player.\n", i+1);
2542 if (BorderElement == EL_EMPTY)
2545 SBX_Right = lev_fieldx - SCR_FIELDX;
2547 SBY_Lower = lev_fieldy - SCR_FIELDY;
2552 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
2554 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
2557 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
2558 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
2560 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
2561 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
2563 /* if local player not found, look for custom element that might create
2564 the player (make some assumptions about the right custom element) */
2565 if (!local_player->present)
2567 int start_x = 0, start_y = 0;
2568 int found_rating = 0;
2569 int found_element = EL_UNDEFINED;
2570 int player_nr = local_player->index_nr;
2572 SCAN_PLAYFIELD(x, y)
2574 int element = Feld[x][y];
2579 if (level.use_start_element[player_nr] &&
2580 level.start_element[player_nr] == element &&
2587 found_element = element;
2590 if (!IS_CUSTOM_ELEMENT(element))
2593 if (CAN_CHANGE(element))
2595 for (i = 0; i < element_info[element].num_change_pages; i++)
2597 /* check for player created from custom element as single target */
2598 content = element_info[element].change_page[i].target_element;
2599 is_player = ELEM_IS_PLAYER(content);
2601 if (is_player && (found_rating < 3 || element < found_element))
2607 found_element = element;
2612 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
2614 /* check for player created from custom element as explosion content */
2615 content = element_info[element].content.e[xx][yy];
2616 is_player = ELEM_IS_PLAYER(content);
2618 if (is_player && (found_rating < 2 || element < found_element))
2620 start_x = x + xx - 1;
2621 start_y = y + yy - 1;
2624 found_element = element;
2627 if (!CAN_CHANGE(element))
2630 for (i = 0; i < element_info[element].num_change_pages; i++)
2632 /* check for player created from custom element as extended target */
2634 element_info[element].change_page[i].target_content.e[xx][yy];
2636 is_player = ELEM_IS_PLAYER(content);
2638 if (is_player && (found_rating < 1 || element < found_element))
2640 start_x = x + xx - 1;
2641 start_y = y + yy - 1;
2644 found_element = element;
2650 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
2651 start_x > SBX_Right + MIDPOSX ? SBX_Right :
2654 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
2655 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
2660 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2661 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2662 local_player->jx - MIDPOSX);
2664 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2665 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2666 local_player->jy - MIDPOSY);
2671 if (!game.restart_level)
2672 CloseDoor(DOOR_CLOSE_1);
2675 FadeOut(REDRAW_FIELD);
2677 /* !!! FIX THIS (START) !!! */
2678 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2680 InitGameEngine_EM();
2682 /* blit playfield from scroll buffer to normal back buffer for fading in */
2683 BlitScreenToBitmap_EM(backbuffer);
2690 /* after drawing the level, correct some elements */
2691 if (game.timegate_time_left == 0)
2692 CloseAllOpenTimegates();
2694 /* blit playfield from scroll buffer to normal back buffer for fading in */
2695 if (setup.soft_scrolling)
2696 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
2698 redraw_mask |= REDRAW_FROM_BACKBUFFER;
2700 /* !!! FIX THIS (END) !!! */
2703 FadeIn(REDRAW_FIELD);
2707 if (!game.restart_level)
2709 /* copy default game door content to main double buffer */
2710 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2711 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2714 SetPanelBackground();
2715 SetDrawBackgroundMask(REDRAW_DOOR_1);
2717 DrawGameDoorValues();
2719 if (!game.restart_level)
2723 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
2724 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
2725 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
2729 /* copy actual game door content to door double buffer for OpenDoor() */
2730 BlitBitmap(drawto, bitmap_db_door,
2731 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
2733 OpenDoor(DOOR_OPEN_ALL);
2735 PlaySound(SND_GAME_STARTING);
2737 if (setup.sound_music)
2740 KeyboardAutoRepeatOffUnlessAutoplay();
2744 for (i = 0; i < MAX_PLAYERS; i++)
2745 printf("Player %d %sactive.\n",
2746 i + 1, (stored_player[i].active ? "" : "not "));
2757 game.restart_level = FALSE;
2760 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
2762 /* this is used for non-R'n'D game engines to update certain engine values */
2764 /* needed to determine if sounds are played within the visible screen area */
2765 scroll_x = actual_scroll_x;
2766 scroll_y = actual_scroll_y;
2769 void InitMovDir(int x, int y)
2771 int i, element = Feld[x][y];
2772 static int xy[4][2] =
2779 static int direction[3][4] =
2781 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
2782 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
2783 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
2792 Feld[x][y] = EL_BUG;
2793 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
2796 case EL_SPACESHIP_RIGHT:
2797 case EL_SPACESHIP_UP:
2798 case EL_SPACESHIP_LEFT:
2799 case EL_SPACESHIP_DOWN:
2800 Feld[x][y] = EL_SPACESHIP;
2801 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
2804 case EL_BD_BUTTERFLY_RIGHT:
2805 case EL_BD_BUTTERFLY_UP:
2806 case EL_BD_BUTTERFLY_LEFT:
2807 case EL_BD_BUTTERFLY_DOWN:
2808 Feld[x][y] = EL_BD_BUTTERFLY;
2809 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
2812 case EL_BD_FIREFLY_RIGHT:
2813 case EL_BD_FIREFLY_UP:
2814 case EL_BD_FIREFLY_LEFT:
2815 case EL_BD_FIREFLY_DOWN:
2816 Feld[x][y] = EL_BD_FIREFLY;
2817 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
2820 case EL_PACMAN_RIGHT:
2822 case EL_PACMAN_LEFT:
2823 case EL_PACMAN_DOWN:
2824 Feld[x][y] = EL_PACMAN;
2825 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
2828 case EL_YAMYAM_LEFT:
2829 case EL_YAMYAM_RIGHT:
2831 case EL_YAMYAM_DOWN:
2832 Feld[x][y] = EL_YAMYAM;
2833 MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
2836 case EL_SP_SNIKSNAK:
2837 MovDir[x][y] = MV_UP;
2840 case EL_SP_ELECTRON:
2841 MovDir[x][y] = MV_LEFT;
2848 Feld[x][y] = EL_MOLE;
2849 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
2853 if (IS_CUSTOM_ELEMENT(element))
2855 struct ElementInfo *ei = &element_info[element];
2856 int move_direction_initial = ei->move_direction_initial;
2857 int move_pattern = ei->move_pattern;
2859 if (move_direction_initial == MV_START_PREVIOUS)
2861 if (MovDir[x][y] != MV_NONE)
2864 move_direction_initial = MV_START_AUTOMATIC;
2867 if (move_direction_initial == MV_START_RANDOM)
2868 MovDir[x][y] = 1 << RND(4);
2869 else if (move_direction_initial & MV_ANY_DIRECTION)
2870 MovDir[x][y] = move_direction_initial;
2871 else if (move_pattern == MV_ALL_DIRECTIONS ||
2872 move_pattern == MV_TURNING_LEFT ||
2873 move_pattern == MV_TURNING_RIGHT ||
2874 move_pattern == MV_TURNING_LEFT_RIGHT ||
2875 move_pattern == MV_TURNING_RIGHT_LEFT ||
2876 move_pattern == MV_TURNING_RANDOM)
2877 MovDir[x][y] = 1 << RND(4);
2878 else if (move_pattern == MV_HORIZONTAL)
2879 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
2880 else if (move_pattern == MV_VERTICAL)
2881 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
2882 else if (move_pattern & MV_ANY_DIRECTION)
2883 MovDir[x][y] = element_info[element].move_pattern;
2884 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
2885 move_pattern == MV_ALONG_RIGHT_SIDE)
2887 /* use random direction as default start direction */
2888 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2889 MovDir[x][y] = 1 << RND(4);
2891 for (i = 0; i < NUM_DIRECTIONS; i++)
2893 int x1 = x + xy[i][0];
2894 int y1 = y + xy[i][1];
2896 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2898 if (move_pattern == MV_ALONG_RIGHT_SIDE)
2899 MovDir[x][y] = direction[0][i];
2901 MovDir[x][y] = direction[1][i];
2910 MovDir[x][y] = 1 << RND(4);
2912 if (element != EL_BUG &&
2913 element != EL_SPACESHIP &&
2914 element != EL_BD_BUTTERFLY &&
2915 element != EL_BD_FIREFLY)
2918 for (i = 0; i < NUM_DIRECTIONS; i++)
2920 int x1 = x + xy[i][0];
2921 int y1 = y + xy[i][1];
2923 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2925 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
2927 MovDir[x][y] = direction[0][i];
2930 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
2931 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
2933 MovDir[x][y] = direction[1][i];
2942 GfxDir[x][y] = MovDir[x][y];
2945 void InitAmoebaNr(int x, int y)
2948 int group_nr = AmoebeNachbarNr(x, y);
2952 for (i = 1; i < MAX_NUM_AMOEBA; i++)
2954 if (AmoebaCnt[i] == 0)
2962 AmoebaNr[x][y] = group_nr;
2963 AmoebaCnt[group_nr]++;
2964 AmoebaCnt2[group_nr]++;
2967 static void PlayerWins(struct PlayerInfo *player)
2969 player->LevelSolved = TRUE;
2970 player->GameOver = TRUE;
2972 player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2973 level.native_em_level->lev->score : player->score);
2978 static int time, time_final;
2979 static int score, score_final;
2980 static int game_over_delay_1 = 0;
2981 static int game_over_delay_2 = 0;
2982 int game_over_delay_value_1 = 50;
2983 int game_over_delay_value_2 = 50;
2985 if (!local_player->LevelSolved_GameWon)
2989 /* do not start end game actions before the player stops moving (to exit) */
2990 if (local_player->MovPos)
2993 local_player->LevelSolved_GameWon = TRUE;
2994 local_player->LevelSolved_SaveTape = tape.recording;
2995 local_player->LevelSolved_SaveScore = !tape.playing;
2997 if (tape.auto_play) /* tape might already be stopped here */
2998 tape.auto_play_level_solved = TRUE;
3004 game_over_delay_1 = game_over_delay_value_1;
3005 game_over_delay_2 = game_over_delay_value_2;
3007 time = time_final = (level.time == 0 ? TimePlayed : TimeLeft);
3008 score = score_final = local_player->score_final;
3013 score_final += TimeLeft * level.score[SC_TIME_BONUS];
3015 else if (level.time == 0 && TimePlayed < 999)
3018 score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
3021 local_player->score_final = score_final;
3023 if (level_editor_test_game)
3026 score = score_final;
3028 DrawGameValue_Time(time);
3029 DrawGameValue_Score(score);
3032 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
3034 if (ExitX >= 0 && ExitY >= 0) /* local player has left the level */
3036 /* close exit door after last player */
3037 if ((AllPlayersGone &&
3038 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
3039 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
3040 Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
3041 Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
3042 Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
3044 int element = Feld[ExitX][ExitY];
3047 if (element == EL_EM_EXIT_OPEN ||
3048 element == EL_EM_STEEL_EXIT_OPEN)
3055 Feld[ExitX][ExitY] =
3056 (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
3057 element == EL_EM_EXIT_OPEN ? EL_EM_EXIT_CLOSING :
3058 element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING:
3059 element == EL_STEEL_EXIT_OPEN ? EL_STEEL_EXIT_CLOSING:
3060 EL_EM_STEEL_EXIT_CLOSING);
3062 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
3066 /* player disappears */
3067 DrawLevelField(ExitX, ExitY);
3070 for (i = 0; i < MAX_PLAYERS; i++)
3072 struct PlayerInfo *player = &stored_player[i];
3074 if (player->present)
3076 RemovePlayer(player);
3078 /* player disappears */
3079 DrawLevelField(player->jx, player->jy);
3084 PlaySound(SND_GAME_WINNING);
3087 if (game_over_delay_1 > 0)
3089 game_over_delay_1--;
3094 if (time != time_final)
3096 int time_to_go = ABS(time_final - time);
3097 int time_count_dir = (time < time_final ? +1 : -1);
3098 int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
3100 time += time_count_steps * time_count_dir;
3101 score += time_count_steps * level.score[SC_TIME_BONUS];
3103 DrawGameValue_Time(time);
3104 DrawGameValue_Score(score);
3106 if (time == time_final)
3107 StopSound(SND_GAME_LEVELTIME_BONUS);
3108 else if (setup.sound_loops)
3109 PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
3111 PlaySound(SND_GAME_LEVELTIME_BONUS);
3116 local_player->LevelSolved_PanelOff = TRUE;
3118 if (game_over_delay_2 > 0)
3120 game_over_delay_2--;
3133 boolean raise_level = FALSE;
3135 local_player->LevelSolved_GameEnd = TRUE;
3137 CloseDoor(DOOR_CLOSE_1);
3139 if (local_player->LevelSolved_SaveTape)
3146 SaveTapeChecked(tape.level_nr); /* ask to save tape */
3148 SaveTape(tape.level_nr); /* ask to save tape */
3152 if (level_editor_test_game)
3154 game_status = GAME_MODE_MAIN;
3161 if (!local_player->LevelSolved_SaveScore)
3163 FadeOut(REDRAW_FIELD);
3165 game_status = GAME_MODE_MAIN;
3167 DrawAndFadeInMainMenu(REDRAW_FIELD);
3172 if (level_nr == leveldir_current->handicap_level)
3174 leveldir_current->handicap_level++;
3175 SaveLevelSetup_SeriesInfo();
3178 if (level_nr < leveldir_current->last_level)
3179 raise_level = TRUE; /* advance to next level */
3181 if ((hi_pos = NewHiScore()) >= 0)
3183 game_status = GAME_MODE_SCORES;
3185 DrawHallOfFame(hi_pos);
3195 FadeOut(REDRAW_FIELD);
3197 game_status = GAME_MODE_MAIN;
3205 DrawAndFadeInMainMenu(REDRAW_FIELD);
3214 LoadScore(level_nr);
3216 if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
3217 local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score)
3220 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
3222 if (local_player->score_final > highscore[k].Score)
3224 /* player has made it to the hall of fame */
3226 if (k < MAX_SCORE_ENTRIES - 1)
3228 int m = MAX_SCORE_ENTRIES - 1;
3231 for (l = k; l < MAX_SCORE_ENTRIES; l++)
3232 if (strEqual(setup.player_name, highscore[l].Name))
3234 if (m == k) /* player's new highscore overwrites his old one */
3238 for (l = m; l > k; l--)
3240 strcpy(highscore[l].Name, highscore[l - 1].Name);
3241 highscore[l].Score = highscore[l - 1].Score;
3248 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
3249 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
3250 highscore[k].Score = local_player->score_final;
3256 else if (!strncmp(setup.player_name, highscore[k].Name,
3257 MAX_PLAYER_NAME_LEN))
3258 break; /* player already there with a higher score */
3264 SaveScore(level_nr);
3269 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
3271 int element = Feld[x][y];
3272 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3273 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
3274 int horiz_move = (dx != 0);
3275 int sign = (horiz_move ? dx : dy);
3276 int step = sign * element_info[element].move_stepsize;
3278 /* special values for move stepsize for spring and things on conveyor belt */
3281 if (CAN_FALL(element) &&
3282 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
3283 step = sign * MOVE_STEPSIZE_NORMAL / 2;
3284 else if (element == EL_SPRING)
3285 step = sign * MOVE_STEPSIZE_NORMAL * 2;
3291 inline static int getElementMoveStepsize(int x, int y)
3293 return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
3296 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
3298 if (player->GfxAction != action || player->GfxDir != dir)
3301 printf("Player frame reset! (%d => %d, %d => %d)\n",
3302 player->GfxAction, action, player->GfxDir, dir);
3305 player->GfxAction = action;
3306 player->GfxDir = dir;
3308 player->StepFrame = 0;
3312 #if USE_GFX_RESET_GFX_ANIMATION
3313 static void ResetGfxFrame(int x, int y, boolean redraw)
3315 int element = Feld[x][y];
3316 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3317 int last_gfx_frame = GfxFrame[x][y];
3319 if (graphic_info[graphic].anim_global_sync)
3320 GfxFrame[x][y] = FrameCounter;
3321 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
3322 GfxFrame[x][y] = CustomValue[x][y];
3323 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
3324 GfxFrame[x][y] = element_info[element].collect_score;
3325 else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
3326 GfxFrame[x][y] = ChangeDelay[x][y];
3328 if (redraw && GfxFrame[x][y] != last_gfx_frame)
3329 DrawLevelGraphicAnimation(x, y, graphic);
3333 static void ResetGfxAnimation(int x, int y)
3335 GfxAction[x][y] = ACTION_DEFAULT;
3336 GfxDir[x][y] = MovDir[x][y];
3339 #if USE_GFX_RESET_GFX_ANIMATION
3340 ResetGfxFrame(x, y, FALSE);
3344 static void ResetRandomAnimationValue(int x, int y)
3346 GfxRandom[x][y] = INIT_GFX_RANDOM();
3349 void InitMovingField(int x, int y, int direction)
3351 int element = Feld[x][y];
3352 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3353 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
3356 boolean is_moving_before, is_moving_after;
3358 boolean continues_moving = (WasJustMoving[x][y] && direction == MovDir[x][y]);
3361 /* check if element was/is moving or being moved before/after mode change */
3363 is_moving_before = WasJustMoving[x][y];
3365 is_moving_before = (getElementMoveStepsizeExt(x, y, MovDir[x][y]) != 0);
3367 is_moving_after = (getElementMoveStepsizeExt(x, y, direction) != 0);
3369 /* reset animation only for moving elements which change direction of moving
3370 or which just started or stopped moving
3371 (else CEs with property "can move" / "not moving" are reset each frame) */
3372 #if USE_GFX_RESET_ONLY_WHEN_MOVING
3374 if (is_moving_before != is_moving_after ||
3375 direction != MovDir[x][y])
3376 ResetGfxAnimation(x, y);
3378 if ((is_moving_before || is_moving_after) && !continues_moving)
3379 ResetGfxAnimation(x, y);
3382 if (!continues_moving)
3383 ResetGfxAnimation(x, y);
3386 MovDir[x][y] = direction;
3387 GfxDir[x][y] = direction;
3389 #if USE_GFX_RESET_ONLY_WHEN_MOVING
3390 GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
3391 direction == MV_DOWN && CAN_FALL(element) ?
3392 ACTION_FALLING : ACTION_MOVING);
3394 GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
3395 ACTION_FALLING : ACTION_MOVING);
3398 /* this is needed for CEs with property "can move" / "not moving" */
3400 if (is_moving_after)
3402 if (Feld[newx][newy] == EL_EMPTY)
3403 Feld[newx][newy] = EL_BLOCKED;
3405 MovDir[newx][newy] = MovDir[x][y];
3407 #if USE_NEW_CUSTOM_VALUE
3408 CustomValue[newx][newy] = CustomValue[x][y];
3411 GfxFrame[newx][newy] = GfxFrame[x][y];
3412 GfxRandom[newx][newy] = GfxRandom[x][y];
3413 GfxAction[newx][newy] = GfxAction[x][y];
3414 GfxDir[newx][newy] = GfxDir[x][y];
3418 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
3420 int direction = MovDir[x][y];
3421 int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
3422 int newy = y + (direction & MV_UP ? -1 : direction & MV_DOWN ? +1 : 0);
3428 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
3430 int oldx = x, oldy = y;
3431 int direction = MovDir[x][y];
3433 if (direction == MV_LEFT)
3435 else if (direction == MV_RIGHT)
3437 else if (direction == MV_UP)
3439 else if (direction == MV_DOWN)
3442 *comes_from_x = oldx;
3443 *comes_from_y = oldy;
3446 int MovingOrBlocked2Element(int x, int y)
3448 int element = Feld[x][y];
3450 if (element == EL_BLOCKED)
3454 Blocked2Moving(x, y, &oldx, &oldy);
3455 return Feld[oldx][oldy];
3461 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
3463 /* like MovingOrBlocked2Element(), but if element is moving
3464 and (x,y) is the field the moving element is just leaving,
3465 return EL_BLOCKED instead of the element value */
3466 int element = Feld[x][y];
3468 if (IS_MOVING(x, y))
3470 if (element == EL_BLOCKED)
3474 Blocked2Moving(x, y, &oldx, &oldy);
3475 return Feld[oldx][oldy];
3484 static void RemoveField(int x, int y)
3486 Feld[x][y] = EL_EMPTY;
3492 #if USE_NEW_CUSTOM_VALUE
3493 CustomValue[x][y] = 0;
3497 ChangeDelay[x][y] = 0;
3498 ChangePage[x][y] = -1;
3499 Pushed[x][y] = FALSE;
3502 ExplodeField[x][y] = EX_TYPE_NONE;
3505 GfxElement[x][y] = EL_UNDEFINED;
3506 GfxAction[x][y] = ACTION_DEFAULT;
3507 GfxDir[x][y] = MV_NONE;
3510 void RemoveMovingField(int x, int y)
3512 int oldx = x, oldy = y, newx = x, newy = y;
3513 int element = Feld[x][y];
3514 int next_element = EL_UNDEFINED;
3516 if (element != EL_BLOCKED && !IS_MOVING(x, y))
3519 if (IS_MOVING(x, y))
3521 Moving2Blocked(x, y, &newx, &newy);
3523 if (Feld[newx][newy] != EL_BLOCKED)
3525 /* element is moving, but target field is not free (blocked), but
3526 already occupied by something different (example: acid pool);
3527 in this case, only remove the moving field, but not the target */
3529 RemoveField(oldx, oldy);
3531 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
3533 DrawLevelField(oldx, oldy);
3538 else if (element == EL_BLOCKED)
3540 Blocked2Moving(x, y, &oldx, &oldy);
3541 if (!IS_MOVING(oldx, oldy))
3545 if (element == EL_BLOCKED &&
3546 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
3547 Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
3548 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
3549 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
3550 Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
3551 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
3552 next_element = get_next_element(Feld[oldx][oldy]);
3554 RemoveField(oldx, oldy);
3555 RemoveField(newx, newy);
3557 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
3559 if (next_element != EL_UNDEFINED)
3560 Feld[oldx][oldy] = next_element;
3562 DrawLevelField(oldx, oldy);
3563 DrawLevelField(newx, newy);
3566 void DrawDynamite(int x, int y)
3568 int sx = SCREENX(x), sy = SCREENY(y);
3569 int graphic = el2img(Feld[x][y]);
3572 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
3575 if (IS_WALKABLE_INSIDE(Back[x][y]))
3579 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
3580 else if (Store[x][y])
3581 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
3583 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
3585 if (Back[x][y] || Store[x][y])
3586 DrawGraphicThruMask(sx, sy, graphic, frame);
3588 DrawGraphic(sx, sy, graphic, frame);
3591 void CheckDynamite(int x, int y)
3593 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
3597 if (MovDelay[x][y] != 0)
3600 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
3606 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
3611 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
3613 boolean num_checked_players = 0;
3616 for (i = 0; i < MAX_PLAYERS; i++)
3618 if (stored_player[i].active)
3620 int sx = stored_player[i].jx;
3621 int sy = stored_player[i].jy;
3623 if (num_checked_players == 0)
3630 *sx1 = MIN(*sx1, sx);
3631 *sy1 = MIN(*sy1, sy);
3632 *sx2 = MAX(*sx2, sx);
3633 *sy2 = MAX(*sy2, sy);
3636 num_checked_players++;
3641 static boolean checkIfAllPlayersFitToScreen_RND()
3643 int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
3645 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
3647 return (sx2 - sx1 < SCR_FIELDX &&
3648 sy2 - sy1 < SCR_FIELDY);
3651 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
3653 int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
3655 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
3657 *sx = (sx1 + sx2) / 2;
3658 *sy = (sy1 + sy2) / 2;
3661 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
3662 boolean center_screen, boolean quick_relocation)
3664 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3665 boolean no_delay = (tape.warp_forward);
3666 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
3667 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
3669 if (quick_relocation)
3671 int offset = (setup.scroll_delay ? 3 : 0);
3673 if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
3677 scroll_x = (x < SBX_Left + MIDPOSX ? SBX_Left :
3678 x > SBX_Right + MIDPOSX ? SBX_Right :
3681 scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
3682 y > SBY_Lower + MIDPOSY ? SBY_Lower :
3687 /* quick relocation (without scrolling), but do not center screen */
3689 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
3690 old_x > SBX_Right + MIDPOSX ? SBX_Right :
3693 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
3694 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
3697 int offset_x = x + (scroll_x - center_scroll_x);
3698 int offset_y = y + (scroll_y - center_scroll_y);
3700 scroll_x = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
3701 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
3702 offset_x - MIDPOSX);
3704 scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
3705 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
3706 offset_y - MIDPOSY);
3711 if ((move_dir == MV_LEFT && scroll_x > x - MIDPOSX + offset) ||
3712 (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
3713 scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
3715 if ((move_dir == MV_UP && scroll_y > y - MIDPOSY + offset) ||
3716 (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
3717 scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
3719 /* don't scroll over playfield boundaries */
3720 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
3721 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
3723 /* don't scroll over playfield boundaries */
3724 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
3725 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
3728 RedrawPlayfield(TRUE, 0,0,0,0);
3732 int scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left :
3733 x > SBX_Right + MIDPOSX ? SBX_Right :
3736 int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
3737 y > SBY_Lower + MIDPOSY ? SBY_Lower :
3740 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
3742 while (scroll_x != scroll_xx || scroll_y != scroll_yy)
3745 int fx = FX, fy = FY;
3747 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
3748 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
3750 if (dx == 0 && dy == 0) /* no scrolling needed at all */
3756 fx += dx * TILEX / 2;
3757 fy += dy * TILEY / 2;
3759 ScrollLevel(dx, dy);
3762 /* scroll in two steps of half tile size to make things smoother */
3763 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
3765 Delay(wait_delay_value);
3767 /* scroll second step to align at full tile size */
3769 Delay(wait_delay_value);
3774 Delay(wait_delay_value);
3778 void RelocatePlayer(int jx, int jy, int el_player_raw)
3780 int el_player = GET_PLAYER_ELEMENT(el_player_raw);
3781 int player_nr = GET_PLAYER_NR(el_player);
3782 struct PlayerInfo *player = &stored_player[player_nr];
3783 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3784 boolean no_delay = (tape.warp_forward);
3785 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
3786 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
3787 int old_jx = player->jx;
3788 int old_jy = player->jy;
3789 int old_element = Feld[old_jx][old_jy];
3790 int element = Feld[jx][jy];
3791 boolean player_relocated = (old_jx != jx || old_jy != jy);
3793 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
3794 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
3795 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
3796 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
3797 int leave_side_horiz = move_dir_horiz;
3798 int leave_side_vert = move_dir_vert;
3799 int enter_side = enter_side_horiz | enter_side_vert;
3800 int leave_side = leave_side_horiz | leave_side_vert;
3802 if (player->GameOver) /* do not reanimate dead player */
3805 if (!player_relocated) /* no need to relocate the player */
3808 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
3810 RemoveField(jx, jy); /* temporarily remove newly placed player */
3811 DrawLevelField(jx, jy);
3814 if (player->present)
3816 while (player->MovPos)
3818 ScrollPlayer(player, SCROLL_GO_ON);
3819 ScrollScreen(NULL, SCROLL_GO_ON);
3821 AdvanceFrameAndPlayerCounters(player->index_nr);
3826 Delay(wait_delay_value);
3829 DrawPlayer(player); /* needed here only to cleanup last field */
3830 DrawLevelField(player->jx, player->jy); /* remove player graphic */
3832 player->is_moving = FALSE;
3835 if (IS_CUSTOM_ELEMENT(old_element))
3836 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
3838 player->index_bit, leave_side);
3840 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
3842 player->index_bit, leave_side);
3844 Feld[jx][jy] = el_player;
3845 InitPlayerField(jx, jy, el_player, TRUE);
3847 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
3849 Feld[jx][jy] = element;
3850 InitField(jx, jy, FALSE);
3853 /* only visually relocate centered player */
3854 DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
3855 FALSE, level.instant_relocation);
3857 TestIfPlayerTouchesBadThing(jx, jy);
3858 TestIfPlayerTouchesCustomElement(jx, jy);
3860 if (IS_CUSTOM_ELEMENT(element))
3861 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
3862 player->index_bit, enter_side);
3864 CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
3865 player->index_bit, enter_side);
3868 void Explode(int ex, int ey, int phase, int mode)
3874 /* !!! eliminate this variable !!! */
3875 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
3877 if (game.explosions_delayed)
3879 ExplodeField[ex][ey] = mode;
3883 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
3885 int center_element = Feld[ex][ey];
3886 int artwork_element, explosion_element; /* set these values later */
3889 /* --- This is only really needed (and now handled) in "Impact()". --- */
3890 /* do not explode moving elements that left the explode field in time */
3891 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
3892 center_element == EL_EMPTY &&
3893 (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
3898 /* !!! at this place, the center element may be EL_BLOCKED !!! */
3899 if (mode == EX_TYPE_NORMAL ||
3900 mode == EX_TYPE_CENTER ||
3901 mode == EX_TYPE_CROSS)
3902 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
3905 /* remove things displayed in background while burning dynamite */
3906 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
3909 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
3911 /* put moving element to center field (and let it explode there) */
3912 center_element = MovingOrBlocked2Element(ex, ey);
3913 RemoveMovingField(ex, ey);
3914 Feld[ex][ey] = center_element;
3917 /* now "center_element" is finally determined -- set related values now */
3918 artwork_element = center_element; /* for custom player artwork */
3919 explosion_element = center_element; /* for custom player artwork */
3921 if (IS_PLAYER(ex, ey))
3923 int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
3925 artwork_element = stored_player[player_nr].artwork_element;
3927 if (level.use_explosion_element[player_nr])
3929 explosion_element = level.explosion_element[player_nr];
3930 artwork_element = explosion_element;
3935 if (mode == EX_TYPE_NORMAL ||
3936 mode == EX_TYPE_CENTER ||
3937 mode == EX_TYPE_CROSS)
3938 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
3941 last_phase = element_info[explosion_element].explosion_delay + 1;
3943 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
3945 int xx = x - ex + 1;
3946 int yy = y - ey + 1;
3949 if (!IN_LEV_FIELD(x, y) ||
3950 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
3951 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
3954 element = Feld[x][y];
3956 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
3958 element = MovingOrBlocked2Element(x, y);
3960 if (!IS_EXPLOSION_PROOF(element))
3961 RemoveMovingField(x, y);
3964 /* indestructible elements can only explode in center (but not flames) */
3965 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
3966 mode == EX_TYPE_BORDER)) ||
3967 element == EL_FLAMES)
3970 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
3971 behaviour, for example when touching a yamyam that explodes to rocks
3972 with active deadly shield, a rock is created under the player !!! */
3973 /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
3975 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
3976 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
3977 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
3979 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
3982 if (IS_ACTIVE_BOMB(element))
3984 /* re-activate things under the bomb like gate or penguin */
3985 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
3992 /* save walkable background elements while explosion on same tile */
3993 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
3994 (x != ex || y != ey || mode == EX_TYPE_BORDER))
3995 Back[x][y] = element;
3997 /* ignite explodable elements reached by other explosion */
3998 if (element == EL_EXPLOSION)
3999 element = Store2[x][y];
4001 if (AmoebaNr[x][y] &&
4002 (element == EL_AMOEBA_FULL ||
4003 element == EL_BD_AMOEBA ||
4004 element == EL_AMOEBA_GROWING))
4006 AmoebaCnt[AmoebaNr[x][y]]--;
4007 AmoebaCnt2[AmoebaNr[x][y]]--;
4012 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
4014 int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
4016 Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
4018 if (PLAYERINFO(ex, ey)->use_murphy)
4019 Store[x][y] = EL_EMPTY;
4022 /* !!! check this case -- currently needed for rnd_rado_negundo_v,
4023 !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
4024 else if (ELEM_IS_PLAYER(center_element))
4025 Store[x][y] = EL_EMPTY;
4026 else if (center_element == EL_YAMYAM)
4027 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
4028 else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
4029 Store[x][y] = element_info[center_element].content.e[xx][yy];
4031 /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
4032 (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
4033 otherwise) -- FIX THIS !!! */
4034 else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
4035 Store[x][y] = element_info[element].content.e[1][1];
4037 else if (!CAN_EXPLODE(element))
4038 Store[x][y] = element_info[element].content.e[1][1];
4041 Store[x][y] = EL_EMPTY;
4043 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
4044 center_element == EL_AMOEBA_TO_DIAMOND)
4045 Store2[x][y] = element;
4047 Feld[x][y] = EL_EXPLOSION;
4048 GfxElement[x][y] = artwork_element;
4050 ExplodePhase[x][y] = 1;
4051 ExplodeDelay[x][y] = last_phase;
4056 if (center_element == EL_YAMYAM)
4057 game.yamyam_content_nr =
4058 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
4070 GfxFrame[x][y] = 0; /* restart explosion animation */
4072 last_phase = ExplodeDelay[x][y];
4074 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
4078 /* activate this even in non-DEBUG version until cause for crash in
4079 getGraphicAnimationFrame() (see below) is found and eliminated */
4085 /* this can happen if the player leaves an explosion just in time */
4086 if (GfxElement[x][y] == EL_UNDEFINED)
4087 GfxElement[x][y] = EL_EMPTY;
4089 if (GfxElement[x][y] == EL_UNDEFINED)
4092 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
4093 printf("Explode(): This should never happen!\n");
4096 GfxElement[x][y] = EL_EMPTY;
4102 border_element = Store2[x][y];
4103 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
4104 border_element = StorePlayer[x][y];
4106 if (phase == element_info[border_element].ignition_delay ||
4107 phase == last_phase)
4109 boolean border_explosion = FALSE;
4111 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
4112 !PLAYER_EXPLOSION_PROTECTED(x, y))
4114 KillPlayerUnlessExplosionProtected(x, y);
4115 border_explosion = TRUE;
4117 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
4119 Feld[x][y] = Store2[x][y];
4122 border_explosion = TRUE;
4124 else if (border_element == EL_AMOEBA_TO_DIAMOND)
4126 AmoebeUmwandeln(x, y);
4128 border_explosion = TRUE;
4131 /* if an element just explodes due to another explosion (chain-reaction),
4132 do not immediately end the new explosion when it was the last frame of
4133 the explosion (as it would be done in the following "if"-statement!) */
4134 if (border_explosion && phase == last_phase)
4138 if (phase == last_phase)
4142 element = Feld[x][y] = Store[x][y];
4143 Store[x][y] = Store2[x][y] = 0;
4144 GfxElement[x][y] = EL_UNDEFINED;
4146 /* player can escape from explosions and might therefore be still alive */
4147 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
4148 element <= EL_PLAYER_IS_EXPLODING_4)
4150 int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
4151 int explosion_element = EL_PLAYER_1 + player_nr;
4152 int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
4153 int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
4155 if (level.use_explosion_element[player_nr])
4156 explosion_element = level.explosion_element[player_nr];
4158 Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
4159 element_info[explosion_element].content.e[xx][yy]);
4162 /* restore probably existing indestructible background element */
4163 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
4164 element = Feld[x][y] = Back[x][y];
4167 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
4168 GfxDir[x][y] = MV_NONE;
4169 ChangeDelay[x][y] = 0;
4170 ChangePage[x][y] = -1;
4172 #if USE_NEW_CUSTOM_VALUE
4173 CustomValue[x][y] = 0;
4176 InitField_WithBug2(x, y, FALSE);
4178 DrawLevelField(x, y);
4180 TestIfElementTouchesCustomElement(x, y);
4182 if (GFX_CRUMBLED(element))
4183 DrawLevelFieldCrumbledSandNeighbours(x, y);
4185 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
4186 StorePlayer[x][y] = 0;
4188 if (ELEM_IS_PLAYER(element))
4189 RelocatePlayer(x, y, element);
4191 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
4193 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
4194 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4197 DrawLevelFieldCrumbledSand(x, y);
4199 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
4201 DrawLevelElement(x, y, Back[x][y]);
4202 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
4204 else if (IS_WALKABLE_UNDER(Back[x][y]))
4206 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
4207 DrawLevelElementThruMask(x, y, Back[x][y]);
4209 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
4210 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
4214 void DynaExplode(int ex, int ey)
4217 int dynabomb_element = Feld[ex][ey];
4218 int dynabomb_size = 1;
4219 boolean dynabomb_xl = FALSE;
4220 struct PlayerInfo *player;
4221 static int xy[4][2] =
4229 if (IS_ACTIVE_BOMB(dynabomb_element))
4231 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
4232 dynabomb_size = player->dynabomb_size;
4233 dynabomb_xl = player->dynabomb_xl;
4234 player->dynabombs_left++;
4237 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
4239 for (i = 0; i < NUM_DIRECTIONS; i++)
4241 for (j = 1; j <= dynabomb_size; j++)
4243 int x = ex + j * xy[i][0];
4244 int y = ey + j * xy[i][1];
4247 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
4250 element = Feld[x][y];
4252 /* do not restart explosions of fields with active bombs */
4253 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
4256 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
4258 if (element != EL_EMPTY && element != EL_EXPLOSION &&
4259 !IS_DIGGABLE(element) && !dynabomb_xl)
4265 void Bang(int x, int y)
4267 int element = MovingOrBlocked2Element(x, y);
4268 int explosion_type = EX_TYPE_NORMAL;
4270 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
4272 struct PlayerInfo *player = PLAYERINFO(x, y);
4274 element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
4275 player->element_nr);
4277 if (level.use_explosion_element[player->index_nr])
4279 int explosion_element = level.explosion_element[player->index_nr];
4281 if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
4282 explosion_type = EX_TYPE_CROSS;
4283 else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
4284 explosion_type = EX_TYPE_CENTER;
4292 case EL_BD_BUTTERFLY:
4295 case EL_DARK_YAMYAM:
4299 RaiseScoreElement(element);
4302 case EL_DYNABOMB_PLAYER_1_ACTIVE:
4303 case EL_DYNABOMB_PLAYER_2_ACTIVE:
4304 case EL_DYNABOMB_PLAYER_3_ACTIVE:
4305 case EL_DYNABOMB_PLAYER_4_ACTIVE:
4306 case EL_DYNABOMB_INCREASE_NUMBER:
4307 case EL_DYNABOMB_INCREASE_SIZE:
4308 case EL_DYNABOMB_INCREASE_POWER:
4309 explosion_type = EX_TYPE_DYNA;
4312 case EL_DC_LANDMINE:
4314 case EL_EM_EXIT_OPEN:
4315 case EL_EM_STEEL_EXIT_OPEN:
4317 explosion_type = EX_TYPE_CENTER;
4322 case EL_LAMP_ACTIVE:
4323 case EL_AMOEBA_TO_DIAMOND:
4324 if (!IS_PLAYER(x, y)) /* penguin and player may be at same field */
4325 explosion_type = EX_TYPE_CENTER;
4329 if (element_info[element].explosion_type == EXPLODES_CROSS)
4330 explosion_type = EX_TYPE_CROSS;
4331 else if (element_info[element].explosion_type == EXPLODES_1X1)
4332 explosion_type = EX_TYPE_CENTER;
4336 if (explosion_type == EX_TYPE_DYNA)
4339 Explode(x, y, EX_PHASE_START, explosion_type);
4341 CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
4344 void SplashAcid(int x, int y)
4346 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
4347 (!IN_LEV_FIELD(x - 1, y - 2) ||
4348 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
4349 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
4351 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
4352 (!IN_LEV_FIELD(x + 1, y - 2) ||
4353 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
4354 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
4356 PlayLevelSound(x, y, SND_ACID_SPLASHING);
4359 static void InitBeltMovement()
4361 static int belt_base_element[4] =
4363 EL_CONVEYOR_BELT_1_LEFT,
4364 EL_CONVEYOR_BELT_2_LEFT,
4365 EL_CONVEYOR_BELT_3_LEFT,
4366 EL_CONVEYOR_BELT_4_LEFT
4368 static int belt_base_active_element[4] =
4370 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
4371 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
4372 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
4373 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
4378 /* set frame order for belt animation graphic according to belt direction */
4379 for (i = 0; i < NUM_BELTS; i++)
4383 for (j = 0; j < NUM_BELT_PARTS; j++)
4385 int element = belt_base_active_element[belt_nr] + j;
4386 int graphic = el2img(element);
4388 if (game.belt_dir[i] == MV_LEFT)
4389 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
4391 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
4395 SCAN_PLAYFIELD(x, y)
4397 int element = Feld[x][y];
4399 for (i = 0; i < NUM_BELTS; i++)
4401 if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
4403 int e_belt_nr = getBeltNrFromBeltElement(element);
4406 if (e_belt_nr == belt_nr)
4408 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
4410 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
4417 static void ToggleBeltSwitch(int x, int y)
4419 static int belt_base_element[4] =
4421 EL_CONVEYOR_BELT_1_LEFT,
4422 EL_CONVEYOR_BELT_2_LEFT,
4423 EL_CONVEYOR_BELT_3_LEFT,
4424 EL_CONVEYOR_BELT_4_LEFT
4426 static int belt_base_active_element[4] =
4428 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
4429 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
4430 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
4431 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
4433 static int belt_base_switch_element[4] =
4435 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
4436 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
4437 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
4438 EL_CONVEYOR_BELT_4_SWITCH_LEFT
4440 static int belt_move_dir[4] =
4448 int element = Feld[x][y];
4449 int belt_nr = getBeltNrFromBeltSwitchElement(element);
4450 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
4451 int belt_dir = belt_move_dir[belt_dir_nr];
4454 if (!IS_BELT_SWITCH(element))
4457 game.belt_dir_nr[belt_nr] = belt_dir_nr;
4458 game.belt_dir[belt_nr] = belt_dir;
4460 if (belt_dir_nr == 3)
4463 /* set frame order for belt animation graphic according to belt direction */
4464 for (i = 0; i < NUM_BELT_PARTS; i++)
4466 int element = belt_base_active_element[belt_nr] + i;
4467 int graphic = el2img(element);
4469 if (belt_dir == MV_LEFT)
4470 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
4472 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
4475 SCAN_PLAYFIELD(xx, yy)
4477 int element = Feld[xx][yy];
4479 if (IS_BELT_SWITCH(element))
4481 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
4483 if (e_belt_nr == belt_nr)
4485 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
4486 DrawLevelField(xx, yy);
4489 else if (IS_BELT(element) && belt_dir != MV_NONE)
4491 int e_belt_nr = getBeltNrFromBeltElement(element);
4493 if (e_belt_nr == belt_nr)
4495 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
4497 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
4498 DrawLevelField(xx, yy);
4501 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
4503 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
4505 if (e_belt_nr == belt_nr)
4507 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
4509 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
4510 DrawLevelField(xx, yy);
4516 static void ToggleSwitchgateSwitch(int x, int y)
4520 game.switchgate_pos = !game.switchgate_pos;
4522 SCAN_PLAYFIELD(xx, yy)
4524 int element = Feld[xx][yy];
4526 #if !USE_BOTH_SWITCHGATE_SWITCHES
4527 if (element == EL_SWITCHGATE_SWITCH_UP ||
4528 element == EL_SWITCHGATE_SWITCH_DOWN)
4530 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
4531 DrawLevelField(xx, yy);
4533 else if (element == EL_DC_SWITCHGATE_SWITCH_UP ||
4534 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
4536 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
4537 DrawLevelField(xx, yy);
4540 if (element == EL_SWITCHGATE_SWITCH_UP)
4542 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
4543 DrawLevelField(xx, yy);
4545 else if (element == EL_SWITCHGATE_SWITCH_DOWN)
4547 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
4548 DrawLevelField(xx, yy);
4550 else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
4552 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
4553 DrawLevelField(xx, yy);
4555 else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
4557 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
4558 DrawLevelField(xx, yy);
4561 else if (element == EL_SWITCHGATE_OPEN ||
4562 element == EL_SWITCHGATE_OPENING)
4564 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
4566 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
4568 else if (element == EL_SWITCHGATE_CLOSED ||
4569 element == EL_SWITCHGATE_CLOSING)
4571 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
4573 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
4578 static int getInvisibleActiveFromInvisibleElement(int element)
4580 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
4581 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
4582 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
4586 static int getInvisibleFromInvisibleActiveElement(int element)
4588 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
4589 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
4590 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
4594 static void RedrawAllLightSwitchesAndInvisibleElements()
4598 SCAN_PLAYFIELD(x, y)
4600 int element = Feld[x][y];
4602 if (element == EL_LIGHT_SWITCH &&
4603 game.light_time_left > 0)
4605 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
4606 DrawLevelField(x, y);
4608 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
4609 game.light_time_left == 0)
4611 Feld[x][y] = EL_LIGHT_SWITCH;
4612 DrawLevelField(x, y);
4614 else if (element == EL_EMC_DRIPPER &&
4615 game.light_time_left > 0)
4617 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
4618 DrawLevelField(x, y);
4620 else if (element == EL_EMC_DRIPPER_ACTIVE &&
4621 game.light_time_left == 0)
4623 Feld[x][y] = EL_EMC_DRIPPER;
4624 DrawLevelField(x, y);
4626 else if (element == EL_INVISIBLE_STEELWALL ||
4627 element == EL_INVISIBLE_WALL ||
4628 element == EL_INVISIBLE_SAND)
4630 if (game.light_time_left > 0)
4631 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
4633 DrawLevelField(x, y);
4635 /* uncrumble neighbour fields, if needed */
4636 if (element == EL_INVISIBLE_SAND)
4637 DrawLevelFieldCrumbledSandNeighbours(x, y);
4639 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
4640 element == EL_INVISIBLE_WALL_ACTIVE ||
4641 element == EL_INVISIBLE_SAND_ACTIVE)
4643 if (game.light_time_left == 0)
4644 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
4646 DrawLevelField(x, y);
4648 /* re-crumble neighbour fields, if needed */
4649 if (element == EL_INVISIBLE_SAND)
4650 DrawLevelFieldCrumbledSandNeighbours(x, y);
4655 static void RedrawAllInvisibleElementsForLenses()
4659 SCAN_PLAYFIELD(x, y)
4661 int element = Feld[x][y];
4663 if (element == EL_EMC_DRIPPER &&
4664 game.lenses_time_left > 0)
4666 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
4667 DrawLevelField(x, y);
4669 else if (element == EL_EMC_DRIPPER_ACTIVE &&
4670 game.lenses_time_left == 0)
4672 Feld[x][y] = EL_EMC_DRIPPER;
4673 DrawLevelField(x, y);
4675 else if (element == EL_INVISIBLE_STEELWALL ||
4676 element == EL_INVISIBLE_WALL ||
4677 element == EL_INVISIBLE_SAND)
4679 if (game.lenses_time_left > 0)
4680 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
4682 DrawLevelField(x, y);
4684 /* uncrumble neighbour fields, if needed */
4685 if (element == EL_INVISIBLE_SAND)
4686 DrawLevelFieldCrumbledSandNeighbours(x, y);
4688 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
4689 element == EL_INVISIBLE_WALL_ACTIVE ||
4690 element == EL_INVISIBLE_SAND_ACTIVE)
4692 if (game.lenses_time_left == 0)
4693 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
4695 DrawLevelField(x, y);
4697 /* re-crumble neighbour fields, if needed */
4698 if (element == EL_INVISIBLE_SAND)
4699 DrawLevelFieldCrumbledSandNeighbours(x, y);
4704 static void RedrawAllInvisibleElementsForMagnifier()
4708 SCAN_PLAYFIELD(x, y)
4710 int element = Feld[x][y];
4712 if (element == EL_EMC_FAKE_GRASS &&
4713 game.magnify_time_left > 0)
4715 Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
4716 DrawLevelField(x, y);
4718 else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
4719 game.magnify_time_left == 0)
4721 Feld[x][y] = EL_EMC_FAKE_GRASS;
4722 DrawLevelField(x, y);
4724 else if (IS_GATE_GRAY(element) &&
4725 game.magnify_time_left > 0)
4727 Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
4728 element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
4729 IS_EM_GATE_GRAY(element) ?
4730 element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
4731 IS_EMC_GATE_GRAY(element) ?
4732 element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
4734 DrawLevelField(x, y);
4736 else if (IS_GATE_GRAY_ACTIVE(element) &&
4737 game.magnify_time_left == 0)
4739 Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
4740 element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
4741 IS_EM_GATE_GRAY_ACTIVE(element) ?
4742 element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
4743 IS_EMC_GATE_GRAY_ACTIVE(element) ?
4744 element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
4746 DrawLevelField(x, y);
4751 static void ToggleLightSwitch(int x, int y)
4753 int element = Feld[x][y];
4755 game.light_time_left =
4756 (element == EL_LIGHT_SWITCH ?
4757 level.time_light * FRAMES_PER_SECOND : 0);
4759 RedrawAllLightSwitchesAndInvisibleElements();
4762 static void ActivateTimegateSwitch(int x, int y)
4766 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
4768 SCAN_PLAYFIELD(xx, yy)
4770 int element = Feld[xx][yy];
4772 if (element == EL_TIMEGATE_CLOSED ||
4773 element == EL_TIMEGATE_CLOSING)
4775 Feld[xx][yy] = EL_TIMEGATE_OPENING;
4776 PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
4780 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
4782 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
4783 DrawLevelField(xx, yy);
4790 Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
4791 EL_DC_TIMEGATE_SWITCH_ACTIVE);
4793 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
4797 void Impact(int x, int y)
4799 boolean last_line = (y == lev_fieldy - 1);
4800 boolean object_hit = FALSE;
4801 boolean impact = (last_line || object_hit);
4802 int element = Feld[x][y];
4803 int smashed = EL_STEELWALL;
4805 if (!last_line) /* check if element below was hit */
4807 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
4810 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
4811 MovDir[x][y + 1] != MV_DOWN ||
4812 MovPos[x][y + 1] <= TILEY / 2));
4814 /* do not smash moving elements that left the smashed field in time */
4815 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
4816 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
4819 #if USE_QUICKSAND_IMPACT_BUGFIX
4820 if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
4822 RemoveMovingField(x, y + 1);
4823 Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
4824 Feld[x][y + 2] = EL_ROCK;
4825 DrawLevelField(x, y + 2);
4830 if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
4832 RemoveMovingField(x, y + 1);
4833 Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
4834 Feld[x][y + 2] = EL_ROCK;
4835 DrawLevelField(x, y + 2);
4842 smashed = MovingOrBlocked2Element(x, y + 1);
4844 impact = (last_line || object_hit);
4847 if (!last_line && smashed == EL_ACID) /* element falls into acid */
4849 SplashAcid(x, y + 1);
4853 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
4854 /* only reset graphic animation if graphic really changes after impact */
4856 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
4858 ResetGfxAnimation(x, y);
4859 DrawLevelField(x, y);
4862 if (impact && CAN_EXPLODE_IMPACT(element))
4867 else if (impact && element == EL_PEARL &&
4868 smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
4870 ResetGfxAnimation(x, y);
4872 Feld[x][y] = EL_PEARL_BREAKING;
4873 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4876 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
4878 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4883 if (impact && element == EL_AMOEBA_DROP)
4885 if (object_hit && IS_PLAYER(x, y + 1))
4886 KillPlayerUnlessEnemyProtected(x, y + 1);
4887 else if (object_hit && smashed == EL_PENGUIN)
4891 Feld[x][y] = EL_AMOEBA_GROWING;
4892 Store[x][y] = EL_AMOEBA_WET;
4894 ResetRandomAnimationValue(x, y);
4899 if (object_hit) /* check which object was hit */
4901 if ((CAN_PASS_MAGIC_WALL(element) &&
4902 (smashed == EL_MAGIC_WALL ||
4903 smashed == EL_BD_MAGIC_WALL)) ||
4904 (CAN_PASS_DC_MAGIC_WALL(element) &&
4905 smashed == EL_DC_MAGIC_WALL))
4908 int activated_magic_wall =
4909 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
4910 smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
4911 EL_DC_MAGIC_WALL_ACTIVE);
4913 /* activate magic wall / mill */
4914 SCAN_PLAYFIELD(xx, yy)
4916 if (Feld[xx][yy] == smashed)
4917 Feld[xx][yy] = activated_magic_wall;
4920 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
4921 game.magic_wall_active = TRUE;
4923 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
4924 SND_MAGIC_WALL_ACTIVATING :
4925 smashed == EL_BD_MAGIC_WALL ?
4926 SND_BD_MAGIC_WALL_ACTIVATING :
4927 SND_DC_MAGIC_WALL_ACTIVATING));
4930 if (IS_PLAYER(x, y + 1))
4932 if (CAN_SMASH_PLAYER(element))
4934 KillPlayerUnlessEnemyProtected(x, y + 1);
4938 else if (smashed == EL_PENGUIN)
4940 if (CAN_SMASH_PLAYER(element))
4946 else if (element == EL_BD_DIAMOND)
4948 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
4954 else if (((element == EL_SP_INFOTRON ||
4955 element == EL_SP_ZONK) &&
4956 (smashed == EL_SP_SNIKSNAK ||
4957 smashed == EL_SP_ELECTRON ||
4958 smashed == EL_SP_DISK_ORANGE)) ||
4959 (element == EL_SP_INFOTRON &&
4960 smashed == EL_SP_DISK_YELLOW))
4965 else if (CAN_SMASH_EVERYTHING(element))
4967 if (IS_CLASSIC_ENEMY(smashed) ||
4968 CAN_EXPLODE_SMASHED(smashed))
4973 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
4975 if (smashed == EL_LAMP ||
4976 smashed == EL_LAMP_ACTIVE)
4981 else if (smashed == EL_NUT)
4983 Feld[x][y + 1] = EL_NUT_BREAKING;
4984 PlayLevelSound(x, y, SND_NUT_BREAKING);
4985 RaiseScoreElement(EL_NUT);
4988 else if (smashed == EL_PEARL)
4990 ResetGfxAnimation(x, y);
4992 Feld[x][y + 1] = EL_PEARL_BREAKING;
4993 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4996 else if (smashed == EL_DIAMOND)
4998 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
4999 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
5002 else if (IS_BELT_SWITCH(smashed))
5004 ToggleBeltSwitch(x, y + 1);
5006 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
5007 smashed == EL_SWITCHGATE_SWITCH_DOWN ||
5008 smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
5009 smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
5011 ToggleSwitchgateSwitch(x, y + 1);
5013 else if (smashed == EL_LIGHT_SWITCH ||
5014 smashed == EL_LIGHT_SWITCH_ACTIVE)
5016 ToggleLightSwitch(x, y + 1);
5021 TestIfElementSmashesCustomElement(x, y, MV_DOWN);
5024 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
5026 CheckElementChangeBySide(x, y + 1, smashed, element,
5027 CE_SWITCHED, CH_SIDE_TOP);
5028 CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
5034 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
5039 /* play sound of magic wall / mill */
5041 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
5042 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
5043 Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
5045 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
5046 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
5047 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
5048 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
5049 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
5050 PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
5055 /* play sound of object that hits the ground */
5056 if (last_line || object_hit)
5057 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
5060 inline static void TurnRoundExt(int x, int y)
5072 { 0, 0 }, { 0, 0 }, { 0, 0 },
5077 int left, right, back;
5081 { MV_DOWN, MV_UP, MV_RIGHT },
5082 { MV_UP, MV_DOWN, MV_LEFT },
5084 { MV_LEFT, MV_RIGHT, MV_DOWN },
5088 { MV_RIGHT, MV_LEFT, MV_UP }
5091 int element = Feld[x][y];
5092 int move_pattern = element_info[element].move_pattern;
5094 int old_move_dir = MovDir[x][y];
5095 int left_dir = turn[old_move_dir].left;
5096 int right_dir = turn[old_move_dir].right;
5097 int back_dir = turn[old_move_dir].back;
5099 int left_dx = move_xy[left_dir].dx, left_dy = move_xy[left_dir].dy;
5100 int right_dx = move_xy[right_dir].dx, right_dy = move_xy[right_dir].dy;
5101 int move_dx = move_xy[old_move_dir].dx, move_dy = move_xy[old_move_dir].dy;
5102 int back_dx = move_xy[back_dir].dx, back_dy = move_xy[back_dir].dy;
5104 int left_x = x + left_dx, left_y = y + left_dy;
5105 int right_x = x + right_dx, right_y = y + right_dy;
5106 int move_x = x + move_dx, move_y = y + move_dy;
5110 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
5112 TestIfBadThingTouchesOtherBadThing(x, y);
5114 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
5115 MovDir[x][y] = right_dir;
5116 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
5117 MovDir[x][y] = left_dir;
5119 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
5121 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
5124 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
5126 TestIfBadThingTouchesOtherBadThing(x, y);
5128 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
5129 MovDir[x][y] = left_dir;
5130 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
5131 MovDir[x][y] = right_dir;
5133 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
5135 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
5138 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
5140 TestIfBadThingTouchesOtherBadThing(x, y);
5142 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
5143 MovDir[x][y] = left_dir;
5144 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
5145 MovDir[x][y] = right_dir;
5147 if (MovDir[x][y] != old_move_dir)
5150 else if (element == EL_YAMYAM)
5152 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
5153 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
5155 if (can_turn_left && can_turn_right)
5156 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5157 else if (can_turn_left)
5158 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5159 else if (can_turn_right)
5160 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5162 MovDir[x][y] = back_dir;
5164 MovDelay[x][y] = 16 + 16 * RND(3);
5166 else if (element == EL_DARK_YAMYAM)
5168 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
5170 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
5173 if (can_turn_left && can_turn_right)
5174 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5175 else if (can_turn_left)
5176 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5177 else if (can_turn_right)
5178 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5180 MovDir[x][y] = back_dir;
5182 MovDelay[x][y] = 16 + 16 * RND(3);
5184 else if (element == EL_PACMAN)
5186 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
5187 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
5189 if (can_turn_left && can_turn_right)
5190 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5191 else if (can_turn_left)
5192 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5193 else if (can_turn_right)
5194 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5196 MovDir[x][y] = back_dir;
5198 MovDelay[x][y] = 6 + RND(40);
5200 else if (element == EL_PIG)
5202 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
5203 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
5204 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
5205 boolean should_turn_left, should_turn_right, should_move_on;
5207 int rnd = RND(rnd_value);
5209 should_turn_left = (can_turn_left &&
5211 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
5212 y + back_dy + left_dy)));
5213 should_turn_right = (can_turn_right &&
5215 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
5216 y + back_dy + right_dy)));
5217 should_move_on = (can_move_on &&
5220 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
5221 y + move_dy + left_dy) ||
5222 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
5223 y + move_dy + right_dy)));
5225 if (should_turn_left || should_turn_right || should_move_on)
5227 if (should_turn_left && should_turn_right && should_move_on)
5228 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
5229 rnd < 2 * rnd_value / 3 ? right_dir :
5231 else if (should_turn_left && should_turn_right)
5232 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
5233 else if (should_turn_left && should_move_on)
5234 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
5235 else if (should_turn_right && should_move_on)
5236 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
5237 else if (should_turn_left)
5238 MovDir[x][y] = left_dir;
5239 else if (should_turn_right)
5240 MovDir[x][y] = right_dir;
5241 else if (should_move_on)
5242 MovDir[x][y] = old_move_dir;
5244 else if (can_move_on && rnd > rnd_value / 8)
5245 MovDir[x][y] = old_move_dir;
5246 else if (can_turn_left && can_turn_right)
5247 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
5248 else if (can_turn_left && rnd > rnd_value / 8)
5249 MovDir[x][y] = left_dir;
5250 else if (can_turn_right && rnd > rnd_value/8)
5251 MovDir[x][y] = right_dir;
5253 MovDir[x][y] = back_dir;
5255 xx = x + move_xy[MovDir[x][y]].dx;
5256 yy = y + move_xy[MovDir[x][y]].dy;
5258 if (!IN_LEV_FIELD(xx, yy) ||
5259 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
5260 MovDir[x][y] = old_move_dir;
5264 else if (element == EL_DRAGON)
5266 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
5267 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
5268 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
5270 int rnd = RND(rnd_value);
5272 if (can_move_on && rnd > rnd_value / 8)
5273 MovDir[x][y] = old_move_dir;
5274 else if (can_turn_left && can_turn_right)
5275 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
5276 else if (can_turn_left && rnd > rnd_value / 8)
5277 MovDir[x][y] = left_dir;
5278 else if (can_turn_right && rnd > rnd_value / 8)
5279 MovDir[x][y] = right_dir;
5281 MovDir[x][y] = back_dir;
5283 xx = x + move_xy[MovDir[x][y]].dx;
5284 yy = y + move_xy[MovDir[x][y]].dy;
5286 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
5287 MovDir[x][y] = old_move_dir;
5291 else if (element == EL_MOLE)
5293 boolean can_move_on =
5294 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
5295 IS_AMOEBOID(Feld[move_x][move_y]) ||
5296 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
5299 boolean can_turn_left =
5300 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
5301 IS_AMOEBOID(Feld[left_x][left_y])));
5303 boolean can_turn_right =
5304 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
5305 IS_AMOEBOID(Feld[right_x][right_y])));
5307 if (can_turn_left && can_turn_right)
5308 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
5309 else if (can_turn_left)
5310 MovDir[x][y] = left_dir;
5312 MovDir[x][y] = right_dir;
5315 if (MovDir[x][y] != old_move_dir)
5318 else if (element == EL_BALLOON)
5320 MovDir[x][y] = game.wind_direction;
5323 else if (element == EL_SPRING)
5325 #if USE_NEW_SPRING_BUMPER
5326 if (MovDir[x][y] & MV_HORIZONTAL)
5328 if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
5329 !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
5331 Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
5332 ResetGfxAnimation(move_x, move_y);
5333 DrawLevelField(move_x, move_y);
5335 MovDir[x][y] = back_dir;
5337 else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
5338 SPRING_CAN_ENTER_FIELD(element, x, y + 1))
5339 MovDir[x][y] = MV_NONE;
5342 if (MovDir[x][y] & MV_HORIZONTAL &&
5343 (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
5344 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
5345 MovDir[x][y] = MV_NONE;
5350 else if (element == EL_ROBOT ||
5351 element == EL_SATELLITE ||
5352 element == EL_PENGUIN ||
5353 element == EL_EMC_ANDROID)
5355 int attr_x = -1, attr_y = -1;
5366 for (i = 0; i < MAX_PLAYERS; i++)
5368 struct PlayerInfo *player = &stored_player[i];
5369 int jx = player->jx, jy = player->jy;
5371 if (!player->active)
5375 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
5383 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
5384 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
5385 game.engine_version < VERSION_IDENT(3,1,0,0)))
5391 if (element == EL_PENGUIN)
5394 static int xy[4][2] =
5402 for (i = 0; i < NUM_DIRECTIONS; i++)
5404 int ex = x + xy[i][0];
5405 int ey = y + xy[i][1];
5407 if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
5408 Feld[ex][ey] == EL_EM_EXIT_OPEN ||
5409 Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
5410 Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
5419 MovDir[x][y] = MV_NONE;
5421 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
5422 else if (attr_x > x)
5423 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
5425 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
5426 else if (attr_y > y)
5427 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
5429 if (element == EL_ROBOT)
5433 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5434 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
5435 Moving2Blocked(x, y, &newx, &newy);
5437 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
5438 MovDelay[x][y] = 8 + 8 * !RND(3);
5440 MovDelay[x][y] = 16;
5442 else if (element == EL_PENGUIN)
5448 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5450 boolean first_horiz = RND(2);
5451 int new_move_dir = MovDir[x][y];
5454 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5455 Moving2Blocked(x, y, &newx, &newy);
5457 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
5461 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5462 Moving2Blocked(x, y, &newx, &newy);
5464 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
5467 MovDir[x][y] = old_move_dir;
5471 else if (element == EL_SATELLITE)
5477 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5479 boolean first_horiz = RND(2);
5480 int new_move_dir = MovDir[x][y];
5483 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5484 Moving2Blocked(x, y, &newx, &newy);
5486 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
5490 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5491 Moving2Blocked(x, y, &newx, &newy);
5493 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
5496 MovDir[x][y] = old_move_dir;
5500 else if (element == EL_EMC_ANDROID)
5502 static int check_pos[16] =
5504 -1, /* 0 => (invalid) */
5505 7, /* 1 => MV_LEFT */
5506 3, /* 2 => MV_RIGHT */
5507 -1, /* 3 => (invalid) */
5509 0, /* 5 => MV_LEFT | MV_UP */
5510 2, /* 6 => MV_RIGHT | MV_UP */
5511 -1, /* 7 => (invalid) */
5512 5, /* 8 => MV_DOWN */
5513 6, /* 9 => MV_LEFT | MV_DOWN */
5514 4, /* 10 => MV_RIGHT | MV_DOWN */
5515 -1, /* 11 => (invalid) */
5516 -1, /* 12 => (invalid) */
5517 -1, /* 13 => (invalid) */
5518 -1, /* 14 => (invalid) */
5519 -1, /* 15 => (invalid) */
5527 { -1, -1, MV_LEFT | MV_UP },
5529 { +1, -1, MV_RIGHT | MV_UP },
5530 { +1, 0, MV_RIGHT },
5531 { +1, +1, MV_RIGHT | MV_DOWN },
5533 { -1, +1, MV_LEFT | MV_DOWN },
5536 int start_pos, check_order;
5537 boolean can_clone = FALSE;
5540 /* check if there is any free field around current position */
5541 for (i = 0; i < 8; i++)
5543 int newx = x + check_xy[i].dx;
5544 int newy = y + check_xy[i].dy;
5546 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5554 if (can_clone) /* randomly find an element to clone */
5558 start_pos = check_pos[RND(8)];
5559 check_order = (RND(2) ? -1 : +1);
5561 for (i = 0; i < 8; i++)
5563 int pos_raw = start_pos + i * check_order;
5564 int pos = (pos_raw + 8) % 8;
5565 int newx = x + check_xy[pos].dx;
5566 int newy = y + check_xy[pos].dy;
5568 if (ANDROID_CAN_CLONE_FIELD(newx, newy))
5570 element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
5571 element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
5573 Store[x][y] = Feld[newx][newy];
5582 if (can_clone) /* randomly find a direction to move */
5586 start_pos = check_pos[RND(8)];
5587 check_order = (RND(2) ? -1 : +1);
5589 for (i = 0; i < 8; i++)
5591 int pos_raw = start_pos + i * check_order;
5592 int pos = (pos_raw + 8) % 8;
5593 int newx = x + check_xy[pos].dx;
5594 int newy = y + check_xy[pos].dy;
5595 int new_move_dir = check_xy[pos].dir;
5597 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5599 MovDir[x][y] = new_move_dir;
5600 MovDelay[x][y] = level.android_clone_time * 8 + 1;
5609 if (can_clone) /* cloning and moving successful */
5612 /* cannot clone -- try to move towards player */
5614 start_pos = check_pos[MovDir[x][y] & 0x0f];
5615 check_order = (RND(2) ? -1 : +1);
5617 for (i = 0; i < 3; i++)
5619 /* first check start_pos, then previous/next or (next/previous) pos */
5620 int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
5621 int pos = (pos_raw + 8) % 8;
5622 int newx = x + check_xy[pos].dx;
5623 int newy = y + check_xy[pos].dy;
5624 int new_move_dir = check_xy[pos].dir;
5626 if (IS_PLAYER(newx, newy))
5629 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
5631 MovDir[x][y] = new_move_dir;
5632 MovDelay[x][y] = level.android_move_time * 8 + 1;
5639 else if (move_pattern == MV_TURNING_LEFT ||
5640 move_pattern == MV_TURNING_RIGHT ||
5641 move_pattern == MV_TURNING_LEFT_RIGHT ||
5642 move_pattern == MV_TURNING_RIGHT_LEFT ||
5643 move_pattern == MV_TURNING_RANDOM ||
5644 move_pattern == MV_ALL_DIRECTIONS)
5646 boolean can_turn_left =
5647 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
5648 boolean can_turn_right =
5649 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
5651 if (element_info[element].move_stepsize == 0) /* "not moving" */
5654 if (move_pattern == MV_TURNING_LEFT)
5655 MovDir[x][y] = left_dir;
5656 else if (move_pattern == MV_TURNING_RIGHT)
5657 MovDir[x][y] = right_dir;
5658 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
5659 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
5660 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
5661 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
5662 else if (move_pattern == MV_TURNING_RANDOM)
5663 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
5664 can_turn_right && !can_turn_left ? right_dir :
5665 RND(2) ? left_dir : right_dir);
5666 else if (can_turn_left && can_turn_right)
5667 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5668 else if (can_turn_left)
5669 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5670 else if (can_turn_right)
5671 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5673 MovDir[x][y] = back_dir;
5675 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5677 else if (move_pattern == MV_HORIZONTAL ||
5678 move_pattern == MV_VERTICAL)
5680 if (move_pattern & old_move_dir)
5681 MovDir[x][y] = back_dir;
5682 else if (move_pattern == MV_HORIZONTAL)
5683 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
5684 else if (move_pattern == MV_VERTICAL)
5685 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
5687 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5689 else if (move_pattern & MV_ANY_DIRECTION)
5691 MovDir[x][y] = move_pattern;
5692 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5694 else if (move_pattern & MV_WIND_DIRECTION)
5696 MovDir[x][y] = game.wind_direction;
5697 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5699 else if (move_pattern == MV_ALONG_LEFT_SIDE)
5701 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
5702 MovDir[x][y] = left_dir;
5703 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5704 MovDir[x][y] = right_dir;
5706 if (MovDir[x][y] != old_move_dir)
5707 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5709 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
5711 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
5712 MovDir[x][y] = right_dir;
5713 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5714 MovDir[x][y] = left_dir;
5716 if (MovDir[x][y] != old_move_dir)
5717 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5719 else if (move_pattern == MV_TOWARDS_PLAYER ||
5720 move_pattern == MV_AWAY_FROM_PLAYER)
5722 int attr_x = -1, attr_y = -1;
5724 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
5735 for (i = 0; i < MAX_PLAYERS; i++)
5737 struct PlayerInfo *player = &stored_player[i];
5738 int jx = player->jx, jy = player->jy;
5740 if (!player->active)
5744 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
5752 MovDir[x][y] = MV_NONE;
5754 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
5755 else if (attr_x > x)
5756 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
5758 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
5759 else if (attr_y > y)
5760 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
5762 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5764 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5766 boolean first_horiz = RND(2);
5767 int new_move_dir = MovDir[x][y];
5769 if (element_info[element].move_stepsize == 0) /* "not moving" */
5771 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
5772 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5778 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5779 Moving2Blocked(x, y, &newx, &newy);
5781 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5785 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5786 Moving2Blocked(x, y, &newx, &newy);
5788 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5791 MovDir[x][y] = old_move_dir;
5794 else if (move_pattern == MV_WHEN_PUSHED ||
5795 move_pattern == MV_WHEN_DROPPED)
5797 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5798 MovDir[x][y] = MV_NONE;
5802 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
5804 static int test_xy[7][2] =
5814 static int test_dir[7] =
5824 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
5825 int move_preference = -1000000; /* start with very low preference */
5826 int new_move_dir = MV_NONE;
5827 int start_test = RND(4);
5830 for (i = 0; i < NUM_DIRECTIONS; i++)
5832 int move_dir = test_dir[start_test + i];
5833 int move_dir_preference;
5835 xx = x + test_xy[start_test + i][0];
5836 yy = y + test_xy[start_test + i][1];
5838 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
5839 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
5841 new_move_dir = move_dir;
5846 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
5849 move_dir_preference = -1 * RunnerVisit[xx][yy];
5850 if (hunter_mode && PlayerVisit[xx][yy] > 0)
5851 move_dir_preference = PlayerVisit[xx][yy];
5853 if (move_dir_preference > move_preference)
5855 /* prefer field that has not been visited for the longest time */
5856 move_preference = move_dir_preference;
5857 new_move_dir = move_dir;
5859 else if (move_dir_preference == move_preference &&
5860 move_dir == old_move_dir)
5862 /* prefer last direction when all directions are preferred equally */
5863 move_preference = move_dir_preference;
5864 new_move_dir = move_dir;
5868 MovDir[x][y] = new_move_dir;
5869 if (old_move_dir != new_move_dir)
5870 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5874 static void TurnRound(int x, int y)
5876 int direction = MovDir[x][y];
5880 GfxDir[x][y] = MovDir[x][y];
5882 if (direction != MovDir[x][y])
5886 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
5888 ResetGfxFrame(x, y, FALSE);
5891 static boolean JustBeingPushed(int x, int y)
5895 for (i = 0; i < MAX_PLAYERS; i++)
5897 struct PlayerInfo *player = &stored_player[i];
5899 if (player->active && player->is_pushing && player->MovPos)
5901 int next_jx = player->jx + (player->jx - player->last_jx);
5902 int next_jy = player->jy + (player->jy - player->last_jy);
5904 if (x == next_jx && y == next_jy)
5912 void StartMoving(int x, int y)
5914 boolean started_moving = FALSE; /* some elements can fall _and_ move */
5915 int element = Feld[x][y];
5920 if (MovDelay[x][y] == 0)
5921 GfxAction[x][y] = ACTION_DEFAULT;
5923 if (CAN_FALL(element) && y < lev_fieldy - 1)
5925 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
5926 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
5927 if (JustBeingPushed(x, y))
5930 if (element == EL_QUICKSAND_FULL)
5932 if (IS_FREE(x, y + 1))
5934 InitMovingField(x, y, MV_DOWN);
5935 started_moving = TRUE;
5937 Feld[x][y] = EL_QUICKSAND_EMPTYING;
5938 #if USE_QUICKSAND_BD_ROCK_BUGFIX
5939 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
5940 Store[x][y] = EL_ROCK;
5942 Store[x][y] = EL_ROCK;
5945 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
5947 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5949 if (!MovDelay[x][y])
5950 MovDelay[x][y] = TILEY + 1;
5959 Feld[x][y] = EL_QUICKSAND_EMPTY;
5960 Feld[x][y + 1] = EL_QUICKSAND_FULL;
5961 Store[x][y + 1] = Store[x][y];
5964 PlayLevelSoundAction(x, y, ACTION_FILLING);
5967 else if (element == EL_QUICKSAND_FAST_FULL)
5969 if (IS_FREE(x, y + 1))
5971 InitMovingField(x, y, MV_DOWN);
5972 started_moving = TRUE;
5974 Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
5975 #if USE_QUICKSAND_BD_ROCK_BUGFIX
5976 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
5977 Store[x][y] = EL_ROCK;
5979 Store[x][y] = EL_ROCK;
5982 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
5984 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
5986 if (!MovDelay[x][y])
5987 MovDelay[x][y] = TILEY + 1;
5996 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
5997 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
5998 Store[x][y + 1] = Store[x][y];
6001 PlayLevelSoundAction(x, y, ACTION_FILLING);
6004 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
6005 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
6007 InitMovingField(x, y, MV_DOWN);
6008 started_moving = TRUE;
6010 Feld[x][y] = EL_QUICKSAND_FILLING;
6011 Store[x][y] = element;
6013 PlayLevelSoundAction(x, y, ACTION_FILLING);
6015 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
6016 Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
6018 InitMovingField(x, y, MV_DOWN);
6019 started_moving = TRUE;
6021 Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
6022 Store[x][y] = element;
6024 PlayLevelSoundAction(x, y, ACTION_FILLING);
6026 else if (element == EL_MAGIC_WALL_FULL)
6028 if (IS_FREE(x, y + 1))
6030 InitMovingField(x, y, MV_DOWN);
6031 started_moving = TRUE;
6033 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
6034 Store[x][y] = EL_CHANGED(Store[x][y]);
6036 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6038 if (!MovDelay[x][y])
6039 MovDelay[x][y] = TILEY/4 + 1;
6048 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
6049 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
6050 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
6054 else if (element == EL_BD_MAGIC_WALL_FULL)
6056 if (IS_FREE(x, y + 1))
6058 InitMovingField(x, y, MV_DOWN);
6059 started_moving = TRUE;
6061 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
6062 Store[x][y] = EL_CHANGED_BD(Store[x][y]);
6064 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6066 if (!MovDelay[x][y])
6067 MovDelay[x][y] = TILEY/4 + 1;
6076 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
6077 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
6078 Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
6082 else if (element == EL_DC_MAGIC_WALL_FULL)
6084 if (IS_FREE(x, y + 1))
6086 InitMovingField(x, y, MV_DOWN);
6087 started_moving = TRUE;
6089 Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
6090 Store[x][y] = EL_CHANGED_DC(Store[x][y]);
6092 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6094 if (!MovDelay[x][y])
6095 MovDelay[x][y] = TILEY/4 + 1;
6104 Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
6105 Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
6106 Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
6110 else if ((CAN_PASS_MAGIC_WALL(element) &&
6111 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6112 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
6113 (CAN_PASS_DC_MAGIC_WALL(element) &&
6114 (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
6117 InitMovingField(x, y, MV_DOWN);
6118 started_moving = TRUE;
6121 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
6122 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
6123 EL_DC_MAGIC_WALL_FILLING);
6124 Store[x][y] = element;
6126 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
6128 SplashAcid(x, y + 1);
6130 InitMovingField(x, y, MV_DOWN);
6131 started_moving = TRUE;
6133 Store[x][y] = EL_ACID;
6136 #if USE_FIX_IMPACT_COLLISION
6137 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
6138 CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
6140 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
6141 CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
6143 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
6144 CAN_FALL(element) && WasJustFalling[x][y] &&
6145 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
6147 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
6148 CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
6149 (Feld[x][y + 1] == EL_BLOCKED)))
6151 /* this is needed for a special case not covered by calling "Impact()"
6152 from "ContinueMoving()": if an element moves to a tile directly below
6153 another element which was just falling on that tile (which was empty
6154 in the previous frame), the falling element above would just stop
6155 instead of smashing the element below (in previous version, the above
6156 element was just checked for "moving" instead of "falling", resulting
6157 in incorrect smashes caused by horizontal movement of the above
6158 element; also, the case of the player being the element to smash was
6159 simply not covered here... :-/ ) */
6161 CheckCollision[x][y] = 0;
6162 CheckImpact[x][y] = 0;
6166 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
6168 if (MovDir[x][y] == MV_NONE)
6170 InitMovingField(x, y, MV_DOWN);
6171 started_moving = TRUE;
6174 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
6176 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
6177 MovDir[x][y] = MV_DOWN;
6179 InitMovingField(x, y, MV_DOWN);
6180 started_moving = TRUE;
6182 else if (element == EL_AMOEBA_DROP)
6184 Feld[x][y] = EL_AMOEBA_GROWING;
6185 Store[x][y] = EL_AMOEBA_WET;
6187 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
6188 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
6189 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
6190 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
6192 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
6193 (IS_FREE(x - 1, y + 1) ||
6194 Feld[x - 1][y + 1] == EL_ACID));
6195 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
6196 (IS_FREE(x + 1, y + 1) ||
6197 Feld[x + 1][y + 1] == EL_ACID));
6198 boolean can_fall_any = (can_fall_left || can_fall_right);
6199 boolean can_fall_both = (can_fall_left && can_fall_right);
6200 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
6202 #if USE_NEW_ALL_SLIPPERY
6203 if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
6205 if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
6206 can_fall_right = FALSE;
6207 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
6208 can_fall_left = FALSE;
6209 else if (slippery_type == SLIPPERY_ONLY_LEFT)
6210 can_fall_right = FALSE;
6211 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
6212 can_fall_left = FALSE;
6214 can_fall_any = (can_fall_left || can_fall_right);
6215 can_fall_both = FALSE;
6218 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
6220 if (slippery_type == SLIPPERY_ONLY_LEFT)
6221 can_fall_right = FALSE;
6222 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
6223 can_fall_left = FALSE;
6224 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
6225 can_fall_right = FALSE;
6226 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
6227 can_fall_left = FALSE;
6229 can_fall_any = (can_fall_left || can_fall_right);
6230 can_fall_both = (can_fall_left && can_fall_right);
6234 #if USE_NEW_ALL_SLIPPERY
6236 #if USE_NEW_SP_SLIPPERY
6237 /* !!! better use the same properties as for custom elements here !!! */
6238 else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
6239 can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
6241 can_fall_right = FALSE; /* slip down on left side */
6242 can_fall_both = FALSE;
6247 #if USE_NEW_ALL_SLIPPERY
6250 if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
6251 can_fall_right = FALSE; /* slip down on left side */
6253 can_fall_left = !(can_fall_right = RND(2));
6255 can_fall_both = FALSE;
6260 if (game.emulation == EMU_BOULDERDASH ||
6261 element == EL_BD_ROCK || element == EL_BD_DIAMOND)
6262 can_fall_right = FALSE; /* slip down on left side */
6264 can_fall_left = !(can_fall_right = RND(2));
6266 can_fall_both = FALSE;
6272 /* if not determined otherwise, prefer left side for slipping down */
6273 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
6274 started_moving = TRUE;
6278 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
6280 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
6283 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
6284 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
6285 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
6286 int belt_dir = game.belt_dir[belt_nr];
6288 if ((belt_dir == MV_LEFT && left_is_free) ||
6289 (belt_dir == MV_RIGHT && right_is_free))
6291 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
6293 InitMovingField(x, y, belt_dir);
6294 started_moving = TRUE;
6296 Pushed[x][y] = TRUE;
6297 Pushed[nextx][y] = TRUE;
6299 GfxAction[x][y] = ACTION_DEFAULT;
6303 MovDir[x][y] = 0; /* if element was moving, stop it */
6308 /* not "else if" because of elements that can fall and move (EL_SPRING) */
6310 if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
6312 if (CAN_MOVE(element) && !started_moving)
6315 int move_pattern = element_info[element].move_pattern;
6320 if (MovDir[x][y] == MV_NONE)
6322 printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
6323 x, y, element, element_info[element].token_name);
6324 printf("StartMoving(): This should never happen!\n");
6329 Moving2Blocked(x, y, &newx, &newy);
6331 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
6334 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
6335 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6337 WasJustMoving[x][y] = 0;
6338 CheckCollision[x][y] = 0;
6340 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
6342 if (Feld[x][y] != element) /* element has changed */
6346 if (!MovDelay[x][y]) /* start new movement phase */
6348 /* all objects that can change their move direction after each step
6349 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
6351 if (element != EL_YAMYAM &&
6352 element != EL_DARK_YAMYAM &&
6353 element != EL_PACMAN &&
6354 !(move_pattern & MV_ANY_DIRECTION) &&
6355 move_pattern != MV_TURNING_LEFT &&
6356 move_pattern != MV_TURNING_RIGHT &&
6357 move_pattern != MV_TURNING_LEFT_RIGHT &&
6358 move_pattern != MV_TURNING_RIGHT_LEFT &&
6359 move_pattern != MV_TURNING_RANDOM)
6363 if (MovDelay[x][y] && (element == EL_BUG ||
6364 element == EL_SPACESHIP ||
6365 element == EL_SP_SNIKSNAK ||
6366 element == EL_SP_ELECTRON ||
6367 element == EL_MOLE))
6368 DrawLevelField(x, y);
6372 if (MovDelay[x][y]) /* wait some time before next movement */
6376 if (element == EL_ROBOT ||
6377 element == EL_YAMYAM ||
6378 element == EL_DARK_YAMYAM)
6380 DrawLevelElementAnimationIfNeeded(x, y, element);
6381 PlayLevelSoundAction(x, y, ACTION_WAITING);
6383 else if (element == EL_SP_ELECTRON)
6384 DrawLevelElementAnimationIfNeeded(x, y, element);
6385 else if (element == EL_DRAGON)
6388 int dir = MovDir[x][y];
6389 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
6390 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
6391 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
6392 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
6393 dir == MV_UP ? IMG_FLAMES_1_UP :
6394 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
6395 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
6397 GfxAction[x][y] = ACTION_ATTACKING;
6399 if (IS_PLAYER(x, y))
6400 DrawPlayerField(x, y);
6402 DrawLevelField(x, y);
6404 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
6406 for (i = 1; i <= 3; i++)
6408 int xx = x + i * dx;
6409 int yy = y + i * dy;
6410 int sx = SCREENX(xx);
6411 int sy = SCREENY(yy);
6412 int flame_graphic = graphic + (i - 1);
6414 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
6419 int flamed = MovingOrBlocked2Element(xx, yy);
6423 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
6425 else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
6426 RemoveMovingField(xx, yy);
6428 RemoveField(xx, yy);
6430 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
6433 RemoveMovingField(xx, yy);
6436 ChangeDelay[xx][yy] = 0;
6438 Feld[xx][yy] = EL_FLAMES;
6440 if (IN_SCR_FIELD(sx, sy))
6442 DrawLevelFieldCrumbledSand(xx, yy);
6443 DrawGraphic(sx, sy, flame_graphic, frame);
6448 if (Feld[xx][yy] == EL_FLAMES)
6449 Feld[xx][yy] = EL_EMPTY;
6450 DrawLevelField(xx, yy);
6455 if (MovDelay[x][y]) /* element still has to wait some time */
6457 PlayLevelSoundAction(x, y, ACTION_WAITING);
6463 /* now make next step */
6465 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
6467 if (DONT_COLLIDE_WITH(element) &&
6468 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
6469 !PLAYER_ENEMY_PROTECTED(newx, newy))
6471 TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
6476 else if (CAN_MOVE_INTO_ACID(element) &&
6477 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
6478 !IS_MV_DIAGONAL(MovDir[x][y]) &&
6479 (MovDir[x][y] == MV_DOWN ||
6480 game.engine_version >= VERSION_IDENT(3,1,0,0)))
6482 SplashAcid(newx, newy);
6483 Store[x][y] = EL_ACID;
6485 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
6487 if (Feld[newx][newy] == EL_EXIT_OPEN ||
6488 Feld[newx][newy] == EL_EM_EXIT_OPEN ||
6489 Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
6490 Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
6493 DrawLevelField(x, y);
6495 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
6496 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
6497 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
6499 local_player->friends_still_needed--;
6500 if (!local_player->friends_still_needed &&
6501 !local_player->GameOver && AllPlayersGone)
6502 PlayerWins(local_player);
6506 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
6508 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
6509 DrawLevelField(newx, newy);
6511 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
6513 else if (!IS_FREE(newx, newy))
6515 GfxAction[x][y] = ACTION_WAITING;
6517 if (IS_PLAYER(x, y))
6518 DrawPlayerField(x, y);
6520 DrawLevelField(x, y);
6525 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
6527 if (IS_FOOD_PIG(Feld[newx][newy]))
6529 if (IS_MOVING(newx, newy))
6530 RemoveMovingField(newx, newy);
6533 Feld[newx][newy] = EL_EMPTY;
6534 DrawLevelField(newx, newy);
6537 PlayLevelSound(x, y, SND_PIG_DIGGING);
6539 else if (!IS_FREE(newx, newy))
6541 if (IS_PLAYER(x, y))
6542 DrawPlayerField(x, y);
6544 DrawLevelField(x, y);
6549 else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
6551 if (Store[x][y] != EL_EMPTY)
6553 boolean can_clone = FALSE;
6556 /* check if element to clone is still there */
6557 for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
6559 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
6567 /* cannot clone or target field not free anymore -- do not clone */
6568 if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6569 Store[x][y] = EL_EMPTY;
6572 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6574 if (IS_MV_DIAGONAL(MovDir[x][y]))
6576 int diagonal_move_dir = MovDir[x][y];
6577 int stored = Store[x][y];
6578 int change_delay = 8;
6581 /* android is moving diagonally */
6583 CreateField(x, y, EL_DIAGONAL_SHRINKING);
6585 Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
6586 GfxElement[x][y] = EL_EMC_ANDROID;
6587 GfxAction[x][y] = ACTION_SHRINKING;
6588 GfxDir[x][y] = diagonal_move_dir;
6589 ChangeDelay[x][y] = change_delay;
6591 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
6594 DrawLevelGraphicAnimation(x, y, graphic);
6595 PlayLevelSoundAction(x, y, ACTION_SHRINKING);
6597 if (Feld[newx][newy] == EL_ACID)
6599 SplashAcid(newx, newy);
6604 CreateField(newx, newy, EL_DIAGONAL_GROWING);
6606 Store[newx][newy] = EL_EMC_ANDROID;
6607 GfxElement[newx][newy] = EL_EMC_ANDROID;
6608 GfxAction[newx][newy] = ACTION_GROWING;
6609 GfxDir[newx][newy] = diagonal_move_dir;
6610 ChangeDelay[newx][newy] = change_delay;
6612 graphic = el_act_dir2img(GfxElement[newx][newy],
6613 GfxAction[newx][newy], GfxDir[newx][newy]);
6615 DrawLevelGraphicAnimation(newx, newy, graphic);
6616 PlayLevelSoundAction(newx, newy, ACTION_GROWING);
6622 Feld[newx][newy] = EL_EMPTY;
6623 DrawLevelField(newx, newy);
6625 PlayLevelSoundAction(x, y, ACTION_DIGGING);
6628 else if (!IS_FREE(newx, newy))
6631 if (IS_PLAYER(x, y))
6632 DrawPlayerField(x, y);
6634 DrawLevelField(x, y);
6640 else if (IS_CUSTOM_ELEMENT(element) &&
6641 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6643 int new_element = Feld[newx][newy];
6645 if (!IS_FREE(newx, newy))
6647 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
6648 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
6651 /* no element can dig solid indestructible elements */
6652 if (IS_INDESTRUCTIBLE(new_element) &&
6653 !IS_DIGGABLE(new_element) &&
6654 !IS_COLLECTIBLE(new_element))
6657 if (AmoebaNr[newx][newy] &&
6658 (new_element == EL_AMOEBA_FULL ||
6659 new_element == EL_BD_AMOEBA ||
6660 new_element == EL_AMOEBA_GROWING))
6662 AmoebaCnt[AmoebaNr[newx][newy]]--;
6663 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6666 if (IS_MOVING(newx, newy))
6667 RemoveMovingField(newx, newy);
6670 RemoveField(newx, newy);
6671 DrawLevelField(newx, newy);
6674 /* if digged element was about to explode, prevent the explosion */
6675 ExplodeField[newx][newy] = EX_TYPE_NONE;
6677 PlayLevelSoundAction(x, y, action);
6680 Store[newx][newy] = EL_EMPTY;
6682 /* this makes it possible to leave the removed element again */
6683 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
6684 Store[newx][newy] = new_element;
6686 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
6688 int move_leave_element = element_info[element].move_leave_element;
6690 /* this makes it possible to leave the removed element again */
6691 Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
6692 new_element : move_leave_element);
6696 if (move_pattern & MV_MAZE_RUNNER_STYLE)
6698 RunnerVisit[x][y] = FrameCounter;
6699 PlayerVisit[x][y] /= 8; /* expire player visit path */
6702 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
6704 if (!IS_FREE(newx, newy))
6706 if (IS_PLAYER(x, y))
6707 DrawPlayerField(x, y);
6709 DrawLevelField(x, y);
6715 boolean wanna_flame = !RND(10);
6716 int dx = newx - x, dy = newy - y;
6717 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
6718 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
6719 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
6720 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
6721 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
6722 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
6725 IS_CLASSIC_ENEMY(element1) ||
6726 IS_CLASSIC_ENEMY(element2)) &&
6727 element1 != EL_DRAGON && element2 != EL_DRAGON &&
6728 element1 != EL_FLAMES && element2 != EL_FLAMES)
6730 ResetGfxAnimation(x, y);
6731 GfxAction[x][y] = ACTION_ATTACKING;
6733 if (IS_PLAYER(x, y))
6734 DrawPlayerField(x, y);
6736 DrawLevelField(x, y);
6738 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
6740 MovDelay[x][y] = 50;
6744 RemoveField(newx, newy);
6746 Feld[newx][newy] = EL_FLAMES;
6747 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
6750 RemoveField(newx1, newy1);
6752 Feld[newx1][newy1] = EL_FLAMES;
6754 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
6757 RemoveField(newx2, newy2);
6759 Feld[newx2][newy2] = EL_FLAMES;
6766 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6767 Feld[newx][newy] == EL_DIAMOND)
6769 if (IS_MOVING(newx, newy))
6770 RemoveMovingField(newx, newy);
6773 Feld[newx][newy] = EL_EMPTY;
6774 DrawLevelField(newx, newy);
6777 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
6779 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6780 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
6782 if (AmoebaNr[newx][newy])
6784 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6785 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
6786 Feld[newx][newy] == EL_BD_AMOEBA)
6787 AmoebaCnt[AmoebaNr[newx][newy]]--;
6792 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
6794 RemoveMovingField(newx, newy);
6797 if (IS_MOVING(newx, newy))
6799 RemoveMovingField(newx, newy);
6804 Feld[newx][newy] = EL_EMPTY;
6805 DrawLevelField(newx, newy);
6808 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
6810 else if ((element == EL_PACMAN || element == EL_MOLE)
6811 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
6813 if (AmoebaNr[newx][newy])
6815 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6816 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
6817 Feld[newx][newy] == EL_BD_AMOEBA)
6818 AmoebaCnt[AmoebaNr[newx][newy]]--;
6821 if (element == EL_MOLE)
6823 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
6824 PlayLevelSound(x, y, SND_MOLE_DIGGING);
6826 ResetGfxAnimation(x, y);
6827 GfxAction[x][y] = ACTION_DIGGING;
6828 DrawLevelField(x, y);
6830 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
6832 return; /* wait for shrinking amoeba */
6834 else /* element == EL_PACMAN */
6836 Feld[newx][newy] = EL_EMPTY;
6837 DrawLevelField(newx, newy);
6838 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
6841 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
6842 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
6843 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
6845 /* wait for shrinking amoeba to completely disappear */
6848 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
6850 /* object was running against a wall */
6855 /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
6856 if (move_pattern & MV_ANY_DIRECTION &&
6857 move_pattern == MovDir[x][y])
6859 int blocking_element =
6860 (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
6862 CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
6865 element = Feld[x][y]; /* element might have changed */
6869 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
6870 DrawLevelElementAnimation(x, y, element);
6872 if (DONT_TOUCH(element))
6873 TestIfBadThingTouchesPlayer(x, y);
6878 InitMovingField(x, y, MovDir[x][y]);
6880 PlayLevelSoundAction(x, y, ACTION_MOVING);
6884 ContinueMoving(x, y);
6887 void ContinueMoving(int x, int y)
6889 int element = Feld[x][y];
6890 struct ElementInfo *ei = &element_info[element];
6891 int direction = MovDir[x][y];
6892 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
6893 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
6894 int newx = x + dx, newy = y + dy;
6895 int stored = Store[x][y];
6896 int stored_new = Store[newx][newy];
6897 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
6898 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
6899 boolean last_line = (newy == lev_fieldy - 1);
6901 MovPos[x][y] += getElementMoveStepsize(x, y);
6903 if (pushed_by_player) /* special case: moving object pushed by player */
6904 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
6906 if (ABS(MovPos[x][y]) < TILEX)
6908 DrawLevelField(x, y);
6910 return; /* element is still moving */
6913 /* element reached destination field */
6915 Feld[x][y] = EL_EMPTY;
6916 Feld[newx][newy] = element;
6917 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
6919 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
6921 element = Feld[newx][newy] = EL_ACID;
6923 else if (element == EL_MOLE)
6925 Feld[x][y] = EL_SAND;
6927 DrawLevelFieldCrumbledSandNeighbours(x, y);
6929 else if (element == EL_QUICKSAND_FILLING)
6931 element = Feld[newx][newy] = get_next_element(element);
6932 Store[newx][newy] = Store[x][y];
6934 else if (element == EL_QUICKSAND_EMPTYING)
6936 Feld[x][y] = get_next_element(element);
6937 element = Feld[newx][newy] = Store[x][y];
6939 else if (element == EL_QUICKSAND_FAST_FILLING)
6941 element = Feld[newx][newy] = get_next_element(element);
6942 Store[newx][newy] = Store[x][y];
6944 else if (element == EL_QUICKSAND_FAST_EMPTYING)
6946 Feld[x][y] = get_next_element(element);
6947 element = Feld[newx][newy] = Store[x][y];
6949 else if (element == EL_MAGIC_WALL_FILLING)
6951 element = Feld[newx][newy] = get_next_element(element);
6952 if (!game.magic_wall_active)
6953 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
6954 Store[newx][newy] = Store[x][y];
6956 else if (element == EL_MAGIC_WALL_EMPTYING)
6958 Feld[x][y] = get_next_element(element);
6959 if (!game.magic_wall_active)
6960 Feld[x][y] = EL_MAGIC_WALL_DEAD;
6961 element = Feld[newx][newy] = Store[x][y];
6963 #if USE_NEW_CUSTOM_VALUE
6964 InitField(newx, newy, FALSE);
6967 else if (element == EL_BD_MAGIC_WALL_FILLING)
6969 element = Feld[newx][newy] = get_next_element(element);
6970 if (!game.magic_wall_active)
6971 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
6972 Store[newx][newy] = Store[x][y];
6974 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
6976 Feld[x][y] = get_next_element(element);
6977 if (!game.magic_wall_active)
6978 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
6979 element = Feld[newx][newy] = Store[x][y];
6981 #if USE_NEW_CUSTOM_VALUE
6982 InitField(newx, newy, FALSE);
6985 else if (element == EL_DC_MAGIC_WALL_FILLING)
6987 element = Feld[newx][newy] = get_next_element(element);
6988 if (!game.magic_wall_active)
6989 element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
6990 Store[newx][newy] = Store[x][y];
6992 else if (element == EL_DC_MAGIC_WALL_EMPTYING)
6994 Feld[x][y] = get_next_element(element);
6995 if (!game.magic_wall_active)
6996 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
6997 element = Feld[newx][newy] = Store[x][y];
6999 #if USE_NEW_CUSTOM_VALUE
7000 InitField(newx, newy, FALSE);
7003 else if (element == EL_AMOEBA_DROPPING)
7005 Feld[x][y] = get_next_element(element);
7006 element = Feld[newx][newy] = Store[x][y];
7008 else if (element == EL_SOKOBAN_OBJECT)
7011 Feld[x][y] = Back[x][y];
7013 if (Back[newx][newy])
7014 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
7016 Back[x][y] = Back[newx][newy] = 0;
7019 Store[x][y] = EL_EMPTY;
7024 MovDelay[newx][newy] = 0;
7026 if (CAN_CHANGE_OR_HAS_ACTION(element))
7028 /* copy element change control values to new field */
7029 ChangeDelay[newx][newy] = ChangeDelay[x][y];
7030 ChangePage[newx][newy] = ChangePage[x][y];
7031 ChangeCount[newx][newy] = ChangeCount[x][y];
7032 ChangeEvent[newx][newy] = ChangeEvent[x][y];
7035 #if USE_NEW_CUSTOM_VALUE
7036 CustomValue[newx][newy] = CustomValue[x][y];
7039 ChangeDelay[x][y] = 0;
7040 ChangePage[x][y] = -1;
7041 ChangeCount[x][y] = 0;
7042 ChangeEvent[x][y] = -1;
7044 #if USE_NEW_CUSTOM_VALUE
7045 CustomValue[x][y] = 0;
7048 /* copy animation control values to new field */
7049 GfxFrame[newx][newy] = GfxFrame[x][y];
7050 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
7051 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
7052 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
7054 Pushed[x][y] = Pushed[newx][newy] = FALSE;
7056 /* some elements can leave other elements behind after moving */
7058 if (ei->move_leave_element != EL_EMPTY &&
7059 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
7060 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
7062 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
7063 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
7064 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
7067 int move_leave_element = ei->move_leave_element;
7071 /* this makes it possible to leave the removed element again */
7072 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
7073 move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
7075 /* this makes it possible to leave the removed element again */
7076 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
7077 move_leave_element = stored;
7080 /* this makes it possible to leave the removed element again */
7081 if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
7082 ei->move_leave_element == EL_TRIGGER_ELEMENT)
7083 move_leave_element = stored;
7086 Feld[x][y] = move_leave_element;
7088 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
7089 MovDir[x][y] = direction;
7091 InitField(x, y, FALSE);
7093 if (GFX_CRUMBLED(Feld[x][y]))
7094 DrawLevelFieldCrumbledSandNeighbours(x, y);
7096 if (ELEM_IS_PLAYER(move_leave_element))
7097 RelocatePlayer(x, y, move_leave_element);
7100 /* do this after checking for left-behind element */
7101 ResetGfxAnimation(x, y); /* reset animation values for old field */
7103 if (!CAN_MOVE(element) ||
7104 (CAN_FALL(element) && direction == MV_DOWN &&
7105 (element == EL_SPRING ||
7106 element_info[element].move_pattern == MV_WHEN_PUSHED ||
7107 element_info[element].move_pattern == MV_WHEN_DROPPED)))
7108 GfxDir[x][y] = MovDir[newx][newy] = 0;
7110 DrawLevelField(x, y);
7111 DrawLevelField(newx, newy);
7113 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
7115 /* prevent pushed element from moving on in pushed direction */
7116 if (pushed_by_player && CAN_MOVE(element) &&
7117 element_info[element].move_pattern & MV_ANY_DIRECTION &&
7118 !(element_info[element].move_pattern & direction))
7119 TurnRound(newx, newy);
7121 /* prevent elements on conveyor belt from moving on in last direction */
7122 if (pushed_by_conveyor && CAN_FALL(element) &&
7123 direction & MV_HORIZONTAL)
7124 MovDir[newx][newy] = 0;
7126 if (!pushed_by_player)
7128 int nextx = newx + dx, nexty = newy + dy;
7129 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
7131 WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
7133 if (CAN_FALL(element) && direction == MV_DOWN)
7134 WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
7136 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
7137 CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
7139 #if USE_FIX_IMPACT_COLLISION
7140 if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
7141 CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
7145 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
7147 TestIfBadThingTouchesPlayer(newx, newy);
7148 TestIfBadThingTouchesFriend(newx, newy);
7150 if (!IS_CUSTOM_ELEMENT(element))
7151 TestIfBadThingTouchesOtherBadThing(newx, newy);
7153 else if (element == EL_PENGUIN)
7154 TestIfFriendTouchesBadThing(newx, newy);
7156 /* give the player one last chance (one more frame) to move away */
7157 if (CAN_FALL(element) && direction == MV_DOWN &&
7158 (last_line || (!IS_FREE(x, newy + 1) &&
7159 (!IS_PLAYER(x, newy + 1) ||
7160 game.engine_version < VERSION_IDENT(3,1,1,0)))))
7163 if (pushed_by_player && !game.use_change_when_pushing_bug)
7165 int push_side = MV_DIR_OPPOSITE(direction);
7166 struct PlayerInfo *player = PLAYERINFO(x, y);
7168 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
7169 player->index_bit, push_side);
7170 CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
7171 player->index_bit, push_side);
7174 if (element == EL_EMC_ANDROID && pushed_by_player) /* make another move */
7175 MovDelay[newx][newy] = 1;
7177 CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
7179 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
7182 if (ChangePage[newx][newy] != -1) /* delayed change */
7184 int page = ChangePage[newx][newy];
7185 struct ElementChangeInfo *change = &ei->change_page[page];
7187 ChangePage[newx][newy] = -1;
7189 if (change->can_change)
7191 if (ChangeElement(newx, newy, element, page))
7193 if (change->post_change_function)
7194 change->post_change_function(newx, newy);
7198 if (change->has_action)
7199 ExecuteCustomElementAction(newx, newy, element, page);
7203 TestIfElementHitsCustomElement(newx, newy, direction);
7204 TestIfPlayerTouchesCustomElement(newx, newy);
7205 TestIfElementTouchesCustomElement(newx, newy);
7207 if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
7208 IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
7209 CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
7210 MV_DIR_OPPOSITE(direction));
7213 int AmoebeNachbarNr(int ax, int ay)
7216 int element = Feld[ax][ay];
7218 static int xy[4][2] =
7226 for (i = 0; i < NUM_DIRECTIONS; i++)
7228 int x = ax + xy[i][0];
7229 int y = ay + xy[i][1];
7231 if (!IN_LEV_FIELD(x, y))
7234 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
7235 group_nr = AmoebaNr[x][y];
7241 void AmoebenVereinigen(int ax, int ay)
7243 int i, x, y, xx, yy;
7244 int new_group_nr = AmoebaNr[ax][ay];
7245 static int xy[4][2] =
7253 if (new_group_nr == 0)
7256 for (i = 0; i < NUM_DIRECTIONS; i++)
7261 if (!IN_LEV_FIELD(x, y))
7264 if ((Feld[x][y] == EL_AMOEBA_FULL ||
7265 Feld[x][y] == EL_BD_AMOEBA ||
7266 Feld[x][y] == EL_AMOEBA_DEAD) &&
7267 AmoebaNr[x][y] != new_group_nr)
7269 int old_group_nr = AmoebaNr[x][y];
7271 if (old_group_nr == 0)
7274 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
7275 AmoebaCnt[old_group_nr] = 0;
7276 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
7277 AmoebaCnt2[old_group_nr] = 0;
7279 SCAN_PLAYFIELD(xx, yy)
7281 if (AmoebaNr[xx][yy] == old_group_nr)
7282 AmoebaNr[xx][yy] = new_group_nr;
7288 void AmoebeUmwandeln(int ax, int ay)
7292 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
7294 int group_nr = AmoebaNr[ax][ay];
7299 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
7300 printf("AmoebeUmwandeln(): This should never happen!\n");
7305 SCAN_PLAYFIELD(x, y)
7307 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
7310 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
7314 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
7315 SND_AMOEBA_TURNING_TO_GEM :
7316 SND_AMOEBA_TURNING_TO_ROCK));
7321 static int xy[4][2] =
7329 for (i = 0; i < NUM_DIRECTIONS; i++)
7334 if (!IN_LEV_FIELD(x, y))
7337 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
7339 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
7340 SND_AMOEBA_TURNING_TO_GEM :
7341 SND_AMOEBA_TURNING_TO_ROCK));
7348 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
7351 int group_nr = AmoebaNr[ax][ay];
7352 boolean done = FALSE;
7357 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
7358 printf("AmoebeUmwandelnBD(): This should never happen!\n");
7363 SCAN_PLAYFIELD(x, y)
7365 if (AmoebaNr[x][y] == group_nr &&
7366 (Feld[x][y] == EL_AMOEBA_DEAD ||
7367 Feld[x][y] == EL_BD_AMOEBA ||
7368 Feld[x][y] == EL_AMOEBA_GROWING))
7371 Feld[x][y] = new_element;
7372 InitField(x, y, FALSE);
7373 DrawLevelField(x, y);
7379 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
7380 SND_BD_AMOEBA_TURNING_TO_ROCK :
7381 SND_BD_AMOEBA_TURNING_TO_GEM));
7384 void AmoebeWaechst(int x, int y)
7386 static unsigned long sound_delay = 0;
7387 static unsigned long sound_delay_value = 0;
7389 if (!MovDelay[x][y]) /* start new growing cycle */
7393 if (DelayReached(&sound_delay, sound_delay_value))
7395 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
7396 sound_delay_value = 30;
7400 if (MovDelay[x][y]) /* wait some time before growing bigger */
7403 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7405 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
7406 6 - MovDelay[x][y]);
7408 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
7411 if (!MovDelay[x][y])
7413 Feld[x][y] = Store[x][y];
7415 DrawLevelField(x, y);
7420 void AmoebaDisappearing(int x, int y)
7422 static unsigned long sound_delay = 0;
7423 static unsigned long sound_delay_value = 0;
7425 if (!MovDelay[x][y]) /* start new shrinking cycle */
7429 if (DelayReached(&sound_delay, sound_delay_value))
7430 sound_delay_value = 30;
7433 if (MovDelay[x][y]) /* wait some time before shrinking */
7436 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7438 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
7439 6 - MovDelay[x][y]);
7441 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
7444 if (!MovDelay[x][y])
7446 Feld[x][y] = EL_EMPTY;
7447 DrawLevelField(x, y);
7449 /* don't let mole enter this field in this cycle;
7450 (give priority to objects falling to this field from above) */
7456 void AmoebeAbleger(int ax, int ay)
7459 int element = Feld[ax][ay];
7460 int graphic = el2img(element);
7461 int newax = ax, neway = ay;
7462 boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
7463 static int xy[4][2] =
7471 if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
7473 Feld[ax][ay] = EL_AMOEBA_DEAD;
7474 DrawLevelField(ax, ay);
7478 if (IS_ANIMATED(graphic))
7479 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7481 if (!MovDelay[ax][ay]) /* start making new amoeba field */
7482 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
7484 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
7487 if (MovDelay[ax][ay])
7491 if (can_drop) /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
7494 int x = ax + xy[start][0];
7495 int y = ay + xy[start][1];
7497 if (!IN_LEV_FIELD(x, y))
7500 if (IS_FREE(x, y) ||
7501 CAN_GROW_INTO(Feld[x][y]) ||
7502 Feld[x][y] == EL_QUICKSAND_EMPTY ||
7503 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
7509 if (newax == ax && neway == ay)
7512 else /* normal or "filled" (BD style) amoeba */
7515 boolean waiting_for_player = FALSE;
7517 for (i = 0; i < NUM_DIRECTIONS; i++)
7519 int j = (start + i) % 4;
7520 int x = ax + xy[j][0];
7521 int y = ay + xy[j][1];
7523 if (!IN_LEV_FIELD(x, y))
7526 if (IS_FREE(x, y) ||
7527 CAN_GROW_INTO(Feld[x][y]) ||
7528 Feld[x][y] == EL_QUICKSAND_EMPTY ||
7529 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
7535 else if (IS_PLAYER(x, y))
7536 waiting_for_player = TRUE;
7539 if (newax == ax && neway == ay) /* amoeba cannot grow */
7541 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
7543 Feld[ax][ay] = EL_AMOEBA_DEAD;
7544 DrawLevelField(ax, ay);
7545 AmoebaCnt[AmoebaNr[ax][ay]]--;
7547 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
7549 if (element == EL_AMOEBA_FULL)
7550 AmoebeUmwandeln(ax, ay);
7551 else if (element == EL_BD_AMOEBA)
7552 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
7557 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
7559 /* amoeba gets larger by growing in some direction */
7561 int new_group_nr = AmoebaNr[ax][ay];
7564 if (new_group_nr == 0)
7566 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
7567 printf("AmoebeAbleger(): This should never happen!\n");
7572 AmoebaNr[newax][neway] = new_group_nr;
7573 AmoebaCnt[new_group_nr]++;
7574 AmoebaCnt2[new_group_nr]++;
7576 /* if amoeba touches other amoeba(s) after growing, unify them */
7577 AmoebenVereinigen(newax, neway);
7579 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
7581 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
7587 if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
7588 (neway == lev_fieldy - 1 && newax != ax))
7590 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
7591 Store[newax][neway] = element;
7593 else if (neway == ay || element == EL_EMC_DRIPPER)
7595 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
7597 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
7601 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
7602 Feld[ax][ay] = EL_AMOEBA_DROPPING;
7603 Store[ax][ay] = EL_AMOEBA_DROP;
7604 ContinueMoving(ax, ay);
7608 DrawLevelField(newax, neway);
7611 void Life(int ax, int ay)
7615 int element = Feld[ax][ay];
7616 int graphic = el2img(element);
7617 int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
7619 boolean changed = FALSE;
7621 if (IS_ANIMATED(graphic))
7622 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7627 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
7628 MovDelay[ax][ay] = life_time;
7630 if (MovDelay[ax][ay]) /* wait some time before next cycle */
7633 if (MovDelay[ax][ay])
7637 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
7639 int xx = ax+x1, yy = ay+y1;
7642 if (!IN_LEV_FIELD(xx, yy))
7645 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
7647 int x = xx+x2, y = yy+y2;
7649 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
7652 if (((Feld[x][y] == element ||
7653 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
7655 (IS_FREE(x, y) && Stop[x][y]))
7659 if (xx == ax && yy == ay) /* field in the middle */
7661 if (nachbarn < life_parameter[0] ||
7662 nachbarn > life_parameter[1])
7664 Feld[xx][yy] = EL_EMPTY;
7666 DrawLevelField(xx, yy);
7667 Stop[xx][yy] = TRUE;
7671 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
7672 { /* free border field */
7673 if (nachbarn >= life_parameter[2] &&
7674 nachbarn <= life_parameter[3])
7676 Feld[xx][yy] = element;
7677 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
7679 DrawLevelField(xx, yy);
7680 Stop[xx][yy] = TRUE;
7687 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
7688 SND_GAME_OF_LIFE_GROWING);
7691 static void InitRobotWheel(int x, int y)
7693 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
7696 static void RunRobotWheel(int x, int y)
7698 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
7701 static void StopRobotWheel(int x, int y)
7703 if (ZX == x && ZY == y)
7707 static void InitTimegateWheel(int x, int y)
7709 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
7712 static void RunTimegateWheel(int x, int y)
7714 PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
7717 static void InitMagicBallDelay(int x, int y)
7720 ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
7722 ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
7726 static void ActivateMagicBall(int bx, int by)
7730 if (level.ball_random)
7732 int pos_border = RND(8); /* select one of the eight border elements */
7733 int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
7734 int xx = pos_content % 3;
7735 int yy = pos_content / 3;
7740 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
7741 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
7745 for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
7747 int xx = x - bx + 1;
7748 int yy = y - by + 1;
7750 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
7751 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
7755 game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
7758 void CheckExit(int x, int y)
7760 if (local_player->gems_still_needed > 0 ||
7761 local_player->sokobanfields_still_needed > 0 ||
7762 local_player->lights_still_needed > 0)
7764 int element = Feld[x][y];
7765 int graphic = el2img(element);
7767 if (IS_ANIMATED(graphic))
7768 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7773 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7776 Feld[x][y] = EL_EXIT_OPENING;
7778 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
7781 void CheckExitEM(int x, int y)
7783 if (local_player->gems_still_needed > 0 ||
7784 local_player->sokobanfields_still_needed > 0 ||
7785 local_player->lights_still_needed > 0)
7787 int element = Feld[x][y];
7788 int graphic = el2img(element);
7790 if (IS_ANIMATED(graphic))
7791 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7796 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7799 Feld[x][y] = EL_EM_EXIT_OPENING;
7801 PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
7804 void CheckExitSteel(int x, int y)
7806 if (local_player->gems_still_needed > 0 ||
7807 local_player->sokobanfields_still_needed > 0 ||
7808 local_player->lights_still_needed > 0)
7810 int element = Feld[x][y];
7811 int graphic = el2img(element);
7813 if (IS_ANIMATED(graphic))
7814 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7819 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7822 Feld[x][y] = EL_STEEL_EXIT_OPENING;
7824 PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
7827 void CheckExitSteelEM(int x, int y)
7829 if (local_player->gems_still_needed > 0 ||
7830 local_player->sokobanfields_still_needed > 0 ||
7831 local_player->lights_still_needed > 0)
7833 int element = Feld[x][y];
7834 int graphic = el2img(element);
7836 if (IS_ANIMATED(graphic))
7837 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7842 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7845 Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
7847 PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
7850 void CheckExitSP(int x, int y)
7852 if (local_player->gems_still_needed > 0)
7854 int element = Feld[x][y];
7855 int graphic = el2img(element);
7857 if (IS_ANIMATED(graphic))
7858 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7863 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7866 Feld[x][y] = EL_SP_EXIT_OPENING;
7868 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
7871 static void CloseAllOpenTimegates()
7875 SCAN_PLAYFIELD(x, y)
7877 int element = Feld[x][y];
7879 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
7881 Feld[x][y] = EL_TIMEGATE_CLOSING;
7883 PlayLevelSoundAction(x, y, ACTION_CLOSING);
7888 void DrawTwinkleOnField(int x, int y)
7890 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
7893 if (Feld[x][y] == EL_BD_DIAMOND)
7896 if (MovDelay[x][y] == 0) /* next animation frame */
7897 MovDelay[x][y] = 11 * !GetSimpleRandom(500);
7899 if (MovDelay[x][y] != 0) /* wait some time before next frame */
7903 if (setup.direct_draw && MovDelay[x][y])
7904 SetDrawtoField(DRAW_BUFFERED);
7906 DrawLevelElementAnimation(x, y, Feld[x][y]);
7908 if (MovDelay[x][y] != 0)
7910 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
7911 10 - MovDelay[x][y]);
7913 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
7915 if (setup.direct_draw)
7919 dest_x = FX + SCREENX(x) * TILEX;
7920 dest_y = FY + SCREENY(y) * TILEY;
7922 BlitBitmap(drawto_field, window,
7923 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
7924 SetDrawtoField(DRAW_DIRECT);
7930 void MauerWaechst(int x, int y)
7934 if (!MovDelay[x][y]) /* next animation frame */
7935 MovDelay[x][y] = 3 * delay;
7937 if (MovDelay[x][y]) /* wait some time before next frame */
7941 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7943 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
7944 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
7946 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
7949 if (!MovDelay[x][y])
7951 if (MovDir[x][y] == MV_LEFT)
7953 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
7954 DrawLevelField(x - 1, y);
7956 else if (MovDir[x][y] == MV_RIGHT)
7958 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
7959 DrawLevelField(x + 1, y);
7961 else if (MovDir[x][y] == MV_UP)
7963 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
7964 DrawLevelField(x, y - 1);
7968 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
7969 DrawLevelField(x, y + 1);
7972 Feld[x][y] = Store[x][y];
7974 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7975 DrawLevelField(x, y);
7980 void MauerAbleger(int ax, int ay)
7982 int element = Feld[ax][ay];
7983 int graphic = el2img(element);
7984 boolean oben_frei = FALSE, unten_frei = FALSE;
7985 boolean links_frei = FALSE, rechts_frei = FALSE;
7986 boolean oben_massiv = FALSE, unten_massiv = FALSE;
7987 boolean links_massiv = FALSE, rechts_massiv = FALSE;
7988 boolean new_wall = FALSE;
7990 if (IS_ANIMATED(graphic))
7991 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7993 if (!MovDelay[ax][ay]) /* start building new wall */
7994 MovDelay[ax][ay] = 6;
7996 if (MovDelay[ax][ay]) /* wait some time before building new wall */
7999 if (MovDelay[ax][ay])
8003 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
8005 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
8007 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
8009 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
8012 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
8013 element == EL_EXPANDABLE_WALL_ANY)
8017 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
8018 Store[ax][ay-1] = element;
8019 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
8020 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
8021 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
8022 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
8027 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
8028 Store[ax][ay+1] = element;
8029 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
8030 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
8031 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
8032 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
8037 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
8038 element == EL_EXPANDABLE_WALL_ANY ||
8039 element == EL_EXPANDABLE_WALL ||
8040 element == EL_BD_EXPANDABLE_WALL)
8044 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
8045 Store[ax-1][ay] = element;
8046 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
8047 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
8048 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
8049 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
8055 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
8056 Store[ax+1][ay] = element;
8057 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
8058 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
8059 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
8060 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
8065 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
8066 DrawLevelField(ax, ay);
8068 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
8070 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
8071 unten_massiv = TRUE;
8072 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
8073 links_massiv = TRUE;
8074 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
8075 rechts_massiv = TRUE;
8077 if (((oben_massiv && unten_massiv) ||
8078 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
8079 element == EL_EXPANDABLE_WALL) &&
8080 ((links_massiv && rechts_massiv) ||
8081 element == EL_EXPANDABLE_WALL_VERTICAL))
8082 Feld[ax][ay] = EL_WALL;
8085 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
8088 void MauerAblegerStahl(int ax, int ay)
8090 int element = Feld[ax][ay];
8091 int graphic = el2img(element);
8092 boolean oben_frei = FALSE, unten_frei = FALSE;
8093 boolean links_frei = FALSE, rechts_frei = FALSE;
8094 boolean oben_massiv = FALSE, unten_massiv = FALSE;
8095 boolean links_massiv = FALSE, rechts_massiv = FALSE;
8096 boolean new_wall = FALSE;
8098 if (IS_ANIMATED(graphic))
8099 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8101 if (!MovDelay[ax][ay]) /* start building new wall */
8102 MovDelay[ax][ay] = 6;
8104 if (MovDelay[ax][ay]) /* wait some time before building new wall */
8107 if (MovDelay[ax][ay])
8111 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
8113 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
8115 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
8117 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
8120 if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
8121 element == EL_EXPANDABLE_STEELWALL_ANY)
8125 Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
8126 Store[ax][ay-1] = element;
8127 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
8128 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
8129 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
8130 IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
8135 Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
8136 Store[ax][ay+1] = element;
8137 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
8138 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
8139 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
8140 IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
8145 if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
8146 element == EL_EXPANDABLE_STEELWALL_ANY)
8150 Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
8151 Store[ax-1][ay] = element;
8152 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
8153 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
8154 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
8155 IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
8161 Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
8162 Store[ax+1][ay] = element;
8163 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
8164 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
8165 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
8166 IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
8171 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
8173 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
8174 unten_massiv = TRUE;
8175 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
8176 links_massiv = TRUE;
8177 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
8178 rechts_massiv = TRUE;
8180 if (((oben_massiv && unten_massiv) ||
8181 element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
8182 ((links_massiv && rechts_massiv) ||
8183 element == EL_EXPANDABLE_STEELWALL_VERTICAL))
8184 Feld[ax][ay] = EL_WALL;
8187 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
8190 void CheckForDragon(int x, int y)
8193 boolean dragon_found = FALSE;
8194 static int xy[4][2] =
8202 for (i = 0; i < NUM_DIRECTIONS; i++)
8204 for (j = 0; j < 4; j++)
8206 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
8208 if (IN_LEV_FIELD(xx, yy) &&
8209 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
8211 if (Feld[xx][yy] == EL_DRAGON)
8212 dragon_found = TRUE;
8221 for (i = 0; i < NUM_DIRECTIONS; i++)
8223 for (j = 0; j < 3; j++)
8225 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
8227 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
8229 Feld[xx][yy] = EL_EMPTY;
8230 DrawLevelField(xx, yy);
8239 static void InitBuggyBase(int x, int y)
8241 int element = Feld[x][y];
8242 int activating_delay = FRAMES_PER_SECOND / 4;
8245 (element == EL_SP_BUGGY_BASE ?
8246 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
8247 element == EL_SP_BUGGY_BASE_ACTIVATING ?
8249 element == EL_SP_BUGGY_BASE_ACTIVE ?
8250 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
8253 static void WarnBuggyBase(int x, int y)
8256 static int xy[4][2] =
8264 for (i = 0; i < NUM_DIRECTIONS; i++)
8266 int xx = x + xy[i][0];
8267 int yy = y + xy[i][1];
8269 if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
8271 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
8278 static void InitTrap(int x, int y)
8280 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
8283 static void ActivateTrap(int x, int y)
8285 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
8288 static void ChangeActiveTrap(int x, int y)
8290 int graphic = IMG_TRAP_ACTIVE;
8292 /* if new animation frame was drawn, correct crumbled sand border */
8293 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
8294 DrawLevelFieldCrumbledSand(x, y);
8297 static int getSpecialActionElement(int element, int number, int base_element)
8299 return (element != EL_EMPTY ? element :
8300 number != -1 ? base_element + number - 1 :
8304 static int getModifiedActionNumber(int value_old, int operator, int operand,
8305 int value_min, int value_max)
8307 int value_new = (operator == CA_MODE_SET ? operand :
8308 operator == CA_MODE_ADD ? value_old + operand :
8309 operator == CA_MODE_SUBTRACT ? value_old - operand :
8310 operator == CA_MODE_MULTIPLY ? value_old * operand :
8311 operator == CA_MODE_DIVIDE ? value_old / MAX(1, operand) :
8312 operator == CA_MODE_MODULO ? value_old % MAX(1, operand) :
8315 return (value_new < value_min ? value_min :
8316 value_new > value_max ? value_max :
8320 static void ExecuteCustomElementAction(int x, int y, int element, int page)
8322 struct ElementInfo *ei = &element_info[element];
8323 struct ElementChangeInfo *change = &ei->change_page[page];
8324 int target_element = change->target_element;
8325 int action_type = change->action_type;
8326 int action_mode = change->action_mode;
8327 int action_arg = change->action_arg;
8330 if (!change->has_action)
8333 /* ---------- determine action paramater values -------------------------- */
8335 int level_time_value =
8336 (level.time > 0 ? TimeLeft :
8339 int action_arg_element =
8340 (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player :
8341 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
8342 action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element :
8345 int action_arg_direction =
8346 (action_arg >= CA_ARG_DIRECTION_LEFT &&
8347 action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
8348 action_arg == CA_ARG_DIRECTION_TRIGGER ?
8349 change->actual_trigger_side :
8350 action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
8351 MV_DIR_OPPOSITE(change->actual_trigger_side) :
8354 int action_arg_number_min =
8355 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
8358 int action_arg_number_max =
8359 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
8360 action_type == CA_SET_LEVEL_GEMS ? 999 :
8361 action_type == CA_SET_LEVEL_TIME ? 9999 :
8362 action_type == CA_SET_LEVEL_SCORE ? 99999 :
8363 action_type == CA_SET_CE_VALUE ? 9999 :
8364 action_type == CA_SET_CE_SCORE ? 9999 :
8367 int action_arg_number_reset =
8368 (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
8369 action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
8370 action_type == CA_SET_LEVEL_TIME ? level.time :
8371 action_type == CA_SET_LEVEL_SCORE ? 0 :
8372 #if USE_NEW_CUSTOM_VALUE
8373 action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
8375 action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
8377 action_type == CA_SET_CE_SCORE ? 0 :
8380 int action_arg_number =
8381 (action_arg <= CA_ARG_MAX ? action_arg :
8382 action_arg >= CA_ARG_SPEED_NOT_MOVING &&
8383 action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
8384 action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
8385 action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
8386 action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
8387 action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
8388 #if USE_NEW_CUSTOM_VALUE
8389 action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
8391 action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
8393 action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
8394 action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
8395 action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
8396 action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
8397 action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
8398 action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
8399 action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
8400 action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
8401 action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
8402 action_arg == CA_ARG_ELEMENT_NR_TARGET ? change->target_element :
8403 action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
8406 int action_arg_number_old =
8407 (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
8408 action_type == CA_SET_LEVEL_TIME ? TimeLeft :
8409 action_type == CA_SET_LEVEL_SCORE ? local_player->score :
8410 action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
8411 action_type == CA_SET_CE_SCORE ? ei->collect_score :
8414 int action_arg_number_new =
8415 getModifiedActionNumber(action_arg_number_old,
8416 action_mode, action_arg_number,
8417 action_arg_number_min, action_arg_number_max);
8419 int trigger_player_bits =
8420 (change->actual_trigger_player >= EL_PLAYER_1 &&
8421 change->actual_trigger_player <= EL_PLAYER_4 ?
8422 (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
8425 int action_arg_player_bits =
8426 (action_arg >= CA_ARG_PLAYER_1 &&
8427 action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
8428 action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
8431 /* ---------- execute action -------------------------------------------- */
8433 switch (action_type)
8440 /* ---------- level actions ------------------------------------------- */
8442 case CA_RESTART_LEVEL:
8444 game.restart_level = TRUE;
8449 case CA_SHOW_ENVELOPE:
8451 int element = getSpecialActionElement(action_arg_element,
8452 action_arg_number, EL_ENVELOPE_1);
8454 if (IS_ENVELOPE(element))
8455 local_player->show_envelope = element;
8460 case CA_SET_LEVEL_TIME:
8462 if (level.time > 0) /* only modify limited time value */
8464 TimeLeft = action_arg_number_new;
8466 DrawGameValue_Time(TimeLeft);
8468 if (!TimeLeft && setup.time_limit)
8469 for (i = 0; i < MAX_PLAYERS; i++)
8470 KillPlayer(&stored_player[i]);
8476 case CA_SET_LEVEL_SCORE:
8478 local_player->score = action_arg_number_new;
8480 DrawGameValue_Score(local_player->score);
8485 case CA_SET_LEVEL_GEMS:
8487 local_player->gems_still_needed = action_arg_number_new;
8489 DrawGameValue_Emeralds(local_player->gems_still_needed);
8494 #if !USE_PLAYER_GRAVITY
8495 case CA_SET_LEVEL_GRAVITY:
8497 game.gravity = (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
8498 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
8499 action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
8505 case CA_SET_LEVEL_WIND:
8507 game.wind_direction = action_arg_direction;
8512 /* ---------- player actions ------------------------------------------ */
8514 case CA_MOVE_PLAYER:
8516 /* automatically move to the next field in specified direction */
8517 for (i = 0; i < MAX_PLAYERS; i++)
8518 if (trigger_player_bits & (1 << i))
8519 stored_player[i].programmed_action = action_arg_direction;
8524 case CA_EXIT_PLAYER:
8526 for (i = 0; i < MAX_PLAYERS; i++)
8527 if (action_arg_player_bits & (1 << i))
8528 PlayerWins(&stored_player[i]);
8533 case CA_KILL_PLAYER:
8535 for (i = 0; i < MAX_PLAYERS; i++)
8536 if (action_arg_player_bits & (1 << i))
8537 KillPlayer(&stored_player[i]);
8542 case CA_SET_PLAYER_KEYS:
8544 int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
8545 int element = getSpecialActionElement(action_arg_element,
8546 action_arg_number, EL_KEY_1);
8548 if (IS_KEY(element))
8550 for (i = 0; i < MAX_PLAYERS; i++)
8552 if (trigger_player_bits & (1 << i))
8554 stored_player[i].key[KEY_NR(element)] = key_state;
8556 DrawGameDoorValues();
8564 case CA_SET_PLAYER_SPEED:
8566 for (i = 0; i < MAX_PLAYERS; i++)
8568 if (trigger_player_bits & (1 << i))
8570 int move_stepsize = TILEX / stored_player[i].move_delay_value;
8572 if (action_arg == CA_ARG_SPEED_FASTER &&
8573 stored_player[i].cannot_move)
8575 action_arg_number = STEPSIZE_VERY_SLOW;
8577 else if (action_arg == CA_ARG_SPEED_SLOWER ||
8578 action_arg == CA_ARG_SPEED_FASTER)
8580 action_arg_number = 2;
8581 action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
8584 else if (action_arg == CA_ARG_NUMBER_RESET)
8586 action_arg_number = level.initial_player_stepsize[i];
8590 getModifiedActionNumber(move_stepsize,
8593 action_arg_number_min,
8594 action_arg_number_max);
8596 SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
8603 case CA_SET_PLAYER_SHIELD:
8605 for (i = 0; i < MAX_PLAYERS; i++)
8607 if (trigger_player_bits & (1 << i))
8609 if (action_arg == CA_ARG_SHIELD_OFF)
8611 stored_player[i].shield_normal_time_left = 0;
8612 stored_player[i].shield_deadly_time_left = 0;
8614 else if (action_arg == CA_ARG_SHIELD_NORMAL)
8616 stored_player[i].shield_normal_time_left = 999999;
8618 else if (action_arg == CA_ARG_SHIELD_DEADLY)
8620 stored_player[i].shield_normal_time_left = 999999;
8621 stored_player[i].shield_deadly_time_left = 999999;
8629 #if USE_PLAYER_GRAVITY
8630 case CA_SET_PLAYER_GRAVITY:
8632 for (i = 0; i < MAX_PLAYERS; i++)
8634 if (trigger_player_bits & (1 << i))
8636 stored_player[i].gravity =
8637 (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
8638 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
8639 action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
8640 stored_player[i].gravity);
8648 case CA_SET_PLAYER_ARTWORK:
8650 for (i = 0; i < MAX_PLAYERS; i++)
8652 if (trigger_player_bits & (1 << i))
8654 int artwork_element = action_arg_element;
8656 if (action_arg == CA_ARG_ELEMENT_RESET)
8658 (level.use_artwork_element[i] ? level.artwork_element[i] :
8659 stored_player[i].element_nr);
8661 #if USE_GFX_RESET_PLAYER_ARTWORK
8662 if (stored_player[i].artwork_element != artwork_element)
8663 stored_player[i].Frame = 0;
8666 stored_player[i].artwork_element = artwork_element;
8668 SetPlayerWaiting(&stored_player[i], FALSE);
8670 /* set number of special actions for bored and sleeping animation */
8671 stored_player[i].num_special_action_bored =
8672 get_num_special_action(artwork_element,
8673 ACTION_BORING_1, ACTION_BORING_LAST);
8674 stored_player[i].num_special_action_sleeping =
8675 get_num_special_action(artwork_element,
8676 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
8683 /* ---------- CE actions ---------------------------------------------- */
8685 case CA_SET_CE_VALUE:
8687 #if USE_NEW_CUSTOM_VALUE
8688 int last_ce_value = CustomValue[x][y];
8690 CustomValue[x][y] = action_arg_number_new;
8692 if (CustomValue[x][y] != last_ce_value)
8694 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
8695 CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
8697 if (CustomValue[x][y] == 0)
8699 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
8700 CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
8708 case CA_SET_CE_SCORE:
8710 #if USE_NEW_CUSTOM_VALUE
8711 int last_ce_score = ei->collect_score;
8713 ei->collect_score = action_arg_number_new;
8715 if (ei->collect_score != last_ce_score)
8717 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
8718 CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
8720 if (ei->collect_score == 0)
8724 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
8725 CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
8728 This is a very special case that seems to be a mixture between
8729 CheckElementChange() and CheckTriggeredElementChange(): while
8730 the first one only affects single elements that are triggered
8731 directly, the second one affects multiple elements in the playfield
8732 that are triggered indirectly by another element. This is a third
8733 case: Changing the CE score always affects multiple identical CEs,
8734 so every affected CE must be checked, not only the single CE for
8735 which the CE score was changed in the first place (as every instance
8736 of that CE shares the same CE score, and therefore also can change)!
8738 SCAN_PLAYFIELD(xx, yy)
8740 if (Feld[xx][yy] == element)
8741 CheckElementChange(xx, yy, element, EL_UNDEFINED,
8742 CE_SCORE_GETS_ZERO);
8751 /* ---------- engine actions ------------------------------------------ */
8753 case CA_SET_ENGINE_SCAN_MODE:
8755 InitPlayfieldScanMode(action_arg);
8765 static void CreateFieldExt(int x, int y, int element, boolean is_change)
8767 int old_element = Feld[x][y];
8768 int new_element = get_element_from_group_element(element);
8769 int previous_move_direction = MovDir[x][y];
8770 #if USE_NEW_CUSTOM_VALUE
8771 int last_ce_value = CustomValue[x][y];
8773 boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
8774 boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
8775 boolean add_player_onto_element = (new_element_is_player &&
8776 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
8777 /* this breaks SnakeBite when a snake is
8778 halfway through a door that closes */
8779 /* NOW FIXED AT LEVEL INIT IN files.c */
8780 new_element != EL_SOKOBAN_FIELD_PLAYER &&
8782 IS_WALKABLE(old_element));
8785 /* check if element under the player changes from accessible to unaccessible
8786 (needed for special case of dropping element which then changes) */
8787 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
8788 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
8796 if (!add_player_onto_element)
8798 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
8799 RemoveMovingField(x, y);
8803 Feld[x][y] = new_element;
8805 #if !USE_GFX_RESET_GFX_ANIMATION
8806 ResetGfxAnimation(x, y);
8807 ResetRandomAnimationValue(x, y);
8810 if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
8811 MovDir[x][y] = previous_move_direction;
8813 #if USE_NEW_CUSTOM_VALUE
8814 if (element_info[new_element].use_last_ce_value)
8815 CustomValue[x][y] = last_ce_value;
8818 InitField_WithBug1(x, y, FALSE);
8820 new_element = Feld[x][y]; /* element may have changed */
8822 #if USE_GFX_RESET_GFX_ANIMATION
8823 ResetGfxAnimation(x, y);
8824 ResetRandomAnimationValue(x, y);
8827 DrawLevelField(x, y);
8829 if (GFX_CRUMBLED(new_element))
8830 DrawLevelFieldCrumbledSandNeighbours(x, y);
8834 /* check if element under the player changes from accessible to unaccessible
8835 (needed for special case of dropping element which then changes) */
8836 /* (must be checked after creating new element for walkable group elements) */
8837 #if USE_FIX_KILLED_BY_NON_WALKABLE
8838 if (IS_PLAYER(x, y) && !player_explosion_protected &&
8839 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
8846 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
8847 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
8856 /* "ChangeCount" not set yet to allow "entered by player" change one time */
8857 if (new_element_is_player)
8858 RelocatePlayer(x, y, new_element);
8861 ChangeCount[x][y]++; /* count number of changes in the same frame */
8863 TestIfBadThingTouchesPlayer(x, y);
8864 TestIfPlayerTouchesCustomElement(x, y);
8865 TestIfElementTouchesCustomElement(x, y);
8868 static void CreateField(int x, int y, int element)
8870 CreateFieldExt(x, y, element, FALSE);
8873 static void CreateElementFromChange(int x, int y, int element)
8875 element = GET_VALID_RUNTIME_ELEMENT(element);
8877 #if USE_STOP_CHANGED_ELEMENTS
8878 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
8880 int old_element = Feld[x][y];
8882 /* prevent changed element from moving in same engine frame
8883 unless both old and new element can either fall or move */
8884 if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
8885 (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
8890 CreateFieldExt(x, y, element, TRUE);
8893 static boolean ChangeElement(int x, int y, int element, int page)
8895 struct ElementInfo *ei = &element_info[element];
8896 struct ElementChangeInfo *change = &ei->change_page[page];
8897 int ce_value = CustomValue[x][y];
8898 int ce_score = ei->collect_score;
8900 int old_element = Feld[x][y];
8902 /* always use default change event to prevent running into a loop */
8903 if (ChangeEvent[x][y] == -1)
8904 ChangeEvent[x][y] = CE_DELAY;
8906 if (ChangeEvent[x][y] == CE_DELAY)
8908 /* reset actual trigger element, trigger player and action element */
8909 change->actual_trigger_element = EL_EMPTY;
8910 change->actual_trigger_player = EL_PLAYER_1;
8911 change->actual_trigger_side = CH_SIDE_NONE;
8912 change->actual_trigger_ce_value = 0;
8913 change->actual_trigger_ce_score = 0;
8916 /* do not change elements more than a specified maximum number of changes */
8917 if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
8920 ChangeCount[x][y]++; /* count number of changes in the same frame */
8922 if (change->explode)
8929 if (change->use_target_content)
8931 boolean complete_replace = TRUE;
8932 boolean can_replace[3][3];
8935 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
8938 boolean is_walkable;
8939 boolean is_diggable;
8940 boolean is_collectible;
8941 boolean is_removable;
8942 boolean is_destructible;
8943 int ex = x + xx - 1;
8944 int ey = y + yy - 1;
8945 int content_element = change->target_content.e[xx][yy];
8948 can_replace[xx][yy] = TRUE;
8950 if (ex == x && ey == y) /* do not check changing element itself */
8953 if (content_element == EL_EMPTY_SPACE)
8955 can_replace[xx][yy] = FALSE; /* do not replace border with space */
8960 if (!IN_LEV_FIELD(ex, ey))
8962 can_replace[xx][yy] = FALSE;
8963 complete_replace = FALSE;
8970 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
8971 e = MovingOrBlocked2Element(ex, ey);
8973 is_empty = (IS_FREE(ex, ey) ||
8974 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
8976 is_walkable = (is_empty || IS_WALKABLE(e));
8977 is_diggable = (is_empty || IS_DIGGABLE(e));
8978 is_collectible = (is_empty || IS_COLLECTIBLE(e));
8979 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
8980 is_removable = (is_diggable || is_collectible);
8982 can_replace[xx][yy] =
8983 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
8984 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
8985 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
8986 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
8987 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
8988 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
8989 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
8991 if (!can_replace[xx][yy])
8992 complete_replace = FALSE;
8995 if (!change->only_if_complete || complete_replace)
8997 boolean something_has_changed = FALSE;
8999 if (change->only_if_complete && change->use_random_replace &&
9000 RND(100) < change->random_percentage)
9003 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
9005 int ex = x + xx - 1;
9006 int ey = y + yy - 1;
9007 int content_element;
9009 if (can_replace[xx][yy] && (!change->use_random_replace ||
9010 RND(100) < change->random_percentage))
9012 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
9013 RemoveMovingField(ex, ey);
9015 ChangeEvent[ex][ey] = ChangeEvent[x][y];
9017 content_element = change->target_content.e[xx][yy];
9018 target_element = GET_TARGET_ELEMENT(element, content_element, change,
9019 ce_value, ce_score);
9021 CreateElementFromChange(ex, ey, target_element);
9023 something_has_changed = TRUE;
9025 /* for symmetry reasons, freeze newly created border elements */
9026 if (ex != x || ey != y)
9027 Stop[ex][ey] = TRUE; /* no more moving in this frame */
9031 if (something_has_changed)
9033 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
9034 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
9040 target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
9041 ce_value, ce_score);
9043 if (element == EL_DIAGONAL_GROWING ||
9044 element == EL_DIAGONAL_SHRINKING)
9046 target_element = Store[x][y];
9048 Store[x][y] = EL_EMPTY;
9051 CreateElementFromChange(x, y, target_element);
9053 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
9054 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
9057 /* this uses direct change before indirect change */
9058 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
9063 #if USE_NEW_DELAYED_ACTION
9065 static void HandleElementChange(int x, int y, int page)
9067 int element = MovingOrBlocked2Element(x, y);
9068 struct ElementInfo *ei = &element_info[element];
9069 struct ElementChangeInfo *change = &ei->change_page[page];
9072 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
9073 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
9076 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
9077 x, y, element, element_info[element].token_name);
9078 printf("HandleElementChange(): This should never happen!\n");
9083 /* this can happen with classic bombs on walkable, changing elements */
9084 if (!CAN_CHANGE_OR_HAS_ACTION(element))
9087 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
9088 ChangeDelay[x][y] = 0;
9094 if (ChangeDelay[x][y] == 0) /* initialize element change */
9096 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
9098 if (change->can_change)
9100 ResetGfxAnimation(x, y);
9101 ResetRandomAnimationValue(x, y);
9103 if (change->pre_change_function)
9104 change->pre_change_function(x, y);
9108 ChangeDelay[x][y]--;
9110 if (ChangeDelay[x][y] != 0) /* continue element change */
9112 if (change->can_change)
9114 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9116 if (IS_ANIMATED(graphic))
9117 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9119 if (change->change_function)
9120 change->change_function(x, y);
9123 else /* finish element change */
9125 if (ChangePage[x][y] != -1) /* remember page from delayed change */
9127 page = ChangePage[x][y];
9128 ChangePage[x][y] = -1;
9130 change = &ei->change_page[page];
9133 if (IS_MOVING(x, y)) /* never change a running system ;-) */
9135 ChangeDelay[x][y] = 1; /* try change after next move step */
9136 ChangePage[x][y] = page; /* remember page to use for change */
9141 if (change->can_change)
9143 if (ChangeElement(x, y, element, page))
9145 if (change->post_change_function)
9146 change->post_change_function(x, y);
9150 if (change->has_action)
9151 ExecuteCustomElementAction(x, y, element, page);
9157 static void HandleElementChange(int x, int y, int page)
9159 int element = MovingOrBlocked2Element(x, y);
9160 struct ElementInfo *ei = &element_info[element];
9161 struct ElementChangeInfo *change = &ei->change_page[page];
9164 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
9167 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
9168 x, y, element, element_info[element].token_name);
9169 printf("HandleElementChange(): This should never happen!\n");
9174 /* this can happen with classic bombs on walkable, changing elements */
9175 if (!CAN_CHANGE(element))
9178 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
9179 ChangeDelay[x][y] = 0;
9185 if (ChangeDelay[x][y] == 0) /* initialize element change */
9187 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
9189 ResetGfxAnimation(x, y);
9190 ResetRandomAnimationValue(x, y);
9192 if (change->pre_change_function)
9193 change->pre_change_function(x, y);
9196 ChangeDelay[x][y]--;
9198 if (ChangeDelay[x][y] != 0) /* continue element change */
9200 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9202 if (IS_ANIMATED(graphic))
9203 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9205 if (change->change_function)
9206 change->change_function(x, y);
9208 else /* finish element change */
9210 if (ChangePage[x][y] != -1) /* remember page from delayed change */
9212 page = ChangePage[x][y];
9213 ChangePage[x][y] = -1;
9215 change = &ei->change_page[page];
9218 if (IS_MOVING(x, y)) /* never change a running system ;-) */
9220 ChangeDelay[x][y] = 1; /* try change after next move step */
9221 ChangePage[x][y] = page; /* remember page to use for change */
9226 if (ChangeElement(x, y, element, page))
9228 if (change->post_change_function)
9229 change->post_change_function(x, y);
9236 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
9237 int trigger_element,
9243 boolean change_done_any = FALSE;
9244 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
9247 if (!(trigger_events[trigger_element][trigger_event]))
9251 printf("::: CheckTriggeredElementChangeExt %d ... [%d, %d, %d, '%s']\n",
9252 trigger_event, recursion_loop_depth, recursion_loop_detected,
9253 recursion_loop_element, EL_NAME(recursion_loop_element));
9256 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
9258 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
9260 int element = EL_CUSTOM_START + i;
9261 boolean change_done = FALSE;
9264 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
9265 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
9268 for (p = 0; p < element_info[element].num_change_pages; p++)
9270 struct ElementChangeInfo *change = &element_info[element].change_page[p];
9272 if (change->can_change_or_has_action &&
9273 change->has_event[trigger_event] &&
9274 change->trigger_side & trigger_side &&
9275 change->trigger_player & trigger_player &&
9276 change->trigger_page & trigger_page_bits &&
9277 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
9279 change->actual_trigger_element = trigger_element;
9280 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
9281 change->actual_trigger_side = trigger_side;
9282 change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
9283 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
9285 if ((change->can_change && !change_done) || change->has_action)
9289 SCAN_PLAYFIELD(x, y)
9291 if (Feld[x][y] == element)
9293 if (change->can_change && !change_done)
9295 ChangeDelay[x][y] = 1;
9296 ChangeEvent[x][y] = trigger_event;
9298 HandleElementChange(x, y, p);
9300 #if USE_NEW_DELAYED_ACTION
9301 else if (change->has_action)
9303 ExecuteCustomElementAction(x, y, element, p);
9304 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
9307 if (change->has_action)
9309 ExecuteCustomElementAction(x, y, element, p);
9310 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
9316 if (change->can_change)
9319 change_done_any = TRUE;
9326 RECURSION_LOOP_DETECTION_END();
9328 return change_done_any;
9331 static boolean CheckElementChangeExt(int x, int y,
9333 int trigger_element,
9338 boolean change_done = FALSE;
9341 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
9342 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
9345 if (Feld[x][y] == EL_BLOCKED)
9347 Blocked2Moving(x, y, &x, &y);
9348 element = Feld[x][y];
9352 /* check if element has already changed */
9353 if (Feld[x][y] != element)
9356 /* check if element has already changed or is about to change after moving */
9357 if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
9358 Feld[x][y] != element) ||
9360 (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
9361 (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
9362 ChangePage[x][y] != -1)))
9367 printf("::: CheckElementChangeExt %d ... [%d, %d, %d, '%s']\n",
9368 trigger_event, recursion_loop_depth, recursion_loop_detected,
9369 recursion_loop_element, EL_NAME(recursion_loop_element));
9372 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
9374 for (p = 0; p < element_info[element].num_change_pages; p++)
9376 struct ElementChangeInfo *change = &element_info[element].change_page[p];
9378 /* check trigger element for all events where the element that is checked
9379 for changing interacts with a directly adjacent element -- this is
9380 different to element changes that affect other elements to change on the
9381 whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
9382 boolean check_trigger_element =
9383 (trigger_event == CE_TOUCHING_X ||
9384 trigger_event == CE_HITTING_X ||
9385 trigger_event == CE_HIT_BY_X ||
9387 /* this one was forgotten until 3.2.3 */
9388 trigger_event == CE_DIGGING_X);
9391 if (change->can_change_or_has_action &&
9392 change->has_event[trigger_event] &&
9393 change->trigger_side & trigger_side &&
9394 change->trigger_player & trigger_player &&
9395 (!check_trigger_element ||
9396 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
9398 change->actual_trigger_element = trigger_element;
9399 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
9400 change->actual_trigger_side = trigger_side;
9401 change->actual_trigger_ce_value = CustomValue[x][y];
9402 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
9404 /* special case: trigger element not at (x,y) position for some events */
9405 if (check_trigger_element)
9417 { 0, 0 }, { 0, 0 }, { 0, 0 },
9421 int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
9422 int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
9424 change->actual_trigger_ce_value = CustomValue[xx][yy];
9425 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
9428 if (change->can_change && !change_done)
9430 ChangeDelay[x][y] = 1;
9431 ChangeEvent[x][y] = trigger_event;
9433 HandleElementChange(x, y, p);
9437 #if USE_NEW_DELAYED_ACTION
9438 else if (change->has_action)
9440 ExecuteCustomElementAction(x, y, element, p);
9441 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
9444 if (change->has_action)
9446 ExecuteCustomElementAction(x, y, element, p);
9447 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
9453 RECURSION_LOOP_DETECTION_END();
9458 static void PlayPlayerSound(struct PlayerInfo *player)
9460 int jx = player->jx, jy = player->jy;
9461 int sound_element = player->artwork_element;
9462 int last_action = player->last_action_waiting;
9463 int action = player->action_waiting;
9465 if (player->is_waiting)
9467 if (action != last_action)
9468 PlayLevelSoundElementAction(jx, jy, sound_element, action);
9470 PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
9474 if (action != last_action)
9475 StopSound(element_info[sound_element].sound[last_action]);
9477 if (last_action == ACTION_SLEEPING)
9478 PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
9482 static void PlayAllPlayersSound()
9486 for (i = 0; i < MAX_PLAYERS; i++)
9487 if (stored_player[i].active)
9488 PlayPlayerSound(&stored_player[i]);
9491 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
9493 boolean last_waiting = player->is_waiting;
9494 int move_dir = player->MovDir;
9496 player->dir_waiting = move_dir;
9497 player->last_action_waiting = player->action_waiting;
9501 if (!last_waiting) /* not waiting -> waiting */
9503 player->is_waiting = TRUE;
9505 player->frame_counter_bored =
9507 game.player_boring_delay_fixed +
9508 GetSimpleRandom(game.player_boring_delay_random);
9509 player->frame_counter_sleeping =
9511 game.player_sleeping_delay_fixed +
9512 GetSimpleRandom(game.player_sleeping_delay_random);
9514 InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
9517 if (game.player_sleeping_delay_fixed +
9518 game.player_sleeping_delay_random > 0 &&
9519 player->anim_delay_counter == 0 &&
9520 player->post_delay_counter == 0 &&
9521 FrameCounter >= player->frame_counter_sleeping)
9522 player->is_sleeping = TRUE;
9523 else if (game.player_boring_delay_fixed +
9524 game.player_boring_delay_random > 0 &&
9525 FrameCounter >= player->frame_counter_bored)
9526 player->is_bored = TRUE;
9528 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
9529 player->is_bored ? ACTION_BORING :
9532 if (player->is_sleeping && player->use_murphy)
9534 /* special case for sleeping Murphy when leaning against non-free tile */
9536 if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
9537 (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
9538 !IS_MOVING(player->jx - 1, player->jy)))
9540 else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
9541 (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
9542 !IS_MOVING(player->jx + 1, player->jy)))
9543 move_dir = MV_RIGHT;
9545 player->is_sleeping = FALSE;
9547 player->dir_waiting = move_dir;
9550 if (player->is_sleeping)
9552 if (player->num_special_action_sleeping > 0)
9554 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
9556 int last_special_action = player->special_action_sleeping;
9557 int num_special_action = player->num_special_action_sleeping;
9558 int special_action =
9559 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
9560 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
9561 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
9562 last_special_action + 1 : ACTION_SLEEPING);
9563 int special_graphic =
9564 el_act_dir2img(player->artwork_element, special_action, move_dir);
9566 player->anim_delay_counter =
9567 graphic_info[special_graphic].anim_delay_fixed +
9568 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
9569 player->post_delay_counter =
9570 graphic_info[special_graphic].post_delay_fixed +
9571 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
9573 player->special_action_sleeping = special_action;
9576 if (player->anim_delay_counter > 0)
9578 player->action_waiting = player->special_action_sleeping;
9579 player->anim_delay_counter--;
9581 else if (player->post_delay_counter > 0)
9583 player->post_delay_counter--;
9587 else if (player->is_bored)
9589 if (player->num_special_action_bored > 0)
9591 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
9593 int special_action =
9594 ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
9595 int special_graphic =
9596 el_act_dir2img(player->artwork_element, special_action, move_dir);
9598 player->anim_delay_counter =
9599 graphic_info[special_graphic].anim_delay_fixed +
9600 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
9601 player->post_delay_counter =
9602 graphic_info[special_graphic].post_delay_fixed +
9603 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
9605 player->special_action_bored = special_action;
9608 if (player->anim_delay_counter > 0)
9610 player->action_waiting = player->special_action_bored;
9611 player->anim_delay_counter--;
9613 else if (player->post_delay_counter > 0)
9615 player->post_delay_counter--;
9620 else if (last_waiting) /* waiting -> not waiting */
9622 player->is_waiting = FALSE;
9623 player->is_bored = FALSE;
9624 player->is_sleeping = FALSE;
9626 player->frame_counter_bored = -1;
9627 player->frame_counter_sleeping = -1;
9629 player->anim_delay_counter = 0;
9630 player->post_delay_counter = 0;
9632 player->dir_waiting = player->MovDir;
9633 player->action_waiting = ACTION_DEFAULT;
9635 player->special_action_bored = ACTION_DEFAULT;
9636 player->special_action_sleeping = ACTION_DEFAULT;
9640 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
9642 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
9643 int left = player_action & JOY_LEFT;
9644 int right = player_action & JOY_RIGHT;
9645 int up = player_action & JOY_UP;
9646 int down = player_action & JOY_DOWN;
9647 int button1 = player_action & JOY_BUTTON_1;
9648 int button2 = player_action & JOY_BUTTON_2;
9649 int dx = (left ? -1 : right ? 1 : 0);
9650 int dy = (up ? -1 : down ? 1 : 0);
9652 if (!player->active || tape.pausing)
9658 snapped = SnapField(player, dx, dy);
9662 dropped = DropElement(player);
9664 moved = MovePlayer(player, dx, dy);
9667 if (tape.single_step && tape.recording && !tape.pausing)
9669 if (button1 || (dropped && !moved))
9671 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9672 SnapField(player, 0, 0); /* stop snapping */
9676 SetPlayerWaiting(player, FALSE);
9678 return player_action;
9682 /* no actions for this player (no input at player's configured device) */
9684 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
9685 SnapField(player, 0, 0);
9686 CheckGravityMovementWhenNotMoving(player);
9688 if (player->MovPos == 0)
9689 SetPlayerWaiting(player, TRUE);
9691 if (player->MovPos == 0) /* needed for tape.playing */
9692 player->is_moving = FALSE;
9694 player->is_dropping = FALSE;
9695 player->is_dropping_pressed = FALSE;
9696 player->drop_pressed_delay = 0;
9702 static void CheckLevelTime()
9706 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9708 if (level.native_em_level->lev->home == 0) /* all players at home */
9710 PlayerWins(local_player);
9712 AllPlayersGone = TRUE;
9714 level.native_em_level->lev->home = -1;
9717 if (level.native_em_level->ply[0]->alive == 0 &&
9718 level.native_em_level->ply[1]->alive == 0 &&
9719 level.native_em_level->ply[2]->alive == 0 &&
9720 level.native_em_level->ply[3]->alive == 0) /* all dead */
9721 AllPlayersGone = TRUE;
9724 if (TimeFrames >= FRAMES_PER_SECOND)
9729 for (i = 0; i < MAX_PLAYERS; i++)
9731 struct PlayerInfo *player = &stored_player[i];
9733 if (SHIELD_ON(player))
9735 player->shield_normal_time_left--;
9737 if (player->shield_deadly_time_left > 0)
9738 player->shield_deadly_time_left--;
9742 if (!local_player->LevelSolved && !level.use_step_counter)
9750 if (TimeLeft <= 10 && setup.time_limit)
9751 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
9753 DrawGameValue_Time(TimeLeft);
9755 if (!TimeLeft && setup.time_limit)
9757 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9758 level.native_em_level->lev->killed_out_of_time = TRUE;
9760 for (i = 0; i < MAX_PLAYERS; i++)
9761 KillPlayer(&stored_player[i]);
9764 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
9765 DrawGameValue_Time(TimePlayed);
9767 level.native_em_level->lev->time =
9768 (level.time == 0 ? TimePlayed : TimeLeft);
9771 if (tape.recording || tape.playing)
9772 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
9776 void AdvanceFrameAndPlayerCounters(int player_nr)
9780 /* advance frame counters (global frame counter and time frame counter) */
9784 /* advance player counters (counters for move delay, move animation etc.) */
9785 for (i = 0; i < MAX_PLAYERS; i++)
9787 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
9788 int move_delay_value = stored_player[i].move_delay_value;
9789 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
9791 if (!advance_player_counters) /* not all players may be affected */
9794 #if USE_NEW_PLAYER_ANIM
9795 if (move_frames == 0) /* less than one move per game frame */
9797 int stepsize = TILEX / move_delay_value;
9798 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
9799 int count = (stored_player[i].is_moving ?
9800 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
9802 if (count % delay == 0)
9807 stored_player[i].Frame += move_frames;
9809 if (stored_player[i].MovPos != 0)
9810 stored_player[i].StepFrame += move_frames;
9812 if (stored_player[i].move_delay > 0)
9813 stored_player[i].move_delay--;
9815 /* due to bugs in previous versions, counter must count up, not down */
9816 if (stored_player[i].push_delay != -1)
9817 stored_player[i].push_delay++;
9819 if (stored_player[i].drop_delay > 0)
9820 stored_player[i].drop_delay--;
9822 if (stored_player[i].is_dropping_pressed)
9823 stored_player[i].drop_pressed_delay++;
9827 void StartGameActions(boolean init_network_game, boolean record_tape,
9830 unsigned long new_random_seed = InitRND(random_seed);
9833 TapeStartRecording(new_random_seed);
9835 #if defined(NETWORK_AVALIABLE)
9836 if (init_network_game)
9838 SendToServer_StartPlaying();
9849 static unsigned long game_frame_delay = 0;
9850 unsigned long game_frame_delay_value;
9851 byte *recorded_player_action;
9852 byte summarized_player_action = 0;
9853 byte tape_action[MAX_PLAYERS];
9856 /* detect endless loops, caused by custom element programming */
9857 if (recursion_loop_detected && recursion_loop_depth == 0)
9859 char *message = getStringCat3("Internal Error ! Element ",
9860 EL_NAME(recursion_loop_element),
9861 " caused endless loop ! Quit the game ?");
9863 Error(ERR_WARN, "element '%s' caused endless loop in game engine",
9864 EL_NAME(recursion_loop_element));
9866 RequestQuitGameExt(FALSE, level_editor_test_game, message);
9868 recursion_loop_detected = FALSE; /* if game should be continued */
9875 if (game.restart_level)
9876 StartGameActions(options.network, setup.autorecord, NEW_RANDOMIZE);
9878 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9880 if (level.native_em_level->lev->home == 0) /* all players at home */
9882 PlayerWins(local_player);
9884 AllPlayersGone = TRUE;
9886 level.native_em_level->lev->home = -1;
9889 if (level.native_em_level->ply[0]->alive == 0 &&
9890 level.native_em_level->ply[1]->alive == 0 &&
9891 level.native_em_level->ply[2]->alive == 0 &&
9892 level.native_em_level->ply[3]->alive == 0) /* all dead */
9893 AllPlayersGone = TRUE;
9896 if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
9899 if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
9902 if (game_status != GAME_MODE_PLAYING) /* status might have changed */
9905 game_frame_delay_value =
9906 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
9908 if (tape.playing && tape.warp_forward && !tape.pausing)
9909 game_frame_delay_value = 0;
9911 /* ---------- main game synchronization point ---------- */
9913 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
9915 if (network_playing && !network_player_action_received)
9917 /* try to get network player actions in time */
9919 #if defined(NETWORK_AVALIABLE)
9920 /* last chance to get network player actions without main loop delay */
9924 /* game was quit by network peer */
9925 if (game_status != GAME_MODE_PLAYING)
9928 if (!network_player_action_received)
9929 return; /* failed to get network player actions in time */
9931 /* do not yet reset "network_player_action_received" (for tape.pausing) */
9937 /* at this point we know that we really continue executing the game */
9939 network_player_action_received = FALSE;
9941 /* when playing tape, read previously recorded player input from tape data */
9942 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
9945 /* TapePlayAction() may return NULL when toggling to "pause before death" */
9950 if (tape.set_centered_player)
9952 game.centered_player_nr_next = tape.centered_player_nr_next;
9953 game.set_centered_player = TRUE;
9956 for (i = 0; i < MAX_PLAYERS; i++)
9958 summarized_player_action |= stored_player[i].action;
9960 if (!network_playing)
9961 stored_player[i].effective_action = stored_player[i].action;
9964 #if defined(NETWORK_AVALIABLE)
9965 if (network_playing)
9966 SendToServer_MovePlayer(summarized_player_action);
9969 if (!options.network && !setup.team_mode)
9970 local_player->effective_action = summarized_player_action;
9972 if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
9974 for (i = 0; i < MAX_PLAYERS; i++)
9975 stored_player[i].effective_action =
9976 (i == game.centered_player_nr ? summarized_player_action : 0);
9979 if (recorded_player_action != NULL)
9980 for (i = 0; i < MAX_PLAYERS; i++)
9981 stored_player[i].effective_action = recorded_player_action[i];
9983 for (i = 0; i < MAX_PLAYERS; i++)
9985 tape_action[i] = stored_player[i].effective_action;
9987 /* (this can only happen in the R'n'D game engine) */
9988 if (tape.recording && tape_action[i] && !tape.player_participates[i])
9989 tape.player_participates[i] = TRUE; /* player just appeared from CE */
9992 /* only record actions from input devices, but not programmed actions */
9994 TapeRecordAction(tape_action);
9996 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9998 GameActions_EM_Main();
10006 void GameActions_EM_Main()
10008 byte effective_action[MAX_PLAYERS];
10009 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
10012 for (i = 0; i < MAX_PLAYERS; i++)
10013 effective_action[i] = stored_player[i].effective_action;
10015 GameActions_EM(effective_action, warp_mode);
10019 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
10022 void GameActions_RND()
10024 int magic_wall_x = 0, magic_wall_y = 0;
10025 int i, x, y, element, graphic;
10027 InitPlayfieldScanModeVars();
10029 #if USE_ONE_MORE_CHANGE_PER_FRAME
10030 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10032 SCAN_PLAYFIELD(x, y)
10034 ChangeCount[x][y] = 0;
10035 ChangeEvent[x][y] = -1;
10040 if (game.set_centered_player)
10042 boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
10044 /* switching to "all players" only possible if all players fit to screen */
10045 if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
10047 game.centered_player_nr_next = game.centered_player_nr;
10048 game.set_centered_player = FALSE;
10051 /* do not switch focus to non-existing (or non-active) player */
10052 if (game.centered_player_nr_next >= 0 &&
10053 !stored_player[game.centered_player_nr_next].active)
10055 game.centered_player_nr_next = game.centered_player_nr;
10056 game.set_centered_player = FALSE;
10060 if (game.set_centered_player &&
10061 ScreenMovPos == 0) /* screen currently aligned at tile position */
10065 if (game.centered_player_nr_next == -1)
10067 setScreenCenteredToAllPlayers(&sx, &sy);
10071 sx = stored_player[game.centered_player_nr_next].jx;
10072 sy = stored_player[game.centered_player_nr_next].jy;
10075 game.centered_player_nr = game.centered_player_nr_next;
10076 game.set_centered_player = FALSE;
10078 DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
10079 DrawGameDoorValues();
10082 for (i = 0; i < MAX_PLAYERS; i++)
10084 int actual_player_action = stored_player[i].effective_action;
10087 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
10088 - rnd_equinox_tetrachloride 048
10089 - rnd_equinox_tetrachloride_ii 096
10090 - rnd_emanuel_schmieg 002
10091 - doctor_sloan_ww 001, 020
10093 if (stored_player[i].MovPos == 0)
10094 CheckGravityMovement(&stored_player[i]);
10097 /* overwrite programmed action with tape action */
10098 if (stored_player[i].programmed_action)
10099 actual_player_action = stored_player[i].programmed_action;
10101 PlayerActions(&stored_player[i], actual_player_action);
10103 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
10106 ScrollScreen(NULL, SCROLL_GO_ON);
10108 /* for backwards compatibility, the following code emulates a fixed bug that
10109 occured when pushing elements (causing elements that just made their last
10110 pushing step to already (if possible) make their first falling step in the
10111 same game frame, which is bad); this code is also needed to use the famous
10112 "spring push bug" which is used in older levels and might be wanted to be
10113 used also in newer levels, but in this case the buggy pushing code is only
10114 affecting the "spring" element and no other elements */
10116 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
10118 for (i = 0; i < MAX_PLAYERS; i++)
10120 struct PlayerInfo *player = &stored_player[i];
10121 int x = player->jx;
10122 int y = player->jy;
10124 if (player->active && player->is_pushing && player->is_moving &&
10126 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
10127 Feld[x][y] == EL_SPRING))
10129 ContinueMoving(x, y);
10131 /* continue moving after pushing (this is actually a bug) */
10132 if (!IS_MOVING(x, y))
10134 Stop[x][y] = FALSE;
10140 SCAN_PLAYFIELD(x, y)
10142 ChangeCount[x][y] = 0;
10143 ChangeEvent[x][y] = -1;
10145 /* this must be handled before main playfield loop */
10146 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
10149 if (MovDelay[x][y] <= 0)
10153 #if USE_NEW_SNAP_DELAY
10154 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
10157 if (MovDelay[x][y] <= 0)
10160 DrawLevelField(x, y);
10162 TestIfElementTouchesCustomElement(x, y); /* for empty space */
10168 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
10170 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
10171 printf("GameActions(): This should never happen!\n");
10173 ChangePage[x][y] = -1;
10177 Stop[x][y] = FALSE;
10178 if (WasJustMoving[x][y] > 0)
10179 WasJustMoving[x][y]--;
10180 if (WasJustFalling[x][y] > 0)
10181 WasJustFalling[x][y]--;
10182 if (CheckCollision[x][y] > 0)
10183 CheckCollision[x][y]--;
10184 if (CheckImpact[x][y] > 0)
10185 CheckImpact[x][y]--;
10189 /* reset finished pushing action (not done in ContinueMoving() to allow
10190 continuous pushing animation for elements with zero push delay) */
10191 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
10193 ResetGfxAnimation(x, y);
10194 DrawLevelField(x, y);
10198 if (IS_BLOCKED(x, y))
10202 Blocked2Moving(x, y, &oldx, &oldy);
10203 if (!IS_MOVING(oldx, oldy))
10205 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
10206 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
10207 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
10208 printf("GameActions(): This should never happen!\n");
10214 SCAN_PLAYFIELD(x, y)
10216 element = Feld[x][y];
10217 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10219 ResetGfxFrame(x, y, TRUE);
10221 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
10222 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
10223 ResetRandomAnimationValue(x, y);
10225 SetRandomAnimationValue(x, y);
10227 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
10229 if (IS_INACTIVE(element))
10231 if (IS_ANIMATED(graphic))
10232 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10237 /* this may take place after moving, so 'element' may have changed */
10238 if (IS_CHANGING(x, y) &&
10239 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
10241 int page = element_info[element].event_page_nr[CE_DELAY];
10244 HandleElementChange(x, y, page);
10246 if (CAN_CHANGE(element))
10247 HandleElementChange(x, y, page);
10249 if (HAS_ACTION(element))
10250 ExecuteCustomElementAction(x, y, element, page);
10253 element = Feld[x][y];
10254 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10257 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
10261 element = Feld[x][y];
10262 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10264 if (IS_ANIMATED(graphic) &&
10265 !IS_MOVING(x, y) &&
10267 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10269 if (IS_GEM(element) || element == EL_SP_INFOTRON)
10270 DrawTwinkleOnField(x, y);
10272 else if ((element == EL_ACID ||
10273 element == EL_EXIT_OPEN ||
10274 element == EL_EM_EXIT_OPEN ||
10275 element == EL_SP_EXIT_OPEN ||
10276 element == EL_STEEL_EXIT_OPEN ||
10277 element == EL_EM_STEEL_EXIT_OPEN ||
10278 element == EL_SP_TERMINAL ||
10279 element == EL_SP_TERMINAL_ACTIVE ||
10280 element == EL_EXTRA_TIME ||
10281 element == EL_SHIELD_NORMAL ||
10282 element == EL_SHIELD_DEADLY) &&
10283 IS_ANIMATED(graphic))
10284 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10285 else if (IS_MOVING(x, y))
10286 ContinueMoving(x, y);
10287 else if (IS_ACTIVE_BOMB(element))
10288 CheckDynamite(x, y);
10289 else if (element == EL_AMOEBA_GROWING)
10290 AmoebeWaechst(x, y);
10291 else if (element == EL_AMOEBA_SHRINKING)
10292 AmoebaDisappearing(x, y);
10294 #if !USE_NEW_AMOEBA_CODE
10295 else if (IS_AMOEBALIVE(element))
10296 AmoebeAbleger(x, y);
10299 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
10301 else if (element == EL_EXIT_CLOSED)
10303 else if (element == EL_EM_EXIT_CLOSED)
10305 else if (element == EL_STEEL_EXIT_CLOSED)
10306 CheckExitSteel(x, y);
10307 else if (element == EL_EM_STEEL_EXIT_CLOSED)
10308 CheckExitSteelEM(x, y);
10309 else if (element == EL_SP_EXIT_CLOSED)
10311 else if (element == EL_EXPANDABLE_WALL_GROWING ||
10312 element == EL_EXPANDABLE_STEELWALL_GROWING)
10313 MauerWaechst(x, y);
10314 else if (element == EL_EXPANDABLE_WALL ||
10315 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
10316 element == EL_EXPANDABLE_WALL_VERTICAL ||
10317 element == EL_EXPANDABLE_WALL_ANY ||
10318 element == EL_BD_EXPANDABLE_WALL)
10319 MauerAbleger(x, y);
10320 else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
10321 element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
10322 element == EL_EXPANDABLE_STEELWALL_ANY)
10323 MauerAblegerStahl(x, y);
10324 else if (element == EL_FLAMES)
10325 CheckForDragon(x, y);
10326 else if (element == EL_EXPLOSION)
10327 ; /* drawing of correct explosion animation is handled separately */
10328 else if (element == EL_ELEMENT_SNAPPING ||
10329 element == EL_DIAGONAL_SHRINKING ||
10330 element == EL_DIAGONAL_GROWING)
10332 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
10334 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10336 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
10337 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10339 if (IS_BELT_ACTIVE(element))
10340 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
10342 if (game.magic_wall_active)
10344 int jx = local_player->jx, jy = local_player->jy;
10346 /* play the element sound at the position nearest to the player */
10347 if ((element == EL_MAGIC_WALL_FULL ||
10348 element == EL_MAGIC_WALL_ACTIVE ||
10349 element == EL_MAGIC_WALL_EMPTYING ||
10350 element == EL_BD_MAGIC_WALL_FULL ||
10351 element == EL_BD_MAGIC_WALL_ACTIVE ||
10352 element == EL_BD_MAGIC_WALL_EMPTYING ||
10353 element == EL_DC_MAGIC_WALL_FULL ||
10354 element == EL_DC_MAGIC_WALL_ACTIVE ||
10355 element == EL_DC_MAGIC_WALL_EMPTYING) &&
10356 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
10364 #if USE_NEW_AMOEBA_CODE
10365 /* new experimental amoeba growth stuff */
10366 if (!(FrameCounter % 8))
10368 static unsigned long random = 1684108901;
10370 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
10372 x = RND(lev_fieldx);
10373 y = RND(lev_fieldy);
10374 element = Feld[x][y];
10376 if (!IS_PLAYER(x,y) &&
10377 (element == EL_EMPTY ||
10378 CAN_GROW_INTO(element) ||
10379 element == EL_QUICKSAND_EMPTY ||
10380 element == EL_QUICKSAND_FAST_EMPTY ||
10381 element == EL_ACID_SPLASH_LEFT ||
10382 element == EL_ACID_SPLASH_RIGHT))
10384 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
10385 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
10386 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
10387 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
10388 Feld[x][y] = EL_AMOEBA_DROP;
10391 random = random * 129 + 1;
10397 if (game.explosions_delayed)
10400 game.explosions_delayed = FALSE;
10402 SCAN_PLAYFIELD(x, y)
10404 element = Feld[x][y];
10406 if (ExplodeField[x][y])
10407 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
10408 else if (element == EL_EXPLOSION)
10409 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
10411 ExplodeField[x][y] = EX_TYPE_NONE;
10414 game.explosions_delayed = TRUE;
10417 if (game.magic_wall_active)
10419 if (!(game.magic_wall_time_left % 4))
10421 int element = Feld[magic_wall_x][magic_wall_y];
10423 if (element == EL_BD_MAGIC_WALL_FULL ||
10424 element == EL_BD_MAGIC_WALL_ACTIVE ||
10425 element == EL_BD_MAGIC_WALL_EMPTYING)
10426 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
10427 else if (element == EL_DC_MAGIC_WALL_FULL ||
10428 element == EL_DC_MAGIC_WALL_ACTIVE ||
10429 element == EL_DC_MAGIC_WALL_EMPTYING)
10430 PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
10432 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
10435 if (game.magic_wall_time_left > 0)
10437 game.magic_wall_time_left--;
10438 if (!game.magic_wall_time_left)
10440 SCAN_PLAYFIELD(x, y)
10442 element = Feld[x][y];
10444 if (element == EL_MAGIC_WALL_ACTIVE ||
10445 element == EL_MAGIC_WALL_FULL)
10447 Feld[x][y] = EL_MAGIC_WALL_DEAD;
10448 DrawLevelField(x, y);
10450 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
10451 element == EL_BD_MAGIC_WALL_FULL)
10453 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
10454 DrawLevelField(x, y);
10456 else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
10457 element == EL_DC_MAGIC_WALL_FULL)
10459 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
10460 DrawLevelField(x, y);
10464 game.magic_wall_active = FALSE;
10469 if (game.light_time_left > 0)
10471 game.light_time_left--;
10473 if (game.light_time_left == 0)
10474 RedrawAllLightSwitchesAndInvisibleElements();
10477 if (game.timegate_time_left > 0)
10479 game.timegate_time_left--;
10481 if (game.timegate_time_left == 0)
10482 CloseAllOpenTimegates();
10485 if (game.lenses_time_left > 0)
10487 game.lenses_time_left--;
10489 if (game.lenses_time_left == 0)
10490 RedrawAllInvisibleElementsForLenses();
10493 if (game.magnify_time_left > 0)
10495 game.magnify_time_left--;
10497 if (game.magnify_time_left == 0)
10498 RedrawAllInvisibleElementsForMagnifier();
10501 for (i = 0; i < MAX_PLAYERS; i++)
10503 struct PlayerInfo *player = &stored_player[i];
10505 if (SHIELD_ON(player))
10507 if (player->shield_deadly_time_left)
10508 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
10509 else if (player->shield_normal_time_left)
10510 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
10517 PlayAllPlayersSound();
10519 if (options.debug) /* calculate frames per second */
10521 static unsigned long fps_counter = 0;
10522 static int fps_frames = 0;
10523 unsigned long fps_delay_ms = Counter() - fps_counter;
10527 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
10529 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
10532 fps_counter = Counter();
10535 redraw_mask |= REDRAW_FPS;
10538 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
10540 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
10542 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
10544 local_player->show_envelope = 0;
10547 /* use random number generator in every frame to make it less predictable */
10548 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
10552 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
10554 int min_x = x, min_y = y, max_x = x, max_y = y;
10557 for (i = 0; i < MAX_PLAYERS; i++)
10559 int jx = stored_player[i].jx, jy = stored_player[i].jy;
10561 if (!stored_player[i].active || &stored_player[i] == player)
10564 min_x = MIN(min_x, jx);
10565 min_y = MIN(min_y, jy);
10566 max_x = MAX(max_x, jx);
10567 max_y = MAX(max_y, jy);
10570 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
10573 static boolean AllPlayersInVisibleScreen()
10577 for (i = 0; i < MAX_PLAYERS; i++)
10579 int jx = stored_player[i].jx, jy = stored_player[i].jy;
10581 if (!stored_player[i].active)
10584 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
10591 void ScrollLevel(int dx, int dy)
10594 static Bitmap *bitmap_db_field2 = NULL;
10595 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
10601 /* only horizontal XOR vertical scroll direction allowed */
10602 if ((dx == 0 && dy == 0) || (dx != 0 && dy != 0))
10606 if (bitmap_db_field2 == NULL)
10607 bitmap_db_field2 = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH);
10609 BlitBitmap(drawto_field, bitmap_db_field2,
10610 FX + TILEX * (dx == -1) - softscroll_offset,
10611 FY + TILEY * (dy == -1) - softscroll_offset,
10612 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
10613 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
10614 FX + TILEX * (dx == 1) - softscroll_offset,
10615 FY + TILEY * (dy == 1) - softscroll_offset);
10616 BlitBitmap(bitmap_db_field2, drawto_field,
10617 FX + TILEX * (dx == 1) - softscroll_offset,
10618 FY + TILEY * (dy == 1) - softscroll_offset,
10619 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
10620 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
10621 FX + TILEX * (dx == 1) - softscroll_offset,
10622 FY + TILEY * (dy == 1) - softscroll_offset);
10627 int xsize = (BX2 - BX1 + 1);
10628 int ysize = (BY2 - BY1 + 1);
10629 int start = (dx != 0 ? (dx == -1 ? BX1 : BX2) : (dy == -1 ? BY1 : BY2));
10630 int end = (dx != 0 ? (dx == -1 ? BX2 : BX1) : (dy == -1 ? BY2 : BY1));
10631 int step = (start < end ? +1 : -1);
10633 for (i = start; i != end; i += step)
10635 BlitBitmap(drawto_field, drawto_field,
10636 FX + TILEX * (dx != 0 ? i + step : 0),
10637 FY + TILEY * (dy != 0 ? i + step : 0),
10638 TILEX * (dx != 0 ? 1 : xsize),
10639 TILEY * (dy != 0 ? 1 : ysize),
10640 FX + TILEX * (dx != 0 ? i : 0),
10641 FY + TILEY * (dy != 0 ? i : 0));
10646 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
10648 BlitBitmap(drawto_field, drawto_field,
10649 FX + TILEX * (dx == -1) - softscroll_offset,
10650 FY + TILEY * (dy == -1) - softscroll_offset,
10651 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
10652 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
10653 FX + TILEX * (dx == 1) - softscroll_offset,
10654 FY + TILEY * (dy == 1) - softscroll_offset);
10660 x = (dx == 1 ? BX1 : BX2);
10661 for (y = BY1; y <= BY2; y++)
10662 DrawScreenField(x, y);
10667 y = (dy == 1 ? BY1 : BY2);
10668 for (x = BX1; x <= BX2; x++)
10669 DrawScreenField(x, y);
10672 redraw_mask |= REDRAW_FIELD;
10675 static boolean canFallDown(struct PlayerInfo *player)
10677 int jx = player->jx, jy = player->jy;
10679 return (IN_LEV_FIELD(jx, jy + 1) &&
10680 (IS_FREE(jx, jy + 1) ||
10681 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
10682 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
10683 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
10686 static boolean canPassField(int x, int y, int move_dir)
10688 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
10689 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
10690 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
10691 int nextx = x + dx;
10692 int nexty = y + dy;
10693 int element = Feld[x][y];
10695 return (IS_PASSABLE_FROM(element, opposite_dir) &&
10696 !CAN_MOVE(element) &&
10697 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
10698 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
10699 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
10702 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
10704 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
10705 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
10706 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
10710 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
10711 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
10712 (IS_DIGGABLE(Feld[newx][newy]) ||
10713 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
10714 canPassField(newx, newy, move_dir)));
10717 static void CheckGravityMovement(struct PlayerInfo *player)
10719 #if USE_PLAYER_GRAVITY
10720 if (player->gravity && !player->programmed_action)
10722 if (game.gravity && !player->programmed_action)
10725 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
10726 int move_dir_vertical = player->effective_action & MV_VERTICAL;
10727 boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
10728 int jx = player->jx, jy = player->jy;
10729 boolean player_is_moving_to_valid_field =
10730 (!player_is_snapping &&
10731 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
10732 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
10733 boolean player_can_fall_down = canFallDown(player);
10735 if (player_can_fall_down &&
10736 !player_is_moving_to_valid_field)
10737 player->programmed_action = MV_DOWN;
10741 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
10743 return CheckGravityMovement(player);
10745 #if USE_PLAYER_GRAVITY
10746 if (player->gravity && !player->programmed_action)
10748 if (game.gravity && !player->programmed_action)
10751 int jx = player->jx, jy = player->jy;
10752 boolean field_under_player_is_free =
10753 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
10754 boolean player_is_standing_on_valid_field =
10755 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
10756 (IS_WALKABLE(Feld[jx][jy]) &&
10757 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
10759 if (field_under_player_is_free && !player_is_standing_on_valid_field)
10760 player->programmed_action = MV_DOWN;
10765 MovePlayerOneStep()
10766 -----------------------------------------------------------------------------
10767 dx, dy: direction (non-diagonal) to try to move the player to
10768 real_dx, real_dy: direction as read from input device (can be diagonal)
10771 boolean MovePlayerOneStep(struct PlayerInfo *player,
10772 int dx, int dy, int real_dx, int real_dy)
10774 int jx = player->jx, jy = player->jy;
10775 int new_jx = jx + dx, new_jy = jy + dy;
10776 #if !USE_FIXED_DONT_RUN_INTO
10780 boolean player_can_move = !player->cannot_move;
10782 if (!player->active || (!dx && !dy))
10783 return MP_NO_ACTION;
10785 player->MovDir = (dx < 0 ? MV_LEFT :
10786 dx > 0 ? MV_RIGHT :
10788 dy > 0 ? MV_DOWN : MV_NONE);
10790 if (!IN_LEV_FIELD(new_jx, new_jy))
10791 return MP_NO_ACTION;
10793 if (!player_can_move)
10795 if (player->MovPos == 0)
10797 player->is_moving = FALSE;
10798 player->is_digging = FALSE;
10799 player->is_collecting = FALSE;
10800 player->is_snapping = FALSE;
10801 player->is_pushing = FALSE;
10806 if (!options.network && game.centered_player_nr == -1 &&
10807 !AllPlayersInSight(player, new_jx, new_jy))
10808 return MP_NO_ACTION;
10810 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
10811 return MP_NO_ACTION;
10814 #if !USE_FIXED_DONT_RUN_INTO
10815 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
10817 /* (moved to DigField()) */
10818 if (player_can_move && DONT_RUN_INTO(element))
10820 if (element == EL_ACID && dx == 0 && dy == 1)
10822 SplashAcid(new_jx, new_jy);
10823 Feld[jx][jy] = EL_PLAYER_1;
10824 InitMovingField(jx, jy, MV_DOWN);
10825 Store[jx][jy] = EL_ACID;
10826 ContinueMoving(jx, jy);
10827 BuryPlayer(player);
10830 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
10836 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
10837 if (can_move != MP_MOVING)
10840 /* check if DigField() has caused relocation of the player */
10841 if (player->jx != jx || player->jy != jy)
10842 return MP_NO_ACTION; /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
10844 StorePlayer[jx][jy] = 0;
10845 player->last_jx = jx;
10846 player->last_jy = jy;
10847 player->jx = new_jx;
10848 player->jy = new_jy;
10849 StorePlayer[new_jx][new_jy] = player->element_nr;
10851 if (player->move_delay_value_next != -1)
10853 player->move_delay_value = player->move_delay_value_next;
10854 player->move_delay_value_next = -1;
10858 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
10860 player->step_counter++;
10862 PlayerVisit[jx][jy] = FrameCounter;
10864 #if USE_UFAST_PLAYER_EXIT_BUGFIX
10865 player->is_moving = TRUE;
10869 /* should better be called in MovePlayer(), but this breaks some tapes */
10870 ScrollPlayer(player, SCROLL_INIT);
10876 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
10878 int jx = player->jx, jy = player->jy;
10879 int old_jx = jx, old_jy = jy;
10880 int moved = MP_NO_ACTION;
10882 if (!player->active)
10887 if (player->MovPos == 0)
10889 player->is_moving = FALSE;
10890 player->is_digging = FALSE;
10891 player->is_collecting = FALSE;
10892 player->is_snapping = FALSE;
10893 player->is_pushing = FALSE;
10899 if (player->move_delay > 0)
10902 player->move_delay = -1; /* set to "uninitialized" value */
10904 /* store if player is automatically moved to next field */
10905 player->is_auto_moving = (player->programmed_action != MV_NONE);
10907 /* remove the last programmed player action */
10908 player->programmed_action = 0;
10910 if (player->MovPos)
10912 /* should only happen if pre-1.2 tape recordings are played */
10913 /* this is only for backward compatibility */
10915 int original_move_delay_value = player->move_delay_value;
10918 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
10922 /* scroll remaining steps with finest movement resolution */
10923 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
10925 while (player->MovPos)
10927 ScrollPlayer(player, SCROLL_GO_ON);
10928 ScrollScreen(NULL, SCROLL_GO_ON);
10930 AdvanceFrameAndPlayerCounters(player->index_nr);
10936 player->move_delay_value = original_move_delay_value;
10939 player->is_active = FALSE;
10941 if (player->last_move_dir & MV_HORIZONTAL)
10943 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
10944 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
10948 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
10949 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
10952 #if USE_FIXED_BORDER_RUNNING_GFX
10953 if (!moved && !player->is_active)
10955 player->is_moving = FALSE;
10956 player->is_digging = FALSE;
10957 player->is_collecting = FALSE;
10958 player->is_snapping = FALSE;
10959 player->is_pushing = FALSE;
10967 if (moved & MP_MOVING && !ScreenMovPos &&
10968 (player->index_nr == game.centered_player_nr ||
10969 game.centered_player_nr == -1))
10971 if (moved & MP_MOVING && !ScreenMovPos &&
10972 (player == local_player || !options.network))
10975 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
10976 int offset = (setup.scroll_delay ? 3 : 0);
10978 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
10980 /* actual player has left the screen -- scroll in that direction */
10981 if (jx != old_jx) /* player has moved horizontally */
10982 scroll_x += (jx - old_jx);
10983 else /* player has moved vertically */
10984 scroll_y += (jy - old_jy);
10988 if (jx != old_jx) /* player has moved horizontally */
10990 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
10991 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
10992 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
10994 /* don't scroll over playfield boundaries */
10995 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
10996 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
10998 /* don't scroll more than one field at a time */
10999 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
11001 /* don't scroll against the player's moving direction */
11002 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
11003 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
11004 scroll_x = old_scroll_x;
11006 else /* player has moved vertically */
11008 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
11009 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
11010 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
11012 /* don't scroll over playfield boundaries */
11013 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
11014 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
11016 /* don't scroll more than one field at a time */
11017 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
11019 /* don't scroll against the player's moving direction */
11020 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
11021 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
11022 scroll_y = old_scroll_y;
11026 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
11029 if (!options.network && game.centered_player_nr == -1 &&
11030 !AllPlayersInVisibleScreen())
11032 scroll_x = old_scroll_x;
11033 scroll_y = old_scroll_y;
11037 if (!options.network && !AllPlayersInVisibleScreen())
11039 scroll_x = old_scroll_x;
11040 scroll_y = old_scroll_y;
11045 ScrollScreen(player, SCROLL_INIT);
11046 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
11051 player->StepFrame = 0;
11053 if (moved & MP_MOVING)
11055 if (old_jx != jx && old_jy == jy)
11056 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
11057 else if (old_jx == jx && old_jy != jy)
11058 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
11060 DrawLevelField(jx, jy); /* for "crumbled sand" */
11062 player->last_move_dir = player->MovDir;
11063 player->is_moving = TRUE;
11064 player->is_snapping = FALSE;
11065 player->is_switching = FALSE;
11066 player->is_dropping = FALSE;
11067 player->is_dropping_pressed = FALSE;
11068 player->drop_pressed_delay = 0;
11071 /* should better be called here than above, but this breaks some tapes */
11072 ScrollPlayer(player, SCROLL_INIT);
11077 CheckGravityMovementWhenNotMoving(player);
11079 player->is_moving = FALSE;
11081 /* at this point, the player is allowed to move, but cannot move right now
11082 (e.g. because of something blocking the way) -- ensure that the player
11083 is also allowed to move in the next frame (in old versions before 3.1.1,
11084 the player was forced to wait again for eight frames before next try) */
11086 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
11087 player->move_delay = 0; /* allow direct movement in the next frame */
11090 if (player->move_delay == -1) /* not yet initialized by DigField() */
11091 player->move_delay = player->move_delay_value;
11093 if (game.engine_version < VERSION_IDENT(3,0,7,0))
11095 TestIfPlayerTouchesBadThing(jx, jy);
11096 TestIfPlayerTouchesCustomElement(jx, jy);
11099 if (!player->active)
11100 RemovePlayer(player);
11105 void ScrollPlayer(struct PlayerInfo *player, int mode)
11107 int jx = player->jx, jy = player->jy;
11108 int last_jx = player->last_jx, last_jy = player->last_jy;
11109 int move_stepsize = TILEX / player->move_delay_value;
11111 #if USE_NEW_PLAYER_SPEED
11112 if (!player->active)
11115 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
11118 if (!player->active || player->MovPos == 0)
11122 if (mode == SCROLL_INIT)
11124 player->actual_frame_counter = FrameCounter;
11125 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
11127 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
11128 Feld[last_jx][last_jy] == EL_EMPTY)
11130 int last_field_block_delay = 0; /* start with no blocking at all */
11131 int block_delay_adjustment = player->block_delay_adjustment;
11133 /* if player blocks last field, add delay for exactly one move */
11134 if (player->block_last_field)
11136 last_field_block_delay += player->move_delay_value;
11138 /* when blocking enabled, prevent moving up despite gravity */
11139 #if USE_PLAYER_GRAVITY
11140 if (player->gravity && player->MovDir == MV_UP)
11141 block_delay_adjustment = -1;
11143 if (game.gravity && player->MovDir == MV_UP)
11144 block_delay_adjustment = -1;
11148 /* add block delay adjustment (also possible when not blocking) */
11149 last_field_block_delay += block_delay_adjustment;
11151 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
11152 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
11155 #if USE_NEW_PLAYER_SPEED
11156 if (player->MovPos != 0) /* player has not yet reached destination */
11162 else if (!FrameReached(&player->actual_frame_counter, 1))
11165 #if USE_NEW_PLAYER_SPEED
11166 if (player->MovPos != 0)
11168 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
11169 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
11171 /* before DrawPlayer() to draw correct player graphic for this case */
11172 if (player->MovPos == 0)
11173 CheckGravityMovement(player);
11176 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
11177 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
11179 /* before DrawPlayer() to draw correct player graphic for this case */
11180 if (player->MovPos == 0)
11181 CheckGravityMovement(player);
11184 if (player->MovPos == 0) /* player reached destination field */
11186 if (player->move_delay_reset_counter > 0)
11188 player->move_delay_reset_counter--;
11190 if (player->move_delay_reset_counter == 0)
11192 /* continue with normal speed after quickly moving through gate */
11193 HALVE_PLAYER_SPEED(player);
11195 /* be able to make the next move without delay */
11196 player->move_delay = 0;
11200 player->last_jx = jx;
11201 player->last_jy = jy;
11203 if (Feld[jx][jy] == EL_EXIT_OPEN ||
11204 Feld[jx][jy] == EL_EM_EXIT_OPEN ||
11205 Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
11206 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
11207 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
11208 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
11210 DrawPlayer(player); /* needed here only to cleanup last field */
11211 RemovePlayer(player);
11213 if (local_player->friends_still_needed == 0 ||
11214 IS_SP_ELEMENT(Feld[jx][jy]))
11215 PlayerWins(player);
11218 /* this breaks one level: "machine", level 000 */
11220 int move_direction = player->MovDir;
11221 int enter_side = MV_DIR_OPPOSITE(move_direction);
11222 int leave_side = move_direction;
11223 int old_jx = last_jx;
11224 int old_jy = last_jy;
11225 int old_element = Feld[old_jx][old_jy];
11226 int new_element = Feld[jx][jy];
11228 if (IS_CUSTOM_ELEMENT(old_element))
11229 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
11231 player->index_bit, leave_side);
11233 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
11234 CE_PLAYER_LEAVES_X,
11235 player->index_bit, leave_side);
11237 if (IS_CUSTOM_ELEMENT(new_element))
11238 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
11239 player->index_bit, enter_side);
11241 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
11242 CE_PLAYER_ENTERS_X,
11243 player->index_bit, enter_side);
11245 CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
11246 CE_MOVE_OF_X, move_direction);
11249 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
11251 TestIfPlayerTouchesBadThing(jx, jy);
11252 TestIfPlayerTouchesCustomElement(jx, jy);
11254 /* needed because pushed element has not yet reached its destination,
11255 so it would trigger a change event at its previous field location */
11256 if (!player->is_pushing)
11257 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
11259 if (!player->active)
11260 RemovePlayer(player);
11263 if (!local_player->LevelSolved && level.use_step_counter)
11273 if (TimeLeft <= 10 && setup.time_limit)
11274 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11276 DrawGameValue_Time(TimeLeft);
11278 if (!TimeLeft && setup.time_limit)
11279 for (i = 0; i < MAX_PLAYERS; i++)
11280 KillPlayer(&stored_player[i]);
11282 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
11283 DrawGameValue_Time(TimePlayed);
11286 if (tape.single_step && tape.recording && !tape.pausing &&
11287 !player->programmed_action)
11288 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
11292 void ScrollScreen(struct PlayerInfo *player, int mode)
11294 static unsigned long screen_frame_counter = 0;
11296 if (mode == SCROLL_INIT)
11298 /* set scrolling step size according to actual player's moving speed */
11299 ScrollStepSize = TILEX / player->move_delay_value;
11301 screen_frame_counter = FrameCounter;
11302 ScreenMovDir = player->MovDir;
11303 ScreenMovPos = player->MovPos;
11304 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
11307 else if (!FrameReached(&screen_frame_counter, 1))
11312 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
11313 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
11314 redraw_mask |= REDRAW_FIELD;
11317 ScreenMovDir = MV_NONE;
11320 void TestIfPlayerTouchesCustomElement(int x, int y)
11322 static int xy[4][2] =
11329 static int trigger_sides[4][2] =
11331 /* center side border side */
11332 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
11333 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
11334 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
11335 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
11337 static int touch_dir[4] =
11339 MV_LEFT | MV_RIGHT,
11344 int center_element = Feld[x][y]; /* should always be non-moving! */
11347 for (i = 0; i < NUM_DIRECTIONS; i++)
11349 int xx = x + xy[i][0];
11350 int yy = y + xy[i][1];
11351 int center_side = trigger_sides[i][0];
11352 int border_side = trigger_sides[i][1];
11353 int border_element;
11355 if (!IN_LEV_FIELD(xx, yy))
11358 if (IS_PLAYER(x, y))
11360 struct PlayerInfo *player = PLAYERINFO(x, y);
11362 if (game.engine_version < VERSION_IDENT(3,0,7,0))
11363 border_element = Feld[xx][yy]; /* may be moving! */
11364 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
11365 border_element = Feld[xx][yy];
11366 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
11367 border_element = MovingOrBlocked2Element(xx, yy);
11369 continue; /* center and border element do not touch */
11371 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
11372 player->index_bit, border_side);
11373 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
11374 CE_PLAYER_TOUCHES_X,
11375 player->index_bit, border_side);
11377 else if (IS_PLAYER(xx, yy))
11379 struct PlayerInfo *player = PLAYERINFO(xx, yy);
11381 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
11383 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
11384 continue; /* center and border element do not touch */
11387 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
11388 player->index_bit, center_side);
11389 CheckTriggeredElementChangeByPlayer(x, y, center_element,
11390 CE_PLAYER_TOUCHES_X,
11391 player->index_bit, center_side);
11397 #if USE_ELEMENT_TOUCHING_BUGFIX
11399 void TestIfElementTouchesCustomElement(int x, int y)
11401 static int xy[4][2] =
11408 static int trigger_sides[4][2] =
11410 /* center side border side */
11411 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
11412 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
11413 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
11414 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
11416 static int touch_dir[4] =
11418 MV_LEFT | MV_RIGHT,
11423 boolean change_center_element = FALSE;
11424 int center_element = Feld[x][y]; /* should always be non-moving! */
11425 int border_element_old[NUM_DIRECTIONS];
11428 for (i = 0; i < NUM_DIRECTIONS; i++)
11430 int xx = x + xy[i][0];
11431 int yy = y + xy[i][1];
11432 int border_element;
11434 border_element_old[i] = -1;
11436 if (!IN_LEV_FIELD(xx, yy))
11439 if (game.engine_version < VERSION_IDENT(3,0,7,0))
11440 border_element = Feld[xx][yy]; /* may be moving! */
11441 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
11442 border_element = Feld[xx][yy];
11443 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
11444 border_element = MovingOrBlocked2Element(xx, yy);
11446 continue; /* center and border element do not touch */
11448 border_element_old[i] = border_element;
11451 for (i = 0; i < NUM_DIRECTIONS; i++)
11453 int xx = x + xy[i][0];
11454 int yy = y + xy[i][1];
11455 int center_side = trigger_sides[i][0];
11456 int border_element = border_element_old[i];
11458 if (border_element == -1)
11461 /* check for change of border element */
11462 CheckElementChangeBySide(xx, yy, border_element, center_element,
11463 CE_TOUCHING_X, center_side);
11466 for (i = 0; i < NUM_DIRECTIONS; i++)
11468 int border_side = trigger_sides[i][1];
11469 int border_element = border_element_old[i];
11471 if (border_element == -1)
11474 /* check for change of center element (but change it only once) */
11475 if (!change_center_element)
11476 change_center_element =
11477 CheckElementChangeBySide(x, y, center_element, border_element,
11478 CE_TOUCHING_X, border_side);
11484 void TestIfElementTouchesCustomElement_OLD(int x, int y)
11486 static int xy[4][2] =
11493 static int trigger_sides[4][2] =
11495 /* center side border side */
11496 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
11497 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
11498 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
11499 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
11501 static int touch_dir[4] =
11503 MV_LEFT | MV_RIGHT,
11508 boolean change_center_element = FALSE;
11509 int center_element = Feld[x][y]; /* should always be non-moving! */
11512 for (i = 0; i < NUM_DIRECTIONS; i++)
11514 int xx = x + xy[i][0];
11515 int yy = y + xy[i][1];
11516 int center_side = trigger_sides[i][0];
11517 int border_side = trigger_sides[i][1];
11518 int border_element;
11520 if (!IN_LEV_FIELD(xx, yy))
11523 if (game.engine_version < VERSION_IDENT(3,0,7,0))
11524 border_element = Feld[xx][yy]; /* may be moving! */
11525 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
11526 border_element = Feld[xx][yy];
11527 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
11528 border_element = MovingOrBlocked2Element(xx, yy);
11530 continue; /* center and border element do not touch */
11532 /* check for change of center element (but change it only once) */
11533 if (!change_center_element)
11534 change_center_element =
11535 CheckElementChangeBySide(x, y, center_element, border_element,
11536 CE_TOUCHING_X, border_side);
11538 /* check for change of border element */
11539 CheckElementChangeBySide(xx, yy, border_element, center_element,
11540 CE_TOUCHING_X, center_side);
11546 void TestIfElementHitsCustomElement(int x, int y, int direction)
11548 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
11549 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
11550 int hitx = x + dx, hity = y + dy;
11551 int hitting_element = Feld[x][y];
11552 int touched_element;
11554 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
11557 touched_element = (IN_LEV_FIELD(hitx, hity) ?
11558 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
11560 if (IN_LEV_FIELD(hitx, hity))
11562 int opposite_direction = MV_DIR_OPPOSITE(direction);
11563 int hitting_side = direction;
11564 int touched_side = opposite_direction;
11565 boolean object_hit = (!IS_MOVING(hitx, hity) ||
11566 MovDir[hitx][hity] != direction ||
11567 ABS(MovPos[hitx][hity]) <= TILEY / 2);
11573 CheckElementChangeBySide(x, y, hitting_element, touched_element,
11574 CE_HITTING_X, touched_side);
11576 CheckElementChangeBySide(hitx, hity, touched_element,
11577 hitting_element, CE_HIT_BY_X, hitting_side);
11579 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
11580 CE_HIT_BY_SOMETHING, opposite_direction);
11584 /* "hitting something" is also true when hitting the playfield border */
11585 CheckElementChangeBySide(x, y, hitting_element, touched_element,
11586 CE_HITTING_SOMETHING, direction);
11590 void TestIfElementSmashesCustomElement(int x, int y, int direction)
11592 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
11593 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
11594 int hitx = x + dx, hity = y + dy;
11595 int hitting_element = Feld[x][y];
11596 int touched_element;
11598 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
11599 !IS_FREE(hitx, hity) &&
11600 (!IS_MOVING(hitx, hity) ||
11601 MovDir[hitx][hity] != direction ||
11602 ABS(MovPos[hitx][hity]) <= TILEY / 2));
11605 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
11609 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
11613 touched_element = (IN_LEV_FIELD(hitx, hity) ?
11614 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
11616 CheckElementChangeBySide(x, y, hitting_element, touched_element,
11617 EP_CAN_SMASH_EVERYTHING, direction);
11619 if (IN_LEV_FIELD(hitx, hity))
11621 int opposite_direction = MV_DIR_OPPOSITE(direction);
11622 int hitting_side = direction;
11623 int touched_side = opposite_direction;
11625 int touched_element = MovingOrBlocked2Element(hitx, hity);
11628 boolean object_hit = (!IS_MOVING(hitx, hity) ||
11629 MovDir[hitx][hity] != direction ||
11630 ABS(MovPos[hitx][hity]) <= TILEY / 2);
11639 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
11640 CE_SMASHED_BY_SOMETHING, opposite_direction);
11642 CheckElementChangeBySide(x, y, hitting_element, touched_element,
11643 CE_OTHER_IS_SMASHING, touched_side);
11645 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
11646 CE_OTHER_GETS_SMASHED, hitting_side);
11652 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
11654 int i, kill_x = -1, kill_y = -1;
11656 int bad_element = -1;
11657 static int test_xy[4][2] =
11664 static int test_dir[4] =
11672 for (i = 0; i < NUM_DIRECTIONS; i++)
11674 int test_x, test_y, test_move_dir, test_element;
11676 test_x = good_x + test_xy[i][0];
11677 test_y = good_y + test_xy[i][1];
11679 if (!IN_LEV_FIELD(test_x, test_y))
11683 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
11685 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
11687 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
11688 2nd case: DONT_TOUCH style bad thing does not move away from good thing
11690 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
11691 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
11695 bad_element = test_element;
11701 if (kill_x != -1 || kill_y != -1)
11703 if (IS_PLAYER(good_x, good_y))
11705 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
11707 if (player->shield_deadly_time_left > 0 &&
11708 !IS_INDESTRUCTIBLE(bad_element))
11709 Bang(kill_x, kill_y);
11710 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
11711 KillPlayer(player);
11714 Bang(good_x, good_y);
11718 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
11720 int i, kill_x = -1, kill_y = -1;
11721 int bad_element = Feld[bad_x][bad_y];
11722 static int test_xy[4][2] =
11729 static int touch_dir[4] =
11731 MV_LEFT | MV_RIGHT,
11736 static int test_dir[4] =
11744 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
11747 for (i = 0; i < NUM_DIRECTIONS; i++)
11749 int test_x, test_y, test_move_dir, test_element;
11751 test_x = bad_x + test_xy[i][0];
11752 test_y = bad_y + test_xy[i][1];
11753 if (!IN_LEV_FIELD(test_x, test_y))
11757 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
11759 test_element = Feld[test_x][test_y];
11761 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
11762 2nd case: DONT_TOUCH style bad thing does not move away from good thing
11764 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
11765 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
11767 /* good thing is player or penguin that does not move away */
11768 if (IS_PLAYER(test_x, test_y))
11770 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
11772 if (bad_element == EL_ROBOT && player->is_moving)
11773 continue; /* robot does not kill player if he is moving */
11775 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
11777 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
11778 continue; /* center and border element do not touch */
11785 else if (test_element == EL_PENGUIN)
11794 if (kill_x != -1 || kill_y != -1)
11796 if (IS_PLAYER(kill_x, kill_y))
11798 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
11800 if (player->shield_deadly_time_left > 0 &&
11801 !IS_INDESTRUCTIBLE(bad_element))
11802 Bang(bad_x, bad_y);
11803 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
11804 KillPlayer(player);
11807 Bang(kill_x, kill_y);
11811 void TestIfPlayerTouchesBadThing(int x, int y)
11813 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
11816 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
11818 TestIfGoodThingHitsBadThing(x, y, move_dir);
11821 void TestIfBadThingTouchesPlayer(int x, int y)
11823 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
11826 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
11828 TestIfBadThingHitsGoodThing(x, y, move_dir);
11831 void TestIfFriendTouchesBadThing(int x, int y)
11833 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
11836 void TestIfBadThingTouchesFriend(int x, int y)
11838 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
11841 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
11843 int i, kill_x = bad_x, kill_y = bad_y;
11844 static int xy[4][2] =
11852 for (i = 0; i < NUM_DIRECTIONS; i++)
11856 x = bad_x + xy[i][0];
11857 y = bad_y + xy[i][1];
11858 if (!IN_LEV_FIELD(x, y))
11861 element = Feld[x][y];
11862 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
11863 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
11871 if (kill_x != bad_x || kill_y != bad_y)
11872 Bang(bad_x, bad_y);
11875 void KillPlayer(struct PlayerInfo *player)
11877 int jx = player->jx, jy = player->jy;
11879 if (!player->active)
11882 /* the following code was introduced to prevent an infinite loop when calling
11884 -> CheckTriggeredElementChangeExt()
11885 -> ExecuteCustomElementAction()
11887 -> (infinitely repeating the above sequence of function calls)
11888 which occurs when killing the player while having a CE with the setting
11889 "kill player X when explosion of <player X>"; the solution using a new
11890 field "player->killed" was chosen for backwards compatibility, although
11891 clever use of the fields "player->active" etc. would probably also work */
11893 if (player->killed)
11897 player->killed = TRUE;
11899 /* remove accessible field at the player's position */
11900 Feld[jx][jy] = EL_EMPTY;
11902 /* deactivate shield (else Bang()/Explode() would not work right) */
11903 player->shield_normal_time_left = 0;
11904 player->shield_deadly_time_left = 0;
11907 BuryPlayer(player);
11910 static void KillPlayerUnlessEnemyProtected(int x, int y)
11912 if (!PLAYER_ENEMY_PROTECTED(x, y))
11913 KillPlayer(PLAYERINFO(x, y));
11916 static void KillPlayerUnlessExplosionProtected(int x, int y)
11918 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
11919 KillPlayer(PLAYERINFO(x, y));
11922 void BuryPlayer(struct PlayerInfo *player)
11924 int jx = player->jx, jy = player->jy;
11926 if (!player->active)
11929 PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
11930 PlayLevelSound(jx, jy, SND_GAME_LOSING);
11932 player->GameOver = TRUE;
11933 RemovePlayer(player);
11936 void RemovePlayer(struct PlayerInfo *player)
11938 int jx = player->jx, jy = player->jy;
11939 int i, found = FALSE;
11941 player->present = FALSE;
11942 player->active = FALSE;
11944 if (!ExplodeField[jx][jy])
11945 StorePlayer[jx][jy] = 0;
11947 if (player->is_moving)
11948 DrawLevelField(player->last_jx, player->last_jy);
11950 for (i = 0; i < MAX_PLAYERS; i++)
11951 if (stored_player[i].active)
11955 AllPlayersGone = TRUE;
11961 #if USE_NEW_SNAP_DELAY
11962 static void setFieldForSnapping(int x, int y, int element, int direction)
11964 struct ElementInfo *ei = &element_info[element];
11965 int direction_bit = MV_DIR_TO_BIT(direction);
11966 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
11967 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
11968 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
11970 Feld[x][y] = EL_ELEMENT_SNAPPING;
11971 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
11973 ResetGfxAnimation(x, y);
11975 GfxElement[x][y] = element;
11976 GfxAction[x][y] = action;
11977 GfxDir[x][y] = direction;
11978 GfxFrame[x][y] = -1;
11983 =============================================================================
11984 checkDiagonalPushing()
11985 -----------------------------------------------------------------------------
11986 check if diagonal input device direction results in pushing of object
11987 (by checking if the alternative direction is walkable, diggable, ...)
11988 =============================================================================
11991 static boolean checkDiagonalPushing(struct PlayerInfo *player,
11992 int x, int y, int real_dx, int real_dy)
11994 int jx, jy, dx, dy, xx, yy;
11996 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
11999 /* diagonal direction: check alternative direction */
12004 xx = jx + (dx == 0 ? real_dx : 0);
12005 yy = jy + (dy == 0 ? real_dy : 0);
12007 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
12011 =============================================================================
12013 -----------------------------------------------------------------------------
12014 x, y: field next to player (non-diagonal) to try to dig to
12015 real_dx, real_dy: direction as read from input device (can be diagonal)
12016 =============================================================================
12019 int DigField(struct PlayerInfo *player,
12020 int oldx, int oldy, int x, int y,
12021 int real_dx, int real_dy, int mode)
12023 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
12024 boolean player_was_pushing = player->is_pushing;
12025 boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
12026 boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
12027 int jx = oldx, jy = oldy;
12028 int dx = x - jx, dy = y - jy;
12029 int nextx = x + dx, nexty = y + dy;
12030 int move_direction = (dx == -1 ? MV_LEFT :
12031 dx == +1 ? MV_RIGHT :
12033 dy == +1 ? MV_DOWN : MV_NONE);
12034 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
12035 int dig_side = MV_DIR_OPPOSITE(move_direction);
12036 int old_element = Feld[jx][jy];
12037 #if USE_FIXED_DONT_RUN_INTO
12038 int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
12044 if (is_player) /* function can also be called by EL_PENGUIN */
12046 if (player->MovPos == 0)
12048 player->is_digging = FALSE;
12049 player->is_collecting = FALSE;
12052 if (player->MovPos == 0) /* last pushing move finished */
12053 player->is_pushing = FALSE;
12055 if (mode == DF_NO_PUSH) /* player just stopped pushing */
12057 player->is_switching = FALSE;
12058 player->push_delay = -1;
12060 return MP_NO_ACTION;
12064 #if !USE_FIXED_DONT_RUN_INTO
12065 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
12066 return MP_NO_ACTION;
12069 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
12070 old_element = Back[jx][jy];
12072 /* in case of element dropped at player position, check background */
12073 else if (Back[jx][jy] != EL_EMPTY &&
12074 game.engine_version >= VERSION_IDENT(2,2,0,0))
12075 old_element = Back[jx][jy];
12077 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
12078 return MP_NO_ACTION; /* field has no opening in this direction */
12080 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
12081 return MP_NO_ACTION; /* field has no opening in this direction */
12083 #if USE_FIXED_DONT_RUN_INTO
12084 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
12088 Feld[jx][jy] = player->artwork_element;
12089 InitMovingField(jx, jy, MV_DOWN);
12090 Store[jx][jy] = EL_ACID;
12091 ContinueMoving(jx, jy);
12092 BuryPlayer(player);
12094 return MP_DONT_RUN_INTO;
12098 #if USE_FIXED_DONT_RUN_INTO
12099 if (player_can_move && DONT_RUN_INTO(element))
12101 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
12103 return MP_DONT_RUN_INTO;
12107 #if USE_FIXED_DONT_RUN_INTO
12108 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
12109 return MP_NO_ACTION;
12112 #if !USE_FIXED_DONT_RUN_INTO
12113 element = Feld[x][y];
12116 collect_count = element_info[element].collect_count_initial;
12118 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
12119 return MP_NO_ACTION;
12121 if (game.engine_version < VERSION_IDENT(2,2,0,0))
12122 player_can_move = player_can_move_or_snap;
12124 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
12125 game.engine_version >= VERSION_IDENT(2,2,0,0))
12127 CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
12128 player->index_bit, dig_side);
12129 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
12130 player->index_bit, dig_side);
12132 if (element == EL_DC_LANDMINE)
12135 if (Feld[x][y] != element) /* field changed by snapping */
12138 return MP_NO_ACTION;
12141 #if USE_PLAYER_GRAVITY
12142 if (player->gravity && is_player && !player->is_auto_moving &&
12143 canFallDown(player) && move_direction != MV_DOWN &&
12144 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
12145 return MP_NO_ACTION; /* player cannot walk here due to gravity */
12147 if (game.gravity && is_player && !player->is_auto_moving &&
12148 canFallDown(player) && move_direction != MV_DOWN &&
12149 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
12150 return MP_NO_ACTION; /* player cannot walk here due to gravity */
12153 if (player_can_move &&
12154 IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
12156 int sound_element = SND_ELEMENT(element);
12157 int sound_action = ACTION_WALKING;
12159 if (IS_RND_GATE(element))
12161 if (!player->key[RND_GATE_NR(element)])
12162 return MP_NO_ACTION;
12164 else if (IS_RND_GATE_GRAY(element))
12166 if (!player->key[RND_GATE_GRAY_NR(element)])
12167 return MP_NO_ACTION;
12169 else if (IS_RND_GATE_GRAY_ACTIVE(element))
12171 if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
12172 return MP_NO_ACTION;
12174 else if (element == EL_EXIT_OPEN ||
12175 element == EL_EM_EXIT_OPEN ||
12176 element == EL_STEEL_EXIT_OPEN ||
12177 element == EL_EM_STEEL_EXIT_OPEN ||
12178 element == EL_SP_EXIT_OPEN ||
12179 element == EL_SP_EXIT_OPENING)
12181 sound_action = ACTION_PASSING; /* player is passing exit */
12183 else if (element == EL_EMPTY)
12185 sound_action = ACTION_MOVING; /* nothing to walk on */
12188 /* play sound from background or player, whatever is available */
12189 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
12190 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
12192 PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
12194 else if (player_can_move &&
12195 IS_PASSABLE(element) && canPassField(x, y, move_direction))
12197 if (!ACCESS_FROM(element, opposite_direction))
12198 return MP_NO_ACTION; /* field not accessible from this direction */
12200 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
12201 return MP_NO_ACTION;
12203 if (IS_EM_GATE(element))
12205 if (!player->key[EM_GATE_NR(element)])
12206 return MP_NO_ACTION;
12208 else if (IS_EM_GATE_GRAY(element))
12210 if (!player->key[EM_GATE_GRAY_NR(element)])
12211 return MP_NO_ACTION;
12213 else if (IS_EM_GATE_GRAY_ACTIVE(element))
12215 if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
12216 return MP_NO_ACTION;
12218 else if (IS_EMC_GATE(element))
12220 if (!player->key[EMC_GATE_NR(element)])
12221 return MP_NO_ACTION;
12223 else if (IS_EMC_GATE_GRAY(element))
12225 if (!player->key[EMC_GATE_GRAY_NR(element)])
12226 return MP_NO_ACTION;
12228 else if (IS_EMC_GATE_GRAY_ACTIVE(element))
12230 if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
12231 return MP_NO_ACTION;
12233 else if (element == EL_DC_GATE_WHITE ||
12234 element == EL_DC_GATE_WHITE_GRAY ||
12235 element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
12237 if (player->num_white_keys == 0)
12238 return MP_NO_ACTION;
12240 player->num_white_keys--;
12242 else if (IS_SP_PORT(element))
12244 if (element == EL_SP_GRAVITY_PORT_LEFT ||
12245 element == EL_SP_GRAVITY_PORT_RIGHT ||
12246 element == EL_SP_GRAVITY_PORT_UP ||
12247 element == EL_SP_GRAVITY_PORT_DOWN)
12248 #if USE_PLAYER_GRAVITY
12249 player->gravity = !player->gravity;
12251 game.gravity = !game.gravity;
12253 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
12254 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
12255 element == EL_SP_GRAVITY_ON_PORT_UP ||
12256 element == EL_SP_GRAVITY_ON_PORT_DOWN)
12257 #if USE_PLAYER_GRAVITY
12258 player->gravity = TRUE;
12260 game.gravity = TRUE;
12262 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
12263 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
12264 element == EL_SP_GRAVITY_OFF_PORT_UP ||
12265 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
12266 #if USE_PLAYER_GRAVITY
12267 player->gravity = FALSE;
12269 game.gravity = FALSE;
12273 /* automatically move to the next field with double speed */
12274 player->programmed_action = move_direction;
12276 if (player->move_delay_reset_counter == 0)
12278 player->move_delay_reset_counter = 2; /* two double speed steps */
12280 DOUBLE_PLAYER_SPEED(player);
12283 PlayLevelSoundAction(x, y, ACTION_PASSING);
12285 else if (player_can_move_or_snap && IS_DIGGABLE(element))
12289 if (mode != DF_SNAP)
12291 GfxElement[x][y] = GFX_ELEMENT(element);
12292 player->is_digging = TRUE;
12295 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
12297 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
12298 player->index_bit, dig_side);
12300 if (mode == DF_SNAP)
12302 #if USE_NEW_SNAP_DELAY
12303 if (level.block_snap_field)
12304 setFieldForSnapping(x, y, element, move_direction);
12306 TestIfElementTouchesCustomElement(x, y); /* for empty space */
12308 TestIfElementTouchesCustomElement(x, y); /* for empty space */
12311 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
12312 player->index_bit, dig_side);
12315 else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
12319 if (is_player && mode != DF_SNAP)
12321 GfxElement[x][y] = element;
12322 player->is_collecting = TRUE;
12325 if (element == EL_SPEED_PILL)
12327 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
12329 else if (element == EL_EXTRA_TIME && level.time > 0)
12331 TimeLeft += level.extra_time;
12332 DrawGameValue_Time(TimeLeft);
12334 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
12336 player->shield_normal_time_left += level.shield_normal_time;
12337 if (element == EL_SHIELD_DEADLY)
12338 player->shield_deadly_time_left += level.shield_deadly_time;
12340 else if (element == EL_DYNAMITE ||
12341 element == EL_EM_DYNAMITE ||
12342 element == EL_SP_DISK_RED)
12344 if (player->inventory_size < MAX_INVENTORY_SIZE)
12345 player->inventory_element[player->inventory_size++] = element;
12347 DrawGameDoorValues();
12349 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
12351 player->dynabomb_count++;
12352 player->dynabombs_left++;
12354 else if (element == EL_DYNABOMB_INCREASE_SIZE)
12356 player->dynabomb_size++;
12358 else if (element == EL_DYNABOMB_INCREASE_POWER)
12360 player->dynabomb_xl = TRUE;
12362 else if (IS_KEY(element))
12364 player->key[KEY_NR(element)] = TRUE;
12366 DrawGameDoorValues();
12368 else if (element == EL_DC_KEY_WHITE)
12370 player->num_white_keys++;
12372 /* display white keys? */
12373 /* DrawGameDoorValues(); */
12375 else if (IS_ENVELOPE(element))
12377 player->show_envelope = element;
12379 else if (element == EL_EMC_LENSES)
12381 game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
12383 RedrawAllInvisibleElementsForLenses();
12385 else if (element == EL_EMC_MAGNIFIER)
12387 game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
12389 RedrawAllInvisibleElementsForMagnifier();
12391 else if (IS_DROPPABLE(element) ||
12392 IS_THROWABLE(element)) /* can be collected and dropped */
12396 if (collect_count == 0)
12397 player->inventory_infinite_element = element;
12399 for (i = 0; i < collect_count; i++)
12400 if (player->inventory_size < MAX_INVENTORY_SIZE)
12401 player->inventory_element[player->inventory_size++] = element;
12403 DrawGameDoorValues();
12405 else if (collect_count > 0)
12407 local_player->gems_still_needed -= collect_count;
12408 if (local_player->gems_still_needed < 0)
12409 local_player->gems_still_needed = 0;
12411 DrawGameValue_Emeralds(local_player->gems_still_needed);
12414 RaiseScoreElement(element);
12415 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
12418 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
12419 player->index_bit, dig_side);
12421 if (mode == DF_SNAP)
12423 #if USE_NEW_SNAP_DELAY
12424 if (level.block_snap_field)
12425 setFieldForSnapping(x, y, element, move_direction);
12427 TestIfElementTouchesCustomElement(x, y); /* for empty space */
12429 TestIfElementTouchesCustomElement(x, y); /* for empty space */
12432 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
12433 player->index_bit, dig_side);
12436 else if (player_can_move_or_snap && IS_PUSHABLE(element))
12438 if (mode == DF_SNAP && element != EL_BD_ROCK)
12439 return MP_NO_ACTION;
12441 if (CAN_FALL(element) && dy)
12442 return MP_NO_ACTION;
12444 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
12445 !(element == EL_SPRING && level.use_spring_bug))
12446 return MP_NO_ACTION;
12448 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
12449 ((move_direction & MV_VERTICAL &&
12450 ((element_info[element].move_pattern & MV_LEFT &&
12451 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
12452 (element_info[element].move_pattern & MV_RIGHT &&
12453 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
12454 (move_direction & MV_HORIZONTAL &&
12455 ((element_info[element].move_pattern & MV_UP &&
12456 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
12457 (element_info[element].move_pattern & MV_DOWN &&
12458 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
12459 return MP_NO_ACTION;
12461 /* do not push elements already moving away faster than player */
12462 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
12463 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
12464 return MP_NO_ACTION;
12466 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
12468 if (player->push_delay_value == -1 || !player_was_pushing)
12469 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12471 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
12473 if (player->push_delay_value == -1)
12474 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12476 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
12478 if (!player->is_pushing)
12479 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12482 player->is_pushing = TRUE;
12483 player->is_active = TRUE;
12485 if (!(IN_LEV_FIELD(nextx, nexty) &&
12486 (IS_FREE(nextx, nexty) ||
12487 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
12488 IS_SB_ELEMENT(element)))))
12489 return MP_NO_ACTION;
12491 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
12492 return MP_NO_ACTION;
12494 if (player->push_delay == -1) /* new pushing; restart delay */
12495 player->push_delay = 0;
12497 if (player->push_delay < player->push_delay_value &&
12498 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
12499 element != EL_SPRING && element != EL_BALLOON)
12501 /* make sure that there is no move delay before next try to push */
12502 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
12503 player->move_delay = 0;
12505 return MP_NO_ACTION;
12508 if (IS_SB_ELEMENT(element))
12510 if (element == EL_SOKOBAN_FIELD_FULL)
12512 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
12513 local_player->sokobanfields_still_needed++;
12516 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
12518 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
12519 local_player->sokobanfields_still_needed--;
12522 Feld[x][y] = EL_SOKOBAN_OBJECT;
12524 if (Back[x][y] == Back[nextx][nexty])
12525 PlayLevelSoundAction(x, y, ACTION_PUSHING);
12526 else if (Back[x][y] != 0)
12527 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
12530 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
12533 if (local_player->sokobanfields_still_needed == 0 &&
12534 game.emulation == EMU_SOKOBAN)
12536 PlayerWins(player);
12538 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
12542 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12544 InitMovingField(x, y, move_direction);
12545 GfxAction[x][y] = ACTION_PUSHING;
12547 if (mode == DF_SNAP)
12548 ContinueMoving(x, y);
12550 MovPos[x][y] = (dx != 0 ? dx : dy);
12552 Pushed[x][y] = TRUE;
12553 Pushed[nextx][nexty] = TRUE;
12555 if (game.engine_version < VERSION_IDENT(2,2,0,7))
12556 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12558 player->push_delay_value = -1; /* get new value later */
12560 /* check for element change _after_ element has been pushed */
12561 if (game.use_change_when_pushing_bug)
12563 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
12564 player->index_bit, dig_side);
12565 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
12566 player->index_bit, dig_side);
12569 else if (IS_SWITCHABLE(element))
12571 if (PLAYER_SWITCHING(player, x, y))
12573 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
12574 player->index_bit, dig_side);
12579 player->is_switching = TRUE;
12580 player->switch_x = x;
12581 player->switch_y = y;
12583 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
12585 if (element == EL_ROBOT_WHEEL)
12587 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
12591 DrawLevelField(x, y);
12593 else if (element == EL_SP_TERMINAL)
12597 SCAN_PLAYFIELD(xx, yy)
12599 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
12601 else if (Feld[xx][yy] == EL_SP_TERMINAL)
12602 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
12605 else if (IS_BELT_SWITCH(element))
12607 ToggleBeltSwitch(x, y);
12609 else if (element == EL_SWITCHGATE_SWITCH_UP ||
12610 element == EL_SWITCHGATE_SWITCH_DOWN ||
12611 element == EL_DC_SWITCHGATE_SWITCH_UP ||
12612 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
12614 ToggleSwitchgateSwitch(x, y);
12616 else if (element == EL_LIGHT_SWITCH ||
12617 element == EL_LIGHT_SWITCH_ACTIVE)
12619 ToggleLightSwitch(x, y);
12621 else if (element == EL_TIMEGATE_SWITCH ||
12622 element == EL_DC_TIMEGATE_SWITCH)
12624 ActivateTimegateSwitch(x, y);
12626 else if (element == EL_BALLOON_SWITCH_LEFT ||
12627 element == EL_BALLOON_SWITCH_RIGHT ||
12628 element == EL_BALLOON_SWITCH_UP ||
12629 element == EL_BALLOON_SWITCH_DOWN ||
12630 element == EL_BALLOON_SWITCH_NONE ||
12631 element == EL_BALLOON_SWITCH_ANY)
12633 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
12634 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
12635 element == EL_BALLOON_SWITCH_UP ? MV_UP :
12636 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
12637 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
12640 else if (element == EL_LAMP)
12642 Feld[x][y] = EL_LAMP_ACTIVE;
12643 local_player->lights_still_needed--;
12645 ResetGfxAnimation(x, y);
12646 DrawLevelField(x, y);
12648 else if (element == EL_TIME_ORB_FULL)
12650 Feld[x][y] = EL_TIME_ORB_EMPTY;
12652 if (level.time > 0 || level.use_time_orb_bug)
12654 TimeLeft += level.time_orb_time;
12655 DrawGameValue_Time(TimeLeft);
12658 ResetGfxAnimation(x, y);
12659 DrawLevelField(x, y);
12661 else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
12662 element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
12666 game.ball_state = !game.ball_state;
12668 SCAN_PLAYFIELD(xx, yy)
12670 int e = Feld[xx][yy];
12672 if (game.ball_state)
12674 if (e == EL_EMC_MAGIC_BALL)
12675 CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
12676 else if (e == EL_EMC_MAGIC_BALL_SWITCH)
12677 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
12681 if (e == EL_EMC_MAGIC_BALL_ACTIVE)
12682 CreateField(xx, yy, EL_EMC_MAGIC_BALL);
12683 else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
12684 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
12689 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
12690 player->index_bit, dig_side);
12692 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
12693 player->index_bit, dig_side);
12695 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
12696 player->index_bit, dig_side);
12702 if (!PLAYER_SWITCHING(player, x, y))
12704 player->is_switching = TRUE;
12705 player->switch_x = x;
12706 player->switch_y = y;
12708 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
12709 player->index_bit, dig_side);
12710 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
12711 player->index_bit, dig_side);
12713 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
12714 player->index_bit, dig_side);
12715 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
12716 player->index_bit, dig_side);
12719 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
12720 player->index_bit, dig_side);
12721 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
12722 player->index_bit, dig_side);
12724 return MP_NO_ACTION;
12727 player->push_delay = -1;
12729 if (is_player) /* function can also be called by EL_PENGUIN */
12731 if (Feld[x][y] != element) /* really digged/collected something */
12733 player->is_collecting = !player->is_digging;
12734 player->is_active = TRUE;
12741 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
12743 int jx = player->jx, jy = player->jy;
12744 int x = jx + dx, y = jy + dy;
12745 int snap_direction = (dx == -1 ? MV_LEFT :
12746 dx == +1 ? MV_RIGHT :
12748 dy == +1 ? MV_DOWN : MV_NONE);
12749 boolean can_continue_snapping = (level.continuous_snapping &&
12750 WasJustFalling[x][y] < CHECK_DELAY_FALLING);
12752 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
12755 if (!player->active || !IN_LEV_FIELD(x, y))
12763 if (player->MovPos == 0)
12764 player->is_pushing = FALSE;
12766 player->is_snapping = FALSE;
12768 if (player->MovPos == 0)
12770 player->is_moving = FALSE;
12771 player->is_digging = FALSE;
12772 player->is_collecting = FALSE;
12778 #if USE_NEW_CONTINUOUS_SNAPPING
12779 /* prevent snapping with already pressed snap key when not allowed */
12780 if (player->is_snapping && !can_continue_snapping)
12783 if (player->is_snapping)
12787 player->MovDir = snap_direction;
12789 if (player->MovPos == 0)
12791 player->is_moving = FALSE;
12792 player->is_digging = FALSE;
12793 player->is_collecting = FALSE;
12796 player->is_dropping = FALSE;
12797 player->is_dropping_pressed = FALSE;
12798 player->drop_pressed_delay = 0;
12800 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
12803 player->is_snapping = TRUE;
12804 player->is_active = TRUE;
12806 if (player->MovPos == 0)
12808 player->is_moving = FALSE;
12809 player->is_digging = FALSE;
12810 player->is_collecting = FALSE;
12813 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
12814 DrawLevelField(player->last_jx, player->last_jy);
12816 DrawLevelField(x, y);
12821 boolean DropElement(struct PlayerInfo *player)
12823 int old_element, new_element;
12824 int dropx = player->jx, dropy = player->jy;
12825 int drop_direction = player->MovDir;
12826 int drop_side = drop_direction;
12827 int drop_element = (player->inventory_size > 0 ?
12828 player->inventory_element[player->inventory_size - 1] :
12829 player->inventory_infinite_element != EL_UNDEFINED ?
12830 player->inventory_infinite_element :
12831 player->dynabombs_left > 0 ?
12832 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
12835 player->is_dropping_pressed = TRUE;
12837 /* do not drop an element on top of another element; when holding drop key
12838 pressed without moving, dropped element must move away before the next
12839 element can be dropped (this is especially important if the next element
12840 is dynamite, which can be placed on background for historical reasons) */
12841 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
12844 if (IS_THROWABLE(drop_element))
12846 dropx += GET_DX_FROM_DIR(drop_direction);
12847 dropy += GET_DY_FROM_DIR(drop_direction);
12849 if (!IN_LEV_FIELD(dropx, dropy))
12853 old_element = Feld[dropx][dropy]; /* old element at dropping position */
12854 new_element = drop_element; /* default: no change when dropping */
12856 /* check if player is active, not moving and ready to drop */
12857 if (!player->active || player->MovPos || player->drop_delay > 0)
12860 /* check if player has anything that can be dropped */
12861 if (new_element == EL_UNDEFINED)
12864 /* check if drop key was pressed long enough for EM style dynamite */
12865 if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
12868 /* check if anything can be dropped at the current position */
12869 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
12872 /* collected custom elements can only be dropped on empty fields */
12873 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
12876 if (old_element != EL_EMPTY)
12877 Back[dropx][dropy] = old_element; /* store old element on this field */
12879 ResetGfxAnimation(dropx, dropy);
12880 ResetRandomAnimationValue(dropx, dropy);
12882 if (player->inventory_size > 0 ||
12883 player->inventory_infinite_element != EL_UNDEFINED)
12885 if (player->inventory_size > 0)
12887 player->inventory_size--;
12889 DrawGameDoorValues();
12891 if (new_element == EL_DYNAMITE)
12892 new_element = EL_DYNAMITE_ACTIVE;
12893 else if (new_element == EL_EM_DYNAMITE)
12894 new_element = EL_EM_DYNAMITE_ACTIVE;
12895 else if (new_element == EL_SP_DISK_RED)
12896 new_element = EL_SP_DISK_RED_ACTIVE;
12899 Feld[dropx][dropy] = new_element;
12901 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
12902 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
12903 el2img(Feld[dropx][dropy]), 0);
12905 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
12907 /* needed if previous element just changed to "empty" in the last frame */
12908 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
12910 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
12911 player->index_bit, drop_side);
12912 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
12914 player->index_bit, drop_side);
12916 TestIfElementTouchesCustomElement(dropx, dropy);
12918 else /* player is dropping a dyna bomb */
12920 player->dynabombs_left--;
12922 Feld[dropx][dropy] = new_element;
12924 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
12925 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
12926 el2img(Feld[dropx][dropy]), 0);
12928 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
12931 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
12932 InitField_WithBug1(dropx, dropy, FALSE);
12934 new_element = Feld[dropx][dropy]; /* element might have changed */
12936 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
12937 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
12939 int move_direction, nextx, nexty;
12941 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
12942 MovDir[dropx][dropy] = drop_direction;
12944 move_direction = MovDir[dropx][dropy];
12945 nextx = dropx + GET_DX_FROM_DIR(move_direction);
12946 nexty = dropy + GET_DY_FROM_DIR(move_direction);
12948 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
12950 #if USE_FIX_IMPACT_COLLISION
12951 /* do not cause impact style collision by dropping elements that can fall */
12952 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
12954 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
12958 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
12959 player->is_dropping = TRUE;
12961 player->drop_pressed_delay = 0;
12962 player->is_dropping_pressed = FALSE;
12964 player->drop_x = dropx;
12965 player->drop_y = dropy;
12970 /* ------------------------------------------------------------------------- */
12971 /* game sound playing functions */
12972 /* ------------------------------------------------------------------------- */
12974 static int *loop_sound_frame = NULL;
12975 static int *loop_sound_volume = NULL;
12977 void InitPlayLevelSound()
12979 int num_sounds = getSoundListSize();
12981 checked_free(loop_sound_frame);
12982 checked_free(loop_sound_volume);
12984 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
12985 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
12988 static void PlayLevelSound(int x, int y, int nr)
12990 int sx = SCREENX(x), sy = SCREENY(y);
12991 int volume, stereo_position;
12992 int max_distance = 8;
12993 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
12995 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
12996 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
12999 if (!IN_LEV_FIELD(x, y) ||
13000 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
13001 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
13004 volume = SOUND_MAX_VOLUME;
13006 if (!IN_SCR_FIELD(sx, sy))
13008 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
13009 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
13011 volume -= volume * (dx > dy ? dx : dy) / max_distance;
13014 stereo_position = (SOUND_MAX_LEFT +
13015 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
13016 (SCR_FIELDX + 2 * max_distance));
13018 if (IS_LOOP_SOUND(nr))
13020 /* This assures that quieter loop sounds do not overwrite louder ones,
13021 while restarting sound volume comparison with each new game frame. */
13023 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
13026 loop_sound_volume[nr] = volume;
13027 loop_sound_frame[nr] = FrameCounter;
13030 PlaySoundExt(nr, volume, stereo_position, type);
13033 static void PlayLevelSoundNearest(int x, int y, int sound_action)
13035 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
13036 x > LEVELX(BX2) ? LEVELX(BX2) : x,
13037 y < LEVELY(BY1) ? LEVELY(BY1) :
13038 y > LEVELY(BY2) ? LEVELY(BY2) : y,
13042 static void PlayLevelSoundAction(int x, int y, int action)
13044 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
13047 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
13049 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
13051 if (sound_effect != SND_UNDEFINED)
13052 PlayLevelSound(x, y, sound_effect);
13055 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
13058 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
13060 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
13061 PlayLevelSound(x, y, sound_effect);
13064 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
13066 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
13068 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
13069 PlayLevelSound(x, y, sound_effect);
13072 static void StopLevelSoundActionIfLoop(int x, int y, int action)
13074 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
13076 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
13077 StopSound(sound_effect);
13080 static void PlayLevelMusic()
13082 if (levelset.music[level_nr] != MUS_UNDEFINED)
13083 PlayMusic(levelset.music[level_nr]); /* from config file */
13085 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
13088 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
13090 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
13091 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
13092 int x = xx - 1 - offset;
13093 int y = yy - 1 - offset;
13098 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
13102 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13106 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
13110 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
13114 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
13118 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
13122 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
13125 case SAMPLE_android_clone:
13126 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
13129 case SAMPLE_android_move:
13130 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
13133 case SAMPLE_spring:
13134 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
13138 PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
13142 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
13145 case SAMPLE_eater_eat:
13146 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13150 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
13153 case SAMPLE_collect:
13154 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13157 case SAMPLE_diamond:
13158 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
13161 case SAMPLE_squash:
13162 /* !!! CHECK THIS !!! */
13164 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
13166 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
13170 case SAMPLE_wonderfall:
13171 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
13175 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
13179 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13183 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13187 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
13191 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
13195 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
13198 case SAMPLE_wonder:
13199 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
13203 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
13206 case SAMPLE_exit_open:
13207 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
13210 case SAMPLE_exit_leave:
13211 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
13214 case SAMPLE_dynamite:
13215 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
13219 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
13223 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
13227 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
13231 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
13235 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
13239 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
13243 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
13249 void ChangeTime(int value)
13251 int *time = (level.time == 0 ? &TimePlayed : &TimeLeft);
13255 /* EMC game engine uses value from time counter of RND game engine */
13256 level.native_em_level->lev->time = *time;
13258 DrawGameValue_Time(*time);
13261 void RaiseScore(int value)
13263 /* EMC game engine and RND game engine have separate score counters */
13264 int *score = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
13265 &level.native_em_level->lev->score : &local_player->score);
13269 DrawGameValue_Score(*score);
13273 void RaiseScore(int value)
13275 local_player->score += value;
13277 DrawGameValue_Score(local_player->score);
13280 void RaiseScoreElement(int element)
13285 case EL_BD_DIAMOND:
13286 case EL_EMERALD_YELLOW:
13287 case EL_EMERALD_RED:
13288 case EL_EMERALD_PURPLE:
13289 case EL_SP_INFOTRON:
13290 RaiseScore(level.score[SC_EMERALD]);
13293 RaiseScore(level.score[SC_DIAMOND]);
13296 RaiseScore(level.score[SC_CRYSTAL]);
13299 RaiseScore(level.score[SC_PEARL]);
13302 case EL_BD_BUTTERFLY:
13303 case EL_SP_ELECTRON:
13304 RaiseScore(level.score[SC_BUG]);
13307 case EL_BD_FIREFLY:
13308 case EL_SP_SNIKSNAK:
13309 RaiseScore(level.score[SC_SPACESHIP]);
13312 case EL_DARK_YAMYAM:
13313 RaiseScore(level.score[SC_YAMYAM]);
13316 RaiseScore(level.score[SC_ROBOT]);
13319 RaiseScore(level.score[SC_PACMAN]);
13322 RaiseScore(level.score[SC_NUT]);
13325 case EL_EM_DYNAMITE:
13326 case EL_SP_DISK_RED:
13327 case EL_DYNABOMB_INCREASE_NUMBER:
13328 case EL_DYNABOMB_INCREASE_SIZE:
13329 case EL_DYNABOMB_INCREASE_POWER:
13330 RaiseScore(level.score[SC_DYNAMITE]);
13332 case EL_SHIELD_NORMAL:
13333 case EL_SHIELD_DEADLY:
13334 RaiseScore(level.score[SC_SHIELD]);
13336 case EL_EXTRA_TIME:
13337 RaiseScore(level.extra_time_score);
13351 case EL_DC_KEY_WHITE:
13352 RaiseScore(level.score[SC_KEY]);
13355 RaiseScore(element_info[element].collect_score);
13360 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
13362 if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
13364 #if defined(NETWORK_AVALIABLE)
13365 if (options.network)
13366 SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
13372 game_status = GAME_MODE_MAIN;
13378 FadeOut(REDRAW_FIELD);
13380 game_status = GAME_MODE_MAIN;
13382 DrawAndFadeInMainMenu(REDRAW_FIELD);
13386 else /* continue playing the game */
13388 if (tape.playing && tape.deactivate_display)
13389 TapeDeactivateDisplayOff(TRUE);
13391 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
13393 if (tape.playing && tape.deactivate_display)
13394 TapeDeactivateDisplayOn();
13398 void RequestQuitGame(boolean ask_if_really_quit)
13400 boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
13401 boolean skip_request = AllPlayersGone || quick_quit;
13403 RequestQuitGameExt(skip_request, quick_quit,
13404 "Do you really want to quit the game ?");
13408 /* ------------------------------------------------------------------------- */
13409 /* random generator functions */
13410 /* ------------------------------------------------------------------------- */
13412 unsigned int InitEngineRandom_RND(long seed)
13414 game.num_random_calls = 0;
13417 unsigned int rnd_seed = InitEngineRandom(seed);
13419 printf("::: START RND: %d\n", rnd_seed);
13424 return InitEngineRandom(seed);
13430 unsigned int RND(int max)
13434 game.num_random_calls++;
13436 return GetEngineRandom(max);
13443 /* ------------------------------------------------------------------------- */
13444 /* game engine snapshot handling functions */
13445 /* ------------------------------------------------------------------------- */
13447 #define ARGS_ADDRESS_AND_SIZEOF(x) (&(x)), (sizeof(x))
13449 struct EngineSnapshotInfo
13451 /* runtime values for custom element collect score */
13452 int collect_score[NUM_CUSTOM_ELEMENTS];
13454 /* runtime values for group element choice position */
13455 int choice_pos[NUM_GROUP_ELEMENTS];
13457 /* runtime values for belt position animations */
13458 int belt_graphic[4 * NUM_BELT_PARTS];
13459 int belt_anim_mode[4 * NUM_BELT_PARTS];
13462 struct EngineSnapshotNodeInfo
13469 static struct EngineSnapshotInfo engine_snapshot_rnd;
13470 static ListNode *engine_snapshot_list = NULL;
13471 static char *snapshot_level_identifier = NULL;
13472 static int snapshot_level_nr = -1;
13474 void FreeEngineSnapshot()
13476 while (engine_snapshot_list != NULL)
13477 deleteNodeFromList(&engine_snapshot_list, engine_snapshot_list->key,
13480 setString(&snapshot_level_identifier, NULL);
13481 snapshot_level_nr = -1;
13484 static void SaveEngineSnapshotValues_RND()
13486 static int belt_base_active_element[4] =
13488 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
13489 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
13490 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
13491 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
13495 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
13497 int element = EL_CUSTOM_START + i;
13499 engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
13502 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
13504 int element = EL_GROUP_START + i;
13506 engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
13509 for (i = 0; i < 4; i++)
13511 for (j = 0; j < NUM_BELT_PARTS; j++)
13513 int element = belt_base_active_element[i] + j;
13514 int graphic = el2img(element);
13515 int anim_mode = graphic_info[graphic].anim_mode;
13517 engine_snapshot_rnd.belt_graphic[i * 4 + j] = graphic;
13518 engine_snapshot_rnd.belt_anim_mode[i * 4 + j] = anim_mode;
13523 static void LoadEngineSnapshotValues_RND()
13525 unsigned long num_random_calls = game.num_random_calls;
13528 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
13530 int element = EL_CUSTOM_START + i;
13532 element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
13535 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
13537 int element = EL_GROUP_START + i;
13539 element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
13542 for (i = 0; i < 4; i++)
13544 for (j = 0; j < NUM_BELT_PARTS; j++)
13546 int graphic = engine_snapshot_rnd.belt_graphic[i * 4 + j];
13547 int anim_mode = engine_snapshot_rnd.belt_anim_mode[i * 4 + j];
13549 graphic_info[graphic].anim_mode = anim_mode;
13553 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
13555 InitRND(tape.random_seed);
13556 for (i = 0; i < num_random_calls; i++)
13560 if (game.num_random_calls != num_random_calls)
13562 Error(ERR_RETURN, "number of random calls out of sync");
13563 Error(ERR_RETURN, "number of random calls should be %d", num_random_calls);
13564 Error(ERR_RETURN, "number of random calls is %d", game.num_random_calls);
13565 Error(ERR_EXIT, "this should not happen -- please debug");
13569 static void SaveEngineSnapshotBuffer(void *buffer, int size)
13571 struct EngineSnapshotNodeInfo *bi =
13572 checked_calloc(sizeof(struct EngineSnapshotNodeInfo));
13574 bi->buffer_orig = buffer;
13575 bi->buffer_copy = checked_malloc(size);
13578 memcpy(bi->buffer_copy, buffer, size);
13580 addNodeToList(&engine_snapshot_list, NULL, bi);
13583 void SaveEngineSnapshot()
13585 FreeEngineSnapshot(); /* free previous snapshot, if needed */
13587 if (level_editor_test_game) /* do not save snapshots from editor */
13590 /* copy some special values to a structure better suited for the snapshot */
13592 SaveEngineSnapshotValues_RND();
13593 SaveEngineSnapshotValues_EM();
13595 /* save values stored in special snapshot structure */
13597 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
13598 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
13600 /* save further RND engine values */
13602 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
13603 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
13604 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
13606 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
13607 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
13608 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
13609 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
13611 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
13612 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
13613 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
13614 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
13615 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
13617 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
13618 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
13619 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
13621 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
13623 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
13625 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
13626 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
13628 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
13629 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
13630 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
13631 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
13632 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
13633 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
13634 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
13635 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
13636 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
13637 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
13638 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
13639 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
13640 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
13641 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
13642 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
13643 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
13644 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
13645 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
13647 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
13648 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
13650 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
13651 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
13652 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
13654 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
13655 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
13657 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
13658 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
13659 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
13660 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
13661 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
13663 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
13664 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
13666 /* save level identification information */
13668 setString(&snapshot_level_identifier, leveldir_current->identifier);
13669 snapshot_level_nr = level_nr;
13672 ListNode *node = engine_snapshot_list;
13675 while (node != NULL)
13677 num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
13682 printf("::: size of engine snapshot: %d bytes\n", num_bytes);
13686 static void LoadEngineSnapshotBuffer(struct EngineSnapshotNodeInfo *bi)
13688 memcpy(bi->buffer_orig, bi->buffer_copy, bi->size);
13691 void LoadEngineSnapshot()
13693 ListNode *node = engine_snapshot_list;
13695 if (engine_snapshot_list == NULL)
13698 while (node != NULL)
13700 LoadEngineSnapshotBuffer((struct EngineSnapshotNodeInfo *)node->content);
13705 /* restore special values from snapshot structure */
13707 LoadEngineSnapshotValues_RND();
13708 LoadEngineSnapshotValues_EM();
13711 boolean CheckEngineSnapshot()
13713 return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
13714 snapshot_level_nr == level_nr);
13718 /* ---------- new game button stuff ---------------------------------------- */
13720 /* graphic position values for game buttons */
13721 #define GAME_BUTTON_XSIZE 30
13722 #define GAME_BUTTON_YSIZE 30
13723 #define GAME_BUTTON_XPOS 5
13724 #define GAME_BUTTON_YPOS 215
13725 #define SOUND_BUTTON_XPOS 5
13726 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
13728 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
13729 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
13730 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
13731 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
13732 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
13733 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
13740 } gamebutton_info[NUM_GAME_BUTTONS] =
13743 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
13748 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
13749 GAME_CTRL_ID_PAUSE,
13753 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
13758 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
13759 SOUND_CTRL_ID_MUSIC,
13760 "background music on/off"
13763 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
13764 SOUND_CTRL_ID_LOOPS,
13765 "sound loops on/off"
13768 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
13769 SOUND_CTRL_ID_SIMPLE,
13770 "normal sounds on/off"
13774 void CreateGameButtons()
13778 for (i = 0; i < NUM_GAME_BUTTONS; i++)
13780 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
13781 struct GadgetInfo *gi;
13784 unsigned long event_mask;
13785 int gd_xoffset, gd_yoffset;
13786 int gd_x1, gd_x2, gd_y1, gd_y2;
13789 gd_xoffset = gamebutton_info[i].x;
13790 gd_yoffset = gamebutton_info[i].y;
13791 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
13792 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
13794 if (id == GAME_CTRL_ID_STOP ||
13795 id == GAME_CTRL_ID_PAUSE ||
13796 id == GAME_CTRL_ID_PLAY)
13798 button_type = GD_TYPE_NORMAL_BUTTON;
13800 event_mask = GD_EVENT_RELEASED;
13801 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
13802 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
13806 button_type = GD_TYPE_CHECK_BUTTON;
13808 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
13809 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
13810 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
13811 event_mask = GD_EVENT_PRESSED;
13812 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
13813 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
13816 gi = CreateGadget(GDI_CUSTOM_ID, id,
13817 GDI_INFO_TEXT, gamebutton_info[i].infotext,
13818 GDI_X, DX + gd_xoffset,
13819 GDI_Y, DY + gd_yoffset,
13820 GDI_WIDTH, GAME_BUTTON_XSIZE,
13821 GDI_HEIGHT, GAME_BUTTON_YSIZE,
13822 GDI_TYPE, button_type,
13823 GDI_STATE, GD_BUTTON_UNPRESSED,
13824 GDI_CHECKED, checked,
13825 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
13826 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
13827 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
13828 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
13829 GDI_EVENT_MASK, event_mask,
13830 GDI_CALLBACK_ACTION, HandleGameButtons,
13834 Error(ERR_EXIT, "cannot create gadget");
13836 game_gadget[id] = gi;
13840 void FreeGameButtons()
13844 for (i = 0; i < NUM_GAME_BUTTONS; i++)
13845 FreeGadget(game_gadget[i]);
13848 static void MapGameButtons()
13852 for (i = 0; i < NUM_GAME_BUTTONS; i++)
13853 MapGadget(game_gadget[i]);
13856 void UnmapGameButtons()
13860 for (i = 0; i < NUM_GAME_BUTTONS; i++)
13861 UnmapGadget(game_gadget[i]);
13864 static void HandleGameButtons(struct GadgetInfo *gi)
13866 int id = gi->custom_id;
13868 if (game_status != GAME_MODE_PLAYING)
13873 case GAME_CTRL_ID_STOP:
13877 RequestQuitGame(TRUE);
13880 case GAME_CTRL_ID_PAUSE:
13881 if (options.network)
13883 #if defined(NETWORK_AVALIABLE)
13885 SendToServer_ContinuePlaying();
13887 SendToServer_PausePlaying();
13891 TapeTogglePause(TAPE_TOGGLE_MANUAL);
13894 case GAME_CTRL_ID_PLAY:
13897 #if defined(NETWORK_AVALIABLE)
13898 if (options.network)
13899 SendToServer_ContinuePlaying();
13903 tape.pausing = FALSE;
13904 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
13909 case SOUND_CTRL_ID_MUSIC:
13910 if (setup.sound_music)
13912 setup.sound_music = FALSE;
13915 else if (audio.music_available)
13917 setup.sound = setup.sound_music = TRUE;
13919 SetAudioMode(setup.sound);
13925 case SOUND_CTRL_ID_LOOPS:
13926 if (setup.sound_loops)
13927 setup.sound_loops = FALSE;
13928 else if (audio.loops_available)
13930 setup.sound = setup.sound_loops = TRUE;
13931 SetAudioMode(setup.sound);
13935 case SOUND_CTRL_ID_SIMPLE:
13936 if (setup.sound_simple)
13937 setup.sound_simple = FALSE;
13938 else if (audio.sound_available)
13940 setup.sound = setup.sound_simple = TRUE;
13941 SetAudioMode(setup.sound);