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:
990 case EL_EM_DYNAMITE_ACTIVE:
995 local_player->lights_still_needed++;
999 local_player->friends_still_needed++;
1004 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1007 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1008 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1009 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1010 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1011 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1012 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1013 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1014 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1015 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1016 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1017 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1018 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1021 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1022 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1023 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1025 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
1027 game.belt_dir[belt_nr] = belt_dir;
1028 game.belt_dir_nr[belt_nr] = belt_dir_nr;
1030 else /* more than one switch -- set it like the first switch */
1032 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1037 case EL_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
1039 Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
1042 case EL_LIGHT_SWITCH_ACTIVE:
1044 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1047 case EL_EMC_MAGIC_BALL:
1048 if (game.ball_state)
1049 Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1052 case EL_EMC_MAGIC_BALL_SWITCH:
1053 if (game.ball_state)
1054 Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1059 if (IS_CUSTOM_ELEMENT(element))
1061 if (CAN_MOVE(element))
1064 #if USE_NEW_CUSTOM_VALUE
1065 if (!element_info[element].use_last_ce_value || init_game)
1066 CustomValue[x][y] = GET_NEW_CUSTOM_VALUE(Feld[x][y]);
1070 if (IS_CUSTOM_ELEMENT(element) && CAN_MOVE(element))
1073 else if (IS_GROUP_ELEMENT(element))
1075 struct ElementGroupInfo *group = element_info[element].group;
1076 int last_anim_random_frame = gfx.anim_random_frame;
1079 if (group->choice_mode == ANIM_RANDOM)
1080 gfx.anim_random_frame = RND(group->num_elements_resolved);
1082 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1083 group->choice_mode, 0,
1086 if (group->choice_mode == ANIM_RANDOM)
1087 gfx.anim_random_frame = last_anim_random_frame;
1089 group->choice_pos++;
1091 Feld[x][y] = group->element_resolved[element_pos];
1093 InitField(x, y, init_game);
1100 CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1105 #if USE_NEW_CUSTOM_VALUE
1108 CustomValue[x][y] = GET_NEW_CUSTOM_VALUE(Feld[x][y]);
1110 CustomValue[x][y] = element_info[Feld[x][y]].custom_value_initial;
1118 static inline void InitField_WithBug1(int x, int y, boolean init_game)
1120 InitField(x, y, init_game);
1122 /* not needed to call InitMovDir() -- already done by InitField()! */
1123 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1124 CAN_MOVE(Feld[x][y]))
1128 static inline void InitField_WithBug2(int x, int y, boolean init_game)
1130 int old_element = Feld[x][y];
1132 InitField(x, y, init_game);
1134 /* not needed to call InitMovDir() -- already done by InitField()! */
1135 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1136 CAN_MOVE(old_element) &&
1137 (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1140 /* this case is in fact a combination of not less than three bugs:
1141 first, it calls InitMovDir() for elements that can move, although this is
1142 already done by InitField(); then, it checks the element that was at this
1143 field _before_ the call to InitField() (which can change it); lastly, it
1144 was not called for "mole with direction" elements, which were treated as
1145 "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1149 inline void DrawGameValue_Emeralds(int value)
1151 DrawText(DX_EMERALDS, DY_EMERALDS, int2str(value, 3), FONT_TEXT_2);
1154 inline void DrawGameValue_Dynamite(int value)
1156 DrawText(DX_DYNAMITE, DY_DYNAMITE, int2str(value, 3), FONT_TEXT_2);
1159 inline void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
1163 /* currently only 4 of 8 possible keys are displayed */
1164 for (i = 0; i < STD_NUM_KEYS; i++)
1167 DrawMiniGraphicExt(drawto, DX_KEYS + i * MINI_TILEX, DY_KEYS,
1168 el2edimg(EL_KEY_1 + i));
1170 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
1171 DOOR_GFX_PAGEX5 + XX_KEYS + i * MINI_TILEX, YY_KEYS,
1172 MINI_TILEX, MINI_TILEY, DX_KEYS + i * MINI_TILEX, DY_KEYS);
1176 inline void DrawGameValue_Score(int value)
1178 DrawText(DX_SCORE, DY_SCORE, int2str(value, 5), FONT_TEXT_2);
1181 inline void DrawGameValue_Time(int value)
1184 DrawText(DX_TIME1, DY_TIME, int2str(value, 3), FONT_TEXT_2);
1186 DrawText(DX_TIME2, DY_TIME, int2str(value, 4), FONT_LEVEL_NUMBER);
1189 inline void DrawGameValue_Level(int value)
1192 DrawText(DX_LEVEL, DY_LEVEL, int2str(value, 2), FONT_TEXT_2);
1195 /* misuse area for displaying emeralds to draw bigger level number */
1196 DrawTextExt(drawto, DX_EMERALDS, DY_EMERALDS,
1197 int2str(value, 3), FONT_LEVEL_NUMBER, BLIT_OPAQUE);
1199 /* now copy it to the area for displaying level number */
1200 BlitBitmap(drawto, drawto,
1201 DX_EMERALDS, DY_EMERALDS + 1,
1202 getFontWidth(FONT_LEVEL_NUMBER) * 3,
1203 getFontHeight(FONT_LEVEL_NUMBER) - 1,
1204 DX_LEVEL - 1, DY_LEVEL + 1);
1206 /* restore the area for displaying emeralds */
1207 DrawGameValue_Emeralds(local_player->gems_still_needed);
1209 /* yes, this is all really ugly :-) */
1213 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
1216 int key[MAX_NUM_KEYS];
1219 for (i = 0; i < MAX_NUM_KEYS; i++)
1220 key[i] = key_bits & (1 << i);
1222 DrawGameValue_Level(level_nr);
1224 DrawGameValue_Emeralds(emeralds);
1225 DrawGameValue_Dynamite(dynamite);
1226 DrawGameValue_Score(score);
1227 DrawGameValue_Time(time);
1229 DrawGameValue_Keys(key);
1232 void DrawGameDoorValues()
1236 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
1238 DrawGameDoorValues_EM();
1243 DrawGameValue_Level(level_nr);
1245 DrawGameValue_Emeralds(local_player->gems_still_needed);
1246 DrawGameValue_Dynamite(local_player->inventory_size);
1247 DrawGameValue_Score(local_player->score);
1248 DrawGameValue_Time(TimeLeft);
1250 for (i = 0; i < MAX_PLAYERS; i++)
1251 DrawGameValue_Keys(stored_player[i].key);
1255 static void resolve_group_element(int group_element, int recursion_depth)
1257 static int group_nr;
1258 static struct ElementGroupInfo *group;
1259 struct ElementGroupInfo *actual_group = element_info[group_element].group;
1262 if (recursion_depth > NUM_GROUP_ELEMENTS) /* recursion too deep */
1264 Error(ERR_WARN, "recursion too deep when resolving group element %d",
1265 group_element - EL_GROUP_START + 1);
1267 /* replace element which caused too deep recursion by question mark */
1268 group->element_resolved[group->num_elements_resolved++] = EL_UNKNOWN;
1273 if (recursion_depth == 0) /* initialization */
1275 group = element_info[group_element].group;
1276 group_nr = group_element - EL_GROUP_START;
1278 group->num_elements_resolved = 0;
1279 group->choice_pos = 0;
1282 for (i = 0; i < actual_group->num_elements; i++)
1284 int element = actual_group->element[i];
1286 if (group->num_elements_resolved == NUM_FILE_ELEMENTS)
1289 if (IS_GROUP_ELEMENT(element))
1290 resolve_group_element(element, recursion_depth + 1);
1293 group->element_resolved[group->num_elements_resolved++] = element;
1294 element_info[element].in_group[group_nr] = TRUE;
1301 =============================================================================
1303 -----------------------------------------------------------------------------
1304 initialize game engine due to level / tape version number
1305 =============================================================================
1308 static void InitGameEngine()
1310 int i, j, k, l, x, y;
1312 /* set game engine from tape file when re-playing, else from level file */
1313 game.engine_version = (tape.playing ? tape.engine_version :
1314 level.game_version);
1316 /* ---------------------------------------------------------------------- */
1317 /* set flags for bugs and changes according to active game engine version */
1318 /* ---------------------------------------------------------------------- */
1321 Summary of bugfix/change:
1322 Fixed handling for custom elements that change when pushed by the player.
1324 Fixed/changed in version:
1328 Before 3.1.0, custom elements that "change when pushing" changed directly
1329 after the player started pushing them (until then handled in "DigField()").
1330 Since 3.1.0, these custom elements are not changed until the "pushing"
1331 move of the element is finished (now handled in "ContinueMoving()").
1333 Affected levels/tapes:
1334 The first condition is generally needed for all levels/tapes before version
1335 3.1.0, which might use the old behaviour before it was changed; known tapes
1336 that are affected are some tapes from the level set "Walpurgis Gardens" by
1338 The second condition is an exception from the above case and is needed for
1339 the special case of tapes recorded with game (not engine!) version 3.1.0 or
1340 above (including some development versions of 3.1.0), but before it was
1341 known that this change would break tapes like the above and was fixed in
1342 3.1.1, so that the changed behaviour was active although the engine version
1343 while recording maybe was before 3.1.0. There is at least one tape that is
1344 affected by this exception, which is the tape for the one-level set "Bug
1345 Machine" by Juergen Bonhagen.
1348 game.use_change_when_pushing_bug =
1349 (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1351 tape.game_version >= VERSION_IDENT(3,1,0,0) &&
1352 tape.game_version < VERSION_IDENT(3,1,1,0)));
1355 Summary of bugfix/change:
1356 Fixed handling for blocking the field the player leaves when moving.
1358 Fixed/changed in version:
1362 Before 3.1.1, when "block last field when moving" was enabled, the field
1363 the player is leaving when moving was blocked for the time of the move,
1364 and was directly unblocked afterwards. This resulted in the last field
1365 being blocked for exactly one less than the number of frames of one player
1366 move. Additionally, even when blocking was disabled, the last field was
1367 blocked for exactly one frame.
1368 Since 3.1.1, due to changes in player movement handling, the last field
1369 is not blocked at all when blocking is disabled. When blocking is enabled,
1370 the last field is blocked for exactly the number of frames of one player
1371 move. Additionally, if the player is Murphy, the hero of Supaplex, the
1372 last field is blocked for exactly one more than the number of frames of
1375 Affected levels/tapes:
1376 (!!! yet to be determined -- probably many !!!)
1379 game.use_block_last_field_bug =
1380 (game.engine_version < VERSION_IDENT(3,1,1,0));
1383 Summary of bugfix/change:
1384 Changed behaviour of CE changes with multiple changes per single frame.
1386 Fixed/changed in version:
1390 Before 3.2.0-6, only one single CE change was allowed in each engine frame.
1391 This resulted in race conditions where CEs seem to behave strange in some
1392 situations (where triggered CE changes were just skipped because there was
1393 already a CE change on that tile in the playfield in that engine frame).
1394 Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
1395 (The number of changes per frame must be limited in any case, because else
1396 it is easily possible to define CE changes that would result in an infinite
1397 loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
1398 should be set large enough so that it would only be reached in cases where
1399 the corresponding CE change conditions run into a loop. Therefore, it seems
1400 to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
1401 maximal number of change pages for custom elements.)
1403 Affected levels/tapes:
1407 #if USE_ONLY_ONE_CHANGE_PER_FRAME
1408 game.max_num_changes_per_frame = 1;
1410 game.max_num_changes_per_frame =
1411 (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
1414 /* ---------------------------------------------------------------------- */
1416 /* default scan direction: scan playfield from top/left to bottom/right */
1417 InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
1419 /* dynamically adjust element properties according to game engine version */
1420 InitElementPropertiesEngine(game.engine_version);
1423 printf("level %d: level version == %06d\n", level_nr, level.game_version);
1424 printf(" tape version == %06d [%s] [file: %06d]\n",
1425 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
1427 printf(" => game.engine_version == %06d\n", game.engine_version);
1431 /* ---------- recursively resolve group elements ------------------------- */
1433 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1434 for (j = 0; j < NUM_GROUP_ELEMENTS; j++)
1435 element_info[i].in_group[j] = FALSE;
1437 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
1438 resolve_group_element(EL_GROUP_START + i, 0);
1441 /* ---------- initialize player's initial move delay --------------------- */
1444 /* dynamically adjust player properties according to level information */
1445 game.initial_move_delay_value =
1446 get_move_delay_from_stepsize(level.initial_player_stepsize);
1448 /* dynamically adjust player properties according to level information */
1449 game.initial_move_delay_value =
1450 (level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED);
1453 /* dynamically adjust player properties according to game engine version */
1454 game.initial_move_delay = (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
1455 game.initial_move_delay_value : 0);
1457 /* ---------- initialize player's initial push delay --------------------- */
1459 /* dynamically adjust player properties according to game engine version */
1460 game.initial_push_delay_value =
1461 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
1463 /* ---------- initialize changing elements ------------------------------- */
1465 /* initialize changing elements information */
1466 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1468 struct ElementInfo *ei = &element_info[i];
1470 /* this pointer might have been changed in the level editor */
1471 ei->change = &ei->change_page[0];
1473 if (!IS_CUSTOM_ELEMENT(i))
1475 ei->change->target_element = EL_EMPTY_SPACE;
1476 ei->change->delay_fixed = 0;
1477 ei->change->delay_random = 0;
1478 ei->change->delay_frames = 1;
1481 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1483 ei->has_change_event[j] = FALSE;
1485 ei->event_page_nr[j] = 0;
1486 ei->event_page[j] = &ei->change_page[0];
1490 /* add changing elements from pre-defined list */
1491 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
1493 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
1494 struct ElementInfo *ei = &element_info[ch_delay->element];
1496 ei->change->target_element = ch_delay->target_element;
1497 ei->change->delay_fixed = ch_delay->change_delay;
1499 ei->change->pre_change_function = ch_delay->pre_change_function;
1500 ei->change->change_function = ch_delay->change_function;
1501 ei->change->post_change_function = ch_delay->post_change_function;
1503 ei->change->can_change = TRUE;
1504 ei->change->can_change_or_has_action = TRUE;
1506 ei->has_change_event[CE_DELAY] = TRUE;
1508 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
1509 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
1512 /* ---------- initialize internal run-time variables ------------- */
1514 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1516 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1518 for (j = 0; j < ei->num_change_pages; j++)
1520 ei->change_page[j].can_change_or_has_action =
1521 (ei->change_page[j].can_change |
1522 ei->change_page[j].has_action);
1526 /* add change events from custom element configuration */
1527 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1529 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1531 for (j = 0; j < ei->num_change_pages; j++)
1533 if (!ei->change_page[j].can_change_or_has_action)
1536 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1538 /* only add event page for the first page found with this event */
1539 if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
1541 ei->has_change_event[k] = TRUE;
1543 ei->event_page_nr[k] = j;
1544 ei->event_page[k] = &ei->change_page[j];
1550 /* ---------- initialize run-time trigger player and element ------------- */
1552 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1554 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1556 for (j = 0; j < ei->num_change_pages; j++)
1558 ei->change_page[j].actual_trigger_element = EL_EMPTY;
1559 ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
1560 ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
1561 ei->change_page[j].actual_trigger_ce_value = 0;
1565 /* ---------- initialize trigger events ---------------------------------- */
1567 /* initialize trigger events information */
1568 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1569 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1570 trigger_events[i][j] = FALSE;
1572 /* add trigger events from element change event properties */
1573 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1575 struct ElementInfo *ei = &element_info[i];
1577 for (j = 0; j < ei->num_change_pages; j++)
1579 if (!ei->change_page[j].can_change_or_has_action)
1582 if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
1584 int trigger_element = ei->change_page[j].trigger_element;
1586 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1588 if (ei->change_page[j].has_event[k])
1590 if (IS_GROUP_ELEMENT(trigger_element))
1592 struct ElementGroupInfo *group =
1593 element_info[trigger_element].group;
1595 for (l = 0; l < group->num_elements_resolved; l++)
1596 trigger_events[group->element_resolved[l]][k] = TRUE;
1599 trigger_events[trigger_element][k] = TRUE;
1606 /* ---------- initialize push delay -------------------------------------- */
1608 /* initialize push delay values to default */
1609 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1611 if (!IS_CUSTOM_ELEMENT(i))
1613 element_info[i].push_delay_fixed = game.default_push_delay_fixed;
1614 element_info[i].push_delay_random = game.default_push_delay_random;
1618 /* set push delay value for certain elements from pre-defined list */
1619 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
1621 int e = push_delay_list[i].element;
1623 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
1624 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
1627 /* set push delay value for Supaplex elements for newer engine versions */
1628 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
1630 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1632 if (IS_SP_ELEMENT(i))
1634 /* set SP push delay to just enough to push under a falling zonk */
1635 int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
1637 element_info[i].push_delay_fixed = delay;
1638 element_info[i].push_delay_random = 0;
1643 /* ---------- initialize move stepsize ----------------------------------- */
1645 /* initialize move stepsize values to default */
1646 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1647 if (!IS_CUSTOM_ELEMENT(i))
1648 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
1650 /* set move stepsize value for certain elements from pre-defined list */
1651 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
1653 int e = move_stepsize_list[i].element;
1655 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
1658 /* ---------- initialize collect score ----------------------------------- */
1660 /* initialize collect score values for custom elements from initial value */
1661 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1662 if (IS_CUSTOM_ELEMENT(i))
1663 element_info[i].collect_score = element_info[i].collect_score_initial;
1665 /* ---------- initialize collect count ----------------------------------- */
1667 /* initialize collect count values for non-custom elements */
1668 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1669 if (!IS_CUSTOM_ELEMENT(i))
1670 element_info[i].collect_count_initial = 0;
1672 /* add collect count values for all elements from pre-defined list */
1673 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
1674 element_info[collect_count_list[i].element].collect_count_initial =
1675 collect_count_list[i].count;
1677 /* ---------- initialize access direction -------------------------------- */
1679 /* initialize access direction values to default (access from every side) */
1680 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1681 if (!IS_CUSTOM_ELEMENT(i))
1682 element_info[i].access_direction = MV_ALL_DIRECTIONS;
1684 /* set access direction value for certain elements from pre-defined list */
1685 for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
1686 element_info[access_direction_list[i].element].access_direction =
1687 access_direction_list[i].direction;
1689 /* ---------- initialize explosion content ------------------------------- */
1690 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1692 if (IS_CUSTOM_ELEMENT(i))
1695 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
1697 /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
1699 element_info[i].content.e[x][y] =
1700 (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
1701 i == EL_PLAYER_2 ? EL_EMERALD_RED :
1702 i == EL_PLAYER_3 ? EL_EMERALD :
1703 i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
1704 i == EL_MOLE ? EL_EMERALD_RED :
1705 i == EL_PENGUIN ? EL_EMERALD_PURPLE :
1706 i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
1707 i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
1708 i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
1709 i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
1710 i == EL_WALL_EMERALD ? EL_EMERALD :
1711 i == EL_WALL_DIAMOND ? EL_DIAMOND :
1712 i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
1713 i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
1714 i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
1715 i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
1716 i == EL_WALL_PEARL ? EL_PEARL :
1717 i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
1723 int get_num_special_action(int element, int action_first, int action_last)
1725 int num_special_action = 0;
1728 for (i = action_first; i <= action_last; i++)
1730 boolean found = FALSE;
1732 for (j = 0; j < NUM_DIRECTIONS; j++)
1733 if (el_act_dir2img(element, i, j) !=
1734 el_act_dir2img(element, ACTION_DEFAULT, j))
1738 num_special_action++;
1743 return num_special_action;
1747 =============================================================================
1749 -----------------------------------------------------------------------------
1750 initialize and start new game
1751 =============================================================================
1756 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
1757 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
1758 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
1763 /* don't play tapes over network */
1764 network_playing = (options.network && !tape.playing);
1766 for (i = 0; i < MAX_PLAYERS; i++)
1768 struct PlayerInfo *player = &stored_player[i];
1770 player->index_nr = i;
1771 player->index_bit = (1 << i);
1772 player->element_nr = EL_PLAYER_1 + i;
1774 player->present = FALSE;
1775 player->active = FALSE;
1778 player->effective_action = 0;
1779 player->programmed_action = 0;
1782 player->gems_still_needed = level.gems_needed;
1783 player->sokobanfields_still_needed = 0;
1784 player->lights_still_needed = 0;
1785 player->friends_still_needed = 0;
1787 for (j = 0; j < MAX_NUM_KEYS; j++)
1788 player->key[j] = FALSE;
1790 player->dynabomb_count = 0;
1791 player->dynabomb_size = 1;
1792 player->dynabombs_left = 0;
1793 player->dynabomb_xl = FALSE;
1795 player->MovDir = MV_NONE;
1798 player->GfxDir = MV_NONE;
1799 player->GfxAction = ACTION_DEFAULT;
1801 player->StepFrame = 0;
1803 player->use_murphy = FALSE;
1804 player->artwork_element =
1805 (level.use_artwork_element[i] ? level.artwork_element[i] :
1806 player->element_nr);
1808 player->block_last_field = FALSE; /* initialized in InitPlayerField() */
1809 player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
1811 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
1813 player->actual_frame_counter = 0;
1815 player->step_counter = 0;
1817 player->last_move_dir = MV_NONE;
1819 player->is_waiting = FALSE;
1820 player->is_moving = FALSE;
1821 player->is_auto_moving = FALSE;
1822 player->is_digging = FALSE;
1823 player->is_snapping = FALSE;
1824 player->is_collecting = FALSE;
1825 player->is_pushing = FALSE;
1826 player->is_switching = FALSE;
1827 player->is_dropping = FALSE;
1829 player->is_bored = FALSE;
1830 player->is_sleeping = FALSE;
1832 player->frame_counter_bored = -1;
1833 player->frame_counter_sleeping = -1;
1835 player->anim_delay_counter = 0;
1836 player->post_delay_counter = 0;
1838 player->action_waiting = ACTION_DEFAULT;
1839 player->last_action_waiting = ACTION_DEFAULT;
1840 player->special_action_bored = ACTION_DEFAULT;
1841 player->special_action_sleeping = ACTION_DEFAULT;
1843 /* set number of special actions for bored and sleeping animation */
1844 player->num_special_action_bored =
1845 get_num_special_action(player->artwork_element,
1846 ACTION_BORING_1, ACTION_BORING_LAST);
1847 player->num_special_action_sleeping =
1848 get_num_special_action(player->artwork_element,
1849 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
1851 player->switch_x = -1;
1852 player->switch_y = -1;
1854 player->drop_x = -1;
1855 player->drop_y = -1;
1857 player->show_envelope = 0;
1860 SetPlayerMoveSpeed(player, level.initial_player_stepsize, TRUE);
1862 player->move_delay = game.initial_move_delay;
1863 player->move_delay_value = game.initial_move_delay_value;
1865 player->move_delay_value_next = -1;
1867 player->move_delay_reset_counter = 0;
1869 player->cannot_move = FALSE;
1872 player->push_delay = -1; /* initialized when pushing starts */
1873 player->push_delay_value = game.initial_push_delay_value;
1875 player->drop_delay = 0;
1877 player->last_jx = player->last_jy = 0;
1878 player->jx = player->jy = 0;
1880 player->shield_normal_time_left = 0;
1881 player->shield_deadly_time_left = 0;
1883 player->inventory_infinite_element = EL_UNDEFINED;
1884 player->inventory_size = 0;
1886 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
1887 SnapField(player, 0, 0);
1889 player->LevelSolved = FALSE;
1890 player->GameOver = FALSE;
1893 network_player_action_received = FALSE;
1895 #if defined(NETWORK_AVALIABLE)
1896 /* initial null action */
1897 if (network_playing)
1898 SendToServer_MovePlayer(MV_NONE);
1907 TimeLeft = level.time;
1910 ScreenMovDir = MV_NONE;
1914 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
1916 AllPlayersGone = FALSE;
1918 game.yamyam_content_nr = 0;
1919 game.magic_wall_active = FALSE;
1920 game.magic_wall_time_left = 0;
1921 game.light_time_left = 0;
1922 game.timegate_time_left = 0;
1923 game.switchgate_pos = 0;
1924 game.wind_direction = level.wind_direction_initial;
1925 game.gravity = level.initial_gravity;
1926 game.explosions_delayed = TRUE;
1928 game.lenses_time_left = 0;
1929 game.magnify_time_left = 0;
1931 game.ball_state = level.ball_state_initial;
1932 game.ball_content_nr = 0;
1934 game.envelope_active = FALSE;
1936 for (i = 0; i < NUM_BELTS; i++)
1938 game.belt_dir[i] = MV_NONE;
1939 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1942 for (i = 0; i < MAX_NUM_AMOEBA; i++)
1943 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
1946 SCAN_PLAYFIELD(x, y)
1948 for (x = 0; x < lev_fieldx; x++) for (y = 0; y < lev_fieldy; y++)
1951 Feld[x][y] = level.field[x][y];
1952 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
1953 ChangeDelay[x][y] = 0;
1954 ChangePage[x][y] = -1;
1955 #if USE_NEW_CUSTOM_VALUE
1956 CustomValue[x][y] = 0; /* initialized in InitField() */
1958 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
1960 WasJustMoving[x][y] = 0;
1961 WasJustFalling[x][y] = 0;
1962 CheckCollision[x][y] = 0;
1964 Pushed[x][y] = FALSE;
1966 ChangeCount[x][y] = 0;
1967 ChangeEvent[x][y] = -1;
1969 ExplodePhase[x][y] = 0;
1970 ExplodeDelay[x][y] = 0;
1971 ExplodeField[x][y] = EX_TYPE_NONE;
1973 RunnerVisit[x][y] = 0;
1974 PlayerVisit[x][y] = 0;
1977 GfxRandom[x][y] = INIT_GFX_RANDOM();
1978 GfxElement[x][y] = EL_UNDEFINED;
1979 GfxAction[x][y] = ACTION_DEFAULT;
1980 GfxDir[x][y] = MV_NONE;
1984 SCAN_PLAYFIELD(x, y)
1986 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
1989 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
1991 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
1993 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
1996 InitField(x, y, TRUE);
2001 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
2002 emulate_sb ? EMU_SOKOBAN :
2003 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
2005 #if USE_NEW_ALL_SLIPPERY
2006 /* initialize type of slippery elements */
2007 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2009 if (!IS_CUSTOM_ELEMENT(i))
2011 /* default: elements slip down either to the left or right randomly */
2012 element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
2014 /* SP style elements prefer to slip down on the left side */
2015 if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
2016 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
2018 /* BD style elements prefer to slip down on the left side */
2019 if (game.emulation == EMU_BOULDERDASH)
2020 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
2025 /* initialize explosion and ignition delay */
2026 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2028 if (!IS_CUSTOM_ELEMENT(i))
2031 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
2032 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
2033 game.emulation == EMU_SUPAPLEX ? 3 : 2);
2034 int last_phase = (num_phase + 1) * delay;
2035 int half_phase = (num_phase / 2) * delay;
2037 element_info[i].explosion_delay = last_phase - 1;
2038 element_info[i].ignition_delay = half_phase;
2040 if (i == EL_BLACK_ORB)
2041 element_info[i].ignition_delay = 1;
2045 if (element_info[i].explosion_delay < 1) /* !!! check again !!! */
2046 element_info[i].explosion_delay = 1;
2048 if (element_info[i].ignition_delay < 1) /* !!! check again !!! */
2049 element_info[i].ignition_delay = 1;
2053 /* correct non-moving belts to start moving left */
2054 for (i = 0; i < NUM_BELTS; i++)
2055 if (game.belt_dir[i] == MV_NONE)
2056 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
2058 /* check if any connected player was not found in playfield */
2059 for (i = 0; i < MAX_PLAYERS; i++)
2061 struct PlayerInfo *player = &stored_player[i];
2063 if (player->connected && !player->present)
2065 for (j = 0; j < MAX_PLAYERS; j++)
2067 struct PlayerInfo *some_player = &stored_player[j];
2068 int jx = some_player->jx, jy = some_player->jy;
2070 /* assign first free player found that is present in the playfield */
2071 if (some_player->present && !some_player->connected)
2073 player->present = TRUE;
2074 player->active = TRUE;
2076 some_player->present = FALSE;
2077 some_player->active = FALSE;
2080 player->element_nr = some_player->element_nr;
2083 player->artwork_element = some_player->artwork_element;
2085 player->block_last_field = some_player->block_last_field;
2086 player->block_delay_adjustment = some_player->block_delay_adjustment;
2088 StorePlayer[jx][jy] = player->element_nr;
2089 player->jx = player->last_jx = jx;
2090 player->jy = player->last_jy = jy;
2100 /* when playing a tape, eliminate all players which do not participate */
2102 for (i = 0; i < MAX_PLAYERS; i++)
2104 if (stored_player[i].active && !tape.player_participates[i])
2106 struct PlayerInfo *player = &stored_player[i];
2107 int jx = player->jx, jy = player->jy;
2109 player->active = FALSE;
2110 StorePlayer[jx][jy] = 0;
2111 Feld[jx][jy] = EL_EMPTY;
2115 else if (!options.network && !setup.team_mode) /* && !tape.playing */
2117 /* when in single player mode, eliminate all but the first active player */
2119 for (i = 0; i < MAX_PLAYERS; i++)
2121 if (stored_player[i].active)
2123 for (j = i + 1; j < MAX_PLAYERS; j++)
2125 if (stored_player[j].active)
2127 struct PlayerInfo *player = &stored_player[j];
2128 int jx = player->jx, jy = player->jy;
2130 player->active = FALSE;
2131 player->present = FALSE;
2133 StorePlayer[jx][jy] = 0;
2134 Feld[jx][jy] = EL_EMPTY;
2141 /* when recording the game, store which players take part in the game */
2144 for (i = 0; i < MAX_PLAYERS; i++)
2145 if (stored_player[i].active)
2146 tape.player_participates[i] = TRUE;
2151 for (i = 0; i < MAX_PLAYERS; i++)
2153 struct PlayerInfo *player = &stored_player[i];
2155 printf("Player %d: present == %d, connected == %d, active == %d.\n",
2160 if (local_player == player)
2161 printf("Player %d is local player.\n", i+1);
2165 if (BorderElement == EL_EMPTY)
2168 SBX_Right = lev_fieldx - SCR_FIELDX;
2170 SBY_Lower = lev_fieldy - SCR_FIELDY;
2175 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
2177 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
2180 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
2181 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
2183 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
2184 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
2186 /* if local player not found, look for custom element that might create
2187 the player (make some assumptions about the right custom element) */
2188 if (!local_player->present)
2190 int start_x = 0, start_y = 0;
2191 int found_rating = 0;
2192 int found_element = EL_UNDEFINED;
2193 int player_nr = local_player->index_nr;
2196 SCAN_PLAYFIELD(x, y)
2198 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
2201 int element = Feld[x][y];
2206 if (level.use_start_element[player_nr] &&
2207 level.start_element[player_nr] == element &&
2214 found_element = element;
2217 if (!IS_CUSTOM_ELEMENT(element))
2220 if (CAN_CHANGE(element))
2222 for (i = 0; i < element_info[element].num_change_pages; i++)
2224 /* check for player created from custom element as single target */
2225 content = element_info[element].change_page[i].target_element;
2226 is_player = ELEM_IS_PLAYER(content);
2228 if (is_player && (found_rating < 3 || element < found_element))
2234 found_element = element;
2239 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
2241 /* check for player created from custom element as explosion content */
2242 content = element_info[element].content.e[xx][yy];
2243 is_player = ELEM_IS_PLAYER(content);
2245 if (is_player && (found_rating < 2 || element < found_element))
2247 start_x = x + xx - 1;
2248 start_y = y + yy - 1;
2251 found_element = element;
2254 if (!CAN_CHANGE(element))
2257 for (i = 0; i < element_info[element].num_change_pages; i++)
2259 /* check for player created from custom element as extended target */
2261 element_info[element].change_page[i].target_content.e[xx][yy];
2263 is_player = ELEM_IS_PLAYER(content);
2265 if (is_player && (found_rating < 1 || element < found_element))
2267 start_x = x + xx - 1;
2268 start_y = y + yy - 1;
2271 found_element = element;
2277 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
2278 start_x > SBX_Right + MIDPOSX ? SBX_Right :
2281 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
2282 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
2287 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2288 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2289 local_player->jx - MIDPOSX);
2291 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2292 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2293 local_player->jy - MIDPOSY);
2296 if (!game.restart_level)
2297 CloseDoor(DOOR_CLOSE_1);
2299 /* !!! FIX THIS (START) !!! */
2300 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2302 InitGameEngine_EM();
2309 /* after drawing the level, correct some elements */
2310 if (game.timegate_time_left == 0)
2311 CloseAllOpenTimegates();
2313 if (setup.soft_scrolling)
2314 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
2316 redraw_mask |= REDRAW_FROM_BACKBUFFER;
2319 /* !!! FIX THIS (END) !!! */
2321 if (!game.restart_level)
2323 /* copy default game door content to main double buffer */
2324 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2325 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2328 DrawGameDoorValues();
2330 if (!game.restart_level)
2334 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
2335 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
2336 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
2340 /* copy actual game door content to door double buffer for OpenDoor() */
2341 BlitBitmap(drawto, bitmap_db_door,
2342 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
2344 OpenDoor(DOOR_OPEN_ALL);
2346 PlaySoundStereo(SND_GAME_STARTING, SOUND_MIDDLE);
2348 if (setup.sound_music)
2351 KeyboardAutoRepeatOffUnlessAutoplay();
2355 for (i = 0; i < MAX_PLAYERS; i++)
2356 printf("Player %d %sactive.\n",
2357 i + 1, (stored_player[i].active ? "" : "not "));
2361 game.restart_level = FALSE;
2364 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
2366 /* this is used for non-R'n'D game engines to update certain engine values */
2368 /* needed to determine if sounds are played within the visible screen area */
2369 scroll_x = actual_scroll_x;
2370 scroll_y = actual_scroll_y;
2373 void InitMovDir(int x, int y)
2375 int i, element = Feld[x][y];
2376 static int xy[4][2] =
2383 static int direction[3][4] =
2385 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
2386 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
2387 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
2396 Feld[x][y] = EL_BUG;
2397 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
2400 case EL_SPACESHIP_RIGHT:
2401 case EL_SPACESHIP_UP:
2402 case EL_SPACESHIP_LEFT:
2403 case EL_SPACESHIP_DOWN:
2404 Feld[x][y] = EL_SPACESHIP;
2405 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
2408 case EL_BD_BUTTERFLY_RIGHT:
2409 case EL_BD_BUTTERFLY_UP:
2410 case EL_BD_BUTTERFLY_LEFT:
2411 case EL_BD_BUTTERFLY_DOWN:
2412 Feld[x][y] = EL_BD_BUTTERFLY;
2413 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
2416 case EL_BD_FIREFLY_RIGHT:
2417 case EL_BD_FIREFLY_UP:
2418 case EL_BD_FIREFLY_LEFT:
2419 case EL_BD_FIREFLY_DOWN:
2420 Feld[x][y] = EL_BD_FIREFLY;
2421 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
2424 case EL_PACMAN_RIGHT:
2426 case EL_PACMAN_LEFT:
2427 case EL_PACMAN_DOWN:
2428 Feld[x][y] = EL_PACMAN;
2429 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
2432 case EL_SP_SNIKSNAK:
2433 MovDir[x][y] = MV_UP;
2436 case EL_SP_ELECTRON:
2437 MovDir[x][y] = MV_LEFT;
2444 Feld[x][y] = EL_MOLE;
2445 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
2449 if (IS_CUSTOM_ELEMENT(element))
2451 struct ElementInfo *ei = &element_info[element];
2452 int move_direction_initial = ei->move_direction_initial;
2453 int move_pattern = ei->move_pattern;
2455 if (move_direction_initial == MV_START_PREVIOUS)
2457 if (MovDir[x][y] != MV_NONE)
2460 move_direction_initial = MV_START_AUTOMATIC;
2463 if (move_direction_initial == MV_START_RANDOM)
2464 MovDir[x][y] = 1 << RND(4);
2465 else if (move_direction_initial & MV_ANY_DIRECTION)
2466 MovDir[x][y] = move_direction_initial;
2467 else if (move_pattern == MV_ALL_DIRECTIONS ||
2468 move_pattern == MV_TURNING_LEFT ||
2469 move_pattern == MV_TURNING_RIGHT ||
2470 move_pattern == MV_TURNING_LEFT_RIGHT ||
2471 move_pattern == MV_TURNING_RIGHT_LEFT ||
2472 move_pattern == MV_TURNING_RANDOM)
2473 MovDir[x][y] = 1 << RND(4);
2474 else if (move_pattern == MV_HORIZONTAL)
2475 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
2476 else if (move_pattern == MV_VERTICAL)
2477 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
2478 else if (move_pattern & MV_ANY_DIRECTION)
2479 MovDir[x][y] = element_info[element].move_pattern;
2480 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
2481 move_pattern == MV_ALONG_RIGHT_SIDE)
2483 /* use random direction as default start direction */
2484 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2485 MovDir[x][y] = 1 << RND(4);
2487 for (i = 0; i < NUM_DIRECTIONS; i++)
2489 int x1 = x + xy[i][0];
2490 int y1 = y + xy[i][1];
2492 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2494 if (move_pattern == MV_ALONG_RIGHT_SIDE)
2495 MovDir[x][y] = direction[0][i];
2497 MovDir[x][y] = direction[1][i];
2506 MovDir[x][y] = 1 << RND(4);
2508 if (element != EL_BUG &&
2509 element != EL_SPACESHIP &&
2510 element != EL_BD_BUTTERFLY &&
2511 element != EL_BD_FIREFLY)
2514 for (i = 0; i < NUM_DIRECTIONS; i++)
2516 int x1 = x + xy[i][0];
2517 int y1 = y + xy[i][1];
2519 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2521 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
2523 MovDir[x][y] = direction[0][i];
2526 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
2527 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
2529 MovDir[x][y] = direction[1][i];
2538 GfxDir[x][y] = MovDir[x][y];
2541 void InitAmoebaNr(int x, int y)
2544 int group_nr = AmoebeNachbarNr(x, y);
2548 for (i = 1; i < MAX_NUM_AMOEBA; i++)
2550 if (AmoebaCnt[i] == 0)
2558 AmoebaNr[x][y] = group_nr;
2559 AmoebaCnt[group_nr]++;
2560 AmoebaCnt2[group_nr]++;
2566 boolean raise_level = FALSE;
2568 if (local_player->MovPos)
2571 if (tape.auto_play) /* tape might already be stopped here */
2572 tape.auto_play_level_solved = TRUE;
2574 local_player->LevelSolved = FALSE;
2576 PlaySoundStereo(SND_GAME_WINNING, SOUND_MIDDLE);
2580 if (!tape.playing && setup.sound_loops)
2581 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2582 SND_CTRL_PLAY_LOOP);
2584 while (TimeLeft > 0)
2586 if (!tape.playing && !setup.sound_loops)
2587 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2589 if (TimeLeft > 100 && TimeLeft % 10 == 0)
2592 RaiseScore(level.score[SC_TIME_BONUS] * 10);
2597 RaiseScore(level.score[SC_TIME_BONUS]);
2600 DrawGameValue_Time(TimeLeft);
2608 if (!tape.playing && setup.sound_loops)
2609 StopSound(SND_GAME_LEVELTIME_BONUS);
2611 else if (level.time == 0) /* level without time limit */
2613 if (!tape.playing && setup.sound_loops)
2614 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2615 SND_CTRL_PLAY_LOOP);
2617 while (TimePlayed < 999)
2619 if (!tape.playing && !setup.sound_loops)
2620 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2622 if (TimePlayed < 900 && TimePlayed % 10 == 0)
2625 RaiseScore(level.score[SC_TIME_BONUS] * 10);
2630 RaiseScore(level.score[SC_TIME_BONUS]);
2633 DrawGameValue_Time(TimePlayed);
2641 if (!tape.playing && setup.sound_loops)
2642 StopSound(SND_GAME_LEVELTIME_BONUS);
2645 /* close exit door after last player */
2646 if (AllPlayersGone && ExitX >= 0 && ExitY >= 0 &&
2647 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
2648 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN))
2650 int element = Feld[ExitX][ExitY];
2652 Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
2653 EL_SP_EXIT_CLOSING);
2655 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
2658 /* player disappears */
2659 if (ExitX >= 0 && ExitY >= 0)
2660 DrawLevelField(ExitX, ExitY);
2666 printf("::: TAPE PLAYING -> DO NOT SAVE SCORE\n");
2668 printf("::: NO TAPE PLAYING -> SAVING SCORE\n");
2674 CloseDoor(DOOR_CLOSE_1);
2679 SaveTape(tape.level_nr); /* Ask to save tape */
2682 if (level_nr == leveldir_current->handicap_level)
2684 leveldir_current->handicap_level++;
2685 SaveLevelSetup_SeriesInfo();
2688 if (level_editor_test_game)
2689 local_player->score = -1; /* no highscore when playing from editor */
2690 else if (level_nr < leveldir_current->last_level)
2691 raise_level = TRUE; /* advance to next level */
2693 if ((hi_pos = NewHiScore()) >= 0)
2695 game_status = GAME_MODE_SCORES;
2696 DrawHallOfFame(hi_pos);
2705 game_status = GAME_MODE_MAIN;
2722 LoadScore(level_nr);
2724 if (strcmp(setup.player_name, EMPTY_PLAYER_NAME) == 0 ||
2725 local_player->score < highscore[MAX_SCORE_ENTRIES - 1].Score)
2728 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
2730 if (local_player->score > highscore[k].Score)
2732 /* player has made it to the hall of fame */
2734 if (k < MAX_SCORE_ENTRIES - 1)
2736 int m = MAX_SCORE_ENTRIES - 1;
2739 for (l = k; l < MAX_SCORE_ENTRIES; l++)
2740 if (!strcmp(setup.player_name, highscore[l].Name))
2742 if (m == k) /* player's new highscore overwrites his old one */
2746 for (l = m; l > k; l--)
2748 strcpy(highscore[l].Name, highscore[l - 1].Name);
2749 highscore[l].Score = highscore[l - 1].Score;
2756 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
2757 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
2758 highscore[k].Score = local_player->score;
2764 else if (!strncmp(setup.player_name, highscore[k].Name,
2765 MAX_PLAYER_NAME_LEN))
2766 break; /* player already there with a higher score */
2772 SaveScore(level_nr);
2777 inline static int getElementMoveStepsize(int x, int y)
2779 int element = Feld[x][y];
2780 int direction = MovDir[x][y];
2781 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2782 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2783 int horiz_move = (dx != 0);
2784 int sign = (horiz_move ? dx : dy);
2785 int step = sign * element_info[element].move_stepsize;
2787 /* special values for move stepsize for spring and things on conveyor belt */
2791 if (element == EL_SPRING)
2792 step = sign * MOVE_STEPSIZE_NORMAL * 2;
2793 else if (CAN_FALL(element) && !CAN_MOVE(element) &&
2794 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
2795 step = sign * MOVE_STEPSIZE_NORMAL / 2;
2797 if (CAN_FALL(element) &&
2798 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
2799 step = sign * MOVE_STEPSIZE_NORMAL / 2;
2800 else if (element == EL_SPRING)
2801 step = sign * MOVE_STEPSIZE_NORMAL * 2;
2808 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
2810 if (player->GfxAction != action || player->GfxDir != dir)
2813 printf("Player frame reset! (%d => %d, %d => %d)\n",
2814 player->GfxAction, action, player->GfxDir, dir);
2817 player->GfxAction = action;
2818 player->GfxDir = dir;
2820 player->StepFrame = 0;
2824 static void ResetRandomAnimationValue(int x, int y)
2826 GfxRandom[x][y] = INIT_GFX_RANDOM();
2829 static void ResetGfxAnimation(int x, int y)
2832 int element, graphic;
2836 GfxAction[x][y] = ACTION_DEFAULT;
2837 GfxDir[x][y] = MovDir[x][y];
2840 element = Feld[x][y];
2841 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
2843 if (graphic_info[graphic].anim_global_sync)
2844 GfxFrame[x][y] = FrameCounter;
2845 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
2846 GfxFrame[x][y] = CustomValue[x][y];
2847 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2848 GfxFrame[x][y] = element_info[element].collect_score;
2852 void InitMovingField(int x, int y, int direction)
2854 int element = Feld[x][y];
2858 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2859 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2863 if (!WasJustMoving[x][y] || direction != MovDir[x][y])
2864 ResetGfxAnimation(x, y);
2866 MovDir[x][y] = direction;
2867 GfxDir[x][y] = direction;
2868 GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
2869 ACTION_FALLING : ACTION_MOVING);
2872 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
2874 if (graphic_info[graphic].anim_global_sync)
2875 GfxFrame[x][y] = FrameCounter;
2876 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
2877 GfxFrame[x][y] = CustomValue[x][y];
2878 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2879 GfxFrame[x][y] = element_info[element].collect_score;
2882 /* this is needed for CEs with property "can move" / "not moving" */
2884 if (getElementMoveStepsize(x, y) != 0) /* moving or being moved */
2886 if (Feld[newx][newy] == EL_EMPTY)
2887 Feld[newx][newy] = EL_BLOCKED;
2889 MovDir[newx][newy] = MovDir[x][y];
2891 #if USE_NEW_CUSTOM_VALUE
2892 CustomValue[newx][newy] = CustomValue[x][y];
2895 GfxFrame[newx][newy] = GfxFrame[x][y];
2896 GfxRandom[newx][newy] = GfxRandom[x][y];
2897 GfxAction[newx][newy] = GfxAction[x][y];
2898 GfxDir[newx][newy] = GfxDir[x][y];
2902 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
2904 int direction = MovDir[x][y];
2906 int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
2907 int newy = y + (direction & MV_UP ? -1 : direction & MV_DOWN ? +1 : 0);
2909 int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2910 int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2917 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
2919 int oldx = x, oldy = y;
2920 int direction = MovDir[x][y];
2922 if (direction == MV_LEFT)
2924 else if (direction == MV_RIGHT)
2926 else if (direction == MV_UP)
2928 else if (direction == MV_DOWN)
2931 *comes_from_x = oldx;
2932 *comes_from_y = oldy;
2935 int MovingOrBlocked2Element(int x, int y)
2937 int element = Feld[x][y];
2939 if (element == EL_BLOCKED)
2943 Blocked2Moving(x, y, &oldx, &oldy);
2944 return Feld[oldx][oldy];
2950 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
2952 /* like MovingOrBlocked2Element(), but if element is moving
2953 and (x,y) is the field the moving element is just leaving,
2954 return EL_BLOCKED instead of the element value */
2955 int element = Feld[x][y];
2957 if (IS_MOVING(x, y))
2959 if (element == EL_BLOCKED)
2963 Blocked2Moving(x, y, &oldx, &oldy);
2964 return Feld[oldx][oldy];
2973 static void RemoveField(int x, int y)
2975 Feld[x][y] = EL_EMPTY;
2981 #if USE_NEW_CUSTOM_VALUE
2982 CustomValue[x][y] = 0;
2986 ChangeDelay[x][y] = 0;
2987 ChangePage[x][y] = -1;
2988 Pushed[x][y] = FALSE;
2991 ExplodeField[x][y] = EX_TYPE_NONE;
2994 GfxElement[x][y] = EL_UNDEFINED;
2995 GfxAction[x][y] = ACTION_DEFAULT;
2996 GfxDir[x][y] = MV_NONE;
2999 void RemoveMovingField(int x, int y)
3001 int oldx = x, oldy = y, newx = x, newy = y;
3002 int element = Feld[x][y];
3003 int next_element = EL_UNDEFINED;
3005 if (element != EL_BLOCKED && !IS_MOVING(x, y))
3008 if (IS_MOVING(x, y))
3010 Moving2Blocked(x, y, &newx, &newy);
3012 if (Feld[newx][newy] != EL_BLOCKED)
3014 /* element is moving, but target field is not free (blocked), but
3015 already occupied by something different (example: acid pool);
3016 in this case, only remove the moving field, but not the target */
3018 RemoveField(oldx, oldy);
3020 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
3022 DrawLevelField(oldx, oldy);
3027 else if (element == EL_BLOCKED)
3029 Blocked2Moving(x, y, &oldx, &oldy);
3030 if (!IS_MOVING(oldx, oldy))
3034 if (element == EL_BLOCKED &&
3035 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
3036 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
3037 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
3038 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
3039 next_element = get_next_element(Feld[oldx][oldy]);
3041 RemoveField(oldx, oldy);
3042 RemoveField(newx, newy);
3044 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
3046 if (next_element != EL_UNDEFINED)
3047 Feld[oldx][oldy] = next_element;
3049 DrawLevelField(oldx, oldy);
3050 DrawLevelField(newx, newy);
3053 void DrawDynamite(int x, int y)
3055 int sx = SCREENX(x), sy = SCREENY(y);
3056 int graphic = el2img(Feld[x][y]);
3059 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
3062 if (IS_WALKABLE_INSIDE(Back[x][y]))
3066 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
3067 else if (Store[x][y])
3068 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
3070 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
3072 if (Back[x][y] || Store[x][y])
3073 DrawGraphicThruMask(sx, sy, graphic, frame);
3075 DrawGraphic(sx, sy, graphic, frame);
3078 void CheckDynamite(int x, int y)
3080 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
3084 if (MovDelay[x][y] != 0)
3087 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
3093 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
3098 void DrawRelocatePlayer(struct PlayerInfo *player)
3100 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3101 boolean no_delay = (tape.warp_forward);
3102 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
3103 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
3104 int jx = player->jx;
3105 int jy = player->jy;
3107 if (level.instant_relocation)
3109 int offset = (setup.scroll_delay ? 3 : 0);
3111 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
3113 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
3114 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
3115 local_player->jx - MIDPOSX);
3117 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
3118 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
3119 local_player->jy - MIDPOSY);
3123 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
3124 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
3125 scroll_x = jx - MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
3127 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
3128 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
3129 scroll_y = jy - MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
3131 /* don't scroll over playfield boundaries */
3132 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
3133 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
3135 /* don't scroll over playfield boundaries */
3136 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
3137 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
3140 RedrawPlayfield(TRUE, 0,0,0,0);
3144 int scroll_xx = -999, scroll_yy = -999;
3146 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
3148 while (scroll_xx != scroll_x || scroll_yy != scroll_y)
3151 int fx = FX, fy = FY;
3153 scroll_xx = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
3154 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
3155 local_player->jx - MIDPOSX);
3157 scroll_yy = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
3158 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
3159 local_player->jy - MIDPOSY);
3161 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
3162 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
3164 if (dx == 0 && dy == 0) /* no scrolling needed at all */
3170 fx += dx * TILEX / 2;
3171 fy += dy * TILEY / 2;
3173 ScrollLevel(dx, dy);
3176 /* scroll in two steps of half tile size to make things smoother */
3177 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
3179 Delay(wait_delay_value);
3181 /* scroll second step to align at full tile size */
3183 Delay(wait_delay_value);
3188 Delay(wait_delay_value);
3192 void RelocatePlayer(int jx, int jy, int el_player_raw)
3194 int el_player = GET_PLAYER_ELEMENT(el_player_raw);
3195 int player_nr = GET_PLAYER_NR(el_player);
3196 struct PlayerInfo *player = &stored_player[player_nr];
3197 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3198 boolean no_delay = (tape.warp_forward);
3199 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
3200 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
3201 int old_jx = player->jx;
3202 int old_jy = player->jy;
3203 int old_element = Feld[old_jx][old_jy];
3204 int element = Feld[jx][jy];
3205 boolean player_relocated = (old_jx != jx || old_jy != jy);
3207 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
3208 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
3209 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
3210 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
3211 int leave_side_horiz = move_dir_horiz;
3212 int leave_side_vert = move_dir_vert;
3213 int enter_side = enter_side_horiz | enter_side_vert;
3214 int leave_side = leave_side_horiz | leave_side_vert;
3216 if (player->GameOver) /* do not reanimate dead player */
3219 if (!player_relocated) /* no need to relocate the player */
3222 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
3224 RemoveField(jx, jy); /* temporarily remove newly placed player */
3225 DrawLevelField(jx, jy);
3228 if (player->present)
3230 while (player->MovPos)
3232 ScrollPlayer(player, SCROLL_GO_ON);
3233 ScrollScreen(NULL, SCROLL_GO_ON);
3235 AdvanceFrameAndPlayerCounters(player->index_nr);
3240 Delay(wait_delay_value);
3243 DrawPlayer(player); /* needed here only to cleanup last field */
3244 DrawLevelField(player->jx, player->jy); /* remove player graphic */
3246 player->is_moving = FALSE;
3249 if (IS_CUSTOM_ELEMENT(old_element))
3250 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
3252 player->index_bit, leave_side);
3254 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
3256 player->index_bit, leave_side);
3258 Feld[jx][jy] = el_player;
3259 InitPlayerField(jx, jy, el_player, TRUE);
3261 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
3263 Feld[jx][jy] = element;
3264 InitField(jx, jy, FALSE);
3267 if (player == local_player) /* only visually relocate local player */
3268 DrawRelocatePlayer(player);
3270 TestIfPlayerTouchesBadThing(jx, jy);
3271 TestIfPlayerTouchesCustomElement(jx, jy);
3273 if (IS_CUSTOM_ELEMENT(element))
3274 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
3275 player->index_bit, enter_side);
3277 CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
3278 player->index_bit, enter_side);
3281 void Explode(int ex, int ey, int phase, int mode)
3287 /* !!! eliminate this variable !!! */
3288 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
3290 if (game.explosions_delayed)
3292 ExplodeField[ex][ey] = mode;
3296 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
3298 int center_element = Feld[ex][ey];
3299 int artwork_element, explosion_element; /* set these values later */
3302 /* --- This is only really needed (and now handled) in "Impact()". --- */
3303 /* do not explode moving elements that left the explode field in time */
3304 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
3305 center_element == EL_EMPTY &&
3306 (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
3311 /* !!! at this place, the center element may be EL_BLOCKED !!! */
3312 if (mode == EX_TYPE_NORMAL ||
3313 mode == EX_TYPE_CENTER ||
3314 mode == EX_TYPE_CROSS)
3315 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
3318 /* remove things displayed in background while burning dynamite */
3319 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
3322 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
3324 /* put moving element to center field (and let it explode there) */
3325 center_element = MovingOrBlocked2Element(ex, ey);
3326 RemoveMovingField(ex, ey);
3327 Feld[ex][ey] = center_element;
3330 /* now "center_element" is finally determined -- set related values now */
3331 artwork_element = center_element; /* for custom player artwork */
3332 explosion_element = center_element; /* for custom player artwork */
3334 if (IS_PLAYER(ex, ey))
3336 int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
3338 artwork_element = stored_player[player_nr].artwork_element;
3340 if (level.use_explosion_element[player_nr])
3342 explosion_element = level.explosion_element[player_nr];
3343 artwork_element = explosion_element;
3348 if (mode == EX_TYPE_NORMAL ||
3349 mode == EX_TYPE_CENTER ||
3350 mode == EX_TYPE_CROSS)
3351 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
3355 last_phase = element_info[explosion_element].explosion_delay + 1;
3357 last_phase = element_info[center_element].explosion_delay + 1;
3360 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
3362 int xx = x - ex + 1;
3363 int yy = y - ey + 1;
3366 if (!IN_LEV_FIELD(x, y) ||
3367 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
3368 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
3371 element = Feld[x][y];
3373 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
3375 element = MovingOrBlocked2Element(x, y);
3377 if (!IS_EXPLOSION_PROOF(element))
3378 RemoveMovingField(x, y);
3381 /* indestructible elements can only explode in center (but not flames) */
3382 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
3383 mode == EX_TYPE_BORDER)) ||
3384 element == EL_FLAMES)
3387 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
3388 behaviour, for example when touching a yamyam that explodes to rocks
3389 with active deadly shield, a rock is created under the player !!! */
3390 /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
3392 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
3393 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
3394 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
3396 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
3399 if (IS_ACTIVE_BOMB(element))
3401 /* re-activate things under the bomb like gate or penguin */
3402 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
3409 /* save walkable background elements while explosion on same tile */
3410 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
3411 (x != ex || y != ey || mode == EX_TYPE_BORDER))
3412 Back[x][y] = element;
3414 /* ignite explodable elements reached by other explosion */
3415 if (element == EL_EXPLOSION)
3416 element = Store2[x][y];
3418 if (AmoebaNr[x][y] &&
3419 (element == EL_AMOEBA_FULL ||
3420 element == EL_BD_AMOEBA ||
3421 element == EL_AMOEBA_GROWING))
3423 AmoebaCnt[AmoebaNr[x][y]]--;
3424 AmoebaCnt2[AmoebaNr[x][y]]--;
3429 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
3432 int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
3434 Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
3436 switch(StorePlayer[ex][ey])
3439 Store[x][y] = EL_PLAYER_IS_EXPLODING_2;
3442 Store[x][y] = EL_PLAYER_IS_EXPLODING_3;
3445 Store[x][y] = EL_PLAYER_IS_EXPLODING_4;
3449 Store[x][y] = EL_PLAYER_IS_EXPLODING_1;
3454 if (PLAYERINFO(ex, ey)->use_murphy)
3455 Store[x][y] = EL_EMPTY;
3458 /* !!! check this case -- currently needed for rnd_rado_negundo_v,
3459 !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
3460 else if (ELEM_IS_PLAYER(center_element))
3461 Store[x][y] = EL_EMPTY;
3462 else if (center_element == EL_YAMYAM)
3463 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
3464 else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
3465 Store[x][y] = element_info[center_element].content.e[xx][yy];
3467 /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
3468 (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
3469 otherwise) -- FIX THIS !!! */
3470 else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
3471 Store[x][y] = element_info[element].content.e[1][1];
3473 else if (!CAN_EXPLODE(element))
3474 Store[x][y] = element_info[element].content.e[1][1];
3477 Store[x][y] = EL_EMPTY;
3479 else if (center_element == EL_MOLE)
3480 Store[x][y] = EL_EMERALD_RED;
3481 else if (center_element == EL_PENGUIN)
3482 Store[x][y] = EL_EMERALD_PURPLE;
3483 else if (center_element == EL_BUG)
3484 Store[x][y] = ((x == ex && y == ey) ? EL_DIAMOND : EL_EMERALD);
3485 else if (center_element == EL_BD_BUTTERFLY)
3486 Store[x][y] = EL_BD_DIAMOND;
3487 else if (center_element == EL_SP_ELECTRON)
3488 Store[x][y] = EL_SP_INFOTRON;
3489 else if (center_element == EL_AMOEBA_TO_DIAMOND)
3490 Store[x][y] = level.amoeba_content;
3491 else if (center_element == EL_YAMYAM)
3492 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
3493 else if (IS_CUSTOM_ELEMENT(center_element) &&
3494 element_info[center_element].content.e[xx][yy] != EL_EMPTY)
3495 Store[x][y] = element_info[center_element].content.e[xx][yy];
3496 else if (element == EL_WALL_EMERALD)
3497 Store[x][y] = EL_EMERALD;
3498 else if (element == EL_WALL_DIAMOND)
3499 Store[x][y] = EL_DIAMOND;
3500 else if (element == EL_WALL_BD_DIAMOND)
3501 Store[x][y] = EL_BD_DIAMOND;
3502 else if (element == EL_WALL_EMERALD_YELLOW)
3503 Store[x][y] = EL_EMERALD_YELLOW;
3504 else if (element == EL_WALL_EMERALD_RED)
3505 Store[x][y] = EL_EMERALD_RED;
3506 else if (element == EL_WALL_EMERALD_PURPLE)
3507 Store[x][y] = EL_EMERALD_PURPLE;
3508 else if (element == EL_WALL_PEARL)
3509 Store[x][y] = EL_PEARL;
3510 else if (element == EL_WALL_CRYSTAL)
3511 Store[x][y] = EL_CRYSTAL;
3512 else if (IS_CUSTOM_ELEMENT(element) && !CAN_EXPLODE(element))
3513 Store[x][y] = element_info[element].content.e[1][1];
3515 Store[x][y] = EL_EMPTY;
3518 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
3519 center_element == EL_AMOEBA_TO_DIAMOND)
3520 Store2[x][y] = element;
3522 Feld[x][y] = EL_EXPLOSION;
3523 GfxElement[x][y] = artwork_element;
3525 ExplodePhase[x][y] = 1;
3526 ExplodeDelay[x][y] = last_phase;
3531 if (center_element == EL_YAMYAM)
3532 game.yamyam_content_nr =
3533 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
3545 GfxFrame[x][y] = 0; /* restart explosion animation */
3547 last_phase = ExplodeDelay[x][y];
3549 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
3553 /* activate this even in non-DEBUG version until cause for crash in
3554 getGraphicAnimationFrame() (see below) is found and eliminated */
3559 if (GfxElement[x][y] == EL_UNDEFINED)
3562 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
3563 printf("Explode(): This should never happen!\n");
3566 GfxElement[x][y] = EL_EMPTY;
3570 border_element = Store2[x][y];
3571 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3572 border_element = StorePlayer[x][y];
3574 if (phase == element_info[border_element].ignition_delay ||
3575 phase == last_phase)
3577 boolean border_explosion = FALSE;
3579 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
3580 !PLAYER_EXPLOSION_PROTECTED(x, y))
3582 KillPlayerUnlessExplosionProtected(x, y);
3583 border_explosion = TRUE;
3585 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
3587 Feld[x][y] = Store2[x][y];
3590 border_explosion = TRUE;
3592 else if (border_element == EL_AMOEBA_TO_DIAMOND)
3594 AmoebeUmwandeln(x, y);
3596 border_explosion = TRUE;
3599 /* if an element just explodes due to another explosion (chain-reaction),
3600 do not immediately end the new explosion when it was the last frame of
3601 the explosion (as it would be done in the following "if"-statement!) */
3602 if (border_explosion && phase == last_phase)
3606 if (phase == last_phase)
3610 element = Feld[x][y] = Store[x][y];
3611 Store[x][y] = Store2[x][y] = 0;
3612 GfxElement[x][y] = EL_UNDEFINED;
3614 /* player can escape from explosions and might therefore be still alive */
3615 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
3616 element <= EL_PLAYER_IS_EXPLODING_4)
3618 int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
3619 int explosion_element = EL_PLAYER_1 + player_nr;
3620 int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
3621 int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
3623 if (level.use_explosion_element[player_nr])
3624 explosion_element = level.explosion_element[player_nr];
3626 Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
3627 element_info[explosion_element].content.e[xx][yy]);
3630 /* restore probably existing indestructible background element */
3631 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
3632 element = Feld[x][y] = Back[x][y];
3635 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
3636 GfxDir[x][y] = MV_NONE;
3637 ChangeDelay[x][y] = 0;
3638 ChangePage[x][y] = -1;
3640 #if USE_NEW_CUSTOM_VALUE
3641 CustomValue[x][y] = 0;
3644 InitField_WithBug2(x, y, FALSE);
3646 DrawLevelField(x, y);
3648 TestIfElementTouchesCustomElement(x, y);
3650 if (GFX_CRUMBLED(element))
3651 DrawLevelFieldCrumbledSandNeighbours(x, y);
3653 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
3654 StorePlayer[x][y] = 0;
3656 if (ELEM_IS_PLAYER(element))
3657 RelocatePlayer(x, y, element);
3659 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3661 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
3662 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
3665 DrawLevelFieldCrumbledSand(x, y);
3667 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
3669 DrawLevelElement(x, y, Back[x][y]);
3670 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
3672 else if (IS_WALKABLE_UNDER(Back[x][y]))
3674 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3675 DrawLevelElementThruMask(x, y, Back[x][y]);
3677 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
3678 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3682 void DynaExplode(int ex, int ey)
3685 int dynabomb_element = Feld[ex][ey];
3686 int dynabomb_size = 1;
3687 boolean dynabomb_xl = FALSE;
3688 struct PlayerInfo *player;
3689 static int xy[4][2] =
3697 if (IS_ACTIVE_BOMB(dynabomb_element))
3699 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
3700 dynabomb_size = player->dynabomb_size;
3701 dynabomb_xl = player->dynabomb_xl;
3702 player->dynabombs_left++;
3705 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
3707 for (i = 0; i < NUM_DIRECTIONS; i++)
3709 for (j = 1; j <= dynabomb_size; j++)
3711 int x = ex + j * xy[i][0];
3712 int y = ey + j * xy[i][1];
3715 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
3718 element = Feld[x][y];
3720 /* do not restart explosions of fields with active bombs */
3721 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
3724 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
3726 if (element != EL_EMPTY && element != EL_EXPLOSION &&
3727 !IS_DIGGABLE(element) && !dynabomb_xl)
3733 void Bang(int x, int y)
3735 int element = MovingOrBlocked2Element(x, y);
3736 int explosion_type = EX_TYPE_NORMAL;
3738 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3740 struct PlayerInfo *player = PLAYERINFO(x, y);
3742 element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
3743 player->element_nr);
3745 if (level.use_explosion_element[player->index_nr])
3747 int explosion_element = level.explosion_element[player->index_nr];
3749 if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
3750 explosion_type = EX_TYPE_CROSS;
3751 else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
3752 explosion_type = EX_TYPE_CENTER;
3760 case EL_BD_BUTTERFLY:
3763 case EL_DARK_YAMYAM:
3767 RaiseScoreElement(element);
3770 case EL_DYNABOMB_PLAYER_1_ACTIVE:
3771 case EL_DYNABOMB_PLAYER_2_ACTIVE:
3772 case EL_DYNABOMB_PLAYER_3_ACTIVE:
3773 case EL_DYNABOMB_PLAYER_4_ACTIVE:
3774 case EL_DYNABOMB_INCREASE_NUMBER:
3775 case EL_DYNABOMB_INCREASE_SIZE:
3776 case EL_DYNABOMB_INCREASE_POWER:
3777 explosion_type = EX_TYPE_DYNA;
3782 case EL_LAMP_ACTIVE:
3783 case EL_AMOEBA_TO_DIAMOND:
3784 if (!IS_PLAYER(x, y)) /* penguin and player may be at same field */
3785 explosion_type = EX_TYPE_CENTER;
3789 if (element_info[element].explosion_type == EXPLODES_CROSS)
3790 explosion_type = EX_TYPE_CROSS;
3791 else if (element_info[element].explosion_type == EXPLODES_1X1)
3792 explosion_type = EX_TYPE_CENTER;
3796 if (explosion_type == EX_TYPE_DYNA)
3799 Explode(x, y, EX_PHASE_START, explosion_type);
3801 CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
3804 void SplashAcid(int x, int y)
3806 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
3807 (!IN_LEV_FIELD(x - 1, y - 2) ||
3808 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
3809 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
3811 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
3812 (!IN_LEV_FIELD(x + 1, y - 2) ||
3813 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
3814 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
3816 PlayLevelSound(x, y, SND_ACID_SPLASHING);
3819 static void InitBeltMovement()
3821 static int belt_base_element[4] =
3823 EL_CONVEYOR_BELT_1_LEFT,
3824 EL_CONVEYOR_BELT_2_LEFT,
3825 EL_CONVEYOR_BELT_3_LEFT,
3826 EL_CONVEYOR_BELT_4_LEFT
3828 static int belt_base_active_element[4] =
3830 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
3831 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
3832 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
3833 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
3838 /* set frame order for belt animation graphic according to belt direction */
3839 for (i = 0; i < NUM_BELTS; i++)
3843 for (j = 0; j < NUM_BELT_PARTS; j++)
3845 int element = belt_base_active_element[belt_nr] + j;
3846 int graphic = el2img(element);
3848 if (game.belt_dir[i] == MV_LEFT)
3849 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
3851 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
3856 SCAN_PLAYFIELD(x, y)
3858 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
3861 int element = Feld[x][y];
3863 for (i = 0; i < NUM_BELTS; i++)
3865 if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
3867 int e_belt_nr = getBeltNrFromBeltElement(element);
3870 if (e_belt_nr == belt_nr)
3872 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
3874 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
3881 static void ToggleBeltSwitch(int x, int y)
3883 static int belt_base_element[4] =
3885 EL_CONVEYOR_BELT_1_LEFT,
3886 EL_CONVEYOR_BELT_2_LEFT,
3887 EL_CONVEYOR_BELT_3_LEFT,
3888 EL_CONVEYOR_BELT_4_LEFT
3890 static int belt_base_active_element[4] =
3892 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
3893 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
3894 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
3895 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
3897 static int belt_base_switch_element[4] =
3899 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
3900 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
3901 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
3902 EL_CONVEYOR_BELT_4_SWITCH_LEFT
3904 static int belt_move_dir[4] =
3912 int element = Feld[x][y];
3913 int belt_nr = getBeltNrFromBeltSwitchElement(element);
3914 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
3915 int belt_dir = belt_move_dir[belt_dir_nr];
3918 if (!IS_BELT_SWITCH(element))
3921 game.belt_dir_nr[belt_nr] = belt_dir_nr;
3922 game.belt_dir[belt_nr] = belt_dir;
3924 if (belt_dir_nr == 3)
3927 /* set frame order for belt animation graphic according to belt direction */
3928 for (i = 0; i < NUM_BELT_PARTS; i++)
3930 int element = belt_base_active_element[belt_nr] + i;
3931 int graphic = el2img(element);
3933 if (belt_dir == MV_LEFT)
3934 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
3936 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
3940 SCAN_PLAYFIELD(xx, yy)
3942 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
3945 int element = Feld[xx][yy];
3947 if (IS_BELT_SWITCH(element))
3949 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
3951 if (e_belt_nr == belt_nr)
3953 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
3954 DrawLevelField(xx, yy);
3957 else if (IS_BELT(element) && belt_dir != MV_NONE)
3959 int e_belt_nr = getBeltNrFromBeltElement(element);
3961 if (e_belt_nr == belt_nr)
3963 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
3965 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
3966 DrawLevelField(xx, yy);
3969 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
3971 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
3973 if (e_belt_nr == belt_nr)
3975 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
3977 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
3978 DrawLevelField(xx, yy);
3984 static void ToggleSwitchgateSwitch(int x, int y)
3988 game.switchgate_pos = !game.switchgate_pos;
3991 SCAN_PLAYFIELD(xx, yy)
3993 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
3996 int element = Feld[xx][yy];
3998 if (element == EL_SWITCHGATE_SWITCH_UP ||
3999 element == EL_SWITCHGATE_SWITCH_DOWN)
4001 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
4002 DrawLevelField(xx, yy);
4004 else if (element == EL_SWITCHGATE_OPEN ||
4005 element == EL_SWITCHGATE_OPENING)
4007 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
4009 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
4011 else if (element == EL_SWITCHGATE_CLOSED ||
4012 element == EL_SWITCHGATE_CLOSING)
4014 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
4016 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
4021 static int getInvisibleActiveFromInvisibleElement(int element)
4023 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
4024 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
4025 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
4029 static int getInvisibleFromInvisibleActiveElement(int element)
4031 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
4032 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
4033 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
4037 static void RedrawAllLightSwitchesAndInvisibleElements()
4042 SCAN_PLAYFIELD(x, y)
4044 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
4047 int element = Feld[x][y];
4049 if (element == EL_LIGHT_SWITCH &&
4050 game.light_time_left > 0)
4052 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
4053 DrawLevelField(x, y);
4055 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
4056 game.light_time_left == 0)
4058 Feld[x][y] = EL_LIGHT_SWITCH;
4059 DrawLevelField(x, y);
4061 else if (element == EL_EMC_DRIPPER &&
4062 game.light_time_left > 0)
4064 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
4065 DrawLevelField(x, y);
4067 else if (element == EL_EMC_DRIPPER_ACTIVE &&
4068 game.light_time_left == 0)
4070 Feld[x][y] = EL_EMC_DRIPPER;
4071 DrawLevelField(x, y);
4073 else if (element == EL_INVISIBLE_STEELWALL ||
4074 element == EL_INVISIBLE_WALL ||
4075 element == EL_INVISIBLE_SAND)
4077 if (game.light_time_left > 0)
4078 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
4080 DrawLevelField(x, y);
4082 /* uncrumble neighbour fields, if needed */
4083 if (element == EL_INVISIBLE_SAND)
4084 DrawLevelFieldCrumbledSandNeighbours(x, y);
4086 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
4087 element == EL_INVISIBLE_WALL_ACTIVE ||
4088 element == EL_INVISIBLE_SAND_ACTIVE)
4090 if (game.light_time_left == 0)
4091 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
4093 DrawLevelField(x, y);
4095 /* re-crumble neighbour fields, if needed */
4096 if (element == EL_INVISIBLE_SAND)
4097 DrawLevelFieldCrumbledSandNeighbours(x, y);
4102 static void RedrawAllInvisibleElementsForLenses()
4107 SCAN_PLAYFIELD(x, y)
4109 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
4112 int element = Feld[x][y];
4114 if (element == EL_EMC_DRIPPER &&
4115 game.lenses_time_left > 0)
4117 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
4118 DrawLevelField(x, y);
4120 else if (element == EL_EMC_DRIPPER_ACTIVE &&
4121 game.lenses_time_left == 0)
4123 Feld[x][y] = EL_EMC_DRIPPER;
4124 DrawLevelField(x, y);
4126 else if (element == EL_INVISIBLE_STEELWALL ||
4127 element == EL_INVISIBLE_WALL ||
4128 element == EL_INVISIBLE_SAND)
4130 if (game.lenses_time_left > 0)
4131 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
4133 DrawLevelField(x, y);
4135 /* uncrumble neighbour fields, if needed */
4136 if (element == EL_INVISIBLE_SAND)
4137 DrawLevelFieldCrumbledSandNeighbours(x, y);
4139 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
4140 element == EL_INVISIBLE_WALL_ACTIVE ||
4141 element == EL_INVISIBLE_SAND_ACTIVE)
4143 if (game.lenses_time_left == 0)
4144 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
4146 DrawLevelField(x, y);
4148 /* re-crumble neighbour fields, if needed */
4149 if (element == EL_INVISIBLE_SAND)
4150 DrawLevelFieldCrumbledSandNeighbours(x, y);
4155 static void RedrawAllInvisibleElementsForMagnifier()
4160 SCAN_PLAYFIELD(x, y)
4162 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
4165 int element = Feld[x][y];
4167 if (element == EL_EMC_FAKE_GRASS &&
4168 game.magnify_time_left > 0)
4170 Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
4171 DrawLevelField(x, y);
4173 else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
4174 game.magnify_time_left == 0)
4176 Feld[x][y] = EL_EMC_FAKE_GRASS;
4177 DrawLevelField(x, y);
4179 else if (IS_GATE_GRAY(element) &&
4180 game.magnify_time_left > 0)
4182 Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
4183 element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
4184 IS_EM_GATE_GRAY(element) ?
4185 element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
4186 IS_EMC_GATE_GRAY(element) ?
4187 element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
4189 DrawLevelField(x, y);
4191 else if (IS_GATE_GRAY_ACTIVE(element) &&
4192 game.magnify_time_left == 0)
4194 Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
4195 element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
4196 IS_EM_GATE_GRAY_ACTIVE(element) ?
4197 element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
4198 IS_EMC_GATE_GRAY_ACTIVE(element) ?
4199 element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
4201 DrawLevelField(x, y);
4206 static void ToggleLightSwitch(int x, int y)
4208 int element = Feld[x][y];
4210 game.light_time_left =
4211 (element == EL_LIGHT_SWITCH ?
4212 level.time_light * FRAMES_PER_SECOND : 0);
4214 RedrawAllLightSwitchesAndInvisibleElements();
4217 static void ActivateTimegateSwitch(int x, int y)
4221 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
4224 SCAN_PLAYFIELD(xx, yy)
4226 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
4229 int element = Feld[xx][yy];
4231 if (element == EL_TIMEGATE_CLOSED ||
4232 element == EL_TIMEGATE_CLOSING)
4234 Feld[xx][yy] = EL_TIMEGATE_OPENING;
4235 PlayLevelSound(xx, yy, SND_TIMEGATE_OPENING);
4239 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
4241 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
4242 DrawLevelField(xx, yy);
4248 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
4251 void Impact(int x, int y)
4253 boolean last_line = (y == lev_fieldy - 1);
4254 boolean object_hit = FALSE;
4255 boolean impact = (last_line || object_hit);
4256 int element = Feld[x][y];
4257 int smashed = EL_STEELWALL;
4259 if (!last_line) /* check if element below was hit */
4261 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
4264 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
4265 MovDir[x][y + 1] != MV_DOWN ||
4266 MovPos[x][y + 1] <= TILEY / 2));
4268 /* do not smash moving elements that left the smashed field in time */
4269 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
4270 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
4273 #if USE_QUICKSAND_IMPACT_BUGFIX
4274 if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
4276 RemoveMovingField(x, y + 1);
4277 Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
4278 Feld[x][y + 2] = EL_ROCK;
4279 DrawLevelField(x, y + 2);
4286 smashed = MovingOrBlocked2Element(x, y + 1);
4288 impact = (last_line || object_hit);
4291 if (!last_line && smashed == EL_ACID) /* element falls into acid */
4293 SplashAcid(x, y + 1);
4297 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
4298 /* only reset graphic animation if graphic really changes after impact */
4300 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
4302 ResetGfxAnimation(x, y);
4303 DrawLevelField(x, y);
4306 if (impact && CAN_EXPLODE_IMPACT(element))
4311 else if (impact && element == EL_PEARL)
4313 ResetGfxAnimation(x, y);
4315 Feld[x][y] = EL_PEARL_BREAKING;
4316 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4319 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
4321 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4326 if (impact && element == EL_AMOEBA_DROP)
4328 if (object_hit && IS_PLAYER(x, y + 1))
4329 KillPlayerUnlessEnemyProtected(x, y + 1);
4330 else if (object_hit && smashed == EL_PENGUIN)
4334 Feld[x][y] = EL_AMOEBA_GROWING;
4335 Store[x][y] = EL_AMOEBA_WET;
4337 ResetRandomAnimationValue(x, y);
4342 if (object_hit) /* check which object was hit */
4344 if (CAN_PASS_MAGIC_WALL(element) &&
4345 (smashed == EL_MAGIC_WALL ||
4346 smashed == EL_BD_MAGIC_WALL))
4349 int activated_magic_wall =
4350 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
4351 EL_BD_MAGIC_WALL_ACTIVE);
4353 /* activate magic wall / mill */
4355 SCAN_PLAYFIELD(xx, yy)
4357 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
4359 if (Feld[xx][yy] == smashed)
4360 Feld[xx][yy] = activated_magic_wall;
4362 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
4363 game.magic_wall_active = TRUE;
4365 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
4366 SND_MAGIC_WALL_ACTIVATING :
4367 SND_BD_MAGIC_WALL_ACTIVATING));
4370 if (IS_PLAYER(x, y + 1))
4372 if (CAN_SMASH_PLAYER(element))
4374 KillPlayerUnlessEnemyProtected(x, y + 1);
4378 else if (smashed == EL_PENGUIN)
4380 if (CAN_SMASH_PLAYER(element))
4386 else if (element == EL_BD_DIAMOND)
4388 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
4394 else if (((element == EL_SP_INFOTRON ||
4395 element == EL_SP_ZONK) &&
4396 (smashed == EL_SP_SNIKSNAK ||
4397 smashed == EL_SP_ELECTRON ||
4398 smashed == EL_SP_DISK_ORANGE)) ||
4399 (element == EL_SP_INFOTRON &&
4400 smashed == EL_SP_DISK_YELLOW))
4405 else if (CAN_SMASH_EVERYTHING(element))
4407 if (IS_CLASSIC_ENEMY(smashed) ||
4408 CAN_EXPLODE_SMASHED(smashed))
4413 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
4415 if (smashed == EL_LAMP ||
4416 smashed == EL_LAMP_ACTIVE)
4421 else if (smashed == EL_NUT)
4423 Feld[x][y + 1] = EL_NUT_BREAKING;
4424 PlayLevelSound(x, y, SND_NUT_BREAKING);
4425 RaiseScoreElement(EL_NUT);
4428 else if (smashed == EL_PEARL)
4430 ResetGfxAnimation(x, y);
4432 Feld[x][y + 1] = EL_PEARL_BREAKING;
4433 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4436 else if (smashed == EL_DIAMOND)
4438 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
4439 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
4442 else if (IS_BELT_SWITCH(smashed))
4444 ToggleBeltSwitch(x, y + 1);
4446 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
4447 smashed == EL_SWITCHGATE_SWITCH_DOWN)
4449 ToggleSwitchgateSwitch(x, y + 1);
4451 else if (smashed == EL_LIGHT_SWITCH ||
4452 smashed == EL_LIGHT_SWITCH_ACTIVE)
4454 ToggleLightSwitch(x, y + 1);
4459 TestIfElementSmashesCustomElement(x, y, MV_DOWN);
4462 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4464 CheckElementChangeBySide(x, y + 1, smashed, element,
4465 CE_SWITCHED, CH_SIDE_TOP);
4466 CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
4472 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4477 /* play sound of magic wall / mill */
4479 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
4480 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
4482 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
4483 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
4484 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
4485 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
4490 /* play sound of object that hits the ground */
4491 if (last_line || object_hit)
4492 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4495 inline static void TurnRoundExt(int x, int y)
4507 { 0, 0 }, { 0, 0 }, { 0, 0 },
4512 int left, right, back;
4516 { MV_DOWN, MV_UP, MV_RIGHT },
4517 { MV_UP, MV_DOWN, MV_LEFT },
4519 { MV_LEFT, MV_RIGHT, MV_DOWN },
4523 { MV_RIGHT, MV_LEFT, MV_UP }
4526 int element = Feld[x][y];
4527 int move_pattern = element_info[element].move_pattern;
4529 int old_move_dir = MovDir[x][y];
4530 int left_dir = turn[old_move_dir].left;
4531 int right_dir = turn[old_move_dir].right;
4532 int back_dir = turn[old_move_dir].back;
4534 int left_dx = move_xy[left_dir].dx, left_dy = move_xy[left_dir].dy;
4535 int right_dx = move_xy[right_dir].dx, right_dy = move_xy[right_dir].dy;
4536 int move_dx = move_xy[old_move_dir].dx, move_dy = move_xy[old_move_dir].dy;
4537 int back_dx = move_xy[back_dir].dx, back_dy = move_xy[back_dir].dy;
4539 int left_x = x + left_dx, left_y = y + left_dy;
4540 int right_x = x + right_dx, right_y = y + right_dy;
4541 int move_x = x + move_dx, move_y = y + move_dy;
4545 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4547 TestIfBadThingTouchesOtherBadThing(x, y);
4549 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
4550 MovDir[x][y] = right_dir;
4551 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4552 MovDir[x][y] = left_dir;
4554 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
4556 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
4559 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
4561 TestIfBadThingTouchesOtherBadThing(x, y);
4563 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
4564 MovDir[x][y] = left_dir;
4565 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4566 MovDir[x][y] = right_dir;
4568 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
4570 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
4573 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4575 TestIfBadThingTouchesOtherBadThing(x, y);
4577 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
4578 MovDir[x][y] = left_dir;
4579 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
4580 MovDir[x][y] = right_dir;
4582 if (MovDir[x][y] != old_move_dir)
4585 else if (element == EL_YAMYAM)
4587 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
4588 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
4590 if (can_turn_left && can_turn_right)
4591 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4592 else if (can_turn_left)
4593 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4594 else if (can_turn_right)
4595 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4597 MovDir[x][y] = back_dir;
4599 MovDelay[x][y] = 16 + 16 * RND(3);
4601 else if (element == EL_DARK_YAMYAM)
4603 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4605 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4608 if (can_turn_left && can_turn_right)
4609 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4610 else if (can_turn_left)
4611 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4612 else if (can_turn_right)
4613 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4615 MovDir[x][y] = back_dir;
4617 MovDelay[x][y] = 16 + 16 * RND(3);
4619 else if (element == EL_PACMAN)
4621 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
4622 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
4624 if (can_turn_left && can_turn_right)
4625 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4626 else if (can_turn_left)
4627 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4628 else if (can_turn_right)
4629 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4631 MovDir[x][y] = back_dir;
4633 MovDelay[x][y] = 6 + RND(40);
4635 else if (element == EL_PIG)
4637 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
4638 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
4639 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
4640 boolean should_turn_left, should_turn_right, should_move_on;
4642 int rnd = RND(rnd_value);
4644 should_turn_left = (can_turn_left &&
4646 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
4647 y + back_dy + left_dy)));
4648 should_turn_right = (can_turn_right &&
4650 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
4651 y + back_dy + right_dy)));
4652 should_move_on = (can_move_on &&
4655 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
4656 y + move_dy + left_dy) ||
4657 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
4658 y + move_dy + right_dy)));
4660 if (should_turn_left || should_turn_right || should_move_on)
4662 if (should_turn_left && should_turn_right && should_move_on)
4663 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
4664 rnd < 2 * rnd_value / 3 ? right_dir :
4666 else if (should_turn_left && should_turn_right)
4667 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4668 else if (should_turn_left && should_move_on)
4669 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
4670 else if (should_turn_right && should_move_on)
4671 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
4672 else if (should_turn_left)
4673 MovDir[x][y] = left_dir;
4674 else if (should_turn_right)
4675 MovDir[x][y] = right_dir;
4676 else if (should_move_on)
4677 MovDir[x][y] = old_move_dir;
4679 else if (can_move_on && rnd > rnd_value / 8)
4680 MovDir[x][y] = old_move_dir;
4681 else if (can_turn_left && can_turn_right)
4682 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4683 else if (can_turn_left && rnd > rnd_value / 8)
4684 MovDir[x][y] = left_dir;
4685 else if (can_turn_right && rnd > rnd_value/8)
4686 MovDir[x][y] = right_dir;
4688 MovDir[x][y] = back_dir;
4690 xx = x + move_xy[MovDir[x][y]].dx;
4691 yy = y + move_xy[MovDir[x][y]].dy;
4693 if (!IN_LEV_FIELD(xx, yy) ||
4694 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
4695 MovDir[x][y] = old_move_dir;
4699 else if (element == EL_DRAGON)
4701 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
4702 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
4703 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
4705 int rnd = RND(rnd_value);
4707 if (can_move_on && rnd > rnd_value / 8)
4708 MovDir[x][y] = old_move_dir;
4709 else if (can_turn_left && can_turn_right)
4710 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4711 else if (can_turn_left && rnd > rnd_value / 8)
4712 MovDir[x][y] = left_dir;
4713 else if (can_turn_right && rnd > rnd_value / 8)
4714 MovDir[x][y] = right_dir;
4716 MovDir[x][y] = back_dir;
4718 xx = x + move_xy[MovDir[x][y]].dx;
4719 yy = y + move_xy[MovDir[x][y]].dy;
4721 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
4722 MovDir[x][y] = old_move_dir;
4726 else if (element == EL_MOLE)
4728 boolean can_move_on =
4729 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
4730 IS_AMOEBOID(Feld[move_x][move_y]) ||
4731 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
4734 boolean can_turn_left =
4735 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
4736 IS_AMOEBOID(Feld[left_x][left_y])));
4738 boolean can_turn_right =
4739 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
4740 IS_AMOEBOID(Feld[right_x][right_y])));
4742 if (can_turn_left && can_turn_right)
4743 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
4744 else if (can_turn_left)
4745 MovDir[x][y] = left_dir;
4747 MovDir[x][y] = right_dir;
4750 if (MovDir[x][y] != old_move_dir)
4753 else if (element == EL_BALLOON)
4755 MovDir[x][y] = game.wind_direction;
4758 else if (element == EL_SPRING)
4760 #if USE_NEW_SPRING_BUMPER
4761 if (MovDir[x][y] & MV_HORIZONTAL)
4763 if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
4764 !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
4766 Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
4767 ResetGfxAnimation(move_x, move_y);
4768 DrawLevelField(move_x, move_y);
4770 MovDir[x][y] = back_dir;
4772 else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
4773 SPRING_CAN_ENTER_FIELD(element, x, y + 1))
4774 MovDir[x][y] = MV_NONE;
4777 if (MovDir[x][y] & MV_HORIZONTAL &&
4778 (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
4779 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
4780 MovDir[x][y] = MV_NONE;
4785 else if (element == EL_ROBOT ||
4786 element == EL_SATELLITE ||
4787 element == EL_PENGUIN ||
4788 element == EL_EMC_ANDROID)
4790 int attr_x = -1, attr_y = -1;
4801 for (i = 0; i < MAX_PLAYERS; i++)
4803 struct PlayerInfo *player = &stored_player[i];
4804 int jx = player->jx, jy = player->jy;
4806 if (!player->active)
4810 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
4818 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
4819 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
4820 game.engine_version < VERSION_IDENT(3,1,0,0)))
4826 if (element == EL_PENGUIN)
4829 static int xy[4][2] =
4837 for (i = 0; i < NUM_DIRECTIONS; i++)
4839 int ex = x + xy[i][0];
4840 int ey = y + xy[i][1];
4842 if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
4851 MovDir[x][y] = MV_NONE;
4853 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
4854 else if (attr_x > x)
4855 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
4857 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
4858 else if (attr_y > y)
4859 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
4861 if (element == EL_ROBOT)
4865 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4866 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
4867 Moving2Blocked(x, y, &newx, &newy);
4869 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
4870 MovDelay[x][y] = 8 + 8 * !RND(3);
4872 MovDelay[x][y] = 16;
4874 else if (element == EL_PENGUIN)
4880 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4882 boolean first_horiz = RND(2);
4883 int new_move_dir = MovDir[x][y];
4886 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4887 Moving2Blocked(x, y, &newx, &newy);
4889 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
4893 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4894 Moving2Blocked(x, y, &newx, &newy);
4896 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
4899 MovDir[x][y] = old_move_dir;
4903 else if (element == EL_SATELLITE)
4909 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4911 boolean first_horiz = RND(2);
4912 int new_move_dir = MovDir[x][y];
4915 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4916 Moving2Blocked(x, y, &newx, &newy);
4918 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
4922 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4923 Moving2Blocked(x, y, &newx, &newy);
4925 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
4928 MovDir[x][y] = old_move_dir;
4932 else if (element == EL_EMC_ANDROID)
4934 static int check_pos[16] =
4936 -1, /* 0 => (invalid) */
4937 7, /* 1 => MV_LEFT */
4938 3, /* 2 => MV_RIGHT */
4939 -1, /* 3 => (invalid) */
4941 0, /* 5 => MV_LEFT | MV_UP */
4942 2, /* 6 => MV_RIGHT | MV_UP */
4943 -1, /* 7 => (invalid) */
4944 5, /* 8 => MV_DOWN */
4945 6, /* 9 => MV_LEFT | MV_DOWN */
4946 4, /* 10 => MV_RIGHT | MV_DOWN */
4947 -1, /* 11 => (invalid) */
4948 -1, /* 12 => (invalid) */
4949 -1, /* 13 => (invalid) */
4950 -1, /* 14 => (invalid) */
4951 -1, /* 15 => (invalid) */
4959 { -1, -1, MV_LEFT | MV_UP },
4961 { +1, -1, MV_RIGHT | MV_UP },
4962 { +1, 0, MV_RIGHT },
4963 { +1, +1, MV_RIGHT | MV_DOWN },
4965 { -1, +1, MV_LEFT | MV_DOWN },
4968 int start_pos, check_order;
4969 boolean can_clone = FALSE;
4972 /* check if there is any free field around current position */
4973 for (i = 0; i < 8; i++)
4975 int newx = x + check_xy[i].dx;
4976 int newy = y + check_xy[i].dy;
4978 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
4986 if (can_clone) /* randomly find an element to clone */
4990 start_pos = check_pos[RND(8)];
4991 check_order = (RND(2) ? -1 : +1);
4993 for (i = 0; i < 8; i++)
4995 int pos_raw = start_pos + i * check_order;
4996 int pos = (pos_raw + 8) % 8;
4997 int newx = x + check_xy[pos].dx;
4998 int newy = y + check_xy[pos].dy;
5000 if (ANDROID_CAN_CLONE_FIELD(newx, newy))
5002 element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
5003 element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
5005 Store[x][y] = Feld[newx][newy];
5014 if (can_clone) /* randomly find a direction to move */
5018 start_pos = check_pos[RND(8)];
5019 check_order = (RND(2) ? -1 : +1);
5021 for (i = 0; i < 8; i++)
5023 int pos_raw = start_pos + i * check_order;
5024 int pos = (pos_raw + 8) % 8;
5025 int newx = x + check_xy[pos].dx;
5026 int newy = y + check_xy[pos].dy;
5027 int new_move_dir = check_xy[pos].dir;
5029 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5031 MovDir[x][y] = new_move_dir;
5032 MovDelay[x][y] = level.android_clone_time * 8 + 1;
5041 if (can_clone) /* cloning and moving successful */
5044 /* cannot clone -- try to move towards player */
5046 start_pos = check_pos[MovDir[x][y] & 0x0f];
5047 check_order = (RND(2) ? -1 : +1);
5049 for (i = 0; i < 3; i++)
5051 /* first check start_pos, then previous/next or (next/previous) pos */
5052 int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
5053 int pos = (pos_raw + 8) % 8;
5054 int newx = x + check_xy[pos].dx;
5055 int newy = y + check_xy[pos].dy;
5056 int new_move_dir = check_xy[pos].dir;
5058 if (IS_PLAYER(newx, newy))
5061 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
5063 MovDir[x][y] = new_move_dir;
5064 MovDelay[x][y] = level.android_move_time * 8 + 1;
5071 else if (move_pattern == MV_TURNING_LEFT ||
5072 move_pattern == MV_TURNING_RIGHT ||
5073 move_pattern == MV_TURNING_LEFT_RIGHT ||
5074 move_pattern == MV_TURNING_RIGHT_LEFT ||
5075 move_pattern == MV_TURNING_RANDOM ||
5076 move_pattern == MV_ALL_DIRECTIONS)
5078 boolean can_turn_left =
5079 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
5080 boolean can_turn_right =
5081 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
5083 if (element_info[element].move_stepsize == 0) /* "not moving" */
5086 if (move_pattern == MV_TURNING_LEFT)
5087 MovDir[x][y] = left_dir;
5088 else if (move_pattern == MV_TURNING_RIGHT)
5089 MovDir[x][y] = right_dir;
5090 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
5091 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
5092 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
5093 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
5094 else if (move_pattern == MV_TURNING_RANDOM)
5095 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
5096 can_turn_right && !can_turn_left ? right_dir :
5097 RND(2) ? left_dir : right_dir);
5098 else if (can_turn_left && can_turn_right)
5099 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5100 else if (can_turn_left)
5101 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5102 else if (can_turn_right)
5103 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5105 MovDir[x][y] = back_dir;
5107 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5109 else if (move_pattern == MV_HORIZONTAL ||
5110 move_pattern == MV_VERTICAL)
5112 if (move_pattern & old_move_dir)
5113 MovDir[x][y] = back_dir;
5114 else if (move_pattern == MV_HORIZONTAL)
5115 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
5116 else if (move_pattern == MV_VERTICAL)
5117 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
5119 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5121 else if (move_pattern & MV_ANY_DIRECTION)
5123 MovDir[x][y] = move_pattern;
5124 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5126 else if (move_pattern & MV_WIND_DIRECTION)
5128 MovDir[x][y] = game.wind_direction;
5129 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5131 else if (move_pattern == MV_ALONG_LEFT_SIDE)
5133 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
5134 MovDir[x][y] = left_dir;
5135 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5136 MovDir[x][y] = right_dir;
5138 if (MovDir[x][y] != old_move_dir)
5139 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5141 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
5143 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
5144 MovDir[x][y] = right_dir;
5145 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5146 MovDir[x][y] = left_dir;
5148 if (MovDir[x][y] != old_move_dir)
5149 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5151 else if (move_pattern == MV_TOWARDS_PLAYER ||
5152 move_pattern == MV_AWAY_FROM_PLAYER)
5154 int attr_x = -1, attr_y = -1;
5156 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
5167 for (i = 0; i < MAX_PLAYERS; i++)
5169 struct PlayerInfo *player = &stored_player[i];
5170 int jx = player->jx, jy = player->jy;
5172 if (!player->active)
5176 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
5184 MovDir[x][y] = MV_NONE;
5186 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
5187 else if (attr_x > x)
5188 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
5190 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
5191 else if (attr_y > y)
5192 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
5194 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5196 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5198 boolean first_horiz = RND(2);
5199 int new_move_dir = MovDir[x][y];
5201 if (element_info[element].move_stepsize == 0) /* "not moving" */
5203 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
5204 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5210 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5211 Moving2Blocked(x, y, &newx, &newy);
5213 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5217 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5218 Moving2Blocked(x, y, &newx, &newy);
5220 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5223 MovDir[x][y] = old_move_dir;
5226 else if (move_pattern == MV_WHEN_PUSHED ||
5227 move_pattern == MV_WHEN_DROPPED)
5229 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5230 MovDir[x][y] = MV_NONE;
5234 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
5236 static int test_xy[7][2] =
5246 static int test_dir[7] =
5256 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
5257 int move_preference = -1000000; /* start with very low preference */
5258 int new_move_dir = MV_NONE;
5259 int start_test = RND(4);
5262 for (i = 0; i < NUM_DIRECTIONS; i++)
5264 int move_dir = test_dir[start_test + i];
5265 int move_dir_preference;
5267 xx = x + test_xy[start_test + i][0];
5268 yy = y + test_xy[start_test + i][1];
5270 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
5271 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
5273 new_move_dir = move_dir;
5278 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
5281 move_dir_preference = -1 * RunnerVisit[xx][yy];
5282 if (hunter_mode && PlayerVisit[xx][yy] > 0)
5283 move_dir_preference = PlayerVisit[xx][yy];
5285 if (move_dir_preference > move_preference)
5287 /* prefer field that has not been visited for the longest time */
5288 move_preference = move_dir_preference;
5289 new_move_dir = move_dir;
5291 else if (move_dir_preference == move_preference &&
5292 move_dir == old_move_dir)
5294 /* prefer last direction when all directions are preferred equally */
5295 move_preference = move_dir_preference;
5296 new_move_dir = move_dir;
5300 MovDir[x][y] = new_move_dir;
5301 if (old_move_dir != new_move_dir)
5302 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5306 static void TurnRound(int x, int y)
5308 int direction = MovDir[x][y];
5310 int element, graphic;
5315 GfxDir[x][y] = MovDir[x][y];
5317 if (direction != MovDir[x][y])
5321 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
5324 element = Feld[x][y];
5325 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5327 if (graphic_info[graphic].anim_global_sync)
5328 GfxFrame[x][y] = FrameCounter;
5329 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5330 GfxFrame[x][y] = CustomValue[x][y];
5331 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5332 GfxFrame[x][y] = element_info[element].collect_score;
5336 static boolean JustBeingPushed(int x, int y)
5340 for (i = 0; i < MAX_PLAYERS; i++)
5342 struct PlayerInfo *player = &stored_player[i];
5344 if (player->active && player->is_pushing && player->MovPos)
5346 int next_jx = player->jx + (player->jx - player->last_jx);
5347 int next_jy = player->jy + (player->jy - player->last_jy);
5349 if (x == next_jx && y == next_jy)
5357 void StartMoving(int x, int y)
5359 boolean started_moving = FALSE; /* some elements can fall _and_ move */
5360 int element = Feld[x][y];
5365 if (MovDelay[x][y] == 0)
5366 GfxAction[x][y] = ACTION_DEFAULT;
5368 if (CAN_FALL(element) && y < lev_fieldy - 1)
5370 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
5371 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
5372 if (JustBeingPushed(x, y))
5375 if (element == EL_QUICKSAND_FULL)
5377 if (IS_FREE(x, y + 1))
5379 InitMovingField(x, y, MV_DOWN);
5380 started_moving = TRUE;
5382 Feld[x][y] = EL_QUICKSAND_EMPTYING;
5383 Store[x][y] = EL_ROCK;
5385 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
5387 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5389 if (!MovDelay[x][y])
5390 MovDelay[x][y] = TILEY + 1;
5399 Feld[x][y] = EL_QUICKSAND_EMPTY;
5400 Feld[x][y + 1] = EL_QUICKSAND_FULL;
5401 Store[x][y + 1] = Store[x][y];
5404 PlayLevelSoundAction(x, y, ACTION_FILLING);
5407 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
5408 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5410 InitMovingField(x, y, MV_DOWN);
5411 started_moving = TRUE;
5413 Feld[x][y] = EL_QUICKSAND_FILLING;
5414 Store[x][y] = element;
5416 PlayLevelSoundAction(x, y, ACTION_FILLING);
5418 else if (element == EL_MAGIC_WALL_FULL)
5420 if (IS_FREE(x, y + 1))
5422 InitMovingField(x, y, MV_DOWN);
5423 started_moving = TRUE;
5425 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
5426 Store[x][y] = EL_CHANGED(Store[x][y]);
5428 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
5430 if (!MovDelay[x][y])
5431 MovDelay[x][y] = TILEY/4 + 1;
5440 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
5441 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
5442 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
5446 else if (element == EL_BD_MAGIC_WALL_FULL)
5448 if (IS_FREE(x, y + 1))
5450 InitMovingField(x, y, MV_DOWN);
5451 started_moving = TRUE;
5453 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
5454 Store[x][y] = EL_CHANGED2(Store[x][y]);
5456 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
5458 if (!MovDelay[x][y])
5459 MovDelay[x][y] = TILEY/4 + 1;
5468 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
5469 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
5470 Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
5474 else if (CAN_PASS_MAGIC_WALL(element) &&
5475 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
5476 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
5478 InitMovingField(x, y, MV_DOWN);
5479 started_moving = TRUE;
5482 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
5483 EL_BD_MAGIC_WALL_FILLING);
5484 Store[x][y] = element;
5486 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
5488 SplashAcid(x, y + 1);
5490 InitMovingField(x, y, MV_DOWN);
5491 started_moving = TRUE;
5493 Store[x][y] = EL_ACID;
5495 else if ((game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5496 CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
5498 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
5499 CAN_FALL(element) && WasJustFalling[x][y] &&
5500 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
5502 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
5503 CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
5504 (Feld[x][y + 1] == EL_BLOCKED)))
5506 /* this is needed for a special case not covered by calling "Impact()"
5507 from "ContinueMoving()": if an element moves to a tile directly below
5508 another element which was just falling on that tile (which was empty
5509 in the previous frame), the falling element above would just stop
5510 instead of smashing the element below (in previous version, the above
5511 element was just checked for "moving" instead of "falling", resulting
5512 in incorrect smashes caused by horizontal movement of the above
5513 element; also, the case of the player being the element to smash was
5514 simply not covered here... :-/ ) */
5516 CheckCollision[x][y] = 0;
5520 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
5522 if (MovDir[x][y] == MV_NONE)
5524 InitMovingField(x, y, MV_DOWN);
5525 started_moving = TRUE;
5528 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
5530 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
5531 MovDir[x][y] = MV_DOWN;
5533 InitMovingField(x, y, MV_DOWN);
5534 started_moving = TRUE;
5536 else if (element == EL_AMOEBA_DROP)
5538 Feld[x][y] = EL_AMOEBA_GROWING;
5539 Store[x][y] = EL_AMOEBA_WET;
5541 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
5542 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
5543 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
5544 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
5546 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
5547 (IS_FREE(x - 1, y + 1) ||
5548 Feld[x - 1][y + 1] == EL_ACID));
5549 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
5550 (IS_FREE(x + 1, y + 1) ||
5551 Feld[x + 1][y + 1] == EL_ACID));
5552 boolean can_fall_any = (can_fall_left || can_fall_right);
5553 boolean can_fall_both = (can_fall_left && can_fall_right);
5554 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
5556 #if USE_NEW_ALL_SLIPPERY
5557 if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
5559 if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
5560 can_fall_right = FALSE;
5561 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
5562 can_fall_left = FALSE;
5563 else if (slippery_type == SLIPPERY_ONLY_LEFT)
5564 can_fall_right = FALSE;
5565 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
5566 can_fall_left = FALSE;
5568 can_fall_any = (can_fall_left || can_fall_right);
5569 can_fall_both = FALSE;
5572 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
5574 if (slippery_type == SLIPPERY_ONLY_LEFT)
5575 can_fall_right = FALSE;
5576 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
5577 can_fall_left = FALSE;
5578 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
5579 can_fall_right = FALSE;
5580 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
5581 can_fall_left = FALSE;
5583 can_fall_any = (can_fall_left || can_fall_right);
5584 can_fall_both = (can_fall_left && can_fall_right);
5588 #if USE_NEW_ALL_SLIPPERY
5590 #if USE_NEW_SP_SLIPPERY
5591 /* !!! better use the same properties as for custom elements here !!! */
5592 else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
5593 can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
5595 can_fall_right = FALSE; /* slip down on left side */
5596 can_fall_both = FALSE;
5601 #if USE_NEW_ALL_SLIPPERY
5604 if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
5605 can_fall_right = FALSE; /* slip down on left side */
5607 can_fall_left = !(can_fall_right = RND(2));
5609 can_fall_both = FALSE;
5614 if (game.emulation == EMU_BOULDERDASH ||
5615 element == EL_BD_ROCK || element == EL_BD_DIAMOND)
5616 can_fall_right = FALSE; /* slip down on left side */
5618 can_fall_left = !(can_fall_right = RND(2));
5620 can_fall_both = FALSE;
5626 /* if not determined otherwise, prefer left side for slipping down */
5627 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
5628 started_moving = TRUE;
5632 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
5634 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
5637 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
5638 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
5639 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
5640 int belt_dir = game.belt_dir[belt_nr];
5642 if ((belt_dir == MV_LEFT && left_is_free) ||
5643 (belt_dir == MV_RIGHT && right_is_free))
5645 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
5647 InitMovingField(x, y, belt_dir);
5648 started_moving = TRUE;
5650 Pushed[x][y] = TRUE;
5651 Pushed[nextx][y] = TRUE;
5653 GfxAction[x][y] = ACTION_DEFAULT;
5657 MovDir[x][y] = 0; /* if element was moving, stop it */
5662 /* not "else if" because of elements that can fall and move (EL_SPRING) */
5664 if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
5666 if (CAN_MOVE(element) && !started_moving)
5669 int move_pattern = element_info[element].move_pattern;
5674 if (MovDir[x][y] == MV_NONE)
5676 printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
5677 x, y, element, element_info[element].token_name);
5678 printf("StartMoving(): This should never happen!\n");
5683 Moving2Blocked(x, y, &newx, &newy);
5685 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
5688 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5689 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5691 WasJustMoving[x][y] = 0;
5692 CheckCollision[x][y] = 0;
5694 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
5696 if (Feld[x][y] != element) /* element has changed */
5700 if (!MovDelay[x][y]) /* start new movement phase */
5702 /* all objects that can change their move direction after each step
5703 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
5705 if (element != EL_YAMYAM &&
5706 element != EL_DARK_YAMYAM &&
5707 element != EL_PACMAN &&
5708 !(move_pattern & MV_ANY_DIRECTION) &&
5709 move_pattern != MV_TURNING_LEFT &&
5710 move_pattern != MV_TURNING_RIGHT &&
5711 move_pattern != MV_TURNING_LEFT_RIGHT &&
5712 move_pattern != MV_TURNING_RIGHT_LEFT &&
5713 move_pattern != MV_TURNING_RANDOM)
5717 if (MovDelay[x][y] && (element == EL_BUG ||
5718 element == EL_SPACESHIP ||
5719 element == EL_SP_SNIKSNAK ||
5720 element == EL_SP_ELECTRON ||
5721 element == EL_MOLE))
5722 DrawLevelField(x, y);
5726 if (MovDelay[x][y]) /* wait some time before next movement */
5730 if (element == EL_ROBOT ||
5731 element == EL_YAMYAM ||
5732 element == EL_DARK_YAMYAM)
5734 DrawLevelElementAnimationIfNeeded(x, y, element);
5735 PlayLevelSoundAction(x, y, ACTION_WAITING);
5737 else if (element == EL_SP_ELECTRON)
5738 DrawLevelElementAnimationIfNeeded(x, y, element);
5739 else if (element == EL_DRAGON)
5742 int dir = MovDir[x][y];
5743 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
5744 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
5745 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
5746 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
5747 dir == MV_UP ? IMG_FLAMES_1_UP :
5748 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
5749 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5751 GfxAction[x][y] = ACTION_ATTACKING;
5753 if (IS_PLAYER(x, y))
5754 DrawPlayerField(x, y);
5756 DrawLevelField(x, y);
5758 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
5760 for (i = 1; i <= 3; i++)
5762 int xx = x + i * dx;
5763 int yy = y + i * dy;
5764 int sx = SCREENX(xx);
5765 int sy = SCREENY(yy);
5766 int flame_graphic = graphic + (i - 1);
5768 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
5773 int flamed = MovingOrBlocked2Element(xx, yy);
5777 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
5779 else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
5780 RemoveMovingField(xx, yy);
5782 RemoveField(xx, yy);
5784 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
5787 RemoveMovingField(xx, yy);
5790 ChangeDelay[xx][yy] = 0;
5792 Feld[xx][yy] = EL_FLAMES;
5794 if (IN_SCR_FIELD(sx, sy))
5796 DrawLevelFieldCrumbledSand(xx, yy);
5797 DrawGraphic(sx, sy, flame_graphic, frame);
5802 if (Feld[xx][yy] == EL_FLAMES)
5803 Feld[xx][yy] = EL_EMPTY;
5804 DrawLevelField(xx, yy);
5809 if (MovDelay[x][y]) /* element still has to wait some time */
5811 PlayLevelSoundAction(x, y, ACTION_WAITING);
5817 /* now make next step */
5819 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
5821 if (DONT_COLLIDE_WITH(element) &&
5822 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
5823 !PLAYER_ENEMY_PROTECTED(newx, newy))
5825 TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
5830 else if (CAN_MOVE_INTO_ACID(element) &&
5831 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
5832 !IS_MV_DIAGONAL(MovDir[x][y]) &&
5833 (MovDir[x][y] == MV_DOWN ||
5834 game.engine_version >= VERSION_IDENT(3,1,0,0)))
5836 SplashAcid(newx, newy);
5837 Store[x][y] = EL_ACID;
5839 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
5841 if (Feld[newx][newy] == EL_EXIT_OPEN)
5844 DrawLevelField(x, y);
5846 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
5847 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
5848 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
5850 local_player->friends_still_needed--;
5851 if (!local_player->friends_still_needed &&
5852 !local_player->GameOver && AllPlayersGone)
5853 local_player->LevelSolved = local_player->GameOver = TRUE;
5857 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
5859 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
5860 DrawLevelField(newx, newy);
5862 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
5864 else if (!IS_FREE(newx, newy))
5866 GfxAction[x][y] = ACTION_WAITING;
5868 if (IS_PLAYER(x, y))
5869 DrawPlayerField(x, y);
5871 DrawLevelField(x, y);
5876 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
5878 if (IS_FOOD_PIG(Feld[newx][newy]))
5880 if (IS_MOVING(newx, newy))
5881 RemoveMovingField(newx, newy);
5884 Feld[newx][newy] = EL_EMPTY;
5885 DrawLevelField(newx, newy);
5888 PlayLevelSound(x, y, SND_PIG_DIGGING);
5890 else if (!IS_FREE(newx, newy))
5892 if (IS_PLAYER(x, y))
5893 DrawPlayerField(x, y);
5895 DrawLevelField(x, y);
5900 else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
5902 if (Store[x][y] != EL_EMPTY)
5904 boolean can_clone = FALSE;
5907 /* check if element to clone is still there */
5908 for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
5910 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
5918 /* cannot clone or target field not free anymore -- do not clone */
5919 if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
5920 Store[x][y] = EL_EMPTY;
5923 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
5925 if (IS_MV_DIAGONAL(MovDir[x][y]))
5927 int diagonal_move_dir = MovDir[x][y];
5928 int stored = Store[x][y];
5929 int change_delay = 8;
5932 /* android is moving diagonally */
5934 CreateField(x, y, EL_DIAGONAL_SHRINKING);
5936 Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
5937 GfxElement[x][y] = EL_EMC_ANDROID;
5938 GfxAction[x][y] = ACTION_SHRINKING;
5939 GfxDir[x][y] = diagonal_move_dir;
5940 ChangeDelay[x][y] = change_delay;
5942 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
5945 DrawLevelGraphicAnimation(x, y, graphic);
5946 PlayLevelSoundAction(x, y, ACTION_SHRINKING);
5948 if (Feld[newx][newy] == EL_ACID)
5950 SplashAcid(newx, newy);
5955 CreateField(newx, newy, EL_DIAGONAL_GROWING);
5957 Store[newx][newy] = EL_EMC_ANDROID;
5958 GfxElement[newx][newy] = EL_EMC_ANDROID;
5959 GfxAction[newx][newy] = ACTION_GROWING;
5960 GfxDir[newx][newy] = diagonal_move_dir;
5961 ChangeDelay[newx][newy] = change_delay;
5963 graphic = el_act_dir2img(GfxElement[newx][newy],
5964 GfxAction[newx][newy], GfxDir[newx][newy]);
5966 DrawLevelGraphicAnimation(newx, newy, graphic);
5967 PlayLevelSoundAction(newx, newy, ACTION_GROWING);
5973 Feld[newx][newy] = EL_EMPTY;
5974 DrawLevelField(newx, newy);
5976 PlayLevelSoundAction(x, y, ACTION_DIGGING);
5979 else if (!IS_FREE(newx, newy))
5982 if (IS_PLAYER(x, y))
5983 DrawPlayerField(x, y);
5985 DrawLevelField(x, y);
5991 else if (IS_CUSTOM_ELEMENT(element) &&
5992 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5994 int new_element = Feld[newx][newy];
5996 if (!IS_FREE(newx, newy))
5998 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
5999 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
6002 /* no element can dig solid indestructible elements */
6003 if (IS_INDESTRUCTIBLE(new_element) &&
6004 !IS_DIGGABLE(new_element) &&
6005 !IS_COLLECTIBLE(new_element))
6008 if (AmoebaNr[newx][newy] &&
6009 (new_element == EL_AMOEBA_FULL ||
6010 new_element == EL_BD_AMOEBA ||
6011 new_element == EL_AMOEBA_GROWING))
6013 AmoebaCnt[AmoebaNr[newx][newy]]--;
6014 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6017 if (IS_MOVING(newx, newy))
6018 RemoveMovingField(newx, newy);
6021 RemoveField(newx, newy);
6022 DrawLevelField(newx, newy);
6025 /* if digged element was about to explode, prevent the explosion */
6026 ExplodeField[newx][newy] = EX_TYPE_NONE;
6028 PlayLevelSoundAction(x, y, action);
6031 Store[newx][newy] = EL_EMPTY;
6033 /* this makes it possible to leave the removed element again */
6034 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
6035 Store[newx][newy] = new_element;
6037 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
6039 int move_leave_element = element_info[element].move_leave_element;
6041 /* this makes it possible to leave the removed element again */
6042 Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
6043 new_element : move_leave_element);
6047 if (move_pattern & MV_MAZE_RUNNER_STYLE)
6049 RunnerVisit[x][y] = FrameCounter;
6050 PlayerVisit[x][y] /= 8; /* expire player visit path */
6053 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
6055 if (!IS_FREE(newx, newy))
6057 if (IS_PLAYER(x, y))
6058 DrawPlayerField(x, y);
6060 DrawLevelField(x, y);
6066 boolean wanna_flame = !RND(10);
6067 int dx = newx - x, dy = newy - y;
6068 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
6069 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
6070 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
6071 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
6072 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
6073 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
6076 IS_CLASSIC_ENEMY(element1) ||
6077 IS_CLASSIC_ENEMY(element2)) &&
6078 element1 != EL_DRAGON && element2 != EL_DRAGON &&
6079 element1 != EL_FLAMES && element2 != EL_FLAMES)
6081 ResetGfxAnimation(x, y);
6082 GfxAction[x][y] = ACTION_ATTACKING;
6084 if (IS_PLAYER(x, y))
6085 DrawPlayerField(x, y);
6087 DrawLevelField(x, y);
6089 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
6091 MovDelay[x][y] = 50;
6095 RemoveField(newx, newy);
6097 Feld[newx][newy] = EL_FLAMES;
6098 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
6101 RemoveField(newx1, newy1);
6103 Feld[newx1][newy1] = EL_FLAMES;
6105 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
6108 RemoveField(newx2, newy2);
6110 Feld[newx2][newy2] = EL_FLAMES;
6117 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6118 Feld[newx][newy] == EL_DIAMOND)
6120 if (IS_MOVING(newx, newy))
6121 RemoveMovingField(newx, newy);
6124 Feld[newx][newy] = EL_EMPTY;
6125 DrawLevelField(newx, newy);
6128 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
6130 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6131 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
6133 if (AmoebaNr[newx][newy])
6135 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6136 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
6137 Feld[newx][newy] == EL_BD_AMOEBA)
6138 AmoebaCnt[AmoebaNr[newx][newy]]--;
6143 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
6145 RemoveMovingField(newx, newy);
6148 if (IS_MOVING(newx, newy))
6150 RemoveMovingField(newx, newy);
6155 Feld[newx][newy] = EL_EMPTY;
6156 DrawLevelField(newx, newy);
6159 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
6161 else if ((element == EL_PACMAN || element == EL_MOLE)
6162 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
6164 if (AmoebaNr[newx][newy])
6166 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6167 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
6168 Feld[newx][newy] == EL_BD_AMOEBA)
6169 AmoebaCnt[AmoebaNr[newx][newy]]--;
6172 if (element == EL_MOLE)
6174 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
6175 PlayLevelSound(x, y, SND_MOLE_DIGGING);
6177 ResetGfxAnimation(x, y);
6178 GfxAction[x][y] = ACTION_DIGGING;
6179 DrawLevelField(x, y);
6181 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
6183 return; /* wait for shrinking amoeba */
6185 else /* element == EL_PACMAN */
6187 Feld[newx][newy] = EL_EMPTY;
6188 DrawLevelField(newx, newy);
6189 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
6192 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
6193 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
6194 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
6196 /* wait for shrinking amoeba to completely disappear */
6199 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
6201 /* object was running against a wall */
6206 /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
6207 if (move_pattern & MV_ANY_DIRECTION &&
6208 move_pattern == MovDir[x][y])
6210 int blocking_element =
6211 (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
6213 CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
6216 element = Feld[x][y]; /* element might have changed */
6220 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
6221 DrawLevelElementAnimation(x, y, element);
6223 if (DONT_TOUCH(element))
6224 TestIfBadThingTouchesPlayer(x, y);
6229 InitMovingField(x, y, MovDir[x][y]);
6231 PlayLevelSoundAction(x, y, ACTION_MOVING);
6235 ContinueMoving(x, y);
6238 void ContinueMoving(int x, int y)
6240 int element = Feld[x][y];
6241 struct ElementInfo *ei = &element_info[element];
6242 int direction = MovDir[x][y];
6243 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
6244 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
6245 int newx = x + dx, newy = y + dy;
6246 int stored = Store[x][y];
6247 int stored_new = Store[newx][newy];
6248 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
6249 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
6250 boolean last_line = (newy == lev_fieldy - 1);
6252 MovPos[x][y] += getElementMoveStepsize(x, y);
6254 if (pushed_by_player) /* special case: moving object pushed by player */
6255 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
6257 if (ABS(MovPos[x][y]) < TILEX)
6259 DrawLevelField(x, y);
6261 return; /* element is still moving */
6264 /* element reached destination field */
6266 Feld[x][y] = EL_EMPTY;
6267 Feld[newx][newy] = element;
6268 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
6270 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
6272 element = Feld[newx][newy] = EL_ACID;
6274 else if (element == EL_MOLE)
6276 Feld[x][y] = EL_SAND;
6278 DrawLevelFieldCrumbledSandNeighbours(x, y);
6280 else if (element == EL_QUICKSAND_FILLING)
6282 element = Feld[newx][newy] = get_next_element(element);
6283 Store[newx][newy] = Store[x][y];
6285 else if (element == EL_QUICKSAND_EMPTYING)
6287 Feld[x][y] = get_next_element(element);
6288 element = Feld[newx][newy] = Store[x][y];
6290 else if (element == EL_MAGIC_WALL_FILLING)
6292 element = Feld[newx][newy] = get_next_element(element);
6293 if (!game.magic_wall_active)
6294 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
6295 Store[newx][newy] = Store[x][y];
6297 else if (element == EL_MAGIC_WALL_EMPTYING)
6299 Feld[x][y] = get_next_element(element);
6300 if (!game.magic_wall_active)
6301 Feld[x][y] = EL_MAGIC_WALL_DEAD;
6302 element = Feld[newx][newy] = Store[x][y];
6304 #if USE_NEW_CUSTOM_VALUE
6305 InitField(newx, newy, FALSE);
6308 else if (element == EL_BD_MAGIC_WALL_FILLING)
6310 element = Feld[newx][newy] = get_next_element(element);
6311 if (!game.magic_wall_active)
6312 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
6313 Store[newx][newy] = Store[x][y];
6315 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
6317 Feld[x][y] = get_next_element(element);
6318 if (!game.magic_wall_active)
6319 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
6320 element = Feld[newx][newy] = Store[x][y];
6322 #if USE_NEW_CUSTOM_VALUE
6323 InitField(newx, newy, FALSE);
6326 else if (element == EL_AMOEBA_DROPPING)
6328 Feld[x][y] = get_next_element(element);
6329 element = Feld[newx][newy] = Store[x][y];
6331 else if (element == EL_SOKOBAN_OBJECT)
6334 Feld[x][y] = Back[x][y];
6336 if (Back[newx][newy])
6337 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
6339 Back[x][y] = Back[newx][newy] = 0;
6342 Store[x][y] = EL_EMPTY;
6347 MovDelay[newx][newy] = 0;
6350 if (CAN_CHANGE_OR_HAS_ACTION(element))
6352 if (CAN_CHANGE(element))
6355 /* copy element change control values to new field */
6356 ChangeDelay[newx][newy] = ChangeDelay[x][y];
6357 ChangePage[newx][newy] = ChangePage[x][y];
6358 ChangeCount[newx][newy] = ChangeCount[x][y];
6359 ChangeEvent[newx][newy] = ChangeEvent[x][y];
6362 #if USE_NEW_CUSTOM_VALUE
6363 CustomValue[newx][newy] = CustomValue[x][y];
6369 #if USE_NEW_CUSTOM_VALUE
6370 CustomValue[newx][newy] = CustomValue[x][y];
6374 ChangeDelay[x][y] = 0;
6375 ChangePage[x][y] = -1;
6376 ChangeCount[x][y] = 0;
6377 ChangeEvent[x][y] = -1;
6379 #if USE_NEW_CUSTOM_VALUE
6380 CustomValue[x][y] = 0;
6383 /* copy animation control values to new field */
6384 GfxFrame[newx][newy] = GfxFrame[x][y];
6385 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
6386 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
6387 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
6389 Pushed[x][y] = Pushed[newx][newy] = FALSE;
6391 /* some elements can leave other elements behind after moving */
6393 if (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)))
6397 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
6398 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
6399 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
6402 int move_leave_element = ei->move_leave_element;
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 == EL_ACID ? EL_EMPTY : stored);
6410 /* this makes it possible to leave the removed element again */
6411 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
6412 move_leave_element = stored;
6415 /* this makes it possible to leave the removed element again */
6416 if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
6417 ei->move_leave_element == EL_TRIGGER_ELEMENT)
6418 move_leave_element = stored;
6421 Feld[x][y] = move_leave_element;
6423 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
6424 MovDir[x][y] = direction;
6426 InitField(x, y, FALSE);
6428 if (GFX_CRUMBLED(Feld[x][y]))
6429 DrawLevelFieldCrumbledSandNeighbours(x, y);
6431 if (ELEM_IS_PLAYER(move_leave_element))
6432 RelocatePlayer(x, y, move_leave_element);
6435 /* do this after checking for left-behind element */
6436 ResetGfxAnimation(x, y); /* reset animation values for old field */
6438 if (!CAN_MOVE(element) ||
6439 (CAN_FALL(element) && direction == MV_DOWN &&
6440 (element == EL_SPRING ||
6441 element_info[element].move_pattern == MV_WHEN_PUSHED ||
6442 element_info[element].move_pattern == MV_WHEN_DROPPED)))
6443 GfxDir[x][y] = MovDir[newx][newy] = 0;
6445 DrawLevelField(x, y);
6446 DrawLevelField(newx, newy);
6448 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
6450 /* prevent pushed element from moving on in pushed direction */
6451 if (pushed_by_player && CAN_MOVE(element) &&
6452 element_info[element].move_pattern & MV_ANY_DIRECTION &&
6453 !(element_info[element].move_pattern & direction))
6454 TurnRound(newx, newy);
6456 /* prevent elements on conveyor belt from moving on in last direction */
6457 if (pushed_by_conveyor && CAN_FALL(element) &&
6458 direction & MV_HORIZONTAL)
6459 MovDir[newx][newy] = 0;
6461 if (!pushed_by_player)
6463 int nextx = newx + dx, nexty = newy + dy;
6464 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
6466 WasJustMoving[newx][newy] = 3;
6468 if (CAN_FALL(element) && direction == MV_DOWN)
6469 WasJustFalling[newx][newy] = 3;
6471 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
6472 CheckCollision[newx][newy] = 2;
6475 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
6477 TestIfBadThingTouchesPlayer(newx, newy);
6478 TestIfBadThingTouchesFriend(newx, newy);
6480 if (!IS_CUSTOM_ELEMENT(element))
6481 TestIfBadThingTouchesOtherBadThing(newx, newy);
6483 else if (element == EL_PENGUIN)
6484 TestIfFriendTouchesBadThing(newx, newy);
6486 /* give the player one last chance (one more frame) to move away */
6487 if (CAN_FALL(element) && direction == MV_DOWN &&
6488 (last_line || (!IS_FREE(x, newy + 1) &&
6489 (!IS_PLAYER(x, newy + 1) ||
6490 game.engine_version < VERSION_IDENT(3,1,1,0)))))
6493 if (pushed_by_player && !game.use_change_when_pushing_bug)
6495 int push_side = MV_DIR_OPPOSITE(direction);
6496 struct PlayerInfo *player = PLAYERINFO(x, y);
6498 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
6499 player->index_bit, push_side);
6500 CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
6501 player->index_bit, push_side);
6504 if (element == EL_EMC_ANDROID && pushed_by_player) /* make another move */
6505 MovDelay[newx][newy] = 1;
6507 CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
6509 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
6512 if (ChangePage[newx][newy] != -1) /* delayed change */
6514 int page = ChangePage[newx][newy];
6515 struct ElementChangeInfo *change = &ei->change_page[page];
6517 ChangePage[newx][newy] = -1;
6519 if (change->can_change)
6521 if (ChangeElement(newx, newy, element, page))
6523 if (change->post_change_function)
6524 change->post_change_function(newx, newy);
6528 if (change->has_action)
6529 ExecuteCustomElementAction(newx, newy, element, page);
6533 TestIfElementHitsCustomElement(newx, newy, direction);
6534 TestIfPlayerTouchesCustomElement(newx, newy);
6535 TestIfElementTouchesCustomElement(newx, newy);
6538 if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
6539 IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
6540 CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
6541 MV_DIR_OPPOSITE(direction));
6545 int AmoebeNachbarNr(int ax, int ay)
6548 int element = Feld[ax][ay];
6550 static int xy[4][2] =
6558 for (i = 0; i < NUM_DIRECTIONS; i++)
6560 int x = ax + xy[i][0];
6561 int y = ay + xy[i][1];
6563 if (!IN_LEV_FIELD(x, y))
6566 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
6567 group_nr = AmoebaNr[x][y];
6573 void AmoebenVereinigen(int ax, int ay)
6575 int i, x, y, xx, yy;
6576 int new_group_nr = AmoebaNr[ax][ay];
6577 static int xy[4][2] =
6585 if (new_group_nr == 0)
6588 for (i = 0; i < NUM_DIRECTIONS; i++)
6593 if (!IN_LEV_FIELD(x, y))
6596 if ((Feld[x][y] == EL_AMOEBA_FULL ||
6597 Feld[x][y] == EL_BD_AMOEBA ||
6598 Feld[x][y] == EL_AMOEBA_DEAD) &&
6599 AmoebaNr[x][y] != new_group_nr)
6601 int old_group_nr = AmoebaNr[x][y];
6603 if (old_group_nr == 0)
6606 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
6607 AmoebaCnt[old_group_nr] = 0;
6608 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
6609 AmoebaCnt2[old_group_nr] = 0;
6612 SCAN_PLAYFIELD(xx, yy)
6614 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
6617 if (AmoebaNr[xx][yy] == old_group_nr)
6618 AmoebaNr[xx][yy] = new_group_nr;
6624 void AmoebeUmwandeln(int ax, int ay)
6628 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
6630 int group_nr = AmoebaNr[ax][ay];
6635 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
6636 printf("AmoebeUmwandeln(): This should never happen!\n");
6642 SCAN_PLAYFIELD(x, y)
6644 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
6647 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
6650 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
6654 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
6655 SND_AMOEBA_TURNING_TO_GEM :
6656 SND_AMOEBA_TURNING_TO_ROCK));
6661 static int xy[4][2] =
6669 for (i = 0; i < NUM_DIRECTIONS; i++)
6674 if (!IN_LEV_FIELD(x, y))
6677 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
6679 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
6680 SND_AMOEBA_TURNING_TO_GEM :
6681 SND_AMOEBA_TURNING_TO_ROCK));
6688 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
6691 int group_nr = AmoebaNr[ax][ay];
6692 boolean done = FALSE;
6697 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
6698 printf("AmoebeUmwandelnBD(): This should never happen!\n");
6704 SCAN_PLAYFIELD(x, y)
6706 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
6709 if (AmoebaNr[x][y] == group_nr &&
6710 (Feld[x][y] == EL_AMOEBA_DEAD ||
6711 Feld[x][y] == EL_BD_AMOEBA ||
6712 Feld[x][y] == EL_AMOEBA_GROWING))
6715 Feld[x][y] = new_element;
6716 InitField(x, y, FALSE);
6717 DrawLevelField(x, y);
6723 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
6724 SND_BD_AMOEBA_TURNING_TO_ROCK :
6725 SND_BD_AMOEBA_TURNING_TO_GEM));
6728 void AmoebeWaechst(int x, int y)
6730 static unsigned long sound_delay = 0;
6731 static unsigned long sound_delay_value = 0;
6733 if (!MovDelay[x][y]) /* start new growing cycle */
6737 if (DelayReached(&sound_delay, sound_delay_value))
6739 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
6740 sound_delay_value = 30;
6744 if (MovDelay[x][y]) /* wait some time before growing bigger */
6747 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6749 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
6750 6 - MovDelay[x][y]);
6752 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
6755 if (!MovDelay[x][y])
6757 Feld[x][y] = Store[x][y];
6759 DrawLevelField(x, y);
6764 void AmoebaDisappearing(int x, int y)
6766 static unsigned long sound_delay = 0;
6767 static unsigned long sound_delay_value = 0;
6769 if (!MovDelay[x][y]) /* start new shrinking cycle */
6773 if (DelayReached(&sound_delay, sound_delay_value))
6774 sound_delay_value = 30;
6777 if (MovDelay[x][y]) /* wait some time before shrinking */
6780 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6782 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
6783 6 - MovDelay[x][y]);
6785 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
6788 if (!MovDelay[x][y])
6790 Feld[x][y] = EL_EMPTY;
6791 DrawLevelField(x, y);
6793 /* don't let mole enter this field in this cycle;
6794 (give priority to objects falling to this field from above) */
6800 void AmoebeAbleger(int ax, int ay)
6803 int element = Feld[ax][ay];
6804 int graphic = el2img(element);
6805 int newax = ax, neway = ay;
6806 boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
6807 static int xy[4][2] =
6815 if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
6817 Feld[ax][ay] = EL_AMOEBA_DEAD;
6818 DrawLevelField(ax, ay);
6822 if (IS_ANIMATED(graphic))
6823 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6825 if (!MovDelay[ax][ay]) /* start making new amoeba field */
6826 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
6828 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
6831 if (MovDelay[ax][ay])
6835 if (can_drop) /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
6838 int x = ax + xy[start][0];
6839 int y = ay + xy[start][1];
6841 if (!IN_LEV_FIELD(x, y))
6844 if (IS_FREE(x, y) ||
6845 CAN_GROW_INTO(Feld[x][y]) ||
6846 Feld[x][y] == EL_QUICKSAND_EMPTY)
6852 if (newax == ax && neway == ay)
6855 else /* normal or "filled" (BD style) amoeba */
6858 boolean waiting_for_player = FALSE;
6860 for (i = 0; i < NUM_DIRECTIONS; i++)
6862 int j = (start + i) % 4;
6863 int x = ax + xy[j][0];
6864 int y = ay + xy[j][1];
6866 if (!IN_LEV_FIELD(x, y))
6869 if (IS_FREE(x, y) ||
6870 CAN_GROW_INTO(Feld[x][y]) ||
6871 Feld[x][y] == EL_QUICKSAND_EMPTY)
6877 else if (IS_PLAYER(x, y))
6878 waiting_for_player = TRUE;
6881 if (newax == ax && neway == ay) /* amoeba cannot grow */
6883 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
6885 Feld[ax][ay] = EL_AMOEBA_DEAD;
6886 DrawLevelField(ax, ay);
6887 AmoebaCnt[AmoebaNr[ax][ay]]--;
6889 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
6891 if (element == EL_AMOEBA_FULL)
6892 AmoebeUmwandeln(ax, ay);
6893 else if (element == EL_BD_AMOEBA)
6894 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
6899 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
6901 /* amoeba gets larger by growing in some direction */
6903 int new_group_nr = AmoebaNr[ax][ay];
6906 if (new_group_nr == 0)
6908 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
6909 printf("AmoebeAbleger(): This should never happen!\n");
6914 AmoebaNr[newax][neway] = new_group_nr;
6915 AmoebaCnt[new_group_nr]++;
6916 AmoebaCnt2[new_group_nr]++;
6918 /* if amoeba touches other amoeba(s) after growing, unify them */
6919 AmoebenVereinigen(newax, neway);
6921 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
6923 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
6929 if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
6930 (neway == lev_fieldy - 1 && newax != ax))
6932 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
6933 Store[newax][neway] = element;
6935 else if (neway == ay || element == EL_EMC_DRIPPER)
6937 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
6939 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
6943 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
6944 Feld[ax][ay] = EL_AMOEBA_DROPPING;
6945 Store[ax][ay] = EL_AMOEBA_DROP;
6946 ContinueMoving(ax, ay);
6950 DrawLevelField(newax, neway);
6953 void Life(int ax, int ay)
6957 static int life[4] = { 2, 3, 3, 3 }; /* parameters for "game of life" */
6960 int element = Feld[ax][ay];
6961 int graphic = el2img(element);
6962 int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
6964 boolean changed = FALSE;
6966 if (IS_ANIMATED(graphic))
6967 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6972 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
6973 MovDelay[ax][ay] = life_time;
6975 if (MovDelay[ax][ay]) /* wait some time before next cycle */
6978 if (MovDelay[ax][ay])
6982 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
6984 int xx = ax+x1, yy = ay+y1;
6987 if (!IN_LEV_FIELD(xx, yy))
6990 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
6992 int x = xx+x2, y = yy+y2;
6994 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
6997 if (((Feld[x][y] == element ||
6998 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
7000 (IS_FREE(x, y) && Stop[x][y]))
7004 if (xx == ax && yy == ay) /* field in the middle */
7006 if (nachbarn < life_parameter[0] ||
7007 nachbarn > life_parameter[1])
7009 Feld[xx][yy] = EL_EMPTY;
7011 DrawLevelField(xx, yy);
7012 Stop[xx][yy] = TRUE;
7016 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
7017 { /* free border field */
7018 if (nachbarn >= life_parameter[2] &&
7019 nachbarn <= life_parameter[3])
7021 Feld[xx][yy] = element;
7022 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
7024 DrawLevelField(xx, yy);
7025 Stop[xx][yy] = TRUE;
7032 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
7033 SND_GAME_OF_LIFE_GROWING);
7036 static void InitRobotWheel(int x, int y)
7038 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
7041 static void RunRobotWheel(int x, int y)
7043 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
7046 static void StopRobotWheel(int x, int y)
7048 if (ZX == x && ZY == y)
7052 static void InitTimegateWheel(int x, int y)
7054 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
7057 static void RunTimegateWheel(int x, int y)
7059 PlayLevelSound(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
7062 static void InitMagicBallDelay(int x, int y)
7065 ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
7067 ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
7071 static void ActivateMagicBall(int bx, int by)
7075 if (level.ball_random)
7077 int pos_border = RND(8); /* select one of the eight border elements */
7078 int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
7079 int xx = pos_content % 3;
7080 int yy = pos_content / 3;
7085 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
7086 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
7090 for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
7092 int xx = x - bx + 1;
7093 int yy = y - by + 1;
7095 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
7096 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
7100 game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
7103 static void InitDiagonalMovingElement(int x, int y)
7106 MovDelay[x][y] = level.android_move_time;
7110 void CheckExit(int x, int y)
7112 if (local_player->gems_still_needed > 0 ||
7113 local_player->sokobanfields_still_needed > 0 ||
7114 local_player->lights_still_needed > 0)
7116 int element = Feld[x][y];
7117 int graphic = el2img(element);
7119 if (IS_ANIMATED(graphic))
7120 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7125 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7128 Feld[x][y] = EL_EXIT_OPENING;
7130 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
7133 void CheckExitSP(int x, int y)
7135 if (local_player->gems_still_needed > 0)
7137 int element = Feld[x][y];
7138 int graphic = el2img(element);
7140 if (IS_ANIMATED(graphic))
7141 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7146 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7149 Feld[x][y] = EL_SP_EXIT_OPENING;
7151 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
7154 static void CloseAllOpenTimegates()
7159 SCAN_PLAYFIELD(x, y)
7161 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7164 int element = Feld[x][y];
7166 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
7168 Feld[x][y] = EL_TIMEGATE_CLOSING;
7170 PlayLevelSoundAction(x, y, ACTION_CLOSING);
7175 void EdelsteinFunkeln(int x, int y)
7177 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
7180 if (Feld[x][y] == EL_BD_DIAMOND)
7183 if (MovDelay[x][y] == 0) /* next animation frame */
7184 MovDelay[x][y] = 11 * !SimpleRND(500);
7186 if (MovDelay[x][y] != 0) /* wait some time before next frame */
7190 if (setup.direct_draw && MovDelay[x][y])
7191 SetDrawtoField(DRAW_BUFFERED);
7193 DrawLevelElementAnimation(x, y, Feld[x][y]);
7195 if (MovDelay[x][y] != 0)
7197 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
7198 10 - MovDelay[x][y]);
7200 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
7202 if (setup.direct_draw)
7206 dest_x = FX + SCREENX(x) * TILEX;
7207 dest_y = FY + SCREENY(y) * TILEY;
7209 BlitBitmap(drawto_field, window,
7210 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
7211 SetDrawtoField(DRAW_DIRECT);
7217 void MauerWaechst(int x, int y)
7221 if (!MovDelay[x][y]) /* next animation frame */
7222 MovDelay[x][y] = 3 * delay;
7224 if (MovDelay[x][y]) /* wait some time before next frame */
7228 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7230 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
7231 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
7233 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
7236 if (!MovDelay[x][y])
7238 if (MovDir[x][y] == MV_LEFT)
7240 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
7241 DrawLevelField(x - 1, y);
7243 else if (MovDir[x][y] == MV_RIGHT)
7245 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
7246 DrawLevelField(x + 1, y);
7248 else if (MovDir[x][y] == MV_UP)
7250 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
7251 DrawLevelField(x, y - 1);
7255 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
7256 DrawLevelField(x, y + 1);
7259 Feld[x][y] = Store[x][y];
7261 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7262 DrawLevelField(x, y);
7267 void MauerAbleger(int ax, int ay)
7269 int element = Feld[ax][ay];
7270 int graphic = el2img(element);
7271 boolean oben_frei = FALSE, unten_frei = FALSE;
7272 boolean links_frei = FALSE, rechts_frei = FALSE;
7273 boolean oben_massiv = FALSE, unten_massiv = FALSE;
7274 boolean links_massiv = FALSE, rechts_massiv = FALSE;
7275 boolean new_wall = FALSE;
7277 if (IS_ANIMATED(graphic))
7278 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7280 if (!MovDelay[ax][ay]) /* start building new wall */
7281 MovDelay[ax][ay] = 6;
7283 if (MovDelay[ax][ay]) /* wait some time before building new wall */
7286 if (MovDelay[ax][ay])
7290 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
7292 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
7294 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
7296 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
7299 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
7300 element == EL_EXPANDABLE_WALL_ANY)
7304 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
7305 Store[ax][ay-1] = element;
7306 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
7307 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
7308 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
7309 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
7314 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
7315 Store[ax][ay+1] = element;
7316 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
7317 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
7318 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
7319 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
7324 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7325 element == EL_EXPANDABLE_WALL_ANY ||
7326 element == EL_EXPANDABLE_WALL)
7330 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
7331 Store[ax-1][ay] = element;
7332 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
7333 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
7334 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
7335 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
7341 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
7342 Store[ax+1][ay] = element;
7343 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
7344 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
7345 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
7346 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
7351 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
7352 DrawLevelField(ax, ay);
7354 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
7356 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
7357 unten_massiv = TRUE;
7358 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
7359 links_massiv = TRUE;
7360 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
7361 rechts_massiv = TRUE;
7363 if (((oben_massiv && unten_massiv) ||
7364 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7365 element == EL_EXPANDABLE_WALL) &&
7366 ((links_massiv && rechts_massiv) ||
7367 element == EL_EXPANDABLE_WALL_VERTICAL))
7368 Feld[ax][ay] = EL_WALL;
7371 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
7374 void CheckForDragon(int x, int y)
7377 boolean dragon_found = FALSE;
7378 static int xy[4][2] =
7386 for (i = 0; i < NUM_DIRECTIONS; i++)
7388 for (j = 0; j < 4; j++)
7390 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7392 if (IN_LEV_FIELD(xx, yy) &&
7393 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
7395 if (Feld[xx][yy] == EL_DRAGON)
7396 dragon_found = TRUE;
7405 for (i = 0; i < NUM_DIRECTIONS; i++)
7407 for (j = 0; j < 3; j++)
7409 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7411 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
7413 Feld[xx][yy] = EL_EMPTY;
7414 DrawLevelField(xx, yy);
7423 static void InitBuggyBase(int x, int y)
7425 int element = Feld[x][y];
7426 int activating_delay = FRAMES_PER_SECOND / 4;
7429 (element == EL_SP_BUGGY_BASE ?
7430 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
7431 element == EL_SP_BUGGY_BASE_ACTIVATING ?
7433 element == EL_SP_BUGGY_BASE_ACTIVE ?
7434 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
7437 static void WarnBuggyBase(int x, int y)
7440 static int xy[4][2] =
7448 for (i = 0; i < NUM_DIRECTIONS; i++)
7450 int xx = x + xy[i][0];
7451 int yy = y + xy[i][1];
7453 if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
7455 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
7462 static void InitTrap(int x, int y)
7464 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
7467 static void ActivateTrap(int x, int y)
7469 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
7472 static void ChangeActiveTrap(int x, int y)
7474 int graphic = IMG_TRAP_ACTIVE;
7476 /* if new animation frame was drawn, correct crumbled sand border */
7477 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
7478 DrawLevelFieldCrumbledSand(x, y);
7481 static int getSpecialActionElement(int element, int number, int base_element)
7483 return (element != EL_EMPTY ? element :
7484 number != -1 ? base_element + number - 1 :
7488 static int getModifiedActionNumber(int value_old, int operator, int operand,
7489 int value_min, int value_max)
7491 int value_new = (operator == CA_MODE_SET ? operand :
7492 operator == CA_MODE_ADD ? value_old + operand :
7493 operator == CA_MODE_SUBTRACT ? value_old - operand :
7494 operator == CA_MODE_MULTIPLY ? value_old * operand :
7495 operator == CA_MODE_DIVIDE ? value_old / MAX(1, operand) :
7496 operator == CA_MODE_MODULO ? value_old % MAX(1, operand) :
7499 return (value_new < value_min ? value_min :
7500 value_new > value_max ? value_max :
7504 static void ExecuteCustomElementAction(int x, int y, int element, int page)
7506 struct ElementInfo *ei = &element_info[element];
7507 struct ElementChangeInfo *change = &ei->change_page[page];
7508 int action_type = change->action_type;
7509 int action_mode = change->action_mode;
7510 int action_arg = change->action_arg;
7513 if (!change->has_action)
7516 /* ---------- determine action paramater values -------------------------- */
7518 int level_time_value =
7519 (level.time > 0 ? TimeLeft :
7522 int action_arg_element =
7523 (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player :
7524 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
7525 action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element :
7528 int action_arg_direction =
7529 (action_arg >= CA_ARG_DIRECTION_LEFT &&
7530 action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
7531 action_arg == CA_ARG_DIRECTION_TRIGGER ?
7532 change->actual_trigger_side :
7533 action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
7534 MV_DIR_OPPOSITE(change->actual_trigger_side) :
7537 int action_arg_number_min =
7538 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
7541 int action_arg_number_max =
7542 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
7543 action_type == CA_SET_LEVEL_GEMS ? 999 :
7544 action_type == CA_SET_LEVEL_TIME ? 9999 :
7545 action_type == CA_SET_LEVEL_SCORE ? 99999 :
7546 action_type == CA_SET_CE_SCORE ? 9999 :
7547 action_type == CA_SET_CE_VALUE ? 9999 :
7550 int action_arg_number_reset =
7551 (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize :
7552 action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
7553 action_type == CA_SET_LEVEL_TIME ? level.time :
7554 action_type == CA_SET_LEVEL_SCORE ? 0 :
7555 action_type == CA_SET_CE_SCORE ? 0 :
7557 action_type == CA_SET_CE_VALUE ? GET_NEW_CUSTOM_VALUE(element) :
7559 action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
7563 int action_arg_number =
7564 (action_arg <= CA_ARG_MAX ? action_arg :
7565 action_arg >= CA_ARG_SPEED_NOT_MOVING &&
7566 action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
7567 action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
7568 action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
7569 action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
7570 action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
7571 action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
7572 #if USE_NEW_CUSTOM_VALUE
7573 action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
7575 action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
7577 action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
7578 action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
7579 action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
7580 action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
7581 action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CUSTOM_VALUE(change->target_element) :
7582 action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
7583 action_arg == CA_ARG_ELEMENT_NR_TARGET ? change->target_element :
7584 action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
7587 int action_arg_number_old =
7588 (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
7589 action_type == CA_SET_LEVEL_TIME ? TimeLeft :
7590 action_type == CA_SET_LEVEL_SCORE ? local_player->score :
7591 action_type == CA_SET_CE_SCORE ? ei->collect_score :
7592 action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
7595 int action_arg_number_new =
7596 getModifiedActionNumber(action_arg_number_old,
7597 action_mode, action_arg_number,
7598 action_arg_number_min, action_arg_number_max);
7600 int trigger_player_bits =
7601 (change->actual_trigger_player >= EL_PLAYER_1 &&
7602 change->actual_trigger_player <= EL_PLAYER_4 ?
7603 (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
7606 int action_arg_player_bits =
7607 (action_arg >= CA_ARG_PLAYER_1 &&
7608 action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
7609 action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
7612 /* ---------- execute action -------------------------------------------- */
7621 /* ---------- level actions ------------------------------------------- */
7623 case CA_RESTART_LEVEL:
7625 game.restart_level = TRUE;
7630 case CA_SHOW_ENVELOPE:
7632 int element = getSpecialActionElement(action_arg_element,
7633 action_arg_number, EL_ENVELOPE_1);
7635 if (IS_ENVELOPE(element))
7636 local_player->show_envelope = element;
7641 case CA_SET_LEVEL_TIME:
7643 if (level.time > 0) /* only modify limited time value */
7645 TimeLeft = action_arg_number_new;
7647 DrawGameValue_Time(TimeLeft);
7649 if (!TimeLeft && setup.time_limit)
7650 for (i = 0; i < MAX_PLAYERS; i++)
7651 KillPlayer(&stored_player[i]);
7657 case CA_SET_LEVEL_SCORE:
7659 local_player->score = action_arg_number_new;
7661 DrawGameValue_Score(local_player->score);
7666 case CA_SET_LEVEL_GEMS:
7668 local_player->gems_still_needed = action_arg_number_new;
7670 DrawGameValue_Emeralds(local_player->gems_still_needed);
7675 case CA_SET_LEVEL_GRAVITY:
7677 game.gravity = (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
7678 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
7679 action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
7684 case CA_SET_LEVEL_WIND:
7686 game.wind_direction = action_arg_direction;
7691 /* ---------- player actions ------------------------------------------ */
7693 case CA_MOVE_PLAYER:
7695 /* automatically move to the next field in specified direction */
7696 for (i = 0; i < MAX_PLAYERS; i++)
7697 if (trigger_player_bits & (1 << i))
7698 stored_player[i].programmed_action = action_arg_direction;
7703 case CA_EXIT_PLAYER:
7705 for (i = 0; i < MAX_PLAYERS; i++)
7706 if (action_arg_player_bits & (1 << i))
7707 stored_player[i].LevelSolved = stored_player[i].GameOver = TRUE;
7712 case CA_KILL_PLAYER:
7714 for (i = 0; i < MAX_PLAYERS; i++)
7715 if (action_arg_player_bits & (1 << i))
7716 KillPlayer(&stored_player[i]);
7721 case CA_SET_PLAYER_KEYS:
7723 int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
7724 int element = getSpecialActionElement(action_arg_element,
7725 action_arg_number, EL_KEY_1);
7727 if (IS_KEY(element))
7729 for (i = 0; i < MAX_PLAYERS; i++)
7731 if (trigger_player_bits & (1 << i))
7733 stored_player[i].key[KEY_NR(element)] = key_state;
7735 DrawGameValue_Keys(stored_player[i].key);
7737 redraw_mask |= REDRAW_DOOR_1;
7745 case CA_SET_PLAYER_SPEED:
7747 for (i = 0; i < MAX_PLAYERS; i++)
7749 if (trigger_player_bits & (1 << i))
7751 int move_stepsize = TILEX / stored_player[i].move_delay_value;
7753 if (action_arg == CA_ARG_SPEED_FASTER &&
7754 stored_player[i].cannot_move)
7756 action_arg_number = STEPSIZE_VERY_SLOW;
7758 else if (action_arg == CA_ARG_SPEED_SLOWER ||
7759 action_arg == CA_ARG_SPEED_FASTER)
7761 action_arg_number = 2;
7762 action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
7767 getModifiedActionNumber(move_stepsize,
7770 action_arg_number_min,
7771 action_arg_number_max);
7774 SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
7776 /* make sure that value is power of 2 */
7777 move_stepsize = (1 << log_2(move_stepsize));
7779 /* do no immediately change -- the player might just be moving */
7780 stored_player[i].move_delay_value_next = TILEX / move_stepsize;
7782 stored_player[i].cannot_move =
7783 (action_arg == CA_ARG_SPEED_NOT_MOVING ? TRUE : FALSE);
7791 case CA_SET_PLAYER_SHIELD:
7793 for (i = 0; i < MAX_PLAYERS; i++)
7795 if (trigger_player_bits & (1 << i))
7797 if (action_arg == CA_ARG_SHIELD_OFF)
7799 stored_player[i].shield_normal_time_left = 0;
7800 stored_player[i].shield_deadly_time_left = 0;
7802 else if (action_arg == CA_ARG_SHIELD_NORMAL)
7804 stored_player[i].shield_normal_time_left = 999999;
7806 else if (action_arg == CA_ARG_SHIELD_DEADLY)
7808 stored_player[i].shield_normal_time_left = 999999;
7809 stored_player[i].shield_deadly_time_left = 999999;
7817 case CA_SET_PLAYER_ARTWORK:
7819 for (i = 0; i < MAX_PLAYERS; i++)
7821 if (trigger_player_bits & (1 << i))
7823 int artwork_element = action_arg_element;
7825 if (action_arg == CA_ARG_ELEMENT_RESET)
7827 (level.use_artwork_element[i] ? level.artwork_element[i] :
7828 stored_player[i].element_nr);
7830 stored_player[i].artwork_element = artwork_element;
7832 SetPlayerWaiting(&stored_player[i], FALSE);
7834 /* set number of special actions for bored and sleeping animation */
7835 stored_player[i].num_special_action_bored =
7836 get_num_special_action(artwork_element,
7837 ACTION_BORING_1, ACTION_BORING_LAST);
7838 stored_player[i].num_special_action_sleeping =
7839 get_num_special_action(artwork_element,
7840 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
7847 /* ---------- CE actions ---------------------------------------------- */
7849 case CA_SET_CE_SCORE:
7851 ei->collect_score = action_arg_number_new;
7856 case CA_SET_CE_VALUE:
7858 #if USE_NEW_CUSTOM_VALUE
7859 int last_custom_value = CustomValue[x][y];
7861 CustomValue[x][y] = action_arg_number_new;
7864 printf("::: Count == %d\n", CustomValue[x][y]);
7867 if (CustomValue[x][y] == 0 && last_custom_value > 0)
7870 printf("::: CE_VALUE_GETS_ZERO\n");
7873 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
7874 CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
7877 printf("::: RESULT: %d, %d\n", Feld[x][y], ChangePage[x][y]);
7885 /* ---------- engine actions ------------------------------------------ */
7887 case CA_SET_ENGINE_SCAN_MODE:
7889 InitPlayfieldScanMode(action_arg);
7899 static void CreateFieldExt(int x, int y, int element, boolean is_change)
7901 int previous_move_direction = MovDir[x][y];
7902 #if USE_NEW_CUSTOM_VALUE
7903 int last_ce_value = CustomValue[x][y];
7905 boolean add_player = (ELEM_IS_PLAYER(element) &&
7906 IS_WALKABLE(Feld[x][y]));
7908 /* check if element under player changes from accessible to unaccessible
7909 (needed for special case of dropping element which then changes) */
7910 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
7911 IS_ACCESSIBLE(Feld[x][y]) && !IS_ACCESSIBLE(element))
7920 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
7921 RemoveMovingField(x, y);
7925 Feld[x][y] = element;
7927 ResetGfxAnimation(x, y);
7928 ResetRandomAnimationValue(x, y);
7930 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
7931 MovDir[x][y] = previous_move_direction;
7933 #if USE_NEW_CUSTOM_VALUE
7934 if (element_info[Feld[x][y]].use_last_ce_value)
7935 CustomValue[x][y] = last_ce_value;
7938 InitField_WithBug1(x, y, FALSE);
7940 DrawLevelField(x, y);
7942 if (GFX_CRUMBLED(Feld[x][y]))
7943 DrawLevelFieldCrumbledSandNeighbours(x, y);
7946 /* "ChangeCount" not set yet to allow "entered by player" change one time */
7947 if (ELEM_IS_PLAYER(element))
7948 RelocatePlayer(x, y, element);
7951 ChangeCount[x][y]++; /* count number of changes in the same frame */
7953 TestIfBadThingTouchesPlayer(x, y);
7954 TestIfPlayerTouchesCustomElement(x, y);
7955 TestIfElementTouchesCustomElement(x, y);
7958 static void CreateField(int x, int y, int element)
7960 CreateFieldExt(x, y, element, FALSE);
7963 static void CreateElementFromChange(int x, int y, int element)
7965 element = GET_VALID_RUNTIME_ELEMENT(element);
7967 #if USE_STOP_CHANGED_ELEMENTS
7968 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
7970 int old_element = Feld[x][y];
7972 /* prevent changed element from moving in same engine frame
7973 unless both old and new element can either fall or move */
7974 if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
7975 (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
7980 CreateFieldExt(x, y, element, TRUE);
7983 static boolean ChangeElement(int x, int y, int element, int page)
7985 struct ElementChangeInfo *change = &element_info[element].change_page[page];
7987 int old_element = Feld[x][y];
7989 /* always use default change event to prevent running into a loop */
7990 if (ChangeEvent[x][y] == -1)
7991 ChangeEvent[x][y] = CE_DELAY;
7993 if (ChangeEvent[x][y] == CE_DELAY)
7995 /* reset actual trigger element, trigger player and action element */
7996 change->actual_trigger_element = EL_EMPTY;
7997 change->actual_trigger_player = EL_PLAYER_1;
7998 change->actual_trigger_side = CH_SIDE_NONE;
7999 change->actual_trigger_ce_value = 0;
8002 /* do not change elements more than a specified maximum number of changes */
8003 if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
8006 ChangeCount[x][y]++; /* count number of changes in the same frame */
8008 if (change->explode)
8015 if (change->use_target_content)
8017 boolean complete_replace = TRUE;
8018 boolean can_replace[3][3];
8021 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
8024 boolean is_walkable;
8025 boolean is_diggable;
8026 boolean is_collectible;
8027 boolean is_removable;
8028 boolean is_destructible;
8029 int ex = x + xx - 1;
8030 int ey = y + yy - 1;
8031 int content_element = change->target_content.e[xx][yy];
8034 can_replace[xx][yy] = TRUE;
8036 if (ex == x && ey == y) /* do not check changing element itself */
8039 if (content_element == EL_EMPTY_SPACE)
8041 can_replace[xx][yy] = FALSE; /* do not replace border with space */
8046 if (!IN_LEV_FIELD(ex, ey))
8048 can_replace[xx][yy] = FALSE;
8049 complete_replace = FALSE;
8056 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
8057 e = MovingOrBlocked2Element(ex, ey);
8059 is_empty = (IS_FREE(ex, ey) ||
8060 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
8062 is_walkable = (is_empty || IS_WALKABLE(e));
8063 is_diggable = (is_empty || IS_DIGGABLE(e));
8064 is_collectible = (is_empty || IS_COLLECTIBLE(e));
8065 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
8066 is_removable = (is_diggable || is_collectible);
8068 can_replace[xx][yy] =
8069 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
8070 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
8071 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
8072 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
8073 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
8074 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
8075 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
8077 if (!can_replace[xx][yy])
8078 complete_replace = FALSE;
8081 if (!change->only_if_complete || complete_replace)
8083 boolean something_has_changed = FALSE;
8085 if (change->only_if_complete && change->use_random_replace &&
8086 RND(100) < change->random_percentage)
8089 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
8091 int ex = x + xx - 1;
8092 int ey = y + yy - 1;
8093 int content_element;
8095 if (can_replace[xx][yy] && (!change->use_random_replace ||
8096 RND(100) < change->random_percentage))
8098 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
8099 RemoveMovingField(ex, ey);
8101 ChangeEvent[ex][ey] = ChangeEvent[x][y];
8103 content_element = change->target_content.e[xx][yy];
8104 target_element = GET_TARGET_ELEMENT(content_element, change);
8106 CreateElementFromChange(ex, ey, target_element);
8108 something_has_changed = TRUE;
8110 /* for symmetry reasons, freeze newly created border elements */
8111 if (ex != x || ey != y)
8112 Stop[ex][ey] = TRUE; /* no more moving in this frame */
8116 if (something_has_changed)
8118 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
8119 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
8125 target_element = GET_TARGET_ELEMENT(change->target_element, change);
8127 if (element == EL_DIAGONAL_GROWING ||
8128 element == EL_DIAGONAL_SHRINKING)
8130 target_element = Store[x][y];
8132 Store[x][y] = EL_EMPTY;
8135 CreateElementFromChange(x, y, target_element);
8137 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
8138 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
8141 /* this uses direct change before indirect change */
8142 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
8147 #if USE_NEW_DELAYED_ACTION
8149 static void HandleElementChange(int x, int y, int page)
8151 int element = MovingOrBlocked2Element(x, y);
8152 struct ElementInfo *ei = &element_info[element];
8153 struct ElementChangeInfo *change = &ei->change_page[page];
8156 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
8157 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
8160 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
8161 x, y, element, element_info[element].token_name);
8162 printf("HandleElementChange(): This should never happen!\n");
8167 /* this can happen with classic bombs on walkable, changing elements */
8168 if (!CAN_CHANGE_OR_HAS_ACTION(element))
8171 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
8172 ChangeDelay[x][y] = 0;
8178 if (ChangeDelay[x][y] == 0) /* initialize element change */
8180 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
8182 if (change->can_change)
8184 ResetGfxAnimation(x, y);
8185 ResetRandomAnimationValue(x, y);
8187 if (change->pre_change_function)
8188 change->pre_change_function(x, y);
8192 ChangeDelay[x][y]--;
8194 if (ChangeDelay[x][y] != 0) /* continue element change */
8196 if (change->can_change)
8198 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8200 if (IS_ANIMATED(graphic))
8201 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8203 if (change->change_function)
8204 change->change_function(x, y);
8207 else /* finish element change */
8209 if (ChangePage[x][y] != -1) /* remember page from delayed change */
8211 page = ChangePage[x][y];
8212 ChangePage[x][y] = -1;
8214 change = &ei->change_page[page];
8217 if (IS_MOVING(x, y)) /* never change a running system ;-) */
8219 ChangeDelay[x][y] = 1; /* try change after next move step */
8220 ChangePage[x][y] = page; /* remember page to use for change */
8225 if (change->can_change)
8227 if (ChangeElement(x, y, element, page))
8229 if (change->post_change_function)
8230 change->post_change_function(x, y);
8234 if (change->has_action)
8235 ExecuteCustomElementAction(x, y, element, page);
8241 static void HandleElementChange(int x, int y, int page)
8243 int element = MovingOrBlocked2Element(x, y);
8244 struct ElementInfo *ei = &element_info[element];
8245 struct ElementChangeInfo *change = &ei->change_page[page];
8248 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
8251 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
8252 x, y, element, element_info[element].token_name);
8253 printf("HandleElementChange(): This should never happen!\n");
8258 /* this can happen with classic bombs on walkable, changing elements */
8259 if (!CAN_CHANGE(element))
8262 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
8263 ChangeDelay[x][y] = 0;
8269 if (ChangeDelay[x][y] == 0) /* initialize element change */
8271 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
8273 ResetGfxAnimation(x, y);
8274 ResetRandomAnimationValue(x, y);
8276 if (change->pre_change_function)
8277 change->pre_change_function(x, y);
8280 ChangeDelay[x][y]--;
8282 if (ChangeDelay[x][y] != 0) /* continue element change */
8284 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8286 if (IS_ANIMATED(graphic))
8287 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8289 if (change->change_function)
8290 change->change_function(x, y);
8292 else /* finish element change */
8294 if (ChangePage[x][y] != -1) /* remember page from delayed change */
8296 page = ChangePage[x][y];
8297 ChangePage[x][y] = -1;
8299 change = &ei->change_page[page];
8302 if (IS_MOVING(x, y)) /* never change a running system ;-) */
8304 ChangeDelay[x][y] = 1; /* try change after next move step */
8305 ChangePage[x][y] = page; /* remember page to use for change */
8310 if (ChangeElement(x, y, element, page))
8312 if (change->post_change_function)
8313 change->post_change_function(x, y);
8320 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
8321 int trigger_element,
8327 boolean change_done_any = FALSE;
8328 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
8331 if (!(trigger_events[trigger_element][trigger_event]))
8334 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8336 int element = EL_CUSTOM_START + i;
8337 boolean change_done = FALSE;
8340 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
8341 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
8344 for (p = 0; p < element_info[element].num_change_pages; p++)
8346 struct ElementChangeInfo *change = &element_info[element].change_page[p];
8348 if (change->can_change_or_has_action &&
8349 change->has_event[trigger_event] &&
8350 change->trigger_side & trigger_side &&
8351 change->trigger_player & trigger_player &&
8352 change->trigger_page & trigger_page_bits &&
8353 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
8355 change->actual_trigger_element = trigger_element;
8356 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
8357 change->actual_trigger_side = trigger_side;
8358 change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
8360 if ((change->can_change && !change_done) || change->has_action)
8365 SCAN_PLAYFIELD(x, y)
8367 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8370 if (Feld[x][y] == element)
8372 if (change->can_change && !change_done)
8374 ChangeDelay[x][y] = 1;
8375 ChangeEvent[x][y] = trigger_event;
8377 HandleElementChange(x, y, p);
8379 #if USE_NEW_DELAYED_ACTION
8380 else if (change->has_action)
8382 ExecuteCustomElementAction(x, y, element, p);
8383 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8386 if (change->has_action)
8388 ExecuteCustomElementAction(x, y, element, p);
8389 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8395 if (change->can_change)
8398 change_done_any = TRUE;
8405 return change_done_any;
8408 static boolean CheckElementChangeExt(int x, int y,
8410 int trigger_element,
8415 boolean change_done = FALSE;
8418 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
8419 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
8422 if (Feld[x][y] == EL_BLOCKED)
8424 Blocked2Moving(x, y, &x, &y);
8425 element = Feld[x][y];
8429 /* check if element has already changed */
8430 if (Feld[x][y] != element)
8433 /* check if element has already changed or is about to change after moving */
8434 if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
8435 Feld[x][y] != element) ||
8437 (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
8438 (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
8439 ChangePage[x][y] != -1)))
8443 for (p = 0; p < element_info[element].num_change_pages; p++)
8445 struct ElementChangeInfo *change = &element_info[element].change_page[p];
8447 boolean check_trigger_element =
8448 (trigger_event == CE_TOUCHING_X ||
8449 trigger_event == CE_HITTING_X ||
8450 trigger_event == CE_HIT_BY_X);
8452 if (change->can_change_or_has_action &&
8453 change->has_event[trigger_event] &&
8454 change->trigger_side & trigger_side &&
8455 change->trigger_player & trigger_player &&
8456 (!check_trigger_element ||
8457 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
8459 change->actual_trigger_element = trigger_element;
8460 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
8461 change->actual_trigger_side = trigger_side;
8462 change->actual_trigger_ce_value = CustomValue[x][y];
8464 /* special case: trigger element not at (x,y) position for some events */
8465 if (check_trigger_element)
8477 { 0, 0 }, { 0, 0 }, { 0, 0 },
8481 int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
8482 int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
8484 change->actual_trigger_ce_value = CustomValue[xx][yy];
8487 if (change->can_change && !change_done)
8489 ChangeDelay[x][y] = 1;
8490 ChangeEvent[x][y] = trigger_event;
8492 HandleElementChange(x, y, p);
8496 #if USE_NEW_DELAYED_ACTION
8497 else if (change->has_action)
8499 ExecuteCustomElementAction(x, y, element, p);
8500 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8503 if (change->has_action)
8505 ExecuteCustomElementAction(x, y, element, p);
8506 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8515 static void PlayPlayerSound(struct PlayerInfo *player)
8517 int jx = player->jx, jy = player->jy;
8518 int sound_element = player->artwork_element;
8519 int last_action = player->last_action_waiting;
8520 int action = player->action_waiting;
8522 if (player->is_waiting)
8524 if (action != last_action)
8525 PlayLevelSoundElementAction(jx, jy, sound_element, action);
8527 PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
8531 if (action != last_action)
8532 StopSound(element_info[sound_element].sound[last_action]);
8534 if (last_action == ACTION_SLEEPING)
8535 PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
8539 static void PlayAllPlayersSound()
8543 for (i = 0; i < MAX_PLAYERS; i++)
8544 if (stored_player[i].active)
8545 PlayPlayerSound(&stored_player[i]);
8548 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
8550 boolean last_waiting = player->is_waiting;
8551 int move_dir = player->MovDir;
8553 player->last_action_waiting = player->action_waiting;
8557 if (!last_waiting) /* not waiting -> waiting */
8559 player->is_waiting = TRUE;
8561 player->frame_counter_bored =
8563 game.player_boring_delay_fixed +
8564 SimpleRND(game.player_boring_delay_random);
8565 player->frame_counter_sleeping =
8567 game.player_sleeping_delay_fixed +
8568 SimpleRND(game.player_sleeping_delay_random);
8570 InitPlayerGfxAnimation(player, ACTION_WAITING, player->MovDir);
8573 if (game.player_sleeping_delay_fixed +
8574 game.player_sleeping_delay_random > 0 &&
8575 player->anim_delay_counter == 0 &&
8576 player->post_delay_counter == 0 &&
8577 FrameCounter >= player->frame_counter_sleeping)
8578 player->is_sleeping = TRUE;
8579 else if (game.player_boring_delay_fixed +
8580 game.player_boring_delay_random > 0 &&
8581 FrameCounter >= player->frame_counter_bored)
8582 player->is_bored = TRUE;
8584 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
8585 player->is_bored ? ACTION_BORING :
8588 if (player->is_sleeping)
8590 if (player->num_special_action_sleeping > 0)
8592 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
8594 int last_special_action = player->special_action_sleeping;
8595 int num_special_action = player->num_special_action_sleeping;
8596 int special_action =
8597 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
8598 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
8599 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
8600 last_special_action + 1 : ACTION_SLEEPING);
8601 int special_graphic =
8602 el_act_dir2img(player->artwork_element, special_action, move_dir);
8604 player->anim_delay_counter =
8605 graphic_info[special_graphic].anim_delay_fixed +
8606 SimpleRND(graphic_info[special_graphic].anim_delay_random);
8607 player->post_delay_counter =
8608 graphic_info[special_graphic].post_delay_fixed +
8609 SimpleRND(graphic_info[special_graphic].post_delay_random);
8611 player->special_action_sleeping = special_action;
8614 if (player->anim_delay_counter > 0)
8616 player->action_waiting = player->special_action_sleeping;
8617 player->anim_delay_counter--;
8619 else if (player->post_delay_counter > 0)
8621 player->post_delay_counter--;
8625 else if (player->is_bored)
8627 if (player->num_special_action_bored > 0)
8629 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
8631 int special_action =
8632 ACTION_BORING_1 + SimpleRND(player->num_special_action_bored);
8633 int special_graphic =
8634 el_act_dir2img(player->artwork_element, special_action, move_dir);
8636 player->anim_delay_counter =
8637 graphic_info[special_graphic].anim_delay_fixed +
8638 SimpleRND(graphic_info[special_graphic].anim_delay_random);
8639 player->post_delay_counter =
8640 graphic_info[special_graphic].post_delay_fixed +
8641 SimpleRND(graphic_info[special_graphic].post_delay_random);
8643 player->special_action_bored = special_action;
8646 if (player->anim_delay_counter > 0)
8648 player->action_waiting = player->special_action_bored;
8649 player->anim_delay_counter--;
8651 else if (player->post_delay_counter > 0)
8653 player->post_delay_counter--;
8658 else if (last_waiting) /* waiting -> not waiting */
8660 player->is_waiting = FALSE;
8661 player->is_bored = FALSE;
8662 player->is_sleeping = FALSE;
8664 player->frame_counter_bored = -1;
8665 player->frame_counter_sleeping = -1;
8667 player->anim_delay_counter = 0;
8668 player->post_delay_counter = 0;
8670 player->action_waiting = ACTION_DEFAULT;
8672 player->special_action_bored = ACTION_DEFAULT;
8673 player->special_action_sleeping = ACTION_DEFAULT;
8677 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
8679 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
8680 int left = player_action & JOY_LEFT;
8681 int right = player_action & JOY_RIGHT;
8682 int up = player_action & JOY_UP;
8683 int down = player_action & JOY_DOWN;
8684 int button1 = player_action & JOY_BUTTON_1;
8685 int button2 = player_action & JOY_BUTTON_2;
8686 int dx = (left ? -1 : right ? 1 : 0);
8687 int dy = (up ? -1 : down ? 1 : 0);
8689 if (!player->active || tape.pausing)
8695 snapped = SnapField(player, dx, dy);
8699 dropped = DropElement(player);
8701 moved = MovePlayer(player, dx, dy);
8704 if (tape.single_step && tape.recording && !tape.pausing)
8706 if (button1 || (dropped && !moved))
8708 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8709 SnapField(player, 0, 0); /* stop snapping */
8713 SetPlayerWaiting(player, FALSE);
8715 return player_action;
8719 /* no actions for this player (no input at player's configured device) */
8721 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
8722 SnapField(player, 0, 0);
8723 CheckGravityMovementWhenNotMoving(player);
8725 if (player->MovPos == 0)
8726 SetPlayerWaiting(player, TRUE);
8728 if (player->MovPos == 0) /* needed for tape.playing */
8729 player->is_moving = FALSE;
8731 player->is_dropping = FALSE;
8737 void AdvanceFrameAndPlayerCounters(int player_nr)
8741 /* advance frame counters (global frame counter and time frame counter) */
8745 /* advance player counters (counters for move delay, move animation etc.) */
8746 for (i = 0; i < MAX_PLAYERS; i++)
8748 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
8749 int move_delay_value = stored_player[i].move_delay_value;
8750 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
8752 if (!advance_player_counters) /* not all players may be affected */
8755 #if USE_NEW_PLAYER_ANIM
8756 if (move_frames == 0) /* less than one move per game frame */
8758 int stepsize = TILEX / move_delay_value;
8759 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
8760 int count = (stored_player[i].is_moving ?
8761 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
8763 if (count % delay == 0)
8768 stored_player[i].Frame += move_frames;
8770 if (stored_player[i].MovPos != 0)
8771 stored_player[i].StepFrame += move_frames;
8773 if (stored_player[i].move_delay > 0)
8774 stored_player[i].move_delay--;
8776 /* due to bugs in previous versions, counter must count up, not down */
8777 if (stored_player[i].push_delay != -1)
8778 stored_player[i].push_delay++;
8780 if (stored_player[i].drop_delay > 0)
8781 stored_player[i].drop_delay--;
8787 static unsigned long game_frame_delay = 0;
8788 unsigned long game_frame_delay_value;
8789 int magic_wall_x = 0, magic_wall_y = 0;
8790 int i, x, y, element, graphic;
8791 byte *recorded_player_action;
8792 byte summarized_player_action = 0;
8793 byte tape_action[MAX_PLAYERS];
8795 if (game_status != GAME_MODE_PLAYING)
8798 game_frame_delay_value =
8799 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
8801 if (tape.playing && tape.warp_forward && !tape.pausing)
8802 game_frame_delay_value = 0;
8804 /* ---------- main game synchronization point ---------- */
8806 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
8808 InitPlayfieldScanModeVars();
8810 #if USE_ONE_MORE_CHANGE_PER_FRAME
8811 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
8813 SCAN_PLAYFIELD(x, y)
8815 ChangeCount[x][y] = 0;
8816 ChangeEvent[x][y] = -1;
8821 if (network_playing && !network_player_action_received)
8823 /* try to get network player actions in time */
8825 #if defined(NETWORK_AVALIABLE)
8826 /* last chance to get network player actions without main loop delay */
8830 /* game was quit by network peer */
8831 if (game_status != GAME_MODE_PLAYING)
8834 if (!network_player_action_received)
8835 return; /* failed to get network player actions in time */
8841 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
8844 /* !!! CHECK THIS (tape.pausing is always FALSE here!) !!! */
8845 if (recorded_player_action == NULL && tape.pausing)
8849 for (i = 0; i < MAX_PLAYERS; i++)
8851 summarized_player_action |= stored_player[i].action;
8853 if (!network_playing)
8854 stored_player[i].effective_action = stored_player[i].action;
8857 #if defined(NETWORK_AVALIABLE)
8858 if (network_playing)
8859 SendToServer_MovePlayer(summarized_player_action);
8862 if (!options.network && !setup.team_mode)
8863 local_player->effective_action = summarized_player_action;
8865 if (recorded_player_action != NULL)
8866 for (i = 0; i < MAX_PLAYERS; i++)
8867 stored_player[i].effective_action = recorded_player_action[i];
8869 for (i = 0; i < MAX_PLAYERS; i++)
8871 tape_action[i] = stored_player[i].effective_action;
8873 if (tape.recording && tape_action[i] && !tape.player_participates[i])
8874 tape.player_participates[i] = TRUE; /* player just appeared from CE */
8877 /* only save actions from input devices, but not programmed actions */
8879 TapeRecordAction(tape_action);
8881 for (i = 0; i < MAX_PLAYERS; i++)
8883 int actual_player_action = stored_player[i].effective_action;
8886 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
8887 - rnd_equinox_tetrachloride 048
8888 - rnd_equinox_tetrachloride_ii 096
8889 - rnd_emanuel_schmieg 002
8890 - doctor_sloan_ww 001, 020
8892 if (stored_player[i].MovPos == 0)
8893 CheckGravityMovement(&stored_player[i]);
8896 /* overwrite programmed action with tape action */
8897 if (stored_player[i].programmed_action)
8898 actual_player_action = stored_player[i].programmed_action;
8901 PlayerActions(&stored_player[i], actual_player_action);
8903 tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
8905 if (tape.recording && tape_action[i] && !tape.player_participates[i])
8906 tape.player_participates[i] = TRUE; /* player just appeared from CE */
8909 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
8912 network_player_action_received = FALSE;
8914 ScrollScreen(NULL, SCROLL_GO_ON);
8916 /* for backwards compatibility, the following code emulates a fixed bug that
8917 occured when pushing elements (causing elements that just made their last
8918 pushing step to already (if possible) make their first falling step in the
8919 same game frame, which is bad); this code is also needed to use the famous
8920 "spring push bug" which is used in older levels and might be wanted to be
8921 used also in newer levels, but in this case the buggy pushing code is only
8922 affecting the "spring" element and no other elements */
8924 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
8926 for (i = 0; i < MAX_PLAYERS; i++)
8928 struct PlayerInfo *player = &stored_player[i];
8932 if (player->active && player->is_pushing && player->is_moving &&
8934 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
8935 Feld[x][y] == EL_SPRING))
8937 ContinueMoving(x, y);
8939 /* continue moving after pushing (this is actually a bug) */
8940 if (!IS_MOVING(x, y))
8949 SCAN_PLAYFIELD(x, y)
8951 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8954 ChangeCount[x][y] = 0;
8955 ChangeEvent[x][y] = -1;
8957 /* this must be handled before main playfield loop */
8958 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
8961 if (MovDelay[x][y] <= 0)
8965 #if USE_NEW_SNAP_DELAY
8966 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
8969 if (MovDelay[x][y] <= 0)
8972 DrawLevelField(x, y);
8974 TestIfElementTouchesCustomElement(x, y); /* for empty space */
8980 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
8982 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
8983 printf("GameActions(): This should never happen!\n");
8985 ChangePage[x][y] = -1;
8990 if (WasJustMoving[x][y] > 0)
8991 WasJustMoving[x][y]--;
8992 if (WasJustFalling[x][y] > 0)
8993 WasJustFalling[x][y]--;
8994 if (CheckCollision[x][y] > 0)
8995 CheckCollision[x][y]--;
8999 /* reset finished pushing action (not done in ContinueMoving() to allow
9000 continuous pushing animation for elements with zero push delay) */
9001 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
9003 ResetGfxAnimation(x, y);
9004 DrawLevelField(x, y);
9008 if (IS_BLOCKED(x, y))
9012 Blocked2Moving(x, y, &oldx, &oldy);
9013 if (!IS_MOVING(oldx, oldy))
9015 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
9016 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
9017 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
9018 printf("GameActions(): This should never happen!\n");
9025 SCAN_PLAYFIELD(x, y)
9027 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9030 element = Feld[x][y];
9031 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9034 printf("::: %d,%d\n", x, y);
9036 if (element == EL_ROCK)
9037 printf("::: Yo man! Rocks can fall!\n");
9040 if (graphic_info[graphic].anim_global_sync)
9041 GfxFrame[x][y] = FrameCounter;
9042 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
9044 int old_gfx_frame = GfxFrame[x][y];
9046 GfxFrame[x][y] = CustomValue[x][y];
9049 if (GfxFrame[x][y] != old_gfx_frame)
9051 DrawLevelGraphicAnimation(x, y, graphic);
9053 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
9055 int old_gfx_frame = GfxFrame[x][y];
9057 GfxFrame[x][y] = element_info[element].collect_score;
9060 if (GfxFrame[x][y] != old_gfx_frame)
9062 DrawLevelGraphicAnimation(x, y, graphic);
9065 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
9066 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
9067 ResetRandomAnimationValue(x, y);
9069 SetRandomAnimationValue(x, y);
9071 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
9073 if (IS_INACTIVE(element))
9075 if (IS_ANIMATED(graphic))
9076 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9081 /* this may take place after moving, so 'element' may have changed */
9082 if (IS_CHANGING(x, y) &&
9083 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
9085 int page = element_info[element].event_page_nr[CE_DELAY];
9087 HandleElementChange(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] : page);
9091 printf("::: ChangeDelay == %d\n", ChangeDelay[x][y]);
9095 if (element == EL_CUSTOM_255)
9096 printf("::: ChangeDelay == %d\n", ChangeDelay[x][y]);
9100 HandleElementChange(x, y, page);
9102 if (CAN_CHANGE(element))
9103 HandleElementChange(x, y, page);
9105 if (HAS_ACTION(element))
9106 ExecuteCustomElementAction(x, y, element, page);
9111 element = Feld[x][y];
9112 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9115 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
9119 element = Feld[x][y];
9120 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9122 if (IS_ANIMATED(graphic) &&
9125 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9127 if (IS_GEM(element) || element == EL_SP_INFOTRON)
9128 EdelsteinFunkeln(x, y);
9130 else if ((element == EL_ACID ||
9131 element == EL_EXIT_OPEN ||
9132 element == EL_SP_EXIT_OPEN ||
9133 element == EL_SP_TERMINAL ||
9134 element == EL_SP_TERMINAL_ACTIVE ||
9135 element == EL_EXTRA_TIME ||
9136 element == EL_SHIELD_NORMAL ||
9137 element == EL_SHIELD_DEADLY) &&
9138 IS_ANIMATED(graphic))
9139 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9140 else if (IS_MOVING(x, y))
9141 ContinueMoving(x, y);
9142 else if (IS_ACTIVE_BOMB(element))
9143 CheckDynamite(x, y);
9144 else if (element == EL_AMOEBA_GROWING)
9145 AmoebeWaechst(x, y);
9146 else if (element == EL_AMOEBA_SHRINKING)
9147 AmoebaDisappearing(x, y);
9149 #if !USE_NEW_AMOEBA_CODE
9150 else if (IS_AMOEBALIVE(element))
9151 AmoebeAbleger(x, y);
9154 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
9156 else if (element == EL_EXIT_CLOSED)
9158 else if (element == EL_SP_EXIT_CLOSED)
9160 else if (element == EL_EXPANDABLE_WALL_GROWING)
9162 else if (element == EL_EXPANDABLE_WALL ||
9163 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9164 element == EL_EXPANDABLE_WALL_VERTICAL ||
9165 element == EL_EXPANDABLE_WALL_ANY)
9167 else if (element == EL_FLAMES)
9168 CheckForDragon(x, y);
9169 else if (element == EL_EXPLOSION)
9170 ; /* drawing of correct explosion animation is handled separately */
9171 else if (element == EL_ELEMENT_SNAPPING ||
9172 element == EL_DIAGONAL_SHRINKING ||
9173 element == EL_DIAGONAL_GROWING)
9176 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
9178 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9181 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
9182 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9185 if (element == EL_CUSTOM_255 ||
9186 element == EL_CUSTOM_256)
9187 DrawLevelGraphicAnimation(x, y, graphic);
9190 if (IS_BELT_ACTIVE(element))
9191 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
9193 if (game.magic_wall_active)
9195 int jx = local_player->jx, jy = local_player->jy;
9197 /* play the element sound at the position nearest to the player */
9198 if ((element == EL_MAGIC_WALL_FULL ||
9199 element == EL_MAGIC_WALL_ACTIVE ||
9200 element == EL_MAGIC_WALL_EMPTYING ||
9201 element == EL_BD_MAGIC_WALL_FULL ||
9202 element == EL_BD_MAGIC_WALL_ACTIVE ||
9203 element == EL_BD_MAGIC_WALL_EMPTYING) &&
9204 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
9212 #if USE_NEW_AMOEBA_CODE
9213 /* new experimental amoeba growth stuff */
9214 if (!(FrameCounter % 8))
9216 static unsigned long random = 1684108901;
9218 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
9220 x = RND(lev_fieldx);
9221 y = RND(lev_fieldy);
9222 element = Feld[x][y];
9224 if (!IS_PLAYER(x,y) &&
9225 (element == EL_EMPTY ||
9226 CAN_GROW_INTO(element) ||
9227 element == EL_QUICKSAND_EMPTY ||
9228 element == EL_ACID_SPLASH_LEFT ||
9229 element == EL_ACID_SPLASH_RIGHT))
9231 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
9232 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
9233 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
9234 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
9235 Feld[x][y] = EL_AMOEBA_DROP;
9238 random = random * 129 + 1;
9244 if (game.explosions_delayed)
9247 game.explosions_delayed = FALSE;
9250 SCAN_PLAYFIELD(x, y)
9252 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9255 element = Feld[x][y];
9257 if (ExplodeField[x][y])
9258 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
9259 else if (element == EL_EXPLOSION)
9260 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
9262 ExplodeField[x][y] = EX_TYPE_NONE;
9265 game.explosions_delayed = TRUE;
9268 if (game.magic_wall_active)
9270 if (!(game.magic_wall_time_left % 4))
9272 int element = Feld[magic_wall_x][magic_wall_y];
9274 if (element == EL_BD_MAGIC_WALL_FULL ||
9275 element == EL_BD_MAGIC_WALL_ACTIVE ||
9276 element == EL_BD_MAGIC_WALL_EMPTYING)
9277 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
9279 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
9282 if (game.magic_wall_time_left > 0)
9284 game.magic_wall_time_left--;
9285 if (!game.magic_wall_time_left)
9288 SCAN_PLAYFIELD(x, y)
9290 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9293 element = Feld[x][y];
9295 if (element == EL_MAGIC_WALL_ACTIVE ||
9296 element == EL_MAGIC_WALL_FULL)
9298 Feld[x][y] = EL_MAGIC_WALL_DEAD;
9299 DrawLevelField(x, y);
9301 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
9302 element == EL_BD_MAGIC_WALL_FULL)
9304 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
9305 DrawLevelField(x, y);
9309 game.magic_wall_active = FALSE;
9314 if (game.light_time_left > 0)
9316 game.light_time_left--;
9318 if (game.light_time_left == 0)
9319 RedrawAllLightSwitchesAndInvisibleElements();
9322 if (game.timegate_time_left > 0)
9324 game.timegate_time_left--;
9326 if (game.timegate_time_left == 0)
9327 CloseAllOpenTimegates();
9330 if (game.lenses_time_left > 0)
9332 game.lenses_time_left--;
9334 if (game.lenses_time_left == 0)
9335 RedrawAllInvisibleElementsForLenses();
9338 if (game.magnify_time_left > 0)
9340 game.magnify_time_left--;
9342 if (game.magnify_time_left == 0)
9343 RedrawAllInvisibleElementsForMagnifier();
9346 for (i = 0; i < MAX_PLAYERS; i++)
9348 struct PlayerInfo *player = &stored_player[i];
9350 if (SHIELD_ON(player))
9352 if (player->shield_deadly_time_left)
9353 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
9354 else if (player->shield_normal_time_left)
9355 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
9359 if (TimeFrames >= FRAMES_PER_SECOND)
9364 for (i = 0; i < MAX_PLAYERS; i++)
9366 struct PlayerInfo *player = &stored_player[i];
9368 if (SHIELD_ON(player))
9370 player->shield_normal_time_left--;
9372 if (player->shield_deadly_time_left > 0)
9373 player->shield_deadly_time_left--;
9377 if (!level.use_step_counter)
9385 if (TimeLeft <= 10 && setup.time_limit)
9386 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
9388 DrawGameValue_Time(TimeLeft);
9390 if (!TimeLeft && setup.time_limit)
9391 for (i = 0; i < MAX_PLAYERS; i++)
9392 KillPlayer(&stored_player[i]);
9394 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
9395 DrawGameValue_Time(TimePlayed);
9398 if (tape.recording || tape.playing)
9399 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
9403 PlayAllPlayersSound();
9405 if (options.debug) /* calculate frames per second */
9407 static unsigned long fps_counter = 0;
9408 static int fps_frames = 0;
9409 unsigned long fps_delay_ms = Counter() - fps_counter;
9413 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
9415 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
9418 fps_counter = Counter();
9421 redraw_mask |= REDRAW_FPS;
9424 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
9426 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
9428 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
9430 local_player->show_envelope = 0;
9433 /* use random number generator in every frame to make it less predictable */
9434 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
9438 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
9440 int min_x = x, min_y = y, max_x = x, max_y = y;
9443 for (i = 0; i < MAX_PLAYERS; i++)
9445 int jx = stored_player[i].jx, jy = stored_player[i].jy;
9447 if (!stored_player[i].active || &stored_player[i] == player)
9450 min_x = MIN(min_x, jx);
9451 min_y = MIN(min_y, jy);
9452 max_x = MAX(max_x, jx);
9453 max_y = MAX(max_y, jy);
9456 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
9459 static boolean AllPlayersInVisibleScreen()
9463 for (i = 0; i < MAX_PLAYERS; i++)
9465 int jx = stored_player[i].jx, jy = stored_player[i].jy;
9467 if (!stored_player[i].active)
9470 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
9477 void ScrollLevel(int dx, int dy)
9479 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
9482 BlitBitmap(drawto_field, drawto_field,
9483 FX + TILEX * (dx == -1) - softscroll_offset,
9484 FY + TILEY * (dy == -1) - softscroll_offset,
9485 SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
9486 SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
9487 FX + TILEX * (dx == 1) - softscroll_offset,
9488 FY + TILEY * (dy == 1) - softscroll_offset);
9492 x = (dx == 1 ? BX1 : BX2);
9493 for (y = BY1; y <= BY2; y++)
9494 DrawScreenField(x, y);
9499 y = (dy == 1 ? BY1 : BY2);
9500 for (x = BX1; x <= BX2; x++)
9501 DrawScreenField(x, y);
9504 redraw_mask |= REDRAW_FIELD;
9507 static boolean canFallDown(struct PlayerInfo *player)
9509 int jx = player->jx, jy = player->jy;
9511 return (IN_LEV_FIELD(jx, jy + 1) &&
9512 (IS_FREE(jx, jy + 1) ||
9513 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
9514 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
9515 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
9518 static boolean canPassField(int x, int y, int move_dir)
9520 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9521 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9522 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
9525 int element = Feld[x][y];
9527 return (IS_PASSABLE_FROM(element, opposite_dir) &&
9528 !CAN_MOVE(element) &&
9529 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
9530 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
9531 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
9534 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
9536 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9537 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9538 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
9542 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9543 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
9544 (IS_DIGGABLE(Feld[newx][newy]) ||
9545 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9546 canPassField(newx, newy, move_dir)));
9549 static void CheckGravityMovement(struct PlayerInfo *player)
9551 if (game.gravity && !player->programmed_action)
9553 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
9554 int move_dir_vertical = player->effective_action & MV_VERTICAL;
9555 boolean player_is_snapping = player->effective_action & JOY_BUTTON_1;
9556 int jx = player->jx, jy = player->jy;
9557 boolean player_is_moving_to_valid_field =
9558 (!player_is_snapping &&
9559 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
9560 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
9561 boolean player_can_fall_down = canFallDown(player);
9563 if (player_can_fall_down &&
9564 !player_is_moving_to_valid_field)
9565 player->programmed_action = MV_DOWN;
9569 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
9571 return CheckGravityMovement(player);
9573 if (game.gravity && !player->programmed_action)
9575 int jx = player->jx, jy = player->jy;
9576 boolean field_under_player_is_free =
9577 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
9578 boolean player_is_standing_on_valid_field =
9579 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
9580 (IS_WALKABLE(Feld[jx][jy]) &&
9581 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
9583 if (field_under_player_is_free && !player_is_standing_on_valid_field)
9584 player->programmed_action = MV_DOWN;
9590 -----------------------------------------------------------------------------
9591 dx, dy: direction (non-diagonal) to try to move the player to
9592 real_dx, real_dy: direction as read from input device (can be diagonal)
9595 boolean MovePlayerOneStep(struct PlayerInfo *player,
9596 int dx, int dy, int real_dx, int real_dy)
9598 int jx = player->jx, jy = player->jy;
9599 int new_jx = jx + dx, new_jy = jy + dy;
9600 #if !USE_FIXED_DONT_RUN_INTO
9604 boolean player_can_move = !player->cannot_move;
9606 if (!player->active || (!dx && !dy))
9607 return MP_NO_ACTION;
9609 player->MovDir = (dx < 0 ? MV_LEFT :
9612 dy > 0 ? MV_DOWN : MV_NONE);
9614 if (!IN_LEV_FIELD(new_jx, new_jy))
9615 return MP_NO_ACTION;
9617 if (!player_can_move)
9620 if (player->MovPos == 0)
9622 player->is_moving = FALSE;
9623 player->is_digging = FALSE;
9624 player->is_collecting = FALSE;
9625 player->is_snapping = FALSE;
9626 player->is_pushing = FALSE;
9629 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
9630 SnapField(player, 0, 0);
9634 return MP_NO_ACTION;
9638 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
9639 return MP_NO_ACTION;
9641 #if !USE_FIXED_DONT_RUN_INTO
9642 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
9644 /* (moved to DigField()) */
9645 if (player_can_move && DONT_RUN_INTO(element))
9647 if (element == EL_ACID && dx == 0 && dy == 1)
9649 SplashAcid(new_jx, new_jy);
9650 Feld[jx][jy] = EL_PLAYER_1;
9651 InitMovingField(jx, jy, MV_DOWN);
9652 Store[jx][jy] = EL_ACID;
9653 ContinueMoving(jx, jy);
9657 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
9663 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
9665 #if USE_FIXED_DONT_RUN_INTO
9666 if (can_move == MP_DONT_RUN_INTO)
9670 if (can_move != MP_MOVING)
9673 #if USE_FIXED_DONT_RUN_INTO
9676 /* check if DigField() has caused relocation of the player */
9677 if (player->jx != jx || player->jy != jy)
9678 return MP_NO_ACTION; /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
9680 StorePlayer[jx][jy] = 0;
9681 player->last_jx = jx;
9682 player->last_jy = jy;
9683 player->jx = new_jx;
9684 player->jy = new_jy;
9685 StorePlayer[new_jx][new_jy] = player->element_nr;
9687 if (player->move_delay_value_next != -1)
9689 player->move_delay_value = player->move_delay_value_next;
9690 player->move_delay_value_next = -1;
9694 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
9696 player->step_counter++;
9698 PlayerVisit[jx][jy] = FrameCounter;
9700 ScrollPlayer(player, SCROLL_INIT);
9705 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
9707 int jx = player->jx, jy = player->jy;
9708 int old_jx = jx, old_jy = jy;
9709 int moved = MP_NO_ACTION;
9711 if (!player->active)
9716 if (player->MovPos == 0)
9718 player->is_moving = FALSE;
9719 player->is_digging = FALSE;
9720 player->is_collecting = FALSE;
9721 player->is_snapping = FALSE;
9722 player->is_pushing = FALSE;
9728 if (player->move_delay > 0)
9731 player->move_delay = -1; /* set to "uninitialized" value */
9733 /* store if player is automatically moved to next field */
9734 player->is_auto_moving = (player->programmed_action != MV_NONE);
9736 /* remove the last programmed player action */
9737 player->programmed_action = 0;
9741 /* should only happen if pre-1.2 tape recordings are played */
9742 /* this is only for backward compatibility */
9744 int original_move_delay_value = player->move_delay_value;
9747 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
9751 /* scroll remaining steps with finest movement resolution */
9752 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
9754 while (player->MovPos)
9756 ScrollPlayer(player, SCROLL_GO_ON);
9757 ScrollScreen(NULL, SCROLL_GO_ON);
9759 AdvanceFrameAndPlayerCounters(player->index_nr);
9765 player->move_delay_value = original_move_delay_value;
9768 if (player->last_move_dir & MV_HORIZONTAL)
9770 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
9771 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
9775 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
9776 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
9782 if (moved & MP_MOVING && !ScreenMovPos &&
9783 (player == local_player || !options.network))
9785 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
9786 int offset = (setup.scroll_delay ? 3 : 0);
9788 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
9790 /* actual player has left the screen -- scroll in that direction */
9791 if (jx != old_jx) /* player has moved horizontally */
9792 scroll_x += (jx - old_jx);
9793 else /* player has moved vertically */
9794 scroll_y += (jy - old_jy);
9798 if (jx != old_jx) /* player has moved horizontally */
9800 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
9801 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
9802 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
9804 /* don't scroll over playfield boundaries */
9805 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
9806 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
9808 /* don't scroll more than one field at a time */
9809 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
9811 /* don't scroll against the player's moving direction */
9812 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
9813 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
9814 scroll_x = old_scroll_x;
9816 else /* player has moved vertically */
9818 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
9819 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
9820 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
9822 /* don't scroll over playfield boundaries */
9823 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
9824 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
9826 /* don't scroll more than one field at a time */
9827 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
9829 /* don't scroll against the player's moving direction */
9830 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
9831 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
9832 scroll_y = old_scroll_y;
9836 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
9838 if (!options.network && !AllPlayersInVisibleScreen())
9840 scroll_x = old_scroll_x;
9841 scroll_y = old_scroll_y;
9845 ScrollScreen(player, SCROLL_INIT);
9846 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
9851 player->StepFrame = 0;
9853 if (moved & MP_MOVING)
9855 if (old_jx != jx && old_jy == jy)
9856 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
9857 else if (old_jx == jx && old_jy != jy)
9858 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
9860 DrawLevelField(jx, jy); /* for "crumbled sand" */
9862 player->last_move_dir = player->MovDir;
9863 player->is_moving = TRUE;
9864 player->is_snapping = FALSE;
9865 player->is_switching = FALSE;
9866 player->is_dropping = FALSE;
9870 CheckGravityMovementWhenNotMoving(player);
9872 player->is_moving = FALSE;
9874 /* at this point, the player is allowed to move, but cannot move right now
9875 (e.g. because of something blocking the way) -- ensure that the player
9876 is also allowed to move in the next frame (in old versions before 3.1.1,
9877 the player was forced to wait again for eight frames before next try) */
9879 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
9880 player->move_delay = 0; /* allow direct movement in the next frame */
9883 if (player->move_delay == -1) /* not yet initialized by DigField() */
9884 player->move_delay = player->move_delay_value;
9886 if (game.engine_version < VERSION_IDENT(3,0,7,0))
9888 TestIfPlayerTouchesBadThing(jx, jy);
9889 TestIfPlayerTouchesCustomElement(jx, jy);
9892 if (!player->active)
9893 RemovePlayer(player);
9898 void ScrollPlayer(struct PlayerInfo *player, int mode)
9900 int jx = player->jx, jy = player->jy;
9901 int last_jx = player->last_jx, last_jy = player->last_jy;
9902 int move_stepsize = TILEX / player->move_delay_value;
9904 #if USE_NEW_PLAYER_SPEED
9905 if (!player->active)
9908 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
9911 if (!player->active || player->MovPos == 0)
9915 if (mode == SCROLL_INIT)
9917 player->actual_frame_counter = FrameCounter;
9918 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
9920 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
9921 Feld[last_jx][last_jy] == EL_EMPTY)
9923 int last_field_block_delay = 0; /* start with no blocking at all */
9924 int block_delay_adjustment = player->block_delay_adjustment;
9926 /* if player blocks last field, add delay for exactly one move */
9927 if (player->block_last_field)
9929 last_field_block_delay += player->move_delay_value;
9931 /* when blocking enabled, prevent moving up despite gravity */
9932 if (game.gravity && player->MovDir == MV_UP)
9933 block_delay_adjustment = -1;
9936 /* add block delay adjustment (also possible when not blocking) */
9937 last_field_block_delay += block_delay_adjustment;
9939 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
9940 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
9943 #if USE_NEW_PLAYER_SPEED
9944 if (player->MovPos != 0) /* player has not yet reached destination */
9950 else if (!FrameReached(&player->actual_frame_counter, 1))
9954 printf("::: player->MovPos: %d -> %d\n",
9956 player->MovPos + (player->MovPos > 0 ? -1 : 1) * move_stepsize);
9959 #if USE_NEW_PLAYER_SPEED
9960 if (player->MovPos != 0)
9962 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
9963 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
9965 /* before DrawPlayer() to draw correct player graphic for this case */
9966 if (player->MovPos == 0)
9967 CheckGravityMovement(player);
9970 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
9971 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
9973 /* before DrawPlayer() to draw correct player graphic for this case */
9974 if (player->MovPos == 0)
9975 CheckGravityMovement(player);
9978 if (player->MovPos == 0) /* player reached destination field */
9981 printf("::: player reached destination field\n");
9984 if (player->move_delay_reset_counter > 0)
9986 player->move_delay_reset_counter--;
9988 if (player->move_delay_reset_counter == 0)
9990 /* continue with normal speed after quickly moving through gate */
9991 HALVE_PLAYER_SPEED(player);
9993 /* be able to make the next move without delay */
9994 player->move_delay = 0;
9998 player->last_jx = jx;
9999 player->last_jy = jy;
10001 if (Feld[jx][jy] == EL_EXIT_OPEN ||
10002 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
10003 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
10005 DrawPlayer(player); /* needed here only to cleanup last field */
10006 RemovePlayer(player);
10008 if (local_player->friends_still_needed == 0 ||
10009 IS_SP_ELEMENT(Feld[jx][jy]))
10010 player->LevelSolved = player->GameOver = TRUE;
10013 /* this breaks one level: "machine", level 000 */
10015 int move_direction = player->MovDir;
10016 int enter_side = MV_DIR_OPPOSITE(move_direction);
10017 int leave_side = move_direction;
10018 int old_jx = last_jx;
10019 int old_jy = last_jy;
10020 int old_element = Feld[old_jx][old_jy];
10021 int new_element = Feld[jx][jy];
10023 if (IS_CUSTOM_ELEMENT(old_element))
10024 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
10026 player->index_bit, leave_side);
10028 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
10029 CE_PLAYER_LEAVES_X,
10030 player->index_bit, leave_side);
10032 if (IS_CUSTOM_ELEMENT(new_element))
10033 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
10034 player->index_bit, enter_side);
10036 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
10037 CE_PLAYER_ENTERS_X,
10038 player->index_bit, enter_side);
10040 CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
10041 CE_MOVE_OF_X, move_direction);
10044 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10046 TestIfPlayerTouchesBadThing(jx, jy);
10047 TestIfPlayerTouchesCustomElement(jx, jy);
10049 /* needed because pushed element has not yet reached its destination,
10050 so it would trigger a change event at its previous field location */
10051 if (!player->is_pushing)
10052 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
10054 if (!player->active)
10055 RemovePlayer(player);
10058 if (level.use_step_counter)
10068 if (TimeLeft <= 10 && setup.time_limit)
10069 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
10071 DrawGameValue_Time(TimeLeft);
10073 if (!TimeLeft && setup.time_limit)
10074 for (i = 0; i < MAX_PLAYERS; i++)
10075 KillPlayer(&stored_player[i]);
10077 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
10078 DrawGameValue_Time(TimePlayed);
10081 if (tape.single_step && tape.recording && !tape.pausing &&
10082 !player->programmed_action)
10083 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10087 void ScrollScreen(struct PlayerInfo *player, int mode)
10089 static unsigned long screen_frame_counter = 0;
10091 if (mode == SCROLL_INIT)
10093 /* set scrolling step size according to actual player's moving speed */
10094 ScrollStepSize = TILEX / player->move_delay_value;
10096 screen_frame_counter = FrameCounter;
10097 ScreenMovDir = player->MovDir;
10098 ScreenMovPos = player->MovPos;
10099 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
10102 else if (!FrameReached(&screen_frame_counter, 1))
10107 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
10108 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
10109 redraw_mask |= REDRAW_FIELD;
10112 ScreenMovDir = MV_NONE;
10115 void TestIfPlayerTouchesCustomElement(int x, int y)
10117 static int xy[4][2] =
10124 static int trigger_sides[4][2] =
10126 /* center side border side */
10127 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
10128 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
10129 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
10130 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
10132 static int touch_dir[4] =
10134 MV_LEFT | MV_RIGHT,
10139 int center_element = Feld[x][y]; /* should always be non-moving! */
10142 for (i = 0; i < NUM_DIRECTIONS; i++)
10144 int xx = x + xy[i][0];
10145 int yy = y + xy[i][1];
10146 int center_side = trigger_sides[i][0];
10147 int border_side = trigger_sides[i][1];
10148 int border_element;
10150 if (!IN_LEV_FIELD(xx, yy))
10153 if (IS_PLAYER(x, y))
10155 struct PlayerInfo *player = PLAYERINFO(x, y);
10157 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10158 border_element = Feld[xx][yy]; /* may be moving! */
10159 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
10160 border_element = Feld[xx][yy];
10161 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
10162 border_element = MovingOrBlocked2Element(xx, yy);
10164 continue; /* center and border element do not touch */
10166 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
10167 player->index_bit, border_side);
10168 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
10169 CE_PLAYER_TOUCHES_X,
10170 player->index_bit, border_side);
10172 else if (IS_PLAYER(xx, yy))
10174 struct PlayerInfo *player = PLAYERINFO(xx, yy);
10176 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10178 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
10179 continue; /* center and border element do not touch */
10182 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
10183 player->index_bit, center_side);
10184 CheckTriggeredElementChangeByPlayer(x, y, center_element,
10185 CE_PLAYER_TOUCHES_X,
10186 player->index_bit, center_side);
10192 #if USE_ELEMENT_TOUCHING_BUGFIX
10194 void TestIfElementTouchesCustomElement(int x, int y)
10196 static int xy[4][2] =
10203 static int trigger_sides[4][2] =
10205 /* center side border side */
10206 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
10207 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
10208 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
10209 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
10211 static int touch_dir[4] =
10213 MV_LEFT | MV_RIGHT,
10218 boolean change_center_element = FALSE;
10219 int center_element = Feld[x][y]; /* should always be non-moving! */
10220 int border_element_old[NUM_DIRECTIONS];
10223 for (i = 0; i < NUM_DIRECTIONS; i++)
10225 int xx = x + xy[i][0];
10226 int yy = y + xy[i][1];
10227 int border_element;
10229 border_element_old[i] = -1;
10231 if (!IN_LEV_FIELD(xx, yy))
10234 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10235 border_element = Feld[xx][yy]; /* may be moving! */
10236 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
10237 border_element = Feld[xx][yy];
10238 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
10239 border_element = MovingOrBlocked2Element(xx, yy);
10241 continue; /* center and border element do not touch */
10243 border_element_old[i] = border_element;
10246 for (i = 0; i < NUM_DIRECTIONS; i++)
10248 int xx = x + xy[i][0];
10249 int yy = y + xy[i][1];
10250 int center_side = trigger_sides[i][0];
10251 int border_element = border_element_old[i];
10253 if (border_element == -1)
10256 /* check for change of border element */
10257 CheckElementChangeBySide(xx, yy, border_element, center_element,
10258 CE_TOUCHING_X, center_side);
10261 for (i = 0; i < NUM_DIRECTIONS; i++)
10263 int border_side = trigger_sides[i][1];
10264 int border_element = border_element_old[i];
10266 if (border_element == -1)
10269 /* check for change of center element (but change it only once) */
10270 if (!change_center_element)
10271 change_center_element =
10272 CheckElementChangeBySide(x, y, center_element, border_element,
10273 CE_TOUCHING_X, border_side);
10279 void TestIfElementTouchesCustomElement_OLD(int x, int y)
10281 static int xy[4][2] =
10288 static int trigger_sides[4][2] =
10290 /* center side border side */
10291 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
10292 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
10293 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
10294 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
10296 static int touch_dir[4] =
10298 MV_LEFT | MV_RIGHT,
10303 boolean change_center_element = FALSE;
10304 int center_element = Feld[x][y]; /* should always be non-moving! */
10307 for (i = 0; i < NUM_DIRECTIONS; i++)
10309 int xx = x + xy[i][0];
10310 int yy = y + xy[i][1];
10311 int center_side = trigger_sides[i][0];
10312 int border_side = trigger_sides[i][1];
10313 int border_element;
10315 if (!IN_LEV_FIELD(xx, yy))
10318 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10319 border_element = Feld[xx][yy]; /* may be moving! */
10320 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
10321 border_element = Feld[xx][yy];
10322 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
10323 border_element = MovingOrBlocked2Element(xx, yy);
10325 continue; /* center and border element do not touch */
10327 /* check for change of center element (but change it only once) */
10328 if (!change_center_element)
10329 change_center_element =
10330 CheckElementChangeBySide(x, y, center_element, border_element,
10331 CE_TOUCHING_X, border_side);
10333 /* check for change of border element */
10334 CheckElementChangeBySide(xx, yy, border_element, center_element,
10335 CE_TOUCHING_X, center_side);
10341 void TestIfElementHitsCustomElement(int x, int y, int direction)
10343 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
10344 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
10345 int hitx = x + dx, hity = y + dy;
10346 int hitting_element = Feld[x][y];
10347 int touched_element;
10349 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
10352 touched_element = (IN_LEV_FIELD(hitx, hity) ?
10353 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
10355 if (IN_LEV_FIELD(hitx, hity))
10357 int opposite_direction = MV_DIR_OPPOSITE(direction);
10358 int hitting_side = direction;
10359 int touched_side = opposite_direction;
10360 boolean object_hit = (!IS_MOVING(hitx, hity) ||
10361 MovDir[hitx][hity] != direction ||
10362 ABS(MovPos[hitx][hity]) <= TILEY / 2);
10368 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10369 CE_HITTING_X, touched_side);
10371 CheckElementChangeBySide(hitx, hity, touched_element,
10372 hitting_element, CE_HIT_BY_X, hitting_side);
10374 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10375 CE_HIT_BY_SOMETHING, opposite_direction);
10379 /* "hitting something" is also true when hitting the playfield border */
10380 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10381 CE_HITTING_SOMETHING, direction);
10385 void TestIfElementSmashesCustomElement(int x, int y, int direction)
10387 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
10388 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
10389 int hitx = x + dx, hity = y + dy;
10390 int hitting_element = Feld[x][y];
10391 int touched_element;
10393 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
10394 !IS_FREE(hitx, hity) &&
10395 (!IS_MOVING(hitx, hity) ||
10396 MovDir[hitx][hity] != direction ||
10397 ABS(MovPos[hitx][hity]) <= TILEY / 2));
10400 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
10404 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
10408 touched_element = (IN_LEV_FIELD(hitx, hity) ?
10409 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
10411 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10412 EP_CAN_SMASH_EVERYTHING, direction);
10414 if (IN_LEV_FIELD(hitx, hity))
10416 int opposite_direction = MV_DIR_OPPOSITE(direction);
10417 int hitting_side = direction;
10418 int touched_side = opposite_direction;
10420 int touched_element = MovingOrBlocked2Element(hitx, hity);
10423 boolean object_hit = (!IS_MOVING(hitx, hity) ||
10424 MovDir[hitx][hity] != direction ||
10425 ABS(MovPos[hitx][hity]) <= TILEY / 2);
10434 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10435 CE_SMASHED_BY_SOMETHING, opposite_direction);
10437 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10438 CE_OTHER_IS_SMASHING, touched_side);
10440 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10441 CE_OTHER_GETS_SMASHED, hitting_side);
10447 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
10449 int i, kill_x = -1, kill_y = -1;
10451 int bad_element = -1;
10452 static int test_xy[4][2] =
10459 static int test_dir[4] =
10467 for (i = 0; i < NUM_DIRECTIONS; i++)
10469 int test_x, test_y, test_move_dir, test_element;
10471 test_x = good_x + test_xy[i][0];
10472 test_y = good_y + test_xy[i][1];
10474 if (!IN_LEV_FIELD(test_x, test_y))
10478 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
10480 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
10482 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
10483 2nd case: DONT_TOUCH style bad thing does not move away from good thing
10485 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
10486 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
10490 bad_element = test_element;
10496 if (kill_x != -1 || kill_y != -1)
10498 if (IS_PLAYER(good_x, good_y))
10500 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
10502 if (player->shield_deadly_time_left > 0 &&
10503 !IS_INDESTRUCTIBLE(bad_element))
10504 Bang(kill_x, kill_y);
10505 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
10506 KillPlayer(player);
10509 Bang(good_x, good_y);
10513 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
10515 int i, kill_x = -1, kill_y = -1;
10516 int bad_element = Feld[bad_x][bad_y];
10517 static int test_xy[4][2] =
10524 static int touch_dir[4] =
10526 MV_LEFT | MV_RIGHT,
10531 static int test_dir[4] =
10539 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
10542 for (i = 0; i < NUM_DIRECTIONS; i++)
10544 int test_x, test_y, test_move_dir, test_element;
10546 test_x = bad_x + test_xy[i][0];
10547 test_y = bad_y + test_xy[i][1];
10548 if (!IN_LEV_FIELD(test_x, test_y))
10552 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
10554 test_element = Feld[test_x][test_y];
10556 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
10557 2nd case: DONT_TOUCH style bad thing does not move away from good thing
10559 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
10560 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
10562 /* good thing is player or penguin that does not move away */
10563 if (IS_PLAYER(test_x, test_y))
10565 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
10567 if (bad_element == EL_ROBOT && player->is_moving)
10568 continue; /* robot does not kill player if he is moving */
10570 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10572 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
10573 continue; /* center and border element do not touch */
10580 else if (test_element == EL_PENGUIN)
10589 if (kill_x != -1 || kill_y != -1)
10591 if (IS_PLAYER(kill_x, kill_y))
10593 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
10595 if (player->shield_deadly_time_left > 0 &&
10596 !IS_INDESTRUCTIBLE(bad_element))
10597 Bang(bad_x, bad_y);
10598 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
10599 KillPlayer(player);
10602 Bang(kill_x, kill_y);
10606 void TestIfPlayerTouchesBadThing(int x, int y)
10608 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
10611 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
10613 TestIfGoodThingHitsBadThing(x, y, move_dir);
10616 void TestIfBadThingTouchesPlayer(int x, int y)
10618 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
10621 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
10623 TestIfBadThingHitsGoodThing(x, y, move_dir);
10626 void TestIfFriendTouchesBadThing(int x, int y)
10628 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
10631 void TestIfBadThingTouchesFriend(int x, int y)
10633 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
10636 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
10638 int i, kill_x = bad_x, kill_y = bad_y;
10639 static int xy[4][2] =
10647 for (i = 0; i < NUM_DIRECTIONS; i++)
10651 x = bad_x + xy[i][0];
10652 y = bad_y + xy[i][1];
10653 if (!IN_LEV_FIELD(x, y))
10656 element = Feld[x][y];
10657 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
10658 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
10666 if (kill_x != bad_x || kill_y != bad_y)
10667 Bang(bad_x, bad_y);
10670 void KillPlayer(struct PlayerInfo *player)
10672 int jx = player->jx, jy = player->jy;
10674 if (!player->active)
10677 /* remove accessible field at the player's position */
10678 Feld[jx][jy] = EL_EMPTY;
10680 /* deactivate shield (else Bang()/Explode() would not work right) */
10681 player->shield_normal_time_left = 0;
10682 player->shield_deadly_time_left = 0;
10685 BuryPlayer(player);
10688 static void KillPlayerUnlessEnemyProtected(int x, int y)
10690 if (!PLAYER_ENEMY_PROTECTED(x, y))
10691 KillPlayer(PLAYERINFO(x, y));
10694 static void KillPlayerUnlessExplosionProtected(int x, int y)
10696 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
10697 KillPlayer(PLAYERINFO(x, y));
10700 void BuryPlayer(struct PlayerInfo *player)
10702 int jx = player->jx, jy = player->jy;
10704 if (!player->active)
10707 PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
10708 PlayLevelSound(jx, jy, SND_GAME_LOSING);
10710 player->GameOver = TRUE;
10711 RemovePlayer(player);
10714 void RemovePlayer(struct PlayerInfo *player)
10716 int jx = player->jx, jy = player->jy;
10717 int i, found = FALSE;
10719 player->present = FALSE;
10720 player->active = FALSE;
10722 if (!ExplodeField[jx][jy])
10723 StorePlayer[jx][jy] = 0;
10725 if (player->is_moving)
10726 DrawLevelField(player->last_jx, player->last_jy);
10728 for (i = 0; i < MAX_PLAYERS; i++)
10729 if (stored_player[i].active)
10733 AllPlayersGone = TRUE;
10739 #if USE_NEW_SNAP_DELAY
10740 static void setFieldForSnapping(int x, int y, int element, int direction)
10742 struct ElementInfo *ei = &element_info[element];
10743 int direction_bit = MV_DIR_TO_BIT(direction);
10744 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
10745 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
10746 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
10748 Feld[x][y] = EL_ELEMENT_SNAPPING;
10749 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
10751 ResetGfxAnimation(x, y);
10753 GfxElement[x][y] = element;
10754 GfxAction[x][y] = action;
10755 GfxDir[x][y] = direction;
10756 GfxFrame[x][y] = -1;
10761 =============================================================================
10762 checkDiagonalPushing()
10763 -----------------------------------------------------------------------------
10764 check if diagonal input device direction results in pushing of object
10765 (by checking if the alternative direction is walkable, diggable, ...)
10766 =============================================================================
10769 static boolean checkDiagonalPushing(struct PlayerInfo *player,
10770 int x, int y, int real_dx, int real_dy)
10772 int jx, jy, dx, dy, xx, yy;
10774 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
10777 /* diagonal direction: check alternative direction */
10782 xx = jx + (dx == 0 ? real_dx : 0);
10783 yy = jy + (dy == 0 ? real_dy : 0);
10785 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
10789 =============================================================================
10791 -----------------------------------------------------------------------------
10792 x, y: field next to player (non-diagonal) to try to dig to
10793 real_dx, real_dy: direction as read from input device (can be diagonal)
10794 =============================================================================
10797 int DigField(struct PlayerInfo *player,
10798 int oldx, int oldy, int x, int y,
10799 int real_dx, int real_dy, int mode)
10801 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
10802 boolean player_was_pushing = player->is_pushing;
10803 boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
10804 boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
10805 int jx = oldx, jy = oldy;
10806 int dx = x - jx, dy = y - jy;
10807 int nextx = x + dx, nexty = y + dy;
10808 int move_direction = (dx == -1 ? MV_LEFT :
10809 dx == +1 ? MV_RIGHT :
10811 dy == +1 ? MV_DOWN : MV_NONE);
10812 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
10813 int dig_side = MV_DIR_OPPOSITE(move_direction);
10814 int old_element = Feld[jx][jy];
10815 #if USE_FIXED_DONT_RUN_INTO
10816 int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
10822 if (is_player) /* function can also be called by EL_PENGUIN */
10824 if (player->MovPos == 0)
10826 player->is_digging = FALSE;
10827 player->is_collecting = FALSE;
10830 if (player->MovPos == 0) /* last pushing move finished */
10831 player->is_pushing = FALSE;
10833 if (mode == DF_NO_PUSH) /* player just stopped pushing */
10835 player->is_switching = FALSE;
10836 player->push_delay = -1;
10838 return MP_NO_ACTION;
10842 #if !USE_FIXED_DONT_RUN_INTO
10843 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
10844 return MP_NO_ACTION;
10847 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
10848 old_element = Back[jx][jy];
10850 /* in case of element dropped at player position, check background */
10851 else if (Back[jx][jy] != EL_EMPTY &&
10852 game.engine_version >= VERSION_IDENT(2,2,0,0))
10853 old_element = Back[jx][jy];
10856 #if USE_FIXED_DONT_RUN_INTO
10857 if (player_can_move && DONT_RUN_INTO(element))
10859 if (element == EL_ACID && dx == 0 && dy == 1)
10862 Feld[jx][jy] = EL_PLAYER_1;
10863 InitMovingField(jx, jy, MV_DOWN);
10864 Store[jx][jy] = EL_ACID;
10865 ContinueMoving(jx, jy);
10866 BuryPlayer(player);
10869 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
10871 return MP_DONT_RUN_INTO;
10877 #if USE_FIXED_DONT_RUN_INTO
10878 if (player_can_move && DONT_RUN_INTO(element))
10880 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
10882 return MP_DONT_RUN_INTO;
10887 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
10888 return MP_NO_ACTION; /* field has no opening in this direction */
10890 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
10891 return MP_NO_ACTION; /* field has no opening in this direction */
10894 #if USE_FIXED_DONT_RUN_INTO
10895 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
10898 Feld[jx][jy] = EL_PLAYER_1;
10899 InitMovingField(jx, jy, MV_DOWN);
10900 Store[jx][jy] = EL_ACID;
10901 ContinueMoving(jx, jy);
10902 BuryPlayer(player);
10904 return MP_DONT_RUN_INTO;
10910 #if USE_FIXED_DONT_RUN_INTO
10911 if (player_can_move && DONT_RUN_INTO(element))
10913 if (element == EL_ACID && dx == 0 && dy == 1)
10916 Feld[jx][jy] = EL_PLAYER_1;
10917 InitMovingField(jx, jy, MV_DOWN);
10918 Store[jx][jy] = EL_ACID;
10919 ContinueMoving(jx, jy);
10920 BuryPlayer(player);
10923 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
10925 return MP_DONT_RUN_INTO;
10930 #if USE_FIXED_DONT_RUN_INTO
10931 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
10932 return MP_NO_ACTION;
10935 #if !USE_FIXED_DONT_RUN_INTO
10936 element = Feld[x][y];
10939 collect_count = element_info[element].collect_count_initial;
10941 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
10942 return MP_NO_ACTION;
10944 if (game.engine_version < VERSION_IDENT(2,2,0,0))
10945 player_can_move = player_can_move_or_snap;
10947 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
10948 game.engine_version >= VERSION_IDENT(2,2,0,0))
10950 CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
10951 player->index_bit, dig_side);
10952 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
10953 player->index_bit, dig_side);
10955 if (Feld[x][y] != element) /* field changed by snapping */
10958 return MP_NO_ACTION;
10961 if (game.gravity && is_player && !player->is_auto_moving &&
10962 canFallDown(player) && move_direction != MV_DOWN &&
10963 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
10964 return MP_NO_ACTION; /* player cannot walk here due to gravity */
10966 if (player_can_move &&
10967 IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
10969 int sound_element = SND_ELEMENT(element);
10970 int sound_action = ACTION_WALKING;
10972 if (IS_RND_GATE(element))
10974 if (!player->key[RND_GATE_NR(element)])
10975 return MP_NO_ACTION;
10977 else if (IS_RND_GATE_GRAY(element))
10979 if (!player->key[RND_GATE_GRAY_NR(element)])
10980 return MP_NO_ACTION;
10982 else if (IS_RND_GATE_GRAY_ACTIVE(element))
10984 if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
10985 return MP_NO_ACTION;
10987 else if (element == EL_EXIT_OPEN ||
10988 element == EL_SP_EXIT_OPEN ||
10989 element == EL_SP_EXIT_OPENING)
10991 sound_action = ACTION_PASSING; /* player is passing exit */
10993 else if (element == EL_EMPTY)
10995 sound_action = ACTION_MOVING; /* nothing to walk on */
10998 /* play sound from background or player, whatever is available */
10999 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
11000 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
11002 PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
11004 else if (player_can_move &&
11005 IS_PASSABLE(element) && canPassField(x, y, move_direction))
11007 if (!ACCESS_FROM(element, opposite_direction))
11008 return MP_NO_ACTION; /* field not accessible from this direction */
11010 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
11011 return MP_NO_ACTION;
11013 if (IS_EM_GATE(element))
11015 if (!player->key[EM_GATE_NR(element)])
11016 return MP_NO_ACTION;
11018 else if (IS_EM_GATE_GRAY(element))
11020 if (!player->key[EM_GATE_GRAY_NR(element)])
11021 return MP_NO_ACTION;
11023 else if (IS_EM_GATE_GRAY_ACTIVE(element))
11025 if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
11026 return MP_NO_ACTION;
11028 else if (IS_SP_PORT(element))
11030 if (element == EL_SP_GRAVITY_PORT_LEFT ||
11031 element == EL_SP_GRAVITY_PORT_RIGHT ||
11032 element == EL_SP_GRAVITY_PORT_UP ||
11033 element == EL_SP_GRAVITY_PORT_DOWN)
11034 game.gravity = !game.gravity;
11035 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
11036 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
11037 element == EL_SP_GRAVITY_ON_PORT_UP ||
11038 element == EL_SP_GRAVITY_ON_PORT_DOWN)
11039 game.gravity = TRUE;
11040 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
11041 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
11042 element == EL_SP_GRAVITY_OFF_PORT_UP ||
11043 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
11044 game.gravity = FALSE;
11047 /* automatically move to the next field with double speed */
11048 player->programmed_action = move_direction;
11050 if (player->move_delay_reset_counter == 0)
11052 player->move_delay_reset_counter = 2; /* two double speed steps */
11054 DOUBLE_PLAYER_SPEED(player);
11057 PlayLevelSoundAction(x, y, ACTION_PASSING);
11059 else if (player_can_move_or_snap && IS_DIGGABLE(element))
11063 if (mode != DF_SNAP)
11065 GfxElement[x][y] = GFX_ELEMENT(element);
11066 player->is_digging = TRUE;
11069 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
11071 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
11072 player->index_bit, dig_side);
11074 if (mode == DF_SNAP)
11076 #if USE_NEW_SNAP_DELAY
11077 if (level.block_snap_field)
11078 setFieldForSnapping(x, y, element, move_direction);
11080 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11082 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11085 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
11086 player->index_bit, dig_side);
11089 else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
11093 if (is_player && mode != DF_SNAP)
11095 GfxElement[x][y] = element;
11096 player->is_collecting = TRUE;
11099 if (element == EL_SPEED_PILL)
11101 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
11103 else if (element == EL_EXTRA_TIME && level.time > 0)
11105 TimeLeft += level.extra_time;
11106 DrawGameValue_Time(TimeLeft);
11108 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
11110 player->shield_normal_time_left += level.shield_normal_time;
11111 if (element == EL_SHIELD_DEADLY)
11112 player->shield_deadly_time_left += level.shield_deadly_time;
11114 else if (element == EL_DYNAMITE ||
11115 element == EL_EM_DYNAMITE ||
11116 element == EL_SP_DISK_RED)
11118 if (player->inventory_size < MAX_INVENTORY_SIZE)
11119 player->inventory_element[player->inventory_size++] = element;
11121 DrawGameValue_Dynamite(local_player->inventory_size);
11123 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
11125 player->dynabomb_count++;
11126 player->dynabombs_left++;
11128 else if (element == EL_DYNABOMB_INCREASE_SIZE)
11130 player->dynabomb_size++;
11132 else if (element == EL_DYNABOMB_INCREASE_POWER)
11134 player->dynabomb_xl = TRUE;
11136 else if (IS_KEY(element))
11138 player->key[KEY_NR(element)] = TRUE;
11140 DrawGameValue_Keys(player->key);
11142 redraw_mask |= REDRAW_DOOR_1;
11144 else if (IS_ENVELOPE(element))
11146 player->show_envelope = element;
11148 else if (element == EL_EMC_LENSES)
11150 game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
11152 RedrawAllInvisibleElementsForLenses();
11154 else if (element == EL_EMC_MAGNIFIER)
11156 game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
11158 RedrawAllInvisibleElementsForMagnifier();
11160 else if (IS_DROPPABLE(element) ||
11161 IS_THROWABLE(element)) /* can be collected and dropped */
11165 if (collect_count == 0)
11166 player->inventory_infinite_element = element;
11168 for (i = 0; i < collect_count; i++)
11169 if (player->inventory_size < MAX_INVENTORY_SIZE)
11170 player->inventory_element[player->inventory_size++] = element;
11172 DrawGameValue_Dynamite(local_player->inventory_size);
11174 else if (collect_count > 0)
11176 local_player->gems_still_needed -= collect_count;
11177 if (local_player->gems_still_needed < 0)
11178 local_player->gems_still_needed = 0;
11180 DrawGameValue_Emeralds(local_player->gems_still_needed);
11183 RaiseScoreElement(element);
11184 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
11187 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
11188 player->index_bit, dig_side);
11190 if (mode == DF_SNAP)
11192 #if USE_NEW_SNAP_DELAY
11193 if (level.block_snap_field)
11194 setFieldForSnapping(x, y, element, move_direction);
11196 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11198 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11201 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
11202 player->index_bit, dig_side);
11205 else if (player_can_move_or_snap && IS_PUSHABLE(element))
11207 if (mode == DF_SNAP && element != EL_BD_ROCK)
11208 return MP_NO_ACTION;
11210 if (CAN_FALL(element) && dy)
11211 return MP_NO_ACTION;
11213 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
11214 !(element == EL_SPRING && level.use_spring_bug))
11215 return MP_NO_ACTION;
11217 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
11218 ((move_direction & MV_VERTICAL &&
11219 ((element_info[element].move_pattern & MV_LEFT &&
11220 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
11221 (element_info[element].move_pattern & MV_RIGHT &&
11222 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
11223 (move_direction & MV_HORIZONTAL &&
11224 ((element_info[element].move_pattern & MV_UP &&
11225 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
11226 (element_info[element].move_pattern & MV_DOWN &&
11227 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
11228 return MP_NO_ACTION;
11230 /* do not push elements already moving away faster than player */
11231 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
11232 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
11233 return MP_NO_ACTION;
11235 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
11237 if (player->push_delay_value == -1 || !player_was_pushing)
11238 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11240 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11242 if (player->push_delay_value == -1)
11243 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11245 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
11247 if (!player->is_pushing)
11248 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11251 player->is_pushing = TRUE;
11253 if (!(IN_LEV_FIELD(nextx, nexty) &&
11254 (IS_FREE(nextx, nexty) ||
11255 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
11256 IS_SB_ELEMENT(element)))))
11257 return MP_NO_ACTION;
11259 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
11260 return MP_NO_ACTION;
11262 if (player->push_delay == -1) /* new pushing; restart delay */
11263 player->push_delay = 0;
11265 if (player->push_delay < player->push_delay_value &&
11266 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
11267 element != EL_SPRING && element != EL_BALLOON)
11269 /* make sure that there is no move delay before next try to push */
11270 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11271 player->move_delay = 0;
11273 return MP_NO_ACTION;
11276 if (IS_SB_ELEMENT(element))
11278 if (element == EL_SOKOBAN_FIELD_FULL)
11280 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
11281 local_player->sokobanfields_still_needed++;
11284 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
11286 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
11287 local_player->sokobanfields_still_needed--;
11290 Feld[x][y] = EL_SOKOBAN_OBJECT;
11292 if (Back[x][y] == Back[nextx][nexty])
11293 PlayLevelSoundAction(x, y, ACTION_PUSHING);
11294 else if (Back[x][y] != 0)
11295 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
11298 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
11301 if (local_player->sokobanfields_still_needed == 0 &&
11302 game.emulation == EMU_SOKOBAN)
11304 player->LevelSolved = player->GameOver = TRUE;
11305 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
11309 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
11311 InitMovingField(x, y, move_direction);
11312 GfxAction[x][y] = ACTION_PUSHING;
11314 if (mode == DF_SNAP)
11315 ContinueMoving(x, y);
11317 MovPos[x][y] = (dx != 0 ? dx : dy);
11319 Pushed[x][y] = TRUE;
11320 Pushed[nextx][nexty] = TRUE;
11322 if (game.engine_version < VERSION_IDENT(2,2,0,7))
11323 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11325 player->push_delay_value = -1; /* get new value later */
11327 /* check for element change _after_ element has been pushed */
11328 if (game.use_change_when_pushing_bug)
11330 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
11331 player->index_bit, dig_side);
11332 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
11333 player->index_bit, dig_side);
11336 else if (IS_SWITCHABLE(element))
11338 if (PLAYER_SWITCHING(player, x, y))
11340 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
11341 player->index_bit, dig_side);
11346 player->is_switching = TRUE;
11347 player->switch_x = x;
11348 player->switch_y = y;
11350 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
11352 if (element == EL_ROBOT_WHEEL)
11354 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
11358 DrawLevelField(x, y);
11360 else if (element == EL_SP_TERMINAL)
11365 SCAN_PLAYFIELD(xx, yy)
11367 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
11370 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
11372 else if (Feld[xx][yy] == EL_SP_TERMINAL)
11373 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
11376 else if (IS_BELT_SWITCH(element))
11378 ToggleBeltSwitch(x, y);
11380 else if (element == EL_SWITCHGATE_SWITCH_UP ||
11381 element == EL_SWITCHGATE_SWITCH_DOWN)
11383 ToggleSwitchgateSwitch(x, y);
11385 else if (element == EL_LIGHT_SWITCH ||
11386 element == EL_LIGHT_SWITCH_ACTIVE)
11388 ToggleLightSwitch(x, y);
11390 else if (element == EL_TIMEGATE_SWITCH)
11392 ActivateTimegateSwitch(x, y);
11394 else if (element == EL_BALLOON_SWITCH_LEFT ||
11395 element == EL_BALLOON_SWITCH_RIGHT ||
11396 element == EL_BALLOON_SWITCH_UP ||
11397 element == EL_BALLOON_SWITCH_DOWN ||
11398 element == EL_BALLOON_SWITCH_NONE ||
11399 element == EL_BALLOON_SWITCH_ANY)
11401 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
11402 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
11403 element == EL_BALLOON_SWITCH_UP ? MV_UP :
11404 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
11405 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
11408 else if (element == EL_LAMP)
11410 Feld[x][y] = EL_LAMP_ACTIVE;
11411 local_player->lights_still_needed--;
11413 ResetGfxAnimation(x, y);
11414 DrawLevelField(x, y);
11416 else if (element == EL_TIME_ORB_FULL)
11418 Feld[x][y] = EL_TIME_ORB_EMPTY;
11420 if (level.time > 0 || level.use_time_orb_bug)
11422 TimeLeft += level.time_orb_time;
11423 DrawGameValue_Time(TimeLeft);
11426 ResetGfxAnimation(x, y);
11427 DrawLevelField(x, y);
11429 else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
11430 element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
11434 game.ball_state = !game.ball_state;
11437 SCAN_PLAYFIELD(xx, yy)
11439 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
11442 int e = Feld[xx][yy];
11444 if (game.ball_state)
11446 if (e == EL_EMC_MAGIC_BALL)
11447 CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
11448 else if (e == EL_EMC_MAGIC_BALL_SWITCH)
11449 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
11453 if (e == EL_EMC_MAGIC_BALL_ACTIVE)
11454 CreateField(xx, yy, EL_EMC_MAGIC_BALL);
11455 else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
11456 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
11461 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
11462 player->index_bit, dig_side);
11464 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
11465 player->index_bit, dig_side);
11467 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
11468 player->index_bit, dig_side);
11474 if (!PLAYER_SWITCHING(player, x, y))
11476 player->is_switching = TRUE;
11477 player->switch_x = x;
11478 player->switch_y = y;
11480 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
11481 player->index_bit, dig_side);
11482 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
11483 player->index_bit, dig_side);
11485 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
11486 player->index_bit, dig_side);
11487 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
11488 player->index_bit, dig_side);
11491 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
11492 player->index_bit, dig_side);
11493 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
11494 player->index_bit, dig_side);
11496 return MP_NO_ACTION;
11499 player->push_delay = -1;
11501 if (is_player) /* function can also be called by EL_PENGUIN */
11503 if (Feld[x][y] != element) /* really digged/collected something */
11504 player->is_collecting = !player->is_digging;
11510 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
11512 int jx = player->jx, jy = player->jy;
11513 int x = jx + dx, y = jy + dy;
11514 int snap_direction = (dx == -1 ? MV_LEFT :
11515 dx == +1 ? MV_RIGHT :
11517 dy == +1 ? MV_DOWN : MV_NONE);
11519 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
11522 if (!player->active || !IN_LEV_FIELD(x, y))
11530 if (player->MovPos == 0)
11531 player->is_pushing = FALSE;
11533 player->is_snapping = FALSE;
11535 if (player->MovPos == 0)
11537 player->is_moving = FALSE;
11538 player->is_digging = FALSE;
11539 player->is_collecting = FALSE;
11545 if (player->is_snapping)
11548 player->MovDir = snap_direction;
11550 if (player->MovPos == 0)
11552 player->is_moving = FALSE;
11553 player->is_digging = FALSE;
11554 player->is_collecting = FALSE;
11557 player->is_dropping = FALSE;
11559 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
11562 player->is_snapping = TRUE;
11564 if (player->MovPos == 0)
11566 player->is_moving = FALSE;
11567 player->is_digging = FALSE;
11568 player->is_collecting = FALSE;
11571 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
11572 DrawLevelField(player->last_jx, player->last_jy);
11574 DrawLevelField(x, y);
11579 boolean DropElement(struct PlayerInfo *player)
11581 int old_element, new_element;
11582 int dropx = player->jx, dropy = player->jy;
11583 int drop_direction = player->MovDir;
11584 int drop_side = drop_direction;
11585 int drop_element = (player->inventory_size > 0 ?
11586 player->inventory_element[player->inventory_size - 1] :
11587 player->inventory_infinite_element != EL_UNDEFINED ?
11588 player->inventory_infinite_element :
11589 player->dynabombs_left > 0 ?
11590 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
11593 /* do not drop an element on top of another element; when holding drop key
11594 pressed without moving, dropped element must move away before the next
11595 element can be dropped (this is especially important if the next element
11596 is dynamite, which can be placed on background for historical reasons) */
11597 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
11600 if (IS_THROWABLE(drop_element))
11602 dropx += GET_DX_FROM_DIR(drop_direction);
11603 dropy += GET_DY_FROM_DIR(drop_direction);
11605 if (!IN_LEV_FIELD(dropx, dropy))
11609 old_element = Feld[dropx][dropy]; /* old element at dropping position */
11610 new_element = drop_element; /* default: no change when dropping */
11612 /* check if player is active, not moving and ready to drop */
11613 if (!player->active || player->MovPos || player->drop_delay > 0)
11616 /* check if player has anything that can be dropped */
11617 if (new_element == EL_UNDEFINED)
11620 /* check if anything can be dropped at the current position */
11621 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
11624 /* collected custom elements can only be dropped on empty fields */
11625 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
11628 if (old_element != EL_EMPTY)
11629 Back[dropx][dropy] = old_element; /* store old element on this field */
11631 ResetGfxAnimation(dropx, dropy);
11632 ResetRandomAnimationValue(dropx, dropy);
11634 if (player->inventory_size > 0 ||
11635 player->inventory_infinite_element != EL_UNDEFINED)
11637 if (player->inventory_size > 0)
11639 player->inventory_size--;
11641 DrawGameValue_Dynamite(local_player->inventory_size);
11643 if (new_element == EL_DYNAMITE)
11644 new_element = EL_DYNAMITE_ACTIVE;
11645 else if (new_element == EL_EM_DYNAMITE)
11646 new_element = EL_EM_DYNAMITE_ACTIVE;
11647 else if (new_element == EL_SP_DISK_RED)
11648 new_element = EL_SP_DISK_RED_ACTIVE;
11651 Feld[dropx][dropy] = new_element;
11653 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
11654 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
11655 el2img(Feld[dropx][dropy]), 0);
11657 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
11659 /* needed if previous element just changed to "empty" in the last frame */
11660 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
11662 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
11663 player->index_bit, drop_side);
11664 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
11666 player->index_bit, drop_side);
11668 TestIfElementTouchesCustomElement(dropx, dropy);
11670 else /* player is dropping a dyna bomb */
11672 player->dynabombs_left--;
11674 Feld[dropx][dropy] = new_element;
11676 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
11677 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
11678 el2img(Feld[dropx][dropy]), 0);
11680 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
11683 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
11684 InitField_WithBug1(dropx, dropy, FALSE);
11686 new_element = Feld[dropx][dropy]; /* element might have changed */
11688 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
11689 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
11691 int move_direction, nextx, nexty;
11693 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
11694 MovDir[dropx][dropy] = drop_direction;
11696 move_direction = MovDir[dropx][dropy];
11697 nextx = dropx + GET_DX_FROM_DIR(move_direction);
11698 nexty = dropy + GET_DY_FROM_DIR(move_direction);
11700 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
11701 CheckCollision[dropx][dropy] = 2;
11704 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
11705 player->is_dropping = TRUE;
11707 player->drop_x = dropx;
11708 player->drop_y = dropy;
11713 /* ------------------------------------------------------------------------- */
11714 /* game sound playing functions */
11715 /* ------------------------------------------------------------------------- */
11717 static int *loop_sound_frame = NULL;
11718 static int *loop_sound_volume = NULL;
11720 void InitPlayLevelSound()
11722 int num_sounds = getSoundListSize();
11724 checked_free(loop_sound_frame);
11725 checked_free(loop_sound_volume);
11727 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
11728 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
11731 static void PlayLevelSound(int x, int y, int nr)
11733 int sx = SCREENX(x), sy = SCREENY(y);
11734 int volume, stereo_position;
11735 int max_distance = 8;
11736 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
11738 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
11739 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
11742 if (!IN_LEV_FIELD(x, y) ||
11743 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
11744 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
11747 volume = SOUND_MAX_VOLUME;
11749 if (!IN_SCR_FIELD(sx, sy))
11751 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
11752 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
11754 volume -= volume * (dx > dy ? dx : dy) / max_distance;
11757 stereo_position = (SOUND_MAX_LEFT +
11758 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
11759 (SCR_FIELDX + 2 * max_distance));
11761 if (IS_LOOP_SOUND(nr))
11763 /* This assures that quieter loop sounds do not overwrite louder ones,
11764 while restarting sound volume comparison with each new game frame. */
11766 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
11769 loop_sound_volume[nr] = volume;
11770 loop_sound_frame[nr] = FrameCounter;
11773 PlaySoundExt(nr, volume, stereo_position, type);
11776 static void PlayLevelSoundNearest(int x, int y, int sound_action)
11778 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
11779 x > LEVELX(BX2) ? LEVELX(BX2) : x,
11780 y < LEVELY(BY1) ? LEVELY(BY1) :
11781 y > LEVELY(BY2) ? LEVELY(BY2) : y,
11785 static void PlayLevelSoundAction(int x, int y, int action)
11787 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
11790 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
11792 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
11794 if (sound_effect != SND_UNDEFINED)
11795 PlayLevelSound(x, y, sound_effect);
11798 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
11801 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
11803 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
11804 PlayLevelSound(x, y, sound_effect);
11807 static void PlayLevelSoundActionIfLoop(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 PlayLevelSound(x, y, sound_effect);
11815 static void StopLevelSoundActionIfLoop(int x, int y, int action)
11817 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
11819 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
11820 StopSound(sound_effect);
11823 static void PlayLevelMusic()
11825 if (levelset.music[level_nr] != MUS_UNDEFINED)
11826 PlayMusic(levelset.music[level_nr]); /* from config file */
11828 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
11831 void PlayLevelSound_EM(int x, int y, int element_em, int sample)
11833 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
11838 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
11842 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
11846 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
11850 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
11854 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
11858 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
11862 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
11865 case SAMPLE_android_clone:
11866 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
11869 case SAMPLE_android_move:
11870 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
11873 case SAMPLE_spring:
11874 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
11878 PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
11882 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
11885 case SAMPLE_eater_eat:
11886 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
11890 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
11893 case SAMPLE_collect:
11894 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
11897 case SAMPLE_diamond:
11898 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
11901 case SAMPLE_squash:
11902 /* !!! CHECK THIS !!! */
11904 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
11906 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
11910 case SAMPLE_wonderfall:
11911 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
11915 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
11919 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
11923 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
11927 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
11931 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
11935 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
11938 case SAMPLE_wonder:
11939 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
11943 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
11946 case SAMPLE_exit_open:
11947 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
11950 case SAMPLE_exit_leave:
11951 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
11954 case SAMPLE_dynamite:
11955 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
11959 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
11963 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
11967 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
11971 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
11975 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
11979 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
11983 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
11988 void RaiseScore(int value)
11990 local_player->score += value;
11992 DrawGameValue_Score(local_player->score);
11995 void RaiseScoreElement(int element)
12000 case EL_BD_DIAMOND:
12001 case EL_EMERALD_YELLOW:
12002 case EL_EMERALD_RED:
12003 case EL_EMERALD_PURPLE:
12004 case EL_SP_INFOTRON:
12005 RaiseScore(level.score[SC_EMERALD]);
12008 RaiseScore(level.score[SC_DIAMOND]);
12011 RaiseScore(level.score[SC_CRYSTAL]);
12014 RaiseScore(level.score[SC_PEARL]);
12017 case EL_BD_BUTTERFLY:
12018 case EL_SP_ELECTRON:
12019 RaiseScore(level.score[SC_BUG]);
12022 case EL_BD_FIREFLY:
12023 case EL_SP_SNIKSNAK:
12024 RaiseScore(level.score[SC_SPACESHIP]);
12027 case EL_DARK_YAMYAM:
12028 RaiseScore(level.score[SC_YAMYAM]);
12031 RaiseScore(level.score[SC_ROBOT]);
12034 RaiseScore(level.score[SC_PACMAN]);
12037 RaiseScore(level.score[SC_NUT]);
12040 case EL_EM_DYNAMITE:
12041 case EL_SP_DISK_RED:
12042 case EL_DYNABOMB_INCREASE_NUMBER:
12043 case EL_DYNABOMB_INCREASE_SIZE:
12044 case EL_DYNABOMB_INCREASE_POWER:
12045 RaiseScore(level.score[SC_DYNAMITE]);
12047 case EL_SHIELD_NORMAL:
12048 case EL_SHIELD_DEADLY:
12049 RaiseScore(level.score[SC_SHIELD]);
12051 case EL_EXTRA_TIME:
12052 RaiseScore(level.extra_time_score);
12066 RaiseScore(level.score[SC_KEY]);
12069 RaiseScore(element_info[element].collect_score);
12074 void RequestQuitGame(boolean ask_if_really_quit)
12076 if (AllPlayersGone ||
12077 !ask_if_really_quit ||
12078 level_editor_test_game ||
12079 Request("Do you really want to quit the game ?",
12080 REQ_ASK | REQ_STAY_CLOSED))
12082 #if defined(NETWORK_AVALIABLE)
12083 if (options.network)
12084 SendToServer_StopPlaying();
12088 game_status = GAME_MODE_MAIN;
12094 if (tape.playing && tape.deactivate_display)
12095 TapeDeactivateDisplayOff(TRUE);
12097 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
12099 if (tape.playing && tape.deactivate_display)
12100 TapeDeactivateDisplayOn();
12105 /* ---------- new game button stuff ---------------------------------------- */
12107 /* graphic position values for game buttons */
12108 #define GAME_BUTTON_XSIZE 30
12109 #define GAME_BUTTON_YSIZE 30
12110 #define GAME_BUTTON_XPOS 5
12111 #define GAME_BUTTON_YPOS 215
12112 #define SOUND_BUTTON_XPOS 5
12113 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
12115 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
12116 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
12117 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
12118 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
12119 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
12120 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
12127 } gamebutton_info[NUM_GAME_BUTTONS] =
12130 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
12135 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
12136 GAME_CTRL_ID_PAUSE,
12140 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
12145 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
12146 SOUND_CTRL_ID_MUSIC,
12147 "background music on/off"
12150 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
12151 SOUND_CTRL_ID_LOOPS,
12152 "sound loops on/off"
12155 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
12156 SOUND_CTRL_ID_SIMPLE,
12157 "normal sounds on/off"
12161 void CreateGameButtons()
12165 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12167 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
12168 struct GadgetInfo *gi;
12171 unsigned long event_mask;
12172 int gd_xoffset, gd_yoffset;
12173 int gd_x1, gd_x2, gd_y1, gd_y2;
12176 gd_xoffset = gamebutton_info[i].x;
12177 gd_yoffset = gamebutton_info[i].y;
12178 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
12179 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
12181 if (id == GAME_CTRL_ID_STOP ||
12182 id == GAME_CTRL_ID_PAUSE ||
12183 id == GAME_CTRL_ID_PLAY)
12185 button_type = GD_TYPE_NORMAL_BUTTON;
12187 event_mask = GD_EVENT_RELEASED;
12188 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12189 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12193 button_type = GD_TYPE_CHECK_BUTTON;
12195 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
12196 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
12197 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
12198 event_mask = GD_EVENT_PRESSED;
12199 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
12200 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12203 gi = CreateGadget(GDI_CUSTOM_ID, id,
12204 GDI_INFO_TEXT, gamebutton_info[i].infotext,
12205 GDI_X, DX + gd_xoffset,
12206 GDI_Y, DY + gd_yoffset,
12207 GDI_WIDTH, GAME_BUTTON_XSIZE,
12208 GDI_HEIGHT, GAME_BUTTON_YSIZE,
12209 GDI_TYPE, button_type,
12210 GDI_STATE, GD_BUTTON_UNPRESSED,
12211 GDI_CHECKED, checked,
12212 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
12213 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
12214 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
12215 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
12216 GDI_EVENT_MASK, event_mask,
12217 GDI_CALLBACK_ACTION, HandleGameButtons,
12221 Error(ERR_EXIT, "cannot create gadget");
12223 game_gadget[id] = gi;
12227 void FreeGameButtons()
12231 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12232 FreeGadget(game_gadget[i]);
12235 static void MapGameButtons()
12239 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12240 MapGadget(game_gadget[i]);
12243 void UnmapGameButtons()
12247 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12248 UnmapGadget(game_gadget[i]);
12251 static void HandleGameButtons(struct GadgetInfo *gi)
12253 int id = gi->custom_id;
12255 if (game_status != GAME_MODE_PLAYING)
12260 case GAME_CTRL_ID_STOP:
12261 RequestQuitGame(TRUE);
12264 case GAME_CTRL_ID_PAUSE:
12265 if (options.network)
12267 #if defined(NETWORK_AVALIABLE)
12269 SendToServer_ContinuePlaying();
12271 SendToServer_PausePlaying();
12275 TapeTogglePause(TAPE_TOGGLE_MANUAL);
12278 case GAME_CTRL_ID_PLAY:
12281 #if defined(NETWORK_AVALIABLE)
12282 if (options.network)
12283 SendToServer_ContinuePlaying();
12287 tape.pausing = FALSE;
12288 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
12293 case SOUND_CTRL_ID_MUSIC:
12294 if (setup.sound_music)
12296 setup.sound_music = FALSE;
12299 else if (audio.music_available)
12301 setup.sound = setup.sound_music = TRUE;
12303 SetAudioMode(setup.sound);
12309 case SOUND_CTRL_ID_LOOPS:
12310 if (setup.sound_loops)
12311 setup.sound_loops = FALSE;
12312 else if (audio.loops_available)
12314 setup.sound = setup.sound_loops = TRUE;
12315 SetAudioMode(setup.sound);
12319 case SOUND_CTRL_ID_SIMPLE:
12320 if (setup.sound_simple)
12321 setup.sound_simple = FALSE;
12322 else if (audio.sound_available)
12324 setup.sound = setup.sound_simple = TRUE;
12325 SetAudioMode(setup.sound);