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 static void ResetRandomAnimationValue(int x, int y)
3017 GfxRandom[x][y] = INIT_GFX_RANDOM();
3020 static void ResetGfxAnimation(int x, int y)
3022 #if USE_GFX_RESET_GFX_ANIMATION
3023 int element, graphic;
3027 GfxAction[x][y] = ACTION_DEFAULT;
3028 GfxDir[x][y] = MovDir[x][y];
3030 #if USE_GFX_RESET_GFX_ANIMATION
3031 element = Feld[x][y];
3032 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3034 if (graphic_info[graphic].anim_global_sync)
3035 GfxFrame[x][y] = FrameCounter;
3036 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
3037 GfxFrame[x][y] = CustomValue[x][y];
3038 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
3039 GfxFrame[x][y] = element_info[element].collect_score;
3043 void InitMovingField(int x, int y, int direction)
3045 int element = Feld[x][y];
3049 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3050 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
3054 if (!WasJustMoving[x][y] || direction != MovDir[x][y])
3055 ResetGfxAnimation(x, y);
3057 MovDir[x][y] = direction;
3058 GfxDir[x][y] = direction;
3059 GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
3060 ACTION_FALLING : ACTION_MOVING);
3063 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3065 if (graphic_info[graphic].anim_global_sync)
3066 GfxFrame[x][y] = FrameCounter;
3067 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
3068 GfxFrame[x][y] = CustomValue[x][y];
3069 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
3070 GfxFrame[x][y] = element_info[element].collect_score;
3073 /* this is needed for CEs with property "can move" / "not moving" */
3075 if (getElementMoveStepsize(x, y) != 0) /* moving or being moved */
3077 if (Feld[newx][newy] == EL_EMPTY)
3078 Feld[newx][newy] = EL_BLOCKED;
3080 MovDir[newx][newy] = MovDir[x][y];
3082 #if USE_NEW_CUSTOM_VALUE
3083 CustomValue[newx][newy] = CustomValue[x][y];
3086 GfxFrame[newx][newy] = GfxFrame[x][y];
3087 GfxRandom[newx][newy] = GfxRandom[x][y];
3088 GfxAction[newx][newy] = GfxAction[x][y];
3089 GfxDir[newx][newy] = GfxDir[x][y];
3093 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
3095 int direction = MovDir[x][y];
3097 int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
3098 int newy = y + (direction & MV_UP ? -1 : direction & MV_DOWN ? +1 : 0);
3100 int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3101 int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
3108 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
3110 int oldx = x, oldy = y;
3111 int direction = MovDir[x][y];
3113 if (direction == MV_LEFT)
3115 else if (direction == MV_RIGHT)
3117 else if (direction == MV_UP)
3119 else if (direction == MV_DOWN)
3122 *comes_from_x = oldx;
3123 *comes_from_y = oldy;
3126 int MovingOrBlocked2Element(int x, int y)
3128 int element = Feld[x][y];
3130 if (element == EL_BLOCKED)
3134 Blocked2Moving(x, y, &oldx, &oldy);
3135 return Feld[oldx][oldy];
3141 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
3143 /* like MovingOrBlocked2Element(), but if element is moving
3144 and (x,y) is the field the moving element is just leaving,
3145 return EL_BLOCKED instead of the element value */
3146 int element = Feld[x][y];
3148 if (IS_MOVING(x, y))
3150 if (element == EL_BLOCKED)
3154 Blocked2Moving(x, y, &oldx, &oldy);
3155 return Feld[oldx][oldy];
3164 static void RemoveField(int x, int y)
3166 Feld[x][y] = EL_EMPTY;
3172 #if USE_NEW_CUSTOM_VALUE
3173 CustomValue[x][y] = 0;
3177 ChangeDelay[x][y] = 0;
3178 ChangePage[x][y] = -1;
3179 Pushed[x][y] = FALSE;
3182 ExplodeField[x][y] = EX_TYPE_NONE;
3185 GfxElement[x][y] = EL_UNDEFINED;
3186 GfxAction[x][y] = ACTION_DEFAULT;
3187 GfxDir[x][y] = MV_NONE;
3190 void RemoveMovingField(int x, int y)
3192 int oldx = x, oldy = y, newx = x, newy = y;
3193 int element = Feld[x][y];
3194 int next_element = EL_UNDEFINED;
3196 if (element != EL_BLOCKED && !IS_MOVING(x, y))
3199 if (IS_MOVING(x, y))
3201 Moving2Blocked(x, y, &newx, &newy);
3203 if (Feld[newx][newy] != EL_BLOCKED)
3205 /* element is moving, but target field is not free (blocked), but
3206 already occupied by something different (example: acid pool);
3207 in this case, only remove the moving field, but not the target */
3209 RemoveField(oldx, oldy);
3211 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
3213 DrawLevelField(oldx, oldy);
3218 else if (element == EL_BLOCKED)
3220 Blocked2Moving(x, y, &oldx, &oldy);
3221 if (!IS_MOVING(oldx, oldy))
3225 if (element == EL_BLOCKED &&
3226 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
3227 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
3228 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
3229 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
3230 next_element = get_next_element(Feld[oldx][oldy]);
3232 RemoveField(oldx, oldy);
3233 RemoveField(newx, newy);
3235 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
3237 if (next_element != EL_UNDEFINED)
3238 Feld[oldx][oldy] = next_element;
3240 DrawLevelField(oldx, oldy);
3241 DrawLevelField(newx, newy);
3244 void DrawDynamite(int x, int y)
3246 int sx = SCREENX(x), sy = SCREENY(y);
3247 int graphic = el2img(Feld[x][y]);
3250 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
3253 if (IS_WALKABLE_INSIDE(Back[x][y]))
3257 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
3258 else if (Store[x][y])
3259 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
3261 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
3263 if (Back[x][y] || Store[x][y])
3264 DrawGraphicThruMask(sx, sy, graphic, frame);
3266 DrawGraphic(sx, sy, graphic, frame);
3269 void CheckDynamite(int x, int y)
3271 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
3275 if (MovDelay[x][y] != 0)
3278 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
3284 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
3291 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
3293 boolean num_checked_players = 0;
3296 for (i = 0; i < MAX_PLAYERS; i++)
3298 if (stored_player[i].active)
3300 int sx = stored_player[i].jx;
3301 int sy = stored_player[i].jy;
3303 if (num_checked_players == 0)
3310 *sx1 = MIN(*sx1, sx);
3311 *sy1 = MIN(*sy1, sy);
3312 *sx2 = MAX(*sx2, sx);
3313 *sy2 = MAX(*sy2, sy);
3316 num_checked_players++;
3321 static boolean checkIfAllPlayersFitToScreen_RND()
3323 int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
3325 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
3327 return (sx2 - sx1 < SCR_FIELDX &&
3328 sy2 - sy1 < SCR_FIELDY);
3331 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
3333 int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
3335 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
3337 *sx = (sx1 + sx2) / 2;
3338 *sy = (sy1 + sy2) / 2;
3342 static void setMaxCenterDistanceForAllPlayers(int *max_dx, int *max_dy,
3343 int center_x, int center_y)
3345 int sx1 = center_x, sy1 = center_y, sx2 = center_x, sy2 = center_y;
3347 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
3349 *max_dx = MAX(ABS(sx1 - center_x), ABS(sx2 - center_x));
3350 *max_dy = MAX(ABS(sy1 - center_y), ABS(sy2 - center_y));
3353 static boolean checkIfAllPlayersAreVisible(int center_x, int center_y)
3357 setMaxCenterDistanceForAllPlayers(&max_dx, &max_dy, center_x, center_y);
3359 return (max_dx <= SCR_FIELDX / 2 &&
3360 max_dy <= SCR_FIELDY / 2);
3368 void DrawRelocateScreen(int x, int y, int move_dir, boolean center_screen,
3369 boolean quick_relocation)
3371 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3372 boolean no_delay = (tape.warp_forward);
3373 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
3374 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
3376 if (quick_relocation)
3378 int offset = (setup.scroll_delay ? 3 : 0);
3385 if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
3387 scroll_x = (x < SBX_Left + MIDPOSX ? SBX_Left :
3388 x > SBX_Right + MIDPOSX ? SBX_Right :
3391 scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
3392 y > SBY_Lower + MIDPOSY ? SBY_Lower :
3397 if ((move_dir == MV_LEFT && scroll_x > x - MIDPOSX + offset) ||
3398 (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
3399 scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
3401 if ((move_dir == MV_UP && scroll_y > y - MIDPOSY + offset) ||
3402 (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
3403 scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
3405 /* don't scroll over playfield boundaries */
3406 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
3407 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
3409 /* don't scroll over playfield boundaries */
3410 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
3411 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
3414 RedrawPlayfield(TRUE, 0,0,0,0);
3418 int scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left :
3419 x > SBX_Right + MIDPOSX ? SBX_Right :
3422 int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
3423 y > SBY_Lower + MIDPOSY ? SBY_Lower :
3426 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
3428 while (scroll_x != scroll_xx || scroll_y != scroll_yy)
3431 int fx = FX, fy = FY;
3433 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
3434 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
3436 if (dx == 0 && dy == 0) /* no scrolling needed at all */
3442 fx += dx * TILEX / 2;
3443 fy += dy * TILEY / 2;
3445 ScrollLevel(dx, dy);
3448 /* scroll in two steps of half tile size to make things smoother */
3449 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
3451 Delay(wait_delay_value);
3453 /* scroll second step to align at full tile size */
3455 Delay(wait_delay_value);
3460 Delay(wait_delay_value);
3466 void DrawRelocatePlayer(struct PlayerInfo *player, boolean quick_relocation)
3468 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3469 boolean no_delay = (tape.warp_forward);
3470 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
3471 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
3472 int jx = player->jx;
3473 int jy = player->jy;
3475 if (quick_relocation)
3477 int offset = (setup.scroll_delay ? 3 : 0);
3479 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
3481 scroll_x = (player->jx < SBX_Left + MIDPOSX ? SBX_Left :
3482 player->jx > SBX_Right + MIDPOSX ? SBX_Right :
3483 player->jx - MIDPOSX);
3485 scroll_y = (player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
3486 player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
3487 player->jy - MIDPOSY);
3491 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
3492 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
3493 scroll_x = jx - MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
3495 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
3496 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
3497 scroll_y = jy - MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
3499 /* don't scroll over playfield boundaries */
3500 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
3501 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
3503 /* don't scroll over playfield boundaries */
3504 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
3505 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
3508 RedrawPlayfield(TRUE, 0,0,0,0);
3512 int scroll_xx = (player->jx < SBX_Left + MIDPOSX ? SBX_Left :
3513 player->jx > SBX_Right + MIDPOSX ? SBX_Right :
3514 player->jx - MIDPOSX);
3516 int scroll_yy = (player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
3517 player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
3518 player->jy - MIDPOSY);
3520 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
3522 while (scroll_x != scroll_xx || scroll_y != scroll_yy)
3525 int fx = FX, fy = FY;
3527 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
3528 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
3530 if (dx == 0 && dy == 0) /* no scrolling needed at all */
3536 fx += dx * TILEX / 2;
3537 fy += dy * TILEY / 2;
3539 ScrollLevel(dx, dy);
3542 /* scroll in two steps of half tile size to make things smoother */
3543 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
3545 Delay(wait_delay_value);
3547 /* scroll second step to align at full tile size */
3549 Delay(wait_delay_value);
3554 Delay(wait_delay_value);
3560 void RelocatePlayer(int jx, int jy, int el_player_raw)
3562 int el_player = GET_PLAYER_ELEMENT(el_player_raw);
3563 int player_nr = GET_PLAYER_NR(el_player);
3564 struct PlayerInfo *player = &stored_player[player_nr];
3565 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3566 boolean no_delay = (tape.warp_forward);
3567 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
3568 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
3569 int old_jx = player->jx;
3570 int old_jy = player->jy;
3571 int old_element = Feld[old_jx][old_jy];
3572 int element = Feld[jx][jy];
3573 boolean player_relocated = (old_jx != jx || old_jy != jy);
3575 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
3576 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
3577 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
3578 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
3579 int leave_side_horiz = move_dir_horiz;
3580 int leave_side_vert = move_dir_vert;
3581 int enter_side = enter_side_horiz | enter_side_vert;
3582 int leave_side = leave_side_horiz | leave_side_vert;
3584 if (player->GameOver) /* do not reanimate dead player */
3587 if (!player_relocated) /* no need to relocate the player */
3590 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
3592 RemoveField(jx, jy); /* temporarily remove newly placed player */
3593 DrawLevelField(jx, jy);
3596 if (player->present)
3598 while (player->MovPos)
3600 ScrollPlayer(player, SCROLL_GO_ON);
3601 ScrollScreen(NULL, SCROLL_GO_ON);
3603 AdvanceFrameAndPlayerCounters(player->index_nr);
3608 Delay(wait_delay_value);
3611 DrawPlayer(player); /* needed here only to cleanup last field */
3612 DrawLevelField(player->jx, player->jy); /* remove player graphic */
3614 player->is_moving = FALSE;
3617 if (IS_CUSTOM_ELEMENT(old_element))
3618 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
3620 player->index_bit, leave_side);
3622 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
3624 player->index_bit, leave_side);
3626 Feld[jx][jy] = el_player;
3627 InitPlayerField(jx, jy, el_player, TRUE);
3629 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
3631 Feld[jx][jy] = element;
3632 InitField(jx, jy, FALSE);
3636 /* only visually relocate centered player */
3638 DrawRelocateScreen(player->jx, player->jy, player->MovDir, FALSE,
3639 level.instant_relocation);
3641 if (player->index_nr == game.centered_player_nr)
3642 DrawRelocatePlayer(player, level.instant_relocation);
3645 if (player == local_player) /* only visually relocate local player */
3646 DrawRelocatePlayer(player, level.instant_relocation);
3649 TestIfPlayerTouchesBadThing(jx, jy);
3650 TestIfPlayerTouchesCustomElement(jx, jy);
3652 if (IS_CUSTOM_ELEMENT(element))
3653 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
3654 player->index_bit, enter_side);
3656 CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
3657 player->index_bit, enter_side);
3660 void Explode(int ex, int ey, int phase, int mode)
3666 /* !!! eliminate this variable !!! */
3667 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
3669 if (game.explosions_delayed)
3671 ExplodeField[ex][ey] = mode;
3675 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
3677 int center_element = Feld[ex][ey];
3678 int artwork_element, explosion_element; /* set these values later */
3681 /* --- This is only really needed (and now handled) in "Impact()". --- */
3682 /* do not explode moving elements that left the explode field in time */
3683 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
3684 center_element == EL_EMPTY &&
3685 (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
3690 /* !!! at this place, the center element may be EL_BLOCKED !!! */
3691 if (mode == EX_TYPE_NORMAL ||
3692 mode == EX_TYPE_CENTER ||
3693 mode == EX_TYPE_CROSS)
3694 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
3697 /* remove things displayed in background while burning dynamite */
3698 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
3701 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
3703 /* put moving element to center field (and let it explode there) */
3704 center_element = MovingOrBlocked2Element(ex, ey);
3705 RemoveMovingField(ex, ey);
3706 Feld[ex][ey] = center_element;
3709 /* now "center_element" is finally determined -- set related values now */
3710 artwork_element = center_element; /* for custom player artwork */
3711 explosion_element = center_element; /* for custom player artwork */
3713 if (IS_PLAYER(ex, ey))
3715 int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
3717 artwork_element = stored_player[player_nr].artwork_element;
3719 if (level.use_explosion_element[player_nr])
3721 explosion_element = level.explosion_element[player_nr];
3722 artwork_element = explosion_element;
3727 if (mode == EX_TYPE_NORMAL ||
3728 mode == EX_TYPE_CENTER ||
3729 mode == EX_TYPE_CROSS)
3730 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
3734 last_phase = element_info[explosion_element].explosion_delay + 1;
3736 last_phase = element_info[center_element].explosion_delay + 1;
3739 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
3741 int xx = x - ex + 1;
3742 int yy = y - ey + 1;
3745 if (!IN_LEV_FIELD(x, y) ||
3746 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
3747 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
3750 element = Feld[x][y];
3752 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
3754 element = MovingOrBlocked2Element(x, y);
3756 if (!IS_EXPLOSION_PROOF(element))
3757 RemoveMovingField(x, y);
3760 /* indestructible elements can only explode in center (but not flames) */
3761 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
3762 mode == EX_TYPE_BORDER)) ||
3763 element == EL_FLAMES)
3766 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
3767 behaviour, for example when touching a yamyam that explodes to rocks
3768 with active deadly shield, a rock is created under the player !!! */
3769 /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
3771 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
3772 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
3773 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
3775 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
3778 if (IS_ACTIVE_BOMB(element))
3780 /* re-activate things under the bomb like gate or penguin */
3781 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
3788 /* save walkable background elements while explosion on same tile */
3789 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
3790 (x != ex || y != ey || mode == EX_TYPE_BORDER))
3791 Back[x][y] = element;
3793 /* ignite explodable elements reached by other explosion */
3794 if (element == EL_EXPLOSION)
3795 element = Store2[x][y];
3797 if (AmoebaNr[x][y] &&
3798 (element == EL_AMOEBA_FULL ||
3799 element == EL_BD_AMOEBA ||
3800 element == EL_AMOEBA_GROWING))
3802 AmoebaCnt[AmoebaNr[x][y]]--;
3803 AmoebaCnt2[AmoebaNr[x][y]]--;
3808 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
3811 int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
3813 Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
3815 switch(StorePlayer[ex][ey])
3818 Store[x][y] = EL_PLAYER_IS_EXPLODING_2;
3821 Store[x][y] = EL_PLAYER_IS_EXPLODING_3;
3824 Store[x][y] = EL_PLAYER_IS_EXPLODING_4;
3828 Store[x][y] = EL_PLAYER_IS_EXPLODING_1;
3833 if (PLAYERINFO(ex, ey)->use_murphy)
3834 Store[x][y] = EL_EMPTY;
3837 /* !!! check this case -- currently needed for rnd_rado_negundo_v,
3838 !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
3839 else if (ELEM_IS_PLAYER(center_element))
3840 Store[x][y] = EL_EMPTY;
3841 else if (center_element == EL_YAMYAM)
3842 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
3843 else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
3844 Store[x][y] = element_info[center_element].content.e[xx][yy];
3846 /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
3847 (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
3848 otherwise) -- FIX THIS !!! */
3849 else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
3850 Store[x][y] = element_info[element].content.e[1][1];
3852 else if (!CAN_EXPLODE(element))
3853 Store[x][y] = element_info[element].content.e[1][1];
3856 Store[x][y] = EL_EMPTY;
3858 else if (center_element == EL_MOLE)
3859 Store[x][y] = EL_EMERALD_RED;
3860 else if (center_element == EL_PENGUIN)
3861 Store[x][y] = EL_EMERALD_PURPLE;
3862 else if (center_element == EL_BUG)
3863 Store[x][y] = ((x == ex && y == ey) ? EL_DIAMOND : EL_EMERALD);
3864 else if (center_element == EL_BD_BUTTERFLY)
3865 Store[x][y] = EL_BD_DIAMOND;
3866 else if (center_element == EL_SP_ELECTRON)
3867 Store[x][y] = EL_SP_INFOTRON;
3868 else if (center_element == EL_AMOEBA_TO_DIAMOND)
3869 Store[x][y] = level.amoeba_content;
3870 else if (center_element == EL_YAMYAM)
3871 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
3872 else if (IS_CUSTOM_ELEMENT(center_element) &&
3873 element_info[center_element].content.e[xx][yy] != EL_EMPTY)
3874 Store[x][y] = element_info[center_element].content.e[xx][yy];
3875 else if (element == EL_WALL_EMERALD)
3876 Store[x][y] = EL_EMERALD;
3877 else if (element == EL_WALL_DIAMOND)
3878 Store[x][y] = EL_DIAMOND;
3879 else if (element == EL_WALL_BD_DIAMOND)
3880 Store[x][y] = EL_BD_DIAMOND;
3881 else if (element == EL_WALL_EMERALD_YELLOW)
3882 Store[x][y] = EL_EMERALD_YELLOW;
3883 else if (element == EL_WALL_EMERALD_RED)
3884 Store[x][y] = EL_EMERALD_RED;
3885 else if (element == EL_WALL_EMERALD_PURPLE)
3886 Store[x][y] = EL_EMERALD_PURPLE;
3887 else if (element == EL_WALL_PEARL)
3888 Store[x][y] = EL_PEARL;
3889 else if (element == EL_WALL_CRYSTAL)
3890 Store[x][y] = EL_CRYSTAL;
3891 else if (IS_CUSTOM_ELEMENT(element) && !CAN_EXPLODE(element))
3892 Store[x][y] = element_info[element].content.e[1][1];
3894 Store[x][y] = EL_EMPTY;
3897 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
3898 center_element == EL_AMOEBA_TO_DIAMOND)
3899 Store2[x][y] = element;
3901 Feld[x][y] = EL_EXPLOSION;
3902 GfxElement[x][y] = artwork_element;
3905 printf(":: setting gfx(%d,%d) to %d ['%s']\n",
3906 x, y, artwork_element, EL_NAME(artwork_element));
3909 ExplodePhase[x][y] = 1;
3910 ExplodeDelay[x][y] = last_phase;
3915 if (center_element == EL_YAMYAM)
3916 game.yamyam_content_nr =
3917 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
3929 GfxFrame[x][y] = 0; /* restart explosion animation */
3931 last_phase = ExplodeDelay[x][y];
3933 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
3937 /* activate this even in non-DEBUG version until cause for crash in
3938 getGraphicAnimationFrame() (see below) is found and eliminated */
3944 /* this can happen if the player leaves an explosion just in time */
3945 if (GfxElement[x][y] == EL_UNDEFINED)
3946 GfxElement[x][y] = EL_EMPTY;
3948 if (GfxElement[x][y] == EL_UNDEFINED)
3951 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
3952 printf("Explode(): This should never happen!\n");
3955 GfxElement[x][y] = EL_EMPTY;
3961 border_element = Store2[x][y];
3962 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3963 border_element = StorePlayer[x][y];
3965 if (phase == element_info[border_element].ignition_delay ||
3966 phase == last_phase)
3968 boolean border_explosion = FALSE;
3970 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
3971 !PLAYER_EXPLOSION_PROTECTED(x, y))
3973 KillPlayerUnlessExplosionProtected(x, y);
3974 border_explosion = TRUE;
3976 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
3978 Feld[x][y] = Store2[x][y];
3981 border_explosion = TRUE;
3983 else if (border_element == EL_AMOEBA_TO_DIAMOND)
3985 AmoebeUmwandeln(x, y);
3987 border_explosion = TRUE;
3990 /* if an element just explodes due to another explosion (chain-reaction),
3991 do not immediately end the new explosion when it was the last frame of
3992 the explosion (as it would be done in the following "if"-statement!) */
3993 if (border_explosion && phase == last_phase)
3997 if (phase == last_phase)
4001 element = Feld[x][y] = Store[x][y];
4002 Store[x][y] = Store2[x][y] = 0;
4003 GfxElement[x][y] = EL_UNDEFINED;
4005 /* player can escape from explosions and might therefore be still alive */
4006 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
4007 element <= EL_PLAYER_IS_EXPLODING_4)
4009 int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
4010 int explosion_element = EL_PLAYER_1 + player_nr;
4011 int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
4012 int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
4014 if (level.use_explosion_element[player_nr])
4015 explosion_element = level.explosion_element[player_nr];
4017 Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
4018 element_info[explosion_element].content.e[xx][yy]);
4021 /* restore probably existing indestructible background element */
4022 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
4023 element = Feld[x][y] = Back[x][y];
4026 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
4027 GfxDir[x][y] = MV_NONE;
4028 ChangeDelay[x][y] = 0;
4029 ChangePage[x][y] = -1;
4031 #if USE_NEW_CUSTOM_VALUE
4032 CustomValue[x][y] = 0;
4035 InitField_WithBug2(x, y, FALSE);
4037 DrawLevelField(x, y);
4039 TestIfElementTouchesCustomElement(x, y);
4041 if (GFX_CRUMBLED(element))
4042 DrawLevelFieldCrumbledSandNeighbours(x, y);
4044 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
4045 StorePlayer[x][y] = 0;
4047 if (ELEM_IS_PLAYER(element))
4048 RelocatePlayer(x, y, element);
4050 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
4052 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
4053 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4056 DrawLevelFieldCrumbledSand(x, y);
4058 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
4060 DrawLevelElement(x, y, Back[x][y]);
4061 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
4063 else if (IS_WALKABLE_UNDER(Back[x][y]))
4065 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
4066 DrawLevelElementThruMask(x, y, Back[x][y]);
4068 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
4069 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
4073 void DynaExplode(int ex, int ey)
4076 int dynabomb_element = Feld[ex][ey];
4077 int dynabomb_size = 1;
4078 boolean dynabomb_xl = FALSE;
4079 struct PlayerInfo *player;
4080 static int xy[4][2] =
4088 if (IS_ACTIVE_BOMB(dynabomb_element))
4090 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
4091 dynabomb_size = player->dynabomb_size;
4092 dynabomb_xl = player->dynabomb_xl;
4093 player->dynabombs_left++;
4096 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
4098 for (i = 0; i < NUM_DIRECTIONS; i++)
4100 for (j = 1; j <= dynabomb_size; j++)
4102 int x = ex + j * xy[i][0];
4103 int y = ey + j * xy[i][1];
4106 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
4109 element = Feld[x][y];
4111 /* do not restart explosions of fields with active bombs */
4112 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
4115 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
4117 if (element != EL_EMPTY && element != EL_EXPLOSION &&
4118 !IS_DIGGABLE(element) && !dynabomb_xl)
4124 void Bang(int x, int y)
4126 int element = MovingOrBlocked2Element(x, y);
4127 int explosion_type = EX_TYPE_NORMAL;
4129 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
4131 struct PlayerInfo *player = PLAYERINFO(x, y);
4133 element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
4134 player->element_nr);
4136 if (level.use_explosion_element[player->index_nr])
4138 int explosion_element = level.explosion_element[player->index_nr];
4140 if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
4141 explosion_type = EX_TYPE_CROSS;
4142 else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
4143 explosion_type = EX_TYPE_CENTER;
4151 case EL_BD_BUTTERFLY:
4154 case EL_DARK_YAMYAM:
4158 RaiseScoreElement(element);
4161 case EL_DYNABOMB_PLAYER_1_ACTIVE:
4162 case EL_DYNABOMB_PLAYER_2_ACTIVE:
4163 case EL_DYNABOMB_PLAYER_3_ACTIVE:
4164 case EL_DYNABOMB_PLAYER_4_ACTIVE:
4165 case EL_DYNABOMB_INCREASE_NUMBER:
4166 case EL_DYNABOMB_INCREASE_SIZE:
4167 case EL_DYNABOMB_INCREASE_POWER:
4168 explosion_type = EX_TYPE_DYNA;
4173 case EL_LAMP_ACTIVE:
4174 case EL_AMOEBA_TO_DIAMOND:
4175 if (!IS_PLAYER(x, y)) /* penguin and player may be at same field */
4176 explosion_type = EX_TYPE_CENTER;
4180 if (element_info[element].explosion_type == EXPLODES_CROSS)
4181 explosion_type = EX_TYPE_CROSS;
4182 else if (element_info[element].explosion_type == EXPLODES_1X1)
4183 explosion_type = EX_TYPE_CENTER;
4187 if (explosion_type == EX_TYPE_DYNA)
4190 Explode(x, y, EX_PHASE_START, explosion_type);
4192 CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
4195 void SplashAcid(int x, int y)
4197 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
4198 (!IN_LEV_FIELD(x - 1, y - 2) ||
4199 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
4200 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
4202 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
4203 (!IN_LEV_FIELD(x + 1, y - 2) ||
4204 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
4205 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
4207 PlayLevelSound(x, y, SND_ACID_SPLASHING);
4210 static void InitBeltMovement()
4212 static int belt_base_element[4] =
4214 EL_CONVEYOR_BELT_1_LEFT,
4215 EL_CONVEYOR_BELT_2_LEFT,
4216 EL_CONVEYOR_BELT_3_LEFT,
4217 EL_CONVEYOR_BELT_4_LEFT
4219 static int belt_base_active_element[4] =
4221 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
4222 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
4223 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
4224 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
4229 /* set frame order for belt animation graphic according to belt direction */
4230 for (i = 0; i < NUM_BELTS; i++)
4234 for (j = 0; j < NUM_BELT_PARTS; j++)
4236 int element = belt_base_active_element[belt_nr] + j;
4237 int graphic = el2img(element);
4239 if (game.belt_dir[i] == MV_LEFT)
4240 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
4242 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
4247 SCAN_PLAYFIELD(x, y)
4249 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
4252 int element = Feld[x][y];
4254 for (i = 0; i < NUM_BELTS; i++)
4256 if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
4258 int e_belt_nr = getBeltNrFromBeltElement(element);
4261 if (e_belt_nr == belt_nr)
4263 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
4265 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
4272 static void ToggleBeltSwitch(int x, int y)
4274 static int belt_base_element[4] =
4276 EL_CONVEYOR_BELT_1_LEFT,
4277 EL_CONVEYOR_BELT_2_LEFT,
4278 EL_CONVEYOR_BELT_3_LEFT,
4279 EL_CONVEYOR_BELT_4_LEFT
4281 static int belt_base_active_element[4] =
4283 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
4284 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
4285 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
4286 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
4288 static int belt_base_switch_element[4] =
4290 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
4291 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
4292 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
4293 EL_CONVEYOR_BELT_4_SWITCH_LEFT
4295 static int belt_move_dir[4] =
4303 int element = Feld[x][y];
4304 int belt_nr = getBeltNrFromBeltSwitchElement(element);
4305 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
4306 int belt_dir = belt_move_dir[belt_dir_nr];
4309 if (!IS_BELT_SWITCH(element))
4312 game.belt_dir_nr[belt_nr] = belt_dir_nr;
4313 game.belt_dir[belt_nr] = belt_dir;
4315 if (belt_dir_nr == 3)
4318 /* set frame order for belt animation graphic according to belt direction */
4319 for (i = 0; i < NUM_BELT_PARTS; i++)
4321 int element = belt_base_active_element[belt_nr] + i;
4322 int graphic = el2img(element);
4324 if (belt_dir == MV_LEFT)
4325 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
4327 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
4331 SCAN_PLAYFIELD(xx, yy)
4333 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
4336 int element = Feld[xx][yy];
4338 if (IS_BELT_SWITCH(element))
4340 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
4342 if (e_belt_nr == belt_nr)
4344 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
4345 DrawLevelField(xx, yy);
4348 else if (IS_BELT(element) && belt_dir != MV_NONE)
4350 int e_belt_nr = getBeltNrFromBeltElement(element);
4352 if (e_belt_nr == belt_nr)
4354 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
4356 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
4357 DrawLevelField(xx, yy);
4360 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
4362 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
4364 if (e_belt_nr == belt_nr)
4366 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
4368 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
4369 DrawLevelField(xx, yy);
4375 static void ToggleSwitchgateSwitch(int x, int y)
4379 game.switchgate_pos = !game.switchgate_pos;
4382 SCAN_PLAYFIELD(xx, yy)
4384 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
4387 int element = Feld[xx][yy];
4389 if (element == EL_SWITCHGATE_SWITCH_UP ||
4390 element == EL_SWITCHGATE_SWITCH_DOWN)
4392 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
4393 DrawLevelField(xx, yy);
4395 else if (element == EL_SWITCHGATE_OPEN ||
4396 element == EL_SWITCHGATE_OPENING)
4398 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
4400 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
4402 else if (element == EL_SWITCHGATE_CLOSED ||
4403 element == EL_SWITCHGATE_CLOSING)
4405 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
4407 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
4412 static int getInvisibleActiveFromInvisibleElement(int element)
4414 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
4415 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
4416 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
4420 static int getInvisibleFromInvisibleActiveElement(int element)
4422 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
4423 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
4424 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
4428 static void RedrawAllLightSwitchesAndInvisibleElements()
4433 SCAN_PLAYFIELD(x, y)
4435 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
4438 int element = Feld[x][y];
4440 if (element == EL_LIGHT_SWITCH &&
4441 game.light_time_left > 0)
4443 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
4444 DrawLevelField(x, y);
4446 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
4447 game.light_time_left == 0)
4449 Feld[x][y] = EL_LIGHT_SWITCH;
4450 DrawLevelField(x, y);
4452 else if (element == EL_EMC_DRIPPER &&
4453 game.light_time_left > 0)
4455 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
4456 DrawLevelField(x, y);
4458 else if (element == EL_EMC_DRIPPER_ACTIVE &&
4459 game.light_time_left == 0)
4461 Feld[x][y] = EL_EMC_DRIPPER;
4462 DrawLevelField(x, y);
4464 else if (element == EL_INVISIBLE_STEELWALL ||
4465 element == EL_INVISIBLE_WALL ||
4466 element == EL_INVISIBLE_SAND)
4468 if (game.light_time_left > 0)
4469 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
4471 DrawLevelField(x, y);
4473 /* uncrumble neighbour fields, if needed */
4474 if (element == EL_INVISIBLE_SAND)
4475 DrawLevelFieldCrumbledSandNeighbours(x, y);
4477 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
4478 element == EL_INVISIBLE_WALL_ACTIVE ||
4479 element == EL_INVISIBLE_SAND_ACTIVE)
4481 if (game.light_time_left == 0)
4482 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
4484 DrawLevelField(x, y);
4486 /* re-crumble neighbour fields, if needed */
4487 if (element == EL_INVISIBLE_SAND)
4488 DrawLevelFieldCrumbledSandNeighbours(x, y);
4493 static void RedrawAllInvisibleElementsForLenses()
4498 SCAN_PLAYFIELD(x, y)
4500 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
4503 int element = Feld[x][y];
4505 if (element == EL_EMC_DRIPPER &&
4506 game.lenses_time_left > 0)
4508 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
4509 DrawLevelField(x, y);
4511 else if (element == EL_EMC_DRIPPER_ACTIVE &&
4512 game.lenses_time_left == 0)
4514 Feld[x][y] = EL_EMC_DRIPPER;
4515 DrawLevelField(x, y);
4517 else if (element == EL_INVISIBLE_STEELWALL ||
4518 element == EL_INVISIBLE_WALL ||
4519 element == EL_INVISIBLE_SAND)
4521 if (game.lenses_time_left > 0)
4522 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
4524 DrawLevelField(x, y);
4526 /* uncrumble neighbour fields, if needed */
4527 if (element == EL_INVISIBLE_SAND)
4528 DrawLevelFieldCrumbledSandNeighbours(x, y);
4530 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
4531 element == EL_INVISIBLE_WALL_ACTIVE ||
4532 element == EL_INVISIBLE_SAND_ACTIVE)
4534 if (game.lenses_time_left == 0)
4535 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
4537 DrawLevelField(x, y);
4539 /* re-crumble neighbour fields, if needed */
4540 if (element == EL_INVISIBLE_SAND)
4541 DrawLevelFieldCrumbledSandNeighbours(x, y);
4546 static void RedrawAllInvisibleElementsForMagnifier()
4551 SCAN_PLAYFIELD(x, y)
4553 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
4556 int element = Feld[x][y];
4558 if (element == EL_EMC_FAKE_GRASS &&
4559 game.magnify_time_left > 0)
4561 Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
4562 DrawLevelField(x, y);
4564 else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
4565 game.magnify_time_left == 0)
4567 Feld[x][y] = EL_EMC_FAKE_GRASS;
4568 DrawLevelField(x, y);
4570 else if (IS_GATE_GRAY(element) &&
4571 game.magnify_time_left > 0)
4573 Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
4574 element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
4575 IS_EM_GATE_GRAY(element) ?
4576 element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
4577 IS_EMC_GATE_GRAY(element) ?
4578 element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
4580 DrawLevelField(x, y);
4582 else if (IS_GATE_GRAY_ACTIVE(element) &&
4583 game.magnify_time_left == 0)
4585 Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
4586 element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
4587 IS_EM_GATE_GRAY_ACTIVE(element) ?
4588 element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
4589 IS_EMC_GATE_GRAY_ACTIVE(element) ?
4590 element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
4592 DrawLevelField(x, y);
4597 static void ToggleLightSwitch(int x, int y)
4599 int element = Feld[x][y];
4601 game.light_time_left =
4602 (element == EL_LIGHT_SWITCH ?
4603 level.time_light * FRAMES_PER_SECOND : 0);
4605 RedrawAllLightSwitchesAndInvisibleElements();
4608 static void ActivateTimegateSwitch(int x, int y)
4612 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
4615 SCAN_PLAYFIELD(xx, yy)
4617 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
4620 int element = Feld[xx][yy];
4622 if (element == EL_TIMEGATE_CLOSED ||
4623 element == EL_TIMEGATE_CLOSING)
4625 Feld[xx][yy] = EL_TIMEGATE_OPENING;
4626 PlayLevelSound(xx, yy, SND_TIMEGATE_OPENING);
4630 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
4632 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
4633 DrawLevelField(xx, yy);
4639 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
4642 void Impact(int x, int y)
4644 boolean last_line = (y == lev_fieldy - 1);
4645 boolean object_hit = FALSE;
4646 boolean impact = (last_line || object_hit);
4647 int element = Feld[x][y];
4648 int smashed = EL_STEELWALL;
4650 if (!last_line) /* check if element below was hit */
4652 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
4655 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
4656 MovDir[x][y + 1] != MV_DOWN ||
4657 MovPos[x][y + 1] <= TILEY / 2));
4659 /* do not smash moving elements that left the smashed field in time */
4660 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
4661 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
4664 #if USE_QUICKSAND_IMPACT_BUGFIX
4665 if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
4667 RemoveMovingField(x, y + 1);
4668 Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
4669 Feld[x][y + 2] = EL_ROCK;
4670 DrawLevelField(x, y + 2);
4677 smashed = MovingOrBlocked2Element(x, y + 1);
4679 impact = (last_line || object_hit);
4682 if (!last_line && smashed == EL_ACID) /* element falls into acid */
4684 SplashAcid(x, y + 1);
4688 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
4689 /* only reset graphic animation if graphic really changes after impact */
4691 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
4693 ResetGfxAnimation(x, y);
4694 DrawLevelField(x, y);
4697 if (impact && CAN_EXPLODE_IMPACT(element))
4702 else if (impact && element == EL_PEARL)
4704 ResetGfxAnimation(x, y);
4706 Feld[x][y] = EL_PEARL_BREAKING;
4707 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4710 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
4712 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4717 if (impact && element == EL_AMOEBA_DROP)
4719 if (object_hit && IS_PLAYER(x, y + 1))
4720 KillPlayerUnlessEnemyProtected(x, y + 1);
4721 else if (object_hit && smashed == EL_PENGUIN)
4725 Feld[x][y] = EL_AMOEBA_GROWING;
4726 Store[x][y] = EL_AMOEBA_WET;
4728 ResetRandomAnimationValue(x, y);
4733 if (object_hit) /* check which object was hit */
4735 if (CAN_PASS_MAGIC_WALL(element) &&
4736 (smashed == EL_MAGIC_WALL ||
4737 smashed == EL_BD_MAGIC_WALL))
4740 int activated_magic_wall =
4741 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
4742 EL_BD_MAGIC_WALL_ACTIVE);
4744 /* activate magic wall / mill */
4746 SCAN_PLAYFIELD(xx, yy)
4748 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
4750 if (Feld[xx][yy] == smashed)
4751 Feld[xx][yy] = activated_magic_wall;
4753 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
4754 game.magic_wall_active = TRUE;
4756 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
4757 SND_MAGIC_WALL_ACTIVATING :
4758 SND_BD_MAGIC_WALL_ACTIVATING));
4761 if (IS_PLAYER(x, y + 1))
4763 if (CAN_SMASH_PLAYER(element))
4765 KillPlayerUnlessEnemyProtected(x, y + 1);
4769 else if (smashed == EL_PENGUIN)
4771 if (CAN_SMASH_PLAYER(element))
4777 else if (element == EL_BD_DIAMOND)
4779 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
4785 else if (((element == EL_SP_INFOTRON ||
4786 element == EL_SP_ZONK) &&
4787 (smashed == EL_SP_SNIKSNAK ||
4788 smashed == EL_SP_ELECTRON ||
4789 smashed == EL_SP_DISK_ORANGE)) ||
4790 (element == EL_SP_INFOTRON &&
4791 smashed == EL_SP_DISK_YELLOW))
4796 else if (CAN_SMASH_EVERYTHING(element))
4798 if (IS_CLASSIC_ENEMY(smashed) ||
4799 CAN_EXPLODE_SMASHED(smashed))
4804 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
4806 if (smashed == EL_LAMP ||
4807 smashed == EL_LAMP_ACTIVE)
4812 else if (smashed == EL_NUT)
4814 Feld[x][y + 1] = EL_NUT_BREAKING;
4815 PlayLevelSound(x, y, SND_NUT_BREAKING);
4816 RaiseScoreElement(EL_NUT);
4819 else if (smashed == EL_PEARL)
4821 ResetGfxAnimation(x, y);
4823 Feld[x][y + 1] = EL_PEARL_BREAKING;
4824 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4827 else if (smashed == EL_DIAMOND)
4829 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
4830 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
4833 else if (IS_BELT_SWITCH(smashed))
4835 ToggleBeltSwitch(x, y + 1);
4837 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
4838 smashed == EL_SWITCHGATE_SWITCH_DOWN)
4840 ToggleSwitchgateSwitch(x, y + 1);
4842 else if (smashed == EL_LIGHT_SWITCH ||
4843 smashed == EL_LIGHT_SWITCH_ACTIVE)
4845 ToggleLightSwitch(x, y + 1);
4850 TestIfElementSmashesCustomElement(x, y, MV_DOWN);
4853 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4855 CheckElementChangeBySide(x, y + 1, smashed, element,
4856 CE_SWITCHED, CH_SIDE_TOP);
4857 CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
4863 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4868 /* play sound of magic wall / mill */
4870 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
4871 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
4873 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
4874 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
4875 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
4876 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
4881 /* play sound of object that hits the ground */
4882 if (last_line || object_hit)
4883 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4886 inline static void TurnRoundExt(int x, int y)
4898 { 0, 0 }, { 0, 0 }, { 0, 0 },
4903 int left, right, back;
4907 { MV_DOWN, MV_UP, MV_RIGHT },
4908 { MV_UP, MV_DOWN, MV_LEFT },
4910 { MV_LEFT, MV_RIGHT, MV_DOWN },
4914 { MV_RIGHT, MV_LEFT, MV_UP }
4917 int element = Feld[x][y];
4918 int move_pattern = element_info[element].move_pattern;
4920 int old_move_dir = MovDir[x][y];
4921 int left_dir = turn[old_move_dir].left;
4922 int right_dir = turn[old_move_dir].right;
4923 int back_dir = turn[old_move_dir].back;
4925 int left_dx = move_xy[left_dir].dx, left_dy = move_xy[left_dir].dy;
4926 int right_dx = move_xy[right_dir].dx, right_dy = move_xy[right_dir].dy;
4927 int move_dx = move_xy[old_move_dir].dx, move_dy = move_xy[old_move_dir].dy;
4928 int back_dx = move_xy[back_dir].dx, back_dy = move_xy[back_dir].dy;
4930 int left_x = x + left_dx, left_y = y + left_dy;
4931 int right_x = x + right_dx, right_y = y + right_dy;
4932 int move_x = x + move_dx, move_y = y + move_dy;
4936 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4938 TestIfBadThingTouchesOtherBadThing(x, y);
4940 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
4941 MovDir[x][y] = right_dir;
4942 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4943 MovDir[x][y] = left_dir;
4945 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
4947 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
4950 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
4952 TestIfBadThingTouchesOtherBadThing(x, y);
4954 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
4955 MovDir[x][y] = left_dir;
4956 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4957 MovDir[x][y] = right_dir;
4959 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
4961 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
4964 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4966 TestIfBadThingTouchesOtherBadThing(x, y);
4968 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
4969 MovDir[x][y] = left_dir;
4970 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
4971 MovDir[x][y] = right_dir;
4973 if (MovDir[x][y] != old_move_dir)
4976 else if (element == EL_YAMYAM)
4978 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
4979 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
4981 if (can_turn_left && can_turn_right)
4982 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4983 else if (can_turn_left)
4984 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4985 else if (can_turn_right)
4986 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4988 MovDir[x][y] = back_dir;
4990 MovDelay[x][y] = 16 + 16 * RND(3);
4992 else if (element == EL_DARK_YAMYAM)
4994 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4996 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4999 if (can_turn_left && can_turn_right)
5000 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5001 else if (can_turn_left)
5002 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5003 else if (can_turn_right)
5004 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5006 MovDir[x][y] = back_dir;
5008 MovDelay[x][y] = 16 + 16 * RND(3);
5010 else if (element == EL_PACMAN)
5012 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
5013 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
5015 if (can_turn_left && can_turn_right)
5016 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5017 else if (can_turn_left)
5018 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5019 else if (can_turn_right)
5020 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5022 MovDir[x][y] = back_dir;
5024 MovDelay[x][y] = 6 + RND(40);
5026 else if (element == EL_PIG)
5028 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
5029 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
5030 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
5031 boolean should_turn_left, should_turn_right, should_move_on;
5033 int rnd = RND(rnd_value);
5035 should_turn_left = (can_turn_left &&
5037 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
5038 y + back_dy + left_dy)));
5039 should_turn_right = (can_turn_right &&
5041 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
5042 y + back_dy + right_dy)));
5043 should_move_on = (can_move_on &&
5046 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
5047 y + move_dy + left_dy) ||
5048 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
5049 y + move_dy + right_dy)));
5051 if (should_turn_left || should_turn_right || should_move_on)
5053 if (should_turn_left && should_turn_right && should_move_on)
5054 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
5055 rnd < 2 * rnd_value / 3 ? right_dir :
5057 else if (should_turn_left && should_turn_right)
5058 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
5059 else if (should_turn_left && should_move_on)
5060 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
5061 else if (should_turn_right && should_move_on)
5062 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
5063 else if (should_turn_left)
5064 MovDir[x][y] = left_dir;
5065 else if (should_turn_right)
5066 MovDir[x][y] = right_dir;
5067 else if (should_move_on)
5068 MovDir[x][y] = old_move_dir;
5070 else if (can_move_on && rnd > rnd_value / 8)
5071 MovDir[x][y] = old_move_dir;
5072 else if (can_turn_left && can_turn_right)
5073 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
5074 else if (can_turn_left && rnd > rnd_value / 8)
5075 MovDir[x][y] = left_dir;
5076 else if (can_turn_right && rnd > rnd_value/8)
5077 MovDir[x][y] = right_dir;
5079 MovDir[x][y] = back_dir;
5081 xx = x + move_xy[MovDir[x][y]].dx;
5082 yy = y + move_xy[MovDir[x][y]].dy;
5084 if (!IN_LEV_FIELD(xx, yy) ||
5085 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
5086 MovDir[x][y] = old_move_dir;
5090 else if (element == EL_DRAGON)
5092 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
5093 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
5094 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
5096 int rnd = RND(rnd_value);
5098 if (can_move_on && rnd > rnd_value / 8)
5099 MovDir[x][y] = old_move_dir;
5100 else if (can_turn_left && can_turn_right)
5101 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
5102 else if (can_turn_left && rnd > rnd_value / 8)
5103 MovDir[x][y] = left_dir;
5104 else if (can_turn_right && rnd > rnd_value / 8)
5105 MovDir[x][y] = right_dir;
5107 MovDir[x][y] = back_dir;
5109 xx = x + move_xy[MovDir[x][y]].dx;
5110 yy = y + move_xy[MovDir[x][y]].dy;
5112 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
5113 MovDir[x][y] = old_move_dir;
5117 else if (element == EL_MOLE)
5119 boolean can_move_on =
5120 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
5121 IS_AMOEBOID(Feld[move_x][move_y]) ||
5122 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
5125 boolean can_turn_left =
5126 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
5127 IS_AMOEBOID(Feld[left_x][left_y])));
5129 boolean can_turn_right =
5130 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
5131 IS_AMOEBOID(Feld[right_x][right_y])));
5133 if (can_turn_left && can_turn_right)
5134 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
5135 else if (can_turn_left)
5136 MovDir[x][y] = left_dir;
5138 MovDir[x][y] = right_dir;
5141 if (MovDir[x][y] != old_move_dir)
5144 else if (element == EL_BALLOON)
5146 MovDir[x][y] = game.wind_direction;
5149 else if (element == EL_SPRING)
5151 #if USE_NEW_SPRING_BUMPER
5152 if (MovDir[x][y] & MV_HORIZONTAL)
5154 if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
5155 !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
5157 Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
5158 ResetGfxAnimation(move_x, move_y);
5159 DrawLevelField(move_x, move_y);
5161 MovDir[x][y] = back_dir;
5163 else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
5164 SPRING_CAN_ENTER_FIELD(element, x, y + 1))
5165 MovDir[x][y] = MV_NONE;
5168 if (MovDir[x][y] & MV_HORIZONTAL &&
5169 (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
5170 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
5171 MovDir[x][y] = MV_NONE;
5176 else if (element == EL_ROBOT ||
5177 element == EL_SATELLITE ||
5178 element == EL_PENGUIN ||
5179 element == EL_EMC_ANDROID)
5181 int attr_x = -1, attr_y = -1;
5192 for (i = 0; i < MAX_PLAYERS; i++)
5194 struct PlayerInfo *player = &stored_player[i];
5195 int jx = player->jx, jy = player->jy;
5197 if (!player->active)
5201 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
5209 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
5210 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
5211 game.engine_version < VERSION_IDENT(3,1,0,0)))
5217 if (element == EL_PENGUIN)
5220 static int xy[4][2] =
5228 for (i = 0; i < NUM_DIRECTIONS; i++)
5230 int ex = x + xy[i][0];
5231 int ey = y + xy[i][1];
5233 if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
5242 MovDir[x][y] = MV_NONE;
5244 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
5245 else if (attr_x > x)
5246 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
5248 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
5249 else if (attr_y > y)
5250 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
5252 if (element == EL_ROBOT)
5256 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5257 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
5258 Moving2Blocked(x, y, &newx, &newy);
5260 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
5261 MovDelay[x][y] = 8 + 8 * !RND(3);
5263 MovDelay[x][y] = 16;
5265 else if (element == EL_PENGUIN)
5271 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5273 boolean first_horiz = RND(2);
5274 int new_move_dir = MovDir[x][y];
5277 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5278 Moving2Blocked(x, y, &newx, &newy);
5280 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
5284 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5285 Moving2Blocked(x, y, &newx, &newy);
5287 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
5290 MovDir[x][y] = old_move_dir;
5294 else if (element == EL_SATELLITE)
5300 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5302 boolean first_horiz = RND(2);
5303 int new_move_dir = MovDir[x][y];
5306 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5307 Moving2Blocked(x, y, &newx, &newy);
5309 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
5313 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5314 Moving2Blocked(x, y, &newx, &newy);
5316 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
5319 MovDir[x][y] = old_move_dir;
5323 else if (element == EL_EMC_ANDROID)
5325 static int check_pos[16] =
5327 -1, /* 0 => (invalid) */
5328 7, /* 1 => MV_LEFT */
5329 3, /* 2 => MV_RIGHT */
5330 -1, /* 3 => (invalid) */
5332 0, /* 5 => MV_LEFT | MV_UP */
5333 2, /* 6 => MV_RIGHT | MV_UP */
5334 -1, /* 7 => (invalid) */
5335 5, /* 8 => MV_DOWN */
5336 6, /* 9 => MV_LEFT | MV_DOWN */
5337 4, /* 10 => MV_RIGHT | MV_DOWN */
5338 -1, /* 11 => (invalid) */
5339 -1, /* 12 => (invalid) */
5340 -1, /* 13 => (invalid) */
5341 -1, /* 14 => (invalid) */
5342 -1, /* 15 => (invalid) */
5350 { -1, -1, MV_LEFT | MV_UP },
5352 { +1, -1, MV_RIGHT | MV_UP },
5353 { +1, 0, MV_RIGHT },
5354 { +1, +1, MV_RIGHT | MV_DOWN },
5356 { -1, +1, MV_LEFT | MV_DOWN },
5359 int start_pos, check_order;
5360 boolean can_clone = FALSE;
5363 /* check if there is any free field around current position */
5364 for (i = 0; i < 8; i++)
5366 int newx = x + check_xy[i].dx;
5367 int newy = y + check_xy[i].dy;
5369 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5377 if (can_clone) /* randomly find an element to clone */
5381 start_pos = check_pos[RND(8)];
5382 check_order = (RND(2) ? -1 : +1);
5384 for (i = 0; i < 8; i++)
5386 int pos_raw = start_pos + i * check_order;
5387 int pos = (pos_raw + 8) % 8;
5388 int newx = x + check_xy[pos].dx;
5389 int newy = y + check_xy[pos].dy;
5391 if (ANDROID_CAN_CLONE_FIELD(newx, newy))
5393 element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
5394 element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
5396 Store[x][y] = Feld[newx][newy];
5405 if (can_clone) /* randomly find a direction to move */
5409 start_pos = check_pos[RND(8)];
5410 check_order = (RND(2) ? -1 : +1);
5412 for (i = 0; i < 8; i++)
5414 int pos_raw = start_pos + i * check_order;
5415 int pos = (pos_raw + 8) % 8;
5416 int newx = x + check_xy[pos].dx;
5417 int newy = y + check_xy[pos].dy;
5418 int new_move_dir = check_xy[pos].dir;
5420 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5422 MovDir[x][y] = new_move_dir;
5423 MovDelay[x][y] = level.android_clone_time * 8 + 1;
5432 if (can_clone) /* cloning and moving successful */
5435 /* cannot clone -- try to move towards player */
5437 start_pos = check_pos[MovDir[x][y] & 0x0f];
5438 check_order = (RND(2) ? -1 : +1);
5440 for (i = 0; i < 3; i++)
5442 /* first check start_pos, then previous/next or (next/previous) pos */
5443 int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
5444 int pos = (pos_raw + 8) % 8;
5445 int newx = x + check_xy[pos].dx;
5446 int newy = y + check_xy[pos].dy;
5447 int new_move_dir = check_xy[pos].dir;
5449 if (IS_PLAYER(newx, newy))
5452 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
5454 MovDir[x][y] = new_move_dir;
5455 MovDelay[x][y] = level.android_move_time * 8 + 1;
5462 else if (move_pattern == MV_TURNING_LEFT ||
5463 move_pattern == MV_TURNING_RIGHT ||
5464 move_pattern == MV_TURNING_LEFT_RIGHT ||
5465 move_pattern == MV_TURNING_RIGHT_LEFT ||
5466 move_pattern == MV_TURNING_RANDOM ||
5467 move_pattern == MV_ALL_DIRECTIONS)
5469 boolean can_turn_left =
5470 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
5471 boolean can_turn_right =
5472 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
5474 if (element_info[element].move_stepsize == 0) /* "not moving" */
5477 if (move_pattern == MV_TURNING_LEFT)
5478 MovDir[x][y] = left_dir;
5479 else if (move_pattern == MV_TURNING_RIGHT)
5480 MovDir[x][y] = right_dir;
5481 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
5482 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
5483 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
5484 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
5485 else if (move_pattern == MV_TURNING_RANDOM)
5486 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
5487 can_turn_right && !can_turn_left ? right_dir :
5488 RND(2) ? left_dir : right_dir);
5489 else if (can_turn_left && can_turn_right)
5490 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5491 else if (can_turn_left)
5492 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5493 else if (can_turn_right)
5494 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5496 MovDir[x][y] = back_dir;
5498 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5500 else if (move_pattern == MV_HORIZONTAL ||
5501 move_pattern == MV_VERTICAL)
5503 if (move_pattern & old_move_dir)
5504 MovDir[x][y] = back_dir;
5505 else if (move_pattern == MV_HORIZONTAL)
5506 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
5507 else if (move_pattern == MV_VERTICAL)
5508 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
5510 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5512 else if (move_pattern & MV_ANY_DIRECTION)
5514 MovDir[x][y] = move_pattern;
5515 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5517 else if (move_pattern & MV_WIND_DIRECTION)
5519 MovDir[x][y] = game.wind_direction;
5520 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5522 else if (move_pattern == MV_ALONG_LEFT_SIDE)
5524 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
5525 MovDir[x][y] = left_dir;
5526 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5527 MovDir[x][y] = right_dir;
5529 if (MovDir[x][y] != old_move_dir)
5530 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5532 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
5534 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
5535 MovDir[x][y] = right_dir;
5536 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5537 MovDir[x][y] = left_dir;
5539 if (MovDir[x][y] != old_move_dir)
5540 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5542 else if (move_pattern == MV_TOWARDS_PLAYER ||
5543 move_pattern == MV_AWAY_FROM_PLAYER)
5545 int attr_x = -1, attr_y = -1;
5547 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
5558 for (i = 0; i < MAX_PLAYERS; i++)
5560 struct PlayerInfo *player = &stored_player[i];
5561 int jx = player->jx, jy = player->jy;
5563 if (!player->active)
5567 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
5575 MovDir[x][y] = MV_NONE;
5577 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
5578 else if (attr_x > x)
5579 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
5581 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
5582 else if (attr_y > y)
5583 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
5585 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5587 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5589 boolean first_horiz = RND(2);
5590 int new_move_dir = MovDir[x][y];
5592 if (element_info[element].move_stepsize == 0) /* "not moving" */
5594 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
5595 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5601 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5602 Moving2Blocked(x, y, &newx, &newy);
5604 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5608 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5609 Moving2Blocked(x, y, &newx, &newy);
5611 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5614 MovDir[x][y] = old_move_dir;
5617 else if (move_pattern == MV_WHEN_PUSHED ||
5618 move_pattern == MV_WHEN_DROPPED)
5620 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5621 MovDir[x][y] = MV_NONE;
5625 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
5627 static int test_xy[7][2] =
5637 static int test_dir[7] =
5647 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
5648 int move_preference = -1000000; /* start with very low preference */
5649 int new_move_dir = MV_NONE;
5650 int start_test = RND(4);
5653 for (i = 0; i < NUM_DIRECTIONS; i++)
5655 int move_dir = test_dir[start_test + i];
5656 int move_dir_preference;
5658 xx = x + test_xy[start_test + i][0];
5659 yy = y + test_xy[start_test + i][1];
5661 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
5662 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
5664 new_move_dir = move_dir;
5669 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
5672 move_dir_preference = -1 * RunnerVisit[xx][yy];
5673 if (hunter_mode && PlayerVisit[xx][yy] > 0)
5674 move_dir_preference = PlayerVisit[xx][yy];
5676 if (move_dir_preference > move_preference)
5678 /* prefer field that has not been visited for the longest time */
5679 move_preference = move_dir_preference;
5680 new_move_dir = move_dir;
5682 else if (move_dir_preference == move_preference &&
5683 move_dir == old_move_dir)
5685 /* prefer last direction when all directions are preferred equally */
5686 move_preference = move_dir_preference;
5687 new_move_dir = move_dir;
5691 MovDir[x][y] = new_move_dir;
5692 if (old_move_dir != new_move_dir)
5693 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5697 static void TurnRound(int x, int y)
5699 int direction = MovDir[x][y];
5701 int element, graphic;
5706 GfxDir[x][y] = MovDir[x][y];
5708 if (direction != MovDir[x][y])
5712 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
5715 element = Feld[x][y];
5716 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5718 if (graphic_info[graphic].anim_global_sync)
5719 GfxFrame[x][y] = FrameCounter;
5720 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5721 GfxFrame[x][y] = CustomValue[x][y];
5722 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5723 GfxFrame[x][y] = element_info[element].collect_score;
5727 static boolean JustBeingPushed(int x, int y)
5731 for (i = 0; i < MAX_PLAYERS; i++)
5733 struct PlayerInfo *player = &stored_player[i];
5735 if (player->active && player->is_pushing && player->MovPos)
5737 int next_jx = player->jx + (player->jx - player->last_jx);
5738 int next_jy = player->jy + (player->jy - player->last_jy);
5740 if (x == next_jx && y == next_jy)
5748 void StartMoving(int x, int y)
5750 boolean started_moving = FALSE; /* some elements can fall _and_ move */
5751 int element = Feld[x][y];
5756 if (MovDelay[x][y] == 0)
5757 GfxAction[x][y] = ACTION_DEFAULT;
5759 if (CAN_FALL(element) && y < lev_fieldy - 1)
5761 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
5762 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
5763 if (JustBeingPushed(x, y))
5766 if (element == EL_QUICKSAND_FULL)
5768 if (IS_FREE(x, y + 1))
5770 InitMovingField(x, y, MV_DOWN);
5771 started_moving = TRUE;
5773 Feld[x][y] = EL_QUICKSAND_EMPTYING;
5774 Store[x][y] = EL_ROCK;
5776 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
5778 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5780 if (!MovDelay[x][y])
5781 MovDelay[x][y] = TILEY + 1;
5790 Feld[x][y] = EL_QUICKSAND_EMPTY;
5791 Feld[x][y + 1] = EL_QUICKSAND_FULL;
5792 Store[x][y + 1] = Store[x][y];
5795 PlayLevelSoundAction(x, y, ACTION_FILLING);
5798 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
5799 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5801 InitMovingField(x, y, MV_DOWN);
5802 started_moving = TRUE;
5804 Feld[x][y] = EL_QUICKSAND_FILLING;
5805 Store[x][y] = element;
5807 PlayLevelSoundAction(x, y, ACTION_FILLING);
5809 else if (element == EL_MAGIC_WALL_FULL)
5811 if (IS_FREE(x, y + 1))
5813 InitMovingField(x, y, MV_DOWN);
5814 started_moving = TRUE;
5816 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
5817 Store[x][y] = EL_CHANGED(Store[x][y]);
5819 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
5821 if (!MovDelay[x][y])
5822 MovDelay[x][y] = TILEY/4 + 1;
5831 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
5832 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
5833 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
5837 else if (element == EL_BD_MAGIC_WALL_FULL)
5839 if (IS_FREE(x, y + 1))
5841 InitMovingField(x, y, MV_DOWN);
5842 started_moving = TRUE;
5844 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
5845 Store[x][y] = EL_CHANGED2(Store[x][y]);
5847 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
5849 if (!MovDelay[x][y])
5850 MovDelay[x][y] = TILEY/4 + 1;
5859 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
5860 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
5861 Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
5865 else if (CAN_PASS_MAGIC_WALL(element) &&
5866 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
5867 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
5869 InitMovingField(x, y, MV_DOWN);
5870 started_moving = TRUE;
5873 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
5874 EL_BD_MAGIC_WALL_FILLING);
5875 Store[x][y] = element;
5877 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
5879 SplashAcid(x, y + 1);
5881 InitMovingField(x, y, MV_DOWN);
5882 started_moving = TRUE;
5884 Store[x][y] = EL_ACID;
5886 else if ((game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5887 CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
5889 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
5890 CAN_FALL(element) && WasJustFalling[x][y] &&
5891 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
5893 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
5894 CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
5895 (Feld[x][y + 1] == EL_BLOCKED)))
5897 /* this is needed for a special case not covered by calling "Impact()"
5898 from "ContinueMoving()": if an element moves to a tile directly below
5899 another element which was just falling on that tile (which was empty
5900 in the previous frame), the falling element above would just stop
5901 instead of smashing the element below (in previous version, the above
5902 element was just checked for "moving" instead of "falling", resulting
5903 in incorrect smashes caused by horizontal movement of the above
5904 element; also, the case of the player being the element to smash was
5905 simply not covered here... :-/ ) */
5907 CheckCollision[x][y] = 0;
5911 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
5913 if (MovDir[x][y] == MV_NONE)
5915 InitMovingField(x, y, MV_DOWN);
5916 started_moving = TRUE;
5919 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
5921 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
5922 MovDir[x][y] = MV_DOWN;
5924 InitMovingField(x, y, MV_DOWN);
5925 started_moving = TRUE;
5927 else if (element == EL_AMOEBA_DROP)
5929 Feld[x][y] = EL_AMOEBA_GROWING;
5930 Store[x][y] = EL_AMOEBA_WET;
5932 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
5933 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
5934 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
5935 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
5937 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
5938 (IS_FREE(x - 1, y + 1) ||
5939 Feld[x - 1][y + 1] == EL_ACID));
5940 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
5941 (IS_FREE(x + 1, y + 1) ||
5942 Feld[x + 1][y + 1] == EL_ACID));
5943 boolean can_fall_any = (can_fall_left || can_fall_right);
5944 boolean can_fall_both = (can_fall_left && can_fall_right);
5945 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
5947 #if USE_NEW_ALL_SLIPPERY
5948 if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
5950 if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
5951 can_fall_right = FALSE;
5952 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
5953 can_fall_left = FALSE;
5954 else if (slippery_type == SLIPPERY_ONLY_LEFT)
5955 can_fall_right = FALSE;
5956 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
5957 can_fall_left = FALSE;
5959 can_fall_any = (can_fall_left || can_fall_right);
5960 can_fall_both = FALSE;
5963 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
5965 if (slippery_type == SLIPPERY_ONLY_LEFT)
5966 can_fall_right = FALSE;
5967 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
5968 can_fall_left = FALSE;
5969 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
5970 can_fall_right = FALSE;
5971 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
5972 can_fall_left = FALSE;
5974 can_fall_any = (can_fall_left || can_fall_right);
5975 can_fall_both = (can_fall_left && can_fall_right);
5979 #if USE_NEW_ALL_SLIPPERY
5981 #if USE_NEW_SP_SLIPPERY
5982 /* !!! better use the same properties as for custom elements here !!! */
5983 else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
5984 can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
5986 can_fall_right = FALSE; /* slip down on left side */
5987 can_fall_both = FALSE;
5992 #if USE_NEW_ALL_SLIPPERY
5995 if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
5996 can_fall_right = FALSE; /* slip down on left side */
5998 can_fall_left = !(can_fall_right = RND(2));
6000 can_fall_both = FALSE;
6005 if (game.emulation == EMU_BOULDERDASH ||
6006 element == EL_BD_ROCK || element == EL_BD_DIAMOND)
6007 can_fall_right = FALSE; /* slip down on left side */
6009 can_fall_left = !(can_fall_right = RND(2));
6011 can_fall_both = FALSE;
6017 /* if not determined otherwise, prefer left side for slipping down */
6018 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
6019 started_moving = TRUE;
6023 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
6025 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
6028 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
6029 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
6030 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
6031 int belt_dir = game.belt_dir[belt_nr];
6033 if ((belt_dir == MV_LEFT && left_is_free) ||
6034 (belt_dir == MV_RIGHT && right_is_free))
6036 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
6038 InitMovingField(x, y, belt_dir);
6039 started_moving = TRUE;
6041 Pushed[x][y] = TRUE;
6042 Pushed[nextx][y] = TRUE;
6044 GfxAction[x][y] = ACTION_DEFAULT;
6048 MovDir[x][y] = 0; /* if element was moving, stop it */
6053 /* not "else if" because of elements that can fall and move (EL_SPRING) */
6055 if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
6057 if (CAN_MOVE(element) && !started_moving)
6060 int move_pattern = element_info[element].move_pattern;
6065 if (MovDir[x][y] == MV_NONE)
6067 printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
6068 x, y, element, element_info[element].token_name);
6069 printf("StartMoving(): This should never happen!\n");
6074 Moving2Blocked(x, y, &newx, &newy);
6076 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
6079 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
6080 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6082 WasJustMoving[x][y] = 0;
6083 CheckCollision[x][y] = 0;
6085 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
6087 if (Feld[x][y] != element) /* element has changed */
6091 if (!MovDelay[x][y]) /* start new movement phase */
6093 /* all objects that can change their move direction after each step
6094 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
6096 if (element != EL_YAMYAM &&
6097 element != EL_DARK_YAMYAM &&
6098 element != EL_PACMAN &&
6099 !(move_pattern & MV_ANY_DIRECTION) &&
6100 move_pattern != MV_TURNING_LEFT &&
6101 move_pattern != MV_TURNING_RIGHT &&
6102 move_pattern != MV_TURNING_LEFT_RIGHT &&
6103 move_pattern != MV_TURNING_RIGHT_LEFT &&
6104 move_pattern != MV_TURNING_RANDOM)
6108 if (MovDelay[x][y] && (element == EL_BUG ||
6109 element == EL_SPACESHIP ||
6110 element == EL_SP_SNIKSNAK ||
6111 element == EL_SP_ELECTRON ||
6112 element == EL_MOLE))
6113 DrawLevelField(x, y);
6117 if (MovDelay[x][y]) /* wait some time before next movement */
6121 if (element == EL_ROBOT ||
6122 element == EL_YAMYAM ||
6123 element == EL_DARK_YAMYAM)
6125 DrawLevelElementAnimationIfNeeded(x, y, element);
6126 PlayLevelSoundAction(x, y, ACTION_WAITING);
6128 else if (element == EL_SP_ELECTRON)
6129 DrawLevelElementAnimationIfNeeded(x, y, element);
6130 else if (element == EL_DRAGON)
6133 int dir = MovDir[x][y];
6134 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
6135 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
6136 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
6137 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
6138 dir == MV_UP ? IMG_FLAMES_1_UP :
6139 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
6140 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
6142 GfxAction[x][y] = ACTION_ATTACKING;
6144 if (IS_PLAYER(x, y))
6145 DrawPlayerField(x, y);
6147 DrawLevelField(x, y);
6149 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
6151 for (i = 1; i <= 3; i++)
6153 int xx = x + i * dx;
6154 int yy = y + i * dy;
6155 int sx = SCREENX(xx);
6156 int sy = SCREENY(yy);
6157 int flame_graphic = graphic + (i - 1);
6159 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
6164 int flamed = MovingOrBlocked2Element(xx, yy);
6168 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
6170 else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
6171 RemoveMovingField(xx, yy);
6173 RemoveField(xx, yy);
6175 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
6178 RemoveMovingField(xx, yy);
6181 ChangeDelay[xx][yy] = 0;
6183 Feld[xx][yy] = EL_FLAMES;
6185 if (IN_SCR_FIELD(sx, sy))
6187 DrawLevelFieldCrumbledSand(xx, yy);
6188 DrawGraphic(sx, sy, flame_graphic, frame);
6193 if (Feld[xx][yy] == EL_FLAMES)
6194 Feld[xx][yy] = EL_EMPTY;
6195 DrawLevelField(xx, yy);
6200 if (MovDelay[x][y]) /* element still has to wait some time */
6202 PlayLevelSoundAction(x, y, ACTION_WAITING);
6208 /* now make next step */
6210 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
6212 if (DONT_COLLIDE_WITH(element) &&
6213 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
6214 !PLAYER_ENEMY_PROTECTED(newx, newy))
6216 TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
6221 else if (CAN_MOVE_INTO_ACID(element) &&
6222 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
6223 !IS_MV_DIAGONAL(MovDir[x][y]) &&
6224 (MovDir[x][y] == MV_DOWN ||
6225 game.engine_version >= VERSION_IDENT(3,1,0,0)))
6227 SplashAcid(newx, newy);
6228 Store[x][y] = EL_ACID;
6230 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
6232 if (Feld[newx][newy] == EL_EXIT_OPEN)
6235 DrawLevelField(x, y);
6237 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
6238 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
6239 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
6241 local_player->friends_still_needed--;
6242 if (!local_player->friends_still_needed &&
6243 !local_player->GameOver && AllPlayersGone)
6244 local_player->LevelSolved = local_player->GameOver = TRUE;
6248 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
6250 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
6251 DrawLevelField(newx, newy);
6253 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
6255 else if (!IS_FREE(newx, newy))
6257 GfxAction[x][y] = ACTION_WAITING;
6259 if (IS_PLAYER(x, y))
6260 DrawPlayerField(x, y);
6262 DrawLevelField(x, y);
6267 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
6269 if (IS_FOOD_PIG(Feld[newx][newy]))
6271 if (IS_MOVING(newx, newy))
6272 RemoveMovingField(newx, newy);
6275 Feld[newx][newy] = EL_EMPTY;
6276 DrawLevelField(newx, newy);
6279 PlayLevelSound(x, y, SND_PIG_DIGGING);
6281 else if (!IS_FREE(newx, newy))
6283 if (IS_PLAYER(x, y))
6284 DrawPlayerField(x, y);
6286 DrawLevelField(x, y);
6291 else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
6293 if (Store[x][y] != EL_EMPTY)
6295 boolean can_clone = FALSE;
6298 /* check if element to clone is still there */
6299 for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
6301 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
6309 /* cannot clone or target field not free anymore -- do not clone */
6310 if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6311 Store[x][y] = EL_EMPTY;
6314 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6316 if (IS_MV_DIAGONAL(MovDir[x][y]))
6318 int diagonal_move_dir = MovDir[x][y];
6319 int stored = Store[x][y];
6320 int change_delay = 8;
6323 /* android is moving diagonally */
6325 CreateField(x, y, EL_DIAGONAL_SHRINKING);
6327 Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
6328 GfxElement[x][y] = EL_EMC_ANDROID;
6329 GfxAction[x][y] = ACTION_SHRINKING;
6330 GfxDir[x][y] = diagonal_move_dir;
6331 ChangeDelay[x][y] = change_delay;
6333 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
6336 DrawLevelGraphicAnimation(x, y, graphic);
6337 PlayLevelSoundAction(x, y, ACTION_SHRINKING);
6339 if (Feld[newx][newy] == EL_ACID)
6341 SplashAcid(newx, newy);
6346 CreateField(newx, newy, EL_DIAGONAL_GROWING);
6348 Store[newx][newy] = EL_EMC_ANDROID;
6349 GfxElement[newx][newy] = EL_EMC_ANDROID;
6350 GfxAction[newx][newy] = ACTION_GROWING;
6351 GfxDir[newx][newy] = diagonal_move_dir;
6352 ChangeDelay[newx][newy] = change_delay;
6354 graphic = el_act_dir2img(GfxElement[newx][newy],
6355 GfxAction[newx][newy], GfxDir[newx][newy]);
6357 DrawLevelGraphicAnimation(newx, newy, graphic);
6358 PlayLevelSoundAction(newx, newy, ACTION_GROWING);
6364 Feld[newx][newy] = EL_EMPTY;
6365 DrawLevelField(newx, newy);
6367 PlayLevelSoundAction(x, y, ACTION_DIGGING);
6370 else if (!IS_FREE(newx, newy))
6373 if (IS_PLAYER(x, y))
6374 DrawPlayerField(x, y);
6376 DrawLevelField(x, y);
6382 else if (IS_CUSTOM_ELEMENT(element) &&
6383 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6385 int new_element = Feld[newx][newy];
6387 if (!IS_FREE(newx, newy))
6389 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
6390 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
6393 /* no element can dig solid indestructible elements */
6394 if (IS_INDESTRUCTIBLE(new_element) &&
6395 !IS_DIGGABLE(new_element) &&
6396 !IS_COLLECTIBLE(new_element))
6399 if (AmoebaNr[newx][newy] &&
6400 (new_element == EL_AMOEBA_FULL ||
6401 new_element == EL_BD_AMOEBA ||
6402 new_element == EL_AMOEBA_GROWING))
6404 AmoebaCnt[AmoebaNr[newx][newy]]--;
6405 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6408 if (IS_MOVING(newx, newy))
6409 RemoveMovingField(newx, newy);
6412 RemoveField(newx, newy);
6413 DrawLevelField(newx, newy);
6416 /* if digged element was about to explode, prevent the explosion */
6417 ExplodeField[newx][newy] = EX_TYPE_NONE;
6419 PlayLevelSoundAction(x, y, action);
6422 Store[newx][newy] = EL_EMPTY;
6424 /* this makes it possible to leave the removed element again */
6425 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
6426 Store[newx][newy] = new_element;
6428 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
6430 int move_leave_element = element_info[element].move_leave_element;
6432 /* this makes it possible to leave the removed element again */
6433 Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
6434 new_element : move_leave_element);
6438 if (move_pattern & MV_MAZE_RUNNER_STYLE)
6440 RunnerVisit[x][y] = FrameCounter;
6441 PlayerVisit[x][y] /= 8; /* expire player visit path */
6444 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
6446 if (!IS_FREE(newx, newy))
6448 if (IS_PLAYER(x, y))
6449 DrawPlayerField(x, y);
6451 DrawLevelField(x, y);
6457 boolean wanna_flame = !RND(10);
6458 int dx = newx - x, dy = newy - y;
6459 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
6460 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
6461 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
6462 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
6463 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
6464 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
6467 IS_CLASSIC_ENEMY(element1) ||
6468 IS_CLASSIC_ENEMY(element2)) &&
6469 element1 != EL_DRAGON && element2 != EL_DRAGON &&
6470 element1 != EL_FLAMES && element2 != EL_FLAMES)
6472 ResetGfxAnimation(x, y);
6473 GfxAction[x][y] = ACTION_ATTACKING;
6475 if (IS_PLAYER(x, y))
6476 DrawPlayerField(x, y);
6478 DrawLevelField(x, y);
6480 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
6482 MovDelay[x][y] = 50;
6486 RemoveField(newx, newy);
6488 Feld[newx][newy] = EL_FLAMES;
6489 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
6492 RemoveField(newx1, newy1);
6494 Feld[newx1][newy1] = EL_FLAMES;
6496 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
6499 RemoveField(newx2, newy2);
6501 Feld[newx2][newy2] = EL_FLAMES;
6508 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6509 Feld[newx][newy] == EL_DIAMOND)
6511 if (IS_MOVING(newx, newy))
6512 RemoveMovingField(newx, newy);
6515 Feld[newx][newy] = EL_EMPTY;
6516 DrawLevelField(newx, newy);
6519 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
6521 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6522 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
6524 if (AmoebaNr[newx][newy])
6526 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6527 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
6528 Feld[newx][newy] == EL_BD_AMOEBA)
6529 AmoebaCnt[AmoebaNr[newx][newy]]--;
6534 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
6536 RemoveMovingField(newx, newy);
6539 if (IS_MOVING(newx, newy))
6541 RemoveMovingField(newx, newy);
6546 Feld[newx][newy] = EL_EMPTY;
6547 DrawLevelField(newx, newy);
6550 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
6552 else if ((element == EL_PACMAN || element == EL_MOLE)
6553 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
6555 if (AmoebaNr[newx][newy])
6557 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6558 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
6559 Feld[newx][newy] == EL_BD_AMOEBA)
6560 AmoebaCnt[AmoebaNr[newx][newy]]--;
6563 if (element == EL_MOLE)
6565 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
6566 PlayLevelSound(x, y, SND_MOLE_DIGGING);
6568 ResetGfxAnimation(x, y);
6569 GfxAction[x][y] = ACTION_DIGGING;
6570 DrawLevelField(x, y);
6572 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
6574 return; /* wait for shrinking amoeba */
6576 else /* element == EL_PACMAN */
6578 Feld[newx][newy] = EL_EMPTY;
6579 DrawLevelField(newx, newy);
6580 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
6583 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
6584 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
6585 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
6587 /* wait for shrinking amoeba to completely disappear */
6590 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
6592 /* object was running against a wall */
6597 /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
6598 if (move_pattern & MV_ANY_DIRECTION &&
6599 move_pattern == MovDir[x][y])
6601 int blocking_element =
6602 (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
6604 CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
6607 element = Feld[x][y]; /* element might have changed */
6611 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
6612 DrawLevelElementAnimation(x, y, element);
6614 if (DONT_TOUCH(element))
6615 TestIfBadThingTouchesPlayer(x, y);
6620 InitMovingField(x, y, MovDir[x][y]);
6622 PlayLevelSoundAction(x, y, ACTION_MOVING);
6626 ContinueMoving(x, y);
6629 void ContinueMoving(int x, int y)
6631 int element = Feld[x][y];
6632 struct ElementInfo *ei = &element_info[element];
6633 int direction = MovDir[x][y];
6634 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
6635 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
6636 int newx = x + dx, newy = y + dy;
6637 int stored = Store[x][y];
6638 int stored_new = Store[newx][newy];
6639 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
6640 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
6641 boolean last_line = (newy == lev_fieldy - 1);
6643 MovPos[x][y] += getElementMoveStepsize(x, y);
6645 if (pushed_by_player) /* special case: moving object pushed by player */
6646 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
6648 if (ABS(MovPos[x][y]) < TILEX)
6650 DrawLevelField(x, y);
6652 return; /* element is still moving */
6655 /* element reached destination field */
6657 Feld[x][y] = EL_EMPTY;
6658 Feld[newx][newy] = element;
6659 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
6661 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
6663 element = Feld[newx][newy] = EL_ACID;
6665 else if (element == EL_MOLE)
6667 Feld[x][y] = EL_SAND;
6669 DrawLevelFieldCrumbledSandNeighbours(x, y);
6671 else if (element == EL_QUICKSAND_FILLING)
6673 element = Feld[newx][newy] = get_next_element(element);
6674 Store[newx][newy] = Store[x][y];
6676 else if (element == EL_QUICKSAND_EMPTYING)
6678 Feld[x][y] = get_next_element(element);
6679 element = Feld[newx][newy] = Store[x][y];
6681 else if (element == EL_MAGIC_WALL_FILLING)
6683 element = Feld[newx][newy] = get_next_element(element);
6684 if (!game.magic_wall_active)
6685 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
6686 Store[newx][newy] = Store[x][y];
6688 else if (element == EL_MAGIC_WALL_EMPTYING)
6690 Feld[x][y] = get_next_element(element);
6691 if (!game.magic_wall_active)
6692 Feld[x][y] = EL_MAGIC_WALL_DEAD;
6693 element = Feld[newx][newy] = Store[x][y];
6695 #if USE_NEW_CUSTOM_VALUE
6696 InitField(newx, newy, FALSE);
6699 else if (element == EL_BD_MAGIC_WALL_FILLING)
6701 element = Feld[newx][newy] = get_next_element(element);
6702 if (!game.magic_wall_active)
6703 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
6704 Store[newx][newy] = Store[x][y];
6706 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
6708 Feld[x][y] = get_next_element(element);
6709 if (!game.magic_wall_active)
6710 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
6711 element = Feld[newx][newy] = Store[x][y];
6713 #if USE_NEW_CUSTOM_VALUE
6714 InitField(newx, newy, FALSE);
6717 else if (element == EL_AMOEBA_DROPPING)
6719 Feld[x][y] = get_next_element(element);
6720 element = Feld[newx][newy] = Store[x][y];
6722 else if (element == EL_SOKOBAN_OBJECT)
6725 Feld[x][y] = Back[x][y];
6727 if (Back[newx][newy])
6728 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
6730 Back[x][y] = Back[newx][newy] = 0;
6733 Store[x][y] = EL_EMPTY;
6738 MovDelay[newx][newy] = 0;
6741 if (CAN_CHANGE_OR_HAS_ACTION(element))
6743 if (CAN_CHANGE(element))
6746 /* copy element change control values to new field */
6747 ChangeDelay[newx][newy] = ChangeDelay[x][y];
6748 ChangePage[newx][newy] = ChangePage[x][y];
6749 ChangeCount[newx][newy] = ChangeCount[x][y];
6750 ChangeEvent[newx][newy] = ChangeEvent[x][y];
6753 #if USE_NEW_CUSTOM_VALUE
6754 CustomValue[newx][newy] = CustomValue[x][y];
6760 #if USE_NEW_CUSTOM_VALUE
6761 CustomValue[newx][newy] = CustomValue[x][y];
6765 ChangeDelay[x][y] = 0;
6766 ChangePage[x][y] = -1;
6767 ChangeCount[x][y] = 0;
6768 ChangeEvent[x][y] = -1;
6770 #if USE_NEW_CUSTOM_VALUE
6771 CustomValue[x][y] = 0;
6774 /* copy animation control values to new field */
6775 GfxFrame[newx][newy] = GfxFrame[x][y];
6776 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
6777 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
6778 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
6780 Pushed[x][y] = Pushed[newx][newy] = FALSE;
6782 /* some elements can leave other elements behind after moving */
6784 if (ei->move_leave_element != EL_EMPTY &&
6785 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
6786 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
6788 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
6789 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
6790 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
6793 int move_leave_element = ei->move_leave_element;
6797 /* this makes it possible to leave the removed element again */
6798 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
6799 move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
6801 /* this makes it possible to leave the removed element again */
6802 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
6803 move_leave_element = stored;
6806 /* this makes it possible to leave the removed element again */
6807 if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
6808 ei->move_leave_element == EL_TRIGGER_ELEMENT)
6809 move_leave_element = stored;
6812 Feld[x][y] = move_leave_element;
6814 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
6815 MovDir[x][y] = direction;
6817 InitField(x, y, FALSE);
6819 if (GFX_CRUMBLED(Feld[x][y]))
6820 DrawLevelFieldCrumbledSandNeighbours(x, y);
6822 if (ELEM_IS_PLAYER(move_leave_element))
6823 RelocatePlayer(x, y, move_leave_element);
6826 /* do this after checking for left-behind element */
6827 ResetGfxAnimation(x, y); /* reset animation values for old field */
6829 if (!CAN_MOVE(element) ||
6830 (CAN_FALL(element) && direction == MV_DOWN &&
6831 (element == EL_SPRING ||
6832 element_info[element].move_pattern == MV_WHEN_PUSHED ||
6833 element_info[element].move_pattern == MV_WHEN_DROPPED)))
6834 GfxDir[x][y] = MovDir[newx][newy] = 0;
6836 DrawLevelField(x, y);
6837 DrawLevelField(newx, newy);
6839 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
6841 /* prevent pushed element from moving on in pushed direction */
6842 if (pushed_by_player && CAN_MOVE(element) &&
6843 element_info[element].move_pattern & MV_ANY_DIRECTION &&
6844 !(element_info[element].move_pattern & direction))
6845 TurnRound(newx, newy);
6847 /* prevent elements on conveyor belt from moving on in last direction */
6848 if (pushed_by_conveyor && CAN_FALL(element) &&
6849 direction & MV_HORIZONTAL)
6850 MovDir[newx][newy] = 0;
6852 if (!pushed_by_player)
6854 int nextx = newx + dx, nexty = newy + dy;
6855 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
6857 WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
6859 if (CAN_FALL(element) && direction == MV_DOWN)
6860 WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
6862 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
6863 CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
6866 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
6868 TestIfBadThingTouchesPlayer(newx, newy);
6869 TestIfBadThingTouchesFriend(newx, newy);
6871 if (!IS_CUSTOM_ELEMENT(element))
6872 TestIfBadThingTouchesOtherBadThing(newx, newy);
6874 else if (element == EL_PENGUIN)
6875 TestIfFriendTouchesBadThing(newx, newy);
6877 /* give the player one last chance (one more frame) to move away */
6878 if (CAN_FALL(element) && direction == MV_DOWN &&
6879 (last_line || (!IS_FREE(x, newy + 1) &&
6880 (!IS_PLAYER(x, newy + 1) ||
6881 game.engine_version < VERSION_IDENT(3,1,1,0)))))
6884 if (pushed_by_player && !game.use_change_when_pushing_bug)
6886 int push_side = MV_DIR_OPPOSITE(direction);
6887 struct PlayerInfo *player = PLAYERINFO(x, y);
6889 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
6890 player->index_bit, push_side);
6891 CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
6892 player->index_bit, push_side);
6895 if (element == EL_EMC_ANDROID && pushed_by_player) /* make another move */
6896 MovDelay[newx][newy] = 1;
6898 CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
6900 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
6903 if (ChangePage[newx][newy] != -1) /* delayed change */
6905 int page = ChangePage[newx][newy];
6906 struct ElementChangeInfo *change = &ei->change_page[page];
6908 ChangePage[newx][newy] = -1;
6910 if (change->can_change)
6912 if (ChangeElement(newx, newy, element, page))
6914 if (change->post_change_function)
6915 change->post_change_function(newx, newy);
6919 if (change->has_action)
6920 ExecuteCustomElementAction(newx, newy, element, page);
6924 TestIfElementHitsCustomElement(newx, newy, direction);
6925 TestIfPlayerTouchesCustomElement(newx, newy);
6926 TestIfElementTouchesCustomElement(newx, newy);
6929 if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
6930 IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
6931 CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
6932 MV_DIR_OPPOSITE(direction));
6936 int AmoebeNachbarNr(int ax, int ay)
6939 int element = Feld[ax][ay];
6941 static int xy[4][2] =
6949 for (i = 0; i < NUM_DIRECTIONS; i++)
6951 int x = ax + xy[i][0];
6952 int y = ay + xy[i][1];
6954 if (!IN_LEV_FIELD(x, y))
6957 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
6958 group_nr = AmoebaNr[x][y];
6964 void AmoebenVereinigen(int ax, int ay)
6966 int i, x, y, xx, yy;
6967 int new_group_nr = AmoebaNr[ax][ay];
6968 static int xy[4][2] =
6976 if (new_group_nr == 0)
6979 for (i = 0; i < NUM_DIRECTIONS; i++)
6984 if (!IN_LEV_FIELD(x, y))
6987 if ((Feld[x][y] == EL_AMOEBA_FULL ||
6988 Feld[x][y] == EL_BD_AMOEBA ||
6989 Feld[x][y] == EL_AMOEBA_DEAD) &&
6990 AmoebaNr[x][y] != new_group_nr)
6992 int old_group_nr = AmoebaNr[x][y];
6994 if (old_group_nr == 0)
6997 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
6998 AmoebaCnt[old_group_nr] = 0;
6999 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
7000 AmoebaCnt2[old_group_nr] = 0;
7003 SCAN_PLAYFIELD(xx, yy)
7005 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
7008 if (AmoebaNr[xx][yy] == old_group_nr)
7009 AmoebaNr[xx][yy] = new_group_nr;
7015 void AmoebeUmwandeln(int ax, int ay)
7019 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
7021 int group_nr = AmoebaNr[ax][ay];
7026 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
7027 printf("AmoebeUmwandeln(): This should never happen!\n");
7033 SCAN_PLAYFIELD(x, y)
7035 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7038 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
7041 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
7045 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
7046 SND_AMOEBA_TURNING_TO_GEM :
7047 SND_AMOEBA_TURNING_TO_ROCK));
7052 static int xy[4][2] =
7060 for (i = 0; i < NUM_DIRECTIONS; i++)
7065 if (!IN_LEV_FIELD(x, y))
7068 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
7070 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
7071 SND_AMOEBA_TURNING_TO_GEM :
7072 SND_AMOEBA_TURNING_TO_ROCK));
7079 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
7082 int group_nr = AmoebaNr[ax][ay];
7083 boolean done = FALSE;
7088 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
7089 printf("AmoebeUmwandelnBD(): This should never happen!\n");
7095 SCAN_PLAYFIELD(x, y)
7097 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7100 if (AmoebaNr[x][y] == group_nr &&
7101 (Feld[x][y] == EL_AMOEBA_DEAD ||
7102 Feld[x][y] == EL_BD_AMOEBA ||
7103 Feld[x][y] == EL_AMOEBA_GROWING))
7106 Feld[x][y] = new_element;
7107 InitField(x, y, FALSE);
7108 DrawLevelField(x, y);
7114 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
7115 SND_BD_AMOEBA_TURNING_TO_ROCK :
7116 SND_BD_AMOEBA_TURNING_TO_GEM));
7119 void AmoebeWaechst(int x, int y)
7121 static unsigned long sound_delay = 0;
7122 static unsigned long sound_delay_value = 0;
7124 if (!MovDelay[x][y]) /* start new growing cycle */
7128 if (DelayReached(&sound_delay, sound_delay_value))
7130 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
7131 sound_delay_value = 30;
7135 if (MovDelay[x][y]) /* wait some time before growing bigger */
7138 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7140 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
7141 6 - MovDelay[x][y]);
7143 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
7146 if (!MovDelay[x][y])
7148 Feld[x][y] = Store[x][y];
7150 DrawLevelField(x, y);
7155 void AmoebaDisappearing(int x, int y)
7157 static unsigned long sound_delay = 0;
7158 static unsigned long sound_delay_value = 0;
7160 if (!MovDelay[x][y]) /* start new shrinking cycle */
7164 if (DelayReached(&sound_delay, sound_delay_value))
7165 sound_delay_value = 30;
7168 if (MovDelay[x][y]) /* wait some time before shrinking */
7171 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7173 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
7174 6 - MovDelay[x][y]);
7176 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
7179 if (!MovDelay[x][y])
7181 Feld[x][y] = EL_EMPTY;
7182 DrawLevelField(x, y);
7184 /* don't let mole enter this field in this cycle;
7185 (give priority to objects falling to this field from above) */
7191 void AmoebeAbleger(int ax, int ay)
7194 int element = Feld[ax][ay];
7195 int graphic = el2img(element);
7196 int newax = ax, neway = ay;
7197 boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
7198 static int xy[4][2] =
7206 if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
7208 Feld[ax][ay] = EL_AMOEBA_DEAD;
7209 DrawLevelField(ax, ay);
7213 if (IS_ANIMATED(graphic))
7214 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7216 if (!MovDelay[ax][ay]) /* start making new amoeba field */
7217 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
7219 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
7222 if (MovDelay[ax][ay])
7226 if (can_drop) /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
7229 int x = ax + xy[start][0];
7230 int y = ay + xy[start][1];
7232 if (!IN_LEV_FIELD(x, y))
7235 if (IS_FREE(x, y) ||
7236 CAN_GROW_INTO(Feld[x][y]) ||
7237 Feld[x][y] == EL_QUICKSAND_EMPTY)
7243 if (newax == ax && neway == ay)
7246 else /* normal or "filled" (BD style) amoeba */
7249 boolean waiting_for_player = FALSE;
7251 for (i = 0; i < NUM_DIRECTIONS; i++)
7253 int j = (start + i) % 4;
7254 int x = ax + xy[j][0];
7255 int y = ay + xy[j][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 else if (IS_PLAYER(x, y))
7269 waiting_for_player = TRUE;
7272 if (newax == ax && neway == ay) /* amoeba cannot grow */
7274 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
7276 Feld[ax][ay] = EL_AMOEBA_DEAD;
7277 DrawLevelField(ax, ay);
7278 AmoebaCnt[AmoebaNr[ax][ay]]--;
7280 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
7282 if (element == EL_AMOEBA_FULL)
7283 AmoebeUmwandeln(ax, ay);
7284 else if (element == EL_BD_AMOEBA)
7285 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
7290 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
7292 /* amoeba gets larger by growing in some direction */
7294 int new_group_nr = AmoebaNr[ax][ay];
7297 if (new_group_nr == 0)
7299 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
7300 printf("AmoebeAbleger(): This should never happen!\n");
7305 AmoebaNr[newax][neway] = new_group_nr;
7306 AmoebaCnt[new_group_nr]++;
7307 AmoebaCnt2[new_group_nr]++;
7309 /* if amoeba touches other amoeba(s) after growing, unify them */
7310 AmoebenVereinigen(newax, neway);
7312 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
7314 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
7320 if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
7321 (neway == lev_fieldy - 1 && newax != ax))
7323 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
7324 Store[newax][neway] = element;
7326 else if (neway == ay || element == EL_EMC_DRIPPER)
7328 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
7330 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
7334 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
7335 Feld[ax][ay] = EL_AMOEBA_DROPPING;
7336 Store[ax][ay] = EL_AMOEBA_DROP;
7337 ContinueMoving(ax, ay);
7341 DrawLevelField(newax, neway);
7344 void Life(int ax, int ay)
7348 static int life[4] = { 2, 3, 3, 3 }; /* parameters for "game of life" */
7351 int element = Feld[ax][ay];
7352 int graphic = el2img(element);
7353 int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
7355 boolean changed = FALSE;
7357 if (IS_ANIMATED(graphic))
7358 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7363 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
7364 MovDelay[ax][ay] = life_time;
7366 if (MovDelay[ax][ay]) /* wait some time before next cycle */
7369 if (MovDelay[ax][ay])
7373 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
7375 int xx = ax+x1, yy = ay+y1;
7378 if (!IN_LEV_FIELD(xx, yy))
7381 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
7383 int x = xx+x2, y = yy+y2;
7385 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
7388 if (((Feld[x][y] == element ||
7389 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
7391 (IS_FREE(x, y) && Stop[x][y]))
7395 if (xx == ax && yy == ay) /* field in the middle */
7397 if (nachbarn < life_parameter[0] ||
7398 nachbarn > life_parameter[1])
7400 Feld[xx][yy] = EL_EMPTY;
7402 DrawLevelField(xx, yy);
7403 Stop[xx][yy] = TRUE;
7407 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
7408 { /* free border field */
7409 if (nachbarn >= life_parameter[2] &&
7410 nachbarn <= life_parameter[3])
7412 Feld[xx][yy] = element;
7413 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
7415 DrawLevelField(xx, yy);
7416 Stop[xx][yy] = TRUE;
7423 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
7424 SND_GAME_OF_LIFE_GROWING);
7427 static void InitRobotWheel(int x, int y)
7429 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
7432 static void RunRobotWheel(int x, int y)
7434 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
7437 static void StopRobotWheel(int x, int y)
7439 if (ZX == x && ZY == y)
7443 static void InitTimegateWheel(int x, int y)
7445 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
7448 static void RunTimegateWheel(int x, int y)
7450 PlayLevelSound(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
7453 static void InitMagicBallDelay(int x, int y)
7456 ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
7458 ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
7462 static void ActivateMagicBall(int bx, int by)
7466 if (level.ball_random)
7468 int pos_border = RND(8); /* select one of the eight border elements */
7469 int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
7470 int xx = pos_content % 3;
7471 int yy = pos_content / 3;
7476 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
7477 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
7481 for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
7483 int xx = x - bx + 1;
7484 int yy = y - by + 1;
7486 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
7487 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
7491 game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
7494 static void InitDiagonalMovingElement(int x, int y)
7497 MovDelay[x][y] = level.android_move_time;
7501 void CheckExit(int x, int y)
7503 if (local_player->gems_still_needed > 0 ||
7504 local_player->sokobanfields_still_needed > 0 ||
7505 local_player->lights_still_needed > 0)
7507 int element = Feld[x][y];
7508 int graphic = el2img(element);
7510 if (IS_ANIMATED(graphic))
7511 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7516 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7519 Feld[x][y] = EL_EXIT_OPENING;
7521 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
7524 void CheckExitSP(int x, int y)
7526 if (local_player->gems_still_needed > 0)
7528 int element = Feld[x][y];
7529 int graphic = el2img(element);
7531 if (IS_ANIMATED(graphic))
7532 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7537 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7540 Feld[x][y] = EL_SP_EXIT_OPENING;
7542 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
7545 static void CloseAllOpenTimegates()
7550 SCAN_PLAYFIELD(x, y)
7552 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7555 int element = Feld[x][y];
7557 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
7559 Feld[x][y] = EL_TIMEGATE_CLOSING;
7561 PlayLevelSoundAction(x, y, ACTION_CLOSING);
7566 void EdelsteinFunkeln(int x, int y)
7568 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
7571 if (Feld[x][y] == EL_BD_DIAMOND)
7574 if (MovDelay[x][y] == 0) /* next animation frame */
7575 MovDelay[x][y] = 11 * !SimpleRND(500);
7577 if (MovDelay[x][y] != 0) /* wait some time before next frame */
7581 if (setup.direct_draw && MovDelay[x][y])
7582 SetDrawtoField(DRAW_BUFFERED);
7584 DrawLevelElementAnimation(x, y, Feld[x][y]);
7586 if (MovDelay[x][y] != 0)
7588 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
7589 10 - MovDelay[x][y]);
7591 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
7593 if (setup.direct_draw)
7597 dest_x = FX + SCREENX(x) * TILEX;
7598 dest_y = FY + SCREENY(y) * TILEY;
7600 BlitBitmap(drawto_field, window,
7601 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
7602 SetDrawtoField(DRAW_DIRECT);
7608 void MauerWaechst(int x, int y)
7612 if (!MovDelay[x][y]) /* next animation frame */
7613 MovDelay[x][y] = 3 * delay;
7615 if (MovDelay[x][y]) /* wait some time before next frame */
7619 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7621 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
7622 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
7624 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
7627 if (!MovDelay[x][y])
7629 if (MovDir[x][y] == MV_LEFT)
7631 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
7632 DrawLevelField(x - 1, y);
7634 else if (MovDir[x][y] == MV_RIGHT)
7636 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
7637 DrawLevelField(x + 1, y);
7639 else if (MovDir[x][y] == MV_UP)
7641 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
7642 DrawLevelField(x, y - 1);
7646 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
7647 DrawLevelField(x, y + 1);
7650 Feld[x][y] = Store[x][y];
7652 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7653 DrawLevelField(x, y);
7658 void MauerAbleger(int ax, int ay)
7660 int element = Feld[ax][ay];
7661 int graphic = el2img(element);
7662 boolean oben_frei = FALSE, unten_frei = FALSE;
7663 boolean links_frei = FALSE, rechts_frei = FALSE;
7664 boolean oben_massiv = FALSE, unten_massiv = FALSE;
7665 boolean links_massiv = FALSE, rechts_massiv = FALSE;
7666 boolean new_wall = FALSE;
7668 if (IS_ANIMATED(graphic))
7669 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7671 if (!MovDelay[ax][ay]) /* start building new wall */
7672 MovDelay[ax][ay] = 6;
7674 if (MovDelay[ax][ay]) /* wait some time before building new wall */
7677 if (MovDelay[ax][ay])
7681 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
7683 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
7685 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
7687 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
7690 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
7691 element == EL_EXPANDABLE_WALL_ANY)
7695 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
7696 Store[ax][ay-1] = element;
7697 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
7698 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
7699 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
7700 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
7705 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
7706 Store[ax][ay+1] = element;
7707 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
7708 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
7709 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
7710 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
7715 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7716 element == EL_EXPANDABLE_WALL_ANY ||
7717 element == EL_EXPANDABLE_WALL)
7721 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
7722 Store[ax-1][ay] = element;
7723 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
7724 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
7725 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
7726 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
7732 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
7733 Store[ax+1][ay] = element;
7734 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
7735 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
7736 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
7737 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
7742 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
7743 DrawLevelField(ax, ay);
7745 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
7747 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
7748 unten_massiv = TRUE;
7749 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
7750 links_massiv = TRUE;
7751 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
7752 rechts_massiv = TRUE;
7754 if (((oben_massiv && unten_massiv) ||
7755 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7756 element == EL_EXPANDABLE_WALL) &&
7757 ((links_massiv && rechts_massiv) ||
7758 element == EL_EXPANDABLE_WALL_VERTICAL))
7759 Feld[ax][ay] = EL_WALL;
7762 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
7765 void CheckForDragon(int x, int y)
7768 boolean dragon_found = FALSE;
7769 static int xy[4][2] =
7777 for (i = 0; i < NUM_DIRECTIONS; i++)
7779 for (j = 0; j < 4; j++)
7781 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7783 if (IN_LEV_FIELD(xx, yy) &&
7784 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
7786 if (Feld[xx][yy] == EL_DRAGON)
7787 dragon_found = TRUE;
7796 for (i = 0; i < NUM_DIRECTIONS; i++)
7798 for (j = 0; j < 3; j++)
7800 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7802 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
7804 Feld[xx][yy] = EL_EMPTY;
7805 DrawLevelField(xx, yy);
7814 static void InitBuggyBase(int x, int y)
7816 int element = Feld[x][y];
7817 int activating_delay = FRAMES_PER_SECOND / 4;
7820 (element == EL_SP_BUGGY_BASE ?
7821 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
7822 element == EL_SP_BUGGY_BASE_ACTIVATING ?
7824 element == EL_SP_BUGGY_BASE_ACTIVE ?
7825 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
7828 static void WarnBuggyBase(int x, int y)
7831 static int xy[4][2] =
7839 for (i = 0; i < NUM_DIRECTIONS; i++)
7841 int xx = x + xy[i][0];
7842 int yy = y + xy[i][1];
7844 if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
7846 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
7853 static void InitTrap(int x, int y)
7855 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
7858 static void ActivateTrap(int x, int y)
7860 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
7863 static void ChangeActiveTrap(int x, int y)
7865 int graphic = IMG_TRAP_ACTIVE;
7867 /* if new animation frame was drawn, correct crumbled sand border */
7868 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
7869 DrawLevelFieldCrumbledSand(x, y);
7872 static int getSpecialActionElement(int element, int number, int base_element)
7874 return (element != EL_EMPTY ? element :
7875 number != -1 ? base_element + number - 1 :
7879 static int getModifiedActionNumber(int value_old, int operator, int operand,
7880 int value_min, int value_max)
7882 int value_new = (operator == CA_MODE_SET ? operand :
7883 operator == CA_MODE_ADD ? value_old + operand :
7884 operator == CA_MODE_SUBTRACT ? value_old - operand :
7885 operator == CA_MODE_MULTIPLY ? value_old * operand :
7886 operator == CA_MODE_DIVIDE ? value_old / MAX(1, operand) :
7887 operator == CA_MODE_MODULO ? value_old % MAX(1, operand) :
7890 return (value_new < value_min ? value_min :
7891 value_new > value_max ? value_max :
7895 static void ExecuteCustomElementAction(int x, int y, int element, int page)
7897 struct ElementInfo *ei = &element_info[element];
7898 struct ElementChangeInfo *change = &ei->change_page[page];
7899 int target_element = change->target_element;
7900 int action_type = change->action_type;
7901 int action_mode = change->action_mode;
7902 int action_arg = change->action_arg;
7905 if (!change->has_action)
7908 /* ---------- determine action paramater values -------------------------- */
7910 int level_time_value =
7911 (level.time > 0 ? TimeLeft :
7914 int action_arg_element =
7915 (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player :
7916 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
7917 action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element :
7920 int action_arg_direction =
7921 (action_arg >= CA_ARG_DIRECTION_LEFT &&
7922 action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
7923 action_arg == CA_ARG_DIRECTION_TRIGGER ?
7924 change->actual_trigger_side :
7925 action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
7926 MV_DIR_OPPOSITE(change->actual_trigger_side) :
7929 int action_arg_number_min =
7930 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
7933 int action_arg_number_max =
7934 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
7935 action_type == CA_SET_LEVEL_GEMS ? 999 :
7936 action_type == CA_SET_LEVEL_TIME ? 9999 :
7937 action_type == CA_SET_LEVEL_SCORE ? 99999 :
7938 action_type == CA_SET_CE_VALUE ? 9999 :
7939 action_type == CA_SET_CE_SCORE ? 9999 :
7942 int action_arg_number_reset =
7943 (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize :
7944 action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
7945 action_type == CA_SET_LEVEL_TIME ? level.time :
7946 action_type == CA_SET_LEVEL_SCORE ? 0 :
7948 action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
7950 action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
7952 action_type == CA_SET_CE_SCORE ? 0 :
7955 int action_arg_number =
7956 (action_arg <= CA_ARG_MAX ? action_arg :
7957 action_arg >= CA_ARG_SPEED_NOT_MOVING &&
7958 action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
7959 action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
7960 action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
7961 action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
7962 action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
7963 #if USE_NEW_CUSTOM_VALUE
7964 action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
7966 action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
7968 action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
7969 action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
7970 action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
7971 action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
7972 action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
7973 action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
7974 action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
7975 action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
7976 action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
7977 action_arg == CA_ARG_ELEMENT_NR_TARGET ? change->target_element :
7978 action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
7981 int action_arg_number_old =
7982 (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
7983 action_type == CA_SET_LEVEL_TIME ? TimeLeft :
7984 action_type == CA_SET_LEVEL_SCORE ? local_player->score :
7985 action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
7986 action_type == CA_SET_CE_SCORE ? ei->collect_score :
7989 int action_arg_number_new =
7990 getModifiedActionNumber(action_arg_number_old,
7991 action_mode, action_arg_number,
7992 action_arg_number_min, action_arg_number_max);
7994 int trigger_player_bits =
7995 (change->actual_trigger_player >= EL_PLAYER_1 &&
7996 change->actual_trigger_player <= EL_PLAYER_4 ?
7997 (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
8000 int action_arg_player_bits =
8001 (action_arg >= CA_ARG_PLAYER_1 &&
8002 action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
8003 action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
8006 /* ---------- execute action -------------------------------------------- */
8015 /* ---------- level actions ------------------------------------------- */
8017 case CA_RESTART_LEVEL:
8019 game.restart_level = TRUE;
8024 case CA_SHOW_ENVELOPE:
8026 int element = getSpecialActionElement(action_arg_element,
8027 action_arg_number, EL_ENVELOPE_1);
8029 if (IS_ENVELOPE(element))
8030 local_player->show_envelope = element;
8035 case CA_SET_LEVEL_TIME:
8037 if (level.time > 0) /* only modify limited time value */
8039 TimeLeft = action_arg_number_new;
8041 DrawGameValue_Time(TimeLeft);
8043 if (!TimeLeft && setup.time_limit)
8044 for (i = 0; i < MAX_PLAYERS; i++)
8045 KillPlayer(&stored_player[i]);
8051 case CA_SET_LEVEL_SCORE:
8053 local_player->score = action_arg_number_new;
8055 DrawGameValue_Score(local_player->score);
8060 case CA_SET_LEVEL_GEMS:
8062 local_player->gems_still_needed = action_arg_number_new;
8064 DrawGameValue_Emeralds(local_player->gems_still_needed);
8069 case CA_SET_LEVEL_GRAVITY:
8071 game.gravity = (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
8072 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
8073 action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
8078 case CA_SET_LEVEL_WIND:
8080 game.wind_direction = action_arg_direction;
8085 /* ---------- player actions ------------------------------------------ */
8087 case CA_MOVE_PLAYER:
8089 /* automatically move to the next field in specified direction */
8090 for (i = 0; i < MAX_PLAYERS; i++)
8091 if (trigger_player_bits & (1 << i))
8092 stored_player[i].programmed_action = action_arg_direction;
8097 case CA_EXIT_PLAYER:
8099 for (i = 0; i < MAX_PLAYERS; i++)
8100 if (action_arg_player_bits & (1 << i))
8101 stored_player[i].LevelSolved = stored_player[i].GameOver = TRUE;
8106 case CA_KILL_PLAYER:
8108 for (i = 0; i < MAX_PLAYERS; i++)
8109 if (action_arg_player_bits & (1 << i))
8110 KillPlayer(&stored_player[i]);
8115 case CA_SET_PLAYER_KEYS:
8117 int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
8118 int element = getSpecialActionElement(action_arg_element,
8119 action_arg_number, EL_KEY_1);
8121 if (IS_KEY(element))
8123 for (i = 0; i < MAX_PLAYERS; i++)
8125 if (trigger_player_bits & (1 << i))
8127 stored_player[i].key[KEY_NR(element)] = key_state;
8130 DrawGameDoorValues();
8132 DrawGameValue_Keys(stored_player[i].key);
8135 redraw_mask |= REDRAW_DOOR_1;
8143 case CA_SET_PLAYER_SPEED:
8145 for (i = 0; i < MAX_PLAYERS; i++)
8147 if (trigger_player_bits & (1 << i))
8149 int move_stepsize = TILEX / stored_player[i].move_delay_value;
8151 if (action_arg == CA_ARG_SPEED_FASTER &&
8152 stored_player[i].cannot_move)
8154 action_arg_number = STEPSIZE_VERY_SLOW;
8156 else if (action_arg == CA_ARG_SPEED_SLOWER ||
8157 action_arg == CA_ARG_SPEED_FASTER)
8159 action_arg_number = 2;
8160 action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
8165 getModifiedActionNumber(move_stepsize,
8168 action_arg_number_min,
8169 action_arg_number_max);
8172 SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
8174 /* make sure that value is power of 2 */
8175 move_stepsize = (1 << log_2(move_stepsize));
8177 /* do no immediately change -- the player might just be moving */
8178 stored_player[i].move_delay_value_next = TILEX / move_stepsize;
8180 stored_player[i].cannot_move =
8181 (action_arg == CA_ARG_SPEED_NOT_MOVING ? TRUE : FALSE);
8189 case CA_SET_PLAYER_SHIELD:
8191 for (i = 0; i < MAX_PLAYERS; i++)
8193 if (trigger_player_bits & (1 << i))
8195 if (action_arg == CA_ARG_SHIELD_OFF)
8197 stored_player[i].shield_normal_time_left = 0;
8198 stored_player[i].shield_deadly_time_left = 0;
8200 else if (action_arg == CA_ARG_SHIELD_NORMAL)
8202 stored_player[i].shield_normal_time_left = 999999;
8204 else if (action_arg == CA_ARG_SHIELD_DEADLY)
8206 stored_player[i].shield_normal_time_left = 999999;
8207 stored_player[i].shield_deadly_time_left = 999999;
8215 case CA_SET_PLAYER_ARTWORK:
8217 for (i = 0; i < MAX_PLAYERS; i++)
8219 if (trigger_player_bits & (1 << i))
8221 int artwork_element = action_arg_element;
8223 if (action_arg == CA_ARG_ELEMENT_RESET)
8225 (level.use_artwork_element[i] ? level.artwork_element[i] :
8226 stored_player[i].element_nr);
8228 stored_player[i].artwork_element = artwork_element;
8230 SetPlayerWaiting(&stored_player[i], FALSE);
8232 /* set number of special actions for bored and sleeping animation */
8233 stored_player[i].num_special_action_bored =
8234 get_num_special_action(artwork_element,
8235 ACTION_BORING_1, ACTION_BORING_LAST);
8236 stored_player[i].num_special_action_sleeping =
8237 get_num_special_action(artwork_element,
8238 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
8245 /* ---------- CE actions ---------------------------------------------- */
8247 case CA_SET_CE_VALUE:
8249 #if USE_NEW_CUSTOM_VALUE
8250 int last_custom_value = CustomValue[x][y];
8252 CustomValue[x][y] = action_arg_number_new;
8255 printf("::: Count == %d\n", CustomValue[x][y]);
8258 if (CustomValue[x][y] == 0 && last_custom_value > 0)
8261 printf("::: CE_VALUE_GETS_ZERO\n");
8264 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
8265 CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
8268 printf("::: RESULT: %d, %d\n", Feld[x][y], ChangePage[x][y]);
8276 case CA_SET_CE_SCORE:
8278 ei->collect_score = action_arg_number_new;
8283 /* ---------- engine actions ------------------------------------------ */
8285 case CA_SET_ENGINE_SCAN_MODE:
8287 InitPlayfieldScanMode(action_arg);
8297 static void CreateFieldExt(int x, int y, int element, boolean is_change)
8299 int old_element = Feld[x][y];
8300 int new_element = get_element_from_group_element(element);
8301 int previous_move_direction = MovDir[x][y];
8302 #if USE_NEW_CUSTOM_VALUE
8303 int last_ce_value = CustomValue[x][y];
8305 boolean add_player = (ELEM_IS_PLAYER(new_element) &&
8306 IS_WALKABLE(old_element));
8309 /* check if element under player changes from accessible to unaccessible
8310 (needed for special case of dropping element which then changes) */
8311 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
8312 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
8322 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
8323 RemoveMovingField(x, y);
8327 Feld[x][y] = new_element;
8329 #if !USE_GFX_RESET_GFX_ANIMATION
8330 ResetGfxAnimation(x, y);
8331 ResetRandomAnimationValue(x, y);
8334 if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
8335 MovDir[x][y] = previous_move_direction;
8337 #if USE_NEW_CUSTOM_VALUE
8338 if (element_info[new_element].use_last_ce_value)
8339 CustomValue[x][y] = last_ce_value;
8342 InitField_WithBug1(x, y, FALSE);
8344 new_element = Feld[x][y]; /* element may have changed */
8346 #if USE_GFX_RESET_GFX_ANIMATION
8347 ResetGfxAnimation(x, y);
8348 ResetRandomAnimationValue(x, y);
8351 DrawLevelField(x, y);
8353 if (GFX_CRUMBLED(new_element))
8354 DrawLevelFieldCrumbledSandNeighbours(x, y);
8358 /* check if element under player changes from accessible to unaccessible
8359 (needed for special case of dropping element which then changes) */
8360 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
8361 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
8369 /* "ChangeCount" not set yet to allow "entered by player" change one time */
8370 if (ELEM_IS_PLAYER(new_element))
8371 RelocatePlayer(x, y, new_element);
8374 ChangeCount[x][y]++; /* count number of changes in the same frame */
8376 TestIfBadThingTouchesPlayer(x, y);
8377 TestIfPlayerTouchesCustomElement(x, y);
8378 TestIfElementTouchesCustomElement(x, y);
8381 static void CreateField(int x, int y, int element)
8383 CreateFieldExt(x, y, element, FALSE);
8386 static void CreateElementFromChange(int x, int y, int element)
8388 element = GET_VALID_RUNTIME_ELEMENT(element);
8390 #if USE_STOP_CHANGED_ELEMENTS
8391 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
8393 int old_element = Feld[x][y];
8395 /* prevent changed element from moving in same engine frame
8396 unless both old and new element can either fall or move */
8397 if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
8398 (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
8403 CreateFieldExt(x, y, element, TRUE);
8406 static boolean ChangeElement(int x, int y, int element, int page)
8408 struct ElementInfo *ei = &element_info[element];
8409 struct ElementChangeInfo *change = &ei->change_page[page];
8410 int ce_value = CustomValue[x][y];
8411 int ce_score = ei->collect_score;
8413 int old_element = Feld[x][y];
8415 /* always use default change event to prevent running into a loop */
8416 if (ChangeEvent[x][y] == -1)
8417 ChangeEvent[x][y] = CE_DELAY;
8419 if (ChangeEvent[x][y] == CE_DELAY)
8421 /* reset actual trigger element, trigger player and action element */
8422 change->actual_trigger_element = EL_EMPTY;
8423 change->actual_trigger_player = EL_PLAYER_1;
8424 change->actual_trigger_side = CH_SIDE_NONE;
8425 change->actual_trigger_ce_value = 0;
8426 change->actual_trigger_ce_score = 0;
8429 /* do not change elements more than a specified maximum number of changes */
8430 if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
8433 ChangeCount[x][y]++; /* count number of changes in the same frame */
8435 if (change->explode)
8442 if (change->use_target_content)
8444 boolean complete_replace = TRUE;
8445 boolean can_replace[3][3];
8448 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
8451 boolean is_walkable;
8452 boolean is_diggable;
8453 boolean is_collectible;
8454 boolean is_removable;
8455 boolean is_destructible;
8456 int ex = x + xx - 1;
8457 int ey = y + yy - 1;
8458 int content_element = change->target_content.e[xx][yy];
8461 can_replace[xx][yy] = TRUE;
8463 if (ex == x && ey == y) /* do not check changing element itself */
8466 if (content_element == EL_EMPTY_SPACE)
8468 can_replace[xx][yy] = FALSE; /* do not replace border with space */
8473 if (!IN_LEV_FIELD(ex, ey))
8475 can_replace[xx][yy] = FALSE;
8476 complete_replace = FALSE;
8483 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
8484 e = MovingOrBlocked2Element(ex, ey);
8486 is_empty = (IS_FREE(ex, ey) ||
8487 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
8489 is_walkable = (is_empty || IS_WALKABLE(e));
8490 is_diggable = (is_empty || IS_DIGGABLE(e));
8491 is_collectible = (is_empty || IS_COLLECTIBLE(e));
8492 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
8493 is_removable = (is_diggable || is_collectible);
8495 can_replace[xx][yy] =
8496 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
8497 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
8498 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
8499 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
8500 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
8501 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
8502 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
8504 if (!can_replace[xx][yy])
8505 complete_replace = FALSE;
8508 if (!change->only_if_complete || complete_replace)
8510 boolean something_has_changed = FALSE;
8512 if (change->only_if_complete && change->use_random_replace &&
8513 RND(100) < change->random_percentage)
8516 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
8518 int ex = x + xx - 1;
8519 int ey = y + yy - 1;
8520 int content_element;
8522 if (can_replace[xx][yy] && (!change->use_random_replace ||
8523 RND(100) < change->random_percentage))
8525 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
8526 RemoveMovingField(ex, ey);
8528 ChangeEvent[ex][ey] = ChangeEvent[x][y];
8530 content_element = change->target_content.e[xx][yy];
8531 target_element = GET_TARGET_ELEMENT(content_element, change,
8532 ce_value, ce_score);
8534 CreateElementFromChange(ex, ey, target_element);
8536 something_has_changed = TRUE;
8538 /* for symmetry reasons, freeze newly created border elements */
8539 if (ex != x || ey != y)
8540 Stop[ex][ey] = TRUE; /* no more moving in this frame */
8544 if (something_has_changed)
8546 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
8547 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
8553 target_element = GET_TARGET_ELEMENT(change->target_element, change,
8554 ce_value, ce_score);
8556 if (element == EL_DIAGONAL_GROWING ||
8557 element == EL_DIAGONAL_SHRINKING)
8559 target_element = Store[x][y];
8561 Store[x][y] = EL_EMPTY;
8564 CreateElementFromChange(x, y, target_element);
8566 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
8567 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
8570 /* this uses direct change before indirect change */
8571 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
8576 #if USE_NEW_DELAYED_ACTION
8578 static void HandleElementChange(int x, int y, int page)
8580 int element = MovingOrBlocked2Element(x, y);
8581 struct ElementInfo *ei = &element_info[element];
8582 struct ElementChangeInfo *change = &ei->change_page[page];
8585 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
8586 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
8589 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
8590 x, y, element, element_info[element].token_name);
8591 printf("HandleElementChange(): This should never happen!\n");
8596 /* this can happen with classic bombs on walkable, changing elements */
8597 if (!CAN_CHANGE_OR_HAS_ACTION(element))
8600 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
8601 ChangeDelay[x][y] = 0;
8607 if (ChangeDelay[x][y] == 0) /* initialize element change */
8609 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
8611 if (change->can_change)
8613 ResetGfxAnimation(x, y);
8614 ResetRandomAnimationValue(x, y);
8616 if (change->pre_change_function)
8617 change->pre_change_function(x, y);
8621 ChangeDelay[x][y]--;
8623 if (ChangeDelay[x][y] != 0) /* continue element change */
8625 if (change->can_change)
8627 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8629 if (IS_ANIMATED(graphic))
8630 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8632 if (change->change_function)
8633 change->change_function(x, y);
8636 else /* finish element change */
8638 if (ChangePage[x][y] != -1) /* remember page from delayed change */
8640 page = ChangePage[x][y];
8641 ChangePage[x][y] = -1;
8643 change = &ei->change_page[page];
8646 if (IS_MOVING(x, y)) /* never change a running system ;-) */
8648 ChangeDelay[x][y] = 1; /* try change after next move step */
8649 ChangePage[x][y] = page; /* remember page to use for change */
8654 if (change->can_change)
8656 if (ChangeElement(x, y, element, page))
8658 if (change->post_change_function)
8659 change->post_change_function(x, y);
8663 if (change->has_action)
8664 ExecuteCustomElementAction(x, y, element, page);
8670 static void HandleElementChange(int x, int y, int page)
8672 int element = MovingOrBlocked2Element(x, y);
8673 struct ElementInfo *ei = &element_info[element];
8674 struct ElementChangeInfo *change = &ei->change_page[page];
8677 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
8680 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
8681 x, y, element, element_info[element].token_name);
8682 printf("HandleElementChange(): This should never happen!\n");
8687 /* this can happen with classic bombs on walkable, changing elements */
8688 if (!CAN_CHANGE(element))
8691 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
8692 ChangeDelay[x][y] = 0;
8698 if (ChangeDelay[x][y] == 0) /* initialize element change */
8700 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
8702 ResetGfxAnimation(x, y);
8703 ResetRandomAnimationValue(x, y);
8705 if (change->pre_change_function)
8706 change->pre_change_function(x, y);
8709 ChangeDelay[x][y]--;
8711 if (ChangeDelay[x][y] != 0) /* continue element change */
8713 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8715 if (IS_ANIMATED(graphic))
8716 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8718 if (change->change_function)
8719 change->change_function(x, y);
8721 else /* finish element change */
8723 if (ChangePage[x][y] != -1) /* remember page from delayed change */
8725 page = ChangePage[x][y];
8726 ChangePage[x][y] = -1;
8728 change = &ei->change_page[page];
8731 if (IS_MOVING(x, y)) /* never change a running system ;-) */
8733 ChangeDelay[x][y] = 1; /* try change after next move step */
8734 ChangePage[x][y] = page; /* remember page to use for change */
8739 if (ChangeElement(x, y, element, page))
8741 if (change->post_change_function)
8742 change->post_change_function(x, y);
8749 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
8750 int trigger_element,
8756 boolean change_done_any = FALSE;
8757 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
8760 if (!(trigger_events[trigger_element][trigger_event]))
8763 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8765 int element = EL_CUSTOM_START + i;
8766 boolean change_done = FALSE;
8769 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
8770 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
8773 for (p = 0; p < element_info[element].num_change_pages; p++)
8775 struct ElementChangeInfo *change = &element_info[element].change_page[p];
8777 if (change->can_change_or_has_action &&
8778 change->has_event[trigger_event] &&
8779 change->trigger_side & trigger_side &&
8780 change->trigger_player & trigger_player &&
8781 change->trigger_page & trigger_page_bits &&
8782 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
8784 change->actual_trigger_element = trigger_element;
8785 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
8786 change->actual_trigger_side = trigger_side;
8787 change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
8788 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
8790 if ((change->can_change && !change_done) || change->has_action)
8795 SCAN_PLAYFIELD(x, y)
8797 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8800 if (Feld[x][y] == element)
8802 if (change->can_change && !change_done)
8804 ChangeDelay[x][y] = 1;
8805 ChangeEvent[x][y] = trigger_event;
8807 HandleElementChange(x, y, p);
8809 #if USE_NEW_DELAYED_ACTION
8810 else if (change->has_action)
8812 ExecuteCustomElementAction(x, y, element, p);
8813 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8816 if (change->has_action)
8818 ExecuteCustomElementAction(x, y, element, p);
8819 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8825 if (change->can_change)
8828 change_done_any = TRUE;
8835 return change_done_any;
8838 static boolean CheckElementChangeExt(int x, int y,
8840 int trigger_element,
8845 boolean change_done = FALSE;
8848 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
8849 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
8852 if (Feld[x][y] == EL_BLOCKED)
8854 Blocked2Moving(x, y, &x, &y);
8855 element = Feld[x][y];
8859 /* check if element has already changed */
8860 if (Feld[x][y] != element)
8863 /* check if element has already changed or is about to change after moving */
8864 if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
8865 Feld[x][y] != element) ||
8867 (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
8868 (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
8869 ChangePage[x][y] != -1)))
8873 for (p = 0; p < element_info[element].num_change_pages; p++)
8875 struct ElementChangeInfo *change = &element_info[element].change_page[p];
8877 boolean check_trigger_element =
8878 (trigger_event == CE_TOUCHING_X ||
8879 trigger_event == CE_HITTING_X ||
8880 trigger_event == CE_HIT_BY_X);
8882 if (change->can_change_or_has_action &&
8883 change->has_event[trigger_event] &&
8884 change->trigger_side & trigger_side &&
8885 change->trigger_player & trigger_player &&
8886 (!check_trigger_element ||
8887 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
8889 change->actual_trigger_element = trigger_element;
8890 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
8891 change->actual_trigger_side = trigger_side;
8892 change->actual_trigger_ce_value = CustomValue[x][y];
8893 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
8895 /* special case: trigger element not at (x,y) position for some events */
8896 if (check_trigger_element)
8908 { 0, 0 }, { 0, 0 }, { 0, 0 },
8912 int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
8913 int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
8915 change->actual_trigger_ce_value = CustomValue[xx][yy];
8916 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
8919 if (change->can_change && !change_done)
8921 ChangeDelay[x][y] = 1;
8922 ChangeEvent[x][y] = trigger_event;
8924 HandleElementChange(x, y, p);
8928 #if USE_NEW_DELAYED_ACTION
8929 else if (change->has_action)
8931 ExecuteCustomElementAction(x, y, element, p);
8932 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8935 if (change->has_action)
8937 ExecuteCustomElementAction(x, y, element, p);
8938 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8947 static void PlayPlayerSound(struct PlayerInfo *player)
8949 int jx = player->jx, jy = player->jy;
8950 int sound_element = player->artwork_element;
8951 int last_action = player->last_action_waiting;
8952 int action = player->action_waiting;
8954 if (player->is_waiting)
8956 if (action != last_action)
8957 PlayLevelSoundElementAction(jx, jy, sound_element, action);
8959 PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
8963 if (action != last_action)
8964 StopSound(element_info[sound_element].sound[last_action]);
8966 if (last_action == ACTION_SLEEPING)
8967 PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
8971 static void PlayAllPlayersSound()
8975 for (i = 0; i < MAX_PLAYERS; i++)
8976 if (stored_player[i].active)
8977 PlayPlayerSound(&stored_player[i]);
8980 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
8982 boolean last_waiting = player->is_waiting;
8983 int move_dir = player->MovDir;
8985 player->dir_waiting = move_dir;
8986 player->last_action_waiting = player->action_waiting;
8990 if (!last_waiting) /* not waiting -> waiting */
8992 player->is_waiting = TRUE;
8994 player->frame_counter_bored =
8996 game.player_boring_delay_fixed +
8997 SimpleRND(game.player_boring_delay_random);
8998 player->frame_counter_sleeping =
9000 game.player_sleeping_delay_fixed +
9001 SimpleRND(game.player_sleeping_delay_random);
9004 InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
9006 InitPlayerGfxAnimation(player, ACTION_WAITING, player->MovDir);
9010 if (game.player_sleeping_delay_fixed +
9011 game.player_sleeping_delay_random > 0 &&
9012 player->anim_delay_counter == 0 &&
9013 player->post_delay_counter == 0 &&
9014 FrameCounter >= player->frame_counter_sleeping)
9015 player->is_sleeping = TRUE;
9016 else if (game.player_boring_delay_fixed +
9017 game.player_boring_delay_random > 0 &&
9018 FrameCounter >= player->frame_counter_bored)
9019 player->is_bored = TRUE;
9021 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
9022 player->is_bored ? ACTION_BORING :
9026 if (player->is_sleeping && player->use_murphy)
9028 /* special case for sleeping Murphy when leaning against non-free tile */
9030 if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
9031 (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
9032 !IS_MOVING(player->jx - 1, player->jy)))
9034 else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
9035 (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
9036 !IS_MOVING(player->jx + 1, player->jy)))
9037 move_dir = MV_RIGHT;
9039 player->is_sleeping = FALSE;
9041 player->dir_waiting = move_dir;
9045 if (player->is_sleeping)
9047 if (player->num_special_action_sleeping > 0)
9049 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
9051 int last_special_action = player->special_action_sleeping;
9052 int num_special_action = player->num_special_action_sleeping;
9053 int special_action =
9054 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
9055 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
9056 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
9057 last_special_action + 1 : ACTION_SLEEPING);
9058 int special_graphic =
9059 el_act_dir2img(player->artwork_element, special_action, move_dir);
9061 player->anim_delay_counter =
9062 graphic_info[special_graphic].anim_delay_fixed +
9063 SimpleRND(graphic_info[special_graphic].anim_delay_random);
9064 player->post_delay_counter =
9065 graphic_info[special_graphic].post_delay_fixed +
9066 SimpleRND(graphic_info[special_graphic].post_delay_random);
9068 player->special_action_sleeping = special_action;
9071 if (player->anim_delay_counter > 0)
9073 player->action_waiting = player->special_action_sleeping;
9074 player->anim_delay_counter--;
9076 else if (player->post_delay_counter > 0)
9078 player->post_delay_counter--;
9082 else if (player->is_bored)
9084 if (player->num_special_action_bored > 0)
9086 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
9088 int special_action =
9089 ACTION_BORING_1 + SimpleRND(player->num_special_action_bored);
9090 int special_graphic =
9091 el_act_dir2img(player->artwork_element, special_action, move_dir);
9093 player->anim_delay_counter =
9094 graphic_info[special_graphic].anim_delay_fixed +
9095 SimpleRND(graphic_info[special_graphic].anim_delay_random);
9096 player->post_delay_counter =
9097 graphic_info[special_graphic].post_delay_fixed +
9098 SimpleRND(graphic_info[special_graphic].post_delay_random);
9100 player->special_action_bored = special_action;
9103 if (player->anim_delay_counter > 0)
9105 player->action_waiting = player->special_action_bored;
9106 player->anim_delay_counter--;
9108 else if (player->post_delay_counter > 0)
9110 player->post_delay_counter--;
9115 else if (last_waiting) /* waiting -> not waiting */
9117 player->is_waiting = FALSE;
9118 player->is_bored = FALSE;
9119 player->is_sleeping = FALSE;
9121 player->frame_counter_bored = -1;
9122 player->frame_counter_sleeping = -1;
9124 player->anim_delay_counter = 0;
9125 player->post_delay_counter = 0;
9127 player->dir_waiting = player->MovDir;
9128 player->action_waiting = ACTION_DEFAULT;
9130 player->special_action_bored = ACTION_DEFAULT;
9131 player->special_action_sleeping = ACTION_DEFAULT;
9135 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
9137 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
9138 int left = player_action & JOY_LEFT;
9139 int right = player_action & JOY_RIGHT;
9140 int up = player_action & JOY_UP;
9141 int down = player_action & JOY_DOWN;
9142 int button1 = player_action & JOY_BUTTON_1;
9143 int button2 = player_action & JOY_BUTTON_2;
9144 int dx = (left ? -1 : right ? 1 : 0);
9145 int dy = (up ? -1 : down ? 1 : 0);
9147 if (!player->active || tape.pausing)
9153 snapped = SnapField(player, dx, dy);
9157 dropped = DropElement(player);
9159 moved = MovePlayer(player, dx, dy);
9162 if (tape.single_step && tape.recording && !tape.pausing)
9164 if (button1 || (dropped && !moved))
9166 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9167 SnapField(player, 0, 0); /* stop snapping */
9171 SetPlayerWaiting(player, FALSE);
9173 return player_action;
9177 /* no actions for this player (no input at player's configured device) */
9179 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
9180 SnapField(player, 0, 0);
9181 CheckGravityMovementWhenNotMoving(player);
9183 if (player->MovPos == 0)
9184 SetPlayerWaiting(player, TRUE);
9186 if (player->MovPos == 0) /* needed for tape.playing */
9187 player->is_moving = FALSE;
9189 player->is_dropping = FALSE;
9190 player->is_dropping_pressed = FALSE;
9191 player->drop_pressed_delay = 0;
9197 static void CheckLevelTime()
9201 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9203 if (level.native_em_level->lev->home == 0) /* all players at home */
9205 local_player->LevelSolved = TRUE;
9206 AllPlayersGone = TRUE;
9208 level.native_em_level->lev->home = -1;
9211 if (level.native_em_level->ply[0]->alive == 0 &&
9212 level.native_em_level->ply[1]->alive == 0 &&
9213 level.native_em_level->ply[2]->alive == 0 &&
9214 level.native_em_level->ply[3]->alive == 0) /* all dead */
9215 AllPlayersGone = TRUE;
9218 if (TimeFrames >= FRAMES_PER_SECOND)
9223 for (i = 0; i < MAX_PLAYERS; i++)
9225 struct PlayerInfo *player = &stored_player[i];
9227 if (SHIELD_ON(player))
9229 player->shield_normal_time_left--;
9231 if (player->shield_deadly_time_left > 0)
9232 player->shield_deadly_time_left--;
9236 if (!level.use_step_counter)
9244 if (TimeLeft <= 10 && setup.time_limit)
9245 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
9247 DrawGameValue_Time(TimeLeft);
9249 if (!TimeLeft && setup.time_limit)
9251 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9252 level.native_em_level->lev->killed_out_of_time = TRUE;
9254 for (i = 0; i < MAX_PLAYERS; i++)
9255 KillPlayer(&stored_player[i]);
9258 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
9259 DrawGameValue_Time(TimePlayed);
9261 level.native_em_level->lev->time =
9262 (level.time == 0 ? TimePlayed : TimeLeft);
9265 if (tape.recording || tape.playing)
9266 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
9270 void AdvanceFrameAndPlayerCounters(int player_nr)
9275 Error(ERR_NETWORK_CLIENT, "advancing frame counter from %d to %d",
9276 FrameCounter, FrameCounter + 1);
9279 /* advance frame counters (global frame counter and time frame counter) */
9283 /* advance player counters (counters for move delay, move animation etc.) */
9284 for (i = 0; i < MAX_PLAYERS; i++)
9286 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
9287 int move_delay_value = stored_player[i].move_delay_value;
9288 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
9290 if (!advance_player_counters) /* not all players may be affected */
9293 #if USE_NEW_PLAYER_ANIM
9294 if (move_frames == 0) /* less than one move per game frame */
9296 int stepsize = TILEX / move_delay_value;
9297 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
9298 int count = (stored_player[i].is_moving ?
9299 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
9301 if (count % delay == 0)
9306 stored_player[i].Frame += move_frames;
9308 if (stored_player[i].MovPos != 0)
9309 stored_player[i].StepFrame += move_frames;
9311 if (stored_player[i].move_delay > 0)
9312 stored_player[i].move_delay--;
9314 /* due to bugs in previous versions, counter must count up, not down */
9315 if (stored_player[i].push_delay != -1)
9316 stored_player[i].push_delay++;
9318 if (stored_player[i].drop_delay > 0)
9319 stored_player[i].drop_delay--;
9321 if (stored_player[i].is_dropping_pressed)
9322 stored_player[i].drop_pressed_delay++;
9326 void StartGameActions(boolean init_network_game, boolean record_tape,
9329 unsigned long new_random_seed = InitRND(random_seed);
9332 TapeStartRecording(new_random_seed);
9334 #if defined(NETWORK_AVALIABLE)
9335 if (init_network_game)
9337 SendToServer_StartPlaying();
9345 game_status = GAME_MODE_PLAYING;
9352 static unsigned long game_frame_delay = 0;
9353 unsigned long game_frame_delay_value;
9354 byte *recorded_player_action;
9355 byte summarized_player_action = 0;
9356 byte tape_action[MAX_PLAYERS];
9359 if (game.restart_level)
9360 StartGameActions(options.network, setup.autorecord, NEW_RANDOMIZE);
9362 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9364 if (level.native_em_level->lev->home == 0) /* all players at home */
9366 local_player->LevelSolved = TRUE;
9367 AllPlayersGone = TRUE;
9369 level.native_em_level->lev->home = -1;
9372 if (level.native_em_level->ply[0]->alive == 0 &&
9373 level.native_em_level->ply[1]->alive == 0 &&
9374 level.native_em_level->ply[2]->alive == 0 &&
9375 level.native_em_level->ply[3]->alive == 0) /* all dead */
9376 AllPlayersGone = TRUE;
9379 if (local_player->LevelSolved)
9382 if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
9385 if (game_status != GAME_MODE_PLAYING) /* status might have changed */
9388 game_frame_delay_value =
9389 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
9391 if (tape.playing && tape.warp_forward && !tape.pausing)
9392 game_frame_delay_value = 0;
9394 /* ---------- main game synchronization point ---------- */
9396 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
9398 if (network_playing && !network_player_action_received)
9400 /* try to get network player actions in time */
9402 #if defined(NETWORK_AVALIABLE)
9403 /* last chance to get network player actions without main loop delay */
9407 /* game was quit by network peer */
9408 if (game_status != GAME_MODE_PLAYING)
9411 if (!network_player_action_received)
9412 return; /* failed to get network player actions in time */
9414 /* do not yet reset "network_player_action_received" (for tape.pausing) */
9420 /* at this point we know that we really continue executing the game */
9423 network_player_action_received = FALSE;
9426 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
9428 if (tape.set_centered_player)
9430 game.centered_player_nr_next = tape.centered_player_nr_next;
9431 game.set_centered_player = TRUE;
9434 for (i = 0; i < MAX_PLAYERS; i++)
9436 summarized_player_action |= stored_player[i].action;
9438 if (!network_playing)
9439 stored_player[i].effective_action = stored_player[i].action;
9442 #if defined(NETWORK_AVALIABLE)
9443 if (network_playing)
9444 SendToServer_MovePlayer(summarized_player_action);
9447 if (!options.network && !setup.team_mode)
9448 local_player->effective_action = summarized_player_action;
9450 if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
9452 for (i = 0; i < MAX_PLAYERS; i++)
9453 stored_player[i].effective_action =
9454 (i == game.centered_player_nr ? summarized_player_action : 0);
9457 if (recorded_player_action != NULL)
9458 for (i = 0; i < MAX_PLAYERS; i++)
9459 stored_player[i].effective_action = recorded_player_action[i];
9461 for (i = 0; i < MAX_PLAYERS; i++)
9463 tape_action[i] = stored_player[i].effective_action;
9465 /* (this can only happen in the R'n'D game engine) */
9466 if (tape.recording && tape_action[i] && !tape.player_participates[i])
9467 tape.player_participates[i] = TRUE; /* player just appeared from CE */
9470 /* only record actions from input devices, but not programmed actions */
9472 TapeRecordAction(tape_action);
9474 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9476 GameActions_EM_Main();
9484 void GameActions_EM_Main()
9486 byte effective_action[MAX_PLAYERS];
9487 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
9490 for (i = 0; i < MAX_PLAYERS; i++)
9491 effective_action[i] = stored_player[i].effective_action;
9493 GameActions_EM(effective_action, warp_mode);
9497 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
9500 void GameActions_RND()
9502 int magic_wall_x = 0, magic_wall_y = 0;
9503 int i, x, y, element, graphic;
9505 InitPlayfieldScanModeVars();
9507 #if USE_ONE_MORE_CHANGE_PER_FRAME
9508 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
9510 SCAN_PLAYFIELD(x, y)
9512 ChangeCount[x][y] = 0;
9513 ChangeEvent[x][y] = -1;
9519 if (game.set_centered_player)
9521 boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
9523 /* switching to "all players" only possible if all players fit to screen */
9524 if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
9526 game.centered_player_nr_next = game.centered_player_nr;
9527 game.set_centered_player = FALSE;
9530 /* do not switch focus to non-existing (or non-active) player */
9531 if (game.centered_player_nr_next >= 0 &&
9532 !stored_player[game.centered_player_nr_next].active)
9534 game.centered_player_nr_next = game.centered_player_nr;
9535 game.set_centered_player = FALSE;
9539 if (game.set_centered_player &&
9540 ScreenMovPos == 0) /* screen currently aligned at tile position */
9544 if (game.centered_player_nr_next == -1)
9546 setScreenCenteredToAllPlayers(&sx, &sy);
9550 sx = stored_player[game.centered_player_nr_next].jx;
9551 sy = stored_player[game.centered_player_nr_next].jy;
9554 game.centered_player_nr = game.centered_player_nr_next;
9555 game.set_centered_player = FALSE;
9557 DrawRelocateScreen(sx, sy, MV_NONE, TRUE, setup.quick_switch);
9558 DrawGameDoorValues();
9562 for (i = 0; i < MAX_PLAYERS; i++)
9564 int actual_player_action = stored_player[i].effective_action;
9567 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
9568 - rnd_equinox_tetrachloride 048
9569 - rnd_equinox_tetrachloride_ii 096
9570 - rnd_emanuel_schmieg 002
9571 - doctor_sloan_ww 001, 020
9573 if (stored_player[i].MovPos == 0)
9574 CheckGravityMovement(&stored_player[i]);
9577 /* overwrite programmed action with tape action */
9578 if (stored_player[i].programmed_action)
9579 actual_player_action = stored_player[i].programmed_action;
9582 PlayerActions(&stored_player[i], actual_player_action);
9584 tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
9586 if (tape.recording && tape_action[i] && !tape.player_participates[i])
9587 tape.player_participates[i] = TRUE; /* player just appeared from CE */
9590 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
9594 network_player_action_received = FALSE;
9597 ScrollScreen(NULL, SCROLL_GO_ON);
9599 /* for backwards compatibility, the following code emulates a fixed bug that
9600 occured when pushing elements (causing elements that just made their last
9601 pushing step to already (if possible) make their first falling step in the
9602 same game frame, which is bad); this code is also needed to use the famous
9603 "spring push bug" which is used in older levels and might be wanted to be
9604 used also in newer levels, but in this case the buggy pushing code is only
9605 affecting the "spring" element and no other elements */
9607 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
9609 for (i = 0; i < MAX_PLAYERS; i++)
9611 struct PlayerInfo *player = &stored_player[i];
9615 if (player->active && player->is_pushing && player->is_moving &&
9617 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
9618 Feld[x][y] == EL_SPRING))
9620 ContinueMoving(x, y);
9622 /* continue moving after pushing (this is actually a bug) */
9623 if (!IS_MOVING(x, y))
9632 SCAN_PLAYFIELD(x, y)
9634 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9637 ChangeCount[x][y] = 0;
9638 ChangeEvent[x][y] = -1;
9640 /* this must be handled before main playfield loop */
9641 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
9644 if (MovDelay[x][y] <= 0)
9648 #if USE_NEW_SNAP_DELAY
9649 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
9652 if (MovDelay[x][y] <= 0)
9655 DrawLevelField(x, y);
9657 TestIfElementTouchesCustomElement(x, y); /* for empty space */
9663 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
9665 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
9666 printf("GameActions(): This should never happen!\n");
9668 ChangePage[x][y] = -1;
9673 if (WasJustMoving[x][y] > 0)
9674 WasJustMoving[x][y]--;
9675 if (WasJustFalling[x][y] > 0)
9676 WasJustFalling[x][y]--;
9677 if (CheckCollision[x][y] > 0)
9678 CheckCollision[x][y]--;
9682 /* reset finished pushing action (not done in ContinueMoving() to allow
9683 continuous pushing animation for elements with zero push delay) */
9684 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
9686 ResetGfxAnimation(x, y);
9687 DrawLevelField(x, y);
9691 if (IS_BLOCKED(x, y))
9695 Blocked2Moving(x, y, &oldx, &oldy);
9696 if (!IS_MOVING(oldx, oldy))
9698 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
9699 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
9700 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
9701 printf("GameActions(): This should never happen!\n");
9708 SCAN_PLAYFIELD(x, y)
9710 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9713 element = Feld[x][y];
9714 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9717 printf("::: %d,%d\n", x, y);
9719 if (element == EL_ROCK)
9720 printf("::: Yo man! Rocks can fall!\n");
9723 if (graphic_info[graphic].anim_global_sync)
9724 GfxFrame[x][y] = FrameCounter;
9725 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
9727 int old_gfx_frame = GfxFrame[x][y];
9729 GfxFrame[x][y] = CustomValue[x][y];
9732 if (GfxFrame[x][y] != old_gfx_frame)
9734 DrawLevelGraphicAnimation(x, y, graphic);
9736 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
9738 int old_gfx_frame = GfxFrame[x][y];
9740 GfxFrame[x][y] = element_info[element].collect_score;
9743 if (GfxFrame[x][y] != old_gfx_frame)
9745 DrawLevelGraphicAnimation(x, y, graphic);
9748 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
9749 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
9750 ResetRandomAnimationValue(x, y);
9752 SetRandomAnimationValue(x, y);
9754 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
9756 if (IS_INACTIVE(element))
9758 if (IS_ANIMATED(graphic))
9759 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9764 /* this may take place after moving, so 'element' may have changed */
9765 if (IS_CHANGING(x, y) &&
9766 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
9768 int page = element_info[element].event_page_nr[CE_DELAY];
9770 HandleElementChange(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] : page);
9774 printf("::: ChangeDelay == %d\n", ChangeDelay[x][y]);
9778 if (element == EL_CUSTOM_255)
9779 printf("::: ChangeDelay == %d\n", ChangeDelay[x][y]);
9783 HandleElementChange(x, y, page);
9785 if (CAN_CHANGE(element))
9786 HandleElementChange(x, y, page);
9788 if (HAS_ACTION(element))
9789 ExecuteCustomElementAction(x, y, element, page);
9794 element = Feld[x][y];
9795 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9798 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
9802 element = Feld[x][y];
9803 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9805 if (IS_ANIMATED(graphic) &&
9808 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9810 if (IS_GEM(element) || element == EL_SP_INFOTRON)
9811 EdelsteinFunkeln(x, y);
9813 else if ((element == EL_ACID ||
9814 element == EL_EXIT_OPEN ||
9815 element == EL_SP_EXIT_OPEN ||
9816 element == EL_SP_TERMINAL ||
9817 element == EL_SP_TERMINAL_ACTIVE ||
9818 element == EL_EXTRA_TIME ||
9819 element == EL_SHIELD_NORMAL ||
9820 element == EL_SHIELD_DEADLY) &&
9821 IS_ANIMATED(graphic))
9822 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9823 else if (IS_MOVING(x, y))
9824 ContinueMoving(x, y);
9825 else if (IS_ACTIVE_BOMB(element))
9826 CheckDynamite(x, y);
9827 else if (element == EL_AMOEBA_GROWING)
9828 AmoebeWaechst(x, y);
9829 else if (element == EL_AMOEBA_SHRINKING)
9830 AmoebaDisappearing(x, y);
9832 #if !USE_NEW_AMOEBA_CODE
9833 else if (IS_AMOEBALIVE(element))
9834 AmoebeAbleger(x, y);
9837 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
9839 else if (element == EL_EXIT_CLOSED)
9841 else if (element == EL_SP_EXIT_CLOSED)
9843 else if (element == EL_EXPANDABLE_WALL_GROWING)
9845 else if (element == EL_EXPANDABLE_WALL ||
9846 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9847 element == EL_EXPANDABLE_WALL_VERTICAL ||
9848 element == EL_EXPANDABLE_WALL_ANY)
9850 else if (element == EL_FLAMES)
9851 CheckForDragon(x, y);
9852 else if (element == EL_EXPLOSION)
9853 ; /* drawing of correct explosion animation is handled separately */
9854 else if (element == EL_ELEMENT_SNAPPING ||
9855 element == EL_DIAGONAL_SHRINKING ||
9856 element == EL_DIAGONAL_GROWING)
9859 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
9861 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9864 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
9865 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9868 if (element == EL_CUSTOM_255 ||
9869 element == EL_CUSTOM_256)
9870 DrawLevelGraphicAnimation(x, y, graphic);
9873 if (IS_BELT_ACTIVE(element))
9874 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
9876 if (game.magic_wall_active)
9878 int jx = local_player->jx, jy = local_player->jy;
9880 /* play the element sound at the position nearest to the player */
9881 if ((element == EL_MAGIC_WALL_FULL ||
9882 element == EL_MAGIC_WALL_ACTIVE ||
9883 element == EL_MAGIC_WALL_EMPTYING ||
9884 element == EL_BD_MAGIC_WALL_FULL ||
9885 element == EL_BD_MAGIC_WALL_ACTIVE ||
9886 element == EL_BD_MAGIC_WALL_EMPTYING) &&
9887 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
9895 #if USE_NEW_AMOEBA_CODE
9896 /* new experimental amoeba growth stuff */
9897 if (!(FrameCounter % 8))
9899 static unsigned long random = 1684108901;
9901 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
9903 x = RND(lev_fieldx);
9904 y = RND(lev_fieldy);
9905 element = Feld[x][y];
9907 if (!IS_PLAYER(x,y) &&
9908 (element == EL_EMPTY ||
9909 CAN_GROW_INTO(element) ||
9910 element == EL_QUICKSAND_EMPTY ||
9911 element == EL_ACID_SPLASH_LEFT ||
9912 element == EL_ACID_SPLASH_RIGHT))
9914 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
9915 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
9916 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
9917 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
9918 Feld[x][y] = EL_AMOEBA_DROP;
9921 random = random * 129 + 1;
9927 if (game.explosions_delayed)
9930 game.explosions_delayed = FALSE;
9933 SCAN_PLAYFIELD(x, y)
9935 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9938 element = Feld[x][y];
9940 if (ExplodeField[x][y])
9941 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
9942 else if (element == EL_EXPLOSION)
9943 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
9945 ExplodeField[x][y] = EX_TYPE_NONE;
9948 game.explosions_delayed = TRUE;
9951 if (game.magic_wall_active)
9953 if (!(game.magic_wall_time_left % 4))
9955 int element = Feld[magic_wall_x][magic_wall_y];
9957 if (element == EL_BD_MAGIC_WALL_FULL ||
9958 element == EL_BD_MAGIC_WALL_ACTIVE ||
9959 element == EL_BD_MAGIC_WALL_EMPTYING)
9960 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
9962 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
9965 if (game.magic_wall_time_left > 0)
9967 game.magic_wall_time_left--;
9968 if (!game.magic_wall_time_left)
9971 SCAN_PLAYFIELD(x, y)
9973 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9976 element = Feld[x][y];
9978 if (element == EL_MAGIC_WALL_ACTIVE ||
9979 element == EL_MAGIC_WALL_FULL)
9981 Feld[x][y] = EL_MAGIC_WALL_DEAD;
9982 DrawLevelField(x, y);
9984 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
9985 element == EL_BD_MAGIC_WALL_FULL)
9987 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
9988 DrawLevelField(x, y);
9992 game.magic_wall_active = FALSE;
9997 if (game.light_time_left > 0)
9999 game.light_time_left--;
10001 if (game.light_time_left == 0)
10002 RedrawAllLightSwitchesAndInvisibleElements();
10005 if (game.timegate_time_left > 0)
10007 game.timegate_time_left--;
10009 if (game.timegate_time_left == 0)
10010 CloseAllOpenTimegates();
10013 if (game.lenses_time_left > 0)
10015 game.lenses_time_left--;
10017 if (game.lenses_time_left == 0)
10018 RedrawAllInvisibleElementsForLenses();
10021 if (game.magnify_time_left > 0)
10023 game.magnify_time_left--;
10025 if (game.magnify_time_left == 0)
10026 RedrawAllInvisibleElementsForMagnifier();
10029 for (i = 0; i < MAX_PLAYERS; i++)
10031 struct PlayerInfo *player = &stored_player[i];
10033 if (SHIELD_ON(player))
10035 if (player->shield_deadly_time_left)
10036 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
10037 else if (player->shield_normal_time_left)
10038 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
10045 PlayAllPlayersSound();
10047 if (options.debug) /* calculate frames per second */
10049 static unsigned long fps_counter = 0;
10050 static int fps_frames = 0;
10051 unsigned long fps_delay_ms = Counter() - fps_counter;
10055 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
10057 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
10060 fps_counter = Counter();
10063 redraw_mask |= REDRAW_FPS;
10066 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
10068 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
10070 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
10072 local_player->show_envelope = 0;
10075 /* use random number generator in every frame to make it less predictable */
10076 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
10080 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
10082 int min_x = x, min_y = y, max_x = x, max_y = y;
10085 for (i = 0; i < MAX_PLAYERS; i++)
10087 int jx = stored_player[i].jx, jy = stored_player[i].jy;
10089 if (!stored_player[i].active || &stored_player[i] == player)
10092 min_x = MIN(min_x, jx);
10093 min_y = MIN(min_y, jy);
10094 max_x = MAX(max_x, jx);
10095 max_y = MAX(max_y, jy);
10098 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
10101 static boolean AllPlayersInVisibleScreen()
10105 for (i = 0; i < MAX_PLAYERS; i++)
10107 int jx = stored_player[i].jx, jy = stored_player[i].jy;
10109 if (!stored_player[i].active)
10112 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
10119 void ScrollLevel(int dx, int dy)
10121 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
10124 BlitBitmap(drawto_field, drawto_field,
10125 FX + TILEX * (dx == -1) - softscroll_offset,
10126 FY + TILEY * (dy == -1) - softscroll_offset,
10127 SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
10128 SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
10129 FX + TILEX * (dx == 1) - softscroll_offset,
10130 FY + TILEY * (dy == 1) - softscroll_offset);
10134 x = (dx == 1 ? BX1 : BX2);
10135 for (y = BY1; y <= BY2; y++)
10136 DrawScreenField(x, y);
10141 y = (dy == 1 ? BY1 : BY2);
10142 for (x = BX1; x <= BX2; x++)
10143 DrawScreenField(x, y);
10146 redraw_mask |= REDRAW_FIELD;
10149 static boolean canFallDown(struct PlayerInfo *player)
10151 int jx = player->jx, jy = player->jy;
10153 return (IN_LEV_FIELD(jx, jy + 1) &&
10154 (IS_FREE(jx, jy + 1) ||
10155 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
10156 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
10157 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
10160 static boolean canPassField(int x, int y, int move_dir)
10162 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
10163 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
10164 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
10165 int nextx = x + dx;
10166 int nexty = y + dy;
10167 int element = Feld[x][y];
10169 return (IS_PASSABLE_FROM(element, opposite_dir) &&
10170 !CAN_MOVE(element) &&
10171 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
10172 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
10173 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
10176 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
10178 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
10179 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
10180 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
10184 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
10185 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
10186 (IS_DIGGABLE(Feld[newx][newy]) ||
10187 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
10188 canPassField(newx, newy, move_dir)));
10191 static void CheckGravityMovement(struct PlayerInfo *player)
10193 if (game.gravity && !player->programmed_action)
10195 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
10196 int move_dir_vertical = player->effective_action & MV_VERTICAL;
10197 boolean player_is_snapping = player->effective_action & JOY_BUTTON_1;
10198 int jx = player->jx, jy = player->jy;
10199 boolean player_is_moving_to_valid_field =
10200 (!player_is_snapping &&
10201 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
10202 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
10203 boolean player_can_fall_down = canFallDown(player);
10205 if (player_can_fall_down &&
10206 !player_is_moving_to_valid_field)
10207 player->programmed_action = MV_DOWN;
10211 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
10213 return CheckGravityMovement(player);
10215 if (game.gravity && !player->programmed_action)
10217 int jx = player->jx, jy = player->jy;
10218 boolean field_under_player_is_free =
10219 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
10220 boolean player_is_standing_on_valid_field =
10221 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
10222 (IS_WALKABLE(Feld[jx][jy]) &&
10223 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
10225 if (field_under_player_is_free && !player_is_standing_on_valid_field)
10226 player->programmed_action = MV_DOWN;
10231 MovePlayerOneStep()
10232 -----------------------------------------------------------------------------
10233 dx, dy: direction (non-diagonal) to try to move the player to
10234 real_dx, real_dy: direction as read from input device (can be diagonal)
10237 boolean MovePlayerOneStep(struct PlayerInfo *player,
10238 int dx, int dy, int real_dx, int real_dy)
10240 int jx = player->jx, jy = player->jy;
10241 int new_jx = jx + dx, new_jy = jy + dy;
10242 #if !USE_FIXED_DONT_RUN_INTO
10246 boolean player_can_move = !player->cannot_move;
10248 if (!player->active || (!dx && !dy))
10249 return MP_NO_ACTION;
10251 player->MovDir = (dx < 0 ? MV_LEFT :
10252 dx > 0 ? MV_RIGHT :
10254 dy > 0 ? MV_DOWN : MV_NONE);
10256 if (!IN_LEV_FIELD(new_jx, new_jy))
10257 return MP_NO_ACTION;
10259 if (!player_can_move)
10262 if (player->MovPos == 0)
10264 player->is_moving = FALSE;
10265 player->is_digging = FALSE;
10266 player->is_collecting = FALSE;
10267 player->is_snapping = FALSE;
10268 player->is_pushing = FALSE;
10271 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
10272 SnapField(player, 0, 0);
10276 return MP_NO_ACTION;
10281 if (!options.network && game.centered_player_nr == -1 &&
10282 !AllPlayersInSight(player, new_jx, new_jy))
10283 return MP_NO_ACTION;
10285 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
10286 return MP_NO_ACTION;
10289 #if !USE_FIXED_DONT_RUN_INTO
10290 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
10292 /* (moved to DigField()) */
10293 if (player_can_move && DONT_RUN_INTO(element))
10295 if (element == EL_ACID && dx == 0 && dy == 1)
10297 SplashAcid(new_jx, new_jy);
10298 Feld[jx][jy] = EL_PLAYER_1;
10299 InitMovingField(jx, jy, MV_DOWN);
10300 Store[jx][jy] = EL_ACID;
10301 ContinueMoving(jx, jy);
10302 BuryPlayer(player);
10305 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
10311 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
10313 #if USE_FIXED_DONT_RUN_INTO
10314 if (can_move == MP_DONT_RUN_INTO)
10318 if (can_move != MP_MOVING)
10321 #if USE_FIXED_DONT_RUN_INTO
10324 /* check if DigField() has caused relocation of the player */
10325 if (player->jx != jx || player->jy != jy)
10326 return MP_NO_ACTION; /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
10328 StorePlayer[jx][jy] = 0;
10329 player->last_jx = jx;
10330 player->last_jy = jy;
10331 player->jx = new_jx;
10332 player->jy = new_jy;
10333 StorePlayer[new_jx][new_jy] = player->element_nr;
10335 if (player->move_delay_value_next != -1)
10337 player->move_delay_value = player->move_delay_value_next;
10338 player->move_delay_value_next = -1;
10342 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
10344 player->step_counter++;
10346 PlayerVisit[jx][jy] = FrameCounter;
10348 ScrollPlayer(player, SCROLL_INIT);
10353 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
10355 int jx = player->jx, jy = player->jy;
10356 int old_jx = jx, old_jy = jy;
10357 int moved = MP_NO_ACTION;
10359 if (!player->active)
10364 if (player->MovPos == 0)
10366 player->is_moving = FALSE;
10367 player->is_digging = FALSE;
10368 player->is_collecting = FALSE;
10369 player->is_snapping = FALSE;
10370 player->is_pushing = FALSE;
10376 if (player->move_delay > 0)
10379 player->move_delay = -1; /* set to "uninitialized" value */
10381 /* store if player is automatically moved to next field */
10382 player->is_auto_moving = (player->programmed_action != MV_NONE);
10384 /* remove the last programmed player action */
10385 player->programmed_action = 0;
10387 if (player->MovPos)
10389 /* should only happen if pre-1.2 tape recordings are played */
10390 /* this is only for backward compatibility */
10392 int original_move_delay_value = player->move_delay_value;
10395 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
10399 /* scroll remaining steps with finest movement resolution */
10400 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
10402 while (player->MovPos)
10404 ScrollPlayer(player, SCROLL_GO_ON);
10405 ScrollScreen(NULL, SCROLL_GO_ON);
10407 AdvanceFrameAndPlayerCounters(player->index_nr);
10413 player->move_delay_value = original_move_delay_value;
10416 if (player->last_move_dir & MV_HORIZONTAL)
10418 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
10419 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
10423 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
10424 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
10431 if (moved & MP_MOVING && !ScreenMovPos &&
10432 (player->index_nr == game.centered_player_nr ||
10433 game.centered_player_nr == -1))
10435 if (moved & MP_MOVING && !ScreenMovPos &&
10436 (player == local_player || !options.network))
10439 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
10440 int offset = (setup.scroll_delay ? 3 : 0);
10442 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
10444 /* actual player has left the screen -- scroll in that direction */
10445 if (jx != old_jx) /* player has moved horizontally */
10446 scroll_x += (jx - old_jx);
10447 else /* player has moved vertically */
10448 scroll_y += (jy - old_jy);
10452 if (jx != old_jx) /* player has moved horizontally */
10454 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
10455 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
10456 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
10458 /* don't scroll over playfield boundaries */
10459 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
10460 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
10462 /* don't scroll more than one field at a time */
10463 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
10465 /* don't scroll against the player's moving direction */
10466 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
10467 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
10468 scroll_x = old_scroll_x;
10470 else /* player has moved vertically */
10472 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
10473 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
10474 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
10476 /* don't scroll over playfield boundaries */
10477 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
10478 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
10480 /* don't scroll more than one field at a time */
10481 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
10483 /* don't scroll against the player's moving direction */
10484 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
10485 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
10486 scroll_y = old_scroll_y;
10490 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
10493 if (!options.network && game.centered_player_nr == -1 &&
10494 !AllPlayersInVisibleScreen())
10496 scroll_x = old_scroll_x;
10497 scroll_y = old_scroll_y;
10501 if (!options.network && !AllPlayersInVisibleScreen())
10503 scroll_x = old_scroll_x;
10504 scroll_y = old_scroll_y;
10509 ScrollScreen(player, SCROLL_INIT);
10510 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
10515 player->StepFrame = 0;
10517 if (moved & MP_MOVING)
10519 if (old_jx != jx && old_jy == jy)
10520 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
10521 else if (old_jx == jx && old_jy != jy)
10522 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
10524 DrawLevelField(jx, jy); /* for "crumbled sand" */
10526 player->last_move_dir = player->MovDir;
10527 player->is_moving = TRUE;
10528 player->is_snapping = FALSE;
10529 player->is_switching = FALSE;
10530 player->is_dropping = FALSE;
10531 player->is_dropping_pressed = FALSE;
10532 player->drop_pressed_delay = 0;
10536 CheckGravityMovementWhenNotMoving(player);
10538 player->is_moving = FALSE;
10540 /* at this point, the player is allowed to move, but cannot move right now
10541 (e.g. because of something blocking the way) -- ensure that the player
10542 is also allowed to move in the next frame (in old versions before 3.1.1,
10543 the player was forced to wait again for eight frames before next try) */
10545 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
10546 player->move_delay = 0; /* allow direct movement in the next frame */
10549 if (player->move_delay == -1) /* not yet initialized by DigField() */
10550 player->move_delay = player->move_delay_value;
10552 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10554 TestIfPlayerTouchesBadThing(jx, jy);
10555 TestIfPlayerTouchesCustomElement(jx, jy);
10558 if (!player->active)
10559 RemovePlayer(player);
10564 void ScrollPlayer(struct PlayerInfo *player, int mode)
10566 int jx = player->jx, jy = player->jy;
10567 int last_jx = player->last_jx, last_jy = player->last_jy;
10568 int move_stepsize = TILEX / player->move_delay_value;
10570 #if USE_NEW_PLAYER_SPEED
10571 if (!player->active)
10574 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
10577 if (!player->active || player->MovPos == 0)
10581 if (mode == SCROLL_INIT)
10583 player->actual_frame_counter = FrameCounter;
10584 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10586 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
10587 Feld[last_jx][last_jy] == EL_EMPTY)
10589 int last_field_block_delay = 0; /* start with no blocking at all */
10590 int block_delay_adjustment = player->block_delay_adjustment;
10592 /* if player blocks last field, add delay for exactly one move */
10593 if (player->block_last_field)
10595 last_field_block_delay += player->move_delay_value;
10597 /* when blocking enabled, prevent moving up despite gravity */
10598 if (game.gravity && player->MovDir == MV_UP)
10599 block_delay_adjustment = -1;
10602 /* add block delay adjustment (also possible when not blocking) */
10603 last_field_block_delay += block_delay_adjustment;
10605 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
10606 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
10609 #if USE_NEW_PLAYER_SPEED
10610 if (player->MovPos != 0) /* player has not yet reached destination */
10616 else if (!FrameReached(&player->actual_frame_counter, 1))
10620 printf("::: player->MovPos: %d -> %d\n",
10622 player->MovPos + (player->MovPos > 0 ? -1 : 1) * move_stepsize);
10625 #if USE_NEW_PLAYER_SPEED
10626 if (player->MovPos != 0)
10628 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
10629 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10631 /* before DrawPlayer() to draw correct player graphic for this case */
10632 if (player->MovPos == 0)
10633 CheckGravityMovement(player);
10636 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
10637 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10639 /* before DrawPlayer() to draw correct player graphic for this case */
10640 if (player->MovPos == 0)
10641 CheckGravityMovement(player);
10644 if (player->MovPos == 0) /* player reached destination field */
10647 printf("::: player reached destination field\n");
10650 if (player->move_delay_reset_counter > 0)
10652 player->move_delay_reset_counter--;
10654 if (player->move_delay_reset_counter == 0)
10656 /* continue with normal speed after quickly moving through gate */
10657 HALVE_PLAYER_SPEED(player);
10659 /* be able to make the next move without delay */
10660 player->move_delay = 0;
10664 player->last_jx = jx;
10665 player->last_jy = jy;
10667 if (Feld[jx][jy] == EL_EXIT_OPEN ||
10668 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
10669 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
10671 DrawPlayer(player); /* needed here only to cleanup last field */
10672 RemovePlayer(player);
10674 if (local_player->friends_still_needed == 0 ||
10675 IS_SP_ELEMENT(Feld[jx][jy]))
10676 player->LevelSolved = player->GameOver = TRUE;
10679 /* this breaks one level: "machine", level 000 */
10681 int move_direction = player->MovDir;
10682 int enter_side = MV_DIR_OPPOSITE(move_direction);
10683 int leave_side = move_direction;
10684 int old_jx = last_jx;
10685 int old_jy = last_jy;
10686 int old_element = Feld[old_jx][old_jy];
10687 int new_element = Feld[jx][jy];
10689 if (IS_CUSTOM_ELEMENT(old_element))
10690 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
10692 player->index_bit, leave_side);
10694 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
10695 CE_PLAYER_LEAVES_X,
10696 player->index_bit, leave_side);
10698 if (IS_CUSTOM_ELEMENT(new_element))
10699 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
10700 player->index_bit, enter_side);
10702 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
10703 CE_PLAYER_ENTERS_X,
10704 player->index_bit, enter_side);
10706 CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
10707 CE_MOVE_OF_X, move_direction);
10710 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10712 TestIfPlayerTouchesBadThing(jx, jy);
10713 TestIfPlayerTouchesCustomElement(jx, jy);
10715 /* needed because pushed element has not yet reached its destination,
10716 so it would trigger a change event at its previous field location */
10717 if (!player->is_pushing)
10718 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
10720 if (!player->active)
10721 RemovePlayer(player);
10724 if (level.use_step_counter)
10734 if (TimeLeft <= 10 && setup.time_limit)
10735 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
10737 DrawGameValue_Time(TimeLeft);
10739 if (!TimeLeft && setup.time_limit)
10740 for (i = 0; i < MAX_PLAYERS; i++)
10741 KillPlayer(&stored_player[i]);
10743 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
10744 DrawGameValue_Time(TimePlayed);
10747 if (tape.single_step && tape.recording && !tape.pausing &&
10748 !player->programmed_action)
10749 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10753 void ScrollScreen(struct PlayerInfo *player, int mode)
10755 static unsigned long screen_frame_counter = 0;
10757 if (mode == SCROLL_INIT)
10759 /* set scrolling step size according to actual player's moving speed */
10760 ScrollStepSize = TILEX / player->move_delay_value;
10762 screen_frame_counter = FrameCounter;
10763 ScreenMovDir = player->MovDir;
10764 ScreenMovPos = player->MovPos;
10765 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
10768 else if (!FrameReached(&screen_frame_counter, 1))
10773 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
10774 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
10775 redraw_mask |= REDRAW_FIELD;
10778 ScreenMovDir = MV_NONE;
10781 void TestIfPlayerTouchesCustomElement(int x, int y)
10783 static int xy[4][2] =
10790 static int trigger_sides[4][2] =
10792 /* center side border side */
10793 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
10794 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
10795 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
10796 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
10798 static int touch_dir[4] =
10800 MV_LEFT | MV_RIGHT,
10805 int center_element = Feld[x][y]; /* should always be non-moving! */
10808 for (i = 0; i < NUM_DIRECTIONS; i++)
10810 int xx = x + xy[i][0];
10811 int yy = y + xy[i][1];
10812 int center_side = trigger_sides[i][0];
10813 int border_side = trigger_sides[i][1];
10814 int border_element;
10816 if (!IN_LEV_FIELD(xx, yy))
10819 if (IS_PLAYER(x, y))
10821 struct PlayerInfo *player = PLAYERINFO(x, y);
10823 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10824 border_element = Feld[xx][yy]; /* may be moving! */
10825 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
10826 border_element = Feld[xx][yy];
10827 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
10828 border_element = MovingOrBlocked2Element(xx, yy);
10830 continue; /* center and border element do not touch */
10832 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
10833 player->index_bit, border_side);
10834 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
10835 CE_PLAYER_TOUCHES_X,
10836 player->index_bit, border_side);
10838 else if (IS_PLAYER(xx, yy))
10840 struct PlayerInfo *player = PLAYERINFO(xx, yy);
10842 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10844 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
10845 continue; /* center and border element do not touch */
10848 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
10849 player->index_bit, center_side);
10850 CheckTriggeredElementChangeByPlayer(x, y, center_element,
10851 CE_PLAYER_TOUCHES_X,
10852 player->index_bit, center_side);
10858 #if USE_ELEMENT_TOUCHING_BUGFIX
10860 void TestIfElementTouchesCustomElement(int x, int y)
10862 static int xy[4][2] =
10869 static int trigger_sides[4][2] =
10871 /* center side border side */
10872 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
10873 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
10874 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
10875 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
10877 static int touch_dir[4] =
10879 MV_LEFT | MV_RIGHT,
10884 boolean change_center_element = FALSE;
10885 int center_element = Feld[x][y]; /* should always be non-moving! */
10886 int border_element_old[NUM_DIRECTIONS];
10889 for (i = 0; i < NUM_DIRECTIONS; i++)
10891 int xx = x + xy[i][0];
10892 int yy = y + xy[i][1];
10893 int border_element;
10895 border_element_old[i] = -1;
10897 if (!IN_LEV_FIELD(xx, yy))
10900 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10901 border_element = Feld[xx][yy]; /* may be moving! */
10902 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
10903 border_element = Feld[xx][yy];
10904 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
10905 border_element = MovingOrBlocked2Element(xx, yy);
10907 continue; /* center and border element do not touch */
10909 border_element_old[i] = border_element;
10912 for (i = 0; i < NUM_DIRECTIONS; i++)
10914 int xx = x + xy[i][0];
10915 int yy = y + xy[i][1];
10916 int center_side = trigger_sides[i][0];
10917 int border_element = border_element_old[i];
10919 if (border_element == -1)
10922 /* check for change of border element */
10923 CheckElementChangeBySide(xx, yy, border_element, center_element,
10924 CE_TOUCHING_X, center_side);
10927 for (i = 0; i < NUM_DIRECTIONS; i++)
10929 int border_side = trigger_sides[i][1];
10930 int border_element = border_element_old[i];
10932 if (border_element == -1)
10935 /* check for change of center element (but change it only once) */
10936 if (!change_center_element)
10937 change_center_element =
10938 CheckElementChangeBySide(x, y, center_element, border_element,
10939 CE_TOUCHING_X, border_side);
10945 void TestIfElementTouchesCustomElement_OLD(int x, int y)
10947 static int xy[4][2] =
10954 static int trigger_sides[4][2] =
10956 /* center side border side */
10957 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
10958 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
10959 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
10960 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
10962 static int touch_dir[4] =
10964 MV_LEFT | MV_RIGHT,
10969 boolean change_center_element = FALSE;
10970 int center_element = Feld[x][y]; /* should always be non-moving! */
10973 for (i = 0; i < NUM_DIRECTIONS; i++)
10975 int xx = x + xy[i][0];
10976 int yy = y + xy[i][1];
10977 int center_side = trigger_sides[i][0];
10978 int border_side = trigger_sides[i][1];
10979 int border_element;
10981 if (!IN_LEV_FIELD(xx, yy))
10984 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10985 border_element = Feld[xx][yy]; /* may be moving! */
10986 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
10987 border_element = Feld[xx][yy];
10988 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
10989 border_element = MovingOrBlocked2Element(xx, yy);
10991 continue; /* center and border element do not touch */
10993 /* check for change of center element (but change it only once) */
10994 if (!change_center_element)
10995 change_center_element =
10996 CheckElementChangeBySide(x, y, center_element, border_element,
10997 CE_TOUCHING_X, border_side);
10999 /* check for change of border element */
11000 CheckElementChangeBySide(xx, yy, border_element, center_element,
11001 CE_TOUCHING_X, center_side);
11007 void TestIfElementHitsCustomElement(int x, int y, int direction)
11009 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
11010 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
11011 int hitx = x + dx, hity = y + dy;
11012 int hitting_element = Feld[x][y];
11013 int touched_element;
11015 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
11018 touched_element = (IN_LEV_FIELD(hitx, hity) ?
11019 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
11021 if (IN_LEV_FIELD(hitx, hity))
11023 int opposite_direction = MV_DIR_OPPOSITE(direction);
11024 int hitting_side = direction;
11025 int touched_side = opposite_direction;
11026 boolean object_hit = (!IS_MOVING(hitx, hity) ||
11027 MovDir[hitx][hity] != direction ||
11028 ABS(MovPos[hitx][hity]) <= TILEY / 2);
11034 CheckElementChangeBySide(x, y, hitting_element, touched_element,
11035 CE_HITTING_X, touched_side);
11037 CheckElementChangeBySide(hitx, hity, touched_element,
11038 hitting_element, CE_HIT_BY_X, hitting_side);
11040 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
11041 CE_HIT_BY_SOMETHING, opposite_direction);
11045 /* "hitting something" is also true when hitting the playfield border */
11046 CheckElementChangeBySide(x, y, hitting_element, touched_element,
11047 CE_HITTING_SOMETHING, direction);
11051 void TestIfElementSmashesCustomElement(int x, int y, int direction)
11053 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
11054 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
11055 int hitx = x + dx, hity = y + dy;
11056 int hitting_element = Feld[x][y];
11057 int touched_element;
11059 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
11060 !IS_FREE(hitx, hity) &&
11061 (!IS_MOVING(hitx, hity) ||
11062 MovDir[hitx][hity] != direction ||
11063 ABS(MovPos[hitx][hity]) <= TILEY / 2));
11066 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
11070 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
11074 touched_element = (IN_LEV_FIELD(hitx, hity) ?
11075 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
11077 CheckElementChangeBySide(x, y, hitting_element, touched_element,
11078 EP_CAN_SMASH_EVERYTHING, direction);
11080 if (IN_LEV_FIELD(hitx, hity))
11082 int opposite_direction = MV_DIR_OPPOSITE(direction);
11083 int hitting_side = direction;
11084 int touched_side = opposite_direction;
11086 int touched_element = MovingOrBlocked2Element(hitx, hity);
11089 boolean object_hit = (!IS_MOVING(hitx, hity) ||
11090 MovDir[hitx][hity] != direction ||
11091 ABS(MovPos[hitx][hity]) <= TILEY / 2);
11100 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
11101 CE_SMASHED_BY_SOMETHING, opposite_direction);
11103 CheckElementChangeBySide(x, y, hitting_element, touched_element,
11104 CE_OTHER_IS_SMASHING, touched_side);
11106 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
11107 CE_OTHER_GETS_SMASHED, hitting_side);
11113 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
11115 int i, kill_x = -1, kill_y = -1;
11117 int bad_element = -1;
11118 static int test_xy[4][2] =
11125 static int test_dir[4] =
11133 for (i = 0; i < NUM_DIRECTIONS; i++)
11135 int test_x, test_y, test_move_dir, test_element;
11137 test_x = good_x + test_xy[i][0];
11138 test_y = good_y + test_xy[i][1];
11140 if (!IN_LEV_FIELD(test_x, test_y))
11144 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
11146 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
11148 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
11149 2nd case: DONT_TOUCH style bad thing does not move away from good thing
11151 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
11152 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
11156 bad_element = test_element;
11162 if (kill_x != -1 || kill_y != -1)
11164 if (IS_PLAYER(good_x, good_y))
11166 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
11168 if (player->shield_deadly_time_left > 0 &&
11169 !IS_INDESTRUCTIBLE(bad_element))
11170 Bang(kill_x, kill_y);
11171 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
11172 KillPlayer(player);
11175 Bang(good_x, good_y);
11179 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
11181 int i, kill_x = -1, kill_y = -1;
11182 int bad_element = Feld[bad_x][bad_y];
11183 static int test_xy[4][2] =
11190 static int touch_dir[4] =
11192 MV_LEFT | MV_RIGHT,
11197 static int test_dir[4] =
11205 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
11208 for (i = 0; i < NUM_DIRECTIONS; i++)
11210 int test_x, test_y, test_move_dir, test_element;
11212 test_x = bad_x + test_xy[i][0];
11213 test_y = bad_y + test_xy[i][1];
11214 if (!IN_LEV_FIELD(test_x, test_y))
11218 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
11220 test_element = Feld[test_x][test_y];
11222 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
11223 2nd case: DONT_TOUCH style bad thing does not move away from good thing
11225 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
11226 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
11228 /* good thing is player or penguin that does not move away */
11229 if (IS_PLAYER(test_x, test_y))
11231 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
11233 if (bad_element == EL_ROBOT && player->is_moving)
11234 continue; /* robot does not kill player if he is moving */
11236 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
11238 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
11239 continue; /* center and border element do not touch */
11246 else if (test_element == EL_PENGUIN)
11255 if (kill_x != -1 || kill_y != -1)
11257 if (IS_PLAYER(kill_x, kill_y))
11259 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
11261 if (player->shield_deadly_time_left > 0 &&
11262 !IS_INDESTRUCTIBLE(bad_element))
11263 Bang(bad_x, bad_y);
11264 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
11265 KillPlayer(player);
11268 Bang(kill_x, kill_y);
11272 void TestIfPlayerTouchesBadThing(int x, int y)
11274 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
11277 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
11279 TestIfGoodThingHitsBadThing(x, y, move_dir);
11282 void TestIfBadThingTouchesPlayer(int x, int y)
11284 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
11287 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
11289 TestIfBadThingHitsGoodThing(x, y, move_dir);
11292 void TestIfFriendTouchesBadThing(int x, int y)
11294 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
11297 void TestIfBadThingTouchesFriend(int x, int y)
11299 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
11302 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
11304 int i, kill_x = bad_x, kill_y = bad_y;
11305 static int xy[4][2] =
11313 for (i = 0; i < NUM_DIRECTIONS; i++)
11317 x = bad_x + xy[i][0];
11318 y = bad_y + xy[i][1];
11319 if (!IN_LEV_FIELD(x, y))
11322 element = Feld[x][y];
11323 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
11324 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
11332 if (kill_x != bad_x || kill_y != bad_y)
11333 Bang(bad_x, bad_y);
11336 void KillPlayer(struct PlayerInfo *player)
11338 int jx = player->jx, jy = player->jy;
11340 if (!player->active)
11343 /* remove accessible field at the player's position */
11344 Feld[jx][jy] = EL_EMPTY;
11346 /* deactivate shield (else Bang()/Explode() would not work right) */
11347 player->shield_normal_time_left = 0;
11348 player->shield_deadly_time_left = 0;
11351 BuryPlayer(player);
11354 static void KillPlayerUnlessEnemyProtected(int x, int y)
11356 if (!PLAYER_ENEMY_PROTECTED(x, y))
11357 KillPlayer(PLAYERINFO(x, y));
11360 static void KillPlayerUnlessExplosionProtected(int x, int y)
11362 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
11363 KillPlayer(PLAYERINFO(x, y));
11366 void BuryPlayer(struct PlayerInfo *player)
11368 int jx = player->jx, jy = player->jy;
11370 if (!player->active)
11373 PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
11374 PlayLevelSound(jx, jy, SND_GAME_LOSING);
11376 player->GameOver = TRUE;
11377 RemovePlayer(player);
11380 void RemovePlayer(struct PlayerInfo *player)
11382 int jx = player->jx, jy = player->jy;
11383 int i, found = FALSE;
11385 player->present = FALSE;
11386 player->active = FALSE;
11388 if (!ExplodeField[jx][jy])
11389 StorePlayer[jx][jy] = 0;
11391 if (player->is_moving)
11392 DrawLevelField(player->last_jx, player->last_jy);
11394 for (i = 0; i < MAX_PLAYERS; i++)
11395 if (stored_player[i].active)
11399 AllPlayersGone = TRUE;
11405 #if USE_NEW_SNAP_DELAY
11406 static void setFieldForSnapping(int x, int y, int element, int direction)
11408 struct ElementInfo *ei = &element_info[element];
11409 int direction_bit = MV_DIR_TO_BIT(direction);
11410 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
11411 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
11412 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
11414 Feld[x][y] = EL_ELEMENT_SNAPPING;
11415 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
11417 ResetGfxAnimation(x, y);
11419 GfxElement[x][y] = element;
11420 GfxAction[x][y] = action;
11421 GfxDir[x][y] = direction;
11422 GfxFrame[x][y] = -1;
11427 =============================================================================
11428 checkDiagonalPushing()
11429 -----------------------------------------------------------------------------
11430 check if diagonal input device direction results in pushing of object
11431 (by checking if the alternative direction is walkable, diggable, ...)
11432 =============================================================================
11435 static boolean checkDiagonalPushing(struct PlayerInfo *player,
11436 int x, int y, int real_dx, int real_dy)
11438 int jx, jy, dx, dy, xx, yy;
11440 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
11443 /* diagonal direction: check alternative direction */
11448 xx = jx + (dx == 0 ? real_dx : 0);
11449 yy = jy + (dy == 0 ? real_dy : 0);
11451 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
11455 =============================================================================
11457 -----------------------------------------------------------------------------
11458 x, y: field next to player (non-diagonal) to try to dig to
11459 real_dx, real_dy: direction as read from input device (can be diagonal)
11460 =============================================================================
11463 int DigField(struct PlayerInfo *player,
11464 int oldx, int oldy, int x, int y,
11465 int real_dx, int real_dy, int mode)
11467 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
11468 boolean player_was_pushing = player->is_pushing;
11469 boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
11470 boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
11471 int jx = oldx, jy = oldy;
11472 int dx = x - jx, dy = y - jy;
11473 int nextx = x + dx, nexty = y + dy;
11474 int move_direction = (dx == -1 ? MV_LEFT :
11475 dx == +1 ? MV_RIGHT :
11477 dy == +1 ? MV_DOWN : MV_NONE);
11478 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
11479 int dig_side = MV_DIR_OPPOSITE(move_direction);
11480 int old_element = Feld[jx][jy];
11481 #if USE_FIXED_DONT_RUN_INTO
11482 int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
11488 if (is_player) /* function can also be called by EL_PENGUIN */
11490 if (player->MovPos == 0)
11492 player->is_digging = FALSE;
11493 player->is_collecting = FALSE;
11496 if (player->MovPos == 0) /* last pushing move finished */
11497 player->is_pushing = FALSE;
11499 if (mode == DF_NO_PUSH) /* player just stopped pushing */
11501 player->is_switching = FALSE;
11502 player->push_delay = -1;
11504 return MP_NO_ACTION;
11508 #if !USE_FIXED_DONT_RUN_INTO
11509 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
11510 return MP_NO_ACTION;
11513 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
11514 old_element = Back[jx][jy];
11516 /* in case of element dropped at player position, check background */
11517 else if (Back[jx][jy] != EL_EMPTY &&
11518 game.engine_version >= VERSION_IDENT(2,2,0,0))
11519 old_element = Back[jx][jy];
11522 #if USE_FIXED_DONT_RUN_INTO
11523 if (player_can_move && DONT_RUN_INTO(element))
11525 if (element == EL_ACID && dx == 0 && dy == 1)
11528 Feld[jx][jy] = EL_PLAYER_1;
11529 InitMovingField(jx, jy, MV_DOWN);
11530 Store[jx][jy] = EL_ACID;
11531 ContinueMoving(jx, jy);
11532 BuryPlayer(player);
11535 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
11537 return MP_DONT_RUN_INTO;
11543 #if USE_FIXED_DONT_RUN_INTO
11544 if (player_can_move && DONT_RUN_INTO(element))
11546 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
11548 return MP_DONT_RUN_INTO;
11553 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
11554 return MP_NO_ACTION; /* field has no opening in this direction */
11556 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
11557 return MP_NO_ACTION; /* field has no opening in this direction */
11560 #if USE_FIXED_DONT_RUN_INTO
11561 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
11564 Feld[jx][jy] = EL_PLAYER_1;
11565 InitMovingField(jx, jy, MV_DOWN);
11566 Store[jx][jy] = EL_ACID;
11567 ContinueMoving(jx, jy);
11568 BuryPlayer(player);
11570 return MP_DONT_RUN_INTO;
11576 #if USE_FIXED_DONT_RUN_INTO
11577 if (player_can_move && DONT_RUN_INTO(element))
11579 if (element == EL_ACID && dx == 0 && dy == 1)
11582 Feld[jx][jy] = EL_PLAYER_1;
11583 InitMovingField(jx, jy, MV_DOWN);
11584 Store[jx][jy] = EL_ACID;
11585 ContinueMoving(jx, jy);
11586 BuryPlayer(player);
11589 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
11591 return MP_DONT_RUN_INTO;
11596 #if USE_FIXED_DONT_RUN_INTO
11597 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
11598 return MP_NO_ACTION;
11601 #if !USE_FIXED_DONT_RUN_INTO
11602 element = Feld[x][y];
11605 collect_count = element_info[element].collect_count_initial;
11607 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
11608 return MP_NO_ACTION;
11610 if (game.engine_version < VERSION_IDENT(2,2,0,0))
11611 player_can_move = player_can_move_or_snap;
11613 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
11614 game.engine_version >= VERSION_IDENT(2,2,0,0))
11616 CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
11617 player->index_bit, dig_side);
11618 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
11619 player->index_bit, dig_side);
11621 if (Feld[x][y] != element) /* field changed by snapping */
11624 return MP_NO_ACTION;
11627 if (game.gravity && is_player && !player->is_auto_moving &&
11628 canFallDown(player) && move_direction != MV_DOWN &&
11629 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
11630 return MP_NO_ACTION; /* player cannot walk here due to gravity */
11632 if (player_can_move &&
11633 IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
11635 int sound_element = SND_ELEMENT(element);
11636 int sound_action = ACTION_WALKING;
11638 if (IS_RND_GATE(element))
11640 if (!player->key[RND_GATE_NR(element)])
11641 return MP_NO_ACTION;
11643 else if (IS_RND_GATE_GRAY(element))
11645 if (!player->key[RND_GATE_GRAY_NR(element)])
11646 return MP_NO_ACTION;
11648 else if (IS_RND_GATE_GRAY_ACTIVE(element))
11650 if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
11651 return MP_NO_ACTION;
11653 else if (element == EL_EXIT_OPEN ||
11654 element == EL_SP_EXIT_OPEN ||
11655 element == EL_SP_EXIT_OPENING)
11657 sound_action = ACTION_PASSING; /* player is passing exit */
11659 else if (element == EL_EMPTY)
11661 sound_action = ACTION_MOVING; /* nothing to walk on */
11664 /* play sound from background or player, whatever is available */
11665 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
11666 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
11668 PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
11670 else if (player_can_move &&
11671 IS_PASSABLE(element) && canPassField(x, y, move_direction))
11673 if (!ACCESS_FROM(element, opposite_direction))
11674 return MP_NO_ACTION; /* field not accessible from this direction */
11676 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
11677 return MP_NO_ACTION;
11679 if (IS_EM_GATE(element))
11681 if (!player->key[EM_GATE_NR(element)])
11682 return MP_NO_ACTION;
11684 else if (IS_EM_GATE_GRAY(element))
11686 if (!player->key[EM_GATE_GRAY_NR(element)])
11687 return MP_NO_ACTION;
11689 else if (IS_EM_GATE_GRAY_ACTIVE(element))
11691 if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
11692 return MP_NO_ACTION;
11694 else if (IS_SP_PORT(element))
11696 if (element == EL_SP_GRAVITY_PORT_LEFT ||
11697 element == EL_SP_GRAVITY_PORT_RIGHT ||
11698 element == EL_SP_GRAVITY_PORT_UP ||
11699 element == EL_SP_GRAVITY_PORT_DOWN)
11700 game.gravity = !game.gravity;
11701 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
11702 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
11703 element == EL_SP_GRAVITY_ON_PORT_UP ||
11704 element == EL_SP_GRAVITY_ON_PORT_DOWN)
11705 game.gravity = TRUE;
11706 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
11707 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
11708 element == EL_SP_GRAVITY_OFF_PORT_UP ||
11709 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
11710 game.gravity = FALSE;
11713 /* automatically move to the next field with double speed */
11714 player->programmed_action = move_direction;
11716 if (player->move_delay_reset_counter == 0)
11718 player->move_delay_reset_counter = 2; /* two double speed steps */
11720 DOUBLE_PLAYER_SPEED(player);
11723 PlayLevelSoundAction(x, y, ACTION_PASSING);
11725 else if (player_can_move_or_snap && IS_DIGGABLE(element))
11729 if (mode != DF_SNAP)
11731 GfxElement[x][y] = GFX_ELEMENT(element);
11732 player->is_digging = TRUE;
11735 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
11737 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
11738 player->index_bit, dig_side);
11740 if (mode == DF_SNAP)
11742 #if USE_NEW_SNAP_DELAY
11743 if (level.block_snap_field)
11744 setFieldForSnapping(x, y, element, move_direction);
11746 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11748 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11751 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
11752 player->index_bit, dig_side);
11755 else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
11759 if (is_player && mode != DF_SNAP)
11761 GfxElement[x][y] = element;
11762 player->is_collecting = TRUE;
11765 if (element == EL_SPEED_PILL)
11767 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
11769 else if (element == EL_EXTRA_TIME && level.time > 0)
11771 TimeLeft += level.extra_time;
11772 DrawGameValue_Time(TimeLeft);
11774 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
11776 player->shield_normal_time_left += level.shield_normal_time;
11777 if (element == EL_SHIELD_DEADLY)
11778 player->shield_deadly_time_left += level.shield_deadly_time;
11780 else if (element == EL_DYNAMITE ||
11781 element == EL_EM_DYNAMITE ||
11782 element == EL_SP_DISK_RED)
11784 if (player->inventory_size < MAX_INVENTORY_SIZE)
11785 player->inventory_element[player->inventory_size++] = element;
11788 DrawGameDoorValues();
11790 DrawGameValue_Dynamite(local_player->inventory_size);
11793 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
11795 player->dynabomb_count++;
11796 player->dynabombs_left++;
11798 else if (element == EL_DYNABOMB_INCREASE_SIZE)
11800 player->dynabomb_size++;
11802 else if (element == EL_DYNABOMB_INCREASE_POWER)
11804 player->dynabomb_xl = TRUE;
11806 else if (IS_KEY(element))
11808 player->key[KEY_NR(element)] = TRUE;
11811 DrawGameDoorValues();
11813 DrawGameValue_Keys(player->key);
11816 redraw_mask |= REDRAW_DOOR_1;
11818 else if (IS_ENVELOPE(element))
11820 player->show_envelope = element;
11822 else if (element == EL_EMC_LENSES)
11824 game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
11826 RedrawAllInvisibleElementsForLenses();
11828 else if (element == EL_EMC_MAGNIFIER)
11830 game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
11832 RedrawAllInvisibleElementsForMagnifier();
11834 else if (IS_DROPPABLE(element) ||
11835 IS_THROWABLE(element)) /* can be collected and dropped */
11839 if (collect_count == 0)
11840 player->inventory_infinite_element = element;
11842 for (i = 0; i < collect_count; i++)
11843 if (player->inventory_size < MAX_INVENTORY_SIZE)
11844 player->inventory_element[player->inventory_size++] = element;
11847 DrawGameDoorValues();
11849 DrawGameValue_Dynamite(local_player->inventory_size);
11852 else if (collect_count > 0)
11854 local_player->gems_still_needed -= collect_count;
11855 if (local_player->gems_still_needed < 0)
11856 local_player->gems_still_needed = 0;
11858 DrawGameValue_Emeralds(local_player->gems_still_needed);
11861 RaiseScoreElement(element);
11862 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
11865 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
11866 player->index_bit, dig_side);
11868 if (mode == DF_SNAP)
11870 #if USE_NEW_SNAP_DELAY
11871 if (level.block_snap_field)
11872 setFieldForSnapping(x, y, element, move_direction);
11874 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11876 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11879 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
11880 player->index_bit, dig_side);
11883 else if (player_can_move_or_snap && IS_PUSHABLE(element))
11885 if (mode == DF_SNAP && element != EL_BD_ROCK)
11886 return MP_NO_ACTION;
11888 if (CAN_FALL(element) && dy)
11889 return MP_NO_ACTION;
11891 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
11892 !(element == EL_SPRING && level.use_spring_bug))
11893 return MP_NO_ACTION;
11895 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
11896 ((move_direction & MV_VERTICAL &&
11897 ((element_info[element].move_pattern & MV_LEFT &&
11898 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
11899 (element_info[element].move_pattern & MV_RIGHT &&
11900 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
11901 (move_direction & MV_HORIZONTAL &&
11902 ((element_info[element].move_pattern & MV_UP &&
11903 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
11904 (element_info[element].move_pattern & MV_DOWN &&
11905 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
11906 return MP_NO_ACTION;
11908 /* do not push elements already moving away faster than player */
11909 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
11910 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
11911 return MP_NO_ACTION;
11913 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
11915 if (player->push_delay_value == -1 || !player_was_pushing)
11916 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11918 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11920 if (player->push_delay_value == -1)
11921 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11923 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
11925 if (!player->is_pushing)
11926 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11929 player->is_pushing = TRUE;
11931 if (!(IN_LEV_FIELD(nextx, nexty) &&
11932 (IS_FREE(nextx, nexty) ||
11933 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
11934 IS_SB_ELEMENT(element)))))
11935 return MP_NO_ACTION;
11937 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
11938 return MP_NO_ACTION;
11940 if (player->push_delay == -1) /* new pushing; restart delay */
11941 player->push_delay = 0;
11943 if (player->push_delay < player->push_delay_value &&
11944 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
11945 element != EL_SPRING && element != EL_BALLOON)
11947 /* make sure that there is no move delay before next try to push */
11948 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11949 player->move_delay = 0;
11951 return MP_NO_ACTION;
11954 if (IS_SB_ELEMENT(element))
11956 if (element == EL_SOKOBAN_FIELD_FULL)
11958 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
11959 local_player->sokobanfields_still_needed++;
11962 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
11964 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
11965 local_player->sokobanfields_still_needed--;
11968 Feld[x][y] = EL_SOKOBAN_OBJECT;
11970 if (Back[x][y] == Back[nextx][nexty])
11971 PlayLevelSoundAction(x, y, ACTION_PUSHING);
11972 else if (Back[x][y] != 0)
11973 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
11976 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
11979 if (local_player->sokobanfields_still_needed == 0 &&
11980 game.emulation == EMU_SOKOBAN)
11982 player->LevelSolved = player->GameOver = TRUE;
11983 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
11987 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
11989 InitMovingField(x, y, move_direction);
11990 GfxAction[x][y] = ACTION_PUSHING;
11992 if (mode == DF_SNAP)
11993 ContinueMoving(x, y);
11995 MovPos[x][y] = (dx != 0 ? dx : dy);
11997 Pushed[x][y] = TRUE;
11998 Pushed[nextx][nexty] = TRUE;
12000 if (game.engine_version < VERSION_IDENT(2,2,0,7))
12001 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12003 player->push_delay_value = -1; /* get new value later */
12005 /* check for element change _after_ element has been pushed */
12006 if (game.use_change_when_pushing_bug)
12008 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
12009 player->index_bit, dig_side);
12010 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
12011 player->index_bit, dig_side);
12014 else if (IS_SWITCHABLE(element))
12016 if (PLAYER_SWITCHING(player, x, y))
12018 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
12019 player->index_bit, dig_side);
12024 player->is_switching = TRUE;
12025 player->switch_x = x;
12026 player->switch_y = y;
12028 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
12030 if (element == EL_ROBOT_WHEEL)
12032 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
12036 DrawLevelField(x, y);
12038 else if (element == EL_SP_TERMINAL)
12043 SCAN_PLAYFIELD(xx, yy)
12045 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
12048 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
12050 else if (Feld[xx][yy] == EL_SP_TERMINAL)
12051 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
12054 else if (IS_BELT_SWITCH(element))
12056 ToggleBeltSwitch(x, y);
12058 else if (element == EL_SWITCHGATE_SWITCH_UP ||
12059 element == EL_SWITCHGATE_SWITCH_DOWN)
12061 ToggleSwitchgateSwitch(x, y);
12063 else if (element == EL_LIGHT_SWITCH ||
12064 element == EL_LIGHT_SWITCH_ACTIVE)
12066 ToggleLightSwitch(x, y);
12068 else if (element == EL_TIMEGATE_SWITCH)
12070 ActivateTimegateSwitch(x, y);
12072 else if (element == EL_BALLOON_SWITCH_LEFT ||
12073 element == EL_BALLOON_SWITCH_RIGHT ||
12074 element == EL_BALLOON_SWITCH_UP ||
12075 element == EL_BALLOON_SWITCH_DOWN ||
12076 element == EL_BALLOON_SWITCH_NONE ||
12077 element == EL_BALLOON_SWITCH_ANY)
12079 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
12080 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
12081 element == EL_BALLOON_SWITCH_UP ? MV_UP :
12082 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
12083 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
12086 else if (element == EL_LAMP)
12088 Feld[x][y] = EL_LAMP_ACTIVE;
12089 local_player->lights_still_needed--;
12091 ResetGfxAnimation(x, y);
12092 DrawLevelField(x, y);
12094 else if (element == EL_TIME_ORB_FULL)
12096 Feld[x][y] = EL_TIME_ORB_EMPTY;
12098 if (level.time > 0 || level.use_time_orb_bug)
12100 TimeLeft += level.time_orb_time;
12101 DrawGameValue_Time(TimeLeft);
12104 ResetGfxAnimation(x, y);
12105 DrawLevelField(x, y);
12107 else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
12108 element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
12112 game.ball_state = !game.ball_state;
12115 SCAN_PLAYFIELD(xx, yy)
12117 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
12120 int e = Feld[xx][yy];
12122 if (game.ball_state)
12124 if (e == EL_EMC_MAGIC_BALL)
12125 CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
12126 else if (e == EL_EMC_MAGIC_BALL_SWITCH)
12127 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
12131 if (e == EL_EMC_MAGIC_BALL_ACTIVE)
12132 CreateField(xx, yy, EL_EMC_MAGIC_BALL);
12133 else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
12134 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
12139 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
12140 player->index_bit, dig_side);
12142 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
12143 player->index_bit, dig_side);
12145 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
12146 player->index_bit, dig_side);
12152 if (!PLAYER_SWITCHING(player, x, y))
12154 player->is_switching = TRUE;
12155 player->switch_x = x;
12156 player->switch_y = y;
12158 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
12159 player->index_bit, dig_side);
12160 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
12161 player->index_bit, dig_side);
12163 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
12164 player->index_bit, dig_side);
12165 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
12166 player->index_bit, dig_side);
12169 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
12170 player->index_bit, dig_side);
12171 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
12172 player->index_bit, dig_side);
12174 return MP_NO_ACTION;
12177 player->push_delay = -1;
12179 if (is_player) /* function can also be called by EL_PENGUIN */
12181 if (Feld[x][y] != element) /* really digged/collected something */
12182 player->is_collecting = !player->is_digging;
12188 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
12190 int jx = player->jx, jy = player->jy;
12191 int x = jx + dx, y = jy + dy;
12192 int snap_direction = (dx == -1 ? MV_LEFT :
12193 dx == +1 ? MV_RIGHT :
12195 dy == +1 ? MV_DOWN : MV_NONE);
12196 boolean can_continue_snapping = (level.continuous_snapping &&
12197 WasJustFalling[x][y] < CHECK_DELAY_FALLING);
12199 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
12202 if (!player->active || !IN_LEV_FIELD(x, y))
12210 if (player->MovPos == 0)
12211 player->is_pushing = FALSE;
12213 player->is_snapping = FALSE;
12215 if (player->MovPos == 0)
12217 player->is_moving = FALSE;
12218 player->is_digging = FALSE;
12219 player->is_collecting = FALSE;
12225 #if USE_NEW_CONTINUOUS_SNAPPING
12226 /* prevent snapping with already pressed snap key when not allowed */
12227 if (player->is_snapping && !can_continue_snapping)
12230 if (player->is_snapping)
12234 player->MovDir = snap_direction;
12236 if (player->MovPos == 0)
12238 player->is_moving = FALSE;
12239 player->is_digging = FALSE;
12240 player->is_collecting = FALSE;
12243 player->is_dropping = FALSE;
12244 player->is_dropping_pressed = FALSE;
12245 player->drop_pressed_delay = 0;
12247 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
12250 player->is_snapping = TRUE;
12252 if (player->MovPos == 0)
12254 player->is_moving = FALSE;
12255 player->is_digging = FALSE;
12256 player->is_collecting = FALSE;
12259 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
12260 DrawLevelField(player->last_jx, player->last_jy);
12262 DrawLevelField(x, y);
12267 boolean DropElement(struct PlayerInfo *player)
12269 int old_element, new_element;
12270 int dropx = player->jx, dropy = player->jy;
12271 int drop_direction = player->MovDir;
12272 int drop_side = drop_direction;
12273 int drop_element = (player->inventory_size > 0 ?
12274 player->inventory_element[player->inventory_size - 1] :
12275 player->inventory_infinite_element != EL_UNDEFINED ?
12276 player->inventory_infinite_element :
12277 player->dynabombs_left > 0 ?
12278 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
12281 player->is_dropping_pressed = TRUE;
12283 /* do not drop an element on top of another element; when holding drop key
12284 pressed without moving, dropped element must move away before the next
12285 element can be dropped (this is especially important if the next element
12286 is dynamite, which can be placed on background for historical reasons) */
12287 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
12290 if (IS_THROWABLE(drop_element))
12292 dropx += GET_DX_FROM_DIR(drop_direction);
12293 dropy += GET_DY_FROM_DIR(drop_direction);
12295 if (!IN_LEV_FIELD(dropx, dropy))
12299 old_element = Feld[dropx][dropy]; /* old element at dropping position */
12300 new_element = drop_element; /* default: no change when dropping */
12302 /* check if player is active, not moving and ready to drop */
12303 if (!player->active || player->MovPos || player->drop_delay > 0)
12306 /* check if player has anything that can be dropped */
12307 if (new_element == EL_UNDEFINED)
12310 /* check if drop key was pressed long enough for EM style dynamite */
12311 if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
12314 /* check if anything can be dropped at the current position */
12315 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
12318 /* collected custom elements can only be dropped on empty fields */
12319 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
12322 if (old_element != EL_EMPTY)
12323 Back[dropx][dropy] = old_element; /* store old element on this field */
12325 ResetGfxAnimation(dropx, dropy);
12326 ResetRandomAnimationValue(dropx, dropy);
12328 if (player->inventory_size > 0 ||
12329 player->inventory_infinite_element != EL_UNDEFINED)
12331 if (player->inventory_size > 0)
12333 player->inventory_size--;
12336 DrawGameDoorValues();
12338 DrawGameValue_Dynamite(local_player->inventory_size);
12341 if (new_element == EL_DYNAMITE)
12342 new_element = EL_DYNAMITE_ACTIVE;
12343 else if (new_element == EL_EM_DYNAMITE)
12344 new_element = EL_EM_DYNAMITE_ACTIVE;
12345 else if (new_element == EL_SP_DISK_RED)
12346 new_element = EL_SP_DISK_RED_ACTIVE;
12349 Feld[dropx][dropy] = new_element;
12351 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
12352 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
12353 el2img(Feld[dropx][dropy]), 0);
12355 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
12357 /* needed if previous element just changed to "empty" in the last frame */
12358 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
12360 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
12361 player->index_bit, drop_side);
12362 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
12364 player->index_bit, drop_side);
12366 TestIfElementTouchesCustomElement(dropx, dropy);
12368 else /* player is dropping a dyna bomb */
12370 player->dynabombs_left--;
12372 Feld[dropx][dropy] = new_element;
12374 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
12375 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
12376 el2img(Feld[dropx][dropy]), 0);
12378 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
12381 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
12382 InitField_WithBug1(dropx, dropy, FALSE);
12384 new_element = Feld[dropx][dropy]; /* element might have changed */
12386 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
12387 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
12389 int move_direction, nextx, nexty;
12391 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
12392 MovDir[dropx][dropy] = drop_direction;
12394 move_direction = MovDir[dropx][dropy];
12395 nextx = dropx + GET_DX_FROM_DIR(move_direction);
12396 nexty = dropy + GET_DY_FROM_DIR(move_direction);
12398 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
12399 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
12402 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
12403 player->is_dropping = TRUE;
12405 player->drop_pressed_delay = 0;
12406 player->is_dropping_pressed = FALSE;
12408 player->drop_x = dropx;
12409 player->drop_y = dropy;
12414 /* ------------------------------------------------------------------------- */
12415 /* game sound playing functions */
12416 /* ------------------------------------------------------------------------- */
12418 static int *loop_sound_frame = NULL;
12419 static int *loop_sound_volume = NULL;
12421 void InitPlayLevelSound()
12423 int num_sounds = getSoundListSize();
12425 checked_free(loop_sound_frame);
12426 checked_free(loop_sound_volume);
12428 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
12429 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
12432 static void PlayLevelSound(int x, int y, int nr)
12434 int sx = SCREENX(x), sy = SCREENY(y);
12435 int volume, stereo_position;
12436 int max_distance = 8;
12437 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
12439 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
12440 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
12443 if (!IN_LEV_FIELD(x, y) ||
12444 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
12445 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
12448 volume = SOUND_MAX_VOLUME;
12450 if (!IN_SCR_FIELD(sx, sy))
12452 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
12453 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
12455 volume -= volume * (dx > dy ? dx : dy) / max_distance;
12458 stereo_position = (SOUND_MAX_LEFT +
12459 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
12460 (SCR_FIELDX + 2 * max_distance));
12462 if (IS_LOOP_SOUND(nr))
12464 /* This assures that quieter loop sounds do not overwrite louder ones,
12465 while restarting sound volume comparison with each new game frame. */
12467 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
12470 loop_sound_volume[nr] = volume;
12471 loop_sound_frame[nr] = FrameCounter;
12474 PlaySoundExt(nr, volume, stereo_position, type);
12477 static void PlayLevelSoundNearest(int x, int y, int sound_action)
12479 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
12480 x > LEVELX(BX2) ? LEVELX(BX2) : x,
12481 y < LEVELY(BY1) ? LEVELY(BY1) :
12482 y > LEVELY(BY2) ? LEVELY(BY2) : y,
12486 static void PlayLevelSoundAction(int x, int y, int action)
12488 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
12491 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
12493 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
12495 if (sound_effect != SND_UNDEFINED)
12496 PlayLevelSound(x, y, sound_effect);
12499 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
12502 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
12504 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12505 PlayLevelSound(x, y, sound_effect);
12508 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
12510 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
12512 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12513 PlayLevelSound(x, y, sound_effect);
12516 static void StopLevelSoundActionIfLoop(int x, int y, int action)
12518 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
12520 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12521 StopSound(sound_effect);
12524 static void PlayLevelMusic()
12526 if (levelset.music[level_nr] != MUS_UNDEFINED)
12527 PlayMusic(levelset.music[level_nr]); /* from config file */
12529 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
12532 void PlayLevelSound_EM(int x, int y, int element_em, int sample)
12534 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
12539 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
12543 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12547 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12551 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12555 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
12559 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12563 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12566 case SAMPLE_android_clone:
12567 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12570 case SAMPLE_android_move:
12571 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12574 case SAMPLE_spring:
12575 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12579 PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
12583 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
12586 case SAMPLE_eater_eat:
12587 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
12591 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12594 case SAMPLE_collect:
12595 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
12598 case SAMPLE_diamond:
12599 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12602 case SAMPLE_squash:
12603 /* !!! CHECK THIS !!! */
12605 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
12607 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
12611 case SAMPLE_wonderfall:
12612 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
12616 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12620 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12624 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
12628 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
12632 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12636 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
12639 case SAMPLE_wonder:
12640 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12644 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
12647 case SAMPLE_exit_open:
12648 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
12651 case SAMPLE_exit_leave:
12652 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
12655 case SAMPLE_dynamite:
12656 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12660 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12664 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
12668 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12672 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
12676 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
12680 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
12684 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
12689 void RaiseScore(int value)
12691 local_player->score += value;
12693 DrawGameValue_Score(local_player->score);
12696 void RaiseScoreElement(int element)
12701 case EL_BD_DIAMOND:
12702 case EL_EMERALD_YELLOW:
12703 case EL_EMERALD_RED:
12704 case EL_EMERALD_PURPLE:
12705 case EL_SP_INFOTRON:
12706 RaiseScore(level.score[SC_EMERALD]);
12709 RaiseScore(level.score[SC_DIAMOND]);
12712 RaiseScore(level.score[SC_CRYSTAL]);
12715 RaiseScore(level.score[SC_PEARL]);
12718 case EL_BD_BUTTERFLY:
12719 case EL_SP_ELECTRON:
12720 RaiseScore(level.score[SC_BUG]);
12723 case EL_BD_FIREFLY:
12724 case EL_SP_SNIKSNAK:
12725 RaiseScore(level.score[SC_SPACESHIP]);
12728 case EL_DARK_YAMYAM:
12729 RaiseScore(level.score[SC_YAMYAM]);
12732 RaiseScore(level.score[SC_ROBOT]);
12735 RaiseScore(level.score[SC_PACMAN]);
12738 RaiseScore(level.score[SC_NUT]);
12741 case EL_EM_DYNAMITE:
12742 case EL_SP_DISK_RED:
12743 case EL_DYNABOMB_INCREASE_NUMBER:
12744 case EL_DYNABOMB_INCREASE_SIZE:
12745 case EL_DYNABOMB_INCREASE_POWER:
12746 RaiseScore(level.score[SC_DYNAMITE]);
12748 case EL_SHIELD_NORMAL:
12749 case EL_SHIELD_DEADLY:
12750 RaiseScore(level.score[SC_SHIELD]);
12752 case EL_EXTRA_TIME:
12753 RaiseScore(level.extra_time_score);
12767 RaiseScore(level.score[SC_KEY]);
12770 RaiseScore(element_info[element].collect_score);
12775 void RequestQuitGame(boolean ask_if_really_quit)
12777 if (AllPlayersGone ||
12778 !ask_if_really_quit ||
12779 level_editor_test_game ||
12780 Request("Do you really want to quit the game ?",
12781 REQ_ASK | REQ_STAY_CLOSED))
12783 #if defined(NETWORK_AVALIABLE)
12784 if (options.network)
12785 SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
12789 game_status = GAME_MODE_MAIN;
12795 if (tape.playing && tape.deactivate_display)
12796 TapeDeactivateDisplayOff(TRUE);
12798 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
12800 if (tape.playing && tape.deactivate_display)
12801 TapeDeactivateDisplayOn();
12806 /* ---------- new game button stuff ---------------------------------------- */
12808 /* graphic position values for game buttons */
12809 #define GAME_BUTTON_XSIZE 30
12810 #define GAME_BUTTON_YSIZE 30
12811 #define GAME_BUTTON_XPOS 5
12812 #define GAME_BUTTON_YPOS 215
12813 #define SOUND_BUTTON_XPOS 5
12814 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
12816 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
12817 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
12818 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
12819 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
12820 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
12821 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
12828 } gamebutton_info[NUM_GAME_BUTTONS] =
12831 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
12836 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
12837 GAME_CTRL_ID_PAUSE,
12841 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
12846 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
12847 SOUND_CTRL_ID_MUSIC,
12848 "background music on/off"
12851 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
12852 SOUND_CTRL_ID_LOOPS,
12853 "sound loops on/off"
12856 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
12857 SOUND_CTRL_ID_SIMPLE,
12858 "normal sounds on/off"
12862 void CreateGameButtons()
12866 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12868 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
12869 struct GadgetInfo *gi;
12872 unsigned long event_mask;
12873 int gd_xoffset, gd_yoffset;
12874 int gd_x1, gd_x2, gd_y1, gd_y2;
12877 gd_xoffset = gamebutton_info[i].x;
12878 gd_yoffset = gamebutton_info[i].y;
12879 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
12880 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
12882 if (id == GAME_CTRL_ID_STOP ||
12883 id == GAME_CTRL_ID_PAUSE ||
12884 id == GAME_CTRL_ID_PLAY)
12886 button_type = GD_TYPE_NORMAL_BUTTON;
12888 event_mask = GD_EVENT_RELEASED;
12889 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12890 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12894 button_type = GD_TYPE_CHECK_BUTTON;
12896 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
12897 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
12898 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
12899 event_mask = GD_EVENT_PRESSED;
12900 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
12901 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12904 gi = CreateGadget(GDI_CUSTOM_ID, id,
12905 GDI_INFO_TEXT, gamebutton_info[i].infotext,
12906 GDI_X, DX + gd_xoffset,
12907 GDI_Y, DY + gd_yoffset,
12908 GDI_WIDTH, GAME_BUTTON_XSIZE,
12909 GDI_HEIGHT, GAME_BUTTON_YSIZE,
12910 GDI_TYPE, button_type,
12911 GDI_STATE, GD_BUTTON_UNPRESSED,
12912 GDI_CHECKED, checked,
12913 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
12914 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
12915 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
12916 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
12917 GDI_EVENT_MASK, event_mask,
12918 GDI_CALLBACK_ACTION, HandleGameButtons,
12922 Error(ERR_EXIT, "cannot create gadget");
12924 game_gadget[id] = gi;
12928 void FreeGameButtons()
12932 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12933 FreeGadget(game_gadget[i]);
12936 static void MapGameButtons()
12940 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12941 MapGadget(game_gadget[i]);
12944 void UnmapGameButtons()
12948 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12949 UnmapGadget(game_gadget[i]);
12952 static void HandleGameButtons(struct GadgetInfo *gi)
12954 int id = gi->custom_id;
12956 if (game_status != GAME_MODE_PLAYING)
12961 case GAME_CTRL_ID_STOP:
12965 RequestQuitGame(TRUE);
12968 case GAME_CTRL_ID_PAUSE:
12969 if (options.network)
12971 #if defined(NETWORK_AVALIABLE)
12973 SendToServer_ContinuePlaying();
12975 SendToServer_PausePlaying();
12979 TapeTogglePause(TAPE_TOGGLE_MANUAL);
12982 case GAME_CTRL_ID_PLAY:
12985 #if defined(NETWORK_AVALIABLE)
12986 if (options.network)
12987 SendToServer_ContinuePlaying();
12991 tape.pausing = FALSE;
12992 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
12997 case SOUND_CTRL_ID_MUSIC:
12998 if (setup.sound_music)
13000 setup.sound_music = FALSE;
13003 else if (audio.music_available)
13005 setup.sound = setup.sound_music = TRUE;
13007 SetAudioMode(setup.sound);
13013 case SOUND_CTRL_ID_LOOPS:
13014 if (setup.sound_loops)
13015 setup.sound_loops = FALSE;
13016 else if (audio.loops_available)
13018 setup.sound = setup.sound_loops = TRUE;
13019 SetAudioMode(setup.sound);
13023 case SOUND_CTRL_ID_SIMPLE:
13024 if (setup.sound_simple)
13025 setup.sound_simple = FALSE;
13026 else if (audio.sound_available)
13028 setup.sound = setup.sound_simple = TRUE;
13029 SetAudioMode(setup.sound);