1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back! *
3 *----------------------------------------------------------*
4 * (c) 1995-2002 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)
60 /* for MovePlayer() */
61 #define MP_NO_ACTION 0
64 #define MP_DONT_RUN_INTO (MP_MOVING | MP_ACTION)
66 /* for ScrollPlayer() */
68 #define SCROLL_GO_ON 1
70 /* for Bang()/Explode() */
71 #define EX_PHASE_START 0
72 #define EX_TYPE_NONE 0
73 #define EX_TYPE_NORMAL (1 << 0)
74 #define EX_TYPE_CENTER (1 << 1)
75 #define EX_TYPE_BORDER (1 << 2)
76 #define EX_TYPE_CROSS (1 << 3)
77 #define EX_TYPE_DYNA (1 << 4)
78 #define EX_TYPE_SINGLE_TILE (EX_TYPE_CENTER | EX_TYPE_BORDER)
80 /* special positions in the game control window (relative to control window) */
83 #define XX_EMERALDS 29
84 #define YY_EMERALDS 54
85 #define XX_DYNAMITE 29
86 #define YY_DYNAMITE 89
95 /* special positions in the game control window (relative to main window) */
96 #define DX_LEVEL (DX + XX_LEVEL)
97 #define DY_LEVEL (DY + YY_LEVEL)
98 #define DX_EMERALDS (DX + XX_EMERALDS)
99 #define DY_EMERALDS (DY + YY_EMERALDS)
100 #define DX_DYNAMITE (DX + XX_DYNAMITE)
101 #define DY_DYNAMITE (DY + YY_DYNAMITE)
102 #define DX_KEYS (DX + XX_KEYS)
103 #define DY_KEYS (DY + YY_KEYS)
104 #define DX_SCORE (DX + XX_SCORE)
105 #define DY_SCORE (DY + YY_SCORE)
106 #define DX_TIME1 (DX + XX_TIME1)
107 #define DX_TIME2 (DX + XX_TIME2)
108 #define DY_TIME (DY + YY_TIME)
110 /* values for delayed check of falling and moving elements and for collision */
111 #define CHECK_DELAY_MOVING 3
112 #define CHECK_DELAY_FALLING 3
113 #define CHECK_DELAY_COLLISION 2
115 /* values for initial player move delay (initial delay counter value) */
116 #define INITIAL_MOVE_DELAY_OFF -1
117 #define INITIAL_MOVE_DELAY_ON 0
119 /* values for player movement speed (which is in fact a delay value) */
120 #define MOVE_DELAY_MIN_SPEED 32
121 #define MOVE_DELAY_NORMAL_SPEED 8
122 #define MOVE_DELAY_HIGH_SPEED 4
123 #define MOVE_DELAY_MAX_SPEED 1
126 #define DOUBLE_MOVE_DELAY(x) (x = (x <= MOVE_DELAY_HIGH_SPEED ? x * 2 : x))
127 #define HALVE_MOVE_DELAY(x) (x = (x >= MOVE_DELAY_HIGH_SPEED ? x / 2 : x))
129 #define DOUBLE_MOVE_DELAY(x) (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
130 #define HALVE_MOVE_DELAY(x) (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
132 #define DOUBLE_PLAYER_SPEED(p) (HALVE_MOVE_DELAY((p)->move_delay_value))
133 #define HALVE_PLAYER_SPEED(p) (DOUBLE_MOVE_DELAY((p)->move_delay_value))
135 /* values for other actions */
136 #define MOVE_STEPSIZE_NORMAL (TILEX / MOVE_DELAY_NORMAL_SPEED)
137 #define MOVE_STEPSIZE_MIN (1)
138 #define MOVE_STEPSIZE_MAX (TILEX)
140 #define GET_DX_FROM_DIR(d) ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
141 #define GET_DY_FROM_DIR(d) ((d) == MV_UP ? -1 : (d) == MV_DOWN ? 1 : 0)
143 #define INIT_GFX_RANDOM() (SimpleRND(1000000))
145 #define GET_NEW_PUSH_DELAY(e) ( (element_info[e].push_delay_fixed) + \
146 RND(element_info[e].push_delay_random))
147 #define GET_NEW_DROP_DELAY(e) ( (element_info[e].drop_delay_fixed) + \
148 RND(element_info[e].drop_delay_random))
149 #define GET_NEW_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
150 RND(element_info[e].move_delay_random))
151 #define GET_MAX_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
152 (element_info[e].move_delay_random))
153 #define GET_NEW_CE_VALUE(e) ( (element_info[e].ce_value_fixed_initial) +\
154 RND(element_info[e].ce_value_random_initial))
155 #define GET_CE_SCORE(e) ( (element_info[e].collect_score))
156 #define GET_CHANGE_DELAY(c) ( ((c)->delay_fixed * (c)->delay_frames) + \
157 RND((c)->delay_random * (c)->delay_frames))
158 #define GET_CE_DELAY_VALUE(c) ( ((c)->delay_fixed) + \
159 RND((c)->delay_random))
163 #define GET_VALID_RUNTIME_ELEMENT(e) \
164 ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
166 #define GET_VALID_FILE_ELEMENT(e) \
167 ((e) >= NUM_FILE_ELEMENTS ? EL_UNKNOWN : (e))
170 #define RESOLVED_REFERENCE_ELEMENT(be, e) \
171 ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START : \
172 (be) + (e) - EL_SELF > EL_CUSTOM_END ? EL_CUSTOM_END : \
173 (be) + (e) - EL_SELF)
175 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs) \
176 ((e) == EL_TRIGGER_PLAYER ? (ch)->actual_trigger_player : \
177 (e) == EL_TRIGGER_ELEMENT ? (ch)->actual_trigger_element : \
178 (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value : \
179 (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score : \
180 (e) == EL_CURRENT_CE_VALUE ? (cv) : \
181 (e) == EL_CURRENT_CE_SCORE ? (cs) : \
182 (e) >= EL_LAST_CE_8 && (e) <= EL_NEXT_CE_8 ? \
183 RESOLVED_REFERENCE_ELEMENT(be, e) : \
186 #define CAN_GROW_INTO(e) \
187 ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
189 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition) \
190 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
193 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition) \
194 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
195 (CAN_MOVE_INTO_ACID(e) && \
196 Feld[x][y] == EL_ACID) || \
199 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition) \
200 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
201 (CAN_MOVE_INTO_ACID(e) && \
202 Feld[x][y] == EL_ACID) || \
205 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition) \
206 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
208 (CAN_MOVE_INTO_ACID(e) && \
209 Feld[x][y] == EL_ACID) || \
210 (DONT_COLLIDE_WITH(e) && \
212 !PLAYER_ENEMY_PROTECTED(x, y))))
214 #define ELEMENT_CAN_ENTER_FIELD(e, x, y) \
215 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
217 #define SATELLITE_CAN_ENTER_FIELD(x, y) \
218 ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
220 #define ANDROID_CAN_ENTER_FIELD(e, x, y) \
221 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
223 #define ANDROID_CAN_CLONE_FIELD(x, y) \
224 (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
225 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
227 #define ENEMY_CAN_ENTER_FIELD(e, x, y) \
228 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
230 #define YAMYAM_CAN_ENTER_FIELD(e, x, y) \
231 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
233 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y) \
234 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
236 #define PACMAN_CAN_ENTER_FIELD(e, x, y) \
237 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
239 #define PIG_CAN_ENTER_FIELD(e, x, y) \
240 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
242 #define PENGUIN_CAN_ENTER_FIELD(e, x, y) \
243 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN ||\
244 IS_FOOD_PENGUIN(Feld[x][y])))
245 #define DRAGON_CAN_ENTER_FIELD(e, x, y) \
246 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
248 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition) \
249 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
251 #define SPRING_CAN_ENTER_FIELD(e, x, y) \
252 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
254 #define SPRING_CAN_BUMP_FROM_FIELD(x, y) \
255 (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER || \
256 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
259 #define GROUP_NR(e) ((e) - EL_GROUP_START)
260 #define IS_IN_GROUP(e, nr) (element_info[e].in_group[nr] == TRUE)
261 #define IS_IN_GROUP_EL(e, ge) (IS_IN_GROUP(e, (ge) - EL_GROUP_START))
263 #define IS_EQUAL_OR_IN_GROUP(e, ge) \
264 (IS_GROUP_ELEMENT(ge) ? IS_IN_GROUP(e, GROUP_NR(ge)) : (e) == (ge))
267 #define MOVE_ENTER_EL(e) (element_info[e].move_enter_element)
269 #define CE_ENTER_FIELD_COND(e, x, y) \
270 (!IS_PLAYER(x, y) && \
271 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
273 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y) \
274 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
276 #define IN_LEV_FIELD_AND_IS_FREE(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
277 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
279 #define ACCESS_FROM(e, d) (element_info[e].access_direction &(d))
280 #define IS_WALKABLE_FROM(e, d) (IS_WALKABLE(e) && ACCESS_FROM(e, d))
281 #define IS_PASSABLE_FROM(e, d) (IS_PASSABLE(e) && ACCESS_FROM(e, d))
282 #define IS_ACCESSIBLE_FROM(e, d) (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
284 /* game button identifiers */
285 #define GAME_CTRL_ID_STOP 0
286 #define GAME_CTRL_ID_PAUSE 1
287 #define GAME_CTRL_ID_PLAY 2
288 #define SOUND_CTRL_ID_MUSIC 3
289 #define SOUND_CTRL_ID_LOOPS 4
290 #define SOUND_CTRL_ID_SIMPLE 5
292 #define NUM_GAME_BUTTONS 6
295 /* forward declaration for internal use */
297 static void CreateField(int, int, int);
299 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
300 static void AdvanceFrameAndPlayerCounters(int);
302 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
303 static boolean MovePlayer(struct PlayerInfo *, int, int);
304 static void ScrollPlayer(struct PlayerInfo *, int);
305 static void ScrollScreen(struct PlayerInfo *, int);
307 int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
309 static void InitBeltMovement(void);
310 static void CloseAllOpenTimegates(void);
311 static void CheckGravityMovement(struct PlayerInfo *);
312 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
313 static void KillPlayerUnlessEnemyProtected(int, int);
314 static void KillPlayerUnlessExplosionProtected(int, int);
316 static void TestIfPlayerTouchesCustomElement(int, int);
317 static void TestIfElementTouchesCustomElement(int, int);
318 static void TestIfElementHitsCustomElement(int, int, int);
320 static void TestIfElementSmashesCustomElement(int, int, int);
323 static void HandleElementChange(int, int, int);
324 static void ExecuteCustomElementAction(int, int, int, int);
325 static boolean ChangeElement(int, int, int, int);
327 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
328 #define CheckTriggeredElementChange(x, y, e, ev) \
329 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
330 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s) \
331 CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
332 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s) \
333 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
334 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p) \
335 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
337 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
338 #define CheckElementChange(x, y, e, te, ev) \
339 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
340 #define CheckElementChangeByPlayer(x, y, e, ev, p, s) \
341 CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
342 #define CheckElementChangeBySide(x, y, e, te, ev, s) \
343 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
345 static void PlayLevelSound(int, int, int);
346 static void PlayLevelSoundNearest(int, int, int);
347 static void PlayLevelSoundAction(int, int, int);
348 static void PlayLevelSoundElementAction(int, int, int, int);
349 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
350 static void PlayLevelSoundActionIfLoop(int, int, int);
351 static void StopLevelSoundActionIfLoop(int, int, int);
352 static void PlayLevelMusic();
354 static void MapGameButtons();
355 static void HandleGameButtons(struct GadgetInfo *);
357 int AmoebeNachbarNr(int, int);
358 void AmoebeUmwandeln(int, int);
359 void ContinueMoving(int, int);
361 void InitMovDir(int, int);
362 void InitAmoebaNr(int, int);
363 int NewHiScore(void);
365 void TestIfGoodThingHitsBadThing(int, int, int);
366 void TestIfBadThingHitsGoodThing(int, int, int);
367 void TestIfPlayerTouchesBadThing(int, int);
368 void TestIfPlayerRunsIntoBadThing(int, int, int);
369 void TestIfBadThingTouchesPlayer(int, int);
370 void TestIfBadThingRunsIntoPlayer(int, int, int);
371 void TestIfFriendTouchesBadThing(int, int);
372 void TestIfBadThingTouchesFriend(int, int);
373 void TestIfBadThingTouchesOtherBadThing(int, int);
375 void KillPlayer(struct PlayerInfo *);
376 void BuryPlayer(struct PlayerInfo *);
377 void RemovePlayer(struct PlayerInfo *);
379 boolean SnapField(struct PlayerInfo *, int, int);
380 boolean DropElement(struct PlayerInfo *);
382 static int getInvisibleActiveFromInvisibleElement(int);
383 static int getInvisibleFromInvisibleActiveElement(int);
385 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
388 /* ------------------------------------------------------------------------- */
389 /* definition of elements that automatically change to other elements after */
390 /* a specified time, eventually calling a function when changing */
391 /* ------------------------------------------------------------------------- */
393 /* forward declaration for changer functions */
394 static void InitBuggyBase(int, int);
395 static void WarnBuggyBase(int, int);
397 static void InitTrap(int, int);
398 static void ActivateTrap(int, int);
399 static void ChangeActiveTrap(int, int);
401 static void InitRobotWheel(int, int);
402 static void RunRobotWheel(int, int);
403 static void StopRobotWheel(int, int);
405 static void InitTimegateWheel(int, int);
406 static void RunTimegateWheel(int, int);
408 static void InitMagicBallDelay(int, int);
409 static void ActivateMagicBall(int, int);
411 static void InitDiagonalMovingElement(int, int);
413 struct ChangingElementInfo
418 void (*pre_change_function)(int x, int y);
419 void (*change_function)(int x, int y);
420 void (*post_change_function)(int x, int y);
423 static struct ChangingElementInfo change_delay_list[] =
474 EL_SWITCHGATE_OPENING,
482 EL_SWITCHGATE_CLOSING,
483 EL_SWITCHGATE_CLOSED,
515 EL_ACID_SPLASH_RIGHT,
524 EL_SP_BUGGY_BASE_ACTIVATING,
531 EL_SP_BUGGY_BASE_ACTIVATING,
532 EL_SP_BUGGY_BASE_ACTIVE,
539 EL_SP_BUGGY_BASE_ACTIVE,
563 EL_ROBOT_WHEEL_ACTIVE,
571 EL_TIMEGATE_SWITCH_ACTIVE,
579 EL_EMC_MAGIC_BALL_ACTIVE,
580 EL_EMC_MAGIC_BALL_ACTIVE,
587 EL_EMC_SPRING_BUMPER_ACTIVE,
588 EL_EMC_SPRING_BUMPER,
595 EL_DIAGONAL_SHRINKING,
608 InitDiagonalMovingElement
624 int push_delay_fixed, push_delay_random;
629 { EL_BALLOON, 0, 0 },
631 { EL_SOKOBAN_OBJECT, 2, 0 },
632 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
633 { EL_SATELLITE, 2, 0 },
634 { EL_SP_DISK_YELLOW, 2, 0 },
636 { EL_UNDEFINED, 0, 0 },
644 move_stepsize_list[] =
646 { EL_AMOEBA_DROP, 2 },
647 { EL_AMOEBA_DROPPING, 2 },
648 { EL_QUICKSAND_FILLING, 1 },
649 { EL_QUICKSAND_EMPTYING, 1 },
650 { EL_MAGIC_WALL_FILLING, 2 },
651 { EL_BD_MAGIC_WALL_FILLING, 2 },
652 { EL_MAGIC_WALL_EMPTYING, 2 },
653 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
663 collect_count_list[] =
666 { EL_BD_DIAMOND, 1 },
667 { EL_EMERALD_YELLOW, 1 },
668 { EL_EMERALD_RED, 1 },
669 { EL_EMERALD_PURPLE, 1 },
671 { EL_SP_INFOTRON, 1 },
683 access_direction_list[] =
685 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
686 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
687 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
688 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
689 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
690 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
691 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
692 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
693 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
694 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
695 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
697 { EL_SP_PORT_LEFT, MV_RIGHT },
698 { EL_SP_PORT_RIGHT, MV_LEFT },
699 { EL_SP_PORT_UP, MV_DOWN },
700 { EL_SP_PORT_DOWN, MV_UP },
701 { EL_SP_PORT_HORIZONTAL, MV_LEFT | MV_RIGHT },
702 { EL_SP_PORT_VERTICAL, MV_UP | MV_DOWN },
703 { EL_SP_PORT_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
704 { EL_SP_GRAVITY_PORT_LEFT, MV_RIGHT },
705 { EL_SP_GRAVITY_PORT_RIGHT, MV_LEFT },
706 { EL_SP_GRAVITY_PORT_UP, MV_DOWN },
707 { EL_SP_GRAVITY_PORT_DOWN, MV_UP },
708 { EL_SP_GRAVITY_ON_PORT_LEFT, MV_RIGHT },
709 { EL_SP_GRAVITY_ON_PORT_RIGHT, MV_LEFT },
710 { EL_SP_GRAVITY_ON_PORT_UP, MV_DOWN },
711 { EL_SP_GRAVITY_ON_PORT_DOWN, MV_UP },
712 { EL_SP_GRAVITY_OFF_PORT_LEFT, MV_RIGHT },
713 { EL_SP_GRAVITY_OFF_PORT_RIGHT, MV_LEFT },
714 { EL_SP_GRAVITY_OFF_PORT_UP, MV_DOWN },
715 { EL_SP_GRAVITY_OFF_PORT_DOWN, MV_UP },
717 { EL_UNDEFINED, MV_NONE }
720 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
722 #define IS_AUTO_CHANGING(e) (element_info[e].has_change_event[CE_DELAY])
723 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
724 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
725 IS_JUST_CHANGING(x, y))
727 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
729 /* static variables for playfield scan mode (scanning forward or backward) */
730 static int playfield_scan_start_x = 0;
731 static int playfield_scan_start_y = 0;
732 static int playfield_scan_delta_x = 1;
733 static int playfield_scan_delta_y = 1;
735 #define SCAN_PLAYFIELD(x, y) for ((y) = playfield_scan_start_y; \
736 (y) >= 0 && (y) <= lev_fieldy - 1; \
737 (y) += playfield_scan_delta_y) \
738 for ((x) = playfield_scan_start_x; \
739 (x) >= 0 && (x) <= lev_fieldx - 1; \
740 (x) += playfield_scan_delta_x) \
743 void DEBUG_SetMaximumDynamite()
747 for (i = 0; i < MAX_INVENTORY_SIZE; i++)
748 if (local_player->inventory_size < MAX_INVENTORY_SIZE)
749 local_player->inventory_element[local_player->inventory_size++] =
754 static void InitPlayfieldScanModeVars()
756 if (game.use_reverse_scan_direction)
758 playfield_scan_start_x = lev_fieldx - 1;
759 playfield_scan_start_y = lev_fieldy - 1;
761 playfield_scan_delta_x = -1;
762 playfield_scan_delta_y = -1;
766 playfield_scan_start_x = 0;
767 playfield_scan_start_y = 0;
769 playfield_scan_delta_x = 1;
770 playfield_scan_delta_y = 1;
774 static void InitPlayfieldScanMode(int mode)
776 game.use_reverse_scan_direction =
777 (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
779 InitPlayfieldScanModeVars();
782 static int get_move_delay_from_stepsize(int move_stepsize)
785 MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
787 /* make sure that stepsize value is always a power of 2 */
788 move_stepsize = (1 << log_2(move_stepsize));
790 return TILEX / move_stepsize;
793 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
796 int player_nr = player->index_nr;
797 int move_delay = get_move_delay_from_stepsize(move_stepsize);
798 boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
800 /* do no immediately change move delay -- the player might just be moving */
801 player->move_delay_value_next = move_delay;
803 /* information if player can move must be set separately */
804 player->cannot_move = cannot_move;
808 player->move_delay = game.initial_move_delay[player_nr];
809 player->move_delay_value = game.initial_move_delay_value[player_nr];
811 player->move_delay_value_next = -1;
813 player->move_delay_reset_counter = 0;
817 void GetPlayerConfig()
819 if (!audio.sound_available)
820 setup.sound_simple = FALSE;
822 if (!audio.loops_available)
823 setup.sound_loops = FALSE;
825 if (!audio.music_available)
826 setup.sound_music = FALSE;
828 if (!video.fullscreen_available)
829 setup.fullscreen = FALSE;
831 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
833 SetAudioMode(setup.sound);
837 static int getBeltNrFromBeltElement(int element)
839 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
840 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
841 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
844 static int getBeltNrFromBeltActiveElement(int element)
846 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
847 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
848 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
851 static int getBeltNrFromBeltSwitchElement(int element)
853 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
854 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
855 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
858 static int getBeltDirNrFromBeltSwitchElement(int element)
860 static int belt_base_element[4] =
862 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
863 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
864 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
865 EL_CONVEYOR_BELT_4_SWITCH_LEFT
868 int belt_nr = getBeltNrFromBeltSwitchElement(element);
869 int belt_dir_nr = element - belt_base_element[belt_nr];
871 return (belt_dir_nr % 3);
874 static int getBeltDirFromBeltSwitchElement(int element)
876 static int belt_move_dir[3] =
883 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
885 return belt_move_dir[belt_dir_nr];
888 static int get_element_from_group_element(int element)
890 if (IS_GROUP_ELEMENT(element))
892 struct ElementGroupInfo *group = element_info[element].group;
893 int last_anim_random_frame = gfx.anim_random_frame;
896 if (group->choice_mode == ANIM_RANDOM)
897 gfx.anim_random_frame = RND(group->num_elements_resolved);
899 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
900 group->choice_mode, 0,
903 if (group->choice_mode == ANIM_RANDOM)
904 gfx.anim_random_frame = last_anim_random_frame;
908 element = group->element_resolved[element_pos];
914 static void InitPlayerField(int x, int y, int element, boolean init_game)
916 if (element == EL_SP_MURPHY)
920 if (stored_player[0].present)
922 Feld[x][y] = EL_SP_MURPHY_CLONE;
928 stored_player[0].use_murphy = TRUE;
930 if (!level.use_artwork_element[0])
931 stored_player[0].artwork_element = EL_SP_MURPHY;
934 Feld[x][y] = EL_PLAYER_1;
940 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
941 int jx = player->jx, jy = player->jy;
943 player->present = TRUE;
945 player->block_last_field = (element == EL_SP_MURPHY ?
946 level.sp_block_last_field :
947 level.block_last_field);
949 /* ---------- initialize player's last field block delay --------------- */
951 /* always start with reliable default value (no adjustment needed) */
952 player->block_delay_adjustment = 0;
954 /* special case 1: in Supaplex, Murphy blocks last field one more frame */
955 if (player->block_last_field && element == EL_SP_MURPHY)
956 player->block_delay_adjustment = 1;
958 /* special case 2: in game engines before 3.1.1, blocking was different */
959 if (game.use_block_last_field_bug)
960 player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
962 if (!options.network || player->connected)
964 player->active = TRUE;
966 /* remove potentially duplicate players */
967 if (StorePlayer[jx][jy] == Feld[x][y])
968 StorePlayer[jx][jy] = 0;
970 StorePlayer[x][y] = Feld[x][y];
974 printf("Player %d activated.\n", player->element_nr);
975 printf("[Local player is %d and currently %s.]\n",
976 local_player->element_nr,
977 local_player->active ? "active" : "not active");
981 Feld[x][y] = EL_EMPTY;
983 player->jx = player->last_jx = x;
984 player->jy = player->last_jy = y;
988 static void InitField(int x, int y, boolean init_game)
990 int element = Feld[x][y];
999 InitPlayerField(x, y, element, init_game);
1002 case EL_SOKOBAN_FIELD_PLAYER:
1003 element = Feld[x][y] = EL_PLAYER_1;
1004 InitField(x, y, init_game);
1006 element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1007 InitField(x, y, init_game);
1010 case EL_SOKOBAN_FIELD_EMPTY:
1011 local_player->sokobanfields_still_needed++;
1015 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1016 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1017 else if (x > 0 && Feld[x-1][y] == EL_ACID)
1018 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1019 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1020 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1021 else if (y > 0 && Feld[x][y-1] == EL_ACID)
1022 Feld[x][y] = EL_ACID_POOL_BOTTOM;
1023 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1024 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1033 case EL_SPACESHIP_RIGHT:
1034 case EL_SPACESHIP_UP:
1035 case EL_SPACESHIP_LEFT:
1036 case EL_SPACESHIP_DOWN:
1037 case EL_BD_BUTTERFLY:
1038 case EL_BD_BUTTERFLY_RIGHT:
1039 case EL_BD_BUTTERFLY_UP:
1040 case EL_BD_BUTTERFLY_LEFT:
1041 case EL_BD_BUTTERFLY_DOWN:
1043 case EL_BD_FIREFLY_RIGHT:
1044 case EL_BD_FIREFLY_UP:
1045 case EL_BD_FIREFLY_LEFT:
1046 case EL_BD_FIREFLY_DOWN:
1047 case EL_PACMAN_RIGHT:
1049 case EL_PACMAN_LEFT:
1050 case EL_PACMAN_DOWN:
1052 case EL_YAMYAM_LEFT:
1053 case EL_YAMYAM_RIGHT:
1055 case EL_YAMYAM_DOWN:
1056 case EL_DARK_YAMYAM:
1059 case EL_SP_SNIKSNAK:
1060 case EL_SP_ELECTRON:
1069 case EL_AMOEBA_FULL:
1074 case EL_AMOEBA_DROP:
1075 if (y == lev_fieldy - 1)
1077 Feld[x][y] = EL_AMOEBA_GROWING;
1078 Store[x][y] = EL_AMOEBA_WET;
1082 case EL_DYNAMITE_ACTIVE:
1083 case EL_SP_DISK_RED_ACTIVE:
1084 case EL_DYNABOMB_PLAYER_1_ACTIVE:
1085 case EL_DYNABOMB_PLAYER_2_ACTIVE:
1086 case EL_DYNABOMB_PLAYER_3_ACTIVE:
1087 case EL_DYNABOMB_PLAYER_4_ACTIVE:
1088 MovDelay[x][y] = 96;
1091 case EL_EM_DYNAMITE_ACTIVE:
1092 MovDelay[x][y] = 32;
1096 local_player->lights_still_needed++;
1100 local_player->friends_still_needed++;
1105 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1108 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1109 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1110 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1111 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1112 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1113 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1114 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1115 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1116 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1117 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1118 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1119 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1122 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1123 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1124 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1126 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
1128 game.belt_dir[belt_nr] = belt_dir;
1129 game.belt_dir_nr[belt_nr] = belt_dir_nr;
1131 else /* more than one switch -- set it like the first switch */
1133 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1138 #if !USE_BOTH_SWITCHGATE_SWITCHES
1139 case EL_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
1141 Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
1145 case EL_LIGHT_SWITCH_ACTIVE:
1147 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1150 case EL_INVISIBLE_STEELWALL:
1151 case EL_INVISIBLE_WALL:
1152 case EL_INVISIBLE_SAND:
1153 if (game.light_time_left > 0 ||
1154 game.lenses_time_left > 0)
1155 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1158 case EL_EMC_MAGIC_BALL:
1159 if (game.ball_state)
1160 Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1163 case EL_EMC_MAGIC_BALL_SWITCH:
1164 if (game.ball_state)
1165 Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1170 if (IS_CUSTOM_ELEMENT(element))
1172 if (CAN_MOVE(element))
1175 #if USE_NEW_CUSTOM_VALUE
1176 if (!element_info[element].use_last_ce_value || init_game)
1177 CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1181 if (IS_CUSTOM_ELEMENT(element) && CAN_MOVE(element))
1184 else if (IS_GROUP_ELEMENT(element))
1187 Feld[x][y] = get_element_from_group_element(element);
1189 InitField(x, y, init_game);
1191 struct ElementGroupInfo *group = element_info[element].group;
1192 int last_anim_random_frame = gfx.anim_random_frame;
1195 if (group->choice_mode == ANIM_RANDOM)
1196 gfx.anim_random_frame = RND(group->num_elements_resolved);
1198 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1199 group->choice_mode, 0,
1202 if (group->choice_mode == ANIM_RANDOM)
1203 gfx.anim_random_frame = last_anim_random_frame;
1205 group->choice_pos++;
1207 Feld[x][y] = group->element_resolved[element_pos];
1209 InitField(x, y, init_game);
1218 CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1223 #if USE_NEW_CUSTOM_VALUE
1226 CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1228 CustomValue[x][y] = element_info[Feld[x][y]].custom_value_initial;
1236 static inline void InitField_WithBug1(int x, int y, boolean init_game)
1238 InitField(x, y, init_game);
1240 /* not needed to call InitMovDir() -- already done by InitField()! */
1241 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1242 CAN_MOVE(Feld[x][y]))
1246 static inline void InitField_WithBug2(int x, int y, boolean init_game)
1248 int old_element = Feld[x][y];
1250 InitField(x, y, init_game);
1252 /* not needed to call InitMovDir() -- already done by InitField()! */
1253 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1254 CAN_MOVE(old_element) &&
1255 (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1258 /* this case is in fact a combination of not less than three bugs:
1259 first, it calls InitMovDir() for elements that can move, although this is
1260 already done by InitField(); then, it checks the element that was at this
1261 field _before_ the call to InitField() (which can change it); lastly, it
1262 was not called for "mole with direction" elements, which were treated as
1263 "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1267 inline void DrawGameValue_Emeralds(int value)
1269 int xpos = (3 * 14 - 3 * getFontWidth(FONT_TEXT_2)) / 2;
1271 DrawText(DX_EMERALDS + xpos, DY_EMERALDS, int2str(value, 3), FONT_TEXT_2);
1274 inline void DrawGameValue_Dynamite(int value)
1276 int xpos = (3 * 14 - 3 * getFontWidth(FONT_TEXT_2)) / 2;
1278 DrawText(DX_DYNAMITE + xpos, DY_DYNAMITE, int2str(value, 3), FONT_TEXT_2);
1281 inline void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
1283 int base_key_graphic = EL_KEY_1;
1286 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
1287 base_key_graphic = EL_EM_KEY_1;
1289 /* currently only 4 of 8 possible keys are displayed */
1290 for (i = 0; i < STD_NUM_KEYS; i++)
1293 DrawMiniGraphicExt(drawto, DX_KEYS + i * MINI_TILEX, DY_KEYS,
1294 el2edimg(base_key_graphic + i));
1296 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
1297 DOOR_GFX_PAGEX5 + XX_KEYS + i * MINI_TILEX, YY_KEYS,
1298 MINI_TILEX, MINI_TILEY, DX_KEYS + i * MINI_TILEX, DY_KEYS);
1302 inline void DrawGameValue_Score(int value)
1304 int xpos = (5 * 14 - 5 * getFontWidth(FONT_TEXT_2)) / 2;
1306 DrawText(DX_SCORE + xpos, DY_SCORE, int2str(value, 5), FONT_TEXT_2);
1309 inline void DrawGameValue_Time(int value)
1311 int xpos3 = (3 * 14 - 3 * getFontWidth(FONT_TEXT_2)) / 2;
1312 int xpos4 = (4 * 10 - 4 * getFontWidth(FONT_LEVEL_NUMBER)) / 2;
1314 /* clear background if value just changed its size */
1315 if (value == 999 || value == 1000)
1316 ClearRectangle(drawto, DX_TIME1, DY_TIME, 14 * 3, 14);
1319 DrawText(DX_TIME1 + xpos3, DY_TIME, int2str(value, 3), FONT_TEXT_2);
1321 DrawText(DX_TIME2 + xpos4, DY_TIME, int2str(value, 4), FONT_LEVEL_NUMBER);
1324 inline void DrawGameValue_Level(int value)
1327 DrawText(DX_LEVEL, DY_LEVEL, int2str(value, 2), FONT_TEXT_2);
1330 /* misuse area for displaying emeralds to draw bigger level number */
1331 DrawTextExt(drawto, DX_EMERALDS, DY_EMERALDS,
1332 int2str(value, 3), FONT_LEVEL_NUMBER, BLIT_OPAQUE);
1334 /* now copy it to the area for displaying level number */
1335 BlitBitmap(drawto, drawto,
1336 DX_EMERALDS, DY_EMERALDS + 1,
1337 getFontWidth(FONT_LEVEL_NUMBER) * 3,
1338 getFontHeight(FONT_LEVEL_NUMBER) - 1,
1339 DX_LEVEL - 1, DY_LEVEL + 1);
1341 /* restore the area for displaying emeralds */
1342 DrawGameValue_Emeralds(local_player->gems_still_needed);
1344 /* yes, this is all really ugly :-) */
1348 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
1351 int key[MAX_NUM_KEYS];
1354 for (i = 0; i < MAX_NUM_KEYS; i++)
1355 key[i] = key_bits & (1 << i);
1357 DrawGameValue_Level(level_nr);
1359 DrawGameValue_Emeralds(emeralds);
1360 DrawGameValue_Dynamite(dynamite);
1361 DrawGameValue_Score(score);
1362 DrawGameValue_Time(time);
1364 DrawGameValue_Keys(key);
1367 void DrawGameDoorValues()
1369 int dynamite_state = 0;
1373 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
1375 DrawGameDoorValues_EM();
1381 DrawGameValue_Level(level_nr);
1383 DrawGameValue_Emeralds(local_player->gems_still_needed);
1384 DrawGameValue_Dynamite(local_player->inventory_size);
1385 DrawGameValue_Score(local_player->score);
1386 DrawGameValue_Time(TimeLeft);
1390 if (game.centered_player_nr == -1)
1392 for (i = 0; i < MAX_PLAYERS; i++)
1394 for (j = 0; j < MAX_NUM_KEYS; j++)
1395 if (stored_player[i].key[j])
1396 key_bits |= (1 << j);
1398 dynamite_state += stored_player[i].inventory_size;
1402 DrawGameValue_Keys(stored_player[i].key);
1407 int player_nr = game.centered_player_nr;
1409 for (i = 0; i < MAX_NUM_KEYS; i++)
1410 if (stored_player[player_nr].key[i])
1411 key_bits |= (1 << i);
1413 dynamite_state = stored_player[player_nr].inventory_size;
1416 DrawAllGameValues(local_player->gems_still_needed, dynamite_state,
1417 local_player->score, TimeLeft, key_bits);
1422 static void resolve_group_element(int group_element, int recursion_depth)
1424 static int group_nr;
1425 static struct ElementGroupInfo *group;
1426 struct ElementGroupInfo *actual_group = element_info[group_element].group;
1429 if (recursion_depth > NUM_GROUP_ELEMENTS) /* recursion too deep */
1431 Error(ERR_WARN, "recursion too deep when resolving group element %d",
1432 group_element - EL_GROUP_START + 1);
1434 /* replace element which caused too deep recursion by question mark */
1435 group->element_resolved[group->num_elements_resolved++] = EL_UNKNOWN;
1440 if (recursion_depth == 0) /* initialization */
1442 group = element_info[group_element].group;
1443 group_nr = group_element - EL_GROUP_START;
1445 group->num_elements_resolved = 0;
1446 group->choice_pos = 0;
1449 for (i = 0; i < actual_group->num_elements; i++)
1451 int element = actual_group->element[i];
1453 if (group->num_elements_resolved == NUM_FILE_ELEMENTS)
1456 if (IS_GROUP_ELEMENT(element))
1457 resolve_group_element(element, recursion_depth + 1);
1460 group->element_resolved[group->num_elements_resolved++] = element;
1461 element_info[element].in_group[group_nr] = TRUE;
1468 static void replace_reference_element(int base_element, int *element)
1470 if (*element >= EL_LAST_CE_8 && *element <= EL_NEXT_CE_8)
1472 *element = base_element + *element - EL_SELF;
1473 *element = (*element < EL_CUSTOM_START ? EL_CUSTOM_START :
1474 *element > EL_CUSTOM_END ? EL_CUSTOM_END : *element);
1480 =============================================================================
1482 -----------------------------------------------------------------------------
1483 initialize game engine due to level / tape version number
1484 =============================================================================
1487 static void InitGameEngine()
1489 int i, j, k, l, x, y;
1491 /* set game engine from tape file when re-playing, else from level file */
1492 game.engine_version = (tape.playing ? tape.engine_version :
1493 level.game_version);
1495 /* ---------------------------------------------------------------------- */
1496 /* set flags for bugs and changes according to active game engine version */
1497 /* ---------------------------------------------------------------------- */
1500 Summary of bugfix/change:
1501 Fixed handling for custom elements that change when pushed by the player.
1503 Fixed/changed in version:
1507 Before 3.1.0, custom elements that "change when pushing" changed directly
1508 after the player started pushing them (until then handled in "DigField()").
1509 Since 3.1.0, these custom elements are not changed until the "pushing"
1510 move of the element is finished (now handled in "ContinueMoving()").
1512 Affected levels/tapes:
1513 The first condition is generally needed for all levels/tapes before version
1514 3.1.0, which might use the old behaviour before it was changed; known tapes
1515 that are affected are some tapes from the level set "Walpurgis Gardens" by
1517 The second condition is an exception from the above case and is needed for
1518 the special case of tapes recorded with game (not engine!) version 3.1.0 or
1519 above (including some development versions of 3.1.0), but before it was
1520 known that this change would break tapes like the above and was fixed in
1521 3.1.1, so that the changed behaviour was active although the engine version
1522 while recording maybe was before 3.1.0. There is at least one tape that is
1523 affected by this exception, which is the tape for the one-level set "Bug
1524 Machine" by Juergen Bonhagen.
1527 game.use_change_when_pushing_bug =
1528 (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1530 tape.game_version >= VERSION_IDENT(3,1,0,0) &&
1531 tape.game_version < VERSION_IDENT(3,1,1,0)));
1534 Summary of bugfix/change:
1535 Fixed handling for blocking the field the player leaves when moving.
1537 Fixed/changed in version:
1541 Before 3.1.1, when "block last field when moving" was enabled, the field
1542 the player is leaving when moving was blocked for the time of the move,
1543 and was directly unblocked afterwards. This resulted in the last field
1544 being blocked for exactly one less than the number of frames of one player
1545 move. Additionally, even when blocking was disabled, the last field was
1546 blocked for exactly one frame.
1547 Since 3.1.1, due to changes in player movement handling, the last field
1548 is not blocked at all when blocking is disabled. When blocking is enabled,
1549 the last field is blocked for exactly the number of frames of one player
1550 move. Additionally, if the player is Murphy, the hero of Supaplex, the
1551 last field is blocked for exactly one more than the number of frames of
1554 Affected levels/tapes:
1555 (!!! yet to be determined -- probably many !!!)
1558 game.use_block_last_field_bug =
1559 (game.engine_version < VERSION_IDENT(3,1,1,0));
1562 Summary of bugfix/change:
1563 Changed behaviour of CE changes with multiple changes per single frame.
1565 Fixed/changed in version:
1569 Before 3.2.0-6, only one single CE change was allowed in each engine frame.
1570 This resulted in race conditions where CEs seem to behave strange in some
1571 situations (where triggered CE changes were just skipped because there was
1572 already a CE change on that tile in the playfield in that engine frame).
1573 Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
1574 (The number of changes per frame must be limited in any case, because else
1575 it is easily possible to define CE changes that would result in an infinite
1576 loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
1577 should be set large enough so that it would only be reached in cases where
1578 the corresponding CE change conditions run into a loop. Therefore, it seems
1579 to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
1580 maximal number of change pages for custom elements.)
1582 Affected levels/tapes:
1586 #if USE_ONLY_ONE_CHANGE_PER_FRAME
1587 game.max_num_changes_per_frame = 1;
1589 game.max_num_changes_per_frame =
1590 (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
1593 /* ---------------------------------------------------------------------- */
1595 /* default scan direction: scan playfield from top/left to bottom/right */
1596 InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
1598 /* dynamically adjust element properties according to game engine version */
1599 InitElementPropertiesEngine(game.engine_version);
1602 printf("level %d: level version == %06d\n", level_nr, level.game_version);
1603 printf(" tape version == %06d [%s] [file: %06d]\n",
1604 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
1606 printf(" => game.engine_version == %06d\n", game.engine_version);
1610 /* ---------- recursively resolve group elements ------------------------- */
1612 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1613 for (j = 0; j < NUM_GROUP_ELEMENTS; j++)
1614 element_info[i].in_group[j] = FALSE;
1616 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
1617 resolve_group_element(EL_GROUP_START + i, 0);
1620 /* ---------- initialize player's initial move delay --------------------- */
1623 /* dynamically adjust player properties according to level information */
1624 for (i = 0; i < MAX_PLAYERS; i++)
1625 game.initial_move_delay_value[i] =
1626 get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
1628 /* dynamically adjust player properties according to level information */
1629 game.initial_move_delay_value =
1630 (level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED);
1633 /* dynamically adjust player properties according to game engine version */
1634 for (i = 0; i < MAX_PLAYERS; i++)
1635 game.initial_move_delay[i] =
1636 (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
1637 game.initial_move_delay_value[i] : 0);
1639 /* ---------- initialize player's initial push delay --------------------- */
1641 /* dynamically adjust player properties according to game engine version */
1642 game.initial_push_delay_value =
1643 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
1645 /* ---------- initialize changing elements ------------------------------- */
1647 /* initialize changing elements information */
1648 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1650 struct ElementInfo *ei = &element_info[i];
1652 /* this pointer might have been changed in the level editor */
1653 ei->change = &ei->change_page[0];
1655 if (!IS_CUSTOM_ELEMENT(i))
1657 ei->change->target_element = EL_EMPTY_SPACE;
1658 ei->change->delay_fixed = 0;
1659 ei->change->delay_random = 0;
1660 ei->change->delay_frames = 1;
1663 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1665 ei->has_change_event[j] = FALSE;
1667 ei->event_page_nr[j] = 0;
1668 ei->event_page[j] = &ei->change_page[0];
1672 /* add changing elements from pre-defined list */
1673 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
1675 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
1676 struct ElementInfo *ei = &element_info[ch_delay->element];
1678 ei->change->target_element = ch_delay->target_element;
1679 ei->change->delay_fixed = ch_delay->change_delay;
1681 ei->change->pre_change_function = ch_delay->pre_change_function;
1682 ei->change->change_function = ch_delay->change_function;
1683 ei->change->post_change_function = ch_delay->post_change_function;
1685 ei->change->can_change = TRUE;
1686 ei->change->can_change_or_has_action = TRUE;
1688 ei->has_change_event[CE_DELAY] = TRUE;
1690 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
1691 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
1694 /* ---------- initialize internal run-time variables ------------- */
1696 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1698 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1700 for (j = 0; j < ei->num_change_pages; j++)
1702 ei->change_page[j].can_change_or_has_action =
1703 (ei->change_page[j].can_change |
1704 ei->change_page[j].has_action);
1708 /* add change events from custom element configuration */
1709 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1711 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1713 for (j = 0; j < ei->num_change_pages; j++)
1715 if (!ei->change_page[j].can_change_or_has_action)
1718 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1720 /* only add event page for the first page found with this event */
1721 if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
1723 ei->has_change_event[k] = TRUE;
1725 ei->event_page_nr[k] = j;
1726 ei->event_page[k] = &ei->change_page[j];
1732 /* ---------- initialize run-time trigger player and element ------------- */
1734 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1736 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1738 for (j = 0; j < ei->num_change_pages; j++)
1740 ei->change_page[j].actual_trigger_element = EL_EMPTY;
1741 ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
1742 ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
1743 ei->change_page[j].actual_trigger_ce_value = 0;
1744 ei->change_page[j].actual_trigger_ce_score = 0;
1748 /* ---------- initialize trigger events ---------------------------------- */
1750 /* initialize trigger events information */
1751 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1752 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1753 trigger_events[i][j] = FALSE;
1755 /* add trigger events from element change event properties */
1756 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1758 struct ElementInfo *ei = &element_info[i];
1760 for (j = 0; j < ei->num_change_pages; j++)
1762 if (!ei->change_page[j].can_change_or_has_action)
1765 if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
1767 int trigger_element = ei->change_page[j].trigger_element;
1769 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1771 if (ei->change_page[j].has_event[k])
1773 if (IS_GROUP_ELEMENT(trigger_element))
1775 struct ElementGroupInfo *group =
1776 element_info[trigger_element].group;
1778 for (l = 0; l < group->num_elements_resolved; l++)
1779 trigger_events[group->element_resolved[l]][k] = TRUE;
1782 else if (trigger_element == EL_ANY_ELEMENT)
1783 for (l = 0; l < MAX_NUM_ELEMENTS; l++)
1784 trigger_events[l][k] = TRUE;
1787 trigger_events[trigger_element][k] = TRUE;
1794 /* ---------- initialize push delay -------------------------------------- */
1796 /* initialize push delay values to default */
1797 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1799 if (!IS_CUSTOM_ELEMENT(i))
1802 /* set default push delay values (corrected since version 3.0.7-1) */
1803 if (game.engine_version < VERSION_IDENT(3,0,7,1))
1805 element_info[i].push_delay_fixed = 2;
1806 element_info[i].push_delay_random = 8;
1810 element_info[i].push_delay_fixed = 8;
1811 element_info[i].push_delay_random = 8;
1814 element_info[i].push_delay_fixed = game.default_push_delay_fixed;
1815 element_info[i].push_delay_random = game.default_push_delay_random;
1820 /* set push delay value for certain elements from pre-defined list */
1821 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
1823 int e = push_delay_list[i].element;
1825 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
1826 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
1829 /* set push delay value for Supaplex elements for newer engine versions */
1830 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
1832 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1834 if (IS_SP_ELEMENT(i))
1836 /* set SP push delay to just enough to push under a falling zonk */
1837 int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
1839 element_info[i].push_delay_fixed = delay;
1840 element_info[i].push_delay_random = 0;
1845 /* ---------- initialize move stepsize ----------------------------------- */
1847 /* initialize move stepsize values to default */
1848 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1849 if (!IS_CUSTOM_ELEMENT(i))
1850 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
1852 /* set move stepsize value for certain elements from pre-defined list */
1853 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
1855 int e = move_stepsize_list[i].element;
1857 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
1860 /* ---------- initialize collect score ----------------------------------- */
1862 /* initialize collect score values for custom elements from initial value */
1863 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1864 if (IS_CUSTOM_ELEMENT(i))
1865 element_info[i].collect_score = element_info[i].collect_score_initial;
1867 /* ---------- initialize collect count ----------------------------------- */
1869 /* initialize collect count values for non-custom elements */
1870 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1871 if (!IS_CUSTOM_ELEMENT(i))
1872 element_info[i].collect_count_initial = 0;
1874 /* add collect count values for all elements from pre-defined list */
1875 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
1876 element_info[collect_count_list[i].element].collect_count_initial =
1877 collect_count_list[i].count;
1879 /* ---------- initialize access direction -------------------------------- */
1881 /* initialize access direction values to default (access from every side) */
1882 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1883 if (!IS_CUSTOM_ELEMENT(i))
1884 element_info[i].access_direction = MV_ALL_DIRECTIONS;
1886 /* set access direction value for certain elements from pre-defined list */
1887 for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
1888 element_info[access_direction_list[i].element].access_direction =
1889 access_direction_list[i].direction;
1891 /* ---------- initialize explosion content ------------------------------- */
1892 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1894 if (IS_CUSTOM_ELEMENT(i))
1897 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
1899 /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
1901 element_info[i].content.e[x][y] =
1902 (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
1903 i == EL_PLAYER_2 ? EL_EMERALD_RED :
1904 i == EL_PLAYER_3 ? EL_EMERALD :
1905 i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
1906 i == EL_MOLE ? EL_EMERALD_RED :
1907 i == EL_PENGUIN ? EL_EMERALD_PURPLE :
1908 i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
1909 i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
1910 i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
1911 i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
1912 i == EL_WALL_EMERALD ? EL_EMERALD :
1913 i == EL_WALL_DIAMOND ? EL_DIAMOND :
1914 i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
1915 i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
1916 i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
1917 i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
1918 i == EL_WALL_PEARL ? EL_PEARL :
1919 i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
1925 /* ---------- initialize reference elements ------------------------------- */
1926 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1928 int element = EL_CUSTOM_START + i;
1929 struct ElementInfo *ei = &element_info[element];
1931 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
1932 replace_reference_element(element, &ei->content.e[x][y]);
1934 for (j = 0; j < ei->num_change_pages; j++)
1936 struct ElementChangeInfo *change = &ei->change_page[j];
1938 replace_reference_element(element, &change->target_element);
1939 replace_reference_element(element, &change->trigger_element);
1941 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
1942 replace_reference_element(element, &change->target_content.e[x][y]);
1948 int get_num_special_action(int element, int action_first, int action_last)
1950 int num_special_action = 0;
1953 for (i = action_first; i <= action_last; i++)
1955 boolean found = FALSE;
1957 for (j = 0; j < NUM_DIRECTIONS; j++)
1958 if (el_act_dir2img(element, i, j) !=
1959 el_act_dir2img(element, ACTION_DEFAULT, j))
1963 num_special_action++;
1969 printf("::: %d->%d: %d\n", action_first, action_last, num_special_action);
1972 return num_special_action;
1976 =============================================================================
1978 -----------------------------------------------------------------------------
1979 initialize and start new game
1980 =============================================================================
1985 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
1986 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
1987 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
1992 /* don't play tapes over network */
1993 network_playing = (options.network && !tape.playing);
1995 for (i = 0; i < MAX_PLAYERS; i++)
1997 struct PlayerInfo *player = &stored_player[i];
1999 player->index_nr = i;
2000 player->index_bit = (1 << i);
2001 player->element_nr = EL_PLAYER_1 + i;
2003 player->present = FALSE;
2004 player->active = FALSE;
2007 player->effective_action = 0;
2008 player->programmed_action = 0;
2011 player->gems_still_needed = level.gems_needed;
2012 player->sokobanfields_still_needed = 0;
2013 player->lights_still_needed = 0;
2014 player->friends_still_needed = 0;
2016 for (j = 0; j < MAX_NUM_KEYS; j++)
2017 player->key[j] = FALSE;
2019 player->dynabomb_count = 0;
2020 player->dynabomb_size = 1;
2021 player->dynabombs_left = 0;
2022 player->dynabomb_xl = FALSE;
2024 player->MovDir = MV_NONE;
2027 player->GfxDir = MV_NONE;
2028 player->GfxAction = ACTION_DEFAULT;
2030 player->StepFrame = 0;
2032 player->use_murphy = FALSE;
2033 player->artwork_element =
2034 (level.use_artwork_element[i] ? level.artwork_element[i] :
2035 player->element_nr);
2037 player->block_last_field = FALSE; /* initialized in InitPlayerField() */
2038 player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
2040 player->gravity = level.initial_player_gravity[i];
2042 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
2044 player->actual_frame_counter = 0;
2046 player->step_counter = 0;
2048 player->last_move_dir = MV_NONE;
2050 player->is_active = FALSE;
2052 player->is_waiting = FALSE;
2053 player->is_moving = FALSE;
2054 player->is_auto_moving = FALSE;
2055 player->is_digging = FALSE;
2056 player->is_snapping = FALSE;
2057 player->is_collecting = FALSE;
2058 player->is_pushing = FALSE;
2059 player->is_switching = FALSE;
2060 player->is_dropping = FALSE;
2061 player->is_dropping_pressed = FALSE;
2063 player->is_bored = FALSE;
2064 player->is_sleeping = FALSE;
2066 player->frame_counter_bored = -1;
2067 player->frame_counter_sleeping = -1;
2069 player->anim_delay_counter = 0;
2070 player->post_delay_counter = 0;
2072 player->dir_waiting = MV_NONE;
2073 player->action_waiting = ACTION_DEFAULT;
2074 player->last_action_waiting = ACTION_DEFAULT;
2075 player->special_action_bored = ACTION_DEFAULT;
2076 player->special_action_sleeping = ACTION_DEFAULT;
2079 /* cannot be set here -- could be modified in Init[Player]Field() below */
2081 /* set number of special actions for bored and sleeping animation */
2082 player->num_special_action_bored =
2083 get_num_special_action(player->artwork_element,
2084 ACTION_BORING_1, ACTION_BORING_LAST);
2085 player->num_special_action_sleeping =
2086 get_num_special_action(player->artwork_element,
2087 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
2090 player->switch_x = -1;
2091 player->switch_y = -1;
2093 player->drop_x = -1;
2094 player->drop_y = -1;
2096 player->show_envelope = 0;
2099 SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
2101 player->move_delay = game.initial_move_delay;
2102 player->move_delay_value = game.initial_move_delay_value;
2104 player->move_delay_value_next = -1;
2106 player->move_delay_reset_counter = 0;
2108 player->cannot_move = FALSE;
2111 player->push_delay = -1; /* initialized when pushing starts */
2112 player->push_delay_value = game.initial_push_delay_value;
2114 player->drop_delay = 0;
2115 player->drop_pressed_delay = 0;
2117 player->last_jx = player->last_jy = 0;
2118 player->jx = player->jy = 0;
2120 player->shield_normal_time_left = 0;
2121 player->shield_deadly_time_left = 0;
2123 player->inventory_infinite_element = EL_UNDEFINED;
2124 player->inventory_size = 0;
2126 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
2127 SnapField(player, 0, 0);
2129 player->LevelSolved = FALSE;
2130 player->GameOver = FALSE;
2133 network_player_action_received = FALSE;
2135 #if defined(NETWORK_AVALIABLE)
2136 /* initial null action */
2137 if (network_playing)
2138 SendToServer_MovePlayer(MV_NONE);
2147 TimeLeft = level.time;
2150 ScreenMovDir = MV_NONE;
2154 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
2156 AllPlayersGone = FALSE;
2158 game.yamyam_content_nr = 0;
2159 game.magic_wall_active = FALSE;
2160 game.magic_wall_time_left = 0;
2161 game.light_time_left = 0;
2162 game.timegate_time_left = 0;
2163 game.switchgate_pos = 0;
2164 game.wind_direction = level.wind_direction_initial;
2166 #if !USE_PLAYER_GRAVITY
2168 game.gravity = FALSE;
2170 game.gravity = level.initial_gravity;
2172 game.explosions_delayed = TRUE;
2175 game.lenses_time_left = 0;
2176 game.magnify_time_left = 0;
2178 game.ball_state = level.ball_state_initial;
2179 game.ball_content_nr = 0;
2181 game.envelope_active = FALSE;
2183 /* set focus to local player for network games, else to all players */
2184 game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
2185 game.centered_player_nr_next = game.centered_player_nr;
2186 game.set_centered_player = FALSE;
2188 if (network_playing && tape.recording)
2190 /* store client dependent player focus when recording network games */
2191 tape.centered_player_nr_next = game.centered_player_nr_next;
2192 tape.set_centered_player = TRUE;
2196 printf("::: focus set to player %d [%d]\n",
2197 game.centered_player_nr, local_player->index_nr);
2200 for (i = 0; i < NUM_BELTS; i++)
2202 game.belt_dir[i] = MV_NONE;
2203 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
2206 for (i = 0; i < MAX_NUM_AMOEBA; i++)
2207 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
2210 SCAN_PLAYFIELD(x, y)
2212 for (x = 0; x < lev_fieldx; x++) for (y = 0; y < lev_fieldy; y++)
2215 Feld[x][y] = level.field[x][y];
2216 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
2217 ChangeDelay[x][y] = 0;
2218 ChangePage[x][y] = -1;
2219 #if USE_NEW_CUSTOM_VALUE
2220 CustomValue[x][y] = 0; /* initialized in InitField() */
2222 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
2224 WasJustMoving[x][y] = 0;
2225 WasJustFalling[x][y] = 0;
2226 CheckCollision[x][y] = 0;
2228 Pushed[x][y] = FALSE;
2230 ChangeCount[x][y] = 0;
2231 ChangeEvent[x][y] = -1;
2233 ExplodePhase[x][y] = 0;
2234 ExplodeDelay[x][y] = 0;
2235 ExplodeField[x][y] = EX_TYPE_NONE;
2237 RunnerVisit[x][y] = 0;
2238 PlayerVisit[x][y] = 0;
2241 GfxRandom[x][y] = INIT_GFX_RANDOM();
2242 GfxElement[x][y] = EL_UNDEFINED;
2243 GfxAction[x][y] = ACTION_DEFAULT;
2244 GfxDir[x][y] = MV_NONE;
2248 SCAN_PLAYFIELD(x, y)
2250 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
2253 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
2255 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
2257 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
2260 InitField(x, y, TRUE);
2265 for (i = 0; i < MAX_PLAYERS; i++)
2267 struct PlayerInfo *player = &stored_player[i];
2270 /* set number of special actions for bored and sleeping animation */
2271 player->num_special_action_bored =
2272 get_num_special_action(player->artwork_element,
2273 ACTION_BORING_1, ACTION_BORING_LAST);
2274 player->num_special_action_sleeping =
2275 get_num_special_action(player->artwork_element,
2276 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
2281 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
2282 emulate_sb ? EMU_SOKOBAN :
2283 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
2285 #if USE_NEW_ALL_SLIPPERY
2286 /* initialize type of slippery elements */
2287 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2289 if (!IS_CUSTOM_ELEMENT(i))
2291 /* default: elements slip down either to the left or right randomly */
2292 element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
2294 /* SP style elements prefer to slip down on the left side */
2295 if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
2296 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
2298 /* BD style elements prefer to slip down on the left side */
2299 if (game.emulation == EMU_BOULDERDASH)
2300 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
2305 /* initialize explosion and ignition delay */
2306 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2308 if (!IS_CUSTOM_ELEMENT(i))
2311 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
2312 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
2313 game.emulation == EMU_SUPAPLEX ? 3 : 2);
2314 int last_phase = (num_phase + 1) * delay;
2315 int half_phase = (num_phase / 2) * delay;
2317 element_info[i].explosion_delay = last_phase - 1;
2318 element_info[i].ignition_delay = half_phase;
2320 if (i == EL_BLACK_ORB)
2321 element_info[i].ignition_delay = 1;
2325 if (element_info[i].explosion_delay < 1) /* !!! check again !!! */
2326 element_info[i].explosion_delay = 1;
2328 if (element_info[i].ignition_delay < 1) /* !!! check again !!! */
2329 element_info[i].ignition_delay = 1;
2333 /* correct non-moving belts to start moving left */
2334 for (i = 0; i < NUM_BELTS; i++)
2335 if (game.belt_dir[i] == MV_NONE)
2336 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
2338 /* check if any connected player was not found in playfield */
2339 for (i = 0; i < MAX_PLAYERS; i++)
2341 struct PlayerInfo *player = &stored_player[i];
2343 if (player->connected && !player->present)
2345 for (j = 0; j < MAX_PLAYERS; j++)
2347 struct PlayerInfo *some_player = &stored_player[j];
2348 int jx = some_player->jx, jy = some_player->jy;
2350 /* assign first free player found that is present in the playfield */
2351 if (some_player->present && !some_player->connected)
2353 player->present = TRUE;
2354 player->active = TRUE;
2356 some_player->present = FALSE;
2357 some_player->active = FALSE;
2360 player->element_nr = some_player->element_nr;
2363 player->artwork_element = some_player->artwork_element;
2365 player->block_last_field = some_player->block_last_field;
2366 player->block_delay_adjustment = some_player->block_delay_adjustment;
2368 StorePlayer[jx][jy] = player->element_nr;
2369 player->jx = player->last_jx = jx;
2370 player->jy = player->last_jy = jy;
2380 /* when playing a tape, eliminate all players who do not participate */
2382 for (i = 0; i < MAX_PLAYERS; i++)
2384 if (stored_player[i].active && !tape.player_participates[i])
2386 struct PlayerInfo *player = &stored_player[i];
2387 int jx = player->jx, jy = player->jy;
2389 player->active = FALSE;
2390 StorePlayer[jx][jy] = 0;
2391 Feld[jx][jy] = EL_EMPTY;
2395 else if (!options.network && !setup.team_mode) /* && !tape.playing */
2397 /* when in single player mode, eliminate all but the first active player */
2399 for (i = 0; i < MAX_PLAYERS; i++)
2401 if (stored_player[i].active)
2403 for (j = i + 1; j < MAX_PLAYERS; j++)
2405 if (stored_player[j].active)
2407 struct PlayerInfo *player = &stored_player[j];
2408 int jx = player->jx, jy = player->jy;
2410 player->active = FALSE;
2411 player->present = FALSE;
2413 StorePlayer[jx][jy] = 0;
2414 Feld[jx][jy] = EL_EMPTY;
2421 /* when recording the game, store which players take part in the game */
2424 for (i = 0; i < MAX_PLAYERS; i++)
2425 if (stored_player[i].active)
2426 tape.player_participates[i] = TRUE;
2431 for (i = 0; i < MAX_PLAYERS; i++)
2433 struct PlayerInfo *player = &stored_player[i];
2435 printf("Player %d: present == %d, connected == %d, active == %d.\n",
2440 if (local_player == player)
2441 printf("Player %d is local player.\n", i+1);
2445 if (BorderElement == EL_EMPTY)
2448 SBX_Right = lev_fieldx - SCR_FIELDX;
2450 SBY_Lower = lev_fieldy - SCR_FIELDY;
2455 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
2457 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
2460 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
2461 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
2463 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
2464 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
2466 /* if local player not found, look for custom element that might create
2467 the player (make some assumptions about the right custom element) */
2468 if (!local_player->present)
2470 int start_x = 0, start_y = 0;
2471 int found_rating = 0;
2472 int found_element = EL_UNDEFINED;
2473 int player_nr = local_player->index_nr;
2476 SCAN_PLAYFIELD(x, y)
2478 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
2481 int element = Feld[x][y];
2486 if (level.use_start_element[player_nr] &&
2487 level.start_element[player_nr] == element &&
2494 found_element = element;
2497 if (!IS_CUSTOM_ELEMENT(element))
2500 if (CAN_CHANGE(element))
2502 for (i = 0; i < element_info[element].num_change_pages; i++)
2504 /* check for player created from custom element as single target */
2505 content = element_info[element].change_page[i].target_element;
2506 is_player = ELEM_IS_PLAYER(content);
2508 if (is_player && (found_rating < 3 || element < found_element))
2514 found_element = element;
2519 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
2521 /* check for player created from custom element as explosion content */
2522 content = element_info[element].content.e[xx][yy];
2523 is_player = ELEM_IS_PLAYER(content);
2525 if (is_player && (found_rating < 2 || element < found_element))
2527 start_x = x + xx - 1;
2528 start_y = y + yy - 1;
2531 found_element = element;
2534 if (!CAN_CHANGE(element))
2537 for (i = 0; i < element_info[element].num_change_pages; i++)
2539 /* check for player created from custom element as extended target */
2541 element_info[element].change_page[i].target_content.e[xx][yy];
2543 is_player = ELEM_IS_PLAYER(content);
2545 if (is_player && (found_rating < 1 || element < found_element))
2547 start_x = x + xx - 1;
2548 start_y = y + yy - 1;
2551 found_element = element;
2557 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
2558 start_x > SBX_Right + MIDPOSX ? SBX_Right :
2561 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
2562 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
2567 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2568 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2569 local_player->jx - MIDPOSX);
2571 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2572 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2573 local_player->jy - MIDPOSY);
2576 if (!game.restart_level)
2577 CloseDoor(DOOR_CLOSE_1);
2579 /* !!! FIX THIS (START) !!! */
2580 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2582 InitGameEngine_EM();
2589 /* after drawing the level, correct some elements */
2590 if (game.timegate_time_left == 0)
2591 CloseAllOpenTimegates();
2593 if (setup.soft_scrolling)
2594 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
2596 redraw_mask |= REDRAW_FROM_BACKBUFFER;
2599 /* !!! FIX THIS (END) !!! */
2601 if (!game.restart_level)
2603 /* copy default game door content to main double buffer */
2604 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2605 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2608 DrawGameDoorValues();
2610 if (!game.restart_level)
2614 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
2615 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
2616 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
2620 /* copy actual game door content to door double buffer for OpenDoor() */
2621 BlitBitmap(drawto, bitmap_db_door,
2622 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
2624 OpenDoor(DOOR_OPEN_ALL);
2626 PlaySoundStereo(SND_GAME_STARTING, SOUND_MIDDLE);
2628 if (setup.sound_music)
2631 KeyboardAutoRepeatOffUnlessAutoplay();
2635 for (i = 0; i < MAX_PLAYERS; i++)
2636 printf("Player %d %sactive.\n",
2637 i + 1, (stored_player[i].active ? "" : "not "));
2641 game.restart_level = FALSE;
2644 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
2646 /* this is used for non-R'n'D game engines to update certain engine values */
2648 /* needed to determine if sounds are played within the visible screen area */
2649 scroll_x = actual_scroll_x;
2650 scroll_y = actual_scroll_y;
2653 void InitMovDir(int x, int y)
2655 int i, element = Feld[x][y];
2656 static int xy[4][2] =
2663 static int direction[3][4] =
2665 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
2666 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
2667 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
2676 Feld[x][y] = EL_BUG;
2677 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
2680 case EL_SPACESHIP_RIGHT:
2681 case EL_SPACESHIP_UP:
2682 case EL_SPACESHIP_LEFT:
2683 case EL_SPACESHIP_DOWN:
2684 Feld[x][y] = EL_SPACESHIP;
2685 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
2688 case EL_BD_BUTTERFLY_RIGHT:
2689 case EL_BD_BUTTERFLY_UP:
2690 case EL_BD_BUTTERFLY_LEFT:
2691 case EL_BD_BUTTERFLY_DOWN:
2692 Feld[x][y] = EL_BD_BUTTERFLY;
2693 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
2696 case EL_BD_FIREFLY_RIGHT:
2697 case EL_BD_FIREFLY_UP:
2698 case EL_BD_FIREFLY_LEFT:
2699 case EL_BD_FIREFLY_DOWN:
2700 Feld[x][y] = EL_BD_FIREFLY;
2701 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
2704 case EL_PACMAN_RIGHT:
2706 case EL_PACMAN_LEFT:
2707 case EL_PACMAN_DOWN:
2708 Feld[x][y] = EL_PACMAN;
2709 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
2712 case EL_YAMYAM_LEFT:
2713 case EL_YAMYAM_RIGHT:
2715 case EL_YAMYAM_DOWN:
2716 Feld[x][y] = EL_YAMYAM;
2717 MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
2720 case EL_SP_SNIKSNAK:
2721 MovDir[x][y] = MV_UP;
2724 case EL_SP_ELECTRON:
2725 MovDir[x][y] = MV_LEFT;
2732 Feld[x][y] = EL_MOLE;
2733 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
2737 if (IS_CUSTOM_ELEMENT(element))
2739 struct ElementInfo *ei = &element_info[element];
2740 int move_direction_initial = ei->move_direction_initial;
2741 int move_pattern = ei->move_pattern;
2743 if (move_direction_initial == MV_START_PREVIOUS)
2745 if (MovDir[x][y] != MV_NONE)
2748 move_direction_initial = MV_START_AUTOMATIC;
2751 if (move_direction_initial == MV_START_RANDOM)
2752 MovDir[x][y] = 1 << RND(4);
2753 else if (move_direction_initial & MV_ANY_DIRECTION)
2754 MovDir[x][y] = move_direction_initial;
2755 else if (move_pattern == MV_ALL_DIRECTIONS ||
2756 move_pattern == MV_TURNING_LEFT ||
2757 move_pattern == MV_TURNING_RIGHT ||
2758 move_pattern == MV_TURNING_LEFT_RIGHT ||
2759 move_pattern == MV_TURNING_RIGHT_LEFT ||
2760 move_pattern == MV_TURNING_RANDOM)
2761 MovDir[x][y] = 1 << RND(4);
2762 else if (move_pattern == MV_HORIZONTAL)
2763 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
2764 else if (move_pattern == MV_VERTICAL)
2765 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
2766 else if (move_pattern & MV_ANY_DIRECTION)
2767 MovDir[x][y] = element_info[element].move_pattern;
2768 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
2769 move_pattern == MV_ALONG_RIGHT_SIDE)
2771 /* use random direction as default start direction */
2772 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2773 MovDir[x][y] = 1 << RND(4);
2775 for (i = 0; i < NUM_DIRECTIONS; i++)
2777 int x1 = x + xy[i][0];
2778 int y1 = y + xy[i][1];
2780 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2782 if (move_pattern == MV_ALONG_RIGHT_SIDE)
2783 MovDir[x][y] = direction[0][i];
2785 MovDir[x][y] = direction[1][i];
2794 MovDir[x][y] = 1 << RND(4);
2796 if (element != EL_BUG &&
2797 element != EL_SPACESHIP &&
2798 element != EL_BD_BUTTERFLY &&
2799 element != EL_BD_FIREFLY)
2802 for (i = 0; i < NUM_DIRECTIONS; i++)
2804 int x1 = x + xy[i][0];
2805 int y1 = y + xy[i][1];
2807 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2809 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
2811 MovDir[x][y] = direction[0][i];
2814 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
2815 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
2817 MovDir[x][y] = direction[1][i];
2826 GfxDir[x][y] = MovDir[x][y];
2829 void InitAmoebaNr(int x, int y)
2832 int group_nr = AmoebeNachbarNr(x, y);
2836 for (i = 1; i < MAX_NUM_AMOEBA; i++)
2838 if (AmoebaCnt[i] == 0)
2846 AmoebaNr[x][y] = group_nr;
2847 AmoebaCnt[group_nr]++;
2848 AmoebaCnt2[group_nr]++;
2854 boolean raise_level = FALSE;
2856 if (local_player->MovPos)
2859 if (tape.auto_play) /* tape might already be stopped here */
2860 tape.auto_play_level_solved = TRUE;
2862 local_player->LevelSolved = FALSE;
2864 PlaySoundStereo(SND_GAME_WINNING, SOUND_MIDDLE);
2868 if (!tape.playing && setup.sound_loops)
2869 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2870 SND_CTRL_PLAY_LOOP);
2872 while (TimeLeft > 0)
2874 if (!tape.playing && !setup.sound_loops)
2875 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2877 if (TimeLeft > 100 && TimeLeft % 10 == 0)
2880 RaiseScore(level.score[SC_TIME_BONUS] * 10);
2885 RaiseScore(level.score[SC_TIME_BONUS]);
2888 DrawGameValue_Time(TimeLeft);
2896 if (!tape.playing && setup.sound_loops)
2897 StopSound(SND_GAME_LEVELTIME_BONUS);
2899 else if (level.time == 0) /* level without time limit */
2901 if (!tape.playing && setup.sound_loops)
2902 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2903 SND_CTRL_PLAY_LOOP);
2905 while (TimePlayed < 999)
2907 if (!tape.playing && !setup.sound_loops)
2908 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2910 if (TimePlayed < 900 && TimePlayed % 10 == 0)
2913 RaiseScore(level.score[SC_TIME_BONUS] * 10);
2918 RaiseScore(level.score[SC_TIME_BONUS]);
2921 DrawGameValue_Time(TimePlayed);
2929 if (!tape.playing && setup.sound_loops)
2930 StopSound(SND_GAME_LEVELTIME_BONUS);
2933 /* close exit door after last player */
2934 if (AllPlayersGone && ExitX >= 0 && ExitY >= 0 &&
2935 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
2936 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN))
2938 int element = Feld[ExitX][ExitY];
2940 Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
2941 EL_SP_EXIT_CLOSING);
2943 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
2946 /* player disappears */
2947 if (ExitX >= 0 && ExitY >= 0)
2948 DrawLevelField(ExitX, ExitY);
2954 printf("::: TAPE PLAYING -> DO NOT SAVE SCORE\n");
2956 printf("::: NO TAPE PLAYING -> SAVING SCORE\n");
2962 CloseDoor(DOOR_CLOSE_1);
2967 SaveTape(tape.level_nr); /* Ask to save tape */
2970 if (level_nr == leveldir_current->handicap_level)
2972 leveldir_current->handicap_level++;
2973 SaveLevelSetup_SeriesInfo();
2976 if (level_editor_test_game)
2977 local_player->score = -1; /* no highscore when playing from editor */
2978 else if (level_nr < leveldir_current->last_level)
2979 raise_level = TRUE; /* advance to next level */
2981 if ((hi_pos = NewHiScore()) >= 0)
2983 game_status = GAME_MODE_SCORES;
2984 DrawHallOfFame(hi_pos);
2993 game_status = GAME_MODE_MAIN;
3010 LoadScore(level_nr);
3012 if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
3013 local_player->score < highscore[MAX_SCORE_ENTRIES - 1].Score)
3016 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
3018 if (local_player->score > highscore[k].Score)
3020 /* player has made it to the hall of fame */
3022 if (k < MAX_SCORE_ENTRIES - 1)
3024 int m = MAX_SCORE_ENTRIES - 1;
3027 for (l = k; l < MAX_SCORE_ENTRIES; l++)
3028 if (strEqual(setup.player_name, highscore[l].Name))
3030 if (m == k) /* player's new highscore overwrites his old one */
3034 for (l = m; l > k; l--)
3036 strcpy(highscore[l].Name, highscore[l - 1].Name);
3037 highscore[l].Score = highscore[l - 1].Score;
3044 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
3045 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
3046 highscore[k].Score = local_player->score;
3052 else if (!strncmp(setup.player_name, highscore[k].Name,
3053 MAX_PLAYER_NAME_LEN))
3054 break; /* player already there with a higher score */
3060 SaveScore(level_nr);
3065 inline static int getElementMoveStepsize(int x, int y)
3067 int element = Feld[x][y];
3068 int direction = MovDir[x][y];
3069 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3070 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
3071 int horiz_move = (dx != 0);
3072 int sign = (horiz_move ? dx : dy);
3073 int step = sign * element_info[element].move_stepsize;
3075 /* special values for move stepsize for spring and things on conveyor belt */
3079 if (element == EL_SPRING)
3080 step = sign * MOVE_STEPSIZE_NORMAL * 2;
3081 else if (CAN_FALL(element) && !CAN_MOVE(element) &&
3082 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
3083 step = sign * MOVE_STEPSIZE_NORMAL / 2;
3085 if (CAN_FALL(element) &&
3086 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
3087 step = sign * MOVE_STEPSIZE_NORMAL / 2;
3088 else if (element == EL_SPRING)
3089 step = sign * MOVE_STEPSIZE_NORMAL * 2;
3096 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
3098 if (player->GfxAction != action || player->GfxDir != dir)
3101 printf("Player frame reset! (%d => %d, %d => %d)\n",
3102 player->GfxAction, action, player->GfxDir, dir);
3105 player->GfxAction = action;
3106 player->GfxDir = dir;
3108 player->StepFrame = 0;
3112 #if USE_GFX_RESET_GFX_ANIMATION
3113 static void ResetGfxFrame(int x, int y, boolean redraw)
3115 int element = Feld[x][y];
3116 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3117 int last_gfx_frame = GfxFrame[x][y];
3119 if (graphic_info[graphic].anim_global_sync)
3120 GfxFrame[x][y] = FrameCounter;
3121 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
3122 GfxFrame[x][y] = CustomValue[x][y];
3123 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
3124 GfxFrame[x][y] = element_info[element].collect_score;
3125 else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
3126 GfxFrame[x][y] = ChangeDelay[x][y];
3128 if (redraw && GfxFrame[x][y] != last_gfx_frame)
3129 DrawLevelGraphicAnimation(x, y, graphic);
3133 static void ResetGfxAnimation(int x, int y)
3136 int element, graphic;
3139 GfxAction[x][y] = ACTION_DEFAULT;
3140 GfxDir[x][y] = MovDir[x][y];
3144 element = Feld[x][y];
3145 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3147 if (graphic_info[graphic].anim_global_sync)
3148 GfxFrame[x][y] = FrameCounter;
3149 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
3150 GfxFrame[x][y] = CustomValue[x][y];
3151 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
3152 GfxFrame[x][y] = element_info[element].collect_score;
3153 else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
3154 GfxFrame[x][y] = ChangeDelay[x][y];
3157 #if USE_GFX_RESET_GFX_ANIMATION
3158 ResetGfxFrame(x, y, FALSE);
3162 static void ResetRandomAnimationValue(int x, int y)
3164 GfxRandom[x][y] = INIT_GFX_RANDOM();
3167 void InitMovingField(int x, int y, int direction)
3169 int element = Feld[x][y];
3173 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3174 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
3178 if (!WasJustMoving[x][y] || direction != MovDir[x][y])
3179 ResetGfxAnimation(x, y);
3181 MovDir[x][y] = direction;
3182 GfxDir[x][y] = direction;
3183 GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
3184 ACTION_FALLING : ACTION_MOVING);
3187 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3189 if (graphic_info[graphic].anim_global_sync)
3190 GfxFrame[x][y] = FrameCounter;
3191 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
3192 GfxFrame[x][y] = CustomValue[x][y];
3193 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
3194 GfxFrame[x][y] = element_info[element].collect_score;
3195 else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
3196 GfxFrame[x][y] = ChangeDelay[x][y];
3199 /* this is needed for CEs with property "can move" / "not moving" */
3201 if (getElementMoveStepsize(x, y) != 0) /* moving or being moved */
3203 if (Feld[newx][newy] == EL_EMPTY)
3204 Feld[newx][newy] = EL_BLOCKED;
3206 MovDir[newx][newy] = MovDir[x][y];
3208 #if USE_NEW_CUSTOM_VALUE
3209 CustomValue[newx][newy] = CustomValue[x][y];
3212 GfxFrame[newx][newy] = GfxFrame[x][y];
3213 GfxRandom[newx][newy] = GfxRandom[x][y];
3214 GfxAction[newx][newy] = GfxAction[x][y];
3215 GfxDir[newx][newy] = GfxDir[x][y];
3219 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
3221 int direction = MovDir[x][y];
3223 int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
3224 int newy = y + (direction & MV_UP ? -1 : direction & MV_DOWN ? +1 : 0);
3226 int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3227 int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
3234 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
3236 int oldx = x, oldy = y;
3237 int direction = MovDir[x][y];
3239 if (direction == MV_LEFT)
3241 else if (direction == MV_RIGHT)
3243 else if (direction == MV_UP)
3245 else if (direction == MV_DOWN)
3248 *comes_from_x = oldx;
3249 *comes_from_y = oldy;
3252 int MovingOrBlocked2Element(int x, int y)
3254 int element = Feld[x][y];
3256 if (element == EL_BLOCKED)
3260 Blocked2Moving(x, y, &oldx, &oldy);
3261 return Feld[oldx][oldy];
3267 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
3269 /* like MovingOrBlocked2Element(), but if element is moving
3270 and (x,y) is the field the moving element is just leaving,
3271 return EL_BLOCKED instead of the element value */
3272 int element = Feld[x][y];
3274 if (IS_MOVING(x, y))
3276 if (element == EL_BLOCKED)
3280 Blocked2Moving(x, y, &oldx, &oldy);
3281 return Feld[oldx][oldy];
3290 static void RemoveField(int x, int y)
3292 Feld[x][y] = EL_EMPTY;
3298 #if USE_NEW_CUSTOM_VALUE
3299 CustomValue[x][y] = 0;
3303 ChangeDelay[x][y] = 0;
3304 ChangePage[x][y] = -1;
3305 Pushed[x][y] = FALSE;
3308 ExplodeField[x][y] = EX_TYPE_NONE;
3311 GfxElement[x][y] = EL_UNDEFINED;
3312 GfxAction[x][y] = ACTION_DEFAULT;
3313 GfxDir[x][y] = MV_NONE;
3316 void RemoveMovingField(int x, int y)
3318 int oldx = x, oldy = y, newx = x, newy = y;
3319 int element = Feld[x][y];
3320 int next_element = EL_UNDEFINED;
3322 if (element != EL_BLOCKED && !IS_MOVING(x, y))
3325 if (IS_MOVING(x, y))
3327 Moving2Blocked(x, y, &newx, &newy);
3329 if (Feld[newx][newy] != EL_BLOCKED)
3331 /* element is moving, but target field is not free (blocked), but
3332 already occupied by something different (example: acid pool);
3333 in this case, only remove the moving field, but not the target */
3335 RemoveField(oldx, oldy);
3337 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
3339 DrawLevelField(oldx, oldy);
3344 else if (element == EL_BLOCKED)
3346 Blocked2Moving(x, y, &oldx, &oldy);
3347 if (!IS_MOVING(oldx, oldy))
3351 if (element == EL_BLOCKED &&
3352 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
3353 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
3354 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
3355 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
3356 next_element = get_next_element(Feld[oldx][oldy]);
3358 RemoveField(oldx, oldy);
3359 RemoveField(newx, newy);
3361 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
3363 if (next_element != EL_UNDEFINED)
3364 Feld[oldx][oldy] = next_element;
3366 DrawLevelField(oldx, oldy);
3367 DrawLevelField(newx, newy);
3370 void DrawDynamite(int x, int y)
3372 int sx = SCREENX(x), sy = SCREENY(y);
3373 int graphic = el2img(Feld[x][y]);
3376 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
3379 if (IS_WALKABLE_INSIDE(Back[x][y]))
3383 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
3384 else if (Store[x][y])
3385 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
3387 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
3389 if (Back[x][y] || Store[x][y])
3390 DrawGraphicThruMask(sx, sy, graphic, frame);
3392 DrawGraphic(sx, sy, graphic, frame);
3395 void CheckDynamite(int x, int y)
3397 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
3401 if (MovDelay[x][y] != 0)
3404 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
3410 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
3417 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
3419 boolean num_checked_players = 0;
3422 for (i = 0; i < MAX_PLAYERS; i++)
3424 if (stored_player[i].active)
3426 int sx = stored_player[i].jx;
3427 int sy = stored_player[i].jy;
3429 if (num_checked_players == 0)
3436 *sx1 = MIN(*sx1, sx);
3437 *sy1 = MIN(*sy1, sy);
3438 *sx2 = MAX(*sx2, sx);
3439 *sy2 = MAX(*sy2, sy);
3442 num_checked_players++;
3447 static boolean checkIfAllPlayersFitToScreen_RND()
3449 int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
3451 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
3453 return (sx2 - sx1 < SCR_FIELDX &&
3454 sy2 - sy1 < SCR_FIELDY);
3457 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
3459 int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
3461 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
3463 *sx = (sx1 + sx2) / 2;
3464 *sy = (sy1 + sy2) / 2;
3468 static void setMaxCenterDistanceForAllPlayers(int *max_dx, int *max_dy,
3469 int center_x, int center_y)
3471 int sx1 = center_x, sy1 = center_y, sx2 = center_x, sy2 = center_y;
3473 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
3475 *max_dx = MAX(ABS(sx1 - center_x), ABS(sx2 - center_x));
3476 *max_dy = MAX(ABS(sy1 - center_y), ABS(sy2 - center_y));
3479 static boolean checkIfAllPlayersAreVisible(int center_x, int center_y)
3483 setMaxCenterDistanceForAllPlayers(&max_dx, &max_dy, center_x, center_y);
3485 return (max_dx <= SCR_FIELDX / 2 &&
3486 max_dy <= SCR_FIELDY / 2);
3494 void DrawRelocateScreen(int x, int y, int move_dir, boolean center_screen,
3495 boolean quick_relocation)
3497 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3498 boolean no_delay = (tape.warp_forward);
3499 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
3500 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
3502 if (quick_relocation)
3504 int offset = (setup.scroll_delay ? 3 : 0);
3511 if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
3513 scroll_x = (x < SBX_Left + MIDPOSX ? SBX_Left :
3514 x > SBX_Right + MIDPOSX ? SBX_Right :
3517 scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
3518 y > SBY_Lower + MIDPOSY ? SBY_Lower :
3523 if ((move_dir == MV_LEFT && scroll_x > x - MIDPOSX + offset) ||
3524 (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
3525 scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
3527 if ((move_dir == MV_UP && scroll_y > y - MIDPOSY + offset) ||
3528 (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
3529 scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
3531 /* don't scroll over playfield boundaries */
3532 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
3533 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
3535 /* don't scroll over playfield boundaries */
3536 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
3537 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
3540 RedrawPlayfield(TRUE, 0,0,0,0);
3544 int scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left :
3545 x > SBX_Right + MIDPOSX ? SBX_Right :
3548 int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
3549 y > SBY_Lower + MIDPOSY ? SBY_Lower :
3552 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
3554 while (scroll_x != scroll_xx || scroll_y != scroll_yy)
3557 int fx = FX, fy = FY;
3559 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
3560 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
3562 if (dx == 0 && dy == 0) /* no scrolling needed at all */
3568 fx += dx * TILEX / 2;
3569 fy += dy * TILEY / 2;
3571 ScrollLevel(dx, dy);
3574 /* scroll in two steps of half tile size to make things smoother */
3575 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
3577 Delay(wait_delay_value);
3579 /* scroll second step to align at full tile size */
3581 Delay(wait_delay_value);
3586 Delay(wait_delay_value);
3592 void DrawRelocatePlayer(struct PlayerInfo *player, boolean quick_relocation)
3594 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3595 boolean no_delay = (tape.warp_forward);
3596 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
3597 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
3598 int jx = player->jx;
3599 int jy = player->jy;
3601 if (quick_relocation)
3603 int offset = (setup.scroll_delay ? 3 : 0);
3605 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
3607 scroll_x = (player->jx < SBX_Left + MIDPOSX ? SBX_Left :
3608 player->jx > SBX_Right + MIDPOSX ? SBX_Right :
3609 player->jx - MIDPOSX);
3611 scroll_y = (player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
3612 player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
3613 player->jy - MIDPOSY);
3617 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
3618 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
3619 scroll_x = jx - MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
3621 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
3622 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
3623 scroll_y = jy - MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
3625 /* don't scroll over playfield boundaries */
3626 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
3627 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
3629 /* don't scroll over playfield boundaries */
3630 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
3631 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
3634 RedrawPlayfield(TRUE, 0,0,0,0);
3638 int scroll_xx = (player->jx < SBX_Left + MIDPOSX ? SBX_Left :
3639 player->jx > SBX_Right + MIDPOSX ? SBX_Right :
3640 player->jx - MIDPOSX);
3642 int scroll_yy = (player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
3643 player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
3644 player->jy - MIDPOSY);
3646 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
3648 while (scroll_x != scroll_xx || scroll_y != scroll_yy)
3651 int fx = FX, fy = FY;
3653 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
3654 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
3656 if (dx == 0 && dy == 0) /* no scrolling needed at all */
3662 fx += dx * TILEX / 2;
3663 fy += dy * TILEY / 2;
3665 ScrollLevel(dx, dy);
3668 /* scroll in two steps of half tile size to make things smoother */
3669 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
3671 Delay(wait_delay_value);
3673 /* scroll second step to align at full tile size */
3675 Delay(wait_delay_value);
3680 Delay(wait_delay_value);
3686 void RelocatePlayer(int jx, int jy, int el_player_raw)
3688 int el_player = GET_PLAYER_ELEMENT(el_player_raw);
3689 int player_nr = GET_PLAYER_NR(el_player);
3690 struct PlayerInfo *player = &stored_player[player_nr];
3691 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3692 boolean no_delay = (tape.warp_forward);
3693 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
3694 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
3695 int old_jx = player->jx;
3696 int old_jy = player->jy;
3697 int old_element = Feld[old_jx][old_jy];
3698 int element = Feld[jx][jy];
3699 boolean player_relocated = (old_jx != jx || old_jy != jy);
3701 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
3702 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
3703 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
3704 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
3705 int leave_side_horiz = move_dir_horiz;
3706 int leave_side_vert = move_dir_vert;
3707 int enter_side = enter_side_horiz | enter_side_vert;
3708 int leave_side = leave_side_horiz | leave_side_vert;
3710 if (player->GameOver) /* do not reanimate dead player */
3713 if (!player_relocated) /* no need to relocate the player */
3716 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
3718 RemoveField(jx, jy); /* temporarily remove newly placed player */
3719 DrawLevelField(jx, jy);
3722 if (player->present)
3724 while (player->MovPos)
3726 ScrollPlayer(player, SCROLL_GO_ON);
3727 ScrollScreen(NULL, SCROLL_GO_ON);
3729 AdvanceFrameAndPlayerCounters(player->index_nr);
3734 Delay(wait_delay_value);
3737 DrawPlayer(player); /* needed here only to cleanup last field */
3738 DrawLevelField(player->jx, player->jy); /* remove player graphic */
3740 player->is_moving = FALSE;
3743 if (IS_CUSTOM_ELEMENT(old_element))
3744 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
3746 player->index_bit, leave_side);
3748 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
3750 player->index_bit, leave_side);
3752 Feld[jx][jy] = el_player;
3753 InitPlayerField(jx, jy, el_player, TRUE);
3755 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
3757 Feld[jx][jy] = element;
3758 InitField(jx, jy, FALSE);
3762 /* only visually relocate centered player */
3764 DrawRelocateScreen(player->jx, player->jy, player->MovDir, FALSE,
3765 level.instant_relocation);
3767 if (player->index_nr == game.centered_player_nr)
3768 DrawRelocatePlayer(player, level.instant_relocation);
3771 if (player == local_player) /* only visually relocate local player */
3772 DrawRelocatePlayer(player, level.instant_relocation);
3775 TestIfPlayerTouchesBadThing(jx, jy);
3776 TestIfPlayerTouchesCustomElement(jx, jy);
3778 if (IS_CUSTOM_ELEMENT(element))
3779 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
3780 player->index_bit, enter_side);
3782 CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
3783 player->index_bit, enter_side);
3786 void Explode(int ex, int ey, int phase, int mode)
3792 /* !!! eliminate this variable !!! */
3793 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
3795 if (game.explosions_delayed)
3797 ExplodeField[ex][ey] = mode;
3801 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
3803 int center_element = Feld[ex][ey];
3804 int artwork_element, explosion_element; /* set these values later */
3807 /* --- This is only really needed (and now handled) in "Impact()". --- */
3808 /* do not explode moving elements that left the explode field in time */
3809 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
3810 center_element == EL_EMPTY &&
3811 (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
3816 /* !!! at this place, the center element may be EL_BLOCKED !!! */
3817 if (mode == EX_TYPE_NORMAL ||
3818 mode == EX_TYPE_CENTER ||
3819 mode == EX_TYPE_CROSS)
3820 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
3823 /* remove things displayed in background while burning dynamite */
3824 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
3827 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
3829 /* put moving element to center field (and let it explode there) */
3830 center_element = MovingOrBlocked2Element(ex, ey);
3831 RemoveMovingField(ex, ey);
3832 Feld[ex][ey] = center_element;
3835 /* now "center_element" is finally determined -- set related values now */
3836 artwork_element = center_element; /* for custom player artwork */
3837 explosion_element = center_element; /* for custom player artwork */
3839 if (IS_PLAYER(ex, ey))
3841 int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
3843 artwork_element = stored_player[player_nr].artwork_element;
3845 if (level.use_explosion_element[player_nr])
3847 explosion_element = level.explosion_element[player_nr];
3848 artwork_element = explosion_element;
3853 if (mode == EX_TYPE_NORMAL ||
3854 mode == EX_TYPE_CENTER ||
3855 mode == EX_TYPE_CROSS)
3856 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
3860 last_phase = element_info[explosion_element].explosion_delay + 1;
3862 last_phase = element_info[center_element].explosion_delay + 1;
3865 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
3867 int xx = x - ex + 1;
3868 int yy = y - ey + 1;
3871 if (!IN_LEV_FIELD(x, y) ||
3872 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
3873 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
3876 element = Feld[x][y];
3878 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
3880 element = MovingOrBlocked2Element(x, y);
3882 if (!IS_EXPLOSION_PROOF(element))
3883 RemoveMovingField(x, y);
3886 /* indestructible elements can only explode in center (but not flames) */
3887 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
3888 mode == EX_TYPE_BORDER)) ||
3889 element == EL_FLAMES)
3892 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
3893 behaviour, for example when touching a yamyam that explodes to rocks
3894 with active deadly shield, a rock is created under the player !!! */
3895 /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
3897 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
3898 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
3899 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
3901 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
3904 if (IS_ACTIVE_BOMB(element))
3906 /* re-activate things under the bomb like gate or penguin */
3907 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
3914 /* save walkable background elements while explosion on same tile */
3915 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
3916 (x != ex || y != ey || mode == EX_TYPE_BORDER))
3917 Back[x][y] = element;
3919 /* ignite explodable elements reached by other explosion */
3920 if (element == EL_EXPLOSION)
3921 element = Store2[x][y];
3923 if (AmoebaNr[x][y] &&
3924 (element == EL_AMOEBA_FULL ||
3925 element == EL_BD_AMOEBA ||
3926 element == EL_AMOEBA_GROWING))
3928 AmoebaCnt[AmoebaNr[x][y]]--;
3929 AmoebaCnt2[AmoebaNr[x][y]]--;
3934 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
3937 int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
3939 Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
3941 switch(StorePlayer[ex][ey])
3944 Store[x][y] = EL_PLAYER_IS_EXPLODING_2;
3947 Store[x][y] = EL_PLAYER_IS_EXPLODING_3;
3950 Store[x][y] = EL_PLAYER_IS_EXPLODING_4;
3954 Store[x][y] = EL_PLAYER_IS_EXPLODING_1;
3959 if (PLAYERINFO(ex, ey)->use_murphy)
3960 Store[x][y] = EL_EMPTY;
3963 /* !!! check this case -- currently needed for rnd_rado_negundo_v,
3964 !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
3965 else if (ELEM_IS_PLAYER(center_element))
3966 Store[x][y] = EL_EMPTY;
3967 else if (center_element == EL_YAMYAM)
3968 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
3969 else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
3970 Store[x][y] = element_info[center_element].content.e[xx][yy];
3972 /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
3973 (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
3974 otherwise) -- FIX THIS !!! */
3975 else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
3976 Store[x][y] = element_info[element].content.e[1][1];
3978 else if (!CAN_EXPLODE(element))
3979 Store[x][y] = element_info[element].content.e[1][1];
3982 Store[x][y] = EL_EMPTY;
3984 else if (center_element == EL_MOLE)
3985 Store[x][y] = EL_EMERALD_RED;
3986 else if (center_element == EL_PENGUIN)
3987 Store[x][y] = EL_EMERALD_PURPLE;
3988 else if (center_element == EL_BUG)
3989 Store[x][y] = ((x == ex && y == ey) ? EL_DIAMOND : EL_EMERALD);
3990 else if (center_element == EL_BD_BUTTERFLY)
3991 Store[x][y] = EL_BD_DIAMOND;
3992 else if (center_element == EL_SP_ELECTRON)
3993 Store[x][y] = EL_SP_INFOTRON;
3994 else if (center_element == EL_AMOEBA_TO_DIAMOND)
3995 Store[x][y] = level.amoeba_content;
3996 else if (center_element == EL_YAMYAM)
3997 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
3998 else if (IS_CUSTOM_ELEMENT(center_element) &&
3999 element_info[center_element].content.e[xx][yy] != EL_EMPTY)
4000 Store[x][y] = element_info[center_element].content.e[xx][yy];
4001 else if (element == EL_WALL_EMERALD)
4002 Store[x][y] = EL_EMERALD;
4003 else if (element == EL_WALL_DIAMOND)
4004 Store[x][y] = EL_DIAMOND;
4005 else if (element == EL_WALL_BD_DIAMOND)
4006 Store[x][y] = EL_BD_DIAMOND;
4007 else if (element == EL_WALL_EMERALD_YELLOW)
4008 Store[x][y] = EL_EMERALD_YELLOW;
4009 else if (element == EL_WALL_EMERALD_RED)
4010 Store[x][y] = EL_EMERALD_RED;
4011 else if (element == EL_WALL_EMERALD_PURPLE)
4012 Store[x][y] = EL_EMERALD_PURPLE;
4013 else if (element == EL_WALL_PEARL)
4014 Store[x][y] = EL_PEARL;
4015 else if (element == EL_WALL_CRYSTAL)
4016 Store[x][y] = EL_CRYSTAL;
4017 else if (IS_CUSTOM_ELEMENT(element) && !CAN_EXPLODE(element))
4018 Store[x][y] = element_info[element].content.e[1][1];
4020 Store[x][y] = EL_EMPTY;
4023 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
4024 center_element == EL_AMOEBA_TO_DIAMOND)
4025 Store2[x][y] = element;
4027 Feld[x][y] = EL_EXPLOSION;
4028 GfxElement[x][y] = artwork_element;
4031 printf(":: setting gfx(%d,%d) to %d ['%s']\n",
4032 x, y, artwork_element, EL_NAME(artwork_element));
4035 ExplodePhase[x][y] = 1;
4036 ExplodeDelay[x][y] = last_phase;
4041 if (center_element == EL_YAMYAM)
4042 game.yamyam_content_nr =
4043 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
4055 GfxFrame[x][y] = 0; /* restart explosion animation */
4057 last_phase = ExplodeDelay[x][y];
4059 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
4063 /* activate this even in non-DEBUG version until cause for crash in
4064 getGraphicAnimationFrame() (see below) is found and eliminated */
4070 /* this can happen if the player leaves an explosion just in time */
4071 if (GfxElement[x][y] == EL_UNDEFINED)
4072 GfxElement[x][y] = EL_EMPTY;
4074 if (GfxElement[x][y] == EL_UNDEFINED)
4077 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
4078 printf("Explode(): This should never happen!\n");
4081 GfxElement[x][y] = EL_EMPTY;
4087 border_element = Store2[x][y];
4088 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
4089 border_element = StorePlayer[x][y];
4091 if (phase == element_info[border_element].ignition_delay ||
4092 phase == last_phase)
4094 boolean border_explosion = FALSE;
4096 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
4097 !PLAYER_EXPLOSION_PROTECTED(x, y))
4099 KillPlayerUnlessExplosionProtected(x, y);
4100 border_explosion = TRUE;
4102 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
4104 Feld[x][y] = Store2[x][y];
4107 border_explosion = TRUE;
4109 else if (border_element == EL_AMOEBA_TO_DIAMOND)
4111 AmoebeUmwandeln(x, y);
4113 border_explosion = TRUE;
4116 /* if an element just explodes due to another explosion (chain-reaction),
4117 do not immediately end the new explosion when it was the last frame of
4118 the explosion (as it would be done in the following "if"-statement!) */
4119 if (border_explosion && phase == last_phase)
4123 if (phase == last_phase)
4127 element = Feld[x][y] = Store[x][y];
4128 Store[x][y] = Store2[x][y] = 0;
4129 GfxElement[x][y] = EL_UNDEFINED;
4131 /* player can escape from explosions and might therefore be still alive */
4132 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
4133 element <= EL_PLAYER_IS_EXPLODING_4)
4135 int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
4136 int explosion_element = EL_PLAYER_1 + player_nr;
4137 int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
4138 int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
4140 if (level.use_explosion_element[player_nr])
4141 explosion_element = level.explosion_element[player_nr];
4143 Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
4144 element_info[explosion_element].content.e[xx][yy]);
4147 /* restore probably existing indestructible background element */
4148 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
4149 element = Feld[x][y] = Back[x][y];
4152 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
4153 GfxDir[x][y] = MV_NONE;
4154 ChangeDelay[x][y] = 0;
4155 ChangePage[x][y] = -1;
4157 #if USE_NEW_CUSTOM_VALUE
4158 CustomValue[x][y] = 0;
4161 InitField_WithBug2(x, y, FALSE);
4163 DrawLevelField(x, y);
4165 TestIfElementTouchesCustomElement(x, y);
4167 if (GFX_CRUMBLED(element))
4168 DrawLevelFieldCrumbledSandNeighbours(x, y);
4170 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
4171 StorePlayer[x][y] = 0;
4173 if (ELEM_IS_PLAYER(element))
4174 RelocatePlayer(x, y, element);
4176 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
4178 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
4179 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4182 DrawLevelFieldCrumbledSand(x, y);
4184 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
4186 DrawLevelElement(x, y, Back[x][y]);
4187 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
4189 else if (IS_WALKABLE_UNDER(Back[x][y]))
4191 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
4192 DrawLevelElementThruMask(x, y, Back[x][y]);
4194 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
4195 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
4199 void DynaExplode(int ex, int ey)
4202 int dynabomb_element = Feld[ex][ey];
4203 int dynabomb_size = 1;
4204 boolean dynabomb_xl = FALSE;
4205 struct PlayerInfo *player;
4206 static int xy[4][2] =
4214 if (IS_ACTIVE_BOMB(dynabomb_element))
4216 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
4217 dynabomb_size = player->dynabomb_size;
4218 dynabomb_xl = player->dynabomb_xl;
4219 player->dynabombs_left++;
4222 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
4224 for (i = 0; i < NUM_DIRECTIONS; i++)
4226 for (j = 1; j <= dynabomb_size; j++)
4228 int x = ex + j * xy[i][0];
4229 int y = ey + j * xy[i][1];
4232 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
4235 element = Feld[x][y];
4237 /* do not restart explosions of fields with active bombs */
4238 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
4241 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
4243 if (element != EL_EMPTY && element != EL_EXPLOSION &&
4244 !IS_DIGGABLE(element) && !dynabomb_xl)
4250 void Bang(int x, int y)
4252 int element = MovingOrBlocked2Element(x, y);
4253 int explosion_type = EX_TYPE_NORMAL;
4255 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
4257 struct PlayerInfo *player = PLAYERINFO(x, y);
4259 element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
4260 player->element_nr);
4262 if (level.use_explosion_element[player->index_nr])
4264 int explosion_element = level.explosion_element[player->index_nr];
4266 if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
4267 explosion_type = EX_TYPE_CROSS;
4268 else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
4269 explosion_type = EX_TYPE_CENTER;
4277 case EL_BD_BUTTERFLY:
4280 case EL_DARK_YAMYAM:
4284 RaiseScoreElement(element);
4287 case EL_DYNABOMB_PLAYER_1_ACTIVE:
4288 case EL_DYNABOMB_PLAYER_2_ACTIVE:
4289 case EL_DYNABOMB_PLAYER_3_ACTIVE:
4290 case EL_DYNABOMB_PLAYER_4_ACTIVE:
4291 case EL_DYNABOMB_INCREASE_NUMBER:
4292 case EL_DYNABOMB_INCREASE_SIZE:
4293 case EL_DYNABOMB_INCREASE_POWER:
4294 explosion_type = EX_TYPE_DYNA;
4299 case EL_LAMP_ACTIVE:
4300 case EL_AMOEBA_TO_DIAMOND:
4301 if (!IS_PLAYER(x, y)) /* penguin and player may be at same field */
4302 explosion_type = EX_TYPE_CENTER;
4306 if (element_info[element].explosion_type == EXPLODES_CROSS)
4307 explosion_type = EX_TYPE_CROSS;
4308 else if (element_info[element].explosion_type == EXPLODES_1X1)
4309 explosion_type = EX_TYPE_CENTER;
4313 if (explosion_type == EX_TYPE_DYNA)
4316 Explode(x, y, EX_PHASE_START, explosion_type);
4318 CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
4321 void SplashAcid(int x, int y)
4323 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
4324 (!IN_LEV_FIELD(x - 1, y - 2) ||
4325 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
4326 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
4328 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
4329 (!IN_LEV_FIELD(x + 1, y - 2) ||
4330 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
4331 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
4333 PlayLevelSound(x, y, SND_ACID_SPLASHING);
4336 static void InitBeltMovement()
4338 static int belt_base_element[4] =
4340 EL_CONVEYOR_BELT_1_LEFT,
4341 EL_CONVEYOR_BELT_2_LEFT,
4342 EL_CONVEYOR_BELT_3_LEFT,
4343 EL_CONVEYOR_BELT_4_LEFT
4345 static int belt_base_active_element[4] =
4347 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
4348 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
4349 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
4350 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
4355 /* set frame order for belt animation graphic according to belt direction */
4356 for (i = 0; i < NUM_BELTS; i++)
4360 for (j = 0; j < NUM_BELT_PARTS; j++)
4362 int element = belt_base_active_element[belt_nr] + j;
4363 int graphic = el2img(element);
4365 if (game.belt_dir[i] == MV_LEFT)
4366 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
4368 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
4373 SCAN_PLAYFIELD(x, y)
4375 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
4378 int element = Feld[x][y];
4380 for (i = 0; i < NUM_BELTS; i++)
4382 if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
4384 int e_belt_nr = getBeltNrFromBeltElement(element);
4387 if (e_belt_nr == belt_nr)
4389 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
4391 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
4398 static void ToggleBeltSwitch(int x, int y)
4400 static int belt_base_element[4] =
4402 EL_CONVEYOR_BELT_1_LEFT,
4403 EL_CONVEYOR_BELT_2_LEFT,
4404 EL_CONVEYOR_BELT_3_LEFT,
4405 EL_CONVEYOR_BELT_4_LEFT
4407 static int belt_base_active_element[4] =
4409 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
4410 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
4411 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
4412 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
4414 static int belt_base_switch_element[4] =
4416 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
4417 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
4418 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
4419 EL_CONVEYOR_BELT_4_SWITCH_LEFT
4421 static int belt_move_dir[4] =
4429 int element = Feld[x][y];
4430 int belt_nr = getBeltNrFromBeltSwitchElement(element);
4431 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
4432 int belt_dir = belt_move_dir[belt_dir_nr];
4435 if (!IS_BELT_SWITCH(element))
4438 game.belt_dir_nr[belt_nr] = belt_dir_nr;
4439 game.belt_dir[belt_nr] = belt_dir;
4441 if (belt_dir_nr == 3)
4444 /* set frame order for belt animation graphic according to belt direction */
4445 for (i = 0; i < NUM_BELT_PARTS; i++)
4447 int element = belt_base_active_element[belt_nr] + i;
4448 int graphic = el2img(element);
4450 if (belt_dir == MV_LEFT)
4451 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
4453 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
4457 SCAN_PLAYFIELD(xx, yy)
4459 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
4462 int element = Feld[xx][yy];
4464 if (IS_BELT_SWITCH(element))
4466 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
4468 if (e_belt_nr == belt_nr)
4470 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
4471 DrawLevelField(xx, yy);
4474 else if (IS_BELT(element) && belt_dir != MV_NONE)
4476 int e_belt_nr = getBeltNrFromBeltElement(element);
4478 if (e_belt_nr == belt_nr)
4480 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
4482 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
4483 DrawLevelField(xx, yy);
4486 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
4488 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
4490 if (e_belt_nr == belt_nr)
4492 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
4494 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
4495 DrawLevelField(xx, yy);
4501 static void ToggleSwitchgateSwitch(int x, int y)
4505 game.switchgate_pos = !game.switchgate_pos;
4508 SCAN_PLAYFIELD(xx, yy)
4510 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
4513 int element = Feld[xx][yy];
4515 #if !USE_BOTH_SWITCHGATE_SWITCHES
4516 if (element == EL_SWITCHGATE_SWITCH_UP ||
4517 element == EL_SWITCHGATE_SWITCH_DOWN)
4519 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
4520 DrawLevelField(xx, yy);
4523 if (element == EL_SWITCHGATE_SWITCH_UP)
4525 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
4526 DrawLevelField(xx, yy);
4528 else if (element == EL_SWITCHGATE_SWITCH_DOWN)
4530 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
4531 DrawLevelField(xx, yy);
4534 else if (element == EL_SWITCHGATE_OPEN ||
4535 element == EL_SWITCHGATE_OPENING)
4537 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
4539 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
4541 else if (element == EL_SWITCHGATE_CLOSED ||
4542 element == EL_SWITCHGATE_CLOSING)
4544 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
4546 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
4551 static int getInvisibleActiveFromInvisibleElement(int element)
4553 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
4554 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
4555 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
4559 static int getInvisibleFromInvisibleActiveElement(int element)
4561 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
4562 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
4563 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
4567 static void RedrawAllLightSwitchesAndInvisibleElements()
4572 SCAN_PLAYFIELD(x, y)
4574 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
4577 int element = Feld[x][y];
4579 if (element == EL_LIGHT_SWITCH &&
4580 game.light_time_left > 0)
4582 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
4583 DrawLevelField(x, y);
4585 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
4586 game.light_time_left == 0)
4588 Feld[x][y] = EL_LIGHT_SWITCH;
4589 DrawLevelField(x, y);
4591 else if (element == EL_EMC_DRIPPER &&
4592 game.light_time_left > 0)
4594 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
4595 DrawLevelField(x, y);
4597 else if (element == EL_EMC_DRIPPER_ACTIVE &&
4598 game.light_time_left == 0)
4600 Feld[x][y] = EL_EMC_DRIPPER;
4601 DrawLevelField(x, y);
4603 else if (element == EL_INVISIBLE_STEELWALL ||
4604 element == EL_INVISIBLE_WALL ||
4605 element == EL_INVISIBLE_SAND)
4607 if (game.light_time_left > 0)
4608 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
4610 DrawLevelField(x, y);
4612 /* uncrumble neighbour fields, if needed */
4613 if (element == EL_INVISIBLE_SAND)
4614 DrawLevelFieldCrumbledSandNeighbours(x, y);
4616 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
4617 element == EL_INVISIBLE_WALL_ACTIVE ||
4618 element == EL_INVISIBLE_SAND_ACTIVE)
4620 if (game.light_time_left == 0)
4621 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
4623 DrawLevelField(x, y);
4625 /* re-crumble neighbour fields, if needed */
4626 if (element == EL_INVISIBLE_SAND)
4627 DrawLevelFieldCrumbledSandNeighbours(x, y);
4632 static void RedrawAllInvisibleElementsForLenses()
4637 SCAN_PLAYFIELD(x, y)
4639 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
4642 int element = Feld[x][y];
4644 if (element == EL_EMC_DRIPPER &&
4645 game.lenses_time_left > 0)
4647 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
4648 DrawLevelField(x, y);
4650 else if (element == EL_EMC_DRIPPER_ACTIVE &&
4651 game.lenses_time_left == 0)
4653 Feld[x][y] = EL_EMC_DRIPPER;
4654 DrawLevelField(x, y);
4656 else if (element == EL_INVISIBLE_STEELWALL ||
4657 element == EL_INVISIBLE_WALL ||
4658 element == EL_INVISIBLE_SAND)
4660 if (game.lenses_time_left > 0)
4661 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
4663 DrawLevelField(x, y);
4665 /* uncrumble neighbour fields, if needed */
4666 if (element == EL_INVISIBLE_SAND)
4667 DrawLevelFieldCrumbledSandNeighbours(x, y);
4669 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
4670 element == EL_INVISIBLE_WALL_ACTIVE ||
4671 element == EL_INVISIBLE_SAND_ACTIVE)
4673 if (game.lenses_time_left == 0)
4674 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
4676 DrawLevelField(x, y);
4678 /* re-crumble neighbour fields, if needed */
4679 if (element == EL_INVISIBLE_SAND)
4680 DrawLevelFieldCrumbledSandNeighbours(x, y);
4685 static void RedrawAllInvisibleElementsForMagnifier()
4690 SCAN_PLAYFIELD(x, y)
4692 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
4695 int element = Feld[x][y];
4697 if (element == EL_EMC_FAKE_GRASS &&
4698 game.magnify_time_left > 0)
4700 Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
4701 DrawLevelField(x, y);
4703 else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
4704 game.magnify_time_left == 0)
4706 Feld[x][y] = EL_EMC_FAKE_GRASS;
4707 DrawLevelField(x, y);
4709 else if (IS_GATE_GRAY(element) &&
4710 game.magnify_time_left > 0)
4712 Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
4713 element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
4714 IS_EM_GATE_GRAY(element) ?
4715 element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
4716 IS_EMC_GATE_GRAY(element) ?
4717 element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
4719 DrawLevelField(x, y);
4721 else if (IS_GATE_GRAY_ACTIVE(element) &&
4722 game.magnify_time_left == 0)
4724 Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
4725 element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
4726 IS_EM_GATE_GRAY_ACTIVE(element) ?
4727 element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
4728 IS_EMC_GATE_GRAY_ACTIVE(element) ?
4729 element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
4731 DrawLevelField(x, y);
4736 static void ToggleLightSwitch(int x, int y)
4738 int element = Feld[x][y];
4740 game.light_time_left =
4741 (element == EL_LIGHT_SWITCH ?
4742 level.time_light * FRAMES_PER_SECOND : 0);
4744 RedrawAllLightSwitchesAndInvisibleElements();
4747 static void ActivateTimegateSwitch(int x, int y)
4751 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
4754 SCAN_PLAYFIELD(xx, yy)
4756 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
4759 int element = Feld[xx][yy];
4761 if (element == EL_TIMEGATE_CLOSED ||
4762 element == EL_TIMEGATE_CLOSING)
4764 Feld[xx][yy] = EL_TIMEGATE_OPENING;
4765 PlayLevelSound(xx, yy, SND_TIMEGATE_OPENING);
4769 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
4771 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
4772 DrawLevelField(xx, yy);
4778 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
4781 void Impact(int x, int y)
4783 boolean last_line = (y == lev_fieldy - 1);
4784 boolean object_hit = FALSE;
4785 boolean impact = (last_line || object_hit);
4786 int element = Feld[x][y];
4787 int smashed = EL_STEELWALL;
4789 if (!last_line) /* check if element below was hit */
4791 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
4794 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
4795 MovDir[x][y + 1] != MV_DOWN ||
4796 MovPos[x][y + 1] <= TILEY / 2));
4798 /* do not smash moving elements that left the smashed field in time */
4799 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
4800 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
4803 #if USE_QUICKSAND_IMPACT_BUGFIX
4804 if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
4806 RemoveMovingField(x, y + 1);
4807 Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
4808 Feld[x][y + 2] = EL_ROCK;
4809 DrawLevelField(x, y + 2);
4816 smashed = MovingOrBlocked2Element(x, y + 1);
4818 impact = (last_line || object_hit);
4821 if (!last_line && smashed == EL_ACID) /* element falls into acid */
4823 SplashAcid(x, y + 1);
4827 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
4828 /* only reset graphic animation if graphic really changes after impact */
4830 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
4832 ResetGfxAnimation(x, y);
4833 DrawLevelField(x, y);
4836 if (impact && CAN_EXPLODE_IMPACT(element))
4841 else if (impact && element == EL_PEARL)
4843 ResetGfxAnimation(x, y);
4845 Feld[x][y] = EL_PEARL_BREAKING;
4846 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4849 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
4851 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4856 if (impact && element == EL_AMOEBA_DROP)
4858 if (object_hit && IS_PLAYER(x, y + 1))
4859 KillPlayerUnlessEnemyProtected(x, y + 1);
4860 else if (object_hit && smashed == EL_PENGUIN)
4864 Feld[x][y] = EL_AMOEBA_GROWING;
4865 Store[x][y] = EL_AMOEBA_WET;
4867 ResetRandomAnimationValue(x, y);
4872 if (object_hit) /* check which object was hit */
4874 if (CAN_PASS_MAGIC_WALL(element) &&
4875 (smashed == EL_MAGIC_WALL ||
4876 smashed == EL_BD_MAGIC_WALL))
4879 int activated_magic_wall =
4880 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
4881 EL_BD_MAGIC_WALL_ACTIVE);
4883 /* activate magic wall / mill */
4885 SCAN_PLAYFIELD(xx, yy)
4887 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
4889 if (Feld[xx][yy] == smashed)
4890 Feld[xx][yy] = activated_magic_wall;
4892 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
4893 game.magic_wall_active = TRUE;
4895 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
4896 SND_MAGIC_WALL_ACTIVATING :
4897 SND_BD_MAGIC_WALL_ACTIVATING));
4900 if (IS_PLAYER(x, y + 1))
4902 if (CAN_SMASH_PLAYER(element))
4904 KillPlayerUnlessEnemyProtected(x, y + 1);
4908 else if (smashed == EL_PENGUIN)
4910 if (CAN_SMASH_PLAYER(element))
4916 else if (element == EL_BD_DIAMOND)
4918 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
4924 else if (((element == EL_SP_INFOTRON ||
4925 element == EL_SP_ZONK) &&
4926 (smashed == EL_SP_SNIKSNAK ||
4927 smashed == EL_SP_ELECTRON ||
4928 smashed == EL_SP_DISK_ORANGE)) ||
4929 (element == EL_SP_INFOTRON &&
4930 smashed == EL_SP_DISK_YELLOW))
4935 else if (CAN_SMASH_EVERYTHING(element))
4937 if (IS_CLASSIC_ENEMY(smashed) ||
4938 CAN_EXPLODE_SMASHED(smashed))
4943 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
4945 if (smashed == EL_LAMP ||
4946 smashed == EL_LAMP_ACTIVE)
4951 else if (smashed == EL_NUT)
4953 Feld[x][y + 1] = EL_NUT_BREAKING;
4954 PlayLevelSound(x, y, SND_NUT_BREAKING);
4955 RaiseScoreElement(EL_NUT);
4958 else if (smashed == EL_PEARL)
4960 ResetGfxAnimation(x, y);
4962 Feld[x][y + 1] = EL_PEARL_BREAKING;
4963 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4966 else if (smashed == EL_DIAMOND)
4968 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
4969 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
4972 else if (IS_BELT_SWITCH(smashed))
4974 ToggleBeltSwitch(x, y + 1);
4976 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
4977 smashed == EL_SWITCHGATE_SWITCH_DOWN)
4979 ToggleSwitchgateSwitch(x, y + 1);
4981 else if (smashed == EL_LIGHT_SWITCH ||
4982 smashed == EL_LIGHT_SWITCH_ACTIVE)
4984 ToggleLightSwitch(x, y + 1);
4989 TestIfElementSmashesCustomElement(x, y, MV_DOWN);
4992 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4994 CheckElementChangeBySide(x, y + 1, smashed, element,
4995 CE_SWITCHED, CH_SIDE_TOP);
4996 CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
5002 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
5007 /* play sound of magic wall / mill */
5009 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
5010 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
5012 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
5013 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
5014 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
5015 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
5020 /* play sound of object that hits the ground */
5021 if (last_line || object_hit)
5022 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
5025 inline static void TurnRoundExt(int x, int y)
5037 { 0, 0 }, { 0, 0 }, { 0, 0 },
5042 int left, right, back;
5046 { MV_DOWN, MV_UP, MV_RIGHT },
5047 { MV_UP, MV_DOWN, MV_LEFT },
5049 { MV_LEFT, MV_RIGHT, MV_DOWN },
5053 { MV_RIGHT, MV_LEFT, MV_UP }
5056 int element = Feld[x][y];
5057 int move_pattern = element_info[element].move_pattern;
5059 int old_move_dir = MovDir[x][y];
5060 int left_dir = turn[old_move_dir].left;
5061 int right_dir = turn[old_move_dir].right;
5062 int back_dir = turn[old_move_dir].back;
5064 int left_dx = move_xy[left_dir].dx, left_dy = move_xy[left_dir].dy;
5065 int right_dx = move_xy[right_dir].dx, right_dy = move_xy[right_dir].dy;
5066 int move_dx = move_xy[old_move_dir].dx, move_dy = move_xy[old_move_dir].dy;
5067 int back_dx = move_xy[back_dir].dx, back_dy = move_xy[back_dir].dy;
5069 int left_x = x + left_dx, left_y = y + left_dy;
5070 int right_x = x + right_dx, right_y = y + right_dy;
5071 int move_x = x + move_dx, move_y = y + move_dy;
5075 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
5077 TestIfBadThingTouchesOtherBadThing(x, y);
5079 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
5080 MovDir[x][y] = right_dir;
5081 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
5082 MovDir[x][y] = left_dir;
5084 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
5086 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
5089 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
5091 TestIfBadThingTouchesOtherBadThing(x, y);
5093 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
5094 MovDir[x][y] = left_dir;
5095 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
5096 MovDir[x][y] = right_dir;
5098 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
5100 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
5103 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
5105 TestIfBadThingTouchesOtherBadThing(x, y);
5107 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
5108 MovDir[x][y] = left_dir;
5109 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
5110 MovDir[x][y] = right_dir;
5112 if (MovDir[x][y] != old_move_dir)
5115 else if (element == EL_YAMYAM)
5117 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
5118 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
5120 if (can_turn_left && can_turn_right)
5121 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5122 else if (can_turn_left)
5123 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5124 else if (can_turn_right)
5125 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5127 MovDir[x][y] = back_dir;
5129 MovDelay[x][y] = 16 + 16 * RND(3);
5131 else if (element == EL_DARK_YAMYAM)
5133 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
5135 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
5138 if (can_turn_left && can_turn_right)
5139 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5140 else if (can_turn_left)
5141 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5142 else if (can_turn_right)
5143 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5145 MovDir[x][y] = back_dir;
5147 MovDelay[x][y] = 16 + 16 * RND(3);
5149 else if (element == EL_PACMAN)
5151 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
5152 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
5154 if (can_turn_left && can_turn_right)
5155 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5156 else if (can_turn_left)
5157 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5158 else if (can_turn_right)
5159 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5161 MovDir[x][y] = back_dir;
5163 MovDelay[x][y] = 6 + RND(40);
5165 else if (element == EL_PIG)
5167 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
5168 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
5169 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
5170 boolean should_turn_left, should_turn_right, should_move_on;
5172 int rnd = RND(rnd_value);
5174 should_turn_left = (can_turn_left &&
5176 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
5177 y + back_dy + left_dy)));
5178 should_turn_right = (can_turn_right &&
5180 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
5181 y + back_dy + right_dy)));
5182 should_move_on = (can_move_on &&
5185 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
5186 y + move_dy + left_dy) ||
5187 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
5188 y + move_dy + right_dy)));
5190 if (should_turn_left || should_turn_right || should_move_on)
5192 if (should_turn_left && should_turn_right && should_move_on)
5193 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
5194 rnd < 2 * rnd_value / 3 ? right_dir :
5196 else if (should_turn_left && should_turn_right)
5197 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
5198 else if (should_turn_left && should_move_on)
5199 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
5200 else if (should_turn_right && should_move_on)
5201 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
5202 else if (should_turn_left)
5203 MovDir[x][y] = left_dir;
5204 else if (should_turn_right)
5205 MovDir[x][y] = right_dir;
5206 else if (should_move_on)
5207 MovDir[x][y] = old_move_dir;
5209 else if (can_move_on && rnd > rnd_value / 8)
5210 MovDir[x][y] = old_move_dir;
5211 else if (can_turn_left && can_turn_right)
5212 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
5213 else if (can_turn_left && rnd > rnd_value / 8)
5214 MovDir[x][y] = left_dir;
5215 else if (can_turn_right && rnd > rnd_value/8)
5216 MovDir[x][y] = right_dir;
5218 MovDir[x][y] = back_dir;
5220 xx = x + move_xy[MovDir[x][y]].dx;
5221 yy = y + move_xy[MovDir[x][y]].dy;
5223 if (!IN_LEV_FIELD(xx, yy) ||
5224 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
5225 MovDir[x][y] = old_move_dir;
5229 else if (element == EL_DRAGON)
5231 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
5232 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
5233 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
5235 int rnd = RND(rnd_value);
5237 if (can_move_on && rnd > rnd_value / 8)
5238 MovDir[x][y] = old_move_dir;
5239 else if (can_turn_left && can_turn_right)
5240 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
5241 else if (can_turn_left && rnd > rnd_value / 8)
5242 MovDir[x][y] = left_dir;
5243 else if (can_turn_right && rnd > rnd_value / 8)
5244 MovDir[x][y] = right_dir;
5246 MovDir[x][y] = back_dir;
5248 xx = x + move_xy[MovDir[x][y]].dx;
5249 yy = y + move_xy[MovDir[x][y]].dy;
5251 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
5252 MovDir[x][y] = old_move_dir;
5256 else if (element == EL_MOLE)
5258 boolean can_move_on =
5259 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
5260 IS_AMOEBOID(Feld[move_x][move_y]) ||
5261 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
5264 boolean can_turn_left =
5265 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
5266 IS_AMOEBOID(Feld[left_x][left_y])));
5268 boolean can_turn_right =
5269 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
5270 IS_AMOEBOID(Feld[right_x][right_y])));
5272 if (can_turn_left && can_turn_right)
5273 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
5274 else if (can_turn_left)
5275 MovDir[x][y] = left_dir;
5277 MovDir[x][y] = right_dir;
5280 if (MovDir[x][y] != old_move_dir)
5283 else if (element == EL_BALLOON)
5285 MovDir[x][y] = game.wind_direction;
5288 else if (element == EL_SPRING)
5290 #if USE_NEW_SPRING_BUMPER
5291 if (MovDir[x][y] & MV_HORIZONTAL)
5293 if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
5294 !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
5296 Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
5297 ResetGfxAnimation(move_x, move_y);
5298 DrawLevelField(move_x, move_y);
5300 MovDir[x][y] = back_dir;
5302 else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
5303 SPRING_CAN_ENTER_FIELD(element, x, y + 1))
5304 MovDir[x][y] = MV_NONE;
5307 if (MovDir[x][y] & MV_HORIZONTAL &&
5308 (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
5309 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
5310 MovDir[x][y] = MV_NONE;
5315 else if (element == EL_ROBOT ||
5316 element == EL_SATELLITE ||
5317 element == EL_PENGUIN ||
5318 element == EL_EMC_ANDROID)
5320 int attr_x = -1, attr_y = -1;
5331 for (i = 0; i < MAX_PLAYERS; i++)
5333 struct PlayerInfo *player = &stored_player[i];
5334 int jx = player->jx, jy = player->jy;
5336 if (!player->active)
5340 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
5348 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
5349 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
5350 game.engine_version < VERSION_IDENT(3,1,0,0)))
5356 if (element == EL_PENGUIN)
5359 static int xy[4][2] =
5367 for (i = 0; i < NUM_DIRECTIONS; i++)
5369 int ex = x + xy[i][0];
5370 int ey = y + xy[i][1];
5372 if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
5381 MovDir[x][y] = MV_NONE;
5383 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
5384 else if (attr_x > x)
5385 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
5387 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
5388 else if (attr_y > y)
5389 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
5391 if (element == EL_ROBOT)
5395 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5396 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
5397 Moving2Blocked(x, y, &newx, &newy);
5399 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
5400 MovDelay[x][y] = 8 + 8 * !RND(3);
5402 MovDelay[x][y] = 16;
5404 else if (element == EL_PENGUIN)
5410 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5412 boolean first_horiz = RND(2);
5413 int new_move_dir = MovDir[x][y];
5416 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5417 Moving2Blocked(x, y, &newx, &newy);
5419 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
5423 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5424 Moving2Blocked(x, y, &newx, &newy);
5426 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
5429 MovDir[x][y] = old_move_dir;
5433 else if (element == EL_SATELLITE)
5439 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5441 boolean first_horiz = RND(2);
5442 int new_move_dir = MovDir[x][y];
5445 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5446 Moving2Blocked(x, y, &newx, &newy);
5448 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
5452 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5453 Moving2Blocked(x, y, &newx, &newy);
5455 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
5458 MovDir[x][y] = old_move_dir;
5462 else if (element == EL_EMC_ANDROID)
5464 static int check_pos[16] =
5466 -1, /* 0 => (invalid) */
5467 7, /* 1 => MV_LEFT */
5468 3, /* 2 => MV_RIGHT */
5469 -1, /* 3 => (invalid) */
5471 0, /* 5 => MV_LEFT | MV_UP */
5472 2, /* 6 => MV_RIGHT | MV_UP */
5473 -1, /* 7 => (invalid) */
5474 5, /* 8 => MV_DOWN */
5475 6, /* 9 => MV_LEFT | MV_DOWN */
5476 4, /* 10 => MV_RIGHT | MV_DOWN */
5477 -1, /* 11 => (invalid) */
5478 -1, /* 12 => (invalid) */
5479 -1, /* 13 => (invalid) */
5480 -1, /* 14 => (invalid) */
5481 -1, /* 15 => (invalid) */
5489 { -1, -1, MV_LEFT | MV_UP },
5491 { +1, -1, MV_RIGHT | MV_UP },
5492 { +1, 0, MV_RIGHT },
5493 { +1, +1, MV_RIGHT | MV_DOWN },
5495 { -1, +1, MV_LEFT | MV_DOWN },
5498 int start_pos, check_order;
5499 boolean can_clone = FALSE;
5502 /* check if there is any free field around current position */
5503 for (i = 0; i < 8; i++)
5505 int newx = x + check_xy[i].dx;
5506 int newy = y + check_xy[i].dy;
5508 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5516 if (can_clone) /* randomly find an element to clone */
5520 start_pos = check_pos[RND(8)];
5521 check_order = (RND(2) ? -1 : +1);
5523 for (i = 0; i < 8; i++)
5525 int pos_raw = start_pos + i * check_order;
5526 int pos = (pos_raw + 8) % 8;
5527 int newx = x + check_xy[pos].dx;
5528 int newy = y + check_xy[pos].dy;
5530 if (ANDROID_CAN_CLONE_FIELD(newx, newy))
5532 element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
5533 element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
5535 Store[x][y] = Feld[newx][newy];
5544 if (can_clone) /* randomly find a direction to move */
5548 start_pos = check_pos[RND(8)];
5549 check_order = (RND(2) ? -1 : +1);
5551 for (i = 0; i < 8; i++)
5553 int pos_raw = start_pos + i * check_order;
5554 int pos = (pos_raw + 8) % 8;
5555 int newx = x + check_xy[pos].dx;
5556 int newy = y + check_xy[pos].dy;
5557 int new_move_dir = check_xy[pos].dir;
5559 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5561 MovDir[x][y] = new_move_dir;
5562 MovDelay[x][y] = level.android_clone_time * 8 + 1;
5571 if (can_clone) /* cloning and moving successful */
5574 /* cannot clone -- try to move towards player */
5576 start_pos = check_pos[MovDir[x][y] & 0x0f];
5577 check_order = (RND(2) ? -1 : +1);
5579 for (i = 0; i < 3; i++)
5581 /* first check start_pos, then previous/next or (next/previous) pos */
5582 int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
5583 int pos = (pos_raw + 8) % 8;
5584 int newx = x + check_xy[pos].dx;
5585 int newy = y + check_xy[pos].dy;
5586 int new_move_dir = check_xy[pos].dir;
5588 if (IS_PLAYER(newx, newy))
5591 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
5593 MovDir[x][y] = new_move_dir;
5594 MovDelay[x][y] = level.android_move_time * 8 + 1;
5601 else if (move_pattern == MV_TURNING_LEFT ||
5602 move_pattern == MV_TURNING_RIGHT ||
5603 move_pattern == MV_TURNING_LEFT_RIGHT ||
5604 move_pattern == MV_TURNING_RIGHT_LEFT ||
5605 move_pattern == MV_TURNING_RANDOM ||
5606 move_pattern == MV_ALL_DIRECTIONS)
5608 boolean can_turn_left =
5609 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
5610 boolean can_turn_right =
5611 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
5613 if (element_info[element].move_stepsize == 0) /* "not moving" */
5616 if (move_pattern == MV_TURNING_LEFT)
5617 MovDir[x][y] = left_dir;
5618 else if (move_pattern == MV_TURNING_RIGHT)
5619 MovDir[x][y] = right_dir;
5620 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
5621 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
5622 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
5623 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
5624 else if (move_pattern == MV_TURNING_RANDOM)
5625 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
5626 can_turn_right && !can_turn_left ? right_dir :
5627 RND(2) ? left_dir : right_dir);
5628 else if (can_turn_left && can_turn_right)
5629 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5630 else if (can_turn_left)
5631 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5632 else if (can_turn_right)
5633 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5635 MovDir[x][y] = back_dir;
5637 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5639 else if (move_pattern == MV_HORIZONTAL ||
5640 move_pattern == MV_VERTICAL)
5642 if (move_pattern & old_move_dir)
5643 MovDir[x][y] = back_dir;
5644 else if (move_pattern == MV_HORIZONTAL)
5645 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
5646 else if (move_pattern == MV_VERTICAL)
5647 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
5649 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5651 else if (move_pattern & MV_ANY_DIRECTION)
5653 MovDir[x][y] = move_pattern;
5654 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5656 else if (move_pattern & MV_WIND_DIRECTION)
5658 MovDir[x][y] = game.wind_direction;
5659 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5661 else if (move_pattern == MV_ALONG_LEFT_SIDE)
5663 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
5664 MovDir[x][y] = left_dir;
5665 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5666 MovDir[x][y] = right_dir;
5668 if (MovDir[x][y] != old_move_dir)
5669 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5671 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
5673 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
5674 MovDir[x][y] = right_dir;
5675 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5676 MovDir[x][y] = left_dir;
5678 if (MovDir[x][y] != old_move_dir)
5679 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5681 else if (move_pattern == MV_TOWARDS_PLAYER ||
5682 move_pattern == MV_AWAY_FROM_PLAYER)
5684 int attr_x = -1, attr_y = -1;
5686 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
5697 for (i = 0; i < MAX_PLAYERS; i++)
5699 struct PlayerInfo *player = &stored_player[i];
5700 int jx = player->jx, jy = player->jy;
5702 if (!player->active)
5706 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
5714 MovDir[x][y] = MV_NONE;
5716 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
5717 else if (attr_x > x)
5718 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
5720 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
5721 else if (attr_y > y)
5722 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
5724 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5726 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5728 boolean first_horiz = RND(2);
5729 int new_move_dir = MovDir[x][y];
5731 if (element_info[element].move_stepsize == 0) /* "not moving" */
5733 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
5734 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5740 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5741 Moving2Blocked(x, y, &newx, &newy);
5743 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5747 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5748 Moving2Blocked(x, y, &newx, &newy);
5750 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5753 MovDir[x][y] = old_move_dir;
5756 else if (move_pattern == MV_WHEN_PUSHED ||
5757 move_pattern == MV_WHEN_DROPPED)
5759 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5760 MovDir[x][y] = MV_NONE;
5764 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
5766 static int test_xy[7][2] =
5776 static int test_dir[7] =
5786 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
5787 int move_preference = -1000000; /* start with very low preference */
5788 int new_move_dir = MV_NONE;
5789 int start_test = RND(4);
5792 for (i = 0; i < NUM_DIRECTIONS; i++)
5794 int move_dir = test_dir[start_test + i];
5795 int move_dir_preference;
5797 xx = x + test_xy[start_test + i][0];
5798 yy = y + test_xy[start_test + i][1];
5800 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
5801 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
5803 new_move_dir = move_dir;
5808 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
5811 move_dir_preference = -1 * RunnerVisit[xx][yy];
5812 if (hunter_mode && PlayerVisit[xx][yy] > 0)
5813 move_dir_preference = PlayerVisit[xx][yy];
5815 if (move_dir_preference > move_preference)
5817 /* prefer field that has not been visited for the longest time */
5818 move_preference = move_dir_preference;
5819 new_move_dir = move_dir;
5821 else if (move_dir_preference == move_preference &&
5822 move_dir == old_move_dir)
5824 /* prefer last direction when all directions are preferred equally */
5825 move_preference = move_dir_preference;
5826 new_move_dir = move_dir;
5830 MovDir[x][y] = new_move_dir;
5831 if (old_move_dir != new_move_dir)
5832 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5836 static void TurnRound(int x, int y)
5838 int direction = MovDir[x][y];
5840 int element, graphic;
5845 GfxDir[x][y] = MovDir[x][y];
5847 if (direction != MovDir[x][y])
5851 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
5854 ResetGfxFrame(x, y, FALSE);
5856 element = Feld[x][y];
5857 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5859 if (graphic_info[graphic].anim_global_sync)
5860 GfxFrame[x][y] = FrameCounter;
5861 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5862 GfxFrame[x][y] = CustomValue[x][y];
5863 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5864 GfxFrame[x][y] = element_info[element].collect_score;
5865 else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5866 GfxFrame[x][y] = ChangeDelay[x][y];
5870 static boolean JustBeingPushed(int x, int y)
5874 for (i = 0; i < MAX_PLAYERS; i++)
5876 struct PlayerInfo *player = &stored_player[i];
5878 if (player->active && player->is_pushing && player->MovPos)
5880 int next_jx = player->jx + (player->jx - player->last_jx);
5881 int next_jy = player->jy + (player->jy - player->last_jy);
5883 if (x == next_jx && y == next_jy)
5891 void StartMoving(int x, int y)
5893 boolean started_moving = FALSE; /* some elements can fall _and_ move */
5894 int element = Feld[x][y];
5899 if (MovDelay[x][y] == 0)
5900 GfxAction[x][y] = ACTION_DEFAULT;
5902 if (CAN_FALL(element) && y < lev_fieldy - 1)
5904 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
5905 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
5906 if (JustBeingPushed(x, y))
5909 if (element == EL_QUICKSAND_FULL)
5911 if (IS_FREE(x, y + 1))
5913 InitMovingField(x, y, MV_DOWN);
5914 started_moving = TRUE;
5916 Feld[x][y] = EL_QUICKSAND_EMPTYING;
5917 #if USE_QUICKSAND_BD_ROCK_BUGFIX
5918 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
5919 Store[x][y] = EL_ROCK;
5921 Store[x][y] = EL_ROCK;
5924 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
5926 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5928 if (!MovDelay[x][y])
5929 MovDelay[x][y] = TILEY + 1;
5938 Feld[x][y] = EL_QUICKSAND_EMPTY;
5939 Feld[x][y + 1] = EL_QUICKSAND_FULL;
5940 Store[x][y + 1] = Store[x][y];
5943 PlayLevelSoundAction(x, y, ACTION_FILLING);
5946 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
5947 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5949 InitMovingField(x, y, MV_DOWN);
5950 started_moving = TRUE;
5952 Feld[x][y] = EL_QUICKSAND_FILLING;
5953 Store[x][y] = element;
5955 PlayLevelSoundAction(x, y, ACTION_FILLING);
5957 else if (element == EL_MAGIC_WALL_FULL)
5959 if (IS_FREE(x, y + 1))
5961 InitMovingField(x, y, MV_DOWN);
5962 started_moving = TRUE;
5964 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
5965 Store[x][y] = EL_CHANGED(Store[x][y]);
5967 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
5969 if (!MovDelay[x][y])
5970 MovDelay[x][y] = TILEY/4 + 1;
5979 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
5980 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
5981 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
5985 else if (element == EL_BD_MAGIC_WALL_FULL)
5987 if (IS_FREE(x, y + 1))
5989 InitMovingField(x, y, MV_DOWN);
5990 started_moving = TRUE;
5992 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
5993 Store[x][y] = EL_CHANGED2(Store[x][y]);
5995 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
5997 if (!MovDelay[x][y])
5998 MovDelay[x][y] = TILEY/4 + 1;
6007 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
6008 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
6009 Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
6013 else if (CAN_PASS_MAGIC_WALL(element) &&
6014 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6015 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
6017 InitMovingField(x, y, MV_DOWN);
6018 started_moving = TRUE;
6021 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
6022 EL_BD_MAGIC_WALL_FILLING);
6023 Store[x][y] = element;
6025 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
6027 SplashAcid(x, y + 1);
6029 InitMovingField(x, y, MV_DOWN);
6030 started_moving = TRUE;
6032 Store[x][y] = EL_ACID;
6034 else if ((game.engine_version >= VERSION_IDENT(3,1,0,0) &&
6035 CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
6037 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
6038 CAN_FALL(element) && WasJustFalling[x][y] &&
6039 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
6041 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
6042 CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
6043 (Feld[x][y + 1] == EL_BLOCKED)))
6045 /* this is needed for a special case not covered by calling "Impact()"
6046 from "ContinueMoving()": if an element moves to a tile directly below
6047 another element which was just falling on that tile (which was empty
6048 in the previous frame), the falling element above would just stop
6049 instead of smashing the element below (in previous version, the above
6050 element was just checked for "moving" instead of "falling", resulting
6051 in incorrect smashes caused by horizontal movement of the above
6052 element; also, the case of the player being the element to smash was
6053 simply not covered here... :-/ ) */
6055 CheckCollision[x][y] = 0;
6059 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
6061 if (MovDir[x][y] == MV_NONE)
6063 InitMovingField(x, y, MV_DOWN);
6064 started_moving = TRUE;
6067 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
6069 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
6070 MovDir[x][y] = MV_DOWN;
6072 InitMovingField(x, y, MV_DOWN);
6073 started_moving = TRUE;
6075 else if (element == EL_AMOEBA_DROP)
6077 Feld[x][y] = EL_AMOEBA_GROWING;
6078 Store[x][y] = EL_AMOEBA_WET;
6080 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
6081 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
6082 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
6083 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
6085 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
6086 (IS_FREE(x - 1, y + 1) ||
6087 Feld[x - 1][y + 1] == EL_ACID));
6088 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
6089 (IS_FREE(x + 1, y + 1) ||
6090 Feld[x + 1][y + 1] == EL_ACID));
6091 boolean can_fall_any = (can_fall_left || can_fall_right);
6092 boolean can_fall_both = (can_fall_left && can_fall_right);
6093 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
6095 #if USE_NEW_ALL_SLIPPERY
6096 if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
6098 if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
6099 can_fall_right = FALSE;
6100 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
6101 can_fall_left = FALSE;
6102 else if (slippery_type == SLIPPERY_ONLY_LEFT)
6103 can_fall_right = FALSE;
6104 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
6105 can_fall_left = FALSE;
6107 can_fall_any = (can_fall_left || can_fall_right);
6108 can_fall_both = FALSE;
6111 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
6113 if (slippery_type == SLIPPERY_ONLY_LEFT)
6114 can_fall_right = FALSE;
6115 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
6116 can_fall_left = FALSE;
6117 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
6118 can_fall_right = FALSE;
6119 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
6120 can_fall_left = FALSE;
6122 can_fall_any = (can_fall_left || can_fall_right);
6123 can_fall_both = (can_fall_left && can_fall_right);
6127 #if USE_NEW_ALL_SLIPPERY
6129 #if USE_NEW_SP_SLIPPERY
6130 /* !!! better use the same properties as for custom elements here !!! */
6131 else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
6132 can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
6134 can_fall_right = FALSE; /* slip down on left side */
6135 can_fall_both = FALSE;
6140 #if USE_NEW_ALL_SLIPPERY
6143 if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
6144 can_fall_right = FALSE; /* slip down on left side */
6146 can_fall_left = !(can_fall_right = RND(2));
6148 can_fall_both = FALSE;
6153 if (game.emulation == EMU_BOULDERDASH ||
6154 element == EL_BD_ROCK || element == EL_BD_DIAMOND)
6155 can_fall_right = FALSE; /* slip down on left side */
6157 can_fall_left = !(can_fall_right = RND(2));
6159 can_fall_both = FALSE;
6165 /* if not determined otherwise, prefer left side for slipping down */
6166 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
6167 started_moving = TRUE;
6171 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
6173 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
6176 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
6177 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
6178 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
6179 int belt_dir = game.belt_dir[belt_nr];
6181 if ((belt_dir == MV_LEFT && left_is_free) ||
6182 (belt_dir == MV_RIGHT && right_is_free))
6184 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
6186 InitMovingField(x, y, belt_dir);
6187 started_moving = TRUE;
6189 Pushed[x][y] = TRUE;
6190 Pushed[nextx][y] = TRUE;
6192 GfxAction[x][y] = ACTION_DEFAULT;
6196 MovDir[x][y] = 0; /* if element was moving, stop it */
6201 /* not "else if" because of elements that can fall and move (EL_SPRING) */
6203 if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
6205 if (CAN_MOVE(element) && !started_moving)
6208 int move_pattern = element_info[element].move_pattern;
6213 if (MovDir[x][y] == MV_NONE)
6215 printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
6216 x, y, element, element_info[element].token_name);
6217 printf("StartMoving(): This should never happen!\n");
6222 Moving2Blocked(x, y, &newx, &newy);
6224 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
6227 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
6228 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6230 WasJustMoving[x][y] = 0;
6231 CheckCollision[x][y] = 0;
6233 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
6235 if (Feld[x][y] != element) /* element has changed */
6239 if (!MovDelay[x][y]) /* start new movement phase */
6241 /* all objects that can change their move direction after each step
6242 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
6244 if (element != EL_YAMYAM &&
6245 element != EL_DARK_YAMYAM &&
6246 element != EL_PACMAN &&
6247 !(move_pattern & MV_ANY_DIRECTION) &&
6248 move_pattern != MV_TURNING_LEFT &&
6249 move_pattern != MV_TURNING_RIGHT &&
6250 move_pattern != MV_TURNING_LEFT_RIGHT &&
6251 move_pattern != MV_TURNING_RIGHT_LEFT &&
6252 move_pattern != MV_TURNING_RANDOM)
6256 if (MovDelay[x][y] && (element == EL_BUG ||
6257 element == EL_SPACESHIP ||
6258 element == EL_SP_SNIKSNAK ||
6259 element == EL_SP_ELECTRON ||
6260 element == EL_MOLE))
6261 DrawLevelField(x, y);
6265 if (MovDelay[x][y]) /* wait some time before next movement */
6269 if (element == EL_ROBOT ||
6270 element == EL_YAMYAM ||
6271 element == EL_DARK_YAMYAM)
6273 DrawLevelElementAnimationIfNeeded(x, y, element);
6274 PlayLevelSoundAction(x, y, ACTION_WAITING);
6276 else if (element == EL_SP_ELECTRON)
6277 DrawLevelElementAnimationIfNeeded(x, y, element);
6278 else if (element == EL_DRAGON)
6281 int dir = MovDir[x][y];
6282 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
6283 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
6284 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
6285 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
6286 dir == MV_UP ? IMG_FLAMES_1_UP :
6287 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
6288 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
6290 GfxAction[x][y] = ACTION_ATTACKING;
6292 if (IS_PLAYER(x, y))
6293 DrawPlayerField(x, y);
6295 DrawLevelField(x, y);
6297 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
6299 for (i = 1; i <= 3; i++)
6301 int xx = x + i * dx;
6302 int yy = y + i * dy;
6303 int sx = SCREENX(xx);
6304 int sy = SCREENY(yy);
6305 int flame_graphic = graphic + (i - 1);
6307 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
6312 int flamed = MovingOrBlocked2Element(xx, yy);
6316 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
6318 else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
6319 RemoveMovingField(xx, yy);
6321 RemoveField(xx, yy);
6323 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
6326 RemoveMovingField(xx, yy);
6329 ChangeDelay[xx][yy] = 0;
6331 Feld[xx][yy] = EL_FLAMES;
6333 if (IN_SCR_FIELD(sx, sy))
6335 DrawLevelFieldCrumbledSand(xx, yy);
6336 DrawGraphic(sx, sy, flame_graphic, frame);
6341 if (Feld[xx][yy] == EL_FLAMES)
6342 Feld[xx][yy] = EL_EMPTY;
6343 DrawLevelField(xx, yy);
6348 if (MovDelay[x][y]) /* element still has to wait some time */
6350 PlayLevelSoundAction(x, y, ACTION_WAITING);
6356 /* now make next step */
6358 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
6360 if (DONT_COLLIDE_WITH(element) &&
6361 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
6362 !PLAYER_ENEMY_PROTECTED(newx, newy))
6364 TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
6369 else if (CAN_MOVE_INTO_ACID(element) &&
6370 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
6371 !IS_MV_DIAGONAL(MovDir[x][y]) &&
6372 (MovDir[x][y] == MV_DOWN ||
6373 game.engine_version >= VERSION_IDENT(3,1,0,0)))
6375 SplashAcid(newx, newy);
6376 Store[x][y] = EL_ACID;
6378 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
6380 if (Feld[newx][newy] == EL_EXIT_OPEN)
6383 DrawLevelField(x, y);
6385 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
6386 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
6387 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
6389 local_player->friends_still_needed--;
6390 if (!local_player->friends_still_needed &&
6391 !local_player->GameOver && AllPlayersGone)
6392 local_player->LevelSolved = local_player->GameOver = TRUE;
6396 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
6398 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
6399 DrawLevelField(newx, newy);
6401 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
6403 else if (!IS_FREE(newx, newy))
6405 GfxAction[x][y] = ACTION_WAITING;
6407 if (IS_PLAYER(x, y))
6408 DrawPlayerField(x, y);
6410 DrawLevelField(x, y);
6415 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
6417 if (IS_FOOD_PIG(Feld[newx][newy]))
6419 if (IS_MOVING(newx, newy))
6420 RemoveMovingField(newx, newy);
6423 Feld[newx][newy] = EL_EMPTY;
6424 DrawLevelField(newx, newy);
6427 PlayLevelSound(x, y, SND_PIG_DIGGING);
6429 else if (!IS_FREE(newx, newy))
6431 if (IS_PLAYER(x, y))
6432 DrawPlayerField(x, y);
6434 DrawLevelField(x, y);
6439 else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
6441 if (Store[x][y] != EL_EMPTY)
6443 boolean can_clone = FALSE;
6446 /* check if element to clone is still there */
6447 for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
6449 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
6457 /* cannot clone or target field not free anymore -- do not clone */
6458 if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6459 Store[x][y] = EL_EMPTY;
6462 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6464 if (IS_MV_DIAGONAL(MovDir[x][y]))
6466 int diagonal_move_dir = MovDir[x][y];
6467 int stored = Store[x][y];
6468 int change_delay = 8;
6471 /* android is moving diagonally */
6473 CreateField(x, y, EL_DIAGONAL_SHRINKING);
6475 Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
6476 GfxElement[x][y] = EL_EMC_ANDROID;
6477 GfxAction[x][y] = ACTION_SHRINKING;
6478 GfxDir[x][y] = diagonal_move_dir;
6479 ChangeDelay[x][y] = change_delay;
6481 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
6484 DrawLevelGraphicAnimation(x, y, graphic);
6485 PlayLevelSoundAction(x, y, ACTION_SHRINKING);
6487 if (Feld[newx][newy] == EL_ACID)
6489 SplashAcid(newx, newy);
6494 CreateField(newx, newy, EL_DIAGONAL_GROWING);
6496 Store[newx][newy] = EL_EMC_ANDROID;
6497 GfxElement[newx][newy] = EL_EMC_ANDROID;
6498 GfxAction[newx][newy] = ACTION_GROWING;
6499 GfxDir[newx][newy] = diagonal_move_dir;
6500 ChangeDelay[newx][newy] = change_delay;
6502 graphic = el_act_dir2img(GfxElement[newx][newy],
6503 GfxAction[newx][newy], GfxDir[newx][newy]);
6505 DrawLevelGraphicAnimation(newx, newy, graphic);
6506 PlayLevelSoundAction(newx, newy, ACTION_GROWING);
6512 Feld[newx][newy] = EL_EMPTY;
6513 DrawLevelField(newx, newy);
6515 PlayLevelSoundAction(x, y, ACTION_DIGGING);
6518 else if (!IS_FREE(newx, newy))
6521 if (IS_PLAYER(x, y))
6522 DrawPlayerField(x, y);
6524 DrawLevelField(x, y);
6530 else if (IS_CUSTOM_ELEMENT(element) &&
6531 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6533 int new_element = Feld[newx][newy];
6535 if (!IS_FREE(newx, newy))
6537 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
6538 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
6541 /* no element can dig solid indestructible elements */
6542 if (IS_INDESTRUCTIBLE(new_element) &&
6543 !IS_DIGGABLE(new_element) &&
6544 !IS_COLLECTIBLE(new_element))
6547 if (AmoebaNr[newx][newy] &&
6548 (new_element == EL_AMOEBA_FULL ||
6549 new_element == EL_BD_AMOEBA ||
6550 new_element == EL_AMOEBA_GROWING))
6552 AmoebaCnt[AmoebaNr[newx][newy]]--;
6553 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6556 if (IS_MOVING(newx, newy))
6557 RemoveMovingField(newx, newy);
6560 RemoveField(newx, newy);
6561 DrawLevelField(newx, newy);
6564 /* if digged element was about to explode, prevent the explosion */
6565 ExplodeField[newx][newy] = EX_TYPE_NONE;
6567 PlayLevelSoundAction(x, y, action);
6570 Store[newx][newy] = EL_EMPTY;
6572 /* this makes it possible to leave the removed element again */
6573 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
6574 Store[newx][newy] = new_element;
6576 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
6578 int move_leave_element = element_info[element].move_leave_element;
6580 /* this makes it possible to leave the removed element again */
6581 Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
6582 new_element : move_leave_element);
6586 if (move_pattern & MV_MAZE_RUNNER_STYLE)
6588 RunnerVisit[x][y] = FrameCounter;
6589 PlayerVisit[x][y] /= 8; /* expire player visit path */
6592 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
6594 if (!IS_FREE(newx, newy))
6596 if (IS_PLAYER(x, y))
6597 DrawPlayerField(x, y);
6599 DrawLevelField(x, y);
6605 boolean wanna_flame = !RND(10);
6606 int dx = newx - x, dy = newy - y;
6607 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
6608 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
6609 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
6610 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
6611 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
6612 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
6615 IS_CLASSIC_ENEMY(element1) ||
6616 IS_CLASSIC_ENEMY(element2)) &&
6617 element1 != EL_DRAGON && element2 != EL_DRAGON &&
6618 element1 != EL_FLAMES && element2 != EL_FLAMES)
6620 ResetGfxAnimation(x, y);
6621 GfxAction[x][y] = ACTION_ATTACKING;
6623 if (IS_PLAYER(x, y))
6624 DrawPlayerField(x, y);
6626 DrawLevelField(x, y);
6628 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
6630 MovDelay[x][y] = 50;
6634 RemoveField(newx, newy);
6636 Feld[newx][newy] = EL_FLAMES;
6637 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
6640 RemoveField(newx1, newy1);
6642 Feld[newx1][newy1] = EL_FLAMES;
6644 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
6647 RemoveField(newx2, newy2);
6649 Feld[newx2][newy2] = EL_FLAMES;
6656 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6657 Feld[newx][newy] == EL_DIAMOND)
6659 if (IS_MOVING(newx, newy))
6660 RemoveMovingField(newx, newy);
6663 Feld[newx][newy] = EL_EMPTY;
6664 DrawLevelField(newx, newy);
6667 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
6669 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6670 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
6672 if (AmoebaNr[newx][newy])
6674 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6675 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
6676 Feld[newx][newy] == EL_BD_AMOEBA)
6677 AmoebaCnt[AmoebaNr[newx][newy]]--;
6682 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
6684 RemoveMovingField(newx, newy);
6687 if (IS_MOVING(newx, newy))
6689 RemoveMovingField(newx, newy);
6694 Feld[newx][newy] = EL_EMPTY;
6695 DrawLevelField(newx, newy);
6698 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
6700 else if ((element == EL_PACMAN || element == EL_MOLE)
6701 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
6703 if (AmoebaNr[newx][newy])
6705 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6706 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
6707 Feld[newx][newy] == EL_BD_AMOEBA)
6708 AmoebaCnt[AmoebaNr[newx][newy]]--;
6711 if (element == EL_MOLE)
6713 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
6714 PlayLevelSound(x, y, SND_MOLE_DIGGING);
6716 ResetGfxAnimation(x, y);
6717 GfxAction[x][y] = ACTION_DIGGING;
6718 DrawLevelField(x, y);
6720 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
6722 return; /* wait for shrinking amoeba */
6724 else /* element == EL_PACMAN */
6726 Feld[newx][newy] = EL_EMPTY;
6727 DrawLevelField(newx, newy);
6728 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
6731 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
6732 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
6733 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
6735 /* wait for shrinking amoeba to completely disappear */
6738 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
6740 /* object was running against a wall */
6745 /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
6746 if (move_pattern & MV_ANY_DIRECTION &&
6747 move_pattern == MovDir[x][y])
6749 int blocking_element =
6750 (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
6752 CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
6755 element = Feld[x][y]; /* element might have changed */
6759 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
6760 DrawLevelElementAnimation(x, y, element);
6762 if (DONT_TOUCH(element))
6763 TestIfBadThingTouchesPlayer(x, y);
6768 InitMovingField(x, y, MovDir[x][y]);
6770 PlayLevelSoundAction(x, y, ACTION_MOVING);
6774 ContinueMoving(x, y);
6777 void ContinueMoving(int x, int y)
6779 int element = Feld[x][y];
6780 struct ElementInfo *ei = &element_info[element];
6781 int direction = MovDir[x][y];
6782 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
6783 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
6784 int newx = x + dx, newy = y + dy;
6785 int stored = Store[x][y];
6786 int stored_new = Store[newx][newy];
6787 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
6788 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
6789 boolean last_line = (newy == lev_fieldy - 1);
6791 MovPos[x][y] += getElementMoveStepsize(x, y);
6793 if (pushed_by_player) /* special case: moving object pushed by player */
6794 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
6796 if (ABS(MovPos[x][y]) < TILEX)
6798 DrawLevelField(x, y);
6800 return; /* element is still moving */
6803 /* element reached destination field */
6805 Feld[x][y] = EL_EMPTY;
6806 Feld[newx][newy] = element;
6807 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
6809 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
6811 element = Feld[newx][newy] = EL_ACID;
6813 else if (element == EL_MOLE)
6815 Feld[x][y] = EL_SAND;
6817 DrawLevelFieldCrumbledSandNeighbours(x, y);
6819 else if (element == EL_QUICKSAND_FILLING)
6821 element = Feld[newx][newy] = get_next_element(element);
6822 Store[newx][newy] = Store[x][y];
6824 else if (element == EL_QUICKSAND_EMPTYING)
6826 Feld[x][y] = get_next_element(element);
6827 element = Feld[newx][newy] = Store[x][y];
6829 else if (element == EL_MAGIC_WALL_FILLING)
6831 element = Feld[newx][newy] = get_next_element(element);
6832 if (!game.magic_wall_active)
6833 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
6834 Store[newx][newy] = Store[x][y];
6836 else if (element == EL_MAGIC_WALL_EMPTYING)
6838 Feld[x][y] = get_next_element(element);
6839 if (!game.magic_wall_active)
6840 Feld[x][y] = EL_MAGIC_WALL_DEAD;
6841 element = Feld[newx][newy] = Store[x][y];
6843 #if USE_NEW_CUSTOM_VALUE
6844 InitField(newx, newy, FALSE);
6847 else if (element == EL_BD_MAGIC_WALL_FILLING)
6849 element = Feld[newx][newy] = get_next_element(element);
6850 if (!game.magic_wall_active)
6851 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
6852 Store[newx][newy] = Store[x][y];
6854 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
6856 Feld[x][y] = get_next_element(element);
6857 if (!game.magic_wall_active)
6858 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
6859 element = Feld[newx][newy] = Store[x][y];
6861 #if USE_NEW_CUSTOM_VALUE
6862 InitField(newx, newy, FALSE);
6865 else if (element == EL_AMOEBA_DROPPING)
6867 Feld[x][y] = get_next_element(element);
6868 element = Feld[newx][newy] = Store[x][y];
6870 else if (element == EL_SOKOBAN_OBJECT)
6873 Feld[x][y] = Back[x][y];
6875 if (Back[newx][newy])
6876 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
6878 Back[x][y] = Back[newx][newy] = 0;
6881 Store[x][y] = EL_EMPTY;
6886 MovDelay[newx][newy] = 0;
6889 if (CAN_CHANGE_OR_HAS_ACTION(element))
6891 if (CAN_CHANGE(element))
6894 /* copy element change control values to new field */
6895 ChangeDelay[newx][newy] = ChangeDelay[x][y];
6896 ChangePage[newx][newy] = ChangePage[x][y];
6897 ChangeCount[newx][newy] = ChangeCount[x][y];
6898 ChangeEvent[newx][newy] = ChangeEvent[x][y];
6901 #if USE_NEW_CUSTOM_VALUE
6902 CustomValue[newx][newy] = CustomValue[x][y];
6908 #if USE_NEW_CUSTOM_VALUE
6909 CustomValue[newx][newy] = CustomValue[x][y];
6913 ChangeDelay[x][y] = 0;
6914 ChangePage[x][y] = -1;
6915 ChangeCount[x][y] = 0;
6916 ChangeEvent[x][y] = -1;
6918 #if USE_NEW_CUSTOM_VALUE
6919 CustomValue[x][y] = 0;
6922 /* copy animation control values to new field */
6923 GfxFrame[newx][newy] = GfxFrame[x][y];
6924 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
6925 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
6926 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
6928 Pushed[x][y] = Pushed[newx][newy] = FALSE;
6930 /* some elements can leave other elements behind after moving */
6932 if (ei->move_leave_element != EL_EMPTY &&
6933 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
6934 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
6936 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
6937 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
6938 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
6941 int move_leave_element = ei->move_leave_element;
6945 /* this makes it possible to leave the removed element again */
6946 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
6947 move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
6949 /* this makes it possible to leave the removed element again */
6950 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
6951 move_leave_element = stored;
6954 /* this makes it possible to leave the removed element again */
6955 if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
6956 ei->move_leave_element == EL_TRIGGER_ELEMENT)
6957 move_leave_element = stored;
6960 Feld[x][y] = move_leave_element;
6962 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
6963 MovDir[x][y] = direction;
6965 InitField(x, y, FALSE);
6967 if (GFX_CRUMBLED(Feld[x][y]))
6968 DrawLevelFieldCrumbledSandNeighbours(x, y);
6970 if (ELEM_IS_PLAYER(move_leave_element))
6971 RelocatePlayer(x, y, move_leave_element);
6974 /* do this after checking for left-behind element */
6975 ResetGfxAnimation(x, y); /* reset animation values for old field */
6977 if (!CAN_MOVE(element) ||
6978 (CAN_FALL(element) && direction == MV_DOWN &&
6979 (element == EL_SPRING ||
6980 element_info[element].move_pattern == MV_WHEN_PUSHED ||
6981 element_info[element].move_pattern == MV_WHEN_DROPPED)))
6982 GfxDir[x][y] = MovDir[newx][newy] = 0;
6984 DrawLevelField(x, y);
6985 DrawLevelField(newx, newy);
6987 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
6989 /* prevent pushed element from moving on in pushed direction */
6990 if (pushed_by_player && CAN_MOVE(element) &&
6991 element_info[element].move_pattern & MV_ANY_DIRECTION &&
6992 !(element_info[element].move_pattern & direction))
6993 TurnRound(newx, newy);
6995 /* prevent elements on conveyor belt from moving on in last direction */
6996 if (pushed_by_conveyor && CAN_FALL(element) &&
6997 direction & MV_HORIZONTAL)
6998 MovDir[newx][newy] = 0;
7000 if (!pushed_by_player)
7002 int nextx = newx + dx, nexty = newy + dy;
7003 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
7005 WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
7007 if (CAN_FALL(element) && direction == MV_DOWN)
7008 WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
7010 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
7011 CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
7014 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
7016 TestIfBadThingTouchesPlayer(newx, newy);
7017 TestIfBadThingTouchesFriend(newx, newy);
7019 if (!IS_CUSTOM_ELEMENT(element))
7020 TestIfBadThingTouchesOtherBadThing(newx, newy);
7022 else if (element == EL_PENGUIN)
7023 TestIfFriendTouchesBadThing(newx, newy);
7025 /* give the player one last chance (one more frame) to move away */
7026 if (CAN_FALL(element) && direction == MV_DOWN &&
7027 (last_line || (!IS_FREE(x, newy + 1) &&
7028 (!IS_PLAYER(x, newy + 1) ||
7029 game.engine_version < VERSION_IDENT(3,1,1,0)))))
7032 if (pushed_by_player && !game.use_change_when_pushing_bug)
7034 int push_side = MV_DIR_OPPOSITE(direction);
7035 struct PlayerInfo *player = PLAYERINFO(x, y);
7037 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
7038 player->index_bit, push_side);
7039 CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
7040 player->index_bit, push_side);
7043 if (element == EL_EMC_ANDROID && pushed_by_player) /* make another move */
7044 MovDelay[newx][newy] = 1;
7046 CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
7048 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
7051 if (ChangePage[newx][newy] != -1) /* delayed change */
7053 int page = ChangePage[newx][newy];
7054 struct ElementChangeInfo *change = &ei->change_page[page];
7056 ChangePage[newx][newy] = -1;
7058 if (change->can_change)
7060 if (ChangeElement(newx, newy, element, page))
7062 if (change->post_change_function)
7063 change->post_change_function(newx, newy);
7067 if (change->has_action)
7068 ExecuteCustomElementAction(newx, newy, element, page);
7072 TestIfElementHitsCustomElement(newx, newy, direction);
7073 TestIfPlayerTouchesCustomElement(newx, newy);
7074 TestIfElementTouchesCustomElement(newx, newy);
7077 if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
7078 IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
7079 CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
7080 MV_DIR_OPPOSITE(direction));
7084 int AmoebeNachbarNr(int ax, int ay)
7087 int element = Feld[ax][ay];
7089 static int xy[4][2] =
7097 for (i = 0; i < NUM_DIRECTIONS; i++)
7099 int x = ax + xy[i][0];
7100 int y = ay + xy[i][1];
7102 if (!IN_LEV_FIELD(x, y))
7105 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
7106 group_nr = AmoebaNr[x][y];
7112 void AmoebenVereinigen(int ax, int ay)
7114 int i, x, y, xx, yy;
7115 int new_group_nr = AmoebaNr[ax][ay];
7116 static int xy[4][2] =
7124 if (new_group_nr == 0)
7127 for (i = 0; i < NUM_DIRECTIONS; i++)
7132 if (!IN_LEV_FIELD(x, y))
7135 if ((Feld[x][y] == EL_AMOEBA_FULL ||
7136 Feld[x][y] == EL_BD_AMOEBA ||
7137 Feld[x][y] == EL_AMOEBA_DEAD) &&
7138 AmoebaNr[x][y] != new_group_nr)
7140 int old_group_nr = AmoebaNr[x][y];
7142 if (old_group_nr == 0)
7145 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
7146 AmoebaCnt[old_group_nr] = 0;
7147 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
7148 AmoebaCnt2[old_group_nr] = 0;
7151 SCAN_PLAYFIELD(xx, yy)
7153 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
7156 if (AmoebaNr[xx][yy] == old_group_nr)
7157 AmoebaNr[xx][yy] = new_group_nr;
7163 void AmoebeUmwandeln(int ax, int ay)
7167 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
7169 int group_nr = AmoebaNr[ax][ay];
7174 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
7175 printf("AmoebeUmwandeln(): This should never happen!\n");
7181 SCAN_PLAYFIELD(x, y)
7183 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7186 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
7189 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
7193 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
7194 SND_AMOEBA_TURNING_TO_GEM :
7195 SND_AMOEBA_TURNING_TO_ROCK));
7200 static int xy[4][2] =
7208 for (i = 0; i < NUM_DIRECTIONS; i++)
7213 if (!IN_LEV_FIELD(x, y))
7216 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
7218 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
7219 SND_AMOEBA_TURNING_TO_GEM :
7220 SND_AMOEBA_TURNING_TO_ROCK));
7227 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
7230 int group_nr = AmoebaNr[ax][ay];
7231 boolean done = FALSE;
7236 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
7237 printf("AmoebeUmwandelnBD(): This should never happen!\n");
7243 SCAN_PLAYFIELD(x, y)
7245 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7248 if (AmoebaNr[x][y] == group_nr &&
7249 (Feld[x][y] == EL_AMOEBA_DEAD ||
7250 Feld[x][y] == EL_BD_AMOEBA ||
7251 Feld[x][y] == EL_AMOEBA_GROWING))
7254 Feld[x][y] = new_element;
7255 InitField(x, y, FALSE);
7256 DrawLevelField(x, y);
7262 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
7263 SND_BD_AMOEBA_TURNING_TO_ROCK :
7264 SND_BD_AMOEBA_TURNING_TO_GEM));
7267 void AmoebeWaechst(int x, int y)
7269 static unsigned long sound_delay = 0;
7270 static unsigned long sound_delay_value = 0;
7272 if (!MovDelay[x][y]) /* start new growing cycle */
7276 if (DelayReached(&sound_delay, sound_delay_value))
7278 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
7279 sound_delay_value = 30;
7283 if (MovDelay[x][y]) /* wait some time before growing bigger */
7286 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7288 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
7289 6 - MovDelay[x][y]);
7291 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
7294 if (!MovDelay[x][y])
7296 Feld[x][y] = Store[x][y];
7298 DrawLevelField(x, y);
7303 void AmoebaDisappearing(int x, int y)
7305 static unsigned long sound_delay = 0;
7306 static unsigned long sound_delay_value = 0;
7308 if (!MovDelay[x][y]) /* start new shrinking cycle */
7312 if (DelayReached(&sound_delay, sound_delay_value))
7313 sound_delay_value = 30;
7316 if (MovDelay[x][y]) /* wait some time before shrinking */
7319 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7321 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
7322 6 - MovDelay[x][y]);
7324 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
7327 if (!MovDelay[x][y])
7329 Feld[x][y] = EL_EMPTY;
7330 DrawLevelField(x, y);
7332 /* don't let mole enter this field in this cycle;
7333 (give priority to objects falling to this field from above) */
7339 void AmoebeAbleger(int ax, int ay)
7342 int element = Feld[ax][ay];
7343 int graphic = el2img(element);
7344 int newax = ax, neway = ay;
7345 boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
7346 static int xy[4][2] =
7354 if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
7356 Feld[ax][ay] = EL_AMOEBA_DEAD;
7357 DrawLevelField(ax, ay);
7361 if (IS_ANIMATED(graphic))
7362 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7364 if (!MovDelay[ax][ay]) /* start making new amoeba field */
7365 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
7367 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
7370 if (MovDelay[ax][ay])
7374 if (can_drop) /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
7377 int x = ax + xy[start][0];
7378 int y = ay + xy[start][1];
7380 if (!IN_LEV_FIELD(x, y))
7383 if (IS_FREE(x, y) ||
7384 CAN_GROW_INTO(Feld[x][y]) ||
7385 Feld[x][y] == EL_QUICKSAND_EMPTY)
7391 if (newax == ax && neway == ay)
7394 else /* normal or "filled" (BD style) amoeba */
7397 boolean waiting_for_player = FALSE;
7399 for (i = 0; i < NUM_DIRECTIONS; i++)
7401 int j = (start + i) % 4;
7402 int x = ax + xy[j][0];
7403 int y = ay + xy[j][1];
7405 if (!IN_LEV_FIELD(x, y))
7408 if (IS_FREE(x, y) ||
7409 CAN_GROW_INTO(Feld[x][y]) ||
7410 Feld[x][y] == EL_QUICKSAND_EMPTY)
7416 else if (IS_PLAYER(x, y))
7417 waiting_for_player = TRUE;
7420 if (newax == ax && neway == ay) /* amoeba cannot grow */
7422 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
7424 Feld[ax][ay] = EL_AMOEBA_DEAD;
7425 DrawLevelField(ax, ay);
7426 AmoebaCnt[AmoebaNr[ax][ay]]--;
7428 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
7430 if (element == EL_AMOEBA_FULL)
7431 AmoebeUmwandeln(ax, ay);
7432 else if (element == EL_BD_AMOEBA)
7433 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
7438 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
7440 /* amoeba gets larger by growing in some direction */
7442 int new_group_nr = AmoebaNr[ax][ay];
7445 if (new_group_nr == 0)
7447 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
7448 printf("AmoebeAbleger(): This should never happen!\n");
7453 AmoebaNr[newax][neway] = new_group_nr;
7454 AmoebaCnt[new_group_nr]++;
7455 AmoebaCnt2[new_group_nr]++;
7457 /* if amoeba touches other amoeba(s) after growing, unify them */
7458 AmoebenVereinigen(newax, neway);
7460 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
7462 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
7468 if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
7469 (neway == lev_fieldy - 1 && newax != ax))
7471 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
7472 Store[newax][neway] = element;
7474 else if (neway == ay || element == EL_EMC_DRIPPER)
7476 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
7478 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
7482 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
7483 Feld[ax][ay] = EL_AMOEBA_DROPPING;
7484 Store[ax][ay] = EL_AMOEBA_DROP;
7485 ContinueMoving(ax, ay);
7489 DrawLevelField(newax, neway);
7492 void Life(int ax, int ay)
7496 static int life[4] = { 2, 3, 3, 3 }; /* parameters for "game of life" */
7499 int element = Feld[ax][ay];
7500 int graphic = el2img(element);
7501 int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
7503 boolean changed = FALSE;
7505 if (IS_ANIMATED(graphic))
7506 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7511 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
7512 MovDelay[ax][ay] = life_time;
7514 if (MovDelay[ax][ay]) /* wait some time before next cycle */
7517 if (MovDelay[ax][ay])
7521 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
7523 int xx = ax+x1, yy = ay+y1;
7526 if (!IN_LEV_FIELD(xx, yy))
7529 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
7531 int x = xx+x2, y = yy+y2;
7533 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
7536 if (((Feld[x][y] == element ||
7537 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
7539 (IS_FREE(x, y) && Stop[x][y]))
7543 if (xx == ax && yy == ay) /* field in the middle */
7545 if (nachbarn < life_parameter[0] ||
7546 nachbarn > life_parameter[1])
7548 Feld[xx][yy] = EL_EMPTY;
7550 DrawLevelField(xx, yy);
7551 Stop[xx][yy] = TRUE;
7555 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
7556 { /* free border field */
7557 if (nachbarn >= life_parameter[2] &&
7558 nachbarn <= life_parameter[3])
7560 Feld[xx][yy] = element;
7561 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
7563 DrawLevelField(xx, yy);
7564 Stop[xx][yy] = TRUE;
7571 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
7572 SND_GAME_OF_LIFE_GROWING);
7575 static void InitRobotWheel(int x, int y)
7577 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
7580 static void RunRobotWheel(int x, int y)
7582 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
7585 static void StopRobotWheel(int x, int y)
7587 if (ZX == x && ZY == y)
7591 static void InitTimegateWheel(int x, int y)
7593 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
7596 static void RunTimegateWheel(int x, int y)
7598 PlayLevelSound(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
7601 static void InitMagicBallDelay(int x, int y)
7604 ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
7606 ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
7610 static void ActivateMagicBall(int bx, int by)
7614 if (level.ball_random)
7616 int pos_border = RND(8); /* select one of the eight border elements */
7617 int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
7618 int xx = pos_content % 3;
7619 int yy = pos_content / 3;
7624 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
7625 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
7629 for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
7631 int xx = x - bx + 1;
7632 int yy = y - by + 1;
7634 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
7635 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
7639 game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
7642 static void InitDiagonalMovingElement(int x, int y)
7645 MovDelay[x][y] = level.android_move_time;
7649 void CheckExit(int x, int y)
7651 if (local_player->gems_still_needed > 0 ||
7652 local_player->sokobanfields_still_needed > 0 ||
7653 local_player->lights_still_needed > 0)
7655 int element = Feld[x][y];
7656 int graphic = el2img(element);
7658 if (IS_ANIMATED(graphic))
7659 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7664 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7667 Feld[x][y] = EL_EXIT_OPENING;
7669 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
7672 void CheckExitSP(int x, int y)
7674 if (local_player->gems_still_needed > 0)
7676 int element = Feld[x][y];
7677 int graphic = el2img(element);
7679 if (IS_ANIMATED(graphic))
7680 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7685 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7688 Feld[x][y] = EL_SP_EXIT_OPENING;
7690 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
7693 static void CloseAllOpenTimegates()
7698 SCAN_PLAYFIELD(x, y)
7700 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7703 int element = Feld[x][y];
7705 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
7707 Feld[x][y] = EL_TIMEGATE_CLOSING;
7709 PlayLevelSoundAction(x, y, ACTION_CLOSING);
7714 void EdelsteinFunkeln(int x, int y)
7716 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
7719 if (Feld[x][y] == EL_BD_DIAMOND)
7722 if (MovDelay[x][y] == 0) /* next animation frame */
7723 MovDelay[x][y] = 11 * !SimpleRND(500);
7725 if (MovDelay[x][y] != 0) /* wait some time before next frame */
7729 if (setup.direct_draw && MovDelay[x][y])
7730 SetDrawtoField(DRAW_BUFFERED);
7732 DrawLevelElementAnimation(x, y, Feld[x][y]);
7734 if (MovDelay[x][y] != 0)
7736 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
7737 10 - MovDelay[x][y]);
7739 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
7741 if (setup.direct_draw)
7745 dest_x = FX + SCREENX(x) * TILEX;
7746 dest_y = FY + SCREENY(y) * TILEY;
7748 BlitBitmap(drawto_field, window,
7749 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
7750 SetDrawtoField(DRAW_DIRECT);
7756 void MauerWaechst(int x, int y)
7760 if (!MovDelay[x][y]) /* next animation frame */
7761 MovDelay[x][y] = 3 * delay;
7763 if (MovDelay[x][y]) /* wait some time before next frame */
7767 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7769 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
7770 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
7772 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
7775 if (!MovDelay[x][y])
7777 if (MovDir[x][y] == MV_LEFT)
7779 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
7780 DrawLevelField(x - 1, y);
7782 else if (MovDir[x][y] == MV_RIGHT)
7784 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
7785 DrawLevelField(x + 1, y);
7787 else if (MovDir[x][y] == MV_UP)
7789 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
7790 DrawLevelField(x, y - 1);
7794 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
7795 DrawLevelField(x, y + 1);
7798 Feld[x][y] = Store[x][y];
7800 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7801 DrawLevelField(x, y);
7806 void MauerAbleger(int ax, int ay)
7808 int element = Feld[ax][ay];
7809 int graphic = el2img(element);
7810 boolean oben_frei = FALSE, unten_frei = FALSE;
7811 boolean links_frei = FALSE, rechts_frei = FALSE;
7812 boolean oben_massiv = FALSE, unten_massiv = FALSE;
7813 boolean links_massiv = FALSE, rechts_massiv = FALSE;
7814 boolean new_wall = FALSE;
7816 if (IS_ANIMATED(graphic))
7817 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7819 if (!MovDelay[ax][ay]) /* start building new wall */
7820 MovDelay[ax][ay] = 6;
7822 if (MovDelay[ax][ay]) /* wait some time before building new wall */
7825 if (MovDelay[ax][ay])
7829 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
7831 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
7833 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
7835 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
7838 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
7839 element == EL_EXPANDABLE_WALL_ANY)
7843 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
7844 Store[ax][ay-1] = element;
7845 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
7846 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
7847 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
7848 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
7853 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
7854 Store[ax][ay+1] = element;
7855 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
7856 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
7857 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
7858 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
7863 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7864 element == EL_EXPANDABLE_WALL_ANY ||
7865 element == EL_EXPANDABLE_WALL ||
7866 element == EL_BD_EXPANDABLE_WALL)
7870 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
7871 Store[ax-1][ay] = element;
7872 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
7873 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
7874 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
7875 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
7881 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
7882 Store[ax+1][ay] = element;
7883 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
7884 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
7885 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
7886 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
7891 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
7892 DrawLevelField(ax, ay);
7894 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
7896 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
7897 unten_massiv = TRUE;
7898 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
7899 links_massiv = TRUE;
7900 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
7901 rechts_massiv = TRUE;
7903 if (((oben_massiv && unten_massiv) ||
7904 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7905 element == EL_EXPANDABLE_WALL) &&
7906 ((links_massiv && rechts_massiv) ||
7907 element == EL_EXPANDABLE_WALL_VERTICAL))
7908 Feld[ax][ay] = EL_WALL;
7911 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
7914 void CheckForDragon(int x, int y)
7917 boolean dragon_found = FALSE;
7918 static int xy[4][2] =
7926 for (i = 0; i < NUM_DIRECTIONS; i++)
7928 for (j = 0; j < 4; j++)
7930 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7932 if (IN_LEV_FIELD(xx, yy) &&
7933 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
7935 if (Feld[xx][yy] == EL_DRAGON)
7936 dragon_found = TRUE;
7945 for (i = 0; i < NUM_DIRECTIONS; i++)
7947 for (j = 0; j < 3; j++)
7949 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7951 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
7953 Feld[xx][yy] = EL_EMPTY;
7954 DrawLevelField(xx, yy);
7963 static void InitBuggyBase(int x, int y)
7965 int element = Feld[x][y];
7966 int activating_delay = FRAMES_PER_SECOND / 4;
7969 (element == EL_SP_BUGGY_BASE ?
7970 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
7971 element == EL_SP_BUGGY_BASE_ACTIVATING ?
7973 element == EL_SP_BUGGY_BASE_ACTIVE ?
7974 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
7977 static void WarnBuggyBase(int x, int y)
7980 static int xy[4][2] =
7988 for (i = 0; i < NUM_DIRECTIONS; i++)
7990 int xx = x + xy[i][0];
7991 int yy = y + xy[i][1];
7993 if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
7995 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
8002 static void InitTrap(int x, int y)
8004 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
8007 static void ActivateTrap(int x, int y)
8009 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
8012 static void ChangeActiveTrap(int x, int y)
8014 int graphic = IMG_TRAP_ACTIVE;
8016 /* if new animation frame was drawn, correct crumbled sand border */
8017 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
8018 DrawLevelFieldCrumbledSand(x, y);
8021 static int getSpecialActionElement(int element, int number, int base_element)
8023 return (element != EL_EMPTY ? element :
8024 number != -1 ? base_element + number - 1 :
8028 static int getModifiedActionNumber(int value_old, int operator, int operand,
8029 int value_min, int value_max)
8031 int value_new = (operator == CA_MODE_SET ? operand :
8032 operator == CA_MODE_ADD ? value_old + operand :
8033 operator == CA_MODE_SUBTRACT ? value_old - operand :
8034 operator == CA_MODE_MULTIPLY ? value_old * operand :
8035 operator == CA_MODE_DIVIDE ? value_old / MAX(1, operand) :
8036 operator == CA_MODE_MODULO ? value_old % MAX(1, operand) :
8039 return (value_new < value_min ? value_min :
8040 value_new > value_max ? value_max :
8044 static void ExecuteCustomElementAction(int x, int y, int element, int page)
8046 struct ElementInfo *ei = &element_info[element];
8047 struct ElementChangeInfo *change = &ei->change_page[page];
8048 int target_element = change->target_element;
8049 int action_type = change->action_type;
8050 int action_mode = change->action_mode;
8051 int action_arg = change->action_arg;
8054 if (!change->has_action)
8057 /* ---------- determine action paramater values -------------------------- */
8059 int level_time_value =
8060 (level.time > 0 ? TimeLeft :
8063 int action_arg_element =
8064 (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player :
8065 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
8066 action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element :
8069 int action_arg_direction =
8070 (action_arg >= CA_ARG_DIRECTION_LEFT &&
8071 action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
8072 action_arg == CA_ARG_DIRECTION_TRIGGER ?
8073 change->actual_trigger_side :
8074 action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
8075 MV_DIR_OPPOSITE(change->actual_trigger_side) :
8078 int action_arg_number_min =
8079 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
8082 int action_arg_number_max =
8083 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
8084 action_type == CA_SET_LEVEL_GEMS ? 999 :
8085 action_type == CA_SET_LEVEL_TIME ? 9999 :
8086 action_type == CA_SET_LEVEL_SCORE ? 99999 :
8087 action_type == CA_SET_CE_VALUE ? 9999 :
8088 action_type == CA_SET_CE_SCORE ? 9999 :
8091 int action_arg_number_reset =
8092 (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
8093 action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
8094 action_type == CA_SET_LEVEL_TIME ? level.time :
8095 action_type == CA_SET_LEVEL_SCORE ? 0 :
8097 action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
8099 action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
8101 action_type == CA_SET_CE_SCORE ? 0 :
8104 int action_arg_number =
8105 (action_arg <= CA_ARG_MAX ? action_arg :
8106 action_arg >= CA_ARG_SPEED_NOT_MOVING &&
8107 action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
8108 action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
8109 action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
8110 action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
8111 action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
8112 #if USE_NEW_CUSTOM_VALUE
8113 action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
8115 action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
8117 action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
8118 action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
8119 action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
8120 action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
8121 action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
8122 action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
8123 action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
8124 action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
8125 action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
8126 action_arg == CA_ARG_ELEMENT_NR_TARGET ? change->target_element :
8127 action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
8130 int action_arg_number_old =
8131 (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
8132 action_type == CA_SET_LEVEL_TIME ? TimeLeft :
8133 action_type == CA_SET_LEVEL_SCORE ? local_player->score :
8134 action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
8135 action_type == CA_SET_CE_SCORE ? ei->collect_score :
8138 int action_arg_number_new =
8139 getModifiedActionNumber(action_arg_number_old,
8140 action_mode, action_arg_number,
8141 action_arg_number_min, action_arg_number_max);
8143 int trigger_player_bits =
8144 (change->actual_trigger_player >= EL_PLAYER_1 &&
8145 change->actual_trigger_player <= EL_PLAYER_4 ?
8146 (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
8149 int action_arg_player_bits =
8150 (action_arg >= CA_ARG_PLAYER_1 &&
8151 action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
8152 action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
8155 /* ---------- execute action -------------------------------------------- */
8164 /* ---------- level actions ------------------------------------------- */
8166 case CA_RESTART_LEVEL:
8168 game.restart_level = TRUE;
8173 case CA_SHOW_ENVELOPE:
8175 int element = getSpecialActionElement(action_arg_element,
8176 action_arg_number, EL_ENVELOPE_1);
8178 if (IS_ENVELOPE(element))
8179 local_player->show_envelope = element;
8184 case CA_SET_LEVEL_TIME:
8186 if (level.time > 0) /* only modify limited time value */
8188 TimeLeft = action_arg_number_new;
8190 DrawGameValue_Time(TimeLeft);
8192 if (!TimeLeft && setup.time_limit)
8193 for (i = 0; i < MAX_PLAYERS; i++)
8194 KillPlayer(&stored_player[i]);
8200 case CA_SET_LEVEL_SCORE:
8202 local_player->score = action_arg_number_new;
8204 DrawGameValue_Score(local_player->score);
8209 case CA_SET_LEVEL_GEMS:
8211 local_player->gems_still_needed = action_arg_number_new;
8213 DrawGameValue_Emeralds(local_player->gems_still_needed);
8218 #if !USE_PLAYER_GRAVITY
8219 case CA_SET_LEVEL_GRAVITY:
8221 game.gravity = (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
8222 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
8223 action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
8229 case CA_SET_LEVEL_WIND:
8231 game.wind_direction = action_arg_direction;
8236 /* ---------- player actions ------------------------------------------ */
8238 case CA_MOVE_PLAYER:
8240 /* automatically move to the next field in specified direction */
8241 for (i = 0; i < MAX_PLAYERS; i++)
8242 if (trigger_player_bits & (1 << i))
8243 stored_player[i].programmed_action = action_arg_direction;
8248 case CA_EXIT_PLAYER:
8250 for (i = 0; i < MAX_PLAYERS; i++)
8251 if (action_arg_player_bits & (1 << i))
8252 stored_player[i].LevelSolved = stored_player[i].GameOver = TRUE;
8257 case CA_KILL_PLAYER:
8259 for (i = 0; i < MAX_PLAYERS; i++)
8260 if (action_arg_player_bits & (1 << i))
8261 KillPlayer(&stored_player[i]);
8266 case CA_SET_PLAYER_KEYS:
8268 int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
8269 int element = getSpecialActionElement(action_arg_element,
8270 action_arg_number, EL_KEY_1);
8272 if (IS_KEY(element))
8274 for (i = 0; i < MAX_PLAYERS; i++)
8276 if (trigger_player_bits & (1 << i))
8278 stored_player[i].key[KEY_NR(element)] = key_state;
8281 DrawGameDoorValues();
8283 DrawGameValue_Keys(stored_player[i].key);
8286 redraw_mask |= REDRAW_DOOR_1;
8294 case CA_SET_PLAYER_SPEED:
8296 for (i = 0; i < MAX_PLAYERS; i++)
8298 if (trigger_player_bits & (1 << i))
8300 int move_stepsize = TILEX / stored_player[i].move_delay_value;
8302 if (action_arg == CA_ARG_SPEED_FASTER &&
8303 stored_player[i].cannot_move)
8305 action_arg_number = STEPSIZE_VERY_SLOW;
8307 else if (action_arg == CA_ARG_SPEED_SLOWER ||
8308 action_arg == CA_ARG_SPEED_FASTER)
8310 action_arg_number = 2;
8311 action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
8314 else if (action_arg == CA_ARG_NUMBER_RESET)
8316 action_arg_number = level.initial_player_stepsize[i];
8320 getModifiedActionNumber(move_stepsize,
8323 action_arg_number_min,
8324 action_arg_number_max);
8327 SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
8329 /* make sure that value is power of 2 */
8330 move_stepsize = (1 << log_2(move_stepsize));
8332 /* do no immediately change -- the player might just be moving */
8333 stored_player[i].move_delay_value_next = TILEX / move_stepsize;
8335 stored_player[i].cannot_move =
8336 (action_arg == CA_ARG_SPEED_NOT_MOVING ? TRUE : FALSE);
8344 case CA_SET_PLAYER_SHIELD:
8346 for (i = 0; i < MAX_PLAYERS; i++)
8348 if (trigger_player_bits & (1 << i))
8350 if (action_arg == CA_ARG_SHIELD_OFF)
8352 stored_player[i].shield_normal_time_left = 0;
8353 stored_player[i].shield_deadly_time_left = 0;
8355 else if (action_arg == CA_ARG_SHIELD_NORMAL)
8357 stored_player[i].shield_normal_time_left = 999999;
8359 else if (action_arg == CA_ARG_SHIELD_DEADLY)
8361 stored_player[i].shield_normal_time_left = 999999;
8362 stored_player[i].shield_deadly_time_left = 999999;
8370 #if USE_PLAYER_GRAVITY
8371 case CA_SET_PLAYER_GRAVITY:
8373 for (i = 0; i < MAX_PLAYERS; i++)
8375 if (trigger_player_bits & (1 << i))
8377 stored_player[i].gravity =
8378 (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
8379 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
8380 action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
8381 stored_player[i].gravity);
8389 case CA_SET_PLAYER_ARTWORK:
8391 for (i = 0; i < MAX_PLAYERS; i++)
8393 if (trigger_player_bits & (1 << i))
8395 int artwork_element = action_arg_element;
8397 if (action_arg == CA_ARG_ELEMENT_RESET)
8399 (level.use_artwork_element[i] ? level.artwork_element[i] :
8400 stored_player[i].element_nr);
8402 stored_player[i].artwork_element = artwork_element;
8404 SetPlayerWaiting(&stored_player[i], FALSE);
8406 /* set number of special actions for bored and sleeping animation */
8407 stored_player[i].num_special_action_bored =
8408 get_num_special_action(artwork_element,
8409 ACTION_BORING_1, ACTION_BORING_LAST);
8410 stored_player[i].num_special_action_sleeping =
8411 get_num_special_action(artwork_element,
8412 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
8419 /* ---------- CE actions ---------------------------------------------- */
8421 case CA_SET_CE_VALUE:
8423 #if USE_NEW_CUSTOM_VALUE
8424 int last_ce_value = CustomValue[x][y];
8426 CustomValue[x][y] = action_arg_number_new;
8429 printf("::: CE value == %d\n", CustomValue[x][y]);
8432 if (CustomValue[x][y] != last_ce_value)
8434 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
8435 CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
8437 if (CustomValue[x][y] == 0)
8440 printf("::: CE_VALUE_GETS_ZERO\n");
8443 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
8444 CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
8447 printf("::: RESULT: %d, %d\n", Feld[x][y], ChangePage[x][y]);
8457 case CA_SET_CE_SCORE:
8459 #if USE_NEW_CUSTOM_VALUE
8460 int last_ce_score = ei->collect_score;
8462 ei->collect_score = action_arg_number_new;
8465 printf("::: CE score == %d\n", ei->collect_score);
8468 if (ei->collect_score != last_ce_score)
8470 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
8471 CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
8473 if (ei->collect_score == 0)
8478 printf("::: CE_SCORE_GETS_ZERO\n");
8481 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
8482 CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
8485 printf("::: RESULT: %d, %d\n", Feld[x][y], ChangePage[x][y]);
8490 This is a very special case that seems to be a mixture between
8491 CheckElementChange() and CheckTriggeredElementChange(): while
8492 the first one only affects single elements that are triggered
8493 directly, the second one affects multiple elements in the playfield
8494 that are triggered indirectly by another element. This is a third
8495 case: Changing the CE score always affects multiple identical CEs,
8496 so every affected CE must be checked, not only the single CE for
8497 which the CE score was changed in the first place (as every instance
8498 of that CE shares the same CE score, and therefore also can change)!
8500 SCAN_PLAYFIELD(xx, yy)
8502 if (Feld[xx][yy] == element)
8503 CheckElementChange(xx, yy, element, EL_UNDEFINED,
8504 CE_SCORE_GETS_ZERO);
8515 /* ---------- engine actions ------------------------------------------ */
8517 case CA_SET_ENGINE_SCAN_MODE:
8519 InitPlayfieldScanMode(action_arg);
8529 static void CreateFieldExt(int x, int y, int element, boolean is_change)
8531 int old_element = Feld[x][y];
8532 int new_element = get_element_from_group_element(element);
8533 int previous_move_direction = MovDir[x][y];
8534 #if USE_NEW_CUSTOM_VALUE
8535 int last_ce_value = CustomValue[x][y];
8537 boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
8538 boolean add_player_onto_element = (new_element_is_player &&
8539 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
8540 /* this breaks SnakeBite when a snake is
8541 halfway through a door that closes */
8542 /* NOW FIXED AT LEVEL INIT IN files.c */
8543 new_element != EL_SOKOBAN_FIELD_PLAYER &&
8545 IS_WALKABLE(old_element));
8548 /* check if element under the player changes from accessible to unaccessible
8549 (needed for special case of dropping element which then changes) */
8550 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
8551 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
8559 if (!add_player_onto_element)
8561 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
8562 RemoveMovingField(x, y);
8566 Feld[x][y] = new_element;
8568 #if !USE_GFX_RESET_GFX_ANIMATION
8569 ResetGfxAnimation(x, y);
8570 ResetRandomAnimationValue(x, y);
8573 if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
8574 MovDir[x][y] = previous_move_direction;
8576 #if USE_NEW_CUSTOM_VALUE
8577 if (element_info[new_element].use_last_ce_value)
8578 CustomValue[x][y] = last_ce_value;
8581 InitField_WithBug1(x, y, FALSE);
8583 new_element = Feld[x][y]; /* element may have changed */
8585 #if USE_GFX_RESET_GFX_ANIMATION
8586 ResetGfxAnimation(x, y);
8587 ResetRandomAnimationValue(x, y);
8590 DrawLevelField(x, y);
8592 if (GFX_CRUMBLED(new_element))
8593 DrawLevelFieldCrumbledSandNeighbours(x, y);
8597 /* check if element under the player changes from accessible to unaccessible
8598 (needed for special case of dropping element which then changes) */
8599 /* (must be checked after creating new element for walkable group elements) */
8600 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
8601 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
8609 /* "ChangeCount" not set yet to allow "entered by player" change one time */
8610 if (new_element_is_player)
8611 RelocatePlayer(x, y, new_element);
8614 ChangeCount[x][y]++; /* count number of changes in the same frame */
8616 TestIfBadThingTouchesPlayer(x, y);
8617 TestIfPlayerTouchesCustomElement(x, y);
8618 TestIfElementTouchesCustomElement(x, y);
8621 static void CreateField(int x, int y, int element)
8623 CreateFieldExt(x, y, element, FALSE);
8626 static void CreateElementFromChange(int x, int y, int element)
8628 element = GET_VALID_RUNTIME_ELEMENT(element);
8630 #if USE_STOP_CHANGED_ELEMENTS
8631 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
8633 int old_element = Feld[x][y];
8635 /* prevent changed element from moving in same engine frame
8636 unless both old and new element can either fall or move */
8637 if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
8638 (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
8643 CreateFieldExt(x, y, element, TRUE);
8646 static boolean ChangeElement(int x, int y, int element, int page)
8648 struct ElementInfo *ei = &element_info[element];
8649 struct ElementChangeInfo *change = &ei->change_page[page];
8650 int ce_value = CustomValue[x][y];
8651 int ce_score = ei->collect_score;
8653 int old_element = Feld[x][y];
8655 /* always use default change event to prevent running into a loop */
8656 if (ChangeEvent[x][y] == -1)
8657 ChangeEvent[x][y] = CE_DELAY;
8659 if (ChangeEvent[x][y] == CE_DELAY)
8661 /* reset actual trigger element, trigger player and action element */
8662 change->actual_trigger_element = EL_EMPTY;
8663 change->actual_trigger_player = EL_PLAYER_1;
8664 change->actual_trigger_side = CH_SIDE_NONE;
8665 change->actual_trigger_ce_value = 0;
8666 change->actual_trigger_ce_score = 0;
8669 /* do not change elements more than a specified maximum number of changes */
8670 if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
8673 ChangeCount[x][y]++; /* count number of changes in the same frame */
8675 if (change->explode)
8682 if (change->use_target_content)
8684 boolean complete_replace = TRUE;
8685 boolean can_replace[3][3];
8688 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
8691 boolean is_walkable;
8692 boolean is_diggable;
8693 boolean is_collectible;
8694 boolean is_removable;
8695 boolean is_destructible;
8696 int ex = x + xx - 1;
8697 int ey = y + yy - 1;
8698 int content_element = change->target_content.e[xx][yy];
8701 can_replace[xx][yy] = TRUE;
8703 if (ex == x && ey == y) /* do not check changing element itself */
8706 if (content_element == EL_EMPTY_SPACE)
8708 can_replace[xx][yy] = FALSE; /* do not replace border with space */
8713 if (!IN_LEV_FIELD(ex, ey))
8715 can_replace[xx][yy] = FALSE;
8716 complete_replace = FALSE;
8723 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
8724 e = MovingOrBlocked2Element(ex, ey);
8726 is_empty = (IS_FREE(ex, ey) ||
8727 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
8729 is_walkable = (is_empty || IS_WALKABLE(e));
8730 is_diggable = (is_empty || IS_DIGGABLE(e));
8731 is_collectible = (is_empty || IS_COLLECTIBLE(e));
8732 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
8733 is_removable = (is_diggable || is_collectible);
8735 can_replace[xx][yy] =
8736 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
8737 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
8738 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
8739 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
8740 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
8741 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
8742 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
8744 if (!can_replace[xx][yy])
8745 complete_replace = FALSE;
8748 if (!change->only_if_complete || complete_replace)
8750 boolean something_has_changed = FALSE;
8752 if (change->only_if_complete && change->use_random_replace &&
8753 RND(100) < change->random_percentage)
8756 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
8758 int ex = x + xx - 1;
8759 int ey = y + yy - 1;
8760 int content_element;
8762 if (can_replace[xx][yy] && (!change->use_random_replace ||
8763 RND(100) < change->random_percentage))
8765 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
8766 RemoveMovingField(ex, ey);
8768 ChangeEvent[ex][ey] = ChangeEvent[x][y];
8770 content_element = change->target_content.e[xx][yy];
8771 target_element = GET_TARGET_ELEMENT(element, content_element, change,
8772 ce_value, ce_score);
8774 CreateElementFromChange(ex, ey, target_element);
8776 something_has_changed = TRUE;
8778 /* for symmetry reasons, freeze newly created border elements */
8779 if (ex != x || ey != y)
8780 Stop[ex][ey] = TRUE; /* no more moving in this frame */
8784 if (something_has_changed)
8786 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
8787 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
8793 target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
8794 ce_value, ce_score);
8796 if (element == EL_DIAGONAL_GROWING ||
8797 element == EL_DIAGONAL_SHRINKING)
8799 target_element = Store[x][y];
8801 Store[x][y] = EL_EMPTY;
8804 CreateElementFromChange(x, y, target_element);
8806 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
8807 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
8810 /* this uses direct change before indirect change */
8811 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
8816 #if USE_NEW_DELAYED_ACTION
8818 static void HandleElementChange(int x, int y, int page)
8820 int element = MovingOrBlocked2Element(x, y);
8821 struct ElementInfo *ei = &element_info[element];
8822 struct ElementChangeInfo *change = &ei->change_page[page];
8825 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
8826 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
8829 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
8830 x, y, element, element_info[element].token_name);
8831 printf("HandleElementChange(): This should never happen!\n");
8836 /* this can happen with classic bombs on walkable, changing elements */
8837 if (!CAN_CHANGE_OR_HAS_ACTION(element))
8840 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
8841 ChangeDelay[x][y] = 0;
8847 if (ChangeDelay[x][y] == 0) /* initialize element change */
8849 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
8851 if (change->can_change)
8853 ResetGfxAnimation(x, y);
8854 ResetRandomAnimationValue(x, y);
8856 if (change->pre_change_function)
8857 change->pre_change_function(x, y);
8861 ChangeDelay[x][y]--;
8863 if (ChangeDelay[x][y] != 0) /* continue element change */
8865 if (change->can_change)
8867 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8869 if (IS_ANIMATED(graphic))
8870 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8872 if (change->change_function)
8873 change->change_function(x, y);
8876 else /* finish element change */
8878 if (ChangePage[x][y] != -1) /* remember page from delayed change */
8880 page = ChangePage[x][y];
8881 ChangePage[x][y] = -1;
8883 change = &ei->change_page[page];
8886 if (IS_MOVING(x, y)) /* never change a running system ;-) */
8888 ChangeDelay[x][y] = 1; /* try change after next move step */
8889 ChangePage[x][y] = page; /* remember page to use for change */
8894 if (change->can_change)
8896 if (ChangeElement(x, y, element, page))
8898 if (change->post_change_function)
8899 change->post_change_function(x, y);
8903 if (change->has_action)
8904 ExecuteCustomElementAction(x, y, element, page);
8910 static void HandleElementChange(int x, int y, int page)
8912 int element = MovingOrBlocked2Element(x, y);
8913 struct ElementInfo *ei = &element_info[element];
8914 struct ElementChangeInfo *change = &ei->change_page[page];
8917 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
8920 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
8921 x, y, element, element_info[element].token_name);
8922 printf("HandleElementChange(): This should never happen!\n");
8927 /* this can happen with classic bombs on walkable, changing elements */
8928 if (!CAN_CHANGE(element))
8931 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
8932 ChangeDelay[x][y] = 0;
8938 if (ChangeDelay[x][y] == 0) /* initialize element change */
8940 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
8942 ResetGfxAnimation(x, y);
8943 ResetRandomAnimationValue(x, y);
8945 if (change->pre_change_function)
8946 change->pre_change_function(x, y);
8949 ChangeDelay[x][y]--;
8951 if (ChangeDelay[x][y] != 0) /* continue element change */
8953 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8955 if (IS_ANIMATED(graphic))
8956 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8958 if (change->change_function)
8959 change->change_function(x, y);
8961 else /* finish element change */
8963 if (ChangePage[x][y] != -1) /* remember page from delayed change */
8965 page = ChangePage[x][y];
8966 ChangePage[x][y] = -1;
8968 change = &ei->change_page[page];
8971 if (IS_MOVING(x, y)) /* never change a running system ;-) */
8973 ChangeDelay[x][y] = 1; /* try change after next move step */
8974 ChangePage[x][y] = page; /* remember page to use for change */
8979 if (ChangeElement(x, y, element, page))
8981 if (change->post_change_function)
8982 change->post_change_function(x, y);
8989 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
8990 int trigger_element,
8996 boolean change_done_any = FALSE;
8997 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
9000 if (!(trigger_events[trigger_element][trigger_event]))
9003 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
9005 int element = EL_CUSTOM_START + i;
9006 boolean change_done = FALSE;
9009 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
9010 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
9013 for (p = 0; p < element_info[element].num_change_pages; p++)
9015 struct ElementChangeInfo *change = &element_info[element].change_page[p];
9017 if (change->can_change_or_has_action &&
9018 change->has_event[trigger_event] &&
9019 change->trigger_side & trigger_side &&
9020 change->trigger_player & trigger_player &&
9021 change->trigger_page & trigger_page_bits &&
9022 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
9024 change->actual_trigger_element = trigger_element;
9025 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
9026 change->actual_trigger_side = trigger_side;
9027 change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
9028 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
9030 if ((change->can_change && !change_done) || change->has_action)
9035 SCAN_PLAYFIELD(x, y)
9037 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9040 if (Feld[x][y] == element)
9042 if (change->can_change && !change_done)
9044 ChangeDelay[x][y] = 1;
9045 ChangeEvent[x][y] = trigger_event;
9047 HandleElementChange(x, y, p);
9049 #if USE_NEW_DELAYED_ACTION
9050 else if (change->has_action)
9052 ExecuteCustomElementAction(x, y, element, p);
9053 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
9056 if (change->has_action)
9058 ExecuteCustomElementAction(x, y, element, p);
9059 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
9065 if (change->can_change)
9068 change_done_any = TRUE;
9075 return change_done_any;
9078 static boolean CheckElementChangeExt(int x, int y,
9080 int trigger_element,
9085 boolean change_done = FALSE;
9088 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
9089 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
9092 if (Feld[x][y] == EL_BLOCKED)
9094 Blocked2Moving(x, y, &x, &y);
9095 element = Feld[x][y];
9099 /* check if element has already changed */
9100 if (Feld[x][y] != element)
9103 /* check if element has already changed or is about to change after moving */
9104 if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
9105 Feld[x][y] != element) ||
9107 (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
9108 (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
9109 ChangePage[x][y] != -1)))
9113 for (p = 0; p < element_info[element].num_change_pages; p++)
9115 struct ElementChangeInfo *change = &element_info[element].change_page[p];
9117 boolean check_trigger_element =
9118 (trigger_event == CE_TOUCHING_X ||
9119 trigger_event == CE_HITTING_X ||
9120 trigger_event == CE_HIT_BY_X);
9122 if (change->can_change_or_has_action &&
9123 change->has_event[trigger_event] &&
9124 change->trigger_side & trigger_side &&
9125 change->trigger_player & trigger_player &&
9126 (!check_trigger_element ||
9127 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
9129 change->actual_trigger_element = trigger_element;
9130 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
9131 change->actual_trigger_side = trigger_side;
9132 change->actual_trigger_ce_value = CustomValue[x][y];
9133 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
9135 /* special case: trigger element not at (x,y) position for some events */
9136 if (check_trigger_element)
9148 { 0, 0 }, { 0, 0 }, { 0, 0 },
9152 int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
9153 int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
9155 change->actual_trigger_ce_value = CustomValue[xx][yy];
9156 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
9159 if (change->can_change && !change_done)
9161 ChangeDelay[x][y] = 1;
9162 ChangeEvent[x][y] = trigger_event;
9164 HandleElementChange(x, y, p);
9168 #if USE_NEW_DELAYED_ACTION
9169 else if (change->has_action)
9171 ExecuteCustomElementAction(x, y, element, p);
9172 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
9175 if (change->has_action)
9177 ExecuteCustomElementAction(x, y, element, p);
9178 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
9187 static void PlayPlayerSound(struct PlayerInfo *player)
9189 int jx = player->jx, jy = player->jy;
9190 int sound_element = player->artwork_element;
9191 int last_action = player->last_action_waiting;
9192 int action = player->action_waiting;
9194 if (player->is_waiting)
9196 if (action != last_action)
9197 PlayLevelSoundElementAction(jx, jy, sound_element, action);
9199 PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
9203 if (action != last_action)
9204 StopSound(element_info[sound_element].sound[last_action]);
9206 if (last_action == ACTION_SLEEPING)
9207 PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
9211 static void PlayAllPlayersSound()
9215 for (i = 0; i < MAX_PLAYERS; i++)
9216 if (stored_player[i].active)
9217 PlayPlayerSound(&stored_player[i]);
9220 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
9222 boolean last_waiting = player->is_waiting;
9223 int move_dir = player->MovDir;
9225 player->dir_waiting = move_dir;
9226 player->last_action_waiting = player->action_waiting;
9230 if (!last_waiting) /* not waiting -> waiting */
9232 player->is_waiting = TRUE;
9234 player->frame_counter_bored =
9236 game.player_boring_delay_fixed +
9237 SimpleRND(game.player_boring_delay_random);
9238 player->frame_counter_sleeping =
9240 game.player_sleeping_delay_fixed +
9241 SimpleRND(game.player_sleeping_delay_random);
9244 InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
9246 InitPlayerGfxAnimation(player, ACTION_WAITING, player->MovDir);
9250 if (game.player_sleeping_delay_fixed +
9251 game.player_sleeping_delay_random > 0 &&
9252 player->anim_delay_counter == 0 &&
9253 player->post_delay_counter == 0 &&
9254 FrameCounter >= player->frame_counter_sleeping)
9255 player->is_sleeping = TRUE;
9256 else if (game.player_boring_delay_fixed +
9257 game.player_boring_delay_random > 0 &&
9258 FrameCounter >= player->frame_counter_bored)
9259 player->is_bored = TRUE;
9261 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
9262 player->is_bored ? ACTION_BORING :
9266 if (player->is_sleeping && player->use_murphy)
9268 /* special case for sleeping Murphy when leaning against non-free tile */
9270 if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
9271 (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
9272 !IS_MOVING(player->jx - 1, player->jy)))
9274 else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
9275 (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
9276 !IS_MOVING(player->jx + 1, player->jy)))
9277 move_dir = MV_RIGHT;
9279 player->is_sleeping = FALSE;
9281 player->dir_waiting = move_dir;
9285 if (player->is_sleeping)
9287 if (player->num_special_action_sleeping > 0)
9289 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
9291 int last_special_action = player->special_action_sleeping;
9292 int num_special_action = player->num_special_action_sleeping;
9293 int special_action =
9294 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
9295 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
9296 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
9297 last_special_action + 1 : ACTION_SLEEPING);
9298 int special_graphic =
9299 el_act_dir2img(player->artwork_element, special_action, move_dir);
9301 player->anim_delay_counter =
9302 graphic_info[special_graphic].anim_delay_fixed +
9303 SimpleRND(graphic_info[special_graphic].anim_delay_random);
9304 player->post_delay_counter =
9305 graphic_info[special_graphic].post_delay_fixed +
9306 SimpleRND(graphic_info[special_graphic].post_delay_random);
9308 player->special_action_sleeping = special_action;
9311 if (player->anim_delay_counter > 0)
9313 player->action_waiting = player->special_action_sleeping;
9314 player->anim_delay_counter--;
9316 else if (player->post_delay_counter > 0)
9318 player->post_delay_counter--;
9322 else if (player->is_bored)
9324 if (player->num_special_action_bored > 0)
9326 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
9328 int special_action =
9329 ACTION_BORING_1 + SimpleRND(player->num_special_action_bored);
9330 int special_graphic =
9331 el_act_dir2img(player->artwork_element, special_action, move_dir);
9333 player->anim_delay_counter =
9334 graphic_info[special_graphic].anim_delay_fixed +
9335 SimpleRND(graphic_info[special_graphic].anim_delay_random);
9336 player->post_delay_counter =
9337 graphic_info[special_graphic].post_delay_fixed +
9338 SimpleRND(graphic_info[special_graphic].post_delay_random);
9340 player->special_action_bored = special_action;
9343 if (player->anim_delay_counter > 0)
9345 player->action_waiting = player->special_action_bored;
9346 player->anim_delay_counter--;
9348 else if (player->post_delay_counter > 0)
9350 player->post_delay_counter--;
9355 else if (last_waiting) /* waiting -> not waiting */
9357 player->is_waiting = FALSE;
9358 player->is_bored = FALSE;
9359 player->is_sleeping = FALSE;
9361 player->frame_counter_bored = -1;
9362 player->frame_counter_sleeping = -1;
9364 player->anim_delay_counter = 0;
9365 player->post_delay_counter = 0;
9367 player->dir_waiting = player->MovDir;
9368 player->action_waiting = ACTION_DEFAULT;
9370 player->special_action_bored = ACTION_DEFAULT;
9371 player->special_action_sleeping = ACTION_DEFAULT;
9375 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
9377 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
9378 int left = player_action & JOY_LEFT;
9379 int right = player_action & JOY_RIGHT;
9380 int up = player_action & JOY_UP;
9381 int down = player_action & JOY_DOWN;
9382 int button1 = player_action & JOY_BUTTON_1;
9383 int button2 = player_action & JOY_BUTTON_2;
9384 int dx = (left ? -1 : right ? 1 : 0);
9385 int dy = (up ? -1 : down ? 1 : 0);
9387 if (!player->active || tape.pausing)
9393 snapped = SnapField(player, dx, dy);
9397 dropped = DropElement(player);
9399 moved = MovePlayer(player, dx, dy);
9402 if (tape.single_step && tape.recording && !tape.pausing)
9404 if (button1 || (dropped && !moved))
9406 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9407 SnapField(player, 0, 0); /* stop snapping */
9411 SetPlayerWaiting(player, FALSE);
9413 return player_action;
9417 /* no actions for this player (no input at player's configured device) */
9419 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
9420 SnapField(player, 0, 0);
9421 CheckGravityMovementWhenNotMoving(player);
9423 if (player->MovPos == 0)
9424 SetPlayerWaiting(player, TRUE);
9426 if (player->MovPos == 0) /* needed for tape.playing */
9427 player->is_moving = FALSE;
9429 player->is_dropping = FALSE;
9430 player->is_dropping_pressed = FALSE;
9431 player->drop_pressed_delay = 0;
9437 static void CheckLevelTime()
9441 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9443 if (level.native_em_level->lev->home == 0) /* all players at home */
9445 local_player->LevelSolved = TRUE;
9446 AllPlayersGone = TRUE;
9448 level.native_em_level->lev->home = -1;
9451 if (level.native_em_level->ply[0]->alive == 0 &&
9452 level.native_em_level->ply[1]->alive == 0 &&
9453 level.native_em_level->ply[2]->alive == 0 &&
9454 level.native_em_level->ply[3]->alive == 0) /* all dead */
9455 AllPlayersGone = TRUE;
9458 if (TimeFrames >= FRAMES_PER_SECOND)
9463 for (i = 0; i < MAX_PLAYERS; i++)
9465 struct PlayerInfo *player = &stored_player[i];
9467 if (SHIELD_ON(player))
9469 player->shield_normal_time_left--;
9471 if (player->shield_deadly_time_left > 0)
9472 player->shield_deadly_time_left--;
9476 if (!level.use_step_counter)
9484 if (TimeLeft <= 10 && setup.time_limit)
9485 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
9487 DrawGameValue_Time(TimeLeft);
9489 if (!TimeLeft && setup.time_limit)
9491 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9492 level.native_em_level->lev->killed_out_of_time = TRUE;
9494 for (i = 0; i < MAX_PLAYERS; i++)
9495 KillPlayer(&stored_player[i]);
9498 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
9499 DrawGameValue_Time(TimePlayed);
9501 level.native_em_level->lev->time =
9502 (level.time == 0 ? TimePlayed : TimeLeft);
9505 if (tape.recording || tape.playing)
9506 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
9510 void AdvanceFrameAndPlayerCounters(int player_nr)
9515 Error(ERR_NETWORK_CLIENT, "advancing frame counter from %d to %d",
9516 FrameCounter, FrameCounter + 1);
9519 /* advance frame counters (global frame counter and time frame counter) */
9523 /* advance player counters (counters for move delay, move animation etc.) */
9524 for (i = 0; i < MAX_PLAYERS; i++)
9526 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
9527 int move_delay_value = stored_player[i].move_delay_value;
9528 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
9530 if (!advance_player_counters) /* not all players may be affected */
9533 #if USE_NEW_PLAYER_ANIM
9534 if (move_frames == 0) /* less than one move per game frame */
9536 int stepsize = TILEX / move_delay_value;
9537 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
9538 int count = (stored_player[i].is_moving ?
9539 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
9541 if (count % delay == 0)
9546 stored_player[i].Frame += move_frames;
9548 if (stored_player[i].MovPos != 0)
9549 stored_player[i].StepFrame += move_frames;
9551 if (stored_player[i].move_delay > 0)
9552 stored_player[i].move_delay--;
9554 /* due to bugs in previous versions, counter must count up, not down */
9555 if (stored_player[i].push_delay != -1)
9556 stored_player[i].push_delay++;
9558 if (stored_player[i].drop_delay > 0)
9559 stored_player[i].drop_delay--;
9561 if (stored_player[i].is_dropping_pressed)
9562 stored_player[i].drop_pressed_delay++;
9566 void StartGameActions(boolean init_network_game, boolean record_tape,
9569 unsigned long new_random_seed = InitRND(random_seed);
9572 TapeStartRecording(new_random_seed);
9574 #if defined(NETWORK_AVALIABLE)
9575 if (init_network_game)
9577 SendToServer_StartPlaying();
9585 game_status = GAME_MODE_PLAYING;
9592 static unsigned long game_frame_delay = 0;
9593 unsigned long game_frame_delay_value;
9594 byte *recorded_player_action;
9595 byte summarized_player_action = 0;
9596 byte tape_action[MAX_PLAYERS];
9599 if (game.restart_level)
9600 StartGameActions(options.network, setup.autorecord, NEW_RANDOMIZE);
9602 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9604 if (level.native_em_level->lev->home == 0) /* all players at home */
9606 local_player->LevelSolved = TRUE;
9607 AllPlayersGone = TRUE;
9609 level.native_em_level->lev->home = -1;
9612 if (level.native_em_level->ply[0]->alive == 0 &&
9613 level.native_em_level->ply[1]->alive == 0 &&
9614 level.native_em_level->ply[2]->alive == 0 &&
9615 level.native_em_level->ply[3]->alive == 0) /* all dead */
9616 AllPlayersGone = TRUE;
9619 if (local_player->LevelSolved)
9622 if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
9625 if (game_status != GAME_MODE_PLAYING) /* status might have changed */
9628 game_frame_delay_value =
9629 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
9631 if (tape.playing && tape.warp_forward && !tape.pausing)
9632 game_frame_delay_value = 0;
9634 /* ---------- main game synchronization point ---------- */
9636 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
9638 if (network_playing && !network_player_action_received)
9640 /* try to get network player actions in time */
9642 #if defined(NETWORK_AVALIABLE)
9643 /* last chance to get network player actions without main loop delay */
9647 /* game was quit by network peer */
9648 if (game_status != GAME_MODE_PLAYING)
9651 if (!network_player_action_received)
9652 return; /* failed to get network player actions in time */
9654 /* do not yet reset "network_player_action_received" (for tape.pausing) */
9660 /* at this point we know that we really continue executing the game */
9663 network_player_action_received = FALSE;
9666 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
9668 if (tape.set_centered_player)
9670 game.centered_player_nr_next = tape.centered_player_nr_next;
9671 game.set_centered_player = TRUE;
9674 for (i = 0; i < MAX_PLAYERS; i++)
9676 summarized_player_action |= stored_player[i].action;
9678 if (!network_playing)
9679 stored_player[i].effective_action = stored_player[i].action;
9682 #if defined(NETWORK_AVALIABLE)
9683 if (network_playing)
9684 SendToServer_MovePlayer(summarized_player_action);
9687 if (!options.network && !setup.team_mode)
9688 local_player->effective_action = summarized_player_action;
9690 if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
9692 for (i = 0; i < MAX_PLAYERS; i++)
9693 stored_player[i].effective_action =
9694 (i == game.centered_player_nr ? summarized_player_action : 0);
9697 if (recorded_player_action != NULL)
9698 for (i = 0; i < MAX_PLAYERS; i++)
9699 stored_player[i].effective_action = recorded_player_action[i];
9701 for (i = 0; i < MAX_PLAYERS; i++)
9703 tape_action[i] = stored_player[i].effective_action;
9705 /* (this can only happen in the R'n'D game engine) */
9706 if (tape.recording && tape_action[i] && !tape.player_participates[i])
9707 tape.player_participates[i] = TRUE; /* player just appeared from CE */
9710 /* only record actions from input devices, but not programmed actions */
9712 TapeRecordAction(tape_action);
9714 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9716 GameActions_EM_Main();
9724 void GameActions_EM_Main()
9726 byte effective_action[MAX_PLAYERS];
9727 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
9730 for (i = 0; i < MAX_PLAYERS; i++)
9731 effective_action[i] = stored_player[i].effective_action;
9734 printf("::: %04d: %08x\n", FrameCounter, effective_action[0]);
9737 GameActions_EM(effective_action, warp_mode);
9741 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
9744 void GameActions_RND()
9746 int magic_wall_x = 0, magic_wall_y = 0;
9747 int i, x, y, element, graphic;
9749 InitPlayfieldScanModeVars();
9751 #if USE_ONE_MORE_CHANGE_PER_FRAME
9752 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
9754 SCAN_PLAYFIELD(x, y)
9756 ChangeCount[x][y] = 0;
9757 ChangeEvent[x][y] = -1;
9763 if (game.set_centered_player)
9765 boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
9767 /* switching to "all players" only possible if all players fit to screen */
9768 if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
9770 game.centered_player_nr_next = game.centered_player_nr;
9771 game.set_centered_player = FALSE;
9774 /* do not switch focus to non-existing (or non-active) player */
9775 if (game.centered_player_nr_next >= 0 &&
9776 !stored_player[game.centered_player_nr_next].active)
9778 game.centered_player_nr_next = game.centered_player_nr;
9779 game.set_centered_player = FALSE;
9783 if (game.set_centered_player &&
9784 ScreenMovPos == 0) /* screen currently aligned at tile position */
9788 if (game.centered_player_nr_next == -1)
9790 setScreenCenteredToAllPlayers(&sx, &sy);
9794 sx = stored_player[game.centered_player_nr_next].jx;
9795 sy = stored_player[game.centered_player_nr_next].jy;
9798 game.centered_player_nr = game.centered_player_nr_next;
9799 game.set_centered_player = FALSE;
9801 DrawRelocateScreen(sx, sy, MV_NONE, TRUE, setup.quick_switch);
9802 DrawGameDoorValues();
9806 for (i = 0; i < MAX_PLAYERS; i++)
9808 int actual_player_action = stored_player[i].effective_action;
9811 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
9812 - rnd_equinox_tetrachloride 048
9813 - rnd_equinox_tetrachloride_ii 096
9814 - rnd_emanuel_schmieg 002
9815 - doctor_sloan_ww 001, 020
9817 if (stored_player[i].MovPos == 0)
9818 CheckGravityMovement(&stored_player[i]);
9821 /* overwrite programmed action with tape action */
9822 if (stored_player[i].programmed_action)
9823 actual_player_action = stored_player[i].programmed_action;
9826 PlayerActions(&stored_player[i], actual_player_action);
9828 tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
9830 if (tape.recording && tape_action[i] && !tape.player_participates[i])
9831 tape.player_participates[i] = TRUE; /* player just appeared from CE */
9834 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
9838 network_player_action_received = FALSE;
9841 ScrollScreen(NULL, SCROLL_GO_ON);
9843 /* for backwards compatibility, the following code emulates a fixed bug that
9844 occured when pushing elements (causing elements that just made their last
9845 pushing step to already (if possible) make their first falling step in the
9846 same game frame, which is bad); this code is also needed to use the famous
9847 "spring push bug" which is used in older levels and might be wanted to be
9848 used also in newer levels, but in this case the buggy pushing code is only
9849 affecting the "spring" element and no other elements */
9851 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
9853 for (i = 0; i < MAX_PLAYERS; i++)
9855 struct PlayerInfo *player = &stored_player[i];
9859 if (player->active && player->is_pushing && player->is_moving &&
9861 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
9862 Feld[x][y] == EL_SPRING))
9864 ContinueMoving(x, y);
9866 /* continue moving after pushing (this is actually a bug) */
9867 if (!IS_MOVING(x, y))
9876 SCAN_PLAYFIELD(x, y)
9878 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9881 ChangeCount[x][y] = 0;
9882 ChangeEvent[x][y] = -1;
9884 /* this must be handled before main playfield loop */
9885 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
9888 if (MovDelay[x][y] <= 0)
9892 #if USE_NEW_SNAP_DELAY
9893 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
9896 if (MovDelay[x][y] <= 0)
9899 DrawLevelField(x, y);
9901 TestIfElementTouchesCustomElement(x, y); /* for empty space */
9907 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
9909 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
9910 printf("GameActions(): This should never happen!\n");
9912 ChangePage[x][y] = -1;
9917 if (WasJustMoving[x][y] > 0)
9918 WasJustMoving[x][y]--;
9919 if (WasJustFalling[x][y] > 0)
9920 WasJustFalling[x][y]--;
9921 if (CheckCollision[x][y] > 0)
9922 CheckCollision[x][y]--;
9926 /* reset finished pushing action (not done in ContinueMoving() to allow
9927 continuous pushing animation for elements with zero push delay) */
9928 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
9930 ResetGfxAnimation(x, y);
9931 DrawLevelField(x, y);
9935 if (IS_BLOCKED(x, y))
9939 Blocked2Moving(x, y, &oldx, &oldy);
9940 if (!IS_MOVING(oldx, oldy))
9942 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
9943 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
9944 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
9945 printf("GameActions(): This should never happen!\n");
9952 SCAN_PLAYFIELD(x, y)
9954 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9957 element = Feld[x][y];
9958 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9961 printf("::: %d,%d\n", x, y);
9963 if (element == EL_ROCK)
9964 printf("::: Yo man! Rocks can fall!\n");
9968 ResetGfxFrame(x, y, TRUE);
9970 if (graphic_info[graphic].anim_global_sync)
9971 GfxFrame[x][y] = FrameCounter;
9972 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
9974 int old_gfx_frame = GfxFrame[x][y];
9976 GfxFrame[x][y] = CustomValue[x][y];
9979 if (GfxFrame[x][y] != old_gfx_frame)
9981 DrawLevelGraphicAnimation(x, y, graphic);
9983 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
9985 int old_gfx_frame = GfxFrame[x][y];
9987 GfxFrame[x][y] = element_info[element].collect_score;
9990 if (GfxFrame[x][y] != old_gfx_frame)
9992 DrawLevelGraphicAnimation(x, y, graphic);
9994 else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
9996 int old_gfx_frame = GfxFrame[x][y];
9998 GfxFrame[x][y] = ChangeDelay[x][y];
10001 if (GfxFrame[x][y] != old_gfx_frame)
10003 DrawLevelGraphicAnimation(x, y, graphic);
10007 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
10008 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
10009 ResetRandomAnimationValue(x, y);
10011 SetRandomAnimationValue(x, y);
10013 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
10015 if (IS_INACTIVE(element))
10017 if (IS_ANIMATED(graphic))
10018 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10023 /* this may take place after moving, so 'element' may have changed */
10024 if (IS_CHANGING(x, y) &&
10025 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
10027 int page = element_info[element].event_page_nr[CE_DELAY];
10029 HandleElementChange(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] : page);
10033 printf("::: ChangeDelay == %d\n", ChangeDelay[x][y]);
10037 if (element == EL_CUSTOM_255)
10038 printf("::: ChangeDelay == %d\n", ChangeDelay[x][y]);
10042 HandleElementChange(x, y, page);
10044 if (CAN_CHANGE(element))
10045 HandleElementChange(x, y, page);
10047 if (HAS_ACTION(element))
10048 ExecuteCustomElementAction(x, y, element, page);
10053 element = Feld[x][y];
10054 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10057 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
10061 element = Feld[x][y];
10062 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10064 if (IS_ANIMATED(graphic) &&
10065 !IS_MOVING(x, y) &&
10067 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10069 if (IS_GEM(element) || element == EL_SP_INFOTRON)
10070 EdelsteinFunkeln(x, y);
10072 else if ((element == EL_ACID ||
10073 element == EL_EXIT_OPEN ||
10074 element == EL_SP_EXIT_OPEN ||
10075 element == EL_SP_TERMINAL ||
10076 element == EL_SP_TERMINAL_ACTIVE ||
10077 element == EL_EXTRA_TIME ||
10078 element == EL_SHIELD_NORMAL ||
10079 element == EL_SHIELD_DEADLY) &&
10080 IS_ANIMATED(graphic))
10081 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10082 else if (IS_MOVING(x, y))
10083 ContinueMoving(x, y);
10084 else if (IS_ACTIVE_BOMB(element))
10085 CheckDynamite(x, y);
10086 else if (element == EL_AMOEBA_GROWING)
10087 AmoebeWaechst(x, y);
10088 else if (element == EL_AMOEBA_SHRINKING)
10089 AmoebaDisappearing(x, y);
10091 #if !USE_NEW_AMOEBA_CODE
10092 else if (IS_AMOEBALIVE(element))
10093 AmoebeAbleger(x, y);
10096 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
10098 else if (element == EL_EXIT_CLOSED)
10100 else if (element == EL_SP_EXIT_CLOSED)
10102 else if (element == EL_EXPANDABLE_WALL_GROWING)
10103 MauerWaechst(x, y);
10104 else if (element == EL_EXPANDABLE_WALL ||
10105 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
10106 element == EL_EXPANDABLE_WALL_VERTICAL ||
10107 element == EL_EXPANDABLE_WALL_ANY ||
10108 element == EL_BD_EXPANDABLE_WALL)
10109 MauerAbleger(x, y);
10110 else if (element == EL_FLAMES)
10111 CheckForDragon(x, y);
10112 else if (element == EL_EXPLOSION)
10113 ; /* drawing of correct explosion animation is handled separately */
10114 else if (element == EL_ELEMENT_SNAPPING ||
10115 element == EL_DIAGONAL_SHRINKING ||
10116 element == EL_DIAGONAL_GROWING)
10119 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
10121 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10124 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
10125 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10128 if (element == EL_CUSTOM_255 ||
10129 element == EL_CUSTOM_256)
10130 DrawLevelGraphicAnimation(x, y, graphic);
10133 if (IS_BELT_ACTIVE(element))
10134 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
10136 if (game.magic_wall_active)
10138 int jx = local_player->jx, jy = local_player->jy;
10140 /* play the element sound at the position nearest to the player */
10141 if ((element == EL_MAGIC_WALL_FULL ||
10142 element == EL_MAGIC_WALL_ACTIVE ||
10143 element == EL_MAGIC_WALL_EMPTYING ||
10144 element == EL_BD_MAGIC_WALL_FULL ||
10145 element == EL_BD_MAGIC_WALL_ACTIVE ||
10146 element == EL_BD_MAGIC_WALL_EMPTYING) &&
10147 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
10155 #if USE_NEW_AMOEBA_CODE
10156 /* new experimental amoeba growth stuff */
10157 if (!(FrameCounter % 8))
10159 static unsigned long random = 1684108901;
10161 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
10163 x = RND(lev_fieldx);
10164 y = RND(lev_fieldy);
10165 element = Feld[x][y];
10167 if (!IS_PLAYER(x,y) &&
10168 (element == EL_EMPTY ||
10169 CAN_GROW_INTO(element) ||
10170 element == EL_QUICKSAND_EMPTY ||
10171 element == EL_ACID_SPLASH_LEFT ||
10172 element == EL_ACID_SPLASH_RIGHT))
10174 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
10175 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
10176 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
10177 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
10178 Feld[x][y] = EL_AMOEBA_DROP;
10181 random = random * 129 + 1;
10187 if (game.explosions_delayed)
10190 game.explosions_delayed = FALSE;
10193 SCAN_PLAYFIELD(x, y)
10195 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
10198 element = Feld[x][y];
10200 if (ExplodeField[x][y])
10201 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
10202 else if (element == EL_EXPLOSION)
10203 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
10205 ExplodeField[x][y] = EX_TYPE_NONE;
10208 game.explosions_delayed = TRUE;
10211 if (game.magic_wall_active)
10213 if (!(game.magic_wall_time_left % 4))
10215 int element = Feld[magic_wall_x][magic_wall_y];
10217 if (element == EL_BD_MAGIC_WALL_FULL ||
10218 element == EL_BD_MAGIC_WALL_ACTIVE ||
10219 element == EL_BD_MAGIC_WALL_EMPTYING)
10220 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
10222 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
10225 if (game.magic_wall_time_left > 0)
10227 game.magic_wall_time_left--;
10228 if (!game.magic_wall_time_left)
10231 SCAN_PLAYFIELD(x, y)
10233 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
10236 element = Feld[x][y];
10238 if (element == EL_MAGIC_WALL_ACTIVE ||
10239 element == EL_MAGIC_WALL_FULL)
10241 Feld[x][y] = EL_MAGIC_WALL_DEAD;
10242 DrawLevelField(x, y);
10244 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
10245 element == EL_BD_MAGIC_WALL_FULL)
10247 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
10248 DrawLevelField(x, y);
10252 game.magic_wall_active = FALSE;
10257 if (game.light_time_left > 0)
10259 game.light_time_left--;
10261 if (game.light_time_left == 0)
10262 RedrawAllLightSwitchesAndInvisibleElements();
10265 if (game.timegate_time_left > 0)
10267 game.timegate_time_left--;
10269 if (game.timegate_time_left == 0)
10270 CloseAllOpenTimegates();
10273 if (game.lenses_time_left > 0)
10275 game.lenses_time_left--;
10277 if (game.lenses_time_left == 0)
10278 RedrawAllInvisibleElementsForLenses();
10281 if (game.magnify_time_left > 0)
10283 game.magnify_time_left--;
10285 if (game.magnify_time_left == 0)
10286 RedrawAllInvisibleElementsForMagnifier();
10289 for (i = 0; i < MAX_PLAYERS; i++)
10291 struct PlayerInfo *player = &stored_player[i];
10293 if (SHIELD_ON(player))
10295 if (player->shield_deadly_time_left)
10296 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
10297 else if (player->shield_normal_time_left)
10298 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
10305 PlayAllPlayersSound();
10307 if (options.debug) /* calculate frames per second */
10309 static unsigned long fps_counter = 0;
10310 static int fps_frames = 0;
10311 unsigned long fps_delay_ms = Counter() - fps_counter;
10315 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
10317 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
10320 fps_counter = Counter();
10323 redraw_mask |= REDRAW_FPS;
10326 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
10328 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
10330 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
10332 local_player->show_envelope = 0;
10335 /* use random number generator in every frame to make it less predictable */
10336 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
10340 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
10342 int min_x = x, min_y = y, max_x = x, max_y = y;
10345 for (i = 0; i < MAX_PLAYERS; i++)
10347 int jx = stored_player[i].jx, jy = stored_player[i].jy;
10349 if (!stored_player[i].active || &stored_player[i] == player)
10352 min_x = MIN(min_x, jx);
10353 min_y = MIN(min_y, jy);
10354 max_x = MAX(max_x, jx);
10355 max_y = MAX(max_y, jy);
10358 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
10361 static boolean AllPlayersInVisibleScreen()
10365 for (i = 0; i < MAX_PLAYERS; i++)
10367 int jx = stored_player[i].jx, jy = stored_player[i].jy;
10369 if (!stored_player[i].active)
10372 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
10379 void ScrollLevel(int dx, int dy)
10381 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
10384 BlitBitmap(drawto_field, drawto_field,
10385 FX + TILEX * (dx == -1) - softscroll_offset,
10386 FY + TILEY * (dy == -1) - softscroll_offset,
10387 SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
10388 SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
10389 FX + TILEX * (dx == 1) - softscroll_offset,
10390 FY + TILEY * (dy == 1) - softscroll_offset);
10394 x = (dx == 1 ? BX1 : BX2);
10395 for (y = BY1; y <= BY2; y++)
10396 DrawScreenField(x, y);
10401 y = (dy == 1 ? BY1 : BY2);
10402 for (x = BX1; x <= BX2; x++)
10403 DrawScreenField(x, y);
10406 redraw_mask |= REDRAW_FIELD;
10409 static boolean canFallDown(struct PlayerInfo *player)
10411 int jx = player->jx, jy = player->jy;
10413 return (IN_LEV_FIELD(jx, jy + 1) &&
10414 (IS_FREE(jx, jy + 1) ||
10415 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
10416 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
10417 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
10420 static boolean canPassField(int x, int y, int move_dir)
10422 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
10423 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
10424 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
10425 int nextx = x + dx;
10426 int nexty = y + dy;
10427 int element = Feld[x][y];
10429 return (IS_PASSABLE_FROM(element, opposite_dir) &&
10430 !CAN_MOVE(element) &&
10431 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
10432 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
10433 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
10436 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
10438 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
10439 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
10440 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
10444 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
10445 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
10446 (IS_DIGGABLE(Feld[newx][newy]) ||
10447 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
10448 canPassField(newx, newy, move_dir)));
10451 static void CheckGravityMovement(struct PlayerInfo *player)
10453 #if USE_PLAYER_GRAVITY
10454 if (player->gravity && !player->programmed_action)
10456 if (game.gravity && !player->programmed_action)
10459 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
10460 int move_dir_vertical = player->effective_action & MV_VERTICAL;
10461 boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
10462 int jx = player->jx, jy = player->jy;
10463 boolean player_is_moving_to_valid_field =
10464 (!player_is_snapping &&
10465 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
10466 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
10467 boolean player_can_fall_down = canFallDown(player);
10469 if (player_can_fall_down &&
10470 !player_is_moving_to_valid_field)
10471 player->programmed_action = MV_DOWN;
10475 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
10477 return CheckGravityMovement(player);
10479 #if USE_PLAYER_GRAVITY
10480 if (player->gravity && !player->programmed_action)
10482 if (game.gravity && !player->programmed_action)
10485 int jx = player->jx, jy = player->jy;
10486 boolean field_under_player_is_free =
10487 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
10488 boolean player_is_standing_on_valid_field =
10489 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
10490 (IS_WALKABLE(Feld[jx][jy]) &&
10491 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
10493 if (field_under_player_is_free && !player_is_standing_on_valid_field)
10494 player->programmed_action = MV_DOWN;
10499 MovePlayerOneStep()
10500 -----------------------------------------------------------------------------
10501 dx, dy: direction (non-diagonal) to try to move the player to
10502 real_dx, real_dy: direction as read from input device (can be diagonal)
10505 boolean MovePlayerOneStep(struct PlayerInfo *player,
10506 int dx, int dy, int real_dx, int real_dy)
10508 int jx = player->jx, jy = player->jy;
10509 int new_jx = jx + dx, new_jy = jy + dy;
10510 #if !USE_FIXED_DONT_RUN_INTO
10514 boolean player_can_move = !player->cannot_move;
10516 if (!player->active || (!dx && !dy))
10517 return MP_NO_ACTION;
10519 player->MovDir = (dx < 0 ? MV_LEFT :
10520 dx > 0 ? MV_RIGHT :
10522 dy > 0 ? MV_DOWN : MV_NONE);
10524 if (!IN_LEV_FIELD(new_jx, new_jy))
10525 return MP_NO_ACTION;
10527 if (!player_can_move)
10530 if (player->MovPos == 0)
10532 player->is_moving = FALSE;
10533 player->is_digging = FALSE;
10534 player->is_collecting = FALSE;
10535 player->is_snapping = FALSE;
10536 player->is_pushing = FALSE;
10539 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
10540 SnapField(player, 0, 0);
10544 return MP_NO_ACTION;
10549 if (!options.network && game.centered_player_nr == -1 &&
10550 !AllPlayersInSight(player, new_jx, new_jy))
10551 return MP_NO_ACTION;
10553 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
10554 return MP_NO_ACTION;
10557 #if !USE_FIXED_DONT_RUN_INTO
10558 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
10560 /* (moved to DigField()) */
10561 if (player_can_move && DONT_RUN_INTO(element))
10563 if (element == EL_ACID && dx == 0 && dy == 1)
10565 SplashAcid(new_jx, new_jy);
10566 Feld[jx][jy] = EL_PLAYER_1;
10567 InitMovingField(jx, jy, MV_DOWN);
10568 Store[jx][jy] = EL_ACID;
10569 ContinueMoving(jx, jy);
10570 BuryPlayer(player);
10573 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
10579 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
10581 #if USE_FIXED_DONT_RUN_INTO
10582 if (can_move == MP_DONT_RUN_INTO)
10586 if (can_move != MP_MOVING)
10589 #if USE_FIXED_DONT_RUN_INTO
10592 /* check if DigField() has caused relocation of the player */
10593 if (player->jx != jx || player->jy != jy)
10594 return MP_NO_ACTION; /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
10596 StorePlayer[jx][jy] = 0;
10597 player->last_jx = jx;
10598 player->last_jy = jy;
10599 player->jx = new_jx;
10600 player->jy = new_jy;
10601 StorePlayer[new_jx][new_jy] = player->element_nr;
10603 if (player->move_delay_value_next != -1)
10605 player->move_delay_value = player->move_delay_value_next;
10606 player->move_delay_value_next = -1;
10610 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
10612 player->step_counter++;
10614 PlayerVisit[jx][jy] = FrameCounter;
10616 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;
10817 CheckGravityMovementWhenNotMoving(player);
10819 player->is_moving = FALSE;
10821 /* at this point, the player is allowed to move, but cannot move right now
10822 (e.g. because of something blocking the way) -- ensure that the player
10823 is also allowed to move in the next frame (in old versions before 3.1.1,
10824 the player was forced to wait again for eight frames before next try) */
10826 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
10827 player->move_delay = 0; /* allow direct movement in the next frame */
10830 if (player->move_delay == -1) /* not yet initialized by DigField() */
10831 player->move_delay = player->move_delay_value;
10833 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10835 TestIfPlayerTouchesBadThing(jx, jy);
10836 TestIfPlayerTouchesCustomElement(jx, jy);
10839 if (!player->active)
10840 RemovePlayer(player);
10845 void ScrollPlayer(struct PlayerInfo *player, int mode)
10847 int jx = player->jx, jy = player->jy;
10848 int last_jx = player->last_jx, last_jy = player->last_jy;
10849 int move_stepsize = TILEX / player->move_delay_value;
10851 #if USE_NEW_PLAYER_SPEED
10852 if (!player->active)
10855 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
10858 if (!player->active || player->MovPos == 0)
10862 if (mode == SCROLL_INIT)
10864 player->actual_frame_counter = FrameCounter;
10865 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10867 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
10868 Feld[last_jx][last_jy] == EL_EMPTY)
10870 int last_field_block_delay = 0; /* start with no blocking at all */
10871 int block_delay_adjustment = player->block_delay_adjustment;
10873 /* if player blocks last field, add delay for exactly one move */
10874 if (player->block_last_field)
10876 last_field_block_delay += player->move_delay_value;
10878 /* when blocking enabled, prevent moving up despite gravity */
10879 #if USE_PLAYER_GRAVITY
10880 if (player->gravity && player->MovDir == MV_UP)
10881 block_delay_adjustment = -1;
10883 if (game.gravity && player->MovDir == MV_UP)
10884 block_delay_adjustment = -1;
10888 /* add block delay adjustment (also possible when not blocking) */
10889 last_field_block_delay += block_delay_adjustment;
10891 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
10892 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
10895 #if USE_NEW_PLAYER_SPEED
10896 if (player->MovPos != 0) /* player has not yet reached destination */
10902 else if (!FrameReached(&player->actual_frame_counter, 1))
10906 printf("::: player->MovPos: %d -> %d\n",
10908 player->MovPos + (player->MovPos > 0 ? -1 : 1) * move_stepsize);
10911 #if USE_NEW_PLAYER_SPEED
10912 if (player->MovPos != 0)
10914 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
10915 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10917 /* before DrawPlayer() to draw correct player graphic for this case */
10918 if (player->MovPos == 0)
10919 CheckGravityMovement(player);
10922 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
10923 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10925 /* before DrawPlayer() to draw correct player graphic for this case */
10926 if (player->MovPos == 0)
10927 CheckGravityMovement(player);
10930 if (player->MovPos == 0) /* player reached destination field */
10933 printf("::: player reached destination field\n");
10936 if (player->move_delay_reset_counter > 0)
10938 player->move_delay_reset_counter--;
10940 if (player->move_delay_reset_counter == 0)
10942 /* continue with normal speed after quickly moving through gate */
10943 HALVE_PLAYER_SPEED(player);
10945 /* be able to make the next move without delay */
10946 player->move_delay = 0;
10950 player->last_jx = jx;
10951 player->last_jy = jy;
10953 if (Feld[jx][jy] == EL_EXIT_OPEN ||
10954 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
10955 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
10957 DrawPlayer(player); /* needed here only to cleanup last field */
10958 RemovePlayer(player);
10960 if (local_player->friends_still_needed == 0 ||
10961 IS_SP_ELEMENT(Feld[jx][jy]))
10962 player->LevelSolved = player->GameOver = TRUE;
10965 /* this breaks one level: "machine", level 000 */
10967 int move_direction = player->MovDir;
10968 int enter_side = MV_DIR_OPPOSITE(move_direction);
10969 int leave_side = move_direction;
10970 int old_jx = last_jx;
10971 int old_jy = last_jy;
10972 int old_element = Feld[old_jx][old_jy];
10973 int new_element = Feld[jx][jy];
10975 if (IS_CUSTOM_ELEMENT(old_element))
10976 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
10978 player->index_bit, leave_side);
10980 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
10981 CE_PLAYER_LEAVES_X,
10982 player->index_bit, leave_side);
10984 if (IS_CUSTOM_ELEMENT(new_element))
10985 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
10986 player->index_bit, enter_side);
10988 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
10989 CE_PLAYER_ENTERS_X,
10990 player->index_bit, enter_side);
10992 CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
10993 CE_MOVE_OF_X, move_direction);
10996 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10998 TestIfPlayerTouchesBadThing(jx, jy);
10999 TestIfPlayerTouchesCustomElement(jx, jy);
11001 /* needed because pushed element has not yet reached its destination,
11002 so it would trigger a change event at its previous field location */
11003 if (!player->is_pushing)
11004 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
11006 if (!player->active)
11007 RemovePlayer(player);
11010 if (level.use_step_counter)
11020 if (TimeLeft <= 10 && setup.time_limit)
11021 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
11023 DrawGameValue_Time(TimeLeft);
11025 if (!TimeLeft && setup.time_limit)
11026 for (i = 0; i < MAX_PLAYERS; i++)
11027 KillPlayer(&stored_player[i]);
11029 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
11030 DrawGameValue_Time(TimePlayed);
11033 if (tape.single_step && tape.recording && !tape.pausing &&
11034 !player->programmed_action)
11035 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
11039 void ScrollScreen(struct PlayerInfo *player, int mode)
11041 static unsigned long screen_frame_counter = 0;
11043 if (mode == SCROLL_INIT)
11045 /* set scrolling step size according to actual player's moving speed */
11046 ScrollStepSize = TILEX / player->move_delay_value;
11048 screen_frame_counter = FrameCounter;
11049 ScreenMovDir = player->MovDir;
11050 ScreenMovPos = player->MovPos;
11051 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
11054 else if (!FrameReached(&screen_frame_counter, 1))
11059 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
11060 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
11061 redraw_mask |= REDRAW_FIELD;
11064 ScreenMovDir = MV_NONE;
11067 void TestIfPlayerTouchesCustomElement(int x, int y)
11069 static int xy[4][2] =
11076 static int trigger_sides[4][2] =
11078 /* center side border side */
11079 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
11080 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
11081 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
11082 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
11084 static int touch_dir[4] =
11086 MV_LEFT | MV_RIGHT,
11091 int center_element = Feld[x][y]; /* should always be non-moving! */
11094 for (i = 0; i < NUM_DIRECTIONS; i++)
11096 int xx = x + xy[i][0];
11097 int yy = y + xy[i][1];
11098 int center_side = trigger_sides[i][0];
11099 int border_side = trigger_sides[i][1];
11100 int border_element;
11102 if (!IN_LEV_FIELD(xx, yy))
11105 if (IS_PLAYER(x, y))
11107 struct PlayerInfo *player = PLAYERINFO(x, y);
11109 if (game.engine_version < VERSION_IDENT(3,0,7,0))
11110 border_element = Feld[xx][yy]; /* may be moving! */
11111 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
11112 border_element = Feld[xx][yy];
11113 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
11114 border_element = MovingOrBlocked2Element(xx, yy);
11116 continue; /* center and border element do not touch */
11118 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
11119 player->index_bit, border_side);
11120 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
11121 CE_PLAYER_TOUCHES_X,
11122 player->index_bit, border_side);
11124 else if (IS_PLAYER(xx, yy))
11126 struct PlayerInfo *player = PLAYERINFO(xx, yy);
11128 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
11130 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
11131 continue; /* center and border element do not touch */
11134 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
11135 player->index_bit, center_side);
11136 CheckTriggeredElementChangeByPlayer(x, y, center_element,
11137 CE_PLAYER_TOUCHES_X,
11138 player->index_bit, center_side);
11144 #if USE_ELEMENT_TOUCHING_BUGFIX
11146 void TestIfElementTouchesCustomElement(int x, int y)
11148 static int xy[4][2] =
11155 static int trigger_sides[4][2] =
11157 /* center side border side */
11158 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
11159 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
11160 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
11161 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
11163 static int touch_dir[4] =
11165 MV_LEFT | MV_RIGHT,
11170 boolean change_center_element = FALSE;
11171 int center_element = Feld[x][y]; /* should always be non-moving! */
11172 int border_element_old[NUM_DIRECTIONS];
11175 for (i = 0; i < NUM_DIRECTIONS; i++)
11177 int xx = x + xy[i][0];
11178 int yy = y + xy[i][1];
11179 int border_element;
11181 border_element_old[i] = -1;
11183 if (!IN_LEV_FIELD(xx, yy))
11186 if (game.engine_version < VERSION_IDENT(3,0,7,0))
11187 border_element = Feld[xx][yy]; /* may be moving! */
11188 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
11189 border_element = Feld[xx][yy];
11190 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
11191 border_element = MovingOrBlocked2Element(xx, yy);
11193 continue; /* center and border element do not touch */
11195 border_element_old[i] = border_element;
11198 for (i = 0; i < NUM_DIRECTIONS; i++)
11200 int xx = x + xy[i][0];
11201 int yy = y + xy[i][1];
11202 int center_side = trigger_sides[i][0];
11203 int border_element = border_element_old[i];
11205 if (border_element == -1)
11208 /* check for change of border element */
11209 CheckElementChangeBySide(xx, yy, border_element, center_element,
11210 CE_TOUCHING_X, center_side);
11213 for (i = 0; i < NUM_DIRECTIONS; i++)
11215 int border_side = trigger_sides[i][1];
11216 int border_element = border_element_old[i];
11218 if (border_element == -1)
11221 /* check for change of center element (but change it only once) */
11222 if (!change_center_element)
11223 change_center_element =
11224 CheckElementChangeBySide(x, y, center_element, border_element,
11225 CE_TOUCHING_X, border_side);
11231 void TestIfElementTouchesCustomElement_OLD(int x, int y)
11233 static int xy[4][2] =
11240 static int trigger_sides[4][2] =
11242 /* center side border side */
11243 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
11244 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
11245 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
11246 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
11248 static int touch_dir[4] =
11250 MV_LEFT | MV_RIGHT,
11255 boolean change_center_element = FALSE;
11256 int center_element = Feld[x][y]; /* should always be non-moving! */
11259 for (i = 0; i < NUM_DIRECTIONS; i++)
11261 int xx = x + xy[i][0];
11262 int yy = y + xy[i][1];
11263 int center_side = trigger_sides[i][0];
11264 int border_side = trigger_sides[i][1];
11265 int border_element;
11267 if (!IN_LEV_FIELD(xx, yy))
11270 if (game.engine_version < VERSION_IDENT(3,0,7,0))
11271 border_element = Feld[xx][yy]; /* may be moving! */
11272 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
11273 border_element = Feld[xx][yy];
11274 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
11275 border_element = MovingOrBlocked2Element(xx, yy);
11277 continue; /* center and border element do not touch */
11279 /* check for change of center element (but change it only once) */
11280 if (!change_center_element)
11281 change_center_element =
11282 CheckElementChangeBySide(x, y, center_element, border_element,
11283 CE_TOUCHING_X, border_side);
11285 /* check for change of border element */
11286 CheckElementChangeBySide(xx, yy, border_element, center_element,
11287 CE_TOUCHING_X, center_side);
11293 void TestIfElementHitsCustomElement(int x, int y, int direction)
11295 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
11296 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
11297 int hitx = x + dx, hity = y + dy;
11298 int hitting_element = Feld[x][y];
11299 int touched_element;
11301 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
11304 touched_element = (IN_LEV_FIELD(hitx, hity) ?
11305 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
11307 if (IN_LEV_FIELD(hitx, hity))
11309 int opposite_direction = MV_DIR_OPPOSITE(direction);
11310 int hitting_side = direction;
11311 int touched_side = opposite_direction;
11312 boolean object_hit = (!IS_MOVING(hitx, hity) ||
11313 MovDir[hitx][hity] != direction ||
11314 ABS(MovPos[hitx][hity]) <= TILEY / 2);
11320 CheckElementChangeBySide(x, y, hitting_element, touched_element,
11321 CE_HITTING_X, touched_side);
11323 CheckElementChangeBySide(hitx, hity, touched_element,
11324 hitting_element, CE_HIT_BY_X, hitting_side);
11326 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
11327 CE_HIT_BY_SOMETHING, opposite_direction);
11331 /* "hitting something" is also true when hitting the playfield border */
11332 CheckElementChangeBySide(x, y, hitting_element, touched_element,
11333 CE_HITTING_SOMETHING, direction);
11337 void TestIfElementSmashesCustomElement(int x, int y, int direction)
11339 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
11340 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
11341 int hitx = x + dx, hity = y + dy;
11342 int hitting_element = Feld[x][y];
11343 int touched_element;
11345 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
11346 !IS_FREE(hitx, hity) &&
11347 (!IS_MOVING(hitx, hity) ||
11348 MovDir[hitx][hity] != direction ||
11349 ABS(MovPos[hitx][hity]) <= TILEY / 2));
11352 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
11356 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
11360 touched_element = (IN_LEV_FIELD(hitx, hity) ?
11361 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
11363 CheckElementChangeBySide(x, y, hitting_element, touched_element,
11364 EP_CAN_SMASH_EVERYTHING, direction);
11366 if (IN_LEV_FIELD(hitx, hity))
11368 int opposite_direction = MV_DIR_OPPOSITE(direction);
11369 int hitting_side = direction;
11370 int touched_side = opposite_direction;
11372 int touched_element = MovingOrBlocked2Element(hitx, hity);
11375 boolean object_hit = (!IS_MOVING(hitx, hity) ||
11376 MovDir[hitx][hity] != direction ||
11377 ABS(MovPos[hitx][hity]) <= TILEY / 2);
11386 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
11387 CE_SMASHED_BY_SOMETHING, opposite_direction);
11389 CheckElementChangeBySide(x, y, hitting_element, touched_element,
11390 CE_OTHER_IS_SMASHING, touched_side);
11392 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
11393 CE_OTHER_GETS_SMASHED, hitting_side);
11399 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
11401 int i, kill_x = -1, kill_y = -1;
11403 int bad_element = -1;
11404 static int test_xy[4][2] =
11411 static int test_dir[4] =
11419 for (i = 0; i < NUM_DIRECTIONS; i++)
11421 int test_x, test_y, test_move_dir, test_element;
11423 test_x = good_x + test_xy[i][0];
11424 test_y = good_y + test_xy[i][1];
11426 if (!IN_LEV_FIELD(test_x, test_y))
11430 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
11432 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
11434 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
11435 2nd case: DONT_TOUCH style bad thing does not move away from good thing
11437 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
11438 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
11442 bad_element = test_element;
11448 if (kill_x != -1 || kill_y != -1)
11450 if (IS_PLAYER(good_x, good_y))
11452 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
11454 if (player->shield_deadly_time_left > 0 &&
11455 !IS_INDESTRUCTIBLE(bad_element))
11456 Bang(kill_x, kill_y);
11457 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
11458 KillPlayer(player);
11461 Bang(good_x, good_y);
11465 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
11467 int i, kill_x = -1, kill_y = -1;
11468 int bad_element = Feld[bad_x][bad_y];
11469 static int test_xy[4][2] =
11476 static int touch_dir[4] =
11478 MV_LEFT | MV_RIGHT,
11483 static int test_dir[4] =
11491 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
11494 for (i = 0; i < NUM_DIRECTIONS; i++)
11496 int test_x, test_y, test_move_dir, test_element;
11498 test_x = bad_x + test_xy[i][0];
11499 test_y = bad_y + test_xy[i][1];
11500 if (!IN_LEV_FIELD(test_x, test_y))
11504 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
11506 test_element = Feld[test_x][test_y];
11508 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
11509 2nd case: DONT_TOUCH style bad thing does not move away from good thing
11511 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
11512 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
11514 /* good thing is player or penguin that does not move away */
11515 if (IS_PLAYER(test_x, test_y))
11517 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
11519 if (bad_element == EL_ROBOT && player->is_moving)
11520 continue; /* robot does not kill player if he is moving */
11522 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
11524 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
11525 continue; /* center and border element do not touch */
11532 else if (test_element == EL_PENGUIN)
11541 if (kill_x != -1 || kill_y != -1)
11543 if (IS_PLAYER(kill_x, kill_y))
11545 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
11547 if (player->shield_deadly_time_left > 0 &&
11548 !IS_INDESTRUCTIBLE(bad_element))
11549 Bang(bad_x, bad_y);
11550 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
11551 KillPlayer(player);
11554 Bang(kill_x, kill_y);
11558 void TestIfPlayerTouchesBadThing(int x, int y)
11560 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
11563 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
11565 TestIfGoodThingHitsBadThing(x, y, move_dir);
11568 void TestIfBadThingTouchesPlayer(int x, int y)
11570 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
11573 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
11575 TestIfBadThingHitsGoodThing(x, y, move_dir);
11578 void TestIfFriendTouchesBadThing(int x, int y)
11580 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
11583 void TestIfBadThingTouchesFriend(int x, int y)
11585 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
11588 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
11590 int i, kill_x = bad_x, kill_y = bad_y;
11591 static int xy[4][2] =
11599 for (i = 0; i < NUM_DIRECTIONS; i++)
11603 x = bad_x + xy[i][0];
11604 y = bad_y + xy[i][1];
11605 if (!IN_LEV_FIELD(x, y))
11608 element = Feld[x][y];
11609 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
11610 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
11618 if (kill_x != bad_x || kill_y != bad_y)
11619 Bang(bad_x, bad_y);
11622 void KillPlayer(struct PlayerInfo *player)
11624 int jx = player->jx, jy = player->jy;
11626 if (!player->active)
11629 /* remove accessible field at the player's position */
11630 Feld[jx][jy] = EL_EMPTY;
11632 /* deactivate shield (else Bang()/Explode() would not work right) */
11633 player->shield_normal_time_left = 0;
11634 player->shield_deadly_time_left = 0;
11637 BuryPlayer(player);
11640 static void KillPlayerUnlessEnemyProtected(int x, int y)
11642 if (!PLAYER_ENEMY_PROTECTED(x, y))
11643 KillPlayer(PLAYERINFO(x, y));
11646 static void KillPlayerUnlessExplosionProtected(int x, int y)
11648 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
11649 KillPlayer(PLAYERINFO(x, y));
11652 void BuryPlayer(struct PlayerInfo *player)
11654 int jx = player->jx, jy = player->jy;
11656 if (!player->active)
11659 PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
11660 PlayLevelSound(jx, jy, SND_GAME_LOSING);
11662 player->GameOver = TRUE;
11663 RemovePlayer(player);
11666 void RemovePlayer(struct PlayerInfo *player)
11668 int jx = player->jx, jy = player->jy;
11669 int i, found = FALSE;
11671 player->present = FALSE;
11672 player->active = FALSE;
11674 if (!ExplodeField[jx][jy])
11675 StorePlayer[jx][jy] = 0;
11677 if (player->is_moving)
11678 DrawLevelField(player->last_jx, player->last_jy);
11680 for (i = 0; i < MAX_PLAYERS; i++)
11681 if (stored_player[i].active)
11685 AllPlayersGone = TRUE;
11691 #if USE_NEW_SNAP_DELAY
11692 static void setFieldForSnapping(int x, int y, int element, int direction)
11694 struct ElementInfo *ei = &element_info[element];
11695 int direction_bit = MV_DIR_TO_BIT(direction);
11696 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
11697 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
11698 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
11700 Feld[x][y] = EL_ELEMENT_SNAPPING;
11701 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
11703 ResetGfxAnimation(x, y);
11705 GfxElement[x][y] = element;
11706 GfxAction[x][y] = action;
11707 GfxDir[x][y] = direction;
11708 GfxFrame[x][y] = -1;
11713 =============================================================================
11714 checkDiagonalPushing()
11715 -----------------------------------------------------------------------------
11716 check if diagonal input device direction results in pushing of object
11717 (by checking if the alternative direction is walkable, diggable, ...)
11718 =============================================================================
11721 static boolean checkDiagonalPushing(struct PlayerInfo *player,
11722 int x, int y, int real_dx, int real_dy)
11724 int jx, jy, dx, dy, xx, yy;
11726 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
11729 /* diagonal direction: check alternative direction */
11734 xx = jx + (dx == 0 ? real_dx : 0);
11735 yy = jy + (dy == 0 ? real_dy : 0);
11737 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
11741 =============================================================================
11743 -----------------------------------------------------------------------------
11744 x, y: field next to player (non-diagonal) to try to dig to
11745 real_dx, real_dy: direction as read from input device (can be diagonal)
11746 =============================================================================
11749 int DigField(struct PlayerInfo *player,
11750 int oldx, int oldy, int x, int y,
11751 int real_dx, int real_dy, int mode)
11753 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
11754 boolean player_was_pushing = player->is_pushing;
11755 boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
11756 boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
11757 int jx = oldx, jy = oldy;
11758 int dx = x - jx, dy = y - jy;
11759 int nextx = x + dx, nexty = y + dy;
11760 int move_direction = (dx == -1 ? MV_LEFT :
11761 dx == +1 ? MV_RIGHT :
11763 dy == +1 ? MV_DOWN : MV_NONE);
11764 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
11765 int dig_side = MV_DIR_OPPOSITE(move_direction);
11766 int old_element = Feld[jx][jy];
11767 #if USE_FIXED_DONT_RUN_INTO
11768 int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
11774 if (is_player) /* function can also be called by EL_PENGUIN */
11776 if (player->MovPos == 0)
11778 player->is_digging = FALSE;
11779 player->is_collecting = FALSE;
11782 if (player->MovPos == 0) /* last pushing move finished */
11783 player->is_pushing = FALSE;
11785 if (mode == DF_NO_PUSH) /* player just stopped pushing */
11787 player->is_switching = FALSE;
11788 player->push_delay = -1;
11790 return MP_NO_ACTION;
11794 #if !USE_FIXED_DONT_RUN_INTO
11795 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
11796 return MP_NO_ACTION;
11799 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
11800 old_element = Back[jx][jy];
11802 /* in case of element dropped at player position, check background */
11803 else if (Back[jx][jy] != EL_EMPTY &&
11804 game.engine_version >= VERSION_IDENT(2,2,0,0))
11805 old_element = Back[jx][jy];
11807 /* checking here causes player to move into acid even if the current field
11808 cannot be left to that direction */
11810 #if USE_FIXED_DONT_RUN_INTO
11811 if (player_can_move && DONT_RUN_INTO(element))
11813 if (element == EL_ACID && dx == 0 && dy == 1)
11816 Feld[jx][jy] = EL_PLAYER_1;
11817 InitMovingField(jx, jy, MV_DOWN);
11818 Store[jx][jy] = EL_ACID;
11819 ContinueMoving(jx, jy);
11820 BuryPlayer(player);
11823 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
11825 return MP_DONT_RUN_INTO;
11830 #if 1 /* ------------------------------ NEW ------------------------------ */
11832 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
11833 return MP_NO_ACTION; /* field has no opening in this direction */
11835 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
11836 return MP_NO_ACTION; /* field has no opening in this direction */
11838 #if USE_FIXED_DONT_RUN_INTO
11839 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
11843 Feld[jx][jy] = player->artwork_element;
11845 Feld[jx][jy] = EL_PLAYER_1;
11847 InitMovingField(jx, jy, MV_DOWN);
11848 Store[jx][jy] = EL_ACID;
11849 ContinueMoving(jx, jy);
11850 BuryPlayer(player);
11852 return MP_DONT_RUN_INTO;
11856 #if USE_FIXED_DONT_RUN_INTO
11857 if (player_can_move && DONT_RUN_INTO(element))
11859 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
11861 return MP_DONT_RUN_INTO;
11865 #else /* ------------------------------ OLD ------------------------------ */
11868 #if USE_FIXED_DONT_RUN_INTO
11869 if (player_can_move && DONT_RUN_INTO(element))
11871 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
11873 return MP_DONT_RUN_INTO;
11878 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
11879 return MP_NO_ACTION; /* field has no opening in this direction */
11881 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
11882 return MP_NO_ACTION; /* field has no opening in this direction */
11884 /* checking here causes player to explode when moving into acid */
11886 #if USE_FIXED_DONT_RUN_INTO
11887 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
11890 Feld[jx][jy] = EL_PLAYER_1;
11891 InitMovingField(jx, jy, MV_DOWN);
11892 Store[jx][jy] = EL_ACID;
11893 ContinueMoving(jx, jy);
11894 BuryPlayer(player);
11896 return MP_DONT_RUN_INTO;
11901 #endif /* ------------------------------ END ------------------------------ */
11904 #if USE_FIXED_DONT_RUN_INTO
11905 if (player_can_move && DONT_RUN_INTO(element))
11907 if (element == EL_ACID && dx == 0 && dy == 1)
11910 Feld[jx][jy] = EL_PLAYER_1;
11911 InitMovingField(jx, jy, MV_DOWN);
11912 Store[jx][jy] = EL_ACID;
11913 ContinueMoving(jx, jy);
11914 BuryPlayer(player);
11917 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
11919 return MP_DONT_RUN_INTO;
11924 #if USE_FIXED_DONT_RUN_INTO
11925 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
11926 return MP_NO_ACTION;
11929 #if !USE_FIXED_DONT_RUN_INTO
11930 element = Feld[x][y];
11933 collect_count = element_info[element].collect_count_initial;
11935 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
11936 return MP_NO_ACTION;
11938 if (game.engine_version < VERSION_IDENT(2,2,0,0))
11939 player_can_move = player_can_move_or_snap;
11941 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
11942 game.engine_version >= VERSION_IDENT(2,2,0,0))
11944 CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
11945 player->index_bit, dig_side);
11946 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
11947 player->index_bit, dig_side);
11949 if (Feld[x][y] != element) /* field changed by snapping */
11952 return MP_NO_ACTION;
11955 #if USE_PLAYER_GRAVITY
11956 if (player->gravity && is_player && !player->is_auto_moving &&
11957 canFallDown(player) && move_direction != MV_DOWN &&
11958 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
11959 return MP_NO_ACTION; /* player cannot walk here due to gravity */
11961 if (game.gravity && is_player && !player->is_auto_moving &&
11962 canFallDown(player) && move_direction != MV_DOWN &&
11963 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
11964 return MP_NO_ACTION; /* player cannot walk here due to gravity */
11967 if (player_can_move &&
11968 IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
11970 int sound_element = SND_ELEMENT(element);
11971 int sound_action = ACTION_WALKING;
11973 if (IS_RND_GATE(element))
11975 if (!player->key[RND_GATE_NR(element)])
11976 return MP_NO_ACTION;
11978 else if (IS_RND_GATE_GRAY(element))
11980 if (!player->key[RND_GATE_GRAY_NR(element)])
11981 return MP_NO_ACTION;
11983 else if (IS_RND_GATE_GRAY_ACTIVE(element))
11985 if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
11986 return MP_NO_ACTION;
11988 else if (element == EL_EXIT_OPEN ||
11989 element == EL_SP_EXIT_OPEN ||
11990 element == EL_SP_EXIT_OPENING)
11992 sound_action = ACTION_PASSING; /* player is passing exit */
11994 else if (element == EL_EMPTY)
11996 sound_action = ACTION_MOVING; /* nothing to walk on */
11999 /* play sound from background or player, whatever is available */
12000 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
12001 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
12003 PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
12005 else if (player_can_move &&
12006 IS_PASSABLE(element) && canPassField(x, y, move_direction))
12008 if (!ACCESS_FROM(element, opposite_direction))
12009 return MP_NO_ACTION; /* field not accessible from this direction */
12011 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
12012 return MP_NO_ACTION;
12014 if (IS_EM_GATE(element))
12016 if (!player->key[EM_GATE_NR(element)])
12017 return MP_NO_ACTION;
12019 else if (IS_EM_GATE_GRAY(element))
12021 if (!player->key[EM_GATE_GRAY_NR(element)])
12022 return MP_NO_ACTION;
12024 else if (IS_EM_GATE_GRAY_ACTIVE(element))
12026 if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
12027 return MP_NO_ACTION;
12029 else if (IS_EMC_GATE(element))
12031 if (!player->key[EMC_GATE_NR(element)])
12032 return MP_NO_ACTION;
12034 else if (IS_EMC_GATE_GRAY(element))
12036 if (!player->key[EMC_GATE_GRAY_NR(element)])
12037 return MP_NO_ACTION;
12039 else if (IS_EMC_GATE_GRAY_ACTIVE(element))
12041 if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
12042 return MP_NO_ACTION;
12044 else if (IS_SP_PORT(element))
12046 if (element == EL_SP_GRAVITY_PORT_LEFT ||
12047 element == EL_SP_GRAVITY_PORT_RIGHT ||
12048 element == EL_SP_GRAVITY_PORT_UP ||
12049 element == EL_SP_GRAVITY_PORT_DOWN)
12050 #if USE_PLAYER_GRAVITY
12051 player->gravity = !player->gravity;
12053 game.gravity = !game.gravity;
12055 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
12056 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
12057 element == EL_SP_GRAVITY_ON_PORT_UP ||
12058 element == EL_SP_GRAVITY_ON_PORT_DOWN)
12059 #if USE_PLAYER_GRAVITY
12060 player->gravity = TRUE;
12062 game.gravity = TRUE;
12064 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
12065 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
12066 element == EL_SP_GRAVITY_OFF_PORT_UP ||
12067 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
12068 #if USE_PLAYER_GRAVITY
12069 player->gravity = FALSE;
12071 game.gravity = FALSE;
12075 /* automatically move to the next field with double speed */
12076 player->programmed_action = move_direction;
12078 if (player->move_delay_reset_counter == 0)
12080 player->move_delay_reset_counter = 2; /* two double speed steps */
12082 DOUBLE_PLAYER_SPEED(player);
12085 PlayLevelSoundAction(x, y, ACTION_PASSING);
12087 else if (player_can_move_or_snap && IS_DIGGABLE(element))
12091 if (mode != DF_SNAP)
12093 GfxElement[x][y] = GFX_ELEMENT(element);
12094 player->is_digging = TRUE;
12097 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
12099 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
12100 player->index_bit, dig_side);
12102 if (mode == DF_SNAP)
12104 #if USE_NEW_SNAP_DELAY
12105 if (level.block_snap_field)
12106 setFieldForSnapping(x, y, element, move_direction);
12108 TestIfElementTouchesCustomElement(x, y); /* for empty space */
12110 TestIfElementTouchesCustomElement(x, y); /* for empty space */
12113 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
12114 player->index_bit, dig_side);
12117 else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
12121 if (is_player && mode != DF_SNAP)
12123 GfxElement[x][y] = element;
12124 player->is_collecting = TRUE;
12127 if (element == EL_SPEED_PILL)
12129 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
12131 else if (element == EL_EXTRA_TIME && level.time > 0)
12133 TimeLeft += level.extra_time;
12134 DrawGameValue_Time(TimeLeft);
12136 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
12138 player->shield_normal_time_left += level.shield_normal_time;
12139 if (element == EL_SHIELD_DEADLY)
12140 player->shield_deadly_time_left += level.shield_deadly_time;
12142 else if (element == EL_DYNAMITE ||
12143 element == EL_EM_DYNAMITE ||
12144 element == EL_SP_DISK_RED)
12146 if (player->inventory_size < MAX_INVENTORY_SIZE)
12147 player->inventory_element[player->inventory_size++] = element;
12150 DrawGameDoorValues();
12152 DrawGameValue_Dynamite(local_player->inventory_size);
12155 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
12157 player->dynabomb_count++;
12158 player->dynabombs_left++;
12160 else if (element == EL_DYNABOMB_INCREASE_SIZE)
12162 player->dynabomb_size++;
12164 else if (element == EL_DYNABOMB_INCREASE_POWER)
12166 player->dynabomb_xl = TRUE;
12168 else if (IS_KEY(element))
12170 player->key[KEY_NR(element)] = TRUE;
12173 DrawGameDoorValues();
12175 DrawGameValue_Keys(player->key);
12178 redraw_mask |= REDRAW_DOOR_1;
12180 else if (IS_ENVELOPE(element))
12182 player->show_envelope = element;
12184 else if (element == EL_EMC_LENSES)
12186 game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
12188 RedrawAllInvisibleElementsForLenses();
12190 else if (element == EL_EMC_MAGNIFIER)
12192 game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
12194 RedrawAllInvisibleElementsForMagnifier();
12196 else if (IS_DROPPABLE(element) ||
12197 IS_THROWABLE(element)) /* can be collected and dropped */
12201 if (collect_count == 0)
12202 player->inventory_infinite_element = element;
12204 for (i = 0; i < collect_count; i++)
12205 if (player->inventory_size < MAX_INVENTORY_SIZE)
12206 player->inventory_element[player->inventory_size++] = element;
12209 DrawGameDoorValues();
12211 DrawGameValue_Dynamite(local_player->inventory_size);
12214 else if (collect_count > 0)
12216 local_player->gems_still_needed -= collect_count;
12217 if (local_player->gems_still_needed < 0)
12218 local_player->gems_still_needed = 0;
12220 DrawGameValue_Emeralds(local_player->gems_still_needed);
12223 RaiseScoreElement(element);
12224 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
12227 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
12228 player->index_bit, dig_side);
12230 if (mode == DF_SNAP)
12232 #if USE_NEW_SNAP_DELAY
12233 if (level.block_snap_field)
12234 setFieldForSnapping(x, y, element, move_direction);
12236 TestIfElementTouchesCustomElement(x, y); /* for empty space */
12238 TestIfElementTouchesCustomElement(x, y); /* for empty space */
12241 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
12242 player->index_bit, dig_side);
12245 else if (player_can_move_or_snap && IS_PUSHABLE(element))
12247 if (mode == DF_SNAP && element != EL_BD_ROCK)
12248 return MP_NO_ACTION;
12250 if (CAN_FALL(element) && dy)
12251 return MP_NO_ACTION;
12253 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
12254 !(element == EL_SPRING && level.use_spring_bug))
12255 return MP_NO_ACTION;
12257 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
12258 ((move_direction & MV_VERTICAL &&
12259 ((element_info[element].move_pattern & MV_LEFT &&
12260 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
12261 (element_info[element].move_pattern & MV_RIGHT &&
12262 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
12263 (move_direction & MV_HORIZONTAL &&
12264 ((element_info[element].move_pattern & MV_UP &&
12265 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
12266 (element_info[element].move_pattern & MV_DOWN &&
12267 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
12268 return MP_NO_ACTION;
12270 /* do not push elements already moving away faster than player */
12271 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
12272 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
12273 return MP_NO_ACTION;
12275 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
12277 if (player->push_delay_value == -1 || !player_was_pushing)
12278 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12280 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
12282 if (player->push_delay_value == -1)
12283 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12285 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
12287 if (!player->is_pushing)
12288 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12291 player->is_pushing = TRUE;
12292 player->is_active = TRUE;
12294 if (!(IN_LEV_FIELD(nextx, nexty) &&
12295 (IS_FREE(nextx, nexty) ||
12296 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
12297 IS_SB_ELEMENT(element)))))
12298 return MP_NO_ACTION;
12300 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
12301 return MP_NO_ACTION;
12303 if (player->push_delay == -1) /* new pushing; restart delay */
12304 player->push_delay = 0;
12306 if (player->push_delay < player->push_delay_value &&
12307 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
12308 element != EL_SPRING && element != EL_BALLOON)
12310 /* make sure that there is no move delay before next try to push */
12311 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
12312 player->move_delay = 0;
12314 return MP_NO_ACTION;
12317 if (IS_SB_ELEMENT(element))
12319 if (element == EL_SOKOBAN_FIELD_FULL)
12321 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
12322 local_player->sokobanfields_still_needed++;
12325 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
12327 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
12328 local_player->sokobanfields_still_needed--;
12331 Feld[x][y] = EL_SOKOBAN_OBJECT;
12333 if (Back[x][y] == Back[nextx][nexty])
12334 PlayLevelSoundAction(x, y, ACTION_PUSHING);
12335 else if (Back[x][y] != 0)
12336 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
12339 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
12342 if (local_player->sokobanfields_still_needed == 0 &&
12343 game.emulation == EMU_SOKOBAN)
12345 player->LevelSolved = player->GameOver = TRUE;
12346 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
12350 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12352 InitMovingField(x, y, move_direction);
12353 GfxAction[x][y] = ACTION_PUSHING;
12355 if (mode == DF_SNAP)
12356 ContinueMoving(x, y);
12358 MovPos[x][y] = (dx != 0 ? dx : dy);
12360 Pushed[x][y] = TRUE;
12361 Pushed[nextx][nexty] = TRUE;
12363 if (game.engine_version < VERSION_IDENT(2,2,0,7))
12364 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12366 player->push_delay_value = -1; /* get new value later */
12368 /* check for element change _after_ element has been pushed */
12369 if (game.use_change_when_pushing_bug)
12371 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
12372 player->index_bit, dig_side);
12373 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
12374 player->index_bit, dig_side);
12377 else if (IS_SWITCHABLE(element))
12379 if (PLAYER_SWITCHING(player, x, y))
12381 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
12382 player->index_bit, dig_side);
12387 player->is_switching = TRUE;
12388 player->switch_x = x;
12389 player->switch_y = y;
12391 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
12393 if (element == EL_ROBOT_WHEEL)
12395 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
12399 DrawLevelField(x, y);
12401 else if (element == EL_SP_TERMINAL)
12406 SCAN_PLAYFIELD(xx, yy)
12408 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
12411 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
12413 else if (Feld[xx][yy] == EL_SP_TERMINAL)
12414 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
12417 else if (IS_BELT_SWITCH(element))
12419 ToggleBeltSwitch(x, y);
12421 else if (element == EL_SWITCHGATE_SWITCH_UP ||
12422 element == EL_SWITCHGATE_SWITCH_DOWN)
12424 ToggleSwitchgateSwitch(x, y);
12426 else if (element == EL_LIGHT_SWITCH ||
12427 element == EL_LIGHT_SWITCH_ACTIVE)
12429 ToggleLightSwitch(x, y);
12431 else if (element == EL_TIMEGATE_SWITCH)
12433 ActivateTimegateSwitch(x, y);
12435 else if (element == EL_BALLOON_SWITCH_LEFT ||
12436 element == EL_BALLOON_SWITCH_RIGHT ||
12437 element == EL_BALLOON_SWITCH_UP ||
12438 element == EL_BALLOON_SWITCH_DOWN ||
12439 element == EL_BALLOON_SWITCH_NONE ||
12440 element == EL_BALLOON_SWITCH_ANY)
12442 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
12443 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
12444 element == EL_BALLOON_SWITCH_UP ? MV_UP :
12445 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
12446 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
12449 else if (element == EL_LAMP)
12451 Feld[x][y] = EL_LAMP_ACTIVE;
12452 local_player->lights_still_needed--;
12454 ResetGfxAnimation(x, y);
12455 DrawLevelField(x, y);
12457 else if (element == EL_TIME_ORB_FULL)
12459 Feld[x][y] = EL_TIME_ORB_EMPTY;
12461 if (level.time > 0 || level.use_time_orb_bug)
12463 TimeLeft += level.time_orb_time;
12464 DrawGameValue_Time(TimeLeft);
12467 ResetGfxAnimation(x, y);
12468 DrawLevelField(x, y);
12470 else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
12471 element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
12475 game.ball_state = !game.ball_state;
12478 SCAN_PLAYFIELD(xx, yy)
12480 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
12483 int e = Feld[xx][yy];
12485 if (game.ball_state)
12487 if (e == EL_EMC_MAGIC_BALL)
12488 CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
12489 else if (e == EL_EMC_MAGIC_BALL_SWITCH)
12490 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
12494 if (e == EL_EMC_MAGIC_BALL_ACTIVE)
12495 CreateField(xx, yy, EL_EMC_MAGIC_BALL);
12496 else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
12497 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
12502 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
12503 player->index_bit, dig_side);
12505 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
12506 player->index_bit, dig_side);
12508 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
12509 player->index_bit, dig_side);
12515 if (!PLAYER_SWITCHING(player, x, y))
12517 player->is_switching = TRUE;
12518 player->switch_x = x;
12519 player->switch_y = y;
12521 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
12522 player->index_bit, dig_side);
12523 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
12524 player->index_bit, dig_side);
12526 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
12527 player->index_bit, dig_side);
12528 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
12529 player->index_bit, dig_side);
12532 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
12533 player->index_bit, dig_side);
12534 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
12535 player->index_bit, dig_side);
12537 return MP_NO_ACTION;
12540 player->push_delay = -1;
12542 if (is_player) /* function can also be called by EL_PENGUIN */
12544 if (Feld[x][y] != element) /* really digged/collected something */
12546 player->is_collecting = !player->is_digging;
12547 player->is_active = TRUE;
12554 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
12556 int jx = player->jx, jy = player->jy;
12557 int x = jx + dx, y = jy + dy;
12558 int snap_direction = (dx == -1 ? MV_LEFT :
12559 dx == +1 ? MV_RIGHT :
12561 dy == +1 ? MV_DOWN : MV_NONE);
12562 boolean can_continue_snapping = (level.continuous_snapping &&
12563 WasJustFalling[x][y] < CHECK_DELAY_FALLING);
12565 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
12568 if (!player->active || !IN_LEV_FIELD(x, y))
12576 if (player->MovPos == 0)
12577 player->is_pushing = FALSE;
12579 player->is_snapping = FALSE;
12581 if (player->MovPos == 0)
12583 player->is_moving = FALSE;
12584 player->is_digging = FALSE;
12585 player->is_collecting = FALSE;
12591 #if USE_NEW_CONTINUOUS_SNAPPING
12592 /* prevent snapping with already pressed snap key when not allowed */
12593 if (player->is_snapping && !can_continue_snapping)
12596 if (player->is_snapping)
12600 player->MovDir = snap_direction;
12602 if (player->MovPos == 0)
12604 player->is_moving = FALSE;
12605 player->is_digging = FALSE;
12606 player->is_collecting = FALSE;
12609 player->is_dropping = FALSE;
12610 player->is_dropping_pressed = FALSE;
12611 player->drop_pressed_delay = 0;
12613 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
12616 player->is_snapping = TRUE;
12617 player->is_active = TRUE;
12619 if (player->MovPos == 0)
12621 player->is_moving = FALSE;
12622 player->is_digging = FALSE;
12623 player->is_collecting = FALSE;
12626 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
12627 DrawLevelField(player->last_jx, player->last_jy);
12629 DrawLevelField(x, y);
12634 boolean DropElement(struct PlayerInfo *player)
12636 int old_element, new_element;
12637 int dropx = player->jx, dropy = player->jy;
12638 int drop_direction = player->MovDir;
12639 int drop_side = drop_direction;
12640 int drop_element = (player->inventory_size > 0 ?
12641 player->inventory_element[player->inventory_size - 1] :
12642 player->inventory_infinite_element != EL_UNDEFINED ?
12643 player->inventory_infinite_element :
12644 player->dynabombs_left > 0 ?
12645 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
12648 player->is_dropping_pressed = TRUE;
12650 /* do not drop an element on top of another element; when holding drop key
12651 pressed without moving, dropped element must move away before the next
12652 element can be dropped (this is especially important if the next element
12653 is dynamite, which can be placed on background for historical reasons) */
12654 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
12657 if (IS_THROWABLE(drop_element))
12659 dropx += GET_DX_FROM_DIR(drop_direction);
12660 dropy += GET_DY_FROM_DIR(drop_direction);
12662 if (!IN_LEV_FIELD(dropx, dropy))
12666 old_element = Feld[dropx][dropy]; /* old element at dropping position */
12667 new_element = drop_element; /* default: no change when dropping */
12669 /* check if player is active, not moving and ready to drop */
12670 if (!player->active || player->MovPos || player->drop_delay > 0)
12673 /* check if player has anything that can be dropped */
12674 if (new_element == EL_UNDEFINED)
12677 /* check if drop key was pressed long enough for EM style dynamite */
12678 if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
12681 /* check if anything can be dropped at the current position */
12682 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
12685 /* collected custom elements can only be dropped on empty fields */
12686 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
12689 if (old_element != EL_EMPTY)
12690 Back[dropx][dropy] = old_element; /* store old element on this field */
12692 ResetGfxAnimation(dropx, dropy);
12693 ResetRandomAnimationValue(dropx, dropy);
12695 if (player->inventory_size > 0 ||
12696 player->inventory_infinite_element != EL_UNDEFINED)
12698 if (player->inventory_size > 0)
12700 player->inventory_size--;
12703 DrawGameDoorValues();
12705 DrawGameValue_Dynamite(local_player->inventory_size);
12708 if (new_element == EL_DYNAMITE)
12709 new_element = EL_DYNAMITE_ACTIVE;
12710 else if (new_element == EL_EM_DYNAMITE)
12711 new_element = EL_EM_DYNAMITE_ACTIVE;
12712 else if (new_element == EL_SP_DISK_RED)
12713 new_element = EL_SP_DISK_RED_ACTIVE;
12716 Feld[dropx][dropy] = new_element;
12718 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
12719 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
12720 el2img(Feld[dropx][dropy]), 0);
12722 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
12724 /* needed if previous element just changed to "empty" in the last frame */
12725 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
12727 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
12728 player->index_bit, drop_side);
12729 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
12731 player->index_bit, drop_side);
12733 TestIfElementTouchesCustomElement(dropx, dropy);
12735 else /* player is dropping a dyna bomb */
12737 player->dynabombs_left--;
12739 Feld[dropx][dropy] = new_element;
12741 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
12742 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
12743 el2img(Feld[dropx][dropy]), 0);
12745 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
12748 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
12749 InitField_WithBug1(dropx, dropy, FALSE);
12751 new_element = Feld[dropx][dropy]; /* element might have changed */
12753 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
12754 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
12756 int move_direction, nextx, nexty;
12758 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
12759 MovDir[dropx][dropy] = drop_direction;
12761 move_direction = MovDir[dropx][dropy];
12762 nextx = dropx + GET_DX_FROM_DIR(move_direction);
12763 nexty = dropy + GET_DY_FROM_DIR(move_direction);
12765 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
12766 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
12769 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
12770 player->is_dropping = TRUE;
12772 player->drop_pressed_delay = 0;
12773 player->is_dropping_pressed = FALSE;
12775 player->drop_x = dropx;
12776 player->drop_y = dropy;
12781 /* ------------------------------------------------------------------------- */
12782 /* game sound playing functions */
12783 /* ------------------------------------------------------------------------- */
12785 static int *loop_sound_frame = NULL;
12786 static int *loop_sound_volume = NULL;
12788 void InitPlayLevelSound()
12790 int num_sounds = getSoundListSize();
12792 checked_free(loop_sound_frame);
12793 checked_free(loop_sound_volume);
12795 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
12796 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
12799 static void PlayLevelSound(int x, int y, int nr)
12801 int sx = SCREENX(x), sy = SCREENY(y);
12802 int volume, stereo_position;
12803 int max_distance = 8;
12804 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
12806 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
12807 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
12810 if (!IN_LEV_FIELD(x, y) ||
12811 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
12812 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
12815 volume = SOUND_MAX_VOLUME;
12817 if (!IN_SCR_FIELD(sx, sy))
12819 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
12820 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
12822 volume -= volume * (dx > dy ? dx : dy) / max_distance;
12825 stereo_position = (SOUND_MAX_LEFT +
12826 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
12827 (SCR_FIELDX + 2 * max_distance));
12829 if (IS_LOOP_SOUND(nr))
12831 /* This assures that quieter loop sounds do not overwrite louder ones,
12832 while restarting sound volume comparison with each new game frame. */
12834 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
12837 loop_sound_volume[nr] = volume;
12838 loop_sound_frame[nr] = FrameCounter;
12841 PlaySoundExt(nr, volume, stereo_position, type);
12844 static void PlayLevelSoundNearest(int x, int y, int sound_action)
12846 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
12847 x > LEVELX(BX2) ? LEVELX(BX2) : x,
12848 y < LEVELY(BY1) ? LEVELY(BY1) :
12849 y > LEVELY(BY2) ? LEVELY(BY2) : y,
12853 static void PlayLevelSoundAction(int x, int y, int action)
12855 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
12858 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
12860 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
12862 if (sound_effect != SND_UNDEFINED)
12863 PlayLevelSound(x, y, sound_effect);
12866 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
12869 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
12871 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12872 PlayLevelSound(x, y, sound_effect);
12875 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
12877 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
12879 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12880 PlayLevelSound(x, y, sound_effect);
12883 static void StopLevelSoundActionIfLoop(int x, int y, int action)
12885 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
12887 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12888 StopSound(sound_effect);
12891 static void PlayLevelMusic()
12893 if (levelset.music[level_nr] != MUS_UNDEFINED)
12894 PlayMusic(levelset.music[level_nr]); /* from config file */
12896 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
12899 void PlayLevelSound_EM(int x, int y, int element_em, int sample)
12901 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
12906 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
12910 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12914 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12918 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12922 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
12926 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12930 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12933 case SAMPLE_android_clone:
12934 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12937 case SAMPLE_android_move:
12938 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12941 case SAMPLE_spring:
12942 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12946 PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
12950 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
12953 case SAMPLE_eater_eat:
12954 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
12958 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12961 case SAMPLE_collect:
12962 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
12965 case SAMPLE_diamond:
12966 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12969 case SAMPLE_squash:
12970 /* !!! CHECK THIS !!! */
12972 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
12974 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
12978 case SAMPLE_wonderfall:
12979 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
12983 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12987 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12991 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
12995 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
12999 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
13003 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
13006 case SAMPLE_wonder:
13007 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
13011 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
13014 case SAMPLE_exit_open:
13015 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
13018 case SAMPLE_exit_leave:
13019 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
13022 case SAMPLE_dynamite:
13023 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
13027 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
13031 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
13035 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
13039 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
13043 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
13047 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
13051 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
13056 void RaiseScore(int value)
13058 local_player->score += value;
13060 DrawGameValue_Score(local_player->score);
13063 void RaiseScoreElement(int element)
13068 case EL_BD_DIAMOND:
13069 case EL_EMERALD_YELLOW:
13070 case EL_EMERALD_RED:
13071 case EL_EMERALD_PURPLE:
13072 case EL_SP_INFOTRON:
13073 RaiseScore(level.score[SC_EMERALD]);
13076 RaiseScore(level.score[SC_DIAMOND]);
13079 RaiseScore(level.score[SC_CRYSTAL]);
13082 RaiseScore(level.score[SC_PEARL]);
13085 case EL_BD_BUTTERFLY:
13086 case EL_SP_ELECTRON:
13087 RaiseScore(level.score[SC_BUG]);
13090 case EL_BD_FIREFLY:
13091 case EL_SP_SNIKSNAK:
13092 RaiseScore(level.score[SC_SPACESHIP]);
13095 case EL_DARK_YAMYAM:
13096 RaiseScore(level.score[SC_YAMYAM]);
13099 RaiseScore(level.score[SC_ROBOT]);
13102 RaiseScore(level.score[SC_PACMAN]);
13105 RaiseScore(level.score[SC_NUT]);
13108 case EL_EM_DYNAMITE:
13109 case EL_SP_DISK_RED:
13110 case EL_DYNABOMB_INCREASE_NUMBER:
13111 case EL_DYNABOMB_INCREASE_SIZE:
13112 case EL_DYNABOMB_INCREASE_POWER:
13113 RaiseScore(level.score[SC_DYNAMITE]);
13115 case EL_SHIELD_NORMAL:
13116 case EL_SHIELD_DEADLY:
13117 RaiseScore(level.score[SC_SHIELD]);
13119 case EL_EXTRA_TIME:
13120 RaiseScore(level.extra_time_score);
13134 RaiseScore(level.score[SC_KEY]);
13137 RaiseScore(element_info[element].collect_score);
13142 void RequestQuitGame(boolean ask_if_really_quit)
13144 if (AllPlayersGone ||
13145 !ask_if_really_quit ||
13146 level_editor_test_game ||
13147 Request("Do you really want to quit the game ?",
13148 REQ_ASK | REQ_STAY_CLOSED))
13150 #if defined(NETWORK_AVALIABLE)
13151 if (options.network)
13152 SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
13156 game_status = GAME_MODE_MAIN;
13162 if (tape.playing && tape.deactivate_display)
13163 TapeDeactivateDisplayOff(TRUE);
13165 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
13167 if (tape.playing && tape.deactivate_display)
13168 TapeDeactivateDisplayOn();
13173 /* ---------- new game button stuff ---------------------------------------- */
13175 /* graphic position values for game buttons */
13176 #define GAME_BUTTON_XSIZE 30
13177 #define GAME_BUTTON_YSIZE 30
13178 #define GAME_BUTTON_XPOS 5
13179 #define GAME_BUTTON_YPOS 215
13180 #define SOUND_BUTTON_XPOS 5
13181 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
13183 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
13184 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
13185 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
13186 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
13187 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
13188 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
13195 } gamebutton_info[NUM_GAME_BUTTONS] =
13198 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
13203 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
13204 GAME_CTRL_ID_PAUSE,
13208 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
13213 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
13214 SOUND_CTRL_ID_MUSIC,
13215 "background music on/off"
13218 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
13219 SOUND_CTRL_ID_LOOPS,
13220 "sound loops on/off"
13223 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
13224 SOUND_CTRL_ID_SIMPLE,
13225 "normal sounds on/off"
13229 void CreateGameButtons()
13233 for (i = 0; i < NUM_GAME_BUTTONS; i++)
13235 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
13236 struct GadgetInfo *gi;
13239 unsigned long event_mask;
13240 int gd_xoffset, gd_yoffset;
13241 int gd_x1, gd_x2, gd_y1, gd_y2;
13244 gd_xoffset = gamebutton_info[i].x;
13245 gd_yoffset = gamebutton_info[i].y;
13246 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
13247 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
13249 if (id == GAME_CTRL_ID_STOP ||
13250 id == GAME_CTRL_ID_PAUSE ||
13251 id == GAME_CTRL_ID_PLAY)
13253 button_type = GD_TYPE_NORMAL_BUTTON;
13255 event_mask = GD_EVENT_RELEASED;
13256 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
13257 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
13261 button_type = GD_TYPE_CHECK_BUTTON;
13263 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
13264 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
13265 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
13266 event_mask = GD_EVENT_PRESSED;
13267 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
13268 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
13271 gi = CreateGadget(GDI_CUSTOM_ID, id,
13272 GDI_INFO_TEXT, gamebutton_info[i].infotext,
13273 GDI_X, DX + gd_xoffset,
13274 GDI_Y, DY + gd_yoffset,
13275 GDI_WIDTH, GAME_BUTTON_XSIZE,
13276 GDI_HEIGHT, GAME_BUTTON_YSIZE,
13277 GDI_TYPE, button_type,
13278 GDI_STATE, GD_BUTTON_UNPRESSED,
13279 GDI_CHECKED, checked,
13280 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
13281 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
13282 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
13283 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
13284 GDI_EVENT_MASK, event_mask,
13285 GDI_CALLBACK_ACTION, HandleGameButtons,
13289 Error(ERR_EXIT, "cannot create gadget");
13291 game_gadget[id] = gi;
13295 void FreeGameButtons()
13299 for (i = 0; i < NUM_GAME_BUTTONS; i++)
13300 FreeGadget(game_gadget[i]);
13303 static void MapGameButtons()
13307 for (i = 0; i < NUM_GAME_BUTTONS; i++)
13308 MapGadget(game_gadget[i]);
13311 void UnmapGameButtons()
13315 for (i = 0; i < NUM_GAME_BUTTONS; i++)
13316 UnmapGadget(game_gadget[i]);
13319 static void HandleGameButtons(struct GadgetInfo *gi)
13321 int id = gi->custom_id;
13323 if (game_status != GAME_MODE_PLAYING)
13328 case GAME_CTRL_ID_STOP:
13332 RequestQuitGame(TRUE);
13335 case GAME_CTRL_ID_PAUSE:
13336 if (options.network)
13338 #if defined(NETWORK_AVALIABLE)
13340 SendToServer_ContinuePlaying();
13342 SendToServer_PausePlaying();
13346 TapeTogglePause(TAPE_TOGGLE_MANUAL);
13349 case GAME_CTRL_ID_PLAY:
13352 #if defined(NETWORK_AVALIABLE)
13353 if (options.network)
13354 SendToServer_ContinuePlaying();
13358 tape.pausing = FALSE;
13359 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
13364 case SOUND_CTRL_ID_MUSIC:
13365 if (setup.sound_music)
13367 setup.sound_music = FALSE;
13370 else if (audio.music_available)
13372 setup.sound = setup.sound_music = TRUE;
13374 SetAudioMode(setup.sound);
13380 case SOUND_CTRL_ID_LOOPS:
13381 if (setup.sound_loops)
13382 setup.sound_loops = FALSE;
13383 else if (audio.loops_available)
13385 setup.sound = setup.sound_loops = TRUE;
13386 SetAudioMode(setup.sound);
13390 case SOUND_CTRL_ID_SIMPLE:
13391 if (setup.sound_simple)
13392 setup.sound_simple = FALSE;
13393 else if (audio.sound_available)
13395 setup.sound = setup.sound_simple = TRUE;
13396 SetAudioMode(setup.sound);