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);
296 static void ExecuteCustomElementAction(int, int, int, int);
297 static boolean ChangeElement(int, int, int, int);
299 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
300 #define CheckTriggeredElementChange(x, y, e, ev) \
301 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
302 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s) \
303 CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
304 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s) \
305 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
306 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p) \
307 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
309 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
310 #define CheckElementChange(x, y, e, te, ev) \
311 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
312 #define CheckElementChangeByPlayer(x, y, e, ev, p, s) \
313 CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
314 #define CheckElementChangeBySide(x, y, e, te, ev, s) \
315 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
317 static void PlayLevelSound(int, int, int);
318 static void PlayLevelSoundNearest(int, int, int);
319 static void PlayLevelSoundAction(int, int, int);
320 static void PlayLevelSoundElementAction(int, int, int, int);
321 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
322 static void PlayLevelSoundActionIfLoop(int, int, int);
323 static void StopLevelSoundActionIfLoop(int, int, int);
324 static void PlayLevelMusic();
326 static void MapGameButtons();
327 static void HandleGameButtons(struct GadgetInfo *);
329 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
332 /* ------------------------------------------------------------------------- */
333 /* definition of elements that automatically change to other elements after */
334 /* a specified time, eventually calling a function when changing */
335 /* ------------------------------------------------------------------------- */
337 /* forward declaration for changer functions */
338 static void InitBuggyBase(int, int);
339 static void WarnBuggyBase(int, int);
341 static void InitTrap(int, int);
342 static void ActivateTrap(int, int);
343 static void ChangeActiveTrap(int, int);
345 static void InitRobotWheel(int, int);
346 static void RunRobotWheel(int, int);
347 static void StopRobotWheel(int, int);
349 static void InitTimegateWheel(int, int);
350 static void RunTimegateWheel(int, int);
352 static void InitMagicBallDelay(int, int);
353 static void ActivateMagicBall(int, int);
355 static void InitDiagonalMovingElement(int, int);
357 struct ChangingElementInfo
362 void (*pre_change_function)(int x, int y);
363 void (*change_function)(int x, int y);
364 void (*post_change_function)(int x, int y);
367 static struct ChangingElementInfo change_delay_list[] =
418 EL_SWITCHGATE_OPENING,
426 EL_SWITCHGATE_CLOSING,
427 EL_SWITCHGATE_CLOSED,
459 EL_ACID_SPLASH_RIGHT,
468 EL_SP_BUGGY_BASE_ACTIVATING,
475 EL_SP_BUGGY_BASE_ACTIVATING,
476 EL_SP_BUGGY_BASE_ACTIVE,
483 EL_SP_BUGGY_BASE_ACTIVE,
507 EL_ROBOT_WHEEL_ACTIVE,
515 EL_TIMEGATE_SWITCH_ACTIVE,
523 EL_EMC_MAGIC_BALL_ACTIVE,
524 EL_EMC_MAGIC_BALL_ACTIVE,
531 EL_EMC_SPRING_BUMPER_ACTIVE,
532 EL_EMC_SPRING_BUMPER,
539 EL_DIAGONAL_SHRINKING,
552 InitDiagonalMovingElement
568 int push_delay_fixed, push_delay_random;
573 { EL_BALLOON, 0, 0 },
575 { EL_SOKOBAN_OBJECT, 2, 0 },
576 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
577 { EL_SATELLITE, 2, 0 },
578 { EL_SP_DISK_YELLOW, 2, 0 },
580 { EL_UNDEFINED, 0, 0 },
588 move_stepsize_list[] =
590 { EL_AMOEBA_DROP, 2 },
591 { EL_AMOEBA_DROPPING, 2 },
592 { EL_QUICKSAND_FILLING, 1 },
593 { EL_QUICKSAND_EMPTYING, 1 },
594 { EL_MAGIC_WALL_FILLING, 2 },
595 { EL_BD_MAGIC_WALL_FILLING, 2 },
596 { EL_MAGIC_WALL_EMPTYING, 2 },
597 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
607 collect_count_list[] =
610 { EL_BD_DIAMOND, 1 },
611 { EL_EMERALD_YELLOW, 1 },
612 { EL_EMERALD_RED, 1 },
613 { EL_EMERALD_PURPLE, 1 },
615 { EL_SP_INFOTRON, 1 },
627 access_direction_list[] =
629 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
630 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
631 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
632 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
633 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
634 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
635 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
636 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
637 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
638 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
639 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
641 { EL_SP_PORT_LEFT, MV_RIGHT },
642 { EL_SP_PORT_RIGHT, MV_LEFT },
643 { EL_SP_PORT_UP, MV_DOWN },
644 { EL_SP_PORT_DOWN, MV_UP },
645 { EL_SP_PORT_HORIZONTAL, MV_LEFT | MV_RIGHT },
646 { EL_SP_PORT_VERTICAL, MV_UP | MV_DOWN },
647 { EL_SP_PORT_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
648 { EL_SP_GRAVITY_PORT_LEFT, MV_RIGHT },
649 { EL_SP_GRAVITY_PORT_RIGHT, MV_LEFT },
650 { EL_SP_GRAVITY_PORT_UP, MV_DOWN },
651 { EL_SP_GRAVITY_PORT_DOWN, MV_UP },
652 { EL_SP_GRAVITY_ON_PORT_LEFT, MV_RIGHT },
653 { EL_SP_GRAVITY_ON_PORT_RIGHT, MV_LEFT },
654 { EL_SP_GRAVITY_ON_PORT_UP, MV_DOWN },
655 { EL_SP_GRAVITY_ON_PORT_DOWN, MV_UP },
656 { EL_SP_GRAVITY_OFF_PORT_LEFT, MV_RIGHT },
657 { EL_SP_GRAVITY_OFF_PORT_RIGHT, MV_LEFT },
658 { EL_SP_GRAVITY_OFF_PORT_UP, MV_DOWN },
659 { EL_SP_GRAVITY_OFF_PORT_DOWN, MV_UP },
661 { EL_UNDEFINED, MV_NONE }
664 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
666 #define IS_AUTO_CHANGING(e) (element_info[e].has_change_event[CE_DELAY])
667 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
668 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
669 IS_JUST_CHANGING(x, y))
671 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
673 /* static variables for playfield scan mode (scanning forward or backward) */
674 static int playfield_scan_start_x = 0;
675 static int playfield_scan_start_y = 0;
676 static int playfield_scan_delta_x = 1;
677 static int playfield_scan_delta_y = 1;
679 #define SCAN_PLAYFIELD(x, y) for ((y) = playfield_scan_start_y; \
680 (y) >= 0 && (y) <= lev_fieldy - 1; \
681 (y) += playfield_scan_delta_y) \
682 for ((x) = playfield_scan_start_x; \
683 (x) >= 0 && (x) <= lev_fieldx - 1; \
684 (x) += playfield_scan_delta_x) \
686 static void InitPlayfieldScanModeVars()
688 if (game.use_reverse_scan_direction)
690 playfield_scan_start_x = lev_fieldx - 1;
691 playfield_scan_start_y = lev_fieldy - 1;
693 playfield_scan_delta_x = -1;
694 playfield_scan_delta_y = -1;
698 playfield_scan_start_x = 0;
699 playfield_scan_start_y = 0;
701 playfield_scan_delta_x = 1;
702 playfield_scan_delta_y = 1;
706 static void InitPlayfieldScanMode(int mode)
708 game.use_reverse_scan_direction =
709 (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
711 InitPlayfieldScanModeVars();
714 static int get_move_delay_from_stepsize(int move_stepsize)
717 MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
719 /* make sure that stepsize value is always a power of 2 */
720 move_stepsize = (1 << log_2(move_stepsize));
722 return TILEX / move_stepsize;
725 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
728 int move_delay = get_move_delay_from_stepsize(move_stepsize);
729 boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
731 /* do no immediately change move delay -- the player might just be moving */
732 player->move_delay_value_next = move_delay;
734 /* information if player can move must be set separately */
735 player->cannot_move = cannot_move;
739 player->move_delay = game.initial_move_delay;
740 player->move_delay_value = game.initial_move_delay_value;
742 player->move_delay_value_next = -1;
744 player->move_delay_reset_counter = 0;
748 void GetPlayerConfig()
750 if (!audio.sound_available)
751 setup.sound_simple = FALSE;
753 if (!audio.loops_available)
754 setup.sound_loops = FALSE;
756 if (!audio.music_available)
757 setup.sound_music = FALSE;
759 if (!video.fullscreen_available)
760 setup.fullscreen = FALSE;
762 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
764 SetAudioMode(setup.sound);
768 static int getBeltNrFromBeltElement(int element)
770 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
771 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
772 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
775 static int getBeltNrFromBeltActiveElement(int element)
777 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
778 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
779 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
782 static int getBeltNrFromBeltSwitchElement(int element)
784 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
785 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
786 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
789 static int getBeltDirNrFromBeltSwitchElement(int element)
791 static int belt_base_element[4] =
793 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
794 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
795 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
796 EL_CONVEYOR_BELT_4_SWITCH_LEFT
799 int belt_nr = getBeltNrFromBeltSwitchElement(element);
800 int belt_dir_nr = element - belt_base_element[belt_nr];
802 return (belt_dir_nr % 3);
805 static int getBeltDirFromBeltSwitchElement(int element)
807 static int belt_move_dir[3] =
814 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
816 return belt_move_dir[belt_dir_nr];
819 static void InitPlayerField(int x, int y, int element, boolean init_game)
821 if (element == EL_SP_MURPHY)
825 if (stored_player[0].present)
827 Feld[x][y] = EL_SP_MURPHY_CLONE;
833 stored_player[0].use_murphy = TRUE;
836 Feld[x][y] = EL_PLAYER_1;
842 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
843 int jx = player->jx, jy = player->jy;
845 player->present = TRUE;
847 player->block_last_field = (element == EL_SP_MURPHY ?
848 level.sp_block_last_field :
849 level.block_last_field);
851 /* ---------- initialize player's last field block delay --------------- */
853 /* always start with reliable default value (no adjustment needed) */
854 player->block_delay_adjustment = 0;
856 /* special case 1: in Supaplex, Murphy blocks last field one more frame */
857 if (player->block_last_field && element == EL_SP_MURPHY)
858 player->block_delay_adjustment = 1;
860 /* special case 2: in game engines before 3.1.1, blocking was different */
861 if (game.use_block_last_field_bug)
862 player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
864 if (!options.network || player->connected)
866 player->active = TRUE;
868 /* remove potentially duplicate players */
869 if (StorePlayer[jx][jy] == Feld[x][y])
870 StorePlayer[jx][jy] = 0;
872 StorePlayer[x][y] = Feld[x][y];
876 printf("Player %d activated.\n", player->element_nr);
877 printf("[Local player is %d and currently %s.]\n",
878 local_player->element_nr,
879 local_player->active ? "active" : "not active");
883 Feld[x][y] = EL_EMPTY;
885 player->jx = player->last_jx = x;
886 player->jy = player->last_jy = y;
890 static void InitField(int x, int y, boolean init_game)
892 int element = Feld[x][y];
901 InitPlayerField(x, y, element, init_game);
904 case EL_SOKOBAN_FIELD_PLAYER:
905 element = Feld[x][y] = EL_PLAYER_1;
906 InitField(x, y, init_game);
908 element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
909 InitField(x, y, init_game);
912 case EL_SOKOBAN_FIELD_EMPTY:
913 local_player->sokobanfields_still_needed++;
917 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
918 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
919 else if (x > 0 && Feld[x-1][y] == EL_ACID)
920 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
921 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
922 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
923 else if (y > 0 && Feld[x][y-1] == EL_ACID)
924 Feld[x][y] = EL_ACID_POOL_BOTTOM;
925 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
926 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
934 case EL_SPACESHIP_RIGHT:
935 case EL_SPACESHIP_UP:
936 case EL_SPACESHIP_LEFT:
937 case EL_SPACESHIP_DOWN:
939 case EL_BD_BUTTERFLY_RIGHT:
940 case EL_BD_BUTTERFLY_UP:
941 case EL_BD_BUTTERFLY_LEFT:
942 case EL_BD_BUTTERFLY_DOWN:
943 case EL_BD_BUTTERFLY:
944 case EL_BD_FIREFLY_RIGHT:
945 case EL_BD_FIREFLY_UP:
946 case EL_BD_FIREFLY_LEFT:
947 case EL_BD_FIREFLY_DOWN:
949 case EL_PACMAN_RIGHT:
973 if (y == lev_fieldy - 1)
975 Feld[x][y] = EL_AMOEBA_GROWING;
976 Store[x][y] = EL_AMOEBA_WET;
980 case EL_DYNAMITE_ACTIVE:
981 case EL_SP_DISK_RED_ACTIVE:
982 case EL_DYNABOMB_PLAYER_1_ACTIVE:
983 case EL_DYNABOMB_PLAYER_2_ACTIVE:
984 case EL_DYNABOMB_PLAYER_3_ACTIVE:
985 case EL_DYNABOMB_PLAYER_4_ACTIVE:
990 local_player->lights_still_needed++;
994 local_player->friends_still_needed++;
999 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1002 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1003 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1004 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1005 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1006 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1007 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1008 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1009 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1010 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1011 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1012 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1013 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1016 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1017 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1018 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1020 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
1022 game.belt_dir[belt_nr] = belt_dir;
1023 game.belt_dir_nr[belt_nr] = belt_dir_nr;
1025 else /* more than one switch -- set it like the first switch */
1027 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1032 case EL_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
1034 Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
1037 case EL_LIGHT_SWITCH_ACTIVE:
1039 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1042 case EL_EMC_MAGIC_BALL:
1043 if (game.ball_state)
1044 Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1047 case EL_EMC_MAGIC_BALL_SWITCH:
1048 if (game.ball_state)
1049 Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1054 if (IS_CUSTOM_ELEMENT(element))
1056 if (CAN_MOVE(element))
1059 #if USE_NEW_CUSTOM_VALUE
1060 if (!element_info[element].use_last_ce_value || init_game)
1061 CustomValue[x][y] = GET_NEW_CUSTOM_VALUE(Feld[x][y]);
1065 if (IS_CUSTOM_ELEMENT(element) && CAN_MOVE(element))
1068 else if (IS_GROUP_ELEMENT(element))
1070 struct ElementGroupInfo *group = element_info[element].group;
1071 int last_anim_random_frame = gfx.anim_random_frame;
1074 if (group->choice_mode == ANIM_RANDOM)
1075 gfx.anim_random_frame = RND(group->num_elements_resolved);
1077 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1078 group->choice_mode, 0,
1081 if (group->choice_mode == ANIM_RANDOM)
1082 gfx.anim_random_frame = last_anim_random_frame;
1084 group->choice_pos++;
1086 Feld[x][y] = group->element_resolved[element_pos];
1088 InitField(x, y, init_game);
1095 CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1100 #if USE_NEW_CUSTOM_VALUE
1103 CustomValue[x][y] = GET_NEW_CUSTOM_VALUE(Feld[x][y]);
1105 CustomValue[x][y] = element_info[Feld[x][y]].custom_value_initial;
1113 static inline void InitField_WithBug1(int x, int y, boolean init_game)
1115 InitField(x, y, init_game);
1117 /* not needed to call InitMovDir() -- already done by InitField()! */
1118 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1119 CAN_MOVE(Feld[x][y]))
1123 static inline void InitField_WithBug2(int x, int y, boolean init_game)
1125 int old_element = Feld[x][y];
1127 InitField(x, y, init_game);
1129 /* not needed to call InitMovDir() -- already done by InitField()! */
1130 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1131 CAN_MOVE(old_element) &&
1132 (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1135 /* this case is in fact a combination of not less than three bugs:
1136 first, it calls InitMovDir() for elements that can move, although this is
1137 already done by InitField(); then, it checks the element that was at this
1138 field _before_ the call to InitField() (which can change it); lastly, it
1139 was not called for "mole with direction" elements, which were treated as
1140 "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1144 inline void DrawGameValue_Emeralds(int value)
1146 DrawText(DX_EMERALDS, DY_EMERALDS, int2str(value, 3), FONT_TEXT_2);
1149 inline void DrawGameValue_Dynamite(int value)
1151 DrawText(DX_DYNAMITE, DY_DYNAMITE, int2str(value, 3), FONT_TEXT_2);
1154 inline void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
1158 /* currently only 4 of 8 possible keys are displayed */
1159 for (i = 0; i < STD_NUM_KEYS; i++)
1162 DrawMiniGraphicExt(drawto, DX_KEYS + i * MINI_TILEX, DY_KEYS,
1163 el2edimg(EL_KEY_1 + i));
1165 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
1166 DOOR_GFX_PAGEX5 + XX_KEYS + i * MINI_TILEX, YY_KEYS,
1167 MINI_TILEX, MINI_TILEY, DX_KEYS + i * MINI_TILEX, DY_KEYS);
1171 inline void DrawGameValue_Score(int value)
1173 DrawText(DX_SCORE, DY_SCORE, int2str(value, 5), FONT_TEXT_2);
1176 inline void DrawGameValue_Time(int value)
1179 DrawText(DX_TIME1, DY_TIME, int2str(value, 3), FONT_TEXT_2);
1181 DrawText(DX_TIME2, DY_TIME, int2str(value, 4), FONT_LEVEL_NUMBER);
1184 inline void DrawGameValue_Level(int value)
1187 DrawText(DX_LEVEL, DY_LEVEL, int2str(value, 2), FONT_TEXT_2);
1190 /* misuse area for displaying emeralds to draw bigger level number */
1191 DrawTextExt(drawto, DX_EMERALDS, DY_EMERALDS,
1192 int2str(value, 3), FONT_LEVEL_NUMBER, BLIT_OPAQUE);
1194 /* now copy it to the area for displaying level number */
1195 BlitBitmap(drawto, drawto,
1196 DX_EMERALDS, DY_EMERALDS + 1,
1197 getFontWidth(FONT_LEVEL_NUMBER) * 3,
1198 getFontHeight(FONT_LEVEL_NUMBER) - 1,
1199 DX_LEVEL - 1, DY_LEVEL + 1);
1201 /* restore the area for displaying emeralds */
1202 DrawGameValue_Emeralds(local_player->gems_still_needed);
1204 /* yes, this is all really ugly :-) */
1208 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
1211 int key[MAX_NUM_KEYS];
1214 for (i = 0; i < MAX_NUM_KEYS; i++)
1215 key[i] = key_bits & (1 << i);
1217 DrawGameValue_Level(level_nr);
1219 DrawGameValue_Emeralds(emeralds);
1220 DrawGameValue_Dynamite(dynamite);
1221 DrawGameValue_Score(score);
1222 DrawGameValue_Time(time);
1224 DrawGameValue_Keys(key);
1227 void DrawGameDoorValues()
1231 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
1233 DrawGameDoorValues_EM();
1238 DrawGameValue_Level(level_nr);
1240 DrawGameValue_Emeralds(local_player->gems_still_needed);
1241 DrawGameValue_Dynamite(local_player->inventory_size);
1242 DrawGameValue_Score(local_player->score);
1243 DrawGameValue_Time(TimeLeft);
1245 for (i = 0; i < MAX_PLAYERS; i++)
1246 DrawGameValue_Keys(stored_player[i].key);
1250 static void resolve_group_element(int group_element, int recursion_depth)
1252 static int group_nr;
1253 static struct ElementGroupInfo *group;
1254 struct ElementGroupInfo *actual_group = element_info[group_element].group;
1257 if (recursion_depth > NUM_GROUP_ELEMENTS) /* recursion too deep */
1259 Error(ERR_WARN, "recursion too deep when resolving group element %d",
1260 group_element - EL_GROUP_START + 1);
1262 /* replace element which caused too deep recursion by question mark */
1263 group->element_resolved[group->num_elements_resolved++] = EL_UNKNOWN;
1268 if (recursion_depth == 0) /* initialization */
1270 group = element_info[group_element].group;
1271 group_nr = group_element - EL_GROUP_START;
1273 group->num_elements_resolved = 0;
1274 group->choice_pos = 0;
1277 for (i = 0; i < actual_group->num_elements; i++)
1279 int element = actual_group->element[i];
1281 if (group->num_elements_resolved == NUM_FILE_ELEMENTS)
1284 if (IS_GROUP_ELEMENT(element))
1285 resolve_group_element(element, recursion_depth + 1);
1288 group->element_resolved[group->num_elements_resolved++] = element;
1289 element_info[element].in_group[group_nr] = TRUE;
1296 =============================================================================
1298 -----------------------------------------------------------------------------
1299 initialize game engine due to level / tape version number
1300 =============================================================================
1303 static void InitGameEngine()
1305 int i, j, k, l, x, y;
1307 /* set game engine from tape file when re-playing, else from level file */
1308 game.engine_version = (tape.playing ? tape.engine_version :
1309 level.game_version);
1311 /* ---------------------------------------------------------------------- */
1312 /* set flags for bugs and changes according to active game engine version */
1313 /* ---------------------------------------------------------------------- */
1316 Summary of bugfix/change:
1317 Fixed handling for custom elements that change when pushed by the player.
1319 Fixed/changed in version:
1323 Before 3.1.0, custom elements that "change when pushing" changed directly
1324 after the player started pushing them (until then handled in "DigField()").
1325 Since 3.1.0, these custom elements are not changed until the "pushing"
1326 move of the element is finished (now handled in "ContinueMoving()").
1328 Affected levels/tapes:
1329 The first condition is generally needed for all levels/tapes before version
1330 3.1.0, which might use the old behaviour before it was changed; known tapes
1331 that are affected are some tapes from the level set "Walpurgis Gardens" by
1333 The second condition is an exception from the above case and is needed for
1334 the special case of tapes recorded with game (not engine!) version 3.1.0 or
1335 above (including some development versions of 3.1.0), but before it was
1336 known that this change would break tapes like the above and was fixed in
1337 3.1.1, so that the changed behaviour was active although the engine version
1338 while recording maybe was before 3.1.0. There is at least one tape that is
1339 affected by this exception, which is the tape for the one-level set "Bug
1340 Machine" by Juergen Bonhagen.
1343 game.use_change_when_pushing_bug =
1344 (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1346 tape.game_version >= VERSION_IDENT(3,1,0,0) &&
1347 tape.game_version < VERSION_IDENT(3,1,1,0)));
1350 Summary of bugfix/change:
1351 Fixed handling for blocking the field the player leaves when moving.
1353 Fixed/changed in version:
1357 Before 3.1.1, when "block last field when moving" was enabled, the field
1358 the player is leaving when moving was blocked for the time of the move,
1359 and was directly unblocked afterwards. This resulted in the last field
1360 being blocked for exactly one less than the number of frames of one player
1361 move. Additionally, even when blocking was disabled, the last field was
1362 blocked for exactly one frame.
1363 Since 3.1.1, due to changes in player movement handling, the last field
1364 is not blocked at all when blocking is disabled. When blocking is enabled,
1365 the last field is blocked for exactly the number of frames of one player
1366 move. Additionally, if the player is Murphy, the hero of Supaplex, the
1367 last field is blocked for exactly one more than the number of frames of
1370 Affected levels/tapes:
1371 (!!! yet to be determined -- probably many !!!)
1374 game.use_block_last_field_bug =
1375 (game.engine_version < VERSION_IDENT(3,1,1,0));
1378 Summary of bugfix/change:
1379 Changed behaviour of CE changes with multiple changes per single frame.
1381 Fixed/changed in version:
1385 Before 3.2.0-6, only one single CE change was allowed in each engine frame.
1386 This resulted in race conditions where CEs seem to behave strange in some
1387 situations (where triggered CE changes were just skipped because there was
1388 already a CE change on that tile in the playfield in that engine frame).
1389 Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
1390 (The number of changes per frame must be limited in any case, because else
1391 it is easily possible to define CE changes that would result in an infinite
1392 loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
1393 should be set large enough so that it would only be reached in cases where
1394 the corresponding CE change conditions run into a loop. Therefore, it seems
1395 to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
1396 maximal number of change pages for custom elements.)
1398 Affected levels/tapes:
1402 #if USE_ONLY_ONE_CHANGE_PER_FRAME
1403 game.max_num_changes_per_frame = 1;
1405 game.max_num_changes_per_frame =
1406 (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
1409 /* ---------------------------------------------------------------------- */
1411 /* default scan direction: scan playfield from top/left to bottom/right */
1412 InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
1414 /* dynamically adjust element properties according to game engine version */
1415 InitElementPropertiesEngine(game.engine_version);
1418 printf("level %d: level version == %06d\n", level_nr, level.game_version);
1419 printf(" tape version == %06d [%s] [file: %06d]\n",
1420 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
1422 printf(" => game.engine_version == %06d\n", game.engine_version);
1426 /* ---------- recursively resolve group elements ------------------------- */
1428 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1429 for (j = 0; j < NUM_GROUP_ELEMENTS; j++)
1430 element_info[i].in_group[j] = FALSE;
1432 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
1433 resolve_group_element(EL_GROUP_START + i, 0);
1436 /* ---------- initialize player's initial move delay --------------------- */
1439 /* dynamically adjust player properties according to level information */
1440 game.initial_move_delay_value =
1441 get_move_delay_from_stepsize(level.initial_player_stepsize);
1443 /* dynamically adjust player properties according to level information */
1444 game.initial_move_delay_value =
1445 (level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED);
1448 /* dynamically adjust player properties according to game engine version */
1449 game.initial_move_delay = (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
1450 game.initial_move_delay_value : 0);
1452 /* ---------- initialize player's initial push delay --------------------- */
1454 /* dynamically adjust player properties according to game engine version */
1455 game.initial_push_delay_value =
1456 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
1458 /* ---------- initialize changing elements ------------------------------- */
1460 /* initialize changing elements information */
1461 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1463 struct ElementInfo *ei = &element_info[i];
1465 /* this pointer might have been changed in the level editor */
1466 ei->change = &ei->change_page[0];
1468 if (!IS_CUSTOM_ELEMENT(i))
1470 ei->change->target_element = EL_EMPTY_SPACE;
1471 ei->change->delay_fixed = 0;
1472 ei->change->delay_random = 0;
1473 ei->change->delay_frames = 1;
1476 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1478 ei->has_change_event[j] = FALSE;
1480 ei->event_page_nr[j] = 0;
1481 ei->event_page[j] = &ei->change_page[0];
1485 /* add changing elements from pre-defined list */
1486 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
1488 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
1489 struct ElementInfo *ei = &element_info[ch_delay->element];
1491 ei->change->target_element = ch_delay->target_element;
1492 ei->change->delay_fixed = ch_delay->change_delay;
1494 ei->change->pre_change_function = ch_delay->pre_change_function;
1495 ei->change->change_function = ch_delay->change_function;
1496 ei->change->post_change_function = ch_delay->post_change_function;
1498 ei->change->can_change = TRUE;
1499 ei->change->can_change_or_has_action = TRUE;
1501 ei->has_change_event[CE_DELAY] = TRUE;
1503 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
1504 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
1507 /* ---------- initialize internal run-time variables ------------- */
1509 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1511 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1513 for (j = 0; j < ei->num_change_pages; j++)
1515 ei->change_page[j].can_change_or_has_action =
1516 (ei->change_page[j].can_change |
1517 ei->change_page[j].has_action);
1521 /* add change events from custom element configuration */
1522 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1524 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1526 for (j = 0; j < ei->num_change_pages; j++)
1528 if (!ei->change_page[j].can_change_or_has_action)
1531 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1533 /* only add event page for the first page found with this event */
1534 if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
1536 ei->has_change_event[k] = TRUE;
1538 ei->event_page_nr[k] = j;
1539 ei->event_page[k] = &ei->change_page[j];
1545 /* ---------- initialize run-time trigger player and element ------------- */
1547 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1549 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1551 for (j = 0; j < ei->num_change_pages; j++)
1553 ei->change_page[j].actual_trigger_element = EL_EMPTY;
1554 ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
1555 ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
1556 ei->change_page[j].actual_trigger_ce_value = 0;
1560 /* ---------- initialize trigger events ---------------------------------- */
1562 /* initialize trigger events information */
1563 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1564 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1565 trigger_events[i][j] = FALSE;
1567 /* add trigger events from element change event properties */
1568 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1570 struct ElementInfo *ei = &element_info[i];
1572 for (j = 0; j < ei->num_change_pages; j++)
1574 if (!ei->change_page[j].can_change_or_has_action)
1577 if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
1579 int trigger_element = ei->change_page[j].trigger_element;
1581 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1583 if (ei->change_page[j].has_event[k])
1585 if (IS_GROUP_ELEMENT(trigger_element))
1587 struct ElementGroupInfo *group =
1588 element_info[trigger_element].group;
1590 for (l = 0; l < group->num_elements_resolved; l++)
1591 trigger_events[group->element_resolved[l]][k] = TRUE;
1594 trigger_events[trigger_element][k] = TRUE;
1601 /* ---------- initialize push delay -------------------------------------- */
1603 /* initialize push delay values to default */
1604 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1606 if (!IS_CUSTOM_ELEMENT(i))
1608 element_info[i].push_delay_fixed = game.default_push_delay_fixed;
1609 element_info[i].push_delay_random = game.default_push_delay_random;
1613 /* set push delay value for certain elements from pre-defined list */
1614 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
1616 int e = push_delay_list[i].element;
1618 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
1619 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
1622 /* set push delay value for Supaplex elements for newer engine versions */
1623 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
1625 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1627 if (IS_SP_ELEMENT(i))
1629 /* set SP push delay to just enough to push under a falling zonk */
1630 int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
1632 element_info[i].push_delay_fixed = delay;
1633 element_info[i].push_delay_random = 0;
1638 /* ---------- initialize move stepsize ----------------------------------- */
1640 /* initialize move stepsize values to default */
1641 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1642 if (!IS_CUSTOM_ELEMENT(i))
1643 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
1645 /* set move stepsize value for certain elements from pre-defined list */
1646 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
1648 int e = move_stepsize_list[i].element;
1650 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
1653 /* ---------- initialize collect score ----------------------------------- */
1655 /* initialize collect score values for custom elements from initial value */
1656 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1657 if (IS_CUSTOM_ELEMENT(i))
1658 element_info[i].collect_score = element_info[i].collect_score_initial;
1660 /* ---------- initialize collect count ----------------------------------- */
1662 /* initialize collect count values for non-custom elements */
1663 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1664 if (!IS_CUSTOM_ELEMENT(i))
1665 element_info[i].collect_count_initial = 0;
1667 /* add collect count values for all elements from pre-defined list */
1668 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
1669 element_info[collect_count_list[i].element].collect_count_initial =
1670 collect_count_list[i].count;
1672 /* ---------- initialize access direction -------------------------------- */
1674 /* initialize access direction values to default (access from every side) */
1675 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1676 if (!IS_CUSTOM_ELEMENT(i))
1677 element_info[i].access_direction = MV_ALL_DIRECTIONS;
1679 /* set access direction value for certain elements from pre-defined list */
1680 for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
1681 element_info[access_direction_list[i].element].access_direction =
1682 access_direction_list[i].direction;
1684 /* ---------- initialize explosion content ------------------------------- */
1685 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1687 if (IS_CUSTOM_ELEMENT(i))
1690 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
1692 /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
1694 element_info[i].content.e[x][y] =
1695 (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
1696 i == EL_PLAYER_2 ? EL_EMERALD_RED :
1697 i == EL_PLAYER_3 ? EL_EMERALD :
1698 i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
1699 i == EL_MOLE ? EL_EMERALD_RED :
1700 i == EL_PENGUIN ? EL_EMERALD_PURPLE :
1701 i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
1702 i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
1703 i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
1704 i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
1705 i == EL_WALL_EMERALD ? EL_EMERALD :
1706 i == EL_WALL_DIAMOND ? EL_DIAMOND :
1707 i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
1708 i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
1709 i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
1710 i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
1711 i == EL_WALL_PEARL ? EL_PEARL :
1712 i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
1718 int get_num_special_action(int element, int action_first, int action_last)
1720 int num_special_action = 0;
1723 for (i = action_first; i <= action_last; i++)
1725 boolean found = FALSE;
1727 for (j = 0; j < NUM_DIRECTIONS; j++)
1728 if (el_act_dir2img(element, i, j) !=
1729 el_act_dir2img(element, ACTION_DEFAULT, j))
1733 num_special_action++;
1738 return num_special_action;
1742 =============================================================================
1744 -----------------------------------------------------------------------------
1745 initialize and start new game
1746 =============================================================================
1751 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
1752 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
1753 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
1758 /* don't play tapes over network */
1759 network_playing = (options.network && !tape.playing);
1761 for (i = 0; i < MAX_PLAYERS; i++)
1763 struct PlayerInfo *player = &stored_player[i];
1765 player->index_nr = i;
1766 player->index_bit = (1 << i);
1767 player->element_nr = EL_PLAYER_1 + i;
1769 player->present = FALSE;
1770 player->active = FALSE;
1773 player->effective_action = 0;
1774 player->programmed_action = 0;
1777 player->gems_still_needed = level.gems_needed;
1778 player->sokobanfields_still_needed = 0;
1779 player->lights_still_needed = 0;
1780 player->friends_still_needed = 0;
1782 for (j = 0; j < MAX_NUM_KEYS; j++)
1783 player->key[j] = FALSE;
1785 player->dynabomb_count = 0;
1786 player->dynabomb_size = 1;
1787 player->dynabombs_left = 0;
1788 player->dynabomb_xl = FALSE;
1790 player->MovDir = MV_NONE;
1793 player->GfxDir = MV_NONE;
1794 player->GfxAction = ACTION_DEFAULT;
1796 player->StepFrame = 0;
1798 player->use_murphy = FALSE;
1799 player->artwork_element =
1800 (level.use_artwork_element[i] ? level.artwork_element[i] :
1801 player->element_nr);
1803 player->block_last_field = FALSE; /* initialized in InitPlayerField() */
1804 player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
1806 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
1808 player->actual_frame_counter = 0;
1810 player->step_counter = 0;
1812 player->last_move_dir = MV_NONE;
1814 player->is_waiting = FALSE;
1815 player->is_moving = FALSE;
1816 player->is_auto_moving = FALSE;
1817 player->is_digging = FALSE;
1818 player->is_snapping = FALSE;
1819 player->is_collecting = FALSE;
1820 player->is_pushing = FALSE;
1821 player->is_switching = FALSE;
1822 player->is_dropping = FALSE;
1824 player->is_bored = FALSE;
1825 player->is_sleeping = FALSE;
1827 player->frame_counter_bored = -1;
1828 player->frame_counter_sleeping = -1;
1830 player->anim_delay_counter = 0;
1831 player->post_delay_counter = 0;
1833 player->action_waiting = ACTION_DEFAULT;
1834 player->last_action_waiting = ACTION_DEFAULT;
1835 player->special_action_bored = ACTION_DEFAULT;
1836 player->special_action_sleeping = ACTION_DEFAULT;
1838 /* set number of special actions for bored and sleeping animation */
1839 player->num_special_action_bored =
1840 get_num_special_action(player->artwork_element,
1841 ACTION_BORING_1, ACTION_BORING_LAST);
1842 player->num_special_action_sleeping =
1843 get_num_special_action(player->artwork_element,
1844 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
1846 player->switch_x = -1;
1847 player->switch_y = -1;
1849 player->drop_x = -1;
1850 player->drop_y = -1;
1852 player->show_envelope = 0;
1855 SetPlayerMoveSpeed(player, level.initial_player_stepsize, TRUE);
1857 player->move_delay = game.initial_move_delay;
1858 player->move_delay_value = game.initial_move_delay_value;
1860 player->move_delay_value_next = -1;
1862 player->move_delay_reset_counter = 0;
1864 player->cannot_move = FALSE;
1867 player->push_delay = -1; /* initialized when pushing starts */
1868 player->push_delay_value = game.initial_push_delay_value;
1870 player->drop_delay = 0;
1872 player->last_jx = player->last_jy = 0;
1873 player->jx = player->jy = 0;
1875 player->shield_normal_time_left = 0;
1876 player->shield_deadly_time_left = 0;
1878 player->inventory_infinite_element = EL_UNDEFINED;
1879 player->inventory_size = 0;
1881 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
1882 SnapField(player, 0, 0);
1884 player->LevelSolved = FALSE;
1885 player->GameOver = FALSE;
1888 network_player_action_received = FALSE;
1890 #if defined(NETWORK_AVALIABLE)
1891 /* initial null action */
1892 if (network_playing)
1893 SendToServer_MovePlayer(MV_NONE);
1902 TimeLeft = level.time;
1905 ScreenMovDir = MV_NONE;
1909 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
1911 AllPlayersGone = FALSE;
1913 game.yamyam_content_nr = 0;
1914 game.magic_wall_active = FALSE;
1915 game.magic_wall_time_left = 0;
1916 game.light_time_left = 0;
1917 game.timegate_time_left = 0;
1918 game.switchgate_pos = 0;
1919 game.wind_direction = level.wind_direction_initial;
1920 game.gravity = level.initial_gravity;
1921 game.explosions_delayed = TRUE;
1923 game.lenses_time_left = 0;
1924 game.magnify_time_left = 0;
1926 game.ball_state = level.ball_state_initial;
1927 game.ball_content_nr = 0;
1929 game.envelope_active = FALSE;
1931 for (i = 0; i < NUM_BELTS; i++)
1933 game.belt_dir[i] = MV_NONE;
1934 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1937 for (i = 0; i < MAX_NUM_AMOEBA; i++)
1938 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
1941 SCAN_PLAYFIELD(x, y)
1943 for (x = 0; x < lev_fieldx; x++) for (y = 0; y < lev_fieldy; y++)
1946 Feld[x][y] = level.field[x][y];
1947 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
1948 ChangeDelay[x][y] = 0;
1949 ChangePage[x][y] = -1;
1950 #if USE_NEW_CUSTOM_VALUE
1951 CustomValue[x][y] = 0; /* initialized in InitField() */
1953 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
1955 WasJustMoving[x][y] = 0;
1956 WasJustFalling[x][y] = 0;
1957 CheckCollision[x][y] = 0;
1959 Pushed[x][y] = FALSE;
1961 ChangeCount[x][y] = 0;
1962 ChangeEvent[x][y] = -1;
1964 ExplodePhase[x][y] = 0;
1965 ExplodeDelay[x][y] = 0;
1966 ExplodeField[x][y] = EX_TYPE_NONE;
1968 RunnerVisit[x][y] = 0;
1969 PlayerVisit[x][y] = 0;
1972 GfxRandom[x][y] = INIT_GFX_RANDOM();
1973 GfxElement[x][y] = EL_UNDEFINED;
1974 GfxAction[x][y] = ACTION_DEFAULT;
1975 GfxDir[x][y] = MV_NONE;
1979 SCAN_PLAYFIELD(x, y)
1981 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
1984 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
1986 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
1988 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
1991 InitField(x, y, TRUE);
1996 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
1997 emulate_sb ? EMU_SOKOBAN :
1998 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
2000 #if USE_NEW_ALL_SLIPPERY
2001 /* initialize type of slippery elements */
2002 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2004 if (!IS_CUSTOM_ELEMENT(i))
2006 /* default: elements slip down either to the left or right randomly */
2007 element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
2009 /* SP style elements prefer to slip down on the left side */
2010 if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
2011 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
2013 /* BD style elements prefer to slip down on the left side */
2014 if (game.emulation == EMU_BOULDERDASH)
2015 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
2020 /* initialize explosion and ignition delay */
2021 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2023 if (!IS_CUSTOM_ELEMENT(i))
2026 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
2027 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
2028 game.emulation == EMU_SUPAPLEX ? 3 : 2);
2029 int last_phase = (num_phase + 1) * delay;
2030 int half_phase = (num_phase / 2) * delay;
2032 element_info[i].explosion_delay = last_phase - 1;
2033 element_info[i].ignition_delay = half_phase;
2035 if (i == EL_BLACK_ORB)
2036 element_info[i].ignition_delay = 1;
2040 if (element_info[i].explosion_delay < 1) /* !!! check again !!! */
2041 element_info[i].explosion_delay = 1;
2043 if (element_info[i].ignition_delay < 1) /* !!! check again !!! */
2044 element_info[i].ignition_delay = 1;
2048 /* correct non-moving belts to start moving left */
2049 for (i = 0; i < NUM_BELTS; i++)
2050 if (game.belt_dir[i] == MV_NONE)
2051 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
2053 /* check if any connected player was not found in playfield */
2054 for (i = 0; i < MAX_PLAYERS; i++)
2056 struct PlayerInfo *player = &stored_player[i];
2058 if (player->connected && !player->present)
2060 for (j = 0; j < MAX_PLAYERS; j++)
2062 struct PlayerInfo *some_player = &stored_player[j];
2063 int jx = some_player->jx, jy = some_player->jy;
2065 /* assign first free player found that is present in the playfield */
2066 if (some_player->present && !some_player->connected)
2068 player->present = TRUE;
2069 player->active = TRUE;
2071 some_player->present = FALSE;
2072 some_player->active = FALSE;
2075 player->element_nr = some_player->element_nr;
2078 player->artwork_element = some_player->artwork_element;
2080 player->block_last_field = some_player->block_last_field;
2081 player->block_delay_adjustment = some_player->block_delay_adjustment;
2083 StorePlayer[jx][jy] = player->element_nr;
2084 player->jx = player->last_jx = jx;
2085 player->jy = player->last_jy = jy;
2095 /* when playing a tape, eliminate all players which do not participate */
2097 for (i = 0; i < MAX_PLAYERS; i++)
2099 if (stored_player[i].active && !tape.player_participates[i])
2101 struct PlayerInfo *player = &stored_player[i];
2102 int jx = player->jx, jy = player->jy;
2104 player->active = FALSE;
2105 StorePlayer[jx][jy] = 0;
2106 Feld[jx][jy] = EL_EMPTY;
2110 else if (!options.network && !setup.team_mode) /* && !tape.playing */
2112 /* when in single player mode, eliminate all but the first active player */
2114 for (i = 0; i < MAX_PLAYERS; i++)
2116 if (stored_player[i].active)
2118 for (j = i + 1; j < MAX_PLAYERS; j++)
2120 if (stored_player[j].active)
2122 struct PlayerInfo *player = &stored_player[j];
2123 int jx = player->jx, jy = player->jy;
2125 player->active = FALSE;
2126 player->present = FALSE;
2128 StorePlayer[jx][jy] = 0;
2129 Feld[jx][jy] = EL_EMPTY;
2136 /* when recording the game, store which players take part in the game */
2139 for (i = 0; i < MAX_PLAYERS; i++)
2140 if (stored_player[i].active)
2141 tape.player_participates[i] = TRUE;
2146 for (i = 0; i < MAX_PLAYERS; i++)
2148 struct PlayerInfo *player = &stored_player[i];
2150 printf("Player %d: present == %d, connected == %d, active == %d.\n",
2155 if (local_player == player)
2156 printf("Player %d is local player.\n", i+1);
2160 if (BorderElement == EL_EMPTY)
2163 SBX_Right = lev_fieldx - SCR_FIELDX;
2165 SBY_Lower = lev_fieldy - SCR_FIELDY;
2170 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
2172 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
2175 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
2176 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
2178 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
2179 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
2181 /* if local player not found, look for custom element that might create
2182 the player (make some assumptions about the right custom element) */
2183 if (!local_player->present)
2185 int start_x = 0, start_y = 0;
2186 int found_rating = 0;
2187 int found_element = EL_UNDEFINED;
2188 int player_nr = local_player->index_nr;
2191 SCAN_PLAYFIELD(x, y)
2193 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
2196 int element = Feld[x][y];
2201 if (level.use_start_element[player_nr] &&
2202 level.start_element[player_nr] == element &&
2209 found_element = element;
2212 if (!IS_CUSTOM_ELEMENT(element))
2215 if (CAN_CHANGE(element))
2217 for (i = 0; i < element_info[element].num_change_pages; i++)
2219 /* check for player created from custom element as single target */
2220 content = element_info[element].change_page[i].target_element;
2221 is_player = ELEM_IS_PLAYER(content);
2223 if (is_player && (found_rating < 3 || element < found_element))
2229 found_element = element;
2234 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
2236 /* check for player created from custom element as explosion content */
2237 content = element_info[element].content.e[xx][yy];
2238 is_player = ELEM_IS_PLAYER(content);
2240 if (is_player && (found_rating < 2 || element < found_element))
2242 start_x = x + xx - 1;
2243 start_y = y + yy - 1;
2246 found_element = element;
2249 if (!CAN_CHANGE(element))
2252 for (i = 0; i < element_info[element].num_change_pages; i++)
2254 /* check for player created from custom element as extended target */
2256 element_info[element].change_page[i].target_content.e[xx][yy];
2258 is_player = ELEM_IS_PLAYER(content);
2260 if (is_player && (found_rating < 1 || element < found_element))
2262 start_x = x + xx - 1;
2263 start_y = y + yy - 1;
2266 found_element = element;
2272 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
2273 start_x > SBX_Right + MIDPOSX ? SBX_Right :
2276 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
2277 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
2282 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2283 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2284 local_player->jx - MIDPOSX);
2286 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2287 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2288 local_player->jy - MIDPOSY);
2291 if (!game.restart_level)
2292 CloseDoor(DOOR_CLOSE_1);
2294 /* !!! FIX THIS (START) !!! */
2295 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2297 InitGameEngine_EM();
2304 /* after drawing the level, correct some elements */
2305 if (game.timegate_time_left == 0)
2306 CloseAllOpenTimegates();
2308 if (setup.soft_scrolling)
2309 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
2311 redraw_mask |= REDRAW_FROM_BACKBUFFER;
2314 /* !!! FIX THIS (END) !!! */
2316 if (!game.restart_level)
2318 /* copy default game door content to main double buffer */
2319 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2320 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2323 DrawGameDoorValues();
2325 if (!game.restart_level)
2329 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
2330 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
2331 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
2335 /* copy actual game door content to door double buffer for OpenDoor() */
2336 BlitBitmap(drawto, bitmap_db_door,
2337 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
2339 OpenDoor(DOOR_OPEN_ALL);
2341 PlaySoundStereo(SND_GAME_STARTING, SOUND_MIDDLE);
2343 if (setup.sound_music)
2346 KeyboardAutoRepeatOffUnlessAutoplay();
2350 for (i = 0; i < MAX_PLAYERS; i++)
2351 printf("Player %d %sactive.\n",
2352 i + 1, (stored_player[i].active ? "" : "not "));
2356 game.restart_level = FALSE;
2359 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
2361 /* this is used for non-R'n'D game engines to update certain engine values */
2363 /* needed to determine if sounds are played within the visible screen area */
2364 scroll_x = actual_scroll_x;
2365 scroll_y = actual_scroll_y;
2368 void InitMovDir(int x, int y)
2370 int i, element = Feld[x][y];
2371 static int xy[4][2] =
2378 static int direction[3][4] =
2380 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
2381 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
2382 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
2391 Feld[x][y] = EL_BUG;
2392 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
2395 case EL_SPACESHIP_RIGHT:
2396 case EL_SPACESHIP_UP:
2397 case EL_SPACESHIP_LEFT:
2398 case EL_SPACESHIP_DOWN:
2399 Feld[x][y] = EL_SPACESHIP;
2400 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
2403 case EL_BD_BUTTERFLY_RIGHT:
2404 case EL_BD_BUTTERFLY_UP:
2405 case EL_BD_BUTTERFLY_LEFT:
2406 case EL_BD_BUTTERFLY_DOWN:
2407 Feld[x][y] = EL_BD_BUTTERFLY;
2408 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
2411 case EL_BD_FIREFLY_RIGHT:
2412 case EL_BD_FIREFLY_UP:
2413 case EL_BD_FIREFLY_LEFT:
2414 case EL_BD_FIREFLY_DOWN:
2415 Feld[x][y] = EL_BD_FIREFLY;
2416 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
2419 case EL_PACMAN_RIGHT:
2421 case EL_PACMAN_LEFT:
2422 case EL_PACMAN_DOWN:
2423 Feld[x][y] = EL_PACMAN;
2424 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
2427 case EL_SP_SNIKSNAK:
2428 MovDir[x][y] = MV_UP;
2431 case EL_SP_ELECTRON:
2432 MovDir[x][y] = MV_LEFT;
2439 Feld[x][y] = EL_MOLE;
2440 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
2444 if (IS_CUSTOM_ELEMENT(element))
2446 struct ElementInfo *ei = &element_info[element];
2447 int move_direction_initial = ei->move_direction_initial;
2448 int move_pattern = ei->move_pattern;
2450 if (move_direction_initial == MV_START_PREVIOUS)
2452 if (MovDir[x][y] != MV_NONE)
2455 move_direction_initial = MV_START_AUTOMATIC;
2458 if (move_direction_initial == MV_START_RANDOM)
2459 MovDir[x][y] = 1 << RND(4);
2460 else if (move_direction_initial & MV_ANY_DIRECTION)
2461 MovDir[x][y] = move_direction_initial;
2462 else if (move_pattern == MV_ALL_DIRECTIONS ||
2463 move_pattern == MV_TURNING_LEFT ||
2464 move_pattern == MV_TURNING_RIGHT ||
2465 move_pattern == MV_TURNING_LEFT_RIGHT ||
2466 move_pattern == MV_TURNING_RIGHT_LEFT ||
2467 move_pattern == MV_TURNING_RANDOM)
2468 MovDir[x][y] = 1 << RND(4);
2469 else if (move_pattern == MV_HORIZONTAL)
2470 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
2471 else if (move_pattern == MV_VERTICAL)
2472 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
2473 else if (move_pattern & MV_ANY_DIRECTION)
2474 MovDir[x][y] = element_info[element].move_pattern;
2475 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
2476 move_pattern == MV_ALONG_RIGHT_SIDE)
2478 /* use random direction as default start direction */
2479 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2480 MovDir[x][y] = 1 << RND(4);
2482 for (i = 0; i < NUM_DIRECTIONS; i++)
2484 int x1 = x + xy[i][0];
2485 int y1 = y + xy[i][1];
2487 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2489 if (move_pattern == MV_ALONG_RIGHT_SIDE)
2490 MovDir[x][y] = direction[0][i];
2492 MovDir[x][y] = direction[1][i];
2501 MovDir[x][y] = 1 << RND(4);
2503 if (element != EL_BUG &&
2504 element != EL_SPACESHIP &&
2505 element != EL_BD_BUTTERFLY &&
2506 element != EL_BD_FIREFLY)
2509 for (i = 0; i < NUM_DIRECTIONS; i++)
2511 int x1 = x + xy[i][0];
2512 int y1 = y + xy[i][1];
2514 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2516 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
2518 MovDir[x][y] = direction[0][i];
2521 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
2522 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
2524 MovDir[x][y] = direction[1][i];
2533 GfxDir[x][y] = MovDir[x][y];
2536 void InitAmoebaNr(int x, int y)
2539 int group_nr = AmoebeNachbarNr(x, y);
2543 for (i = 1; i < MAX_NUM_AMOEBA; i++)
2545 if (AmoebaCnt[i] == 0)
2553 AmoebaNr[x][y] = group_nr;
2554 AmoebaCnt[group_nr]++;
2555 AmoebaCnt2[group_nr]++;
2561 boolean raise_level = FALSE;
2563 if (local_player->MovPos)
2566 if (tape.auto_play) /* tape might already be stopped here */
2567 tape.auto_play_level_solved = TRUE;
2569 local_player->LevelSolved = FALSE;
2571 PlaySoundStereo(SND_GAME_WINNING, SOUND_MIDDLE);
2575 if (!tape.playing && setup.sound_loops)
2576 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2577 SND_CTRL_PLAY_LOOP);
2579 while (TimeLeft > 0)
2581 if (!tape.playing && !setup.sound_loops)
2582 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2584 if (TimeLeft > 100 && TimeLeft % 10 == 0)
2587 RaiseScore(level.score[SC_TIME_BONUS] * 10);
2592 RaiseScore(level.score[SC_TIME_BONUS]);
2595 DrawGameValue_Time(TimeLeft);
2603 if (!tape.playing && setup.sound_loops)
2604 StopSound(SND_GAME_LEVELTIME_BONUS);
2606 else if (level.time == 0) /* level without time limit */
2608 if (!tape.playing && setup.sound_loops)
2609 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2610 SND_CTRL_PLAY_LOOP);
2612 while (TimePlayed < 999)
2614 if (!tape.playing && !setup.sound_loops)
2615 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2617 if (TimePlayed < 900 && TimePlayed % 10 == 0)
2620 RaiseScore(level.score[SC_TIME_BONUS] * 10);
2625 RaiseScore(level.score[SC_TIME_BONUS]);
2628 DrawGameValue_Time(TimePlayed);
2636 if (!tape.playing && setup.sound_loops)
2637 StopSound(SND_GAME_LEVELTIME_BONUS);
2640 /* close exit door after last player */
2641 if (AllPlayersGone && ExitX >= 0 && ExitY >= 0 &&
2642 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
2643 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN))
2645 int element = Feld[ExitX][ExitY];
2647 Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
2648 EL_SP_EXIT_CLOSING);
2650 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
2653 /* player disappears */
2654 if (ExitX >= 0 && ExitY >= 0)
2655 DrawLevelField(ExitX, ExitY);
2662 CloseDoor(DOOR_CLOSE_1);
2667 SaveTape(tape.level_nr); /* Ask to save tape */
2670 if (level_nr == leveldir_current->handicap_level)
2672 leveldir_current->handicap_level++;
2673 SaveLevelSetup_SeriesInfo();
2676 if (level_editor_test_game)
2677 local_player->score = -1; /* no highscore when playing from editor */
2678 else if (level_nr < leveldir_current->last_level)
2679 raise_level = TRUE; /* advance to next level */
2681 if ((hi_pos = NewHiScore()) >= 0)
2683 game_status = GAME_MODE_SCORES;
2684 DrawHallOfFame(hi_pos);
2693 game_status = GAME_MODE_MAIN;
2710 LoadScore(level_nr);
2712 if (strcmp(setup.player_name, EMPTY_PLAYER_NAME) == 0 ||
2713 local_player->score < highscore[MAX_SCORE_ENTRIES - 1].Score)
2716 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
2718 if (local_player->score > highscore[k].Score)
2720 /* player has made it to the hall of fame */
2722 if (k < MAX_SCORE_ENTRIES - 1)
2724 int m = MAX_SCORE_ENTRIES - 1;
2727 for (l = k; l < MAX_SCORE_ENTRIES; l++)
2728 if (!strcmp(setup.player_name, highscore[l].Name))
2730 if (m == k) /* player's new highscore overwrites his old one */
2734 for (l = m; l > k; l--)
2736 strcpy(highscore[l].Name, highscore[l - 1].Name);
2737 highscore[l].Score = highscore[l - 1].Score;
2744 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
2745 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
2746 highscore[k].Score = local_player->score;
2752 else if (!strncmp(setup.player_name, highscore[k].Name,
2753 MAX_PLAYER_NAME_LEN))
2754 break; /* player already there with a higher score */
2760 SaveScore(level_nr);
2765 inline static int getElementMoveStepsize(int x, int y)
2767 int element = Feld[x][y];
2768 int direction = MovDir[x][y];
2769 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2770 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2771 int horiz_move = (dx != 0);
2772 int sign = (horiz_move ? dx : dy);
2773 int step = sign * element_info[element].move_stepsize;
2775 /* special values for move stepsize for spring and things on conveyor belt */
2779 if (element == EL_SPRING)
2780 step = sign * MOVE_STEPSIZE_NORMAL * 2;
2781 else if (CAN_FALL(element) && !CAN_MOVE(element) &&
2782 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
2783 step = sign * MOVE_STEPSIZE_NORMAL / 2;
2785 if (CAN_FALL(element) &&
2786 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
2787 step = sign * MOVE_STEPSIZE_NORMAL / 2;
2788 else if (element == EL_SPRING)
2789 step = sign * MOVE_STEPSIZE_NORMAL * 2;
2796 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
2798 if (player->GfxAction != action || player->GfxDir != dir)
2801 printf("Player frame reset! (%d => %d, %d => %d)\n",
2802 player->GfxAction, action, player->GfxDir, dir);
2805 player->GfxAction = action;
2806 player->GfxDir = dir;
2808 player->StepFrame = 0;
2812 static void ResetRandomAnimationValue(int x, int y)
2814 GfxRandom[x][y] = INIT_GFX_RANDOM();
2817 static void ResetGfxAnimation(int x, int y)
2820 int element, graphic;
2824 GfxAction[x][y] = ACTION_DEFAULT;
2825 GfxDir[x][y] = MovDir[x][y];
2828 element = Feld[x][y];
2829 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
2831 if (graphic_info[graphic].anim_global_sync)
2832 GfxFrame[x][y] = FrameCounter;
2833 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
2834 GfxFrame[x][y] = CustomValue[x][y];
2835 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2836 GfxFrame[x][y] = element_info[element].collect_score;
2840 void InitMovingField(int x, int y, int direction)
2842 int element = Feld[x][y];
2846 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2847 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2851 if (!WasJustMoving[x][y] || direction != MovDir[x][y])
2852 ResetGfxAnimation(x, y);
2854 MovDir[x][y] = direction;
2855 GfxDir[x][y] = direction;
2856 GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
2857 ACTION_FALLING : ACTION_MOVING);
2860 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
2862 if (graphic_info[graphic].anim_global_sync)
2863 GfxFrame[x][y] = FrameCounter;
2864 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
2865 GfxFrame[x][y] = CustomValue[x][y];
2866 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2867 GfxFrame[x][y] = element_info[element].collect_score;
2870 /* this is needed for CEs with property "can move" / "not moving" */
2872 if (getElementMoveStepsize(x, y) != 0) /* moving or being moved */
2874 if (Feld[newx][newy] == EL_EMPTY)
2875 Feld[newx][newy] = EL_BLOCKED;
2877 MovDir[newx][newy] = MovDir[x][y];
2879 #if USE_NEW_CUSTOM_VALUE
2880 CustomValue[newx][newy] = CustomValue[x][y];
2883 GfxFrame[newx][newy] = GfxFrame[x][y];
2884 GfxRandom[newx][newy] = GfxRandom[x][y];
2885 GfxAction[newx][newy] = GfxAction[x][y];
2886 GfxDir[newx][newy] = GfxDir[x][y];
2890 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
2892 int direction = MovDir[x][y];
2894 int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
2895 int newy = y + (direction & MV_UP ? -1 : direction & MV_DOWN ? +1 : 0);
2897 int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2898 int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2905 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
2907 int oldx = x, oldy = y;
2908 int direction = MovDir[x][y];
2910 if (direction == MV_LEFT)
2912 else if (direction == MV_RIGHT)
2914 else if (direction == MV_UP)
2916 else if (direction == MV_DOWN)
2919 *comes_from_x = oldx;
2920 *comes_from_y = oldy;
2923 int MovingOrBlocked2Element(int x, int y)
2925 int element = Feld[x][y];
2927 if (element == EL_BLOCKED)
2931 Blocked2Moving(x, y, &oldx, &oldy);
2932 return Feld[oldx][oldy];
2938 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
2940 /* like MovingOrBlocked2Element(), but if element is moving
2941 and (x,y) is the field the moving element is just leaving,
2942 return EL_BLOCKED instead of the element value */
2943 int element = Feld[x][y];
2945 if (IS_MOVING(x, y))
2947 if (element == EL_BLOCKED)
2951 Blocked2Moving(x, y, &oldx, &oldy);
2952 return Feld[oldx][oldy];
2961 static void RemoveField(int x, int y)
2963 Feld[x][y] = EL_EMPTY;
2969 #if USE_NEW_CUSTOM_VALUE
2970 CustomValue[x][y] = 0;
2974 ChangeDelay[x][y] = 0;
2975 ChangePage[x][y] = -1;
2976 Pushed[x][y] = FALSE;
2979 ExplodeField[x][y] = EX_TYPE_NONE;
2982 GfxElement[x][y] = EL_UNDEFINED;
2983 GfxAction[x][y] = ACTION_DEFAULT;
2984 GfxDir[x][y] = MV_NONE;
2987 void RemoveMovingField(int x, int y)
2989 int oldx = x, oldy = y, newx = x, newy = y;
2990 int element = Feld[x][y];
2991 int next_element = EL_UNDEFINED;
2993 if (element != EL_BLOCKED && !IS_MOVING(x, y))
2996 if (IS_MOVING(x, y))
2998 Moving2Blocked(x, y, &newx, &newy);
3000 if (Feld[newx][newy] != EL_BLOCKED)
3002 /* element is moving, but target field is not free (blocked), but
3003 already occupied by something different (example: acid pool);
3004 in this case, only remove the moving field, but not the target */
3006 RemoveField(oldx, oldy);
3008 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
3010 DrawLevelField(oldx, oldy);
3015 else if (element == EL_BLOCKED)
3017 Blocked2Moving(x, y, &oldx, &oldy);
3018 if (!IS_MOVING(oldx, oldy))
3022 if (element == EL_BLOCKED &&
3023 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
3024 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
3025 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
3026 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
3027 next_element = get_next_element(Feld[oldx][oldy]);
3029 RemoveField(oldx, oldy);
3030 RemoveField(newx, newy);
3032 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
3034 if (next_element != EL_UNDEFINED)
3035 Feld[oldx][oldy] = next_element;
3037 DrawLevelField(oldx, oldy);
3038 DrawLevelField(newx, newy);
3041 void DrawDynamite(int x, int y)
3043 int sx = SCREENX(x), sy = SCREENY(y);
3044 int graphic = el2img(Feld[x][y]);
3047 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
3050 if (IS_WALKABLE_INSIDE(Back[x][y]))
3054 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
3055 else if (Store[x][y])
3056 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
3058 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
3060 if (Back[x][y] || Store[x][y])
3061 DrawGraphicThruMask(sx, sy, graphic, frame);
3063 DrawGraphic(sx, sy, graphic, frame);
3066 void CheckDynamite(int x, int y)
3068 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
3072 if (MovDelay[x][y] != 0)
3075 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
3081 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
3086 void DrawRelocatePlayer(struct PlayerInfo *player)
3088 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3089 boolean no_delay = (tape.warp_forward);
3090 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
3091 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
3092 int jx = player->jx;
3093 int jy = player->jy;
3095 if (level.instant_relocation)
3097 int offset = (setup.scroll_delay ? 3 : 0);
3099 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
3101 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
3102 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
3103 local_player->jx - MIDPOSX);
3105 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
3106 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
3107 local_player->jy - MIDPOSY);
3111 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
3112 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
3113 scroll_x = jx - MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
3115 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
3116 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
3117 scroll_y = jy - MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
3119 /* don't scroll over playfield boundaries */
3120 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
3121 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
3123 /* don't scroll over playfield boundaries */
3124 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
3125 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
3128 RedrawPlayfield(TRUE, 0,0,0,0);
3132 int scroll_xx = -999, scroll_yy = -999;
3134 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
3136 while (scroll_xx != scroll_x || scroll_yy != scroll_y)
3139 int fx = FX, fy = FY;
3141 scroll_xx = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
3142 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
3143 local_player->jx - MIDPOSX);
3145 scroll_yy = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
3146 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
3147 local_player->jy - MIDPOSY);
3149 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
3150 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
3152 if (dx == 0 && dy == 0) /* no scrolling needed at all */
3158 fx += dx * TILEX / 2;
3159 fy += dy * TILEY / 2;
3161 ScrollLevel(dx, dy);
3164 /* scroll in two steps of half tile size to make things smoother */
3165 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
3167 Delay(wait_delay_value);
3169 /* scroll second step to align at full tile size */
3171 Delay(wait_delay_value);
3176 Delay(wait_delay_value);
3180 void RelocatePlayer(int jx, int jy, int el_player_raw)
3182 int el_player = GET_PLAYER_ELEMENT(el_player_raw);
3183 int player_nr = GET_PLAYER_NR(el_player);
3184 struct PlayerInfo *player = &stored_player[player_nr];
3185 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3186 boolean no_delay = (tape.warp_forward);
3187 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
3188 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
3189 int old_jx = player->jx;
3190 int old_jy = player->jy;
3191 int old_element = Feld[old_jx][old_jy];
3192 int element = Feld[jx][jy];
3193 boolean player_relocated = (old_jx != jx || old_jy != jy);
3195 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
3196 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
3197 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
3198 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
3199 int leave_side_horiz = move_dir_horiz;
3200 int leave_side_vert = move_dir_vert;
3201 int enter_side = enter_side_horiz | enter_side_vert;
3202 int leave_side = leave_side_horiz | leave_side_vert;
3204 if (player->GameOver) /* do not reanimate dead player */
3207 if (!player_relocated) /* no need to relocate the player */
3210 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
3212 RemoveField(jx, jy); /* temporarily remove newly placed player */
3213 DrawLevelField(jx, jy);
3216 if (player->present)
3218 while (player->MovPos)
3220 ScrollPlayer(player, SCROLL_GO_ON);
3221 ScrollScreen(NULL, SCROLL_GO_ON);
3223 AdvanceFrameAndPlayerCounters(player->index_nr);
3228 Delay(wait_delay_value);
3231 DrawPlayer(player); /* needed here only to cleanup last field */
3232 DrawLevelField(player->jx, player->jy); /* remove player graphic */
3234 player->is_moving = FALSE;
3237 if (IS_CUSTOM_ELEMENT(old_element))
3238 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
3240 player->index_bit, leave_side);
3242 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
3244 player->index_bit, leave_side);
3246 Feld[jx][jy] = el_player;
3247 InitPlayerField(jx, jy, el_player, TRUE);
3249 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
3251 Feld[jx][jy] = element;
3252 InitField(jx, jy, FALSE);
3255 if (player == local_player) /* only visually relocate local player */
3256 DrawRelocatePlayer(player);
3258 TestIfPlayerTouchesBadThing(jx, jy);
3259 TestIfPlayerTouchesCustomElement(jx, jy);
3261 if (IS_CUSTOM_ELEMENT(element))
3262 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
3263 player->index_bit, enter_side);
3265 CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
3266 player->index_bit, enter_side);
3269 void Explode(int ex, int ey, int phase, int mode)
3275 /* !!! eliminate this variable !!! */
3276 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
3278 if (game.explosions_delayed)
3280 ExplodeField[ex][ey] = mode;
3284 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
3286 int center_element = Feld[ex][ey];
3287 int artwork_element, explosion_element; /* set these values later */
3290 /* --- This is only really needed (and now handled) in "Impact()". --- */
3291 /* do not explode moving elements that left the explode field in time */
3292 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
3293 center_element == EL_EMPTY &&
3294 (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
3299 /* !!! at this place, the center element may be EL_BLOCKED !!! */
3300 if (mode == EX_TYPE_NORMAL ||
3301 mode == EX_TYPE_CENTER ||
3302 mode == EX_TYPE_CROSS)
3303 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
3306 /* remove things displayed in background while burning dynamite */
3307 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
3310 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
3312 /* put moving element to center field (and let it explode there) */
3313 center_element = MovingOrBlocked2Element(ex, ey);
3314 RemoveMovingField(ex, ey);
3315 Feld[ex][ey] = center_element;
3318 /* now "center_element" is finally determined -- set related values now */
3319 artwork_element = center_element; /* for custom player artwork */
3320 explosion_element = center_element; /* for custom player artwork */
3322 if (IS_PLAYER(ex, ey))
3324 int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
3326 artwork_element = stored_player[player_nr].artwork_element;
3328 if (level.use_explosion_element[player_nr])
3330 explosion_element = level.explosion_element[player_nr];
3331 artwork_element = explosion_element;
3336 if (mode == EX_TYPE_NORMAL ||
3337 mode == EX_TYPE_CENTER ||
3338 mode == EX_TYPE_CROSS)
3339 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
3343 last_phase = element_info[explosion_element].explosion_delay + 1;
3345 last_phase = element_info[center_element].explosion_delay + 1;
3348 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
3350 int xx = x - ex + 1;
3351 int yy = y - ey + 1;
3354 if (!IN_LEV_FIELD(x, y) ||
3355 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
3356 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
3359 element = Feld[x][y];
3361 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
3363 element = MovingOrBlocked2Element(x, y);
3365 if (!IS_EXPLOSION_PROOF(element))
3366 RemoveMovingField(x, y);
3369 /* indestructible elements can only explode in center (but not flames) */
3370 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
3371 mode == EX_TYPE_BORDER)) ||
3372 element == EL_FLAMES)
3375 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
3376 behaviour, for example when touching a yamyam that explodes to rocks
3377 with active deadly shield, a rock is created under the player !!! */
3378 /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
3380 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
3381 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
3382 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
3384 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
3387 if (IS_ACTIVE_BOMB(element))
3389 /* re-activate things under the bomb like gate or penguin */
3390 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
3397 /* save walkable background elements while explosion on same tile */
3398 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
3399 (x != ex || y != ey || mode == EX_TYPE_BORDER))
3400 Back[x][y] = element;
3402 /* ignite explodable elements reached by other explosion */
3403 if (element == EL_EXPLOSION)
3404 element = Store2[x][y];
3406 if (AmoebaNr[x][y] &&
3407 (element == EL_AMOEBA_FULL ||
3408 element == EL_BD_AMOEBA ||
3409 element == EL_AMOEBA_GROWING))
3411 AmoebaCnt[AmoebaNr[x][y]]--;
3412 AmoebaCnt2[AmoebaNr[x][y]]--;
3417 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
3420 int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
3422 Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
3424 switch(StorePlayer[ex][ey])
3427 Store[x][y] = EL_PLAYER_IS_EXPLODING_2;
3430 Store[x][y] = EL_PLAYER_IS_EXPLODING_3;
3433 Store[x][y] = EL_PLAYER_IS_EXPLODING_4;
3437 Store[x][y] = EL_PLAYER_IS_EXPLODING_1;
3442 if (PLAYERINFO(ex, ey)->use_murphy)
3443 Store[x][y] = EL_EMPTY;
3446 /* !!! check this case -- currently needed for rnd_rado_negundo_v,
3447 !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
3448 else if (ELEM_IS_PLAYER(center_element))
3449 Store[x][y] = EL_EMPTY;
3450 else if (center_element == EL_YAMYAM)
3451 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
3452 else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
3453 Store[x][y] = element_info[center_element].content.e[xx][yy];
3455 /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
3456 (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
3457 otherwise) -- FIX THIS !!! */
3458 else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
3459 Store[x][y] = element_info[element].content.e[1][1];
3461 else if (!CAN_EXPLODE(element))
3462 Store[x][y] = element_info[element].content.e[1][1];
3465 Store[x][y] = EL_EMPTY;
3467 else if (center_element == EL_MOLE)
3468 Store[x][y] = EL_EMERALD_RED;
3469 else if (center_element == EL_PENGUIN)
3470 Store[x][y] = EL_EMERALD_PURPLE;
3471 else if (center_element == EL_BUG)
3472 Store[x][y] = ((x == ex && y == ey) ? EL_DIAMOND : EL_EMERALD);
3473 else if (center_element == EL_BD_BUTTERFLY)
3474 Store[x][y] = EL_BD_DIAMOND;
3475 else if (center_element == EL_SP_ELECTRON)
3476 Store[x][y] = EL_SP_INFOTRON;
3477 else if (center_element == EL_AMOEBA_TO_DIAMOND)
3478 Store[x][y] = level.amoeba_content;
3479 else if (center_element == EL_YAMYAM)
3480 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
3481 else if (IS_CUSTOM_ELEMENT(center_element) &&
3482 element_info[center_element].content.e[xx][yy] != EL_EMPTY)
3483 Store[x][y] = element_info[center_element].content.e[xx][yy];
3484 else if (element == EL_WALL_EMERALD)
3485 Store[x][y] = EL_EMERALD;
3486 else if (element == EL_WALL_DIAMOND)
3487 Store[x][y] = EL_DIAMOND;
3488 else if (element == EL_WALL_BD_DIAMOND)
3489 Store[x][y] = EL_BD_DIAMOND;
3490 else if (element == EL_WALL_EMERALD_YELLOW)
3491 Store[x][y] = EL_EMERALD_YELLOW;
3492 else if (element == EL_WALL_EMERALD_RED)
3493 Store[x][y] = EL_EMERALD_RED;
3494 else if (element == EL_WALL_EMERALD_PURPLE)
3495 Store[x][y] = EL_EMERALD_PURPLE;
3496 else if (element == EL_WALL_PEARL)
3497 Store[x][y] = EL_PEARL;
3498 else if (element == EL_WALL_CRYSTAL)
3499 Store[x][y] = EL_CRYSTAL;
3500 else if (IS_CUSTOM_ELEMENT(element) && !CAN_EXPLODE(element))
3501 Store[x][y] = element_info[element].content.e[1][1];
3503 Store[x][y] = EL_EMPTY;
3506 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
3507 center_element == EL_AMOEBA_TO_DIAMOND)
3508 Store2[x][y] = element;
3510 Feld[x][y] = EL_EXPLOSION;
3511 GfxElement[x][y] = artwork_element;
3513 ExplodePhase[x][y] = 1;
3514 ExplodeDelay[x][y] = last_phase;
3519 if (center_element == EL_YAMYAM)
3520 game.yamyam_content_nr =
3521 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
3533 GfxFrame[x][y] = 0; /* restart explosion animation */
3535 last_phase = ExplodeDelay[x][y];
3537 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
3541 /* activate this even in non-DEBUG version until cause for crash in
3542 getGraphicAnimationFrame() (see below) is found and eliminated */
3547 if (GfxElement[x][y] == EL_UNDEFINED)
3550 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
3551 printf("Explode(): This should never happen!\n");
3554 GfxElement[x][y] = EL_EMPTY;
3558 border_element = Store2[x][y];
3559 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3560 border_element = StorePlayer[x][y];
3562 if (phase == element_info[border_element].ignition_delay ||
3563 phase == last_phase)
3565 boolean border_explosion = FALSE;
3567 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
3568 !PLAYER_EXPLOSION_PROTECTED(x, y))
3570 KillPlayerUnlessExplosionProtected(x, y);
3571 border_explosion = TRUE;
3573 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
3575 Feld[x][y] = Store2[x][y];
3578 border_explosion = TRUE;
3580 else if (border_element == EL_AMOEBA_TO_DIAMOND)
3582 AmoebeUmwandeln(x, y);
3584 border_explosion = TRUE;
3587 /* if an element just explodes due to another explosion (chain-reaction),
3588 do not immediately end the new explosion when it was the last frame of
3589 the explosion (as it would be done in the following "if"-statement!) */
3590 if (border_explosion && phase == last_phase)
3594 if (phase == last_phase)
3598 element = Feld[x][y] = Store[x][y];
3599 Store[x][y] = Store2[x][y] = 0;
3600 GfxElement[x][y] = EL_UNDEFINED;
3602 /* player can escape from explosions and might therefore be still alive */
3603 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
3604 element <= EL_PLAYER_IS_EXPLODING_4)
3606 int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
3607 int explosion_element = EL_PLAYER_1 + player_nr;
3608 int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
3609 int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
3611 if (level.use_explosion_element[player_nr])
3612 explosion_element = level.explosion_element[player_nr];
3614 Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
3615 element_info[explosion_element].content.e[xx][yy]);
3618 /* restore probably existing indestructible background element */
3619 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
3620 element = Feld[x][y] = Back[x][y];
3623 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
3624 GfxDir[x][y] = MV_NONE;
3625 ChangeDelay[x][y] = 0;
3626 ChangePage[x][y] = -1;
3628 #if USE_NEW_CUSTOM_VALUE
3629 CustomValue[x][y] = 0;
3632 InitField_WithBug2(x, y, FALSE);
3634 DrawLevelField(x, y);
3636 TestIfElementTouchesCustomElement(x, y);
3638 if (GFX_CRUMBLED(element))
3639 DrawLevelFieldCrumbledSandNeighbours(x, y);
3641 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
3642 StorePlayer[x][y] = 0;
3644 if (ELEM_IS_PLAYER(element))
3645 RelocatePlayer(x, y, element);
3647 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3649 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
3650 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
3653 DrawLevelFieldCrumbledSand(x, y);
3655 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
3657 DrawLevelElement(x, y, Back[x][y]);
3658 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
3660 else if (IS_WALKABLE_UNDER(Back[x][y]))
3662 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3663 DrawLevelElementThruMask(x, y, Back[x][y]);
3665 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
3666 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3670 void DynaExplode(int ex, int ey)
3673 int dynabomb_element = Feld[ex][ey];
3674 int dynabomb_size = 1;
3675 boolean dynabomb_xl = FALSE;
3676 struct PlayerInfo *player;
3677 static int xy[4][2] =
3685 if (IS_ACTIVE_BOMB(dynabomb_element))
3687 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
3688 dynabomb_size = player->dynabomb_size;
3689 dynabomb_xl = player->dynabomb_xl;
3690 player->dynabombs_left++;
3693 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
3695 for (i = 0; i < NUM_DIRECTIONS; i++)
3697 for (j = 1; j <= dynabomb_size; j++)
3699 int x = ex + j * xy[i][0];
3700 int y = ey + j * xy[i][1];
3703 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
3706 element = Feld[x][y];
3708 /* do not restart explosions of fields with active bombs */
3709 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
3712 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
3714 if (element != EL_EMPTY && element != EL_EXPLOSION &&
3715 !IS_DIGGABLE(element) && !dynabomb_xl)
3721 void Bang(int x, int y)
3723 int element = MovingOrBlocked2Element(x, y);
3724 int explosion_type = EX_TYPE_NORMAL;
3726 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3728 struct PlayerInfo *player = PLAYERINFO(x, y);
3730 element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
3731 player->element_nr);
3733 if (level.use_explosion_element[player->index_nr])
3735 int explosion_element = level.explosion_element[player->index_nr];
3737 if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
3738 explosion_type = EX_TYPE_CROSS;
3739 else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
3740 explosion_type = EX_TYPE_CENTER;
3748 case EL_BD_BUTTERFLY:
3751 case EL_DARK_YAMYAM:
3755 RaiseScoreElement(element);
3758 case EL_DYNABOMB_PLAYER_1_ACTIVE:
3759 case EL_DYNABOMB_PLAYER_2_ACTIVE:
3760 case EL_DYNABOMB_PLAYER_3_ACTIVE:
3761 case EL_DYNABOMB_PLAYER_4_ACTIVE:
3762 case EL_DYNABOMB_INCREASE_NUMBER:
3763 case EL_DYNABOMB_INCREASE_SIZE:
3764 case EL_DYNABOMB_INCREASE_POWER:
3765 explosion_type = EX_TYPE_DYNA;
3770 case EL_LAMP_ACTIVE:
3771 case EL_AMOEBA_TO_DIAMOND:
3772 if (!IS_PLAYER(x, y)) /* penguin and player may be at same field */
3773 explosion_type = EX_TYPE_CENTER;
3777 if (element_info[element].explosion_type == EXPLODES_CROSS)
3778 explosion_type = EX_TYPE_CROSS;
3779 else if (element_info[element].explosion_type == EXPLODES_1X1)
3780 explosion_type = EX_TYPE_CENTER;
3784 if (explosion_type == EX_TYPE_DYNA)
3787 Explode(x, y, EX_PHASE_START, explosion_type);
3789 CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
3792 void SplashAcid(int x, int y)
3794 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
3795 (!IN_LEV_FIELD(x - 1, y - 2) ||
3796 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
3797 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
3799 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
3800 (!IN_LEV_FIELD(x + 1, y - 2) ||
3801 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
3802 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
3804 PlayLevelSound(x, y, SND_ACID_SPLASHING);
3807 static void InitBeltMovement()
3809 static int belt_base_element[4] =
3811 EL_CONVEYOR_BELT_1_LEFT,
3812 EL_CONVEYOR_BELT_2_LEFT,
3813 EL_CONVEYOR_BELT_3_LEFT,
3814 EL_CONVEYOR_BELT_4_LEFT
3816 static int belt_base_active_element[4] =
3818 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
3819 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
3820 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
3821 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
3826 /* set frame order for belt animation graphic according to belt direction */
3827 for (i = 0; i < NUM_BELTS; i++)
3831 for (j = 0; j < NUM_BELT_PARTS; j++)
3833 int element = belt_base_active_element[belt_nr] + j;
3834 int graphic = el2img(element);
3836 if (game.belt_dir[i] == MV_LEFT)
3837 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
3839 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
3844 SCAN_PLAYFIELD(x, y)
3846 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
3849 int element = Feld[x][y];
3851 for (i = 0; i < NUM_BELTS; i++)
3853 if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
3855 int e_belt_nr = getBeltNrFromBeltElement(element);
3858 if (e_belt_nr == belt_nr)
3860 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
3862 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
3869 static void ToggleBeltSwitch(int x, int y)
3871 static int belt_base_element[4] =
3873 EL_CONVEYOR_BELT_1_LEFT,
3874 EL_CONVEYOR_BELT_2_LEFT,
3875 EL_CONVEYOR_BELT_3_LEFT,
3876 EL_CONVEYOR_BELT_4_LEFT
3878 static int belt_base_active_element[4] =
3880 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
3881 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
3882 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
3883 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
3885 static int belt_base_switch_element[4] =
3887 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
3888 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
3889 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
3890 EL_CONVEYOR_BELT_4_SWITCH_LEFT
3892 static int belt_move_dir[4] =
3900 int element = Feld[x][y];
3901 int belt_nr = getBeltNrFromBeltSwitchElement(element);
3902 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
3903 int belt_dir = belt_move_dir[belt_dir_nr];
3906 if (!IS_BELT_SWITCH(element))
3909 game.belt_dir_nr[belt_nr] = belt_dir_nr;
3910 game.belt_dir[belt_nr] = belt_dir;
3912 if (belt_dir_nr == 3)
3915 /* set frame order for belt animation graphic according to belt direction */
3916 for (i = 0; i < NUM_BELT_PARTS; i++)
3918 int element = belt_base_active_element[belt_nr] + i;
3919 int graphic = el2img(element);
3921 if (belt_dir == MV_LEFT)
3922 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
3924 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
3928 SCAN_PLAYFIELD(xx, yy)
3930 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
3933 int element = Feld[xx][yy];
3935 if (IS_BELT_SWITCH(element))
3937 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
3939 if (e_belt_nr == belt_nr)
3941 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
3942 DrawLevelField(xx, yy);
3945 else if (IS_BELT(element) && belt_dir != MV_NONE)
3947 int e_belt_nr = getBeltNrFromBeltElement(element);
3949 if (e_belt_nr == belt_nr)
3951 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
3953 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
3954 DrawLevelField(xx, yy);
3957 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
3959 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
3961 if (e_belt_nr == belt_nr)
3963 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
3965 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
3966 DrawLevelField(xx, yy);
3972 static void ToggleSwitchgateSwitch(int x, int y)
3976 game.switchgate_pos = !game.switchgate_pos;
3979 SCAN_PLAYFIELD(xx, yy)
3981 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
3984 int element = Feld[xx][yy];
3986 if (element == EL_SWITCHGATE_SWITCH_UP ||
3987 element == EL_SWITCHGATE_SWITCH_DOWN)
3989 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
3990 DrawLevelField(xx, yy);
3992 else if (element == EL_SWITCHGATE_OPEN ||
3993 element == EL_SWITCHGATE_OPENING)
3995 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
3997 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
3999 else if (element == EL_SWITCHGATE_CLOSED ||
4000 element == EL_SWITCHGATE_CLOSING)
4002 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
4004 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
4009 static int getInvisibleActiveFromInvisibleElement(int element)
4011 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
4012 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
4013 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
4017 static int getInvisibleFromInvisibleActiveElement(int element)
4019 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
4020 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
4021 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
4025 static void RedrawAllLightSwitchesAndInvisibleElements()
4030 SCAN_PLAYFIELD(x, y)
4032 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
4035 int element = Feld[x][y];
4037 if (element == EL_LIGHT_SWITCH &&
4038 game.light_time_left > 0)
4040 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
4041 DrawLevelField(x, y);
4043 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
4044 game.light_time_left == 0)
4046 Feld[x][y] = EL_LIGHT_SWITCH;
4047 DrawLevelField(x, y);
4049 else if (element == EL_EMC_DRIPPER &&
4050 game.light_time_left > 0)
4052 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
4053 DrawLevelField(x, y);
4055 else if (element == EL_EMC_DRIPPER_ACTIVE &&
4056 game.light_time_left == 0)
4058 Feld[x][y] = EL_EMC_DRIPPER;
4059 DrawLevelField(x, y);
4061 else if (element == EL_INVISIBLE_STEELWALL ||
4062 element == EL_INVISIBLE_WALL ||
4063 element == EL_INVISIBLE_SAND)
4065 if (game.light_time_left > 0)
4066 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
4068 DrawLevelField(x, y);
4070 /* uncrumble neighbour fields, if needed */
4071 if (element == EL_INVISIBLE_SAND)
4072 DrawLevelFieldCrumbledSandNeighbours(x, y);
4074 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
4075 element == EL_INVISIBLE_WALL_ACTIVE ||
4076 element == EL_INVISIBLE_SAND_ACTIVE)
4078 if (game.light_time_left == 0)
4079 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
4081 DrawLevelField(x, y);
4083 /* re-crumble neighbour fields, if needed */
4084 if (element == EL_INVISIBLE_SAND)
4085 DrawLevelFieldCrumbledSandNeighbours(x, y);
4090 static void RedrawAllInvisibleElementsForLenses()
4095 SCAN_PLAYFIELD(x, y)
4097 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
4100 int element = Feld[x][y];
4102 if (element == EL_EMC_DRIPPER &&
4103 game.lenses_time_left > 0)
4105 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
4106 DrawLevelField(x, y);
4108 else if (element == EL_EMC_DRIPPER_ACTIVE &&
4109 game.lenses_time_left == 0)
4111 Feld[x][y] = EL_EMC_DRIPPER;
4112 DrawLevelField(x, y);
4114 else if (element == EL_INVISIBLE_STEELWALL ||
4115 element == EL_INVISIBLE_WALL ||
4116 element == EL_INVISIBLE_SAND)
4118 if (game.lenses_time_left > 0)
4119 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
4121 DrawLevelField(x, y);
4123 /* uncrumble neighbour fields, if needed */
4124 if (element == EL_INVISIBLE_SAND)
4125 DrawLevelFieldCrumbledSandNeighbours(x, y);
4127 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
4128 element == EL_INVISIBLE_WALL_ACTIVE ||
4129 element == EL_INVISIBLE_SAND_ACTIVE)
4131 if (game.lenses_time_left == 0)
4132 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
4134 DrawLevelField(x, y);
4136 /* re-crumble neighbour fields, if needed */
4137 if (element == EL_INVISIBLE_SAND)
4138 DrawLevelFieldCrumbledSandNeighbours(x, y);
4143 static void RedrawAllInvisibleElementsForMagnifier()
4148 SCAN_PLAYFIELD(x, y)
4150 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
4153 int element = Feld[x][y];
4155 if (element == EL_EMC_FAKE_GRASS &&
4156 game.magnify_time_left > 0)
4158 Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
4159 DrawLevelField(x, y);
4161 else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
4162 game.magnify_time_left == 0)
4164 Feld[x][y] = EL_EMC_FAKE_GRASS;
4165 DrawLevelField(x, y);
4167 else if (IS_GATE_GRAY(element) &&
4168 game.magnify_time_left > 0)
4170 Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
4171 element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
4172 IS_EM_GATE_GRAY(element) ?
4173 element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
4174 IS_EMC_GATE_GRAY(element) ?
4175 element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
4177 DrawLevelField(x, y);
4179 else if (IS_GATE_GRAY_ACTIVE(element) &&
4180 game.magnify_time_left == 0)
4182 Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
4183 element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
4184 IS_EM_GATE_GRAY_ACTIVE(element) ?
4185 element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
4186 IS_EMC_GATE_GRAY_ACTIVE(element) ?
4187 element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
4189 DrawLevelField(x, y);
4194 static void ToggleLightSwitch(int x, int y)
4196 int element = Feld[x][y];
4198 game.light_time_left =
4199 (element == EL_LIGHT_SWITCH ?
4200 level.time_light * FRAMES_PER_SECOND : 0);
4202 RedrawAllLightSwitchesAndInvisibleElements();
4205 static void ActivateTimegateSwitch(int x, int y)
4209 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
4212 SCAN_PLAYFIELD(xx, yy)
4214 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
4217 int element = Feld[xx][yy];
4219 if (element == EL_TIMEGATE_CLOSED ||
4220 element == EL_TIMEGATE_CLOSING)
4222 Feld[xx][yy] = EL_TIMEGATE_OPENING;
4223 PlayLevelSound(xx, yy, SND_TIMEGATE_OPENING);
4227 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
4229 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
4230 DrawLevelField(xx, yy);
4236 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
4239 void Impact(int x, int y)
4241 boolean last_line = (y == lev_fieldy - 1);
4242 boolean object_hit = FALSE;
4243 boolean impact = (last_line || object_hit);
4244 int element = Feld[x][y];
4245 int smashed = EL_STEELWALL;
4247 if (!last_line) /* check if element below was hit */
4249 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
4252 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
4253 MovDir[x][y + 1] != MV_DOWN ||
4254 MovPos[x][y + 1] <= TILEY / 2));
4256 /* do not smash moving elements that left the smashed field in time */
4257 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
4258 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
4261 #if USE_QUICKSAND_IMPACT_BUGFIX
4262 if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
4264 RemoveMovingField(x, y + 1);
4265 Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
4266 Feld[x][y + 2] = EL_ROCK;
4267 DrawLevelField(x, y + 2);
4274 smashed = MovingOrBlocked2Element(x, y + 1);
4276 impact = (last_line || object_hit);
4279 if (!last_line && smashed == EL_ACID) /* element falls into acid */
4281 SplashAcid(x, y + 1);
4285 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
4286 /* only reset graphic animation if graphic really changes after impact */
4288 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
4290 ResetGfxAnimation(x, y);
4291 DrawLevelField(x, y);
4294 if (impact && CAN_EXPLODE_IMPACT(element))
4299 else if (impact && element == EL_PEARL)
4301 ResetGfxAnimation(x, y);
4303 Feld[x][y] = EL_PEARL_BREAKING;
4304 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4307 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
4309 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4314 if (impact && element == EL_AMOEBA_DROP)
4316 if (object_hit && IS_PLAYER(x, y + 1))
4317 KillPlayerUnlessEnemyProtected(x, y + 1);
4318 else if (object_hit && smashed == EL_PENGUIN)
4322 Feld[x][y] = EL_AMOEBA_GROWING;
4323 Store[x][y] = EL_AMOEBA_WET;
4325 ResetRandomAnimationValue(x, y);
4330 if (object_hit) /* check which object was hit */
4332 if (CAN_PASS_MAGIC_WALL(element) &&
4333 (smashed == EL_MAGIC_WALL ||
4334 smashed == EL_BD_MAGIC_WALL))
4337 int activated_magic_wall =
4338 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
4339 EL_BD_MAGIC_WALL_ACTIVE);
4341 /* activate magic wall / mill */
4343 SCAN_PLAYFIELD(xx, yy)
4345 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
4347 if (Feld[xx][yy] == smashed)
4348 Feld[xx][yy] = activated_magic_wall;
4350 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
4351 game.magic_wall_active = TRUE;
4353 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
4354 SND_MAGIC_WALL_ACTIVATING :
4355 SND_BD_MAGIC_WALL_ACTIVATING));
4358 if (IS_PLAYER(x, y + 1))
4360 if (CAN_SMASH_PLAYER(element))
4362 KillPlayerUnlessEnemyProtected(x, y + 1);
4366 else if (smashed == EL_PENGUIN)
4368 if (CAN_SMASH_PLAYER(element))
4374 else if (element == EL_BD_DIAMOND)
4376 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
4382 else if (((element == EL_SP_INFOTRON ||
4383 element == EL_SP_ZONK) &&
4384 (smashed == EL_SP_SNIKSNAK ||
4385 smashed == EL_SP_ELECTRON ||
4386 smashed == EL_SP_DISK_ORANGE)) ||
4387 (element == EL_SP_INFOTRON &&
4388 smashed == EL_SP_DISK_YELLOW))
4393 else if (CAN_SMASH_EVERYTHING(element))
4395 if (IS_CLASSIC_ENEMY(smashed) ||
4396 CAN_EXPLODE_SMASHED(smashed))
4401 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
4403 if (smashed == EL_LAMP ||
4404 smashed == EL_LAMP_ACTIVE)
4409 else if (smashed == EL_NUT)
4411 Feld[x][y + 1] = EL_NUT_BREAKING;
4412 PlayLevelSound(x, y, SND_NUT_BREAKING);
4413 RaiseScoreElement(EL_NUT);
4416 else if (smashed == EL_PEARL)
4418 ResetGfxAnimation(x, y);
4420 Feld[x][y + 1] = EL_PEARL_BREAKING;
4421 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4424 else if (smashed == EL_DIAMOND)
4426 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
4427 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
4430 else if (IS_BELT_SWITCH(smashed))
4432 ToggleBeltSwitch(x, y + 1);
4434 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
4435 smashed == EL_SWITCHGATE_SWITCH_DOWN)
4437 ToggleSwitchgateSwitch(x, y + 1);
4439 else if (smashed == EL_LIGHT_SWITCH ||
4440 smashed == EL_LIGHT_SWITCH_ACTIVE)
4442 ToggleLightSwitch(x, y + 1);
4447 TestIfElementSmashesCustomElement(x, y, MV_DOWN);
4450 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4452 CheckElementChangeBySide(x, y + 1, smashed, element,
4453 CE_SWITCHED, CH_SIDE_TOP);
4454 CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
4460 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4465 /* play sound of magic wall / mill */
4467 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
4468 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
4470 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
4471 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
4472 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
4473 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
4478 /* play sound of object that hits the ground */
4479 if (last_line || object_hit)
4480 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4483 inline static void TurnRoundExt(int x, int y)
4495 { 0, 0 }, { 0, 0 }, { 0, 0 },
4500 int left, right, back;
4504 { MV_DOWN, MV_UP, MV_RIGHT },
4505 { MV_UP, MV_DOWN, MV_LEFT },
4507 { MV_LEFT, MV_RIGHT, MV_DOWN },
4511 { MV_RIGHT, MV_LEFT, MV_UP }
4514 int element = Feld[x][y];
4515 int move_pattern = element_info[element].move_pattern;
4517 int old_move_dir = MovDir[x][y];
4518 int left_dir = turn[old_move_dir].left;
4519 int right_dir = turn[old_move_dir].right;
4520 int back_dir = turn[old_move_dir].back;
4522 int left_dx = move_xy[left_dir].dx, left_dy = move_xy[left_dir].dy;
4523 int right_dx = move_xy[right_dir].dx, right_dy = move_xy[right_dir].dy;
4524 int move_dx = move_xy[old_move_dir].dx, move_dy = move_xy[old_move_dir].dy;
4525 int back_dx = move_xy[back_dir].dx, back_dy = move_xy[back_dir].dy;
4527 int left_x = x + left_dx, left_y = y + left_dy;
4528 int right_x = x + right_dx, right_y = y + right_dy;
4529 int move_x = x + move_dx, move_y = y + move_dy;
4533 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4535 TestIfBadThingTouchesOtherBadThing(x, y);
4537 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
4538 MovDir[x][y] = right_dir;
4539 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4540 MovDir[x][y] = left_dir;
4542 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
4544 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
4547 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
4549 TestIfBadThingTouchesOtherBadThing(x, y);
4551 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
4552 MovDir[x][y] = left_dir;
4553 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4554 MovDir[x][y] = right_dir;
4556 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
4558 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
4561 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4563 TestIfBadThingTouchesOtherBadThing(x, y);
4565 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
4566 MovDir[x][y] = left_dir;
4567 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
4568 MovDir[x][y] = right_dir;
4570 if (MovDir[x][y] != old_move_dir)
4573 else if (element == EL_YAMYAM)
4575 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
4576 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
4578 if (can_turn_left && can_turn_right)
4579 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4580 else if (can_turn_left)
4581 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4582 else if (can_turn_right)
4583 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4585 MovDir[x][y] = back_dir;
4587 MovDelay[x][y] = 16 + 16 * RND(3);
4589 else if (element == EL_DARK_YAMYAM)
4591 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4593 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4596 if (can_turn_left && can_turn_right)
4597 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4598 else if (can_turn_left)
4599 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4600 else if (can_turn_right)
4601 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4603 MovDir[x][y] = back_dir;
4605 MovDelay[x][y] = 16 + 16 * RND(3);
4607 else if (element == EL_PACMAN)
4609 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
4610 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
4612 if (can_turn_left && can_turn_right)
4613 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4614 else if (can_turn_left)
4615 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4616 else if (can_turn_right)
4617 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4619 MovDir[x][y] = back_dir;
4621 MovDelay[x][y] = 6 + RND(40);
4623 else if (element == EL_PIG)
4625 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
4626 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
4627 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
4628 boolean should_turn_left, should_turn_right, should_move_on;
4630 int rnd = RND(rnd_value);
4632 should_turn_left = (can_turn_left &&
4634 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
4635 y + back_dy + left_dy)));
4636 should_turn_right = (can_turn_right &&
4638 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
4639 y + back_dy + right_dy)));
4640 should_move_on = (can_move_on &&
4643 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
4644 y + move_dy + left_dy) ||
4645 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
4646 y + move_dy + right_dy)));
4648 if (should_turn_left || should_turn_right || should_move_on)
4650 if (should_turn_left && should_turn_right && should_move_on)
4651 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
4652 rnd < 2 * rnd_value / 3 ? right_dir :
4654 else if (should_turn_left && should_turn_right)
4655 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4656 else if (should_turn_left && should_move_on)
4657 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
4658 else if (should_turn_right && should_move_on)
4659 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
4660 else if (should_turn_left)
4661 MovDir[x][y] = left_dir;
4662 else if (should_turn_right)
4663 MovDir[x][y] = right_dir;
4664 else if (should_move_on)
4665 MovDir[x][y] = old_move_dir;
4667 else if (can_move_on && rnd > rnd_value / 8)
4668 MovDir[x][y] = old_move_dir;
4669 else if (can_turn_left && can_turn_right)
4670 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4671 else if (can_turn_left && rnd > rnd_value / 8)
4672 MovDir[x][y] = left_dir;
4673 else if (can_turn_right && rnd > rnd_value/8)
4674 MovDir[x][y] = right_dir;
4676 MovDir[x][y] = back_dir;
4678 xx = x + move_xy[MovDir[x][y]].dx;
4679 yy = y + move_xy[MovDir[x][y]].dy;
4681 if (!IN_LEV_FIELD(xx, yy) ||
4682 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
4683 MovDir[x][y] = old_move_dir;
4687 else if (element == EL_DRAGON)
4689 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
4690 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
4691 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
4693 int rnd = RND(rnd_value);
4695 if (can_move_on && rnd > rnd_value / 8)
4696 MovDir[x][y] = old_move_dir;
4697 else if (can_turn_left && can_turn_right)
4698 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4699 else if (can_turn_left && rnd > rnd_value / 8)
4700 MovDir[x][y] = left_dir;
4701 else if (can_turn_right && rnd > rnd_value / 8)
4702 MovDir[x][y] = right_dir;
4704 MovDir[x][y] = back_dir;
4706 xx = x + move_xy[MovDir[x][y]].dx;
4707 yy = y + move_xy[MovDir[x][y]].dy;
4709 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
4710 MovDir[x][y] = old_move_dir;
4714 else if (element == EL_MOLE)
4716 boolean can_move_on =
4717 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
4718 IS_AMOEBOID(Feld[move_x][move_y]) ||
4719 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
4722 boolean can_turn_left =
4723 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
4724 IS_AMOEBOID(Feld[left_x][left_y])));
4726 boolean can_turn_right =
4727 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
4728 IS_AMOEBOID(Feld[right_x][right_y])));
4730 if (can_turn_left && can_turn_right)
4731 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
4732 else if (can_turn_left)
4733 MovDir[x][y] = left_dir;
4735 MovDir[x][y] = right_dir;
4738 if (MovDir[x][y] != old_move_dir)
4741 else if (element == EL_BALLOON)
4743 MovDir[x][y] = game.wind_direction;
4746 else if (element == EL_SPRING)
4748 #if USE_NEW_SPRING_BUMPER
4749 if (MovDir[x][y] & MV_HORIZONTAL)
4751 if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
4752 !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
4754 Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
4755 ResetGfxAnimation(move_x, move_y);
4756 DrawLevelField(move_x, move_y);
4758 MovDir[x][y] = back_dir;
4760 else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
4761 SPRING_CAN_ENTER_FIELD(element, x, y + 1))
4762 MovDir[x][y] = MV_NONE;
4765 if (MovDir[x][y] & MV_HORIZONTAL &&
4766 (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
4767 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
4768 MovDir[x][y] = MV_NONE;
4773 else if (element == EL_ROBOT ||
4774 element == EL_SATELLITE ||
4775 element == EL_PENGUIN ||
4776 element == EL_EMC_ANDROID)
4778 int attr_x = -1, attr_y = -1;
4789 for (i = 0; i < MAX_PLAYERS; i++)
4791 struct PlayerInfo *player = &stored_player[i];
4792 int jx = player->jx, jy = player->jy;
4794 if (!player->active)
4798 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
4806 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
4807 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
4808 game.engine_version < VERSION_IDENT(3,1,0,0)))
4814 if (element == EL_PENGUIN)
4817 static int xy[4][2] =
4825 for (i = 0; i < NUM_DIRECTIONS; i++)
4827 int ex = x + xy[i][0];
4828 int ey = y + xy[i][1];
4830 if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
4839 MovDir[x][y] = MV_NONE;
4841 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
4842 else if (attr_x > x)
4843 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
4845 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
4846 else if (attr_y > y)
4847 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
4849 if (element == EL_ROBOT)
4853 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4854 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
4855 Moving2Blocked(x, y, &newx, &newy);
4857 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
4858 MovDelay[x][y] = 8 + 8 * !RND(3);
4860 MovDelay[x][y] = 16;
4862 else if (element == EL_PENGUIN)
4868 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4870 boolean first_horiz = RND(2);
4871 int new_move_dir = MovDir[x][y];
4874 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4875 Moving2Blocked(x, y, &newx, &newy);
4877 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
4881 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4882 Moving2Blocked(x, y, &newx, &newy);
4884 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
4887 MovDir[x][y] = old_move_dir;
4891 else if (element == EL_SATELLITE)
4897 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4899 boolean first_horiz = RND(2);
4900 int new_move_dir = MovDir[x][y];
4903 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4904 Moving2Blocked(x, y, &newx, &newy);
4906 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
4910 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4911 Moving2Blocked(x, y, &newx, &newy);
4913 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
4916 MovDir[x][y] = old_move_dir;
4920 else if (element == EL_EMC_ANDROID)
4922 static int check_pos[16] =
4924 -1, /* 0 => (invalid) */
4925 7, /* 1 => MV_LEFT */
4926 3, /* 2 => MV_RIGHT */
4927 -1, /* 3 => (invalid) */
4929 0, /* 5 => MV_LEFT | MV_UP */
4930 2, /* 6 => MV_RIGHT | MV_UP */
4931 -1, /* 7 => (invalid) */
4932 5, /* 8 => MV_DOWN */
4933 6, /* 9 => MV_LEFT | MV_DOWN */
4934 4, /* 10 => MV_RIGHT | MV_DOWN */
4935 -1, /* 11 => (invalid) */
4936 -1, /* 12 => (invalid) */
4937 -1, /* 13 => (invalid) */
4938 -1, /* 14 => (invalid) */
4939 -1, /* 15 => (invalid) */
4947 { -1, -1, MV_LEFT | MV_UP },
4949 { +1, -1, MV_RIGHT | MV_UP },
4950 { +1, 0, MV_RIGHT },
4951 { +1, +1, MV_RIGHT | MV_DOWN },
4953 { -1, +1, MV_LEFT | MV_DOWN },
4956 int start_pos, check_order;
4957 boolean can_clone = FALSE;
4960 /* check if there is any free field around current position */
4961 for (i = 0; i < 8; i++)
4963 int newx = x + check_xy[i].dx;
4964 int newy = y + check_xy[i].dy;
4966 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
4974 if (can_clone) /* randomly find an element to clone */
4978 start_pos = check_pos[RND(8)];
4979 check_order = (RND(2) ? -1 : +1);
4981 for (i = 0; i < 8; i++)
4983 int pos_raw = start_pos + i * check_order;
4984 int pos = (pos_raw + 8) % 8;
4985 int newx = x + check_xy[pos].dx;
4986 int newy = y + check_xy[pos].dy;
4988 if (ANDROID_CAN_CLONE_FIELD(newx, newy))
4990 element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
4991 element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
4993 Store[x][y] = Feld[newx][newy];
5002 if (can_clone) /* randomly find a direction to move */
5006 start_pos = check_pos[RND(8)];
5007 check_order = (RND(2) ? -1 : +1);
5009 for (i = 0; i < 8; i++)
5011 int pos_raw = start_pos + i * check_order;
5012 int pos = (pos_raw + 8) % 8;
5013 int newx = x + check_xy[pos].dx;
5014 int newy = y + check_xy[pos].dy;
5015 int new_move_dir = check_xy[pos].dir;
5017 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5019 MovDir[x][y] = new_move_dir;
5020 MovDelay[x][y] = level.android_clone_time * 8 + 1;
5029 if (can_clone) /* cloning and moving successful */
5032 /* cannot clone -- try to move towards player */
5034 start_pos = check_pos[MovDir[x][y] & 0x0f];
5035 check_order = (RND(2) ? -1 : +1);
5037 for (i = 0; i < 3; i++)
5039 /* first check start_pos, then previous/next or (next/previous) pos */
5040 int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
5041 int pos = (pos_raw + 8) % 8;
5042 int newx = x + check_xy[pos].dx;
5043 int newy = y + check_xy[pos].dy;
5044 int new_move_dir = check_xy[pos].dir;
5046 if (IS_PLAYER(newx, newy))
5049 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
5051 MovDir[x][y] = new_move_dir;
5052 MovDelay[x][y] = level.android_move_time * 8 + 1;
5059 else if (move_pattern == MV_TURNING_LEFT ||
5060 move_pattern == MV_TURNING_RIGHT ||
5061 move_pattern == MV_TURNING_LEFT_RIGHT ||
5062 move_pattern == MV_TURNING_RIGHT_LEFT ||
5063 move_pattern == MV_TURNING_RANDOM ||
5064 move_pattern == MV_ALL_DIRECTIONS)
5066 boolean can_turn_left =
5067 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
5068 boolean can_turn_right =
5069 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
5071 if (element_info[element].move_stepsize == 0) /* "not moving" */
5074 if (move_pattern == MV_TURNING_LEFT)
5075 MovDir[x][y] = left_dir;
5076 else if (move_pattern == MV_TURNING_RIGHT)
5077 MovDir[x][y] = right_dir;
5078 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
5079 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
5080 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
5081 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
5082 else if (move_pattern == MV_TURNING_RANDOM)
5083 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
5084 can_turn_right && !can_turn_left ? right_dir :
5085 RND(2) ? left_dir : right_dir);
5086 else if (can_turn_left && can_turn_right)
5087 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5088 else if (can_turn_left)
5089 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5090 else if (can_turn_right)
5091 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5093 MovDir[x][y] = back_dir;
5095 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5097 else if (move_pattern == MV_HORIZONTAL ||
5098 move_pattern == MV_VERTICAL)
5100 if (move_pattern & old_move_dir)
5101 MovDir[x][y] = back_dir;
5102 else if (move_pattern == MV_HORIZONTAL)
5103 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
5104 else if (move_pattern == MV_VERTICAL)
5105 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
5107 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5109 else if (move_pattern & MV_ANY_DIRECTION)
5111 MovDir[x][y] = move_pattern;
5112 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5114 else if (move_pattern & MV_WIND_DIRECTION)
5116 MovDir[x][y] = game.wind_direction;
5117 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5119 else if (move_pattern == MV_ALONG_LEFT_SIDE)
5121 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
5122 MovDir[x][y] = left_dir;
5123 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5124 MovDir[x][y] = right_dir;
5126 if (MovDir[x][y] != old_move_dir)
5127 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5129 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
5131 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
5132 MovDir[x][y] = right_dir;
5133 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5134 MovDir[x][y] = left_dir;
5136 if (MovDir[x][y] != old_move_dir)
5137 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5139 else if (move_pattern == MV_TOWARDS_PLAYER ||
5140 move_pattern == MV_AWAY_FROM_PLAYER)
5142 int attr_x = -1, attr_y = -1;
5144 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
5155 for (i = 0; i < MAX_PLAYERS; i++)
5157 struct PlayerInfo *player = &stored_player[i];
5158 int jx = player->jx, jy = player->jy;
5160 if (!player->active)
5164 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
5172 MovDir[x][y] = MV_NONE;
5174 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
5175 else if (attr_x > x)
5176 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
5178 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
5179 else if (attr_y > y)
5180 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
5182 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5184 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5186 boolean first_horiz = RND(2);
5187 int new_move_dir = MovDir[x][y];
5189 if (element_info[element].move_stepsize == 0) /* "not moving" */
5191 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
5192 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5198 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5199 Moving2Blocked(x, y, &newx, &newy);
5201 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5205 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5206 Moving2Blocked(x, y, &newx, &newy);
5208 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5211 MovDir[x][y] = old_move_dir;
5214 else if (move_pattern == MV_WHEN_PUSHED ||
5215 move_pattern == MV_WHEN_DROPPED)
5217 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5218 MovDir[x][y] = MV_NONE;
5222 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
5224 static int test_xy[7][2] =
5234 static int test_dir[7] =
5244 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
5245 int move_preference = -1000000; /* start with very low preference */
5246 int new_move_dir = MV_NONE;
5247 int start_test = RND(4);
5250 for (i = 0; i < NUM_DIRECTIONS; i++)
5252 int move_dir = test_dir[start_test + i];
5253 int move_dir_preference;
5255 xx = x + test_xy[start_test + i][0];
5256 yy = y + test_xy[start_test + i][1];
5258 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
5259 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
5261 new_move_dir = move_dir;
5266 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
5269 move_dir_preference = -1 * RunnerVisit[xx][yy];
5270 if (hunter_mode && PlayerVisit[xx][yy] > 0)
5271 move_dir_preference = PlayerVisit[xx][yy];
5273 if (move_dir_preference > move_preference)
5275 /* prefer field that has not been visited for the longest time */
5276 move_preference = move_dir_preference;
5277 new_move_dir = move_dir;
5279 else if (move_dir_preference == move_preference &&
5280 move_dir == old_move_dir)
5282 /* prefer last direction when all directions are preferred equally */
5283 move_preference = move_dir_preference;
5284 new_move_dir = move_dir;
5288 MovDir[x][y] = new_move_dir;
5289 if (old_move_dir != new_move_dir)
5290 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5294 static void TurnRound(int x, int y)
5296 int direction = MovDir[x][y];
5298 int element, graphic;
5303 GfxDir[x][y] = MovDir[x][y];
5305 if (direction != MovDir[x][y])
5309 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
5312 element = Feld[x][y];
5313 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5315 if (graphic_info[graphic].anim_global_sync)
5316 GfxFrame[x][y] = FrameCounter;
5317 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5318 GfxFrame[x][y] = CustomValue[x][y];
5319 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5320 GfxFrame[x][y] = element_info[element].collect_score;
5324 static boolean JustBeingPushed(int x, int y)
5328 for (i = 0; i < MAX_PLAYERS; i++)
5330 struct PlayerInfo *player = &stored_player[i];
5332 if (player->active && player->is_pushing && player->MovPos)
5334 int next_jx = player->jx + (player->jx - player->last_jx);
5335 int next_jy = player->jy + (player->jy - player->last_jy);
5337 if (x == next_jx && y == next_jy)
5345 void StartMoving(int x, int y)
5347 boolean started_moving = FALSE; /* some elements can fall _and_ move */
5348 int element = Feld[x][y];
5353 if (MovDelay[x][y] == 0)
5354 GfxAction[x][y] = ACTION_DEFAULT;
5356 if (CAN_FALL(element) && y < lev_fieldy - 1)
5358 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
5359 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
5360 if (JustBeingPushed(x, y))
5363 if (element == EL_QUICKSAND_FULL)
5365 if (IS_FREE(x, y + 1))
5367 InitMovingField(x, y, MV_DOWN);
5368 started_moving = TRUE;
5370 Feld[x][y] = EL_QUICKSAND_EMPTYING;
5371 Store[x][y] = EL_ROCK;
5373 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
5375 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5377 if (!MovDelay[x][y])
5378 MovDelay[x][y] = TILEY + 1;
5387 Feld[x][y] = EL_QUICKSAND_EMPTY;
5388 Feld[x][y + 1] = EL_QUICKSAND_FULL;
5389 Store[x][y + 1] = Store[x][y];
5392 PlayLevelSoundAction(x, y, ACTION_FILLING);
5395 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
5396 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5398 InitMovingField(x, y, MV_DOWN);
5399 started_moving = TRUE;
5401 Feld[x][y] = EL_QUICKSAND_FILLING;
5402 Store[x][y] = element;
5404 PlayLevelSoundAction(x, y, ACTION_FILLING);
5406 else if (element == EL_MAGIC_WALL_FULL)
5408 if (IS_FREE(x, y + 1))
5410 InitMovingField(x, y, MV_DOWN);
5411 started_moving = TRUE;
5413 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
5414 Store[x][y] = EL_CHANGED(Store[x][y]);
5416 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
5418 if (!MovDelay[x][y])
5419 MovDelay[x][y] = TILEY/4 + 1;
5428 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
5429 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
5430 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
5434 else if (element == EL_BD_MAGIC_WALL_FULL)
5436 if (IS_FREE(x, y + 1))
5438 InitMovingField(x, y, MV_DOWN);
5439 started_moving = TRUE;
5441 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
5442 Store[x][y] = EL_CHANGED2(Store[x][y]);
5444 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
5446 if (!MovDelay[x][y])
5447 MovDelay[x][y] = TILEY/4 + 1;
5456 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
5457 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
5458 Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
5462 else if (CAN_PASS_MAGIC_WALL(element) &&
5463 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
5464 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
5466 InitMovingField(x, y, MV_DOWN);
5467 started_moving = TRUE;
5470 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
5471 EL_BD_MAGIC_WALL_FILLING);
5472 Store[x][y] = element;
5474 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
5476 SplashAcid(x, y + 1);
5478 InitMovingField(x, y, MV_DOWN);
5479 started_moving = TRUE;
5481 Store[x][y] = EL_ACID;
5483 else if ((game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5484 CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
5486 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
5487 CAN_FALL(element) && WasJustFalling[x][y] &&
5488 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
5490 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
5491 CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
5492 (Feld[x][y + 1] == EL_BLOCKED)))
5494 /* this is needed for a special case not covered by calling "Impact()"
5495 from "ContinueMoving()": if an element moves to a tile directly below
5496 another element which was just falling on that tile (which was empty
5497 in the previous frame), the falling element above would just stop
5498 instead of smashing the element below (in previous version, the above
5499 element was just checked for "moving" instead of "falling", resulting
5500 in incorrect smashes caused by horizontal movement of the above
5501 element; also, the case of the player being the element to smash was
5502 simply not covered here... :-/ ) */
5504 CheckCollision[x][y] = 0;
5508 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
5510 if (MovDir[x][y] == MV_NONE)
5512 InitMovingField(x, y, MV_DOWN);
5513 started_moving = TRUE;
5516 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
5518 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
5519 MovDir[x][y] = MV_DOWN;
5521 InitMovingField(x, y, MV_DOWN);
5522 started_moving = TRUE;
5524 else if (element == EL_AMOEBA_DROP)
5526 Feld[x][y] = EL_AMOEBA_GROWING;
5527 Store[x][y] = EL_AMOEBA_WET;
5529 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
5530 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
5531 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
5532 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
5534 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
5535 (IS_FREE(x - 1, y + 1) ||
5536 Feld[x - 1][y + 1] == EL_ACID));
5537 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
5538 (IS_FREE(x + 1, y + 1) ||
5539 Feld[x + 1][y + 1] == EL_ACID));
5540 boolean can_fall_any = (can_fall_left || can_fall_right);
5541 boolean can_fall_both = (can_fall_left && can_fall_right);
5542 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
5544 #if USE_NEW_ALL_SLIPPERY
5545 if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
5547 if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
5548 can_fall_right = FALSE;
5549 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
5550 can_fall_left = FALSE;
5551 else if (slippery_type == SLIPPERY_ONLY_LEFT)
5552 can_fall_right = FALSE;
5553 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
5554 can_fall_left = FALSE;
5556 can_fall_any = (can_fall_left || can_fall_right);
5557 can_fall_both = FALSE;
5560 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
5562 if (slippery_type == SLIPPERY_ONLY_LEFT)
5563 can_fall_right = FALSE;
5564 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
5565 can_fall_left = FALSE;
5566 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
5567 can_fall_right = FALSE;
5568 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
5569 can_fall_left = FALSE;
5571 can_fall_any = (can_fall_left || can_fall_right);
5572 can_fall_both = (can_fall_left && can_fall_right);
5576 #if USE_NEW_ALL_SLIPPERY
5578 #if USE_NEW_SP_SLIPPERY
5579 /* !!! better use the same properties as for custom elements here !!! */
5580 else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
5581 can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
5583 can_fall_right = FALSE; /* slip down on left side */
5584 can_fall_both = FALSE;
5589 #if USE_NEW_ALL_SLIPPERY
5592 if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
5593 can_fall_right = FALSE; /* slip down on left side */
5595 can_fall_left = !(can_fall_right = RND(2));
5597 can_fall_both = FALSE;
5602 if (game.emulation == EMU_BOULDERDASH ||
5603 element == EL_BD_ROCK || element == EL_BD_DIAMOND)
5604 can_fall_right = FALSE; /* slip down on left side */
5606 can_fall_left = !(can_fall_right = RND(2));
5608 can_fall_both = FALSE;
5614 /* if not determined otherwise, prefer left side for slipping down */
5615 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
5616 started_moving = TRUE;
5620 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
5622 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
5625 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
5626 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
5627 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
5628 int belt_dir = game.belt_dir[belt_nr];
5630 if ((belt_dir == MV_LEFT && left_is_free) ||
5631 (belt_dir == MV_RIGHT && right_is_free))
5633 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
5635 InitMovingField(x, y, belt_dir);
5636 started_moving = TRUE;
5638 Pushed[x][y] = TRUE;
5639 Pushed[nextx][y] = TRUE;
5641 GfxAction[x][y] = ACTION_DEFAULT;
5645 MovDir[x][y] = 0; /* if element was moving, stop it */
5650 /* not "else if" because of elements that can fall and move (EL_SPRING) */
5652 if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
5654 if (CAN_MOVE(element) && !started_moving)
5657 int move_pattern = element_info[element].move_pattern;
5662 if (MovDir[x][y] == MV_NONE)
5664 printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
5665 x, y, element, element_info[element].token_name);
5666 printf("StartMoving(): This should never happen!\n");
5671 Moving2Blocked(x, y, &newx, &newy);
5673 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
5676 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5677 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5679 WasJustMoving[x][y] = 0;
5680 CheckCollision[x][y] = 0;
5682 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
5684 if (Feld[x][y] != element) /* element has changed */
5688 if (!MovDelay[x][y]) /* start new movement phase */
5690 /* all objects that can change their move direction after each step
5691 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
5693 if (element != EL_YAMYAM &&
5694 element != EL_DARK_YAMYAM &&
5695 element != EL_PACMAN &&
5696 !(move_pattern & MV_ANY_DIRECTION) &&
5697 move_pattern != MV_TURNING_LEFT &&
5698 move_pattern != MV_TURNING_RIGHT &&
5699 move_pattern != MV_TURNING_LEFT_RIGHT &&
5700 move_pattern != MV_TURNING_RIGHT_LEFT &&
5701 move_pattern != MV_TURNING_RANDOM)
5705 if (MovDelay[x][y] && (element == EL_BUG ||
5706 element == EL_SPACESHIP ||
5707 element == EL_SP_SNIKSNAK ||
5708 element == EL_SP_ELECTRON ||
5709 element == EL_MOLE))
5710 DrawLevelField(x, y);
5714 if (MovDelay[x][y]) /* wait some time before next movement */
5718 if (element == EL_ROBOT ||
5719 element == EL_YAMYAM ||
5720 element == EL_DARK_YAMYAM)
5722 DrawLevelElementAnimationIfNeeded(x, y, element);
5723 PlayLevelSoundAction(x, y, ACTION_WAITING);
5725 else if (element == EL_SP_ELECTRON)
5726 DrawLevelElementAnimationIfNeeded(x, y, element);
5727 else if (element == EL_DRAGON)
5730 int dir = MovDir[x][y];
5731 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
5732 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
5733 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
5734 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
5735 dir == MV_UP ? IMG_FLAMES_1_UP :
5736 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
5737 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5739 GfxAction[x][y] = ACTION_ATTACKING;
5741 if (IS_PLAYER(x, y))
5742 DrawPlayerField(x, y);
5744 DrawLevelField(x, y);
5746 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
5748 for (i = 1; i <= 3; i++)
5750 int xx = x + i * dx;
5751 int yy = y + i * dy;
5752 int sx = SCREENX(xx);
5753 int sy = SCREENY(yy);
5754 int flame_graphic = graphic + (i - 1);
5756 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
5761 int flamed = MovingOrBlocked2Element(xx, yy);
5765 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
5767 else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
5768 RemoveMovingField(xx, yy);
5770 RemoveField(xx, yy);
5772 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
5775 RemoveMovingField(xx, yy);
5778 ChangeDelay[xx][yy] = 0;
5780 Feld[xx][yy] = EL_FLAMES;
5782 if (IN_SCR_FIELD(sx, sy))
5784 DrawLevelFieldCrumbledSand(xx, yy);
5785 DrawGraphic(sx, sy, flame_graphic, frame);
5790 if (Feld[xx][yy] == EL_FLAMES)
5791 Feld[xx][yy] = EL_EMPTY;
5792 DrawLevelField(xx, yy);
5797 if (MovDelay[x][y]) /* element still has to wait some time */
5799 PlayLevelSoundAction(x, y, ACTION_WAITING);
5805 /* now make next step */
5807 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
5809 if (DONT_COLLIDE_WITH(element) &&
5810 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
5811 !PLAYER_ENEMY_PROTECTED(newx, newy))
5813 TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
5818 else if (CAN_MOVE_INTO_ACID(element) &&
5819 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
5820 !IS_MV_DIAGONAL(MovDir[x][y]) &&
5821 (MovDir[x][y] == MV_DOWN ||
5822 game.engine_version >= VERSION_IDENT(3,1,0,0)))
5824 SplashAcid(newx, newy);
5825 Store[x][y] = EL_ACID;
5827 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
5829 if (Feld[newx][newy] == EL_EXIT_OPEN)
5832 DrawLevelField(x, y);
5834 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
5835 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
5836 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
5838 local_player->friends_still_needed--;
5839 if (!local_player->friends_still_needed &&
5840 !local_player->GameOver && AllPlayersGone)
5841 local_player->LevelSolved = local_player->GameOver = TRUE;
5845 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
5847 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
5848 DrawLevelField(newx, newy);
5850 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
5852 else if (!IS_FREE(newx, newy))
5854 GfxAction[x][y] = ACTION_WAITING;
5856 if (IS_PLAYER(x, y))
5857 DrawPlayerField(x, y);
5859 DrawLevelField(x, y);
5864 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
5866 if (IS_FOOD_PIG(Feld[newx][newy]))
5868 if (IS_MOVING(newx, newy))
5869 RemoveMovingField(newx, newy);
5872 Feld[newx][newy] = EL_EMPTY;
5873 DrawLevelField(newx, newy);
5876 PlayLevelSound(x, y, SND_PIG_DIGGING);
5878 else if (!IS_FREE(newx, newy))
5880 if (IS_PLAYER(x, y))
5881 DrawPlayerField(x, y);
5883 DrawLevelField(x, y);
5888 else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
5890 if (Store[x][y] != EL_EMPTY)
5892 boolean can_clone = FALSE;
5895 /* check if element to clone is still there */
5896 for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
5898 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
5906 /* cannot clone or target field not free anymore -- do not clone */
5907 if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
5908 Store[x][y] = EL_EMPTY;
5911 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
5913 if (IS_MV_DIAGONAL(MovDir[x][y]))
5915 int diagonal_move_dir = MovDir[x][y];
5916 int stored = Store[x][y];
5917 int change_delay = 8;
5920 /* android is moving diagonally */
5922 CreateField(x, y, EL_DIAGONAL_SHRINKING);
5924 Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
5925 GfxElement[x][y] = EL_EMC_ANDROID;
5926 GfxAction[x][y] = ACTION_SHRINKING;
5927 GfxDir[x][y] = diagonal_move_dir;
5928 ChangeDelay[x][y] = change_delay;
5930 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
5933 DrawLevelGraphicAnimation(x, y, graphic);
5934 PlayLevelSoundAction(x, y, ACTION_SHRINKING);
5936 if (Feld[newx][newy] == EL_ACID)
5938 SplashAcid(newx, newy);
5943 CreateField(newx, newy, EL_DIAGONAL_GROWING);
5945 Store[newx][newy] = EL_EMC_ANDROID;
5946 GfxElement[newx][newy] = EL_EMC_ANDROID;
5947 GfxAction[newx][newy] = ACTION_GROWING;
5948 GfxDir[newx][newy] = diagonal_move_dir;
5949 ChangeDelay[newx][newy] = change_delay;
5951 graphic = el_act_dir2img(GfxElement[newx][newy],
5952 GfxAction[newx][newy], GfxDir[newx][newy]);
5954 DrawLevelGraphicAnimation(newx, newy, graphic);
5955 PlayLevelSoundAction(newx, newy, ACTION_GROWING);
5961 Feld[newx][newy] = EL_EMPTY;
5962 DrawLevelField(newx, newy);
5964 PlayLevelSoundAction(x, y, ACTION_DIGGING);
5967 else if (!IS_FREE(newx, newy))
5970 if (IS_PLAYER(x, y))
5971 DrawPlayerField(x, y);
5973 DrawLevelField(x, y);
5979 else if (IS_CUSTOM_ELEMENT(element) &&
5980 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5982 int new_element = Feld[newx][newy];
5984 if (!IS_FREE(newx, newy))
5986 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
5987 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
5990 /* no element can dig solid indestructible elements */
5991 if (IS_INDESTRUCTIBLE(new_element) &&
5992 !IS_DIGGABLE(new_element) &&
5993 !IS_COLLECTIBLE(new_element))
5996 if (AmoebaNr[newx][newy] &&
5997 (new_element == EL_AMOEBA_FULL ||
5998 new_element == EL_BD_AMOEBA ||
5999 new_element == EL_AMOEBA_GROWING))
6001 AmoebaCnt[AmoebaNr[newx][newy]]--;
6002 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6005 if (IS_MOVING(newx, newy))
6006 RemoveMovingField(newx, newy);
6009 RemoveField(newx, newy);
6010 DrawLevelField(newx, newy);
6013 /* if digged element was about to explode, prevent the explosion */
6014 ExplodeField[newx][newy] = EX_TYPE_NONE;
6016 PlayLevelSoundAction(x, y, action);
6019 Store[newx][newy] = EL_EMPTY;
6021 /* this makes it possible to leave the removed element again */
6022 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
6023 Store[newx][newy] = new_element;
6025 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
6027 int move_leave_element = element_info[element].move_leave_element;
6029 /* this makes it possible to leave the removed element again */
6030 Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
6031 new_element : move_leave_element);
6035 if (move_pattern & MV_MAZE_RUNNER_STYLE)
6037 RunnerVisit[x][y] = FrameCounter;
6038 PlayerVisit[x][y] /= 8; /* expire player visit path */
6041 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
6043 if (!IS_FREE(newx, newy))
6045 if (IS_PLAYER(x, y))
6046 DrawPlayerField(x, y);
6048 DrawLevelField(x, y);
6054 boolean wanna_flame = !RND(10);
6055 int dx = newx - x, dy = newy - y;
6056 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
6057 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
6058 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
6059 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
6060 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
6061 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
6064 IS_CLASSIC_ENEMY(element1) ||
6065 IS_CLASSIC_ENEMY(element2)) &&
6066 element1 != EL_DRAGON && element2 != EL_DRAGON &&
6067 element1 != EL_FLAMES && element2 != EL_FLAMES)
6069 ResetGfxAnimation(x, y);
6070 GfxAction[x][y] = ACTION_ATTACKING;
6072 if (IS_PLAYER(x, y))
6073 DrawPlayerField(x, y);
6075 DrawLevelField(x, y);
6077 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
6079 MovDelay[x][y] = 50;
6083 RemoveField(newx, newy);
6085 Feld[newx][newy] = EL_FLAMES;
6086 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
6089 RemoveField(newx1, newy1);
6091 Feld[newx1][newy1] = EL_FLAMES;
6093 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
6096 RemoveField(newx2, newy2);
6098 Feld[newx2][newy2] = EL_FLAMES;
6105 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6106 Feld[newx][newy] == EL_DIAMOND)
6108 if (IS_MOVING(newx, newy))
6109 RemoveMovingField(newx, newy);
6112 Feld[newx][newy] = EL_EMPTY;
6113 DrawLevelField(newx, newy);
6116 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
6118 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6119 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
6121 if (AmoebaNr[newx][newy])
6123 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6124 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
6125 Feld[newx][newy] == EL_BD_AMOEBA)
6126 AmoebaCnt[AmoebaNr[newx][newy]]--;
6131 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
6133 RemoveMovingField(newx, newy);
6136 if (IS_MOVING(newx, newy))
6138 RemoveMovingField(newx, newy);
6143 Feld[newx][newy] = EL_EMPTY;
6144 DrawLevelField(newx, newy);
6147 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
6149 else if ((element == EL_PACMAN || element == EL_MOLE)
6150 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
6152 if (AmoebaNr[newx][newy])
6154 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6155 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
6156 Feld[newx][newy] == EL_BD_AMOEBA)
6157 AmoebaCnt[AmoebaNr[newx][newy]]--;
6160 if (element == EL_MOLE)
6162 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
6163 PlayLevelSound(x, y, SND_MOLE_DIGGING);
6165 ResetGfxAnimation(x, y);
6166 GfxAction[x][y] = ACTION_DIGGING;
6167 DrawLevelField(x, y);
6169 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
6171 return; /* wait for shrinking amoeba */
6173 else /* element == EL_PACMAN */
6175 Feld[newx][newy] = EL_EMPTY;
6176 DrawLevelField(newx, newy);
6177 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
6180 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
6181 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
6182 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
6184 /* wait for shrinking amoeba to completely disappear */
6187 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
6189 /* object was running against a wall */
6194 /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
6195 if (move_pattern & MV_ANY_DIRECTION &&
6196 move_pattern == MovDir[x][y])
6198 int blocking_element =
6199 (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
6201 CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
6204 element = Feld[x][y]; /* element might have changed */
6208 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
6209 DrawLevelElementAnimation(x, y, element);
6211 if (DONT_TOUCH(element))
6212 TestIfBadThingTouchesPlayer(x, y);
6217 InitMovingField(x, y, MovDir[x][y]);
6219 PlayLevelSoundAction(x, y, ACTION_MOVING);
6223 ContinueMoving(x, y);
6226 void ContinueMoving(int x, int y)
6228 int element = Feld[x][y];
6229 struct ElementInfo *ei = &element_info[element];
6230 int direction = MovDir[x][y];
6231 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
6232 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
6233 int newx = x + dx, newy = y + dy;
6234 int stored = Store[x][y];
6235 int stored_new = Store[newx][newy];
6236 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
6237 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
6238 boolean last_line = (newy == lev_fieldy - 1);
6240 MovPos[x][y] += getElementMoveStepsize(x, y);
6242 if (pushed_by_player) /* special case: moving object pushed by player */
6243 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
6245 if (ABS(MovPos[x][y]) < TILEX)
6247 DrawLevelField(x, y);
6249 return; /* element is still moving */
6252 /* element reached destination field */
6254 Feld[x][y] = EL_EMPTY;
6255 Feld[newx][newy] = element;
6256 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
6258 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
6260 element = Feld[newx][newy] = EL_ACID;
6262 else if (element == EL_MOLE)
6264 Feld[x][y] = EL_SAND;
6266 DrawLevelFieldCrumbledSandNeighbours(x, y);
6268 else if (element == EL_QUICKSAND_FILLING)
6270 element = Feld[newx][newy] = get_next_element(element);
6271 Store[newx][newy] = Store[x][y];
6273 else if (element == EL_QUICKSAND_EMPTYING)
6275 Feld[x][y] = get_next_element(element);
6276 element = Feld[newx][newy] = Store[x][y];
6278 else if (element == EL_MAGIC_WALL_FILLING)
6280 element = Feld[newx][newy] = get_next_element(element);
6281 if (!game.magic_wall_active)
6282 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
6283 Store[newx][newy] = Store[x][y];
6285 else if (element == EL_MAGIC_WALL_EMPTYING)
6287 Feld[x][y] = get_next_element(element);
6288 if (!game.magic_wall_active)
6289 Feld[x][y] = EL_MAGIC_WALL_DEAD;
6290 element = Feld[newx][newy] = Store[x][y];
6292 #if USE_NEW_CUSTOM_VALUE
6293 InitField(newx, newy, FALSE);
6296 else if (element == EL_BD_MAGIC_WALL_FILLING)
6298 element = Feld[newx][newy] = get_next_element(element);
6299 if (!game.magic_wall_active)
6300 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
6301 Store[newx][newy] = Store[x][y];
6303 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
6305 Feld[x][y] = get_next_element(element);
6306 if (!game.magic_wall_active)
6307 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
6308 element = Feld[newx][newy] = Store[x][y];
6310 #if USE_NEW_CUSTOM_VALUE
6311 InitField(newx, newy, FALSE);
6314 else if (element == EL_AMOEBA_DROPPING)
6316 Feld[x][y] = get_next_element(element);
6317 element = Feld[newx][newy] = Store[x][y];
6319 else if (element == EL_SOKOBAN_OBJECT)
6322 Feld[x][y] = Back[x][y];
6324 if (Back[newx][newy])
6325 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
6327 Back[x][y] = Back[newx][newy] = 0;
6330 Store[x][y] = EL_EMPTY;
6335 MovDelay[newx][newy] = 0;
6338 if (CAN_CHANGE_OR_HAS_ACTION(element))
6340 if (CAN_CHANGE(element))
6343 /* copy element change control values to new field */
6344 ChangeDelay[newx][newy] = ChangeDelay[x][y];
6345 ChangePage[newx][newy] = ChangePage[x][y];
6346 ChangeCount[newx][newy] = ChangeCount[x][y];
6347 ChangeEvent[newx][newy] = ChangeEvent[x][y];
6350 #if USE_NEW_CUSTOM_VALUE
6351 CustomValue[newx][newy] = CustomValue[x][y];
6357 #if USE_NEW_CUSTOM_VALUE
6358 CustomValue[newx][newy] = CustomValue[x][y];
6362 ChangeDelay[x][y] = 0;
6363 ChangePage[x][y] = -1;
6364 ChangeCount[x][y] = 0;
6365 ChangeEvent[x][y] = -1;
6367 #if USE_NEW_CUSTOM_VALUE
6368 CustomValue[x][y] = 0;
6371 /* copy animation control values to new field */
6372 GfxFrame[newx][newy] = GfxFrame[x][y];
6373 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
6374 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
6375 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
6377 Pushed[x][y] = Pushed[newx][newy] = FALSE;
6379 /* some elements can leave other elements behind after moving */
6381 if (ei->move_leave_element != EL_EMPTY &&
6382 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
6383 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
6385 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
6386 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
6387 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
6390 int move_leave_element = ei->move_leave_element;
6394 /* this makes it possible to leave the removed element again */
6395 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
6396 move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
6398 /* this makes it possible to leave the removed element again */
6399 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
6400 move_leave_element = stored;
6403 /* this makes it possible to leave the removed element again */
6404 if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
6405 ei->move_leave_element == EL_TRIGGER_ELEMENT)
6406 move_leave_element = stored;
6409 Feld[x][y] = move_leave_element;
6411 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
6412 MovDir[x][y] = direction;
6414 InitField(x, y, FALSE);
6416 if (GFX_CRUMBLED(Feld[x][y]))
6417 DrawLevelFieldCrumbledSandNeighbours(x, y);
6419 if (ELEM_IS_PLAYER(move_leave_element))
6420 RelocatePlayer(x, y, move_leave_element);
6423 /* do this after checking for left-behind element */
6424 ResetGfxAnimation(x, y); /* reset animation values for old field */
6426 if (!CAN_MOVE(element) ||
6427 (CAN_FALL(element) && direction == MV_DOWN &&
6428 (element == EL_SPRING ||
6429 element_info[element].move_pattern == MV_WHEN_PUSHED ||
6430 element_info[element].move_pattern == MV_WHEN_DROPPED)))
6431 GfxDir[x][y] = MovDir[newx][newy] = 0;
6433 DrawLevelField(x, y);
6434 DrawLevelField(newx, newy);
6436 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
6438 /* prevent pushed element from moving on in pushed direction */
6439 if (pushed_by_player && CAN_MOVE(element) &&
6440 element_info[element].move_pattern & MV_ANY_DIRECTION &&
6441 !(element_info[element].move_pattern & direction))
6442 TurnRound(newx, newy);
6444 /* prevent elements on conveyor belt from moving on in last direction */
6445 if (pushed_by_conveyor && CAN_FALL(element) &&
6446 direction & MV_HORIZONTAL)
6447 MovDir[newx][newy] = 0;
6449 if (!pushed_by_player)
6451 int nextx = newx + dx, nexty = newy + dy;
6452 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
6454 WasJustMoving[newx][newy] = 3;
6456 if (CAN_FALL(element) && direction == MV_DOWN)
6457 WasJustFalling[newx][newy] = 3;
6459 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
6460 CheckCollision[newx][newy] = 2;
6463 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
6465 TestIfBadThingTouchesPlayer(newx, newy);
6466 TestIfBadThingTouchesFriend(newx, newy);
6468 if (!IS_CUSTOM_ELEMENT(element))
6469 TestIfBadThingTouchesOtherBadThing(newx, newy);
6471 else if (element == EL_PENGUIN)
6472 TestIfFriendTouchesBadThing(newx, newy);
6474 /* give the player one last chance (one more frame) to move away */
6475 if (CAN_FALL(element) && direction == MV_DOWN &&
6476 (last_line || (!IS_FREE(x, newy + 1) &&
6477 (!IS_PLAYER(x, newy + 1) ||
6478 game.engine_version < VERSION_IDENT(3,1,1,0)))))
6481 if (pushed_by_player && !game.use_change_when_pushing_bug)
6483 int push_side = MV_DIR_OPPOSITE(direction);
6484 struct PlayerInfo *player = PLAYERINFO(x, y);
6486 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
6487 player->index_bit, push_side);
6488 CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
6489 player->index_bit, push_side);
6492 if (element == EL_EMC_ANDROID && pushed_by_player) /* make another move */
6493 MovDelay[newx][newy] = 1;
6495 CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
6497 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
6500 if (ChangePage[newx][newy] != -1) /* delayed change */
6502 int page = ChangePage[newx][newy];
6503 struct ElementChangeInfo *change = &ei->change_page[page];
6505 ChangePage[newx][newy] = -1;
6507 if (change->can_change)
6509 if (ChangeElement(newx, newy, element, page))
6511 if (change->post_change_function)
6512 change->post_change_function(newx, newy);
6516 if (change->has_action)
6517 ExecuteCustomElementAction(newx, newy, element, page);
6521 TestIfElementHitsCustomElement(newx, newy, direction);
6522 TestIfPlayerTouchesCustomElement(newx, newy);
6523 TestIfElementTouchesCustomElement(newx, newy);
6526 if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
6527 IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
6528 CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
6529 MV_DIR_OPPOSITE(direction));
6533 int AmoebeNachbarNr(int ax, int ay)
6536 int element = Feld[ax][ay];
6538 static int xy[4][2] =
6546 for (i = 0; i < NUM_DIRECTIONS; i++)
6548 int x = ax + xy[i][0];
6549 int y = ay + xy[i][1];
6551 if (!IN_LEV_FIELD(x, y))
6554 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
6555 group_nr = AmoebaNr[x][y];
6561 void AmoebenVereinigen(int ax, int ay)
6563 int i, x, y, xx, yy;
6564 int new_group_nr = AmoebaNr[ax][ay];
6565 static int xy[4][2] =
6573 if (new_group_nr == 0)
6576 for (i = 0; i < NUM_DIRECTIONS; i++)
6581 if (!IN_LEV_FIELD(x, y))
6584 if ((Feld[x][y] == EL_AMOEBA_FULL ||
6585 Feld[x][y] == EL_BD_AMOEBA ||
6586 Feld[x][y] == EL_AMOEBA_DEAD) &&
6587 AmoebaNr[x][y] != new_group_nr)
6589 int old_group_nr = AmoebaNr[x][y];
6591 if (old_group_nr == 0)
6594 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
6595 AmoebaCnt[old_group_nr] = 0;
6596 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
6597 AmoebaCnt2[old_group_nr] = 0;
6600 SCAN_PLAYFIELD(xx, yy)
6602 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
6605 if (AmoebaNr[xx][yy] == old_group_nr)
6606 AmoebaNr[xx][yy] = new_group_nr;
6612 void AmoebeUmwandeln(int ax, int ay)
6616 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
6618 int group_nr = AmoebaNr[ax][ay];
6623 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
6624 printf("AmoebeUmwandeln(): This should never happen!\n");
6630 SCAN_PLAYFIELD(x, y)
6632 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
6635 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
6638 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
6642 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
6643 SND_AMOEBA_TURNING_TO_GEM :
6644 SND_AMOEBA_TURNING_TO_ROCK));
6649 static int xy[4][2] =
6657 for (i = 0; i < NUM_DIRECTIONS; i++)
6662 if (!IN_LEV_FIELD(x, y))
6665 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
6667 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
6668 SND_AMOEBA_TURNING_TO_GEM :
6669 SND_AMOEBA_TURNING_TO_ROCK));
6676 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
6679 int group_nr = AmoebaNr[ax][ay];
6680 boolean done = FALSE;
6685 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
6686 printf("AmoebeUmwandelnBD(): This should never happen!\n");
6692 SCAN_PLAYFIELD(x, y)
6694 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
6697 if (AmoebaNr[x][y] == group_nr &&
6698 (Feld[x][y] == EL_AMOEBA_DEAD ||
6699 Feld[x][y] == EL_BD_AMOEBA ||
6700 Feld[x][y] == EL_AMOEBA_GROWING))
6703 Feld[x][y] = new_element;
6704 InitField(x, y, FALSE);
6705 DrawLevelField(x, y);
6711 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
6712 SND_BD_AMOEBA_TURNING_TO_ROCK :
6713 SND_BD_AMOEBA_TURNING_TO_GEM));
6716 void AmoebeWaechst(int x, int y)
6718 static unsigned long sound_delay = 0;
6719 static unsigned long sound_delay_value = 0;
6721 if (!MovDelay[x][y]) /* start new growing cycle */
6725 if (DelayReached(&sound_delay, sound_delay_value))
6727 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
6728 sound_delay_value = 30;
6732 if (MovDelay[x][y]) /* wait some time before growing bigger */
6735 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6737 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
6738 6 - MovDelay[x][y]);
6740 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
6743 if (!MovDelay[x][y])
6745 Feld[x][y] = Store[x][y];
6747 DrawLevelField(x, y);
6752 void AmoebaDisappearing(int x, int y)
6754 static unsigned long sound_delay = 0;
6755 static unsigned long sound_delay_value = 0;
6757 if (!MovDelay[x][y]) /* start new shrinking cycle */
6761 if (DelayReached(&sound_delay, sound_delay_value))
6762 sound_delay_value = 30;
6765 if (MovDelay[x][y]) /* wait some time before shrinking */
6768 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6770 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
6771 6 - MovDelay[x][y]);
6773 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
6776 if (!MovDelay[x][y])
6778 Feld[x][y] = EL_EMPTY;
6779 DrawLevelField(x, y);
6781 /* don't let mole enter this field in this cycle;
6782 (give priority to objects falling to this field from above) */
6788 void AmoebeAbleger(int ax, int ay)
6791 int element = Feld[ax][ay];
6792 int graphic = el2img(element);
6793 int newax = ax, neway = ay;
6794 boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
6795 static int xy[4][2] =
6803 if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
6805 Feld[ax][ay] = EL_AMOEBA_DEAD;
6806 DrawLevelField(ax, ay);
6810 if (IS_ANIMATED(graphic))
6811 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6813 if (!MovDelay[ax][ay]) /* start making new amoeba field */
6814 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
6816 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
6819 if (MovDelay[ax][ay])
6823 if (can_drop) /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
6826 int x = ax + xy[start][0];
6827 int y = ay + xy[start][1];
6829 if (!IN_LEV_FIELD(x, y))
6832 if (IS_FREE(x, y) ||
6833 CAN_GROW_INTO(Feld[x][y]) ||
6834 Feld[x][y] == EL_QUICKSAND_EMPTY)
6840 if (newax == ax && neway == ay)
6843 else /* normal or "filled" (BD style) amoeba */
6846 boolean waiting_for_player = FALSE;
6848 for (i = 0; i < NUM_DIRECTIONS; i++)
6850 int j = (start + i) % 4;
6851 int x = ax + xy[j][0];
6852 int y = ay + xy[j][1];
6854 if (!IN_LEV_FIELD(x, y))
6857 if (IS_FREE(x, y) ||
6858 CAN_GROW_INTO(Feld[x][y]) ||
6859 Feld[x][y] == EL_QUICKSAND_EMPTY)
6865 else if (IS_PLAYER(x, y))
6866 waiting_for_player = TRUE;
6869 if (newax == ax && neway == ay) /* amoeba cannot grow */
6871 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
6873 Feld[ax][ay] = EL_AMOEBA_DEAD;
6874 DrawLevelField(ax, ay);
6875 AmoebaCnt[AmoebaNr[ax][ay]]--;
6877 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
6879 if (element == EL_AMOEBA_FULL)
6880 AmoebeUmwandeln(ax, ay);
6881 else if (element == EL_BD_AMOEBA)
6882 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
6887 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
6889 /* amoeba gets larger by growing in some direction */
6891 int new_group_nr = AmoebaNr[ax][ay];
6894 if (new_group_nr == 0)
6896 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
6897 printf("AmoebeAbleger(): This should never happen!\n");
6902 AmoebaNr[newax][neway] = new_group_nr;
6903 AmoebaCnt[new_group_nr]++;
6904 AmoebaCnt2[new_group_nr]++;
6906 /* if amoeba touches other amoeba(s) after growing, unify them */
6907 AmoebenVereinigen(newax, neway);
6909 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
6911 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
6917 if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
6918 (neway == lev_fieldy - 1 && newax != ax))
6920 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
6921 Store[newax][neway] = element;
6923 else if (neway == ay || element == EL_EMC_DRIPPER)
6925 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
6927 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
6931 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
6932 Feld[ax][ay] = EL_AMOEBA_DROPPING;
6933 Store[ax][ay] = EL_AMOEBA_DROP;
6934 ContinueMoving(ax, ay);
6938 DrawLevelField(newax, neway);
6941 void Life(int ax, int ay)
6945 static int life[4] = { 2, 3, 3, 3 }; /* parameters for "game of life" */
6948 int element = Feld[ax][ay];
6949 int graphic = el2img(element);
6950 int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
6952 boolean changed = FALSE;
6954 if (IS_ANIMATED(graphic))
6955 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6960 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
6961 MovDelay[ax][ay] = life_time;
6963 if (MovDelay[ax][ay]) /* wait some time before next cycle */
6966 if (MovDelay[ax][ay])
6970 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
6972 int xx = ax+x1, yy = ay+y1;
6975 if (!IN_LEV_FIELD(xx, yy))
6978 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
6980 int x = xx+x2, y = yy+y2;
6982 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
6985 if (((Feld[x][y] == element ||
6986 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
6988 (IS_FREE(x, y) && Stop[x][y]))
6992 if (xx == ax && yy == ay) /* field in the middle */
6994 if (nachbarn < life_parameter[0] ||
6995 nachbarn > life_parameter[1])
6997 Feld[xx][yy] = EL_EMPTY;
6999 DrawLevelField(xx, yy);
7000 Stop[xx][yy] = TRUE;
7004 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
7005 { /* free border field */
7006 if (nachbarn >= life_parameter[2] &&
7007 nachbarn <= life_parameter[3])
7009 Feld[xx][yy] = element;
7010 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
7012 DrawLevelField(xx, yy);
7013 Stop[xx][yy] = TRUE;
7020 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
7021 SND_GAME_OF_LIFE_GROWING);
7024 static void InitRobotWheel(int x, int y)
7026 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
7029 static void RunRobotWheel(int x, int y)
7031 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
7034 static void StopRobotWheel(int x, int y)
7036 if (ZX == x && ZY == y)
7040 static void InitTimegateWheel(int x, int y)
7042 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
7045 static void RunTimegateWheel(int x, int y)
7047 PlayLevelSound(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
7050 static void InitMagicBallDelay(int x, int y)
7053 ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
7055 ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
7059 static void ActivateMagicBall(int bx, int by)
7063 if (level.ball_random)
7065 int pos_border = RND(8); /* select one of the eight border elements */
7066 int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
7067 int xx = pos_content % 3;
7068 int yy = pos_content / 3;
7073 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
7074 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
7078 for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
7080 int xx = x - bx + 1;
7081 int yy = y - by + 1;
7083 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
7084 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
7088 game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
7091 static void InitDiagonalMovingElement(int x, int y)
7094 MovDelay[x][y] = level.android_move_time;
7098 void CheckExit(int x, int y)
7100 if (local_player->gems_still_needed > 0 ||
7101 local_player->sokobanfields_still_needed > 0 ||
7102 local_player->lights_still_needed > 0)
7104 int element = Feld[x][y];
7105 int graphic = el2img(element);
7107 if (IS_ANIMATED(graphic))
7108 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7113 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7116 Feld[x][y] = EL_EXIT_OPENING;
7118 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
7121 void CheckExitSP(int x, int y)
7123 if (local_player->gems_still_needed > 0)
7125 int element = Feld[x][y];
7126 int graphic = el2img(element);
7128 if (IS_ANIMATED(graphic))
7129 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7134 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7137 Feld[x][y] = EL_SP_EXIT_OPENING;
7139 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
7142 static void CloseAllOpenTimegates()
7147 SCAN_PLAYFIELD(x, y)
7149 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7152 int element = Feld[x][y];
7154 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
7156 Feld[x][y] = EL_TIMEGATE_CLOSING;
7158 PlayLevelSoundAction(x, y, ACTION_CLOSING);
7163 void EdelsteinFunkeln(int x, int y)
7165 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
7168 if (Feld[x][y] == EL_BD_DIAMOND)
7171 if (MovDelay[x][y] == 0) /* next animation frame */
7172 MovDelay[x][y] = 11 * !SimpleRND(500);
7174 if (MovDelay[x][y] != 0) /* wait some time before next frame */
7178 if (setup.direct_draw && MovDelay[x][y])
7179 SetDrawtoField(DRAW_BUFFERED);
7181 DrawLevelElementAnimation(x, y, Feld[x][y]);
7183 if (MovDelay[x][y] != 0)
7185 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
7186 10 - MovDelay[x][y]);
7188 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
7190 if (setup.direct_draw)
7194 dest_x = FX + SCREENX(x) * TILEX;
7195 dest_y = FY + SCREENY(y) * TILEY;
7197 BlitBitmap(drawto_field, window,
7198 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
7199 SetDrawtoField(DRAW_DIRECT);
7205 void MauerWaechst(int x, int y)
7209 if (!MovDelay[x][y]) /* next animation frame */
7210 MovDelay[x][y] = 3 * delay;
7212 if (MovDelay[x][y]) /* wait some time before next frame */
7216 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7218 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
7219 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
7221 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
7224 if (!MovDelay[x][y])
7226 if (MovDir[x][y] == MV_LEFT)
7228 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
7229 DrawLevelField(x - 1, y);
7231 else if (MovDir[x][y] == MV_RIGHT)
7233 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
7234 DrawLevelField(x + 1, y);
7236 else if (MovDir[x][y] == MV_UP)
7238 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
7239 DrawLevelField(x, y - 1);
7243 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
7244 DrawLevelField(x, y + 1);
7247 Feld[x][y] = Store[x][y];
7249 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7250 DrawLevelField(x, y);
7255 void MauerAbleger(int ax, int ay)
7257 int element = Feld[ax][ay];
7258 int graphic = el2img(element);
7259 boolean oben_frei = FALSE, unten_frei = FALSE;
7260 boolean links_frei = FALSE, rechts_frei = FALSE;
7261 boolean oben_massiv = FALSE, unten_massiv = FALSE;
7262 boolean links_massiv = FALSE, rechts_massiv = FALSE;
7263 boolean new_wall = FALSE;
7265 if (IS_ANIMATED(graphic))
7266 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7268 if (!MovDelay[ax][ay]) /* start building new wall */
7269 MovDelay[ax][ay] = 6;
7271 if (MovDelay[ax][ay]) /* wait some time before building new wall */
7274 if (MovDelay[ax][ay])
7278 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
7280 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
7282 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
7284 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
7287 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
7288 element == EL_EXPANDABLE_WALL_ANY)
7292 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
7293 Store[ax][ay-1] = element;
7294 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
7295 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
7296 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
7297 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
7302 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
7303 Store[ax][ay+1] = element;
7304 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
7305 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
7306 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
7307 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
7312 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7313 element == EL_EXPANDABLE_WALL_ANY ||
7314 element == EL_EXPANDABLE_WALL)
7318 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
7319 Store[ax-1][ay] = element;
7320 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
7321 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
7322 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
7323 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
7329 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
7330 Store[ax+1][ay] = element;
7331 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
7332 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
7333 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
7334 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
7339 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
7340 DrawLevelField(ax, ay);
7342 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
7344 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
7345 unten_massiv = TRUE;
7346 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
7347 links_massiv = TRUE;
7348 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
7349 rechts_massiv = TRUE;
7351 if (((oben_massiv && unten_massiv) ||
7352 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7353 element == EL_EXPANDABLE_WALL) &&
7354 ((links_massiv && rechts_massiv) ||
7355 element == EL_EXPANDABLE_WALL_VERTICAL))
7356 Feld[ax][ay] = EL_WALL;
7359 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
7362 void CheckForDragon(int x, int y)
7365 boolean dragon_found = FALSE;
7366 static int xy[4][2] =
7374 for (i = 0; i < NUM_DIRECTIONS; i++)
7376 for (j = 0; j < 4; j++)
7378 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7380 if (IN_LEV_FIELD(xx, yy) &&
7381 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
7383 if (Feld[xx][yy] == EL_DRAGON)
7384 dragon_found = TRUE;
7393 for (i = 0; i < NUM_DIRECTIONS; i++)
7395 for (j = 0; j < 3; j++)
7397 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7399 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
7401 Feld[xx][yy] = EL_EMPTY;
7402 DrawLevelField(xx, yy);
7411 static void InitBuggyBase(int x, int y)
7413 int element = Feld[x][y];
7414 int activating_delay = FRAMES_PER_SECOND / 4;
7417 (element == EL_SP_BUGGY_BASE ?
7418 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
7419 element == EL_SP_BUGGY_BASE_ACTIVATING ?
7421 element == EL_SP_BUGGY_BASE_ACTIVE ?
7422 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
7425 static void WarnBuggyBase(int x, int y)
7428 static int xy[4][2] =
7436 for (i = 0; i < NUM_DIRECTIONS; i++)
7438 int xx = x + xy[i][0], yy = y + xy[i][1];
7440 if (IS_PLAYER(xx, yy))
7442 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
7449 static void InitTrap(int x, int y)
7451 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
7454 static void ActivateTrap(int x, int y)
7456 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
7459 static void ChangeActiveTrap(int x, int y)
7461 int graphic = IMG_TRAP_ACTIVE;
7463 /* if new animation frame was drawn, correct crumbled sand border */
7464 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
7465 DrawLevelFieldCrumbledSand(x, y);
7468 static int getSpecialActionElement(int element, int number, int base_element)
7470 return (element != EL_EMPTY ? element :
7471 number != -1 ? base_element + number - 1 :
7475 static int getModifiedActionNumber(int value_old, int operator, int operand,
7476 int value_min, int value_max)
7478 int value_new = (operator == CA_MODE_SET ? operand :
7479 operator == CA_MODE_ADD ? value_old + operand :
7480 operator == CA_MODE_SUBTRACT ? value_old - operand :
7481 operator == CA_MODE_MULTIPLY ? value_old * operand :
7482 operator == CA_MODE_DIVIDE ? value_old / MAX(1, operand) :
7483 operator == CA_MODE_MODULO ? value_old % MAX(1, operand) :
7486 return (value_new < value_min ? value_min :
7487 value_new > value_max ? value_max :
7491 static void ExecuteCustomElementAction(int x, int y, int element, int page)
7493 struct ElementInfo *ei = &element_info[element];
7494 struct ElementChangeInfo *change = &ei->change_page[page];
7495 int action_type = change->action_type;
7496 int action_mode = change->action_mode;
7497 int action_arg = change->action_arg;
7500 if (!change->has_action)
7503 /* ---------- determine action paramater values -------------------------- */
7505 int level_time_value =
7506 (level.time > 0 ? TimeLeft :
7509 int action_arg_element =
7510 (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player :
7511 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
7512 action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element :
7515 int action_arg_direction =
7516 (action_arg >= CA_ARG_DIRECTION_LEFT &&
7517 action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
7518 action_arg == CA_ARG_DIRECTION_TRIGGER ?
7519 change->actual_trigger_side :
7520 action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
7521 MV_DIR_OPPOSITE(change->actual_trigger_side) :
7524 int action_arg_number_min =
7525 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
7528 int action_arg_number_max =
7529 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
7530 action_type == CA_SET_LEVEL_GEMS ? 999 :
7531 action_type == CA_SET_LEVEL_TIME ? 9999 :
7532 action_type == CA_SET_LEVEL_SCORE ? 99999 :
7533 action_type == CA_SET_CE_SCORE ? 9999 :
7534 action_type == CA_SET_CE_VALUE ? 9999 :
7537 int action_arg_number_reset =
7538 (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize :
7539 action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
7540 action_type == CA_SET_LEVEL_TIME ? level.time :
7541 action_type == CA_SET_LEVEL_SCORE ? 0 :
7542 action_type == CA_SET_CE_SCORE ? 0 :
7544 action_type == CA_SET_CE_VALUE ? GET_NEW_CUSTOM_VALUE(element) :
7546 action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
7550 int action_arg_number =
7551 (action_arg <= CA_ARG_MAX ? action_arg :
7552 action_arg >= CA_ARG_SPEED_NOT_MOVING &&
7553 action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
7554 action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
7555 action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
7556 action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
7557 action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
7558 action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
7559 #if USE_NEW_CUSTOM_VALUE
7560 action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
7562 action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
7564 action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
7565 action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
7566 action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
7567 action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
7568 action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CUSTOM_VALUE(change->target_element) :
7569 action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
7570 action_arg == CA_ARG_ELEMENT_NR_TARGET ? change->target_element :
7571 action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
7574 int action_arg_number_old =
7575 (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
7576 action_type == CA_SET_LEVEL_TIME ? TimeLeft :
7577 action_type == CA_SET_LEVEL_SCORE ? local_player->score :
7578 action_type == CA_SET_CE_SCORE ? ei->collect_score :
7579 action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
7582 int action_arg_number_new =
7583 getModifiedActionNumber(action_arg_number_old,
7584 action_mode, action_arg_number,
7585 action_arg_number_min, action_arg_number_max);
7587 int trigger_player_bits =
7588 (change->actual_trigger_player >= EL_PLAYER_1 &&
7589 change->actual_trigger_player <= EL_PLAYER_4 ?
7590 (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
7593 int action_arg_player_bits =
7594 (action_arg >= CA_ARG_PLAYER_1 &&
7595 action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
7596 action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
7599 /* ---------- execute action -------------------------------------------- */
7608 /* ---------- level actions ------------------------------------------- */
7610 case CA_RESTART_LEVEL:
7612 game.restart_level = TRUE;
7617 case CA_SHOW_ENVELOPE:
7619 int element = getSpecialActionElement(action_arg_element,
7620 action_arg_number, EL_ENVELOPE_1);
7622 if (IS_ENVELOPE(element))
7623 local_player->show_envelope = element;
7628 case CA_SET_LEVEL_TIME:
7630 if (level.time > 0) /* only modify limited time value */
7632 TimeLeft = action_arg_number_new;
7634 DrawGameValue_Time(TimeLeft);
7636 if (!TimeLeft && setup.time_limit)
7637 for (i = 0; i < MAX_PLAYERS; i++)
7638 KillPlayer(&stored_player[i]);
7644 case CA_SET_LEVEL_SCORE:
7646 local_player->score = action_arg_number_new;
7648 DrawGameValue_Score(local_player->score);
7653 case CA_SET_LEVEL_GEMS:
7655 local_player->gems_still_needed = action_arg_number_new;
7657 DrawGameValue_Emeralds(local_player->gems_still_needed);
7662 case CA_SET_LEVEL_GRAVITY:
7664 game.gravity = (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
7665 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
7666 action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
7671 case CA_SET_LEVEL_WIND:
7673 game.wind_direction = action_arg_direction;
7678 /* ---------- player actions ------------------------------------------ */
7680 case CA_MOVE_PLAYER:
7682 /* automatically move to the next field in specified direction */
7683 for (i = 0; i < MAX_PLAYERS; i++)
7684 if (trigger_player_bits & (1 << i))
7685 stored_player[i].programmed_action = action_arg_direction;
7690 case CA_EXIT_PLAYER:
7692 for (i = 0; i < MAX_PLAYERS; i++)
7693 if (action_arg_player_bits & (1 << i))
7694 stored_player[i].LevelSolved = stored_player[i].GameOver = TRUE;
7699 case CA_KILL_PLAYER:
7701 for (i = 0; i < MAX_PLAYERS; i++)
7702 if (action_arg_player_bits & (1 << i))
7703 KillPlayer(&stored_player[i]);
7708 case CA_SET_PLAYER_KEYS:
7710 int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
7711 int element = getSpecialActionElement(action_arg_element,
7712 action_arg_number, EL_KEY_1);
7714 if (IS_KEY(element))
7716 for (i = 0; i < MAX_PLAYERS; i++)
7718 if (trigger_player_bits & (1 << i))
7720 stored_player[i].key[KEY_NR(element)] = key_state;
7722 DrawGameValue_Keys(stored_player[i].key);
7724 redraw_mask |= REDRAW_DOOR_1;
7732 case CA_SET_PLAYER_SPEED:
7734 for (i = 0; i < MAX_PLAYERS; i++)
7736 if (trigger_player_bits & (1 << i))
7738 int move_stepsize = TILEX / stored_player[i].move_delay_value;
7740 if (action_arg == CA_ARG_SPEED_FASTER &&
7741 stored_player[i].cannot_move)
7743 action_arg_number = STEPSIZE_VERY_SLOW;
7745 else if (action_arg == CA_ARG_SPEED_SLOWER ||
7746 action_arg == CA_ARG_SPEED_FASTER)
7748 action_arg_number = 2;
7749 action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
7754 getModifiedActionNumber(move_stepsize,
7757 action_arg_number_min,
7758 action_arg_number_max);
7761 SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
7763 /* make sure that value is power of 2 */
7764 move_stepsize = (1 << log_2(move_stepsize));
7766 /* do no immediately change -- the player might just be moving */
7767 stored_player[i].move_delay_value_next = TILEX / move_stepsize;
7769 stored_player[i].cannot_move =
7770 (action_arg == CA_ARG_SPEED_NOT_MOVING ? TRUE : FALSE);
7778 case CA_SET_PLAYER_SHIELD:
7780 for (i = 0; i < MAX_PLAYERS; i++)
7782 if (trigger_player_bits & (1 << i))
7784 if (action_arg == CA_ARG_SHIELD_OFF)
7786 stored_player[i].shield_normal_time_left = 0;
7787 stored_player[i].shield_deadly_time_left = 0;
7789 else if (action_arg == CA_ARG_SHIELD_NORMAL)
7791 stored_player[i].shield_normal_time_left = 999999;
7793 else if (action_arg == CA_ARG_SHIELD_DEADLY)
7795 stored_player[i].shield_normal_time_left = 999999;
7796 stored_player[i].shield_deadly_time_left = 999999;
7804 case CA_SET_PLAYER_ARTWORK:
7806 for (i = 0; i < MAX_PLAYERS; i++)
7808 if (trigger_player_bits & (1 << i))
7810 int artwork_element = action_arg_element;
7812 if (action_arg == CA_ARG_ELEMENT_RESET)
7814 (level.use_artwork_element[i] ? level.artwork_element[i] :
7815 stored_player[i].element_nr);
7817 stored_player[i].artwork_element = artwork_element;
7819 SetPlayerWaiting(&stored_player[i], FALSE);
7821 /* set number of special actions for bored and sleeping animation */
7822 stored_player[i].num_special_action_bored =
7823 get_num_special_action(artwork_element,
7824 ACTION_BORING_1, ACTION_BORING_LAST);
7825 stored_player[i].num_special_action_sleeping =
7826 get_num_special_action(artwork_element,
7827 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
7834 /* ---------- CE actions ---------------------------------------------- */
7836 case CA_SET_CE_SCORE:
7838 ei->collect_score = action_arg_number_new;
7843 case CA_SET_CE_VALUE:
7845 #if USE_NEW_CUSTOM_VALUE
7846 int last_custom_value = CustomValue[x][y];
7848 CustomValue[x][y] = action_arg_number_new;
7851 printf("::: Count == %d\n", CustomValue[x][y]);
7854 if (CustomValue[x][y] == 0 && last_custom_value > 0)
7857 printf("::: CE_VALUE_GETS_ZERO\n");
7860 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
7861 CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
7864 printf("::: RESULT: %d, %d\n", Feld[x][y], ChangePage[x][y]);
7872 /* ---------- engine actions ------------------------------------------ */
7874 case CA_SET_ENGINE_SCAN_MODE:
7876 InitPlayfieldScanMode(action_arg);
7886 static void CreateFieldExt(int x, int y, int element, boolean is_change)
7888 int previous_move_direction = MovDir[x][y];
7889 #if USE_NEW_CUSTOM_VALUE
7890 int last_ce_value = CustomValue[x][y];
7892 boolean add_player = (ELEM_IS_PLAYER(element) &&
7893 IS_WALKABLE(Feld[x][y]));
7895 /* check if element under player changes from accessible to unaccessible
7896 (needed for special case of dropping element which then changes) */
7897 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
7898 IS_ACCESSIBLE(Feld[x][y]) && !IS_ACCESSIBLE(element))
7907 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
7908 RemoveMovingField(x, y);
7912 Feld[x][y] = element;
7914 ResetGfxAnimation(x, y);
7915 ResetRandomAnimationValue(x, y);
7917 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
7918 MovDir[x][y] = previous_move_direction;
7920 #if USE_NEW_CUSTOM_VALUE
7921 if (element_info[Feld[x][y]].use_last_ce_value)
7922 CustomValue[x][y] = last_ce_value;
7925 InitField_WithBug1(x, y, FALSE);
7927 DrawLevelField(x, y);
7929 if (GFX_CRUMBLED(Feld[x][y]))
7930 DrawLevelFieldCrumbledSandNeighbours(x, y);
7933 /* "ChangeCount" not set yet to allow "entered by player" change one time */
7934 if (ELEM_IS_PLAYER(element))
7935 RelocatePlayer(x, y, element);
7938 ChangeCount[x][y]++; /* count number of changes in the same frame */
7940 TestIfBadThingTouchesPlayer(x, y);
7941 TestIfPlayerTouchesCustomElement(x, y);
7942 TestIfElementTouchesCustomElement(x, y);
7945 static void CreateField(int x, int y, int element)
7947 CreateFieldExt(x, y, element, FALSE);
7950 static void CreateElementFromChange(int x, int y, int element)
7952 element = GET_VALID_RUNTIME_ELEMENT(element);
7954 #if USE_STOP_CHANGED_ELEMENTS
7955 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
7957 int old_element = Feld[x][y];
7959 /* prevent changed element from moving in same engine frame
7960 unless both old and new element can either fall or move */
7961 if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
7962 (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
7967 CreateFieldExt(x, y, element, TRUE);
7970 static boolean ChangeElement(int x, int y, int element, int page)
7972 struct ElementChangeInfo *change = &element_info[element].change_page[page];
7974 int old_element = Feld[x][y];
7976 /* always use default change event to prevent running into a loop */
7977 if (ChangeEvent[x][y] == -1)
7978 ChangeEvent[x][y] = CE_DELAY;
7980 if (ChangeEvent[x][y] == CE_DELAY)
7982 /* reset actual trigger element, trigger player and action element */
7983 change->actual_trigger_element = EL_EMPTY;
7984 change->actual_trigger_player = EL_PLAYER_1;
7985 change->actual_trigger_side = CH_SIDE_NONE;
7986 change->actual_trigger_ce_value = 0;
7989 /* do not change elements more than a specified maximum number of changes */
7990 if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
7993 ChangeCount[x][y]++; /* count number of changes in the same frame */
7995 if (change->explode)
8002 if (change->use_target_content)
8004 boolean complete_replace = TRUE;
8005 boolean can_replace[3][3];
8008 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
8011 boolean is_walkable;
8012 boolean is_diggable;
8013 boolean is_collectible;
8014 boolean is_removable;
8015 boolean is_destructible;
8016 int ex = x + xx - 1;
8017 int ey = y + yy - 1;
8018 int content_element = change->target_content.e[xx][yy];
8021 can_replace[xx][yy] = TRUE;
8023 if (ex == x && ey == y) /* do not check changing element itself */
8026 if (content_element == EL_EMPTY_SPACE)
8028 can_replace[xx][yy] = FALSE; /* do not replace border with space */
8033 if (!IN_LEV_FIELD(ex, ey))
8035 can_replace[xx][yy] = FALSE;
8036 complete_replace = FALSE;
8043 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
8044 e = MovingOrBlocked2Element(ex, ey);
8046 is_empty = (IS_FREE(ex, ey) ||
8047 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
8049 is_walkable = (is_empty || IS_WALKABLE(e));
8050 is_diggable = (is_empty || IS_DIGGABLE(e));
8051 is_collectible = (is_empty || IS_COLLECTIBLE(e));
8052 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
8053 is_removable = (is_diggable || is_collectible);
8055 can_replace[xx][yy] =
8056 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
8057 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
8058 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
8059 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
8060 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
8061 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
8062 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
8064 if (!can_replace[xx][yy])
8065 complete_replace = FALSE;
8068 if (!change->only_if_complete || complete_replace)
8070 boolean something_has_changed = FALSE;
8072 if (change->only_if_complete && change->use_random_replace &&
8073 RND(100) < change->random_percentage)
8076 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
8078 int ex = x + xx - 1;
8079 int ey = y + yy - 1;
8080 int content_element;
8082 if (can_replace[xx][yy] && (!change->use_random_replace ||
8083 RND(100) < change->random_percentage))
8085 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
8086 RemoveMovingField(ex, ey);
8088 ChangeEvent[ex][ey] = ChangeEvent[x][y];
8090 content_element = change->target_content.e[xx][yy];
8091 target_element = GET_TARGET_ELEMENT(content_element, change);
8093 CreateElementFromChange(ex, ey, target_element);
8095 something_has_changed = TRUE;
8097 /* for symmetry reasons, freeze newly created border elements */
8098 if (ex != x || ey != y)
8099 Stop[ex][ey] = TRUE; /* no more moving in this frame */
8103 if (something_has_changed)
8105 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
8106 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
8112 target_element = GET_TARGET_ELEMENT(change->target_element, change);
8114 if (element == EL_DIAGONAL_GROWING ||
8115 element == EL_DIAGONAL_SHRINKING)
8117 target_element = Store[x][y];
8119 Store[x][y] = EL_EMPTY;
8122 CreateElementFromChange(x, y, target_element);
8124 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
8125 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
8128 /* this uses direct change before indirect change */
8129 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
8134 #if USE_NEW_DELAYED_ACTION
8136 static void HandleElementChange(int x, int y, int page)
8138 int element = MovingOrBlocked2Element(x, y);
8139 struct ElementInfo *ei = &element_info[element];
8140 struct ElementChangeInfo *change = &ei->change_page[page];
8143 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
8144 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
8147 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
8148 x, y, element, element_info[element].token_name);
8149 printf("HandleElementChange(): This should never happen!\n");
8154 /* this can happen with classic bombs on walkable, changing elements */
8155 if (!CAN_CHANGE_OR_HAS_ACTION(element))
8158 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
8159 ChangeDelay[x][y] = 0;
8165 if (ChangeDelay[x][y] == 0) /* initialize element change */
8167 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
8169 if (change->can_change)
8171 ResetGfxAnimation(x, y);
8172 ResetRandomAnimationValue(x, y);
8174 if (change->pre_change_function)
8175 change->pre_change_function(x, y);
8179 ChangeDelay[x][y]--;
8181 if (ChangeDelay[x][y] != 0) /* continue element change */
8183 if (change->can_change)
8185 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8187 if (IS_ANIMATED(graphic))
8188 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8190 if (change->change_function)
8191 change->change_function(x, y);
8194 else /* finish element change */
8196 if (ChangePage[x][y] != -1) /* remember page from delayed change */
8198 page = ChangePage[x][y];
8199 ChangePage[x][y] = -1;
8201 change = &ei->change_page[page];
8204 if (IS_MOVING(x, y)) /* never change a running system ;-) */
8206 ChangeDelay[x][y] = 1; /* try change after next move step */
8207 ChangePage[x][y] = page; /* remember page to use for change */
8212 if (change->can_change)
8214 if (ChangeElement(x, y, element, page))
8216 if (change->post_change_function)
8217 change->post_change_function(x, y);
8221 if (change->has_action)
8222 ExecuteCustomElementAction(x, y, element, page);
8228 static void HandleElementChange(int x, int y, int page)
8230 int element = MovingOrBlocked2Element(x, y);
8231 struct ElementInfo *ei = &element_info[element];
8232 struct ElementChangeInfo *change = &ei->change_page[page];
8235 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
8238 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
8239 x, y, element, element_info[element].token_name);
8240 printf("HandleElementChange(): This should never happen!\n");
8245 /* this can happen with classic bombs on walkable, changing elements */
8246 if (!CAN_CHANGE(element))
8249 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
8250 ChangeDelay[x][y] = 0;
8256 if (ChangeDelay[x][y] == 0) /* initialize element change */
8258 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
8260 ResetGfxAnimation(x, y);
8261 ResetRandomAnimationValue(x, y);
8263 if (change->pre_change_function)
8264 change->pre_change_function(x, y);
8267 ChangeDelay[x][y]--;
8269 if (ChangeDelay[x][y] != 0) /* continue element change */
8271 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8273 if (IS_ANIMATED(graphic))
8274 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8276 if (change->change_function)
8277 change->change_function(x, y);
8279 else /* finish element change */
8281 if (ChangePage[x][y] != -1) /* remember page from delayed change */
8283 page = ChangePage[x][y];
8284 ChangePage[x][y] = -1;
8286 change = &ei->change_page[page];
8289 if (IS_MOVING(x, y)) /* never change a running system ;-) */
8291 ChangeDelay[x][y] = 1; /* try change after next move step */
8292 ChangePage[x][y] = page; /* remember page to use for change */
8297 if (ChangeElement(x, y, element, page))
8299 if (change->post_change_function)
8300 change->post_change_function(x, y);
8307 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
8308 int trigger_element,
8314 boolean change_done_any = FALSE;
8315 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
8318 if (!(trigger_events[trigger_element][trigger_event]))
8321 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8323 int element = EL_CUSTOM_START + i;
8324 boolean change_done = FALSE;
8327 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
8328 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
8331 for (p = 0; p < element_info[element].num_change_pages; p++)
8333 struct ElementChangeInfo *change = &element_info[element].change_page[p];
8335 if (change->can_change_or_has_action &&
8336 change->has_event[trigger_event] &&
8337 change->trigger_side & trigger_side &&
8338 change->trigger_player & trigger_player &&
8339 change->trigger_page & trigger_page_bits &&
8340 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
8342 change->actual_trigger_element = trigger_element;
8343 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
8344 change->actual_trigger_side = trigger_side;
8345 change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
8347 if ((change->can_change && !change_done) || change->has_action)
8352 SCAN_PLAYFIELD(x, y)
8354 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8357 if (Feld[x][y] == element)
8359 if (change->can_change && !change_done)
8361 ChangeDelay[x][y] = 1;
8362 ChangeEvent[x][y] = trigger_event;
8364 HandleElementChange(x, y, p);
8366 #if USE_NEW_DELAYED_ACTION
8367 else if (change->has_action)
8369 ExecuteCustomElementAction(x, y, element, p);
8370 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8373 if (change->has_action)
8375 ExecuteCustomElementAction(x, y, element, p);
8376 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8382 if (change->can_change)
8385 change_done_any = TRUE;
8392 return change_done_any;
8395 static boolean CheckElementChangeExt(int x, int y,
8397 int trigger_element,
8402 boolean change_done = FALSE;
8405 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
8406 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
8409 if (Feld[x][y] == EL_BLOCKED)
8411 Blocked2Moving(x, y, &x, &y);
8412 element = Feld[x][y];
8416 /* check if element has already changed */
8417 if (Feld[x][y] != element)
8420 /* check if element has already changed or is about to change after moving */
8421 if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
8422 Feld[x][y] != element) ||
8424 (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
8425 (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
8426 ChangePage[x][y] != -1)))
8430 for (p = 0; p < element_info[element].num_change_pages; p++)
8432 struct ElementChangeInfo *change = &element_info[element].change_page[p];
8434 boolean check_trigger_element =
8435 (trigger_event == CE_TOUCHING_X ||
8436 trigger_event == CE_HITTING_X ||
8437 trigger_event == CE_HIT_BY_X);
8439 if (change->can_change_or_has_action &&
8440 change->has_event[trigger_event] &&
8441 change->trigger_side & trigger_side &&
8442 change->trigger_player & trigger_player &&
8443 (!check_trigger_element ||
8444 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
8446 change->actual_trigger_element = trigger_element;
8447 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
8448 change->actual_trigger_side = trigger_side;
8449 change->actual_trigger_ce_value = CustomValue[x][y];
8451 /* special case: trigger element not at (x,y) position for some events */
8452 if (check_trigger_element)
8464 { 0, 0 }, { 0, 0 }, { 0, 0 },
8468 int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
8469 int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
8471 change->actual_trigger_ce_value = CustomValue[xx][yy];
8474 if (change->can_change && !change_done)
8476 ChangeDelay[x][y] = 1;
8477 ChangeEvent[x][y] = trigger_event;
8479 HandleElementChange(x, y, p);
8483 #if USE_NEW_DELAYED_ACTION
8484 else if (change->has_action)
8486 ExecuteCustomElementAction(x, y, element, p);
8487 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8490 if (change->has_action)
8492 ExecuteCustomElementAction(x, y, element, p);
8493 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8502 static void PlayPlayerSound(struct PlayerInfo *player)
8504 int jx = player->jx, jy = player->jy;
8505 int sound_element = player->artwork_element;
8506 int last_action = player->last_action_waiting;
8507 int action = player->action_waiting;
8509 if (player->is_waiting)
8511 if (action != last_action)
8512 PlayLevelSoundElementAction(jx, jy, sound_element, action);
8514 PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
8518 if (action != last_action)
8519 StopSound(element_info[sound_element].sound[last_action]);
8521 if (last_action == ACTION_SLEEPING)
8522 PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
8526 static void PlayAllPlayersSound()
8530 for (i = 0; i < MAX_PLAYERS; i++)
8531 if (stored_player[i].active)
8532 PlayPlayerSound(&stored_player[i]);
8535 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
8537 boolean last_waiting = player->is_waiting;
8538 int move_dir = player->MovDir;
8540 player->last_action_waiting = player->action_waiting;
8544 if (!last_waiting) /* not waiting -> waiting */
8546 player->is_waiting = TRUE;
8548 player->frame_counter_bored =
8550 game.player_boring_delay_fixed +
8551 SimpleRND(game.player_boring_delay_random);
8552 player->frame_counter_sleeping =
8554 game.player_sleeping_delay_fixed +
8555 SimpleRND(game.player_sleeping_delay_random);
8557 InitPlayerGfxAnimation(player, ACTION_WAITING, player->MovDir);
8560 if (game.player_sleeping_delay_fixed +
8561 game.player_sleeping_delay_random > 0 &&
8562 player->anim_delay_counter == 0 &&
8563 player->post_delay_counter == 0 &&
8564 FrameCounter >= player->frame_counter_sleeping)
8565 player->is_sleeping = TRUE;
8566 else if (game.player_boring_delay_fixed +
8567 game.player_boring_delay_random > 0 &&
8568 FrameCounter >= player->frame_counter_bored)
8569 player->is_bored = TRUE;
8571 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
8572 player->is_bored ? ACTION_BORING :
8575 if (player->is_sleeping)
8577 if (player->num_special_action_sleeping > 0)
8579 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
8581 int last_special_action = player->special_action_sleeping;
8582 int num_special_action = player->num_special_action_sleeping;
8583 int special_action =
8584 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
8585 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
8586 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
8587 last_special_action + 1 : ACTION_SLEEPING);
8588 int special_graphic =
8589 el_act_dir2img(player->artwork_element, special_action, move_dir);
8591 player->anim_delay_counter =
8592 graphic_info[special_graphic].anim_delay_fixed +
8593 SimpleRND(graphic_info[special_graphic].anim_delay_random);
8594 player->post_delay_counter =
8595 graphic_info[special_graphic].post_delay_fixed +
8596 SimpleRND(graphic_info[special_graphic].post_delay_random);
8598 player->special_action_sleeping = special_action;
8601 if (player->anim_delay_counter > 0)
8603 player->action_waiting = player->special_action_sleeping;
8604 player->anim_delay_counter--;
8606 else if (player->post_delay_counter > 0)
8608 player->post_delay_counter--;
8612 else if (player->is_bored)
8614 if (player->num_special_action_bored > 0)
8616 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
8618 int special_action =
8619 ACTION_BORING_1 + SimpleRND(player->num_special_action_bored);
8620 int special_graphic =
8621 el_act_dir2img(player->artwork_element, special_action, move_dir);
8623 player->anim_delay_counter =
8624 graphic_info[special_graphic].anim_delay_fixed +
8625 SimpleRND(graphic_info[special_graphic].anim_delay_random);
8626 player->post_delay_counter =
8627 graphic_info[special_graphic].post_delay_fixed +
8628 SimpleRND(graphic_info[special_graphic].post_delay_random);
8630 player->special_action_bored = special_action;
8633 if (player->anim_delay_counter > 0)
8635 player->action_waiting = player->special_action_bored;
8636 player->anim_delay_counter--;
8638 else if (player->post_delay_counter > 0)
8640 player->post_delay_counter--;
8645 else if (last_waiting) /* waiting -> not waiting */
8647 player->is_waiting = FALSE;
8648 player->is_bored = FALSE;
8649 player->is_sleeping = FALSE;
8651 player->frame_counter_bored = -1;
8652 player->frame_counter_sleeping = -1;
8654 player->anim_delay_counter = 0;
8655 player->post_delay_counter = 0;
8657 player->action_waiting = ACTION_DEFAULT;
8659 player->special_action_bored = ACTION_DEFAULT;
8660 player->special_action_sleeping = ACTION_DEFAULT;
8664 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
8666 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
8667 int left = player_action & JOY_LEFT;
8668 int right = player_action & JOY_RIGHT;
8669 int up = player_action & JOY_UP;
8670 int down = player_action & JOY_DOWN;
8671 int button1 = player_action & JOY_BUTTON_1;
8672 int button2 = player_action & JOY_BUTTON_2;
8673 int dx = (left ? -1 : right ? 1 : 0);
8674 int dy = (up ? -1 : down ? 1 : 0);
8676 if (!player->active || tape.pausing)
8682 snapped = SnapField(player, dx, dy);
8686 dropped = DropElement(player);
8688 moved = MovePlayer(player, dx, dy);
8691 if (tape.single_step && tape.recording && !tape.pausing)
8693 if (button1 || (dropped && !moved))
8695 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8696 SnapField(player, 0, 0); /* stop snapping */
8700 SetPlayerWaiting(player, FALSE);
8702 return player_action;
8706 /* no actions for this player (no input at player's configured device) */
8708 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
8709 SnapField(player, 0, 0);
8710 CheckGravityMovementWhenNotMoving(player);
8712 if (player->MovPos == 0)
8713 SetPlayerWaiting(player, TRUE);
8715 if (player->MovPos == 0) /* needed for tape.playing */
8716 player->is_moving = FALSE;
8718 player->is_dropping = FALSE;
8724 void AdvanceFrameAndPlayerCounters(int player_nr)
8728 /* advance frame counters (global frame counter and time frame counter) */
8732 /* advance player counters (counters for move delay, move animation etc.) */
8733 for (i = 0; i < MAX_PLAYERS; i++)
8735 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
8736 int move_delay_value = stored_player[i].move_delay_value;
8737 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
8739 if (!advance_player_counters) /* not all players may be affected */
8742 #if USE_NEW_PLAYER_ANIM
8743 if (move_frames == 0) /* less than one move per game frame */
8745 int stepsize = TILEX / move_delay_value;
8746 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
8747 int count = (stored_player[i].is_moving ?
8748 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
8750 if (count % delay == 0)
8755 stored_player[i].Frame += move_frames;
8757 if (stored_player[i].MovPos != 0)
8758 stored_player[i].StepFrame += move_frames;
8760 if (stored_player[i].move_delay > 0)
8761 stored_player[i].move_delay--;
8763 /* due to bugs in previous versions, counter must count up, not down */
8764 if (stored_player[i].push_delay != -1)
8765 stored_player[i].push_delay++;
8767 if (stored_player[i].drop_delay > 0)
8768 stored_player[i].drop_delay--;
8774 static unsigned long game_frame_delay = 0;
8775 unsigned long game_frame_delay_value;
8776 int magic_wall_x = 0, magic_wall_y = 0;
8777 int i, x, y, element, graphic;
8778 byte *recorded_player_action;
8779 byte summarized_player_action = 0;
8780 byte tape_action[MAX_PLAYERS];
8782 if (game_status != GAME_MODE_PLAYING)
8785 game_frame_delay_value =
8786 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
8788 if (tape.playing && tape.warp_forward && !tape.pausing)
8789 game_frame_delay_value = 0;
8791 /* ---------- main game synchronization point ---------- */
8793 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
8795 InitPlayfieldScanModeVars();
8797 #if USE_ONE_MORE_CHANGE_PER_FRAME
8798 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
8800 SCAN_PLAYFIELD(x, y)
8802 ChangeCount[x][y] = 0;
8803 ChangeEvent[x][y] = -1;
8808 if (network_playing && !network_player_action_received)
8810 /* try to get network player actions in time */
8812 #if defined(NETWORK_AVALIABLE)
8813 /* last chance to get network player actions without main loop delay */
8817 /* game was quit by network peer */
8818 if (game_status != GAME_MODE_PLAYING)
8821 if (!network_player_action_received)
8822 return; /* failed to get network player actions in time */
8828 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
8831 /* !!! CHECK THIS (tape.pausing is always FALSE here!) !!! */
8832 if (recorded_player_action == NULL && tape.pausing)
8836 for (i = 0; i < MAX_PLAYERS; i++)
8838 summarized_player_action |= stored_player[i].action;
8840 if (!network_playing)
8841 stored_player[i].effective_action = stored_player[i].action;
8844 #if defined(NETWORK_AVALIABLE)
8845 if (network_playing)
8846 SendToServer_MovePlayer(summarized_player_action);
8849 if (!options.network && !setup.team_mode)
8850 local_player->effective_action = summarized_player_action;
8852 if (recorded_player_action != NULL)
8853 for (i = 0; i < MAX_PLAYERS; i++)
8854 stored_player[i].effective_action = recorded_player_action[i];
8856 for (i = 0; i < MAX_PLAYERS; i++)
8858 tape_action[i] = stored_player[i].effective_action;
8860 if (tape.recording && tape_action[i] && !tape.player_participates[i])
8861 tape.player_participates[i] = TRUE; /* player just appeared from CE */
8864 /* only save actions from input devices, but not programmed actions */
8866 TapeRecordAction(tape_action);
8868 for (i = 0; i < MAX_PLAYERS; i++)
8870 int actual_player_action = stored_player[i].effective_action;
8873 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
8874 - rnd_equinox_tetrachloride 048
8875 - rnd_equinox_tetrachloride_ii 096
8876 - rnd_emanuel_schmieg 002
8877 - doctor_sloan_ww 001, 020
8879 if (stored_player[i].MovPos == 0)
8880 CheckGravityMovement(&stored_player[i]);
8883 /* overwrite programmed action with tape action */
8884 if (stored_player[i].programmed_action)
8885 actual_player_action = stored_player[i].programmed_action;
8888 PlayerActions(&stored_player[i], actual_player_action);
8890 tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
8892 if (tape.recording && tape_action[i] && !tape.player_participates[i])
8893 tape.player_participates[i] = TRUE; /* player just appeared from CE */
8896 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
8899 network_player_action_received = FALSE;
8901 ScrollScreen(NULL, SCROLL_GO_ON);
8903 /* for backwards compatibility, the following code emulates a fixed bug that
8904 occured when pushing elements (causing elements that just made their last
8905 pushing step to already (if possible) make their first falling step in the
8906 same game frame, which is bad); this code is also needed to use the famous
8907 "spring push bug" which is used in older levels and might be wanted to be
8908 used also in newer levels, but in this case the buggy pushing code is only
8909 affecting the "spring" element and no other elements */
8911 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
8913 for (i = 0; i < MAX_PLAYERS; i++)
8915 struct PlayerInfo *player = &stored_player[i];
8919 if (player->active && player->is_pushing && player->is_moving &&
8921 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
8922 Feld[x][y] == EL_SPRING))
8924 ContinueMoving(x, y);
8926 /* continue moving after pushing (this is actually a bug) */
8927 if (!IS_MOVING(x, y))
8936 SCAN_PLAYFIELD(x, y)
8938 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8941 ChangeCount[x][y] = 0;
8942 ChangeEvent[x][y] = -1;
8944 /* this must be handled before main playfield loop */
8945 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
8948 if (MovDelay[x][y] <= 0)
8952 #if USE_NEW_SNAP_DELAY
8953 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
8956 if (MovDelay[x][y] <= 0)
8959 DrawLevelField(x, y);
8961 TestIfElementTouchesCustomElement(x, y); /* for empty space */
8967 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
8969 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
8970 printf("GameActions(): This should never happen!\n");
8972 ChangePage[x][y] = -1;
8977 if (WasJustMoving[x][y] > 0)
8978 WasJustMoving[x][y]--;
8979 if (WasJustFalling[x][y] > 0)
8980 WasJustFalling[x][y]--;
8981 if (CheckCollision[x][y] > 0)
8982 CheckCollision[x][y]--;
8986 /* reset finished pushing action (not done in ContinueMoving() to allow
8987 continuous pushing animation for elements with zero push delay) */
8988 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
8990 ResetGfxAnimation(x, y);
8991 DrawLevelField(x, y);
8995 if (IS_BLOCKED(x, y))
8999 Blocked2Moving(x, y, &oldx, &oldy);
9000 if (!IS_MOVING(oldx, oldy))
9002 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
9003 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
9004 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
9005 printf("GameActions(): This should never happen!\n");
9012 SCAN_PLAYFIELD(x, y)
9014 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9017 element = Feld[x][y];
9018 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9021 printf("::: %d,%d\n", x, y);
9023 if (element == EL_ROCK)
9024 printf("::: Yo man! Rocks can fall!\n");
9027 if (graphic_info[graphic].anim_global_sync)
9028 GfxFrame[x][y] = FrameCounter;
9029 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
9031 int old_gfx_frame = GfxFrame[x][y];
9033 GfxFrame[x][y] = CustomValue[x][y];
9036 if (GfxFrame[x][y] != old_gfx_frame)
9038 DrawLevelGraphicAnimation(x, y, graphic);
9040 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
9042 int old_gfx_frame = GfxFrame[x][y];
9044 GfxFrame[x][y] = element_info[element].collect_score;
9047 if (GfxFrame[x][y] != old_gfx_frame)
9049 DrawLevelGraphicAnimation(x, y, graphic);
9052 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
9053 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
9054 ResetRandomAnimationValue(x, y);
9056 SetRandomAnimationValue(x, y);
9058 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
9060 if (IS_INACTIVE(element))
9062 if (IS_ANIMATED(graphic))
9063 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9068 /* this may take place after moving, so 'element' may have changed */
9069 if (IS_CHANGING(x, y) &&
9070 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
9072 int page = element_info[element].event_page_nr[CE_DELAY];
9074 HandleElementChange(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] : page);
9078 printf("::: ChangeDelay == %d\n", ChangeDelay[x][y]);
9082 if (element == EL_CUSTOM_255)
9083 printf("::: ChangeDelay == %d\n", ChangeDelay[x][y]);
9087 HandleElementChange(x, y, page);
9089 if (CAN_CHANGE(element))
9090 HandleElementChange(x, y, page);
9092 if (HAS_ACTION(element))
9093 ExecuteCustomElementAction(x, y, element, page);
9098 element = Feld[x][y];
9099 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9102 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
9106 element = Feld[x][y];
9107 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9109 if (IS_ANIMATED(graphic) &&
9112 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9114 if (IS_GEM(element) || element == EL_SP_INFOTRON)
9115 EdelsteinFunkeln(x, y);
9117 else if ((element == EL_ACID ||
9118 element == EL_EXIT_OPEN ||
9119 element == EL_SP_EXIT_OPEN ||
9120 element == EL_SP_TERMINAL ||
9121 element == EL_SP_TERMINAL_ACTIVE ||
9122 element == EL_EXTRA_TIME ||
9123 element == EL_SHIELD_NORMAL ||
9124 element == EL_SHIELD_DEADLY) &&
9125 IS_ANIMATED(graphic))
9126 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9127 else if (IS_MOVING(x, y))
9128 ContinueMoving(x, y);
9129 else if (IS_ACTIVE_BOMB(element))
9130 CheckDynamite(x, y);
9131 else if (element == EL_AMOEBA_GROWING)
9132 AmoebeWaechst(x, y);
9133 else if (element == EL_AMOEBA_SHRINKING)
9134 AmoebaDisappearing(x, y);
9136 #if !USE_NEW_AMOEBA_CODE
9137 else if (IS_AMOEBALIVE(element))
9138 AmoebeAbleger(x, y);
9141 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
9143 else if (element == EL_EXIT_CLOSED)
9145 else if (element == EL_SP_EXIT_CLOSED)
9147 else if (element == EL_EXPANDABLE_WALL_GROWING)
9149 else if (element == EL_EXPANDABLE_WALL ||
9150 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9151 element == EL_EXPANDABLE_WALL_VERTICAL ||
9152 element == EL_EXPANDABLE_WALL_ANY)
9154 else if (element == EL_FLAMES)
9155 CheckForDragon(x, y);
9156 else if (element == EL_EXPLOSION)
9157 ; /* drawing of correct explosion animation is handled separately */
9158 else if (element == EL_ELEMENT_SNAPPING ||
9159 element == EL_DIAGONAL_SHRINKING ||
9160 element == EL_DIAGONAL_GROWING)
9163 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
9165 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9168 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
9169 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9172 if (element == EL_CUSTOM_255 ||
9173 element == EL_CUSTOM_256)
9174 DrawLevelGraphicAnimation(x, y, graphic);
9177 if (IS_BELT_ACTIVE(element))
9178 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
9180 if (game.magic_wall_active)
9182 int jx = local_player->jx, jy = local_player->jy;
9184 /* play the element sound at the position nearest to the player */
9185 if ((element == EL_MAGIC_WALL_FULL ||
9186 element == EL_MAGIC_WALL_ACTIVE ||
9187 element == EL_MAGIC_WALL_EMPTYING ||
9188 element == EL_BD_MAGIC_WALL_FULL ||
9189 element == EL_BD_MAGIC_WALL_ACTIVE ||
9190 element == EL_BD_MAGIC_WALL_EMPTYING) &&
9191 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
9199 #if USE_NEW_AMOEBA_CODE
9200 /* new experimental amoeba growth stuff */
9201 if (!(FrameCounter % 8))
9203 static unsigned long random = 1684108901;
9205 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
9207 x = RND(lev_fieldx);
9208 y = RND(lev_fieldy);
9209 element = Feld[x][y];
9211 if (!IS_PLAYER(x,y) &&
9212 (element == EL_EMPTY ||
9213 CAN_GROW_INTO(element) ||
9214 element == EL_QUICKSAND_EMPTY ||
9215 element == EL_ACID_SPLASH_LEFT ||
9216 element == EL_ACID_SPLASH_RIGHT))
9218 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
9219 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
9220 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
9221 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
9222 Feld[x][y] = EL_AMOEBA_DROP;
9225 random = random * 129 + 1;
9231 if (game.explosions_delayed)
9234 game.explosions_delayed = FALSE;
9237 SCAN_PLAYFIELD(x, y)
9239 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9242 element = Feld[x][y];
9244 if (ExplodeField[x][y])
9245 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
9246 else if (element == EL_EXPLOSION)
9247 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
9249 ExplodeField[x][y] = EX_TYPE_NONE;
9252 game.explosions_delayed = TRUE;
9255 if (game.magic_wall_active)
9257 if (!(game.magic_wall_time_left % 4))
9259 int element = Feld[magic_wall_x][magic_wall_y];
9261 if (element == EL_BD_MAGIC_WALL_FULL ||
9262 element == EL_BD_MAGIC_WALL_ACTIVE ||
9263 element == EL_BD_MAGIC_WALL_EMPTYING)
9264 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
9266 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
9269 if (game.magic_wall_time_left > 0)
9271 game.magic_wall_time_left--;
9272 if (!game.magic_wall_time_left)
9275 SCAN_PLAYFIELD(x, y)
9277 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9280 element = Feld[x][y];
9282 if (element == EL_MAGIC_WALL_ACTIVE ||
9283 element == EL_MAGIC_WALL_FULL)
9285 Feld[x][y] = EL_MAGIC_WALL_DEAD;
9286 DrawLevelField(x, y);
9288 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
9289 element == EL_BD_MAGIC_WALL_FULL)
9291 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
9292 DrawLevelField(x, y);
9296 game.magic_wall_active = FALSE;
9301 if (game.light_time_left > 0)
9303 game.light_time_left--;
9305 if (game.light_time_left == 0)
9306 RedrawAllLightSwitchesAndInvisibleElements();
9309 if (game.timegate_time_left > 0)
9311 game.timegate_time_left--;
9313 if (game.timegate_time_left == 0)
9314 CloseAllOpenTimegates();
9317 if (game.lenses_time_left > 0)
9319 game.lenses_time_left--;
9321 if (game.lenses_time_left == 0)
9322 RedrawAllInvisibleElementsForLenses();
9325 if (game.magnify_time_left > 0)
9327 game.magnify_time_left--;
9329 if (game.magnify_time_left == 0)
9330 RedrawAllInvisibleElementsForMagnifier();
9333 for (i = 0; i < MAX_PLAYERS; i++)
9335 struct PlayerInfo *player = &stored_player[i];
9337 if (SHIELD_ON(player))
9339 if (player->shield_deadly_time_left)
9340 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
9341 else if (player->shield_normal_time_left)
9342 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
9346 if (TimeFrames >= FRAMES_PER_SECOND)
9351 for (i = 0; i < MAX_PLAYERS; i++)
9353 struct PlayerInfo *player = &stored_player[i];
9355 if (SHIELD_ON(player))
9357 player->shield_normal_time_left--;
9359 if (player->shield_deadly_time_left > 0)
9360 player->shield_deadly_time_left--;
9364 if (!level.use_step_counter)
9372 if (TimeLeft <= 10 && setup.time_limit)
9373 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
9375 DrawGameValue_Time(TimeLeft);
9377 if (!TimeLeft && setup.time_limit)
9378 for (i = 0; i < MAX_PLAYERS; i++)
9379 KillPlayer(&stored_player[i]);
9381 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
9382 DrawGameValue_Time(TimePlayed);
9385 if (tape.recording || tape.playing)
9386 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
9390 PlayAllPlayersSound();
9392 if (options.debug) /* calculate frames per second */
9394 static unsigned long fps_counter = 0;
9395 static int fps_frames = 0;
9396 unsigned long fps_delay_ms = Counter() - fps_counter;
9400 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
9402 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
9405 fps_counter = Counter();
9408 redraw_mask |= REDRAW_FPS;
9411 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
9413 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
9415 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
9417 local_player->show_envelope = 0;
9420 /* use random number generator in every frame to make it less predictable */
9421 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
9425 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
9427 int min_x = x, min_y = y, max_x = x, max_y = y;
9430 for (i = 0; i < MAX_PLAYERS; i++)
9432 int jx = stored_player[i].jx, jy = stored_player[i].jy;
9434 if (!stored_player[i].active || &stored_player[i] == player)
9437 min_x = MIN(min_x, jx);
9438 min_y = MIN(min_y, jy);
9439 max_x = MAX(max_x, jx);
9440 max_y = MAX(max_y, jy);
9443 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
9446 static boolean AllPlayersInVisibleScreen()
9450 for (i = 0; i < MAX_PLAYERS; i++)
9452 int jx = stored_player[i].jx, jy = stored_player[i].jy;
9454 if (!stored_player[i].active)
9457 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
9464 void ScrollLevel(int dx, int dy)
9466 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
9469 BlitBitmap(drawto_field, drawto_field,
9470 FX + TILEX * (dx == -1) - softscroll_offset,
9471 FY + TILEY * (dy == -1) - softscroll_offset,
9472 SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
9473 SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
9474 FX + TILEX * (dx == 1) - softscroll_offset,
9475 FY + TILEY * (dy == 1) - softscroll_offset);
9479 x = (dx == 1 ? BX1 : BX2);
9480 for (y = BY1; y <= BY2; y++)
9481 DrawScreenField(x, y);
9486 y = (dy == 1 ? BY1 : BY2);
9487 for (x = BX1; x <= BX2; x++)
9488 DrawScreenField(x, y);
9491 redraw_mask |= REDRAW_FIELD;
9494 static boolean canFallDown(struct PlayerInfo *player)
9496 int jx = player->jx, jy = player->jy;
9498 return (IN_LEV_FIELD(jx, jy + 1) &&
9499 (IS_FREE(jx, jy + 1) ||
9500 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
9501 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
9502 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
9505 static boolean canPassField(int x, int y, int move_dir)
9507 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9508 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9509 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
9512 int element = Feld[x][y];
9514 return (IS_PASSABLE_FROM(element, opposite_dir) &&
9515 !CAN_MOVE(element) &&
9516 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
9517 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
9518 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
9521 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
9523 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9524 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9525 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
9529 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9530 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
9531 (IS_DIGGABLE(Feld[newx][newy]) ||
9532 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9533 canPassField(newx, newy, move_dir)));
9536 static void CheckGravityMovement(struct PlayerInfo *player)
9538 if (game.gravity && !player->programmed_action)
9540 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
9541 int move_dir_vertical = player->effective_action & MV_VERTICAL;
9542 boolean player_is_snapping = player->effective_action & JOY_BUTTON_1;
9543 int jx = player->jx, jy = player->jy;
9544 boolean player_is_moving_to_valid_field =
9545 (!player_is_snapping &&
9546 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
9547 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
9548 boolean player_can_fall_down = canFallDown(player);
9550 if (player_can_fall_down &&
9551 !player_is_moving_to_valid_field)
9552 player->programmed_action = MV_DOWN;
9556 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
9558 return CheckGravityMovement(player);
9560 if (game.gravity && !player->programmed_action)
9562 int jx = player->jx, jy = player->jy;
9563 boolean field_under_player_is_free =
9564 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
9565 boolean player_is_standing_on_valid_field =
9566 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
9567 (IS_WALKABLE(Feld[jx][jy]) &&
9568 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
9570 if (field_under_player_is_free && !player_is_standing_on_valid_field)
9571 player->programmed_action = MV_DOWN;
9577 -----------------------------------------------------------------------------
9578 dx, dy: direction (non-diagonal) to try to move the player to
9579 real_dx, real_dy: direction as read from input device (can be diagonal)
9582 boolean MovePlayerOneStep(struct PlayerInfo *player,
9583 int dx, int dy, int real_dx, int real_dy)
9585 int jx = player->jx, jy = player->jy;
9586 int new_jx = jx + dx, new_jy = jy + dy;
9587 #if !USE_FIXED_DONT_RUN_INTO
9591 boolean player_can_move = !player->cannot_move;
9593 if (!player->active || (!dx && !dy))
9594 return MP_NO_ACTION;
9596 player->MovDir = (dx < 0 ? MV_LEFT :
9599 dy > 0 ? MV_DOWN : MV_NONE);
9601 if (!IN_LEV_FIELD(new_jx, new_jy))
9602 return MP_NO_ACTION;
9604 if (!player_can_move)
9607 if (player->MovPos == 0)
9609 player->is_moving = FALSE;
9610 player->is_digging = FALSE;
9611 player->is_collecting = FALSE;
9612 player->is_snapping = FALSE;
9613 player->is_pushing = FALSE;
9616 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
9617 SnapField(player, 0, 0);
9621 return MP_NO_ACTION;
9625 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
9626 return MP_NO_ACTION;
9628 #if !USE_FIXED_DONT_RUN_INTO
9629 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
9631 /* (moved to DigField()) */
9632 if (player_can_move && DONT_RUN_INTO(element))
9634 if (element == EL_ACID && dx == 0 && dy == 1)
9636 SplashAcid(new_jx, new_jy);
9637 Feld[jx][jy] = EL_PLAYER_1;
9638 InitMovingField(jx, jy, MV_DOWN);
9639 Store[jx][jy] = EL_ACID;
9640 ContinueMoving(jx, jy);
9644 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
9650 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
9652 #if USE_FIXED_DONT_RUN_INTO
9653 if (can_move == MP_DONT_RUN_INTO)
9657 if (can_move != MP_MOVING)
9660 #if USE_FIXED_DONT_RUN_INTO
9663 /* check if DigField() has caused relocation of the player */
9664 if (player->jx != jx || player->jy != jy)
9665 return MP_NO_ACTION; /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
9667 StorePlayer[jx][jy] = 0;
9668 player->last_jx = jx;
9669 player->last_jy = jy;
9670 player->jx = new_jx;
9671 player->jy = new_jy;
9672 StorePlayer[new_jx][new_jy] = player->element_nr;
9674 if (player->move_delay_value_next != -1)
9676 player->move_delay_value = player->move_delay_value_next;
9677 player->move_delay_value_next = -1;
9681 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
9683 player->step_counter++;
9685 PlayerVisit[jx][jy] = FrameCounter;
9687 ScrollPlayer(player, SCROLL_INIT);
9692 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
9694 int jx = player->jx, jy = player->jy;
9695 int old_jx = jx, old_jy = jy;
9696 int moved = MP_NO_ACTION;
9698 if (!player->active)
9703 if (player->MovPos == 0)
9705 player->is_moving = FALSE;
9706 player->is_digging = FALSE;
9707 player->is_collecting = FALSE;
9708 player->is_snapping = FALSE;
9709 player->is_pushing = FALSE;
9715 if (player->move_delay > 0)
9718 player->move_delay = -1; /* set to "uninitialized" value */
9720 /* store if player is automatically moved to next field */
9721 player->is_auto_moving = (player->programmed_action != MV_NONE);
9723 /* remove the last programmed player action */
9724 player->programmed_action = 0;
9728 /* should only happen if pre-1.2 tape recordings are played */
9729 /* this is only for backward compatibility */
9731 int original_move_delay_value = player->move_delay_value;
9734 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
9738 /* scroll remaining steps with finest movement resolution */
9739 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
9741 while (player->MovPos)
9743 ScrollPlayer(player, SCROLL_GO_ON);
9744 ScrollScreen(NULL, SCROLL_GO_ON);
9746 AdvanceFrameAndPlayerCounters(player->index_nr);
9752 player->move_delay_value = original_move_delay_value;
9755 if (player->last_move_dir & MV_HORIZONTAL)
9757 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
9758 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
9762 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
9763 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
9769 if (moved & MP_MOVING && !ScreenMovPos &&
9770 (player == local_player || !options.network))
9772 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
9773 int offset = (setup.scroll_delay ? 3 : 0);
9775 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
9777 /* actual player has left the screen -- scroll in that direction */
9778 if (jx != old_jx) /* player has moved horizontally */
9779 scroll_x += (jx - old_jx);
9780 else /* player has moved vertically */
9781 scroll_y += (jy - old_jy);
9785 if (jx != old_jx) /* player has moved horizontally */
9787 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
9788 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
9789 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
9791 /* don't scroll over playfield boundaries */
9792 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
9793 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
9795 /* don't scroll more than one field at a time */
9796 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
9798 /* don't scroll against the player's moving direction */
9799 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
9800 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
9801 scroll_x = old_scroll_x;
9803 else /* player has moved vertically */
9805 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
9806 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
9807 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
9809 /* don't scroll over playfield boundaries */
9810 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
9811 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
9813 /* don't scroll more than one field at a time */
9814 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
9816 /* don't scroll against the player's moving direction */
9817 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
9818 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
9819 scroll_y = old_scroll_y;
9823 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
9825 if (!options.network && !AllPlayersInVisibleScreen())
9827 scroll_x = old_scroll_x;
9828 scroll_y = old_scroll_y;
9832 ScrollScreen(player, SCROLL_INIT);
9833 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
9838 player->StepFrame = 0;
9840 if (moved & MP_MOVING)
9842 if (old_jx != jx && old_jy == jy)
9843 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
9844 else if (old_jx == jx && old_jy != jy)
9845 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
9847 DrawLevelField(jx, jy); /* for "crumbled sand" */
9849 player->last_move_dir = player->MovDir;
9850 player->is_moving = TRUE;
9851 player->is_snapping = FALSE;
9852 player->is_switching = FALSE;
9853 player->is_dropping = FALSE;
9857 CheckGravityMovementWhenNotMoving(player);
9859 player->is_moving = FALSE;
9861 /* at this point, the player is allowed to move, but cannot move right now
9862 (e.g. because of something blocking the way) -- ensure that the player
9863 is also allowed to move in the next frame (in old versions before 3.1.1,
9864 the player was forced to wait again for eight frames before next try) */
9866 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
9867 player->move_delay = 0; /* allow direct movement in the next frame */
9870 if (player->move_delay == -1) /* not yet initialized by DigField() */
9871 player->move_delay = player->move_delay_value;
9873 if (game.engine_version < VERSION_IDENT(3,0,7,0))
9875 TestIfPlayerTouchesBadThing(jx, jy);
9876 TestIfPlayerTouchesCustomElement(jx, jy);
9879 if (!player->active)
9880 RemovePlayer(player);
9885 void ScrollPlayer(struct PlayerInfo *player, int mode)
9887 int jx = player->jx, jy = player->jy;
9888 int last_jx = player->last_jx, last_jy = player->last_jy;
9889 int move_stepsize = TILEX / player->move_delay_value;
9891 #if USE_NEW_PLAYER_SPEED
9892 if (!player->active)
9895 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
9898 if (!player->active || player->MovPos == 0)
9902 if (mode == SCROLL_INIT)
9904 player->actual_frame_counter = FrameCounter;
9905 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
9907 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
9908 Feld[last_jx][last_jy] == EL_EMPTY)
9910 int last_field_block_delay = 0; /* start with no blocking at all */
9911 int block_delay_adjustment = player->block_delay_adjustment;
9913 /* if player blocks last field, add delay for exactly one move */
9914 if (player->block_last_field)
9916 last_field_block_delay += player->move_delay_value;
9918 /* when blocking enabled, prevent moving up despite gravity */
9919 if (game.gravity && player->MovDir == MV_UP)
9920 block_delay_adjustment = -1;
9923 /* add block delay adjustment (also possible when not blocking) */
9924 last_field_block_delay += block_delay_adjustment;
9926 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
9927 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
9930 #if USE_NEW_PLAYER_SPEED
9931 if (player->MovPos != 0) /* player has not yet reached destination */
9937 else if (!FrameReached(&player->actual_frame_counter, 1))
9941 printf("::: player->MovPos: %d -> %d\n",
9943 player->MovPos + (player->MovPos > 0 ? -1 : 1) * move_stepsize);
9946 #if USE_NEW_PLAYER_SPEED
9947 if (player->MovPos != 0)
9949 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
9950 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
9952 /* before DrawPlayer() to draw correct player graphic for this case */
9953 if (player->MovPos == 0)
9954 CheckGravityMovement(player);
9957 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
9958 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
9960 /* before DrawPlayer() to draw correct player graphic for this case */
9961 if (player->MovPos == 0)
9962 CheckGravityMovement(player);
9965 if (player->MovPos == 0) /* player reached destination field */
9968 printf("::: player reached destination field\n");
9971 if (player->move_delay_reset_counter > 0)
9973 player->move_delay_reset_counter--;
9975 if (player->move_delay_reset_counter == 0)
9977 /* continue with normal speed after quickly moving through gate */
9978 HALVE_PLAYER_SPEED(player);
9980 /* be able to make the next move without delay */
9981 player->move_delay = 0;
9985 player->last_jx = jx;
9986 player->last_jy = jy;
9988 if (Feld[jx][jy] == EL_EXIT_OPEN ||
9989 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
9990 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
9992 DrawPlayer(player); /* needed here only to cleanup last field */
9993 RemovePlayer(player);
9995 if (local_player->friends_still_needed == 0 ||
9996 IS_SP_ELEMENT(Feld[jx][jy]))
9997 player->LevelSolved = player->GameOver = TRUE;
10000 /* this breaks one level: "machine", level 000 */
10002 int move_direction = player->MovDir;
10003 int enter_side = MV_DIR_OPPOSITE(move_direction);
10004 int leave_side = move_direction;
10005 int old_jx = last_jx;
10006 int old_jy = last_jy;
10007 int old_element = Feld[old_jx][old_jy];
10008 int new_element = Feld[jx][jy];
10010 if (IS_CUSTOM_ELEMENT(old_element))
10011 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
10013 player->index_bit, leave_side);
10015 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
10016 CE_PLAYER_LEAVES_X,
10017 player->index_bit, leave_side);
10019 if (IS_CUSTOM_ELEMENT(new_element))
10020 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
10021 player->index_bit, enter_side);
10023 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
10024 CE_PLAYER_ENTERS_X,
10025 player->index_bit, enter_side);
10027 CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
10028 CE_MOVE_OF_X, move_direction);
10031 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10033 TestIfPlayerTouchesBadThing(jx, jy);
10034 TestIfPlayerTouchesCustomElement(jx, jy);
10036 /* needed because pushed element has not yet reached its destination,
10037 so it would trigger a change event at its previous field location */
10038 if (!player->is_pushing)
10039 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
10041 if (!player->active)
10042 RemovePlayer(player);
10045 if (level.use_step_counter)
10055 if (TimeLeft <= 10 && setup.time_limit)
10056 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
10058 DrawGameValue_Time(TimeLeft);
10060 if (!TimeLeft && setup.time_limit)
10061 for (i = 0; i < MAX_PLAYERS; i++)
10062 KillPlayer(&stored_player[i]);
10064 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
10065 DrawGameValue_Time(TimePlayed);
10068 if (tape.single_step && tape.recording && !tape.pausing &&
10069 !player->programmed_action)
10070 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10074 void ScrollScreen(struct PlayerInfo *player, int mode)
10076 static unsigned long screen_frame_counter = 0;
10078 if (mode == SCROLL_INIT)
10080 /* set scrolling step size according to actual player's moving speed */
10081 ScrollStepSize = TILEX / player->move_delay_value;
10083 screen_frame_counter = FrameCounter;
10084 ScreenMovDir = player->MovDir;
10085 ScreenMovPos = player->MovPos;
10086 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
10089 else if (!FrameReached(&screen_frame_counter, 1))
10094 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
10095 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
10096 redraw_mask |= REDRAW_FIELD;
10099 ScreenMovDir = MV_NONE;
10102 void TestIfPlayerTouchesCustomElement(int x, int y)
10104 static int xy[4][2] =
10111 static int trigger_sides[4][2] =
10113 /* center side border side */
10114 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
10115 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
10116 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
10117 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
10119 static int touch_dir[4] =
10121 MV_LEFT | MV_RIGHT,
10126 int center_element = Feld[x][y]; /* should always be non-moving! */
10129 for (i = 0; i < NUM_DIRECTIONS; i++)
10131 int xx = x + xy[i][0];
10132 int yy = y + xy[i][1];
10133 int center_side = trigger_sides[i][0];
10134 int border_side = trigger_sides[i][1];
10135 int border_element;
10137 if (!IN_LEV_FIELD(xx, yy))
10140 if (IS_PLAYER(x, y))
10142 struct PlayerInfo *player = PLAYERINFO(x, y);
10144 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10145 border_element = Feld[xx][yy]; /* may be moving! */
10146 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
10147 border_element = Feld[xx][yy];
10148 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
10149 border_element = MovingOrBlocked2Element(xx, yy);
10151 continue; /* center and border element do not touch */
10153 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
10154 player->index_bit, border_side);
10155 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
10156 CE_PLAYER_TOUCHES_X,
10157 player->index_bit, border_side);
10159 else if (IS_PLAYER(xx, yy))
10161 struct PlayerInfo *player = PLAYERINFO(xx, yy);
10163 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10165 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
10166 continue; /* center and border element do not touch */
10169 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
10170 player->index_bit, center_side);
10171 CheckTriggeredElementChangeByPlayer(x, y, center_element,
10172 CE_PLAYER_TOUCHES_X,
10173 player->index_bit, center_side);
10179 void TestIfElementTouchesCustomElement(int x, int y)
10181 static int xy[4][2] =
10188 static int trigger_sides[4][2] =
10190 /* center side border side */
10191 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
10192 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
10193 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
10194 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
10196 static int touch_dir[4] =
10198 MV_LEFT | MV_RIGHT,
10203 boolean change_center_element = FALSE;
10204 int center_element = Feld[x][y]; /* should always be non-moving! */
10207 for (i = 0; i < NUM_DIRECTIONS; i++)
10209 int xx = x + xy[i][0];
10210 int yy = y + xy[i][1];
10211 int center_side = trigger_sides[i][0];
10212 int border_side = trigger_sides[i][1];
10213 int border_element;
10215 if (!IN_LEV_FIELD(xx, yy))
10218 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10219 border_element = Feld[xx][yy]; /* may be moving! */
10220 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
10221 border_element = Feld[xx][yy];
10222 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
10223 border_element = MovingOrBlocked2Element(xx, yy);
10225 continue; /* center and border element do not touch */
10227 /* check for change of center element (but change it only once) */
10228 if (!change_center_element)
10229 change_center_element =
10230 CheckElementChangeBySide(x, y, center_element, border_element,
10231 CE_TOUCHING_X, border_side);
10233 /* check for change of border element */
10234 CheckElementChangeBySide(xx, yy, border_element, center_element,
10235 CE_TOUCHING_X, center_side);
10239 void TestIfElementHitsCustomElement(int x, int y, int direction)
10241 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
10242 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
10243 int hitx = x + dx, hity = y + dy;
10244 int hitting_element = Feld[x][y];
10245 int touched_element;
10247 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
10250 touched_element = (IN_LEV_FIELD(hitx, hity) ?
10251 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
10253 if (IN_LEV_FIELD(hitx, hity))
10255 int opposite_direction = MV_DIR_OPPOSITE(direction);
10256 int hitting_side = direction;
10257 int touched_side = opposite_direction;
10258 boolean object_hit = (!IS_MOVING(hitx, hity) ||
10259 MovDir[hitx][hity] != direction ||
10260 ABS(MovPos[hitx][hity]) <= TILEY / 2);
10266 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10267 CE_HITTING_X, touched_side);
10269 CheckElementChangeBySide(hitx, hity, touched_element,
10270 hitting_element, CE_HIT_BY_X, hitting_side);
10272 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10273 CE_HIT_BY_SOMETHING, opposite_direction);
10277 /* "hitting something" is also true when hitting the playfield border */
10278 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10279 CE_HITTING_SOMETHING, direction);
10283 void TestIfElementSmashesCustomElement(int x, int y, int direction)
10285 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
10286 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
10287 int hitx = x + dx, hity = y + dy;
10288 int hitting_element = Feld[x][y];
10289 int touched_element;
10291 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
10292 !IS_FREE(hitx, hity) &&
10293 (!IS_MOVING(hitx, hity) ||
10294 MovDir[hitx][hity] != direction ||
10295 ABS(MovPos[hitx][hity]) <= TILEY / 2));
10298 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
10302 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
10306 touched_element = (IN_LEV_FIELD(hitx, hity) ?
10307 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
10309 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10310 EP_CAN_SMASH_EVERYTHING, direction);
10312 if (IN_LEV_FIELD(hitx, hity))
10314 int opposite_direction = MV_DIR_OPPOSITE(direction);
10315 int hitting_side = direction;
10316 int touched_side = opposite_direction;
10318 int touched_element = MovingOrBlocked2Element(hitx, hity);
10321 boolean object_hit = (!IS_MOVING(hitx, hity) ||
10322 MovDir[hitx][hity] != direction ||
10323 ABS(MovPos[hitx][hity]) <= TILEY / 2);
10332 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10333 CE_SMASHED_BY_SOMETHING, opposite_direction);
10335 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10336 CE_OTHER_IS_SMASHING, touched_side);
10338 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10339 CE_OTHER_GETS_SMASHED, hitting_side);
10345 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
10347 int i, kill_x = -1, kill_y = -1;
10349 int bad_element = -1;
10350 static int test_xy[4][2] =
10357 static int test_dir[4] =
10365 for (i = 0; i < NUM_DIRECTIONS; i++)
10367 int test_x, test_y, test_move_dir, test_element;
10369 test_x = good_x + test_xy[i][0];
10370 test_y = good_y + test_xy[i][1];
10372 if (!IN_LEV_FIELD(test_x, test_y))
10376 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
10378 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
10380 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
10381 2nd case: DONT_TOUCH style bad thing does not move away from good thing
10383 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
10384 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
10388 bad_element = test_element;
10394 if (kill_x != -1 || kill_y != -1)
10396 if (IS_PLAYER(good_x, good_y))
10398 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
10400 if (player->shield_deadly_time_left > 0 &&
10401 !IS_INDESTRUCTIBLE(bad_element))
10402 Bang(kill_x, kill_y);
10403 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
10404 KillPlayer(player);
10407 Bang(good_x, good_y);
10411 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
10413 int i, kill_x = -1, kill_y = -1;
10414 int bad_element = Feld[bad_x][bad_y];
10415 static int test_xy[4][2] =
10422 static int touch_dir[4] =
10424 MV_LEFT | MV_RIGHT,
10429 static int test_dir[4] =
10437 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
10440 for (i = 0; i < NUM_DIRECTIONS; i++)
10442 int test_x, test_y, test_move_dir, test_element;
10444 test_x = bad_x + test_xy[i][0];
10445 test_y = bad_y + test_xy[i][1];
10446 if (!IN_LEV_FIELD(test_x, test_y))
10450 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
10452 test_element = Feld[test_x][test_y];
10454 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
10455 2nd case: DONT_TOUCH style bad thing does not move away from good thing
10457 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
10458 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
10460 /* good thing is player or penguin that does not move away */
10461 if (IS_PLAYER(test_x, test_y))
10463 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
10465 if (bad_element == EL_ROBOT && player->is_moving)
10466 continue; /* robot does not kill player if he is moving */
10468 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10470 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
10471 continue; /* center and border element do not touch */
10478 else if (test_element == EL_PENGUIN)
10487 if (kill_x != -1 || kill_y != -1)
10489 if (IS_PLAYER(kill_x, kill_y))
10491 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
10493 if (player->shield_deadly_time_left > 0 &&
10494 !IS_INDESTRUCTIBLE(bad_element))
10495 Bang(bad_x, bad_y);
10496 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
10497 KillPlayer(player);
10500 Bang(kill_x, kill_y);
10504 void TestIfPlayerTouchesBadThing(int x, int y)
10506 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
10509 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
10511 TestIfGoodThingHitsBadThing(x, y, move_dir);
10514 void TestIfBadThingTouchesPlayer(int x, int y)
10516 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
10519 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
10521 TestIfBadThingHitsGoodThing(x, y, move_dir);
10524 void TestIfFriendTouchesBadThing(int x, int y)
10526 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
10529 void TestIfBadThingTouchesFriend(int x, int y)
10531 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
10534 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
10536 int i, kill_x = bad_x, kill_y = bad_y;
10537 static int xy[4][2] =
10545 for (i = 0; i < NUM_DIRECTIONS; i++)
10549 x = bad_x + xy[i][0];
10550 y = bad_y + xy[i][1];
10551 if (!IN_LEV_FIELD(x, y))
10554 element = Feld[x][y];
10555 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
10556 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
10564 if (kill_x != bad_x || kill_y != bad_y)
10565 Bang(bad_x, bad_y);
10568 void KillPlayer(struct PlayerInfo *player)
10570 int jx = player->jx, jy = player->jy;
10572 if (!player->active)
10575 /* remove accessible field at the player's position */
10576 Feld[jx][jy] = EL_EMPTY;
10578 /* deactivate shield (else Bang()/Explode() would not work right) */
10579 player->shield_normal_time_left = 0;
10580 player->shield_deadly_time_left = 0;
10583 BuryPlayer(player);
10586 static void KillPlayerUnlessEnemyProtected(int x, int y)
10588 if (!PLAYER_ENEMY_PROTECTED(x, y))
10589 KillPlayer(PLAYERINFO(x, y));
10592 static void KillPlayerUnlessExplosionProtected(int x, int y)
10594 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
10595 KillPlayer(PLAYERINFO(x, y));
10598 void BuryPlayer(struct PlayerInfo *player)
10600 int jx = player->jx, jy = player->jy;
10602 if (!player->active)
10605 PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
10606 PlayLevelSound(jx, jy, SND_GAME_LOSING);
10608 player->GameOver = TRUE;
10609 RemovePlayer(player);
10612 void RemovePlayer(struct PlayerInfo *player)
10614 int jx = player->jx, jy = player->jy;
10615 int i, found = FALSE;
10617 player->present = FALSE;
10618 player->active = FALSE;
10620 if (!ExplodeField[jx][jy])
10621 StorePlayer[jx][jy] = 0;
10623 if (player->is_moving)
10624 DrawLevelField(player->last_jx, player->last_jy);
10626 for (i = 0; i < MAX_PLAYERS; i++)
10627 if (stored_player[i].active)
10631 AllPlayersGone = TRUE;
10637 #if USE_NEW_SNAP_DELAY
10638 static void setFieldForSnapping(int x, int y, int element, int direction)
10640 struct ElementInfo *ei = &element_info[element];
10641 int direction_bit = MV_DIR_TO_BIT(direction);
10642 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
10643 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
10644 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
10646 Feld[x][y] = EL_ELEMENT_SNAPPING;
10647 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
10649 ResetGfxAnimation(x, y);
10651 GfxElement[x][y] = element;
10652 GfxAction[x][y] = action;
10653 GfxDir[x][y] = direction;
10654 GfxFrame[x][y] = -1;
10659 =============================================================================
10660 checkDiagonalPushing()
10661 -----------------------------------------------------------------------------
10662 check if diagonal input device direction results in pushing of object
10663 (by checking if the alternative direction is walkable, diggable, ...)
10664 =============================================================================
10667 static boolean checkDiagonalPushing(struct PlayerInfo *player,
10668 int x, int y, int real_dx, int real_dy)
10670 int jx, jy, dx, dy, xx, yy;
10672 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
10675 /* diagonal direction: check alternative direction */
10680 xx = jx + (dx == 0 ? real_dx : 0);
10681 yy = jy + (dy == 0 ? real_dy : 0);
10683 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
10687 =============================================================================
10689 -----------------------------------------------------------------------------
10690 x, y: field next to player (non-diagonal) to try to dig to
10691 real_dx, real_dy: direction as read from input device (can be diagonal)
10692 =============================================================================
10695 int DigField(struct PlayerInfo *player,
10696 int oldx, int oldy, int x, int y,
10697 int real_dx, int real_dy, int mode)
10699 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
10700 boolean player_was_pushing = player->is_pushing;
10701 boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
10702 boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
10703 int jx = oldx, jy = oldy;
10704 int dx = x - jx, dy = y - jy;
10705 int nextx = x + dx, nexty = y + dy;
10706 int move_direction = (dx == -1 ? MV_LEFT :
10707 dx == +1 ? MV_RIGHT :
10709 dy == +1 ? MV_DOWN : MV_NONE);
10710 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
10711 int dig_side = MV_DIR_OPPOSITE(move_direction);
10712 int old_element = Feld[jx][jy];
10713 #if USE_FIXED_DONT_RUN_INTO
10714 int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
10720 if (is_player) /* function can also be called by EL_PENGUIN */
10722 if (player->MovPos == 0)
10724 player->is_digging = FALSE;
10725 player->is_collecting = FALSE;
10728 if (player->MovPos == 0) /* last pushing move finished */
10729 player->is_pushing = FALSE;
10731 if (mode == DF_NO_PUSH) /* player just stopped pushing */
10733 player->is_switching = FALSE;
10734 player->push_delay = -1;
10736 return MP_NO_ACTION;
10740 #if !USE_FIXED_DONT_RUN_INTO
10741 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
10742 return MP_NO_ACTION;
10745 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
10746 old_element = Back[jx][jy];
10748 /* in case of element dropped at player position, check background */
10749 else if (Back[jx][jy] != EL_EMPTY &&
10750 game.engine_version >= VERSION_IDENT(2,2,0,0))
10751 old_element = Back[jx][jy];
10754 #if USE_FIXED_DONT_RUN_INTO
10755 if (player_can_move && DONT_RUN_INTO(element))
10757 if (element == EL_ACID && dx == 0 && dy == 1)
10760 Feld[jx][jy] = EL_PLAYER_1;
10761 InitMovingField(jx, jy, MV_DOWN);
10762 Store[jx][jy] = EL_ACID;
10763 ContinueMoving(jx, jy);
10764 BuryPlayer(player);
10767 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
10769 return MP_DONT_RUN_INTO;
10775 #if USE_FIXED_DONT_RUN_INTO
10776 if (player_can_move && DONT_RUN_INTO(element))
10778 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
10780 return MP_DONT_RUN_INTO;
10785 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
10786 return MP_NO_ACTION; /* field has no opening in this direction */
10788 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
10789 return MP_NO_ACTION; /* field has no opening in this direction */
10792 #if USE_FIXED_DONT_RUN_INTO
10793 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
10796 Feld[jx][jy] = EL_PLAYER_1;
10797 InitMovingField(jx, jy, MV_DOWN);
10798 Store[jx][jy] = EL_ACID;
10799 ContinueMoving(jx, jy);
10800 BuryPlayer(player);
10802 return MP_DONT_RUN_INTO;
10808 #if USE_FIXED_DONT_RUN_INTO
10809 if (player_can_move && DONT_RUN_INTO(element))
10811 if (element == EL_ACID && dx == 0 && dy == 1)
10814 Feld[jx][jy] = EL_PLAYER_1;
10815 InitMovingField(jx, jy, MV_DOWN);
10816 Store[jx][jy] = EL_ACID;
10817 ContinueMoving(jx, jy);
10818 BuryPlayer(player);
10821 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
10823 return MP_DONT_RUN_INTO;
10828 #if USE_FIXED_DONT_RUN_INTO
10829 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
10830 return MP_NO_ACTION;
10833 #if !USE_FIXED_DONT_RUN_INTO
10834 element = Feld[x][y];
10837 collect_count = element_info[element].collect_count_initial;
10839 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
10840 return MP_NO_ACTION;
10842 if (game.engine_version < VERSION_IDENT(2,2,0,0))
10843 player_can_move = player_can_move_or_snap;
10845 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
10846 game.engine_version >= VERSION_IDENT(2,2,0,0))
10848 CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
10849 player->index_bit, dig_side);
10850 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
10851 player->index_bit, dig_side);
10853 if (Feld[x][y] != element) /* field changed by snapping */
10856 return MP_NO_ACTION;
10859 if (game.gravity && is_player && !player->is_auto_moving &&
10860 canFallDown(player) && move_direction != MV_DOWN &&
10861 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
10862 return MP_NO_ACTION; /* player cannot walk here due to gravity */
10864 if (player_can_move &&
10865 IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
10867 int sound_element = SND_ELEMENT(element);
10868 int sound_action = ACTION_WALKING;
10870 if (IS_RND_GATE(element))
10872 if (!player->key[RND_GATE_NR(element)])
10873 return MP_NO_ACTION;
10875 else if (IS_RND_GATE_GRAY(element))
10877 if (!player->key[RND_GATE_GRAY_NR(element)])
10878 return MP_NO_ACTION;
10880 else if (IS_RND_GATE_GRAY_ACTIVE(element))
10882 if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
10883 return MP_NO_ACTION;
10885 else if (element == EL_EXIT_OPEN ||
10886 element == EL_SP_EXIT_OPEN ||
10887 element == EL_SP_EXIT_OPENING)
10889 sound_action = ACTION_PASSING; /* player is passing exit */
10891 else if (element == EL_EMPTY)
10893 sound_action = ACTION_MOVING; /* nothing to walk on */
10896 /* play sound from background or player, whatever is available */
10897 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
10898 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
10900 PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
10902 else if (player_can_move &&
10903 IS_PASSABLE(element) && canPassField(x, y, move_direction))
10905 if (!ACCESS_FROM(element, opposite_direction))
10906 return MP_NO_ACTION; /* field not accessible from this direction */
10908 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
10909 return MP_NO_ACTION;
10911 if (IS_EM_GATE(element))
10913 if (!player->key[EM_GATE_NR(element)])
10914 return MP_NO_ACTION;
10916 else if (IS_EM_GATE_GRAY(element))
10918 if (!player->key[EM_GATE_GRAY_NR(element)])
10919 return MP_NO_ACTION;
10921 else if (IS_EM_GATE_GRAY_ACTIVE(element))
10923 if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
10924 return MP_NO_ACTION;
10926 else if (IS_SP_PORT(element))
10928 if (element == EL_SP_GRAVITY_PORT_LEFT ||
10929 element == EL_SP_GRAVITY_PORT_RIGHT ||
10930 element == EL_SP_GRAVITY_PORT_UP ||
10931 element == EL_SP_GRAVITY_PORT_DOWN)
10932 game.gravity = !game.gravity;
10933 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
10934 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
10935 element == EL_SP_GRAVITY_ON_PORT_UP ||
10936 element == EL_SP_GRAVITY_ON_PORT_DOWN)
10937 game.gravity = TRUE;
10938 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
10939 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
10940 element == EL_SP_GRAVITY_OFF_PORT_UP ||
10941 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
10942 game.gravity = FALSE;
10945 /* automatically move to the next field with double speed */
10946 player->programmed_action = move_direction;
10948 if (player->move_delay_reset_counter == 0)
10950 player->move_delay_reset_counter = 2; /* two double speed steps */
10952 DOUBLE_PLAYER_SPEED(player);
10955 PlayLevelSoundAction(x, y, ACTION_PASSING);
10957 else if (player_can_move_or_snap && IS_DIGGABLE(element))
10961 if (mode != DF_SNAP)
10963 GfxElement[x][y] = GFX_ELEMENT(element);
10964 player->is_digging = TRUE;
10967 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
10969 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
10970 player->index_bit, dig_side);
10972 if (mode == DF_SNAP)
10974 #if USE_NEW_SNAP_DELAY
10975 if (level.block_snap_field)
10976 setFieldForSnapping(x, y, element, move_direction);
10978 TestIfElementTouchesCustomElement(x, y); /* for empty space */
10980 TestIfElementTouchesCustomElement(x, y); /* for empty space */
10983 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
10984 player->index_bit, dig_side);
10987 else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
10991 if (is_player && mode != DF_SNAP)
10993 GfxElement[x][y] = element;
10994 player->is_collecting = TRUE;
10997 if (element == EL_SPEED_PILL)
10999 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
11001 else if (element == EL_EXTRA_TIME && level.time > 0)
11003 TimeLeft += level.extra_time;
11004 DrawGameValue_Time(TimeLeft);
11006 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
11008 player->shield_normal_time_left += level.shield_normal_time;
11009 if (element == EL_SHIELD_DEADLY)
11010 player->shield_deadly_time_left += level.shield_deadly_time;
11012 else if (element == EL_DYNAMITE || element == EL_SP_DISK_RED)
11014 if (player->inventory_size < MAX_INVENTORY_SIZE)
11015 player->inventory_element[player->inventory_size++] = element;
11017 DrawGameValue_Dynamite(local_player->inventory_size);
11019 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
11021 player->dynabomb_count++;
11022 player->dynabombs_left++;
11024 else if (element == EL_DYNABOMB_INCREASE_SIZE)
11026 player->dynabomb_size++;
11028 else if (element == EL_DYNABOMB_INCREASE_POWER)
11030 player->dynabomb_xl = TRUE;
11032 else if (IS_KEY(element))
11034 player->key[KEY_NR(element)] = TRUE;
11036 DrawGameValue_Keys(player->key);
11038 redraw_mask |= REDRAW_DOOR_1;
11040 else if (IS_ENVELOPE(element))
11042 player->show_envelope = element;
11044 else if (element == EL_EMC_LENSES)
11046 game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
11048 RedrawAllInvisibleElementsForLenses();
11050 else if (element == EL_EMC_MAGNIFIER)
11052 game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
11054 RedrawAllInvisibleElementsForMagnifier();
11056 else if (IS_DROPPABLE(element) ||
11057 IS_THROWABLE(element)) /* can be collected and dropped */
11061 if (collect_count == 0)
11062 player->inventory_infinite_element = element;
11064 for (i = 0; i < collect_count; i++)
11065 if (player->inventory_size < MAX_INVENTORY_SIZE)
11066 player->inventory_element[player->inventory_size++] = element;
11068 DrawGameValue_Dynamite(local_player->inventory_size);
11070 else if (collect_count > 0)
11072 local_player->gems_still_needed -= collect_count;
11073 if (local_player->gems_still_needed < 0)
11074 local_player->gems_still_needed = 0;
11076 DrawGameValue_Emeralds(local_player->gems_still_needed);
11079 RaiseScoreElement(element);
11080 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
11083 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
11084 player->index_bit, dig_side);
11086 if (mode == DF_SNAP)
11088 #if USE_NEW_SNAP_DELAY
11089 if (level.block_snap_field)
11090 setFieldForSnapping(x, y, element, move_direction);
11092 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11094 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11097 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
11098 player->index_bit, dig_side);
11101 else if (player_can_move_or_snap && IS_PUSHABLE(element))
11103 if (mode == DF_SNAP && element != EL_BD_ROCK)
11104 return MP_NO_ACTION;
11106 if (CAN_FALL(element) && dy)
11107 return MP_NO_ACTION;
11109 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
11110 !(element == EL_SPRING && level.use_spring_bug))
11111 return MP_NO_ACTION;
11113 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
11114 ((move_direction & MV_VERTICAL &&
11115 ((element_info[element].move_pattern & MV_LEFT &&
11116 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
11117 (element_info[element].move_pattern & MV_RIGHT &&
11118 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
11119 (move_direction & MV_HORIZONTAL &&
11120 ((element_info[element].move_pattern & MV_UP &&
11121 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
11122 (element_info[element].move_pattern & MV_DOWN &&
11123 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
11124 return MP_NO_ACTION;
11126 /* do not push elements already moving away faster than player */
11127 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
11128 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
11129 return MP_NO_ACTION;
11131 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
11133 if (player->push_delay_value == -1 || !player_was_pushing)
11134 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11136 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11138 if (player->push_delay_value == -1)
11139 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11141 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
11143 if (!player->is_pushing)
11144 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11147 player->is_pushing = TRUE;
11149 if (!(IN_LEV_FIELD(nextx, nexty) &&
11150 (IS_FREE(nextx, nexty) ||
11151 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
11152 IS_SB_ELEMENT(element)))))
11153 return MP_NO_ACTION;
11155 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
11156 return MP_NO_ACTION;
11158 if (player->push_delay == -1) /* new pushing; restart delay */
11159 player->push_delay = 0;
11161 if (player->push_delay < player->push_delay_value &&
11162 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
11163 element != EL_SPRING && element != EL_BALLOON)
11165 /* make sure that there is no move delay before next try to push */
11166 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11167 player->move_delay = 0;
11169 return MP_NO_ACTION;
11172 if (IS_SB_ELEMENT(element))
11174 if (element == EL_SOKOBAN_FIELD_FULL)
11176 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
11177 local_player->sokobanfields_still_needed++;
11180 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
11182 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
11183 local_player->sokobanfields_still_needed--;
11186 Feld[x][y] = EL_SOKOBAN_OBJECT;
11188 if (Back[x][y] == Back[nextx][nexty])
11189 PlayLevelSoundAction(x, y, ACTION_PUSHING);
11190 else if (Back[x][y] != 0)
11191 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
11194 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
11197 if (local_player->sokobanfields_still_needed == 0 &&
11198 game.emulation == EMU_SOKOBAN)
11200 player->LevelSolved = player->GameOver = TRUE;
11201 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
11205 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
11207 InitMovingField(x, y, move_direction);
11208 GfxAction[x][y] = ACTION_PUSHING;
11210 if (mode == DF_SNAP)
11211 ContinueMoving(x, y);
11213 MovPos[x][y] = (dx != 0 ? dx : dy);
11215 Pushed[x][y] = TRUE;
11216 Pushed[nextx][nexty] = TRUE;
11218 if (game.engine_version < VERSION_IDENT(2,2,0,7))
11219 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11221 player->push_delay_value = -1; /* get new value later */
11223 /* check for element change _after_ element has been pushed */
11224 if (game.use_change_when_pushing_bug)
11226 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
11227 player->index_bit, dig_side);
11228 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
11229 player->index_bit, dig_side);
11232 else if (IS_SWITCHABLE(element))
11234 if (PLAYER_SWITCHING(player, x, y))
11236 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
11237 player->index_bit, dig_side);
11242 player->is_switching = TRUE;
11243 player->switch_x = x;
11244 player->switch_y = y;
11246 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
11248 if (element == EL_ROBOT_WHEEL)
11250 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
11254 DrawLevelField(x, y);
11256 else if (element == EL_SP_TERMINAL)
11261 SCAN_PLAYFIELD(xx, yy)
11263 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
11266 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
11268 else if (Feld[xx][yy] == EL_SP_TERMINAL)
11269 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
11272 else if (IS_BELT_SWITCH(element))
11274 ToggleBeltSwitch(x, y);
11276 else if (element == EL_SWITCHGATE_SWITCH_UP ||
11277 element == EL_SWITCHGATE_SWITCH_DOWN)
11279 ToggleSwitchgateSwitch(x, y);
11281 else if (element == EL_LIGHT_SWITCH ||
11282 element == EL_LIGHT_SWITCH_ACTIVE)
11284 ToggleLightSwitch(x, y);
11286 else if (element == EL_TIMEGATE_SWITCH)
11288 ActivateTimegateSwitch(x, y);
11290 else if (element == EL_BALLOON_SWITCH_LEFT ||
11291 element == EL_BALLOON_SWITCH_RIGHT ||
11292 element == EL_BALLOON_SWITCH_UP ||
11293 element == EL_BALLOON_SWITCH_DOWN ||
11294 element == EL_BALLOON_SWITCH_NONE ||
11295 element == EL_BALLOON_SWITCH_ANY)
11297 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
11298 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
11299 element == EL_BALLOON_SWITCH_UP ? MV_UP :
11300 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
11301 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
11304 else if (element == EL_LAMP)
11306 Feld[x][y] = EL_LAMP_ACTIVE;
11307 local_player->lights_still_needed--;
11309 ResetGfxAnimation(x, y);
11310 DrawLevelField(x, y);
11312 else if (element == EL_TIME_ORB_FULL)
11314 Feld[x][y] = EL_TIME_ORB_EMPTY;
11316 if (level.time > 0 || level.use_time_orb_bug)
11318 TimeLeft += level.time_orb_time;
11319 DrawGameValue_Time(TimeLeft);
11322 ResetGfxAnimation(x, y);
11323 DrawLevelField(x, y);
11325 else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
11326 element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
11330 game.ball_state = !game.ball_state;
11333 SCAN_PLAYFIELD(xx, yy)
11335 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
11338 int e = Feld[xx][yy];
11340 if (game.ball_state)
11342 if (e == EL_EMC_MAGIC_BALL)
11343 CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
11344 else if (e == EL_EMC_MAGIC_BALL_SWITCH)
11345 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
11349 if (e == EL_EMC_MAGIC_BALL_ACTIVE)
11350 CreateField(xx, yy, EL_EMC_MAGIC_BALL);
11351 else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
11352 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
11357 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
11358 player->index_bit, dig_side);
11360 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
11361 player->index_bit, dig_side);
11363 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
11364 player->index_bit, dig_side);
11370 if (!PLAYER_SWITCHING(player, x, y))
11372 player->is_switching = TRUE;
11373 player->switch_x = x;
11374 player->switch_y = y;
11376 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
11377 player->index_bit, dig_side);
11378 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
11379 player->index_bit, dig_side);
11381 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
11382 player->index_bit, dig_side);
11383 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
11384 player->index_bit, dig_side);
11387 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
11388 player->index_bit, dig_side);
11389 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
11390 player->index_bit, dig_side);
11392 return MP_NO_ACTION;
11395 player->push_delay = -1;
11397 if (is_player) /* function can also be called by EL_PENGUIN */
11399 if (Feld[x][y] != element) /* really digged/collected something */
11400 player->is_collecting = !player->is_digging;
11406 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
11408 int jx = player->jx, jy = player->jy;
11409 int x = jx + dx, y = jy + dy;
11410 int snap_direction = (dx == -1 ? MV_LEFT :
11411 dx == +1 ? MV_RIGHT :
11413 dy == +1 ? MV_DOWN : MV_NONE);
11415 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
11418 if (!player->active || !IN_LEV_FIELD(x, y))
11426 if (player->MovPos == 0)
11427 player->is_pushing = FALSE;
11429 player->is_snapping = FALSE;
11431 if (player->MovPos == 0)
11433 player->is_moving = FALSE;
11434 player->is_digging = FALSE;
11435 player->is_collecting = FALSE;
11441 if (player->is_snapping)
11444 player->MovDir = snap_direction;
11446 if (player->MovPos == 0)
11448 player->is_moving = FALSE;
11449 player->is_digging = FALSE;
11450 player->is_collecting = FALSE;
11453 player->is_dropping = FALSE;
11455 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
11458 player->is_snapping = TRUE;
11460 if (player->MovPos == 0)
11462 player->is_moving = FALSE;
11463 player->is_digging = FALSE;
11464 player->is_collecting = FALSE;
11467 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
11468 DrawLevelField(player->last_jx, player->last_jy);
11470 DrawLevelField(x, y);
11475 boolean DropElement(struct PlayerInfo *player)
11477 int old_element, new_element;
11478 int dropx = player->jx, dropy = player->jy;
11479 int drop_direction = player->MovDir;
11480 int drop_side = drop_direction;
11481 int drop_element = (player->inventory_size > 0 ?
11482 player->inventory_element[player->inventory_size - 1] :
11483 player->inventory_infinite_element != EL_UNDEFINED ?
11484 player->inventory_infinite_element :
11485 player->dynabombs_left > 0 ?
11486 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
11489 /* do not drop an element on top of another element; when holding drop key
11490 pressed without moving, dropped element must move away before the next
11491 element can be dropped (this is especially important if the next element
11492 is dynamite, which can be placed on background for historical reasons) */
11493 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
11496 if (IS_THROWABLE(drop_element))
11498 dropx += GET_DX_FROM_DIR(drop_direction);
11499 dropy += GET_DY_FROM_DIR(drop_direction);
11501 if (!IN_LEV_FIELD(dropx, dropy))
11505 old_element = Feld[dropx][dropy]; /* old element at dropping position */
11506 new_element = drop_element; /* default: no change when dropping */
11508 /* check if player is active, not moving and ready to drop */
11509 if (!player->active || player->MovPos || player->drop_delay > 0)
11512 /* check if player has anything that can be dropped */
11513 if (new_element == EL_UNDEFINED)
11516 /* check if anything can be dropped at the current position */
11517 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
11520 /* collected custom elements can only be dropped on empty fields */
11521 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
11524 if (old_element != EL_EMPTY)
11525 Back[dropx][dropy] = old_element; /* store old element on this field */
11527 ResetGfxAnimation(dropx, dropy);
11528 ResetRandomAnimationValue(dropx, dropy);
11530 if (player->inventory_size > 0 ||
11531 player->inventory_infinite_element != EL_UNDEFINED)
11533 if (player->inventory_size > 0)
11535 player->inventory_size--;
11537 DrawGameValue_Dynamite(local_player->inventory_size);
11539 if (new_element == EL_DYNAMITE)
11540 new_element = EL_DYNAMITE_ACTIVE;
11541 else if (new_element == EL_SP_DISK_RED)
11542 new_element = EL_SP_DISK_RED_ACTIVE;
11545 Feld[dropx][dropy] = new_element;
11547 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
11548 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
11549 el2img(Feld[dropx][dropy]), 0);
11551 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
11553 /* needed if previous element just changed to "empty" in the last frame */
11554 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
11556 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
11557 player->index_bit, drop_side);
11558 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
11560 player->index_bit, drop_side);
11562 TestIfElementTouchesCustomElement(dropx, dropy);
11564 else /* player is dropping a dyna bomb */
11566 player->dynabombs_left--;
11568 Feld[dropx][dropy] = new_element;
11570 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
11571 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
11572 el2img(Feld[dropx][dropy]), 0);
11574 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
11577 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
11578 InitField_WithBug1(dropx, dropy, FALSE);
11580 new_element = Feld[dropx][dropy]; /* element might have changed */
11582 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
11583 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
11585 int move_direction, nextx, nexty;
11587 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
11588 MovDir[dropx][dropy] = drop_direction;
11590 move_direction = MovDir[dropx][dropy];
11591 nextx = dropx + GET_DX_FROM_DIR(move_direction);
11592 nexty = dropy + GET_DY_FROM_DIR(move_direction);
11594 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
11595 CheckCollision[dropx][dropy] = 2;
11598 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
11599 player->is_dropping = TRUE;
11601 player->drop_x = dropx;
11602 player->drop_y = dropy;
11607 /* ------------------------------------------------------------------------- */
11608 /* game sound playing functions */
11609 /* ------------------------------------------------------------------------- */
11611 static int *loop_sound_frame = NULL;
11612 static int *loop_sound_volume = NULL;
11614 void InitPlayLevelSound()
11616 int num_sounds = getSoundListSize();
11618 checked_free(loop_sound_frame);
11619 checked_free(loop_sound_volume);
11621 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
11622 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
11625 static void PlayLevelSound(int x, int y, int nr)
11627 int sx = SCREENX(x), sy = SCREENY(y);
11628 int volume, stereo_position;
11629 int max_distance = 8;
11630 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
11632 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
11633 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
11636 if (!IN_LEV_FIELD(x, y) ||
11637 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
11638 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
11641 volume = SOUND_MAX_VOLUME;
11643 if (!IN_SCR_FIELD(sx, sy))
11645 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
11646 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
11648 volume -= volume * (dx > dy ? dx : dy) / max_distance;
11651 stereo_position = (SOUND_MAX_LEFT +
11652 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
11653 (SCR_FIELDX + 2 * max_distance));
11655 if (IS_LOOP_SOUND(nr))
11657 /* This assures that quieter loop sounds do not overwrite louder ones,
11658 while restarting sound volume comparison with each new game frame. */
11660 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
11663 loop_sound_volume[nr] = volume;
11664 loop_sound_frame[nr] = FrameCounter;
11667 PlaySoundExt(nr, volume, stereo_position, type);
11670 static void PlayLevelSoundNearest(int x, int y, int sound_action)
11672 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
11673 x > LEVELX(BX2) ? LEVELX(BX2) : x,
11674 y < LEVELY(BY1) ? LEVELY(BY1) :
11675 y > LEVELY(BY2) ? LEVELY(BY2) : y,
11679 static void PlayLevelSoundAction(int x, int y, int action)
11681 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
11684 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
11686 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
11688 if (sound_effect != SND_UNDEFINED)
11689 PlayLevelSound(x, y, sound_effect);
11692 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
11695 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
11697 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
11698 PlayLevelSound(x, y, sound_effect);
11701 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
11703 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
11705 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
11706 PlayLevelSound(x, y, sound_effect);
11709 static void StopLevelSoundActionIfLoop(int x, int y, int action)
11711 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
11713 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
11714 StopSound(sound_effect);
11717 static void PlayLevelMusic()
11719 if (levelset.music[level_nr] != MUS_UNDEFINED)
11720 PlayMusic(levelset.music[level_nr]); /* from config file */
11722 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
11725 void PlayLevelSound_EM(int x, int y, int element_em, int sample)
11727 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
11732 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
11736 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
11740 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
11744 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
11748 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
11752 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
11756 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
11759 case SAMPLE_android_clone:
11760 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
11763 case SAMPLE_android_move:
11764 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
11767 case SAMPLE_spring:
11768 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
11772 PlayLevelSoundElementAction(x, y, element, ACTION_SLURPING);
11776 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
11779 case SAMPLE_eater_eat:
11780 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
11784 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
11787 case SAMPLE_collect:
11788 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
11791 case SAMPLE_diamond:
11792 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
11795 case SAMPLE_squash:
11796 /* !!! CHECK THIS !!! */
11798 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
11800 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
11804 case SAMPLE_wonderfall:
11805 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
11809 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
11813 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
11817 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
11821 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
11825 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
11829 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
11832 case SAMPLE_wonder:
11833 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
11837 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
11840 case SAMPLE_exit_open:
11841 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
11844 case SAMPLE_exit_leave:
11845 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
11848 case SAMPLE_dynamite:
11849 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
11853 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
11857 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
11861 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
11865 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
11869 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
11873 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
11877 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
11882 void RaiseScore(int value)
11884 local_player->score += value;
11886 DrawGameValue_Score(local_player->score);
11889 void RaiseScoreElement(int element)
11894 case EL_BD_DIAMOND:
11895 case EL_EMERALD_YELLOW:
11896 case EL_EMERALD_RED:
11897 case EL_EMERALD_PURPLE:
11898 case EL_SP_INFOTRON:
11899 RaiseScore(level.score[SC_EMERALD]);
11902 RaiseScore(level.score[SC_DIAMOND]);
11905 RaiseScore(level.score[SC_CRYSTAL]);
11908 RaiseScore(level.score[SC_PEARL]);
11911 case EL_BD_BUTTERFLY:
11912 case EL_SP_ELECTRON:
11913 RaiseScore(level.score[SC_BUG]);
11916 case EL_BD_FIREFLY:
11917 case EL_SP_SNIKSNAK:
11918 RaiseScore(level.score[SC_SPACESHIP]);
11921 case EL_DARK_YAMYAM:
11922 RaiseScore(level.score[SC_YAMYAM]);
11925 RaiseScore(level.score[SC_ROBOT]);
11928 RaiseScore(level.score[SC_PACMAN]);
11931 RaiseScore(level.score[SC_NUT]);
11934 case EL_SP_DISK_RED:
11935 case EL_DYNABOMB_INCREASE_NUMBER:
11936 case EL_DYNABOMB_INCREASE_SIZE:
11937 case EL_DYNABOMB_INCREASE_POWER:
11938 RaiseScore(level.score[SC_DYNAMITE]);
11940 case EL_SHIELD_NORMAL:
11941 case EL_SHIELD_DEADLY:
11942 RaiseScore(level.score[SC_SHIELD]);
11944 case EL_EXTRA_TIME:
11945 RaiseScore(level.extra_time_score);
11959 RaiseScore(level.score[SC_KEY]);
11962 RaiseScore(element_info[element].collect_score);
11967 void RequestQuitGame(boolean ask_if_really_quit)
11969 if (AllPlayersGone ||
11970 !ask_if_really_quit ||
11971 level_editor_test_game ||
11972 Request("Do you really want to quit the game ?",
11973 REQ_ASK | REQ_STAY_CLOSED))
11975 #if defined(NETWORK_AVALIABLE)
11976 if (options.network)
11977 SendToServer_StopPlaying();
11981 game_status = GAME_MODE_MAIN;
11987 if (tape.playing && tape.deactivate_display)
11988 TapeDeactivateDisplayOff(TRUE);
11990 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
11992 if (tape.playing && tape.deactivate_display)
11993 TapeDeactivateDisplayOn();
11998 /* ---------- new game button stuff ---------------------------------------- */
12000 /* graphic position values for game buttons */
12001 #define GAME_BUTTON_XSIZE 30
12002 #define GAME_BUTTON_YSIZE 30
12003 #define GAME_BUTTON_XPOS 5
12004 #define GAME_BUTTON_YPOS 215
12005 #define SOUND_BUTTON_XPOS 5
12006 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
12008 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
12009 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
12010 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
12011 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
12012 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
12013 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
12020 } gamebutton_info[NUM_GAME_BUTTONS] =
12023 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
12028 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
12029 GAME_CTRL_ID_PAUSE,
12033 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
12038 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
12039 SOUND_CTRL_ID_MUSIC,
12040 "background music on/off"
12043 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
12044 SOUND_CTRL_ID_LOOPS,
12045 "sound loops on/off"
12048 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
12049 SOUND_CTRL_ID_SIMPLE,
12050 "normal sounds on/off"
12054 void CreateGameButtons()
12058 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12060 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
12061 struct GadgetInfo *gi;
12064 unsigned long event_mask;
12065 int gd_xoffset, gd_yoffset;
12066 int gd_x1, gd_x2, gd_y1, gd_y2;
12069 gd_xoffset = gamebutton_info[i].x;
12070 gd_yoffset = gamebutton_info[i].y;
12071 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
12072 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
12074 if (id == GAME_CTRL_ID_STOP ||
12075 id == GAME_CTRL_ID_PAUSE ||
12076 id == GAME_CTRL_ID_PLAY)
12078 button_type = GD_TYPE_NORMAL_BUTTON;
12080 event_mask = GD_EVENT_RELEASED;
12081 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12082 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12086 button_type = GD_TYPE_CHECK_BUTTON;
12088 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
12089 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
12090 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
12091 event_mask = GD_EVENT_PRESSED;
12092 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
12093 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12096 gi = CreateGadget(GDI_CUSTOM_ID, id,
12097 GDI_INFO_TEXT, gamebutton_info[i].infotext,
12098 GDI_X, DX + gd_xoffset,
12099 GDI_Y, DY + gd_yoffset,
12100 GDI_WIDTH, GAME_BUTTON_XSIZE,
12101 GDI_HEIGHT, GAME_BUTTON_YSIZE,
12102 GDI_TYPE, button_type,
12103 GDI_STATE, GD_BUTTON_UNPRESSED,
12104 GDI_CHECKED, checked,
12105 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
12106 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
12107 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
12108 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
12109 GDI_EVENT_MASK, event_mask,
12110 GDI_CALLBACK_ACTION, HandleGameButtons,
12114 Error(ERR_EXIT, "cannot create gadget");
12116 game_gadget[id] = gi;
12120 void FreeGameButtons()
12124 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12125 FreeGadget(game_gadget[i]);
12128 static void MapGameButtons()
12132 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12133 MapGadget(game_gadget[i]);
12136 void UnmapGameButtons()
12140 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12141 UnmapGadget(game_gadget[i]);
12144 static void HandleGameButtons(struct GadgetInfo *gi)
12146 int id = gi->custom_id;
12148 if (game_status != GAME_MODE_PLAYING)
12153 case GAME_CTRL_ID_STOP:
12154 RequestQuitGame(TRUE);
12157 case GAME_CTRL_ID_PAUSE:
12158 if (options.network)
12160 #if defined(NETWORK_AVALIABLE)
12162 SendToServer_ContinuePlaying();
12164 SendToServer_PausePlaying();
12168 TapeTogglePause(TAPE_TOGGLE_MANUAL);
12171 case GAME_CTRL_ID_PLAY:
12174 #if defined(NETWORK_AVALIABLE)
12175 if (options.network)
12176 SendToServer_ContinuePlaying();
12180 tape.pausing = FALSE;
12181 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
12186 case SOUND_CTRL_ID_MUSIC:
12187 if (setup.sound_music)
12189 setup.sound_music = FALSE;
12192 else if (audio.music_available)
12194 setup.sound = setup.sound_music = TRUE;
12196 SetAudioMode(setup.sound);
12202 case SOUND_CTRL_ID_LOOPS:
12203 if (setup.sound_loops)
12204 setup.sound_loops = FALSE;
12205 else if (audio.loops_available)
12207 setup.sound = setup.sound_loops = TRUE;
12208 SetAudioMode(setup.sound);
12212 case SOUND_CTRL_ID_SIMPLE:
12213 if (setup.sound_simple)
12214 setup.sound_simple = FALSE;
12215 else if (audio.sound_available)
12217 setup.sound = setup.sound_simple = TRUE;
12218 SetAudioMode(setup.sound);