1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back! *
3 *----------------------------------------------------------*
4 * (c) 1995-2002 Artsoft Entertainment *
6 * Detmolder Strasse 189 *
9 * e-mail: info@artsoft.org *
10 *----------------------------------------------------------*
12 ***********************************************************/
14 #include "libgame/libgame.h"
24 /* EXPERIMENTAL STUFF */
25 #define USE_NEW_AMOEBA_CODE FALSE
27 /* EXPERIMENTAL STUFF */
28 #define USE_NEW_STUFF ( 1)
30 #define USE_NEW_SP_SLIPPERY (USE_NEW_STUFF * 1)
31 #define USE_NEW_CUSTOM_VALUE (USE_NEW_STUFF * 1)
32 #define USE_NEW_PLAYER_ANIM (USE_NEW_STUFF * 1)
33 #define USE_NEW_ALL_SLIPPERY (USE_NEW_STUFF * 1)
34 #define USE_NEW_PLAYER_SPEED (USE_NEW_STUFF * 1)
35 #define USE_NEW_DELAYED_ACTION (USE_NEW_STUFF * 1)
36 #define USE_NEW_SNAP_DELAY (USE_NEW_STUFF * 1)
37 #define USE_ONLY_ONE_CHANGE_PER_FRAME (USE_NEW_STUFF * 1)
38 #define USE_ONE_MORE_CHANGE_PER_FRAME (USE_NEW_STUFF * 1)
39 #define USE_FIXED_DONT_RUN_INTO (USE_NEW_STUFF * 1)
40 #define USE_NEW_SPRING_BUMPER (USE_NEW_STUFF * 1)
41 #define USE_STOP_CHANGED_ELEMENTS (USE_NEW_STUFF * 1)
42 #define USE_ELEMENT_TOUCHING_BUGFIX (USE_NEW_STUFF * 1)
44 #define USE_QUICKSAND_IMPACT_BUGFIX (USE_NEW_STUFF * 0)
51 /* for MovePlayer() */
52 #define MP_NO_ACTION 0
55 #define MP_DONT_RUN_INTO (MP_MOVING | MP_ACTION)
57 /* for ScrollPlayer() */
59 #define SCROLL_GO_ON 1
61 /* for Bang()/Explode() */
62 #define EX_PHASE_START 0
63 #define EX_TYPE_NONE 0
64 #define EX_TYPE_NORMAL (1 << 0)
65 #define EX_TYPE_CENTER (1 << 1)
66 #define EX_TYPE_BORDER (1 << 2)
67 #define EX_TYPE_CROSS (1 << 3)
68 #define EX_TYPE_DYNA (1 << 4)
69 #define EX_TYPE_SINGLE_TILE (EX_TYPE_CENTER | EX_TYPE_BORDER)
71 /* special positions in the game control window (relative to control window) */
74 #define XX_EMERALDS 29
75 #define YY_EMERALDS 54
76 #define XX_DYNAMITE 29
77 #define YY_DYNAMITE 89
86 /* special positions in the game control window (relative to main window) */
87 #define DX_LEVEL (DX + XX_LEVEL)
88 #define DY_LEVEL (DY + YY_LEVEL)
89 #define DX_EMERALDS (DX + XX_EMERALDS)
90 #define DY_EMERALDS (DY + YY_EMERALDS)
91 #define DX_DYNAMITE (DX + XX_DYNAMITE)
92 #define DY_DYNAMITE (DY + YY_DYNAMITE)
93 #define DX_KEYS (DX + XX_KEYS)
94 #define DY_KEYS (DY + YY_KEYS)
95 #define DX_SCORE (DX + XX_SCORE)
96 #define DY_SCORE (DY + YY_SCORE)
97 #define DX_TIME1 (DX + XX_TIME1)
98 #define DX_TIME2 (DX + XX_TIME2)
99 #define DY_TIME (DY + YY_TIME)
101 /* values for initial player move delay (initial delay counter value) */
102 #define INITIAL_MOVE_DELAY_OFF -1
103 #define INITIAL_MOVE_DELAY_ON 0
105 /* values for player movement speed (which is in fact a delay value) */
106 #define MOVE_DELAY_MIN_SPEED 32
107 #define MOVE_DELAY_NORMAL_SPEED 8
108 #define MOVE_DELAY_HIGH_SPEED 4
109 #define MOVE_DELAY_MAX_SPEED 1
112 #define DOUBLE_MOVE_DELAY(x) (x = (x <= MOVE_DELAY_HIGH_SPEED ? x * 2 : x))
113 #define HALVE_MOVE_DELAY(x) (x = (x >= MOVE_DELAY_HIGH_SPEED ? x / 2 : x))
115 #define DOUBLE_MOVE_DELAY(x) (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
116 #define HALVE_MOVE_DELAY(x) (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
118 #define DOUBLE_PLAYER_SPEED(p) (HALVE_MOVE_DELAY((p)->move_delay_value))
119 #define HALVE_PLAYER_SPEED(p) (DOUBLE_MOVE_DELAY((p)->move_delay_value))
121 /* values for other actions */
122 #define MOVE_STEPSIZE_NORMAL (TILEX / MOVE_DELAY_NORMAL_SPEED)
123 #define MOVE_STEPSIZE_MIN (1)
124 #define MOVE_STEPSIZE_MAX (TILEX)
126 #define GET_DX_FROM_DIR(d) ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
127 #define GET_DY_FROM_DIR(d) ((d) == MV_UP ? -1 : (d) == MV_DOWN ? 1 : 0)
129 #define INIT_GFX_RANDOM() (SimpleRND(1000000))
131 #define GET_NEW_PUSH_DELAY(e) ( (element_info[e].push_delay_fixed) + \
132 RND(element_info[e].push_delay_random))
133 #define GET_NEW_DROP_DELAY(e) ( (element_info[e].drop_delay_fixed) + \
134 RND(element_info[e].drop_delay_random))
135 #define GET_NEW_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
136 RND(element_info[e].move_delay_random))
137 #define GET_MAX_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
138 (element_info[e].move_delay_random))
139 #define GET_NEW_CUSTOM_VALUE(e) ( (element_info[e].ce_value_fixed_initial) +\
140 RND(element_info[e].ce_value_random_initial))
141 #define GET_CHANGE_DELAY(c) ( ((c)->delay_fixed * (c)->delay_frames) + \
142 RND((c)->delay_random * (c)->delay_frames))
143 #define GET_CE_DELAY_VALUE(c) ( ((c)->delay_fixed) + \
144 RND((c)->delay_random))
147 #define GET_VALID_RUNTIME_ELEMENT(e) \
148 ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
150 #define GET_VALID_FILE_ELEMENT(e) \
151 ((e) >= NUM_FILE_ELEMENTS ? EL_UNKNOWN : (e))
154 #define GET_TARGET_ELEMENT(e, ch) \
155 ((e) == EL_TRIGGER_PLAYER ? (ch)->actual_trigger_player : \
156 (e) == EL_TRIGGER_ELEMENT ? (ch)->actual_trigger_element : \
157 (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value : (e))
159 #define CAN_GROW_INTO(e) \
160 ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
162 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition) \
163 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
166 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition) \
167 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
168 (CAN_MOVE_INTO_ACID(e) && \
169 Feld[x][y] == EL_ACID) || \
172 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition) \
173 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
174 (CAN_MOVE_INTO_ACID(e) && \
175 Feld[x][y] == EL_ACID) || \
178 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition) \
179 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
181 (CAN_MOVE_INTO_ACID(e) && \
182 Feld[x][y] == EL_ACID) || \
183 (DONT_COLLIDE_WITH(e) && \
185 !PLAYER_ENEMY_PROTECTED(x, y))))
187 #define ELEMENT_CAN_ENTER_FIELD(e, x, y) \
188 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
190 #define SATELLITE_CAN_ENTER_FIELD(x, y) \
191 ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
193 #define ANDROID_CAN_ENTER_FIELD(e, x, y) \
194 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
196 #define ANDROID_CAN_CLONE_FIELD(x, y) \
197 (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
198 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
200 #define ENEMY_CAN_ENTER_FIELD(e, x, y) \
201 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
203 #define YAMYAM_CAN_ENTER_FIELD(e, x, y) \
204 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
206 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y) \
207 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
209 #define PACMAN_CAN_ENTER_FIELD(e, x, y) \
210 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
212 #define PIG_CAN_ENTER_FIELD(e, x, y) \
213 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
215 #define PENGUIN_CAN_ENTER_FIELD(e, x, y) \
216 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN ||\
217 IS_FOOD_PENGUIN(Feld[x][y])))
218 #define DRAGON_CAN_ENTER_FIELD(e, x, y) \
219 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
221 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition) \
222 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
224 #define SPRING_CAN_ENTER_FIELD(e, x, y) \
225 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
227 #define SPRING_CAN_BUMP_FROM_FIELD(x, y) \
228 (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER || \
229 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
232 #define GROUP_NR(e) ((e) - EL_GROUP_START)
233 #define IS_IN_GROUP(e, nr) (element_info[e].in_group[nr] == TRUE)
234 #define IS_IN_GROUP_EL(e, ge) (IS_IN_GROUP(e, (ge) - EL_GROUP_START))
236 #define IS_EQUAL_OR_IN_GROUP(e, ge) \
237 (IS_GROUP_ELEMENT(ge) ? IS_IN_GROUP(e, GROUP_NR(ge)) : (e) == (ge))
240 #define MOVE_ENTER_EL(e) (element_info[e].move_enter_element)
242 #define CE_ENTER_FIELD_COND(e, x, y) \
243 (!IS_PLAYER(x, y) && \
244 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
246 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y) \
247 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
249 #define IN_LEV_FIELD_AND_IS_FREE(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
250 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
252 #define ACCESS_FROM(e, d) (element_info[e].access_direction &(d))
253 #define IS_WALKABLE_FROM(e, d) (IS_WALKABLE(e) && ACCESS_FROM(e, d))
254 #define IS_PASSABLE_FROM(e, d) (IS_PASSABLE(e) && ACCESS_FROM(e, d))
255 #define IS_ACCESSIBLE_FROM(e, d) (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
257 /* game button identifiers */
258 #define GAME_CTRL_ID_STOP 0
259 #define GAME_CTRL_ID_PAUSE 1
260 #define GAME_CTRL_ID_PLAY 2
261 #define SOUND_CTRL_ID_MUSIC 3
262 #define SOUND_CTRL_ID_LOOPS 4
263 #define SOUND_CTRL_ID_SIMPLE 5
265 #define NUM_GAME_BUTTONS 6
268 /* forward declaration for internal use */
270 static void CreateField(int, int, int);
272 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
273 static void AdvanceFrameAndPlayerCounters(int);
275 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
276 static boolean MovePlayer(struct PlayerInfo *, int, int);
277 static void ScrollPlayer(struct PlayerInfo *, int);
278 static void ScrollScreen(struct PlayerInfo *, int);
280 int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
282 static void InitBeltMovement(void);
283 static void CloseAllOpenTimegates(void);
284 static void CheckGravityMovement(struct PlayerInfo *);
285 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
286 static void KillPlayerUnlessEnemyProtected(int, int);
287 static void KillPlayerUnlessExplosionProtected(int, int);
289 static void TestIfPlayerTouchesCustomElement(int, int);
290 static void TestIfElementTouchesCustomElement(int, int);
291 static void TestIfElementHitsCustomElement(int, int, int);
293 static void TestIfElementSmashesCustomElement(int, int, int);
296 static void HandleElementChange(int, int, int);
297 static void ExecuteCustomElementAction(int, int, int, int);
298 static boolean ChangeElement(int, int, int, int);
300 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
301 #define CheckTriggeredElementChange(x, y, e, ev) \
302 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
303 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s) \
304 CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
305 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s) \
306 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
307 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p) \
308 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
310 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
311 #define CheckElementChange(x, y, e, te, ev) \
312 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
313 #define CheckElementChangeByPlayer(x, y, e, ev, p, s) \
314 CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
315 #define CheckElementChangeBySide(x, y, e, te, ev, s) \
316 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
318 static void PlayLevelSound(int, int, int);
319 static void PlayLevelSoundNearest(int, int, int);
320 static void PlayLevelSoundAction(int, int, int);
321 static void PlayLevelSoundElementAction(int, int, int, int);
322 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
323 static void PlayLevelSoundActionIfLoop(int, int, int);
324 static void StopLevelSoundActionIfLoop(int, int, int);
325 static void PlayLevelMusic();
327 static void MapGameButtons();
328 static void HandleGameButtons(struct GadgetInfo *);
330 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
333 /* ------------------------------------------------------------------------- */
334 /* definition of elements that automatically change to other elements after */
335 /* a specified time, eventually calling a function when changing */
336 /* ------------------------------------------------------------------------- */
338 /* forward declaration for changer functions */
339 static void InitBuggyBase(int, int);
340 static void WarnBuggyBase(int, int);
342 static void InitTrap(int, int);
343 static void ActivateTrap(int, int);
344 static void ChangeActiveTrap(int, int);
346 static void InitRobotWheel(int, int);
347 static void RunRobotWheel(int, int);
348 static void StopRobotWheel(int, int);
350 static void InitTimegateWheel(int, int);
351 static void RunTimegateWheel(int, int);
353 static void InitMagicBallDelay(int, int);
354 static void ActivateMagicBall(int, int);
356 static void InitDiagonalMovingElement(int, int);
358 struct ChangingElementInfo
363 void (*pre_change_function)(int x, int y);
364 void (*change_function)(int x, int y);
365 void (*post_change_function)(int x, int y);
368 static struct ChangingElementInfo change_delay_list[] =
419 EL_SWITCHGATE_OPENING,
427 EL_SWITCHGATE_CLOSING,
428 EL_SWITCHGATE_CLOSED,
460 EL_ACID_SPLASH_RIGHT,
469 EL_SP_BUGGY_BASE_ACTIVATING,
476 EL_SP_BUGGY_BASE_ACTIVATING,
477 EL_SP_BUGGY_BASE_ACTIVE,
484 EL_SP_BUGGY_BASE_ACTIVE,
508 EL_ROBOT_WHEEL_ACTIVE,
516 EL_TIMEGATE_SWITCH_ACTIVE,
524 EL_EMC_MAGIC_BALL_ACTIVE,
525 EL_EMC_MAGIC_BALL_ACTIVE,
532 EL_EMC_SPRING_BUMPER_ACTIVE,
533 EL_EMC_SPRING_BUMPER,
540 EL_DIAGONAL_SHRINKING,
553 InitDiagonalMovingElement
569 int push_delay_fixed, push_delay_random;
574 { EL_BALLOON, 0, 0 },
576 { EL_SOKOBAN_OBJECT, 2, 0 },
577 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
578 { EL_SATELLITE, 2, 0 },
579 { EL_SP_DISK_YELLOW, 2, 0 },
581 { EL_UNDEFINED, 0, 0 },
589 move_stepsize_list[] =
591 { EL_AMOEBA_DROP, 2 },
592 { EL_AMOEBA_DROPPING, 2 },
593 { EL_QUICKSAND_FILLING, 1 },
594 { EL_QUICKSAND_EMPTYING, 1 },
595 { EL_MAGIC_WALL_FILLING, 2 },
596 { EL_BD_MAGIC_WALL_FILLING, 2 },
597 { EL_MAGIC_WALL_EMPTYING, 2 },
598 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
608 collect_count_list[] =
611 { EL_BD_DIAMOND, 1 },
612 { EL_EMERALD_YELLOW, 1 },
613 { EL_EMERALD_RED, 1 },
614 { EL_EMERALD_PURPLE, 1 },
616 { EL_SP_INFOTRON, 1 },
628 access_direction_list[] =
630 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
631 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
632 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
633 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
634 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
635 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
636 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
637 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
638 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
639 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
640 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
642 { EL_SP_PORT_LEFT, MV_RIGHT },
643 { EL_SP_PORT_RIGHT, MV_LEFT },
644 { EL_SP_PORT_UP, MV_DOWN },
645 { EL_SP_PORT_DOWN, MV_UP },
646 { EL_SP_PORT_HORIZONTAL, MV_LEFT | MV_RIGHT },
647 { EL_SP_PORT_VERTICAL, MV_UP | MV_DOWN },
648 { EL_SP_PORT_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
649 { EL_SP_GRAVITY_PORT_LEFT, MV_RIGHT },
650 { EL_SP_GRAVITY_PORT_RIGHT, MV_LEFT },
651 { EL_SP_GRAVITY_PORT_UP, MV_DOWN },
652 { EL_SP_GRAVITY_PORT_DOWN, MV_UP },
653 { EL_SP_GRAVITY_ON_PORT_LEFT, MV_RIGHT },
654 { EL_SP_GRAVITY_ON_PORT_RIGHT, MV_LEFT },
655 { EL_SP_GRAVITY_ON_PORT_UP, MV_DOWN },
656 { EL_SP_GRAVITY_ON_PORT_DOWN, MV_UP },
657 { EL_SP_GRAVITY_OFF_PORT_LEFT, MV_RIGHT },
658 { EL_SP_GRAVITY_OFF_PORT_RIGHT, MV_LEFT },
659 { EL_SP_GRAVITY_OFF_PORT_UP, MV_DOWN },
660 { EL_SP_GRAVITY_OFF_PORT_DOWN, MV_UP },
662 { EL_UNDEFINED, MV_NONE }
665 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
667 #define IS_AUTO_CHANGING(e) (element_info[e].has_change_event[CE_DELAY])
668 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
669 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
670 IS_JUST_CHANGING(x, y))
672 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
674 /* static variables for playfield scan mode (scanning forward or backward) */
675 static int playfield_scan_start_x = 0;
676 static int playfield_scan_start_y = 0;
677 static int playfield_scan_delta_x = 1;
678 static int playfield_scan_delta_y = 1;
680 #define SCAN_PLAYFIELD(x, y) for ((y) = playfield_scan_start_y; \
681 (y) >= 0 && (y) <= lev_fieldy - 1; \
682 (y) += playfield_scan_delta_y) \
683 for ((x) = playfield_scan_start_x; \
684 (x) >= 0 && (x) <= lev_fieldx - 1; \
685 (x) += playfield_scan_delta_x) \
687 static void InitPlayfieldScanModeVars()
689 if (game.use_reverse_scan_direction)
691 playfield_scan_start_x = lev_fieldx - 1;
692 playfield_scan_start_y = lev_fieldy - 1;
694 playfield_scan_delta_x = -1;
695 playfield_scan_delta_y = -1;
699 playfield_scan_start_x = 0;
700 playfield_scan_start_y = 0;
702 playfield_scan_delta_x = 1;
703 playfield_scan_delta_y = 1;
707 static void InitPlayfieldScanMode(int mode)
709 game.use_reverse_scan_direction =
710 (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
712 InitPlayfieldScanModeVars();
715 static int get_move_delay_from_stepsize(int move_stepsize)
718 MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
720 /* make sure that stepsize value is always a power of 2 */
721 move_stepsize = (1 << log_2(move_stepsize));
723 return TILEX / move_stepsize;
726 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
729 int move_delay = get_move_delay_from_stepsize(move_stepsize);
730 boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
732 /* do no immediately change move delay -- the player might just be moving */
733 player->move_delay_value_next = move_delay;
735 /* information if player can move must be set separately */
736 player->cannot_move = cannot_move;
740 player->move_delay = game.initial_move_delay;
741 player->move_delay_value = game.initial_move_delay_value;
743 player->move_delay_value_next = -1;
745 player->move_delay_reset_counter = 0;
749 void GetPlayerConfig()
751 if (!audio.sound_available)
752 setup.sound_simple = FALSE;
754 if (!audio.loops_available)
755 setup.sound_loops = FALSE;
757 if (!audio.music_available)
758 setup.sound_music = FALSE;
760 if (!video.fullscreen_available)
761 setup.fullscreen = FALSE;
763 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
765 SetAudioMode(setup.sound);
769 static int getBeltNrFromBeltElement(int element)
771 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
772 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
773 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
776 static int getBeltNrFromBeltActiveElement(int element)
778 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
779 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
780 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
783 static int getBeltNrFromBeltSwitchElement(int element)
785 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
786 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
787 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
790 static int getBeltDirNrFromBeltSwitchElement(int element)
792 static int belt_base_element[4] =
794 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
795 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
796 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
797 EL_CONVEYOR_BELT_4_SWITCH_LEFT
800 int belt_nr = getBeltNrFromBeltSwitchElement(element);
801 int belt_dir_nr = element - belt_base_element[belt_nr];
803 return (belt_dir_nr % 3);
806 static int getBeltDirFromBeltSwitchElement(int element)
808 static int belt_move_dir[3] =
815 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
817 return belt_move_dir[belt_dir_nr];
820 static void InitPlayerField(int x, int y, int element, boolean init_game)
822 if (element == EL_SP_MURPHY)
826 if (stored_player[0].present)
828 Feld[x][y] = EL_SP_MURPHY_CLONE;
834 stored_player[0].use_murphy = TRUE;
837 Feld[x][y] = EL_PLAYER_1;
843 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
844 int jx = player->jx, jy = player->jy;
846 player->present = TRUE;
848 player->block_last_field = (element == EL_SP_MURPHY ?
849 level.sp_block_last_field :
850 level.block_last_field);
852 /* ---------- initialize player's last field block delay --------------- */
854 /* always start with reliable default value (no adjustment needed) */
855 player->block_delay_adjustment = 0;
857 /* special case 1: in Supaplex, Murphy blocks last field one more frame */
858 if (player->block_last_field && element == EL_SP_MURPHY)
859 player->block_delay_adjustment = 1;
861 /* special case 2: in game engines before 3.1.1, blocking was different */
862 if (game.use_block_last_field_bug)
863 player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
865 if (!options.network || player->connected)
867 player->active = TRUE;
869 /* remove potentially duplicate players */
870 if (StorePlayer[jx][jy] == Feld[x][y])
871 StorePlayer[jx][jy] = 0;
873 StorePlayer[x][y] = Feld[x][y];
877 printf("Player %d activated.\n", player->element_nr);
878 printf("[Local player is %d and currently %s.]\n",
879 local_player->element_nr,
880 local_player->active ? "active" : "not active");
884 Feld[x][y] = EL_EMPTY;
886 player->jx = player->last_jx = x;
887 player->jy = player->last_jy = y;
891 static void InitField(int x, int y, boolean init_game)
893 int element = Feld[x][y];
902 InitPlayerField(x, y, element, init_game);
905 case EL_SOKOBAN_FIELD_PLAYER:
906 element = Feld[x][y] = EL_PLAYER_1;
907 InitField(x, y, init_game);
909 element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
910 InitField(x, y, init_game);
913 case EL_SOKOBAN_FIELD_EMPTY:
914 local_player->sokobanfields_still_needed++;
918 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
919 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
920 else if (x > 0 && Feld[x-1][y] == EL_ACID)
921 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
922 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
923 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
924 else if (y > 0 && Feld[x][y-1] == EL_ACID)
925 Feld[x][y] = EL_ACID_POOL_BOTTOM;
926 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
927 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
935 case EL_SPACESHIP_RIGHT:
936 case EL_SPACESHIP_UP:
937 case EL_SPACESHIP_LEFT:
938 case EL_SPACESHIP_DOWN:
940 case EL_BD_BUTTERFLY_RIGHT:
941 case EL_BD_BUTTERFLY_UP:
942 case EL_BD_BUTTERFLY_LEFT:
943 case EL_BD_BUTTERFLY_DOWN:
944 case EL_BD_BUTTERFLY:
945 case EL_BD_FIREFLY_RIGHT:
946 case EL_BD_FIREFLY_UP:
947 case EL_BD_FIREFLY_LEFT:
948 case EL_BD_FIREFLY_DOWN:
950 case EL_PACMAN_RIGHT:
974 if (y == lev_fieldy - 1)
976 Feld[x][y] = EL_AMOEBA_GROWING;
977 Store[x][y] = EL_AMOEBA_WET;
981 case EL_DYNAMITE_ACTIVE:
982 case EL_SP_DISK_RED_ACTIVE:
983 case EL_DYNABOMB_PLAYER_1_ACTIVE:
984 case EL_DYNABOMB_PLAYER_2_ACTIVE:
985 case EL_DYNABOMB_PLAYER_3_ACTIVE:
986 case EL_DYNABOMB_PLAYER_4_ACTIVE:
991 local_player->lights_still_needed++;
995 local_player->friends_still_needed++;
1000 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1003 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1004 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1005 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1006 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1007 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1008 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1009 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1010 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1011 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1012 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1013 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1014 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1017 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1018 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1019 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1021 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
1023 game.belt_dir[belt_nr] = belt_dir;
1024 game.belt_dir_nr[belt_nr] = belt_dir_nr;
1026 else /* more than one switch -- set it like the first switch */
1028 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1033 case EL_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
1035 Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
1038 case EL_LIGHT_SWITCH_ACTIVE:
1040 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1043 case EL_EMC_MAGIC_BALL:
1044 if (game.ball_state)
1045 Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1048 case EL_EMC_MAGIC_BALL_SWITCH:
1049 if (game.ball_state)
1050 Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1055 if (IS_CUSTOM_ELEMENT(element))
1057 if (CAN_MOVE(element))
1060 #if USE_NEW_CUSTOM_VALUE
1061 if (!element_info[element].use_last_ce_value || init_game)
1062 CustomValue[x][y] = GET_NEW_CUSTOM_VALUE(Feld[x][y]);
1066 if (IS_CUSTOM_ELEMENT(element) && CAN_MOVE(element))
1069 else if (IS_GROUP_ELEMENT(element))
1071 struct ElementGroupInfo *group = element_info[element].group;
1072 int last_anim_random_frame = gfx.anim_random_frame;
1075 if (group->choice_mode == ANIM_RANDOM)
1076 gfx.anim_random_frame = RND(group->num_elements_resolved);
1078 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1079 group->choice_mode, 0,
1082 if (group->choice_mode == ANIM_RANDOM)
1083 gfx.anim_random_frame = last_anim_random_frame;
1085 group->choice_pos++;
1087 Feld[x][y] = group->element_resolved[element_pos];
1089 InitField(x, y, init_game);
1096 CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1101 #if USE_NEW_CUSTOM_VALUE
1104 CustomValue[x][y] = GET_NEW_CUSTOM_VALUE(Feld[x][y]);
1106 CustomValue[x][y] = element_info[Feld[x][y]].custom_value_initial;
1114 static inline void InitField_WithBug1(int x, int y, boolean init_game)
1116 InitField(x, y, init_game);
1118 /* not needed to call InitMovDir() -- already done by InitField()! */
1119 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1120 CAN_MOVE(Feld[x][y]))
1124 static inline void InitField_WithBug2(int x, int y, boolean init_game)
1126 int old_element = Feld[x][y];
1128 InitField(x, y, init_game);
1130 /* not needed to call InitMovDir() -- already done by InitField()! */
1131 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1132 CAN_MOVE(old_element) &&
1133 (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1136 /* this case is in fact a combination of not less than three bugs:
1137 first, it calls InitMovDir() for elements that can move, although this is
1138 already done by InitField(); then, it checks the element that was at this
1139 field _before_ the call to InitField() (which can change it); lastly, it
1140 was not called for "mole with direction" elements, which were treated as
1141 "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1145 inline void DrawGameValue_Emeralds(int value)
1147 DrawText(DX_EMERALDS, DY_EMERALDS, int2str(value, 3), FONT_TEXT_2);
1150 inline void DrawGameValue_Dynamite(int value)
1152 DrawText(DX_DYNAMITE, DY_DYNAMITE, int2str(value, 3), FONT_TEXT_2);
1155 inline void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
1159 /* currently only 4 of 8 possible keys are displayed */
1160 for (i = 0; i < STD_NUM_KEYS; i++)
1163 DrawMiniGraphicExt(drawto, DX_KEYS + i * MINI_TILEX, DY_KEYS,
1164 el2edimg(EL_KEY_1 + i));
1166 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
1167 DOOR_GFX_PAGEX5 + XX_KEYS + i * MINI_TILEX, YY_KEYS,
1168 MINI_TILEX, MINI_TILEY, DX_KEYS + i * MINI_TILEX, DY_KEYS);
1172 inline void DrawGameValue_Score(int value)
1174 DrawText(DX_SCORE, DY_SCORE, int2str(value, 5), FONT_TEXT_2);
1177 inline void DrawGameValue_Time(int value)
1180 DrawText(DX_TIME1, DY_TIME, int2str(value, 3), FONT_TEXT_2);
1182 DrawText(DX_TIME2, DY_TIME, int2str(value, 4), FONT_LEVEL_NUMBER);
1185 inline void DrawGameValue_Level(int value)
1188 DrawText(DX_LEVEL, DY_LEVEL, int2str(value, 2), FONT_TEXT_2);
1191 /* misuse area for displaying emeralds to draw bigger level number */
1192 DrawTextExt(drawto, DX_EMERALDS, DY_EMERALDS,
1193 int2str(value, 3), FONT_LEVEL_NUMBER, BLIT_OPAQUE);
1195 /* now copy it to the area for displaying level number */
1196 BlitBitmap(drawto, drawto,
1197 DX_EMERALDS, DY_EMERALDS + 1,
1198 getFontWidth(FONT_LEVEL_NUMBER) * 3,
1199 getFontHeight(FONT_LEVEL_NUMBER) - 1,
1200 DX_LEVEL - 1, DY_LEVEL + 1);
1202 /* restore the area for displaying emeralds */
1203 DrawGameValue_Emeralds(local_player->gems_still_needed);
1205 /* yes, this is all really ugly :-) */
1209 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
1212 int key[MAX_NUM_KEYS];
1215 for (i = 0; i < MAX_NUM_KEYS; i++)
1216 key[i] = key_bits & (1 << i);
1218 DrawGameValue_Level(level_nr);
1220 DrawGameValue_Emeralds(emeralds);
1221 DrawGameValue_Dynamite(dynamite);
1222 DrawGameValue_Score(score);
1223 DrawGameValue_Time(time);
1225 DrawGameValue_Keys(key);
1228 void DrawGameDoorValues()
1232 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
1234 DrawGameDoorValues_EM();
1239 DrawGameValue_Level(level_nr);
1241 DrawGameValue_Emeralds(local_player->gems_still_needed);
1242 DrawGameValue_Dynamite(local_player->inventory_size);
1243 DrawGameValue_Score(local_player->score);
1244 DrawGameValue_Time(TimeLeft);
1246 for (i = 0; i < MAX_PLAYERS; i++)
1247 DrawGameValue_Keys(stored_player[i].key);
1251 static void resolve_group_element(int group_element, int recursion_depth)
1253 static int group_nr;
1254 static struct ElementGroupInfo *group;
1255 struct ElementGroupInfo *actual_group = element_info[group_element].group;
1258 if (recursion_depth > NUM_GROUP_ELEMENTS) /* recursion too deep */
1260 Error(ERR_WARN, "recursion too deep when resolving group element %d",
1261 group_element - EL_GROUP_START + 1);
1263 /* replace element which caused too deep recursion by question mark */
1264 group->element_resolved[group->num_elements_resolved++] = EL_UNKNOWN;
1269 if (recursion_depth == 0) /* initialization */
1271 group = element_info[group_element].group;
1272 group_nr = group_element - EL_GROUP_START;
1274 group->num_elements_resolved = 0;
1275 group->choice_pos = 0;
1278 for (i = 0; i < actual_group->num_elements; i++)
1280 int element = actual_group->element[i];
1282 if (group->num_elements_resolved == NUM_FILE_ELEMENTS)
1285 if (IS_GROUP_ELEMENT(element))
1286 resolve_group_element(element, recursion_depth + 1);
1289 group->element_resolved[group->num_elements_resolved++] = element;
1290 element_info[element].in_group[group_nr] = TRUE;
1297 =============================================================================
1299 -----------------------------------------------------------------------------
1300 initialize game engine due to level / tape version number
1301 =============================================================================
1304 static void InitGameEngine()
1306 int i, j, k, l, x, y;
1308 /* set game engine from tape file when re-playing, else from level file */
1309 game.engine_version = (tape.playing ? tape.engine_version :
1310 level.game_version);
1312 /* ---------------------------------------------------------------------- */
1313 /* set flags for bugs and changes according to active game engine version */
1314 /* ---------------------------------------------------------------------- */
1317 Summary of bugfix/change:
1318 Fixed handling for custom elements that change when pushed by the player.
1320 Fixed/changed in version:
1324 Before 3.1.0, custom elements that "change when pushing" changed directly
1325 after the player started pushing them (until then handled in "DigField()").
1326 Since 3.1.0, these custom elements are not changed until the "pushing"
1327 move of the element is finished (now handled in "ContinueMoving()").
1329 Affected levels/tapes:
1330 The first condition is generally needed for all levels/tapes before version
1331 3.1.0, which might use the old behaviour before it was changed; known tapes
1332 that are affected are some tapes from the level set "Walpurgis Gardens" by
1334 The second condition is an exception from the above case and is needed for
1335 the special case of tapes recorded with game (not engine!) version 3.1.0 or
1336 above (including some development versions of 3.1.0), but before it was
1337 known that this change would break tapes like the above and was fixed in
1338 3.1.1, so that the changed behaviour was active although the engine version
1339 while recording maybe was before 3.1.0. There is at least one tape that is
1340 affected by this exception, which is the tape for the one-level set "Bug
1341 Machine" by Juergen Bonhagen.
1344 game.use_change_when_pushing_bug =
1345 (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1347 tape.game_version >= VERSION_IDENT(3,1,0,0) &&
1348 tape.game_version < VERSION_IDENT(3,1,1,0)));
1351 Summary of bugfix/change:
1352 Fixed handling for blocking the field the player leaves when moving.
1354 Fixed/changed in version:
1358 Before 3.1.1, when "block last field when moving" was enabled, the field
1359 the player is leaving when moving was blocked for the time of the move,
1360 and was directly unblocked afterwards. This resulted in the last field
1361 being blocked for exactly one less than the number of frames of one player
1362 move. Additionally, even when blocking was disabled, the last field was
1363 blocked for exactly one frame.
1364 Since 3.1.1, due to changes in player movement handling, the last field
1365 is not blocked at all when blocking is disabled. When blocking is enabled,
1366 the last field is blocked for exactly the number of frames of one player
1367 move. Additionally, if the player is Murphy, the hero of Supaplex, the
1368 last field is blocked for exactly one more than the number of frames of
1371 Affected levels/tapes:
1372 (!!! yet to be determined -- probably many !!!)
1375 game.use_block_last_field_bug =
1376 (game.engine_version < VERSION_IDENT(3,1,1,0));
1379 Summary of bugfix/change:
1380 Changed behaviour of CE changes with multiple changes per single frame.
1382 Fixed/changed in version:
1386 Before 3.2.0-6, only one single CE change was allowed in each engine frame.
1387 This resulted in race conditions where CEs seem to behave strange in some
1388 situations (where triggered CE changes were just skipped because there was
1389 already a CE change on that tile in the playfield in that engine frame).
1390 Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
1391 (The number of changes per frame must be limited in any case, because else
1392 it is easily possible to define CE changes that would result in an infinite
1393 loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
1394 should be set large enough so that it would only be reached in cases where
1395 the corresponding CE change conditions run into a loop. Therefore, it seems
1396 to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
1397 maximal number of change pages for custom elements.)
1399 Affected levels/tapes:
1403 #if USE_ONLY_ONE_CHANGE_PER_FRAME
1404 game.max_num_changes_per_frame = 1;
1406 game.max_num_changes_per_frame =
1407 (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
1410 /* ---------------------------------------------------------------------- */
1412 /* default scan direction: scan playfield from top/left to bottom/right */
1413 InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
1415 /* dynamically adjust element properties according to game engine version */
1416 InitElementPropertiesEngine(game.engine_version);
1419 printf("level %d: level version == %06d\n", level_nr, level.game_version);
1420 printf(" tape version == %06d [%s] [file: %06d]\n",
1421 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
1423 printf(" => game.engine_version == %06d\n", game.engine_version);
1427 /* ---------- recursively resolve group elements ------------------------- */
1429 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1430 for (j = 0; j < NUM_GROUP_ELEMENTS; j++)
1431 element_info[i].in_group[j] = FALSE;
1433 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
1434 resolve_group_element(EL_GROUP_START + i, 0);
1437 /* ---------- initialize player's initial move delay --------------------- */
1440 /* dynamically adjust player properties according to level information */
1441 game.initial_move_delay_value =
1442 get_move_delay_from_stepsize(level.initial_player_stepsize);
1444 /* dynamically adjust player properties according to level information */
1445 game.initial_move_delay_value =
1446 (level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED);
1449 /* dynamically adjust player properties according to game engine version */
1450 game.initial_move_delay = (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
1451 game.initial_move_delay_value : 0);
1453 /* ---------- initialize player's initial push delay --------------------- */
1455 /* dynamically adjust player properties according to game engine version */
1456 game.initial_push_delay_value =
1457 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
1459 /* ---------- initialize changing elements ------------------------------- */
1461 /* initialize changing elements information */
1462 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1464 struct ElementInfo *ei = &element_info[i];
1466 /* this pointer might have been changed in the level editor */
1467 ei->change = &ei->change_page[0];
1469 if (!IS_CUSTOM_ELEMENT(i))
1471 ei->change->target_element = EL_EMPTY_SPACE;
1472 ei->change->delay_fixed = 0;
1473 ei->change->delay_random = 0;
1474 ei->change->delay_frames = 1;
1477 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1479 ei->has_change_event[j] = FALSE;
1481 ei->event_page_nr[j] = 0;
1482 ei->event_page[j] = &ei->change_page[0];
1486 /* add changing elements from pre-defined list */
1487 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
1489 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
1490 struct ElementInfo *ei = &element_info[ch_delay->element];
1492 ei->change->target_element = ch_delay->target_element;
1493 ei->change->delay_fixed = ch_delay->change_delay;
1495 ei->change->pre_change_function = ch_delay->pre_change_function;
1496 ei->change->change_function = ch_delay->change_function;
1497 ei->change->post_change_function = ch_delay->post_change_function;
1499 ei->change->can_change = TRUE;
1500 ei->change->can_change_or_has_action = TRUE;
1502 ei->has_change_event[CE_DELAY] = TRUE;
1504 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
1505 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
1508 /* ---------- initialize internal run-time variables ------------- */
1510 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1512 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1514 for (j = 0; j < ei->num_change_pages; j++)
1516 ei->change_page[j].can_change_or_has_action =
1517 (ei->change_page[j].can_change |
1518 ei->change_page[j].has_action);
1522 /* add change events from custom element configuration */
1523 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1525 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1527 for (j = 0; j < ei->num_change_pages; j++)
1529 if (!ei->change_page[j].can_change_or_has_action)
1532 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1534 /* only add event page for the first page found with this event */
1535 if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
1537 ei->has_change_event[k] = TRUE;
1539 ei->event_page_nr[k] = j;
1540 ei->event_page[k] = &ei->change_page[j];
1546 /* ---------- initialize run-time trigger player and element ------------- */
1548 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1550 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1552 for (j = 0; j < ei->num_change_pages; j++)
1554 ei->change_page[j].actual_trigger_element = EL_EMPTY;
1555 ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
1556 ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
1557 ei->change_page[j].actual_trigger_ce_value = 0;
1561 /* ---------- initialize trigger events ---------------------------------- */
1563 /* initialize trigger events information */
1564 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1565 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1566 trigger_events[i][j] = FALSE;
1568 /* add trigger events from element change event properties */
1569 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1571 struct ElementInfo *ei = &element_info[i];
1573 for (j = 0; j < ei->num_change_pages; j++)
1575 if (!ei->change_page[j].can_change_or_has_action)
1578 if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
1580 int trigger_element = ei->change_page[j].trigger_element;
1582 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1584 if (ei->change_page[j].has_event[k])
1586 if (IS_GROUP_ELEMENT(trigger_element))
1588 struct ElementGroupInfo *group =
1589 element_info[trigger_element].group;
1591 for (l = 0; l < group->num_elements_resolved; l++)
1592 trigger_events[group->element_resolved[l]][k] = TRUE;
1595 trigger_events[trigger_element][k] = TRUE;
1602 /* ---------- initialize push delay -------------------------------------- */
1604 /* initialize push delay values to default */
1605 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1607 if (!IS_CUSTOM_ELEMENT(i))
1609 element_info[i].push_delay_fixed = game.default_push_delay_fixed;
1610 element_info[i].push_delay_random = game.default_push_delay_random;
1614 /* set push delay value for certain elements from pre-defined list */
1615 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
1617 int e = push_delay_list[i].element;
1619 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
1620 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
1623 /* set push delay value for Supaplex elements for newer engine versions */
1624 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
1626 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1628 if (IS_SP_ELEMENT(i))
1630 /* set SP push delay to just enough to push under a falling zonk */
1631 int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
1633 element_info[i].push_delay_fixed = delay;
1634 element_info[i].push_delay_random = 0;
1639 /* ---------- initialize move stepsize ----------------------------------- */
1641 /* initialize move stepsize values to default */
1642 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1643 if (!IS_CUSTOM_ELEMENT(i))
1644 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
1646 /* set move stepsize value for certain elements from pre-defined list */
1647 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
1649 int e = move_stepsize_list[i].element;
1651 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
1654 /* ---------- initialize collect score ----------------------------------- */
1656 /* initialize collect score values for custom elements from initial value */
1657 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1658 if (IS_CUSTOM_ELEMENT(i))
1659 element_info[i].collect_score = element_info[i].collect_score_initial;
1661 /* ---------- initialize collect count ----------------------------------- */
1663 /* initialize collect count values for non-custom elements */
1664 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1665 if (!IS_CUSTOM_ELEMENT(i))
1666 element_info[i].collect_count_initial = 0;
1668 /* add collect count values for all elements from pre-defined list */
1669 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
1670 element_info[collect_count_list[i].element].collect_count_initial =
1671 collect_count_list[i].count;
1673 /* ---------- initialize access direction -------------------------------- */
1675 /* initialize access direction values to default (access from every side) */
1676 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1677 if (!IS_CUSTOM_ELEMENT(i))
1678 element_info[i].access_direction = MV_ALL_DIRECTIONS;
1680 /* set access direction value for certain elements from pre-defined list */
1681 for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
1682 element_info[access_direction_list[i].element].access_direction =
1683 access_direction_list[i].direction;
1685 /* ---------- initialize explosion content ------------------------------- */
1686 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1688 if (IS_CUSTOM_ELEMENT(i))
1691 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
1693 /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
1695 element_info[i].content.e[x][y] =
1696 (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
1697 i == EL_PLAYER_2 ? EL_EMERALD_RED :
1698 i == EL_PLAYER_3 ? EL_EMERALD :
1699 i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
1700 i == EL_MOLE ? EL_EMERALD_RED :
1701 i == EL_PENGUIN ? EL_EMERALD_PURPLE :
1702 i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
1703 i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
1704 i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
1705 i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
1706 i == EL_WALL_EMERALD ? EL_EMERALD :
1707 i == EL_WALL_DIAMOND ? EL_DIAMOND :
1708 i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
1709 i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
1710 i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
1711 i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
1712 i == EL_WALL_PEARL ? EL_PEARL :
1713 i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
1719 int get_num_special_action(int element, int action_first, int action_last)
1721 int num_special_action = 0;
1724 for (i = action_first; i <= action_last; i++)
1726 boolean found = FALSE;
1728 for (j = 0; j < NUM_DIRECTIONS; j++)
1729 if (el_act_dir2img(element, i, j) !=
1730 el_act_dir2img(element, ACTION_DEFAULT, j))
1734 num_special_action++;
1739 return num_special_action;
1743 =============================================================================
1745 -----------------------------------------------------------------------------
1746 initialize and start new game
1747 =============================================================================
1752 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
1753 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
1754 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
1759 /* don't play tapes over network */
1760 network_playing = (options.network && !tape.playing);
1762 for (i = 0; i < MAX_PLAYERS; i++)
1764 struct PlayerInfo *player = &stored_player[i];
1766 player->index_nr = i;
1767 player->index_bit = (1 << i);
1768 player->element_nr = EL_PLAYER_1 + i;
1770 player->present = FALSE;
1771 player->active = FALSE;
1774 player->effective_action = 0;
1775 player->programmed_action = 0;
1778 player->gems_still_needed = level.gems_needed;
1779 player->sokobanfields_still_needed = 0;
1780 player->lights_still_needed = 0;
1781 player->friends_still_needed = 0;
1783 for (j = 0; j < MAX_NUM_KEYS; j++)
1784 player->key[j] = FALSE;
1786 player->dynabomb_count = 0;
1787 player->dynabomb_size = 1;
1788 player->dynabombs_left = 0;
1789 player->dynabomb_xl = FALSE;
1791 player->MovDir = MV_NONE;
1794 player->GfxDir = MV_NONE;
1795 player->GfxAction = ACTION_DEFAULT;
1797 player->StepFrame = 0;
1799 player->use_murphy = FALSE;
1800 player->artwork_element =
1801 (level.use_artwork_element[i] ? level.artwork_element[i] :
1802 player->element_nr);
1804 player->block_last_field = FALSE; /* initialized in InitPlayerField() */
1805 player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
1807 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
1809 player->actual_frame_counter = 0;
1811 player->step_counter = 0;
1813 player->last_move_dir = MV_NONE;
1815 player->is_waiting = FALSE;
1816 player->is_moving = FALSE;
1817 player->is_auto_moving = FALSE;
1818 player->is_digging = FALSE;
1819 player->is_snapping = FALSE;
1820 player->is_collecting = FALSE;
1821 player->is_pushing = FALSE;
1822 player->is_switching = FALSE;
1823 player->is_dropping = FALSE;
1825 player->is_bored = FALSE;
1826 player->is_sleeping = FALSE;
1828 player->frame_counter_bored = -1;
1829 player->frame_counter_sleeping = -1;
1831 player->anim_delay_counter = 0;
1832 player->post_delay_counter = 0;
1834 player->action_waiting = ACTION_DEFAULT;
1835 player->last_action_waiting = ACTION_DEFAULT;
1836 player->special_action_bored = ACTION_DEFAULT;
1837 player->special_action_sleeping = ACTION_DEFAULT;
1839 /* set number of special actions for bored and sleeping animation */
1840 player->num_special_action_bored =
1841 get_num_special_action(player->artwork_element,
1842 ACTION_BORING_1, ACTION_BORING_LAST);
1843 player->num_special_action_sleeping =
1844 get_num_special_action(player->artwork_element,
1845 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
1847 player->switch_x = -1;
1848 player->switch_y = -1;
1850 player->drop_x = -1;
1851 player->drop_y = -1;
1853 player->show_envelope = 0;
1856 SetPlayerMoveSpeed(player, level.initial_player_stepsize, TRUE);
1858 player->move_delay = game.initial_move_delay;
1859 player->move_delay_value = game.initial_move_delay_value;
1861 player->move_delay_value_next = -1;
1863 player->move_delay_reset_counter = 0;
1865 player->cannot_move = FALSE;
1868 player->push_delay = -1; /* initialized when pushing starts */
1869 player->push_delay_value = game.initial_push_delay_value;
1871 player->drop_delay = 0;
1873 player->last_jx = player->last_jy = 0;
1874 player->jx = player->jy = 0;
1876 player->shield_normal_time_left = 0;
1877 player->shield_deadly_time_left = 0;
1879 player->inventory_infinite_element = EL_UNDEFINED;
1880 player->inventory_size = 0;
1882 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
1883 SnapField(player, 0, 0);
1885 player->LevelSolved = FALSE;
1886 player->GameOver = FALSE;
1889 network_player_action_received = FALSE;
1891 #if defined(NETWORK_AVALIABLE)
1892 /* initial null action */
1893 if (network_playing)
1894 SendToServer_MovePlayer(MV_NONE);
1903 TimeLeft = level.time;
1906 ScreenMovDir = MV_NONE;
1910 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
1912 AllPlayersGone = FALSE;
1914 game.yamyam_content_nr = 0;
1915 game.magic_wall_active = FALSE;
1916 game.magic_wall_time_left = 0;
1917 game.light_time_left = 0;
1918 game.timegate_time_left = 0;
1919 game.switchgate_pos = 0;
1920 game.wind_direction = level.wind_direction_initial;
1921 game.gravity = level.initial_gravity;
1922 game.explosions_delayed = TRUE;
1924 game.lenses_time_left = 0;
1925 game.magnify_time_left = 0;
1927 game.ball_state = level.ball_state_initial;
1928 game.ball_content_nr = 0;
1930 game.envelope_active = FALSE;
1932 for (i = 0; i < NUM_BELTS; i++)
1934 game.belt_dir[i] = MV_NONE;
1935 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1938 for (i = 0; i < MAX_NUM_AMOEBA; i++)
1939 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
1942 SCAN_PLAYFIELD(x, y)
1944 for (x = 0; x < lev_fieldx; x++) for (y = 0; y < lev_fieldy; y++)
1947 Feld[x][y] = level.field[x][y];
1948 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
1949 ChangeDelay[x][y] = 0;
1950 ChangePage[x][y] = -1;
1951 #if USE_NEW_CUSTOM_VALUE
1952 CustomValue[x][y] = 0; /* initialized in InitField() */
1954 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
1956 WasJustMoving[x][y] = 0;
1957 WasJustFalling[x][y] = 0;
1958 CheckCollision[x][y] = 0;
1960 Pushed[x][y] = FALSE;
1962 ChangeCount[x][y] = 0;
1963 ChangeEvent[x][y] = -1;
1965 ExplodePhase[x][y] = 0;
1966 ExplodeDelay[x][y] = 0;
1967 ExplodeField[x][y] = EX_TYPE_NONE;
1969 RunnerVisit[x][y] = 0;
1970 PlayerVisit[x][y] = 0;
1973 GfxRandom[x][y] = INIT_GFX_RANDOM();
1974 GfxElement[x][y] = EL_UNDEFINED;
1975 GfxAction[x][y] = ACTION_DEFAULT;
1976 GfxDir[x][y] = MV_NONE;
1980 SCAN_PLAYFIELD(x, y)
1982 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
1985 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
1987 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
1989 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
1992 InitField(x, y, TRUE);
1997 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
1998 emulate_sb ? EMU_SOKOBAN :
1999 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
2001 #if USE_NEW_ALL_SLIPPERY
2002 /* initialize type of slippery elements */
2003 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2005 if (!IS_CUSTOM_ELEMENT(i))
2007 /* default: elements slip down either to the left or right randomly */
2008 element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
2010 /* SP style elements prefer to slip down on the left side */
2011 if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
2012 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
2014 /* BD style elements prefer to slip down on the left side */
2015 if (game.emulation == EMU_BOULDERDASH)
2016 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
2021 /* initialize explosion and ignition delay */
2022 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2024 if (!IS_CUSTOM_ELEMENT(i))
2027 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
2028 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
2029 game.emulation == EMU_SUPAPLEX ? 3 : 2);
2030 int last_phase = (num_phase + 1) * delay;
2031 int half_phase = (num_phase / 2) * delay;
2033 element_info[i].explosion_delay = last_phase - 1;
2034 element_info[i].ignition_delay = half_phase;
2036 if (i == EL_BLACK_ORB)
2037 element_info[i].ignition_delay = 1;
2041 if (element_info[i].explosion_delay < 1) /* !!! check again !!! */
2042 element_info[i].explosion_delay = 1;
2044 if (element_info[i].ignition_delay < 1) /* !!! check again !!! */
2045 element_info[i].ignition_delay = 1;
2049 /* correct non-moving belts to start moving left */
2050 for (i = 0; i < NUM_BELTS; i++)
2051 if (game.belt_dir[i] == MV_NONE)
2052 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
2054 /* check if any connected player was not found in playfield */
2055 for (i = 0; i < MAX_PLAYERS; i++)
2057 struct PlayerInfo *player = &stored_player[i];
2059 if (player->connected && !player->present)
2061 for (j = 0; j < MAX_PLAYERS; j++)
2063 struct PlayerInfo *some_player = &stored_player[j];
2064 int jx = some_player->jx, jy = some_player->jy;
2066 /* assign first free player found that is present in the playfield */
2067 if (some_player->present && !some_player->connected)
2069 player->present = TRUE;
2070 player->active = TRUE;
2072 some_player->present = FALSE;
2073 some_player->active = FALSE;
2076 player->element_nr = some_player->element_nr;
2079 player->artwork_element = some_player->artwork_element;
2081 player->block_last_field = some_player->block_last_field;
2082 player->block_delay_adjustment = some_player->block_delay_adjustment;
2084 StorePlayer[jx][jy] = player->element_nr;
2085 player->jx = player->last_jx = jx;
2086 player->jy = player->last_jy = jy;
2096 /* when playing a tape, eliminate all players which do not participate */
2098 for (i = 0; i < MAX_PLAYERS; i++)
2100 if (stored_player[i].active && !tape.player_participates[i])
2102 struct PlayerInfo *player = &stored_player[i];
2103 int jx = player->jx, jy = player->jy;
2105 player->active = FALSE;
2106 StorePlayer[jx][jy] = 0;
2107 Feld[jx][jy] = EL_EMPTY;
2111 else if (!options.network && !setup.team_mode) /* && !tape.playing */
2113 /* when in single player mode, eliminate all but the first active player */
2115 for (i = 0; i < MAX_PLAYERS; i++)
2117 if (stored_player[i].active)
2119 for (j = i + 1; j < MAX_PLAYERS; j++)
2121 if (stored_player[j].active)
2123 struct PlayerInfo *player = &stored_player[j];
2124 int jx = player->jx, jy = player->jy;
2126 player->active = FALSE;
2127 player->present = FALSE;
2129 StorePlayer[jx][jy] = 0;
2130 Feld[jx][jy] = EL_EMPTY;
2137 /* when recording the game, store which players take part in the game */
2140 for (i = 0; i < MAX_PLAYERS; i++)
2141 if (stored_player[i].active)
2142 tape.player_participates[i] = TRUE;
2147 for (i = 0; i < MAX_PLAYERS; i++)
2149 struct PlayerInfo *player = &stored_player[i];
2151 printf("Player %d: present == %d, connected == %d, active == %d.\n",
2156 if (local_player == player)
2157 printf("Player %d is local player.\n", i+1);
2161 if (BorderElement == EL_EMPTY)
2164 SBX_Right = lev_fieldx - SCR_FIELDX;
2166 SBY_Lower = lev_fieldy - SCR_FIELDY;
2171 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
2173 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
2176 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
2177 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
2179 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
2180 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
2182 /* if local player not found, look for custom element that might create
2183 the player (make some assumptions about the right custom element) */
2184 if (!local_player->present)
2186 int start_x = 0, start_y = 0;
2187 int found_rating = 0;
2188 int found_element = EL_UNDEFINED;
2189 int player_nr = local_player->index_nr;
2192 SCAN_PLAYFIELD(x, y)
2194 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
2197 int element = Feld[x][y];
2202 if (level.use_start_element[player_nr] &&
2203 level.start_element[player_nr] == element &&
2210 found_element = element;
2213 if (!IS_CUSTOM_ELEMENT(element))
2216 if (CAN_CHANGE(element))
2218 for (i = 0; i < element_info[element].num_change_pages; i++)
2220 /* check for player created from custom element as single target */
2221 content = element_info[element].change_page[i].target_element;
2222 is_player = ELEM_IS_PLAYER(content);
2224 if (is_player && (found_rating < 3 || element < found_element))
2230 found_element = element;
2235 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
2237 /* check for player created from custom element as explosion content */
2238 content = element_info[element].content.e[xx][yy];
2239 is_player = ELEM_IS_PLAYER(content);
2241 if (is_player && (found_rating < 2 || element < found_element))
2243 start_x = x + xx - 1;
2244 start_y = y + yy - 1;
2247 found_element = element;
2250 if (!CAN_CHANGE(element))
2253 for (i = 0; i < element_info[element].num_change_pages; i++)
2255 /* check for player created from custom element as extended target */
2257 element_info[element].change_page[i].target_content.e[xx][yy];
2259 is_player = ELEM_IS_PLAYER(content);
2261 if (is_player && (found_rating < 1 || element < found_element))
2263 start_x = x + xx - 1;
2264 start_y = y + yy - 1;
2267 found_element = element;
2273 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
2274 start_x > SBX_Right + MIDPOSX ? SBX_Right :
2277 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
2278 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
2283 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2284 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2285 local_player->jx - MIDPOSX);
2287 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2288 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2289 local_player->jy - MIDPOSY);
2292 if (!game.restart_level)
2293 CloseDoor(DOOR_CLOSE_1);
2295 /* !!! FIX THIS (START) !!! */
2296 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2298 InitGameEngine_EM();
2305 /* after drawing the level, correct some elements */
2306 if (game.timegate_time_left == 0)
2307 CloseAllOpenTimegates();
2309 if (setup.soft_scrolling)
2310 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
2312 redraw_mask |= REDRAW_FROM_BACKBUFFER;
2315 /* !!! FIX THIS (END) !!! */
2317 if (!game.restart_level)
2319 /* copy default game door content to main double buffer */
2320 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2321 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2324 DrawGameDoorValues();
2326 if (!game.restart_level)
2330 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
2331 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
2332 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
2336 /* copy actual game door content to door double buffer for OpenDoor() */
2337 BlitBitmap(drawto, bitmap_db_door,
2338 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
2340 OpenDoor(DOOR_OPEN_ALL);
2342 PlaySoundStereo(SND_GAME_STARTING, SOUND_MIDDLE);
2344 if (setup.sound_music)
2347 KeyboardAutoRepeatOffUnlessAutoplay();
2351 for (i = 0; i < MAX_PLAYERS; i++)
2352 printf("Player %d %sactive.\n",
2353 i + 1, (stored_player[i].active ? "" : "not "));
2357 game.restart_level = FALSE;
2360 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
2362 /* this is used for non-R'n'D game engines to update certain engine values */
2364 /* needed to determine if sounds are played within the visible screen area */
2365 scroll_x = actual_scroll_x;
2366 scroll_y = actual_scroll_y;
2369 void InitMovDir(int x, int y)
2371 int i, element = Feld[x][y];
2372 static int xy[4][2] =
2379 static int direction[3][4] =
2381 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
2382 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
2383 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
2392 Feld[x][y] = EL_BUG;
2393 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
2396 case EL_SPACESHIP_RIGHT:
2397 case EL_SPACESHIP_UP:
2398 case EL_SPACESHIP_LEFT:
2399 case EL_SPACESHIP_DOWN:
2400 Feld[x][y] = EL_SPACESHIP;
2401 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
2404 case EL_BD_BUTTERFLY_RIGHT:
2405 case EL_BD_BUTTERFLY_UP:
2406 case EL_BD_BUTTERFLY_LEFT:
2407 case EL_BD_BUTTERFLY_DOWN:
2408 Feld[x][y] = EL_BD_BUTTERFLY;
2409 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
2412 case EL_BD_FIREFLY_RIGHT:
2413 case EL_BD_FIREFLY_UP:
2414 case EL_BD_FIREFLY_LEFT:
2415 case EL_BD_FIREFLY_DOWN:
2416 Feld[x][y] = EL_BD_FIREFLY;
2417 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
2420 case EL_PACMAN_RIGHT:
2422 case EL_PACMAN_LEFT:
2423 case EL_PACMAN_DOWN:
2424 Feld[x][y] = EL_PACMAN;
2425 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
2428 case EL_SP_SNIKSNAK:
2429 MovDir[x][y] = MV_UP;
2432 case EL_SP_ELECTRON:
2433 MovDir[x][y] = MV_LEFT;
2440 Feld[x][y] = EL_MOLE;
2441 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
2445 if (IS_CUSTOM_ELEMENT(element))
2447 struct ElementInfo *ei = &element_info[element];
2448 int move_direction_initial = ei->move_direction_initial;
2449 int move_pattern = ei->move_pattern;
2451 if (move_direction_initial == MV_START_PREVIOUS)
2453 if (MovDir[x][y] != MV_NONE)
2456 move_direction_initial = MV_START_AUTOMATIC;
2459 if (move_direction_initial == MV_START_RANDOM)
2460 MovDir[x][y] = 1 << RND(4);
2461 else if (move_direction_initial & MV_ANY_DIRECTION)
2462 MovDir[x][y] = move_direction_initial;
2463 else if (move_pattern == MV_ALL_DIRECTIONS ||
2464 move_pattern == MV_TURNING_LEFT ||
2465 move_pattern == MV_TURNING_RIGHT ||
2466 move_pattern == MV_TURNING_LEFT_RIGHT ||
2467 move_pattern == MV_TURNING_RIGHT_LEFT ||
2468 move_pattern == MV_TURNING_RANDOM)
2469 MovDir[x][y] = 1 << RND(4);
2470 else if (move_pattern == MV_HORIZONTAL)
2471 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
2472 else if (move_pattern == MV_VERTICAL)
2473 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
2474 else if (move_pattern & MV_ANY_DIRECTION)
2475 MovDir[x][y] = element_info[element].move_pattern;
2476 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
2477 move_pattern == MV_ALONG_RIGHT_SIDE)
2479 /* use random direction as default start direction */
2480 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2481 MovDir[x][y] = 1 << RND(4);
2483 for (i = 0; i < NUM_DIRECTIONS; i++)
2485 int x1 = x + xy[i][0];
2486 int y1 = y + xy[i][1];
2488 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2490 if (move_pattern == MV_ALONG_RIGHT_SIDE)
2491 MovDir[x][y] = direction[0][i];
2493 MovDir[x][y] = direction[1][i];
2502 MovDir[x][y] = 1 << RND(4);
2504 if (element != EL_BUG &&
2505 element != EL_SPACESHIP &&
2506 element != EL_BD_BUTTERFLY &&
2507 element != EL_BD_FIREFLY)
2510 for (i = 0; i < NUM_DIRECTIONS; i++)
2512 int x1 = x + xy[i][0];
2513 int y1 = y + xy[i][1];
2515 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2517 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
2519 MovDir[x][y] = direction[0][i];
2522 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
2523 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
2525 MovDir[x][y] = direction[1][i];
2534 GfxDir[x][y] = MovDir[x][y];
2537 void InitAmoebaNr(int x, int y)
2540 int group_nr = AmoebeNachbarNr(x, y);
2544 for (i = 1; i < MAX_NUM_AMOEBA; i++)
2546 if (AmoebaCnt[i] == 0)
2554 AmoebaNr[x][y] = group_nr;
2555 AmoebaCnt[group_nr]++;
2556 AmoebaCnt2[group_nr]++;
2562 boolean raise_level = FALSE;
2564 if (local_player->MovPos)
2567 if (tape.auto_play) /* tape might already be stopped here */
2568 tape.auto_play_level_solved = TRUE;
2570 local_player->LevelSolved = FALSE;
2572 PlaySoundStereo(SND_GAME_WINNING, SOUND_MIDDLE);
2576 if (!tape.playing && setup.sound_loops)
2577 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2578 SND_CTRL_PLAY_LOOP);
2580 while (TimeLeft > 0)
2582 if (!tape.playing && !setup.sound_loops)
2583 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2585 if (TimeLeft > 100 && TimeLeft % 10 == 0)
2588 RaiseScore(level.score[SC_TIME_BONUS] * 10);
2593 RaiseScore(level.score[SC_TIME_BONUS]);
2596 DrawGameValue_Time(TimeLeft);
2604 if (!tape.playing && setup.sound_loops)
2605 StopSound(SND_GAME_LEVELTIME_BONUS);
2607 else if (level.time == 0) /* level without time limit */
2609 if (!tape.playing && setup.sound_loops)
2610 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2611 SND_CTRL_PLAY_LOOP);
2613 while (TimePlayed < 999)
2615 if (!tape.playing && !setup.sound_loops)
2616 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2618 if (TimePlayed < 900 && TimePlayed % 10 == 0)
2621 RaiseScore(level.score[SC_TIME_BONUS] * 10);
2626 RaiseScore(level.score[SC_TIME_BONUS]);
2629 DrawGameValue_Time(TimePlayed);
2637 if (!tape.playing && setup.sound_loops)
2638 StopSound(SND_GAME_LEVELTIME_BONUS);
2641 /* close exit door after last player */
2642 if (AllPlayersGone && ExitX >= 0 && ExitY >= 0 &&
2643 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
2644 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN))
2646 int element = Feld[ExitX][ExitY];
2648 Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
2649 EL_SP_EXIT_CLOSING);
2651 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
2654 /* player disappears */
2655 if (ExitX >= 0 && ExitY >= 0)
2656 DrawLevelField(ExitX, ExitY);
2662 printf("::: TAPE PLAYING -> DO NOT SAVE SCORE\n");
2664 printf("::: NO TAPE PLAYING -> SAVING SCORE\n");
2670 CloseDoor(DOOR_CLOSE_1);
2675 SaveTape(tape.level_nr); /* Ask to save tape */
2678 if (level_nr == leveldir_current->handicap_level)
2680 leveldir_current->handicap_level++;
2681 SaveLevelSetup_SeriesInfo();
2684 if (level_editor_test_game)
2685 local_player->score = -1; /* no highscore when playing from editor */
2686 else if (level_nr < leveldir_current->last_level)
2687 raise_level = TRUE; /* advance to next level */
2689 if ((hi_pos = NewHiScore()) >= 0)
2691 game_status = GAME_MODE_SCORES;
2692 DrawHallOfFame(hi_pos);
2701 game_status = GAME_MODE_MAIN;
2718 LoadScore(level_nr);
2720 if (strcmp(setup.player_name, EMPTY_PLAYER_NAME) == 0 ||
2721 local_player->score < highscore[MAX_SCORE_ENTRIES - 1].Score)
2724 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
2726 if (local_player->score > highscore[k].Score)
2728 /* player has made it to the hall of fame */
2730 if (k < MAX_SCORE_ENTRIES - 1)
2732 int m = MAX_SCORE_ENTRIES - 1;
2735 for (l = k; l < MAX_SCORE_ENTRIES; l++)
2736 if (!strcmp(setup.player_name, highscore[l].Name))
2738 if (m == k) /* player's new highscore overwrites his old one */
2742 for (l = m; l > k; l--)
2744 strcpy(highscore[l].Name, highscore[l - 1].Name);
2745 highscore[l].Score = highscore[l - 1].Score;
2752 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
2753 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
2754 highscore[k].Score = local_player->score;
2760 else if (!strncmp(setup.player_name, highscore[k].Name,
2761 MAX_PLAYER_NAME_LEN))
2762 break; /* player already there with a higher score */
2768 SaveScore(level_nr);
2773 inline static int getElementMoveStepsize(int x, int y)
2775 int element = Feld[x][y];
2776 int direction = MovDir[x][y];
2777 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2778 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2779 int horiz_move = (dx != 0);
2780 int sign = (horiz_move ? dx : dy);
2781 int step = sign * element_info[element].move_stepsize;
2783 /* special values for move stepsize for spring and things on conveyor belt */
2787 if (element == EL_SPRING)
2788 step = sign * MOVE_STEPSIZE_NORMAL * 2;
2789 else if (CAN_FALL(element) && !CAN_MOVE(element) &&
2790 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
2791 step = sign * MOVE_STEPSIZE_NORMAL / 2;
2793 if (CAN_FALL(element) &&
2794 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
2795 step = sign * MOVE_STEPSIZE_NORMAL / 2;
2796 else if (element == EL_SPRING)
2797 step = sign * MOVE_STEPSIZE_NORMAL * 2;
2804 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
2806 if (player->GfxAction != action || player->GfxDir != dir)
2809 printf("Player frame reset! (%d => %d, %d => %d)\n",
2810 player->GfxAction, action, player->GfxDir, dir);
2813 player->GfxAction = action;
2814 player->GfxDir = dir;
2816 player->StepFrame = 0;
2820 static void ResetRandomAnimationValue(int x, int y)
2822 GfxRandom[x][y] = INIT_GFX_RANDOM();
2825 static void ResetGfxAnimation(int x, int y)
2828 int element, graphic;
2832 GfxAction[x][y] = ACTION_DEFAULT;
2833 GfxDir[x][y] = MovDir[x][y];
2836 element = Feld[x][y];
2837 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
2839 if (graphic_info[graphic].anim_global_sync)
2840 GfxFrame[x][y] = FrameCounter;
2841 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
2842 GfxFrame[x][y] = CustomValue[x][y];
2843 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2844 GfxFrame[x][y] = element_info[element].collect_score;
2848 void InitMovingField(int x, int y, int direction)
2850 int element = Feld[x][y];
2854 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2855 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2859 if (!WasJustMoving[x][y] || direction != MovDir[x][y])
2860 ResetGfxAnimation(x, y);
2862 MovDir[x][y] = direction;
2863 GfxDir[x][y] = direction;
2864 GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
2865 ACTION_FALLING : ACTION_MOVING);
2868 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
2870 if (graphic_info[graphic].anim_global_sync)
2871 GfxFrame[x][y] = FrameCounter;
2872 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
2873 GfxFrame[x][y] = CustomValue[x][y];
2874 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2875 GfxFrame[x][y] = element_info[element].collect_score;
2878 /* this is needed for CEs with property "can move" / "not moving" */
2880 if (getElementMoveStepsize(x, y) != 0) /* moving or being moved */
2882 if (Feld[newx][newy] == EL_EMPTY)
2883 Feld[newx][newy] = EL_BLOCKED;
2885 MovDir[newx][newy] = MovDir[x][y];
2887 #if USE_NEW_CUSTOM_VALUE
2888 CustomValue[newx][newy] = CustomValue[x][y];
2891 GfxFrame[newx][newy] = GfxFrame[x][y];
2892 GfxRandom[newx][newy] = GfxRandom[x][y];
2893 GfxAction[newx][newy] = GfxAction[x][y];
2894 GfxDir[newx][newy] = GfxDir[x][y];
2898 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
2900 int direction = MovDir[x][y];
2902 int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
2903 int newy = y + (direction & MV_UP ? -1 : direction & MV_DOWN ? +1 : 0);
2905 int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2906 int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2913 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
2915 int oldx = x, oldy = y;
2916 int direction = MovDir[x][y];
2918 if (direction == MV_LEFT)
2920 else if (direction == MV_RIGHT)
2922 else if (direction == MV_UP)
2924 else if (direction == MV_DOWN)
2927 *comes_from_x = oldx;
2928 *comes_from_y = oldy;
2931 int MovingOrBlocked2Element(int x, int y)
2933 int element = Feld[x][y];
2935 if (element == EL_BLOCKED)
2939 Blocked2Moving(x, y, &oldx, &oldy);
2940 return Feld[oldx][oldy];
2946 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
2948 /* like MovingOrBlocked2Element(), but if element is moving
2949 and (x,y) is the field the moving element is just leaving,
2950 return EL_BLOCKED instead of the element value */
2951 int element = Feld[x][y];
2953 if (IS_MOVING(x, y))
2955 if (element == EL_BLOCKED)
2959 Blocked2Moving(x, y, &oldx, &oldy);
2960 return Feld[oldx][oldy];
2969 static void RemoveField(int x, int y)
2971 Feld[x][y] = EL_EMPTY;
2977 #if USE_NEW_CUSTOM_VALUE
2978 CustomValue[x][y] = 0;
2982 ChangeDelay[x][y] = 0;
2983 ChangePage[x][y] = -1;
2984 Pushed[x][y] = FALSE;
2987 ExplodeField[x][y] = EX_TYPE_NONE;
2990 GfxElement[x][y] = EL_UNDEFINED;
2991 GfxAction[x][y] = ACTION_DEFAULT;
2992 GfxDir[x][y] = MV_NONE;
2995 void RemoveMovingField(int x, int y)
2997 int oldx = x, oldy = y, newx = x, newy = y;
2998 int element = Feld[x][y];
2999 int next_element = EL_UNDEFINED;
3001 if (element != EL_BLOCKED && !IS_MOVING(x, y))
3004 if (IS_MOVING(x, y))
3006 Moving2Blocked(x, y, &newx, &newy);
3008 if (Feld[newx][newy] != EL_BLOCKED)
3010 /* element is moving, but target field is not free (blocked), but
3011 already occupied by something different (example: acid pool);
3012 in this case, only remove the moving field, but not the target */
3014 RemoveField(oldx, oldy);
3016 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
3018 DrawLevelField(oldx, oldy);
3023 else if (element == EL_BLOCKED)
3025 Blocked2Moving(x, y, &oldx, &oldy);
3026 if (!IS_MOVING(oldx, oldy))
3030 if (element == EL_BLOCKED &&
3031 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
3032 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
3033 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
3034 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
3035 next_element = get_next_element(Feld[oldx][oldy]);
3037 RemoveField(oldx, oldy);
3038 RemoveField(newx, newy);
3040 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
3042 if (next_element != EL_UNDEFINED)
3043 Feld[oldx][oldy] = next_element;
3045 DrawLevelField(oldx, oldy);
3046 DrawLevelField(newx, newy);
3049 void DrawDynamite(int x, int y)
3051 int sx = SCREENX(x), sy = SCREENY(y);
3052 int graphic = el2img(Feld[x][y]);
3055 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
3058 if (IS_WALKABLE_INSIDE(Back[x][y]))
3062 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
3063 else if (Store[x][y])
3064 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
3066 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
3068 if (Back[x][y] || Store[x][y])
3069 DrawGraphicThruMask(sx, sy, graphic, frame);
3071 DrawGraphic(sx, sy, graphic, frame);
3074 void CheckDynamite(int x, int y)
3076 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
3080 if (MovDelay[x][y] != 0)
3083 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
3089 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
3094 void DrawRelocatePlayer(struct PlayerInfo *player)
3096 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3097 boolean no_delay = (tape.warp_forward);
3098 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
3099 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
3100 int jx = player->jx;
3101 int jy = player->jy;
3103 if (level.instant_relocation)
3105 int offset = (setup.scroll_delay ? 3 : 0);
3107 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
3109 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
3110 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
3111 local_player->jx - MIDPOSX);
3113 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
3114 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
3115 local_player->jy - MIDPOSY);
3119 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
3120 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
3121 scroll_x = jx - MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
3123 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
3124 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
3125 scroll_y = jy - MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
3127 /* don't scroll over playfield boundaries */
3128 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
3129 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
3131 /* don't scroll over playfield boundaries */
3132 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
3133 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
3136 RedrawPlayfield(TRUE, 0,0,0,0);
3140 int scroll_xx = -999, scroll_yy = -999;
3142 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
3144 while (scroll_xx != scroll_x || scroll_yy != scroll_y)
3147 int fx = FX, fy = FY;
3149 scroll_xx = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
3150 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
3151 local_player->jx - MIDPOSX);
3153 scroll_yy = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
3154 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
3155 local_player->jy - MIDPOSY);
3157 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
3158 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
3160 if (dx == 0 && dy == 0) /* no scrolling needed at all */
3166 fx += dx * TILEX / 2;
3167 fy += dy * TILEY / 2;
3169 ScrollLevel(dx, dy);
3172 /* scroll in two steps of half tile size to make things smoother */
3173 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
3175 Delay(wait_delay_value);
3177 /* scroll second step to align at full tile size */
3179 Delay(wait_delay_value);
3184 Delay(wait_delay_value);
3188 void RelocatePlayer(int jx, int jy, int el_player_raw)
3190 int el_player = GET_PLAYER_ELEMENT(el_player_raw);
3191 int player_nr = GET_PLAYER_NR(el_player);
3192 struct PlayerInfo *player = &stored_player[player_nr];
3193 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3194 boolean no_delay = (tape.warp_forward);
3195 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
3196 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
3197 int old_jx = player->jx;
3198 int old_jy = player->jy;
3199 int old_element = Feld[old_jx][old_jy];
3200 int element = Feld[jx][jy];
3201 boolean player_relocated = (old_jx != jx || old_jy != jy);
3203 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
3204 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
3205 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
3206 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
3207 int leave_side_horiz = move_dir_horiz;
3208 int leave_side_vert = move_dir_vert;
3209 int enter_side = enter_side_horiz | enter_side_vert;
3210 int leave_side = leave_side_horiz | leave_side_vert;
3212 if (player->GameOver) /* do not reanimate dead player */
3215 if (!player_relocated) /* no need to relocate the player */
3218 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
3220 RemoveField(jx, jy); /* temporarily remove newly placed player */
3221 DrawLevelField(jx, jy);
3224 if (player->present)
3226 while (player->MovPos)
3228 ScrollPlayer(player, SCROLL_GO_ON);
3229 ScrollScreen(NULL, SCROLL_GO_ON);
3231 AdvanceFrameAndPlayerCounters(player->index_nr);
3236 Delay(wait_delay_value);
3239 DrawPlayer(player); /* needed here only to cleanup last field */
3240 DrawLevelField(player->jx, player->jy); /* remove player graphic */
3242 player->is_moving = FALSE;
3245 if (IS_CUSTOM_ELEMENT(old_element))
3246 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
3248 player->index_bit, leave_side);
3250 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
3252 player->index_bit, leave_side);
3254 Feld[jx][jy] = el_player;
3255 InitPlayerField(jx, jy, el_player, TRUE);
3257 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
3259 Feld[jx][jy] = element;
3260 InitField(jx, jy, FALSE);
3263 if (player == local_player) /* only visually relocate local player */
3264 DrawRelocatePlayer(player);
3266 TestIfPlayerTouchesBadThing(jx, jy);
3267 TestIfPlayerTouchesCustomElement(jx, jy);
3269 if (IS_CUSTOM_ELEMENT(element))
3270 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
3271 player->index_bit, enter_side);
3273 CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
3274 player->index_bit, enter_side);
3277 void Explode(int ex, int ey, int phase, int mode)
3283 /* !!! eliminate this variable !!! */
3284 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
3286 if (game.explosions_delayed)
3288 ExplodeField[ex][ey] = mode;
3292 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
3294 int center_element = Feld[ex][ey];
3295 int artwork_element, explosion_element; /* set these values later */
3298 /* --- This is only really needed (and now handled) in "Impact()". --- */
3299 /* do not explode moving elements that left the explode field in time */
3300 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
3301 center_element == EL_EMPTY &&
3302 (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
3307 /* !!! at this place, the center element may be EL_BLOCKED !!! */
3308 if (mode == EX_TYPE_NORMAL ||
3309 mode == EX_TYPE_CENTER ||
3310 mode == EX_TYPE_CROSS)
3311 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
3314 /* remove things displayed in background while burning dynamite */
3315 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
3318 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
3320 /* put moving element to center field (and let it explode there) */
3321 center_element = MovingOrBlocked2Element(ex, ey);
3322 RemoveMovingField(ex, ey);
3323 Feld[ex][ey] = center_element;
3326 /* now "center_element" is finally determined -- set related values now */
3327 artwork_element = center_element; /* for custom player artwork */
3328 explosion_element = center_element; /* for custom player artwork */
3330 if (IS_PLAYER(ex, ey))
3332 int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
3334 artwork_element = stored_player[player_nr].artwork_element;
3336 if (level.use_explosion_element[player_nr])
3338 explosion_element = level.explosion_element[player_nr];
3339 artwork_element = explosion_element;
3344 if (mode == EX_TYPE_NORMAL ||
3345 mode == EX_TYPE_CENTER ||
3346 mode == EX_TYPE_CROSS)
3347 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
3351 last_phase = element_info[explosion_element].explosion_delay + 1;
3353 last_phase = element_info[center_element].explosion_delay + 1;
3356 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
3358 int xx = x - ex + 1;
3359 int yy = y - ey + 1;
3362 if (!IN_LEV_FIELD(x, y) ||
3363 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
3364 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
3367 element = Feld[x][y];
3369 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
3371 element = MovingOrBlocked2Element(x, y);
3373 if (!IS_EXPLOSION_PROOF(element))
3374 RemoveMovingField(x, y);
3377 /* indestructible elements can only explode in center (but not flames) */
3378 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
3379 mode == EX_TYPE_BORDER)) ||
3380 element == EL_FLAMES)
3383 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
3384 behaviour, for example when touching a yamyam that explodes to rocks
3385 with active deadly shield, a rock is created under the player !!! */
3386 /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
3388 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
3389 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
3390 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
3392 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
3395 if (IS_ACTIVE_BOMB(element))
3397 /* re-activate things under the bomb like gate or penguin */
3398 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
3405 /* save walkable background elements while explosion on same tile */
3406 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
3407 (x != ex || y != ey || mode == EX_TYPE_BORDER))
3408 Back[x][y] = element;
3410 /* ignite explodable elements reached by other explosion */
3411 if (element == EL_EXPLOSION)
3412 element = Store2[x][y];
3414 if (AmoebaNr[x][y] &&
3415 (element == EL_AMOEBA_FULL ||
3416 element == EL_BD_AMOEBA ||
3417 element == EL_AMOEBA_GROWING))
3419 AmoebaCnt[AmoebaNr[x][y]]--;
3420 AmoebaCnt2[AmoebaNr[x][y]]--;
3425 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
3428 int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
3430 Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
3432 switch(StorePlayer[ex][ey])
3435 Store[x][y] = EL_PLAYER_IS_EXPLODING_2;
3438 Store[x][y] = EL_PLAYER_IS_EXPLODING_3;
3441 Store[x][y] = EL_PLAYER_IS_EXPLODING_4;
3445 Store[x][y] = EL_PLAYER_IS_EXPLODING_1;
3450 if (PLAYERINFO(ex, ey)->use_murphy)
3451 Store[x][y] = EL_EMPTY;
3454 /* !!! check this case -- currently needed for rnd_rado_negundo_v,
3455 !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
3456 else if (ELEM_IS_PLAYER(center_element))
3457 Store[x][y] = EL_EMPTY;
3458 else if (center_element == EL_YAMYAM)
3459 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
3460 else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
3461 Store[x][y] = element_info[center_element].content.e[xx][yy];
3463 /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
3464 (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
3465 otherwise) -- FIX THIS !!! */
3466 else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
3467 Store[x][y] = element_info[element].content.e[1][1];
3469 else if (!CAN_EXPLODE(element))
3470 Store[x][y] = element_info[element].content.e[1][1];
3473 Store[x][y] = EL_EMPTY;
3475 else if (center_element == EL_MOLE)
3476 Store[x][y] = EL_EMERALD_RED;
3477 else if (center_element == EL_PENGUIN)
3478 Store[x][y] = EL_EMERALD_PURPLE;
3479 else if (center_element == EL_BUG)
3480 Store[x][y] = ((x == ex && y == ey) ? EL_DIAMOND : EL_EMERALD);
3481 else if (center_element == EL_BD_BUTTERFLY)
3482 Store[x][y] = EL_BD_DIAMOND;
3483 else if (center_element == EL_SP_ELECTRON)
3484 Store[x][y] = EL_SP_INFOTRON;
3485 else if (center_element == EL_AMOEBA_TO_DIAMOND)
3486 Store[x][y] = level.amoeba_content;
3487 else if (center_element == EL_YAMYAM)
3488 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
3489 else if (IS_CUSTOM_ELEMENT(center_element) &&
3490 element_info[center_element].content.e[xx][yy] != EL_EMPTY)
3491 Store[x][y] = element_info[center_element].content.e[xx][yy];
3492 else if (element == EL_WALL_EMERALD)
3493 Store[x][y] = EL_EMERALD;
3494 else if (element == EL_WALL_DIAMOND)
3495 Store[x][y] = EL_DIAMOND;
3496 else if (element == EL_WALL_BD_DIAMOND)
3497 Store[x][y] = EL_BD_DIAMOND;
3498 else if (element == EL_WALL_EMERALD_YELLOW)
3499 Store[x][y] = EL_EMERALD_YELLOW;
3500 else if (element == EL_WALL_EMERALD_RED)
3501 Store[x][y] = EL_EMERALD_RED;
3502 else if (element == EL_WALL_EMERALD_PURPLE)
3503 Store[x][y] = EL_EMERALD_PURPLE;
3504 else if (element == EL_WALL_PEARL)
3505 Store[x][y] = EL_PEARL;
3506 else if (element == EL_WALL_CRYSTAL)
3507 Store[x][y] = EL_CRYSTAL;
3508 else if (IS_CUSTOM_ELEMENT(element) && !CAN_EXPLODE(element))
3509 Store[x][y] = element_info[element].content.e[1][1];
3511 Store[x][y] = EL_EMPTY;
3514 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
3515 center_element == EL_AMOEBA_TO_DIAMOND)
3516 Store2[x][y] = element;
3518 Feld[x][y] = EL_EXPLOSION;
3519 GfxElement[x][y] = artwork_element;
3521 ExplodePhase[x][y] = 1;
3522 ExplodeDelay[x][y] = last_phase;
3527 if (center_element == EL_YAMYAM)
3528 game.yamyam_content_nr =
3529 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
3541 GfxFrame[x][y] = 0; /* restart explosion animation */
3543 last_phase = ExplodeDelay[x][y];
3545 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
3549 /* activate this even in non-DEBUG version until cause for crash in
3550 getGraphicAnimationFrame() (see below) is found and eliminated */
3555 if (GfxElement[x][y] == EL_UNDEFINED)
3558 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
3559 printf("Explode(): This should never happen!\n");
3562 GfxElement[x][y] = EL_EMPTY;
3566 border_element = Store2[x][y];
3567 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3568 border_element = StorePlayer[x][y];
3570 if (phase == element_info[border_element].ignition_delay ||
3571 phase == last_phase)
3573 boolean border_explosion = FALSE;
3575 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
3576 !PLAYER_EXPLOSION_PROTECTED(x, y))
3578 KillPlayerUnlessExplosionProtected(x, y);
3579 border_explosion = TRUE;
3581 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
3583 Feld[x][y] = Store2[x][y];
3586 border_explosion = TRUE;
3588 else if (border_element == EL_AMOEBA_TO_DIAMOND)
3590 AmoebeUmwandeln(x, y);
3592 border_explosion = TRUE;
3595 /* if an element just explodes due to another explosion (chain-reaction),
3596 do not immediately end the new explosion when it was the last frame of
3597 the explosion (as it would be done in the following "if"-statement!) */
3598 if (border_explosion && phase == last_phase)
3602 if (phase == last_phase)
3606 element = Feld[x][y] = Store[x][y];
3607 Store[x][y] = Store2[x][y] = 0;
3608 GfxElement[x][y] = EL_UNDEFINED;
3610 /* player can escape from explosions and might therefore be still alive */
3611 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
3612 element <= EL_PLAYER_IS_EXPLODING_4)
3614 int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
3615 int explosion_element = EL_PLAYER_1 + player_nr;
3616 int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
3617 int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
3619 if (level.use_explosion_element[player_nr])
3620 explosion_element = level.explosion_element[player_nr];
3622 Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
3623 element_info[explosion_element].content.e[xx][yy]);
3626 /* restore probably existing indestructible background element */
3627 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
3628 element = Feld[x][y] = Back[x][y];
3631 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
3632 GfxDir[x][y] = MV_NONE;
3633 ChangeDelay[x][y] = 0;
3634 ChangePage[x][y] = -1;
3636 #if USE_NEW_CUSTOM_VALUE
3637 CustomValue[x][y] = 0;
3640 InitField_WithBug2(x, y, FALSE);
3642 DrawLevelField(x, y);
3644 TestIfElementTouchesCustomElement(x, y);
3646 if (GFX_CRUMBLED(element))
3647 DrawLevelFieldCrumbledSandNeighbours(x, y);
3649 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
3650 StorePlayer[x][y] = 0;
3652 if (ELEM_IS_PLAYER(element))
3653 RelocatePlayer(x, y, element);
3655 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3657 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
3658 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
3661 DrawLevelFieldCrumbledSand(x, y);
3663 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
3665 DrawLevelElement(x, y, Back[x][y]);
3666 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
3668 else if (IS_WALKABLE_UNDER(Back[x][y]))
3670 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3671 DrawLevelElementThruMask(x, y, Back[x][y]);
3673 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
3674 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3678 void DynaExplode(int ex, int ey)
3681 int dynabomb_element = Feld[ex][ey];
3682 int dynabomb_size = 1;
3683 boolean dynabomb_xl = FALSE;
3684 struct PlayerInfo *player;
3685 static int xy[4][2] =
3693 if (IS_ACTIVE_BOMB(dynabomb_element))
3695 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
3696 dynabomb_size = player->dynabomb_size;
3697 dynabomb_xl = player->dynabomb_xl;
3698 player->dynabombs_left++;
3701 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
3703 for (i = 0; i < NUM_DIRECTIONS; i++)
3705 for (j = 1; j <= dynabomb_size; j++)
3707 int x = ex + j * xy[i][0];
3708 int y = ey + j * xy[i][1];
3711 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
3714 element = Feld[x][y];
3716 /* do not restart explosions of fields with active bombs */
3717 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
3720 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
3722 if (element != EL_EMPTY && element != EL_EXPLOSION &&
3723 !IS_DIGGABLE(element) && !dynabomb_xl)
3729 void Bang(int x, int y)
3731 int element = MovingOrBlocked2Element(x, y);
3732 int explosion_type = EX_TYPE_NORMAL;
3734 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3736 struct PlayerInfo *player = PLAYERINFO(x, y);
3738 element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
3739 player->element_nr);
3741 if (level.use_explosion_element[player->index_nr])
3743 int explosion_element = level.explosion_element[player->index_nr];
3745 if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
3746 explosion_type = EX_TYPE_CROSS;
3747 else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
3748 explosion_type = EX_TYPE_CENTER;
3756 case EL_BD_BUTTERFLY:
3759 case EL_DARK_YAMYAM:
3763 RaiseScoreElement(element);
3766 case EL_DYNABOMB_PLAYER_1_ACTIVE:
3767 case EL_DYNABOMB_PLAYER_2_ACTIVE:
3768 case EL_DYNABOMB_PLAYER_3_ACTIVE:
3769 case EL_DYNABOMB_PLAYER_4_ACTIVE:
3770 case EL_DYNABOMB_INCREASE_NUMBER:
3771 case EL_DYNABOMB_INCREASE_SIZE:
3772 case EL_DYNABOMB_INCREASE_POWER:
3773 explosion_type = EX_TYPE_DYNA;
3778 case EL_LAMP_ACTIVE:
3779 case EL_AMOEBA_TO_DIAMOND:
3780 if (!IS_PLAYER(x, y)) /* penguin and player may be at same field */
3781 explosion_type = EX_TYPE_CENTER;
3785 if (element_info[element].explosion_type == EXPLODES_CROSS)
3786 explosion_type = EX_TYPE_CROSS;
3787 else if (element_info[element].explosion_type == EXPLODES_1X1)
3788 explosion_type = EX_TYPE_CENTER;
3792 if (explosion_type == EX_TYPE_DYNA)
3795 Explode(x, y, EX_PHASE_START, explosion_type);
3797 CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
3800 void SplashAcid(int x, int y)
3802 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
3803 (!IN_LEV_FIELD(x - 1, y - 2) ||
3804 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
3805 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
3807 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
3808 (!IN_LEV_FIELD(x + 1, y - 2) ||
3809 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
3810 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
3812 PlayLevelSound(x, y, SND_ACID_SPLASHING);
3815 static void InitBeltMovement()
3817 static int belt_base_element[4] =
3819 EL_CONVEYOR_BELT_1_LEFT,
3820 EL_CONVEYOR_BELT_2_LEFT,
3821 EL_CONVEYOR_BELT_3_LEFT,
3822 EL_CONVEYOR_BELT_4_LEFT
3824 static int belt_base_active_element[4] =
3826 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
3827 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
3828 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
3829 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
3834 /* set frame order for belt animation graphic according to belt direction */
3835 for (i = 0; i < NUM_BELTS; i++)
3839 for (j = 0; j < NUM_BELT_PARTS; j++)
3841 int element = belt_base_active_element[belt_nr] + j;
3842 int graphic = el2img(element);
3844 if (game.belt_dir[i] == MV_LEFT)
3845 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
3847 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
3852 SCAN_PLAYFIELD(x, y)
3854 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
3857 int element = Feld[x][y];
3859 for (i = 0; i < NUM_BELTS; i++)
3861 if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
3863 int e_belt_nr = getBeltNrFromBeltElement(element);
3866 if (e_belt_nr == belt_nr)
3868 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
3870 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
3877 static void ToggleBeltSwitch(int x, int y)
3879 static int belt_base_element[4] =
3881 EL_CONVEYOR_BELT_1_LEFT,
3882 EL_CONVEYOR_BELT_2_LEFT,
3883 EL_CONVEYOR_BELT_3_LEFT,
3884 EL_CONVEYOR_BELT_4_LEFT
3886 static int belt_base_active_element[4] =
3888 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
3889 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
3890 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
3891 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
3893 static int belt_base_switch_element[4] =
3895 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
3896 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
3897 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
3898 EL_CONVEYOR_BELT_4_SWITCH_LEFT
3900 static int belt_move_dir[4] =
3908 int element = Feld[x][y];
3909 int belt_nr = getBeltNrFromBeltSwitchElement(element);
3910 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
3911 int belt_dir = belt_move_dir[belt_dir_nr];
3914 if (!IS_BELT_SWITCH(element))
3917 game.belt_dir_nr[belt_nr] = belt_dir_nr;
3918 game.belt_dir[belt_nr] = belt_dir;
3920 if (belt_dir_nr == 3)
3923 /* set frame order for belt animation graphic according to belt direction */
3924 for (i = 0; i < NUM_BELT_PARTS; i++)
3926 int element = belt_base_active_element[belt_nr] + i;
3927 int graphic = el2img(element);
3929 if (belt_dir == MV_LEFT)
3930 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
3932 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
3936 SCAN_PLAYFIELD(xx, yy)
3938 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
3941 int element = Feld[xx][yy];
3943 if (IS_BELT_SWITCH(element))
3945 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
3947 if (e_belt_nr == belt_nr)
3949 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
3950 DrawLevelField(xx, yy);
3953 else if (IS_BELT(element) && belt_dir != MV_NONE)
3955 int e_belt_nr = getBeltNrFromBeltElement(element);
3957 if (e_belt_nr == belt_nr)
3959 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
3961 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
3962 DrawLevelField(xx, yy);
3965 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
3967 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
3969 if (e_belt_nr == belt_nr)
3971 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
3973 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
3974 DrawLevelField(xx, yy);
3980 static void ToggleSwitchgateSwitch(int x, int y)
3984 game.switchgate_pos = !game.switchgate_pos;
3987 SCAN_PLAYFIELD(xx, yy)
3989 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
3992 int element = Feld[xx][yy];
3994 if (element == EL_SWITCHGATE_SWITCH_UP ||
3995 element == EL_SWITCHGATE_SWITCH_DOWN)
3997 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
3998 DrawLevelField(xx, yy);
4000 else if (element == EL_SWITCHGATE_OPEN ||
4001 element == EL_SWITCHGATE_OPENING)
4003 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
4005 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
4007 else if (element == EL_SWITCHGATE_CLOSED ||
4008 element == EL_SWITCHGATE_CLOSING)
4010 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
4012 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
4017 static int getInvisibleActiveFromInvisibleElement(int element)
4019 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
4020 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
4021 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
4025 static int getInvisibleFromInvisibleActiveElement(int element)
4027 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
4028 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
4029 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
4033 static void RedrawAllLightSwitchesAndInvisibleElements()
4038 SCAN_PLAYFIELD(x, y)
4040 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
4043 int element = Feld[x][y];
4045 if (element == EL_LIGHT_SWITCH &&
4046 game.light_time_left > 0)
4048 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
4049 DrawLevelField(x, y);
4051 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
4052 game.light_time_left == 0)
4054 Feld[x][y] = EL_LIGHT_SWITCH;
4055 DrawLevelField(x, y);
4057 else if (element == EL_EMC_DRIPPER &&
4058 game.light_time_left > 0)
4060 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
4061 DrawLevelField(x, y);
4063 else if (element == EL_EMC_DRIPPER_ACTIVE &&
4064 game.light_time_left == 0)
4066 Feld[x][y] = EL_EMC_DRIPPER;
4067 DrawLevelField(x, y);
4069 else if (element == EL_INVISIBLE_STEELWALL ||
4070 element == EL_INVISIBLE_WALL ||
4071 element == EL_INVISIBLE_SAND)
4073 if (game.light_time_left > 0)
4074 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
4076 DrawLevelField(x, y);
4078 /* uncrumble neighbour fields, if needed */
4079 if (element == EL_INVISIBLE_SAND)
4080 DrawLevelFieldCrumbledSandNeighbours(x, y);
4082 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
4083 element == EL_INVISIBLE_WALL_ACTIVE ||
4084 element == EL_INVISIBLE_SAND_ACTIVE)
4086 if (game.light_time_left == 0)
4087 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
4089 DrawLevelField(x, y);
4091 /* re-crumble neighbour fields, if needed */
4092 if (element == EL_INVISIBLE_SAND)
4093 DrawLevelFieldCrumbledSandNeighbours(x, y);
4098 static void RedrawAllInvisibleElementsForLenses()
4103 SCAN_PLAYFIELD(x, y)
4105 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
4108 int element = Feld[x][y];
4110 if (element == EL_EMC_DRIPPER &&
4111 game.lenses_time_left > 0)
4113 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
4114 DrawLevelField(x, y);
4116 else if (element == EL_EMC_DRIPPER_ACTIVE &&
4117 game.lenses_time_left == 0)
4119 Feld[x][y] = EL_EMC_DRIPPER;
4120 DrawLevelField(x, y);
4122 else if (element == EL_INVISIBLE_STEELWALL ||
4123 element == EL_INVISIBLE_WALL ||
4124 element == EL_INVISIBLE_SAND)
4126 if (game.lenses_time_left > 0)
4127 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
4129 DrawLevelField(x, y);
4131 /* uncrumble neighbour fields, if needed */
4132 if (element == EL_INVISIBLE_SAND)
4133 DrawLevelFieldCrumbledSandNeighbours(x, y);
4135 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
4136 element == EL_INVISIBLE_WALL_ACTIVE ||
4137 element == EL_INVISIBLE_SAND_ACTIVE)
4139 if (game.lenses_time_left == 0)
4140 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
4142 DrawLevelField(x, y);
4144 /* re-crumble neighbour fields, if needed */
4145 if (element == EL_INVISIBLE_SAND)
4146 DrawLevelFieldCrumbledSandNeighbours(x, y);
4151 static void RedrawAllInvisibleElementsForMagnifier()
4156 SCAN_PLAYFIELD(x, y)
4158 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
4161 int element = Feld[x][y];
4163 if (element == EL_EMC_FAKE_GRASS &&
4164 game.magnify_time_left > 0)
4166 Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
4167 DrawLevelField(x, y);
4169 else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
4170 game.magnify_time_left == 0)
4172 Feld[x][y] = EL_EMC_FAKE_GRASS;
4173 DrawLevelField(x, y);
4175 else if (IS_GATE_GRAY(element) &&
4176 game.magnify_time_left > 0)
4178 Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
4179 element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
4180 IS_EM_GATE_GRAY(element) ?
4181 element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
4182 IS_EMC_GATE_GRAY(element) ?
4183 element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
4185 DrawLevelField(x, y);
4187 else if (IS_GATE_GRAY_ACTIVE(element) &&
4188 game.magnify_time_left == 0)
4190 Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
4191 element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
4192 IS_EM_GATE_GRAY_ACTIVE(element) ?
4193 element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
4194 IS_EMC_GATE_GRAY_ACTIVE(element) ?
4195 element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
4197 DrawLevelField(x, y);
4202 static void ToggleLightSwitch(int x, int y)
4204 int element = Feld[x][y];
4206 game.light_time_left =
4207 (element == EL_LIGHT_SWITCH ?
4208 level.time_light * FRAMES_PER_SECOND : 0);
4210 RedrawAllLightSwitchesAndInvisibleElements();
4213 static void ActivateTimegateSwitch(int x, int y)
4217 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
4220 SCAN_PLAYFIELD(xx, yy)
4222 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
4225 int element = Feld[xx][yy];
4227 if (element == EL_TIMEGATE_CLOSED ||
4228 element == EL_TIMEGATE_CLOSING)
4230 Feld[xx][yy] = EL_TIMEGATE_OPENING;
4231 PlayLevelSound(xx, yy, SND_TIMEGATE_OPENING);
4235 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
4237 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
4238 DrawLevelField(xx, yy);
4244 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
4247 void Impact(int x, int y)
4249 boolean last_line = (y == lev_fieldy - 1);
4250 boolean object_hit = FALSE;
4251 boolean impact = (last_line || object_hit);
4252 int element = Feld[x][y];
4253 int smashed = EL_STEELWALL;
4255 if (!last_line) /* check if element below was hit */
4257 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
4260 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
4261 MovDir[x][y + 1] != MV_DOWN ||
4262 MovPos[x][y + 1] <= TILEY / 2));
4264 /* do not smash moving elements that left the smashed field in time */
4265 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
4266 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
4269 #if USE_QUICKSAND_IMPACT_BUGFIX
4270 if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
4272 RemoveMovingField(x, y + 1);
4273 Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
4274 Feld[x][y + 2] = EL_ROCK;
4275 DrawLevelField(x, y + 2);
4282 smashed = MovingOrBlocked2Element(x, y + 1);
4284 impact = (last_line || object_hit);
4287 if (!last_line && smashed == EL_ACID) /* element falls into acid */
4289 SplashAcid(x, y + 1);
4293 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
4294 /* only reset graphic animation if graphic really changes after impact */
4296 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
4298 ResetGfxAnimation(x, y);
4299 DrawLevelField(x, y);
4302 if (impact && CAN_EXPLODE_IMPACT(element))
4307 else if (impact && element == EL_PEARL)
4309 ResetGfxAnimation(x, y);
4311 Feld[x][y] = EL_PEARL_BREAKING;
4312 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4315 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
4317 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4322 if (impact && element == EL_AMOEBA_DROP)
4324 if (object_hit && IS_PLAYER(x, y + 1))
4325 KillPlayerUnlessEnemyProtected(x, y + 1);
4326 else if (object_hit && smashed == EL_PENGUIN)
4330 Feld[x][y] = EL_AMOEBA_GROWING;
4331 Store[x][y] = EL_AMOEBA_WET;
4333 ResetRandomAnimationValue(x, y);
4338 if (object_hit) /* check which object was hit */
4340 if (CAN_PASS_MAGIC_WALL(element) &&
4341 (smashed == EL_MAGIC_WALL ||
4342 smashed == EL_BD_MAGIC_WALL))
4345 int activated_magic_wall =
4346 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
4347 EL_BD_MAGIC_WALL_ACTIVE);
4349 /* activate magic wall / mill */
4351 SCAN_PLAYFIELD(xx, yy)
4353 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
4355 if (Feld[xx][yy] == smashed)
4356 Feld[xx][yy] = activated_magic_wall;
4358 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
4359 game.magic_wall_active = TRUE;
4361 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
4362 SND_MAGIC_WALL_ACTIVATING :
4363 SND_BD_MAGIC_WALL_ACTIVATING));
4366 if (IS_PLAYER(x, y + 1))
4368 if (CAN_SMASH_PLAYER(element))
4370 KillPlayerUnlessEnemyProtected(x, y + 1);
4374 else if (smashed == EL_PENGUIN)
4376 if (CAN_SMASH_PLAYER(element))
4382 else if (element == EL_BD_DIAMOND)
4384 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
4390 else if (((element == EL_SP_INFOTRON ||
4391 element == EL_SP_ZONK) &&
4392 (smashed == EL_SP_SNIKSNAK ||
4393 smashed == EL_SP_ELECTRON ||
4394 smashed == EL_SP_DISK_ORANGE)) ||
4395 (element == EL_SP_INFOTRON &&
4396 smashed == EL_SP_DISK_YELLOW))
4401 else if (CAN_SMASH_EVERYTHING(element))
4403 if (IS_CLASSIC_ENEMY(smashed) ||
4404 CAN_EXPLODE_SMASHED(smashed))
4409 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
4411 if (smashed == EL_LAMP ||
4412 smashed == EL_LAMP_ACTIVE)
4417 else if (smashed == EL_NUT)
4419 Feld[x][y + 1] = EL_NUT_BREAKING;
4420 PlayLevelSound(x, y, SND_NUT_BREAKING);
4421 RaiseScoreElement(EL_NUT);
4424 else if (smashed == EL_PEARL)
4426 ResetGfxAnimation(x, y);
4428 Feld[x][y + 1] = EL_PEARL_BREAKING;
4429 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4432 else if (smashed == EL_DIAMOND)
4434 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
4435 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
4438 else if (IS_BELT_SWITCH(smashed))
4440 ToggleBeltSwitch(x, y + 1);
4442 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
4443 smashed == EL_SWITCHGATE_SWITCH_DOWN)
4445 ToggleSwitchgateSwitch(x, y + 1);
4447 else if (smashed == EL_LIGHT_SWITCH ||
4448 smashed == EL_LIGHT_SWITCH_ACTIVE)
4450 ToggleLightSwitch(x, y + 1);
4455 TestIfElementSmashesCustomElement(x, y, MV_DOWN);
4458 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4460 CheckElementChangeBySide(x, y + 1, smashed, element,
4461 CE_SWITCHED, CH_SIDE_TOP);
4462 CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
4468 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4473 /* play sound of magic wall / mill */
4475 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
4476 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
4478 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
4479 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
4480 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
4481 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
4486 /* play sound of object that hits the ground */
4487 if (last_line || object_hit)
4488 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4491 inline static void TurnRoundExt(int x, int y)
4503 { 0, 0 }, { 0, 0 }, { 0, 0 },
4508 int left, right, back;
4512 { MV_DOWN, MV_UP, MV_RIGHT },
4513 { MV_UP, MV_DOWN, MV_LEFT },
4515 { MV_LEFT, MV_RIGHT, MV_DOWN },
4519 { MV_RIGHT, MV_LEFT, MV_UP }
4522 int element = Feld[x][y];
4523 int move_pattern = element_info[element].move_pattern;
4525 int old_move_dir = MovDir[x][y];
4526 int left_dir = turn[old_move_dir].left;
4527 int right_dir = turn[old_move_dir].right;
4528 int back_dir = turn[old_move_dir].back;
4530 int left_dx = move_xy[left_dir].dx, left_dy = move_xy[left_dir].dy;
4531 int right_dx = move_xy[right_dir].dx, right_dy = move_xy[right_dir].dy;
4532 int move_dx = move_xy[old_move_dir].dx, move_dy = move_xy[old_move_dir].dy;
4533 int back_dx = move_xy[back_dir].dx, back_dy = move_xy[back_dir].dy;
4535 int left_x = x + left_dx, left_y = y + left_dy;
4536 int right_x = x + right_dx, right_y = y + right_dy;
4537 int move_x = x + move_dx, move_y = y + move_dy;
4541 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4543 TestIfBadThingTouchesOtherBadThing(x, y);
4545 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
4546 MovDir[x][y] = right_dir;
4547 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4548 MovDir[x][y] = left_dir;
4550 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
4552 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
4555 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
4557 TestIfBadThingTouchesOtherBadThing(x, y);
4559 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
4560 MovDir[x][y] = left_dir;
4561 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4562 MovDir[x][y] = right_dir;
4564 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
4566 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
4569 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4571 TestIfBadThingTouchesOtherBadThing(x, y);
4573 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
4574 MovDir[x][y] = left_dir;
4575 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
4576 MovDir[x][y] = right_dir;
4578 if (MovDir[x][y] != old_move_dir)
4581 else if (element == EL_YAMYAM)
4583 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
4584 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
4586 if (can_turn_left && can_turn_right)
4587 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4588 else if (can_turn_left)
4589 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4590 else if (can_turn_right)
4591 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4593 MovDir[x][y] = back_dir;
4595 MovDelay[x][y] = 16 + 16 * RND(3);
4597 else if (element == EL_DARK_YAMYAM)
4599 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4601 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4604 if (can_turn_left && can_turn_right)
4605 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4606 else if (can_turn_left)
4607 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4608 else if (can_turn_right)
4609 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4611 MovDir[x][y] = back_dir;
4613 MovDelay[x][y] = 16 + 16 * RND(3);
4615 else if (element == EL_PACMAN)
4617 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
4618 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
4620 if (can_turn_left && can_turn_right)
4621 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4622 else if (can_turn_left)
4623 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4624 else if (can_turn_right)
4625 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4627 MovDir[x][y] = back_dir;
4629 MovDelay[x][y] = 6 + RND(40);
4631 else if (element == EL_PIG)
4633 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
4634 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
4635 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
4636 boolean should_turn_left, should_turn_right, should_move_on;
4638 int rnd = RND(rnd_value);
4640 should_turn_left = (can_turn_left &&
4642 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
4643 y + back_dy + left_dy)));
4644 should_turn_right = (can_turn_right &&
4646 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
4647 y + back_dy + right_dy)));
4648 should_move_on = (can_move_on &&
4651 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
4652 y + move_dy + left_dy) ||
4653 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
4654 y + move_dy + right_dy)));
4656 if (should_turn_left || should_turn_right || should_move_on)
4658 if (should_turn_left && should_turn_right && should_move_on)
4659 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
4660 rnd < 2 * rnd_value / 3 ? right_dir :
4662 else if (should_turn_left && should_turn_right)
4663 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4664 else if (should_turn_left && should_move_on)
4665 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
4666 else if (should_turn_right && should_move_on)
4667 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
4668 else if (should_turn_left)
4669 MovDir[x][y] = left_dir;
4670 else if (should_turn_right)
4671 MovDir[x][y] = right_dir;
4672 else if (should_move_on)
4673 MovDir[x][y] = old_move_dir;
4675 else if (can_move_on && rnd > rnd_value / 8)
4676 MovDir[x][y] = old_move_dir;
4677 else if (can_turn_left && can_turn_right)
4678 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4679 else if (can_turn_left && rnd > rnd_value / 8)
4680 MovDir[x][y] = left_dir;
4681 else if (can_turn_right && rnd > rnd_value/8)
4682 MovDir[x][y] = right_dir;
4684 MovDir[x][y] = back_dir;
4686 xx = x + move_xy[MovDir[x][y]].dx;
4687 yy = y + move_xy[MovDir[x][y]].dy;
4689 if (!IN_LEV_FIELD(xx, yy) ||
4690 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
4691 MovDir[x][y] = old_move_dir;
4695 else if (element == EL_DRAGON)
4697 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
4698 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
4699 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
4701 int rnd = RND(rnd_value);
4703 if (can_move_on && rnd > rnd_value / 8)
4704 MovDir[x][y] = old_move_dir;
4705 else if (can_turn_left && can_turn_right)
4706 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4707 else if (can_turn_left && rnd > rnd_value / 8)
4708 MovDir[x][y] = left_dir;
4709 else if (can_turn_right && rnd > rnd_value / 8)
4710 MovDir[x][y] = right_dir;
4712 MovDir[x][y] = back_dir;
4714 xx = x + move_xy[MovDir[x][y]].dx;
4715 yy = y + move_xy[MovDir[x][y]].dy;
4717 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
4718 MovDir[x][y] = old_move_dir;
4722 else if (element == EL_MOLE)
4724 boolean can_move_on =
4725 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
4726 IS_AMOEBOID(Feld[move_x][move_y]) ||
4727 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
4730 boolean can_turn_left =
4731 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
4732 IS_AMOEBOID(Feld[left_x][left_y])));
4734 boolean can_turn_right =
4735 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
4736 IS_AMOEBOID(Feld[right_x][right_y])));
4738 if (can_turn_left && can_turn_right)
4739 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
4740 else if (can_turn_left)
4741 MovDir[x][y] = left_dir;
4743 MovDir[x][y] = right_dir;
4746 if (MovDir[x][y] != old_move_dir)
4749 else if (element == EL_BALLOON)
4751 MovDir[x][y] = game.wind_direction;
4754 else if (element == EL_SPRING)
4756 #if USE_NEW_SPRING_BUMPER
4757 if (MovDir[x][y] & MV_HORIZONTAL)
4759 if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
4760 !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
4762 Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
4763 ResetGfxAnimation(move_x, move_y);
4764 DrawLevelField(move_x, move_y);
4766 MovDir[x][y] = back_dir;
4768 else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
4769 SPRING_CAN_ENTER_FIELD(element, x, y + 1))
4770 MovDir[x][y] = MV_NONE;
4773 if (MovDir[x][y] & MV_HORIZONTAL &&
4774 (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
4775 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
4776 MovDir[x][y] = MV_NONE;
4781 else if (element == EL_ROBOT ||
4782 element == EL_SATELLITE ||
4783 element == EL_PENGUIN ||
4784 element == EL_EMC_ANDROID)
4786 int attr_x = -1, attr_y = -1;
4797 for (i = 0; i < MAX_PLAYERS; i++)
4799 struct PlayerInfo *player = &stored_player[i];
4800 int jx = player->jx, jy = player->jy;
4802 if (!player->active)
4806 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
4814 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
4815 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
4816 game.engine_version < VERSION_IDENT(3,1,0,0)))
4822 if (element == EL_PENGUIN)
4825 static int xy[4][2] =
4833 for (i = 0; i < NUM_DIRECTIONS; i++)
4835 int ex = x + xy[i][0];
4836 int ey = y + xy[i][1];
4838 if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
4847 MovDir[x][y] = MV_NONE;
4849 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
4850 else if (attr_x > x)
4851 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
4853 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
4854 else if (attr_y > y)
4855 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
4857 if (element == EL_ROBOT)
4861 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4862 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
4863 Moving2Blocked(x, y, &newx, &newy);
4865 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
4866 MovDelay[x][y] = 8 + 8 * !RND(3);
4868 MovDelay[x][y] = 16;
4870 else if (element == EL_PENGUIN)
4876 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4878 boolean first_horiz = RND(2);
4879 int new_move_dir = MovDir[x][y];
4882 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4883 Moving2Blocked(x, y, &newx, &newy);
4885 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
4889 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4890 Moving2Blocked(x, y, &newx, &newy);
4892 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
4895 MovDir[x][y] = old_move_dir;
4899 else if (element == EL_SATELLITE)
4905 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4907 boolean first_horiz = RND(2);
4908 int new_move_dir = MovDir[x][y];
4911 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4912 Moving2Blocked(x, y, &newx, &newy);
4914 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
4918 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4919 Moving2Blocked(x, y, &newx, &newy);
4921 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
4924 MovDir[x][y] = old_move_dir;
4928 else if (element == EL_EMC_ANDROID)
4930 static int check_pos[16] =
4932 -1, /* 0 => (invalid) */
4933 7, /* 1 => MV_LEFT */
4934 3, /* 2 => MV_RIGHT */
4935 -1, /* 3 => (invalid) */
4937 0, /* 5 => MV_LEFT | MV_UP */
4938 2, /* 6 => MV_RIGHT | MV_UP */
4939 -1, /* 7 => (invalid) */
4940 5, /* 8 => MV_DOWN */
4941 6, /* 9 => MV_LEFT | MV_DOWN */
4942 4, /* 10 => MV_RIGHT | MV_DOWN */
4943 -1, /* 11 => (invalid) */
4944 -1, /* 12 => (invalid) */
4945 -1, /* 13 => (invalid) */
4946 -1, /* 14 => (invalid) */
4947 -1, /* 15 => (invalid) */
4955 { -1, -1, MV_LEFT | MV_UP },
4957 { +1, -1, MV_RIGHT | MV_UP },
4958 { +1, 0, MV_RIGHT },
4959 { +1, +1, MV_RIGHT | MV_DOWN },
4961 { -1, +1, MV_LEFT | MV_DOWN },
4964 int start_pos, check_order;
4965 boolean can_clone = FALSE;
4968 /* check if there is any free field around current position */
4969 for (i = 0; i < 8; i++)
4971 int newx = x + check_xy[i].dx;
4972 int newy = y + check_xy[i].dy;
4974 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
4982 if (can_clone) /* randomly find an element to clone */
4986 start_pos = check_pos[RND(8)];
4987 check_order = (RND(2) ? -1 : +1);
4989 for (i = 0; i < 8; i++)
4991 int pos_raw = start_pos + i * check_order;
4992 int pos = (pos_raw + 8) % 8;
4993 int newx = x + check_xy[pos].dx;
4994 int newy = y + check_xy[pos].dy;
4996 if (ANDROID_CAN_CLONE_FIELD(newx, newy))
4998 element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
4999 element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
5001 Store[x][y] = Feld[newx][newy];
5010 if (can_clone) /* randomly find a direction to move */
5014 start_pos = check_pos[RND(8)];
5015 check_order = (RND(2) ? -1 : +1);
5017 for (i = 0; i < 8; i++)
5019 int pos_raw = start_pos + i * check_order;
5020 int pos = (pos_raw + 8) % 8;
5021 int newx = x + check_xy[pos].dx;
5022 int newy = y + check_xy[pos].dy;
5023 int new_move_dir = check_xy[pos].dir;
5025 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5027 MovDir[x][y] = new_move_dir;
5028 MovDelay[x][y] = level.android_clone_time * 8 + 1;
5037 if (can_clone) /* cloning and moving successful */
5040 /* cannot clone -- try to move towards player */
5042 start_pos = check_pos[MovDir[x][y] & 0x0f];
5043 check_order = (RND(2) ? -1 : +1);
5045 for (i = 0; i < 3; i++)
5047 /* first check start_pos, then previous/next or (next/previous) pos */
5048 int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
5049 int pos = (pos_raw + 8) % 8;
5050 int newx = x + check_xy[pos].dx;
5051 int newy = y + check_xy[pos].dy;
5052 int new_move_dir = check_xy[pos].dir;
5054 if (IS_PLAYER(newx, newy))
5057 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
5059 MovDir[x][y] = new_move_dir;
5060 MovDelay[x][y] = level.android_move_time * 8 + 1;
5067 else if (move_pattern == MV_TURNING_LEFT ||
5068 move_pattern == MV_TURNING_RIGHT ||
5069 move_pattern == MV_TURNING_LEFT_RIGHT ||
5070 move_pattern == MV_TURNING_RIGHT_LEFT ||
5071 move_pattern == MV_TURNING_RANDOM ||
5072 move_pattern == MV_ALL_DIRECTIONS)
5074 boolean can_turn_left =
5075 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
5076 boolean can_turn_right =
5077 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
5079 if (element_info[element].move_stepsize == 0) /* "not moving" */
5082 if (move_pattern == MV_TURNING_LEFT)
5083 MovDir[x][y] = left_dir;
5084 else if (move_pattern == MV_TURNING_RIGHT)
5085 MovDir[x][y] = right_dir;
5086 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
5087 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
5088 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
5089 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
5090 else if (move_pattern == MV_TURNING_RANDOM)
5091 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
5092 can_turn_right && !can_turn_left ? right_dir :
5093 RND(2) ? left_dir : right_dir);
5094 else if (can_turn_left && can_turn_right)
5095 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5096 else if (can_turn_left)
5097 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5098 else if (can_turn_right)
5099 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5101 MovDir[x][y] = back_dir;
5103 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5105 else if (move_pattern == MV_HORIZONTAL ||
5106 move_pattern == MV_VERTICAL)
5108 if (move_pattern & old_move_dir)
5109 MovDir[x][y] = back_dir;
5110 else if (move_pattern == MV_HORIZONTAL)
5111 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
5112 else if (move_pattern == MV_VERTICAL)
5113 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
5115 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5117 else if (move_pattern & MV_ANY_DIRECTION)
5119 MovDir[x][y] = move_pattern;
5120 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5122 else if (move_pattern & MV_WIND_DIRECTION)
5124 MovDir[x][y] = game.wind_direction;
5125 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5127 else if (move_pattern == MV_ALONG_LEFT_SIDE)
5129 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
5130 MovDir[x][y] = left_dir;
5131 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5132 MovDir[x][y] = right_dir;
5134 if (MovDir[x][y] != old_move_dir)
5135 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5137 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
5139 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
5140 MovDir[x][y] = right_dir;
5141 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5142 MovDir[x][y] = left_dir;
5144 if (MovDir[x][y] != old_move_dir)
5145 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5147 else if (move_pattern == MV_TOWARDS_PLAYER ||
5148 move_pattern == MV_AWAY_FROM_PLAYER)
5150 int attr_x = -1, attr_y = -1;
5152 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
5163 for (i = 0; i < MAX_PLAYERS; i++)
5165 struct PlayerInfo *player = &stored_player[i];
5166 int jx = player->jx, jy = player->jy;
5168 if (!player->active)
5172 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
5180 MovDir[x][y] = MV_NONE;
5182 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
5183 else if (attr_x > x)
5184 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
5186 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
5187 else if (attr_y > y)
5188 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
5190 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5192 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5194 boolean first_horiz = RND(2);
5195 int new_move_dir = MovDir[x][y];
5197 if (element_info[element].move_stepsize == 0) /* "not moving" */
5199 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
5200 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5206 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5207 Moving2Blocked(x, y, &newx, &newy);
5209 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5213 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5214 Moving2Blocked(x, y, &newx, &newy);
5216 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5219 MovDir[x][y] = old_move_dir;
5222 else if (move_pattern == MV_WHEN_PUSHED ||
5223 move_pattern == MV_WHEN_DROPPED)
5225 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5226 MovDir[x][y] = MV_NONE;
5230 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
5232 static int test_xy[7][2] =
5242 static int test_dir[7] =
5252 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
5253 int move_preference = -1000000; /* start with very low preference */
5254 int new_move_dir = MV_NONE;
5255 int start_test = RND(4);
5258 for (i = 0; i < NUM_DIRECTIONS; i++)
5260 int move_dir = test_dir[start_test + i];
5261 int move_dir_preference;
5263 xx = x + test_xy[start_test + i][0];
5264 yy = y + test_xy[start_test + i][1];
5266 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
5267 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
5269 new_move_dir = move_dir;
5274 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
5277 move_dir_preference = -1 * RunnerVisit[xx][yy];
5278 if (hunter_mode && PlayerVisit[xx][yy] > 0)
5279 move_dir_preference = PlayerVisit[xx][yy];
5281 if (move_dir_preference > move_preference)
5283 /* prefer field that has not been visited for the longest time */
5284 move_preference = move_dir_preference;
5285 new_move_dir = move_dir;
5287 else if (move_dir_preference == move_preference &&
5288 move_dir == old_move_dir)
5290 /* prefer last direction when all directions are preferred equally */
5291 move_preference = move_dir_preference;
5292 new_move_dir = move_dir;
5296 MovDir[x][y] = new_move_dir;
5297 if (old_move_dir != new_move_dir)
5298 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5302 static void TurnRound(int x, int y)
5304 int direction = MovDir[x][y];
5306 int element, graphic;
5311 GfxDir[x][y] = MovDir[x][y];
5313 if (direction != MovDir[x][y])
5317 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
5320 element = Feld[x][y];
5321 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5323 if (graphic_info[graphic].anim_global_sync)
5324 GfxFrame[x][y] = FrameCounter;
5325 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5326 GfxFrame[x][y] = CustomValue[x][y];
5327 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5328 GfxFrame[x][y] = element_info[element].collect_score;
5332 static boolean JustBeingPushed(int x, int y)
5336 for (i = 0; i < MAX_PLAYERS; i++)
5338 struct PlayerInfo *player = &stored_player[i];
5340 if (player->active && player->is_pushing && player->MovPos)
5342 int next_jx = player->jx + (player->jx - player->last_jx);
5343 int next_jy = player->jy + (player->jy - player->last_jy);
5345 if (x == next_jx && y == next_jy)
5353 void StartMoving(int x, int y)
5355 boolean started_moving = FALSE; /* some elements can fall _and_ move */
5356 int element = Feld[x][y];
5361 if (MovDelay[x][y] == 0)
5362 GfxAction[x][y] = ACTION_DEFAULT;
5364 if (CAN_FALL(element) && y < lev_fieldy - 1)
5366 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
5367 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
5368 if (JustBeingPushed(x, y))
5371 if (element == EL_QUICKSAND_FULL)
5373 if (IS_FREE(x, y + 1))
5375 InitMovingField(x, y, MV_DOWN);
5376 started_moving = TRUE;
5378 Feld[x][y] = EL_QUICKSAND_EMPTYING;
5379 Store[x][y] = EL_ROCK;
5381 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
5383 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5385 if (!MovDelay[x][y])
5386 MovDelay[x][y] = TILEY + 1;
5395 Feld[x][y] = EL_QUICKSAND_EMPTY;
5396 Feld[x][y + 1] = EL_QUICKSAND_FULL;
5397 Store[x][y + 1] = Store[x][y];
5400 PlayLevelSoundAction(x, y, ACTION_FILLING);
5403 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
5404 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5406 InitMovingField(x, y, MV_DOWN);
5407 started_moving = TRUE;
5409 Feld[x][y] = EL_QUICKSAND_FILLING;
5410 Store[x][y] = element;
5412 PlayLevelSoundAction(x, y, ACTION_FILLING);
5414 else if (element == EL_MAGIC_WALL_FULL)
5416 if (IS_FREE(x, y + 1))
5418 InitMovingField(x, y, MV_DOWN);
5419 started_moving = TRUE;
5421 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
5422 Store[x][y] = EL_CHANGED(Store[x][y]);
5424 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
5426 if (!MovDelay[x][y])
5427 MovDelay[x][y] = TILEY/4 + 1;
5436 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
5437 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
5438 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
5442 else if (element == EL_BD_MAGIC_WALL_FULL)
5444 if (IS_FREE(x, y + 1))
5446 InitMovingField(x, y, MV_DOWN);
5447 started_moving = TRUE;
5449 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
5450 Store[x][y] = EL_CHANGED2(Store[x][y]);
5452 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
5454 if (!MovDelay[x][y])
5455 MovDelay[x][y] = TILEY/4 + 1;
5464 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
5465 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
5466 Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
5470 else if (CAN_PASS_MAGIC_WALL(element) &&
5471 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
5472 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
5474 InitMovingField(x, y, MV_DOWN);
5475 started_moving = TRUE;
5478 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
5479 EL_BD_MAGIC_WALL_FILLING);
5480 Store[x][y] = element;
5482 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
5484 SplashAcid(x, y + 1);
5486 InitMovingField(x, y, MV_DOWN);
5487 started_moving = TRUE;
5489 Store[x][y] = EL_ACID;
5491 else if ((game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5492 CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
5494 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
5495 CAN_FALL(element) && WasJustFalling[x][y] &&
5496 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
5498 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
5499 CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
5500 (Feld[x][y + 1] == EL_BLOCKED)))
5502 /* this is needed for a special case not covered by calling "Impact()"
5503 from "ContinueMoving()": if an element moves to a tile directly below
5504 another element which was just falling on that tile (which was empty
5505 in the previous frame), the falling element above would just stop
5506 instead of smashing the element below (in previous version, the above
5507 element was just checked for "moving" instead of "falling", resulting
5508 in incorrect smashes caused by horizontal movement of the above
5509 element; also, the case of the player being the element to smash was
5510 simply not covered here... :-/ ) */
5512 CheckCollision[x][y] = 0;
5516 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
5518 if (MovDir[x][y] == MV_NONE)
5520 InitMovingField(x, y, MV_DOWN);
5521 started_moving = TRUE;
5524 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
5526 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
5527 MovDir[x][y] = MV_DOWN;
5529 InitMovingField(x, y, MV_DOWN);
5530 started_moving = TRUE;
5532 else if (element == EL_AMOEBA_DROP)
5534 Feld[x][y] = EL_AMOEBA_GROWING;
5535 Store[x][y] = EL_AMOEBA_WET;
5537 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
5538 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
5539 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
5540 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
5542 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
5543 (IS_FREE(x - 1, y + 1) ||
5544 Feld[x - 1][y + 1] == EL_ACID));
5545 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
5546 (IS_FREE(x + 1, y + 1) ||
5547 Feld[x + 1][y + 1] == EL_ACID));
5548 boolean can_fall_any = (can_fall_left || can_fall_right);
5549 boolean can_fall_both = (can_fall_left && can_fall_right);
5550 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
5552 #if USE_NEW_ALL_SLIPPERY
5553 if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
5555 if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
5556 can_fall_right = FALSE;
5557 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
5558 can_fall_left = FALSE;
5559 else if (slippery_type == SLIPPERY_ONLY_LEFT)
5560 can_fall_right = FALSE;
5561 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
5562 can_fall_left = FALSE;
5564 can_fall_any = (can_fall_left || can_fall_right);
5565 can_fall_both = FALSE;
5568 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
5570 if (slippery_type == SLIPPERY_ONLY_LEFT)
5571 can_fall_right = FALSE;
5572 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
5573 can_fall_left = FALSE;
5574 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
5575 can_fall_right = FALSE;
5576 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
5577 can_fall_left = FALSE;
5579 can_fall_any = (can_fall_left || can_fall_right);
5580 can_fall_both = (can_fall_left && can_fall_right);
5584 #if USE_NEW_ALL_SLIPPERY
5586 #if USE_NEW_SP_SLIPPERY
5587 /* !!! better use the same properties as for custom elements here !!! */
5588 else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
5589 can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
5591 can_fall_right = FALSE; /* slip down on left side */
5592 can_fall_both = FALSE;
5597 #if USE_NEW_ALL_SLIPPERY
5600 if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
5601 can_fall_right = FALSE; /* slip down on left side */
5603 can_fall_left = !(can_fall_right = RND(2));
5605 can_fall_both = FALSE;
5610 if (game.emulation == EMU_BOULDERDASH ||
5611 element == EL_BD_ROCK || element == EL_BD_DIAMOND)
5612 can_fall_right = FALSE; /* slip down on left side */
5614 can_fall_left = !(can_fall_right = RND(2));
5616 can_fall_both = FALSE;
5622 /* if not determined otherwise, prefer left side for slipping down */
5623 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
5624 started_moving = TRUE;
5628 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
5630 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
5633 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
5634 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
5635 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
5636 int belt_dir = game.belt_dir[belt_nr];
5638 if ((belt_dir == MV_LEFT && left_is_free) ||
5639 (belt_dir == MV_RIGHT && right_is_free))
5641 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
5643 InitMovingField(x, y, belt_dir);
5644 started_moving = TRUE;
5646 Pushed[x][y] = TRUE;
5647 Pushed[nextx][y] = TRUE;
5649 GfxAction[x][y] = ACTION_DEFAULT;
5653 MovDir[x][y] = 0; /* if element was moving, stop it */
5658 /* not "else if" because of elements that can fall and move (EL_SPRING) */
5660 if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
5662 if (CAN_MOVE(element) && !started_moving)
5665 int move_pattern = element_info[element].move_pattern;
5670 if (MovDir[x][y] == MV_NONE)
5672 printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
5673 x, y, element, element_info[element].token_name);
5674 printf("StartMoving(): This should never happen!\n");
5679 Moving2Blocked(x, y, &newx, &newy);
5681 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
5684 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5685 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5687 WasJustMoving[x][y] = 0;
5688 CheckCollision[x][y] = 0;
5690 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
5692 if (Feld[x][y] != element) /* element has changed */
5696 if (!MovDelay[x][y]) /* start new movement phase */
5698 /* all objects that can change their move direction after each step
5699 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
5701 if (element != EL_YAMYAM &&
5702 element != EL_DARK_YAMYAM &&
5703 element != EL_PACMAN &&
5704 !(move_pattern & MV_ANY_DIRECTION) &&
5705 move_pattern != MV_TURNING_LEFT &&
5706 move_pattern != MV_TURNING_RIGHT &&
5707 move_pattern != MV_TURNING_LEFT_RIGHT &&
5708 move_pattern != MV_TURNING_RIGHT_LEFT &&
5709 move_pattern != MV_TURNING_RANDOM)
5713 if (MovDelay[x][y] && (element == EL_BUG ||
5714 element == EL_SPACESHIP ||
5715 element == EL_SP_SNIKSNAK ||
5716 element == EL_SP_ELECTRON ||
5717 element == EL_MOLE))
5718 DrawLevelField(x, y);
5722 if (MovDelay[x][y]) /* wait some time before next movement */
5726 if (element == EL_ROBOT ||
5727 element == EL_YAMYAM ||
5728 element == EL_DARK_YAMYAM)
5730 DrawLevelElementAnimationIfNeeded(x, y, element);
5731 PlayLevelSoundAction(x, y, ACTION_WAITING);
5733 else if (element == EL_SP_ELECTRON)
5734 DrawLevelElementAnimationIfNeeded(x, y, element);
5735 else if (element == EL_DRAGON)
5738 int dir = MovDir[x][y];
5739 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
5740 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
5741 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
5742 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
5743 dir == MV_UP ? IMG_FLAMES_1_UP :
5744 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
5745 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5747 GfxAction[x][y] = ACTION_ATTACKING;
5749 if (IS_PLAYER(x, y))
5750 DrawPlayerField(x, y);
5752 DrawLevelField(x, y);
5754 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
5756 for (i = 1; i <= 3; i++)
5758 int xx = x + i * dx;
5759 int yy = y + i * dy;
5760 int sx = SCREENX(xx);
5761 int sy = SCREENY(yy);
5762 int flame_graphic = graphic + (i - 1);
5764 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
5769 int flamed = MovingOrBlocked2Element(xx, yy);
5773 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
5775 else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
5776 RemoveMovingField(xx, yy);
5778 RemoveField(xx, yy);
5780 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
5783 RemoveMovingField(xx, yy);
5786 ChangeDelay[xx][yy] = 0;
5788 Feld[xx][yy] = EL_FLAMES;
5790 if (IN_SCR_FIELD(sx, sy))
5792 DrawLevelFieldCrumbledSand(xx, yy);
5793 DrawGraphic(sx, sy, flame_graphic, frame);
5798 if (Feld[xx][yy] == EL_FLAMES)
5799 Feld[xx][yy] = EL_EMPTY;
5800 DrawLevelField(xx, yy);
5805 if (MovDelay[x][y]) /* element still has to wait some time */
5807 PlayLevelSoundAction(x, y, ACTION_WAITING);
5813 /* now make next step */
5815 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
5817 if (DONT_COLLIDE_WITH(element) &&
5818 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
5819 !PLAYER_ENEMY_PROTECTED(newx, newy))
5821 TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
5826 else if (CAN_MOVE_INTO_ACID(element) &&
5827 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
5828 !IS_MV_DIAGONAL(MovDir[x][y]) &&
5829 (MovDir[x][y] == MV_DOWN ||
5830 game.engine_version >= VERSION_IDENT(3,1,0,0)))
5832 SplashAcid(newx, newy);
5833 Store[x][y] = EL_ACID;
5835 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
5837 if (Feld[newx][newy] == EL_EXIT_OPEN)
5840 DrawLevelField(x, y);
5842 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
5843 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
5844 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
5846 local_player->friends_still_needed--;
5847 if (!local_player->friends_still_needed &&
5848 !local_player->GameOver && AllPlayersGone)
5849 local_player->LevelSolved = local_player->GameOver = TRUE;
5853 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
5855 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
5856 DrawLevelField(newx, newy);
5858 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
5860 else if (!IS_FREE(newx, newy))
5862 GfxAction[x][y] = ACTION_WAITING;
5864 if (IS_PLAYER(x, y))
5865 DrawPlayerField(x, y);
5867 DrawLevelField(x, y);
5872 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
5874 if (IS_FOOD_PIG(Feld[newx][newy]))
5876 if (IS_MOVING(newx, newy))
5877 RemoveMovingField(newx, newy);
5880 Feld[newx][newy] = EL_EMPTY;
5881 DrawLevelField(newx, newy);
5884 PlayLevelSound(x, y, SND_PIG_DIGGING);
5886 else if (!IS_FREE(newx, newy))
5888 if (IS_PLAYER(x, y))
5889 DrawPlayerField(x, y);
5891 DrawLevelField(x, y);
5896 else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
5898 if (Store[x][y] != EL_EMPTY)
5900 boolean can_clone = FALSE;
5903 /* check if element to clone is still there */
5904 for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
5906 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
5914 /* cannot clone or target field not free anymore -- do not clone */
5915 if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
5916 Store[x][y] = EL_EMPTY;
5919 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
5921 if (IS_MV_DIAGONAL(MovDir[x][y]))
5923 int diagonal_move_dir = MovDir[x][y];
5924 int stored = Store[x][y];
5925 int change_delay = 8;
5928 /* android is moving diagonally */
5930 CreateField(x, y, EL_DIAGONAL_SHRINKING);
5932 Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
5933 GfxElement[x][y] = EL_EMC_ANDROID;
5934 GfxAction[x][y] = ACTION_SHRINKING;
5935 GfxDir[x][y] = diagonal_move_dir;
5936 ChangeDelay[x][y] = change_delay;
5938 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
5941 DrawLevelGraphicAnimation(x, y, graphic);
5942 PlayLevelSoundAction(x, y, ACTION_SHRINKING);
5944 if (Feld[newx][newy] == EL_ACID)
5946 SplashAcid(newx, newy);
5951 CreateField(newx, newy, EL_DIAGONAL_GROWING);
5953 Store[newx][newy] = EL_EMC_ANDROID;
5954 GfxElement[newx][newy] = EL_EMC_ANDROID;
5955 GfxAction[newx][newy] = ACTION_GROWING;
5956 GfxDir[newx][newy] = diagonal_move_dir;
5957 ChangeDelay[newx][newy] = change_delay;
5959 graphic = el_act_dir2img(GfxElement[newx][newy],
5960 GfxAction[newx][newy], GfxDir[newx][newy]);
5962 DrawLevelGraphicAnimation(newx, newy, graphic);
5963 PlayLevelSoundAction(newx, newy, ACTION_GROWING);
5969 Feld[newx][newy] = EL_EMPTY;
5970 DrawLevelField(newx, newy);
5972 PlayLevelSoundAction(x, y, ACTION_DIGGING);
5975 else if (!IS_FREE(newx, newy))
5978 if (IS_PLAYER(x, y))
5979 DrawPlayerField(x, y);
5981 DrawLevelField(x, y);
5987 else if (IS_CUSTOM_ELEMENT(element) &&
5988 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5990 int new_element = Feld[newx][newy];
5992 if (!IS_FREE(newx, newy))
5994 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
5995 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
5998 /* no element can dig solid indestructible elements */
5999 if (IS_INDESTRUCTIBLE(new_element) &&
6000 !IS_DIGGABLE(new_element) &&
6001 !IS_COLLECTIBLE(new_element))
6004 if (AmoebaNr[newx][newy] &&
6005 (new_element == EL_AMOEBA_FULL ||
6006 new_element == EL_BD_AMOEBA ||
6007 new_element == EL_AMOEBA_GROWING))
6009 AmoebaCnt[AmoebaNr[newx][newy]]--;
6010 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6013 if (IS_MOVING(newx, newy))
6014 RemoveMovingField(newx, newy);
6017 RemoveField(newx, newy);
6018 DrawLevelField(newx, newy);
6021 /* if digged element was about to explode, prevent the explosion */
6022 ExplodeField[newx][newy] = EX_TYPE_NONE;
6024 PlayLevelSoundAction(x, y, action);
6027 Store[newx][newy] = EL_EMPTY;
6029 /* this makes it possible to leave the removed element again */
6030 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
6031 Store[newx][newy] = new_element;
6033 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
6035 int move_leave_element = element_info[element].move_leave_element;
6037 /* this makes it possible to leave the removed element again */
6038 Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
6039 new_element : move_leave_element);
6043 if (move_pattern & MV_MAZE_RUNNER_STYLE)
6045 RunnerVisit[x][y] = FrameCounter;
6046 PlayerVisit[x][y] /= 8; /* expire player visit path */
6049 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
6051 if (!IS_FREE(newx, newy))
6053 if (IS_PLAYER(x, y))
6054 DrawPlayerField(x, y);
6056 DrawLevelField(x, y);
6062 boolean wanna_flame = !RND(10);
6063 int dx = newx - x, dy = newy - y;
6064 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
6065 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
6066 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
6067 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
6068 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
6069 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
6072 IS_CLASSIC_ENEMY(element1) ||
6073 IS_CLASSIC_ENEMY(element2)) &&
6074 element1 != EL_DRAGON && element2 != EL_DRAGON &&
6075 element1 != EL_FLAMES && element2 != EL_FLAMES)
6077 ResetGfxAnimation(x, y);
6078 GfxAction[x][y] = ACTION_ATTACKING;
6080 if (IS_PLAYER(x, y))
6081 DrawPlayerField(x, y);
6083 DrawLevelField(x, y);
6085 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
6087 MovDelay[x][y] = 50;
6091 RemoveField(newx, newy);
6093 Feld[newx][newy] = EL_FLAMES;
6094 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
6097 RemoveField(newx1, newy1);
6099 Feld[newx1][newy1] = EL_FLAMES;
6101 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
6104 RemoveField(newx2, newy2);
6106 Feld[newx2][newy2] = EL_FLAMES;
6113 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6114 Feld[newx][newy] == EL_DIAMOND)
6116 if (IS_MOVING(newx, newy))
6117 RemoveMovingField(newx, newy);
6120 Feld[newx][newy] = EL_EMPTY;
6121 DrawLevelField(newx, newy);
6124 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
6126 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6127 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
6129 if (AmoebaNr[newx][newy])
6131 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6132 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
6133 Feld[newx][newy] == EL_BD_AMOEBA)
6134 AmoebaCnt[AmoebaNr[newx][newy]]--;
6139 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
6141 RemoveMovingField(newx, newy);
6144 if (IS_MOVING(newx, newy))
6146 RemoveMovingField(newx, newy);
6151 Feld[newx][newy] = EL_EMPTY;
6152 DrawLevelField(newx, newy);
6155 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
6157 else if ((element == EL_PACMAN || element == EL_MOLE)
6158 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
6160 if (AmoebaNr[newx][newy])
6162 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6163 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
6164 Feld[newx][newy] == EL_BD_AMOEBA)
6165 AmoebaCnt[AmoebaNr[newx][newy]]--;
6168 if (element == EL_MOLE)
6170 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
6171 PlayLevelSound(x, y, SND_MOLE_DIGGING);
6173 ResetGfxAnimation(x, y);
6174 GfxAction[x][y] = ACTION_DIGGING;
6175 DrawLevelField(x, y);
6177 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
6179 return; /* wait for shrinking amoeba */
6181 else /* element == EL_PACMAN */
6183 Feld[newx][newy] = EL_EMPTY;
6184 DrawLevelField(newx, newy);
6185 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
6188 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
6189 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
6190 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
6192 /* wait for shrinking amoeba to completely disappear */
6195 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
6197 /* object was running against a wall */
6202 /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
6203 if (move_pattern & MV_ANY_DIRECTION &&
6204 move_pattern == MovDir[x][y])
6206 int blocking_element =
6207 (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
6209 CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
6212 element = Feld[x][y]; /* element might have changed */
6216 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
6217 DrawLevelElementAnimation(x, y, element);
6219 if (DONT_TOUCH(element))
6220 TestIfBadThingTouchesPlayer(x, y);
6225 InitMovingField(x, y, MovDir[x][y]);
6227 PlayLevelSoundAction(x, y, ACTION_MOVING);
6231 ContinueMoving(x, y);
6234 void ContinueMoving(int x, int y)
6236 int element = Feld[x][y];
6237 struct ElementInfo *ei = &element_info[element];
6238 int direction = MovDir[x][y];
6239 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
6240 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
6241 int newx = x + dx, newy = y + dy;
6242 int stored = Store[x][y];
6243 int stored_new = Store[newx][newy];
6244 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
6245 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
6246 boolean last_line = (newy == lev_fieldy - 1);
6248 MovPos[x][y] += getElementMoveStepsize(x, y);
6250 if (pushed_by_player) /* special case: moving object pushed by player */
6251 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
6253 if (ABS(MovPos[x][y]) < TILEX)
6255 DrawLevelField(x, y);
6257 return; /* element is still moving */
6260 /* element reached destination field */
6262 Feld[x][y] = EL_EMPTY;
6263 Feld[newx][newy] = element;
6264 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
6266 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
6268 element = Feld[newx][newy] = EL_ACID;
6270 else if (element == EL_MOLE)
6272 Feld[x][y] = EL_SAND;
6274 DrawLevelFieldCrumbledSandNeighbours(x, y);
6276 else if (element == EL_QUICKSAND_FILLING)
6278 element = Feld[newx][newy] = get_next_element(element);
6279 Store[newx][newy] = Store[x][y];
6281 else if (element == EL_QUICKSAND_EMPTYING)
6283 Feld[x][y] = get_next_element(element);
6284 element = Feld[newx][newy] = Store[x][y];
6286 else if (element == EL_MAGIC_WALL_FILLING)
6288 element = Feld[newx][newy] = get_next_element(element);
6289 if (!game.magic_wall_active)
6290 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
6291 Store[newx][newy] = Store[x][y];
6293 else if (element == EL_MAGIC_WALL_EMPTYING)
6295 Feld[x][y] = get_next_element(element);
6296 if (!game.magic_wall_active)
6297 Feld[x][y] = EL_MAGIC_WALL_DEAD;
6298 element = Feld[newx][newy] = Store[x][y];
6300 #if USE_NEW_CUSTOM_VALUE
6301 InitField(newx, newy, FALSE);
6304 else if (element == EL_BD_MAGIC_WALL_FILLING)
6306 element = Feld[newx][newy] = get_next_element(element);
6307 if (!game.magic_wall_active)
6308 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
6309 Store[newx][newy] = Store[x][y];
6311 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
6313 Feld[x][y] = get_next_element(element);
6314 if (!game.magic_wall_active)
6315 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
6316 element = Feld[newx][newy] = Store[x][y];
6318 #if USE_NEW_CUSTOM_VALUE
6319 InitField(newx, newy, FALSE);
6322 else if (element == EL_AMOEBA_DROPPING)
6324 Feld[x][y] = get_next_element(element);
6325 element = Feld[newx][newy] = Store[x][y];
6327 else if (element == EL_SOKOBAN_OBJECT)
6330 Feld[x][y] = Back[x][y];
6332 if (Back[newx][newy])
6333 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
6335 Back[x][y] = Back[newx][newy] = 0;
6338 Store[x][y] = EL_EMPTY;
6343 MovDelay[newx][newy] = 0;
6346 if (CAN_CHANGE_OR_HAS_ACTION(element))
6348 if (CAN_CHANGE(element))
6351 /* copy element change control values to new field */
6352 ChangeDelay[newx][newy] = ChangeDelay[x][y];
6353 ChangePage[newx][newy] = ChangePage[x][y];
6354 ChangeCount[newx][newy] = ChangeCount[x][y];
6355 ChangeEvent[newx][newy] = ChangeEvent[x][y];
6358 #if USE_NEW_CUSTOM_VALUE
6359 CustomValue[newx][newy] = CustomValue[x][y];
6365 #if USE_NEW_CUSTOM_VALUE
6366 CustomValue[newx][newy] = CustomValue[x][y];
6370 ChangeDelay[x][y] = 0;
6371 ChangePage[x][y] = -1;
6372 ChangeCount[x][y] = 0;
6373 ChangeEvent[x][y] = -1;
6375 #if USE_NEW_CUSTOM_VALUE
6376 CustomValue[x][y] = 0;
6379 /* copy animation control values to new field */
6380 GfxFrame[newx][newy] = GfxFrame[x][y];
6381 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
6382 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
6383 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
6385 Pushed[x][y] = Pushed[newx][newy] = FALSE;
6387 /* some elements can leave other elements behind after moving */
6389 if (ei->move_leave_element != EL_EMPTY &&
6390 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
6391 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
6393 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
6394 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
6395 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
6398 int move_leave_element = ei->move_leave_element;
6402 /* this makes it possible to leave the removed element again */
6403 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
6404 move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
6406 /* this makes it possible to leave the removed element again */
6407 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
6408 move_leave_element = stored;
6411 /* this makes it possible to leave the removed element again */
6412 if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
6413 ei->move_leave_element == EL_TRIGGER_ELEMENT)
6414 move_leave_element = stored;
6417 Feld[x][y] = move_leave_element;
6419 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
6420 MovDir[x][y] = direction;
6422 InitField(x, y, FALSE);
6424 if (GFX_CRUMBLED(Feld[x][y]))
6425 DrawLevelFieldCrumbledSandNeighbours(x, y);
6427 if (ELEM_IS_PLAYER(move_leave_element))
6428 RelocatePlayer(x, y, move_leave_element);
6431 /* do this after checking for left-behind element */
6432 ResetGfxAnimation(x, y); /* reset animation values for old field */
6434 if (!CAN_MOVE(element) ||
6435 (CAN_FALL(element) && direction == MV_DOWN &&
6436 (element == EL_SPRING ||
6437 element_info[element].move_pattern == MV_WHEN_PUSHED ||
6438 element_info[element].move_pattern == MV_WHEN_DROPPED)))
6439 GfxDir[x][y] = MovDir[newx][newy] = 0;
6441 DrawLevelField(x, y);
6442 DrawLevelField(newx, newy);
6444 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
6446 /* prevent pushed element from moving on in pushed direction */
6447 if (pushed_by_player && CAN_MOVE(element) &&
6448 element_info[element].move_pattern & MV_ANY_DIRECTION &&
6449 !(element_info[element].move_pattern & direction))
6450 TurnRound(newx, newy);
6452 /* prevent elements on conveyor belt from moving on in last direction */
6453 if (pushed_by_conveyor && CAN_FALL(element) &&
6454 direction & MV_HORIZONTAL)
6455 MovDir[newx][newy] = 0;
6457 if (!pushed_by_player)
6459 int nextx = newx + dx, nexty = newy + dy;
6460 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
6462 WasJustMoving[newx][newy] = 3;
6464 if (CAN_FALL(element) && direction == MV_DOWN)
6465 WasJustFalling[newx][newy] = 3;
6467 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
6468 CheckCollision[newx][newy] = 2;
6471 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
6473 TestIfBadThingTouchesPlayer(newx, newy);
6474 TestIfBadThingTouchesFriend(newx, newy);
6476 if (!IS_CUSTOM_ELEMENT(element))
6477 TestIfBadThingTouchesOtherBadThing(newx, newy);
6479 else if (element == EL_PENGUIN)
6480 TestIfFriendTouchesBadThing(newx, newy);
6482 /* give the player one last chance (one more frame) to move away */
6483 if (CAN_FALL(element) && direction == MV_DOWN &&
6484 (last_line || (!IS_FREE(x, newy + 1) &&
6485 (!IS_PLAYER(x, newy + 1) ||
6486 game.engine_version < VERSION_IDENT(3,1,1,0)))))
6489 if (pushed_by_player && !game.use_change_when_pushing_bug)
6491 int push_side = MV_DIR_OPPOSITE(direction);
6492 struct PlayerInfo *player = PLAYERINFO(x, y);
6494 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
6495 player->index_bit, push_side);
6496 CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
6497 player->index_bit, push_side);
6500 if (element == EL_EMC_ANDROID && pushed_by_player) /* make another move */
6501 MovDelay[newx][newy] = 1;
6503 CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
6505 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
6508 if (ChangePage[newx][newy] != -1) /* delayed change */
6510 int page = ChangePage[newx][newy];
6511 struct ElementChangeInfo *change = &ei->change_page[page];
6513 ChangePage[newx][newy] = -1;
6515 if (change->can_change)
6517 if (ChangeElement(newx, newy, element, page))
6519 if (change->post_change_function)
6520 change->post_change_function(newx, newy);
6524 if (change->has_action)
6525 ExecuteCustomElementAction(newx, newy, element, page);
6529 TestIfElementHitsCustomElement(newx, newy, direction);
6530 TestIfPlayerTouchesCustomElement(newx, newy);
6531 TestIfElementTouchesCustomElement(newx, newy);
6534 if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
6535 IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
6536 CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
6537 MV_DIR_OPPOSITE(direction));
6541 int AmoebeNachbarNr(int ax, int ay)
6544 int element = Feld[ax][ay];
6546 static int xy[4][2] =
6554 for (i = 0; i < NUM_DIRECTIONS; i++)
6556 int x = ax + xy[i][0];
6557 int y = ay + xy[i][1];
6559 if (!IN_LEV_FIELD(x, y))
6562 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
6563 group_nr = AmoebaNr[x][y];
6569 void AmoebenVereinigen(int ax, int ay)
6571 int i, x, y, xx, yy;
6572 int new_group_nr = AmoebaNr[ax][ay];
6573 static int xy[4][2] =
6581 if (new_group_nr == 0)
6584 for (i = 0; i < NUM_DIRECTIONS; i++)
6589 if (!IN_LEV_FIELD(x, y))
6592 if ((Feld[x][y] == EL_AMOEBA_FULL ||
6593 Feld[x][y] == EL_BD_AMOEBA ||
6594 Feld[x][y] == EL_AMOEBA_DEAD) &&
6595 AmoebaNr[x][y] != new_group_nr)
6597 int old_group_nr = AmoebaNr[x][y];
6599 if (old_group_nr == 0)
6602 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
6603 AmoebaCnt[old_group_nr] = 0;
6604 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
6605 AmoebaCnt2[old_group_nr] = 0;
6608 SCAN_PLAYFIELD(xx, yy)
6610 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
6613 if (AmoebaNr[xx][yy] == old_group_nr)
6614 AmoebaNr[xx][yy] = new_group_nr;
6620 void AmoebeUmwandeln(int ax, int ay)
6624 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
6626 int group_nr = AmoebaNr[ax][ay];
6631 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
6632 printf("AmoebeUmwandeln(): This should never happen!\n");
6638 SCAN_PLAYFIELD(x, y)
6640 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
6643 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
6646 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
6650 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
6651 SND_AMOEBA_TURNING_TO_GEM :
6652 SND_AMOEBA_TURNING_TO_ROCK));
6657 static int xy[4][2] =
6665 for (i = 0; i < NUM_DIRECTIONS; i++)
6670 if (!IN_LEV_FIELD(x, y))
6673 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
6675 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
6676 SND_AMOEBA_TURNING_TO_GEM :
6677 SND_AMOEBA_TURNING_TO_ROCK));
6684 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
6687 int group_nr = AmoebaNr[ax][ay];
6688 boolean done = FALSE;
6693 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
6694 printf("AmoebeUmwandelnBD(): This should never happen!\n");
6700 SCAN_PLAYFIELD(x, y)
6702 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
6705 if (AmoebaNr[x][y] == group_nr &&
6706 (Feld[x][y] == EL_AMOEBA_DEAD ||
6707 Feld[x][y] == EL_BD_AMOEBA ||
6708 Feld[x][y] == EL_AMOEBA_GROWING))
6711 Feld[x][y] = new_element;
6712 InitField(x, y, FALSE);
6713 DrawLevelField(x, y);
6719 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
6720 SND_BD_AMOEBA_TURNING_TO_ROCK :
6721 SND_BD_AMOEBA_TURNING_TO_GEM));
6724 void AmoebeWaechst(int x, int y)
6726 static unsigned long sound_delay = 0;
6727 static unsigned long sound_delay_value = 0;
6729 if (!MovDelay[x][y]) /* start new growing cycle */
6733 if (DelayReached(&sound_delay, sound_delay_value))
6735 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
6736 sound_delay_value = 30;
6740 if (MovDelay[x][y]) /* wait some time before growing bigger */
6743 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6745 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
6746 6 - MovDelay[x][y]);
6748 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
6751 if (!MovDelay[x][y])
6753 Feld[x][y] = Store[x][y];
6755 DrawLevelField(x, y);
6760 void AmoebaDisappearing(int x, int y)
6762 static unsigned long sound_delay = 0;
6763 static unsigned long sound_delay_value = 0;
6765 if (!MovDelay[x][y]) /* start new shrinking cycle */
6769 if (DelayReached(&sound_delay, sound_delay_value))
6770 sound_delay_value = 30;
6773 if (MovDelay[x][y]) /* wait some time before shrinking */
6776 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6778 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
6779 6 - MovDelay[x][y]);
6781 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
6784 if (!MovDelay[x][y])
6786 Feld[x][y] = EL_EMPTY;
6787 DrawLevelField(x, y);
6789 /* don't let mole enter this field in this cycle;
6790 (give priority to objects falling to this field from above) */
6796 void AmoebeAbleger(int ax, int ay)
6799 int element = Feld[ax][ay];
6800 int graphic = el2img(element);
6801 int newax = ax, neway = ay;
6802 boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
6803 static int xy[4][2] =
6811 if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
6813 Feld[ax][ay] = EL_AMOEBA_DEAD;
6814 DrawLevelField(ax, ay);
6818 if (IS_ANIMATED(graphic))
6819 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6821 if (!MovDelay[ax][ay]) /* start making new amoeba field */
6822 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
6824 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
6827 if (MovDelay[ax][ay])
6831 if (can_drop) /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
6834 int x = ax + xy[start][0];
6835 int y = ay + xy[start][1];
6837 if (!IN_LEV_FIELD(x, y))
6840 if (IS_FREE(x, y) ||
6841 CAN_GROW_INTO(Feld[x][y]) ||
6842 Feld[x][y] == EL_QUICKSAND_EMPTY)
6848 if (newax == ax && neway == ay)
6851 else /* normal or "filled" (BD style) amoeba */
6854 boolean waiting_for_player = FALSE;
6856 for (i = 0; i < NUM_DIRECTIONS; i++)
6858 int j = (start + i) % 4;
6859 int x = ax + xy[j][0];
6860 int y = ay + xy[j][1];
6862 if (!IN_LEV_FIELD(x, y))
6865 if (IS_FREE(x, y) ||
6866 CAN_GROW_INTO(Feld[x][y]) ||
6867 Feld[x][y] == EL_QUICKSAND_EMPTY)
6873 else if (IS_PLAYER(x, y))
6874 waiting_for_player = TRUE;
6877 if (newax == ax && neway == ay) /* amoeba cannot grow */
6879 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
6881 Feld[ax][ay] = EL_AMOEBA_DEAD;
6882 DrawLevelField(ax, ay);
6883 AmoebaCnt[AmoebaNr[ax][ay]]--;
6885 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
6887 if (element == EL_AMOEBA_FULL)
6888 AmoebeUmwandeln(ax, ay);
6889 else if (element == EL_BD_AMOEBA)
6890 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
6895 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
6897 /* amoeba gets larger by growing in some direction */
6899 int new_group_nr = AmoebaNr[ax][ay];
6902 if (new_group_nr == 0)
6904 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
6905 printf("AmoebeAbleger(): This should never happen!\n");
6910 AmoebaNr[newax][neway] = new_group_nr;
6911 AmoebaCnt[new_group_nr]++;
6912 AmoebaCnt2[new_group_nr]++;
6914 /* if amoeba touches other amoeba(s) after growing, unify them */
6915 AmoebenVereinigen(newax, neway);
6917 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
6919 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
6925 if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
6926 (neway == lev_fieldy - 1 && newax != ax))
6928 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
6929 Store[newax][neway] = element;
6931 else if (neway == ay || element == EL_EMC_DRIPPER)
6933 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
6935 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
6939 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
6940 Feld[ax][ay] = EL_AMOEBA_DROPPING;
6941 Store[ax][ay] = EL_AMOEBA_DROP;
6942 ContinueMoving(ax, ay);
6946 DrawLevelField(newax, neway);
6949 void Life(int ax, int ay)
6953 static int life[4] = { 2, 3, 3, 3 }; /* parameters for "game of life" */
6956 int element = Feld[ax][ay];
6957 int graphic = el2img(element);
6958 int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
6960 boolean changed = FALSE;
6962 if (IS_ANIMATED(graphic))
6963 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6968 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
6969 MovDelay[ax][ay] = life_time;
6971 if (MovDelay[ax][ay]) /* wait some time before next cycle */
6974 if (MovDelay[ax][ay])
6978 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
6980 int xx = ax+x1, yy = ay+y1;
6983 if (!IN_LEV_FIELD(xx, yy))
6986 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
6988 int x = xx+x2, y = yy+y2;
6990 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
6993 if (((Feld[x][y] == element ||
6994 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
6996 (IS_FREE(x, y) && Stop[x][y]))
7000 if (xx == ax && yy == ay) /* field in the middle */
7002 if (nachbarn < life_parameter[0] ||
7003 nachbarn > life_parameter[1])
7005 Feld[xx][yy] = EL_EMPTY;
7007 DrawLevelField(xx, yy);
7008 Stop[xx][yy] = TRUE;
7012 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
7013 { /* free border field */
7014 if (nachbarn >= life_parameter[2] &&
7015 nachbarn <= life_parameter[3])
7017 Feld[xx][yy] = element;
7018 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
7020 DrawLevelField(xx, yy);
7021 Stop[xx][yy] = TRUE;
7028 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
7029 SND_GAME_OF_LIFE_GROWING);
7032 static void InitRobotWheel(int x, int y)
7034 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
7037 static void RunRobotWheel(int x, int y)
7039 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
7042 static void StopRobotWheel(int x, int y)
7044 if (ZX == x && ZY == y)
7048 static void InitTimegateWheel(int x, int y)
7050 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
7053 static void RunTimegateWheel(int x, int y)
7055 PlayLevelSound(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
7058 static void InitMagicBallDelay(int x, int y)
7061 ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
7063 ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
7067 static void ActivateMagicBall(int bx, int by)
7071 if (level.ball_random)
7073 int pos_border = RND(8); /* select one of the eight border elements */
7074 int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
7075 int xx = pos_content % 3;
7076 int yy = pos_content / 3;
7081 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
7082 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
7086 for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
7088 int xx = x - bx + 1;
7089 int yy = y - by + 1;
7091 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
7092 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
7096 game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
7099 static void InitDiagonalMovingElement(int x, int y)
7102 MovDelay[x][y] = level.android_move_time;
7106 void CheckExit(int x, int y)
7108 if (local_player->gems_still_needed > 0 ||
7109 local_player->sokobanfields_still_needed > 0 ||
7110 local_player->lights_still_needed > 0)
7112 int element = Feld[x][y];
7113 int graphic = el2img(element);
7115 if (IS_ANIMATED(graphic))
7116 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7121 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7124 Feld[x][y] = EL_EXIT_OPENING;
7126 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
7129 void CheckExitSP(int x, int y)
7131 if (local_player->gems_still_needed > 0)
7133 int element = Feld[x][y];
7134 int graphic = el2img(element);
7136 if (IS_ANIMATED(graphic))
7137 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7142 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7145 Feld[x][y] = EL_SP_EXIT_OPENING;
7147 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
7150 static void CloseAllOpenTimegates()
7155 SCAN_PLAYFIELD(x, y)
7157 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7160 int element = Feld[x][y];
7162 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
7164 Feld[x][y] = EL_TIMEGATE_CLOSING;
7166 PlayLevelSoundAction(x, y, ACTION_CLOSING);
7171 void EdelsteinFunkeln(int x, int y)
7173 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
7176 if (Feld[x][y] == EL_BD_DIAMOND)
7179 if (MovDelay[x][y] == 0) /* next animation frame */
7180 MovDelay[x][y] = 11 * !SimpleRND(500);
7182 if (MovDelay[x][y] != 0) /* wait some time before next frame */
7186 if (setup.direct_draw && MovDelay[x][y])
7187 SetDrawtoField(DRAW_BUFFERED);
7189 DrawLevelElementAnimation(x, y, Feld[x][y]);
7191 if (MovDelay[x][y] != 0)
7193 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
7194 10 - MovDelay[x][y]);
7196 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
7198 if (setup.direct_draw)
7202 dest_x = FX + SCREENX(x) * TILEX;
7203 dest_y = FY + SCREENY(y) * TILEY;
7205 BlitBitmap(drawto_field, window,
7206 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
7207 SetDrawtoField(DRAW_DIRECT);
7213 void MauerWaechst(int x, int y)
7217 if (!MovDelay[x][y]) /* next animation frame */
7218 MovDelay[x][y] = 3 * delay;
7220 if (MovDelay[x][y]) /* wait some time before next frame */
7224 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7226 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
7227 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
7229 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
7232 if (!MovDelay[x][y])
7234 if (MovDir[x][y] == MV_LEFT)
7236 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
7237 DrawLevelField(x - 1, y);
7239 else if (MovDir[x][y] == MV_RIGHT)
7241 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
7242 DrawLevelField(x + 1, y);
7244 else if (MovDir[x][y] == MV_UP)
7246 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
7247 DrawLevelField(x, y - 1);
7251 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
7252 DrawLevelField(x, y + 1);
7255 Feld[x][y] = Store[x][y];
7257 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7258 DrawLevelField(x, y);
7263 void MauerAbleger(int ax, int ay)
7265 int element = Feld[ax][ay];
7266 int graphic = el2img(element);
7267 boolean oben_frei = FALSE, unten_frei = FALSE;
7268 boolean links_frei = FALSE, rechts_frei = FALSE;
7269 boolean oben_massiv = FALSE, unten_massiv = FALSE;
7270 boolean links_massiv = FALSE, rechts_massiv = FALSE;
7271 boolean new_wall = FALSE;
7273 if (IS_ANIMATED(graphic))
7274 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7276 if (!MovDelay[ax][ay]) /* start building new wall */
7277 MovDelay[ax][ay] = 6;
7279 if (MovDelay[ax][ay]) /* wait some time before building new wall */
7282 if (MovDelay[ax][ay])
7286 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
7288 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
7290 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
7292 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
7295 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
7296 element == EL_EXPANDABLE_WALL_ANY)
7300 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
7301 Store[ax][ay-1] = element;
7302 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
7303 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
7304 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
7305 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
7310 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
7311 Store[ax][ay+1] = element;
7312 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
7313 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
7314 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
7315 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
7320 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7321 element == EL_EXPANDABLE_WALL_ANY ||
7322 element == EL_EXPANDABLE_WALL)
7326 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
7327 Store[ax-1][ay] = element;
7328 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
7329 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
7330 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
7331 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
7337 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
7338 Store[ax+1][ay] = element;
7339 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
7340 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
7341 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
7342 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
7347 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
7348 DrawLevelField(ax, ay);
7350 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
7352 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
7353 unten_massiv = TRUE;
7354 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
7355 links_massiv = TRUE;
7356 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
7357 rechts_massiv = TRUE;
7359 if (((oben_massiv && unten_massiv) ||
7360 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7361 element == EL_EXPANDABLE_WALL) &&
7362 ((links_massiv && rechts_massiv) ||
7363 element == EL_EXPANDABLE_WALL_VERTICAL))
7364 Feld[ax][ay] = EL_WALL;
7367 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
7370 void CheckForDragon(int x, int y)
7373 boolean dragon_found = FALSE;
7374 static int xy[4][2] =
7382 for (i = 0; i < NUM_DIRECTIONS; i++)
7384 for (j = 0; j < 4; j++)
7386 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7388 if (IN_LEV_FIELD(xx, yy) &&
7389 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
7391 if (Feld[xx][yy] == EL_DRAGON)
7392 dragon_found = TRUE;
7401 for (i = 0; i < NUM_DIRECTIONS; i++)
7403 for (j = 0; j < 3; j++)
7405 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7407 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
7409 Feld[xx][yy] = EL_EMPTY;
7410 DrawLevelField(xx, yy);
7419 static void InitBuggyBase(int x, int y)
7421 int element = Feld[x][y];
7422 int activating_delay = FRAMES_PER_SECOND / 4;
7425 (element == EL_SP_BUGGY_BASE ?
7426 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
7427 element == EL_SP_BUGGY_BASE_ACTIVATING ?
7429 element == EL_SP_BUGGY_BASE_ACTIVE ?
7430 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
7433 static void WarnBuggyBase(int x, int y)
7436 static int xy[4][2] =
7444 for (i = 0; i < NUM_DIRECTIONS; i++)
7446 int xx = x + xy[i][0];
7447 int yy = y + xy[i][1];
7449 if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
7451 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
7458 static void InitTrap(int x, int y)
7460 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
7463 static void ActivateTrap(int x, int y)
7465 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
7468 static void ChangeActiveTrap(int x, int y)
7470 int graphic = IMG_TRAP_ACTIVE;
7472 /* if new animation frame was drawn, correct crumbled sand border */
7473 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
7474 DrawLevelFieldCrumbledSand(x, y);
7477 static int getSpecialActionElement(int element, int number, int base_element)
7479 return (element != EL_EMPTY ? element :
7480 number != -1 ? base_element + number - 1 :
7484 static int getModifiedActionNumber(int value_old, int operator, int operand,
7485 int value_min, int value_max)
7487 int value_new = (operator == CA_MODE_SET ? operand :
7488 operator == CA_MODE_ADD ? value_old + operand :
7489 operator == CA_MODE_SUBTRACT ? value_old - operand :
7490 operator == CA_MODE_MULTIPLY ? value_old * operand :
7491 operator == CA_MODE_DIVIDE ? value_old / MAX(1, operand) :
7492 operator == CA_MODE_MODULO ? value_old % MAX(1, operand) :
7495 return (value_new < value_min ? value_min :
7496 value_new > value_max ? value_max :
7500 static void ExecuteCustomElementAction(int x, int y, int element, int page)
7502 struct ElementInfo *ei = &element_info[element];
7503 struct ElementChangeInfo *change = &ei->change_page[page];
7504 int action_type = change->action_type;
7505 int action_mode = change->action_mode;
7506 int action_arg = change->action_arg;
7509 if (!change->has_action)
7512 /* ---------- determine action paramater values -------------------------- */
7514 int level_time_value =
7515 (level.time > 0 ? TimeLeft :
7518 int action_arg_element =
7519 (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player :
7520 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
7521 action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element :
7524 int action_arg_direction =
7525 (action_arg >= CA_ARG_DIRECTION_LEFT &&
7526 action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
7527 action_arg == CA_ARG_DIRECTION_TRIGGER ?
7528 change->actual_trigger_side :
7529 action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
7530 MV_DIR_OPPOSITE(change->actual_trigger_side) :
7533 int action_arg_number_min =
7534 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
7537 int action_arg_number_max =
7538 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
7539 action_type == CA_SET_LEVEL_GEMS ? 999 :
7540 action_type == CA_SET_LEVEL_TIME ? 9999 :
7541 action_type == CA_SET_LEVEL_SCORE ? 99999 :
7542 action_type == CA_SET_CE_SCORE ? 9999 :
7543 action_type == CA_SET_CE_VALUE ? 9999 :
7546 int action_arg_number_reset =
7547 (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize :
7548 action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
7549 action_type == CA_SET_LEVEL_TIME ? level.time :
7550 action_type == CA_SET_LEVEL_SCORE ? 0 :
7551 action_type == CA_SET_CE_SCORE ? 0 :
7553 action_type == CA_SET_CE_VALUE ? GET_NEW_CUSTOM_VALUE(element) :
7555 action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
7559 int action_arg_number =
7560 (action_arg <= CA_ARG_MAX ? action_arg :
7561 action_arg >= CA_ARG_SPEED_NOT_MOVING &&
7562 action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
7563 action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
7564 action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
7565 action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
7566 action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
7567 action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
7568 #if USE_NEW_CUSTOM_VALUE
7569 action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
7571 action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
7573 action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
7574 action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
7575 action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
7576 action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
7577 action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CUSTOM_VALUE(change->target_element) :
7578 action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
7579 action_arg == CA_ARG_ELEMENT_NR_TARGET ? change->target_element :
7580 action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
7583 int action_arg_number_old =
7584 (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
7585 action_type == CA_SET_LEVEL_TIME ? TimeLeft :
7586 action_type == CA_SET_LEVEL_SCORE ? local_player->score :
7587 action_type == CA_SET_CE_SCORE ? ei->collect_score :
7588 action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
7591 int action_arg_number_new =
7592 getModifiedActionNumber(action_arg_number_old,
7593 action_mode, action_arg_number,
7594 action_arg_number_min, action_arg_number_max);
7596 int trigger_player_bits =
7597 (change->actual_trigger_player >= EL_PLAYER_1 &&
7598 change->actual_trigger_player <= EL_PLAYER_4 ?
7599 (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
7602 int action_arg_player_bits =
7603 (action_arg >= CA_ARG_PLAYER_1 &&
7604 action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
7605 action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
7608 /* ---------- execute action -------------------------------------------- */
7617 /* ---------- level actions ------------------------------------------- */
7619 case CA_RESTART_LEVEL:
7621 game.restart_level = TRUE;
7626 case CA_SHOW_ENVELOPE:
7628 int element = getSpecialActionElement(action_arg_element,
7629 action_arg_number, EL_ENVELOPE_1);
7631 if (IS_ENVELOPE(element))
7632 local_player->show_envelope = element;
7637 case CA_SET_LEVEL_TIME:
7639 if (level.time > 0) /* only modify limited time value */
7641 TimeLeft = action_arg_number_new;
7643 DrawGameValue_Time(TimeLeft);
7645 if (!TimeLeft && setup.time_limit)
7646 for (i = 0; i < MAX_PLAYERS; i++)
7647 KillPlayer(&stored_player[i]);
7653 case CA_SET_LEVEL_SCORE:
7655 local_player->score = action_arg_number_new;
7657 DrawGameValue_Score(local_player->score);
7662 case CA_SET_LEVEL_GEMS:
7664 local_player->gems_still_needed = action_arg_number_new;
7666 DrawGameValue_Emeralds(local_player->gems_still_needed);
7671 case CA_SET_LEVEL_GRAVITY:
7673 game.gravity = (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
7674 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
7675 action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
7680 case CA_SET_LEVEL_WIND:
7682 game.wind_direction = action_arg_direction;
7687 /* ---------- player actions ------------------------------------------ */
7689 case CA_MOVE_PLAYER:
7691 /* automatically move to the next field in specified direction */
7692 for (i = 0; i < MAX_PLAYERS; i++)
7693 if (trigger_player_bits & (1 << i))
7694 stored_player[i].programmed_action = action_arg_direction;
7699 case CA_EXIT_PLAYER:
7701 for (i = 0; i < MAX_PLAYERS; i++)
7702 if (action_arg_player_bits & (1 << i))
7703 stored_player[i].LevelSolved = stored_player[i].GameOver = TRUE;
7708 case CA_KILL_PLAYER:
7710 for (i = 0; i < MAX_PLAYERS; i++)
7711 if (action_arg_player_bits & (1 << i))
7712 KillPlayer(&stored_player[i]);
7717 case CA_SET_PLAYER_KEYS:
7719 int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
7720 int element = getSpecialActionElement(action_arg_element,
7721 action_arg_number, EL_KEY_1);
7723 if (IS_KEY(element))
7725 for (i = 0; i < MAX_PLAYERS; i++)
7727 if (trigger_player_bits & (1 << i))
7729 stored_player[i].key[KEY_NR(element)] = key_state;
7731 DrawGameValue_Keys(stored_player[i].key);
7733 redraw_mask |= REDRAW_DOOR_1;
7741 case CA_SET_PLAYER_SPEED:
7743 for (i = 0; i < MAX_PLAYERS; i++)
7745 if (trigger_player_bits & (1 << i))
7747 int move_stepsize = TILEX / stored_player[i].move_delay_value;
7749 if (action_arg == CA_ARG_SPEED_FASTER &&
7750 stored_player[i].cannot_move)
7752 action_arg_number = STEPSIZE_VERY_SLOW;
7754 else if (action_arg == CA_ARG_SPEED_SLOWER ||
7755 action_arg == CA_ARG_SPEED_FASTER)
7757 action_arg_number = 2;
7758 action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
7763 getModifiedActionNumber(move_stepsize,
7766 action_arg_number_min,
7767 action_arg_number_max);
7770 SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
7772 /* make sure that value is power of 2 */
7773 move_stepsize = (1 << log_2(move_stepsize));
7775 /* do no immediately change -- the player might just be moving */
7776 stored_player[i].move_delay_value_next = TILEX / move_stepsize;
7778 stored_player[i].cannot_move =
7779 (action_arg == CA_ARG_SPEED_NOT_MOVING ? TRUE : FALSE);
7787 case CA_SET_PLAYER_SHIELD:
7789 for (i = 0; i < MAX_PLAYERS; i++)
7791 if (trigger_player_bits & (1 << i))
7793 if (action_arg == CA_ARG_SHIELD_OFF)
7795 stored_player[i].shield_normal_time_left = 0;
7796 stored_player[i].shield_deadly_time_left = 0;
7798 else if (action_arg == CA_ARG_SHIELD_NORMAL)
7800 stored_player[i].shield_normal_time_left = 999999;
7802 else if (action_arg == CA_ARG_SHIELD_DEADLY)
7804 stored_player[i].shield_normal_time_left = 999999;
7805 stored_player[i].shield_deadly_time_left = 999999;
7813 case CA_SET_PLAYER_ARTWORK:
7815 for (i = 0; i < MAX_PLAYERS; i++)
7817 if (trigger_player_bits & (1 << i))
7819 int artwork_element = action_arg_element;
7821 if (action_arg == CA_ARG_ELEMENT_RESET)
7823 (level.use_artwork_element[i] ? level.artwork_element[i] :
7824 stored_player[i].element_nr);
7826 stored_player[i].artwork_element = artwork_element;
7828 SetPlayerWaiting(&stored_player[i], FALSE);
7830 /* set number of special actions for bored and sleeping animation */
7831 stored_player[i].num_special_action_bored =
7832 get_num_special_action(artwork_element,
7833 ACTION_BORING_1, ACTION_BORING_LAST);
7834 stored_player[i].num_special_action_sleeping =
7835 get_num_special_action(artwork_element,
7836 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
7843 /* ---------- CE actions ---------------------------------------------- */
7845 case CA_SET_CE_SCORE:
7847 ei->collect_score = action_arg_number_new;
7852 case CA_SET_CE_VALUE:
7854 #if USE_NEW_CUSTOM_VALUE
7855 int last_custom_value = CustomValue[x][y];
7857 CustomValue[x][y] = action_arg_number_new;
7860 printf("::: Count == %d\n", CustomValue[x][y]);
7863 if (CustomValue[x][y] == 0 && last_custom_value > 0)
7866 printf("::: CE_VALUE_GETS_ZERO\n");
7869 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
7870 CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
7873 printf("::: RESULT: %d, %d\n", Feld[x][y], ChangePage[x][y]);
7881 /* ---------- engine actions ------------------------------------------ */
7883 case CA_SET_ENGINE_SCAN_MODE:
7885 InitPlayfieldScanMode(action_arg);
7895 static void CreateFieldExt(int x, int y, int element, boolean is_change)
7897 int previous_move_direction = MovDir[x][y];
7898 #if USE_NEW_CUSTOM_VALUE
7899 int last_ce_value = CustomValue[x][y];
7901 boolean add_player = (ELEM_IS_PLAYER(element) &&
7902 IS_WALKABLE(Feld[x][y]));
7904 /* check if element under player changes from accessible to unaccessible
7905 (needed for special case of dropping element which then changes) */
7906 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
7907 IS_ACCESSIBLE(Feld[x][y]) && !IS_ACCESSIBLE(element))
7916 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
7917 RemoveMovingField(x, y);
7921 Feld[x][y] = element;
7923 ResetGfxAnimation(x, y);
7924 ResetRandomAnimationValue(x, y);
7926 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
7927 MovDir[x][y] = previous_move_direction;
7929 #if USE_NEW_CUSTOM_VALUE
7930 if (element_info[Feld[x][y]].use_last_ce_value)
7931 CustomValue[x][y] = last_ce_value;
7934 InitField_WithBug1(x, y, FALSE);
7936 DrawLevelField(x, y);
7938 if (GFX_CRUMBLED(Feld[x][y]))
7939 DrawLevelFieldCrumbledSandNeighbours(x, y);
7942 /* "ChangeCount" not set yet to allow "entered by player" change one time */
7943 if (ELEM_IS_PLAYER(element))
7944 RelocatePlayer(x, y, element);
7947 ChangeCount[x][y]++; /* count number of changes in the same frame */
7949 TestIfBadThingTouchesPlayer(x, y);
7950 TestIfPlayerTouchesCustomElement(x, y);
7951 TestIfElementTouchesCustomElement(x, y);
7954 static void CreateField(int x, int y, int element)
7956 CreateFieldExt(x, y, element, FALSE);
7959 static void CreateElementFromChange(int x, int y, int element)
7961 element = GET_VALID_RUNTIME_ELEMENT(element);
7963 #if USE_STOP_CHANGED_ELEMENTS
7964 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
7966 int old_element = Feld[x][y];
7968 /* prevent changed element from moving in same engine frame
7969 unless both old and new element can either fall or move */
7970 if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
7971 (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
7976 CreateFieldExt(x, y, element, TRUE);
7979 static boolean ChangeElement(int x, int y, int element, int page)
7981 struct ElementChangeInfo *change = &element_info[element].change_page[page];
7983 int old_element = Feld[x][y];
7985 /* always use default change event to prevent running into a loop */
7986 if (ChangeEvent[x][y] == -1)
7987 ChangeEvent[x][y] = CE_DELAY;
7989 if (ChangeEvent[x][y] == CE_DELAY)
7991 /* reset actual trigger element, trigger player and action element */
7992 change->actual_trigger_element = EL_EMPTY;
7993 change->actual_trigger_player = EL_PLAYER_1;
7994 change->actual_trigger_side = CH_SIDE_NONE;
7995 change->actual_trigger_ce_value = 0;
7998 /* do not change elements more than a specified maximum number of changes */
7999 if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
8002 ChangeCount[x][y]++; /* count number of changes in the same frame */
8004 if (change->explode)
8011 if (change->use_target_content)
8013 boolean complete_replace = TRUE;
8014 boolean can_replace[3][3];
8017 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
8020 boolean is_walkable;
8021 boolean is_diggable;
8022 boolean is_collectible;
8023 boolean is_removable;
8024 boolean is_destructible;
8025 int ex = x + xx - 1;
8026 int ey = y + yy - 1;
8027 int content_element = change->target_content.e[xx][yy];
8030 can_replace[xx][yy] = TRUE;
8032 if (ex == x && ey == y) /* do not check changing element itself */
8035 if (content_element == EL_EMPTY_SPACE)
8037 can_replace[xx][yy] = FALSE; /* do not replace border with space */
8042 if (!IN_LEV_FIELD(ex, ey))
8044 can_replace[xx][yy] = FALSE;
8045 complete_replace = FALSE;
8052 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
8053 e = MovingOrBlocked2Element(ex, ey);
8055 is_empty = (IS_FREE(ex, ey) ||
8056 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
8058 is_walkable = (is_empty || IS_WALKABLE(e));
8059 is_diggable = (is_empty || IS_DIGGABLE(e));
8060 is_collectible = (is_empty || IS_COLLECTIBLE(e));
8061 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
8062 is_removable = (is_diggable || is_collectible);
8064 can_replace[xx][yy] =
8065 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
8066 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
8067 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
8068 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
8069 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
8070 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
8071 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
8073 if (!can_replace[xx][yy])
8074 complete_replace = FALSE;
8077 if (!change->only_if_complete || complete_replace)
8079 boolean something_has_changed = FALSE;
8081 if (change->only_if_complete && change->use_random_replace &&
8082 RND(100) < change->random_percentage)
8085 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
8087 int ex = x + xx - 1;
8088 int ey = y + yy - 1;
8089 int content_element;
8091 if (can_replace[xx][yy] && (!change->use_random_replace ||
8092 RND(100) < change->random_percentage))
8094 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
8095 RemoveMovingField(ex, ey);
8097 ChangeEvent[ex][ey] = ChangeEvent[x][y];
8099 content_element = change->target_content.e[xx][yy];
8100 target_element = GET_TARGET_ELEMENT(content_element, change);
8102 CreateElementFromChange(ex, ey, target_element);
8104 something_has_changed = TRUE;
8106 /* for symmetry reasons, freeze newly created border elements */
8107 if (ex != x || ey != y)
8108 Stop[ex][ey] = TRUE; /* no more moving in this frame */
8112 if (something_has_changed)
8114 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
8115 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
8121 target_element = GET_TARGET_ELEMENT(change->target_element, change);
8123 if (element == EL_DIAGONAL_GROWING ||
8124 element == EL_DIAGONAL_SHRINKING)
8126 target_element = Store[x][y];
8128 Store[x][y] = EL_EMPTY;
8131 CreateElementFromChange(x, y, target_element);
8133 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
8134 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
8137 /* this uses direct change before indirect change */
8138 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
8143 #if USE_NEW_DELAYED_ACTION
8145 static void HandleElementChange(int x, int y, int page)
8147 int element = MovingOrBlocked2Element(x, y);
8148 struct ElementInfo *ei = &element_info[element];
8149 struct ElementChangeInfo *change = &ei->change_page[page];
8152 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
8153 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
8156 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
8157 x, y, element, element_info[element].token_name);
8158 printf("HandleElementChange(): This should never happen!\n");
8163 /* this can happen with classic bombs on walkable, changing elements */
8164 if (!CAN_CHANGE_OR_HAS_ACTION(element))
8167 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
8168 ChangeDelay[x][y] = 0;
8174 if (ChangeDelay[x][y] == 0) /* initialize element change */
8176 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
8178 if (change->can_change)
8180 ResetGfxAnimation(x, y);
8181 ResetRandomAnimationValue(x, y);
8183 if (change->pre_change_function)
8184 change->pre_change_function(x, y);
8188 ChangeDelay[x][y]--;
8190 if (ChangeDelay[x][y] != 0) /* continue element change */
8192 if (change->can_change)
8194 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8196 if (IS_ANIMATED(graphic))
8197 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8199 if (change->change_function)
8200 change->change_function(x, y);
8203 else /* finish element change */
8205 if (ChangePage[x][y] != -1) /* remember page from delayed change */
8207 page = ChangePage[x][y];
8208 ChangePage[x][y] = -1;
8210 change = &ei->change_page[page];
8213 if (IS_MOVING(x, y)) /* never change a running system ;-) */
8215 ChangeDelay[x][y] = 1; /* try change after next move step */
8216 ChangePage[x][y] = page; /* remember page to use for change */
8221 if (change->can_change)
8223 if (ChangeElement(x, y, element, page))
8225 if (change->post_change_function)
8226 change->post_change_function(x, y);
8230 if (change->has_action)
8231 ExecuteCustomElementAction(x, y, element, page);
8237 static void HandleElementChange(int x, int y, int page)
8239 int element = MovingOrBlocked2Element(x, y);
8240 struct ElementInfo *ei = &element_info[element];
8241 struct ElementChangeInfo *change = &ei->change_page[page];
8244 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
8247 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
8248 x, y, element, element_info[element].token_name);
8249 printf("HandleElementChange(): This should never happen!\n");
8254 /* this can happen with classic bombs on walkable, changing elements */
8255 if (!CAN_CHANGE(element))
8258 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
8259 ChangeDelay[x][y] = 0;
8265 if (ChangeDelay[x][y] == 0) /* initialize element change */
8267 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
8269 ResetGfxAnimation(x, y);
8270 ResetRandomAnimationValue(x, y);
8272 if (change->pre_change_function)
8273 change->pre_change_function(x, y);
8276 ChangeDelay[x][y]--;
8278 if (ChangeDelay[x][y] != 0) /* continue element change */
8280 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8282 if (IS_ANIMATED(graphic))
8283 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8285 if (change->change_function)
8286 change->change_function(x, y);
8288 else /* finish element change */
8290 if (ChangePage[x][y] != -1) /* remember page from delayed change */
8292 page = ChangePage[x][y];
8293 ChangePage[x][y] = -1;
8295 change = &ei->change_page[page];
8298 if (IS_MOVING(x, y)) /* never change a running system ;-) */
8300 ChangeDelay[x][y] = 1; /* try change after next move step */
8301 ChangePage[x][y] = page; /* remember page to use for change */
8306 if (ChangeElement(x, y, element, page))
8308 if (change->post_change_function)
8309 change->post_change_function(x, y);
8316 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
8317 int trigger_element,
8323 boolean change_done_any = FALSE;
8324 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
8327 if (!(trigger_events[trigger_element][trigger_event]))
8330 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8332 int element = EL_CUSTOM_START + i;
8333 boolean change_done = FALSE;
8336 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
8337 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
8340 for (p = 0; p < element_info[element].num_change_pages; p++)
8342 struct ElementChangeInfo *change = &element_info[element].change_page[p];
8344 if (change->can_change_or_has_action &&
8345 change->has_event[trigger_event] &&
8346 change->trigger_side & trigger_side &&
8347 change->trigger_player & trigger_player &&
8348 change->trigger_page & trigger_page_bits &&
8349 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
8351 change->actual_trigger_element = trigger_element;
8352 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
8353 change->actual_trigger_side = trigger_side;
8354 change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
8356 if ((change->can_change && !change_done) || change->has_action)
8361 SCAN_PLAYFIELD(x, y)
8363 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8366 if (Feld[x][y] == element)
8368 if (change->can_change && !change_done)
8370 ChangeDelay[x][y] = 1;
8371 ChangeEvent[x][y] = trigger_event;
8373 HandleElementChange(x, y, p);
8375 #if USE_NEW_DELAYED_ACTION
8376 else if (change->has_action)
8378 ExecuteCustomElementAction(x, y, element, p);
8379 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8382 if (change->has_action)
8384 ExecuteCustomElementAction(x, y, element, p);
8385 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8391 if (change->can_change)
8394 change_done_any = TRUE;
8401 return change_done_any;
8404 static boolean CheckElementChangeExt(int x, int y,
8406 int trigger_element,
8411 boolean change_done = FALSE;
8414 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
8415 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
8418 if (Feld[x][y] == EL_BLOCKED)
8420 Blocked2Moving(x, y, &x, &y);
8421 element = Feld[x][y];
8425 /* check if element has already changed */
8426 if (Feld[x][y] != element)
8429 /* check if element has already changed or is about to change after moving */
8430 if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
8431 Feld[x][y] != element) ||
8433 (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
8434 (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
8435 ChangePage[x][y] != -1)))
8439 for (p = 0; p < element_info[element].num_change_pages; p++)
8441 struct ElementChangeInfo *change = &element_info[element].change_page[p];
8443 boolean check_trigger_element =
8444 (trigger_event == CE_TOUCHING_X ||
8445 trigger_event == CE_HITTING_X ||
8446 trigger_event == CE_HIT_BY_X);
8448 if (change->can_change_or_has_action &&
8449 change->has_event[trigger_event] &&
8450 change->trigger_side & trigger_side &&
8451 change->trigger_player & trigger_player &&
8452 (!check_trigger_element ||
8453 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
8455 change->actual_trigger_element = trigger_element;
8456 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
8457 change->actual_trigger_side = trigger_side;
8458 change->actual_trigger_ce_value = CustomValue[x][y];
8460 /* special case: trigger element not at (x,y) position for some events */
8461 if (check_trigger_element)
8473 { 0, 0 }, { 0, 0 }, { 0, 0 },
8477 int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
8478 int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
8480 change->actual_trigger_ce_value = CustomValue[xx][yy];
8483 if (change->can_change && !change_done)
8485 ChangeDelay[x][y] = 1;
8486 ChangeEvent[x][y] = trigger_event;
8488 HandleElementChange(x, y, p);
8492 #if USE_NEW_DELAYED_ACTION
8493 else if (change->has_action)
8495 ExecuteCustomElementAction(x, y, element, p);
8496 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8499 if (change->has_action)
8501 ExecuteCustomElementAction(x, y, element, p);
8502 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8511 static void PlayPlayerSound(struct PlayerInfo *player)
8513 int jx = player->jx, jy = player->jy;
8514 int sound_element = player->artwork_element;
8515 int last_action = player->last_action_waiting;
8516 int action = player->action_waiting;
8518 if (player->is_waiting)
8520 if (action != last_action)
8521 PlayLevelSoundElementAction(jx, jy, sound_element, action);
8523 PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
8527 if (action != last_action)
8528 StopSound(element_info[sound_element].sound[last_action]);
8530 if (last_action == ACTION_SLEEPING)
8531 PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
8535 static void PlayAllPlayersSound()
8539 for (i = 0; i < MAX_PLAYERS; i++)
8540 if (stored_player[i].active)
8541 PlayPlayerSound(&stored_player[i]);
8544 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
8546 boolean last_waiting = player->is_waiting;
8547 int move_dir = player->MovDir;
8549 player->last_action_waiting = player->action_waiting;
8553 if (!last_waiting) /* not waiting -> waiting */
8555 player->is_waiting = TRUE;
8557 player->frame_counter_bored =
8559 game.player_boring_delay_fixed +
8560 SimpleRND(game.player_boring_delay_random);
8561 player->frame_counter_sleeping =
8563 game.player_sleeping_delay_fixed +
8564 SimpleRND(game.player_sleeping_delay_random);
8566 InitPlayerGfxAnimation(player, ACTION_WAITING, player->MovDir);
8569 if (game.player_sleeping_delay_fixed +
8570 game.player_sleeping_delay_random > 0 &&
8571 player->anim_delay_counter == 0 &&
8572 player->post_delay_counter == 0 &&
8573 FrameCounter >= player->frame_counter_sleeping)
8574 player->is_sleeping = TRUE;
8575 else if (game.player_boring_delay_fixed +
8576 game.player_boring_delay_random > 0 &&
8577 FrameCounter >= player->frame_counter_bored)
8578 player->is_bored = TRUE;
8580 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
8581 player->is_bored ? ACTION_BORING :
8584 if (player->is_sleeping)
8586 if (player->num_special_action_sleeping > 0)
8588 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
8590 int last_special_action = player->special_action_sleeping;
8591 int num_special_action = player->num_special_action_sleeping;
8592 int special_action =
8593 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
8594 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
8595 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
8596 last_special_action + 1 : ACTION_SLEEPING);
8597 int special_graphic =
8598 el_act_dir2img(player->artwork_element, special_action, move_dir);
8600 player->anim_delay_counter =
8601 graphic_info[special_graphic].anim_delay_fixed +
8602 SimpleRND(graphic_info[special_graphic].anim_delay_random);
8603 player->post_delay_counter =
8604 graphic_info[special_graphic].post_delay_fixed +
8605 SimpleRND(graphic_info[special_graphic].post_delay_random);
8607 player->special_action_sleeping = special_action;
8610 if (player->anim_delay_counter > 0)
8612 player->action_waiting = player->special_action_sleeping;
8613 player->anim_delay_counter--;
8615 else if (player->post_delay_counter > 0)
8617 player->post_delay_counter--;
8621 else if (player->is_bored)
8623 if (player->num_special_action_bored > 0)
8625 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
8627 int special_action =
8628 ACTION_BORING_1 + SimpleRND(player->num_special_action_bored);
8629 int special_graphic =
8630 el_act_dir2img(player->artwork_element, special_action, move_dir);
8632 player->anim_delay_counter =
8633 graphic_info[special_graphic].anim_delay_fixed +
8634 SimpleRND(graphic_info[special_graphic].anim_delay_random);
8635 player->post_delay_counter =
8636 graphic_info[special_graphic].post_delay_fixed +
8637 SimpleRND(graphic_info[special_graphic].post_delay_random);
8639 player->special_action_bored = special_action;
8642 if (player->anim_delay_counter > 0)
8644 player->action_waiting = player->special_action_bored;
8645 player->anim_delay_counter--;
8647 else if (player->post_delay_counter > 0)
8649 player->post_delay_counter--;
8654 else if (last_waiting) /* waiting -> not waiting */
8656 player->is_waiting = FALSE;
8657 player->is_bored = FALSE;
8658 player->is_sleeping = FALSE;
8660 player->frame_counter_bored = -1;
8661 player->frame_counter_sleeping = -1;
8663 player->anim_delay_counter = 0;
8664 player->post_delay_counter = 0;
8666 player->action_waiting = ACTION_DEFAULT;
8668 player->special_action_bored = ACTION_DEFAULT;
8669 player->special_action_sleeping = ACTION_DEFAULT;
8673 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
8675 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
8676 int left = player_action & JOY_LEFT;
8677 int right = player_action & JOY_RIGHT;
8678 int up = player_action & JOY_UP;
8679 int down = player_action & JOY_DOWN;
8680 int button1 = player_action & JOY_BUTTON_1;
8681 int button2 = player_action & JOY_BUTTON_2;
8682 int dx = (left ? -1 : right ? 1 : 0);
8683 int dy = (up ? -1 : down ? 1 : 0);
8685 if (!player->active || tape.pausing)
8691 snapped = SnapField(player, dx, dy);
8695 dropped = DropElement(player);
8697 moved = MovePlayer(player, dx, dy);
8700 if (tape.single_step && tape.recording && !tape.pausing)
8702 if (button1 || (dropped && !moved))
8704 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8705 SnapField(player, 0, 0); /* stop snapping */
8709 SetPlayerWaiting(player, FALSE);
8711 return player_action;
8715 /* no actions for this player (no input at player's configured device) */
8717 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
8718 SnapField(player, 0, 0);
8719 CheckGravityMovementWhenNotMoving(player);
8721 if (player->MovPos == 0)
8722 SetPlayerWaiting(player, TRUE);
8724 if (player->MovPos == 0) /* needed for tape.playing */
8725 player->is_moving = FALSE;
8727 player->is_dropping = FALSE;
8733 void AdvanceFrameAndPlayerCounters(int player_nr)
8737 /* advance frame counters (global frame counter and time frame counter) */
8741 /* advance player counters (counters for move delay, move animation etc.) */
8742 for (i = 0; i < MAX_PLAYERS; i++)
8744 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
8745 int move_delay_value = stored_player[i].move_delay_value;
8746 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
8748 if (!advance_player_counters) /* not all players may be affected */
8751 #if USE_NEW_PLAYER_ANIM
8752 if (move_frames == 0) /* less than one move per game frame */
8754 int stepsize = TILEX / move_delay_value;
8755 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
8756 int count = (stored_player[i].is_moving ?
8757 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
8759 if (count % delay == 0)
8764 stored_player[i].Frame += move_frames;
8766 if (stored_player[i].MovPos != 0)
8767 stored_player[i].StepFrame += move_frames;
8769 if (stored_player[i].move_delay > 0)
8770 stored_player[i].move_delay--;
8772 /* due to bugs in previous versions, counter must count up, not down */
8773 if (stored_player[i].push_delay != -1)
8774 stored_player[i].push_delay++;
8776 if (stored_player[i].drop_delay > 0)
8777 stored_player[i].drop_delay--;
8783 static unsigned long game_frame_delay = 0;
8784 unsigned long game_frame_delay_value;
8785 int magic_wall_x = 0, magic_wall_y = 0;
8786 int i, x, y, element, graphic;
8787 byte *recorded_player_action;
8788 byte summarized_player_action = 0;
8789 byte tape_action[MAX_PLAYERS];
8791 if (game_status != GAME_MODE_PLAYING)
8794 game_frame_delay_value =
8795 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
8797 if (tape.playing && tape.warp_forward && !tape.pausing)
8798 game_frame_delay_value = 0;
8800 /* ---------- main game synchronization point ---------- */
8802 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
8804 InitPlayfieldScanModeVars();
8806 #if USE_ONE_MORE_CHANGE_PER_FRAME
8807 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
8809 SCAN_PLAYFIELD(x, y)
8811 ChangeCount[x][y] = 0;
8812 ChangeEvent[x][y] = -1;
8817 if (network_playing && !network_player_action_received)
8819 /* try to get network player actions in time */
8821 #if defined(NETWORK_AVALIABLE)
8822 /* last chance to get network player actions without main loop delay */
8826 /* game was quit by network peer */
8827 if (game_status != GAME_MODE_PLAYING)
8830 if (!network_player_action_received)
8831 return; /* failed to get network player actions in time */
8837 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
8840 /* !!! CHECK THIS (tape.pausing is always FALSE here!) !!! */
8841 if (recorded_player_action == NULL && tape.pausing)
8845 for (i = 0; i < MAX_PLAYERS; i++)
8847 summarized_player_action |= stored_player[i].action;
8849 if (!network_playing)
8850 stored_player[i].effective_action = stored_player[i].action;
8853 #if defined(NETWORK_AVALIABLE)
8854 if (network_playing)
8855 SendToServer_MovePlayer(summarized_player_action);
8858 if (!options.network && !setup.team_mode)
8859 local_player->effective_action = summarized_player_action;
8861 if (recorded_player_action != NULL)
8862 for (i = 0; i < MAX_PLAYERS; i++)
8863 stored_player[i].effective_action = recorded_player_action[i];
8865 for (i = 0; i < MAX_PLAYERS; i++)
8867 tape_action[i] = stored_player[i].effective_action;
8869 if (tape.recording && tape_action[i] && !tape.player_participates[i])
8870 tape.player_participates[i] = TRUE; /* player just appeared from CE */
8873 /* only save actions from input devices, but not programmed actions */
8875 TapeRecordAction(tape_action);
8877 for (i = 0; i < MAX_PLAYERS; i++)
8879 int actual_player_action = stored_player[i].effective_action;
8882 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
8883 - rnd_equinox_tetrachloride 048
8884 - rnd_equinox_tetrachloride_ii 096
8885 - rnd_emanuel_schmieg 002
8886 - doctor_sloan_ww 001, 020
8888 if (stored_player[i].MovPos == 0)
8889 CheckGravityMovement(&stored_player[i]);
8892 /* overwrite programmed action with tape action */
8893 if (stored_player[i].programmed_action)
8894 actual_player_action = stored_player[i].programmed_action;
8897 PlayerActions(&stored_player[i], actual_player_action);
8899 tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
8901 if (tape.recording && tape_action[i] && !tape.player_participates[i])
8902 tape.player_participates[i] = TRUE; /* player just appeared from CE */
8905 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
8908 network_player_action_received = FALSE;
8910 ScrollScreen(NULL, SCROLL_GO_ON);
8912 /* for backwards compatibility, the following code emulates a fixed bug that
8913 occured when pushing elements (causing elements that just made their last
8914 pushing step to already (if possible) make their first falling step in the
8915 same game frame, which is bad); this code is also needed to use the famous
8916 "spring push bug" which is used in older levels and might be wanted to be
8917 used also in newer levels, but in this case the buggy pushing code is only
8918 affecting the "spring" element and no other elements */
8920 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
8922 for (i = 0; i < MAX_PLAYERS; i++)
8924 struct PlayerInfo *player = &stored_player[i];
8928 if (player->active && player->is_pushing && player->is_moving &&
8930 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
8931 Feld[x][y] == EL_SPRING))
8933 ContinueMoving(x, y);
8935 /* continue moving after pushing (this is actually a bug) */
8936 if (!IS_MOVING(x, y))
8945 SCAN_PLAYFIELD(x, y)
8947 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8950 ChangeCount[x][y] = 0;
8951 ChangeEvent[x][y] = -1;
8953 /* this must be handled before main playfield loop */
8954 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
8957 if (MovDelay[x][y] <= 0)
8961 #if USE_NEW_SNAP_DELAY
8962 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
8965 if (MovDelay[x][y] <= 0)
8968 DrawLevelField(x, y);
8970 TestIfElementTouchesCustomElement(x, y); /* for empty space */
8976 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
8978 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
8979 printf("GameActions(): This should never happen!\n");
8981 ChangePage[x][y] = -1;
8986 if (WasJustMoving[x][y] > 0)
8987 WasJustMoving[x][y]--;
8988 if (WasJustFalling[x][y] > 0)
8989 WasJustFalling[x][y]--;
8990 if (CheckCollision[x][y] > 0)
8991 CheckCollision[x][y]--;
8995 /* reset finished pushing action (not done in ContinueMoving() to allow
8996 continuous pushing animation for elements with zero push delay) */
8997 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
8999 ResetGfxAnimation(x, y);
9000 DrawLevelField(x, y);
9004 if (IS_BLOCKED(x, y))
9008 Blocked2Moving(x, y, &oldx, &oldy);
9009 if (!IS_MOVING(oldx, oldy))
9011 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
9012 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
9013 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
9014 printf("GameActions(): This should never happen!\n");
9021 SCAN_PLAYFIELD(x, y)
9023 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9026 element = Feld[x][y];
9027 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9030 printf("::: %d,%d\n", x, y);
9032 if (element == EL_ROCK)
9033 printf("::: Yo man! Rocks can fall!\n");
9036 if (graphic_info[graphic].anim_global_sync)
9037 GfxFrame[x][y] = FrameCounter;
9038 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
9040 int old_gfx_frame = GfxFrame[x][y];
9042 GfxFrame[x][y] = CustomValue[x][y];
9045 if (GfxFrame[x][y] != old_gfx_frame)
9047 DrawLevelGraphicAnimation(x, y, graphic);
9049 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
9051 int old_gfx_frame = GfxFrame[x][y];
9053 GfxFrame[x][y] = element_info[element].collect_score;
9056 if (GfxFrame[x][y] != old_gfx_frame)
9058 DrawLevelGraphicAnimation(x, y, graphic);
9061 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
9062 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
9063 ResetRandomAnimationValue(x, y);
9065 SetRandomAnimationValue(x, y);
9067 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
9069 if (IS_INACTIVE(element))
9071 if (IS_ANIMATED(graphic))
9072 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9077 /* this may take place after moving, so 'element' may have changed */
9078 if (IS_CHANGING(x, y) &&
9079 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
9081 int page = element_info[element].event_page_nr[CE_DELAY];
9083 HandleElementChange(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] : page);
9087 printf("::: ChangeDelay == %d\n", ChangeDelay[x][y]);
9091 if (element == EL_CUSTOM_255)
9092 printf("::: ChangeDelay == %d\n", ChangeDelay[x][y]);
9096 HandleElementChange(x, y, page);
9098 if (CAN_CHANGE(element))
9099 HandleElementChange(x, y, page);
9101 if (HAS_ACTION(element))
9102 ExecuteCustomElementAction(x, y, element, page);
9107 element = Feld[x][y];
9108 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9111 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
9115 element = Feld[x][y];
9116 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9118 if (IS_ANIMATED(graphic) &&
9121 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9123 if (IS_GEM(element) || element == EL_SP_INFOTRON)
9124 EdelsteinFunkeln(x, y);
9126 else if ((element == EL_ACID ||
9127 element == EL_EXIT_OPEN ||
9128 element == EL_SP_EXIT_OPEN ||
9129 element == EL_SP_TERMINAL ||
9130 element == EL_SP_TERMINAL_ACTIVE ||
9131 element == EL_EXTRA_TIME ||
9132 element == EL_SHIELD_NORMAL ||
9133 element == EL_SHIELD_DEADLY) &&
9134 IS_ANIMATED(graphic))
9135 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9136 else if (IS_MOVING(x, y))
9137 ContinueMoving(x, y);
9138 else if (IS_ACTIVE_BOMB(element))
9139 CheckDynamite(x, y);
9140 else if (element == EL_AMOEBA_GROWING)
9141 AmoebeWaechst(x, y);
9142 else if (element == EL_AMOEBA_SHRINKING)
9143 AmoebaDisappearing(x, y);
9145 #if !USE_NEW_AMOEBA_CODE
9146 else if (IS_AMOEBALIVE(element))
9147 AmoebeAbleger(x, y);
9150 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
9152 else if (element == EL_EXIT_CLOSED)
9154 else if (element == EL_SP_EXIT_CLOSED)
9156 else if (element == EL_EXPANDABLE_WALL_GROWING)
9158 else if (element == EL_EXPANDABLE_WALL ||
9159 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9160 element == EL_EXPANDABLE_WALL_VERTICAL ||
9161 element == EL_EXPANDABLE_WALL_ANY)
9163 else if (element == EL_FLAMES)
9164 CheckForDragon(x, y);
9165 else if (element == EL_EXPLOSION)
9166 ; /* drawing of correct explosion animation is handled separately */
9167 else if (element == EL_ELEMENT_SNAPPING ||
9168 element == EL_DIAGONAL_SHRINKING ||
9169 element == EL_DIAGONAL_GROWING)
9172 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
9174 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9177 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
9178 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9181 if (element == EL_CUSTOM_255 ||
9182 element == EL_CUSTOM_256)
9183 DrawLevelGraphicAnimation(x, y, graphic);
9186 if (IS_BELT_ACTIVE(element))
9187 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
9189 if (game.magic_wall_active)
9191 int jx = local_player->jx, jy = local_player->jy;
9193 /* play the element sound at the position nearest to the player */
9194 if ((element == EL_MAGIC_WALL_FULL ||
9195 element == EL_MAGIC_WALL_ACTIVE ||
9196 element == EL_MAGIC_WALL_EMPTYING ||
9197 element == EL_BD_MAGIC_WALL_FULL ||
9198 element == EL_BD_MAGIC_WALL_ACTIVE ||
9199 element == EL_BD_MAGIC_WALL_EMPTYING) &&
9200 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
9208 #if USE_NEW_AMOEBA_CODE
9209 /* new experimental amoeba growth stuff */
9210 if (!(FrameCounter % 8))
9212 static unsigned long random = 1684108901;
9214 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
9216 x = RND(lev_fieldx);
9217 y = RND(lev_fieldy);
9218 element = Feld[x][y];
9220 if (!IS_PLAYER(x,y) &&
9221 (element == EL_EMPTY ||
9222 CAN_GROW_INTO(element) ||
9223 element == EL_QUICKSAND_EMPTY ||
9224 element == EL_ACID_SPLASH_LEFT ||
9225 element == EL_ACID_SPLASH_RIGHT))
9227 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
9228 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
9229 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
9230 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
9231 Feld[x][y] = EL_AMOEBA_DROP;
9234 random = random * 129 + 1;
9240 if (game.explosions_delayed)
9243 game.explosions_delayed = FALSE;
9246 SCAN_PLAYFIELD(x, y)
9248 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9251 element = Feld[x][y];
9253 if (ExplodeField[x][y])
9254 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
9255 else if (element == EL_EXPLOSION)
9256 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
9258 ExplodeField[x][y] = EX_TYPE_NONE;
9261 game.explosions_delayed = TRUE;
9264 if (game.magic_wall_active)
9266 if (!(game.magic_wall_time_left % 4))
9268 int element = Feld[magic_wall_x][magic_wall_y];
9270 if (element == EL_BD_MAGIC_WALL_FULL ||
9271 element == EL_BD_MAGIC_WALL_ACTIVE ||
9272 element == EL_BD_MAGIC_WALL_EMPTYING)
9273 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
9275 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
9278 if (game.magic_wall_time_left > 0)
9280 game.magic_wall_time_left--;
9281 if (!game.magic_wall_time_left)
9284 SCAN_PLAYFIELD(x, y)
9286 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9289 element = Feld[x][y];
9291 if (element == EL_MAGIC_WALL_ACTIVE ||
9292 element == EL_MAGIC_WALL_FULL)
9294 Feld[x][y] = EL_MAGIC_WALL_DEAD;
9295 DrawLevelField(x, y);
9297 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
9298 element == EL_BD_MAGIC_WALL_FULL)
9300 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
9301 DrawLevelField(x, y);
9305 game.magic_wall_active = FALSE;
9310 if (game.light_time_left > 0)
9312 game.light_time_left--;
9314 if (game.light_time_left == 0)
9315 RedrawAllLightSwitchesAndInvisibleElements();
9318 if (game.timegate_time_left > 0)
9320 game.timegate_time_left--;
9322 if (game.timegate_time_left == 0)
9323 CloseAllOpenTimegates();
9326 if (game.lenses_time_left > 0)
9328 game.lenses_time_left--;
9330 if (game.lenses_time_left == 0)
9331 RedrawAllInvisibleElementsForLenses();
9334 if (game.magnify_time_left > 0)
9336 game.magnify_time_left--;
9338 if (game.magnify_time_left == 0)
9339 RedrawAllInvisibleElementsForMagnifier();
9342 for (i = 0; i < MAX_PLAYERS; i++)
9344 struct PlayerInfo *player = &stored_player[i];
9346 if (SHIELD_ON(player))
9348 if (player->shield_deadly_time_left)
9349 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
9350 else if (player->shield_normal_time_left)
9351 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
9355 if (TimeFrames >= FRAMES_PER_SECOND)
9360 for (i = 0; i < MAX_PLAYERS; i++)
9362 struct PlayerInfo *player = &stored_player[i];
9364 if (SHIELD_ON(player))
9366 player->shield_normal_time_left--;
9368 if (player->shield_deadly_time_left > 0)
9369 player->shield_deadly_time_left--;
9373 if (!level.use_step_counter)
9381 if (TimeLeft <= 10 && setup.time_limit)
9382 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
9384 DrawGameValue_Time(TimeLeft);
9386 if (!TimeLeft && setup.time_limit)
9387 for (i = 0; i < MAX_PLAYERS; i++)
9388 KillPlayer(&stored_player[i]);
9390 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
9391 DrawGameValue_Time(TimePlayed);
9394 if (tape.recording || tape.playing)
9395 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
9399 PlayAllPlayersSound();
9401 if (options.debug) /* calculate frames per second */
9403 static unsigned long fps_counter = 0;
9404 static int fps_frames = 0;
9405 unsigned long fps_delay_ms = Counter() - fps_counter;
9409 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
9411 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
9414 fps_counter = Counter();
9417 redraw_mask |= REDRAW_FPS;
9420 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
9422 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
9424 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
9426 local_player->show_envelope = 0;
9429 /* use random number generator in every frame to make it less predictable */
9430 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
9434 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
9436 int min_x = x, min_y = y, max_x = x, max_y = y;
9439 for (i = 0; i < MAX_PLAYERS; i++)
9441 int jx = stored_player[i].jx, jy = stored_player[i].jy;
9443 if (!stored_player[i].active || &stored_player[i] == player)
9446 min_x = MIN(min_x, jx);
9447 min_y = MIN(min_y, jy);
9448 max_x = MAX(max_x, jx);
9449 max_y = MAX(max_y, jy);
9452 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
9455 static boolean AllPlayersInVisibleScreen()
9459 for (i = 0; i < MAX_PLAYERS; i++)
9461 int jx = stored_player[i].jx, jy = stored_player[i].jy;
9463 if (!stored_player[i].active)
9466 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
9473 void ScrollLevel(int dx, int dy)
9475 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
9478 BlitBitmap(drawto_field, drawto_field,
9479 FX + TILEX * (dx == -1) - softscroll_offset,
9480 FY + TILEY * (dy == -1) - softscroll_offset,
9481 SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
9482 SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
9483 FX + TILEX * (dx == 1) - softscroll_offset,
9484 FY + TILEY * (dy == 1) - softscroll_offset);
9488 x = (dx == 1 ? BX1 : BX2);
9489 for (y = BY1; y <= BY2; y++)
9490 DrawScreenField(x, y);
9495 y = (dy == 1 ? BY1 : BY2);
9496 for (x = BX1; x <= BX2; x++)
9497 DrawScreenField(x, y);
9500 redraw_mask |= REDRAW_FIELD;
9503 static boolean canFallDown(struct PlayerInfo *player)
9505 int jx = player->jx, jy = player->jy;
9507 return (IN_LEV_FIELD(jx, jy + 1) &&
9508 (IS_FREE(jx, jy + 1) ||
9509 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
9510 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
9511 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
9514 static boolean canPassField(int x, int y, int move_dir)
9516 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9517 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9518 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
9521 int element = Feld[x][y];
9523 return (IS_PASSABLE_FROM(element, opposite_dir) &&
9524 !CAN_MOVE(element) &&
9525 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
9526 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
9527 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
9530 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
9532 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9533 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9534 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
9538 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9539 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
9540 (IS_DIGGABLE(Feld[newx][newy]) ||
9541 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9542 canPassField(newx, newy, move_dir)));
9545 static void CheckGravityMovement(struct PlayerInfo *player)
9547 if (game.gravity && !player->programmed_action)
9549 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
9550 int move_dir_vertical = player->effective_action & MV_VERTICAL;
9551 boolean player_is_snapping = player->effective_action & JOY_BUTTON_1;
9552 int jx = player->jx, jy = player->jy;
9553 boolean player_is_moving_to_valid_field =
9554 (!player_is_snapping &&
9555 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
9556 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
9557 boolean player_can_fall_down = canFallDown(player);
9559 if (player_can_fall_down &&
9560 !player_is_moving_to_valid_field)
9561 player->programmed_action = MV_DOWN;
9565 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
9567 return CheckGravityMovement(player);
9569 if (game.gravity && !player->programmed_action)
9571 int jx = player->jx, jy = player->jy;
9572 boolean field_under_player_is_free =
9573 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
9574 boolean player_is_standing_on_valid_field =
9575 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
9576 (IS_WALKABLE(Feld[jx][jy]) &&
9577 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
9579 if (field_under_player_is_free && !player_is_standing_on_valid_field)
9580 player->programmed_action = MV_DOWN;
9586 -----------------------------------------------------------------------------
9587 dx, dy: direction (non-diagonal) to try to move the player to
9588 real_dx, real_dy: direction as read from input device (can be diagonal)
9591 boolean MovePlayerOneStep(struct PlayerInfo *player,
9592 int dx, int dy, int real_dx, int real_dy)
9594 int jx = player->jx, jy = player->jy;
9595 int new_jx = jx + dx, new_jy = jy + dy;
9596 #if !USE_FIXED_DONT_RUN_INTO
9600 boolean player_can_move = !player->cannot_move;
9602 if (!player->active || (!dx && !dy))
9603 return MP_NO_ACTION;
9605 player->MovDir = (dx < 0 ? MV_LEFT :
9608 dy > 0 ? MV_DOWN : MV_NONE);
9610 if (!IN_LEV_FIELD(new_jx, new_jy))
9611 return MP_NO_ACTION;
9613 if (!player_can_move)
9616 if (player->MovPos == 0)
9618 player->is_moving = FALSE;
9619 player->is_digging = FALSE;
9620 player->is_collecting = FALSE;
9621 player->is_snapping = FALSE;
9622 player->is_pushing = FALSE;
9625 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
9626 SnapField(player, 0, 0);
9630 return MP_NO_ACTION;
9634 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
9635 return MP_NO_ACTION;
9637 #if !USE_FIXED_DONT_RUN_INTO
9638 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
9640 /* (moved to DigField()) */
9641 if (player_can_move && DONT_RUN_INTO(element))
9643 if (element == EL_ACID && dx == 0 && dy == 1)
9645 SplashAcid(new_jx, new_jy);
9646 Feld[jx][jy] = EL_PLAYER_1;
9647 InitMovingField(jx, jy, MV_DOWN);
9648 Store[jx][jy] = EL_ACID;
9649 ContinueMoving(jx, jy);
9653 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
9659 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
9661 #if USE_FIXED_DONT_RUN_INTO
9662 if (can_move == MP_DONT_RUN_INTO)
9666 if (can_move != MP_MOVING)
9669 #if USE_FIXED_DONT_RUN_INTO
9672 /* check if DigField() has caused relocation of the player */
9673 if (player->jx != jx || player->jy != jy)
9674 return MP_NO_ACTION; /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
9676 StorePlayer[jx][jy] = 0;
9677 player->last_jx = jx;
9678 player->last_jy = jy;
9679 player->jx = new_jx;
9680 player->jy = new_jy;
9681 StorePlayer[new_jx][new_jy] = player->element_nr;
9683 if (player->move_delay_value_next != -1)
9685 player->move_delay_value = player->move_delay_value_next;
9686 player->move_delay_value_next = -1;
9690 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
9692 player->step_counter++;
9694 PlayerVisit[jx][jy] = FrameCounter;
9696 ScrollPlayer(player, SCROLL_INIT);
9701 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
9703 int jx = player->jx, jy = player->jy;
9704 int old_jx = jx, old_jy = jy;
9705 int moved = MP_NO_ACTION;
9707 if (!player->active)
9712 if (player->MovPos == 0)
9714 player->is_moving = FALSE;
9715 player->is_digging = FALSE;
9716 player->is_collecting = FALSE;
9717 player->is_snapping = FALSE;
9718 player->is_pushing = FALSE;
9724 if (player->move_delay > 0)
9727 player->move_delay = -1; /* set to "uninitialized" value */
9729 /* store if player is automatically moved to next field */
9730 player->is_auto_moving = (player->programmed_action != MV_NONE);
9732 /* remove the last programmed player action */
9733 player->programmed_action = 0;
9737 /* should only happen if pre-1.2 tape recordings are played */
9738 /* this is only for backward compatibility */
9740 int original_move_delay_value = player->move_delay_value;
9743 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
9747 /* scroll remaining steps with finest movement resolution */
9748 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
9750 while (player->MovPos)
9752 ScrollPlayer(player, SCROLL_GO_ON);
9753 ScrollScreen(NULL, SCROLL_GO_ON);
9755 AdvanceFrameAndPlayerCounters(player->index_nr);
9761 player->move_delay_value = original_move_delay_value;
9764 if (player->last_move_dir & MV_HORIZONTAL)
9766 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
9767 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
9771 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
9772 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
9778 if (moved & MP_MOVING && !ScreenMovPos &&
9779 (player == local_player || !options.network))
9781 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
9782 int offset = (setup.scroll_delay ? 3 : 0);
9784 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
9786 /* actual player has left the screen -- scroll in that direction */
9787 if (jx != old_jx) /* player has moved horizontally */
9788 scroll_x += (jx - old_jx);
9789 else /* player has moved vertically */
9790 scroll_y += (jy - old_jy);
9794 if (jx != old_jx) /* player has moved horizontally */
9796 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
9797 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
9798 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
9800 /* don't scroll over playfield boundaries */
9801 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
9802 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
9804 /* don't scroll more than one field at a time */
9805 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
9807 /* don't scroll against the player's moving direction */
9808 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
9809 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
9810 scroll_x = old_scroll_x;
9812 else /* player has moved vertically */
9814 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
9815 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
9816 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
9818 /* don't scroll over playfield boundaries */
9819 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
9820 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
9822 /* don't scroll more than one field at a time */
9823 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
9825 /* don't scroll against the player's moving direction */
9826 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
9827 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
9828 scroll_y = old_scroll_y;
9832 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
9834 if (!options.network && !AllPlayersInVisibleScreen())
9836 scroll_x = old_scroll_x;
9837 scroll_y = old_scroll_y;
9841 ScrollScreen(player, SCROLL_INIT);
9842 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
9847 player->StepFrame = 0;
9849 if (moved & MP_MOVING)
9851 if (old_jx != jx && old_jy == jy)
9852 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
9853 else if (old_jx == jx && old_jy != jy)
9854 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
9856 DrawLevelField(jx, jy); /* for "crumbled sand" */
9858 player->last_move_dir = player->MovDir;
9859 player->is_moving = TRUE;
9860 player->is_snapping = FALSE;
9861 player->is_switching = FALSE;
9862 player->is_dropping = FALSE;
9866 CheckGravityMovementWhenNotMoving(player);
9868 player->is_moving = FALSE;
9870 /* at this point, the player is allowed to move, but cannot move right now
9871 (e.g. because of something blocking the way) -- ensure that the player
9872 is also allowed to move in the next frame (in old versions before 3.1.1,
9873 the player was forced to wait again for eight frames before next try) */
9875 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
9876 player->move_delay = 0; /* allow direct movement in the next frame */
9879 if (player->move_delay == -1) /* not yet initialized by DigField() */
9880 player->move_delay = player->move_delay_value;
9882 if (game.engine_version < VERSION_IDENT(3,0,7,0))
9884 TestIfPlayerTouchesBadThing(jx, jy);
9885 TestIfPlayerTouchesCustomElement(jx, jy);
9888 if (!player->active)
9889 RemovePlayer(player);
9894 void ScrollPlayer(struct PlayerInfo *player, int mode)
9896 int jx = player->jx, jy = player->jy;
9897 int last_jx = player->last_jx, last_jy = player->last_jy;
9898 int move_stepsize = TILEX / player->move_delay_value;
9900 #if USE_NEW_PLAYER_SPEED
9901 if (!player->active)
9904 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
9907 if (!player->active || player->MovPos == 0)
9911 if (mode == SCROLL_INIT)
9913 player->actual_frame_counter = FrameCounter;
9914 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
9916 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
9917 Feld[last_jx][last_jy] == EL_EMPTY)
9919 int last_field_block_delay = 0; /* start with no blocking at all */
9920 int block_delay_adjustment = player->block_delay_adjustment;
9922 /* if player blocks last field, add delay for exactly one move */
9923 if (player->block_last_field)
9925 last_field_block_delay += player->move_delay_value;
9927 /* when blocking enabled, prevent moving up despite gravity */
9928 if (game.gravity && player->MovDir == MV_UP)
9929 block_delay_adjustment = -1;
9932 /* add block delay adjustment (also possible when not blocking) */
9933 last_field_block_delay += block_delay_adjustment;
9935 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
9936 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
9939 #if USE_NEW_PLAYER_SPEED
9940 if (player->MovPos != 0) /* player has not yet reached destination */
9946 else if (!FrameReached(&player->actual_frame_counter, 1))
9950 printf("::: player->MovPos: %d -> %d\n",
9952 player->MovPos + (player->MovPos > 0 ? -1 : 1) * move_stepsize);
9955 #if USE_NEW_PLAYER_SPEED
9956 if (player->MovPos != 0)
9958 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
9959 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
9961 /* before DrawPlayer() to draw correct player graphic for this case */
9962 if (player->MovPos == 0)
9963 CheckGravityMovement(player);
9966 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
9967 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
9969 /* before DrawPlayer() to draw correct player graphic for this case */
9970 if (player->MovPos == 0)
9971 CheckGravityMovement(player);
9974 if (player->MovPos == 0) /* player reached destination field */
9977 printf("::: player reached destination field\n");
9980 if (player->move_delay_reset_counter > 0)
9982 player->move_delay_reset_counter--;
9984 if (player->move_delay_reset_counter == 0)
9986 /* continue with normal speed after quickly moving through gate */
9987 HALVE_PLAYER_SPEED(player);
9989 /* be able to make the next move without delay */
9990 player->move_delay = 0;
9994 player->last_jx = jx;
9995 player->last_jy = jy;
9997 if (Feld[jx][jy] == EL_EXIT_OPEN ||
9998 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
9999 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
10001 DrawPlayer(player); /* needed here only to cleanup last field */
10002 RemovePlayer(player);
10004 if (local_player->friends_still_needed == 0 ||
10005 IS_SP_ELEMENT(Feld[jx][jy]))
10006 player->LevelSolved = player->GameOver = TRUE;
10009 /* this breaks one level: "machine", level 000 */
10011 int move_direction = player->MovDir;
10012 int enter_side = MV_DIR_OPPOSITE(move_direction);
10013 int leave_side = move_direction;
10014 int old_jx = last_jx;
10015 int old_jy = last_jy;
10016 int old_element = Feld[old_jx][old_jy];
10017 int new_element = Feld[jx][jy];
10019 if (IS_CUSTOM_ELEMENT(old_element))
10020 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
10022 player->index_bit, leave_side);
10024 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
10025 CE_PLAYER_LEAVES_X,
10026 player->index_bit, leave_side);
10028 if (IS_CUSTOM_ELEMENT(new_element))
10029 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
10030 player->index_bit, enter_side);
10032 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
10033 CE_PLAYER_ENTERS_X,
10034 player->index_bit, enter_side);
10036 CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
10037 CE_MOVE_OF_X, move_direction);
10040 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10042 TestIfPlayerTouchesBadThing(jx, jy);
10043 TestIfPlayerTouchesCustomElement(jx, jy);
10045 /* needed because pushed element has not yet reached its destination,
10046 so it would trigger a change event at its previous field location */
10047 if (!player->is_pushing)
10048 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
10050 if (!player->active)
10051 RemovePlayer(player);
10054 if (level.use_step_counter)
10064 if (TimeLeft <= 10 && setup.time_limit)
10065 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
10067 DrawGameValue_Time(TimeLeft);
10069 if (!TimeLeft && setup.time_limit)
10070 for (i = 0; i < MAX_PLAYERS; i++)
10071 KillPlayer(&stored_player[i]);
10073 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
10074 DrawGameValue_Time(TimePlayed);
10077 if (tape.single_step && tape.recording && !tape.pausing &&
10078 !player->programmed_action)
10079 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10083 void ScrollScreen(struct PlayerInfo *player, int mode)
10085 static unsigned long screen_frame_counter = 0;
10087 if (mode == SCROLL_INIT)
10089 /* set scrolling step size according to actual player's moving speed */
10090 ScrollStepSize = TILEX / player->move_delay_value;
10092 screen_frame_counter = FrameCounter;
10093 ScreenMovDir = player->MovDir;
10094 ScreenMovPos = player->MovPos;
10095 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
10098 else if (!FrameReached(&screen_frame_counter, 1))
10103 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
10104 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
10105 redraw_mask |= REDRAW_FIELD;
10108 ScreenMovDir = MV_NONE;
10111 void TestIfPlayerTouchesCustomElement(int x, int y)
10113 static int xy[4][2] =
10120 static int trigger_sides[4][2] =
10122 /* center side border side */
10123 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
10124 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
10125 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
10126 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
10128 static int touch_dir[4] =
10130 MV_LEFT | MV_RIGHT,
10135 int center_element = Feld[x][y]; /* should always be non-moving! */
10138 for (i = 0; i < NUM_DIRECTIONS; i++)
10140 int xx = x + xy[i][0];
10141 int yy = y + xy[i][1];
10142 int center_side = trigger_sides[i][0];
10143 int border_side = trigger_sides[i][1];
10144 int border_element;
10146 if (!IN_LEV_FIELD(xx, yy))
10149 if (IS_PLAYER(x, y))
10151 struct PlayerInfo *player = PLAYERINFO(x, y);
10153 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10154 border_element = Feld[xx][yy]; /* may be moving! */
10155 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
10156 border_element = Feld[xx][yy];
10157 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
10158 border_element = MovingOrBlocked2Element(xx, yy);
10160 continue; /* center and border element do not touch */
10162 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
10163 player->index_bit, border_side);
10164 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
10165 CE_PLAYER_TOUCHES_X,
10166 player->index_bit, border_side);
10168 else if (IS_PLAYER(xx, yy))
10170 struct PlayerInfo *player = PLAYERINFO(xx, yy);
10172 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10174 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
10175 continue; /* center and border element do not touch */
10178 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
10179 player->index_bit, center_side);
10180 CheckTriggeredElementChangeByPlayer(x, y, center_element,
10181 CE_PLAYER_TOUCHES_X,
10182 player->index_bit, center_side);
10188 #if USE_ELEMENT_TOUCHING_BUGFIX
10190 void TestIfElementTouchesCustomElement(int x, int y)
10192 static int xy[4][2] =
10199 static int trigger_sides[4][2] =
10201 /* center side border side */
10202 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
10203 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
10204 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
10205 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
10207 static int touch_dir[4] =
10209 MV_LEFT | MV_RIGHT,
10214 boolean change_center_element = FALSE;
10215 int center_element = Feld[x][y]; /* should always be non-moving! */
10216 int border_element_old[NUM_DIRECTIONS];
10219 for (i = 0; i < NUM_DIRECTIONS; i++)
10221 int xx = x + xy[i][0];
10222 int yy = y + xy[i][1];
10223 int border_element;
10225 border_element_old[i] = -1;
10227 if (!IN_LEV_FIELD(xx, yy))
10230 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10231 border_element = Feld[xx][yy]; /* may be moving! */
10232 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
10233 border_element = Feld[xx][yy];
10234 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
10235 border_element = MovingOrBlocked2Element(xx, yy);
10237 continue; /* center and border element do not touch */
10239 border_element_old[i] = border_element;
10242 for (i = 0; i < NUM_DIRECTIONS; i++)
10244 int xx = x + xy[i][0];
10245 int yy = y + xy[i][1];
10246 int center_side = trigger_sides[i][0];
10247 int border_element = border_element_old[i];
10249 if (border_element == -1)
10252 /* check for change of border element */
10253 CheckElementChangeBySide(xx, yy, border_element, center_element,
10254 CE_TOUCHING_X, center_side);
10257 for (i = 0; i < NUM_DIRECTIONS; i++)
10259 int border_side = trigger_sides[i][1];
10260 int border_element = border_element_old[i];
10262 if (border_element == -1)
10265 /* check for change of center element (but change it only once) */
10266 if (!change_center_element)
10267 change_center_element =
10268 CheckElementChangeBySide(x, y, center_element, border_element,
10269 CE_TOUCHING_X, border_side);
10275 void TestIfElementTouchesCustomElement_OLD(int x, int y)
10277 static int xy[4][2] =
10284 static int trigger_sides[4][2] =
10286 /* center side border side */
10287 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
10288 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
10289 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
10290 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
10292 static int touch_dir[4] =
10294 MV_LEFT | MV_RIGHT,
10299 boolean change_center_element = FALSE;
10300 int center_element = Feld[x][y]; /* should always be non-moving! */
10303 for (i = 0; i < NUM_DIRECTIONS; i++)
10305 int xx = x + xy[i][0];
10306 int yy = y + xy[i][1];
10307 int center_side = trigger_sides[i][0];
10308 int border_side = trigger_sides[i][1];
10309 int border_element;
10311 if (!IN_LEV_FIELD(xx, yy))
10314 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10315 border_element = Feld[xx][yy]; /* may be moving! */
10316 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
10317 border_element = Feld[xx][yy];
10318 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
10319 border_element = MovingOrBlocked2Element(xx, yy);
10321 continue; /* center and border element do not touch */
10323 /* check for change of center element (but change it only once) */
10324 if (!change_center_element)
10325 change_center_element =
10326 CheckElementChangeBySide(x, y, center_element, border_element,
10327 CE_TOUCHING_X, border_side);
10329 /* check for change of border element */
10330 CheckElementChangeBySide(xx, yy, border_element, center_element,
10331 CE_TOUCHING_X, center_side);
10337 void TestIfElementHitsCustomElement(int x, int y, int direction)
10339 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
10340 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
10341 int hitx = x + dx, hity = y + dy;
10342 int hitting_element = Feld[x][y];
10343 int touched_element;
10345 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
10348 touched_element = (IN_LEV_FIELD(hitx, hity) ?
10349 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
10351 if (IN_LEV_FIELD(hitx, hity))
10353 int opposite_direction = MV_DIR_OPPOSITE(direction);
10354 int hitting_side = direction;
10355 int touched_side = opposite_direction;
10356 boolean object_hit = (!IS_MOVING(hitx, hity) ||
10357 MovDir[hitx][hity] != direction ||
10358 ABS(MovPos[hitx][hity]) <= TILEY / 2);
10364 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10365 CE_HITTING_X, touched_side);
10367 CheckElementChangeBySide(hitx, hity, touched_element,
10368 hitting_element, CE_HIT_BY_X, hitting_side);
10370 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10371 CE_HIT_BY_SOMETHING, opposite_direction);
10375 /* "hitting something" is also true when hitting the playfield border */
10376 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10377 CE_HITTING_SOMETHING, direction);
10381 void TestIfElementSmashesCustomElement(int x, int y, int direction)
10383 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
10384 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
10385 int hitx = x + dx, hity = y + dy;
10386 int hitting_element = Feld[x][y];
10387 int touched_element;
10389 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
10390 !IS_FREE(hitx, hity) &&
10391 (!IS_MOVING(hitx, hity) ||
10392 MovDir[hitx][hity] != direction ||
10393 ABS(MovPos[hitx][hity]) <= TILEY / 2));
10396 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
10400 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
10404 touched_element = (IN_LEV_FIELD(hitx, hity) ?
10405 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
10407 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10408 EP_CAN_SMASH_EVERYTHING, direction);
10410 if (IN_LEV_FIELD(hitx, hity))
10412 int opposite_direction = MV_DIR_OPPOSITE(direction);
10413 int hitting_side = direction;
10414 int touched_side = opposite_direction;
10416 int touched_element = MovingOrBlocked2Element(hitx, hity);
10419 boolean object_hit = (!IS_MOVING(hitx, hity) ||
10420 MovDir[hitx][hity] != direction ||
10421 ABS(MovPos[hitx][hity]) <= TILEY / 2);
10430 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10431 CE_SMASHED_BY_SOMETHING, opposite_direction);
10433 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10434 CE_OTHER_IS_SMASHING, touched_side);
10436 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10437 CE_OTHER_GETS_SMASHED, hitting_side);
10443 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
10445 int i, kill_x = -1, kill_y = -1;
10447 int bad_element = -1;
10448 static int test_xy[4][2] =
10455 static int test_dir[4] =
10463 for (i = 0; i < NUM_DIRECTIONS; i++)
10465 int test_x, test_y, test_move_dir, test_element;
10467 test_x = good_x + test_xy[i][0];
10468 test_y = good_y + test_xy[i][1];
10470 if (!IN_LEV_FIELD(test_x, test_y))
10474 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
10476 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
10478 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
10479 2nd case: DONT_TOUCH style bad thing does not move away from good thing
10481 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
10482 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
10486 bad_element = test_element;
10492 if (kill_x != -1 || kill_y != -1)
10494 if (IS_PLAYER(good_x, good_y))
10496 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
10498 if (player->shield_deadly_time_left > 0 &&
10499 !IS_INDESTRUCTIBLE(bad_element))
10500 Bang(kill_x, kill_y);
10501 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
10502 KillPlayer(player);
10505 Bang(good_x, good_y);
10509 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
10511 int i, kill_x = -1, kill_y = -1;
10512 int bad_element = Feld[bad_x][bad_y];
10513 static int test_xy[4][2] =
10520 static int touch_dir[4] =
10522 MV_LEFT | MV_RIGHT,
10527 static int test_dir[4] =
10535 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
10538 for (i = 0; i < NUM_DIRECTIONS; i++)
10540 int test_x, test_y, test_move_dir, test_element;
10542 test_x = bad_x + test_xy[i][0];
10543 test_y = bad_y + test_xy[i][1];
10544 if (!IN_LEV_FIELD(test_x, test_y))
10548 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
10550 test_element = Feld[test_x][test_y];
10552 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
10553 2nd case: DONT_TOUCH style bad thing does not move away from good thing
10555 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
10556 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
10558 /* good thing is player or penguin that does not move away */
10559 if (IS_PLAYER(test_x, test_y))
10561 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
10563 if (bad_element == EL_ROBOT && player->is_moving)
10564 continue; /* robot does not kill player if he is moving */
10566 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10568 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
10569 continue; /* center and border element do not touch */
10576 else if (test_element == EL_PENGUIN)
10585 if (kill_x != -1 || kill_y != -1)
10587 if (IS_PLAYER(kill_x, kill_y))
10589 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
10591 if (player->shield_deadly_time_left > 0 &&
10592 !IS_INDESTRUCTIBLE(bad_element))
10593 Bang(bad_x, bad_y);
10594 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
10595 KillPlayer(player);
10598 Bang(kill_x, kill_y);
10602 void TestIfPlayerTouchesBadThing(int x, int y)
10604 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
10607 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
10609 TestIfGoodThingHitsBadThing(x, y, move_dir);
10612 void TestIfBadThingTouchesPlayer(int x, int y)
10614 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
10617 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
10619 TestIfBadThingHitsGoodThing(x, y, move_dir);
10622 void TestIfFriendTouchesBadThing(int x, int y)
10624 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
10627 void TestIfBadThingTouchesFriend(int x, int y)
10629 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
10632 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
10634 int i, kill_x = bad_x, kill_y = bad_y;
10635 static int xy[4][2] =
10643 for (i = 0; i < NUM_DIRECTIONS; i++)
10647 x = bad_x + xy[i][0];
10648 y = bad_y + xy[i][1];
10649 if (!IN_LEV_FIELD(x, y))
10652 element = Feld[x][y];
10653 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
10654 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
10662 if (kill_x != bad_x || kill_y != bad_y)
10663 Bang(bad_x, bad_y);
10666 void KillPlayer(struct PlayerInfo *player)
10668 int jx = player->jx, jy = player->jy;
10670 if (!player->active)
10673 /* remove accessible field at the player's position */
10674 Feld[jx][jy] = EL_EMPTY;
10676 /* deactivate shield (else Bang()/Explode() would not work right) */
10677 player->shield_normal_time_left = 0;
10678 player->shield_deadly_time_left = 0;
10681 BuryPlayer(player);
10684 static void KillPlayerUnlessEnemyProtected(int x, int y)
10686 if (!PLAYER_ENEMY_PROTECTED(x, y))
10687 KillPlayer(PLAYERINFO(x, y));
10690 static void KillPlayerUnlessExplosionProtected(int x, int y)
10692 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
10693 KillPlayer(PLAYERINFO(x, y));
10696 void BuryPlayer(struct PlayerInfo *player)
10698 int jx = player->jx, jy = player->jy;
10700 if (!player->active)
10703 PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
10704 PlayLevelSound(jx, jy, SND_GAME_LOSING);
10706 player->GameOver = TRUE;
10707 RemovePlayer(player);
10710 void RemovePlayer(struct PlayerInfo *player)
10712 int jx = player->jx, jy = player->jy;
10713 int i, found = FALSE;
10715 player->present = FALSE;
10716 player->active = FALSE;
10718 if (!ExplodeField[jx][jy])
10719 StorePlayer[jx][jy] = 0;
10721 if (player->is_moving)
10722 DrawLevelField(player->last_jx, player->last_jy);
10724 for (i = 0; i < MAX_PLAYERS; i++)
10725 if (stored_player[i].active)
10729 AllPlayersGone = TRUE;
10735 #if USE_NEW_SNAP_DELAY
10736 static void setFieldForSnapping(int x, int y, int element, int direction)
10738 struct ElementInfo *ei = &element_info[element];
10739 int direction_bit = MV_DIR_TO_BIT(direction);
10740 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
10741 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
10742 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
10744 Feld[x][y] = EL_ELEMENT_SNAPPING;
10745 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
10747 ResetGfxAnimation(x, y);
10749 GfxElement[x][y] = element;
10750 GfxAction[x][y] = action;
10751 GfxDir[x][y] = direction;
10752 GfxFrame[x][y] = -1;
10757 =============================================================================
10758 checkDiagonalPushing()
10759 -----------------------------------------------------------------------------
10760 check if diagonal input device direction results in pushing of object
10761 (by checking if the alternative direction is walkable, diggable, ...)
10762 =============================================================================
10765 static boolean checkDiagonalPushing(struct PlayerInfo *player,
10766 int x, int y, int real_dx, int real_dy)
10768 int jx, jy, dx, dy, xx, yy;
10770 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
10773 /* diagonal direction: check alternative direction */
10778 xx = jx + (dx == 0 ? real_dx : 0);
10779 yy = jy + (dy == 0 ? real_dy : 0);
10781 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
10785 =============================================================================
10787 -----------------------------------------------------------------------------
10788 x, y: field next to player (non-diagonal) to try to dig to
10789 real_dx, real_dy: direction as read from input device (can be diagonal)
10790 =============================================================================
10793 int DigField(struct PlayerInfo *player,
10794 int oldx, int oldy, int x, int y,
10795 int real_dx, int real_dy, int mode)
10797 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
10798 boolean player_was_pushing = player->is_pushing;
10799 boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
10800 boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
10801 int jx = oldx, jy = oldy;
10802 int dx = x - jx, dy = y - jy;
10803 int nextx = x + dx, nexty = y + dy;
10804 int move_direction = (dx == -1 ? MV_LEFT :
10805 dx == +1 ? MV_RIGHT :
10807 dy == +1 ? MV_DOWN : MV_NONE);
10808 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
10809 int dig_side = MV_DIR_OPPOSITE(move_direction);
10810 int old_element = Feld[jx][jy];
10811 #if USE_FIXED_DONT_RUN_INTO
10812 int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
10818 if (is_player) /* function can also be called by EL_PENGUIN */
10820 if (player->MovPos == 0)
10822 player->is_digging = FALSE;
10823 player->is_collecting = FALSE;
10826 if (player->MovPos == 0) /* last pushing move finished */
10827 player->is_pushing = FALSE;
10829 if (mode == DF_NO_PUSH) /* player just stopped pushing */
10831 player->is_switching = FALSE;
10832 player->push_delay = -1;
10834 return MP_NO_ACTION;
10838 #if !USE_FIXED_DONT_RUN_INTO
10839 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
10840 return MP_NO_ACTION;
10843 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
10844 old_element = Back[jx][jy];
10846 /* in case of element dropped at player position, check background */
10847 else if (Back[jx][jy] != EL_EMPTY &&
10848 game.engine_version >= VERSION_IDENT(2,2,0,0))
10849 old_element = Back[jx][jy];
10852 #if USE_FIXED_DONT_RUN_INTO
10853 if (player_can_move && DONT_RUN_INTO(element))
10855 if (element == EL_ACID && dx == 0 && dy == 1)
10858 Feld[jx][jy] = EL_PLAYER_1;
10859 InitMovingField(jx, jy, MV_DOWN);
10860 Store[jx][jy] = EL_ACID;
10861 ContinueMoving(jx, jy);
10862 BuryPlayer(player);
10865 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
10867 return MP_DONT_RUN_INTO;
10873 #if USE_FIXED_DONT_RUN_INTO
10874 if (player_can_move && DONT_RUN_INTO(element))
10876 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
10878 return MP_DONT_RUN_INTO;
10883 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
10884 return MP_NO_ACTION; /* field has no opening in this direction */
10886 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
10887 return MP_NO_ACTION; /* field has no opening in this direction */
10890 #if USE_FIXED_DONT_RUN_INTO
10891 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
10894 Feld[jx][jy] = EL_PLAYER_1;
10895 InitMovingField(jx, jy, MV_DOWN);
10896 Store[jx][jy] = EL_ACID;
10897 ContinueMoving(jx, jy);
10898 BuryPlayer(player);
10900 return MP_DONT_RUN_INTO;
10906 #if USE_FIXED_DONT_RUN_INTO
10907 if (player_can_move && DONT_RUN_INTO(element))
10909 if (element == EL_ACID && dx == 0 && dy == 1)
10912 Feld[jx][jy] = EL_PLAYER_1;
10913 InitMovingField(jx, jy, MV_DOWN);
10914 Store[jx][jy] = EL_ACID;
10915 ContinueMoving(jx, jy);
10916 BuryPlayer(player);
10919 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
10921 return MP_DONT_RUN_INTO;
10926 #if USE_FIXED_DONT_RUN_INTO
10927 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
10928 return MP_NO_ACTION;
10931 #if !USE_FIXED_DONT_RUN_INTO
10932 element = Feld[x][y];
10935 collect_count = element_info[element].collect_count_initial;
10937 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
10938 return MP_NO_ACTION;
10940 if (game.engine_version < VERSION_IDENT(2,2,0,0))
10941 player_can_move = player_can_move_or_snap;
10943 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
10944 game.engine_version >= VERSION_IDENT(2,2,0,0))
10946 CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
10947 player->index_bit, dig_side);
10948 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
10949 player->index_bit, dig_side);
10951 if (Feld[x][y] != element) /* field changed by snapping */
10954 return MP_NO_ACTION;
10957 if (game.gravity && is_player && !player->is_auto_moving &&
10958 canFallDown(player) && move_direction != MV_DOWN &&
10959 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
10960 return MP_NO_ACTION; /* player cannot walk here due to gravity */
10962 if (player_can_move &&
10963 IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
10965 int sound_element = SND_ELEMENT(element);
10966 int sound_action = ACTION_WALKING;
10968 if (IS_RND_GATE(element))
10970 if (!player->key[RND_GATE_NR(element)])
10971 return MP_NO_ACTION;
10973 else if (IS_RND_GATE_GRAY(element))
10975 if (!player->key[RND_GATE_GRAY_NR(element)])
10976 return MP_NO_ACTION;
10978 else if (IS_RND_GATE_GRAY_ACTIVE(element))
10980 if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
10981 return MP_NO_ACTION;
10983 else if (element == EL_EXIT_OPEN ||
10984 element == EL_SP_EXIT_OPEN ||
10985 element == EL_SP_EXIT_OPENING)
10987 sound_action = ACTION_PASSING; /* player is passing exit */
10989 else if (element == EL_EMPTY)
10991 sound_action = ACTION_MOVING; /* nothing to walk on */
10994 /* play sound from background or player, whatever is available */
10995 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
10996 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
10998 PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
11000 else if (player_can_move &&
11001 IS_PASSABLE(element) && canPassField(x, y, move_direction))
11003 if (!ACCESS_FROM(element, opposite_direction))
11004 return MP_NO_ACTION; /* field not accessible from this direction */
11006 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
11007 return MP_NO_ACTION;
11009 if (IS_EM_GATE(element))
11011 if (!player->key[EM_GATE_NR(element)])
11012 return MP_NO_ACTION;
11014 else if (IS_EM_GATE_GRAY(element))
11016 if (!player->key[EM_GATE_GRAY_NR(element)])
11017 return MP_NO_ACTION;
11019 else if (IS_EM_GATE_GRAY_ACTIVE(element))
11021 if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
11022 return MP_NO_ACTION;
11024 else if (IS_SP_PORT(element))
11026 if (element == EL_SP_GRAVITY_PORT_LEFT ||
11027 element == EL_SP_GRAVITY_PORT_RIGHT ||
11028 element == EL_SP_GRAVITY_PORT_UP ||
11029 element == EL_SP_GRAVITY_PORT_DOWN)
11030 game.gravity = !game.gravity;
11031 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
11032 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
11033 element == EL_SP_GRAVITY_ON_PORT_UP ||
11034 element == EL_SP_GRAVITY_ON_PORT_DOWN)
11035 game.gravity = TRUE;
11036 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
11037 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
11038 element == EL_SP_GRAVITY_OFF_PORT_UP ||
11039 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
11040 game.gravity = FALSE;
11043 /* automatically move to the next field with double speed */
11044 player->programmed_action = move_direction;
11046 if (player->move_delay_reset_counter == 0)
11048 player->move_delay_reset_counter = 2; /* two double speed steps */
11050 DOUBLE_PLAYER_SPEED(player);
11053 PlayLevelSoundAction(x, y, ACTION_PASSING);
11055 else if (player_can_move_or_snap && IS_DIGGABLE(element))
11059 if (mode != DF_SNAP)
11061 GfxElement[x][y] = GFX_ELEMENT(element);
11062 player->is_digging = TRUE;
11065 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
11067 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
11068 player->index_bit, dig_side);
11070 if (mode == DF_SNAP)
11072 #if USE_NEW_SNAP_DELAY
11073 if (level.block_snap_field)
11074 setFieldForSnapping(x, y, element, move_direction);
11076 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11078 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11081 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
11082 player->index_bit, dig_side);
11085 else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
11089 if (is_player && mode != DF_SNAP)
11091 GfxElement[x][y] = element;
11092 player->is_collecting = TRUE;
11095 if (element == EL_SPEED_PILL)
11097 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
11099 else if (element == EL_EXTRA_TIME && level.time > 0)
11101 TimeLeft += level.extra_time;
11102 DrawGameValue_Time(TimeLeft);
11104 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
11106 player->shield_normal_time_left += level.shield_normal_time;
11107 if (element == EL_SHIELD_DEADLY)
11108 player->shield_deadly_time_left += level.shield_deadly_time;
11110 else if (element == EL_DYNAMITE || element == EL_SP_DISK_RED)
11112 if (player->inventory_size < MAX_INVENTORY_SIZE)
11113 player->inventory_element[player->inventory_size++] = element;
11115 DrawGameValue_Dynamite(local_player->inventory_size);
11117 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
11119 player->dynabomb_count++;
11120 player->dynabombs_left++;
11122 else if (element == EL_DYNABOMB_INCREASE_SIZE)
11124 player->dynabomb_size++;
11126 else if (element == EL_DYNABOMB_INCREASE_POWER)
11128 player->dynabomb_xl = TRUE;
11130 else if (IS_KEY(element))
11132 player->key[KEY_NR(element)] = TRUE;
11134 DrawGameValue_Keys(player->key);
11136 redraw_mask |= REDRAW_DOOR_1;
11138 else if (IS_ENVELOPE(element))
11140 player->show_envelope = element;
11142 else if (element == EL_EMC_LENSES)
11144 game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
11146 RedrawAllInvisibleElementsForLenses();
11148 else if (element == EL_EMC_MAGNIFIER)
11150 game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
11152 RedrawAllInvisibleElementsForMagnifier();
11154 else if (IS_DROPPABLE(element) ||
11155 IS_THROWABLE(element)) /* can be collected and dropped */
11159 if (collect_count == 0)
11160 player->inventory_infinite_element = element;
11162 for (i = 0; i < collect_count; i++)
11163 if (player->inventory_size < MAX_INVENTORY_SIZE)
11164 player->inventory_element[player->inventory_size++] = element;
11166 DrawGameValue_Dynamite(local_player->inventory_size);
11168 else if (collect_count > 0)
11170 local_player->gems_still_needed -= collect_count;
11171 if (local_player->gems_still_needed < 0)
11172 local_player->gems_still_needed = 0;
11174 DrawGameValue_Emeralds(local_player->gems_still_needed);
11177 RaiseScoreElement(element);
11178 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
11181 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
11182 player->index_bit, dig_side);
11184 if (mode == DF_SNAP)
11186 #if USE_NEW_SNAP_DELAY
11187 if (level.block_snap_field)
11188 setFieldForSnapping(x, y, element, move_direction);
11190 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11192 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11195 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
11196 player->index_bit, dig_side);
11199 else if (player_can_move_or_snap && IS_PUSHABLE(element))
11201 if (mode == DF_SNAP && element != EL_BD_ROCK)
11202 return MP_NO_ACTION;
11204 if (CAN_FALL(element) && dy)
11205 return MP_NO_ACTION;
11207 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
11208 !(element == EL_SPRING && level.use_spring_bug))
11209 return MP_NO_ACTION;
11211 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
11212 ((move_direction & MV_VERTICAL &&
11213 ((element_info[element].move_pattern & MV_LEFT &&
11214 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
11215 (element_info[element].move_pattern & MV_RIGHT &&
11216 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
11217 (move_direction & MV_HORIZONTAL &&
11218 ((element_info[element].move_pattern & MV_UP &&
11219 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
11220 (element_info[element].move_pattern & MV_DOWN &&
11221 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
11222 return MP_NO_ACTION;
11224 /* do not push elements already moving away faster than player */
11225 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
11226 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
11227 return MP_NO_ACTION;
11229 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
11231 if (player->push_delay_value == -1 || !player_was_pushing)
11232 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11234 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11236 if (player->push_delay_value == -1)
11237 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11239 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
11241 if (!player->is_pushing)
11242 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11245 player->is_pushing = TRUE;
11247 if (!(IN_LEV_FIELD(nextx, nexty) &&
11248 (IS_FREE(nextx, nexty) ||
11249 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
11250 IS_SB_ELEMENT(element)))))
11251 return MP_NO_ACTION;
11253 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
11254 return MP_NO_ACTION;
11256 if (player->push_delay == -1) /* new pushing; restart delay */
11257 player->push_delay = 0;
11259 if (player->push_delay < player->push_delay_value &&
11260 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
11261 element != EL_SPRING && element != EL_BALLOON)
11263 /* make sure that there is no move delay before next try to push */
11264 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11265 player->move_delay = 0;
11267 return MP_NO_ACTION;
11270 if (IS_SB_ELEMENT(element))
11272 if (element == EL_SOKOBAN_FIELD_FULL)
11274 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
11275 local_player->sokobanfields_still_needed++;
11278 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
11280 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
11281 local_player->sokobanfields_still_needed--;
11284 Feld[x][y] = EL_SOKOBAN_OBJECT;
11286 if (Back[x][y] == Back[nextx][nexty])
11287 PlayLevelSoundAction(x, y, ACTION_PUSHING);
11288 else if (Back[x][y] != 0)
11289 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
11292 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
11295 if (local_player->sokobanfields_still_needed == 0 &&
11296 game.emulation == EMU_SOKOBAN)
11298 player->LevelSolved = player->GameOver = TRUE;
11299 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
11303 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
11305 InitMovingField(x, y, move_direction);
11306 GfxAction[x][y] = ACTION_PUSHING;
11308 if (mode == DF_SNAP)
11309 ContinueMoving(x, y);
11311 MovPos[x][y] = (dx != 0 ? dx : dy);
11313 Pushed[x][y] = TRUE;
11314 Pushed[nextx][nexty] = TRUE;
11316 if (game.engine_version < VERSION_IDENT(2,2,0,7))
11317 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11319 player->push_delay_value = -1; /* get new value later */
11321 /* check for element change _after_ element has been pushed */
11322 if (game.use_change_when_pushing_bug)
11324 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
11325 player->index_bit, dig_side);
11326 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
11327 player->index_bit, dig_side);
11330 else if (IS_SWITCHABLE(element))
11332 if (PLAYER_SWITCHING(player, x, y))
11334 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
11335 player->index_bit, dig_side);
11340 player->is_switching = TRUE;
11341 player->switch_x = x;
11342 player->switch_y = y;
11344 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
11346 if (element == EL_ROBOT_WHEEL)
11348 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
11352 DrawLevelField(x, y);
11354 else if (element == EL_SP_TERMINAL)
11359 SCAN_PLAYFIELD(xx, yy)
11361 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
11364 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
11366 else if (Feld[xx][yy] == EL_SP_TERMINAL)
11367 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
11370 else if (IS_BELT_SWITCH(element))
11372 ToggleBeltSwitch(x, y);
11374 else if (element == EL_SWITCHGATE_SWITCH_UP ||
11375 element == EL_SWITCHGATE_SWITCH_DOWN)
11377 ToggleSwitchgateSwitch(x, y);
11379 else if (element == EL_LIGHT_SWITCH ||
11380 element == EL_LIGHT_SWITCH_ACTIVE)
11382 ToggleLightSwitch(x, y);
11384 else if (element == EL_TIMEGATE_SWITCH)
11386 ActivateTimegateSwitch(x, y);
11388 else if (element == EL_BALLOON_SWITCH_LEFT ||
11389 element == EL_BALLOON_SWITCH_RIGHT ||
11390 element == EL_BALLOON_SWITCH_UP ||
11391 element == EL_BALLOON_SWITCH_DOWN ||
11392 element == EL_BALLOON_SWITCH_NONE ||
11393 element == EL_BALLOON_SWITCH_ANY)
11395 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
11396 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
11397 element == EL_BALLOON_SWITCH_UP ? MV_UP :
11398 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
11399 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
11402 else if (element == EL_LAMP)
11404 Feld[x][y] = EL_LAMP_ACTIVE;
11405 local_player->lights_still_needed--;
11407 ResetGfxAnimation(x, y);
11408 DrawLevelField(x, y);
11410 else if (element == EL_TIME_ORB_FULL)
11412 Feld[x][y] = EL_TIME_ORB_EMPTY;
11414 if (level.time > 0 || level.use_time_orb_bug)
11416 TimeLeft += level.time_orb_time;
11417 DrawGameValue_Time(TimeLeft);
11420 ResetGfxAnimation(x, y);
11421 DrawLevelField(x, y);
11423 else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
11424 element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
11428 game.ball_state = !game.ball_state;
11431 SCAN_PLAYFIELD(xx, yy)
11433 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
11436 int e = Feld[xx][yy];
11438 if (game.ball_state)
11440 if (e == EL_EMC_MAGIC_BALL)
11441 CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
11442 else if (e == EL_EMC_MAGIC_BALL_SWITCH)
11443 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
11447 if (e == EL_EMC_MAGIC_BALL_ACTIVE)
11448 CreateField(xx, yy, EL_EMC_MAGIC_BALL);
11449 else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
11450 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
11455 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
11456 player->index_bit, dig_side);
11458 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
11459 player->index_bit, dig_side);
11461 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
11462 player->index_bit, dig_side);
11468 if (!PLAYER_SWITCHING(player, x, y))
11470 player->is_switching = TRUE;
11471 player->switch_x = x;
11472 player->switch_y = y;
11474 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
11475 player->index_bit, dig_side);
11476 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
11477 player->index_bit, dig_side);
11479 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
11480 player->index_bit, dig_side);
11481 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
11482 player->index_bit, dig_side);
11485 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
11486 player->index_bit, dig_side);
11487 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
11488 player->index_bit, dig_side);
11490 return MP_NO_ACTION;
11493 player->push_delay = -1;
11495 if (is_player) /* function can also be called by EL_PENGUIN */
11497 if (Feld[x][y] != element) /* really digged/collected something */
11498 player->is_collecting = !player->is_digging;
11504 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
11506 int jx = player->jx, jy = player->jy;
11507 int x = jx + dx, y = jy + dy;
11508 int snap_direction = (dx == -1 ? MV_LEFT :
11509 dx == +1 ? MV_RIGHT :
11511 dy == +1 ? MV_DOWN : MV_NONE);
11513 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
11516 if (!player->active || !IN_LEV_FIELD(x, y))
11524 if (player->MovPos == 0)
11525 player->is_pushing = FALSE;
11527 player->is_snapping = FALSE;
11529 if (player->MovPos == 0)
11531 player->is_moving = FALSE;
11532 player->is_digging = FALSE;
11533 player->is_collecting = FALSE;
11539 if (player->is_snapping)
11542 player->MovDir = snap_direction;
11544 if (player->MovPos == 0)
11546 player->is_moving = FALSE;
11547 player->is_digging = FALSE;
11548 player->is_collecting = FALSE;
11551 player->is_dropping = FALSE;
11553 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
11556 player->is_snapping = TRUE;
11558 if (player->MovPos == 0)
11560 player->is_moving = FALSE;
11561 player->is_digging = FALSE;
11562 player->is_collecting = FALSE;
11565 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
11566 DrawLevelField(player->last_jx, player->last_jy);
11568 DrawLevelField(x, y);
11573 boolean DropElement(struct PlayerInfo *player)
11575 int old_element, new_element;
11576 int dropx = player->jx, dropy = player->jy;
11577 int drop_direction = player->MovDir;
11578 int drop_side = drop_direction;
11579 int drop_element = (player->inventory_size > 0 ?
11580 player->inventory_element[player->inventory_size - 1] :
11581 player->inventory_infinite_element != EL_UNDEFINED ?
11582 player->inventory_infinite_element :
11583 player->dynabombs_left > 0 ?
11584 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
11587 /* do not drop an element on top of another element; when holding drop key
11588 pressed without moving, dropped element must move away before the next
11589 element can be dropped (this is especially important if the next element
11590 is dynamite, which can be placed on background for historical reasons) */
11591 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
11594 if (IS_THROWABLE(drop_element))
11596 dropx += GET_DX_FROM_DIR(drop_direction);
11597 dropy += GET_DY_FROM_DIR(drop_direction);
11599 if (!IN_LEV_FIELD(dropx, dropy))
11603 old_element = Feld[dropx][dropy]; /* old element at dropping position */
11604 new_element = drop_element; /* default: no change when dropping */
11606 /* check if player is active, not moving and ready to drop */
11607 if (!player->active || player->MovPos || player->drop_delay > 0)
11610 /* check if player has anything that can be dropped */
11611 if (new_element == EL_UNDEFINED)
11614 /* check if anything can be dropped at the current position */
11615 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
11618 /* collected custom elements can only be dropped on empty fields */
11619 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
11622 if (old_element != EL_EMPTY)
11623 Back[dropx][dropy] = old_element; /* store old element on this field */
11625 ResetGfxAnimation(dropx, dropy);
11626 ResetRandomAnimationValue(dropx, dropy);
11628 if (player->inventory_size > 0 ||
11629 player->inventory_infinite_element != EL_UNDEFINED)
11631 if (player->inventory_size > 0)
11633 player->inventory_size--;
11635 DrawGameValue_Dynamite(local_player->inventory_size);
11637 if (new_element == EL_DYNAMITE)
11638 new_element = EL_DYNAMITE_ACTIVE;
11639 else if (new_element == EL_SP_DISK_RED)
11640 new_element = EL_SP_DISK_RED_ACTIVE;
11643 Feld[dropx][dropy] = new_element;
11645 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
11646 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
11647 el2img(Feld[dropx][dropy]), 0);
11649 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
11651 /* needed if previous element just changed to "empty" in the last frame */
11652 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
11654 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
11655 player->index_bit, drop_side);
11656 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
11658 player->index_bit, drop_side);
11660 TestIfElementTouchesCustomElement(dropx, dropy);
11662 else /* player is dropping a dyna bomb */
11664 player->dynabombs_left--;
11666 Feld[dropx][dropy] = new_element;
11668 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
11669 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
11670 el2img(Feld[dropx][dropy]), 0);
11672 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
11675 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
11676 InitField_WithBug1(dropx, dropy, FALSE);
11678 new_element = Feld[dropx][dropy]; /* element might have changed */
11680 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
11681 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
11683 int move_direction, nextx, nexty;
11685 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
11686 MovDir[dropx][dropy] = drop_direction;
11688 move_direction = MovDir[dropx][dropy];
11689 nextx = dropx + GET_DX_FROM_DIR(move_direction);
11690 nexty = dropy + GET_DY_FROM_DIR(move_direction);
11692 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
11693 CheckCollision[dropx][dropy] = 2;
11696 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
11697 player->is_dropping = TRUE;
11699 player->drop_x = dropx;
11700 player->drop_y = dropy;
11705 /* ------------------------------------------------------------------------- */
11706 /* game sound playing functions */
11707 /* ------------------------------------------------------------------------- */
11709 static int *loop_sound_frame = NULL;
11710 static int *loop_sound_volume = NULL;
11712 void InitPlayLevelSound()
11714 int num_sounds = getSoundListSize();
11716 checked_free(loop_sound_frame);
11717 checked_free(loop_sound_volume);
11719 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
11720 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
11723 static void PlayLevelSound(int x, int y, int nr)
11725 int sx = SCREENX(x), sy = SCREENY(y);
11726 int volume, stereo_position;
11727 int max_distance = 8;
11728 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
11730 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
11731 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
11734 if (!IN_LEV_FIELD(x, y) ||
11735 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
11736 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
11739 volume = SOUND_MAX_VOLUME;
11741 if (!IN_SCR_FIELD(sx, sy))
11743 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
11744 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
11746 volume -= volume * (dx > dy ? dx : dy) / max_distance;
11749 stereo_position = (SOUND_MAX_LEFT +
11750 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
11751 (SCR_FIELDX + 2 * max_distance));
11753 if (IS_LOOP_SOUND(nr))
11755 /* This assures that quieter loop sounds do not overwrite louder ones,
11756 while restarting sound volume comparison with each new game frame. */
11758 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
11761 loop_sound_volume[nr] = volume;
11762 loop_sound_frame[nr] = FrameCounter;
11765 PlaySoundExt(nr, volume, stereo_position, type);
11768 static void PlayLevelSoundNearest(int x, int y, int sound_action)
11770 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
11771 x > LEVELX(BX2) ? LEVELX(BX2) : x,
11772 y < LEVELY(BY1) ? LEVELY(BY1) :
11773 y > LEVELY(BY2) ? LEVELY(BY2) : y,
11777 static void PlayLevelSoundAction(int x, int y, int action)
11779 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
11782 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
11784 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
11786 if (sound_effect != SND_UNDEFINED)
11787 PlayLevelSound(x, y, sound_effect);
11790 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
11793 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
11795 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
11796 PlayLevelSound(x, y, sound_effect);
11799 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
11801 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
11803 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
11804 PlayLevelSound(x, y, sound_effect);
11807 static void StopLevelSoundActionIfLoop(int x, int y, int action)
11809 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
11811 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
11812 StopSound(sound_effect);
11815 static void PlayLevelMusic()
11817 if (levelset.music[level_nr] != MUS_UNDEFINED)
11818 PlayMusic(levelset.music[level_nr]); /* from config file */
11820 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
11823 void PlayLevelSound_EM(int x, int y, int element_em, int sample)
11825 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
11830 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
11834 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
11838 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
11842 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
11846 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
11850 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
11854 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
11857 case SAMPLE_android_clone:
11858 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
11861 case SAMPLE_android_move:
11862 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
11865 case SAMPLE_spring:
11866 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
11870 PlayLevelSoundElementAction(x, y, element, ACTION_SLURPING);
11874 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
11877 case SAMPLE_eater_eat:
11878 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
11882 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
11885 case SAMPLE_collect:
11886 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
11889 case SAMPLE_diamond:
11890 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
11893 case SAMPLE_squash:
11894 /* !!! CHECK THIS !!! */
11896 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
11898 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
11902 case SAMPLE_wonderfall:
11903 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
11907 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
11911 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
11915 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
11919 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
11923 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
11927 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
11930 case SAMPLE_wonder:
11931 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
11935 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
11938 case SAMPLE_exit_open:
11939 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
11942 case SAMPLE_exit_leave:
11943 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
11946 case SAMPLE_dynamite:
11947 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
11951 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
11955 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
11959 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
11963 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
11967 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
11971 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
11975 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
11980 void RaiseScore(int value)
11982 local_player->score += value;
11984 DrawGameValue_Score(local_player->score);
11987 void RaiseScoreElement(int element)
11992 case EL_BD_DIAMOND:
11993 case EL_EMERALD_YELLOW:
11994 case EL_EMERALD_RED:
11995 case EL_EMERALD_PURPLE:
11996 case EL_SP_INFOTRON:
11997 RaiseScore(level.score[SC_EMERALD]);
12000 RaiseScore(level.score[SC_DIAMOND]);
12003 RaiseScore(level.score[SC_CRYSTAL]);
12006 RaiseScore(level.score[SC_PEARL]);
12009 case EL_BD_BUTTERFLY:
12010 case EL_SP_ELECTRON:
12011 RaiseScore(level.score[SC_BUG]);
12014 case EL_BD_FIREFLY:
12015 case EL_SP_SNIKSNAK:
12016 RaiseScore(level.score[SC_SPACESHIP]);
12019 case EL_DARK_YAMYAM:
12020 RaiseScore(level.score[SC_YAMYAM]);
12023 RaiseScore(level.score[SC_ROBOT]);
12026 RaiseScore(level.score[SC_PACMAN]);
12029 RaiseScore(level.score[SC_NUT]);
12032 case EL_SP_DISK_RED:
12033 case EL_DYNABOMB_INCREASE_NUMBER:
12034 case EL_DYNABOMB_INCREASE_SIZE:
12035 case EL_DYNABOMB_INCREASE_POWER:
12036 RaiseScore(level.score[SC_DYNAMITE]);
12038 case EL_SHIELD_NORMAL:
12039 case EL_SHIELD_DEADLY:
12040 RaiseScore(level.score[SC_SHIELD]);
12042 case EL_EXTRA_TIME:
12043 RaiseScore(level.extra_time_score);
12057 RaiseScore(level.score[SC_KEY]);
12060 RaiseScore(element_info[element].collect_score);
12065 void RequestQuitGame(boolean ask_if_really_quit)
12067 if (AllPlayersGone ||
12068 !ask_if_really_quit ||
12069 level_editor_test_game ||
12070 Request("Do you really want to quit the game ?",
12071 REQ_ASK | REQ_STAY_CLOSED))
12073 #if defined(NETWORK_AVALIABLE)
12074 if (options.network)
12075 SendToServer_StopPlaying();
12079 game_status = GAME_MODE_MAIN;
12085 if (tape.playing && tape.deactivate_display)
12086 TapeDeactivateDisplayOff(TRUE);
12088 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
12090 if (tape.playing && tape.deactivate_display)
12091 TapeDeactivateDisplayOn();
12096 /* ---------- new game button stuff ---------------------------------------- */
12098 /* graphic position values for game buttons */
12099 #define GAME_BUTTON_XSIZE 30
12100 #define GAME_BUTTON_YSIZE 30
12101 #define GAME_BUTTON_XPOS 5
12102 #define GAME_BUTTON_YPOS 215
12103 #define SOUND_BUTTON_XPOS 5
12104 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
12106 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
12107 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
12108 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
12109 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
12110 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
12111 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
12118 } gamebutton_info[NUM_GAME_BUTTONS] =
12121 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
12126 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
12127 GAME_CTRL_ID_PAUSE,
12131 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
12136 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
12137 SOUND_CTRL_ID_MUSIC,
12138 "background music on/off"
12141 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
12142 SOUND_CTRL_ID_LOOPS,
12143 "sound loops on/off"
12146 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
12147 SOUND_CTRL_ID_SIMPLE,
12148 "normal sounds on/off"
12152 void CreateGameButtons()
12156 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12158 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
12159 struct GadgetInfo *gi;
12162 unsigned long event_mask;
12163 int gd_xoffset, gd_yoffset;
12164 int gd_x1, gd_x2, gd_y1, gd_y2;
12167 gd_xoffset = gamebutton_info[i].x;
12168 gd_yoffset = gamebutton_info[i].y;
12169 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
12170 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
12172 if (id == GAME_CTRL_ID_STOP ||
12173 id == GAME_CTRL_ID_PAUSE ||
12174 id == GAME_CTRL_ID_PLAY)
12176 button_type = GD_TYPE_NORMAL_BUTTON;
12178 event_mask = GD_EVENT_RELEASED;
12179 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12180 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12184 button_type = GD_TYPE_CHECK_BUTTON;
12186 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
12187 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
12188 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
12189 event_mask = GD_EVENT_PRESSED;
12190 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
12191 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12194 gi = CreateGadget(GDI_CUSTOM_ID, id,
12195 GDI_INFO_TEXT, gamebutton_info[i].infotext,
12196 GDI_X, DX + gd_xoffset,
12197 GDI_Y, DY + gd_yoffset,
12198 GDI_WIDTH, GAME_BUTTON_XSIZE,
12199 GDI_HEIGHT, GAME_BUTTON_YSIZE,
12200 GDI_TYPE, button_type,
12201 GDI_STATE, GD_BUTTON_UNPRESSED,
12202 GDI_CHECKED, checked,
12203 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
12204 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
12205 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
12206 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
12207 GDI_EVENT_MASK, event_mask,
12208 GDI_CALLBACK_ACTION, HandleGameButtons,
12212 Error(ERR_EXIT, "cannot create gadget");
12214 game_gadget[id] = gi;
12218 void FreeGameButtons()
12222 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12223 FreeGadget(game_gadget[i]);
12226 static void MapGameButtons()
12230 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12231 MapGadget(game_gadget[i]);
12234 void UnmapGameButtons()
12238 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12239 UnmapGadget(game_gadget[i]);
12242 static void HandleGameButtons(struct GadgetInfo *gi)
12244 int id = gi->custom_id;
12246 if (game_status != GAME_MODE_PLAYING)
12251 case GAME_CTRL_ID_STOP:
12252 RequestQuitGame(TRUE);
12255 case GAME_CTRL_ID_PAUSE:
12256 if (options.network)
12258 #if defined(NETWORK_AVALIABLE)
12260 SendToServer_ContinuePlaying();
12262 SendToServer_PausePlaying();
12266 TapeTogglePause(TAPE_TOGGLE_MANUAL);
12269 case GAME_CTRL_ID_PLAY:
12272 #if defined(NETWORK_AVALIABLE)
12273 if (options.network)
12274 SendToServer_ContinuePlaying();
12278 tape.pausing = FALSE;
12279 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
12284 case SOUND_CTRL_ID_MUSIC:
12285 if (setup.sound_music)
12287 setup.sound_music = FALSE;
12290 else if (audio.music_available)
12292 setup.sound = setup.sound_music = TRUE;
12294 SetAudioMode(setup.sound);
12300 case SOUND_CTRL_ID_LOOPS:
12301 if (setup.sound_loops)
12302 setup.sound_loops = FALSE;
12303 else if (audio.loops_available)
12305 setup.sound = setup.sound_loops = TRUE;
12306 SetAudioMode(setup.sound);
12310 case SOUND_CTRL_ID_SIMPLE:
12311 if (setup.sound_simple)
12312 setup.sound_simple = FALSE;
12313 else if (audio.sound_available)
12315 setup.sound = setup.sound_simple = TRUE;
12316 SetAudioMode(setup.sound);