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_QUICKSAND_FAST_FILLING, 2 },
738 { EL_QUICKSAND_FAST_EMPTYING, 2 },
739 { EL_MAGIC_WALL_FILLING, 2 },
740 { EL_MAGIC_WALL_EMPTYING, 2 },
741 { EL_BD_MAGIC_WALL_FILLING, 2 },
742 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
743 { EL_DC_MAGIC_WALL_FILLING, 2 },
744 { EL_DC_MAGIC_WALL_EMPTYING, 2 },
754 collect_count_list[] =
757 { EL_BD_DIAMOND, 1 },
758 { EL_EMERALD_YELLOW, 1 },
759 { EL_EMERALD_RED, 1 },
760 { EL_EMERALD_PURPLE, 1 },
762 { EL_SP_INFOTRON, 1 },
774 access_direction_list[] =
776 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
777 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
778 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
779 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
780 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
781 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
782 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
783 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
784 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
785 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
786 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
788 { EL_SP_PORT_LEFT, MV_RIGHT },
789 { EL_SP_PORT_RIGHT, MV_LEFT },
790 { EL_SP_PORT_UP, MV_DOWN },
791 { EL_SP_PORT_DOWN, MV_UP },
792 { EL_SP_PORT_HORIZONTAL, MV_LEFT | MV_RIGHT },
793 { EL_SP_PORT_VERTICAL, MV_UP | MV_DOWN },
794 { EL_SP_PORT_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
795 { EL_SP_GRAVITY_PORT_LEFT, MV_RIGHT },
796 { EL_SP_GRAVITY_PORT_RIGHT, MV_LEFT },
797 { EL_SP_GRAVITY_PORT_UP, MV_DOWN },
798 { EL_SP_GRAVITY_PORT_DOWN, MV_UP },
799 { EL_SP_GRAVITY_ON_PORT_LEFT, MV_RIGHT },
800 { EL_SP_GRAVITY_ON_PORT_RIGHT, MV_LEFT },
801 { EL_SP_GRAVITY_ON_PORT_UP, MV_DOWN },
802 { EL_SP_GRAVITY_ON_PORT_DOWN, MV_UP },
803 { EL_SP_GRAVITY_OFF_PORT_LEFT, MV_RIGHT },
804 { EL_SP_GRAVITY_OFF_PORT_RIGHT, MV_LEFT },
805 { EL_SP_GRAVITY_OFF_PORT_UP, MV_DOWN },
806 { EL_SP_GRAVITY_OFF_PORT_DOWN, MV_UP },
808 { EL_UNDEFINED, MV_NONE }
811 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
813 #define IS_AUTO_CHANGING(e) (element_info[e].has_change_event[CE_DELAY])
814 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
815 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
816 IS_JUST_CHANGING(x, y))
818 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
820 /* static variables for playfield scan mode (scanning forward or backward) */
821 static int playfield_scan_start_x = 0;
822 static int playfield_scan_start_y = 0;
823 static int playfield_scan_delta_x = 1;
824 static int playfield_scan_delta_y = 1;
826 #define SCAN_PLAYFIELD(x, y) for ((y) = playfield_scan_start_y; \
827 (y) >= 0 && (y) <= lev_fieldy - 1; \
828 (y) += playfield_scan_delta_y) \
829 for ((x) = playfield_scan_start_x; \
830 (x) >= 0 && (x) <= lev_fieldx - 1; \
831 (x) += playfield_scan_delta_x) \
834 void DEBUG_SetMaximumDynamite()
838 for (i = 0; i < MAX_INVENTORY_SIZE; i++)
839 if (local_player->inventory_size < MAX_INVENTORY_SIZE)
840 local_player->inventory_element[local_player->inventory_size++] =
845 static void InitPlayfieldScanModeVars()
847 if (game.use_reverse_scan_direction)
849 playfield_scan_start_x = lev_fieldx - 1;
850 playfield_scan_start_y = lev_fieldy - 1;
852 playfield_scan_delta_x = -1;
853 playfield_scan_delta_y = -1;
857 playfield_scan_start_x = 0;
858 playfield_scan_start_y = 0;
860 playfield_scan_delta_x = 1;
861 playfield_scan_delta_y = 1;
865 static void InitPlayfieldScanMode(int mode)
867 game.use_reverse_scan_direction =
868 (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
870 InitPlayfieldScanModeVars();
873 static int get_move_delay_from_stepsize(int move_stepsize)
876 MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
878 /* make sure that stepsize value is always a power of 2 */
879 move_stepsize = (1 << log_2(move_stepsize));
881 return TILEX / move_stepsize;
884 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
887 int player_nr = player->index_nr;
888 int move_delay = get_move_delay_from_stepsize(move_stepsize);
889 boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
891 /* do no immediately change move delay -- the player might just be moving */
892 player->move_delay_value_next = move_delay;
894 /* information if player can move must be set separately */
895 player->cannot_move = cannot_move;
899 player->move_delay = game.initial_move_delay[player_nr];
900 player->move_delay_value = game.initial_move_delay_value[player_nr];
902 player->move_delay_value_next = -1;
904 player->move_delay_reset_counter = 0;
908 void GetPlayerConfig()
910 if (!audio.sound_available)
911 setup.sound_simple = FALSE;
913 if (!audio.loops_available)
914 setup.sound_loops = FALSE;
916 if (!audio.music_available)
917 setup.sound_music = FALSE;
919 if (!video.fullscreen_available)
920 setup.fullscreen = FALSE;
922 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
924 SetAudioMode(setup.sound);
928 static int getBeltNrFromBeltElement(int element)
930 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
931 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
932 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
935 static int getBeltNrFromBeltActiveElement(int element)
937 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
938 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
939 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
942 static int getBeltNrFromBeltSwitchElement(int element)
944 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
945 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
946 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
949 static int getBeltDirNrFromBeltSwitchElement(int element)
951 static int belt_base_element[4] =
953 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
954 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
955 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
956 EL_CONVEYOR_BELT_4_SWITCH_LEFT
959 int belt_nr = getBeltNrFromBeltSwitchElement(element);
960 int belt_dir_nr = element - belt_base_element[belt_nr];
962 return (belt_dir_nr % 3);
965 static int getBeltDirFromBeltSwitchElement(int element)
967 static int belt_move_dir[3] =
974 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
976 return belt_move_dir[belt_dir_nr];
979 static int get_element_from_group_element(int element)
981 if (IS_GROUP_ELEMENT(element))
983 struct ElementGroupInfo *group = element_info[element].group;
984 int last_anim_random_frame = gfx.anim_random_frame;
987 if (group->choice_mode == ANIM_RANDOM)
988 gfx.anim_random_frame = RND(group->num_elements_resolved);
990 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
991 group->choice_mode, 0,
994 if (group->choice_mode == ANIM_RANDOM)
995 gfx.anim_random_frame = last_anim_random_frame;
999 element = group->element_resolved[element_pos];
1005 static void InitPlayerField(int x, int y, int element, boolean init_game)
1007 if (element == EL_SP_MURPHY)
1011 if (stored_player[0].present)
1013 Feld[x][y] = EL_SP_MURPHY_CLONE;
1019 stored_player[0].use_murphy = TRUE;
1021 if (!level.use_artwork_element[0])
1022 stored_player[0].artwork_element = EL_SP_MURPHY;
1025 Feld[x][y] = EL_PLAYER_1;
1031 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1032 int jx = player->jx, jy = player->jy;
1034 player->present = TRUE;
1036 player->block_last_field = (element == EL_SP_MURPHY ?
1037 level.sp_block_last_field :
1038 level.block_last_field);
1040 /* ---------- initialize player's last field block delay --------------- */
1042 /* always start with reliable default value (no adjustment needed) */
1043 player->block_delay_adjustment = 0;
1045 /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1046 if (player->block_last_field && element == EL_SP_MURPHY)
1047 player->block_delay_adjustment = 1;
1049 /* special case 2: in game engines before 3.1.1, blocking was different */
1050 if (game.use_block_last_field_bug)
1051 player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1053 if (!options.network || player->connected)
1055 player->active = TRUE;
1057 /* remove potentially duplicate players */
1058 if (StorePlayer[jx][jy] == Feld[x][y])
1059 StorePlayer[jx][jy] = 0;
1061 StorePlayer[x][y] = Feld[x][y];
1065 printf("Player %d activated.\n", player->element_nr);
1066 printf("[Local player is %d and currently %s.]\n",
1067 local_player->element_nr,
1068 local_player->active ? "active" : "not active");
1072 Feld[x][y] = EL_EMPTY;
1074 player->jx = player->last_jx = x;
1075 player->jy = player->last_jy = y;
1079 static void InitField(int x, int y, boolean init_game)
1081 int element = Feld[x][y];
1090 InitPlayerField(x, y, element, init_game);
1093 case EL_SOKOBAN_FIELD_PLAYER:
1094 element = Feld[x][y] = EL_PLAYER_1;
1095 InitField(x, y, init_game);
1097 element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1098 InitField(x, y, init_game);
1101 case EL_SOKOBAN_FIELD_EMPTY:
1102 local_player->sokobanfields_still_needed++;
1106 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1107 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1108 else if (x > 0 && Feld[x-1][y] == EL_ACID)
1109 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1110 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1111 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1112 else if (y > 0 && Feld[x][y-1] == EL_ACID)
1113 Feld[x][y] = EL_ACID_POOL_BOTTOM;
1114 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1115 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1124 case EL_SPACESHIP_RIGHT:
1125 case EL_SPACESHIP_UP:
1126 case EL_SPACESHIP_LEFT:
1127 case EL_SPACESHIP_DOWN:
1128 case EL_BD_BUTTERFLY:
1129 case EL_BD_BUTTERFLY_RIGHT:
1130 case EL_BD_BUTTERFLY_UP:
1131 case EL_BD_BUTTERFLY_LEFT:
1132 case EL_BD_BUTTERFLY_DOWN:
1134 case EL_BD_FIREFLY_RIGHT:
1135 case EL_BD_FIREFLY_UP:
1136 case EL_BD_FIREFLY_LEFT:
1137 case EL_BD_FIREFLY_DOWN:
1138 case EL_PACMAN_RIGHT:
1140 case EL_PACMAN_LEFT:
1141 case EL_PACMAN_DOWN:
1143 case EL_YAMYAM_LEFT:
1144 case EL_YAMYAM_RIGHT:
1146 case EL_YAMYAM_DOWN:
1147 case EL_DARK_YAMYAM:
1150 case EL_SP_SNIKSNAK:
1151 case EL_SP_ELECTRON:
1160 case EL_AMOEBA_FULL:
1165 case EL_AMOEBA_DROP:
1166 if (y == lev_fieldy - 1)
1168 Feld[x][y] = EL_AMOEBA_GROWING;
1169 Store[x][y] = EL_AMOEBA_WET;
1173 case EL_DYNAMITE_ACTIVE:
1174 case EL_SP_DISK_RED_ACTIVE:
1175 case EL_DYNABOMB_PLAYER_1_ACTIVE:
1176 case EL_DYNABOMB_PLAYER_2_ACTIVE:
1177 case EL_DYNABOMB_PLAYER_3_ACTIVE:
1178 case EL_DYNABOMB_PLAYER_4_ACTIVE:
1179 MovDelay[x][y] = 96;
1182 case EL_EM_DYNAMITE_ACTIVE:
1183 MovDelay[x][y] = 32;
1187 local_player->lights_still_needed++;
1191 local_player->friends_still_needed++;
1196 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1199 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1200 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1201 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1202 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1203 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1204 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1205 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1206 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1207 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1208 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1209 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1210 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1213 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1214 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1215 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1217 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
1219 game.belt_dir[belt_nr] = belt_dir;
1220 game.belt_dir_nr[belt_nr] = belt_dir_nr;
1222 else /* more than one switch -- set it like the first switch */
1224 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1229 #if !USE_BOTH_SWITCHGATE_SWITCHES
1230 case EL_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
1232 Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
1235 case EL_DC_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
1237 Feld[x][y] = EL_DC_SWITCHGATE_SWITCH_UP;
1241 case EL_LIGHT_SWITCH_ACTIVE:
1243 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1246 case EL_INVISIBLE_STEELWALL:
1247 case EL_INVISIBLE_WALL:
1248 case EL_INVISIBLE_SAND:
1249 if (game.light_time_left > 0 ||
1250 game.lenses_time_left > 0)
1251 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1254 case EL_EMC_MAGIC_BALL:
1255 if (game.ball_state)
1256 Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1259 case EL_EMC_MAGIC_BALL_SWITCH:
1260 if (game.ball_state)
1261 Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1265 if (IS_CUSTOM_ELEMENT(element))
1267 if (CAN_MOVE(element))
1270 #if USE_NEW_CUSTOM_VALUE
1271 if (!element_info[element].use_last_ce_value || init_game)
1272 CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1275 else if (IS_GROUP_ELEMENT(element))
1277 Feld[x][y] = get_element_from_group_element(element);
1279 InitField(x, y, init_game);
1286 CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1289 static inline void InitField_WithBug1(int x, int y, boolean init_game)
1291 InitField(x, y, init_game);
1293 /* not needed to call InitMovDir() -- already done by InitField()! */
1294 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1295 CAN_MOVE(Feld[x][y]))
1299 static inline void InitField_WithBug2(int x, int y, boolean init_game)
1301 int old_element = Feld[x][y];
1303 InitField(x, y, init_game);
1305 /* not needed to call InitMovDir() -- already done by InitField()! */
1306 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1307 CAN_MOVE(old_element) &&
1308 (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1311 /* this case is in fact a combination of not less than three bugs:
1312 first, it calls InitMovDir() for elements that can move, although this is
1313 already done by InitField(); then, it checks the element that was at this
1314 field _before_ the call to InitField() (which can change it); lastly, it
1315 was not called for "mole with direction" elements, which were treated as
1316 "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1320 inline void DrawGameValue_Emeralds(int value)
1322 int xpos = (3 * 14 - 3 * getFontWidth(FONT_TEXT_2)) / 2;
1324 if (PANEL_DEACTIVATED(game.panel.gems))
1327 DrawText(DX_EMERALDS + xpos, DY_EMERALDS, int2str(value, 3), FONT_TEXT_2);
1330 inline void DrawGameValue_Dynamite(int value)
1332 int xpos = (3 * 14 - 3 * getFontWidth(FONT_TEXT_2)) / 2;
1334 if (PANEL_DEACTIVATED(game.panel.inventory))
1337 DrawText(DX_DYNAMITE + xpos, DY_DYNAMITE, int2str(value, 3), FONT_TEXT_2);
1340 inline void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
1342 int base_key_graphic = EL_KEY_1;
1345 if (PANEL_DEACTIVATED(game.panel.keys))
1348 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
1349 base_key_graphic = EL_EM_KEY_1;
1351 /* currently only 4 of 8 possible keys are displayed */
1352 for (i = 0; i < STD_NUM_KEYS; i++)
1354 int x = XX_KEYS + i * MINI_TILEX;
1358 DrawMiniGraphicExt(drawto, DX + x,DY + y, el2edimg(base_key_graphic + i));
1360 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
1361 DOOR_GFX_PAGEX5 + x, y, MINI_TILEX, MINI_TILEY, DX + x,DY + y);
1365 inline void DrawGameValue_Score(int value)
1367 int xpos = (5 * 14 - 5 * getFontWidth(FONT_TEXT_2)) / 2;
1369 if (PANEL_DEACTIVATED(game.panel.score))
1372 DrawText(DX_SCORE + xpos, DY_SCORE, int2str(value, 5), FONT_TEXT_2);
1375 inline void DrawGameValue_Time(int value)
1377 int xpos3 = (3 * 14 - 3 * getFontWidth(FONT_TEXT_2)) / 2;
1378 int xpos4 = (4 * 10 - 4 * getFontWidth(FONT_LEVEL_NUMBER)) / 2;
1380 if (PANEL_DEACTIVATED(game.panel.time))
1383 /* clear background if value just changed its size */
1384 if (value == 999 || value == 1000)
1385 ClearRectangleOnBackground(drawto, DX_TIME1, DY_TIME, 14 * 3, 14);
1388 DrawText(DX_TIME1 + xpos3, DY_TIME, int2str(value, 3), FONT_TEXT_2);
1390 DrawText(DX_TIME2 + xpos4, DY_TIME, int2str(value, 4), FONT_LEVEL_NUMBER);
1393 inline void DrawGameValue_Level(int value)
1395 if (PANEL_DEACTIVATED(game.panel.level))
1399 DrawText(DX_LEVEL1, DY_LEVEL, int2str(value, 2), FONT_TEXT_2);
1401 DrawText(DX_LEVEL2, DY_LEVEL, int2str(value, 3), FONT_LEVEL_NUMBER);
1404 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
1407 int key[MAX_NUM_KEYS];
1410 /* prevent EM engine from updating time/score values parallel to GameWon() */
1411 if (level.game_engine_type == GAME_ENGINE_TYPE_EM &&
1412 local_player->LevelSolved)
1415 for (i = 0; i < MAX_NUM_KEYS; i++)
1416 key[i] = key_bits & (1 << i);
1418 DrawGameValue_Level(level_nr);
1420 DrawGameValue_Emeralds(emeralds);
1421 DrawGameValue_Dynamite(dynamite);
1422 DrawGameValue_Score(score);
1423 DrawGameValue_Time(time);
1425 DrawGameValue_Keys(key);
1428 void DrawGameDoorValues()
1430 int time_value = (level.time == 0 ? TimePlayed : TimeLeft);
1431 int dynamite_value = 0;
1432 int score_value = (local_player->LevelSolved ? local_player->score_final :
1433 local_player->score);
1434 int gems_value = local_player->gems_still_needed;
1438 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
1440 DrawGameDoorValues_EM();
1445 if (game.centered_player_nr == -1)
1447 for (i = 0; i < MAX_PLAYERS; i++)
1449 for (j = 0; j < MAX_NUM_KEYS; j++)
1450 if (stored_player[i].key[j])
1451 key_bits |= (1 << j);
1453 dynamite_value += stored_player[i].inventory_size;
1458 int player_nr = game.centered_player_nr;
1460 for (i = 0; i < MAX_NUM_KEYS; i++)
1461 if (stored_player[player_nr].key[i])
1462 key_bits |= (1 << i);
1464 dynamite_value = stored_player[player_nr].inventory_size;
1467 DrawAllGameValues(gems_value, dynamite_value, score_value, time_value,
1473 =============================================================================
1475 -----------------------------------------------------------------------------
1476 initialize game engine due to level / tape version number
1477 =============================================================================
1480 static void InitGameEngine()
1482 int i, j, k, l, x, y;
1484 /* set game engine from tape file when re-playing, else from level file */
1485 game.engine_version = (tape.playing ? tape.engine_version :
1486 level.game_version);
1488 /* ---------------------------------------------------------------------- */
1489 /* set flags for bugs and changes according to active game engine version */
1490 /* ---------------------------------------------------------------------- */
1493 Summary of bugfix/change:
1494 Fixed handling for custom elements that change when pushed by the player.
1496 Fixed/changed in version:
1500 Before 3.1.0, custom elements that "change when pushing" changed directly
1501 after the player started pushing them (until then handled in "DigField()").
1502 Since 3.1.0, these custom elements are not changed until the "pushing"
1503 move of the element is finished (now handled in "ContinueMoving()").
1505 Affected levels/tapes:
1506 The first condition is generally needed for all levels/tapes before version
1507 3.1.0, which might use the old behaviour before it was changed; known tapes
1508 that are affected are some tapes from the level set "Walpurgis Gardens" by
1510 The second condition is an exception from the above case and is needed for
1511 the special case of tapes recorded with game (not engine!) version 3.1.0 or
1512 above (including some development versions of 3.1.0), but before it was
1513 known that this change would break tapes like the above and was fixed in
1514 3.1.1, so that the changed behaviour was active although the engine version
1515 while recording maybe was before 3.1.0. There is at least one tape that is
1516 affected by this exception, which is the tape for the one-level set "Bug
1517 Machine" by Juergen Bonhagen.
1520 game.use_change_when_pushing_bug =
1521 (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1523 tape.game_version >= VERSION_IDENT(3,1,0,0) &&
1524 tape.game_version < VERSION_IDENT(3,1,1,0)));
1527 Summary of bugfix/change:
1528 Fixed handling for blocking the field the player leaves when moving.
1530 Fixed/changed in version:
1534 Before 3.1.1, when "block last field when moving" was enabled, the field
1535 the player is leaving when moving was blocked for the time of the move,
1536 and was directly unblocked afterwards. This resulted in the last field
1537 being blocked for exactly one less than the number of frames of one player
1538 move. Additionally, even when blocking was disabled, the last field was
1539 blocked for exactly one frame.
1540 Since 3.1.1, due to changes in player movement handling, the last field
1541 is not blocked at all when blocking is disabled. When blocking is enabled,
1542 the last field is blocked for exactly the number of frames of one player
1543 move. Additionally, if the player is Murphy, the hero of Supaplex, the
1544 last field is blocked for exactly one more than the number of frames of
1547 Affected levels/tapes:
1548 (!!! yet to be determined -- probably many !!!)
1551 game.use_block_last_field_bug =
1552 (game.engine_version < VERSION_IDENT(3,1,1,0));
1555 Summary of bugfix/change:
1556 Changed behaviour of CE changes with multiple changes per single frame.
1558 Fixed/changed in version:
1562 Before 3.2.0-6, only one single CE change was allowed in each engine frame.
1563 This resulted in race conditions where CEs seem to behave strange in some
1564 situations (where triggered CE changes were just skipped because there was
1565 already a CE change on that tile in the playfield in that engine frame).
1566 Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
1567 (The number of changes per frame must be limited in any case, because else
1568 it is easily possible to define CE changes that would result in an infinite
1569 loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
1570 should be set large enough so that it would only be reached in cases where
1571 the corresponding CE change conditions run into a loop. Therefore, it seems
1572 to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
1573 maximal number of change pages for custom elements.)
1575 Affected levels/tapes:
1579 #if USE_ONLY_ONE_CHANGE_PER_FRAME
1580 game.max_num_changes_per_frame = 1;
1582 game.max_num_changes_per_frame =
1583 (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
1586 /* ---------------------------------------------------------------------- */
1588 /* default scan direction: scan playfield from top/left to bottom/right */
1589 InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
1591 /* dynamically adjust element properties according to game engine version */
1592 InitElementPropertiesEngine(game.engine_version);
1595 printf("level %d: level version == %06d\n", level_nr, level.game_version);
1596 printf(" tape version == %06d [%s] [file: %06d]\n",
1597 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
1599 printf(" => game.engine_version == %06d\n", game.engine_version);
1602 /* ---------- initialize player's initial move delay --------------------- */
1604 /* dynamically adjust player properties according to level information */
1605 for (i = 0; i < MAX_PLAYERS; i++)
1606 game.initial_move_delay_value[i] =
1607 get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
1609 /* dynamically adjust player properties according to game engine version */
1610 for (i = 0; i < MAX_PLAYERS; i++)
1611 game.initial_move_delay[i] =
1612 (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
1613 game.initial_move_delay_value[i] : 0);
1615 /* ---------- initialize player's initial push delay --------------------- */
1617 /* dynamically adjust player properties according to game engine version */
1618 game.initial_push_delay_value =
1619 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
1621 /* ---------- initialize changing elements ------------------------------- */
1623 /* initialize changing elements information */
1624 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1626 struct ElementInfo *ei = &element_info[i];
1628 /* this pointer might have been changed in the level editor */
1629 ei->change = &ei->change_page[0];
1631 if (!IS_CUSTOM_ELEMENT(i))
1633 ei->change->target_element = EL_EMPTY_SPACE;
1634 ei->change->delay_fixed = 0;
1635 ei->change->delay_random = 0;
1636 ei->change->delay_frames = 1;
1639 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1641 ei->has_change_event[j] = FALSE;
1643 ei->event_page_nr[j] = 0;
1644 ei->event_page[j] = &ei->change_page[0];
1648 /* add changing elements from pre-defined list */
1649 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
1651 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
1652 struct ElementInfo *ei = &element_info[ch_delay->element];
1654 ei->change->target_element = ch_delay->target_element;
1655 ei->change->delay_fixed = ch_delay->change_delay;
1657 ei->change->pre_change_function = ch_delay->pre_change_function;
1658 ei->change->change_function = ch_delay->change_function;
1659 ei->change->post_change_function = ch_delay->post_change_function;
1661 ei->change->can_change = TRUE;
1662 ei->change->can_change_or_has_action = TRUE;
1664 ei->has_change_event[CE_DELAY] = TRUE;
1666 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
1667 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
1670 /* ---------- initialize internal run-time variables ------------- */
1672 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1674 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1676 for (j = 0; j < ei->num_change_pages; j++)
1678 ei->change_page[j].can_change_or_has_action =
1679 (ei->change_page[j].can_change |
1680 ei->change_page[j].has_action);
1684 /* add change events from custom element configuration */
1685 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1687 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1689 for (j = 0; j < ei->num_change_pages; j++)
1691 if (!ei->change_page[j].can_change_or_has_action)
1694 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1696 /* only add event page for the first page found with this event */
1697 if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
1699 ei->has_change_event[k] = TRUE;
1701 ei->event_page_nr[k] = j;
1702 ei->event_page[k] = &ei->change_page[j];
1708 /* ---------- initialize run-time trigger player and element ------------- */
1710 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1712 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1714 for (j = 0; j < ei->num_change_pages; j++)
1716 ei->change_page[j].actual_trigger_element = EL_EMPTY;
1717 ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
1718 ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
1719 ei->change_page[j].actual_trigger_ce_value = 0;
1720 ei->change_page[j].actual_trigger_ce_score = 0;
1724 /* ---------- initialize trigger events ---------------------------------- */
1726 /* initialize trigger events information */
1727 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1728 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1729 trigger_events[i][j] = FALSE;
1731 /* add trigger events from element change event properties */
1732 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1734 struct ElementInfo *ei = &element_info[i];
1736 for (j = 0; j < ei->num_change_pages; j++)
1738 if (!ei->change_page[j].can_change_or_has_action)
1741 if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
1743 int trigger_element = ei->change_page[j].trigger_element;
1745 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1747 if (ei->change_page[j].has_event[k])
1749 if (IS_GROUP_ELEMENT(trigger_element))
1751 struct ElementGroupInfo *group =
1752 element_info[trigger_element].group;
1754 for (l = 0; l < group->num_elements_resolved; l++)
1755 trigger_events[group->element_resolved[l]][k] = TRUE;
1757 else if (trigger_element == EL_ANY_ELEMENT)
1758 for (l = 0; l < MAX_NUM_ELEMENTS; l++)
1759 trigger_events[l][k] = TRUE;
1761 trigger_events[trigger_element][k] = TRUE;
1768 /* ---------- initialize push delay -------------------------------------- */
1770 /* initialize push delay values to default */
1771 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1773 if (!IS_CUSTOM_ELEMENT(i))
1775 /* set default push delay values (corrected since version 3.0.7-1) */
1776 if (game.engine_version < VERSION_IDENT(3,0,7,1))
1778 element_info[i].push_delay_fixed = 2;
1779 element_info[i].push_delay_random = 8;
1783 element_info[i].push_delay_fixed = 8;
1784 element_info[i].push_delay_random = 8;
1789 /* set push delay value for certain elements from pre-defined list */
1790 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
1792 int e = push_delay_list[i].element;
1794 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
1795 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
1798 /* set push delay value for Supaplex elements for newer engine versions */
1799 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
1801 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1803 if (IS_SP_ELEMENT(i))
1805 /* set SP push delay to just enough to push under a falling zonk */
1806 int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
1808 element_info[i].push_delay_fixed = delay;
1809 element_info[i].push_delay_random = 0;
1814 /* ---------- initialize move stepsize ----------------------------------- */
1816 /* initialize move stepsize values to default */
1817 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1818 if (!IS_CUSTOM_ELEMENT(i))
1819 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
1821 /* set move stepsize value for certain elements from pre-defined list */
1822 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
1824 int e = move_stepsize_list[i].element;
1826 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
1829 /* ---------- initialize collect score ----------------------------------- */
1831 /* initialize collect score values for custom elements from initial value */
1832 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1833 if (IS_CUSTOM_ELEMENT(i))
1834 element_info[i].collect_score = element_info[i].collect_score_initial;
1836 /* ---------- initialize collect count ----------------------------------- */
1838 /* initialize collect count values for non-custom elements */
1839 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1840 if (!IS_CUSTOM_ELEMENT(i))
1841 element_info[i].collect_count_initial = 0;
1843 /* add collect count values for all elements from pre-defined list */
1844 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
1845 element_info[collect_count_list[i].element].collect_count_initial =
1846 collect_count_list[i].count;
1848 /* ---------- initialize access direction -------------------------------- */
1850 /* initialize access direction values to default (access from every side) */
1851 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1852 if (!IS_CUSTOM_ELEMENT(i))
1853 element_info[i].access_direction = MV_ALL_DIRECTIONS;
1855 /* set access direction value for certain elements from pre-defined list */
1856 for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
1857 element_info[access_direction_list[i].element].access_direction =
1858 access_direction_list[i].direction;
1860 /* ---------- initialize explosion content ------------------------------- */
1861 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1863 if (IS_CUSTOM_ELEMENT(i))
1866 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
1868 /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
1870 element_info[i].content.e[x][y] =
1871 (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
1872 i == EL_PLAYER_2 ? EL_EMERALD_RED :
1873 i == EL_PLAYER_3 ? EL_EMERALD :
1874 i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
1875 i == EL_MOLE ? EL_EMERALD_RED :
1876 i == EL_PENGUIN ? EL_EMERALD_PURPLE :
1877 i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
1878 i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
1879 i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
1880 i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
1881 i == EL_WALL_EMERALD ? EL_EMERALD :
1882 i == EL_WALL_DIAMOND ? EL_DIAMOND :
1883 i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
1884 i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
1885 i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
1886 i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
1887 i == EL_WALL_PEARL ? EL_PEARL :
1888 i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
1893 /* ---------- initialize recursion detection ------------------------------ */
1894 recursion_loop_depth = 0;
1895 recursion_loop_detected = FALSE;
1896 recursion_loop_element = EL_UNDEFINED;
1899 int get_num_special_action(int element, int action_first, int action_last)
1901 int num_special_action = 0;
1904 for (i = action_first; i <= action_last; i++)
1906 boolean found = FALSE;
1908 for (j = 0; j < NUM_DIRECTIONS; j++)
1909 if (el_act_dir2img(element, i, j) !=
1910 el_act_dir2img(element, ACTION_DEFAULT, j))
1914 num_special_action++;
1919 return num_special_action;
1924 =============================================================================
1926 -----------------------------------------------------------------------------
1927 initialize and start new game
1928 =============================================================================
1933 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
1934 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
1935 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
1936 boolean do_fading = (game_status == GAME_MODE_MAIN);
1939 game_status = GAME_MODE_PLAYING;
1943 /* don't play tapes over network */
1944 network_playing = (options.network && !tape.playing);
1946 for (i = 0; i < MAX_PLAYERS; i++)
1948 struct PlayerInfo *player = &stored_player[i];
1950 player->index_nr = i;
1951 player->index_bit = (1 << i);
1952 player->element_nr = EL_PLAYER_1 + i;
1954 player->present = FALSE;
1955 player->active = FALSE;
1956 player->killed = FALSE;
1959 player->effective_action = 0;
1960 player->programmed_action = 0;
1963 player->score_final = 0;
1965 player->gems_still_needed = level.gems_needed;
1966 player->sokobanfields_still_needed = 0;
1967 player->lights_still_needed = 0;
1968 player->friends_still_needed = 0;
1970 for (j = 0; j < MAX_NUM_KEYS; j++)
1971 player->key[j] = FALSE;
1973 player->num_white_keys = 0;
1975 player->dynabomb_count = 0;
1976 player->dynabomb_size = 1;
1977 player->dynabombs_left = 0;
1978 player->dynabomb_xl = FALSE;
1980 player->MovDir = MV_NONE;
1983 player->GfxDir = MV_NONE;
1984 player->GfxAction = ACTION_DEFAULT;
1986 player->StepFrame = 0;
1988 player->use_murphy = FALSE;
1989 player->artwork_element =
1990 (level.use_artwork_element[i] ? level.artwork_element[i] :
1991 player->element_nr);
1993 player->block_last_field = FALSE; /* initialized in InitPlayerField() */
1994 player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
1996 player->gravity = level.initial_player_gravity[i];
1998 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
2000 player->actual_frame_counter = 0;
2002 player->step_counter = 0;
2004 player->last_move_dir = MV_NONE;
2006 player->is_active = FALSE;
2008 player->is_waiting = FALSE;
2009 player->is_moving = FALSE;
2010 player->is_auto_moving = FALSE;
2011 player->is_digging = FALSE;
2012 player->is_snapping = FALSE;
2013 player->is_collecting = FALSE;
2014 player->is_pushing = FALSE;
2015 player->is_switching = FALSE;
2016 player->is_dropping = FALSE;
2017 player->is_dropping_pressed = FALSE;
2019 player->is_bored = FALSE;
2020 player->is_sleeping = FALSE;
2022 player->frame_counter_bored = -1;
2023 player->frame_counter_sleeping = -1;
2025 player->anim_delay_counter = 0;
2026 player->post_delay_counter = 0;
2028 player->dir_waiting = MV_NONE;
2029 player->action_waiting = ACTION_DEFAULT;
2030 player->last_action_waiting = ACTION_DEFAULT;
2031 player->special_action_bored = ACTION_DEFAULT;
2032 player->special_action_sleeping = ACTION_DEFAULT;
2034 player->switch_x = -1;
2035 player->switch_y = -1;
2037 player->drop_x = -1;
2038 player->drop_y = -1;
2040 player->show_envelope = 0;
2042 SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
2044 player->push_delay = -1; /* initialized when pushing starts */
2045 player->push_delay_value = game.initial_push_delay_value;
2047 player->drop_delay = 0;
2048 player->drop_pressed_delay = 0;
2050 player->last_jx = -1;
2051 player->last_jy = -1;
2055 player->shield_normal_time_left = 0;
2056 player->shield_deadly_time_left = 0;
2058 player->inventory_infinite_element = EL_UNDEFINED;
2059 player->inventory_size = 0;
2061 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
2062 SnapField(player, 0, 0);
2064 player->LevelSolved = FALSE;
2065 player->GameOver = FALSE;
2067 player->LevelSolved_GameEnd = FALSE;
2068 player->LevelSolved_SaveTape = FALSE;
2069 player->LevelSolved_SaveScore = FALSE;
2072 network_player_action_received = FALSE;
2074 #if defined(NETWORK_AVALIABLE)
2075 /* initial null action */
2076 if (network_playing)
2077 SendToServer_MovePlayer(MV_NONE);
2086 TimeLeft = level.time;
2089 ScreenMovDir = MV_NONE;
2093 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
2095 AllPlayersGone = FALSE;
2097 game.yamyam_content_nr = 0;
2098 game.magic_wall_active = FALSE;
2099 game.magic_wall_time_left = 0;
2100 game.light_time_left = 0;
2101 game.timegate_time_left = 0;
2102 game.switchgate_pos = 0;
2103 game.wind_direction = level.wind_direction_initial;
2105 #if !USE_PLAYER_GRAVITY
2106 game.gravity = FALSE;
2107 game.explosions_delayed = TRUE;
2110 game.lenses_time_left = 0;
2111 game.magnify_time_left = 0;
2113 game.ball_state = level.ball_state_initial;
2114 game.ball_content_nr = 0;
2116 game.envelope_active = FALSE;
2118 /* set focus to local player for network games, else to all players */
2119 game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
2120 game.centered_player_nr_next = game.centered_player_nr;
2121 game.set_centered_player = FALSE;
2123 if (network_playing && tape.recording)
2125 /* store client dependent player focus when recording network games */
2126 tape.centered_player_nr_next = game.centered_player_nr_next;
2127 tape.set_centered_player = TRUE;
2130 for (i = 0; i < NUM_BELTS; i++)
2132 game.belt_dir[i] = MV_NONE;
2133 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
2136 for (i = 0; i < MAX_NUM_AMOEBA; i++)
2137 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
2139 SCAN_PLAYFIELD(x, y)
2141 Feld[x][y] = level.field[x][y];
2142 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
2143 ChangeDelay[x][y] = 0;
2144 ChangePage[x][y] = -1;
2145 #if USE_NEW_CUSTOM_VALUE
2146 CustomValue[x][y] = 0; /* initialized in InitField() */
2148 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
2150 WasJustMoving[x][y] = 0;
2151 WasJustFalling[x][y] = 0;
2152 CheckCollision[x][y] = 0;
2153 CheckImpact[x][y] = 0;
2155 Pushed[x][y] = FALSE;
2157 ChangeCount[x][y] = 0;
2158 ChangeEvent[x][y] = -1;
2160 ExplodePhase[x][y] = 0;
2161 ExplodeDelay[x][y] = 0;
2162 ExplodeField[x][y] = EX_TYPE_NONE;
2164 RunnerVisit[x][y] = 0;
2165 PlayerVisit[x][y] = 0;
2168 GfxRandom[x][y] = INIT_GFX_RANDOM();
2169 GfxElement[x][y] = EL_UNDEFINED;
2170 GfxAction[x][y] = ACTION_DEFAULT;
2171 GfxDir[x][y] = MV_NONE;
2174 SCAN_PLAYFIELD(x, y)
2176 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
2178 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
2180 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
2183 InitField(x, y, TRUE);
2188 for (i = 0; i < MAX_PLAYERS; i++)
2190 struct PlayerInfo *player = &stored_player[i];
2192 /* set number of special actions for bored and sleeping animation */
2193 player->num_special_action_bored =
2194 get_num_special_action(player->artwork_element,
2195 ACTION_BORING_1, ACTION_BORING_LAST);
2196 player->num_special_action_sleeping =
2197 get_num_special_action(player->artwork_element,
2198 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
2201 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
2202 emulate_sb ? EMU_SOKOBAN :
2203 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
2205 #if USE_NEW_ALL_SLIPPERY
2206 /* initialize type of slippery elements */
2207 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2209 if (!IS_CUSTOM_ELEMENT(i))
2211 /* default: elements slip down either to the left or right randomly */
2212 element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
2214 /* SP style elements prefer to slip down on the left side */
2215 if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
2216 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
2218 /* BD style elements prefer to slip down on the left side */
2219 if (game.emulation == EMU_BOULDERDASH)
2220 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
2225 /* initialize explosion and ignition delay */
2226 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2228 if (!IS_CUSTOM_ELEMENT(i))
2231 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
2232 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
2233 game.emulation == EMU_SUPAPLEX ? 3 : 2);
2234 int last_phase = (num_phase + 1) * delay;
2235 int half_phase = (num_phase / 2) * delay;
2237 element_info[i].explosion_delay = last_phase - 1;
2238 element_info[i].ignition_delay = half_phase;
2240 if (i == EL_BLACK_ORB)
2241 element_info[i].ignition_delay = 1;
2245 if (element_info[i].explosion_delay < 1) /* !!! check again !!! */
2246 element_info[i].explosion_delay = 1;
2248 if (element_info[i].ignition_delay < 1) /* !!! check again !!! */
2249 element_info[i].ignition_delay = 1;
2253 /* correct non-moving belts to start moving left */
2254 for (i = 0; i < NUM_BELTS; i++)
2255 if (game.belt_dir[i] == MV_NONE)
2256 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
2258 /* check if any connected player was not found in playfield */
2259 for (i = 0; i < MAX_PLAYERS; i++)
2261 struct PlayerInfo *player = &stored_player[i];
2263 if (player->connected && !player->present)
2265 for (j = 0; j < MAX_PLAYERS; j++)
2267 struct PlayerInfo *some_player = &stored_player[j];
2268 int jx = some_player->jx, jy = some_player->jy;
2270 /* assign first free player found that is present in the playfield */
2271 if (some_player->present && !some_player->connected)
2273 player->present = TRUE;
2274 player->active = TRUE;
2276 some_player->present = FALSE;
2277 some_player->active = FALSE;
2279 player->artwork_element = some_player->artwork_element;
2281 player->block_last_field = some_player->block_last_field;
2282 player->block_delay_adjustment = some_player->block_delay_adjustment;
2284 StorePlayer[jx][jy] = player->element_nr;
2285 player->jx = player->last_jx = jx;
2286 player->jy = player->last_jy = jy;
2296 /* when playing a tape, eliminate all players who do not participate */
2298 for (i = 0; i < MAX_PLAYERS; i++)
2300 if (stored_player[i].active && !tape.player_participates[i])
2302 struct PlayerInfo *player = &stored_player[i];
2303 int jx = player->jx, jy = player->jy;
2305 player->active = FALSE;
2306 StorePlayer[jx][jy] = 0;
2307 Feld[jx][jy] = EL_EMPTY;
2311 else if (!options.network && !setup.team_mode) /* && !tape.playing */
2313 /* when in single player mode, eliminate all but the first active player */
2315 for (i = 0; i < MAX_PLAYERS; i++)
2317 if (stored_player[i].active)
2319 for (j = i + 1; j < MAX_PLAYERS; j++)
2321 if (stored_player[j].active)
2323 struct PlayerInfo *player = &stored_player[j];
2324 int jx = player->jx, jy = player->jy;
2326 player->active = FALSE;
2327 player->present = FALSE;
2329 StorePlayer[jx][jy] = 0;
2330 Feld[jx][jy] = EL_EMPTY;
2337 /* when recording the game, store which players take part in the game */
2340 for (i = 0; i < MAX_PLAYERS; i++)
2341 if (stored_player[i].active)
2342 tape.player_participates[i] = TRUE;
2347 for (i = 0; i < MAX_PLAYERS; i++)
2349 struct PlayerInfo *player = &stored_player[i];
2351 printf("Player %d: present == %d, connected == %d, active == %d.\n",
2356 if (local_player == player)
2357 printf("Player %d is local player.\n", i+1);
2361 if (BorderElement == EL_EMPTY)
2364 SBX_Right = lev_fieldx - SCR_FIELDX;
2366 SBY_Lower = lev_fieldy - SCR_FIELDY;
2371 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
2373 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
2376 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
2377 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
2379 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
2380 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
2382 /* if local player not found, look for custom element that might create
2383 the player (make some assumptions about the right custom element) */
2384 if (!local_player->present)
2386 int start_x = 0, start_y = 0;
2387 int found_rating = 0;
2388 int found_element = EL_UNDEFINED;
2389 int player_nr = local_player->index_nr;
2391 SCAN_PLAYFIELD(x, y)
2393 int element = Feld[x][y];
2398 if (level.use_start_element[player_nr] &&
2399 level.start_element[player_nr] == element &&
2406 found_element = element;
2409 if (!IS_CUSTOM_ELEMENT(element))
2412 if (CAN_CHANGE(element))
2414 for (i = 0; i < element_info[element].num_change_pages; i++)
2416 /* check for player created from custom element as single target */
2417 content = element_info[element].change_page[i].target_element;
2418 is_player = ELEM_IS_PLAYER(content);
2420 if (is_player && (found_rating < 3 || element < found_element))
2426 found_element = element;
2431 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
2433 /* check for player created from custom element as explosion content */
2434 content = element_info[element].content.e[xx][yy];
2435 is_player = ELEM_IS_PLAYER(content);
2437 if (is_player && (found_rating < 2 || element < found_element))
2439 start_x = x + xx - 1;
2440 start_y = y + yy - 1;
2443 found_element = element;
2446 if (!CAN_CHANGE(element))
2449 for (i = 0; i < element_info[element].num_change_pages; i++)
2451 /* check for player created from custom element as extended target */
2453 element_info[element].change_page[i].target_content.e[xx][yy];
2455 is_player = ELEM_IS_PLAYER(content);
2457 if (is_player && (found_rating < 1 || element < found_element))
2459 start_x = x + xx - 1;
2460 start_y = y + yy - 1;
2463 found_element = element;
2469 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
2470 start_x > SBX_Right + MIDPOSX ? SBX_Right :
2473 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
2474 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
2479 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2480 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2481 local_player->jx - MIDPOSX);
2483 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2484 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2485 local_player->jy - MIDPOSY);
2490 if (!game.restart_level)
2491 CloseDoor(DOOR_CLOSE_1);
2494 FadeOut(REDRAW_FIELD);
2496 /* !!! FIX THIS (START) !!! */
2497 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2499 InitGameEngine_EM();
2501 /* blit playfield from scroll buffer to normal back buffer for fading in */
2502 BlitScreenToBitmap_EM(backbuffer);
2509 /* after drawing the level, correct some elements */
2510 if (game.timegate_time_left == 0)
2511 CloseAllOpenTimegates();
2513 /* blit playfield from scroll buffer to normal back buffer for fading in */
2514 if (setup.soft_scrolling)
2515 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
2517 redraw_mask |= REDRAW_FROM_BACKBUFFER;
2519 /* !!! FIX THIS (END) !!! */
2522 FadeIn(REDRAW_FIELD);
2526 if (!game.restart_level)
2528 /* copy default game door content to main double buffer */
2529 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2530 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2533 SetPanelBackground();
2534 SetDrawBackgroundMask(REDRAW_DOOR_1);
2536 DrawGameDoorValues();
2538 if (!game.restart_level)
2542 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
2543 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
2544 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
2548 /* copy actual game door content to door double buffer for OpenDoor() */
2549 BlitBitmap(drawto, bitmap_db_door,
2550 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
2552 OpenDoor(DOOR_OPEN_ALL);
2554 PlaySound(SND_GAME_STARTING);
2556 if (setup.sound_music)
2559 KeyboardAutoRepeatOffUnlessAutoplay();
2563 for (i = 0; i < MAX_PLAYERS; i++)
2564 printf("Player %d %sactive.\n",
2565 i + 1, (stored_player[i].active ? "" : "not "));
2576 game.restart_level = FALSE;
2579 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
2581 /* this is used for non-R'n'D game engines to update certain engine values */
2583 /* needed to determine if sounds are played within the visible screen area */
2584 scroll_x = actual_scroll_x;
2585 scroll_y = actual_scroll_y;
2588 void InitMovDir(int x, int y)
2590 int i, element = Feld[x][y];
2591 static int xy[4][2] =
2598 static int direction[3][4] =
2600 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
2601 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
2602 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
2611 Feld[x][y] = EL_BUG;
2612 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
2615 case EL_SPACESHIP_RIGHT:
2616 case EL_SPACESHIP_UP:
2617 case EL_SPACESHIP_LEFT:
2618 case EL_SPACESHIP_DOWN:
2619 Feld[x][y] = EL_SPACESHIP;
2620 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
2623 case EL_BD_BUTTERFLY_RIGHT:
2624 case EL_BD_BUTTERFLY_UP:
2625 case EL_BD_BUTTERFLY_LEFT:
2626 case EL_BD_BUTTERFLY_DOWN:
2627 Feld[x][y] = EL_BD_BUTTERFLY;
2628 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
2631 case EL_BD_FIREFLY_RIGHT:
2632 case EL_BD_FIREFLY_UP:
2633 case EL_BD_FIREFLY_LEFT:
2634 case EL_BD_FIREFLY_DOWN:
2635 Feld[x][y] = EL_BD_FIREFLY;
2636 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
2639 case EL_PACMAN_RIGHT:
2641 case EL_PACMAN_LEFT:
2642 case EL_PACMAN_DOWN:
2643 Feld[x][y] = EL_PACMAN;
2644 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
2647 case EL_YAMYAM_LEFT:
2648 case EL_YAMYAM_RIGHT:
2650 case EL_YAMYAM_DOWN:
2651 Feld[x][y] = EL_YAMYAM;
2652 MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
2655 case EL_SP_SNIKSNAK:
2656 MovDir[x][y] = MV_UP;
2659 case EL_SP_ELECTRON:
2660 MovDir[x][y] = MV_LEFT;
2667 Feld[x][y] = EL_MOLE;
2668 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
2672 if (IS_CUSTOM_ELEMENT(element))
2674 struct ElementInfo *ei = &element_info[element];
2675 int move_direction_initial = ei->move_direction_initial;
2676 int move_pattern = ei->move_pattern;
2678 if (move_direction_initial == MV_START_PREVIOUS)
2680 if (MovDir[x][y] != MV_NONE)
2683 move_direction_initial = MV_START_AUTOMATIC;
2686 if (move_direction_initial == MV_START_RANDOM)
2687 MovDir[x][y] = 1 << RND(4);
2688 else if (move_direction_initial & MV_ANY_DIRECTION)
2689 MovDir[x][y] = move_direction_initial;
2690 else if (move_pattern == MV_ALL_DIRECTIONS ||
2691 move_pattern == MV_TURNING_LEFT ||
2692 move_pattern == MV_TURNING_RIGHT ||
2693 move_pattern == MV_TURNING_LEFT_RIGHT ||
2694 move_pattern == MV_TURNING_RIGHT_LEFT ||
2695 move_pattern == MV_TURNING_RANDOM)
2696 MovDir[x][y] = 1 << RND(4);
2697 else if (move_pattern == MV_HORIZONTAL)
2698 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
2699 else if (move_pattern == MV_VERTICAL)
2700 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
2701 else if (move_pattern & MV_ANY_DIRECTION)
2702 MovDir[x][y] = element_info[element].move_pattern;
2703 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
2704 move_pattern == MV_ALONG_RIGHT_SIDE)
2706 /* use random direction as default start direction */
2707 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2708 MovDir[x][y] = 1 << RND(4);
2710 for (i = 0; i < NUM_DIRECTIONS; i++)
2712 int x1 = x + xy[i][0];
2713 int y1 = y + xy[i][1];
2715 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2717 if (move_pattern == MV_ALONG_RIGHT_SIDE)
2718 MovDir[x][y] = direction[0][i];
2720 MovDir[x][y] = direction[1][i];
2729 MovDir[x][y] = 1 << RND(4);
2731 if (element != EL_BUG &&
2732 element != EL_SPACESHIP &&
2733 element != EL_BD_BUTTERFLY &&
2734 element != EL_BD_FIREFLY)
2737 for (i = 0; i < NUM_DIRECTIONS; i++)
2739 int x1 = x + xy[i][0];
2740 int y1 = y + xy[i][1];
2742 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2744 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
2746 MovDir[x][y] = direction[0][i];
2749 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
2750 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
2752 MovDir[x][y] = direction[1][i];
2761 GfxDir[x][y] = MovDir[x][y];
2764 void InitAmoebaNr(int x, int y)
2767 int group_nr = AmoebeNachbarNr(x, y);
2771 for (i = 1; i < MAX_NUM_AMOEBA; i++)
2773 if (AmoebaCnt[i] == 0)
2781 AmoebaNr[x][y] = group_nr;
2782 AmoebaCnt[group_nr]++;
2783 AmoebaCnt2[group_nr]++;
2786 static void PlayerWins(struct PlayerInfo *player)
2788 player->LevelSolved = TRUE;
2789 player->GameOver = TRUE;
2791 player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2792 level.native_em_level->lev->score : player->score);
2797 static int time, time_final;
2798 static int score, score_final;
2799 static int game_over_delay = 0;
2800 int game_over_delay_value = 50;
2802 if (!local_player->LevelSolved_GameEnd)
2806 /* do not start end game actions before the player stops moving (to exit) */
2807 if (local_player->MovPos)
2810 local_player->LevelSolved_GameEnd = TRUE;
2811 local_player->LevelSolved_SaveTape = tape.recording;
2812 local_player->LevelSolved_SaveScore = !tape.playing;
2814 if (tape.auto_play) /* tape might already be stopped here */
2815 tape.auto_play_level_solved = TRUE;
2821 game_over_delay = game_over_delay_value;
2823 time = time_final = (level.time == 0 ? TimePlayed : TimeLeft);
2824 score = score_final = local_player->score_final;
2829 score_final += TimeLeft * level.score[SC_TIME_BONUS];
2831 else if (level.time == 0 && TimePlayed < 999)
2834 score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
2837 local_player->score_final = score_final;
2839 if (level_editor_test_game)
2842 score = score_final;
2844 DrawGameValue_Time(time);
2845 DrawGameValue_Score(score);
2848 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
2850 if (ExitX >= 0 && ExitY >= 0) /* local player has left the level */
2852 /* close exit door after last player */
2853 if ((AllPlayersGone &&
2854 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
2855 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
2856 Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
2857 Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
2858 Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
2860 int element = Feld[ExitX][ExitY];
2863 if (element == EL_EM_EXIT_OPEN ||
2864 element == EL_EM_STEEL_EXIT_OPEN)
2871 Feld[ExitX][ExitY] =
2872 (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
2873 element == EL_EM_EXIT_OPEN ? EL_EM_EXIT_CLOSING :
2874 element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING:
2875 element == EL_STEEL_EXIT_OPEN ? EL_STEEL_EXIT_CLOSING:
2876 EL_EM_STEEL_EXIT_CLOSING);
2878 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
2882 /* player disappears */
2883 DrawLevelField(ExitX, ExitY);
2886 for (i = 0; i < MAX_PLAYERS; i++)
2888 struct PlayerInfo *player = &stored_player[i];
2890 if (player->present)
2892 RemovePlayer(player);
2894 /* player disappears */
2895 DrawLevelField(player->jx, player->jy);
2900 PlaySound(SND_GAME_WINNING);
2903 if (game_over_delay > 0)
2910 if (time != time_final)
2912 int time_to_go = ABS(time_final - time);
2913 int time_count_dir = (time < time_final ? +1 : -1);
2914 int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
2916 time += time_count_steps * time_count_dir;
2917 score += time_count_steps * level.score[SC_TIME_BONUS];
2919 DrawGameValue_Time(time);
2920 DrawGameValue_Score(score);
2922 if (time == time_final)
2923 StopSound(SND_GAME_LEVELTIME_BONUS);
2924 else if (setup.sound_loops)
2925 PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
2927 PlaySound(SND_GAME_LEVELTIME_BONUS);
2934 boolean raise_level = FALSE;
2936 CloseDoor(DOOR_CLOSE_1);
2938 if (local_player->LevelSolved_SaveTape)
2945 SaveTapeChecked(tape.level_nr); /* ask to save tape */
2947 SaveTape(tape.level_nr); /* ask to save tape */
2951 if (level_editor_test_game)
2953 game_status = GAME_MODE_MAIN;
2960 if (!local_player->LevelSolved_SaveScore)
2962 FadeOut(REDRAW_FIELD);
2964 game_status = GAME_MODE_MAIN;
2966 DrawAndFadeInMainMenu(REDRAW_FIELD);
2971 if (level_nr == leveldir_current->handicap_level)
2973 leveldir_current->handicap_level++;
2974 SaveLevelSetup_SeriesInfo();
2977 if (level_nr < leveldir_current->last_level)
2978 raise_level = TRUE; /* advance to next level */
2980 if ((hi_pos = NewHiScore()) >= 0)
2982 game_status = GAME_MODE_SCORES;
2984 DrawHallOfFame(hi_pos);
2994 FadeOut(REDRAW_FIELD);
2996 game_status = GAME_MODE_MAIN;
3004 DrawAndFadeInMainMenu(REDRAW_FIELD);
3013 LoadScore(level_nr);
3015 if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
3016 local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score)
3019 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
3021 if (local_player->score_final > highscore[k].Score)
3023 /* player has made it to the hall of fame */
3025 if (k < MAX_SCORE_ENTRIES - 1)
3027 int m = MAX_SCORE_ENTRIES - 1;
3030 for (l = k; l < MAX_SCORE_ENTRIES; l++)
3031 if (strEqual(setup.player_name, highscore[l].Name))
3033 if (m == k) /* player's new highscore overwrites his old one */
3037 for (l = m; l > k; l--)
3039 strcpy(highscore[l].Name, highscore[l - 1].Name);
3040 highscore[l].Score = highscore[l - 1].Score;
3047 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
3048 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
3049 highscore[k].Score = local_player->score_final;
3055 else if (!strncmp(setup.player_name, highscore[k].Name,
3056 MAX_PLAYER_NAME_LEN))
3057 break; /* player already there with a higher score */
3063 SaveScore(level_nr);
3068 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
3070 int element = Feld[x][y];
3071 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3072 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
3073 int horiz_move = (dx != 0);
3074 int sign = (horiz_move ? dx : dy);
3075 int step = sign * element_info[element].move_stepsize;
3077 /* special values for move stepsize for spring and things on conveyor belt */
3080 if (CAN_FALL(element) &&
3081 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
3082 step = sign * MOVE_STEPSIZE_NORMAL / 2;
3083 else if (element == EL_SPRING)
3084 step = sign * MOVE_STEPSIZE_NORMAL * 2;
3090 inline static int getElementMoveStepsize(int x, int y)
3092 return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
3095 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
3097 if (player->GfxAction != action || player->GfxDir != dir)
3100 printf("Player frame reset! (%d => %d, %d => %d)\n",
3101 player->GfxAction, action, player->GfxDir, dir);
3104 player->GfxAction = action;
3105 player->GfxDir = dir;
3107 player->StepFrame = 0;
3111 #if USE_GFX_RESET_GFX_ANIMATION
3112 static void ResetGfxFrame(int x, int y, boolean redraw)
3114 int element = Feld[x][y];
3115 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3116 int last_gfx_frame = GfxFrame[x][y];
3118 if (graphic_info[graphic].anim_global_sync)
3119 GfxFrame[x][y] = FrameCounter;
3120 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
3121 GfxFrame[x][y] = CustomValue[x][y];
3122 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
3123 GfxFrame[x][y] = element_info[element].collect_score;
3124 else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
3125 GfxFrame[x][y] = ChangeDelay[x][y];
3127 if (redraw && GfxFrame[x][y] != last_gfx_frame)
3128 DrawLevelGraphicAnimation(x, y, graphic);
3132 static void ResetGfxAnimation(int x, int y)
3134 GfxAction[x][y] = ACTION_DEFAULT;
3135 GfxDir[x][y] = MovDir[x][y];
3138 #if USE_GFX_RESET_GFX_ANIMATION
3139 ResetGfxFrame(x, y, FALSE);
3143 static void ResetRandomAnimationValue(int x, int y)
3145 GfxRandom[x][y] = INIT_GFX_RANDOM();
3148 void InitMovingField(int x, int y, int direction)
3150 int element = Feld[x][y];
3151 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3152 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
3155 boolean is_moving_before, is_moving_after;
3157 boolean continues_moving = (WasJustMoving[x][y] && direction == MovDir[x][y]);
3160 /* check if element was/is moving or being moved before/after mode change */
3162 is_moving_before = WasJustMoving[x][y];
3164 is_moving_before = (getElementMoveStepsizeExt(x, y, MovDir[x][y]) != 0);
3166 is_moving_after = (getElementMoveStepsizeExt(x, y, direction) != 0);
3168 /* reset animation only for moving elements which change direction of moving
3169 or which just started or stopped moving
3170 (else CEs with property "can move" / "not moving" are reset each frame) */
3171 #if USE_GFX_RESET_ONLY_WHEN_MOVING
3173 if (is_moving_before != is_moving_after ||
3174 direction != MovDir[x][y])
3175 ResetGfxAnimation(x, y);
3177 if ((is_moving_before || is_moving_after) && !continues_moving)
3178 ResetGfxAnimation(x, y);
3181 if (!continues_moving)
3182 ResetGfxAnimation(x, y);
3185 MovDir[x][y] = direction;
3186 GfxDir[x][y] = direction;
3188 #if USE_GFX_RESET_ONLY_WHEN_MOVING
3189 GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
3190 direction == MV_DOWN && CAN_FALL(element) ?
3191 ACTION_FALLING : ACTION_MOVING);
3193 GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
3194 ACTION_FALLING : ACTION_MOVING);
3197 /* this is needed for CEs with property "can move" / "not moving" */
3199 if (is_moving_after)
3201 if (Feld[newx][newy] == EL_EMPTY)
3202 Feld[newx][newy] = EL_BLOCKED;
3204 MovDir[newx][newy] = MovDir[x][y];
3206 #if USE_NEW_CUSTOM_VALUE
3207 CustomValue[newx][newy] = CustomValue[x][y];
3210 GfxFrame[newx][newy] = GfxFrame[x][y];
3211 GfxRandom[newx][newy] = GfxRandom[x][y];
3212 GfxAction[newx][newy] = GfxAction[x][y];
3213 GfxDir[newx][newy] = GfxDir[x][y];
3217 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
3219 int direction = MovDir[x][y];
3220 int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
3221 int newy = y + (direction & MV_UP ? -1 : direction & MV_DOWN ? +1 : 0);
3227 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
3229 int oldx = x, oldy = y;
3230 int direction = MovDir[x][y];
3232 if (direction == MV_LEFT)
3234 else if (direction == MV_RIGHT)
3236 else if (direction == MV_UP)
3238 else if (direction == MV_DOWN)
3241 *comes_from_x = oldx;
3242 *comes_from_y = oldy;
3245 int MovingOrBlocked2Element(int x, int y)
3247 int element = Feld[x][y];
3249 if (element == EL_BLOCKED)
3253 Blocked2Moving(x, y, &oldx, &oldy);
3254 return Feld[oldx][oldy];
3260 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
3262 /* like MovingOrBlocked2Element(), but if element is moving
3263 and (x,y) is the field the moving element is just leaving,
3264 return EL_BLOCKED instead of the element value */
3265 int element = Feld[x][y];
3267 if (IS_MOVING(x, y))
3269 if (element == EL_BLOCKED)
3273 Blocked2Moving(x, y, &oldx, &oldy);
3274 return Feld[oldx][oldy];
3283 static void RemoveField(int x, int y)
3285 Feld[x][y] = EL_EMPTY;
3291 #if USE_NEW_CUSTOM_VALUE
3292 CustomValue[x][y] = 0;
3296 ChangeDelay[x][y] = 0;
3297 ChangePage[x][y] = -1;
3298 Pushed[x][y] = FALSE;
3301 ExplodeField[x][y] = EX_TYPE_NONE;
3304 GfxElement[x][y] = EL_UNDEFINED;
3305 GfxAction[x][y] = ACTION_DEFAULT;
3306 GfxDir[x][y] = MV_NONE;
3309 void RemoveMovingField(int x, int y)
3311 int oldx = x, oldy = y, newx = x, newy = y;
3312 int element = Feld[x][y];
3313 int next_element = EL_UNDEFINED;
3315 if (element != EL_BLOCKED && !IS_MOVING(x, y))
3318 if (IS_MOVING(x, y))
3320 Moving2Blocked(x, y, &newx, &newy);
3322 if (Feld[newx][newy] != EL_BLOCKED)
3324 /* element is moving, but target field is not free (blocked), but
3325 already occupied by something different (example: acid pool);
3326 in this case, only remove the moving field, but not the target */
3328 RemoveField(oldx, oldy);
3330 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
3332 DrawLevelField(oldx, oldy);
3337 else if (element == EL_BLOCKED)
3339 Blocked2Moving(x, y, &oldx, &oldy);
3340 if (!IS_MOVING(oldx, oldy))
3344 if (element == EL_BLOCKED &&
3345 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
3346 Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
3347 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
3348 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
3349 Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
3350 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
3351 next_element = get_next_element(Feld[oldx][oldy]);
3353 RemoveField(oldx, oldy);
3354 RemoveField(newx, newy);
3356 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
3358 if (next_element != EL_UNDEFINED)
3359 Feld[oldx][oldy] = next_element;
3361 DrawLevelField(oldx, oldy);
3362 DrawLevelField(newx, newy);
3365 void DrawDynamite(int x, int y)
3367 int sx = SCREENX(x), sy = SCREENY(y);
3368 int graphic = el2img(Feld[x][y]);
3371 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
3374 if (IS_WALKABLE_INSIDE(Back[x][y]))
3378 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
3379 else if (Store[x][y])
3380 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
3382 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
3384 if (Back[x][y] || Store[x][y])
3385 DrawGraphicThruMask(sx, sy, graphic, frame);
3387 DrawGraphic(sx, sy, graphic, frame);
3390 void CheckDynamite(int x, int y)
3392 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
3396 if (MovDelay[x][y] != 0)
3399 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
3405 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
3410 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
3412 boolean num_checked_players = 0;
3415 for (i = 0; i < MAX_PLAYERS; i++)
3417 if (stored_player[i].active)
3419 int sx = stored_player[i].jx;
3420 int sy = stored_player[i].jy;
3422 if (num_checked_players == 0)
3429 *sx1 = MIN(*sx1, sx);
3430 *sy1 = MIN(*sy1, sy);
3431 *sx2 = MAX(*sx2, sx);
3432 *sy2 = MAX(*sy2, sy);
3435 num_checked_players++;
3440 static boolean checkIfAllPlayersFitToScreen_RND()
3442 int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
3444 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
3446 return (sx2 - sx1 < SCR_FIELDX &&
3447 sy2 - sy1 < SCR_FIELDY);
3450 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
3452 int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
3454 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
3456 *sx = (sx1 + sx2) / 2;
3457 *sy = (sy1 + sy2) / 2;
3460 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
3461 boolean center_screen, boolean quick_relocation)
3463 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3464 boolean no_delay = (tape.warp_forward);
3465 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
3466 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
3468 if (quick_relocation)
3470 int offset = (setup.scroll_delay ? 3 : 0);
3472 if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
3476 scroll_x = (x < SBX_Left + MIDPOSX ? SBX_Left :
3477 x > SBX_Right + MIDPOSX ? SBX_Right :
3480 scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
3481 y > SBY_Lower + MIDPOSY ? SBY_Lower :
3486 /* quick relocation (without scrolling), but do not center screen */
3488 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
3489 old_x > SBX_Right + MIDPOSX ? SBX_Right :
3492 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
3493 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
3496 int offset_x = x + (scroll_x - center_scroll_x);
3497 int offset_y = y + (scroll_y - center_scroll_y);
3499 scroll_x = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
3500 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
3501 offset_x - MIDPOSX);
3503 scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
3504 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
3505 offset_y - MIDPOSY);
3510 if ((move_dir == MV_LEFT && scroll_x > x - MIDPOSX + offset) ||
3511 (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
3512 scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
3514 if ((move_dir == MV_UP && scroll_y > y - MIDPOSY + offset) ||
3515 (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
3516 scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
3518 /* don't scroll over playfield boundaries */
3519 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
3520 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
3522 /* don't scroll over playfield boundaries */
3523 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
3524 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
3527 RedrawPlayfield(TRUE, 0,0,0,0);
3531 int scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left :
3532 x > SBX_Right + MIDPOSX ? SBX_Right :
3535 int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
3536 y > SBY_Lower + MIDPOSY ? SBY_Lower :
3539 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
3541 while (scroll_x != scroll_xx || scroll_y != scroll_yy)
3544 int fx = FX, fy = FY;
3546 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
3547 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
3549 if (dx == 0 && dy == 0) /* no scrolling needed at all */
3555 fx += dx * TILEX / 2;
3556 fy += dy * TILEY / 2;
3558 ScrollLevel(dx, dy);
3561 /* scroll in two steps of half tile size to make things smoother */
3562 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
3564 Delay(wait_delay_value);
3566 /* scroll second step to align at full tile size */
3568 Delay(wait_delay_value);
3573 Delay(wait_delay_value);
3577 void RelocatePlayer(int jx, int jy, int el_player_raw)
3579 int el_player = GET_PLAYER_ELEMENT(el_player_raw);
3580 int player_nr = GET_PLAYER_NR(el_player);
3581 struct PlayerInfo *player = &stored_player[player_nr];
3582 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3583 boolean no_delay = (tape.warp_forward);
3584 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
3585 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
3586 int old_jx = player->jx;
3587 int old_jy = player->jy;
3588 int old_element = Feld[old_jx][old_jy];
3589 int element = Feld[jx][jy];
3590 boolean player_relocated = (old_jx != jx || old_jy != jy);
3592 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
3593 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
3594 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
3595 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
3596 int leave_side_horiz = move_dir_horiz;
3597 int leave_side_vert = move_dir_vert;
3598 int enter_side = enter_side_horiz | enter_side_vert;
3599 int leave_side = leave_side_horiz | leave_side_vert;
3601 if (player->GameOver) /* do not reanimate dead player */
3604 if (!player_relocated) /* no need to relocate the player */
3607 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
3609 RemoveField(jx, jy); /* temporarily remove newly placed player */
3610 DrawLevelField(jx, jy);
3613 if (player->present)
3615 while (player->MovPos)
3617 ScrollPlayer(player, SCROLL_GO_ON);
3618 ScrollScreen(NULL, SCROLL_GO_ON);
3620 AdvanceFrameAndPlayerCounters(player->index_nr);
3625 Delay(wait_delay_value);
3628 DrawPlayer(player); /* needed here only to cleanup last field */
3629 DrawLevelField(player->jx, player->jy); /* remove player graphic */
3631 player->is_moving = FALSE;
3634 if (IS_CUSTOM_ELEMENT(old_element))
3635 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
3637 player->index_bit, leave_side);
3639 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
3641 player->index_bit, leave_side);
3643 Feld[jx][jy] = el_player;
3644 InitPlayerField(jx, jy, el_player, TRUE);
3646 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
3648 Feld[jx][jy] = element;
3649 InitField(jx, jy, FALSE);
3652 /* only visually relocate centered player */
3653 DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
3654 FALSE, level.instant_relocation);
3656 TestIfPlayerTouchesBadThing(jx, jy);
3657 TestIfPlayerTouchesCustomElement(jx, jy);
3659 if (IS_CUSTOM_ELEMENT(element))
3660 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
3661 player->index_bit, enter_side);
3663 CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
3664 player->index_bit, enter_side);
3667 void Explode(int ex, int ey, int phase, int mode)
3673 /* !!! eliminate this variable !!! */
3674 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
3676 if (game.explosions_delayed)
3678 ExplodeField[ex][ey] = mode;
3682 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
3684 int center_element = Feld[ex][ey];
3685 int artwork_element, explosion_element; /* set these values later */
3688 /* --- This is only really needed (and now handled) in "Impact()". --- */
3689 /* do not explode moving elements that left the explode field in time */
3690 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
3691 center_element == EL_EMPTY &&
3692 (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
3697 /* !!! at this place, the center element may be EL_BLOCKED !!! */
3698 if (mode == EX_TYPE_NORMAL ||
3699 mode == EX_TYPE_CENTER ||
3700 mode == EX_TYPE_CROSS)
3701 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
3704 /* remove things displayed in background while burning dynamite */
3705 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
3708 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
3710 /* put moving element to center field (and let it explode there) */
3711 center_element = MovingOrBlocked2Element(ex, ey);
3712 RemoveMovingField(ex, ey);
3713 Feld[ex][ey] = center_element;
3716 /* now "center_element" is finally determined -- set related values now */
3717 artwork_element = center_element; /* for custom player artwork */
3718 explosion_element = center_element; /* for custom player artwork */
3720 if (IS_PLAYER(ex, ey))
3722 int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
3724 artwork_element = stored_player[player_nr].artwork_element;
3726 if (level.use_explosion_element[player_nr])
3728 explosion_element = level.explosion_element[player_nr];
3729 artwork_element = explosion_element;
3734 if (mode == EX_TYPE_NORMAL ||
3735 mode == EX_TYPE_CENTER ||
3736 mode == EX_TYPE_CROSS)
3737 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
3740 last_phase = element_info[explosion_element].explosion_delay + 1;
3742 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
3744 int xx = x - ex + 1;
3745 int yy = y - ey + 1;
3748 if (!IN_LEV_FIELD(x, y) ||
3749 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
3750 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
3753 element = Feld[x][y];
3755 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
3757 element = MovingOrBlocked2Element(x, y);
3759 if (!IS_EXPLOSION_PROOF(element))
3760 RemoveMovingField(x, y);
3763 /* indestructible elements can only explode in center (but not flames) */
3764 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
3765 mode == EX_TYPE_BORDER)) ||
3766 element == EL_FLAMES)
3769 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
3770 behaviour, for example when touching a yamyam that explodes to rocks
3771 with active deadly shield, a rock is created under the player !!! */
3772 /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
3774 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
3775 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
3776 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
3778 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
3781 if (IS_ACTIVE_BOMB(element))
3783 /* re-activate things under the bomb like gate or penguin */
3784 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
3791 /* save walkable background elements while explosion on same tile */
3792 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
3793 (x != ex || y != ey || mode == EX_TYPE_BORDER))
3794 Back[x][y] = element;
3796 /* ignite explodable elements reached by other explosion */
3797 if (element == EL_EXPLOSION)
3798 element = Store2[x][y];
3800 if (AmoebaNr[x][y] &&
3801 (element == EL_AMOEBA_FULL ||
3802 element == EL_BD_AMOEBA ||
3803 element == EL_AMOEBA_GROWING))
3805 AmoebaCnt[AmoebaNr[x][y]]--;
3806 AmoebaCnt2[AmoebaNr[x][y]]--;
3811 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
3813 int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
3815 Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
3817 if (PLAYERINFO(ex, ey)->use_murphy)
3818 Store[x][y] = EL_EMPTY;
3821 /* !!! check this case -- currently needed for rnd_rado_negundo_v,
3822 !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
3823 else if (ELEM_IS_PLAYER(center_element))
3824 Store[x][y] = EL_EMPTY;
3825 else if (center_element == EL_YAMYAM)
3826 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
3827 else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
3828 Store[x][y] = element_info[center_element].content.e[xx][yy];
3830 /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
3831 (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
3832 otherwise) -- FIX THIS !!! */
3833 else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
3834 Store[x][y] = element_info[element].content.e[1][1];
3836 else if (!CAN_EXPLODE(element))
3837 Store[x][y] = element_info[element].content.e[1][1];
3840 Store[x][y] = EL_EMPTY;
3842 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
3843 center_element == EL_AMOEBA_TO_DIAMOND)
3844 Store2[x][y] = element;
3846 Feld[x][y] = EL_EXPLOSION;
3847 GfxElement[x][y] = artwork_element;
3849 ExplodePhase[x][y] = 1;
3850 ExplodeDelay[x][y] = last_phase;
3855 if (center_element == EL_YAMYAM)
3856 game.yamyam_content_nr =
3857 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
3869 GfxFrame[x][y] = 0; /* restart explosion animation */
3871 last_phase = ExplodeDelay[x][y];
3873 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
3877 /* activate this even in non-DEBUG version until cause for crash in
3878 getGraphicAnimationFrame() (see below) is found and eliminated */
3884 /* this can happen if the player leaves an explosion just in time */
3885 if (GfxElement[x][y] == EL_UNDEFINED)
3886 GfxElement[x][y] = EL_EMPTY;
3888 if (GfxElement[x][y] == EL_UNDEFINED)
3891 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
3892 printf("Explode(): This should never happen!\n");
3895 GfxElement[x][y] = EL_EMPTY;
3901 border_element = Store2[x][y];
3902 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3903 border_element = StorePlayer[x][y];
3905 if (phase == element_info[border_element].ignition_delay ||
3906 phase == last_phase)
3908 boolean border_explosion = FALSE;
3910 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
3911 !PLAYER_EXPLOSION_PROTECTED(x, y))
3913 KillPlayerUnlessExplosionProtected(x, y);
3914 border_explosion = TRUE;
3916 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
3918 Feld[x][y] = Store2[x][y];
3921 border_explosion = TRUE;
3923 else if (border_element == EL_AMOEBA_TO_DIAMOND)
3925 AmoebeUmwandeln(x, y);
3927 border_explosion = TRUE;
3930 /* if an element just explodes due to another explosion (chain-reaction),
3931 do not immediately end the new explosion when it was the last frame of
3932 the explosion (as it would be done in the following "if"-statement!) */
3933 if (border_explosion && phase == last_phase)
3937 if (phase == last_phase)
3941 element = Feld[x][y] = Store[x][y];
3942 Store[x][y] = Store2[x][y] = 0;
3943 GfxElement[x][y] = EL_UNDEFINED;
3945 /* player can escape from explosions and might therefore be still alive */
3946 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
3947 element <= EL_PLAYER_IS_EXPLODING_4)
3949 int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
3950 int explosion_element = EL_PLAYER_1 + player_nr;
3951 int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
3952 int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
3954 if (level.use_explosion_element[player_nr])
3955 explosion_element = level.explosion_element[player_nr];
3957 Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
3958 element_info[explosion_element].content.e[xx][yy]);
3961 /* restore probably existing indestructible background element */
3962 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
3963 element = Feld[x][y] = Back[x][y];
3966 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
3967 GfxDir[x][y] = MV_NONE;
3968 ChangeDelay[x][y] = 0;
3969 ChangePage[x][y] = -1;
3971 #if USE_NEW_CUSTOM_VALUE
3972 CustomValue[x][y] = 0;
3975 InitField_WithBug2(x, y, FALSE);
3977 DrawLevelField(x, y);
3979 TestIfElementTouchesCustomElement(x, y);
3981 if (GFX_CRUMBLED(element))
3982 DrawLevelFieldCrumbledSandNeighbours(x, y);
3984 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
3985 StorePlayer[x][y] = 0;
3987 if (ELEM_IS_PLAYER(element))
3988 RelocatePlayer(x, y, element);
3990 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3992 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
3993 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
3996 DrawLevelFieldCrumbledSand(x, y);
3998 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
4000 DrawLevelElement(x, y, Back[x][y]);
4001 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
4003 else if (IS_WALKABLE_UNDER(Back[x][y]))
4005 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
4006 DrawLevelElementThruMask(x, y, Back[x][y]);
4008 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
4009 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
4013 void DynaExplode(int ex, int ey)
4016 int dynabomb_element = Feld[ex][ey];
4017 int dynabomb_size = 1;
4018 boolean dynabomb_xl = FALSE;
4019 struct PlayerInfo *player;
4020 static int xy[4][2] =
4028 if (IS_ACTIVE_BOMB(dynabomb_element))
4030 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
4031 dynabomb_size = player->dynabomb_size;
4032 dynabomb_xl = player->dynabomb_xl;
4033 player->dynabombs_left++;
4036 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
4038 for (i = 0; i < NUM_DIRECTIONS; i++)
4040 for (j = 1; j <= dynabomb_size; j++)
4042 int x = ex + j * xy[i][0];
4043 int y = ey + j * xy[i][1];
4046 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
4049 element = Feld[x][y];
4051 /* do not restart explosions of fields with active bombs */
4052 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
4055 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
4057 if (element != EL_EMPTY && element != EL_EXPLOSION &&
4058 !IS_DIGGABLE(element) && !dynabomb_xl)
4064 void Bang(int x, int y)
4066 int element = MovingOrBlocked2Element(x, y);
4067 int explosion_type = EX_TYPE_NORMAL;
4069 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
4071 struct PlayerInfo *player = PLAYERINFO(x, y);
4073 element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
4074 player->element_nr);
4076 if (level.use_explosion_element[player->index_nr])
4078 int explosion_element = level.explosion_element[player->index_nr];
4080 if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
4081 explosion_type = EX_TYPE_CROSS;
4082 else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
4083 explosion_type = EX_TYPE_CENTER;
4091 case EL_BD_BUTTERFLY:
4094 case EL_DARK_YAMYAM:
4098 RaiseScoreElement(element);
4101 case EL_DYNABOMB_PLAYER_1_ACTIVE:
4102 case EL_DYNABOMB_PLAYER_2_ACTIVE:
4103 case EL_DYNABOMB_PLAYER_3_ACTIVE:
4104 case EL_DYNABOMB_PLAYER_4_ACTIVE:
4105 case EL_DYNABOMB_INCREASE_NUMBER:
4106 case EL_DYNABOMB_INCREASE_SIZE:
4107 case EL_DYNABOMB_INCREASE_POWER:
4108 explosion_type = EX_TYPE_DYNA;
4111 case EL_DC_LANDMINE:
4113 case EL_EM_EXIT_OPEN:
4114 case EL_EM_STEEL_EXIT_OPEN:
4116 explosion_type = EX_TYPE_CENTER;
4121 case EL_LAMP_ACTIVE:
4122 case EL_AMOEBA_TO_DIAMOND:
4123 if (!IS_PLAYER(x, y)) /* penguin and player may be at same field */
4124 explosion_type = EX_TYPE_CENTER;
4128 if (element_info[element].explosion_type == EXPLODES_CROSS)
4129 explosion_type = EX_TYPE_CROSS;
4130 else if (element_info[element].explosion_type == EXPLODES_1X1)
4131 explosion_type = EX_TYPE_CENTER;
4135 if (explosion_type == EX_TYPE_DYNA)
4138 Explode(x, y, EX_PHASE_START, explosion_type);
4140 CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
4143 void SplashAcid(int x, int y)
4145 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
4146 (!IN_LEV_FIELD(x - 1, y - 2) ||
4147 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
4148 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
4150 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
4151 (!IN_LEV_FIELD(x + 1, y - 2) ||
4152 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
4153 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
4155 PlayLevelSound(x, y, SND_ACID_SPLASHING);
4158 static void InitBeltMovement()
4160 static int belt_base_element[4] =
4162 EL_CONVEYOR_BELT_1_LEFT,
4163 EL_CONVEYOR_BELT_2_LEFT,
4164 EL_CONVEYOR_BELT_3_LEFT,
4165 EL_CONVEYOR_BELT_4_LEFT
4167 static int belt_base_active_element[4] =
4169 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
4170 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
4171 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
4172 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
4177 /* set frame order for belt animation graphic according to belt direction */
4178 for (i = 0; i < NUM_BELTS; i++)
4182 for (j = 0; j < NUM_BELT_PARTS; j++)
4184 int element = belt_base_active_element[belt_nr] + j;
4185 int graphic = el2img(element);
4187 if (game.belt_dir[i] == MV_LEFT)
4188 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
4190 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
4194 SCAN_PLAYFIELD(x, y)
4196 int element = Feld[x][y];
4198 for (i = 0; i < NUM_BELTS; i++)
4200 if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
4202 int e_belt_nr = getBeltNrFromBeltElement(element);
4205 if (e_belt_nr == belt_nr)
4207 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
4209 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
4216 static void ToggleBeltSwitch(int x, int y)
4218 static int belt_base_element[4] =
4220 EL_CONVEYOR_BELT_1_LEFT,
4221 EL_CONVEYOR_BELT_2_LEFT,
4222 EL_CONVEYOR_BELT_3_LEFT,
4223 EL_CONVEYOR_BELT_4_LEFT
4225 static int belt_base_active_element[4] =
4227 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
4228 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
4229 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
4230 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
4232 static int belt_base_switch_element[4] =
4234 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
4235 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
4236 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
4237 EL_CONVEYOR_BELT_4_SWITCH_LEFT
4239 static int belt_move_dir[4] =
4247 int element = Feld[x][y];
4248 int belt_nr = getBeltNrFromBeltSwitchElement(element);
4249 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
4250 int belt_dir = belt_move_dir[belt_dir_nr];
4253 if (!IS_BELT_SWITCH(element))
4256 game.belt_dir_nr[belt_nr] = belt_dir_nr;
4257 game.belt_dir[belt_nr] = belt_dir;
4259 if (belt_dir_nr == 3)
4262 /* set frame order for belt animation graphic according to belt direction */
4263 for (i = 0; i < NUM_BELT_PARTS; i++)
4265 int element = belt_base_active_element[belt_nr] + i;
4266 int graphic = el2img(element);
4268 if (belt_dir == MV_LEFT)
4269 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
4271 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
4274 SCAN_PLAYFIELD(xx, yy)
4276 int element = Feld[xx][yy];
4278 if (IS_BELT_SWITCH(element))
4280 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
4282 if (e_belt_nr == belt_nr)
4284 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
4285 DrawLevelField(xx, yy);
4288 else if (IS_BELT(element) && belt_dir != MV_NONE)
4290 int e_belt_nr = getBeltNrFromBeltElement(element);
4292 if (e_belt_nr == belt_nr)
4294 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
4296 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
4297 DrawLevelField(xx, yy);
4300 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
4302 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
4304 if (e_belt_nr == belt_nr)
4306 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
4308 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
4309 DrawLevelField(xx, yy);
4315 static void ToggleSwitchgateSwitch(int x, int y)
4319 game.switchgate_pos = !game.switchgate_pos;
4321 SCAN_PLAYFIELD(xx, yy)
4323 int element = Feld[xx][yy];
4325 #if !USE_BOTH_SWITCHGATE_SWITCHES
4326 if (element == EL_SWITCHGATE_SWITCH_UP ||
4327 element == EL_SWITCHGATE_SWITCH_DOWN)
4329 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
4330 DrawLevelField(xx, yy);
4332 else if (element == EL_DC_SWITCHGATE_SWITCH_UP ||
4333 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
4335 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
4336 DrawLevelField(xx, yy);
4339 if (element == EL_SWITCHGATE_SWITCH_UP)
4341 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
4342 DrawLevelField(xx, yy);
4344 else if (element == EL_SWITCHGATE_SWITCH_DOWN)
4346 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
4347 DrawLevelField(xx, yy);
4349 else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
4351 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
4352 DrawLevelField(xx, yy);
4354 else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
4356 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
4357 DrawLevelField(xx, yy);
4360 else if (element == EL_SWITCHGATE_OPEN ||
4361 element == EL_SWITCHGATE_OPENING)
4363 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
4365 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
4367 else if (element == EL_SWITCHGATE_CLOSED ||
4368 element == EL_SWITCHGATE_CLOSING)
4370 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
4372 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
4377 static int getInvisibleActiveFromInvisibleElement(int element)
4379 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
4380 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
4381 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
4385 static int getInvisibleFromInvisibleActiveElement(int element)
4387 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
4388 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
4389 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
4393 static void RedrawAllLightSwitchesAndInvisibleElements()
4397 SCAN_PLAYFIELD(x, y)
4399 int element = Feld[x][y];
4401 if (element == EL_LIGHT_SWITCH &&
4402 game.light_time_left > 0)
4404 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
4405 DrawLevelField(x, y);
4407 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
4408 game.light_time_left == 0)
4410 Feld[x][y] = EL_LIGHT_SWITCH;
4411 DrawLevelField(x, y);
4413 else if (element == EL_EMC_DRIPPER &&
4414 game.light_time_left > 0)
4416 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
4417 DrawLevelField(x, y);
4419 else if (element == EL_EMC_DRIPPER_ACTIVE &&
4420 game.light_time_left == 0)
4422 Feld[x][y] = EL_EMC_DRIPPER;
4423 DrawLevelField(x, y);
4425 else if (element == EL_INVISIBLE_STEELWALL ||
4426 element == EL_INVISIBLE_WALL ||
4427 element == EL_INVISIBLE_SAND)
4429 if (game.light_time_left > 0)
4430 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
4432 DrawLevelField(x, y);
4434 /* uncrumble neighbour fields, if needed */
4435 if (element == EL_INVISIBLE_SAND)
4436 DrawLevelFieldCrumbledSandNeighbours(x, y);
4438 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
4439 element == EL_INVISIBLE_WALL_ACTIVE ||
4440 element == EL_INVISIBLE_SAND_ACTIVE)
4442 if (game.light_time_left == 0)
4443 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
4445 DrawLevelField(x, y);
4447 /* re-crumble neighbour fields, if needed */
4448 if (element == EL_INVISIBLE_SAND)
4449 DrawLevelFieldCrumbledSandNeighbours(x, y);
4454 static void RedrawAllInvisibleElementsForLenses()
4458 SCAN_PLAYFIELD(x, y)
4460 int element = Feld[x][y];
4462 if (element == EL_EMC_DRIPPER &&
4463 game.lenses_time_left > 0)
4465 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
4466 DrawLevelField(x, y);
4468 else if (element == EL_EMC_DRIPPER_ACTIVE &&
4469 game.lenses_time_left == 0)
4471 Feld[x][y] = EL_EMC_DRIPPER;
4472 DrawLevelField(x, y);
4474 else if (element == EL_INVISIBLE_STEELWALL ||
4475 element == EL_INVISIBLE_WALL ||
4476 element == EL_INVISIBLE_SAND)
4478 if (game.lenses_time_left > 0)
4479 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
4481 DrawLevelField(x, y);
4483 /* uncrumble neighbour fields, if needed */
4484 if (element == EL_INVISIBLE_SAND)
4485 DrawLevelFieldCrumbledSandNeighbours(x, y);
4487 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
4488 element == EL_INVISIBLE_WALL_ACTIVE ||
4489 element == EL_INVISIBLE_SAND_ACTIVE)
4491 if (game.lenses_time_left == 0)
4492 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
4494 DrawLevelField(x, y);
4496 /* re-crumble neighbour fields, if needed */
4497 if (element == EL_INVISIBLE_SAND)
4498 DrawLevelFieldCrumbledSandNeighbours(x, y);
4503 static void RedrawAllInvisibleElementsForMagnifier()
4507 SCAN_PLAYFIELD(x, y)
4509 int element = Feld[x][y];
4511 if (element == EL_EMC_FAKE_GRASS &&
4512 game.magnify_time_left > 0)
4514 Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
4515 DrawLevelField(x, y);
4517 else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
4518 game.magnify_time_left == 0)
4520 Feld[x][y] = EL_EMC_FAKE_GRASS;
4521 DrawLevelField(x, y);
4523 else if (IS_GATE_GRAY(element) &&
4524 game.magnify_time_left > 0)
4526 Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
4527 element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
4528 IS_EM_GATE_GRAY(element) ?
4529 element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
4530 IS_EMC_GATE_GRAY(element) ?
4531 element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
4533 DrawLevelField(x, y);
4535 else if (IS_GATE_GRAY_ACTIVE(element) &&
4536 game.magnify_time_left == 0)
4538 Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
4539 element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
4540 IS_EM_GATE_GRAY_ACTIVE(element) ?
4541 element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
4542 IS_EMC_GATE_GRAY_ACTIVE(element) ?
4543 element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
4545 DrawLevelField(x, y);
4550 static void ToggleLightSwitch(int x, int y)
4552 int element = Feld[x][y];
4554 game.light_time_left =
4555 (element == EL_LIGHT_SWITCH ?
4556 level.time_light * FRAMES_PER_SECOND : 0);
4558 RedrawAllLightSwitchesAndInvisibleElements();
4561 static void ActivateTimegateSwitch(int x, int y)
4565 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
4567 SCAN_PLAYFIELD(xx, yy)
4569 int element = Feld[xx][yy];
4571 if (element == EL_TIMEGATE_CLOSED ||
4572 element == EL_TIMEGATE_CLOSING)
4574 Feld[xx][yy] = EL_TIMEGATE_OPENING;
4575 PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
4579 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
4581 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
4582 DrawLevelField(xx, yy);
4589 Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
4590 EL_DC_TIMEGATE_SWITCH_ACTIVE);
4592 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
4596 void Impact(int x, int y)
4598 boolean last_line = (y == lev_fieldy - 1);
4599 boolean object_hit = FALSE;
4600 boolean impact = (last_line || object_hit);
4601 int element = Feld[x][y];
4602 int smashed = EL_STEELWALL;
4604 if (!last_line) /* check if element below was hit */
4606 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
4609 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
4610 MovDir[x][y + 1] != MV_DOWN ||
4611 MovPos[x][y + 1] <= TILEY / 2));
4613 /* do not smash moving elements that left the smashed field in time */
4614 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
4615 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
4618 #if USE_QUICKSAND_IMPACT_BUGFIX
4619 if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
4621 RemoveMovingField(x, y + 1);
4622 Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
4623 Feld[x][y + 2] = EL_ROCK;
4624 DrawLevelField(x, y + 2);
4629 if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
4631 RemoveMovingField(x, y + 1);
4632 Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
4633 Feld[x][y + 2] = EL_ROCK;
4634 DrawLevelField(x, y + 2);
4641 smashed = MovingOrBlocked2Element(x, y + 1);
4643 impact = (last_line || object_hit);
4646 if (!last_line && smashed == EL_ACID) /* element falls into acid */
4648 SplashAcid(x, y + 1);
4652 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
4653 /* only reset graphic animation if graphic really changes after impact */
4655 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
4657 ResetGfxAnimation(x, y);
4658 DrawLevelField(x, y);
4661 if (impact && CAN_EXPLODE_IMPACT(element))
4666 else if (impact && element == EL_PEARL &&
4667 smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
4669 ResetGfxAnimation(x, y);
4671 Feld[x][y] = EL_PEARL_BREAKING;
4672 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4675 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
4677 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4682 if (impact && element == EL_AMOEBA_DROP)
4684 if (object_hit && IS_PLAYER(x, y + 1))
4685 KillPlayerUnlessEnemyProtected(x, y + 1);
4686 else if (object_hit && smashed == EL_PENGUIN)
4690 Feld[x][y] = EL_AMOEBA_GROWING;
4691 Store[x][y] = EL_AMOEBA_WET;
4693 ResetRandomAnimationValue(x, y);
4698 if (object_hit) /* check which object was hit */
4700 if ((CAN_PASS_MAGIC_WALL(element) &&
4701 (smashed == EL_MAGIC_WALL ||
4702 smashed == EL_BD_MAGIC_WALL)) ||
4703 (CAN_PASS_DC_MAGIC_WALL(element) &&
4704 smashed == EL_DC_MAGIC_WALL))
4707 int activated_magic_wall =
4708 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
4709 smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
4710 EL_DC_MAGIC_WALL_ACTIVE);
4712 /* activate magic wall / mill */
4713 SCAN_PLAYFIELD(xx, yy)
4715 if (Feld[xx][yy] == smashed)
4716 Feld[xx][yy] = activated_magic_wall;
4719 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
4720 game.magic_wall_active = TRUE;
4722 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
4723 SND_MAGIC_WALL_ACTIVATING :
4724 smashed == EL_BD_MAGIC_WALL ?
4725 SND_BD_MAGIC_WALL_ACTIVATING :
4726 SND_DC_MAGIC_WALL_ACTIVATING));
4729 if (IS_PLAYER(x, y + 1))
4731 if (CAN_SMASH_PLAYER(element))
4733 KillPlayerUnlessEnemyProtected(x, y + 1);
4737 else if (smashed == EL_PENGUIN)
4739 if (CAN_SMASH_PLAYER(element))
4745 else if (element == EL_BD_DIAMOND)
4747 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
4753 else if (((element == EL_SP_INFOTRON ||
4754 element == EL_SP_ZONK) &&
4755 (smashed == EL_SP_SNIKSNAK ||
4756 smashed == EL_SP_ELECTRON ||
4757 smashed == EL_SP_DISK_ORANGE)) ||
4758 (element == EL_SP_INFOTRON &&
4759 smashed == EL_SP_DISK_YELLOW))
4764 else if (CAN_SMASH_EVERYTHING(element))
4766 if (IS_CLASSIC_ENEMY(smashed) ||
4767 CAN_EXPLODE_SMASHED(smashed))
4772 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
4774 if (smashed == EL_LAMP ||
4775 smashed == EL_LAMP_ACTIVE)
4780 else if (smashed == EL_NUT)
4782 Feld[x][y + 1] = EL_NUT_BREAKING;
4783 PlayLevelSound(x, y, SND_NUT_BREAKING);
4784 RaiseScoreElement(EL_NUT);
4787 else if (smashed == EL_PEARL)
4789 ResetGfxAnimation(x, y);
4791 Feld[x][y + 1] = EL_PEARL_BREAKING;
4792 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4795 else if (smashed == EL_DIAMOND)
4797 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
4798 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
4801 else if (IS_BELT_SWITCH(smashed))
4803 ToggleBeltSwitch(x, y + 1);
4805 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
4806 smashed == EL_SWITCHGATE_SWITCH_DOWN ||
4807 smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
4808 smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
4810 ToggleSwitchgateSwitch(x, y + 1);
4812 else if (smashed == EL_LIGHT_SWITCH ||
4813 smashed == EL_LIGHT_SWITCH_ACTIVE)
4815 ToggleLightSwitch(x, y + 1);
4820 TestIfElementSmashesCustomElement(x, y, MV_DOWN);
4823 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4825 CheckElementChangeBySide(x, y + 1, smashed, element,
4826 CE_SWITCHED, CH_SIDE_TOP);
4827 CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
4833 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4838 /* play sound of magic wall / mill */
4840 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
4841 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
4842 Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
4844 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
4845 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
4846 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
4847 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
4848 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
4849 PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
4854 /* play sound of object that hits the ground */
4855 if (last_line || object_hit)
4856 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4859 inline static void TurnRoundExt(int x, int y)
4871 { 0, 0 }, { 0, 0 }, { 0, 0 },
4876 int left, right, back;
4880 { MV_DOWN, MV_UP, MV_RIGHT },
4881 { MV_UP, MV_DOWN, MV_LEFT },
4883 { MV_LEFT, MV_RIGHT, MV_DOWN },
4887 { MV_RIGHT, MV_LEFT, MV_UP }
4890 int element = Feld[x][y];
4891 int move_pattern = element_info[element].move_pattern;
4893 int old_move_dir = MovDir[x][y];
4894 int left_dir = turn[old_move_dir].left;
4895 int right_dir = turn[old_move_dir].right;
4896 int back_dir = turn[old_move_dir].back;
4898 int left_dx = move_xy[left_dir].dx, left_dy = move_xy[left_dir].dy;
4899 int right_dx = move_xy[right_dir].dx, right_dy = move_xy[right_dir].dy;
4900 int move_dx = move_xy[old_move_dir].dx, move_dy = move_xy[old_move_dir].dy;
4901 int back_dx = move_xy[back_dir].dx, back_dy = move_xy[back_dir].dy;
4903 int left_x = x + left_dx, left_y = y + left_dy;
4904 int right_x = x + right_dx, right_y = y + right_dy;
4905 int move_x = x + move_dx, move_y = y + move_dy;
4909 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4911 TestIfBadThingTouchesOtherBadThing(x, y);
4913 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
4914 MovDir[x][y] = right_dir;
4915 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4916 MovDir[x][y] = left_dir;
4918 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
4920 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
4923 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
4925 TestIfBadThingTouchesOtherBadThing(x, y);
4927 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
4928 MovDir[x][y] = left_dir;
4929 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4930 MovDir[x][y] = right_dir;
4932 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
4934 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
4937 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4939 TestIfBadThingTouchesOtherBadThing(x, y);
4941 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
4942 MovDir[x][y] = left_dir;
4943 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
4944 MovDir[x][y] = right_dir;
4946 if (MovDir[x][y] != old_move_dir)
4949 else if (element == EL_YAMYAM)
4951 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
4952 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
4954 if (can_turn_left && can_turn_right)
4955 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4956 else if (can_turn_left)
4957 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4958 else if (can_turn_right)
4959 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4961 MovDir[x][y] = back_dir;
4963 MovDelay[x][y] = 16 + 16 * RND(3);
4965 else if (element == EL_DARK_YAMYAM)
4967 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4969 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4972 if (can_turn_left && can_turn_right)
4973 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4974 else if (can_turn_left)
4975 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4976 else if (can_turn_right)
4977 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4979 MovDir[x][y] = back_dir;
4981 MovDelay[x][y] = 16 + 16 * RND(3);
4983 else if (element == EL_PACMAN)
4985 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
4986 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
4988 if (can_turn_left && can_turn_right)
4989 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4990 else if (can_turn_left)
4991 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4992 else if (can_turn_right)
4993 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4995 MovDir[x][y] = back_dir;
4997 MovDelay[x][y] = 6 + RND(40);
4999 else if (element == EL_PIG)
5001 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
5002 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
5003 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
5004 boolean should_turn_left, should_turn_right, should_move_on;
5006 int rnd = RND(rnd_value);
5008 should_turn_left = (can_turn_left &&
5010 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
5011 y + back_dy + left_dy)));
5012 should_turn_right = (can_turn_right &&
5014 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
5015 y + back_dy + right_dy)));
5016 should_move_on = (can_move_on &&
5019 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
5020 y + move_dy + left_dy) ||
5021 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
5022 y + move_dy + right_dy)));
5024 if (should_turn_left || should_turn_right || should_move_on)
5026 if (should_turn_left && should_turn_right && should_move_on)
5027 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
5028 rnd < 2 * rnd_value / 3 ? right_dir :
5030 else if (should_turn_left && should_turn_right)
5031 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
5032 else if (should_turn_left && should_move_on)
5033 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
5034 else if (should_turn_right && should_move_on)
5035 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
5036 else if (should_turn_left)
5037 MovDir[x][y] = left_dir;
5038 else if (should_turn_right)
5039 MovDir[x][y] = right_dir;
5040 else if (should_move_on)
5041 MovDir[x][y] = old_move_dir;
5043 else if (can_move_on && rnd > rnd_value / 8)
5044 MovDir[x][y] = old_move_dir;
5045 else if (can_turn_left && can_turn_right)
5046 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
5047 else if (can_turn_left && rnd > rnd_value / 8)
5048 MovDir[x][y] = left_dir;
5049 else if (can_turn_right && rnd > rnd_value/8)
5050 MovDir[x][y] = right_dir;
5052 MovDir[x][y] = back_dir;
5054 xx = x + move_xy[MovDir[x][y]].dx;
5055 yy = y + move_xy[MovDir[x][y]].dy;
5057 if (!IN_LEV_FIELD(xx, yy) ||
5058 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
5059 MovDir[x][y] = old_move_dir;
5063 else if (element == EL_DRAGON)
5065 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
5066 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
5067 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
5069 int rnd = RND(rnd_value);
5071 if (can_move_on && rnd > rnd_value / 8)
5072 MovDir[x][y] = old_move_dir;
5073 else if (can_turn_left && can_turn_right)
5074 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
5075 else if (can_turn_left && rnd > rnd_value / 8)
5076 MovDir[x][y] = left_dir;
5077 else if (can_turn_right && rnd > rnd_value / 8)
5078 MovDir[x][y] = right_dir;
5080 MovDir[x][y] = back_dir;
5082 xx = x + move_xy[MovDir[x][y]].dx;
5083 yy = y + move_xy[MovDir[x][y]].dy;
5085 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
5086 MovDir[x][y] = old_move_dir;
5090 else if (element == EL_MOLE)
5092 boolean can_move_on =
5093 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
5094 IS_AMOEBOID(Feld[move_x][move_y]) ||
5095 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
5098 boolean can_turn_left =
5099 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
5100 IS_AMOEBOID(Feld[left_x][left_y])));
5102 boolean can_turn_right =
5103 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
5104 IS_AMOEBOID(Feld[right_x][right_y])));
5106 if (can_turn_left && can_turn_right)
5107 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
5108 else if (can_turn_left)
5109 MovDir[x][y] = left_dir;
5111 MovDir[x][y] = right_dir;
5114 if (MovDir[x][y] != old_move_dir)
5117 else if (element == EL_BALLOON)
5119 MovDir[x][y] = game.wind_direction;
5122 else if (element == EL_SPRING)
5124 #if USE_NEW_SPRING_BUMPER
5125 if (MovDir[x][y] & MV_HORIZONTAL)
5127 if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
5128 !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
5130 Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
5131 ResetGfxAnimation(move_x, move_y);
5132 DrawLevelField(move_x, move_y);
5134 MovDir[x][y] = back_dir;
5136 else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
5137 SPRING_CAN_ENTER_FIELD(element, x, y + 1))
5138 MovDir[x][y] = MV_NONE;
5141 if (MovDir[x][y] & MV_HORIZONTAL &&
5142 (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
5143 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
5144 MovDir[x][y] = MV_NONE;
5149 else if (element == EL_ROBOT ||
5150 element == EL_SATELLITE ||
5151 element == EL_PENGUIN ||
5152 element == EL_EMC_ANDROID)
5154 int attr_x = -1, attr_y = -1;
5165 for (i = 0; i < MAX_PLAYERS; i++)
5167 struct PlayerInfo *player = &stored_player[i];
5168 int jx = player->jx, jy = player->jy;
5170 if (!player->active)
5174 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
5182 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
5183 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
5184 game.engine_version < VERSION_IDENT(3,1,0,0)))
5190 if (element == EL_PENGUIN)
5193 static int xy[4][2] =
5201 for (i = 0; i < NUM_DIRECTIONS; i++)
5203 int ex = x + xy[i][0];
5204 int ey = y + xy[i][1];
5206 if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
5207 Feld[ex][ey] == EL_EM_EXIT_OPEN ||
5208 Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
5209 Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
5218 MovDir[x][y] = MV_NONE;
5220 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
5221 else if (attr_x > x)
5222 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
5224 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
5225 else if (attr_y > y)
5226 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
5228 if (element == EL_ROBOT)
5232 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5233 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
5234 Moving2Blocked(x, y, &newx, &newy);
5236 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
5237 MovDelay[x][y] = 8 + 8 * !RND(3);
5239 MovDelay[x][y] = 16;
5241 else if (element == EL_PENGUIN)
5247 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5249 boolean first_horiz = RND(2);
5250 int new_move_dir = MovDir[x][y];
5253 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5254 Moving2Blocked(x, y, &newx, &newy);
5256 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
5260 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5261 Moving2Blocked(x, y, &newx, &newy);
5263 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
5266 MovDir[x][y] = old_move_dir;
5270 else if (element == EL_SATELLITE)
5276 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5278 boolean first_horiz = RND(2);
5279 int new_move_dir = MovDir[x][y];
5282 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5283 Moving2Blocked(x, y, &newx, &newy);
5285 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
5289 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5290 Moving2Blocked(x, y, &newx, &newy);
5292 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
5295 MovDir[x][y] = old_move_dir;
5299 else if (element == EL_EMC_ANDROID)
5301 static int check_pos[16] =
5303 -1, /* 0 => (invalid) */
5304 7, /* 1 => MV_LEFT */
5305 3, /* 2 => MV_RIGHT */
5306 -1, /* 3 => (invalid) */
5308 0, /* 5 => MV_LEFT | MV_UP */
5309 2, /* 6 => MV_RIGHT | MV_UP */
5310 -1, /* 7 => (invalid) */
5311 5, /* 8 => MV_DOWN */
5312 6, /* 9 => MV_LEFT | MV_DOWN */
5313 4, /* 10 => MV_RIGHT | MV_DOWN */
5314 -1, /* 11 => (invalid) */
5315 -1, /* 12 => (invalid) */
5316 -1, /* 13 => (invalid) */
5317 -1, /* 14 => (invalid) */
5318 -1, /* 15 => (invalid) */
5326 { -1, -1, MV_LEFT | MV_UP },
5328 { +1, -1, MV_RIGHT | MV_UP },
5329 { +1, 0, MV_RIGHT },
5330 { +1, +1, MV_RIGHT | MV_DOWN },
5332 { -1, +1, MV_LEFT | MV_DOWN },
5335 int start_pos, check_order;
5336 boolean can_clone = FALSE;
5339 /* check if there is any free field around current position */
5340 for (i = 0; i < 8; i++)
5342 int newx = x + check_xy[i].dx;
5343 int newy = y + check_xy[i].dy;
5345 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5353 if (can_clone) /* randomly find an element to clone */
5357 start_pos = check_pos[RND(8)];
5358 check_order = (RND(2) ? -1 : +1);
5360 for (i = 0; i < 8; i++)
5362 int pos_raw = start_pos + i * check_order;
5363 int pos = (pos_raw + 8) % 8;
5364 int newx = x + check_xy[pos].dx;
5365 int newy = y + check_xy[pos].dy;
5367 if (ANDROID_CAN_CLONE_FIELD(newx, newy))
5369 element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
5370 element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
5372 Store[x][y] = Feld[newx][newy];
5381 if (can_clone) /* randomly find a direction to move */
5385 start_pos = check_pos[RND(8)];
5386 check_order = (RND(2) ? -1 : +1);
5388 for (i = 0; i < 8; i++)
5390 int pos_raw = start_pos + i * check_order;
5391 int pos = (pos_raw + 8) % 8;
5392 int newx = x + check_xy[pos].dx;
5393 int newy = y + check_xy[pos].dy;
5394 int new_move_dir = check_xy[pos].dir;
5396 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5398 MovDir[x][y] = new_move_dir;
5399 MovDelay[x][y] = level.android_clone_time * 8 + 1;
5408 if (can_clone) /* cloning and moving successful */
5411 /* cannot clone -- try to move towards player */
5413 start_pos = check_pos[MovDir[x][y] & 0x0f];
5414 check_order = (RND(2) ? -1 : +1);
5416 for (i = 0; i < 3; i++)
5418 /* first check start_pos, then previous/next or (next/previous) pos */
5419 int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
5420 int pos = (pos_raw + 8) % 8;
5421 int newx = x + check_xy[pos].dx;
5422 int newy = y + check_xy[pos].dy;
5423 int new_move_dir = check_xy[pos].dir;
5425 if (IS_PLAYER(newx, newy))
5428 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
5430 MovDir[x][y] = new_move_dir;
5431 MovDelay[x][y] = level.android_move_time * 8 + 1;
5438 else if (move_pattern == MV_TURNING_LEFT ||
5439 move_pattern == MV_TURNING_RIGHT ||
5440 move_pattern == MV_TURNING_LEFT_RIGHT ||
5441 move_pattern == MV_TURNING_RIGHT_LEFT ||
5442 move_pattern == MV_TURNING_RANDOM ||
5443 move_pattern == MV_ALL_DIRECTIONS)
5445 boolean can_turn_left =
5446 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
5447 boolean can_turn_right =
5448 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
5450 if (element_info[element].move_stepsize == 0) /* "not moving" */
5453 if (move_pattern == MV_TURNING_LEFT)
5454 MovDir[x][y] = left_dir;
5455 else if (move_pattern == MV_TURNING_RIGHT)
5456 MovDir[x][y] = right_dir;
5457 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
5458 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
5459 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
5460 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
5461 else if (move_pattern == MV_TURNING_RANDOM)
5462 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
5463 can_turn_right && !can_turn_left ? right_dir :
5464 RND(2) ? left_dir : right_dir);
5465 else if (can_turn_left && can_turn_right)
5466 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5467 else if (can_turn_left)
5468 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5469 else if (can_turn_right)
5470 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5472 MovDir[x][y] = back_dir;
5474 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5476 else if (move_pattern == MV_HORIZONTAL ||
5477 move_pattern == MV_VERTICAL)
5479 if (move_pattern & old_move_dir)
5480 MovDir[x][y] = back_dir;
5481 else if (move_pattern == MV_HORIZONTAL)
5482 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
5483 else if (move_pattern == MV_VERTICAL)
5484 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
5486 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5488 else if (move_pattern & MV_ANY_DIRECTION)
5490 MovDir[x][y] = move_pattern;
5491 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5493 else if (move_pattern & MV_WIND_DIRECTION)
5495 MovDir[x][y] = game.wind_direction;
5496 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5498 else if (move_pattern == MV_ALONG_LEFT_SIDE)
5500 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
5501 MovDir[x][y] = left_dir;
5502 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5503 MovDir[x][y] = right_dir;
5505 if (MovDir[x][y] != old_move_dir)
5506 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5508 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
5510 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
5511 MovDir[x][y] = right_dir;
5512 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5513 MovDir[x][y] = left_dir;
5515 if (MovDir[x][y] != old_move_dir)
5516 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5518 else if (move_pattern == MV_TOWARDS_PLAYER ||
5519 move_pattern == MV_AWAY_FROM_PLAYER)
5521 int attr_x = -1, attr_y = -1;
5523 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
5534 for (i = 0; i < MAX_PLAYERS; i++)
5536 struct PlayerInfo *player = &stored_player[i];
5537 int jx = player->jx, jy = player->jy;
5539 if (!player->active)
5543 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
5551 MovDir[x][y] = MV_NONE;
5553 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
5554 else if (attr_x > x)
5555 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
5557 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
5558 else if (attr_y > y)
5559 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
5561 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5563 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5565 boolean first_horiz = RND(2);
5566 int new_move_dir = MovDir[x][y];
5568 if (element_info[element].move_stepsize == 0) /* "not moving" */
5570 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
5571 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5577 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5578 Moving2Blocked(x, y, &newx, &newy);
5580 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5584 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5585 Moving2Blocked(x, y, &newx, &newy);
5587 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5590 MovDir[x][y] = old_move_dir;
5593 else if (move_pattern == MV_WHEN_PUSHED ||
5594 move_pattern == MV_WHEN_DROPPED)
5596 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5597 MovDir[x][y] = MV_NONE;
5601 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
5603 static int test_xy[7][2] =
5613 static int test_dir[7] =
5623 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
5624 int move_preference = -1000000; /* start with very low preference */
5625 int new_move_dir = MV_NONE;
5626 int start_test = RND(4);
5629 for (i = 0; i < NUM_DIRECTIONS; i++)
5631 int move_dir = test_dir[start_test + i];
5632 int move_dir_preference;
5634 xx = x + test_xy[start_test + i][0];
5635 yy = y + test_xy[start_test + i][1];
5637 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
5638 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
5640 new_move_dir = move_dir;
5645 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
5648 move_dir_preference = -1 * RunnerVisit[xx][yy];
5649 if (hunter_mode && PlayerVisit[xx][yy] > 0)
5650 move_dir_preference = PlayerVisit[xx][yy];
5652 if (move_dir_preference > move_preference)
5654 /* prefer field that has not been visited for the longest time */
5655 move_preference = move_dir_preference;
5656 new_move_dir = move_dir;
5658 else if (move_dir_preference == move_preference &&
5659 move_dir == old_move_dir)
5661 /* prefer last direction when all directions are preferred equally */
5662 move_preference = move_dir_preference;
5663 new_move_dir = move_dir;
5667 MovDir[x][y] = new_move_dir;
5668 if (old_move_dir != new_move_dir)
5669 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5673 static void TurnRound(int x, int y)
5675 int direction = MovDir[x][y];
5679 GfxDir[x][y] = MovDir[x][y];
5681 if (direction != MovDir[x][y])
5685 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
5687 ResetGfxFrame(x, y, FALSE);
5690 static boolean JustBeingPushed(int x, int y)
5694 for (i = 0; i < MAX_PLAYERS; i++)
5696 struct PlayerInfo *player = &stored_player[i];
5698 if (player->active && player->is_pushing && player->MovPos)
5700 int next_jx = player->jx + (player->jx - player->last_jx);
5701 int next_jy = player->jy + (player->jy - player->last_jy);
5703 if (x == next_jx && y == next_jy)
5711 void StartMoving(int x, int y)
5713 boolean started_moving = FALSE; /* some elements can fall _and_ move */
5714 int element = Feld[x][y];
5719 if (MovDelay[x][y] == 0)
5720 GfxAction[x][y] = ACTION_DEFAULT;
5722 if (CAN_FALL(element) && y < lev_fieldy - 1)
5724 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
5725 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
5726 if (JustBeingPushed(x, y))
5729 if (element == EL_QUICKSAND_FULL)
5731 if (IS_FREE(x, y + 1))
5733 InitMovingField(x, y, MV_DOWN);
5734 started_moving = TRUE;
5736 Feld[x][y] = EL_QUICKSAND_EMPTYING;
5737 #if USE_QUICKSAND_BD_ROCK_BUGFIX
5738 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
5739 Store[x][y] = EL_ROCK;
5741 Store[x][y] = EL_ROCK;
5744 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
5746 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5748 if (!MovDelay[x][y])
5749 MovDelay[x][y] = TILEY + 1;
5758 Feld[x][y] = EL_QUICKSAND_EMPTY;
5759 Feld[x][y + 1] = EL_QUICKSAND_FULL;
5760 Store[x][y + 1] = Store[x][y];
5763 PlayLevelSoundAction(x, y, ACTION_FILLING);
5766 else if (element == EL_QUICKSAND_FAST_FULL)
5768 if (IS_FREE(x, y + 1))
5770 InitMovingField(x, y, MV_DOWN);
5771 started_moving = TRUE;
5773 Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
5774 #if USE_QUICKSAND_BD_ROCK_BUGFIX
5775 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
5776 Store[x][y] = EL_ROCK;
5778 Store[x][y] = EL_ROCK;
5781 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
5783 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
5785 if (!MovDelay[x][y])
5786 MovDelay[x][y] = TILEY + 1;
5795 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
5796 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
5797 Store[x][y + 1] = Store[x][y];
5800 PlayLevelSoundAction(x, y, ACTION_FILLING);
5803 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
5804 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5806 InitMovingField(x, y, MV_DOWN);
5807 started_moving = TRUE;
5809 Feld[x][y] = EL_QUICKSAND_FILLING;
5810 Store[x][y] = element;
5812 PlayLevelSoundAction(x, y, ACTION_FILLING);
5814 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
5815 Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
5817 InitMovingField(x, y, MV_DOWN);
5818 started_moving = TRUE;
5820 Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
5821 Store[x][y] = element;
5823 PlayLevelSoundAction(x, y, ACTION_FILLING);
5825 else if (element == EL_MAGIC_WALL_FULL)
5827 if (IS_FREE(x, y + 1))
5829 InitMovingField(x, y, MV_DOWN);
5830 started_moving = TRUE;
5832 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
5833 Store[x][y] = EL_CHANGED(Store[x][y]);
5835 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
5837 if (!MovDelay[x][y])
5838 MovDelay[x][y] = TILEY/4 + 1;
5847 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
5848 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
5849 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
5853 else if (element == EL_BD_MAGIC_WALL_FULL)
5855 if (IS_FREE(x, y + 1))
5857 InitMovingField(x, y, MV_DOWN);
5858 started_moving = TRUE;
5860 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
5861 Store[x][y] = EL_CHANGED_BD(Store[x][y]);
5863 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
5865 if (!MovDelay[x][y])
5866 MovDelay[x][y] = TILEY/4 + 1;
5875 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
5876 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
5877 Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
5881 else if (element == EL_DC_MAGIC_WALL_FULL)
5883 if (IS_FREE(x, y + 1))
5885 InitMovingField(x, y, MV_DOWN);
5886 started_moving = TRUE;
5888 Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
5889 Store[x][y] = EL_CHANGED_DC(Store[x][y]);
5891 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
5893 if (!MovDelay[x][y])
5894 MovDelay[x][y] = TILEY/4 + 1;
5903 Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
5904 Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
5905 Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
5909 else if ((CAN_PASS_MAGIC_WALL(element) &&
5910 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
5911 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
5912 (CAN_PASS_DC_MAGIC_WALL(element) &&
5913 (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
5916 InitMovingField(x, y, MV_DOWN);
5917 started_moving = TRUE;
5920 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
5921 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
5922 EL_DC_MAGIC_WALL_FILLING);
5923 Store[x][y] = element;
5925 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
5927 SplashAcid(x, y + 1);
5929 InitMovingField(x, y, MV_DOWN);
5930 started_moving = TRUE;
5932 Store[x][y] = EL_ACID;
5935 #if USE_FIX_IMPACT_COLLISION
5936 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5937 CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
5939 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5940 CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
5942 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
5943 CAN_FALL(element) && WasJustFalling[x][y] &&
5944 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
5946 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
5947 CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
5948 (Feld[x][y + 1] == EL_BLOCKED)))
5950 /* this is needed for a special case not covered by calling "Impact()"
5951 from "ContinueMoving()": if an element moves to a tile directly below
5952 another element which was just falling on that tile (which was empty
5953 in the previous frame), the falling element above would just stop
5954 instead of smashing the element below (in previous version, the above
5955 element was just checked for "moving" instead of "falling", resulting
5956 in incorrect smashes caused by horizontal movement of the above
5957 element; also, the case of the player being the element to smash was
5958 simply not covered here... :-/ ) */
5960 CheckCollision[x][y] = 0;
5961 CheckImpact[x][y] = 0;
5965 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
5967 if (MovDir[x][y] == MV_NONE)
5969 InitMovingField(x, y, MV_DOWN);
5970 started_moving = TRUE;
5973 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
5975 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
5976 MovDir[x][y] = MV_DOWN;
5978 InitMovingField(x, y, MV_DOWN);
5979 started_moving = TRUE;
5981 else if (element == EL_AMOEBA_DROP)
5983 Feld[x][y] = EL_AMOEBA_GROWING;
5984 Store[x][y] = EL_AMOEBA_WET;
5986 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
5987 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
5988 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
5989 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
5991 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
5992 (IS_FREE(x - 1, y + 1) ||
5993 Feld[x - 1][y + 1] == EL_ACID));
5994 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
5995 (IS_FREE(x + 1, y + 1) ||
5996 Feld[x + 1][y + 1] == EL_ACID));
5997 boolean can_fall_any = (can_fall_left || can_fall_right);
5998 boolean can_fall_both = (can_fall_left && can_fall_right);
5999 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
6001 #if USE_NEW_ALL_SLIPPERY
6002 if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
6004 if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
6005 can_fall_right = FALSE;
6006 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
6007 can_fall_left = FALSE;
6008 else if (slippery_type == SLIPPERY_ONLY_LEFT)
6009 can_fall_right = FALSE;
6010 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
6011 can_fall_left = FALSE;
6013 can_fall_any = (can_fall_left || can_fall_right);
6014 can_fall_both = FALSE;
6017 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
6019 if (slippery_type == SLIPPERY_ONLY_LEFT)
6020 can_fall_right = FALSE;
6021 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
6022 can_fall_left = FALSE;
6023 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
6024 can_fall_right = FALSE;
6025 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
6026 can_fall_left = FALSE;
6028 can_fall_any = (can_fall_left || can_fall_right);
6029 can_fall_both = (can_fall_left && can_fall_right);
6033 #if USE_NEW_ALL_SLIPPERY
6035 #if USE_NEW_SP_SLIPPERY
6036 /* !!! better use the same properties as for custom elements here !!! */
6037 else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
6038 can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
6040 can_fall_right = FALSE; /* slip down on left side */
6041 can_fall_both = FALSE;
6046 #if USE_NEW_ALL_SLIPPERY
6049 if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
6050 can_fall_right = FALSE; /* slip down on left side */
6052 can_fall_left = !(can_fall_right = RND(2));
6054 can_fall_both = FALSE;
6059 if (game.emulation == EMU_BOULDERDASH ||
6060 element == EL_BD_ROCK || element == EL_BD_DIAMOND)
6061 can_fall_right = FALSE; /* slip down on left side */
6063 can_fall_left = !(can_fall_right = RND(2));
6065 can_fall_both = FALSE;
6071 /* if not determined otherwise, prefer left side for slipping down */
6072 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
6073 started_moving = TRUE;
6077 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
6079 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
6082 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
6083 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
6084 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
6085 int belt_dir = game.belt_dir[belt_nr];
6087 if ((belt_dir == MV_LEFT && left_is_free) ||
6088 (belt_dir == MV_RIGHT && right_is_free))
6090 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
6092 InitMovingField(x, y, belt_dir);
6093 started_moving = TRUE;
6095 Pushed[x][y] = TRUE;
6096 Pushed[nextx][y] = TRUE;
6098 GfxAction[x][y] = ACTION_DEFAULT;
6102 MovDir[x][y] = 0; /* if element was moving, stop it */
6107 /* not "else if" because of elements that can fall and move (EL_SPRING) */
6109 if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
6111 if (CAN_MOVE(element) && !started_moving)
6114 int move_pattern = element_info[element].move_pattern;
6119 if (MovDir[x][y] == MV_NONE)
6121 printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
6122 x, y, element, element_info[element].token_name);
6123 printf("StartMoving(): This should never happen!\n");
6128 Moving2Blocked(x, y, &newx, &newy);
6130 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
6133 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
6134 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6136 WasJustMoving[x][y] = 0;
6137 CheckCollision[x][y] = 0;
6139 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
6141 if (Feld[x][y] != element) /* element has changed */
6145 if (!MovDelay[x][y]) /* start new movement phase */
6147 /* all objects that can change their move direction after each step
6148 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
6150 if (element != EL_YAMYAM &&
6151 element != EL_DARK_YAMYAM &&
6152 element != EL_PACMAN &&
6153 !(move_pattern & MV_ANY_DIRECTION) &&
6154 move_pattern != MV_TURNING_LEFT &&
6155 move_pattern != MV_TURNING_RIGHT &&
6156 move_pattern != MV_TURNING_LEFT_RIGHT &&
6157 move_pattern != MV_TURNING_RIGHT_LEFT &&
6158 move_pattern != MV_TURNING_RANDOM)
6162 if (MovDelay[x][y] && (element == EL_BUG ||
6163 element == EL_SPACESHIP ||
6164 element == EL_SP_SNIKSNAK ||
6165 element == EL_SP_ELECTRON ||
6166 element == EL_MOLE))
6167 DrawLevelField(x, y);
6171 if (MovDelay[x][y]) /* wait some time before next movement */
6175 if (element == EL_ROBOT ||
6176 element == EL_YAMYAM ||
6177 element == EL_DARK_YAMYAM)
6179 DrawLevelElementAnimationIfNeeded(x, y, element);
6180 PlayLevelSoundAction(x, y, ACTION_WAITING);
6182 else if (element == EL_SP_ELECTRON)
6183 DrawLevelElementAnimationIfNeeded(x, y, element);
6184 else if (element == EL_DRAGON)
6187 int dir = MovDir[x][y];
6188 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
6189 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
6190 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
6191 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
6192 dir == MV_UP ? IMG_FLAMES_1_UP :
6193 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
6194 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
6196 GfxAction[x][y] = ACTION_ATTACKING;
6198 if (IS_PLAYER(x, y))
6199 DrawPlayerField(x, y);
6201 DrawLevelField(x, y);
6203 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
6205 for (i = 1; i <= 3; i++)
6207 int xx = x + i * dx;
6208 int yy = y + i * dy;
6209 int sx = SCREENX(xx);
6210 int sy = SCREENY(yy);
6211 int flame_graphic = graphic + (i - 1);
6213 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
6218 int flamed = MovingOrBlocked2Element(xx, yy);
6222 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
6224 else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
6225 RemoveMovingField(xx, yy);
6227 RemoveField(xx, yy);
6229 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
6232 RemoveMovingField(xx, yy);
6235 ChangeDelay[xx][yy] = 0;
6237 Feld[xx][yy] = EL_FLAMES;
6239 if (IN_SCR_FIELD(sx, sy))
6241 DrawLevelFieldCrumbledSand(xx, yy);
6242 DrawGraphic(sx, sy, flame_graphic, frame);
6247 if (Feld[xx][yy] == EL_FLAMES)
6248 Feld[xx][yy] = EL_EMPTY;
6249 DrawLevelField(xx, yy);
6254 if (MovDelay[x][y]) /* element still has to wait some time */
6256 PlayLevelSoundAction(x, y, ACTION_WAITING);
6262 /* now make next step */
6264 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
6266 if (DONT_COLLIDE_WITH(element) &&
6267 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
6268 !PLAYER_ENEMY_PROTECTED(newx, newy))
6270 TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
6275 else if (CAN_MOVE_INTO_ACID(element) &&
6276 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
6277 !IS_MV_DIAGONAL(MovDir[x][y]) &&
6278 (MovDir[x][y] == MV_DOWN ||
6279 game.engine_version >= VERSION_IDENT(3,1,0,0)))
6281 SplashAcid(newx, newy);
6282 Store[x][y] = EL_ACID;
6284 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
6286 if (Feld[newx][newy] == EL_EXIT_OPEN ||
6287 Feld[newx][newy] == EL_EM_EXIT_OPEN ||
6288 Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
6289 Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
6292 DrawLevelField(x, y);
6294 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
6295 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
6296 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
6298 local_player->friends_still_needed--;
6299 if (!local_player->friends_still_needed &&
6300 !local_player->GameOver && AllPlayersGone)
6301 PlayerWins(local_player);
6305 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
6307 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
6308 DrawLevelField(newx, newy);
6310 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
6312 else if (!IS_FREE(newx, newy))
6314 GfxAction[x][y] = ACTION_WAITING;
6316 if (IS_PLAYER(x, y))
6317 DrawPlayerField(x, y);
6319 DrawLevelField(x, y);
6324 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
6326 if (IS_FOOD_PIG(Feld[newx][newy]))
6328 if (IS_MOVING(newx, newy))
6329 RemoveMovingField(newx, newy);
6332 Feld[newx][newy] = EL_EMPTY;
6333 DrawLevelField(newx, newy);
6336 PlayLevelSound(x, y, SND_PIG_DIGGING);
6338 else if (!IS_FREE(newx, newy))
6340 if (IS_PLAYER(x, y))
6341 DrawPlayerField(x, y);
6343 DrawLevelField(x, y);
6348 else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
6350 if (Store[x][y] != EL_EMPTY)
6352 boolean can_clone = FALSE;
6355 /* check if element to clone is still there */
6356 for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
6358 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
6366 /* cannot clone or target field not free anymore -- do not clone */
6367 if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6368 Store[x][y] = EL_EMPTY;
6371 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6373 if (IS_MV_DIAGONAL(MovDir[x][y]))
6375 int diagonal_move_dir = MovDir[x][y];
6376 int stored = Store[x][y];
6377 int change_delay = 8;
6380 /* android is moving diagonally */
6382 CreateField(x, y, EL_DIAGONAL_SHRINKING);
6384 Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
6385 GfxElement[x][y] = EL_EMC_ANDROID;
6386 GfxAction[x][y] = ACTION_SHRINKING;
6387 GfxDir[x][y] = diagonal_move_dir;
6388 ChangeDelay[x][y] = change_delay;
6390 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
6393 DrawLevelGraphicAnimation(x, y, graphic);
6394 PlayLevelSoundAction(x, y, ACTION_SHRINKING);
6396 if (Feld[newx][newy] == EL_ACID)
6398 SplashAcid(newx, newy);
6403 CreateField(newx, newy, EL_DIAGONAL_GROWING);
6405 Store[newx][newy] = EL_EMC_ANDROID;
6406 GfxElement[newx][newy] = EL_EMC_ANDROID;
6407 GfxAction[newx][newy] = ACTION_GROWING;
6408 GfxDir[newx][newy] = diagonal_move_dir;
6409 ChangeDelay[newx][newy] = change_delay;
6411 graphic = el_act_dir2img(GfxElement[newx][newy],
6412 GfxAction[newx][newy], GfxDir[newx][newy]);
6414 DrawLevelGraphicAnimation(newx, newy, graphic);
6415 PlayLevelSoundAction(newx, newy, ACTION_GROWING);
6421 Feld[newx][newy] = EL_EMPTY;
6422 DrawLevelField(newx, newy);
6424 PlayLevelSoundAction(x, y, ACTION_DIGGING);
6427 else if (!IS_FREE(newx, newy))
6430 if (IS_PLAYER(x, y))
6431 DrawPlayerField(x, y);
6433 DrawLevelField(x, y);
6439 else if (IS_CUSTOM_ELEMENT(element) &&
6440 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6442 int new_element = Feld[newx][newy];
6444 if (!IS_FREE(newx, newy))
6446 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
6447 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
6450 /* no element can dig solid indestructible elements */
6451 if (IS_INDESTRUCTIBLE(new_element) &&
6452 !IS_DIGGABLE(new_element) &&
6453 !IS_COLLECTIBLE(new_element))
6456 if (AmoebaNr[newx][newy] &&
6457 (new_element == EL_AMOEBA_FULL ||
6458 new_element == EL_BD_AMOEBA ||
6459 new_element == EL_AMOEBA_GROWING))
6461 AmoebaCnt[AmoebaNr[newx][newy]]--;
6462 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6465 if (IS_MOVING(newx, newy))
6466 RemoveMovingField(newx, newy);
6469 RemoveField(newx, newy);
6470 DrawLevelField(newx, newy);
6473 /* if digged element was about to explode, prevent the explosion */
6474 ExplodeField[newx][newy] = EX_TYPE_NONE;
6476 PlayLevelSoundAction(x, y, action);
6479 Store[newx][newy] = EL_EMPTY;
6481 /* this makes it possible to leave the removed element again */
6482 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
6483 Store[newx][newy] = new_element;
6485 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
6487 int move_leave_element = element_info[element].move_leave_element;
6489 /* this makes it possible to leave the removed element again */
6490 Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
6491 new_element : move_leave_element);
6495 if (move_pattern & MV_MAZE_RUNNER_STYLE)
6497 RunnerVisit[x][y] = FrameCounter;
6498 PlayerVisit[x][y] /= 8; /* expire player visit path */
6501 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
6503 if (!IS_FREE(newx, newy))
6505 if (IS_PLAYER(x, y))
6506 DrawPlayerField(x, y);
6508 DrawLevelField(x, y);
6514 boolean wanna_flame = !RND(10);
6515 int dx = newx - x, dy = newy - y;
6516 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
6517 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
6518 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
6519 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
6520 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
6521 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
6524 IS_CLASSIC_ENEMY(element1) ||
6525 IS_CLASSIC_ENEMY(element2)) &&
6526 element1 != EL_DRAGON && element2 != EL_DRAGON &&
6527 element1 != EL_FLAMES && element2 != EL_FLAMES)
6529 ResetGfxAnimation(x, y);
6530 GfxAction[x][y] = ACTION_ATTACKING;
6532 if (IS_PLAYER(x, y))
6533 DrawPlayerField(x, y);
6535 DrawLevelField(x, y);
6537 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
6539 MovDelay[x][y] = 50;
6543 RemoveField(newx, newy);
6545 Feld[newx][newy] = EL_FLAMES;
6546 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
6549 RemoveField(newx1, newy1);
6551 Feld[newx1][newy1] = EL_FLAMES;
6553 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
6556 RemoveField(newx2, newy2);
6558 Feld[newx2][newy2] = EL_FLAMES;
6565 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6566 Feld[newx][newy] == EL_DIAMOND)
6568 if (IS_MOVING(newx, newy))
6569 RemoveMovingField(newx, newy);
6572 Feld[newx][newy] = EL_EMPTY;
6573 DrawLevelField(newx, newy);
6576 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
6578 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6579 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
6581 if (AmoebaNr[newx][newy])
6583 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6584 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
6585 Feld[newx][newy] == EL_BD_AMOEBA)
6586 AmoebaCnt[AmoebaNr[newx][newy]]--;
6591 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
6593 RemoveMovingField(newx, newy);
6596 if (IS_MOVING(newx, newy))
6598 RemoveMovingField(newx, newy);
6603 Feld[newx][newy] = EL_EMPTY;
6604 DrawLevelField(newx, newy);
6607 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
6609 else if ((element == EL_PACMAN || element == EL_MOLE)
6610 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
6612 if (AmoebaNr[newx][newy])
6614 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6615 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
6616 Feld[newx][newy] == EL_BD_AMOEBA)
6617 AmoebaCnt[AmoebaNr[newx][newy]]--;
6620 if (element == EL_MOLE)
6622 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
6623 PlayLevelSound(x, y, SND_MOLE_DIGGING);
6625 ResetGfxAnimation(x, y);
6626 GfxAction[x][y] = ACTION_DIGGING;
6627 DrawLevelField(x, y);
6629 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
6631 return; /* wait for shrinking amoeba */
6633 else /* element == EL_PACMAN */
6635 Feld[newx][newy] = EL_EMPTY;
6636 DrawLevelField(newx, newy);
6637 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
6640 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
6641 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
6642 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
6644 /* wait for shrinking amoeba to completely disappear */
6647 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
6649 /* object was running against a wall */
6654 /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
6655 if (move_pattern & MV_ANY_DIRECTION &&
6656 move_pattern == MovDir[x][y])
6658 int blocking_element =
6659 (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
6661 CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
6664 element = Feld[x][y]; /* element might have changed */
6668 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
6669 DrawLevelElementAnimation(x, y, element);
6671 if (DONT_TOUCH(element))
6672 TestIfBadThingTouchesPlayer(x, y);
6677 InitMovingField(x, y, MovDir[x][y]);
6679 PlayLevelSoundAction(x, y, ACTION_MOVING);
6683 ContinueMoving(x, y);
6686 void ContinueMoving(int x, int y)
6688 int element = Feld[x][y];
6689 struct ElementInfo *ei = &element_info[element];
6690 int direction = MovDir[x][y];
6691 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
6692 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
6693 int newx = x + dx, newy = y + dy;
6694 int stored = Store[x][y];
6695 int stored_new = Store[newx][newy];
6696 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
6697 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
6698 boolean last_line = (newy == lev_fieldy - 1);
6700 MovPos[x][y] += getElementMoveStepsize(x, y);
6702 if (pushed_by_player) /* special case: moving object pushed by player */
6703 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
6705 if (ABS(MovPos[x][y]) < TILEX)
6707 DrawLevelField(x, y);
6709 return; /* element is still moving */
6712 /* element reached destination field */
6714 Feld[x][y] = EL_EMPTY;
6715 Feld[newx][newy] = element;
6716 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
6718 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
6720 element = Feld[newx][newy] = EL_ACID;
6722 else if (element == EL_MOLE)
6724 Feld[x][y] = EL_SAND;
6726 DrawLevelFieldCrumbledSandNeighbours(x, y);
6728 else if (element == EL_QUICKSAND_FILLING)
6730 element = Feld[newx][newy] = get_next_element(element);
6731 Store[newx][newy] = Store[x][y];
6733 else if (element == EL_QUICKSAND_EMPTYING)
6735 Feld[x][y] = get_next_element(element);
6736 element = Feld[newx][newy] = Store[x][y];
6738 else if (element == EL_QUICKSAND_FAST_FILLING)
6740 element = Feld[newx][newy] = get_next_element(element);
6741 Store[newx][newy] = Store[x][y];
6743 else if (element == EL_QUICKSAND_FAST_EMPTYING)
6745 Feld[x][y] = get_next_element(element);
6746 element = Feld[newx][newy] = Store[x][y];
6748 else if (element == EL_MAGIC_WALL_FILLING)
6750 element = Feld[newx][newy] = get_next_element(element);
6751 if (!game.magic_wall_active)
6752 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
6753 Store[newx][newy] = Store[x][y];
6755 else if (element == EL_MAGIC_WALL_EMPTYING)
6757 Feld[x][y] = get_next_element(element);
6758 if (!game.magic_wall_active)
6759 Feld[x][y] = EL_MAGIC_WALL_DEAD;
6760 element = Feld[newx][newy] = Store[x][y];
6762 #if USE_NEW_CUSTOM_VALUE
6763 InitField(newx, newy, FALSE);
6766 else if (element == EL_BD_MAGIC_WALL_FILLING)
6768 element = Feld[newx][newy] = get_next_element(element);
6769 if (!game.magic_wall_active)
6770 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
6771 Store[newx][newy] = Store[x][y];
6773 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
6775 Feld[x][y] = get_next_element(element);
6776 if (!game.magic_wall_active)
6777 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
6778 element = Feld[newx][newy] = Store[x][y];
6780 #if USE_NEW_CUSTOM_VALUE
6781 InitField(newx, newy, FALSE);
6784 else if (element == EL_DC_MAGIC_WALL_FILLING)
6786 element = Feld[newx][newy] = get_next_element(element);
6787 if (!game.magic_wall_active)
6788 element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
6789 Store[newx][newy] = Store[x][y];
6791 else if (element == EL_DC_MAGIC_WALL_EMPTYING)
6793 Feld[x][y] = get_next_element(element);
6794 if (!game.magic_wall_active)
6795 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
6796 element = Feld[newx][newy] = Store[x][y];
6798 #if USE_NEW_CUSTOM_VALUE
6799 InitField(newx, newy, FALSE);
6802 else if (element == EL_AMOEBA_DROPPING)
6804 Feld[x][y] = get_next_element(element);
6805 element = Feld[newx][newy] = Store[x][y];
6807 else if (element == EL_SOKOBAN_OBJECT)
6810 Feld[x][y] = Back[x][y];
6812 if (Back[newx][newy])
6813 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
6815 Back[x][y] = Back[newx][newy] = 0;
6818 Store[x][y] = EL_EMPTY;
6823 MovDelay[newx][newy] = 0;
6825 if (CAN_CHANGE_OR_HAS_ACTION(element))
6827 /* copy element change control values to new field */
6828 ChangeDelay[newx][newy] = ChangeDelay[x][y];
6829 ChangePage[newx][newy] = ChangePage[x][y];
6830 ChangeCount[newx][newy] = ChangeCount[x][y];
6831 ChangeEvent[newx][newy] = ChangeEvent[x][y];
6834 #if USE_NEW_CUSTOM_VALUE
6835 CustomValue[newx][newy] = CustomValue[x][y];
6838 ChangeDelay[x][y] = 0;
6839 ChangePage[x][y] = -1;
6840 ChangeCount[x][y] = 0;
6841 ChangeEvent[x][y] = -1;
6843 #if USE_NEW_CUSTOM_VALUE
6844 CustomValue[x][y] = 0;
6847 /* copy animation control values to new field */
6848 GfxFrame[newx][newy] = GfxFrame[x][y];
6849 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
6850 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
6851 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
6853 Pushed[x][y] = Pushed[newx][newy] = FALSE;
6855 /* some elements can leave other elements behind after moving */
6857 if (ei->move_leave_element != EL_EMPTY &&
6858 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
6859 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
6861 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
6862 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
6863 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
6866 int move_leave_element = ei->move_leave_element;
6870 /* this makes it possible to leave the removed element again */
6871 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
6872 move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
6874 /* this makes it possible to leave the removed element again */
6875 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
6876 move_leave_element = stored;
6879 /* this makes it possible to leave the removed element again */
6880 if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
6881 ei->move_leave_element == EL_TRIGGER_ELEMENT)
6882 move_leave_element = stored;
6885 Feld[x][y] = move_leave_element;
6887 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
6888 MovDir[x][y] = direction;
6890 InitField(x, y, FALSE);
6892 if (GFX_CRUMBLED(Feld[x][y]))
6893 DrawLevelFieldCrumbledSandNeighbours(x, y);
6895 if (ELEM_IS_PLAYER(move_leave_element))
6896 RelocatePlayer(x, y, move_leave_element);
6899 /* do this after checking for left-behind element */
6900 ResetGfxAnimation(x, y); /* reset animation values for old field */
6902 if (!CAN_MOVE(element) ||
6903 (CAN_FALL(element) && direction == MV_DOWN &&
6904 (element == EL_SPRING ||
6905 element_info[element].move_pattern == MV_WHEN_PUSHED ||
6906 element_info[element].move_pattern == MV_WHEN_DROPPED)))
6907 GfxDir[x][y] = MovDir[newx][newy] = 0;
6909 DrawLevelField(x, y);
6910 DrawLevelField(newx, newy);
6912 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
6914 /* prevent pushed element from moving on in pushed direction */
6915 if (pushed_by_player && CAN_MOVE(element) &&
6916 element_info[element].move_pattern & MV_ANY_DIRECTION &&
6917 !(element_info[element].move_pattern & direction))
6918 TurnRound(newx, newy);
6920 /* prevent elements on conveyor belt from moving on in last direction */
6921 if (pushed_by_conveyor && CAN_FALL(element) &&
6922 direction & MV_HORIZONTAL)
6923 MovDir[newx][newy] = 0;
6925 if (!pushed_by_player)
6927 int nextx = newx + dx, nexty = newy + dy;
6928 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
6930 WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
6932 if (CAN_FALL(element) && direction == MV_DOWN)
6933 WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
6935 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
6936 CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
6938 #if USE_FIX_IMPACT_COLLISION
6939 if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
6940 CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
6944 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
6946 TestIfBadThingTouchesPlayer(newx, newy);
6947 TestIfBadThingTouchesFriend(newx, newy);
6949 if (!IS_CUSTOM_ELEMENT(element))
6950 TestIfBadThingTouchesOtherBadThing(newx, newy);
6952 else if (element == EL_PENGUIN)
6953 TestIfFriendTouchesBadThing(newx, newy);
6955 /* give the player one last chance (one more frame) to move away */
6956 if (CAN_FALL(element) && direction == MV_DOWN &&
6957 (last_line || (!IS_FREE(x, newy + 1) &&
6958 (!IS_PLAYER(x, newy + 1) ||
6959 game.engine_version < VERSION_IDENT(3,1,1,0)))))
6962 if (pushed_by_player && !game.use_change_when_pushing_bug)
6964 int push_side = MV_DIR_OPPOSITE(direction);
6965 struct PlayerInfo *player = PLAYERINFO(x, y);
6967 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
6968 player->index_bit, push_side);
6969 CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
6970 player->index_bit, push_side);
6973 if (element == EL_EMC_ANDROID && pushed_by_player) /* make another move */
6974 MovDelay[newx][newy] = 1;
6976 CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
6978 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
6981 if (ChangePage[newx][newy] != -1) /* delayed change */
6983 int page = ChangePage[newx][newy];
6984 struct ElementChangeInfo *change = &ei->change_page[page];
6986 ChangePage[newx][newy] = -1;
6988 if (change->can_change)
6990 if (ChangeElement(newx, newy, element, page))
6992 if (change->post_change_function)
6993 change->post_change_function(newx, newy);
6997 if (change->has_action)
6998 ExecuteCustomElementAction(newx, newy, element, page);
7002 TestIfElementHitsCustomElement(newx, newy, direction);
7003 TestIfPlayerTouchesCustomElement(newx, newy);
7004 TestIfElementTouchesCustomElement(newx, newy);
7006 if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
7007 IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
7008 CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
7009 MV_DIR_OPPOSITE(direction));
7012 int AmoebeNachbarNr(int ax, int ay)
7015 int element = Feld[ax][ay];
7017 static int xy[4][2] =
7025 for (i = 0; i < NUM_DIRECTIONS; i++)
7027 int x = ax + xy[i][0];
7028 int y = ay + xy[i][1];
7030 if (!IN_LEV_FIELD(x, y))
7033 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
7034 group_nr = AmoebaNr[x][y];
7040 void AmoebenVereinigen(int ax, int ay)
7042 int i, x, y, xx, yy;
7043 int new_group_nr = AmoebaNr[ax][ay];
7044 static int xy[4][2] =
7052 if (new_group_nr == 0)
7055 for (i = 0; i < NUM_DIRECTIONS; i++)
7060 if (!IN_LEV_FIELD(x, y))
7063 if ((Feld[x][y] == EL_AMOEBA_FULL ||
7064 Feld[x][y] == EL_BD_AMOEBA ||
7065 Feld[x][y] == EL_AMOEBA_DEAD) &&
7066 AmoebaNr[x][y] != new_group_nr)
7068 int old_group_nr = AmoebaNr[x][y];
7070 if (old_group_nr == 0)
7073 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
7074 AmoebaCnt[old_group_nr] = 0;
7075 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
7076 AmoebaCnt2[old_group_nr] = 0;
7078 SCAN_PLAYFIELD(xx, yy)
7080 if (AmoebaNr[xx][yy] == old_group_nr)
7081 AmoebaNr[xx][yy] = new_group_nr;
7087 void AmoebeUmwandeln(int ax, int ay)
7091 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
7093 int group_nr = AmoebaNr[ax][ay];
7098 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
7099 printf("AmoebeUmwandeln(): This should never happen!\n");
7104 SCAN_PLAYFIELD(x, y)
7106 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
7109 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
7113 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
7114 SND_AMOEBA_TURNING_TO_GEM :
7115 SND_AMOEBA_TURNING_TO_ROCK));
7120 static int xy[4][2] =
7128 for (i = 0; i < NUM_DIRECTIONS; i++)
7133 if (!IN_LEV_FIELD(x, y))
7136 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
7138 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
7139 SND_AMOEBA_TURNING_TO_GEM :
7140 SND_AMOEBA_TURNING_TO_ROCK));
7147 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
7150 int group_nr = AmoebaNr[ax][ay];
7151 boolean done = FALSE;
7156 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
7157 printf("AmoebeUmwandelnBD(): This should never happen!\n");
7162 SCAN_PLAYFIELD(x, y)
7164 if (AmoebaNr[x][y] == group_nr &&
7165 (Feld[x][y] == EL_AMOEBA_DEAD ||
7166 Feld[x][y] == EL_BD_AMOEBA ||
7167 Feld[x][y] == EL_AMOEBA_GROWING))
7170 Feld[x][y] = new_element;
7171 InitField(x, y, FALSE);
7172 DrawLevelField(x, y);
7178 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
7179 SND_BD_AMOEBA_TURNING_TO_ROCK :
7180 SND_BD_AMOEBA_TURNING_TO_GEM));
7183 void AmoebeWaechst(int x, int y)
7185 static unsigned long sound_delay = 0;
7186 static unsigned long sound_delay_value = 0;
7188 if (!MovDelay[x][y]) /* start new growing cycle */
7192 if (DelayReached(&sound_delay, sound_delay_value))
7194 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
7195 sound_delay_value = 30;
7199 if (MovDelay[x][y]) /* wait some time before growing bigger */
7202 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7204 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
7205 6 - MovDelay[x][y]);
7207 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
7210 if (!MovDelay[x][y])
7212 Feld[x][y] = Store[x][y];
7214 DrawLevelField(x, y);
7219 void AmoebaDisappearing(int x, int y)
7221 static unsigned long sound_delay = 0;
7222 static unsigned long sound_delay_value = 0;
7224 if (!MovDelay[x][y]) /* start new shrinking cycle */
7228 if (DelayReached(&sound_delay, sound_delay_value))
7229 sound_delay_value = 30;
7232 if (MovDelay[x][y]) /* wait some time before shrinking */
7235 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7237 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
7238 6 - MovDelay[x][y]);
7240 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
7243 if (!MovDelay[x][y])
7245 Feld[x][y] = EL_EMPTY;
7246 DrawLevelField(x, y);
7248 /* don't let mole enter this field in this cycle;
7249 (give priority to objects falling to this field from above) */
7255 void AmoebeAbleger(int ax, int ay)
7258 int element = Feld[ax][ay];
7259 int graphic = el2img(element);
7260 int newax = ax, neway = ay;
7261 boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
7262 static int xy[4][2] =
7270 if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
7272 Feld[ax][ay] = EL_AMOEBA_DEAD;
7273 DrawLevelField(ax, ay);
7277 if (IS_ANIMATED(graphic))
7278 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7280 if (!MovDelay[ax][ay]) /* start making new amoeba field */
7281 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
7283 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
7286 if (MovDelay[ax][ay])
7290 if (can_drop) /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
7293 int x = ax + xy[start][0];
7294 int y = ay + xy[start][1];
7296 if (!IN_LEV_FIELD(x, y))
7299 if (IS_FREE(x, y) ||
7300 CAN_GROW_INTO(Feld[x][y]) ||
7301 Feld[x][y] == EL_QUICKSAND_EMPTY ||
7302 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
7308 if (newax == ax && neway == ay)
7311 else /* normal or "filled" (BD style) amoeba */
7314 boolean waiting_for_player = FALSE;
7316 for (i = 0; i < NUM_DIRECTIONS; i++)
7318 int j = (start + i) % 4;
7319 int x = ax + xy[j][0];
7320 int y = ay + xy[j][1];
7322 if (!IN_LEV_FIELD(x, y))
7325 if (IS_FREE(x, y) ||
7326 CAN_GROW_INTO(Feld[x][y]) ||
7327 Feld[x][y] == EL_QUICKSAND_EMPTY ||
7328 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
7334 else if (IS_PLAYER(x, y))
7335 waiting_for_player = TRUE;
7338 if (newax == ax && neway == ay) /* amoeba cannot grow */
7340 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
7342 Feld[ax][ay] = EL_AMOEBA_DEAD;
7343 DrawLevelField(ax, ay);
7344 AmoebaCnt[AmoebaNr[ax][ay]]--;
7346 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
7348 if (element == EL_AMOEBA_FULL)
7349 AmoebeUmwandeln(ax, ay);
7350 else if (element == EL_BD_AMOEBA)
7351 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
7356 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
7358 /* amoeba gets larger by growing in some direction */
7360 int new_group_nr = AmoebaNr[ax][ay];
7363 if (new_group_nr == 0)
7365 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
7366 printf("AmoebeAbleger(): This should never happen!\n");
7371 AmoebaNr[newax][neway] = new_group_nr;
7372 AmoebaCnt[new_group_nr]++;
7373 AmoebaCnt2[new_group_nr]++;
7375 /* if amoeba touches other amoeba(s) after growing, unify them */
7376 AmoebenVereinigen(newax, neway);
7378 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
7380 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
7386 if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
7387 (neway == lev_fieldy - 1 && newax != ax))
7389 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
7390 Store[newax][neway] = element;
7392 else if (neway == ay || element == EL_EMC_DRIPPER)
7394 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
7396 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
7400 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
7401 Feld[ax][ay] = EL_AMOEBA_DROPPING;
7402 Store[ax][ay] = EL_AMOEBA_DROP;
7403 ContinueMoving(ax, ay);
7407 DrawLevelField(newax, neway);
7410 void Life(int ax, int ay)
7414 int element = Feld[ax][ay];
7415 int graphic = el2img(element);
7416 int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
7418 boolean changed = FALSE;
7420 if (IS_ANIMATED(graphic))
7421 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7426 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
7427 MovDelay[ax][ay] = life_time;
7429 if (MovDelay[ax][ay]) /* wait some time before next cycle */
7432 if (MovDelay[ax][ay])
7436 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
7438 int xx = ax+x1, yy = ay+y1;
7441 if (!IN_LEV_FIELD(xx, yy))
7444 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
7446 int x = xx+x2, y = yy+y2;
7448 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
7451 if (((Feld[x][y] == element ||
7452 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
7454 (IS_FREE(x, y) && Stop[x][y]))
7458 if (xx == ax && yy == ay) /* field in the middle */
7460 if (nachbarn < life_parameter[0] ||
7461 nachbarn > life_parameter[1])
7463 Feld[xx][yy] = EL_EMPTY;
7465 DrawLevelField(xx, yy);
7466 Stop[xx][yy] = TRUE;
7470 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
7471 { /* free border field */
7472 if (nachbarn >= life_parameter[2] &&
7473 nachbarn <= life_parameter[3])
7475 Feld[xx][yy] = element;
7476 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
7478 DrawLevelField(xx, yy);
7479 Stop[xx][yy] = TRUE;
7486 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
7487 SND_GAME_OF_LIFE_GROWING);
7490 static void InitRobotWheel(int x, int y)
7492 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
7495 static void RunRobotWheel(int x, int y)
7497 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
7500 static void StopRobotWheel(int x, int y)
7502 if (ZX == x && ZY == y)
7506 static void InitTimegateWheel(int x, int y)
7508 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
7511 static void RunTimegateWheel(int x, int y)
7513 PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
7516 static void InitMagicBallDelay(int x, int y)
7519 ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
7521 ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
7525 static void ActivateMagicBall(int bx, int by)
7529 if (level.ball_random)
7531 int pos_border = RND(8); /* select one of the eight border elements */
7532 int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
7533 int xx = pos_content % 3;
7534 int yy = pos_content / 3;
7539 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
7540 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
7544 for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
7546 int xx = x - bx + 1;
7547 int yy = y - by + 1;
7549 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
7550 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
7554 game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
7557 void CheckExit(int x, int y)
7559 if (local_player->gems_still_needed > 0 ||
7560 local_player->sokobanfields_still_needed > 0 ||
7561 local_player->lights_still_needed > 0)
7563 int element = Feld[x][y];
7564 int graphic = el2img(element);
7566 if (IS_ANIMATED(graphic))
7567 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7572 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7575 Feld[x][y] = EL_EXIT_OPENING;
7577 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
7580 void CheckExitEM(int x, int y)
7582 if (local_player->gems_still_needed > 0 ||
7583 local_player->sokobanfields_still_needed > 0 ||
7584 local_player->lights_still_needed > 0)
7586 int element = Feld[x][y];
7587 int graphic = el2img(element);
7589 if (IS_ANIMATED(graphic))
7590 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7595 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7598 Feld[x][y] = EL_EM_EXIT_OPENING;
7600 PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
7603 void CheckExitSteel(int x, int y)
7605 if (local_player->gems_still_needed > 0 ||
7606 local_player->sokobanfields_still_needed > 0 ||
7607 local_player->lights_still_needed > 0)
7609 int element = Feld[x][y];
7610 int graphic = el2img(element);
7612 if (IS_ANIMATED(graphic))
7613 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7618 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7621 Feld[x][y] = EL_STEEL_EXIT_OPENING;
7623 PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
7626 void CheckExitSteelEM(int x, int y)
7628 if (local_player->gems_still_needed > 0 ||
7629 local_player->sokobanfields_still_needed > 0 ||
7630 local_player->lights_still_needed > 0)
7632 int element = Feld[x][y];
7633 int graphic = el2img(element);
7635 if (IS_ANIMATED(graphic))
7636 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7641 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7644 Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
7646 PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
7649 void CheckExitSP(int x, int y)
7651 if (local_player->gems_still_needed > 0)
7653 int element = Feld[x][y];
7654 int graphic = el2img(element);
7656 if (IS_ANIMATED(graphic))
7657 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7662 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7665 Feld[x][y] = EL_SP_EXIT_OPENING;
7667 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
7670 static void CloseAllOpenTimegates()
7674 SCAN_PLAYFIELD(x, y)
7676 int element = Feld[x][y];
7678 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
7680 Feld[x][y] = EL_TIMEGATE_CLOSING;
7682 PlayLevelSoundAction(x, y, ACTION_CLOSING);
7687 void DrawTwinkleOnField(int x, int y)
7689 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
7692 if (Feld[x][y] == EL_BD_DIAMOND)
7695 if (MovDelay[x][y] == 0) /* next animation frame */
7696 MovDelay[x][y] = 11 * !GetSimpleRandom(500);
7698 if (MovDelay[x][y] != 0) /* wait some time before next frame */
7702 if (setup.direct_draw && MovDelay[x][y])
7703 SetDrawtoField(DRAW_BUFFERED);
7705 DrawLevelElementAnimation(x, y, Feld[x][y]);
7707 if (MovDelay[x][y] != 0)
7709 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
7710 10 - MovDelay[x][y]);
7712 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
7714 if (setup.direct_draw)
7718 dest_x = FX + SCREENX(x) * TILEX;
7719 dest_y = FY + SCREENY(y) * TILEY;
7721 BlitBitmap(drawto_field, window,
7722 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
7723 SetDrawtoField(DRAW_DIRECT);
7729 void MauerWaechst(int x, int y)
7733 if (!MovDelay[x][y]) /* next animation frame */
7734 MovDelay[x][y] = 3 * delay;
7736 if (MovDelay[x][y]) /* wait some time before next frame */
7740 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7742 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
7743 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
7745 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
7748 if (!MovDelay[x][y])
7750 if (MovDir[x][y] == MV_LEFT)
7752 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
7753 DrawLevelField(x - 1, y);
7755 else if (MovDir[x][y] == MV_RIGHT)
7757 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
7758 DrawLevelField(x + 1, y);
7760 else if (MovDir[x][y] == MV_UP)
7762 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
7763 DrawLevelField(x, y - 1);
7767 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
7768 DrawLevelField(x, y + 1);
7771 Feld[x][y] = Store[x][y];
7773 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7774 DrawLevelField(x, y);
7779 void MauerAbleger(int ax, int ay)
7781 int element = Feld[ax][ay];
7782 int graphic = el2img(element);
7783 boolean oben_frei = FALSE, unten_frei = FALSE;
7784 boolean links_frei = FALSE, rechts_frei = FALSE;
7785 boolean oben_massiv = FALSE, unten_massiv = FALSE;
7786 boolean links_massiv = FALSE, rechts_massiv = FALSE;
7787 boolean new_wall = FALSE;
7789 if (IS_ANIMATED(graphic))
7790 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7792 if (!MovDelay[ax][ay]) /* start building new wall */
7793 MovDelay[ax][ay] = 6;
7795 if (MovDelay[ax][ay]) /* wait some time before building new wall */
7798 if (MovDelay[ax][ay])
7802 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
7804 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
7806 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
7808 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
7811 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
7812 element == EL_EXPANDABLE_WALL_ANY)
7816 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
7817 Store[ax][ay-1] = element;
7818 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
7819 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
7820 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
7821 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
7826 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
7827 Store[ax][ay+1] = element;
7828 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
7829 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
7830 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
7831 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
7836 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7837 element == EL_EXPANDABLE_WALL_ANY ||
7838 element == EL_EXPANDABLE_WALL ||
7839 element == EL_BD_EXPANDABLE_WALL)
7843 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
7844 Store[ax-1][ay] = element;
7845 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
7846 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
7847 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
7848 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
7854 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
7855 Store[ax+1][ay] = element;
7856 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
7857 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
7858 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
7859 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
7864 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
7865 DrawLevelField(ax, ay);
7867 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
7869 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
7870 unten_massiv = TRUE;
7871 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
7872 links_massiv = TRUE;
7873 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
7874 rechts_massiv = TRUE;
7876 if (((oben_massiv && unten_massiv) ||
7877 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7878 element == EL_EXPANDABLE_WALL) &&
7879 ((links_massiv && rechts_massiv) ||
7880 element == EL_EXPANDABLE_WALL_VERTICAL))
7881 Feld[ax][ay] = EL_WALL;
7884 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
7887 void MauerAblegerStahl(int ax, int ay)
7889 int element = Feld[ax][ay];
7890 int graphic = el2img(element);
7891 boolean oben_frei = FALSE, unten_frei = FALSE;
7892 boolean links_frei = FALSE, rechts_frei = FALSE;
7893 boolean oben_massiv = FALSE, unten_massiv = FALSE;
7894 boolean links_massiv = FALSE, rechts_massiv = FALSE;
7895 boolean new_wall = FALSE;
7897 if (IS_ANIMATED(graphic))
7898 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7900 if (!MovDelay[ax][ay]) /* start building new wall */
7901 MovDelay[ax][ay] = 6;
7903 if (MovDelay[ax][ay]) /* wait some time before building new wall */
7906 if (MovDelay[ax][ay])
7910 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
7912 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
7914 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
7916 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
7919 if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
7920 element == EL_EXPANDABLE_STEELWALL_ANY)
7924 Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
7925 Store[ax][ay-1] = element;
7926 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
7927 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
7928 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
7929 IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
7934 Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
7935 Store[ax][ay+1] = element;
7936 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
7937 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
7938 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
7939 IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
7944 if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
7945 element == EL_EXPANDABLE_STEELWALL_ANY)
7949 Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
7950 Store[ax-1][ay] = element;
7951 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
7952 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
7953 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
7954 IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
7960 Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
7961 Store[ax+1][ay] = element;
7962 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
7963 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
7964 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
7965 IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
7970 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
7972 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
7973 unten_massiv = TRUE;
7974 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
7975 links_massiv = TRUE;
7976 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
7977 rechts_massiv = TRUE;
7979 if (((oben_massiv && unten_massiv) ||
7980 element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
7981 ((links_massiv && rechts_massiv) ||
7982 element == EL_EXPANDABLE_STEELWALL_VERTICAL))
7983 Feld[ax][ay] = EL_WALL;
7986 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
7989 void CheckForDragon(int x, int y)
7992 boolean dragon_found = FALSE;
7993 static int xy[4][2] =
8001 for (i = 0; i < NUM_DIRECTIONS; i++)
8003 for (j = 0; j < 4; j++)
8005 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
8007 if (IN_LEV_FIELD(xx, yy) &&
8008 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
8010 if (Feld[xx][yy] == EL_DRAGON)
8011 dragon_found = TRUE;
8020 for (i = 0; i < NUM_DIRECTIONS; i++)
8022 for (j = 0; j < 3; j++)
8024 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
8026 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
8028 Feld[xx][yy] = EL_EMPTY;
8029 DrawLevelField(xx, yy);
8038 static void InitBuggyBase(int x, int y)
8040 int element = Feld[x][y];
8041 int activating_delay = FRAMES_PER_SECOND / 4;
8044 (element == EL_SP_BUGGY_BASE ?
8045 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
8046 element == EL_SP_BUGGY_BASE_ACTIVATING ?
8048 element == EL_SP_BUGGY_BASE_ACTIVE ?
8049 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
8052 static void WarnBuggyBase(int x, int y)
8055 static int xy[4][2] =
8063 for (i = 0; i < NUM_DIRECTIONS; i++)
8065 int xx = x + xy[i][0];
8066 int yy = y + xy[i][1];
8068 if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
8070 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
8077 static void InitTrap(int x, int y)
8079 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
8082 static void ActivateTrap(int x, int y)
8084 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
8087 static void ChangeActiveTrap(int x, int y)
8089 int graphic = IMG_TRAP_ACTIVE;
8091 /* if new animation frame was drawn, correct crumbled sand border */
8092 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
8093 DrawLevelFieldCrumbledSand(x, y);
8096 static int getSpecialActionElement(int element, int number, int base_element)
8098 return (element != EL_EMPTY ? element :
8099 number != -1 ? base_element + number - 1 :
8103 static int getModifiedActionNumber(int value_old, int operator, int operand,
8104 int value_min, int value_max)
8106 int value_new = (operator == CA_MODE_SET ? operand :
8107 operator == CA_MODE_ADD ? value_old + operand :
8108 operator == CA_MODE_SUBTRACT ? value_old - operand :
8109 operator == CA_MODE_MULTIPLY ? value_old * operand :
8110 operator == CA_MODE_DIVIDE ? value_old / MAX(1, operand) :
8111 operator == CA_MODE_MODULO ? value_old % MAX(1, operand) :
8114 return (value_new < value_min ? value_min :
8115 value_new > value_max ? value_max :
8119 static void ExecuteCustomElementAction(int x, int y, int element, int page)
8121 struct ElementInfo *ei = &element_info[element];
8122 struct ElementChangeInfo *change = &ei->change_page[page];
8123 int target_element = change->target_element;
8124 int action_type = change->action_type;
8125 int action_mode = change->action_mode;
8126 int action_arg = change->action_arg;
8129 if (!change->has_action)
8132 /* ---------- determine action paramater values -------------------------- */
8134 int level_time_value =
8135 (level.time > 0 ? TimeLeft :
8138 int action_arg_element =
8139 (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player :
8140 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
8141 action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element :
8144 int action_arg_direction =
8145 (action_arg >= CA_ARG_DIRECTION_LEFT &&
8146 action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
8147 action_arg == CA_ARG_DIRECTION_TRIGGER ?
8148 change->actual_trigger_side :
8149 action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
8150 MV_DIR_OPPOSITE(change->actual_trigger_side) :
8153 int action_arg_number_min =
8154 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
8157 int action_arg_number_max =
8158 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
8159 action_type == CA_SET_LEVEL_GEMS ? 999 :
8160 action_type == CA_SET_LEVEL_TIME ? 9999 :
8161 action_type == CA_SET_LEVEL_SCORE ? 99999 :
8162 action_type == CA_SET_CE_VALUE ? 9999 :
8163 action_type == CA_SET_CE_SCORE ? 9999 :
8166 int action_arg_number_reset =
8167 (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
8168 action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
8169 action_type == CA_SET_LEVEL_TIME ? level.time :
8170 action_type == CA_SET_LEVEL_SCORE ? 0 :
8171 #if USE_NEW_CUSTOM_VALUE
8172 action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
8174 action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
8176 action_type == CA_SET_CE_SCORE ? 0 :
8179 int action_arg_number =
8180 (action_arg <= CA_ARG_MAX ? action_arg :
8181 action_arg >= CA_ARG_SPEED_NOT_MOVING &&
8182 action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
8183 action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
8184 action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
8185 action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
8186 action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
8187 #if USE_NEW_CUSTOM_VALUE
8188 action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
8190 action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
8192 action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
8193 action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
8194 action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
8195 action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
8196 action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
8197 action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
8198 action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
8199 action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
8200 action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
8201 action_arg == CA_ARG_ELEMENT_NR_TARGET ? change->target_element :
8202 action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
8205 int action_arg_number_old =
8206 (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
8207 action_type == CA_SET_LEVEL_TIME ? TimeLeft :
8208 action_type == CA_SET_LEVEL_SCORE ? local_player->score :
8209 action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
8210 action_type == CA_SET_CE_SCORE ? ei->collect_score :
8213 int action_arg_number_new =
8214 getModifiedActionNumber(action_arg_number_old,
8215 action_mode, action_arg_number,
8216 action_arg_number_min, action_arg_number_max);
8218 int trigger_player_bits =
8219 (change->actual_trigger_player >= EL_PLAYER_1 &&
8220 change->actual_trigger_player <= EL_PLAYER_4 ?
8221 (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
8224 int action_arg_player_bits =
8225 (action_arg >= CA_ARG_PLAYER_1 &&
8226 action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
8227 action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
8230 /* ---------- execute action -------------------------------------------- */
8232 switch (action_type)
8239 /* ---------- level actions ------------------------------------------- */
8241 case CA_RESTART_LEVEL:
8243 game.restart_level = TRUE;
8248 case CA_SHOW_ENVELOPE:
8250 int element = getSpecialActionElement(action_arg_element,
8251 action_arg_number, EL_ENVELOPE_1);
8253 if (IS_ENVELOPE(element))
8254 local_player->show_envelope = element;
8259 case CA_SET_LEVEL_TIME:
8261 if (level.time > 0) /* only modify limited time value */
8263 TimeLeft = action_arg_number_new;
8265 DrawGameValue_Time(TimeLeft);
8267 if (!TimeLeft && setup.time_limit)
8268 for (i = 0; i < MAX_PLAYERS; i++)
8269 KillPlayer(&stored_player[i]);
8275 case CA_SET_LEVEL_SCORE:
8277 local_player->score = action_arg_number_new;
8279 DrawGameValue_Score(local_player->score);
8284 case CA_SET_LEVEL_GEMS:
8286 local_player->gems_still_needed = action_arg_number_new;
8288 DrawGameValue_Emeralds(local_player->gems_still_needed);
8293 #if !USE_PLAYER_GRAVITY
8294 case CA_SET_LEVEL_GRAVITY:
8296 game.gravity = (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
8297 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
8298 action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
8304 case CA_SET_LEVEL_WIND:
8306 game.wind_direction = action_arg_direction;
8311 /* ---------- player actions ------------------------------------------ */
8313 case CA_MOVE_PLAYER:
8315 /* automatically move to the next field in specified direction */
8316 for (i = 0; i < MAX_PLAYERS; i++)
8317 if (trigger_player_bits & (1 << i))
8318 stored_player[i].programmed_action = action_arg_direction;
8323 case CA_EXIT_PLAYER:
8325 for (i = 0; i < MAX_PLAYERS; i++)
8326 if (action_arg_player_bits & (1 << i))
8327 PlayerWins(&stored_player[i]);
8332 case CA_KILL_PLAYER:
8334 for (i = 0; i < MAX_PLAYERS; i++)
8335 if (action_arg_player_bits & (1 << i))
8336 KillPlayer(&stored_player[i]);
8341 case CA_SET_PLAYER_KEYS:
8343 int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
8344 int element = getSpecialActionElement(action_arg_element,
8345 action_arg_number, EL_KEY_1);
8347 if (IS_KEY(element))
8349 for (i = 0; i < MAX_PLAYERS; i++)
8351 if (trigger_player_bits & (1 << i))
8353 stored_player[i].key[KEY_NR(element)] = key_state;
8355 DrawGameDoorValues();
8363 case CA_SET_PLAYER_SPEED:
8365 for (i = 0; i < MAX_PLAYERS; i++)
8367 if (trigger_player_bits & (1 << i))
8369 int move_stepsize = TILEX / stored_player[i].move_delay_value;
8371 if (action_arg == CA_ARG_SPEED_FASTER &&
8372 stored_player[i].cannot_move)
8374 action_arg_number = STEPSIZE_VERY_SLOW;
8376 else if (action_arg == CA_ARG_SPEED_SLOWER ||
8377 action_arg == CA_ARG_SPEED_FASTER)
8379 action_arg_number = 2;
8380 action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
8383 else if (action_arg == CA_ARG_NUMBER_RESET)
8385 action_arg_number = level.initial_player_stepsize[i];
8389 getModifiedActionNumber(move_stepsize,
8392 action_arg_number_min,
8393 action_arg_number_max);
8395 SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
8402 case CA_SET_PLAYER_SHIELD:
8404 for (i = 0; i < MAX_PLAYERS; i++)
8406 if (trigger_player_bits & (1 << i))
8408 if (action_arg == CA_ARG_SHIELD_OFF)
8410 stored_player[i].shield_normal_time_left = 0;
8411 stored_player[i].shield_deadly_time_left = 0;
8413 else if (action_arg == CA_ARG_SHIELD_NORMAL)
8415 stored_player[i].shield_normal_time_left = 999999;
8417 else if (action_arg == CA_ARG_SHIELD_DEADLY)
8419 stored_player[i].shield_normal_time_left = 999999;
8420 stored_player[i].shield_deadly_time_left = 999999;
8428 #if USE_PLAYER_GRAVITY
8429 case CA_SET_PLAYER_GRAVITY:
8431 for (i = 0; i < MAX_PLAYERS; i++)
8433 if (trigger_player_bits & (1 << i))
8435 stored_player[i].gravity =
8436 (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
8437 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
8438 action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
8439 stored_player[i].gravity);
8447 case CA_SET_PLAYER_ARTWORK:
8449 for (i = 0; i < MAX_PLAYERS; i++)
8451 if (trigger_player_bits & (1 << i))
8453 int artwork_element = action_arg_element;
8455 if (action_arg == CA_ARG_ELEMENT_RESET)
8457 (level.use_artwork_element[i] ? level.artwork_element[i] :
8458 stored_player[i].element_nr);
8460 #if USE_GFX_RESET_PLAYER_ARTWORK
8461 if (stored_player[i].artwork_element != artwork_element)
8462 stored_player[i].Frame = 0;
8465 stored_player[i].artwork_element = artwork_element;
8467 SetPlayerWaiting(&stored_player[i], FALSE);
8469 /* set number of special actions for bored and sleeping animation */
8470 stored_player[i].num_special_action_bored =
8471 get_num_special_action(artwork_element,
8472 ACTION_BORING_1, ACTION_BORING_LAST);
8473 stored_player[i].num_special_action_sleeping =
8474 get_num_special_action(artwork_element,
8475 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
8482 /* ---------- CE actions ---------------------------------------------- */
8484 case CA_SET_CE_VALUE:
8486 #if USE_NEW_CUSTOM_VALUE
8487 int last_ce_value = CustomValue[x][y];
8489 CustomValue[x][y] = action_arg_number_new;
8491 if (CustomValue[x][y] != last_ce_value)
8493 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
8494 CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
8496 if (CustomValue[x][y] == 0)
8498 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
8499 CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
8507 case CA_SET_CE_SCORE:
8509 #if USE_NEW_CUSTOM_VALUE
8510 int last_ce_score = ei->collect_score;
8512 ei->collect_score = action_arg_number_new;
8514 if (ei->collect_score != last_ce_score)
8516 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
8517 CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
8519 if (ei->collect_score == 0)
8523 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
8524 CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
8527 This is a very special case that seems to be a mixture between
8528 CheckElementChange() and CheckTriggeredElementChange(): while
8529 the first one only affects single elements that are triggered
8530 directly, the second one affects multiple elements in the playfield
8531 that are triggered indirectly by another element. This is a third
8532 case: Changing the CE score always affects multiple identical CEs,
8533 so every affected CE must be checked, not only the single CE for
8534 which the CE score was changed in the first place (as every instance
8535 of that CE shares the same CE score, and therefore also can change)!
8537 SCAN_PLAYFIELD(xx, yy)
8539 if (Feld[xx][yy] == element)
8540 CheckElementChange(xx, yy, element, EL_UNDEFINED,
8541 CE_SCORE_GETS_ZERO);
8550 /* ---------- engine actions ------------------------------------------ */
8552 case CA_SET_ENGINE_SCAN_MODE:
8554 InitPlayfieldScanMode(action_arg);
8564 static void CreateFieldExt(int x, int y, int element, boolean is_change)
8566 int old_element = Feld[x][y];
8567 int new_element = get_element_from_group_element(element);
8568 int previous_move_direction = MovDir[x][y];
8569 #if USE_NEW_CUSTOM_VALUE
8570 int last_ce_value = CustomValue[x][y];
8572 boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
8573 boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
8574 boolean add_player_onto_element = (new_element_is_player &&
8575 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
8576 /* this breaks SnakeBite when a snake is
8577 halfway through a door that closes */
8578 /* NOW FIXED AT LEVEL INIT IN files.c */
8579 new_element != EL_SOKOBAN_FIELD_PLAYER &&
8581 IS_WALKABLE(old_element));
8584 /* check if element under the player changes from accessible to unaccessible
8585 (needed for special case of dropping element which then changes) */
8586 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
8587 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
8595 if (!add_player_onto_element)
8597 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
8598 RemoveMovingField(x, y);
8602 Feld[x][y] = new_element;
8604 #if !USE_GFX_RESET_GFX_ANIMATION
8605 ResetGfxAnimation(x, y);
8606 ResetRandomAnimationValue(x, y);
8609 if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
8610 MovDir[x][y] = previous_move_direction;
8612 #if USE_NEW_CUSTOM_VALUE
8613 if (element_info[new_element].use_last_ce_value)
8614 CustomValue[x][y] = last_ce_value;
8617 InitField_WithBug1(x, y, FALSE);
8619 new_element = Feld[x][y]; /* element may have changed */
8621 #if USE_GFX_RESET_GFX_ANIMATION
8622 ResetGfxAnimation(x, y);
8623 ResetRandomAnimationValue(x, y);
8626 DrawLevelField(x, y);
8628 if (GFX_CRUMBLED(new_element))
8629 DrawLevelFieldCrumbledSandNeighbours(x, y);
8633 /* check if element under the player changes from accessible to unaccessible
8634 (needed for special case of dropping element which then changes) */
8635 /* (must be checked after creating new element for walkable group elements) */
8636 #if USE_FIX_KILLED_BY_NON_WALKABLE
8637 if (IS_PLAYER(x, y) && !player_explosion_protected &&
8638 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
8645 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
8646 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
8655 /* "ChangeCount" not set yet to allow "entered by player" change one time */
8656 if (new_element_is_player)
8657 RelocatePlayer(x, y, new_element);
8660 ChangeCount[x][y]++; /* count number of changes in the same frame */
8662 TestIfBadThingTouchesPlayer(x, y);
8663 TestIfPlayerTouchesCustomElement(x, y);
8664 TestIfElementTouchesCustomElement(x, y);
8667 static void CreateField(int x, int y, int element)
8669 CreateFieldExt(x, y, element, FALSE);
8672 static void CreateElementFromChange(int x, int y, int element)
8674 element = GET_VALID_RUNTIME_ELEMENT(element);
8676 #if USE_STOP_CHANGED_ELEMENTS
8677 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
8679 int old_element = Feld[x][y];
8681 /* prevent changed element from moving in same engine frame
8682 unless both old and new element can either fall or move */
8683 if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
8684 (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
8689 CreateFieldExt(x, y, element, TRUE);
8692 static boolean ChangeElement(int x, int y, int element, int page)
8694 struct ElementInfo *ei = &element_info[element];
8695 struct ElementChangeInfo *change = &ei->change_page[page];
8696 int ce_value = CustomValue[x][y];
8697 int ce_score = ei->collect_score;
8699 int old_element = Feld[x][y];
8701 /* always use default change event to prevent running into a loop */
8702 if (ChangeEvent[x][y] == -1)
8703 ChangeEvent[x][y] = CE_DELAY;
8705 if (ChangeEvent[x][y] == CE_DELAY)
8707 /* reset actual trigger element, trigger player and action element */
8708 change->actual_trigger_element = EL_EMPTY;
8709 change->actual_trigger_player = EL_PLAYER_1;
8710 change->actual_trigger_side = CH_SIDE_NONE;
8711 change->actual_trigger_ce_value = 0;
8712 change->actual_trigger_ce_score = 0;
8715 /* do not change elements more than a specified maximum number of changes */
8716 if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
8719 ChangeCount[x][y]++; /* count number of changes in the same frame */
8721 if (change->explode)
8728 if (change->use_target_content)
8730 boolean complete_replace = TRUE;
8731 boolean can_replace[3][3];
8734 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
8737 boolean is_walkable;
8738 boolean is_diggable;
8739 boolean is_collectible;
8740 boolean is_removable;
8741 boolean is_destructible;
8742 int ex = x + xx - 1;
8743 int ey = y + yy - 1;
8744 int content_element = change->target_content.e[xx][yy];
8747 can_replace[xx][yy] = TRUE;
8749 if (ex == x && ey == y) /* do not check changing element itself */
8752 if (content_element == EL_EMPTY_SPACE)
8754 can_replace[xx][yy] = FALSE; /* do not replace border with space */
8759 if (!IN_LEV_FIELD(ex, ey))
8761 can_replace[xx][yy] = FALSE;
8762 complete_replace = FALSE;
8769 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
8770 e = MovingOrBlocked2Element(ex, ey);
8772 is_empty = (IS_FREE(ex, ey) ||
8773 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
8775 is_walkable = (is_empty || IS_WALKABLE(e));
8776 is_diggable = (is_empty || IS_DIGGABLE(e));
8777 is_collectible = (is_empty || IS_COLLECTIBLE(e));
8778 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
8779 is_removable = (is_diggable || is_collectible);
8781 can_replace[xx][yy] =
8782 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
8783 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
8784 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
8785 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
8786 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
8787 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
8788 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
8790 if (!can_replace[xx][yy])
8791 complete_replace = FALSE;
8794 if (!change->only_if_complete || complete_replace)
8796 boolean something_has_changed = FALSE;
8798 if (change->only_if_complete && change->use_random_replace &&
8799 RND(100) < change->random_percentage)
8802 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
8804 int ex = x + xx - 1;
8805 int ey = y + yy - 1;
8806 int content_element;
8808 if (can_replace[xx][yy] && (!change->use_random_replace ||
8809 RND(100) < change->random_percentage))
8811 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
8812 RemoveMovingField(ex, ey);
8814 ChangeEvent[ex][ey] = ChangeEvent[x][y];
8816 content_element = change->target_content.e[xx][yy];
8817 target_element = GET_TARGET_ELEMENT(element, content_element, change,
8818 ce_value, ce_score);
8820 CreateElementFromChange(ex, ey, target_element);
8822 something_has_changed = TRUE;
8824 /* for symmetry reasons, freeze newly created border elements */
8825 if (ex != x || ey != y)
8826 Stop[ex][ey] = TRUE; /* no more moving in this frame */
8830 if (something_has_changed)
8832 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
8833 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
8839 target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
8840 ce_value, ce_score);
8842 if (element == EL_DIAGONAL_GROWING ||
8843 element == EL_DIAGONAL_SHRINKING)
8845 target_element = Store[x][y];
8847 Store[x][y] = EL_EMPTY;
8850 CreateElementFromChange(x, y, target_element);
8852 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
8853 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
8856 /* this uses direct change before indirect change */
8857 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
8862 #if USE_NEW_DELAYED_ACTION
8864 static void HandleElementChange(int x, int y, int page)
8866 int element = MovingOrBlocked2Element(x, y);
8867 struct ElementInfo *ei = &element_info[element];
8868 struct ElementChangeInfo *change = &ei->change_page[page];
8871 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
8872 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
8875 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
8876 x, y, element, element_info[element].token_name);
8877 printf("HandleElementChange(): This should never happen!\n");
8882 /* this can happen with classic bombs on walkable, changing elements */
8883 if (!CAN_CHANGE_OR_HAS_ACTION(element))
8886 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
8887 ChangeDelay[x][y] = 0;
8893 if (ChangeDelay[x][y] == 0) /* initialize element change */
8895 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
8897 if (change->can_change)
8899 ResetGfxAnimation(x, y);
8900 ResetRandomAnimationValue(x, y);
8902 if (change->pre_change_function)
8903 change->pre_change_function(x, y);
8907 ChangeDelay[x][y]--;
8909 if (ChangeDelay[x][y] != 0) /* continue element change */
8911 if (change->can_change)
8913 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8915 if (IS_ANIMATED(graphic))
8916 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8918 if (change->change_function)
8919 change->change_function(x, y);
8922 else /* finish element change */
8924 if (ChangePage[x][y] != -1) /* remember page from delayed change */
8926 page = ChangePage[x][y];
8927 ChangePage[x][y] = -1;
8929 change = &ei->change_page[page];
8932 if (IS_MOVING(x, y)) /* never change a running system ;-) */
8934 ChangeDelay[x][y] = 1; /* try change after next move step */
8935 ChangePage[x][y] = page; /* remember page to use for change */
8940 if (change->can_change)
8942 if (ChangeElement(x, y, element, page))
8944 if (change->post_change_function)
8945 change->post_change_function(x, y);
8949 if (change->has_action)
8950 ExecuteCustomElementAction(x, y, element, page);
8956 static void HandleElementChange(int x, int y, int page)
8958 int element = MovingOrBlocked2Element(x, y);
8959 struct ElementInfo *ei = &element_info[element];
8960 struct ElementChangeInfo *change = &ei->change_page[page];
8963 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
8966 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
8967 x, y, element, element_info[element].token_name);
8968 printf("HandleElementChange(): This should never happen!\n");
8973 /* this can happen with classic bombs on walkable, changing elements */
8974 if (!CAN_CHANGE(element))
8977 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
8978 ChangeDelay[x][y] = 0;
8984 if (ChangeDelay[x][y] == 0) /* initialize element change */
8986 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
8988 ResetGfxAnimation(x, y);
8989 ResetRandomAnimationValue(x, y);
8991 if (change->pre_change_function)
8992 change->pre_change_function(x, y);
8995 ChangeDelay[x][y]--;
8997 if (ChangeDelay[x][y] != 0) /* continue element change */
8999 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9001 if (IS_ANIMATED(graphic))
9002 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9004 if (change->change_function)
9005 change->change_function(x, y);
9007 else /* finish element change */
9009 if (ChangePage[x][y] != -1) /* remember page from delayed change */
9011 page = ChangePage[x][y];
9012 ChangePage[x][y] = -1;
9014 change = &ei->change_page[page];
9017 if (IS_MOVING(x, y)) /* never change a running system ;-) */
9019 ChangeDelay[x][y] = 1; /* try change after next move step */
9020 ChangePage[x][y] = page; /* remember page to use for change */
9025 if (ChangeElement(x, y, element, page))
9027 if (change->post_change_function)
9028 change->post_change_function(x, y);
9035 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
9036 int trigger_element,
9042 boolean change_done_any = FALSE;
9043 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
9046 if (!(trigger_events[trigger_element][trigger_event]))
9050 printf("::: CheckTriggeredElementChangeExt %d ... [%d, %d, %d, '%s']\n",
9051 trigger_event, recursion_loop_depth, recursion_loop_detected,
9052 recursion_loop_element, EL_NAME(recursion_loop_element));
9055 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
9057 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
9059 int element = EL_CUSTOM_START + i;
9060 boolean change_done = FALSE;
9063 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
9064 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
9067 for (p = 0; p < element_info[element].num_change_pages; p++)
9069 struct ElementChangeInfo *change = &element_info[element].change_page[p];
9071 if (change->can_change_or_has_action &&
9072 change->has_event[trigger_event] &&
9073 change->trigger_side & trigger_side &&
9074 change->trigger_player & trigger_player &&
9075 change->trigger_page & trigger_page_bits &&
9076 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
9078 change->actual_trigger_element = trigger_element;
9079 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
9080 change->actual_trigger_side = trigger_side;
9081 change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
9082 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
9084 if ((change->can_change && !change_done) || change->has_action)
9088 SCAN_PLAYFIELD(x, y)
9090 if (Feld[x][y] == element)
9092 if (change->can_change && !change_done)
9094 ChangeDelay[x][y] = 1;
9095 ChangeEvent[x][y] = trigger_event;
9097 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 if (change->can_change)
9118 change_done_any = TRUE;
9125 RECURSION_LOOP_DETECTION_END();
9127 return change_done_any;
9130 static boolean CheckElementChangeExt(int x, int y,
9132 int trigger_element,
9137 boolean change_done = FALSE;
9140 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
9141 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
9144 if (Feld[x][y] == EL_BLOCKED)
9146 Blocked2Moving(x, y, &x, &y);
9147 element = Feld[x][y];
9151 /* check if element has already changed */
9152 if (Feld[x][y] != element)
9155 /* check if element has already changed or is about to change after moving */
9156 if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
9157 Feld[x][y] != element) ||
9159 (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
9160 (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
9161 ChangePage[x][y] != -1)))
9166 printf("::: CheckElementChangeExt %d ... [%d, %d, %d, '%s']\n",
9167 trigger_event, recursion_loop_depth, recursion_loop_detected,
9168 recursion_loop_element, EL_NAME(recursion_loop_element));
9171 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
9173 for (p = 0; p < element_info[element].num_change_pages; p++)
9175 struct ElementChangeInfo *change = &element_info[element].change_page[p];
9177 /* check trigger element for all events where the element that is checked
9178 for changing interacts with a directly adjacent element -- this is
9179 different to element changes that affect other elements to change on the
9180 whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
9181 boolean check_trigger_element =
9182 (trigger_event == CE_TOUCHING_X ||
9183 trigger_event == CE_HITTING_X ||
9184 trigger_event == CE_HIT_BY_X ||
9186 /* this one was forgotten until 3.2.3 */
9187 trigger_event == CE_DIGGING_X);
9190 if (change->can_change_or_has_action &&
9191 change->has_event[trigger_event] &&
9192 change->trigger_side & trigger_side &&
9193 change->trigger_player & trigger_player &&
9194 (!check_trigger_element ||
9195 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
9197 change->actual_trigger_element = trigger_element;
9198 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
9199 change->actual_trigger_side = trigger_side;
9200 change->actual_trigger_ce_value = CustomValue[x][y];
9201 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
9203 /* special case: trigger element not at (x,y) position for some events */
9204 if (check_trigger_element)
9216 { 0, 0 }, { 0, 0 }, { 0, 0 },
9220 int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
9221 int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
9223 change->actual_trigger_ce_value = CustomValue[xx][yy];
9224 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
9227 if (change->can_change && !change_done)
9229 ChangeDelay[x][y] = 1;
9230 ChangeEvent[x][y] = trigger_event;
9232 HandleElementChange(x, y, p);
9236 #if USE_NEW_DELAYED_ACTION
9237 else if (change->has_action)
9239 ExecuteCustomElementAction(x, y, element, p);
9240 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
9243 if (change->has_action)
9245 ExecuteCustomElementAction(x, y, element, p);
9246 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
9252 RECURSION_LOOP_DETECTION_END();
9257 static void PlayPlayerSound(struct PlayerInfo *player)
9259 int jx = player->jx, jy = player->jy;
9260 int sound_element = player->artwork_element;
9261 int last_action = player->last_action_waiting;
9262 int action = player->action_waiting;
9264 if (player->is_waiting)
9266 if (action != last_action)
9267 PlayLevelSoundElementAction(jx, jy, sound_element, action);
9269 PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
9273 if (action != last_action)
9274 StopSound(element_info[sound_element].sound[last_action]);
9276 if (last_action == ACTION_SLEEPING)
9277 PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
9281 static void PlayAllPlayersSound()
9285 for (i = 0; i < MAX_PLAYERS; i++)
9286 if (stored_player[i].active)
9287 PlayPlayerSound(&stored_player[i]);
9290 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
9292 boolean last_waiting = player->is_waiting;
9293 int move_dir = player->MovDir;
9295 player->dir_waiting = move_dir;
9296 player->last_action_waiting = player->action_waiting;
9300 if (!last_waiting) /* not waiting -> waiting */
9302 player->is_waiting = TRUE;
9304 player->frame_counter_bored =
9306 game.player_boring_delay_fixed +
9307 GetSimpleRandom(game.player_boring_delay_random);
9308 player->frame_counter_sleeping =
9310 game.player_sleeping_delay_fixed +
9311 GetSimpleRandom(game.player_sleeping_delay_random);
9313 InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
9316 if (game.player_sleeping_delay_fixed +
9317 game.player_sleeping_delay_random > 0 &&
9318 player->anim_delay_counter == 0 &&
9319 player->post_delay_counter == 0 &&
9320 FrameCounter >= player->frame_counter_sleeping)
9321 player->is_sleeping = TRUE;
9322 else if (game.player_boring_delay_fixed +
9323 game.player_boring_delay_random > 0 &&
9324 FrameCounter >= player->frame_counter_bored)
9325 player->is_bored = TRUE;
9327 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
9328 player->is_bored ? ACTION_BORING :
9331 if (player->is_sleeping && player->use_murphy)
9333 /* special case for sleeping Murphy when leaning against non-free tile */
9335 if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
9336 (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
9337 !IS_MOVING(player->jx - 1, player->jy)))
9339 else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
9340 (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
9341 !IS_MOVING(player->jx + 1, player->jy)))
9342 move_dir = MV_RIGHT;
9344 player->is_sleeping = FALSE;
9346 player->dir_waiting = move_dir;
9349 if (player->is_sleeping)
9351 if (player->num_special_action_sleeping > 0)
9353 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
9355 int last_special_action = player->special_action_sleeping;
9356 int num_special_action = player->num_special_action_sleeping;
9357 int special_action =
9358 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
9359 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
9360 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
9361 last_special_action + 1 : ACTION_SLEEPING);
9362 int special_graphic =
9363 el_act_dir2img(player->artwork_element, special_action, move_dir);
9365 player->anim_delay_counter =
9366 graphic_info[special_graphic].anim_delay_fixed +
9367 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
9368 player->post_delay_counter =
9369 graphic_info[special_graphic].post_delay_fixed +
9370 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
9372 player->special_action_sleeping = special_action;
9375 if (player->anim_delay_counter > 0)
9377 player->action_waiting = player->special_action_sleeping;
9378 player->anim_delay_counter--;
9380 else if (player->post_delay_counter > 0)
9382 player->post_delay_counter--;
9386 else if (player->is_bored)
9388 if (player->num_special_action_bored > 0)
9390 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
9392 int special_action =
9393 ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
9394 int special_graphic =
9395 el_act_dir2img(player->artwork_element, special_action, move_dir);
9397 player->anim_delay_counter =
9398 graphic_info[special_graphic].anim_delay_fixed +
9399 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
9400 player->post_delay_counter =
9401 graphic_info[special_graphic].post_delay_fixed +
9402 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
9404 player->special_action_bored = special_action;
9407 if (player->anim_delay_counter > 0)
9409 player->action_waiting = player->special_action_bored;
9410 player->anim_delay_counter--;
9412 else if (player->post_delay_counter > 0)
9414 player->post_delay_counter--;
9419 else if (last_waiting) /* waiting -> not waiting */
9421 player->is_waiting = FALSE;
9422 player->is_bored = FALSE;
9423 player->is_sleeping = FALSE;
9425 player->frame_counter_bored = -1;
9426 player->frame_counter_sleeping = -1;
9428 player->anim_delay_counter = 0;
9429 player->post_delay_counter = 0;
9431 player->dir_waiting = player->MovDir;
9432 player->action_waiting = ACTION_DEFAULT;
9434 player->special_action_bored = ACTION_DEFAULT;
9435 player->special_action_sleeping = ACTION_DEFAULT;
9439 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
9441 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
9442 int left = player_action & JOY_LEFT;
9443 int right = player_action & JOY_RIGHT;
9444 int up = player_action & JOY_UP;
9445 int down = player_action & JOY_DOWN;
9446 int button1 = player_action & JOY_BUTTON_1;
9447 int button2 = player_action & JOY_BUTTON_2;
9448 int dx = (left ? -1 : right ? 1 : 0);
9449 int dy = (up ? -1 : down ? 1 : 0);
9451 if (!player->active || tape.pausing)
9457 snapped = SnapField(player, dx, dy);
9461 dropped = DropElement(player);
9463 moved = MovePlayer(player, dx, dy);
9466 if (tape.single_step && tape.recording && !tape.pausing)
9468 if (button1 || (dropped && !moved))
9470 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9471 SnapField(player, 0, 0); /* stop snapping */
9475 SetPlayerWaiting(player, FALSE);
9477 return player_action;
9481 /* no actions for this player (no input at player's configured device) */
9483 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
9484 SnapField(player, 0, 0);
9485 CheckGravityMovementWhenNotMoving(player);
9487 if (player->MovPos == 0)
9488 SetPlayerWaiting(player, TRUE);
9490 if (player->MovPos == 0) /* needed for tape.playing */
9491 player->is_moving = FALSE;
9493 player->is_dropping = FALSE;
9494 player->is_dropping_pressed = FALSE;
9495 player->drop_pressed_delay = 0;
9501 static void CheckLevelTime()
9505 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9507 if (level.native_em_level->lev->home == 0) /* all players at home */
9509 PlayerWins(local_player);
9511 AllPlayersGone = TRUE;
9513 level.native_em_level->lev->home = -1;
9516 if (level.native_em_level->ply[0]->alive == 0 &&
9517 level.native_em_level->ply[1]->alive == 0 &&
9518 level.native_em_level->ply[2]->alive == 0 &&
9519 level.native_em_level->ply[3]->alive == 0) /* all dead */
9520 AllPlayersGone = TRUE;
9523 if (TimeFrames >= FRAMES_PER_SECOND)
9528 for (i = 0; i < MAX_PLAYERS; i++)
9530 struct PlayerInfo *player = &stored_player[i];
9532 if (SHIELD_ON(player))
9534 player->shield_normal_time_left--;
9536 if (player->shield_deadly_time_left > 0)
9537 player->shield_deadly_time_left--;
9541 if (!local_player->LevelSolved && !level.use_step_counter)
9549 if (TimeLeft <= 10 && setup.time_limit)
9550 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
9552 DrawGameValue_Time(TimeLeft);
9554 if (!TimeLeft && setup.time_limit)
9556 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9557 level.native_em_level->lev->killed_out_of_time = TRUE;
9559 for (i = 0; i < MAX_PLAYERS; i++)
9560 KillPlayer(&stored_player[i]);
9563 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
9564 DrawGameValue_Time(TimePlayed);
9566 level.native_em_level->lev->time =
9567 (level.time == 0 ? TimePlayed : TimeLeft);
9570 if (tape.recording || tape.playing)
9571 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
9575 void AdvanceFrameAndPlayerCounters(int player_nr)
9579 /* advance frame counters (global frame counter and time frame counter) */
9583 /* advance player counters (counters for move delay, move animation etc.) */
9584 for (i = 0; i < MAX_PLAYERS; i++)
9586 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
9587 int move_delay_value = stored_player[i].move_delay_value;
9588 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
9590 if (!advance_player_counters) /* not all players may be affected */
9593 #if USE_NEW_PLAYER_ANIM
9594 if (move_frames == 0) /* less than one move per game frame */
9596 int stepsize = TILEX / move_delay_value;
9597 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
9598 int count = (stored_player[i].is_moving ?
9599 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
9601 if (count % delay == 0)
9606 stored_player[i].Frame += move_frames;
9608 if (stored_player[i].MovPos != 0)
9609 stored_player[i].StepFrame += move_frames;
9611 if (stored_player[i].move_delay > 0)
9612 stored_player[i].move_delay--;
9614 /* due to bugs in previous versions, counter must count up, not down */
9615 if (stored_player[i].push_delay != -1)
9616 stored_player[i].push_delay++;
9618 if (stored_player[i].drop_delay > 0)
9619 stored_player[i].drop_delay--;
9621 if (stored_player[i].is_dropping_pressed)
9622 stored_player[i].drop_pressed_delay++;
9626 void StartGameActions(boolean init_network_game, boolean record_tape,
9629 unsigned long new_random_seed = InitRND(random_seed);
9632 TapeStartRecording(new_random_seed);
9634 #if defined(NETWORK_AVALIABLE)
9635 if (init_network_game)
9637 SendToServer_StartPlaying();
9648 static unsigned long game_frame_delay = 0;
9649 unsigned long game_frame_delay_value;
9650 byte *recorded_player_action;
9651 byte summarized_player_action = 0;
9652 byte tape_action[MAX_PLAYERS];
9655 /* detect endless loops, caused by custom element programming */
9656 if (recursion_loop_detected && recursion_loop_depth == 0)
9658 char *message = getStringCat3("Internal Error ! Element ",
9659 EL_NAME(recursion_loop_element),
9660 " caused endless loop ! Quit the game ?");
9662 Error(ERR_WARN, "element '%s' caused endless loop in game engine",
9663 EL_NAME(recursion_loop_element));
9665 RequestQuitGameExt(FALSE, level_editor_test_game, message);
9667 recursion_loop_detected = FALSE; /* if game should be continued */
9674 if (game.restart_level)
9675 StartGameActions(options.network, setup.autorecord, NEW_RANDOMIZE);
9677 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9679 if (level.native_em_level->lev->home == 0) /* all players at home */
9681 PlayerWins(local_player);
9683 AllPlayersGone = TRUE;
9685 level.native_em_level->lev->home = -1;
9688 if (level.native_em_level->ply[0]->alive == 0 &&
9689 level.native_em_level->ply[1]->alive == 0 &&
9690 level.native_em_level->ply[2]->alive == 0 &&
9691 level.native_em_level->ply[3]->alive == 0) /* all dead */
9692 AllPlayersGone = TRUE;
9695 if (local_player->LevelSolved)
9698 if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
9701 if (game_status != GAME_MODE_PLAYING) /* status might have changed */
9704 game_frame_delay_value =
9705 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
9707 if (tape.playing && tape.warp_forward && !tape.pausing)
9708 game_frame_delay_value = 0;
9710 /* ---------- main game synchronization point ---------- */
9712 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
9714 if (network_playing && !network_player_action_received)
9716 /* try to get network player actions in time */
9718 #if defined(NETWORK_AVALIABLE)
9719 /* last chance to get network player actions without main loop delay */
9723 /* game was quit by network peer */
9724 if (game_status != GAME_MODE_PLAYING)
9727 if (!network_player_action_received)
9728 return; /* failed to get network player actions in time */
9730 /* do not yet reset "network_player_action_received" (for tape.pausing) */
9736 /* at this point we know that we really continue executing the game */
9738 network_player_action_received = FALSE;
9740 /* when playing tape, read previously recorded player input from tape data */
9741 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
9744 /* TapePlayAction() may return NULL when toggling to "pause before death" */
9749 if (tape.set_centered_player)
9751 game.centered_player_nr_next = tape.centered_player_nr_next;
9752 game.set_centered_player = TRUE;
9755 for (i = 0; i < MAX_PLAYERS; i++)
9757 summarized_player_action |= stored_player[i].action;
9759 if (!network_playing)
9760 stored_player[i].effective_action = stored_player[i].action;
9763 #if defined(NETWORK_AVALIABLE)
9764 if (network_playing)
9765 SendToServer_MovePlayer(summarized_player_action);
9768 if (!options.network && !setup.team_mode)
9769 local_player->effective_action = summarized_player_action;
9771 if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
9773 for (i = 0; i < MAX_PLAYERS; i++)
9774 stored_player[i].effective_action =
9775 (i == game.centered_player_nr ? summarized_player_action : 0);
9778 if (recorded_player_action != NULL)
9779 for (i = 0; i < MAX_PLAYERS; i++)
9780 stored_player[i].effective_action = recorded_player_action[i];
9782 for (i = 0; i < MAX_PLAYERS; i++)
9784 tape_action[i] = stored_player[i].effective_action;
9786 /* (this can only happen in the R'n'D game engine) */
9787 if (tape.recording && tape_action[i] && !tape.player_participates[i])
9788 tape.player_participates[i] = TRUE; /* player just appeared from CE */
9791 /* only record actions from input devices, but not programmed actions */
9793 TapeRecordAction(tape_action);
9795 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9797 GameActions_EM_Main();
9805 void GameActions_EM_Main()
9807 byte effective_action[MAX_PLAYERS];
9808 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
9811 for (i = 0; i < MAX_PLAYERS; i++)
9812 effective_action[i] = stored_player[i].effective_action;
9814 GameActions_EM(effective_action, warp_mode);
9818 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
9821 void GameActions_RND()
9823 int magic_wall_x = 0, magic_wall_y = 0;
9824 int i, x, y, element, graphic;
9826 InitPlayfieldScanModeVars();
9828 #if USE_ONE_MORE_CHANGE_PER_FRAME
9829 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
9831 SCAN_PLAYFIELD(x, y)
9833 ChangeCount[x][y] = 0;
9834 ChangeEvent[x][y] = -1;
9839 if (game.set_centered_player)
9841 boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
9843 /* switching to "all players" only possible if all players fit to screen */
9844 if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
9846 game.centered_player_nr_next = game.centered_player_nr;
9847 game.set_centered_player = FALSE;
9850 /* do not switch focus to non-existing (or non-active) player */
9851 if (game.centered_player_nr_next >= 0 &&
9852 !stored_player[game.centered_player_nr_next].active)
9854 game.centered_player_nr_next = game.centered_player_nr;
9855 game.set_centered_player = FALSE;
9859 if (game.set_centered_player &&
9860 ScreenMovPos == 0) /* screen currently aligned at tile position */
9864 if (game.centered_player_nr_next == -1)
9866 setScreenCenteredToAllPlayers(&sx, &sy);
9870 sx = stored_player[game.centered_player_nr_next].jx;
9871 sy = stored_player[game.centered_player_nr_next].jy;
9874 game.centered_player_nr = game.centered_player_nr_next;
9875 game.set_centered_player = FALSE;
9877 DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
9878 DrawGameDoorValues();
9881 for (i = 0; i < MAX_PLAYERS; i++)
9883 int actual_player_action = stored_player[i].effective_action;
9886 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
9887 - rnd_equinox_tetrachloride 048
9888 - rnd_equinox_tetrachloride_ii 096
9889 - rnd_emanuel_schmieg 002
9890 - doctor_sloan_ww 001, 020
9892 if (stored_player[i].MovPos == 0)
9893 CheckGravityMovement(&stored_player[i]);
9896 /* overwrite programmed action with tape action */
9897 if (stored_player[i].programmed_action)
9898 actual_player_action = stored_player[i].programmed_action;
9900 PlayerActions(&stored_player[i], actual_player_action);
9902 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
9905 ScrollScreen(NULL, SCROLL_GO_ON);
9907 /* for backwards compatibility, the following code emulates a fixed bug that
9908 occured when pushing elements (causing elements that just made their last
9909 pushing step to already (if possible) make their first falling step in the
9910 same game frame, which is bad); this code is also needed to use the famous
9911 "spring push bug" which is used in older levels and might be wanted to be
9912 used also in newer levels, but in this case the buggy pushing code is only
9913 affecting the "spring" element and no other elements */
9915 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
9917 for (i = 0; i < MAX_PLAYERS; i++)
9919 struct PlayerInfo *player = &stored_player[i];
9923 if (player->active && player->is_pushing && player->is_moving &&
9925 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
9926 Feld[x][y] == EL_SPRING))
9928 ContinueMoving(x, y);
9930 /* continue moving after pushing (this is actually a bug) */
9931 if (!IS_MOVING(x, y))
9939 SCAN_PLAYFIELD(x, y)
9941 ChangeCount[x][y] = 0;
9942 ChangeEvent[x][y] = -1;
9944 /* this must be handled before main playfield loop */
9945 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
9948 if (MovDelay[x][y] <= 0)
9952 #if USE_NEW_SNAP_DELAY
9953 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
9956 if (MovDelay[x][y] <= 0)
9959 DrawLevelField(x, y);
9961 TestIfElementTouchesCustomElement(x, y); /* for empty space */
9967 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
9969 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
9970 printf("GameActions(): This should never happen!\n");
9972 ChangePage[x][y] = -1;
9977 if (WasJustMoving[x][y] > 0)
9978 WasJustMoving[x][y]--;
9979 if (WasJustFalling[x][y] > 0)
9980 WasJustFalling[x][y]--;
9981 if (CheckCollision[x][y] > 0)
9982 CheckCollision[x][y]--;
9983 if (CheckImpact[x][y] > 0)
9984 CheckImpact[x][y]--;
9988 /* reset finished pushing action (not done in ContinueMoving() to allow
9989 continuous pushing animation for elements with zero push delay) */
9990 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
9992 ResetGfxAnimation(x, y);
9993 DrawLevelField(x, y);
9997 if (IS_BLOCKED(x, y))
10001 Blocked2Moving(x, y, &oldx, &oldy);
10002 if (!IS_MOVING(oldx, oldy))
10004 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
10005 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
10006 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
10007 printf("GameActions(): This should never happen!\n");
10013 SCAN_PLAYFIELD(x, y)
10015 element = Feld[x][y];
10016 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10018 ResetGfxFrame(x, y, TRUE);
10020 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
10021 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
10022 ResetRandomAnimationValue(x, y);
10024 SetRandomAnimationValue(x, y);
10026 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
10028 if (IS_INACTIVE(element))
10030 if (IS_ANIMATED(graphic))
10031 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10036 /* this may take place after moving, so 'element' may have changed */
10037 if (IS_CHANGING(x, y) &&
10038 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
10040 int page = element_info[element].event_page_nr[CE_DELAY];
10043 HandleElementChange(x, y, page);
10045 if (CAN_CHANGE(element))
10046 HandleElementChange(x, y, page);
10048 if (HAS_ACTION(element))
10049 ExecuteCustomElementAction(x, y, element, page);
10052 element = Feld[x][y];
10053 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10056 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
10060 element = Feld[x][y];
10061 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10063 if (IS_ANIMATED(graphic) &&
10064 !IS_MOVING(x, y) &&
10066 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10068 if (IS_GEM(element) || element == EL_SP_INFOTRON)
10069 DrawTwinkleOnField(x, y);
10071 else if ((element == EL_ACID ||
10072 element == EL_EXIT_OPEN ||
10073 element == EL_EM_EXIT_OPEN ||
10074 element == EL_SP_EXIT_OPEN ||
10075 element == EL_STEEL_EXIT_OPEN ||
10076 element == EL_EM_STEEL_EXIT_OPEN ||
10077 element == EL_SP_TERMINAL ||
10078 element == EL_SP_TERMINAL_ACTIVE ||
10079 element == EL_EXTRA_TIME ||
10080 element == EL_SHIELD_NORMAL ||
10081 element == EL_SHIELD_DEADLY) &&
10082 IS_ANIMATED(graphic))
10083 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10084 else if (IS_MOVING(x, y))
10085 ContinueMoving(x, y);
10086 else if (IS_ACTIVE_BOMB(element))
10087 CheckDynamite(x, y);
10088 else if (element == EL_AMOEBA_GROWING)
10089 AmoebeWaechst(x, y);
10090 else if (element == EL_AMOEBA_SHRINKING)
10091 AmoebaDisappearing(x, y);
10093 #if !USE_NEW_AMOEBA_CODE
10094 else if (IS_AMOEBALIVE(element))
10095 AmoebeAbleger(x, y);
10098 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
10100 else if (element == EL_EXIT_CLOSED)
10102 else if (element == EL_EM_EXIT_CLOSED)
10104 else if (element == EL_STEEL_EXIT_CLOSED)
10105 CheckExitSteel(x, y);
10106 else if (element == EL_EM_STEEL_EXIT_CLOSED)
10107 CheckExitSteelEM(x, y);
10108 else if (element == EL_SP_EXIT_CLOSED)
10110 else if (element == EL_EXPANDABLE_WALL_GROWING ||
10111 element == EL_EXPANDABLE_STEELWALL_GROWING)
10112 MauerWaechst(x, y);
10113 else if (element == EL_EXPANDABLE_WALL ||
10114 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
10115 element == EL_EXPANDABLE_WALL_VERTICAL ||
10116 element == EL_EXPANDABLE_WALL_ANY ||
10117 element == EL_BD_EXPANDABLE_WALL)
10118 MauerAbleger(x, y);
10119 else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
10120 element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
10121 element == EL_EXPANDABLE_STEELWALL_ANY)
10122 MauerAblegerStahl(x, y);
10123 else if (element == EL_FLAMES)
10124 CheckForDragon(x, y);
10125 else if (element == EL_EXPLOSION)
10126 ; /* drawing of correct explosion animation is handled separately */
10127 else if (element == EL_ELEMENT_SNAPPING ||
10128 element == EL_DIAGONAL_SHRINKING ||
10129 element == EL_DIAGONAL_GROWING)
10131 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
10133 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10135 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
10136 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10138 if (IS_BELT_ACTIVE(element))
10139 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
10141 if (game.magic_wall_active)
10143 int jx = local_player->jx, jy = local_player->jy;
10145 /* play the element sound at the position nearest to the player */
10146 if ((element == EL_MAGIC_WALL_FULL ||
10147 element == EL_MAGIC_WALL_ACTIVE ||
10148 element == EL_MAGIC_WALL_EMPTYING ||
10149 element == EL_BD_MAGIC_WALL_FULL ||
10150 element == EL_BD_MAGIC_WALL_ACTIVE ||
10151 element == EL_BD_MAGIC_WALL_EMPTYING ||
10152 element == EL_DC_MAGIC_WALL_FULL ||
10153 element == EL_DC_MAGIC_WALL_ACTIVE ||
10154 element == EL_DC_MAGIC_WALL_EMPTYING) &&
10155 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
10163 #if USE_NEW_AMOEBA_CODE
10164 /* new experimental amoeba growth stuff */
10165 if (!(FrameCounter % 8))
10167 static unsigned long random = 1684108901;
10169 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
10171 x = RND(lev_fieldx);
10172 y = RND(lev_fieldy);
10173 element = Feld[x][y];
10175 if (!IS_PLAYER(x,y) &&
10176 (element == EL_EMPTY ||
10177 CAN_GROW_INTO(element) ||
10178 element == EL_QUICKSAND_EMPTY ||
10179 element == EL_QUICKSAND_FAST_EMPTY ||
10180 element == EL_ACID_SPLASH_LEFT ||
10181 element == EL_ACID_SPLASH_RIGHT))
10183 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
10184 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
10185 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
10186 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
10187 Feld[x][y] = EL_AMOEBA_DROP;
10190 random = random * 129 + 1;
10196 if (game.explosions_delayed)
10199 game.explosions_delayed = FALSE;
10201 SCAN_PLAYFIELD(x, y)
10203 element = Feld[x][y];
10205 if (ExplodeField[x][y])
10206 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
10207 else if (element == EL_EXPLOSION)
10208 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
10210 ExplodeField[x][y] = EX_TYPE_NONE;
10213 game.explosions_delayed = TRUE;
10216 if (game.magic_wall_active)
10218 if (!(game.magic_wall_time_left % 4))
10220 int element = Feld[magic_wall_x][magic_wall_y];
10222 if (element == EL_BD_MAGIC_WALL_FULL ||
10223 element == EL_BD_MAGIC_WALL_ACTIVE ||
10224 element == EL_BD_MAGIC_WALL_EMPTYING)
10225 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
10226 else if (element == EL_DC_MAGIC_WALL_FULL ||
10227 element == EL_DC_MAGIC_WALL_ACTIVE ||
10228 element == EL_DC_MAGIC_WALL_EMPTYING)
10229 PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
10231 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
10234 if (game.magic_wall_time_left > 0)
10236 game.magic_wall_time_left--;
10237 if (!game.magic_wall_time_left)
10239 SCAN_PLAYFIELD(x, y)
10241 element = Feld[x][y];
10243 if (element == EL_MAGIC_WALL_ACTIVE ||
10244 element == EL_MAGIC_WALL_FULL)
10246 Feld[x][y] = EL_MAGIC_WALL_DEAD;
10247 DrawLevelField(x, y);
10249 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
10250 element == EL_BD_MAGIC_WALL_FULL)
10252 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
10253 DrawLevelField(x, y);
10255 else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
10256 element == EL_DC_MAGIC_WALL_FULL)
10258 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
10259 DrawLevelField(x, y);
10263 game.magic_wall_active = FALSE;
10268 if (game.light_time_left > 0)
10270 game.light_time_left--;
10272 if (game.light_time_left == 0)
10273 RedrawAllLightSwitchesAndInvisibleElements();
10276 if (game.timegate_time_left > 0)
10278 game.timegate_time_left--;
10280 if (game.timegate_time_left == 0)
10281 CloseAllOpenTimegates();
10284 if (game.lenses_time_left > 0)
10286 game.lenses_time_left--;
10288 if (game.lenses_time_left == 0)
10289 RedrawAllInvisibleElementsForLenses();
10292 if (game.magnify_time_left > 0)
10294 game.magnify_time_left--;
10296 if (game.magnify_time_left == 0)
10297 RedrawAllInvisibleElementsForMagnifier();
10300 for (i = 0; i < MAX_PLAYERS; i++)
10302 struct PlayerInfo *player = &stored_player[i];
10304 if (SHIELD_ON(player))
10306 if (player->shield_deadly_time_left)
10307 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
10308 else if (player->shield_normal_time_left)
10309 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
10316 PlayAllPlayersSound();
10318 if (options.debug) /* calculate frames per second */
10320 static unsigned long fps_counter = 0;
10321 static int fps_frames = 0;
10322 unsigned long fps_delay_ms = Counter() - fps_counter;
10326 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
10328 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
10331 fps_counter = Counter();
10334 redraw_mask |= REDRAW_FPS;
10337 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
10339 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
10341 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
10343 local_player->show_envelope = 0;
10346 /* use random number generator in every frame to make it less predictable */
10347 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
10351 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
10353 int min_x = x, min_y = y, max_x = x, max_y = y;
10356 for (i = 0; i < MAX_PLAYERS; i++)
10358 int jx = stored_player[i].jx, jy = stored_player[i].jy;
10360 if (!stored_player[i].active || &stored_player[i] == player)
10363 min_x = MIN(min_x, jx);
10364 min_y = MIN(min_y, jy);
10365 max_x = MAX(max_x, jx);
10366 max_y = MAX(max_y, jy);
10369 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
10372 static boolean AllPlayersInVisibleScreen()
10376 for (i = 0; i < MAX_PLAYERS; i++)
10378 int jx = stored_player[i].jx, jy = stored_player[i].jy;
10380 if (!stored_player[i].active)
10383 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
10390 void ScrollLevel(int dx, int dy)
10392 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
10395 BlitBitmap(drawto_field, drawto_field,
10396 FX + TILEX * (dx == -1) - softscroll_offset,
10397 FY + TILEY * (dy == -1) - softscroll_offset,
10398 SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
10399 SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
10400 FX + TILEX * (dx == 1) - softscroll_offset,
10401 FY + TILEY * (dy == 1) - softscroll_offset);
10405 x = (dx == 1 ? BX1 : BX2);
10406 for (y = BY1; y <= BY2; y++)
10407 DrawScreenField(x, y);
10412 y = (dy == 1 ? BY1 : BY2);
10413 for (x = BX1; x <= BX2; x++)
10414 DrawScreenField(x, y);
10417 redraw_mask |= REDRAW_FIELD;
10420 static boolean canFallDown(struct PlayerInfo *player)
10422 int jx = player->jx, jy = player->jy;
10424 return (IN_LEV_FIELD(jx, jy + 1) &&
10425 (IS_FREE(jx, jy + 1) ||
10426 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
10427 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
10428 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
10431 static boolean canPassField(int x, int y, int move_dir)
10433 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
10434 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
10435 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
10436 int nextx = x + dx;
10437 int nexty = y + dy;
10438 int element = Feld[x][y];
10440 return (IS_PASSABLE_FROM(element, opposite_dir) &&
10441 !CAN_MOVE(element) &&
10442 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
10443 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
10444 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
10447 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
10449 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
10450 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
10451 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
10455 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
10456 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
10457 (IS_DIGGABLE(Feld[newx][newy]) ||
10458 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
10459 canPassField(newx, newy, move_dir)));
10462 static void CheckGravityMovement(struct PlayerInfo *player)
10464 #if USE_PLAYER_GRAVITY
10465 if (player->gravity && !player->programmed_action)
10467 if (game.gravity && !player->programmed_action)
10470 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
10471 int move_dir_vertical = player->effective_action & MV_VERTICAL;
10472 boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
10473 int jx = player->jx, jy = player->jy;
10474 boolean player_is_moving_to_valid_field =
10475 (!player_is_snapping &&
10476 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
10477 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
10478 boolean player_can_fall_down = canFallDown(player);
10480 if (player_can_fall_down &&
10481 !player_is_moving_to_valid_field)
10482 player->programmed_action = MV_DOWN;
10486 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
10488 return CheckGravityMovement(player);
10490 #if USE_PLAYER_GRAVITY
10491 if (player->gravity && !player->programmed_action)
10493 if (game.gravity && !player->programmed_action)
10496 int jx = player->jx, jy = player->jy;
10497 boolean field_under_player_is_free =
10498 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
10499 boolean player_is_standing_on_valid_field =
10500 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
10501 (IS_WALKABLE(Feld[jx][jy]) &&
10502 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
10504 if (field_under_player_is_free && !player_is_standing_on_valid_field)
10505 player->programmed_action = MV_DOWN;
10510 MovePlayerOneStep()
10511 -----------------------------------------------------------------------------
10512 dx, dy: direction (non-diagonal) to try to move the player to
10513 real_dx, real_dy: direction as read from input device (can be diagonal)
10516 boolean MovePlayerOneStep(struct PlayerInfo *player,
10517 int dx, int dy, int real_dx, int real_dy)
10519 int jx = player->jx, jy = player->jy;
10520 int new_jx = jx + dx, new_jy = jy + dy;
10521 #if !USE_FIXED_DONT_RUN_INTO
10525 boolean player_can_move = !player->cannot_move;
10527 if (!player->active || (!dx && !dy))
10528 return MP_NO_ACTION;
10530 player->MovDir = (dx < 0 ? MV_LEFT :
10531 dx > 0 ? MV_RIGHT :
10533 dy > 0 ? MV_DOWN : MV_NONE);
10535 if (!IN_LEV_FIELD(new_jx, new_jy))
10536 return MP_NO_ACTION;
10538 if (!player_can_move)
10540 if (player->MovPos == 0)
10542 player->is_moving = FALSE;
10543 player->is_digging = FALSE;
10544 player->is_collecting = FALSE;
10545 player->is_snapping = FALSE;
10546 player->is_pushing = FALSE;
10551 if (!options.network && game.centered_player_nr == -1 &&
10552 !AllPlayersInSight(player, new_jx, new_jy))
10553 return MP_NO_ACTION;
10555 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
10556 return MP_NO_ACTION;
10559 #if !USE_FIXED_DONT_RUN_INTO
10560 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
10562 /* (moved to DigField()) */
10563 if (player_can_move && DONT_RUN_INTO(element))
10565 if (element == EL_ACID && dx == 0 && dy == 1)
10567 SplashAcid(new_jx, new_jy);
10568 Feld[jx][jy] = EL_PLAYER_1;
10569 InitMovingField(jx, jy, MV_DOWN);
10570 Store[jx][jy] = EL_ACID;
10571 ContinueMoving(jx, jy);
10572 BuryPlayer(player);
10575 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
10581 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
10582 if (can_move != MP_MOVING)
10585 /* check if DigField() has caused relocation of the player */
10586 if (player->jx != jx || player->jy != jy)
10587 return MP_NO_ACTION; /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
10589 StorePlayer[jx][jy] = 0;
10590 player->last_jx = jx;
10591 player->last_jy = jy;
10592 player->jx = new_jx;
10593 player->jy = new_jy;
10594 StorePlayer[new_jx][new_jy] = player->element_nr;
10596 if (player->move_delay_value_next != -1)
10598 player->move_delay_value = player->move_delay_value_next;
10599 player->move_delay_value_next = -1;
10603 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
10605 player->step_counter++;
10607 PlayerVisit[jx][jy] = FrameCounter;
10609 #if USE_UFAST_PLAYER_EXIT_BUGFIX
10610 player->is_moving = TRUE;
10614 /* should better be called in MovePlayer(), but this breaks some tapes */
10615 ScrollPlayer(player, SCROLL_INIT);
10621 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
10623 int jx = player->jx, jy = player->jy;
10624 int old_jx = jx, old_jy = jy;
10625 int moved = MP_NO_ACTION;
10627 if (!player->active)
10632 if (player->MovPos == 0)
10634 player->is_moving = FALSE;
10635 player->is_digging = FALSE;
10636 player->is_collecting = FALSE;
10637 player->is_snapping = FALSE;
10638 player->is_pushing = FALSE;
10644 if (player->move_delay > 0)
10647 player->move_delay = -1; /* set to "uninitialized" value */
10649 /* store if player is automatically moved to next field */
10650 player->is_auto_moving = (player->programmed_action != MV_NONE);
10652 /* remove the last programmed player action */
10653 player->programmed_action = 0;
10655 if (player->MovPos)
10657 /* should only happen if pre-1.2 tape recordings are played */
10658 /* this is only for backward compatibility */
10660 int original_move_delay_value = player->move_delay_value;
10663 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
10667 /* scroll remaining steps with finest movement resolution */
10668 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
10670 while (player->MovPos)
10672 ScrollPlayer(player, SCROLL_GO_ON);
10673 ScrollScreen(NULL, SCROLL_GO_ON);
10675 AdvanceFrameAndPlayerCounters(player->index_nr);
10681 player->move_delay_value = original_move_delay_value;
10684 player->is_active = FALSE;
10686 if (player->last_move_dir & MV_HORIZONTAL)
10688 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
10689 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
10693 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
10694 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
10697 #if USE_FIXED_BORDER_RUNNING_GFX
10698 if (!moved && !player->is_active)
10700 player->is_moving = FALSE;
10701 player->is_digging = FALSE;
10702 player->is_collecting = FALSE;
10703 player->is_snapping = FALSE;
10704 player->is_pushing = FALSE;
10712 if (moved & MP_MOVING && !ScreenMovPos &&
10713 (player->index_nr == game.centered_player_nr ||
10714 game.centered_player_nr == -1))
10716 if (moved & MP_MOVING && !ScreenMovPos &&
10717 (player == local_player || !options.network))
10720 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
10721 int offset = (setup.scroll_delay ? 3 : 0);
10723 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
10725 /* actual player has left the screen -- scroll in that direction */
10726 if (jx != old_jx) /* player has moved horizontally */
10727 scroll_x += (jx - old_jx);
10728 else /* player has moved vertically */
10729 scroll_y += (jy - old_jy);
10733 if (jx != old_jx) /* player has moved horizontally */
10735 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
10736 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
10737 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
10739 /* don't scroll over playfield boundaries */
10740 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
10741 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
10743 /* don't scroll more than one field at a time */
10744 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
10746 /* don't scroll against the player's moving direction */
10747 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
10748 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
10749 scroll_x = old_scroll_x;
10751 else /* player has moved vertically */
10753 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
10754 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
10755 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
10757 /* don't scroll over playfield boundaries */
10758 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
10759 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
10761 /* don't scroll more than one field at a time */
10762 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
10764 /* don't scroll against the player's moving direction */
10765 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
10766 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
10767 scroll_y = old_scroll_y;
10771 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
10774 if (!options.network && game.centered_player_nr == -1 &&
10775 !AllPlayersInVisibleScreen())
10777 scroll_x = old_scroll_x;
10778 scroll_y = old_scroll_y;
10782 if (!options.network && !AllPlayersInVisibleScreen())
10784 scroll_x = old_scroll_x;
10785 scroll_y = old_scroll_y;
10790 ScrollScreen(player, SCROLL_INIT);
10791 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
10796 player->StepFrame = 0;
10798 if (moved & MP_MOVING)
10800 if (old_jx != jx && old_jy == jy)
10801 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
10802 else if (old_jx == jx && old_jy != jy)
10803 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
10805 DrawLevelField(jx, jy); /* for "crumbled sand" */
10807 player->last_move_dir = player->MovDir;
10808 player->is_moving = TRUE;
10809 player->is_snapping = FALSE;
10810 player->is_switching = FALSE;
10811 player->is_dropping = FALSE;
10812 player->is_dropping_pressed = FALSE;
10813 player->drop_pressed_delay = 0;
10816 /* should better be called here than above, but this breaks some tapes */
10817 ScrollPlayer(player, SCROLL_INIT);
10822 CheckGravityMovementWhenNotMoving(player);
10824 player->is_moving = FALSE;
10826 /* at this point, the player is allowed to move, but cannot move right now
10827 (e.g. because of something blocking the way) -- ensure that the player
10828 is also allowed to move in the next frame (in old versions before 3.1.1,
10829 the player was forced to wait again for eight frames before next try) */
10831 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
10832 player->move_delay = 0; /* allow direct movement in the next frame */
10835 if (player->move_delay == -1) /* not yet initialized by DigField() */
10836 player->move_delay = player->move_delay_value;
10838 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10840 TestIfPlayerTouchesBadThing(jx, jy);
10841 TestIfPlayerTouchesCustomElement(jx, jy);
10844 if (!player->active)
10845 RemovePlayer(player);
10850 void ScrollPlayer(struct PlayerInfo *player, int mode)
10852 int jx = player->jx, jy = player->jy;
10853 int last_jx = player->last_jx, last_jy = player->last_jy;
10854 int move_stepsize = TILEX / player->move_delay_value;
10856 #if USE_NEW_PLAYER_SPEED
10857 if (!player->active)
10860 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
10863 if (!player->active || player->MovPos == 0)
10867 if (mode == SCROLL_INIT)
10869 player->actual_frame_counter = FrameCounter;
10870 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10872 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
10873 Feld[last_jx][last_jy] == EL_EMPTY)
10875 int last_field_block_delay = 0; /* start with no blocking at all */
10876 int block_delay_adjustment = player->block_delay_adjustment;
10878 /* if player blocks last field, add delay for exactly one move */
10879 if (player->block_last_field)
10881 last_field_block_delay += player->move_delay_value;
10883 /* when blocking enabled, prevent moving up despite gravity */
10884 #if USE_PLAYER_GRAVITY
10885 if (player->gravity && player->MovDir == MV_UP)
10886 block_delay_adjustment = -1;
10888 if (game.gravity && player->MovDir == MV_UP)
10889 block_delay_adjustment = -1;
10893 /* add block delay adjustment (also possible when not blocking) */
10894 last_field_block_delay += block_delay_adjustment;
10896 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
10897 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
10900 #if USE_NEW_PLAYER_SPEED
10901 if (player->MovPos != 0) /* player has not yet reached destination */
10907 else if (!FrameReached(&player->actual_frame_counter, 1))
10910 #if USE_NEW_PLAYER_SPEED
10911 if (player->MovPos != 0)
10913 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
10914 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10916 /* before DrawPlayer() to draw correct player graphic for this case */
10917 if (player->MovPos == 0)
10918 CheckGravityMovement(player);
10921 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
10922 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10924 /* before DrawPlayer() to draw correct player graphic for this case */
10925 if (player->MovPos == 0)
10926 CheckGravityMovement(player);
10929 if (player->MovPos == 0) /* player reached destination field */
10931 if (player->move_delay_reset_counter > 0)
10933 player->move_delay_reset_counter--;
10935 if (player->move_delay_reset_counter == 0)
10937 /* continue with normal speed after quickly moving through gate */
10938 HALVE_PLAYER_SPEED(player);
10940 /* be able to make the next move without delay */
10941 player->move_delay = 0;
10945 player->last_jx = jx;
10946 player->last_jy = jy;
10948 if (Feld[jx][jy] == EL_EXIT_OPEN ||
10949 Feld[jx][jy] == EL_EM_EXIT_OPEN ||
10950 Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
10951 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
10952 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
10953 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
10955 DrawPlayer(player); /* needed here only to cleanup last field */
10956 RemovePlayer(player);
10958 if (local_player->friends_still_needed == 0 ||
10959 IS_SP_ELEMENT(Feld[jx][jy]))
10960 PlayerWins(player);
10963 /* this breaks one level: "machine", level 000 */
10965 int move_direction = player->MovDir;
10966 int enter_side = MV_DIR_OPPOSITE(move_direction);
10967 int leave_side = move_direction;
10968 int old_jx = last_jx;
10969 int old_jy = last_jy;
10970 int old_element = Feld[old_jx][old_jy];
10971 int new_element = Feld[jx][jy];
10973 if (IS_CUSTOM_ELEMENT(old_element))
10974 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
10976 player->index_bit, leave_side);
10978 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
10979 CE_PLAYER_LEAVES_X,
10980 player->index_bit, leave_side);
10982 if (IS_CUSTOM_ELEMENT(new_element))
10983 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
10984 player->index_bit, enter_side);
10986 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
10987 CE_PLAYER_ENTERS_X,
10988 player->index_bit, enter_side);
10990 CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
10991 CE_MOVE_OF_X, move_direction);
10994 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10996 TestIfPlayerTouchesBadThing(jx, jy);
10997 TestIfPlayerTouchesCustomElement(jx, jy);
10999 /* needed because pushed element has not yet reached its destination,
11000 so it would trigger a change event at its previous field location */
11001 if (!player->is_pushing)
11002 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
11004 if (!player->active)
11005 RemovePlayer(player);
11008 if (!local_player->LevelSolved && level.use_step_counter)
11018 if (TimeLeft <= 10 && setup.time_limit)
11019 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11021 DrawGameValue_Time(TimeLeft);
11023 if (!TimeLeft && setup.time_limit)
11024 for (i = 0; i < MAX_PLAYERS; i++)
11025 KillPlayer(&stored_player[i]);
11027 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
11028 DrawGameValue_Time(TimePlayed);
11031 if (tape.single_step && tape.recording && !tape.pausing &&
11032 !player->programmed_action)
11033 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
11037 void ScrollScreen(struct PlayerInfo *player, int mode)
11039 static unsigned long screen_frame_counter = 0;
11041 if (mode == SCROLL_INIT)
11043 /* set scrolling step size according to actual player's moving speed */
11044 ScrollStepSize = TILEX / player->move_delay_value;
11046 screen_frame_counter = FrameCounter;
11047 ScreenMovDir = player->MovDir;
11048 ScreenMovPos = player->MovPos;
11049 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
11052 else if (!FrameReached(&screen_frame_counter, 1))
11057 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
11058 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
11059 redraw_mask |= REDRAW_FIELD;
11062 ScreenMovDir = MV_NONE;
11065 void TestIfPlayerTouchesCustomElement(int x, int y)
11067 static int xy[4][2] =
11074 static int trigger_sides[4][2] =
11076 /* center side border side */
11077 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
11078 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
11079 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
11080 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
11082 static int touch_dir[4] =
11084 MV_LEFT | MV_RIGHT,
11089 int center_element = Feld[x][y]; /* should always be non-moving! */
11092 for (i = 0; i < NUM_DIRECTIONS; i++)
11094 int xx = x + xy[i][0];
11095 int yy = y + xy[i][1];
11096 int center_side = trigger_sides[i][0];
11097 int border_side = trigger_sides[i][1];
11098 int border_element;
11100 if (!IN_LEV_FIELD(xx, yy))
11103 if (IS_PLAYER(x, y))
11105 struct PlayerInfo *player = PLAYERINFO(x, y);
11107 if (game.engine_version < VERSION_IDENT(3,0,7,0))
11108 border_element = Feld[xx][yy]; /* may be moving! */
11109 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
11110 border_element = Feld[xx][yy];
11111 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
11112 border_element = MovingOrBlocked2Element(xx, yy);
11114 continue; /* center and border element do not touch */
11116 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
11117 player->index_bit, border_side);
11118 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
11119 CE_PLAYER_TOUCHES_X,
11120 player->index_bit, border_side);
11122 else if (IS_PLAYER(xx, yy))
11124 struct PlayerInfo *player = PLAYERINFO(xx, yy);
11126 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
11128 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
11129 continue; /* center and border element do not touch */
11132 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
11133 player->index_bit, center_side);
11134 CheckTriggeredElementChangeByPlayer(x, y, center_element,
11135 CE_PLAYER_TOUCHES_X,
11136 player->index_bit, center_side);
11142 #if USE_ELEMENT_TOUCHING_BUGFIX
11144 void TestIfElementTouchesCustomElement(int x, int y)
11146 static int xy[4][2] =
11153 static int trigger_sides[4][2] =
11155 /* center side border side */
11156 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
11157 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
11158 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
11159 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
11161 static int touch_dir[4] =
11163 MV_LEFT | MV_RIGHT,
11168 boolean change_center_element = FALSE;
11169 int center_element = Feld[x][y]; /* should always be non-moving! */
11170 int border_element_old[NUM_DIRECTIONS];
11173 for (i = 0; i < NUM_DIRECTIONS; i++)
11175 int xx = x + xy[i][0];
11176 int yy = y + xy[i][1];
11177 int border_element;
11179 border_element_old[i] = -1;
11181 if (!IN_LEV_FIELD(xx, yy))
11184 if (game.engine_version < VERSION_IDENT(3,0,7,0))
11185 border_element = Feld[xx][yy]; /* may be moving! */
11186 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
11187 border_element = Feld[xx][yy];
11188 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
11189 border_element = MovingOrBlocked2Element(xx, yy);
11191 continue; /* center and border element do not touch */
11193 border_element_old[i] = border_element;
11196 for (i = 0; i < NUM_DIRECTIONS; i++)
11198 int xx = x + xy[i][0];
11199 int yy = y + xy[i][1];
11200 int center_side = trigger_sides[i][0];
11201 int border_element = border_element_old[i];
11203 if (border_element == -1)
11206 /* check for change of border element */
11207 CheckElementChangeBySide(xx, yy, border_element, center_element,
11208 CE_TOUCHING_X, center_side);
11211 for (i = 0; i < NUM_DIRECTIONS; i++)
11213 int border_side = trigger_sides[i][1];
11214 int border_element = border_element_old[i];
11216 if (border_element == -1)
11219 /* check for change of center element (but change it only once) */
11220 if (!change_center_element)
11221 change_center_element =
11222 CheckElementChangeBySide(x, y, center_element, border_element,
11223 CE_TOUCHING_X, border_side);
11229 void TestIfElementTouchesCustomElement_OLD(int x, int y)
11231 static int xy[4][2] =
11238 static int trigger_sides[4][2] =
11240 /* center side border side */
11241 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
11242 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
11243 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
11244 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
11246 static int touch_dir[4] =
11248 MV_LEFT | MV_RIGHT,
11253 boolean change_center_element = FALSE;
11254 int center_element = Feld[x][y]; /* should always be non-moving! */
11257 for (i = 0; i < NUM_DIRECTIONS; i++)
11259 int xx = x + xy[i][0];
11260 int yy = y + xy[i][1];
11261 int center_side = trigger_sides[i][0];
11262 int border_side = trigger_sides[i][1];
11263 int border_element;
11265 if (!IN_LEV_FIELD(xx, yy))
11268 if (game.engine_version < VERSION_IDENT(3,0,7,0))
11269 border_element = Feld[xx][yy]; /* may be moving! */
11270 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
11271 border_element = Feld[xx][yy];
11272 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
11273 border_element = MovingOrBlocked2Element(xx, yy);
11275 continue; /* center and border element do not touch */
11277 /* check for change of center element (but change it only once) */
11278 if (!change_center_element)
11279 change_center_element =
11280 CheckElementChangeBySide(x, y, center_element, border_element,
11281 CE_TOUCHING_X, border_side);
11283 /* check for change of border element */
11284 CheckElementChangeBySide(xx, yy, border_element, center_element,
11285 CE_TOUCHING_X, center_side);
11291 void TestIfElementHitsCustomElement(int x, int y, int direction)
11293 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
11294 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
11295 int hitx = x + dx, hity = y + dy;
11296 int hitting_element = Feld[x][y];
11297 int touched_element;
11299 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
11302 touched_element = (IN_LEV_FIELD(hitx, hity) ?
11303 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
11305 if (IN_LEV_FIELD(hitx, hity))
11307 int opposite_direction = MV_DIR_OPPOSITE(direction);
11308 int hitting_side = direction;
11309 int touched_side = opposite_direction;
11310 boolean object_hit = (!IS_MOVING(hitx, hity) ||
11311 MovDir[hitx][hity] != direction ||
11312 ABS(MovPos[hitx][hity]) <= TILEY / 2);
11318 CheckElementChangeBySide(x, y, hitting_element, touched_element,
11319 CE_HITTING_X, touched_side);
11321 CheckElementChangeBySide(hitx, hity, touched_element,
11322 hitting_element, CE_HIT_BY_X, hitting_side);
11324 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
11325 CE_HIT_BY_SOMETHING, opposite_direction);
11329 /* "hitting something" is also true when hitting the playfield border */
11330 CheckElementChangeBySide(x, y, hitting_element, touched_element,
11331 CE_HITTING_SOMETHING, direction);
11335 void TestIfElementSmashesCustomElement(int x, int y, int direction)
11337 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
11338 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
11339 int hitx = x + dx, hity = y + dy;
11340 int hitting_element = Feld[x][y];
11341 int touched_element;
11343 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
11344 !IS_FREE(hitx, hity) &&
11345 (!IS_MOVING(hitx, hity) ||
11346 MovDir[hitx][hity] != direction ||
11347 ABS(MovPos[hitx][hity]) <= TILEY / 2));
11350 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
11354 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
11358 touched_element = (IN_LEV_FIELD(hitx, hity) ?
11359 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
11361 CheckElementChangeBySide(x, y, hitting_element, touched_element,
11362 EP_CAN_SMASH_EVERYTHING, direction);
11364 if (IN_LEV_FIELD(hitx, hity))
11366 int opposite_direction = MV_DIR_OPPOSITE(direction);
11367 int hitting_side = direction;
11368 int touched_side = opposite_direction;
11370 int touched_element = MovingOrBlocked2Element(hitx, hity);
11373 boolean object_hit = (!IS_MOVING(hitx, hity) ||
11374 MovDir[hitx][hity] != direction ||
11375 ABS(MovPos[hitx][hity]) <= TILEY / 2);
11384 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
11385 CE_SMASHED_BY_SOMETHING, opposite_direction);
11387 CheckElementChangeBySide(x, y, hitting_element, touched_element,
11388 CE_OTHER_IS_SMASHING, touched_side);
11390 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
11391 CE_OTHER_GETS_SMASHED, hitting_side);
11397 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
11399 int i, kill_x = -1, kill_y = -1;
11401 int bad_element = -1;
11402 static int test_xy[4][2] =
11409 static int test_dir[4] =
11417 for (i = 0; i < NUM_DIRECTIONS; i++)
11419 int test_x, test_y, test_move_dir, test_element;
11421 test_x = good_x + test_xy[i][0];
11422 test_y = good_y + test_xy[i][1];
11424 if (!IN_LEV_FIELD(test_x, test_y))
11428 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
11430 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
11432 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
11433 2nd case: DONT_TOUCH style bad thing does not move away from good thing
11435 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
11436 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
11440 bad_element = test_element;
11446 if (kill_x != -1 || kill_y != -1)
11448 if (IS_PLAYER(good_x, good_y))
11450 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
11452 if (player->shield_deadly_time_left > 0 &&
11453 !IS_INDESTRUCTIBLE(bad_element))
11454 Bang(kill_x, kill_y);
11455 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
11456 KillPlayer(player);
11459 Bang(good_x, good_y);
11463 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
11465 int i, kill_x = -1, kill_y = -1;
11466 int bad_element = Feld[bad_x][bad_y];
11467 static int test_xy[4][2] =
11474 static int touch_dir[4] =
11476 MV_LEFT | MV_RIGHT,
11481 static int test_dir[4] =
11489 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
11492 for (i = 0; i < NUM_DIRECTIONS; i++)
11494 int test_x, test_y, test_move_dir, test_element;
11496 test_x = bad_x + test_xy[i][0];
11497 test_y = bad_y + test_xy[i][1];
11498 if (!IN_LEV_FIELD(test_x, test_y))
11502 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
11504 test_element = Feld[test_x][test_y];
11506 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
11507 2nd case: DONT_TOUCH style bad thing does not move away from good thing
11509 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
11510 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
11512 /* good thing is player or penguin that does not move away */
11513 if (IS_PLAYER(test_x, test_y))
11515 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
11517 if (bad_element == EL_ROBOT && player->is_moving)
11518 continue; /* robot does not kill player if he is moving */
11520 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
11522 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
11523 continue; /* center and border element do not touch */
11530 else if (test_element == EL_PENGUIN)
11539 if (kill_x != -1 || kill_y != -1)
11541 if (IS_PLAYER(kill_x, kill_y))
11543 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
11545 if (player->shield_deadly_time_left > 0 &&
11546 !IS_INDESTRUCTIBLE(bad_element))
11547 Bang(bad_x, bad_y);
11548 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
11549 KillPlayer(player);
11552 Bang(kill_x, kill_y);
11556 void TestIfPlayerTouchesBadThing(int x, int y)
11558 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
11561 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
11563 TestIfGoodThingHitsBadThing(x, y, move_dir);
11566 void TestIfBadThingTouchesPlayer(int x, int y)
11568 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
11571 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
11573 TestIfBadThingHitsGoodThing(x, y, move_dir);
11576 void TestIfFriendTouchesBadThing(int x, int y)
11578 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
11581 void TestIfBadThingTouchesFriend(int x, int y)
11583 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
11586 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
11588 int i, kill_x = bad_x, kill_y = bad_y;
11589 static int xy[4][2] =
11597 for (i = 0; i < NUM_DIRECTIONS; i++)
11601 x = bad_x + xy[i][0];
11602 y = bad_y + xy[i][1];
11603 if (!IN_LEV_FIELD(x, y))
11606 element = Feld[x][y];
11607 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
11608 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
11616 if (kill_x != bad_x || kill_y != bad_y)
11617 Bang(bad_x, bad_y);
11620 void KillPlayer(struct PlayerInfo *player)
11622 int jx = player->jx, jy = player->jy;
11624 if (!player->active)
11627 /* the following code was introduced to prevent an infinite loop when calling
11629 -> CheckTriggeredElementChangeExt()
11630 -> ExecuteCustomElementAction()
11632 -> (infinitely repeating the above sequence of function calls)
11633 which occurs when killing the player while having a CE with the setting
11634 "kill player X when explosion of <player X>"; the solution using a new
11635 field "player->killed" was chosen for backwards compatibility, although
11636 clever use of the fields "player->active" etc. would probably also work */
11638 if (player->killed)
11642 player->killed = TRUE;
11644 /* remove accessible field at the player's position */
11645 Feld[jx][jy] = EL_EMPTY;
11647 /* deactivate shield (else Bang()/Explode() would not work right) */
11648 player->shield_normal_time_left = 0;
11649 player->shield_deadly_time_left = 0;
11652 BuryPlayer(player);
11655 static void KillPlayerUnlessEnemyProtected(int x, int y)
11657 if (!PLAYER_ENEMY_PROTECTED(x, y))
11658 KillPlayer(PLAYERINFO(x, y));
11661 static void KillPlayerUnlessExplosionProtected(int x, int y)
11663 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
11664 KillPlayer(PLAYERINFO(x, y));
11667 void BuryPlayer(struct PlayerInfo *player)
11669 int jx = player->jx, jy = player->jy;
11671 if (!player->active)
11674 PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
11675 PlayLevelSound(jx, jy, SND_GAME_LOSING);
11677 player->GameOver = TRUE;
11678 RemovePlayer(player);
11681 void RemovePlayer(struct PlayerInfo *player)
11683 int jx = player->jx, jy = player->jy;
11684 int i, found = FALSE;
11686 player->present = FALSE;
11687 player->active = FALSE;
11689 if (!ExplodeField[jx][jy])
11690 StorePlayer[jx][jy] = 0;
11692 if (player->is_moving)
11693 DrawLevelField(player->last_jx, player->last_jy);
11695 for (i = 0; i < MAX_PLAYERS; i++)
11696 if (stored_player[i].active)
11700 AllPlayersGone = TRUE;
11706 #if USE_NEW_SNAP_DELAY
11707 static void setFieldForSnapping(int x, int y, int element, int direction)
11709 struct ElementInfo *ei = &element_info[element];
11710 int direction_bit = MV_DIR_TO_BIT(direction);
11711 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
11712 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
11713 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
11715 Feld[x][y] = EL_ELEMENT_SNAPPING;
11716 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
11718 ResetGfxAnimation(x, y);
11720 GfxElement[x][y] = element;
11721 GfxAction[x][y] = action;
11722 GfxDir[x][y] = direction;
11723 GfxFrame[x][y] = -1;
11728 =============================================================================
11729 checkDiagonalPushing()
11730 -----------------------------------------------------------------------------
11731 check if diagonal input device direction results in pushing of object
11732 (by checking if the alternative direction is walkable, diggable, ...)
11733 =============================================================================
11736 static boolean checkDiagonalPushing(struct PlayerInfo *player,
11737 int x, int y, int real_dx, int real_dy)
11739 int jx, jy, dx, dy, xx, yy;
11741 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
11744 /* diagonal direction: check alternative direction */
11749 xx = jx + (dx == 0 ? real_dx : 0);
11750 yy = jy + (dy == 0 ? real_dy : 0);
11752 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
11756 =============================================================================
11758 -----------------------------------------------------------------------------
11759 x, y: field next to player (non-diagonal) to try to dig to
11760 real_dx, real_dy: direction as read from input device (can be diagonal)
11761 =============================================================================
11764 int DigField(struct PlayerInfo *player,
11765 int oldx, int oldy, int x, int y,
11766 int real_dx, int real_dy, int mode)
11768 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
11769 boolean player_was_pushing = player->is_pushing;
11770 boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
11771 boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
11772 int jx = oldx, jy = oldy;
11773 int dx = x - jx, dy = y - jy;
11774 int nextx = x + dx, nexty = y + dy;
11775 int move_direction = (dx == -1 ? MV_LEFT :
11776 dx == +1 ? MV_RIGHT :
11778 dy == +1 ? MV_DOWN : MV_NONE);
11779 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
11780 int dig_side = MV_DIR_OPPOSITE(move_direction);
11781 int old_element = Feld[jx][jy];
11782 #if USE_FIXED_DONT_RUN_INTO
11783 int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
11789 if (is_player) /* function can also be called by EL_PENGUIN */
11791 if (player->MovPos == 0)
11793 player->is_digging = FALSE;
11794 player->is_collecting = FALSE;
11797 if (player->MovPos == 0) /* last pushing move finished */
11798 player->is_pushing = FALSE;
11800 if (mode == DF_NO_PUSH) /* player just stopped pushing */
11802 player->is_switching = FALSE;
11803 player->push_delay = -1;
11805 return MP_NO_ACTION;
11809 #if !USE_FIXED_DONT_RUN_INTO
11810 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
11811 return MP_NO_ACTION;
11814 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
11815 old_element = Back[jx][jy];
11817 /* in case of element dropped at player position, check background */
11818 else if (Back[jx][jy] != EL_EMPTY &&
11819 game.engine_version >= VERSION_IDENT(2,2,0,0))
11820 old_element = Back[jx][jy];
11822 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
11823 return MP_NO_ACTION; /* field has no opening in this direction */
11825 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
11826 return MP_NO_ACTION; /* field has no opening in this direction */
11828 #if USE_FIXED_DONT_RUN_INTO
11829 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
11833 Feld[jx][jy] = player->artwork_element;
11834 InitMovingField(jx, jy, MV_DOWN);
11835 Store[jx][jy] = EL_ACID;
11836 ContinueMoving(jx, jy);
11837 BuryPlayer(player);
11839 return MP_DONT_RUN_INTO;
11843 #if USE_FIXED_DONT_RUN_INTO
11844 if (player_can_move && DONT_RUN_INTO(element))
11846 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
11848 return MP_DONT_RUN_INTO;
11852 #if USE_FIXED_DONT_RUN_INTO
11853 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
11854 return MP_NO_ACTION;
11857 #if !USE_FIXED_DONT_RUN_INTO
11858 element = Feld[x][y];
11861 collect_count = element_info[element].collect_count_initial;
11863 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
11864 return MP_NO_ACTION;
11866 if (game.engine_version < VERSION_IDENT(2,2,0,0))
11867 player_can_move = player_can_move_or_snap;
11869 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
11870 game.engine_version >= VERSION_IDENT(2,2,0,0))
11872 CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
11873 player->index_bit, dig_side);
11874 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
11875 player->index_bit, dig_side);
11877 if (element == EL_DC_LANDMINE)
11880 if (Feld[x][y] != element) /* field changed by snapping */
11883 return MP_NO_ACTION;
11886 #if USE_PLAYER_GRAVITY
11887 if (player->gravity && is_player && !player->is_auto_moving &&
11888 canFallDown(player) && move_direction != MV_DOWN &&
11889 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
11890 return MP_NO_ACTION; /* player cannot walk here due to gravity */
11892 if (game.gravity && is_player && !player->is_auto_moving &&
11893 canFallDown(player) && move_direction != MV_DOWN &&
11894 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
11895 return MP_NO_ACTION; /* player cannot walk here due to gravity */
11898 if (player_can_move &&
11899 IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
11901 int sound_element = SND_ELEMENT(element);
11902 int sound_action = ACTION_WALKING;
11904 if (IS_RND_GATE(element))
11906 if (!player->key[RND_GATE_NR(element)])
11907 return MP_NO_ACTION;
11909 else if (IS_RND_GATE_GRAY(element))
11911 if (!player->key[RND_GATE_GRAY_NR(element)])
11912 return MP_NO_ACTION;
11914 else if (IS_RND_GATE_GRAY_ACTIVE(element))
11916 if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
11917 return MP_NO_ACTION;
11919 else if (element == EL_EXIT_OPEN ||
11920 element == EL_EM_EXIT_OPEN ||
11921 element == EL_STEEL_EXIT_OPEN ||
11922 element == EL_EM_STEEL_EXIT_OPEN ||
11923 element == EL_SP_EXIT_OPEN ||
11924 element == EL_SP_EXIT_OPENING)
11926 sound_action = ACTION_PASSING; /* player is passing exit */
11928 else if (element == EL_EMPTY)
11930 sound_action = ACTION_MOVING; /* nothing to walk on */
11933 /* play sound from background or player, whatever is available */
11934 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
11935 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
11937 PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
11939 else if (player_can_move &&
11940 IS_PASSABLE(element) && canPassField(x, y, move_direction))
11942 if (!ACCESS_FROM(element, opposite_direction))
11943 return MP_NO_ACTION; /* field not accessible from this direction */
11945 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
11946 return MP_NO_ACTION;
11948 if (IS_EM_GATE(element))
11950 if (!player->key[EM_GATE_NR(element)])
11951 return MP_NO_ACTION;
11953 else if (IS_EM_GATE_GRAY(element))
11955 if (!player->key[EM_GATE_GRAY_NR(element)])
11956 return MP_NO_ACTION;
11958 else if (IS_EM_GATE_GRAY_ACTIVE(element))
11960 if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
11961 return MP_NO_ACTION;
11963 else if (IS_EMC_GATE(element))
11965 if (!player->key[EMC_GATE_NR(element)])
11966 return MP_NO_ACTION;
11968 else if (IS_EMC_GATE_GRAY(element))
11970 if (!player->key[EMC_GATE_GRAY_NR(element)])
11971 return MP_NO_ACTION;
11973 else if (IS_EMC_GATE_GRAY_ACTIVE(element))
11975 if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
11976 return MP_NO_ACTION;
11978 else if (element == EL_DC_GATE_WHITE ||
11979 element == EL_DC_GATE_WHITE_GRAY ||
11980 element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
11982 if (player->num_white_keys == 0)
11983 return MP_NO_ACTION;
11985 player->num_white_keys--;
11987 else if (IS_SP_PORT(element))
11989 if (element == EL_SP_GRAVITY_PORT_LEFT ||
11990 element == EL_SP_GRAVITY_PORT_RIGHT ||
11991 element == EL_SP_GRAVITY_PORT_UP ||
11992 element == EL_SP_GRAVITY_PORT_DOWN)
11993 #if USE_PLAYER_GRAVITY
11994 player->gravity = !player->gravity;
11996 game.gravity = !game.gravity;
11998 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
11999 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
12000 element == EL_SP_GRAVITY_ON_PORT_UP ||
12001 element == EL_SP_GRAVITY_ON_PORT_DOWN)
12002 #if USE_PLAYER_GRAVITY
12003 player->gravity = TRUE;
12005 game.gravity = TRUE;
12007 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
12008 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
12009 element == EL_SP_GRAVITY_OFF_PORT_UP ||
12010 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
12011 #if USE_PLAYER_GRAVITY
12012 player->gravity = FALSE;
12014 game.gravity = FALSE;
12018 /* automatically move to the next field with double speed */
12019 player->programmed_action = move_direction;
12021 if (player->move_delay_reset_counter == 0)
12023 player->move_delay_reset_counter = 2; /* two double speed steps */
12025 DOUBLE_PLAYER_SPEED(player);
12028 PlayLevelSoundAction(x, y, ACTION_PASSING);
12030 else if (player_can_move_or_snap && IS_DIGGABLE(element))
12034 if (mode != DF_SNAP)
12036 GfxElement[x][y] = GFX_ELEMENT(element);
12037 player->is_digging = TRUE;
12040 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
12042 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
12043 player->index_bit, dig_side);
12045 if (mode == DF_SNAP)
12047 #if USE_NEW_SNAP_DELAY
12048 if (level.block_snap_field)
12049 setFieldForSnapping(x, y, element, move_direction);
12051 TestIfElementTouchesCustomElement(x, y); /* for empty space */
12053 TestIfElementTouchesCustomElement(x, y); /* for empty space */
12056 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
12057 player->index_bit, dig_side);
12060 else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
12064 if (is_player && mode != DF_SNAP)
12066 GfxElement[x][y] = element;
12067 player->is_collecting = TRUE;
12070 if (element == EL_SPEED_PILL)
12072 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
12074 else if (element == EL_EXTRA_TIME && level.time > 0)
12076 TimeLeft += level.extra_time;
12077 DrawGameValue_Time(TimeLeft);
12079 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
12081 player->shield_normal_time_left += level.shield_normal_time;
12082 if (element == EL_SHIELD_DEADLY)
12083 player->shield_deadly_time_left += level.shield_deadly_time;
12085 else if (element == EL_DYNAMITE ||
12086 element == EL_EM_DYNAMITE ||
12087 element == EL_SP_DISK_RED)
12089 if (player->inventory_size < MAX_INVENTORY_SIZE)
12090 player->inventory_element[player->inventory_size++] = element;
12092 DrawGameDoorValues();
12094 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
12096 player->dynabomb_count++;
12097 player->dynabombs_left++;
12099 else if (element == EL_DYNABOMB_INCREASE_SIZE)
12101 player->dynabomb_size++;
12103 else if (element == EL_DYNABOMB_INCREASE_POWER)
12105 player->dynabomb_xl = TRUE;
12107 else if (IS_KEY(element))
12109 player->key[KEY_NR(element)] = TRUE;
12111 DrawGameDoorValues();
12113 else if (element == EL_DC_KEY_WHITE)
12115 player->num_white_keys++;
12117 /* display white keys? */
12118 /* DrawGameDoorValues(); */
12120 else if (IS_ENVELOPE(element))
12122 player->show_envelope = element;
12124 else if (element == EL_EMC_LENSES)
12126 game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
12128 RedrawAllInvisibleElementsForLenses();
12130 else if (element == EL_EMC_MAGNIFIER)
12132 game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
12134 RedrawAllInvisibleElementsForMagnifier();
12136 else if (IS_DROPPABLE(element) ||
12137 IS_THROWABLE(element)) /* can be collected and dropped */
12141 if (collect_count == 0)
12142 player->inventory_infinite_element = element;
12144 for (i = 0; i < collect_count; i++)
12145 if (player->inventory_size < MAX_INVENTORY_SIZE)
12146 player->inventory_element[player->inventory_size++] = element;
12148 DrawGameDoorValues();
12150 else if (collect_count > 0)
12152 local_player->gems_still_needed -= collect_count;
12153 if (local_player->gems_still_needed < 0)
12154 local_player->gems_still_needed = 0;
12156 DrawGameValue_Emeralds(local_player->gems_still_needed);
12159 RaiseScoreElement(element);
12160 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
12163 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
12164 player->index_bit, dig_side);
12166 if (mode == DF_SNAP)
12168 #if USE_NEW_SNAP_DELAY
12169 if (level.block_snap_field)
12170 setFieldForSnapping(x, y, element, move_direction);
12172 TestIfElementTouchesCustomElement(x, y); /* for empty space */
12174 TestIfElementTouchesCustomElement(x, y); /* for empty space */
12177 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
12178 player->index_bit, dig_side);
12181 else if (player_can_move_or_snap && IS_PUSHABLE(element))
12183 if (mode == DF_SNAP && element != EL_BD_ROCK)
12184 return MP_NO_ACTION;
12186 if (CAN_FALL(element) && dy)
12187 return MP_NO_ACTION;
12189 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
12190 !(element == EL_SPRING && level.use_spring_bug))
12191 return MP_NO_ACTION;
12193 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
12194 ((move_direction & MV_VERTICAL &&
12195 ((element_info[element].move_pattern & MV_LEFT &&
12196 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
12197 (element_info[element].move_pattern & MV_RIGHT &&
12198 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
12199 (move_direction & MV_HORIZONTAL &&
12200 ((element_info[element].move_pattern & MV_UP &&
12201 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
12202 (element_info[element].move_pattern & MV_DOWN &&
12203 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
12204 return MP_NO_ACTION;
12206 /* do not push elements already moving away faster than player */
12207 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
12208 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
12209 return MP_NO_ACTION;
12211 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
12213 if (player->push_delay_value == -1 || !player_was_pushing)
12214 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12216 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
12218 if (player->push_delay_value == -1)
12219 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12221 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
12223 if (!player->is_pushing)
12224 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12227 player->is_pushing = TRUE;
12228 player->is_active = TRUE;
12230 if (!(IN_LEV_FIELD(nextx, nexty) &&
12231 (IS_FREE(nextx, nexty) ||
12232 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
12233 IS_SB_ELEMENT(element)))))
12234 return MP_NO_ACTION;
12236 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
12237 return MP_NO_ACTION;
12239 if (player->push_delay == -1) /* new pushing; restart delay */
12240 player->push_delay = 0;
12242 if (player->push_delay < player->push_delay_value &&
12243 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
12244 element != EL_SPRING && element != EL_BALLOON)
12246 /* make sure that there is no move delay before next try to push */
12247 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
12248 player->move_delay = 0;
12250 return MP_NO_ACTION;
12253 if (IS_SB_ELEMENT(element))
12255 if (element == EL_SOKOBAN_FIELD_FULL)
12257 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
12258 local_player->sokobanfields_still_needed++;
12261 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
12263 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
12264 local_player->sokobanfields_still_needed--;
12267 Feld[x][y] = EL_SOKOBAN_OBJECT;
12269 if (Back[x][y] == Back[nextx][nexty])
12270 PlayLevelSoundAction(x, y, ACTION_PUSHING);
12271 else if (Back[x][y] != 0)
12272 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
12275 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
12278 if (local_player->sokobanfields_still_needed == 0 &&
12279 game.emulation == EMU_SOKOBAN)
12281 PlayerWins(player);
12283 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
12287 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12289 InitMovingField(x, y, move_direction);
12290 GfxAction[x][y] = ACTION_PUSHING;
12292 if (mode == DF_SNAP)
12293 ContinueMoving(x, y);
12295 MovPos[x][y] = (dx != 0 ? dx : dy);
12297 Pushed[x][y] = TRUE;
12298 Pushed[nextx][nexty] = TRUE;
12300 if (game.engine_version < VERSION_IDENT(2,2,0,7))
12301 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12303 player->push_delay_value = -1; /* get new value later */
12305 /* check for element change _after_ element has been pushed */
12306 if (game.use_change_when_pushing_bug)
12308 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
12309 player->index_bit, dig_side);
12310 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
12311 player->index_bit, dig_side);
12314 else if (IS_SWITCHABLE(element))
12316 if (PLAYER_SWITCHING(player, x, y))
12318 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
12319 player->index_bit, dig_side);
12324 player->is_switching = TRUE;
12325 player->switch_x = x;
12326 player->switch_y = y;
12328 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
12330 if (element == EL_ROBOT_WHEEL)
12332 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
12336 DrawLevelField(x, y);
12338 else if (element == EL_SP_TERMINAL)
12342 SCAN_PLAYFIELD(xx, yy)
12344 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
12346 else if (Feld[xx][yy] == EL_SP_TERMINAL)
12347 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
12350 else if (IS_BELT_SWITCH(element))
12352 ToggleBeltSwitch(x, y);
12354 else if (element == EL_SWITCHGATE_SWITCH_UP ||
12355 element == EL_SWITCHGATE_SWITCH_DOWN ||
12356 element == EL_DC_SWITCHGATE_SWITCH_UP ||
12357 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
12359 ToggleSwitchgateSwitch(x, y);
12361 else if (element == EL_LIGHT_SWITCH ||
12362 element == EL_LIGHT_SWITCH_ACTIVE)
12364 ToggleLightSwitch(x, y);
12366 else if (element == EL_TIMEGATE_SWITCH ||
12367 element == EL_DC_TIMEGATE_SWITCH)
12369 ActivateTimegateSwitch(x, y);
12371 else if (element == EL_BALLOON_SWITCH_LEFT ||
12372 element == EL_BALLOON_SWITCH_RIGHT ||
12373 element == EL_BALLOON_SWITCH_UP ||
12374 element == EL_BALLOON_SWITCH_DOWN ||
12375 element == EL_BALLOON_SWITCH_NONE ||
12376 element == EL_BALLOON_SWITCH_ANY)
12378 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
12379 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
12380 element == EL_BALLOON_SWITCH_UP ? MV_UP :
12381 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
12382 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
12385 else if (element == EL_LAMP)
12387 Feld[x][y] = EL_LAMP_ACTIVE;
12388 local_player->lights_still_needed--;
12390 ResetGfxAnimation(x, y);
12391 DrawLevelField(x, y);
12393 else if (element == EL_TIME_ORB_FULL)
12395 Feld[x][y] = EL_TIME_ORB_EMPTY;
12397 if (level.time > 0 || level.use_time_orb_bug)
12399 TimeLeft += level.time_orb_time;
12400 DrawGameValue_Time(TimeLeft);
12403 ResetGfxAnimation(x, y);
12404 DrawLevelField(x, y);
12406 else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
12407 element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
12411 game.ball_state = !game.ball_state;
12413 SCAN_PLAYFIELD(xx, yy)
12415 int e = Feld[xx][yy];
12417 if (game.ball_state)
12419 if (e == EL_EMC_MAGIC_BALL)
12420 CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
12421 else if (e == EL_EMC_MAGIC_BALL_SWITCH)
12422 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
12426 if (e == EL_EMC_MAGIC_BALL_ACTIVE)
12427 CreateField(xx, yy, EL_EMC_MAGIC_BALL);
12428 else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
12429 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
12434 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
12435 player->index_bit, dig_side);
12437 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
12438 player->index_bit, dig_side);
12440 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
12441 player->index_bit, dig_side);
12447 if (!PLAYER_SWITCHING(player, x, y))
12449 player->is_switching = TRUE;
12450 player->switch_x = x;
12451 player->switch_y = y;
12453 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
12454 player->index_bit, dig_side);
12455 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
12456 player->index_bit, dig_side);
12458 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
12459 player->index_bit, dig_side);
12460 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
12461 player->index_bit, dig_side);
12464 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
12465 player->index_bit, dig_side);
12466 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
12467 player->index_bit, dig_side);
12469 return MP_NO_ACTION;
12472 player->push_delay = -1;
12474 if (is_player) /* function can also be called by EL_PENGUIN */
12476 if (Feld[x][y] != element) /* really digged/collected something */
12478 player->is_collecting = !player->is_digging;
12479 player->is_active = TRUE;
12486 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
12488 int jx = player->jx, jy = player->jy;
12489 int x = jx + dx, y = jy + dy;
12490 int snap_direction = (dx == -1 ? MV_LEFT :
12491 dx == +1 ? MV_RIGHT :
12493 dy == +1 ? MV_DOWN : MV_NONE);
12494 boolean can_continue_snapping = (level.continuous_snapping &&
12495 WasJustFalling[x][y] < CHECK_DELAY_FALLING);
12497 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
12500 if (!player->active || !IN_LEV_FIELD(x, y))
12508 if (player->MovPos == 0)
12509 player->is_pushing = FALSE;
12511 player->is_snapping = FALSE;
12513 if (player->MovPos == 0)
12515 player->is_moving = FALSE;
12516 player->is_digging = FALSE;
12517 player->is_collecting = FALSE;
12523 #if USE_NEW_CONTINUOUS_SNAPPING
12524 /* prevent snapping with already pressed snap key when not allowed */
12525 if (player->is_snapping && !can_continue_snapping)
12528 if (player->is_snapping)
12532 player->MovDir = snap_direction;
12534 if (player->MovPos == 0)
12536 player->is_moving = FALSE;
12537 player->is_digging = FALSE;
12538 player->is_collecting = FALSE;
12541 player->is_dropping = FALSE;
12542 player->is_dropping_pressed = FALSE;
12543 player->drop_pressed_delay = 0;
12545 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
12548 player->is_snapping = TRUE;
12549 player->is_active = TRUE;
12551 if (player->MovPos == 0)
12553 player->is_moving = FALSE;
12554 player->is_digging = FALSE;
12555 player->is_collecting = FALSE;
12558 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
12559 DrawLevelField(player->last_jx, player->last_jy);
12561 DrawLevelField(x, y);
12566 boolean DropElement(struct PlayerInfo *player)
12568 int old_element, new_element;
12569 int dropx = player->jx, dropy = player->jy;
12570 int drop_direction = player->MovDir;
12571 int drop_side = drop_direction;
12572 int drop_element = (player->inventory_size > 0 ?
12573 player->inventory_element[player->inventory_size - 1] :
12574 player->inventory_infinite_element != EL_UNDEFINED ?
12575 player->inventory_infinite_element :
12576 player->dynabombs_left > 0 ?
12577 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
12580 player->is_dropping_pressed = TRUE;
12582 /* do not drop an element on top of another element; when holding drop key
12583 pressed without moving, dropped element must move away before the next
12584 element can be dropped (this is especially important if the next element
12585 is dynamite, which can be placed on background for historical reasons) */
12586 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
12589 if (IS_THROWABLE(drop_element))
12591 dropx += GET_DX_FROM_DIR(drop_direction);
12592 dropy += GET_DY_FROM_DIR(drop_direction);
12594 if (!IN_LEV_FIELD(dropx, dropy))
12598 old_element = Feld[dropx][dropy]; /* old element at dropping position */
12599 new_element = drop_element; /* default: no change when dropping */
12601 /* check if player is active, not moving and ready to drop */
12602 if (!player->active || player->MovPos || player->drop_delay > 0)
12605 /* check if player has anything that can be dropped */
12606 if (new_element == EL_UNDEFINED)
12609 /* check if drop key was pressed long enough for EM style dynamite */
12610 if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
12613 /* check if anything can be dropped at the current position */
12614 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
12617 /* collected custom elements can only be dropped on empty fields */
12618 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
12621 if (old_element != EL_EMPTY)
12622 Back[dropx][dropy] = old_element; /* store old element on this field */
12624 ResetGfxAnimation(dropx, dropy);
12625 ResetRandomAnimationValue(dropx, dropy);
12627 if (player->inventory_size > 0 ||
12628 player->inventory_infinite_element != EL_UNDEFINED)
12630 if (player->inventory_size > 0)
12632 player->inventory_size--;
12634 DrawGameDoorValues();
12636 if (new_element == EL_DYNAMITE)
12637 new_element = EL_DYNAMITE_ACTIVE;
12638 else if (new_element == EL_EM_DYNAMITE)
12639 new_element = EL_EM_DYNAMITE_ACTIVE;
12640 else if (new_element == EL_SP_DISK_RED)
12641 new_element = EL_SP_DISK_RED_ACTIVE;
12644 Feld[dropx][dropy] = new_element;
12646 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
12647 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
12648 el2img(Feld[dropx][dropy]), 0);
12650 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
12652 /* needed if previous element just changed to "empty" in the last frame */
12653 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
12655 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
12656 player->index_bit, drop_side);
12657 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
12659 player->index_bit, drop_side);
12661 TestIfElementTouchesCustomElement(dropx, dropy);
12663 else /* player is dropping a dyna bomb */
12665 player->dynabombs_left--;
12667 Feld[dropx][dropy] = new_element;
12669 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
12670 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
12671 el2img(Feld[dropx][dropy]), 0);
12673 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
12676 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
12677 InitField_WithBug1(dropx, dropy, FALSE);
12679 new_element = Feld[dropx][dropy]; /* element might have changed */
12681 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
12682 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
12684 int move_direction, nextx, nexty;
12686 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
12687 MovDir[dropx][dropy] = drop_direction;
12689 move_direction = MovDir[dropx][dropy];
12690 nextx = dropx + GET_DX_FROM_DIR(move_direction);
12691 nexty = dropy + GET_DY_FROM_DIR(move_direction);
12693 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
12695 #if USE_FIX_IMPACT_COLLISION
12696 /* do not cause impact style collision by dropping elements that can fall */
12697 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
12699 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
12703 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
12704 player->is_dropping = TRUE;
12706 player->drop_pressed_delay = 0;
12707 player->is_dropping_pressed = FALSE;
12709 player->drop_x = dropx;
12710 player->drop_y = dropy;
12715 /* ------------------------------------------------------------------------- */
12716 /* game sound playing functions */
12717 /* ------------------------------------------------------------------------- */
12719 static int *loop_sound_frame = NULL;
12720 static int *loop_sound_volume = NULL;
12722 void InitPlayLevelSound()
12724 int num_sounds = getSoundListSize();
12726 checked_free(loop_sound_frame);
12727 checked_free(loop_sound_volume);
12729 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
12730 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
12733 static void PlayLevelSound(int x, int y, int nr)
12735 int sx = SCREENX(x), sy = SCREENY(y);
12736 int volume, stereo_position;
12737 int max_distance = 8;
12738 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
12740 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
12741 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
12744 if (!IN_LEV_FIELD(x, y) ||
12745 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
12746 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
12749 volume = SOUND_MAX_VOLUME;
12751 if (!IN_SCR_FIELD(sx, sy))
12753 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
12754 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
12756 volume -= volume * (dx > dy ? dx : dy) / max_distance;
12759 stereo_position = (SOUND_MAX_LEFT +
12760 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
12761 (SCR_FIELDX + 2 * max_distance));
12763 if (IS_LOOP_SOUND(nr))
12765 /* This assures that quieter loop sounds do not overwrite louder ones,
12766 while restarting sound volume comparison with each new game frame. */
12768 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
12771 loop_sound_volume[nr] = volume;
12772 loop_sound_frame[nr] = FrameCounter;
12775 PlaySoundExt(nr, volume, stereo_position, type);
12778 static void PlayLevelSoundNearest(int x, int y, int sound_action)
12780 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
12781 x > LEVELX(BX2) ? LEVELX(BX2) : x,
12782 y < LEVELY(BY1) ? LEVELY(BY1) :
12783 y > LEVELY(BY2) ? LEVELY(BY2) : y,
12787 static void PlayLevelSoundAction(int x, int y, int action)
12789 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
12792 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
12794 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
12796 if (sound_effect != SND_UNDEFINED)
12797 PlayLevelSound(x, y, sound_effect);
12800 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
12803 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
12805 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12806 PlayLevelSound(x, y, sound_effect);
12809 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
12811 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
12813 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12814 PlayLevelSound(x, y, sound_effect);
12817 static void StopLevelSoundActionIfLoop(int x, int y, int action)
12819 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
12821 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12822 StopSound(sound_effect);
12825 static void PlayLevelMusic()
12827 if (levelset.music[level_nr] != MUS_UNDEFINED)
12828 PlayMusic(levelset.music[level_nr]); /* from config file */
12830 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
12833 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
12835 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
12836 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
12837 int x = xx - 1 - offset;
12838 int y = yy - 1 - offset;
12843 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
12847 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12851 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12855 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12859 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
12863 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12867 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12870 case SAMPLE_android_clone:
12871 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12874 case SAMPLE_android_move:
12875 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12878 case SAMPLE_spring:
12879 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12883 PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
12887 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
12890 case SAMPLE_eater_eat:
12891 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
12895 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12898 case SAMPLE_collect:
12899 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
12902 case SAMPLE_diamond:
12903 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12906 case SAMPLE_squash:
12907 /* !!! CHECK THIS !!! */
12909 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
12911 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
12915 case SAMPLE_wonderfall:
12916 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
12920 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12924 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12928 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
12932 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
12936 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12940 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
12943 case SAMPLE_wonder:
12944 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12948 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
12951 case SAMPLE_exit_open:
12952 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
12955 case SAMPLE_exit_leave:
12956 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
12959 case SAMPLE_dynamite:
12960 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12964 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12968 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
12972 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12976 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
12980 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
12984 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12988 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
12994 void ChangeTime(int value)
12996 int *time = (level.time == 0 ? &TimePlayed : &TimeLeft);
13000 /* EMC game engine uses value from time counter of RND game engine */
13001 level.native_em_level->lev->time = *time;
13003 DrawGameValue_Time(*time);
13006 void RaiseScore(int value)
13008 /* EMC game engine and RND game engine have separate score counters */
13009 int *score = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
13010 &level.native_em_level->lev->score : &local_player->score);
13014 DrawGameValue_Score(*score);
13018 void RaiseScore(int value)
13020 local_player->score += value;
13022 DrawGameValue_Score(local_player->score);
13025 void RaiseScoreElement(int element)
13030 case EL_BD_DIAMOND:
13031 case EL_EMERALD_YELLOW:
13032 case EL_EMERALD_RED:
13033 case EL_EMERALD_PURPLE:
13034 case EL_SP_INFOTRON:
13035 RaiseScore(level.score[SC_EMERALD]);
13038 RaiseScore(level.score[SC_DIAMOND]);
13041 RaiseScore(level.score[SC_CRYSTAL]);
13044 RaiseScore(level.score[SC_PEARL]);
13047 case EL_BD_BUTTERFLY:
13048 case EL_SP_ELECTRON:
13049 RaiseScore(level.score[SC_BUG]);
13052 case EL_BD_FIREFLY:
13053 case EL_SP_SNIKSNAK:
13054 RaiseScore(level.score[SC_SPACESHIP]);
13057 case EL_DARK_YAMYAM:
13058 RaiseScore(level.score[SC_YAMYAM]);
13061 RaiseScore(level.score[SC_ROBOT]);
13064 RaiseScore(level.score[SC_PACMAN]);
13067 RaiseScore(level.score[SC_NUT]);
13070 case EL_EM_DYNAMITE:
13071 case EL_SP_DISK_RED:
13072 case EL_DYNABOMB_INCREASE_NUMBER:
13073 case EL_DYNABOMB_INCREASE_SIZE:
13074 case EL_DYNABOMB_INCREASE_POWER:
13075 RaiseScore(level.score[SC_DYNAMITE]);
13077 case EL_SHIELD_NORMAL:
13078 case EL_SHIELD_DEADLY:
13079 RaiseScore(level.score[SC_SHIELD]);
13081 case EL_EXTRA_TIME:
13082 RaiseScore(level.extra_time_score);
13096 case EL_DC_KEY_WHITE:
13097 RaiseScore(level.score[SC_KEY]);
13100 RaiseScore(element_info[element].collect_score);
13105 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
13107 if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
13109 #if defined(NETWORK_AVALIABLE)
13110 if (options.network)
13111 SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
13117 game_status = GAME_MODE_MAIN;
13123 FadeOut(REDRAW_FIELD);
13125 game_status = GAME_MODE_MAIN;
13127 DrawAndFadeInMainMenu(REDRAW_FIELD);
13131 else /* continue playing the game */
13133 if (tape.playing && tape.deactivate_display)
13134 TapeDeactivateDisplayOff(TRUE);
13136 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
13138 if (tape.playing && tape.deactivate_display)
13139 TapeDeactivateDisplayOn();
13143 void RequestQuitGame(boolean ask_if_really_quit)
13145 boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
13146 boolean skip_request = AllPlayersGone || quick_quit;
13148 RequestQuitGameExt(skip_request, quick_quit,
13149 "Do you really want to quit the game ?");
13153 /* ------------------------------------------------------------------------- */
13154 /* random generator functions */
13155 /* ------------------------------------------------------------------------- */
13157 unsigned int InitEngineRandom_RND(long seed)
13159 game.num_random_calls = 0;
13162 unsigned int rnd_seed = InitEngineRandom(seed);
13164 printf("::: START RND: %d\n", rnd_seed);
13169 return InitEngineRandom(seed);
13175 unsigned int RND(int max)
13179 game.num_random_calls++;
13181 return GetEngineRandom(max);
13188 /* ------------------------------------------------------------------------- */
13189 /* game engine snapshot handling functions */
13190 /* ------------------------------------------------------------------------- */
13192 #define ARGS_ADDRESS_AND_SIZEOF(x) (&(x)), (sizeof(x))
13194 struct EngineSnapshotInfo
13196 /* runtime values for custom element collect score */
13197 int collect_score[NUM_CUSTOM_ELEMENTS];
13199 /* runtime values for group element choice position */
13200 int choice_pos[NUM_GROUP_ELEMENTS];
13202 /* runtime values for belt position animations */
13203 int belt_graphic[4 * NUM_BELT_PARTS];
13204 int belt_anim_mode[4 * NUM_BELT_PARTS];
13207 struct EngineSnapshotNodeInfo
13214 static struct EngineSnapshotInfo engine_snapshot_rnd;
13215 static ListNode *engine_snapshot_list = NULL;
13216 static char *snapshot_level_identifier = NULL;
13217 static int snapshot_level_nr = -1;
13219 void FreeEngineSnapshot()
13221 while (engine_snapshot_list != NULL)
13222 deleteNodeFromList(&engine_snapshot_list, engine_snapshot_list->key,
13225 setString(&snapshot_level_identifier, NULL);
13226 snapshot_level_nr = -1;
13229 static void SaveEngineSnapshotValues_RND()
13231 static int belt_base_active_element[4] =
13233 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
13234 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
13235 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
13236 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
13240 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
13242 int element = EL_CUSTOM_START + i;
13244 engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
13247 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
13249 int element = EL_GROUP_START + i;
13251 engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
13254 for (i = 0; i < 4; i++)
13256 for (j = 0; j < NUM_BELT_PARTS; j++)
13258 int element = belt_base_active_element[i] + j;
13259 int graphic = el2img(element);
13260 int anim_mode = graphic_info[graphic].anim_mode;
13262 engine_snapshot_rnd.belt_graphic[i * 4 + j] = graphic;
13263 engine_snapshot_rnd.belt_anim_mode[i * 4 + j] = anim_mode;
13268 static void LoadEngineSnapshotValues_RND()
13270 unsigned long num_random_calls = game.num_random_calls;
13273 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
13275 int element = EL_CUSTOM_START + i;
13277 element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
13280 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
13282 int element = EL_GROUP_START + i;
13284 element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
13287 for (i = 0; i < 4; i++)
13289 for (j = 0; j < NUM_BELT_PARTS; j++)
13291 int graphic = engine_snapshot_rnd.belt_graphic[i * 4 + j];
13292 int anim_mode = engine_snapshot_rnd.belt_anim_mode[i * 4 + j];
13294 graphic_info[graphic].anim_mode = anim_mode;
13298 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
13300 InitRND(tape.random_seed);
13301 for (i = 0; i < num_random_calls; i++)
13305 if (game.num_random_calls != num_random_calls)
13307 Error(ERR_RETURN, "number of random calls out of sync");
13308 Error(ERR_RETURN, "number of random calls should be %d", num_random_calls);
13309 Error(ERR_RETURN, "number of random calls is %d", game.num_random_calls);
13310 Error(ERR_EXIT, "this should not happen -- please debug");
13314 static void SaveEngineSnapshotBuffer(void *buffer, int size)
13316 struct EngineSnapshotNodeInfo *bi =
13317 checked_calloc(sizeof(struct EngineSnapshotNodeInfo));
13319 bi->buffer_orig = buffer;
13320 bi->buffer_copy = checked_malloc(size);
13323 memcpy(bi->buffer_copy, buffer, size);
13325 addNodeToList(&engine_snapshot_list, NULL, bi);
13328 void SaveEngineSnapshot()
13330 FreeEngineSnapshot(); /* free previous snapshot, if needed */
13332 if (level_editor_test_game) /* do not save snapshots from editor */
13335 /* copy some special values to a structure better suited for the snapshot */
13337 SaveEngineSnapshotValues_RND();
13338 SaveEngineSnapshotValues_EM();
13340 /* save values stored in special snapshot structure */
13342 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
13343 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
13345 /* save further RND engine values */
13347 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
13348 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
13349 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
13351 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
13352 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
13353 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
13354 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
13356 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
13357 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
13358 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
13359 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
13360 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
13362 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
13363 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
13364 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
13366 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
13368 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
13370 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
13371 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
13373 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
13374 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
13375 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
13376 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
13377 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
13378 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
13379 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
13380 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
13381 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
13382 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
13383 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
13384 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
13385 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
13386 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
13387 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
13388 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
13389 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
13390 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
13392 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
13393 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
13395 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
13396 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
13397 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
13399 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
13400 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
13402 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
13403 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
13404 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
13405 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
13406 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
13408 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
13409 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
13411 /* save level identification information */
13413 setString(&snapshot_level_identifier, leveldir_current->identifier);
13414 snapshot_level_nr = level_nr;
13417 ListNode *node = engine_snapshot_list;
13420 while (node != NULL)
13422 num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
13427 printf("::: size of engine snapshot: %d bytes\n", num_bytes);
13431 static void LoadEngineSnapshotBuffer(struct EngineSnapshotNodeInfo *bi)
13433 memcpy(bi->buffer_orig, bi->buffer_copy, bi->size);
13436 void LoadEngineSnapshot()
13438 ListNode *node = engine_snapshot_list;
13440 if (engine_snapshot_list == NULL)
13443 while (node != NULL)
13445 LoadEngineSnapshotBuffer((struct EngineSnapshotNodeInfo *)node->content);
13450 /* restore special values from snapshot structure */
13452 LoadEngineSnapshotValues_RND();
13453 LoadEngineSnapshotValues_EM();
13456 boolean CheckEngineSnapshot()
13458 return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
13459 snapshot_level_nr == level_nr);
13463 /* ---------- new game button stuff ---------------------------------------- */
13465 /* graphic position values for game buttons */
13466 #define GAME_BUTTON_XSIZE 30
13467 #define GAME_BUTTON_YSIZE 30
13468 #define GAME_BUTTON_XPOS 5
13469 #define GAME_BUTTON_YPOS 215
13470 #define SOUND_BUTTON_XPOS 5
13471 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
13473 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
13474 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
13475 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
13476 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
13477 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
13478 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
13485 } gamebutton_info[NUM_GAME_BUTTONS] =
13488 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
13493 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
13494 GAME_CTRL_ID_PAUSE,
13498 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
13503 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
13504 SOUND_CTRL_ID_MUSIC,
13505 "background music on/off"
13508 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
13509 SOUND_CTRL_ID_LOOPS,
13510 "sound loops on/off"
13513 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
13514 SOUND_CTRL_ID_SIMPLE,
13515 "normal sounds on/off"
13519 void CreateGameButtons()
13523 for (i = 0; i < NUM_GAME_BUTTONS; i++)
13525 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
13526 struct GadgetInfo *gi;
13529 unsigned long event_mask;
13530 int gd_xoffset, gd_yoffset;
13531 int gd_x1, gd_x2, gd_y1, gd_y2;
13534 gd_xoffset = gamebutton_info[i].x;
13535 gd_yoffset = gamebutton_info[i].y;
13536 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
13537 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
13539 if (id == GAME_CTRL_ID_STOP ||
13540 id == GAME_CTRL_ID_PAUSE ||
13541 id == GAME_CTRL_ID_PLAY)
13543 button_type = GD_TYPE_NORMAL_BUTTON;
13545 event_mask = GD_EVENT_RELEASED;
13546 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
13547 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
13551 button_type = GD_TYPE_CHECK_BUTTON;
13553 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
13554 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
13555 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
13556 event_mask = GD_EVENT_PRESSED;
13557 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
13558 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
13561 gi = CreateGadget(GDI_CUSTOM_ID, id,
13562 GDI_INFO_TEXT, gamebutton_info[i].infotext,
13563 GDI_X, DX + gd_xoffset,
13564 GDI_Y, DY + gd_yoffset,
13565 GDI_WIDTH, GAME_BUTTON_XSIZE,
13566 GDI_HEIGHT, GAME_BUTTON_YSIZE,
13567 GDI_TYPE, button_type,
13568 GDI_STATE, GD_BUTTON_UNPRESSED,
13569 GDI_CHECKED, checked,
13570 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
13571 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
13572 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
13573 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
13574 GDI_EVENT_MASK, event_mask,
13575 GDI_CALLBACK_ACTION, HandleGameButtons,
13579 Error(ERR_EXIT, "cannot create gadget");
13581 game_gadget[id] = gi;
13585 void FreeGameButtons()
13589 for (i = 0; i < NUM_GAME_BUTTONS; i++)
13590 FreeGadget(game_gadget[i]);
13593 static void MapGameButtons()
13597 for (i = 0; i < NUM_GAME_BUTTONS; i++)
13598 MapGadget(game_gadget[i]);
13601 void UnmapGameButtons()
13605 for (i = 0; i < NUM_GAME_BUTTONS; i++)
13606 UnmapGadget(game_gadget[i]);
13609 static void HandleGameButtons(struct GadgetInfo *gi)
13611 int id = gi->custom_id;
13613 if (game_status != GAME_MODE_PLAYING)
13618 case GAME_CTRL_ID_STOP:
13622 RequestQuitGame(TRUE);
13625 case GAME_CTRL_ID_PAUSE:
13626 if (options.network)
13628 #if defined(NETWORK_AVALIABLE)
13630 SendToServer_ContinuePlaying();
13632 SendToServer_PausePlaying();
13636 TapeTogglePause(TAPE_TOGGLE_MANUAL);
13639 case GAME_CTRL_ID_PLAY:
13642 #if defined(NETWORK_AVALIABLE)
13643 if (options.network)
13644 SendToServer_ContinuePlaying();
13648 tape.pausing = FALSE;
13649 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
13654 case SOUND_CTRL_ID_MUSIC:
13655 if (setup.sound_music)
13657 setup.sound_music = FALSE;
13660 else if (audio.music_available)
13662 setup.sound = setup.sound_music = TRUE;
13664 SetAudioMode(setup.sound);
13670 case SOUND_CTRL_ID_LOOPS:
13671 if (setup.sound_loops)
13672 setup.sound_loops = FALSE;
13673 else if (audio.loops_available)
13675 setup.sound = setup.sound_loops = TRUE;
13676 SetAudioMode(setup.sound);
13680 case SOUND_CTRL_ID_SIMPLE:
13681 if (setup.sound_simple)
13682 setup.sound_simple = FALSE;
13683 else if (audio.sound_available)
13685 setup.sound = setup.sound_simple = TRUE;
13686 SetAudioMode(setup.sound);