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 get_element_from_group_element(int element)
945 if (IS_GROUP_ELEMENT(element))
947 struct ElementGroupInfo *group = element_info[element].group;
948 int last_anim_random_frame = gfx.anim_random_frame;
951 if (group->choice_mode == ANIM_RANDOM)
952 gfx.anim_random_frame = RND(group->num_elements_resolved);
954 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
955 group->choice_mode, 0,
958 if (group->choice_mode == ANIM_RANDOM)
959 gfx.anim_random_frame = last_anim_random_frame;
963 element = group->element_resolved[element_pos];
969 static void InitPlayerField(int x, int y, int element, boolean init_game)
971 if (element == EL_SP_MURPHY)
975 if (stored_player[0].present)
977 Feld[x][y] = EL_SP_MURPHY_CLONE;
983 stored_player[0].use_murphy = TRUE;
985 if (!level.use_artwork_element[0])
986 stored_player[0].artwork_element = EL_SP_MURPHY;
989 Feld[x][y] = EL_PLAYER_1;
995 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
996 int jx = player->jx, jy = player->jy;
998 player->present = TRUE;
1000 player->block_last_field = (element == EL_SP_MURPHY ?
1001 level.sp_block_last_field :
1002 level.block_last_field);
1004 /* ---------- initialize player's last field block delay --------------- */
1006 /* always start with reliable default value (no adjustment needed) */
1007 player->block_delay_adjustment = 0;
1009 /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1010 if (player->block_last_field && element == EL_SP_MURPHY)
1011 player->block_delay_adjustment = 1;
1013 /* special case 2: in game engines before 3.1.1, blocking was different */
1014 if (game.use_block_last_field_bug)
1015 player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1017 if (!options.network || player->connected)
1019 player->active = TRUE;
1021 /* remove potentially duplicate players */
1022 if (StorePlayer[jx][jy] == Feld[x][y])
1023 StorePlayer[jx][jy] = 0;
1025 StorePlayer[x][y] = Feld[x][y];
1029 printf("Player %d activated.\n", player->element_nr);
1030 printf("[Local player is %d and currently %s.]\n",
1031 local_player->element_nr,
1032 local_player->active ? "active" : "not active");
1036 Feld[x][y] = EL_EMPTY;
1038 player->jx = player->last_jx = x;
1039 player->jy = player->last_jy = y;
1043 static void InitField(int x, int y, boolean init_game)
1045 int element = Feld[x][y];
1054 InitPlayerField(x, y, element, init_game);
1057 case EL_SOKOBAN_FIELD_PLAYER:
1058 element = Feld[x][y] = EL_PLAYER_1;
1059 InitField(x, y, init_game);
1061 element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1062 InitField(x, y, init_game);
1065 case EL_SOKOBAN_FIELD_EMPTY:
1066 local_player->sokobanfields_still_needed++;
1070 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1071 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1072 else if (x > 0 && Feld[x-1][y] == EL_ACID)
1073 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1074 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1075 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1076 else if (y > 0 && Feld[x][y-1] == EL_ACID)
1077 Feld[x][y] = EL_ACID_POOL_BOTTOM;
1078 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1079 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1088 case EL_SPACESHIP_RIGHT:
1089 case EL_SPACESHIP_UP:
1090 case EL_SPACESHIP_LEFT:
1091 case EL_SPACESHIP_DOWN:
1092 case EL_BD_BUTTERFLY:
1093 case EL_BD_BUTTERFLY_RIGHT:
1094 case EL_BD_BUTTERFLY_UP:
1095 case EL_BD_BUTTERFLY_LEFT:
1096 case EL_BD_BUTTERFLY_DOWN:
1098 case EL_BD_FIREFLY_RIGHT:
1099 case EL_BD_FIREFLY_UP:
1100 case EL_BD_FIREFLY_LEFT:
1101 case EL_BD_FIREFLY_DOWN:
1102 case EL_PACMAN_RIGHT:
1104 case EL_PACMAN_LEFT:
1105 case EL_PACMAN_DOWN:
1107 case EL_YAMYAM_LEFT:
1108 case EL_YAMYAM_RIGHT:
1110 case EL_YAMYAM_DOWN:
1111 case EL_DARK_YAMYAM:
1114 case EL_SP_SNIKSNAK:
1115 case EL_SP_ELECTRON:
1124 case EL_AMOEBA_FULL:
1129 case EL_AMOEBA_DROP:
1130 if (y == lev_fieldy - 1)
1132 Feld[x][y] = EL_AMOEBA_GROWING;
1133 Store[x][y] = EL_AMOEBA_WET;
1137 case EL_DYNAMITE_ACTIVE:
1138 case EL_SP_DISK_RED_ACTIVE:
1139 case EL_DYNABOMB_PLAYER_1_ACTIVE:
1140 case EL_DYNABOMB_PLAYER_2_ACTIVE:
1141 case EL_DYNABOMB_PLAYER_3_ACTIVE:
1142 case EL_DYNABOMB_PLAYER_4_ACTIVE:
1143 MovDelay[x][y] = 96;
1146 case EL_EM_DYNAMITE_ACTIVE:
1147 MovDelay[x][y] = 32;
1151 local_player->lights_still_needed++;
1155 local_player->friends_still_needed++;
1160 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1163 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1164 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1165 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1166 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1167 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1168 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1169 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1170 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1171 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1172 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1173 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1174 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1177 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1178 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1179 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1181 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
1183 game.belt_dir[belt_nr] = belt_dir;
1184 game.belt_dir_nr[belt_nr] = belt_dir_nr;
1186 else /* more than one switch -- set it like the first switch */
1188 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1193 #if !USE_BOTH_SWITCHGATE_SWITCHES
1194 case EL_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
1196 Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
1199 case EL_DC_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
1201 Feld[x][y] = EL_DC_SWITCHGATE_SWITCH_UP;
1205 case EL_LIGHT_SWITCH_ACTIVE:
1207 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1210 case EL_INVISIBLE_STEELWALL:
1211 case EL_INVISIBLE_WALL:
1212 case EL_INVISIBLE_SAND:
1213 if (game.light_time_left > 0 ||
1214 game.lenses_time_left > 0)
1215 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1218 case EL_EMC_MAGIC_BALL:
1219 if (game.ball_state)
1220 Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1223 case EL_EMC_MAGIC_BALL_SWITCH:
1224 if (game.ball_state)
1225 Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1229 if (IS_CUSTOM_ELEMENT(element))
1231 if (CAN_MOVE(element))
1234 #if USE_NEW_CUSTOM_VALUE
1235 if (!element_info[element].use_last_ce_value || init_game)
1236 CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1239 else if (IS_GROUP_ELEMENT(element))
1241 Feld[x][y] = get_element_from_group_element(element);
1243 InitField(x, y, init_game);
1250 CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1253 static inline void InitField_WithBug1(int x, int y, boolean init_game)
1255 InitField(x, y, init_game);
1257 /* not needed to call InitMovDir() -- already done by InitField()! */
1258 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1259 CAN_MOVE(Feld[x][y]))
1263 static inline void InitField_WithBug2(int x, int y, boolean init_game)
1265 int old_element = Feld[x][y];
1267 InitField(x, y, init_game);
1269 /* not needed to call InitMovDir() -- already done by InitField()! */
1270 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1271 CAN_MOVE(old_element) &&
1272 (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1275 /* this case is in fact a combination of not less than three bugs:
1276 first, it calls InitMovDir() for elements that can move, although this is
1277 already done by InitField(); then, it checks the element that was at this
1278 field _before_ the call to InitField() (which can change it); lastly, it
1279 was not called for "mole with direction" elements, which were treated as
1280 "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1286 void DrawGameValue_Emeralds(int value)
1288 struct TextPosInfo *pos = &game.panel.gems;
1289 int font_nr = FONT_TEXT_2;
1290 int font_width = getFontWidth(font_nr);
1291 int digits = pos->chars;
1293 if (PANEL_DEACTIVATED(pos))
1296 pos->width = digits * font_width;
1298 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, digits), font_nr);
1301 void DrawGameValue_Dynamite(int value)
1303 struct TextPosInfo *pos = &game.panel.inventory;
1304 int font_nr = FONT_TEXT_2;
1305 int font_width = getFontWidth(font_nr);
1306 int digits = pos->chars;
1308 if (PANEL_DEACTIVATED(pos))
1311 pos->width = digits * font_width;
1313 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, digits), font_nr);
1316 void DrawGameValue_Score(int value)
1318 struct TextPosInfo *pos = &game.panel.score;
1319 int font_nr = FONT_TEXT_2;
1320 int font_width = getFontWidth(font_nr);
1321 int digits = pos->chars;
1323 if (PANEL_DEACTIVATED(pos))
1326 pos->width = digits * font_width;
1328 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, digits), font_nr);
1331 void DrawGameValue_Time(int value)
1333 struct TextPosInfo *pos = &game.panel.time;
1334 static int last_value = -1;
1337 int digits = pos->chars;
1338 int font1_nr = FONT_TEXT_2;
1339 int font2_nr = FONT_TEXT_1;
1340 int font_nr = font1_nr;
1341 boolean use_dynamic_digits = (digits == -1 ? TRUE : FALSE);
1343 if (PANEL_DEACTIVATED(pos))
1346 if (use_dynamic_digits) /* use dynamic number of digits */
1348 digits = (value < 1000 ? digits1 : digits2);
1349 font_nr = (value < 1000 ? font1_nr : font2_nr);
1352 /* clear background if value just changed its size (dynamic digits only) */
1353 if (use_dynamic_digits && (last_value < 1000) != (value < 1000))
1355 int width1 = digits1 * getFontWidth(font1_nr);
1356 int width2 = digits2 * getFontWidth(font2_nr);
1357 int max_width = MAX(width1, width2);
1358 int max_height = MAX(getFontHeight(font1_nr), getFontHeight(font2_nr));
1360 pos->width = max_width;
1362 ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
1363 max_width, max_height);
1366 pos->width = digits * getFontWidth(font_nr);
1368 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, digits), font_nr);
1373 void DrawGameValue_Level(int value)
1375 struct TextPosInfo *pos = &game.panel.level;
1378 int digits = pos->chars;
1379 int font1_nr = FONT_TEXT_2;
1380 int font2_nr = FONT_TEXT_1;
1381 int font_nr = font1_nr;
1382 boolean use_dynamic_digits = (digits == -1 ? TRUE : FALSE);
1384 if (PANEL_DEACTIVATED(pos))
1387 if (use_dynamic_digits) /* use dynamic number of digits */
1389 digits = (level_nr < 100 ? digits1 : digits2);
1390 font_nr = (level_nr < 100 ? font1_nr : font2_nr);
1393 pos->width = digits * getFontWidth(font_nr);
1395 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, digits), font_nr);
1398 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
1400 struct TextPosInfo *pos = &game.panel.keys;
1401 int base_key_graphic = EL_KEY_1;
1404 if (PANEL_DEACTIVATED(pos))
1407 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
1408 base_key_graphic = EL_EM_KEY_1;
1410 pos->width = 4 * MINI_TILEX;
1412 /* currently only 4 of 8 possible keys are displayed */
1413 for (i = 0; i < STD_NUM_KEYS; i++)
1415 int src_x = DOOR_GFX_PAGEX5 + 18;
1416 int src_y = DOOR_GFX_PAGEY1 + 123;
1417 int dst_x = PANEL_XPOS(pos) + i * MINI_TILEX;
1418 int dst_y = PANEL_YPOS(pos);
1421 DrawMiniGraphicExt(drawto, dst_x, dst_y, el2edimg(base_key_graphic + i));
1423 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
1424 MINI_TILEX, MINI_TILEY, dst_x, dst_y);
1430 void DrawGameValue_Emeralds(int value)
1432 int font_nr = FONT_TEXT_2;
1433 int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
1435 if (PANEL_DEACTIVATED(game.panel.gems))
1438 DrawText(DX_EMERALDS + xpos, DY_EMERALDS, int2str(value, 3), font_nr);
1441 void DrawGameValue_Dynamite(int value)
1443 int font_nr = FONT_TEXT_2;
1444 int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
1446 if (PANEL_DEACTIVATED(game.panel.inventory))
1449 DrawText(DX_DYNAMITE + xpos, DY_DYNAMITE, int2str(value, 3), font_nr);
1452 void DrawGameValue_Score(int value)
1454 int font_nr = FONT_TEXT_2;
1455 int xpos = (5 * 14 - 5 * getFontWidth(font_nr)) / 2;
1457 if (PANEL_DEACTIVATED(game.panel.score))
1460 DrawText(DX_SCORE + xpos, DY_SCORE, int2str(value, 5), font_nr);
1463 void DrawGameValue_Time(int value)
1465 int font1_nr = FONT_TEXT_2;
1467 int font2_nr = FONT_TEXT_1;
1469 int font2_nr = FONT_LEVEL_NUMBER;
1471 int xpos3 = (3 * 14 - 3 * getFontWidth(font1_nr)) / 2;
1472 int xpos4 = (4 * 10 - 4 * getFontWidth(font2_nr)) / 2;
1474 if (PANEL_DEACTIVATED(game.panel.time))
1477 /* clear background if value just changed its size */
1478 if (value == 999 || value == 1000)
1479 ClearRectangleOnBackground(drawto, DX_TIME1, DY_TIME, 14 * 3, 14);
1482 DrawText(DX_TIME1 + xpos3, DY_TIME, int2str(value, 3), font1_nr);
1484 DrawText(DX_TIME2 + xpos4, DY_TIME, int2str(value, 4), font2_nr);
1487 void DrawGameValue_Level(int value)
1489 int font1_nr = FONT_TEXT_2;
1491 int font2_nr = FONT_TEXT_1;
1493 int font2_nr = FONT_LEVEL_NUMBER;
1496 if (PANEL_DEACTIVATED(game.panel.level))
1500 DrawText(DX_LEVEL1, DY_LEVEL, int2str(value, 2), font1_nr);
1502 DrawText(DX_LEVEL2, DY_LEVEL, int2str(value, 3), font2_nr);
1505 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
1507 int base_key_graphic = EL_KEY_1;
1510 if (PANEL_DEACTIVATED(game.panel.keys))
1513 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
1514 base_key_graphic = EL_EM_KEY_1;
1516 /* currently only 4 of 8 possible keys are displayed */
1517 for (i = 0; i < STD_NUM_KEYS; i++)
1519 int x = XX_KEYS + i * MINI_TILEX;
1523 DrawMiniGraphicExt(drawto, DX + x,DY + y, el2edimg(base_key_graphic + i));
1525 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
1526 DOOR_GFX_PAGEX5 + x, y, MINI_TILEX, MINI_TILEY, DX + x,DY + y);
1532 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
1535 int key[MAX_NUM_KEYS];
1538 /* prevent EM engine from updating time/score values parallel to GameWon() */
1539 if (level.game_engine_type == GAME_ENGINE_TYPE_EM &&
1540 local_player->LevelSolved)
1543 for (i = 0; i < MAX_NUM_KEYS; i++)
1544 key[i] = key_bits & (1 << i);
1546 DrawGameValue_Level(level_nr);
1548 DrawGameValue_Emeralds(emeralds);
1549 DrawGameValue_Dynamite(dynamite);
1550 DrawGameValue_Score(score);
1551 DrawGameValue_Time(time);
1553 DrawGameValue_Keys(key);
1556 void DrawGameDoorValues()
1558 int time_value = (level.time == 0 ? TimePlayed : TimeLeft);
1559 int dynamite_value = 0;
1560 int score_value = (local_player->LevelSolved ? local_player->score_final :
1561 local_player->score);
1562 int gems_value = local_player->gems_still_needed;
1566 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
1568 DrawGameDoorValues_EM();
1573 if (game.centered_player_nr == -1)
1575 for (i = 0; i < MAX_PLAYERS; i++)
1577 for (j = 0; j < MAX_NUM_KEYS; j++)
1578 if (stored_player[i].key[j])
1579 key_bits |= (1 << j);
1581 dynamite_value += stored_player[i].inventory_size;
1586 int player_nr = game.centered_player_nr;
1588 for (i = 0; i < MAX_NUM_KEYS; i++)
1589 if (stored_player[player_nr].key[i])
1590 key_bits |= (1 << i);
1592 dynamite_value = stored_player[player_nr].inventory_size;
1595 DrawAllGameValues(gems_value, dynamite_value, score_value, time_value,
1601 =============================================================================
1603 -----------------------------------------------------------------------------
1604 initialize game engine due to level / tape version number
1605 =============================================================================
1608 static void InitGameEngine()
1610 int i, j, k, l, x, y;
1612 /* set game engine from tape file when re-playing, else from level file */
1613 game.engine_version = (tape.playing ? tape.engine_version :
1614 level.game_version);
1616 /* ---------------------------------------------------------------------- */
1617 /* set flags for bugs and changes according to active game engine version */
1618 /* ---------------------------------------------------------------------- */
1621 Summary of bugfix/change:
1622 Fixed handling for custom elements that change when pushed by the player.
1624 Fixed/changed in version:
1628 Before 3.1.0, custom elements that "change when pushing" changed directly
1629 after the player started pushing them (until then handled in "DigField()").
1630 Since 3.1.0, these custom elements are not changed until the "pushing"
1631 move of the element is finished (now handled in "ContinueMoving()").
1633 Affected levels/tapes:
1634 The first condition is generally needed for all levels/tapes before version
1635 3.1.0, which might use the old behaviour before it was changed; known tapes
1636 that are affected are some tapes from the level set "Walpurgis Gardens" by
1638 The second condition is an exception from the above case and is needed for
1639 the special case of tapes recorded with game (not engine!) version 3.1.0 or
1640 above (including some development versions of 3.1.0), but before it was
1641 known that this change would break tapes like the above and was fixed in
1642 3.1.1, so that the changed behaviour was active although the engine version
1643 while recording maybe was before 3.1.0. There is at least one tape that is
1644 affected by this exception, which is the tape for the one-level set "Bug
1645 Machine" by Juergen Bonhagen.
1648 game.use_change_when_pushing_bug =
1649 (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1651 tape.game_version >= VERSION_IDENT(3,1,0,0) &&
1652 tape.game_version < VERSION_IDENT(3,1,1,0)));
1655 Summary of bugfix/change:
1656 Fixed handling for blocking the field the player leaves when moving.
1658 Fixed/changed in version:
1662 Before 3.1.1, when "block last field when moving" was enabled, the field
1663 the player is leaving when moving was blocked for the time of the move,
1664 and was directly unblocked afterwards. This resulted in the last field
1665 being blocked for exactly one less than the number of frames of one player
1666 move. Additionally, even when blocking was disabled, the last field was
1667 blocked for exactly one frame.
1668 Since 3.1.1, due to changes in player movement handling, the last field
1669 is not blocked at all when blocking is disabled. When blocking is enabled,
1670 the last field is blocked for exactly the number of frames of one player
1671 move. Additionally, if the player is Murphy, the hero of Supaplex, the
1672 last field is blocked for exactly one more than the number of frames of
1675 Affected levels/tapes:
1676 (!!! yet to be determined -- probably many !!!)
1679 game.use_block_last_field_bug =
1680 (game.engine_version < VERSION_IDENT(3,1,1,0));
1683 Summary of bugfix/change:
1684 Changed behaviour of CE changes with multiple changes per single frame.
1686 Fixed/changed in version:
1690 Before 3.2.0-6, only one single CE change was allowed in each engine frame.
1691 This resulted in race conditions where CEs seem to behave strange in some
1692 situations (where triggered CE changes were just skipped because there was
1693 already a CE change on that tile in the playfield in that engine frame).
1694 Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
1695 (The number of changes per frame must be limited in any case, because else
1696 it is easily possible to define CE changes that would result in an infinite
1697 loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
1698 should be set large enough so that it would only be reached in cases where
1699 the corresponding CE change conditions run into a loop. Therefore, it seems
1700 to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
1701 maximal number of change pages for custom elements.)
1703 Affected levels/tapes:
1707 #if USE_ONLY_ONE_CHANGE_PER_FRAME
1708 game.max_num_changes_per_frame = 1;
1710 game.max_num_changes_per_frame =
1711 (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
1714 /* ---------------------------------------------------------------------- */
1716 /* default scan direction: scan playfield from top/left to bottom/right */
1717 InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
1719 /* dynamically adjust element properties according to game engine version */
1720 InitElementPropertiesEngine(game.engine_version);
1723 printf("level %d: level version == %06d\n", level_nr, level.game_version);
1724 printf(" tape version == %06d [%s] [file: %06d]\n",
1725 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
1727 printf(" => game.engine_version == %06d\n", game.engine_version);
1730 /* ---------- initialize player's initial move delay --------------------- */
1732 /* dynamically adjust player properties according to level information */
1733 for (i = 0; i < MAX_PLAYERS; i++)
1734 game.initial_move_delay_value[i] =
1735 get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
1737 /* dynamically adjust player properties according to game engine version */
1738 for (i = 0; i < MAX_PLAYERS; i++)
1739 game.initial_move_delay[i] =
1740 (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
1741 game.initial_move_delay_value[i] : 0);
1743 /* ---------- initialize player's initial push delay --------------------- */
1745 /* dynamically adjust player properties according to game engine version */
1746 game.initial_push_delay_value =
1747 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
1749 /* ---------- initialize changing elements ------------------------------- */
1751 /* initialize changing elements information */
1752 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1754 struct ElementInfo *ei = &element_info[i];
1756 /* this pointer might have been changed in the level editor */
1757 ei->change = &ei->change_page[0];
1759 if (!IS_CUSTOM_ELEMENT(i))
1761 ei->change->target_element = EL_EMPTY_SPACE;
1762 ei->change->delay_fixed = 0;
1763 ei->change->delay_random = 0;
1764 ei->change->delay_frames = 1;
1767 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1769 ei->has_change_event[j] = FALSE;
1771 ei->event_page_nr[j] = 0;
1772 ei->event_page[j] = &ei->change_page[0];
1776 /* add changing elements from pre-defined list */
1777 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
1779 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
1780 struct ElementInfo *ei = &element_info[ch_delay->element];
1782 ei->change->target_element = ch_delay->target_element;
1783 ei->change->delay_fixed = ch_delay->change_delay;
1785 ei->change->pre_change_function = ch_delay->pre_change_function;
1786 ei->change->change_function = ch_delay->change_function;
1787 ei->change->post_change_function = ch_delay->post_change_function;
1789 ei->change->can_change = TRUE;
1790 ei->change->can_change_or_has_action = TRUE;
1792 ei->has_change_event[CE_DELAY] = TRUE;
1794 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
1795 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
1798 /* ---------- initialize internal run-time variables ------------- */
1800 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1802 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1804 for (j = 0; j < ei->num_change_pages; j++)
1806 ei->change_page[j].can_change_or_has_action =
1807 (ei->change_page[j].can_change |
1808 ei->change_page[j].has_action);
1812 /* add change events from custom element configuration */
1813 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1815 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1817 for (j = 0; j < ei->num_change_pages; j++)
1819 if (!ei->change_page[j].can_change_or_has_action)
1822 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1824 /* only add event page for the first page found with this event */
1825 if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
1827 ei->has_change_event[k] = TRUE;
1829 ei->event_page_nr[k] = j;
1830 ei->event_page[k] = &ei->change_page[j];
1836 /* ---------- initialize run-time trigger player and element ------------- */
1838 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1840 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1842 for (j = 0; j < ei->num_change_pages; j++)
1844 ei->change_page[j].actual_trigger_element = EL_EMPTY;
1845 ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
1846 ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
1847 ei->change_page[j].actual_trigger_ce_value = 0;
1848 ei->change_page[j].actual_trigger_ce_score = 0;
1852 /* ---------- initialize trigger events ---------------------------------- */
1854 /* initialize trigger events information */
1855 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1856 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1857 trigger_events[i][j] = FALSE;
1859 /* add trigger events from element change event properties */
1860 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1862 struct ElementInfo *ei = &element_info[i];
1864 for (j = 0; j < ei->num_change_pages; j++)
1866 if (!ei->change_page[j].can_change_or_has_action)
1869 if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
1871 int trigger_element = ei->change_page[j].trigger_element;
1873 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1875 if (ei->change_page[j].has_event[k])
1877 if (IS_GROUP_ELEMENT(trigger_element))
1879 struct ElementGroupInfo *group =
1880 element_info[trigger_element].group;
1882 for (l = 0; l < group->num_elements_resolved; l++)
1883 trigger_events[group->element_resolved[l]][k] = TRUE;
1885 else if (trigger_element == EL_ANY_ELEMENT)
1886 for (l = 0; l < MAX_NUM_ELEMENTS; l++)
1887 trigger_events[l][k] = TRUE;
1889 trigger_events[trigger_element][k] = TRUE;
1896 /* ---------- initialize push delay -------------------------------------- */
1898 /* initialize push delay values to default */
1899 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1901 if (!IS_CUSTOM_ELEMENT(i))
1903 /* set default push delay values (corrected since version 3.0.7-1) */
1904 if (game.engine_version < VERSION_IDENT(3,0,7,1))
1906 element_info[i].push_delay_fixed = 2;
1907 element_info[i].push_delay_random = 8;
1911 element_info[i].push_delay_fixed = 8;
1912 element_info[i].push_delay_random = 8;
1917 /* set push delay value for certain elements from pre-defined list */
1918 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
1920 int e = push_delay_list[i].element;
1922 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
1923 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
1926 /* set push delay value for Supaplex elements for newer engine versions */
1927 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
1929 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1931 if (IS_SP_ELEMENT(i))
1933 /* set SP push delay to just enough to push under a falling zonk */
1934 int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
1936 element_info[i].push_delay_fixed = delay;
1937 element_info[i].push_delay_random = 0;
1942 /* ---------- initialize move stepsize ----------------------------------- */
1944 /* initialize move stepsize values to default */
1945 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1946 if (!IS_CUSTOM_ELEMENT(i))
1947 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
1949 /* set move stepsize value for certain elements from pre-defined list */
1950 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
1952 int e = move_stepsize_list[i].element;
1954 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
1957 /* ---------- initialize collect score ----------------------------------- */
1959 /* initialize collect score values for custom elements from initial value */
1960 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1961 if (IS_CUSTOM_ELEMENT(i))
1962 element_info[i].collect_score = element_info[i].collect_score_initial;
1964 /* ---------- initialize collect count ----------------------------------- */
1966 /* initialize collect count values for non-custom elements */
1967 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1968 if (!IS_CUSTOM_ELEMENT(i))
1969 element_info[i].collect_count_initial = 0;
1971 /* add collect count values for all elements from pre-defined list */
1972 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
1973 element_info[collect_count_list[i].element].collect_count_initial =
1974 collect_count_list[i].count;
1976 /* ---------- initialize access direction -------------------------------- */
1978 /* initialize access direction values to default (access from every side) */
1979 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1980 if (!IS_CUSTOM_ELEMENT(i))
1981 element_info[i].access_direction = MV_ALL_DIRECTIONS;
1983 /* set access direction value for certain elements from pre-defined list */
1984 for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
1985 element_info[access_direction_list[i].element].access_direction =
1986 access_direction_list[i].direction;
1988 /* ---------- initialize explosion content ------------------------------- */
1989 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1991 if (IS_CUSTOM_ELEMENT(i))
1994 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
1996 /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
1998 element_info[i].content.e[x][y] =
1999 (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
2000 i == EL_PLAYER_2 ? EL_EMERALD_RED :
2001 i == EL_PLAYER_3 ? EL_EMERALD :
2002 i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
2003 i == EL_MOLE ? EL_EMERALD_RED :
2004 i == EL_PENGUIN ? EL_EMERALD_PURPLE :
2005 i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
2006 i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
2007 i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
2008 i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
2009 i == EL_WALL_EMERALD ? EL_EMERALD :
2010 i == EL_WALL_DIAMOND ? EL_DIAMOND :
2011 i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
2012 i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
2013 i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
2014 i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
2015 i == EL_WALL_PEARL ? EL_PEARL :
2016 i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
2021 /* ---------- initialize recursion detection ------------------------------ */
2022 recursion_loop_depth = 0;
2023 recursion_loop_detected = FALSE;
2024 recursion_loop_element = EL_UNDEFINED;
2027 int get_num_special_action(int element, int action_first, int action_last)
2029 int num_special_action = 0;
2032 for (i = action_first; i <= action_last; i++)
2034 boolean found = FALSE;
2036 for (j = 0; j < NUM_DIRECTIONS; j++)
2037 if (el_act_dir2img(element, i, j) !=
2038 el_act_dir2img(element, ACTION_DEFAULT, j))
2042 num_special_action++;
2047 return num_special_action;
2052 =============================================================================
2054 -----------------------------------------------------------------------------
2055 initialize and start new game
2056 =============================================================================
2061 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
2062 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
2063 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
2064 boolean do_fading = (game_status == GAME_MODE_MAIN);
2067 game_status = GAME_MODE_PLAYING;
2071 /* don't play tapes over network */
2072 network_playing = (options.network && !tape.playing);
2074 for (i = 0; i < MAX_PLAYERS; i++)
2076 struct PlayerInfo *player = &stored_player[i];
2078 player->index_nr = i;
2079 player->index_bit = (1 << i);
2080 player->element_nr = EL_PLAYER_1 + i;
2082 player->present = FALSE;
2083 player->active = FALSE;
2084 player->killed = FALSE;
2087 player->effective_action = 0;
2088 player->programmed_action = 0;
2091 player->score_final = 0;
2093 player->gems_still_needed = level.gems_needed;
2094 player->sokobanfields_still_needed = 0;
2095 player->lights_still_needed = 0;
2096 player->friends_still_needed = 0;
2098 for (j = 0; j < MAX_NUM_KEYS; j++)
2099 player->key[j] = FALSE;
2101 player->num_white_keys = 0;
2103 player->dynabomb_count = 0;
2104 player->dynabomb_size = 1;
2105 player->dynabombs_left = 0;
2106 player->dynabomb_xl = FALSE;
2108 player->MovDir = MV_NONE;
2111 player->GfxDir = MV_NONE;
2112 player->GfxAction = ACTION_DEFAULT;
2114 player->StepFrame = 0;
2116 player->use_murphy = FALSE;
2117 player->artwork_element =
2118 (level.use_artwork_element[i] ? level.artwork_element[i] :
2119 player->element_nr);
2121 player->block_last_field = FALSE; /* initialized in InitPlayerField() */
2122 player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
2124 player->gravity = level.initial_player_gravity[i];
2126 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
2128 player->actual_frame_counter = 0;
2130 player->step_counter = 0;
2132 player->last_move_dir = MV_NONE;
2134 player->is_active = FALSE;
2136 player->is_waiting = FALSE;
2137 player->is_moving = FALSE;
2138 player->is_auto_moving = FALSE;
2139 player->is_digging = FALSE;
2140 player->is_snapping = FALSE;
2141 player->is_collecting = FALSE;
2142 player->is_pushing = FALSE;
2143 player->is_switching = FALSE;
2144 player->is_dropping = FALSE;
2145 player->is_dropping_pressed = FALSE;
2147 player->is_bored = FALSE;
2148 player->is_sleeping = FALSE;
2150 player->frame_counter_bored = -1;
2151 player->frame_counter_sleeping = -1;
2153 player->anim_delay_counter = 0;
2154 player->post_delay_counter = 0;
2156 player->dir_waiting = MV_NONE;
2157 player->action_waiting = ACTION_DEFAULT;
2158 player->last_action_waiting = ACTION_DEFAULT;
2159 player->special_action_bored = ACTION_DEFAULT;
2160 player->special_action_sleeping = ACTION_DEFAULT;
2162 player->switch_x = -1;
2163 player->switch_y = -1;
2165 player->drop_x = -1;
2166 player->drop_y = -1;
2168 player->show_envelope = 0;
2170 SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
2172 player->push_delay = -1; /* initialized when pushing starts */
2173 player->push_delay_value = game.initial_push_delay_value;
2175 player->drop_delay = 0;
2176 player->drop_pressed_delay = 0;
2178 player->last_jx = -1;
2179 player->last_jy = -1;
2183 player->shield_normal_time_left = 0;
2184 player->shield_deadly_time_left = 0;
2186 player->inventory_infinite_element = EL_UNDEFINED;
2187 player->inventory_size = 0;
2189 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
2190 SnapField(player, 0, 0);
2192 player->LevelSolved = FALSE;
2193 player->GameOver = FALSE;
2195 player->LevelSolved_GameWon = FALSE;
2196 player->LevelSolved_GameEnd = FALSE;
2197 player->LevelSolved_PanelOff = FALSE;
2198 player->LevelSolved_SaveTape = FALSE;
2199 player->LevelSolved_SaveScore = FALSE;
2202 network_player_action_received = FALSE;
2204 #if defined(NETWORK_AVALIABLE)
2205 /* initial null action */
2206 if (network_playing)
2207 SendToServer_MovePlayer(MV_NONE);
2216 TimeLeft = level.time;
2219 ScreenMovDir = MV_NONE;
2223 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
2225 AllPlayersGone = FALSE;
2227 game.yamyam_content_nr = 0;
2228 game.magic_wall_active = FALSE;
2229 game.magic_wall_time_left = 0;
2230 game.light_time_left = 0;
2231 game.timegate_time_left = 0;
2232 game.switchgate_pos = 0;
2233 game.wind_direction = level.wind_direction_initial;
2235 #if !USE_PLAYER_GRAVITY
2236 game.gravity = FALSE;
2237 game.explosions_delayed = TRUE;
2240 game.lenses_time_left = 0;
2241 game.magnify_time_left = 0;
2243 game.ball_state = level.ball_state_initial;
2244 game.ball_content_nr = 0;
2246 game.envelope_active = FALSE;
2248 /* set focus to local player for network games, else to all players */
2249 game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
2250 game.centered_player_nr_next = game.centered_player_nr;
2251 game.set_centered_player = FALSE;
2253 if (network_playing && tape.recording)
2255 /* store client dependent player focus when recording network games */
2256 tape.centered_player_nr_next = game.centered_player_nr_next;
2257 tape.set_centered_player = TRUE;
2260 for (i = 0; i < NUM_BELTS; i++)
2262 game.belt_dir[i] = MV_NONE;
2263 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
2266 for (i = 0; i < MAX_NUM_AMOEBA; i++)
2267 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
2269 SCAN_PLAYFIELD(x, y)
2271 Feld[x][y] = level.field[x][y];
2272 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
2273 ChangeDelay[x][y] = 0;
2274 ChangePage[x][y] = -1;
2275 #if USE_NEW_CUSTOM_VALUE
2276 CustomValue[x][y] = 0; /* initialized in InitField() */
2278 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
2280 WasJustMoving[x][y] = 0;
2281 WasJustFalling[x][y] = 0;
2282 CheckCollision[x][y] = 0;
2283 CheckImpact[x][y] = 0;
2285 Pushed[x][y] = FALSE;
2287 ChangeCount[x][y] = 0;
2288 ChangeEvent[x][y] = -1;
2290 ExplodePhase[x][y] = 0;
2291 ExplodeDelay[x][y] = 0;
2292 ExplodeField[x][y] = EX_TYPE_NONE;
2294 RunnerVisit[x][y] = 0;
2295 PlayerVisit[x][y] = 0;
2298 GfxRandom[x][y] = INIT_GFX_RANDOM();
2299 GfxElement[x][y] = EL_UNDEFINED;
2300 GfxAction[x][y] = ACTION_DEFAULT;
2301 GfxDir[x][y] = MV_NONE;
2304 SCAN_PLAYFIELD(x, y)
2306 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
2308 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
2310 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
2313 InitField(x, y, TRUE);
2318 for (i = 0; i < MAX_PLAYERS; i++)
2320 struct PlayerInfo *player = &stored_player[i];
2322 /* set number of special actions for bored and sleeping animation */
2323 player->num_special_action_bored =
2324 get_num_special_action(player->artwork_element,
2325 ACTION_BORING_1, ACTION_BORING_LAST);
2326 player->num_special_action_sleeping =
2327 get_num_special_action(player->artwork_element,
2328 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
2331 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
2332 emulate_sb ? EMU_SOKOBAN :
2333 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
2335 #if USE_NEW_ALL_SLIPPERY
2336 /* initialize type of slippery elements */
2337 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2339 if (!IS_CUSTOM_ELEMENT(i))
2341 /* default: elements slip down either to the left or right randomly */
2342 element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
2344 /* SP style elements prefer to slip down on the left side */
2345 if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
2346 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
2348 /* BD style elements prefer to slip down on the left side */
2349 if (game.emulation == EMU_BOULDERDASH)
2350 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
2355 /* initialize explosion and ignition delay */
2356 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2358 if (!IS_CUSTOM_ELEMENT(i))
2361 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
2362 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
2363 game.emulation == EMU_SUPAPLEX ? 3 : 2);
2364 int last_phase = (num_phase + 1) * delay;
2365 int half_phase = (num_phase / 2) * delay;
2367 element_info[i].explosion_delay = last_phase - 1;
2368 element_info[i].ignition_delay = half_phase;
2370 if (i == EL_BLACK_ORB)
2371 element_info[i].ignition_delay = 1;
2375 if (element_info[i].explosion_delay < 1) /* !!! check again !!! */
2376 element_info[i].explosion_delay = 1;
2378 if (element_info[i].ignition_delay < 1) /* !!! check again !!! */
2379 element_info[i].ignition_delay = 1;
2383 /* correct non-moving belts to start moving left */
2384 for (i = 0; i < NUM_BELTS; i++)
2385 if (game.belt_dir[i] == MV_NONE)
2386 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
2388 /* check if any connected player was not found in playfield */
2389 for (i = 0; i < MAX_PLAYERS; i++)
2391 struct PlayerInfo *player = &stored_player[i];
2393 if (player->connected && !player->present)
2395 for (j = 0; j < MAX_PLAYERS; j++)
2397 struct PlayerInfo *some_player = &stored_player[j];
2398 int jx = some_player->jx, jy = some_player->jy;
2400 /* assign first free player found that is present in the playfield */
2401 if (some_player->present && !some_player->connected)
2403 player->present = TRUE;
2404 player->active = TRUE;
2406 some_player->present = FALSE;
2407 some_player->active = FALSE;
2409 player->artwork_element = some_player->artwork_element;
2411 player->block_last_field = some_player->block_last_field;
2412 player->block_delay_adjustment = some_player->block_delay_adjustment;
2414 StorePlayer[jx][jy] = player->element_nr;
2415 player->jx = player->last_jx = jx;
2416 player->jy = player->last_jy = jy;
2426 /* when playing a tape, eliminate all players who do not participate */
2428 for (i = 0; i < MAX_PLAYERS; i++)
2430 if (stored_player[i].active && !tape.player_participates[i])
2432 struct PlayerInfo *player = &stored_player[i];
2433 int jx = player->jx, jy = player->jy;
2435 player->active = FALSE;
2436 StorePlayer[jx][jy] = 0;
2437 Feld[jx][jy] = EL_EMPTY;
2441 else if (!options.network && !setup.team_mode) /* && !tape.playing */
2443 /* when in single player mode, eliminate all but the first active player */
2445 for (i = 0; i < MAX_PLAYERS; i++)
2447 if (stored_player[i].active)
2449 for (j = i + 1; j < MAX_PLAYERS; j++)
2451 if (stored_player[j].active)
2453 struct PlayerInfo *player = &stored_player[j];
2454 int jx = player->jx, jy = player->jy;
2456 player->active = FALSE;
2457 player->present = FALSE;
2459 StorePlayer[jx][jy] = 0;
2460 Feld[jx][jy] = EL_EMPTY;
2467 /* when recording the game, store which players take part in the game */
2470 for (i = 0; i < MAX_PLAYERS; i++)
2471 if (stored_player[i].active)
2472 tape.player_participates[i] = TRUE;
2477 for (i = 0; i < MAX_PLAYERS; i++)
2479 struct PlayerInfo *player = &stored_player[i];
2481 printf("Player %d: present == %d, connected == %d, active == %d.\n",
2486 if (local_player == player)
2487 printf("Player %d is local player.\n", i+1);
2491 if (BorderElement == EL_EMPTY)
2494 SBX_Right = lev_fieldx - SCR_FIELDX;
2496 SBY_Lower = lev_fieldy - SCR_FIELDY;
2501 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
2503 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
2506 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
2507 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
2509 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
2510 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
2512 /* if local player not found, look for custom element that might create
2513 the player (make some assumptions about the right custom element) */
2514 if (!local_player->present)
2516 int start_x = 0, start_y = 0;
2517 int found_rating = 0;
2518 int found_element = EL_UNDEFINED;
2519 int player_nr = local_player->index_nr;
2521 SCAN_PLAYFIELD(x, y)
2523 int element = Feld[x][y];
2528 if (level.use_start_element[player_nr] &&
2529 level.start_element[player_nr] == element &&
2536 found_element = element;
2539 if (!IS_CUSTOM_ELEMENT(element))
2542 if (CAN_CHANGE(element))
2544 for (i = 0; i < element_info[element].num_change_pages; i++)
2546 /* check for player created from custom element as single target */
2547 content = element_info[element].change_page[i].target_element;
2548 is_player = ELEM_IS_PLAYER(content);
2550 if (is_player && (found_rating < 3 || element < found_element))
2556 found_element = element;
2561 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
2563 /* check for player created from custom element as explosion content */
2564 content = element_info[element].content.e[xx][yy];
2565 is_player = ELEM_IS_PLAYER(content);
2567 if (is_player && (found_rating < 2 || element < found_element))
2569 start_x = x + xx - 1;
2570 start_y = y + yy - 1;
2573 found_element = element;
2576 if (!CAN_CHANGE(element))
2579 for (i = 0; i < element_info[element].num_change_pages; i++)
2581 /* check for player created from custom element as extended target */
2583 element_info[element].change_page[i].target_content.e[xx][yy];
2585 is_player = ELEM_IS_PLAYER(content);
2587 if (is_player && (found_rating < 1 || element < found_element))
2589 start_x = x + xx - 1;
2590 start_y = y + yy - 1;
2593 found_element = element;
2599 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
2600 start_x > SBX_Right + MIDPOSX ? SBX_Right :
2603 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
2604 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
2609 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2610 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2611 local_player->jx - MIDPOSX);
2613 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2614 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2615 local_player->jy - MIDPOSY);
2620 if (!game.restart_level)
2621 CloseDoor(DOOR_CLOSE_1);
2624 FadeOut(REDRAW_FIELD);
2626 /* !!! FIX THIS (START) !!! */
2627 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2629 InitGameEngine_EM();
2631 /* blit playfield from scroll buffer to normal back buffer for fading in */
2632 BlitScreenToBitmap_EM(backbuffer);
2639 /* after drawing the level, correct some elements */
2640 if (game.timegate_time_left == 0)
2641 CloseAllOpenTimegates();
2643 /* blit playfield from scroll buffer to normal back buffer for fading in */
2644 if (setup.soft_scrolling)
2645 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
2647 redraw_mask |= REDRAW_FROM_BACKBUFFER;
2649 /* !!! FIX THIS (END) !!! */
2652 FadeIn(REDRAW_FIELD);
2656 if (!game.restart_level)
2658 /* copy default game door content to main double buffer */
2659 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2660 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2663 SetPanelBackground();
2664 SetDrawBackgroundMask(REDRAW_DOOR_1);
2666 DrawGameDoorValues();
2668 if (!game.restart_level)
2672 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
2673 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
2674 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
2678 /* copy actual game door content to door double buffer for OpenDoor() */
2679 BlitBitmap(drawto, bitmap_db_door,
2680 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
2682 OpenDoor(DOOR_OPEN_ALL);
2684 PlaySound(SND_GAME_STARTING);
2686 if (setup.sound_music)
2689 KeyboardAutoRepeatOffUnlessAutoplay();
2693 for (i = 0; i < MAX_PLAYERS; i++)
2694 printf("Player %d %sactive.\n",
2695 i + 1, (stored_player[i].active ? "" : "not "));
2706 game.restart_level = FALSE;
2709 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
2711 /* this is used for non-R'n'D game engines to update certain engine values */
2713 /* needed to determine if sounds are played within the visible screen area */
2714 scroll_x = actual_scroll_x;
2715 scroll_y = actual_scroll_y;
2718 void InitMovDir(int x, int y)
2720 int i, element = Feld[x][y];
2721 static int xy[4][2] =
2728 static int direction[3][4] =
2730 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
2731 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
2732 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
2741 Feld[x][y] = EL_BUG;
2742 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
2745 case EL_SPACESHIP_RIGHT:
2746 case EL_SPACESHIP_UP:
2747 case EL_SPACESHIP_LEFT:
2748 case EL_SPACESHIP_DOWN:
2749 Feld[x][y] = EL_SPACESHIP;
2750 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
2753 case EL_BD_BUTTERFLY_RIGHT:
2754 case EL_BD_BUTTERFLY_UP:
2755 case EL_BD_BUTTERFLY_LEFT:
2756 case EL_BD_BUTTERFLY_DOWN:
2757 Feld[x][y] = EL_BD_BUTTERFLY;
2758 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
2761 case EL_BD_FIREFLY_RIGHT:
2762 case EL_BD_FIREFLY_UP:
2763 case EL_BD_FIREFLY_LEFT:
2764 case EL_BD_FIREFLY_DOWN:
2765 Feld[x][y] = EL_BD_FIREFLY;
2766 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
2769 case EL_PACMAN_RIGHT:
2771 case EL_PACMAN_LEFT:
2772 case EL_PACMAN_DOWN:
2773 Feld[x][y] = EL_PACMAN;
2774 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
2777 case EL_YAMYAM_LEFT:
2778 case EL_YAMYAM_RIGHT:
2780 case EL_YAMYAM_DOWN:
2781 Feld[x][y] = EL_YAMYAM;
2782 MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
2785 case EL_SP_SNIKSNAK:
2786 MovDir[x][y] = MV_UP;
2789 case EL_SP_ELECTRON:
2790 MovDir[x][y] = MV_LEFT;
2797 Feld[x][y] = EL_MOLE;
2798 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
2802 if (IS_CUSTOM_ELEMENT(element))
2804 struct ElementInfo *ei = &element_info[element];
2805 int move_direction_initial = ei->move_direction_initial;
2806 int move_pattern = ei->move_pattern;
2808 if (move_direction_initial == MV_START_PREVIOUS)
2810 if (MovDir[x][y] != MV_NONE)
2813 move_direction_initial = MV_START_AUTOMATIC;
2816 if (move_direction_initial == MV_START_RANDOM)
2817 MovDir[x][y] = 1 << RND(4);
2818 else if (move_direction_initial & MV_ANY_DIRECTION)
2819 MovDir[x][y] = move_direction_initial;
2820 else if (move_pattern == MV_ALL_DIRECTIONS ||
2821 move_pattern == MV_TURNING_LEFT ||
2822 move_pattern == MV_TURNING_RIGHT ||
2823 move_pattern == MV_TURNING_LEFT_RIGHT ||
2824 move_pattern == MV_TURNING_RIGHT_LEFT ||
2825 move_pattern == MV_TURNING_RANDOM)
2826 MovDir[x][y] = 1 << RND(4);
2827 else if (move_pattern == MV_HORIZONTAL)
2828 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
2829 else if (move_pattern == MV_VERTICAL)
2830 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
2831 else if (move_pattern & MV_ANY_DIRECTION)
2832 MovDir[x][y] = element_info[element].move_pattern;
2833 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
2834 move_pattern == MV_ALONG_RIGHT_SIDE)
2836 /* use random direction as default start direction */
2837 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2838 MovDir[x][y] = 1 << RND(4);
2840 for (i = 0; i < NUM_DIRECTIONS; i++)
2842 int x1 = x + xy[i][0];
2843 int y1 = y + xy[i][1];
2845 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2847 if (move_pattern == MV_ALONG_RIGHT_SIDE)
2848 MovDir[x][y] = direction[0][i];
2850 MovDir[x][y] = direction[1][i];
2859 MovDir[x][y] = 1 << RND(4);
2861 if (element != EL_BUG &&
2862 element != EL_SPACESHIP &&
2863 element != EL_BD_BUTTERFLY &&
2864 element != EL_BD_FIREFLY)
2867 for (i = 0; i < NUM_DIRECTIONS; i++)
2869 int x1 = x + xy[i][0];
2870 int y1 = y + xy[i][1];
2872 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2874 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
2876 MovDir[x][y] = direction[0][i];
2879 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
2880 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
2882 MovDir[x][y] = direction[1][i];
2891 GfxDir[x][y] = MovDir[x][y];
2894 void InitAmoebaNr(int x, int y)
2897 int group_nr = AmoebeNachbarNr(x, y);
2901 for (i = 1; i < MAX_NUM_AMOEBA; i++)
2903 if (AmoebaCnt[i] == 0)
2911 AmoebaNr[x][y] = group_nr;
2912 AmoebaCnt[group_nr]++;
2913 AmoebaCnt2[group_nr]++;
2916 static void PlayerWins(struct PlayerInfo *player)
2918 player->LevelSolved = TRUE;
2919 player->GameOver = TRUE;
2921 player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2922 level.native_em_level->lev->score : player->score);
2927 static int time, time_final;
2928 static int score, score_final;
2929 static int game_over_delay_1 = 0;
2930 static int game_over_delay_2 = 0;
2931 int game_over_delay_value_1 = 50;
2932 int game_over_delay_value_2 = 50;
2934 if (!local_player->LevelSolved_GameWon)
2938 /* do not start end game actions before the player stops moving (to exit) */
2939 if (local_player->MovPos)
2942 local_player->LevelSolved_GameWon = TRUE;
2943 local_player->LevelSolved_SaveTape = tape.recording;
2944 local_player->LevelSolved_SaveScore = !tape.playing;
2946 if (tape.auto_play) /* tape might already be stopped here */
2947 tape.auto_play_level_solved = TRUE;
2953 game_over_delay_1 = game_over_delay_value_1;
2954 game_over_delay_2 = game_over_delay_value_2;
2956 time = time_final = (level.time == 0 ? TimePlayed : TimeLeft);
2957 score = score_final = local_player->score_final;
2962 score_final += TimeLeft * level.score[SC_TIME_BONUS];
2964 else if (level.time == 0 && TimePlayed < 999)
2967 score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
2970 local_player->score_final = score_final;
2972 if (level_editor_test_game)
2975 score = score_final;
2977 DrawGameValue_Time(time);
2978 DrawGameValue_Score(score);
2981 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
2983 if (ExitX >= 0 && ExitY >= 0) /* local player has left the level */
2985 /* close exit door after last player */
2986 if ((AllPlayersGone &&
2987 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
2988 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
2989 Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
2990 Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
2991 Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
2993 int element = Feld[ExitX][ExitY];
2996 if (element == EL_EM_EXIT_OPEN ||
2997 element == EL_EM_STEEL_EXIT_OPEN)
3004 Feld[ExitX][ExitY] =
3005 (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
3006 element == EL_EM_EXIT_OPEN ? EL_EM_EXIT_CLOSING :
3007 element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING:
3008 element == EL_STEEL_EXIT_OPEN ? EL_STEEL_EXIT_CLOSING:
3009 EL_EM_STEEL_EXIT_CLOSING);
3011 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
3015 /* player disappears */
3016 DrawLevelField(ExitX, ExitY);
3019 for (i = 0; i < MAX_PLAYERS; i++)
3021 struct PlayerInfo *player = &stored_player[i];
3023 if (player->present)
3025 RemovePlayer(player);
3027 /* player disappears */
3028 DrawLevelField(player->jx, player->jy);
3033 PlaySound(SND_GAME_WINNING);
3036 if (game_over_delay_1 > 0)
3038 game_over_delay_1--;
3043 if (time != time_final)
3045 int time_to_go = ABS(time_final - time);
3046 int time_count_dir = (time < time_final ? +1 : -1);
3047 int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
3049 time += time_count_steps * time_count_dir;
3050 score += time_count_steps * level.score[SC_TIME_BONUS];
3052 DrawGameValue_Time(time);
3053 DrawGameValue_Score(score);
3055 if (time == time_final)
3056 StopSound(SND_GAME_LEVELTIME_BONUS);
3057 else if (setup.sound_loops)
3058 PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
3060 PlaySound(SND_GAME_LEVELTIME_BONUS);
3065 local_player->LevelSolved_PanelOff = TRUE;
3067 if (game_over_delay_2 > 0)
3069 game_over_delay_2--;
3082 boolean raise_level = FALSE;
3084 local_player->LevelSolved_GameEnd = TRUE;
3086 CloseDoor(DOOR_CLOSE_1);
3088 if (local_player->LevelSolved_SaveTape)
3095 SaveTapeChecked(tape.level_nr); /* ask to save tape */
3097 SaveTape(tape.level_nr); /* ask to save tape */
3101 if (level_editor_test_game)
3103 game_status = GAME_MODE_MAIN;
3110 if (!local_player->LevelSolved_SaveScore)
3112 FadeOut(REDRAW_FIELD);
3114 game_status = GAME_MODE_MAIN;
3116 DrawAndFadeInMainMenu(REDRAW_FIELD);
3121 if (level_nr == leveldir_current->handicap_level)
3123 leveldir_current->handicap_level++;
3124 SaveLevelSetup_SeriesInfo();
3127 if (level_nr < leveldir_current->last_level)
3128 raise_level = TRUE; /* advance to next level */
3130 if ((hi_pos = NewHiScore()) >= 0)
3132 game_status = GAME_MODE_SCORES;
3134 DrawHallOfFame(hi_pos);
3144 FadeOut(REDRAW_FIELD);
3146 game_status = GAME_MODE_MAIN;
3154 DrawAndFadeInMainMenu(REDRAW_FIELD);
3163 LoadScore(level_nr);
3165 if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
3166 local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score)
3169 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
3171 if (local_player->score_final > highscore[k].Score)
3173 /* player has made it to the hall of fame */
3175 if (k < MAX_SCORE_ENTRIES - 1)
3177 int m = MAX_SCORE_ENTRIES - 1;
3180 for (l = k; l < MAX_SCORE_ENTRIES; l++)
3181 if (strEqual(setup.player_name, highscore[l].Name))
3183 if (m == k) /* player's new highscore overwrites his old one */
3187 for (l = m; l > k; l--)
3189 strcpy(highscore[l].Name, highscore[l - 1].Name);
3190 highscore[l].Score = highscore[l - 1].Score;
3197 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
3198 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
3199 highscore[k].Score = local_player->score_final;
3205 else if (!strncmp(setup.player_name, highscore[k].Name,
3206 MAX_PLAYER_NAME_LEN))
3207 break; /* player already there with a higher score */
3213 SaveScore(level_nr);
3218 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
3220 int element = Feld[x][y];
3221 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3222 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
3223 int horiz_move = (dx != 0);
3224 int sign = (horiz_move ? dx : dy);
3225 int step = sign * element_info[element].move_stepsize;
3227 /* special values for move stepsize for spring and things on conveyor belt */
3230 if (CAN_FALL(element) &&
3231 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
3232 step = sign * MOVE_STEPSIZE_NORMAL / 2;
3233 else if (element == EL_SPRING)
3234 step = sign * MOVE_STEPSIZE_NORMAL * 2;
3240 inline static int getElementMoveStepsize(int x, int y)
3242 return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
3245 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
3247 if (player->GfxAction != action || player->GfxDir != dir)
3250 printf("Player frame reset! (%d => %d, %d => %d)\n",
3251 player->GfxAction, action, player->GfxDir, dir);
3254 player->GfxAction = action;
3255 player->GfxDir = dir;
3257 player->StepFrame = 0;
3261 #if USE_GFX_RESET_GFX_ANIMATION
3262 static void ResetGfxFrame(int x, int y, boolean redraw)
3264 int element = Feld[x][y];
3265 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3266 int last_gfx_frame = GfxFrame[x][y];
3268 if (graphic_info[graphic].anim_global_sync)
3269 GfxFrame[x][y] = FrameCounter;
3270 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
3271 GfxFrame[x][y] = CustomValue[x][y];
3272 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
3273 GfxFrame[x][y] = element_info[element].collect_score;
3274 else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
3275 GfxFrame[x][y] = ChangeDelay[x][y];
3277 if (redraw && GfxFrame[x][y] != last_gfx_frame)
3278 DrawLevelGraphicAnimation(x, y, graphic);
3282 static void ResetGfxAnimation(int x, int y)
3284 GfxAction[x][y] = ACTION_DEFAULT;
3285 GfxDir[x][y] = MovDir[x][y];
3288 #if USE_GFX_RESET_GFX_ANIMATION
3289 ResetGfxFrame(x, y, FALSE);
3293 static void ResetRandomAnimationValue(int x, int y)
3295 GfxRandom[x][y] = INIT_GFX_RANDOM();
3298 void InitMovingField(int x, int y, int direction)
3300 int element = Feld[x][y];
3301 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3302 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
3305 boolean is_moving_before, is_moving_after;
3307 boolean continues_moving = (WasJustMoving[x][y] && direction == MovDir[x][y]);
3310 /* check if element was/is moving or being moved before/after mode change */
3312 is_moving_before = WasJustMoving[x][y];
3314 is_moving_before = (getElementMoveStepsizeExt(x, y, MovDir[x][y]) != 0);
3316 is_moving_after = (getElementMoveStepsizeExt(x, y, direction) != 0);
3318 /* reset animation only for moving elements which change direction of moving
3319 or which just started or stopped moving
3320 (else CEs with property "can move" / "not moving" are reset each frame) */
3321 #if USE_GFX_RESET_ONLY_WHEN_MOVING
3323 if (is_moving_before != is_moving_after ||
3324 direction != MovDir[x][y])
3325 ResetGfxAnimation(x, y);
3327 if ((is_moving_before || is_moving_after) && !continues_moving)
3328 ResetGfxAnimation(x, y);
3331 if (!continues_moving)
3332 ResetGfxAnimation(x, y);
3335 MovDir[x][y] = direction;
3336 GfxDir[x][y] = direction;
3338 #if USE_GFX_RESET_ONLY_WHEN_MOVING
3339 GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
3340 direction == MV_DOWN && CAN_FALL(element) ?
3341 ACTION_FALLING : ACTION_MOVING);
3343 GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
3344 ACTION_FALLING : ACTION_MOVING);
3347 /* this is needed for CEs with property "can move" / "not moving" */
3349 if (is_moving_after)
3351 if (Feld[newx][newy] == EL_EMPTY)
3352 Feld[newx][newy] = EL_BLOCKED;
3354 MovDir[newx][newy] = MovDir[x][y];
3356 #if USE_NEW_CUSTOM_VALUE
3357 CustomValue[newx][newy] = CustomValue[x][y];
3360 GfxFrame[newx][newy] = GfxFrame[x][y];
3361 GfxRandom[newx][newy] = GfxRandom[x][y];
3362 GfxAction[newx][newy] = GfxAction[x][y];
3363 GfxDir[newx][newy] = GfxDir[x][y];
3367 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
3369 int direction = MovDir[x][y];
3370 int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
3371 int newy = y + (direction & MV_UP ? -1 : direction & MV_DOWN ? +1 : 0);
3377 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
3379 int oldx = x, oldy = y;
3380 int direction = MovDir[x][y];
3382 if (direction == MV_LEFT)
3384 else if (direction == MV_RIGHT)
3386 else if (direction == MV_UP)
3388 else if (direction == MV_DOWN)
3391 *comes_from_x = oldx;
3392 *comes_from_y = oldy;
3395 int MovingOrBlocked2Element(int x, int y)
3397 int element = Feld[x][y];
3399 if (element == EL_BLOCKED)
3403 Blocked2Moving(x, y, &oldx, &oldy);
3404 return Feld[oldx][oldy];
3410 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
3412 /* like MovingOrBlocked2Element(), but if element is moving
3413 and (x,y) is the field the moving element is just leaving,
3414 return EL_BLOCKED instead of the element value */
3415 int element = Feld[x][y];
3417 if (IS_MOVING(x, y))
3419 if (element == EL_BLOCKED)
3423 Blocked2Moving(x, y, &oldx, &oldy);
3424 return Feld[oldx][oldy];
3433 static void RemoveField(int x, int y)
3435 Feld[x][y] = EL_EMPTY;
3441 #if USE_NEW_CUSTOM_VALUE
3442 CustomValue[x][y] = 0;
3446 ChangeDelay[x][y] = 0;
3447 ChangePage[x][y] = -1;
3448 Pushed[x][y] = FALSE;
3451 ExplodeField[x][y] = EX_TYPE_NONE;
3454 GfxElement[x][y] = EL_UNDEFINED;
3455 GfxAction[x][y] = ACTION_DEFAULT;
3456 GfxDir[x][y] = MV_NONE;
3459 void RemoveMovingField(int x, int y)
3461 int oldx = x, oldy = y, newx = x, newy = y;
3462 int element = Feld[x][y];
3463 int next_element = EL_UNDEFINED;
3465 if (element != EL_BLOCKED && !IS_MOVING(x, y))
3468 if (IS_MOVING(x, y))
3470 Moving2Blocked(x, y, &newx, &newy);
3472 if (Feld[newx][newy] != EL_BLOCKED)
3474 /* element is moving, but target field is not free (blocked), but
3475 already occupied by something different (example: acid pool);
3476 in this case, only remove the moving field, but not the target */
3478 RemoveField(oldx, oldy);
3480 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
3482 DrawLevelField(oldx, oldy);
3487 else if (element == EL_BLOCKED)
3489 Blocked2Moving(x, y, &oldx, &oldy);
3490 if (!IS_MOVING(oldx, oldy))
3494 if (element == EL_BLOCKED &&
3495 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
3496 Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
3497 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
3498 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
3499 Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
3500 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
3501 next_element = get_next_element(Feld[oldx][oldy]);
3503 RemoveField(oldx, oldy);
3504 RemoveField(newx, newy);
3506 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
3508 if (next_element != EL_UNDEFINED)
3509 Feld[oldx][oldy] = next_element;
3511 DrawLevelField(oldx, oldy);
3512 DrawLevelField(newx, newy);
3515 void DrawDynamite(int x, int y)
3517 int sx = SCREENX(x), sy = SCREENY(y);
3518 int graphic = el2img(Feld[x][y]);
3521 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
3524 if (IS_WALKABLE_INSIDE(Back[x][y]))
3528 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
3529 else if (Store[x][y])
3530 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
3532 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
3534 if (Back[x][y] || Store[x][y])
3535 DrawGraphicThruMask(sx, sy, graphic, frame);
3537 DrawGraphic(sx, sy, graphic, frame);
3540 void CheckDynamite(int x, int y)
3542 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
3546 if (MovDelay[x][y] != 0)
3549 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
3555 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
3560 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
3562 boolean num_checked_players = 0;
3565 for (i = 0; i < MAX_PLAYERS; i++)
3567 if (stored_player[i].active)
3569 int sx = stored_player[i].jx;
3570 int sy = stored_player[i].jy;
3572 if (num_checked_players == 0)
3579 *sx1 = MIN(*sx1, sx);
3580 *sy1 = MIN(*sy1, sy);
3581 *sx2 = MAX(*sx2, sx);
3582 *sy2 = MAX(*sy2, sy);
3585 num_checked_players++;
3590 static boolean checkIfAllPlayersFitToScreen_RND()
3592 int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
3594 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
3596 return (sx2 - sx1 < SCR_FIELDX &&
3597 sy2 - sy1 < SCR_FIELDY);
3600 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
3602 int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
3604 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
3606 *sx = (sx1 + sx2) / 2;
3607 *sy = (sy1 + sy2) / 2;
3610 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
3611 boolean center_screen, boolean quick_relocation)
3613 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3614 boolean no_delay = (tape.warp_forward);
3615 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
3616 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
3618 if (quick_relocation)
3620 int offset = (setup.scroll_delay ? 3 : 0);
3622 if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
3626 scroll_x = (x < SBX_Left + MIDPOSX ? SBX_Left :
3627 x > SBX_Right + MIDPOSX ? SBX_Right :
3630 scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
3631 y > SBY_Lower + MIDPOSY ? SBY_Lower :
3636 /* quick relocation (without scrolling), but do not center screen */
3638 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
3639 old_x > SBX_Right + MIDPOSX ? SBX_Right :
3642 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
3643 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
3646 int offset_x = x + (scroll_x - center_scroll_x);
3647 int offset_y = y + (scroll_y - center_scroll_y);
3649 scroll_x = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
3650 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
3651 offset_x - MIDPOSX);
3653 scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
3654 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
3655 offset_y - MIDPOSY);
3660 if ((move_dir == MV_LEFT && scroll_x > x - MIDPOSX + offset) ||
3661 (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
3662 scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
3664 if ((move_dir == MV_UP && scroll_y > y - MIDPOSY + offset) ||
3665 (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
3666 scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
3668 /* don't scroll over playfield boundaries */
3669 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
3670 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
3672 /* don't scroll over playfield boundaries */
3673 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
3674 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
3677 RedrawPlayfield(TRUE, 0,0,0,0);
3681 int scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left :
3682 x > SBX_Right + MIDPOSX ? SBX_Right :
3685 int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
3686 y > SBY_Lower + MIDPOSY ? SBY_Lower :
3689 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
3691 while (scroll_x != scroll_xx || scroll_y != scroll_yy)
3694 int fx = FX, fy = FY;
3696 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
3697 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
3699 if (dx == 0 && dy == 0) /* no scrolling needed at all */
3705 fx += dx * TILEX / 2;
3706 fy += dy * TILEY / 2;
3708 ScrollLevel(dx, dy);
3711 /* scroll in two steps of half tile size to make things smoother */
3712 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
3714 Delay(wait_delay_value);
3716 /* scroll second step to align at full tile size */
3718 Delay(wait_delay_value);
3723 Delay(wait_delay_value);
3727 void RelocatePlayer(int jx, int jy, int el_player_raw)
3729 int el_player = GET_PLAYER_ELEMENT(el_player_raw);
3730 int player_nr = GET_PLAYER_NR(el_player);
3731 struct PlayerInfo *player = &stored_player[player_nr];
3732 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3733 boolean no_delay = (tape.warp_forward);
3734 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
3735 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
3736 int old_jx = player->jx;
3737 int old_jy = player->jy;
3738 int old_element = Feld[old_jx][old_jy];
3739 int element = Feld[jx][jy];
3740 boolean player_relocated = (old_jx != jx || old_jy != jy);
3742 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
3743 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
3744 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
3745 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
3746 int leave_side_horiz = move_dir_horiz;
3747 int leave_side_vert = move_dir_vert;
3748 int enter_side = enter_side_horiz | enter_side_vert;
3749 int leave_side = leave_side_horiz | leave_side_vert;
3751 if (player->GameOver) /* do not reanimate dead player */
3754 if (!player_relocated) /* no need to relocate the player */
3757 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
3759 RemoveField(jx, jy); /* temporarily remove newly placed player */
3760 DrawLevelField(jx, jy);
3763 if (player->present)
3765 while (player->MovPos)
3767 ScrollPlayer(player, SCROLL_GO_ON);
3768 ScrollScreen(NULL, SCROLL_GO_ON);
3770 AdvanceFrameAndPlayerCounters(player->index_nr);
3775 Delay(wait_delay_value);
3778 DrawPlayer(player); /* needed here only to cleanup last field */
3779 DrawLevelField(player->jx, player->jy); /* remove player graphic */
3781 player->is_moving = FALSE;
3784 if (IS_CUSTOM_ELEMENT(old_element))
3785 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
3787 player->index_bit, leave_side);
3789 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
3791 player->index_bit, leave_side);
3793 Feld[jx][jy] = el_player;
3794 InitPlayerField(jx, jy, el_player, TRUE);
3796 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
3798 Feld[jx][jy] = element;
3799 InitField(jx, jy, FALSE);
3802 /* only visually relocate centered player */
3803 DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
3804 FALSE, level.instant_relocation);
3806 TestIfPlayerTouchesBadThing(jx, jy);
3807 TestIfPlayerTouchesCustomElement(jx, jy);
3809 if (IS_CUSTOM_ELEMENT(element))
3810 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
3811 player->index_bit, enter_side);
3813 CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
3814 player->index_bit, enter_side);
3817 void Explode(int ex, int ey, int phase, int mode)
3823 /* !!! eliminate this variable !!! */
3824 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
3826 if (game.explosions_delayed)
3828 ExplodeField[ex][ey] = mode;
3832 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
3834 int center_element = Feld[ex][ey];
3835 int artwork_element, explosion_element; /* set these values later */
3838 /* --- This is only really needed (and now handled) in "Impact()". --- */
3839 /* do not explode moving elements that left the explode field in time */
3840 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
3841 center_element == EL_EMPTY &&
3842 (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
3847 /* !!! at this place, the center element may be EL_BLOCKED !!! */
3848 if (mode == EX_TYPE_NORMAL ||
3849 mode == EX_TYPE_CENTER ||
3850 mode == EX_TYPE_CROSS)
3851 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
3854 /* remove things displayed in background while burning dynamite */
3855 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
3858 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
3860 /* put moving element to center field (and let it explode there) */
3861 center_element = MovingOrBlocked2Element(ex, ey);
3862 RemoveMovingField(ex, ey);
3863 Feld[ex][ey] = center_element;
3866 /* now "center_element" is finally determined -- set related values now */
3867 artwork_element = center_element; /* for custom player artwork */
3868 explosion_element = center_element; /* for custom player artwork */
3870 if (IS_PLAYER(ex, ey))
3872 int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
3874 artwork_element = stored_player[player_nr].artwork_element;
3876 if (level.use_explosion_element[player_nr])
3878 explosion_element = level.explosion_element[player_nr];
3879 artwork_element = explosion_element;
3884 if (mode == EX_TYPE_NORMAL ||
3885 mode == EX_TYPE_CENTER ||
3886 mode == EX_TYPE_CROSS)
3887 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
3890 last_phase = element_info[explosion_element].explosion_delay + 1;
3892 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
3894 int xx = x - ex + 1;
3895 int yy = y - ey + 1;
3898 if (!IN_LEV_FIELD(x, y) ||
3899 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
3900 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
3903 element = Feld[x][y];
3905 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
3907 element = MovingOrBlocked2Element(x, y);
3909 if (!IS_EXPLOSION_PROOF(element))
3910 RemoveMovingField(x, y);
3913 /* indestructible elements can only explode in center (but not flames) */
3914 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
3915 mode == EX_TYPE_BORDER)) ||
3916 element == EL_FLAMES)
3919 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
3920 behaviour, for example when touching a yamyam that explodes to rocks
3921 with active deadly shield, a rock is created under the player !!! */
3922 /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
3924 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
3925 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
3926 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
3928 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
3931 if (IS_ACTIVE_BOMB(element))
3933 /* re-activate things under the bomb like gate or penguin */
3934 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
3941 /* save walkable background elements while explosion on same tile */
3942 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
3943 (x != ex || y != ey || mode == EX_TYPE_BORDER))
3944 Back[x][y] = element;
3946 /* ignite explodable elements reached by other explosion */
3947 if (element == EL_EXPLOSION)
3948 element = Store2[x][y];
3950 if (AmoebaNr[x][y] &&
3951 (element == EL_AMOEBA_FULL ||
3952 element == EL_BD_AMOEBA ||
3953 element == EL_AMOEBA_GROWING))
3955 AmoebaCnt[AmoebaNr[x][y]]--;
3956 AmoebaCnt2[AmoebaNr[x][y]]--;
3961 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
3963 int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
3965 Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
3967 if (PLAYERINFO(ex, ey)->use_murphy)
3968 Store[x][y] = EL_EMPTY;
3971 /* !!! check this case -- currently needed for rnd_rado_negundo_v,
3972 !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
3973 else if (ELEM_IS_PLAYER(center_element))
3974 Store[x][y] = EL_EMPTY;
3975 else if (center_element == EL_YAMYAM)
3976 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
3977 else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
3978 Store[x][y] = element_info[center_element].content.e[xx][yy];
3980 /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
3981 (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
3982 otherwise) -- FIX THIS !!! */
3983 else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
3984 Store[x][y] = element_info[element].content.e[1][1];
3986 else if (!CAN_EXPLODE(element))
3987 Store[x][y] = element_info[element].content.e[1][1];
3990 Store[x][y] = EL_EMPTY;
3992 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
3993 center_element == EL_AMOEBA_TO_DIAMOND)
3994 Store2[x][y] = element;
3996 Feld[x][y] = EL_EXPLOSION;
3997 GfxElement[x][y] = artwork_element;
3999 ExplodePhase[x][y] = 1;
4000 ExplodeDelay[x][y] = last_phase;
4005 if (center_element == EL_YAMYAM)
4006 game.yamyam_content_nr =
4007 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
4019 GfxFrame[x][y] = 0; /* restart explosion animation */
4021 last_phase = ExplodeDelay[x][y];
4023 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
4027 /* activate this even in non-DEBUG version until cause for crash in
4028 getGraphicAnimationFrame() (see below) is found and eliminated */
4034 /* this can happen if the player leaves an explosion just in time */
4035 if (GfxElement[x][y] == EL_UNDEFINED)
4036 GfxElement[x][y] = EL_EMPTY;
4038 if (GfxElement[x][y] == EL_UNDEFINED)
4041 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
4042 printf("Explode(): This should never happen!\n");
4045 GfxElement[x][y] = EL_EMPTY;
4051 border_element = Store2[x][y];
4052 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
4053 border_element = StorePlayer[x][y];
4055 if (phase == element_info[border_element].ignition_delay ||
4056 phase == last_phase)
4058 boolean border_explosion = FALSE;
4060 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
4061 !PLAYER_EXPLOSION_PROTECTED(x, y))
4063 KillPlayerUnlessExplosionProtected(x, y);
4064 border_explosion = TRUE;
4066 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
4068 Feld[x][y] = Store2[x][y];
4071 border_explosion = TRUE;
4073 else if (border_element == EL_AMOEBA_TO_DIAMOND)
4075 AmoebeUmwandeln(x, y);
4077 border_explosion = TRUE;
4080 /* if an element just explodes due to another explosion (chain-reaction),
4081 do not immediately end the new explosion when it was the last frame of
4082 the explosion (as it would be done in the following "if"-statement!) */
4083 if (border_explosion && phase == last_phase)
4087 if (phase == last_phase)
4091 element = Feld[x][y] = Store[x][y];
4092 Store[x][y] = Store2[x][y] = 0;
4093 GfxElement[x][y] = EL_UNDEFINED;
4095 /* player can escape from explosions and might therefore be still alive */
4096 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
4097 element <= EL_PLAYER_IS_EXPLODING_4)
4099 int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
4100 int explosion_element = EL_PLAYER_1 + player_nr;
4101 int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
4102 int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
4104 if (level.use_explosion_element[player_nr])
4105 explosion_element = level.explosion_element[player_nr];
4107 Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
4108 element_info[explosion_element].content.e[xx][yy]);
4111 /* restore probably existing indestructible background element */
4112 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
4113 element = Feld[x][y] = Back[x][y];
4116 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
4117 GfxDir[x][y] = MV_NONE;
4118 ChangeDelay[x][y] = 0;
4119 ChangePage[x][y] = -1;
4121 #if USE_NEW_CUSTOM_VALUE
4122 CustomValue[x][y] = 0;
4125 InitField_WithBug2(x, y, FALSE);
4127 DrawLevelField(x, y);
4129 TestIfElementTouchesCustomElement(x, y);
4131 if (GFX_CRUMBLED(element))
4132 DrawLevelFieldCrumbledSandNeighbours(x, y);
4134 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
4135 StorePlayer[x][y] = 0;
4137 if (ELEM_IS_PLAYER(element))
4138 RelocatePlayer(x, y, element);
4140 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
4142 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
4143 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4146 DrawLevelFieldCrumbledSand(x, y);
4148 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
4150 DrawLevelElement(x, y, Back[x][y]);
4151 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
4153 else if (IS_WALKABLE_UNDER(Back[x][y]))
4155 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
4156 DrawLevelElementThruMask(x, y, Back[x][y]);
4158 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
4159 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
4163 void DynaExplode(int ex, int ey)
4166 int dynabomb_element = Feld[ex][ey];
4167 int dynabomb_size = 1;
4168 boolean dynabomb_xl = FALSE;
4169 struct PlayerInfo *player;
4170 static int xy[4][2] =
4178 if (IS_ACTIVE_BOMB(dynabomb_element))
4180 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
4181 dynabomb_size = player->dynabomb_size;
4182 dynabomb_xl = player->dynabomb_xl;
4183 player->dynabombs_left++;
4186 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
4188 for (i = 0; i < NUM_DIRECTIONS; i++)
4190 for (j = 1; j <= dynabomb_size; j++)
4192 int x = ex + j * xy[i][0];
4193 int y = ey + j * xy[i][1];
4196 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
4199 element = Feld[x][y];
4201 /* do not restart explosions of fields with active bombs */
4202 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
4205 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
4207 if (element != EL_EMPTY && element != EL_EXPLOSION &&
4208 !IS_DIGGABLE(element) && !dynabomb_xl)
4214 void Bang(int x, int y)
4216 int element = MovingOrBlocked2Element(x, y);
4217 int explosion_type = EX_TYPE_NORMAL;
4219 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
4221 struct PlayerInfo *player = PLAYERINFO(x, y);
4223 element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
4224 player->element_nr);
4226 if (level.use_explosion_element[player->index_nr])
4228 int explosion_element = level.explosion_element[player->index_nr];
4230 if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
4231 explosion_type = EX_TYPE_CROSS;
4232 else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
4233 explosion_type = EX_TYPE_CENTER;
4241 case EL_BD_BUTTERFLY:
4244 case EL_DARK_YAMYAM:
4248 RaiseScoreElement(element);
4251 case EL_DYNABOMB_PLAYER_1_ACTIVE:
4252 case EL_DYNABOMB_PLAYER_2_ACTIVE:
4253 case EL_DYNABOMB_PLAYER_3_ACTIVE:
4254 case EL_DYNABOMB_PLAYER_4_ACTIVE:
4255 case EL_DYNABOMB_INCREASE_NUMBER:
4256 case EL_DYNABOMB_INCREASE_SIZE:
4257 case EL_DYNABOMB_INCREASE_POWER:
4258 explosion_type = EX_TYPE_DYNA;
4261 case EL_DC_LANDMINE:
4263 case EL_EM_EXIT_OPEN:
4264 case EL_EM_STEEL_EXIT_OPEN:
4266 explosion_type = EX_TYPE_CENTER;
4271 case EL_LAMP_ACTIVE:
4272 case EL_AMOEBA_TO_DIAMOND:
4273 if (!IS_PLAYER(x, y)) /* penguin and player may be at same field */
4274 explosion_type = EX_TYPE_CENTER;
4278 if (element_info[element].explosion_type == EXPLODES_CROSS)
4279 explosion_type = EX_TYPE_CROSS;
4280 else if (element_info[element].explosion_type == EXPLODES_1X1)
4281 explosion_type = EX_TYPE_CENTER;
4285 if (explosion_type == EX_TYPE_DYNA)
4288 Explode(x, y, EX_PHASE_START, explosion_type);
4290 CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
4293 void SplashAcid(int x, int y)
4295 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
4296 (!IN_LEV_FIELD(x - 1, y - 2) ||
4297 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
4298 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
4300 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
4301 (!IN_LEV_FIELD(x + 1, y - 2) ||
4302 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
4303 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
4305 PlayLevelSound(x, y, SND_ACID_SPLASHING);
4308 static void InitBeltMovement()
4310 static int belt_base_element[4] =
4312 EL_CONVEYOR_BELT_1_LEFT,
4313 EL_CONVEYOR_BELT_2_LEFT,
4314 EL_CONVEYOR_BELT_3_LEFT,
4315 EL_CONVEYOR_BELT_4_LEFT
4317 static int belt_base_active_element[4] =
4319 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
4320 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
4321 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
4322 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
4327 /* set frame order for belt animation graphic according to belt direction */
4328 for (i = 0; i < NUM_BELTS; i++)
4332 for (j = 0; j < NUM_BELT_PARTS; j++)
4334 int element = belt_base_active_element[belt_nr] + j;
4335 int graphic = el2img(element);
4337 if (game.belt_dir[i] == MV_LEFT)
4338 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
4340 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
4344 SCAN_PLAYFIELD(x, y)
4346 int element = Feld[x][y];
4348 for (i = 0; i < NUM_BELTS; i++)
4350 if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
4352 int e_belt_nr = getBeltNrFromBeltElement(element);
4355 if (e_belt_nr == belt_nr)
4357 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
4359 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
4366 static void ToggleBeltSwitch(int x, int y)
4368 static int belt_base_element[4] =
4370 EL_CONVEYOR_BELT_1_LEFT,
4371 EL_CONVEYOR_BELT_2_LEFT,
4372 EL_CONVEYOR_BELT_3_LEFT,
4373 EL_CONVEYOR_BELT_4_LEFT
4375 static int belt_base_active_element[4] =
4377 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
4378 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
4379 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
4380 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
4382 static int belt_base_switch_element[4] =
4384 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
4385 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
4386 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
4387 EL_CONVEYOR_BELT_4_SWITCH_LEFT
4389 static int belt_move_dir[4] =
4397 int element = Feld[x][y];
4398 int belt_nr = getBeltNrFromBeltSwitchElement(element);
4399 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
4400 int belt_dir = belt_move_dir[belt_dir_nr];
4403 if (!IS_BELT_SWITCH(element))
4406 game.belt_dir_nr[belt_nr] = belt_dir_nr;
4407 game.belt_dir[belt_nr] = belt_dir;
4409 if (belt_dir_nr == 3)
4412 /* set frame order for belt animation graphic according to belt direction */
4413 for (i = 0; i < NUM_BELT_PARTS; i++)
4415 int element = belt_base_active_element[belt_nr] + i;
4416 int graphic = el2img(element);
4418 if (belt_dir == MV_LEFT)
4419 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
4421 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
4424 SCAN_PLAYFIELD(xx, yy)
4426 int element = Feld[xx][yy];
4428 if (IS_BELT_SWITCH(element))
4430 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
4432 if (e_belt_nr == belt_nr)
4434 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
4435 DrawLevelField(xx, yy);
4438 else if (IS_BELT(element) && belt_dir != MV_NONE)
4440 int e_belt_nr = getBeltNrFromBeltElement(element);
4442 if (e_belt_nr == belt_nr)
4444 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
4446 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
4447 DrawLevelField(xx, yy);
4450 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
4452 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
4454 if (e_belt_nr == belt_nr)
4456 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
4458 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
4459 DrawLevelField(xx, yy);
4465 static void ToggleSwitchgateSwitch(int x, int y)
4469 game.switchgate_pos = !game.switchgate_pos;
4471 SCAN_PLAYFIELD(xx, yy)
4473 int element = Feld[xx][yy];
4475 #if !USE_BOTH_SWITCHGATE_SWITCHES
4476 if (element == EL_SWITCHGATE_SWITCH_UP ||
4477 element == EL_SWITCHGATE_SWITCH_DOWN)
4479 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
4480 DrawLevelField(xx, yy);
4482 else if (element == EL_DC_SWITCHGATE_SWITCH_UP ||
4483 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
4485 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
4486 DrawLevelField(xx, yy);
4489 if (element == EL_SWITCHGATE_SWITCH_UP)
4491 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
4492 DrawLevelField(xx, yy);
4494 else if (element == EL_SWITCHGATE_SWITCH_DOWN)
4496 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
4497 DrawLevelField(xx, yy);
4499 else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
4501 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
4502 DrawLevelField(xx, yy);
4504 else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
4506 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
4507 DrawLevelField(xx, yy);
4510 else if (element == EL_SWITCHGATE_OPEN ||
4511 element == EL_SWITCHGATE_OPENING)
4513 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
4515 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
4517 else if (element == EL_SWITCHGATE_CLOSED ||
4518 element == EL_SWITCHGATE_CLOSING)
4520 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
4522 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
4527 static int getInvisibleActiveFromInvisibleElement(int element)
4529 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
4530 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
4531 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
4535 static int getInvisibleFromInvisibleActiveElement(int element)
4537 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
4538 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
4539 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
4543 static void RedrawAllLightSwitchesAndInvisibleElements()
4547 SCAN_PLAYFIELD(x, y)
4549 int element = Feld[x][y];
4551 if (element == EL_LIGHT_SWITCH &&
4552 game.light_time_left > 0)
4554 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
4555 DrawLevelField(x, y);
4557 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
4558 game.light_time_left == 0)
4560 Feld[x][y] = EL_LIGHT_SWITCH;
4561 DrawLevelField(x, y);
4563 else if (element == EL_EMC_DRIPPER &&
4564 game.light_time_left > 0)
4566 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
4567 DrawLevelField(x, y);
4569 else if (element == EL_EMC_DRIPPER_ACTIVE &&
4570 game.light_time_left == 0)
4572 Feld[x][y] = EL_EMC_DRIPPER;
4573 DrawLevelField(x, y);
4575 else if (element == EL_INVISIBLE_STEELWALL ||
4576 element == EL_INVISIBLE_WALL ||
4577 element == EL_INVISIBLE_SAND)
4579 if (game.light_time_left > 0)
4580 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
4582 DrawLevelField(x, y);
4584 /* uncrumble neighbour fields, if needed */
4585 if (element == EL_INVISIBLE_SAND)
4586 DrawLevelFieldCrumbledSandNeighbours(x, y);
4588 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
4589 element == EL_INVISIBLE_WALL_ACTIVE ||
4590 element == EL_INVISIBLE_SAND_ACTIVE)
4592 if (game.light_time_left == 0)
4593 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
4595 DrawLevelField(x, y);
4597 /* re-crumble neighbour fields, if needed */
4598 if (element == EL_INVISIBLE_SAND)
4599 DrawLevelFieldCrumbledSandNeighbours(x, y);
4604 static void RedrawAllInvisibleElementsForLenses()
4608 SCAN_PLAYFIELD(x, y)
4610 int element = Feld[x][y];
4612 if (element == EL_EMC_DRIPPER &&
4613 game.lenses_time_left > 0)
4615 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
4616 DrawLevelField(x, y);
4618 else if (element == EL_EMC_DRIPPER_ACTIVE &&
4619 game.lenses_time_left == 0)
4621 Feld[x][y] = EL_EMC_DRIPPER;
4622 DrawLevelField(x, y);
4624 else if (element == EL_INVISIBLE_STEELWALL ||
4625 element == EL_INVISIBLE_WALL ||
4626 element == EL_INVISIBLE_SAND)
4628 if (game.lenses_time_left > 0)
4629 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
4631 DrawLevelField(x, y);
4633 /* uncrumble neighbour fields, if needed */
4634 if (element == EL_INVISIBLE_SAND)
4635 DrawLevelFieldCrumbledSandNeighbours(x, y);
4637 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
4638 element == EL_INVISIBLE_WALL_ACTIVE ||
4639 element == EL_INVISIBLE_SAND_ACTIVE)
4641 if (game.lenses_time_left == 0)
4642 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
4644 DrawLevelField(x, y);
4646 /* re-crumble neighbour fields, if needed */
4647 if (element == EL_INVISIBLE_SAND)
4648 DrawLevelFieldCrumbledSandNeighbours(x, y);
4653 static void RedrawAllInvisibleElementsForMagnifier()
4657 SCAN_PLAYFIELD(x, y)
4659 int element = Feld[x][y];
4661 if (element == EL_EMC_FAKE_GRASS &&
4662 game.magnify_time_left > 0)
4664 Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
4665 DrawLevelField(x, y);
4667 else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
4668 game.magnify_time_left == 0)
4670 Feld[x][y] = EL_EMC_FAKE_GRASS;
4671 DrawLevelField(x, y);
4673 else if (IS_GATE_GRAY(element) &&
4674 game.magnify_time_left > 0)
4676 Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
4677 element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
4678 IS_EM_GATE_GRAY(element) ?
4679 element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
4680 IS_EMC_GATE_GRAY(element) ?
4681 element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
4683 DrawLevelField(x, y);
4685 else if (IS_GATE_GRAY_ACTIVE(element) &&
4686 game.magnify_time_left == 0)
4688 Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
4689 element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
4690 IS_EM_GATE_GRAY_ACTIVE(element) ?
4691 element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
4692 IS_EMC_GATE_GRAY_ACTIVE(element) ?
4693 element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
4695 DrawLevelField(x, y);
4700 static void ToggleLightSwitch(int x, int y)
4702 int element = Feld[x][y];
4704 game.light_time_left =
4705 (element == EL_LIGHT_SWITCH ?
4706 level.time_light * FRAMES_PER_SECOND : 0);
4708 RedrawAllLightSwitchesAndInvisibleElements();
4711 static void ActivateTimegateSwitch(int x, int y)
4715 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
4717 SCAN_PLAYFIELD(xx, yy)
4719 int element = Feld[xx][yy];
4721 if (element == EL_TIMEGATE_CLOSED ||
4722 element == EL_TIMEGATE_CLOSING)
4724 Feld[xx][yy] = EL_TIMEGATE_OPENING;
4725 PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
4729 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
4731 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
4732 DrawLevelField(xx, yy);
4739 Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
4740 EL_DC_TIMEGATE_SWITCH_ACTIVE);
4742 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
4746 void Impact(int x, int y)
4748 boolean last_line = (y == lev_fieldy - 1);
4749 boolean object_hit = FALSE;
4750 boolean impact = (last_line || object_hit);
4751 int element = Feld[x][y];
4752 int smashed = EL_STEELWALL;
4754 if (!last_line) /* check if element below was hit */
4756 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
4759 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
4760 MovDir[x][y + 1] != MV_DOWN ||
4761 MovPos[x][y + 1] <= TILEY / 2));
4763 /* do not smash moving elements that left the smashed field in time */
4764 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
4765 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
4768 #if USE_QUICKSAND_IMPACT_BUGFIX
4769 if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
4771 RemoveMovingField(x, y + 1);
4772 Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
4773 Feld[x][y + 2] = EL_ROCK;
4774 DrawLevelField(x, y + 2);
4779 if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
4781 RemoveMovingField(x, y + 1);
4782 Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
4783 Feld[x][y + 2] = EL_ROCK;
4784 DrawLevelField(x, y + 2);
4791 smashed = MovingOrBlocked2Element(x, y + 1);
4793 impact = (last_line || object_hit);
4796 if (!last_line && smashed == EL_ACID) /* element falls into acid */
4798 SplashAcid(x, y + 1);
4802 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
4803 /* only reset graphic animation if graphic really changes after impact */
4805 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
4807 ResetGfxAnimation(x, y);
4808 DrawLevelField(x, y);
4811 if (impact && CAN_EXPLODE_IMPACT(element))
4816 else if (impact && element == EL_PEARL &&
4817 smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
4819 ResetGfxAnimation(x, y);
4821 Feld[x][y] = EL_PEARL_BREAKING;
4822 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4825 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
4827 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4832 if (impact && element == EL_AMOEBA_DROP)
4834 if (object_hit && IS_PLAYER(x, y + 1))
4835 KillPlayerUnlessEnemyProtected(x, y + 1);
4836 else if (object_hit && smashed == EL_PENGUIN)
4840 Feld[x][y] = EL_AMOEBA_GROWING;
4841 Store[x][y] = EL_AMOEBA_WET;
4843 ResetRandomAnimationValue(x, y);
4848 if (object_hit) /* check which object was hit */
4850 if ((CAN_PASS_MAGIC_WALL(element) &&
4851 (smashed == EL_MAGIC_WALL ||
4852 smashed == EL_BD_MAGIC_WALL)) ||
4853 (CAN_PASS_DC_MAGIC_WALL(element) &&
4854 smashed == EL_DC_MAGIC_WALL))
4857 int activated_magic_wall =
4858 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
4859 smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
4860 EL_DC_MAGIC_WALL_ACTIVE);
4862 /* activate magic wall / mill */
4863 SCAN_PLAYFIELD(xx, yy)
4865 if (Feld[xx][yy] == smashed)
4866 Feld[xx][yy] = activated_magic_wall;
4869 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
4870 game.magic_wall_active = TRUE;
4872 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
4873 SND_MAGIC_WALL_ACTIVATING :
4874 smashed == EL_BD_MAGIC_WALL ?
4875 SND_BD_MAGIC_WALL_ACTIVATING :
4876 SND_DC_MAGIC_WALL_ACTIVATING));
4879 if (IS_PLAYER(x, y + 1))
4881 if (CAN_SMASH_PLAYER(element))
4883 KillPlayerUnlessEnemyProtected(x, y + 1);
4887 else if (smashed == EL_PENGUIN)
4889 if (CAN_SMASH_PLAYER(element))
4895 else if (element == EL_BD_DIAMOND)
4897 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
4903 else if (((element == EL_SP_INFOTRON ||
4904 element == EL_SP_ZONK) &&
4905 (smashed == EL_SP_SNIKSNAK ||
4906 smashed == EL_SP_ELECTRON ||
4907 smashed == EL_SP_DISK_ORANGE)) ||
4908 (element == EL_SP_INFOTRON &&
4909 smashed == EL_SP_DISK_YELLOW))
4914 else if (CAN_SMASH_EVERYTHING(element))
4916 if (IS_CLASSIC_ENEMY(smashed) ||
4917 CAN_EXPLODE_SMASHED(smashed))
4922 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
4924 if (smashed == EL_LAMP ||
4925 smashed == EL_LAMP_ACTIVE)
4930 else if (smashed == EL_NUT)
4932 Feld[x][y + 1] = EL_NUT_BREAKING;
4933 PlayLevelSound(x, y, SND_NUT_BREAKING);
4934 RaiseScoreElement(EL_NUT);
4937 else if (smashed == EL_PEARL)
4939 ResetGfxAnimation(x, y);
4941 Feld[x][y + 1] = EL_PEARL_BREAKING;
4942 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4945 else if (smashed == EL_DIAMOND)
4947 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
4948 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
4951 else if (IS_BELT_SWITCH(smashed))
4953 ToggleBeltSwitch(x, y + 1);
4955 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
4956 smashed == EL_SWITCHGATE_SWITCH_DOWN ||
4957 smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
4958 smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
4960 ToggleSwitchgateSwitch(x, y + 1);
4962 else if (smashed == EL_LIGHT_SWITCH ||
4963 smashed == EL_LIGHT_SWITCH_ACTIVE)
4965 ToggleLightSwitch(x, y + 1);
4970 TestIfElementSmashesCustomElement(x, y, MV_DOWN);
4973 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4975 CheckElementChangeBySide(x, y + 1, smashed, element,
4976 CE_SWITCHED, CH_SIDE_TOP);
4977 CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
4983 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4988 /* play sound of magic wall / mill */
4990 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
4991 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
4992 Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
4994 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
4995 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
4996 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
4997 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
4998 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
4999 PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
5004 /* play sound of object that hits the ground */
5005 if (last_line || object_hit)
5006 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
5009 inline static void TurnRoundExt(int x, int y)
5021 { 0, 0 }, { 0, 0 }, { 0, 0 },
5026 int left, right, back;
5030 { MV_DOWN, MV_UP, MV_RIGHT },
5031 { MV_UP, MV_DOWN, MV_LEFT },
5033 { MV_LEFT, MV_RIGHT, MV_DOWN },
5037 { MV_RIGHT, MV_LEFT, MV_UP }
5040 int element = Feld[x][y];
5041 int move_pattern = element_info[element].move_pattern;
5043 int old_move_dir = MovDir[x][y];
5044 int left_dir = turn[old_move_dir].left;
5045 int right_dir = turn[old_move_dir].right;
5046 int back_dir = turn[old_move_dir].back;
5048 int left_dx = move_xy[left_dir].dx, left_dy = move_xy[left_dir].dy;
5049 int right_dx = move_xy[right_dir].dx, right_dy = move_xy[right_dir].dy;
5050 int move_dx = move_xy[old_move_dir].dx, move_dy = move_xy[old_move_dir].dy;
5051 int back_dx = move_xy[back_dir].dx, back_dy = move_xy[back_dir].dy;
5053 int left_x = x + left_dx, left_y = y + left_dy;
5054 int right_x = x + right_dx, right_y = y + right_dy;
5055 int move_x = x + move_dx, move_y = y + move_dy;
5059 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
5061 TestIfBadThingTouchesOtherBadThing(x, y);
5063 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
5064 MovDir[x][y] = right_dir;
5065 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
5066 MovDir[x][y] = left_dir;
5068 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
5070 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
5073 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
5075 TestIfBadThingTouchesOtherBadThing(x, y);
5077 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
5078 MovDir[x][y] = left_dir;
5079 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
5080 MovDir[x][y] = right_dir;
5082 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
5084 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
5087 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
5089 TestIfBadThingTouchesOtherBadThing(x, y);
5091 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
5092 MovDir[x][y] = left_dir;
5093 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
5094 MovDir[x][y] = right_dir;
5096 if (MovDir[x][y] != old_move_dir)
5099 else if (element == EL_YAMYAM)
5101 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
5102 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
5104 if (can_turn_left && can_turn_right)
5105 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5106 else if (can_turn_left)
5107 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5108 else if (can_turn_right)
5109 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5111 MovDir[x][y] = back_dir;
5113 MovDelay[x][y] = 16 + 16 * RND(3);
5115 else if (element == EL_DARK_YAMYAM)
5117 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
5119 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
5122 if (can_turn_left && can_turn_right)
5123 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5124 else if (can_turn_left)
5125 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5126 else if (can_turn_right)
5127 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5129 MovDir[x][y] = back_dir;
5131 MovDelay[x][y] = 16 + 16 * RND(3);
5133 else if (element == EL_PACMAN)
5135 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
5136 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
5138 if (can_turn_left && can_turn_right)
5139 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5140 else if (can_turn_left)
5141 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5142 else if (can_turn_right)
5143 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5145 MovDir[x][y] = back_dir;
5147 MovDelay[x][y] = 6 + RND(40);
5149 else if (element == EL_PIG)
5151 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
5152 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
5153 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
5154 boolean should_turn_left, should_turn_right, should_move_on;
5156 int rnd = RND(rnd_value);
5158 should_turn_left = (can_turn_left &&
5160 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
5161 y + back_dy + left_dy)));
5162 should_turn_right = (can_turn_right &&
5164 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
5165 y + back_dy + right_dy)));
5166 should_move_on = (can_move_on &&
5169 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
5170 y + move_dy + left_dy) ||
5171 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
5172 y + move_dy + right_dy)));
5174 if (should_turn_left || should_turn_right || should_move_on)
5176 if (should_turn_left && should_turn_right && should_move_on)
5177 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
5178 rnd < 2 * rnd_value / 3 ? right_dir :
5180 else if (should_turn_left && should_turn_right)
5181 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
5182 else if (should_turn_left && should_move_on)
5183 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
5184 else if (should_turn_right && should_move_on)
5185 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
5186 else if (should_turn_left)
5187 MovDir[x][y] = left_dir;
5188 else if (should_turn_right)
5189 MovDir[x][y] = right_dir;
5190 else if (should_move_on)
5191 MovDir[x][y] = old_move_dir;
5193 else if (can_move_on && rnd > rnd_value / 8)
5194 MovDir[x][y] = old_move_dir;
5195 else if (can_turn_left && can_turn_right)
5196 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
5197 else if (can_turn_left && rnd > rnd_value / 8)
5198 MovDir[x][y] = left_dir;
5199 else if (can_turn_right && rnd > rnd_value/8)
5200 MovDir[x][y] = right_dir;
5202 MovDir[x][y] = back_dir;
5204 xx = x + move_xy[MovDir[x][y]].dx;
5205 yy = y + move_xy[MovDir[x][y]].dy;
5207 if (!IN_LEV_FIELD(xx, yy) ||
5208 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
5209 MovDir[x][y] = old_move_dir;
5213 else if (element == EL_DRAGON)
5215 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
5216 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
5217 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
5219 int rnd = RND(rnd_value);
5221 if (can_move_on && rnd > rnd_value / 8)
5222 MovDir[x][y] = old_move_dir;
5223 else if (can_turn_left && can_turn_right)
5224 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
5225 else if (can_turn_left && rnd > rnd_value / 8)
5226 MovDir[x][y] = left_dir;
5227 else if (can_turn_right && rnd > rnd_value / 8)
5228 MovDir[x][y] = right_dir;
5230 MovDir[x][y] = back_dir;
5232 xx = x + move_xy[MovDir[x][y]].dx;
5233 yy = y + move_xy[MovDir[x][y]].dy;
5235 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
5236 MovDir[x][y] = old_move_dir;
5240 else if (element == EL_MOLE)
5242 boolean can_move_on =
5243 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
5244 IS_AMOEBOID(Feld[move_x][move_y]) ||
5245 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
5248 boolean can_turn_left =
5249 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
5250 IS_AMOEBOID(Feld[left_x][left_y])));
5252 boolean can_turn_right =
5253 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
5254 IS_AMOEBOID(Feld[right_x][right_y])));
5256 if (can_turn_left && can_turn_right)
5257 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
5258 else if (can_turn_left)
5259 MovDir[x][y] = left_dir;
5261 MovDir[x][y] = right_dir;
5264 if (MovDir[x][y] != old_move_dir)
5267 else if (element == EL_BALLOON)
5269 MovDir[x][y] = game.wind_direction;
5272 else if (element == EL_SPRING)
5274 #if USE_NEW_SPRING_BUMPER
5275 if (MovDir[x][y] & MV_HORIZONTAL)
5277 if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
5278 !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
5280 Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
5281 ResetGfxAnimation(move_x, move_y);
5282 DrawLevelField(move_x, move_y);
5284 MovDir[x][y] = back_dir;
5286 else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
5287 SPRING_CAN_ENTER_FIELD(element, x, y + 1))
5288 MovDir[x][y] = MV_NONE;
5291 if (MovDir[x][y] & MV_HORIZONTAL &&
5292 (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
5293 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
5294 MovDir[x][y] = MV_NONE;
5299 else if (element == EL_ROBOT ||
5300 element == EL_SATELLITE ||
5301 element == EL_PENGUIN ||
5302 element == EL_EMC_ANDROID)
5304 int attr_x = -1, attr_y = -1;
5315 for (i = 0; i < MAX_PLAYERS; i++)
5317 struct PlayerInfo *player = &stored_player[i];
5318 int jx = player->jx, jy = player->jy;
5320 if (!player->active)
5324 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
5332 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
5333 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
5334 game.engine_version < VERSION_IDENT(3,1,0,0)))
5340 if (element == EL_PENGUIN)
5343 static int xy[4][2] =
5351 for (i = 0; i < NUM_DIRECTIONS; i++)
5353 int ex = x + xy[i][0];
5354 int ey = y + xy[i][1];
5356 if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
5357 Feld[ex][ey] == EL_EM_EXIT_OPEN ||
5358 Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
5359 Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
5368 MovDir[x][y] = MV_NONE;
5370 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
5371 else if (attr_x > x)
5372 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
5374 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
5375 else if (attr_y > y)
5376 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
5378 if (element == EL_ROBOT)
5382 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5383 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
5384 Moving2Blocked(x, y, &newx, &newy);
5386 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
5387 MovDelay[x][y] = 8 + 8 * !RND(3);
5389 MovDelay[x][y] = 16;
5391 else if (element == EL_PENGUIN)
5397 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5399 boolean first_horiz = RND(2);
5400 int new_move_dir = MovDir[x][y];
5403 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5404 Moving2Blocked(x, y, &newx, &newy);
5406 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
5410 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5411 Moving2Blocked(x, y, &newx, &newy);
5413 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
5416 MovDir[x][y] = old_move_dir;
5420 else if (element == EL_SATELLITE)
5426 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5428 boolean first_horiz = RND(2);
5429 int new_move_dir = MovDir[x][y];
5432 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5433 Moving2Blocked(x, y, &newx, &newy);
5435 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
5439 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5440 Moving2Blocked(x, y, &newx, &newy);
5442 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
5445 MovDir[x][y] = old_move_dir;
5449 else if (element == EL_EMC_ANDROID)
5451 static int check_pos[16] =
5453 -1, /* 0 => (invalid) */
5454 7, /* 1 => MV_LEFT */
5455 3, /* 2 => MV_RIGHT */
5456 -1, /* 3 => (invalid) */
5458 0, /* 5 => MV_LEFT | MV_UP */
5459 2, /* 6 => MV_RIGHT | MV_UP */
5460 -1, /* 7 => (invalid) */
5461 5, /* 8 => MV_DOWN */
5462 6, /* 9 => MV_LEFT | MV_DOWN */
5463 4, /* 10 => MV_RIGHT | MV_DOWN */
5464 -1, /* 11 => (invalid) */
5465 -1, /* 12 => (invalid) */
5466 -1, /* 13 => (invalid) */
5467 -1, /* 14 => (invalid) */
5468 -1, /* 15 => (invalid) */
5476 { -1, -1, MV_LEFT | MV_UP },
5478 { +1, -1, MV_RIGHT | MV_UP },
5479 { +1, 0, MV_RIGHT },
5480 { +1, +1, MV_RIGHT | MV_DOWN },
5482 { -1, +1, MV_LEFT | MV_DOWN },
5485 int start_pos, check_order;
5486 boolean can_clone = FALSE;
5489 /* check if there is any free field around current position */
5490 for (i = 0; i < 8; i++)
5492 int newx = x + check_xy[i].dx;
5493 int newy = y + check_xy[i].dy;
5495 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5503 if (can_clone) /* randomly find an element to clone */
5507 start_pos = check_pos[RND(8)];
5508 check_order = (RND(2) ? -1 : +1);
5510 for (i = 0; i < 8; i++)
5512 int pos_raw = start_pos + i * check_order;
5513 int pos = (pos_raw + 8) % 8;
5514 int newx = x + check_xy[pos].dx;
5515 int newy = y + check_xy[pos].dy;
5517 if (ANDROID_CAN_CLONE_FIELD(newx, newy))
5519 element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
5520 element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
5522 Store[x][y] = Feld[newx][newy];
5531 if (can_clone) /* randomly find a direction to move */
5535 start_pos = check_pos[RND(8)];
5536 check_order = (RND(2) ? -1 : +1);
5538 for (i = 0; i < 8; i++)
5540 int pos_raw = start_pos + i * check_order;
5541 int pos = (pos_raw + 8) % 8;
5542 int newx = x + check_xy[pos].dx;
5543 int newy = y + check_xy[pos].dy;
5544 int new_move_dir = check_xy[pos].dir;
5546 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5548 MovDir[x][y] = new_move_dir;
5549 MovDelay[x][y] = level.android_clone_time * 8 + 1;
5558 if (can_clone) /* cloning and moving successful */
5561 /* cannot clone -- try to move towards player */
5563 start_pos = check_pos[MovDir[x][y] & 0x0f];
5564 check_order = (RND(2) ? -1 : +1);
5566 for (i = 0; i < 3; i++)
5568 /* first check start_pos, then previous/next or (next/previous) pos */
5569 int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
5570 int pos = (pos_raw + 8) % 8;
5571 int newx = x + check_xy[pos].dx;
5572 int newy = y + check_xy[pos].dy;
5573 int new_move_dir = check_xy[pos].dir;
5575 if (IS_PLAYER(newx, newy))
5578 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
5580 MovDir[x][y] = new_move_dir;
5581 MovDelay[x][y] = level.android_move_time * 8 + 1;
5588 else if (move_pattern == MV_TURNING_LEFT ||
5589 move_pattern == MV_TURNING_RIGHT ||
5590 move_pattern == MV_TURNING_LEFT_RIGHT ||
5591 move_pattern == MV_TURNING_RIGHT_LEFT ||
5592 move_pattern == MV_TURNING_RANDOM ||
5593 move_pattern == MV_ALL_DIRECTIONS)
5595 boolean can_turn_left =
5596 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
5597 boolean can_turn_right =
5598 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
5600 if (element_info[element].move_stepsize == 0) /* "not moving" */
5603 if (move_pattern == MV_TURNING_LEFT)
5604 MovDir[x][y] = left_dir;
5605 else if (move_pattern == MV_TURNING_RIGHT)
5606 MovDir[x][y] = right_dir;
5607 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
5608 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
5609 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
5610 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
5611 else if (move_pattern == MV_TURNING_RANDOM)
5612 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
5613 can_turn_right && !can_turn_left ? right_dir :
5614 RND(2) ? left_dir : right_dir);
5615 else if (can_turn_left && can_turn_right)
5616 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5617 else if (can_turn_left)
5618 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5619 else if (can_turn_right)
5620 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5622 MovDir[x][y] = back_dir;
5624 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5626 else if (move_pattern == MV_HORIZONTAL ||
5627 move_pattern == MV_VERTICAL)
5629 if (move_pattern & old_move_dir)
5630 MovDir[x][y] = back_dir;
5631 else if (move_pattern == MV_HORIZONTAL)
5632 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
5633 else if (move_pattern == MV_VERTICAL)
5634 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
5636 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5638 else if (move_pattern & MV_ANY_DIRECTION)
5640 MovDir[x][y] = move_pattern;
5641 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5643 else if (move_pattern & MV_WIND_DIRECTION)
5645 MovDir[x][y] = game.wind_direction;
5646 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5648 else if (move_pattern == MV_ALONG_LEFT_SIDE)
5650 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
5651 MovDir[x][y] = left_dir;
5652 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5653 MovDir[x][y] = right_dir;
5655 if (MovDir[x][y] != old_move_dir)
5656 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5658 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
5660 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
5661 MovDir[x][y] = right_dir;
5662 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5663 MovDir[x][y] = left_dir;
5665 if (MovDir[x][y] != old_move_dir)
5666 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5668 else if (move_pattern == MV_TOWARDS_PLAYER ||
5669 move_pattern == MV_AWAY_FROM_PLAYER)
5671 int attr_x = -1, attr_y = -1;
5673 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
5684 for (i = 0; i < MAX_PLAYERS; i++)
5686 struct PlayerInfo *player = &stored_player[i];
5687 int jx = player->jx, jy = player->jy;
5689 if (!player->active)
5693 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
5701 MovDir[x][y] = MV_NONE;
5703 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
5704 else if (attr_x > x)
5705 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
5707 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
5708 else if (attr_y > y)
5709 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
5711 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5713 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5715 boolean first_horiz = RND(2);
5716 int new_move_dir = MovDir[x][y];
5718 if (element_info[element].move_stepsize == 0) /* "not moving" */
5720 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
5721 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5727 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5728 Moving2Blocked(x, y, &newx, &newy);
5730 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5734 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5735 Moving2Blocked(x, y, &newx, &newy);
5737 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5740 MovDir[x][y] = old_move_dir;
5743 else if (move_pattern == MV_WHEN_PUSHED ||
5744 move_pattern == MV_WHEN_DROPPED)
5746 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5747 MovDir[x][y] = MV_NONE;
5751 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
5753 static int test_xy[7][2] =
5763 static int test_dir[7] =
5773 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
5774 int move_preference = -1000000; /* start with very low preference */
5775 int new_move_dir = MV_NONE;
5776 int start_test = RND(4);
5779 for (i = 0; i < NUM_DIRECTIONS; i++)
5781 int move_dir = test_dir[start_test + i];
5782 int move_dir_preference;
5784 xx = x + test_xy[start_test + i][0];
5785 yy = y + test_xy[start_test + i][1];
5787 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
5788 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
5790 new_move_dir = move_dir;
5795 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
5798 move_dir_preference = -1 * RunnerVisit[xx][yy];
5799 if (hunter_mode && PlayerVisit[xx][yy] > 0)
5800 move_dir_preference = PlayerVisit[xx][yy];
5802 if (move_dir_preference > move_preference)
5804 /* prefer field that has not been visited for the longest time */
5805 move_preference = move_dir_preference;
5806 new_move_dir = move_dir;
5808 else if (move_dir_preference == move_preference &&
5809 move_dir == old_move_dir)
5811 /* prefer last direction when all directions are preferred equally */
5812 move_preference = move_dir_preference;
5813 new_move_dir = move_dir;
5817 MovDir[x][y] = new_move_dir;
5818 if (old_move_dir != new_move_dir)
5819 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5823 static void TurnRound(int x, int y)
5825 int direction = MovDir[x][y];
5829 GfxDir[x][y] = MovDir[x][y];
5831 if (direction != MovDir[x][y])
5835 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
5837 ResetGfxFrame(x, y, FALSE);
5840 static boolean JustBeingPushed(int x, int y)
5844 for (i = 0; i < MAX_PLAYERS; i++)
5846 struct PlayerInfo *player = &stored_player[i];
5848 if (player->active && player->is_pushing && player->MovPos)
5850 int next_jx = player->jx + (player->jx - player->last_jx);
5851 int next_jy = player->jy + (player->jy - player->last_jy);
5853 if (x == next_jx && y == next_jy)
5861 void StartMoving(int x, int y)
5863 boolean started_moving = FALSE; /* some elements can fall _and_ move */
5864 int element = Feld[x][y];
5869 if (MovDelay[x][y] == 0)
5870 GfxAction[x][y] = ACTION_DEFAULT;
5872 if (CAN_FALL(element) && y < lev_fieldy - 1)
5874 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
5875 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
5876 if (JustBeingPushed(x, y))
5879 if (element == EL_QUICKSAND_FULL)
5881 if (IS_FREE(x, y + 1))
5883 InitMovingField(x, y, MV_DOWN);
5884 started_moving = TRUE;
5886 Feld[x][y] = EL_QUICKSAND_EMPTYING;
5887 #if USE_QUICKSAND_BD_ROCK_BUGFIX
5888 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
5889 Store[x][y] = EL_ROCK;
5891 Store[x][y] = EL_ROCK;
5894 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
5896 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5898 if (!MovDelay[x][y])
5899 MovDelay[x][y] = TILEY + 1;
5908 Feld[x][y] = EL_QUICKSAND_EMPTY;
5909 Feld[x][y + 1] = EL_QUICKSAND_FULL;
5910 Store[x][y + 1] = Store[x][y];
5913 PlayLevelSoundAction(x, y, ACTION_FILLING);
5916 else if (element == EL_QUICKSAND_FAST_FULL)
5918 if (IS_FREE(x, y + 1))
5920 InitMovingField(x, y, MV_DOWN);
5921 started_moving = TRUE;
5923 Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
5924 #if USE_QUICKSAND_BD_ROCK_BUGFIX
5925 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
5926 Store[x][y] = EL_ROCK;
5928 Store[x][y] = EL_ROCK;
5931 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
5933 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
5935 if (!MovDelay[x][y])
5936 MovDelay[x][y] = TILEY + 1;
5945 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
5946 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
5947 Store[x][y + 1] = Store[x][y];
5950 PlayLevelSoundAction(x, y, ACTION_FILLING);
5953 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
5954 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5956 InitMovingField(x, y, MV_DOWN);
5957 started_moving = TRUE;
5959 Feld[x][y] = EL_QUICKSAND_FILLING;
5960 Store[x][y] = element;
5962 PlayLevelSoundAction(x, y, ACTION_FILLING);
5964 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
5965 Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
5967 InitMovingField(x, y, MV_DOWN);
5968 started_moving = TRUE;
5970 Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
5971 Store[x][y] = element;
5973 PlayLevelSoundAction(x, y, ACTION_FILLING);
5975 else if (element == EL_MAGIC_WALL_FULL)
5977 if (IS_FREE(x, y + 1))
5979 InitMovingField(x, y, MV_DOWN);
5980 started_moving = TRUE;
5982 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
5983 Store[x][y] = EL_CHANGED(Store[x][y]);
5985 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
5987 if (!MovDelay[x][y])
5988 MovDelay[x][y] = TILEY/4 + 1;
5997 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
5998 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
5999 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
6003 else if (element == EL_BD_MAGIC_WALL_FULL)
6005 if (IS_FREE(x, y + 1))
6007 InitMovingField(x, y, MV_DOWN);
6008 started_moving = TRUE;
6010 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
6011 Store[x][y] = EL_CHANGED_BD(Store[x][y]);
6013 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6015 if (!MovDelay[x][y])
6016 MovDelay[x][y] = TILEY/4 + 1;
6025 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
6026 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
6027 Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
6031 else if (element == EL_DC_MAGIC_WALL_FULL)
6033 if (IS_FREE(x, y + 1))
6035 InitMovingField(x, y, MV_DOWN);
6036 started_moving = TRUE;
6038 Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
6039 Store[x][y] = EL_CHANGED_DC(Store[x][y]);
6041 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6043 if (!MovDelay[x][y])
6044 MovDelay[x][y] = TILEY/4 + 1;
6053 Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
6054 Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
6055 Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
6059 else if ((CAN_PASS_MAGIC_WALL(element) &&
6060 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6061 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
6062 (CAN_PASS_DC_MAGIC_WALL(element) &&
6063 (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
6066 InitMovingField(x, y, MV_DOWN);
6067 started_moving = TRUE;
6070 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
6071 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
6072 EL_DC_MAGIC_WALL_FILLING);
6073 Store[x][y] = element;
6075 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
6077 SplashAcid(x, y + 1);
6079 InitMovingField(x, y, MV_DOWN);
6080 started_moving = TRUE;
6082 Store[x][y] = EL_ACID;
6085 #if USE_FIX_IMPACT_COLLISION
6086 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
6087 CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
6089 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
6090 CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
6092 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
6093 CAN_FALL(element) && WasJustFalling[x][y] &&
6094 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
6096 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
6097 CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
6098 (Feld[x][y + 1] == EL_BLOCKED)))
6100 /* this is needed for a special case not covered by calling "Impact()"
6101 from "ContinueMoving()": if an element moves to a tile directly below
6102 another element which was just falling on that tile (which was empty
6103 in the previous frame), the falling element above would just stop
6104 instead of smashing the element below (in previous version, the above
6105 element was just checked for "moving" instead of "falling", resulting
6106 in incorrect smashes caused by horizontal movement of the above
6107 element; also, the case of the player being the element to smash was
6108 simply not covered here... :-/ ) */
6110 CheckCollision[x][y] = 0;
6111 CheckImpact[x][y] = 0;
6115 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
6117 if (MovDir[x][y] == MV_NONE)
6119 InitMovingField(x, y, MV_DOWN);
6120 started_moving = TRUE;
6123 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
6125 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
6126 MovDir[x][y] = MV_DOWN;
6128 InitMovingField(x, y, MV_DOWN);
6129 started_moving = TRUE;
6131 else if (element == EL_AMOEBA_DROP)
6133 Feld[x][y] = EL_AMOEBA_GROWING;
6134 Store[x][y] = EL_AMOEBA_WET;
6136 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
6137 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
6138 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
6139 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
6141 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
6142 (IS_FREE(x - 1, y + 1) ||
6143 Feld[x - 1][y + 1] == EL_ACID));
6144 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
6145 (IS_FREE(x + 1, y + 1) ||
6146 Feld[x + 1][y + 1] == EL_ACID));
6147 boolean can_fall_any = (can_fall_left || can_fall_right);
6148 boolean can_fall_both = (can_fall_left && can_fall_right);
6149 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
6151 #if USE_NEW_ALL_SLIPPERY
6152 if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
6154 if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
6155 can_fall_right = FALSE;
6156 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
6157 can_fall_left = FALSE;
6158 else if (slippery_type == SLIPPERY_ONLY_LEFT)
6159 can_fall_right = FALSE;
6160 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
6161 can_fall_left = FALSE;
6163 can_fall_any = (can_fall_left || can_fall_right);
6164 can_fall_both = FALSE;
6167 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
6169 if (slippery_type == SLIPPERY_ONLY_LEFT)
6170 can_fall_right = FALSE;
6171 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
6172 can_fall_left = FALSE;
6173 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
6174 can_fall_right = FALSE;
6175 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
6176 can_fall_left = FALSE;
6178 can_fall_any = (can_fall_left || can_fall_right);
6179 can_fall_both = (can_fall_left && can_fall_right);
6183 #if USE_NEW_ALL_SLIPPERY
6185 #if USE_NEW_SP_SLIPPERY
6186 /* !!! better use the same properties as for custom elements here !!! */
6187 else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
6188 can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
6190 can_fall_right = FALSE; /* slip down on left side */
6191 can_fall_both = FALSE;
6196 #if USE_NEW_ALL_SLIPPERY
6199 if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
6200 can_fall_right = FALSE; /* slip down on left side */
6202 can_fall_left = !(can_fall_right = RND(2));
6204 can_fall_both = FALSE;
6209 if (game.emulation == EMU_BOULDERDASH ||
6210 element == EL_BD_ROCK || element == EL_BD_DIAMOND)
6211 can_fall_right = FALSE; /* slip down on left side */
6213 can_fall_left = !(can_fall_right = RND(2));
6215 can_fall_both = FALSE;
6221 /* if not determined otherwise, prefer left side for slipping down */
6222 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
6223 started_moving = TRUE;
6227 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
6229 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
6232 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
6233 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
6234 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
6235 int belt_dir = game.belt_dir[belt_nr];
6237 if ((belt_dir == MV_LEFT && left_is_free) ||
6238 (belt_dir == MV_RIGHT && right_is_free))
6240 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
6242 InitMovingField(x, y, belt_dir);
6243 started_moving = TRUE;
6245 Pushed[x][y] = TRUE;
6246 Pushed[nextx][y] = TRUE;
6248 GfxAction[x][y] = ACTION_DEFAULT;
6252 MovDir[x][y] = 0; /* if element was moving, stop it */
6257 /* not "else if" because of elements that can fall and move (EL_SPRING) */
6259 if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
6261 if (CAN_MOVE(element) && !started_moving)
6264 int move_pattern = element_info[element].move_pattern;
6269 if (MovDir[x][y] == MV_NONE)
6271 printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
6272 x, y, element, element_info[element].token_name);
6273 printf("StartMoving(): This should never happen!\n");
6278 Moving2Blocked(x, y, &newx, &newy);
6280 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
6283 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
6284 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6286 WasJustMoving[x][y] = 0;
6287 CheckCollision[x][y] = 0;
6289 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
6291 if (Feld[x][y] != element) /* element has changed */
6295 if (!MovDelay[x][y]) /* start new movement phase */
6297 /* all objects that can change their move direction after each step
6298 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
6300 if (element != EL_YAMYAM &&
6301 element != EL_DARK_YAMYAM &&
6302 element != EL_PACMAN &&
6303 !(move_pattern & MV_ANY_DIRECTION) &&
6304 move_pattern != MV_TURNING_LEFT &&
6305 move_pattern != MV_TURNING_RIGHT &&
6306 move_pattern != MV_TURNING_LEFT_RIGHT &&
6307 move_pattern != MV_TURNING_RIGHT_LEFT &&
6308 move_pattern != MV_TURNING_RANDOM)
6312 if (MovDelay[x][y] && (element == EL_BUG ||
6313 element == EL_SPACESHIP ||
6314 element == EL_SP_SNIKSNAK ||
6315 element == EL_SP_ELECTRON ||
6316 element == EL_MOLE))
6317 DrawLevelField(x, y);
6321 if (MovDelay[x][y]) /* wait some time before next movement */
6325 if (element == EL_ROBOT ||
6326 element == EL_YAMYAM ||
6327 element == EL_DARK_YAMYAM)
6329 DrawLevelElementAnimationIfNeeded(x, y, element);
6330 PlayLevelSoundAction(x, y, ACTION_WAITING);
6332 else if (element == EL_SP_ELECTRON)
6333 DrawLevelElementAnimationIfNeeded(x, y, element);
6334 else if (element == EL_DRAGON)
6337 int dir = MovDir[x][y];
6338 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
6339 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
6340 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
6341 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
6342 dir == MV_UP ? IMG_FLAMES_1_UP :
6343 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
6344 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
6346 GfxAction[x][y] = ACTION_ATTACKING;
6348 if (IS_PLAYER(x, y))
6349 DrawPlayerField(x, y);
6351 DrawLevelField(x, y);
6353 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
6355 for (i = 1; i <= 3; i++)
6357 int xx = x + i * dx;
6358 int yy = y + i * dy;
6359 int sx = SCREENX(xx);
6360 int sy = SCREENY(yy);
6361 int flame_graphic = graphic + (i - 1);
6363 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
6368 int flamed = MovingOrBlocked2Element(xx, yy);
6372 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
6374 else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
6375 RemoveMovingField(xx, yy);
6377 RemoveField(xx, yy);
6379 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
6382 RemoveMovingField(xx, yy);
6385 ChangeDelay[xx][yy] = 0;
6387 Feld[xx][yy] = EL_FLAMES;
6389 if (IN_SCR_FIELD(sx, sy))
6391 DrawLevelFieldCrumbledSand(xx, yy);
6392 DrawGraphic(sx, sy, flame_graphic, frame);
6397 if (Feld[xx][yy] == EL_FLAMES)
6398 Feld[xx][yy] = EL_EMPTY;
6399 DrawLevelField(xx, yy);
6404 if (MovDelay[x][y]) /* element still has to wait some time */
6406 PlayLevelSoundAction(x, y, ACTION_WAITING);
6412 /* now make next step */
6414 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
6416 if (DONT_COLLIDE_WITH(element) &&
6417 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
6418 !PLAYER_ENEMY_PROTECTED(newx, newy))
6420 TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
6425 else if (CAN_MOVE_INTO_ACID(element) &&
6426 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
6427 !IS_MV_DIAGONAL(MovDir[x][y]) &&
6428 (MovDir[x][y] == MV_DOWN ||
6429 game.engine_version >= VERSION_IDENT(3,1,0,0)))
6431 SplashAcid(newx, newy);
6432 Store[x][y] = EL_ACID;
6434 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
6436 if (Feld[newx][newy] == EL_EXIT_OPEN ||
6437 Feld[newx][newy] == EL_EM_EXIT_OPEN ||
6438 Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
6439 Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
6442 DrawLevelField(x, y);
6444 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
6445 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
6446 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
6448 local_player->friends_still_needed--;
6449 if (!local_player->friends_still_needed &&
6450 !local_player->GameOver && AllPlayersGone)
6451 PlayerWins(local_player);
6455 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
6457 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
6458 DrawLevelField(newx, newy);
6460 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
6462 else if (!IS_FREE(newx, newy))
6464 GfxAction[x][y] = ACTION_WAITING;
6466 if (IS_PLAYER(x, y))
6467 DrawPlayerField(x, y);
6469 DrawLevelField(x, y);
6474 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
6476 if (IS_FOOD_PIG(Feld[newx][newy]))
6478 if (IS_MOVING(newx, newy))
6479 RemoveMovingField(newx, newy);
6482 Feld[newx][newy] = EL_EMPTY;
6483 DrawLevelField(newx, newy);
6486 PlayLevelSound(x, y, SND_PIG_DIGGING);
6488 else if (!IS_FREE(newx, newy))
6490 if (IS_PLAYER(x, y))
6491 DrawPlayerField(x, y);
6493 DrawLevelField(x, y);
6498 else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
6500 if (Store[x][y] != EL_EMPTY)
6502 boolean can_clone = FALSE;
6505 /* check if element to clone is still there */
6506 for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
6508 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
6516 /* cannot clone or target field not free anymore -- do not clone */
6517 if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6518 Store[x][y] = EL_EMPTY;
6521 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6523 if (IS_MV_DIAGONAL(MovDir[x][y]))
6525 int diagonal_move_dir = MovDir[x][y];
6526 int stored = Store[x][y];
6527 int change_delay = 8;
6530 /* android is moving diagonally */
6532 CreateField(x, y, EL_DIAGONAL_SHRINKING);
6534 Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
6535 GfxElement[x][y] = EL_EMC_ANDROID;
6536 GfxAction[x][y] = ACTION_SHRINKING;
6537 GfxDir[x][y] = diagonal_move_dir;
6538 ChangeDelay[x][y] = change_delay;
6540 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
6543 DrawLevelGraphicAnimation(x, y, graphic);
6544 PlayLevelSoundAction(x, y, ACTION_SHRINKING);
6546 if (Feld[newx][newy] == EL_ACID)
6548 SplashAcid(newx, newy);
6553 CreateField(newx, newy, EL_DIAGONAL_GROWING);
6555 Store[newx][newy] = EL_EMC_ANDROID;
6556 GfxElement[newx][newy] = EL_EMC_ANDROID;
6557 GfxAction[newx][newy] = ACTION_GROWING;
6558 GfxDir[newx][newy] = diagonal_move_dir;
6559 ChangeDelay[newx][newy] = change_delay;
6561 graphic = el_act_dir2img(GfxElement[newx][newy],
6562 GfxAction[newx][newy], GfxDir[newx][newy]);
6564 DrawLevelGraphicAnimation(newx, newy, graphic);
6565 PlayLevelSoundAction(newx, newy, ACTION_GROWING);
6571 Feld[newx][newy] = EL_EMPTY;
6572 DrawLevelField(newx, newy);
6574 PlayLevelSoundAction(x, y, ACTION_DIGGING);
6577 else if (!IS_FREE(newx, newy))
6580 if (IS_PLAYER(x, y))
6581 DrawPlayerField(x, y);
6583 DrawLevelField(x, y);
6589 else if (IS_CUSTOM_ELEMENT(element) &&
6590 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6592 int new_element = Feld[newx][newy];
6594 if (!IS_FREE(newx, newy))
6596 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
6597 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
6600 /* no element can dig solid indestructible elements */
6601 if (IS_INDESTRUCTIBLE(new_element) &&
6602 !IS_DIGGABLE(new_element) &&
6603 !IS_COLLECTIBLE(new_element))
6606 if (AmoebaNr[newx][newy] &&
6607 (new_element == EL_AMOEBA_FULL ||
6608 new_element == EL_BD_AMOEBA ||
6609 new_element == EL_AMOEBA_GROWING))
6611 AmoebaCnt[AmoebaNr[newx][newy]]--;
6612 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6615 if (IS_MOVING(newx, newy))
6616 RemoveMovingField(newx, newy);
6619 RemoveField(newx, newy);
6620 DrawLevelField(newx, newy);
6623 /* if digged element was about to explode, prevent the explosion */
6624 ExplodeField[newx][newy] = EX_TYPE_NONE;
6626 PlayLevelSoundAction(x, y, action);
6629 Store[newx][newy] = EL_EMPTY;
6631 /* this makes it possible to leave the removed element again */
6632 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
6633 Store[newx][newy] = new_element;
6635 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
6637 int move_leave_element = element_info[element].move_leave_element;
6639 /* this makes it possible to leave the removed element again */
6640 Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
6641 new_element : move_leave_element);
6645 if (move_pattern & MV_MAZE_RUNNER_STYLE)
6647 RunnerVisit[x][y] = FrameCounter;
6648 PlayerVisit[x][y] /= 8; /* expire player visit path */
6651 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
6653 if (!IS_FREE(newx, newy))
6655 if (IS_PLAYER(x, y))
6656 DrawPlayerField(x, y);
6658 DrawLevelField(x, y);
6664 boolean wanna_flame = !RND(10);
6665 int dx = newx - x, dy = newy - y;
6666 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
6667 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
6668 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
6669 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
6670 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
6671 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
6674 IS_CLASSIC_ENEMY(element1) ||
6675 IS_CLASSIC_ENEMY(element2)) &&
6676 element1 != EL_DRAGON && element2 != EL_DRAGON &&
6677 element1 != EL_FLAMES && element2 != EL_FLAMES)
6679 ResetGfxAnimation(x, y);
6680 GfxAction[x][y] = ACTION_ATTACKING;
6682 if (IS_PLAYER(x, y))
6683 DrawPlayerField(x, y);
6685 DrawLevelField(x, y);
6687 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
6689 MovDelay[x][y] = 50;
6693 RemoveField(newx, newy);
6695 Feld[newx][newy] = EL_FLAMES;
6696 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
6699 RemoveField(newx1, newy1);
6701 Feld[newx1][newy1] = EL_FLAMES;
6703 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
6706 RemoveField(newx2, newy2);
6708 Feld[newx2][newy2] = EL_FLAMES;
6715 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6716 Feld[newx][newy] == EL_DIAMOND)
6718 if (IS_MOVING(newx, newy))
6719 RemoveMovingField(newx, newy);
6722 Feld[newx][newy] = EL_EMPTY;
6723 DrawLevelField(newx, newy);
6726 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
6728 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6729 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
6731 if (AmoebaNr[newx][newy])
6733 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6734 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
6735 Feld[newx][newy] == EL_BD_AMOEBA)
6736 AmoebaCnt[AmoebaNr[newx][newy]]--;
6741 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
6743 RemoveMovingField(newx, newy);
6746 if (IS_MOVING(newx, newy))
6748 RemoveMovingField(newx, newy);
6753 Feld[newx][newy] = EL_EMPTY;
6754 DrawLevelField(newx, newy);
6757 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
6759 else if ((element == EL_PACMAN || element == EL_MOLE)
6760 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
6762 if (AmoebaNr[newx][newy])
6764 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6765 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
6766 Feld[newx][newy] == EL_BD_AMOEBA)
6767 AmoebaCnt[AmoebaNr[newx][newy]]--;
6770 if (element == EL_MOLE)
6772 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
6773 PlayLevelSound(x, y, SND_MOLE_DIGGING);
6775 ResetGfxAnimation(x, y);
6776 GfxAction[x][y] = ACTION_DIGGING;
6777 DrawLevelField(x, y);
6779 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
6781 return; /* wait for shrinking amoeba */
6783 else /* element == EL_PACMAN */
6785 Feld[newx][newy] = EL_EMPTY;
6786 DrawLevelField(newx, newy);
6787 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
6790 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
6791 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
6792 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
6794 /* wait for shrinking amoeba to completely disappear */
6797 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
6799 /* object was running against a wall */
6804 /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
6805 if (move_pattern & MV_ANY_DIRECTION &&
6806 move_pattern == MovDir[x][y])
6808 int blocking_element =
6809 (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
6811 CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
6814 element = Feld[x][y]; /* element might have changed */
6818 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
6819 DrawLevelElementAnimation(x, y, element);
6821 if (DONT_TOUCH(element))
6822 TestIfBadThingTouchesPlayer(x, y);
6827 InitMovingField(x, y, MovDir[x][y]);
6829 PlayLevelSoundAction(x, y, ACTION_MOVING);
6833 ContinueMoving(x, y);
6836 void ContinueMoving(int x, int y)
6838 int element = Feld[x][y];
6839 struct ElementInfo *ei = &element_info[element];
6840 int direction = MovDir[x][y];
6841 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
6842 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
6843 int newx = x + dx, newy = y + dy;
6844 int stored = Store[x][y];
6845 int stored_new = Store[newx][newy];
6846 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
6847 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
6848 boolean last_line = (newy == lev_fieldy - 1);
6850 MovPos[x][y] += getElementMoveStepsize(x, y);
6852 if (pushed_by_player) /* special case: moving object pushed by player */
6853 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
6855 if (ABS(MovPos[x][y]) < TILEX)
6857 DrawLevelField(x, y);
6859 return; /* element is still moving */
6862 /* element reached destination field */
6864 Feld[x][y] = EL_EMPTY;
6865 Feld[newx][newy] = element;
6866 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
6868 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
6870 element = Feld[newx][newy] = EL_ACID;
6872 else if (element == EL_MOLE)
6874 Feld[x][y] = EL_SAND;
6876 DrawLevelFieldCrumbledSandNeighbours(x, y);
6878 else if (element == EL_QUICKSAND_FILLING)
6880 element = Feld[newx][newy] = get_next_element(element);
6881 Store[newx][newy] = Store[x][y];
6883 else if (element == EL_QUICKSAND_EMPTYING)
6885 Feld[x][y] = get_next_element(element);
6886 element = Feld[newx][newy] = Store[x][y];
6888 else if (element == EL_QUICKSAND_FAST_FILLING)
6890 element = Feld[newx][newy] = get_next_element(element);
6891 Store[newx][newy] = Store[x][y];
6893 else if (element == EL_QUICKSAND_FAST_EMPTYING)
6895 Feld[x][y] = get_next_element(element);
6896 element = Feld[newx][newy] = Store[x][y];
6898 else if (element == EL_MAGIC_WALL_FILLING)
6900 element = Feld[newx][newy] = get_next_element(element);
6901 if (!game.magic_wall_active)
6902 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
6903 Store[newx][newy] = Store[x][y];
6905 else if (element == EL_MAGIC_WALL_EMPTYING)
6907 Feld[x][y] = get_next_element(element);
6908 if (!game.magic_wall_active)
6909 Feld[x][y] = EL_MAGIC_WALL_DEAD;
6910 element = Feld[newx][newy] = Store[x][y];
6912 #if USE_NEW_CUSTOM_VALUE
6913 InitField(newx, newy, FALSE);
6916 else if (element == EL_BD_MAGIC_WALL_FILLING)
6918 element = Feld[newx][newy] = get_next_element(element);
6919 if (!game.magic_wall_active)
6920 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
6921 Store[newx][newy] = Store[x][y];
6923 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
6925 Feld[x][y] = get_next_element(element);
6926 if (!game.magic_wall_active)
6927 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
6928 element = Feld[newx][newy] = Store[x][y];
6930 #if USE_NEW_CUSTOM_VALUE
6931 InitField(newx, newy, FALSE);
6934 else if (element == EL_DC_MAGIC_WALL_FILLING)
6936 element = Feld[newx][newy] = get_next_element(element);
6937 if (!game.magic_wall_active)
6938 element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
6939 Store[newx][newy] = Store[x][y];
6941 else if (element == EL_DC_MAGIC_WALL_EMPTYING)
6943 Feld[x][y] = get_next_element(element);
6944 if (!game.magic_wall_active)
6945 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
6946 element = Feld[newx][newy] = Store[x][y];
6948 #if USE_NEW_CUSTOM_VALUE
6949 InitField(newx, newy, FALSE);
6952 else if (element == EL_AMOEBA_DROPPING)
6954 Feld[x][y] = get_next_element(element);
6955 element = Feld[newx][newy] = Store[x][y];
6957 else if (element == EL_SOKOBAN_OBJECT)
6960 Feld[x][y] = Back[x][y];
6962 if (Back[newx][newy])
6963 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
6965 Back[x][y] = Back[newx][newy] = 0;
6968 Store[x][y] = EL_EMPTY;
6973 MovDelay[newx][newy] = 0;
6975 if (CAN_CHANGE_OR_HAS_ACTION(element))
6977 /* copy element change control values to new field */
6978 ChangeDelay[newx][newy] = ChangeDelay[x][y];
6979 ChangePage[newx][newy] = ChangePage[x][y];
6980 ChangeCount[newx][newy] = ChangeCount[x][y];
6981 ChangeEvent[newx][newy] = ChangeEvent[x][y];
6984 #if USE_NEW_CUSTOM_VALUE
6985 CustomValue[newx][newy] = CustomValue[x][y];
6988 ChangeDelay[x][y] = 0;
6989 ChangePage[x][y] = -1;
6990 ChangeCount[x][y] = 0;
6991 ChangeEvent[x][y] = -1;
6993 #if USE_NEW_CUSTOM_VALUE
6994 CustomValue[x][y] = 0;
6997 /* copy animation control values to new field */
6998 GfxFrame[newx][newy] = GfxFrame[x][y];
6999 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
7000 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
7001 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
7003 Pushed[x][y] = Pushed[newx][newy] = FALSE;
7005 /* some elements can leave other elements behind after moving */
7007 if (ei->move_leave_element != EL_EMPTY &&
7008 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
7009 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
7011 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
7012 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
7013 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
7016 int move_leave_element = ei->move_leave_element;
7020 /* this makes it possible to leave the removed element again */
7021 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
7022 move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
7024 /* this makes it possible to leave the removed element again */
7025 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
7026 move_leave_element = stored;
7029 /* this makes it possible to leave the removed element again */
7030 if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
7031 ei->move_leave_element == EL_TRIGGER_ELEMENT)
7032 move_leave_element = stored;
7035 Feld[x][y] = move_leave_element;
7037 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
7038 MovDir[x][y] = direction;
7040 InitField(x, y, FALSE);
7042 if (GFX_CRUMBLED(Feld[x][y]))
7043 DrawLevelFieldCrumbledSandNeighbours(x, y);
7045 if (ELEM_IS_PLAYER(move_leave_element))
7046 RelocatePlayer(x, y, move_leave_element);
7049 /* do this after checking for left-behind element */
7050 ResetGfxAnimation(x, y); /* reset animation values for old field */
7052 if (!CAN_MOVE(element) ||
7053 (CAN_FALL(element) && direction == MV_DOWN &&
7054 (element == EL_SPRING ||
7055 element_info[element].move_pattern == MV_WHEN_PUSHED ||
7056 element_info[element].move_pattern == MV_WHEN_DROPPED)))
7057 GfxDir[x][y] = MovDir[newx][newy] = 0;
7059 DrawLevelField(x, y);
7060 DrawLevelField(newx, newy);
7062 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
7064 /* prevent pushed element from moving on in pushed direction */
7065 if (pushed_by_player && CAN_MOVE(element) &&
7066 element_info[element].move_pattern & MV_ANY_DIRECTION &&
7067 !(element_info[element].move_pattern & direction))
7068 TurnRound(newx, newy);
7070 /* prevent elements on conveyor belt from moving on in last direction */
7071 if (pushed_by_conveyor && CAN_FALL(element) &&
7072 direction & MV_HORIZONTAL)
7073 MovDir[newx][newy] = 0;
7075 if (!pushed_by_player)
7077 int nextx = newx + dx, nexty = newy + dy;
7078 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
7080 WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
7082 if (CAN_FALL(element) && direction == MV_DOWN)
7083 WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
7085 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
7086 CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
7088 #if USE_FIX_IMPACT_COLLISION
7089 if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
7090 CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
7094 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
7096 TestIfBadThingTouchesPlayer(newx, newy);
7097 TestIfBadThingTouchesFriend(newx, newy);
7099 if (!IS_CUSTOM_ELEMENT(element))
7100 TestIfBadThingTouchesOtherBadThing(newx, newy);
7102 else if (element == EL_PENGUIN)
7103 TestIfFriendTouchesBadThing(newx, newy);
7105 /* give the player one last chance (one more frame) to move away */
7106 if (CAN_FALL(element) && direction == MV_DOWN &&
7107 (last_line || (!IS_FREE(x, newy + 1) &&
7108 (!IS_PLAYER(x, newy + 1) ||
7109 game.engine_version < VERSION_IDENT(3,1,1,0)))))
7112 if (pushed_by_player && !game.use_change_when_pushing_bug)
7114 int push_side = MV_DIR_OPPOSITE(direction);
7115 struct PlayerInfo *player = PLAYERINFO(x, y);
7117 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
7118 player->index_bit, push_side);
7119 CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
7120 player->index_bit, push_side);
7123 if (element == EL_EMC_ANDROID && pushed_by_player) /* make another move */
7124 MovDelay[newx][newy] = 1;
7126 CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
7128 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
7131 if (ChangePage[newx][newy] != -1) /* delayed change */
7133 int page = ChangePage[newx][newy];
7134 struct ElementChangeInfo *change = &ei->change_page[page];
7136 ChangePage[newx][newy] = -1;
7138 if (change->can_change)
7140 if (ChangeElement(newx, newy, element, page))
7142 if (change->post_change_function)
7143 change->post_change_function(newx, newy);
7147 if (change->has_action)
7148 ExecuteCustomElementAction(newx, newy, element, page);
7152 TestIfElementHitsCustomElement(newx, newy, direction);
7153 TestIfPlayerTouchesCustomElement(newx, newy);
7154 TestIfElementTouchesCustomElement(newx, newy);
7156 if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
7157 IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
7158 CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
7159 MV_DIR_OPPOSITE(direction));
7162 int AmoebeNachbarNr(int ax, int ay)
7165 int element = Feld[ax][ay];
7167 static int xy[4][2] =
7175 for (i = 0; i < NUM_DIRECTIONS; i++)
7177 int x = ax + xy[i][0];
7178 int y = ay + xy[i][1];
7180 if (!IN_LEV_FIELD(x, y))
7183 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
7184 group_nr = AmoebaNr[x][y];
7190 void AmoebenVereinigen(int ax, int ay)
7192 int i, x, y, xx, yy;
7193 int new_group_nr = AmoebaNr[ax][ay];
7194 static int xy[4][2] =
7202 if (new_group_nr == 0)
7205 for (i = 0; i < NUM_DIRECTIONS; i++)
7210 if (!IN_LEV_FIELD(x, y))
7213 if ((Feld[x][y] == EL_AMOEBA_FULL ||
7214 Feld[x][y] == EL_BD_AMOEBA ||
7215 Feld[x][y] == EL_AMOEBA_DEAD) &&
7216 AmoebaNr[x][y] != new_group_nr)
7218 int old_group_nr = AmoebaNr[x][y];
7220 if (old_group_nr == 0)
7223 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
7224 AmoebaCnt[old_group_nr] = 0;
7225 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
7226 AmoebaCnt2[old_group_nr] = 0;
7228 SCAN_PLAYFIELD(xx, yy)
7230 if (AmoebaNr[xx][yy] == old_group_nr)
7231 AmoebaNr[xx][yy] = new_group_nr;
7237 void AmoebeUmwandeln(int ax, int ay)
7241 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
7243 int group_nr = AmoebaNr[ax][ay];
7248 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
7249 printf("AmoebeUmwandeln(): This should never happen!\n");
7254 SCAN_PLAYFIELD(x, y)
7256 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
7259 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
7263 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
7264 SND_AMOEBA_TURNING_TO_GEM :
7265 SND_AMOEBA_TURNING_TO_ROCK));
7270 static int xy[4][2] =
7278 for (i = 0; i < NUM_DIRECTIONS; i++)
7283 if (!IN_LEV_FIELD(x, y))
7286 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
7288 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
7289 SND_AMOEBA_TURNING_TO_GEM :
7290 SND_AMOEBA_TURNING_TO_ROCK));
7297 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
7300 int group_nr = AmoebaNr[ax][ay];
7301 boolean done = FALSE;
7306 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
7307 printf("AmoebeUmwandelnBD(): This should never happen!\n");
7312 SCAN_PLAYFIELD(x, y)
7314 if (AmoebaNr[x][y] == group_nr &&
7315 (Feld[x][y] == EL_AMOEBA_DEAD ||
7316 Feld[x][y] == EL_BD_AMOEBA ||
7317 Feld[x][y] == EL_AMOEBA_GROWING))
7320 Feld[x][y] = new_element;
7321 InitField(x, y, FALSE);
7322 DrawLevelField(x, y);
7328 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
7329 SND_BD_AMOEBA_TURNING_TO_ROCK :
7330 SND_BD_AMOEBA_TURNING_TO_GEM));
7333 void AmoebeWaechst(int x, int y)
7335 static unsigned long sound_delay = 0;
7336 static unsigned long sound_delay_value = 0;
7338 if (!MovDelay[x][y]) /* start new growing cycle */
7342 if (DelayReached(&sound_delay, sound_delay_value))
7344 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
7345 sound_delay_value = 30;
7349 if (MovDelay[x][y]) /* wait some time before growing bigger */
7352 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7354 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
7355 6 - MovDelay[x][y]);
7357 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
7360 if (!MovDelay[x][y])
7362 Feld[x][y] = Store[x][y];
7364 DrawLevelField(x, y);
7369 void AmoebaDisappearing(int x, int y)
7371 static unsigned long sound_delay = 0;
7372 static unsigned long sound_delay_value = 0;
7374 if (!MovDelay[x][y]) /* start new shrinking cycle */
7378 if (DelayReached(&sound_delay, sound_delay_value))
7379 sound_delay_value = 30;
7382 if (MovDelay[x][y]) /* wait some time before shrinking */
7385 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7387 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
7388 6 - MovDelay[x][y]);
7390 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
7393 if (!MovDelay[x][y])
7395 Feld[x][y] = EL_EMPTY;
7396 DrawLevelField(x, y);
7398 /* don't let mole enter this field in this cycle;
7399 (give priority to objects falling to this field from above) */
7405 void AmoebeAbleger(int ax, int ay)
7408 int element = Feld[ax][ay];
7409 int graphic = el2img(element);
7410 int newax = ax, neway = ay;
7411 boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
7412 static int xy[4][2] =
7420 if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
7422 Feld[ax][ay] = EL_AMOEBA_DEAD;
7423 DrawLevelField(ax, ay);
7427 if (IS_ANIMATED(graphic))
7428 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7430 if (!MovDelay[ax][ay]) /* start making new amoeba field */
7431 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
7433 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
7436 if (MovDelay[ax][ay])
7440 if (can_drop) /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
7443 int x = ax + xy[start][0];
7444 int y = ay + xy[start][1];
7446 if (!IN_LEV_FIELD(x, y))
7449 if (IS_FREE(x, y) ||
7450 CAN_GROW_INTO(Feld[x][y]) ||
7451 Feld[x][y] == EL_QUICKSAND_EMPTY ||
7452 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
7458 if (newax == ax && neway == ay)
7461 else /* normal or "filled" (BD style) amoeba */
7464 boolean waiting_for_player = FALSE;
7466 for (i = 0; i < NUM_DIRECTIONS; i++)
7468 int j = (start + i) % 4;
7469 int x = ax + xy[j][0];
7470 int y = ay + xy[j][1];
7472 if (!IN_LEV_FIELD(x, y))
7475 if (IS_FREE(x, y) ||
7476 CAN_GROW_INTO(Feld[x][y]) ||
7477 Feld[x][y] == EL_QUICKSAND_EMPTY ||
7478 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
7484 else if (IS_PLAYER(x, y))
7485 waiting_for_player = TRUE;
7488 if (newax == ax && neway == ay) /* amoeba cannot grow */
7490 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
7492 Feld[ax][ay] = EL_AMOEBA_DEAD;
7493 DrawLevelField(ax, ay);
7494 AmoebaCnt[AmoebaNr[ax][ay]]--;
7496 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
7498 if (element == EL_AMOEBA_FULL)
7499 AmoebeUmwandeln(ax, ay);
7500 else if (element == EL_BD_AMOEBA)
7501 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
7506 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
7508 /* amoeba gets larger by growing in some direction */
7510 int new_group_nr = AmoebaNr[ax][ay];
7513 if (new_group_nr == 0)
7515 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
7516 printf("AmoebeAbleger(): This should never happen!\n");
7521 AmoebaNr[newax][neway] = new_group_nr;
7522 AmoebaCnt[new_group_nr]++;
7523 AmoebaCnt2[new_group_nr]++;
7525 /* if amoeba touches other amoeba(s) after growing, unify them */
7526 AmoebenVereinigen(newax, neway);
7528 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
7530 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
7536 if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
7537 (neway == lev_fieldy - 1 && newax != ax))
7539 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
7540 Store[newax][neway] = element;
7542 else if (neway == ay || element == EL_EMC_DRIPPER)
7544 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
7546 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
7550 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
7551 Feld[ax][ay] = EL_AMOEBA_DROPPING;
7552 Store[ax][ay] = EL_AMOEBA_DROP;
7553 ContinueMoving(ax, ay);
7557 DrawLevelField(newax, neway);
7560 void Life(int ax, int ay)
7564 int element = Feld[ax][ay];
7565 int graphic = el2img(element);
7566 int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
7568 boolean changed = FALSE;
7570 if (IS_ANIMATED(graphic))
7571 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7576 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
7577 MovDelay[ax][ay] = life_time;
7579 if (MovDelay[ax][ay]) /* wait some time before next cycle */
7582 if (MovDelay[ax][ay])
7586 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
7588 int xx = ax+x1, yy = ay+y1;
7591 if (!IN_LEV_FIELD(xx, yy))
7594 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
7596 int x = xx+x2, y = yy+y2;
7598 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
7601 if (((Feld[x][y] == element ||
7602 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
7604 (IS_FREE(x, y) && Stop[x][y]))
7608 if (xx == ax && yy == ay) /* field in the middle */
7610 if (nachbarn < life_parameter[0] ||
7611 nachbarn > life_parameter[1])
7613 Feld[xx][yy] = EL_EMPTY;
7615 DrawLevelField(xx, yy);
7616 Stop[xx][yy] = TRUE;
7620 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
7621 { /* free border field */
7622 if (nachbarn >= life_parameter[2] &&
7623 nachbarn <= life_parameter[3])
7625 Feld[xx][yy] = element;
7626 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
7628 DrawLevelField(xx, yy);
7629 Stop[xx][yy] = TRUE;
7636 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
7637 SND_GAME_OF_LIFE_GROWING);
7640 static void InitRobotWheel(int x, int y)
7642 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
7645 static void RunRobotWheel(int x, int y)
7647 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
7650 static void StopRobotWheel(int x, int y)
7652 if (ZX == x && ZY == y)
7656 static void InitTimegateWheel(int x, int y)
7658 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
7661 static void RunTimegateWheel(int x, int y)
7663 PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
7666 static void InitMagicBallDelay(int x, int y)
7669 ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
7671 ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
7675 static void ActivateMagicBall(int bx, int by)
7679 if (level.ball_random)
7681 int pos_border = RND(8); /* select one of the eight border elements */
7682 int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
7683 int xx = pos_content % 3;
7684 int yy = pos_content / 3;
7689 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
7690 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
7694 for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
7696 int xx = x - bx + 1;
7697 int yy = y - by + 1;
7699 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
7700 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
7704 game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
7707 void CheckExit(int x, int y)
7709 if (local_player->gems_still_needed > 0 ||
7710 local_player->sokobanfields_still_needed > 0 ||
7711 local_player->lights_still_needed > 0)
7713 int element = Feld[x][y];
7714 int graphic = el2img(element);
7716 if (IS_ANIMATED(graphic))
7717 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7722 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7725 Feld[x][y] = EL_EXIT_OPENING;
7727 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
7730 void CheckExitEM(int x, int y)
7732 if (local_player->gems_still_needed > 0 ||
7733 local_player->sokobanfields_still_needed > 0 ||
7734 local_player->lights_still_needed > 0)
7736 int element = Feld[x][y];
7737 int graphic = el2img(element);
7739 if (IS_ANIMATED(graphic))
7740 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7745 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7748 Feld[x][y] = EL_EM_EXIT_OPENING;
7750 PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
7753 void CheckExitSteel(int x, int y)
7755 if (local_player->gems_still_needed > 0 ||
7756 local_player->sokobanfields_still_needed > 0 ||
7757 local_player->lights_still_needed > 0)
7759 int element = Feld[x][y];
7760 int graphic = el2img(element);
7762 if (IS_ANIMATED(graphic))
7763 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7768 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7771 Feld[x][y] = EL_STEEL_EXIT_OPENING;
7773 PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
7776 void CheckExitSteelEM(int x, int y)
7778 if (local_player->gems_still_needed > 0 ||
7779 local_player->sokobanfields_still_needed > 0 ||
7780 local_player->lights_still_needed > 0)
7782 int element = Feld[x][y];
7783 int graphic = el2img(element);
7785 if (IS_ANIMATED(graphic))
7786 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7791 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7794 Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
7796 PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
7799 void CheckExitSP(int x, int y)
7801 if (local_player->gems_still_needed > 0)
7803 int element = Feld[x][y];
7804 int graphic = el2img(element);
7806 if (IS_ANIMATED(graphic))
7807 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7812 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7815 Feld[x][y] = EL_SP_EXIT_OPENING;
7817 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
7820 static void CloseAllOpenTimegates()
7824 SCAN_PLAYFIELD(x, y)
7826 int element = Feld[x][y];
7828 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
7830 Feld[x][y] = EL_TIMEGATE_CLOSING;
7832 PlayLevelSoundAction(x, y, ACTION_CLOSING);
7837 void DrawTwinkleOnField(int x, int y)
7839 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
7842 if (Feld[x][y] == EL_BD_DIAMOND)
7845 if (MovDelay[x][y] == 0) /* next animation frame */
7846 MovDelay[x][y] = 11 * !GetSimpleRandom(500);
7848 if (MovDelay[x][y] != 0) /* wait some time before next frame */
7852 if (setup.direct_draw && MovDelay[x][y])
7853 SetDrawtoField(DRAW_BUFFERED);
7855 DrawLevelElementAnimation(x, y, Feld[x][y]);
7857 if (MovDelay[x][y] != 0)
7859 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
7860 10 - MovDelay[x][y]);
7862 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
7864 if (setup.direct_draw)
7868 dest_x = FX + SCREENX(x) * TILEX;
7869 dest_y = FY + SCREENY(y) * TILEY;
7871 BlitBitmap(drawto_field, window,
7872 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
7873 SetDrawtoField(DRAW_DIRECT);
7879 void MauerWaechst(int x, int y)
7883 if (!MovDelay[x][y]) /* next animation frame */
7884 MovDelay[x][y] = 3 * delay;
7886 if (MovDelay[x][y]) /* wait some time before next frame */
7890 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7892 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
7893 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
7895 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
7898 if (!MovDelay[x][y])
7900 if (MovDir[x][y] == MV_LEFT)
7902 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
7903 DrawLevelField(x - 1, y);
7905 else if (MovDir[x][y] == MV_RIGHT)
7907 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
7908 DrawLevelField(x + 1, y);
7910 else if (MovDir[x][y] == MV_UP)
7912 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
7913 DrawLevelField(x, y - 1);
7917 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
7918 DrawLevelField(x, y + 1);
7921 Feld[x][y] = Store[x][y];
7923 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7924 DrawLevelField(x, y);
7929 void MauerAbleger(int ax, int ay)
7931 int element = Feld[ax][ay];
7932 int graphic = el2img(element);
7933 boolean oben_frei = FALSE, unten_frei = FALSE;
7934 boolean links_frei = FALSE, rechts_frei = FALSE;
7935 boolean oben_massiv = FALSE, unten_massiv = FALSE;
7936 boolean links_massiv = FALSE, rechts_massiv = FALSE;
7937 boolean new_wall = FALSE;
7939 if (IS_ANIMATED(graphic))
7940 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7942 if (!MovDelay[ax][ay]) /* start building new wall */
7943 MovDelay[ax][ay] = 6;
7945 if (MovDelay[ax][ay]) /* wait some time before building new wall */
7948 if (MovDelay[ax][ay])
7952 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
7954 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
7956 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
7958 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
7961 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
7962 element == EL_EXPANDABLE_WALL_ANY)
7966 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
7967 Store[ax][ay-1] = element;
7968 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
7969 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
7970 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
7971 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
7976 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
7977 Store[ax][ay+1] = element;
7978 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
7979 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
7980 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
7981 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
7986 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7987 element == EL_EXPANDABLE_WALL_ANY ||
7988 element == EL_EXPANDABLE_WALL ||
7989 element == EL_BD_EXPANDABLE_WALL)
7993 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
7994 Store[ax-1][ay] = element;
7995 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
7996 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
7997 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
7998 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
8004 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
8005 Store[ax+1][ay] = element;
8006 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
8007 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
8008 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
8009 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
8014 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
8015 DrawLevelField(ax, ay);
8017 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
8019 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
8020 unten_massiv = TRUE;
8021 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
8022 links_massiv = TRUE;
8023 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
8024 rechts_massiv = TRUE;
8026 if (((oben_massiv && unten_massiv) ||
8027 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
8028 element == EL_EXPANDABLE_WALL) &&
8029 ((links_massiv && rechts_massiv) ||
8030 element == EL_EXPANDABLE_WALL_VERTICAL))
8031 Feld[ax][ay] = EL_WALL;
8034 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
8037 void MauerAblegerStahl(int ax, int ay)
8039 int element = Feld[ax][ay];
8040 int graphic = el2img(element);
8041 boolean oben_frei = FALSE, unten_frei = FALSE;
8042 boolean links_frei = FALSE, rechts_frei = FALSE;
8043 boolean oben_massiv = FALSE, unten_massiv = FALSE;
8044 boolean links_massiv = FALSE, rechts_massiv = FALSE;
8045 boolean new_wall = FALSE;
8047 if (IS_ANIMATED(graphic))
8048 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8050 if (!MovDelay[ax][ay]) /* start building new wall */
8051 MovDelay[ax][ay] = 6;
8053 if (MovDelay[ax][ay]) /* wait some time before building new wall */
8056 if (MovDelay[ax][ay])
8060 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
8062 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
8064 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
8066 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
8069 if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
8070 element == EL_EXPANDABLE_STEELWALL_ANY)
8074 Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
8075 Store[ax][ay-1] = element;
8076 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
8077 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
8078 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
8079 IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
8084 Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
8085 Store[ax][ay+1] = element;
8086 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
8087 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
8088 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
8089 IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
8094 if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
8095 element == EL_EXPANDABLE_STEELWALL_ANY)
8099 Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
8100 Store[ax-1][ay] = element;
8101 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
8102 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
8103 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
8104 IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
8110 Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
8111 Store[ax+1][ay] = element;
8112 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
8113 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
8114 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
8115 IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
8120 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
8122 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
8123 unten_massiv = TRUE;
8124 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
8125 links_massiv = TRUE;
8126 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
8127 rechts_massiv = TRUE;
8129 if (((oben_massiv && unten_massiv) ||
8130 element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
8131 ((links_massiv && rechts_massiv) ||
8132 element == EL_EXPANDABLE_STEELWALL_VERTICAL))
8133 Feld[ax][ay] = EL_WALL;
8136 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
8139 void CheckForDragon(int x, int y)
8142 boolean dragon_found = FALSE;
8143 static int xy[4][2] =
8151 for (i = 0; i < NUM_DIRECTIONS; i++)
8153 for (j = 0; j < 4; j++)
8155 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
8157 if (IN_LEV_FIELD(xx, yy) &&
8158 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
8160 if (Feld[xx][yy] == EL_DRAGON)
8161 dragon_found = TRUE;
8170 for (i = 0; i < NUM_DIRECTIONS; i++)
8172 for (j = 0; j < 3; j++)
8174 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
8176 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
8178 Feld[xx][yy] = EL_EMPTY;
8179 DrawLevelField(xx, yy);
8188 static void InitBuggyBase(int x, int y)
8190 int element = Feld[x][y];
8191 int activating_delay = FRAMES_PER_SECOND / 4;
8194 (element == EL_SP_BUGGY_BASE ?
8195 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
8196 element == EL_SP_BUGGY_BASE_ACTIVATING ?
8198 element == EL_SP_BUGGY_BASE_ACTIVE ?
8199 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
8202 static void WarnBuggyBase(int x, int y)
8205 static int xy[4][2] =
8213 for (i = 0; i < NUM_DIRECTIONS; i++)
8215 int xx = x + xy[i][0];
8216 int yy = y + xy[i][1];
8218 if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
8220 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
8227 static void InitTrap(int x, int y)
8229 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
8232 static void ActivateTrap(int x, int y)
8234 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
8237 static void ChangeActiveTrap(int x, int y)
8239 int graphic = IMG_TRAP_ACTIVE;
8241 /* if new animation frame was drawn, correct crumbled sand border */
8242 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
8243 DrawLevelFieldCrumbledSand(x, y);
8246 static int getSpecialActionElement(int element, int number, int base_element)
8248 return (element != EL_EMPTY ? element :
8249 number != -1 ? base_element + number - 1 :
8253 static int getModifiedActionNumber(int value_old, int operator, int operand,
8254 int value_min, int value_max)
8256 int value_new = (operator == CA_MODE_SET ? operand :
8257 operator == CA_MODE_ADD ? value_old + operand :
8258 operator == CA_MODE_SUBTRACT ? value_old - operand :
8259 operator == CA_MODE_MULTIPLY ? value_old * operand :
8260 operator == CA_MODE_DIVIDE ? value_old / MAX(1, operand) :
8261 operator == CA_MODE_MODULO ? value_old % MAX(1, operand) :
8264 return (value_new < value_min ? value_min :
8265 value_new > value_max ? value_max :
8269 static void ExecuteCustomElementAction(int x, int y, int element, int page)
8271 struct ElementInfo *ei = &element_info[element];
8272 struct ElementChangeInfo *change = &ei->change_page[page];
8273 int target_element = change->target_element;
8274 int action_type = change->action_type;
8275 int action_mode = change->action_mode;
8276 int action_arg = change->action_arg;
8279 if (!change->has_action)
8282 /* ---------- determine action paramater values -------------------------- */
8284 int level_time_value =
8285 (level.time > 0 ? TimeLeft :
8288 int action_arg_element =
8289 (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player :
8290 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
8291 action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element :
8294 int action_arg_direction =
8295 (action_arg >= CA_ARG_DIRECTION_LEFT &&
8296 action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
8297 action_arg == CA_ARG_DIRECTION_TRIGGER ?
8298 change->actual_trigger_side :
8299 action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
8300 MV_DIR_OPPOSITE(change->actual_trigger_side) :
8303 int action_arg_number_min =
8304 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
8307 int action_arg_number_max =
8308 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
8309 action_type == CA_SET_LEVEL_GEMS ? 999 :
8310 action_type == CA_SET_LEVEL_TIME ? 9999 :
8311 action_type == CA_SET_LEVEL_SCORE ? 99999 :
8312 action_type == CA_SET_CE_VALUE ? 9999 :
8313 action_type == CA_SET_CE_SCORE ? 9999 :
8316 int action_arg_number_reset =
8317 (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
8318 action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
8319 action_type == CA_SET_LEVEL_TIME ? level.time :
8320 action_type == CA_SET_LEVEL_SCORE ? 0 :
8321 #if USE_NEW_CUSTOM_VALUE
8322 action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
8324 action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
8326 action_type == CA_SET_CE_SCORE ? 0 :
8329 int action_arg_number =
8330 (action_arg <= CA_ARG_MAX ? action_arg :
8331 action_arg >= CA_ARG_SPEED_NOT_MOVING &&
8332 action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
8333 action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
8334 action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
8335 action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
8336 action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
8337 #if USE_NEW_CUSTOM_VALUE
8338 action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
8340 action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
8342 action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
8343 action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
8344 action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
8345 action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
8346 action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
8347 action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
8348 action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
8349 action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
8350 action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
8351 action_arg == CA_ARG_ELEMENT_NR_TARGET ? change->target_element :
8352 action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
8355 int action_arg_number_old =
8356 (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
8357 action_type == CA_SET_LEVEL_TIME ? TimeLeft :
8358 action_type == CA_SET_LEVEL_SCORE ? local_player->score :
8359 action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
8360 action_type == CA_SET_CE_SCORE ? ei->collect_score :
8363 int action_arg_number_new =
8364 getModifiedActionNumber(action_arg_number_old,
8365 action_mode, action_arg_number,
8366 action_arg_number_min, action_arg_number_max);
8368 int trigger_player_bits =
8369 (change->actual_trigger_player >= EL_PLAYER_1 &&
8370 change->actual_trigger_player <= EL_PLAYER_4 ?
8371 (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
8374 int action_arg_player_bits =
8375 (action_arg >= CA_ARG_PLAYER_1 &&
8376 action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
8377 action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
8380 /* ---------- execute action -------------------------------------------- */
8382 switch (action_type)
8389 /* ---------- level actions ------------------------------------------- */
8391 case CA_RESTART_LEVEL:
8393 game.restart_level = TRUE;
8398 case CA_SHOW_ENVELOPE:
8400 int element = getSpecialActionElement(action_arg_element,
8401 action_arg_number, EL_ENVELOPE_1);
8403 if (IS_ENVELOPE(element))
8404 local_player->show_envelope = element;
8409 case CA_SET_LEVEL_TIME:
8411 if (level.time > 0) /* only modify limited time value */
8413 TimeLeft = action_arg_number_new;
8415 DrawGameValue_Time(TimeLeft);
8417 if (!TimeLeft && setup.time_limit)
8418 for (i = 0; i < MAX_PLAYERS; i++)
8419 KillPlayer(&stored_player[i]);
8425 case CA_SET_LEVEL_SCORE:
8427 local_player->score = action_arg_number_new;
8429 DrawGameValue_Score(local_player->score);
8434 case CA_SET_LEVEL_GEMS:
8436 local_player->gems_still_needed = action_arg_number_new;
8438 DrawGameValue_Emeralds(local_player->gems_still_needed);
8443 #if !USE_PLAYER_GRAVITY
8444 case CA_SET_LEVEL_GRAVITY:
8446 game.gravity = (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
8447 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
8448 action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
8454 case CA_SET_LEVEL_WIND:
8456 game.wind_direction = action_arg_direction;
8461 /* ---------- player actions ------------------------------------------ */
8463 case CA_MOVE_PLAYER:
8465 /* automatically move to the next field in specified direction */
8466 for (i = 0; i < MAX_PLAYERS; i++)
8467 if (trigger_player_bits & (1 << i))
8468 stored_player[i].programmed_action = action_arg_direction;
8473 case CA_EXIT_PLAYER:
8475 for (i = 0; i < MAX_PLAYERS; i++)
8476 if (action_arg_player_bits & (1 << i))
8477 PlayerWins(&stored_player[i]);
8482 case CA_KILL_PLAYER:
8484 for (i = 0; i < MAX_PLAYERS; i++)
8485 if (action_arg_player_bits & (1 << i))
8486 KillPlayer(&stored_player[i]);
8491 case CA_SET_PLAYER_KEYS:
8493 int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
8494 int element = getSpecialActionElement(action_arg_element,
8495 action_arg_number, EL_KEY_1);
8497 if (IS_KEY(element))
8499 for (i = 0; i < MAX_PLAYERS; i++)
8501 if (trigger_player_bits & (1 << i))
8503 stored_player[i].key[KEY_NR(element)] = key_state;
8505 DrawGameDoorValues();
8513 case CA_SET_PLAYER_SPEED:
8515 for (i = 0; i < MAX_PLAYERS; i++)
8517 if (trigger_player_bits & (1 << i))
8519 int move_stepsize = TILEX / stored_player[i].move_delay_value;
8521 if (action_arg == CA_ARG_SPEED_FASTER &&
8522 stored_player[i].cannot_move)
8524 action_arg_number = STEPSIZE_VERY_SLOW;
8526 else if (action_arg == CA_ARG_SPEED_SLOWER ||
8527 action_arg == CA_ARG_SPEED_FASTER)
8529 action_arg_number = 2;
8530 action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
8533 else if (action_arg == CA_ARG_NUMBER_RESET)
8535 action_arg_number = level.initial_player_stepsize[i];
8539 getModifiedActionNumber(move_stepsize,
8542 action_arg_number_min,
8543 action_arg_number_max);
8545 SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
8552 case CA_SET_PLAYER_SHIELD:
8554 for (i = 0; i < MAX_PLAYERS; i++)
8556 if (trigger_player_bits & (1 << i))
8558 if (action_arg == CA_ARG_SHIELD_OFF)
8560 stored_player[i].shield_normal_time_left = 0;
8561 stored_player[i].shield_deadly_time_left = 0;
8563 else if (action_arg == CA_ARG_SHIELD_NORMAL)
8565 stored_player[i].shield_normal_time_left = 999999;
8567 else if (action_arg == CA_ARG_SHIELD_DEADLY)
8569 stored_player[i].shield_normal_time_left = 999999;
8570 stored_player[i].shield_deadly_time_left = 999999;
8578 #if USE_PLAYER_GRAVITY
8579 case CA_SET_PLAYER_GRAVITY:
8581 for (i = 0; i < MAX_PLAYERS; i++)
8583 if (trigger_player_bits & (1 << i))
8585 stored_player[i].gravity =
8586 (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
8587 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
8588 action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
8589 stored_player[i].gravity);
8597 case CA_SET_PLAYER_ARTWORK:
8599 for (i = 0; i < MAX_PLAYERS; i++)
8601 if (trigger_player_bits & (1 << i))
8603 int artwork_element = action_arg_element;
8605 if (action_arg == CA_ARG_ELEMENT_RESET)
8607 (level.use_artwork_element[i] ? level.artwork_element[i] :
8608 stored_player[i].element_nr);
8610 #if USE_GFX_RESET_PLAYER_ARTWORK
8611 if (stored_player[i].artwork_element != artwork_element)
8612 stored_player[i].Frame = 0;
8615 stored_player[i].artwork_element = artwork_element;
8617 SetPlayerWaiting(&stored_player[i], FALSE);
8619 /* set number of special actions for bored and sleeping animation */
8620 stored_player[i].num_special_action_bored =
8621 get_num_special_action(artwork_element,
8622 ACTION_BORING_1, ACTION_BORING_LAST);
8623 stored_player[i].num_special_action_sleeping =
8624 get_num_special_action(artwork_element,
8625 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
8632 /* ---------- CE actions ---------------------------------------------- */
8634 case CA_SET_CE_VALUE:
8636 #if USE_NEW_CUSTOM_VALUE
8637 int last_ce_value = CustomValue[x][y];
8639 CustomValue[x][y] = action_arg_number_new;
8641 if (CustomValue[x][y] != last_ce_value)
8643 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
8644 CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
8646 if (CustomValue[x][y] == 0)
8648 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
8649 CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
8657 case CA_SET_CE_SCORE:
8659 #if USE_NEW_CUSTOM_VALUE
8660 int last_ce_score = ei->collect_score;
8662 ei->collect_score = action_arg_number_new;
8664 if (ei->collect_score != last_ce_score)
8666 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
8667 CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
8669 if (ei->collect_score == 0)
8673 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
8674 CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
8677 This is a very special case that seems to be a mixture between
8678 CheckElementChange() and CheckTriggeredElementChange(): while
8679 the first one only affects single elements that are triggered
8680 directly, the second one affects multiple elements in the playfield
8681 that are triggered indirectly by another element. This is a third
8682 case: Changing the CE score always affects multiple identical CEs,
8683 so every affected CE must be checked, not only the single CE for
8684 which the CE score was changed in the first place (as every instance
8685 of that CE shares the same CE score, and therefore also can change)!
8687 SCAN_PLAYFIELD(xx, yy)
8689 if (Feld[xx][yy] == element)
8690 CheckElementChange(xx, yy, element, EL_UNDEFINED,
8691 CE_SCORE_GETS_ZERO);
8700 /* ---------- engine actions ------------------------------------------ */
8702 case CA_SET_ENGINE_SCAN_MODE:
8704 InitPlayfieldScanMode(action_arg);
8714 static void CreateFieldExt(int x, int y, int element, boolean is_change)
8716 int old_element = Feld[x][y];
8717 int new_element = get_element_from_group_element(element);
8718 int previous_move_direction = MovDir[x][y];
8719 #if USE_NEW_CUSTOM_VALUE
8720 int last_ce_value = CustomValue[x][y];
8722 boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
8723 boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
8724 boolean add_player_onto_element = (new_element_is_player &&
8725 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
8726 /* this breaks SnakeBite when a snake is
8727 halfway through a door that closes */
8728 /* NOW FIXED AT LEVEL INIT IN files.c */
8729 new_element != EL_SOKOBAN_FIELD_PLAYER &&
8731 IS_WALKABLE(old_element));
8734 /* check if element under the player changes from accessible to unaccessible
8735 (needed for special case of dropping element which then changes) */
8736 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
8737 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
8745 if (!add_player_onto_element)
8747 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
8748 RemoveMovingField(x, y);
8752 Feld[x][y] = new_element;
8754 #if !USE_GFX_RESET_GFX_ANIMATION
8755 ResetGfxAnimation(x, y);
8756 ResetRandomAnimationValue(x, y);
8759 if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
8760 MovDir[x][y] = previous_move_direction;
8762 #if USE_NEW_CUSTOM_VALUE
8763 if (element_info[new_element].use_last_ce_value)
8764 CustomValue[x][y] = last_ce_value;
8767 InitField_WithBug1(x, y, FALSE);
8769 new_element = Feld[x][y]; /* element may have changed */
8771 #if USE_GFX_RESET_GFX_ANIMATION
8772 ResetGfxAnimation(x, y);
8773 ResetRandomAnimationValue(x, y);
8776 DrawLevelField(x, y);
8778 if (GFX_CRUMBLED(new_element))
8779 DrawLevelFieldCrumbledSandNeighbours(x, y);
8783 /* check if element under the player changes from accessible to unaccessible
8784 (needed for special case of dropping element which then changes) */
8785 /* (must be checked after creating new element for walkable group elements) */
8786 #if USE_FIX_KILLED_BY_NON_WALKABLE
8787 if (IS_PLAYER(x, y) && !player_explosion_protected &&
8788 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
8795 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
8796 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
8805 /* "ChangeCount" not set yet to allow "entered by player" change one time */
8806 if (new_element_is_player)
8807 RelocatePlayer(x, y, new_element);
8810 ChangeCount[x][y]++; /* count number of changes in the same frame */
8812 TestIfBadThingTouchesPlayer(x, y);
8813 TestIfPlayerTouchesCustomElement(x, y);
8814 TestIfElementTouchesCustomElement(x, y);
8817 static void CreateField(int x, int y, int element)
8819 CreateFieldExt(x, y, element, FALSE);
8822 static void CreateElementFromChange(int x, int y, int element)
8824 element = GET_VALID_RUNTIME_ELEMENT(element);
8826 #if USE_STOP_CHANGED_ELEMENTS
8827 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
8829 int old_element = Feld[x][y];
8831 /* prevent changed element from moving in same engine frame
8832 unless both old and new element can either fall or move */
8833 if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
8834 (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
8839 CreateFieldExt(x, y, element, TRUE);
8842 static boolean ChangeElement(int x, int y, int element, int page)
8844 struct ElementInfo *ei = &element_info[element];
8845 struct ElementChangeInfo *change = &ei->change_page[page];
8846 int ce_value = CustomValue[x][y];
8847 int ce_score = ei->collect_score;
8849 int old_element = Feld[x][y];
8851 /* always use default change event to prevent running into a loop */
8852 if (ChangeEvent[x][y] == -1)
8853 ChangeEvent[x][y] = CE_DELAY;
8855 if (ChangeEvent[x][y] == CE_DELAY)
8857 /* reset actual trigger element, trigger player and action element */
8858 change->actual_trigger_element = EL_EMPTY;
8859 change->actual_trigger_player = EL_PLAYER_1;
8860 change->actual_trigger_side = CH_SIDE_NONE;
8861 change->actual_trigger_ce_value = 0;
8862 change->actual_trigger_ce_score = 0;
8865 /* do not change elements more than a specified maximum number of changes */
8866 if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
8869 ChangeCount[x][y]++; /* count number of changes in the same frame */
8871 if (change->explode)
8878 if (change->use_target_content)
8880 boolean complete_replace = TRUE;
8881 boolean can_replace[3][3];
8884 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
8887 boolean is_walkable;
8888 boolean is_diggable;
8889 boolean is_collectible;
8890 boolean is_removable;
8891 boolean is_destructible;
8892 int ex = x + xx - 1;
8893 int ey = y + yy - 1;
8894 int content_element = change->target_content.e[xx][yy];
8897 can_replace[xx][yy] = TRUE;
8899 if (ex == x && ey == y) /* do not check changing element itself */
8902 if (content_element == EL_EMPTY_SPACE)
8904 can_replace[xx][yy] = FALSE; /* do not replace border with space */
8909 if (!IN_LEV_FIELD(ex, ey))
8911 can_replace[xx][yy] = FALSE;
8912 complete_replace = FALSE;
8919 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
8920 e = MovingOrBlocked2Element(ex, ey);
8922 is_empty = (IS_FREE(ex, ey) ||
8923 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
8925 is_walkable = (is_empty || IS_WALKABLE(e));
8926 is_diggable = (is_empty || IS_DIGGABLE(e));
8927 is_collectible = (is_empty || IS_COLLECTIBLE(e));
8928 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
8929 is_removable = (is_diggable || is_collectible);
8931 can_replace[xx][yy] =
8932 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
8933 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
8934 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
8935 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
8936 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
8937 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
8938 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
8940 if (!can_replace[xx][yy])
8941 complete_replace = FALSE;
8944 if (!change->only_if_complete || complete_replace)
8946 boolean something_has_changed = FALSE;
8948 if (change->only_if_complete && change->use_random_replace &&
8949 RND(100) < change->random_percentage)
8952 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
8954 int ex = x + xx - 1;
8955 int ey = y + yy - 1;
8956 int content_element;
8958 if (can_replace[xx][yy] && (!change->use_random_replace ||
8959 RND(100) < change->random_percentage))
8961 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
8962 RemoveMovingField(ex, ey);
8964 ChangeEvent[ex][ey] = ChangeEvent[x][y];
8966 content_element = change->target_content.e[xx][yy];
8967 target_element = GET_TARGET_ELEMENT(element, content_element, change,
8968 ce_value, ce_score);
8970 CreateElementFromChange(ex, ey, target_element);
8972 something_has_changed = TRUE;
8974 /* for symmetry reasons, freeze newly created border elements */
8975 if (ex != x || ey != y)
8976 Stop[ex][ey] = TRUE; /* no more moving in this frame */
8980 if (something_has_changed)
8982 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
8983 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
8989 target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
8990 ce_value, ce_score);
8992 if (element == EL_DIAGONAL_GROWING ||
8993 element == EL_DIAGONAL_SHRINKING)
8995 target_element = Store[x][y];
8997 Store[x][y] = EL_EMPTY;
9000 CreateElementFromChange(x, y, target_element);
9002 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
9003 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
9006 /* this uses direct change before indirect change */
9007 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
9012 #if USE_NEW_DELAYED_ACTION
9014 static void HandleElementChange(int x, int y, int page)
9016 int element = MovingOrBlocked2Element(x, y);
9017 struct ElementInfo *ei = &element_info[element];
9018 struct ElementChangeInfo *change = &ei->change_page[page];
9021 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
9022 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
9025 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
9026 x, y, element, element_info[element].token_name);
9027 printf("HandleElementChange(): This should never happen!\n");
9032 /* this can happen with classic bombs on walkable, changing elements */
9033 if (!CAN_CHANGE_OR_HAS_ACTION(element))
9036 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
9037 ChangeDelay[x][y] = 0;
9043 if (ChangeDelay[x][y] == 0) /* initialize element change */
9045 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
9047 if (change->can_change)
9049 ResetGfxAnimation(x, y);
9050 ResetRandomAnimationValue(x, y);
9052 if (change->pre_change_function)
9053 change->pre_change_function(x, y);
9057 ChangeDelay[x][y]--;
9059 if (ChangeDelay[x][y] != 0) /* continue element change */
9061 if (change->can_change)
9063 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9065 if (IS_ANIMATED(graphic))
9066 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9068 if (change->change_function)
9069 change->change_function(x, y);
9072 else /* finish element change */
9074 if (ChangePage[x][y] != -1) /* remember page from delayed change */
9076 page = ChangePage[x][y];
9077 ChangePage[x][y] = -1;
9079 change = &ei->change_page[page];
9082 if (IS_MOVING(x, y)) /* never change a running system ;-) */
9084 ChangeDelay[x][y] = 1; /* try change after next move step */
9085 ChangePage[x][y] = page; /* remember page to use for change */
9090 if (change->can_change)
9092 if (ChangeElement(x, y, element, page))
9094 if (change->post_change_function)
9095 change->post_change_function(x, y);
9099 if (change->has_action)
9100 ExecuteCustomElementAction(x, y, element, page);
9106 static void HandleElementChange(int x, int y, int page)
9108 int element = MovingOrBlocked2Element(x, y);
9109 struct ElementInfo *ei = &element_info[element];
9110 struct ElementChangeInfo *change = &ei->change_page[page];
9113 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
9116 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
9117 x, y, element, element_info[element].token_name);
9118 printf("HandleElementChange(): This should never happen!\n");
9123 /* this can happen with classic bombs on walkable, changing elements */
9124 if (!CAN_CHANGE(element))
9127 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
9128 ChangeDelay[x][y] = 0;
9134 if (ChangeDelay[x][y] == 0) /* initialize element change */
9136 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
9138 ResetGfxAnimation(x, y);
9139 ResetRandomAnimationValue(x, y);
9141 if (change->pre_change_function)
9142 change->pre_change_function(x, y);
9145 ChangeDelay[x][y]--;
9147 if (ChangeDelay[x][y] != 0) /* continue element change */
9149 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9151 if (IS_ANIMATED(graphic))
9152 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9154 if (change->change_function)
9155 change->change_function(x, y);
9157 else /* finish element change */
9159 if (ChangePage[x][y] != -1) /* remember page from delayed change */
9161 page = ChangePage[x][y];
9162 ChangePage[x][y] = -1;
9164 change = &ei->change_page[page];
9167 if (IS_MOVING(x, y)) /* never change a running system ;-) */
9169 ChangeDelay[x][y] = 1; /* try change after next move step */
9170 ChangePage[x][y] = page; /* remember page to use for change */
9175 if (ChangeElement(x, y, element, page))
9177 if (change->post_change_function)
9178 change->post_change_function(x, y);
9185 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
9186 int trigger_element,
9192 boolean change_done_any = FALSE;
9193 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
9196 if (!(trigger_events[trigger_element][trigger_event]))
9200 printf("::: CheckTriggeredElementChangeExt %d ... [%d, %d, %d, '%s']\n",
9201 trigger_event, recursion_loop_depth, recursion_loop_detected,
9202 recursion_loop_element, EL_NAME(recursion_loop_element));
9205 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
9207 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
9209 int element = EL_CUSTOM_START + i;
9210 boolean change_done = FALSE;
9213 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
9214 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
9217 for (p = 0; p < element_info[element].num_change_pages; p++)
9219 struct ElementChangeInfo *change = &element_info[element].change_page[p];
9221 if (change->can_change_or_has_action &&
9222 change->has_event[trigger_event] &&
9223 change->trigger_side & trigger_side &&
9224 change->trigger_player & trigger_player &&
9225 change->trigger_page & trigger_page_bits &&
9226 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
9228 change->actual_trigger_element = trigger_element;
9229 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
9230 change->actual_trigger_side = trigger_side;
9231 change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
9232 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
9234 if ((change->can_change && !change_done) || change->has_action)
9238 SCAN_PLAYFIELD(x, y)
9240 if (Feld[x][y] == element)
9242 if (change->can_change && !change_done)
9244 ChangeDelay[x][y] = 1;
9245 ChangeEvent[x][y] = trigger_event;
9247 HandleElementChange(x, y, p);
9249 #if USE_NEW_DELAYED_ACTION
9250 else if (change->has_action)
9252 ExecuteCustomElementAction(x, y, element, p);
9253 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
9256 if (change->has_action)
9258 ExecuteCustomElementAction(x, y, element, p);
9259 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
9265 if (change->can_change)
9268 change_done_any = TRUE;
9275 RECURSION_LOOP_DETECTION_END();
9277 return change_done_any;
9280 static boolean CheckElementChangeExt(int x, int y,
9282 int trigger_element,
9287 boolean change_done = FALSE;
9290 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
9291 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
9294 if (Feld[x][y] == EL_BLOCKED)
9296 Blocked2Moving(x, y, &x, &y);
9297 element = Feld[x][y];
9301 /* check if element has already changed */
9302 if (Feld[x][y] != element)
9305 /* check if element has already changed or is about to change after moving */
9306 if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
9307 Feld[x][y] != element) ||
9309 (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
9310 (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
9311 ChangePage[x][y] != -1)))
9316 printf("::: CheckElementChangeExt %d ... [%d, %d, %d, '%s']\n",
9317 trigger_event, recursion_loop_depth, recursion_loop_detected,
9318 recursion_loop_element, EL_NAME(recursion_loop_element));
9321 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
9323 for (p = 0; p < element_info[element].num_change_pages; p++)
9325 struct ElementChangeInfo *change = &element_info[element].change_page[p];
9327 /* check trigger element for all events where the element that is checked
9328 for changing interacts with a directly adjacent element -- this is
9329 different to element changes that affect other elements to change on the
9330 whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
9331 boolean check_trigger_element =
9332 (trigger_event == CE_TOUCHING_X ||
9333 trigger_event == CE_HITTING_X ||
9334 trigger_event == CE_HIT_BY_X ||
9336 /* this one was forgotten until 3.2.3 */
9337 trigger_event == CE_DIGGING_X);
9340 if (change->can_change_or_has_action &&
9341 change->has_event[trigger_event] &&
9342 change->trigger_side & trigger_side &&
9343 change->trigger_player & trigger_player &&
9344 (!check_trigger_element ||
9345 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
9347 change->actual_trigger_element = trigger_element;
9348 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
9349 change->actual_trigger_side = trigger_side;
9350 change->actual_trigger_ce_value = CustomValue[x][y];
9351 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
9353 /* special case: trigger element not at (x,y) position for some events */
9354 if (check_trigger_element)
9366 { 0, 0 }, { 0, 0 }, { 0, 0 },
9370 int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
9371 int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
9373 change->actual_trigger_ce_value = CustomValue[xx][yy];
9374 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
9377 if (change->can_change && !change_done)
9379 ChangeDelay[x][y] = 1;
9380 ChangeEvent[x][y] = trigger_event;
9382 HandleElementChange(x, y, p);
9386 #if USE_NEW_DELAYED_ACTION
9387 else if (change->has_action)
9389 ExecuteCustomElementAction(x, y, element, p);
9390 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
9393 if (change->has_action)
9395 ExecuteCustomElementAction(x, y, element, p);
9396 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
9402 RECURSION_LOOP_DETECTION_END();
9407 static void PlayPlayerSound(struct PlayerInfo *player)
9409 int jx = player->jx, jy = player->jy;
9410 int sound_element = player->artwork_element;
9411 int last_action = player->last_action_waiting;
9412 int action = player->action_waiting;
9414 if (player->is_waiting)
9416 if (action != last_action)
9417 PlayLevelSoundElementAction(jx, jy, sound_element, action);
9419 PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
9423 if (action != last_action)
9424 StopSound(element_info[sound_element].sound[last_action]);
9426 if (last_action == ACTION_SLEEPING)
9427 PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
9431 static void PlayAllPlayersSound()
9435 for (i = 0; i < MAX_PLAYERS; i++)
9436 if (stored_player[i].active)
9437 PlayPlayerSound(&stored_player[i]);
9440 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
9442 boolean last_waiting = player->is_waiting;
9443 int move_dir = player->MovDir;
9445 player->dir_waiting = move_dir;
9446 player->last_action_waiting = player->action_waiting;
9450 if (!last_waiting) /* not waiting -> waiting */
9452 player->is_waiting = TRUE;
9454 player->frame_counter_bored =
9456 game.player_boring_delay_fixed +
9457 GetSimpleRandom(game.player_boring_delay_random);
9458 player->frame_counter_sleeping =
9460 game.player_sleeping_delay_fixed +
9461 GetSimpleRandom(game.player_sleeping_delay_random);
9463 InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
9466 if (game.player_sleeping_delay_fixed +
9467 game.player_sleeping_delay_random > 0 &&
9468 player->anim_delay_counter == 0 &&
9469 player->post_delay_counter == 0 &&
9470 FrameCounter >= player->frame_counter_sleeping)
9471 player->is_sleeping = TRUE;
9472 else if (game.player_boring_delay_fixed +
9473 game.player_boring_delay_random > 0 &&
9474 FrameCounter >= player->frame_counter_bored)
9475 player->is_bored = TRUE;
9477 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
9478 player->is_bored ? ACTION_BORING :
9481 if (player->is_sleeping && player->use_murphy)
9483 /* special case for sleeping Murphy when leaning against non-free tile */
9485 if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
9486 (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
9487 !IS_MOVING(player->jx - 1, player->jy)))
9489 else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
9490 (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
9491 !IS_MOVING(player->jx + 1, player->jy)))
9492 move_dir = MV_RIGHT;
9494 player->is_sleeping = FALSE;
9496 player->dir_waiting = move_dir;
9499 if (player->is_sleeping)
9501 if (player->num_special_action_sleeping > 0)
9503 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
9505 int last_special_action = player->special_action_sleeping;
9506 int num_special_action = player->num_special_action_sleeping;
9507 int special_action =
9508 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
9509 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
9510 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
9511 last_special_action + 1 : ACTION_SLEEPING);
9512 int special_graphic =
9513 el_act_dir2img(player->artwork_element, special_action, move_dir);
9515 player->anim_delay_counter =
9516 graphic_info[special_graphic].anim_delay_fixed +
9517 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
9518 player->post_delay_counter =
9519 graphic_info[special_graphic].post_delay_fixed +
9520 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
9522 player->special_action_sleeping = special_action;
9525 if (player->anim_delay_counter > 0)
9527 player->action_waiting = player->special_action_sleeping;
9528 player->anim_delay_counter--;
9530 else if (player->post_delay_counter > 0)
9532 player->post_delay_counter--;
9536 else if (player->is_bored)
9538 if (player->num_special_action_bored > 0)
9540 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
9542 int special_action =
9543 ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
9544 int special_graphic =
9545 el_act_dir2img(player->artwork_element, special_action, move_dir);
9547 player->anim_delay_counter =
9548 graphic_info[special_graphic].anim_delay_fixed +
9549 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
9550 player->post_delay_counter =
9551 graphic_info[special_graphic].post_delay_fixed +
9552 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
9554 player->special_action_bored = special_action;
9557 if (player->anim_delay_counter > 0)
9559 player->action_waiting = player->special_action_bored;
9560 player->anim_delay_counter--;
9562 else if (player->post_delay_counter > 0)
9564 player->post_delay_counter--;
9569 else if (last_waiting) /* waiting -> not waiting */
9571 player->is_waiting = FALSE;
9572 player->is_bored = FALSE;
9573 player->is_sleeping = FALSE;
9575 player->frame_counter_bored = -1;
9576 player->frame_counter_sleeping = -1;
9578 player->anim_delay_counter = 0;
9579 player->post_delay_counter = 0;
9581 player->dir_waiting = player->MovDir;
9582 player->action_waiting = ACTION_DEFAULT;
9584 player->special_action_bored = ACTION_DEFAULT;
9585 player->special_action_sleeping = ACTION_DEFAULT;
9589 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
9591 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
9592 int left = player_action & JOY_LEFT;
9593 int right = player_action & JOY_RIGHT;
9594 int up = player_action & JOY_UP;
9595 int down = player_action & JOY_DOWN;
9596 int button1 = player_action & JOY_BUTTON_1;
9597 int button2 = player_action & JOY_BUTTON_2;
9598 int dx = (left ? -1 : right ? 1 : 0);
9599 int dy = (up ? -1 : down ? 1 : 0);
9601 if (!player->active || tape.pausing)
9607 snapped = SnapField(player, dx, dy);
9611 dropped = DropElement(player);
9613 moved = MovePlayer(player, dx, dy);
9616 if (tape.single_step && tape.recording && !tape.pausing)
9618 if (button1 || (dropped && !moved))
9620 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9621 SnapField(player, 0, 0); /* stop snapping */
9625 SetPlayerWaiting(player, FALSE);
9627 return player_action;
9631 /* no actions for this player (no input at player's configured device) */
9633 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
9634 SnapField(player, 0, 0);
9635 CheckGravityMovementWhenNotMoving(player);
9637 if (player->MovPos == 0)
9638 SetPlayerWaiting(player, TRUE);
9640 if (player->MovPos == 0) /* needed for tape.playing */
9641 player->is_moving = FALSE;
9643 player->is_dropping = FALSE;
9644 player->is_dropping_pressed = FALSE;
9645 player->drop_pressed_delay = 0;
9651 static void CheckLevelTime()
9655 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9657 if (level.native_em_level->lev->home == 0) /* all players at home */
9659 PlayerWins(local_player);
9661 AllPlayersGone = TRUE;
9663 level.native_em_level->lev->home = -1;
9666 if (level.native_em_level->ply[0]->alive == 0 &&
9667 level.native_em_level->ply[1]->alive == 0 &&
9668 level.native_em_level->ply[2]->alive == 0 &&
9669 level.native_em_level->ply[3]->alive == 0) /* all dead */
9670 AllPlayersGone = TRUE;
9673 if (TimeFrames >= FRAMES_PER_SECOND)
9678 for (i = 0; i < MAX_PLAYERS; i++)
9680 struct PlayerInfo *player = &stored_player[i];
9682 if (SHIELD_ON(player))
9684 player->shield_normal_time_left--;
9686 if (player->shield_deadly_time_left > 0)
9687 player->shield_deadly_time_left--;
9691 if (!local_player->LevelSolved && !level.use_step_counter)
9699 if (TimeLeft <= 10 && setup.time_limit)
9700 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
9702 DrawGameValue_Time(TimeLeft);
9704 if (!TimeLeft && setup.time_limit)
9706 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9707 level.native_em_level->lev->killed_out_of_time = TRUE;
9709 for (i = 0; i < MAX_PLAYERS; i++)
9710 KillPlayer(&stored_player[i]);
9713 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
9714 DrawGameValue_Time(TimePlayed);
9716 level.native_em_level->lev->time =
9717 (level.time == 0 ? TimePlayed : TimeLeft);
9720 if (tape.recording || tape.playing)
9721 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
9725 void AdvanceFrameAndPlayerCounters(int player_nr)
9729 /* advance frame counters (global frame counter and time frame counter) */
9733 /* advance player counters (counters for move delay, move animation etc.) */
9734 for (i = 0; i < MAX_PLAYERS; i++)
9736 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
9737 int move_delay_value = stored_player[i].move_delay_value;
9738 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
9740 if (!advance_player_counters) /* not all players may be affected */
9743 #if USE_NEW_PLAYER_ANIM
9744 if (move_frames == 0) /* less than one move per game frame */
9746 int stepsize = TILEX / move_delay_value;
9747 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
9748 int count = (stored_player[i].is_moving ?
9749 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
9751 if (count % delay == 0)
9756 stored_player[i].Frame += move_frames;
9758 if (stored_player[i].MovPos != 0)
9759 stored_player[i].StepFrame += move_frames;
9761 if (stored_player[i].move_delay > 0)
9762 stored_player[i].move_delay--;
9764 /* due to bugs in previous versions, counter must count up, not down */
9765 if (stored_player[i].push_delay != -1)
9766 stored_player[i].push_delay++;
9768 if (stored_player[i].drop_delay > 0)
9769 stored_player[i].drop_delay--;
9771 if (stored_player[i].is_dropping_pressed)
9772 stored_player[i].drop_pressed_delay++;
9776 void StartGameActions(boolean init_network_game, boolean record_tape,
9779 unsigned long new_random_seed = InitRND(random_seed);
9782 TapeStartRecording(new_random_seed);
9784 #if defined(NETWORK_AVALIABLE)
9785 if (init_network_game)
9787 SendToServer_StartPlaying();
9798 static unsigned long game_frame_delay = 0;
9799 unsigned long game_frame_delay_value;
9800 byte *recorded_player_action;
9801 byte summarized_player_action = 0;
9802 byte tape_action[MAX_PLAYERS];
9805 /* detect endless loops, caused by custom element programming */
9806 if (recursion_loop_detected && recursion_loop_depth == 0)
9808 char *message = getStringCat3("Internal Error ! Element ",
9809 EL_NAME(recursion_loop_element),
9810 " caused endless loop ! Quit the game ?");
9812 Error(ERR_WARN, "element '%s' caused endless loop in game engine",
9813 EL_NAME(recursion_loop_element));
9815 RequestQuitGameExt(FALSE, level_editor_test_game, message);
9817 recursion_loop_detected = FALSE; /* if game should be continued */
9824 if (game.restart_level)
9825 StartGameActions(options.network, setup.autorecord, NEW_RANDOMIZE);
9827 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9829 if (level.native_em_level->lev->home == 0) /* all players at home */
9831 PlayerWins(local_player);
9833 AllPlayersGone = TRUE;
9835 level.native_em_level->lev->home = -1;
9838 if (level.native_em_level->ply[0]->alive == 0 &&
9839 level.native_em_level->ply[1]->alive == 0 &&
9840 level.native_em_level->ply[2]->alive == 0 &&
9841 level.native_em_level->ply[3]->alive == 0) /* all dead */
9842 AllPlayersGone = TRUE;
9845 if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
9848 if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
9851 if (game_status != GAME_MODE_PLAYING) /* status might have changed */
9854 game_frame_delay_value =
9855 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
9857 if (tape.playing && tape.warp_forward && !tape.pausing)
9858 game_frame_delay_value = 0;
9860 /* ---------- main game synchronization point ---------- */
9862 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
9864 if (network_playing && !network_player_action_received)
9866 /* try to get network player actions in time */
9868 #if defined(NETWORK_AVALIABLE)
9869 /* last chance to get network player actions without main loop delay */
9873 /* game was quit by network peer */
9874 if (game_status != GAME_MODE_PLAYING)
9877 if (!network_player_action_received)
9878 return; /* failed to get network player actions in time */
9880 /* do not yet reset "network_player_action_received" (for tape.pausing) */
9886 /* at this point we know that we really continue executing the game */
9888 network_player_action_received = FALSE;
9890 /* when playing tape, read previously recorded player input from tape data */
9891 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
9894 /* TapePlayAction() may return NULL when toggling to "pause before death" */
9899 if (tape.set_centered_player)
9901 game.centered_player_nr_next = tape.centered_player_nr_next;
9902 game.set_centered_player = TRUE;
9905 for (i = 0; i < MAX_PLAYERS; i++)
9907 summarized_player_action |= stored_player[i].action;
9909 if (!network_playing)
9910 stored_player[i].effective_action = stored_player[i].action;
9913 #if defined(NETWORK_AVALIABLE)
9914 if (network_playing)
9915 SendToServer_MovePlayer(summarized_player_action);
9918 if (!options.network && !setup.team_mode)
9919 local_player->effective_action = summarized_player_action;
9921 if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
9923 for (i = 0; i < MAX_PLAYERS; i++)
9924 stored_player[i].effective_action =
9925 (i == game.centered_player_nr ? summarized_player_action : 0);
9928 if (recorded_player_action != NULL)
9929 for (i = 0; i < MAX_PLAYERS; i++)
9930 stored_player[i].effective_action = recorded_player_action[i];
9932 for (i = 0; i < MAX_PLAYERS; i++)
9934 tape_action[i] = stored_player[i].effective_action;
9936 /* (this can only happen in the R'n'D game engine) */
9937 if (tape.recording && tape_action[i] && !tape.player_participates[i])
9938 tape.player_participates[i] = TRUE; /* player just appeared from CE */
9941 /* only record actions from input devices, but not programmed actions */
9943 TapeRecordAction(tape_action);
9945 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9947 GameActions_EM_Main();
9955 void GameActions_EM_Main()
9957 byte effective_action[MAX_PLAYERS];
9958 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
9961 for (i = 0; i < MAX_PLAYERS; i++)
9962 effective_action[i] = stored_player[i].effective_action;
9964 GameActions_EM(effective_action, warp_mode);
9968 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
9971 void GameActions_RND()
9973 int magic_wall_x = 0, magic_wall_y = 0;
9974 int i, x, y, element, graphic;
9976 InitPlayfieldScanModeVars();
9978 #if USE_ONE_MORE_CHANGE_PER_FRAME
9979 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
9981 SCAN_PLAYFIELD(x, y)
9983 ChangeCount[x][y] = 0;
9984 ChangeEvent[x][y] = -1;
9989 if (game.set_centered_player)
9991 boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
9993 /* switching to "all players" only possible if all players fit to screen */
9994 if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
9996 game.centered_player_nr_next = game.centered_player_nr;
9997 game.set_centered_player = FALSE;
10000 /* do not switch focus to non-existing (or non-active) player */
10001 if (game.centered_player_nr_next >= 0 &&
10002 !stored_player[game.centered_player_nr_next].active)
10004 game.centered_player_nr_next = game.centered_player_nr;
10005 game.set_centered_player = FALSE;
10009 if (game.set_centered_player &&
10010 ScreenMovPos == 0) /* screen currently aligned at tile position */
10014 if (game.centered_player_nr_next == -1)
10016 setScreenCenteredToAllPlayers(&sx, &sy);
10020 sx = stored_player[game.centered_player_nr_next].jx;
10021 sy = stored_player[game.centered_player_nr_next].jy;
10024 game.centered_player_nr = game.centered_player_nr_next;
10025 game.set_centered_player = FALSE;
10027 DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
10028 DrawGameDoorValues();
10031 for (i = 0; i < MAX_PLAYERS; i++)
10033 int actual_player_action = stored_player[i].effective_action;
10036 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
10037 - rnd_equinox_tetrachloride 048
10038 - rnd_equinox_tetrachloride_ii 096
10039 - rnd_emanuel_schmieg 002
10040 - doctor_sloan_ww 001, 020
10042 if (stored_player[i].MovPos == 0)
10043 CheckGravityMovement(&stored_player[i]);
10046 /* overwrite programmed action with tape action */
10047 if (stored_player[i].programmed_action)
10048 actual_player_action = stored_player[i].programmed_action;
10050 PlayerActions(&stored_player[i], actual_player_action);
10052 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
10055 ScrollScreen(NULL, SCROLL_GO_ON);
10057 /* for backwards compatibility, the following code emulates a fixed bug that
10058 occured when pushing elements (causing elements that just made their last
10059 pushing step to already (if possible) make their first falling step in the
10060 same game frame, which is bad); this code is also needed to use the famous
10061 "spring push bug" which is used in older levels and might be wanted to be
10062 used also in newer levels, but in this case the buggy pushing code is only
10063 affecting the "spring" element and no other elements */
10065 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
10067 for (i = 0; i < MAX_PLAYERS; i++)
10069 struct PlayerInfo *player = &stored_player[i];
10070 int x = player->jx;
10071 int y = player->jy;
10073 if (player->active && player->is_pushing && player->is_moving &&
10075 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
10076 Feld[x][y] == EL_SPRING))
10078 ContinueMoving(x, y);
10080 /* continue moving after pushing (this is actually a bug) */
10081 if (!IS_MOVING(x, y))
10083 Stop[x][y] = FALSE;
10089 SCAN_PLAYFIELD(x, y)
10091 ChangeCount[x][y] = 0;
10092 ChangeEvent[x][y] = -1;
10094 /* this must be handled before main playfield loop */
10095 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
10098 if (MovDelay[x][y] <= 0)
10102 #if USE_NEW_SNAP_DELAY
10103 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
10106 if (MovDelay[x][y] <= 0)
10109 DrawLevelField(x, y);
10111 TestIfElementTouchesCustomElement(x, y); /* for empty space */
10117 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
10119 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
10120 printf("GameActions(): This should never happen!\n");
10122 ChangePage[x][y] = -1;
10126 Stop[x][y] = FALSE;
10127 if (WasJustMoving[x][y] > 0)
10128 WasJustMoving[x][y]--;
10129 if (WasJustFalling[x][y] > 0)
10130 WasJustFalling[x][y]--;
10131 if (CheckCollision[x][y] > 0)
10132 CheckCollision[x][y]--;
10133 if (CheckImpact[x][y] > 0)
10134 CheckImpact[x][y]--;
10138 /* reset finished pushing action (not done in ContinueMoving() to allow
10139 continuous pushing animation for elements with zero push delay) */
10140 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
10142 ResetGfxAnimation(x, y);
10143 DrawLevelField(x, y);
10147 if (IS_BLOCKED(x, y))
10151 Blocked2Moving(x, y, &oldx, &oldy);
10152 if (!IS_MOVING(oldx, oldy))
10154 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
10155 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
10156 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
10157 printf("GameActions(): This should never happen!\n");
10163 SCAN_PLAYFIELD(x, y)
10165 element = Feld[x][y];
10166 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10168 ResetGfxFrame(x, y, TRUE);
10170 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
10171 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
10172 ResetRandomAnimationValue(x, y);
10174 SetRandomAnimationValue(x, y);
10176 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
10178 if (IS_INACTIVE(element))
10180 if (IS_ANIMATED(graphic))
10181 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10186 /* this may take place after moving, so 'element' may have changed */
10187 if (IS_CHANGING(x, y) &&
10188 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
10190 int page = element_info[element].event_page_nr[CE_DELAY];
10193 HandleElementChange(x, y, page);
10195 if (CAN_CHANGE(element))
10196 HandleElementChange(x, y, page);
10198 if (HAS_ACTION(element))
10199 ExecuteCustomElementAction(x, y, element, page);
10202 element = Feld[x][y];
10203 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10206 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
10210 element = Feld[x][y];
10211 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10213 if (IS_ANIMATED(graphic) &&
10214 !IS_MOVING(x, y) &&
10216 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10218 if (IS_GEM(element) || element == EL_SP_INFOTRON)
10219 DrawTwinkleOnField(x, y);
10221 else if ((element == EL_ACID ||
10222 element == EL_EXIT_OPEN ||
10223 element == EL_EM_EXIT_OPEN ||
10224 element == EL_SP_EXIT_OPEN ||
10225 element == EL_STEEL_EXIT_OPEN ||
10226 element == EL_EM_STEEL_EXIT_OPEN ||
10227 element == EL_SP_TERMINAL ||
10228 element == EL_SP_TERMINAL_ACTIVE ||
10229 element == EL_EXTRA_TIME ||
10230 element == EL_SHIELD_NORMAL ||
10231 element == EL_SHIELD_DEADLY) &&
10232 IS_ANIMATED(graphic))
10233 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10234 else if (IS_MOVING(x, y))
10235 ContinueMoving(x, y);
10236 else if (IS_ACTIVE_BOMB(element))
10237 CheckDynamite(x, y);
10238 else if (element == EL_AMOEBA_GROWING)
10239 AmoebeWaechst(x, y);
10240 else if (element == EL_AMOEBA_SHRINKING)
10241 AmoebaDisappearing(x, y);
10243 #if !USE_NEW_AMOEBA_CODE
10244 else if (IS_AMOEBALIVE(element))
10245 AmoebeAbleger(x, y);
10248 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
10250 else if (element == EL_EXIT_CLOSED)
10252 else if (element == EL_EM_EXIT_CLOSED)
10254 else if (element == EL_STEEL_EXIT_CLOSED)
10255 CheckExitSteel(x, y);
10256 else if (element == EL_EM_STEEL_EXIT_CLOSED)
10257 CheckExitSteelEM(x, y);
10258 else if (element == EL_SP_EXIT_CLOSED)
10260 else if (element == EL_EXPANDABLE_WALL_GROWING ||
10261 element == EL_EXPANDABLE_STEELWALL_GROWING)
10262 MauerWaechst(x, y);
10263 else if (element == EL_EXPANDABLE_WALL ||
10264 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
10265 element == EL_EXPANDABLE_WALL_VERTICAL ||
10266 element == EL_EXPANDABLE_WALL_ANY ||
10267 element == EL_BD_EXPANDABLE_WALL)
10268 MauerAbleger(x, y);
10269 else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
10270 element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
10271 element == EL_EXPANDABLE_STEELWALL_ANY)
10272 MauerAblegerStahl(x, y);
10273 else if (element == EL_FLAMES)
10274 CheckForDragon(x, y);
10275 else if (element == EL_EXPLOSION)
10276 ; /* drawing of correct explosion animation is handled separately */
10277 else if (element == EL_ELEMENT_SNAPPING ||
10278 element == EL_DIAGONAL_SHRINKING ||
10279 element == EL_DIAGONAL_GROWING)
10281 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
10283 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10285 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
10286 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10288 if (IS_BELT_ACTIVE(element))
10289 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
10291 if (game.magic_wall_active)
10293 int jx = local_player->jx, jy = local_player->jy;
10295 /* play the element sound at the position nearest to the player */
10296 if ((element == EL_MAGIC_WALL_FULL ||
10297 element == EL_MAGIC_WALL_ACTIVE ||
10298 element == EL_MAGIC_WALL_EMPTYING ||
10299 element == EL_BD_MAGIC_WALL_FULL ||
10300 element == EL_BD_MAGIC_WALL_ACTIVE ||
10301 element == EL_BD_MAGIC_WALL_EMPTYING ||
10302 element == EL_DC_MAGIC_WALL_FULL ||
10303 element == EL_DC_MAGIC_WALL_ACTIVE ||
10304 element == EL_DC_MAGIC_WALL_EMPTYING) &&
10305 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
10313 #if USE_NEW_AMOEBA_CODE
10314 /* new experimental amoeba growth stuff */
10315 if (!(FrameCounter % 8))
10317 static unsigned long random = 1684108901;
10319 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
10321 x = RND(lev_fieldx);
10322 y = RND(lev_fieldy);
10323 element = Feld[x][y];
10325 if (!IS_PLAYER(x,y) &&
10326 (element == EL_EMPTY ||
10327 CAN_GROW_INTO(element) ||
10328 element == EL_QUICKSAND_EMPTY ||
10329 element == EL_QUICKSAND_FAST_EMPTY ||
10330 element == EL_ACID_SPLASH_LEFT ||
10331 element == EL_ACID_SPLASH_RIGHT))
10333 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
10334 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
10335 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
10336 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
10337 Feld[x][y] = EL_AMOEBA_DROP;
10340 random = random * 129 + 1;
10346 if (game.explosions_delayed)
10349 game.explosions_delayed = FALSE;
10351 SCAN_PLAYFIELD(x, y)
10353 element = Feld[x][y];
10355 if (ExplodeField[x][y])
10356 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
10357 else if (element == EL_EXPLOSION)
10358 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
10360 ExplodeField[x][y] = EX_TYPE_NONE;
10363 game.explosions_delayed = TRUE;
10366 if (game.magic_wall_active)
10368 if (!(game.magic_wall_time_left % 4))
10370 int element = Feld[magic_wall_x][magic_wall_y];
10372 if (element == EL_BD_MAGIC_WALL_FULL ||
10373 element == EL_BD_MAGIC_WALL_ACTIVE ||
10374 element == EL_BD_MAGIC_WALL_EMPTYING)
10375 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
10376 else if (element == EL_DC_MAGIC_WALL_FULL ||
10377 element == EL_DC_MAGIC_WALL_ACTIVE ||
10378 element == EL_DC_MAGIC_WALL_EMPTYING)
10379 PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
10381 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
10384 if (game.magic_wall_time_left > 0)
10386 game.magic_wall_time_left--;
10387 if (!game.magic_wall_time_left)
10389 SCAN_PLAYFIELD(x, y)
10391 element = Feld[x][y];
10393 if (element == EL_MAGIC_WALL_ACTIVE ||
10394 element == EL_MAGIC_WALL_FULL)
10396 Feld[x][y] = EL_MAGIC_WALL_DEAD;
10397 DrawLevelField(x, y);
10399 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
10400 element == EL_BD_MAGIC_WALL_FULL)
10402 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
10403 DrawLevelField(x, y);
10405 else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
10406 element == EL_DC_MAGIC_WALL_FULL)
10408 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
10409 DrawLevelField(x, y);
10413 game.magic_wall_active = FALSE;
10418 if (game.light_time_left > 0)
10420 game.light_time_left--;
10422 if (game.light_time_left == 0)
10423 RedrawAllLightSwitchesAndInvisibleElements();
10426 if (game.timegate_time_left > 0)
10428 game.timegate_time_left--;
10430 if (game.timegate_time_left == 0)
10431 CloseAllOpenTimegates();
10434 if (game.lenses_time_left > 0)
10436 game.lenses_time_left--;
10438 if (game.lenses_time_left == 0)
10439 RedrawAllInvisibleElementsForLenses();
10442 if (game.magnify_time_left > 0)
10444 game.magnify_time_left--;
10446 if (game.magnify_time_left == 0)
10447 RedrawAllInvisibleElementsForMagnifier();
10450 for (i = 0; i < MAX_PLAYERS; i++)
10452 struct PlayerInfo *player = &stored_player[i];
10454 if (SHIELD_ON(player))
10456 if (player->shield_deadly_time_left)
10457 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
10458 else if (player->shield_normal_time_left)
10459 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
10466 PlayAllPlayersSound();
10468 if (options.debug) /* calculate frames per second */
10470 static unsigned long fps_counter = 0;
10471 static int fps_frames = 0;
10472 unsigned long fps_delay_ms = Counter() - fps_counter;
10476 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
10478 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
10481 fps_counter = Counter();
10484 redraw_mask |= REDRAW_FPS;
10487 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
10489 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
10491 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
10493 local_player->show_envelope = 0;
10496 /* use random number generator in every frame to make it less predictable */
10497 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
10501 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
10503 int min_x = x, min_y = y, max_x = x, max_y = y;
10506 for (i = 0; i < MAX_PLAYERS; i++)
10508 int jx = stored_player[i].jx, jy = stored_player[i].jy;
10510 if (!stored_player[i].active || &stored_player[i] == player)
10513 min_x = MIN(min_x, jx);
10514 min_y = MIN(min_y, jy);
10515 max_x = MAX(max_x, jx);
10516 max_y = MAX(max_y, jy);
10519 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
10522 static boolean AllPlayersInVisibleScreen()
10526 for (i = 0; i < MAX_PLAYERS; i++)
10528 int jx = stored_player[i].jx, jy = stored_player[i].jy;
10530 if (!stored_player[i].active)
10533 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
10540 void ScrollLevel(int dx, int dy)
10543 static Bitmap *bitmap_db_field2 = NULL;
10544 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
10550 /* only horizontal XOR vertical scroll direction allowed */
10551 if ((dx == 0 && dy == 0) || (dx != 0 && dy != 0))
10555 if (bitmap_db_field2 == NULL)
10556 bitmap_db_field2 = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH);
10558 BlitBitmap(drawto_field, bitmap_db_field2,
10559 FX + TILEX * (dx == -1) - softscroll_offset,
10560 FY + TILEY * (dy == -1) - softscroll_offset,
10561 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
10562 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
10563 FX + TILEX * (dx == 1) - softscroll_offset,
10564 FY + TILEY * (dy == 1) - softscroll_offset);
10565 BlitBitmap(bitmap_db_field2, drawto_field,
10566 FX + TILEX * (dx == 1) - softscroll_offset,
10567 FY + TILEY * (dy == 1) - softscroll_offset,
10568 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
10569 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
10570 FX + TILEX * (dx == 1) - softscroll_offset,
10571 FY + TILEY * (dy == 1) - softscroll_offset);
10576 int xsize = (BX2 - BX1 + 1);
10577 int ysize = (BY2 - BY1 + 1);
10578 int start = (dx != 0 ? (dx == -1 ? BX1 : BX2) : (dy == -1 ? BY1 : BY2));
10579 int end = (dx != 0 ? (dx == -1 ? BX2 : BX1) : (dy == -1 ? BY2 : BY1));
10580 int step = (start < end ? +1 : -1);
10582 for (i = start; i != end; i += step)
10584 BlitBitmap(drawto_field, drawto_field,
10585 FX + TILEX * (dx != 0 ? i + step : 0),
10586 FY + TILEY * (dy != 0 ? i + step : 0),
10587 TILEX * (dx != 0 ? 1 : xsize),
10588 TILEY * (dy != 0 ? 1 : ysize),
10589 FX + TILEX * (dx != 0 ? i : 0),
10590 FY + TILEY * (dy != 0 ? i : 0));
10595 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
10597 BlitBitmap(drawto_field, drawto_field,
10598 FX + TILEX * (dx == -1) - softscroll_offset,
10599 FY + TILEY * (dy == -1) - softscroll_offset,
10600 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
10601 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
10602 FX + TILEX * (dx == 1) - softscroll_offset,
10603 FY + TILEY * (dy == 1) - softscroll_offset);
10609 x = (dx == 1 ? BX1 : BX2);
10610 for (y = BY1; y <= BY2; y++)
10611 DrawScreenField(x, y);
10616 y = (dy == 1 ? BY1 : BY2);
10617 for (x = BX1; x <= BX2; x++)
10618 DrawScreenField(x, y);
10621 redraw_mask |= REDRAW_FIELD;
10624 static boolean canFallDown(struct PlayerInfo *player)
10626 int jx = player->jx, jy = player->jy;
10628 return (IN_LEV_FIELD(jx, jy + 1) &&
10629 (IS_FREE(jx, jy + 1) ||
10630 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
10631 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
10632 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
10635 static boolean canPassField(int x, int y, int move_dir)
10637 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
10638 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
10639 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
10640 int nextx = x + dx;
10641 int nexty = y + dy;
10642 int element = Feld[x][y];
10644 return (IS_PASSABLE_FROM(element, opposite_dir) &&
10645 !CAN_MOVE(element) &&
10646 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
10647 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
10648 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
10651 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
10653 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
10654 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
10655 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
10659 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
10660 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
10661 (IS_DIGGABLE(Feld[newx][newy]) ||
10662 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
10663 canPassField(newx, newy, move_dir)));
10666 static void CheckGravityMovement(struct PlayerInfo *player)
10668 #if USE_PLAYER_GRAVITY
10669 if (player->gravity && !player->programmed_action)
10671 if (game.gravity && !player->programmed_action)
10674 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
10675 int move_dir_vertical = player->effective_action & MV_VERTICAL;
10676 boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
10677 int jx = player->jx, jy = player->jy;
10678 boolean player_is_moving_to_valid_field =
10679 (!player_is_snapping &&
10680 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
10681 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
10682 boolean player_can_fall_down = canFallDown(player);
10684 if (player_can_fall_down &&
10685 !player_is_moving_to_valid_field)
10686 player->programmed_action = MV_DOWN;
10690 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
10692 return CheckGravityMovement(player);
10694 #if USE_PLAYER_GRAVITY
10695 if (player->gravity && !player->programmed_action)
10697 if (game.gravity && !player->programmed_action)
10700 int jx = player->jx, jy = player->jy;
10701 boolean field_under_player_is_free =
10702 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
10703 boolean player_is_standing_on_valid_field =
10704 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
10705 (IS_WALKABLE(Feld[jx][jy]) &&
10706 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
10708 if (field_under_player_is_free && !player_is_standing_on_valid_field)
10709 player->programmed_action = MV_DOWN;
10714 MovePlayerOneStep()
10715 -----------------------------------------------------------------------------
10716 dx, dy: direction (non-diagonal) to try to move the player to
10717 real_dx, real_dy: direction as read from input device (can be diagonal)
10720 boolean MovePlayerOneStep(struct PlayerInfo *player,
10721 int dx, int dy, int real_dx, int real_dy)
10723 int jx = player->jx, jy = player->jy;
10724 int new_jx = jx + dx, new_jy = jy + dy;
10725 #if !USE_FIXED_DONT_RUN_INTO
10729 boolean player_can_move = !player->cannot_move;
10731 if (!player->active || (!dx && !dy))
10732 return MP_NO_ACTION;
10734 player->MovDir = (dx < 0 ? MV_LEFT :
10735 dx > 0 ? MV_RIGHT :
10737 dy > 0 ? MV_DOWN : MV_NONE);
10739 if (!IN_LEV_FIELD(new_jx, new_jy))
10740 return MP_NO_ACTION;
10742 if (!player_can_move)
10744 if (player->MovPos == 0)
10746 player->is_moving = FALSE;
10747 player->is_digging = FALSE;
10748 player->is_collecting = FALSE;
10749 player->is_snapping = FALSE;
10750 player->is_pushing = FALSE;
10755 if (!options.network && game.centered_player_nr == -1 &&
10756 !AllPlayersInSight(player, new_jx, new_jy))
10757 return MP_NO_ACTION;
10759 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
10760 return MP_NO_ACTION;
10763 #if !USE_FIXED_DONT_RUN_INTO
10764 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
10766 /* (moved to DigField()) */
10767 if (player_can_move && DONT_RUN_INTO(element))
10769 if (element == EL_ACID && dx == 0 && dy == 1)
10771 SplashAcid(new_jx, new_jy);
10772 Feld[jx][jy] = EL_PLAYER_1;
10773 InitMovingField(jx, jy, MV_DOWN);
10774 Store[jx][jy] = EL_ACID;
10775 ContinueMoving(jx, jy);
10776 BuryPlayer(player);
10779 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
10785 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
10786 if (can_move != MP_MOVING)
10789 /* check if DigField() has caused relocation of the player */
10790 if (player->jx != jx || player->jy != jy)
10791 return MP_NO_ACTION; /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
10793 StorePlayer[jx][jy] = 0;
10794 player->last_jx = jx;
10795 player->last_jy = jy;
10796 player->jx = new_jx;
10797 player->jy = new_jy;
10798 StorePlayer[new_jx][new_jy] = player->element_nr;
10800 if (player->move_delay_value_next != -1)
10802 player->move_delay_value = player->move_delay_value_next;
10803 player->move_delay_value_next = -1;
10807 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
10809 player->step_counter++;
10811 PlayerVisit[jx][jy] = FrameCounter;
10813 #if USE_UFAST_PLAYER_EXIT_BUGFIX
10814 player->is_moving = TRUE;
10818 /* should better be called in MovePlayer(), but this breaks some tapes */
10819 ScrollPlayer(player, SCROLL_INIT);
10825 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
10827 int jx = player->jx, jy = player->jy;
10828 int old_jx = jx, old_jy = jy;
10829 int moved = MP_NO_ACTION;
10831 if (!player->active)
10836 if (player->MovPos == 0)
10838 player->is_moving = FALSE;
10839 player->is_digging = FALSE;
10840 player->is_collecting = FALSE;
10841 player->is_snapping = FALSE;
10842 player->is_pushing = FALSE;
10848 if (player->move_delay > 0)
10851 player->move_delay = -1; /* set to "uninitialized" value */
10853 /* store if player is automatically moved to next field */
10854 player->is_auto_moving = (player->programmed_action != MV_NONE);
10856 /* remove the last programmed player action */
10857 player->programmed_action = 0;
10859 if (player->MovPos)
10861 /* should only happen if pre-1.2 tape recordings are played */
10862 /* this is only for backward compatibility */
10864 int original_move_delay_value = player->move_delay_value;
10867 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
10871 /* scroll remaining steps with finest movement resolution */
10872 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
10874 while (player->MovPos)
10876 ScrollPlayer(player, SCROLL_GO_ON);
10877 ScrollScreen(NULL, SCROLL_GO_ON);
10879 AdvanceFrameAndPlayerCounters(player->index_nr);
10885 player->move_delay_value = original_move_delay_value;
10888 player->is_active = FALSE;
10890 if (player->last_move_dir & MV_HORIZONTAL)
10892 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
10893 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
10897 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
10898 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
10901 #if USE_FIXED_BORDER_RUNNING_GFX
10902 if (!moved && !player->is_active)
10904 player->is_moving = FALSE;
10905 player->is_digging = FALSE;
10906 player->is_collecting = FALSE;
10907 player->is_snapping = FALSE;
10908 player->is_pushing = FALSE;
10916 if (moved & MP_MOVING && !ScreenMovPos &&
10917 (player->index_nr == game.centered_player_nr ||
10918 game.centered_player_nr == -1))
10920 if (moved & MP_MOVING && !ScreenMovPos &&
10921 (player == local_player || !options.network))
10924 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
10925 int offset = (setup.scroll_delay ? 3 : 0);
10927 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
10929 /* actual player has left the screen -- scroll in that direction */
10930 if (jx != old_jx) /* player has moved horizontally */
10931 scroll_x += (jx - old_jx);
10932 else /* player has moved vertically */
10933 scroll_y += (jy - old_jy);
10937 if (jx != old_jx) /* player has moved horizontally */
10939 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
10940 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
10941 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
10943 /* don't scroll over playfield boundaries */
10944 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
10945 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
10947 /* don't scroll more than one field at a time */
10948 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
10950 /* don't scroll against the player's moving direction */
10951 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
10952 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
10953 scroll_x = old_scroll_x;
10955 else /* player has moved vertically */
10957 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
10958 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
10959 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
10961 /* don't scroll over playfield boundaries */
10962 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
10963 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
10965 /* don't scroll more than one field at a time */
10966 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
10968 /* don't scroll against the player's moving direction */
10969 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
10970 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
10971 scroll_y = old_scroll_y;
10975 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
10978 if (!options.network && game.centered_player_nr == -1 &&
10979 !AllPlayersInVisibleScreen())
10981 scroll_x = old_scroll_x;
10982 scroll_y = old_scroll_y;
10986 if (!options.network && !AllPlayersInVisibleScreen())
10988 scroll_x = old_scroll_x;
10989 scroll_y = old_scroll_y;
10994 ScrollScreen(player, SCROLL_INIT);
10995 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
11000 player->StepFrame = 0;
11002 if (moved & MP_MOVING)
11004 if (old_jx != jx && old_jy == jy)
11005 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
11006 else if (old_jx == jx && old_jy != jy)
11007 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
11009 DrawLevelField(jx, jy); /* for "crumbled sand" */
11011 player->last_move_dir = player->MovDir;
11012 player->is_moving = TRUE;
11013 player->is_snapping = FALSE;
11014 player->is_switching = FALSE;
11015 player->is_dropping = FALSE;
11016 player->is_dropping_pressed = FALSE;
11017 player->drop_pressed_delay = 0;
11020 /* should better be called here than above, but this breaks some tapes */
11021 ScrollPlayer(player, SCROLL_INIT);
11026 CheckGravityMovementWhenNotMoving(player);
11028 player->is_moving = FALSE;
11030 /* at this point, the player is allowed to move, but cannot move right now
11031 (e.g. because of something blocking the way) -- ensure that the player
11032 is also allowed to move in the next frame (in old versions before 3.1.1,
11033 the player was forced to wait again for eight frames before next try) */
11035 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
11036 player->move_delay = 0; /* allow direct movement in the next frame */
11039 if (player->move_delay == -1) /* not yet initialized by DigField() */
11040 player->move_delay = player->move_delay_value;
11042 if (game.engine_version < VERSION_IDENT(3,0,7,0))
11044 TestIfPlayerTouchesBadThing(jx, jy);
11045 TestIfPlayerTouchesCustomElement(jx, jy);
11048 if (!player->active)
11049 RemovePlayer(player);
11054 void ScrollPlayer(struct PlayerInfo *player, int mode)
11056 int jx = player->jx, jy = player->jy;
11057 int last_jx = player->last_jx, last_jy = player->last_jy;
11058 int move_stepsize = TILEX / player->move_delay_value;
11060 #if USE_NEW_PLAYER_SPEED
11061 if (!player->active)
11064 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
11067 if (!player->active || player->MovPos == 0)
11071 if (mode == SCROLL_INIT)
11073 player->actual_frame_counter = FrameCounter;
11074 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
11076 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
11077 Feld[last_jx][last_jy] == EL_EMPTY)
11079 int last_field_block_delay = 0; /* start with no blocking at all */
11080 int block_delay_adjustment = player->block_delay_adjustment;
11082 /* if player blocks last field, add delay for exactly one move */
11083 if (player->block_last_field)
11085 last_field_block_delay += player->move_delay_value;
11087 /* when blocking enabled, prevent moving up despite gravity */
11088 #if USE_PLAYER_GRAVITY
11089 if (player->gravity && player->MovDir == MV_UP)
11090 block_delay_adjustment = -1;
11092 if (game.gravity && player->MovDir == MV_UP)
11093 block_delay_adjustment = -1;
11097 /* add block delay adjustment (also possible when not blocking) */
11098 last_field_block_delay += block_delay_adjustment;
11100 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
11101 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
11104 #if USE_NEW_PLAYER_SPEED
11105 if (player->MovPos != 0) /* player has not yet reached destination */
11111 else if (!FrameReached(&player->actual_frame_counter, 1))
11114 #if USE_NEW_PLAYER_SPEED
11115 if (player->MovPos != 0)
11117 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
11118 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
11120 /* before DrawPlayer() to draw correct player graphic for this case */
11121 if (player->MovPos == 0)
11122 CheckGravityMovement(player);
11125 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
11126 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
11128 /* before DrawPlayer() to draw correct player graphic for this case */
11129 if (player->MovPos == 0)
11130 CheckGravityMovement(player);
11133 if (player->MovPos == 0) /* player reached destination field */
11135 if (player->move_delay_reset_counter > 0)
11137 player->move_delay_reset_counter--;
11139 if (player->move_delay_reset_counter == 0)
11141 /* continue with normal speed after quickly moving through gate */
11142 HALVE_PLAYER_SPEED(player);
11144 /* be able to make the next move without delay */
11145 player->move_delay = 0;
11149 player->last_jx = jx;
11150 player->last_jy = jy;
11152 if (Feld[jx][jy] == EL_EXIT_OPEN ||
11153 Feld[jx][jy] == EL_EM_EXIT_OPEN ||
11154 Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
11155 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
11156 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
11157 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
11159 DrawPlayer(player); /* needed here only to cleanup last field */
11160 RemovePlayer(player);
11162 if (local_player->friends_still_needed == 0 ||
11163 IS_SP_ELEMENT(Feld[jx][jy]))
11164 PlayerWins(player);
11167 /* this breaks one level: "machine", level 000 */
11169 int move_direction = player->MovDir;
11170 int enter_side = MV_DIR_OPPOSITE(move_direction);
11171 int leave_side = move_direction;
11172 int old_jx = last_jx;
11173 int old_jy = last_jy;
11174 int old_element = Feld[old_jx][old_jy];
11175 int new_element = Feld[jx][jy];
11177 if (IS_CUSTOM_ELEMENT(old_element))
11178 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
11180 player->index_bit, leave_side);
11182 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
11183 CE_PLAYER_LEAVES_X,
11184 player->index_bit, leave_side);
11186 if (IS_CUSTOM_ELEMENT(new_element))
11187 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
11188 player->index_bit, enter_side);
11190 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
11191 CE_PLAYER_ENTERS_X,
11192 player->index_bit, enter_side);
11194 CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
11195 CE_MOVE_OF_X, move_direction);
11198 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
11200 TestIfPlayerTouchesBadThing(jx, jy);
11201 TestIfPlayerTouchesCustomElement(jx, jy);
11203 /* needed because pushed element has not yet reached its destination,
11204 so it would trigger a change event at its previous field location */
11205 if (!player->is_pushing)
11206 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
11208 if (!player->active)
11209 RemovePlayer(player);
11212 if (!local_player->LevelSolved && level.use_step_counter)
11222 if (TimeLeft <= 10 && setup.time_limit)
11223 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11225 DrawGameValue_Time(TimeLeft);
11227 if (!TimeLeft && setup.time_limit)
11228 for (i = 0; i < MAX_PLAYERS; i++)
11229 KillPlayer(&stored_player[i]);
11231 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
11232 DrawGameValue_Time(TimePlayed);
11235 if (tape.single_step && tape.recording && !tape.pausing &&
11236 !player->programmed_action)
11237 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
11241 void ScrollScreen(struct PlayerInfo *player, int mode)
11243 static unsigned long screen_frame_counter = 0;
11245 if (mode == SCROLL_INIT)
11247 /* set scrolling step size according to actual player's moving speed */
11248 ScrollStepSize = TILEX / player->move_delay_value;
11250 screen_frame_counter = FrameCounter;
11251 ScreenMovDir = player->MovDir;
11252 ScreenMovPos = player->MovPos;
11253 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
11256 else if (!FrameReached(&screen_frame_counter, 1))
11261 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
11262 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
11263 redraw_mask |= REDRAW_FIELD;
11266 ScreenMovDir = MV_NONE;
11269 void TestIfPlayerTouchesCustomElement(int x, int y)
11271 static int xy[4][2] =
11278 static int trigger_sides[4][2] =
11280 /* center side border side */
11281 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
11282 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
11283 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
11284 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
11286 static int touch_dir[4] =
11288 MV_LEFT | MV_RIGHT,
11293 int center_element = Feld[x][y]; /* should always be non-moving! */
11296 for (i = 0; i < NUM_DIRECTIONS; i++)
11298 int xx = x + xy[i][0];
11299 int yy = y + xy[i][1];
11300 int center_side = trigger_sides[i][0];
11301 int border_side = trigger_sides[i][1];
11302 int border_element;
11304 if (!IN_LEV_FIELD(xx, yy))
11307 if (IS_PLAYER(x, y))
11309 struct PlayerInfo *player = PLAYERINFO(x, y);
11311 if (game.engine_version < VERSION_IDENT(3,0,7,0))
11312 border_element = Feld[xx][yy]; /* may be moving! */
11313 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
11314 border_element = Feld[xx][yy];
11315 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
11316 border_element = MovingOrBlocked2Element(xx, yy);
11318 continue; /* center and border element do not touch */
11320 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
11321 player->index_bit, border_side);
11322 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
11323 CE_PLAYER_TOUCHES_X,
11324 player->index_bit, border_side);
11326 else if (IS_PLAYER(xx, yy))
11328 struct PlayerInfo *player = PLAYERINFO(xx, yy);
11330 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
11332 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
11333 continue; /* center and border element do not touch */
11336 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
11337 player->index_bit, center_side);
11338 CheckTriggeredElementChangeByPlayer(x, y, center_element,
11339 CE_PLAYER_TOUCHES_X,
11340 player->index_bit, center_side);
11346 #if USE_ELEMENT_TOUCHING_BUGFIX
11348 void TestIfElementTouchesCustomElement(int x, int y)
11350 static int xy[4][2] =
11357 static int trigger_sides[4][2] =
11359 /* center side border side */
11360 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
11361 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
11362 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
11363 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
11365 static int touch_dir[4] =
11367 MV_LEFT | MV_RIGHT,
11372 boolean change_center_element = FALSE;
11373 int center_element = Feld[x][y]; /* should always be non-moving! */
11374 int border_element_old[NUM_DIRECTIONS];
11377 for (i = 0; i < NUM_DIRECTIONS; i++)
11379 int xx = x + xy[i][0];
11380 int yy = y + xy[i][1];
11381 int border_element;
11383 border_element_old[i] = -1;
11385 if (!IN_LEV_FIELD(xx, yy))
11388 if (game.engine_version < VERSION_IDENT(3,0,7,0))
11389 border_element = Feld[xx][yy]; /* may be moving! */
11390 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
11391 border_element = Feld[xx][yy];
11392 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
11393 border_element = MovingOrBlocked2Element(xx, yy);
11395 continue; /* center and border element do not touch */
11397 border_element_old[i] = border_element;
11400 for (i = 0; i < NUM_DIRECTIONS; i++)
11402 int xx = x + xy[i][0];
11403 int yy = y + xy[i][1];
11404 int center_side = trigger_sides[i][0];
11405 int border_element = border_element_old[i];
11407 if (border_element == -1)
11410 /* check for change of border element */
11411 CheckElementChangeBySide(xx, yy, border_element, center_element,
11412 CE_TOUCHING_X, center_side);
11415 for (i = 0; i < NUM_DIRECTIONS; i++)
11417 int border_side = trigger_sides[i][1];
11418 int border_element = border_element_old[i];
11420 if (border_element == -1)
11423 /* check for change of center element (but change it only once) */
11424 if (!change_center_element)
11425 change_center_element =
11426 CheckElementChangeBySide(x, y, center_element, border_element,
11427 CE_TOUCHING_X, border_side);
11433 void TestIfElementTouchesCustomElement_OLD(int x, int y)
11435 static int xy[4][2] =
11442 static int trigger_sides[4][2] =
11444 /* center side border side */
11445 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
11446 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
11447 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
11448 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
11450 static int touch_dir[4] =
11452 MV_LEFT | MV_RIGHT,
11457 boolean change_center_element = FALSE;
11458 int center_element = Feld[x][y]; /* should always be non-moving! */
11461 for (i = 0; i < NUM_DIRECTIONS; i++)
11463 int xx = x + xy[i][0];
11464 int yy = y + xy[i][1];
11465 int center_side = trigger_sides[i][0];
11466 int border_side = trigger_sides[i][1];
11467 int border_element;
11469 if (!IN_LEV_FIELD(xx, yy))
11472 if (game.engine_version < VERSION_IDENT(3,0,7,0))
11473 border_element = Feld[xx][yy]; /* may be moving! */
11474 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
11475 border_element = Feld[xx][yy];
11476 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
11477 border_element = MovingOrBlocked2Element(xx, yy);
11479 continue; /* center and border element do not touch */
11481 /* check for change of center element (but change it only once) */
11482 if (!change_center_element)
11483 change_center_element =
11484 CheckElementChangeBySide(x, y, center_element, border_element,
11485 CE_TOUCHING_X, border_side);
11487 /* check for change of border element */
11488 CheckElementChangeBySide(xx, yy, border_element, center_element,
11489 CE_TOUCHING_X, center_side);
11495 void TestIfElementHitsCustomElement(int x, int y, int direction)
11497 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
11498 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
11499 int hitx = x + dx, hity = y + dy;
11500 int hitting_element = Feld[x][y];
11501 int touched_element;
11503 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
11506 touched_element = (IN_LEV_FIELD(hitx, hity) ?
11507 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
11509 if (IN_LEV_FIELD(hitx, hity))
11511 int opposite_direction = MV_DIR_OPPOSITE(direction);
11512 int hitting_side = direction;
11513 int touched_side = opposite_direction;
11514 boolean object_hit = (!IS_MOVING(hitx, hity) ||
11515 MovDir[hitx][hity] != direction ||
11516 ABS(MovPos[hitx][hity]) <= TILEY / 2);
11522 CheckElementChangeBySide(x, y, hitting_element, touched_element,
11523 CE_HITTING_X, touched_side);
11525 CheckElementChangeBySide(hitx, hity, touched_element,
11526 hitting_element, CE_HIT_BY_X, hitting_side);
11528 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
11529 CE_HIT_BY_SOMETHING, opposite_direction);
11533 /* "hitting something" is also true when hitting the playfield border */
11534 CheckElementChangeBySide(x, y, hitting_element, touched_element,
11535 CE_HITTING_SOMETHING, direction);
11539 void TestIfElementSmashesCustomElement(int x, int y, int direction)
11541 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
11542 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
11543 int hitx = x + dx, hity = y + dy;
11544 int hitting_element = Feld[x][y];
11545 int touched_element;
11547 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
11548 !IS_FREE(hitx, hity) &&
11549 (!IS_MOVING(hitx, hity) ||
11550 MovDir[hitx][hity] != direction ||
11551 ABS(MovPos[hitx][hity]) <= TILEY / 2));
11554 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
11558 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
11562 touched_element = (IN_LEV_FIELD(hitx, hity) ?
11563 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
11565 CheckElementChangeBySide(x, y, hitting_element, touched_element,
11566 EP_CAN_SMASH_EVERYTHING, direction);
11568 if (IN_LEV_FIELD(hitx, hity))
11570 int opposite_direction = MV_DIR_OPPOSITE(direction);
11571 int hitting_side = direction;
11572 int touched_side = opposite_direction;
11574 int touched_element = MovingOrBlocked2Element(hitx, hity);
11577 boolean object_hit = (!IS_MOVING(hitx, hity) ||
11578 MovDir[hitx][hity] != direction ||
11579 ABS(MovPos[hitx][hity]) <= TILEY / 2);
11588 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
11589 CE_SMASHED_BY_SOMETHING, opposite_direction);
11591 CheckElementChangeBySide(x, y, hitting_element, touched_element,
11592 CE_OTHER_IS_SMASHING, touched_side);
11594 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
11595 CE_OTHER_GETS_SMASHED, hitting_side);
11601 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
11603 int i, kill_x = -1, kill_y = -1;
11605 int bad_element = -1;
11606 static int test_xy[4][2] =
11613 static int test_dir[4] =
11621 for (i = 0; i < NUM_DIRECTIONS; i++)
11623 int test_x, test_y, test_move_dir, test_element;
11625 test_x = good_x + test_xy[i][0];
11626 test_y = good_y + test_xy[i][1];
11628 if (!IN_LEV_FIELD(test_x, test_y))
11632 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
11634 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
11636 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
11637 2nd case: DONT_TOUCH style bad thing does not move away from good thing
11639 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
11640 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
11644 bad_element = test_element;
11650 if (kill_x != -1 || kill_y != -1)
11652 if (IS_PLAYER(good_x, good_y))
11654 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
11656 if (player->shield_deadly_time_left > 0 &&
11657 !IS_INDESTRUCTIBLE(bad_element))
11658 Bang(kill_x, kill_y);
11659 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
11660 KillPlayer(player);
11663 Bang(good_x, good_y);
11667 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
11669 int i, kill_x = -1, kill_y = -1;
11670 int bad_element = Feld[bad_x][bad_y];
11671 static int test_xy[4][2] =
11678 static int touch_dir[4] =
11680 MV_LEFT | MV_RIGHT,
11685 static int test_dir[4] =
11693 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
11696 for (i = 0; i < NUM_DIRECTIONS; i++)
11698 int test_x, test_y, test_move_dir, test_element;
11700 test_x = bad_x + test_xy[i][0];
11701 test_y = bad_y + test_xy[i][1];
11702 if (!IN_LEV_FIELD(test_x, test_y))
11706 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
11708 test_element = Feld[test_x][test_y];
11710 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
11711 2nd case: DONT_TOUCH style bad thing does not move away from good thing
11713 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
11714 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
11716 /* good thing is player or penguin that does not move away */
11717 if (IS_PLAYER(test_x, test_y))
11719 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
11721 if (bad_element == EL_ROBOT && player->is_moving)
11722 continue; /* robot does not kill player if he is moving */
11724 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
11726 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
11727 continue; /* center and border element do not touch */
11734 else if (test_element == EL_PENGUIN)
11743 if (kill_x != -1 || kill_y != -1)
11745 if (IS_PLAYER(kill_x, kill_y))
11747 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
11749 if (player->shield_deadly_time_left > 0 &&
11750 !IS_INDESTRUCTIBLE(bad_element))
11751 Bang(bad_x, bad_y);
11752 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
11753 KillPlayer(player);
11756 Bang(kill_x, kill_y);
11760 void TestIfPlayerTouchesBadThing(int x, int y)
11762 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
11765 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
11767 TestIfGoodThingHitsBadThing(x, y, move_dir);
11770 void TestIfBadThingTouchesPlayer(int x, int y)
11772 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
11775 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
11777 TestIfBadThingHitsGoodThing(x, y, move_dir);
11780 void TestIfFriendTouchesBadThing(int x, int y)
11782 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
11785 void TestIfBadThingTouchesFriend(int x, int y)
11787 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
11790 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
11792 int i, kill_x = bad_x, kill_y = bad_y;
11793 static int xy[4][2] =
11801 for (i = 0; i < NUM_DIRECTIONS; i++)
11805 x = bad_x + xy[i][0];
11806 y = bad_y + xy[i][1];
11807 if (!IN_LEV_FIELD(x, y))
11810 element = Feld[x][y];
11811 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
11812 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
11820 if (kill_x != bad_x || kill_y != bad_y)
11821 Bang(bad_x, bad_y);
11824 void KillPlayer(struct PlayerInfo *player)
11826 int jx = player->jx, jy = player->jy;
11828 if (!player->active)
11831 /* the following code was introduced to prevent an infinite loop when calling
11833 -> CheckTriggeredElementChangeExt()
11834 -> ExecuteCustomElementAction()
11836 -> (infinitely repeating the above sequence of function calls)
11837 which occurs when killing the player while having a CE with the setting
11838 "kill player X when explosion of <player X>"; the solution using a new
11839 field "player->killed" was chosen for backwards compatibility, although
11840 clever use of the fields "player->active" etc. would probably also work */
11842 if (player->killed)
11846 player->killed = TRUE;
11848 /* remove accessible field at the player's position */
11849 Feld[jx][jy] = EL_EMPTY;
11851 /* deactivate shield (else Bang()/Explode() would not work right) */
11852 player->shield_normal_time_left = 0;
11853 player->shield_deadly_time_left = 0;
11856 BuryPlayer(player);
11859 static void KillPlayerUnlessEnemyProtected(int x, int y)
11861 if (!PLAYER_ENEMY_PROTECTED(x, y))
11862 KillPlayer(PLAYERINFO(x, y));
11865 static void KillPlayerUnlessExplosionProtected(int x, int y)
11867 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
11868 KillPlayer(PLAYERINFO(x, y));
11871 void BuryPlayer(struct PlayerInfo *player)
11873 int jx = player->jx, jy = player->jy;
11875 if (!player->active)
11878 PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
11879 PlayLevelSound(jx, jy, SND_GAME_LOSING);
11881 player->GameOver = TRUE;
11882 RemovePlayer(player);
11885 void RemovePlayer(struct PlayerInfo *player)
11887 int jx = player->jx, jy = player->jy;
11888 int i, found = FALSE;
11890 player->present = FALSE;
11891 player->active = FALSE;
11893 if (!ExplodeField[jx][jy])
11894 StorePlayer[jx][jy] = 0;
11896 if (player->is_moving)
11897 DrawLevelField(player->last_jx, player->last_jy);
11899 for (i = 0; i < MAX_PLAYERS; i++)
11900 if (stored_player[i].active)
11904 AllPlayersGone = TRUE;
11910 #if USE_NEW_SNAP_DELAY
11911 static void setFieldForSnapping(int x, int y, int element, int direction)
11913 struct ElementInfo *ei = &element_info[element];
11914 int direction_bit = MV_DIR_TO_BIT(direction);
11915 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
11916 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
11917 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
11919 Feld[x][y] = EL_ELEMENT_SNAPPING;
11920 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
11922 ResetGfxAnimation(x, y);
11924 GfxElement[x][y] = element;
11925 GfxAction[x][y] = action;
11926 GfxDir[x][y] = direction;
11927 GfxFrame[x][y] = -1;
11932 =============================================================================
11933 checkDiagonalPushing()
11934 -----------------------------------------------------------------------------
11935 check if diagonal input device direction results in pushing of object
11936 (by checking if the alternative direction is walkable, diggable, ...)
11937 =============================================================================
11940 static boolean checkDiagonalPushing(struct PlayerInfo *player,
11941 int x, int y, int real_dx, int real_dy)
11943 int jx, jy, dx, dy, xx, yy;
11945 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
11948 /* diagonal direction: check alternative direction */
11953 xx = jx + (dx == 0 ? real_dx : 0);
11954 yy = jy + (dy == 0 ? real_dy : 0);
11956 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
11960 =============================================================================
11962 -----------------------------------------------------------------------------
11963 x, y: field next to player (non-diagonal) to try to dig to
11964 real_dx, real_dy: direction as read from input device (can be diagonal)
11965 =============================================================================
11968 int DigField(struct PlayerInfo *player,
11969 int oldx, int oldy, int x, int y,
11970 int real_dx, int real_dy, int mode)
11972 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
11973 boolean player_was_pushing = player->is_pushing;
11974 boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
11975 boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
11976 int jx = oldx, jy = oldy;
11977 int dx = x - jx, dy = y - jy;
11978 int nextx = x + dx, nexty = y + dy;
11979 int move_direction = (dx == -1 ? MV_LEFT :
11980 dx == +1 ? MV_RIGHT :
11982 dy == +1 ? MV_DOWN : MV_NONE);
11983 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
11984 int dig_side = MV_DIR_OPPOSITE(move_direction);
11985 int old_element = Feld[jx][jy];
11986 #if USE_FIXED_DONT_RUN_INTO
11987 int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
11993 if (is_player) /* function can also be called by EL_PENGUIN */
11995 if (player->MovPos == 0)
11997 player->is_digging = FALSE;
11998 player->is_collecting = FALSE;
12001 if (player->MovPos == 0) /* last pushing move finished */
12002 player->is_pushing = FALSE;
12004 if (mode == DF_NO_PUSH) /* player just stopped pushing */
12006 player->is_switching = FALSE;
12007 player->push_delay = -1;
12009 return MP_NO_ACTION;
12013 #if !USE_FIXED_DONT_RUN_INTO
12014 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
12015 return MP_NO_ACTION;
12018 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
12019 old_element = Back[jx][jy];
12021 /* in case of element dropped at player position, check background */
12022 else if (Back[jx][jy] != EL_EMPTY &&
12023 game.engine_version >= VERSION_IDENT(2,2,0,0))
12024 old_element = Back[jx][jy];
12026 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
12027 return MP_NO_ACTION; /* field has no opening in this direction */
12029 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
12030 return MP_NO_ACTION; /* field has no opening in this direction */
12032 #if USE_FIXED_DONT_RUN_INTO
12033 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
12037 Feld[jx][jy] = player->artwork_element;
12038 InitMovingField(jx, jy, MV_DOWN);
12039 Store[jx][jy] = EL_ACID;
12040 ContinueMoving(jx, jy);
12041 BuryPlayer(player);
12043 return MP_DONT_RUN_INTO;
12047 #if USE_FIXED_DONT_RUN_INTO
12048 if (player_can_move && DONT_RUN_INTO(element))
12050 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
12052 return MP_DONT_RUN_INTO;
12056 #if USE_FIXED_DONT_RUN_INTO
12057 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
12058 return MP_NO_ACTION;
12061 #if !USE_FIXED_DONT_RUN_INTO
12062 element = Feld[x][y];
12065 collect_count = element_info[element].collect_count_initial;
12067 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
12068 return MP_NO_ACTION;
12070 if (game.engine_version < VERSION_IDENT(2,2,0,0))
12071 player_can_move = player_can_move_or_snap;
12073 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
12074 game.engine_version >= VERSION_IDENT(2,2,0,0))
12076 CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
12077 player->index_bit, dig_side);
12078 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
12079 player->index_bit, dig_side);
12081 if (element == EL_DC_LANDMINE)
12084 if (Feld[x][y] != element) /* field changed by snapping */
12087 return MP_NO_ACTION;
12090 #if USE_PLAYER_GRAVITY
12091 if (player->gravity && is_player && !player->is_auto_moving &&
12092 canFallDown(player) && move_direction != MV_DOWN &&
12093 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
12094 return MP_NO_ACTION; /* player cannot walk here due to gravity */
12096 if (game.gravity && is_player && !player->is_auto_moving &&
12097 canFallDown(player) && move_direction != MV_DOWN &&
12098 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
12099 return MP_NO_ACTION; /* player cannot walk here due to gravity */
12102 if (player_can_move &&
12103 IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
12105 int sound_element = SND_ELEMENT(element);
12106 int sound_action = ACTION_WALKING;
12108 if (IS_RND_GATE(element))
12110 if (!player->key[RND_GATE_NR(element)])
12111 return MP_NO_ACTION;
12113 else if (IS_RND_GATE_GRAY(element))
12115 if (!player->key[RND_GATE_GRAY_NR(element)])
12116 return MP_NO_ACTION;
12118 else if (IS_RND_GATE_GRAY_ACTIVE(element))
12120 if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
12121 return MP_NO_ACTION;
12123 else if (element == EL_EXIT_OPEN ||
12124 element == EL_EM_EXIT_OPEN ||
12125 element == EL_STEEL_EXIT_OPEN ||
12126 element == EL_EM_STEEL_EXIT_OPEN ||
12127 element == EL_SP_EXIT_OPEN ||
12128 element == EL_SP_EXIT_OPENING)
12130 sound_action = ACTION_PASSING; /* player is passing exit */
12132 else if (element == EL_EMPTY)
12134 sound_action = ACTION_MOVING; /* nothing to walk on */
12137 /* play sound from background or player, whatever is available */
12138 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
12139 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
12141 PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
12143 else if (player_can_move &&
12144 IS_PASSABLE(element) && canPassField(x, y, move_direction))
12146 if (!ACCESS_FROM(element, opposite_direction))
12147 return MP_NO_ACTION; /* field not accessible from this direction */
12149 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
12150 return MP_NO_ACTION;
12152 if (IS_EM_GATE(element))
12154 if (!player->key[EM_GATE_NR(element)])
12155 return MP_NO_ACTION;
12157 else if (IS_EM_GATE_GRAY(element))
12159 if (!player->key[EM_GATE_GRAY_NR(element)])
12160 return MP_NO_ACTION;
12162 else if (IS_EM_GATE_GRAY_ACTIVE(element))
12164 if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
12165 return MP_NO_ACTION;
12167 else if (IS_EMC_GATE(element))
12169 if (!player->key[EMC_GATE_NR(element)])
12170 return MP_NO_ACTION;
12172 else if (IS_EMC_GATE_GRAY(element))
12174 if (!player->key[EMC_GATE_GRAY_NR(element)])
12175 return MP_NO_ACTION;
12177 else if (IS_EMC_GATE_GRAY_ACTIVE(element))
12179 if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
12180 return MP_NO_ACTION;
12182 else if (element == EL_DC_GATE_WHITE ||
12183 element == EL_DC_GATE_WHITE_GRAY ||
12184 element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
12186 if (player->num_white_keys == 0)
12187 return MP_NO_ACTION;
12189 player->num_white_keys--;
12191 else if (IS_SP_PORT(element))
12193 if (element == EL_SP_GRAVITY_PORT_LEFT ||
12194 element == EL_SP_GRAVITY_PORT_RIGHT ||
12195 element == EL_SP_GRAVITY_PORT_UP ||
12196 element == EL_SP_GRAVITY_PORT_DOWN)
12197 #if USE_PLAYER_GRAVITY
12198 player->gravity = !player->gravity;
12200 game.gravity = !game.gravity;
12202 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
12203 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
12204 element == EL_SP_GRAVITY_ON_PORT_UP ||
12205 element == EL_SP_GRAVITY_ON_PORT_DOWN)
12206 #if USE_PLAYER_GRAVITY
12207 player->gravity = TRUE;
12209 game.gravity = TRUE;
12211 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
12212 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
12213 element == EL_SP_GRAVITY_OFF_PORT_UP ||
12214 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
12215 #if USE_PLAYER_GRAVITY
12216 player->gravity = FALSE;
12218 game.gravity = FALSE;
12222 /* automatically move to the next field with double speed */
12223 player->programmed_action = move_direction;
12225 if (player->move_delay_reset_counter == 0)
12227 player->move_delay_reset_counter = 2; /* two double speed steps */
12229 DOUBLE_PLAYER_SPEED(player);
12232 PlayLevelSoundAction(x, y, ACTION_PASSING);
12234 else if (player_can_move_or_snap && IS_DIGGABLE(element))
12238 if (mode != DF_SNAP)
12240 GfxElement[x][y] = GFX_ELEMENT(element);
12241 player->is_digging = TRUE;
12244 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
12246 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
12247 player->index_bit, dig_side);
12249 if (mode == DF_SNAP)
12251 #if USE_NEW_SNAP_DELAY
12252 if (level.block_snap_field)
12253 setFieldForSnapping(x, y, element, move_direction);
12255 TestIfElementTouchesCustomElement(x, y); /* for empty space */
12257 TestIfElementTouchesCustomElement(x, y); /* for empty space */
12260 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
12261 player->index_bit, dig_side);
12264 else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
12268 if (is_player && mode != DF_SNAP)
12270 GfxElement[x][y] = element;
12271 player->is_collecting = TRUE;
12274 if (element == EL_SPEED_PILL)
12276 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
12278 else if (element == EL_EXTRA_TIME && level.time > 0)
12280 TimeLeft += level.extra_time;
12281 DrawGameValue_Time(TimeLeft);
12283 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
12285 player->shield_normal_time_left += level.shield_normal_time;
12286 if (element == EL_SHIELD_DEADLY)
12287 player->shield_deadly_time_left += level.shield_deadly_time;
12289 else if (element == EL_DYNAMITE ||
12290 element == EL_EM_DYNAMITE ||
12291 element == EL_SP_DISK_RED)
12293 if (player->inventory_size < MAX_INVENTORY_SIZE)
12294 player->inventory_element[player->inventory_size++] = element;
12296 DrawGameDoorValues();
12298 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
12300 player->dynabomb_count++;
12301 player->dynabombs_left++;
12303 else if (element == EL_DYNABOMB_INCREASE_SIZE)
12305 player->dynabomb_size++;
12307 else if (element == EL_DYNABOMB_INCREASE_POWER)
12309 player->dynabomb_xl = TRUE;
12311 else if (IS_KEY(element))
12313 player->key[KEY_NR(element)] = TRUE;
12315 DrawGameDoorValues();
12317 else if (element == EL_DC_KEY_WHITE)
12319 player->num_white_keys++;
12321 /* display white keys? */
12322 /* DrawGameDoorValues(); */
12324 else if (IS_ENVELOPE(element))
12326 player->show_envelope = element;
12328 else if (element == EL_EMC_LENSES)
12330 game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
12332 RedrawAllInvisibleElementsForLenses();
12334 else if (element == EL_EMC_MAGNIFIER)
12336 game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
12338 RedrawAllInvisibleElementsForMagnifier();
12340 else if (IS_DROPPABLE(element) ||
12341 IS_THROWABLE(element)) /* can be collected and dropped */
12345 if (collect_count == 0)
12346 player->inventory_infinite_element = element;
12348 for (i = 0; i < collect_count; i++)
12349 if (player->inventory_size < MAX_INVENTORY_SIZE)
12350 player->inventory_element[player->inventory_size++] = element;
12352 DrawGameDoorValues();
12354 else if (collect_count > 0)
12356 local_player->gems_still_needed -= collect_count;
12357 if (local_player->gems_still_needed < 0)
12358 local_player->gems_still_needed = 0;
12360 DrawGameValue_Emeralds(local_player->gems_still_needed);
12363 RaiseScoreElement(element);
12364 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
12367 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
12368 player->index_bit, dig_side);
12370 if (mode == DF_SNAP)
12372 #if USE_NEW_SNAP_DELAY
12373 if (level.block_snap_field)
12374 setFieldForSnapping(x, y, element, move_direction);
12376 TestIfElementTouchesCustomElement(x, y); /* for empty space */
12378 TestIfElementTouchesCustomElement(x, y); /* for empty space */
12381 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
12382 player->index_bit, dig_side);
12385 else if (player_can_move_or_snap && IS_PUSHABLE(element))
12387 if (mode == DF_SNAP && element != EL_BD_ROCK)
12388 return MP_NO_ACTION;
12390 if (CAN_FALL(element) && dy)
12391 return MP_NO_ACTION;
12393 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
12394 !(element == EL_SPRING && level.use_spring_bug))
12395 return MP_NO_ACTION;
12397 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
12398 ((move_direction & MV_VERTICAL &&
12399 ((element_info[element].move_pattern & MV_LEFT &&
12400 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
12401 (element_info[element].move_pattern & MV_RIGHT &&
12402 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
12403 (move_direction & MV_HORIZONTAL &&
12404 ((element_info[element].move_pattern & MV_UP &&
12405 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
12406 (element_info[element].move_pattern & MV_DOWN &&
12407 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
12408 return MP_NO_ACTION;
12410 /* do not push elements already moving away faster than player */
12411 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
12412 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
12413 return MP_NO_ACTION;
12415 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
12417 if (player->push_delay_value == -1 || !player_was_pushing)
12418 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12420 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
12422 if (player->push_delay_value == -1)
12423 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12425 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
12427 if (!player->is_pushing)
12428 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12431 player->is_pushing = TRUE;
12432 player->is_active = TRUE;
12434 if (!(IN_LEV_FIELD(nextx, nexty) &&
12435 (IS_FREE(nextx, nexty) ||
12436 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
12437 IS_SB_ELEMENT(element)))))
12438 return MP_NO_ACTION;
12440 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
12441 return MP_NO_ACTION;
12443 if (player->push_delay == -1) /* new pushing; restart delay */
12444 player->push_delay = 0;
12446 if (player->push_delay < player->push_delay_value &&
12447 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
12448 element != EL_SPRING && element != EL_BALLOON)
12450 /* make sure that there is no move delay before next try to push */
12451 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
12452 player->move_delay = 0;
12454 return MP_NO_ACTION;
12457 if (IS_SB_ELEMENT(element))
12459 if (element == EL_SOKOBAN_FIELD_FULL)
12461 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
12462 local_player->sokobanfields_still_needed++;
12465 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
12467 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
12468 local_player->sokobanfields_still_needed--;
12471 Feld[x][y] = EL_SOKOBAN_OBJECT;
12473 if (Back[x][y] == Back[nextx][nexty])
12474 PlayLevelSoundAction(x, y, ACTION_PUSHING);
12475 else if (Back[x][y] != 0)
12476 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
12479 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
12482 if (local_player->sokobanfields_still_needed == 0 &&
12483 game.emulation == EMU_SOKOBAN)
12485 PlayerWins(player);
12487 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
12491 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12493 InitMovingField(x, y, move_direction);
12494 GfxAction[x][y] = ACTION_PUSHING;
12496 if (mode == DF_SNAP)
12497 ContinueMoving(x, y);
12499 MovPos[x][y] = (dx != 0 ? dx : dy);
12501 Pushed[x][y] = TRUE;
12502 Pushed[nextx][nexty] = TRUE;
12504 if (game.engine_version < VERSION_IDENT(2,2,0,7))
12505 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12507 player->push_delay_value = -1; /* get new value later */
12509 /* check for element change _after_ element has been pushed */
12510 if (game.use_change_when_pushing_bug)
12512 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
12513 player->index_bit, dig_side);
12514 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
12515 player->index_bit, dig_side);
12518 else if (IS_SWITCHABLE(element))
12520 if (PLAYER_SWITCHING(player, x, y))
12522 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
12523 player->index_bit, dig_side);
12528 player->is_switching = TRUE;
12529 player->switch_x = x;
12530 player->switch_y = y;
12532 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
12534 if (element == EL_ROBOT_WHEEL)
12536 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
12540 DrawLevelField(x, y);
12542 else if (element == EL_SP_TERMINAL)
12546 SCAN_PLAYFIELD(xx, yy)
12548 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
12550 else if (Feld[xx][yy] == EL_SP_TERMINAL)
12551 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
12554 else if (IS_BELT_SWITCH(element))
12556 ToggleBeltSwitch(x, y);
12558 else if (element == EL_SWITCHGATE_SWITCH_UP ||
12559 element == EL_SWITCHGATE_SWITCH_DOWN ||
12560 element == EL_DC_SWITCHGATE_SWITCH_UP ||
12561 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
12563 ToggleSwitchgateSwitch(x, y);
12565 else if (element == EL_LIGHT_SWITCH ||
12566 element == EL_LIGHT_SWITCH_ACTIVE)
12568 ToggleLightSwitch(x, y);
12570 else if (element == EL_TIMEGATE_SWITCH ||
12571 element == EL_DC_TIMEGATE_SWITCH)
12573 ActivateTimegateSwitch(x, y);
12575 else if (element == EL_BALLOON_SWITCH_LEFT ||
12576 element == EL_BALLOON_SWITCH_RIGHT ||
12577 element == EL_BALLOON_SWITCH_UP ||
12578 element == EL_BALLOON_SWITCH_DOWN ||
12579 element == EL_BALLOON_SWITCH_NONE ||
12580 element == EL_BALLOON_SWITCH_ANY)
12582 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
12583 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
12584 element == EL_BALLOON_SWITCH_UP ? MV_UP :
12585 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
12586 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
12589 else if (element == EL_LAMP)
12591 Feld[x][y] = EL_LAMP_ACTIVE;
12592 local_player->lights_still_needed--;
12594 ResetGfxAnimation(x, y);
12595 DrawLevelField(x, y);
12597 else if (element == EL_TIME_ORB_FULL)
12599 Feld[x][y] = EL_TIME_ORB_EMPTY;
12601 if (level.time > 0 || level.use_time_orb_bug)
12603 TimeLeft += level.time_orb_time;
12604 DrawGameValue_Time(TimeLeft);
12607 ResetGfxAnimation(x, y);
12608 DrawLevelField(x, y);
12610 else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
12611 element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
12615 game.ball_state = !game.ball_state;
12617 SCAN_PLAYFIELD(xx, yy)
12619 int e = Feld[xx][yy];
12621 if (game.ball_state)
12623 if (e == EL_EMC_MAGIC_BALL)
12624 CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
12625 else if (e == EL_EMC_MAGIC_BALL_SWITCH)
12626 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
12630 if (e == EL_EMC_MAGIC_BALL_ACTIVE)
12631 CreateField(xx, yy, EL_EMC_MAGIC_BALL);
12632 else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
12633 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
12638 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
12639 player->index_bit, dig_side);
12641 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
12642 player->index_bit, dig_side);
12644 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
12645 player->index_bit, dig_side);
12651 if (!PLAYER_SWITCHING(player, x, y))
12653 player->is_switching = TRUE;
12654 player->switch_x = x;
12655 player->switch_y = y;
12657 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
12658 player->index_bit, dig_side);
12659 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
12660 player->index_bit, dig_side);
12662 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
12663 player->index_bit, dig_side);
12664 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
12665 player->index_bit, dig_side);
12668 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
12669 player->index_bit, dig_side);
12670 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
12671 player->index_bit, dig_side);
12673 return MP_NO_ACTION;
12676 player->push_delay = -1;
12678 if (is_player) /* function can also be called by EL_PENGUIN */
12680 if (Feld[x][y] != element) /* really digged/collected something */
12682 player->is_collecting = !player->is_digging;
12683 player->is_active = TRUE;
12690 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
12692 int jx = player->jx, jy = player->jy;
12693 int x = jx + dx, y = jy + dy;
12694 int snap_direction = (dx == -1 ? MV_LEFT :
12695 dx == +1 ? MV_RIGHT :
12697 dy == +1 ? MV_DOWN : MV_NONE);
12698 boolean can_continue_snapping = (level.continuous_snapping &&
12699 WasJustFalling[x][y] < CHECK_DELAY_FALLING);
12701 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
12704 if (!player->active || !IN_LEV_FIELD(x, y))
12712 if (player->MovPos == 0)
12713 player->is_pushing = FALSE;
12715 player->is_snapping = FALSE;
12717 if (player->MovPos == 0)
12719 player->is_moving = FALSE;
12720 player->is_digging = FALSE;
12721 player->is_collecting = FALSE;
12727 #if USE_NEW_CONTINUOUS_SNAPPING
12728 /* prevent snapping with already pressed snap key when not allowed */
12729 if (player->is_snapping && !can_continue_snapping)
12732 if (player->is_snapping)
12736 player->MovDir = snap_direction;
12738 if (player->MovPos == 0)
12740 player->is_moving = FALSE;
12741 player->is_digging = FALSE;
12742 player->is_collecting = FALSE;
12745 player->is_dropping = FALSE;
12746 player->is_dropping_pressed = FALSE;
12747 player->drop_pressed_delay = 0;
12749 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
12752 player->is_snapping = TRUE;
12753 player->is_active = TRUE;
12755 if (player->MovPos == 0)
12757 player->is_moving = FALSE;
12758 player->is_digging = FALSE;
12759 player->is_collecting = FALSE;
12762 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
12763 DrawLevelField(player->last_jx, player->last_jy);
12765 DrawLevelField(x, y);
12770 boolean DropElement(struct PlayerInfo *player)
12772 int old_element, new_element;
12773 int dropx = player->jx, dropy = player->jy;
12774 int drop_direction = player->MovDir;
12775 int drop_side = drop_direction;
12776 int drop_element = (player->inventory_size > 0 ?
12777 player->inventory_element[player->inventory_size - 1] :
12778 player->inventory_infinite_element != EL_UNDEFINED ?
12779 player->inventory_infinite_element :
12780 player->dynabombs_left > 0 ?
12781 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
12784 player->is_dropping_pressed = TRUE;
12786 /* do not drop an element on top of another element; when holding drop key
12787 pressed without moving, dropped element must move away before the next
12788 element can be dropped (this is especially important if the next element
12789 is dynamite, which can be placed on background for historical reasons) */
12790 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
12793 if (IS_THROWABLE(drop_element))
12795 dropx += GET_DX_FROM_DIR(drop_direction);
12796 dropy += GET_DY_FROM_DIR(drop_direction);
12798 if (!IN_LEV_FIELD(dropx, dropy))
12802 old_element = Feld[dropx][dropy]; /* old element at dropping position */
12803 new_element = drop_element; /* default: no change when dropping */
12805 /* check if player is active, not moving and ready to drop */
12806 if (!player->active || player->MovPos || player->drop_delay > 0)
12809 /* check if player has anything that can be dropped */
12810 if (new_element == EL_UNDEFINED)
12813 /* check if drop key was pressed long enough for EM style dynamite */
12814 if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
12817 /* check if anything can be dropped at the current position */
12818 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
12821 /* collected custom elements can only be dropped on empty fields */
12822 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
12825 if (old_element != EL_EMPTY)
12826 Back[dropx][dropy] = old_element; /* store old element on this field */
12828 ResetGfxAnimation(dropx, dropy);
12829 ResetRandomAnimationValue(dropx, dropy);
12831 if (player->inventory_size > 0 ||
12832 player->inventory_infinite_element != EL_UNDEFINED)
12834 if (player->inventory_size > 0)
12836 player->inventory_size--;
12838 DrawGameDoorValues();
12840 if (new_element == EL_DYNAMITE)
12841 new_element = EL_DYNAMITE_ACTIVE;
12842 else if (new_element == EL_EM_DYNAMITE)
12843 new_element = EL_EM_DYNAMITE_ACTIVE;
12844 else if (new_element == EL_SP_DISK_RED)
12845 new_element = EL_SP_DISK_RED_ACTIVE;
12848 Feld[dropx][dropy] = new_element;
12850 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
12851 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
12852 el2img(Feld[dropx][dropy]), 0);
12854 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
12856 /* needed if previous element just changed to "empty" in the last frame */
12857 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
12859 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
12860 player->index_bit, drop_side);
12861 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
12863 player->index_bit, drop_side);
12865 TestIfElementTouchesCustomElement(dropx, dropy);
12867 else /* player is dropping a dyna bomb */
12869 player->dynabombs_left--;
12871 Feld[dropx][dropy] = new_element;
12873 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
12874 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
12875 el2img(Feld[dropx][dropy]), 0);
12877 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
12880 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
12881 InitField_WithBug1(dropx, dropy, FALSE);
12883 new_element = Feld[dropx][dropy]; /* element might have changed */
12885 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
12886 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
12888 int move_direction, nextx, nexty;
12890 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
12891 MovDir[dropx][dropy] = drop_direction;
12893 move_direction = MovDir[dropx][dropy];
12894 nextx = dropx + GET_DX_FROM_DIR(move_direction);
12895 nexty = dropy + GET_DY_FROM_DIR(move_direction);
12897 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
12899 #if USE_FIX_IMPACT_COLLISION
12900 /* do not cause impact style collision by dropping elements that can fall */
12901 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
12903 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
12907 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
12908 player->is_dropping = TRUE;
12910 player->drop_pressed_delay = 0;
12911 player->is_dropping_pressed = FALSE;
12913 player->drop_x = dropx;
12914 player->drop_y = dropy;
12919 /* ------------------------------------------------------------------------- */
12920 /* game sound playing functions */
12921 /* ------------------------------------------------------------------------- */
12923 static int *loop_sound_frame = NULL;
12924 static int *loop_sound_volume = NULL;
12926 void InitPlayLevelSound()
12928 int num_sounds = getSoundListSize();
12930 checked_free(loop_sound_frame);
12931 checked_free(loop_sound_volume);
12933 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
12934 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
12937 static void PlayLevelSound(int x, int y, int nr)
12939 int sx = SCREENX(x), sy = SCREENY(y);
12940 int volume, stereo_position;
12941 int max_distance = 8;
12942 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
12944 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
12945 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
12948 if (!IN_LEV_FIELD(x, y) ||
12949 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
12950 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
12953 volume = SOUND_MAX_VOLUME;
12955 if (!IN_SCR_FIELD(sx, sy))
12957 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
12958 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
12960 volume -= volume * (dx > dy ? dx : dy) / max_distance;
12963 stereo_position = (SOUND_MAX_LEFT +
12964 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
12965 (SCR_FIELDX + 2 * max_distance));
12967 if (IS_LOOP_SOUND(nr))
12969 /* This assures that quieter loop sounds do not overwrite louder ones,
12970 while restarting sound volume comparison with each new game frame. */
12972 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
12975 loop_sound_volume[nr] = volume;
12976 loop_sound_frame[nr] = FrameCounter;
12979 PlaySoundExt(nr, volume, stereo_position, type);
12982 static void PlayLevelSoundNearest(int x, int y, int sound_action)
12984 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
12985 x > LEVELX(BX2) ? LEVELX(BX2) : x,
12986 y < LEVELY(BY1) ? LEVELY(BY1) :
12987 y > LEVELY(BY2) ? LEVELY(BY2) : y,
12991 static void PlayLevelSoundAction(int x, int y, int action)
12993 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
12996 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
12998 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
13000 if (sound_effect != SND_UNDEFINED)
13001 PlayLevelSound(x, y, sound_effect);
13004 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
13007 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
13009 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
13010 PlayLevelSound(x, y, sound_effect);
13013 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
13015 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
13017 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
13018 PlayLevelSound(x, y, sound_effect);
13021 static void StopLevelSoundActionIfLoop(int x, int y, int action)
13023 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
13025 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
13026 StopSound(sound_effect);
13029 static void PlayLevelMusic()
13031 if (levelset.music[level_nr] != MUS_UNDEFINED)
13032 PlayMusic(levelset.music[level_nr]); /* from config file */
13034 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
13037 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
13039 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
13040 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
13041 int x = xx - 1 - offset;
13042 int y = yy - 1 - offset;
13047 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
13051 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13055 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
13059 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
13063 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
13067 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
13071 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
13074 case SAMPLE_android_clone:
13075 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
13078 case SAMPLE_android_move:
13079 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
13082 case SAMPLE_spring:
13083 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
13087 PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
13091 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
13094 case SAMPLE_eater_eat:
13095 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13099 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
13102 case SAMPLE_collect:
13103 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13106 case SAMPLE_diamond:
13107 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
13110 case SAMPLE_squash:
13111 /* !!! CHECK THIS !!! */
13113 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
13115 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
13119 case SAMPLE_wonderfall:
13120 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
13124 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
13128 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13132 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13136 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
13140 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
13144 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
13147 case SAMPLE_wonder:
13148 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
13152 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
13155 case SAMPLE_exit_open:
13156 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
13159 case SAMPLE_exit_leave:
13160 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
13163 case SAMPLE_dynamite:
13164 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
13168 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
13172 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
13176 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
13180 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
13184 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
13188 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
13192 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
13198 void ChangeTime(int value)
13200 int *time = (level.time == 0 ? &TimePlayed : &TimeLeft);
13204 /* EMC game engine uses value from time counter of RND game engine */
13205 level.native_em_level->lev->time = *time;
13207 DrawGameValue_Time(*time);
13210 void RaiseScore(int value)
13212 /* EMC game engine and RND game engine have separate score counters */
13213 int *score = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
13214 &level.native_em_level->lev->score : &local_player->score);
13218 DrawGameValue_Score(*score);
13222 void RaiseScore(int value)
13224 local_player->score += value;
13226 DrawGameValue_Score(local_player->score);
13229 void RaiseScoreElement(int element)
13234 case EL_BD_DIAMOND:
13235 case EL_EMERALD_YELLOW:
13236 case EL_EMERALD_RED:
13237 case EL_EMERALD_PURPLE:
13238 case EL_SP_INFOTRON:
13239 RaiseScore(level.score[SC_EMERALD]);
13242 RaiseScore(level.score[SC_DIAMOND]);
13245 RaiseScore(level.score[SC_CRYSTAL]);
13248 RaiseScore(level.score[SC_PEARL]);
13251 case EL_BD_BUTTERFLY:
13252 case EL_SP_ELECTRON:
13253 RaiseScore(level.score[SC_BUG]);
13256 case EL_BD_FIREFLY:
13257 case EL_SP_SNIKSNAK:
13258 RaiseScore(level.score[SC_SPACESHIP]);
13261 case EL_DARK_YAMYAM:
13262 RaiseScore(level.score[SC_YAMYAM]);
13265 RaiseScore(level.score[SC_ROBOT]);
13268 RaiseScore(level.score[SC_PACMAN]);
13271 RaiseScore(level.score[SC_NUT]);
13274 case EL_EM_DYNAMITE:
13275 case EL_SP_DISK_RED:
13276 case EL_DYNABOMB_INCREASE_NUMBER:
13277 case EL_DYNABOMB_INCREASE_SIZE:
13278 case EL_DYNABOMB_INCREASE_POWER:
13279 RaiseScore(level.score[SC_DYNAMITE]);
13281 case EL_SHIELD_NORMAL:
13282 case EL_SHIELD_DEADLY:
13283 RaiseScore(level.score[SC_SHIELD]);
13285 case EL_EXTRA_TIME:
13286 RaiseScore(level.extra_time_score);
13300 case EL_DC_KEY_WHITE:
13301 RaiseScore(level.score[SC_KEY]);
13304 RaiseScore(element_info[element].collect_score);
13309 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
13311 if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
13313 #if defined(NETWORK_AVALIABLE)
13314 if (options.network)
13315 SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
13321 game_status = GAME_MODE_MAIN;
13327 FadeOut(REDRAW_FIELD);
13329 game_status = GAME_MODE_MAIN;
13331 DrawAndFadeInMainMenu(REDRAW_FIELD);
13335 else /* continue playing the game */
13337 if (tape.playing && tape.deactivate_display)
13338 TapeDeactivateDisplayOff(TRUE);
13340 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
13342 if (tape.playing && tape.deactivate_display)
13343 TapeDeactivateDisplayOn();
13347 void RequestQuitGame(boolean ask_if_really_quit)
13349 boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
13350 boolean skip_request = AllPlayersGone || quick_quit;
13352 RequestQuitGameExt(skip_request, quick_quit,
13353 "Do you really want to quit the game ?");
13357 /* ------------------------------------------------------------------------- */
13358 /* random generator functions */
13359 /* ------------------------------------------------------------------------- */
13361 unsigned int InitEngineRandom_RND(long seed)
13363 game.num_random_calls = 0;
13366 unsigned int rnd_seed = InitEngineRandom(seed);
13368 printf("::: START RND: %d\n", rnd_seed);
13373 return InitEngineRandom(seed);
13379 unsigned int RND(int max)
13383 game.num_random_calls++;
13385 return GetEngineRandom(max);
13392 /* ------------------------------------------------------------------------- */
13393 /* game engine snapshot handling functions */
13394 /* ------------------------------------------------------------------------- */
13396 #define ARGS_ADDRESS_AND_SIZEOF(x) (&(x)), (sizeof(x))
13398 struct EngineSnapshotInfo
13400 /* runtime values for custom element collect score */
13401 int collect_score[NUM_CUSTOM_ELEMENTS];
13403 /* runtime values for group element choice position */
13404 int choice_pos[NUM_GROUP_ELEMENTS];
13406 /* runtime values for belt position animations */
13407 int belt_graphic[4 * NUM_BELT_PARTS];
13408 int belt_anim_mode[4 * NUM_BELT_PARTS];
13411 struct EngineSnapshotNodeInfo
13418 static struct EngineSnapshotInfo engine_snapshot_rnd;
13419 static ListNode *engine_snapshot_list = NULL;
13420 static char *snapshot_level_identifier = NULL;
13421 static int snapshot_level_nr = -1;
13423 void FreeEngineSnapshot()
13425 while (engine_snapshot_list != NULL)
13426 deleteNodeFromList(&engine_snapshot_list, engine_snapshot_list->key,
13429 setString(&snapshot_level_identifier, NULL);
13430 snapshot_level_nr = -1;
13433 static void SaveEngineSnapshotValues_RND()
13435 static int belt_base_active_element[4] =
13437 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
13438 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
13439 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
13440 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
13444 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
13446 int element = EL_CUSTOM_START + i;
13448 engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
13451 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
13453 int element = EL_GROUP_START + i;
13455 engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
13458 for (i = 0; i < 4; i++)
13460 for (j = 0; j < NUM_BELT_PARTS; j++)
13462 int element = belt_base_active_element[i] + j;
13463 int graphic = el2img(element);
13464 int anim_mode = graphic_info[graphic].anim_mode;
13466 engine_snapshot_rnd.belt_graphic[i * 4 + j] = graphic;
13467 engine_snapshot_rnd.belt_anim_mode[i * 4 + j] = anim_mode;
13472 static void LoadEngineSnapshotValues_RND()
13474 unsigned long num_random_calls = game.num_random_calls;
13477 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
13479 int element = EL_CUSTOM_START + i;
13481 element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
13484 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
13486 int element = EL_GROUP_START + i;
13488 element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
13491 for (i = 0; i < 4; i++)
13493 for (j = 0; j < NUM_BELT_PARTS; j++)
13495 int graphic = engine_snapshot_rnd.belt_graphic[i * 4 + j];
13496 int anim_mode = engine_snapshot_rnd.belt_anim_mode[i * 4 + j];
13498 graphic_info[graphic].anim_mode = anim_mode;
13502 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
13504 InitRND(tape.random_seed);
13505 for (i = 0; i < num_random_calls; i++)
13509 if (game.num_random_calls != num_random_calls)
13511 Error(ERR_RETURN, "number of random calls out of sync");
13512 Error(ERR_RETURN, "number of random calls should be %d", num_random_calls);
13513 Error(ERR_RETURN, "number of random calls is %d", game.num_random_calls);
13514 Error(ERR_EXIT, "this should not happen -- please debug");
13518 static void SaveEngineSnapshotBuffer(void *buffer, int size)
13520 struct EngineSnapshotNodeInfo *bi =
13521 checked_calloc(sizeof(struct EngineSnapshotNodeInfo));
13523 bi->buffer_orig = buffer;
13524 bi->buffer_copy = checked_malloc(size);
13527 memcpy(bi->buffer_copy, buffer, size);
13529 addNodeToList(&engine_snapshot_list, NULL, bi);
13532 void SaveEngineSnapshot()
13534 FreeEngineSnapshot(); /* free previous snapshot, if needed */
13536 if (level_editor_test_game) /* do not save snapshots from editor */
13539 /* copy some special values to a structure better suited for the snapshot */
13541 SaveEngineSnapshotValues_RND();
13542 SaveEngineSnapshotValues_EM();
13544 /* save values stored in special snapshot structure */
13546 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
13547 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
13549 /* save further RND engine values */
13551 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
13552 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
13553 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
13555 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
13556 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
13557 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
13558 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
13560 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
13561 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
13562 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
13563 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
13564 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
13566 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
13567 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
13568 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
13570 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
13572 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
13574 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
13575 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
13577 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
13578 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
13579 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
13580 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
13581 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
13582 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
13583 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
13584 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
13585 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
13586 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
13587 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
13588 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
13589 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
13590 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
13591 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
13592 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
13593 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
13594 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
13596 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
13597 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
13599 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
13600 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
13601 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
13603 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
13604 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
13606 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
13607 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
13608 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
13609 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
13610 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
13612 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
13613 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
13615 /* save level identification information */
13617 setString(&snapshot_level_identifier, leveldir_current->identifier);
13618 snapshot_level_nr = level_nr;
13621 ListNode *node = engine_snapshot_list;
13624 while (node != NULL)
13626 num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
13631 printf("::: size of engine snapshot: %d bytes\n", num_bytes);
13635 static void LoadEngineSnapshotBuffer(struct EngineSnapshotNodeInfo *bi)
13637 memcpy(bi->buffer_orig, bi->buffer_copy, bi->size);
13640 void LoadEngineSnapshot()
13642 ListNode *node = engine_snapshot_list;
13644 if (engine_snapshot_list == NULL)
13647 while (node != NULL)
13649 LoadEngineSnapshotBuffer((struct EngineSnapshotNodeInfo *)node->content);
13654 /* restore special values from snapshot structure */
13656 LoadEngineSnapshotValues_RND();
13657 LoadEngineSnapshotValues_EM();
13660 boolean CheckEngineSnapshot()
13662 return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
13663 snapshot_level_nr == level_nr);
13667 /* ---------- new game button stuff ---------------------------------------- */
13669 /* graphic position values for game buttons */
13670 #define GAME_BUTTON_XSIZE 30
13671 #define GAME_BUTTON_YSIZE 30
13672 #define GAME_BUTTON_XPOS 5
13673 #define GAME_BUTTON_YPOS 215
13674 #define SOUND_BUTTON_XPOS 5
13675 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
13677 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
13678 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
13679 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
13680 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
13681 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
13682 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
13689 } gamebutton_info[NUM_GAME_BUTTONS] =
13692 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
13697 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
13698 GAME_CTRL_ID_PAUSE,
13702 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
13707 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
13708 SOUND_CTRL_ID_MUSIC,
13709 "background music on/off"
13712 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
13713 SOUND_CTRL_ID_LOOPS,
13714 "sound loops on/off"
13717 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
13718 SOUND_CTRL_ID_SIMPLE,
13719 "normal sounds on/off"
13723 void CreateGameButtons()
13727 for (i = 0; i < NUM_GAME_BUTTONS; i++)
13729 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
13730 struct GadgetInfo *gi;
13733 unsigned long event_mask;
13734 int gd_xoffset, gd_yoffset;
13735 int gd_x1, gd_x2, gd_y1, gd_y2;
13738 gd_xoffset = gamebutton_info[i].x;
13739 gd_yoffset = gamebutton_info[i].y;
13740 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
13741 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
13743 if (id == GAME_CTRL_ID_STOP ||
13744 id == GAME_CTRL_ID_PAUSE ||
13745 id == GAME_CTRL_ID_PLAY)
13747 button_type = GD_TYPE_NORMAL_BUTTON;
13749 event_mask = GD_EVENT_RELEASED;
13750 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
13751 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
13755 button_type = GD_TYPE_CHECK_BUTTON;
13757 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
13758 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
13759 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
13760 event_mask = GD_EVENT_PRESSED;
13761 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
13762 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
13765 gi = CreateGadget(GDI_CUSTOM_ID, id,
13766 GDI_INFO_TEXT, gamebutton_info[i].infotext,
13767 GDI_X, DX + gd_xoffset,
13768 GDI_Y, DY + gd_yoffset,
13769 GDI_WIDTH, GAME_BUTTON_XSIZE,
13770 GDI_HEIGHT, GAME_BUTTON_YSIZE,
13771 GDI_TYPE, button_type,
13772 GDI_STATE, GD_BUTTON_UNPRESSED,
13773 GDI_CHECKED, checked,
13774 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
13775 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
13776 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
13777 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
13778 GDI_EVENT_MASK, event_mask,
13779 GDI_CALLBACK_ACTION, HandleGameButtons,
13783 Error(ERR_EXIT, "cannot create gadget");
13785 game_gadget[id] = gi;
13789 void FreeGameButtons()
13793 for (i = 0; i < NUM_GAME_BUTTONS; i++)
13794 FreeGadget(game_gadget[i]);
13797 static void MapGameButtons()
13801 for (i = 0; i < NUM_GAME_BUTTONS; i++)
13802 MapGadget(game_gadget[i]);
13805 void UnmapGameButtons()
13809 for (i = 0; i < NUM_GAME_BUTTONS; i++)
13810 UnmapGadget(game_gadget[i]);
13813 static void HandleGameButtons(struct GadgetInfo *gi)
13815 int id = gi->custom_id;
13817 if (game_status != GAME_MODE_PLAYING)
13822 case GAME_CTRL_ID_STOP:
13826 RequestQuitGame(TRUE);
13829 case GAME_CTRL_ID_PAUSE:
13830 if (options.network)
13832 #if defined(NETWORK_AVALIABLE)
13834 SendToServer_ContinuePlaying();
13836 SendToServer_PausePlaying();
13840 TapeTogglePause(TAPE_TOGGLE_MANUAL);
13843 case GAME_CTRL_ID_PLAY:
13846 #if defined(NETWORK_AVALIABLE)
13847 if (options.network)
13848 SendToServer_ContinuePlaying();
13852 tape.pausing = FALSE;
13853 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
13858 case SOUND_CTRL_ID_MUSIC:
13859 if (setup.sound_music)
13861 setup.sound_music = FALSE;
13864 else if (audio.music_available)
13866 setup.sound = setup.sound_music = TRUE;
13868 SetAudioMode(setup.sound);
13874 case SOUND_CTRL_ID_LOOPS:
13875 if (setup.sound_loops)
13876 setup.sound_loops = FALSE;
13877 else if (audio.loops_available)
13879 setup.sound = setup.sound_loops = TRUE;
13880 SetAudioMode(setup.sound);
13884 case SOUND_CTRL_ID_SIMPLE:
13885 if (setup.sound_simple)
13886 setup.sound_simple = FALSE;
13887 else if (audio.sound_available)
13889 setup.sound = setup.sound_simple = TRUE;
13890 SetAudioMode(setup.sound);