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)
46 #define USE_QUICKSAND_IMPACT_BUGFIX (USE_NEW_STUFF * 0)
53 /* for MovePlayer() */
54 #define MP_NO_ACTION 0
57 #define MP_DONT_RUN_INTO (MP_MOVING | MP_ACTION)
59 /* for ScrollPlayer() */
61 #define SCROLL_GO_ON 1
63 /* for Bang()/Explode() */
64 #define EX_PHASE_START 0
65 #define EX_TYPE_NONE 0
66 #define EX_TYPE_NORMAL (1 << 0)
67 #define EX_TYPE_CENTER (1 << 1)
68 #define EX_TYPE_BORDER (1 << 2)
69 #define EX_TYPE_CROSS (1 << 3)
70 #define EX_TYPE_DYNA (1 << 4)
71 #define EX_TYPE_SINGLE_TILE (EX_TYPE_CENTER | EX_TYPE_BORDER)
73 /* special positions in the game control window (relative to control window) */
76 #define XX_EMERALDS 29
77 #define YY_EMERALDS 54
78 #define XX_DYNAMITE 29
79 #define YY_DYNAMITE 89
88 /* special positions in the game control window (relative to main window) */
89 #define DX_LEVEL (DX + XX_LEVEL)
90 #define DY_LEVEL (DY + YY_LEVEL)
91 #define DX_EMERALDS (DX + XX_EMERALDS)
92 #define DY_EMERALDS (DY + YY_EMERALDS)
93 #define DX_DYNAMITE (DX + XX_DYNAMITE)
94 #define DY_DYNAMITE (DY + YY_DYNAMITE)
95 #define DX_KEYS (DX + XX_KEYS)
96 #define DY_KEYS (DY + YY_KEYS)
97 #define DX_SCORE (DX + XX_SCORE)
98 #define DY_SCORE (DY + YY_SCORE)
99 #define DX_TIME1 (DX + XX_TIME1)
100 #define DX_TIME2 (DX + XX_TIME2)
101 #define DY_TIME (DY + YY_TIME)
103 /* values for delayed check of falling and moving elements and for collision */
104 #define CHECK_DELAY_MOVING 3
105 #define CHECK_DELAY_FALLING 3
106 #define CHECK_DELAY_COLLISION 2
108 /* values for initial player move delay (initial delay counter value) */
109 #define INITIAL_MOVE_DELAY_OFF -1
110 #define INITIAL_MOVE_DELAY_ON 0
112 /* values for player movement speed (which is in fact a delay value) */
113 #define MOVE_DELAY_MIN_SPEED 32
114 #define MOVE_DELAY_NORMAL_SPEED 8
115 #define MOVE_DELAY_HIGH_SPEED 4
116 #define MOVE_DELAY_MAX_SPEED 1
119 #define DOUBLE_MOVE_DELAY(x) (x = (x <= MOVE_DELAY_HIGH_SPEED ? x * 2 : x))
120 #define HALVE_MOVE_DELAY(x) (x = (x >= MOVE_DELAY_HIGH_SPEED ? x / 2 : x))
122 #define DOUBLE_MOVE_DELAY(x) (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
123 #define HALVE_MOVE_DELAY(x) (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
125 #define DOUBLE_PLAYER_SPEED(p) (HALVE_MOVE_DELAY((p)->move_delay_value))
126 #define HALVE_PLAYER_SPEED(p) (DOUBLE_MOVE_DELAY((p)->move_delay_value))
128 /* values for other actions */
129 #define MOVE_STEPSIZE_NORMAL (TILEX / MOVE_DELAY_NORMAL_SPEED)
130 #define MOVE_STEPSIZE_MIN (1)
131 #define MOVE_STEPSIZE_MAX (TILEX)
133 #define GET_DX_FROM_DIR(d) ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
134 #define GET_DY_FROM_DIR(d) ((d) == MV_UP ? -1 : (d) == MV_DOWN ? 1 : 0)
136 #define INIT_GFX_RANDOM() (SimpleRND(1000000))
138 #define GET_NEW_PUSH_DELAY(e) ( (element_info[e].push_delay_fixed) + \
139 RND(element_info[e].push_delay_random))
140 #define GET_NEW_DROP_DELAY(e) ( (element_info[e].drop_delay_fixed) + \
141 RND(element_info[e].drop_delay_random))
142 #define GET_NEW_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
143 RND(element_info[e].move_delay_random))
144 #define GET_MAX_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
145 (element_info[e].move_delay_random))
146 #define GET_NEW_CE_VALUE(e) ( (element_info[e].ce_value_fixed_initial) +\
147 RND(element_info[e].ce_value_random_initial))
148 #define GET_CE_SCORE(e) ( (element_info[e].collect_score))
149 #define GET_CHANGE_DELAY(c) ( ((c)->delay_fixed * (c)->delay_frames) + \
150 RND((c)->delay_random * (c)->delay_frames))
151 #define GET_CE_DELAY_VALUE(c) ( ((c)->delay_fixed) + \
152 RND((c)->delay_random))
156 #define GET_VALID_RUNTIME_ELEMENT(e) \
157 ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
159 #define GET_VALID_FILE_ELEMENT(e) \
160 ((e) >= NUM_FILE_ELEMENTS ? EL_UNKNOWN : (e))
163 #define GET_TARGET_ELEMENT(e, ch, cv, cs) \
164 ((e) == EL_TRIGGER_PLAYER ? (ch)->actual_trigger_player : \
165 (e) == EL_TRIGGER_ELEMENT ? (ch)->actual_trigger_element : \
166 (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value : \
167 (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score : \
168 (e) == EL_CURRENT_CE_VALUE ? (cv) : \
169 (e) == EL_CURRENT_CE_SCORE ? (cs) : (e))
171 #define CAN_GROW_INTO(e) \
172 ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
174 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition) \
175 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
178 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition) \
179 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
180 (CAN_MOVE_INTO_ACID(e) && \
181 Feld[x][y] == EL_ACID) || \
184 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition) \
185 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
186 (CAN_MOVE_INTO_ACID(e) && \
187 Feld[x][y] == EL_ACID) || \
190 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition) \
191 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
193 (CAN_MOVE_INTO_ACID(e) && \
194 Feld[x][y] == EL_ACID) || \
195 (DONT_COLLIDE_WITH(e) && \
197 !PLAYER_ENEMY_PROTECTED(x, y))))
199 #define ELEMENT_CAN_ENTER_FIELD(e, x, y) \
200 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
202 #define SATELLITE_CAN_ENTER_FIELD(x, y) \
203 ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
205 #define ANDROID_CAN_ENTER_FIELD(e, x, y) \
206 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
208 #define ANDROID_CAN_CLONE_FIELD(x, y) \
209 (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
210 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
212 #define ENEMY_CAN_ENTER_FIELD(e, x, y) \
213 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
215 #define YAMYAM_CAN_ENTER_FIELD(e, x, y) \
216 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
218 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y) \
219 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
221 #define PACMAN_CAN_ENTER_FIELD(e, x, y) \
222 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
224 #define PIG_CAN_ENTER_FIELD(e, x, y) \
225 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
227 #define PENGUIN_CAN_ENTER_FIELD(e, x, y) \
228 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN ||\
229 IS_FOOD_PENGUIN(Feld[x][y])))
230 #define DRAGON_CAN_ENTER_FIELD(e, x, y) \
231 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
233 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition) \
234 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
236 #define SPRING_CAN_ENTER_FIELD(e, x, y) \
237 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
239 #define SPRING_CAN_BUMP_FROM_FIELD(x, y) \
240 (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER || \
241 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
244 #define GROUP_NR(e) ((e) - EL_GROUP_START)
245 #define IS_IN_GROUP(e, nr) (element_info[e].in_group[nr] == TRUE)
246 #define IS_IN_GROUP_EL(e, ge) (IS_IN_GROUP(e, (ge) - EL_GROUP_START))
248 #define IS_EQUAL_OR_IN_GROUP(e, ge) \
249 (IS_GROUP_ELEMENT(ge) ? IS_IN_GROUP(e, GROUP_NR(ge)) : (e) == (ge))
252 #define MOVE_ENTER_EL(e) (element_info[e].move_enter_element)
254 #define CE_ENTER_FIELD_COND(e, x, y) \
255 (!IS_PLAYER(x, y) && \
256 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
258 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y) \
259 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
261 #define IN_LEV_FIELD_AND_IS_FREE(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
262 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
264 #define ACCESS_FROM(e, d) (element_info[e].access_direction &(d))
265 #define IS_WALKABLE_FROM(e, d) (IS_WALKABLE(e) && ACCESS_FROM(e, d))
266 #define IS_PASSABLE_FROM(e, d) (IS_PASSABLE(e) && ACCESS_FROM(e, d))
267 #define IS_ACCESSIBLE_FROM(e, d) (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
269 /* game button identifiers */
270 #define GAME_CTRL_ID_STOP 0
271 #define GAME_CTRL_ID_PAUSE 1
272 #define GAME_CTRL_ID_PLAY 2
273 #define SOUND_CTRL_ID_MUSIC 3
274 #define SOUND_CTRL_ID_LOOPS 4
275 #define SOUND_CTRL_ID_SIMPLE 5
277 #define NUM_GAME_BUTTONS 6
280 /* forward declaration for internal use */
282 static void CreateField(int, int, int);
284 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
285 static void AdvanceFrameAndPlayerCounters(int);
287 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
288 static boolean MovePlayer(struct PlayerInfo *, int, int);
289 static void ScrollPlayer(struct PlayerInfo *, int);
290 static void ScrollScreen(struct PlayerInfo *, int);
292 int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
294 static void InitBeltMovement(void);
295 static void CloseAllOpenTimegates(void);
296 static void CheckGravityMovement(struct PlayerInfo *);
297 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
298 static void KillPlayerUnlessEnemyProtected(int, int);
299 static void KillPlayerUnlessExplosionProtected(int, int);
301 static void TestIfPlayerTouchesCustomElement(int, int);
302 static void TestIfElementTouchesCustomElement(int, int);
303 static void TestIfElementHitsCustomElement(int, int, int);
305 static void TestIfElementSmashesCustomElement(int, int, int);
308 static void HandleElementChange(int, int, int);
309 static void ExecuteCustomElementAction(int, int, int, int);
310 static boolean ChangeElement(int, int, int, int);
312 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
313 #define CheckTriggeredElementChange(x, y, e, ev) \
314 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
315 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s) \
316 CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
317 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s) \
318 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
319 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p) \
320 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
322 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
323 #define CheckElementChange(x, y, e, te, ev) \
324 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
325 #define CheckElementChangeByPlayer(x, y, e, ev, p, s) \
326 CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
327 #define CheckElementChangeBySide(x, y, e, te, ev, s) \
328 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
330 static void PlayLevelSound(int, int, int);
331 static void PlayLevelSoundNearest(int, int, int);
332 static void PlayLevelSoundAction(int, int, int);
333 static void PlayLevelSoundElementAction(int, int, int, int);
334 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
335 static void PlayLevelSoundActionIfLoop(int, int, int);
336 static void StopLevelSoundActionIfLoop(int, int, int);
337 static void PlayLevelMusic();
339 static void MapGameButtons();
340 static void HandleGameButtons(struct GadgetInfo *);
342 int AmoebeNachbarNr(int, int);
343 void AmoebeUmwandeln(int, int);
344 void ContinueMoving(int, int);
346 void InitMovDir(int, int);
347 void InitAmoebaNr(int, int);
348 int NewHiScore(void);
350 void TestIfGoodThingHitsBadThing(int, int, int);
351 void TestIfBadThingHitsGoodThing(int, int, int);
352 void TestIfPlayerTouchesBadThing(int, int);
353 void TestIfPlayerRunsIntoBadThing(int, int, int);
354 void TestIfBadThingTouchesPlayer(int, int);
355 void TestIfBadThingRunsIntoPlayer(int, int, int);
356 void TestIfFriendTouchesBadThing(int, int);
357 void TestIfBadThingTouchesFriend(int, int);
358 void TestIfBadThingTouchesOtherBadThing(int, int);
360 void KillPlayer(struct PlayerInfo *);
361 void BuryPlayer(struct PlayerInfo *);
362 void RemovePlayer(struct PlayerInfo *);
364 boolean SnapField(struct PlayerInfo *, int, int);
365 boolean DropElement(struct PlayerInfo *);
368 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
371 /* ------------------------------------------------------------------------- */
372 /* definition of elements that automatically change to other elements after */
373 /* a specified time, eventually calling a function when changing */
374 /* ------------------------------------------------------------------------- */
376 /* forward declaration for changer functions */
377 static void InitBuggyBase(int, int);
378 static void WarnBuggyBase(int, int);
380 static void InitTrap(int, int);
381 static void ActivateTrap(int, int);
382 static void ChangeActiveTrap(int, int);
384 static void InitRobotWheel(int, int);
385 static void RunRobotWheel(int, int);
386 static void StopRobotWheel(int, int);
388 static void InitTimegateWheel(int, int);
389 static void RunTimegateWheel(int, int);
391 static void InitMagicBallDelay(int, int);
392 static void ActivateMagicBall(int, int);
394 static void InitDiagonalMovingElement(int, int);
396 struct ChangingElementInfo
401 void (*pre_change_function)(int x, int y);
402 void (*change_function)(int x, int y);
403 void (*post_change_function)(int x, int y);
406 static struct ChangingElementInfo change_delay_list[] =
457 EL_SWITCHGATE_OPENING,
465 EL_SWITCHGATE_CLOSING,
466 EL_SWITCHGATE_CLOSED,
498 EL_ACID_SPLASH_RIGHT,
507 EL_SP_BUGGY_BASE_ACTIVATING,
514 EL_SP_BUGGY_BASE_ACTIVATING,
515 EL_SP_BUGGY_BASE_ACTIVE,
522 EL_SP_BUGGY_BASE_ACTIVE,
546 EL_ROBOT_WHEEL_ACTIVE,
554 EL_TIMEGATE_SWITCH_ACTIVE,
562 EL_EMC_MAGIC_BALL_ACTIVE,
563 EL_EMC_MAGIC_BALL_ACTIVE,
570 EL_EMC_SPRING_BUMPER_ACTIVE,
571 EL_EMC_SPRING_BUMPER,
578 EL_DIAGONAL_SHRINKING,
591 InitDiagonalMovingElement
607 int push_delay_fixed, push_delay_random;
612 { EL_BALLOON, 0, 0 },
614 { EL_SOKOBAN_OBJECT, 2, 0 },
615 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
616 { EL_SATELLITE, 2, 0 },
617 { EL_SP_DISK_YELLOW, 2, 0 },
619 { EL_UNDEFINED, 0, 0 },
627 move_stepsize_list[] =
629 { EL_AMOEBA_DROP, 2 },
630 { EL_AMOEBA_DROPPING, 2 },
631 { EL_QUICKSAND_FILLING, 1 },
632 { EL_QUICKSAND_EMPTYING, 1 },
633 { EL_MAGIC_WALL_FILLING, 2 },
634 { EL_BD_MAGIC_WALL_FILLING, 2 },
635 { EL_MAGIC_WALL_EMPTYING, 2 },
636 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
646 collect_count_list[] =
649 { EL_BD_DIAMOND, 1 },
650 { EL_EMERALD_YELLOW, 1 },
651 { EL_EMERALD_RED, 1 },
652 { EL_EMERALD_PURPLE, 1 },
654 { EL_SP_INFOTRON, 1 },
666 access_direction_list[] =
668 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
669 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
670 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
671 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
672 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
673 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
674 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
675 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
676 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
677 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
678 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
680 { EL_SP_PORT_LEFT, MV_RIGHT },
681 { EL_SP_PORT_RIGHT, MV_LEFT },
682 { EL_SP_PORT_UP, MV_DOWN },
683 { EL_SP_PORT_DOWN, MV_UP },
684 { EL_SP_PORT_HORIZONTAL, MV_LEFT | MV_RIGHT },
685 { EL_SP_PORT_VERTICAL, MV_UP | MV_DOWN },
686 { EL_SP_PORT_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
687 { EL_SP_GRAVITY_PORT_LEFT, MV_RIGHT },
688 { EL_SP_GRAVITY_PORT_RIGHT, MV_LEFT },
689 { EL_SP_GRAVITY_PORT_UP, MV_DOWN },
690 { EL_SP_GRAVITY_PORT_DOWN, MV_UP },
691 { EL_SP_GRAVITY_ON_PORT_LEFT, MV_RIGHT },
692 { EL_SP_GRAVITY_ON_PORT_RIGHT, MV_LEFT },
693 { EL_SP_GRAVITY_ON_PORT_UP, MV_DOWN },
694 { EL_SP_GRAVITY_ON_PORT_DOWN, MV_UP },
695 { EL_SP_GRAVITY_OFF_PORT_LEFT, MV_RIGHT },
696 { EL_SP_GRAVITY_OFF_PORT_RIGHT, MV_LEFT },
697 { EL_SP_GRAVITY_OFF_PORT_UP, MV_DOWN },
698 { EL_SP_GRAVITY_OFF_PORT_DOWN, MV_UP },
700 { EL_UNDEFINED, MV_NONE }
703 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
705 #define IS_AUTO_CHANGING(e) (element_info[e].has_change_event[CE_DELAY])
706 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
707 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
708 IS_JUST_CHANGING(x, y))
710 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
712 /* static variables for playfield scan mode (scanning forward or backward) */
713 static int playfield_scan_start_x = 0;
714 static int playfield_scan_start_y = 0;
715 static int playfield_scan_delta_x = 1;
716 static int playfield_scan_delta_y = 1;
718 #define SCAN_PLAYFIELD(x, y) for ((y) = playfield_scan_start_y; \
719 (y) >= 0 && (y) <= lev_fieldy - 1; \
720 (y) += playfield_scan_delta_y) \
721 for ((x) = playfield_scan_start_x; \
722 (x) >= 0 && (x) <= lev_fieldx - 1; \
723 (x) += playfield_scan_delta_x) \
726 void DEBUG_SetMaximumDynamite()
730 for (i = 0; i < MAX_INVENTORY_SIZE; i++)
731 if (local_player->inventory_size < MAX_INVENTORY_SIZE)
732 local_player->inventory_element[local_player->inventory_size++] =
737 static void InitPlayfieldScanModeVars()
739 if (game.use_reverse_scan_direction)
741 playfield_scan_start_x = lev_fieldx - 1;
742 playfield_scan_start_y = lev_fieldy - 1;
744 playfield_scan_delta_x = -1;
745 playfield_scan_delta_y = -1;
749 playfield_scan_start_x = 0;
750 playfield_scan_start_y = 0;
752 playfield_scan_delta_x = 1;
753 playfield_scan_delta_y = 1;
757 static void InitPlayfieldScanMode(int mode)
759 game.use_reverse_scan_direction =
760 (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
762 InitPlayfieldScanModeVars();
765 static int get_move_delay_from_stepsize(int move_stepsize)
768 MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
770 /* make sure that stepsize value is always a power of 2 */
771 move_stepsize = (1 << log_2(move_stepsize));
773 return TILEX / move_stepsize;
776 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
779 int move_delay = get_move_delay_from_stepsize(move_stepsize);
780 boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
782 /* do no immediately change move delay -- the player might just be moving */
783 player->move_delay_value_next = move_delay;
785 /* information if player can move must be set separately */
786 player->cannot_move = cannot_move;
790 player->move_delay = game.initial_move_delay;
791 player->move_delay_value = game.initial_move_delay_value;
793 player->move_delay_value_next = -1;
795 player->move_delay_reset_counter = 0;
799 void GetPlayerConfig()
801 if (!audio.sound_available)
802 setup.sound_simple = FALSE;
804 if (!audio.loops_available)
805 setup.sound_loops = FALSE;
807 if (!audio.music_available)
808 setup.sound_music = FALSE;
810 if (!video.fullscreen_available)
811 setup.fullscreen = FALSE;
813 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
815 SetAudioMode(setup.sound);
819 static int getBeltNrFromBeltElement(int element)
821 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
822 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
823 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
826 static int getBeltNrFromBeltActiveElement(int element)
828 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
829 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
830 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
833 static int getBeltNrFromBeltSwitchElement(int element)
835 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
836 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
837 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
840 static int getBeltDirNrFromBeltSwitchElement(int element)
842 static int belt_base_element[4] =
844 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
845 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
846 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
847 EL_CONVEYOR_BELT_4_SWITCH_LEFT
850 int belt_nr = getBeltNrFromBeltSwitchElement(element);
851 int belt_dir_nr = element - belt_base_element[belt_nr];
853 return (belt_dir_nr % 3);
856 static int getBeltDirFromBeltSwitchElement(int element)
858 static int belt_move_dir[3] =
865 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
867 return belt_move_dir[belt_dir_nr];
870 static int get_element_from_group_element(int element)
872 if (IS_GROUP_ELEMENT(element))
874 struct ElementGroupInfo *group = element_info[element].group;
875 int last_anim_random_frame = gfx.anim_random_frame;
878 if (group->choice_mode == ANIM_RANDOM)
879 gfx.anim_random_frame = RND(group->num_elements_resolved);
881 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
882 group->choice_mode, 0,
885 if (group->choice_mode == ANIM_RANDOM)
886 gfx.anim_random_frame = last_anim_random_frame;
890 element = group->element_resolved[element_pos];
896 static void InitPlayerField(int x, int y, int element, boolean init_game)
898 if (element == EL_SP_MURPHY)
902 if (stored_player[0].present)
904 Feld[x][y] = EL_SP_MURPHY_CLONE;
910 stored_player[0].use_murphy = TRUE;
912 if (!level.use_artwork_element[0])
913 stored_player[0].artwork_element = EL_SP_MURPHY;
916 Feld[x][y] = EL_PLAYER_1;
922 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
923 int jx = player->jx, jy = player->jy;
925 player->present = TRUE;
927 player->block_last_field = (element == EL_SP_MURPHY ?
928 level.sp_block_last_field :
929 level.block_last_field);
931 /* ---------- initialize player's last field block delay --------------- */
933 /* always start with reliable default value (no adjustment needed) */
934 player->block_delay_adjustment = 0;
936 /* special case 1: in Supaplex, Murphy blocks last field one more frame */
937 if (player->block_last_field && element == EL_SP_MURPHY)
938 player->block_delay_adjustment = 1;
940 /* special case 2: in game engines before 3.1.1, blocking was different */
941 if (game.use_block_last_field_bug)
942 player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
944 if (!options.network || player->connected)
946 player->active = TRUE;
948 /* remove potentially duplicate players */
949 if (StorePlayer[jx][jy] == Feld[x][y])
950 StorePlayer[jx][jy] = 0;
952 StorePlayer[x][y] = Feld[x][y];
956 printf("Player %d activated.\n", player->element_nr);
957 printf("[Local player is %d and currently %s.]\n",
958 local_player->element_nr,
959 local_player->active ? "active" : "not active");
963 Feld[x][y] = EL_EMPTY;
965 player->jx = player->last_jx = x;
966 player->jy = player->last_jy = y;
970 static void InitField(int x, int y, boolean init_game)
972 int element = Feld[x][y];
981 InitPlayerField(x, y, element, init_game);
984 case EL_SOKOBAN_FIELD_PLAYER:
985 element = Feld[x][y] = EL_PLAYER_1;
986 InitField(x, y, init_game);
988 element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
989 InitField(x, y, init_game);
992 case EL_SOKOBAN_FIELD_EMPTY:
993 local_player->sokobanfields_still_needed++;
997 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
998 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
999 else if (x > 0 && Feld[x-1][y] == EL_ACID)
1000 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1001 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1002 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1003 else if (y > 0 && Feld[x][y-1] == EL_ACID)
1004 Feld[x][y] = EL_ACID_POOL_BOTTOM;
1005 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1006 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1015 case EL_SPACESHIP_RIGHT:
1016 case EL_SPACESHIP_UP:
1017 case EL_SPACESHIP_LEFT:
1018 case EL_SPACESHIP_DOWN:
1019 case EL_BD_BUTTERFLY:
1020 case EL_BD_BUTTERFLY_RIGHT:
1021 case EL_BD_BUTTERFLY_UP:
1022 case EL_BD_BUTTERFLY_LEFT:
1023 case EL_BD_BUTTERFLY_DOWN:
1025 case EL_BD_FIREFLY_RIGHT:
1026 case EL_BD_FIREFLY_UP:
1027 case EL_BD_FIREFLY_LEFT:
1028 case EL_BD_FIREFLY_DOWN:
1029 case EL_PACMAN_RIGHT:
1031 case EL_PACMAN_LEFT:
1032 case EL_PACMAN_DOWN:
1034 case EL_YAMYAM_LEFT:
1035 case EL_YAMYAM_RIGHT:
1037 case EL_YAMYAM_DOWN:
1038 case EL_DARK_YAMYAM:
1041 case EL_SP_SNIKSNAK:
1042 case EL_SP_ELECTRON:
1051 case EL_AMOEBA_FULL:
1056 case EL_AMOEBA_DROP:
1057 if (y == lev_fieldy - 1)
1059 Feld[x][y] = EL_AMOEBA_GROWING;
1060 Store[x][y] = EL_AMOEBA_WET;
1064 case EL_DYNAMITE_ACTIVE:
1065 case EL_SP_DISK_RED_ACTIVE:
1066 case EL_DYNABOMB_PLAYER_1_ACTIVE:
1067 case EL_DYNABOMB_PLAYER_2_ACTIVE:
1068 case EL_DYNABOMB_PLAYER_3_ACTIVE:
1069 case EL_DYNABOMB_PLAYER_4_ACTIVE:
1070 MovDelay[x][y] = 96;
1073 case EL_EM_DYNAMITE_ACTIVE:
1074 MovDelay[x][y] = 32;
1078 local_player->lights_still_needed++;
1082 local_player->friends_still_needed++;
1087 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1090 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1091 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1092 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1093 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1094 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1095 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1096 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1097 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1098 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1099 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1100 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1101 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1104 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1105 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1106 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1108 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
1110 game.belt_dir[belt_nr] = belt_dir;
1111 game.belt_dir_nr[belt_nr] = belt_dir_nr;
1113 else /* more than one switch -- set it like the first switch */
1115 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1120 case EL_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
1122 Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
1125 case EL_LIGHT_SWITCH_ACTIVE:
1127 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1130 case EL_EMC_MAGIC_BALL:
1131 if (game.ball_state)
1132 Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1135 case EL_EMC_MAGIC_BALL_SWITCH:
1136 if (game.ball_state)
1137 Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1142 if (IS_CUSTOM_ELEMENT(element))
1144 if (CAN_MOVE(element))
1147 #if USE_NEW_CUSTOM_VALUE
1148 if (!element_info[element].use_last_ce_value || init_game)
1149 CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1153 if (IS_CUSTOM_ELEMENT(element) && CAN_MOVE(element))
1156 else if (IS_GROUP_ELEMENT(element))
1159 Feld[x][y] = get_element_from_group_element(element);
1161 InitField(x, y, init_game);
1163 struct ElementGroupInfo *group = element_info[element].group;
1164 int last_anim_random_frame = gfx.anim_random_frame;
1167 if (group->choice_mode == ANIM_RANDOM)
1168 gfx.anim_random_frame = RND(group->num_elements_resolved);
1170 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1171 group->choice_mode, 0,
1174 if (group->choice_mode == ANIM_RANDOM)
1175 gfx.anim_random_frame = last_anim_random_frame;
1177 group->choice_pos++;
1179 Feld[x][y] = group->element_resolved[element_pos];
1181 InitField(x, y, init_game);
1189 CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1194 #if USE_NEW_CUSTOM_VALUE
1197 CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1199 CustomValue[x][y] = element_info[Feld[x][y]].custom_value_initial;
1207 static inline void InitField_WithBug1(int x, int y, boolean init_game)
1209 InitField(x, y, init_game);
1211 /* not needed to call InitMovDir() -- already done by InitField()! */
1212 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1213 CAN_MOVE(Feld[x][y]))
1217 static inline void InitField_WithBug2(int x, int y, boolean init_game)
1219 int old_element = Feld[x][y];
1221 InitField(x, y, init_game);
1223 /* not needed to call InitMovDir() -- already done by InitField()! */
1224 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1225 CAN_MOVE(old_element) &&
1226 (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1229 /* this case is in fact a combination of not less than three bugs:
1230 first, it calls InitMovDir() for elements that can move, although this is
1231 already done by InitField(); then, it checks the element that was at this
1232 field _before_ the call to InitField() (which can change it); lastly, it
1233 was not called for "mole with direction" elements, which were treated as
1234 "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1238 inline void DrawGameValue_Emeralds(int value)
1240 int xpos = (3 * 14 - 3 * getFontWidth(FONT_TEXT_2)) / 2;
1242 DrawText(DX_EMERALDS + xpos, DY_EMERALDS, int2str(value, 3), FONT_TEXT_2);
1245 inline void DrawGameValue_Dynamite(int value)
1247 int xpos = (3 * 14 - 3 * getFontWidth(FONT_TEXT_2)) / 2;
1249 DrawText(DX_DYNAMITE + xpos, DY_DYNAMITE, int2str(value, 3), FONT_TEXT_2);
1252 inline void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
1254 int base_key_graphic = EL_KEY_1;
1257 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
1258 base_key_graphic = EL_EM_KEY_1;
1260 /* currently only 4 of 8 possible keys are displayed */
1261 for (i = 0; i < STD_NUM_KEYS; i++)
1264 DrawMiniGraphicExt(drawto, DX_KEYS + i * MINI_TILEX, DY_KEYS,
1265 el2edimg(base_key_graphic + i));
1267 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
1268 DOOR_GFX_PAGEX5 + XX_KEYS + i * MINI_TILEX, YY_KEYS,
1269 MINI_TILEX, MINI_TILEY, DX_KEYS + i * MINI_TILEX, DY_KEYS);
1273 inline void DrawGameValue_Score(int value)
1275 int xpos = (5 * 14 - 5 * getFontWidth(FONT_TEXT_2)) / 2;
1277 DrawText(DX_SCORE + xpos, DY_SCORE, int2str(value, 5), FONT_TEXT_2);
1280 inline void DrawGameValue_Time(int value)
1282 int xpos3 = (3 * 14 - 3 * getFontWidth(FONT_TEXT_2)) / 2;
1283 int xpos4 = (4 * 10 - 4 * getFontWidth(FONT_LEVEL_NUMBER)) / 2;
1285 /* clear background if value just changed its size */
1286 if (value == 999 || value == 1000)
1287 ClearRectangle(drawto, DX_TIME1, DY_TIME, 14 * 3, 14);
1290 DrawText(DX_TIME1 + xpos3, DY_TIME, int2str(value, 3), FONT_TEXT_2);
1292 DrawText(DX_TIME2 + xpos4, DY_TIME, int2str(value, 4), FONT_LEVEL_NUMBER);
1295 inline void DrawGameValue_Level(int value)
1298 DrawText(DX_LEVEL, DY_LEVEL, int2str(value, 2), FONT_TEXT_2);
1301 /* misuse area for displaying emeralds to draw bigger level number */
1302 DrawTextExt(drawto, DX_EMERALDS, DY_EMERALDS,
1303 int2str(value, 3), FONT_LEVEL_NUMBER, BLIT_OPAQUE);
1305 /* now copy it to the area for displaying level number */
1306 BlitBitmap(drawto, drawto,
1307 DX_EMERALDS, DY_EMERALDS + 1,
1308 getFontWidth(FONT_LEVEL_NUMBER) * 3,
1309 getFontHeight(FONT_LEVEL_NUMBER) - 1,
1310 DX_LEVEL - 1, DY_LEVEL + 1);
1312 /* restore the area for displaying emeralds */
1313 DrawGameValue_Emeralds(local_player->gems_still_needed);
1315 /* yes, this is all really ugly :-) */
1319 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
1322 int key[MAX_NUM_KEYS];
1325 for (i = 0; i < MAX_NUM_KEYS; i++)
1326 key[i] = key_bits & (1 << i);
1328 DrawGameValue_Level(level_nr);
1330 DrawGameValue_Emeralds(emeralds);
1331 DrawGameValue_Dynamite(dynamite);
1332 DrawGameValue_Score(score);
1333 DrawGameValue_Time(time);
1335 DrawGameValue_Keys(key);
1338 void DrawGameDoorValues()
1340 int dynamite_state = 0;
1344 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
1346 DrawGameDoorValues_EM();
1352 DrawGameValue_Level(level_nr);
1354 DrawGameValue_Emeralds(local_player->gems_still_needed);
1355 DrawGameValue_Dynamite(local_player->inventory_size);
1356 DrawGameValue_Score(local_player->score);
1357 DrawGameValue_Time(TimeLeft);
1361 if (game.centered_player_nr == -1)
1363 for (i = 0; i < MAX_PLAYERS; i++)
1365 for (j = 0; j < MAX_NUM_KEYS; j++)
1366 if (stored_player[i].key[j])
1367 key_bits |= (1 << j);
1369 dynamite_state += stored_player[i].inventory_size;
1373 DrawGameValue_Keys(stored_player[i].key);
1378 int player_nr = game.centered_player_nr;
1380 for (i = 0; i < MAX_NUM_KEYS; i++)
1381 if (stored_player[player_nr].key[i])
1382 key_bits |= (1 << i);
1384 dynamite_state = stored_player[player_nr].inventory_size;
1387 DrawAllGameValues(local_player->gems_still_needed, dynamite_state,
1388 local_player->score, TimeLeft, key_bits);
1393 static void resolve_group_element(int group_element, int recursion_depth)
1395 static int group_nr;
1396 static struct ElementGroupInfo *group;
1397 struct ElementGroupInfo *actual_group = element_info[group_element].group;
1400 if (recursion_depth > NUM_GROUP_ELEMENTS) /* recursion too deep */
1402 Error(ERR_WARN, "recursion too deep when resolving group element %d",
1403 group_element - EL_GROUP_START + 1);
1405 /* replace element which caused too deep recursion by question mark */
1406 group->element_resolved[group->num_elements_resolved++] = EL_UNKNOWN;
1411 if (recursion_depth == 0) /* initialization */
1413 group = element_info[group_element].group;
1414 group_nr = group_element - EL_GROUP_START;
1416 group->num_elements_resolved = 0;
1417 group->choice_pos = 0;
1420 for (i = 0; i < actual_group->num_elements; i++)
1422 int element = actual_group->element[i];
1424 if (group->num_elements_resolved == NUM_FILE_ELEMENTS)
1427 if (IS_GROUP_ELEMENT(element))
1428 resolve_group_element(element, recursion_depth + 1);
1431 group->element_resolved[group->num_elements_resolved++] = element;
1432 element_info[element].in_group[group_nr] = TRUE;
1439 =============================================================================
1441 -----------------------------------------------------------------------------
1442 initialize game engine due to level / tape version number
1443 =============================================================================
1446 static void InitGameEngine()
1448 int i, j, k, l, x, y;
1450 /* set game engine from tape file when re-playing, else from level file */
1451 game.engine_version = (tape.playing ? tape.engine_version :
1452 level.game_version);
1454 /* ---------------------------------------------------------------------- */
1455 /* set flags for bugs and changes according to active game engine version */
1456 /* ---------------------------------------------------------------------- */
1459 Summary of bugfix/change:
1460 Fixed handling for custom elements that change when pushed by the player.
1462 Fixed/changed in version:
1466 Before 3.1.0, custom elements that "change when pushing" changed directly
1467 after the player started pushing them (until then handled in "DigField()").
1468 Since 3.1.0, these custom elements are not changed until the "pushing"
1469 move of the element is finished (now handled in "ContinueMoving()").
1471 Affected levels/tapes:
1472 The first condition is generally needed for all levels/tapes before version
1473 3.1.0, which might use the old behaviour before it was changed; known tapes
1474 that are affected are some tapes from the level set "Walpurgis Gardens" by
1476 The second condition is an exception from the above case and is needed for
1477 the special case of tapes recorded with game (not engine!) version 3.1.0 or
1478 above (including some development versions of 3.1.0), but before it was
1479 known that this change would break tapes like the above and was fixed in
1480 3.1.1, so that the changed behaviour was active although the engine version
1481 while recording maybe was before 3.1.0. There is at least one tape that is
1482 affected by this exception, which is the tape for the one-level set "Bug
1483 Machine" by Juergen Bonhagen.
1486 game.use_change_when_pushing_bug =
1487 (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1489 tape.game_version >= VERSION_IDENT(3,1,0,0) &&
1490 tape.game_version < VERSION_IDENT(3,1,1,0)));
1493 Summary of bugfix/change:
1494 Fixed handling for blocking the field the player leaves when moving.
1496 Fixed/changed in version:
1500 Before 3.1.1, when "block last field when moving" was enabled, the field
1501 the player is leaving when moving was blocked for the time of the move,
1502 and was directly unblocked afterwards. This resulted in the last field
1503 being blocked for exactly one less than the number of frames of one player
1504 move. Additionally, even when blocking was disabled, the last field was
1505 blocked for exactly one frame.
1506 Since 3.1.1, due to changes in player movement handling, the last field
1507 is not blocked at all when blocking is disabled. When blocking is enabled,
1508 the last field is blocked for exactly the number of frames of one player
1509 move. Additionally, if the player is Murphy, the hero of Supaplex, the
1510 last field is blocked for exactly one more than the number of frames of
1513 Affected levels/tapes:
1514 (!!! yet to be determined -- probably many !!!)
1517 game.use_block_last_field_bug =
1518 (game.engine_version < VERSION_IDENT(3,1,1,0));
1521 Summary of bugfix/change:
1522 Changed behaviour of CE changes with multiple changes per single frame.
1524 Fixed/changed in version:
1528 Before 3.2.0-6, only one single CE change was allowed in each engine frame.
1529 This resulted in race conditions where CEs seem to behave strange in some
1530 situations (where triggered CE changes were just skipped because there was
1531 already a CE change on that tile in the playfield in that engine frame).
1532 Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
1533 (The number of changes per frame must be limited in any case, because else
1534 it is easily possible to define CE changes that would result in an infinite
1535 loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
1536 should be set large enough so that it would only be reached in cases where
1537 the corresponding CE change conditions run into a loop. Therefore, it seems
1538 to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
1539 maximal number of change pages for custom elements.)
1541 Affected levels/tapes:
1545 #if USE_ONLY_ONE_CHANGE_PER_FRAME
1546 game.max_num_changes_per_frame = 1;
1548 game.max_num_changes_per_frame =
1549 (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
1552 /* ---------------------------------------------------------------------- */
1554 /* default scan direction: scan playfield from top/left to bottom/right */
1555 InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
1557 /* dynamically adjust element properties according to game engine version */
1558 InitElementPropertiesEngine(game.engine_version);
1561 printf("level %d: level version == %06d\n", level_nr, level.game_version);
1562 printf(" tape version == %06d [%s] [file: %06d]\n",
1563 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
1565 printf(" => game.engine_version == %06d\n", game.engine_version);
1569 /* ---------- recursively resolve group elements ------------------------- */
1571 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1572 for (j = 0; j < NUM_GROUP_ELEMENTS; j++)
1573 element_info[i].in_group[j] = FALSE;
1575 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
1576 resolve_group_element(EL_GROUP_START + i, 0);
1579 /* ---------- initialize player's initial move delay --------------------- */
1582 /* dynamically adjust player properties according to level information */
1583 game.initial_move_delay_value =
1584 get_move_delay_from_stepsize(level.initial_player_stepsize);
1586 /* dynamically adjust player properties according to level information */
1587 game.initial_move_delay_value =
1588 (level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED);
1591 /* dynamically adjust player properties according to game engine version */
1592 game.initial_move_delay = (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
1593 game.initial_move_delay_value : 0);
1595 /* ---------- initialize player's initial push delay --------------------- */
1597 /* dynamically adjust player properties according to game engine version */
1598 game.initial_push_delay_value =
1599 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
1601 /* ---------- initialize changing elements ------------------------------- */
1603 /* initialize changing elements information */
1604 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1606 struct ElementInfo *ei = &element_info[i];
1608 /* this pointer might have been changed in the level editor */
1609 ei->change = &ei->change_page[0];
1611 if (!IS_CUSTOM_ELEMENT(i))
1613 ei->change->target_element = EL_EMPTY_SPACE;
1614 ei->change->delay_fixed = 0;
1615 ei->change->delay_random = 0;
1616 ei->change->delay_frames = 1;
1619 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1621 ei->has_change_event[j] = FALSE;
1623 ei->event_page_nr[j] = 0;
1624 ei->event_page[j] = &ei->change_page[0];
1628 /* add changing elements from pre-defined list */
1629 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
1631 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
1632 struct ElementInfo *ei = &element_info[ch_delay->element];
1634 ei->change->target_element = ch_delay->target_element;
1635 ei->change->delay_fixed = ch_delay->change_delay;
1637 ei->change->pre_change_function = ch_delay->pre_change_function;
1638 ei->change->change_function = ch_delay->change_function;
1639 ei->change->post_change_function = ch_delay->post_change_function;
1641 ei->change->can_change = TRUE;
1642 ei->change->can_change_or_has_action = TRUE;
1644 ei->has_change_event[CE_DELAY] = TRUE;
1646 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
1647 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
1650 /* ---------- initialize internal run-time variables ------------- */
1652 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1654 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1656 for (j = 0; j < ei->num_change_pages; j++)
1658 ei->change_page[j].can_change_or_has_action =
1659 (ei->change_page[j].can_change |
1660 ei->change_page[j].has_action);
1664 /* add change events from custom element configuration */
1665 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1667 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1669 for (j = 0; j < ei->num_change_pages; j++)
1671 if (!ei->change_page[j].can_change_or_has_action)
1674 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1676 /* only add event page for the first page found with this event */
1677 if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
1679 ei->has_change_event[k] = TRUE;
1681 ei->event_page_nr[k] = j;
1682 ei->event_page[k] = &ei->change_page[j];
1688 /* ---------- initialize run-time trigger player and element ------------- */
1690 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1692 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1694 for (j = 0; j < ei->num_change_pages; j++)
1696 ei->change_page[j].actual_trigger_element = EL_EMPTY;
1697 ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
1698 ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
1699 ei->change_page[j].actual_trigger_ce_value = 0;
1700 ei->change_page[j].actual_trigger_ce_score = 0;
1704 /* ---------- initialize trigger events ---------------------------------- */
1706 /* initialize trigger events information */
1707 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1708 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1709 trigger_events[i][j] = FALSE;
1711 /* add trigger events from element change event properties */
1712 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1714 struct ElementInfo *ei = &element_info[i];
1716 for (j = 0; j < ei->num_change_pages; j++)
1718 if (!ei->change_page[j].can_change_or_has_action)
1721 if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
1723 int trigger_element = ei->change_page[j].trigger_element;
1725 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1727 if (ei->change_page[j].has_event[k])
1729 if (IS_GROUP_ELEMENT(trigger_element))
1731 struct ElementGroupInfo *group =
1732 element_info[trigger_element].group;
1734 for (l = 0; l < group->num_elements_resolved; l++)
1735 trigger_events[group->element_resolved[l]][k] = TRUE;
1738 trigger_events[trigger_element][k] = TRUE;
1745 /* ---------- initialize push delay -------------------------------------- */
1747 /* initialize push delay values to default */
1748 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1750 if (!IS_CUSTOM_ELEMENT(i))
1752 element_info[i].push_delay_fixed = game.default_push_delay_fixed;
1753 element_info[i].push_delay_random = game.default_push_delay_random;
1757 /* set push delay value for certain elements from pre-defined list */
1758 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
1760 int e = push_delay_list[i].element;
1762 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
1763 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
1766 /* set push delay value for Supaplex elements for newer engine versions */
1767 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
1769 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1771 if (IS_SP_ELEMENT(i))
1773 /* set SP push delay to just enough to push under a falling zonk */
1774 int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
1776 element_info[i].push_delay_fixed = delay;
1777 element_info[i].push_delay_random = 0;
1782 /* ---------- initialize move stepsize ----------------------------------- */
1784 /* initialize move stepsize values to default */
1785 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1786 if (!IS_CUSTOM_ELEMENT(i))
1787 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
1789 /* set move stepsize value for certain elements from pre-defined list */
1790 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
1792 int e = move_stepsize_list[i].element;
1794 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
1797 /* ---------- initialize collect score ----------------------------------- */
1799 /* initialize collect score values for custom elements from initial value */
1800 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1801 if (IS_CUSTOM_ELEMENT(i))
1802 element_info[i].collect_score = element_info[i].collect_score_initial;
1804 /* ---------- initialize collect count ----------------------------------- */
1806 /* initialize collect count values for non-custom elements */
1807 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1808 if (!IS_CUSTOM_ELEMENT(i))
1809 element_info[i].collect_count_initial = 0;
1811 /* add collect count values for all elements from pre-defined list */
1812 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
1813 element_info[collect_count_list[i].element].collect_count_initial =
1814 collect_count_list[i].count;
1816 /* ---------- initialize access direction -------------------------------- */
1818 /* initialize access direction values to default (access from every side) */
1819 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1820 if (!IS_CUSTOM_ELEMENT(i))
1821 element_info[i].access_direction = MV_ALL_DIRECTIONS;
1823 /* set access direction value for certain elements from pre-defined list */
1824 for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
1825 element_info[access_direction_list[i].element].access_direction =
1826 access_direction_list[i].direction;
1828 /* ---------- initialize explosion content ------------------------------- */
1829 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1831 if (IS_CUSTOM_ELEMENT(i))
1834 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
1836 /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
1838 element_info[i].content.e[x][y] =
1839 (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
1840 i == EL_PLAYER_2 ? EL_EMERALD_RED :
1841 i == EL_PLAYER_3 ? EL_EMERALD :
1842 i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
1843 i == EL_MOLE ? EL_EMERALD_RED :
1844 i == EL_PENGUIN ? EL_EMERALD_PURPLE :
1845 i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
1846 i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
1847 i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
1848 i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
1849 i == EL_WALL_EMERALD ? EL_EMERALD :
1850 i == EL_WALL_DIAMOND ? EL_DIAMOND :
1851 i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
1852 i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
1853 i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
1854 i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
1855 i == EL_WALL_PEARL ? EL_PEARL :
1856 i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
1862 int get_num_special_action(int element, int action_first, int action_last)
1864 int num_special_action = 0;
1867 for (i = action_first; i <= action_last; i++)
1869 boolean found = FALSE;
1871 for (j = 0; j < NUM_DIRECTIONS; j++)
1872 if (el_act_dir2img(element, i, j) !=
1873 el_act_dir2img(element, ACTION_DEFAULT, j))
1877 num_special_action++;
1883 printf("::: %d->%d: %d\n", action_first, action_last, num_special_action);
1886 return num_special_action;
1890 =============================================================================
1892 -----------------------------------------------------------------------------
1893 initialize and start new game
1894 =============================================================================
1899 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
1900 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
1901 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
1906 /* don't play tapes over network */
1907 network_playing = (options.network && !tape.playing);
1909 for (i = 0; i < MAX_PLAYERS; i++)
1911 struct PlayerInfo *player = &stored_player[i];
1913 player->index_nr = i;
1914 player->index_bit = (1 << i);
1915 player->element_nr = EL_PLAYER_1 + i;
1917 player->present = FALSE;
1918 player->active = FALSE;
1921 player->effective_action = 0;
1922 player->programmed_action = 0;
1925 player->gems_still_needed = level.gems_needed;
1926 player->sokobanfields_still_needed = 0;
1927 player->lights_still_needed = 0;
1928 player->friends_still_needed = 0;
1930 for (j = 0; j < MAX_NUM_KEYS; j++)
1931 player->key[j] = FALSE;
1933 player->dynabomb_count = 0;
1934 player->dynabomb_size = 1;
1935 player->dynabombs_left = 0;
1936 player->dynabomb_xl = FALSE;
1938 player->MovDir = MV_NONE;
1941 player->GfxDir = MV_NONE;
1942 player->GfxAction = ACTION_DEFAULT;
1944 player->StepFrame = 0;
1946 player->use_murphy = FALSE;
1947 player->artwork_element =
1948 (level.use_artwork_element[i] ? level.artwork_element[i] :
1949 player->element_nr);
1951 player->block_last_field = FALSE; /* initialized in InitPlayerField() */
1952 player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
1954 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
1956 player->actual_frame_counter = 0;
1958 player->step_counter = 0;
1960 player->last_move_dir = MV_NONE;
1962 player->is_waiting = FALSE;
1963 player->is_moving = FALSE;
1964 player->is_auto_moving = FALSE;
1965 player->is_digging = FALSE;
1966 player->is_snapping = FALSE;
1967 player->is_collecting = FALSE;
1968 player->is_pushing = FALSE;
1969 player->is_switching = FALSE;
1970 player->is_dropping = FALSE;
1971 player->is_dropping_pressed = FALSE;
1973 player->is_bored = FALSE;
1974 player->is_sleeping = FALSE;
1976 player->frame_counter_bored = -1;
1977 player->frame_counter_sleeping = -1;
1979 player->anim_delay_counter = 0;
1980 player->post_delay_counter = 0;
1982 player->dir_waiting = MV_NONE;
1983 player->action_waiting = ACTION_DEFAULT;
1984 player->last_action_waiting = ACTION_DEFAULT;
1985 player->special_action_bored = ACTION_DEFAULT;
1986 player->special_action_sleeping = ACTION_DEFAULT;
1989 /* cannot be set here -- could be modified in Init[Player]Field() below */
1991 /* set number of special actions for bored and sleeping animation */
1992 player->num_special_action_bored =
1993 get_num_special_action(player->artwork_element,
1994 ACTION_BORING_1, ACTION_BORING_LAST);
1995 player->num_special_action_sleeping =
1996 get_num_special_action(player->artwork_element,
1997 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
2000 player->switch_x = -1;
2001 player->switch_y = -1;
2003 player->drop_x = -1;
2004 player->drop_y = -1;
2006 player->show_envelope = 0;
2009 SetPlayerMoveSpeed(player, level.initial_player_stepsize, TRUE);
2011 player->move_delay = game.initial_move_delay;
2012 player->move_delay_value = game.initial_move_delay_value;
2014 player->move_delay_value_next = -1;
2016 player->move_delay_reset_counter = 0;
2018 player->cannot_move = FALSE;
2021 player->push_delay = -1; /* initialized when pushing starts */
2022 player->push_delay_value = game.initial_push_delay_value;
2024 player->drop_delay = 0;
2025 player->drop_pressed_delay = 0;
2027 player->last_jx = player->last_jy = 0;
2028 player->jx = player->jy = 0;
2030 player->shield_normal_time_left = 0;
2031 player->shield_deadly_time_left = 0;
2033 player->inventory_infinite_element = EL_UNDEFINED;
2034 player->inventory_size = 0;
2036 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
2037 SnapField(player, 0, 0);
2039 player->LevelSolved = FALSE;
2040 player->GameOver = FALSE;
2043 network_player_action_received = FALSE;
2045 #if defined(NETWORK_AVALIABLE)
2046 /* initial null action */
2047 if (network_playing)
2048 SendToServer_MovePlayer(MV_NONE);
2057 TimeLeft = level.time;
2060 ScreenMovDir = MV_NONE;
2064 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
2066 AllPlayersGone = FALSE;
2068 game.yamyam_content_nr = 0;
2069 game.magic_wall_active = FALSE;
2070 game.magic_wall_time_left = 0;
2071 game.light_time_left = 0;
2072 game.timegate_time_left = 0;
2073 game.switchgate_pos = 0;
2074 game.wind_direction = level.wind_direction_initial;
2075 game.gravity = level.initial_gravity;
2076 game.explosions_delayed = TRUE;
2078 game.lenses_time_left = 0;
2079 game.magnify_time_left = 0;
2081 game.ball_state = level.ball_state_initial;
2082 game.ball_content_nr = 0;
2084 game.envelope_active = FALSE;
2086 /* set focus to local player for network games, else to all players */
2087 game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
2088 game.centered_player_nr_next = game.centered_player_nr;
2089 game.set_centered_player = FALSE;
2091 if (network_playing && tape.recording)
2093 /* store client dependent player focus when recording network games */
2094 tape.centered_player_nr_next = game.centered_player_nr_next;
2095 tape.set_centered_player = TRUE;
2099 printf("::: focus set to player %d [%d]\n",
2100 game.centered_player_nr, local_player->index_nr);
2103 for (i = 0; i < NUM_BELTS; i++)
2105 game.belt_dir[i] = MV_NONE;
2106 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
2109 for (i = 0; i < MAX_NUM_AMOEBA; i++)
2110 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
2113 SCAN_PLAYFIELD(x, y)
2115 for (x = 0; x < lev_fieldx; x++) for (y = 0; y < lev_fieldy; y++)
2118 Feld[x][y] = level.field[x][y];
2119 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
2120 ChangeDelay[x][y] = 0;
2121 ChangePage[x][y] = -1;
2122 #if USE_NEW_CUSTOM_VALUE
2123 CustomValue[x][y] = 0; /* initialized in InitField() */
2125 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
2127 WasJustMoving[x][y] = 0;
2128 WasJustFalling[x][y] = 0;
2129 CheckCollision[x][y] = 0;
2131 Pushed[x][y] = FALSE;
2133 ChangeCount[x][y] = 0;
2134 ChangeEvent[x][y] = -1;
2136 ExplodePhase[x][y] = 0;
2137 ExplodeDelay[x][y] = 0;
2138 ExplodeField[x][y] = EX_TYPE_NONE;
2140 RunnerVisit[x][y] = 0;
2141 PlayerVisit[x][y] = 0;
2144 GfxRandom[x][y] = INIT_GFX_RANDOM();
2145 GfxElement[x][y] = EL_UNDEFINED;
2146 GfxAction[x][y] = ACTION_DEFAULT;
2147 GfxDir[x][y] = MV_NONE;
2151 SCAN_PLAYFIELD(x, y)
2153 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
2156 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
2158 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
2160 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
2163 InitField(x, y, TRUE);
2168 for (i = 0; i < MAX_PLAYERS; i++)
2170 struct PlayerInfo *player = &stored_player[i];
2173 /* set number of special actions for bored and sleeping animation */
2174 player->num_special_action_bored =
2175 get_num_special_action(player->artwork_element,
2176 ACTION_BORING_1, ACTION_BORING_LAST);
2177 player->num_special_action_sleeping =
2178 get_num_special_action(player->artwork_element,
2179 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
2184 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
2185 emulate_sb ? EMU_SOKOBAN :
2186 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
2188 #if USE_NEW_ALL_SLIPPERY
2189 /* initialize type of slippery elements */
2190 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2192 if (!IS_CUSTOM_ELEMENT(i))
2194 /* default: elements slip down either to the left or right randomly */
2195 element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
2197 /* SP style elements prefer to slip down on the left side */
2198 if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
2199 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
2201 /* BD style elements prefer to slip down on the left side */
2202 if (game.emulation == EMU_BOULDERDASH)
2203 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
2208 /* initialize explosion and ignition delay */
2209 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2211 if (!IS_CUSTOM_ELEMENT(i))
2214 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
2215 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
2216 game.emulation == EMU_SUPAPLEX ? 3 : 2);
2217 int last_phase = (num_phase + 1) * delay;
2218 int half_phase = (num_phase / 2) * delay;
2220 element_info[i].explosion_delay = last_phase - 1;
2221 element_info[i].ignition_delay = half_phase;
2223 if (i == EL_BLACK_ORB)
2224 element_info[i].ignition_delay = 1;
2228 if (element_info[i].explosion_delay < 1) /* !!! check again !!! */
2229 element_info[i].explosion_delay = 1;
2231 if (element_info[i].ignition_delay < 1) /* !!! check again !!! */
2232 element_info[i].ignition_delay = 1;
2236 /* correct non-moving belts to start moving left */
2237 for (i = 0; i < NUM_BELTS; i++)
2238 if (game.belt_dir[i] == MV_NONE)
2239 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
2241 /* check if any connected player was not found in playfield */
2242 for (i = 0; i < MAX_PLAYERS; i++)
2244 struct PlayerInfo *player = &stored_player[i];
2246 if (player->connected && !player->present)
2248 for (j = 0; j < MAX_PLAYERS; j++)
2250 struct PlayerInfo *some_player = &stored_player[j];
2251 int jx = some_player->jx, jy = some_player->jy;
2253 /* assign first free player found that is present in the playfield */
2254 if (some_player->present && !some_player->connected)
2256 player->present = TRUE;
2257 player->active = TRUE;
2259 some_player->present = FALSE;
2260 some_player->active = FALSE;
2263 player->element_nr = some_player->element_nr;
2266 player->artwork_element = some_player->artwork_element;
2268 player->block_last_field = some_player->block_last_field;
2269 player->block_delay_adjustment = some_player->block_delay_adjustment;
2271 StorePlayer[jx][jy] = player->element_nr;
2272 player->jx = player->last_jx = jx;
2273 player->jy = player->last_jy = jy;
2283 /* when playing a tape, eliminate all players who do not participate */
2285 for (i = 0; i < MAX_PLAYERS; i++)
2287 if (stored_player[i].active && !tape.player_participates[i])
2289 struct PlayerInfo *player = &stored_player[i];
2290 int jx = player->jx, jy = player->jy;
2292 player->active = FALSE;
2293 StorePlayer[jx][jy] = 0;
2294 Feld[jx][jy] = EL_EMPTY;
2298 else if (!options.network && !setup.team_mode) /* && !tape.playing */
2300 /* when in single player mode, eliminate all but the first active player */
2302 for (i = 0; i < MAX_PLAYERS; i++)
2304 if (stored_player[i].active)
2306 for (j = i + 1; j < MAX_PLAYERS; j++)
2308 if (stored_player[j].active)
2310 struct PlayerInfo *player = &stored_player[j];
2311 int jx = player->jx, jy = player->jy;
2313 player->active = FALSE;
2314 player->present = FALSE;
2316 StorePlayer[jx][jy] = 0;
2317 Feld[jx][jy] = EL_EMPTY;
2324 /* when recording the game, store which players take part in the game */
2327 for (i = 0; i < MAX_PLAYERS; i++)
2328 if (stored_player[i].active)
2329 tape.player_participates[i] = TRUE;
2334 for (i = 0; i < MAX_PLAYERS; i++)
2336 struct PlayerInfo *player = &stored_player[i];
2338 printf("Player %d: present == %d, connected == %d, active == %d.\n",
2343 if (local_player == player)
2344 printf("Player %d is local player.\n", i+1);
2348 if (BorderElement == EL_EMPTY)
2351 SBX_Right = lev_fieldx - SCR_FIELDX;
2353 SBY_Lower = lev_fieldy - SCR_FIELDY;
2358 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
2360 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
2363 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
2364 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
2366 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
2367 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
2369 /* if local player not found, look for custom element that might create
2370 the player (make some assumptions about the right custom element) */
2371 if (!local_player->present)
2373 int start_x = 0, start_y = 0;
2374 int found_rating = 0;
2375 int found_element = EL_UNDEFINED;
2376 int player_nr = local_player->index_nr;
2379 SCAN_PLAYFIELD(x, y)
2381 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
2384 int element = Feld[x][y];
2389 if (level.use_start_element[player_nr] &&
2390 level.start_element[player_nr] == element &&
2397 found_element = element;
2400 if (!IS_CUSTOM_ELEMENT(element))
2403 if (CAN_CHANGE(element))
2405 for (i = 0; i < element_info[element].num_change_pages; i++)
2407 /* check for player created from custom element as single target */
2408 content = element_info[element].change_page[i].target_element;
2409 is_player = ELEM_IS_PLAYER(content);
2411 if (is_player && (found_rating < 3 || element < found_element))
2417 found_element = element;
2422 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
2424 /* check for player created from custom element as explosion content */
2425 content = element_info[element].content.e[xx][yy];
2426 is_player = ELEM_IS_PLAYER(content);
2428 if (is_player && (found_rating < 2 || element < found_element))
2430 start_x = x + xx - 1;
2431 start_y = y + yy - 1;
2434 found_element = element;
2437 if (!CAN_CHANGE(element))
2440 for (i = 0; i < element_info[element].num_change_pages; i++)
2442 /* check for player created from custom element as extended target */
2444 element_info[element].change_page[i].target_content.e[xx][yy];
2446 is_player = ELEM_IS_PLAYER(content);
2448 if (is_player && (found_rating < 1 || element < found_element))
2450 start_x = x + xx - 1;
2451 start_y = y + yy - 1;
2454 found_element = element;
2460 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
2461 start_x > SBX_Right + MIDPOSX ? SBX_Right :
2464 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
2465 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
2470 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2471 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2472 local_player->jx - MIDPOSX);
2474 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2475 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2476 local_player->jy - MIDPOSY);
2479 if (!game.restart_level)
2480 CloseDoor(DOOR_CLOSE_1);
2482 /* !!! FIX THIS (START) !!! */
2483 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2485 InitGameEngine_EM();
2492 /* after drawing the level, correct some elements */
2493 if (game.timegate_time_left == 0)
2494 CloseAllOpenTimegates();
2496 if (setup.soft_scrolling)
2497 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
2499 redraw_mask |= REDRAW_FROM_BACKBUFFER;
2502 /* !!! FIX THIS (END) !!! */
2504 if (!game.restart_level)
2506 /* copy default game door content to main double buffer */
2507 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2508 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2511 DrawGameDoorValues();
2513 if (!game.restart_level)
2517 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
2518 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
2519 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
2523 /* copy actual game door content to door double buffer for OpenDoor() */
2524 BlitBitmap(drawto, bitmap_db_door,
2525 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
2527 OpenDoor(DOOR_OPEN_ALL);
2529 PlaySoundStereo(SND_GAME_STARTING, SOUND_MIDDLE);
2531 if (setup.sound_music)
2534 KeyboardAutoRepeatOffUnlessAutoplay();
2538 for (i = 0; i < MAX_PLAYERS; i++)
2539 printf("Player %d %sactive.\n",
2540 i + 1, (stored_player[i].active ? "" : "not "));
2544 game.restart_level = FALSE;
2547 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
2549 /* this is used for non-R'n'D game engines to update certain engine values */
2551 /* needed to determine if sounds are played within the visible screen area */
2552 scroll_x = actual_scroll_x;
2553 scroll_y = actual_scroll_y;
2556 void InitMovDir(int x, int y)
2558 int i, element = Feld[x][y];
2559 static int xy[4][2] =
2566 static int direction[3][4] =
2568 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
2569 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
2570 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
2579 Feld[x][y] = EL_BUG;
2580 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
2583 case EL_SPACESHIP_RIGHT:
2584 case EL_SPACESHIP_UP:
2585 case EL_SPACESHIP_LEFT:
2586 case EL_SPACESHIP_DOWN:
2587 Feld[x][y] = EL_SPACESHIP;
2588 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
2591 case EL_BD_BUTTERFLY_RIGHT:
2592 case EL_BD_BUTTERFLY_UP:
2593 case EL_BD_BUTTERFLY_LEFT:
2594 case EL_BD_BUTTERFLY_DOWN:
2595 Feld[x][y] = EL_BD_BUTTERFLY;
2596 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
2599 case EL_BD_FIREFLY_RIGHT:
2600 case EL_BD_FIREFLY_UP:
2601 case EL_BD_FIREFLY_LEFT:
2602 case EL_BD_FIREFLY_DOWN:
2603 Feld[x][y] = EL_BD_FIREFLY;
2604 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
2607 case EL_PACMAN_RIGHT:
2609 case EL_PACMAN_LEFT:
2610 case EL_PACMAN_DOWN:
2611 Feld[x][y] = EL_PACMAN;
2612 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
2615 case EL_YAMYAM_LEFT:
2616 case EL_YAMYAM_RIGHT:
2618 case EL_YAMYAM_DOWN:
2619 Feld[x][y] = EL_YAMYAM;
2620 MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
2623 case EL_SP_SNIKSNAK:
2624 MovDir[x][y] = MV_UP;
2627 case EL_SP_ELECTRON:
2628 MovDir[x][y] = MV_LEFT;
2635 Feld[x][y] = EL_MOLE;
2636 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
2640 if (IS_CUSTOM_ELEMENT(element))
2642 struct ElementInfo *ei = &element_info[element];
2643 int move_direction_initial = ei->move_direction_initial;
2644 int move_pattern = ei->move_pattern;
2646 if (move_direction_initial == MV_START_PREVIOUS)
2648 if (MovDir[x][y] != MV_NONE)
2651 move_direction_initial = MV_START_AUTOMATIC;
2654 if (move_direction_initial == MV_START_RANDOM)
2655 MovDir[x][y] = 1 << RND(4);
2656 else if (move_direction_initial & MV_ANY_DIRECTION)
2657 MovDir[x][y] = move_direction_initial;
2658 else if (move_pattern == MV_ALL_DIRECTIONS ||
2659 move_pattern == MV_TURNING_LEFT ||
2660 move_pattern == MV_TURNING_RIGHT ||
2661 move_pattern == MV_TURNING_LEFT_RIGHT ||
2662 move_pattern == MV_TURNING_RIGHT_LEFT ||
2663 move_pattern == MV_TURNING_RANDOM)
2664 MovDir[x][y] = 1 << RND(4);
2665 else if (move_pattern == MV_HORIZONTAL)
2666 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
2667 else if (move_pattern == MV_VERTICAL)
2668 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
2669 else if (move_pattern & MV_ANY_DIRECTION)
2670 MovDir[x][y] = element_info[element].move_pattern;
2671 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
2672 move_pattern == MV_ALONG_RIGHT_SIDE)
2674 /* use random direction as default start direction */
2675 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2676 MovDir[x][y] = 1 << RND(4);
2678 for (i = 0; i < NUM_DIRECTIONS; i++)
2680 int x1 = x + xy[i][0];
2681 int y1 = y + xy[i][1];
2683 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2685 if (move_pattern == MV_ALONG_RIGHT_SIDE)
2686 MovDir[x][y] = direction[0][i];
2688 MovDir[x][y] = direction[1][i];
2697 MovDir[x][y] = 1 << RND(4);
2699 if (element != EL_BUG &&
2700 element != EL_SPACESHIP &&
2701 element != EL_BD_BUTTERFLY &&
2702 element != EL_BD_FIREFLY)
2705 for (i = 0; i < NUM_DIRECTIONS; i++)
2707 int x1 = x + xy[i][0];
2708 int y1 = y + xy[i][1];
2710 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2712 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
2714 MovDir[x][y] = direction[0][i];
2717 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
2718 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
2720 MovDir[x][y] = direction[1][i];
2729 GfxDir[x][y] = MovDir[x][y];
2732 void InitAmoebaNr(int x, int y)
2735 int group_nr = AmoebeNachbarNr(x, y);
2739 for (i = 1; i < MAX_NUM_AMOEBA; i++)
2741 if (AmoebaCnt[i] == 0)
2749 AmoebaNr[x][y] = group_nr;
2750 AmoebaCnt[group_nr]++;
2751 AmoebaCnt2[group_nr]++;
2757 boolean raise_level = FALSE;
2759 if (local_player->MovPos)
2762 if (tape.auto_play) /* tape might already be stopped here */
2763 tape.auto_play_level_solved = TRUE;
2765 local_player->LevelSolved = FALSE;
2767 PlaySoundStereo(SND_GAME_WINNING, SOUND_MIDDLE);
2771 if (!tape.playing && setup.sound_loops)
2772 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2773 SND_CTRL_PLAY_LOOP);
2775 while (TimeLeft > 0)
2777 if (!tape.playing && !setup.sound_loops)
2778 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2780 if (TimeLeft > 100 && TimeLeft % 10 == 0)
2783 RaiseScore(level.score[SC_TIME_BONUS] * 10);
2788 RaiseScore(level.score[SC_TIME_BONUS]);
2791 DrawGameValue_Time(TimeLeft);
2799 if (!tape.playing && setup.sound_loops)
2800 StopSound(SND_GAME_LEVELTIME_BONUS);
2802 else if (level.time == 0) /* level without time limit */
2804 if (!tape.playing && setup.sound_loops)
2805 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2806 SND_CTRL_PLAY_LOOP);
2808 while (TimePlayed < 999)
2810 if (!tape.playing && !setup.sound_loops)
2811 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2813 if (TimePlayed < 900 && TimePlayed % 10 == 0)
2816 RaiseScore(level.score[SC_TIME_BONUS] * 10);
2821 RaiseScore(level.score[SC_TIME_BONUS]);
2824 DrawGameValue_Time(TimePlayed);
2832 if (!tape.playing && setup.sound_loops)
2833 StopSound(SND_GAME_LEVELTIME_BONUS);
2836 /* close exit door after last player */
2837 if (AllPlayersGone && ExitX >= 0 && ExitY >= 0 &&
2838 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
2839 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN))
2841 int element = Feld[ExitX][ExitY];
2843 Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
2844 EL_SP_EXIT_CLOSING);
2846 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
2849 /* player disappears */
2850 if (ExitX >= 0 && ExitY >= 0)
2851 DrawLevelField(ExitX, ExitY);
2857 printf("::: TAPE PLAYING -> DO NOT SAVE SCORE\n");
2859 printf("::: NO TAPE PLAYING -> SAVING SCORE\n");
2865 CloseDoor(DOOR_CLOSE_1);
2870 SaveTape(tape.level_nr); /* Ask to save tape */
2873 if (level_nr == leveldir_current->handicap_level)
2875 leveldir_current->handicap_level++;
2876 SaveLevelSetup_SeriesInfo();
2879 if (level_editor_test_game)
2880 local_player->score = -1; /* no highscore when playing from editor */
2881 else if (level_nr < leveldir_current->last_level)
2882 raise_level = TRUE; /* advance to next level */
2884 if ((hi_pos = NewHiScore()) >= 0)
2886 game_status = GAME_MODE_SCORES;
2887 DrawHallOfFame(hi_pos);
2896 game_status = GAME_MODE_MAIN;
2913 LoadScore(level_nr);
2915 if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
2916 local_player->score < highscore[MAX_SCORE_ENTRIES - 1].Score)
2919 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
2921 if (local_player->score > highscore[k].Score)
2923 /* player has made it to the hall of fame */
2925 if (k < MAX_SCORE_ENTRIES - 1)
2927 int m = MAX_SCORE_ENTRIES - 1;
2930 for (l = k; l < MAX_SCORE_ENTRIES; l++)
2931 if (strEqual(setup.player_name, highscore[l].Name))
2933 if (m == k) /* player's new highscore overwrites his old one */
2937 for (l = m; l > k; l--)
2939 strcpy(highscore[l].Name, highscore[l - 1].Name);
2940 highscore[l].Score = highscore[l - 1].Score;
2947 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
2948 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
2949 highscore[k].Score = local_player->score;
2955 else if (!strncmp(setup.player_name, highscore[k].Name,
2956 MAX_PLAYER_NAME_LEN))
2957 break; /* player already there with a higher score */
2963 SaveScore(level_nr);
2968 inline static int getElementMoveStepsize(int x, int y)
2970 int element = Feld[x][y];
2971 int direction = MovDir[x][y];
2972 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2973 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2974 int horiz_move = (dx != 0);
2975 int sign = (horiz_move ? dx : dy);
2976 int step = sign * element_info[element].move_stepsize;
2978 /* special values for move stepsize for spring and things on conveyor belt */
2982 if (element == EL_SPRING)
2983 step = sign * MOVE_STEPSIZE_NORMAL * 2;
2984 else if (CAN_FALL(element) && !CAN_MOVE(element) &&
2985 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
2986 step = sign * MOVE_STEPSIZE_NORMAL / 2;
2988 if (CAN_FALL(element) &&
2989 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
2990 step = sign * MOVE_STEPSIZE_NORMAL / 2;
2991 else if (element == EL_SPRING)
2992 step = sign * MOVE_STEPSIZE_NORMAL * 2;
2999 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
3001 if (player->GfxAction != action || player->GfxDir != dir)
3004 printf("Player frame reset! (%d => %d, %d => %d)\n",
3005 player->GfxAction, action, player->GfxDir, dir);
3008 player->GfxAction = action;
3009 player->GfxDir = dir;
3011 player->StepFrame = 0;
3015 #if USE_GFX_RESET_GFX_ANIMATION
3016 static void ResetGfxFrame(int x, int y, boolean redraw)
3018 int element = Feld[x][y];
3019 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3020 int last_gfx_frame = GfxFrame[x][y];
3022 if (graphic_info[graphic].anim_global_sync)
3023 GfxFrame[x][y] = FrameCounter;
3024 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
3025 GfxFrame[x][y] = CustomValue[x][y];
3026 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
3027 GfxFrame[x][y] = element_info[element].collect_score;
3029 if (redraw && GfxFrame[x][y] != last_gfx_frame)
3030 DrawLevelGraphicAnimation(x, y, graphic);
3034 static void ResetGfxAnimation(int x, int y)
3037 int element, graphic;
3040 GfxAction[x][y] = ACTION_DEFAULT;
3041 GfxDir[x][y] = MovDir[x][y];
3045 element = Feld[x][y];
3046 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3048 if (graphic_info[graphic].anim_global_sync)
3049 GfxFrame[x][y] = FrameCounter;
3050 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
3051 GfxFrame[x][y] = CustomValue[x][y];
3052 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
3053 GfxFrame[x][y] = element_info[element].collect_score;
3056 #if USE_GFX_RESET_GFX_ANIMATION
3057 ResetGfxFrame(x, y, FALSE);
3061 static void ResetRandomAnimationValue(int x, int y)
3063 GfxRandom[x][y] = INIT_GFX_RANDOM();
3066 void InitMovingField(int x, int y, int direction)
3068 int element = Feld[x][y];
3072 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3073 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
3077 if (!WasJustMoving[x][y] || direction != MovDir[x][y])
3078 ResetGfxAnimation(x, y);
3080 MovDir[x][y] = direction;
3081 GfxDir[x][y] = direction;
3082 GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
3083 ACTION_FALLING : ACTION_MOVING);
3086 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3088 if (graphic_info[graphic].anim_global_sync)
3089 GfxFrame[x][y] = FrameCounter;
3090 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
3091 GfxFrame[x][y] = CustomValue[x][y];
3092 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
3093 GfxFrame[x][y] = element_info[element].collect_score;
3096 /* this is needed for CEs with property "can move" / "not moving" */
3098 if (getElementMoveStepsize(x, y) != 0) /* moving or being moved */
3100 if (Feld[newx][newy] == EL_EMPTY)
3101 Feld[newx][newy] = EL_BLOCKED;
3103 MovDir[newx][newy] = MovDir[x][y];
3105 #if USE_NEW_CUSTOM_VALUE
3106 CustomValue[newx][newy] = CustomValue[x][y];
3109 GfxFrame[newx][newy] = GfxFrame[x][y];
3110 GfxRandom[newx][newy] = GfxRandom[x][y];
3111 GfxAction[newx][newy] = GfxAction[x][y];
3112 GfxDir[newx][newy] = GfxDir[x][y];
3116 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
3118 int direction = MovDir[x][y];
3120 int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
3121 int newy = y + (direction & MV_UP ? -1 : direction & MV_DOWN ? +1 : 0);
3123 int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3124 int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
3131 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
3133 int oldx = x, oldy = y;
3134 int direction = MovDir[x][y];
3136 if (direction == MV_LEFT)
3138 else if (direction == MV_RIGHT)
3140 else if (direction == MV_UP)
3142 else if (direction == MV_DOWN)
3145 *comes_from_x = oldx;
3146 *comes_from_y = oldy;
3149 int MovingOrBlocked2Element(int x, int y)
3151 int element = Feld[x][y];
3153 if (element == EL_BLOCKED)
3157 Blocked2Moving(x, y, &oldx, &oldy);
3158 return Feld[oldx][oldy];
3164 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
3166 /* like MovingOrBlocked2Element(), but if element is moving
3167 and (x,y) is the field the moving element is just leaving,
3168 return EL_BLOCKED instead of the element value */
3169 int element = Feld[x][y];
3171 if (IS_MOVING(x, y))
3173 if (element == EL_BLOCKED)
3177 Blocked2Moving(x, y, &oldx, &oldy);
3178 return Feld[oldx][oldy];
3187 static void RemoveField(int x, int y)
3189 Feld[x][y] = EL_EMPTY;
3195 #if USE_NEW_CUSTOM_VALUE
3196 CustomValue[x][y] = 0;
3200 ChangeDelay[x][y] = 0;
3201 ChangePage[x][y] = -1;
3202 Pushed[x][y] = FALSE;
3205 ExplodeField[x][y] = EX_TYPE_NONE;
3208 GfxElement[x][y] = EL_UNDEFINED;
3209 GfxAction[x][y] = ACTION_DEFAULT;
3210 GfxDir[x][y] = MV_NONE;
3213 void RemoveMovingField(int x, int y)
3215 int oldx = x, oldy = y, newx = x, newy = y;
3216 int element = Feld[x][y];
3217 int next_element = EL_UNDEFINED;
3219 if (element != EL_BLOCKED && !IS_MOVING(x, y))
3222 if (IS_MOVING(x, y))
3224 Moving2Blocked(x, y, &newx, &newy);
3226 if (Feld[newx][newy] != EL_BLOCKED)
3228 /* element is moving, but target field is not free (blocked), but
3229 already occupied by something different (example: acid pool);
3230 in this case, only remove the moving field, but not the target */
3232 RemoveField(oldx, oldy);
3234 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
3236 DrawLevelField(oldx, oldy);
3241 else if (element == EL_BLOCKED)
3243 Blocked2Moving(x, y, &oldx, &oldy);
3244 if (!IS_MOVING(oldx, oldy))
3248 if (element == EL_BLOCKED &&
3249 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
3250 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
3251 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
3252 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
3253 next_element = get_next_element(Feld[oldx][oldy]);
3255 RemoveField(oldx, oldy);
3256 RemoveField(newx, newy);
3258 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
3260 if (next_element != EL_UNDEFINED)
3261 Feld[oldx][oldy] = next_element;
3263 DrawLevelField(oldx, oldy);
3264 DrawLevelField(newx, newy);
3267 void DrawDynamite(int x, int y)
3269 int sx = SCREENX(x), sy = SCREENY(y);
3270 int graphic = el2img(Feld[x][y]);
3273 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
3276 if (IS_WALKABLE_INSIDE(Back[x][y]))
3280 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
3281 else if (Store[x][y])
3282 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
3284 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
3286 if (Back[x][y] || Store[x][y])
3287 DrawGraphicThruMask(sx, sy, graphic, frame);
3289 DrawGraphic(sx, sy, graphic, frame);
3292 void CheckDynamite(int x, int y)
3294 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
3298 if (MovDelay[x][y] != 0)
3301 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
3307 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
3314 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
3316 boolean num_checked_players = 0;
3319 for (i = 0; i < MAX_PLAYERS; i++)
3321 if (stored_player[i].active)
3323 int sx = stored_player[i].jx;
3324 int sy = stored_player[i].jy;
3326 if (num_checked_players == 0)
3333 *sx1 = MIN(*sx1, sx);
3334 *sy1 = MIN(*sy1, sy);
3335 *sx2 = MAX(*sx2, sx);
3336 *sy2 = MAX(*sy2, sy);
3339 num_checked_players++;
3344 static boolean checkIfAllPlayersFitToScreen_RND()
3346 int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
3348 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
3350 return (sx2 - sx1 < SCR_FIELDX &&
3351 sy2 - sy1 < SCR_FIELDY);
3354 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
3356 int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
3358 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
3360 *sx = (sx1 + sx2) / 2;
3361 *sy = (sy1 + sy2) / 2;
3365 static void setMaxCenterDistanceForAllPlayers(int *max_dx, int *max_dy,
3366 int center_x, int center_y)
3368 int sx1 = center_x, sy1 = center_y, sx2 = center_x, sy2 = center_y;
3370 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
3372 *max_dx = MAX(ABS(sx1 - center_x), ABS(sx2 - center_x));
3373 *max_dy = MAX(ABS(sy1 - center_y), ABS(sy2 - center_y));
3376 static boolean checkIfAllPlayersAreVisible(int center_x, int center_y)
3380 setMaxCenterDistanceForAllPlayers(&max_dx, &max_dy, center_x, center_y);
3382 return (max_dx <= SCR_FIELDX / 2 &&
3383 max_dy <= SCR_FIELDY / 2);
3391 void DrawRelocateScreen(int x, int y, int move_dir, boolean center_screen,
3392 boolean quick_relocation)
3394 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3395 boolean no_delay = (tape.warp_forward);
3396 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
3397 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
3399 if (quick_relocation)
3401 int offset = (setup.scroll_delay ? 3 : 0);
3408 if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
3410 scroll_x = (x < SBX_Left + MIDPOSX ? SBX_Left :
3411 x > SBX_Right + MIDPOSX ? SBX_Right :
3414 scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
3415 y > SBY_Lower + MIDPOSY ? SBY_Lower :
3420 if ((move_dir == MV_LEFT && scroll_x > x - MIDPOSX + offset) ||
3421 (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
3422 scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
3424 if ((move_dir == MV_UP && scroll_y > y - MIDPOSY + offset) ||
3425 (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
3426 scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
3428 /* don't scroll over playfield boundaries */
3429 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
3430 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
3432 /* don't scroll over playfield boundaries */
3433 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
3434 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
3437 RedrawPlayfield(TRUE, 0,0,0,0);
3441 int scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left :
3442 x > SBX_Right + MIDPOSX ? SBX_Right :
3445 int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
3446 y > SBY_Lower + MIDPOSY ? SBY_Lower :
3449 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
3451 while (scroll_x != scroll_xx || scroll_y != scroll_yy)
3454 int fx = FX, fy = FY;
3456 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
3457 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
3459 if (dx == 0 && dy == 0) /* no scrolling needed at all */
3465 fx += dx * TILEX / 2;
3466 fy += dy * TILEY / 2;
3468 ScrollLevel(dx, dy);
3471 /* scroll in two steps of half tile size to make things smoother */
3472 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
3474 Delay(wait_delay_value);
3476 /* scroll second step to align at full tile size */
3478 Delay(wait_delay_value);
3483 Delay(wait_delay_value);
3489 void DrawRelocatePlayer(struct PlayerInfo *player, boolean quick_relocation)
3491 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3492 boolean no_delay = (tape.warp_forward);
3493 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
3494 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
3495 int jx = player->jx;
3496 int jy = player->jy;
3498 if (quick_relocation)
3500 int offset = (setup.scroll_delay ? 3 : 0);
3502 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
3504 scroll_x = (player->jx < SBX_Left + MIDPOSX ? SBX_Left :
3505 player->jx > SBX_Right + MIDPOSX ? SBX_Right :
3506 player->jx - MIDPOSX);
3508 scroll_y = (player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
3509 player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
3510 player->jy - MIDPOSY);
3514 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
3515 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
3516 scroll_x = jx - MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
3518 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
3519 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
3520 scroll_y = jy - MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
3522 /* don't scroll over playfield boundaries */
3523 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
3524 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
3526 /* don't scroll over playfield boundaries */
3527 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
3528 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
3531 RedrawPlayfield(TRUE, 0,0,0,0);
3535 int scroll_xx = (player->jx < SBX_Left + MIDPOSX ? SBX_Left :
3536 player->jx > SBX_Right + MIDPOSX ? SBX_Right :
3537 player->jx - MIDPOSX);
3539 int scroll_yy = (player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
3540 player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
3541 player->jy - MIDPOSY);
3543 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
3545 while (scroll_x != scroll_xx || scroll_y != scroll_yy)
3548 int fx = FX, fy = FY;
3550 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
3551 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
3553 if (dx == 0 && dy == 0) /* no scrolling needed at all */
3559 fx += dx * TILEX / 2;
3560 fy += dy * TILEY / 2;
3562 ScrollLevel(dx, dy);
3565 /* scroll in two steps of half tile size to make things smoother */
3566 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
3568 Delay(wait_delay_value);
3570 /* scroll second step to align at full tile size */
3572 Delay(wait_delay_value);
3577 Delay(wait_delay_value);
3583 void RelocatePlayer(int jx, int jy, int el_player_raw)
3585 int el_player = GET_PLAYER_ELEMENT(el_player_raw);
3586 int player_nr = GET_PLAYER_NR(el_player);
3587 struct PlayerInfo *player = &stored_player[player_nr];
3588 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3589 boolean no_delay = (tape.warp_forward);
3590 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
3591 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
3592 int old_jx = player->jx;
3593 int old_jy = player->jy;
3594 int old_element = Feld[old_jx][old_jy];
3595 int element = Feld[jx][jy];
3596 boolean player_relocated = (old_jx != jx || old_jy != jy);
3598 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
3599 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
3600 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
3601 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
3602 int leave_side_horiz = move_dir_horiz;
3603 int leave_side_vert = move_dir_vert;
3604 int enter_side = enter_side_horiz | enter_side_vert;
3605 int leave_side = leave_side_horiz | leave_side_vert;
3607 if (player->GameOver) /* do not reanimate dead player */
3610 if (!player_relocated) /* no need to relocate the player */
3613 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
3615 RemoveField(jx, jy); /* temporarily remove newly placed player */
3616 DrawLevelField(jx, jy);
3619 if (player->present)
3621 while (player->MovPos)
3623 ScrollPlayer(player, SCROLL_GO_ON);
3624 ScrollScreen(NULL, SCROLL_GO_ON);
3626 AdvanceFrameAndPlayerCounters(player->index_nr);
3631 Delay(wait_delay_value);
3634 DrawPlayer(player); /* needed here only to cleanup last field */
3635 DrawLevelField(player->jx, player->jy); /* remove player graphic */
3637 player->is_moving = FALSE;
3640 if (IS_CUSTOM_ELEMENT(old_element))
3641 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
3643 player->index_bit, leave_side);
3645 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
3647 player->index_bit, leave_side);
3649 Feld[jx][jy] = el_player;
3650 InitPlayerField(jx, jy, el_player, TRUE);
3652 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
3654 Feld[jx][jy] = element;
3655 InitField(jx, jy, FALSE);
3659 /* only visually relocate centered player */
3661 DrawRelocateScreen(player->jx, player->jy, player->MovDir, FALSE,
3662 level.instant_relocation);
3664 if (player->index_nr == game.centered_player_nr)
3665 DrawRelocatePlayer(player, level.instant_relocation);
3668 if (player == local_player) /* only visually relocate local player */
3669 DrawRelocatePlayer(player, level.instant_relocation);
3672 TestIfPlayerTouchesBadThing(jx, jy);
3673 TestIfPlayerTouchesCustomElement(jx, jy);
3675 if (IS_CUSTOM_ELEMENT(element))
3676 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
3677 player->index_bit, enter_side);
3679 CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
3680 player->index_bit, enter_side);
3683 void Explode(int ex, int ey, int phase, int mode)
3689 /* !!! eliminate this variable !!! */
3690 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
3692 if (game.explosions_delayed)
3694 ExplodeField[ex][ey] = mode;
3698 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
3700 int center_element = Feld[ex][ey];
3701 int artwork_element, explosion_element; /* set these values later */
3704 /* --- This is only really needed (and now handled) in "Impact()". --- */
3705 /* do not explode moving elements that left the explode field in time */
3706 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
3707 center_element == EL_EMPTY &&
3708 (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
3713 /* !!! at this place, the center element may be EL_BLOCKED !!! */
3714 if (mode == EX_TYPE_NORMAL ||
3715 mode == EX_TYPE_CENTER ||
3716 mode == EX_TYPE_CROSS)
3717 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
3720 /* remove things displayed in background while burning dynamite */
3721 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
3724 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
3726 /* put moving element to center field (and let it explode there) */
3727 center_element = MovingOrBlocked2Element(ex, ey);
3728 RemoveMovingField(ex, ey);
3729 Feld[ex][ey] = center_element;
3732 /* now "center_element" is finally determined -- set related values now */
3733 artwork_element = center_element; /* for custom player artwork */
3734 explosion_element = center_element; /* for custom player artwork */
3736 if (IS_PLAYER(ex, ey))
3738 int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
3740 artwork_element = stored_player[player_nr].artwork_element;
3742 if (level.use_explosion_element[player_nr])
3744 explosion_element = level.explosion_element[player_nr];
3745 artwork_element = explosion_element;
3750 if (mode == EX_TYPE_NORMAL ||
3751 mode == EX_TYPE_CENTER ||
3752 mode == EX_TYPE_CROSS)
3753 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
3757 last_phase = element_info[explosion_element].explosion_delay + 1;
3759 last_phase = element_info[center_element].explosion_delay + 1;
3762 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
3764 int xx = x - ex + 1;
3765 int yy = y - ey + 1;
3768 if (!IN_LEV_FIELD(x, y) ||
3769 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
3770 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
3773 element = Feld[x][y];
3775 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
3777 element = MovingOrBlocked2Element(x, y);
3779 if (!IS_EXPLOSION_PROOF(element))
3780 RemoveMovingField(x, y);
3783 /* indestructible elements can only explode in center (but not flames) */
3784 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
3785 mode == EX_TYPE_BORDER)) ||
3786 element == EL_FLAMES)
3789 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
3790 behaviour, for example when touching a yamyam that explodes to rocks
3791 with active deadly shield, a rock is created under the player !!! */
3792 /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
3794 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
3795 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
3796 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
3798 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
3801 if (IS_ACTIVE_BOMB(element))
3803 /* re-activate things under the bomb like gate or penguin */
3804 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
3811 /* save walkable background elements while explosion on same tile */
3812 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
3813 (x != ex || y != ey || mode == EX_TYPE_BORDER))
3814 Back[x][y] = element;
3816 /* ignite explodable elements reached by other explosion */
3817 if (element == EL_EXPLOSION)
3818 element = Store2[x][y];
3820 if (AmoebaNr[x][y] &&
3821 (element == EL_AMOEBA_FULL ||
3822 element == EL_BD_AMOEBA ||
3823 element == EL_AMOEBA_GROWING))
3825 AmoebaCnt[AmoebaNr[x][y]]--;
3826 AmoebaCnt2[AmoebaNr[x][y]]--;
3831 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
3834 int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
3836 Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
3838 switch(StorePlayer[ex][ey])
3841 Store[x][y] = EL_PLAYER_IS_EXPLODING_2;
3844 Store[x][y] = EL_PLAYER_IS_EXPLODING_3;
3847 Store[x][y] = EL_PLAYER_IS_EXPLODING_4;
3851 Store[x][y] = EL_PLAYER_IS_EXPLODING_1;
3856 if (PLAYERINFO(ex, ey)->use_murphy)
3857 Store[x][y] = EL_EMPTY;
3860 /* !!! check this case -- currently needed for rnd_rado_negundo_v,
3861 !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
3862 else if (ELEM_IS_PLAYER(center_element))
3863 Store[x][y] = EL_EMPTY;
3864 else if (center_element == EL_YAMYAM)
3865 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
3866 else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
3867 Store[x][y] = element_info[center_element].content.e[xx][yy];
3869 /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
3870 (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
3871 otherwise) -- FIX THIS !!! */
3872 else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
3873 Store[x][y] = element_info[element].content.e[1][1];
3875 else if (!CAN_EXPLODE(element))
3876 Store[x][y] = element_info[element].content.e[1][1];
3879 Store[x][y] = EL_EMPTY;
3881 else if (center_element == EL_MOLE)
3882 Store[x][y] = EL_EMERALD_RED;
3883 else if (center_element == EL_PENGUIN)
3884 Store[x][y] = EL_EMERALD_PURPLE;
3885 else if (center_element == EL_BUG)
3886 Store[x][y] = ((x == ex && y == ey) ? EL_DIAMOND : EL_EMERALD);
3887 else if (center_element == EL_BD_BUTTERFLY)
3888 Store[x][y] = EL_BD_DIAMOND;
3889 else if (center_element == EL_SP_ELECTRON)
3890 Store[x][y] = EL_SP_INFOTRON;
3891 else if (center_element == EL_AMOEBA_TO_DIAMOND)
3892 Store[x][y] = level.amoeba_content;
3893 else if (center_element == EL_YAMYAM)
3894 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
3895 else if (IS_CUSTOM_ELEMENT(center_element) &&
3896 element_info[center_element].content.e[xx][yy] != EL_EMPTY)
3897 Store[x][y] = element_info[center_element].content.e[xx][yy];
3898 else if (element == EL_WALL_EMERALD)
3899 Store[x][y] = EL_EMERALD;
3900 else if (element == EL_WALL_DIAMOND)
3901 Store[x][y] = EL_DIAMOND;
3902 else if (element == EL_WALL_BD_DIAMOND)
3903 Store[x][y] = EL_BD_DIAMOND;
3904 else if (element == EL_WALL_EMERALD_YELLOW)
3905 Store[x][y] = EL_EMERALD_YELLOW;
3906 else if (element == EL_WALL_EMERALD_RED)
3907 Store[x][y] = EL_EMERALD_RED;
3908 else if (element == EL_WALL_EMERALD_PURPLE)
3909 Store[x][y] = EL_EMERALD_PURPLE;
3910 else if (element == EL_WALL_PEARL)
3911 Store[x][y] = EL_PEARL;
3912 else if (element == EL_WALL_CRYSTAL)
3913 Store[x][y] = EL_CRYSTAL;
3914 else if (IS_CUSTOM_ELEMENT(element) && !CAN_EXPLODE(element))
3915 Store[x][y] = element_info[element].content.e[1][1];
3917 Store[x][y] = EL_EMPTY;
3920 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
3921 center_element == EL_AMOEBA_TO_DIAMOND)
3922 Store2[x][y] = element;
3924 Feld[x][y] = EL_EXPLOSION;
3925 GfxElement[x][y] = artwork_element;
3928 printf(":: setting gfx(%d,%d) to %d ['%s']\n",
3929 x, y, artwork_element, EL_NAME(artwork_element));
3932 ExplodePhase[x][y] = 1;
3933 ExplodeDelay[x][y] = last_phase;
3938 if (center_element == EL_YAMYAM)
3939 game.yamyam_content_nr =
3940 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
3952 GfxFrame[x][y] = 0; /* restart explosion animation */
3954 last_phase = ExplodeDelay[x][y];
3956 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
3960 /* activate this even in non-DEBUG version until cause for crash in
3961 getGraphicAnimationFrame() (see below) is found and eliminated */
3967 /* this can happen if the player leaves an explosion just in time */
3968 if (GfxElement[x][y] == EL_UNDEFINED)
3969 GfxElement[x][y] = EL_EMPTY;
3971 if (GfxElement[x][y] == EL_UNDEFINED)
3974 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
3975 printf("Explode(): This should never happen!\n");
3978 GfxElement[x][y] = EL_EMPTY;
3984 border_element = Store2[x][y];
3985 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3986 border_element = StorePlayer[x][y];
3988 if (phase == element_info[border_element].ignition_delay ||
3989 phase == last_phase)
3991 boolean border_explosion = FALSE;
3993 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
3994 !PLAYER_EXPLOSION_PROTECTED(x, y))
3996 KillPlayerUnlessExplosionProtected(x, y);
3997 border_explosion = TRUE;
3999 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
4001 Feld[x][y] = Store2[x][y];
4004 border_explosion = TRUE;
4006 else if (border_element == EL_AMOEBA_TO_DIAMOND)
4008 AmoebeUmwandeln(x, y);
4010 border_explosion = TRUE;
4013 /* if an element just explodes due to another explosion (chain-reaction),
4014 do not immediately end the new explosion when it was the last frame of
4015 the explosion (as it would be done in the following "if"-statement!) */
4016 if (border_explosion && phase == last_phase)
4020 if (phase == last_phase)
4024 element = Feld[x][y] = Store[x][y];
4025 Store[x][y] = Store2[x][y] = 0;
4026 GfxElement[x][y] = EL_UNDEFINED;
4028 /* player can escape from explosions and might therefore be still alive */
4029 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
4030 element <= EL_PLAYER_IS_EXPLODING_4)
4032 int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
4033 int explosion_element = EL_PLAYER_1 + player_nr;
4034 int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
4035 int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
4037 if (level.use_explosion_element[player_nr])
4038 explosion_element = level.explosion_element[player_nr];
4040 Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
4041 element_info[explosion_element].content.e[xx][yy]);
4044 /* restore probably existing indestructible background element */
4045 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
4046 element = Feld[x][y] = Back[x][y];
4049 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
4050 GfxDir[x][y] = MV_NONE;
4051 ChangeDelay[x][y] = 0;
4052 ChangePage[x][y] = -1;
4054 #if USE_NEW_CUSTOM_VALUE
4055 CustomValue[x][y] = 0;
4058 InitField_WithBug2(x, y, FALSE);
4060 DrawLevelField(x, y);
4062 TestIfElementTouchesCustomElement(x, y);
4064 if (GFX_CRUMBLED(element))
4065 DrawLevelFieldCrumbledSandNeighbours(x, y);
4067 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
4068 StorePlayer[x][y] = 0;
4070 if (ELEM_IS_PLAYER(element))
4071 RelocatePlayer(x, y, element);
4073 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
4075 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
4076 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4079 DrawLevelFieldCrumbledSand(x, y);
4081 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
4083 DrawLevelElement(x, y, Back[x][y]);
4084 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
4086 else if (IS_WALKABLE_UNDER(Back[x][y]))
4088 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
4089 DrawLevelElementThruMask(x, y, Back[x][y]);
4091 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
4092 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
4096 void DynaExplode(int ex, int ey)
4099 int dynabomb_element = Feld[ex][ey];
4100 int dynabomb_size = 1;
4101 boolean dynabomb_xl = FALSE;
4102 struct PlayerInfo *player;
4103 static int xy[4][2] =
4111 if (IS_ACTIVE_BOMB(dynabomb_element))
4113 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
4114 dynabomb_size = player->dynabomb_size;
4115 dynabomb_xl = player->dynabomb_xl;
4116 player->dynabombs_left++;
4119 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
4121 for (i = 0; i < NUM_DIRECTIONS; i++)
4123 for (j = 1; j <= dynabomb_size; j++)
4125 int x = ex + j * xy[i][0];
4126 int y = ey + j * xy[i][1];
4129 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
4132 element = Feld[x][y];
4134 /* do not restart explosions of fields with active bombs */
4135 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
4138 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
4140 if (element != EL_EMPTY && element != EL_EXPLOSION &&
4141 !IS_DIGGABLE(element) && !dynabomb_xl)
4147 void Bang(int x, int y)
4149 int element = MovingOrBlocked2Element(x, y);
4150 int explosion_type = EX_TYPE_NORMAL;
4152 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
4154 struct PlayerInfo *player = PLAYERINFO(x, y);
4156 element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
4157 player->element_nr);
4159 if (level.use_explosion_element[player->index_nr])
4161 int explosion_element = level.explosion_element[player->index_nr];
4163 if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
4164 explosion_type = EX_TYPE_CROSS;
4165 else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
4166 explosion_type = EX_TYPE_CENTER;
4174 case EL_BD_BUTTERFLY:
4177 case EL_DARK_YAMYAM:
4181 RaiseScoreElement(element);
4184 case EL_DYNABOMB_PLAYER_1_ACTIVE:
4185 case EL_DYNABOMB_PLAYER_2_ACTIVE:
4186 case EL_DYNABOMB_PLAYER_3_ACTIVE:
4187 case EL_DYNABOMB_PLAYER_4_ACTIVE:
4188 case EL_DYNABOMB_INCREASE_NUMBER:
4189 case EL_DYNABOMB_INCREASE_SIZE:
4190 case EL_DYNABOMB_INCREASE_POWER:
4191 explosion_type = EX_TYPE_DYNA;
4196 case EL_LAMP_ACTIVE:
4197 case EL_AMOEBA_TO_DIAMOND:
4198 if (!IS_PLAYER(x, y)) /* penguin and player may be at same field */
4199 explosion_type = EX_TYPE_CENTER;
4203 if (element_info[element].explosion_type == EXPLODES_CROSS)
4204 explosion_type = EX_TYPE_CROSS;
4205 else if (element_info[element].explosion_type == EXPLODES_1X1)
4206 explosion_type = EX_TYPE_CENTER;
4210 if (explosion_type == EX_TYPE_DYNA)
4213 Explode(x, y, EX_PHASE_START, explosion_type);
4215 CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
4218 void SplashAcid(int x, int y)
4220 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
4221 (!IN_LEV_FIELD(x - 1, y - 2) ||
4222 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
4223 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
4225 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
4226 (!IN_LEV_FIELD(x + 1, y - 2) ||
4227 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
4228 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
4230 PlayLevelSound(x, y, SND_ACID_SPLASHING);
4233 static void InitBeltMovement()
4235 static int belt_base_element[4] =
4237 EL_CONVEYOR_BELT_1_LEFT,
4238 EL_CONVEYOR_BELT_2_LEFT,
4239 EL_CONVEYOR_BELT_3_LEFT,
4240 EL_CONVEYOR_BELT_4_LEFT
4242 static int belt_base_active_element[4] =
4244 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
4245 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
4246 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
4247 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
4252 /* set frame order for belt animation graphic according to belt direction */
4253 for (i = 0; i < NUM_BELTS; i++)
4257 for (j = 0; j < NUM_BELT_PARTS; j++)
4259 int element = belt_base_active_element[belt_nr] + j;
4260 int graphic = el2img(element);
4262 if (game.belt_dir[i] == MV_LEFT)
4263 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
4265 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
4270 SCAN_PLAYFIELD(x, y)
4272 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
4275 int element = Feld[x][y];
4277 for (i = 0; i < NUM_BELTS; i++)
4279 if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
4281 int e_belt_nr = getBeltNrFromBeltElement(element);
4284 if (e_belt_nr == belt_nr)
4286 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
4288 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
4295 static void ToggleBeltSwitch(int x, int y)
4297 static int belt_base_element[4] =
4299 EL_CONVEYOR_BELT_1_LEFT,
4300 EL_CONVEYOR_BELT_2_LEFT,
4301 EL_CONVEYOR_BELT_3_LEFT,
4302 EL_CONVEYOR_BELT_4_LEFT
4304 static int belt_base_active_element[4] =
4306 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
4307 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
4308 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
4309 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
4311 static int belt_base_switch_element[4] =
4313 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
4314 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
4315 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
4316 EL_CONVEYOR_BELT_4_SWITCH_LEFT
4318 static int belt_move_dir[4] =
4326 int element = Feld[x][y];
4327 int belt_nr = getBeltNrFromBeltSwitchElement(element);
4328 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
4329 int belt_dir = belt_move_dir[belt_dir_nr];
4332 if (!IS_BELT_SWITCH(element))
4335 game.belt_dir_nr[belt_nr] = belt_dir_nr;
4336 game.belt_dir[belt_nr] = belt_dir;
4338 if (belt_dir_nr == 3)
4341 /* set frame order for belt animation graphic according to belt direction */
4342 for (i = 0; i < NUM_BELT_PARTS; i++)
4344 int element = belt_base_active_element[belt_nr] + i;
4345 int graphic = el2img(element);
4347 if (belt_dir == MV_LEFT)
4348 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
4350 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
4354 SCAN_PLAYFIELD(xx, yy)
4356 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
4359 int element = Feld[xx][yy];
4361 if (IS_BELT_SWITCH(element))
4363 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
4365 if (e_belt_nr == belt_nr)
4367 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
4368 DrawLevelField(xx, yy);
4371 else if (IS_BELT(element) && belt_dir != MV_NONE)
4373 int e_belt_nr = getBeltNrFromBeltElement(element);
4375 if (e_belt_nr == belt_nr)
4377 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
4379 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
4380 DrawLevelField(xx, yy);
4383 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
4385 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
4387 if (e_belt_nr == belt_nr)
4389 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
4391 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
4392 DrawLevelField(xx, yy);
4398 static void ToggleSwitchgateSwitch(int x, int y)
4402 game.switchgate_pos = !game.switchgate_pos;
4405 SCAN_PLAYFIELD(xx, yy)
4407 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
4410 int element = Feld[xx][yy];
4412 if (element == EL_SWITCHGATE_SWITCH_UP ||
4413 element == EL_SWITCHGATE_SWITCH_DOWN)
4415 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
4416 DrawLevelField(xx, yy);
4418 else if (element == EL_SWITCHGATE_OPEN ||
4419 element == EL_SWITCHGATE_OPENING)
4421 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
4423 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
4425 else if (element == EL_SWITCHGATE_CLOSED ||
4426 element == EL_SWITCHGATE_CLOSING)
4428 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
4430 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
4435 static int getInvisibleActiveFromInvisibleElement(int element)
4437 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
4438 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
4439 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
4443 static int getInvisibleFromInvisibleActiveElement(int element)
4445 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
4446 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
4447 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
4451 static void RedrawAllLightSwitchesAndInvisibleElements()
4456 SCAN_PLAYFIELD(x, y)
4458 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
4461 int element = Feld[x][y];
4463 if (element == EL_LIGHT_SWITCH &&
4464 game.light_time_left > 0)
4466 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
4467 DrawLevelField(x, y);
4469 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
4470 game.light_time_left == 0)
4472 Feld[x][y] = EL_LIGHT_SWITCH;
4473 DrawLevelField(x, y);
4475 else if (element == EL_EMC_DRIPPER &&
4476 game.light_time_left > 0)
4478 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
4479 DrawLevelField(x, y);
4481 else if (element == EL_EMC_DRIPPER_ACTIVE &&
4482 game.light_time_left == 0)
4484 Feld[x][y] = EL_EMC_DRIPPER;
4485 DrawLevelField(x, y);
4487 else if (element == EL_INVISIBLE_STEELWALL ||
4488 element == EL_INVISIBLE_WALL ||
4489 element == EL_INVISIBLE_SAND)
4491 if (game.light_time_left > 0)
4492 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
4494 DrawLevelField(x, y);
4496 /* uncrumble neighbour fields, if needed */
4497 if (element == EL_INVISIBLE_SAND)
4498 DrawLevelFieldCrumbledSandNeighbours(x, y);
4500 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
4501 element == EL_INVISIBLE_WALL_ACTIVE ||
4502 element == EL_INVISIBLE_SAND_ACTIVE)
4504 if (game.light_time_left == 0)
4505 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
4507 DrawLevelField(x, y);
4509 /* re-crumble neighbour fields, if needed */
4510 if (element == EL_INVISIBLE_SAND)
4511 DrawLevelFieldCrumbledSandNeighbours(x, y);
4516 static void RedrawAllInvisibleElementsForLenses()
4521 SCAN_PLAYFIELD(x, y)
4523 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
4526 int element = Feld[x][y];
4528 if (element == EL_EMC_DRIPPER &&
4529 game.lenses_time_left > 0)
4531 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
4532 DrawLevelField(x, y);
4534 else if (element == EL_EMC_DRIPPER_ACTIVE &&
4535 game.lenses_time_left == 0)
4537 Feld[x][y] = EL_EMC_DRIPPER;
4538 DrawLevelField(x, y);
4540 else if (element == EL_INVISIBLE_STEELWALL ||
4541 element == EL_INVISIBLE_WALL ||
4542 element == EL_INVISIBLE_SAND)
4544 if (game.lenses_time_left > 0)
4545 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
4547 DrawLevelField(x, y);
4549 /* uncrumble neighbour fields, if needed */
4550 if (element == EL_INVISIBLE_SAND)
4551 DrawLevelFieldCrumbledSandNeighbours(x, y);
4553 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
4554 element == EL_INVISIBLE_WALL_ACTIVE ||
4555 element == EL_INVISIBLE_SAND_ACTIVE)
4557 if (game.lenses_time_left == 0)
4558 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
4560 DrawLevelField(x, y);
4562 /* re-crumble neighbour fields, if needed */
4563 if (element == EL_INVISIBLE_SAND)
4564 DrawLevelFieldCrumbledSandNeighbours(x, y);
4569 static void RedrawAllInvisibleElementsForMagnifier()
4574 SCAN_PLAYFIELD(x, y)
4576 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
4579 int element = Feld[x][y];
4581 if (element == EL_EMC_FAKE_GRASS &&
4582 game.magnify_time_left > 0)
4584 Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
4585 DrawLevelField(x, y);
4587 else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
4588 game.magnify_time_left == 0)
4590 Feld[x][y] = EL_EMC_FAKE_GRASS;
4591 DrawLevelField(x, y);
4593 else if (IS_GATE_GRAY(element) &&
4594 game.magnify_time_left > 0)
4596 Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
4597 element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
4598 IS_EM_GATE_GRAY(element) ?
4599 element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
4600 IS_EMC_GATE_GRAY(element) ?
4601 element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
4603 DrawLevelField(x, y);
4605 else if (IS_GATE_GRAY_ACTIVE(element) &&
4606 game.magnify_time_left == 0)
4608 Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
4609 element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
4610 IS_EM_GATE_GRAY_ACTIVE(element) ?
4611 element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
4612 IS_EMC_GATE_GRAY_ACTIVE(element) ?
4613 element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
4615 DrawLevelField(x, y);
4620 static void ToggleLightSwitch(int x, int y)
4622 int element = Feld[x][y];
4624 game.light_time_left =
4625 (element == EL_LIGHT_SWITCH ?
4626 level.time_light * FRAMES_PER_SECOND : 0);
4628 RedrawAllLightSwitchesAndInvisibleElements();
4631 static void ActivateTimegateSwitch(int x, int y)
4635 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
4638 SCAN_PLAYFIELD(xx, yy)
4640 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
4643 int element = Feld[xx][yy];
4645 if (element == EL_TIMEGATE_CLOSED ||
4646 element == EL_TIMEGATE_CLOSING)
4648 Feld[xx][yy] = EL_TIMEGATE_OPENING;
4649 PlayLevelSound(xx, yy, SND_TIMEGATE_OPENING);
4653 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
4655 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
4656 DrawLevelField(xx, yy);
4662 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
4665 void Impact(int x, int y)
4667 boolean last_line = (y == lev_fieldy - 1);
4668 boolean object_hit = FALSE;
4669 boolean impact = (last_line || object_hit);
4670 int element = Feld[x][y];
4671 int smashed = EL_STEELWALL;
4673 if (!last_line) /* check if element below was hit */
4675 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
4678 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
4679 MovDir[x][y + 1] != MV_DOWN ||
4680 MovPos[x][y + 1] <= TILEY / 2));
4682 /* do not smash moving elements that left the smashed field in time */
4683 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
4684 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
4687 #if USE_QUICKSAND_IMPACT_BUGFIX
4688 if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
4690 RemoveMovingField(x, y + 1);
4691 Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
4692 Feld[x][y + 2] = EL_ROCK;
4693 DrawLevelField(x, y + 2);
4700 smashed = MovingOrBlocked2Element(x, y + 1);
4702 impact = (last_line || object_hit);
4705 if (!last_line && smashed == EL_ACID) /* element falls into acid */
4707 SplashAcid(x, y + 1);
4711 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
4712 /* only reset graphic animation if graphic really changes after impact */
4714 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
4716 ResetGfxAnimation(x, y);
4717 DrawLevelField(x, y);
4720 if (impact && CAN_EXPLODE_IMPACT(element))
4725 else if (impact && element == EL_PEARL)
4727 ResetGfxAnimation(x, y);
4729 Feld[x][y] = EL_PEARL_BREAKING;
4730 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4733 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
4735 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4740 if (impact && element == EL_AMOEBA_DROP)
4742 if (object_hit && IS_PLAYER(x, y + 1))
4743 KillPlayerUnlessEnemyProtected(x, y + 1);
4744 else if (object_hit && smashed == EL_PENGUIN)
4748 Feld[x][y] = EL_AMOEBA_GROWING;
4749 Store[x][y] = EL_AMOEBA_WET;
4751 ResetRandomAnimationValue(x, y);
4756 if (object_hit) /* check which object was hit */
4758 if (CAN_PASS_MAGIC_WALL(element) &&
4759 (smashed == EL_MAGIC_WALL ||
4760 smashed == EL_BD_MAGIC_WALL))
4763 int activated_magic_wall =
4764 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
4765 EL_BD_MAGIC_WALL_ACTIVE);
4767 /* activate magic wall / mill */
4769 SCAN_PLAYFIELD(xx, yy)
4771 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
4773 if (Feld[xx][yy] == smashed)
4774 Feld[xx][yy] = activated_magic_wall;
4776 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
4777 game.magic_wall_active = TRUE;
4779 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
4780 SND_MAGIC_WALL_ACTIVATING :
4781 SND_BD_MAGIC_WALL_ACTIVATING));
4784 if (IS_PLAYER(x, y + 1))
4786 if (CAN_SMASH_PLAYER(element))
4788 KillPlayerUnlessEnemyProtected(x, y + 1);
4792 else if (smashed == EL_PENGUIN)
4794 if (CAN_SMASH_PLAYER(element))
4800 else if (element == EL_BD_DIAMOND)
4802 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
4808 else if (((element == EL_SP_INFOTRON ||
4809 element == EL_SP_ZONK) &&
4810 (smashed == EL_SP_SNIKSNAK ||
4811 smashed == EL_SP_ELECTRON ||
4812 smashed == EL_SP_DISK_ORANGE)) ||
4813 (element == EL_SP_INFOTRON &&
4814 smashed == EL_SP_DISK_YELLOW))
4819 else if (CAN_SMASH_EVERYTHING(element))
4821 if (IS_CLASSIC_ENEMY(smashed) ||
4822 CAN_EXPLODE_SMASHED(smashed))
4827 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
4829 if (smashed == EL_LAMP ||
4830 smashed == EL_LAMP_ACTIVE)
4835 else if (smashed == EL_NUT)
4837 Feld[x][y + 1] = EL_NUT_BREAKING;
4838 PlayLevelSound(x, y, SND_NUT_BREAKING);
4839 RaiseScoreElement(EL_NUT);
4842 else if (smashed == EL_PEARL)
4844 ResetGfxAnimation(x, y);
4846 Feld[x][y + 1] = EL_PEARL_BREAKING;
4847 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4850 else if (smashed == EL_DIAMOND)
4852 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
4853 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
4856 else if (IS_BELT_SWITCH(smashed))
4858 ToggleBeltSwitch(x, y + 1);
4860 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
4861 smashed == EL_SWITCHGATE_SWITCH_DOWN)
4863 ToggleSwitchgateSwitch(x, y + 1);
4865 else if (smashed == EL_LIGHT_SWITCH ||
4866 smashed == EL_LIGHT_SWITCH_ACTIVE)
4868 ToggleLightSwitch(x, y + 1);
4873 TestIfElementSmashesCustomElement(x, y, MV_DOWN);
4876 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4878 CheckElementChangeBySide(x, y + 1, smashed, element,
4879 CE_SWITCHED, CH_SIDE_TOP);
4880 CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
4886 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4891 /* play sound of magic wall / mill */
4893 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
4894 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
4896 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
4897 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
4898 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
4899 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
4904 /* play sound of object that hits the ground */
4905 if (last_line || object_hit)
4906 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4909 inline static void TurnRoundExt(int x, int y)
4921 { 0, 0 }, { 0, 0 }, { 0, 0 },
4926 int left, right, back;
4930 { MV_DOWN, MV_UP, MV_RIGHT },
4931 { MV_UP, MV_DOWN, MV_LEFT },
4933 { MV_LEFT, MV_RIGHT, MV_DOWN },
4937 { MV_RIGHT, MV_LEFT, MV_UP }
4940 int element = Feld[x][y];
4941 int move_pattern = element_info[element].move_pattern;
4943 int old_move_dir = MovDir[x][y];
4944 int left_dir = turn[old_move_dir].left;
4945 int right_dir = turn[old_move_dir].right;
4946 int back_dir = turn[old_move_dir].back;
4948 int left_dx = move_xy[left_dir].dx, left_dy = move_xy[left_dir].dy;
4949 int right_dx = move_xy[right_dir].dx, right_dy = move_xy[right_dir].dy;
4950 int move_dx = move_xy[old_move_dir].dx, move_dy = move_xy[old_move_dir].dy;
4951 int back_dx = move_xy[back_dir].dx, back_dy = move_xy[back_dir].dy;
4953 int left_x = x + left_dx, left_y = y + left_dy;
4954 int right_x = x + right_dx, right_y = y + right_dy;
4955 int move_x = x + move_dx, move_y = y + move_dy;
4959 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4961 TestIfBadThingTouchesOtherBadThing(x, y);
4963 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
4964 MovDir[x][y] = right_dir;
4965 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4966 MovDir[x][y] = left_dir;
4968 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
4970 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
4973 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
4975 TestIfBadThingTouchesOtherBadThing(x, y);
4977 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
4978 MovDir[x][y] = left_dir;
4979 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4980 MovDir[x][y] = right_dir;
4982 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
4984 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
4987 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4989 TestIfBadThingTouchesOtherBadThing(x, y);
4991 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
4992 MovDir[x][y] = left_dir;
4993 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
4994 MovDir[x][y] = right_dir;
4996 if (MovDir[x][y] != old_move_dir)
4999 else if (element == EL_YAMYAM)
5001 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
5002 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
5004 if (can_turn_left && can_turn_right)
5005 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5006 else if (can_turn_left)
5007 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5008 else if (can_turn_right)
5009 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5011 MovDir[x][y] = back_dir;
5013 MovDelay[x][y] = 16 + 16 * RND(3);
5015 else if (element == EL_DARK_YAMYAM)
5017 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
5019 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
5022 if (can_turn_left && can_turn_right)
5023 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5024 else if (can_turn_left)
5025 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5026 else if (can_turn_right)
5027 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5029 MovDir[x][y] = back_dir;
5031 MovDelay[x][y] = 16 + 16 * RND(3);
5033 else if (element == EL_PACMAN)
5035 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
5036 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
5038 if (can_turn_left && can_turn_right)
5039 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5040 else if (can_turn_left)
5041 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5042 else if (can_turn_right)
5043 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5045 MovDir[x][y] = back_dir;
5047 MovDelay[x][y] = 6 + RND(40);
5049 else if (element == EL_PIG)
5051 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
5052 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
5053 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
5054 boolean should_turn_left, should_turn_right, should_move_on;
5056 int rnd = RND(rnd_value);
5058 should_turn_left = (can_turn_left &&
5060 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
5061 y + back_dy + left_dy)));
5062 should_turn_right = (can_turn_right &&
5064 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
5065 y + back_dy + right_dy)));
5066 should_move_on = (can_move_on &&
5069 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
5070 y + move_dy + left_dy) ||
5071 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
5072 y + move_dy + right_dy)));
5074 if (should_turn_left || should_turn_right || should_move_on)
5076 if (should_turn_left && should_turn_right && should_move_on)
5077 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
5078 rnd < 2 * rnd_value / 3 ? right_dir :
5080 else if (should_turn_left && should_turn_right)
5081 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
5082 else if (should_turn_left && should_move_on)
5083 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
5084 else if (should_turn_right && should_move_on)
5085 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
5086 else if (should_turn_left)
5087 MovDir[x][y] = left_dir;
5088 else if (should_turn_right)
5089 MovDir[x][y] = right_dir;
5090 else if (should_move_on)
5091 MovDir[x][y] = old_move_dir;
5093 else if (can_move_on && rnd > rnd_value / 8)
5094 MovDir[x][y] = old_move_dir;
5095 else if (can_turn_left && can_turn_right)
5096 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
5097 else if (can_turn_left && rnd > rnd_value / 8)
5098 MovDir[x][y] = left_dir;
5099 else if (can_turn_right && rnd > rnd_value/8)
5100 MovDir[x][y] = right_dir;
5102 MovDir[x][y] = back_dir;
5104 xx = x + move_xy[MovDir[x][y]].dx;
5105 yy = y + move_xy[MovDir[x][y]].dy;
5107 if (!IN_LEV_FIELD(xx, yy) ||
5108 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
5109 MovDir[x][y] = old_move_dir;
5113 else if (element == EL_DRAGON)
5115 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
5116 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
5117 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
5119 int rnd = RND(rnd_value);
5121 if (can_move_on && rnd > rnd_value / 8)
5122 MovDir[x][y] = old_move_dir;
5123 else if (can_turn_left && can_turn_right)
5124 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
5125 else if (can_turn_left && rnd > rnd_value / 8)
5126 MovDir[x][y] = left_dir;
5127 else if (can_turn_right && rnd > rnd_value / 8)
5128 MovDir[x][y] = right_dir;
5130 MovDir[x][y] = back_dir;
5132 xx = x + move_xy[MovDir[x][y]].dx;
5133 yy = y + move_xy[MovDir[x][y]].dy;
5135 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
5136 MovDir[x][y] = old_move_dir;
5140 else if (element == EL_MOLE)
5142 boolean can_move_on =
5143 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
5144 IS_AMOEBOID(Feld[move_x][move_y]) ||
5145 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
5148 boolean can_turn_left =
5149 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
5150 IS_AMOEBOID(Feld[left_x][left_y])));
5152 boolean can_turn_right =
5153 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
5154 IS_AMOEBOID(Feld[right_x][right_y])));
5156 if (can_turn_left && can_turn_right)
5157 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
5158 else if (can_turn_left)
5159 MovDir[x][y] = left_dir;
5161 MovDir[x][y] = right_dir;
5164 if (MovDir[x][y] != old_move_dir)
5167 else if (element == EL_BALLOON)
5169 MovDir[x][y] = game.wind_direction;
5172 else if (element == EL_SPRING)
5174 #if USE_NEW_SPRING_BUMPER
5175 if (MovDir[x][y] & MV_HORIZONTAL)
5177 if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
5178 !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
5180 Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
5181 ResetGfxAnimation(move_x, move_y);
5182 DrawLevelField(move_x, move_y);
5184 MovDir[x][y] = back_dir;
5186 else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
5187 SPRING_CAN_ENTER_FIELD(element, x, y + 1))
5188 MovDir[x][y] = MV_NONE;
5191 if (MovDir[x][y] & MV_HORIZONTAL &&
5192 (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
5193 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
5194 MovDir[x][y] = MV_NONE;
5199 else if (element == EL_ROBOT ||
5200 element == EL_SATELLITE ||
5201 element == EL_PENGUIN ||
5202 element == EL_EMC_ANDROID)
5204 int attr_x = -1, attr_y = -1;
5215 for (i = 0; i < MAX_PLAYERS; i++)
5217 struct PlayerInfo *player = &stored_player[i];
5218 int jx = player->jx, jy = player->jy;
5220 if (!player->active)
5224 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
5232 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
5233 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
5234 game.engine_version < VERSION_IDENT(3,1,0,0)))
5240 if (element == EL_PENGUIN)
5243 static int xy[4][2] =
5251 for (i = 0; i < NUM_DIRECTIONS; i++)
5253 int ex = x + xy[i][0];
5254 int ey = y + xy[i][1];
5256 if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
5265 MovDir[x][y] = MV_NONE;
5267 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
5268 else if (attr_x > x)
5269 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
5271 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
5272 else if (attr_y > y)
5273 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
5275 if (element == EL_ROBOT)
5279 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5280 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
5281 Moving2Blocked(x, y, &newx, &newy);
5283 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
5284 MovDelay[x][y] = 8 + 8 * !RND(3);
5286 MovDelay[x][y] = 16;
5288 else if (element == EL_PENGUIN)
5294 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5296 boolean first_horiz = RND(2);
5297 int new_move_dir = MovDir[x][y];
5300 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5301 Moving2Blocked(x, y, &newx, &newy);
5303 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
5307 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5308 Moving2Blocked(x, y, &newx, &newy);
5310 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
5313 MovDir[x][y] = old_move_dir;
5317 else if (element == EL_SATELLITE)
5323 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5325 boolean first_horiz = RND(2);
5326 int new_move_dir = MovDir[x][y];
5329 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5330 Moving2Blocked(x, y, &newx, &newy);
5332 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
5336 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5337 Moving2Blocked(x, y, &newx, &newy);
5339 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
5342 MovDir[x][y] = old_move_dir;
5346 else if (element == EL_EMC_ANDROID)
5348 static int check_pos[16] =
5350 -1, /* 0 => (invalid) */
5351 7, /* 1 => MV_LEFT */
5352 3, /* 2 => MV_RIGHT */
5353 -1, /* 3 => (invalid) */
5355 0, /* 5 => MV_LEFT | MV_UP */
5356 2, /* 6 => MV_RIGHT | MV_UP */
5357 -1, /* 7 => (invalid) */
5358 5, /* 8 => MV_DOWN */
5359 6, /* 9 => MV_LEFT | MV_DOWN */
5360 4, /* 10 => MV_RIGHT | MV_DOWN */
5361 -1, /* 11 => (invalid) */
5362 -1, /* 12 => (invalid) */
5363 -1, /* 13 => (invalid) */
5364 -1, /* 14 => (invalid) */
5365 -1, /* 15 => (invalid) */
5373 { -1, -1, MV_LEFT | MV_UP },
5375 { +1, -1, MV_RIGHT | MV_UP },
5376 { +1, 0, MV_RIGHT },
5377 { +1, +1, MV_RIGHT | MV_DOWN },
5379 { -1, +1, MV_LEFT | MV_DOWN },
5382 int start_pos, check_order;
5383 boolean can_clone = FALSE;
5386 /* check if there is any free field around current position */
5387 for (i = 0; i < 8; i++)
5389 int newx = x + check_xy[i].dx;
5390 int newy = y + check_xy[i].dy;
5392 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5400 if (can_clone) /* randomly find an element to clone */
5404 start_pos = check_pos[RND(8)];
5405 check_order = (RND(2) ? -1 : +1);
5407 for (i = 0; i < 8; i++)
5409 int pos_raw = start_pos + i * check_order;
5410 int pos = (pos_raw + 8) % 8;
5411 int newx = x + check_xy[pos].dx;
5412 int newy = y + check_xy[pos].dy;
5414 if (ANDROID_CAN_CLONE_FIELD(newx, newy))
5416 element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
5417 element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
5419 Store[x][y] = Feld[newx][newy];
5428 if (can_clone) /* randomly find a direction to move */
5432 start_pos = check_pos[RND(8)];
5433 check_order = (RND(2) ? -1 : +1);
5435 for (i = 0; i < 8; i++)
5437 int pos_raw = start_pos + i * check_order;
5438 int pos = (pos_raw + 8) % 8;
5439 int newx = x + check_xy[pos].dx;
5440 int newy = y + check_xy[pos].dy;
5441 int new_move_dir = check_xy[pos].dir;
5443 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5445 MovDir[x][y] = new_move_dir;
5446 MovDelay[x][y] = level.android_clone_time * 8 + 1;
5455 if (can_clone) /* cloning and moving successful */
5458 /* cannot clone -- try to move towards player */
5460 start_pos = check_pos[MovDir[x][y] & 0x0f];
5461 check_order = (RND(2) ? -1 : +1);
5463 for (i = 0; i < 3; i++)
5465 /* first check start_pos, then previous/next or (next/previous) pos */
5466 int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
5467 int pos = (pos_raw + 8) % 8;
5468 int newx = x + check_xy[pos].dx;
5469 int newy = y + check_xy[pos].dy;
5470 int new_move_dir = check_xy[pos].dir;
5472 if (IS_PLAYER(newx, newy))
5475 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
5477 MovDir[x][y] = new_move_dir;
5478 MovDelay[x][y] = level.android_move_time * 8 + 1;
5485 else if (move_pattern == MV_TURNING_LEFT ||
5486 move_pattern == MV_TURNING_RIGHT ||
5487 move_pattern == MV_TURNING_LEFT_RIGHT ||
5488 move_pattern == MV_TURNING_RIGHT_LEFT ||
5489 move_pattern == MV_TURNING_RANDOM ||
5490 move_pattern == MV_ALL_DIRECTIONS)
5492 boolean can_turn_left =
5493 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
5494 boolean can_turn_right =
5495 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
5497 if (element_info[element].move_stepsize == 0) /* "not moving" */
5500 if (move_pattern == MV_TURNING_LEFT)
5501 MovDir[x][y] = left_dir;
5502 else if (move_pattern == MV_TURNING_RIGHT)
5503 MovDir[x][y] = right_dir;
5504 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
5505 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
5506 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
5507 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
5508 else if (move_pattern == MV_TURNING_RANDOM)
5509 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
5510 can_turn_right && !can_turn_left ? right_dir :
5511 RND(2) ? left_dir : right_dir);
5512 else if (can_turn_left && can_turn_right)
5513 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5514 else if (can_turn_left)
5515 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5516 else if (can_turn_right)
5517 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5519 MovDir[x][y] = back_dir;
5521 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5523 else if (move_pattern == MV_HORIZONTAL ||
5524 move_pattern == MV_VERTICAL)
5526 if (move_pattern & old_move_dir)
5527 MovDir[x][y] = back_dir;
5528 else if (move_pattern == MV_HORIZONTAL)
5529 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
5530 else if (move_pattern == MV_VERTICAL)
5531 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
5533 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5535 else if (move_pattern & MV_ANY_DIRECTION)
5537 MovDir[x][y] = move_pattern;
5538 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5540 else if (move_pattern & MV_WIND_DIRECTION)
5542 MovDir[x][y] = game.wind_direction;
5543 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5545 else if (move_pattern == MV_ALONG_LEFT_SIDE)
5547 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
5548 MovDir[x][y] = left_dir;
5549 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5550 MovDir[x][y] = right_dir;
5552 if (MovDir[x][y] != old_move_dir)
5553 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5555 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
5557 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
5558 MovDir[x][y] = right_dir;
5559 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5560 MovDir[x][y] = left_dir;
5562 if (MovDir[x][y] != old_move_dir)
5563 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5565 else if (move_pattern == MV_TOWARDS_PLAYER ||
5566 move_pattern == MV_AWAY_FROM_PLAYER)
5568 int attr_x = -1, attr_y = -1;
5570 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
5581 for (i = 0; i < MAX_PLAYERS; i++)
5583 struct PlayerInfo *player = &stored_player[i];
5584 int jx = player->jx, jy = player->jy;
5586 if (!player->active)
5590 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
5598 MovDir[x][y] = MV_NONE;
5600 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
5601 else if (attr_x > x)
5602 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
5604 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
5605 else if (attr_y > y)
5606 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
5608 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5610 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5612 boolean first_horiz = RND(2);
5613 int new_move_dir = MovDir[x][y];
5615 if (element_info[element].move_stepsize == 0) /* "not moving" */
5617 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
5618 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5624 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5625 Moving2Blocked(x, y, &newx, &newy);
5627 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5631 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5632 Moving2Blocked(x, y, &newx, &newy);
5634 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5637 MovDir[x][y] = old_move_dir;
5640 else if (move_pattern == MV_WHEN_PUSHED ||
5641 move_pattern == MV_WHEN_DROPPED)
5643 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5644 MovDir[x][y] = MV_NONE;
5648 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
5650 static int test_xy[7][2] =
5660 static int test_dir[7] =
5670 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
5671 int move_preference = -1000000; /* start with very low preference */
5672 int new_move_dir = MV_NONE;
5673 int start_test = RND(4);
5676 for (i = 0; i < NUM_DIRECTIONS; i++)
5678 int move_dir = test_dir[start_test + i];
5679 int move_dir_preference;
5681 xx = x + test_xy[start_test + i][0];
5682 yy = y + test_xy[start_test + i][1];
5684 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
5685 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
5687 new_move_dir = move_dir;
5692 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
5695 move_dir_preference = -1 * RunnerVisit[xx][yy];
5696 if (hunter_mode && PlayerVisit[xx][yy] > 0)
5697 move_dir_preference = PlayerVisit[xx][yy];
5699 if (move_dir_preference > move_preference)
5701 /* prefer field that has not been visited for the longest time */
5702 move_preference = move_dir_preference;
5703 new_move_dir = move_dir;
5705 else if (move_dir_preference == move_preference &&
5706 move_dir == old_move_dir)
5708 /* prefer last direction when all directions are preferred equally */
5709 move_preference = move_dir_preference;
5710 new_move_dir = move_dir;
5714 MovDir[x][y] = new_move_dir;
5715 if (old_move_dir != new_move_dir)
5716 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5720 static void TurnRound(int x, int y)
5722 int direction = MovDir[x][y];
5724 int element, graphic;
5729 GfxDir[x][y] = MovDir[x][y];
5731 if (direction != MovDir[x][y])
5735 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
5738 ResetGfxFrame(x, y, FALSE);
5740 element = Feld[x][y];
5741 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5743 if (graphic_info[graphic].anim_global_sync)
5744 GfxFrame[x][y] = FrameCounter;
5745 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5746 GfxFrame[x][y] = CustomValue[x][y];
5747 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5748 GfxFrame[x][y] = element_info[element].collect_score;
5752 static boolean JustBeingPushed(int x, int y)
5756 for (i = 0; i < MAX_PLAYERS; i++)
5758 struct PlayerInfo *player = &stored_player[i];
5760 if (player->active && player->is_pushing && player->MovPos)
5762 int next_jx = player->jx + (player->jx - player->last_jx);
5763 int next_jy = player->jy + (player->jy - player->last_jy);
5765 if (x == next_jx && y == next_jy)
5773 void StartMoving(int x, int y)
5775 boolean started_moving = FALSE; /* some elements can fall _and_ move */
5776 int element = Feld[x][y];
5781 if (MovDelay[x][y] == 0)
5782 GfxAction[x][y] = ACTION_DEFAULT;
5784 if (CAN_FALL(element) && y < lev_fieldy - 1)
5786 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
5787 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
5788 if (JustBeingPushed(x, y))
5791 if (element == EL_QUICKSAND_FULL)
5793 if (IS_FREE(x, y + 1))
5795 InitMovingField(x, y, MV_DOWN);
5796 started_moving = TRUE;
5798 Feld[x][y] = EL_QUICKSAND_EMPTYING;
5799 Store[x][y] = EL_ROCK;
5801 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
5803 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5805 if (!MovDelay[x][y])
5806 MovDelay[x][y] = TILEY + 1;
5815 Feld[x][y] = EL_QUICKSAND_EMPTY;
5816 Feld[x][y + 1] = EL_QUICKSAND_FULL;
5817 Store[x][y + 1] = Store[x][y];
5820 PlayLevelSoundAction(x, y, ACTION_FILLING);
5823 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
5824 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5826 InitMovingField(x, y, MV_DOWN);
5827 started_moving = TRUE;
5829 Feld[x][y] = EL_QUICKSAND_FILLING;
5830 Store[x][y] = element;
5832 PlayLevelSoundAction(x, y, ACTION_FILLING);
5834 else if (element == EL_MAGIC_WALL_FULL)
5836 if (IS_FREE(x, y + 1))
5838 InitMovingField(x, y, MV_DOWN);
5839 started_moving = TRUE;
5841 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
5842 Store[x][y] = EL_CHANGED(Store[x][y]);
5844 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
5846 if (!MovDelay[x][y])
5847 MovDelay[x][y] = TILEY/4 + 1;
5856 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
5857 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
5858 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
5862 else if (element == EL_BD_MAGIC_WALL_FULL)
5864 if (IS_FREE(x, y + 1))
5866 InitMovingField(x, y, MV_DOWN);
5867 started_moving = TRUE;
5869 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
5870 Store[x][y] = EL_CHANGED2(Store[x][y]);
5872 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
5874 if (!MovDelay[x][y])
5875 MovDelay[x][y] = TILEY/4 + 1;
5884 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
5885 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
5886 Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
5890 else if (CAN_PASS_MAGIC_WALL(element) &&
5891 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
5892 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
5894 InitMovingField(x, y, MV_DOWN);
5895 started_moving = TRUE;
5898 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
5899 EL_BD_MAGIC_WALL_FILLING);
5900 Store[x][y] = element;
5902 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
5904 SplashAcid(x, y + 1);
5906 InitMovingField(x, y, MV_DOWN);
5907 started_moving = TRUE;
5909 Store[x][y] = EL_ACID;
5911 else if ((game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5912 CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
5914 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
5915 CAN_FALL(element) && WasJustFalling[x][y] &&
5916 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
5918 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
5919 CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
5920 (Feld[x][y + 1] == EL_BLOCKED)))
5922 /* this is needed for a special case not covered by calling "Impact()"
5923 from "ContinueMoving()": if an element moves to a tile directly below
5924 another element which was just falling on that tile (which was empty
5925 in the previous frame), the falling element above would just stop
5926 instead of smashing the element below (in previous version, the above
5927 element was just checked for "moving" instead of "falling", resulting
5928 in incorrect smashes caused by horizontal movement of the above
5929 element; also, the case of the player being the element to smash was
5930 simply not covered here... :-/ ) */
5932 CheckCollision[x][y] = 0;
5936 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
5938 if (MovDir[x][y] == MV_NONE)
5940 InitMovingField(x, y, MV_DOWN);
5941 started_moving = TRUE;
5944 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
5946 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
5947 MovDir[x][y] = MV_DOWN;
5949 InitMovingField(x, y, MV_DOWN);
5950 started_moving = TRUE;
5952 else if (element == EL_AMOEBA_DROP)
5954 Feld[x][y] = EL_AMOEBA_GROWING;
5955 Store[x][y] = EL_AMOEBA_WET;
5957 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
5958 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
5959 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
5960 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
5962 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
5963 (IS_FREE(x - 1, y + 1) ||
5964 Feld[x - 1][y + 1] == EL_ACID));
5965 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
5966 (IS_FREE(x + 1, y + 1) ||
5967 Feld[x + 1][y + 1] == EL_ACID));
5968 boolean can_fall_any = (can_fall_left || can_fall_right);
5969 boolean can_fall_both = (can_fall_left && can_fall_right);
5970 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
5972 #if USE_NEW_ALL_SLIPPERY
5973 if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
5975 if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
5976 can_fall_right = FALSE;
5977 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
5978 can_fall_left = FALSE;
5979 else if (slippery_type == SLIPPERY_ONLY_LEFT)
5980 can_fall_right = FALSE;
5981 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
5982 can_fall_left = FALSE;
5984 can_fall_any = (can_fall_left || can_fall_right);
5985 can_fall_both = FALSE;
5988 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
5990 if (slippery_type == SLIPPERY_ONLY_LEFT)
5991 can_fall_right = FALSE;
5992 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
5993 can_fall_left = FALSE;
5994 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
5995 can_fall_right = FALSE;
5996 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
5997 can_fall_left = FALSE;
5999 can_fall_any = (can_fall_left || can_fall_right);
6000 can_fall_both = (can_fall_left && can_fall_right);
6004 #if USE_NEW_ALL_SLIPPERY
6006 #if USE_NEW_SP_SLIPPERY
6007 /* !!! better use the same properties as for custom elements here !!! */
6008 else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
6009 can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
6011 can_fall_right = FALSE; /* slip down on left side */
6012 can_fall_both = FALSE;
6017 #if USE_NEW_ALL_SLIPPERY
6020 if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
6021 can_fall_right = FALSE; /* slip down on left side */
6023 can_fall_left = !(can_fall_right = RND(2));
6025 can_fall_both = FALSE;
6030 if (game.emulation == EMU_BOULDERDASH ||
6031 element == EL_BD_ROCK || element == EL_BD_DIAMOND)
6032 can_fall_right = FALSE; /* slip down on left side */
6034 can_fall_left = !(can_fall_right = RND(2));
6036 can_fall_both = FALSE;
6042 /* if not determined otherwise, prefer left side for slipping down */
6043 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
6044 started_moving = TRUE;
6048 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
6050 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
6053 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
6054 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
6055 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
6056 int belt_dir = game.belt_dir[belt_nr];
6058 if ((belt_dir == MV_LEFT && left_is_free) ||
6059 (belt_dir == MV_RIGHT && right_is_free))
6061 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
6063 InitMovingField(x, y, belt_dir);
6064 started_moving = TRUE;
6066 Pushed[x][y] = TRUE;
6067 Pushed[nextx][y] = TRUE;
6069 GfxAction[x][y] = ACTION_DEFAULT;
6073 MovDir[x][y] = 0; /* if element was moving, stop it */
6078 /* not "else if" because of elements that can fall and move (EL_SPRING) */
6080 if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
6082 if (CAN_MOVE(element) && !started_moving)
6085 int move_pattern = element_info[element].move_pattern;
6090 if (MovDir[x][y] == MV_NONE)
6092 printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
6093 x, y, element, element_info[element].token_name);
6094 printf("StartMoving(): This should never happen!\n");
6099 Moving2Blocked(x, y, &newx, &newy);
6101 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
6104 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
6105 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6107 WasJustMoving[x][y] = 0;
6108 CheckCollision[x][y] = 0;
6110 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
6112 if (Feld[x][y] != element) /* element has changed */
6116 if (!MovDelay[x][y]) /* start new movement phase */
6118 /* all objects that can change their move direction after each step
6119 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
6121 if (element != EL_YAMYAM &&
6122 element != EL_DARK_YAMYAM &&
6123 element != EL_PACMAN &&
6124 !(move_pattern & MV_ANY_DIRECTION) &&
6125 move_pattern != MV_TURNING_LEFT &&
6126 move_pattern != MV_TURNING_RIGHT &&
6127 move_pattern != MV_TURNING_LEFT_RIGHT &&
6128 move_pattern != MV_TURNING_RIGHT_LEFT &&
6129 move_pattern != MV_TURNING_RANDOM)
6133 if (MovDelay[x][y] && (element == EL_BUG ||
6134 element == EL_SPACESHIP ||
6135 element == EL_SP_SNIKSNAK ||
6136 element == EL_SP_ELECTRON ||
6137 element == EL_MOLE))
6138 DrawLevelField(x, y);
6142 if (MovDelay[x][y]) /* wait some time before next movement */
6146 if (element == EL_ROBOT ||
6147 element == EL_YAMYAM ||
6148 element == EL_DARK_YAMYAM)
6150 DrawLevelElementAnimationIfNeeded(x, y, element);
6151 PlayLevelSoundAction(x, y, ACTION_WAITING);
6153 else if (element == EL_SP_ELECTRON)
6154 DrawLevelElementAnimationIfNeeded(x, y, element);
6155 else if (element == EL_DRAGON)
6158 int dir = MovDir[x][y];
6159 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
6160 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
6161 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
6162 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
6163 dir == MV_UP ? IMG_FLAMES_1_UP :
6164 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
6165 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
6167 GfxAction[x][y] = ACTION_ATTACKING;
6169 if (IS_PLAYER(x, y))
6170 DrawPlayerField(x, y);
6172 DrawLevelField(x, y);
6174 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
6176 for (i = 1; i <= 3; i++)
6178 int xx = x + i * dx;
6179 int yy = y + i * dy;
6180 int sx = SCREENX(xx);
6181 int sy = SCREENY(yy);
6182 int flame_graphic = graphic + (i - 1);
6184 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
6189 int flamed = MovingOrBlocked2Element(xx, yy);
6193 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
6195 else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
6196 RemoveMovingField(xx, yy);
6198 RemoveField(xx, yy);
6200 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
6203 RemoveMovingField(xx, yy);
6206 ChangeDelay[xx][yy] = 0;
6208 Feld[xx][yy] = EL_FLAMES;
6210 if (IN_SCR_FIELD(sx, sy))
6212 DrawLevelFieldCrumbledSand(xx, yy);
6213 DrawGraphic(sx, sy, flame_graphic, frame);
6218 if (Feld[xx][yy] == EL_FLAMES)
6219 Feld[xx][yy] = EL_EMPTY;
6220 DrawLevelField(xx, yy);
6225 if (MovDelay[x][y]) /* element still has to wait some time */
6227 PlayLevelSoundAction(x, y, ACTION_WAITING);
6233 /* now make next step */
6235 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
6237 if (DONT_COLLIDE_WITH(element) &&
6238 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
6239 !PLAYER_ENEMY_PROTECTED(newx, newy))
6241 TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
6246 else if (CAN_MOVE_INTO_ACID(element) &&
6247 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
6248 !IS_MV_DIAGONAL(MovDir[x][y]) &&
6249 (MovDir[x][y] == MV_DOWN ||
6250 game.engine_version >= VERSION_IDENT(3,1,0,0)))
6252 SplashAcid(newx, newy);
6253 Store[x][y] = EL_ACID;
6255 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
6257 if (Feld[newx][newy] == EL_EXIT_OPEN)
6260 DrawLevelField(x, y);
6262 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
6263 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
6264 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
6266 local_player->friends_still_needed--;
6267 if (!local_player->friends_still_needed &&
6268 !local_player->GameOver && AllPlayersGone)
6269 local_player->LevelSolved = local_player->GameOver = TRUE;
6273 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
6275 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
6276 DrawLevelField(newx, newy);
6278 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
6280 else if (!IS_FREE(newx, newy))
6282 GfxAction[x][y] = ACTION_WAITING;
6284 if (IS_PLAYER(x, y))
6285 DrawPlayerField(x, y);
6287 DrawLevelField(x, y);
6292 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
6294 if (IS_FOOD_PIG(Feld[newx][newy]))
6296 if (IS_MOVING(newx, newy))
6297 RemoveMovingField(newx, newy);
6300 Feld[newx][newy] = EL_EMPTY;
6301 DrawLevelField(newx, newy);
6304 PlayLevelSound(x, y, SND_PIG_DIGGING);
6306 else if (!IS_FREE(newx, newy))
6308 if (IS_PLAYER(x, y))
6309 DrawPlayerField(x, y);
6311 DrawLevelField(x, y);
6316 else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
6318 if (Store[x][y] != EL_EMPTY)
6320 boolean can_clone = FALSE;
6323 /* check if element to clone is still there */
6324 for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
6326 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
6334 /* cannot clone or target field not free anymore -- do not clone */
6335 if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6336 Store[x][y] = EL_EMPTY;
6339 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6341 if (IS_MV_DIAGONAL(MovDir[x][y]))
6343 int diagonal_move_dir = MovDir[x][y];
6344 int stored = Store[x][y];
6345 int change_delay = 8;
6348 /* android is moving diagonally */
6350 CreateField(x, y, EL_DIAGONAL_SHRINKING);
6352 Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
6353 GfxElement[x][y] = EL_EMC_ANDROID;
6354 GfxAction[x][y] = ACTION_SHRINKING;
6355 GfxDir[x][y] = diagonal_move_dir;
6356 ChangeDelay[x][y] = change_delay;
6358 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
6361 DrawLevelGraphicAnimation(x, y, graphic);
6362 PlayLevelSoundAction(x, y, ACTION_SHRINKING);
6364 if (Feld[newx][newy] == EL_ACID)
6366 SplashAcid(newx, newy);
6371 CreateField(newx, newy, EL_DIAGONAL_GROWING);
6373 Store[newx][newy] = EL_EMC_ANDROID;
6374 GfxElement[newx][newy] = EL_EMC_ANDROID;
6375 GfxAction[newx][newy] = ACTION_GROWING;
6376 GfxDir[newx][newy] = diagonal_move_dir;
6377 ChangeDelay[newx][newy] = change_delay;
6379 graphic = el_act_dir2img(GfxElement[newx][newy],
6380 GfxAction[newx][newy], GfxDir[newx][newy]);
6382 DrawLevelGraphicAnimation(newx, newy, graphic);
6383 PlayLevelSoundAction(newx, newy, ACTION_GROWING);
6389 Feld[newx][newy] = EL_EMPTY;
6390 DrawLevelField(newx, newy);
6392 PlayLevelSoundAction(x, y, ACTION_DIGGING);
6395 else if (!IS_FREE(newx, newy))
6398 if (IS_PLAYER(x, y))
6399 DrawPlayerField(x, y);
6401 DrawLevelField(x, y);
6407 else if (IS_CUSTOM_ELEMENT(element) &&
6408 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6410 int new_element = Feld[newx][newy];
6412 if (!IS_FREE(newx, newy))
6414 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
6415 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
6418 /* no element can dig solid indestructible elements */
6419 if (IS_INDESTRUCTIBLE(new_element) &&
6420 !IS_DIGGABLE(new_element) &&
6421 !IS_COLLECTIBLE(new_element))
6424 if (AmoebaNr[newx][newy] &&
6425 (new_element == EL_AMOEBA_FULL ||
6426 new_element == EL_BD_AMOEBA ||
6427 new_element == EL_AMOEBA_GROWING))
6429 AmoebaCnt[AmoebaNr[newx][newy]]--;
6430 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6433 if (IS_MOVING(newx, newy))
6434 RemoveMovingField(newx, newy);
6437 RemoveField(newx, newy);
6438 DrawLevelField(newx, newy);
6441 /* if digged element was about to explode, prevent the explosion */
6442 ExplodeField[newx][newy] = EX_TYPE_NONE;
6444 PlayLevelSoundAction(x, y, action);
6447 Store[newx][newy] = EL_EMPTY;
6449 /* this makes it possible to leave the removed element again */
6450 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
6451 Store[newx][newy] = new_element;
6453 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
6455 int move_leave_element = element_info[element].move_leave_element;
6457 /* this makes it possible to leave the removed element again */
6458 Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
6459 new_element : move_leave_element);
6463 if (move_pattern & MV_MAZE_RUNNER_STYLE)
6465 RunnerVisit[x][y] = FrameCounter;
6466 PlayerVisit[x][y] /= 8; /* expire player visit path */
6469 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
6471 if (!IS_FREE(newx, newy))
6473 if (IS_PLAYER(x, y))
6474 DrawPlayerField(x, y);
6476 DrawLevelField(x, y);
6482 boolean wanna_flame = !RND(10);
6483 int dx = newx - x, dy = newy - y;
6484 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
6485 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
6486 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
6487 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
6488 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
6489 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
6492 IS_CLASSIC_ENEMY(element1) ||
6493 IS_CLASSIC_ENEMY(element2)) &&
6494 element1 != EL_DRAGON && element2 != EL_DRAGON &&
6495 element1 != EL_FLAMES && element2 != EL_FLAMES)
6497 ResetGfxAnimation(x, y);
6498 GfxAction[x][y] = ACTION_ATTACKING;
6500 if (IS_PLAYER(x, y))
6501 DrawPlayerField(x, y);
6503 DrawLevelField(x, y);
6505 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
6507 MovDelay[x][y] = 50;
6511 RemoveField(newx, newy);
6513 Feld[newx][newy] = EL_FLAMES;
6514 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
6517 RemoveField(newx1, newy1);
6519 Feld[newx1][newy1] = EL_FLAMES;
6521 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
6524 RemoveField(newx2, newy2);
6526 Feld[newx2][newy2] = EL_FLAMES;
6533 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6534 Feld[newx][newy] == EL_DIAMOND)
6536 if (IS_MOVING(newx, newy))
6537 RemoveMovingField(newx, newy);
6540 Feld[newx][newy] = EL_EMPTY;
6541 DrawLevelField(newx, newy);
6544 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
6546 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6547 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
6549 if (AmoebaNr[newx][newy])
6551 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6552 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
6553 Feld[newx][newy] == EL_BD_AMOEBA)
6554 AmoebaCnt[AmoebaNr[newx][newy]]--;
6559 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
6561 RemoveMovingField(newx, newy);
6564 if (IS_MOVING(newx, newy))
6566 RemoveMovingField(newx, newy);
6571 Feld[newx][newy] = EL_EMPTY;
6572 DrawLevelField(newx, newy);
6575 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
6577 else if ((element == EL_PACMAN || element == EL_MOLE)
6578 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
6580 if (AmoebaNr[newx][newy])
6582 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6583 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
6584 Feld[newx][newy] == EL_BD_AMOEBA)
6585 AmoebaCnt[AmoebaNr[newx][newy]]--;
6588 if (element == EL_MOLE)
6590 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
6591 PlayLevelSound(x, y, SND_MOLE_DIGGING);
6593 ResetGfxAnimation(x, y);
6594 GfxAction[x][y] = ACTION_DIGGING;
6595 DrawLevelField(x, y);
6597 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
6599 return; /* wait for shrinking amoeba */
6601 else /* element == EL_PACMAN */
6603 Feld[newx][newy] = EL_EMPTY;
6604 DrawLevelField(newx, newy);
6605 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
6608 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
6609 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
6610 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
6612 /* wait for shrinking amoeba to completely disappear */
6615 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
6617 /* object was running against a wall */
6622 /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
6623 if (move_pattern & MV_ANY_DIRECTION &&
6624 move_pattern == MovDir[x][y])
6626 int blocking_element =
6627 (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
6629 CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
6632 element = Feld[x][y]; /* element might have changed */
6636 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
6637 DrawLevelElementAnimation(x, y, element);
6639 if (DONT_TOUCH(element))
6640 TestIfBadThingTouchesPlayer(x, y);
6645 InitMovingField(x, y, MovDir[x][y]);
6647 PlayLevelSoundAction(x, y, ACTION_MOVING);
6651 ContinueMoving(x, y);
6654 void ContinueMoving(int x, int y)
6656 int element = Feld[x][y];
6657 struct ElementInfo *ei = &element_info[element];
6658 int direction = MovDir[x][y];
6659 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
6660 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
6661 int newx = x + dx, newy = y + dy;
6662 int stored = Store[x][y];
6663 int stored_new = Store[newx][newy];
6664 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
6665 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
6666 boolean last_line = (newy == lev_fieldy - 1);
6668 MovPos[x][y] += getElementMoveStepsize(x, y);
6670 if (pushed_by_player) /* special case: moving object pushed by player */
6671 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
6673 if (ABS(MovPos[x][y]) < TILEX)
6675 DrawLevelField(x, y);
6677 return; /* element is still moving */
6680 /* element reached destination field */
6682 Feld[x][y] = EL_EMPTY;
6683 Feld[newx][newy] = element;
6684 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
6686 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
6688 element = Feld[newx][newy] = EL_ACID;
6690 else if (element == EL_MOLE)
6692 Feld[x][y] = EL_SAND;
6694 DrawLevelFieldCrumbledSandNeighbours(x, y);
6696 else if (element == EL_QUICKSAND_FILLING)
6698 element = Feld[newx][newy] = get_next_element(element);
6699 Store[newx][newy] = Store[x][y];
6701 else if (element == EL_QUICKSAND_EMPTYING)
6703 Feld[x][y] = get_next_element(element);
6704 element = Feld[newx][newy] = Store[x][y];
6706 else if (element == EL_MAGIC_WALL_FILLING)
6708 element = Feld[newx][newy] = get_next_element(element);
6709 if (!game.magic_wall_active)
6710 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
6711 Store[newx][newy] = Store[x][y];
6713 else if (element == EL_MAGIC_WALL_EMPTYING)
6715 Feld[x][y] = get_next_element(element);
6716 if (!game.magic_wall_active)
6717 Feld[x][y] = EL_MAGIC_WALL_DEAD;
6718 element = Feld[newx][newy] = Store[x][y];
6720 #if USE_NEW_CUSTOM_VALUE
6721 InitField(newx, newy, FALSE);
6724 else if (element == EL_BD_MAGIC_WALL_FILLING)
6726 element = Feld[newx][newy] = get_next_element(element);
6727 if (!game.magic_wall_active)
6728 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
6729 Store[newx][newy] = Store[x][y];
6731 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
6733 Feld[x][y] = get_next_element(element);
6734 if (!game.magic_wall_active)
6735 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
6736 element = Feld[newx][newy] = Store[x][y];
6738 #if USE_NEW_CUSTOM_VALUE
6739 InitField(newx, newy, FALSE);
6742 else if (element == EL_AMOEBA_DROPPING)
6744 Feld[x][y] = get_next_element(element);
6745 element = Feld[newx][newy] = Store[x][y];
6747 else if (element == EL_SOKOBAN_OBJECT)
6750 Feld[x][y] = Back[x][y];
6752 if (Back[newx][newy])
6753 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
6755 Back[x][y] = Back[newx][newy] = 0;
6758 Store[x][y] = EL_EMPTY;
6763 MovDelay[newx][newy] = 0;
6766 if (CAN_CHANGE_OR_HAS_ACTION(element))
6768 if (CAN_CHANGE(element))
6771 /* copy element change control values to new field */
6772 ChangeDelay[newx][newy] = ChangeDelay[x][y];
6773 ChangePage[newx][newy] = ChangePage[x][y];
6774 ChangeCount[newx][newy] = ChangeCount[x][y];
6775 ChangeEvent[newx][newy] = ChangeEvent[x][y];
6778 #if USE_NEW_CUSTOM_VALUE
6779 CustomValue[newx][newy] = CustomValue[x][y];
6785 #if USE_NEW_CUSTOM_VALUE
6786 CustomValue[newx][newy] = CustomValue[x][y];
6790 ChangeDelay[x][y] = 0;
6791 ChangePage[x][y] = -1;
6792 ChangeCount[x][y] = 0;
6793 ChangeEvent[x][y] = -1;
6795 #if USE_NEW_CUSTOM_VALUE
6796 CustomValue[x][y] = 0;
6799 /* copy animation control values to new field */
6800 GfxFrame[newx][newy] = GfxFrame[x][y];
6801 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
6802 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
6803 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
6805 Pushed[x][y] = Pushed[newx][newy] = FALSE;
6807 /* some elements can leave other elements behind after moving */
6809 if (ei->move_leave_element != EL_EMPTY &&
6810 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
6811 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
6813 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
6814 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
6815 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
6818 int move_leave_element = ei->move_leave_element;
6822 /* this makes it possible to leave the removed element again */
6823 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
6824 move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
6826 /* this makes it possible to leave the removed element again */
6827 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
6828 move_leave_element = stored;
6831 /* this makes it possible to leave the removed element again */
6832 if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
6833 ei->move_leave_element == EL_TRIGGER_ELEMENT)
6834 move_leave_element = stored;
6837 Feld[x][y] = move_leave_element;
6839 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
6840 MovDir[x][y] = direction;
6842 InitField(x, y, FALSE);
6844 if (GFX_CRUMBLED(Feld[x][y]))
6845 DrawLevelFieldCrumbledSandNeighbours(x, y);
6847 if (ELEM_IS_PLAYER(move_leave_element))
6848 RelocatePlayer(x, y, move_leave_element);
6851 /* do this after checking for left-behind element */
6852 ResetGfxAnimation(x, y); /* reset animation values for old field */
6854 if (!CAN_MOVE(element) ||
6855 (CAN_FALL(element) && direction == MV_DOWN &&
6856 (element == EL_SPRING ||
6857 element_info[element].move_pattern == MV_WHEN_PUSHED ||
6858 element_info[element].move_pattern == MV_WHEN_DROPPED)))
6859 GfxDir[x][y] = MovDir[newx][newy] = 0;
6861 DrawLevelField(x, y);
6862 DrawLevelField(newx, newy);
6864 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
6866 /* prevent pushed element from moving on in pushed direction */
6867 if (pushed_by_player && CAN_MOVE(element) &&
6868 element_info[element].move_pattern & MV_ANY_DIRECTION &&
6869 !(element_info[element].move_pattern & direction))
6870 TurnRound(newx, newy);
6872 /* prevent elements on conveyor belt from moving on in last direction */
6873 if (pushed_by_conveyor && CAN_FALL(element) &&
6874 direction & MV_HORIZONTAL)
6875 MovDir[newx][newy] = 0;
6877 if (!pushed_by_player)
6879 int nextx = newx + dx, nexty = newy + dy;
6880 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
6882 WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
6884 if (CAN_FALL(element) && direction == MV_DOWN)
6885 WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
6887 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
6888 CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
6891 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
6893 TestIfBadThingTouchesPlayer(newx, newy);
6894 TestIfBadThingTouchesFriend(newx, newy);
6896 if (!IS_CUSTOM_ELEMENT(element))
6897 TestIfBadThingTouchesOtherBadThing(newx, newy);
6899 else if (element == EL_PENGUIN)
6900 TestIfFriendTouchesBadThing(newx, newy);
6902 /* give the player one last chance (one more frame) to move away */
6903 if (CAN_FALL(element) && direction == MV_DOWN &&
6904 (last_line || (!IS_FREE(x, newy + 1) &&
6905 (!IS_PLAYER(x, newy + 1) ||
6906 game.engine_version < VERSION_IDENT(3,1,1,0)))))
6909 if (pushed_by_player && !game.use_change_when_pushing_bug)
6911 int push_side = MV_DIR_OPPOSITE(direction);
6912 struct PlayerInfo *player = PLAYERINFO(x, y);
6914 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
6915 player->index_bit, push_side);
6916 CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
6917 player->index_bit, push_side);
6920 if (element == EL_EMC_ANDROID && pushed_by_player) /* make another move */
6921 MovDelay[newx][newy] = 1;
6923 CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
6925 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
6928 if (ChangePage[newx][newy] != -1) /* delayed change */
6930 int page = ChangePage[newx][newy];
6931 struct ElementChangeInfo *change = &ei->change_page[page];
6933 ChangePage[newx][newy] = -1;
6935 if (change->can_change)
6937 if (ChangeElement(newx, newy, element, page))
6939 if (change->post_change_function)
6940 change->post_change_function(newx, newy);
6944 if (change->has_action)
6945 ExecuteCustomElementAction(newx, newy, element, page);
6949 TestIfElementHitsCustomElement(newx, newy, direction);
6950 TestIfPlayerTouchesCustomElement(newx, newy);
6951 TestIfElementTouchesCustomElement(newx, newy);
6954 if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
6955 IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
6956 CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
6957 MV_DIR_OPPOSITE(direction));
6961 int AmoebeNachbarNr(int ax, int ay)
6964 int element = Feld[ax][ay];
6966 static int xy[4][2] =
6974 for (i = 0; i < NUM_DIRECTIONS; i++)
6976 int x = ax + xy[i][0];
6977 int y = ay + xy[i][1];
6979 if (!IN_LEV_FIELD(x, y))
6982 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
6983 group_nr = AmoebaNr[x][y];
6989 void AmoebenVereinigen(int ax, int ay)
6991 int i, x, y, xx, yy;
6992 int new_group_nr = AmoebaNr[ax][ay];
6993 static int xy[4][2] =
7001 if (new_group_nr == 0)
7004 for (i = 0; i < NUM_DIRECTIONS; i++)
7009 if (!IN_LEV_FIELD(x, y))
7012 if ((Feld[x][y] == EL_AMOEBA_FULL ||
7013 Feld[x][y] == EL_BD_AMOEBA ||
7014 Feld[x][y] == EL_AMOEBA_DEAD) &&
7015 AmoebaNr[x][y] != new_group_nr)
7017 int old_group_nr = AmoebaNr[x][y];
7019 if (old_group_nr == 0)
7022 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
7023 AmoebaCnt[old_group_nr] = 0;
7024 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
7025 AmoebaCnt2[old_group_nr] = 0;
7028 SCAN_PLAYFIELD(xx, yy)
7030 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
7033 if (AmoebaNr[xx][yy] == old_group_nr)
7034 AmoebaNr[xx][yy] = new_group_nr;
7040 void AmoebeUmwandeln(int ax, int ay)
7044 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
7046 int group_nr = AmoebaNr[ax][ay];
7051 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
7052 printf("AmoebeUmwandeln(): This should never happen!\n");
7058 SCAN_PLAYFIELD(x, y)
7060 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7063 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
7066 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
7070 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
7071 SND_AMOEBA_TURNING_TO_GEM :
7072 SND_AMOEBA_TURNING_TO_ROCK));
7077 static int xy[4][2] =
7085 for (i = 0; i < NUM_DIRECTIONS; i++)
7090 if (!IN_LEV_FIELD(x, y))
7093 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
7095 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
7096 SND_AMOEBA_TURNING_TO_GEM :
7097 SND_AMOEBA_TURNING_TO_ROCK));
7104 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
7107 int group_nr = AmoebaNr[ax][ay];
7108 boolean done = FALSE;
7113 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
7114 printf("AmoebeUmwandelnBD(): This should never happen!\n");
7120 SCAN_PLAYFIELD(x, y)
7122 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7125 if (AmoebaNr[x][y] == group_nr &&
7126 (Feld[x][y] == EL_AMOEBA_DEAD ||
7127 Feld[x][y] == EL_BD_AMOEBA ||
7128 Feld[x][y] == EL_AMOEBA_GROWING))
7131 Feld[x][y] = new_element;
7132 InitField(x, y, FALSE);
7133 DrawLevelField(x, y);
7139 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
7140 SND_BD_AMOEBA_TURNING_TO_ROCK :
7141 SND_BD_AMOEBA_TURNING_TO_GEM));
7144 void AmoebeWaechst(int x, int y)
7146 static unsigned long sound_delay = 0;
7147 static unsigned long sound_delay_value = 0;
7149 if (!MovDelay[x][y]) /* start new growing cycle */
7153 if (DelayReached(&sound_delay, sound_delay_value))
7155 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
7156 sound_delay_value = 30;
7160 if (MovDelay[x][y]) /* wait some time before growing bigger */
7163 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7165 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
7166 6 - MovDelay[x][y]);
7168 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
7171 if (!MovDelay[x][y])
7173 Feld[x][y] = Store[x][y];
7175 DrawLevelField(x, y);
7180 void AmoebaDisappearing(int x, int y)
7182 static unsigned long sound_delay = 0;
7183 static unsigned long sound_delay_value = 0;
7185 if (!MovDelay[x][y]) /* start new shrinking cycle */
7189 if (DelayReached(&sound_delay, sound_delay_value))
7190 sound_delay_value = 30;
7193 if (MovDelay[x][y]) /* wait some time before shrinking */
7196 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7198 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
7199 6 - MovDelay[x][y]);
7201 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
7204 if (!MovDelay[x][y])
7206 Feld[x][y] = EL_EMPTY;
7207 DrawLevelField(x, y);
7209 /* don't let mole enter this field in this cycle;
7210 (give priority to objects falling to this field from above) */
7216 void AmoebeAbleger(int ax, int ay)
7219 int element = Feld[ax][ay];
7220 int graphic = el2img(element);
7221 int newax = ax, neway = ay;
7222 boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
7223 static int xy[4][2] =
7231 if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
7233 Feld[ax][ay] = EL_AMOEBA_DEAD;
7234 DrawLevelField(ax, ay);
7238 if (IS_ANIMATED(graphic))
7239 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7241 if (!MovDelay[ax][ay]) /* start making new amoeba field */
7242 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
7244 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
7247 if (MovDelay[ax][ay])
7251 if (can_drop) /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
7254 int x = ax + xy[start][0];
7255 int y = ay + xy[start][1];
7257 if (!IN_LEV_FIELD(x, y))
7260 if (IS_FREE(x, y) ||
7261 CAN_GROW_INTO(Feld[x][y]) ||
7262 Feld[x][y] == EL_QUICKSAND_EMPTY)
7268 if (newax == ax && neway == ay)
7271 else /* normal or "filled" (BD style) amoeba */
7274 boolean waiting_for_player = FALSE;
7276 for (i = 0; i < NUM_DIRECTIONS; i++)
7278 int j = (start + i) % 4;
7279 int x = ax + xy[j][0];
7280 int y = ay + xy[j][1];
7282 if (!IN_LEV_FIELD(x, y))
7285 if (IS_FREE(x, y) ||
7286 CAN_GROW_INTO(Feld[x][y]) ||
7287 Feld[x][y] == EL_QUICKSAND_EMPTY)
7293 else if (IS_PLAYER(x, y))
7294 waiting_for_player = TRUE;
7297 if (newax == ax && neway == ay) /* amoeba cannot grow */
7299 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
7301 Feld[ax][ay] = EL_AMOEBA_DEAD;
7302 DrawLevelField(ax, ay);
7303 AmoebaCnt[AmoebaNr[ax][ay]]--;
7305 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
7307 if (element == EL_AMOEBA_FULL)
7308 AmoebeUmwandeln(ax, ay);
7309 else if (element == EL_BD_AMOEBA)
7310 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
7315 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
7317 /* amoeba gets larger by growing in some direction */
7319 int new_group_nr = AmoebaNr[ax][ay];
7322 if (new_group_nr == 0)
7324 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
7325 printf("AmoebeAbleger(): This should never happen!\n");
7330 AmoebaNr[newax][neway] = new_group_nr;
7331 AmoebaCnt[new_group_nr]++;
7332 AmoebaCnt2[new_group_nr]++;
7334 /* if amoeba touches other amoeba(s) after growing, unify them */
7335 AmoebenVereinigen(newax, neway);
7337 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
7339 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
7345 if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
7346 (neway == lev_fieldy - 1 && newax != ax))
7348 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
7349 Store[newax][neway] = element;
7351 else if (neway == ay || element == EL_EMC_DRIPPER)
7353 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
7355 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
7359 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
7360 Feld[ax][ay] = EL_AMOEBA_DROPPING;
7361 Store[ax][ay] = EL_AMOEBA_DROP;
7362 ContinueMoving(ax, ay);
7366 DrawLevelField(newax, neway);
7369 void Life(int ax, int ay)
7373 static int life[4] = { 2, 3, 3, 3 }; /* parameters for "game of life" */
7376 int element = Feld[ax][ay];
7377 int graphic = el2img(element);
7378 int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
7380 boolean changed = FALSE;
7382 if (IS_ANIMATED(graphic))
7383 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7388 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
7389 MovDelay[ax][ay] = life_time;
7391 if (MovDelay[ax][ay]) /* wait some time before next cycle */
7394 if (MovDelay[ax][ay])
7398 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
7400 int xx = ax+x1, yy = ay+y1;
7403 if (!IN_LEV_FIELD(xx, yy))
7406 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
7408 int x = xx+x2, y = yy+y2;
7410 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
7413 if (((Feld[x][y] == element ||
7414 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
7416 (IS_FREE(x, y) && Stop[x][y]))
7420 if (xx == ax && yy == ay) /* field in the middle */
7422 if (nachbarn < life_parameter[0] ||
7423 nachbarn > life_parameter[1])
7425 Feld[xx][yy] = EL_EMPTY;
7427 DrawLevelField(xx, yy);
7428 Stop[xx][yy] = TRUE;
7432 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
7433 { /* free border field */
7434 if (nachbarn >= life_parameter[2] &&
7435 nachbarn <= life_parameter[3])
7437 Feld[xx][yy] = element;
7438 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
7440 DrawLevelField(xx, yy);
7441 Stop[xx][yy] = TRUE;
7448 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
7449 SND_GAME_OF_LIFE_GROWING);
7452 static void InitRobotWheel(int x, int y)
7454 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
7457 static void RunRobotWheel(int x, int y)
7459 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
7462 static void StopRobotWheel(int x, int y)
7464 if (ZX == x && ZY == y)
7468 static void InitTimegateWheel(int x, int y)
7470 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
7473 static void RunTimegateWheel(int x, int y)
7475 PlayLevelSound(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
7478 static void InitMagicBallDelay(int x, int y)
7481 ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
7483 ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
7487 static void ActivateMagicBall(int bx, int by)
7491 if (level.ball_random)
7493 int pos_border = RND(8); /* select one of the eight border elements */
7494 int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
7495 int xx = pos_content % 3;
7496 int yy = pos_content / 3;
7501 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
7502 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
7506 for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
7508 int xx = x - bx + 1;
7509 int yy = y - by + 1;
7511 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
7512 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
7516 game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
7519 static void InitDiagonalMovingElement(int x, int y)
7522 MovDelay[x][y] = level.android_move_time;
7526 void CheckExit(int x, int y)
7528 if (local_player->gems_still_needed > 0 ||
7529 local_player->sokobanfields_still_needed > 0 ||
7530 local_player->lights_still_needed > 0)
7532 int element = Feld[x][y];
7533 int graphic = el2img(element);
7535 if (IS_ANIMATED(graphic))
7536 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7541 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7544 Feld[x][y] = EL_EXIT_OPENING;
7546 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
7549 void CheckExitSP(int x, int y)
7551 if (local_player->gems_still_needed > 0)
7553 int element = Feld[x][y];
7554 int graphic = el2img(element);
7556 if (IS_ANIMATED(graphic))
7557 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7562 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7565 Feld[x][y] = EL_SP_EXIT_OPENING;
7567 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
7570 static void CloseAllOpenTimegates()
7575 SCAN_PLAYFIELD(x, y)
7577 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7580 int element = Feld[x][y];
7582 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
7584 Feld[x][y] = EL_TIMEGATE_CLOSING;
7586 PlayLevelSoundAction(x, y, ACTION_CLOSING);
7591 void EdelsteinFunkeln(int x, int y)
7593 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
7596 if (Feld[x][y] == EL_BD_DIAMOND)
7599 if (MovDelay[x][y] == 0) /* next animation frame */
7600 MovDelay[x][y] = 11 * !SimpleRND(500);
7602 if (MovDelay[x][y] != 0) /* wait some time before next frame */
7606 if (setup.direct_draw && MovDelay[x][y])
7607 SetDrawtoField(DRAW_BUFFERED);
7609 DrawLevelElementAnimation(x, y, Feld[x][y]);
7611 if (MovDelay[x][y] != 0)
7613 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
7614 10 - MovDelay[x][y]);
7616 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
7618 if (setup.direct_draw)
7622 dest_x = FX + SCREENX(x) * TILEX;
7623 dest_y = FY + SCREENY(y) * TILEY;
7625 BlitBitmap(drawto_field, window,
7626 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
7627 SetDrawtoField(DRAW_DIRECT);
7633 void MauerWaechst(int x, int y)
7637 if (!MovDelay[x][y]) /* next animation frame */
7638 MovDelay[x][y] = 3 * delay;
7640 if (MovDelay[x][y]) /* wait some time before next frame */
7644 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7646 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
7647 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
7649 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
7652 if (!MovDelay[x][y])
7654 if (MovDir[x][y] == MV_LEFT)
7656 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
7657 DrawLevelField(x - 1, y);
7659 else if (MovDir[x][y] == MV_RIGHT)
7661 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
7662 DrawLevelField(x + 1, y);
7664 else if (MovDir[x][y] == MV_UP)
7666 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
7667 DrawLevelField(x, y - 1);
7671 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
7672 DrawLevelField(x, y + 1);
7675 Feld[x][y] = Store[x][y];
7677 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7678 DrawLevelField(x, y);
7683 void MauerAbleger(int ax, int ay)
7685 int element = Feld[ax][ay];
7686 int graphic = el2img(element);
7687 boolean oben_frei = FALSE, unten_frei = FALSE;
7688 boolean links_frei = FALSE, rechts_frei = FALSE;
7689 boolean oben_massiv = FALSE, unten_massiv = FALSE;
7690 boolean links_massiv = FALSE, rechts_massiv = FALSE;
7691 boolean new_wall = FALSE;
7693 if (IS_ANIMATED(graphic))
7694 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7696 if (!MovDelay[ax][ay]) /* start building new wall */
7697 MovDelay[ax][ay] = 6;
7699 if (MovDelay[ax][ay]) /* wait some time before building new wall */
7702 if (MovDelay[ax][ay])
7706 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
7708 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
7710 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
7712 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
7715 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
7716 element == EL_EXPANDABLE_WALL_ANY)
7720 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
7721 Store[ax][ay-1] = element;
7722 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
7723 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
7724 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
7725 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
7730 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
7731 Store[ax][ay+1] = element;
7732 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
7733 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
7734 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
7735 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
7740 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7741 element == EL_EXPANDABLE_WALL_ANY ||
7742 element == EL_EXPANDABLE_WALL)
7746 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
7747 Store[ax-1][ay] = element;
7748 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
7749 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
7750 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
7751 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
7757 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
7758 Store[ax+1][ay] = element;
7759 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
7760 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
7761 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
7762 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
7767 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
7768 DrawLevelField(ax, ay);
7770 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
7772 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
7773 unten_massiv = TRUE;
7774 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
7775 links_massiv = TRUE;
7776 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
7777 rechts_massiv = TRUE;
7779 if (((oben_massiv && unten_massiv) ||
7780 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7781 element == EL_EXPANDABLE_WALL) &&
7782 ((links_massiv && rechts_massiv) ||
7783 element == EL_EXPANDABLE_WALL_VERTICAL))
7784 Feld[ax][ay] = EL_WALL;
7787 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
7790 void CheckForDragon(int x, int y)
7793 boolean dragon_found = FALSE;
7794 static int xy[4][2] =
7802 for (i = 0; i < NUM_DIRECTIONS; i++)
7804 for (j = 0; j < 4; j++)
7806 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7808 if (IN_LEV_FIELD(xx, yy) &&
7809 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
7811 if (Feld[xx][yy] == EL_DRAGON)
7812 dragon_found = TRUE;
7821 for (i = 0; i < NUM_DIRECTIONS; i++)
7823 for (j = 0; j < 3; j++)
7825 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7827 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
7829 Feld[xx][yy] = EL_EMPTY;
7830 DrawLevelField(xx, yy);
7839 static void InitBuggyBase(int x, int y)
7841 int element = Feld[x][y];
7842 int activating_delay = FRAMES_PER_SECOND / 4;
7845 (element == EL_SP_BUGGY_BASE ?
7846 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
7847 element == EL_SP_BUGGY_BASE_ACTIVATING ?
7849 element == EL_SP_BUGGY_BASE_ACTIVE ?
7850 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
7853 static void WarnBuggyBase(int x, int y)
7856 static int xy[4][2] =
7864 for (i = 0; i < NUM_DIRECTIONS; i++)
7866 int xx = x + xy[i][0];
7867 int yy = y + xy[i][1];
7869 if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
7871 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
7878 static void InitTrap(int x, int y)
7880 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
7883 static void ActivateTrap(int x, int y)
7885 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
7888 static void ChangeActiveTrap(int x, int y)
7890 int graphic = IMG_TRAP_ACTIVE;
7892 /* if new animation frame was drawn, correct crumbled sand border */
7893 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
7894 DrawLevelFieldCrumbledSand(x, y);
7897 static int getSpecialActionElement(int element, int number, int base_element)
7899 return (element != EL_EMPTY ? element :
7900 number != -1 ? base_element + number - 1 :
7904 static int getModifiedActionNumber(int value_old, int operator, int operand,
7905 int value_min, int value_max)
7907 int value_new = (operator == CA_MODE_SET ? operand :
7908 operator == CA_MODE_ADD ? value_old + operand :
7909 operator == CA_MODE_SUBTRACT ? value_old - operand :
7910 operator == CA_MODE_MULTIPLY ? value_old * operand :
7911 operator == CA_MODE_DIVIDE ? value_old / MAX(1, operand) :
7912 operator == CA_MODE_MODULO ? value_old % MAX(1, operand) :
7915 return (value_new < value_min ? value_min :
7916 value_new > value_max ? value_max :
7920 static void ExecuteCustomElementAction(int x, int y, int element, int page)
7922 struct ElementInfo *ei = &element_info[element];
7923 struct ElementChangeInfo *change = &ei->change_page[page];
7924 int target_element = change->target_element;
7925 int action_type = change->action_type;
7926 int action_mode = change->action_mode;
7927 int action_arg = change->action_arg;
7930 if (!change->has_action)
7933 /* ---------- determine action paramater values -------------------------- */
7935 int level_time_value =
7936 (level.time > 0 ? TimeLeft :
7939 int action_arg_element =
7940 (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player :
7941 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
7942 action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element :
7945 int action_arg_direction =
7946 (action_arg >= CA_ARG_DIRECTION_LEFT &&
7947 action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
7948 action_arg == CA_ARG_DIRECTION_TRIGGER ?
7949 change->actual_trigger_side :
7950 action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
7951 MV_DIR_OPPOSITE(change->actual_trigger_side) :
7954 int action_arg_number_min =
7955 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
7958 int action_arg_number_max =
7959 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
7960 action_type == CA_SET_LEVEL_GEMS ? 999 :
7961 action_type == CA_SET_LEVEL_TIME ? 9999 :
7962 action_type == CA_SET_LEVEL_SCORE ? 99999 :
7963 action_type == CA_SET_CE_VALUE ? 9999 :
7964 action_type == CA_SET_CE_SCORE ? 9999 :
7967 int action_arg_number_reset =
7968 (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize :
7969 action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
7970 action_type == CA_SET_LEVEL_TIME ? level.time :
7971 action_type == CA_SET_LEVEL_SCORE ? 0 :
7973 action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
7975 action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
7977 action_type == CA_SET_CE_SCORE ? 0 :
7980 int action_arg_number =
7981 (action_arg <= CA_ARG_MAX ? action_arg :
7982 action_arg >= CA_ARG_SPEED_NOT_MOVING &&
7983 action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
7984 action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
7985 action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
7986 action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
7987 action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
7988 #if USE_NEW_CUSTOM_VALUE
7989 action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
7991 action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
7993 action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
7994 action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
7995 action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
7996 action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
7997 action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
7998 action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
7999 action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
8000 action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
8001 action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
8002 action_arg == CA_ARG_ELEMENT_NR_TARGET ? change->target_element :
8003 action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
8006 int action_arg_number_old =
8007 (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
8008 action_type == CA_SET_LEVEL_TIME ? TimeLeft :
8009 action_type == CA_SET_LEVEL_SCORE ? local_player->score :
8010 action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
8011 action_type == CA_SET_CE_SCORE ? ei->collect_score :
8014 int action_arg_number_new =
8015 getModifiedActionNumber(action_arg_number_old,
8016 action_mode, action_arg_number,
8017 action_arg_number_min, action_arg_number_max);
8019 int trigger_player_bits =
8020 (change->actual_trigger_player >= EL_PLAYER_1 &&
8021 change->actual_trigger_player <= EL_PLAYER_4 ?
8022 (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
8025 int action_arg_player_bits =
8026 (action_arg >= CA_ARG_PLAYER_1 &&
8027 action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
8028 action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
8031 /* ---------- execute action -------------------------------------------- */
8040 /* ---------- level actions ------------------------------------------- */
8042 case CA_RESTART_LEVEL:
8044 game.restart_level = TRUE;
8049 case CA_SHOW_ENVELOPE:
8051 int element = getSpecialActionElement(action_arg_element,
8052 action_arg_number, EL_ENVELOPE_1);
8054 if (IS_ENVELOPE(element))
8055 local_player->show_envelope = element;
8060 case CA_SET_LEVEL_TIME:
8062 if (level.time > 0) /* only modify limited time value */
8064 TimeLeft = action_arg_number_new;
8066 DrawGameValue_Time(TimeLeft);
8068 if (!TimeLeft && setup.time_limit)
8069 for (i = 0; i < MAX_PLAYERS; i++)
8070 KillPlayer(&stored_player[i]);
8076 case CA_SET_LEVEL_SCORE:
8078 local_player->score = action_arg_number_new;
8080 DrawGameValue_Score(local_player->score);
8085 case CA_SET_LEVEL_GEMS:
8087 local_player->gems_still_needed = action_arg_number_new;
8089 DrawGameValue_Emeralds(local_player->gems_still_needed);
8094 case CA_SET_LEVEL_GRAVITY:
8096 game.gravity = (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
8097 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
8098 action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
8103 case CA_SET_LEVEL_WIND:
8105 game.wind_direction = action_arg_direction;
8110 /* ---------- player actions ------------------------------------------ */
8112 case CA_MOVE_PLAYER:
8114 /* automatically move to the next field in specified direction */
8115 for (i = 0; i < MAX_PLAYERS; i++)
8116 if (trigger_player_bits & (1 << i))
8117 stored_player[i].programmed_action = action_arg_direction;
8122 case CA_EXIT_PLAYER:
8124 for (i = 0; i < MAX_PLAYERS; i++)
8125 if (action_arg_player_bits & (1 << i))
8126 stored_player[i].LevelSolved = stored_player[i].GameOver = TRUE;
8131 case CA_KILL_PLAYER:
8133 for (i = 0; i < MAX_PLAYERS; i++)
8134 if (action_arg_player_bits & (1 << i))
8135 KillPlayer(&stored_player[i]);
8140 case CA_SET_PLAYER_KEYS:
8142 int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
8143 int element = getSpecialActionElement(action_arg_element,
8144 action_arg_number, EL_KEY_1);
8146 if (IS_KEY(element))
8148 for (i = 0; i < MAX_PLAYERS; i++)
8150 if (trigger_player_bits & (1 << i))
8152 stored_player[i].key[KEY_NR(element)] = key_state;
8155 DrawGameDoorValues();
8157 DrawGameValue_Keys(stored_player[i].key);
8160 redraw_mask |= REDRAW_DOOR_1;
8168 case CA_SET_PLAYER_SPEED:
8170 for (i = 0; i < MAX_PLAYERS; i++)
8172 if (trigger_player_bits & (1 << i))
8174 int move_stepsize = TILEX / stored_player[i].move_delay_value;
8176 if (action_arg == CA_ARG_SPEED_FASTER &&
8177 stored_player[i].cannot_move)
8179 action_arg_number = STEPSIZE_VERY_SLOW;
8181 else if (action_arg == CA_ARG_SPEED_SLOWER ||
8182 action_arg == CA_ARG_SPEED_FASTER)
8184 action_arg_number = 2;
8185 action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
8190 getModifiedActionNumber(move_stepsize,
8193 action_arg_number_min,
8194 action_arg_number_max);
8197 SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
8199 /* make sure that value is power of 2 */
8200 move_stepsize = (1 << log_2(move_stepsize));
8202 /* do no immediately change -- the player might just be moving */
8203 stored_player[i].move_delay_value_next = TILEX / move_stepsize;
8205 stored_player[i].cannot_move =
8206 (action_arg == CA_ARG_SPEED_NOT_MOVING ? TRUE : FALSE);
8214 case CA_SET_PLAYER_SHIELD:
8216 for (i = 0; i < MAX_PLAYERS; i++)
8218 if (trigger_player_bits & (1 << i))
8220 if (action_arg == CA_ARG_SHIELD_OFF)
8222 stored_player[i].shield_normal_time_left = 0;
8223 stored_player[i].shield_deadly_time_left = 0;
8225 else if (action_arg == CA_ARG_SHIELD_NORMAL)
8227 stored_player[i].shield_normal_time_left = 999999;
8229 else if (action_arg == CA_ARG_SHIELD_DEADLY)
8231 stored_player[i].shield_normal_time_left = 999999;
8232 stored_player[i].shield_deadly_time_left = 999999;
8240 case CA_SET_PLAYER_ARTWORK:
8242 for (i = 0; i < MAX_PLAYERS; i++)
8244 if (trigger_player_bits & (1 << i))
8246 int artwork_element = action_arg_element;
8248 if (action_arg == CA_ARG_ELEMENT_RESET)
8250 (level.use_artwork_element[i] ? level.artwork_element[i] :
8251 stored_player[i].element_nr);
8253 stored_player[i].artwork_element = artwork_element;
8255 SetPlayerWaiting(&stored_player[i], FALSE);
8257 /* set number of special actions for bored and sleeping animation */
8258 stored_player[i].num_special_action_bored =
8259 get_num_special_action(artwork_element,
8260 ACTION_BORING_1, ACTION_BORING_LAST);
8261 stored_player[i].num_special_action_sleeping =
8262 get_num_special_action(artwork_element,
8263 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
8270 /* ---------- CE actions ---------------------------------------------- */
8272 case CA_SET_CE_VALUE:
8274 #if USE_NEW_CUSTOM_VALUE
8275 int last_custom_value = CustomValue[x][y];
8277 CustomValue[x][y] = action_arg_number_new;
8280 printf("::: Count == %d\n", CustomValue[x][y]);
8283 if (CustomValue[x][y] == 0 && last_custom_value > 0)
8286 printf("::: CE_VALUE_GETS_ZERO\n");
8289 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
8290 CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
8293 printf("::: RESULT: %d, %d\n", Feld[x][y], ChangePage[x][y]);
8301 case CA_SET_CE_SCORE:
8303 ei->collect_score = action_arg_number_new;
8308 /* ---------- engine actions ------------------------------------------ */
8310 case CA_SET_ENGINE_SCAN_MODE:
8312 InitPlayfieldScanMode(action_arg);
8322 static void CreateFieldExt(int x, int y, int element, boolean is_change)
8324 int old_element = Feld[x][y];
8325 int new_element = get_element_from_group_element(element);
8326 int previous_move_direction = MovDir[x][y];
8327 #if USE_NEW_CUSTOM_VALUE
8328 int last_ce_value = CustomValue[x][y];
8330 boolean add_player = (ELEM_IS_PLAYER(new_element) &&
8331 IS_WALKABLE(old_element));
8334 /* check if element under player changes from accessible to unaccessible
8335 (needed for special case of dropping element which then changes) */
8336 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
8337 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
8347 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
8348 RemoveMovingField(x, y);
8352 Feld[x][y] = new_element;
8354 #if !USE_GFX_RESET_GFX_ANIMATION
8355 ResetGfxAnimation(x, y);
8356 ResetRandomAnimationValue(x, y);
8359 if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
8360 MovDir[x][y] = previous_move_direction;
8362 #if USE_NEW_CUSTOM_VALUE
8363 if (element_info[new_element].use_last_ce_value)
8364 CustomValue[x][y] = last_ce_value;
8367 InitField_WithBug1(x, y, FALSE);
8369 new_element = Feld[x][y]; /* element may have changed */
8371 #if USE_GFX_RESET_GFX_ANIMATION
8372 ResetGfxAnimation(x, y);
8373 ResetRandomAnimationValue(x, y);
8376 DrawLevelField(x, y);
8378 if (GFX_CRUMBLED(new_element))
8379 DrawLevelFieldCrumbledSandNeighbours(x, y);
8383 /* check if element under player changes from accessible to unaccessible
8384 (needed for special case of dropping element which then changes) */
8385 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
8386 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
8394 /* "ChangeCount" not set yet to allow "entered by player" change one time */
8395 if (ELEM_IS_PLAYER(new_element))
8396 RelocatePlayer(x, y, new_element);
8399 ChangeCount[x][y]++; /* count number of changes in the same frame */
8401 TestIfBadThingTouchesPlayer(x, y);
8402 TestIfPlayerTouchesCustomElement(x, y);
8403 TestIfElementTouchesCustomElement(x, y);
8406 static void CreateField(int x, int y, int element)
8408 CreateFieldExt(x, y, element, FALSE);
8411 static void CreateElementFromChange(int x, int y, int element)
8413 element = GET_VALID_RUNTIME_ELEMENT(element);
8415 #if USE_STOP_CHANGED_ELEMENTS
8416 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
8418 int old_element = Feld[x][y];
8420 /* prevent changed element from moving in same engine frame
8421 unless both old and new element can either fall or move */
8422 if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
8423 (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
8428 CreateFieldExt(x, y, element, TRUE);
8431 static boolean ChangeElement(int x, int y, int element, int page)
8433 struct ElementInfo *ei = &element_info[element];
8434 struct ElementChangeInfo *change = &ei->change_page[page];
8435 int ce_value = CustomValue[x][y];
8436 int ce_score = ei->collect_score;
8438 int old_element = Feld[x][y];
8440 /* always use default change event to prevent running into a loop */
8441 if (ChangeEvent[x][y] == -1)
8442 ChangeEvent[x][y] = CE_DELAY;
8444 if (ChangeEvent[x][y] == CE_DELAY)
8446 /* reset actual trigger element, trigger player and action element */
8447 change->actual_trigger_element = EL_EMPTY;
8448 change->actual_trigger_player = EL_PLAYER_1;
8449 change->actual_trigger_side = CH_SIDE_NONE;
8450 change->actual_trigger_ce_value = 0;
8451 change->actual_trigger_ce_score = 0;
8454 /* do not change elements more than a specified maximum number of changes */
8455 if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
8458 ChangeCount[x][y]++; /* count number of changes in the same frame */
8460 if (change->explode)
8467 if (change->use_target_content)
8469 boolean complete_replace = TRUE;
8470 boolean can_replace[3][3];
8473 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
8476 boolean is_walkable;
8477 boolean is_diggable;
8478 boolean is_collectible;
8479 boolean is_removable;
8480 boolean is_destructible;
8481 int ex = x + xx - 1;
8482 int ey = y + yy - 1;
8483 int content_element = change->target_content.e[xx][yy];
8486 can_replace[xx][yy] = TRUE;
8488 if (ex == x && ey == y) /* do not check changing element itself */
8491 if (content_element == EL_EMPTY_SPACE)
8493 can_replace[xx][yy] = FALSE; /* do not replace border with space */
8498 if (!IN_LEV_FIELD(ex, ey))
8500 can_replace[xx][yy] = FALSE;
8501 complete_replace = FALSE;
8508 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
8509 e = MovingOrBlocked2Element(ex, ey);
8511 is_empty = (IS_FREE(ex, ey) ||
8512 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
8514 is_walkable = (is_empty || IS_WALKABLE(e));
8515 is_diggable = (is_empty || IS_DIGGABLE(e));
8516 is_collectible = (is_empty || IS_COLLECTIBLE(e));
8517 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
8518 is_removable = (is_diggable || is_collectible);
8520 can_replace[xx][yy] =
8521 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
8522 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
8523 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
8524 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
8525 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
8526 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
8527 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
8529 if (!can_replace[xx][yy])
8530 complete_replace = FALSE;
8533 if (!change->only_if_complete || complete_replace)
8535 boolean something_has_changed = FALSE;
8537 if (change->only_if_complete && change->use_random_replace &&
8538 RND(100) < change->random_percentage)
8541 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
8543 int ex = x + xx - 1;
8544 int ey = y + yy - 1;
8545 int content_element;
8547 if (can_replace[xx][yy] && (!change->use_random_replace ||
8548 RND(100) < change->random_percentage))
8550 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
8551 RemoveMovingField(ex, ey);
8553 ChangeEvent[ex][ey] = ChangeEvent[x][y];
8555 content_element = change->target_content.e[xx][yy];
8556 target_element = GET_TARGET_ELEMENT(content_element, change,
8557 ce_value, ce_score);
8559 CreateElementFromChange(ex, ey, target_element);
8561 something_has_changed = TRUE;
8563 /* for symmetry reasons, freeze newly created border elements */
8564 if (ex != x || ey != y)
8565 Stop[ex][ey] = TRUE; /* no more moving in this frame */
8569 if (something_has_changed)
8571 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
8572 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
8578 target_element = GET_TARGET_ELEMENT(change->target_element, change,
8579 ce_value, ce_score);
8581 if (element == EL_DIAGONAL_GROWING ||
8582 element == EL_DIAGONAL_SHRINKING)
8584 target_element = Store[x][y];
8586 Store[x][y] = EL_EMPTY;
8589 CreateElementFromChange(x, y, target_element);
8591 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
8592 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
8595 /* this uses direct change before indirect change */
8596 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
8601 #if USE_NEW_DELAYED_ACTION
8603 static void HandleElementChange(int x, int y, int page)
8605 int element = MovingOrBlocked2Element(x, y);
8606 struct ElementInfo *ei = &element_info[element];
8607 struct ElementChangeInfo *change = &ei->change_page[page];
8610 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
8611 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
8614 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
8615 x, y, element, element_info[element].token_name);
8616 printf("HandleElementChange(): This should never happen!\n");
8621 /* this can happen with classic bombs on walkable, changing elements */
8622 if (!CAN_CHANGE_OR_HAS_ACTION(element))
8625 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
8626 ChangeDelay[x][y] = 0;
8632 if (ChangeDelay[x][y] == 0) /* initialize element change */
8634 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
8636 if (change->can_change)
8638 ResetGfxAnimation(x, y);
8639 ResetRandomAnimationValue(x, y);
8641 if (change->pre_change_function)
8642 change->pre_change_function(x, y);
8646 ChangeDelay[x][y]--;
8648 if (ChangeDelay[x][y] != 0) /* continue element change */
8650 if (change->can_change)
8652 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8654 if (IS_ANIMATED(graphic))
8655 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8657 if (change->change_function)
8658 change->change_function(x, y);
8661 else /* finish element change */
8663 if (ChangePage[x][y] != -1) /* remember page from delayed change */
8665 page = ChangePage[x][y];
8666 ChangePage[x][y] = -1;
8668 change = &ei->change_page[page];
8671 if (IS_MOVING(x, y)) /* never change a running system ;-) */
8673 ChangeDelay[x][y] = 1; /* try change after next move step */
8674 ChangePage[x][y] = page; /* remember page to use for change */
8679 if (change->can_change)
8681 if (ChangeElement(x, y, element, page))
8683 if (change->post_change_function)
8684 change->post_change_function(x, y);
8688 if (change->has_action)
8689 ExecuteCustomElementAction(x, y, element, page);
8695 static void HandleElementChange(int x, int y, int page)
8697 int element = MovingOrBlocked2Element(x, y);
8698 struct ElementInfo *ei = &element_info[element];
8699 struct ElementChangeInfo *change = &ei->change_page[page];
8702 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
8705 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
8706 x, y, element, element_info[element].token_name);
8707 printf("HandleElementChange(): This should never happen!\n");
8712 /* this can happen with classic bombs on walkable, changing elements */
8713 if (!CAN_CHANGE(element))
8716 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
8717 ChangeDelay[x][y] = 0;
8723 if (ChangeDelay[x][y] == 0) /* initialize element change */
8725 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
8727 ResetGfxAnimation(x, y);
8728 ResetRandomAnimationValue(x, y);
8730 if (change->pre_change_function)
8731 change->pre_change_function(x, y);
8734 ChangeDelay[x][y]--;
8736 if (ChangeDelay[x][y] != 0) /* continue element change */
8738 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8740 if (IS_ANIMATED(graphic))
8741 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8743 if (change->change_function)
8744 change->change_function(x, y);
8746 else /* finish element change */
8748 if (ChangePage[x][y] != -1) /* remember page from delayed change */
8750 page = ChangePage[x][y];
8751 ChangePage[x][y] = -1;
8753 change = &ei->change_page[page];
8756 if (IS_MOVING(x, y)) /* never change a running system ;-) */
8758 ChangeDelay[x][y] = 1; /* try change after next move step */
8759 ChangePage[x][y] = page; /* remember page to use for change */
8764 if (ChangeElement(x, y, element, page))
8766 if (change->post_change_function)
8767 change->post_change_function(x, y);
8774 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
8775 int trigger_element,
8781 boolean change_done_any = FALSE;
8782 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
8785 if (!(trigger_events[trigger_element][trigger_event]))
8788 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8790 int element = EL_CUSTOM_START + i;
8791 boolean change_done = FALSE;
8794 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
8795 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
8798 for (p = 0; p < element_info[element].num_change_pages; p++)
8800 struct ElementChangeInfo *change = &element_info[element].change_page[p];
8802 if (change->can_change_or_has_action &&
8803 change->has_event[trigger_event] &&
8804 change->trigger_side & trigger_side &&
8805 change->trigger_player & trigger_player &&
8806 change->trigger_page & trigger_page_bits &&
8807 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
8809 change->actual_trigger_element = trigger_element;
8810 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
8811 change->actual_trigger_side = trigger_side;
8812 change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
8813 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
8815 if ((change->can_change && !change_done) || change->has_action)
8820 SCAN_PLAYFIELD(x, y)
8822 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8825 if (Feld[x][y] == element)
8827 if (change->can_change && !change_done)
8829 ChangeDelay[x][y] = 1;
8830 ChangeEvent[x][y] = trigger_event;
8832 HandleElementChange(x, y, p);
8834 #if USE_NEW_DELAYED_ACTION
8835 else if (change->has_action)
8837 ExecuteCustomElementAction(x, y, element, p);
8838 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8841 if (change->has_action)
8843 ExecuteCustomElementAction(x, y, element, p);
8844 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8850 if (change->can_change)
8853 change_done_any = TRUE;
8860 return change_done_any;
8863 static boolean CheckElementChangeExt(int x, int y,
8865 int trigger_element,
8870 boolean change_done = FALSE;
8873 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
8874 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
8877 if (Feld[x][y] == EL_BLOCKED)
8879 Blocked2Moving(x, y, &x, &y);
8880 element = Feld[x][y];
8884 /* check if element has already changed */
8885 if (Feld[x][y] != element)
8888 /* check if element has already changed or is about to change after moving */
8889 if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
8890 Feld[x][y] != element) ||
8892 (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
8893 (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
8894 ChangePage[x][y] != -1)))
8898 for (p = 0; p < element_info[element].num_change_pages; p++)
8900 struct ElementChangeInfo *change = &element_info[element].change_page[p];
8902 boolean check_trigger_element =
8903 (trigger_event == CE_TOUCHING_X ||
8904 trigger_event == CE_HITTING_X ||
8905 trigger_event == CE_HIT_BY_X);
8907 if (change->can_change_or_has_action &&
8908 change->has_event[trigger_event] &&
8909 change->trigger_side & trigger_side &&
8910 change->trigger_player & trigger_player &&
8911 (!check_trigger_element ||
8912 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
8914 change->actual_trigger_element = trigger_element;
8915 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
8916 change->actual_trigger_side = trigger_side;
8917 change->actual_trigger_ce_value = CustomValue[x][y];
8918 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
8920 /* special case: trigger element not at (x,y) position for some events */
8921 if (check_trigger_element)
8933 { 0, 0 }, { 0, 0 }, { 0, 0 },
8937 int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
8938 int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
8940 change->actual_trigger_ce_value = CustomValue[xx][yy];
8941 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
8944 if (change->can_change && !change_done)
8946 ChangeDelay[x][y] = 1;
8947 ChangeEvent[x][y] = trigger_event;
8949 HandleElementChange(x, y, p);
8953 #if USE_NEW_DELAYED_ACTION
8954 else if (change->has_action)
8956 ExecuteCustomElementAction(x, y, element, p);
8957 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8960 if (change->has_action)
8962 ExecuteCustomElementAction(x, y, element, p);
8963 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8972 static void PlayPlayerSound(struct PlayerInfo *player)
8974 int jx = player->jx, jy = player->jy;
8975 int sound_element = player->artwork_element;
8976 int last_action = player->last_action_waiting;
8977 int action = player->action_waiting;
8979 if (player->is_waiting)
8981 if (action != last_action)
8982 PlayLevelSoundElementAction(jx, jy, sound_element, action);
8984 PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
8988 if (action != last_action)
8989 StopSound(element_info[sound_element].sound[last_action]);
8991 if (last_action == ACTION_SLEEPING)
8992 PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
8996 static void PlayAllPlayersSound()
9000 for (i = 0; i < MAX_PLAYERS; i++)
9001 if (stored_player[i].active)
9002 PlayPlayerSound(&stored_player[i]);
9005 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
9007 boolean last_waiting = player->is_waiting;
9008 int move_dir = player->MovDir;
9010 player->dir_waiting = move_dir;
9011 player->last_action_waiting = player->action_waiting;
9015 if (!last_waiting) /* not waiting -> waiting */
9017 player->is_waiting = TRUE;
9019 player->frame_counter_bored =
9021 game.player_boring_delay_fixed +
9022 SimpleRND(game.player_boring_delay_random);
9023 player->frame_counter_sleeping =
9025 game.player_sleeping_delay_fixed +
9026 SimpleRND(game.player_sleeping_delay_random);
9029 InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
9031 InitPlayerGfxAnimation(player, ACTION_WAITING, player->MovDir);
9035 if (game.player_sleeping_delay_fixed +
9036 game.player_sleeping_delay_random > 0 &&
9037 player->anim_delay_counter == 0 &&
9038 player->post_delay_counter == 0 &&
9039 FrameCounter >= player->frame_counter_sleeping)
9040 player->is_sleeping = TRUE;
9041 else if (game.player_boring_delay_fixed +
9042 game.player_boring_delay_random > 0 &&
9043 FrameCounter >= player->frame_counter_bored)
9044 player->is_bored = TRUE;
9046 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
9047 player->is_bored ? ACTION_BORING :
9051 if (player->is_sleeping && player->use_murphy)
9053 /* special case for sleeping Murphy when leaning against non-free tile */
9055 if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
9056 (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
9057 !IS_MOVING(player->jx - 1, player->jy)))
9059 else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
9060 (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
9061 !IS_MOVING(player->jx + 1, player->jy)))
9062 move_dir = MV_RIGHT;
9064 player->is_sleeping = FALSE;
9066 player->dir_waiting = move_dir;
9070 if (player->is_sleeping)
9072 if (player->num_special_action_sleeping > 0)
9074 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
9076 int last_special_action = player->special_action_sleeping;
9077 int num_special_action = player->num_special_action_sleeping;
9078 int special_action =
9079 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
9080 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
9081 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
9082 last_special_action + 1 : ACTION_SLEEPING);
9083 int special_graphic =
9084 el_act_dir2img(player->artwork_element, special_action, move_dir);
9086 player->anim_delay_counter =
9087 graphic_info[special_graphic].anim_delay_fixed +
9088 SimpleRND(graphic_info[special_graphic].anim_delay_random);
9089 player->post_delay_counter =
9090 graphic_info[special_graphic].post_delay_fixed +
9091 SimpleRND(graphic_info[special_graphic].post_delay_random);
9093 player->special_action_sleeping = special_action;
9096 if (player->anim_delay_counter > 0)
9098 player->action_waiting = player->special_action_sleeping;
9099 player->anim_delay_counter--;
9101 else if (player->post_delay_counter > 0)
9103 player->post_delay_counter--;
9107 else if (player->is_bored)
9109 if (player->num_special_action_bored > 0)
9111 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
9113 int special_action =
9114 ACTION_BORING_1 + SimpleRND(player->num_special_action_bored);
9115 int special_graphic =
9116 el_act_dir2img(player->artwork_element, special_action, move_dir);
9118 player->anim_delay_counter =
9119 graphic_info[special_graphic].anim_delay_fixed +
9120 SimpleRND(graphic_info[special_graphic].anim_delay_random);
9121 player->post_delay_counter =
9122 graphic_info[special_graphic].post_delay_fixed +
9123 SimpleRND(graphic_info[special_graphic].post_delay_random);
9125 player->special_action_bored = special_action;
9128 if (player->anim_delay_counter > 0)
9130 player->action_waiting = player->special_action_bored;
9131 player->anim_delay_counter--;
9133 else if (player->post_delay_counter > 0)
9135 player->post_delay_counter--;
9140 else if (last_waiting) /* waiting -> not waiting */
9142 player->is_waiting = FALSE;
9143 player->is_bored = FALSE;
9144 player->is_sleeping = FALSE;
9146 player->frame_counter_bored = -1;
9147 player->frame_counter_sleeping = -1;
9149 player->anim_delay_counter = 0;
9150 player->post_delay_counter = 0;
9152 player->dir_waiting = player->MovDir;
9153 player->action_waiting = ACTION_DEFAULT;
9155 player->special_action_bored = ACTION_DEFAULT;
9156 player->special_action_sleeping = ACTION_DEFAULT;
9160 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
9162 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
9163 int left = player_action & JOY_LEFT;
9164 int right = player_action & JOY_RIGHT;
9165 int up = player_action & JOY_UP;
9166 int down = player_action & JOY_DOWN;
9167 int button1 = player_action & JOY_BUTTON_1;
9168 int button2 = player_action & JOY_BUTTON_2;
9169 int dx = (left ? -1 : right ? 1 : 0);
9170 int dy = (up ? -1 : down ? 1 : 0);
9172 if (!player->active || tape.pausing)
9178 snapped = SnapField(player, dx, dy);
9182 dropped = DropElement(player);
9184 moved = MovePlayer(player, dx, dy);
9187 if (tape.single_step && tape.recording && !tape.pausing)
9189 if (button1 || (dropped && !moved))
9191 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9192 SnapField(player, 0, 0); /* stop snapping */
9196 SetPlayerWaiting(player, FALSE);
9198 return player_action;
9202 /* no actions for this player (no input at player's configured device) */
9204 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
9205 SnapField(player, 0, 0);
9206 CheckGravityMovementWhenNotMoving(player);
9208 if (player->MovPos == 0)
9209 SetPlayerWaiting(player, TRUE);
9211 if (player->MovPos == 0) /* needed for tape.playing */
9212 player->is_moving = FALSE;
9214 player->is_dropping = FALSE;
9215 player->is_dropping_pressed = FALSE;
9216 player->drop_pressed_delay = 0;
9222 static void CheckLevelTime()
9226 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9228 if (level.native_em_level->lev->home == 0) /* all players at home */
9230 local_player->LevelSolved = TRUE;
9231 AllPlayersGone = TRUE;
9233 level.native_em_level->lev->home = -1;
9236 if (level.native_em_level->ply[0]->alive == 0 &&
9237 level.native_em_level->ply[1]->alive == 0 &&
9238 level.native_em_level->ply[2]->alive == 0 &&
9239 level.native_em_level->ply[3]->alive == 0) /* all dead */
9240 AllPlayersGone = TRUE;
9243 if (TimeFrames >= FRAMES_PER_SECOND)
9248 for (i = 0; i < MAX_PLAYERS; i++)
9250 struct PlayerInfo *player = &stored_player[i];
9252 if (SHIELD_ON(player))
9254 player->shield_normal_time_left--;
9256 if (player->shield_deadly_time_left > 0)
9257 player->shield_deadly_time_left--;
9261 if (!level.use_step_counter)
9269 if (TimeLeft <= 10 && setup.time_limit)
9270 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
9272 DrawGameValue_Time(TimeLeft);
9274 if (!TimeLeft && setup.time_limit)
9276 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9277 level.native_em_level->lev->killed_out_of_time = TRUE;
9279 for (i = 0; i < MAX_PLAYERS; i++)
9280 KillPlayer(&stored_player[i]);
9283 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
9284 DrawGameValue_Time(TimePlayed);
9286 level.native_em_level->lev->time =
9287 (level.time == 0 ? TimePlayed : TimeLeft);
9290 if (tape.recording || tape.playing)
9291 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
9295 void AdvanceFrameAndPlayerCounters(int player_nr)
9300 Error(ERR_NETWORK_CLIENT, "advancing frame counter from %d to %d",
9301 FrameCounter, FrameCounter + 1);
9304 /* advance frame counters (global frame counter and time frame counter) */
9308 /* advance player counters (counters for move delay, move animation etc.) */
9309 for (i = 0; i < MAX_PLAYERS; i++)
9311 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
9312 int move_delay_value = stored_player[i].move_delay_value;
9313 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
9315 if (!advance_player_counters) /* not all players may be affected */
9318 #if USE_NEW_PLAYER_ANIM
9319 if (move_frames == 0) /* less than one move per game frame */
9321 int stepsize = TILEX / move_delay_value;
9322 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
9323 int count = (stored_player[i].is_moving ?
9324 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
9326 if (count % delay == 0)
9331 stored_player[i].Frame += move_frames;
9333 if (stored_player[i].MovPos != 0)
9334 stored_player[i].StepFrame += move_frames;
9336 if (stored_player[i].move_delay > 0)
9337 stored_player[i].move_delay--;
9339 /* due to bugs in previous versions, counter must count up, not down */
9340 if (stored_player[i].push_delay != -1)
9341 stored_player[i].push_delay++;
9343 if (stored_player[i].drop_delay > 0)
9344 stored_player[i].drop_delay--;
9346 if (stored_player[i].is_dropping_pressed)
9347 stored_player[i].drop_pressed_delay++;
9351 void StartGameActions(boolean init_network_game, boolean record_tape,
9354 unsigned long new_random_seed = InitRND(random_seed);
9357 TapeStartRecording(new_random_seed);
9359 #if defined(NETWORK_AVALIABLE)
9360 if (init_network_game)
9362 SendToServer_StartPlaying();
9370 game_status = GAME_MODE_PLAYING;
9377 static unsigned long game_frame_delay = 0;
9378 unsigned long game_frame_delay_value;
9379 byte *recorded_player_action;
9380 byte summarized_player_action = 0;
9381 byte tape_action[MAX_PLAYERS];
9384 if (game.restart_level)
9385 StartGameActions(options.network, setup.autorecord, NEW_RANDOMIZE);
9387 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9389 if (level.native_em_level->lev->home == 0) /* all players at home */
9391 local_player->LevelSolved = TRUE;
9392 AllPlayersGone = TRUE;
9394 level.native_em_level->lev->home = -1;
9397 if (level.native_em_level->ply[0]->alive == 0 &&
9398 level.native_em_level->ply[1]->alive == 0 &&
9399 level.native_em_level->ply[2]->alive == 0 &&
9400 level.native_em_level->ply[3]->alive == 0) /* all dead */
9401 AllPlayersGone = TRUE;
9404 if (local_player->LevelSolved)
9407 if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
9410 if (game_status != GAME_MODE_PLAYING) /* status might have changed */
9413 game_frame_delay_value =
9414 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
9416 if (tape.playing && tape.warp_forward && !tape.pausing)
9417 game_frame_delay_value = 0;
9419 /* ---------- main game synchronization point ---------- */
9421 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
9423 if (network_playing && !network_player_action_received)
9425 /* try to get network player actions in time */
9427 #if defined(NETWORK_AVALIABLE)
9428 /* last chance to get network player actions without main loop delay */
9432 /* game was quit by network peer */
9433 if (game_status != GAME_MODE_PLAYING)
9436 if (!network_player_action_received)
9437 return; /* failed to get network player actions in time */
9439 /* do not yet reset "network_player_action_received" (for tape.pausing) */
9445 /* at this point we know that we really continue executing the game */
9448 network_player_action_received = FALSE;
9451 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
9453 if (tape.set_centered_player)
9455 game.centered_player_nr_next = tape.centered_player_nr_next;
9456 game.set_centered_player = TRUE;
9459 for (i = 0; i < MAX_PLAYERS; i++)
9461 summarized_player_action |= stored_player[i].action;
9463 if (!network_playing)
9464 stored_player[i].effective_action = stored_player[i].action;
9467 #if defined(NETWORK_AVALIABLE)
9468 if (network_playing)
9469 SendToServer_MovePlayer(summarized_player_action);
9472 if (!options.network && !setup.team_mode)
9473 local_player->effective_action = summarized_player_action;
9475 if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
9477 for (i = 0; i < MAX_PLAYERS; i++)
9478 stored_player[i].effective_action =
9479 (i == game.centered_player_nr ? summarized_player_action : 0);
9482 if (recorded_player_action != NULL)
9483 for (i = 0; i < MAX_PLAYERS; i++)
9484 stored_player[i].effective_action = recorded_player_action[i];
9486 for (i = 0; i < MAX_PLAYERS; i++)
9488 tape_action[i] = stored_player[i].effective_action;
9490 /* (this can only happen in the R'n'D game engine) */
9491 if (tape.recording && tape_action[i] && !tape.player_participates[i])
9492 tape.player_participates[i] = TRUE; /* player just appeared from CE */
9495 /* only record actions from input devices, but not programmed actions */
9497 TapeRecordAction(tape_action);
9499 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9501 GameActions_EM_Main();
9509 void GameActions_EM_Main()
9511 byte effective_action[MAX_PLAYERS];
9512 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
9515 for (i = 0; i < MAX_PLAYERS; i++)
9516 effective_action[i] = stored_player[i].effective_action;
9518 GameActions_EM(effective_action, warp_mode);
9522 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
9525 void GameActions_RND()
9527 int magic_wall_x = 0, magic_wall_y = 0;
9528 int i, x, y, element, graphic;
9530 InitPlayfieldScanModeVars();
9532 #if USE_ONE_MORE_CHANGE_PER_FRAME
9533 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
9535 SCAN_PLAYFIELD(x, y)
9537 ChangeCount[x][y] = 0;
9538 ChangeEvent[x][y] = -1;
9544 if (game.set_centered_player)
9546 boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
9548 /* switching to "all players" only possible if all players fit to screen */
9549 if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
9551 game.centered_player_nr_next = game.centered_player_nr;
9552 game.set_centered_player = FALSE;
9555 /* do not switch focus to non-existing (or non-active) player */
9556 if (game.centered_player_nr_next >= 0 &&
9557 !stored_player[game.centered_player_nr_next].active)
9559 game.centered_player_nr_next = game.centered_player_nr;
9560 game.set_centered_player = FALSE;
9564 if (game.set_centered_player &&
9565 ScreenMovPos == 0) /* screen currently aligned at tile position */
9569 if (game.centered_player_nr_next == -1)
9571 setScreenCenteredToAllPlayers(&sx, &sy);
9575 sx = stored_player[game.centered_player_nr_next].jx;
9576 sy = stored_player[game.centered_player_nr_next].jy;
9579 game.centered_player_nr = game.centered_player_nr_next;
9580 game.set_centered_player = FALSE;
9582 DrawRelocateScreen(sx, sy, MV_NONE, TRUE, setup.quick_switch);
9583 DrawGameDoorValues();
9587 for (i = 0; i < MAX_PLAYERS; i++)
9589 int actual_player_action = stored_player[i].effective_action;
9592 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
9593 - rnd_equinox_tetrachloride 048
9594 - rnd_equinox_tetrachloride_ii 096
9595 - rnd_emanuel_schmieg 002
9596 - doctor_sloan_ww 001, 020
9598 if (stored_player[i].MovPos == 0)
9599 CheckGravityMovement(&stored_player[i]);
9602 /* overwrite programmed action with tape action */
9603 if (stored_player[i].programmed_action)
9604 actual_player_action = stored_player[i].programmed_action;
9607 PlayerActions(&stored_player[i], actual_player_action);
9609 tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
9611 if (tape.recording && tape_action[i] && !tape.player_participates[i])
9612 tape.player_participates[i] = TRUE; /* player just appeared from CE */
9615 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
9619 network_player_action_received = FALSE;
9622 ScrollScreen(NULL, SCROLL_GO_ON);
9624 /* for backwards compatibility, the following code emulates a fixed bug that
9625 occured when pushing elements (causing elements that just made their last
9626 pushing step to already (if possible) make their first falling step in the
9627 same game frame, which is bad); this code is also needed to use the famous
9628 "spring push bug" which is used in older levels and might be wanted to be
9629 used also in newer levels, but in this case the buggy pushing code is only
9630 affecting the "spring" element and no other elements */
9632 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
9634 for (i = 0; i < MAX_PLAYERS; i++)
9636 struct PlayerInfo *player = &stored_player[i];
9640 if (player->active && player->is_pushing && player->is_moving &&
9642 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
9643 Feld[x][y] == EL_SPRING))
9645 ContinueMoving(x, y);
9647 /* continue moving after pushing (this is actually a bug) */
9648 if (!IS_MOVING(x, y))
9657 SCAN_PLAYFIELD(x, y)
9659 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9662 ChangeCount[x][y] = 0;
9663 ChangeEvent[x][y] = -1;
9665 /* this must be handled before main playfield loop */
9666 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
9669 if (MovDelay[x][y] <= 0)
9673 #if USE_NEW_SNAP_DELAY
9674 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
9677 if (MovDelay[x][y] <= 0)
9680 DrawLevelField(x, y);
9682 TestIfElementTouchesCustomElement(x, y); /* for empty space */
9688 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
9690 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
9691 printf("GameActions(): This should never happen!\n");
9693 ChangePage[x][y] = -1;
9698 if (WasJustMoving[x][y] > 0)
9699 WasJustMoving[x][y]--;
9700 if (WasJustFalling[x][y] > 0)
9701 WasJustFalling[x][y]--;
9702 if (CheckCollision[x][y] > 0)
9703 CheckCollision[x][y]--;
9707 /* reset finished pushing action (not done in ContinueMoving() to allow
9708 continuous pushing animation for elements with zero push delay) */
9709 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
9711 ResetGfxAnimation(x, y);
9712 DrawLevelField(x, y);
9716 if (IS_BLOCKED(x, y))
9720 Blocked2Moving(x, y, &oldx, &oldy);
9721 if (!IS_MOVING(oldx, oldy))
9723 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
9724 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
9725 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
9726 printf("GameActions(): This should never happen!\n");
9733 SCAN_PLAYFIELD(x, y)
9735 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9738 element = Feld[x][y];
9739 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9742 printf("::: %d,%d\n", x, y);
9744 if (element == EL_ROCK)
9745 printf("::: Yo man! Rocks can fall!\n");
9749 ResetGfxFrame(x, y, TRUE);
9751 if (graphic_info[graphic].anim_global_sync)
9752 GfxFrame[x][y] = FrameCounter;
9753 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
9755 int old_gfx_frame = GfxFrame[x][y];
9757 GfxFrame[x][y] = CustomValue[x][y];
9760 if (GfxFrame[x][y] != old_gfx_frame)
9762 DrawLevelGraphicAnimation(x, y, graphic);
9764 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
9766 int old_gfx_frame = GfxFrame[x][y];
9768 GfxFrame[x][y] = element_info[element].collect_score;
9771 if (GfxFrame[x][y] != old_gfx_frame)
9773 DrawLevelGraphicAnimation(x, y, graphic);
9777 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
9778 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
9779 ResetRandomAnimationValue(x, y);
9781 SetRandomAnimationValue(x, y);
9783 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
9785 if (IS_INACTIVE(element))
9787 if (IS_ANIMATED(graphic))
9788 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9793 /* this may take place after moving, so 'element' may have changed */
9794 if (IS_CHANGING(x, y) &&
9795 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
9797 int page = element_info[element].event_page_nr[CE_DELAY];
9799 HandleElementChange(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] : page);
9803 printf("::: ChangeDelay == %d\n", ChangeDelay[x][y]);
9807 if (element == EL_CUSTOM_255)
9808 printf("::: ChangeDelay == %d\n", ChangeDelay[x][y]);
9812 HandleElementChange(x, y, page);
9814 if (CAN_CHANGE(element))
9815 HandleElementChange(x, y, page);
9817 if (HAS_ACTION(element))
9818 ExecuteCustomElementAction(x, y, element, page);
9823 element = Feld[x][y];
9824 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9827 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
9831 element = Feld[x][y];
9832 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9834 if (IS_ANIMATED(graphic) &&
9837 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9839 if (IS_GEM(element) || element == EL_SP_INFOTRON)
9840 EdelsteinFunkeln(x, y);
9842 else if ((element == EL_ACID ||
9843 element == EL_EXIT_OPEN ||
9844 element == EL_SP_EXIT_OPEN ||
9845 element == EL_SP_TERMINAL ||
9846 element == EL_SP_TERMINAL_ACTIVE ||
9847 element == EL_EXTRA_TIME ||
9848 element == EL_SHIELD_NORMAL ||
9849 element == EL_SHIELD_DEADLY) &&
9850 IS_ANIMATED(graphic))
9851 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9852 else if (IS_MOVING(x, y))
9853 ContinueMoving(x, y);
9854 else if (IS_ACTIVE_BOMB(element))
9855 CheckDynamite(x, y);
9856 else if (element == EL_AMOEBA_GROWING)
9857 AmoebeWaechst(x, y);
9858 else if (element == EL_AMOEBA_SHRINKING)
9859 AmoebaDisappearing(x, y);
9861 #if !USE_NEW_AMOEBA_CODE
9862 else if (IS_AMOEBALIVE(element))
9863 AmoebeAbleger(x, y);
9866 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
9868 else if (element == EL_EXIT_CLOSED)
9870 else if (element == EL_SP_EXIT_CLOSED)
9872 else if (element == EL_EXPANDABLE_WALL_GROWING)
9874 else if (element == EL_EXPANDABLE_WALL ||
9875 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9876 element == EL_EXPANDABLE_WALL_VERTICAL ||
9877 element == EL_EXPANDABLE_WALL_ANY)
9879 else if (element == EL_FLAMES)
9880 CheckForDragon(x, y);
9881 else if (element == EL_EXPLOSION)
9882 ; /* drawing of correct explosion animation is handled separately */
9883 else if (element == EL_ELEMENT_SNAPPING ||
9884 element == EL_DIAGONAL_SHRINKING ||
9885 element == EL_DIAGONAL_GROWING)
9888 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
9890 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9893 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
9894 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9897 if (element == EL_CUSTOM_255 ||
9898 element == EL_CUSTOM_256)
9899 DrawLevelGraphicAnimation(x, y, graphic);
9902 if (IS_BELT_ACTIVE(element))
9903 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
9905 if (game.magic_wall_active)
9907 int jx = local_player->jx, jy = local_player->jy;
9909 /* play the element sound at the position nearest to the player */
9910 if ((element == EL_MAGIC_WALL_FULL ||
9911 element == EL_MAGIC_WALL_ACTIVE ||
9912 element == EL_MAGIC_WALL_EMPTYING ||
9913 element == EL_BD_MAGIC_WALL_FULL ||
9914 element == EL_BD_MAGIC_WALL_ACTIVE ||
9915 element == EL_BD_MAGIC_WALL_EMPTYING) &&
9916 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
9924 #if USE_NEW_AMOEBA_CODE
9925 /* new experimental amoeba growth stuff */
9926 if (!(FrameCounter % 8))
9928 static unsigned long random = 1684108901;
9930 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
9932 x = RND(lev_fieldx);
9933 y = RND(lev_fieldy);
9934 element = Feld[x][y];
9936 if (!IS_PLAYER(x,y) &&
9937 (element == EL_EMPTY ||
9938 CAN_GROW_INTO(element) ||
9939 element == EL_QUICKSAND_EMPTY ||
9940 element == EL_ACID_SPLASH_LEFT ||
9941 element == EL_ACID_SPLASH_RIGHT))
9943 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
9944 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
9945 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
9946 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
9947 Feld[x][y] = EL_AMOEBA_DROP;
9950 random = random * 129 + 1;
9956 if (game.explosions_delayed)
9959 game.explosions_delayed = FALSE;
9962 SCAN_PLAYFIELD(x, y)
9964 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9967 element = Feld[x][y];
9969 if (ExplodeField[x][y])
9970 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
9971 else if (element == EL_EXPLOSION)
9972 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
9974 ExplodeField[x][y] = EX_TYPE_NONE;
9977 game.explosions_delayed = TRUE;
9980 if (game.magic_wall_active)
9982 if (!(game.magic_wall_time_left % 4))
9984 int element = Feld[magic_wall_x][magic_wall_y];
9986 if (element == EL_BD_MAGIC_WALL_FULL ||
9987 element == EL_BD_MAGIC_WALL_ACTIVE ||
9988 element == EL_BD_MAGIC_WALL_EMPTYING)
9989 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
9991 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
9994 if (game.magic_wall_time_left > 0)
9996 game.magic_wall_time_left--;
9997 if (!game.magic_wall_time_left)
10000 SCAN_PLAYFIELD(x, y)
10002 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
10005 element = Feld[x][y];
10007 if (element == EL_MAGIC_WALL_ACTIVE ||
10008 element == EL_MAGIC_WALL_FULL)
10010 Feld[x][y] = EL_MAGIC_WALL_DEAD;
10011 DrawLevelField(x, y);
10013 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
10014 element == EL_BD_MAGIC_WALL_FULL)
10016 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
10017 DrawLevelField(x, y);
10021 game.magic_wall_active = FALSE;
10026 if (game.light_time_left > 0)
10028 game.light_time_left--;
10030 if (game.light_time_left == 0)
10031 RedrawAllLightSwitchesAndInvisibleElements();
10034 if (game.timegate_time_left > 0)
10036 game.timegate_time_left--;
10038 if (game.timegate_time_left == 0)
10039 CloseAllOpenTimegates();
10042 if (game.lenses_time_left > 0)
10044 game.lenses_time_left--;
10046 if (game.lenses_time_left == 0)
10047 RedrawAllInvisibleElementsForLenses();
10050 if (game.magnify_time_left > 0)
10052 game.magnify_time_left--;
10054 if (game.magnify_time_left == 0)
10055 RedrawAllInvisibleElementsForMagnifier();
10058 for (i = 0; i < MAX_PLAYERS; i++)
10060 struct PlayerInfo *player = &stored_player[i];
10062 if (SHIELD_ON(player))
10064 if (player->shield_deadly_time_left)
10065 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
10066 else if (player->shield_normal_time_left)
10067 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
10074 PlayAllPlayersSound();
10076 if (options.debug) /* calculate frames per second */
10078 static unsigned long fps_counter = 0;
10079 static int fps_frames = 0;
10080 unsigned long fps_delay_ms = Counter() - fps_counter;
10084 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
10086 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
10089 fps_counter = Counter();
10092 redraw_mask |= REDRAW_FPS;
10095 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
10097 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
10099 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
10101 local_player->show_envelope = 0;
10104 /* use random number generator in every frame to make it less predictable */
10105 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
10109 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
10111 int min_x = x, min_y = y, max_x = x, max_y = y;
10114 for (i = 0; i < MAX_PLAYERS; i++)
10116 int jx = stored_player[i].jx, jy = stored_player[i].jy;
10118 if (!stored_player[i].active || &stored_player[i] == player)
10121 min_x = MIN(min_x, jx);
10122 min_y = MIN(min_y, jy);
10123 max_x = MAX(max_x, jx);
10124 max_y = MAX(max_y, jy);
10127 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
10130 static boolean AllPlayersInVisibleScreen()
10134 for (i = 0; i < MAX_PLAYERS; i++)
10136 int jx = stored_player[i].jx, jy = stored_player[i].jy;
10138 if (!stored_player[i].active)
10141 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
10148 void ScrollLevel(int dx, int dy)
10150 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
10153 BlitBitmap(drawto_field, drawto_field,
10154 FX + TILEX * (dx == -1) - softscroll_offset,
10155 FY + TILEY * (dy == -1) - softscroll_offset,
10156 SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
10157 SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
10158 FX + TILEX * (dx == 1) - softscroll_offset,
10159 FY + TILEY * (dy == 1) - softscroll_offset);
10163 x = (dx == 1 ? BX1 : BX2);
10164 for (y = BY1; y <= BY2; y++)
10165 DrawScreenField(x, y);
10170 y = (dy == 1 ? BY1 : BY2);
10171 for (x = BX1; x <= BX2; x++)
10172 DrawScreenField(x, y);
10175 redraw_mask |= REDRAW_FIELD;
10178 static boolean canFallDown(struct PlayerInfo *player)
10180 int jx = player->jx, jy = player->jy;
10182 return (IN_LEV_FIELD(jx, jy + 1) &&
10183 (IS_FREE(jx, jy + 1) ||
10184 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
10185 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
10186 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
10189 static boolean canPassField(int x, int y, int move_dir)
10191 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
10192 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
10193 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
10194 int nextx = x + dx;
10195 int nexty = y + dy;
10196 int element = Feld[x][y];
10198 return (IS_PASSABLE_FROM(element, opposite_dir) &&
10199 !CAN_MOVE(element) &&
10200 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
10201 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
10202 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
10205 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
10207 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
10208 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
10209 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
10213 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
10214 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
10215 (IS_DIGGABLE(Feld[newx][newy]) ||
10216 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
10217 canPassField(newx, newy, move_dir)));
10220 static void CheckGravityMovement(struct PlayerInfo *player)
10222 if (game.gravity && !player->programmed_action)
10224 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
10225 int move_dir_vertical = player->effective_action & MV_VERTICAL;
10226 boolean player_is_snapping = player->effective_action & JOY_BUTTON_1;
10227 int jx = player->jx, jy = player->jy;
10228 boolean player_is_moving_to_valid_field =
10229 (!player_is_snapping &&
10230 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
10231 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
10232 boolean player_can_fall_down = canFallDown(player);
10234 if (player_can_fall_down &&
10235 !player_is_moving_to_valid_field)
10236 player->programmed_action = MV_DOWN;
10240 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
10242 return CheckGravityMovement(player);
10244 if (game.gravity && !player->programmed_action)
10246 int jx = player->jx, jy = player->jy;
10247 boolean field_under_player_is_free =
10248 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
10249 boolean player_is_standing_on_valid_field =
10250 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
10251 (IS_WALKABLE(Feld[jx][jy]) &&
10252 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
10254 if (field_under_player_is_free && !player_is_standing_on_valid_field)
10255 player->programmed_action = MV_DOWN;
10260 MovePlayerOneStep()
10261 -----------------------------------------------------------------------------
10262 dx, dy: direction (non-diagonal) to try to move the player to
10263 real_dx, real_dy: direction as read from input device (can be diagonal)
10266 boolean MovePlayerOneStep(struct PlayerInfo *player,
10267 int dx, int dy, int real_dx, int real_dy)
10269 int jx = player->jx, jy = player->jy;
10270 int new_jx = jx + dx, new_jy = jy + dy;
10271 #if !USE_FIXED_DONT_RUN_INTO
10275 boolean player_can_move = !player->cannot_move;
10277 if (!player->active || (!dx && !dy))
10278 return MP_NO_ACTION;
10280 player->MovDir = (dx < 0 ? MV_LEFT :
10281 dx > 0 ? MV_RIGHT :
10283 dy > 0 ? MV_DOWN : MV_NONE);
10285 if (!IN_LEV_FIELD(new_jx, new_jy))
10286 return MP_NO_ACTION;
10288 if (!player_can_move)
10291 if (player->MovPos == 0)
10293 player->is_moving = FALSE;
10294 player->is_digging = FALSE;
10295 player->is_collecting = FALSE;
10296 player->is_snapping = FALSE;
10297 player->is_pushing = FALSE;
10300 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
10301 SnapField(player, 0, 0);
10305 return MP_NO_ACTION;
10310 if (!options.network && game.centered_player_nr == -1 &&
10311 !AllPlayersInSight(player, new_jx, new_jy))
10312 return MP_NO_ACTION;
10314 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
10315 return MP_NO_ACTION;
10318 #if !USE_FIXED_DONT_RUN_INTO
10319 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
10321 /* (moved to DigField()) */
10322 if (player_can_move && DONT_RUN_INTO(element))
10324 if (element == EL_ACID && dx == 0 && dy == 1)
10326 SplashAcid(new_jx, new_jy);
10327 Feld[jx][jy] = EL_PLAYER_1;
10328 InitMovingField(jx, jy, MV_DOWN);
10329 Store[jx][jy] = EL_ACID;
10330 ContinueMoving(jx, jy);
10331 BuryPlayer(player);
10334 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
10340 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
10342 #if USE_FIXED_DONT_RUN_INTO
10343 if (can_move == MP_DONT_RUN_INTO)
10347 if (can_move != MP_MOVING)
10350 #if USE_FIXED_DONT_RUN_INTO
10353 /* check if DigField() has caused relocation of the player */
10354 if (player->jx != jx || player->jy != jy)
10355 return MP_NO_ACTION; /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
10357 StorePlayer[jx][jy] = 0;
10358 player->last_jx = jx;
10359 player->last_jy = jy;
10360 player->jx = new_jx;
10361 player->jy = new_jy;
10362 StorePlayer[new_jx][new_jy] = player->element_nr;
10364 if (player->move_delay_value_next != -1)
10366 player->move_delay_value = player->move_delay_value_next;
10367 player->move_delay_value_next = -1;
10371 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
10373 player->step_counter++;
10375 PlayerVisit[jx][jy] = FrameCounter;
10377 ScrollPlayer(player, SCROLL_INIT);
10382 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
10384 int jx = player->jx, jy = player->jy;
10385 int old_jx = jx, old_jy = jy;
10386 int moved = MP_NO_ACTION;
10388 if (!player->active)
10393 if (player->MovPos == 0)
10395 player->is_moving = FALSE;
10396 player->is_digging = FALSE;
10397 player->is_collecting = FALSE;
10398 player->is_snapping = FALSE;
10399 player->is_pushing = FALSE;
10405 if (player->move_delay > 0)
10408 player->move_delay = -1; /* set to "uninitialized" value */
10410 /* store if player is automatically moved to next field */
10411 player->is_auto_moving = (player->programmed_action != MV_NONE);
10413 /* remove the last programmed player action */
10414 player->programmed_action = 0;
10416 if (player->MovPos)
10418 /* should only happen if pre-1.2 tape recordings are played */
10419 /* this is only for backward compatibility */
10421 int original_move_delay_value = player->move_delay_value;
10424 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
10428 /* scroll remaining steps with finest movement resolution */
10429 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
10431 while (player->MovPos)
10433 ScrollPlayer(player, SCROLL_GO_ON);
10434 ScrollScreen(NULL, SCROLL_GO_ON);
10436 AdvanceFrameAndPlayerCounters(player->index_nr);
10442 player->move_delay_value = original_move_delay_value;
10445 if (player->last_move_dir & MV_HORIZONTAL)
10447 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
10448 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
10452 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
10453 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
10460 if (moved & MP_MOVING && !ScreenMovPos &&
10461 (player->index_nr == game.centered_player_nr ||
10462 game.centered_player_nr == -1))
10464 if (moved & MP_MOVING && !ScreenMovPos &&
10465 (player == local_player || !options.network))
10468 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
10469 int offset = (setup.scroll_delay ? 3 : 0);
10471 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
10473 /* actual player has left the screen -- scroll in that direction */
10474 if (jx != old_jx) /* player has moved horizontally */
10475 scroll_x += (jx - old_jx);
10476 else /* player has moved vertically */
10477 scroll_y += (jy - old_jy);
10481 if (jx != old_jx) /* player has moved horizontally */
10483 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
10484 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
10485 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
10487 /* don't scroll over playfield boundaries */
10488 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
10489 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
10491 /* don't scroll more than one field at a time */
10492 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
10494 /* don't scroll against the player's moving direction */
10495 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
10496 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
10497 scroll_x = old_scroll_x;
10499 else /* player has moved vertically */
10501 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
10502 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
10503 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
10505 /* don't scroll over playfield boundaries */
10506 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
10507 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
10509 /* don't scroll more than one field at a time */
10510 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
10512 /* don't scroll against the player's moving direction */
10513 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
10514 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
10515 scroll_y = old_scroll_y;
10519 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
10522 if (!options.network && game.centered_player_nr == -1 &&
10523 !AllPlayersInVisibleScreen())
10525 scroll_x = old_scroll_x;
10526 scroll_y = old_scroll_y;
10530 if (!options.network && !AllPlayersInVisibleScreen())
10532 scroll_x = old_scroll_x;
10533 scroll_y = old_scroll_y;
10538 ScrollScreen(player, SCROLL_INIT);
10539 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
10544 player->StepFrame = 0;
10546 if (moved & MP_MOVING)
10548 if (old_jx != jx && old_jy == jy)
10549 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
10550 else if (old_jx == jx && old_jy != jy)
10551 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
10553 DrawLevelField(jx, jy); /* for "crumbled sand" */
10555 player->last_move_dir = player->MovDir;
10556 player->is_moving = TRUE;
10557 player->is_snapping = FALSE;
10558 player->is_switching = FALSE;
10559 player->is_dropping = FALSE;
10560 player->is_dropping_pressed = FALSE;
10561 player->drop_pressed_delay = 0;
10565 CheckGravityMovementWhenNotMoving(player);
10567 player->is_moving = FALSE;
10569 /* at this point, the player is allowed to move, but cannot move right now
10570 (e.g. because of something blocking the way) -- ensure that the player
10571 is also allowed to move in the next frame (in old versions before 3.1.1,
10572 the player was forced to wait again for eight frames before next try) */
10574 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
10575 player->move_delay = 0; /* allow direct movement in the next frame */
10578 if (player->move_delay == -1) /* not yet initialized by DigField() */
10579 player->move_delay = player->move_delay_value;
10581 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10583 TestIfPlayerTouchesBadThing(jx, jy);
10584 TestIfPlayerTouchesCustomElement(jx, jy);
10587 if (!player->active)
10588 RemovePlayer(player);
10593 void ScrollPlayer(struct PlayerInfo *player, int mode)
10595 int jx = player->jx, jy = player->jy;
10596 int last_jx = player->last_jx, last_jy = player->last_jy;
10597 int move_stepsize = TILEX / player->move_delay_value;
10599 #if USE_NEW_PLAYER_SPEED
10600 if (!player->active)
10603 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
10606 if (!player->active || player->MovPos == 0)
10610 if (mode == SCROLL_INIT)
10612 player->actual_frame_counter = FrameCounter;
10613 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10615 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
10616 Feld[last_jx][last_jy] == EL_EMPTY)
10618 int last_field_block_delay = 0; /* start with no blocking at all */
10619 int block_delay_adjustment = player->block_delay_adjustment;
10621 /* if player blocks last field, add delay for exactly one move */
10622 if (player->block_last_field)
10624 last_field_block_delay += player->move_delay_value;
10626 /* when blocking enabled, prevent moving up despite gravity */
10627 if (game.gravity && player->MovDir == MV_UP)
10628 block_delay_adjustment = -1;
10631 /* add block delay adjustment (also possible when not blocking) */
10632 last_field_block_delay += block_delay_adjustment;
10634 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
10635 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
10638 #if USE_NEW_PLAYER_SPEED
10639 if (player->MovPos != 0) /* player has not yet reached destination */
10645 else if (!FrameReached(&player->actual_frame_counter, 1))
10649 printf("::: player->MovPos: %d -> %d\n",
10651 player->MovPos + (player->MovPos > 0 ? -1 : 1) * move_stepsize);
10654 #if USE_NEW_PLAYER_SPEED
10655 if (player->MovPos != 0)
10657 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
10658 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10660 /* before DrawPlayer() to draw correct player graphic for this case */
10661 if (player->MovPos == 0)
10662 CheckGravityMovement(player);
10665 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
10666 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10668 /* before DrawPlayer() to draw correct player graphic for this case */
10669 if (player->MovPos == 0)
10670 CheckGravityMovement(player);
10673 if (player->MovPos == 0) /* player reached destination field */
10676 printf("::: player reached destination field\n");
10679 if (player->move_delay_reset_counter > 0)
10681 player->move_delay_reset_counter--;
10683 if (player->move_delay_reset_counter == 0)
10685 /* continue with normal speed after quickly moving through gate */
10686 HALVE_PLAYER_SPEED(player);
10688 /* be able to make the next move without delay */
10689 player->move_delay = 0;
10693 player->last_jx = jx;
10694 player->last_jy = jy;
10696 if (Feld[jx][jy] == EL_EXIT_OPEN ||
10697 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
10698 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
10700 DrawPlayer(player); /* needed here only to cleanup last field */
10701 RemovePlayer(player);
10703 if (local_player->friends_still_needed == 0 ||
10704 IS_SP_ELEMENT(Feld[jx][jy]))
10705 player->LevelSolved = player->GameOver = TRUE;
10708 /* this breaks one level: "machine", level 000 */
10710 int move_direction = player->MovDir;
10711 int enter_side = MV_DIR_OPPOSITE(move_direction);
10712 int leave_side = move_direction;
10713 int old_jx = last_jx;
10714 int old_jy = last_jy;
10715 int old_element = Feld[old_jx][old_jy];
10716 int new_element = Feld[jx][jy];
10718 if (IS_CUSTOM_ELEMENT(old_element))
10719 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
10721 player->index_bit, leave_side);
10723 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
10724 CE_PLAYER_LEAVES_X,
10725 player->index_bit, leave_side);
10727 if (IS_CUSTOM_ELEMENT(new_element))
10728 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
10729 player->index_bit, enter_side);
10731 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
10732 CE_PLAYER_ENTERS_X,
10733 player->index_bit, enter_side);
10735 CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
10736 CE_MOVE_OF_X, move_direction);
10739 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10741 TestIfPlayerTouchesBadThing(jx, jy);
10742 TestIfPlayerTouchesCustomElement(jx, jy);
10744 /* needed because pushed element has not yet reached its destination,
10745 so it would trigger a change event at its previous field location */
10746 if (!player->is_pushing)
10747 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
10749 if (!player->active)
10750 RemovePlayer(player);
10753 if (level.use_step_counter)
10763 if (TimeLeft <= 10 && setup.time_limit)
10764 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
10766 DrawGameValue_Time(TimeLeft);
10768 if (!TimeLeft && setup.time_limit)
10769 for (i = 0; i < MAX_PLAYERS; i++)
10770 KillPlayer(&stored_player[i]);
10772 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
10773 DrawGameValue_Time(TimePlayed);
10776 if (tape.single_step && tape.recording && !tape.pausing &&
10777 !player->programmed_action)
10778 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10782 void ScrollScreen(struct PlayerInfo *player, int mode)
10784 static unsigned long screen_frame_counter = 0;
10786 if (mode == SCROLL_INIT)
10788 /* set scrolling step size according to actual player's moving speed */
10789 ScrollStepSize = TILEX / player->move_delay_value;
10791 screen_frame_counter = FrameCounter;
10792 ScreenMovDir = player->MovDir;
10793 ScreenMovPos = player->MovPos;
10794 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
10797 else if (!FrameReached(&screen_frame_counter, 1))
10802 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
10803 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
10804 redraw_mask |= REDRAW_FIELD;
10807 ScreenMovDir = MV_NONE;
10810 void TestIfPlayerTouchesCustomElement(int x, int y)
10812 static int xy[4][2] =
10819 static int trigger_sides[4][2] =
10821 /* center side border side */
10822 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
10823 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
10824 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
10825 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
10827 static int touch_dir[4] =
10829 MV_LEFT | MV_RIGHT,
10834 int center_element = Feld[x][y]; /* should always be non-moving! */
10837 for (i = 0; i < NUM_DIRECTIONS; i++)
10839 int xx = x + xy[i][0];
10840 int yy = y + xy[i][1];
10841 int center_side = trigger_sides[i][0];
10842 int border_side = trigger_sides[i][1];
10843 int border_element;
10845 if (!IN_LEV_FIELD(xx, yy))
10848 if (IS_PLAYER(x, y))
10850 struct PlayerInfo *player = PLAYERINFO(x, y);
10852 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10853 border_element = Feld[xx][yy]; /* may be moving! */
10854 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
10855 border_element = Feld[xx][yy];
10856 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
10857 border_element = MovingOrBlocked2Element(xx, yy);
10859 continue; /* center and border element do not touch */
10861 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
10862 player->index_bit, border_side);
10863 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
10864 CE_PLAYER_TOUCHES_X,
10865 player->index_bit, border_side);
10867 else if (IS_PLAYER(xx, yy))
10869 struct PlayerInfo *player = PLAYERINFO(xx, yy);
10871 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10873 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
10874 continue; /* center and border element do not touch */
10877 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
10878 player->index_bit, center_side);
10879 CheckTriggeredElementChangeByPlayer(x, y, center_element,
10880 CE_PLAYER_TOUCHES_X,
10881 player->index_bit, center_side);
10887 #if USE_ELEMENT_TOUCHING_BUGFIX
10889 void TestIfElementTouchesCustomElement(int x, int y)
10891 static int xy[4][2] =
10898 static int trigger_sides[4][2] =
10900 /* center side border side */
10901 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
10902 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
10903 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
10904 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
10906 static int touch_dir[4] =
10908 MV_LEFT | MV_RIGHT,
10913 boolean change_center_element = FALSE;
10914 int center_element = Feld[x][y]; /* should always be non-moving! */
10915 int border_element_old[NUM_DIRECTIONS];
10918 for (i = 0; i < NUM_DIRECTIONS; i++)
10920 int xx = x + xy[i][0];
10921 int yy = y + xy[i][1];
10922 int border_element;
10924 border_element_old[i] = -1;
10926 if (!IN_LEV_FIELD(xx, yy))
10929 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10930 border_element = Feld[xx][yy]; /* may be moving! */
10931 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
10932 border_element = Feld[xx][yy];
10933 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
10934 border_element = MovingOrBlocked2Element(xx, yy);
10936 continue; /* center and border element do not touch */
10938 border_element_old[i] = border_element;
10941 for (i = 0; i < NUM_DIRECTIONS; i++)
10943 int xx = x + xy[i][0];
10944 int yy = y + xy[i][1];
10945 int center_side = trigger_sides[i][0];
10946 int border_element = border_element_old[i];
10948 if (border_element == -1)
10951 /* check for change of border element */
10952 CheckElementChangeBySide(xx, yy, border_element, center_element,
10953 CE_TOUCHING_X, center_side);
10956 for (i = 0; i < NUM_DIRECTIONS; i++)
10958 int border_side = trigger_sides[i][1];
10959 int border_element = border_element_old[i];
10961 if (border_element == -1)
10964 /* check for change of center element (but change it only once) */
10965 if (!change_center_element)
10966 change_center_element =
10967 CheckElementChangeBySide(x, y, center_element, border_element,
10968 CE_TOUCHING_X, border_side);
10974 void TestIfElementTouchesCustomElement_OLD(int x, int y)
10976 static int xy[4][2] =
10983 static int trigger_sides[4][2] =
10985 /* center side border side */
10986 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
10987 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
10988 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
10989 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
10991 static int touch_dir[4] =
10993 MV_LEFT | MV_RIGHT,
10998 boolean change_center_element = FALSE;
10999 int center_element = Feld[x][y]; /* should always be non-moving! */
11002 for (i = 0; i < NUM_DIRECTIONS; i++)
11004 int xx = x + xy[i][0];
11005 int yy = y + xy[i][1];
11006 int center_side = trigger_sides[i][0];
11007 int border_side = trigger_sides[i][1];
11008 int border_element;
11010 if (!IN_LEV_FIELD(xx, yy))
11013 if (game.engine_version < VERSION_IDENT(3,0,7,0))
11014 border_element = Feld[xx][yy]; /* may be moving! */
11015 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
11016 border_element = Feld[xx][yy];
11017 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
11018 border_element = MovingOrBlocked2Element(xx, yy);
11020 continue; /* center and border element do not touch */
11022 /* check for change of center element (but change it only once) */
11023 if (!change_center_element)
11024 change_center_element =
11025 CheckElementChangeBySide(x, y, center_element, border_element,
11026 CE_TOUCHING_X, border_side);
11028 /* check for change of border element */
11029 CheckElementChangeBySide(xx, yy, border_element, center_element,
11030 CE_TOUCHING_X, center_side);
11036 void TestIfElementHitsCustomElement(int x, int y, int direction)
11038 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
11039 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
11040 int hitx = x + dx, hity = y + dy;
11041 int hitting_element = Feld[x][y];
11042 int touched_element;
11044 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
11047 touched_element = (IN_LEV_FIELD(hitx, hity) ?
11048 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
11050 if (IN_LEV_FIELD(hitx, hity))
11052 int opposite_direction = MV_DIR_OPPOSITE(direction);
11053 int hitting_side = direction;
11054 int touched_side = opposite_direction;
11055 boolean object_hit = (!IS_MOVING(hitx, hity) ||
11056 MovDir[hitx][hity] != direction ||
11057 ABS(MovPos[hitx][hity]) <= TILEY / 2);
11063 CheckElementChangeBySide(x, y, hitting_element, touched_element,
11064 CE_HITTING_X, touched_side);
11066 CheckElementChangeBySide(hitx, hity, touched_element,
11067 hitting_element, CE_HIT_BY_X, hitting_side);
11069 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
11070 CE_HIT_BY_SOMETHING, opposite_direction);
11074 /* "hitting something" is also true when hitting the playfield border */
11075 CheckElementChangeBySide(x, y, hitting_element, touched_element,
11076 CE_HITTING_SOMETHING, direction);
11080 void TestIfElementSmashesCustomElement(int x, int y, int direction)
11082 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
11083 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
11084 int hitx = x + dx, hity = y + dy;
11085 int hitting_element = Feld[x][y];
11086 int touched_element;
11088 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
11089 !IS_FREE(hitx, hity) &&
11090 (!IS_MOVING(hitx, hity) ||
11091 MovDir[hitx][hity] != direction ||
11092 ABS(MovPos[hitx][hity]) <= TILEY / 2));
11095 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
11099 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
11103 touched_element = (IN_LEV_FIELD(hitx, hity) ?
11104 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
11106 CheckElementChangeBySide(x, y, hitting_element, touched_element,
11107 EP_CAN_SMASH_EVERYTHING, direction);
11109 if (IN_LEV_FIELD(hitx, hity))
11111 int opposite_direction = MV_DIR_OPPOSITE(direction);
11112 int hitting_side = direction;
11113 int touched_side = opposite_direction;
11115 int touched_element = MovingOrBlocked2Element(hitx, hity);
11118 boolean object_hit = (!IS_MOVING(hitx, hity) ||
11119 MovDir[hitx][hity] != direction ||
11120 ABS(MovPos[hitx][hity]) <= TILEY / 2);
11129 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
11130 CE_SMASHED_BY_SOMETHING, opposite_direction);
11132 CheckElementChangeBySide(x, y, hitting_element, touched_element,
11133 CE_OTHER_IS_SMASHING, touched_side);
11135 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
11136 CE_OTHER_GETS_SMASHED, hitting_side);
11142 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
11144 int i, kill_x = -1, kill_y = -1;
11146 int bad_element = -1;
11147 static int test_xy[4][2] =
11154 static int test_dir[4] =
11162 for (i = 0; i < NUM_DIRECTIONS; i++)
11164 int test_x, test_y, test_move_dir, test_element;
11166 test_x = good_x + test_xy[i][0];
11167 test_y = good_y + test_xy[i][1];
11169 if (!IN_LEV_FIELD(test_x, test_y))
11173 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
11175 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
11177 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
11178 2nd case: DONT_TOUCH style bad thing does not move away from good thing
11180 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
11181 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
11185 bad_element = test_element;
11191 if (kill_x != -1 || kill_y != -1)
11193 if (IS_PLAYER(good_x, good_y))
11195 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
11197 if (player->shield_deadly_time_left > 0 &&
11198 !IS_INDESTRUCTIBLE(bad_element))
11199 Bang(kill_x, kill_y);
11200 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
11201 KillPlayer(player);
11204 Bang(good_x, good_y);
11208 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
11210 int i, kill_x = -1, kill_y = -1;
11211 int bad_element = Feld[bad_x][bad_y];
11212 static int test_xy[4][2] =
11219 static int touch_dir[4] =
11221 MV_LEFT | MV_RIGHT,
11226 static int test_dir[4] =
11234 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
11237 for (i = 0; i < NUM_DIRECTIONS; i++)
11239 int test_x, test_y, test_move_dir, test_element;
11241 test_x = bad_x + test_xy[i][0];
11242 test_y = bad_y + test_xy[i][1];
11243 if (!IN_LEV_FIELD(test_x, test_y))
11247 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
11249 test_element = Feld[test_x][test_y];
11251 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
11252 2nd case: DONT_TOUCH style bad thing does not move away from good thing
11254 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
11255 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
11257 /* good thing is player or penguin that does not move away */
11258 if (IS_PLAYER(test_x, test_y))
11260 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
11262 if (bad_element == EL_ROBOT && player->is_moving)
11263 continue; /* robot does not kill player if he is moving */
11265 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
11267 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
11268 continue; /* center and border element do not touch */
11275 else if (test_element == EL_PENGUIN)
11284 if (kill_x != -1 || kill_y != -1)
11286 if (IS_PLAYER(kill_x, kill_y))
11288 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
11290 if (player->shield_deadly_time_left > 0 &&
11291 !IS_INDESTRUCTIBLE(bad_element))
11292 Bang(bad_x, bad_y);
11293 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
11294 KillPlayer(player);
11297 Bang(kill_x, kill_y);
11301 void TestIfPlayerTouchesBadThing(int x, int y)
11303 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
11306 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
11308 TestIfGoodThingHitsBadThing(x, y, move_dir);
11311 void TestIfBadThingTouchesPlayer(int x, int y)
11313 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
11316 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
11318 TestIfBadThingHitsGoodThing(x, y, move_dir);
11321 void TestIfFriendTouchesBadThing(int x, int y)
11323 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
11326 void TestIfBadThingTouchesFriend(int x, int y)
11328 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
11331 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
11333 int i, kill_x = bad_x, kill_y = bad_y;
11334 static int xy[4][2] =
11342 for (i = 0; i < NUM_DIRECTIONS; i++)
11346 x = bad_x + xy[i][0];
11347 y = bad_y + xy[i][1];
11348 if (!IN_LEV_FIELD(x, y))
11351 element = Feld[x][y];
11352 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
11353 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
11361 if (kill_x != bad_x || kill_y != bad_y)
11362 Bang(bad_x, bad_y);
11365 void KillPlayer(struct PlayerInfo *player)
11367 int jx = player->jx, jy = player->jy;
11369 if (!player->active)
11372 /* remove accessible field at the player's position */
11373 Feld[jx][jy] = EL_EMPTY;
11375 /* deactivate shield (else Bang()/Explode() would not work right) */
11376 player->shield_normal_time_left = 0;
11377 player->shield_deadly_time_left = 0;
11380 BuryPlayer(player);
11383 static void KillPlayerUnlessEnemyProtected(int x, int y)
11385 if (!PLAYER_ENEMY_PROTECTED(x, y))
11386 KillPlayer(PLAYERINFO(x, y));
11389 static void KillPlayerUnlessExplosionProtected(int x, int y)
11391 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
11392 KillPlayer(PLAYERINFO(x, y));
11395 void BuryPlayer(struct PlayerInfo *player)
11397 int jx = player->jx, jy = player->jy;
11399 if (!player->active)
11402 PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
11403 PlayLevelSound(jx, jy, SND_GAME_LOSING);
11405 player->GameOver = TRUE;
11406 RemovePlayer(player);
11409 void RemovePlayer(struct PlayerInfo *player)
11411 int jx = player->jx, jy = player->jy;
11412 int i, found = FALSE;
11414 player->present = FALSE;
11415 player->active = FALSE;
11417 if (!ExplodeField[jx][jy])
11418 StorePlayer[jx][jy] = 0;
11420 if (player->is_moving)
11421 DrawLevelField(player->last_jx, player->last_jy);
11423 for (i = 0; i < MAX_PLAYERS; i++)
11424 if (stored_player[i].active)
11428 AllPlayersGone = TRUE;
11434 #if USE_NEW_SNAP_DELAY
11435 static void setFieldForSnapping(int x, int y, int element, int direction)
11437 struct ElementInfo *ei = &element_info[element];
11438 int direction_bit = MV_DIR_TO_BIT(direction);
11439 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
11440 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
11441 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
11443 Feld[x][y] = EL_ELEMENT_SNAPPING;
11444 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
11446 ResetGfxAnimation(x, y);
11448 GfxElement[x][y] = element;
11449 GfxAction[x][y] = action;
11450 GfxDir[x][y] = direction;
11451 GfxFrame[x][y] = -1;
11456 =============================================================================
11457 checkDiagonalPushing()
11458 -----------------------------------------------------------------------------
11459 check if diagonal input device direction results in pushing of object
11460 (by checking if the alternative direction is walkable, diggable, ...)
11461 =============================================================================
11464 static boolean checkDiagonalPushing(struct PlayerInfo *player,
11465 int x, int y, int real_dx, int real_dy)
11467 int jx, jy, dx, dy, xx, yy;
11469 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
11472 /* diagonal direction: check alternative direction */
11477 xx = jx + (dx == 0 ? real_dx : 0);
11478 yy = jy + (dy == 0 ? real_dy : 0);
11480 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
11484 =============================================================================
11486 -----------------------------------------------------------------------------
11487 x, y: field next to player (non-diagonal) to try to dig to
11488 real_dx, real_dy: direction as read from input device (can be diagonal)
11489 =============================================================================
11492 int DigField(struct PlayerInfo *player,
11493 int oldx, int oldy, int x, int y,
11494 int real_dx, int real_dy, int mode)
11496 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
11497 boolean player_was_pushing = player->is_pushing;
11498 boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
11499 boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
11500 int jx = oldx, jy = oldy;
11501 int dx = x - jx, dy = y - jy;
11502 int nextx = x + dx, nexty = y + dy;
11503 int move_direction = (dx == -1 ? MV_LEFT :
11504 dx == +1 ? MV_RIGHT :
11506 dy == +1 ? MV_DOWN : MV_NONE);
11507 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
11508 int dig_side = MV_DIR_OPPOSITE(move_direction);
11509 int old_element = Feld[jx][jy];
11510 #if USE_FIXED_DONT_RUN_INTO
11511 int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
11517 if (is_player) /* function can also be called by EL_PENGUIN */
11519 if (player->MovPos == 0)
11521 player->is_digging = FALSE;
11522 player->is_collecting = FALSE;
11525 if (player->MovPos == 0) /* last pushing move finished */
11526 player->is_pushing = FALSE;
11528 if (mode == DF_NO_PUSH) /* player just stopped pushing */
11530 player->is_switching = FALSE;
11531 player->push_delay = -1;
11533 return MP_NO_ACTION;
11537 #if !USE_FIXED_DONT_RUN_INTO
11538 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
11539 return MP_NO_ACTION;
11542 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
11543 old_element = Back[jx][jy];
11545 /* in case of element dropped at player position, check background */
11546 else if (Back[jx][jy] != EL_EMPTY &&
11547 game.engine_version >= VERSION_IDENT(2,2,0,0))
11548 old_element = Back[jx][jy];
11551 #if USE_FIXED_DONT_RUN_INTO
11552 if (player_can_move && DONT_RUN_INTO(element))
11554 if (element == EL_ACID && dx == 0 && dy == 1)
11557 Feld[jx][jy] = EL_PLAYER_1;
11558 InitMovingField(jx, jy, MV_DOWN);
11559 Store[jx][jy] = EL_ACID;
11560 ContinueMoving(jx, jy);
11561 BuryPlayer(player);
11564 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
11566 return MP_DONT_RUN_INTO;
11572 #if USE_FIXED_DONT_RUN_INTO
11573 if (player_can_move && DONT_RUN_INTO(element))
11575 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
11577 return MP_DONT_RUN_INTO;
11582 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
11583 return MP_NO_ACTION; /* field has no opening in this direction */
11585 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
11586 return MP_NO_ACTION; /* field has no opening in this direction */
11589 #if USE_FIXED_DONT_RUN_INTO
11590 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
11593 Feld[jx][jy] = EL_PLAYER_1;
11594 InitMovingField(jx, jy, MV_DOWN);
11595 Store[jx][jy] = EL_ACID;
11596 ContinueMoving(jx, jy);
11597 BuryPlayer(player);
11599 return MP_DONT_RUN_INTO;
11605 #if USE_FIXED_DONT_RUN_INTO
11606 if (player_can_move && DONT_RUN_INTO(element))
11608 if (element == EL_ACID && dx == 0 && dy == 1)
11611 Feld[jx][jy] = EL_PLAYER_1;
11612 InitMovingField(jx, jy, MV_DOWN);
11613 Store[jx][jy] = EL_ACID;
11614 ContinueMoving(jx, jy);
11615 BuryPlayer(player);
11618 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
11620 return MP_DONT_RUN_INTO;
11625 #if USE_FIXED_DONT_RUN_INTO
11626 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
11627 return MP_NO_ACTION;
11630 #if !USE_FIXED_DONT_RUN_INTO
11631 element = Feld[x][y];
11634 collect_count = element_info[element].collect_count_initial;
11636 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
11637 return MP_NO_ACTION;
11639 if (game.engine_version < VERSION_IDENT(2,2,0,0))
11640 player_can_move = player_can_move_or_snap;
11642 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
11643 game.engine_version >= VERSION_IDENT(2,2,0,0))
11645 CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
11646 player->index_bit, dig_side);
11647 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
11648 player->index_bit, dig_side);
11650 if (Feld[x][y] != element) /* field changed by snapping */
11653 return MP_NO_ACTION;
11656 if (game.gravity && is_player && !player->is_auto_moving &&
11657 canFallDown(player) && move_direction != MV_DOWN &&
11658 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
11659 return MP_NO_ACTION; /* player cannot walk here due to gravity */
11661 if (player_can_move &&
11662 IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
11664 int sound_element = SND_ELEMENT(element);
11665 int sound_action = ACTION_WALKING;
11667 if (IS_RND_GATE(element))
11669 if (!player->key[RND_GATE_NR(element)])
11670 return MP_NO_ACTION;
11672 else if (IS_RND_GATE_GRAY(element))
11674 if (!player->key[RND_GATE_GRAY_NR(element)])
11675 return MP_NO_ACTION;
11677 else if (IS_RND_GATE_GRAY_ACTIVE(element))
11679 if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
11680 return MP_NO_ACTION;
11682 else if (element == EL_EXIT_OPEN ||
11683 element == EL_SP_EXIT_OPEN ||
11684 element == EL_SP_EXIT_OPENING)
11686 sound_action = ACTION_PASSING; /* player is passing exit */
11688 else if (element == EL_EMPTY)
11690 sound_action = ACTION_MOVING; /* nothing to walk on */
11693 /* play sound from background or player, whatever is available */
11694 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
11695 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
11697 PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
11699 else if (player_can_move &&
11700 IS_PASSABLE(element) && canPassField(x, y, move_direction))
11702 if (!ACCESS_FROM(element, opposite_direction))
11703 return MP_NO_ACTION; /* field not accessible from this direction */
11705 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
11706 return MP_NO_ACTION;
11708 if (IS_EM_GATE(element))
11710 if (!player->key[EM_GATE_NR(element)])
11711 return MP_NO_ACTION;
11713 else if (IS_EM_GATE_GRAY(element))
11715 if (!player->key[EM_GATE_GRAY_NR(element)])
11716 return MP_NO_ACTION;
11718 else if (IS_EM_GATE_GRAY_ACTIVE(element))
11720 if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
11721 return MP_NO_ACTION;
11723 else if (IS_SP_PORT(element))
11725 if (element == EL_SP_GRAVITY_PORT_LEFT ||
11726 element == EL_SP_GRAVITY_PORT_RIGHT ||
11727 element == EL_SP_GRAVITY_PORT_UP ||
11728 element == EL_SP_GRAVITY_PORT_DOWN)
11729 game.gravity = !game.gravity;
11730 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
11731 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
11732 element == EL_SP_GRAVITY_ON_PORT_UP ||
11733 element == EL_SP_GRAVITY_ON_PORT_DOWN)
11734 game.gravity = TRUE;
11735 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
11736 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
11737 element == EL_SP_GRAVITY_OFF_PORT_UP ||
11738 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
11739 game.gravity = FALSE;
11742 /* automatically move to the next field with double speed */
11743 player->programmed_action = move_direction;
11745 if (player->move_delay_reset_counter == 0)
11747 player->move_delay_reset_counter = 2; /* two double speed steps */
11749 DOUBLE_PLAYER_SPEED(player);
11752 PlayLevelSoundAction(x, y, ACTION_PASSING);
11754 else if (player_can_move_or_snap && IS_DIGGABLE(element))
11758 if (mode != DF_SNAP)
11760 GfxElement[x][y] = GFX_ELEMENT(element);
11761 player->is_digging = TRUE;
11764 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
11766 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
11767 player->index_bit, dig_side);
11769 if (mode == DF_SNAP)
11771 #if USE_NEW_SNAP_DELAY
11772 if (level.block_snap_field)
11773 setFieldForSnapping(x, y, element, move_direction);
11775 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11777 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11780 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
11781 player->index_bit, dig_side);
11784 else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
11788 if (is_player && mode != DF_SNAP)
11790 GfxElement[x][y] = element;
11791 player->is_collecting = TRUE;
11794 if (element == EL_SPEED_PILL)
11796 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
11798 else if (element == EL_EXTRA_TIME && level.time > 0)
11800 TimeLeft += level.extra_time;
11801 DrawGameValue_Time(TimeLeft);
11803 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
11805 player->shield_normal_time_left += level.shield_normal_time;
11806 if (element == EL_SHIELD_DEADLY)
11807 player->shield_deadly_time_left += level.shield_deadly_time;
11809 else if (element == EL_DYNAMITE ||
11810 element == EL_EM_DYNAMITE ||
11811 element == EL_SP_DISK_RED)
11813 if (player->inventory_size < MAX_INVENTORY_SIZE)
11814 player->inventory_element[player->inventory_size++] = element;
11817 DrawGameDoorValues();
11819 DrawGameValue_Dynamite(local_player->inventory_size);
11822 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
11824 player->dynabomb_count++;
11825 player->dynabombs_left++;
11827 else if (element == EL_DYNABOMB_INCREASE_SIZE)
11829 player->dynabomb_size++;
11831 else if (element == EL_DYNABOMB_INCREASE_POWER)
11833 player->dynabomb_xl = TRUE;
11835 else if (IS_KEY(element))
11837 player->key[KEY_NR(element)] = TRUE;
11840 DrawGameDoorValues();
11842 DrawGameValue_Keys(player->key);
11845 redraw_mask |= REDRAW_DOOR_1;
11847 else if (IS_ENVELOPE(element))
11849 player->show_envelope = element;
11851 else if (element == EL_EMC_LENSES)
11853 game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
11855 RedrawAllInvisibleElementsForLenses();
11857 else if (element == EL_EMC_MAGNIFIER)
11859 game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
11861 RedrawAllInvisibleElementsForMagnifier();
11863 else if (IS_DROPPABLE(element) ||
11864 IS_THROWABLE(element)) /* can be collected and dropped */
11868 if (collect_count == 0)
11869 player->inventory_infinite_element = element;
11871 for (i = 0; i < collect_count; i++)
11872 if (player->inventory_size < MAX_INVENTORY_SIZE)
11873 player->inventory_element[player->inventory_size++] = element;
11876 DrawGameDoorValues();
11878 DrawGameValue_Dynamite(local_player->inventory_size);
11881 else if (collect_count > 0)
11883 local_player->gems_still_needed -= collect_count;
11884 if (local_player->gems_still_needed < 0)
11885 local_player->gems_still_needed = 0;
11887 DrawGameValue_Emeralds(local_player->gems_still_needed);
11890 RaiseScoreElement(element);
11891 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
11894 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
11895 player->index_bit, dig_side);
11897 if (mode == DF_SNAP)
11899 #if USE_NEW_SNAP_DELAY
11900 if (level.block_snap_field)
11901 setFieldForSnapping(x, y, element, move_direction);
11903 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11905 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11908 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
11909 player->index_bit, dig_side);
11912 else if (player_can_move_or_snap && IS_PUSHABLE(element))
11914 if (mode == DF_SNAP && element != EL_BD_ROCK)
11915 return MP_NO_ACTION;
11917 if (CAN_FALL(element) && dy)
11918 return MP_NO_ACTION;
11920 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
11921 !(element == EL_SPRING && level.use_spring_bug))
11922 return MP_NO_ACTION;
11924 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
11925 ((move_direction & MV_VERTICAL &&
11926 ((element_info[element].move_pattern & MV_LEFT &&
11927 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
11928 (element_info[element].move_pattern & MV_RIGHT &&
11929 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
11930 (move_direction & MV_HORIZONTAL &&
11931 ((element_info[element].move_pattern & MV_UP &&
11932 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
11933 (element_info[element].move_pattern & MV_DOWN &&
11934 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
11935 return MP_NO_ACTION;
11937 /* do not push elements already moving away faster than player */
11938 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
11939 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
11940 return MP_NO_ACTION;
11942 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
11944 if (player->push_delay_value == -1 || !player_was_pushing)
11945 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11947 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11949 if (player->push_delay_value == -1)
11950 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11952 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
11954 if (!player->is_pushing)
11955 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11958 player->is_pushing = TRUE;
11960 if (!(IN_LEV_FIELD(nextx, nexty) &&
11961 (IS_FREE(nextx, nexty) ||
11962 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
11963 IS_SB_ELEMENT(element)))))
11964 return MP_NO_ACTION;
11966 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
11967 return MP_NO_ACTION;
11969 if (player->push_delay == -1) /* new pushing; restart delay */
11970 player->push_delay = 0;
11972 if (player->push_delay < player->push_delay_value &&
11973 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
11974 element != EL_SPRING && element != EL_BALLOON)
11976 /* make sure that there is no move delay before next try to push */
11977 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11978 player->move_delay = 0;
11980 return MP_NO_ACTION;
11983 if (IS_SB_ELEMENT(element))
11985 if (element == EL_SOKOBAN_FIELD_FULL)
11987 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
11988 local_player->sokobanfields_still_needed++;
11991 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
11993 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
11994 local_player->sokobanfields_still_needed--;
11997 Feld[x][y] = EL_SOKOBAN_OBJECT;
11999 if (Back[x][y] == Back[nextx][nexty])
12000 PlayLevelSoundAction(x, y, ACTION_PUSHING);
12001 else if (Back[x][y] != 0)
12002 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
12005 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
12008 if (local_player->sokobanfields_still_needed == 0 &&
12009 game.emulation == EMU_SOKOBAN)
12011 player->LevelSolved = player->GameOver = TRUE;
12012 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
12016 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12018 InitMovingField(x, y, move_direction);
12019 GfxAction[x][y] = ACTION_PUSHING;
12021 if (mode == DF_SNAP)
12022 ContinueMoving(x, y);
12024 MovPos[x][y] = (dx != 0 ? dx : dy);
12026 Pushed[x][y] = TRUE;
12027 Pushed[nextx][nexty] = TRUE;
12029 if (game.engine_version < VERSION_IDENT(2,2,0,7))
12030 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12032 player->push_delay_value = -1; /* get new value later */
12034 /* check for element change _after_ element has been pushed */
12035 if (game.use_change_when_pushing_bug)
12037 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
12038 player->index_bit, dig_side);
12039 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
12040 player->index_bit, dig_side);
12043 else if (IS_SWITCHABLE(element))
12045 if (PLAYER_SWITCHING(player, x, y))
12047 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
12048 player->index_bit, dig_side);
12053 player->is_switching = TRUE;
12054 player->switch_x = x;
12055 player->switch_y = y;
12057 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
12059 if (element == EL_ROBOT_WHEEL)
12061 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
12065 DrawLevelField(x, y);
12067 else if (element == EL_SP_TERMINAL)
12072 SCAN_PLAYFIELD(xx, yy)
12074 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
12077 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
12079 else if (Feld[xx][yy] == EL_SP_TERMINAL)
12080 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
12083 else if (IS_BELT_SWITCH(element))
12085 ToggleBeltSwitch(x, y);
12087 else if (element == EL_SWITCHGATE_SWITCH_UP ||
12088 element == EL_SWITCHGATE_SWITCH_DOWN)
12090 ToggleSwitchgateSwitch(x, y);
12092 else if (element == EL_LIGHT_SWITCH ||
12093 element == EL_LIGHT_SWITCH_ACTIVE)
12095 ToggleLightSwitch(x, y);
12097 else if (element == EL_TIMEGATE_SWITCH)
12099 ActivateTimegateSwitch(x, y);
12101 else if (element == EL_BALLOON_SWITCH_LEFT ||
12102 element == EL_BALLOON_SWITCH_RIGHT ||
12103 element == EL_BALLOON_SWITCH_UP ||
12104 element == EL_BALLOON_SWITCH_DOWN ||
12105 element == EL_BALLOON_SWITCH_NONE ||
12106 element == EL_BALLOON_SWITCH_ANY)
12108 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
12109 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
12110 element == EL_BALLOON_SWITCH_UP ? MV_UP :
12111 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
12112 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
12115 else if (element == EL_LAMP)
12117 Feld[x][y] = EL_LAMP_ACTIVE;
12118 local_player->lights_still_needed--;
12120 ResetGfxAnimation(x, y);
12121 DrawLevelField(x, y);
12123 else if (element == EL_TIME_ORB_FULL)
12125 Feld[x][y] = EL_TIME_ORB_EMPTY;
12127 if (level.time > 0 || level.use_time_orb_bug)
12129 TimeLeft += level.time_orb_time;
12130 DrawGameValue_Time(TimeLeft);
12133 ResetGfxAnimation(x, y);
12134 DrawLevelField(x, y);
12136 else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
12137 element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
12141 game.ball_state = !game.ball_state;
12144 SCAN_PLAYFIELD(xx, yy)
12146 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
12149 int e = Feld[xx][yy];
12151 if (game.ball_state)
12153 if (e == EL_EMC_MAGIC_BALL)
12154 CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
12155 else if (e == EL_EMC_MAGIC_BALL_SWITCH)
12156 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
12160 if (e == EL_EMC_MAGIC_BALL_ACTIVE)
12161 CreateField(xx, yy, EL_EMC_MAGIC_BALL);
12162 else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
12163 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
12168 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
12169 player->index_bit, dig_side);
12171 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
12172 player->index_bit, dig_side);
12174 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
12175 player->index_bit, dig_side);
12181 if (!PLAYER_SWITCHING(player, x, y))
12183 player->is_switching = TRUE;
12184 player->switch_x = x;
12185 player->switch_y = y;
12187 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
12188 player->index_bit, dig_side);
12189 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
12190 player->index_bit, dig_side);
12192 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
12193 player->index_bit, dig_side);
12194 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
12195 player->index_bit, dig_side);
12198 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
12199 player->index_bit, dig_side);
12200 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
12201 player->index_bit, dig_side);
12203 return MP_NO_ACTION;
12206 player->push_delay = -1;
12208 if (is_player) /* function can also be called by EL_PENGUIN */
12210 if (Feld[x][y] != element) /* really digged/collected something */
12211 player->is_collecting = !player->is_digging;
12217 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
12219 int jx = player->jx, jy = player->jy;
12220 int x = jx + dx, y = jy + dy;
12221 int snap_direction = (dx == -1 ? MV_LEFT :
12222 dx == +1 ? MV_RIGHT :
12224 dy == +1 ? MV_DOWN : MV_NONE);
12225 boolean can_continue_snapping = (level.continuous_snapping &&
12226 WasJustFalling[x][y] < CHECK_DELAY_FALLING);
12228 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
12231 if (!player->active || !IN_LEV_FIELD(x, y))
12239 if (player->MovPos == 0)
12240 player->is_pushing = FALSE;
12242 player->is_snapping = FALSE;
12244 if (player->MovPos == 0)
12246 player->is_moving = FALSE;
12247 player->is_digging = FALSE;
12248 player->is_collecting = FALSE;
12254 #if USE_NEW_CONTINUOUS_SNAPPING
12255 /* prevent snapping with already pressed snap key when not allowed */
12256 if (player->is_snapping && !can_continue_snapping)
12259 if (player->is_snapping)
12263 player->MovDir = snap_direction;
12265 if (player->MovPos == 0)
12267 player->is_moving = FALSE;
12268 player->is_digging = FALSE;
12269 player->is_collecting = FALSE;
12272 player->is_dropping = FALSE;
12273 player->is_dropping_pressed = FALSE;
12274 player->drop_pressed_delay = 0;
12276 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
12279 player->is_snapping = TRUE;
12281 if (player->MovPos == 0)
12283 player->is_moving = FALSE;
12284 player->is_digging = FALSE;
12285 player->is_collecting = FALSE;
12288 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
12289 DrawLevelField(player->last_jx, player->last_jy);
12291 DrawLevelField(x, y);
12296 boolean DropElement(struct PlayerInfo *player)
12298 int old_element, new_element;
12299 int dropx = player->jx, dropy = player->jy;
12300 int drop_direction = player->MovDir;
12301 int drop_side = drop_direction;
12302 int drop_element = (player->inventory_size > 0 ?
12303 player->inventory_element[player->inventory_size - 1] :
12304 player->inventory_infinite_element != EL_UNDEFINED ?
12305 player->inventory_infinite_element :
12306 player->dynabombs_left > 0 ?
12307 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
12310 player->is_dropping_pressed = TRUE;
12312 /* do not drop an element on top of another element; when holding drop key
12313 pressed without moving, dropped element must move away before the next
12314 element can be dropped (this is especially important if the next element
12315 is dynamite, which can be placed on background for historical reasons) */
12316 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
12319 if (IS_THROWABLE(drop_element))
12321 dropx += GET_DX_FROM_DIR(drop_direction);
12322 dropy += GET_DY_FROM_DIR(drop_direction);
12324 if (!IN_LEV_FIELD(dropx, dropy))
12328 old_element = Feld[dropx][dropy]; /* old element at dropping position */
12329 new_element = drop_element; /* default: no change when dropping */
12331 /* check if player is active, not moving and ready to drop */
12332 if (!player->active || player->MovPos || player->drop_delay > 0)
12335 /* check if player has anything that can be dropped */
12336 if (new_element == EL_UNDEFINED)
12339 /* check if drop key was pressed long enough for EM style dynamite */
12340 if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
12343 /* check if anything can be dropped at the current position */
12344 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
12347 /* collected custom elements can only be dropped on empty fields */
12348 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
12351 if (old_element != EL_EMPTY)
12352 Back[dropx][dropy] = old_element; /* store old element on this field */
12354 ResetGfxAnimation(dropx, dropy);
12355 ResetRandomAnimationValue(dropx, dropy);
12357 if (player->inventory_size > 0 ||
12358 player->inventory_infinite_element != EL_UNDEFINED)
12360 if (player->inventory_size > 0)
12362 player->inventory_size--;
12365 DrawGameDoorValues();
12367 DrawGameValue_Dynamite(local_player->inventory_size);
12370 if (new_element == EL_DYNAMITE)
12371 new_element = EL_DYNAMITE_ACTIVE;
12372 else if (new_element == EL_EM_DYNAMITE)
12373 new_element = EL_EM_DYNAMITE_ACTIVE;
12374 else if (new_element == EL_SP_DISK_RED)
12375 new_element = EL_SP_DISK_RED_ACTIVE;
12378 Feld[dropx][dropy] = new_element;
12380 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
12381 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
12382 el2img(Feld[dropx][dropy]), 0);
12384 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
12386 /* needed if previous element just changed to "empty" in the last frame */
12387 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
12389 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
12390 player->index_bit, drop_side);
12391 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
12393 player->index_bit, drop_side);
12395 TestIfElementTouchesCustomElement(dropx, dropy);
12397 else /* player is dropping a dyna bomb */
12399 player->dynabombs_left--;
12401 Feld[dropx][dropy] = new_element;
12403 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
12404 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
12405 el2img(Feld[dropx][dropy]), 0);
12407 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
12410 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
12411 InitField_WithBug1(dropx, dropy, FALSE);
12413 new_element = Feld[dropx][dropy]; /* element might have changed */
12415 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
12416 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
12418 int move_direction, nextx, nexty;
12420 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
12421 MovDir[dropx][dropy] = drop_direction;
12423 move_direction = MovDir[dropx][dropy];
12424 nextx = dropx + GET_DX_FROM_DIR(move_direction);
12425 nexty = dropy + GET_DY_FROM_DIR(move_direction);
12427 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
12428 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
12431 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
12432 player->is_dropping = TRUE;
12434 player->drop_pressed_delay = 0;
12435 player->is_dropping_pressed = FALSE;
12437 player->drop_x = dropx;
12438 player->drop_y = dropy;
12443 /* ------------------------------------------------------------------------- */
12444 /* game sound playing functions */
12445 /* ------------------------------------------------------------------------- */
12447 static int *loop_sound_frame = NULL;
12448 static int *loop_sound_volume = NULL;
12450 void InitPlayLevelSound()
12452 int num_sounds = getSoundListSize();
12454 checked_free(loop_sound_frame);
12455 checked_free(loop_sound_volume);
12457 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
12458 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
12461 static void PlayLevelSound(int x, int y, int nr)
12463 int sx = SCREENX(x), sy = SCREENY(y);
12464 int volume, stereo_position;
12465 int max_distance = 8;
12466 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
12468 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
12469 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
12472 if (!IN_LEV_FIELD(x, y) ||
12473 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
12474 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
12477 volume = SOUND_MAX_VOLUME;
12479 if (!IN_SCR_FIELD(sx, sy))
12481 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
12482 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
12484 volume -= volume * (dx > dy ? dx : dy) / max_distance;
12487 stereo_position = (SOUND_MAX_LEFT +
12488 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
12489 (SCR_FIELDX + 2 * max_distance));
12491 if (IS_LOOP_SOUND(nr))
12493 /* This assures that quieter loop sounds do not overwrite louder ones,
12494 while restarting sound volume comparison with each new game frame. */
12496 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
12499 loop_sound_volume[nr] = volume;
12500 loop_sound_frame[nr] = FrameCounter;
12503 PlaySoundExt(nr, volume, stereo_position, type);
12506 static void PlayLevelSoundNearest(int x, int y, int sound_action)
12508 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
12509 x > LEVELX(BX2) ? LEVELX(BX2) : x,
12510 y < LEVELY(BY1) ? LEVELY(BY1) :
12511 y > LEVELY(BY2) ? LEVELY(BY2) : y,
12515 static void PlayLevelSoundAction(int x, int y, int action)
12517 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
12520 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
12522 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
12524 if (sound_effect != SND_UNDEFINED)
12525 PlayLevelSound(x, y, sound_effect);
12528 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
12531 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
12533 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12534 PlayLevelSound(x, y, sound_effect);
12537 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
12539 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
12541 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12542 PlayLevelSound(x, y, sound_effect);
12545 static void StopLevelSoundActionIfLoop(int x, int y, int action)
12547 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
12549 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12550 StopSound(sound_effect);
12553 static void PlayLevelMusic()
12555 if (levelset.music[level_nr] != MUS_UNDEFINED)
12556 PlayMusic(levelset.music[level_nr]); /* from config file */
12558 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
12561 void PlayLevelSound_EM(int x, int y, int element_em, int sample)
12563 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
12568 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
12572 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12576 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12580 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12584 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
12588 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12592 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12595 case SAMPLE_android_clone:
12596 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12599 case SAMPLE_android_move:
12600 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12603 case SAMPLE_spring:
12604 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12608 PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
12612 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
12615 case SAMPLE_eater_eat:
12616 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
12620 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12623 case SAMPLE_collect:
12624 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
12627 case SAMPLE_diamond:
12628 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12631 case SAMPLE_squash:
12632 /* !!! CHECK THIS !!! */
12634 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
12636 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
12640 case SAMPLE_wonderfall:
12641 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
12645 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12649 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12653 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
12657 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
12661 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12665 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
12668 case SAMPLE_wonder:
12669 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12673 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
12676 case SAMPLE_exit_open:
12677 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
12680 case SAMPLE_exit_leave:
12681 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
12684 case SAMPLE_dynamite:
12685 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12689 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12693 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
12697 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12701 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
12705 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
12709 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
12713 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
12718 void RaiseScore(int value)
12720 local_player->score += value;
12722 DrawGameValue_Score(local_player->score);
12725 void RaiseScoreElement(int element)
12730 case EL_BD_DIAMOND:
12731 case EL_EMERALD_YELLOW:
12732 case EL_EMERALD_RED:
12733 case EL_EMERALD_PURPLE:
12734 case EL_SP_INFOTRON:
12735 RaiseScore(level.score[SC_EMERALD]);
12738 RaiseScore(level.score[SC_DIAMOND]);
12741 RaiseScore(level.score[SC_CRYSTAL]);
12744 RaiseScore(level.score[SC_PEARL]);
12747 case EL_BD_BUTTERFLY:
12748 case EL_SP_ELECTRON:
12749 RaiseScore(level.score[SC_BUG]);
12752 case EL_BD_FIREFLY:
12753 case EL_SP_SNIKSNAK:
12754 RaiseScore(level.score[SC_SPACESHIP]);
12757 case EL_DARK_YAMYAM:
12758 RaiseScore(level.score[SC_YAMYAM]);
12761 RaiseScore(level.score[SC_ROBOT]);
12764 RaiseScore(level.score[SC_PACMAN]);
12767 RaiseScore(level.score[SC_NUT]);
12770 case EL_EM_DYNAMITE:
12771 case EL_SP_DISK_RED:
12772 case EL_DYNABOMB_INCREASE_NUMBER:
12773 case EL_DYNABOMB_INCREASE_SIZE:
12774 case EL_DYNABOMB_INCREASE_POWER:
12775 RaiseScore(level.score[SC_DYNAMITE]);
12777 case EL_SHIELD_NORMAL:
12778 case EL_SHIELD_DEADLY:
12779 RaiseScore(level.score[SC_SHIELD]);
12781 case EL_EXTRA_TIME:
12782 RaiseScore(level.extra_time_score);
12796 RaiseScore(level.score[SC_KEY]);
12799 RaiseScore(element_info[element].collect_score);
12804 void RequestQuitGame(boolean ask_if_really_quit)
12806 if (AllPlayersGone ||
12807 !ask_if_really_quit ||
12808 level_editor_test_game ||
12809 Request("Do you really want to quit the game ?",
12810 REQ_ASK | REQ_STAY_CLOSED))
12812 #if defined(NETWORK_AVALIABLE)
12813 if (options.network)
12814 SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
12818 game_status = GAME_MODE_MAIN;
12824 if (tape.playing && tape.deactivate_display)
12825 TapeDeactivateDisplayOff(TRUE);
12827 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
12829 if (tape.playing && tape.deactivate_display)
12830 TapeDeactivateDisplayOn();
12835 /* ---------- new game button stuff ---------------------------------------- */
12837 /* graphic position values for game buttons */
12838 #define GAME_BUTTON_XSIZE 30
12839 #define GAME_BUTTON_YSIZE 30
12840 #define GAME_BUTTON_XPOS 5
12841 #define GAME_BUTTON_YPOS 215
12842 #define SOUND_BUTTON_XPOS 5
12843 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
12845 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
12846 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
12847 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
12848 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
12849 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
12850 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
12857 } gamebutton_info[NUM_GAME_BUTTONS] =
12860 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
12865 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
12866 GAME_CTRL_ID_PAUSE,
12870 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
12875 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
12876 SOUND_CTRL_ID_MUSIC,
12877 "background music on/off"
12880 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
12881 SOUND_CTRL_ID_LOOPS,
12882 "sound loops on/off"
12885 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
12886 SOUND_CTRL_ID_SIMPLE,
12887 "normal sounds on/off"
12891 void CreateGameButtons()
12895 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12897 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
12898 struct GadgetInfo *gi;
12901 unsigned long event_mask;
12902 int gd_xoffset, gd_yoffset;
12903 int gd_x1, gd_x2, gd_y1, gd_y2;
12906 gd_xoffset = gamebutton_info[i].x;
12907 gd_yoffset = gamebutton_info[i].y;
12908 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
12909 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
12911 if (id == GAME_CTRL_ID_STOP ||
12912 id == GAME_CTRL_ID_PAUSE ||
12913 id == GAME_CTRL_ID_PLAY)
12915 button_type = GD_TYPE_NORMAL_BUTTON;
12917 event_mask = GD_EVENT_RELEASED;
12918 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12919 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12923 button_type = GD_TYPE_CHECK_BUTTON;
12925 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
12926 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
12927 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
12928 event_mask = GD_EVENT_PRESSED;
12929 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
12930 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12933 gi = CreateGadget(GDI_CUSTOM_ID, id,
12934 GDI_INFO_TEXT, gamebutton_info[i].infotext,
12935 GDI_X, DX + gd_xoffset,
12936 GDI_Y, DY + gd_yoffset,
12937 GDI_WIDTH, GAME_BUTTON_XSIZE,
12938 GDI_HEIGHT, GAME_BUTTON_YSIZE,
12939 GDI_TYPE, button_type,
12940 GDI_STATE, GD_BUTTON_UNPRESSED,
12941 GDI_CHECKED, checked,
12942 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
12943 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
12944 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
12945 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
12946 GDI_EVENT_MASK, event_mask,
12947 GDI_CALLBACK_ACTION, HandleGameButtons,
12951 Error(ERR_EXIT, "cannot create gadget");
12953 game_gadget[id] = gi;
12957 void FreeGameButtons()
12961 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12962 FreeGadget(game_gadget[i]);
12965 static void MapGameButtons()
12969 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12970 MapGadget(game_gadget[i]);
12973 void UnmapGameButtons()
12977 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12978 UnmapGadget(game_gadget[i]);
12981 static void HandleGameButtons(struct GadgetInfo *gi)
12983 int id = gi->custom_id;
12985 if (game_status != GAME_MODE_PLAYING)
12990 case GAME_CTRL_ID_STOP:
12994 RequestQuitGame(TRUE);
12997 case GAME_CTRL_ID_PAUSE:
12998 if (options.network)
13000 #if defined(NETWORK_AVALIABLE)
13002 SendToServer_ContinuePlaying();
13004 SendToServer_PausePlaying();
13008 TapeTogglePause(TAPE_TOGGLE_MANUAL);
13011 case GAME_CTRL_ID_PLAY:
13014 #if defined(NETWORK_AVALIABLE)
13015 if (options.network)
13016 SendToServer_ContinuePlaying();
13020 tape.pausing = FALSE;
13021 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
13026 case SOUND_CTRL_ID_MUSIC:
13027 if (setup.sound_music)
13029 setup.sound_music = FALSE;
13032 else if (audio.music_available)
13034 setup.sound = setup.sound_music = TRUE;
13036 SetAudioMode(setup.sound);
13042 case SOUND_CTRL_ID_LOOPS:
13043 if (setup.sound_loops)
13044 setup.sound_loops = FALSE;
13045 else if (audio.loops_available)
13047 setup.sound = setup.sound_loops = TRUE;
13048 SetAudioMode(setup.sound);
13052 case SOUND_CTRL_ID_SIMPLE:
13053 if (setup.sound_simple)
13054 setup.sound_simple = FALSE;
13055 else if (audio.sound_available)
13057 setup.sound = setup.sound_simple = TRUE;
13058 SetAudioMode(setup.sound);