1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back! *
3 *----------------------------------------------------------*
4 * (c) 1995-2006 Artsoft Entertainment *
6 * Detmolder Strasse 189 *
9 * e-mail: info@artsoft.org *
10 *----------------------------------------------------------*
12 ***********************************************************/
14 #include "libgame/libgame.h"
24 /* EXPERIMENTAL STUFF */
25 #define USE_NEW_AMOEBA_CODE FALSE
27 /* EXPERIMENTAL STUFF */
28 #define USE_NEW_STUFF ( 1)
30 #define USE_NEW_SP_SLIPPERY (USE_NEW_STUFF * 1)
31 #define USE_NEW_CUSTOM_VALUE (USE_NEW_STUFF * 1)
32 #define USE_NEW_PLAYER_ANIM (USE_NEW_STUFF * 1)
33 #define USE_NEW_ALL_SLIPPERY (USE_NEW_STUFF * 1)
34 #define USE_NEW_PLAYER_SPEED (USE_NEW_STUFF * 1)
35 #define USE_NEW_DELAYED_ACTION (USE_NEW_STUFF * 1)
36 #define USE_NEW_SNAP_DELAY (USE_NEW_STUFF * 1)
37 #define USE_ONLY_ONE_CHANGE_PER_FRAME (USE_NEW_STUFF * 1)
38 #define USE_ONE_MORE_CHANGE_PER_FRAME (USE_NEW_STUFF * 1)
39 #define USE_FIXED_DONT_RUN_INTO (USE_NEW_STUFF * 1)
40 #define USE_NEW_SPRING_BUMPER (USE_NEW_STUFF * 1)
41 #define USE_STOP_CHANGED_ELEMENTS (USE_NEW_STUFF * 1)
42 #define USE_ELEMENT_TOUCHING_BUGFIX (USE_NEW_STUFF * 1)
43 #define USE_NEW_CONTINUOUS_SNAPPING (USE_NEW_STUFF * 1)
44 #define USE_GFX_RESET_GFX_ANIMATION (USE_NEW_STUFF * 1)
45 #define USE_BOTH_SWITCHGATE_SWITCHES (USE_NEW_STUFF * 1)
46 #define USE_PLAYER_GRAVITY (USE_NEW_STUFF * 1)
47 #define USE_FIXED_BORDER_RUNNING_GFX (USE_NEW_STUFF * 1)
48 #define USE_QUICKSAND_BD_ROCK_BUGFIX (USE_NEW_STUFF * 0)
50 #define USE_QUICKSAND_IMPACT_BUGFIX (USE_NEW_STUFF * 0)
52 #define USE_CODE_THAT_BREAKS_SNAKE_BITE (USE_NEW_STUFF * 1)
54 #define USE_UFAST_PLAYER_EXIT_BUGFIX (USE_NEW_STUFF * 1)
56 #define USE_GFX_RESET_ONLY_WHEN_MOVING (USE_NEW_STUFF * 1)
57 #define USE_GFX_RESET_PLAYER_ARTWORK (USE_NEW_STUFF * 1)
59 #define USE_FIX_KILLED_BY_NON_WALKABLE (USE_NEW_STUFF * 1)
60 #define USE_FIX_IMPACT_COLLISION (USE_NEW_STUFF * 1)
68 /* for MovePlayer() */
69 #define MP_NO_ACTION 0
72 #define MP_DONT_RUN_INTO (MP_MOVING | MP_ACTION)
74 /* for ScrollPlayer() */
76 #define SCROLL_GO_ON 1
78 /* for Bang()/Explode() */
79 #define EX_PHASE_START 0
80 #define EX_TYPE_NONE 0
81 #define EX_TYPE_NORMAL (1 << 0)
82 #define EX_TYPE_CENTER (1 << 1)
83 #define EX_TYPE_BORDER (1 << 2)
84 #define EX_TYPE_CROSS (1 << 3)
85 #define EX_TYPE_DYNA (1 << 4)
86 #define EX_TYPE_SINGLE_TILE (EX_TYPE_CENTER | EX_TYPE_BORDER)
88 #define PANEL_DEACTIVATED(p) ((p).x < 0 || (p).y < 0)
90 /* special positions in the game control window (relative to control window) */
91 #define XX_LEVEL1 (game.panel.level.x)
92 #define XX_LEVEL2 (game.panel.level.x - 1)
93 #define YY_LEVEL (game.panel.level.y)
94 #define XX_EMERALDS (game.panel.gems.x)
95 #define YY_EMERALDS (game.panel.gems.y)
96 #define XX_DYNAMITE (game.panel.inventory.x)
97 #define YY_DYNAMITE (game.panel.inventory.y)
98 #define XX_KEYS (game.panel.keys.x)
99 #define YY_KEYS (game.panel.keys.y)
100 #define XX_SCORE (game.panel.score.x)
101 #define YY_SCORE (game.panel.score.y)
102 #define XX_TIME1 (game.panel.time.x)
103 #define XX_TIME2 (game.panel.time.x + 1)
104 #define YY_TIME (game.panel.time.y)
106 /* special positions in the game control window (relative to main window) */
107 #define DX_LEVEL1 (DX + XX_LEVEL1)
108 #define DX_LEVEL2 (DX + XX_LEVEL2)
109 #define DY_LEVEL (DY + YY_LEVEL)
110 #define DX_EMERALDS (DX + XX_EMERALDS)
111 #define DY_EMERALDS (DY + YY_EMERALDS)
112 #define DX_DYNAMITE (DX + XX_DYNAMITE)
113 #define DY_DYNAMITE (DY + YY_DYNAMITE)
114 #define DX_KEYS (DX + XX_KEYS)
115 #define DY_KEYS (DY + YY_KEYS)
116 #define DX_SCORE (DX + XX_SCORE)
117 #define DY_SCORE (DY + YY_SCORE)
118 #define DX_TIME1 (DX + XX_TIME1)
119 #define DX_TIME2 (DX + XX_TIME2)
120 #define DY_TIME (DY + YY_TIME)
122 /* values for delayed check of falling and moving elements and for collision */
123 #define CHECK_DELAY_MOVING 3
124 #define CHECK_DELAY_FALLING CHECK_DELAY_MOVING
125 #define CHECK_DELAY_COLLISION 2
126 #define CHECK_DELAY_IMPACT CHECK_DELAY_COLLISION
128 /* values for initial player move delay (initial delay counter value) */
129 #define INITIAL_MOVE_DELAY_OFF -1
130 #define INITIAL_MOVE_DELAY_ON 0
132 /* values for player movement speed (which is in fact a delay value) */
133 #define MOVE_DELAY_MIN_SPEED 32
134 #define MOVE_DELAY_NORMAL_SPEED 8
135 #define MOVE_DELAY_HIGH_SPEED 4
136 #define MOVE_DELAY_MAX_SPEED 1
138 #define DOUBLE_MOVE_DELAY(x) (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
139 #define HALVE_MOVE_DELAY(x) (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
141 #define DOUBLE_PLAYER_SPEED(p) (HALVE_MOVE_DELAY( (p)->move_delay_value))
142 #define HALVE_PLAYER_SPEED(p) (DOUBLE_MOVE_DELAY((p)->move_delay_value))
144 /* values for other actions */
145 #define MOVE_STEPSIZE_NORMAL (TILEX / MOVE_DELAY_NORMAL_SPEED)
146 #define MOVE_STEPSIZE_MIN (1)
147 #define MOVE_STEPSIZE_MAX (TILEX)
149 #define GET_DX_FROM_DIR(d) ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
150 #define GET_DY_FROM_DIR(d) ((d) == MV_UP ? -1 : (d) == MV_DOWN ? 1 : 0)
152 #define INIT_GFX_RANDOM() (GetSimpleRandom(1000000))
154 #define GET_NEW_PUSH_DELAY(e) ( (element_info[e].push_delay_fixed) + \
155 RND(element_info[e].push_delay_random))
156 #define GET_NEW_DROP_DELAY(e) ( (element_info[e].drop_delay_fixed) + \
157 RND(element_info[e].drop_delay_random))
158 #define GET_NEW_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
159 RND(element_info[e].move_delay_random))
160 #define GET_MAX_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
161 (element_info[e].move_delay_random))
162 #define GET_NEW_CE_VALUE(e) ( (element_info[e].ce_value_fixed_initial) +\
163 RND(element_info[e].ce_value_random_initial))
164 #define GET_CE_SCORE(e) ( (element_info[e].collect_score))
165 #define GET_CHANGE_DELAY(c) ( ((c)->delay_fixed * (c)->delay_frames) + \
166 RND((c)->delay_random * (c)->delay_frames))
167 #define GET_CE_DELAY_VALUE(c) ( ((c)->delay_fixed) + \
168 RND((c)->delay_random))
171 #define GET_VALID_RUNTIME_ELEMENT(e) \
172 ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
174 #define RESOLVED_REFERENCE_ELEMENT(be, e) \
175 ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START : \
176 (be) + (e) - EL_SELF > EL_CUSTOM_END ? EL_CUSTOM_END : \
177 (be) + (e) - EL_SELF)
179 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs) \
180 ((e) == EL_TRIGGER_PLAYER ? (ch)->actual_trigger_player : \
181 (e) == EL_TRIGGER_ELEMENT ? (ch)->actual_trigger_element : \
182 (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value : \
183 (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score : \
184 (e) == EL_CURRENT_CE_VALUE ? (cv) : \
185 (e) == EL_CURRENT_CE_SCORE ? (cs) : \
186 (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ? \
187 RESOLVED_REFERENCE_ELEMENT(be, e) : \
190 #define CAN_GROW_INTO(e) \
191 ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
193 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition) \
194 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
197 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition) \
198 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
199 (CAN_MOVE_INTO_ACID(e) && \
200 Feld[x][y] == EL_ACID) || \
203 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition) \
204 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
205 (CAN_MOVE_INTO_ACID(e) && \
206 Feld[x][y] == EL_ACID) || \
209 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition) \
210 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
212 (CAN_MOVE_INTO_ACID(e) && \
213 Feld[x][y] == EL_ACID) || \
214 (DONT_COLLIDE_WITH(e) && \
216 !PLAYER_ENEMY_PROTECTED(x, y))))
218 #define ELEMENT_CAN_ENTER_FIELD(e, x, y) \
219 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
221 #define SATELLITE_CAN_ENTER_FIELD(x, y) \
222 ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
224 #define ANDROID_CAN_ENTER_FIELD(e, x, y) \
225 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
227 #define ANDROID_CAN_CLONE_FIELD(x, y) \
228 (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
229 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
231 #define ENEMY_CAN_ENTER_FIELD(e, x, y) \
232 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
234 #define YAMYAM_CAN_ENTER_FIELD(e, x, y) \
235 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
237 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y) \
238 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
240 #define PACMAN_CAN_ENTER_FIELD(e, x, y) \
241 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
243 #define PIG_CAN_ENTER_FIELD(e, x, y) \
244 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
246 #define PENGUIN_CAN_ENTER_FIELD(e, x, y) \
247 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
248 Feld[x][y] == EL_EM_EXIT_OPEN || \
249 Feld[x][y] == EL_STEEL_EXIT_OPEN || \
250 Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
251 IS_FOOD_PENGUIN(Feld[x][y])))
252 #define DRAGON_CAN_ENTER_FIELD(e, x, y) \
253 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
255 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition) \
256 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
258 #define SPRING_CAN_ENTER_FIELD(e, x, y) \
259 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
261 #define SPRING_CAN_BUMP_FROM_FIELD(x, y) \
262 (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER || \
263 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
265 #define MOVE_ENTER_EL(e) (element_info[e].move_enter_element)
267 #define CE_ENTER_FIELD_COND(e, x, y) \
268 (!IS_PLAYER(x, y) && \
269 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
271 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y) \
272 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
274 #define IN_LEV_FIELD_AND_IS_FREE(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
275 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
277 #define ACCESS_FROM(e, d) (element_info[e].access_direction &(d))
278 #define IS_WALKABLE_FROM(e, d) (IS_WALKABLE(e) && ACCESS_FROM(e, d))
279 #define IS_PASSABLE_FROM(e, d) (IS_PASSABLE(e) && ACCESS_FROM(e, d))
280 #define IS_ACCESSIBLE_FROM(e, d) (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
282 /* game button identifiers */
283 #define GAME_CTRL_ID_STOP 0
284 #define GAME_CTRL_ID_PAUSE 1
285 #define GAME_CTRL_ID_PLAY 2
286 #define SOUND_CTRL_ID_MUSIC 3
287 #define SOUND_CTRL_ID_LOOPS 4
288 #define SOUND_CTRL_ID_SIMPLE 5
290 #define NUM_GAME_BUTTONS 6
293 /* forward declaration for internal use */
295 static void CreateField(int, int, int);
297 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
298 static void AdvanceFrameAndPlayerCounters(int);
300 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
301 static boolean MovePlayer(struct PlayerInfo *, int, int);
302 static void ScrollPlayer(struct PlayerInfo *, int);
303 static void ScrollScreen(struct PlayerInfo *, int);
305 int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
307 static void InitBeltMovement(void);
308 static void CloseAllOpenTimegates(void);
309 static void CheckGravityMovement(struct PlayerInfo *);
310 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
311 static void KillPlayerUnlessEnemyProtected(int, int);
312 static void KillPlayerUnlessExplosionProtected(int, int);
314 static void TestIfPlayerTouchesCustomElement(int, int);
315 static void TestIfElementTouchesCustomElement(int, int);
316 static void TestIfElementHitsCustomElement(int, int, int);
318 static void TestIfElementSmashesCustomElement(int, int, int);
321 static void HandleElementChange(int, int, int);
322 static void ExecuteCustomElementAction(int, int, int, int);
323 static boolean ChangeElement(int, int, int, int);
325 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
326 #define CheckTriggeredElementChange(x, y, e, ev) \
327 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
328 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s) \
329 CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
330 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s) \
331 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
332 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p) \
333 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
335 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
336 #define CheckElementChange(x, y, e, te, ev) \
337 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
338 #define CheckElementChangeByPlayer(x, y, e, ev, p, s) \
339 CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
340 #define CheckElementChangeBySide(x, y, e, te, ev, s) \
341 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
343 static void PlayLevelSound(int, int, int);
344 static void PlayLevelSoundNearest(int, int, int);
345 static void PlayLevelSoundAction(int, int, int);
346 static void PlayLevelSoundElementAction(int, int, int, int);
347 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
348 static void PlayLevelSoundActionIfLoop(int, int, int);
349 static void StopLevelSoundActionIfLoop(int, int, int);
350 static void PlayLevelMusic();
352 static void MapGameButtons();
353 static void HandleGameButtons(struct GadgetInfo *);
355 int AmoebeNachbarNr(int, int);
356 void AmoebeUmwandeln(int, int);
357 void ContinueMoving(int, int);
359 void InitMovDir(int, int);
360 void InitAmoebaNr(int, int);
361 int NewHiScore(void);
363 void TestIfGoodThingHitsBadThing(int, int, int);
364 void TestIfBadThingHitsGoodThing(int, int, int);
365 void TestIfPlayerTouchesBadThing(int, int);
366 void TestIfPlayerRunsIntoBadThing(int, int, int);
367 void TestIfBadThingTouchesPlayer(int, int);
368 void TestIfBadThingRunsIntoPlayer(int, int, int);
369 void TestIfFriendTouchesBadThing(int, int);
370 void TestIfBadThingTouchesFriend(int, int);
371 void TestIfBadThingTouchesOtherBadThing(int, int);
373 void KillPlayer(struct PlayerInfo *);
374 void BuryPlayer(struct PlayerInfo *);
375 void RemovePlayer(struct PlayerInfo *);
377 boolean SnapField(struct PlayerInfo *, int, int);
378 boolean DropElement(struct PlayerInfo *);
380 static int getInvisibleActiveFromInvisibleElement(int);
381 static int getInvisibleFromInvisibleActiveElement(int);
383 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
385 /* for detection of endless loops, caused by custom element programming */
386 /* (using maximal playfield width x 10 is just a rough approximation) */
387 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH (MAX_PLAYFIELD_WIDTH * 10)
389 #define RECURSION_LOOP_DETECTION_START(e, rc) \
391 if (recursion_loop_detected) \
394 if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH) \
396 recursion_loop_detected = TRUE; \
397 recursion_loop_element = (e); \
400 recursion_loop_depth++; \
403 #define RECURSION_LOOP_DETECTION_END() \
405 recursion_loop_depth--; \
408 static int recursion_loop_depth;
409 static boolean recursion_loop_detected;
410 static boolean recursion_loop_element;
413 /* ------------------------------------------------------------------------- */
414 /* definition of elements that automatically change to other elements after */
415 /* a specified time, eventually calling a function when changing */
416 /* ------------------------------------------------------------------------- */
418 /* forward declaration for changer functions */
419 static void InitBuggyBase(int, int);
420 static void WarnBuggyBase(int, int);
422 static void InitTrap(int, int);
423 static void ActivateTrap(int, int);
424 static void ChangeActiveTrap(int, int);
426 static void InitRobotWheel(int, int);
427 static void RunRobotWheel(int, int);
428 static void StopRobotWheel(int, int);
430 static void InitTimegateWheel(int, int);
431 static void RunTimegateWheel(int, int);
433 static void InitMagicBallDelay(int, int);
434 static void ActivateMagicBall(int, int);
436 struct ChangingElementInfo
441 void (*pre_change_function)(int x, int y);
442 void (*change_function)(int x, int y);
443 void (*post_change_function)(int x, int y);
446 static struct ChangingElementInfo change_delay_list[] =
481 EL_STEEL_EXIT_OPENING,
489 EL_STEEL_EXIT_CLOSING,
490 EL_STEEL_EXIT_CLOSED,
517 EL_EM_STEEL_EXIT_OPENING,
518 EL_EM_STEEL_EXIT_OPEN,
525 EL_EM_STEEL_EXIT_CLOSING,
529 EL_EM_STEEL_EXIT_CLOSED,
553 EL_SWITCHGATE_OPENING,
561 EL_SWITCHGATE_CLOSING,
562 EL_SWITCHGATE_CLOSED,
594 EL_ACID_SPLASH_RIGHT,
603 EL_SP_BUGGY_BASE_ACTIVATING,
610 EL_SP_BUGGY_BASE_ACTIVATING,
611 EL_SP_BUGGY_BASE_ACTIVE,
618 EL_SP_BUGGY_BASE_ACTIVE,
642 EL_ROBOT_WHEEL_ACTIVE,
650 EL_TIMEGATE_SWITCH_ACTIVE,
658 EL_DC_TIMEGATE_SWITCH_ACTIVE,
659 EL_DC_TIMEGATE_SWITCH,
666 EL_EMC_MAGIC_BALL_ACTIVE,
667 EL_EMC_MAGIC_BALL_ACTIVE,
674 EL_EMC_SPRING_BUMPER_ACTIVE,
675 EL_EMC_SPRING_BUMPER,
682 EL_DIAGONAL_SHRINKING,
711 int push_delay_fixed, push_delay_random;
716 { EL_BALLOON, 0, 0 },
718 { EL_SOKOBAN_OBJECT, 2, 0 },
719 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
720 { EL_SATELLITE, 2, 0 },
721 { EL_SP_DISK_YELLOW, 2, 0 },
723 { EL_UNDEFINED, 0, 0 },
731 move_stepsize_list[] =
733 { EL_AMOEBA_DROP, 2 },
734 { EL_AMOEBA_DROPPING, 2 },
735 { EL_QUICKSAND_FILLING, 1 },
736 { EL_QUICKSAND_EMPTYING, 1 },
737 { EL_MAGIC_WALL_FILLING, 2 },
738 { EL_BD_MAGIC_WALL_FILLING, 2 },
739 { EL_MAGIC_WALL_EMPTYING, 2 },
740 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
750 collect_count_list[] =
753 { EL_BD_DIAMOND, 1 },
754 { EL_EMERALD_YELLOW, 1 },
755 { EL_EMERALD_RED, 1 },
756 { EL_EMERALD_PURPLE, 1 },
758 { EL_SP_INFOTRON, 1 },
770 access_direction_list[] =
772 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
773 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
774 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
775 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
776 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
777 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
778 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
779 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
780 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
781 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
782 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
784 { EL_SP_PORT_LEFT, MV_RIGHT },
785 { EL_SP_PORT_RIGHT, MV_LEFT },
786 { EL_SP_PORT_UP, MV_DOWN },
787 { EL_SP_PORT_DOWN, MV_UP },
788 { EL_SP_PORT_HORIZONTAL, MV_LEFT | MV_RIGHT },
789 { EL_SP_PORT_VERTICAL, MV_UP | MV_DOWN },
790 { EL_SP_PORT_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
791 { EL_SP_GRAVITY_PORT_LEFT, MV_RIGHT },
792 { EL_SP_GRAVITY_PORT_RIGHT, MV_LEFT },
793 { EL_SP_GRAVITY_PORT_UP, MV_DOWN },
794 { EL_SP_GRAVITY_PORT_DOWN, MV_UP },
795 { EL_SP_GRAVITY_ON_PORT_LEFT, MV_RIGHT },
796 { EL_SP_GRAVITY_ON_PORT_RIGHT, MV_LEFT },
797 { EL_SP_GRAVITY_ON_PORT_UP, MV_DOWN },
798 { EL_SP_GRAVITY_ON_PORT_DOWN, MV_UP },
799 { EL_SP_GRAVITY_OFF_PORT_LEFT, MV_RIGHT },
800 { EL_SP_GRAVITY_OFF_PORT_RIGHT, MV_LEFT },
801 { EL_SP_GRAVITY_OFF_PORT_UP, MV_DOWN },
802 { EL_SP_GRAVITY_OFF_PORT_DOWN, MV_UP },
804 { EL_UNDEFINED, MV_NONE }
807 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
809 #define IS_AUTO_CHANGING(e) (element_info[e].has_change_event[CE_DELAY])
810 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
811 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
812 IS_JUST_CHANGING(x, y))
814 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
816 /* static variables for playfield scan mode (scanning forward or backward) */
817 static int playfield_scan_start_x = 0;
818 static int playfield_scan_start_y = 0;
819 static int playfield_scan_delta_x = 1;
820 static int playfield_scan_delta_y = 1;
822 #define SCAN_PLAYFIELD(x, y) for ((y) = playfield_scan_start_y; \
823 (y) >= 0 && (y) <= lev_fieldy - 1; \
824 (y) += playfield_scan_delta_y) \
825 for ((x) = playfield_scan_start_x; \
826 (x) >= 0 && (x) <= lev_fieldx - 1; \
827 (x) += playfield_scan_delta_x) \
830 void DEBUG_SetMaximumDynamite()
834 for (i = 0; i < MAX_INVENTORY_SIZE; i++)
835 if (local_player->inventory_size < MAX_INVENTORY_SIZE)
836 local_player->inventory_element[local_player->inventory_size++] =
841 static void InitPlayfieldScanModeVars()
843 if (game.use_reverse_scan_direction)
845 playfield_scan_start_x = lev_fieldx - 1;
846 playfield_scan_start_y = lev_fieldy - 1;
848 playfield_scan_delta_x = -1;
849 playfield_scan_delta_y = -1;
853 playfield_scan_start_x = 0;
854 playfield_scan_start_y = 0;
856 playfield_scan_delta_x = 1;
857 playfield_scan_delta_y = 1;
861 static void InitPlayfieldScanMode(int mode)
863 game.use_reverse_scan_direction =
864 (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
866 InitPlayfieldScanModeVars();
869 static int get_move_delay_from_stepsize(int move_stepsize)
872 MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
874 /* make sure that stepsize value is always a power of 2 */
875 move_stepsize = (1 << log_2(move_stepsize));
877 return TILEX / move_stepsize;
880 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
883 int player_nr = player->index_nr;
884 int move_delay = get_move_delay_from_stepsize(move_stepsize);
885 boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
887 /* do no immediately change move delay -- the player might just be moving */
888 player->move_delay_value_next = move_delay;
890 /* information if player can move must be set separately */
891 player->cannot_move = cannot_move;
895 player->move_delay = game.initial_move_delay[player_nr];
896 player->move_delay_value = game.initial_move_delay_value[player_nr];
898 player->move_delay_value_next = -1;
900 player->move_delay_reset_counter = 0;
904 void GetPlayerConfig()
906 if (!audio.sound_available)
907 setup.sound_simple = FALSE;
909 if (!audio.loops_available)
910 setup.sound_loops = FALSE;
912 if (!audio.music_available)
913 setup.sound_music = FALSE;
915 if (!video.fullscreen_available)
916 setup.fullscreen = FALSE;
918 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
920 SetAudioMode(setup.sound);
924 static int getBeltNrFromBeltElement(int element)
926 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
927 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
928 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
931 static int getBeltNrFromBeltActiveElement(int element)
933 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
934 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
935 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
938 static int getBeltNrFromBeltSwitchElement(int element)
940 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
941 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
942 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
945 static int getBeltDirNrFromBeltSwitchElement(int element)
947 static int belt_base_element[4] =
949 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
950 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
951 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
952 EL_CONVEYOR_BELT_4_SWITCH_LEFT
955 int belt_nr = getBeltNrFromBeltSwitchElement(element);
956 int belt_dir_nr = element - belt_base_element[belt_nr];
958 return (belt_dir_nr % 3);
961 static int getBeltDirFromBeltSwitchElement(int element)
963 static int belt_move_dir[3] =
970 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
972 return belt_move_dir[belt_dir_nr];
975 static int get_element_from_group_element(int element)
977 if (IS_GROUP_ELEMENT(element))
979 struct ElementGroupInfo *group = element_info[element].group;
980 int last_anim_random_frame = gfx.anim_random_frame;
983 if (group->choice_mode == ANIM_RANDOM)
984 gfx.anim_random_frame = RND(group->num_elements_resolved);
986 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
987 group->choice_mode, 0,
990 if (group->choice_mode == ANIM_RANDOM)
991 gfx.anim_random_frame = last_anim_random_frame;
995 element = group->element_resolved[element_pos];
1001 static void InitPlayerField(int x, int y, int element, boolean init_game)
1003 if (element == EL_SP_MURPHY)
1007 if (stored_player[0].present)
1009 Feld[x][y] = EL_SP_MURPHY_CLONE;
1015 stored_player[0].use_murphy = TRUE;
1017 if (!level.use_artwork_element[0])
1018 stored_player[0].artwork_element = EL_SP_MURPHY;
1021 Feld[x][y] = EL_PLAYER_1;
1027 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1028 int jx = player->jx, jy = player->jy;
1030 player->present = TRUE;
1032 player->block_last_field = (element == EL_SP_MURPHY ?
1033 level.sp_block_last_field :
1034 level.block_last_field);
1036 /* ---------- initialize player's last field block delay --------------- */
1038 /* always start with reliable default value (no adjustment needed) */
1039 player->block_delay_adjustment = 0;
1041 /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1042 if (player->block_last_field && element == EL_SP_MURPHY)
1043 player->block_delay_adjustment = 1;
1045 /* special case 2: in game engines before 3.1.1, blocking was different */
1046 if (game.use_block_last_field_bug)
1047 player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1049 if (!options.network || player->connected)
1051 player->active = TRUE;
1053 /* remove potentially duplicate players */
1054 if (StorePlayer[jx][jy] == Feld[x][y])
1055 StorePlayer[jx][jy] = 0;
1057 StorePlayer[x][y] = Feld[x][y];
1061 printf("Player %d activated.\n", player->element_nr);
1062 printf("[Local player is %d and currently %s.]\n",
1063 local_player->element_nr,
1064 local_player->active ? "active" : "not active");
1068 Feld[x][y] = EL_EMPTY;
1070 player->jx = player->last_jx = x;
1071 player->jy = player->last_jy = y;
1075 static void InitField(int x, int y, boolean init_game)
1077 int element = Feld[x][y];
1086 InitPlayerField(x, y, element, init_game);
1089 case EL_SOKOBAN_FIELD_PLAYER:
1090 element = Feld[x][y] = EL_PLAYER_1;
1091 InitField(x, y, init_game);
1093 element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1094 InitField(x, y, init_game);
1097 case EL_SOKOBAN_FIELD_EMPTY:
1098 local_player->sokobanfields_still_needed++;
1102 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1103 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1104 else if (x > 0 && Feld[x-1][y] == EL_ACID)
1105 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1106 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1107 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1108 else if (y > 0 && Feld[x][y-1] == EL_ACID)
1109 Feld[x][y] = EL_ACID_POOL_BOTTOM;
1110 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1111 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1120 case EL_SPACESHIP_RIGHT:
1121 case EL_SPACESHIP_UP:
1122 case EL_SPACESHIP_LEFT:
1123 case EL_SPACESHIP_DOWN:
1124 case EL_BD_BUTTERFLY:
1125 case EL_BD_BUTTERFLY_RIGHT:
1126 case EL_BD_BUTTERFLY_UP:
1127 case EL_BD_BUTTERFLY_LEFT:
1128 case EL_BD_BUTTERFLY_DOWN:
1130 case EL_BD_FIREFLY_RIGHT:
1131 case EL_BD_FIREFLY_UP:
1132 case EL_BD_FIREFLY_LEFT:
1133 case EL_BD_FIREFLY_DOWN:
1134 case EL_PACMAN_RIGHT:
1136 case EL_PACMAN_LEFT:
1137 case EL_PACMAN_DOWN:
1139 case EL_YAMYAM_LEFT:
1140 case EL_YAMYAM_RIGHT:
1142 case EL_YAMYAM_DOWN:
1143 case EL_DARK_YAMYAM:
1146 case EL_SP_SNIKSNAK:
1147 case EL_SP_ELECTRON:
1156 case EL_AMOEBA_FULL:
1161 case EL_AMOEBA_DROP:
1162 if (y == lev_fieldy - 1)
1164 Feld[x][y] = EL_AMOEBA_GROWING;
1165 Store[x][y] = EL_AMOEBA_WET;
1169 case EL_DYNAMITE_ACTIVE:
1170 case EL_SP_DISK_RED_ACTIVE:
1171 case EL_DYNABOMB_PLAYER_1_ACTIVE:
1172 case EL_DYNABOMB_PLAYER_2_ACTIVE:
1173 case EL_DYNABOMB_PLAYER_3_ACTIVE:
1174 case EL_DYNABOMB_PLAYER_4_ACTIVE:
1175 MovDelay[x][y] = 96;
1178 case EL_EM_DYNAMITE_ACTIVE:
1179 MovDelay[x][y] = 32;
1183 local_player->lights_still_needed++;
1187 local_player->friends_still_needed++;
1192 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1195 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1196 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1197 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1198 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1199 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1200 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1201 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1202 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1203 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1204 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1205 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1206 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1209 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1210 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1211 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1213 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
1215 game.belt_dir[belt_nr] = belt_dir;
1216 game.belt_dir_nr[belt_nr] = belt_dir_nr;
1218 else /* more than one switch -- set it like the first switch */
1220 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1225 #if !USE_BOTH_SWITCHGATE_SWITCHES
1226 case EL_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
1228 Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
1231 case EL_DC_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
1233 Feld[x][y] = EL_DC_SWITCHGATE_SWITCH_UP;
1237 case EL_LIGHT_SWITCH_ACTIVE:
1239 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1242 case EL_INVISIBLE_STEELWALL:
1243 case EL_INVISIBLE_WALL:
1244 case EL_INVISIBLE_SAND:
1245 if (game.light_time_left > 0 ||
1246 game.lenses_time_left > 0)
1247 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1250 case EL_EMC_MAGIC_BALL:
1251 if (game.ball_state)
1252 Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1255 case EL_EMC_MAGIC_BALL_SWITCH:
1256 if (game.ball_state)
1257 Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1261 if (IS_CUSTOM_ELEMENT(element))
1263 if (CAN_MOVE(element))
1266 #if USE_NEW_CUSTOM_VALUE
1267 if (!element_info[element].use_last_ce_value || init_game)
1268 CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1271 else if (IS_GROUP_ELEMENT(element))
1273 Feld[x][y] = get_element_from_group_element(element);
1275 InitField(x, y, init_game);
1282 CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1285 static inline void InitField_WithBug1(int x, int y, boolean init_game)
1287 InitField(x, y, init_game);
1289 /* not needed to call InitMovDir() -- already done by InitField()! */
1290 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1291 CAN_MOVE(Feld[x][y]))
1295 static inline void InitField_WithBug2(int x, int y, boolean init_game)
1297 int old_element = Feld[x][y];
1299 InitField(x, y, init_game);
1301 /* not needed to call InitMovDir() -- already done by InitField()! */
1302 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1303 CAN_MOVE(old_element) &&
1304 (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1307 /* this case is in fact a combination of not less than three bugs:
1308 first, it calls InitMovDir() for elements that can move, although this is
1309 already done by InitField(); then, it checks the element that was at this
1310 field _before_ the call to InitField() (which can change it); lastly, it
1311 was not called for "mole with direction" elements, which were treated as
1312 "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1316 inline void DrawGameValue_Emeralds(int value)
1318 int xpos = (3 * 14 - 3 * getFontWidth(FONT_TEXT_2)) / 2;
1320 if (PANEL_DEACTIVATED(game.panel.gems))
1323 DrawText(DX_EMERALDS + xpos, DY_EMERALDS, int2str(value, 3), FONT_TEXT_2);
1326 inline void DrawGameValue_Dynamite(int value)
1328 int xpos = (3 * 14 - 3 * getFontWidth(FONT_TEXT_2)) / 2;
1330 if (PANEL_DEACTIVATED(game.panel.inventory))
1333 DrawText(DX_DYNAMITE + xpos, DY_DYNAMITE, int2str(value, 3), FONT_TEXT_2);
1336 inline void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
1338 int base_key_graphic = EL_KEY_1;
1341 if (PANEL_DEACTIVATED(game.panel.keys))
1344 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
1345 base_key_graphic = EL_EM_KEY_1;
1347 /* currently only 4 of 8 possible keys are displayed */
1348 for (i = 0; i < STD_NUM_KEYS; i++)
1350 int x = XX_KEYS + i * MINI_TILEX;
1354 DrawMiniGraphicExt(drawto, DX + x,DY + y, el2edimg(base_key_graphic + i));
1356 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
1357 DOOR_GFX_PAGEX5 + x, y, MINI_TILEX, MINI_TILEY, DX + x,DY + y);
1361 inline void DrawGameValue_Score(int value)
1363 int xpos = (5 * 14 - 5 * getFontWidth(FONT_TEXT_2)) / 2;
1365 if (PANEL_DEACTIVATED(game.panel.score))
1368 DrawText(DX_SCORE + xpos, DY_SCORE, int2str(value, 5), FONT_TEXT_2);
1371 inline void DrawGameValue_Time(int value)
1373 int xpos3 = (3 * 14 - 3 * getFontWidth(FONT_TEXT_2)) / 2;
1374 int xpos4 = (4 * 10 - 4 * getFontWidth(FONT_LEVEL_NUMBER)) / 2;
1376 if (PANEL_DEACTIVATED(game.panel.time))
1379 /* clear background if value just changed its size */
1380 if (value == 999 || value == 1000)
1381 ClearRectangleOnBackground(drawto, DX_TIME1, DY_TIME, 14 * 3, 14);
1384 DrawText(DX_TIME1 + xpos3, DY_TIME, int2str(value, 3), FONT_TEXT_2);
1386 DrawText(DX_TIME2 + xpos4, DY_TIME, int2str(value, 4), FONT_LEVEL_NUMBER);
1389 inline void DrawGameValue_Level(int value)
1391 if (PANEL_DEACTIVATED(game.panel.level))
1395 DrawText(DX_LEVEL1, DY_LEVEL, int2str(value, 2), FONT_TEXT_2);
1397 DrawText(DX_LEVEL2, DY_LEVEL, int2str(value, 3), FONT_LEVEL_NUMBER);
1400 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
1403 int key[MAX_NUM_KEYS];
1406 /* prevent EM engine from updating time/score values parallel to GameWon() */
1407 if (level.game_engine_type == GAME_ENGINE_TYPE_EM &&
1408 local_player->LevelSolved)
1411 for (i = 0; i < MAX_NUM_KEYS; i++)
1412 key[i] = key_bits & (1 << i);
1414 DrawGameValue_Level(level_nr);
1416 DrawGameValue_Emeralds(emeralds);
1417 DrawGameValue_Dynamite(dynamite);
1418 DrawGameValue_Score(score);
1419 DrawGameValue_Time(time);
1421 DrawGameValue_Keys(key);
1424 void DrawGameDoorValues()
1426 int time_value = (level.time == 0 ? TimePlayed : TimeLeft);
1427 int dynamite_value = 0;
1428 int score_value = (local_player->LevelSolved ? local_player->score_final :
1429 local_player->score);
1430 int gems_value = local_player->gems_still_needed;
1434 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
1436 DrawGameDoorValues_EM();
1441 if (game.centered_player_nr == -1)
1443 for (i = 0; i < MAX_PLAYERS; i++)
1445 for (j = 0; j < MAX_NUM_KEYS; j++)
1446 if (stored_player[i].key[j])
1447 key_bits |= (1 << j);
1449 dynamite_value += stored_player[i].inventory_size;
1454 int player_nr = game.centered_player_nr;
1456 for (i = 0; i < MAX_NUM_KEYS; i++)
1457 if (stored_player[player_nr].key[i])
1458 key_bits |= (1 << i);
1460 dynamite_value = stored_player[player_nr].inventory_size;
1463 DrawAllGameValues(gems_value, dynamite_value, score_value, time_value,
1469 =============================================================================
1471 -----------------------------------------------------------------------------
1472 initialize game engine due to level / tape version number
1473 =============================================================================
1476 static void InitGameEngine()
1478 int i, j, k, l, x, y;
1480 /* set game engine from tape file when re-playing, else from level file */
1481 game.engine_version = (tape.playing ? tape.engine_version :
1482 level.game_version);
1484 /* ---------------------------------------------------------------------- */
1485 /* set flags for bugs and changes according to active game engine version */
1486 /* ---------------------------------------------------------------------- */
1489 Summary of bugfix/change:
1490 Fixed handling for custom elements that change when pushed by the player.
1492 Fixed/changed in version:
1496 Before 3.1.0, custom elements that "change when pushing" changed directly
1497 after the player started pushing them (until then handled in "DigField()").
1498 Since 3.1.0, these custom elements are not changed until the "pushing"
1499 move of the element is finished (now handled in "ContinueMoving()").
1501 Affected levels/tapes:
1502 The first condition is generally needed for all levels/tapes before version
1503 3.1.0, which might use the old behaviour before it was changed; known tapes
1504 that are affected are some tapes from the level set "Walpurgis Gardens" by
1506 The second condition is an exception from the above case and is needed for
1507 the special case of tapes recorded with game (not engine!) version 3.1.0 or
1508 above (including some development versions of 3.1.0), but before it was
1509 known that this change would break tapes like the above and was fixed in
1510 3.1.1, so that the changed behaviour was active although the engine version
1511 while recording maybe was before 3.1.0. There is at least one tape that is
1512 affected by this exception, which is the tape for the one-level set "Bug
1513 Machine" by Juergen Bonhagen.
1516 game.use_change_when_pushing_bug =
1517 (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1519 tape.game_version >= VERSION_IDENT(3,1,0,0) &&
1520 tape.game_version < VERSION_IDENT(3,1,1,0)));
1523 Summary of bugfix/change:
1524 Fixed handling for blocking the field the player leaves when moving.
1526 Fixed/changed in version:
1530 Before 3.1.1, when "block last field when moving" was enabled, the field
1531 the player is leaving when moving was blocked for the time of the move,
1532 and was directly unblocked afterwards. This resulted in the last field
1533 being blocked for exactly one less than the number of frames of one player
1534 move. Additionally, even when blocking was disabled, the last field was
1535 blocked for exactly one frame.
1536 Since 3.1.1, due to changes in player movement handling, the last field
1537 is not blocked at all when blocking is disabled. When blocking is enabled,
1538 the last field is blocked for exactly the number of frames of one player
1539 move. Additionally, if the player is Murphy, the hero of Supaplex, the
1540 last field is blocked for exactly one more than the number of frames of
1543 Affected levels/tapes:
1544 (!!! yet to be determined -- probably many !!!)
1547 game.use_block_last_field_bug =
1548 (game.engine_version < VERSION_IDENT(3,1,1,0));
1551 Summary of bugfix/change:
1552 Changed behaviour of CE changes with multiple changes per single frame.
1554 Fixed/changed in version:
1558 Before 3.2.0-6, only one single CE change was allowed in each engine frame.
1559 This resulted in race conditions where CEs seem to behave strange in some
1560 situations (where triggered CE changes were just skipped because there was
1561 already a CE change on that tile in the playfield in that engine frame).
1562 Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
1563 (The number of changes per frame must be limited in any case, because else
1564 it is easily possible to define CE changes that would result in an infinite
1565 loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
1566 should be set large enough so that it would only be reached in cases where
1567 the corresponding CE change conditions run into a loop. Therefore, it seems
1568 to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
1569 maximal number of change pages for custom elements.)
1571 Affected levels/tapes:
1575 #if USE_ONLY_ONE_CHANGE_PER_FRAME
1576 game.max_num_changes_per_frame = 1;
1578 game.max_num_changes_per_frame =
1579 (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
1582 /* ---------------------------------------------------------------------- */
1584 /* default scan direction: scan playfield from top/left to bottom/right */
1585 InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
1587 /* dynamically adjust element properties according to game engine version */
1588 InitElementPropertiesEngine(game.engine_version);
1591 printf("level %d: level version == %06d\n", level_nr, level.game_version);
1592 printf(" tape version == %06d [%s] [file: %06d]\n",
1593 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
1595 printf(" => game.engine_version == %06d\n", game.engine_version);
1598 /* ---------- initialize player's initial move delay --------------------- */
1600 /* dynamically adjust player properties according to level information */
1601 for (i = 0; i < MAX_PLAYERS; i++)
1602 game.initial_move_delay_value[i] =
1603 get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
1605 /* dynamically adjust player properties according to game engine version */
1606 for (i = 0; i < MAX_PLAYERS; i++)
1607 game.initial_move_delay[i] =
1608 (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
1609 game.initial_move_delay_value[i] : 0);
1611 /* ---------- initialize player's initial push delay --------------------- */
1613 /* dynamically adjust player properties according to game engine version */
1614 game.initial_push_delay_value =
1615 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
1617 /* ---------- initialize changing elements ------------------------------- */
1619 /* initialize changing elements information */
1620 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1622 struct ElementInfo *ei = &element_info[i];
1624 /* this pointer might have been changed in the level editor */
1625 ei->change = &ei->change_page[0];
1627 if (!IS_CUSTOM_ELEMENT(i))
1629 ei->change->target_element = EL_EMPTY_SPACE;
1630 ei->change->delay_fixed = 0;
1631 ei->change->delay_random = 0;
1632 ei->change->delay_frames = 1;
1635 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1637 ei->has_change_event[j] = FALSE;
1639 ei->event_page_nr[j] = 0;
1640 ei->event_page[j] = &ei->change_page[0];
1644 /* add changing elements from pre-defined list */
1645 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
1647 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
1648 struct ElementInfo *ei = &element_info[ch_delay->element];
1650 ei->change->target_element = ch_delay->target_element;
1651 ei->change->delay_fixed = ch_delay->change_delay;
1653 ei->change->pre_change_function = ch_delay->pre_change_function;
1654 ei->change->change_function = ch_delay->change_function;
1655 ei->change->post_change_function = ch_delay->post_change_function;
1657 ei->change->can_change = TRUE;
1658 ei->change->can_change_or_has_action = TRUE;
1660 ei->has_change_event[CE_DELAY] = TRUE;
1662 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
1663 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
1666 /* ---------- initialize internal run-time variables ------------- */
1668 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1670 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1672 for (j = 0; j < ei->num_change_pages; j++)
1674 ei->change_page[j].can_change_or_has_action =
1675 (ei->change_page[j].can_change |
1676 ei->change_page[j].has_action);
1680 /* add change events from custom element configuration */
1681 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1683 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1685 for (j = 0; j < ei->num_change_pages; j++)
1687 if (!ei->change_page[j].can_change_or_has_action)
1690 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1692 /* only add event page for the first page found with this event */
1693 if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
1695 ei->has_change_event[k] = TRUE;
1697 ei->event_page_nr[k] = j;
1698 ei->event_page[k] = &ei->change_page[j];
1704 /* ---------- initialize run-time trigger player and element ------------- */
1706 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1708 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1710 for (j = 0; j < ei->num_change_pages; j++)
1712 ei->change_page[j].actual_trigger_element = EL_EMPTY;
1713 ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
1714 ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
1715 ei->change_page[j].actual_trigger_ce_value = 0;
1716 ei->change_page[j].actual_trigger_ce_score = 0;
1720 /* ---------- initialize trigger events ---------------------------------- */
1722 /* initialize trigger events information */
1723 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1724 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1725 trigger_events[i][j] = FALSE;
1727 /* add trigger events from element change event properties */
1728 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1730 struct ElementInfo *ei = &element_info[i];
1732 for (j = 0; j < ei->num_change_pages; j++)
1734 if (!ei->change_page[j].can_change_or_has_action)
1737 if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
1739 int trigger_element = ei->change_page[j].trigger_element;
1741 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1743 if (ei->change_page[j].has_event[k])
1745 if (IS_GROUP_ELEMENT(trigger_element))
1747 struct ElementGroupInfo *group =
1748 element_info[trigger_element].group;
1750 for (l = 0; l < group->num_elements_resolved; l++)
1751 trigger_events[group->element_resolved[l]][k] = TRUE;
1753 else if (trigger_element == EL_ANY_ELEMENT)
1754 for (l = 0; l < MAX_NUM_ELEMENTS; l++)
1755 trigger_events[l][k] = TRUE;
1757 trigger_events[trigger_element][k] = TRUE;
1764 /* ---------- initialize push delay -------------------------------------- */
1766 /* initialize push delay values to default */
1767 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1769 if (!IS_CUSTOM_ELEMENT(i))
1771 /* set default push delay values (corrected since version 3.0.7-1) */
1772 if (game.engine_version < VERSION_IDENT(3,0,7,1))
1774 element_info[i].push_delay_fixed = 2;
1775 element_info[i].push_delay_random = 8;
1779 element_info[i].push_delay_fixed = 8;
1780 element_info[i].push_delay_random = 8;
1785 /* set push delay value for certain elements from pre-defined list */
1786 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
1788 int e = push_delay_list[i].element;
1790 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
1791 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
1794 /* set push delay value for Supaplex elements for newer engine versions */
1795 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
1797 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1799 if (IS_SP_ELEMENT(i))
1801 /* set SP push delay to just enough to push under a falling zonk */
1802 int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
1804 element_info[i].push_delay_fixed = delay;
1805 element_info[i].push_delay_random = 0;
1810 /* ---------- initialize move stepsize ----------------------------------- */
1812 /* initialize move stepsize values to default */
1813 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1814 if (!IS_CUSTOM_ELEMENT(i))
1815 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
1817 /* set move stepsize value for certain elements from pre-defined list */
1818 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
1820 int e = move_stepsize_list[i].element;
1822 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
1825 /* ---------- initialize collect score ----------------------------------- */
1827 /* initialize collect score values for custom elements from initial value */
1828 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1829 if (IS_CUSTOM_ELEMENT(i))
1830 element_info[i].collect_score = element_info[i].collect_score_initial;
1832 /* ---------- initialize collect count ----------------------------------- */
1834 /* initialize collect count values for non-custom elements */
1835 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1836 if (!IS_CUSTOM_ELEMENT(i))
1837 element_info[i].collect_count_initial = 0;
1839 /* add collect count values for all elements from pre-defined list */
1840 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
1841 element_info[collect_count_list[i].element].collect_count_initial =
1842 collect_count_list[i].count;
1844 /* ---------- initialize access direction -------------------------------- */
1846 /* initialize access direction values to default (access from every side) */
1847 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1848 if (!IS_CUSTOM_ELEMENT(i))
1849 element_info[i].access_direction = MV_ALL_DIRECTIONS;
1851 /* set access direction value for certain elements from pre-defined list */
1852 for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
1853 element_info[access_direction_list[i].element].access_direction =
1854 access_direction_list[i].direction;
1856 /* ---------- initialize explosion content ------------------------------- */
1857 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1859 if (IS_CUSTOM_ELEMENT(i))
1862 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
1864 /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
1866 element_info[i].content.e[x][y] =
1867 (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
1868 i == EL_PLAYER_2 ? EL_EMERALD_RED :
1869 i == EL_PLAYER_3 ? EL_EMERALD :
1870 i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
1871 i == EL_MOLE ? EL_EMERALD_RED :
1872 i == EL_PENGUIN ? EL_EMERALD_PURPLE :
1873 i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
1874 i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
1875 i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
1876 i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
1877 i == EL_WALL_EMERALD ? EL_EMERALD :
1878 i == EL_WALL_DIAMOND ? EL_DIAMOND :
1879 i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
1880 i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
1881 i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
1882 i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
1883 i == EL_WALL_PEARL ? EL_PEARL :
1884 i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
1889 /* ---------- initialize recursion detection ------------------------------ */
1890 recursion_loop_depth = 0;
1891 recursion_loop_detected = FALSE;
1892 recursion_loop_element = EL_UNDEFINED;
1895 int get_num_special_action(int element, int action_first, int action_last)
1897 int num_special_action = 0;
1900 for (i = action_first; i <= action_last; i++)
1902 boolean found = FALSE;
1904 for (j = 0; j < NUM_DIRECTIONS; j++)
1905 if (el_act_dir2img(element, i, j) !=
1906 el_act_dir2img(element, ACTION_DEFAULT, j))
1910 num_special_action++;
1915 return num_special_action;
1920 =============================================================================
1922 -----------------------------------------------------------------------------
1923 initialize and start new game
1924 =============================================================================
1929 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
1930 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
1931 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
1932 boolean do_fading = (game_status == GAME_MODE_MAIN);
1935 game_status = GAME_MODE_PLAYING;
1939 /* don't play tapes over network */
1940 network_playing = (options.network && !tape.playing);
1942 for (i = 0; i < MAX_PLAYERS; i++)
1944 struct PlayerInfo *player = &stored_player[i];
1946 player->index_nr = i;
1947 player->index_bit = (1 << i);
1948 player->element_nr = EL_PLAYER_1 + i;
1950 player->present = FALSE;
1951 player->active = FALSE;
1952 player->killed = FALSE;
1955 player->effective_action = 0;
1956 player->programmed_action = 0;
1959 player->score_final = 0;
1961 player->gems_still_needed = level.gems_needed;
1962 player->sokobanfields_still_needed = 0;
1963 player->lights_still_needed = 0;
1964 player->friends_still_needed = 0;
1966 for (j = 0; j < MAX_NUM_KEYS; j++)
1967 player->key[j] = FALSE;
1969 player->num_white_keys = 0;
1971 player->dynabomb_count = 0;
1972 player->dynabomb_size = 1;
1973 player->dynabombs_left = 0;
1974 player->dynabomb_xl = FALSE;
1976 player->MovDir = MV_NONE;
1979 player->GfxDir = MV_NONE;
1980 player->GfxAction = ACTION_DEFAULT;
1982 player->StepFrame = 0;
1984 player->use_murphy = FALSE;
1985 player->artwork_element =
1986 (level.use_artwork_element[i] ? level.artwork_element[i] :
1987 player->element_nr);
1989 player->block_last_field = FALSE; /* initialized in InitPlayerField() */
1990 player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
1992 player->gravity = level.initial_player_gravity[i];
1994 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
1996 player->actual_frame_counter = 0;
1998 player->step_counter = 0;
2000 player->last_move_dir = MV_NONE;
2002 player->is_active = FALSE;
2004 player->is_waiting = FALSE;
2005 player->is_moving = FALSE;
2006 player->is_auto_moving = FALSE;
2007 player->is_digging = FALSE;
2008 player->is_snapping = FALSE;
2009 player->is_collecting = FALSE;
2010 player->is_pushing = FALSE;
2011 player->is_switching = FALSE;
2012 player->is_dropping = FALSE;
2013 player->is_dropping_pressed = FALSE;
2015 player->is_bored = FALSE;
2016 player->is_sleeping = FALSE;
2018 player->frame_counter_bored = -1;
2019 player->frame_counter_sleeping = -1;
2021 player->anim_delay_counter = 0;
2022 player->post_delay_counter = 0;
2024 player->dir_waiting = MV_NONE;
2025 player->action_waiting = ACTION_DEFAULT;
2026 player->last_action_waiting = ACTION_DEFAULT;
2027 player->special_action_bored = ACTION_DEFAULT;
2028 player->special_action_sleeping = ACTION_DEFAULT;
2030 player->switch_x = -1;
2031 player->switch_y = -1;
2033 player->drop_x = -1;
2034 player->drop_y = -1;
2036 player->show_envelope = 0;
2038 SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
2040 player->push_delay = -1; /* initialized when pushing starts */
2041 player->push_delay_value = game.initial_push_delay_value;
2043 player->drop_delay = 0;
2044 player->drop_pressed_delay = 0;
2046 player->last_jx = -1;
2047 player->last_jy = -1;
2051 player->shield_normal_time_left = 0;
2052 player->shield_deadly_time_left = 0;
2054 player->inventory_infinite_element = EL_UNDEFINED;
2055 player->inventory_size = 0;
2057 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
2058 SnapField(player, 0, 0);
2060 player->LevelSolved = FALSE;
2061 player->GameOver = FALSE;
2063 player->LevelSolved_GameEnd = FALSE;
2064 player->LevelSolved_SaveTape = FALSE;
2065 player->LevelSolved_SaveScore = FALSE;
2068 network_player_action_received = FALSE;
2070 #if defined(NETWORK_AVALIABLE)
2071 /* initial null action */
2072 if (network_playing)
2073 SendToServer_MovePlayer(MV_NONE);
2082 TimeLeft = level.time;
2085 ScreenMovDir = MV_NONE;
2089 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
2091 AllPlayersGone = FALSE;
2093 game.yamyam_content_nr = 0;
2094 game.magic_wall_active = FALSE;
2095 game.magic_wall_time_left = 0;
2096 game.light_time_left = 0;
2097 game.timegate_time_left = 0;
2098 game.switchgate_pos = 0;
2099 game.wind_direction = level.wind_direction_initial;
2101 #if !USE_PLAYER_GRAVITY
2102 game.gravity = FALSE;
2103 game.explosions_delayed = TRUE;
2106 game.lenses_time_left = 0;
2107 game.magnify_time_left = 0;
2109 game.ball_state = level.ball_state_initial;
2110 game.ball_content_nr = 0;
2112 game.envelope_active = FALSE;
2114 /* set focus to local player for network games, else to all players */
2115 game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
2116 game.centered_player_nr_next = game.centered_player_nr;
2117 game.set_centered_player = FALSE;
2119 if (network_playing && tape.recording)
2121 /* store client dependent player focus when recording network games */
2122 tape.centered_player_nr_next = game.centered_player_nr_next;
2123 tape.set_centered_player = TRUE;
2126 for (i = 0; i < NUM_BELTS; i++)
2128 game.belt_dir[i] = MV_NONE;
2129 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
2132 for (i = 0; i < MAX_NUM_AMOEBA; i++)
2133 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
2135 SCAN_PLAYFIELD(x, y)
2137 Feld[x][y] = level.field[x][y];
2138 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
2139 ChangeDelay[x][y] = 0;
2140 ChangePage[x][y] = -1;
2141 #if USE_NEW_CUSTOM_VALUE
2142 CustomValue[x][y] = 0; /* initialized in InitField() */
2144 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
2146 WasJustMoving[x][y] = 0;
2147 WasJustFalling[x][y] = 0;
2148 CheckCollision[x][y] = 0;
2149 CheckImpact[x][y] = 0;
2151 Pushed[x][y] = FALSE;
2153 ChangeCount[x][y] = 0;
2154 ChangeEvent[x][y] = -1;
2156 ExplodePhase[x][y] = 0;
2157 ExplodeDelay[x][y] = 0;
2158 ExplodeField[x][y] = EX_TYPE_NONE;
2160 RunnerVisit[x][y] = 0;
2161 PlayerVisit[x][y] = 0;
2164 GfxRandom[x][y] = INIT_GFX_RANDOM();
2165 GfxElement[x][y] = EL_UNDEFINED;
2166 GfxAction[x][y] = ACTION_DEFAULT;
2167 GfxDir[x][y] = MV_NONE;
2170 SCAN_PLAYFIELD(x, y)
2172 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
2174 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
2176 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
2179 InitField(x, y, TRUE);
2184 for (i = 0; i < MAX_PLAYERS; i++)
2186 struct PlayerInfo *player = &stored_player[i];
2188 /* set number of special actions for bored and sleeping animation */
2189 player->num_special_action_bored =
2190 get_num_special_action(player->artwork_element,
2191 ACTION_BORING_1, ACTION_BORING_LAST);
2192 player->num_special_action_sleeping =
2193 get_num_special_action(player->artwork_element,
2194 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
2197 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
2198 emulate_sb ? EMU_SOKOBAN :
2199 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
2201 #if USE_NEW_ALL_SLIPPERY
2202 /* initialize type of slippery elements */
2203 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2205 if (!IS_CUSTOM_ELEMENT(i))
2207 /* default: elements slip down either to the left or right randomly */
2208 element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
2210 /* SP style elements prefer to slip down on the left side */
2211 if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
2212 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
2214 /* BD style elements prefer to slip down on the left side */
2215 if (game.emulation == EMU_BOULDERDASH)
2216 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
2221 /* initialize explosion and ignition delay */
2222 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2224 if (!IS_CUSTOM_ELEMENT(i))
2227 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
2228 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
2229 game.emulation == EMU_SUPAPLEX ? 3 : 2);
2230 int last_phase = (num_phase + 1) * delay;
2231 int half_phase = (num_phase / 2) * delay;
2233 element_info[i].explosion_delay = last_phase - 1;
2234 element_info[i].ignition_delay = half_phase;
2236 if (i == EL_BLACK_ORB)
2237 element_info[i].ignition_delay = 1;
2241 if (element_info[i].explosion_delay < 1) /* !!! check again !!! */
2242 element_info[i].explosion_delay = 1;
2244 if (element_info[i].ignition_delay < 1) /* !!! check again !!! */
2245 element_info[i].ignition_delay = 1;
2249 /* correct non-moving belts to start moving left */
2250 for (i = 0; i < NUM_BELTS; i++)
2251 if (game.belt_dir[i] == MV_NONE)
2252 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
2254 /* check if any connected player was not found in playfield */
2255 for (i = 0; i < MAX_PLAYERS; i++)
2257 struct PlayerInfo *player = &stored_player[i];
2259 if (player->connected && !player->present)
2261 for (j = 0; j < MAX_PLAYERS; j++)
2263 struct PlayerInfo *some_player = &stored_player[j];
2264 int jx = some_player->jx, jy = some_player->jy;
2266 /* assign first free player found that is present in the playfield */
2267 if (some_player->present && !some_player->connected)
2269 player->present = TRUE;
2270 player->active = TRUE;
2272 some_player->present = FALSE;
2273 some_player->active = FALSE;
2275 player->artwork_element = some_player->artwork_element;
2277 player->block_last_field = some_player->block_last_field;
2278 player->block_delay_adjustment = some_player->block_delay_adjustment;
2280 StorePlayer[jx][jy] = player->element_nr;
2281 player->jx = player->last_jx = jx;
2282 player->jy = player->last_jy = jy;
2292 /* when playing a tape, eliminate all players who do not participate */
2294 for (i = 0; i < MAX_PLAYERS; i++)
2296 if (stored_player[i].active && !tape.player_participates[i])
2298 struct PlayerInfo *player = &stored_player[i];
2299 int jx = player->jx, jy = player->jy;
2301 player->active = FALSE;
2302 StorePlayer[jx][jy] = 0;
2303 Feld[jx][jy] = EL_EMPTY;
2307 else if (!options.network && !setup.team_mode) /* && !tape.playing */
2309 /* when in single player mode, eliminate all but the first active player */
2311 for (i = 0; i < MAX_PLAYERS; i++)
2313 if (stored_player[i].active)
2315 for (j = i + 1; j < MAX_PLAYERS; j++)
2317 if (stored_player[j].active)
2319 struct PlayerInfo *player = &stored_player[j];
2320 int jx = player->jx, jy = player->jy;
2322 player->active = FALSE;
2323 player->present = FALSE;
2325 StorePlayer[jx][jy] = 0;
2326 Feld[jx][jy] = EL_EMPTY;
2333 /* when recording the game, store which players take part in the game */
2336 for (i = 0; i < MAX_PLAYERS; i++)
2337 if (stored_player[i].active)
2338 tape.player_participates[i] = TRUE;
2343 for (i = 0; i < MAX_PLAYERS; i++)
2345 struct PlayerInfo *player = &stored_player[i];
2347 printf("Player %d: present == %d, connected == %d, active == %d.\n",
2352 if (local_player == player)
2353 printf("Player %d is local player.\n", i+1);
2357 if (BorderElement == EL_EMPTY)
2360 SBX_Right = lev_fieldx - SCR_FIELDX;
2362 SBY_Lower = lev_fieldy - SCR_FIELDY;
2367 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
2369 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
2372 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
2373 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
2375 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
2376 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
2378 /* if local player not found, look for custom element that might create
2379 the player (make some assumptions about the right custom element) */
2380 if (!local_player->present)
2382 int start_x = 0, start_y = 0;
2383 int found_rating = 0;
2384 int found_element = EL_UNDEFINED;
2385 int player_nr = local_player->index_nr;
2387 SCAN_PLAYFIELD(x, y)
2389 int element = Feld[x][y];
2394 if (level.use_start_element[player_nr] &&
2395 level.start_element[player_nr] == element &&
2402 found_element = element;
2405 if (!IS_CUSTOM_ELEMENT(element))
2408 if (CAN_CHANGE(element))
2410 for (i = 0; i < element_info[element].num_change_pages; i++)
2412 /* check for player created from custom element as single target */
2413 content = element_info[element].change_page[i].target_element;
2414 is_player = ELEM_IS_PLAYER(content);
2416 if (is_player && (found_rating < 3 || element < found_element))
2422 found_element = element;
2427 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
2429 /* check for player created from custom element as explosion content */
2430 content = element_info[element].content.e[xx][yy];
2431 is_player = ELEM_IS_PLAYER(content);
2433 if (is_player && (found_rating < 2 || element < found_element))
2435 start_x = x + xx - 1;
2436 start_y = y + yy - 1;
2439 found_element = element;
2442 if (!CAN_CHANGE(element))
2445 for (i = 0; i < element_info[element].num_change_pages; i++)
2447 /* check for player created from custom element as extended target */
2449 element_info[element].change_page[i].target_content.e[xx][yy];
2451 is_player = ELEM_IS_PLAYER(content);
2453 if (is_player && (found_rating < 1 || element < found_element))
2455 start_x = x + xx - 1;
2456 start_y = y + yy - 1;
2459 found_element = element;
2465 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
2466 start_x > SBX_Right + MIDPOSX ? SBX_Right :
2469 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
2470 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
2475 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2476 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2477 local_player->jx - MIDPOSX);
2479 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2480 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2481 local_player->jy - MIDPOSY);
2486 if (!game.restart_level)
2487 CloseDoor(DOOR_CLOSE_1);
2490 FadeOut(REDRAW_FIELD);
2492 /* !!! FIX THIS (START) !!! */
2493 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2495 InitGameEngine_EM();
2497 /* blit playfield from scroll buffer to normal back buffer for fading in */
2498 BlitScreenToBitmap_EM(backbuffer);
2505 /* after drawing the level, correct some elements */
2506 if (game.timegate_time_left == 0)
2507 CloseAllOpenTimegates();
2509 /* blit playfield from scroll buffer to normal back buffer for fading in */
2510 if (setup.soft_scrolling)
2511 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
2513 redraw_mask |= REDRAW_FROM_BACKBUFFER;
2515 /* !!! FIX THIS (END) !!! */
2518 FadeIn(REDRAW_FIELD);
2522 if (!game.restart_level)
2524 /* copy default game door content to main double buffer */
2525 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2526 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2529 SetPanelBackground();
2530 SetDrawBackgroundMask(REDRAW_DOOR_1);
2532 DrawGameDoorValues();
2534 if (!game.restart_level)
2538 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
2539 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
2540 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
2544 /* copy actual game door content to door double buffer for OpenDoor() */
2545 BlitBitmap(drawto, bitmap_db_door,
2546 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
2548 OpenDoor(DOOR_OPEN_ALL);
2550 PlaySound(SND_GAME_STARTING);
2552 if (setup.sound_music)
2555 KeyboardAutoRepeatOffUnlessAutoplay();
2559 for (i = 0; i < MAX_PLAYERS; i++)
2560 printf("Player %d %sactive.\n",
2561 i + 1, (stored_player[i].active ? "" : "not "));
2572 game.restart_level = FALSE;
2575 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
2577 /* this is used for non-R'n'D game engines to update certain engine values */
2579 /* needed to determine if sounds are played within the visible screen area */
2580 scroll_x = actual_scroll_x;
2581 scroll_y = actual_scroll_y;
2584 void InitMovDir(int x, int y)
2586 int i, element = Feld[x][y];
2587 static int xy[4][2] =
2594 static int direction[3][4] =
2596 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
2597 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
2598 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
2607 Feld[x][y] = EL_BUG;
2608 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
2611 case EL_SPACESHIP_RIGHT:
2612 case EL_SPACESHIP_UP:
2613 case EL_SPACESHIP_LEFT:
2614 case EL_SPACESHIP_DOWN:
2615 Feld[x][y] = EL_SPACESHIP;
2616 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
2619 case EL_BD_BUTTERFLY_RIGHT:
2620 case EL_BD_BUTTERFLY_UP:
2621 case EL_BD_BUTTERFLY_LEFT:
2622 case EL_BD_BUTTERFLY_DOWN:
2623 Feld[x][y] = EL_BD_BUTTERFLY;
2624 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
2627 case EL_BD_FIREFLY_RIGHT:
2628 case EL_BD_FIREFLY_UP:
2629 case EL_BD_FIREFLY_LEFT:
2630 case EL_BD_FIREFLY_DOWN:
2631 Feld[x][y] = EL_BD_FIREFLY;
2632 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
2635 case EL_PACMAN_RIGHT:
2637 case EL_PACMAN_LEFT:
2638 case EL_PACMAN_DOWN:
2639 Feld[x][y] = EL_PACMAN;
2640 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
2643 case EL_YAMYAM_LEFT:
2644 case EL_YAMYAM_RIGHT:
2646 case EL_YAMYAM_DOWN:
2647 Feld[x][y] = EL_YAMYAM;
2648 MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
2651 case EL_SP_SNIKSNAK:
2652 MovDir[x][y] = MV_UP;
2655 case EL_SP_ELECTRON:
2656 MovDir[x][y] = MV_LEFT;
2663 Feld[x][y] = EL_MOLE;
2664 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
2668 if (IS_CUSTOM_ELEMENT(element))
2670 struct ElementInfo *ei = &element_info[element];
2671 int move_direction_initial = ei->move_direction_initial;
2672 int move_pattern = ei->move_pattern;
2674 if (move_direction_initial == MV_START_PREVIOUS)
2676 if (MovDir[x][y] != MV_NONE)
2679 move_direction_initial = MV_START_AUTOMATIC;
2682 if (move_direction_initial == MV_START_RANDOM)
2683 MovDir[x][y] = 1 << RND(4);
2684 else if (move_direction_initial & MV_ANY_DIRECTION)
2685 MovDir[x][y] = move_direction_initial;
2686 else if (move_pattern == MV_ALL_DIRECTIONS ||
2687 move_pattern == MV_TURNING_LEFT ||
2688 move_pattern == MV_TURNING_RIGHT ||
2689 move_pattern == MV_TURNING_LEFT_RIGHT ||
2690 move_pattern == MV_TURNING_RIGHT_LEFT ||
2691 move_pattern == MV_TURNING_RANDOM)
2692 MovDir[x][y] = 1 << RND(4);
2693 else if (move_pattern == MV_HORIZONTAL)
2694 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
2695 else if (move_pattern == MV_VERTICAL)
2696 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
2697 else if (move_pattern & MV_ANY_DIRECTION)
2698 MovDir[x][y] = element_info[element].move_pattern;
2699 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
2700 move_pattern == MV_ALONG_RIGHT_SIDE)
2702 /* use random direction as default start direction */
2703 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2704 MovDir[x][y] = 1 << RND(4);
2706 for (i = 0; i < NUM_DIRECTIONS; i++)
2708 int x1 = x + xy[i][0];
2709 int y1 = y + xy[i][1];
2711 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2713 if (move_pattern == MV_ALONG_RIGHT_SIDE)
2714 MovDir[x][y] = direction[0][i];
2716 MovDir[x][y] = direction[1][i];
2725 MovDir[x][y] = 1 << RND(4);
2727 if (element != EL_BUG &&
2728 element != EL_SPACESHIP &&
2729 element != EL_BD_BUTTERFLY &&
2730 element != EL_BD_FIREFLY)
2733 for (i = 0; i < NUM_DIRECTIONS; i++)
2735 int x1 = x + xy[i][0];
2736 int y1 = y + xy[i][1];
2738 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2740 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
2742 MovDir[x][y] = direction[0][i];
2745 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
2746 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
2748 MovDir[x][y] = direction[1][i];
2757 GfxDir[x][y] = MovDir[x][y];
2760 void InitAmoebaNr(int x, int y)
2763 int group_nr = AmoebeNachbarNr(x, y);
2767 for (i = 1; i < MAX_NUM_AMOEBA; i++)
2769 if (AmoebaCnt[i] == 0)
2777 AmoebaNr[x][y] = group_nr;
2778 AmoebaCnt[group_nr]++;
2779 AmoebaCnt2[group_nr]++;
2782 static void PlayerWins(struct PlayerInfo *player)
2784 player->LevelSolved = TRUE;
2785 player->GameOver = TRUE;
2787 player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2788 level.native_em_level->lev->score : player->score);
2793 static int time, time_final;
2794 static int score, score_final;
2795 static int game_over_delay = 0;
2796 int game_over_delay_value = 50;
2798 if (!local_player->LevelSolved_GameEnd)
2802 /* do not start end game actions before the player stops moving (to exit) */
2803 if (local_player->MovPos)
2806 local_player->LevelSolved_GameEnd = TRUE;
2807 local_player->LevelSolved_SaveTape = tape.recording;
2808 local_player->LevelSolved_SaveScore = !tape.playing;
2810 if (tape.auto_play) /* tape might already be stopped here */
2811 tape.auto_play_level_solved = TRUE;
2817 game_over_delay = game_over_delay_value;
2819 time = time_final = (level.time == 0 ? TimePlayed : TimeLeft);
2820 score = score_final = local_player->score_final;
2825 score_final += TimeLeft * level.score[SC_TIME_BONUS];
2827 else if (level.time == 0 && TimePlayed < 999)
2830 score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
2833 local_player->score_final = score_final;
2835 if (level_editor_test_game)
2838 score = score_final;
2840 DrawGameValue_Time(time);
2841 DrawGameValue_Score(score);
2844 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
2846 if (ExitX >= 0 && ExitY >= 0) /* local player has left the level */
2848 /* close exit door after last player */
2849 if ((AllPlayersGone &&
2850 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
2851 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
2852 Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
2853 Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
2854 Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
2856 int element = Feld[ExitX][ExitY];
2859 if (element == EL_EM_EXIT_OPEN ||
2860 element == EL_EM_STEEL_EXIT_OPEN)
2867 Feld[ExitX][ExitY] =
2868 (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
2869 element == EL_EM_EXIT_OPEN ? EL_EM_EXIT_CLOSING :
2870 element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING:
2871 element == EL_STEEL_EXIT_OPEN ? EL_STEEL_EXIT_CLOSING:
2872 EL_EM_STEEL_EXIT_CLOSING);
2874 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
2878 /* player disappears */
2879 DrawLevelField(ExitX, ExitY);
2882 for (i = 0; i < MAX_PLAYERS; i++)
2884 struct PlayerInfo *player = &stored_player[i];
2886 if (player->present)
2888 RemovePlayer(player);
2890 /* player disappears */
2891 DrawLevelField(player->jx, player->jy);
2896 PlaySound(SND_GAME_WINNING);
2899 if (game_over_delay > 0)
2906 if (time != time_final)
2908 int time_to_go = ABS(time_final - time);
2909 int time_count_dir = (time < time_final ? +1 : -1);
2910 int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
2912 time += time_count_steps * time_count_dir;
2913 score += time_count_steps * level.score[SC_TIME_BONUS];
2915 DrawGameValue_Time(time);
2916 DrawGameValue_Score(score);
2918 if (time == time_final)
2919 StopSound(SND_GAME_LEVELTIME_BONUS);
2920 else if (setup.sound_loops)
2921 PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
2923 PlaySound(SND_GAME_LEVELTIME_BONUS);
2930 boolean raise_level = FALSE;
2932 CloseDoor(DOOR_CLOSE_1);
2934 if (local_player->LevelSolved_SaveTape)
2941 SaveTapeChecked(tape.level_nr); /* ask to save tape */
2943 SaveTape(tape.level_nr); /* ask to save tape */
2947 if (level_editor_test_game)
2949 game_status = GAME_MODE_MAIN;
2956 if (!local_player->LevelSolved_SaveScore)
2958 FadeOut(REDRAW_FIELD);
2960 game_status = GAME_MODE_MAIN;
2962 DrawAndFadeInMainMenu(REDRAW_FIELD);
2967 if (level_nr == leveldir_current->handicap_level)
2969 leveldir_current->handicap_level++;
2970 SaveLevelSetup_SeriesInfo();
2973 if (level_nr < leveldir_current->last_level)
2974 raise_level = TRUE; /* advance to next level */
2976 if ((hi_pos = NewHiScore()) >= 0)
2978 game_status = GAME_MODE_SCORES;
2980 DrawHallOfFame(hi_pos);
2990 FadeOut(REDRAW_FIELD);
2992 game_status = GAME_MODE_MAIN;
3000 DrawAndFadeInMainMenu(REDRAW_FIELD);
3009 LoadScore(level_nr);
3011 if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
3012 local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score)
3015 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
3017 if (local_player->score_final > highscore[k].Score)
3019 /* player has made it to the hall of fame */
3021 if (k < MAX_SCORE_ENTRIES - 1)
3023 int m = MAX_SCORE_ENTRIES - 1;
3026 for (l = k; l < MAX_SCORE_ENTRIES; l++)
3027 if (strEqual(setup.player_name, highscore[l].Name))
3029 if (m == k) /* player's new highscore overwrites his old one */
3033 for (l = m; l > k; l--)
3035 strcpy(highscore[l].Name, highscore[l - 1].Name);
3036 highscore[l].Score = highscore[l - 1].Score;
3043 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
3044 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
3045 highscore[k].Score = local_player->score_final;
3051 else if (!strncmp(setup.player_name, highscore[k].Name,
3052 MAX_PLAYER_NAME_LEN))
3053 break; /* player already there with a higher score */
3059 SaveScore(level_nr);
3064 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
3066 int element = Feld[x][y];
3067 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3068 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
3069 int horiz_move = (dx != 0);
3070 int sign = (horiz_move ? dx : dy);
3071 int step = sign * element_info[element].move_stepsize;
3073 /* special values for move stepsize for spring and things on conveyor belt */
3076 if (CAN_FALL(element) &&
3077 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
3078 step = sign * MOVE_STEPSIZE_NORMAL / 2;
3079 else if (element == EL_SPRING)
3080 step = sign * MOVE_STEPSIZE_NORMAL * 2;
3086 inline static int getElementMoveStepsize(int x, int y)
3088 return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
3091 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
3093 if (player->GfxAction != action || player->GfxDir != dir)
3096 printf("Player frame reset! (%d => %d, %d => %d)\n",
3097 player->GfxAction, action, player->GfxDir, dir);
3100 player->GfxAction = action;
3101 player->GfxDir = dir;
3103 player->StepFrame = 0;
3107 #if USE_GFX_RESET_GFX_ANIMATION
3108 static void ResetGfxFrame(int x, int y, boolean redraw)
3110 int element = Feld[x][y];
3111 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3112 int last_gfx_frame = GfxFrame[x][y];
3114 if (graphic_info[graphic].anim_global_sync)
3115 GfxFrame[x][y] = FrameCounter;
3116 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
3117 GfxFrame[x][y] = CustomValue[x][y];
3118 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
3119 GfxFrame[x][y] = element_info[element].collect_score;
3120 else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
3121 GfxFrame[x][y] = ChangeDelay[x][y];
3123 if (redraw && GfxFrame[x][y] != last_gfx_frame)
3124 DrawLevelGraphicAnimation(x, y, graphic);
3128 static void ResetGfxAnimation(int x, int y)
3130 GfxAction[x][y] = ACTION_DEFAULT;
3131 GfxDir[x][y] = MovDir[x][y];
3134 #if USE_GFX_RESET_GFX_ANIMATION
3135 ResetGfxFrame(x, y, FALSE);
3139 static void ResetRandomAnimationValue(int x, int y)
3141 GfxRandom[x][y] = INIT_GFX_RANDOM();
3144 void InitMovingField(int x, int y, int direction)
3146 int element = Feld[x][y];
3147 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3148 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
3151 boolean is_moving_before, is_moving_after;
3153 boolean continues_moving = (WasJustMoving[x][y] && direction == MovDir[x][y]);
3156 /* check if element was/is moving or being moved before/after mode change */
3158 is_moving_before = WasJustMoving[x][y];
3160 is_moving_before = (getElementMoveStepsizeExt(x, y, MovDir[x][y]) != 0);
3162 is_moving_after = (getElementMoveStepsizeExt(x, y, direction) != 0);
3164 /* reset animation only for moving elements which change direction of moving
3165 or which just started or stopped moving
3166 (else CEs with property "can move" / "not moving" are reset each frame) */
3167 #if USE_GFX_RESET_ONLY_WHEN_MOVING
3169 if (is_moving_before != is_moving_after ||
3170 direction != MovDir[x][y])
3171 ResetGfxAnimation(x, y);
3173 if ((is_moving_before || is_moving_after) && !continues_moving)
3174 ResetGfxAnimation(x, y);
3177 if (!continues_moving)
3178 ResetGfxAnimation(x, y);
3181 MovDir[x][y] = direction;
3182 GfxDir[x][y] = direction;
3184 #if USE_GFX_RESET_ONLY_WHEN_MOVING
3185 GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
3186 direction == MV_DOWN && CAN_FALL(element) ?
3187 ACTION_FALLING : ACTION_MOVING);
3189 GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
3190 ACTION_FALLING : ACTION_MOVING);
3193 /* this is needed for CEs with property "can move" / "not moving" */
3195 if (is_moving_after)
3197 if (Feld[newx][newy] == EL_EMPTY)
3198 Feld[newx][newy] = EL_BLOCKED;
3200 MovDir[newx][newy] = MovDir[x][y];
3202 #if USE_NEW_CUSTOM_VALUE
3203 CustomValue[newx][newy] = CustomValue[x][y];
3206 GfxFrame[newx][newy] = GfxFrame[x][y];
3207 GfxRandom[newx][newy] = GfxRandom[x][y];
3208 GfxAction[newx][newy] = GfxAction[x][y];
3209 GfxDir[newx][newy] = GfxDir[x][y];
3213 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
3215 int direction = MovDir[x][y];
3216 int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
3217 int newy = y + (direction & MV_UP ? -1 : direction & MV_DOWN ? +1 : 0);
3223 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
3225 int oldx = x, oldy = y;
3226 int direction = MovDir[x][y];
3228 if (direction == MV_LEFT)
3230 else if (direction == MV_RIGHT)
3232 else if (direction == MV_UP)
3234 else if (direction == MV_DOWN)
3237 *comes_from_x = oldx;
3238 *comes_from_y = oldy;
3241 int MovingOrBlocked2Element(int x, int y)
3243 int element = Feld[x][y];
3245 if (element == EL_BLOCKED)
3249 Blocked2Moving(x, y, &oldx, &oldy);
3250 return Feld[oldx][oldy];
3256 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
3258 /* like MovingOrBlocked2Element(), but if element is moving
3259 and (x,y) is the field the moving element is just leaving,
3260 return EL_BLOCKED instead of the element value */
3261 int element = Feld[x][y];
3263 if (IS_MOVING(x, y))
3265 if (element == EL_BLOCKED)
3269 Blocked2Moving(x, y, &oldx, &oldy);
3270 return Feld[oldx][oldy];
3279 static void RemoveField(int x, int y)
3281 Feld[x][y] = EL_EMPTY;
3287 #if USE_NEW_CUSTOM_VALUE
3288 CustomValue[x][y] = 0;
3292 ChangeDelay[x][y] = 0;
3293 ChangePage[x][y] = -1;
3294 Pushed[x][y] = FALSE;
3297 ExplodeField[x][y] = EX_TYPE_NONE;
3300 GfxElement[x][y] = EL_UNDEFINED;
3301 GfxAction[x][y] = ACTION_DEFAULT;
3302 GfxDir[x][y] = MV_NONE;
3305 void RemoveMovingField(int x, int y)
3307 int oldx = x, oldy = y, newx = x, newy = y;
3308 int element = Feld[x][y];
3309 int next_element = EL_UNDEFINED;
3311 if (element != EL_BLOCKED && !IS_MOVING(x, y))
3314 if (IS_MOVING(x, y))
3316 Moving2Blocked(x, y, &newx, &newy);
3318 if (Feld[newx][newy] != EL_BLOCKED)
3320 /* element is moving, but target field is not free (blocked), but
3321 already occupied by something different (example: acid pool);
3322 in this case, only remove the moving field, but not the target */
3324 RemoveField(oldx, oldy);
3326 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
3328 DrawLevelField(oldx, oldy);
3333 else if (element == EL_BLOCKED)
3335 Blocked2Moving(x, y, &oldx, &oldy);
3336 if (!IS_MOVING(oldx, oldy))
3340 if (element == EL_BLOCKED &&
3341 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
3342 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
3343 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
3344 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
3345 next_element = get_next_element(Feld[oldx][oldy]);
3347 RemoveField(oldx, oldy);
3348 RemoveField(newx, newy);
3350 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
3352 if (next_element != EL_UNDEFINED)
3353 Feld[oldx][oldy] = next_element;
3355 DrawLevelField(oldx, oldy);
3356 DrawLevelField(newx, newy);
3359 void DrawDynamite(int x, int y)
3361 int sx = SCREENX(x), sy = SCREENY(y);
3362 int graphic = el2img(Feld[x][y]);
3365 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
3368 if (IS_WALKABLE_INSIDE(Back[x][y]))
3372 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
3373 else if (Store[x][y])
3374 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
3376 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
3378 if (Back[x][y] || Store[x][y])
3379 DrawGraphicThruMask(sx, sy, graphic, frame);
3381 DrawGraphic(sx, sy, graphic, frame);
3384 void CheckDynamite(int x, int y)
3386 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
3390 if (MovDelay[x][y] != 0)
3393 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
3399 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
3404 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
3406 boolean num_checked_players = 0;
3409 for (i = 0; i < MAX_PLAYERS; i++)
3411 if (stored_player[i].active)
3413 int sx = stored_player[i].jx;
3414 int sy = stored_player[i].jy;
3416 if (num_checked_players == 0)
3423 *sx1 = MIN(*sx1, sx);
3424 *sy1 = MIN(*sy1, sy);
3425 *sx2 = MAX(*sx2, sx);
3426 *sy2 = MAX(*sy2, sy);
3429 num_checked_players++;
3434 static boolean checkIfAllPlayersFitToScreen_RND()
3436 int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
3438 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
3440 return (sx2 - sx1 < SCR_FIELDX &&
3441 sy2 - sy1 < SCR_FIELDY);
3444 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
3446 int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
3448 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
3450 *sx = (sx1 + sx2) / 2;
3451 *sy = (sy1 + sy2) / 2;
3454 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
3455 boolean center_screen, boolean quick_relocation)
3457 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3458 boolean no_delay = (tape.warp_forward);
3459 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
3460 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
3462 if (quick_relocation)
3464 int offset = (setup.scroll_delay ? 3 : 0);
3466 if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
3470 scroll_x = (x < SBX_Left + MIDPOSX ? SBX_Left :
3471 x > SBX_Right + MIDPOSX ? SBX_Right :
3474 scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
3475 y > SBY_Lower + MIDPOSY ? SBY_Lower :
3480 /* quick relocation (without scrolling), but do not center screen */
3482 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
3483 old_x > SBX_Right + MIDPOSX ? SBX_Right :
3486 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
3487 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
3490 int offset_x = x + (scroll_x - center_scroll_x);
3491 int offset_y = y + (scroll_y - center_scroll_y);
3493 scroll_x = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
3494 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
3495 offset_x - MIDPOSX);
3497 scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
3498 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
3499 offset_y - MIDPOSY);
3504 if ((move_dir == MV_LEFT && scroll_x > x - MIDPOSX + offset) ||
3505 (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
3506 scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
3508 if ((move_dir == MV_UP && scroll_y > y - MIDPOSY + offset) ||
3509 (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
3510 scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
3512 /* don't scroll over playfield boundaries */
3513 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
3514 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
3516 /* don't scroll over playfield boundaries */
3517 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
3518 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
3521 RedrawPlayfield(TRUE, 0,0,0,0);
3525 int scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left :
3526 x > SBX_Right + MIDPOSX ? SBX_Right :
3529 int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
3530 y > SBY_Lower + MIDPOSY ? SBY_Lower :
3533 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
3535 while (scroll_x != scroll_xx || scroll_y != scroll_yy)
3538 int fx = FX, fy = FY;
3540 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
3541 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
3543 if (dx == 0 && dy == 0) /* no scrolling needed at all */
3549 fx += dx * TILEX / 2;
3550 fy += dy * TILEY / 2;
3552 ScrollLevel(dx, dy);
3555 /* scroll in two steps of half tile size to make things smoother */
3556 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
3558 Delay(wait_delay_value);
3560 /* scroll second step to align at full tile size */
3562 Delay(wait_delay_value);
3567 Delay(wait_delay_value);
3571 void RelocatePlayer(int jx, int jy, int el_player_raw)
3573 int el_player = GET_PLAYER_ELEMENT(el_player_raw);
3574 int player_nr = GET_PLAYER_NR(el_player);
3575 struct PlayerInfo *player = &stored_player[player_nr];
3576 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3577 boolean no_delay = (tape.warp_forward);
3578 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
3579 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
3580 int old_jx = player->jx;
3581 int old_jy = player->jy;
3582 int old_element = Feld[old_jx][old_jy];
3583 int element = Feld[jx][jy];
3584 boolean player_relocated = (old_jx != jx || old_jy != jy);
3586 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
3587 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
3588 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
3589 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
3590 int leave_side_horiz = move_dir_horiz;
3591 int leave_side_vert = move_dir_vert;
3592 int enter_side = enter_side_horiz | enter_side_vert;
3593 int leave_side = leave_side_horiz | leave_side_vert;
3595 if (player->GameOver) /* do not reanimate dead player */
3598 if (!player_relocated) /* no need to relocate the player */
3601 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
3603 RemoveField(jx, jy); /* temporarily remove newly placed player */
3604 DrawLevelField(jx, jy);
3607 if (player->present)
3609 while (player->MovPos)
3611 ScrollPlayer(player, SCROLL_GO_ON);
3612 ScrollScreen(NULL, SCROLL_GO_ON);
3614 AdvanceFrameAndPlayerCounters(player->index_nr);
3619 Delay(wait_delay_value);
3622 DrawPlayer(player); /* needed here only to cleanup last field */
3623 DrawLevelField(player->jx, player->jy); /* remove player graphic */
3625 player->is_moving = FALSE;
3628 if (IS_CUSTOM_ELEMENT(old_element))
3629 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
3631 player->index_bit, leave_side);
3633 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
3635 player->index_bit, leave_side);
3637 Feld[jx][jy] = el_player;
3638 InitPlayerField(jx, jy, el_player, TRUE);
3640 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
3642 Feld[jx][jy] = element;
3643 InitField(jx, jy, FALSE);
3646 /* only visually relocate centered player */
3647 DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
3648 FALSE, level.instant_relocation);
3650 TestIfPlayerTouchesBadThing(jx, jy);
3651 TestIfPlayerTouchesCustomElement(jx, jy);
3653 if (IS_CUSTOM_ELEMENT(element))
3654 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
3655 player->index_bit, enter_side);
3657 CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
3658 player->index_bit, enter_side);
3661 void Explode(int ex, int ey, int phase, int mode)
3667 /* !!! eliminate this variable !!! */
3668 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
3670 if (game.explosions_delayed)
3672 ExplodeField[ex][ey] = mode;
3676 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
3678 int center_element = Feld[ex][ey];
3679 int artwork_element, explosion_element; /* set these values later */
3682 /* --- This is only really needed (and now handled) in "Impact()". --- */
3683 /* do not explode moving elements that left the explode field in time */
3684 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
3685 center_element == EL_EMPTY &&
3686 (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
3691 /* !!! at this place, the center element may be EL_BLOCKED !!! */
3692 if (mode == EX_TYPE_NORMAL ||
3693 mode == EX_TYPE_CENTER ||
3694 mode == EX_TYPE_CROSS)
3695 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
3698 /* remove things displayed in background while burning dynamite */
3699 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
3702 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
3704 /* put moving element to center field (and let it explode there) */
3705 center_element = MovingOrBlocked2Element(ex, ey);
3706 RemoveMovingField(ex, ey);
3707 Feld[ex][ey] = center_element;
3710 /* now "center_element" is finally determined -- set related values now */
3711 artwork_element = center_element; /* for custom player artwork */
3712 explosion_element = center_element; /* for custom player artwork */
3714 if (IS_PLAYER(ex, ey))
3716 int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
3718 artwork_element = stored_player[player_nr].artwork_element;
3720 if (level.use_explosion_element[player_nr])
3722 explosion_element = level.explosion_element[player_nr];
3723 artwork_element = explosion_element;
3728 if (mode == EX_TYPE_NORMAL ||
3729 mode == EX_TYPE_CENTER ||
3730 mode == EX_TYPE_CROSS)
3731 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
3734 last_phase = element_info[explosion_element].explosion_delay + 1;
3736 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
3738 int xx = x - ex + 1;
3739 int yy = y - ey + 1;
3742 if (!IN_LEV_FIELD(x, y) ||
3743 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
3744 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
3747 element = Feld[x][y];
3749 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
3751 element = MovingOrBlocked2Element(x, y);
3753 if (!IS_EXPLOSION_PROOF(element))
3754 RemoveMovingField(x, y);
3757 /* indestructible elements can only explode in center (but not flames) */
3758 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
3759 mode == EX_TYPE_BORDER)) ||
3760 element == EL_FLAMES)
3763 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
3764 behaviour, for example when touching a yamyam that explodes to rocks
3765 with active deadly shield, a rock is created under the player !!! */
3766 /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
3768 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
3769 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
3770 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
3772 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
3775 if (IS_ACTIVE_BOMB(element))
3777 /* re-activate things under the bomb like gate or penguin */
3778 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
3785 /* save walkable background elements while explosion on same tile */
3786 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
3787 (x != ex || y != ey || mode == EX_TYPE_BORDER))
3788 Back[x][y] = element;
3790 /* ignite explodable elements reached by other explosion */
3791 if (element == EL_EXPLOSION)
3792 element = Store2[x][y];
3794 if (AmoebaNr[x][y] &&
3795 (element == EL_AMOEBA_FULL ||
3796 element == EL_BD_AMOEBA ||
3797 element == EL_AMOEBA_GROWING))
3799 AmoebaCnt[AmoebaNr[x][y]]--;
3800 AmoebaCnt2[AmoebaNr[x][y]]--;
3805 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
3807 int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
3809 Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
3811 if (PLAYERINFO(ex, ey)->use_murphy)
3812 Store[x][y] = EL_EMPTY;
3815 /* !!! check this case -- currently needed for rnd_rado_negundo_v,
3816 !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
3817 else if (ELEM_IS_PLAYER(center_element))
3818 Store[x][y] = EL_EMPTY;
3819 else if (center_element == EL_YAMYAM)
3820 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
3821 else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
3822 Store[x][y] = element_info[center_element].content.e[xx][yy];
3824 /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
3825 (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
3826 otherwise) -- FIX THIS !!! */
3827 else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
3828 Store[x][y] = element_info[element].content.e[1][1];
3830 else if (!CAN_EXPLODE(element))
3831 Store[x][y] = element_info[element].content.e[1][1];
3834 Store[x][y] = EL_EMPTY;
3836 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
3837 center_element == EL_AMOEBA_TO_DIAMOND)
3838 Store2[x][y] = element;
3840 Feld[x][y] = EL_EXPLOSION;
3841 GfxElement[x][y] = artwork_element;
3843 ExplodePhase[x][y] = 1;
3844 ExplodeDelay[x][y] = last_phase;
3849 if (center_element == EL_YAMYAM)
3850 game.yamyam_content_nr =
3851 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
3863 GfxFrame[x][y] = 0; /* restart explosion animation */
3865 last_phase = ExplodeDelay[x][y];
3867 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
3871 /* activate this even in non-DEBUG version until cause for crash in
3872 getGraphicAnimationFrame() (see below) is found and eliminated */
3878 /* this can happen if the player leaves an explosion just in time */
3879 if (GfxElement[x][y] == EL_UNDEFINED)
3880 GfxElement[x][y] = EL_EMPTY;
3882 if (GfxElement[x][y] == EL_UNDEFINED)
3885 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
3886 printf("Explode(): This should never happen!\n");
3889 GfxElement[x][y] = EL_EMPTY;
3895 border_element = Store2[x][y];
3896 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3897 border_element = StorePlayer[x][y];
3899 if (phase == element_info[border_element].ignition_delay ||
3900 phase == last_phase)
3902 boolean border_explosion = FALSE;
3904 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
3905 !PLAYER_EXPLOSION_PROTECTED(x, y))
3907 KillPlayerUnlessExplosionProtected(x, y);
3908 border_explosion = TRUE;
3910 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
3912 Feld[x][y] = Store2[x][y];
3915 border_explosion = TRUE;
3917 else if (border_element == EL_AMOEBA_TO_DIAMOND)
3919 AmoebeUmwandeln(x, y);
3921 border_explosion = TRUE;
3924 /* if an element just explodes due to another explosion (chain-reaction),
3925 do not immediately end the new explosion when it was the last frame of
3926 the explosion (as it would be done in the following "if"-statement!) */
3927 if (border_explosion && phase == last_phase)
3931 if (phase == last_phase)
3935 element = Feld[x][y] = Store[x][y];
3936 Store[x][y] = Store2[x][y] = 0;
3937 GfxElement[x][y] = EL_UNDEFINED;
3939 /* player can escape from explosions and might therefore be still alive */
3940 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
3941 element <= EL_PLAYER_IS_EXPLODING_4)
3943 int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
3944 int explosion_element = EL_PLAYER_1 + player_nr;
3945 int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
3946 int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
3948 if (level.use_explosion_element[player_nr])
3949 explosion_element = level.explosion_element[player_nr];
3951 Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
3952 element_info[explosion_element].content.e[xx][yy]);
3955 /* restore probably existing indestructible background element */
3956 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
3957 element = Feld[x][y] = Back[x][y];
3960 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
3961 GfxDir[x][y] = MV_NONE;
3962 ChangeDelay[x][y] = 0;
3963 ChangePage[x][y] = -1;
3965 #if USE_NEW_CUSTOM_VALUE
3966 CustomValue[x][y] = 0;
3969 InitField_WithBug2(x, y, FALSE);
3971 DrawLevelField(x, y);
3973 TestIfElementTouchesCustomElement(x, y);
3975 if (GFX_CRUMBLED(element))
3976 DrawLevelFieldCrumbledSandNeighbours(x, y);
3978 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
3979 StorePlayer[x][y] = 0;
3981 if (ELEM_IS_PLAYER(element))
3982 RelocatePlayer(x, y, element);
3984 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3986 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
3987 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
3990 DrawLevelFieldCrumbledSand(x, y);
3992 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
3994 DrawLevelElement(x, y, Back[x][y]);
3995 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
3997 else if (IS_WALKABLE_UNDER(Back[x][y]))
3999 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
4000 DrawLevelElementThruMask(x, y, Back[x][y]);
4002 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
4003 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
4007 void DynaExplode(int ex, int ey)
4010 int dynabomb_element = Feld[ex][ey];
4011 int dynabomb_size = 1;
4012 boolean dynabomb_xl = FALSE;
4013 struct PlayerInfo *player;
4014 static int xy[4][2] =
4022 if (IS_ACTIVE_BOMB(dynabomb_element))
4024 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
4025 dynabomb_size = player->dynabomb_size;
4026 dynabomb_xl = player->dynabomb_xl;
4027 player->dynabombs_left++;
4030 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
4032 for (i = 0; i < NUM_DIRECTIONS; i++)
4034 for (j = 1; j <= dynabomb_size; j++)
4036 int x = ex + j * xy[i][0];
4037 int y = ey + j * xy[i][1];
4040 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
4043 element = Feld[x][y];
4045 /* do not restart explosions of fields with active bombs */
4046 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
4049 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
4051 if (element != EL_EMPTY && element != EL_EXPLOSION &&
4052 !IS_DIGGABLE(element) && !dynabomb_xl)
4058 void Bang(int x, int y)
4060 int element = MovingOrBlocked2Element(x, y);
4061 int explosion_type = EX_TYPE_NORMAL;
4063 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
4065 struct PlayerInfo *player = PLAYERINFO(x, y);
4067 element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
4068 player->element_nr);
4070 if (level.use_explosion_element[player->index_nr])
4072 int explosion_element = level.explosion_element[player->index_nr];
4074 if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
4075 explosion_type = EX_TYPE_CROSS;
4076 else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
4077 explosion_type = EX_TYPE_CENTER;
4085 case EL_BD_BUTTERFLY:
4088 case EL_DARK_YAMYAM:
4092 RaiseScoreElement(element);
4095 case EL_DYNABOMB_PLAYER_1_ACTIVE:
4096 case EL_DYNABOMB_PLAYER_2_ACTIVE:
4097 case EL_DYNABOMB_PLAYER_3_ACTIVE:
4098 case EL_DYNABOMB_PLAYER_4_ACTIVE:
4099 case EL_DYNABOMB_INCREASE_NUMBER:
4100 case EL_DYNABOMB_INCREASE_SIZE:
4101 case EL_DYNABOMB_INCREASE_POWER:
4102 explosion_type = EX_TYPE_DYNA;
4105 case EL_DC_LANDMINE:
4107 case EL_EM_EXIT_OPEN:
4108 case EL_EM_STEEL_EXIT_OPEN:
4110 explosion_type = EX_TYPE_CENTER;
4115 case EL_LAMP_ACTIVE:
4116 case EL_AMOEBA_TO_DIAMOND:
4117 if (!IS_PLAYER(x, y)) /* penguin and player may be at same field */
4118 explosion_type = EX_TYPE_CENTER;
4122 if (element_info[element].explosion_type == EXPLODES_CROSS)
4123 explosion_type = EX_TYPE_CROSS;
4124 else if (element_info[element].explosion_type == EXPLODES_1X1)
4125 explosion_type = EX_TYPE_CENTER;
4129 if (explosion_type == EX_TYPE_DYNA)
4132 Explode(x, y, EX_PHASE_START, explosion_type);
4134 CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
4137 void SplashAcid(int x, int y)
4139 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
4140 (!IN_LEV_FIELD(x - 1, y - 2) ||
4141 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
4142 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
4144 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
4145 (!IN_LEV_FIELD(x + 1, y - 2) ||
4146 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
4147 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
4149 PlayLevelSound(x, y, SND_ACID_SPLASHING);
4152 static void InitBeltMovement()
4154 static int belt_base_element[4] =
4156 EL_CONVEYOR_BELT_1_LEFT,
4157 EL_CONVEYOR_BELT_2_LEFT,
4158 EL_CONVEYOR_BELT_3_LEFT,
4159 EL_CONVEYOR_BELT_4_LEFT
4161 static int belt_base_active_element[4] =
4163 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
4164 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
4165 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
4166 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
4171 /* set frame order for belt animation graphic according to belt direction */
4172 for (i = 0; i < NUM_BELTS; i++)
4176 for (j = 0; j < NUM_BELT_PARTS; j++)
4178 int element = belt_base_active_element[belt_nr] + j;
4179 int graphic = el2img(element);
4181 if (game.belt_dir[i] == MV_LEFT)
4182 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
4184 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
4188 SCAN_PLAYFIELD(x, y)
4190 int element = Feld[x][y];
4192 for (i = 0; i < NUM_BELTS; i++)
4194 if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
4196 int e_belt_nr = getBeltNrFromBeltElement(element);
4199 if (e_belt_nr == belt_nr)
4201 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
4203 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
4210 static void ToggleBeltSwitch(int x, int y)
4212 static int belt_base_element[4] =
4214 EL_CONVEYOR_BELT_1_LEFT,
4215 EL_CONVEYOR_BELT_2_LEFT,
4216 EL_CONVEYOR_BELT_3_LEFT,
4217 EL_CONVEYOR_BELT_4_LEFT
4219 static int belt_base_active_element[4] =
4221 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
4222 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
4223 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
4224 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
4226 static int belt_base_switch_element[4] =
4228 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
4229 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
4230 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
4231 EL_CONVEYOR_BELT_4_SWITCH_LEFT
4233 static int belt_move_dir[4] =
4241 int element = Feld[x][y];
4242 int belt_nr = getBeltNrFromBeltSwitchElement(element);
4243 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
4244 int belt_dir = belt_move_dir[belt_dir_nr];
4247 if (!IS_BELT_SWITCH(element))
4250 game.belt_dir_nr[belt_nr] = belt_dir_nr;
4251 game.belt_dir[belt_nr] = belt_dir;
4253 if (belt_dir_nr == 3)
4256 /* set frame order for belt animation graphic according to belt direction */
4257 for (i = 0; i < NUM_BELT_PARTS; i++)
4259 int element = belt_base_active_element[belt_nr] + i;
4260 int graphic = el2img(element);
4262 if (belt_dir == MV_LEFT)
4263 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
4265 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
4268 SCAN_PLAYFIELD(xx, yy)
4270 int element = Feld[xx][yy];
4272 if (IS_BELT_SWITCH(element))
4274 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
4276 if (e_belt_nr == belt_nr)
4278 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
4279 DrawLevelField(xx, yy);
4282 else if (IS_BELT(element) && belt_dir != MV_NONE)
4284 int e_belt_nr = getBeltNrFromBeltElement(element);
4286 if (e_belt_nr == belt_nr)
4288 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
4290 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
4291 DrawLevelField(xx, yy);
4294 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
4296 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
4298 if (e_belt_nr == belt_nr)
4300 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
4302 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
4303 DrawLevelField(xx, yy);
4309 static void ToggleSwitchgateSwitch(int x, int y)
4313 game.switchgate_pos = !game.switchgate_pos;
4315 SCAN_PLAYFIELD(xx, yy)
4317 int element = Feld[xx][yy];
4319 #if !USE_BOTH_SWITCHGATE_SWITCHES
4320 if (element == EL_SWITCHGATE_SWITCH_UP ||
4321 element == EL_SWITCHGATE_SWITCH_DOWN)
4323 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
4324 DrawLevelField(xx, yy);
4326 else if (element == EL_DC_SWITCHGATE_SWITCH_UP ||
4327 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
4329 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
4330 DrawLevelField(xx, yy);
4333 if (element == EL_SWITCHGATE_SWITCH_UP)
4335 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
4336 DrawLevelField(xx, yy);
4338 else if (element == EL_SWITCHGATE_SWITCH_DOWN)
4340 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
4341 DrawLevelField(xx, yy);
4343 else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
4345 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
4346 DrawLevelField(xx, yy);
4348 else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
4350 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
4351 DrawLevelField(xx, yy);
4354 else if (element == EL_SWITCHGATE_OPEN ||
4355 element == EL_SWITCHGATE_OPENING)
4357 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
4359 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
4361 else if (element == EL_SWITCHGATE_CLOSED ||
4362 element == EL_SWITCHGATE_CLOSING)
4364 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
4366 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
4371 static int getInvisibleActiveFromInvisibleElement(int element)
4373 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
4374 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
4375 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
4379 static int getInvisibleFromInvisibleActiveElement(int element)
4381 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
4382 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
4383 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
4387 static void RedrawAllLightSwitchesAndInvisibleElements()
4391 SCAN_PLAYFIELD(x, y)
4393 int element = Feld[x][y];
4395 if (element == EL_LIGHT_SWITCH &&
4396 game.light_time_left > 0)
4398 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
4399 DrawLevelField(x, y);
4401 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
4402 game.light_time_left == 0)
4404 Feld[x][y] = EL_LIGHT_SWITCH;
4405 DrawLevelField(x, y);
4407 else if (element == EL_EMC_DRIPPER &&
4408 game.light_time_left > 0)
4410 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
4411 DrawLevelField(x, y);
4413 else if (element == EL_EMC_DRIPPER_ACTIVE &&
4414 game.light_time_left == 0)
4416 Feld[x][y] = EL_EMC_DRIPPER;
4417 DrawLevelField(x, y);
4419 else if (element == EL_INVISIBLE_STEELWALL ||
4420 element == EL_INVISIBLE_WALL ||
4421 element == EL_INVISIBLE_SAND)
4423 if (game.light_time_left > 0)
4424 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
4426 DrawLevelField(x, y);
4428 /* uncrumble neighbour fields, if needed */
4429 if (element == EL_INVISIBLE_SAND)
4430 DrawLevelFieldCrumbledSandNeighbours(x, y);
4432 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
4433 element == EL_INVISIBLE_WALL_ACTIVE ||
4434 element == EL_INVISIBLE_SAND_ACTIVE)
4436 if (game.light_time_left == 0)
4437 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
4439 DrawLevelField(x, y);
4441 /* re-crumble neighbour fields, if needed */
4442 if (element == EL_INVISIBLE_SAND)
4443 DrawLevelFieldCrumbledSandNeighbours(x, y);
4448 static void RedrawAllInvisibleElementsForLenses()
4452 SCAN_PLAYFIELD(x, y)
4454 int element = Feld[x][y];
4456 if (element == EL_EMC_DRIPPER &&
4457 game.lenses_time_left > 0)
4459 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
4460 DrawLevelField(x, y);
4462 else if (element == EL_EMC_DRIPPER_ACTIVE &&
4463 game.lenses_time_left == 0)
4465 Feld[x][y] = EL_EMC_DRIPPER;
4466 DrawLevelField(x, y);
4468 else if (element == EL_INVISIBLE_STEELWALL ||
4469 element == EL_INVISIBLE_WALL ||
4470 element == EL_INVISIBLE_SAND)
4472 if (game.lenses_time_left > 0)
4473 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
4475 DrawLevelField(x, y);
4477 /* uncrumble neighbour fields, if needed */
4478 if (element == EL_INVISIBLE_SAND)
4479 DrawLevelFieldCrumbledSandNeighbours(x, y);
4481 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
4482 element == EL_INVISIBLE_WALL_ACTIVE ||
4483 element == EL_INVISIBLE_SAND_ACTIVE)
4485 if (game.lenses_time_left == 0)
4486 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
4488 DrawLevelField(x, y);
4490 /* re-crumble neighbour fields, if needed */
4491 if (element == EL_INVISIBLE_SAND)
4492 DrawLevelFieldCrumbledSandNeighbours(x, y);
4497 static void RedrawAllInvisibleElementsForMagnifier()
4501 SCAN_PLAYFIELD(x, y)
4503 int element = Feld[x][y];
4505 if (element == EL_EMC_FAKE_GRASS &&
4506 game.magnify_time_left > 0)
4508 Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
4509 DrawLevelField(x, y);
4511 else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
4512 game.magnify_time_left == 0)
4514 Feld[x][y] = EL_EMC_FAKE_GRASS;
4515 DrawLevelField(x, y);
4517 else if (IS_GATE_GRAY(element) &&
4518 game.magnify_time_left > 0)
4520 Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
4521 element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
4522 IS_EM_GATE_GRAY(element) ?
4523 element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
4524 IS_EMC_GATE_GRAY(element) ?
4525 element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
4527 DrawLevelField(x, y);
4529 else if (IS_GATE_GRAY_ACTIVE(element) &&
4530 game.magnify_time_left == 0)
4532 Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
4533 element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
4534 IS_EM_GATE_GRAY_ACTIVE(element) ?
4535 element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
4536 IS_EMC_GATE_GRAY_ACTIVE(element) ?
4537 element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
4539 DrawLevelField(x, y);
4544 static void ToggleLightSwitch(int x, int y)
4546 int element = Feld[x][y];
4548 game.light_time_left =
4549 (element == EL_LIGHT_SWITCH ?
4550 level.time_light * FRAMES_PER_SECOND : 0);
4552 RedrawAllLightSwitchesAndInvisibleElements();
4555 static void ActivateTimegateSwitch(int x, int y)
4559 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
4561 SCAN_PLAYFIELD(xx, yy)
4563 int element = Feld[xx][yy];
4565 if (element == EL_TIMEGATE_CLOSED ||
4566 element == EL_TIMEGATE_CLOSING)
4568 Feld[xx][yy] = EL_TIMEGATE_OPENING;
4569 PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
4573 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
4575 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
4576 DrawLevelField(xx, yy);
4583 Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
4584 EL_DC_TIMEGATE_SWITCH_ACTIVE);
4586 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
4590 void Impact(int x, int y)
4592 boolean last_line = (y == lev_fieldy - 1);
4593 boolean object_hit = FALSE;
4594 boolean impact = (last_line || object_hit);
4595 int element = Feld[x][y];
4596 int smashed = EL_STEELWALL;
4598 if (!last_line) /* check if element below was hit */
4600 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
4603 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
4604 MovDir[x][y + 1] != MV_DOWN ||
4605 MovPos[x][y + 1] <= TILEY / 2));
4607 /* do not smash moving elements that left the smashed field in time */
4608 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
4609 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
4612 #if USE_QUICKSAND_IMPACT_BUGFIX
4613 if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
4615 RemoveMovingField(x, y + 1);
4616 Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
4617 Feld[x][y + 2] = EL_ROCK;
4618 DrawLevelField(x, y + 2);
4625 smashed = MovingOrBlocked2Element(x, y + 1);
4627 impact = (last_line || object_hit);
4630 if (!last_line && smashed == EL_ACID) /* element falls into acid */
4632 SplashAcid(x, y + 1);
4636 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
4637 /* only reset graphic animation if graphic really changes after impact */
4639 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
4641 ResetGfxAnimation(x, y);
4642 DrawLevelField(x, y);
4645 if (impact && CAN_EXPLODE_IMPACT(element))
4650 else if (impact && element == EL_PEARL)
4652 ResetGfxAnimation(x, y);
4654 Feld[x][y] = EL_PEARL_BREAKING;
4655 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4658 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
4660 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4665 if (impact && element == EL_AMOEBA_DROP)
4667 if (object_hit && IS_PLAYER(x, y + 1))
4668 KillPlayerUnlessEnemyProtected(x, y + 1);
4669 else if (object_hit && smashed == EL_PENGUIN)
4673 Feld[x][y] = EL_AMOEBA_GROWING;
4674 Store[x][y] = EL_AMOEBA_WET;
4676 ResetRandomAnimationValue(x, y);
4681 if (object_hit) /* check which object was hit */
4683 if (CAN_PASS_MAGIC_WALL(element) &&
4684 (smashed == EL_MAGIC_WALL ||
4685 smashed == EL_BD_MAGIC_WALL))
4688 int activated_magic_wall =
4689 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
4690 EL_BD_MAGIC_WALL_ACTIVE);
4692 /* activate magic wall / mill */
4693 SCAN_PLAYFIELD(xx, yy)
4694 if (Feld[xx][yy] == smashed)
4695 Feld[xx][yy] = activated_magic_wall;
4697 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
4698 game.magic_wall_active = TRUE;
4700 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
4701 SND_MAGIC_WALL_ACTIVATING :
4702 SND_BD_MAGIC_WALL_ACTIVATING));
4705 if (IS_PLAYER(x, y + 1))
4707 if (CAN_SMASH_PLAYER(element))
4709 KillPlayerUnlessEnemyProtected(x, y + 1);
4713 else if (smashed == EL_PENGUIN)
4715 if (CAN_SMASH_PLAYER(element))
4721 else if (element == EL_BD_DIAMOND)
4723 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
4729 else if (((element == EL_SP_INFOTRON ||
4730 element == EL_SP_ZONK) &&
4731 (smashed == EL_SP_SNIKSNAK ||
4732 smashed == EL_SP_ELECTRON ||
4733 smashed == EL_SP_DISK_ORANGE)) ||
4734 (element == EL_SP_INFOTRON &&
4735 smashed == EL_SP_DISK_YELLOW))
4740 else if (CAN_SMASH_EVERYTHING(element))
4742 if (IS_CLASSIC_ENEMY(smashed) ||
4743 CAN_EXPLODE_SMASHED(smashed))
4748 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
4750 if (smashed == EL_LAMP ||
4751 smashed == EL_LAMP_ACTIVE)
4756 else if (smashed == EL_NUT)
4758 Feld[x][y + 1] = EL_NUT_BREAKING;
4759 PlayLevelSound(x, y, SND_NUT_BREAKING);
4760 RaiseScoreElement(EL_NUT);
4763 else if (smashed == EL_PEARL)
4765 ResetGfxAnimation(x, y);
4767 Feld[x][y + 1] = EL_PEARL_BREAKING;
4768 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4771 else if (smashed == EL_DIAMOND)
4773 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
4774 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
4777 else if (IS_BELT_SWITCH(smashed))
4779 ToggleBeltSwitch(x, y + 1);
4781 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
4782 smashed == EL_SWITCHGATE_SWITCH_DOWN ||
4783 smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
4784 smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
4786 ToggleSwitchgateSwitch(x, y + 1);
4788 else if (smashed == EL_LIGHT_SWITCH ||
4789 smashed == EL_LIGHT_SWITCH_ACTIVE)
4791 ToggleLightSwitch(x, y + 1);
4796 TestIfElementSmashesCustomElement(x, y, MV_DOWN);
4799 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4801 CheckElementChangeBySide(x, y + 1, smashed, element,
4802 CE_SWITCHED, CH_SIDE_TOP);
4803 CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
4809 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4814 /* play sound of magic wall / mill */
4816 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
4817 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
4819 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
4820 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
4821 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
4822 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
4827 /* play sound of object that hits the ground */
4828 if (last_line || object_hit)
4829 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4832 inline static void TurnRoundExt(int x, int y)
4844 { 0, 0 }, { 0, 0 }, { 0, 0 },
4849 int left, right, back;
4853 { MV_DOWN, MV_UP, MV_RIGHT },
4854 { MV_UP, MV_DOWN, MV_LEFT },
4856 { MV_LEFT, MV_RIGHT, MV_DOWN },
4860 { MV_RIGHT, MV_LEFT, MV_UP }
4863 int element = Feld[x][y];
4864 int move_pattern = element_info[element].move_pattern;
4866 int old_move_dir = MovDir[x][y];
4867 int left_dir = turn[old_move_dir].left;
4868 int right_dir = turn[old_move_dir].right;
4869 int back_dir = turn[old_move_dir].back;
4871 int left_dx = move_xy[left_dir].dx, left_dy = move_xy[left_dir].dy;
4872 int right_dx = move_xy[right_dir].dx, right_dy = move_xy[right_dir].dy;
4873 int move_dx = move_xy[old_move_dir].dx, move_dy = move_xy[old_move_dir].dy;
4874 int back_dx = move_xy[back_dir].dx, back_dy = move_xy[back_dir].dy;
4876 int left_x = x + left_dx, left_y = y + left_dy;
4877 int right_x = x + right_dx, right_y = y + right_dy;
4878 int move_x = x + move_dx, move_y = y + move_dy;
4882 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4884 TestIfBadThingTouchesOtherBadThing(x, y);
4886 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
4887 MovDir[x][y] = right_dir;
4888 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4889 MovDir[x][y] = left_dir;
4891 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
4893 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
4896 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
4898 TestIfBadThingTouchesOtherBadThing(x, y);
4900 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
4901 MovDir[x][y] = left_dir;
4902 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4903 MovDir[x][y] = right_dir;
4905 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
4907 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
4910 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4912 TestIfBadThingTouchesOtherBadThing(x, y);
4914 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
4915 MovDir[x][y] = left_dir;
4916 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
4917 MovDir[x][y] = right_dir;
4919 if (MovDir[x][y] != old_move_dir)
4922 else if (element == EL_YAMYAM)
4924 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
4925 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
4927 if (can_turn_left && can_turn_right)
4928 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4929 else if (can_turn_left)
4930 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4931 else if (can_turn_right)
4932 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4934 MovDir[x][y] = back_dir;
4936 MovDelay[x][y] = 16 + 16 * RND(3);
4938 else if (element == EL_DARK_YAMYAM)
4940 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4942 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4945 if (can_turn_left && can_turn_right)
4946 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4947 else if (can_turn_left)
4948 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4949 else if (can_turn_right)
4950 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4952 MovDir[x][y] = back_dir;
4954 MovDelay[x][y] = 16 + 16 * RND(3);
4956 else if (element == EL_PACMAN)
4958 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
4959 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
4961 if (can_turn_left && can_turn_right)
4962 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4963 else if (can_turn_left)
4964 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4965 else if (can_turn_right)
4966 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4968 MovDir[x][y] = back_dir;
4970 MovDelay[x][y] = 6 + RND(40);
4972 else if (element == EL_PIG)
4974 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
4975 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
4976 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
4977 boolean should_turn_left, should_turn_right, should_move_on;
4979 int rnd = RND(rnd_value);
4981 should_turn_left = (can_turn_left &&
4983 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
4984 y + back_dy + left_dy)));
4985 should_turn_right = (can_turn_right &&
4987 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
4988 y + back_dy + right_dy)));
4989 should_move_on = (can_move_on &&
4992 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
4993 y + move_dy + left_dy) ||
4994 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
4995 y + move_dy + right_dy)));
4997 if (should_turn_left || should_turn_right || should_move_on)
4999 if (should_turn_left && should_turn_right && should_move_on)
5000 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
5001 rnd < 2 * rnd_value / 3 ? right_dir :
5003 else if (should_turn_left && should_turn_right)
5004 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
5005 else if (should_turn_left && should_move_on)
5006 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
5007 else if (should_turn_right && should_move_on)
5008 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
5009 else if (should_turn_left)
5010 MovDir[x][y] = left_dir;
5011 else if (should_turn_right)
5012 MovDir[x][y] = right_dir;
5013 else if (should_move_on)
5014 MovDir[x][y] = old_move_dir;
5016 else if (can_move_on && rnd > rnd_value / 8)
5017 MovDir[x][y] = old_move_dir;
5018 else if (can_turn_left && can_turn_right)
5019 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
5020 else if (can_turn_left && rnd > rnd_value / 8)
5021 MovDir[x][y] = left_dir;
5022 else if (can_turn_right && rnd > rnd_value/8)
5023 MovDir[x][y] = right_dir;
5025 MovDir[x][y] = back_dir;
5027 xx = x + move_xy[MovDir[x][y]].dx;
5028 yy = y + move_xy[MovDir[x][y]].dy;
5030 if (!IN_LEV_FIELD(xx, yy) ||
5031 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
5032 MovDir[x][y] = old_move_dir;
5036 else if (element == EL_DRAGON)
5038 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
5039 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
5040 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
5042 int rnd = RND(rnd_value);
5044 if (can_move_on && rnd > rnd_value / 8)
5045 MovDir[x][y] = old_move_dir;
5046 else if (can_turn_left && can_turn_right)
5047 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
5048 else if (can_turn_left && rnd > rnd_value / 8)
5049 MovDir[x][y] = left_dir;
5050 else if (can_turn_right && rnd > rnd_value / 8)
5051 MovDir[x][y] = right_dir;
5053 MovDir[x][y] = back_dir;
5055 xx = x + move_xy[MovDir[x][y]].dx;
5056 yy = y + move_xy[MovDir[x][y]].dy;
5058 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
5059 MovDir[x][y] = old_move_dir;
5063 else if (element == EL_MOLE)
5065 boolean can_move_on =
5066 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
5067 IS_AMOEBOID(Feld[move_x][move_y]) ||
5068 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
5071 boolean can_turn_left =
5072 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
5073 IS_AMOEBOID(Feld[left_x][left_y])));
5075 boolean can_turn_right =
5076 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
5077 IS_AMOEBOID(Feld[right_x][right_y])));
5079 if (can_turn_left && can_turn_right)
5080 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
5081 else if (can_turn_left)
5082 MovDir[x][y] = left_dir;
5084 MovDir[x][y] = right_dir;
5087 if (MovDir[x][y] != old_move_dir)
5090 else if (element == EL_BALLOON)
5092 MovDir[x][y] = game.wind_direction;
5095 else if (element == EL_SPRING)
5097 #if USE_NEW_SPRING_BUMPER
5098 if (MovDir[x][y] & MV_HORIZONTAL)
5100 if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
5101 !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
5103 Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
5104 ResetGfxAnimation(move_x, move_y);
5105 DrawLevelField(move_x, move_y);
5107 MovDir[x][y] = back_dir;
5109 else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
5110 SPRING_CAN_ENTER_FIELD(element, x, y + 1))
5111 MovDir[x][y] = MV_NONE;
5114 if (MovDir[x][y] & MV_HORIZONTAL &&
5115 (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
5116 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
5117 MovDir[x][y] = MV_NONE;
5122 else if (element == EL_ROBOT ||
5123 element == EL_SATELLITE ||
5124 element == EL_PENGUIN ||
5125 element == EL_EMC_ANDROID)
5127 int attr_x = -1, attr_y = -1;
5138 for (i = 0; i < MAX_PLAYERS; i++)
5140 struct PlayerInfo *player = &stored_player[i];
5141 int jx = player->jx, jy = player->jy;
5143 if (!player->active)
5147 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
5155 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
5156 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
5157 game.engine_version < VERSION_IDENT(3,1,0,0)))
5163 if (element == EL_PENGUIN)
5166 static int xy[4][2] =
5174 for (i = 0; i < NUM_DIRECTIONS; i++)
5176 int ex = x + xy[i][0];
5177 int ey = y + xy[i][1];
5179 if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
5180 Feld[ex][ey] == EL_EM_EXIT_OPEN ||
5181 Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
5182 Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
5191 MovDir[x][y] = MV_NONE;
5193 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
5194 else if (attr_x > x)
5195 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
5197 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
5198 else if (attr_y > y)
5199 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
5201 if (element == EL_ROBOT)
5205 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5206 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
5207 Moving2Blocked(x, y, &newx, &newy);
5209 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
5210 MovDelay[x][y] = 8 + 8 * !RND(3);
5212 MovDelay[x][y] = 16;
5214 else if (element == EL_PENGUIN)
5220 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5222 boolean first_horiz = RND(2);
5223 int new_move_dir = MovDir[x][y];
5226 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5227 Moving2Blocked(x, y, &newx, &newy);
5229 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
5233 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5234 Moving2Blocked(x, y, &newx, &newy);
5236 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
5239 MovDir[x][y] = old_move_dir;
5243 else if (element == EL_SATELLITE)
5249 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5251 boolean first_horiz = RND(2);
5252 int new_move_dir = MovDir[x][y];
5255 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5256 Moving2Blocked(x, y, &newx, &newy);
5258 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
5262 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5263 Moving2Blocked(x, y, &newx, &newy);
5265 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
5268 MovDir[x][y] = old_move_dir;
5272 else if (element == EL_EMC_ANDROID)
5274 static int check_pos[16] =
5276 -1, /* 0 => (invalid) */
5277 7, /* 1 => MV_LEFT */
5278 3, /* 2 => MV_RIGHT */
5279 -1, /* 3 => (invalid) */
5281 0, /* 5 => MV_LEFT | MV_UP */
5282 2, /* 6 => MV_RIGHT | MV_UP */
5283 -1, /* 7 => (invalid) */
5284 5, /* 8 => MV_DOWN */
5285 6, /* 9 => MV_LEFT | MV_DOWN */
5286 4, /* 10 => MV_RIGHT | MV_DOWN */
5287 -1, /* 11 => (invalid) */
5288 -1, /* 12 => (invalid) */
5289 -1, /* 13 => (invalid) */
5290 -1, /* 14 => (invalid) */
5291 -1, /* 15 => (invalid) */
5299 { -1, -1, MV_LEFT | MV_UP },
5301 { +1, -1, MV_RIGHT | MV_UP },
5302 { +1, 0, MV_RIGHT },
5303 { +1, +1, MV_RIGHT | MV_DOWN },
5305 { -1, +1, MV_LEFT | MV_DOWN },
5308 int start_pos, check_order;
5309 boolean can_clone = FALSE;
5312 /* check if there is any free field around current position */
5313 for (i = 0; i < 8; i++)
5315 int newx = x + check_xy[i].dx;
5316 int newy = y + check_xy[i].dy;
5318 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5326 if (can_clone) /* randomly find an element to clone */
5330 start_pos = check_pos[RND(8)];
5331 check_order = (RND(2) ? -1 : +1);
5333 for (i = 0; i < 8; i++)
5335 int pos_raw = start_pos + i * check_order;
5336 int pos = (pos_raw + 8) % 8;
5337 int newx = x + check_xy[pos].dx;
5338 int newy = y + check_xy[pos].dy;
5340 if (ANDROID_CAN_CLONE_FIELD(newx, newy))
5342 element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
5343 element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
5345 Store[x][y] = Feld[newx][newy];
5354 if (can_clone) /* randomly find a direction to move */
5358 start_pos = check_pos[RND(8)];
5359 check_order = (RND(2) ? -1 : +1);
5361 for (i = 0; i < 8; i++)
5363 int pos_raw = start_pos + i * check_order;
5364 int pos = (pos_raw + 8) % 8;
5365 int newx = x + check_xy[pos].dx;
5366 int newy = y + check_xy[pos].dy;
5367 int new_move_dir = check_xy[pos].dir;
5369 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5371 MovDir[x][y] = new_move_dir;
5372 MovDelay[x][y] = level.android_clone_time * 8 + 1;
5381 if (can_clone) /* cloning and moving successful */
5384 /* cannot clone -- try to move towards player */
5386 start_pos = check_pos[MovDir[x][y] & 0x0f];
5387 check_order = (RND(2) ? -1 : +1);
5389 for (i = 0; i < 3; i++)
5391 /* first check start_pos, then previous/next or (next/previous) pos */
5392 int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
5393 int pos = (pos_raw + 8) % 8;
5394 int newx = x + check_xy[pos].dx;
5395 int newy = y + check_xy[pos].dy;
5396 int new_move_dir = check_xy[pos].dir;
5398 if (IS_PLAYER(newx, newy))
5401 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
5403 MovDir[x][y] = new_move_dir;
5404 MovDelay[x][y] = level.android_move_time * 8 + 1;
5411 else if (move_pattern == MV_TURNING_LEFT ||
5412 move_pattern == MV_TURNING_RIGHT ||
5413 move_pattern == MV_TURNING_LEFT_RIGHT ||
5414 move_pattern == MV_TURNING_RIGHT_LEFT ||
5415 move_pattern == MV_TURNING_RANDOM ||
5416 move_pattern == MV_ALL_DIRECTIONS)
5418 boolean can_turn_left =
5419 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
5420 boolean can_turn_right =
5421 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
5423 if (element_info[element].move_stepsize == 0) /* "not moving" */
5426 if (move_pattern == MV_TURNING_LEFT)
5427 MovDir[x][y] = left_dir;
5428 else if (move_pattern == MV_TURNING_RIGHT)
5429 MovDir[x][y] = right_dir;
5430 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
5431 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
5432 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
5433 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
5434 else if (move_pattern == MV_TURNING_RANDOM)
5435 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
5436 can_turn_right && !can_turn_left ? right_dir :
5437 RND(2) ? left_dir : right_dir);
5438 else if (can_turn_left && can_turn_right)
5439 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5440 else if (can_turn_left)
5441 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5442 else if (can_turn_right)
5443 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5445 MovDir[x][y] = back_dir;
5447 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5449 else if (move_pattern == MV_HORIZONTAL ||
5450 move_pattern == MV_VERTICAL)
5452 if (move_pattern & old_move_dir)
5453 MovDir[x][y] = back_dir;
5454 else if (move_pattern == MV_HORIZONTAL)
5455 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
5456 else if (move_pattern == MV_VERTICAL)
5457 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
5459 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5461 else if (move_pattern & MV_ANY_DIRECTION)
5463 MovDir[x][y] = move_pattern;
5464 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5466 else if (move_pattern & MV_WIND_DIRECTION)
5468 MovDir[x][y] = game.wind_direction;
5469 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5471 else if (move_pattern == MV_ALONG_LEFT_SIDE)
5473 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
5474 MovDir[x][y] = left_dir;
5475 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5476 MovDir[x][y] = right_dir;
5478 if (MovDir[x][y] != old_move_dir)
5479 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5481 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
5483 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
5484 MovDir[x][y] = right_dir;
5485 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5486 MovDir[x][y] = left_dir;
5488 if (MovDir[x][y] != old_move_dir)
5489 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5491 else if (move_pattern == MV_TOWARDS_PLAYER ||
5492 move_pattern == MV_AWAY_FROM_PLAYER)
5494 int attr_x = -1, attr_y = -1;
5496 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
5507 for (i = 0; i < MAX_PLAYERS; i++)
5509 struct PlayerInfo *player = &stored_player[i];
5510 int jx = player->jx, jy = player->jy;
5512 if (!player->active)
5516 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
5524 MovDir[x][y] = MV_NONE;
5526 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
5527 else if (attr_x > x)
5528 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
5530 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
5531 else if (attr_y > y)
5532 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
5534 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5536 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5538 boolean first_horiz = RND(2);
5539 int new_move_dir = MovDir[x][y];
5541 if (element_info[element].move_stepsize == 0) /* "not moving" */
5543 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
5544 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5550 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5551 Moving2Blocked(x, y, &newx, &newy);
5553 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5557 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5558 Moving2Blocked(x, y, &newx, &newy);
5560 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5563 MovDir[x][y] = old_move_dir;
5566 else if (move_pattern == MV_WHEN_PUSHED ||
5567 move_pattern == MV_WHEN_DROPPED)
5569 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5570 MovDir[x][y] = MV_NONE;
5574 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
5576 static int test_xy[7][2] =
5586 static int test_dir[7] =
5596 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
5597 int move_preference = -1000000; /* start with very low preference */
5598 int new_move_dir = MV_NONE;
5599 int start_test = RND(4);
5602 for (i = 0; i < NUM_DIRECTIONS; i++)
5604 int move_dir = test_dir[start_test + i];
5605 int move_dir_preference;
5607 xx = x + test_xy[start_test + i][0];
5608 yy = y + test_xy[start_test + i][1];
5610 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
5611 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
5613 new_move_dir = move_dir;
5618 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
5621 move_dir_preference = -1 * RunnerVisit[xx][yy];
5622 if (hunter_mode && PlayerVisit[xx][yy] > 0)
5623 move_dir_preference = PlayerVisit[xx][yy];
5625 if (move_dir_preference > move_preference)
5627 /* prefer field that has not been visited for the longest time */
5628 move_preference = move_dir_preference;
5629 new_move_dir = move_dir;
5631 else if (move_dir_preference == move_preference &&
5632 move_dir == old_move_dir)
5634 /* prefer last direction when all directions are preferred equally */
5635 move_preference = move_dir_preference;
5636 new_move_dir = move_dir;
5640 MovDir[x][y] = new_move_dir;
5641 if (old_move_dir != new_move_dir)
5642 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5646 static void TurnRound(int x, int y)
5648 int direction = MovDir[x][y];
5652 GfxDir[x][y] = MovDir[x][y];
5654 if (direction != MovDir[x][y])
5658 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
5660 ResetGfxFrame(x, y, FALSE);
5663 static boolean JustBeingPushed(int x, int y)
5667 for (i = 0; i < MAX_PLAYERS; i++)
5669 struct PlayerInfo *player = &stored_player[i];
5671 if (player->active && player->is_pushing && player->MovPos)
5673 int next_jx = player->jx + (player->jx - player->last_jx);
5674 int next_jy = player->jy + (player->jy - player->last_jy);
5676 if (x == next_jx && y == next_jy)
5684 void StartMoving(int x, int y)
5686 boolean started_moving = FALSE; /* some elements can fall _and_ move */
5687 int element = Feld[x][y];
5692 if (MovDelay[x][y] == 0)
5693 GfxAction[x][y] = ACTION_DEFAULT;
5695 if (CAN_FALL(element) && y < lev_fieldy - 1)
5697 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
5698 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
5699 if (JustBeingPushed(x, y))
5702 if (element == EL_QUICKSAND_FULL)
5704 if (IS_FREE(x, y + 1))
5706 InitMovingField(x, y, MV_DOWN);
5707 started_moving = TRUE;
5709 Feld[x][y] = EL_QUICKSAND_EMPTYING;
5710 #if USE_QUICKSAND_BD_ROCK_BUGFIX
5711 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
5712 Store[x][y] = EL_ROCK;
5714 Store[x][y] = EL_ROCK;
5717 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
5719 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5721 if (!MovDelay[x][y])
5722 MovDelay[x][y] = TILEY + 1;
5731 Feld[x][y] = EL_QUICKSAND_EMPTY;
5732 Feld[x][y + 1] = EL_QUICKSAND_FULL;
5733 Store[x][y + 1] = Store[x][y];
5736 PlayLevelSoundAction(x, y, ACTION_FILLING);
5739 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
5740 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5742 InitMovingField(x, y, MV_DOWN);
5743 started_moving = TRUE;
5745 Feld[x][y] = EL_QUICKSAND_FILLING;
5746 Store[x][y] = element;
5748 PlayLevelSoundAction(x, y, ACTION_FILLING);
5750 else if (element == EL_MAGIC_WALL_FULL)
5752 if (IS_FREE(x, y + 1))
5754 InitMovingField(x, y, MV_DOWN);
5755 started_moving = TRUE;
5757 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
5758 Store[x][y] = EL_CHANGED(Store[x][y]);
5760 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
5762 if (!MovDelay[x][y])
5763 MovDelay[x][y] = TILEY/4 + 1;
5772 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
5773 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
5774 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
5778 else if (element == EL_BD_MAGIC_WALL_FULL)
5780 if (IS_FREE(x, y + 1))
5782 InitMovingField(x, y, MV_DOWN);
5783 started_moving = TRUE;
5785 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
5786 Store[x][y] = EL_CHANGED2(Store[x][y]);
5788 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
5790 if (!MovDelay[x][y])
5791 MovDelay[x][y] = TILEY/4 + 1;
5800 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
5801 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
5802 Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
5806 else if (CAN_PASS_MAGIC_WALL(element) &&
5807 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
5808 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
5810 InitMovingField(x, y, MV_DOWN);
5811 started_moving = TRUE;
5814 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
5815 EL_BD_MAGIC_WALL_FILLING);
5816 Store[x][y] = element;
5818 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
5820 SplashAcid(x, y + 1);
5822 InitMovingField(x, y, MV_DOWN);
5823 started_moving = TRUE;
5825 Store[x][y] = EL_ACID;
5828 #if USE_FIX_IMPACT_COLLISION
5829 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5830 CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
5832 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5833 CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
5835 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
5836 CAN_FALL(element) && WasJustFalling[x][y] &&
5837 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
5839 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
5840 CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
5841 (Feld[x][y + 1] == EL_BLOCKED)))
5843 /* this is needed for a special case not covered by calling "Impact()"
5844 from "ContinueMoving()": if an element moves to a tile directly below
5845 another element which was just falling on that tile (which was empty
5846 in the previous frame), the falling element above would just stop
5847 instead of smashing the element below (in previous version, the above
5848 element was just checked for "moving" instead of "falling", resulting
5849 in incorrect smashes caused by horizontal movement of the above
5850 element; also, the case of the player being the element to smash was
5851 simply not covered here... :-/ ) */
5853 CheckCollision[x][y] = 0;
5854 CheckImpact[x][y] = 0;
5858 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
5860 if (MovDir[x][y] == MV_NONE)
5862 InitMovingField(x, y, MV_DOWN);
5863 started_moving = TRUE;
5866 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
5868 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
5869 MovDir[x][y] = MV_DOWN;
5871 InitMovingField(x, y, MV_DOWN);
5872 started_moving = TRUE;
5874 else if (element == EL_AMOEBA_DROP)
5876 Feld[x][y] = EL_AMOEBA_GROWING;
5877 Store[x][y] = EL_AMOEBA_WET;
5879 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
5880 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
5881 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
5882 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
5884 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
5885 (IS_FREE(x - 1, y + 1) ||
5886 Feld[x - 1][y + 1] == EL_ACID));
5887 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
5888 (IS_FREE(x + 1, y + 1) ||
5889 Feld[x + 1][y + 1] == EL_ACID));
5890 boolean can_fall_any = (can_fall_left || can_fall_right);
5891 boolean can_fall_both = (can_fall_left && can_fall_right);
5892 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
5894 #if USE_NEW_ALL_SLIPPERY
5895 if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
5897 if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
5898 can_fall_right = FALSE;
5899 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
5900 can_fall_left = FALSE;
5901 else if (slippery_type == SLIPPERY_ONLY_LEFT)
5902 can_fall_right = FALSE;
5903 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
5904 can_fall_left = FALSE;
5906 can_fall_any = (can_fall_left || can_fall_right);
5907 can_fall_both = FALSE;
5910 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
5912 if (slippery_type == SLIPPERY_ONLY_LEFT)
5913 can_fall_right = FALSE;
5914 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
5915 can_fall_left = FALSE;
5916 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
5917 can_fall_right = FALSE;
5918 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
5919 can_fall_left = FALSE;
5921 can_fall_any = (can_fall_left || can_fall_right);
5922 can_fall_both = (can_fall_left && can_fall_right);
5926 #if USE_NEW_ALL_SLIPPERY
5928 #if USE_NEW_SP_SLIPPERY
5929 /* !!! better use the same properties as for custom elements here !!! */
5930 else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
5931 can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
5933 can_fall_right = FALSE; /* slip down on left side */
5934 can_fall_both = FALSE;
5939 #if USE_NEW_ALL_SLIPPERY
5942 if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
5943 can_fall_right = FALSE; /* slip down on left side */
5945 can_fall_left = !(can_fall_right = RND(2));
5947 can_fall_both = FALSE;
5952 if (game.emulation == EMU_BOULDERDASH ||
5953 element == EL_BD_ROCK || element == EL_BD_DIAMOND)
5954 can_fall_right = FALSE; /* slip down on left side */
5956 can_fall_left = !(can_fall_right = RND(2));
5958 can_fall_both = FALSE;
5964 /* if not determined otherwise, prefer left side for slipping down */
5965 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
5966 started_moving = TRUE;
5970 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
5972 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
5975 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
5976 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
5977 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
5978 int belt_dir = game.belt_dir[belt_nr];
5980 if ((belt_dir == MV_LEFT && left_is_free) ||
5981 (belt_dir == MV_RIGHT && right_is_free))
5983 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
5985 InitMovingField(x, y, belt_dir);
5986 started_moving = TRUE;
5988 Pushed[x][y] = TRUE;
5989 Pushed[nextx][y] = TRUE;
5991 GfxAction[x][y] = ACTION_DEFAULT;
5995 MovDir[x][y] = 0; /* if element was moving, stop it */
6000 /* not "else if" because of elements that can fall and move (EL_SPRING) */
6002 if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
6004 if (CAN_MOVE(element) && !started_moving)
6007 int move_pattern = element_info[element].move_pattern;
6012 if (MovDir[x][y] == MV_NONE)
6014 printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
6015 x, y, element, element_info[element].token_name);
6016 printf("StartMoving(): This should never happen!\n");
6021 Moving2Blocked(x, y, &newx, &newy);
6023 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
6026 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
6027 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6029 WasJustMoving[x][y] = 0;
6030 CheckCollision[x][y] = 0;
6032 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
6034 if (Feld[x][y] != element) /* element has changed */
6038 if (!MovDelay[x][y]) /* start new movement phase */
6040 /* all objects that can change their move direction after each step
6041 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
6043 if (element != EL_YAMYAM &&
6044 element != EL_DARK_YAMYAM &&
6045 element != EL_PACMAN &&
6046 !(move_pattern & MV_ANY_DIRECTION) &&
6047 move_pattern != MV_TURNING_LEFT &&
6048 move_pattern != MV_TURNING_RIGHT &&
6049 move_pattern != MV_TURNING_LEFT_RIGHT &&
6050 move_pattern != MV_TURNING_RIGHT_LEFT &&
6051 move_pattern != MV_TURNING_RANDOM)
6055 if (MovDelay[x][y] && (element == EL_BUG ||
6056 element == EL_SPACESHIP ||
6057 element == EL_SP_SNIKSNAK ||
6058 element == EL_SP_ELECTRON ||
6059 element == EL_MOLE))
6060 DrawLevelField(x, y);
6064 if (MovDelay[x][y]) /* wait some time before next movement */
6068 if (element == EL_ROBOT ||
6069 element == EL_YAMYAM ||
6070 element == EL_DARK_YAMYAM)
6072 DrawLevelElementAnimationIfNeeded(x, y, element);
6073 PlayLevelSoundAction(x, y, ACTION_WAITING);
6075 else if (element == EL_SP_ELECTRON)
6076 DrawLevelElementAnimationIfNeeded(x, y, element);
6077 else if (element == EL_DRAGON)
6080 int dir = MovDir[x][y];
6081 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
6082 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
6083 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
6084 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
6085 dir == MV_UP ? IMG_FLAMES_1_UP :
6086 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
6087 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
6089 GfxAction[x][y] = ACTION_ATTACKING;
6091 if (IS_PLAYER(x, y))
6092 DrawPlayerField(x, y);
6094 DrawLevelField(x, y);
6096 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
6098 for (i = 1; i <= 3; i++)
6100 int xx = x + i * dx;
6101 int yy = y + i * dy;
6102 int sx = SCREENX(xx);
6103 int sy = SCREENY(yy);
6104 int flame_graphic = graphic + (i - 1);
6106 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
6111 int flamed = MovingOrBlocked2Element(xx, yy);
6115 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
6117 else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
6118 RemoveMovingField(xx, yy);
6120 RemoveField(xx, yy);
6122 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
6125 RemoveMovingField(xx, yy);
6128 ChangeDelay[xx][yy] = 0;
6130 Feld[xx][yy] = EL_FLAMES;
6132 if (IN_SCR_FIELD(sx, sy))
6134 DrawLevelFieldCrumbledSand(xx, yy);
6135 DrawGraphic(sx, sy, flame_graphic, frame);
6140 if (Feld[xx][yy] == EL_FLAMES)
6141 Feld[xx][yy] = EL_EMPTY;
6142 DrawLevelField(xx, yy);
6147 if (MovDelay[x][y]) /* element still has to wait some time */
6149 PlayLevelSoundAction(x, y, ACTION_WAITING);
6155 /* now make next step */
6157 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
6159 if (DONT_COLLIDE_WITH(element) &&
6160 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
6161 !PLAYER_ENEMY_PROTECTED(newx, newy))
6163 TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
6168 else if (CAN_MOVE_INTO_ACID(element) &&
6169 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
6170 !IS_MV_DIAGONAL(MovDir[x][y]) &&
6171 (MovDir[x][y] == MV_DOWN ||
6172 game.engine_version >= VERSION_IDENT(3,1,0,0)))
6174 SplashAcid(newx, newy);
6175 Store[x][y] = EL_ACID;
6177 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
6179 if (Feld[newx][newy] == EL_EXIT_OPEN ||
6180 Feld[newx][newy] == EL_EM_EXIT_OPEN ||
6181 Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
6182 Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
6185 DrawLevelField(x, y);
6187 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
6188 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
6189 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
6191 local_player->friends_still_needed--;
6192 if (!local_player->friends_still_needed &&
6193 !local_player->GameOver && AllPlayersGone)
6194 PlayerWins(local_player);
6198 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
6200 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
6201 DrawLevelField(newx, newy);
6203 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
6205 else if (!IS_FREE(newx, newy))
6207 GfxAction[x][y] = ACTION_WAITING;
6209 if (IS_PLAYER(x, y))
6210 DrawPlayerField(x, y);
6212 DrawLevelField(x, y);
6217 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
6219 if (IS_FOOD_PIG(Feld[newx][newy]))
6221 if (IS_MOVING(newx, newy))
6222 RemoveMovingField(newx, newy);
6225 Feld[newx][newy] = EL_EMPTY;
6226 DrawLevelField(newx, newy);
6229 PlayLevelSound(x, y, SND_PIG_DIGGING);
6231 else if (!IS_FREE(newx, newy))
6233 if (IS_PLAYER(x, y))
6234 DrawPlayerField(x, y);
6236 DrawLevelField(x, y);
6241 else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
6243 if (Store[x][y] != EL_EMPTY)
6245 boolean can_clone = FALSE;
6248 /* check if element to clone is still there */
6249 for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
6251 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
6259 /* cannot clone or target field not free anymore -- do not clone */
6260 if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6261 Store[x][y] = EL_EMPTY;
6264 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6266 if (IS_MV_DIAGONAL(MovDir[x][y]))
6268 int diagonal_move_dir = MovDir[x][y];
6269 int stored = Store[x][y];
6270 int change_delay = 8;
6273 /* android is moving diagonally */
6275 CreateField(x, y, EL_DIAGONAL_SHRINKING);
6277 Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
6278 GfxElement[x][y] = EL_EMC_ANDROID;
6279 GfxAction[x][y] = ACTION_SHRINKING;
6280 GfxDir[x][y] = diagonal_move_dir;
6281 ChangeDelay[x][y] = change_delay;
6283 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
6286 DrawLevelGraphicAnimation(x, y, graphic);
6287 PlayLevelSoundAction(x, y, ACTION_SHRINKING);
6289 if (Feld[newx][newy] == EL_ACID)
6291 SplashAcid(newx, newy);
6296 CreateField(newx, newy, EL_DIAGONAL_GROWING);
6298 Store[newx][newy] = EL_EMC_ANDROID;
6299 GfxElement[newx][newy] = EL_EMC_ANDROID;
6300 GfxAction[newx][newy] = ACTION_GROWING;
6301 GfxDir[newx][newy] = diagonal_move_dir;
6302 ChangeDelay[newx][newy] = change_delay;
6304 graphic = el_act_dir2img(GfxElement[newx][newy],
6305 GfxAction[newx][newy], GfxDir[newx][newy]);
6307 DrawLevelGraphicAnimation(newx, newy, graphic);
6308 PlayLevelSoundAction(newx, newy, ACTION_GROWING);
6314 Feld[newx][newy] = EL_EMPTY;
6315 DrawLevelField(newx, newy);
6317 PlayLevelSoundAction(x, y, ACTION_DIGGING);
6320 else if (!IS_FREE(newx, newy))
6323 if (IS_PLAYER(x, y))
6324 DrawPlayerField(x, y);
6326 DrawLevelField(x, y);
6332 else if (IS_CUSTOM_ELEMENT(element) &&
6333 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6335 int new_element = Feld[newx][newy];
6337 if (!IS_FREE(newx, newy))
6339 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
6340 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
6343 /* no element can dig solid indestructible elements */
6344 if (IS_INDESTRUCTIBLE(new_element) &&
6345 !IS_DIGGABLE(new_element) &&
6346 !IS_COLLECTIBLE(new_element))
6349 if (AmoebaNr[newx][newy] &&
6350 (new_element == EL_AMOEBA_FULL ||
6351 new_element == EL_BD_AMOEBA ||
6352 new_element == EL_AMOEBA_GROWING))
6354 AmoebaCnt[AmoebaNr[newx][newy]]--;
6355 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6358 if (IS_MOVING(newx, newy))
6359 RemoveMovingField(newx, newy);
6362 RemoveField(newx, newy);
6363 DrawLevelField(newx, newy);
6366 /* if digged element was about to explode, prevent the explosion */
6367 ExplodeField[newx][newy] = EX_TYPE_NONE;
6369 PlayLevelSoundAction(x, y, action);
6372 Store[newx][newy] = EL_EMPTY;
6374 /* this makes it possible to leave the removed element again */
6375 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
6376 Store[newx][newy] = new_element;
6378 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
6380 int move_leave_element = element_info[element].move_leave_element;
6382 /* this makes it possible to leave the removed element again */
6383 Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
6384 new_element : move_leave_element);
6388 if (move_pattern & MV_MAZE_RUNNER_STYLE)
6390 RunnerVisit[x][y] = FrameCounter;
6391 PlayerVisit[x][y] /= 8; /* expire player visit path */
6394 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
6396 if (!IS_FREE(newx, newy))
6398 if (IS_PLAYER(x, y))
6399 DrawPlayerField(x, y);
6401 DrawLevelField(x, y);
6407 boolean wanna_flame = !RND(10);
6408 int dx = newx - x, dy = newy - y;
6409 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
6410 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
6411 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
6412 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
6413 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
6414 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
6417 IS_CLASSIC_ENEMY(element1) ||
6418 IS_CLASSIC_ENEMY(element2)) &&
6419 element1 != EL_DRAGON && element2 != EL_DRAGON &&
6420 element1 != EL_FLAMES && element2 != EL_FLAMES)
6422 ResetGfxAnimation(x, y);
6423 GfxAction[x][y] = ACTION_ATTACKING;
6425 if (IS_PLAYER(x, y))
6426 DrawPlayerField(x, y);
6428 DrawLevelField(x, y);
6430 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
6432 MovDelay[x][y] = 50;
6436 RemoveField(newx, newy);
6438 Feld[newx][newy] = EL_FLAMES;
6439 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
6442 RemoveField(newx1, newy1);
6444 Feld[newx1][newy1] = EL_FLAMES;
6446 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
6449 RemoveField(newx2, newy2);
6451 Feld[newx2][newy2] = EL_FLAMES;
6458 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6459 Feld[newx][newy] == EL_DIAMOND)
6461 if (IS_MOVING(newx, newy))
6462 RemoveMovingField(newx, newy);
6465 Feld[newx][newy] = EL_EMPTY;
6466 DrawLevelField(newx, newy);
6469 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
6471 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6472 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
6474 if (AmoebaNr[newx][newy])
6476 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6477 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
6478 Feld[newx][newy] == EL_BD_AMOEBA)
6479 AmoebaCnt[AmoebaNr[newx][newy]]--;
6484 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
6486 RemoveMovingField(newx, newy);
6489 if (IS_MOVING(newx, newy))
6491 RemoveMovingField(newx, newy);
6496 Feld[newx][newy] = EL_EMPTY;
6497 DrawLevelField(newx, newy);
6500 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
6502 else if ((element == EL_PACMAN || element == EL_MOLE)
6503 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
6505 if (AmoebaNr[newx][newy])
6507 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6508 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
6509 Feld[newx][newy] == EL_BD_AMOEBA)
6510 AmoebaCnt[AmoebaNr[newx][newy]]--;
6513 if (element == EL_MOLE)
6515 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
6516 PlayLevelSound(x, y, SND_MOLE_DIGGING);
6518 ResetGfxAnimation(x, y);
6519 GfxAction[x][y] = ACTION_DIGGING;
6520 DrawLevelField(x, y);
6522 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
6524 return; /* wait for shrinking amoeba */
6526 else /* element == EL_PACMAN */
6528 Feld[newx][newy] = EL_EMPTY;
6529 DrawLevelField(newx, newy);
6530 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
6533 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
6534 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
6535 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
6537 /* wait for shrinking amoeba to completely disappear */
6540 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
6542 /* object was running against a wall */
6547 /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
6548 if (move_pattern & MV_ANY_DIRECTION &&
6549 move_pattern == MovDir[x][y])
6551 int blocking_element =
6552 (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
6554 CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
6557 element = Feld[x][y]; /* element might have changed */
6561 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
6562 DrawLevelElementAnimation(x, y, element);
6564 if (DONT_TOUCH(element))
6565 TestIfBadThingTouchesPlayer(x, y);
6570 InitMovingField(x, y, MovDir[x][y]);
6572 PlayLevelSoundAction(x, y, ACTION_MOVING);
6576 ContinueMoving(x, y);
6579 void ContinueMoving(int x, int y)
6581 int element = Feld[x][y];
6582 struct ElementInfo *ei = &element_info[element];
6583 int direction = MovDir[x][y];
6584 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
6585 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
6586 int newx = x + dx, newy = y + dy;
6587 int stored = Store[x][y];
6588 int stored_new = Store[newx][newy];
6589 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
6590 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
6591 boolean last_line = (newy == lev_fieldy - 1);
6593 MovPos[x][y] += getElementMoveStepsize(x, y);
6595 if (pushed_by_player) /* special case: moving object pushed by player */
6596 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
6598 if (ABS(MovPos[x][y]) < TILEX)
6600 DrawLevelField(x, y);
6602 return; /* element is still moving */
6605 /* element reached destination field */
6607 Feld[x][y] = EL_EMPTY;
6608 Feld[newx][newy] = element;
6609 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
6611 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
6613 element = Feld[newx][newy] = EL_ACID;
6615 else if (element == EL_MOLE)
6617 Feld[x][y] = EL_SAND;
6619 DrawLevelFieldCrumbledSandNeighbours(x, y);
6621 else if (element == EL_QUICKSAND_FILLING)
6623 element = Feld[newx][newy] = get_next_element(element);
6624 Store[newx][newy] = Store[x][y];
6626 else if (element == EL_QUICKSAND_EMPTYING)
6628 Feld[x][y] = get_next_element(element);
6629 element = Feld[newx][newy] = Store[x][y];
6631 else if (element == EL_MAGIC_WALL_FILLING)
6633 element = Feld[newx][newy] = get_next_element(element);
6634 if (!game.magic_wall_active)
6635 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
6636 Store[newx][newy] = Store[x][y];
6638 else if (element == EL_MAGIC_WALL_EMPTYING)
6640 Feld[x][y] = get_next_element(element);
6641 if (!game.magic_wall_active)
6642 Feld[x][y] = EL_MAGIC_WALL_DEAD;
6643 element = Feld[newx][newy] = Store[x][y];
6645 #if USE_NEW_CUSTOM_VALUE
6646 InitField(newx, newy, FALSE);
6649 else if (element == EL_BD_MAGIC_WALL_FILLING)
6651 element = Feld[newx][newy] = get_next_element(element);
6652 if (!game.magic_wall_active)
6653 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
6654 Store[newx][newy] = Store[x][y];
6656 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
6658 Feld[x][y] = get_next_element(element);
6659 if (!game.magic_wall_active)
6660 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
6661 element = Feld[newx][newy] = Store[x][y];
6663 #if USE_NEW_CUSTOM_VALUE
6664 InitField(newx, newy, FALSE);
6667 else if (element == EL_AMOEBA_DROPPING)
6669 Feld[x][y] = get_next_element(element);
6670 element = Feld[newx][newy] = Store[x][y];
6672 else if (element == EL_SOKOBAN_OBJECT)
6675 Feld[x][y] = Back[x][y];
6677 if (Back[newx][newy])
6678 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
6680 Back[x][y] = Back[newx][newy] = 0;
6683 Store[x][y] = EL_EMPTY;
6688 MovDelay[newx][newy] = 0;
6690 if (CAN_CHANGE_OR_HAS_ACTION(element))
6692 /* copy element change control values to new field */
6693 ChangeDelay[newx][newy] = ChangeDelay[x][y];
6694 ChangePage[newx][newy] = ChangePage[x][y];
6695 ChangeCount[newx][newy] = ChangeCount[x][y];
6696 ChangeEvent[newx][newy] = ChangeEvent[x][y];
6699 #if USE_NEW_CUSTOM_VALUE
6700 CustomValue[newx][newy] = CustomValue[x][y];
6703 ChangeDelay[x][y] = 0;
6704 ChangePage[x][y] = -1;
6705 ChangeCount[x][y] = 0;
6706 ChangeEvent[x][y] = -1;
6708 #if USE_NEW_CUSTOM_VALUE
6709 CustomValue[x][y] = 0;
6712 /* copy animation control values to new field */
6713 GfxFrame[newx][newy] = GfxFrame[x][y];
6714 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
6715 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
6716 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
6718 Pushed[x][y] = Pushed[newx][newy] = FALSE;
6720 /* some elements can leave other elements behind after moving */
6722 if (ei->move_leave_element != EL_EMPTY &&
6723 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
6724 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
6726 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
6727 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
6728 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
6731 int move_leave_element = ei->move_leave_element;
6735 /* this makes it possible to leave the removed element again */
6736 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
6737 move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
6739 /* this makes it possible to leave the removed element again */
6740 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
6741 move_leave_element = stored;
6744 /* this makes it possible to leave the removed element again */
6745 if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
6746 ei->move_leave_element == EL_TRIGGER_ELEMENT)
6747 move_leave_element = stored;
6750 Feld[x][y] = move_leave_element;
6752 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
6753 MovDir[x][y] = direction;
6755 InitField(x, y, FALSE);
6757 if (GFX_CRUMBLED(Feld[x][y]))
6758 DrawLevelFieldCrumbledSandNeighbours(x, y);
6760 if (ELEM_IS_PLAYER(move_leave_element))
6761 RelocatePlayer(x, y, move_leave_element);
6764 /* do this after checking for left-behind element */
6765 ResetGfxAnimation(x, y); /* reset animation values for old field */
6767 if (!CAN_MOVE(element) ||
6768 (CAN_FALL(element) && direction == MV_DOWN &&
6769 (element == EL_SPRING ||
6770 element_info[element].move_pattern == MV_WHEN_PUSHED ||
6771 element_info[element].move_pattern == MV_WHEN_DROPPED)))
6772 GfxDir[x][y] = MovDir[newx][newy] = 0;
6774 DrawLevelField(x, y);
6775 DrawLevelField(newx, newy);
6777 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
6779 /* prevent pushed element from moving on in pushed direction */
6780 if (pushed_by_player && CAN_MOVE(element) &&
6781 element_info[element].move_pattern & MV_ANY_DIRECTION &&
6782 !(element_info[element].move_pattern & direction))
6783 TurnRound(newx, newy);
6785 /* prevent elements on conveyor belt from moving on in last direction */
6786 if (pushed_by_conveyor && CAN_FALL(element) &&
6787 direction & MV_HORIZONTAL)
6788 MovDir[newx][newy] = 0;
6790 if (!pushed_by_player)
6792 int nextx = newx + dx, nexty = newy + dy;
6793 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
6795 WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
6797 if (CAN_FALL(element) && direction == MV_DOWN)
6798 WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
6800 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
6801 CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
6803 #if USE_FIX_IMPACT_COLLISION
6804 if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
6805 CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
6809 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
6811 TestIfBadThingTouchesPlayer(newx, newy);
6812 TestIfBadThingTouchesFriend(newx, newy);
6814 if (!IS_CUSTOM_ELEMENT(element))
6815 TestIfBadThingTouchesOtherBadThing(newx, newy);
6817 else if (element == EL_PENGUIN)
6818 TestIfFriendTouchesBadThing(newx, newy);
6820 /* give the player one last chance (one more frame) to move away */
6821 if (CAN_FALL(element) && direction == MV_DOWN &&
6822 (last_line || (!IS_FREE(x, newy + 1) &&
6823 (!IS_PLAYER(x, newy + 1) ||
6824 game.engine_version < VERSION_IDENT(3,1,1,0)))))
6827 if (pushed_by_player && !game.use_change_when_pushing_bug)
6829 int push_side = MV_DIR_OPPOSITE(direction);
6830 struct PlayerInfo *player = PLAYERINFO(x, y);
6832 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
6833 player->index_bit, push_side);
6834 CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
6835 player->index_bit, push_side);
6838 if (element == EL_EMC_ANDROID && pushed_by_player) /* make another move */
6839 MovDelay[newx][newy] = 1;
6841 CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
6843 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
6846 if (ChangePage[newx][newy] != -1) /* delayed change */
6848 int page = ChangePage[newx][newy];
6849 struct ElementChangeInfo *change = &ei->change_page[page];
6851 ChangePage[newx][newy] = -1;
6853 if (change->can_change)
6855 if (ChangeElement(newx, newy, element, page))
6857 if (change->post_change_function)
6858 change->post_change_function(newx, newy);
6862 if (change->has_action)
6863 ExecuteCustomElementAction(newx, newy, element, page);
6867 TestIfElementHitsCustomElement(newx, newy, direction);
6868 TestIfPlayerTouchesCustomElement(newx, newy);
6869 TestIfElementTouchesCustomElement(newx, newy);
6871 if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
6872 IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
6873 CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
6874 MV_DIR_OPPOSITE(direction));
6877 int AmoebeNachbarNr(int ax, int ay)
6880 int element = Feld[ax][ay];
6882 static int xy[4][2] =
6890 for (i = 0; i < NUM_DIRECTIONS; i++)
6892 int x = ax + xy[i][0];
6893 int y = ay + xy[i][1];
6895 if (!IN_LEV_FIELD(x, y))
6898 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
6899 group_nr = AmoebaNr[x][y];
6905 void AmoebenVereinigen(int ax, int ay)
6907 int i, x, y, xx, yy;
6908 int new_group_nr = AmoebaNr[ax][ay];
6909 static int xy[4][2] =
6917 if (new_group_nr == 0)
6920 for (i = 0; i < NUM_DIRECTIONS; i++)
6925 if (!IN_LEV_FIELD(x, y))
6928 if ((Feld[x][y] == EL_AMOEBA_FULL ||
6929 Feld[x][y] == EL_BD_AMOEBA ||
6930 Feld[x][y] == EL_AMOEBA_DEAD) &&
6931 AmoebaNr[x][y] != new_group_nr)
6933 int old_group_nr = AmoebaNr[x][y];
6935 if (old_group_nr == 0)
6938 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
6939 AmoebaCnt[old_group_nr] = 0;
6940 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
6941 AmoebaCnt2[old_group_nr] = 0;
6943 SCAN_PLAYFIELD(xx, yy)
6945 if (AmoebaNr[xx][yy] == old_group_nr)
6946 AmoebaNr[xx][yy] = new_group_nr;
6952 void AmoebeUmwandeln(int ax, int ay)
6956 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
6958 int group_nr = AmoebaNr[ax][ay];
6963 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
6964 printf("AmoebeUmwandeln(): This should never happen!\n");
6969 SCAN_PLAYFIELD(x, y)
6971 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
6974 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
6978 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
6979 SND_AMOEBA_TURNING_TO_GEM :
6980 SND_AMOEBA_TURNING_TO_ROCK));
6985 static int xy[4][2] =
6993 for (i = 0; i < NUM_DIRECTIONS; i++)
6998 if (!IN_LEV_FIELD(x, y))
7001 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
7003 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
7004 SND_AMOEBA_TURNING_TO_GEM :
7005 SND_AMOEBA_TURNING_TO_ROCK));
7012 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
7015 int group_nr = AmoebaNr[ax][ay];
7016 boolean done = FALSE;
7021 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
7022 printf("AmoebeUmwandelnBD(): This should never happen!\n");
7027 SCAN_PLAYFIELD(x, y)
7029 if (AmoebaNr[x][y] == group_nr &&
7030 (Feld[x][y] == EL_AMOEBA_DEAD ||
7031 Feld[x][y] == EL_BD_AMOEBA ||
7032 Feld[x][y] == EL_AMOEBA_GROWING))
7035 Feld[x][y] = new_element;
7036 InitField(x, y, FALSE);
7037 DrawLevelField(x, y);
7043 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
7044 SND_BD_AMOEBA_TURNING_TO_ROCK :
7045 SND_BD_AMOEBA_TURNING_TO_GEM));
7048 void AmoebeWaechst(int x, int y)
7050 static unsigned long sound_delay = 0;
7051 static unsigned long sound_delay_value = 0;
7053 if (!MovDelay[x][y]) /* start new growing cycle */
7057 if (DelayReached(&sound_delay, sound_delay_value))
7059 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
7060 sound_delay_value = 30;
7064 if (MovDelay[x][y]) /* wait some time before growing bigger */
7067 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7069 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
7070 6 - MovDelay[x][y]);
7072 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
7075 if (!MovDelay[x][y])
7077 Feld[x][y] = Store[x][y];
7079 DrawLevelField(x, y);
7084 void AmoebaDisappearing(int x, int y)
7086 static unsigned long sound_delay = 0;
7087 static unsigned long sound_delay_value = 0;
7089 if (!MovDelay[x][y]) /* start new shrinking cycle */
7093 if (DelayReached(&sound_delay, sound_delay_value))
7094 sound_delay_value = 30;
7097 if (MovDelay[x][y]) /* wait some time before shrinking */
7100 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7102 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
7103 6 - MovDelay[x][y]);
7105 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
7108 if (!MovDelay[x][y])
7110 Feld[x][y] = EL_EMPTY;
7111 DrawLevelField(x, y);
7113 /* don't let mole enter this field in this cycle;
7114 (give priority to objects falling to this field from above) */
7120 void AmoebeAbleger(int ax, int ay)
7123 int element = Feld[ax][ay];
7124 int graphic = el2img(element);
7125 int newax = ax, neway = ay;
7126 boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
7127 static int xy[4][2] =
7135 if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
7137 Feld[ax][ay] = EL_AMOEBA_DEAD;
7138 DrawLevelField(ax, ay);
7142 if (IS_ANIMATED(graphic))
7143 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7145 if (!MovDelay[ax][ay]) /* start making new amoeba field */
7146 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
7148 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
7151 if (MovDelay[ax][ay])
7155 if (can_drop) /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
7158 int x = ax + xy[start][0];
7159 int y = ay + xy[start][1];
7161 if (!IN_LEV_FIELD(x, y))
7164 if (IS_FREE(x, y) ||
7165 CAN_GROW_INTO(Feld[x][y]) ||
7166 Feld[x][y] == EL_QUICKSAND_EMPTY)
7172 if (newax == ax && neway == ay)
7175 else /* normal or "filled" (BD style) amoeba */
7178 boolean waiting_for_player = FALSE;
7180 for (i = 0; i < NUM_DIRECTIONS; i++)
7182 int j = (start + i) % 4;
7183 int x = ax + xy[j][0];
7184 int y = ay + xy[j][1];
7186 if (!IN_LEV_FIELD(x, y))
7189 if (IS_FREE(x, y) ||
7190 CAN_GROW_INTO(Feld[x][y]) ||
7191 Feld[x][y] == EL_QUICKSAND_EMPTY)
7197 else if (IS_PLAYER(x, y))
7198 waiting_for_player = TRUE;
7201 if (newax == ax && neway == ay) /* amoeba cannot grow */
7203 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
7205 Feld[ax][ay] = EL_AMOEBA_DEAD;
7206 DrawLevelField(ax, ay);
7207 AmoebaCnt[AmoebaNr[ax][ay]]--;
7209 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
7211 if (element == EL_AMOEBA_FULL)
7212 AmoebeUmwandeln(ax, ay);
7213 else if (element == EL_BD_AMOEBA)
7214 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
7219 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
7221 /* amoeba gets larger by growing in some direction */
7223 int new_group_nr = AmoebaNr[ax][ay];
7226 if (new_group_nr == 0)
7228 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
7229 printf("AmoebeAbleger(): This should never happen!\n");
7234 AmoebaNr[newax][neway] = new_group_nr;
7235 AmoebaCnt[new_group_nr]++;
7236 AmoebaCnt2[new_group_nr]++;
7238 /* if amoeba touches other amoeba(s) after growing, unify them */
7239 AmoebenVereinigen(newax, neway);
7241 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
7243 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
7249 if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
7250 (neway == lev_fieldy - 1 && newax != ax))
7252 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
7253 Store[newax][neway] = element;
7255 else if (neway == ay || element == EL_EMC_DRIPPER)
7257 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
7259 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
7263 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
7264 Feld[ax][ay] = EL_AMOEBA_DROPPING;
7265 Store[ax][ay] = EL_AMOEBA_DROP;
7266 ContinueMoving(ax, ay);
7270 DrawLevelField(newax, neway);
7273 void Life(int ax, int ay)
7277 int element = Feld[ax][ay];
7278 int graphic = el2img(element);
7279 int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
7281 boolean changed = FALSE;
7283 if (IS_ANIMATED(graphic))
7284 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7289 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
7290 MovDelay[ax][ay] = life_time;
7292 if (MovDelay[ax][ay]) /* wait some time before next cycle */
7295 if (MovDelay[ax][ay])
7299 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
7301 int xx = ax+x1, yy = ay+y1;
7304 if (!IN_LEV_FIELD(xx, yy))
7307 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
7309 int x = xx+x2, y = yy+y2;
7311 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
7314 if (((Feld[x][y] == element ||
7315 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
7317 (IS_FREE(x, y) && Stop[x][y]))
7321 if (xx == ax && yy == ay) /* field in the middle */
7323 if (nachbarn < life_parameter[0] ||
7324 nachbarn > life_parameter[1])
7326 Feld[xx][yy] = EL_EMPTY;
7328 DrawLevelField(xx, yy);
7329 Stop[xx][yy] = TRUE;
7333 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
7334 { /* free border field */
7335 if (nachbarn >= life_parameter[2] &&
7336 nachbarn <= life_parameter[3])
7338 Feld[xx][yy] = element;
7339 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
7341 DrawLevelField(xx, yy);
7342 Stop[xx][yy] = TRUE;
7349 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
7350 SND_GAME_OF_LIFE_GROWING);
7353 static void InitRobotWheel(int x, int y)
7355 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
7358 static void RunRobotWheel(int x, int y)
7360 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
7363 static void StopRobotWheel(int x, int y)
7365 if (ZX == x && ZY == y)
7369 static void InitTimegateWheel(int x, int y)
7371 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
7374 static void RunTimegateWheel(int x, int y)
7376 PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
7379 static void InitMagicBallDelay(int x, int y)
7382 ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
7384 ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
7388 static void ActivateMagicBall(int bx, int by)
7392 if (level.ball_random)
7394 int pos_border = RND(8); /* select one of the eight border elements */
7395 int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
7396 int xx = pos_content % 3;
7397 int yy = pos_content / 3;
7402 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
7403 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
7407 for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
7409 int xx = x - bx + 1;
7410 int yy = y - by + 1;
7412 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
7413 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
7417 game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
7420 void CheckExit(int x, int y)
7422 if (local_player->gems_still_needed > 0 ||
7423 local_player->sokobanfields_still_needed > 0 ||
7424 local_player->lights_still_needed > 0)
7426 int element = Feld[x][y];
7427 int graphic = el2img(element);
7429 if (IS_ANIMATED(graphic))
7430 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7435 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7438 Feld[x][y] = EL_EXIT_OPENING;
7440 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
7443 void CheckExitEM(int x, int y)
7445 if (local_player->gems_still_needed > 0 ||
7446 local_player->sokobanfields_still_needed > 0 ||
7447 local_player->lights_still_needed > 0)
7449 int element = Feld[x][y];
7450 int graphic = el2img(element);
7452 if (IS_ANIMATED(graphic))
7453 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7458 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7461 Feld[x][y] = EL_EM_EXIT_OPENING;
7463 PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
7466 void CheckExitSteel(int x, int y)
7468 if (local_player->gems_still_needed > 0 ||
7469 local_player->sokobanfields_still_needed > 0 ||
7470 local_player->lights_still_needed > 0)
7472 int element = Feld[x][y];
7473 int graphic = el2img(element);
7475 if (IS_ANIMATED(graphic))
7476 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7481 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7484 Feld[x][y] = EL_STEEL_EXIT_OPENING;
7486 PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
7489 void CheckExitSteelEM(int x, int y)
7491 if (local_player->gems_still_needed > 0 ||
7492 local_player->sokobanfields_still_needed > 0 ||
7493 local_player->lights_still_needed > 0)
7495 int element = Feld[x][y];
7496 int graphic = el2img(element);
7498 if (IS_ANIMATED(graphic))
7499 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7504 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7507 Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
7509 PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
7512 void CheckExitSP(int x, int y)
7514 if (local_player->gems_still_needed > 0)
7516 int element = Feld[x][y];
7517 int graphic = el2img(element);
7519 if (IS_ANIMATED(graphic))
7520 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7525 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7528 Feld[x][y] = EL_SP_EXIT_OPENING;
7530 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
7533 static void CloseAllOpenTimegates()
7537 SCAN_PLAYFIELD(x, y)
7539 int element = Feld[x][y];
7541 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
7543 Feld[x][y] = EL_TIMEGATE_CLOSING;
7545 PlayLevelSoundAction(x, y, ACTION_CLOSING);
7550 void DrawTwinkleOnField(int x, int y)
7552 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
7555 if (Feld[x][y] == EL_BD_DIAMOND)
7558 if (MovDelay[x][y] == 0) /* next animation frame */
7559 MovDelay[x][y] = 11 * !GetSimpleRandom(500);
7561 if (MovDelay[x][y] != 0) /* wait some time before next frame */
7565 if (setup.direct_draw && MovDelay[x][y])
7566 SetDrawtoField(DRAW_BUFFERED);
7568 DrawLevelElementAnimation(x, y, Feld[x][y]);
7570 if (MovDelay[x][y] != 0)
7572 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
7573 10 - MovDelay[x][y]);
7575 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
7577 if (setup.direct_draw)
7581 dest_x = FX + SCREENX(x) * TILEX;
7582 dest_y = FY + SCREENY(y) * TILEY;
7584 BlitBitmap(drawto_field, window,
7585 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
7586 SetDrawtoField(DRAW_DIRECT);
7592 void MauerWaechst(int x, int y)
7596 if (!MovDelay[x][y]) /* next animation frame */
7597 MovDelay[x][y] = 3 * delay;
7599 if (MovDelay[x][y]) /* wait some time before next frame */
7603 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7605 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
7606 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
7608 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
7611 if (!MovDelay[x][y])
7613 if (MovDir[x][y] == MV_LEFT)
7615 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
7616 DrawLevelField(x - 1, y);
7618 else if (MovDir[x][y] == MV_RIGHT)
7620 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
7621 DrawLevelField(x + 1, y);
7623 else if (MovDir[x][y] == MV_UP)
7625 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
7626 DrawLevelField(x, y - 1);
7630 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
7631 DrawLevelField(x, y + 1);
7634 Feld[x][y] = Store[x][y];
7636 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7637 DrawLevelField(x, y);
7642 void MauerAbleger(int ax, int ay)
7644 int element = Feld[ax][ay];
7645 int graphic = el2img(element);
7646 boolean oben_frei = FALSE, unten_frei = FALSE;
7647 boolean links_frei = FALSE, rechts_frei = FALSE;
7648 boolean oben_massiv = FALSE, unten_massiv = FALSE;
7649 boolean links_massiv = FALSE, rechts_massiv = FALSE;
7650 boolean new_wall = FALSE;
7652 if (IS_ANIMATED(graphic))
7653 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7655 if (!MovDelay[ax][ay]) /* start building new wall */
7656 MovDelay[ax][ay] = 6;
7658 if (MovDelay[ax][ay]) /* wait some time before building new wall */
7661 if (MovDelay[ax][ay])
7665 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
7667 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
7669 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
7671 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
7674 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
7675 element == EL_EXPANDABLE_WALL_ANY)
7679 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
7680 Store[ax][ay-1] = element;
7681 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
7682 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
7683 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
7684 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
7689 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
7690 Store[ax][ay+1] = element;
7691 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
7692 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
7693 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
7694 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
7699 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7700 element == EL_EXPANDABLE_WALL_ANY ||
7701 element == EL_EXPANDABLE_WALL ||
7702 element == EL_BD_EXPANDABLE_WALL)
7706 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
7707 Store[ax-1][ay] = element;
7708 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
7709 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
7710 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
7711 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
7717 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
7718 Store[ax+1][ay] = element;
7719 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
7720 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
7721 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
7722 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
7727 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
7728 DrawLevelField(ax, ay);
7730 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
7732 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
7733 unten_massiv = TRUE;
7734 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
7735 links_massiv = TRUE;
7736 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
7737 rechts_massiv = TRUE;
7739 if (((oben_massiv && unten_massiv) ||
7740 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7741 element == EL_EXPANDABLE_WALL) &&
7742 ((links_massiv && rechts_massiv) ||
7743 element == EL_EXPANDABLE_WALL_VERTICAL))
7744 Feld[ax][ay] = EL_WALL;
7747 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
7750 void MauerAblegerStahl(int ax, int ay)
7752 int element = Feld[ax][ay];
7753 int graphic = el2img(element);
7754 boolean oben_frei = FALSE, unten_frei = FALSE;
7755 boolean links_frei = FALSE, rechts_frei = FALSE;
7756 boolean oben_massiv = FALSE, unten_massiv = FALSE;
7757 boolean links_massiv = FALSE, rechts_massiv = FALSE;
7758 boolean new_wall = FALSE;
7760 if (IS_ANIMATED(graphic))
7761 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7763 if (!MovDelay[ax][ay]) /* start building new wall */
7764 MovDelay[ax][ay] = 6;
7766 if (MovDelay[ax][ay]) /* wait some time before building new wall */
7769 if (MovDelay[ax][ay])
7773 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
7775 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
7777 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
7779 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
7782 if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
7783 element == EL_EXPANDABLE_STEELWALL_ANY)
7787 Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
7788 Store[ax][ay-1] = element;
7789 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
7790 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
7791 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
7792 IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
7797 Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
7798 Store[ax][ay+1] = element;
7799 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
7800 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
7801 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
7802 IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
7807 if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
7808 element == EL_EXPANDABLE_STEELWALL_ANY)
7812 Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
7813 Store[ax-1][ay] = element;
7814 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
7815 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
7816 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
7817 IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
7823 Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
7824 Store[ax+1][ay] = element;
7825 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
7826 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
7827 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
7828 IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
7833 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
7835 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
7836 unten_massiv = TRUE;
7837 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
7838 links_massiv = TRUE;
7839 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
7840 rechts_massiv = TRUE;
7842 if (((oben_massiv && unten_massiv) ||
7843 element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
7844 ((links_massiv && rechts_massiv) ||
7845 element == EL_EXPANDABLE_STEELWALL_VERTICAL))
7846 Feld[ax][ay] = EL_WALL;
7849 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
7852 void CheckForDragon(int x, int y)
7855 boolean dragon_found = FALSE;
7856 static int xy[4][2] =
7864 for (i = 0; i < NUM_DIRECTIONS; i++)
7866 for (j = 0; j < 4; j++)
7868 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7870 if (IN_LEV_FIELD(xx, yy) &&
7871 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
7873 if (Feld[xx][yy] == EL_DRAGON)
7874 dragon_found = TRUE;
7883 for (i = 0; i < NUM_DIRECTIONS; i++)
7885 for (j = 0; j < 3; j++)
7887 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7889 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
7891 Feld[xx][yy] = EL_EMPTY;
7892 DrawLevelField(xx, yy);
7901 static void InitBuggyBase(int x, int y)
7903 int element = Feld[x][y];
7904 int activating_delay = FRAMES_PER_SECOND / 4;
7907 (element == EL_SP_BUGGY_BASE ?
7908 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
7909 element == EL_SP_BUGGY_BASE_ACTIVATING ?
7911 element == EL_SP_BUGGY_BASE_ACTIVE ?
7912 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
7915 static void WarnBuggyBase(int x, int y)
7918 static int xy[4][2] =
7926 for (i = 0; i < NUM_DIRECTIONS; i++)
7928 int xx = x + xy[i][0];
7929 int yy = y + xy[i][1];
7931 if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
7933 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
7940 static void InitTrap(int x, int y)
7942 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
7945 static void ActivateTrap(int x, int y)
7947 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
7950 static void ChangeActiveTrap(int x, int y)
7952 int graphic = IMG_TRAP_ACTIVE;
7954 /* if new animation frame was drawn, correct crumbled sand border */
7955 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
7956 DrawLevelFieldCrumbledSand(x, y);
7959 static int getSpecialActionElement(int element, int number, int base_element)
7961 return (element != EL_EMPTY ? element :
7962 number != -1 ? base_element + number - 1 :
7966 static int getModifiedActionNumber(int value_old, int operator, int operand,
7967 int value_min, int value_max)
7969 int value_new = (operator == CA_MODE_SET ? operand :
7970 operator == CA_MODE_ADD ? value_old + operand :
7971 operator == CA_MODE_SUBTRACT ? value_old - operand :
7972 operator == CA_MODE_MULTIPLY ? value_old * operand :
7973 operator == CA_MODE_DIVIDE ? value_old / MAX(1, operand) :
7974 operator == CA_MODE_MODULO ? value_old % MAX(1, operand) :
7977 return (value_new < value_min ? value_min :
7978 value_new > value_max ? value_max :
7982 static void ExecuteCustomElementAction(int x, int y, int element, int page)
7984 struct ElementInfo *ei = &element_info[element];
7985 struct ElementChangeInfo *change = &ei->change_page[page];
7986 int target_element = change->target_element;
7987 int action_type = change->action_type;
7988 int action_mode = change->action_mode;
7989 int action_arg = change->action_arg;
7992 if (!change->has_action)
7995 /* ---------- determine action paramater values -------------------------- */
7997 int level_time_value =
7998 (level.time > 0 ? TimeLeft :
8001 int action_arg_element =
8002 (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player :
8003 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
8004 action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element :
8007 int action_arg_direction =
8008 (action_arg >= CA_ARG_DIRECTION_LEFT &&
8009 action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
8010 action_arg == CA_ARG_DIRECTION_TRIGGER ?
8011 change->actual_trigger_side :
8012 action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
8013 MV_DIR_OPPOSITE(change->actual_trigger_side) :
8016 int action_arg_number_min =
8017 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
8020 int action_arg_number_max =
8021 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
8022 action_type == CA_SET_LEVEL_GEMS ? 999 :
8023 action_type == CA_SET_LEVEL_TIME ? 9999 :
8024 action_type == CA_SET_LEVEL_SCORE ? 99999 :
8025 action_type == CA_SET_CE_VALUE ? 9999 :
8026 action_type == CA_SET_CE_SCORE ? 9999 :
8029 int action_arg_number_reset =
8030 (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
8031 action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
8032 action_type == CA_SET_LEVEL_TIME ? level.time :
8033 action_type == CA_SET_LEVEL_SCORE ? 0 :
8034 #if USE_NEW_CUSTOM_VALUE
8035 action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
8037 action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
8039 action_type == CA_SET_CE_SCORE ? 0 :
8042 int action_arg_number =
8043 (action_arg <= CA_ARG_MAX ? action_arg :
8044 action_arg >= CA_ARG_SPEED_NOT_MOVING &&
8045 action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
8046 action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
8047 action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
8048 action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
8049 action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
8050 #if USE_NEW_CUSTOM_VALUE
8051 action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
8053 action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
8055 action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
8056 action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
8057 action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
8058 action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
8059 action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
8060 action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
8061 action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
8062 action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
8063 action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
8064 action_arg == CA_ARG_ELEMENT_NR_TARGET ? change->target_element :
8065 action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
8068 int action_arg_number_old =
8069 (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
8070 action_type == CA_SET_LEVEL_TIME ? TimeLeft :
8071 action_type == CA_SET_LEVEL_SCORE ? local_player->score :
8072 action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
8073 action_type == CA_SET_CE_SCORE ? ei->collect_score :
8076 int action_arg_number_new =
8077 getModifiedActionNumber(action_arg_number_old,
8078 action_mode, action_arg_number,
8079 action_arg_number_min, action_arg_number_max);
8081 int trigger_player_bits =
8082 (change->actual_trigger_player >= EL_PLAYER_1 &&
8083 change->actual_trigger_player <= EL_PLAYER_4 ?
8084 (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
8087 int action_arg_player_bits =
8088 (action_arg >= CA_ARG_PLAYER_1 &&
8089 action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
8090 action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
8093 /* ---------- execute action -------------------------------------------- */
8095 switch (action_type)
8102 /* ---------- level actions ------------------------------------------- */
8104 case CA_RESTART_LEVEL:
8106 game.restart_level = TRUE;
8111 case CA_SHOW_ENVELOPE:
8113 int element = getSpecialActionElement(action_arg_element,
8114 action_arg_number, EL_ENVELOPE_1);
8116 if (IS_ENVELOPE(element))
8117 local_player->show_envelope = element;
8122 case CA_SET_LEVEL_TIME:
8124 if (level.time > 0) /* only modify limited time value */
8126 TimeLeft = action_arg_number_new;
8128 DrawGameValue_Time(TimeLeft);
8130 if (!TimeLeft && setup.time_limit)
8131 for (i = 0; i < MAX_PLAYERS; i++)
8132 KillPlayer(&stored_player[i]);
8138 case CA_SET_LEVEL_SCORE:
8140 local_player->score = action_arg_number_new;
8142 DrawGameValue_Score(local_player->score);
8147 case CA_SET_LEVEL_GEMS:
8149 local_player->gems_still_needed = action_arg_number_new;
8151 DrawGameValue_Emeralds(local_player->gems_still_needed);
8156 #if !USE_PLAYER_GRAVITY
8157 case CA_SET_LEVEL_GRAVITY:
8159 game.gravity = (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
8160 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
8161 action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
8167 case CA_SET_LEVEL_WIND:
8169 game.wind_direction = action_arg_direction;
8174 /* ---------- player actions ------------------------------------------ */
8176 case CA_MOVE_PLAYER:
8178 /* automatically move to the next field in specified direction */
8179 for (i = 0; i < MAX_PLAYERS; i++)
8180 if (trigger_player_bits & (1 << i))
8181 stored_player[i].programmed_action = action_arg_direction;
8186 case CA_EXIT_PLAYER:
8188 for (i = 0; i < MAX_PLAYERS; i++)
8189 if (action_arg_player_bits & (1 << i))
8190 PlayerWins(&stored_player[i]);
8195 case CA_KILL_PLAYER:
8197 for (i = 0; i < MAX_PLAYERS; i++)
8198 if (action_arg_player_bits & (1 << i))
8199 KillPlayer(&stored_player[i]);
8204 case CA_SET_PLAYER_KEYS:
8206 int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
8207 int element = getSpecialActionElement(action_arg_element,
8208 action_arg_number, EL_KEY_1);
8210 if (IS_KEY(element))
8212 for (i = 0; i < MAX_PLAYERS; i++)
8214 if (trigger_player_bits & (1 << i))
8216 stored_player[i].key[KEY_NR(element)] = key_state;
8218 DrawGameDoorValues();
8226 case CA_SET_PLAYER_SPEED:
8228 for (i = 0; i < MAX_PLAYERS; i++)
8230 if (trigger_player_bits & (1 << i))
8232 int move_stepsize = TILEX / stored_player[i].move_delay_value;
8234 if (action_arg == CA_ARG_SPEED_FASTER &&
8235 stored_player[i].cannot_move)
8237 action_arg_number = STEPSIZE_VERY_SLOW;
8239 else if (action_arg == CA_ARG_SPEED_SLOWER ||
8240 action_arg == CA_ARG_SPEED_FASTER)
8242 action_arg_number = 2;
8243 action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
8246 else if (action_arg == CA_ARG_NUMBER_RESET)
8248 action_arg_number = level.initial_player_stepsize[i];
8252 getModifiedActionNumber(move_stepsize,
8255 action_arg_number_min,
8256 action_arg_number_max);
8258 SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
8265 case CA_SET_PLAYER_SHIELD:
8267 for (i = 0; i < MAX_PLAYERS; i++)
8269 if (trigger_player_bits & (1 << i))
8271 if (action_arg == CA_ARG_SHIELD_OFF)
8273 stored_player[i].shield_normal_time_left = 0;
8274 stored_player[i].shield_deadly_time_left = 0;
8276 else if (action_arg == CA_ARG_SHIELD_NORMAL)
8278 stored_player[i].shield_normal_time_left = 999999;
8280 else if (action_arg == CA_ARG_SHIELD_DEADLY)
8282 stored_player[i].shield_normal_time_left = 999999;
8283 stored_player[i].shield_deadly_time_left = 999999;
8291 #if USE_PLAYER_GRAVITY
8292 case CA_SET_PLAYER_GRAVITY:
8294 for (i = 0; i < MAX_PLAYERS; i++)
8296 if (trigger_player_bits & (1 << i))
8298 stored_player[i].gravity =
8299 (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
8300 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
8301 action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
8302 stored_player[i].gravity);
8310 case CA_SET_PLAYER_ARTWORK:
8312 for (i = 0; i < MAX_PLAYERS; i++)
8314 if (trigger_player_bits & (1 << i))
8316 int artwork_element = action_arg_element;
8318 if (action_arg == CA_ARG_ELEMENT_RESET)
8320 (level.use_artwork_element[i] ? level.artwork_element[i] :
8321 stored_player[i].element_nr);
8323 #if USE_GFX_RESET_PLAYER_ARTWORK
8324 if (stored_player[i].artwork_element != artwork_element)
8325 stored_player[i].Frame = 0;
8328 stored_player[i].artwork_element = artwork_element;
8330 SetPlayerWaiting(&stored_player[i], FALSE);
8332 /* set number of special actions for bored and sleeping animation */
8333 stored_player[i].num_special_action_bored =
8334 get_num_special_action(artwork_element,
8335 ACTION_BORING_1, ACTION_BORING_LAST);
8336 stored_player[i].num_special_action_sleeping =
8337 get_num_special_action(artwork_element,
8338 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
8345 /* ---------- CE actions ---------------------------------------------- */
8347 case CA_SET_CE_VALUE:
8349 #if USE_NEW_CUSTOM_VALUE
8350 int last_ce_value = CustomValue[x][y];
8352 CustomValue[x][y] = action_arg_number_new;
8354 if (CustomValue[x][y] != last_ce_value)
8356 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
8357 CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
8359 if (CustomValue[x][y] == 0)
8361 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
8362 CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
8370 case CA_SET_CE_SCORE:
8372 #if USE_NEW_CUSTOM_VALUE
8373 int last_ce_score = ei->collect_score;
8375 ei->collect_score = action_arg_number_new;
8377 if (ei->collect_score != last_ce_score)
8379 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
8380 CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
8382 if (ei->collect_score == 0)
8386 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
8387 CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
8390 This is a very special case that seems to be a mixture between
8391 CheckElementChange() and CheckTriggeredElementChange(): while
8392 the first one only affects single elements that are triggered
8393 directly, the second one affects multiple elements in the playfield
8394 that are triggered indirectly by another element. This is a third
8395 case: Changing the CE score always affects multiple identical CEs,
8396 so every affected CE must be checked, not only the single CE for
8397 which the CE score was changed in the first place (as every instance
8398 of that CE shares the same CE score, and therefore also can change)!
8400 SCAN_PLAYFIELD(xx, yy)
8402 if (Feld[xx][yy] == element)
8403 CheckElementChange(xx, yy, element, EL_UNDEFINED,
8404 CE_SCORE_GETS_ZERO);
8413 /* ---------- engine actions ------------------------------------------ */
8415 case CA_SET_ENGINE_SCAN_MODE:
8417 InitPlayfieldScanMode(action_arg);
8427 static void CreateFieldExt(int x, int y, int element, boolean is_change)
8429 int old_element = Feld[x][y];
8430 int new_element = get_element_from_group_element(element);
8431 int previous_move_direction = MovDir[x][y];
8432 #if USE_NEW_CUSTOM_VALUE
8433 int last_ce_value = CustomValue[x][y];
8435 boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
8436 boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
8437 boolean add_player_onto_element = (new_element_is_player &&
8438 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
8439 /* this breaks SnakeBite when a snake is
8440 halfway through a door that closes */
8441 /* NOW FIXED AT LEVEL INIT IN files.c */
8442 new_element != EL_SOKOBAN_FIELD_PLAYER &&
8444 IS_WALKABLE(old_element));
8447 /* check if element under the player changes from accessible to unaccessible
8448 (needed for special case of dropping element which then changes) */
8449 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
8450 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
8458 if (!add_player_onto_element)
8460 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
8461 RemoveMovingField(x, y);
8465 Feld[x][y] = new_element;
8467 #if !USE_GFX_RESET_GFX_ANIMATION
8468 ResetGfxAnimation(x, y);
8469 ResetRandomAnimationValue(x, y);
8472 if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
8473 MovDir[x][y] = previous_move_direction;
8475 #if USE_NEW_CUSTOM_VALUE
8476 if (element_info[new_element].use_last_ce_value)
8477 CustomValue[x][y] = last_ce_value;
8480 InitField_WithBug1(x, y, FALSE);
8482 new_element = Feld[x][y]; /* element may have changed */
8484 #if USE_GFX_RESET_GFX_ANIMATION
8485 ResetGfxAnimation(x, y);
8486 ResetRandomAnimationValue(x, y);
8489 DrawLevelField(x, y);
8491 if (GFX_CRUMBLED(new_element))
8492 DrawLevelFieldCrumbledSandNeighbours(x, y);
8496 /* check if element under the player changes from accessible to unaccessible
8497 (needed for special case of dropping element which then changes) */
8498 /* (must be checked after creating new element for walkable group elements) */
8499 #if USE_FIX_KILLED_BY_NON_WALKABLE
8500 if (IS_PLAYER(x, y) && !player_explosion_protected &&
8501 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
8508 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
8509 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
8518 /* "ChangeCount" not set yet to allow "entered by player" change one time */
8519 if (new_element_is_player)
8520 RelocatePlayer(x, y, new_element);
8523 ChangeCount[x][y]++; /* count number of changes in the same frame */
8525 TestIfBadThingTouchesPlayer(x, y);
8526 TestIfPlayerTouchesCustomElement(x, y);
8527 TestIfElementTouchesCustomElement(x, y);
8530 static void CreateField(int x, int y, int element)
8532 CreateFieldExt(x, y, element, FALSE);
8535 static void CreateElementFromChange(int x, int y, int element)
8537 element = GET_VALID_RUNTIME_ELEMENT(element);
8539 #if USE_STOP_CHANGED_ELEMENTS
8540 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
8542 int old_element = Feld[x][y];
8544 /* prevent changed element from moving in same engine frame
8545 unless both old and new element can either fall or move */
8546 if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
8547 (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
8552 CreateFieldExt(x, y, element, TRUE);
8555 static boolean ChangeElement(int x, int y, int element, int page)
8557 struct ElementInfo *ei = &element_info[element];
8558 struct ElementChangeInfo *change = &ei->change_page[page];
8559 int ce_value = CustomValue[x][y];
8560 int ce_score = ei->collect_score;
8562 int old_element = Feld[x][y];
8564 /* always use default change event to prevent running into a loop */
8565 if (ChangeEvent[x][y] == -1)
8566 ChangeEvent[x][y] = CE_DELAY;
8568 if (ChangeEvent[x][y] == CE_DELAY)
8570 /* reset actual trigger element, trigger player and action element */
8571 change->actual_trigger_element = EL_EMPTY;
8572 change->actual_trigger_player = EL_PLAYER_1;
8573 change->actual_trigger_side = CH_SIDE_NONE;
8574 change->actual_trigger_ce_value = 0;
8575 change->actual_trigger_ce_score = 0;
8578 /* do not change elements more than a specified maximum number of changes */
8579 if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
8582 ChangeCount[x][y]++; /* count number of changes in the same frame */
8584 if (change->explode)
8591 if (change->use_target_content)
8593 boolean complete_replace = TRUE;
8594 boolean can_replace[3][3];
8597 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
8600 boolean is_walkable;
8601 boolean is_diggable;
8602 boolean is_collectible;
8603 boolean is_removable;
8604 boolean is_destructible;
8605 int ex = x + xx - 1;
8606 int ey = y + yy - 1;
8607 int content_element = change->target_content.e[xx][yy];
8610 can_replace[xx][yy] = TRUE;
8612 if (ex == x && ey == y) /* do not check changing element itself */
8615 if (content_element == EL_EMPTY_SPACE)
8617 can_replace[xx][yy] = FALSE; /* do not replace border with space */
8622 if (!IN_LEV_FIELD(ex, ey))
8624 can_replace[xx][yy] = FALSE;
8625 complete_replace = FALSE;
8632 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
8633 e = MovingOrBlocked2Element(ex, ey);
8635 is_empty = (IS_FREE(ex, ey) ||
8636 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
8638 is_walkable = (is_empty || IS_WALKABLE(e));
8639 is_diggable = (is_empty || IS_DIGGABLE(e));
8640 is_collectible = (is_empty || IS_COLLECTIBLE(e));
8641 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
8642 is_removable = (is_diggable || is_collectible);
8644 can_replace[xx][yy] =
8645 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
8646 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
8647 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
8648 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
8649 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
8650 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
8651 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
8653 if (!can_replace[xx][yy])
8654 complete_replace = FALSE;
8657 if (!change->only_if_complete || complete_replace)
8659 boolean something_has_changed = FALSE;
8661 if (change->only_if_complete && change->use_random_replace &&
8662 RND(100) < change->random_percentage)
8665 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
8667 int ex = x + xx - 1;
8668 int ey = y + yy - 1;
8669 int content_element;
8671 if (can_replace[xx][yy] && (!change->use_random_replace ||
8672 RND(100) < change->random_percentage))
8674 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
8675 RemoveMovingField(ex, ey);
8677 ChangeEvent[ex][ey] = ChangeEvent[x][y];
8679 content_element = change->target_content.e[xx][yy];
8680 target_element = GET_TARGET_ELEMENT(element, content_element, change,
8681 ce_value, ce_score);
8683 CreateElementFromChange(ex, ey, target_element);
8685 something_has_changed = TRUE;
8687 /* for symmetry reasons, freeze newly created border elements */
8688 if (ex != x || ey != y)
8689 Stop[ex][ey] = TRUE; /* no more moving in this frame */
8693 if (something_has_changed)
8695 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
8696 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
8702 target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
8703 ce_value, ce_score);
8705 if (element == EL_DIAGONAL_GROWING ||
8706 element == EL_DIAGONAL_SHRINKING)
8708 target_element = Store[x][y];
8710 Store[x][y] = EL_EMPTY;
8713 CreateElementFromChange(x, y, target_element);
8715 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
8716 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
8719 /* this uses direct change before indirect change */
8720 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
8725 #if USE_NEW_DELAYED_ACTION
8727 static void HandleElementChange(int x, int y, int page)
8729 int element = MovingOrBlocked2Element(x, y);
8730 struct ElementInfo *ei = &element_info[element];
8731 struct ElementChangeInfo *change = &ei->change_page[page];
8734 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
8735 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
8738 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
8739 x, y, element, element_info[element].token_name);
8740 printf("HandleElementChange(): This should never happen!\n");
8745 /* this can happen with classic bombs on walkable, changing elements */
8746 if (!CAN_CHANGE_OR_HAS_ACTION(element))
8749 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
8750 ChangeDelay[x][y] = 0;
8756 if (ChangeDelay[x][y] == 0) /* initialize element change */
8758 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
8760 if (change->can_change)
8762 ResetGfxAnimation(x, y);
8763 ResetRandomAnimationValue(x, y);
8765 if (change->pre_change_function)
8766 change->pre_change_function(x, y);
8770 ChangeDelay[x][y]--;
8772 if (ChangeDelay[x][y] != 0) /* continue element change */
8774 if (change->can_change)
8776 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8778 if (IS_ANIMATED(graphic))
8779 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8781 if (change->change_function)
8782 change->change_function(x, y);
8785 else /* finish element change */
8787 if (ChangePage[x][y] != -1) /* remember page from delayed change */
8789 page = ChangePage[x][y];
8790 ChangePage[x][y] = -1;
8792 change = &ei->change_page[page];
8795 if (IS_MOVING(x, y)) /* never change a running system ;-) */
8797 ChangeDelay[x][y] = 1; /* try change after next move step */
8798 ChangePage[x][y] = page; /* remember page to use for change */
8803 if (change->can_change)
8805 if (ChangeElement(x, y, element, page))
8807 if (change->post_change_function)
8808 change->post_change_function(x, y);
8812 if (change->has_action)
8813 ExecuteCustomElementAction(x, y, element, page);
8819 static void HandleElementChange(int x, int y, int page)
8821 int element = MovingOrBlocked2Element(x, y);
8822 struct ElementInfo *ei = &element_info[element];
8823 struct ElementChangeInfo *change = &ei->change_page[page];
8826 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
8829 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
8830 x, y, element, element_info[element].token_name);
8831 printf("HandleElementChange(): This should never happen!\n");
8836 /* this can happen with classic bombs on walkable, changing elements */
8837 if (!CAN_CHANGE(element))
8840 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
8841 ChangeDelay[x][y] = 0;
8847 if (ChangeDelay[x][y] == 0) /* initialize element change */
8849 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
8851 ResetGfxAnimation(x, y);
8852 ResetRandomAnimationValue(x, y);
8854 if (change->pre_change_function)
8855 change->pre_change_function(x, y);
8858 ChangeDelay[x][y]--;
8860 if (ChangeDelay[x][y] != 0) /* continue element change */
8862 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8864 if (IS_ANIMATED(graphic))
8865 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8867 if (change->change_function)
8868 change->change_function(x, y);
8870 else /* finish element change */
8872 if (ChangePage[x][y] != -1) /* remember page from delayed change */
8874 page = ChangePage[x][y];
8875 ChangePage[x][y] = -1;
8877 change = &ei->change_page[page];
8880 if (IS_MOVING(x, y)) /* never change a running system ;-) */
8882 ChangeDelay[x][y] = 1; /* try change after next move step */
8883 ChangePage[x][y] = page; /* remember page to use for change */
8888 if (ChangeElement(x, y, element, page))
8890 if (change->post_change_function)
8891 change->post_change_function(x, y);
8898 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
8899 int trigger_element,
8905 boolean change_done_any = FALSE;
8906 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
8909 if (!(trigger_events[trigger_element][trigger_event]))
8913 printf("::: CheckTriggeredElementChangeExt %d ... [%d, %d, %d, '%s']\n",
8914 trigger_event, recursion_loop_depth, recursion_loop_detected,
8915 recursion_loop_element, EL_NAME(recursion_loop_element));
8918 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
8920 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8922 int element = EL_CUSTOM_START + i;
8923 boolean change_done = FALSE;
8926 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
8927 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
8930 for (p = 0; p < element_info[element].num_change_pages; p++)
8932 struct ElementChangeInfo *change = &element_info[element].change_page[p];
8934 if (change->can_change_or_has_action &&
8935 change->has_event[trigger_event] &&
8936 change->trigger_side & trigger_side &&
8937 change->trigger_player & trigger_player &&
8938 change->trigger_page & trigger_page_bits &&
8939 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
8941 change->actual_trigger_element = trigger_element;
8942 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
8943 change->actual_trigger_side = trigger_side;
8944 change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
8945 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
8947 if ((change->can_change && !change_done) || change->has_action)
8951 SCAN_PLAYFIELD(x, y)
8953 if (Feld[x][y] == element)
8955 if (change->can_change && !change_done)
8957 ChangeDelay[x][y] = 1;
8958 ChangeEvent[x][y] = trigger_event;
8960 HandleElementChange(x, y, p);
8962 #if USE_NEW_DELAYED_ACTION
8963 else if (change->has_action)
8965 ExecuteCustomElementAction(x, y, element, p);
8966 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8969 if (change->has_action)
8971 ExecuteCustomElementAction(x, y, element, p);
8972 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8978 if (change->can_change)
8981 change_done_any = TRUE;
8988 RECURSION_LOOP_DETECTION_END();
8990 return change_done_any;
8993 static boolean CheckElementChangeExt(int x, int y,
8995 int trigger_element,
9000 boolean change_done = FALSE;
9003 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
9004 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
9007 if (Feld[x][y] == EL_BLOCKED)
9009 Blocked2Moving(x, y, &x, &y);
9010 element = Feld[x][y];
9014 /* check if element has already changed */
9015 if (Feld[x][y] != element)
9018 /* check if element has already changed or is about to change after moving */
9019 if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
9020 Feld[x][y] != element) ||
9022 (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
9023 (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
9024 ChangePage[x][y] != -1)))
9029 printf("::: CheckElementChangeExt %d ... [%d, %d, %d, '%s']\n",
9030 trigger_event, recursion_loop_depth, recursion_loop_detected,
9031 recursion_loop_element, EL_NAME(recursion_loop_element));
9034 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
9036 for (p = 0; p < element_info[element].num_change_pages; p++)
9038 struct ElementChangeInfo *change = &element_info[element].change_page[p];
9040 /* check trigger element for all events where the element that is checked
9041 for changing interacts with a directly adjacent element -- this is
9042 different to element changes that affect other elements to change on the
9043 whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
9044 boolean check_trigger_element =
9045 (trigger_event == CE_TOUCHING_X ||
9046 trigger_event == CE_HITTING_X ||
9047 trigger_event == CE_HIT_BY_X ||
9049 /* this one was forgotten until 3.2.3 */
9050 trigger_event == CE_DIGGING_X);
9053 if (change->can_change_or_has_action &&
9054 change->has_event[trigger_event] &&
9055 change->trigger_side & trigger_side &&
9056 change->trigger_player & trigger_player &&
9057 (!check_trigger_element ||
9058 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
9060 change->actual_trigger_element = trigger_element;
9061 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
9062 change->actual_trigger_side = trigger_side;
9063 change->actual_trigger_ce_value = CustomValue[x][y];
9064 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
9066 /* special case: trigger element not at (x,y) position for some events */
9067 if (check_trigger_element)
9079 { 0, 0 }, { 0, 0 }, { 0, 0 },
9083 int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
9084 int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
9086 change->actual_trigger_ce_value = CustomValue[xx][yy];
9087 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
9090 if (change->can_change && !change_done)
9092 ChangeDelay[x][y] = 1;
9093 ChangeEvent[x][y] = trigger_event;
9095 HandleElementChange(x, y, p);
9099 #if USE_NEW_DELAYED_ACTION
9100 else if (change->has_action)
9102 ExecuteCustomElementAction(x, y, element, p);
9103 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
9106 if (change->has_action)
9108 ExecuteCustomElementAction(x, y, element, p);
9109 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
9115 RECURSION_LOOP_DETECTION_END();
9120 static void PlayPlayerSound(struct PlayerInfo *player)
9122 int jx = player->jx, jy = player->jy;
9123 int sound_element = player->artwork_element;
9124 int last_action = player->last_action_waiting;
9125 int action = player->action_waiting;
9127 if (player->is_waiting)
9129 if (action != last_action)
9130 PlayLevelSoundElementAction(jx, jy, sound_element, action);
9132 PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
9136 if (action != last_action)
9137 StopSound(element_info[sound_element].sound[last_action]);
9139 if (last_action == ACTION_SLEEPING)
9140 PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
9144 static void PlayAllPlayersSound()
9148 for (i = 0; i < MAX_PLAYERS; i++)
9149 if (stored_player[i].active)
9150 PlayPlayerSound(&stored_player[i]);
9153 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
9155 boolean last_waiting = player->is_waiting;
9156 int move_dir = player->MovDir;
9158 player->dir_waiting = move_dir;
9159 player->last_action_waiting = player->action_waiting;
9163 if (!last_waiting) /* not waiting -> waiting */
9165 player->is_waiting = TRUE;
9167 player->frame_counter_bored =
9169 game.player_boring_delay_fixed +
9170 GetSimpleRandom(game.player_boring_delay_random);
9171 player->frame_counter_sleeping =
9173 game.player_sleeping_delay_fixed +
9174 GetSimpleRandom(game.player_sleeping_delay_random);
9176 InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
9179 if (game.player_sleeping_delay_fixed +
9180 game.player_sleeping_delay_random > 0 &&
9181 player->anim_delay_counter == 0 &&
9182 player->post_delay_counter == 0 &&
9183 FrameCounter >= player->frame_counter_sleeping)
9184 player->is_sleeping = TRUE;
9185 else if (game.player_boring_delay_fixed +
9186 game.player_boring_delay_random > 0 &&
9187 FrameCounter >= player->frame_counter_bored)
9188 player->is_bored = TRUE;
9190 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
9191 player->is_bored ? ACTION_BORING :
9194 if (player->is_sleeping && player->use_murphy)
9196 /* special case for sleeping Murphy when leaning against non-free tile */
9198 if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
9199 (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
9200 !IS_MOVING(player->jx - 1, player->jy)))
9202 else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
9203 (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
9204 !IS_MOVING(player->jx + 1, player->jy)))
9205 move_dir = MV_RIGHT;
9207 player->is_sleeping = FALSE;
9209 player->dir_waiting = move_dir;
9212 if (player->is_sleeping)
9214 if (player->num_special_action_sleeping > 0)
9216 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
9218 int last_special_action = player->special_action_sleeping;
9219 int num_special_action = player->num_special_action_sleeping;
9220 int special_action =
9221 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
9222 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
9223 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
9224 last_special_action + 1 : ACTION_SLEEPING);
9225 int special_graphic =
9226 el_act_dir2img(player->artwork_element, special_action, move_dir);
9228 player->anim_delay_counter =
9229 graphic_info[special_graphic].anim_delay_fixed +
9230 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
9231 player->post_delay_counter =
9232 graphic_info[special_graphic].post_delay_fixed +
9233 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
9235 player->special_action_sleeping = special_action;
9238 if (player->anim_delay_counter > 0)
9240 player->action_waiting = player->special_action_sleeping;
9241 player->anim_delay_counter--;
9243 else if (player->post_delay_counter > 0)
9245 player->post_delay_counter--;
9249 else if (player->is_bored)
9251 if (player->num_special_action_bored > 0)
9253 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
9255 int special_action =
9256 ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
9257 int special_graphic =
9258 el_act_dir2img(player->artwork_element, special_action, move_dir);
9260 player->anim_delay_counter =
9261 graphic_info[special_graphic].anim_delay_fixed +
9262 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
9263 player->post_delay_counter =
9264 graphic_info[special_graphic].post_delay_fixed +
9265 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
9267 player->special_action_bored = special_action;
9270 if (player->anim_delay_counter > 0)
9272 player->action_waiting = player->special_action_bored;
9273 player->anim_delay_counter--;
9275 else if (player->post_delay_counter > 0)
9277 player->post_delay_counter--;
9282 else if (last_waiting) /* waiting -> not waiting */
9284 player->is_waiting = FALSE;
9285 player->is_bored = FALSE;
9286 player->is_sleeping = FALSE;
9288 player->frame_counter_bored = -1;
9289 player->frame_counter_sleeping = -1;
9291 player->anim_delay_counter = 0;
9292 player->post_delay_counter = 0;
9294 player->dir_waiting = player->MovDir;
9295 player->action_waiting = ACTION_DEFAULT;
9297 player->special_action_bored = ACTION_DEFAULT;
9298 player->special_action_sleeping = ACTION_DEFAULT;
9302 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
9304 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
9305 int left = player_action & JOY_LEFT;
9306 int right = player_action & JOY_RIGHT;
9307 int up = player_action & JOY_UP;
9308 int down = player_action & JOY_DOWN;
9309 int button1 = player_action & JOY_BUTTON_1;
9310 int button2 = player_action & JOY_BUTTON_2;
9311 int dx = (left ? -1 : right ? 1 : 0);
9312 int dy = (up ? -1 : down ? 1 : 0);
9314 if (!player->active || tape.pausing)
9320 snapped = SnapField(player, dx, dy);
9324 dropped = DropElement(player);
9326 moved = MovePlayer(player, dx, dy);
9329 if (tape.single_step && tape.recording && !tape.pausing)
9331 if (button1 || (dropped && !moved))
9333 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9334 SnapField(player, 0, 0); /* stop snapping */
9338 SetPlayerWaiting(player, FALSE);
9340 return player_action;
9344 /* no actions for this player (no input at player's configured device) */
9346 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
9347 SnapField(player, 0, 0);
9348 CheckGravityMovementWhenNotMoving(player);
9350 if (player->MovPos == 0)
9351 SetPlayerWaiting(player, TRUE);
9353 if (player->MovPos == 0) /* needed for tape.playing */
9354 player->is_moving = FALSE;
9356 player->is_dropping = FALSE;
9357 player->is_dropping_pressed = FALSE;
9358 player->drop_pressed_delay = 0;
9364 static void CheckLevelTime()
9368 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9370 if (level.native_em_level->lev->home == 0) /* all players at home */
9372 PlayerWins(local_player);
9374 AllPlayersGone = TRUE;
9376 level.native_em_level->lev->home = -1;
9379 if (level.native_em_level->ply[0]->alive == 0 &&
9380 level.native_em_level->ply[1]->alive == 0 &&
9381 level.native_em_level->ply[2]->alive == 0 &&
9382 level.native_em_level->ply[3]->alive == 0) /* all dead */
9383 AllPlayersGone = TRUE;
9386 if (TimeFrames >= FRAMES_PER_SECOND)
9391 for (i = 0; i < MAX_PLAYERS; i++)
9393 struct PlayerInfo *player = &stored_player[i];
9395 if (SHIELD_ON(player))
9397 player->shield_normal_time_left--;
9399 if (player->shield_deadly_time_left > 0)
9400 player->shield_deadly_time_left--;
9404 if (!local_player->LevelSolved && !level.use_step_counter)
9412 if (TimeLeft <= 10 && setup.time_limit)
9413 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
9415 DrawGameValue_Time(TimeLeft);
9417 if (!TimeLeft && setup.time_limit)
9419 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9420 level.native_em_level->lev->killed_out_of_time = TRUE;
9422 for (i = 0; i < MAX_PLAYERS; i++)
9423 KillPlayer(&stored_player[i]);
9426 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
9427 DrawGameValue_Time(TimePlayed);
9429 level.native_em_level->lev->time =
9430 (level.time == 0 ? TimePlayed : TimeLeft);
9433 if (tape.recording || tape.playing)
9434 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
9438 void AdvanceFrameAndPlayerCounters(int player_nr)
9442 /* advance frame counters (global frame counter and time frame counter) */
9446 /* advance player counters (counters for move delay, move animation etc.) */
9447 for (i = 0; i < MAX_PLAYERS; i++)
9449 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
9450 int move_delay_value = stored_player[i].move_delay_value;
9451 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
9453 if (!advance_player_counters) /* not all players may be affected */
9456 #if USE_NEW_PLAYER_ANIM
9457 if (move_frames == 0) /* less than one move per game frame */
9459 int stepsize = TILEX / move_delay_value;
9460 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
9461 int count = (stored_player[i].is_moving ?
9462 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
9464 if (count % delay == 0)
9469 stored_player[i].Frame += move_frames;
9471 if (stored_player[i].MovPos != 0)
9472 stored_player[i].StepFrame += move_frames;
9474 if (stored_player[i].move_delay > 0)
9475 stored_player[i].move_delay--;
9477 /* due to bugs in previous versions, counter must count up, not down */
9478 if (stored_player[i].push_delay != -1)
9479 stored_player[i].push_delay++;
9481 if (stored_player[i].drop_delay > 0)
9482 stored_player[i].drop_delay--;
9484 if (stored_player[i].is_dropping_pressed)
9485 stored_player[i].drop_pressed_delay++;
9489 void StartGameActions(boolean init_network_game, boolean record_tape,
9492 unsigned long new_random_seed = InitRND(random_seed);
9495 TapeStartRecording(new_random_seed);
9497 #if defined(NETWORK_AVALIABLE)
9498 if (init_network_game)
9500 SendToServer_StartPlaying();
9511 static unsigned long game_frame_delay = 0;
9512 unsigned long game_frame_delay_value;
9513 byte *recorded_player_action;
9514 byte summarized_player_action = 0;
9515 byte tape_action[MAX_PLAYERS];
9518 /* detect endless loops, caused by custom element programming */
9519 if (recursion_loop_detected && recursion_loop_depth == 0)
9521 char *message = getStringCat3("Internal Error ! Element ",
9522 EL_NAME(recursion_loop_element),
9523 " caused endless loop ! Quit the game ?");
9525 Error(ERR_WARN, "element '%s' caused endless loop in game engine",
9526 EL_NAME(recursion_loop_element));
9528 RequestQuitGameExt(FALSE, level_editor_test_game, message);
9530 recursion_loop_detected = FALSE; /* if game should be continued */
9537 if (game.restart_level)
9538 StartGameActions(options.network, setup.autorecord, NEW_RANDOMIZE);
9540 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9542 if (level.native_em_level->lev->home == 0) /* all players at home */
9544 PlayerWins(local_player);
9546 AllPlayersGone = TRUE;
9548 level.native_em_level->lev->home = -1;
9551 if (level.native_em_level->ply[0]->alive == 0 &&
9552 level.native_em_level->ply[1]->alive == 0 &&
9553 level.native_em_level->ply[2]->alive == 0 &&
9554 level.native_em_level->ply[3]->alive == 0) /* all dead */
9555 AllPlayersGone = TRUE;
9558 if (local_player->LevelSolved)
9561 if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
9564 if (game_status != GAME_MODE_PLAYING) /* status might have changed */
9567 game_frame_delay_value =
9568 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
9570 if (tape.playing && tape.warp_forward && !tape.pausing)
9571 game_frame_delay_value = 0;
9573 /* ---------- main game synchronization point ---------- */
9575 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
9577 if (network_playing && !network_player_action_received)
9579 /* try to get network player actions in time */
9581 #if defined(NETWORK_AVALIABLE)
9582 /* last chance to get network player actions without main loop delay */
9586 /* game was quit by network peer */
9587 if (game_status != GAME_MODE_PLAYING)
9590 if (!network_player_action_received)
9591 return; /* failed to get network player actions in time */
9593 /* do not yet reset "network_player_action_received" (for tape.pausing) */
9599 /* at this point we know that we really continue executing the game */
9601 network_player_action_received = FALSE;
9603 /* when playing tape, read previously recorded player input from tape data */
9604 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
9607 /* TapePlayAction() may return NULL when toggling to "pause before death" */
9612 if (tape.set_centered_player)
9614 game.centered_player_nr_next = tape.centered_player_nr_next;
9615 game.set_centered_player = TRUE;
9618 for (i = 0; i < MAX_PLAYERS; i++)
9620 summarized_player_action |= stored_player[i].action;
9622 if (!network_playing)
9623 stored_player[i].effective_action = stored_player[i].action;
9626 #if defined(NETWORK_AVALIABLE)
9627 if (network_playing)
9628 SendToServer_MovePlayer(summarized_player_action);
9631 if (!options.network && !setup.team_mode)
9632 local_player->effective_action = summarized_player_action;
9634 if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
9636 for (i = 0; i < MAX_PLAYERS; i++)
9637 stored_player[i].effective_action =
9638 (i == game.centered_player_nr ? summarized_player_action : 0);
9641 if (recorded_player_action != NULL)
9642 for (i = 0; i < MAX_PLAYERS; i++)
9643 stored_player[i].effective_action = recorded_player_action[i];
9645 for (i = 0; i < MAX_PLAYERS; i++)
9647 tape_action[i] = stored_player[i].effective_action;
9649 /* (this can only happen in the R'n'D game engine) */
9650 if (tape.recording && tape_action[i] && !tape.player_participates[i])
9651 tape.player_participates[i] = TRUE; /* player just appeared from CE */
9654 /* only record actions from input devices, but not programmed actions */
9656 TapeRecordAction(tape_action);
9658 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9660 GameActions_EM_Main();
9668 void GameActions_EM_Main()
9670 byte effective_action[MAX_PLAYERS];
9671 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
9674 for (i = 0; i < MAX_PLAYERS; i++)
9675 effective_action[i] = stored_player[i].effective_action;
9677 GameActions_EM(effective_action, warp_mode);
9681 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
9684 void GameActions_RND()
9686 int magic_wall_x = 0, magic_wall_y = 0;
9687 int i, x, y, element, graphic;
9689 InitPlayfieldScanModeVars();
9691 #if USE_ONE_MORE_CHANGE_PER_FRAME
9692 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
9694 SCAN_PLAYFIELD(x, y)
9696 ChangeCount[x][y] = 0;
9697 ChangeEvent[x][y] = -1;
9702 if (game.set_centered_player)
9704 boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
9706 /* switching to "all players" only possible if all players fit to screen */
9707 if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
9709 game.centered_player_nr_next = game.centered_player_nr;
9710 game.set_centered_player = FALSE;
9713 /* do not switch focus to non-existing (or non-active) player */
9714 if (game.centered_player_nr_next >= 0 &&
9715 !stored_player[game.centered_player_nr_next].active)
9717 game.centered_player_nr_next = game.centered_player_nr;
9718 game.set_centered_player = FALSE;
9722 if (game.set_centered_player &&
9723 ScreenMovPos == 0) /* screen currently aligned at tile position */
9727 if (game.centered_player_nr_next == -1)
9729 setScreenCenteredToAllPlayers(&sx, &sy);
9733 sx = stored_player[game.centered_player_nr_next].jx;
9734 sy = stored_player[game.centered_player_nr_next].jy;
9737 game.centered_player_nr = game.centered_player_nr_next;
9738 game.set_centered_player = FALSE;
9740 DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
9741 DrawGameDoorValues();
9744 for (i = 0; i < MAX_PLAYERS; i++)
9746 int actual_player_action = stored_player[i].effective_action;
9749 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
9750 - rnd_equinox_tetrachloride 048
9751 - rnd_equinox_tetrachloride_ii 096
9752 - rnd_emanuel_schmieg 002
9753 - doctor_sloan_ww 001, 020
9755 if (stored_player[i].MovPos == 0)
9756 CheckGravityMovement(&stored_player[i]);
9759 /* overwrite programmed action with tape action */
9760 if (stored_player[i].programmed_action)
9761 actual_player_action = stored_player[i].programmed_action;
9763 PlayerActions(&stored_player[i], actual_player_action);
9765 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
9768 ScrollScreen(NULL, SCROLL_GO_ON);
9770 /* for backwards compatibility, the following code emulates a fixed bug that
9771 occured when pushing elements (causing elements that just made their last
9772 pushing step to already (if possible) make their first falling step in the
9773 same game frame, which is bad); this code is also needed to use the famous
9774 "spring push bug" which is used in older levels and might be wanted to be
9775 used also in newer levels, but in this case the buggy pushing code is only
9776 affecting the "spring" element and no other elements */
9778 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
9780 for (i = 0; i < MAX_PLAYERS; i++)
9782 struct PlayerInfo *player = &stored_player[i];
9786 if (player->active && player->is_pushing && player->is_moving &&
9788 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
9789 Feld[x][y] == EL_SPRING))
9791 ContinueMoving(x, y);
9793 /* continue moving after pushing (this is actually a bug) */
9794 if (!IS_MOVING(x, y))
9802 SCAN_PLAYFIELD(x, y)
9804 ChangeCount[x][y] = 0;
9805 ChangeEvent[x][y] = -1;
9807 /* this must be handled before main playfield loop */
9808 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
9811 if (MovDelay[x][y] <= 0)
9815 #if USE_NEW_SNAP_DELAY
9816 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
9819 if (MovDelay[x][y] <= 0)
9822 DrawLevelField(x, y);
9824 TestIfElementTouchesCustomElement(x, y); /* for empty space */
9830 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
9832 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
9833 printf("GameActions(): This should never happen!\n");
9835 ChangePage[x][y] = -1;
9840 if (WasJustMoving[x][y] > 0)
9841 WasJustMoving[x][y]--;
9842 if (WasJustFalling[x][y] > 0)
9843 WasJustFalling[x][y]--;
9844 if (CheckCollision[x][y] > 0)
9845 CheckCollision[x][y]--;
9846 if (CheckImpact[x][y] > 0)
9847 CheckImpact[x][y]--;
9851 /* reset finished pushing action (not done in ContinueMoving() to allow
9852 continuous pushing animation for elements with zero push delay) */
9853 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
9855 ResetGfxAnimation(x, y);
9856 DrawLevelField(x, y);
9860 if (IS_BLOCKED(x, y))
9864 Blocked2Moving(x, y, &oldx, &oldy);
9865 if (!IS_MOVING(oldx, oldy))
9867 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
9868 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
9869 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
9870 printf("GameActions(): This should never happen!\n");
9876 SCAN_PLAYFIELD(x, y)
9878 element = Feld[x][y];
9879 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9881 ResetGfxFrame(x, y, TRUE);
9883 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
9884 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
9885 ResetRandomAnimationValue(x, y);
9887 SetRandomAnimationValue(x, y);
9889 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
9891 if (IS_INACTIVE(element))
9893 if (IS_ANIMATED(graphic))
9894 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9899 /* this may take place after moving, so 'element' may have changed */
9900 if (IS_CHANGING(x, y) &&
9901 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
9903 int page = element_info[element].event_page_nr[CE_DELAY];
9906 HandleElementChange(x, y, page);
9908 if (CAN_CHANGE(element))
9909 HandleElementChange(x, y, page);
9911 if (HAS_ACTION(element))
9912 ExecuteCustomElementAction(x, y, element, page);
9915 element = Feld[x][y];
9916 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9919 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
9923 element = Feld[x][y];
9924 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9926 if (IS_ANIMATED(graphic) &&
9929 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9931 if (IS_GEM(element) || element == EL_SP_INFOTRON)
9932 DrawTwinkleOnField(x, y);
9934 else if ((element == EL_ACID ||
9935 element == EL_EXIT_OPEN ||
9936 element == EL_EM_EXIT_OPEN ||
9937 element == EL_SP_EXIT_OPEN ||
9938 element == EL_STEEL_EXIT_OPEN ||
9939 element == EL_EM_STEEL_EXIT_OPEN ||
9940 element == EL_SP_TERMINAL ||
9941 element == EL_SP_TERMINAL_ACTIVE ||
9942 element == EL_EXTRA_TIME ||
9943 element == EL_SHIELD_NORMAL ||
9944 element == EL_SHIELD_DEADLY) &&
9945 IS_ANIMATED(graphic))
9946 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9947 else if (IS_MOVING(x, y))
9948 ContinueMoving(x, y);
9949 else if (IS_ACTIVE_BOMB(element))
9950 CheckDynamite(x, y);
9951 else if (element == EL_AMOEBA_GROWING)
9952 AmoebeWaechst(x, y);
9953 else if (element == EL_AMOEBA_SHRINKING)
9954 AmoebaDisappearing(x, y);
9956 #if !USE_NEW_AMOEBA_CODE
9957 else if (IS_AMOEBALIVE(element))
9958 AmoebeAbleger(x, y);
9961 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
9963 else if (element == EL_EXIT_CLOSED)
9965 else if (element == EL_EM_EXIT_CLOSED)
9967 else if (element == EL_STEEL_EXIT_CLOSED)
9968 CheckExitSteel(x, y);
9969 else if (element == EL_EM_STEEL_EXIT_CLOSED)
9970 CheckExitSteelEM(x, y);
9971 else if (element == EL_SP_EXIT_CLOSED)
9973 else if (element == EL_EXPANDABLE_WALL_GROWING ||
9974 element == EL_EXPANDABLE_STEELWALL_GROWING)
9976 else if (element == EL_EXPANDABLE_WALL ||
9977 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9978 element == EL_EXPANDABLE_WALL_VERTICAL ||
9979 element == EL_EXPANDABLE_WALL_ANY ||
9980 element == EL_BD_EXPANDABLE_WALL)
9982 else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9983 element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9984 element == EL_EXPANDABLE_STEELWALL_ANY)
9985 MauerAblegerStahl(x, y);
9986 else if (element == EL_FLAMES)
9987 CheckForDragon(x, y);
9988 else if (element == EL_EXPLOSION)
9989 ; /* drawing of correct explosion animation is handled separately */
9990 else if (element == EL_ELEMENT_SNAPPING ||
9991 element == EL_DIAGONAL_SHRINKING ||
9992 element == EL_DIAGONAL_GROWING)
9994 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
9996 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9998 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
9999 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10001 if (IS_BELT_ACTIVE(element))
10002 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
10004 if (game.magic_wall_active)
10006 int jx = local_player->jx, jy = local_player->jy;
10008 /* play the element sound at the position nearest to the player */
10009 if ((element == EL_MAGIC_WALL_FULL ||
10010 element == EL_MAGIC_WALL_ACTIVE ||
10011 element == EL_MAGIC_WALL_EMPTYING ||
10012 element == EL_BD_MAGIC_WALL_FULL ||
10013 element == EL_BD_MAGIC_WALL_ACTIVE ||
10014 element == EL_BD_MAGIC_WALL_EMPTYING) &&
10015 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
10023 #if USE_NEW_AMOEBA_CODE
10024 /* new experimental amoeba growth stuff */
10025 if (!(FrameCounter % 8))
10027 static unsigned long random = 1684108901;
10029 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
10031 x = RND(lev_fieldx);
10032 y = RND(lev_fieldy);
10033 element = Feld[x][y];
10035 if (!IS_PLAYER(x,y) &&
10036 (element == EL_EMPTY ||
10037 CAN_GROW_INTO(element) ||
10038 element == EL_QUICKSAND_EMPTY ||
10039 element == EL_ACID_SPLASH_LEFT ||
10040 element == EL_ACID_SPLASH_RIGHT))
10042 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
10043 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
10044 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
10045 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
10046 Feld[x][y] = EL_AMOEBA_DROP;
10049 random = random * 129 + 1;
10055 if (game.explosions_delayed)
10058 game.explosions_delayed = FALSE;
10060 SCAN_PLAYFIELD(x, y)
10062 element = Feld[x][y];
10064 if (ExplodeField[x][y])
10065 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
10066 else if (element == EL_EXPLOSION)
10067 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
10069 ExplodeField[x][y] = EX_TYPE_NONE;
10072 game.explosions_delayed = TRUE;
10075 if (game.magic_wall_active)
10077 if (!(game.magic_wall_time_left % 4))
10079 int element = Feld[magic_wall_x][magic_wall_y];
10081 if (element == EL_BD_MAGIC_WALL_FULL ||
10082 element == EL_BD_MAGIC_WALL_ACTIVE ||
10083 element == EL_BD_MAGIC_WALL_EMPTYING)
10084 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
10086 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
10089 if (game.magic_wall_time_left > 0)
10091 game.magic_wall_time_left--;
10092 if (!game.magic_wall_time_left)
10094 SCAN_PLAYFIELD(x, y)
10096 element = Feld[x][y];
10098 if (element == EL_MAGIC_WALL_ACTIVE ||
10099 element == EL_MAGIC_WALL_FULL)
10101 Feld[x][y] = EL_MAGIC_WALL_DEAD;
10102 DrawLevelField(x, y);
10104 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
10105 element == EL_BD_MAGIC_WALL_FULL)
10107 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
10108 DrawLevelField(x, y);
10112 game.magic_wall_active = FALSE;
10117 if (game.light_time_left > 0)
10119 game.light_time_left--;
10121 if (game.light_time_left == 0)
10122 RedrawAllLightSwitchesAndInvisibleElements();
10125 if (game.timegate_time_left > 0)
10127 game.timegate_time_left--;
10129 if (game.timegate_time_left == 0)
10130 CloseAllOpenTimegates();
10133 if (game.lenses_time_left > 0)
10135 game.lenses_time_left--;
10137 if (game.lenses_time_left == 0)
10138 RedrawAllInvisibleElementsForLenses();
10141 if (game.magnify_time_left > 0)
10143 game.magnify_time_left--;
10145 if (game.magnify_time_left == 0)
10146 RedrawAllInvisibleElementsForMagnifier();
10149 for (i = 0; i < MAX_PLAYERS; i++)
10151 struct PlayerInfo *player = &stored_player[i];
10153 if (SHIELD_ON(player))
10155 if (player->shield_deadly_time_left)
10156 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
10157 else if (player->shield_normal_time_left)
10158 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
10165 PlayAllPlayersSound();
10167 if (options.debug) /* calculate frames per second */
10169 static unsigned long fps_counter = 0;
10170 static int fps_frames = 0;
10171 unsigned long fps_delay_ms = Counter() - fps_counter;
10175 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
10177 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
10180 fps_counter = Counter();
10183 redraw_mask |= REDRAW_FPS;
10186 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
10188 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
10190 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
10192 local_player->show_envelope = 0;
10195 /* use random number generator in every frame to make it less predictable */
10196 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
10200 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
10202 int min_x = x, min_y = y, max_x = x, max_y = y;
10205 for (i = 0; i < MAX_PLAYERS; i++)
10207 int jx = stored_player[i].jx, jy = stored_player[i].jy;
10209 if (!stored_player[i].active || &stored_player[i] == player)
10212 min_x = MIN(min_x, jx);
10213 min_y = MIN(min_y, jy);
10214 max_x = MAX(max_x, jx);
10215 max_y = MAX(max_y, jy);
10218 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
10221 static boolean AllPlayersInVisibleScreen()
10225 for (i = 0; i < MAX_PLAYERS; i++)
10227 int jx = stored_player[i].jx, jy = stored_player[i].jy;
10229 if (!stored_player[i].active)
10232 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
10239 void ScrollLevel(int dx, int dy)
10241 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
10244 BlitBitmap(drawto_field, drawto_field,
10245 FX + TILEX * (dx == -1) - softscroll_offset,
10246 FY + TILEY * (dy == -1) - softscroll_offset,
10247 SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
10248 SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
10249 FX + TILEX * (dx == 1) - softscroll_offset,
10250 FY + TILEY * (dy == 1) - softscroll_offset);
10254 x = (dx == 1 ? BX1 : BX2);
10255 for (y = BY1; y <= BY2; y++)
10256 DrawScreenField(x, y);
10261 y = (dy == 1 ? BY1 : BY2);
10262 for (x = BX1; x <= BX2; x++)
10263 DrawScreenField(x, y);
10266 redraw_mask |= REDRAW_FIELD;
10269 static boolean canFallDown(struct PlayerInfo *player)
10271 int jx = player->jx, jy = player->jy;
10273 return (IN_LEV_FIELD(jx, jy + 1) &&
10274 (IS_FREE(jx, jy + 1) ||
10275 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
10276 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
10277 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
10280 static boolean canPassField(int x, int y, int move_dir)
10282 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
10283 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
10284 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
10285 int nextx = x + dx;
10286 int nexty = y + dy;
10287 int element = Feld[x][y];
10289 return (IS_PASSABLE_FROM(element, opposite_dir) &&
10290 !CAN_MOVE(element) &&
10291 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
10292 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
10293 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
10296 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
10298 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
10299 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
10300 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
10304 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
10305 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
10306 (IS_DIGGABLE(Feld[newx][newy]) ||
10307 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
10308 canPassField(newx, newy, move_dir)));
10311 static void CheckGravityMovement(struct PlayerInfo *player)
10313 #if USE_PLAYER_GRAVITY
10314 if (player->gravity && !player->programmed_action)
10316 if (game.gravity && !player->programmed_action)
10319 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
10320 int move_dir_vertical = player->effective_action & MV_VERTICAL;
10321 boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
10322 int jx = player->jx, jy = player->jy;
10323 boolean player_is_moving_to_valid_field =
10324 (!player_is_snapping &&
10325 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
10326 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
10327 boolean player_can_fall_down = canFallDown(player);
10329 if (player_can_fall_down &&
10330 !player_is_moving_to_valid_field)
10331 player->programmed_action = MV_DOWN;
10335 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
10337 return CheckGravityMovement(player);
10339 #if USE_PLAYER_GRAVITY
10340 if (player->gravity && !player->programmed_action)
10342 if (game.gravity && !player->programmed_action)
10345 int jx = player->jx, jy = player->jy;
10346 boolean field_under_player_is_free =
10347 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
10348 boolean player_is_standing_on_valid_field =
10349 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
10350 (IS_WALKABLE(Feld[jx][jy]) &&
10351 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
10353 if (field_under_player_is_free && !player_is_standing_on_valid_field)
10354 player->programmed_action = MV_DOWN;
10359 MovePlayerOneStep()
10360 -----------------------------------------------------------------------------
10361 dx, dy: direction (non-diagonal) to try to move the player to
10362 real_dx, real_dy: direction as read from input device (can be diagonal)
10365 boolean MovePlayerOneStep(struct PlayerInfo *player,
10366 int dx, int dy, int real_dx, int real_dy)
10368 int jx = player->jx, jy = player->jy;
10369 int new_jx = jx + dx, new_jy = jy + dy;
10370 #if !USE_FIXED_DONT_RUN_INTO
10374 boolean player_can_move = !player->cannot_move;
10376 if (!player->active || (!dx && !dy))
10377 return MP_NO_ACTION;
10379 player->MovDir = (dx < 0 ? MV_LEFT :
10380 dx > 0 ? MV_RIGHT :
10382 dy > 0 ? MV_DOWN : MV_NONE);
10384 if (!IN_LEV_FIELD(new_jx, new_jy))
10385 return MP_NO_ACTION;
10387 if (!player_can_move)
10389 if (player->MovPos == 0)
10391 player->is_moving = FALSE;
10392 player->is_digging = FALSE;
10393 player->is_collecting = FALSE;
10394 player->is_snapping = FALSE;
10395 player->is_pushing = FALSE;
10400 if (!options.network && game.centered_player_nr == -1 &&
10401 !AllPlayersInSight(player, new_jx, new_jy))
10402 return MP_NO_ACTION;
10404 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
10405 return MP_NO_ACTION;
10408 #if !USE_FIXED_DONT_RUN_INTO
10409 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
10411 /* (moved to DigField()) */
10412 if (player_can_move && DONT_RUN_INTO(element))
10414 if (element == EL_ACID && dx == 0 && dy == 1)
10416 SplashAcid(new_jx, new_jy);
10417 Feld[jx][jy] = EL_PLAYER_1;
10418 InitMovingField(jx, jy, MV_DOWN);
10419 Store[jx][jy] = EL_ACID;
10420 ContinueMoving(jx, jy);
10421 BuryPlayer(player);
10424 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
10430 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
10431 if (can_move != MP_MOVING)
10434 /* check if DigField() has caused relocation of the player */
10435 if (player->jx != jx || player->jy != jy)
10436 return MP_NO_ACTION; /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
10438 StorePlayer[jx][jy] = 0;
10439 player->last_jx = jx;
10440 player->last_jy = jy;
10441 player->jx = new_jx;
10442 player->jy = new_jy;
10443 StorePlayer[new_jx][new_jy] = player->element_nr;
10445 if (player->move_delay_value_next != -1)
10447 player->move_delay_value = player->move_delay_value_next;
10448 player->move_delay_value_next = -1;
10452 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
10454 player->step_counter++;
10456 PlayerVisit[jx][jy] = FrameCounter;
10458 #if USE_UFAST_PLAYER_EXIT_BUGFIX
10459 player->is_moving = TRUE;
10463 /* should better be called in MovePlayer(), but this breaks some tapes */
10464 ScrollPlayer(player, SCROLL_INIT);
10470 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
10472 int jx = player->jx, jy = player->jy;
10473 int old_jx = jx, old_jy = jy;
10474 int moved = MP_NO_ACTION;
10476 if (!player->active)
10481 if (player->MovPos == 0)
10483 player->is_moving = FALSE;
10484 player->is_digging = FALSE;
10485 player->is_collecting = FALSE;
10486 player->is_snapping = FALSE;
10487 player->is_pushing = FALSE;
10493 if (player->move_delay > 0)
10496 player->move_delay = -1; /* set to "uninitialized" value */
10498 /* store if player is automatically moved to next field */
10499 player->is_auto_moving = (player->programmed_action != MV_NONE);
10501 /* remove the last programmed player action */
10502 player->programmed_action = 0;
10504 if (player->MovPos)
10506 /* should only happen if pre-1.2 tape recordings are played */
10507 /* this is only for backward compatibility */
10509 int original_move_delay_value = player->move_delay_value;
10512 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
10516 /* scroll remaining steps with finest movement resolution */
10517 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
10519 while (player->MovPos)
10521 ScrollPlayer(player, SCROLL_GO_ON);
10522 ScrollScreen(NULL, SCROLL_GO_ON);
10524 AdvanceFrameAndPlayerCounters(player->index_nr);
10530 player->move_delay_value = original_move_delay_value;
10533 player->is_active = FALSE;
10535 if (player->last_move_dir & MV_HORIZONTAL)
10537 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
10538 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
10542 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
10543 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
10546 #if USE_FIXED_BORDER_RUNNING_GFX
10547 if (!moved && !player->is_active)
10549 player->is_moving = FALSE;
10550 player->is_digging = FALSE;
10551 player->is_collecting = FALSE;
10552 player->is_snapping = FALSE;
10553 player->is_pushing = FALSE;
10561 if (moved & MP_MOVING && !ScreenMovPos &&
10562 (player->index_nr == game.centered_player_nr ||
10563 game.centered_player_nr == -1))
10565 if (moved & MP_MOVING && !ScreenMovPos &&
10566 (player == local_player || !options.network))
10569 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
10570 int offset = (setup.scroll_delay ? 3 : 0);
10572 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
10574 /* actual player has left the screen -- scroll in that direction */
10575 if (jx != old_jx) /* player has moved horizontally */
10576 scroll_x += (jx - old_jx);
10577 else /* player has moved vertically */
10578 scroll_y += (jy - old_jy);
10582 if (jx != old_jx) /* player has moved horizontally */
10584 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
10585 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
10586 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
10588 /* don't scroll over playfield boundaries */
10589 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
10590 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
10592 /* don't scroll more than one field at a time */
10593 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
10595 /* don't scroll against the player's moving direction */
10596 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
10597 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
10598 scroll_x = old_scroll_x;
10600 else /* player has moved vertically */
10602 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
10603 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
10604 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
10606 /* don't scroll over playfield boundaries */
10607 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
10608 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
10610 /* don't scroll more than one field at a time */
10611 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
10613 /* don't scroll against the player's moving direction */
10614 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
10615 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
10616 scroll_y = old_scroll_y;
10620 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
10623 if (!options.network && game.centered_player_nr == -1 &&
10624 !AllPlayersInVisibleScreen())
10626 scroll_x = old_scroll_x;
10627 scroll_y = old_scroll_y;
10631 if (!options.network && !AllPlayersInVisibleScreen())
10633 scroll_x = old_scroll_x;
10634 scroll_y = old_scroll_y;
10639 ScrollScreen(player, SCROLL_INIT);
10640 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
10645 player->StepFrame = 0;
10647 if (moved & MP_MOVING)
10649 if (old_jx != jx && old_jy == jy)
10650 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
10651 else if (old_jx == jx && old_jy != jy)
10652 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
10654 DrawLevelField(jx, jy); /* for "crumbled sand" */
10656 player->last_move_dir = player->MovDir;
10657 player->is_moving = TRUE;
10658 player->is_snapping = FALSE;
10659 player->is_switching = FALSE;
10660 player->is_dropping = FALSE;
10661 player->is_dropping_pressed = FALSE;
10662 player->drop_pressed_delay = 0;
10665 /* should better be called here than above, but this breaks some tapes */
10666 ScrollPlayer(player, SCROLL_INIT);
10671 CheckGravityMovementWhenNotMoving(player);
10673 player->is_moving = FALSE;
10675 /* at this point, the player is allowed to move, but cannot move right now
10676 (e.g. because of something blocking the way) -- ensure that the player
10677 is also allowed to move in the next frame (in old versions before 3.1.1,
10678 the player was forced to wait again for eight frames before next try) */
10680 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
10681 player->move_delay = 0; /* allow direct movement in the next frame */
10684 if (player->move_delay == -1) /* not yet initialized by DigField() */
10685 player->move_delay = player->move_delay_value;
10687 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10689 TestIfPlayerTouchesBadThing(jx, jy);
10690 TestIfPlayerTouchesCustomElement(jx, jy);
10693 if (!player->active)
10694 RemovePlayer(player);
10699 void ScrollPlayer(struct PlayerInfo *player, int mode)
10701 int jx = player->jx, jy = player->jy;
10702 int last_jx = player->last_jx, last_jy = player->last_jy;
10703 int move_stepsize = TILEX / player->move_delay_value;
10705 #if USE_NEW_PLAYER_SPEED
10706 if (!player->active)
10709 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
10712 if (!player->active || player->MovPos == 0)
10716 if (mode == SCROLL_INIT)
10718 player->actual_frame_counter = FrameCounter;
10719 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10721 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
10722 Feld[last_jx][last_jy] == EL_EMPTY)
10724 int last_field_block_delay = 0; /* start with no blocking at all */
10725 int block_delay_adjustment = player->block_delay_adjustment;
10727 /* if player blocks last field, add delay for exactly one move */
10728 if (player->block_last_field)
10730 last_field_block_delay += player->move_delay_value;
10732 /* when blocking enabled, prevent moving up despite gravity */
10733 #if USE_PLAYER_GRAVITY
10734 if (player->gravity && player->MovDir == MV_UP)
10735 block_delay_adjustment = -1;
10737 if (game.gravity && player->MovDir == MV_UP)
10738 block_delay_adjustment = -1;
10742 /* add block delay adjustment (also possible when not blocking) */
10743 last_field_block_delay += block_delay_adjustment;
10745 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
10746 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
10749 #if USE_NEW_PLAYER_SPEED
10750 if (player->MovPos != 0) /* player has not yet reached destination */
10756 else if (!FrameReached(&player->actual_frame_counter, 1))
10759 #if USE_NEW_PLAYER_SPEED
10760 if (player->MovPos != 0)
10762 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
10763 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10765 /* before DrawPlayer() to draw correct player graphic for this case */
10766 if (player->MovPos == 0)
10767 CheckGravityMovement(player);
10770 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
10771 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10773 /* before DrawPlayer() to draw correct player graphic for this case */
10774 if (player->MovPos == 0)
10775 CheckGravityMovement(player);
10778 if (player->MovPos == 0) /* player reached destination field */
10780 if (player->move_delay_reset_counter > 0)
10782 player->move_delay_reset_counter--;
10784 if (player->move_delay_reset_counter == 0)
10786 /* continue with normal speed after quickly moving through gate */
10787 HALVE_PLAYER_SPEED(player);
10789 /* be able to make the next move without delay */
10790 player->move_delay = 0;
10794 player->last_jx = jx;
10795 player->last_jy = jy;
10797 if (Feld[jx][jy] == EL_EXIT_OPEN ||
10798 Feld[jx][jy] == EL_EM_EXIT_OPEN ||
10799 Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
10800 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
10801 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
10802 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
10804 DrawPlayer(player); /* needed here only to cleanup last field */
10805 RemovePlayer(player);
10807 if (local_player->friends_still_needed == 0 ||
10808 IS_SP_ELEMENT(Feld[jx][jy]))
10809 PlayerWins(player);
10812 /* this breaks one level: "machine", level 000 */
10814 int move_direction = player->MovDir;
10815 int enter_side = MV_DIR_OPPOSITE(move_direction);
10816 int leave_side = move_direction;
10817 int old_jx = last_jx;
10818 int old_jy = last_jy;
10819 int old_element = Feld[old_jx][old_jy];
10820 int new_element = Feld[jx][jy];
10822 if (IS_CUSTOM_ELEMENT(old_element))
10823 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
10825 player->index_bit, leave_side);
10827 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
10828 CE_PLAYER_LEAVES_X,
10829 player->index_bit, leave_side);
10831 if (IS_CUSTOM_ELEMENT(new_element))
10832 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
10833 player->index_bit, enter_side);
10835 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
10836 CE_PLAYER_ENTERS_X,
10837 player->index_bit, enter_side);
10839 CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
10840 CE_MOVE_OF_X, move_direction);
10843 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10845 TestIfPlayerTouchesBadThing(jx, jy);
10846 TestIfPlayerTouchesCustomElement(jx, jy);
10848 /* needed because pushed element has not yet reached its destination,
10849 so it would trigger a change event at its previous field location */
10850 if (!player->is_pushing)
10851 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
10853 if (!player->active)
10854 RemovePlayer(player);
10857 if (!local_player->LevelSolved && level.use_step_counter)
10867 if (TimeLeft <= 10 && setup.time_limit)
10868 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
10870 DrawGameValue_Time(TimeLeft);
10872 if (!TimeLeft && setup.time_limit)
10873 for (i = 0; i < MAX_PLAYERS; i++)
10874 KillPlayer(&stored_player[i]);
10876 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
10877 DrawGameValue_Time(TimePlayed);
10880 if (tape.single_step && tape.recording && !tape.pausing &&
10881 !player->programmed_action)
10882 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10886 void ScrollScreen(struct PlayerInfo *player, int mode)
10888 static unsigned long screen_frame_counter = 0;
10890 if (mode == SCROLL_INIT)
10892 /* set scrolling step size according to actual player's moving speed */
10893 ScrollStepSize = TILEX / player->move_delay_value;
10895 screen_frame_counter = FrameCounter;
10896 ScreenMovDir = player->MovDir;
10897 ScreenMovPos = player->MovPos;
10898 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
10901 else if (!FrameReached(&screen_frame_counter, 1))
10906 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
10907 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
10908 redraw_mask |= REDRAW_FIELD;
10911 ScreenMovDir = MV_NONE;
10914 void TestIfPlayerTouchesCustomElement(int x, int y)
10916 static int xy[4][2] =
10923 static int trigger_sides[4][2] =
10925 /* center side border side */
10926 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
10927 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
10928 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
10929 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
10931 static int touch_dir[4] =
10933 MV_LEFT | MV_RIGHT,
10938 int center_element = Feld[x][y]; /* should always be non-moving! */
10941 for (i = 0; i < NUM_DIRECTIONS; i++)
10943 int xx = x + xy[i][0];
10944 int yy = y + xy[i][1];
10945 int center_side = trigger_sides[i][0];
10946 int border_side = trigger_sides[i][1];
10947 int border_element;
10949 if (!IN_LEV_FIELD(xx, yy))
10952 if (IS_PLAYER(x, y))
10954 struct PlayerInfo *player = PLAYERINFO(x, y);
10956 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10957 border_element = Feld[xx][yy]; /* may be moving! */
10958 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
10959 border_element = Feld[xx][yy];
10960 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
10961 border_element = MovingOrBlocked2Element(xx, yy);
10963 continue; /* center and border element do not touch */
10965 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
10966 player->index_bit, border_side);
10967 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
10968 CE_PLAYER_TOUCHES_X,
10969 player->index_bit, border_side);
10971 else if (IS_PLAYER(xx, yy))
10973 struct PlayerInfo *player = PLAYERINFO(xx, yy);
10975 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10977 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
10978 continue; /* center and border element do not touch */
10981 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
10982 player->index_bit, center_side);
10983 CheckTriggeredElementChangeByPlayer(x, y, center_element,
10984 CE_PLAYER_TOUCHES_X,
10985 player->index_bit, center_side);
10991 #if USE_ELEMENT_TOUCHING_BUGFIX
10993 void TestIfElementTouchesCustomElement(int x, int y)
10995 static int xy[4][2] =
11002 static int trigger_sides[4][2] =
11004 /* center side border side */
11005 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
11006 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
11007 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
11008 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
11010 static int touch_dir[4] =
11012 MV_LEFT | MV_RIGHT,
11017 boolean change_center_element = FALSE;
11018 int center_element = Feld[x][y]; /* should always be non-moving! */
11019 int border_element_old[NUM_DIRECTIONS];
11022 for (i = 0; i < NUM_DIRECTIONS; i++)
11024 int xx = x + xy[i][0];
11025 int yy = y + xy[i][1];
11026 int border_element;
11028 border_element_old[i] = -1;
11030 if (!IN_LEV_FIELD(xx, yy))
11033 if (game.engine_version < VERSION_IDENT(3,0,7,0))
11034 border_element = Feld[xx][yy]; /* may be moving! */
11035 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
11036 border_element = Feld[xx][yy];
11037 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
11038 border_element = MovingOrBlocked2Element(xx, yy);
11040 continue; /* center and border element do not touch */
11042 border_element_old[i] = border_element;
11045 for (i = 0; i < NUM_DIRECTIONS; i++)
11047 int xx = x + xy[i][0];
11048 int yy = y + xy[i][1];
11049 int center_side = trigger_sides[i][0];
11050 int border_element = border_element_old[i];
11052 if (border_element == -1)
11055 /* check for change of border element */
11056 CheckElementChangeBySide(xx, yy, border_element, center_element,
11057 CE_TOUCHING_X, center_side);
11060 for (i = 0; i < NUM_DIRECTIONS; i++)
11062 int border_side = trigger_sides[i][1];
11063 int border_element = border_element_old[i];
11065 if (border_element == -1)
11068 /* check for change of center element (but change it only once) */
11069 if (!change_center_element)
11070 change_center_element =
11071 CheckElementChangeBySide(x, y, center_element, border_element,
11072 CE_TOUCHING_X, border_side);
11078 void TestIfElementTouchesCustomElement_OLD(int x, int y)
11080 static int xy[4][2] =
11087 static int trigger_sides[4][2] =
11089 /* center side border side */
11090 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
11091 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
11092 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
11093 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
11095 static int touch_dir[4] =
11097 MV_LEFT | MV_RIGHT,
11102 boolean change_center_element = FALSE;
11103 int center_element = Feld[x][y]; /* should always be non-moving! */
11106 for (i = 0; i < NUM_DIRECTIONS; i++)
11108 int xx = x + xy[i][0];
11109 int yy = y + xy[i][1];
11110 int center_side = trigger_sides[i][0];
11111 int border_side = trigger_sides[i][1];
11112 int border_element;
11114 if (!IN_LEV_FIELD(xx, yy))
11117 if (game.engine_version < VERSION_IDENT(3,0,7,0))
11118 border_element = Feld[xx][yy]; /* may be moving! */
11119 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
11120 border_element = Feld[xx][yy];
11121 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
11122 border_element = MovingOrBlocked2Element(xx, yy);
11124 continue; /* center and border element do not touch */
11126 /* check for change of center element (but change it only once) */
11127 if (!change_center_element)
11128 change_center_element =
11129 CheckElementChangeBySide(x, y, center_element, border_element,
11130 CE_TOUCHING_X, border_side);
11132 /* check for change of border element */
11133 CheckElementChangeBySide(xx, yy, border_element, center_element,
11134 CE_TOUCHING_X, center_side);
11140 void TestIfElementHitsCustomElement(int x, int y, int direction)
11142 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
11143 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
11144 int hitx = x + dx, hity = y + dy;
11145 int hitting_element = Feld[x][y];
11146 int touched_element;
11148 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
11151 touched_element = (IN_LEV_FIELD(hitx, hity) ?
11152 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
11154 if (IN_LEV_FIELD(hitx, hity))
11156 int opposite_direction = MV_DIR_OPPOSITE(direction);
11157 int hitting_side = direction;
11158 int touched_side = opposite_direction;
11159 boolean object_hit = (!IS_MOVING(hitx, hity) ||
11160 MovDir[hitx][hity] != direction ||
11161 ABS(MovPos[hitx][hity]) <= TILEY / 2);
11167 CheckElementChangeBySide(x, y, hitting_element, touched_element,
11168 CE_HITTING_X, touched_side);
11170 CheckElementChangeBySide(hitx, hity, touched_element,
11171 hitting_element, CE_HIT_BY_X, hitting_side);
11173 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
11174 CE_HIT_BY_SOMETHING, opposite_direction);
11178 /* "hitting something" is also true when hitting the playfield border */
11179 CheckElementChangeBySide(x, y, hitting_element, touched_element,
11180 CE_HITTING_SOMETHING, direction);
11184 void TestIfElementSmashesCustomElement(int x, int y, int direction)
11186 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
11187 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
11188 int hitx = x + dx, hity = y + dy;
11189 int hitting_element = Feld[x][y];
11190 int touched_element;
11192 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
11193 !IS_FREE(hitx, hity) &&
11194 (!IS_MOVING(hitx, hity) ||
11195 MovDir[hitx][hity] != direction ||
11196 ABS(MovPos[hitx][hity]) <= TILEY / 2));
11199 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
11203 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
11207 touched_element = (IN_LEV_FIELD(hitx, hity) ?
11208 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
11210 CheckElementChangeBySide(x, y, hitting_element, touched_element,
11211 EP_CAN_SMASH_EVERYTHING, direction);
11213 if (IN_LEV_FIELD(hitx, hity))
11215 int opposite_direction = MV_DIR_OPPOSITE(direction);
11216 int hitting_side = direction;
11217 int touched_side = opposite_direction;
11219 int touched_element = MovingOrBlocked2Element(hitx, hity);
11222 boolean object_hit = (!IS_MOVING(hitx, hity) ||
11223 MovDir[hitx][hity] != direction ||
11224 ABS(MovPos[hitx][hity]) <= TILEY / 2);
11233 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
11234 CE_SMASHED_BY_SOMETHING, opposite_direction);
11236 CheckElementChangeBySide(x, y, hitting_element, touched_element,
11237 CE_OTHER_IS_SMASHING, touched_side);
11239 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
11240 CE_OTHER_GETS_SMASHED, hitting_side);
11246 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
11248 int i, kill_x = -1, kill_y = -1;
11250 int bad_element = -1;
11251 static int test_xy[4][2] =
11258 static int test_dir[4] =
11266 for (i = 0; i < NUM_DIRECTIONS; i++)
11268 int test_x, test_y, test_move_dir, test_element;
11270 test_x = good_x + test_xy[i][0];
11271 test_y = good_y + test_xy[i][1];
11273 if (!IN_LEV_FIELD(test_x, test_y))
11277 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
11279 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
11281 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
11282 2nd case: DONT_TOUCH style bad thing does not move away from good thing
11284 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
11285 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
11289 bad_element = test_element;
11295 if (kill_x != -1 || kill_y != -1)
11297 if (IS_PLAYER(good_x, good_y))
11299 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
11301 if (player->shield_deadly_time_left > 0 &&
11302 !IS_INDESTRUCTIBLE(bad_element))
11303 Bang(kill_x, kill_y);
11304 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
11305 KillPlayer(player);
11308 Bang(good_x, good_y);
11312 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
11314 int i, kill_x = -1, kill_y = -1;
11315 int bad_element = Feld[bad_x][bad_y];
11316 static int test_xy[4][2] =
11323 static int touch_dir[4] =
11325 MV_LEFT | MV_RIGHT,
11330 static int test_dir[4] =
11338 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
11341 for (i = 0; i < NUM_DIRECTIONS; i++)
11343 int test_x, test_y, test_move_dir, test_element;
11345 test_x = bad_x + test_xy[i][0];
11346 test_y = bad_y + test_xy[i][1];
11347 if (!IN_LEV_FIELD(test_x, test_y))
11351 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
11353 test_element = Feld[test_x][test_y];
11355 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
11356 2nd case: DONT_TOUCH style bad thing does not move away from good thing
11358 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
11359 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
11361 /* good thing is player or penguin that does not move away */
11362 if (IS_PLAYER(test_x, test_y))
11364 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
11366 if (bad_element == EL_ROBOT && player->is_moving)
11367 continue; /* robot does not kill player if he is moving */
11369 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
11371 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
11372 continue; /* center and border element do not touch */
11379 else if (test_element == EL_PENGUIN)
11388 if (kill_x != -1 || kill_y != -1)
11390 if (IS_PLAYER(kill_x, kill_y))
11392 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
11394 if (player->shield_deadly_time_left > 0 &&
11395 !IS_INDESTRUCTIBLE(bad_element))
11396 Bang(bad_x, bad_y);
11397 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
11398 KillPlayer(player);
11401 Bang(kill_x, kill_y);
11405 void TestIfPlayerTouchesBadThing(int x, int y)
11407 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
11410 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
11412 TestIfGoodThingHitsBadThing(x, y, move_dir);
11415 void TestIfBadThingTouchesPlayer(int x, int y)
11417 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
11420 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
11422 TestIfBadThingHitsGoodThing(x, y, move_dir);
11425 void TestIfFriendTouchesBadThing(int x, int y)
11427 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
11430 void TestIfBadThingTouchesFriend(int x, int y)
11432 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
11435 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
11437 int i, kill_x = bad_x, kill_y = bad_y;
11438 static int xy[4][2] =
11446 for (i = 0; i < NUM_DIRECTIONS; i++)
11450 x = bad_x + xy[i][0];
11451 y = bad_y + xy[i][1];
11452 if (!IN_LEV_FIELD(x, y))
11455 element = Feld[x][y];
11456 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
11457 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
11465 if (kill_x != bad_x || kill_y != bad_y)
11466 Bang(bad_x, bad_y);
11469 void KillPlayer(struct PlayerInfo *player)
11471 int jx = player->jx, jy = player->jy;
11473 if (!player->active)
11476 /* the following code was introduced to prevent an infinite loop when calling
11478 -> CheckTriggeredElementChangeExt()
11479 -> ExecuteCustomElementAction()
11481 -> (infinitely repeating the above sequence of function calls)
11482 which occurs when killing the player while having a CE with the setting
11483 "kill player X when explosion of <player X>"; the solution using a new
11484 field "player->killed" was chosen for backwards compatibility, although
11485 clever use of the fields "player->active" etc. would probably also work */
11487 if (player->killed)
11491 player->killed = TRUE;
11493 /* remove accessible field at the player's position */
11494 Feld[jx][jy] = EL_EMPTY;
11496 /* deactivate shield (else Bang()/Explode() would not work right) */
11497 player->shield_normal_time_left = 0;
11498 player->shield_deadly_time_left = 0;
11501 BuryPlayer(player);
11504 static void KillPlayerUnlessEnemyProtected(int x, int y)
11506 if (!PLAYER_ENEMY_PROTECTED(x, y))
11507 KillPlayer(PLAYERINFO(x, y));
11510 static void KillPlayerUnlessExplosionProtected(int x, int y)
11512 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
11513 KillPlayer(PLAYERINFO(x, y));
11516 void BuryPlayer(struct PlayerInfo *player)
11518 int jx = player->jx, jy = player->jy;
11520 if (!player->active)
11523 PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
11524 PlayLevelSound(jx, jy, SND_GAME_LOSING);
11526 player->GameOver = TRUE;
11527 RemovePlayer(player);
11530 void RemovePlayer(struct PlayerInfo *player)
11532 int jx = player->jx, jy = player->jy;
11533 int i, found = FALSE;
11535 player->present = FALSE;
11536 player->active = FALSE;
11538 if (!ExplodeField[jx][jy])
11539 StorePlayer[jx][jy] = 0;
11541 if (player->is_moving)
11542 DrawLevelField(player->last_jx, player->last_jy);
11544 for (i = 0; i < MAX_PLAYERS; i++)
11545 if (stored_player[i].active)
11549 AllPlayersGone = TRUE;
11555 #if USE_NEW_SNAP_DELAY
11556 static void setFieldForSnapping(int x, int y, int element, int direction)
11558 struct ElementInfo *ei = &element_info[element];
11559 int direction_bit = MV_DIR_TO_BIT(direction);
11560 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
11561 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
11562 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
11564 Feld[x][y] = EL_ELEMENT_SNAPPING;
11565 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
11567 ResetGfxAnimation(x, y);
11569 GfxElement[x][y] = element;
11570 GfxAction[x][y] = action;
11571 GfxDir[x][y] = direction;
11572 GfxFrame[x][y] = -1;
11577 =============================================================================
11578 checkDiagonalPushing()
11579 -----------------------------------------------------------------------------
11580 check if diagonal input device direction results in pushing of object
11581 (by checking if the alternative direction is walkable, diggable, ...)
11582 =============================================================================
11585 static boolean checkDiagonalPushing(struct PlayerInfo *player,
11586 int x, int y, int real_dx, int real_dy)
11588 int jx, jy, dx, dy, xx, yy;
11590 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
11593 /* diagonal direction: check alternative direction */
11598 xx = jx + (dx == 0 ? real_dx : 0);
11599 yy = jy + (dy == 0 ? real_dy : 0);
11601 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
11605 =============================================================================
11607 -----------------------------------------------------------------------------
11608 x, y: field next to player (non-diagonal) to try to dig to
11609 real_dx, real_dy: direction as read from input device (can be diagonal)
11610 =============================================================================
11613 int DigField(struct PlayerInfo *player,
11614 int oldx, int oldy, int x, int y,
11615 int real_dx, int real_dy, int mode)
11617 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
11618 boolean player_was_pushing = player->is_pushing;
11619 boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
11620 boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
11621 int jx = oldx, jy = oldy;
11622 int dx = x - jx, dy = y - jy;
11623 int nextx = x + dx, nexty = y + dy;
11624 int move_direction = (dx == -1 ? MV_LEFT :
11625 dx == +1 ? MV_RIGHT :
11627 dy == +1 ? MV_DOWN : MV_NONE);
11628 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
11629 int dig_side = MV_DIR_OPPOSITE(move_direction);
11630 int old_element = Feld[jx][jy];
11631 #if USE_FIXED_DONT_RUN_INTO
11632 int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
11638 if (is_player) /* function can also be called by EL_PENGUIN */
11640 if (player->MovPos == 0)
11642 player->is_digging = FALSE;
11643 player->is_collecting = FALSE;
11646 if (player->MovPos == 0) /* last pushing move finished */
11647 player->is_pushing = FALSE;
11649 if (mode == DF_NO_PUSH) /* player just stopped pushing */
11651 player->is_switching = FALSE;
11652 player->push_delay = -1;
11654 return MP_NO_ACTION;
11658 #if !USE_FIXED_DONT_RUN_INTO
11659 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
11660 return MP_NO_ACTION;
11663 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
11664 old_element = Back[jx][jy];
11666 /* in case of element dropped at player position, check background */
11667 else if (Back[jx][jy] != EL_EMPTY &&
11668 game.engine_version >= VERSION_IDENT(2,2,0,0))
11669 old_element = Back[jx][jy];
11671 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
11672 return MP_NO_ACTION; /* field has no opening in this direction */
11674 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
11675 return MP_NO_ACTION; /* field has no opening in this direction */
11677 #if USE_FIXED_DONT_RUN_INTO
11678 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
11682 Feld[jx][jy] = player->artwork_element;
11683 InitMovingField(jx, jy, MV_DOWN);
11684 Store[jx][jy] = EL_ACID;
11685 ContinueMoving(jx, jy);
11686 BuryPlayer(player);
11688 return MP_DONT_RUN_INTO;
11692 #if USE_FIXED_DONT_RUN_INTO
11693 if (player_can_move && DONT_RUN_INTO(element))
11695 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
11697 return MP_DONT_RUN_INTO;
11701 #if USE_FIXED_DONT_RUN_INTO
11702 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
11703 return MP_NO_ACTION;
11706 #if !USE_FIXED_DONT_RUN_INTO
11707 element = Feld[x][y];
11710 collect_count = element_info[element].collect_count_initial;
11712 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
11713 return MP_NO_ACTION;
11715 if (game.engine_version < VERSION_IDENT(2,2,0,0))
11716 player_can_move = player_can_move_or_snap;
11718 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
11719 game.engine_version >= VERSION_IDENT(2,2,0,0))
11721 CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
11722 player->index_bit, dig_side);
11723 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
11724 player->index_bit, dig_side);
11726 if (element == EL_DC_LANDMINE)
11729 if (Feld[x][y] != element) /* field changed by snapping */
11732 return MP_NO_ACTION;
11735 #if USE_PLAYER_GRAVITY
11736 if (player->gravity && is_player && !player->is_auto_moving &&
11737 canFallDown(player) && move_direction != MV_DOWN &&
11738 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
11739 return MP_NO_ACTION; /* player cannot walk here due to gravity */
11741 if (game.gravity && is_player && !player->is_auto_moving &&
11742 canFallDown(player) && move_direction != MV_DOWN &&
11743 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
11744 return MP_NO_ACTION; /* player cannot walk here due to gravity */
11747 if (player_can_move &&
11748 IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
11750 int sound_element = SND_ELEMENT(element);
11751 int sound_action = ACTION_WALKING;
11753 if (IS_RND_GATE(element))
11755 if (!player->key[RND_GATE_NR(element)])
11756 return MP_NO_ACTION;
11758 else if (IS_RND_GATE_GRAY(element))
11760 if (!player->key[RND_GATE_GRAY_NR(element)])
11761 return MP_NO_ACTION;
11763 else if (IS_RND_GATE_GRAY_ACTIVE(element))
11765 if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
11766 return MP_NO_ACTION;
11768 else if (element == EL_EXIT_OPEN ||
11769 element == EL_EM_EXIT_OPEN ||
11770 element == EL_STEEL_EXIT_OPEN ||
11771 element == EL_EM_STEEL_EXIT_OPEN ||
11772 element == EL_SP_EXIT_OPEN ||
11773 element == EL_SP_EXIT_OPENING)
11775 sound_action = ACTION_PASSING; /* player is passing exit */
11777 else if (element == EL_EMPTY)
11779 sound_action = ACTION_MOVING; /* nothing to walk on */
11782 /* play sound from background or player, whatever is available */
11783 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
11784 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
11786 PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
11788 else if (player_can_move &&
11789 IS_PASSABLE(element) && canPassField(x, y, move_direction))
11791 if (!ACCESS_FROM(element, opposite_direction))
11792 return MP_NO_ACTION; /* field not accessible from this direction */
11794 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
11795 return MP_NO_ACTION;
11797 if (IS_EM_GATE(element))
11799 if (!player->key[EM_GATE_NR(element)])
11800 return MP_NO_ACTION;
11802 else if (IS_EM_GATE_GRAY(element))
11804 if (!player->key[EM_GATE_GRAY_NR(element)])
11805 return MP_NO_ACTION;
11807 else if (IS_EM_GATE_GRAY_ACTIVE(element))
11809 if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
11810 return MP_NO_ACTION;
11812 else if (IS_EMC_GATE(element))
11814 if (!player->key[EMC_GATE_NR(element)])
11815 return MP_NO_ACTION;
11817 else if (IS_EMC_GATE_GRAY(element))
11819 if (!player->key[EMC_GATE_GRAY_NR(element)])
11820 return MP_NO_ACTION;
11822 else if (IS_EMC_GATE_GRAY_ACTIVE(element))
11824 if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
11825 return MP_NO_ACTION;
11827 else if (element == EL_DC_GATE_WHITE ||
11828 element == EL_DC_GATE_WHITE_GRAY ||
11829 element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
11831 if (player->num_white_keys == 0)
11832 return MP_NO_ACTION;
11834 player->num_white_keys--;
11836 else if (IS_SP_PORT(element))
11838 if (element == EL_SP_GRAVITY_PORT_LEFT ||
11839 element == EL_SP_GRAVITY_PORT_RIGHT ||
11840 element == EL_SP_GRAVITY_PORT_UP ||
11841 element == EL_SP_GRAVITY_PORT_DOWN)
11842 #if USE_PLAYER_GRAVITY
11843 player->gravity = !player->gravity;
11845 game.gravity = !game.gravity;
11847 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
11848 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
11849 element == EL_SP_GRAVITY_ON_PORT_UP ||
11850 element == EL_SP_GRAVITY_ON_PORT_DOWN)
11851 #if USE_PLAYER_GRAVITY
11852 player->gravity = TRUE;
11854 game.gravity = TRUE;
11856 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
11857 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
11858 element == EL_SP_GRAVITY_OFF_PORT_UP ||
11859 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
11860 #if USE_PLAYER_GRAVITY
11861 player->gravity = FALSE;
11863 game.gravity = FALSE;
11867 /* automatically move to the next field with double speed */
11868 player->programmed_action = move_direction;
11870 if (player->move_delay_reset_counter == 0)
11872 player->move_delay_reset_counter = 2; /* two double speed steps */
11874 DOUBLE_PLAYER_SPEED(player);
11877 PlayLevelSoundAction(x, y, ACTION_PASSING);
11879 else if (player_can_move_or_snap && IS_DIGGABLE(element))
11883 if (mode != DF_SNAP)
11885 GfxElement[x][y] = GFX_ELEMENT(element);
11886 player->is_digging = TRUE;
11889 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
11891 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
11892 player->index_bit, dig_side);
11894 if (mode == DF_SNAP)
11896 #if USE_NEW_SNAP_DELAY
11897 if (level.block_snap_field)
11898 setFieldForSnapping(x, y, element, move_direction);
11900 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11902 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11905 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
11906 player->index_bit, dig_side);
11909 else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
11913 if (is_player && mode != DF_SNAP)
11915 GfxElement[x][y] = element;
11916 player->is_collecting = TRUE;
11919 if (element == EL_SPEED_PILL)
11921 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
11923 else if (element == EL_EXTRA_TIME && level.time > 0)
11925 TimeLeft += level.extra_time;
11926 DrawGameValue_Time(TimeLeft);
11928 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
11930 player->shield_normal_time_left += level.shield_normal_time;
11931 if (element == EL_SHIELD_DEADLY)
11932 player->shield_deadly_time_left += level.shield_deadly_time;
11934 else if (element == EL_DYNAMITE ||
11935 element == EL_EM_DYNAMITE ||
11936 element == EL_SP_DISK_RED)
11938 if (player->inventory_size < MAX_INVENTORY_SIZE)
11939 player->inventory_element[player->inventory_size++] = element;
11941 DrawGameDoorValues();
11943 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
11945 player->dynabomb_count++;
11946 player->dynabombs_left++;
11948 else if (element == EL_DYNABOMB_INCREASE_SIZE)
11950 player->dynabomb_size++;
11952 else if (element == EL_DYNABOMB_INCREASE_POWER)
11954 player->dynabomb_xl = TRUE;
11956 else if (IS_KEY(element))
11958 player->key[KEY_NR(element)] = TRUE;
11960 DrawGameDoorValues();
11962 else if (element == EL_DC_KEY_WHITE)
11964 player->num_white_keys++;
11966 /* display white keys? */
11967 /* DrawGameDoorValues(); */
11969 else if (IS_ENVELOPE(element))
11971 player->show_envelope = element;
11973 else if (element == EL_EMC_LENSES)
11975 game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
11977 RedrawAllInvisibleElementsForLenses();
11979 else if (element == EL_EMC_MAGNIFIER)
11981 game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
11983 RedrawAllInvisibleElementsForMagnifier();
11985 else if (IS_DROPPABLE(element) ||
11986 IS_THROWABLE(element)) /* can be collected and dropped */
11990 if (collect_count == 0)
11991 player->inventory_infinite_element = element;
11993 for (i = 0; i < collect_count; i++)
11994 if (player->inventory_size < MAX_INVENTORY_SIZE)
11995 player->inventory_element[player->inventory_size++] = element;
11997 DrawGameDoorValues();
11999 else if (collect_count > 0)
12001 local_player->gems_still_needed -= collect_count;
12002 if (local_player->gems_still_needed < 0)
12003 local_player->gems_still_needed = 0;
12005 DrawGameValue_Emeralds(local_player->gems_still_needed);
12008 RaiseScoreElement(element);
12009 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
12012 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
12013 player->index_bit, dig_side);
12015 if (mode == DF_SNAP)
12017 #if USE_NEW_SNAP_DELAY
12018 if (level.block_snap_field)
12019 setFieldForSnapping(x, y, element, move_direction);
12021 TestIfElementTouchesCustomElement(x, y); /* for empty space */
12023 TestIfElementTouchesCustomElement(x, y); /* for empty space */
12026 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
12027 player->index_bit, dig_side);
12030 else if (player_can_move_or_snap && IS_PUSHABLE(element))
12032 if (mode == DF_SNAP && element != EL_BD_ROCK)
12033 return MP_NO_ACTION;
12035 if (CAN_FALL(element) && dy)
12036 return MP_NO_ACTION;
12038 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
12039 !(element == EL_SPRING && level.use_spring_bug))
12040 return MP_NO_ACTION;
12042 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
12043 ((move_direction & MV_VERTICAL &&
12044 ((element_info[element].move_pattern & MV_LEFT &&
12045 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
12046 (element_info[element].move_pattern & MV_RIGHT &&
12047 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
12048 (move_direction & MV_HORIZONTAL &&
12049 ((element_info[element].move_pattern & MV_UP &&
12050 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
12051 (element_info[element].move_pattern & MV_DOWN &&
12052 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
12053 return MP_NO_ACTION;
12055 /* do not push elements already moving away faster than player */
12056 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
12057 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
12058 return MP_NO_ACTION;
12060 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
12062 if (player->push_delay_value == -1 || !player_was_pushing)
12063 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12065 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
12067 if (player->push_delay_value == -1)
12068 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12070 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
12072 if (!player->is_pushing)
12073 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12076 player->is_pushing = TRUE;
12077 player->is_active = TRUE;
12079 if (!(IN_LEV_FIELD(nextx, nexty) &&
12080 (IS_FREE(nextx, nexty) ||
12081 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
12082 IS_SB_ELEMENT(element)))))
12083 return MP_NO_ACTION;
12085 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
12086 return MP_NO_ACTION;
12088 if (player->push_delay == -1) /* new pushing; restart delay */
12089 player->push_delay = 0;
12091 if (player->push_delay < player->push_delay_value &&
12092 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
12093 element != EL_SPRING && element != EL_BALLOON)
12095 /* make sure that there is no move delay before next try to push */
12096 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
12097 player->move_delay = 0;
12099 return MP_NO_ACTION;
12102 if (IS_SB_ELEMENT(element))
12104 if (element == EL_SOKOBAN_FIELD_FULL)
12106 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
12107 local_player->sokobanfields_still_needed++;
12110 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
12112 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
12113 local_player->sokobanfields_still_needed--;
12116 Feld[x][y] = EL_SOKOBAN_OBJECT;
12118 if (Back[x][y] == Back[nextx][nexty])
12119 PlayLevelSoundAction(x, y, ACTION_PUSHING);
12120 else if (Back[x][y] != 0)
12121 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
12124 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
12127 if (local_player->sokobanfields_still_needed == 0 &&
12128 game.emulation == EMU_SOKOBAN)
12130 PlayerWins(player);
12132 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
12136 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12138 InitMovingField(x, y, move_direction);
12139 GfxAction[x][y] = ACTION_PUSHING;
12141 if (mode == DF_SNAP)
12142 ContinueMoving(x, y);
12144 MovPos[x][y] = (dx != 0 ? dx : dy);
12146 Pushed[x][y] = TRUE;
12147 Pushed[nextx][nexty] = TRUE;
12149 if (game.engine_version < VERSION_IDENT(2,2,0,7))
12150 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12152 player->push_delay_value = -1; /* get new value later */
12154 /* check for element change _after_ element has been pushed */
12155 if (game.use_change_when_pushing_bug)
12157 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
12158 player->index_bit, dig_side);
12159 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
12160 player->index_bit, dig_side);
12163 else if (IS_SWITCHABLE(element))
12165 if (PLAYER_SWITCHING(player, x, y))
12167 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
12168 player->index_bit, dig_side);
12173 player->is_switching = TRUE;
12174 player->switch_x = x;
12175 player->switch_y = y;
12177 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
12179 if (element == EL_ROBOT_WHEEL)
12181 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
12185 DrawLevelField(x, y);
12187 else if (element == EL_SP_TERMINAL)
12191 SCAN_PLAYFIELD(xx, yy)
12193 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
12195 else if (Feld[xx][yy] == EL_SP_TERMINAL)
12196 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
12199 else if (IS_BELT_SWITCH(element))
12201 ToggleBeltSwitch(x, y);
12203 else if (element == EL_SWITCHGATE_SWITCH_UP ||
12204 element == EL_SWITCHGATE_SWITCH_DOWN ||
12205 element == EL_DC_SWITCHGATE_SWITCH_UP ||
12206 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
12208 ToggleSwitchgateSwitch(x, y);
12210 else if (element == EL_LIGHT_SWITCH ||
12211 element == EL_LIGHT_SWITCH_ACTIVE)
12213 ToggleLightSwitch(x, y);
12215 else if (element == EL_TIMEGATE_SWITCH ||
12216 element == EL_DC_TIMEGATE_SWITCH)
12218 ActivateTimegateSwitch(x, y);
12220 else if (element == EL_BALLOON_SWITCH_LEFT ||
12221 element == EL_BALLOON_SWITCH_RIGHT ||
12222 element == EL_BALLOON_SWITCH_UP ||
12223 element == EL_BALLOON_SWITCH_DOWN ||
12224 element == EL_BALLOON_SWITCH_NONE ||
12225 element == EL_BALLOON_SWITCH_ANY)
12227 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
12228 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
12229 element == EL_BALLOON_SWITCH_UP ? MV_UP :
12230 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
12231 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
12234 else if (element == EL_LAMP)
12236 Feld[x][y] = EL_LAMP_ACTIVE;
12237 local_player->lights_still_needed--;
12239 ResetGfxAnimation(x, y);
12240 DrawLevelField(x, y);
12242 else if (element == EL_TIME_ORB_FULL)
12244 Feld[x][y] = EL_TIME_ORB_EMPTY;
12246 if (level.time > 0 || level.use_time_orb_bug)
12248 TimeLeft += level.time_orb_time;
12249 DrawGameValue_Time(TimeLeft);
12252 ResetGfxAnimation(x, y);
12253 DrawLevelField(x, y);
12255 else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
12256 element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
12260 game.ball_state = !game.ball_state;
12262 SCAN_PLAYFIELD(xx, yy)
12264 int e = Feld[xx][yy];
12266 if (game.ball_state)
12268 if (e == EL_EMC_MAGIC_BALL)
12269 CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
12270 else if (e == EL_EMC_MAGIC_BALL_SWITCH)
12271 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
12275 if (e == EL_EMC_MAGIC_BALL_ACTIVE)
12276 CreateField(xx, yy, EL_EMC_MAGIC_BALL);
12277 else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
12278 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
12283 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
12284 player->index_bit, dig_side);
12286 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
12287 player->index_bit, dig_side);
12289 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
12290 player->index_bit, dig_side);
12296 if (!PLAYER_SWITCHING(player, x, y))
12298 player->is_switching = TRUE;
12299 player->switch_x = x;
12300 player->switch_y = y;
12302 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
12303 player->index_bit, dig_side);
12304 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
12305 player->index_bit, dig_side);
12307 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
12308 player->index_bit, dig_side);
12309 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
12310 player->index_bit, dig_side);
12313 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
12314 player->index_bit, dig_side);
12315 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
12316 player->index_bit, dig_side);
12318 return MP_NO_ACTION;
12321 player->push_delay = -1;
12323 if (is_player) /* function can also be called by EL_PENGUIN */
12325 if (Feld[x][y] != element) /* really digged/collected something */
12327 player->is_collecting = !player->is_digging;
12328 player->is_active = TRUE;
12335 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
12337 int jx = player->jx, jy = player->jy;
12338 int x = jx + dx, y = jy + dy;
12339 int snap_direction = (dx == -1 ? MV_LEFT :
12340 dx == +1 ? MV_RIGHT :
12342 dy == +1 ? MV_DOWN : MV_NONE);
12343 boolean can_continue_snapping = (level.continuous_snapping &&
12344 WasJustFalling[x][y] < CHECK_DELAY_FALLING);
12346 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
12349 if (!player->active || !IN_LEV_FIELD(x, y))
12357 if (player->MovPos == 0)
12358 player->is_pushing = FALSE;
12360 player->is_snapping = FALSE;
12362 if (player->MovPos == 0)
12364 player->is_moving = FALSE;
12365 player->is_digging = FALSE;
12366 player->is_collecting = FALSE;
12372 #if USE_NEW_CONTINUOUS_SNAPPING
12373 /* prevent snapping with already pressed snap key when not allowed */
12374 if (player->is_snapping && !can_continue_snapping)
12377 if (player->is_snapping)
12381 player->MovDir = snap_direction;
12383 if (player->MovPos == 0)
12385 player->is_moving = FALSE;
12386 player->is_digging = FALSE;
12387 player->is_collecting = FALSE;
12390 player->is_dropping = FALSE;
12391 player->is_dropping_pressed = FALSE;
12392 player->drop_pressed_delay = 0;
12394 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
12397 player->is_snapping = TRUE;
12398 player->is_active = TRUE;
12400 if (player->MovPos == 0)
12402 player->is_moving = FALSE;
12403 player->is_digging = FALSE;
12404 player->is_collecting = FALSE;
12407 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
12408 DrawLevelField(player->last_jx, player->last_jy);
12410 DrawLevelField(x, y);
12415 boolean DropElement(struct PlayerInfo *player)
12417 int old_element, new_element;
12418 int dropx = player->jx, dropy = player->jy;
12419 int drop_direction = player->MovDir;
12420 int drop_side = drop_direction;
12421 int drop_element = (player->inventory_size > 0 ?
12422 player->inventory_element[player->inventory_size - 1] :
12423 player->inventory_infinite_element != EL_UNDEFINED ?
12424 player->inventory_infinite_element :
12425 player->dynabombs_left > 0 ?
12426 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
12429 player->is_dropping_pressed = TRUE;
12431 /* do not drop an element on top of another element; when holding drop key
12432 pressed without moving, dropped element must move away before the next
12433 element can be dropped (this is especially important if the next element
12434 is dynamite, which can be placed on background for historical reasons) */
12435 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
12438 if (IS_THROWABLE(drop_element))
12440 dropx += GET_DX_FROM_DIR(drop_direction);
12441 dropy += GET_DY_FROM_DIR(drop_direction);
12443 if (!IN_LEV_FIELD(dropx, dropy))
12447 old_element = Feld[dropx][dropy]; /* old element at dropping position */
12448 new_element = drop_element; /* default: no change when dropping */
12450 /* check if player is active, not moving and ready to drop */
12451 if (!player->active || player->MovPos || player->drop_delay > 0)
12454 /* check if player has anything that can be dropped */
12455 if (new_element == EL_UNDEFINED)
12458 /* check if drop key was pressed long enough for EM style dynamite */
12459 if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
12462 /* check if anything can be dropped at the current position */
12463 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
12466 /* collected custom elements can only be dropped on empty fields */
12467 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
12470 if (old_element != EL_EMPTY)
12471 Back[dropx][dropy] = old_element; /* store old element on this field */
12473 ResetGfxAnimation(dropx, dropy);
12474 ResetRandomAnimationValue(dropx, dropy);
12476 if (player->inventory_size > 0 ||
12477 player->inventory_infinite_element != EL_UNDEFINED)
12479 if (player->inventory_size > 0)
12481 player->inventory_size--;
12483 DrawGameDoorValues();
12485 if (new_element == EL_DYNAMITE)
12486 new_element = EL_DYNAMITE_ACTIVE;
12487 else if (new_element == EL_EM_DYNAMITE)
12488 new_element = EL_EM_DYNAMITE_ACTIVE;
12489 else if (new_element == EL_SP_DISK_RED)
12490 new_element = EL_SP_DISK_RED_ACTIVE;
12493 Feld[dropx][dropy] = new_element;
12495 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
12496 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
12497 el2img(Feld[dropx][dropy]), 0);
12499 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
12501 /* needed if previous element just changed to "empty" in the last frame */
12502 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
12504 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
12505 player->index_bit, drop_side);
12506 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
12508 player->index_bit, drop_side);
12510 TestIfElementTouchesCustomElement(dropx, dropy);
12512 else /* player is dropping a dyna bomb */
12514 player->dynabombs_left--;
12516 Feld[dropx][dropy] = new_element;
12518 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
12519 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
12520 el2img(Feld[dropx][dropy]), 0);
12522 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
12525 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
12526 InitField_WithBug1(dropx, dropy, FALSE);
12528 new_element = Feld[dropx][dropy]; /* element might have changed */
12530 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
12531 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
12533 int move_direction, nextx, nexty;
12535 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
12536 MovDir[dropx][dropy] = drop_direction;
12538 move_direction = MovDir[dropx][dropy];
12539 nextx = dropx + GET_DX_FROM_DIR(move_direction);
12540 nexty = dropy + GET_DY_FROM_DIR(move_direction);
12542 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
12544 #if USE_FIX_IMPACT_COLLISION
12545 /* do not cause impact style collision by dropping elements that can fall */
12546 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
12548 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
12552 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
12553 player->is_dropping = TRUE;
12555 player->drop_pressed_delay = 0;
12556 player->is_dropping_pressed = FALSE;
12558 player->drop_x = dropx;
12559 player->drop_y = dropy;
12564 /* ------------------------------------------------------------------------- */
12565 /* game sound playing functions */
12566 /* ------------------------------------------------------------------------- */
12568 static int *loop_sound_frame = NULL;
12569 static int *loop_sound_volume = NULL;
12571 void InitPlayLevelSound()
12573 int num_sounds = getSoundListSize();
12575 checked_free(loop_sound_frame);
12576 checked_free(loop_sound_volume);
12578 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
12579 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
12582 static void PlayLevelSound(int x, int y, int nr)
12584 int sx = SCREENX(x), sy = SCREENY(y);
12585 int volume, stereo_position;
12586 int max_distance = 8;
12587 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
12589 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
12590 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
12593 if (!IN_LEV_FIELD(x, y) ||
12594 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
12595 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
12598 volume = SOUND_MAX_VOLUME;
12600 if (!IN_SCR_FIELD(sx, sy))
12602 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
12603 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
12605 volume -= volume * (dx > dy ? dx : dy) / max_distance;
12608 stereo_position = (SOUND_MAX_LEFT +
12609 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
12610 (SCR_FIELDX + 2 * max_distance));
12612 if (IS_LOOP_SOUND(nr))
12614 /* This assures that quieter loop sounds do not overwrite louder ones,
12615 while restarting sound volume comparison with each new game frame. */
12617 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
12620 loop_sound_volume[nr] = volume;
12621 loop_sound_frame[nr] = FrameCounter;
12624 PlaySoundExt(nr, volume, stereo_position, type);
12627 static void PlayLevelSoundNearest(int x, int y, int sound_action)
12629 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
12630 x > LEVELX(BX2) ? LEVELX(BX2) : x,
12631 y < LEVELY(BY1) ? LEVELY(BY1) :
12632 y > LEVELY(BY2) ? LEVELY(BY2) : y,
12636 static void PlayLevelSoundAction(int x, int y, int action)
12638 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
12641 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
12643 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
12645 if (sound_effect != SND_UNDEFINED)
12646 PlayLevelSound(x, y, sound_effect);
12649 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
12652 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
12654 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12655 PlayLevelSound(x, y, sound_effect);
12658 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
12660 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
12662 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12663 PlayLevelSound(x, y, sound_effect);
12666 static void StopLevelSoundActionIfLoop(int x, int y, int action)
12668 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
12670 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12671 StopSound(sound_effect);
12674 static void PlayLevelMusic()
12676 if (levelset.music[level_nr] != MUS_UNDEFINED)
12677 PlayMusic(levelset.music[level_nr]); /* from config file */
12679 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
12682 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
12684 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
12685 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
12686 int x = xx - 1 - offset;
12687 int y = yy - 1 - offset;
12692 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
12696 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12700 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12704 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12708 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
12712 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12716 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12719 case SAMPLE_android_clone:
12720 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12723 case SAMPLE_android_move:
12724 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12727 case SAMPLE_spring:
12728 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12732 PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
12736 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
12739 case SAMPLE_eater_eat:
12740 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
12744 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12747 case SAMPLE_collect:
12748 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
12751 case SAMPLE_diamond:
12752 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12755 case SAMPLE_squash:
12756 /* !!! CHECK THIS !!! */
12758 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
12760 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
12764 case SAMPLE_wonderfall:
12765 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
12769 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12773 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12777 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
12781 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
12785 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12789 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
12792 case SAMPLE_wonder:
12793 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12797 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
12800 case SAMPLE_exit_open:
12801 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
12804 case SAMPLE_exit_leave:
12805 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
12808 case SAMPLE_dynamite:
12809 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12813 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12817 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
12821 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12825 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
12829 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
12833 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12837 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
12843 void ChangeTime(int value)
12845 int *time = (level.time == 0 ? &TimePlayed : &TimeLeft);
12849 /* EMC game engine uses value from time counter of RND game engine */
12850 level.native_em_level->lev->time = *time;
12852 DrawGameValue_Time(*time);
12855 void RaiseScore(int value)
12857 /* EMC game engine and RND game engine have separate score counters */
12858 int *score = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
12859 &level.native_em_level->lev->score : &local_player->score);
12863 DrawGameValue_Score(*score);
12867 void RaiseScore(int value)
12869 local_player->score += value;
12871 DrawGameValue_Score(local_player->score);
12874 void RaiseScoreElement(int element)
12879 case EL_BD_DIAMOND:
12880 case EL_EMERALD_YELLOW:
12881 case EL_EMERALD_RED:
12882 case EL_EMERALD_PURPLE:
12883 case EL_SP_INFOTRON:
12884 RaiseScore(level.score[SC_EMERALD]);
12887 RaiseScore(level.score[SC_DIAMOND]);
12890 RaiseScore(level.score[SC_CRYSTAL]);
12893 RaiseScore(level.score[SC_PEARL]);
12896 case EL_BD_BUTTERFLY:
12897 case EL_SP_ELECTRON:
12898 RaiseScore(level.score[SC_BUG]);
12901 case EL_BD_FIREFLY:
12902 case EL_SP_SNIKSNAK:
12903 RaiseScore(level.score[SC_SPACESHIP]);
12906 case EL_DARK_YAMYAM:
12907 RaiseScore(level.score[SC_YAMYAM]);
12910 RaiseScore(level.score[SC_ROBOT]);
12913 RaiseScore(level.score[SC_PACMAN]);
12916 RaiseScore(level.score[SC_NUT]);
12919 case EL_EM_DYNAMITE:
12920 case EL_SP_DISK_RED:
12921 case EL_DYNABOMB_INCREASE_NUMBER:
12922 case EL_DYNABOMB_INCREASE_SIZE:
12923 case EL_DYNABOMB_INCREASE_POWER:
12924 RaiseScore(level.score[SC_DYNAMITE]);
12926 case EL_SHIELD_NORMAL:
12927 case EL_SHIELD_DEADLY:
12928 RaiseScore(level.score[SC_SHIELD]);
12930 case EL_EXTRA_TIME:
12931 RaiseScore(level.extra_time_score);
12945 case EL_DC_KEY_WHITE:
12946 RaiseScore(level.score[SC_KEY]);
12949 RaiseScore(element_info[element].collect_score);
12954 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
12956 if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
12958 #if defined(NETWORK_AVALIABLE)
12959 if (options.network)
12960 SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
12966 game_status = GAME_MODE_MAIN;
12972 FadeOut(REDRAW_FIELD);
12974 game_status = GAME_MODE_MAIN;
12976 DrawAndFadeInMainMenu(REDRAW_FIELD);
12980 else /* continue playing the game */
12982 if (tape.playing && tape.deactivate_display)
12983 TapeDeactivateDisplayOff(TRUE);
12985 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
12987 if (tape.playing && tape.deactivate_display)
12988 TapeDeactivateDisplayOn();
12992 void RequestQuitGame(boolean ask_if_really_quit)
12994 boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
12995 boolean skip_request = AllPlayersGone || quick_quit;
12997 RequestQuitGameExt(skip_request, quick_quit,
12998 "Do you really want to quit the game ?");
13002 /* ------------------------------------------------------------------------- */
13003 /* random generator functions */
13004 /* ------------------------------------------------------------------------- */
13006 unsigned int InitEngineRandom_RND(long seed)
13008 game.num_random_calls = 0;
13011 unsigned int rnd_seed = InitEngineRandom(seed);
13013 printf("::: START RND: %d\n", rnd_seed);
13018 return InitEngineRandom(seed);
13024 unsigned int RND(int max)
13028 game.num_random_calls++;
13030 return GetEngineRandom(max);
13037 /* ------------------------------------------------------------------------- */
13038 /* game engine snapshot handling functions */
13039 /* ------------------------------------------------------------------------- */
13041 #define ARGS_ADDRESS_AND_SIZEOF(x) (&(x)), (sizeof(x))
13043 struct EngineSnapshotInfo
13045 /* runtime values for custom element collect score */
13046 int collect_score[NUM_CUSTOM_ELEMENTS];
13048 /* runtime values for group element choice position */
13049 int choice_pos[NUM_GROUP_ELEMENTS];
13051 /* runtime values for belt position animations */
13052 int belt_graphic[4 * NUM_BELT_PARTS];
13053 int belt_anim_mode[4 * NUM_BELT_PARTS];
13056 struct EngineSnapshotNodeInfo
13063 static struct EngineSnapshotInfo engine_snapshot_rnd;
13064 static ListNode *engine_snapshot_list = NULL;
13065 static char *snapshot_level_identifier = NULL;
13066 static int snapshot_level_nr = -1;
13068 void FreeEngineSnapshot()
13070 while (engine_snapshot_list != NULL)
13071 deleteNodeFromList(&engine_snapshot_list, engine_snapshot_list->key,
13074 setString(&snapshot_level_identifier, NULL);
13075 snapshot_level_nr = -1;
13078 static void SaveEngineSnapshotValues_RND()
13080 static int belt_base_active_element[4] =
13082 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
13083 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
13084 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
13085 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
13089 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
13091 int element = EL_CUSTOM_START + i;
13093 engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
13096 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
13098 int element = EL_GROUP_START + i;
13100 engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
13103 for (i = 0; i < 4; i++)
13105 for (j = 0; j < NUM_BELT_PARTS; j++)
13107 int element = belt_base_active_element[i] + j;
13108 int graphic = el2img(element);
13109 int anim_mode = graphic_info[graphic].anim_mode;
13111 engine_snapshot_rnd.belt_graphic[i * 4 + j] = graphic;
13112 engine_snapshot_rnd.belt_anim_mode[i * 4 + j] = anim_mode;
13117 static void LoadEngineSnapshotValues_RND()
13119 unsigned long num_random_calls = game.num_random_calls;
13122 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
13124 int element = EL_CUSTOM_START + i;
13126 element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
13129 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
13131 int element = EL_GROUP_START + i;
13133 element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
13136 for (i = 0; i < 4; i++)
13138 for (j = 0; j < NUM_BELT_PARTS; j++)
13140 int graphic = engine_snapshot_rnd.belt_graphic[i * 4 + j];
13141 int anim_mode = engine_snapshot_rnd.belt_anim_mode[i * 4 + j];
13143 graphic_info[graphic].anim_mode = anim_mode;
13147 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
13149 InitRND(tape.random_seed);
13150 for (i = 0; i < num_random_calls; i++)
13154 if (game.num_random_calls != num_random_calls)
13156 Error(ERR_RETURN, "number of random calls out of sync");
13157 Error(ERR_RETURN, "number of random calls should be %d", num_random_calls);
13158 Error(ERR_RETURN, "number of random calls is %d", game.num_random_calls);
13159 Error(ERR_EXIT, "this should not happen -- please debug");
13163 static void SaveEngineSnapshotBuffer(void *buffer, int size)
13165 struct EngineSnapshotNodeInfo *bi =
13166 checked_calloc(sizeof(struct EngineSnapshotNodeInfo));
13168 bi->buffer_orig = buffer;
13169 bi->buffer_copy = checked_malloc(size);
13172 memcpy(bi->buffer_copy, buffer, size);
13174 addNodeToList(&engine_snapshot_list, NULL, bi);
13177 void SaveEngineSnapshot()
13179 FreeEngineSnapshot(); /* free previous snapshot, if needed */
13181 if (level_editor_test_game) /* do not save snapshots from editor */
13184 /* copy some special values to a structure better suited for the snapshot */
13186 SaveEngineSnapshotValues_RND();
13187 SaveEngineSnapshotValues_EM();
13189 /* save values stored in special snapshot structure */
13191 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
13192 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
13194 /* save further RND engine values */
13196 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
13197 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
13198 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
13200 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
13201 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
13202 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
13203 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
13205 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
13206 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
13207 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
13208 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
13209 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
13211 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
13212 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
13213 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
13215 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
13217 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
13219 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
13220 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
13222 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
13223 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
13224 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
13225 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
13226 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
13227 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
13228 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
13229 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
13230 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
13231 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
13232 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
13233 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
13234 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
13235 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
13236 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
13237 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
13238 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
13239 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
13241 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
13242 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
13244 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
13245 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
13246 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
13248 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
13249 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
13251 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
13252 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
13253 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
13254 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
13255 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
13257 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
13258 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
13260 /* save level identification information */
13262 setString(&snapshot_level_identifier, leveldir_current->identifier);
13263 snapshot_level_nr = level_nr;
13266 ListNode *node = engine_snapshot_list;
13269 while (node != NULL)
13271 num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
13276 printf("::: size of engine snapshot: %d bytes\n", num_bytes);
13280 static void LoadEngineSnapshotBuffer(struct EngineSnapshotNodeInfo *bi)
13282 memcpy(bi->buffer_orig, bi->buffer_copy, bi->size);
13285 void LoadEngineSnapshot()
13287 ListNode *node = engine_snapshot_list;
13289 if (engine_snapshot_list == NULL)
13292 while (node != NULL)
13294 LoadEngineSnapshotBuffer((struct EngineSnapshotNodeInfo *)node->content);
13299 /* restore special values from snapshot structure */
13301 LoadEngineSnapshotValues_RND();
13302 LoadEngineSnapshotValues_EM();
13305 boolean CheckEngineSnapshot()
13307 return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
13308 snapshot_level_nr == level_nr);
13312 /* ---------- new game button stuff ---------------------------------------- */
13314 /* graphic position values for game buttons */
13315 #define GAME_BUTTON_XSIZE 30
13316 #define GAME_BUTTON_YSIZE 30
13317 #define GAME_BUTTON_XPOS 5
13318 #define GAME_BUTTON_YPOS 215
13319 #define SOUND_BUTTON_XPOS 5
13320 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
13322 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
13323 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
13324 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
13325 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
13326 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
13327 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
13334 } gamebutton_info[NUM_GAME_BUTTONS] =
13337 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
13342 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
13343 GAME_CTRL_ID_PAUSE,
13347 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
13352 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
13353 SOUND_CTRL_ID_MUSIC,
13354 "background music on/off"
13357 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
13358 SOUND_CTRL_ID_LOOPS,
13359 "sound loops on/off"
13362 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
13363 SOUND_CTRL_ID_SIMPLE,
13364 "normal sounds on/off"
13368 void CreateGameButtons()
13372 for (i = 0; i < NUM_GAME_BUTTONS; i++)
13374 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
13375 struct GadgetInfo *gi;
13378 unsigned long event_mask;
13379 int gd_xoffset, gd_yoffset;
13380 int gd_x1, gd_x2, gd_y1, gd_y2;
13383 gd_xoffset = gamebutton_info[i].x;
13384 gd_yoffset = gamebutton_info[i].y;
13385 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
13386 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
13388 if (id == GAME_CTRL_ID_STOP ||
13389 id == GAME_CTRL_ID_PAUSE ||
13390 id == GAME_CTRL_ID_PLAY)
13392 button_type = GD_TYPE_NORMAL_BUTTON;
13394 event_mask = GD_EVENT_RELEASED;
13395 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
13396 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
13400 button_type = GD_TYPE_CHECK_BUTTON;
13402 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
13403 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
13404 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
13405 event_mask = GD_EVENT_PRESSED;
13406 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
13407 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
13410 gi = CreateGadget(GDI_CUSTOM_ID, id,
13411 GDI_INFO_TEXT, gamebutton_info[i].infotext,
13412 GDI_X, DX + gd_xoffset,
13413 GDI_Y, DY + gd_yoffset,
13414 GDI_WIDTH, GAME_BUTTON_XSIZE,
13415 GDI_HEIGHT, GAME_BUTTON_YSIZE,
13416 GDI_TYPE, button_type,
13417 GDI_STATE, GD_BUTTON_UNPRESSED,
13418 GDI_CHECKED, checked,
13419 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
13420 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
13421 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
13422 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
13423 GDI_EVENT_MASK, event_mask,
13424 GDI_CALLBACK_ACTION, HandleGameButtons,
13428 Error(ERR_EXIT, "cannot create gadget");
13430 game_gadget[id] = gi;
13434 void FreeGameButtons()
13438 for (i = 0; i < NUM_GAME_BUTTONS; i++)
13439 FreeGadget(game_gadget[i]);
13442 static void MapGameButtons()
13446 for (i = 0; i < NUM_GAME_BUTTONS; i++)
13447 MapGadget(game_gadget[i]);
13450 void UnmapGameButtons()
13454 for (i = 0; i < NUM_GAME_BUTTONS; i++)
13455 UnmapGadget(game_gadget[i]);
13458 static void HandleGameButtons(struct GadgetInfo *gi)
13460 int id = gi->custom_id;
13462 if (game_status != GAME_MODE_PLAYING)
13467 case GAME_CTRL_ID_STOP:
13471 RequestQuitGame(TRUE);
13474 case GAME_CTRL_ID_PAUSE:
13475 if (options.network)
13477 #if defined(NETWORK_AVALIABLE)
13479 SendToServer_ContinuePlaying();
13481 SendToServer_PausePlaying();
13485 TapeTogglePause(TAPE_TOGGLE_MANUAL);
13488 case GAME_CTRL_ID_PLAY:
13491 #if defined(NETWORK_AVALIABLE)
13492 if (options.network)
13493 SendToServer_ContinuePlaying();
13497 tape.pausing = FALSE;
13498 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
13503 case SOUND_CTRL_ID_MUSIC:
13504 if (setup.sound_music)
13506 setup.sound_music = FALSE;
13509 else if (audio.music_available)
13511 setup.sound = setup.sound_music = TRUE;
13513 SetAudioMode(setup.sound);
13519 case SOUND_CTRL_ID_LOOPS:
13520 if (setup.sound_loops)
13521 setup.sound_loops = FALSE;
13522 else if (audio.loops_available)
13524 setup.sound = setup.sound_loops = TRUE;
13525 SetAudioMode(setup.sound);
13529 case SOUND_CTRL_ID_SIMPLE:
13530 if (setup.sound_simple)
13531 setup.sound_simple = FALSE;
13532 else if (audio.sound_available)
13534 setup.sound = setup.sound_simple = TRUE;
13535 SetAudioMode(setup.sound);