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);
10181 void TestIfElementTouchesCustomElement(int x, int y)
10183 static int xy[4][2] =
10190 static int trigger_sides[4][2] =
10192 /* center side border side */
10193 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
10194 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
10195 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
10196 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
10198 static int touch_dir[4] =
10200 MV_LEFT | MV_RIGHT,
10205 boolean change_center_element = FALSE;
10206 int center_element = Feld[x][y]; /* should always be non-moving! */
10207 int border_element_old[NUM_DIRECTIONS];
10210 for (i = 0; i < NUM_DIRECTIONS; i++)
10212 int xx = x + xy[i][0];
10213 int yy = y + xy[i][1];
10214 int border_element;
10216 border_element_old[i] = -1;
10218 if (!IN_LEV_FIELD(xx, yy))
10221 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10222 border_element = Feld[xx][yy]; /* may be moving! */
10223 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
10224 border_element = Feld[xx][yy];
10225 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
10226 border_element = MovingOrBlocked2Element(xx, yy);
10228 continue; /* center and border element do not touch */
10230 border_element_old[i] = border_element;
10233 for (i = 0; i < NUM_DIRECTIONS; i++)
10235 int xx = x + xy[i][0];
10236 int yy = y + xy[i][1];
10237 int center_side = trigger_sides[i][0];
10238 int border_element = border_element_old[i];
10240 if (border_element == -1)
10243 /* check for change of border element */
10244 CheckElementChangeBySide(xx, yy, border_element, center_element,
10245 CE_TOUCHING_X, center_side);
10248 for (i = 0; i < NUM_DIRECTIONS; i++)
10250 int border_side = trigger_sides[i][1];
10251 int border_element = border_element_old[i];
10253 if (border_element == -1)
10256 /* check for change of center element (but change it only once) */
10257 if (!change_center_element)
10258 change_center_element =
10259 CheckElementChangeBySide(x, y, center_element, border_element,
10260 CE_TOUCHING_X, border_side);
10266 void TestIfElementTouchesCustomElement_OLD(int x, int y)
10268 static int xy[4][2] =
10275 static int trigger_sides[4][2] =
10277 /* center side border side */
10278 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
10279 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
10280 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
10281 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
10283 static int touch_dir[4] =
10285 MV_LEFT | MV_RIGHT,
10290 boolean change_center_element = FALSE;
10291 int center_element = Feld[x][y]; /* should always be non-moving! */
10294 for (i = 0; i < NUM_DIRECTIONS; i++)
10296 int xx = x + xy[i][0];
10297 int yy = y + xy[i][1];
10298 int center_side = trigger_sides[i][0];
10299 int border_side = trigger_sides[i][1];
10300 int border_element;
10302 if (!IN_LEV_FIELD(xx, yy))
10305 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10306 border_element = Feld[xx][yy]; /* may be moving! */
10307 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
10308 border_element = Feld[xx][yy];
10309 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
10310 border_element = MovingOrBlocked2Element(xx, yy);
10312 continue; /* center and border element do not touch */
10314 /* check for change of center element (but change it only once) */
10315 if (!change_center_element)
10316 change_center_element =
10317 CheckElementChangeBySide(x, y, center_element, border_element,
10318 CE_TOUCHING_X, border_side);
10320 /* check for change of border element */
10321 CheckElementChangeBySide(xx, yy, border_element, center_element,
10322 CE_TOUCHING_X, center_side);
10328 void TestIfElementHitsCustomElement(int x, int y, int direction)
10330 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
10331 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
10332 int hitx = x + dx, hity = y + dy;
10333 int hitting_element = Feld[x][y];
10334 int touched_element;
10336 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
10339 touched_element = (IN_LEV_FIELD(hitx, hity) ?
10340 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
10342 if (IN_LEV_FIELD(hitx, hity))
10344 int opposite_direction = MV_DIR_OPPOSITE(direction);
10345 int hitting_side = direction;
10346 int touched_side = opposite_direction;
10347 boolean object_hit = (!IS_MOVING(hitx, hity) ||
10348 MovDir[hitx][hity] != direction ||
10349 ABS(MovPos[hitx][hity]) <= TILEY / 2);
10355 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10356 CE_HITTING_X, touched_side);
10358 CheckElementChangeBySide(hitx, hity, touched_element,
10359 hitting_element, CE_HIT_BY_X, hitting_side);
10361 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10362 CE_HIT_BY_SOMETHING, opposite_direction);
10366 /* "hitting something" is also true when hitting the playfield border */
10367 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10368 CE_HITTING_SOMETHING, direction);
10372 void TestIfElementSmashesCustomElement(int x, int y, int direction)
10374 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
10375 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
10376 int hitx = x + dx, hity = y + dy;
10377 int hitting_element = Feld[x][y];
10378 int touched_element;
10380 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
10381 !IS_FREE(hitx, hity) &&
10382 (!IS_MOVING(hitx, hity) ||
10383 MovDir[hitx][hity] != direction ||
10384 ABS(MovPos[hitx][hity]) <= TILEY / 2));
10387 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
10391 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
10395 touched_element = (IN_LEV_FIELD(hitx, hity) ?
10396 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
10398 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10399 EP_CAN_SMASH_EVERYTHING, direction);
10401 if (IN_LEV_FIELD(hitx, hity))
10403 int opposite_direction = MV_DIR_OPPOSITE(direction);
10404 int hitting_side = direction;
10405 int touched_side = opposite_direction;
10407 int touched_element = MovingOrBlocked2Element(hitx, hity);
10410 boolean object_hit = (!IS_MOVING(hitx, hity) ||
10411 MovDir[hitx][hity] != direction ||
10412 ABS(MovPos[hitx][hity]) <= TILEY / 2);
10421 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10422 CE_SMASHED_BY_SOMETHING, opposite_direction);
10424 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10425 CE_OTHER_IS_SMASHING, touched_side);
10427 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10428 CE_OTHER_GETS_SMASHED, hitting_side);
10434 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
10436 int i, kill_x = -1, kill_y = -1;
10438 int bad_element = -1;
10439 static int test_xy[4][2] =
10446 static int test_dir[4] =
10454 for (i = 0; i < NUM_DIRECTIONS; i++)
10456 int test_x, test_y, test_move_dir, test_element;
10458 test_x = good_x + test_xy[i][0];
10459 test_y = good_y + test_xy[i][1];
10461 if (!IN_LEV_FIELD(test_x, test_y))
10465 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
10467 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
10469 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
10470 2nd case: DONT_TOUCH style bad thing does not move away from good thing
10472 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
10473 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
10477 bad_element = test_element;
10483 if (kill_x != -1 || kill_y != -1)
10485 if (IS_PLAYER(good_x, good_y))
10487 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
10489 if (player->shield_deadly_time_left > 0 &&
10490 !IS_INDESTRUCTIBLE(bad_element))
10491 Bang(kill_x, kill_y);
10492 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
10493 KillPlayer(player);
10496 Bang(good_x, good_y);
10500 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
10502 int i, kill_x = -1, kill_y = -1;
10503 int bad_element = Feld[bad_x][bad_y];
10504 static int test_xy[4][2] =
10511 static int touch_dir[4] =
10513 MV_LEFT | MV_RIGHT,
10518 static int test_dir[4] =
10526 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
10529 for (i = 0; i < NUM_DIRECTIONS; i++)
10531 int test_x, test_y, test_move_dir, test_element;
10533 test_x = bad_x + test_xy[i][0];
10534 test_y = bad_y + test_xy[i][1];
10535 if (!IN_LEV_FIELD(test_x, test_y))
10539 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
10541 test_element = Feld[test_x][test_y];
10543 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
10544 2nd case: DONT_TOUCH style bad thing does not move away from good thing
10546 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
10547 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
10549 /* good thing is player or penguin that does not move away */
10550 if (IS_PLAYER(test_x, test_y))
10552 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
10554 if (bad_element == EL_ROBOT && player->is_moving)
10555 continue; /* robot does not kill player if he is moving */
10557 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10559 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
10560 continue; /* center and border element do not touch */
10567 else if (test_element == EL_PENGUIN)
10576 if (kill_x != -1 || kill_y != -1)
10578 if (IS_PLAYER(kill_x, kill_y))
10580 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
10582 if (player->shield_deadly_time_left > 0 &&
10583 !IS_INDESTRUCTIBLE(bad_element))
10584 Bang(bad_x, bad_y);
10585 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
10586 KillPlayer(player);
10589 Bang(kill_x, kill_y);
10593 void TestIfPlayerTouchesBadThing(int x, int y)
10595 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
10598 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
10600 TestIfGoodThingHitsBadThing(x, y, move_dir);
10603 void TestIfBadThingTouchesPlayer(int x, int y)
10605 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
10608 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
10610 TestIfBadThingHitsGoodThing(x, y, move_dir);
10613 void TestIfFriendTouchesBadThing(int x, int y)
10615 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
10618 void TestIfBadThingTouchesFriend(int x, int y)
10620 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
10623 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
10625 int i, kill_x = bad_x, kill_y = bad_y;
10626 static int xy[4][2] =
10634 for (i = 0; i < NUM_DIRECTIONS; i++)
10638 x = bad_x + xy[i][0];
10639 y = bad_y + xy[i][1];
10640 if (!IN_LEV_FIELD(x, y))
10643 element = Feld[x][y];
10644 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
10645 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
10653 if (kill_x != bad_x || kill_y != bad_y)
10654 Bang(bad_x, bad_y);
10657 void KillPlayer(struct PlayerInfo *player)
10659 int jx = player->jx, jy = player->jy;
10661 if (!player->active)
10664 /* remove accessible field at the player's position */
10665 Feld[jx][jy] = EL_EMPTY;
10667 /* deactivate shield (else Bang()/Explode() would not work right) */
10668 player->shield_normal_time_left = 0;
10669 player->shield_deadly_time_left = 0;
10672 BuryPlayer(player);
10675 static void KillPlayerUnlessEnemyProtected(int x, int y)
10677 if (!PLAYER_ENEMY_PROTECTED(x, y))
10678 KillPlayer(PLAYERINFO(x, y));
10681 static void KillPlayerUnlessExplosionProtected(int x, int y)
10683 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
10684 KillPlayer(PLAYERINFO(x, y));
10687 void BuryPlayer(struct PlayerInfo *player)
10689 int jx = player->jx, jy = player->jy;
10691 if (!player->active)
10694 PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
10695 PlayLevelSound(jx, jy, SND_GAME_LOSING);
10697 player->GameOver = TRUE;
10698 RemovePlayer(player);
10701 void RemovePlayer(struct PlayerInfo *player)
10703 int jx = player->jx, jy = player->jy;
10704 int i, found = FALSE;
10706 player->present = FALSE;
10707 player->active = FALSE;
10709 if (!ExplodeField[jx][jy])
10710 StorePlayer[jx][jy] = 0;
10712 if (player->is_moving)
10713 DrawLevelField(player->last_jx, player->last_jy);
10715 for (i = 0; i < MAX_PLAYERS; i++)
10716 if (stored_player[i].active)
10720 AllPlayersGone = TRUE;
10726 #if USE_NEW_SNAP_DELAY
10727 static void setFieldForSnapping(int x, int y, int element, int direction)
10729 struct ElementInfo *ei = &element_info[element];
10730 int direction_bit = MV_DIR_TO_BIT(direction);
10731 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
10732 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
10733 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
10735 Feld[x][y] = EL_ELEMENT_SNAPPING;
10736 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
10738 ResetGfxAnimation(x, y);
10740 GfxElement[x][y] = element;
10741 GfxAction[x][y] = action;
10742 GfxDir[x][y] = direction;
10743 GfxFrame[x][y] = -1;
10748 =============================================================================
10749 checkDiagonalPushing()
10750 -----------------------------------------------------------------------------
10751 check if diagonal input device direction results in pushing of object
10752 (by checking if the alternative direction is walkable, diggable, ...)
10753 =============================================================================
10756 static boolean checkDiagonalPushing(struct PlayerInfo *player,
10757 int x, int y, int real_dx, int real_dy)
10759 int jx, jy, dx, dy, xx, yy;
10761 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
10764 /* diagonal direction: check alternative direction */
10769 xx = jx + (dx == 0 ? real_dx : 0);
10770 yy = jy + (dy == 0 ? real_dy : 0);
10772 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
10776 =============================================================================
10778 -----------------------------------------------------------------------------
10779 x, y: field next to player (non-diagonal) to try to dig to
10780 real_dx, real_dy: direction as read from input device (can be diagonal)
10781 =============================================================================
10784 int DigField(struct PlayerInfo *player,
10785 int oldx, int oldy, int x, int y,
10786 int real_dx, int real_dy, int mode)
10788 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
10789 boolean player_was_pushing = player->is_pushing;
10790 boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
10791 boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
10792 int jx = oldx, jy = oldy;
10793 int dx = x - jx, dy = y - jy;
10794 int nextx = x + dx, nexty = y + dy;
10795 int move_direction = (dx == -1 ? MV_LEFT :
10796 dx == +1 ? MV_RIGHT :
10798 dy == +1 ? MV_DOWN : MV_NONE);
10799 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
10800 int dig_side = MV_DIR_OPPOSITE(move_direction);
10801 int old_element = Feld[jx][jy];
10802 #if USE_FIXED_DONT_RUN_INTO
10803 int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
10809 if (is_player) /* function can also be called by EL_PENGUIN */
10811 if (player->MovPos == 0)
10813 player->is_digging = FALSE;
10814 player->is_collecting = FALSE;
10817 if (player->MovPos == 0) /* last pushing move finished */
10818 player->is_pushing = FALSE;
10820 if (mode == DF_NO_PUSH) /* player just stopped pushing */
10822 player->is_switching = FALSE;
10823 player->push_delay = -1;
10825 return MP_NO_ACTION;
10829 #if !USE_FIXED_DONT_RUN_INTO
10830 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
10831 return MP_NO_ACTION;
10834 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
10835 old_element = Back[jx][jy];
10837 /* in case of element dropped at player position, check background */
10838 else if (Back[jx][jy] != EL_EMPTY &&
10839 game.engine_version >= VERSION_IDENT(2,2,0,0))
10840 old_element = Back[jx][jy];
10843 #if USE_FIXED_DONT_RUN_INTO
10844 if (player_can_move && DONT_RUN_INTO(element))
10846 if (element == EL_ACID && dx == 0 && dy == 1)
10849 Feld[jx][jy] = EL_PLAYER_1;
10850 InitMovingField(jx, jy, MV_DOWN);
10851 Store[jx][jy] = EL_ACID;
10852 ContinueMoving(jx, jy);
10853 BuryPlayer(player);
10856 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
10858 return MP_DONT_RUN_INTO;
10864 #if USE_FIXED_DONT_RUN_INTO
10865 if (player_can_move && DONT_RUN_INTO(element))
10867 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
10869 return MP_DONT_RUN_INTO;
10874 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
10875 return MP_NO_ACTION; /* field has no opening in this direction */
10877 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
10878 return MP_NO_ACTION; /* field has no opening in this direction */
10881 #if USE_FIXED_DONT_RUN_INTO
10882 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
10885 Feld[jx][jy] = EL_PLAYER_1;
10886 InitMovingField(jx, jy, MV_DOWN);
10887 Store[jx][jy] = EL_ACID;
10888 ContinueMoving(jx, jy);
10889 BuryPlayer(player);
10891 return MP_DONT_RUN_INTO;
10897 #if USE_FIXED_DONT_RUN_INTO
10898 if (player_can_move && DONT_RUN_INTO(element))
10900 if (element == EL_ACID && dx == 0 && dy == 1)
10903 Feld[jx][jy] = EL_PLAYER_1;
10904 InitMovingField(jx, jy, MV_DOWN);
10905 Store[jx][jy] = EL_ACID;
10906 ContinueMoving(jx, jy);
10907 BuryPlayer(player);
10910 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
10912 return MP_DONT_RUN_INTO;
10917 #if USE_FIXED_DONT_RUN_INTO
10918 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
10919 return MP_NO_ACTION;
10922 #if !USE_FIXED_DONT_RUN_INTO
10923 element = Feld[x][y];
10926 collect_count = element_info[element].collect_count_initial;
10928 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
10929 return MP_NO_ACTION;
10931 if (game.engine_version < VERSION_IDENT(2,2,0,0))
10932 player_can_move = player_can_move_or_snap;
10934 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
10935 game.engine_version >= VERSION_IDENT(2,2,0,0))
10937 CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
10938 player->index_bit, dig_side);
10939 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
10940 player->index_bit, dig_side);
10942 if (Feld[x][y] != element) /* field changed by snapping */
10945 return MP_NO_ACTION;
10948 if (game.gravity && is_player && !player->is_auto_moving &&
10949 canFallDown(player) && move_direction != MV_DOWN &&
10950 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
10951 return MP_NO_ACTION; /* player cannot walk here due to gravity */
10953 if (player_can_move &&
10954 IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
10956 int sound_element = SND_ELEMENT(element);
10957 int sound_action = ACTION_WALKING;
10959 if (IS_RND_GATE(element))
10961 if (!player->key[RND_GATE_NR(element)])
10962 return MP_NO_ACTION;
10964 else if (IS_RND_GATE_GRAY(element))
10966 if (!player->key[RND_GATE_GRAY_NR(element)])
10967 return MP_NO_ACTION;
10969 else if (IS_RND_GATE_GRAY_ACTIVE(element))
10971 if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
10972 return MP_NO_ACTION;
10974 else if (element == EL_EXIT_OPEN ||
10975 element == EL_SP_EXIT_OPEN ||
10976 element == EL_SP_EXIT_OPENING)
10978 sound_action = ACTION_PASSING; /* player is passing exit */
10980 else if (element == EL_EMPTY)
10982 sound_action = ACTION_MOVING; /* nothing to walk on */
10985 /* play sound from background or player, whatever is available */
10986 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
10987 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
10989 PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
10991 else if (player_can_move &&
10992 IS_PASSABLE(element) && canPassField(x, y, move_direction))
10994 if (!ACCESS_FROM(element, opposite_direction))
10995 return MP_NO_ACTION; /* field not accessible from this direction */
10997 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
10998 return MP_NO_ACTION;
11000 if (IS_EM_GATE(element))
11002 if (!player->key[EM_GATE_NR(element)])
11003 return MP_NO_ACTION;
11005 else if (IS_EM_GATE_GRAY(element))
11007 if (!player->key[EM_GATE_GRAY_NR(element)])
11008 return MP_NO_ACTION;
11010 else if (IS_EM_GATE_GRAY_ACTIVE(element))
11012 if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
11013 return MP_NO_ACTION;
11015 else if (IS_SP_PORT(element))
11017 if (element == EL_SP_GRAVITY_PORT_LEFT ||
11018 element == EL_SP_GRAVITY_PORT_RIGHT ||
11019 element == EL_SP_GRAVITY_PORT_UP ||
11020 element == EL_SP_GRAVITY_PORT_DOWN)
11021 game.gravity = !game.gravity;
11022 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
11023 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
11024 element == EL_SP_GRAVITY_ON_PORT_UP ||
11025 element == EL_SP_GRAVITY_ON_PORT_DOWN)
11026 game.gravity = TRUE;
11027 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
11028 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
11029 element == EL_SP_GRAVITY_OFF_PORT_UP ||
11030 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
11031 game.gravity = FALSE;
11034 /* automatically move to the next field with double speed */
11035 player->programmed_action = move_direction;
11037 if (player->move_delay_reset_counter == 0)
11039 player->move_delay_reset_counter = 2; /* two double speed steps */
11041 DOUBLE_PLAYER_SPEED(player);
11044 PlayLevelSoundAction(x, y, ACTION_PASSING);
11046 else if (player_can_move_or_snap && IS_DIGGABLE(element))
11050 if (mode != DF_SNAP)
11052 GfxElement[x][y] = GFX_ELEMENT(element);
11053 player->is_digging = TRUE;
11056 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
11058 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
11059 player->index_bit, dig_side);
11061 if (mode == DF_SNAP)
11063 #if USE_NEW_SNAP_DELAY
11064 if (level.block_snap_field)
11065 setFieldForSnapping(x, y, element, move_direction);
11067 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11069 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11072 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
11073 player->index_bit, dig_side);
11076 else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
11080 if (is_player && mode != DF_SNAP)
11082 GfxElement[x][y] = element;
11083 player->is_collecting = TRUE;
11086 if (element == EL_SPEED_PILL)
11088 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
11090 else if (element == EL_EXTRA_TIME && level.time > 0)
11092 TimeLeft += level.extra_time;
11093 DrawGameValue_Time(TimeLeft);
11095 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
11097 player->shield_normal_time_left += level.shield_normal_time;
11098 if (element == EL_SHIELD_DEADLY)
11099 player->shield_deadly_time_left += level.shield_deadly_time;
11101 else if (element == EL_DYNAMITE || element == EL_SP_DISK_RED)
11103 if (player->inventory_size < MAX_INVENTORY_SIZE)
11104 player->inventory_element[player->inventory_size++] = element;
11106 DrawGameValue_Dynamite(local_player->inventory_size);
11108 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
11110 player->dynabomb_count++;
11111 player->dynabombs_left++;
11113 else if (element == EL_DYNABOMB_INCREASE_SIZE)
11115 player->dynabomb_size++;
11117 else if (element == EL_DYNABOMB_INCREASE_POWER)
11119 player->dynabomb_xl = TRUE;
11121 else if (IS_KEY(element))
11123 player->key[KEY_NR(element)] = TRUE;
11125 DrawGameValue_Keys(player->key);
11127 redraw_mask |= REDRAW_DOOR_1;
11129 else if (IS_ENVELOPE(element))
11131 player->show_envelope = element;
11133 else if (element == EL_EMC_LENSES)
11135 game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
11137 RedrawAllInvisibleElementsForLenses();
11139 else if (element == EL_EMC_MAGNIFIER)
11141 game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
11143 RedrawAllInvisibleElementsForMagnifier();
11145 else if (IS_DROPPABLE(element) ||
11146 IS_THROWABLE(element)) /* can be collected and dropped */
11150 if (collect_count == 0)
11151 player->inventory_infinite_element = element;
11153 for (i = 0; i < collect_count; i++)
11154 if (player->inventory_size < MAX_INVENTORY_SIZE)
11155 player->inventory_element[player->inventory_size++] = element;
11157 DrawGameValue_Dynamite(local_player->inventory_size);
11159 else if (collect_count > 0)
11161 local_player->gems_still_needed -= collect_count;
11162 if (local_player->gems_still_needed < 0)
11163 local_player->gems_still_needed = 0;
11165 DrawGameValue_Emeralds(local_player->gems_still_needed);
11168 RaiseScoreElement(element);
11169 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
11172 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
11173 player->index_bit, dig_side);
11175 if (mode == DF_SNAP)
11177 #if USE_NEW_SNAP_DELAY
11178 if (level.block_snap_field)
11179 setFieldForSnapping(x, y, element, move_direction);
11181 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11183 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11186 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
11187 player->index_bit, dig_side);
11190 else if (player_can_move_or_snap && IS_PUSHABLE(element))
11192 if (mode == DF_SNAP && element != EL_BD_ROCK)
11193 return MP_NO_ACTION;
11195 if (CAN_FALL(element) && dy)
11196 return MP_NO_ACTION;
11198 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
11199 !(element == EL_SPRING && level.use_spring_bug))
11200 return MP_NO_ACTION;
11202 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
11203 ((move_direction & MV_VERTICAL &&
11204 ((element_info[element].move_pattern & MV_LEFT &&
11205 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
11206 (element_info[element].move_pattern & MV_RIGHT &&
11207 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
11208 (move_direction & MV_HORIZONTAL &&
11209 ((element_info[element].move_pattern & MV_UP &&
11210 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
11211 (element_info[element].move_pattern & MV_DOWN &&
11212 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
11213 return MP_NO_ACTION;
11215 /* do not push elements already moving away faster than player */
11216 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
11217 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
11218 return MP_NO_ACTION;
11220 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
11222 if (player->push_delay_value == -1 || !player_was_pushing)
11223 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11225 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11227 if (player->push_delay_value == -1)
11228 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11230 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
11232 if (!player->is_pushing)
11233 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11236 player->is_pushing = TRUE;
11238 if (!(IN_LEV_FIELD(nextx, nexty) &&
11239 (IS_FREE(nextx, nexty) ||
11240 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
11241 IS_SB_ELEMENT(element)))))
11242 return MP_NO_ACTION;
11244 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
11245 return MP_NO_ACTION;
11247 if (player->push_delay == -1) /* new pushing; restart delay */
11248 player->push_delay = 0;
11250 if (player->push_delay < player->push_delay_value &&
11251 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
11252 element != EL_SPRING && element != EL_BALLOON)
11254 /* make sure that there is no move delay before next try to push */
11255 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11256 player->move_delay = 0;
11258 return MP_NO_ACTION;
11261 if (IS_SB_ELEMENT(element))
11263 if (element == EL_SOKOBAN_FIELD_FULL)
11265 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
11266 local_player->sokobanfields_still_needed++;
11269 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
11271 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
11272 local_player->sokobanfields_still_needed--;
11275 Feld[x][y] = EL_SOKOBAN_OBJECT;
11277 if (Back[x][y] == Back[nextx][nexty])
11278 PlayLevelSoundAction(x, y, ACTION_PUSHING);
11279 else if (Back[x][y] != 0)
11280 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
11283 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
11286 if (local_player->sokobanfields_still_needed == 0 &&
11287 game.emulation == EMU_SOKOBAN)
11289 player->LevelSolved = player->GameOver = TRUE;
11290 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
11294 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
11296 InitMovingField(x, y, move_direction);
11297 GfxAction[x][y] = ACTION_PUSHING;
11299 if (mode == DF_SNAP)
11300 ContinueMoving(x, y);
11302 MovPos[x][y] = (dx != 0 ? dx : dy);
11304 Pushed[x][y] = TRUE;
11305 Pushed[nextx][nexty] = TRUE;
11307 if (game.engine_version < VERSION_IDENT(2,2,0,7))
11308 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11310 player->push_delay_value = -1; /* get new value later */
11312 /* check for element change _after_ element has been pushed */
11313 if (game.use_change_when_pushing_bug)
11315 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
11316 player->index_bit, dig_side);
11317 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
11318 player->index_bit, dig_side);
11321 else if (IS_SWITCHABLE(element))
11323 if (PLAYER_SWITCHING(player, x, y))
11325 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
11326 player->index_bit, dig_side);
11331 player->is_switching = TRUE;
11332 player->switch_x = x;
11333 player->switch_y = y;
11335 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
11337 if (element == EL_ROBOT_WHEEL)
11339 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
11343 DrawLevelField(x, y);
11345 else if (element == EL_SP_TERMINAL)
11350 SCAN_PLAYFIELD(xx, yy)
11352 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
11355 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
11357 else if (Feld[xx][yy] == EL_SP_TERMINAL)
11358 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
11361 else if (IS_BELT_SWITCH(element))
11363 ToggleBeltSwitch(x, y);
11365 else if (element == EL_SWITCHGATE_SWITCH_UP ||
11366 element == EL_SWITCHGATE_SWITCH_DOWN)
11368 ToggleSwitchgateSwitch(x, y);
11370 else if (element == EL_LIGHT_SWITCH ||
11371 element == EL_LIGHT_SWITCH_ACTIVE)
11373 ToggleLightSwitch(x, y);
11375 else if (element == EL_TIMEGATE_SWITCH)
11377 ActivateTimegateSwitch(x, y);
11379 else if (element == EL_BALLOON_SWITCH_LEFT ||
11380 element == EL_BALLOON_SWITCH_RIGHT ||
11381 element == EL_BALLOON_SWITCH_UP ||
11382 element == EL_BALLOON_SWITCH_DOWN ||
11383 element == EL_BALLOON_SWITCH_NONE ||
11384 element == EL_BALLOON_SWITCH_ANY)
11386 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
11387 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
11388 element == EL_BALLOON_SWITCH_UP ? MV_UP :
11389 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
11390 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
11393 else if (element == EL_LAMP)
11395 Feld[x][y] = EL_LAMP_ACTIVE;
11396 local_player->lights_still_needed--;
11398 ResetGfxAnimation(x, y);
11399 DrawLevelField(x, y);
11401 else if (element == EL_TIME_ORB_FULL)
11403 Feld[x][y] = EL_TIME_ORB_EMPTY;
11405 if (level.time > 0 || level.use_time_orb_bug)
11407 TimeLeft += level.time_orb_time;
11408 DrawGameValue_Time(TimeLeft);
11411 ResetGfxAnimation(x, y);
11412 DrawLevelField(x, y);
11414 else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
11415 element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
11419 game.ball_state = !game.ball_state;
11422 SCAN_PLAYFIELD(xx, yy)
11424 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
11427 int e = Feld[xx][yy];
11429 if (game.ball_state)
11431 if (e == EL_EMC_MAGIC_BALL)
11432 CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
11433 else if (e == EL_EMC_MAGIC_BALL_SWITCH)
11434 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
11438 if (e == EL_EMC_MAGIC_BALL_ACTIVE)
11439 CreateField(xx, yy, EL_EMC_MAGIC_BALL);
11440 else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
11441 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
11446 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
11447 player->index_bit, dig_side);
11449 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
11450 player->index_bit, dig_side);
11452 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
11453 player->index_bit, dig_side);
11459 if (!PLAYER_SWITCHING(player, x, y))
11461 player->is_switching = TRUE;
11462 player->switch_x = x;
11463 player->switch_y = y;
11465 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
11466 player->index_bit, dig_side);
11467 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
11468 player->index_bit, dig_side);
11470 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
11471 player->index_bit, dig_side);
11472 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
11473 player->index_bit, dig_side);
11476 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
11477 player->index_bit, dig_side);
11478 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
11479 player->index_bit, dig_side);
11481 return MP_NO_ACTION;
11484 player->push_delay = -1;
11486 if (is_player) /* function can also be called by EL_PENGUIN */
11488 if (Feld[x][y] != element) /* really digged/collected something */
11489 player->is_collecting = !player->is_digging;
11495 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
11497 int jx = player->jx, jy = player->jy;
11498 int x = jx + dx, y = jy + dy;
11499 int snap_direction = (dx == -1 ? MV_LEFT :
11500 dx == +1 ? MV_RIGHT :
11502 dy == +1 ? MV_DOWN : MV_NONE);
11504 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
11507 if (!player->active || !IN_LEV_FIELD(x, y))
11515 if (player->MovPos == 0)
11516 player->is_pushing = FALSE;
11518 player->is_snapping = FALSE;
11520 if (player->MovPos == 0)
11522 player->is_moving = FALSE;
11523 player->is_digging = FALSE;
11524 player->is_collecting = FALSE;
11530 if (player->is_snapping)
11533 player->MovDir = snap_direction;
11535 if (player->MovPos == 0)
11537 player->is_moving = FALSE;
11538 player->is_digging = FALSE;
11539 player->is_collecting = FALSE;
11542 player->is_dropping = FALSE;
11544 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
11547 player->is_snapping = TRUE;
11549 if (player->MovPos == 0)
11551 player->is_moving = FALSE;
11552 player->is_digging = FALSE;
11553 player->is_collecting = FALSE;
11556 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
11557 DrawLevelField(player->last_jx, player->last_jy);
11559 DrawLevelField(x, y);
11564 boolean DropElement(struct PlayerInfo *player)
11566 int old_element, new_element;
11567 int dropx = player->jx, dropy = player->jy;
11568 int drop_direction = player->MovDir;
11569 int drop_side = drop_direction;
11570 int drop_element = (player->inventory_size > 0 ?
11571 player->inventory_element[player->inventory_size - 1] :
11572 player->inventory_infinite_element != EL_UNDEFINED ?
11573 player->inventory_infinite_element :
11574 player->dynabombs_left > 0 ?
11575 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
11578 /* do not drop an element on top of another element; when holding drop key
11579 pressed without moving, dropped element must move away before the next
11580 element can be dropped (this is especially important if the next element
11581 is dynamite, which can be placed on background for historical reasons) */
11582 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
11585 if (IS_THROWABLE(drop_element))
11587 dropx += GET_DX_FROM_DIR(drop_direction);
11588 dropy += GET_DY_FROM_DIR(drop_direction);
11590 if (!IN_LEV_FIELD(dropx, dropy))
11594 old_element = Feld[dropx][dropy]; /* old element at dropping position */
11595 new_element = drop_element; /* default: no change when dropping */
11597 /* check if player is active, not moving and ready to drop */
11598 if (!player->active || player->MovPos || player->drop_delay > 0)
11601 /* check if player has anything that can be dropped */
11602 if (new_element == EL_UNDEFINED)
11605 /* check if anything can be dropped at the current position */
11606 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
11609 /* collected custom elements can only be dropped on empty fields */
11610 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
11613 if (old_element != EL_EMPTY)
11614 Back[dropx][dropy] = old_element; /* store old element on this field */
11616 ResetGfxAnimation(dropx, dropy);
11617 ResetRandomAnimationValue(dropx, dropy);
11619 if (player->inventory_size > 0 ||
11620 player->inventory_infinite_element != EL_UNDEFINED)
11622 if (player->inventory_size > 0)
11624 player->inventory_size--;
11626 DrawGameValue_Dynamite(local_player->inventory_size);
11628 if (new_element == EL_DYNAMITE)
11629 new_element = EL_DYNAMITE_ACTIVE;
11630 else if (new_element == EL_SP_DISK_RED)
11631 new_element = EL_SP_DISK_RED_ACTIVE;
11634 Feld[dropx][dropy] = new_element;
11636 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
11637 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
11638 el2img(Feld[dropx][dropy]), 0);
11640 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
11642 /* needed if previous element just changed to "empty" in the last frame */
11643 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
11645 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
11646 player->index_bit, drop_side);
11647 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
11649 player->index_bit, drop_side);
11651 TestIfElementTouchesCustomElement(dropx, dropy);
11653 else /* player is dropping a dyna bomb */
11655 player->dynabombs_left--;
11657 Feld[dropx][dropy] = new_element;
11659 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
11660 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
11661 el2img(Feld[dropx][dropy]), 0);
11663 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
11666 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
11667 InitField_WithBug1(dropx, dropy, FALSE);
11669 new_element = Feld[dropx][dropy]; /* element might have changed */
11671 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
11672 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
11674 int move_direction, nextx, nexty;
11676 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
11677 MovDir[dropx][dropy] = drop_direction;
11679 move_direction = MovDir[dropx][dropy];
11680 nextx = dropx + GET_DX_FROM_DIR(move_direction);
11681 nexty = dropy + GET_DY_FROM_DIR(move_direction);
11683 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
11684 CheckCollision[dropx][dropy] = 2;
11687 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
11688 player->is_dropping = TRUE;
11690 player->drop_x = dropx;
11691 player->drop_y = dropy;
11696 /* ------------------------------------------------------------------------- */
11697 /* game sound playing functions */
11698 /* ------------------------------------------------------------------------- */
11700 static int *loop_sound_frame = NULL;
11701 static int *loop_sound_volume = NULL;
11703 void InitPlayLevelSound()
11705 int num_sounds = getSoundListSize();
11707 checked_free(loop_sound_frame);
11708 checked_free(loop_sound_volume);
11710 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
11711 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
11714 static void PlayLevelSound(int x, int y, int nr)
11716 int sx = SCREENX(x), sy = SCREENY(y);
11717 int volume, stereo_position;
11718 int max_distance = 8;
11719 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
11721 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
11722 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
11725 if (!IN_LEV_FIELD(x, y) ||
11726 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
11727 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
11730 volume = SOUND_MAX_VOLUME;
11732 if (!IN_SCR_FIELD(sx, sy))
11734 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
11735 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
11737 volume -= volume * (dx > dy ? dx : dy) / max_distance;
11740 stereo_position = (SOUND_MAX_LEFT +
11741 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
11742 (SCR_FIELDX + 2 * max_distance));
11744 if (IS_LOOP_SOUND(nr))
11746 /* This assures that quieter loop sounds do not overwrite louder ones,
11747 while restarting sound volume comparison with each new game frame. */
11749 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
11752 loop_sound_volume[nr] = volume;
11753 loop_sound_frame[nr] = FrameCounter;
11756 PlaySoundExt(nr, volume, stereo_position, type);
11759 static void PlayLevelSoundNearest(int x, int y, int sound_action)
11761 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
11762 x > LEVELX(BX2) ? LEVELX(BX2) : x,
11763 y < LEVELY(BY1) ? LEVELY(BY1) :
11764 y > LEVELY(BY2) ? LEVELY(BY2) : y,
11768 static void PlayLevelSoundAction(int x, int y, int action)
11770 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
11773 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
11775 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
11777 if (sound_effect != SND_UNDEFINED)
11778 PlayLevelSound(x, y, sound_effect);
11781 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
11784 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
11786 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
11787 PlayLevelSound(x, y, sound_effect);
11790 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
11792 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
11794 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
11795 PlayLevelSound(x, y, sound_effect);
11798 static void StopLevelSoundActionIfLoop(int x, int y, int action)
11800 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
11802 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
11803 StopSound(sound_effect);
11806 static void PlayLevelMusic()
11808 if (levelset.music[level_nr] != MUS_UNDEFINED)
11809 PlayMusic(levelset.music[level_nr]); /* from config file */
11811 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
11814 void PlayLevelSound_EM(int x, int y, int element_em, int sample)
11816 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
11821 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
11825 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
11829 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
11833 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
11837 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
11841 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
11845 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
11848 case SAMPLE_android_clone:
11849 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
11852 case SAMPLE_android_move:
11853 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
11856 case SAMPLE_spring:
11857 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
11861 PlayLevelSoundElementAction(x, y, element, ACTION_SLURPING);
11865 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
11868 case SAMPLE_eater_eat:
11869 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
11873 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
11876 case SAMPLE_collect:
11877 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
11880 case SAMPLE_diamond:
11881 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
11884 case SAMPLE_squash:
11885 /* !!! CHECK THIS !!! */
11887 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
11889 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
11893 case SAMPLE_wonderfall:
11894 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
11898 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
11902 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
11906 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
11910 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
11914 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
11918 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
11921 case SAMPLE_wonder:
11922 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
11926 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
11929 case SAMPLE_exit_open:
11930 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
11933 case SAMPLE_exit_leave:
11934 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
11937 case SAMPLE_dynamite:
11938 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
11942 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
11946 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
11950 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
11954 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
11958 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
11962 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
11966 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
11971 void RaiseScore(int value)
11973 local_player->score += value;
11975 DrawGameValue_Score(local_player->score);
11978 void RaiseScoreElement(int element)
11983 case EL_BD_DIAMOND:
11984 case EL_EMERALD_YELLOW:
11985 case EL_EMERALD_RED:
11986 case EL_EMERALD_PURPLE:
11987 case EL_SP_INFOTRON:
11988 RaiseScore(level.score[SC_EMERALD]);
11991 RaiseScore(level.score[SC_DIAMOND]);
11994 RaiseScore(level.score[SC_CRYSTAL]);
11997 RaiseScore(level.score[SC_PEARL]);
12000 case EL_BD_BUTTERFLY:
12001 case EL_SP_ELECTRON:
12002 RaiseScore(level.score[SC_BUG]);
12005 case EL_BD_FIREFLY:
12006 case EL_SP_SNIKSNAK:
12007 RaiseScore(level.score[SC_SPACESHIP]);
12010 case EL_DARK_YAMYAM:
12011 RaiseScore(level.score[SC_YAMYAM]);
12014 RaiseScore(level.score[SC_ROBOT]);
12017 RaiseScore(level.score[SC_PACMAN]);
12020 RaiseScore(level.score[SC_NUT]);
12023 case EL_SP_DISK_RED:
12024 case EL_DYNABOMB_INCREASE_NUMBER:
12025 case EL_DYNABOMB_INCREASE_SIZE:
12026 case EL_DYNABOMB_INCREASE_POWER:
12027 RaiseScore(level.score[SC_DYNAMITE]);
12029 case EL_SHIELD_NORMAL:
12030 case EL_SHIELD_DEADLY:
12031 RaiseScore(level.score[SC_SHIELD]);
12033 case EL_EXTRA_TIME:
12034 RaiseScore(level.extra_time_score);
12048 RaiseScore(level.score[SC_KEY]);
12051 RaiseScore(element_info[element].collect_score);
12056 void RequestQuitGame(boolean ask_if_really_quit)
12058 if (AllPlayersGone ||
12059 !ask_if_really_quit ||
12060 level_editor_test_game ||
12061 Request("Do you really want to quit the game ?",
12062 REQ_ASK | REQ_STAY_CLOSED))
12064 #if defined(NETWORK_AVALIABLE)
12065 if (options.network)
12066 SendToServer_StopPlaying();
12070 game_status = GAME_MODE_MAIN;
12076 if (tape.playing && tape.deactivate_display)
12077 TapeDeactivateDisplayOff(TRUE);
12079 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
12081 if (tape.playing && tape.deactivate_display)
12082 TapeDeactivateDisplayOn();
12087 /* ---------- new game button stuff ---------------------------------------- */
12089 /* graphic position values for game buttons */
12090 #define GAME_BUTTON_XSIZE 30
12091 #define GAME_BUTTON_YSIZE 30
12092 #define GAME_BUTTON_XPOS 5
12093 #define GAME_BUTTON_YPOS 215
12094 #define SOUND_BUTTON_XPOS 5
12095 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
12097 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
12098 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
12099 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
12100 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
12101 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
12102 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
12109 } gamebutton_info[NUM_GAME_BUTTONS] =
12112 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
12117 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
12118 GAME_CTRL_ID_PAUSE,
12122 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
12127 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
12128 SOUND_CTRL_ID_MUSIC,
12129 "background music on/off"
12132 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
12133 SOUND_CTRL_ID_LOOPS,
12134 "sound loops on/off"
12137 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
12138 SOUND_CTRL_ID_SIMPLE,
12139 "normal sounds on/off"
12143 void CreateGameButtons()
12147 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12149 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
12150 struct GadgetInfo *gi;
12153 unsigned long event_mask;
12154 int gd_xoffset, gd_yoffset;
12155 int gd_x1, gd_x2, gd_y1, gd_y2;
12158 gd_xoffset = gamebutton_info[i].x;
12159 gd_yoffset = gamebutton_info[i].y;
12160 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
12161 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
12163 if (id == GAME_CTRL_ID_STOP ||
12164 id == GAME_CTRL_ID_PAUSE ||
12165 id == GAME_CTRL_ID_PLAY)
12167 button_type = GD_TYPE_NORMAL_BUTTON;
12169 event_mask = GD_EVENT_RELEASED;
12170 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12171 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12175 button_type = GD_TYPE_CHECK_BUTTON;
12177 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
12178 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
12179 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
12180 event_mask = GD_EVENT_PRESSED;
12181 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
12182 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12185 gi = CreateGadget(GDI_CUSTOM_ID, id,
12186 GDI_INFO_TEXT, gamebutton_info[i].infotext,
12187 GDI_X, DX + gd_xoffset,
12188 GDI_Y, DY + gd_yoffset,
12189 GDI_WIDTH, GAME_BUTTON_XSIZE,
12190 GDI_HEIGHT, GAME_BUTTON_YSIZE,
12191 GDI_TYPE, button_type,
12192 GDI_STATE, GD_BUTTON_UNPRESSED,
12193 GDI_CHECKED, checked,
12194 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
12195 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
12196 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
12197 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
12198 GDI_EVENT_MASK, event_mask,
12199 GDI_CALLBACK_ACTION, HandleGameButtons,
12203 Error(ERR_EXIT, "cannot create gadget");
12205 game_gadget[id] = gi;
12209 void FreeGameButtons()
12213 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12214 FreeGadget(game_gadget[i]);
12217 static void MapGameButtons()
12221 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12222 MapGadget(game_gadget[i]);
12225 void UnmapGameButtons()
12229 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12230 UnmapGadget(game_gadget[i]);
12233 static void HandleGameButtons(struct GadgetInfo *gi)
12235 int id = gi->custom_id;
12237 if (game_status != GAME_MODE_PLAYING)
12242 case GAME_CTRL_ID_STOP:
12243 RequestQuitGame(TRUE);
12246 case GAME_CTRL_ID_PAUSE:
12247 if (options.network)
12249 #if defined(NETWORK_AVALIABLE)
12251 SendToServer_ContinuePlaying();
12253 SendToServer_PausePlaying();
12257 TapeTogglePause(TAPE_TOGGLE_MANUAL);
12260 case GAME_CTRL_ID_PLAY:
12263 #if defined(NETWORK_AVALIABLE)
12264 if (options.network)
12265 SendToServer_ContinuePlaying();
12269 tape.pausing = FALSE;
12270 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
12275 case SOUND_CTRL_ID_MUSIC:
12276 if (setup.sound_music)
12278 setup.sound_music = FALSE;
12281 else if (audio.music_available)
12283 setup.sound = setup.sound_music = TRUE;
12285 SetAudioMode(setup.sound);
12291 case SOUND_CTRL_ID_LOOPS:
12292 if (setup.sound_loops)
12293 setup.sound_loops = FALSE;
12294 else if (audio.loops_available)
12296 setup.sound = setup.sound_loops = TRUE;
12297 SetAudioMode(setup.sound);
12301 case SOUND_CTRL_ID_SIMPLE:
12302 if (setup.sound_simple)
12303 setup.sound_simple = FALSE;
12304 else if (audio.sound_available)
12306 setup.sound = setup.sound_simple = TRUE;
12307 SetAudioMode(setup.sound);