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)
43 #define USE_QUICKSAND_IMPACT_BUGFIX (USE_NEW_STUFF * 0)
50 /* for MovePlayer() */
51 #define MP_NO_ACTION 0
54 #define MP_DONT_RUN_INTO (MP_MOVING | MP_ACTION)
56 /* for ScrollPlayer() */
58 #define SCROLL_GO_ON 1
60 /* for Bang()/Explode() */
61 #define EX_PHASE_START 0
62 #define EX_TYPE_NONE 0
63 #define EX_TYPE_NORMAL (1 << 0)
64 #define EX_TYPE_CENTER (1 << 1)
65 #define EX_TYPE_BORDER (1 << 2)
66 #define EX_TYPE_CROSS (1 << 3)
67 #define EX_TYPE_DYNA (1 << 4)
68 #define EX_TYPE_SINGLE_TILE (EX_TYPE_CENTER | EX_TYPE_BORDER)
70 /* special positions in the game control window (relative to control window) */
73 #define XX_EMERALDS 29
74 #define YY_EMERALDS 54
75 #define XX_DYNAMITE 29
76 #define YY_DYNAMITE 89
85 /* special positions in the game control window (relative to main window) */
86 #define DX_LEVEL (DX + XX_LEVEL)
87 #define DY_LEVEL (DY + YY_LEVEL)
88 #define DX_EMERALDS (DX + XX_EMERALDS)
89 #define DY_EMERALDS (DY + YY_EMERALDS)
90 #define DX_DYNAMITE (DX + XX_DYNAMITE)
91 #define DY_DYNAMITE (DY + YY_DYNAMITE)
92 #define DX_KEYS (DX + XX_KEYS)
93 #define DY_KEYS (DY + YY_KEYS)
94 #define DX_SCORE (DX + XX_SCORE)
95 #define DY_SCORE (DY + YY_SCORE)
96 #define DX_TIME1 (DX + XX_TIME1)
97 #define DX_TIME2 (DX + XX_TIME2)
98 #define DY_TIME (DY + YY_TIME)
100 /* values for initial player move delay (initial delay counter value) */
101 #define INITIAL_MOVE_DELAY_OFF -1
102 #define INITIAL_MOVE_DELAY_ON 0
104 /* values for player movement speed (which is in fact a delay value) */
105 #define MOVE_DELAY_MIN_SPEED 32
106 #define MOVE_DELAY_NORMAL_SPEED 8
107 #define MOVE_DELAY_HIGH_SPEED 4
108 #define MOVE_DELAY_MAX_SPEED 1
111 #define DOUBLE_MOVE_DELAY(x) (x = (x <= MOVE_DELAY_HIGH_SPEED ? x * 2 : x))
112 #define HALVE_MOVE_DELAY(x) (x = (x >= MOVE_DELAY_HIGH_SPEED ? x / 2 : x))
114 #define DOUBLE_MOVE_DELAY(x) (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
115 #define HALVE_MOVE_DELAY(x) (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
117 #define DOUBLE_PLAYER_SPEED(p) (HALVE_MOVE_DELAY((p)->move_delay_value))
118 #define HALVE_PLAYER_SPEED(p) (DOUBLE_MOVE_DELAY((p)->move_delay_value))
120 /* values for other actions */
121 #define MOVE_STEPSIZE_NORMAL (TILEX / MOVE_DELAY_NORMAL_SPEED)
122 #define MOVE_STEPSIZE_MIN (1)
123 #define MOVE_STEPSIZE_MAX (TILEX)
125 #define GET_DX_FROM_DIR(d) ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
126 #define GET_DY_FROM_DIR(d) ((d) == MV_UP ? -1 : (d) == MV_DOWN ? 1 : 0)
128 #define INIT_GFX_RANDOM() (SimpleRND(1000000))
130 #define GET_NEW_PUSH_DELAY(e) ( (element_info[e].push_delay_fixed) + \
131 RND(element_info[e].push_delay_random))
132 #define GET_NEW_DROP_DELAY(e) ( (element_info[e].drop_delay_fixed) + \
133 RND(element_info[e].drop_delay_random))
134 #define GET_NEW_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
135 RND(element_info[e].move_delay_random))
136 #define GET_MAX_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
137 (element_info[e].move_delay_random))
138 #define GET_NEW_CUSTOM_VALUE(e) ( (element_info[e].ce_value_fixed_initial) +\
139 RND(element_info[e].ce_value_random_initial))
140 #define GET_CHANGE_DELAY(c) ( ((c)->delay_fixed * (c)->delay_frames) + \
141 RND((c)->delay_random * (c)->delay_frames))
142 #define GET_CE_DELAY_VALUE(c) ( ((c)->delay_fixed) + \
143 RND((c)->delay_random))
146 #define GET_VALID_RUNTIME_ELEMENT(e) \
147 ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
149 #define GET_VALID_FILE_ELEMENT(e) \
150 ((e) >= NUM_FILE_ELEMENTS ? EL_UNKNOWN : (e))
153 #define GET_TARGET_ELEMENT(e, ch) \
154 ((e) == EL_TRIGGER_PLAYER ? (ch)->actual_trigger_player : \
155 (e) == EL_TRIGGER_ELEMENT ? (ch)->actual_trigger_element : \
156 (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value : (e))
158 #define CAN_GROW_INTO(e) \
159 ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
161 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition) \
162 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
165 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition) \
166 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
167 (CAN_MOVE_INTO_ACID(e) && \
168 Feld[x][y] == EL_ACID) || \
171 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition) \
172 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
173 (CAN_MOVE_INTO_ACID(e) && \
174 Feld[x][y] == EL_ACID) || \
177 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition) \
178 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
180 (CAN_MOVE_INTO_ACID(e) && \
181 Feld[x][y] == EL_ACID) || \
182 (DONT_COLLIDE_WITH(e) && \
184 !PLAYER_ENEMY_PROTECTED(x, y))))
186 #define ELEMENT_CAN_ENTER_FIELD(e, x, y) \
187 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
189 #define SATELLITE_CAN_ENTER_FIELD(x, y) \
190 ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
192 #define ANDROID_CAN_ENTER_FIELD(e, x, y) \
193 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
195 #define ANDROID_CAN_CLONE_FIELD(x, y) \
196 (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
197 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
199 #define ENEMY_CAN_ENTER_FIELD(e, x, y) \
200 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
202 #define YAMYAM_CAN_ENTER_FIELD(e, x, y) \
203 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
205 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y) \
206 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
208 #define PACMAN_CAN_ENTER_FIELD(e, x, y) \
209 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
211 #define PIG_CAN_ENTER_FIELD(e, x, y) \
212 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
214 #define PENGUIN_CAN_ENTER_FIELD(e, x, y) \
215 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN ||\
216 IS_FOOD_PENGUIN(Feld[x][y])))
217 #define DRAGON_CAN_ENTER_FIELD(e, x, y) \
218 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
220 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition) \
221 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
223 #define SPRING_CAN_ENTER_FIELD(e, x, y) \
224 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
226 #define SPRING_CAN_BUMP_FROM_FIELD(x, y) \
227 (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER || \
228 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
231 #define GROUP_NR(e) ((e) - EL_GROUP_START)
232 #define IS_IN_GROUP(e, nr) (element_info[e].in_group[nr] == TRUE)
233 #define IS_IN_GROUP_EL(e, ge) (IS_IN_GROUP(e, (ge) - EL_GROUP_START))
235 #define IS_EQUAL_OR_IN_GROUP(e, ge) \
236 (IS_GROUP_ELEMENT(ge) ? IS_IN_GROUP(e, GROUP_NR(ge)) : (e) == (ge))
239 #define MOVE_ENTER_EL(e) (element_info[e].move_enter_element)
241 #define CE_ENTER_FIELD_COND(e, x, y) \
242 (!IS_PLAYER(x, y) && \
243 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
245 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y) \
246 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
248 #define IN_LEV_FIELD_AND_IS_FREE(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
249 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
251 #define ACCESS_FROM(e, d) (element_info[e].access_direction &(d))
252 #define IS_WALKABLE_FROM(e, d) (IS_WALKABLE(e) && ACCESS_FROM(e, d))
253 #define IS_PASSABLE_FROM(e, d) (IS_PASSABLE(e) && ACCESS_FROM(e, d))
254 #define IS_ACCESSIBLE_FROM(e, d) (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
256 /* game button identifiers */
257 #define GAME_CTRL_ID_STOP 0
258 #define GAME_CTRL_ID_PAUSE 1
259 #define GAME_CTRL_ID_PLAY 2
260 #define SOUND_CTRL_ID_MUSIC 3
261 #define SOUND_CTRL_ID_LOOPS 4
262 #define SOUND_CTRL_ID_SIMPLE 5
264 #define NUM_GAME_BUTTONS 6
267 /* forward declaration for internal use */
269 static void CreateField(int, int, int);
271 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
272 static void AdvanceFrameAndPlayerCounters(int);
274 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
275 static boolean MovePlayer(struct PlayerInfo *, int, int);
276 static void ScrollPlayer(struct PlayerInfo *, int);
277 static void ScrollScreen(struct PlayerInfo *, int);
279 int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
281 static void InitBeltMovement(void);
282 static void CloseAllOpenTimegates(void);
283 static void CheckGravityMovement(struct PlayerInfo *);
284 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
285 static void KillPlayerUnlessEnemyProtected(int, int);
286 static void KillPlayerUnlessExplosionProtected(int, int);
288 static void TestIfPlayerTouchesCustomElement(int, int);
289 static void TestIfElementTouchesCustomElement(int, int);
290 static void TestIfElementHitsCustomElement(int, int, int);
292 static void TestIfElementSmashesCustomElement(int, int, int);
295 static void HandleElementChange(int, int, int);
297 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
298 #define CheckTriggeredElementChange(x, y, e, ev) \
299 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
300 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s) \
301 CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
302 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s) \
303 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
304 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p) \
305 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
307 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
308 #define CheckElementChange(x, y, e, te, ev) \
309 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
310 #define CheckElementChangeByPlayer(x, y, e, ev, p, s) \
311 CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
312 #define CheckElementChangeBySide(x, y, e, te, ev, s) \
313 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
315 static void PlayLevelSound(int, int, int);
316 static void PlayLevelSoundNearest(int, int, int);
317 static void PlayLevelSoundAction(int, int, int);
318 static void PlayLevelSoundElementAction(int, int, int, int);
319 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
320 static void PlayLevelSoundActionIfLoop(int, int, int);
321 static void StopLevelSoundActionIfLoop(int, int, int);
322 static void PlayLevelMusic();
324 static void MapGameButtons();
325 static void HandleGameButtons(struct GadgetInfo *);
327 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
330 /* ------------------------------------------------------------------------- */
331 /* definition of elements that automatically change to other elements after */
332 /* a specified time, eventually calling a function when changing */
333 /* ------------------------------------------------------------------------- */
335 /* forward declaration for changer functions */
336 static void InitBuggyBase(int, int);
337 static void WarnBuggyBase(int, int);
339 static void InitTrap(int, int);
340 static void ActivateTrap(int, int);
341 static void ChangeActiveTrap(int, int);
343 static void InitRobotWheel(int, int);
344 static void RunRobotWheel(int, int);
345 static void StopRobotWheel(int, int);
347 static void InitTimegateWheel(int, int);
348 static void RunTimegateWheel(int, int);
350 static void InitMagicBallDelay(int, int);
351 static void ActivateMagicBall(int, int);
353 static void InitDiagonalMovingElement(int, int);
355 struct ChangingElementInfo
360 void (*pre_change_function)(int x, int y);
361 void (*change_function)(int x, int y);
362 void (*post_change_function)(int x, int y);
365 static struct ChangingElementInfo change_delay_list[] =
416 EL_SWITCHGATE_OPENING,
424 EL_SWITCHGATE_CLOSING,
425 EL_SWITCHGATE_CLOSED,
457 EL_ACID_SPLASH_RIGHT,
466 EL_SP_BUGGY_BASE_ACTIVATING,
473 EL_SP_BUGGY_BASE_ACTIVATING,
474 EL_SP_BUGGY_BASE_ACTIVE,
481 EL_SP_BUGGY_BASE_ACTIVE,
505 EL_ROBOT_WHEEL_ACTIVE,
513 EL_TIMEGATE_SWITCH_ACTIVE,
521 EL_EMC_MAGIC_BALL_ACTIVE,
522 EL_EMC_MAGIC_BALL_ACTIVE,
529 EL_EMC_SPRING_BUMPER_ACTIVE,
530 EL_EMC_SPRING_BUMPER,
537 EL_DIAGONAL_SHRINKING,
550 InitDiagonalMovingElement
566 int push_delay_fixed, push_delay_random;
571 { EL_BALLOON, 0, 0 },
573 { EL_SOKOBAN_OBJECT, 2, 0 },
574 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
575 { EL_SATELLITE, 2, 0 },
576 { EL_SP_DISK_YELLOW, 2, 0 },
578 { EL_UNDEFINED, 0, 0 },
586 move_stepsize_list[] =
588 { EL_AMOEBA_DROP, 2 },
589 { EL_AMOEBA_DROPPING, 2 },
590 { EL_QUICKSAND_FILLING, 1 },
591 { EL_QUICKSAND_EMPTYING, 1 },
592 { EL_MAGIC_WALL_FILLING, 2 },
593 { EL_BD_MAGIC_WALL_FILLING, 2 },
594 { EL_MAGIC_WALL_EMPTYING, 2 },
595 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
605 collect_count_list[] =
608 { EL_BD_DIAMOND, 1 },
609 { EL_EMERALD_YELLOW, 1 },
610 { EL_EMERALD_RED, 1 },
611 { EL_EMERALD_PURPLE, 1 },
613 { EL_SP_INFOTRON, 1 },
625 access_direction_list[] =
627 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
628 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
629 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
630 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
631 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
632 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
633 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
634 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
635 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
636 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
637 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
639 { EL_SP_PORT_LEFT, MV_RIGHT },
640 { EL_SP_PORT_RIGHT, MV_LEFT },
641 { EL_SP_PORT_UP, MV_DOWN },
642 { EL_SP_PORT_DOWN, MV_UP },
643 { EL_SP_PORT_HORIZONTAL, MV_LEFT | MV_RIGHT },
644 { EL_SP_PORT_VERTICAL, MV_UP | MV_DOWN },
645 { EL_SP_PORT_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
646 { EL_SP_GRAVITY_PORT_LEFT, MV_RIGHT },
647 { EL_SP_GRAVITY_PORT_RIGHT, MV_LEFT },
648 { EL_SP_GRAVITY_PORT_UP, MV_DOWN },
649 { EL_SP_GRAVITY_PORT_DOWN, MV_UP },
650 { EL_SP_GRAVITY_ON_PORT_LEFT, MV_RIGHT },
651 { EL_SP_GRAVITY_ON_PORT_RIGHT, MV_LEFT },
652 { EL_SP_GRAVITY_ON_PORT_UP, MV_DOWN },
653 { EL_SP_GRAVITY_ON_PORT_DOWN, MV_UP },
654 { EL_SP_GRAVITY_OFF_PORT_LEFT, MV_RIGHT },
655 { EL_SP_GRAVITY_OFF_PORT_RIGHT, MV_LEFT },
656 { EL_SP_GRAVITY_OFF_PORT_UP, MV_DOWN },
657 { EL_SP_GRAVITY_OFF_PORT_DOWN, MV_UP },
659 { EL_UNDEFINED, MV_NONE }
662 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
664 #define IS_AUTO_CHANGING(e) (element_info[e].has_change_event[CE_DELAY])
665 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
666 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
667 IS_JUST_CHANGING(x, y))
669 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
671 /* static variables for playfield scan mode (scanning forward or backward) */
672 static int playfield_scan_start_x = 0;
673 static int playfield_scan_start_y = 0;
674 static int playfield_scan_delta_x = 1;
675 static int playfield_scan_delta_y = 1;
677 #define SCAN_PLAYFIELD(x, y) for ((y) = playfield_scan_start_y; \
678 (y) >= 0 && (y) <= lev_fieldy - 1; \
679 (y) += playfield_scan_delta_y) \
680 for ((x) = playfield_scan_start_x; \
681 (x) >= 0 && (x) <= lev_fieldx - 1; \
682 (x) += playfield_scan_delta_x) \
684 static void InitPlayfieldScanModeVars()
686 if (game.use_reverse_scan_direction)
688 playfield_scan_start_x = lev_fieldx - 1;
689 playfield_scan_start_y = lev_fieldy - 1;
691 playfield_scan_delta_x = -1;
692 playfield_scan_delta_y = -1;
696 playfield_scan_start_x = 0;
697 playfield_scan_start_y = 0;
699 playfield_scan_delta_x = 1;
700 playfield_scan_delta_y = 1;
704 static void InitPlayfieldScanMode(int mode)
706 game.use_reverse_scan_direction =
707 (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
709 InitPlayfieldScanModeVars();
712 static int get_move_delay_from_stepsize(int move_stepsize)
715 MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
717 /* make sure that stepsize value is always a power of 2 */
718 move_stepsize = (1 << log_2(move_stepsize));
720 return TILEX / move_stepsize;
723 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
726 int move_delay = get_move_delay_from_stepsize(move_stepsize);
727 boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
729 /* do no immediately change move delay -- the player might just be moving */
730 player->move_delay_value_next = move_delay;
732 /* information if player can move must be set separately */
733 player->cannot_move = cannot_move;
737 player->move_delay = game.initial_move_delay;
738 player->move_delay_value = game.initial_move_delay_value;
740 player->move_delay_value_next = -1;
742 player->move_delay_reset_counter = 0;
746 void GetPlayerConfig()
748 if (!audio.sound_available)
749 setup.sound_simple = FALSE;
751 if (!audio.loops_available)
752 setup.sound_loops = FALSE;
754 if (!audio.music_available)
755 setup.sound_music = FALSE;
757 if (!video.fullscreen_available)
758 setup.fullscreen = FALSE;
760 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
762 SetAudioMode(setup.sound);
766 static int getBeltNrFromBeltElement(int element)
768 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
769 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
770 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
773 static int getBeltNrFromBeltActiveElement(int element)
775 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
776 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
777 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
780 static int getBeltNrFromBeltSwitchElement(int element)
782 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
783 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
784 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
787 static int getBeltDirNrFromBeltSwitchElement(int element)
789 static int belt_base_element[4] =
791 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
792 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
793 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
794 EL_CONVEYOR_BELT_4_SWITCH_LEFT
797 int belt_nr = getBeltNrFromBeltSwitchElement(element);
798 int belt_dir_nr = element - belt_base_element[belt_nr];
800 return (belt_dir_nr % 3);
803 static int getBeltDirFromBeltSwitchElement(int element)
805 static int belt_move_dir[3] =
812 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
814 return belt_move_dir[belt_dir_nr];
817 static void InitPlayerField(int x, int y, int element, boolean init_game)
819 if (element == EL_SP_MURPHY)
823 if (stored_player[0].present)
825 Feld[x][y] = EL_SP_MURPHY_CLONE;
831 stored_player[0].use_murphy = TRUE;
834 Feld[x][y] = EL_PLAYER_1;
840 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
841 int jx = player->jx, jy = player->jy;
843 player->present = TRUE;
845 player->block_last_field = (element == EL_SP_MURPHY ?
846 level.sp_block_last_field :
847 level.block_last_field);
849 /* ---------- initialize player's last field block delay --------------- */
851 /* always start with reliable default value (no adjustment needed) */
852 player->block_delay_adjustment = 0;
854 /* special case 1: in Supaplex, Murphy blocks last field one more frame */
855 if (player->block_last_field && element == EL_SP_MURPHY)
856 player->block_delay_adjustment = 1;
858 /* special case 2: in game engines before 3.1.1, blocking was different */
859 if (game.use_block_last_field_bug)
860 player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
862 if (!options.network || player->connected)
864 player->active = TRUE;
866 /* remove potentially duplicate players */
867 if (StorePlayer[jx][jy] == Feld[x][y])
868 StorePlayer[jx][jy] = 0;
870 StorePlayer[x][y] = Feld[x][y];
874 printf("Player %d activated.\n", player->element_nr);
875 printf("[Local player is %d and currently %s.]\n",
876 local_player->element_nr,
877 local_player->active ? "active" : "not active");
881 Feld[x][y] = EL_EMPTY;
883 player->jx = player->last_jx = x;
884 player->jy = player->last_jy = y;
888 static void InitField(int x, int y, boolean init_game)
890 int element = Feld[x][y];
899 InitPlayerField(x, y, element, init_game);
902 case EL_SOKOBAN_FIELD_PLAYER:
903 element = Feld[x][y] = EL_PLAYER_1;
904 InitField(x, y, init_game);
906 element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
907 InitField(x, y, init_game);
910 case EL_SOKOBAN_FIELD_EMPTY:
911 local_player->sokobanfields_still_needed++;
915 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
916 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
917 else if (x > 0 && Feld[x-1][y] == EL_ACID)
918 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
919 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
920 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
921 else if (y > 0 && Feld[x][y-1] == EL_ACID)
922 Feld[x][y] = EL_ACID_POOL_BOTTOM;
923 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
924 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
932 case EL_SPACESHIP_RIGHT:
933 case EL_SPACESHIP_UP:
934 case EL_SPACESHIP_LEFT:
935 case EL_SPACESHIP_DOWN:
937 case EL_BD_BUTTERFLY_RIGHT:
938 case EL_BD_BUTTERFLY_UP:
939 case EL_BD_BUTTERFLY_LEFT:
940 case EL_BD_BUTTERFLY_DOWN:
941 case EL_BD_BUTTERFLY:
942 case EL_BD_FIREFLY_RIGHT:
943 case EL_BD_FIREFLY_UP:
944 case EL_BD_FIREFLY_LEFT:
945 case EL_BD_FIREFLY_DOWN:
947 case EL_PACMAN_RIGHT:
971 if (y == lev_fieldy - 1)
973 Feld[x][y] = EL_AMOEBA_GROWING;
974 Store[x][y] = EL_AMOEBA_WET;
978 case EL_DYNAMITE_ACTIVE:
979 case EL_SP_DISK_RED_ACTIVE:
980 case EL_DYNABOMB_PLAYER_1_ACTIVE:
981 case EL_DYNABOMB_PLAYER_2_ACTIVE:
982 case EL_DYNABOMB_PLAYER_3_ACTIVE:
983 case EL_DYNABOMB_PLAYER_4_ACTIVE:
988 local_player->lights_still_needed++;
992 local_player->friends_still_needed++;
997 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1000 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1001 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1002 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1003 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1004 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1005 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1006 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1007 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1008 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1009 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1010 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1011 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1014 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1015 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1016 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1018 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
1020 game.belt_dir[belt_nr] = belt_dir;
1021 game.belt_dir_nr[belt_nr] = belt_dir_nr;
1023 else /* more than one switch -- set it like the first switch */
1025 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1030 case EL_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
1032 Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
1035 case EL_LIGHT_SWITCH_ACTIVE:
1037 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1040 case EL_EMC_MAGIC_BALL:
1041 if (game.ball_state)
1042 Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1045 case EL_EMC_MAGIC_BALL_SWITCH:
1046 if (game.ball_state)
1047 Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1052 if (IS_CUSTOM_ELEMENT(element))
1054 if (CAN_MOVE(element))
1057 #if USE_NEW_CUSTOM_VALUE
1058 if (!element_info[element].use_last_ce_value || init_game)
1059 CustomValue[x][y] = GET_NEW_CUSTOM_VALUE(Feld[x][y]);
1063 if (IS_CUSTOM_ELEMENT(element) && CAN_MOVE(element))
1066 else if (IS_GROUP_ELEMENT(element))
1068 struct ElementGroupInfo *group = element_info[element].group;
1069 int last_anim_random_frame = gfx.anim_random_frame;
1072 if (group->choice_mode == ANIM_RANDOM)
1073 gfx.anim_random_frame = RND(group->num_elements_resolved);
1075 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1076 group->choice_mode, 0,
1079 if (group->choice_mode == ANIM_RANDOM)
1080 gfx.anim_random_frame = last_anim_random_frame;
1082 group->choice_pos++;
1084 Feld[x][y] = group->element_resolved[element_pos];
1086 InitField(x, y, init_game);
1093 CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1098 #if USE_NEW_CUSTOM_VALUE
1101 CustomValue[x][y] = GET_NEW_CUSTOM_VALUE(Feld[x][y]);
1103 CustomValue[x][y] = element_info[Feld[x][y]].custom_value_initial;
1111 static inline void InitField_WithBug1(int x, int y, boolean init_game)
1113 InitField(x, y, init_game);
1115 /* not needed to call InitMovDir() -- already done by InitField()! */
1116 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1117 CAN_MOVE(Feld[x][y]))
1121 static inline void InitField_WithBug2(int x, int y, boolean init_game)
1123 int old_element = Feld[x][y];
1125 InitField(x, y, init_game);
1127 /* not needed to call InitMovDir() -- already done by InitField()! */
1128 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1129 CAN_MOVE(old_element) &&
1130 (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1133 /* this case is in fact a combination of not less than three bugs:
1134 first, it calls InitMovDir() for elements that can move, although this is
1135 already done by InitField(); then, it checks the element that was at this
1136 field _before_ the call to InitField() (which can change it); lastly, it
1137 was not called for "mole with direction" elements, which were treated as
1138 "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1142 inline void DrawGameValue_Emeralds(int value)
1144 DrawText(DX_EMERALDS, DY_EMERALDS, int2str(value, 3), FONT_TEXT_2);
1147 inline void DrawGameValue_Dynamite(int value)
1149 DrawText(DX_DYNAMITE, DY_DYNAMITE, int2str(value, 3), FONT_TEXT_2);
1152 inline void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
1156 /* currently only 4 of 8 possible keys are displayed */
1157 for (i = 0; i < STD_NUM_KEYS; i++)
1160 DrawMiniGraphicExt(drawto, DX_KEYS + i * MINI_TILEX, DY_KEYS,
1161 el2edimg(EL_KEY_1 + i));
1163 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
1164 DOOR_GFX_PAGEX5 + XX_KEYS + i * MINI_TILEX, YY_KEYS,
1165 MINI_TILEX, MINI_TILEY, DX_KEYS + i * MINI_TILEX, DY_KEYS);
1169 inline void DrawGameValue_Score(int value)
1171 DrawText(DX_SCORE, DY_SCORE, int2str(value, 5), FONT_TEXT_2);
1174 inline void DrawGameValue_Time(int value)
1177 DrawText(DX_TIME1, DY_TIME, int2str(value, 3), FONT_TEXT_2);
1179 DrawText(DX_TIME2, DY_TIME, int2str(value, 4), FONT_LEVEL_NUMBER);
1182 inline void DrawGameValue_Level(int value)
1185 DrawText(DX_LEVEL, DY_LEVEL, int2str(value, 2), FONT_TEXT_2);
1188 /* misuse area for displaying emeralds to draw bigger level number */
1189 DrawTextExt(drawto, DX_EMERALDS, DY_EMERALDS,
1190 int2str(value, 3), FONT_LEVEL_NUMBER, BLIT_OPAQUE);
1192 /* now copy it to the area for displaying level number */
1193 BlitBitmap(drawto, drawto,
1194 DX_EMERALDS, DY_EMERALDS + 1,
1195 getFontWidth(FONT_LEVEL_NUMBER) * 3,
1196 getFontHeight(FONT_LEVEL_NUMBER) - 1,
1197 DX_LEVEL - 1, DY_LEVEL + 1);
1199 /* restore the area for displaying emeralds */
1200 DrawGameValue_Emeralds(local_player->gems_still_needed);
1202 /* yes, this is all really ugly :-) */
1206 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
1209 int key[MAX_NUM_KEYS];
1212 for (i = 0; i < MAX_NUM_KEYS; i++)
1213 key[i] = key_bits & (1 << i);
1215 DrawGameValue_Level(level_nr);
1217 DrawGameValue_Emeralds(emeralds);
1218 DrawGameValue_Dynamite(dynamite);
1219 DrawGameValue_Score(score);
1220 DrawGameValue_Time(time);
1222 DrawGameValue_Keys(key);
1225 void DrawGameDoorValues()
1229 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
1231 DrawGameDoorValues_EM();
1236 DrawGameValue_Level(level_nr);
1238 DrawGameValue_Emeralds(local_player->gems_still_needed);
1239 DrawGameValue_Dynamite(local_player->inventory_size);
1240 DrawGameValue_Score(local_player->score);
1241 DrawGameValue_Time(TimeLeft);
1243 for (i = 0; i < MAX_PLAYERS; i++)
1244 DrawGameValue_Keys(stored_player[i].key);
1248 static void resolve_group_element(int group_element, int recursion_depth)
1250 static int group_nr;
1251 static struct ElementGroupInfo *group;
1252 struct ElementGroupInfo *actual_group = element_info[group_element].group;
1255 if (recursion_depth > NUM_GROUP_ELEMENTS) /* recursion too deep */
1257 Error(ERR_WARN, "recursion too deep when resolving group element %d",
1258 group_element - EL_GROUP_START + 1);
1260 /* replace element which caused too deep recursion by question mark */
1261 group->element_resolved[group->num_elements_resolved++] = EL_UNKNOWN;
1266 if (recursion_depth == 0) /* initialization */
1268 group = element_info[group_element].group;
1269 group_nr = group_element - EL_GROUP_START;
1271 group->num_elements_resolved = 0;
1272 group->choice_pos = 0;
1275 for (i = 0; i < actual_group->num_elements; i++)
1277 int element = actual_group->element[i];
1279 if (group->num_elements_resolved == NUM_FILE_ELEMENTS)
1282 if (IS_GROUP_ELEMENT(element))
1283 resolve_group_element(element, recursion_depth + 1);
1286 group->element_resolved[group->num_elements_resolved++] = element;
1287 element_info[element].in_group[group_nr] = TRUE;
1294 =============================================================================
1296 -----------------------------------------------------------------------------
1297 initialize game engine due to level / tape version number
1298 =============================================================================
1301 static void InitGameEngine()
1303 int i, j, k, l, x, y;
1305 /* set game engine from tape file when re-playing, else from level file */
1306 game.engine_version = (tape.playing ? tape.engine_version :
1307 level.game_version);
1309 /* ---------------------------------------------------------------------- */
1310 /* set flags for bugs and changes according to active game engine version */
1311 /* ---------------------------------------------------------------------- */
1314 Summary of bugfix/change:
1315 Fixed handling for custom elements that change when pushed by the player.
1317 Fixed/changed in version:
1321 Before 3.1.0, custom elements that "change when pushing" changed directly
1322 after the player started pushing them (until then handled in "DigField()").
1323 Since 3.1.0, these custom elements are not changed until the "pushing"
1324 move of the element is finished (now handled in "ContinueMoving()").
1326 Affected levels/tapes:
1327 The first condition is generally needed for all levels/tapes before version
1328 3.1.0, which might use the old behaviour before it was changed; known tapes
1329 that are affected are some tapes from the level set "Walpurgis Gardens" by
1331 The second condition is an exception from the above case and is needed for
1332 the special case of tapes recorded with game (not engine!) version 3.1.0 or
1333 above (including some development versions of 3.1.0), but before it was
1334 known that this change would break tapes like the above and was fixed in
1335 3.1.1, so that the changed behaviour was active although the engine version
1336 while recording maybe was before 3.1.0. There is at least one tape that is
1337 affected by this exception, which is the tape for the one-level set "Bug
1338 Machine" by Juergen Bonhagen.
1341 game.use_change_when_pushing_bug =
1342 (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1344 tape.game_version >= VERSION_IDENT(3,1,0,0) &&
1345 tape.game_version < VERSION_IDENT(3,1,1,0)));
1348 Summary of bugfix/change:
1349 Fixed handling for blocking the field the player leaves when moving.
1351 Fixed/changed in version:
1355 Before 3.1.1, when "block last field when moving" was enabled, the field
1356 the player is leaving when moving was blocked for the time of the move,
1357 and was directly unblocked afterwards. This resulted in the last field
1358 being blocked for exactly one less than the number of frames of one player
1359 move. Additionally, even when blocking was disabled, the last field was
1360 blocked for exactly one frame.
1361 Since 3.1.1, due to changes in player movement handling, the last field
1362 is not blocked at all when blocking is disabled. When blocking is enabled,
1363 the last field is blocked for exactly the number of frames of one player
1364 move. Additionally, if the player is Murphy, the hero of Supaplex, the
1365 last field is blocked for exactly one more than the number of frames of
1368 Affected levels/tapes:
1369 (!!! yet to be determined -- probably many !!!)
1372 game.use_block_last_field_bug =
1373 (game.engine_version < VERSION_IDENT(3,1,1,0));
1376 Summary of bugfix/change:
1377 Changed behaviour of CE changes with multiple changes per single frame.
1379 Fixed/changed in version:
1383 Before 3.2.0-6, only one single CE change was allowed in each engine frame.
1384 This resulted in race conditions where CEs seem to behave strange in some
1385 situations (where triggered CE changes were just skipped because there was
1386 already a CE change on that tile in the playfield in that engine frame).
1387 Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
1388 (The number of changes per frame must be limited in any case, because else
1389 it is easily possible to define CE changes that would result in an infinite
1390 loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
1391 should be set large enough so that it would only be reached in cases where
1392 the corresponding CE change conditions run into a loop. Therefore, it seems
1393 to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
1394 maximal number of change pages for custom elements.)
1396 Affected levels/tapes:
1400 #if USE_ONLY_ONE_CHANGE_PER_FRAME
1401 game.max_num_changes_per_frame = 1;
1403 game.max_num_changes_per_frame =
1404 (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
1407 /* ---------------------------------------------------------------------- */
1409 /* default scan direction: scan playfield from top/left to bottom/right */
1410 InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
1412 /* dynamically adjust element properties according to game engine version */
1413 InitElementPropertiesEngine(game.engine_version);
1416 printf("level %d: level version == %06d\n", level_nr, level.game_version);
1417 printf(" tape version == %06d [%s] [file: %06d]\n",
1418 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
1420 printf(" => game.engine_version == %06d\n", game.engine_version);
1424 /* ---------- recursively resolve group elements ------------------------- */
1426 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1427 for (j = 0; j < NUM_GROUP_ELEMENTS; j++)
1428 element_info[i].in_group[j] = FALSE;
1430 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
1431 resolve_group_element(EL_GROUP_START + i, 0);
1434 /* ---------- initialize player's initial move delay --------------------- */
1437 /* dynamically adjust player properties according to level information */
1438 game.initial_move_delay_value =
1439 get_move_delay_from_stepsize(level.initial_player_stepsize);
1441 /* dynamically adjust player properties according to level information */
1442 game.initial_move_delay_value =
1443 (level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED);
1446 /* dynamically adjust player properties according to game engine version */
1447 game.initial_move_delay = (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
1448 game.initial_move_delay_value : 0);
1450 /* ---------- initialize player's initial push delay --------------------- */
1452 /* dynamically adjust player properties according to game engine version */
1453 game.initial_push_delay_value =
1454 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
1456 /* ---------- initialize changing elements ------------------------------- */
1458 /* initialize changing elements information */
1459 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1461 struct ElementInfo *ei = &element_info[i];
1463 /* this pointer might have been changed in the level editor */
1464 ei->change = &ei->change_page[0];
1466 if (!IS_CUSTOM_ELEMENT(i))
1468 ei->change->target_element = EL_EMPTY_SPACE;
1469 ei->change->delay_fixed = 0;
1470 ei->change->delay_random = 0;
1471 ei->change->delay_frames = 1;
1474 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1476 ei->has_change_event[j] = FALSE;
1478 ei->event_page_nr[j] = 0;
1479 ei->event_page[j] = &ei->change_page[0];
1483 /* add changing elements from pre-defined list */
1484 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
1486 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
1487 struct ElementInfo *ei = &element_info[ch_delay->element];
1489 ei->change->target_element = ch_delay->target_element;
1490 ei->change->delay_fixed = ch_delay->change_delay;
1492 ei->change->pre_change_function = ch_delay->pre_change_function;
1493 ei->change->change_function = ch_delay->change_function;
1494 ei->change->post_change_function = ch_delay->post_change_function;
1496 ei->change->can_change = TRUE;
1497 ei->change->can_change_or_has_action = TRUE;
1499 ei->has_change_event[CE_DELAY] = TRUE;
1501 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
1502 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
1505 /* ---------- initialize internal run-time variables ------------- */
1507 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1509 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1511 for (j = 0; j < ei->num_change_pages; j++)
1513 ei->change_page[j].can_change_or_has_action =
1514 (ei->change_page[j].can_change |
1515 ei->change_page[j].has_action);
1519 /* add change events from custom element configuration */
1520 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1522 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1524 for (j = 0; j < ei->num_change_pages; j++)
1526 if (!ei->change_page[j].can_change_or_has_action)
1529 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1531 /* only add event page for the first page found with this event */
1532 if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
1534 ei->has_change_event[k] = TRUE;
1536 ei->event_page_nr[k] = j;
1537 ei->event_page[k] = &ei->change_page[j];
1543 /* ---------- initialize run-time trigger player and element ------------- */
1545 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1547 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1549 for (j = 0; j < ei->num_change_pages; j++)
1551 ei->change_page[j].actual_trigger_element = EL_EMPTY;
1552 ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
1553 ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
1554 ei->change_page[j].actual_trigger_ce_value = 0;
1558 /* ---------- initialize trigger events ---------------------------------- */
1560 /* initialize trigger events information */
1561 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1562 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1563 trigger_events[i][j] = FALSE;
1565 /* add trigger events from element change event properties */
1566 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1568 struct ElementInfo *ei = &element_info[i];
1570 for (j = 0; j < ei->num_change_pages; j++)
1572 if (!ei->change_page[j].can_change_or_has_action)
1575 if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
1577 int trigger_element = ei->change_page[j].trigger_element;
1579 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1581 if (ei->change_page[j].has_event[k])
1583 if (IS_GROUP_ELEMENT(trigger_element))
1585 struct ElementGroupInfo *group =
1586 element_info[trigger_element].group;
1588 for (l = 0; l < group->num_elements_resolved; l++)
1589 trigger_events[group->element_resolved[l]][k] = TRUE;
1592 trigger_events[trigger_element][k] = TRUE;
1599 /* ---------- initialize push delay -------------------------------------- */
1601 /* initialize push delay values to default */
1602 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1604 if (!IS_CUSTOM_ELEMENT(i))
1606 element_info[i].push_delay_fixed = game.default_push_delay_fixed;
1607 element_info[i].push_delay_random = game.default_push_delay_random;
1611 /* set push delay value for certain elements from pre-defined list */
1612 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
1614 int e = push_delay_list[i].element;
1616 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
1617 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
1620 /* set push delay value for Supaplex elements for newer engine versions */
1621 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
1623 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1625 if (IS_SP_ELEMENT(i))
1627 /* set SP push delay to just enough to push under a falling zonk */
1628 int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
1630 element_info[i].push_delay_fixed = delay;
1631 element_info[i].push_delay_random = 0;
1636 /* ---------- initialize move stepsize ----------------------------------- */
1638 /* initialize move stepsize values to default */
1639 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1640 if (!IS_CUSTOM_ELEMENT(i))
1641 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
1643 /* set move stepsize value for certain elements from pre-defined list */
1644 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
1646 int e = move_stepsize_list[i].element;
1648 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
1651 /* ---------- initialize collect score ----------------------------------- */
1653 /* initialize collect score values for custom elements from initial value */
1654 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1655 if (IS_CUSTOM_ELEMENT(i))
1656 element_info[i].collect_score = element_info[i].collect_score_initial;
1658 /* ---------- initialize collect count ----------------------------------- */
1660 /* initialize collect count values for non-custom elements */
1661 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1662 if (!IS_CUSTOM_ELEMENT(i))
1663 element_info[i].collect_count_initial = 0;
1665 /* add collect count values for all elements from pre-defined list */
1666 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
1667 element_info[collect_count_list[i].element].collect_count_initial =
1668 collect_count_list[i].count;
1670 /* ---------- initialize access direction -------------------------------- */
1672 /* initialize access direction values to default (access from every side) */
1673 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1674 if (!IS_CUSTOM_ELEMENT(i))
1675 element_info[i].access_direction = MV_ALL_DIRECTIONS;
1677 /* set access direction value for certain elements from pre-defined list */
1678 for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
1679 element_info[access_direction_list[i].element].access_direction =
1680 access_direction_list[i].direction;
1682 /* ---------- initialize explosion content ------------------------------- */
1683 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1685 if (IS_CUSTOM_ELEMENT(i))
1688 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
1690 /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
1692 element_info[i].content.e[x][y] =
1693 (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
1694 i == EL_PLAYER_2 ? EL_EMERALD_RED :
1695 i == EL_PLAYER_3 ? EL_EMERALD :
1696 i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
1697 i == EL_MOLE ? EL_EMERALD_RED :
1698 i == EL_PENGUIN ? EL_EMERALD_PURPLE :
1699 i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
1700 i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
1701 i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
1702 i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
1703 i == EL_WALL_EMERALD ? EL_EMERALD :
1704 i == EL_WALL_DIAMOND ? EL_DIAMOND :
1705 i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
1706 i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
1707 i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
1708 i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
1709 i == EL_WALL_PEARL ? EL_PEARL :
1710 i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
1716 int get_num_special_action(int element, int action_first, int action_last)
1718 int num_special_action = 0;
1721 for (i = action_first; i <= action_last; i++)
1723 boolean found = FALSE;
1725 for (j = 0; j < NUM_DIRECTIONS; j++)
1726 if (el_act_dir2img(element, i, j) !=
1727 el_act_dir2img(element, ACTION_DEFAULT, j))
1731 num_special_action++;
1736 return num_special_action;
1740 =============================================================================
1742 -----------------------------------------------------------------------------
1743 initialize and start new game
1744 =============================================================================
1749 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
1750 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
1751 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
1756 /* don't play tapes over network */
1757 network_playing = (options.network && !tape.playing);
1759 for (i = 0; i < MAX_PLAYERS; i++)
1761 struct PlayerInfo *player = &stored_player[i];
1763 player->index_nr = i;
1764 player->index_bit = (1 << i);
1765 player->element_nr = EL_PLAYER_1 + i;
1767 player->present = FALSE;
1768 player->active = FALSE;
1771 player->effective_action = 0;
1772 player->programmed_action = 0;
1775 player->gems_still_needed = level.gems_needed;
1776 player->sokobanfields_still_needed = 0;
1777 player->lights_still_needed = 0;
1778 player->friends_still_needed = 0;
1780 for (j = 0; j < MAX_NUM_KEYS; j++)
1781 player->key[j] = FALSE;
1783 player->dynabomb_count = 0;
1784 player->dynabomb_size = 1;
1785 player->dynabombs_left = 0;
1786 player->dynabomb_xl = FALSE;
1788 player->MovDir = MV_NONE;
1791 player->GfxDir = MV_NONE;
1792 player->GfxAction = ACTION_DEFAULT;
1794 player->StepFrame = 0;
1796 player->use_murphy = FALSE;
1797 player->artwork_element =
1798 (level.use_artwork_element[i] ? level.artwork_element[i] :
1799 player->element_nr);
1801 player->block_last_field = FALSE; /* initialized in InitPlayerField() */
1802 player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
1804 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
1806 player->actual_frame_counter = 0;
1808 player->step_counter = 0;
1810 player->last_move_dir = MV_NONE;
1812 player->is_waiting = FALSE;
1813 player->is_moving = FALSE;
1814 player->is_auto_moving = FALSE;
1815 player->is_digging = FALSE;
1816 player->is_snapping = FALSE;
1817 player->is_collecting = FALSE;
1818 player->is_pushing = FALSE;
1819 player->is_switching = FALSE;
1820 player->is_dropping = FALSE;
1822 player->is_bored = FALSE;
1823 player->is_sleeping = FALSE;
1825 player->frame_counter_bored = -1;
1826 player->frame_counter_sleeping = -1;
1828 player->anim_delay_counter = 0;
1829 player->post_delay_counter = 0;
1831 player->action_waiting = ACTION_DEFAULT;
1832 player->last_action_waiting = ACTION_DEFAULT;
1833 player->special_action_bored = ACTION_DEFAULT;
1834 player->special_action_sleeping = ACTION_DEFAULT;
1836 /* set number of special actions for bored and sleeping animation */
1837 player->num_special_action_bored =
1838 get_num_special_action(player->artwork_element,
1839 ACTION_BORING_1, ACTION_BORING_LAST);
1840 player->num_special_action_sleeping =
1841 get_num_special_action(player->artwork_element,
1842 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
1844 player->switch_x = -1;
1845 player->switch_y = -1;
1847 player->drop_x = -1;
1848 player->drop_y = -1;
1850 player->show_envelope = 0;
1853 SetPlayerMoveSpeed(player, level.initial_player_stepsize, TRUE);
1855 player->move_delay = game.initial_move_delay;
1856 player->move_delay_value = game.initial_move_delay_value;
1858 player->move_delay_value_next = -1;
1860 player->move_delay_reset_counter = 0;
1862 player->cannot_move = FALSE;
1865 player->push_delay = -1; /* initialized when pushing starts */
1866 player->push_delay_value = game.initial_push_delay_value;
1868 player->drop_delay = 0;
1870 player->last_jx = player->last_jy = 0;
1871 player->jx = player->jy = 0;
1873 player->shield_normal_time_left = 0;
1874 player->shield_deadly_time_left = 0;
1876 player->inventory_infinite_element = EL_UNDEFINED;
1877 player->inventory_size = 0;
1879 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
1880 SnapField(player, 0, 0);
1882 player->LevelSolved = FALSE;
1883 player->GameOver = FALSE;
1886 network_player_action_received = FALSE;
1888 #if defined(NETWORK_AVALIABLE)
1889 /* initial null action */
1890 if (network_playing)
1891 SendToServer_MovePlayer(MV_NONE);
1900 TimeLeft = level.time;
1903 ScreenMovDir = MV_NONE;
1907 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
1909 AllPlayersGone = FALSE;
1911 game.yamyam_content_nr = 0;
1912 game.magic_wall_active = FALSE;
1913 game.magic_wall_time_left = 0;
1914 game.light_time_left = 0;
1915 game.timegate_time_left = 0;
1916 game.switchgate_pos = 0;
1917 game.wind_direction = level.wind_direction_initial;
1918 game.gravity = level.initial_gravity;
1919 game.explosions_delayed = TRUE;
1921 game.lenses_time_left = 0;
1922 game.magnify_time_left = 0;
1924 game.ball_state = level.ball_state_initial;
1925 game.ball_content_nr = 0;
1927 game.envelope_active = FALSE;
1929 for (i = 0; i < NUM_BELTS; i++)
1931 game.belt_dir[i] = MV_NONE;
1932 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1935 for (i = 0; i < MAX_NUM_AMOEBA; i++)
1936 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
1939 SCAN_PLAYFIELD(x, y)
1941 for (x = 0; x < lev_fieldx; x++) for (y = 0; y < lev_fieldy; y++)
1944 Feld[x][y] = level.field[x][y];
1945 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
1946 ChangeDelay[x][y] = 0;
1947 ChangePage[x][y] = -1;
1948 #if USE_NEW_CUSTOM_VALUE
1949 CustomValue[x][y] = 0; /* initialized in InitField() */
1951 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
1953 WasJustMoving[x][y] = 0;
1954 WasJustFalling[x][y] = 0;
1955 CheckCollision[x][y] = 0;
1957 Pushed[x][y] = FALSE;
1959 ChangeCount[x][y] = 0;
1960 ChangeEvent[x][y] = -1;
1962 ExplodePhase[x][y] = 0;
1963 ExplodeDelay[x][y] = 0;
1964 ExplodeField[x][y] = EX_TYPE_NONE;
1966 RunnerVisit[x][y] = 0;
1967 PlayerVisit[x][y] = 0;
1970 GfxRandom[x][y] = INIT_GFX_RANDOM();
1971 GfxElement[x][y] = EL_UNDEFINED;
1972 GfxAction[x][y] = ACTION_DEFAULT;
1973 GfxDir[x][y] = MV_NONE;
1977 SCAN_PLAYFIELD(x, y)
1979 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
1982 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
1984 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
1986 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
1989 InitField(x, y, TRUE);
1994 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
1995 emulate_sb ? EMU_SOKOBAN :
1996 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
1998 #if USE_NEW_ALL_SLIPPERY
1999 /* initialize type of slippery elements */
2000 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2002 if (!IS_CUSTOM_ELEMENT(i))
2004 /* default: elements slip down either to the left or right randomly */
2005 element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
2007 /* SP style elements prefer to slip down on the left side */
2008 if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
2009 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
2011 /* BD style elements prefer to slip down on the left side */
2012 if (game.emulation == EMU_BOULDERDASH)
2013 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
2018 /* initialize explosion and ignition delay */
2019 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2021 if (!IS_CUSTOM_ELEMENT(i))
2024 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
2025 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
2026 game.emulation == EMU_SUPAPLEX ? 3 : 2);
2027 int last_phase = (num_phase + 1) * delay;
2028 int half_phase = (num_phase / 2) * delay;
2030 element_info[i].explosion_delay = last_phase - 1;
2031 element_info[i].ignition_delay = half_phase;
2033 if (i == EL_BLACK_ORB)
2034 element_info[i].ignition_delay = 1;
2038 if (element_info[i].explosion_delay < 1) /* !!! check again !!! */
2039 element_info[i].explosion_delay = 1;
2041 if (element_info[i].ignition_delay < 1) /* !!! check again !!! */
2042 element_info[i].ignition_delay = 1;
2046 /* correct non-moving belts to start moving left */
2047 for (i = 0; i < NUM_BELTS; i++)
2048 if (game.belt_dir[i] == MV_NONE)
2049 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
2051 /* check if any connected player was not found in playfield */
2052 for (i = 0; i < MAX_PLAYERS; i++)
2054 struct PlayerInfo *player = &stored_player[i];
2056 if (player->connected && !player->present)
2058 for (j = 0; j < MAX_PLAYERS; j++)
2060 struct PlayerInfo *some_player = &stored_player[j];
2061 int jx = some_player->jx, jy = some_player->jy;
2063 /* assign first free player found that is present in the playfield */
2064 if (some_player->present && !some_player->connected)
2066 player->present = TRUE;
2067 player->active = TRUE;
2069 some_player->present = FALSE;
2070 some_player->active = FALSE;
2073 player->element_nr = some_player->element_nr;
2076 player->artwork_element = some_player->artwork_element;
2078 player->block_last_field = some_player->block_last_field;
2079 player->block_delay_adjustment = some_player->block_delay_adjustment;
2081 StorePlayer[jx][jy] = player->element_nr;
2082 player->jx = player->last_jx = jx;
2083 player->jy = player->last_jy = jy;
2093 /* when playing a tape, eliminate all players which do not participate */
2095 for (i = 0; i < MAX_PLAYERS; i++)
2097 if (stored_player[i].active && !tape.player_participates[i])
2099 struct PlayerInfo *player = &stored_player[i];
2100 int jx = player->jx, jy = player->jy;
2102 player->active = FALSE;
2103 StorePlayer[jx][jy] = 0;
2104 Feld[jx][jy] = EL_EMPTY;
2108 else if (!options.network && !setup.team_mode) /* && !tape.playing */
2110 /* when in single player mode, eliminate all but the first active player */
2112 for (i = 0; i < MAX_PLAYERS; i++)
2114 if (stored_player[i].active)
2116 for (j = i + 1; j < MAX_PLAYERS; j++)
2118 if (stored_player[j].active)
2120 struct PlayerInfo *player = &stored_player[j];
2121 int jx = player->jx, jy = player->jy;
2123 player->active = FALSE;
2124 player->present = FALSE;
2126 StorePlayer[jx][jy] = 0;
2127 Feld[jx][jy] = EL_EMPTY;
2134 /* when recording the game, store which players take part in the game */
2137 for (i = 0; i < MAX_PLAYERS; i++)
2138 if (stored_player[i].active)
2139 tape.player_participates[i] = TRUE;
2144 for (i = 0; i < MAX_PLAYERS; i++)
2146 struct PlayerInfo *player = &stored_player[i];
2148 printf("Player %d: present == %d, connected == %d, active == %d.\n",
2153 if (local_player == player)
2154 printf("Player %d is local player.\n", i+1);
2158 if (BorderElement == EL_EMPTY)
2161 SBX_Right = lev_fieldx - SCR_FIELDX;
2163 SBY_Lower = lev_fieldy - SCR_FIELDY;
2168 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
2170 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
2173 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
2174 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
2176 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
2177 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
2179 /* if local player not found, look for custom element that might create
2180 the player (make some assumptions about the right custom element) */
2181 if (!local_player->present)
2183 int start_x = 0, start_y = 0;
2184 int found_rating = 0;
2185 int found_element = EL_UNDEFINED;
2186 int player_nr = local_player->index_nr;
2189 SCAN_PLAYFIELD(x, y)
2191 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
2194 int element = Feld[x][y];
2199 if (level.use_start_element[player_nr] &&
2200 level.start_element[player_nr] == element &&
2207 found_element = element;
2210 if (!IS_CUSTOM_ELEMENT(element))
2213 if (CAN_CHANGE(element))
2215 for (i = 0; i < element_info[element].num_change_pages; i++)
2217 /* check for player created from custom element as single target */
2218 content = element_info[element].change_page[i].target_element;
2219 is_player = ELEM_IS_PLAYER(content);
2221 if (is_player && (found_rating < 3 || element < found_element))
2227 found_element = element;
2232 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
2234 /* check for player created from custom element as explosion content */
2235 content = element_info[element].content.e[xx][yy];
2236 is_player = ELEM_IS_PLAYER(content);
2238 if (is_player && (found_rating < 2 || element < found_element))
2240 start_x = x + xx - 1;
2241 start_y = y + yy - 1;
2244 found_element = element;
2247 if (!CAN_CHANGE(element))
2250 for (i = 0; i < element_info[element].num_change_pages; i++)
2252 /* check for player created from custom element as extended target */
2254 element_info[element].change_page[i].target_content.e[xx][yy];
2256 is_player = ELEM_IS_PLAYER(content);
2258 if (is_player && (found_rating < 1 || element < found_element))
2260 start_x = x + xx - 1;
2261 start_y = y + yy - 1;
2264 found_element = element;
2270 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
2271 start_x > SBX_Right + MIDPOSX ? SBX_Right :
2274 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
2275 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
2280 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2281 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2282 local_player->jx - MIDPOSX);
2284 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2285 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2286 local_player->jy - MIDPOSY);
2289 if (!game.restart_level)
2290 CloseDoor(DOOR_CLOSE_1);
2292 /* !!! FIX THIS (START) !!! */
2293 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2295 InitGameEngine_EM();
2302 /* after drawing the level, correct some elements */
2303 if (game.timegate_time_left == 0)
2304 CloseAllOpenTimegates();
2306 if (setup.soft_scrolling)
2307 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
2309 redraw_mask |= REDRAW_FROM_BACKBUFFER;
2312 /* !!! FIX THIS (END) !!! */
2314 if (!game.restart_level)
2316 /* copy default game door content to main double buffer */
2317 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2318 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2321 DrawGameDoorValues();
2323 if (!game.restart_level)
2327 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
2328 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
2329 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
2333 /* copy actual game door content to door double buffer for OpenDoor() */
2334 BlitBitmap(drawto, bitmap_db_door,
2335 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
2337 OpenDoor(DOOR_OPEN_ALL);
2339 PlaySoundStereo(SND_GAME_STARTING, SOUND_MIDDLE);
2341 if (setup.sound_music)
2344 KeyboardAutoRepeatOffUnlessAutoplay();
2348 for (i = 0; i < MAX_PLAYERS; i++)
2349 printf("Player %d %sactive.\n",
2350 i + 1, (stored_player[i].active ? "" : "not "));
2354 game.restart_level = FALSE;
2357 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
2359 /* this is used for non-R'n'D game engines to update certain engine values */
2361 /* needed to determine if sounds are played within the visible screen area */
2362 scroll_x = actual_scroll_x;
2363 scroll_y = actual_scroll_y;
2366 void InitMovDir(int x, int y)
2368 int i, element = Feld[x][y];
2369 static int xy[4][2] =
2376 static int direction[3][4] =
2378 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
2379 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
2380 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
2389 Feld[x][y] = EL_BUG;
2390 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
2393 case EL_SPACESHIP_RIGHT:
2394 case EL_SPACESHIP_UP:
2395 case EL_SPACESHIP_LEFT:
2396 case EL_SPACESHIP_DOWN:
2397 Feld[x][y] = EL_SPACESHIP;
2398 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
2401 case EL_BD_BUTTERFLY_RIGHT:
2402 case EL_BD_BUTTERFLY_UP:
2403 case EL_BD_BUTTERFLY_LEFT:
2404 case EL_BD_BUTTERFLY_DOWN:
2405 Feld[x][y] = EL_BD_BUTTERFLY;
2406 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
2409 case EL_BD_FIREFLY_RIGHT:
2410 case EL_BD_FIREFLY_UP:
2411 case EL_BD_FIREFLY_LEFT:
2412 case EL_BD_FIREFLY_DOWN:
2413 Feld[x][y] = EL_BD_FIREFLY;
2414 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
2417 case EL_PACMAN_RIGHT:
2419 case EL_PACMAN_LEFT:
2420 case EL_PACMAN_DOWN:
2421 Feld[x][y] = EL_PACMAN;
2422 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
2425 case EL_SP_SNIKSNAK:
2426 MovDir[x][y] = MV_UP;
2429 case EL_SP_ELECTRON:
2430 MovDir[x][y] = MV_LEFT;
2437 Feld[x][y] = EL_MOLE;
2438 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
2442 if (IS_CUSTOM_ELEMENT(element))
2444 struct ElementInfo *ei = &element_info[element];
2445 int move_direction_initial = ei->move_direction_initial;
2446 int move_pattern = ei->move_pattern;
2448 if (move_direction_initial == MV_START_PREVIOUS)
2450 if (MovDir[x][y] != MV_NONE)
2453 move_direction_initial = MV_START_AUTOMATIC;
2456 if (move_direction_initial == MV_START_RANDOM)
2457 MovDir[x][y] = 1 << RND(4);
2458 else if (move_direction_initial & MV_ANY_DIRECTION)
2459 MovDir[x][y] = move_direction_initial;
2460 else if (move_pattern == MV_ALL_DIRECTIONS ||
2461 move_pattern == MV_TURNING_LEFT ||
2462 move_pattern == MV_TURNING_RIGHT ||
2463 move_pattern == MV_TURNING_LEFT_RIGHT ||
2464 move_pattern == MV_TURNING_RIGHT_LEFT ||
2465 move_pattern == MV_TURNING_RANDOM)
2466 MovDir[x][y] = 1 << RND(4);
2467 else if (move_pattern == MV_HORIZONTAL)
2468 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
2469 else if (move_pattern == MV_VERTICAL)
2470 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
2471 else if (move_pattern & MV_ANY_DIRECTION)
2472 MovDir[x][y] = element_info[element].move_pattern;
2473 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
2474 move_pattern == MV_ALONG_RIGHT_SIDE)
2476 /* use random direction as default start direction */
2477 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2478 MovDir[x][y] = 1 << RND(4);
2480 for (i = 0; i < NUM_DIRECTIONS; i++)
2482 int x1 = x + xy[i][0];
2483 int y1 = y + xy[i][1];
2485 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2487 if (move_pattern == MV_ALONG_RIGHT_SIDE)
2488 MovDir[x][y] = direction[0][i];
2490 MovDir[x][y] = direction[1][i];
2499 MovDir[x][y] = 1 << RND(4);
2501 if (element != EL_BUG &&
2502 element != EL_SPACESHIP &&
2503 element != EL_BD_BUTTERFLY &&
2504 element != EL_BD_FIREFLY)
2507 for (i = 0; i < NUM_DIRECTIONS; i++)
2509 int x1 = x + xy[i][0];
2510 int y1 = y + xy[i][1];
2512 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2514 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
2516 MovDir[x][y] = direction[0][i];
2519 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
2520 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
2522 MovDir[x][y] = direction[1][i];
2531 GfxDir[x][y] = MovDir[x][y];
2534 void InitAmoebaNr(int x, int y)
2537 int group_nr = AmoebeNachbarNr(x, y);
2541 for (i = 1; i < MAX_NUM_AMOEBA; i++)
2543 if (AmoebaCnt[i] == 0)
2551 AmoebaNr[x][y] = group_nr;
2552 AmoebaCnt[group_nr]++;
2553 AmoebaCnt2[group_nr]++;
2559 boolean raise_level = FALSE;
2561 if (local_player->MovPos)
2564 if (tape.auto_play) /* tape might already be stopped here */
2565 tape.auto_play_level_solved = TRUE;
2567 local_player->LevelSolved = FALSE;
2569 PlaySoundStereo(SND_GAME_WINNING, SOUND_MIDDLE);
2573 if (!tape.playing && setup.sound_loops)
2574 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2575 SND_CTRL_PLAY_LOOP);
2577 while (TimeLeft > 0)
2579 if (!tape.playing && !setup.sound_loops)
2580 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2582 if (TimeLeft > 100 && TimeLeft % 10 == 0)
2585 RaiseScore(level.score[SC_TIME_BONUS] * 10);
2590 RaiseScore(level.score[SC_TIME_BONUS]);
2593 DrawGameValue_Time(TimeLeft);
2601 if (!tape.playing && setup.sound_loops)
2602 StopSound(SND_GAME_LEVELTIME_BONUS);
2604 else if (level.time == 0) /* level without time limit */
2606 if (!tape.playing && setup.sound_loops)
2607 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2608 SND_CTRL_PLAY_LOOP);
2610 while (TimePlayed < 999)
2612 if (!tape.playing && !setup.sound_loops)
2613 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2615 if (TimePlayed < 900 && TimePlayed % 10 == 0)
2618 RaiseScore(level.score[SC_TIME_BONUS] * 10);
2623 RaiseScore(level.score[SC_TIME_BONUS]);
2626 DrawGameValue_Time(TimePlayed);
2634 if (!tape.playing && setup.sound_loops)
2635 StopSound(SND_GAME_LEVELTIME_BONUS);
2638 /* close exit door after last player */
2639 if (AllPlayersGone && ExitX >= 0 && ExitY >= 0 &&
2640 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
2641 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN))
2643 int element = Feld[ExitX][ExitY];
2645 Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
2646 EL_SP_EXIT_CLOSING);
2648 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
2651 /* player disappears */
2652 if (ExitX >= 0 && ExitY >= 0)
2653 DrawLevelField(ExitX, ExitY);
2660 CloseDoor(DOOR_CLOSE_1);
2665 SaveTape(tape.level_nr); /* Ask to save tape */
2668 if (level_nr == leveldir_current->handicap_level)
2670 leveldir_current->handicap_level++;
2671 SaveLevelSetup_SeriesInfo();
2674 if (level_editor_test_game)
2675 local_player->score = -1; /* no highscore when playing from editor */
2676 else if (level_nr < leveldir_current->last_level)
2677 raise_level = TRUE; /* advance to next level */
2679 if ((hi_pos = NewHiScore()) >= 0)
2681 game_status = GAME_MODE_SCORES;
2682 DrawHallOfFame(hi_pos);
2691 game_status = GAME_MODE_MAIN;
2708 LoadScore(level_nr);
2710 if (strcmp(setup.player_name, EMPTY_PLAYER_NAME) == 0 ||
2711 local_player->score < highscore[MAX_SCORE_ENTRIES - 1].Score)
2714 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
2716 if (local_player->score > highscore[k].Score)
2718 /* player has made it to the hall of fame */
2720 if (k < MAX_SCORE_ENTRIES - 1)
2722 int m = MAX_SCORE_ENTRIES - 1;
2725 for (l = k; l < MAX_SCORE_ENTRIES; l++)
2726 if (!strcmp(setup.player_name, highscore[l].Name))
2728 if (m == k) /* player's new highscore overwrites his old one */
2732 for (l = m; l > k; l--)
2734 strcpy(highscore[l].Name, highscore[l - 1].Name);
2735 highscore[l].Score = highscore[l - 1].Score;
2742 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
2743 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
2744 highscore[k].Score = local_player->score;
2750 else if (!strncmp(setup.player_name, highscore[k].Name,
2751 MAX_PLAYER_NAME_LEN))
2752 break; /* player already there with a higher score */
2758 SaveScore(level_nr);
2763 inline static int getElementMoveStepsize(int x, int y)
2765 int element = Feld[x][y];
2766 int direction = MovDir[x][y];
2767 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2768 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2769 int horiz_move = (dx != 0);
2770 int sign = (horiz_move ? dx : dy);
2771 int step = sign * element_info[element].move_stepsize;
2773 /* special values for move stepsize for spring and things on conveyor belt */
2777 if (element == EL_SPRING)
2778 step = sign * MOVE_STEPSIZE_NORMAL * 2;
2779 else if (CAN_FALL(element) && !CAN_MOVE(element) &&
2780 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
2781 step = sign * MOVE_STEPSIZE_NORMAL / 2;
2783 if (CAN_FALL(element) &&
2784 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
2785 step = sign * MOVE_STEPSIZE_NORMAL / 2;
2786 else if (element == EL_SPRING)
2787 step = sign * MOVE_STEPSIZE_NORMAL * 2;
2794 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
2796 if (player->GfxAction != action || player->GfxDir != dir)
2799 printf("Player frame reset! (%d => %d, %d => %d)\n",
2800 player->GfxAction, action, player->GfxDir, dir);
2803 player->GfxAction = action;
2804 player->GfxDir = dir;
2806 player->StepFrame = 0;
2810 static void ResetRandomAnimationValue(int x, int y)
2812 GfxRandom[x][y] = INIT_GFX_RANDOM();
2815 static void ResetGfxAnimation(int x, int y)
2818 GfxAction[x][y] = ACTION_DEFAULT;
2819 GfxDir[x][y] = MovDir[x][y];
2822 void InitMovingField(int x, int y, int direction)
2824 int element = Feld[x][y];
2825 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2826 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2830 if (!WasJustMoving[x][y] || direction != MovDir[x][y])
2831 ResetGfxAnimation(x, y);
2833 MovDir[x][y] = direction;
2834 GfxDir[x][y] = direction;
2835 GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
2836 ACTION_FALLING : ACTION_MOVING);
2838 /* this is needed for CEs with property "can move" / "not moving" */
2840 if (getElementMoveStepsize(x, y) != 0) /* moving or being moved */
2842 if (Feld[newx][newy] == EL_EMPTY)
2843 Feld[newx][newy] = EL_BLOCKED;
2845 MovDir[newx][newy] = MovDir[x][y];
2847 #if USE_NEW_CUSTOM_VALUE
2848 CustomValue[newx][newy] = CustomValue[x][y];
2851 GfxFrame[newx][newy] = GfxFrame[x][y];
2852 GfxRandom[newx][newy] = GfxRandom[x][y];
2853 GfxAction[newx][newy] = GfxAction[x][y];
2854 GfxDir[newx][newy] = GfxDir[x][y];
2858 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
2860 int direction = MovDir[x][y];
2862 int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
2863 int newy = y + (direction & MV_UP ? -1 : direction & MV_DOWN ? +1 : 0);
2865 int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2866 int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2873 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
2875 int oldx = x, oldy = y;
2876 int direction = MovDir[x][y];
2878 if (direction == MV_LEFT)
2880 else if (direction == MV_RIGHT)
2882 else if (direction == MV_UP)
2884 else if (direction == MV_DOWN)
2887 *comes_from_x = oldx;
2888 *comes_from_y = oldy;
2891 int MovingOrBlocked2Element(int x, int y)
2893 int element = Feld[x][y];
2895 if (element == EL_BLOCKED)
2899 Blocked2Moving(x, y, &oldx, &oldy);
2900 return Feld[oldx][oldy];
2906 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
2908 /* like MovingOrBlocked2Element(), but if element is moving
2909 and (x,y) is the field the moving element is just leaving,
2910 return EL_BLOCKED instead of the element value */
2911 int element = Feld[x][y];
2913 if (IS_MOVING(x, y))
2915 if (element == EL_BLOCKED)
2919 Blocked2Moving(x, y, &oldx, &oldy);
2920 return Feld[oldx][oldy];
2929 static void RemoveField(int x, int y)
2931 Feld[x][y] = EL_EMPTY;
2937 #if USE_NEW_CUSTOM_VALUE
2938 CustomValue[x][y] = 0;
2942 ChangeDelay[x][y] = 0;
2943 ChangePage[x][y] = -1;
2944 Pushed[x][y] = FALSE;
2947 ExplodeField[x][y] = EX_TYPE_NONE;
2950 GfxElement[x][y] = EL_UNDEFINED;
2951 GfxAction[x][y] = ACTION_DEFAULT;
2952 GfxDir[x][y] = MV_NONE;
2955 void RemoveMovingField(int x, int y)
2957 int oldx = x, oldy = y, newx = x, newy = y;
2958 int element = Feld[x][y];
2959 int next_element = EL_UNDEFINED;
2961 if (element != EL_BLOCKED && !IS_MOVING(x, y))
2964 if (IS_MOVING(x, y))
2966 Moving2Blocked(x, y, &newx, &newy);
2968 if (Feld[newx][newy] != EL_BLOCKED)
2970 /* element is moving, but target field is not free (blocked), but
2971 already occupied by something different (example: acid pool);
2972 in this case, only remove the moving field, but not the target */
2974 RemoveField(oldx, oldy);
2976 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
2978 DrawLevelField(oldx, oldy);
2983 else if (element == EL_BLOCKED)
2985 Blocked2Moving(x, y, &oldx, &oldy);
2986 if (!IS_MOVING(oldx, oldy))
2990 if (element == EL_BLOCKED &&
2991 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
2992 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
2993 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
2994 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
2995 next_element = get_next_element(Feld[oldx][oldy]);
2997 RemoveField(oldx, oldy);
2998 RemoveField(newx, newy);
3000 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
3002 if (next_element != EL_UNDEFINED)
3003 Feld[oldx][oldy] = next_element;
3005 DrawLevelField(oldx, oldy);
3006 DrawLevelField(newx, newy);
3009 void DrawDynamite(int x, int y)
3011 int sx = SCREENX(x), sy = SCREENY(y);
3012 int graphic = el2img(Feld[x][y]);
3015 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
3018 if (IS_WALKABLE_INSIDE(Back[x][y]))
3022 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
3023 else if (Store[x][y])
3024 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
3026 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
3028 if (Back[x][y] || Store[x][y])
3029 DrawGraphicThruMask(sx, sy, graphic, frame);
3031 DrawGraphic(sx, sy, graphic, frame);
3034 void CheckDynamite(int x, int y)
3036 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
3040 if (MovDelay[x][y] != 0)
3043 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
3049 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
3054 void DrawRelocatePlayer(struct PlayerInfo *player)
3056 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3057 boolean no_delay = (tape.warp_forward);
3058 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
3059 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
3060 int jx = player->jx;
3061 int jy = player->jy;
3063 if (level.instant_relocation)
3065 int offset = (setup.scroll_delay ? 3 : 0);
3067 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
3069 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
3070 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
3071 local_player->jx - MIDPOSX);
3073 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
3074 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
3075 local_player->jy - MIDPOSY);
3079 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
3080 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
3081 scroll_x = jx - MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
3083 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
3084 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
3085 scroll_y = jy - MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
3087 /* don't scroll over playfield boundaries */
3088 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
3089 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
3091 /* don't scroll over playfield boundaries */
3092 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
3093 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
3096 RedrawPlayfield(TRUE, 0,0,0,0);
3100 int scroll_xx = -999, scroll_yy = -999;
3102 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
3104 while (scroll_xx != scroll_x || scroll_yy != scroll_y)
3107 int fx = FX, fy = FY;
3109 scroll_xx = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
3110 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
3111 local_player->jx - MIDPOSX);
3113 scroll_yy = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
3114 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
3115 local_player->jy - MIDPOSY);
3117 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
3118 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
3120 if (dx == 0 && dy == 0) /* no scrolling needed at all */
3126 fx += dx * TILEX / 2;
3127 fy += dy * TILEY / 2;
3129 ScrollLevel(dx, dy);
3132 /* scroll in two steps of half tile size to make things smoother */
3133 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
3135 Delay(wait_delay_value);
3137 /* scroll second step to align at full tile size */
3139 Delay(wait_delay_value);
3144 Delay(wait_delay_value);
3148 void RelocatePlayer(int jx, int jy, int el_player_raw)
3150 int el_player = GET_PLAYER_ELEMENT(el_player_raw);
3151 int player_nr = GET_PLAYER_NR(el_player);
3152 struct PlayerInfo *player = &stored_player[player_nr];
3153 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3154 boolean no_delay = (tape.warp_forward);
3155 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
3156 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
3157 int old_jx = player->jx;
3158 int old_jy = player->jy;
3159 int old_element = Feld[old_jx][old_jy];
3160 int element = Feld[jx][jy];
3161 boolean player_relocated = (old_jx != jx || old_jy != jy);
3163 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
3164 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
3165 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
3166 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
3167 int leave_side_horiz = move_dir_horiz;
3168 int leave_side_vert = move_dir_vert;
3169 int enter_side = enter_side_horiz | enter_side_vert;
3170 int leave_side = leave_side_horiz | leave_side_vert;
3172 if (player->GameOver) /* do not reanimate dead player */
3175 if (!player_relocated) /* no need to relocate the player */
3178 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
3180 RemoveField(jx, jy); /* temporarily remove newly placed player */
3181 DrawLevelField(jx, jy);
3184 if (player->present)
3186 while (player->MovPos)
3188 ScrollPlayer(player, SCROLL_GO_ON);
3189 ScrollScreen(NULL, SCROLL_GO_ON);
3191 AdvanceFrameAndPlayerCounters(player->index_nr);
3196 Delay(wait_delay_value);
3199 DrawPlayer(player); /* needed here only to cleanup last field */
3200 DrawLevelField(player->jx, player->jy); /* remove player graphic */
3202 player->is_moving = FALSE;
3205 if (IS_CUSTOM_ELEMENT(old_element))
3206 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
3208 player->index_bit, leave_side);
3210 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
3212 player->index_bit, leave_side);
3214 Feld[jx][jy] = el_player;
3215 InitPlayerField(jx, jy, el_player, TRUE);
3217 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
3219 Feld[jx][jy] = element;
3220 InitField(jx, jy, FALSE);
3223 if (player == local_player) /* only visually relocate local player */
3224 DrawRelocatePlayer(player);
3226 TestIfPlayerTouchesBadThing(jx, jy);
3227 TestIfPlayerTouchesCustomElement(jx, jy);
3229 if (IS_CUSTOM_ELEMENT(element))
3230 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
3231 player->index_bit, enter_side);
3233 CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
3234 player->index_bit, enter_side);
3237 void Explode(int ex, int ey, int phase, int mode)
3243 /* !!! eliminate this variable !!! */
3244 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
3246 if (game.explosions_delayed)
3248 ExplodeField[ex][ey] = mode;
3252 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
3254 int center_element = Feld[ex][ey];
3255 int artwork_element, explosion_element; /* set these values later */
3258 /* --- This is only really needed (and now handled) in "Impact()". --- */
3259 /* do not explode moving elements that left the explode field in time */
3260 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
3261 center_element == EL_EMPTY &&
3262 (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
3267 /* !!! at this place, the center element may be EL_BLOCKED !!! */
3268 if (mode == EX_TYPE_NORMAL ||
3269 mode == EX_TYPE_CENTER ||
3270 mode == EX_TYPE_CROSS)
3271 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
3274 /* remove things displayed in background while burning dynamite */
3275 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
3278 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
3280 /* put moving element to center field (and let it explode there) */
3281 center_element = MovingOrBlocked2Element(ex, ey);
3282 RemoveMovingField(ex, ey);
3283 Feld[ex][ey] = center_element;
3286 /* now "center_element" is finally determined -- set related values now */
3287 artwork_element = center_element; /* for custom player artwork */
3288 explosion_element = center_element; /* for custom player artwork */
3290 if (IS_PLAYER(ex, ey))
3292 int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
3294 artwork_element = stored_player[player_nr].artwork_element;
3296 if (level.use_explosion_element[player_nr])
3298 explosion_element = level.explosion_element[player_nr];
3299 artwork_element = explosion_element;
3304 if (mode == EX_TYPE_NORMAL ||
3305 mode == EX_TYPE_CENTER ||
3306 mode == EX_TYPE_CROSS)
3307 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
3311 last_phase = element_info[explosion_element].explosion_delay + 1;
3313 last_phase = element_info[center_element].explosion_delay + 1;
3316 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
3318 int xx = x - ex + 1;
3319 int yy = y - ey + 1;
3322 if (!IN_LEV_FIELD(x, y) ||
3323 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
3324 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
3327 element = Feld[x][y];
3329 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
3331 element = MovingOrBlocked2Element(x, y);
3333 if (!IS_EXPLOSION_PROOF(element))
3334 RemoveMovingField(x, y);
3337 /* indestructible elements can only explode in center (but not flames) */
3338 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
3339 mode == EX_TYPE_BORDER)) ||
3340 element == EL_FLAMES)
3343 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
3344 behaviour, for example when touching a yamyam that explodes to rocks
3345 with active deadly shield, a rock is created under the player !!! */
3346 /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
3348 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
3349 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
3350 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
3352 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
3355 if (IS_ACTIVE_BOMB(element))
3357 /* re-activate things under the bomb like gate or penguin */
3358 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
3365 /* save walkable background elements while explosion on same tile */
3366 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
3367 (x != ex || y != ey || mode == EX_TYPE_BORDER))
3368 Back[x][y] = element;
3370 /* ignite explodable elements reached by other explosion */
3371 if (element == EL_EXPLOSION)
3372 element = Store2[x][y];
3374 if (AmoebaNr[x][y] &&
3375 (element == EL_AMOEBA_FULL ||
3376 element == EL_BD_AMOEBA ||
3377 element == EL_AMOEBA_GROWING))
3379 AmoebaCnt[AmoebaNr[x][y]]--;
3380 AmoebaCnt2[AmoebaNr[x][y]]--;
3385 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
3388 int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
3390 Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
3392 switch(StorePlayer[ex][ey])
3395 Store[x][y] = EL_PLAYER_IS_EXPLODING_2;
3398 Store[x][y] = EL_PLAYER_IS_EXPLODING_3;
3401 Store[x][y] = EL_PLAYER_IS_EXPLODING_4;
3405 Store[x][y] = EL_PLAYER_IS_EXPLODING_1;
3410 if (PLAYERINFO(ex, ey)->use_murphy)
3411 Store[x][y] = EL_EMPTY;
3414 /* !!! check this case -- currently needed for rnd_rado_negundo_v,
3415 !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
3416 else if (ELEM_IS_PLAYER(center_element))
3417 Store[x][y] = EL_EMPTY;
3418 else if (center_element == EL_YAMYAM)
3419 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
3420 else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
3421 Store[x][y] = element_info[center_element].content.e[xx][yy];
3423 /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
3424 (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
3425 otherwise) -- FIX THIS !!! */
3426 else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
3427 Store[x][y] = element_info[element].content.e[1][1];
3429 else if (!CAN_EXPLODE(element))
3430 Store[x][y] = element_info[element].content.e[1][1];
3433 Store[x][y] = EL_EMPTY;
3435 else if (center_element == EL_MOLE)
3436 Store[x][y] = EL_EMERALD_RED;
3437 else if (center_element == EL_PENGUIN)
3438 Store[x][y] = EL_EMERALD_PURPLE;
3439 else if (center_element == EL_BUG)
3440 Store[x][y] = ((x == ex && y == ey) ? EL_DIAMOND : EL_EMERALD);
3441 else if (center_element == EL_BD_BUTTERFLY)
3442 Store[x][y] = EL_BD_DIAMOND;
3443 else if (center_element == EL_SP_ELECTRON)
3444 Store[x][y] = EL_SP_INFOTRON;
3445 else if (center_element == EL_AMOEBA_TO_DIAMOND)
3446 Store[x][y] = level.amoeba_content;
3447 else if (center_element == EL_YAMYAM)
3448 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
3449 else if (IS_CUSTOM_ELEMENT(center_element) &&
3450 element_info[center_element].content.e[xx][yy] != EL_EMPTY)
3451 Store[x][y] = element_info[center_element].content.e[xx][yy];
3452 else if (element == EL_WALL_EMERALD)
3453 Store[x][y] = EL_EMERALD;
3454 else if (element == EL_WALL_DIAMOND)
3455 Store[x][y] = EL_DIAMOND;
3456 else if (element == EL_WALL_BD_DIAMOND)
3457 Store[x][y] = EL_BD_DIAMOND;
3458 else if (element == EL_WALL_EMERALD_YELLOW)
3459 Store[x][y] = EL_EMERALD_YELLOW;
3460 else if (element == EL_WALL_EMERALD_RED)
3461 Store[x][y] = EL_EMERALD_RED;
3462 else if (element == EL_WALL_EMERALD_PURPLE)
3463 Store[x][y] = EL_EMERALD_PURPLE;
3464 else if (element == EL_WALL_PEARL)
3465 Store[x][y] = EL_PEARL;
3466 else if (element == EL_WALL_CRYSTAL)
3467 Store[x][y] = EL_CRYSTAL;
3468 else if (IS_CUSTOM_ELEMENT(element) && !CAN_EXPLODE(element))
3469 Store[x][y] = element_info[element].content.e[1][1];
3471 Store[x][y] = EL_EMPTY;
3474 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
3475 center_element == EL_AMOEBA_TO_DIAMOND)
3476 Store2[x][y] = element;
3478 Feld[x][y] = EL_EXPLOSION;
3479 GfxElement[x][y] = artwork_element;
3481 ExplodePhase[x][y] = 1;
3482 ExplodeDelay[x][y] = last_phase;
3487 if (center_element == EL_YAMYAM)
3488 game.yamyam_content_nr =
3489 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
3501 GfxFrame[x][y] = 0; /* restart explosion animation */
3503 last_phase = ExplodeDelay[x][y];
3505 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
3509 /* activate this even in non-DEBUG version until cause for crash in
3510 getGraphicAnimationFrame() (see below) is found and eliminated */
3515 if (GfxElement[x][y] == EL_UNDEFINED)
3518 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
3519 printf("Explode(): This should never happen!\n");
3522 GfxElement[x][y] = EL_EMPTY;
3526 border_element = Store2[x][y];
3527 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3528 border_element = StorePlayer[x][y];
3530 if (phase == element_info[border_element].ignition_delay ||
3531 phase == last_phase)
3533 boolean border_explosion = FALSE;
3535 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
3536 !PLAYER_EXPLOSION_PROTECTED(x, y))
3538 KillPlayerUnlessExplosionProtected(x, y);
3539 border_explosion = TRUE;
3541 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
3543 Feld[x][y] = Store2[x][y];
3546 border_explosion = TRUE;
3548 else if (border_element == EL_AMOEBA_TO_DIAMOND)
3550 AmoebeUmwandeln(x, y);
3552 border_explosion = TRUE;
3555 /* if an element just explodes due to another explosion (chain-reaction),
3556 do not immediately end the new explosion when it was the last frame of
3557 the explosion (as it would be done in the following "if"-statement!) */
3558 if (border_explosion && phase == last_phase)
3562 if (phase == last_phase)
3566 element = Feld[x][y] = Store[x][y];
3567 Store[x][y] = Store2[x][y] = 0;
3568 GfxElement[x][y] = EL_UNDEFINED;
3570 /* player can escape from explosions and might therefore be still alive */
3571 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
3572 element <= EL_PLAYER_IS_EXPLODING_4)
3574 int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
3575 int explosion_element = EL_PLAYER_1 + player_nr;
3576 int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
3577 int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
3579 if (level.use_explosion_element[player_nr])
3580 explosion_element = level.explosion_element[player_nr];
3582 Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
3583 element_info[explosion_element].content.e[xx][yy]);
3586 /* restore probably existing indestructible background element */
3587 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
3588 element = Feld[x][y] = Back[x][y];
3591 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
3592 GfxDir[x][y] = MV_NONE;
3593 ChangeDelay[x][y] = 0;
3594 ChangePage[x][y] = -1;
3596 #if USE_NEW_CUSTOM_VALUE
3597 CustomValue[x][y] = 0;
3600 InitField_WithBug2(x, y, FALSE);
3602 DrawLevelField(x, y);
3604 TestIfElementTouchesCustomElement(x, y);
3606 if (GFX_CRUMBLED(element))
3607 DrawLevelFieldCrumbledSandNeighbours(x, y);
3609 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
3610 StorePlayer[x][y] = 0;
3612 if (ELEM_IS_PLAYER(element))
3613 RelocatePlayer(x, y, element);
3615 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3617 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
3618 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
3621 DrawLevelFieldCrumbledSand(x, y);
3623 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
3625 DrawLevelElement(x, y, Back[x][y]);
3626 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
3628 else if (IS_WALKABLE_UNDER(Back[x][y]))
3630 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3631 DrawLevelElementThruMask(x, y, Back[x][y]);
3633 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
3634 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3638 void DynaExplode(int ex, int ey)
3641 int dynabomb_element = Feld[ex][ey];
3642 int dynabomb_size = 1;
3643 boolean dynabomb_xl = FALSE;
3644 struct PlayerInfo *player;
3645 static int xy[4][2] =
3653 if (IS_ACTIVE_BOMB(dynabomb_element))
3655 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
3656 dynabomb_size = player->dynabomb_size;
3657 dynabomb_xl = player->dynabomb_xl;
3658 player->dynabombs_left++;
3661 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
3663 for (i = 0; i < NUM_DIRECTIONS; i++)
3665 for (j = 1; j <= dynabomb_size; j++)
3667 int x = ex + j * xy[i][0];
3668 int y = ey + j * xy[i][1];
3671 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
3674 element = Feld[x][y];
3676 /* do not restart explosions of fields with active bombs */
3677 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
3680 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
3682 if (element != EL_EMPTY && element != EL_EXPLOSION &&
3683 !IS_DIGGABLE(element) && !dynabomb_xl)
3689 void Bang(int x, int y)
3691 int element = MovingOrBlocked2Element(x, y);
3692 int explosion_type = EX_TYPE_NORMAL;
3694 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3696 struct PlayerInfo *player = PLAYERINFO(x, y);
3698 element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
3699 player->element_nr);
3701 if (level.use_explosion_element[player->index_nr])
3703 int explosion_element = level.explosion_element[player->index_nr];
3705 if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
3706 explosion_type = EX_TYPE_CROSS;
3707 else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
3708 explosion_type = EX_TYPE_CENTER;
3716 case EL_BD_BUTTERFLY:
3719 case EL_DARK_YAMYAM:
3723 RaiseScoreElement(element);
3726 case EL_DYNABOMB_PLAYER_1_ACTIVE:
3727 case EL_DYNABOMB_PLAYER_2_ACTIVE:
3728 case EL_DYNABOMB_PLAYER_3_ACTIVE:
3729 case EL_DYNABOMB_PLAYER_4_ACTIVE:
3730 case EL_DYNABOMB_INCREASE_NUMBER:
3731 case EL_DYNABOMB_INCREASE_SIZE:
3732 case EL_DYNABOMB_INCREASE_POWER:
3733 explosion_type = EX_TYPE_DYNA;
3738 case EL_LAMP_ACTIVE:
3739 case EL_AMOEBA_TO_DIAMOND:
3740 if (!IS_PLAYER(x, y)) /* penguin and player may be at same field */
3741 explosion_type = EX_TYPE_CENTER;
3745 if (element_info[element].explosion_type == EXPLODES_CROSS)
3746 explosion_type = EX_TYPE_CROSS;
3747 else if (element_info[element].explosion_type == EXPLODES_1X1)
3748 explosion_type = EX_TYPE_CENTER;
3752 if (explosion_type == EX_TYPE_DYNA)
3755 Explode(x, y, EX_PHASE_START, explosion_type);
3757 CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
3760 void SplashAcid(int x, int y)
3762 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
3763 (!IN_LEV_FIELD(x - 1, y - 2) ||
3764 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
3765 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
3767 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
3768 (!IN_LEV_FIELD(x + 1, y - 2) ||
3769 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
3770 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
3772 PlayLevelSound(x, y, SND_ACID_SPLASHING);
3775 static void InitBeltMovement()
3777 static int belt_base_element[4] =
3779 EL_CONVEYOR_BELT_1_LEFT,
3780 EL_CONVEYOR_BELT_2_LEFT,
3781 EL_CONVEYOR_BELT_3_LEFT,
3782 EL_CONVEYOR_BELT_4_LEFT
3784 static int belt_base_active_element[4] =
3786 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
3787 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
3788 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
3789 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
3794 /* set frame order for belt animation graphic according to belt direction */
3795 for (i = 0; i < NUM_BELTS; i++)
3799 for (j = 0; j < NUM_BELT_PARTS; j++)
3801 int element = belt_base_active_element[belt_nr] + j;
3802 int graphic = el2img(element);
3804 if (game.belt_dir[i] == MV_LEFT)
3805 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
3807 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
3812 SCAN_PLAYFIELD(x, y)
3814 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
3817 int element = Feld[x][y];
3819 for (i = 0; i < NUM_BELTS; i++)
3821 if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
3823 int e_belt_nr = getBeltNrFromBeltElement(element);
3826 if (e_belt_nr == belt_nr)
3828 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
3830 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
3837 static void ToggleBeltSwitch(int x, int y)
3839 static int belt_base_element[4] =
3841 EL_CONVEYOR_BELT_1_LEFT,
3842 EL_CONVEYOR_BELT_2_LEFT,
3843 EL_CONVEYOR_BELT_3_LEFT,
3844 EL_CONVEYOR_BELT_4_LEFT
3846 static int belt_base_active_element[4] =
3848 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
3849 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
3850 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
3851 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
3853 static int belt_base_switch_element[4] =
3855 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
3856 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
3857 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
3858 EL_CONVEYOR_BELT_4_SWITCH_LEFT
3860 static int belt_move_dir[4] =
3868 int element = Feld[x][y];
3869 int belt_nr = getBeltNrFromBeltSwitchElement(element);
3870 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
3871 int belt_dir = belt_move_dir[belt_dir_nr];
3874 if (!IS_BELT_SWITCH(element))
3877 game.belt_dir_nr[belt_nr] = belt_dir_nr;
3878 game.belt_dir[belt_nr] = belt_dir;
3880 if (belt_dir_nr == 3)
3883 /* set frame order for belt animation graphic according to belt direction */
3884 for (i = 0; i < NUM_BELT_PARTS; i++)
3886 int element = belt_base_active_element[belt_nr] + i;
3887 int graphic = el2img(element);
3889 if (belt_dir == MV_LEFT)
3890 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
3892 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
3896 SCAN_PLAYFIELD(xx, yy)
3898 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
3901 int element = Feld[xx][yy];
3903 if (IS_BELT_SWITCH(element))
3905 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
3907 if (e_belt_nr == belt_nr)
3909 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
3910 DrawLevelField(xx, yy);
3913 else if (IS_BELT(element) && belt_dir != MV_NONE)
3915 int e_belt_nr = getBeltNrFromBeltElement(element);
3917 if (e_belt_nr == belt_nr)
3919 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
3921 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
3922 DrawLevelField(xx, yy);
3925 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
3927 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
3929 if (e_belt_nr == belt_nr)
3931 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
3933 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
3934 DrawLevelField(xx, yy);
3940 static void ToggleSwitchgateSwitch(int x, int y)
3944 game.switchgate_pos = !game.switchgate_pos;
3947 SCAN_PLAYFIELD(xx, yy)
3949 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
3952 int element = Feld[xx][yy];
3954 if (element == EL_SWITCHGATE_SWITCH_UP ||
3955 element == EL_SWITCHGATE_SWITCH_DOWN)
3957 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
3958 DrawLevelField(xx, yy);
3960 else if (element == EL_SWITCHGATE_OPEN ||
3961 element == EL_SWITCHGATE_OPENING)
3963 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
3965 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
3967 else if (element == EL_SWITCHGATE_CLOSED ||
3968 element == EL_SWITCHGATE_CLOSING)
3970 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
3972 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
3977 static int getInvisibleActiveFromInvisibleElement(int element)
3979 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
3980 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
3981 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
3985 static int getInvisibleFromInvisibleActiveElement(int element)
3987 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
3988 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
3989 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
3993 static void RedrawAllLightSwitchesAndInvisibleElements()
3998 SCAN_PLAYFIELD(x, y)
4000 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
4003 int element = Feld[x][y];
4005 if (element == EL_LIGHT_SWITCH &&
4006 game.light_time_left > 0)
4008 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
4009 DrawLevelField(x, y);
4011 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
4012 game.light_time_left == 0)
4014 Feld[x][y] = EL_LIGHT_SWITCH;
4015 DrawLevelField(x, y);
4017 else if (element == EL_EMC_DRIPPER &&
4018 game.light_time_left > 0)
4020 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
4021 DrawLevelField(x, y);
4023 else if (element == EL_EMC_DRIPPER_ACTIVE &&
4024 game.light_time_left == 0)
4026 Feld[x][y] = EL_EMC_DRIPPER;
4027 DrawLevelField(x, y);
4029 else if (element == EL_INVISIBLE_STEELWALL ||
4030 element == EL_INVISIBLE_WALL ||
4031 element == EL_INVISIBLE_SAND)
4033 if (game.light_time_left > 0)
4034 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
4036 DrawLevelField(x, y);
4038 /* uncrumble neighbour fields, if needed */
4039 if (element == EL_INVISIBLE_SAND)
4040 DrawLevelFieldCrumbledSandNeighbours(x, y);
4042 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
4043 element == EL_INVISIBLE_WALL_ACTIVE ||
4044 element == EL_INVISIBLE_SAND_ACTIVE)
4046 if (game.light_time_left == 0)
4047 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
4049 DrawLevelField(x, y);
4051 /* re-crumble neighbour fields, if needed */
4052 if (element == EL_INVISIBLE_SAND)
4053 DrawLevelFieldCrumbledSandNeighbours(x, y);
4058 static void RedrawAllInvisibleElementsForLenses()
4063 SCAN_PLAYFIELD(x, y)
4065 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
4068 int element = Feld[x][y];
4070 if (element == EL_EMC_DRIPPER &&
4071 game.lenses_time_left > 0)
4073 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
4074 DrawLevelField(x, y);
4076 else if (element == EL_EMC_DRIPPER_ACTIVE &&
4077 game.lenses_time_left == 0)
4079 Feld[x][y] = EL_EMC_DRIPPER;
4080 DrawLevelField(x, y);
4082 else if (element == EL_INVISIBLE_STEELWALL ||
4083 element == EL_INVISIBLE_WALL ||
4084 element == EL_INVISIBLE_SAND)
4086 if (game.lenses_time_left > 0)
4087 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
4089 DrawLevelField(x, y);
4091 /* uncrumble neighbour fields, if needed */
4092 if (element == EL_INVISIBLE_SAND)
4093 DrawLevelFieldCrumbledSandNeighbours(x, y);
4095 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
4096 element == EL_INVISIBLE_WALL_ACTIVE ||
4097 element == EL_INVISIBLE_SAND_ACTIVE)
4099 if (game.lenses_time_left == 0)
4100 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
4102 DrawLevelField(x, y);
4104 /* re-crumble neighbour fields, if needed */
4105 if (element == EL_INVISIBLE_SAND)
4106 DrawLevelFieldCrumbledSandNeighbours(x, y);
4111 static void RedrawAllInvisibleElementsForMagnifier()
4116 SCAN_PLAYFIELD(x, y)
4118 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
4121 int element = Feld[x][y];
4123 if (element == EL_EMC_FAKE_GRASS &&
4124 game.magnify_time_left > 0)
4126 Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
4127 DrawLevelField(x, y);
4129 else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
4130 game.magnify_time_left == 0)
4132 Feld[x][y] = EL_EMC_FAKE_GRASS;
4133 DrawLevelField(x, y);
4135 else if (IS_GATE_GRAY(element) &&
4136 game.magnify_time_left > 0)
4138 Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
4139 element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
4140 IS_EM_GATE_GRAY(element) ?
4141 element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
4142 IS_EMC_GATE_GRAY(element) ?
4143 element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
4145 DrawLevelField(x, y);
4147 else if (IS_GATE_GRAY_ACTIVE(element) &&
4148 game.magnify_time_left == 0)
4150 Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
4151 element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
4152 IS_EM_GATE_GRAY_ACTIVE(element) ?
4153 element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
4154 IS_EMC_GATE_GRAY_ACTIVE(element) ?
4155 element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
4157 DrawLevelField(x, y);
4162 static void ToggleLightSwitch(int x, int y)
4164 int element = Feld[x][y];
4166 game.light_time_left =
4167 (element == EL_LIGHT_SWITCH ?
4168 level.time_light * FRAMES_PER_SECOND : 0);
4170 RedrawAllLightSwitchesAndInvisibleElements();
4173 static void ActivateTimegateSwitch(int x, int y)
4177 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
4180 SCAN_PLAYFIELD(xx, yy)
4182 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
4185 int element = Feld[xx][yy];
4187 if (element == EL_TIMEGATE_CLOSED ||
4188 element == EL_TIMEGATE_CLOSING)
4190 Feld[xx][yy] = EL_TIMEGATE_OPENING;
4191 PlayLevelSound(xx, yy, SND_TIMEGATE_OPENING);
4195 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
4197 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
4198 DrawLevelField(xx, yy);
4204 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
4207 void Impact(int x, int y)
4209 boolean last_line = (y == lev_fieldy - 1);
4210 boolean object_hit = FALSE;
4211 boolean impact = (last_line || object_hit);
4212 int element = Feld[x][y];
4213 int smashed = EL_STEELWALL;
4215 if (!last_line) /* check if element below was hit */
4217 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
4220 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
4221 MovDir[x][y + 1] != MV_DOWN ||
4222 MovPos[x][y + 1] <= TILEY / 2));
4224 /* do not smash moving elements that left the smashed field in time */
4225 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
4226 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
4229 #if USE_QUICKSAND_IMPACT_BUGFIX
4230 if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
4232 RemoveMovingField(x, y + 1);
4233 Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
4234 Feld[x][y + 2] = EL_ROCK;
4235 DrawLevelField(x, y + 2);
4242 smashed = MovingOrBlocked2Element(x, y + 1);
4244 impact = (last_line || object_hit);
4247 if (!last_line && smashed == EL_ACID) /* element falls into acid */
4249 SplashAcid(x, y + 1);
4253 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
4254 /* only reset graphic animation if graphic really changes after impact */
4256 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
4258 ResetGfxAnimation(x, y);
4259 DrawLevelField(x, y);
4262 if (impact && CAN_EXPLODE_IMPACT(element))
4267 else if (impact && element == EL_PEARL)
4269 ResetGfxAnimation(x, y);
4271 Feld[x][y] = EL_PEARL_BREAKING;
4272 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4275 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
4277 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4282 if (impact && element == EL_AMOEBA_DROP)
4284 if (object_hit && IS_PLAYER(x, y + 1))
4285 KillPlayerUnlessEnemyProtected(x, y + 1);
4286 else if (object_hit && smashed == EL_PENGUIN)
4290 Feld[x][y] = EL_AMOEBA_GROWING;
4291 Store[x][y] = EL_AMOEBA_WET;
4293 ResetRandomAnimationValue(x, y);
4298 if (object_hit) /* check which object was hit */
4300 if (CAN_PASS_MAGIC_WALL(element) &&
4301 (smashed == EL_MAGIC_WALL ||
4302 smashed == EL_BD_MAGIC_WALL))
4305 int activated_magic_wall =
4306 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
4307 EL_BD_MAGIC_WALL_ACTIVE);
4309 /* activate magic wall / mill */
4311 SCAN_PLAYFIELD(xx, yy)
4313 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
4315 if (Feld[xx][yy] == smashed)
4316 Feld[xx][yy] = activated_magic_wall;
4318 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
4319 game.magic_wall_active = TRUE;
4321 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
4322 SND_MAGIC_WALL_ACTIVATING :
4323 SND_BD_MAGIC_WALL_ACTIVATING));
4326 if (IS_PLAYER(x, y + 1))
4328 if (CAN_SMASH_PLAYER(element))
4330 KillPlayerUnlessEnemyProtected(x, y + 1);
4334 else if (smashed == EL_PENGUIN)
4336 if (CAN_SMASH_PLAYER(element))
4342 else if (element == EL_BD_DIAMOND)
4344 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
4350 else if (((element == EL_SP_INFOTRON ||
4351 element == EL_SP_ZONK) &&
4352 (smashed == EL_SP_SNIKSNAK ||
4353 smashed == EL_SP_ELECTRON ||
4354 smashed == EL_SP_DISK_ORANGE)) ||
4355 (element == EL_SP_INFOTRON &&
4356 smashed == EL_SP_DISK_YELLOW))
4361 else if (CAN_SMASH_EVERYTHING(element))
4363 if (IS_CLASSIC_ENEMY(smashed) ||
4364 CAN_EXPLODE_SMASHED(smashed))
4369 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
4371 if (smashed == EL_LAMP ||
4372 smashed == EL_LAMP_ACTIVE)
4377 else if (smashed == EL_NUT)
4379 Feld[x][y + 1] = EL_NUT_BREAKING;
4380 PlayLevelSound(x, y, SND_NUT_BREAKING);
4381 RaiseScoreElement(EL_NUT);
4384 else if (smashed == EL_PEARL)
4386 ResetGfxAnimation(x, y);
4388 Feld[x][y + 1] = EL_PEARL_BREAKING;
4389 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4392 else if (smashed == EL_DIAMOND)
4394 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
4395 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
4398 else if (IS_BELT_SWITCH(smashed))
4400 ToggleBeltSwitch(x, y + 1);
4402 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
4403 smashed == EL_SWITCHGATE_SWITCH_DOWN)
4405 ToggleSwitchgateSwitch(x, y + 1);
4407 else if (smashed == EL_LIGHT_SWITCH ||
4408 smashed == EL_LIGHT_SWITCH_ACTIVE)
4410 ToggleLightSwitch(x, y + 1);
4415 TestIfElementSmashesCustomElement(x, y, MV_DOWN);
4418 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4420 CheckElementChangeBySide(x, y + 1, smashed, element,
4421 CE_SWITCHED, CH_SIDE_TOP);
4422 CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
4428 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4433 /* play sound of magic wall / mill */
4435 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
4436 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
4438 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
4439 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
4440 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
4441 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
4446 /* play sound of object that hits the ground */
4447 if (last_line || object_hit)
4448 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4451 inline static void TurnRoundExt(int x, int y)
4463 { 0, 0 }, { 0, 0 }, { 0, 0 },
4468 int left, right, back;
4472 { MV_DOWN, MV_UP, MV_RIGHT },
4473 { MV_UP, MV_DOWN, MV_LEFT },
4475 { MV_LEFT, MV_RIGHT, MV_DOWN },
4479 { MV_RIGHT, MV_LEFT, MV_UP }
4482 int element = Feld[x][y];
4483 int move_pattern = element_info[element].move_pattern;
4485 int old_move_dir = MovDir[x][y];
4486 int left_dir = turn[old_move_dir].left;
4487 int right_dir = turn[old_move_dir].right;
4488 int back_dir = turn[old_move_dir].back;
4490 int left_dx = move_xy[left_dir].dx, left_dy = move_xy[left_dir].dy;
4491 int right_dx = move_xy[right_dir].dx, right_dy = move_xy[right_dir].dy;
4492 int move_dx = move_xy[old_move_dir].dx, move_dy = move_xy[old_move_dir].dy;
4493 int back_dx = move_xy[back_dir].dx, back_dy = move_xy[back_dir].dy;
4495 int left_x = x + left_dx, left_y = y + left_dy;
4496 int right_x = x + right_dx, right_y = y + right_dy;
4497 int move_x = x + move_dx, move_y = y + move_dy;
4501 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4503 TestIfBadThingTouchesOtherBadThing(x, y);
4505 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
4506 MovDir[x][y] = right_dir;
4507 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4508 MovDir[x][y] = left_dir;
4510 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
4512 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
4515 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
4517 TestIfBadThingTouchesOtherBadThing(x, y);
4519 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
4520 MovDir[x][y] = left_dir;
4521 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4522 MovDir[x][y] = right_dir;
4524 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
4526 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
4529 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4531 TestIfBadThingTouchesOtherBadThing(x, y);
4533 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
4534 MovDir[x][y] = left_dir;
4535 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
4536 MovDir[x][y] = right_dir;
4538 if (MovDir[x][y] != old_move_dir)
4541 else if (element == EL_YAMYAM)
4543 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
4544 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
4546 if (can_turn_left && can_turn_right)
4547 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4548 else if (can_turn_left)
4549 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4550 else if (can_turn_right)
4551 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4553 MovDir[x][y] = back_dir;
4555 MovDelay[x][y] = 16 + 16 * RND(3);
4557 else if (element == EL_DARK_YAMYAM)
4559 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4561 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4564 if (can_turn_left && can_turn_right)
4565 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4566 else if (can_turn_left)
4567 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4568 else if (can_turn_right)
4569 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4571 MovDir[x][y] = back_dir;
4573 MovDelay[x][y] = 16 + 16 * RND(3);
4575 else if (element == EL_PACMAN)
4577 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
4578 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
4580 if (can_turn_left && can_turn_right)
4581 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4582 else if (can_turn_left)
4583 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4584 else if (can_turn_right)
4585 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4587 MovDir[x][y] = back_dir;
4589 MovDelay[x][y] = 6 + RND(40);
4591 else if (element == EL_PIG)
4593 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
4594 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
4595 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
4596 boolean should_turn_left, should_turn_right, should_move_on;
4598 int rnd = RND(rnd_value);
4600 should_turn_left = (can_turn_left &&
4602 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
4603 y + back_dy + left_dy)));
4604 should_turn_right = (can_turn_right &&
4606 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
4607 y + back_dy + right_dy)));
4608 should_move_on = (can_move_on &&
4611 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
4612 y + move_dy + left_dy) ||
4613 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
4614 y + move_dy + right_dy)));
4616 if (should_turn_left || should_turn_right || should_move_on)
4618 if (should_turn_left && should_turn_right && should_move_on)
4619 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
4620 rnd < 2 * rnd_value / 3 ? right_dir :
4622 else if (should_turn_left && should_turn_right)
4623 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4624 else if (should_turn_left && should_move_on)
4625 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
4626 else if (should_turn_right && should_move_on)
4627 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
4628 else if (should_turn_left)
4629 MovDir[x][y] = left_dir;
4630 else if (should_turn_right)
4631 MovDir[x][y] = right_dir;
4632 else if (should_move_on)
4633 MovDir[x][y] = old_move_dir;
4635 else if (can_move_on && rnd > rnd_value / 8)
4636 MovDir[x][y] = old_move_dir;
4637 else if (can_turn_left && can_turn_right)
4638 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4639 else if (can_turn_left && rnd > rnd_value / 8)
4640 MovDir[x][y] = left_dir;
4641 else if (can_turn_right && rnd > rnd_value/8)
4642 MovDir[x][y] = right_dir;
4644 MovDir[x][y] = back_dir;
4646 xx = x + move_xy[MovDir[x][y]].dx;
4647 yy = y + move_xy[MovDir[x][y]].dy;
4649 if (!IN_LEV_FIELD(xx, yy) ||
4650 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
4651 MovDir[x][y] = old_move_dir;
4655 else if (element == EL_DRAGON)
4657 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
4658 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
4659 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
4661 int rnd = RND(rnd_value);
4663 if (can_move_on && rnd > rnd_value / 8)
4664 MovDir[x][y] = old_move_dir;
4665 else if (can_turn_left && can_turn_right)
4666 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4667 else if (can_turn_left && rnd > rnd_value / 8)
4668 MovDir[x][y] = left_dir;
4669 else if (can_turn_right && rnd > rnd_value / 8)
4670 MovDir[x][y] = right_dir;
4672 MovDir[x][y] = back_dir;
4674 xx = x + move_xy[MovDir[x][y]].dx;
4675 yy = y + move_xy[MovDir[x][y]].dy;
4677 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
4678 MovDir[x][y] = old_move_dir;
4682 else if (element == EL_MOLE)
4684 boolean can_move_on =
4685 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
4686 IS_AMOEBOID(Feld[move_x][move_y]) ||
4687 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
4690 boolean can_turn_left =
4691 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
4692 IS_AMOEBOID(Feld[left_x][left_y])));
4694 boolean can_turn_right =
4695 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
4696 IS_AMOEBOID(Feld[right_x][right_y])));
4698 if (can_turn_left && can_turn_right)
4699 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
4700 else if (can_turn_left)
4701 MovDir[x][y] = left_dir;
4703 MovDir[x][y] = right_dir;
4706 if (MovDir[x][y] != old_move_dir)
4709 else if (element == EL_BALLOON)
4711 MovDir[x][y] = game.wind_direction;
4714 else if (element == EL_SPRING)
4716 #if USE_NEW_SPRING_BUMPER
4717 if (MovDir[x][y] & MV_HORIZONTAL)
4719 if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
4720 !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
4722 Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
4723 ResetGfxAnimation(move_x, move_y);
4724 DrawLevelField(move_x, move_y);
4726 MovDir[x][y] = back_dir;
4728 else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
4729 SPRING_CAN_ENTER_FIELD(element, x, y + 1))
4730 MovDir[x][y] = MV_NONE;
4733 if (MovDir[x][y] & MV_HORIZONTAL &&
4734 (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
4735 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
4736 MovDir[x][y] = MV_NONE;
4741 else if (element == EL_ROBOT ||
4742 element == EL_SATELLITE ||
4743 element == EL_PENGUIN ||
4744 element == EL_EMC_ANDROID)
4746 int attr_x = -1, attr_y = -1;
4757 for (i = 0; i < MAX_PLAYERS; i++)
4759 struct PlayerInfo *player = &stored_player[i];
4760 int jx = player->jx, jy = player->jy;
4762 if (!player->active)
4766 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
4774 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
4775 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
4776 game.engine_version < VERSION_IDENT(3,1,0,0)))
4782 if (element == EL_PENGUIN)
4785 static int xy[4][2] =
4793 for (i = 0; i < NUM_DIRECTIONS; i++)
4795 int ex = x + xy[i][0];
4796 int ey = y + xy[i][1];
4798 if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
4807 MovDir[x][y] = MV_NONE;
4809 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
4810 else if (attr_x > x)
4811 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
4813 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
4814 else if (attr_y > y)
4815 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
4817 if (element == EL_ROBOT)
4821 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4822 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
4823 Moving2Blocked(x, y, &newx, &newy);
4825 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
4826 MovDelay[x][y] = 8 + 8 * !RND(3);
4828 MovDelay[x][y] = 16;
4830 else if (element == EL_PENGUIN)
4836 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4838 boolean first_horiz = RND(2);
4839 int new_move_dir = MovDir[x][y];
4842 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4843 Moving2Blocked(x, y, &newx, &newy);
4845 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
4849 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4850 Moving2Blocked(x, y, &newx, &newy);
4852 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
4855 MovDir[x][y] = old_move_dir;
4859 else if (element == EL_SATELLITE)
4865 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4867 boolean first_horiz = RND(2);
4868 int new_move_dir = MovDir[x][y];
4871 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4872 Moving2Blocked(x, y, &newx, &newy);
4874 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
4878 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4879 Moving2Blocked(x, y, &newx, &newy);
4881 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
4884 MovDir[x][y] = old_move_dir;
4888 else if (element == EL_EMC_ANDROID)
4890 static int check_pos[16] =
4892 -1, /* 0 => (invalid) */
4893 7, /* 1 => MV_LEFT */
4894 3, /* 2 => MV_RIGHT */
4895 -1, /* 3 => (invalid) */
4897 0, /* 5 => MV_LEFT | MV_UP */
4898 2, /* 6 => MV_RIGHT | MV_UP */
4899 -1, /* 7 => (invalid) */
4900 5, /* 8 => MV_DOWN */
4901 6, /* 9 => MV_LEFT | MV_DOWN */
4902 4, /* 10 => MV_RIGHT | MV_DOWN */
4903 -1, /* 11 => (invalid) */
4904 -1, /* 12 => (invalid) */
4905 -1, /* 13 => (invalid) */
4906 -1, /* 14 => (invalid) */
4907 -1, /* 15 => (invalid) */
4915 { -1, -1, MV_LEFT | MV_UP },
4917 { +1, -1, MV_RIGHT | MV_UP },
4918 { +1, 0, MV_RIGHT },
4919 { +1, +1, MV_RIGHT | MV_DOWN },
4921 { -1, +1, MV_LEFT | MV_DOWN },
4924 int start_pos, check_order;
4925 boolean can_clone = FALSE;
4928 /* check if there is any free field around current position */
4929 for (i = 0; i < 8; i++)
4931 int newx = x + check_xy[i].dx;
4932 int newy = y + check_xy[i].dy;
4934 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
4942 if (can_clone) /* randomly find an element to clone */
4946 start_pos = check_pos[RND(8)];
4947 check_order = (RND(2) ? -1 : +1);
4949 for (i = 0; i < 8; i++)
4951 int pos_raw = start_pos + i * check_order;
4952 int pos = (pos_raw + 8) % 8;
4953 int newx = x + check_xy[pos].dx;
4954 int newy = y + check_xy[pos].dy;
4956 if (ANDROID_CAN_CLONE_FIELD(newx, newy))
4958 element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
4959 element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
4961 Store[x][y] = Feld[newx][newy];
4970 if (can_clone) /* randomly find a direction to move */
4974 start_pos = check_pos[RND(8)];
4975 check_order = (RND(2) ? -1 : +1);
4977 for (i = 0; i < 8; i++)
4979 int pos_raw = start_pos + i * check_order;
4980 int pos = (pos_raw + 8) % 8;
4981 int newx = x + check_xy[pos].dx;
4982 int newy = y + check_xy[pos].dy;
4983 int new_move_dir = check_xy[pos].dir;
4985 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
4987 MovDir[x][y] = new_move_dir;
4988 MovDelay[x][y] = level.android_clone_time * 8 + 1;
4997 if (can_clone) /* cloning and moving successful */
5000 /* cannot clone -- try to move towards player */
5002 start_pos = check_pos[MovDir[x][y] & 0x0f];
5003 check_order = (RND(2) ? -1 : +1);
5005 for (i = 0; i < 3; i++)
5007 /* first check start_pos, then previous/next or (next/previous) pos */
5008 int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
5009 int pos = (pos_raw + 8) % 8;
5010 int newx = x + check_xy[pos].dx;
5011 int newy = y + check_xy[pos].dy;
5012 int new_move_dir = check_xy[pos].dir;
5014 if (IS_PLAYER(newx, newy))
5017 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
5019 MovDir[x][y] = new_move_dir;
5020 MovDelay[x][y] = level.android_move_time * 8 + 1;
5027 else if (move_pattern == MV_TURNING_LEFT ||
5028 move_pattern == MV_TURNING_RIGHT ||
5029 move_pattern == MV_TURNING_LEFT_RIGHT ||
5030 move_pattern == MV_TURNING_RIGHT_LEFT ||
5031 move_pattern == MV_TURNING_RANDOM ||
5032 move_pattern == MV_ALL_DIRECTIONS)
5034 boolean can_turn_left =
5035 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
5036 boolean can_turn_right =
5037 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
5039 if (element_info[element].move_stepsize == 0) /* "not moving" */
5042 if (move_pattern == MV_TURNING_LEFT)
5043 MovDir[x][y] = left_dir;
5044 else if (move_pattern == MV_TURNING_RIGHT)
5045 MovDir[x][y] = right_dir;
5046 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
5047 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
5048 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
5049 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
5050 else if (move_pattern == MV_TURNING_RANDOM)
5051 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
5052 can_turn_right && !can_turn_left ? right_dir :
5053 RND(2) ? left_dir : right_dir);
5054 else if (can_turn_left && can_turn_right)
5055 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5056 else if (can_turn_left)
5057 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5058 else if (can_turn_right)
5059 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5061 MovDir[x][y] = back_dir;
5063 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5065 else if (move_pattern == MV_HORIZONTAL ||
5066 move_pattern == MV_VERTICAL)
5068 if (move_pattern & old_move_dir)
5069 MovDir[x][y] = back_dir;
5070 else if (move_pattern == MV_HORIZONTAL)
5071 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
5072 else if (move_pattern == MV_VERTICAL)
5073 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
5075 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5077 else if (move_pattern & MV_ANY_DIRECTION)
5079 MovDir[x][y] = move_pattern;
5080 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5082 else if (move_pattern & MV_WIND_DIRECTION)
5084 MovDir[x][y] = game.wind_direction;
5085 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5087 else if (move_pattern == MV_ALONG_LEFT_SIDE)
5089 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
5090 MovDir[x][y] = left_dir;
5091 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5092 MovDir[x][y] = right_dir;
5094 if (MovDir[x][y] != old_move_dir)
5095 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5097 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
5099 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
5100 MovDir[x][y] = right_dir;
5101 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5102 MovDir[x][y] = left_dir;
5104 if (MovDir[x][y] != old_move_dir)
5105 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5107 else if (move_pattern == MV_TOWARDS_PLAYER ||
5108 move_pattern == MV_AWAY_FROM_PLAYER)
5110 int attr_x = -1, attr_y = -1;
5112 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
5123 for (i = 0; i < MAX_PLAYERS; i++)
5125 struct PlayerInfo *player = &stored_player[i];
5126 int jx = player->jx, jy = player->jy;
5128 if (!player->active)
5132 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
5140 MovDir[x][y] = MV_NONE;
5142 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
5143 else if (attr_x > x)
5144 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
5146 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
5147 else if (attr_y > y)
5148 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
5150 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5152 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5154 boolean first_horiz = RND(2);
5155 int new_move_dir = MovDir[x][y];
5157 if (element_info[element].move_stepsize == 0) /* "not moving" */
5159 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
5160 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5166 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5167 Moving2Blocked(x, y, &newx, &newy);
5169 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5173 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5174 Moving2Blocked(x, y, &newx, &newy);
5176 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5179 MovDir[x][y] = old_move_dir;
5182 else if (move_pattern == MV_WHEN_PUSHED ||
5183 move_pattern == MV_WHEN_DROPPED)
5185 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5186 MovDir[x][y] = MV_NONE;
5190 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
5192 static int test_xy[7][2] =
5202 static int test_dir[7] =
5212 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
5213 int move_preference = -1000000; /* start with very low preference */
5214 int new_move_dir = MV_NONE;
5215 int start_test = RND(4);
5218 for (i = 0; i < NUM_DIRECTIONS; i++)
5220 int move_dir = test_dir[start_test + i];
5221 int move_dir_preference;
5223 xx = x + test_xy[start_test + i][0];
5224 yy = y + test_xy[start_test + i][1];
5226 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
5227 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
5229 new_move_dir = move_dir;
5234 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
5237 move_dir_preference = -1 * RunnerVisit[xx][yy];
5238 if (hunter_mode && PlayerVisit[xx][yy] > 0)
5239 move_dir_preference = PlayerVisit[xx][yy];
5241 if (move_dir_preference > move_preference)
5243 /* prefer field that has not been visited for the longest time */
5244 move_preference = move_dir_preference;
5245 new_move_dir = move_dir;
5247 else if (move_dir_preference == move_preference &&
5248 move_dir == old_move_dir)
5250 /* prefer last direction when all directions are preferred equally */
5251 move_preference = move_dir_preference;
5252 new_move_dir = move_dir;
5256 MovDir[x][y] = new_move_dir;
5257 if (old_move_dir != new_move_dir)
5258 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5262 static void TurnRound(int x, int y)
5264 int direction = MovDir[x][y];
5268 GfxDir[x][y] = MovDir[x][y];
5270 if (direction != MovDir[x][y])
5274 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
5277 static boolean JustBeingPushed(int x, int y)
5281 for (i = 0; i < MAX_PLAYERS; i++)
5283 struct PlayerInfo *player = &stored_player[i];
5285 if (player->active && player->is_pushing && player->MovPos)
5287 int next_jx = player->jx + (player->jx - player->last_jx);
5288 int next_jy = player->jy + (player->jy - player->last_jy);
5290 if (x == next_jx && y == next_jy)
5298 void StartMoving(int x, int y)
5300 boolean started_moving = FALSE; /* some elements can fall _and_ move */
5301 int element = Feld[x][y];
5306 if (MovDelay[x][y] == 0)
5307 GfxAction[x][y] = ACTION_DEFAULT;
5309 if (CAN_FALL(element) && y < lev_fieldy - 1)
5311 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
5312 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
5313 if (JustBeingPushed(x, y))
5316 if (element == EL_QUICKSAND_FULL)
5318 if (IS_FREE(x, y + 1))
5320 InitMovingField(x, y, MV_DOWN);
5321 started_moving = TRUE;
5323 Feld[x][y] = EL_QUICKSAND_EMPTYING;
5324 Store[x][y] = EL_ROCK;
5326 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
5328 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5330 if (!MovDelay[x][y])
5331 MovDelay[x][y] = TILEY + 1;
5340 Feld[x][y] = EL_QUICKSAND_EMPTY;
5341 Feld[x][y + 1] = EL_QUICKSAND_FULL;
5342 Store[x][y + 1] = Store[x][y];
5345 PlayLevelSoundAction(x, y, ACTION_FILLING);
5348 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
5349 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5351 InitMovingField(x, y, MV_DOWN);
5352 started_moving = TRUE;
5354 Feld[x][y] = EL_QUICKSAND_FILLING;
5355 Store[x][y] = element;
5357 PlayLevelSoundAction(x, y, ACTION_FILLING);
5359 else if (element == EL_MAGIC_WALL_FULL)
5361 if (IS_FREE(x, y + 1))
5363 InitMovingField(x, y, MV_DOWN);
5364 started_moving = TRUE;
5366 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
5367 Store[x][y] = EL_CHANGED(Store[x][y]);
5369 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
5371 if (!MovDelay[x][y])
5372 MovDelay[x][y] = TILEY/4 + 1;
5381 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
5382 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
5383 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
5387 else if (element == EL_BD_MAGIC_WALL_FULL)
5389 if (IS_FREE(x, y + 1))
5391 InitMovingField(x, y, MV_DOWN);
5392 started_moving = TRUE;
5394 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
5395 Store[x][y] = EL_CHANGED2(Store[x][y]);
5397 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
5399 if (!MovDelay[x][y])
5400 MovDelay[x][y] = TILEY/4 + 1;
5409 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
5410 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
5411 Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
5415 else if (CAN_PASS_MAGIC_WALL(element) &&
5416 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
5417 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
5419 InitMovingField(x, y, MV_DOWN);
5420 started_moving = TRUE;
5423 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
5424 EL_BD_MAGIC_WALL_FILLING);
5425 Store[x][y] = element;
5427 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
5429 SplashAcid(x, y + 1);
5431 InitMovingField(x, y, MV_DOWN);
5432 started_moving = TRUE;
5434 Store[x][y] = EL_ACID;
5436 else if ((game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5437 CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
5439 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
5440 CAN_FALL(element) && WasJustFalling[x][y] &&
5441 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
5443 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
5444 CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
5445 (Feld[x][y + 1] == EL_BLOCKED)))
5447 /* this is needed for a special case not covered by calling "Impact()"
5448 from "ContinueMoving()": if an element moves to a tile directly below
5449 another element which was just falling on that tile (which was empty
5450 in the previous frame), the falling element above would just stop
5451 instead of smashing the element below (in previous version, the above
5452 element was just checked for "moving" instead of "falling", resulting
5453 in incorrect smashes caused by horizontal movement of the above
5454 element; also, the case of the player being the element to smash was
5455 simply not covered here... :-/ ) */
5457 CheckCollision[x][y] = 0;
5461 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
5463 if (MovDir[x][y] == MV_NONE)
5465 InitMovingField(x, y, MV_DOWN);
5466 started_moving = TRUE;
5469 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
5471 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
5472 MovDir[x][y] = MV_DOWN;
5474 InitMovingField(x, y, MV_DOWN);
5475 started_moving = TRUE;
5477 else if (element == EL_AMOEBA_DROP)
5479 Feld[x][y] = EL_AMOEBA_GROWING;
5480 Store[x][y] = EL_AMOEBA_WET;
5482 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
5483 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
5484 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
5485 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
5487 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
5488 (IS_FREE(x - 1, y + 1) ||
5489 Feld[x - 1][y + 1] == EL_ACID));
5490 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
5491 (IS_FREE(x + 1, y + 1) ||
5492 Feld[x + 1][y + 1] == EL_ACID));
5493 boolean can_fall_any = (can_fall_left || can_fall_right);
5494 boolean can_fall_both = (can_fall_left && can_fall_right);
5495 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
5497 #if USE_NEW_ALL_SLIPPERY
5498 if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
5500 if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
5501 can_fall_right = FALSE;
5502 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
5503 can_fall_left = FALSE;
5504 else if (slippery_type == SLIPPERY_ONLY_LEFT)
5505 can_fall_right = FALSE;
5506 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
5507 can_fall_left = FALSE;
5509 can_fall_any = (can_fall_left || can_fall_right);
5510 can_fall_both = FALSE;
5513 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
5515 if (slippery_type == SLIPPERY_ONLY_LEFT)
5516 can_fall_right = FALSE;
5517 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
5518 can_fall_left = FALSE;
5519 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
5520 can_fall_right = FALSE;
5521 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
5522 can_fall_left = FALSE;
5524 can_fall_any = (can_fall_left || can_fall_right);
5525 can_fall_both = (can_fall_left && can_fall_right);
5529 #if USE_NEW_ALL_SLIPPERY
5531 #if USE_NEW_SP_SLIPPERY
5532 /* !!! better use the same properties as for custom elements here !!! */
5533 else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
5534 can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
5536 can_fall_right = FALSE; /* slip down on left side */
5537 can_fall_both = FALSE;
5542 #if USE_NEW_ALL_SLIPPERY
5545 if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
5546 can_fall_right = FALSE; /* slip down on left side */
5548 can_fall_left = !(can_fall_right = RND(2));
5550 can_fall_both = FALSE;
5555 if (game.emulation == EMU_BOULDERDASH ||
5556 element == EL_BD_ROCK || element == EL_BD_DIAMOND)
5557 can_fall_right = FALSE; /* slip down on left side */
5559 can_fall_left = !(can_fall_right = RND(2));
5561 can_fall_both = FALSE;
5567 /* if not determined otherwise, prefer left side for slipping down */
5568 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
5569 started_moving = TRUE;
5573 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
5575 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
5578 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
5579 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
5580 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
5581 int belt_dir = game.belt_dir[belt_nr];
5583 if ((belt_dir == MV_LEFT && left_is_free) ||
5584 (belt_dir == MV_RIGHT && right_is_free))
5586 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
5588 InitMovingField(x, y, belt_dir);
5589 started_moving = TRUE;
5591 Pushed[x][y] = TRUE;
5592 Pushed[nextx][y] = TRUE;
5594 GfxAction[x][y] = ACTION_DEFAULT;
5598 MovDir[x][y] = 0; /* if element was moving, stop it */
5603 /* not "else if" because of elements that can fall and move (EL_SPRING) */
5605 if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
5607 if (CAN_MOVE(element) && !started_moving)
5610 int move_pattern = element_info[element].move_pattern;
5615 if (MovDir[x][y] == MV_NONE)
5617 printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
5618 x, y, element, element_info[element].token_name);
5619 printf("StartMoving(): This should never happen!\n");
5624 Moving2Blocked(x, y, &newx, &newy);
5626 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
5629 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5630 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5632 WasJustMoving[x][y] = 0;
5633 CheckCollision[x][y] = 0;
5635 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
5637 if (Feld[x][y] != element) /* element has changed */
5641 if (!MovDelay[x][y]) /* start new movement phase */
5643 /* all objects that can change their move direction after each step
5644 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
5646 if (element != EL_YAMYAM &&
5647 element != EL_DARK_YAMYAM &&
5648 element != EL_PACMAN &&
5649 !(move_pattern & MV_ANY_DIRECTION) &&
5650 move_pattern != MV_TURNING_LEFT &&
5651 move_pattern != MV_TURNING_RIGHT &&
5652 move_pattern != MV_TURNING_LEFT_RIGHT &&
5653 move_pattern != MV_TURNING_RIGHT_LEFT &&
5654 move_pattern != MV_TURNING_RANDOM)
5658 if (MovDelay[x][y] && (element == EL_BUG ||
5659 element == EL_SPACESHIP ||
5660 element == EL_SP_SNIKSNAK ||
5661 element == EL_SP_ELECTRON ||
5662 element == EL_MOLE))
5663 DrawLevelField(x, y);
5667 if (MovDelay[x][y]) /* wait some time before next movement */
5671 if (element == EL_ROBOT ||
5672 element == EL_YAMYAM ||
5673 element == EL_DARK_YAMYAM)
5675 DrawLevelElementAnimationIfNeeded(x, y, element);
5676 PlayLevelSoundAction(x, y, ACTION_WAITING);
5678 else if (element == EL_SP_ELECTRON)
5679 DrawLevelElementAnimationIfNeeded(x, y, element);
5680 else if (element == EL_DRAGON)
5683 int dir = MovDir[x][y];
5684 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
5685 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
5686 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
5687 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
5688 dir == MV_UP ? IMG_FLAMES_1_UP :
5689 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
5690 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5692 GfxAction[x][y] = ACTION_ATTACKING;
5694 if (IS_PLAYER(x, y))
5695 DrawPlayerField(x, y);
5697 DrawLevelField(x, y);
5699 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
5701 for (i = 1; i <= 3; i++)
5703 int xx = x + i * dx;
5704 int yy = y + i * dy;
5705 int sx = SCREENX(xx);
5706 int sy = SCREENY(yy);
5707 int flame_graphic = graphic + (i - 1);
5709 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
5714 int flamed = MovingOrBlocked2Element(xx, yy);
5718 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
5720 else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
5721 RemoveMovingField(xx, yy);
5723 RemoveField(xx, yy);
5725 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
5728 RemoveMovingField(xx, yy);
5731 ChangeDelay[xx][yy] = 0;
5733 Feld[xx][yy] = EL_FLAMES;
5735 if (IN_SCR_FIELD(sx, sy))
5737 DrawLevelFieldCrumbledSand(xx, yy);
5738 DrawGraphic(sx, sy, flame_graphic, frame);
5743 if (Feld[xx][yy] == EL_FLAMES)
5744 Feld[xx][yy] = EL_EMPTY;
5745 DrawLevelField(xx, yy);
5750 if (MovDelay[x][y]) /* element still has to wait some time */
5752 PlayLevelSoundAction(x, y, ACTION_WAITING);
5758 /* now make next step */
5760 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
5762 if (DONT_COLLIDE_WITH(element) &&
5763 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
5764 !PLAYER_ENEMY_PROTECTED(newx, newy))
5766 TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
5771 else if (CAN_MOVE_INTO_ACID(element) &&
5772 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
5773 !IS_MV_DIAGONAL(MovDir[x][y]) &&
5774 (MovDir[x][y] == MV_DOWN ||
5775 game.engine_version >= VERSION_IDENT(3,1,0,0)))
5777 SplashAcid(newx, newy);
5778 Store[x][y] = EL_ACID;
5780 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
5782 if (Feld[newx][newy] == EL_EXIT_OPEN)
5785 DrawLevelField(x, y);
5787 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
5788 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
5789 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
5791 local_player->friends_still_needed--;
5792 if (!local_player->friends_still_needed &&
5793 !local_player->GameOver && AllPlayersGone)
5794 local_player->LevelSolved = local_player->GameOver = TRUE;
5798 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
5800 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
5801 DrawLevelField(newx, newy);
5803 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
5805 else if (!IS_FREE(newx, newy))
5807 GfxAction[x][y] = ACTION_WAITING;
5809 if (IS_PLAYER(x, y))
5810 DrawPlayerField(x, y);
5812 DrawLevelField(x, y);
5817 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
5819 if (IS_FOOD_PIG(Feld[newx][newy]))
5821 if (IS_MOVING(newx, newy))
5822 RemoveMovingField(newx, newy);
5825 Feld[newx][newy] = EL_EMPTY;
5826 DrawLevelField(newx, newy);
5829 PlayLevelSound(x, y, SND_PIG_DIGGING);
5831 else if (!IS_FREE(newx, newy))
5833 if (IS_PLAYER(x, y))
5834 DrawPlayerField(x, y);
5836 DrawLevelField(x, y);
5841 else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
5843 if (Store[x][y] != EL_EMPTY)
5845 boolean can_clone = FALSE;
5848 /* check if element to clone is still there */
5849 for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
5851 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
5859 /* cannot clone or target field not free anymore -- do not clone */
5860 if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
5861 Store[x][y] = EL_EMPTY;
5864 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
5866 if (IS_MV_DIAGONAL(MovDir[x][y]))
5868 int diagonal_move_dir = MovDir[x][y];
5869 int stored = Store[x][y];
5870 int change_delay = 8;
5873 /* android is moving diagonally */
5875 CreateField(x, y, EL_DIAGONAL_SHRINKING);
5877 Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
5878 GfxElement[x][y] = EL_EMC_ANDROID;
5879 GfxAction[x][y] = ACTION_SHRINKING;
5880 GfxDir[x][y] = diagonal_move_dir;
5881 ChangeDelay[x][y] = change_delay;
5883 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
5886 DrawLevelGraphicAnimation(x, y, graphic);
5887 PlayLevelSoundAction(x, y, ACTION_SHRINKING);
5889 if (Feld[newx][newy] == EL_ACID)
5891 SplashAcid(newx, newy);
5896 CreateField(newx, newy, EL_DIAGONAL_GROWING);
5898 Store[newx][newy] = EL_EMC_ANDROID;
5899 GfxElement[newx][newy] = EL_EMC_ANDROID;
5900 GfxAction[newx][newy] = ACTION_GROWING;
5901 GfxDir[newx][newy] = diagonal_move_dir;
5902 ChangeDelay[newx][newy] = change_delay;
5904 graphic = el_act_dir2img(GfxElement[newx][newy],
5905 GfxAction[newx][newy], GfxDir[newx][newy]);
5907 DrawLevelGraphicAnimation(newx, newy, graphic);
5908 PlayLevelSoundAction(newx, newy, ACTION_GROWING);
5914 Feld[newx][newy] = EL_EMPTY;
5915 DrawLevelField(newx, newy);
5917 PlayLevelSoundAction(x, y, ACTION_DIGGING);
5920 else if (!IS_FREE(newx, newy))
5923 if (IS_PLAYER(x, y))
5924 DrawPlayerField(x, y);
5926 DrawLevelField(x, y);
5932 else if (IS_CUSTOM_ELEMENT(element) &&
5933 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5935 int new_element = Feld[newx][newy];
5937 if (!IS_FREE(newx, newy))
5939 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
5940 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
5943 /* no element can dig solid indestructible elements */
5944 if (IS_INDESTRUCTIBLE(new_element) &&
5945 !IS_DIGGABLE(new_element) &&
5946 !IS_COLLECTIBLE(new_element))
5949 if (AmoebaNr[newx][newy] &&
5950 (new_element == EL_AMOEBA_FULL ||
5951 new_element == EL_BD_AMOEBA ||
5952 new_element == EL_AMOEBA_GROWING))
5954 AmoebaCnt[AmoebaNr[newx][newy]]--;
5955 AmoebaCnt2[AmoebaNr[newx][newy]]--;
5958 if (IS_MOVING(newx, newy))
5959 RemoveMovingField(newx, newy);
5962 RemoveField(newx, newy);
5963 DrawLevelField(newx, newy);
5966 /* if digged element was about to explode, prevent the explosion */
5967 ExplodeField[newx][newy] = EX_TYPE_NONE;
5969 PlayLevelSoundAction(x, y, action);
5972 Store[newx][newy] = EL_EMPTY;
5974 /* this makes it possible to leave the removed element again */
5975 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
5976 Store[newx][newy] = new_element;
5978 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
5980 int move_leave_element = element_info[element].move_leave_element;
5982 /* this makes it possible to leave the removed element again */
5983 Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
5984 new_element : move_leave_element);
5988 if (move_pattern & MV_MAZE_RUNNER_STYLE)
5990 RunnerVisit[x][y] = FrameCounter;
5991 PlayerVisit[x][y] /= 8; /* expire player visit path */
5994 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
5996 if (!IS_FREE(newx, newy))
5998 if (IS_PLAYER(x, y))
5999 DrawPlayerField(x, y);
6001 DrawLevelField(x, y);
6007 boolean wanna_flame = !RND(10);
6008 int dx = newx - x, dy = newy - y;
6009 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
6010 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
6011 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
6012 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
6013 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
6014 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
6017 IS_CLASSIC_ENEMY(element1) ||
6018 IS_CLASSIC_ENEMY(element2)) &&
6019 element1 != EL_DRAGON && element2 != EL_DRAGON &&
6020 element1 != EL_FLAMES && element2 != EL_FLAMES)
6022 ResetGfxAnimation(x, y);
6023 GfxAction[x][y] = ACTION_ATTACKING;
6025 if (IS_PLAYER(x, y))
6026 DrawPlayerField(x, y);
6028 DrawLevelField(x, y);
6030 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
6032 MovDelay[x][y] = 50;
6036 RemoveField(newx, newy);
6038 Feld[newx][newy] = EL_FLAMES;
6039 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
6042 RemoveField(newx1, newy1);
6044 Feld[newx1][newy1] = EL_FLAMES;
6046 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
6049 RemoveField(newx2, newy2);
6051 Feld[newx2][newy2] = EL_FLAMES;
6058 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6059 Feld[newx][newy] == EL_DIAMOND)
6061 if (IS_MOVING(newx, newy))
6062 RemoveMovingField(newx, newy);
6065 Feld[newx][newy] = EL_EMPTY;
6066 DrawLevelField(newx, newy);
6069 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
6071 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6072 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
6074 if (AmoebaNr[newx][newy])
6076 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6077 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
6078 Feld[newx][newy] == EL_BD_AMOEBA)
6079 AmoebaCnt[AmoebaNr[newx][newy]]--;
6084 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
6086 RemoveMovingField(newx, newy);
6089 if (IS_MOVING(newx, newy))
6091 RemoveMovingField(newx, newy);
6096 Feld[newx][newy] = EL_EMPTY;
6097 DrawLevelField(newx, newy);
6100 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
6102 else if ((element == EL_PACMAN || element == EL_MOLE)
6103 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
6105 if (AmoebaNr[newx][newy])
6107 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6108 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
6109 Feld[newx][newy] == EL_BD_AMOEBA)
6110 AmoebaCnt[AmoebaNr[newx][newy]]--;
6113 if (element == EL_MOLE)
6115 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
6116 PlayLevelSound(x, y, SND_MOLE_DIGGING);
6118 ResetGfxAnimation(x, y);
6119 GfxAction[x][y] = ACTION_DIGGING;
6120 DrawLevelField(x, y);
6122 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
6124 return; /* wait for shrinking amoeba */
6126 else /* element == EL_PACMAN */
6128 Feld[newx][newy] = EL_EMPTY;
6129 DrawLevelField(newx, newy);
6130 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
6133 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
6134 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
6135 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
6137 /* wait for shrinking amoeba to completely disappear */
6140 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
6142 /* object was running against a wall */
6147 /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
6148 if (move_pattern & MV_ANY_DIRECTION &&
6149 move_pattern == MovDir[x][y])
6151 int blocking_element =
6152 (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
6154 CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
6157 element = Feld[x][y]; /* element might have changed */
6161 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
6162 DrawLevelElementAnimation(x, y, element);
6164 if (DONT_TOUCH(element))
6165 TestIfBadThingTouchesPlayer(x, y);
6170 InitMovingField(x, y, MovDir[x][y]);
6172 PlayLevelSoundAction(x, y, ACTION_MOVING);
6176 ContinueMoving(x, y);
6179 void ContinueMoving(int x, int y)
6181 int element = Feld[x][y];
6182 struct ElementInfo *ei = &element_info[element];
6183 int direction = MovDir[x][y];
6184 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
6185 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
6186 int newx = x + dx, newy = y + dy;
6187 int stored = Store[x][y];
6188 int stored_new = Store[newx][newy];
6189 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
6190 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
6191 boolean last_line = (newy == lev_fieldy - 1);
6193 MovPos[x][y] += getElementMoveStepsize(x, y);
6195 if (pushed_by_player) /* special case: moving object pushed by player */
6196 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
6198 if (ABS(MovPos[x][y]) < TILEX)
6200 DrawLevelField(x, y);
6202 return; /* element is still moving */
6205 /* element reached destination field */
6207 Feld[x][y] = EL_EMPTY;
6208 Feld[newx][newy] = element;
6209 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
6211 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
6213 element = Feld[newx][newy] = EL_ACID;
6215 else if (element == EL_MOLE)
6217 Feld[x][y] = EL_SAND;
6219 DrawLevelFieldCrumbledSandNeighbours(x, y);
6221 else if (element == EL_QUICKSAND_FILLING)
6223 element = Feld[newx][newy] = get_next_element(element);
6224 Store[newx][newy] = Store[x][y];
6226 else if (element == EL_QUICKSAND_EMPTYING)
6228 Feld[x][y] = get_next_element(element);
6229 element = Feld[newx][newy] = Store[x][y];
6231 else if (element == EL_MAGIC_WALL_FILLING)
6233 element = Feld[newx][newy] = get_next_element(element);
6234 if (!game.magic_wall_active)
6235 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
6236 Store[newx][newy] = Store[x][y];
6238 else if (element == EL_MAGIC_WALL_EMPTYING)
6240 Feld[x][y] = get_next_element(element);
6241 if (!game.magic_wall_active)
6242 Feld[x][y] = EL_MAGIC_WALL_DEAD;
6243 element = Feld[newx][newy] = Store[x][y];
6245 #if USE_NEW_CUSTOM_VALUE
6246 InitField(newx, newy, FALSE);
6249 else if (element == EL_BD_MAGIC_WALL_FILLING)
6251 element = Feld[newx][newy] = get_next_element(element);
6252 if (!game.magic_wall_active)
6253 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
6254 Store[newx][newy] = Store[x][y];
6256 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
6258 Feld[x][y] = get_next_element(element);
6259 if (!game.magic_wall_active)
6260 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
6261 element = Feld[newx][newy] = Store[x][y];
6263 #if USE_NEW_CUSTOM_VALUE
6264 InitField(newx, newy, FALSE);
6267 else if (element == EL_AMOEBA_DROPPING)
6269 Feld[x][y] = get_next_element(element);
6270 element = Feld[newx][newy] = Store[x][y];
6272 else if (element == EL_SOKOBAN_OBJECT)
6275 Feld[x][y] = Back[x][y];
6277 if (Back[newx][newy])
6278 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
6280 Back[x][y] = Back[newx][newy] = 0;
6283 Store[x][y] = EL_EMPTY;
6288 MovDelay[newx][newy] = 0;
6291 if (CAN_CHANGE_OR_HAS_ACTION(element))
6293 if (CAN_CHANGE(element))
6296 /* copy element change control values to new field */
6297 ChangeDelay[newx][newy] = ChangeDelay[x][y];
6298 ChangePage[newx][newy] = ChangePage[x][y];
6299 ChangeCount[newx][newy] = ChangeCount[x][y];
6300 ChangeEvent[newx][newy] = ChangeEvent[x][y];
6303 #if USE_NEW_CUSTOM_VALUE
6304 CustomValue[newx][newy] = CustomValue[x][y];
6310 #if USE_NEW_CUSTOM_VALUE
6311 CustomValue[newx][newy] = CustomValue[x][y];
6315 ChangeDelay[x][y] = 0;
6316 ChangePage[x][y] = -1;
6317 ChangeCount[x][y] = 0;
6318 ChangeEvent[x][y] = -1;
6320 #if USE_NEW_CUSTOM_VALUE
6321 CustomValue[x][y] = 0;
6324 /* copy animation control values to new field */
6325 GfxFrame[newx][newy] = GfxFrame[x][y];
6326 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
6327 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
6328 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
6330 Pushed[x][y] = Pushed[newx][newy] = FALSE;
6332 /* some elements can leave other elements behind after moving */
6334 if (ei->move_leave_element != EL_EMPTY &&
6335 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
6336 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
6338 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
6339 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
6340 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
6343 int move_leave_element = ei->move_leave_element;
6347 /* this makes it possible to leave the removed element again */
6348 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
6349 move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
6351 /* this makes it possible to leave the removed element again */
6352 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
6353 move_leave_element = stored;
6356 /* this makes it possible to leave the removed element again */
6357 if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
6358 ei->move_leave_element == EL_TRIGGER_ELEMENT)
6359 move_leave_element = stored;
6362 Feld[x][y] = move_leave_element;
6364 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
6365 MovDir[x][y] = direction;
6367 InitField(x, y, FALSE);
6369 if (GFX_CRUMBLED(Feld[x][y]))
6370 DrawLevelFieldCrumbledSandNeighbours(x, y);
6372 if (ELEM_IS_PLAYER(move_leave_element))
6373 RelocatePlayer(x, y, move_leave_element);
6376 /* do this after checking for left-behind element */
6377 ResetGfxAnimation(x, y); /* reset animation values for old field */
6379 if (!CAN_MOVE(element) ||
6380 (CAN_FALL(element) && direction == MV_DOWN &&
6381 (element == EL_SPRING ||
6382 element_info[element].move_pattern == MV_WHEN_PUSHED ||
6383 element_info[element].move_pattern == MV_WHEN_DROPPED)))
6384 GfxDir[x][y] = MovDir[newx][newy] = 0;
6386 DrawLevelField(x, y);
6387 DrawLevelField(newx, newy);
6389 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
6391 /* prevent pushed element from moving on in pushed direction */
6392 if (pushed_by_player && CAN_MOVE(element) &&
6393 element_info[element].move_pattern & MV_ANY_DIRECTION &&
6394 !(element_info[element].move_pattern & direction))
6395 TurnRound(newx, newy);
6397 /* prevent elements on conveyor belt from moving on in last direction */
6398 if (pushed_by_conveyor && CAN_FALL(element) &&
6399 direction & MV_HORIZONTAL)
6400 MovDir[newx][newy] = 0;
6402 if (!pushed_by_player)
6404 int nextx = newx + dx, nexty = newy + dy;
6405 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
6407 WasJustMoving[newx][newy] = 3;
6409 if (CAN_FALL(element) && direction == MV_DOWN)
6410 WasJustFalling[newx][newy] = 3;
6412 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
6413 CheckCollision[newx][newy] = 2;
6416 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
6418 TestIfBadThingTouchesPlayer(newx, newy);
6419 TestIfBadThingTouchesFriend(newx, newy);
6421 if (!IS_CUSTOM_ELEMENT(element))
6422 TestIfBadThingTouchesOtherBadThing(newx, newy);
6424 else if (element == EL_PENGUIN)
6425 TestIfFriendTouchesBadThing(newx, newy);
6427 /* give the player one last chance (one more frame) to move away */
6428 if (CAN_FALL(element) && direction == MV_DOWN &&
6429 (last_line || (!IS_FREE(x, newy + 1) &&
6430 (!IS_PLAYER(x, newy + 1) ||
6431 game.engine_version < VERSION_IDENT(3,1,1,0)))))
6434 if (pushed_by_player && !game.use_change_when_pushing_bug)
6436 int push_side = MV_DIR_OPPOSITE(direction);
6437 struct PlayerInfo *player = PLAYERINFO(x, y);
6439 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
6440 player->index_bit, push_side);
6441 CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
6442 player->index_bit, push_side);
6445 if (element == EL_EMC_ANDROID && pushed_by_player) /* make another move */
6446 MovDelay[newx][newy] = 1;
6448 CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
6450 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
6452 TestIfElementHitsCustomElement(newx, newy, direction);
6453 TestIfPlayerTouchesCustomElement(newx, newy);
6454 TestIfElementTouchesCustomElement(newx, newy);
6457 if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
6458 IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
6459 CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
6460 MV_DIR_OPPOSITE(direction));
6464 int AmoebeNachbarNr(int ax, int ay)
6467 int element = Feld[ax][ay];
6469 static int xy[4][2] =
6477 for (i = 0; i < NUM_DIRECTIONS; i++)
6479 int x = ax + xy[i][0];
6480 int y = ay + xy[i][1];
6482 if (!IN_LEV_FIELD(x, y))
6485 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
6486 group_nr = AmoebaNr[x][y];
6492 void AmoebenVereinigen(int ax, int ay)
6494 int i, x, y, xx, yy;
6495 int new_group_nr = AmoebaNr[ax][ay];
6496 static int xy[4][2] =
6504 if (new_group_nr == 0)
6507 for (i = 0; i < NUM_DIRECTIONS; i++)
6512 if (!IN_LEV_FIELD(x, y))
6515 if ((Feld[x][y] == EL_AMOEBA_FULL ||
6516 Feld[x][y] == EL_BD_AMOEBA ||
6517 Feld[x][y] == EL_AMOEBA_DEAD) &&
6518 AmoebaNr[x][y] != new_group_nr)
6520 int old_group_nr = AmoebaNr[x][y];
6522 if (old_group_nr == 0)
6525 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
6526 AmoebaCnt[old_group_nr] = 0;
6527 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
6528 AmoebaCnt2[old_group_nr] = 0;
6531 SCAN_PLAYFIELD(xx, yy)
6533 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
6536 if (AmoebaNr[xx][yy] == old_group_nr)
6537 AmoebaNr[xx][yy] = new_group_nr;
6543 void AmoebeUmwandeln(int ax, int ay)
6547 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
6549 int group_nr = AmoebaNr[ax][ay];
6554 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
6555 printf("AmoebeUmwandeln(): This should never happen!\n");
6561 SCAN_PLAYFIELD(x, y)
6563 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
6566 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
6569 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
6573 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
6574 SND_AMOEBA_TURNING_TO_GEM :
6575 SND_AMOEBA_TURNING_TO_ROCK));
6580 static int xy[4][2] =
6588 for (i = 0; i < NUM_DIRECTIONS; i++)
6593 if (!IN_LEV_FIELD(x, y))
6596 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
6598 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
6599 SND_AMOEBA_TURNING_TO_GEM :
6600 SND_AMOEBA_TURNING_TO_ROCK));
6607 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
6610 int group_nr = AmoebaNr[ax][ay];
6611 boolean done = FALSE;
6616 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
6617 printf("AmoebeUmwandelnBD(): This should never happen!\n");
6623 SCAN_PLAYFIELD(x, y)
6625 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
6628 if (AmoebaNr[x][y] == group_nr &&
6629 (Feld[x][y] == EL_AMOEBA_DEAD ||
6630 Feld[x][y] == EL_BD_AMOEBA ||
6631 Feld[x][y] == EL_AMOEBA_GROWING))
6634 Feld[x][y] = new_element;
6635 InitField(x, y, FALSE);
6636 DrawLevelField(x, y);
6642 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
6643 SND_BD_AMOEBA_TURNING_TO_ROCK :
6644 SND_BD_AMOEBA_TURNING_TO_GEM));
6647 void AmoebeWaechst(int x, int y)
6649 static unsigned long sound_delay = 0;
6650 static unsigned long sound_delay_value = 0;
6652 if (!MovDelay[x][y]) /* start new growing cycle */
6656 if (DelayReached(&sound_delay, sound_delay_value))
6658 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
6659 sound_delay_value = 30;
6663 if (MovDelay[x][y]) /* wait some time before growing bigger */
6666 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6668 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
6669 6 - MovDelay[x][y]);
6671 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
6674 if (!MovDelay[x][y])
6676 Feld[x][y] = Store[x][y];
6678 DrawLevelField(x, y);
6683 void AmoebaDisappearing(int x, int y)
6685 static unsigned long sound_delay = 0;
6686 static unsigned long sound_delay_value = 0;
6688 if (!MovDelay[x][y]) /* start new shrinking cycle */
6692 if (DelayReached(&sound_delay, sound_delay_value))
6693 sound_delay_value = 30;
6696 if (MovDelay[x][y]) /* wait some time before shrinking */
6699 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6701 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
6702 6 - MovDelay[x][y]);
6704 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
6707 if (!MovDelay[x][y])
6709 Feld[x][y] = EL_EMPTY;
6710 DrawLevelField(x, y);
6712 /* don't let mole enter this field in this cycle;
6713 (give priority to objects falling to this field from above) */
6719 void AmoebeAbleger(int ax, int ay)
6722 int element = Feld[ax][ay];
6723 int graphic = el2img(element);
6724 int newax = ax, neway = ay;
6725 boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
6726 static int xy[4][2] =
6734 if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
6736 Feld[ax][ay] = EL_AMOEBA_DEAD;
6737 DrawLevelField(ax, ay);
6741 if (IS_ANIMATED(graphic))
6742 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6744 if (!MovDelay[ax][ay]) /* start making new amoeba field */
6745 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
6747 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
6750 if (MovDelay[ax][ay])
6754 if (can_drop) /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
6757 int x = ax + xy[start][0];
6758 int y = ay + xy[start][1];
6760 if (!IN_LEV_FIELD(x, y))
6763 if (IS_FREE(x, y) ||
6764 CAN_GROW_INTO(Feld[x][y]) ||
6765 Feld[x][y] == EL_QUICKSAND_EMPTY)
6771 if (newax == ax && neway == ay)
6774 else /* normal or "filled" (BD style) amoeba */
6777 boolean waiting_for_player = FALSE;
6779 for (i = 0; i < NUM_DIRECTIONS; i++)
6781 int j = (start + i) % 4;
6782 int x = ax + xy[j][0];
6783 int y = ay + xy[j][1];
6785 if (!IN_LEV_FIELD(x, y))
6788 if (IS_FREE(x, y) ||
6789 CAN_GROW_INTO(Feld[x][y]) ||
6790 Feld[x][y] == EL_QUICKSAND_EMPTY)
6796 else if (IS_PLAYER(x, y))
6797 waiting_for_player = TRUE;
6800 if (newax == ax && neway == ay) /* amoeba cannot grow */
6802 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
6804 Feld[ax][ay] = EL_AMOEBA_DEAD;
6805 DrawLevelField(ax, ay);
6806 AmoebaCnt[AmoebaNr[ax][ay]]--;
6808 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
6810 if (element == EL_AMOEBA_FULL)
6811 AmoebeUmwandeln(ax, ay);
6812 else if (element == EL_BD_AMOEBA)
6813 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
6818 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
6820 /* amoeba gets larger by growing in some direction */
6822 int new_group_nr = AmoebaNr[ax][ay];
6825 if (new_group_nr == 0)
6827 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
6828 printf("AmoebeAbleger(): This should never happen!\n");
6833 AmoebaNr[newax][neway] = new_group_nr;
6834 AmoebaCnt[new_group_nr]++;
6835 AmoebaCnt2[new_group_nr]++;
6837 /* if amoeba touches other amoeba(s) after growing, unify them */
6838 AmoebenVereinigen(newax, neway);
6840 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
6842 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
6848 if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
6849 (neway == lev_fieldy - 1 && newax != ax))
6851 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
6852 Store[newax][neway] = element;
6854 else if (neway == ay || element == EL_EMC_DRIPPER)
6856 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
6858 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
6862 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
6863 Feld[ax][ay] = EL_AMOEBA_DROPPING;
6864 Store[ax][ay] = EL_AMOEBA_DROP;
6865 ContinueMoving(ax, ay);
6869 DrawLevelField(newax, neway);
6872 void Life(int ax, int ay)
6876 static int life[4] = { 2, 3, 3, 3 }; /* parameters for "game of life" */
6879 int element = Feld[ax][ay];
6880 int graphic = el2img(element);
6881 int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
6883 boolean changed = FALSE;
6885 if (IS_ANIMATED(graphic))
6886 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6891 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
6892 MovDelay[ax][ay] = life_time;
6894 if (MovDelay[ax][ay]) /* wait some time before next cycle */
6897 if (MovDelay[ax][ay])
6901 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
6903 int xx = ax+x1, yy = ay+y1;
6906 if (!IN_LEV_FIELD(xx, yy))
6909 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
6911 int x = xx+x2, y = yy+y2;
6913 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
6916 if (((Feld[x][y] == element ||
6917 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
6919 (IS_FREE(x, y) && Stop[x][y]))
6923 if (xx == ax && yy == ay) /* field in the middle */
6925 if (nachbarn < life_parameter[0] ||
6926 nachbarn > life_parameter[1])
6928 Feld[xx][yy] = EL_EMPTY;
6930 DrawLevelField(xx, yy);
6931 Stop[xx][yy] = TRUE;
6935 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
6936 { /* free border field */
6937 if (nachbarn >= life_parameter[2] &&
6938 nachbarn <= life_parameter[3])
6940 Feld[xx][yy] = element;
6941 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
6943 DrawLevelField(xx, yy);
6944 Stop[xx][yy] = TRUE;
6951 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
6952 SND_GAME_OF_LIFE_GROWING);
6955 static void InitRobotWheel(int x, int y)
6957 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
6960 static void RunRobotWheel(int x, int y)
6962 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
6965 static void StopRobotWheel(int x, int y)
6967 if (ZX == x && ZY == y)
6971 static void InitTimegateWheel(int x, int y)
6973 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
6976 static void RunTimegateWheel(int x, int y)
6978 PlayLevelSound(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
6981 static void InitMagicBallDelay(int x, int y)
6984 ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
6986 ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
6990 static void ActivateMagicBall(int bx, int by)
6994 if (level.ball_random)
6996 int pos_border = RND(8); /* select one of the eight border elements */
6997 int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
6998 int xx = pos_content % 3;
6999 int yy = pos_content / 3;
7004 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
7005 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
7009 for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
7011 int xx = x - bx + 1;
7012 int yy = y - by + 1;
7014 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
7015 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
7019 game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
7022 static void InitDiagonalMovingElement(int x, int y)
7025 MovDelay[x][y] = level.android_move_time;
7029 void CheckExit(int x, int y)
7031 if (local_player->gems_still_needed > 0 ||
7032 local_player->sokobanfields_still_needed > 0 ||
7033 local_player->lights_still_needed > 0)
7035 int element = Feld[x][y];
7036 int graphic = el2img(element);
7038 if (IS_ANIMATED(graphic))
7039 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7044 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7047 Feld[x][y] = EL_EXIT_OPENING;
7049 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
7052 void CheckExitSP(int x, int y)
7054 if (local_player->gems_still_needed > 0)
7056 int element = Feld[x][y];
7057 int graphic = el2img(element);
7059 if (IS_ANIMATED(graphic))
7060 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7065 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7068 Feld[x][y] = EL_SP_EXIT_OPENING;
7070 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
7073 static void CloseAllOpenTimegates()
7078 SCAN_PLAYFIELD(x, y)
7080 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7083 int element = Feld[x][y];
7085 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
7087 Feld[x][y] = EL_TIMEGATE_CLOSING;
7089 PlayLevelSoundAction(x, y, ACTION_CLOSING);
7094 void EdelsteinFunkeln(int x, int y)
7096 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
7099 if (Feld[x][y] == EL_BD_DIAMOND)
7102 if (MovDelay[x][y] == 0) /* next animation frame */
7103 MovDelay[x][y] = 11 * !SimpleRND(500);
7105 if (MovDelay[x][y] != 0) /* wait some time before next frame */
7109 if (setup.direct_draw && MovDelay[x][y])
7110 SetDrawtoField(DRAW_BUFFERED);
7112 DrawLevelElementAnimation(x, y, Feld[x][y]);
7114 if (MovDelay[x][y] != 0)
7116 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
7117 10 - MovDelay[x][y]);
7119 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
7121 if (setup.direct_draw)
7125 dest_x = FX + SCREENX(x) * TILEX;
7126 dest_y = FY + SCREENY(y) * TILEY;
7128 BlitBitmap(drawto_field, window,
7129 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
7130 SetDrawtoField(DRAW_DIRECT);
7136 void MauerWaechst(int x, int y)
7140 if (!MovDelay[x][y]) /* next animation frame */
7141 MovDelay[x][y] = 3 * delay;
7143 if (MovDelay[x][y]) /* wait some time before next frame */
7147 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7149 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
7150 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
7152 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
7155 if (!MovDelay[x][y])
7157 if (MovDir[x][y] == MV_LEFT)
7159 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
7160 DrawLevelField(x - 1, y);
7162 else if (MovDir[x][y] == MV_RIGHT)
7164 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
7165 DrawLevelField(x + 1, y);
7167 else if (MovDir[x][y] == MV_UP)
7169 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
7170 DrawLevelField(x, y - 1);
7174 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
7175 DrawLevelField(x, y + 1);
7178 Feld[x][y] = Store[x][y];
7180 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7181 DrawLevelField(x, y);
7186 void MauerAbleger(int ax, int ay)
7188 int element = Feld[ax][ay];
7189 int graphic = el2img(element);
7190 boolean oben_frei = FALSE, unten_frei = FALSE;
7191 boolean links_frei = FALSE, rechts_frei = FALSE;
7192 boolean oben_massiv = FALSE, unten_massiv = FALSE;
7193 boolean links_massiv = FALSE, rechts_massiv = FALSE;
7194 boolean new_wall = FALSE;
7196 if (IS_ANIMATED(graphic))
7197 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7199 if (!MovDelay[ax][ay]) /* start building new wall */
7200 MovDelay[ax][ay] = 6;
7202 if (MovDelay[ax][ay]) /* wait some time before building new wall */
7205 if (MovDelay[ax][ay])
7209 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
7211 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
7213 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
7215 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
7218 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
7219 element == EL_EXPANDABLE_WALL_ANY)
7223 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
7224 Store[ax][ay-1] = element;
7225 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
7226 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
7227 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
7228 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
7233 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
7234 Store[ax][ay+1] = element;
7235 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
7236 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
7237 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
7238 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
7243 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7244 element == EL_EXPANDABLE_WALL_ANY ||
7245 element == EL_EXPANDABLE_WALL)
7249 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
7250 Store[ax-1][ay] = element;
7251 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
7252 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
7253 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
7254 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
7260 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
7261 Store[ax+1][ay] = element;
7262 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
7263 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
7264 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
7265 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
7270 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
7271 DrawLevelField(ax, ay);
7273 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
7275 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
7276 unten_massiv = TRUE;
7277 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
7278 links_massiv = TRUE;
7279 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
7280 rechts_massiv = TRUE;
7282 if (((oben_massiv && unten_massiv) ||
7283 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7284 element == EL_EXPANDABLE_WALL) &&
7285 ((links_massiv && rechts_massiv) ||
7286 element == EL_EXPANDABLE_WALL_VERTICAL))
7287 Feld[ax][ay] = EL_WALL;
7290 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
7293 void CheckForDragon(int x, int y)
7296 boolean dragon_found = FALSE;
7297 static int xy[4][2] =
7305 for (i = 0; i < NUM_DIRECTIONS; i++)
7307 for (j = 0; j < 4; j++)
7309 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7311 if (IN_LEV_FIELD(xx, yy) &&
7312 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
7314 if (Feld[xx][yy] == EL_DRAGON)
7315 dragon_found = TRUE;
7324 for (i = 0; i < NUM_DIRECTIONS; i++)
7326 for (j = 0; j < 3; j++)
7328 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7330 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
7332 Feld[xx][yy] = EL_EMPTY;
7333 DrawLevelField(xx, yy);
7342 static void InitBuggyBase(int x, int y)
7344 int element = Feld[x][y];
7345 int activating_delay = FRAMES_PER_SECOND / 4;
7348 (element == EL_SP_BUGGY_BASE ?
7349 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
7350 element == EL_SP_BUGGY_BASE_ACTIVATING ?
7352 element == EL_SP_BUGGY_BASE_ACTIVE ?
7353 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
7356 static void WarnBuggyBase(int x, int y)
7359 static int xy[4][2] =
7367 for (i = 0; i < NUM_DIRECTIONS; i++)
7369 int xx = x + xy[i][0], yy = y + xy[i][1];
7371 if (IS_PLAYER(xx, yy))
7373 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
7380 static void InitTrap(int x, int y)
7382 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
7385 static void ActivateTrap(int x, int y)
7387 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
7390 static void ChangeActiveTrap(int x, int y)
7392 int graphic = IMG_TRAP_ACTIVE;
7394 /* if new animation frame was drawn, correct crumbled sand border */
7395 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
7396 DrawLevelFieldCrumbledSand(x, y);
7399 static int getSpecialActionElement(int element, int number, int base_element)
7401 return (element != EL_EMPTY ? element :
7402 number != -1 ? base_element + number - 1 :
7406 static int getModifiedActionNumber(int value_old, int operator, int operand,
7407 int value_min, int value_max)
7409 int value_new = (operator == CA_MODE_SET ? operand :
7410 operator == CA_MODE_ADD ? value_old + operand :
7411 operator == CA_MODE_SUBTRACT ? value_old - operand :
7412 operator == CA_MODE_MULTIPLY ? value_old * operand :
7413 operator == CA_MODE_DIVIDE ? value_old / MAX(1, operand) :
7414 operator == CA_MODE_MODULO ? value_old % MAX(1, operand) :
7417 return (value_new < value_min ? value_min :
7418 value_new > value_max ? value_max :
7422 static void ExecuteCustomElementAction(int x, int y, int element, int page)
7424 struct ElementInfo *ei = &element_info[element];
7425 struct ElementChangeInfo *change = &ei->change_page[page];
7426 int action_type = change->action_type;
7427 int action_mode = change->action_mode;
7428 int action_arg = change->action_arg;
7431 if (!change->has_action)
7434 /* ---------- determine action paramater values -------------------------- */
7436 int level_time_value =
7437 (level.time > 0 ? TimeLeft :
7440 int action_arg_element =
7441 (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player :
7442 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
7443 action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element :
7446 int action_arg_direction =
7447 (action_arg >= CA_ARG_DIRECTION_LEFT &&
7448 action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
7449 action_arg == CA_ARG_DIRECTION_TRIGGER ?
7450 change->actual_trigger_side :
7451 action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
7452 MV_DIR_OPPOSITE(change->actual_trigger_side) :
7455 int action_arg_number_min =
7456 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
7459 int action_arg_number_max =
7460 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
7461 action_type == CA_SET_LEVEL_GEMS ? 999 :
7462 action_type == CA_SET_LEVEL_TIME ? 9999 :
7463 action_type == CA_SET_LEVEL_SCORE ? 99999 :
7464 action_type == CA_SET_CE_SCORE ? 9999 :
7465 action_type == CA_SET_CE_VALUE ? 9999 :
7468 int action_arg_number_reset =
7469 (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize :
7470 action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
7471 action_type == CA_SET_LEVEL_TIME ? level.time :
7472 action_type == CA_SET_LEVEL_SCORE ? 0 :
7473 action_type == CA_SET_CE_SCORE ? 0 :
7475 action_type == CA_SET_CE_VALUE ? GET_NEW_CUSTOM_VALUE(element) :
7477 action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
7481 int action_arg_number =
7482 (action_arg <= CA_ARG_MAX ? action_arg :
7483 action_arg >= CA_ARG_SPEED_NOT_MOVING &&
7484 action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
7485 action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
7486 action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
7487 action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
7488 action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
7489 action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
7490 #if USE_NEW_CUSTOM_VALUE
7491 action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
7493 action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
7495 action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
7496 action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
7497 action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
7498 action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
7499 action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CUSTOM_VALUE(change->target_element) :
7500 action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
7501 action_arg == CA_ARG_ELEMENT_NR_TARGET ? change->target_element :
7502 action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
7505 int action_arg_number_old =
7506 (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
7507 action_type == CA_SET_LEVEL_TIME ? TimeLeft :
7508 action_type == CA_SET_LEVEL_SCORE ? local_player->score :
7509 action_type == CA_SET_CE_SCORE ? ei->collect_score :
7510 action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
7513 int action_arg_number_new =
7514 getModifiedActionNumber(action_arg_number_old,
7515 action_mode, action_arg_number,
7516 action_arg_number_min, action_arg_number_max);
7518 int trigger_player_bits =
7519 (change->actual_trigger_player >= EL_PLAYER_1 &&
7520 change->actual_trigger_player <= EL_PLAYER_4 ?
7521 (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
7524 int action_arg_player_bits =
7525 (action_arg >= CA_ARG_PLAYER_1 &&
7526 action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
7527 action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
7530 /* ---------- execute action -------------------------------------------- */
7539 /* ---------- level actions ------------------------------------------- */
7541 case CA_RESTART_LEVEL:
7543 game.restart_level = TRUE;
7548 case CA_SHOW_ENVELOPE:
7550 int element = getSpecialActionElement(action_arg_element,
7551 action_arg_number, EL_ENVELOPE_1);
7553 if (IS_ENVELOPE(element))
7554 local_player->show_envelope = element;
7559 case CA_SET_LEVEL_TIME:
7561 if (level.time > 0) /* only modify limited time value */
7563 TimeLeft = action_arg_number_new;
7565 DrawGameValue_Time(TimeLeft);
7567 if (!TimeLeft && setup.time_limit)
7568 for (i = 0; i < MAX_PLAYERS; i++)
7569 KillPlayer(&stored_player[i]);
7575 case CA_SET_LEVEL_SCORE:
7577 local_player->score = action_arg_number_new;
7579 DrawGameValue_Score(local_player->score);
7584 case CA_SET_LEVEL_GEMS:
7586 local_player->gems_still_needed = action_arg_number_new;
7588 DrawGameValue_Emeralds(local_player->gems_still_needed);
7593 case CA_SET_LEVEL_GRAVITY:
7595 game.gravity = (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
7596 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
7597 action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
7602 case CA_SET_LEVEL_WIND:
7604 game.wind_direction = action_arg_direction;
7609 /* ---------- player actions ------------------------------------------ */
7611 case CA_MOVE_PLAYER:
7613 /* automatically move to the next field in specified direction */
7614 for (i = 0; i < MAX_PLAYERS; i++)
7615 if (trigger_player_bits & (1 << i))
7616 stored_player[i].programmed_action = action_arg_direction;
7621 case CA_EXIT_PLAYER:
7623 for (i = 0; i < MAX_PLAYERS; i++)
7624 if (action_arg_player_bits & (1 << i))
7625 stored_player[i].LevelSolved = stored_player[i].GameOver = TRUE;
7630 case CA_KILL_PLAYER:
7632 for (i = 0; i < MAX_PLAYERS; i++)
7633 if (action_arg_player_bits & (1 << i))
7634 KillPlayer(&stored_player[i]);
7639 case CA_SET_PLAYER_KEYS:
7641 int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
7642 int element = getSpecialActionElement(action_arg_element,
7643 action_arg_number, EL_KEY_1);
7645 if (IS_KEY(element))
7647 for (i = 0; i < MAX_PLAYERS; i++)
7649 if (trigger_player_bits & (1 << i))
7651 stored_player[i].key[KEY_NR(element)] = key_state;
7653 DrawGameValue_Keys(stored_player[i].key);
7655 redraw_mask |= REDRAW_DOOR_1;
7663 case CA_SET_PLAYER_SPEED:
7665 for (i = 0; i < MAX_PLAYERS; i++)
7667 if (trigger_player_bits & (1 << i))
7669 int move_stepsize = TILEX / stored_player[i].move_delay_value;
7671 if (action_arg == CA_ARG_SPEED_FASTER &&
7672 stored_player[i].cannot_move)
7674 action_arg_number = STEPSIZE_VERY_SLOW;
7676 else if (action_arg == CA_ARG_SPEED_SLOWER ||
7677 action_arg == CA_ARG_SPEED_FASTER)
7679 action_arg_number = 2;
7680 action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
7685 getModifiedActionNumber(move_stepsize,
7688 action_arg_number_min,
7689 action_arg_number_max);
7692 SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
7694 /* make sure that value is power of 2 */
7695 move_stepsize = (1 << log_2(move_stepsize));
7697 /* do no immediately change -- the player might just be moving */
7698 stored_player[i].move_delay_value_next = TILEX / move_stepsize;
7700 stored_player[i].cannot_move =
7701 (action_arg == CA_ARG_SPEED_NOT_MOVING ? TRUE : FALSE);
7709 case CA_SET_PLAYER_SHIELD:
7711 for (i = 0; i < MAX_PLAYERS; i++)
7713 if (trigger_player_bits & (1 << i))
7715 if (action_arg == CA_ARG_SHIELD_OFF)
7717 stored_player[i].shield_normal_time_left = 0;
7718 stored_player[i].shield_deadly_time_left = 0;
7720 else if (action_arg == CA_ARG_SHIELD_NORMAL)
7722 stored_player[i].shield_normal_time_left = 999999;
7724 else if (action_arg == CA_ARG_SHIELD_DEADLY)
7726 stored_player[i].shield_normal_time_left = 999999;
7727 stored_player[i].shield_deadly_time_left = 999999;
7735 case CA_SET_PLAYER_ARTWORK:
7737 for (i = 0; i < MAX_PLAYERS; i++)
7739 if (trigger_player_bits & (1 << i))
7741 int artwork_element = action_arg_element;
7743 if (action_arg == CA_ARG_ELEMENT_RESET)
7745 (level.use_artwork_element[i] ? level.artwork_element[i] :
7746 stored_player[i].element_nr);
7748 stored_player[i].artwork_element = artwork_element;
7750 SetPlayerWaiting(&stored_player[i], FALSE);
7752 /* set number of special actions for bored and sleeping animation */
7753 stored_player[i].num_special_action_bored =
7754 get_num_special_action(artwork_element,
7755 ACTION_BORING_1, ACTION_BORING_LAST);
7756 stored_player[i].num_special_action_sleeping =
7757 get_num_special_action(artwork_element,
7758 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
7765 /* ---------- CE actions ---------------------------------------------- */
7767 case CA_SET_CE_SCORE:
7769 ei->collect_score = action_arg_number_new;
7774 case CA_SET_CE_VALUE:
7776 #if USE_NEW_CUSTOM_VALUE
7777 int last_custom_value = CustomValue[x][y];
7779 CustomValue[x][y] = action_arg_number_new;
7782 printf("::: Count == %d\n", CustomValue[x][y]);
7785 if (CustomValue[x][y] == 0 && last_custom_value > 0)
7788 printf("::: CE_VALUE_GETS_ZERO\n");
7791 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
7792 CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
7799 /* ---------- engine actions ------------------------------------------ */
7801 case CA_SET_ENGINE_SCAN_MODE:
7803 InitPlayfieldScanMode(action_arg);
7813 static void CreateFieldExt(int x, int y, int element, boolean is_change)
7815 int previous_move_direction = MovDir[x][y];
7816 #if USE_NEW_CUSTOM_VALUE
7817 int last_ce_value = CustomValue[x][y];
7819 boolean add_player = (ELEM_IS_PLAYER(element) &&
7820 IS_WALKABLE(Feld[x][y]));
7822 /* check if element under player changes from accessible to unaccessible
7823 (needed for special case of dropping element which then changes) */
7824 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
7825 IS_ACCESSIBLE(Feld[x][y]) && !IS_ACCESSIBLE(element))
7834 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
7835 RemoveMovingField(x, y);
7839 Feld[x][y] = element;
7841 ResetGfxAnimation(x, y);
7842 ResetRandomAnimationValue(x, y);
7844 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
7845 MovDir[x][y] = previous_move_direction;
7847 #if USE_NEW_CUSTOM_VALUE
7848 if (element_info[Feld[x][y]].use_last_ce_value)
7849 CustomValue[x][y] = last_ce_value;
7852 InitField_WithBug1(x, y, FALSE);
7854 DrawLevelField(x, y);
7856 if (GFX_CRUMBLED(Feld[x][y]))
7857 DrawLevelFieldCrumbledSandNeighbours(x, y);
7860 /* "ChangeCount" not set yet to allow "entered by player" change one time */
7861 if (ELEM_IS_PLAYER(element))
7862 RelocatePlayer(x, y, element);
7865 ChangeCount[x][y]++; /* count number of changes in the same frame */
7867 TestIfBadThingTouchesPlayer(x, y);
7868 TestIfPlayerTouchesCustomElement(x, y);
7869 TestIfElementTouchesCustomElement(x, y);
7872 static void CreateField(int x, int y, int element)
7874 CreateFieldExt(x, y, element, FALSE);
7877 static void CreateElementFromChange(int x, int y, int element)
7879 element = GET_VALID_RUNTIME_ELEMENT(element);
7881 #if USE_STOP_CHANGED_ELEMENTS
7882 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
7884 int old_element = Feld[x][y];
7886 /* prevent changed element from moving in same engine frame
7887 unless both old and new element can either fall or move */
7888 if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
7889 (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
7894 CreateFieldExt(x, y, element, TRUE);
7897 static boolean ChangeElement(int x, int y, int element, int page)
7899 struct ElementChangeInfo *change = &element_info[element].change_page[page];
7901 int old_element = Feld[x][y];
7903 /* always use default change event to prevent running into a loop */
7904 if (ChangeEvent[x][y] == -1)
7905 ChangeEvent[x][y] = CE_DELAY;
7907 if (ChangeEvent[x][y] == CE_DELAY)
7909 /* reset actual trigger element, trigger player and action element */
7910 change->actual_trigger_element = EL_EMPTY;
7911 change->actual_trigger_player = EL_PLAYER_1;
7912 change->actual_trigger_side = CH_SIDE_NONE;
7913 change->actual_trigger_ce_value = 0;
7916 /* do not change elements more than a specified maximum number of changes */
7917 if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
7920 ChangeCount[x][y]++; /* count number of changes in the same frame */
7922 if (change->explode)
7929 if (change->use_target_content)
7931 boolean complete_replace = TRUE;
7932 boolean can_replace[3][3];
7935 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
7938 boolean is_walkable;
7939 boolean is_diggable;
7940 boolean is_collectible;
7941 boolean is_removable;
7942 boolean is_destructible;
7943 int ex = x + xx - 1;
7944 int ey = y + yy - 1;
7945 int content_element = change->target_content.e[xx][yy];
7948 can_replace[xx][yy] = TRUE;
7950 if (ex == x && ey == y) /* do not check changing element itself */
7953 if (content_element == EL_EMPTY_SPACE)
7955 can_replace[xx][yy] = FALSE; /* do not replace border with space */
7960 if (!IN_LEV_FIELD(ex, ey))
7962 can_replace[xx][yy] = FALSE;
7963 complete_replace = FALSE;
7970 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
7971 e = MovingOrBlocked2Element(ex, ey);
7973 is_empty = (IS_FREE(ex, ey) ||
7974 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
7976 is_walkable = (is_empty || IS_WALKABLE(e));
7977 is_diggable = (is_empty || IS_DIGGABLE(e));
7978 is_collectible = (is_empty || IS_COLLECTIBLE(e));
7979 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
7980 is_removable = (is_diggable || is_collectible);
7982 can_replace[xx][yy] =
7983 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
7984 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
7985 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
7986 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
7987 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
7988 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
7989 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
7991 if (!can_replace[xx][yy])
7992 complete_replace = FALSE;
7995 if (!change->only_if_complete || complete_replace)
7997 boolean something_has_changed = FALSE;
7999 if (change->only_if_complete && change->use_random_replace &&
8000 RND(100) < change->random_percentage)
8003 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
8005 int ex = x + xx - 1;
8006 int ey = y + yy - 1;
8007 int content_element;
8009 if (can_replace[xx][yy] && (!change->use_random_replace ||
8010 RND(100) < change->random_percentage))
8012 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
8013 RemoveMovingField(ex, ey);
8015 ChangeEvent[ex][ey] = ChangeEvent[x][y];
8017 content_element = change->target_content.e[xx][yy];
8018 target_element = GET_TARGET_ELEMENT(content_element, change);
8020 CreateElementFromChange(ex, ey, target_element);
8022 something_has_changed = TRUE;
8024 /* for symmetry reasons, freeze newly created border elements */
8025 if (ex != x || ey != y)
8026 Stop[ex][ey] = TRUE; /* no more moving in this frame */
8030 if (something_has_changed)
8032 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
8033 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
8039 target_element = GET_TARGET_ELEMENT(change->target_element, change);
8041 if (element == EL_DIAGONAL_GROWING ||
8042 element == EL_DIAGONAL_SHRINKING)
8044 target_element = Store[x][y];
8046 Store[x][y] = EL_EMPTY;
8049 CreateElementFromChange(x, y, target_element);
8051 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
8052 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
8055 /* this uses direct change before indirect change */
8056 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
8061 #if USE_NEW_DELAYED_ACTION
8063 static void HandleElementChange(int x, int y, int page)
8065 int element = MovingOrBlocked2Element(x, y);
8066 struct ElementInfo *ei = &element_info[element];
8067 struct ElementChangeInfo *change = &ei->change_page[page];
8070 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
8071 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
8074 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
8075 x, y, element, element_info[element].token_name);
8076 printf("HandleElementChange(): This should never happen!\n");
8081 /* this can happen with classic bombs on walkable, changing elements */
8082 if (!CAN_CHANGE_OR_HAS_ACTION(element))
8085 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
8086 ChangeDelay[x][y] = 0;
8092 if (ChangeDelay[x][y] == 0) /* initialize element change */
8094 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
8096 if (change->can_change)
8098 ResetGfxAnimation(x, y);
8099 ResetRandomAnimationValue(x, y);
8101 if (change->pre_change_function)
8102 change->pre_change_function(x, y);
8106 ChangeDelay[x][y]--;
8108 if (ChangeDelay[x][y] != 0) /* continue element change */
8110 if (change->can_change)
8112 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8114 if (IS_ANIMATED(graphic))
8115 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8117 if (change->change_function)
8118 change->change_function(x, y);
8121 else /* finish element change */
8123 if (ChangePage[x][y] != -1) /* remember page from delayed change */
8125 page = ChangePage[x][y];
8126 ChangePage[x][y] = -1;
8128 change = &ei->change_page[page];
8131 if (IS_MOVING(x, y)) /* never change a running system ;-) */
8133 ChangeDelay[x][y] = 1; /* try change after next move step */
8134 ChangePage[x][y] = page; /* remember page to use for change */
8139 if (change->can_change)
8141 if (ChangeElement(x, y, element, page))
8143 if (change->post_change_function)
8144 change->post_change_function(x, y);
8148 if (change->has_action)
8149 ExecuteCustomElementAction(x, y, element, page);
8155 static void HandleElementChange(int x, int y, int page)
8157 int element = MovingOrBlocked2Element(x, y);
8158 struct ElementInfo *ei = &element_info[element];
8159 struct ElementChangeInfo *change = &ei->change_page[page];
8162 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
8165 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
8166 x, y, element, element_info[element].token_name);
8167 printf("HandleElementChange(): This should never happen!\n");
8172 /* this can happen with classic bombs on walkable, changing elements */
8173 if (!CAN_CHANGE(element))
8176 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
8177 ChangeDelay[x][y] = 0;
8183 if (ChangeDelay[x][y] == 0) /* initialize element change */
8185 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
8187 ResetGfxAnimation(x, y);
8188 ResetRandomAnimationValue(x, y);
8190 if (change->pre_change_function)
8191 change->pre_change_function(x, y);
8194 ChangeDelay[x][y]--;
8196 if (ChangeDelay[x][y] != 0) /* continue element change */
8198 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8200 if (IS_ANIMATED(graphic))
8201 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8203 if (change->change_function)
8204 change->change_function(x, y);
8206 else /* finish element change */
8208 if (ChangePage[x][y] != -1) /* remember page from delayed change */
8210 page = ChangePage[x][y];
8211 ChangePage[x][y] = -1;
8213 change = &ei->change_page[page];
8216 if (IS_MOVING(x, y)) /* never change a running system ;-) */
8218 ChangeDelay[x][y] = 1; /* try change after next move step */
8219 ChangePage[x][y] = page; /* remember page to use for change */
8224 if (ChangeElement(x, y, element, page))
8226 if (change->post_change_function)
8227 change->post_change_function(x, y);
8234 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
8235 int trigger_element,
8241 boolean change_done_any = FALSE;
8242 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
8245 if (!(trigger_events[trigger_element][trigger_event]))
8248 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8250 int element = EL_CUSTOM_START + i;
8251 boolean change_done = FALSE;
8254 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
8255 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
8258 for (p = 0; p < element_info[element].num_change_pages; p++)
8260 struct ElementChangeInfo *change = &element_info[element].change_page[p];
8262 if (change->can_change_or_has_action &&
8263 change->has_event[trigger_event] &&
8264 change->trigger_side & trigger_side &&
8265 change->trigger_player & trigger_player &&
8266 change->trigger_page & trigger_page_bits &&
8267 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
8269 change->actual_trigger_element = trigger_element;
8270 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
8271 change->actual_trigger_side = trigger_side;
8272 change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
8274 if ((change->can_change && !change_done) || change->has_action)
8279 SCAN_PLAYFIELD(x, y)
8281 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8284 if (Feld[x][y] == element)
8286 if (change->can_change && !change_done)
8288 ChangeDelay[x][y] = 1;
8289 ChangeEvent[x][y] = trigger_event;
8291 HandleElementChange(x, y, p);
8293 #if USE_NEW_DELAYED_ACTION
8294 else if (change->has_action)
8296 ExecuteCustomElementAction(x, y, element, p);
8297 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8300 if (change->has_action)
8302 ExecuteCustomElementAction(x, y, element, p);
8303 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8309 if (change->can_change)
8312 change_done_any = TRUE;
8319 return change_done_any;
8322 static boolean CheckElementChangeExt(int x, int y,
8324 int trigger_element,
8329 boolean change_done = FALSE;
8332 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
8333 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
8336 if (Feld[x][y] == EL_BLOCKED)
8338 Blocked2Moving(x, y, &x, &y);
8339 element = Feld[x][y];
8342 if (Feld[x][y] != element) /* check if element has already changed */
8345 for (p = 0; p < element_info[element].num_change_pages; p++)
8347 struct ElementChangeInfo *change = &element_info[element].change_page[p];
8349 boolean check_trigger_element =
8350 (trigger_event == CE_TOUCHING_X ||
8351 trigger_event == CE_HITTING_X ||
8352 trigger_event == CE_HIT_BY_X);
8354 if (change->can_change_or_has_action &&
8355 change->has_event[trigger_event] &&
8356 change->trigger_side & trigger_side &&
8357 change->trigger_player & trigger_player &&
8358 (!check_trigger_element ||
8359 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
8361 change->actual_trigger_element = trigger_element;
8362 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
8363 change->actual_trigger_side = trigger_side;
8364 change->actual_trigger_ce_value = CustomValue[x][y];
8366 /* special case: trigger element not at (x,y) position for some events */
8367 if (check_trigger_element)
8379 { 0, 0 }, { 0, 0 }, { 0, 0 },
8383 int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
8384 int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
8386 change->actual_trigger_ce_value = CustomValue[xx][yy];
8389 if (change->can_change && !change_done)
8391 ChangeDelay[x][y] = 1;
8392 ChangeEvent[x][y] = trigger_event;
8394 HandleElementChange(x, y, p);
8398 #if USE_NEW_DELAYED_ACTION
8399 else if (change->has_action)
8401 ExecuteCustomElementAction(x, y, element, p);
8402 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8405 if (change->has_action)
8407 ExecuteCustomElementAction(x, y, element, p);
8408 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8417 static void PlayPlayerSound(struct PlayerInfo *player)
8419 int jx = player->jx, jy = player->jy;
8420 int sound_element = player->artwork_element;
8421 int last_action = player->last_action_waiting;
8422 int action = player->action_waiting;
8424 if (player->is_waiting)
8426 if (action != last_action)
8427 PlayLevelSoundElementAction(jx, jy, sound_element, action);
8429 PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
8433 if (action != last_action)
8434 StopSound(element_info[sound_element].sound[last_action]);
8436 if (last_action == ACTION_SLEEPING)
8437 PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
8441 static void PlayAllPlayersSound()
8445 for (i = 0; i < MAX_PLAYERS; i++)
8446 if (stored_player[i].active)
8447 PlayPlayerSound(&stored_player[i]);
8450 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
8452 boolean last_waiting = player->is_waiting;
8453 int move_dir = player->MovDir;
8455 player->last_action_waiting = player->action_waiting;
8459 if (!last_waiting) /* not waiting -> waiting */
8461 player->is_waiting = TRUE;
8463 player->frame_counter_bored =
8465 game.player_boring_delay_fixed +
8466 SimpleRND(game.player_boring_delay_random);
8467 player->frame_counter_sleeping =
8469 game.player_sleeping_delay_fixed +
8470 SimpleRND(game.player_sleeping_delay_random);
8472 InitPlayerGfxAnimation(player, ACTION_WAITING, player->MovDir);
8475 if (game.player_sleeping_delay_fixed +
8476 game.player_sleeping_delay_random > 0 &&
8477 player->anim_delay_counter == 0 &&
8478 player->post_delay_counter == 0 &&
8479 FrameCounter >= player->frame_counter_sleeping)
8480 player->is_sleeping = TRUE;
8481 else if (game.player_boring_delay_fixed +
8482 game.player_boring_delay_random > 0 &&
8483 FrameCounter >= player->frame_counter_bored)
8484 player->is_bored = TRUE;
8486 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
8487 player->is_bored ? ACTION_BORING :
8490 if (player->is_sleeping)
8492 if (player->num_special_action_sleeping > 0)
8494 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
8496 int last_special_action = player->special_action_sleeping;
8497 int num_special_action = player->num_special_action_sleeping;
8498 int special_action =
8499 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
8500 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
8501 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
8502 last_special_action + 1 : ACTION_SLEEPING);
8503 int special_graphic =
8504 el_act_dir2img(player->artwork_element, special_action, move_dir);
8506 player->anim_delay_counter =
8507 graphic_info[special_graphic].anim_delay_fixed +
8508 SimpleRND(graphic_info[special_graphic].anim_delay_random);
8509 player->post_delay_counter =
8510 graphic_info[special_graphic].post_delay_fixed +
8511 SimpleRND(graphic_info[special_graphic].post_delay_random);
8513 player->special_action_sleeping = special_action;
8516 if (player->anim_delay_counter > 0)
8518 player->action_waiting = player->special_action_sleeping;
8519 player->anim_delay_counter--;
8521 else if (player->post_delay_counter > 0)
8523 player->post_delay_counter--;
8527 else if (player->is_bored)
8529 if (player->num_special_action_bored > 0)
8531 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
8533 int special_action =
8534 ACTION_BORING_1 + SimpleRND(player->num_special_action_bored);
8535 int special_graphic =
8536 el_act_dir2img(player->artwork_element, special_action, move_dir);
8538 player->anim_delay_counter =
8539 graphic_info[special_graphic].anim_delay_fixed +
8540 SimpleRND(graphic_info[special_graphic].anim_delay_random);
8541 player->post_delay_counter =
8542 graphic_info[special_graphic].post_delay_fixed +
8543 SimpleRND(graphic_info[special_graphic].post_delay_random);
8545 player->special_action_bored = special_action;
8548 if (player->anim_delay_counter > 0)
8550 player->action_waiting = player->special_action_bored;
8551 player->anim_delay_counter--;
8553 else if (player->post_delay_counter > 0)
8555 player->post_delay_counter--;
8560 else if (last_waiting) /* waiting -> not waiting */
8562 player->is_waiting = FALSE;
8563 player->is_bored = FALSE;
8564 player->is_sleeping = FALSE;
8566 player->frame_counter_bored = -1;
8567 player->frame_counter_sleeping = -1;
8569 player->anim_delay_counter = 0;
8570 player->post_delay_counter = 0;
8572 player->action_waiting = ACTION_DEFAULT;
8574 player->special_action_bored = ACTION_DEFAULT;
8575 player->special_action_sleeping = ACTION_DEFAULT;
8579 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
8581 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
8582 int left = player_action & JOY_LEFT;
8583 int right = player_action & JOY_RIGHT;
8584 int up = player_action & JOY_UP;
8585 int down = player_action & JOY_DOWN;
8586 int button1 = player_action & JOY_BUTTON_1;
8587 int button2 = player_action & JOY_BUTTON_2;
8588 int dx = (left ? -1 : right ? 1 : 0);
8589 int dy = (up ? -1 : down ? 1 : 0);
8591 if (!player->active || tape.pausing)
8597 snapped = SnapField(player, dx, dy);
8601 dropped = DropElement(player);
8603 moved = MovePlayer(player, dx, dy);
8606 if (tape.single_step && tape.recording && !tape.pausing)
8608 if (button1 || (dropped && !moved))
8610 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8611 SnapField(player, 0, 0); /* stop snapping */
8615 SetPlayerWaiting(player, FALSE);
8617 return player_action;
8621 /* no actions for this player (no input at player's configured device) */
8623 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
8624 SnapField(player, 0, 0);
8625 CheckGravityMovementWhenNotMoving(player);
8627 if (player->MovPos == 0)
8628 SetPlayerWaiting(player, TRUE);
8630 if (player->MovPos == 0) /* needed for tape.playing */
8631 player->is_moving = FALSE;
8633 player->is_dropping = FALSE;
8639 void AdvanceFrameAndPlayerCounters(int player_nr)
8643 /* advance frame counters (global frame counter and time frame counter) */
8647 /* advance player counters (counters for move delay, move animation etc.) */
8648 for (i = 0; i < MAX_PLAYERS; i++)
8650 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
8651 int move_delay_value = stored_player[i].move_delay_value;
8652 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
8654 if (!advance_player_counters) /* not all players may be affected */
8657 #if USE_NEW_PLAYER_ANIM
8658 if (move_frames == 0) /* less than one move per game frame */
8660 int stepsize = TILEX / move_delay_value;
8661 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
8662 int count = (stored_player[i].is_moving ?
8663 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
8665 if (count % delay == 0)
8670 stored_player[i].Frame += move_frames;
8672 if (stored_player[i].MovPos != 0)
8673 stored_player[i].StepFrame += move_frames;
8675 if (stored_player[i].move_delay > 0)
8676 stored_player[i].move_delay--;
8678 /* due to bugs in previous versions, counter must count up, not down */
8679 if (stored_player[i].push_delay != -1)
8680 stored_player[i].push_delay++;
8682 if (stored_player[i].drop_delay > 0)
8683 stored_player[i].drop_delay--;
8689 static unsigned long game_frame_delay = 0;
8690 unsigned long game_frame_delay_value;
8691 int magic_wall_x = 0, magic_wall_y = 0;
8692 int i, x, y, element, graphic;
8693 byte *recorded_player_action;
8694 byte summarized_player_action = 0;
8695 byte tape_action[MAX_PLAYERS];
8697 if (game_status != GAME_MODE_PLAYING)
8700 game_frame_delay_value =
8701 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
8703 if (tape.playing && tape.warp_forward && !tape.pausing)
8704 game_frame_delay_value = 0;
8706 /* ---------- main game synchronization point ---------- */
8708 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
8710 InitPlayfieldScanModeVars();
8712 #if USE_ONE_MORE_CHANGE_PER_FRAME
8713 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
8715 SCAN_PLAYFIELD(x, y)
8717 ChangeCount[x][y] = 0;
8718 ChangeEvent[x][y] = -1;
8723 if (network_playing && !network_player_action_received)
8725 /* try to get network player actions in time */
8727 #if defined(NETWORK_AVALIABLE)
8728 /* last chance to get network player actions without main loop delay */
8732 /* game was quit by network peer */
8733 if (game_status != GAME_MODE_PLAYING)
8736 if (!network_player_action_received)
8737 return; /* failed to get network player actions in time */
8743 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
8746 /* !!! CHECK THIS (tape.pausing is always FALSE here!) !!! */
8747 if (recorded_player_action == NULL && tape.pausing)
8751 for (i = 0; i < MAX_PLAYERS; i++)
8753 summarized_player_action |= stored_player[i].action;
8755 if (!network_playing)
8756 stored_player[i].effective_action = stored_player[i].action;
8759 #if defined(NETWORK_AVALIABLE)
8760 if (network_playing)
8761 SendToServer_MovePlayer(summarized_player_action);
8764 if (!options.network && !setup.team_mode)
8765 local_player->effective_action = summarized_player_action;
8767 if (recorded_player_action != NULL)
8768 for (i = 0; i < MAX_PLAYERS; i++)
8769 stored_player[i].effective_action = recorded_player_action[i];
8771 for (i = 0; i < MAX_PLAYERS; i++)
8773 tape_action[i] = stored_player[i].effective_action;
8775 if (tape.recording && tape_action[i] && !tape.player_participates[i])
8776 tape.player_participates[i] = TRUE; /* player just appeared from CE */
8779 /* only save actions from input devices, but not programmed actions */
8781 TapeRecordAction(tape_action);
8783 for (i = 0; i < MAX_PLAYERS; i++)
8785 int actual_player_action = stored_player[i].effective_action;
8788 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
8789 - rnd_equinox_tetrachloride 048
8790 - rnd_equinox_tetrachloride_ii 096
8791 - rnd_emanuel_schmieg 002
8792 - doctor_sloan_ww 001, 020
8794 if (stored_player[i].MovPos == 0)
8795 CheckGravityMovement(&stored_player[i]);
8798 /* overwrite programmed action with tape action */
8799 if (stored_player[i].programmed_action)
8800 actual_player_action = stored_player[i].programmed_action;
8803 PlayerActions(&stored_player[i], actual_player_action);
8805 tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
8807 if (tape.recording && tape_action[i] && !tape.player_participates[i])
8808 tape.player_participates[i] = TRUE; /* player just appeared from CE */
8811 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
8814 network_player_action_received = FALSE;
8816 ScrollScreen(NULL, SCROLL_GO_ON);
8818 /* for backwards compatibility, the following code emulates a fixed bug that
8819 occured when pushing elements (causing elements that just made their last
8820 pushing step to already (if possible) make their first falling step in the
8821 same game frame, which is bad); this code is also needed to use the famous
8822 "spring push bug" which is used in older levels and might be wanted to be
8823 used also in newer levels, but in this case the buggy pushing code is only
8824 affecting the "spring" element and no other elements */
8826 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
8828 for (i = 0; i < MAX_PLAYERS; i++)
8830 struct PlayerInfo *player = &stored_player[i];
8834 if (player->active && player->is_pushing && player->is_moving &&
8836 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
8837 Feld[x][y] == EL_SPRING))
8839 ContinueMoving(x, y);
8841 /* continue moving after pushing (this is actually a bug) */
8842 if (!IS_MOVING(x, y))
8851 SCAN_PLAYFIELD(x, y)
8853 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8856 ChangeCount[x][y] = 0;
8857 ChangeEvent[x][y] = -1;
8859 /* this must be handled before main playfield loop */
8860 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
8863 if (MovDelay[x][y] <= 0)
8867 #if USE_NEW_SNAP_DELAY
8868 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
8871 if (MovDelay[x][y] <= 0)
8874 DrawLevelField(x, y);
8876 TestIfElementTouchesCustomElement(x, y); /* for empty space */
8882 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
8884 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
8885 printf("GameActions(): This should never happen!\n");
8887 ChangePage[x][y] = -1;
8892 if (WasJustMoving[x][y] > 0)
8893 WasJustMoving[x][y]--;
8894 if (WasJustFalling[x][y] > 0)
8895 WasJustFalling[x][y]--;
8896 if (CheckCollision[x][y] > 0)
8897 CheckCollision[x][y]--;
8901 /* reset finished pushing action (not done in ContinueMoving() to allow
8902 continuous pushing animation for elements with zero push delay) */
8903 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
8905 ResetGfxAnimation(x, y);
8906 DrawLevelField(x, y);
8910 if (IS_BLOCKED(x, y))
8914 Blocked2Moving(x, y, &oldx, &oldy);
8915 if (!IS_MOVING(oldx, oldy))
8917 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
8918 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
8919 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
8920 printf("GameActions(): This should never happen!\n");
8927 SCAN_PLAYFIELD(x, y)
8929 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8932 element = Feld[x][y];
8933 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8936 printf("::: %d,%d\n", x, y);
8938 if (element == EL_ROCK)
8939 printf("::: Yo man! Rocks can fall!\n");
8942 if (graphic_info[graphic].anim_global_sync)
8943 GfxFrame[x][y] = FrameCounter;
8944 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
8946 int old_gfx_frame = GfxFrame[x][y];
8948 GfxFrame[x][y] = CustomValue[x][y];
8951 if (GfxFrame[x][y] != old_gfx_frame)
8953 DrawLevelGraphicAnimation(x, y, graphic);
8955 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
8957 int old_gfx_frame = GfxFrame[x][y];
8959 GfxFrame[x][y] = element_info[element].collect_score;
8962 if (GfxFrame[x][y] != old_gfx_frame)
8964 DrawLevelGraphicAnimation(x, y, graphic);
8967 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
8968 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
8969 ResetRandomAnimationValue(x, y);
8971 SetRandomAnimationValue(x, y);
8973 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
8975 if (IS_INACTIVE(element))
8977 if (IS_ANIMATED(graphic))
8978 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8983 /* this may take place after moving, so 'element' may have changed */
8984 if (IS_CHANGING(x, y) &&
8985 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
8987 int page = element_info[element].event_page_nr[CE_DELAY];
8989 HandleElementChange(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] : page);
8993 printf("::: ChangeDelay == %d\n", ChangeDelay[x][y]);
8997 if (element == EL_CUSTOM_255)
8998 printf("::: ChangeDelay == %d\n", ChangeDelay[x][y]);
9002 HandleElementChange(x, y, page);
9004 if (CAN_CHANGE(element))
9005 HandleElementChange(x, y, page);
9007 if (HAS_ACTION(element))
9008 ExecuteCustomElementAction(x, y, element, page);
9013 element = Feld[x][y];
9014 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9017 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
9021 element = Feld[x][y];
9022 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9024 if (IS_ANIMATED(graphic) &&
9027 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9029 if (IS_GEM(element) || element == EL_SP_INFOTRON)
9030 EdelsteinFunkeln(x, y);
9032 else if ((element == EL_ACID ||
9033 element == EL_EXIT_OPEN ||
9034 element == EL_SP_EXIT_OPEN ||
9035 element == EL_SP_TERMINAL ||
9036 element == EL_SP_TERMINAL_ACTIVE ||
9037 element == EL_EXTRA_TIME ||
9038 element == EL_SHIELD_NORMAL ||
9039 element == EL_SHIELD_DEADLY) &&
9040 IS_ANIMATED(graphic))
9041 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9042 else if (IS_MOVING(x, y))
9043 ContinueMoving(x, y);
9044 else if (IS_ACTIVE_BOMB(element))
9045 CheckDynamite(x, y);
9046 else if (element == EL_AMOEBA_GROWING)
9047 AmoebeWaechst(x, y);
9048 else if (element == EL_AMOEBA_SHRINKING)
9049 AmoebaDisappearing(x, y);
9051 #if !USE_NEW_AMOEBA_CODE
9052 else if (IS_AMOEBALIVE(element))
9053 AmoebeAbleger(x, y);
9056 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
9058 else if (element == EL_EXIT_CLOSED)
9060 else if (element == EL_SP_EXIT_CLOSED)
9062 else if (element == EL_EXPANDABLE_WALL_GROWING)
9064 else if (element == EL_EXPANDABLE_WALL ||
9065 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9066 element == EL_EXPANDABLE_WALL_VERTICAL ||
9067 element == EL_EXPANDABLE_WALL_ANY)
9069 else if (element == EL_FLAMES)
9070 CheckForDragon(x, y);
9071 else if (element == EL_EXPLOSION)
9072 ; /* drawing of correct explosion animation is handled separately */
9073 else if (element == EL_ELEMENT_SNAPPING ||
9074 element == EL_DIAGONAL_SHRINKING ||
9075 element == EL_DIAGONAL_GROWING)
9078 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
9080 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9083 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
9084 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9087 if (element == EL_CUSTOM_255 ||
9088 element == EL_CUSTOM_256)
9089 DrawLevelGraphicAnimation(x, y, graphic);
9092 if (IS_BELT_ACTIVE(element))
9093 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
9095 if (game.magic_wall_active)
9097 int jx = local_player->jx, jy = local_player->jy;
9099 /* play the element sound at the position nearest to the player */
9100 if ((element == EL_MAGIC_WALL_FULL ||
9101 element == EL_MAGIC_WALL_ACTIVE ||
9102 element == EL_MAGIC_WALL_EMPTYING ||
9103 element == EL_BD_MAGIC_WALL_FULL ||
9104 element == EL_BD_MAGIC_WALL_ACTIVE ||
9105 element == EL_BD_MAGIC_WALL_EMPTYING) &&
9106 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
9114 #if USE_NEW_AMOEBA_CODE
9115 /* new experimental amoeba growth stuff */
9116 if (!(FrameCounter % 8))
9118 static unsigned long random = 1684108901;
9120 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
9122 x = RND(lev_fieldx);
9123 y = RND(lev_fieldy);
9124 element = Feld[x][y];
9126 if (!IS_PLAYER(x,y) &&
9127 (element == EL_EMPTY ||
9128 CAN_GROW_INTO(element) ||
9129 element == EL_QUICKSAND_EMPTY ||
9130 element == EL_ACID_SPLASH_LEFT ||
9131 element == EL_ACID_SPLASH_RIGHT))
9133 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
9134 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
9135 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
9136 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
9137 Feld[x][y] = EL_AMOEBA_DROP;
9140 random = random * 129 + 1;
9146 if (game.explosions_delayed)
9149 game.explosions_delayed = FALSE;
9152 SCAN_PLAYFIELD(x, y)
9154 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9157 element = Feld[x][y];
9159 if (ExplodeField[x][y])
9160 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
9161 else if (element == EL_EXPLOSION)
9162 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
9164 ExplodeField[x][y] = EX_TYPE_NONE;
9167 game.explosions_delayed = TRUE;
9170 if (game.magic_wall_active)
9172 if (!(game.magic_wall_time_left % 4))
9174 int element = Feld[magic_wall_x][magic_wall_y];
9176 if (element == EL_BD_MAGIC_WALL_FULL ||
9177 element == EL_BD_MAGIC_WALL_ACTIVE ||
9178 element == EL_BD_MAGIC_WALL_EMPTYING)
9179 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
9181 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
9184 if (game.magic_wall_time_left > 0)
9186 game.magic_wall_time_left--;
9187 if (!game.magic_wall_time_left)
9190 SCAN_PLAYFIELD(x, y)
9192 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9195 element = Feld[x][y];
9197 if (element == EL_MAGIC_WALL_ACTIVE ||
9198 element == EL_MAGIC_WALL_FULL)
9200 Feld[x][y] = EL_MAGIC_WALL_DEAD;
9201 DrawLevelField(x, y);
9203 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
9204 element == EL_BD_MAGIC_WALL_FULL)
9206 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
9207 DrawLevelField(x, y);
9211 game.magic_wall_active = FALSE;
9216 if (game.light_time_left > 0)
9218 game.light_time_left--;
9220 if (game.light_time_left == 0)
9221 RedrawAllLightSwitchesAndInvisibleElements();
9224 if (game.timegate_time_left > 0)
9226 game.timegate_time_left--;
9228 if (game.timegate_time_left == 0)
9229 CloseAllOpenTimegates();
9232 if (game.lenses_time_left > 0)
9234 game.lenses_time_left--;
9236 if (game.lenses_time_left == 0)
9237 RedrawAllInvisibleElementsForLenses();
9240 if (game.magnify_time_left > 0)
9242 game.magnify_time_left--;
9244 if (game.magnify_time_left == 0)
9245 RedrawAllInvisibleElementsForMagnifier();
9248 for (i = 0; i < MAX_PLAYERS; i++)
9250 struct PlayerInfo *player = &stored_player[i];
9252 if (SHIELD_ON(player))
9254 if (player->shield_deadly_time_left)
9255 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
9256 else if (player->shield_normal_time_left)
9257 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
9261 if (TimeFrames >= FRAMES_PER_SECOND)
9266 for (i = 0; i < MAX_PLAYERS; i++)
9268 struct PlayerInfo *player = &stored_player[i];
9270 if (SHIELD_ON(player))
9272 player->shield_normal_time_left--;
9274 if (player->shield_deadly_time_left > 0)
9275 player->shield_deadly_time_left--;
9279 if (!level.use_step_counter)
9287 if (TimeLeft <= 10 && setup.time_limit)
9288 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
9290 DrawGameValue_Time(TimeLeft);
9292 if (!TimeLeft && setup.time_limit)
9293 for (i = 0; i < MAX_PLAYERS; i++)
9294 KillPlayer(&stored_player[i]);
9296 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
9297 DrawGameValue_Time(TimePlayed);
9300 if (tape.recording || tape.playing)
9301 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
9305 PlayAllPlayersSound();
9307 if (options.debug) /* calculate frames per second */
9309 static unsigned long fps_counter = 0;
9310 static int fps_frames = 0;
9311 unsigned long fps_delay_ms = Counter() - fps_counter;
9315 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
9317 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
9320 fps_counter = Counter();
9323 redraw_mask |= REDRAW_FPS;
9326 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
9328 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
9330 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
9332 local_player->show_envelope = 0;
9335 /* use random number generator in every frame to make it less predictable */
9336 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
9340 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
9342 int min_x = x, min_y = y, max_x = x, max_y = y;
9345 for (i = 0; i < MAX_PLAYERS; i++)
9347 int jx = stored_player[i].jx, jy = stored_player[i].jy;
9349 if (!stored_player[i].active || &stored_player[i] == player)
9352 min_x = MIN(min_x, jx);
9353 min_y = MIN(min_y, jy);
9354 max_x = MAX(max_x, jx);
9355 max_y = MAX(max_y, jy);
9358 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
9361 static boolean AllPlayersInVisibleScreen()
9365 for (i = 0; i < MAX_PLAYERS; i++)
9367 int jx = stored_player[i].jx, jy = stored_player[i].jy;
9369 if (!stored_player[i].active)
9372 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
9379 void ScrollLevel(int dx, int dy)
9381 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
9384 BlitBitmap(drawto_field, drawto_field,
9385 FX + TILEX * (dx == -1) - softscroll_offset,
9386 FY + TILEY * (dy == -1) - softscroll_offset,
9387 SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
9388 SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
9389 FX + TILEX * (dx == 1) - softscroll_offset,
9390 FY + TILEY * (dy == 1) - softscroll_offset);
9394 x = (dx == 1 ? BX1 : BX2);
9395 for (y = BY1; y <= BY2; y++)
9396 DrawScreenField(x, y);
9401 y = (dy == 1 ? BY1 : BY2);
9402 for (x = BX1; x <= BX2; x++)
9403 DrawScreenField(x, y);
9406 redraw_mask |= REDRAW_FIELD;
9409 static boolean canFallDown(struct PlayerInfo *player)
9411 int jx = player->jx, jy = player->jy;
9413 return (IN_LEV_FIELD(jx, jy + 1) &&
9414 (IS_FREE(jx, jy + 1) ||
9415 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
9416 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
9417 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
9420 static boolean canPassField(int x, int y, int move_dir)
9422 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9423 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9424 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
9427 int element = Feld[x][y];
9429 return (IS_PASSABLE_FROM(element, opposite_dir) &&
9430 !CAN_MOVE(element) &&
9431 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
9432 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
9433 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
9436 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
9438 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9439 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9440 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
9444 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9445 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
9446 (IS_DIGGABLE(Feld[newx][newy]) ||
9447 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9448 canPassField(newx, newy, move_dir)));
9451 static void CheckGravityMovement(struct PlayerInfo *player)
9453 if (game.gravity && !player->programmed_action)
9455 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
9456 int move_dir_vertical = player->effective_action & MV_VERTICAL;
9457 boolean player_is_snapping = player->effective_action & JOY_BUTTON_1;
9458 int jx = player->jx, jy = player->jy;
9459 boolean player_is_moving_to_valid_field =
9460 (!player_is_snapping &&
9461 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
9462 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
9463 boolean player_can_fall_down = canFallDown(player);
9465 if (player_can_fall_down &&
9466 !player_is_moving_to_valid_field)
9467 player->programmed_action = MV_DOWN;
9471 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
9473 return CheckGravityMovement(player);
9475 if (game.gravity && !player->programmed_action)
9477 int jx = player->jx, jy = player->jy;
9478 boolean field_under_player_is_free =
9479 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
9480 boolean player_is_standing_on_valid_field =
9481 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
9482 (IS_WALKABLE(Feld[jx][jy]) &&
9483 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
9485 if (field_under_player_is_free && !player_is_standing_on_valid_field)
9486 player->programmed_action = MV_DOWN;
9492 -----------------------------------------------------------------------------
9493 dx, dy: direction (non-diagonal) to try to move the player to
9494 real_dx, real_dy: direction as read from input device (can be diagonal)
9497 boolean MovePlayerOneStep(struct PlayerInfo *player,
9498 int dx, int dy, int real_dx, int real_dy)
9500 int jx = player->jx, jy = player->jy;
9501 int new_jx = jx + dx, new_jy = jy + dy;
9502 #if !USE_FIXED_DONT_RUN_INTO
9506 boolean player_can_move = !player->cannot_move;
9508 if (!player->active || (!dx && !dy))
9509 return MP_NO_ACTION;
9511 player->MovDir = (dx < 0 ? MV_LEFT :
9514 dy > 0 ? MV_DOWN : MV_NONE);
9516 if (!IN_LEV_FIELD(new_jx, new_jy))
9517 return MP_NO_ACTION;
9519 if (!player_can_move)
9522 if (player->MovPos == 0)
9524 player->is_moving = FALSE;
9525 player->is_digging = FALSE;
9526 player->is_collecting = FALSE;
9527 player->is_snapping = FALSE;
9528 player->is_pushing = FALSE;
9531 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
9532 SnapField(player, 0, 0);
9536 return MP_NO_ACTION;
9540 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
9541 return MP_NO_ACTION;
9543 #if !USE_FIXED_DONT_RUN_INTO
9544 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
9546 /* (moved to DigField()) */
9547 if (player_can_move && DONT_RUN_INTO(element))
9549 if (element == EL_ACID && dx == 0 && dy == 1)
9551 SplashAcid(new_jx, new_jy);
9552 Feld[jx][jy] = EL_PLAYER_1;
9553 InitMovingField(jx, jy, MV_DOWN);
9554 Store[jx][jy] = EL_ACID;
9555 ContinueMoving(jx, jy);
9559 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
9565 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
9567 #if USE_FIXED_DONT_RUN_INTO
9568 if (can_move == MP_DONT_RUN_INTO)
9572 if (can_move != MP_MOVING)
9575 #if USE_FIXED_DONT_RUN_INTO
9578 /* check if DigField() has caused relocation of the player */
9579 if (player->jx != jx || player->jy != jy)
9580 return MP_NO_ACTION; /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
9582 StorePlayer[jx][jy] = 0;
9583 player->last_jx = jx;
9584 player->last_jy = jy;
9585 player->jx = new_jx;
9586 player->jy = new_jy;
9587 StorePlayer[new_jx][new_jy] = player->element_nr;
9589 if (player->move_delay_value_next != -1)
9591 player->move_delay_value = player->move_delay_value_next;
9592 player->move_delay_value_next = -1;
9596 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
9598 player->step_counter++;
9600 PlayerVisit[jx][jy] = FrameCounter;
9602 ScrollPlayer(player, SCROLL_INIT);
9607 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
9609 int jx = player->jx, jy = player->jy;
9610 int old_jx = jx, old_jy = jy;
9611 int moved = MP_NO_ACTION;
9613 if (!player->active)
9618 if (player->MovPos == 0)
9620 player->is_moving = FALSE;
9621 player->is_digging = FALSE;
9622 player->is_collecting = FALSE;
9623 player->is_snapping = FALSE;
9624 player->is_pushing = FALSE;
9630 if (player->move_delay > 0)
9633 player->move_delay = -1; /* set to "uninitialized" value */
9635 /* store if player is automatically moved to next field */
9636 player->is_auto_moving = (player->programmed_action != MV_NONE);
9638 /* remove the last programmed player action */
9639 player->programmed_action = 0;
9643 /* should only happen if pre-1.2 tape recordings are played */
9644 /* this is only for backward compatibility */
9646 int original_move_delay_value = player->move_delay_value;
9649 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
9653 /* scroll remaining steps with finest movement resolution */
9654 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
9656 while (player->MovPos)
9658 ScrollPlayer(player, SCROLL_GO_ON);
9659 ScrollScreen(NULL, SCROLL_GO_ON);
9661 AdvanceFrameAndPlayerCounters(player->index_nr);
9667 player->move_delay_value = original_move_delay_value;
9670 if (player->last_move_dir & MV_HORIZONTAL)
9672 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
9673 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
9677 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
9678 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
9684 if (moved & MP_MOVING && !ScreenMovPos &&
9685 (player == local_player || !options.network))
9687 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
9688 int offset = (setup.scroll_delay ? 3 : 0);
9690 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
9692 /* actual player has left the screen -- scroll in that direction */
9693 if (jx != old_jx) /* player has moved horizontally */
9694 scroll_x += (jx - old_jx);
9695 else /* player has moved vertically */
9696 scroll_y += (jy - old_jy);
9700 if (jx != old_jx) /* player has moved horizontally */
9702 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
9703 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
9704 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
9706 /* don't scroll over playfield boundaries */
9707 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
9708 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
9710 /* don't scroll more than one field at a time */
9711 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
9713 /* don't scroll against the player's moving direction */
9714 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
9715 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
9716 scroll_x = old_scroll_x;
9718 else /* player has moved vertically */
9720 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
9721 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
9722 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
9724 /* don't scroll over playfield boundaries */
9725 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
9726 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
9728 /* don't scroll more than one field at a time */
9729 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
9731 /* don't scroll against the player's moving direction */
9732 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
9733 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
9734 scroll_y = old_scroll_y;
9738 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
9740 if (!options.network && !AllPlayersInVisibleScreen())
9742 scroll_x = old_scroll_x;
9743 scroll_y = old_scroll_y;
9747 ScrollScreen(player, SCROLL_INIT);
9748 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
9753 player->StepFrame = 0;
9755 if (moved & MP_MOVING)
9757 if (old_jx != jx && old_jy == jy)
9758 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
9759 else if (old_jx == jx && old_jy != jy)
9760 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
9762 DrawLevelField(jx, jy); /* for "crumbled sand" */
9764 player->last_move_dir = player->MovDir;
9765 player->is_moving = TRUE;
9766 player->is_snapping = FALSE;
9767 player->is_switching = FALSE;
9768 player->is_dropping = FALSE;
9772 CheckGravityMovementWhenNotMoving(player);
9774 player->is_moving = FALSE;
9776 /* at this point, the player is allowed to move, but cannot move right now
9777 (e.g. because of something blocking the way) -- ensure that the player
9778 is also allowed to move in the next frame (in old versions before 3.1.1,
9779 the player was forced to wait again for eight frames before next try) */
9781 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
9782 player->move_delay = 0; /* allow direct movement in the next frame */
9785 if (player->move_delay == -1) /* not yet initialized by DigField() */
9786 player->move_delay = player->move_delay_value;
9788 if (game.engine_version < VERSION_IDENT(3,0,7,0))
9790 TestIfPlayerTouchesBadThing(jx, jy);
9791 TestIfPlayerTouchesCustomElement(jx, jy);
9794 if (!player->active)
9795 RemovePlayer(player);
9800 void ScrollPlayer(struct PlayerInfo *player, int mode)
9802 int jx = player->jx, jy = player->jy;
9803 int last_jx = player->last_jx, last_jy = player->last_jy;
9804 int move_stepsize = TILEX / player->move_delay_value;
9806 #if USE_NEW_PLAYER_SPEED
9807 if (!player->active)
9810 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
9813 if (!player->active || player->MovPos == 0)
9817 if (mode == SCROLL_INIT)
9819 player->actual_frame_counter = FrameCounter;
9820 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
9822 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
9823 Feld[last_jx][last_jy] == EL_EMPTY)
9825 int last_field_block_delay = 0; /* start with no blocking at all */
9826 int block_delay_adjustment = player->block_delay_adjustment;
9828 /* if player blocks last field, add delay for exactly one move */
9829 if (player->block_last_field)
9831 last_field_block_delay += player->move_delay_value;
9833 /* when blocking enabled, prevent moving up despite gravity */
9834 if (game.gravity && player->MovDir == MV_UP)
9835 block_delay_adjustment = -1;
9838 /* add block delay adjustment (also possible when not blocking) */
9839 last_field_block_delay += block_delay_adjustment;
9841 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
9842 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
9845 #if USE_NEW_PLAYER_SPEED
9846 if (player->MovPos != 0) /* player has not yet reached destination */
9852 else if (!FrameReached(&player->actual_frame_counter, 1))
9856 printf("::: player->MovPos: %d -> %d\n",
9858 player->MovPos + (player->MovPos > 0 ? -1 : 1) * move_stepsize);
9861 #if USE_NEW_PLAYER_SPEED
9862 if (player->MovPos != 0)
9864 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
9865 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
9867 /* before DrawPlayer() to draw correct player graphic for this case */
9868 if (player->MovPos == 0)
9869 CheckGravityMovement(player);
9872 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
9873 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
9875 /* before DrawPlayer() to draw correct player graphic for this case */
9876 if (player->MovPos == 0)
9877 CheckGravityMovement(player);
9880 if (player->MovPos == 0) /* player reached destination field */
9883 printf("::: player reached destination field\n");
9886 if (player->move_delay_reset_counter > 0)
9888 player->move_delay_reset_counter--;
9890 if (player->move_delay_reset_counter == 0)
9892 /* continue with normal speed after quickly moving through gate */
9893 HALVE_PLAYER_SPEED(player);
9895 /* be able to make the next move without delay */
9896 player->move_delay = 0;
9900 player->last_jx = jx;
9901 player->last_jy = jy;
9903 if (Feld[jx][jy] == EL_EXIT_OPEN ||
9904 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
9905 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
9907 DrawPlayer(player); /* needed here only to cleanup last field */
9908 RemovePlayer(player);
9910 if (local_player->friends_still_needed == 0 ||
9911 IS_SP_ELEMENT(Feld[jx][jy]))
9912 player->LevelSolved = player->GameOver = TRUE;
9915 /* this breaks one level: "machine", level 000 */
9917 int move_direction = player->MovDir;
9918 int enter_side = MV_DIR_OPPOSITE(move_direction);
9919 int leave_side = move_direction;
9920 int old_jx = last_jx;
9921 int old_jy = last_jy;
9922 int old_element = Feld[old_jx][old_jy];
9923 int new_element = Feld[jx][jy];
9925 if (IS_CUSTOM_ELEMENT(old_element))
9926 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
9928 player->index_bit, leave_side);
9930 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
9932 player->index_bit, leave_side);
9934 if (IS_CUSTOM_ELEMENT(new_element))
9935 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
9936 player->index_bit, enter_side);
9938 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
9940 player->index_bit, enter_side);
9942 CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
9943 CE_MOVE_OF_X, move_direction);
9946 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
9948 TestIfPlayerTouchesBadThing(jx, jy);
9949 TestIfPlayerTouchesCustomElement(jx, jy);
9951 /* needed because pushed element has not yet reached its destination,
9952 so it would trigger a change event at its previous field location */
9953 if (!player->is_pushing)
9954 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
9956 if (!player->active)
9957 RemovePlayer(player);
9960 if (level.use_step_counter)
9970 if (TimeLeft <= 10 && setup.time_limit)
9971 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
9973 DrawGameValue_Time(TimeLeft);
9975 if (!TimeLeft && setup.time_limit)
9976 for (i = 0; i < MAX_PLAYERS; i++)
9977 KillPlayer(&stored_player[i]);
9979 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
9980 DrawGameValue_Time(TimePlayed);
9983 if (tape.single_step && tape.recording && !tape.pausing &&
9984 !player->programmed_action)
9985 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9989 void ScrollScreen(struct PlayerInfo *player, int mode)
9991 static unsigned long screen_frame_counter = 0;
9993 if (mode == SCROLL_INIT)
9995 /* set scrolling step size according to actual player's moving speed */
9996 ScrollStepSize = TILEX / player->move_delay_value;
9998 screen_frame_counter = FrameCounter;
9999 ScreenMovDir = player->MovDir;
10000 ScreenMovPos = player->MovPos;
10001 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
10004 else if (!FrameReached(&screen_frame_counter, 1))
10009 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
10010 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
10011 redraw_mask |= REDRAW_FIELD;
10014 ScreenMovDir = MV_NONE;
10017 void TestIfPlayerTouchesCustomElement(int x, int y)
10019 static int xy[4][2] =
10026 static int trigger_sides[4][2] =
10028 /* center side border side */
10029 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
10030 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
10031 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
10032 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
10034 static int touch_dir[4] =
10036 MV_LEFT | MV_RIGHT,
10041 int center_element = Feld[x][y]; /* should always be non-moving! */
10044 for (i = 0; i < NUM_DIRECTIONS; i++)
10046 int xx = x + xy[i][0];
10047 int yy = y + xy[i][1];
10048 int center_side = trigger_sides[i][0];
10049 int border_side = trigger_sides[i][1];
10050 int border_element;
10052 if (!IN_LEV_FIELD(xx, yy))
10055 if (IS_PLAYER(x, y))
10057 struct PlayerInfo *player = PLAYERINFO(x, y);
10059 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10060 border_element = Feld[xx][yy]; /* may be moving! */
10061 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
10062 border_element = Feld[xx][yy];
10063 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
10064 border_element = MovingOrBlocked2Element(xx, yy);
10066 continue; /* center and border element do not touch */
10068 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
10069 player->index_bit, border_side);
10070 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
10071 CE_PLAYER_TOUCHES_X,
10072 player->index_bit, border_side);
10074 else if (IS_PLAYER(xx, yy))
10076 struct PlayerInfo *player = PLAYERINFO(xx, yy);
10078 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10080 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
10081 continue; /* center and border element do not touch */
10084 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
10085 player->index_bit, center_side);
10086 CheckTriggeredElementChangeByPlayer(x, y, center_element,
10087 CE_PLAYER_TOUCHES_X,
10088 player->index_bit, center_side);
10094 void TestIfElementTouchesCustomElement(int x, int y)
10096 static int xy[4][2] =
10103 static int trigger_sides[4][2] =
10105 /* center side border side */
10106 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
10107 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
10108 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
10109 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
10111 static int touch_dir[4] =
10113 MV_LEFT | MV_RIGHT,
10118 boolean change_center_element = FALSE;
10119 int center_element = Feld[x][y]; /* should always be non-moving! */
10122 for (i = 0; i < NUM_DIRECTIONS; i++)
10124 int xx = x + xy[i][0];
10125 int yy = y + xy[i][1];
10126 int center_side = trigger_sides[i][0];
10127 int border_side = trigger_sides[i][1];
10128 int border_element;
10130 if (!IN_LEV_FIELD(xx, yy))
10133 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10134 border_element = Feld[xx][yy]; /* may be moving! */
10135 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
10136 border_element = Feld[xx][yy];
10137 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
10138 border_element = MovingOrBlocked2Element(xx, yy);
10140 continue; /* center and border element do not touch */
10142 /* check for change of center element (but change it only once) */
10143 if (!change_center_element)
10144 change_center_element =
10145 CheckElementChangeBySide(x, y, center_element, border_element,
10146 CE_TOUCHING_X, border_side);
10148 /* check for change of border element */
10149 CheckElementChangeBySide(xx, yy, border_element, center_element,
10150 CE_TOUCHING_X, center_side);
10154 void TestIfElementHitsCustomElement(int x, int y, int direction)
10156 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
10157 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
10158 int hitx = x + dx, hity = y + dy;
10159 int hitting_element = Feld[x][y];
10160 int touched_element;
10162 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
10165 touched_element = (IN_LEV_FIELD(hitx, hity) ?
10166 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
10168 if (IN_LEV_FIELD(hitx, hity))
10170 int opposite_direction = MV_DIR_OPPOSITE(direction);
10171 int hitting_side = direction;
10172 int touched_side = opposite_direction;
10173 boolean object_hit = (!IS_MOVING(hitx, hity) ||
10174 MovDir[hitx][hity] != direction ||
10175 ABS(MovPos[hitx][hity]) <= TILEY / 2);
10181 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10182 CE_HITTING_X, touched_side);
10184 CheckElementChangeBySide(hitx, hity, touched_element,
10185 hitting_element, CE_HIT_BY_X, hitting_side);
10187 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10188 CE_HIT_BY_SOMETHING, opposite_direction);
10192 /* "hitting something" is also true when hitting the playfield border */
10193 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10194 CE_HITTING_SOMETHING, direction);
10198 void TestIfElementSmashesCustomElement(int x, int y, int direction)
10200 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
10201 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
10202 int hitx = x + dx, hity = y + dy;
10203 int hitting_element = Feld[x][y];
10204 int touched_element;
10206 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
10207 !IS_FREE(hitx, hity) &&
10208 (!IS_MOVING(hitx, hity) ||
10209 MovDir[hitx][hity] != direction ||
10210 ABS(MovPos[hitx][hity]) <= TILEY / 2));
10213 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
10217 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
10221 touched_element = (IN_LEV_FIELD(hitx, hity) ?
10222 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
10224 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10225 EP_CAN_SMASH_EVERYTHING, direction);
10227 if (IN_LEV_FIELD(hitx, hity))
10229 int opposite_direction = MV_DIR_OPPOSITE(direction);
10230 int hitting_side = direction;
10231 int touched_side = opposite_direction;
10233 int touched_element = MovingOrBlocked2Element(hitx, hity);
10236 boolean object_hit = (!IS_MOVING(hitx, hity) ||
10237 MovDir[hitx][hity] != direction ||
10238 ABS(MovPos[hitx][hity]) <= TILEY / 2);
10247 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10248 CE_SMASHED_BY_SOMETHING, opposite_direction);
10250 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10251 CE_OTHER_IS_SMASHING, touched_side);
10253 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10254 CE_OTHER_GETS_SMASHED, hitting_side);
10260 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
10262 int i, kill_x = -1, kill_y = -1;
10264 int bad_element = -1;
10265 static int test_xy[4][2] =
10272 static int test_dir[4] =
10280 for (i = 0; i < NUM_DIRECTIONS; i++)
10282 int test_x, test_y, test_move_dir, test_element;
10284 test_x = good_x + test_xy[i][0];
10285 test_y = good_y + test_xy[i][1];
10287 if (!IN_LEV_FIELD(test_x, test_y))
10291 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
10293 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
10295 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
10296 2nd case: DONT_TOUCH style bad thing does not move away from good thing
10298 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
10299 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
10303 bad_element = test_element;
10309 if (kill_x != -1 || kill_y != -1)
10311 if (IS_PLAYER(good_x, good_y))
10313 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
10315 if (player->shield_deadly_time_left > 0 &&
10316 !IS_INDESTRUCTIBLE(bad_element))
10317 Bang(kill_x, kill_y);
10318 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
10319 KillPlayer(player);
10322 Bang(good_x, good_y);
10326 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
10328 int i, kill_x = -1, kill_y = -1;
10329 int bad_element = Feld[bad_x][bad_y];
10330 static int test_xy[4][2] =
10337 static int touch_dir[4] =
10339 MV_LEFT | MV_RIGHT,
10344 static int test_dir[4] =
10352 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
10355 for (i = 0; i < NUM_DIRECTIONS; i++)
10357 int test_x, test_y, test_move_dir, test_element;
10359 test_x = bad_x + test_xy[i][0];
10360 test_y = bad_y + test_xy[i][1];
10361 if (!IN_LEV_FIELD(test_x, test_y))
10365 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
10367 test_element = Feld[test_x][test_y];
10369 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
10370 2nd case: DONT_TOUCH style bad thing does not move away from good thing
10372 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
10373 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
10375 /* good thing is player or penguin that does not move away */
10376 if (IS_PLAYER(test_x, test_y))
10378 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
10380 if (bad_element == EL_ROBOT && player->is_moving)
10381 continue; /* robot does not kill player if he is moving */
10383 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10385 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
10386 continue; /* center and border element do not touch */
10393 else if (test_element == EL_PENGUIN)
10402 if (kill_x != -1 || kill_y != -1)
10404 if (IS_PLAYER(kill_x, kill_y))
10406 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
10408 if (player->shield_deadly_time_left > 0 &&
10409 !IS_INDESTRUCTIBLE(bad_element))
10410 Bang(bad_x, bad_y);
10411 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
10412 KillPlayer(player);
10415 Bang(kill_x, kill_y);
10419 void TestIfPlayerTouchesBadThing(int x, int y)
10421 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
10424 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
10426 TestIfGoodThingHitsBadThing(x, y, move_dir);
10429 void TestIfBadThingTouchesPlayer(int x, int y)
10431 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
10434 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
10436 TestIfBadThingHitsGoodThing(x, y, move_dir);
10439 void TestIfFriendTouchesBadThing(int x, int y)
10441 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
10444 void TestIfBadThingTouchesFriend(int x, int y)
10446 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
10449 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
10451 int i, kill_x = bad_x, kill_y = bad_y;
10452 static int xy[4][2] =
10460 for (i = 0; i < NUM_DIRECTIONS; i++)
10464 x = bad_x + xy[i][0];
10465 y = bad_y + xy[i][1];
10466 if (!IN_LEV_FIELD(x, y))
10469 element = Feld[x][y];
10470 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
10471 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
10479 if (kill_x != bad_x || kill_y != bad_y)
10480 Bang(bad_x, bad_y);
10483 void KillPlayer(struct PlayerInfo *player)
10485 int jx = player->jx, jy = player->jy;
10487 if (!player->active)
10490 /* remove accessible field at the player's position */
10491 Feld[jx][jy] = EL_EMPTY;
10493 /* deactivate shield (else Bang()/Explode() would not work right) */
10494 player->shield_normal_time_left = 0;
10495 player->shield_deadly_time_left = 0;
10498 BuryPlayer(player);
10501 static void KillPlayerUnlessEnemyProtected(int x, int y)
10503 if (!PLAYER_ENEMY_PROTECTED(x, y))
10504 KillPlayer(PLAYERINFO(x, y));
10507 static void KillPlayerUnlessExplosionProtected(int x, int y)
10509 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
10510 KillPlayer(PLAYERINFO(x, y));
10513 void BuryPlayer(struct PlayerInfo *player)
10515 int jx = player->jx, jy = player->jy;
10517 if (!player->active)
10520 PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
10521 PlayLevelSound(jx, jy, SND_GAME_LOSING);
10523 player->GameOver = TRUE;
10524 RemovePlayer(player);
10527 void RemovePlayer(struct PlayerInfo *player)
10529 int jx = player->jx, jy = player->jy;
10530 int i, found = FALSE;
10532 player->present = FALSE;
10533 player->active = FALSE;
10535 if (!ExplodeField[jx][jy])
10536 StorePlayer[jx][jy] = 0;
10538 if (player->is_moving)
10539 DrawLevelField(player->last_jx, player->last_jy);
10541 for (i = 0; i < MAX_PLAYERS; i++)
10542 if (stored_player[i].active)
10546 AllPlayersGone = TRUE;
10552 #if USE_NEW_SNAP_DELAY
10553 static void setFieldForSnapping(int x, int y, int element, int direction)
10555 struct ElementInfo *ei = &element_info[element];
10556 int direction_bit = MV_DIR_TO_BIT(direction);
10557 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
10558 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
10559 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
10561 Feld[x][y] = EL_ELEMENT_SNAPPING;
10562 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
10564 ResetGfxAnimation(x, y);
10566 GfxElement[x][y] = element;
10567 GfxAction[x][y] = action;
10568 GfxDir[x][y] = direction;
10569 GfxFrame[x][y] = -1;
10574 =============================================================================
10575 checkDiagonalPushing()
10576 -----------------------------------------------------------------------------
10577 check if diagonal input device direction results in pushing of object
10578 (by checking if the alternative direction is walkable, diggable, ...)
10579 =============================================================================
10582 static boolean checkDiagonalPushing(struct PlayerInfo *player,
10583 int x, int y, int real_dx, int real_dy)
10585 int jx, jy, dx, dy, xx, yy;
10587 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
10590 /* diagonal direction: check alternative direction */
10595 xx = jx + (dx == 0 ? real_dx : 0);
10596 yy = jy + (dy == 0 ? real_dy : 0);
10598 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
10602 =============================================================================
10604 -----------------------------------------------------------------------------
10605 x, y: field next to player (non-diagonal) to try to dig to
10606 real_dx, real_dy: direction as read from input device (can be diagonal)
10607 =============================================================================
10610 int DigField(struct PlayerInfo *player,
10611 int oldx, int oldy, int x, int y,
10612 int real_dx, int real_dy, int mode)
10614 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
10615 boolean player_was_pushing = player->is_pushing;
10616 boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
10617 boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
10618 int jx = oldx, jy = oldy;
10619 int dx = x - jx, dy = y - jy;
10620 int nextx = x + dx, nexty = y + dy;
10621 int move_direction = (dx == -1 ? MV_LEFT :
10622 dx == +1 ? MV_RIGHT :
10624 dy == +1 ? MV_DOWN : MV_NONE);
10625 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
10626 int dig_side = MV_DIR_OPPOSITE(move_direction);
10627 int old_element = Feld[jx][jy];
10628 #if USE_FIXED_DONT_RUN_INTO
10629 int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
10635 if (is_player) /* function can also be called by EL_PENGUIN */
10637 if (player->MovPos == 0)
10639 player->is_digging = FALSE;
10640 player->is_collecting = FALSE;
10643 if (player->MovPos == 0) /* last pushing move finished */
10644 player->is_pushing = FALSE;
10646 if (mode == DF_NO_PUSH) /* player just stopped pushing */
10648 player->is_switching = FALSE;
10649 player->push_delay = -1;
10651 return MP_NO_ACTION;
10655 #if !USE_FIXED_DONT_RUN_INTO
10656 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
10657 return MP_NO_ACTION;
10660 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
10661 old_element = Back[jx][jy];
10663 /* in case of element dropped at player position, check background */
10664 else if (Back[jx][jy] != EL_EMPTY &&
10665 game.engine_version >= VERSION_IDENT(2,2,0,0))
10666 old_element = Back[jx][jy];
10669 #if USE_FIXED_DONT_RUN_INTO
10670 if (player_can_move && DONT_RUN_INTO(element))
10672 if (element == EL_ACID && dx == 0 && dy == 1)
10675 Feld[jx][jy] = EL_PLAYER_1;
10676 InitMovingField(jx, jy, MV_DOWN);
10677 Store[jx][jy] = EL_ACID;
10678 ContinueMoving(jx, jy);
10679 BuryPlayer(player);
10682 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
10684 return MP_DONT_RUN_INTO;
10690 #if USE_FIXED_DONT_RUN_INTO
10691 if (player_can_move && DONT_RUN_INTO(element))
10693 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
10695 return MP_DONT_RUN_INTO;
10700 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
10701 return MP_NO_ACTION; /* field has no opening in this direction */
10703 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
10704 return MP_NO_ACTION; /* field has no opening in this direction */
10707 #if USE_FIXED_DONT_RUN_INTO
10708 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
10711 Feld[jx][jy] = EL_PLAYER_1;
10712 InitMovingField(jx, jy, MV_DOWN);
10713 Store[jx][jy] = EL_ACID;
10714 ContinueMoving(jx, jy);
10715 BuryPlayer(player);
10717 return MP_DONT_RUN_INTO;
10723 #if USE_FIXED_DONT_RUN_INTO
10724 if (player_can_move && DONT_RUN_INTO(element))
10726 if (element == EL_ACID && dx == 0 && dy == 1)
10729 Feld[jx][jy] = EL_PLAYER_1;
10730 InitMovingField(jx, jy, MV_DOWN);
10731 Store[jx][jy] = EL_ACID;
10732 ContinueMoving(jx, jy);
10733 BuryPlayer(player);
10736 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
10738 return MP_DONT_RUN_INTO;
10743 #if USE_FIXED_DONT_RUN_INTO
10744 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
10745 return MP_NO_ACTION;
10748 #if !USE_FIXED_DONT_RUN_INTO
10749 element = Feld[x][y];
10752 collect_count = element_info[element].collect_count_initial;
10754 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
10755 return MP_NO_ACTION;
10757 if (game.engine_version < VERSION_IDENT(2,2,0,0))
10758 player_can_move = player_can_move_or_snap;
10760 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
10761 game.engine_version >= VERSION_IDENT(2,2,0,0))
10763 CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
10764 player->index_bit, dig_side);
10765 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
10766 player->index_bit, dig_side);
10768 if (Feld[x][y] != element) /* field changed by snapping */
10771 return MP_NO_ACTION;
10774 if (game.gravity && is_player && !player->is_auto_moving &&
10775 canFallDown(player) && move_direction != MV_DOWN &&
10776 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
10777 return MP_NO_ACTION; /* player cannot walk here due to gravity */
10779 if (player_can_move &&
10780 IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
10782 int sound_element = SND_ELEMENT(element);
10783 int sound_action = ACTION_WALKING;
10785 if (IS_RND_GATE(element))
10787 if (!player->key[RND_GATE_NR(element)])
10788 return MP_NO_ACTION;
10790 else if (IS_RND_GATE_GRAY(element))
10792 if (!player->key[RND_GATE_GRAY_NR(element)])
10793 return MP_NO_ACTION;
10795 else if (IS_RND_GATE_GRAY_ACTIVE(element))
10797 if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
10798 return MP_NO_ACTION;
10800 else if (element == EL_EXIT_OPEN ||
10801 element == EL_SP_EXIT_OPEN ||
10802 element == EL_SP_EXIT_OPENING)
10804 sound_action = ACTION_PASSING; /* player is passing exit */
10806 else if (element == EL_EMPTY)
10808 sound_action = ACTION_MOVING; /* nothing to walk on */
10811 /* play sound from background or player, whatever is available */
10812 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
10813 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
10815 PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
10817 else if (player_can_move &&
10818 IS_PASSABLE(element) && canPassField(x, y, move_direction))
10820 if (!ACCESS_FROM(element, opposite_direction))
10821 return MP_NO_ACTION; /* field not accessible from this direction */
10823 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
10824 return MP_NO_ACTION;
10826 if (IS_EM_GATE(element))
10828 if (!player->key[EM_GATE_NR(element)])
10829 return MP_NO_ACTION;
10831 else if (IS_EM_GATE_GRAY(element))
10833 if (!player->key[EM_GATE_GRAY_NR(element)])
10834 return MP_NO_ACTION;
10836 else if (IS_EM_GATE_GRAY_ACTIVE(element))
10838 if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
10839 return MP_NO_ACTION;
10841 else if (IS_SP_PORT(element))
10843 if (element == EL_SP_GRAVITY_PORT_LEFT ||
10844 element == EL_SP_GRAVITY_PORT_RIGHT ||
10845 element == EL_SP_GRAVITY_PORT_UP ||
10846 element == EL_SP_GRAVITY_PORT_DOWN)
10847 game.gravity = !game.gravity;
10848 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
10849 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
10850 element == EL_SP_GRAVITY_ON_PORT_UP ||
10851 element == EL_SP_GRAVITY_ON_PORT_DOWN)
10852 game.gravity = TRUE;
10853 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
10854 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
10855 element == EL_SP_GRAVITY_OFF_PORT_UP ||
10856 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
10857 game.gravity = FALSE;
10860 /* automatically move to the next field with double speed */
10861 player->programmed_action = move_direction;
10863 if (player->move_delay_reset_counter == 0)
10865 player->move_delay_reset_counter = 2; /* two double speed steps */
10867 DOUBLE_PLAYER_SPEED(player);
10870 PlayLevelSoundAction(x, y, ACTION_PASSING);
10872 else if (player_can_move_or_snap && IS_DIGGABLE(element))
10876 if (mode != DF_SNAP)
10878 GfxElement[x][y] = GFX_ELEMENT(element);
10879 player->is_digging = TRUE;
10882 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
10884 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
10885 player->index_bit, dig_side);
10887 if (mode == DF_SNAP)
10889 #if USE_NEW_SNAP_DELAY
10890 if (level.block_snap_field)
10891 setFieldForSnapping(x, y, element, move_direction);
10893 TestIfElementTouchesCustomElement(x, y); /* for empty space */
10895 TestIfElementTouchesCustomElement(x, y); /* for empty space */
10898 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
10899 player->index_bit, dig_side);
10902 else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
10906 if (is_player && mode != DF_SNAP)
10908 GfxElement[x][y] = element;
10909 player->is_collecting = TRUE;
10912 if (element == EL_SPEED_PILL)
10914 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
10916 else if (element == EL_EXTRA_TIME && level.time > 0)
10918 TimeLeft += level.extra_time;
10919 DrawGameValue_Time(TimeLeft);
10921 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
10923 player->shield_normal_time_left += level.shield_normal_time;
10924 if (element == EL_SHIELD_DEADLY)
10925 player->shield_deadly_time_left += level.shield_deadly_time;
10927 else if (element == EL_DYNAMITE || element == EL_SP_DISK_RED)
10929 if (player->inventory_size < MAX_INVENTORY_SIZE)
10930 player->inventory_element[player->inventory_size++] = element;
10932 DrawGameValue_Dynamite(local_player->inventory_size);
10934 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
10936 player->dynabomb_count++;
10937 player->dynabombs_left++;
10939 else if (element == EL_DYNABOMB_INCREASE_SIZE)
10941 player->dynabomb_size++;
10943 else if (element == EL_DYNABOMB_INCREASE_POWER)
10945 player->dynabomb_xl = TRUE;
10947 else if (IS_KEY(element))
10949 player->key[KEY_NR(element)] = TRUE;
10951 DrawGameValue_Keys(player->key);
10953 redraw_mask |= REDRAW_DOOR_1;
10955 else if (IS_ENVELOPE(element))
10957 player->show_envelope = element;
10959 else if (element == EL_EMC_LENSES)
10961 game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
10963 RedrawAllInvisibleElementsForLenses();
10965 else if (element == EL_EMC_MAGNIFIER)
10967 game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
10969 RedrawAllInvisibleElementsForMagnifier();
10971 else if (IS_DROPPABLE(element) ||
10972 IS_THROWABLE(element)) /* can be collected and dropped */
10976 if (collect_count == 0)
10977 player->inventory_infinite_element = element;
10979 for (i = 0; i < collect_count; i++)
10980 if (player->inventory_size < MAX_INVENTORY_SIZE)
10981 player->inventory_element[player->inventory_size++] = element;
10983 DrawGameValue_Dynamite(local_player->inventory_size);
10985 else if (collect_count > 0)
10987 local_player->gems_still_needed -= collect_count;
10988 if (local_player->gems_still_needed < 0)
10989 local_player->gems_still_needed = 0;
10991 DrawGameValue_Emeralds(local_player->gems_still_needed);
10994 RaiseScoreElement(element);
10995 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
10998 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
10999 player->index_bit, dig_side);
11001 if (mode == DF_SNAP)
11003 #if USE_NEW_SNAP_DELAY
11004 if (level.block_snap_field)
11005 setFieldForSnapping(x, y, element, move_direction);
11007 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11009 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11012 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
11013 player->index_bit, dig_side);
11016 else if (player_can_move_or_snap && IS_PUSHABLE(element))
11018 if (mode == DF_SNAP && element != EL_BD_ROCK)
11019 return MP_NO_ACTION;
11021 if (CAN_FALL(element) && dy)
11022 return MP_NO_ACTION;
11024 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
11025 !(element == EL_SPRING && level.use_spring_bug))
11026 return MP_NO_ACTION;
11028 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
11029 ((move_direction & MV_VERTICAL &&
11030 ((element_info[element].move_pattern & MV_LEFT &&
11031 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
11032 (element_info[element].move_pattern & MV_RIGHT &&
11033 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
11034 (move_direction & MV_HORIZONTAL &&
11035 ((element_info[element].move_pattern & MV_UP &&
11036 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
11037 (element_info[element].move_pattern & MV_DOWN &&
11038 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
11039 return MP_NO_ACTION;
11041 /* do not push elements already moving away faster than player */
11042 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
11043 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
11044 return MP_NO_ACTION;
11046 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
11048 if (player->push_delay_value == -1 || !player_was_pushing)
11049 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11051 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11053 if (player->push_delay_value == -1)
11054 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11056 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
11058 if (!player->is_pushing)
11059 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11062 player->is_pushing = TRUE;
11064 if (!(IN_LEV_FIELD(nextx, nexty) &&
11065 (IS_FREE(nextx, nexty) ||
11066 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
11067 IS_SB_ELEMENT(element)))))
11068 return MP_NO_ACTION;
11070 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
11071 return MP_NO_ACTION;
11073 if (player->push_delay == -1) /* new pushing; restart delay */
11074 player->push_delay = 0;
11076 if (player->push_delay < player->push_delay_value &&
11077 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
11078 element != EL_SPRING && element != EL_BALLOON)
11080 /* make sure that there is no move delay before next try to push */
11081 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11082 player->move_delay = 0;
11084 return MP_NO_ACTION;
11087 if (IS_SB_ELEMENT(element))
11089 if (element == EL_SOKOBAN_FIELD_FULL)
11091 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
11092 local_player->sokobanfields_still_needed++;
11095 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
11097 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
11098 local_player->sokobanfields_still_needed--;
11101 Feld[x][y] = EL_SOKOBAN_OBJECT;
11103 if (Back[x][y] == Back[nextx][nexty])
11104 PlayLevelSoundAction(x, y, ACTION_PUSHING);
11105 else if (Back[x][y] != 0)
11106 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
11109 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
11112 if (local_player->sokobanfields_still_needed == 0 &&
11113 game.emulation == EMU_SOKOBAN)
11115 player->LevelSolved = player->GameOver = TRUE;
11116 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
11120 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
11122 InitMovingField(x, y, move_direction);
11123 GfxAction[x][y] = ACTION_PUSHING;
11125 if (mode == DF_SNAP)
11126 ContinueMoving(x, y);
11128 MovPos[x][y] = (dx != 0 ? dx : dy);
11130 Pushed[x][y] = TRUE;
11131 Pushed[nextx][nexty] = TRUE;
11133 if (game.engine_version < VERSION_IDENT(2,2,0,7))
11134 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11136 player->push_delay_value = -1; /* get new value later */
11138 /* check for element change _after_ element has been pushed */
11139 if (game.use_change_when_pushing_bug)
11141 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
11142 player->index_bit, dig_side);
11143 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
11144 player->index_bit, dig_side);
11147 else if (IS_SWITCHABLE(element))
11149 if (PLAYER_SWITCHING(player, x, y))
11151 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
11152 player->index_bit, dig_side);
11157 player->is_switching = TRUE;
11158 player->switch_x = x;
11159 player->switch_y = y;
11161 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
11163 if (element == EL_ROBOT_WHEEL)
11165 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
11169 DrawLevelField(x, y);
11171 else if (element == EL_SP_TERMINAL)
11176 SCAN_PLAYFIELD(xx, yy)
11178 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
11181 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
11183 else if (Feld[xx][yy] == EL_SP_TERMINAL)
11184 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
11187 else if (IS_BELT_SWITCH(element))
11189 ToggleBeltSwitch(x, y);
11191 else if (element == EL_SWITCHGATE_SWITCH_UP ||
11192 element == EL_SWITCHGATE_SWITCH_DOWN)
11194 ToggleSwitchgateSwitch(x, y);
11196 else if (element == EL_LIGHT_SWITCH ||
11197 element == EL_LIGHT_SWITCH_ACTIVE)
11199 ToggleLightSwitch(x, y);
11201 else if (element == EL_TIMEGATE_SWITCH)
11203 ActivateTimegateSwitch(x, y);
11205 else if (element == EL_BALLOON_SWITCH_LEFT ||
11206 element == EL_BALLOON_SWITCH_RIGHT ||
11207 element == EL_BALLOON_SWITCH_UP ||
11208 element == EL_BALLOON_SWITCH_DOWN ||
11209 element == EL_BALLOON_SWITCH_NONE ||
11210 element == EL_BALLOON_SWITCH_ANY)
11212 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
11213 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
11214 element == EL_BALLOON_SWITCH_UP ? MV_UP :
11215 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
11216 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
11219 else if (element == EL_LAMP)
11221 Feld[x][y] = EL_LAMP_ACTIVE;
11222 local_player->lights_still_needed--;
11224 ResetGfxAnimation(x, y);
11225 DrawLevelField(x, y);
11227 else if (element == EL_TIME_ORB_FULL)
11229 Feld[x][y] = EL_TIME_ORB_EMPTY;
11231 if (level.time > 0 || level.use_time_orb_bug)
11233 TimeLeft += level.time_orb_time;
11234 DrawGameValue_Time(TimeLeft);
11237 ResetGfxAnimation(x, y);
11238 DrawLevelField(x, y);
11240 else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
11241 element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
11245 game.ball_state = !game.ball_state;
11248 SCAN_PLAYFIELD(xx, yy)
11250 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
11253 int e = Feld[xx][yy];
11255 if (game.ball_state)
11257 if (e == EL_EMC_MAGIC_BALL)
11258 CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
11259 else if (e == EL_EMC_MAGIC_BALL_SWITCH)
11260 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
11264 if (e == EL_EMC_MAGIC_BALL_ACTIVE)
11265 CreateField(xx, yy, EL_EMC_MAGIC_BALL);
11266 else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
11267 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
11272 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
11273 player->index_bit, dig_side);
11275 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
11276 player->index_bit, dig_side);
11278 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
11279 player->index_bit, dig_side);
11285 if (!PLAYER_SWITCHING(player, x, y))
11287 player->is_switching = TRUE;
11288 player->switch_x = x;
11289 player->switch_y = y;
11291 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
11292 player->index_bit, dig_side);
11293 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
11294 player->index_bit, dig_side);
11296 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
11297 player->index_bit, dig_side);
11298 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
11299 player->index_bit, dig_side);
11302 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
11303 player->index_bit, dig_side);
11304 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
11305 player->index_bit, dig_side);
11307 return MP_NO_ACTION;
11310 player->push_delay = -1;
11312 if (is_player) /* function can also be called by EL_PENGUIN */
11314 if (Feld[x][y] != element) /* really digged/collected something */
11315 player->is_collecting = !player->is_digging;
11321 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
11323 int jx = player->jx, jy = player->jy;
11324 int x = jx + dx, y = jy + dy;
11325 int snap_direction = (dx == -1 ? MV_LEFT :
11326 dx == +1 ? MV_RIGHT :
11328 dy == +1 ? MV_DOWN : MV_NONE);
11330 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
11333 if (!player->active || !IN_LEV_FIELD(x, y))
11341 if (player->MovPos == 0)
11342 player->is_pushing = FALSE;
11344 player->is_snapping = FALSE;
11346 if (player->MovPos == 0)
11348 player->is_moving = FALSE;
11349 player->is_digging = FALSE;
11350 player->is_collecting = FALSE;
11356 if (player->is_snapping)
11359 player->MovDir = snap_direction;
11361 if (player->MovPos == 0)
11363 player->is_moving = FALSE;
11364 player->is_digging = FALSE;
11365 player->is_collecting = FALSE;
11368 player->is_dropping = FALSE;
11370 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
11373 player->is_snapping = TRUE;
11375 if (player->MovPos == 0)
11377 player->is_moving = FALSE;
11378 player->is_digging = FALSE;
11379 player->is_collecting = FALSE;
11382 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
11383 DrawLevelField(player->last_jx, player->last_jy);
11385 DrawLevelField(x, y);
11390 boolean DropElement(struct PlayerInfo *player)
11392 int old_element, new_element;
11393 int dropx = player->jx, dropy = player->jy;
11394 int drop_direction = player->MovDir;
11395 int drop_side = drop_direction;
11396 int drop_element = (player->inventory_size > 0 ?
11397 player->inventory_element[player->inventory_size - 1] :
11398 player->inventory_infinite_element != EL_UNDEFINED ?
11399 player->inventory_infinite_element :
11400 player->dynabombs_left > 0 ?
11401 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
11404 /* do not drop an element on top of another element; when holding drop key
11405 pressed without moving, dropped element must move away before the next
11406 element can be dropped (this is especially important if the next element
11407 is dynamite, which can be placed on background for historical reasons) */
11408 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
11411 if (IS_THROWABLE(drop_element))
11413 dropx += GET_DX_FROM_DIR(drop_direction);
11414 dropy += GET_DY_FROM_DIR(drop_direction);
11416 if (!IN_LEV_FIELD(dropx, dropy))
11420 old_element = Feld[dropx][dropy]; /* old element at dropping position */
11421 new_element = drop_element; /* default: no change when dropping */
11423 /* check if player is active, not moving and ready to drop */
11424 if (!player->active || player->MovPos || player->drop_delay > 0)
11427 /* check if player has anything that can be dropped */
11428 if (new_element == EL_UNDEFINED)
11431 /* check if anything can be dropped at the current position */
11432 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
11435 /* collected custom elements can only be dropped on empty fields */
11436 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
11439 if (old_element != EL_EMPTY)
11440 Back[dropx][dropy] = old_element; /* store old element on this field */
11442 ResetGfxAnimation(dropx, dropy);
11443 ResetRandomAnimationValue(dropx, dropy);
11445 if (player->inventory_size > 0 ||
11446 player->inventory_infinite_element != EL_UNDEFINED)
11448 if (player->inventory_size > 0)
11450 player->inventory_size--;
11452 DrawGameValue_Dynamite(local_player->inventory_size);
11454 if (new_element == EL_DYNAMITE)
11455 new_element = EL_DYNAMITE_ACTIVE;
11456 else if (new_element == EL_SP_DISK_RED)
11457 new_element = EL_SP_DISK_RED_ACTIVE;
11460 Feld[dropx][dropy] = new_element;
11462 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
11463 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
11464 el2img(Feld[dropx][dropy]), 0);
11466 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
11468 /* needed if previous element just changed to "empty" in the last frame */
11469 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
11471 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
11472 player->index_bit, drop_side);
11473 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
11475 player->index_bit, drop_side);
11477 TestIfElementTouchesCustomElement(dropx, dropy);
11479 else /* player is dropping a dyna bomb */
11481 player->dynabombs_left--;
11483 Feld[dropx][dropy] = new_element;
11485 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
11486 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
11487 el2img(Feld[dropx][dropy]), 0);
11489 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
11492 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
11493 InitField_WithBug1(dropx, dropy, FALSE);
11495 new_element = Feld[dropx][dropy]; /* element might have changed */
11497 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
11498 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
11500 int move_direction, nextx, nexty;
11502 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
11503 MovDir[dropx][dropy] = drop_direction;
11505 move_direction = MovDir[dropx][dropy];
11506 nextx = dropx + GET_DX_FROM_DIR(move_direction);
11507 nexty = dropy + GET_DY_FROM_DIR(move_direction);
11509 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
11510 CheckCollision[dropx][dropy] = 2;
11513 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
11514 player->is_dropping = TRUE;
11516 player->drop_x = dropx;
11517 player->drop_y = dropy;
11522 /* ------------------------------------------------------------------------- */
11523 /* game sound playing functions */
11524 /* ------------------------------------------------------------------------- */
11526 static int *loop_sound_frame = NULL;
11527 static int *loop_sound_volume = NULL;
11529 void InitPlayLevelSound()
11531 int num_sounds = getSoundListSize();
11533 checked_free(loop_sound_frame);
11534 checked_free(loop_sound_volume);
11536 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
11537 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
11540 static void PlayLevelSound(int x, int y, int nr)
11542 int sx = SCREENX(x), sy = SCREENY(y);
11543 int volume, stereo_position;
11544 int max_distance = 8;
11545 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
11547 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
11548 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
11551 if (!IN_LEV_FIELD(x, y) ||
11552 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
11553 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
11556 volume = SOUND_MAX_VOLUME;
11558 if (!IN_SCR_FIELD(sx, sy))
11560 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
11561 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
11563 volume -= volume * (dx > dy ? dx : dy) / max_distance;
11566 stereo_position = (SOUND_MAX_LEFT +
11567 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
11568 (SCR_FIELDX + 2 * max_distance));
11570 if (IS_LOOP_SOUND(nr))
11572 /* This assures that quieter loop sounds do not overwrite louder ones,
11573 while restarting sound volume comparison with each new game frame. */
11575 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
11578 loop_sound_volume[nr] = volume;
11579 loop_sound_frame[nr] = FrameCounter;
11582 PlaySoundExt(nr, volume, stereo_position, type);
11585 static void PlayLevelSoundNearest(int x, int y, int sound_action)
11587 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
11588 x > LEVELX(BX2) ? LEVELX(BX2) : x,
11589 y < LEVELY(BY1) ? LEVELY(BY1) :
11590 y > LEVELY(BY2) ? LEVELY(BY2) : y,
11594 static void PlayLevelSoundAction(int x, int y, int action)
11596 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
11599 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
11601 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
11603 if (sound_effect != SND_UNDEFINED)
11604 PlayLevelSound(x, y, sound_effect);
11607 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
11610 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
11612 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
11613 PlayLevelSound(x, y, sound_effect);
11616 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
11618 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
11620 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
11621 PlayLevelSound(x, y, sound_effect);
11624 static void StopLevelSoundActionIfLoop(int x, int y, int action)
11626 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
11628 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
11629 StopSound(sound_effect);
11632 static void PlayLevelMusic()
11634 if (levelset.music[level_nr] != MUS_UNDEFINED)
11635 PlayMusic(levelset.music[level_nr]); /* from config file */
11637 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
11640 void PlayLevelSound_EM(int x, int y, int element_em, int sample)
11642 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
11647 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
11651 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
11655 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
11659 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
11663 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
11667 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
11671 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
11674 case SAMPLE_android_clone:
11675 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
11678 case SAMPLE_android_move:
11679 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
11682 case SAMPLE_spring:
11683 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
11687 PlayLevelSoundElementAction(x, y, element, ACTION_SLURPED_BY_SPRING);
11691 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
11694 case SAMPLE_eater_eat:
11695 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
11699 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
11702 case SAMPLE_collect:
11703 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
11706 case SAMPLE_diamond:
11707 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
11710 case SAMPLE_squash:
11711 /* !!! CHECK THIS !!! */
11713 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
11715 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
11719 case SAMPLE_wonderfall:
11720 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
11724 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
11728 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
11732 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
11736 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
11740 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
11744 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
11747 case SAMPLE_wonder:
11748 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
11752 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
11755 case SAMPLE_exit_open:
11756 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
11759 case SAMPLE_exit_leave:
11760 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
11763 case SAMPLE_dynamite:
11764 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
11768 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
11772 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
11776 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
11780 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
11784 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
11788 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
11792 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
11797 void RaiseScore(int value)
11799 local_player->score += value;
11801 DrawGameValue_Score(local_player->score);
11804 void RaiseScoreElement(int element)
11809 case EL_BD_DIAMOND:
11810 case EL_EMERALD_YELLOW:
11811 case EL_EMERALD_RED:
11812 case EL_EMERALD_PURPLE:
11813 case EL_SP_INFOTRON:
11814 RaiseScore(level.score[SC_EMERALD]);
11817 RaiseScore(level.score[SC_DIAMOND]);
11820 RaiseScore(level.score[SC_CRYSTAL]);
11823 RaiseScore(level.score[SC_PEARL]);
11826 case EL_BD_BUTTERFLY:
11827 case EL_SP_ELECTRON:
11828 RaiseScore(level.score[SC_BUG]);
11831 case EL_BD_FIREFLY:
11832 case EL_SP_SNIKSNAK:
11833 RaiseScore(level.score[SC_SPACESHIP]);
11836 case EL_DARK_YAMYAM:
11837 RaiseScore(level.score[SC_YAMYAM]);
11840 RaiseScore(level.score[SC_ROBOT]);
11843 RaiseScore(level.score[SC_PACMAN]);
11846 RaiseScore(level.score[SC_NUT]);
11849 case EL_SP_DISK_RED:
11850 case EL_DYNABOMB_INCREASE_NUMBER:
11851 case EL_DYNABOMB_INCREASE_SIZE:
11852 case EL_DYNABOMB_INCREASE_POWER:
11853 RaiseScore(level.score[SC_DYNAMITE]);
11855 case EL_SHIELD_NORMAL:
11856 case EL_SHIELD_DEADLY:
11857 RaiseScore(level.score[SC_SHIELD]);
11859 case EL_EXTRA_TIME:
11860 RaiseScore(level.extra_time_score);
11874 RaiseScore(level.score[SC_KEY]);
11877 RaiseScore(element_info[element].collect_score);
11882 void RequestQuitGame(boolean ask_if_really_quit)
11884 if (AllPlayersGone ||
11885 !ask_if_really_quit ||
11886 level_editor_test_game ||
11887 Request("Do you really want to quit the game ?",
11888 REQ_ASK | REQ_STAY_CLOSED))
11890 #if defined(NETWORK_AVALIABLE)
11891 if (options.network)
11892 SendToServer_StopPlaying();
11896 game_status = GAME_MODE_MAIN;
11902 if (tape.playing && tape.deactivate_display)
11903 TapeDeactivateDisplayOff(TRUE);
11905 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
11907 if (tape.playing && tape.deactivate_display)
11908 TapeDeactivateDisplayOn();
11913 /* ---------- new game button stuff ---------------------------------------- */
11915 /* graphic position values for game buttons */
11916 #define GAME_BUTTON_XSIZE 30
11917 #define GAME_BUTTON_YSIZE 30
11918 #define GAME_BUTTON_XPOS 5
11919 #define GAME_BUTTON_YPOS 215
11920 #define SOUND_BUTTON_XPOS 5
11921 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
11923 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
11924 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
11925 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
11926 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
11927 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
11928 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
11935 } gamebutton_info[NUM_GAME_BUTTONS] =
11938 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
11943 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
11944 GAME_CTRL_ID_PAUSE,
11948 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
11953 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
11954 SOUND_CTRL_ID_MUSIC,
11955 "background music on/off"
11958 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
11959 SOUND_CTRL_ID_LOOPS,
11960 "sound loops on/off"
11963 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
11964 SOUND_CTRL_ID_SIMPLE,
11965 "normal sounds on/off"
11969 void CreateGameButtons()
11973 for (i = 0; i < NUM_GAME_BUTTONS; i++)
11975 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
11976 struct GadgetInfo *gi;
11979 unsigned long event_mask;
11980 int gd_xoffset, gd_yoffset;
11981 int gd_x1, gd_x2, gd_y1, gd_y2;
11984 gd_xoffset = gamebutton_info[i].x;
11985 gd_yoffset = gamebutton_info[i].y;
11986 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
11987 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
11989 if (id == GAME_CTRL_ID_STOP ||
11990 id == GAME_CTRL_ID_PAUSE ||
11991 id == GAME_CTRL_ID_PLAY)
11993 button_type = GD_TYPE_NORMAL_BUTTON;
11995 event_mask = GD_EVENT_RELEASED;
11996 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
11997 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12001 button_type = GD_TYPE_CHECK_BUTTON;
12003 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
12004 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
12005 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
12006 event_mask = GD_EVENT_PRESSED;
12007 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
12008 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12011 gi = CreateGadget(GDI_CUSTOM_ID, id,
12012 GDI_INFO_TEXT, gamebutton_info[i].infotext,
12013 GDI_X, DX + gd_xoffset,
12014 GDI_Y, DY + gd_yoffset,
12015 GDI_WIDTH, GAME_BUTTON_XSIZE,
12016 GDI_HEIGHT, GAME_BUTTON_YSIZE,
12017 GDI_TYPE, button_type,
12018 GDI_STATE, GD_BUTTON_UNPRESSED,
12019 GDI_CHECKED, checked,
12020 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
12021 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
12022 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
12023 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
12024 GDI_EVENT_MASK, event_mask,
12025 GDI_CALLBACK_ACTION, HandleGameButtons,
12029 Error(ERR_EXIT, "cannot create gadget");
12031 game_gadget[id] = gi;
12035 void FreeGameButtons()
12039 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12040 FreeGadget(game_gadget[i]);
12043 static void MapGameButtons()
12047 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12048 MapGadget(game_gadget[i]);
12051 void UnmapGameButtons()
12055 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12056 UnmapGadget(game_gadget[i]);
12059 static void HandleGameButtons(struct GadgetInfo *gi)
12061 int id = gi->custom_id;
12063 if (game_status != GAME_MODE_PLAYING)
12068 case GAME_CTRL_ID_STOP:
12069 RequestQuitGame(TRUE);
12072 case GAME_CTRL_ID_PAUSE:
12073 if (options.network)
12075 #if defined(NETWORK_AVALIABLE)
12077 SendToServer_ContinuePlaying();
12079 SendToServer_PausePlaying();
12083 TapeTogglePause(TAPE_TOGGLE_MANUAL);
12086 case GAME_CTRL_ID_PLAY:
12089 #if defined(NETWORK_AVALIABLE)
12090 if (options.network)
12091 SendToServer_ContinuePlaying();
12095 tape.pausing = FALSE;
12096 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
12101 case SOUND_CTRL_ID_MUSIC:
12102 if (setup.sound_music)
12104 setup.sound_music = FALSE;
12107 else if (audio.music_available)
12109 setup.sound = setup.sound_music = TRUE;
12111 SetAudioMode(setup.sound);
12117 case SOUND_CTRL_ID_LOOPS:
12118 if (setup.sound_loops)
12119 setup.sound_loops = FALSE;
12120 else if (audio.loops_available)
12122 setup.sound = setup.sound_loops = TRUE;
12123 SetAudioMode(setup.sound);
12127 case SOUND_CTRL_ID_SIMPLE:
12128 if (setup.sound_simple)
12129 setup.sound_simple = FALSE;
12130 else if (audio.sound_available)
12132 setup.sound = setup.sound_simple = TRUE;
12133 SetAudioMode(setup.sound);