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 game.centered_to_player = game.centered_to_player_next = 0; /* player_1 */
1938 for (i = 0; i < NUM_BELTS; i++)
1940 game.belt_dir[i] = MV_NONE;
1941 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1944 for (i = 0; i < MAX_NUM_AMOEBA; i++)
1945 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
1948 SCAN_PLAYFIELD(x, y)
1950 for (x = 0; x < lev_fieldx; x++) for (y = 0; y < lev_fieldy; y++)
1953 Feld[x][y] = level.field[x][y];
1954 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
1955 ChangeDelay[x][y] = 0;
1956 ChangePage[x][y] = -1;
1957 #if USE_NEW_CUSTOM_VALUE
1958 CustomValue[x][y] = 0; /* initialized in InitField() */
1960 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
1962 WasJustMoving[x][y] = 0;
1963 WasJustFalling[x][y] = 0;
1964 CheckCollision[x][y] = 0;
1966 Pushed[x][y] = FALSE;
1968 ChangeCount[x][y] = 0;
1969 ChangeEvent[x][y] = -1;
1971 ExplodePhase[x][y] = 0;
1972 ExplodeDelay[x][y] = 0;
1973 ExplodeField[x][y] = EX_TYPE_NONE;
1975 RunnerVisit[x][y] = 0;
1976 PlayerVisit[x][y] = 0;
1979 GfxRandom[x][y] = INIT_GFX_RANDOM();
1980 GfxElement[x][y] = EL_UNDEFINED;
1981 GfxAction[x][y] = ACTION_DEFAULT;
1982 GfxDir[x][y] = MV_NONE;
1986 SCAN_PLAYFIELD(x, y)
1988 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
1991 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
1993 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
1995 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
1998 InitField(x, y, TRUE);
2003 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
2004 emulate_sb ? EMU_SOKOBAN :
2005 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
2007 #if USE_NEW_ALL_SLIPPERY
2008 /* initialize type of slippery elements */
2009 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2011 if (!IS_CUSTOM_ELEMENT(i))
2013 /* default: elements slip down either to the left or right randomly */
2014 element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
2016 /* SP style elements prefer to slip down on the left side */
2017 if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
2018 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
2020 /* BD style elements prefer to slip down on the left side */
2021 if (game.emulation == EMU_BOULDERDASH)
2022 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
2027 /* initialize explosion and ignition delay */
2028 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2030 if (!IS_CUSTOM_ELEMENT(i))
2033 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
2034 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
2035 game.emulation == EMU_SUPAPLEX ? 3 : 2);
2036 int last_phase = (num_phase + 1) * delay;
2037 int half_phase = (num_phase / 2) * delay;
2039 element_info[i].explosion_delay = last_phase - 1;
2040 element_info[i].ignition_delay = half_phase;
2042 if (i == EL_BLACK_ORB)
2043 element_info[i].ignition_delay = 1;
2047 if (element_info[i].explosion_delay < 1) /* !!! check again !!! */
2048 element_info[i].explosion_delay = 1;
2050 if (element_info[i].ignition_delay < 1) /* !!! check again !!! */
2051 element_info[i].ignition_delay = 1;
2055 /* correct non-moving belts to start moving left */
2056 for (i = 0; i < NUM_BELTS; i++)
2057 if (game.belt_dir[i] == MV_NONE)
2058 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
2060 /* check if any connected player was not found in playfield */
2061 for (i = 0; i < MAX_PLAYERS; i++)
2063 struct PlayerInfo *player = &stored_player[i];
2065 if (player->connected && !player->present)
2067 for (j = 0; j < MAX_PLAYERS; j++)
2069 struct PlayerInfo *some_player = &stored_player[j];
2070 int jx = some_player->jx, jy = some_player->jy;
2072 /* assign first free player found that is present in the playfield */
2073 if (some_player->present && !some_player->connected)
2075 player->present = TRUE;
2076 player->active = TRUE;
2078 some_player->present = FALSE;
2079 some_player->active = FALSE;
2082 player->element_nr = some_player->element_nr;
2085 player->artwork_element = some_player->artwork_element;
2087 player->block_last_field = some_player->block_last_field;
2088 player->block_delay_adjustment = some_player->block_delay_adjustment;
2090 StorePlayer[jx][jy] = player->element_nr;
2091 player->jx = player->last_jx = jx;
2092 player->jy = player->last_jy = jy;
2102 /* when playing a tape, eliminate all players which do not participate */
2104 for (i = 0; i < MAX_PLAYERS; i++)
2106 if (stored_player[i].active && !tape.player_participates[i])
2108 struct PlayerInfo *player = &stored_player[i];
2109 int jx = player->jx, jy = player->jy;
2111 player->active = FALSE;
2112 StorePlayer[jx][jy] = 0;
2113 Feld[jx][jy] = EL_EMPTY;
2117 else if (!options.network && !setup.team_mode) /* && !tape.playing */
2119 /* when in single player mode, eliminate all but the first active player */
2121 for (i = 0; i < MAX_PLAYERS; i++)
2123 if (stored_player[i].active)
2125 for (j = i + 1; j < MAX_PLAYERS; j++)
2127 if (stored_player[j].active)
2129 struct PlayerInfo *player = &stored_player[j];
2130 int jx = player->jx, jy = player->jy;
2132 player->active = FALSE;
2133 player->present = FALSE;
2135 StorePlayer[jx][jy] = 0;
2136 Feld[jx][jy] = EL_EMPTY;
2143 /* when recording the game, store which players take part in the game */
2146 for (i = 0; i < MAX_PLAYERS; i++)
2147 if (stored_player[i].active)
2148 tape.player_participates[i] = TRUE;
2153 for (i = 0; i < MAX_PLAYERS; i++)
2155 struct PlayerInfo *player = &stored_player[i];
2157 printf("Player %d: present == %d, connected == %d, active == %d.\n",
2162 if (local_player == player)
2163 printf("Player %d is local player.\n", i+1);
2167 if (BorderElement == EL_EMPTY)
2170 SBX_Right = lev_fieldx - SCR_FIELDX;
2172 SBY_Lower = lev_fieldy - SCR_FIELDY;
2177 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
2179 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
2182 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
2183 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
2185 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
2186 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
2188 /* if local player not found, look for custom element that might create
2189 the player (make some assumptions about the right custom element) */
2190 if (!local_player->present)
2192 int start_x = 0, start_y = 0;
2193 int found_rating = 0;
2194 int found_element = EL_UNDEFINED;
2195 int player_nr = local_player->index_nr;
2198 SCAN_PLAYFIELD(x, y)
2200 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
2203 int element = Feld[x][y];
2208 if (level.use_start_element[player_nr] &&
2209 level.start_element[player_nr] == element &&
2216 found_element = element;
2219 if (!IS_CUSTOM_ELEMENT(element))
2222 if (CAN_CHANGE(element))
2224 for (i = 0; i < element_info[element].num_change_pages; i++)
2226 /* check for player created from custom element as single target */
2227 content = element_info[element].change_page[i].target_element;
2228 is_player = ELEM_IS_PLAYER(content);
2230 if (is_player && (found_rating < 3 || element < found_element))
2236 found_element = element;
2241 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
2243 /* check for player created from custom element as explosion content */
2244 content = element_info[element].content.e[xx][yy];
2245 is_player = ELEM_IS_PLAYER(content);
2247 if (is_player && (found_rating < 2 || element < found_element))
2249 start_x = x + xx - 1;
2250 start_y = y + yy - 1;
2253 found_element = element;
2256 if (!CAN_CHANGE(element))
2259 for (i = 0; i < element_info[element].num_change_pages; i++)
2261 /* check for player created from custom element as extended target */
2263 element_info[element].change_page[i].target_content.e[xx][yy];
2265 is_player = ELEM_IS_PLAYER(content);
2267 if (is_player && (found_rating < 1 || element < found_element))
2269 start_x = x + xx - 1;
2270 start_y = y + yy - 1;
2273 found_element = element;
2279 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
2280 start_x > SBX_Right + MIDPOSX ? SBX_Right :
2283 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
2284 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
2289 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2290 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2291 local_player->jx - MIDPOSX);
2293 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2294 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2295 local_player->jy - MIDPOSY);
2298 if (!game.restart_level)
2299 CloseDoor(DOOR_CLOSE_1);
2301 /* !!! FIX THIS (START) !!! */
2302 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2304 InitGameEngine_EM();
2311 /* after drawing the level, correct some elements */
2312 if (game.timegate_time_left == 0)
2313 CloseAllOpenTimegates();
2315 if (setup.soft_scrolling)
2316 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
2318 redraw_mask |= REDRAW_FROM_BACKBUFFER;
2321 /* !!! FIX THIS (END) !!! */
2323 if (!game.restart_level)
2325 /* copy default game door content to main double buffer */
2326 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2327 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2330 DrawGameDoorValues();
2332 if (!game.restart_level)
2336 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
2337 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
2338 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
2342 /* copy actual game door content to door double buffer for OpenDoor() */
2343 BlitBitmap(drawto, bitmap_db_door,
2344 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
2346 OpenDoor(DOOR_OPEN_ALL);
2348 PlaySoundStereo(SND_GAME_STARTING, SOUND_MIDDLE);
2350 if (setup.sound_music)
2353 KeyboardAutoRepeatOffUnlessAutoplay();
2357 for (i = 0; i < MAX_PLAYERS; i++)
2358 printf("Player %d %sactive.\n",
2359 i + 1, (stored_player[i].active ? "" : "not "));
2363 game.restart_level = FALSE;
2366 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
2368 /* this is used for non-R'n'D game engines to update certain engine values */
2370 /* needed to determine if sounds are played within the visible screen area */
2371 scroll_x = actual_scroll_x;
2372 scroll_y = actual_scroll_y;
2375 void InitMovDir(int x, int y)
2377 int i, element = Feld[x][y];
2378 static int xy[4][2] =
2385 static int direction[3][4] =
2387 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
2388 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
2389 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
2398 Feld[x][y] = EL_BUG;
2399 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
2402 case EL_SPACESHIP_RIGHT:
2403 case EL_SPACESHIP_UP:
2404 case EL_SPACESHIP_LEFT:
2405 case EL_SPACESHIP_DOWN:
2406 Feld[x][y] = EL_SPACESHIP;
2407 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
2410 case EL_BD_BUTTERFLY_RIGHT:
2411 case EL_BD_BUTTERFLY_UP:
2412 case EL_BD_BUTTERFLY_LEFT:
2413 case EL_BD_BUTTERFLY_DOWN:
2414 Feld[x][y] = EL_BD_BUTTERFLY;
2415 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
2418 case EL_BD_FIREFLY_RIGHT:
2419 case EL_BD_FIREFLY_UP:
2420 case EL_BD_FIREFLY_LEFT:
2421 case EL_BD_FIREFLY_DOWN:
2422 Feld[x][y] = EL_BD_FIREFLY;
2423 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
2426 case EL_PACMAN_RIGHT:
2428 case EL_PACMAN_LEFT:
2429 case EL_PACMAN_DOWN:
2430 Feld[x][y] = EL_PACMAN;
2431 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
2434 case EL_SP_SNIKSNAK:
2435 MovDir[x][y] = MV_UP;
2438 case EL_SP_ELECTRON:
2439 MovDir[x][y] = MV_LEFT;
2446 Feld[x][y] = EL_MOLE;
2447 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
2451 if (IS_CUSTOM_ELEMENT(element))
2453 struct ElementInfo *ei = &element_info[element];
2454 int move_direction_initial = ei->move_direction_initial;
2455 int move_pattern = ei->move_pattern;
2457 if (move_direction_initial == MV_START_PREVIOUS)
2459 if (MovDir[x][y] != MV_NONE)
2462 move_direction_initial = MV_START_AUTOMATIC;
2465 if (move_direction_initial == MV_START_RANDOM)
2466 MovDir[x][y] = 1 << RND(4);
2467 else if (move_direction_initial & MV_ANY_DIRECTION)
2468 MovDir[x][y] = move_direction_initial;
2469 else if (move_pattern == MV_ALL_DIRECTIONS ||
2470 move_pattern == MV_TURNING_LEFT ||
2471 move_pattern == MV_TURNING_RIGHT ||
2472 move_pattern == MV_TURNING_LEFT_RIGHT ||
2473 move_pattern == MV_TURNING_RIGHT_LEFT ||
2474 move_pattern == MV_TURNING_RANDOM)
2475 MovDir[x][y] = 1 << RND(4);
2476 else if (move_pattern == MV_HORIZONTAL)
2477 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
2478 else if (move_pattern == MV_VERTICAL)
2479 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
2480 else if (move_pattern & MV_ANY_DIRECTION)
2481 MovDir[x][y] = element_info[element].move_pattern;
2482 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
2483 move_pattern == MV_ALONG_RIGHT_SIDE)
2485 /* use random direction as default start direction */
2486 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2487 MovDir[x][y] = 1 << RND(4);
2489 for (i = 0; i < NUM_DIRECTIONS; i++)
2491 int x1 = x + xy[i][0];
2492 int y1 = y + xy[i][1];
2494 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2496 if (move_pattern == MV_ALONG_RIGHT_SIDE)
2497 MovDir[x][y] = direction[0][i];
2499 MovDir[x][y] = direction[1][i];
2508 MovDir[x][y] = 1 << RND(4);
2510 if (element != EL_BUG &&
2511 element != EL_SPACESHIP &&
2512 element != EL_BD_BUTTERFLY &&
2513 element != EL_BD_FIREFLY)
2516 for (i = 0; i < NUM_DIRECTIONS; i++)
2518 int x1 = x + xy[i][0];
2519 int y1 = y + xy[i][1];
2521 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2523 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
2525 MovDir[x][y] = direction[0][i];
2528 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
2529 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
2531 MovDir[x][y] = direction[1][i];
2540 GfxDir[x][y] = MovDir[x][y];
2543 void InitAmoebaNr(int x, int y)
2546 int group_nr = AmoebeNachbarNr(x, y);
2550 for (i = 1; i < MAX_NUM_AMOEBA; i++)
2552 if (AmoebaCnt[i] == 0)
2560 AmoebaNr[x][y] = group_nr;
2561 AmoebaCnt[group_nr]++;
2562 AmoebaCnt2[group_nr]++;
2568 boolean raise_level = FALSE;
2570 if (local_player->MovPos)
2573 if (tape.auto_play) /* tape might already be stopped here */
2574 tape.auto_play_level_solved = TRUE;
2576 local_player->LevelSolved = FALSE;
2578 PlaySoundStereo(SND_GAME_WINNING, SOUND_MIDDLE);
2582 if (!tape.playing && setup.sound_loops)
2583 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2584 SND_CTRL_PLAY_LOOP);
2586 while (TimeLeft > 0)
2588 if (!tape.playing && !setup.sound_loops)
2589 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2591 if (TimeLeft > 100 && TimeLeft % 10 == 0)
2594 RaiseScore(level.score[SC_TIME_BONUS] * 10);
2599 RaiseScore(level.score[SC_TIME_BONUS]);
2602 DrawGameValue_Time(TimeLeft);
2610 if (!tape.playing && setup.sound_loops)
2611 StopSound(SND_GAME_LEVELTIME_BONUS);
2613 else if (level.time == 0) /* level without time limit */
2615 if (!tape.playing && setup.sound_loops)
2616 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2617 SND_CTRL_PLAY_LOOP);
2619 while (TimePlayed < 999)
2621 if (!tape.playing && !setup.sound_loops)
2622 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2624 if (TimePlayed < 900 && TimePlayed % 10 == 0)
2627 RaiseScore(level.score[SC_TIME_BONUS] * 10);
2632 RaiseScore(level.score[SC_TIME_BONUS]);
2635 DrawGameValue_Time(TimePlayed);
2643 if (!tape.playing && setup.sound_loops)
2644 StopSound(SND_GAME_LEVELTIME_BONUS);
2647 /* close exit door after last player */
2648 if (AllPlayersGone && ExitX >= 0 && ExitY >= 0 &&
2649 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
2650 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN))
2652 int element = Feld[ExitX][ExitY];
2654 Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
2655 EL_SP_EXIT_CLOSING);
2657 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
2660 /* player disappears */
2661 if (ExitX >= 0 && ExitY >= 0)
2662 DrawLevelField(ExitX, ExitY);
2668 printf("::: TAPE PLAYING -> DO NOT SAVE SCORE\n");
2670 printf("::: NO TAPE PLAYING -> SAVING SCORE\n");
2676 CloseDoor(DOOR_CLOSE_1);
2681 SaveTape(tape.level_nr); /* Ask to save tape */
2684 if (level_nr == leveldir_current->handicap_level)
2686 leveldir_current->handicap_level++;
2687 SaveLevelSetup_SeriesInfo();
2690 if (level_editor_test_game)
2691 local_player->score = -1; /* no highscore when playing from editor */
2692 else if (level_nr < leveldir_current->last_level)
2693 raise_level = TRUE; /* advance to next level */
2695 if ((hi_pos = NewHiScore()) >= 0)
2697 game_status = GAME_MODE_SCORES;
2698 DrawHallOfFame(hi_pos);
2707 game_status = GAME_MODE_MAIN;
2724 LoadScore(level_nr);
2726 if (strcmp(setup.player_name, EMPTY_PLAYER_NAME) == 0 ||
2727 local_player->score < highscore[MAX_SCORE_ENTRIES - 1].Score)
2730 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
2732 if (local_player->score > highscore[k].Score)
2734 /* player has made it to the hall of fame */
2736 if (k < MAX_SCORE_ENTRIES - 1)
2738 int m = MAX_SCORE_ENTRIES - 1;
2741 for (l = k; l < MAX_SCORE_ENTRIES; l++)
2742 if (!strcmp(setup.player_name, highscore[l].Name))
2744 if (m == k) /* player's new highscore overwrites his old one */
2748 for (l = m; l > k; l--)
2750 strcpy(highscore[l].Name, highscore[l - 1].Name);
2751 highscore[l].Score = highscore[l - 1].Score;
2758 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
2759 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
2760 highscore[k].Score = local_player->score;
2766 else if (!strncmp(setup.player_name, highscore[k].Name,
2767 MAX_PLAYER_NAME_LEN))
2768 break; /* player already there with a higher score */
2774 SaveScore(level_nr);
2779 inline static int getElementMoveStepsize(int x, int y)
2781 int element = Feld[x][y];
2782 int direction = MovDir[x][y];
2783 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2784 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2785 int horiz_move = (dx != 0);
2786 int sign = (horiz_move ? dx : dy);
2787 int step = sign * element_info[element].move_stepsize;
2789 /* special values for move stepsize for spring and things on conveyor belt */
2793 if (element == EL_SPRING)
2794 step = sign * MOVE_STEPSIZE_NORMAL * 2;
2795 else if (CAN_FALL(element) && !CAN_MOVE(element) &&
2796 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
2797 step = sign * MOVE_STEPSIZE_NORMAL / 2;
2799 if (CAN_FALL(element) &&
2800 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
2801 step = sign * MOVE_STEPSIZE_NORMAL / 2;
2802 else if (element == EL_SPRING)
2803 step = sign * MOVE_STEPSIZE_NORMAL * 2;
2810 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
2812 if (player->GfxAction != action || player->GfxDir != dir)
2815 printf("Player frame reset! (%d => %d, %d => %d)\n",
2816 player->GfxAction, action, player->GfxDir, dir);
2819 player->GfxAction = action;
2820 player->GfxDir = dir;
2822 player->StepFrame = 0;
2826 static void ResetRandomAnimationValue(int x, int y)
2828 GfxRandom[x][y] = INIT_GFX_RANDOM();
2831 static void ResetGfxAnimation(int x, int y)
2834 int element, graphic;
2838 GfxAction[x][y] = ACTION_DEFAULT;
2839 GfxDir[x][y] = MovDir[x][y];
2842 element = Feld[x][y];
2843 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
2845 if (graphic_info[graphic].anim_global_sync)
2846 GfxFrame[x][y] = FrameCounter;
2847 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
2848 GfxFrame[x][y] = CustomValue[x][y];
2849 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2850 GfxFrame[x][y] = element_info[element].collect_score;
2854 void InitMovingField(int x, int y, int direction)
2856 int element = Feld[x][y];
2860 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2861 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2865 if (!WasJustMoving[x][y] || direction != MovDir[x][y])
2866 ResetGfxAnimation(x, y);
2868 MovDir[x][y] = direction;
2869 GfxDir[x][y] = direction;
2870 GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
2871 ACTION_FALLING : ACTION_MOVING);
2874 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
2876 if (graphic_info[graphic].anim_global_sync)
2877 GfxFrame[x][y] = FrameCounter;
2878 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
2879 GfxFrame[x][y] = CustomValue[x][y];
2880 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2881 GfxFrame[x][y] = element_info[element].collect_score;
2884 /* this is needed for CEs with property "can move" / "not moving" */
2886 if (getElementMoveStepsize(x, y) != 0) /* moving or being moved */
2888 if (Feld[newx][newy] == EL_EMPTY)
2889 Feld[newx][newy] = EL_BLOCKED;
2891 MovDir[newx][newy] = MovDir[x][y];
2893 #if USE_NEW_CUSTOM_VALUE
2894 CustomValue[newx][newy] = CustomValue[x][y];
2897 GfxFrame[newx][newy] = GfxFrame[x][y];
2898 GfxRandom[newx][newy] = GfxRandom[x][y];
2899 GfxAction[newx][newy] = GfxAction[x][y];
2900 GfxDir[newx][newy] = GfxDir[x][y];
2904 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
2906 int direction = MovDir[x][y];
2908 int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
2909 int newy = y + (direction & MV_UP ? -1 : direction & MV_DOWN ? +1 : 0);
2911 int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2912 int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2919 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
2921 int oldx = x, oldy = y;
2922 int direction = MovDir[x][y];
2924 if (direction == MV_LEFT)
2926 else if (direction == MV_RIGHT)
2928 else if (direction == MV_UP)
2930 else if (direction == MV_DOWN)
2933 *comes_from_x = oldx;
2934 *comes_from_y = oldy;
2937 int MovingOrBlocked2Element(int x, int y)
2939 int element = Feld[x][y];
2941 if (element == EL_BLOCKED)
2945 Blocked2Moving(x, y, &oldx, &oldy);
2946 return Feld[oldx][oldy];
2952 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
2954 /* like MovingOrBlocked2Element(), but if element is moving
2955 and (x,y) is the field the moving element is just leaving,
2956 return EL_BLOCKED instead of the element value */
2957 int element = Feld[x][y];
2959 if (IS_MOVING(x, y))
2961 if (element == EL_BLOCKED)
2965 Blocked2Moving(x, y, &oldx, &oldy);
2966 return Feld[oldx][oldy];
2975 static void RemoveField(int x, int y)
2977 Feld[x][y] = EL_EMPTY;
2983 #if USE_NEW_CUSTOM_VALUE
2984 CustomValue[x][y] = 0;
2988 ChangeDelay[x][y] = 0;
2989 ChangePage[x][y] = -1;
2990 Pushed[x][y] = FALSE;
2993 ExplodeField[x][y] = EX_TYPE_NONE;
2996 GfxElement[x][y] = EL_UNDEFINED;
2997 GfxAction[x][y] = ACTION_DEFAULT;
2998 GfxDir[x][y] = MV_NONE;
3001 void RemoveMovingField(int x, int y)
3003 int oldx = x, oldy = y, newx = x, newy = y;
3004 int element = Feld[x][y];
3005 int next_element = EL_UNDEFINED;
3007 if (element != EL_BLOCKED && !IS_MOVING(x, y))
3010 if (IS_MOVING(x, y))
3012 Moving2Blocked(x, y, &newx, &newy);
3014 if (Feld[newx][newy] != EL_BLOCKED)
3016 /* element is moving, but target field is not free (blocked), but
3017 already occupied by something different (example: acid pool);
3018 in this case, only remove the moving field, but not the target */
3020 RemoveField(oldx, oldy);
3022 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
3024 DrawLevelField(oldx, oldy);
3029 else if (element == EL_BLOCKED)
3031 Blocked2Moving(x, y, &oldx, &oldy);
3032 if (!IS_MOVING(oldx, oldy))
3036 if (element == EL_BLOCKED &&
3037 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
3038 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
3039 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
3040 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
3041 next_element = get_next_element(Feld[oldx][oldy]);
3043 RemoveField(oldx, oldy);
3044 RemoveField(newx, newy);
3046 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
3048 if (next_element != EL_UNDEFINED)
3049 Feld[oldx][oldy] = next_element;
3051 DrawLevelField(oldx, oldy);
3052 DrawLevelField(newx, newy);
3055 void DrawDynamite(int x, int y)
3057 int sx = SCREENX(x), sy = SCREENY(y);
3058 int graphic = el2img(Feld[x][y]);
3061 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
3064 if (IS_WALKABLE_INSIDE(Back[x][y]))
3068 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
3069 else if (Store[x][y])
3070 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
3072 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
3074 if (Back[x][y] || Store[x][y])
3075 DrawGraphicThruMask(sx, sy, graphic, frame);
3077 DrawGraphic(sx, sy, graphic, frame);
3080 void CheckDynamite(int x, int y)
3082 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
3086 if (MovDelay[x][y] != 0)
3089 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
3095 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
3100 void DrawRelocatePlayer(struct PlayerInfo *player, boolean quick_relocation)
3102 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3103 boolean no_delay = (tape.warp_forward);
3104 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
3105 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
3106 int jx = player->jx;
3107 int jy = player->jy;
3109 if (quick_relocation)
3111 int offset = (setup.scroll_delay ? 3 : 0);
3113 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
3115 scroll_x = (player->jx < SBX_Left + MIDPOSX ? SBX_Left :
3116 player->jx > SBX_Right + MIDPOSX ? SBX_Right :
3117 player->jx - MIDPOSX);
3119 scroll_y = (player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
3120 player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
3121 player->jy - MIDPOSY);
3125 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
3126 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
3127 scroll_x = jx - MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
3129 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
3130 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
3131 scroll_y = jy - MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
3133 /* don't scroll over playfield boundaries */
3134 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
3135 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
3137 /* don't scroll over playfield boundaries */
3138 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
3139 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
3142 RedrawPlayfield(TRUE, 0,0,0,0);
3146 int scroll_xx = -999, scroll_yy = -999;
3148 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
3150 while (scroll_xx != scroll_x || scroll_yy != scroll_y)
3153 int fx = FX, fy = FY;
3155 scroll_xx = (player->jx < SBX_Left + MIDPOSX ? SBX_Left :
3156 player->jx > SBX_Right + MIDPOSX ? SBX_Right :
3157 player->jx - MIDPOSX);
3159 scroll_yy = (player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
3160 player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
3161 player->jy - MIDPOSY);
3163 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
3164 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
3166 if (dx == 0 && dy == 0) /* no scrolling needed at all */
3172 fx += dx * TILEX / 2;
3173 fy += dy * TILEY / 2;
3175 ScrollLevel(dx, dy);
3178 /* scroll in two steps of half tile size to make things smoother */
3179 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
3181 Delay(wait_delay_value);
3183 /* scroll second step to align at full tile size */
3185 Delay(wait_delay_value);
3190 Delay(wait_delay_value);
3194 void RelocatePlayer(int jx, int jy, int el_player_raw)
3196 int el_player = GET_PLAYER_ELEMENT(el_player_raw);
3197 int player_nr = GET_PLAYER_NR(el_player);
3198 struct PlayerInfo *player = &stored_player[player_nr];
3199 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3200 boolean no_delay = (tape.warp_forward);
3201 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
3202 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
3203 int old_jx = player->jx;
3204 int old_jy = player->jy;
3205 int old_element = Feld[old_jx][old_jy];
3206 int element = Feld[jx][jy];
3207 boolean player_relocated = (old_jx != jx || old_jy != jy);
3209 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
3210 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
3211 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
3212 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
3213 int leave_side_horiz = move_dir_horiz;
3214 int leave_side_vert = move_dir_vert;
3215 int enter_side = enter_side_horiz | enter_side_vert;
3216 int leave_side = leave_side_horiz | leave_side_vert;
3218 if (player->GameOver) /* do not reanimate dead player */
3221 if (!player_relocated) /* no need to relocate the player */
3224 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
3226 RemoveField(jx, jy); /* temporarily remove newly placed player */
3227 DrawLevelField(jx, jy);
3230 if (player->present)
3232 while (player->MovPos)
3234 ScrollPlayer(player, SCROLL_GO_ON);
3235 ScrollScreen(NULL, SCROLL_GO_ON);
3237 AdvanceFrameAndPlayerCounters(player->index_nr);
3242 Delay(wait_delay_value);
3245 DrawPlayer(player); /* needed here only to cleanup last field */
3246 DrawLevelField(player->jx, player->jy); /* remove player graphic */
3248 player->is_moving = FALSE;
3251 if (IS_CUSTOM_ELEMENT(old_element))
3252 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
3254 player->index_bit, leave_side);
3256 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
3258 player->index_bit, leave_side);
3260 Feld[jx][jy] = el_player;
3261 InitPlayerField(jx, jy, el_player, TRUE);
3263 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
3265 Feld[jx][jy] = element;
3266 InitField(jx, jy, FALSE);
3269 if (player == local_player) /* only visually relocate local player */
3270 DrawRelocatePlayer(player, level.instant_relocation);
3272 TestIfPlayerTouchesBadThing(jx, jy);
3273 TestIfPlayerTouchesCustomElement(jx, jy);
3275 if (IS_CUSTOM_ELEMENT(element))
3276 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
3277 player->index_bit, enter_side);
3279 CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
3280 player->index_bit, enter_side);
3283 void Explode(int ex, int ey, int phase, int mode)
3289 /* !!! eliminate this variable !!! */
3290 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
3292 if (game.explosions_delayed)
3294 ExplodeField[ex][ey] = mode;
3298 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
3300 int center_element = Feld[ex][ey];
3301 int artwork_element, explosion_element; /* set these values later */
3304 /* --- This is only really needed (and now handled) in "Impact()". --- */
3305 /* do not explode moving elements that left the explode field in time */
3306 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
3307 center_element == EL_EMPTY &&
3308 (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
3313 /* !!! at this place, the center element may be EL_BLOCKED !!! */
3314 if (mode == EX_TYPE_NORMAL ||
3315 mode == EX_TYPE_CENTER ||
3316 mode == EX_TYPE_CROSS)
3317 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
3320 /* remove things displayed in background while burning dynamite */
3321 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
3324 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
3326 /* put moving element to center field (and let it explode there) */
3327 center_element = MovingOrBlocked2Element(ex, ey);
3328 RemoveMovingField(ex, ey);
3329 Feld[ex][ey] = center_element;
3332 /* now "center_element" is finally determined -- set related values now */
3333 artwork_element = center_element; /* for custom player artwork */
3334 explosion_element = center_element; /* for custom player artwork */
3336 if (IS_PLAYER(ex, ey))
3338 int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
3340 artwork_element = stored_player[player_nr].artwork_element;
3342 if (level.use_explosion_element[player_nr])
3344 explosion_element = level.explosion_element[player_nr];
3345 artwork_element = explosion_element;
3350 if (mode == EX_TYPE_NORMAL ||
3351 mode == EX_TYPE_CENTER ||
3352 mode == EX_TYPE_CROSS)
3353 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
3357 last_phase = element_info[explosion_element].explosion_delay + 1;
3359 last_phase = element_info[center_element].explosion_delay + 1;
3362 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
3364 int xx = x - ex + 1;
3365 int yy = y - ey + 1;
3368 if (!IN_LEV_FIELD(x, y) ||
3369 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
3370 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
3373 element = Feld[x][y];
3375 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
3377 element = MovingOrBlocked2Element(x, y);
3379 if (!IS_EXPLOSION_PROOF(element))
3380 RemoveMovingField(x, y);
3383 /* indestructible elements can only explode in center (but not flames) */
3384 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
3385 mode == EX_TYPE_BORDER)) ||
3386 element == EL_FLAMES)
3389 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
3390 behaviour, for example when touching a yamyam that explodes to rocks
3391 with active deadly shield, a rock is created under the player !!! */
3392 /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
3394 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
3395 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
3396 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
3398 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
3401 if (IS_ACTIVE_BOMB(element))
3403 /* re-activate things under the bomb like gate or penguin */
3404 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
3411 /* save walkable background elements while explosion on same tile */
3412 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
3413 (x != ex || y != ey || mode == EX_TYPE_BORDER))
3414 Back[x][y] = element;
3416 /* ignite explodable elements reached by other explosion */
3417 if (element == EL_EXPLOSION)
3418 element = Store2[x][y];
3420 if (AmoebaNr[x][y] &&
3421 (element == EL_AMOEBA_FULL ||
3422 element == EL_BD_AMOEBA ||
3423 element == EL_AMOEBA_GROWING))
3425 AmoebaCnt[AmoebaNr[x][y]]--;
3426 AmoebaCnt2[AmoebaNr[x][y]]--;
3431 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
3434 int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
3436 Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
3438 switch(StorePlayer[ex][ey])
3441 Store[x][y] = EL_PLAYER_IS_EXPLODING_2;
3444 Store[x][y] = EL_PLAYER_IS_EXPLODING_3;
3447 Store[x][y] = EL_PLAYER_IS_EXPLODING_4;
3451 Store[x][y] = EL_PLAYER_IS_EXPLODING_1;
3456 if (PLAYERINFO(ex, ey)->use_murphy)
3457 Store[x][y] = EL_EMPTY;
3460 /* !!! check this case -- currently needed for rnd_rado_negundo_v,
3461 !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
3462 else if (ELEM_IS_PLAYER(center_element))
3463 Store[x][y] = EL_EMPTY;
3464 else if (center_element == EL_YAMYAM)
3465 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
3466 else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
3467 Store[x][y] = element_info[center_element].content.e[xx][yy];
3469 /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
3470 (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
3471 otherwise) -- FIX THIS !!! */
3472 else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
3473 Store[x][y] = element_info[element].content.e[1][1];
3475 else if (!CAN_EXPLODE(element))
3476 Store[x][y] = element_info[element].content.e[1][1];
3479 Store[x][y] = EL_EMPTY;
3481 else if (center_element == EL_MOLE)
3482 Store[x][y] = EL_EMERALD_RED;
3483 else if (center_element == EL_PENGUIN)
3484 Store[x][y] = EL_EMERALD_PURPLE;
3485 else if (center_element == EL_BUG)
3486 Store[x][y] = ((x == ex && y == ey) ? EL_DIAMOND : EL_EMERALD);
3487 else if (center_element == EL_BD_BUTTERFLY)
3488 Store[x][y] = EL_BD_DIAMOND;
3489 else if (center_element == EL_SP_ELECTRON)
3490 Store[x][y] = EL_SP_INFOTRON;
3491 else if (center_element == EL_AMOEBA_TO_DIAMOND)
3492 Store[x][y] = level.amoeba_content;
3493 else if (center_element == EL_YAMYAM)
3494 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
3495 else if (IS_CUSTOM_ELEMENT(center_element) &&
3496 element_info[center_element].content.e[xx][yy] != EL_EMPTY)
3497 Store[x][y] = element_info[center_element].content.e[xx][yy];
3498 else if (element == EL_WALL_EMERALD)
3499 Store[x][y] = EL_EMERALD;
3500 else if (element == EL_WALL_DIAMOND)
3501 Store[x][y] = EL_DIAMOND;
3502 else if (element == EL_WALL_BD_DIAMOND)
3503 Store[x][y] = EL_BD_DIAMOND;
3504 else if (element == EL_WALL_EMERALD_YELLOW)
3505 Store[x][y] = EL_EMERALD_YELLOW;
3506 else if (element == EL_WALL_EMERALD_RED)
3507 Store[x][y] = EL_EMERALD_RED;
3508 else if (element == EL_WALL_EMERALD_PURPLE)
3509 Store[x][y] = EL_EMERALD_PURPLE;
3510 else if (element == EL_WALL_PEARL)
3511 Store[x][y] = EL_PEARL;
3512 else if (element == EL_WALL_CRYSTAL)
3513 Store[x][y] = EL_CRYSTAL;
3514 else if (IS_CUSTOM_ELEMENT(element) && !CAN_EXPLODE(element))
3515 Store[x][y] = element_info[element].content.e[1][1];
3517 Store[x][y] = EL_EMPTY;
3520 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
3521 center_element == EL_AMOEBA_TO_DIAMOND)
3522 Store2[x][y] = element;
3524 Feld[x][y] = EL_EXPLOSION;
3525 GfxElement[x][y] = artwork_element;
3527 ExplodePhase[x][y] = 1;
3528 ExplodeDelay[x][y] = last_phase;
3533 if (center_element == EL_YAMYAM)
3534 game.yamyam_content_nr =
3535 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
3547 GfxFrame[x][y] = 0; /* restart explosion animation */
3549 last_phase = ExplodeDelay[x][y];
3551 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
3555 /* activate this even in non-DEBUG version until cause for crash in
3556 getGraphicAnimationFrame() (see below) is found and eliminated */
3561 if (GfxElement[x][y] == EL_UNDEFINED)
3564 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
3565 printf("Explode(): This should never happen!\n");
3568 GfxElement[x][y] = EL_EMPTY;
3572 border_element = Store2[x][y];
3573 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3574 border_element = StorePlayer[x][y];
3576 if (phase == element_info[border_element].ignition_delay ||
3577 phase == last_phase)
3579 boolean border_explosion = FALSE;
3581 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
3582 !PLAYER_EXPLOSION_PROTECTED(x, y))
3584 KillPlayerUnlessExplosionProtected(x, y);
3585 border_explosion = TRUE;
3587 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
3589 Feld[x][y] = Store2[x][y];
3592 border_explosion = TRUE;
3594 else if (border_element == EL_AMOEBA_TO_DIAMOND)
3596 AmoebeUmwandeln(x, y);
3598 border_explosion = TRUE;
3601 /* if an element just explodes due to another explosion (chain-reaction),
3602 do not immediately end the new explosion when it was the last frame of
3603 the explosion (as it would be done in the following "if"-statement!) */
3604 if (border_explosion && phase == last_phase)
3608 if (phase == last_phase)
3612 element = Feld[x][y] = Store[x][y];
3613 Store[x][y] = Store2[x][y] = 0;
3614 GfxElement[x][y] = EL_UNDEFINED;
3616 /* player can escape from explosions and might therefore be still alive */
3617 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
3618 element <= EL_PLAYER_IS_EXPLODING_4)
3620 int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
3621 int explosion_element = EL_PLAYER_1 + player_nr;
3622 int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
3623 int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
3625 if (level.use_explosion_element[player_nr])
3626 explosion_element = level.explosion_element[player_nr];
3628 Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
3629 element_info[explosion_element].content.e[xx][yy]);
3632 /* restore probably existing indestructible background element */
3633 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
3634 element = Feld[x][y] = Back[x][y];
3637 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
3638 GfxDir[x][y] = MV_NONE;
3639 ChangeDelay[x][y] = 0;
3640 ChangePage[x][y] = -1;
3642 #if USE_NEW_CUSTOM_VALUE
3643 CustomValue[x][y] = 0;
3646 InitField_WithBug2(x, y, FALSE);
3648 DrawLevelField(x, y);
3650 TestIfElementTouchesCustomElement(x, y);
3652 if (GFX_CRUMBLED(element))
3653 DrawLevelFieldCrumbledSandNeighbours(x, y);
3655 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
3656 StorePlayer[x][y] = 0;
3658 if (ELEM_IS_PLAYER(element))
3659 RelocatePlayer(x, y, element);
3661 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3663 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
3664 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
3667 DrawLevelFieldCrumbledSand(x, y);
3669 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
3671 DrawLevelElement(x, y, Back[x][y]);
3672 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
3674 else if (IS_WALKABLE_UNDER(Back[x][y]))
3676 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3677 DrawLevelElementThruMask(x, y, Back[x][y]);
3679 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
3680 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3684 void DynaExplode(int ex, int ey)
3687 int dynabomb_element = Feld[ex][ey];
3688 int dynabomb_size = 1;
3689 boolean dynabomb_xl = FALSE;
3690 struct PlayerInfo *player;
3691 static int xy[4][2] =
3699 if (IS_ACTIVE_BOMB(dynabomb_element))
3701 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
3702 dynabomb_size = player->dynabomb_size;
3703 dynabomb_xl = player->dynabomb_xl;
3704 player->dynabombs_left++;
3707 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
3709 for (i = 0; i < NUM_DIRECTIONS; i++)
3711 for (j = 1; j <= dynabomb_size; j++)
3713 int x = ex + j * xy[i][0];
3714 int y = ey + j * xy[i][1];
3717 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
3720 element = Feld[x][y];
3722 /* do not restart explosions of fields with active bombs */
3723 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
3726 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
3728 if (element != EL_EMPTY && element != EL_EXPLOSION &&
3729 !IS_DIGGABLE(element) && !dynabomb_xl)
3735 void Bang(int x, int y)
3737 int element = MovingOrBlocked2Element(x, y);
3738 int explosion_type = EX_TYPE_NORMAL;
3740 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3742 struct PlayerInfo *player = PLAYERINFO(x, y);
3744 element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
3745 player->element_nr);
3747 if (level.use_explosion_element[player->index_nr])
3749 int explosion_element = level.explosion_element[player->index_nr];
3751 if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
3752 explosion_type = EX_TYPE_CROSS;
3753 else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
3754 explosion_type = EX_TYPE_CENTER;
3762 case EL_BD_BUTTERFLY:
3765 case EL_DARK_YAMYAM:
3769 RaiseScoreElement(element);
3772 case EL_DYNABOMB_PLAYER_1_ACTIVE:
3773 case EL_DYNABOMB_PLAYER_2_ACTIVE:
3774 case EL_DYNABOMB_PLAYER_3_ACTIVE:
3775 case EL_DYNABOMB_PLAYER_4_ACTIVE:
3776 case EL_DYNABOMB_INCREASE_NUMBER:
3777 case EL_DYNABOMB_INCREASE_SIZE:
3778 case EL_DYNABOMB_INCREASE_POWER:
3779 explosion_type = EX_TYPE_DYNA;
3784 case EL_LAMP_ACTIVE:
3785 case EL_AMOEBA_TO_DIAMOND:
3786 if (!IS_PLAYER(x, y)) /* penguin and player may be at same field */
3787 explosion_type = EX_TYPE_CENTER;
3791 if (element_info[element].explosion_type == EXPLODES_CROSS)
3792 explosion_type = EX_TYPE_CROSS;
3793 else if (element_info[element].explosion_type == EXPLODES_1X1)
3794 explosion_type = EX_TYPE_CENTER;
3798 if (explosion_type == EX_TYPE_DYNA)
3801 Explode(x, y, EX_PHASE_START, explosion_type);
3803 CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
3806 void SplashAcid(int x, int y)
3808 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
3809 (!IN_LEV_FIELD(x - 1, y - 2) ||
3810 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
3811 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
3813 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
3814 (!IN_LEV_FIELD(x + 1, y - 2) ||
3815 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
3816 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
3818 PlayLevelSound(x, y, SND_ACID_SPLASHING);
3821 static void InitBeltMovement()
3823 static int belt_base_element[4] =
3825 EL_CONVEYOR_BELT_1_LEFT,
3826 EL_CONVEYOR_BELT_2_LEFT,
3827 EL_CONVEYOR_BELT_3_LEFT,
3828 EL_CONVEYOR_BELT_4_LEFT
3830 static int belt_base_active_element[4] =
3832 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
3833 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
3834 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
3835 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
3840 /* set frame order for belt animation graphic according to belt direction */
3841 for (i = 0; i < NUM_BELTS; i++)
3845 for (j = 0; j < NUM_BELT_PARTS; j++)
3847 int element = belt_base_active_element[belt_nr] + j;
3848 int graphic = el2img(element);
3850 if (game.belt_dir[i] == MV_LEFT)
3851 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
3853 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
3858 SCAN_PLAYFIELD(x, y)
3860 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
3863 int element = Feld[x][y];
3865 for (i = 0; i < NUM_BELTS; i++)
3867 if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
3869 int e_belt_nr = getBeltNrFromBeltElement(element);
3872 if (e_belt_nr == belt_nr)
3874 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
3876 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
3883 static void ToggleBeltSwitch(int x, int y)
3885 static int belt_base_element[4] =
3887 EL_CONVEYOR_BELT_1_LEFT,
3888 EL_CONVEYOR_BELT_2_LEFT,
3889 EL_CONVEYOR_BELT_3_LEFT,
3890 EL_CONVEYOR_BELT_4_LEFT
3892 static int belt_base_active_element[4] =
3894 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
3895 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
3896 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
3897 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
3899 static int belt_base_switch_element[4] =
3901 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
3902 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
3903 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
3904 EL_CONVEYOR_BELT_4_SWITCH_LEFT
3906 static int belt_move_dir[4] =
3914 int element = Feld[x][y];
3915 int belt_nr = getBeltNrFromBeltSwitchElement(element);
3916 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
3917 int belt_dir = belt_move_dir[belt_dir_nr];
3920 if (!IS_BELT_SWITCH(element))
3923 game.belt_dir_nr[belt_nr] = belt_dir_nr;
3924 game.belt_dir[belt_nr] = belt_dir;
3926 if (belt_dir_nr == 3)
3929 /* set frame order for belt animation graphic according to belt direction */
3930 for (i = 0; i < NUM_BELT_PARTS; i++)
3932 int element = belt_base_active_element[belt_nr] + i;
3933 int graphic = el2img(element);
3935 if (belt_dir == MV_LEFT)
3936 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
3938 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
3942 SCAN_PLAYFIELD(xx, yy)
3944 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
3947 int element = Feld[xx][yy];
3949 if (IS_BELT_SWITCH(element))
3951 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
3953 if (e_belt_nr == belt_nr)
3955 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
3956 DrawLevelField(xx, yy);
3959 else if (IS_BELT(element) && belt_dir != MV_NONE)
3961 int e_belt_nr = getBeltNrFromBeltElement(element);
3963 if (e_belt_nr == belt_nr)
3965 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
3967 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
3968 DrawLevelField(xx, yy);
3971 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
3973 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
3975 if (e_belt_nr == belt_nr)
3977 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
3979 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
3980 DrawLevelField(xx, yy);
3986 static void ToggleSwitchgateSwitch(int x, int y)
3990 game.switchgate_pos = !game.switchgate_pos;
3993 SCAN_PLAYFIELD(xx, yy)
3995 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
3998 int element = Feld[xx][yy];
4000 if (element == EL_SWITCHGATE_SWITCH_UP ||
4001 element == EL_SWITCHGATE_SWITCH_DOWN)
4003 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
4004 DrawLevelField(xx, yy);
4006 else if (element == EL_SWITCHGATE_OPEN ||
4007 element == EL_SWITCHGATE_OPENING)
4009 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
4011 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
4013 else if (element == EL_SWITCHGATE_CLOSED ||
4014 element == EL_SWITCHGATE_CLOSING)
4016 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
4018 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
4023 static int getInvisibleActiveFromInvisibleElement(int element)
4025 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
4026 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
4027 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
4031 static int getInvisibleFromInvisibleActiveElement(int element)
4033 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
4034 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
4035 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
4039 static void RedrawAllLightSwitchesAndInvisibleElements()
4044 SCAN_PLAYFIELD(x, y)
4046 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
4049 int element = Feld[x][y];
4051 if (element == EL_LIGHT_SWITCH &&
4052 game.light_time_left > 0)
4054 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
4055 DrawLevelField(x, y);
4057 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
4058 game.light_time_left == 0)
4060 Feld[x][y] = EL_LIGHT_SWITCH;
4061 DrawLevelField(x, y);
4063 else if (element == EL_EMC_DRIPPER &&
4064 game.light_time_left > 0)
4066 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
4067 DrawLevelField(x, y);
4069 else if (element == EL_EMC_DRIPPER_ACTIVE &&
4070 game.light_time_left == 0)
4072 Feld[x][y] = EL_EMC_DRIPPER;
4073 DrawLevelField(x, y);
4075 else if (element == EL_INVISIBLE_STEELWALL ||
4076 element == EL_INVISIBLE_WALL ||
4077 element == EL_INVISIBLE_SAND)
4079 if (game.light_time_left > 0)
4080 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
4082 DrawLevelField(x, y);
4084 /* uncrumble neighbour fields, if needed */
4085 if (element == EL_INVISIBLE_SAND)
4086 DrawLevelFieldCrumbledSandNeighbours(x, y);
4088 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
4089 element == EL_INVISIBLE_WALL_ACTIVE ||
4090 element == EL_INVISIBLE_SAND_ACTIVE)
4092 if (game.light_time_left == 0)
4093 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
4095 DrawLevelField(x, y);
4097 /* re-crumble neighbour fields, if needed */
4098 if (element == EL_INVISIBLE_SAND)
4099 DrawLevelFieldCrumbledSandNeighbours(x, y);
4104 static void RedrawAllInvisibleElementsForLenses()
4109 SCAN_PLAYFIELD(x, y)
4111 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
4114 int element = Feld[x][y];
4116 if (element == EL_EMC_DRIPPER &&
4117 game.lenses_time_left > 0)
4119 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
4120 DrawLevelField(x, y);
4122 else if (element == EL_EMC_DRIPPER_ACTIVE &&
4123 game.lenses_time_left == 0)
4125 Feld[x][y] = EL_EMC_DRIPPER;
4126 DrawLevelField(x, y);
4128 else if (element == EL_INVISIBLE_STEELWALL ||
4129 element == EL_INVISIBLE_WALL ||
4130 element == EL_INVISIBLE_SAND)
4132 if (game.lenses_time_left > 0)
4133 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
4135 DrawLevelField(x, y);
4137 /* uncrumble neighbour fields, if needed */
4138 if (element == EL_INVISIBLE_SAND)
4139 DrawLevelFieldCrumbledSandNeighbours(x, y);
4141 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
4142 element == EL_INVISIBLE_WALL_ACTIVE ||
4143 element == EL_INVISIBLE_SAND_ACTIVE)
4145 if (game.lenses_time_left == 0)
4146 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
4148 DrawLevelField(x, y);
4150 /* re-crumble neighbour fields, if needed */
4151 if (element == EL_INVISIBLE_SAND)
4152 DrawLevelFieldCrumbledSandNeighbours(x, y);
4157 static void RedrawAllInvisibleElementsForMagnifier()
4162 SCAN_PLAYFIELD(x, y)
4164 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
4167 int element = Feld[x][y];
4169 if (element == EL_EMC_FAKE_GRASS &&
4170 game.magnify_time_left > 0)
4172 Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
4173 DrawLevelField(x, y);
4175 else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
4176 game.magnify_time_left == 0)
4178 Feld[x][y] = EL_EMC_FAKE_GRASS;
4179 DrawLevelField(x, y);
4181 else if (IS_GATE_GRAY(element) &&
4182 game.magnify_time_left > 0)
4184 Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
4185 element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
4186 IS_EM_GATE_GRAY(element) ?
4187 element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
4188 IS_EMC_GATE_GRAY(element) ?
4189 element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
4191 DrawLevelField(x, y);
4193 else if (IS_GATE_GRAY_ACTIVE(element) &&
4194 game.magnify_time_left == 0)
4196 Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
4197 element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
4198 IS_EM_GATE_GRAY_ACTIVE(element) ?
4199 element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
4200 IS_EMC_GATE_GRAY_ACTIVE(element) ?
4201 element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
4203 DrawLevelField(x, y);
4208 static void ToggleLightSwitch(int x, int y)
4210 int element = Feld[x][y];
4212 game.light_time_left =
4213 (element == EL_LIGHT_SWITCH ?
4214 level.time_light * FRAMES_PER_SECOND : 0);
4216 RedrawAllLightSwitchesAndInvisibleElements();
4219 static void ActivateTimegateSwitch(int x, int y)
4223 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
4226 SCAN_PLAYFIELD(xx, yy)
4228 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
4231 int element = Feld[xx][yy];
4233 if (element == EL_TIMEGATE_CLOSED ||
4234 element == EL_TIMEGATE_CLOSING)
4236 Feld[xx][yy] = EL_TIMEGATE_OPENING;
4237 PlayLevelSound(xx, yy, SND_TIMEGATE_OPENING);
4241 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
4243 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
4244 DrawLevelField(xx, yy);
4250 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
4253 void Impact(int x, int y)
4255 boolean last_line = (y == lev_fieldy - 1);
4256 boolean object_hit = FALSE;
4257 boolean impact = (last_line || object_hit);
4258 int element = Feld[x][y];
4259 int smashed = EL_STEELWALL;
4261 if (!last_line) /* check if element below was hit */
4263 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
4266 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
4267 MovDir[x][y + 1] != MV_DOWN ||
4268 MovPos[x][y + 1] <= TILEY / 2));
4270 /* do not smash moving elements that left the smashed field in time */
4271 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
4272 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
4275 #if USE_QUICKSAND_IMPACT_BUGFIX
4276 if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
4278 RemoveMovingField(x, y + 1);
4279 Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
4280 Feld[x][y + 2] = EL_ROCK;
4281 DrawLevelField(x, y + 2);
4288 smashed = MovingOrBlocked2Element(x, y + 1);
4290 impact = (last_line || object_hit);
4293 if (!last_line && smashed == EL_ACID) /* element falls into acid */
4295 SplashAcid(x, y + 1);
4299 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
4300 /* only reset graphic animation if graphic really changes after impact */
4302 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
4304 ResetGfxAnimation(x, y);
4305 DrawLevelField(x, y);
4308 if (impact && CAN_EXPLODE_IMPACT(element))
4313 else if (impact && element == EL_PEARL)
4315 ResetGfxAnimation(x, y);
4317 Feld[x][y] = EL_PEARL_BREAKING;
4318 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4321 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
4323 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4328 if (impact && element == EL_AMOEBA_DROP)
4330 if (object_hit && IS_PLAYER(x, y + 1))
4331 KillPlayerUnlessEnemyProtected(x, y + 1);
4332 else if (object_hit && smashed == EL_PENGUIN)
4336 Feld[x][y] = EL_AMOEBA_GROWING;
4337 Store[x][y] = EL_AMOEBA_WET;
4339 ResetRandomAnimationValue(x, y);
4344 if (object_hit) /* check which object was hit */
4346 if (CAN_PASS_MAGIC_WALL(element) &&
4347 (smashed == EL_MAGIC_WALL ||
4348 smashed == EL_BD_MAGIC_WALL))
4351 int activated_magic_wall =
4352 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
4353 EL_BD_MAGIC_WALL_ACTIVE);
4355 /* activate magic wall / mill */
4357 SCAN_PLAYFIELD(xx, yy)
4359 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
4361 if (Feld[xx][yy] == smashed)
4362 Feld[xx][yy] = activated_magic_wall;
4364 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
4365 game.magic_wall_active = TRUE;
4367 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
4368 SND_MAGIC_WALL_ACTIVATING :
4369 SND_BD_MAGIC_WALL_ACTIVATING));
4372 if (IS_PLAYER(x, y + 1))
4374 if (CAN_SMASH_PLAYER(element))
4376 KillPlayerUnlessEnemyProtected(x, y + 1);
4380 else if (smashed == EL_PENGUIN)
4382 if (CAN_SMASH_PLAYER(element))
4388 else if (element == EL_BD_DIAMOND)
4390 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
4396 else if (((element == EL_SP_INFOTRON ||
4397 element == EL_SP_ZONK) &&
4398 (smashed == EL_SP_SNIKSNAK ||
4399 smashed == EL_SP_ELECTRON ||
4400 smashed == EL_SP_DISK_ORANGE)) ||
4401 (element == EL_SP_INFOTRON &&
4402 smashed == EL_SP_DISK_YELLOW))
4407 else if (CAN_SMASH_EVERYTHING(element))
4409 if (IS_CLASSIC_ENEMY(smashed) ||
4410 CAN_EXPLODE_SMASHED(smashed))
4415 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
4417 if (smashed == EL_LAMP ||
4418 smashed == EL_LAMP_ACTIVE)
4423 else if (smashed == EL_NUT)
4425 Feld[x][y + 1] = EL_NUT_BREAKING;
4426 PlayLevelSound(x, y, SND_NUT_BREAKING);
4427 RaiseScoreElement(EL_NUT);
4430 else if (smashed == EL_PEARL)
4432 ResetGfxAnimation(x, y);
4434 Feld[x][y + 1] = EL_PEARL_BREAKING;
4435 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4438 else if (smashed == EL_DIAMOND)
4440 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
4441 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
4444 else if (IS_BELT_SWITCH(smashed))
4446 ToggleBeltSwitch(x, y + 1);
4448 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
4449 smashed == EL_SWITCHGATE_SWITCH_DOWN)
4451 ToggleSwitchgateSwitch(x, y + 1);
4453 else if (smashed == EL_LIGHT_SWITCH ||
4454 smashed == EL_LIGHT_SWITCH_ACTIVE)
4456 ToggleLightSwitch(x, y + 1);
4461 TestIfElementSmashesCustomElement(x, y, MV_DOWN);
4464 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4466 CheckElementChangeBySide(x, y + 1, smashed, element,
4467 CE_SWITCHED, CH_SIDE_TOP);
4468 CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
4474 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4479 /* play sound of magic wall / mill */
4481 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
4482 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
4484 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
4485 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
4486 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
4487 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
4492 /* play sound of object that hits the ground */
4493 if (last_line || object_hit)
4494 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4497 inline static void TurnRoundExt(int x, int y)
4509 { 0, 0 }, { 0, 0 }, { 0, 0 },
4514 int left, right, back;
4518 { MV_DOWN, MV_UP, MV_RIGHT },
4519 { MV_UP, MV_DOWN, MV_LEFT },
4521 { MV_LEFT, MV_RIGHT, MV_DOWN },
4525 { MV_RIGHT, MV_LEFT, MV_UP }
4528 int element = Feld[x][y];
4529 int move_pattern = element_info[element].move_pattern;
4531 int old_move_dir = MovDir[x][y];
4532 int left_dir = turn[old_move_dir].left;
4533 int right_dir = turn[old_move_dir].right;
4534 int back_dir = turn[old_move_dir].back;
4536 int left_dx = move_xy[left_dir].dx, left_dy = move_xy[left_dir].dy;
4537 int right_dx = move_xy[right_dir].dx, right_dy = move_xy[right_dir].dy;
4538 int move_dx = move_xy[old_move_dir].dx, move_dy = move_xy[old_move_dir].dy;
4539 int back_dx = move_xy[back_dir].dx, back_dy = move_xy[back_dir].dy;
4541 int left_x = x + left_dx, left_y = y + left_dy;
4542 int right_x = x + right_dx, right_y = y + right_dy;
4543 int move_x = x + move_dx, move_y = y + move_dy;
4547 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4549 TestIfBadThingTouchesOtherBadThing(x, y);
4551 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
4552 MovDir[x][y] = right_dir;
4553 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4554 MovDir[x][y] = left_dir;
4556 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
4558 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
4561 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
4563 TestIfBadThingTouchesOtherBadThing(x, y);
4565 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
4566 MovDir[x][y] = left_dir;
4567 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4568 MovDir[x][y] = right_dir;
4570 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
4572 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
4575 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4577 TestIfBadThingTouchesOtherBadThing(x, y);
4579 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
4580 MovDir[x][y] = left_dir;
4581 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
4582 MovDir[x][y] = right_dir;
4584 if (MovDir[x][y] != old_move_dir)
4587 else if (element == EL_YAMYAM)
4589 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
4590 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
4592 if (can_turn_left && can_turn_right)
4593 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4594 else if (can_turn_left)
4595 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4596 else if (can_turn_right)
4597 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4599 MovDir[x][y] = back_dir;
4601 MovDelay[x][y] = 16 + 16 * RND(3);
4603 else if (element == EL_DARK_YAMYAM)
4605 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4607 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4610 if (can_turn_left && can_turn_right)
4611 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4612 else if (can_turn_left)
4613 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4614 else if (can_turn_right)
4615 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4617 MovDir[x][y] = back_dir;
4619 MovDelay[x][y] = 16 + 16 * RND(3);
4621 else if (element == EL_PACMAN)
4623 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
4624 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
4626 if (can_turn_left && can_turn_right)
4627 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4628 else if (can_turn_left)
4629 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4630 else if (can_turn_right)
4631 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4633 MovDir[x][y] = back_dir;
4635 MovDelay[x][y] = 6 + RND(40);
4637 else if (element == EL_PIG)
4639 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
4640 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
4641 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
4642 boolean should_turn_left, should_turn_right, should_move_on;
4644 int rnd = RND(rnd_value);
4646 should_turn_left = (can_turn_left &&
4648 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
4649 y + back_dy + left_dy)));
4650 should_turn_right = (can_turn_right &&
4652 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
4653 y + back_dy + right_dy)));
4654 should_move_on = (can_move_on &&
4657 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
4658 y + move_dy + left_dy) ||
4659 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
4660 y + move_dy + right_dy)));
4662 if (should_turn_left || should_turn_right || should_move_on)
4664 if (should_turn_left && should_turn_right && should_move_on)
4665 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
4666 rnd < 2 * rnd_value / 3 ? right_dir :
4668 else if (should_turn_left && should_turn_right)
4669 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4670 else if (should_turn_left && should_move_on)
4671 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
4672 else if (should_turn_right && should_move_on)
4673 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
4674 else if (should_turn_left)
4675 MovDir[x][y] = left_dir;
4676 else if (should_turn_right)
4677 MovDir[x][y] = right_dir;
4678 else if (should_move_on)
4679 MovDir[x][y] = old_move_dir;
4681 else if (can_move_on && rnd > rnd_value / 8)
4682 MovDir[x][y] = old_move_dir;
4683 else if (can_turn_left && can_turn_right)
4684 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4685 else if (can_turn_left && rnd > rnd_value / 8)
4686 MovDir[x][y] = left_dir;
4687 else if (can_turn_right && rnd > rnd_value/8)
4688 MovDir[x][y] = right_dir;
4690 MovDir[x][y] = back_dir;
4692 xx = x + move_xy[MovDir[x][y]].dx;
4693 yy = y + move_xy[MovDir[x][y]].dy;
4695 if (!IN_LEV_FIELD(xx, yy) ||
4696 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
4697 MovDir[x][y] = old_move_dir;
4701 else if (element == EL_DRAGON)
4703 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
4704 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
4705 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
4707 int rnd = RND(rnd_value);
4709 if (can_move_on && rnd > rnd_value / 8)
4710 MovDir[x][y] = old_move_dir;
4711 else if (can_turn_left && can_turn_right)
4712 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4713 else if (can_turn_left && rnd > rnd_value / 8)
4714 MovDir[x][y] = left_dir;
4715 else if (can_turn_right && rnd > rnd_value / 8)
4716 MovDir[x][y] = right_dir;
4718 MovDir[x][y] = back_dir;
4720 xx = x + move_xy[MovDir[x][y]].dx;
4721 yy = y + move_xy[MovDir[x][y]].dy;
4723 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
4724 MovDir[x][y] = old_move_dir;
4728 else if (element == EL_MOLE)
4730 boolean can_move_on =
4731 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
4732 IS_AMOEBOID(Feld[move_x][move_y]) ||
4733 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
4736 boolean can_turn_left =
4737 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
4738 IS_AMOEBOID(Feld[left_x][left_y])));
4740 boolean can_turn_right =
4741 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
4742 IS_AMOEBOID(Feld[right_x][right_y])));
4744 if (can_turn_left && can_turn_right)
4745 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
4746 else if (can_turn_left)
4747 MovDir[x][y] = left_dir;
4749 MovDir[x][y] = right_dir;
4752 if (MovDir[x][y] != old_move_dir)
4755 else if (element == EL_BALLOON)
4757 MovDir[x][y] = game.wind_direction;
4760 else if (element == EL_SPRING)
4762 #if USE_NEW_SPRING_BUMPER
4763 if (MovDir[x][y] & MV_HORIZONTAL)
4765 if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
4766 !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
4768 Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
4769 ResetGfxAnimation(move_x, move_y);
4770 DrawLevelField(move_x, move_y);
4772 MovDir[x][y] = back_dir;
4774 else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
4775 SPRING_CAN_ENTER_FIELD(element, x, y + 1))
4776 MovDir[x][y] = MV_NONE;
4779 if (MovDir[x][y] & MV_HORIZONTAL &&
4780 (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
4781 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
4782 MovDir[x][y] = MV_NONE;
4787 else if (element == EL_ROBOT ||
4788 element == EL_SATELLITE ||
4789 element == EL_PENGUIN ||
4790 element == EL_EMC_ANDROID)
4792 int attr_x = -1, attr_y = -1;
4803 for (i = 0; i < MAX_PLAYERS; i++)
4805 struct PlayerInfo *player = &stored_player[i];
4806 int jx = player->jx, jy = player->jy;
4808 if (!player->active)
4812 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
4820 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
4821 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
4822 game.engine_version < VERSION_IDENT(3,1,0,0)))
4828 if (element == EL_PENGUIN)
4831 static int xy[4][2] =
4839 for (i = 0; i < NUM_DIRECTIONS; i++)
4841 int ex = x + xy[i][0];
4842 int ey = y + xy[i][1];
4844 if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
4853 MovDir[x][y] = MV_NONE;
4855 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
4856 else if (attr_x > x)
4857 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
4859 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
4860 else if (attr_y > y)
4861 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
4863 if (element == EL_ROBOT)
4867 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4868 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
4869 Moving2Blocked(x, y, &newx, &newy);
4871 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
4872 MovDelay[x][y] = 8 + 8 * !RND(3);
4874 MovDelay[x][y] = 16;
4876 else if (element == EL_PENGUIN)
4882 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4884 boolean first_horiz = RND(2);
4885 int new_move_dir = MovDir[x][y];
4888 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4889 Moving2Blocked(x, y, &newx, &newy);
4891 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
4895 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4896 Moving2Blocked(x, y, &newx, &newy);
4898 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
4901 MovDir[x][y] = old_move_dir;
4905 else if (element == EL_SATELLITE)
4911 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4913 boolean first_horiz = RND(2);
4914 int new_move_dir = MovDir[x][y];
4917 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4918 Moving2Blocked(x, y, &newx, &newy);
4920 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
4924 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4925 Moving2Blocked(x, y, &newx, &newy);
4927 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
4930 MovDir[x][y] = old_move_dir;
4934 else if (element == EL_EMC_ANDROID)
4936 static int check_pos[16] =
4938 -1, /* 0 => (invalid) */
4939 7, /* 1 => MV_LEFT */
4940 3, /* 2 => MV_RIGHT */
4941 -1, /* 3 => (invalid) */
4943 0, /* 5 => MV_LEFT | MV_UP */
4944 2, /* 6 => MV_RIGHT | MV_UP */
4945 -1, /* 7 => (invalid) */
4946 5, /* 8 => MV_DOWN */
4947 6, /* 9 => MV_LEFT | MV_DOWN */
4948 4, /* 10 => MV_RIGHT | MV_DOWN */
4949 -1, /* 11 => (invalid) */
4950 -1, /* 12 => (invalid) */
4951 -1, /* 13 => (invalid) */
4952 -1, /* 14 => (invalid) */
4953 -1, /* 15 => (invalid) */
4961 { -1, -1, MV_LEFT | MV_UP },
4963 { +1, -1, MV_RIGHT | MV_UP },
4964 { +1, 0, MV_RIGHT },
4965 { +1, +1, MV_RIGHT | MV_DOWN },
4967 { -1, +1, MV_LEFT | MV_DOWN },
4970 int start_pos, check_order;
4971 boolean can_clone = FALSE;
4974 /* check if there is any free field around current position */
4975 for (i = 0; i < 8; i++)
4977 int newx = x + check_xy[i].dx;
4978 int newy = y + check_xy[i].dy;
4980 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
4988 if (can_clone) /* randomly find an element to clone */
4992 start_pos = check_pos[RND(8)];
4993 check_order = (RND(2) ? -1 : +1);
4995 for (i = 0; i < 8; i++)
4997 int pos_raw = start_pos + i * check_order;
4998 int pos = (pos_raw + 8) % 8;
4999 int newx = x + check_xy[pos].dx;
5000 int newy = y + check_xy[pos].dy;
5002 if (ANDROID_CAN_CLONE_FIELD(newx, newy))
5004 element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
5005 element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
5007 Store[x][y] = Feld[newx][newy];
5016 if (can_clone) /* randomly find a direction to move */
5020 start_pos = check_pos[RND(8)];
5021 check_order = (RND(2) ? -1 : +1);
5023 for (i = 0; i < 8; i++)
5025 int pos_raw = start_pos + i * check_order;
5026 int pos = (pos_raw + 8) % 8;
5027 int newx = x + check_xy[pos].dx;
5028 int newy = y + check_xy[pos].dy;
5029 int new_move_dir = check_xy[pos].dir;
5031 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5033 MovDir[x][y] = new_move_dir;
5034 MovDelay[x][y] = level.android_clone_time * 8 + 1;
5043 if (can_clone) /* cloning and moving successful */
5046 /* cannot clone -- try to move towards player */
5048 start_pos = check_pos[MovDir[x][y] & 0x0f];
5049 check_order = (RND(2) ? -1 : +1);
5051 for (i = 0; i < 3; i++)
5053 /* first check start_pos, then previous/next or (next/previous) pos */
5054 int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
5055 int pos = (pos_raw + 8) % 8;
5056 int newx = x + check_xy[pos].dx;
5057 int newy = y + check_xy[pos].dy;
5058 int new_move_dir = check_xy[pos].dir;
5060 if (IS_PLAYER(newx, newy))
5063 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
5065 MovDir[x][y] = new_move_dir;
5066 MovDelay[x][y] = level.android_move_time * 8 + 1;
5073 else if (move_pattern == MV_TURNING_LEFT ||
5074 move_pattern == MV_TURNING_RIGHT ||
5075 move_pattern == MV_TURNING_LEFT_RIGHT ||
5076 move_pattern == MV_TURNING_RIGHT_LEFT ||
5077 move_pattern == MV_TURNING_RANDOM ||
5078 move_pattern == MV_ALL_DIRECTIONS)
5080 boolean can_turn_left =
5081 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
5082 boolean can_turn_right =
5083 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
5085 if (element_info[element].move_stepsize == 0) /* "not moving" */
5088 if (move_pattern == MV_TURNING_LEFT)
5089 MovDir[x][y] = left_dir;
5090 else if (move_pattern == MV_TURNING_RIGHT)
5091 MovDir[x][y] = right_dir;
5092 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
5093 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
5094 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
5095 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
5096 else if (move_pattern == MV_TURNING_RANDOM)
5097 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
5098 can_turn_right && !can_turn_left ? right_dir :
5099 RND(2) ? left_dir : right_dir);
5100 else if (can_turn_left && can_turn_right)
5101 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5102 else if (can_turn_left)
5103 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5104 else if (can_turn_right)
5105 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5107 MovDir[x][y] = back_dir;
5109 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5111 else if (move_pattern == MV_HORIZONTAL ||
5112 move_pattern == MV_VERTICAL)
5114 if (move_pattern & old_move_dir)
5115 MovDir[x][y] = back_dir;
5116 else if (move_pattern == MV_HORIZONTAL)
5117 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
5118 else if (move_pattern == MV_VERTICAL)
5119 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
5121 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5123 else if (move_pattern & MV_ANY_DIRECTION)
5125 MovDir[x][y] = move_pattern;
5126 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5128 else if (move_pattern & MV_WIND_DIRECTION)
5130 MovDir[x][y] = game.wind_direction;
5131 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5133 else if (move_pattern == MV_ALONG_LEFT_SIDE)
5135 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
5136 MovDir[x][y] = left_dir;
5137 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5138 MovDir[x][y] = right_dir;
5140 if (MovDir[x][y] != old_move_dir)
5141 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5143 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
5145 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
5146 MovDir[x][y] = right_dir;
5147 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5148 MovDir[x][y] = left_dir;
5150 if (MovDir[x][y] != old_move_dir)
5151 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5153 else if (move_pattern == MV_TOWARDS_PLAYER ||
5154 move_pattern == MV_AWAY_FROM_PLAYER)
5156 int attr_x = -1, attr_y = -1;
5158 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
5169 for (i = 0; i < MAX_PLAYERS; i++)
5171 struct PlayerInfo *player = &stored_player[i];
5172 int jx = player->jx, jy = player->jy;
5174 if (!player->active)
5178 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
5186 MovDir[x][y] = MV_NONE;
5188 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
5189 else if (attr_x > x)
5190 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
5192 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
5193 else if (attr_y > y)
5194 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
5196 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5198 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5200 boolean first_horiz = RND(2);
5201 int new_move_dir = MovDir[x][y];
5203 if (element_info[element].move_stepsize == 0) /* "not moving" */
5205 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
5206 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5212 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5213 Moving2Blocked(x, y, &newx, &newy);
5215 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5219 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5220 Moving2Blocked(x, y, &newx, &newy);
5222 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5225 MovDir[x][y] = old_move_dir;
5228 else if (move_pattern == MV_WHEN_PUSHED ||
5229 move_pattern == MV_WHEN_DROPPED)
5231 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5232 MovDir[x][y] = MV_NONE;
5236 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
5238 static int test_xy[7][2] =
5248 static int test_dir[7] =
5258 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
5259 int move_preference = -1000000; /* start with very low preference */
5260 int new_move_dir = MV_NONE;
5261 int start_test = RND(4);
5264 for (i = 0; i < NUM_DIRECTIONS; i++)
5266 int move_dir = test_dir[start_test + i];
5267 int move_dir_preference;
5269 xx = x + test_xy[start_test + i][0];
5270 yy = y + test_xy[start_test + i][1];
5272 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
5273 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
5275 new_move_dir = move_dir;
5280 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
5283 move_dir_preference = -1 * RunnerVisit[xx][yy];
5284 if (hunter_mode && PlayerVisit[xx][yy] > 0)
5285 move_dir_preference = PlayerVisit[xx][yy];
5287 if (move_dir_preference > move_preference)
5289 /* prefer field that has not been visited for the longest time */
5290 move_preference = move_dir_preference;
5291 new_move_dir = move_dir;
5293 else if (move_dir_preference == move_preference &&
5294 move_dir == old_move_dir)
5296 /* prefer last direction when all directions are preferred equally */
5297 move_preference = move_dir_preference;
5298 new_move_dir = move_dir;
5302 MovDir[x][y] = new_move_dir;
5303 if (old_move_dir != new_move_dir)
5304 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5308 static void TurnRound(int x, int y)
5310 int direction = MovDir[x][y];
5312 int element, graphic;
5317 GfxDir[x][y] = MovDir[x][y];
5319 if (direction != MovDir[x][y])
5323 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
5326 element = Feld[x][y];
5327 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5329 if (graphic_info[graphic].anim_global_sync)
5330 GfxFrame[x][y] = FrameCounter;
5331 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5332 GfxFrame[x][y] = CustomValue[x][y];
5333 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5334 GfxFrame[x][y] = element_info[element].collect_score;
5338 static boolean JustBeingPushed(int x, int y)
5342 for (i = 0; i < MAX_PLAYERS; i++)
5344 struct PlayerInfo *player = &stored_player[i];
5346 if (player->active && player->is_pushing && player->MovPos)
5348 int next_jx = player->jx + (player->jx - player->last_jx);
5349 int next_jy = player->jy + (player->jy - player->last_jy);
5351 if (x == next_jx && y == next_jy)
5359 void StartMoving(int x, int y)
5361 boolean started_moving = FALSE; /* some elements can fall _and_ move */
5362 int element = Feld[x][y];
5367 if (MovDelay[x][y] == 0)
5368 GfxAction[x][y] = ACTION_DEFAULT;
5370 if (CAN_FALL(element) && y < lev_fieldy - 1)
5372 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
5373 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
5374 if (JustBeingPushed(x, y))
5377 if (element == EL_QUICKSAND_FULL)
5379 if (IS_FREE(x, y + 1))
5381 InitMovingField(x, y, MV_DOWN);
5382 started_moving = TRUE;
5384 Feld[x][y] = EL_QUICKSAND_EMPTYING;
5385 Store[x][y] = EL_ROCK;
5387 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
5389 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5391 if (!MovDelay[x][y])
5392 MovDelay[x][y] = TILEY + 1;
5401 Feld[x][y] = EL_QUICKSAND_EMPTY;
5402 Feld[x][y + 1] = EL_QUICKSAND_FULL;
5403 Store[x][y + 1] = Store[x][y];
5406 PlayLevelSoundAction(x, y, ACTION_FILLING);
5409 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
5410 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5412 InitMovingField(x, y, MV_DOWN);
5413 started_moving = TRUE;
5415 Feld[x][y] = EL_QUICKSAND_FILLING;
5416 Store[x][y] = element;
5418 PlayLevelSoundAction(x, y, ACTION_FILLING);
5420 else if (element == EL_MAGIC_WALL_FULL)
5422 if (IS_FREE(x, y + 1))
5424 InitMovingField(x, y, MV_DOWN);
5425 started_moving = TRUE;
5427 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
5428 Store[x][y] = EL_CHANGED(Store[x][y]);
5430 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
5432 if (!MovDelay[x][y])
5433 MovDelay[x][y] = TILEY/4 + 1;
5442 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
5443 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
5444 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
5448 else if (element == EL_BD_MAGIC_WALL_FULL)
5450 if (IS_FREE(x, y + 1))
5452 InitMovingField(x, y, MV_DOWN);
5453 started_moving = TRUE;
5455 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
5456 Store[x][y] = EL_CHANGED2(Store[x][y]);
5458 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
5460 if (!MovDelay[x][y])
5461 MovDelay[x][y] = TILEY/4 + 1;
5470 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
5471 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
5472 Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
5476 else if (CAN_PASS_MAGIC_WALL(element) &&
5477 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
5478 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
5480 InitMovingField(x, y, MV_DOWN);
5481 started_moving = TRUE;
5484 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
5485 EL_BD_MAGIC_WALL_FILLING);
5486 Store[x][y] = element;
5488 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
5490 SplashAcid(x, y + 1);
5492 InitMovingField(x, y, MV_DOWN);
5493 started_moving = TRUE;
5495 Store[x][y] = EL_ACID;
5497 else if ((game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5498 CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
5500 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
5501 CAN_FALL(element) && WasJustFalling[x][y] &&
5502 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
5504 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
5505 CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
5506 (Feld[x][y + 1] == EL_BLOCKED)))
5508 /* this is needed for a special case not covered by calling "Impact()"
5509 from "ContinueMoving()": if an element moves to a tile directly below
5510 another element which was just falling on that tile (which was empty
5511 in the previous frame), the falling element above would just stop
5512 instead of smashing the element below (in previous version, the above
5513 element was just checked for "moving" instead of "falling", resulting
5514 in incorrect smashes caused by horizontal movement of the above
5515 element; also, the case of the player being the element to smash was
5516 simply not covered here... :-/ ) */
5518 CheckCollision[x][y] = 0;
5522 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
5524 if (MovDir[x][y] == MV_NONE)
5526 InitMovingField(x, y, MV_DOWN);
5527 started_moving = TRUE;
5530 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
5532 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
5533 MovDir[x][y] = MV_DOWN;
5535 InitMovingField(x, y, MV_DOWN);
5536 started_moving = TRUE;
5538 else if (element == EL_AMOEBA_DROP)
5540 Feld[x][y] = EL_AMOEBA_GROWING;
5541 Store[x][y] = EL_AMOEBA_WET;
5543 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
5544 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
5545 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
5546 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
5548 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
5549 (IS_FREE(x - 1, y + 1) ||
5550 Feld[x - 1][y + 1] == EL_ACID));
5551 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
5552 (IS_FREE(x + 1, y + 1) ||
5553 Feld[x + 1][y + 1] == EL_ACID));
5554 boolean can_fall_any = (can_fall_left || can_fall_right);
5555 boolean can_fall_both = (can_fall_left && can_fall_right);
5556 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
5558 #if USE_NEW_ALL_SLIPPERY
5559 if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
5561 if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
5562 can_fall_right = FALSE;
5563 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
5564 can_fall_left = FALSE;
5565 else if (slippery_type == SLIPPERY_ONLY_LEFT)
5566 can_fall_right = FALSE;
5567 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
5568 can_fall_left = FALSE;
5570 can_fall_any = (can_fall_left || can_fall_right);
5571 can_fall_both = FALSE;
5574 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
5576 if (slippery_type == SLIPPERY_ONLY_LEFT)
5577 can_fall_right = FALSE;
5578 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
5579 can_fall_left = FALSE;
5580 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
5581 can_fall_right = FALSE;
5582 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
5583 can_fall_left = FALSE;
5585 can_fall_any = (can_fall_left || can_fall_right);
5586 can_fall_both = (can_fall_left && can_fall_right);
5590 #if USE_NEW_ALL_SLIPPERY
5592 #if USE_NEW_SP_SLIPPERY
5593 /* !!! better use the same properties as for custom elements here !!! */
5594 else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
5595 can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
5597 can_fall_right = FALSE; /* slip down on left side */
5598 can_fall_both = FALSE;
5603 #if USE_NEW_ALL_SLIPPERY
5606 if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
5607 can_fall_right = FALSE; /* slip down on left side */
5609 can_fall_left = !(can_fall_right = RND(2));
5611 can_fall_both = FALSE;
5616 if (game.emulation == EMU_BOULDERDASH ||
5617 element == EL_BD_ROCK || element == EL_BD_DIAMOND)
5618 can_fall_right = FALSE; /* slip down on left side */
5620 can_fall_left = !(can_fall_right = RND(2));
5622 can_fall_both = FALSE;
5628 /* if not determined otherwise, prefer left side for slipping down */
5629 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
5630 started_moving = TRUE;
5634 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
5636 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
5639 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
5640 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
5641 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
5642 int belt_dir = game.belt_dir[belt_nr];
5644 if ((belt_dir == MV_LEFT && left_is_free) ||
5645 (belt_dir == MV_RIGHT && right_is_free))
5647 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
5649 InitMovingField(x, y, belt_dir);
5650 started_moving = TRUE;
5652 Pushed[x][y] = TRUE;
5653 Pushed[nextx][y] = TRUE;
5655 GfxAction[x][y] = ACTION_DEFAULT;
5659 MovDir[x][y] = 0; /* if element was moving, stop it */
5664 /* not "else if" because of elements that can fall and move (EL_SPRING) */
5666 if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
5668 if (CAN_MOVE(element) && !started_moving)
5671 int move_pattern = element_info[element].move_pattern;
5676 if (MovDir[x][y] == MV_NONE)
5678 printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
5679 x, y, element, element_info[element].token_name);
5680 printf("StartMoving(): This should never happen!\n");
5685 Moving2Blocked(x, y, &newx, &newy);
5687 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
5690 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5691 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5693 WasJustMoving[x][y] = 0;
5694 CheckCollision[x][y] = 0;
5696 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
5698 if (Feld[x][y] != element) /* element has changed */
5702 if (!MovDelay[x][y]) /* start new movement phase */
5704 /* all objects that can change their move direction after each step
5705 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
5707 if (element != EL_YAMYAM &&
5708 element != EL_DARK_YAMYAM &&
5709 element != EL_PACMAN &&
5710 !(move_pattern & MV_ANY_DIRECTION) &&
5711 move_pattern != MV_TURNING_LEFT &&
5712 move_pattern != MV_TURNING_RIGHT &&
5713 move_pattern != MV_TURNING_LEFT_RIGHT &&
5714 move_pattern != MV_TURNING_RIGHT_LEFT &&
5715 move_pattern != MV_TURNING_RANDOM)
5719 if (MovDelay[x][y] && (element == EL_BUG ||
5720 element == EL_SPACESHIP ||
5721 element == EL_SP_SNIKSNAK ||
5722 element == EL_SP_ELECTRON ||
5723 element == EL_MOLE))
5724 DrawLevelField(x, y);
5728 if (MovDelay[x][y]) /* wait some time before next movement */
5732 if (element == EL_ROBOT ||
5733 element == EL_YAMYAM ||
5734 element == EL_DARK_YAMYAM)
5736 DrawLevelElementAnimationIfNeeded(x, y, element);
5737 PlayLevelSoundAction(x, y, ACTION_WAITING);
5739 else if (element == EL_SP_ELECTRON)
5740 DrawLevelElementAnimationIfNeeded(x, y, element);
5741 else if (element == EL_DRAGON)
5744 int dir = MovDir[x][y];
5745 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
5746 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
5747 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
5748 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
5749 dir == MV_UP ? IMG_FLAMES_1_UP :
5750 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
5751 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5753 GfxAction[x][y] = ACTION_ATTACKING;
5755 if (IS_PLAYER(x, y))
5756 DrawPlayerField(x, y);
5758 DrawLevelField(x, y);
5760 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
5762 for (i = 1; i <= 3; i++)
5764 int xx = x + i * dx;
5765 int yy = y + i * dy;
5766 int sx = SCREENX(xx);
5767 int sy = SCREENY(yy);
5768 int flame_graphic = graphic + (i - 1);
5770 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
5775 int flamed = MovingOrBlocked2Element(xx, yy);
5779 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
5781 else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
5782 RemoveMovingField(xx, yy);
5784 RemoveField(xx, yy);
5786 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
5789 RemoveMovingField(xx, yy);
5792 ChangeDelay[xx][yy] = 0;
5794 Feld[xx][yy] = EL_FLAMES;
5796 if (IN_SCR_FIELD(sx, sy))
5798 DrawLevelFieldCrumbledSand(xx, yy);
5799 DrawGraphic(sx, sy, flame_graphic, frame);
5804 if (Feld[xx][yy] == EL_FLAMES)
5805 Feld[xx][yy] = EL_EMPTY;
5806 DrawLevelField(xx, yy);
5811 if (MovDelay[x][y]) /* element still has to wait some time */
5813 PlayLevelSoundAction(x, y, ACTION_WAITING);
5819 /* now make next step */
5821 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
5823 if (DONT_COLLIDE_WITH(element) &&
5824 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
5825 !PLAYER_ENEMY_PROTECTED(newx, newy))
5827 TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
5832 else if (CAN_MOVE_INTO_ACID(element) &&
5833 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
5834 !IS_MV_DIAGONAL(MovDir[x][y]) &&
5835 (MovDir[x][y] == MV_DOWN ||
5836 game.engine_version >= VERSION_IDENT(3,1,0,0)))
5838 SplashAcid(newx, newy);
5839 Store[x][y] = EL_ACID;
5841 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
5843 if (Feld[newx][newy] == EL_EXIT_OPEN)
5846 DrawLevelField(x, y);
5848 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
5849 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
5850 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
5852 local_player->friends_still_needed--;
5853 if (!local_player->friends_still_needed &&
5854 !local_player->GameOver && AllPlayersGone)
5855 local_player->LevelSolved = local_player->GameOver = TRUE;
5859 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
5861 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
5862 DrawLevelField(newx, newy);
5864 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
5866 else if (!IS_FREE(newx, newy))
5868 GfxAction[x][y] = ACTION_WAITING;
5870 if (IS_PLAYER(x, y))
5871 DrawPlayerField(x, y);
5873 DrawLevelField(x, y);
5878 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
5880 if (IS_FOOD_PIG(Feld[newx][newy]))
5882 if (IS_MOVING(newx, newy))
5883 RemoveMovingField(newx, newy);
5886 Feld[newx][newy] = EL_EMPTY;
5887 DrawLevelField(newx, newy);
5890 PlayLevelSound(x, y, SND_PIG_DIGGING);
5892 else if (!IS_FREE(newx, newy))
5894 if (IS_PLAYER(x, y))
5895 DrawPlayerField(x, y);
5897 DrawLevelField(x, y);
5902 else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
5904 if (Store[x][y] != EL_EMPTY)
5906 boolean can_clone = FALSE;
5909 /* check if element to clone is still there */
5910 for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
5912 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
5920 /* cannot clone or target field not free anymore -- do not clone */
5921 if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
5922 Store[x][y] = EL_EMPTY;
5925 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
5927 if (IS_MV_DIAGONAL(MovDir[x][y]))
5929 int diagonal_move_dir = MovDir[x][y];
5930 int stored = Store[x][y];
5931 int change_delay = 8;
5934 /* android is moving diagonally */
5936 CreateField(x, y, EL_DIAGONAL_SHRINKING);
5938 Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
5939 GfxElement[x][y] = EL_EMC_ANDROID;
5940 GfxAction[x][y] = ACTION_SHRINKING;
5941 GfxDir[x][y] = diagonal_move_dir;
5942 ChangeDelay[x][y] = change_delay;
5944 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
5947 DrawLevelGraphicAnimation(x, y, graphic);
5948 PlayLevelSoundAction(x, y, ACTION_SHRINKING);
5950 if (Feld[newx][newy] == EL_ACID)
5952 SplashAcid(newx, newy);
5957 CreateField(newx, newy, EL_DIAGONAL_GROWING);
5959 Store[newx][newy] = EL_EMC_ANDROID;
5960 GfxElement[newx][newy] = EL_EMC_ANDROID;
5961 GfxAction[newx][newy] = ACTION_GROWING;
5962 GfxDir[newx][newy] = diagonal_move_dir;
5963 ChangeDelay[newx][newy] = change_delay;
5965 graphic = el_act_dir2img(GfxElement[newx][newy],
5966 GfxAction[newx][newy], GfxDir[newx][newy]);
5968 DrawLevelGraphicAnimation(newx, newy, graphic);
5969 PlayLevelSoundAction(newx, newy, ACTION_GROWING);
5975 Feld[newx][newy] = EL_EMPTY;
5976 DrawLevelField(newx, newy);
5978 PlayLevelSoundAction(x, y, ACTION_DIGGING);
5981 else if (!IS_FREE(newx, newy))
5984 if (IS_PLAYER(x, y))
5985 DrawPlayerField(x, y);
5987 DrawLevelField(x, y);
5993 else if (IS_CUSTOM_ELEMENT(element) &&
5994 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5996 int new_element = Feld[newx][newy];
5998 if (!IS_FREE(newx, newy))
6000 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
6001 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
6004 /* no element can dig solid indestructible elements */
6005 if (IS_INDESTRUCTIBLE(new_element) &&
6006 !IS_DIGGABLE(new_element) &&
6007 !IS_COLLECTIBLE(new_element))
6010 if (AmoebaNr[newx][newy] &&
6011 (new_element == EL_AMOEBA_FULL ||
6012 new_element == EL_BD_AMOEBA ||
6013 new_element == EL_AMOEBA_GROWING))
6015 AmoebaCnt[AmoebaNr[newx][newy]]--;
6016 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6019 if (IS_MOVING(newx, newy))
6020 RemoveMovingField(newx, newy);
6023 RemoveField(newx, newy);
6024 DrawLevelField(newx, newy);
6027 /* if digged element was about to explode, prevent the explosion */
6028 ExplodeField[newx][newy] = EX_TYPE_NONE;
6030 PlayLevelSoundAction(x, y, action);
6033 Store[newx][newy] = EL_EMPTY;
6035 /* this makes it possible to leave the removed element again */
6036 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
6037 Store[newx][newy] = new_element;
6039 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
6041 int move_leave_element = element_info[element].move_leave_element;
6043 /* this makes it possible to leave the removed element again */
6044 Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
6045 new_element : move_leave_element);
6049 if (move_pattern & MV_MAZE_RUNNER_STYLE)
6051 RunnerVisit[x][y] = FrameCounter;
6052 PlayerVisit[x][y] /= 8; /* expire player visit path */
6055 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
6057 if (!IS_FREE(newx, newy))
6059 if (IS_PLAYER(x, y))
6060 DrawPlayerField(x, y);
6062 DrawLevelField(x, y);
6068 boolean wanna_flame = !RND(10);
6069 int dx = newx - x, dy = newy - y;
6070 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
6071 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
6072 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
6073 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
6074 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
6075 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
6078 IS_CLASSIC_ENEMY(element1) ||
6079 IS_CLASSIC_ENEMY(element2)) &&
6080 element1 != EL_DRAGON && element2 != EL_DRAGON &&
6081 element1 != EL_FLAMES && element2 != EL_FLAMES)
6083 ResetGfxAnimation(x, y);
6084 GfxAction[x][y] = ACTION_ATTACKING;
6086 if (IS_PLAYER(x, y))
6087 DrawPlayerField(x, y);
6089 DrawLevelField(x, y);
6091 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
6093 MovDelay[x][y] = 50;
6097 RemoveField(newx, newy);
6099 Feld[newx][newy] = EL_FLAMES;
6100 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
6103 RemoveField(newx1, newy1);
6105 Feld[newx1][newy1] = EL_FLAMES;
6107 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
6110 RemoveField(newx2, newy2);
6112 Feld[newx2][newy2] = EL_FLAMES;
6119 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6120 Feld[newx][newy] == EL_DIAMOND)
6122 if (IS_MOVING(newx, newy))
6123 RemoveMovingField(newx, newy);
6126 Feld[newx][newy] = EL_EMPTY;
6127 DrawLevelField(newx, newy);
6130 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
6132 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6133 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
6135 if (AmoebaNr[newx][newy])
6137 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6138 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
6139 Feld[newx][newy] == EL_BD_AMOEBA)
6140 AmoebaCnt[AmoebaNr[newx][newy]]--;
6145 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
6147 RemoveMovingField(newx, newy);
6150 if (IS_MOVING(newx, newy))
6152 RemoveMovingField(newx, newy);
6157 Feld[newx][newy] = EL_EMPTY;
6158 DrawLevelField(newx, newy);
6161 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
6163 else if ((element == EL_PACMAN || element == EL_MOLE)
6164 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
6166 if (AmoebaNr[newx][newy])
6168 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6169 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
6170 Feld[newx][newy] == EL_BD_AMOEBA)
6171 AmoebaCnt[AmoebaNr[newx][newy]]--;
6174 if (element == EL_MOLE)
6176 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
6177 PlayLevelSound(x, y, SND_MOLE_DIGGING);
6179 ResetGfxAnimation(x, y);
6180 GfxAction[x][y] = ACTION_DIGGING;
6181 DrawLevelField(x, y);
6183 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
6185 return; /* wait for shrinking amoeba */
6187 else /* element == EL_PACMAN */
6189 Feld[newx][newy] = EL_EMPTY;
6190 DrawLevelField(newx, newy);
6191 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
6194 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
6195 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
6196 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
6198 /* wait for shrinking amoeba to completely disappear */
6201 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
6203 /* object was running against a wall */
6208 /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
6209 if (move_pattern & MV_ANY_DIRECTION &&
6210 move_pattern == MovDir[x][y])
6212 int blocking_element =
6213 (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
6215 CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
6218 element = Feld[x][y]; /* element might have changed */
6222 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
6223 DrawLevelElementAnimation(x, y, element);
6225 if (DONT_TOUCH(element))
6226 TestIfBadThingTouchesPlayer(x, y);
6231 InitMovingField(x, y, MovDir[x][y]);
6233 PlayLevelSoundAction(x, y, ACTION_MOVING);
6237 ContinueMoving(x, y);
6240 void ContinueMoving(int x, int y)
6242 int element = Feld[x][y];
6243 struct ElementInfo *ei = &element_info[element];
6244 int direction = MovDir[x][y];
6245 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
6246 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
6247 int newx = x + dx, newy = y + dy;
6248 int stored = Store[x][y];
6249 int stored_new = Store[newx][newy];
6250 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
6251 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
6252 boolean last_line = (newy == lev_fieldy - 1);
6254 MovPos[x][y] += getElementMoveStepsize(x, y);
6256 if (pushed_by_player) /* special case: moving object pushed by player */
6257 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
6259 if (ABS(MovPos[x][y]) < TILEX)
6261 DrawLevelField(x, y);
6263 return; /* element is still moving */
6266 /* element reached destination field */
6268 Feld[x][y] = EL_EMPTY;
6269 Feld[newx][newy] = element;
6270 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
6272 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
6274 element = Feld[newx][newy] = EL_ACID;
6276 else if (element == EL_MOLE)
6278 Feld[x][y] = EL_SAND;
6280 DrawLevelFieldCrumbledSandNeighbours(x, y);
6282 else if (element == EL_QUICKSAND_FILLING)
6284 element = Feld[newx][newy] = get_next_element(element);
6285 Store[newx][newy] = Store[x][y];
6287 else if (element == EL_QUICKSAND_EMPTYING)
6289 Feld[x][y] = get_next_element(element);
6290 element = Feld[newx][newy] = Store[x][y];
6292 else if (element == EL_MAGIC_WALL_FILLING)
6294 element = Feld[newx][newy] = get_next_element(element);
6295 if (!game.magic_wall_active)
6296 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
6297 Store[newx][newy] = Store[x][y];
6299 else if (element == EL_MAGIC_WALL_EMPTYING)
6301 Feld[x][y] = get_next_element(element);
6302 if (!game.magic_wall_active)
6303 Feld[x][y] = EL_MAGIC_WALL_DEAD;
6304 element = Feld[newx][newy] = Store[x][y];
6306 #if USE_NEW_CUSTOM_VALUE
6307 InitField(newx, newy, FALSE);
6310 else if (element == EL_BD_MAGIC_WALL_FILLING)
6312 element = Feld[newx][newy] = get_next_element(element);
6313 if (!game.magic_wall_active)
6314 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
6315 Store[newx][newy] = Store[x][y];
6317 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
6319 Feld[x][y] = get_next_element(element);
6320 if (!game.magic_wall_active)
6321 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
6322 element = Feld[newx][newy] = Store[x][y];
6324 #if USE_NEW_CUSTOM_VALUE
6325 InitField(newx, newy, FALSE);
6328 else if (element == EL_AMOEBA_DROPPING)
6330 Feld[x][y] = get_next_element(element);
6331 element = Feld[newx][newy] = Store[x][y];
6333 else if (element == EL_SOKOBAN_OBJECT)
6336 Feld[x][y] = Back[x][y];
6338 if (Back[newx][newy])
6339 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
6341 Back[x][y] = Back[newx][newy] = 0;
6344 Store[x][y] = EL_EMPTY;
6349 MovDelay[newx][newy] = 0;
6352 if (CAN_CHANGE_OR_HAS_ACTION(element))
6354 if (CAN_CHANGE(element))
6357 /* copy element change control values to new field */
6358 ChangeDelay[newx][newy] = ChangeDelay[x][y];
6359 ChangePage[newx][newy] = ChangePage[x][y];
6360 ChangeCount[newx][newy] = ChangeCount[x][y];
6361 ChangeEvent[newx][newy] = ChangeEvent[x][y];
6364 #if USE_NEW_CUSTOM_VALUE
6365 CustomValue[newx][newy] = CustomValue[x][y];
6371 #if USE_NEW_CUSTOM_VALUE
6372 CustomValue[newx][newy] = CustomValue[x][y];
6376 ChangeDelay[x][y] = 0;
6377 ChangePage[x][y] = -1;
6378 ChangeCount[x][y] = 0;
6379 ChangeEvent[x][y] = -1;
6381 #if USE_NEW_CUSTOM_VALUE
6382 CustomValue[x][y] = 0;
6385 /* copy animation control values to new field */
6386 GfxFrame[newx][newy] = GfxFrame[x][y];
6387 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
6388 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
6389 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
6391 Pushed[x][y] = Pushed[newx][newy] = FALSE;
6393 /* some elements can leave other elements behind after moving */
6395 if (ei->move_leave_element != EL_EMPTY &&
6396 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
6397 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
6399 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
6400 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
6401 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
6404 int move_leave_element = ei->move_leave_element;
6408 /* this makes it possible to leave the removed element again */
6409 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
6410 move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
6412 /* this makes it possible to leave the removed element again */
6413 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
6414 move_leave_element = stored;
6417 /* this makes it possible to leave the removed element again */
6418 if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
6419 ei->move_leave_element == EL_TRIGGER_ELEMENT)
6420 move_leave_element = stored;
6423 Feld[x][y] = move_leave_element;
6425 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
6426 MovDir[x][y] = direction;
6428 InitField(x, y, FALSE);
6430 if (GFX_CRUMBLED(Feld[x][y]))
6431 DrawLevelFieldCrumbledSandNeighbours(x, y);
6433 if (ELEM_IS_PLAYER(move_leave_element))
6434 RelocatePlayer(x, y, move_leave_element);
6437 /* do this after checking for left-behind element */
6438 ResetGfxAnimation(x, y); /* reset animation values for old field */
6440 if (!CAN_MOVE(element) ||
6441 (CAN_FALL(element) && direction == MV_DOWN &&
6442 (element == EL_SPRING ||
6443 element_info[element].move_pattern == MV_WHEN_PUSHED ||
6444 element_info[element].move_pattern == MV_WHEN_DROPPED)))
6445 GfxDir[x][y] = MovDir[newx][newy] = 0;
6447 DrawLevelField(x, y);
6448 DrawLevelField(newx, newy);
6450 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
6452 /* prevent pushed element from moving on in pushed direction */
6453 if (pushed_by_player && CAN_MOVE(element) &&
6454 element_info[element].move_pattern & MV_ANY_DIRECTION &&
6455 !(element_info[element].move_pattern & direction))
6456 TurnRound(newx, newy);
6458 /* prevent elements on conveyor belt from moving on in last direction */
6459 if (pushed_by_conveyor && CAN_FALL(element) &&
6460 direction & MV_HORIZONTAL)
6461 MovDir[newx][newy] = 0;
6463 if (!pushed_by_player)
6465 int nextx = newx + dx, nexty = newy + dy;
6466 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
6468 WasJustMoving[newx][newy] = 3;
6470 if (CAN_FALL(element) && direction == MV_DOWN)
6471 WasJustFalling[newx][newy] = 3;
6473 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
6474 CheckCollision[newx][newy] = 2;
6477 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
6479 TestIfBadThingTouchesPlayer(newx, newy);
6480 TestIfBadThingTouchesFriend(newx, newy);
6482 if (!IS_CUSTOM_ELEMENT(element))
6483 TestIfBadThingTouchesOtherBadThing(newx, newy);
6485 else if (element == EL_PENGUIN)
6486 TestIfFriendTouchesBadThing(newx, newy);
6488 /* give the player one last chance (one more frame) to move away */
6489 if (CAN_FALL(element) && direction == MV_DOWN &&
6490 (last_line || (!IS_FREE(x, newy + 1) &&
6491 (!IS_PLAYER(x, newy + 1) ||
6492 game.engine_version < VERSION_IDENT(3,1,1,0)))))
6495 if (pushed_by_player && !game.use_change_when_pushing_bug)
6497 int push_side = MV_DIR_OPPOSITE(direction);
6498 struct PlayerInfo *player = PLAYERINFO(x, y);
6500 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
6501 player->index_bit, push_side);
6502 CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
6503 player->index_bit, push_side);
6506 if (element == EL_EMC_ANDROID && pushed_by_player) /* make another move */
6507 MovDelay[newx][newy] = 1;
6509 CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
6511 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
6514 if (ChangePage[newx][newy] != -1) /* delayed change */
6516 int page = ChangePage[newx][newy];
6517 struct ElementChangeInfo *change = &ei->change_page[page];
6519 ChangePage[newx][newy] = -1;
6521 if (change->can_change)
6523 if (ChangeElement(newx, newy, element, page))
6525 if (change->post_change_function)
6526 change->post_change_function(newx, newy);
6530 if (change->has_action)
6531 ExecuteCustomElementAction(newx, newy, element, page);
6535 TestIfElementHitsCustomElement(newx, newy, direction);
6536 TestIfPlayerTouchesCustomElement(newx, newy);
6537 TestIfElementTouchesCustomElement(newx, newy);
6540 if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
6541 IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
6542 CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
6543 MV_DIR_OPPOSITE(direction));
6547 int AmoebeNachbarNr(int ax, int ay)
6550 int element = Feld[ax][ay];
6552 static int xy[4][2] =
6560 for (i = 0; i < NUM_DIRECTIONS; i++)
6562 int x = ax + xy[i][0];
6563 int y = ay + xy[i][1];
6565 if (!IN_LEV_FIELD(x, y))
6568 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
6569 group_nr = AmoebaNr[x][y];
6575 void AmoebenVereinigen(int ax, int ay)
6577 int i, x, y, xx, yy;
6578 int new_group_nr = AmoebaNr[ax][ay];
6579 static int xy[4][2] =
6587 if (new_group_nr == 0)
6590 for (i = 0; i < NUM_DIRECTIONS; i++)
6595 if (!IN_LEV_FIELD(x, y))
6598 if ((Feld[x][y] == EL_AMOEBA_FULL ||
6599 Feld[x][y] == EL_BD_AMOEBA ||
6600 Feld[x][y] == EL_AMOEBA_DEAD) &&
6601 AmoebaNr[x][y] != new_group_nr)
6603 int old_group_nr = AmoebaNr[x][y];
6605 if (old_group_nr == 0)
6608 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
6609 AmoebaCnt[old_group_nr] = 0;
6610 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
6611 AmoebaCnt2[old_group_nr] = 0;
6614 SCAN_PLAYFIELD(xx, yy)
6616 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
6619 if (AmoebaNr[xx][yy] == old_group_nr)
6620 AmoebaNr[xx][yy] = new_group_nr;
6626 void AmoebeUmwandeln(int ax, int ay)
6630 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
6632 int group_nr = AmoebaNr[ax][ay];
6637 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
6638 printf("AmoebeUmwandeln(): This should never happen!\n");
6644 SCAN_PLAYFIELD(x, y)
6646 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
6649 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
6652 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
6656 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
6657 SND_AMOEBA_TURNING_TO_GEM :
6658 SND_AMOEBA_TURNING_TO_ROCK));
6663 static int xy[4][2] =
6671 for (i = 0; i < NUM_DIRECTIONS; i++)
6676 if (!IN_LEV_FIELD(x, y))
6679 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
6681 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
6682 SND_AMOEBA_TURNING_TO_GEM :
6683 SND_AMOEBA_TURNING_TO_ROCK));
6690 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
6693 int group_nr = AmoebaNr[ax][ay];
6694 boolean done = FALSE;
6699 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
6700 printf("AmoebeUmwandelnBD(): This should never happen!\n");
6706 SCAN_PLAYFIELD(x, y)
6708 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
6711 if (AmoebaNr[x][y] == group_nr &&
6712 (Feld[x][y] == EL_AMOEBA_DEAD ||
6713 Feld[x][y] == EL_BD_AMOEBA ||
6714 Feld[x][y] == EL_AMOEBA_GROWING))
6717 Feld[x][y] = new_element;
6718 InitField(x, y, FALSE);
6719 DrawLevelField(x, y);
6725 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
6726 SND_BD_AMOEBA_TURNING_TO_ROCK :
6727 SND_BD_AMOEBA_TURNING_TO_GEM));
6730 void AmoebeWaechst(int x, int y)
6732 static unsigned long sound_delay = 0;
6733 static unsigned long sound_delay_value = 0;
6735 if (!MovDelay[x][y]) /* start new growing cycle */
6739 if (DelayReached(&sound_delay, sound_delay_value))
6741 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
6742 sound_delay_value = 30;
6746 if (MovDelay[x][y]) /* wait some time before growing bigger */
6749 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6751 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
6752 6 - MovDelay[x][y]);
6754 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
6757 if (!MovDelay[x][y])
6759 Feld[x][y] = Store[x][y];
6761 DrawLevelField(x, y);
6766 void AmoebaDisappearing(int x, int y)
6768 static unsigned long sound_delay = 0;
6769 static unsigned long sound_delay_value = 0;
6771 if (!MovDelay[x][y]) /* start new shrinking cycle */
6775 if (DelayReached(&sound_delay, sound_delay_value))
6776 sound_delay_value = 30;
6779 if (MovDelay[x][y]) /* wait some time before shrinking */
6782 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6784 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
6785 6 - MovDelay[x][y]);
6787 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
6790 if (!MovDelay[x][y])
6792 Feld[x][y] = EL_EMPTY;
6793 DrawLevelField(x, y);
6795 /* don't let mole enter this field in this cycle;
6796 (give priority to objects falling to this field from above) */
6802 void AmoebeAbleger(int ax, int ay)
6805 int element = Feld[ax][ay];
6806 int graphic = el2img(element);
6807 int newax = ax, neway = ay;
6808 boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
6809 static int xy[4][2] =
6817 if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
6819 Feld[ax][ay] = EL_AMOEBA_DEAD;
6820 DrawLevelField(ax, ay);
6824 if (IS_ANIMATED(graphic))
6825 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6827 if (!MovDelay[ax][ay]) /* start making new amoeba field */
6828 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
6830 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
6833 if (MovDelay[ax][ay])
6837 if (can_drop) /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
6840 int x = ax + xy[start][0];
6841 int y = ay + xy[start][1];
6843 if (!IN_LEV_FIELD(x, y))
6846 if (IS_FREE(x, y) ||
6847 CAN_GROW_INTO(Feld[x][y]) ||
6848 Feld[x][y] == EL_QUICKSAND_EMPTY)
6854 if (newax == ax && neway == ay)
6857 else /* normal or "filled" (BD style) amoeba */
6860 boolean waiting_for_player = FALSE;
6862 for (i = 0; i < NUM_DIRECTIONS; i++)
6864 int j = (start + i) % 4;
6865 int x = ax + xy[j][0];
6866 int y = ay + xy[j][1];
6868 if (!IN_LEV_FIELD(x, y))
6871 if (IS_FREE(x, y) ||
6872 CAN_GROW_INTO(Feld[x][y]) ||
6873 Feld[x][y] == EL_QUICKSAND_EMPTY)
6879 else if (IS_PLAYER(x, y))
6880 waiting_for_player = TRUE;
6883 if (newax == ax && neway == ay) /* amoeba cannot grow */
6885 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
6887 Feld[ax][ay] = EL_AMOEBA_DEAD;
6888 DrawLevelField(ax, ay);
6889 AmoebaCnt[AmoebaNr[ax][ay]]--;
6891 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
6893 if (element == EL_AMOEBA_FULL)
6894 AmoebeUmwandeln(ax, ay);
6895 else if (element == EL_BD_AMOEBA)
6896 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
6901 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
6903 /* amoeba gets larger by growing in some direction */
6905 int new_group_nr = AmoebaNr[ax][ay];
6908 if (new_group_nr == 0)
6910 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
6911 printf("AmoebeAbleger(): This should never happen!\n");
6916 AmoebaNr[newax][neway] = new_group_nr;
6917 AmoebaCnt[new_group_nr]++;
6918 AmoebaCnt2[new_group_nr]++;
6920 /* if amoeba touches other amoeba(s) after growing, unify them */
6921 AmoebenVereinigen(newax, neway);
6923 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
6925 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
6931 if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
6932 (neway == lev_fieldy - 1 && newax != ax))
6934 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
6935 Store[newax][neway] = element;
6937 else if (neway == ay || element == EL_EMC_DRIPPER)
6939 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
6941 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
6945 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
6946 Feld[ax][ay] = EL_AMOEBA_DROPPING;
6947 Store[ax][ay] = EL_AMOEBA_DROP;
6948 ContinueMoving(ax, ay);
6952 DrawLevelField(newax, neway);
6955 void Life(int ax, int ay)
6959 static int life[4] = { 2, 3, 3, 3 }; /* parameters for "game of life" */
6962 int element = Feld[ax][ay];
6963 int graphic = el2img(element);
6964 int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
6966 boolean changed = FALSE;
6968 if (IS_ANIMATED(graphic))
6969 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6974 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
6975 MovDelay[ax][ay] = life_time;
6977 if (MovDelay[ax][ay]) /* wait some time before next cycle */
6980 if (MovDelay[ax][ay])
6984 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
6986 int xx = ax+x1, yy = ay+y1;
6989 if (!IN_LEV_FIELD(xx, yy))
6992 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
6994 int x = xx+x2, y = yy+y2;
6996 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
6999 if (((Feld[x][y] == element ||
7000 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
7002 (IS_FREE(x, y) && Stop[x][y]))
7006 if (xx == ax && yy == ay) /* field in the middle */
7008 if (nachbarn < life_parameter[0] ||
7009 nachbarn > life_parameter[1])
7011 Feld[xx][yy] = EL_EMPTY;
7013 DrawLevelField(xx, yy);
7014 Stop[xx][yy] = TRUE;
7018 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
7019 { /* free border field */
7020 if (nachbarn >= life_parameter[2] &&
7021 nachbarn <= life_parameter[3])
7023 Feld[xx][yy] = element;
7024 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
7026 DrawLevelField(xx, yy);
7027 Stop[xx][yy] = TRUE;
7034 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
7035 SND_GAME_OF_LIFE_GROWING);
7038 static void InitRobotWheel(int x, int y)
7040 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
7043 static void RunRobotWheel(int x, int y)
7045 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
7048 static void StopRobotWheel(int x, int y)
7050 if (ZX == x && ZY == y)
7054 static void InitTimegateWheel(int x, int y)
7056 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
7059 static void RunTimegateWheel(int x, int y)
7061 PlayLevelSound(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
7064 static void InitMagicBallDelay(int x, int y)
7067 ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
7069 ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
7073 static void ActivateMagicBall(int bx, int by)
7077 if (level.ball_random)
7079 int pos_border = RND(8); /* select one of the eight border elements */
7080 int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
7081 int xx = pos_content % 3;
7082 int yy = pos_content / 3;
7087 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
7088 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
7092 for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
7094 int xx = x - bx + 1;
7095 int yy = y - by + 1;
7097 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
7098 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
7102 game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
7105 static void InitDiagonalMovingElement(int x, int y)
7108 MovDelay[x][y] = level.android_move_time;
7112 void CheckExit(int x, int y)
7114 if (local_player->gems_still_needed > 0 ||
7115 local_player->sokobanfields_still_needed > 0 ||
7116 local_player->lights_still_needed > 0)
7118 int element = Feld[x][y];
7119 int graphic = el2img(element);
7121 if (IS_ANIMATED(graphic))
7122 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7127 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7130 Feld[x][y] = EL_EXIT_OPENING;
7132 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
7135 void CheckExitSP(int x, int y)
7137 if (local_player->gems_still_needed > 0)
7139 int element = Feld[x][y];
7140 int graphic = el2img(element);
7142 if (IS_ANIMATED(graphic))
7143 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7148 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7151 Feld[x][y] = EL_SP_EXIT_OPENING;
7153 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
7156 static void CloseAllOpenTimegates()
7161 SCAN_PLAYFIELD(x, y)
7163 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7166 int element = Feld[x][y];
7168 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
7170 Feld[x][y] = EL_TIMEGATE_CLOSING;
7172 PlayLevelSoundAction(x, y, ACTION_CLOSING);
7177 void EdelsteinFunkeln(int x, int y)
7179 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
7182 if (Feld[x][y] == EL_BD_DIAMOND)
7185 if (MovDelay[x][y] == 0) /* next animation frame */
7186 MovDelay[x][y] = 11 * !SimpleRND(500);
7188 if (MovDelay[x][y] != 0) /* wait some time before next frame */
7192 if (setup.direct_draw && MovDelay[x][y])
7193 SetDrawtoField(DRAW_BUFFERED);
7195 DrawLevelElementAnimation(x, y, Feld[x][y]);
7197 if (MovDelay[x][y] != 0)
7199 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
7200 10 - MovDelay[x][y]);
7202 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
7204 if (setup.direct_draw)
7208 dest_x = FX + SCREENX(x) * TILEX;
7209 dest_y = FY + SCREENY(y) * TILEY;
7211 BlitBitmap(drawto_field, window,
7212 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
7213 SetDrawtoField(DRAW_DIRECT);
7219 void MauerWaechst(int x, int y)
7223 if (!MovDelay[x][y]) /* next animation frame */
7224 MovDelay[x][y] = 3 * delay;
7226 if (MovDelay[x][y]) /* wait some time before next frame */
7230 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7232 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
7233 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
7235 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
7238 if (!MovDelay[x][y])
7240 if (MovDir[x][y] == MV_LEFT)
7242 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
7243 DrawLevelField(x - 1, y);
7245 else if (MovDir[x][y] == MV_RIGHT)
7247 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
7248 DrawLevelField(x + 1, y);
7250 else if (MovDir[x][y] == MV_UP)
7252 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
7253 DrawLevelField(x, y - 1);
7257 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
7258 DrawLevelField(x, y + 1);
7261 Feld[x][y] = Store[x][y];
7263 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7264 DrawLevelField(x, y);
7269 void MauerAbleger(int ax, int ay)
7271 int element = Feld[ax][ay];
7272 int graphic = el2img(element);
7273 boolean oben_frei = FALSE, unten_frei = FALSE;
7274 boolean links_frei = FALSE, rechts_frei = FALSE;
7275 boolean oben_massiv = FALSE, unten_massiv = FALSE;
7276 boolean links_massiv = FALSE, rechts_massiv = FALSE;
7277 boolean new_wall = FALSE;
7279 if (IS_ANIMATED(graphic))
7280 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7282 if (!MovDelay[ax][ay]) /* start building new wall */
7283 MovDelay[ax][ay] = 6;
7285 if (MovDelay[ax][ay]) /* wait some time before building new wall */
7288 if (MovDelay[ax][ay])
7292 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
7294 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
7296 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
7298 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
7301 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
7302 element == EL_EXPANDABLE_WALL_ANY)
7306 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
7307 Store[ax][ay-1] = element;
7308 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
7309 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
7310 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
7311 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
7316 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
7317 Store[ax][ay+1] = element;
7318 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
7319 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
7320 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
7321 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
7326 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7327 element == EL_EXPANDABLE_WALL_ANY ||
7328 element == EL_EXPANDABLE_WALL)
7332 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
7333 Store[ax-1][ay] = element;
7334 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
7335 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
7336 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
7337 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
7343 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
7344 Store[ax+1][ay] = element;
7345 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
7346 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
7347 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
7348 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
7353 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
7354 DrawLevelField(ax, ay);
7356 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
7358 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
7359 unten_massiv = TRUE;
7360 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
7361 links_massiv = TRUE;
7362 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
7363 rechts_massiv = TRUE;
7365 if (((oben_massiv && unten_massiv) ||
7366 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7367 element == EL_EXPANDABLE_WALL) &&
7368 ((links_massiv && rechts_massiv) ||
7369 element == EL_EXPANDABLE_WALL_VERTICAL))
7370 Feld[ax][ay] = EL_WALL;
7373 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
7376 void CheckForDragon(int x, int y)
7379 boolean dragon_found = FALSE;
7380 static int xy[4][2] =
7388 for (i = 0; i < NUM_DIRECTIONS; i++)
7390 for (j = 0; j < 4; j++)
7392 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7394 if (IN_LEV_FIELD(xx, yy) &&
7395 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
7397 if (Feld[xx][yy] == EL_DRAGON)
7398 dragon_found = TRUE;
7407 for (i = 0; i < NUM_DIRECTIONS; i++)
7409 for (j = 0; j < 3; j++)
7411 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7413 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
7415 Feld[xx][yy] = EL_EMPTY;
7416 DrawLevelField(xx, yy);
7425 static void InitBuggyBase(int x, int y)
7427 int element = Feld[x][y];
7428 int activating_delay = FRAMES_PER_SECOND / 4;
7431 (element == EL_SP_BUGGY_BASE ?
7432 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
7433 element == EL_SP_BUGGY_BASE_ACTIVATING ?
7435 element == EL_SP_BUGGY_BASE_ACTIVE ?
7436 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
7439 static void WarnBuggyBase(int x, int y)
7442 static int xy[4][2] =
7450 for (i = 0; i < NUM_DIRECTIONS; i++)
7452 int xx = x + xy[i][0];
7453 int yy = y + xy[i][1];
7455 if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
7457 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
7464 static void InitTrap(int x, int y)
7466 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
7469 static void ActivateTrap(int x, int y)
7471 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
7474 static void ChangeActiveTrap(int x, int y)
7476 int graphic = IMG_TRAP_ACTIVE;
7478 /* if new animation frame was drawn, correct crumbled sand border */
7479 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
7480 DrawLevelFieldCrumbledSand(x, y);
7483 static int getSpecialActionElement(int element, int number, int base_element)
7485 return (element != EL_EMPTY ? element :
7486 number != -1 ? base_element + number - 1 :
7490 static int getModifiedActionNumber(int value_old, int operator, int operand,
7491 int value_min, int value_max)
7493 int value_new = (operator == CA_MODE_SET ? operand :
7494 operator == CA_MODE_ADD ? value_old + operand :
7495 operator == CA_MODE_SUBTRACT ? value_old - operand :
7496 operator == CA_MODE_MULTIPLY ? value_old * operand :
7497 operator == CA_MODE_DIVIDE ? value_old / MAX(1, operand) :
7498 operator == CA_MODE_MODULO ? value_old % MAX(1, operand) :
7501 return (value_new < value_min ? value_min :
7502 value_new > value_max ? value_max :
7506 static void ExecuteCustomElementAction(int x, int y, int element, int page)
7508 struct ElementInfo *ei = &element_info[element];
7509 struct ElementChangeInfo *change = &ei->change_page[page];
7510 int action_type = change->action_type;
7511 int action_mode = change->action_mode;
7512 int action_arg = change->action_arg;
7515 if (!change->has_action)
7518 /* ---------- determine action paramater values -------------------------- */
7520 int level_time_value =
7521 (level.time > 0 ? TimeLeft :
7524 int action_arg_element =
7525 (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player :
7526 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
7527 action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element :
7530 int action_arg_direction =
7531 (action_arg >= CA_ARG_DIRECTION_LEFT &&
7532 action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
7533 action_arg == CA_ARG_DIRECTION_TRIGGER ?
7534 change->actual_trigger_side :
7535 action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
7536 MV_DIR_OPPOSITE(change->actual_trigger_side) :
7539 int action_arg_number_min =
7540 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
7543 int action_arg_number_max =
7544 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
7545 action_type == CA_SET_LEVEL_GEMS ? 999 :
7546 action_type == CA_SET_LEVEL_TIME ? 9999 :
7547 action_type == CA_SET_LEVEL_SCORE ? 99999 :
7548 action_type == CA_SET_CE_SCORE ? 9999 :
7549 action_type == CA_SET_CE_VALUE ? 9999 :
7552 int action_arg_number_reset =
7553 (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize :
7554 action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
7555 action_type == CA_SET_LEVEL_TIME ? level.time :
7556 action_type == CA_SET_LEVEL_SCORE ? 0 :
7557 action_type == CA_SET_CE_SCORE ? 0 :
7559 action_type == CA_SET_CE_VALUE ? GET_NEW_CUSTOM_VALUE(element) :
7561 action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
7565 int action_arg_number =
7566 (action_arg <= CA_ARG_MAX ? action_arg :
7567 action_arg >= CA_ARG_SPEED_NOT_MOVING &&
7568 action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
7569 action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
7570 action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
7571 action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
7572 action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
7573 action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
7574 #if USE_NEW_CUSTOM_VALUE
7575 action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
7577 action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
7579 action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
7580 action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
7581 action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
7582 action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
7583 action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CUSTOM_VALUE(change->target_element) :
7584 action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
7585 action_arg == CA_ARG_ELEMENT_NR_TARGET ? change->target_element :
7586 action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
7589 int action_arg_number_old =
7590 (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
7591 action_type == CA_SET_LEVEL_TIME ? TimeLeft :
7592 action_type == CA_SET_LEVEL_SCORE ? local_player->score :
7593 action_type == CA_SET_CE_SCORE ? ei->collect_score :
7594 action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
7597 int action_arg_number_new =
7598 getModifiedActionNumber(action_arg_number_old,
7599 action_mode, action_arg_number,
7600 action_arg_number_min, action_arg_number_max);
7602 int trigger_player_bits =
7603 (change->actual_trigger_player >= EL_PLAYER_1 &&
7604 change->actual_trigger_player <= EL_PLAYER_4 ?
7605 (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
7608 int action_arg_player_bits =
7609 (action_arg >= CA_ARG_PLAYER_1 &&
7610 action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
7611 action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
7614 /* ---------- execute action -------------------------------------------- */
7623 /* ---------- level actions ------------------------------------------- */
7625 case CA_RESTART_LEVEL:
7627 game.restart_level = TRUE;
7632 case CA_SHOW_ENVELOPE:
7634 int element = getSpecialActionElement(action_arg_element,
7635 action_arg_number, EL_ENVELOPE_1);
7637 if (IS_ENVELOPE(element))
7638 local_player->show_envelope = element;
7643 case CA_SET_LEVEL_TIME:
7645 if (level.time > 0) /* only modify limited time value */
7647 TimeLeft = action_arg_number_new;
7649 DrawGameValue_Time(TimeLeft);
7651 if (!TimeLeft && setup.time_limit)
7652 for (i = 0; i < MAX_PLAYERS; i++)
7653 KillPlayer(&stored_player[i]);
7659 case CA_SET_LEVEL_SCORE:
7661 local_player->score = action_arg_number_new;
7663 DrawGameValue_Score(local_player->score);
7668 case CA_SET_LEVEL_GEMS:
7670 local_player->gems_still_needed = action_arg_number_new;
7672 DrawGameValue_Emeralds(local_player->gems_still_needed);
7677 case CA_SET_LEVEL_GRAVITY:
7679 game.gravity = (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
7680 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
7681 action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
7686 case CA_SET_LEVEL_WIND:
7688 game.wind_direction = action_arg_direction;
7693 /* ---------- player actions ------------------------------------------ */
7695 case CA_MOVE_PLAYER:
7697 /* automatically move to the next field in specified direction */
7698 for (i = 0; i < MAX_PLAYERS; i++)
7699 if (trigger_player_bits & (1 << i))
7700 stored_player[i].programmed_action = action_arg_direction;
7705 case CA_EXIT_PLAYER:
7707 for (i = 0; i < MAX_PLAYERS; i++)
7708 if (action_arg_player_bits & (1 << i))
7709 stored_player[i].LevelSolved = stored_player[i].GameOver = TRUE;
7714 case CA_KILL_PLAYER:
7716 for (i = 0; i < MAX_PLAYERS; i++)
7717 if (action_arg_player_bits & (1 << i))
7718 KillPlayer(&stored_player[i]);
7723 case CA_SET_PLAYER_KEYS:
7725 int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
7726 int element = getSpecialActionElement(action_arg_element,
7727 action_arg_number, EL_KEY_1);
7729 if (IS_KEY(element))
7731 for (i = 0; i < MAX_PLAYERS; i++)
7733 if (trigger_player_bits & (1 << i))
7735 stored_player[i].key[KEY_NR(element)] = key_state;
7737 DrawGameValue_Keys(stored_player[i].key);
7739 redraw_mask |= REDRAW_DOOR_1;
7747 case CA_SET_PLAYER_SPEED:
7749 for (i = 0; i < MAX_PLAYERS; i++)
7751 if (trigger_player_bits & (1 << i))
7753 int move_stepsize = TILEX / stored_player[i].move_delay_value;
7755 if (action_arg == CA_ARG_SPEED_FASTER &&
7756 stored_player[i].cannot_move)
7758 action_arg_number = STEPSIZE_VERY_SLOW;
7760 else if (action_arg == CA_ARG_SPEED_SLOWER ||
7761 action_arg == CA_ARG_SPEED_FASTER)
7763 action_arg_number = 2;
7764 action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
7769 getModifiedActionNumber(move_stepsize,
7772 action_arg_number_min,
7773 action_arg_number_max);
7776 SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
7778 /* make sure that value is power of 2 */
7779 move_stepsize = (1 << log_2(move_stepsize));
7781 /* do no immediately change -- the player might just be moving */
7782 stored_player[i].move_delay_value_next = TILEX / move_stepsize;
7784 stored_player[i].cannot_move =
7785 (action_arg == CA_ARG_SPEED_NOT_MOVING ? TRUE : FALSE);
7793 case CA_SET_PLAYER_SHIELD:
7795 for (i = 0; i < MAX_PLAYERS; i++)
7797 if (trigger_player_bits & (1 << i))
7799 if (action_arg == CA_ARG_SHIELD_OFF)
7801 stored_player[i].shield_normal_time_left = 0;
7802 stored_player[i].shield_deadly_time_left = 0;
7804 else if (action_arg == CA_ARG_SHIELD_NORMAL)
7806 stored_player[i].shield_normal_time_left = 999999;
7808 else if (action_arg == CA_ARG_SHIELD_DEADLY)
7810 stored_player[i].shield_normal_time_left = 999999;
7811 stored_player[i].shield_deadly_time_left = 999999;
7819 case CA_SET_PLAYER_ARTWORK:
7821 for (i = 0; i < MAX_PLAYERS; i++)
7823 if (trigger_player_bits & (1 << i))
7825 int artwork_element = action_arg_element;
7827 if (action_arg == CA_ARG_ELEMENT_RESET)
7829 (level.use_artwork_element[i] ? level.artwork_element[i] :
7830 stored_player[i].element_nr);
7832 stored_player[i].artwork_element = artwork_element;
7834 SetPlayerWaiting(&stored_player[i], FALSE);
7836 /* set number of special actions for bored and sleeping animation */
7837 stored_player[i].num_special_action_bored =
7838 get_num_special_action(artwork_element,
7839 ACTION_BORING_1, ACTION_BORING_LAST);
7840 stored_player[i].num_special_action_sleeping =
7841 get_num_special_action(artwork_element,
7842 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
7849 /* ---------- CE actions ---------------------------------------------- */
7851 case CA_SET_CE_SCORE:
7853 ei->collect_score = action_arg_number_new;
7858 case CA_SET_CE_VALUE:
7860 #if USE_NEW_CUSTOM_VALUE
7861 int last_custom_value = CustomValue[x][y];
7863 CustomValue[x][y] = action_arg_number_new;
7866 printf("::: Count == %d\n", CustomValue[x][y]);
7869 if (CustomValue[x][y] == 0 && last_custom_value > 0)
7872 printf("::: CE_VALUE_GETS_ZERO\n");
7875 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
7876 CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
7879 printf("::: RESULT: %d, %d\n", Feld[x][y], ChangePage[x][y]);
7887 /* ---------- engine actions ------------------------------------------ */
7889 case CA_SET_ENGINE_SCAN_MODE:
7891 InitPlayfieldScanMode(action_arg);
7901 static void CreateFieldExt(int x, int y, int element, boolean is_change)
7903 int previous_move_direction = MovDir[x][y];
7904 #if USE_NEW_CUSTOM_VALUE
7905 int last_ce_value = CustomValue[x][y];
7907 boolean add_player = (ELEM_IS_PLAYER(element) &&
7908 IS_WALKABLE(Feld[x][y]));
7910 /* check if element under player changes from accessible to unaccessible
7911 (needed for special case of dropping element which then changes) */
7912 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
7913 IS_ACCESSIBLE(Feld[x][y]) && !IS_ACCESSIBLE(element))
7922 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
7923 RemoveMovingField(x, y);
7927 Feld[x][y] = element;
7929 ResetGfxAnimation(x, y);
7930 ResetRandomAnimationValue(x, y);
7932 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
7933 MovDir[x][y] = previous_move_direction;
7935 #if USE_NEW_CUSTOM_VALUE
7936 if (element_info[Feld[x][y]].use_last_ce_value)
7937 CustomValue[x][y] = last_ce_value;
7940 InitField_WithBug1(x, y, FALSE);
7942 DrawLevelField(x, y);
7944 if (GFX_CRUMBLED(Feld[x][y]))
7945 DrawLevelFieldCrumbledSandNeighbours(x, y);
7948 /* "ChangeCount" not set yet to allow "entered by player" change one time */
7949 if (ELEM_IS_PLAYER(element))
7950 RelocatePlayer(x, y, element);
7953 ChangeCount[x][y]++; /* count number of changes in the same frame */
7955 TestIfBadThingTouchesPlayer(x, y);
7956 TestIfPlayerTouchesCustomElement(x, y);
7957 TestIfElementTouchesCustomElement(x, y);
7960 static void CreateField(int x, int y, int element)
7962 CreateFieldExt(x, y, element, FALSE);
7965 static void CreateElementFromChange(int x, int y, int element)
7967 element = GET_VALID_RUNTIME_ELEMENT(element);
7969 #if USE_STOP_CHANGED_ELEMENTS
7970 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
7972 int old_element = Feld[x][y];
7974 /* prevent changed element from moving in same engine frame
7975 unless both old and new element can either fall or move */
7976 if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
7977 (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
7982 CreateFieldExt(x, y, element, TRUE);
7985 static boolean ChangeElement(int x, int y, int element, int page)
7987 struct ElementChangeInfo *change = &element_info[element].change_page[page];
7989 int old_element = Feld[x][y];
7991 /* always use default change event to prevent running into a loop */
7992 if (ChangeEvent[x][y] == -1)
7993 ChangeEvent[x][y] = CE_DELAY;
7995 if (ChangeEvent[x][y] == CE_DELAY)
7997 /* reset actual trigger element, trigger player and action element */
7998 change->actual_trigger_element = EL_EMPTY;
7999 change->actual_trigger_player = EL_PLAYER_1;
8000 change->actual_trigger_side = CH_SIDE_NONE;
8001 change->actual_trigger_ce_value = 0;
8004 /* do not change elements more than a specified maximum number of changes */
8005 if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
8008 ChangeCount[x][y]++; /* count number of changes in the same frame */
8010 if (change->explode)
8017 if (change->use_target_content)
8019 boolean complete_replace = TRUE;
8020 boolean can_replace[3][3];
8023 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
8026 boolean is_walkable;
8027 boolean is_diggable;
8028 boolean is_collectible;
8029 boolean is_removable;
8030 boolean is_destructible;
8031 int ex = x + xx - 1;
8032 int ey = y + yy - 1;
8033 int content_element = change->target_content.e[xx][yy];
8036 can_replace[xx][yy] = TRUE;
8038 if (ex == x && ey == y) /* do not check changing element itself */
8041 if (content_element == EL_EMPTY_SPACE)
8043 can_replace[xx][yy] = FALSE; /* do not replace border with space */
8048 if (!IN_LEV_FIELD(ex, ey))
8050 can_replace[xx][yy] = FALSE;
8051 complete_replace = FALSE;
8058 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
8059 e = MovingOrBlocked2Element(ex, ey);
8061 is_empty = (IS_FREE(ex, ey) ||
8062 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
8064 is_walkable = (is_empty || IS_WALKABLE(e));
8065 is_diggable = (is_empty || IS_DIGGABLE(e));
8066 is_collectible = (is_empty || IS_COLLECTIBLE(e));
8067 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
8068 is_removable = (is_diggable || is_collectible);
8070 can_replace[xx][yy] =
8071 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
8072 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
8073 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
8074 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
8075 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
8076 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
8077 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
8079 if (!can_replace[xx][yy])
8080 complete_replace = FALSE;
8083 if (!change->only_if_complete || complete_replace)
8085 boolean something_has_changed = FALSE;
8087 if (change->only_if_complete && change->use_random_replace &&
8088 RND(100) < change->random_percentage)
8091 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
8093 int ex = x + xx - 1;
8094 int ey = y + yy - 1;
8095 int content_element;
8097 if (can_replace[xx][yy] && (!change->use_random_replace ||
8098 RND(100) < change->random_percentage))
8100 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
8101 RemoveMovingField(ex, ey);
8103 ChangeEvent[ex][ey] = ChangeEvent[x][y];
8105 content_element = change->target_content.e[xx][yy];
8106 target_element = GET_TARGET_ELEMENT(content_element, change);
8108 CreateElementFromChange(ex, ey, target_element);
8110 something_has_changed = TRUE;
8112 /* for symmetry reasons, freeze newly created border elements */
8113 if (ex != x || ey != y)
8114 Stop[ex][ey] = TRUE; /* no more moving in this frame */
8118 if (something_has_changed)
8120 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
8121 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
8127 target_element = GET_TARGET_ELEMENT(change->target_element, change);
8129 if (element == EL_DIAGONAL_GROWING ||
8130 element == EL_DIAGONAL_SHRINKING)
8132 target_element = Store[x][y];
8134 Store[x][y] = EL_EMPTY;
8137 CreateElementFromChange(x, y, target_element);
8139 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
8140 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
8143 /* this uses direct change before indirect change */
8144 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
8149 #if USE_NEW_DELAYED_ACTION
8151 static void HandleElementChange(int x, int y, int page)
8153 int element = MovingOrBlocked2Element(x, y);
8154 struct ElementInfo *ei = &element_info[element];
8155 struct ElementChangeInfo *change = &ei->change_page[page];
8158 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
8159 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
8162 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
8163 x, y, element, element_info[element].token_name);
8164 printf("HandleElementChange(): This should never happen!\n");
8169 /* this can happen with classic bombs on walkable, changing elements */
8170 if (!CAN_CHANGE_OR_HAS_ACTION(element))
8173 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
8174 ChangeDelay[x][y] = 0;
8180 if (ChangeDelay[x][y] == 0) /* initialize element change */
8182 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
8184 if (change->can_change)
8186 ResetGfxAnimation(x, y);
8187 ResetRandomAnimationValue(x, y);
8189 if (change->pre_change_function)
8190 change->pre_change_function(x, y);
8194 ChangeDelay[x][y]--;
8196 if (ChangeDelay[x][y] != 0) /* continue element change */
8198 if (change->can_change)
8200 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8202 if (IS_ANIMATED(graphic))
8203 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8205 if (change->change_function)
8206 change->change_function(x, y);
8209 else /* finish element change */
8211 if (ChangePage[x][y] != -1) /* remember page from delayed change */
8213 page = ChangePage[x][y];
8214 ChangePage[x][y] = -1;
8216 change = &ei->change_page[page];
8219 if (IS_MOVING(x, y)) /* never change a running system ;-) */
8221 ChangeDelay[x][y] = 1; /* try change after next move step */
8222 ChangePage[x][y] = page; /* remember page to use for change */
8227 if (change->can_change)
8229 if (ChangeElement(x, y, element, page))
8231 if (change->post_change_function)
8232 change->post_change_function(x, y);
8236 if (change->has_action)
8237 ExecuteCustomElementAction(x, y, element, page);
8243 static void HandleElementChange(int x, int y, int page)
8245 int element = MovingOrBlocked2Element(x, y);
8246 struct ElementInfo *ei = &element_info[element];
8247 struct ElementChangeInfo *change = &ei->change_page[page];
8250 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
8253 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
8254 x, y, element, element_info[element].token_name);
8255 printf("HandleElementChange(): This should never happen!\n");
8260 /* this can happen with classic bombs on walkable, changing elements */
8261 if (!CAN_CHANGE(element))
8264 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
8265 ChangeDelay[x][y] = 0;
8271 if (ChangeDelay[x][y] == 0) /* initialize element change */
8273 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
8275 ResetGfxAnimation(x, y);
8276 ResetRandomAnimationValue(x, y);
8278 if (change->pre_change_function)
8279 change->pre_change_function(x, y);
8282 ChangeDelay[x][y]--;
8284 if (ChangeDelay[x][y] != 0) /* continue element change */
8286 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8288 if (IS_ANIMATED(graphic))
8289 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8291 if (change->change_function)
8292 change->change_function(x, y);
8294 else /* finish element change */
8296 if (ChangePage[x][y] != -1) /* remember page from delayed change */
8298 page = ChangePage[x][y];
8299 ChangePage[x][y] = -1;
8301 change = &ei->change_page[page];
8304 if (IS_MOVING(x, y)) /* never change a running system ;-) */
8306 ChangeDelay[x][y] = 1; /* try change after next move step */
8307 ChangePage[x][y] = page; /* remember page to use for change */
8312 if (ChangeElement(x, y, element, page))
8314 if (change->post_change_function)
8315 change->post_change_function(x, y);
8322 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
8323 int trigger_element,
8329 boolean change_done_any = FALSE;
8330 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
8333 if (!(trigger_events[trigger_element][trigger_event]))
8336 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8338 int element = EL_CUSTOM_START + i;
8339 boolean change_done = FALSE;
8342 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
8343 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
8346 for (p = 0; p < element_info[element].num_change_pages; p++)
8348 struct ElementChangeInfo *change = &element_info[element].change_page[p];
8350 if (change->can_change_or_has_action &&
8351 change->has_event[trigger_event] &&
8352 change->trigger_side & trigger_side &&
8353 change->trigger_player & trigger_player &&
8354 change->trigger_page & trigger_page_bits &&
8355 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
8357 change->actual_trigger_element = trigger_element;
8358 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
8359 change->actual_trigger_side = trigger_side;
8360 change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
8362 if ((change->can_change && !change_done) || change->has_action)
8367 SCAN_PLAYFIELD(x, y)
8369 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8372 if (Feld[x][y] == element)
8374 if (change->can_change && !change_done)
8376 ChangeDelay[x][y] = 1;
8377 ChangeEvent[x][y] = trigger_event;
8379 HandleElementChange(x, y, p);
8381 #if USE_NEW_DELAYED_ACTION
8382 else if (change->has_action)
8384 ExecuteCustomElementAction(x, y, element, p);
8385 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8388 if (change->has_action)
8390 ExecuteCustomElementAction(x, y, element, p);
8391 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8397 if (change->can_change)
8400 change_done_any = TRUE;
8407 return change_done_any;
8410 static boolean CheckElementChangeExt(int x, int y,
8412 int trigger_element,
8417 boolean change_done = FALSE;
8420 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
8421 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
8424 if (Feld[x][y] == EL_BLOCKED)
8426 Blocked2Moving(x, y, &x, &y);
8427 element = Feld[x][y];
8431 /* check if element has already changed */
8432 if (Feld[x][y] != element)
8435 /* check if element has already changed or is about to change after moving */
8436 if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
8437 Feld[x][y] != element) ||
8439 (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
8440 (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
8441 ChangePage[x][y] != -1)))
8445 for (p = 0; p < element_info[element].num_change_pages; p++)
8447 struct ElementChangeInfo *change = &element_info[element].change_page[p];
8449 boolean check_trigger_element =
8450 (trigger_event == CE_TOUCHING_X ||
8451 trigger_event == CE_HITTING_X ||
8452 trigger_event == CE_HIT_BY_X);
8454 if (change->can_change_or_has_action &&
8455 change->has_event[trigger_event] &&
8456 change->trigger_side & trigger_side &&
8457 change->trigger_player & trigger_player &&
8458 (!check_trigger_element ||
8459 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
8461 change->actual_trigger_element = trigger_element;
8462 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
8463 change->actual_trigger_side = trigger_side;
8464 change->actual_trigger_ce_value = CustomValue[x][y];
8466 /* special case: trigger element not at (x,y) position for some events */
8467 if (check_trigger_element)
8479 { 0, 0 }, { 0, 0 }, { 0, 0 },
8483 int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
8484 int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
8486 change->actual_trigger_ce_value = CustomValue[xx][yy];
8489 if (change->can_change && !change_done)
8491 ChangeDelay[x][y] = 1;
8492 ChangeEvent[x][y] = trigger_event;
8494 HandleElementChange(x, y, p);
8498 #if USE_NEW_DELAYED_ACTION
8499 else if (change->has_action)
8501 ExecuteCustomElementAction(x, y, element, p);
8502 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8505 if (change->has_action)
8507 ExecuteCustomElementAction(x, y, element, p);
8508 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8517 static void PlayPlayerSound(struct PlayerInfo *player)
8519 int jx = player->jx, jy = player->jy;
8520 int sound_element = player->artwork_element;
8521 int last_action = player->last_action_waiting;
8522 int action = player->action_waiting;
8524 if (player->is_waiting)
8526 if (action != last_action)
8527 PlayLevelSoundElementAction(jx, jy, sound_element, action);
8529 PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
8533 if (action != last_action)
8534 StopSound(element_info[sound_element].sound[last_action]);
8536 if (last_action == ACTION_SLEEPING)
8537 PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
8541 static void PlayAllPlayersSound()
8545 for (i = 0; i < MAX_PLAYERS; i++)
8546 if (stored_player[i].active)
8547 PlayPlayerSound(&stored_player[i]);
8550 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
8552 boolean last_waiting = player->is_waiting;
8553 int move_dir = player->MovDir;
8555 player->last_action_waiting = player->action_waiting;
8559 if (!last_waiting) /* not waiting -> waiting */
8561 player->is_waiting = TRUE;
8563 player->frame_counter_bored =
8565 game.player_boring_delay_fixed +
8566 SimpleRND(game.player_boring_delay_random);
8567 player->frame_counter_sleeping =
8569 game.player_sleeping_delay_fixed +
8570 SimpleRND(game.player_sleeping_delay_random);
8572 InitPlayerGfxAnimation(player, ACTION_WAITING, player->MovDir);
8575 if (game.player_sleeping_delay_fixed +
8576 game.player_sleeping_delay_random > 0 &&
8577 player->anim_delay_counter == 0 &&
8578 player->post_delay_counter == 0 &&
8579 FrameCounter >= player->frame_counter_sleeping)
8580 player->is_sleeping = TRUE;
8581 else if (game.player_boring_delay_fixed +
8582 game.player_boring_delay_random > 0 &&
8583 FrameCounter >= player->frame_counter_bored)
8584 player->is_bored = TRUE;
8586 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
8587 player->is_bored ? ACTION_BORING :
8590 if (player->is_sleeping)
8592 if (player->num_special_action_sleeping > 0)
8594 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
8596 int last_special_action = player->special_action_sleeping;
8597 int num_special_action = player->num_special_action_sleeping;
8598 int special_action =
8599 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
8600 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
8601 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
8602 last_special_action + 1 : ACTION_SLEEPING);
8603 int special_graphic =
8604 el_act_dir2img(player->artwork_element, special_action, move_dir);
8606 player->anim_delay_counter =
8607 graphic_info[special_graphic].anim_delay_fixed +
8608 SimpleRND(graphic_info[special_graphic].anim_delay_random);
8609 player->post_delay_counter =
8610 graphic_info[special_graphic].post_delay_fixed +
8611 SimpleRND(graphic_info[special_graphic].post_delay_random);
8613 player->special_action_sleeping = special_action;
8616 if (player->anim_delay_counter > 0)
8618 player->action_waiting = player->special_action_sleeping;
8619 player->anim_delay_counter--;
8621 else if (player->post_delay_counter > 0)
8623 player->post_delay_counter--;
8627 else if (player->is_bored)
8629 if (player->num_special_action_bored > 0)
8631 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
8633 int special_action =
8634 ACTION_BORING_1 + SimpleRND(player->num_special_action_bored);
8635 int special_graphic =
8636 el_act_dir2img(player->artwork_element, special_action, move_dir);
8638 player->anim_delay_counter =
8639 graphic_info[special_graphic].anim_delay_fixed +
8640 SimpleRND(graphic_info[special_graphic].anim_delay_random);
8641 player->post_delay_counter =
8642 graphic_info[special_graphic].post_delay_fixed +
8643 SimpleRND(graphic_info[special_graphic].post_delay_random);
8645 player->special_action_bored = special_action;
8648 if (player->anim_delay_counter > 0)
8650 player->action_waiting = player->special_action_bored;
8651 player->anim_delay_counter--;
8653 else if (player->post_delay_counter > 0)
8655 player->post_delay_counter--;
8660 else if (last_waiting) /* waiting -> not waiting */
8662 player->is_waiting = FALSE;
8663 player->is_bored = FALSE;
8664 player->is_sleeping = FALSE;
8666 player->frame_counter_bored = -1;
8667 player->frame_counter_sleeping = -1;
8669 player->anim_delay_counter = 0;
8670 player->post_delay_counter = 0;
8672 player->action_waiting = ACTION_DEFAULT;
8674 player->special_action_bored = ACTION_DEFAULT;
8675 player->special_action_sleeping = ACTION_DEFAULT;
8679 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
8681 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
8682 int left = player_action & JOY_LEFT;
8683 int right = player_action & JOY_RIGHT;
8684 int up = player_action & JOY_UP;
8685 int down = player_action & JOY_DOWN;
8686 int button1 = player_action & JOY_BUTTON_1;
8687 int button2 = player_action & JOY_BUTTON_2;
8688 int dx = (left ? -1 : right ? 1 : 0);
8689 int dy = (up ? -1 : down ? 1 : 0);
8691 if (!player->active || tape.pausing)
8697 snapped = SnapField(player, dx, dy);
8701 dropped = DropElement(player);
8703 moved = MovePlayer(player, dx, dy);
8706 if (tape.single_step && tape.recording && !tape.pausing)
8708 if (button1 || (dropped && !moved))
8710 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8711 SnapField(player, 0, 0); /* stop snapping */
8715 SetPlayerWaiting(player, FALSE);
8717 return player_action;
8721 /* no actions for this player (no input at player's configured device) */
8723 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
8724 SnapField(player, 0, 0);
8725 CheckGravityMovementWhenNotMoving(player);
8727 if (player->MovPos == 0)
8728 SetPlayerWaiting(player, TRUE);
8730 if (player->MovPos == 0) /* needed for tape.playing */
8731 player->is_moving = FALSE;
8733 player->is_dropping = FALSE;
8739 void AdvanceFrameAndPlayerCounters(int player_nr)
8743 /* advance frame counters (global frame counter and time frame counter) */
8747 /* advance player counters (counters for move delay, move animation etc.) */
8748 for (i = 0; i < MAX_PLAYERS; i++)
8750 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
8751 int move_delay_value = stored_player[i].move_delay_value;
8752 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
8754 if (!advance_player_counters) /* not all players may be affected */
8757 #if USE_NEW_PLAYER_ANIM
8758 if (move_frames == 0) /* less than one move per game frame */
8760 int stepsize = TILEX / move_delay_value;
8761 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
8762 int count = (stored_player[i].is_moving ?
8763 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
8765 if (count % delay == 0)
8770 stored_player[i].Frame += move_frames;
8772 if (stored_player[i].MovPos != 0)
8773 stored_player[i].StepFrame += move_frames;
8775 if (stored_player[i].move_delay > 0)
8776 stored_player[i].move_delay--;
8778 /* due to bugs in previous versions, counter must count up, not down */
8779 if (stored_player[i].push_delay != -1)
8780 stored_player[i].push_delay++;
8782 if (stored_player[i].drop_delay > 0)
8783 stored_player[i].drop_delay--;
8789 static unsigned long game_frame_delay = 0;
8790 unsigned long game_frame_delay_value;
8791 int magic_wall_x = 0, magic_wall_y = 0;
8792 int i, x, y, element, graphic;
8793 byte *recorded_player_action;
8794 byte summarized_player_action = 0;
8795 byte tape_action[MAX_PLAYERS];
8797 if (game_status != GAME_MODE_PLAYING)
8800 game_frame_delay_value =
8801 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
8803 if (tape.playing && tape.warp_forward && !tape.pausing)
8804 game_frame_delay_value = 0;
8806 /* ---------- main game synchronization point ---------- */
8808 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
8810 InitPlayfieldScanModeVars();
8812 if (ScreenMovPos == 0) /* screen currently aligned at tile position */
8814 if (game.centered_to_player != game.centered_to_player_next)
8816 struct PlayerInfo *player = &stored_player[game.centered_to_player_next];
8818 DrawRelocatePlayer(player, setup.quick_switch);
8820 game.centered_to_player = game.centered_to_player_next;
8824 #if USE_ONE_MORE_CHANGE_PER_FRAME
8825 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
8827 SCAN_PLAYFIELD(x, y)
8829 ChangeCount[x][y] = 0;
8830 ChangeEvent[x][y] = -1;
8835 if (network_playing && !network_player_action_received)
8837 /* try to get network player actions in time */
8839 #if defined(NETWORK_AVALIABLE)
8840 /* last chance to get network player actions without main loop delay */
8844 /* game was quit by network peer */
8845 if (game_status != GAME_MODE_PLAYING)
8848 if (!network_player_action_received)
8849 return; /* failed to get network player actions in time */
8855 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
8858 /* !!! CHECK THIS (tape.pausing is always FALSE here!) !!! */
8859 if (recorded_player_action == NULL && tape.pausing)
8863 for (i = 0; i < MAX_PLAYERS; i++)
8865 summarized_player_action |= stored_player[i].action;
8867 if (!network_playing)
8868 stored_player[i].effective_action = stored_player[i].action;
8871 #if defined(NETWORK_AVALIABLE)
8872 if (network_playing)
8873 SendToServer_MovePlayer(summarized_player_action);
8876 if (!options.network && !setup.team_mode)
8877 local_player->effective_action = summarized_player_action;
8879 if (recorded_player_action != NULL)
8880 for (i = 0; i < MAX_PLAYERS; i++)
8881 stored_player[i].effective_action = recorded_player_action[i];
8883 for (i = 0; i < MAX_PLAYERS; i++)
8885 tape_action[i] = stored_player[i].effective_action;
8887 if (tape.recording && tape_action[i] && !tape.player_participates[i])
8888 tape.player_participates[i] = TRUE; /* player just appeared from CE */
8891 /* only save actions from input devices, but not programmed actions */
8893 TapeRecordAction(tape_action);
8895 for (i = 0; i < MAX_PLAYERS; i++)
8897 int actual_player_action = stored_player[i].effective_action;
8900 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
8901 - rnd_equinox_tetrachloride 048
8902 - rnd_equinox_tetrachloride_ii 096
8903 - rnd_emanuel_schmieg 002
8904 - doctor_sloan_ww 001, 020
8906 if (stored_player[i].MovPos == 0)
8907 CheckGravityMovement(&stored_player[i]);
8910 /* overwrite programmed action with tape action */
8911 if (stored_player[i].programmed_action)
8912 actual_player_action = stored_player[i].programmed_action;
8915 PlayerActions(&stored_player[i], actual_player_action);
8917 tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
8919 if (tape.recording && tape_action[i] && !tape.player_participates[i])
8920 tape.player_participates[i] = TRUE; /* player just appeared from CE */
8923 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
8926 network_player_action_received = FALSE;
8928 ScrollScreen(NULL, SCROLL_GO_ON);
8930 /* for backwards compatibility, the following code emulates a fixed bug that
8931 occured when pushing elements (causing elements that just made their last
8932 pushing step to already (if possible) make their first falling step in the
8933 same game frame, which is bad); this code is also needed to use the famous
8934 "spring push bug" which is used in older levels and might be wanted to be
8935 used also in newer levels, but in this case the buggy pushing code is only
8936 affecting the "spring" element and no other elements */
8938 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
8940 for (i = 0; i < MAX_PLAYERS; i++)
8942 struct PlayerInfo *player = &stored_player[i];
8946 if (player->active && player->is_pushing && player->is_moving &&
8948 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
8949 Feld[x][y] == EL_SPRING))
8951 ContinueMoving(x, y);
8953 /* continue moving after pushing (this is actually a bug) */
8954 if (!IS_MOVING(x, y))
8963 SCAN_PLAYFIELD(x, y)
8965 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8968 ChangeCount[x][y] = 0;
8969 ChangeEvent[x][y] = -1;
8971 /* this must be handled before main playfield loop */
8972 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
8975 if (MovDelay[x][y] <= 0)
8979 #if USE_NEW_SNAP_DELAY
8980 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
8983 if (MovDelay[x][y] <= 0)
8986 DrawLevelField(x, y);
8988 TestIfElementTouchesCustomElement(x, y); /* for empty space */
8994 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
8996 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
8997 printf("GameActions(): This should never happen!\n");
8999 ChangePage[x][y] = -1;
9004 if (WasJustMoving[x][y] > 0)
9005 WasJustMoving[x][y]--;
9006 if (WasJustFalling[x][y] > 0)
9007 WasJustFalling[x][y]--;
9008 if (CheckCollision[x][y] > 0)
9009 CheckCollision[x][y]--;
9013 /* reset finished pushing action (not done in ContinueMoving() to allow
9014 continuous pushing animation for elements with zero push delay) */
9015 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
9017 ResetGfxAnimation(x, y);
9018 DrawLevelField(x, y);
9022 if (IS_BLOCKED(x, y))
9026 Blocked2Moving(x, y, &oldx, &oldy);
9027 if (!IS_MOVING(oldx, oldy))
9029 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
9030 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
9031 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
9032 printf("GameActions(): This should never happen!\n");
9039 SCAN_PLAYFIELD(x, y)
9041 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9044 element = Feld[x][y];
9045 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9048 printf("::: %d,%d\n", x, y);
9050 if (element == EL_ROCK)
9051 printf("::: Yo man! Rocks can fall!\n");
9054 if (graphic_info[graphic].anim_global_sync)
9055 GfxFrame[x][y] = FrameCounter;
9056 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
9058 int old_gfx_frame = GfxFrame[x][y];
9060 GfxFrame[x][y] = CustomValue[x][y];
9063 if (GfxFrame[x][y] != old_gfx_frame)
9065 DrawLevelGraphicAnimation(x, y, graphic);
9067 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
9069 int old_gfx_frame = GfxFrame[x][y];
9071 GfxFrame[x][y] = element_info[element].collect_score;
9074 if (GfxFrame[x][y] != old_gfx_frame)
9076 DrawLevelGraphicAnimation(x, y, graphic);
9079 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
9080 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
9081 ResetRandomAnimationValue(x, y);
9083 SetRandomAnimationValue(x, y);
9085 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
9087 if (IS_INACTIVE(element))
9089 if (IS_ANIMATED(graphic))
9090 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9095 /* this may take place after moving, so 'element' may have changed */
9096 if (IS_CHANGING(x, y) &&
9097 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
9099 int page = element_info[element].event_page_nr[CE_DELAY];
9101 HandleElementChange(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] : page);
9105 printf("::: ChangeDelay == %d\n", ChangeDelay[x][y]);
9109 if (element == EL_CUSTOM_255)
9110 printf("::: ChangeDelay == %d\n", ChangeDelay[x][y]);
9114 HandleElementChange(x, y, page);
9116 if (CAN_CHANGE(element))
9117 HandleElementChange(x, y, page);
9119 if (HAS_ACTION(element))
9120 ExecuteCustomElementAction(x, y, element, page);
9125 element = Feld[x][y];
9126 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9129 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
9133 element = Feld[x][y];
9134 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9136 if (IS_ANIMATED(graphic) &&
9139 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9141 if (IS_GEM(element) || element == EL_SP_INFOTRON)
9142 EdelsteinFunkeln(x, y);
9144 else if ((element == EL_ACID ||
9145 element == EL_EXIT_OPEN ||
9146 element == EL_SP_EXIT_OPEN ||
9147 element == EL_SP_TERMINAL ||
9148 element == EL_SP_TERMINAL_ACTIVE ||
9149 element == EL_EXTRA_TIME ||
9150 element == EL_SHIELD_NORMAL ||
9151 element == EL_SHIELD_DEADLY) &&
9152 IS_ANIMATED(graphic))
9153 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9154 else if (IS_MOVING(x, y))
9155 ContinueMoving(x, y);
9156 else if (IS_ACTIVE_BOMB(element))
9157 CheckDynamite(x, y);
9158 else if (element == EL_AMOEBA_GROWING)
9159 AmoebeWaechst(x, y);
9160 else if (element == EL_AMOEBA_SHRINKING)
9161 AmoebaDisappearing(x, y);
9163 #if !USE_NEW_AMOEBA_CODE
9164 else if (IS_AMOEBALIVE(element))
9165 AmoebeAbleger(x, y);
9168 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
9170 else if (element == EL_EXIT_CLOSED)
9172 else if (element == EL_SP_EXIT_CLOSED)
9174 else if (element == EL_EXPANDABLE_WALL_GROWING)
9176 else if (element == EL_EXPANDABLE_WALL ||
9177 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9178 element == EL_EXPANDABLE_WALL_VERTICAL ||
9179 element == EL_EXPANDABLE_WALL_ANY)
9181 else if (element == EL_FLAMES)
9182 CheckForDragon(x, y);
9183 else if (element == EL_EXPLOSION)
9184 ; /* drawing of correct explosion animation is handled separately */
9185 else if (element == EL_ELEMENT_SNAPPING ||
9186 element == EL_DIAGONAL_SHRINKING ||
9187 element == EL_DIAGONAL_GROWING)
9190 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
9192 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9195 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
9196 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9199 if (element == EL_CUSTOM_255 ||
9200 element == EL_CUSTOM_256)
9201 DrawLevelGraphicAnimation(x, y, graphic);
9204 if (IS_BELT_ACTIVE(element))
9205 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
9207 if (game.magic_wall_active)
9209 int jx = local_player->jx, jy = local_player->jy;
9211 /* play the element sound at the position nearest to the player */
9212 if ((element == EL_MAGIC_WALL_FULL ||
9213 element == EL_MAGIC_WALL_ACTIVE ||
9214 element == EL_MAGIC_WALL_EMPTYING ||
9215 element == EL_BD_MAGIC_WALL_FULL ||
9216 element == EL_BD_MAGIC_WALL_ACTIVE ||
9217 element == EL_BD_MAGIC_WALL_EMPTYING) &&
9218 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
9226 #if USE_NEW_AMOEBA_CODE
9227 /* new experimental amoeba growth stuff */
9228 if (!(FrameCounter % 8))
9230 static unsigned long random = 1684108901;
9232 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
9234 x = RND(lev_fieldx);
9235 y = RND(lev_fieldy);
9236 element = Feld[x][y];
9238 if (!IS_PLAYER(x,y) &&
9239 (element == EL_EMPTY ||
9240 CAN_GROW_INTO(element) ||
9241 element == EL_QUICKSAND_EMPTY ||
9242 element == EL_ACID_SPLASH_LEFT ||
9243 element == EL_ACID_SPLASH_RIGHT))
9245 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
9246 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
9247 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
9248 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
9249 Feld[x][y] = EL_AMOEBA_DROP;
9252 random = random * 129 + 1;
9258 if (game.explosions_delayed)
9261 game.explosions_delayed = FALSE;
9264 SCAN_PLAYFIELD(x, y)
9266 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9269 element = Feld[x][y];
9271 if (ExplodeField[x][y])
9272 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
9273 else if (element == EL_EXPLOSION)
9274 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
9276 ExplodeField[x][y] = EX_TYPE_NONE;
9279 game.explosions_delayed = TRUE;
9282 if (game.magic_wall_active)
9284 if (!(game.magic_wall_time_left % 4))
9286 int element = Feld[magic_wall_x][magic_wall_y];
9288 if (element == EL_BD_MAGIC_WALL_FULL ||
9289 element == EL_BD_MAGIC_WALL_ACTIVE ||
9290 element == EL_BD_MAGIC_WALL_EMPTYING)
9291 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
9293 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
9296 if (game.magic_wall_time_left > 0)
9298 game.magic_wall_time_left--;
9299 if (!game.magic_wall_time_left)
9302 SCAN_PLAYFIELD(x, y)
9304 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9307 element = Feld[x][y];
9309 if (element == EL_MAGIC_WALL_ACTIVE ||
9310 element == EL_MAGIC_WALL_FULL)
9312 Feld[x][y] = EL_MAGIC_WALL_DEAD;
9313 DrawLevelField(x, y);
9315 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
9316 element == EL_BD_MAGIC_WALL_FULL)
9318 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
9319 DrawLevelField(x, y);
9323 game.magic_wall_active = FALSE;
9328 if (game.light_time_left > 0)
9330 game.light_time_left--;
9332 if (game.light_time_left == 0)
9333 RedrawAllLightSwitchesAndInvisibleElements();
9336 if (game.timegate_time_left > 0)
9338 game.timegate_time_left--;
9340 if (game.timegate_time_left == 0)
9341 CloseAllOpenTimegates();
9344 if (game.lenses_time_left > 0)
9346 game.lenses_time_left--;
9348 if (game.lenses_time_left == 0)
9349 RedrawAllInvisibleElementsForLenses();
9352 if (game.magnify_time_left > 0)
9354 game.magnify_time_left--;
9356 if (game.magnify_time_left == 0)
9357 RedrawAllInvisibleElementsForMagnifier();
9360 for (i = 0; i < MAX_PLAYERS; i++)
9362 struct PlayerInfo *player = &stored_player[i];
9364 if (SHIELD_ON(player))
9366 if (player->shield_deadly_time_left)
9367 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
9368 else if (player->shield_normal_time_left)
9369 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
9373 if (TimeFrames >= FRAMES_PER_SECOND)
9378 for (i = 0; i < MAX_PLAYERS; i++)
9380 struct PlayerInfo *player = &stored_player[i];
9382 if (SHIELD_ON(player))
9384 player->shield_normal_time_left--;
9386 if (player->shield_deadly_time_left > 0)
9387 player->shield_deadly_time_left--;
9391 if (!level.use_step_counter)
9399 if (TimeLeft <= 10 && setup.time_limit)
9400 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
9402 DrawGameValue_Time(TimeLeft);
9404 if (!TimeLeft && setup.time_limit)
9405 for (i = 0; i < MAX_PLAYERS; i++)
9406 KillPlayer(&stored_player[i]);
9408 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
9409 DrawGameValue_Time(TimePlayed);
9412 if (tape.recording || tape.playing)
9413 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
9417 PlayAllPlayersSound();
9419 if (options.debug) /* calculate frames per second */
9421 static unsigned long fps_counter = 0;
9422 static int fps_frames = 0;
9423 unsigned long fps_delay_ms = Counter() - fps_counter;
9427 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
9429 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
9432 fps_counter = Counter();
9435 redraw_mask |= REDRAW_FPS;
9438 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
9440 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
9442 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
9444 local_player->show_envelope = 0;
9447 /* use random number generator in every frame to make it less predictable */
9448 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
9452 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
9454 int min_x = x, min_y = y, max_x = x, max_y = y;
9457 for (i = 0; i < MAX_PLAYERS; i++)
9459 int jx = stored_player[i].jx, jy = stored_player[i].jy;
9461 if (!stored_player[i].active || &stored_player[i] == player)
9464 min_x = MIN(min_x, jx);
9465 min_y = MIN(min_y, jy);
9466 max_x = MAX(max_x, jx);
9467 max_y = MAX(max_y, jy);
9470 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
9473 static boolean AllPlayersInVisibleScreen()
9477 for (i = 0; i < MAX_PLAYERS; i++)
9479 int jx = stored_player[i].jx, jy = stored_player[i].jy;
9481 if (!stored_player[i].active)
9484 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
9491 void ScrollLevel(int dx, int dy)
9493 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
9496 BlitBitmap(drawto_field, drawto_field,
9497 FX + TILEX * (dx == -1) - softscroll_offset,
9498 FY + TILEY * (dy == -1) - softscroll_offset,
9499 SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
9500 SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
9501 FX + TILEX * (dx == 1) - softscroll_offset,
9502 FY + TILEY * (dy == 1) - softscroll_offset);
9506 x = (dx == 1 ? BX1 : BX2);
9507 for (y = BY1; y <= BY2; y++)
9508 DrawScreenField(x, y);
9513 y = (dy == 1 ? BY1 : BY2);
9514 for (x = BX1; x <= BX2; x++)
9515 DrawScreenField(x, y);
9518 redraw_mask |= REDRAW_FIELD;
9521 static boolean canFallDown(struct PlayerInfo *player)
9523 int jx = player->jx, jy = player->jy;
9525 return (IN_LEV_FIELD(jx, jy + 1) &&
9526 (IS_FREE(jx, jy + 1) ||
9527 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
9528 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
9529 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
9532 static boolean canPassField(int x, int y, int move_dir)
9534 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9535 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9536 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
9539 int element = Feld[x][y];
9541 return (IS_PASSABLE_FROM(element, opposite_dir) &&
9542 !CAN_MOVE(element) &&
9543 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
9544 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
9545 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
9548 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
9550 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9551 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9552 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
9556 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9557 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
9558 (IS_DIGGABLE(Feld[newx][newy]) ||
9559 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9560 canPassField(newx, newy, move_dir)));
9563 static void CheckGravityMovement(struct PlayerInfo *player)
9565 if (game.gravity && !player->programmed_action)
9567 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
9568 int move_dir_vertical = player->effective_action & MV_VERTICAL;
9569 boolean player_is_snapping = player->effective_action & JOY_BUTTON_1;
9570 int jx = player->jx, jy = player->jy;
9571 boolean player_is_moving_to_valid_field =
9572 (!player_is_snapping &&
9573 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
9574 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
9575 boolean player_can_fall_down = canFallDown(player);
9577 if (player_can_fall_down &&
9578 !player_is_moving_to_valid_field)
9579 player->programmed_action = MV_DOWN;
9583 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
9585 return CheckGravityMovement(player);
9587 if (game.gravity && !player->programmed_action)
9589 int jx = player->jx, jy = player->jy;
9590 boolean field_under_player_is_free =
9591 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
9592 boolean player_is_standing_on_valid_field =
9593 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
9594 (IS_WALKABLE(Feld[jx][jy]) &&
9595 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
9597 if (field_under_player_is_free && !player_is_standing_on_valid_field)
9598 player->programmed_action = MV_DOWN;
9604 -----------------------------------------------------------------------------
9605 dx, dy: direction (non-diagonal) to try to move the player to
9606 real_dx, real_dy: direction as read from input device (can be diagonal)
9609 boolean MovePlayerOneStep(struct PlayerInfo *player,
9610 int dx, int dy, int real_dx, int real_dy)
9612 int jx = player->jx, jy = player->jy;
9613 int new_jx = jx + dx, new_jy = jy + dy;
9614 #if !USE_FIXED_DONT_RUN_INTO
9618 boolean player_can_move = !player->cannot_move;
9620 if (!player->active || (!dx && !dy))
9621 return MP_NO_ACTION;
9623 player->MovDir = (dx < 0 ? MV_LEFT :
9626 dy > 0 ? MV_DOWN : MV_NONE);
9628 if (!IN_LEV_FIELD(new_jx, new_jy))
9629 return MP_NO_ACTION;
9631 if (!player_can_move)
9634 if (player->MovPos == 0)
9636 player->is_moving = FALSE;
9637 player->is_digging = FALSE;
9638 player->is_collecting = FALSE;
9639 player->is_snapping = FALSE;
9640 player->is_pushing = FALSE;
9643 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
9644 SnapField(player, 0, 0);
9648 return MP_NO_ACTION;
9653 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
9654 return MP_NO_ACTION;
9657 #if !USE_FIXED_DONT_RUN_INTO
9658 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
9660 /* (moved to DigField()) */
9661 if (player_can_move && DONT_RUN_INTO(element))
9663 if (element == EL_ACID && dx == 0 && dy == 1)
9665 SplashAcid(new_jx, new_jy);
9666 Feld[jx][jy] = EL_PLAYER_1;
9667 InitMovingField(jx, jy, MV_DOWN);
9668 Store[jx][jy] = EL_ACID;
9669 ContinueMoving(jx, jy);
9673 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
9679 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
9681 #if USE_FIXED_DONT_RUN_INTO
9682 if (can_move == MP_DONT_RUN_INTO)
9686 if (can_move != MP_MOVING)
9689 #if USE_FIXED_DONT_RUN_INTO
9692 /* check if DigField() has caused relocation of the player */
9693 if (player->jx != jx || player->jy != jy)
9694 return MP_NO_ACTION; /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
9696 StorePlayer[jx][jy] = 0;
9697 player->last_jx = jx;
9698 player->last_jy = jy;
9699 player->jx = new_jx;
9700 player->jy = new_jy;
9701 StorePlayer[new_jx][new_jy] = player->element_nr;
9703 if (player->move_delay_value_next != -1)
9705 player->move_delay_value = player->move_delay_value_next;
9706 player->move_delay_value_next = -1;
9710 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
9712 player->step_counter++;
9714 PlayerVisit[jx][jy] = FrameCounter;
9716 ScrollPlayer(player, SCROLL_INIT);
9721 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
9723 int jx = player->jx, jy = player->jy;
9724 int old_jx = jx, old_jy = jy;
9725 int moved = MP_NO_ACTION;
9727 if (!player->active)
9732 if (player->MovPos == 0)
9734 player->is_moving = FALSE;
9735 player->is_digging = FALSE;
9736 player->is_collecting = FALSE;
9737 player->is_snapping = FALSE;
9738 player->is_pushing = FALSE;
9744 if (player->move_delay > 0)
9747 player->move_delay = -1; /* set to "uninitialized" value */
9749 /* store if player is automatically moved to next field */
9750 player->is_auto_moving = (player->programmed_action != MV_NONE);
9752 /* remove the last programmed player action */
9753 player->programmed_action = 0;
9757 /* should only happen if pre-1.2 tape recordings are played */
9758 /* this is only for backward compatibility */
9760 int original_move_delay_value = player->move_delay_value;
9763 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
9767 /* scroll remaining steps with finest movement resolution */
9768 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
9770 while (player->MovPos)
9772 ScrollPlayer(player, SCROLL_GO_ON);
9773 ScrollScreen(NULL, SCROLL_GO_ON);
9775 AdvanceFrameAndPlayerCounters(player->index_nr);
9781 player->move_delay_value = original_move_delay_value;
9784 if (player->last_move_dir & MV_HORIZONTAL)
9786 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
9787 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
9791 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
9792 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
9799 if (moved & MP_MOVING && !ScreenMovPos &&
9800 player->index_nr == game.centered_to_player)
9802 if (moved & MP_MOVING && !ScreenMovPos &&
9803 (player == local_player || !options.network))
9806 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
9807 int offset = (setup.scroll_delay ? 3 : 0);
9809 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
9811 /* actual player has left the screen -- scroll in that direction */
9812 if (jx != old_jx) /* player has moved horizontally */
9813 scroll_x += (jx - old_jx);
9814 else /* player has moved vertically */
9815 scroll_y += (jy - old_jy);
9819 if (jx != old_jx) /* player has moved horizontally */
9821 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
9822 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
9823 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
9825 /* don't scroll over playfield boundaries */
9826 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
9827 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
9829 /* don't scroll more than one field at a time */
9830 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
9832 /* don't scroll against the player's moving direction */
9833 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
9834 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
9835 scroll_x = old_scroll_x;
9837 else /* player has moved vertically */
9839 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
9840 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
9841 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
9843 /* don't scroll over playfield boundaries */
9844 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
9845 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
9847 /* don't scroll more than one field at a time */
9848 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
9850 /* don't scroll against the player's moving direction */
9851 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
9852 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
9853 scroll_y = old_scroll_y;
9857 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
9860 if (!options.network && !AllPlayersInVisibleScreen())
9862 scroll_x = old_scroll_x;
9863 scroll_y = old_scroll_y;
9868 ScrollScreen(player, SCROLL_INIT);
9869 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
9874 player->StepFrame = 0;
9876 if (moved & MP_MOVING)
9878 if (old_jx != jx && old_jy == jy)
9879 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
9880 else if (old_jx == jx && old_jy != jy)
9881 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
9883 DrawLevelField(jx, jy); /* for "crumbled sand" */
9885 player->last_move_dir = player->MovDir;
9886 player->is_moving = TRUE;
9887 player->is_snapping = FALSE;
9888 player->is_switching = FALSE;
9889 player->is_dropping = FALSE;
9893 CheckGravityMovementWhenNotMoving(player);
9895 player->is_moving = FALSE;
9897 /* at this point, the player is allowed to move, but cannot move right now
9898 (e.g. because of something blocking the way) -- ensure that the player
9899 is also allowed to move in the next frame (in old versions before 3.1.1,
9900 the player was forced to wait again for eight frames before next try) */
9902 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
9903 player->move_delay = 0; /* allow direct movement in the next frame */
9906 if (player->move_delay == -1) /* not yet initialized by DigField() */
9907 player->move_delay = player->move_delay_value;
9909 if (game.engine_version < VERSION_IDENT(3,0,7,0))
9911 TestIfPlayerTouchesBadThing(jx, jy);
9912 TestIfPlayerTouchesCustomElement(jx, jy);
9915 if (!player->active)
9916 RemovePlayer(player);
9921 void ScrollPlayer(struct PlayerInfo *player, int mode)
9923 int jx = player->jx, jy = player->jy;
9924 int last_jx = player->last_jx, last_jy = player->last_jy;
9925 int move_stepsize = TILEX / player->move_delay_value;
9927 #if USE_NEW_PLAYER_SPEED
9928 if (!player->active)
9931 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
9934 if (!player->active || player->MovPos == 0)
9938 if (mode == SCROLL_INIT)
9940 player->actual_frame_counter = FrameCounter;
9941 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
9943 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
9944 Feld[last_jx][last_jy] == EL_EMPTY)
9946 int last_field_block_delay = 0; /* start with no blocking at all */
9947 int block_delay_adjustment = player->block_delay_adjustment;
9949 /* if player blocks last field, add delay for exactly one move */
9950 if (player->block_last_field)
9952 last_field_block_delay += player->move_delay_value;
9954 /* when blocking enabled, prevent moving up despite gravity */
9955 if (game.gravity && player->MovDir == MV_UP)
9956 block_delay_adjustment = -1;
9959 /* add block delay adjustment (also possible when not blocking) */
9960 last_field_block_delay += block_delay_adjustment;
9962 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
9963 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
9966 #if USE_NEW_PLAYER_SPEED
9967 if (player->MovPos != 0) /* player has not yet reached destination */
9973 else if (!FrameReached(&player->actual_frame_counter, 1))
9977 printf("::: player->MovPos: %d -> %d\n",
9979 player->MovPos + (player->MovPos > 0 ? -1 : 1) * move_stepsize);
9982 #if USE_NEW_PLAYER_SPEED
9983 if (player->MovPos != 0)
9985 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
9986 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
9988 /* before DrawPlayer() to draw correct player graphic for this case */
9989 if (player->MovPos == 0)
9990 CheckGravityMovement(player);
9993 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
9994 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
9996 /* before DrawPlayer() to draw correct player graphic for this case */
9997 if (player->MovPos == 0)
9998 CheckGravityMovement(player);
10001 if (player->MovPos == 0) /* player reached destination field */
10004 printf("::: player reached destination field\n");
10007 if (player->move_delay_reset_counter > 0)
10009 player->move_delay_reset_counter--;
10011 if (player->move_delay_reset_counter == 0)
10013 /* continue with normal speed after quickly moving through gate */
10014 HALVE_PLAYER_SPEED(player);
10016 /* be able to make the next move without delay */
10017 player->move_delay = 0;
10021 player->last_jx = jx;
10022 player->last_jy = jy;
10024 if (Feld[jx][jy] == EL_EXIT_OPEN ||
10025 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
10026 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
10028 DrawPlayer(player); /* needed here only to cleanup last field */
10029 RemovePlayer(player);
10031 if (local_player->friends_still_needed == 0 ||
10032 IS_SP_ELEMENT(Feld[jx][jy]))
10033 player->LevelSolved = player->GameOver = TRUE;
10036 /* this breaks one level: "machine", level 000 */
10038 int move_direction = player->MovDir;
10039 int enter_side = MV_DIR_OPPOSITE(move_direction);
10040 int leave_side = move_direction;
10041 int old_jx = last_jx;
10042 int old_jy = last_jy;
10043 int old_element = Feld[old_jx][old_jy];
10044 int new_element = Feld[jx][jy];
10046 if (IS_CUSTOM_ELEMENT(old_element))
10047 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
10049 player->index_bit, leave_side);
10051 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
10052 CE_PLAYER_LEAVES_X,
10053 player->index_bit, leave_side);
10055 if (IS_CUSTOM_ELEMENT(new_element))
10056 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
10057 player->index_bit, enter_side);
10059 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
10060 CE_PLAYER_ENTERS_X,
10061 player->index_bit, enter_side);
10063 CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
10064 CE_MOVE_OF_X, move_direction);
10067 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10069 TestIfPlayerTouchesBadThing(jx, jy);
10070 TestIfPlayerTouchesCustomElement(jx, jy);
10072 /* needed because pushed element has not yet reached its destination,
10073 so it would trigger a change event at its previous field location */
10074 if (!player->is_pushing)
10075 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
10077 if (!player->active)
10078 RemovePlayer(player);
10081 if (level.use_step_counter)
10091 if (TimeLeft <= 10 && setup.time_limit)
10092 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
10094 DrawGameValue_Time(TimeLeft);
10096 if (!TimeLeft && setup.time_limit)
10097 for (i = 0; i < MAX_PLAYERS; i++)
10098 KillPlayer(&stored_player[i]);
10100 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
10101 DrawGameValue_Time(TimePlayed);
10104 if (tape.single_step && tape.recording && !tape.pausing &&
10105 !player->programmed_action)
10106 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10110 void ScrollScreen(struct PlayerInfo *player, int mode)
10112 static unsigned long screen_frame_counter = 0;
10114 if (mode == SCROLL_INIT)
10116 /* set scrolling step size according to actual player's moving speed */
10117 ScrollStepSize = TILEX / player->move_delay_value;
10119 screen_frame_counter = FrameCounter;
10120 ScreenMovDir = player->MovDir;
10121 ScreenMovPos = player->MovPos;
10122 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
10125 else if (!FrameReached(&screen_frame_counter, 1))
10130 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
10131 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
10132 redraw_mask |= REDRAW_FIELD;
10135 ScreenMovDir = MV_NONE;
10138 void TestIfPlayerTouchesCustomElement(int x, int y)
10140 static int xy[4][2] =
10147 static int trigger_sides[4][2] =
10149 /* center side border side */
10150 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
10151 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
10152 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
10153 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
10155 static int touch_dir[4] =
10157 MV_LEFT | MV_RIGHT,
10162 int center_element = Feld[x][y]; /* should always be non-moving! */
10165 for (i = 0; i < NUM_DIRECTIONS; i++)
10167 int xx = x + xy[i][0];
10168 int yy = y + xy[i][1];
10169 int center_side = trigger_sides[i][0];
10170 int border_side = trigger_sides[i][1];
10171 int border_element;
10173 if (!IN_LEV_FIELD(xx, yy))
10176 if (IS_PLAYER(x, y))
10178 struct PlayerInfo *player = PLAYERINFO(x, y);
10180 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10181 border_element = Feld[xx][yy]; /* may be moving! */
10182 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
10183 border_element = Feld[xx][yy];
10184 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
10185 border_element = MovingOrBlocked2Element(xx, yy);
10187 continue; /* center and border element do not touch */
10189 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
10190 player->index_bit, border_side);
10191 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
10192 CE_PLAYER_TOUCHES_X,
10193 player->index_bit, border_side);
10195 else if (IS_PLAYER(xx, yy))
10197 struct PlayerInfo *player = PLAYERINFO(xx, yy);
10199 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10201 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
10202 continue; /* center and border element do not touch */
10205 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
10206 player->index_bit, center_side);
10207 CheckTriggeredElementChangeByPlayer(x, y, center_element,
10208 CE_PLAYER_TOUCHES_X,
10209 player->index_bit, center_side);
10215 #if USE_ELEMENT_TOUCHING_BUGFIX
10217 void TestIfElementTouchesCustomElement(int x, int y)
10219 static int xy[4][2] =
10226 static int trigger_sides[4][2] =
10228 /* center side border side */
10229 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
10230 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
10231 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
10232 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
10234 static int touch_dir[4] =
10236 MV_LEFT | MV_RIGHT,
10241 boolean change_center_element = FALSE;
10242 int center_element = Feld[x][y]; /* should always be non-moving! */
10243 int border_element_old[NUM_DIRECTIONS];
10246 for (i = 0; i < NUM_DIRECTIONS; i++)
10248 int xx = x + xy[i][0];
10249 int yy = y + xy[i][1];
10250 int border_element;
10252 border_element_old[i] = -1;
10254 if (!IN_LEV_FIELD(xx, yy))
10257 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10258 border_element = Feld[xx][yy]; /* may be moving! */
10259 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
10260 border_element = Feld[xx][yy];
10261 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
10262 border_element = MovingOrBlocked2Element(xx, yy);
10264 continue; /* center and border element do not touch */
10266 border_element_old[i] = border_element;
10269 for (i = 0; i < NUM_DIRECTIONS; i++)
10271 int xx = x + xy[i][0];
10272 int yy = y + xy[i][1];
10273 int center_side = trigger_sides[i][0];
10274 int border_element = border_element_old[i];
10276 if (border_element == -1)
10279 /* check for change of border element */
10280 CheckElementChangeBySide(xx, yy, border_element, center_element,
10281 CE_TOUCHING_X, center_side);
10284 for (i = 0; i < NUM_DIRECTIONS; i++)
10286 int border_side = trigger_sides[i][1];
10287 int border_element = border_element_old[i];
10289 if (border_element == -1)
10292 /* check for change of center element (but change it only once) */
10293 if (!change_center_element)
10294 change_center_element =
10295 CheckElementChangeBySide(x, y, center_element, border_element,
10296 CE_TOUCHING_X, border_side);
10302 void TestIfElementTouchesCustomElement_OLD(int x, int y)
10304 static int xy[4][2] =
10311 static int trigger_sides[4][2] =
10313 /* center side border side */
10314 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
10315 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
10316 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
10317 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
10319 static int touch_dir[4] =
10321 MV_LEFT | MV_RIGHT,
10326 boolean change_center_element = FALSE;
10327 int center_element = Feld[x][y]; /* should always be non-moving! */
10330 for (i = 0; i < NUM_DIRECTIONS; i++)
10332 int xx = x + xy[i][0];
10333 int yy = y + xy[i][1];
10334 int center_side = trigger_sides[i][0];
10335 int border_side = trigger_sides[i][1];
10336 int border_element;
10338 if (!IN_LEV_FIELD(xx, yy))
10341 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10342 border_element = Feld[xx][yy]; /* may be moving! */
10343 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
10344 border_element = Feld[xx][yy];
10345 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
10346 border_element = MovingOrBlocked2Element(xx, yy);
10348 continue; /* center and border element do not touch */
10350 /* check for change of center element (but change it only once) */
10351 if (!change_center_element)
10352 change_center_element =
10353 CheckElementChangeBySide(x, y, center_element, border_element,
10354 CE_TOUCHING_X, border_side);
10356 /* check for change of border element */
10357 CheckElementChangeBySide(xx, yy, border_element, center_element,
10358 CE_TOUCHING_X, center_side);
10364 void TestIfElementHitsCustomElement(int x, int y, int direction)
10366 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
10367 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
10368 int hitx = x + dx, hity = y + dy;
10369 int hitting_element = Feld[x][y];
10370 int touched_element;
10372 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
10375 touched_element = (IN_LEV_FIELD(hitx, hity) ?
10376 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
10378 if (IN_LEV_FIELD(hitx, hity))
10380 int opposite_direction = MV_DIR_OPPOSITE(direction);
10381 int hitting_side = direction;
10382 int touched_side = opposite_direction;
10383 boolean object_hit = (!IS_MOVING(hitx, hity) ||
10384 MovDir[hitx][hity] != direction ||
10385 ABS(MovPos[hitx][hity]) <= TILEY / 2);
10391 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10392 CE_HITTING_X, touched_side);
10394 CheckElementChangeBySide(hitx, hity, touched_element,
10395 hitting_element, CE_HIT_BY_X, hitting_side);
10397 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10398 CE_HIT_BY_SOMETHING, opposite_direction);
10402 /* "hitting something" is also true when hitting the playfield border */
10403 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10404 CE_HITTING_SOMETHING, direction);
10408 void TestIfElementSmashesCustomElement(int x, int y, int direction)
10410 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
10411 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
10412 int hitx = x + dx, hity = y + dy;
10413 int hitting_element = Feld[x][y];
10414 int touched_element;
10416 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
10417 !IS_FREE(hitx, hity) &&
10418 (!IS_MOVING(hitx, hity) ||
10419 MovDir[hitx][hity] != direction ||
10420 ABS(MovPos[hitx][hity]) <= TILEY / 2));
10423 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
10427 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
10431 touched_element = (IN_LEV_FIELD(hitx, hity) ?
10432 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
10434 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10435 EP_CAN_SMASH_EVERYTHING, direction);
10437 if (IN_LEV_FIELD(hitx, hity))
10439 int opposite_direction = MV_DIR_OPPOSITE(direction);
10440 int hitting_side = direction;
10441 int touched_side = opposite_direction;
10443 int touched_element = MovingOrBlocked2Element(hitx, hity);
10446 boolean object_hit = (!IS_MOVING(hitx, hity) ||
10447 MovDir[hitx][hity] != direction ||
10448 ABS(MovPos[hitx][hity]) <= TILEY / 2);
10457 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10458 CE_SMASHED_BY_SOMETHING, opposite_direction);
10460 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10461 CE_OTHER_IS_SMASHING, touched_side);
10463 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10464 CE_OTHER_GETS_SMASHED, hitting_side);
10470 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
10472 int i, kill_x = -1, kill_y = -1;
10474 int bad_element = -1;
10475 static int test_xy[4][2] =
10482 static int test_dir[4] =
10490 for (i = 0; i < NUM_DIRECTIONS; i++)
10492 int test_x, test_y, test_move_dir, test_element;
10494 test_x = good_x + test_xy[i][0];
10495 test_y = good_y + test_xy[i][1];
10497 if (!IN_LEV_FIELD(test_x, test_y))
10501 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
10503 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
10505 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
10506 2nd case: DONT_TOUCH style bad thing does not move away from good thing
10508 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
10509 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
10513 bad_element = test_element;
10519 if (kill_x != -1 || kill_y != -1)
10521 if (IS_PLAYER(good_x, good_y))
10523 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
10525 if (player->shield_deadly_time_left > 0 &&
10526 !IS_INDESTRUCTIBLE(bad_element))
10527 Bang(kill_x, kill_y);
10528 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
10529 KillPlayer(player);
10532 Bang(good_x, good_y);
10536 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
10538 int i, kill_x = -1, kill_y = -1;
10539 int bad_element = Feld[bad_x][bad_y];
10540 static int test_xy[4][2] =
10547 static int touch_dir[4] =
10549 MV_LEFT | MV_RIGHT,
10554 static int test_dir[4] =
10562 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
10565 for (i = 0; i < NUM_DIRECTIONS; i++)
10567 int test_x, test_y, test_move_dir, test_element;
10569 test_x = bad_x + test_xy[i][0];
10570 test_y = bad_y + test_xy[i][1];
10571 if (!IN_LEV_FIELD(test_x, test_y))
10575 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
10577 test_element = Feld[test_x][test_y];
10579 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
10580 2nd case: DONT_TOUCH style bad thing does not move away from good thing
10582 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
10583 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
10585 /* good thing is player or penguin that does not move away */
10586 if (IS_PLAYER(test_x, test_y))
10588 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
10590 if (bad_element == EL_ROBOT && player->is_moving)
10591 continue; /* robot does not kill player if he is moving */
10593 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10595 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
10596 continue; /* center and border element do not touch */
10603 else if (test_element == EL_PENGUIN)
10612 if (kill_x != -1 || kill_y != -1)
10614 if (IS_PLAYER(kill_x, kill_y))
10616 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
10618 if (player->shield_deadly_time_left > 0 &&
10619 !IS_INDESTRUCTIBLE(bad_element))
10620 Bang(bad_x, bad_y);
10621 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
10622 KillPlayer(player);
10625 Bang(kill_x, kill_y);
10629 void TestIfPlayerTouchesBadThing(int x, int y)
10631 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
10634 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
10636 TestIfGoodThingHitsBadThing(x, y, move_dir);
10639 void TestIfBadThingTouchesPlayer(int x, int y)
10641 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
10644 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
10646 TestIfBadThingHitsGoodThing(x, y, move_dir);
10649 void TestIfFriendTouchesBadThing(int x, int y)
10651 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
10654 void TestIfBadThingTouchesFriend(int x, int y)
10656 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
10659 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
10661 int i, kill_x = bad_x, kill_y = bad_y;
10662 static int xy[4][2] =
10670 for (i = 0; i < NUM_DIRECTIONS; i++)
10674 x = bad_x + xy[i][0];
10675 y = bad_y + xy[i][1];
10676 if (!IN_LEV_FIELD(x, y))
10679 element = Feld[x][y];
10680 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
10681 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
10689 if (kill_x != bad_x || kill_y != bad_y)
10690 Bang(bad_x, bad_y);
10693 void KillPlayer(struct PlayerInfo *player)
10695 int jx = player->jx, jy = player->jy;
10697 if (!player->active)
10700 /* remove accessible field at the player's position */
10701 Feld[jx][jy] = EL_EMPTY;
10703 /* deactivate shield (else Bang()/Explode() would not work right) */
10704 player->shield_normal_time_left = 0;
10705 player->shield_deadly_time_left = 0;
10708 BuryPlayer(player);
10711 static void KillPlayerUnlessEnemyProtected(int x, int y)
10713 if (!PLAYER_ENEMY_PROTECTED(x, y))
10714 KillPlayer(PLAYERINFO(x, y));
10717 static void KillPlayerUnlessExplosionProtected(int x, int y)
10719 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
10720 KillPlayer(PLAYERINFO(x, y));
10723 void BuryPlayer(struct PlayerInfo *player)
10725 int jx = player->jx, jy = player->jy;
10727 if (!player->active)
10730 PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
10731 PlayLevelSound(jx, jy, SND_GAME_LOSING);
10733 player->GameOver = TRUE;
10734 RemovePlayer(player);
10737 void RemovePlayer(struct PlayerInfo *player)
10739 int jx = player->jx, jy = player->jy;
10740 int i, found = FALSE;
10742 player->present = FALSE;
10743 player->active = FALSE;
10745 if (!ExplodeField[jx][jy])
10746 StorePlayer[jx][jy] = 0;
10748 if (player->is_moving)
10749 DrawLevelField(player->last_jx, player->last_jy);
10751 for (i = 0; i < MAX_PLAYERS; i++)
10752 if (stored_player[i].active)
10756 AllPlayersGone = TRUE;
10762 #if USE_NEW_SNAP_DELAY
10763 static void setFieldForSnapping(int x, int y, int element, int direction)
10765 struct ElementInfo *ei = &element_info[element];
10766 int direction_bit = MV_DIR_TO_BIT(direction);
10767 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
10768 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
10769 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
10771 Feld[x][y] = EL_ELEMENT_SNAPPING;
10772 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
10774 ResetGfxAnimation(x, y);
10776 GfxElement[x][y] = element;
10777 GfxAction[x][y] = action;
10778 GfxDir[x][y] = direction;
10779 GfxFrame[x][y] = -1;
10784 =============================================================================
10785 checkDiagonalPushing()
10786 -----------------------------------------------------------------------------
10787 check if diagonal input device direction results in pushing of object
10788 (by checking if the alternative direction is walkable, diggable, ...)
10789 =============================================================================
10792 static boolean checkDiagonalPushing(struct PlayerInfo *player,
10793 int x, int y, int real_dx, int real_dy)
10795 int jx, jy, dx, dy, xx, yy;
10797 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
10800 /* diagonal direction: check alternative direction */
10805 xx = jx + (dx == 0 ? real_dx : 0);
10806 yy = jy + (dy == 0 ? real_dy : 0);
10808 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
10812 =============================================================================
10814 -----------------------------------------------------------------------------
10815 x, y: field next to player (non-diagonal) to try to dig to
10816 real_dx, real_dy: direction as read from input device (can be diagonal)
10817 =============================================================================
10820 int DigField(struct PlayerInfo *player,
10821 int oldx, int oldy, int x, int y,
10822 int real_dx, int real_dy, int mode)
10824 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
10825 boolean player_was_pushing = player->is_pushing;
10826 boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
10827 boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
10828 int jx = oldx, jy = oldy;
10829 int dx = x - jx, dy = y - jy;
10830 int nextx = x + dx, nexty = y + dy;
10831 int move_direction = (dx == -1 ? MV_LEFT :
10832 dx == +1 ? MV_RIGHT :
10834 dy == +1 ? MV_DOWN : MV_NONE);
10835 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
10836 int dig_side = MV_DIR_OPPOSITE(move_direction);
10837 int old_element = Feld[jx][jy];
10838 #if USE_FIXED_DONT_RUN_INTO
10839 int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
10845 if (is_player) /* function can also be called by EL_PENGUIN */
10847 if (player->MovPos == 0)
10849 player->is_digging = FALSE;
10850 player->is_collecting = FALSE;
10853 if (player->MovPos == 0) /* last pushing move finished */
10854 player->is_pushing = FALSE;
10856 if (mode == DF_NO_PUSH) /* player just stopped pushing */
10858 player->is_switching = FALSE;
10859 player->push_delay = -1;
10861 return MP_NO_ACTION;
10865 #if !USE_FIXED_DONT_RUN_INTO
10866 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
10867 return MP_NO_ACTION;
10870 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
10871 old_element = Back[jx][jy];
10873 /* in case of element dropped at player position, check background */
10874 else if (Back[jx][jy] != EL_EMPTY &&
10875 game.engine_version >= VERSION_IDENT(2,2,0,0))
10876 old_element = Back[jx][jy];
10879 #if USE_FIXED_DONT_RUN_INTO
10880 if (player_can_move && DONT_RUN_INTO(element))
10882 if (element == EL_ACID && dx == 0 && dy == 1)
10885 Feld[jx][jy] = EL_PLAYER_1;
10886 InitMovingField(jx, jy, MV_DOWN);
10887 Store[jx][jy] = EL_ACID;
10888 ContinueMoving(jx, jy);
10889 BuryPlayer(player);
10892 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
10894 return MP_DONT_RUN_INTO;
10900 #if USE_FIXED_DONT_RUN_INTO
10901 if (player_can_move && DONT_RUN_INTO(element))
10903 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
10905 return MP_DONT_RUN_INTO;
10910 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
10911 return MP_NO_ACTION; /* field has no opening in this direction */
10913 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
10914 return MP_NO_ACTION; /* field has no opening in this direction */
10917 #if USE_FIXED_DONT_RUN_INTO
10918 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
10921 Feld[jx][jy] = EL_PLAYER_1;
10922 InitMovingField(jx, jy, MV_DOWN);
10923 Store[jx][jy] = EL_ACID;
10924 ContinueMoving(jx, jy);
10925 BuryPlayer(player);
10927 return MP_DONT_RUN_INTO;
10933 #if USE_FIXED_DONT_RUN_INTO
10934 if (player_can_move && DONT_RUN_INTO(element))
10936 if (element == EL_ACID && dx == 0 && dy == 1)
10939 Feld[jx][jy] = EL_PLAYER_1;
10940 InitMovingField(jx, jy, MV_DOWN);
10941 Store[jx][jy] = EL_ACID;
10942 ContinueMoving(jx, jy);
10943 BuryPlayer(player);
10946 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
10948 return MP_DONT_RUN_INTO;
10953 #if USE_FIXED_DONT_RUN_INTO
10954 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
10955 return MP_NO_ACTION;
10958 #if !USE_FIXED_DONT_RUN_INTO
10959 element = Feld[x][y];
10962 collect_count = element_info[element].collect_count_initial;
10964 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
10965 return MP_NO_ACTION;
10967 if (game.engine_version < VERSION_IDENT(2,2,0,0))
10968 player_can_move = player_can_move_or_snap;
10970 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
10971 game.engine_version >= VERSION_IDENT(2,2,0,0))
10973 CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
10974 player->index_bit, dig_side);
10975 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
10976 player->index_bit, dig_side);
10978 if (Feld[x][y] != element) /* field changed by snapping */
10981 return MP_NO_ACTION;
10984 if (game.gravity && is_player && !player->is_auto_moving &&
10985 canFallDown(player) && move_direction != MV_DOWN &&
10986 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
10987 return MP_NO_ACTION; /* player cannot walk here due to gravity */
10989 if (player_can_move &&
10990 IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
10992 int sound_element = SND_ELEMENT(element);
10993 int sound_action = ACTION_WALKING;
10995 if (IS_RND_GATE(element))
10997 if (!player->key[RND_GATE_NR(element)])
10998 return MP_NO_ACTION;
11000 else if (IS_RND_GATE_GRAY(element))
11002 if (!player->key[RND_GATE_GRAY_NR(element)])
11003 return MP_NO_ACTION;
11005 else if (IS_RND_GATE_GRAY_ACTIVE(element))
11007 if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
11008 return MP_NO_ACTION;
11010 else if (element == EL_EXIT_OPEN ||
11011 element == EL_SP_EXIT_OPEN ||
11012 element == EL_SP_EXIT_OPENING)
11014 sound_action = ACTION_PASSING; /* player is passing exit */
11016 else if (element == EL_EMPTY)
11018 sound_action = ACTION_MOVING; /* nothing to walk on */
11021 /* play sound from background or player, whatever is available */
11022 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
11023 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
11025 PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
11027 else if (player_can_move &&
11028 IS_PASSABLE(element) && canPassField(x, y, move_direction))
11030 if (!ACCESS_FROM(element, opposite_direction))
11031 return MP_NO_ACTION; /* field not accessible from this direction */
11033 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
11034 return MP_NO_ACTION;
11036 if (IS_EM_GATE(element))
11038 if (!player->key[EM_GATE_NR(element)])
11039 return MP_NO_ACTION;
11041 else if (IS_EM_GATE_GRAY(element))
11043 if (!player->key[EM_GATE_GRAY_NR(element)])
11044 return MP_NO_ACTION;
11046 else if (IS_EM_GATE_GRAY_ACTIVE(element))
11048 if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
11049 return MP_NO_ACTION;
11051 else if (IS_SP_PORT(element))
11053 if (element == EL_SP_GRAVITY_PORT_LEFT ||
11054 element == EL_SP_GRAVITY_PORT_RIGHT ||
11055 element == EL_SP_GRAVITY_PORT_UP ||
11056 element == EL_SP_GRAVITY_PORT_DOWN)
11057 game.gravity = !game.gravity;
11058 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
11059 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
11060 element == EL_SP_GRAVITY_ON_PORT_UP ||
11061 element == EL_SP_GRAVITY_ON_PORT_DOWN)
11062 game.gravity = TRUE;
11063 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
11064 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
11065 element == EL_SP_GRAVITY_OFF_PORT_UP ||
11066 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
11067 game.gravity = FALSE;
11070 /* automatically move to the next field with double speed */
11071 player->programmed_action = move_direction;
11073 if (player->move_delay_reset_counter == 0)
11075 player->move_delay_reset_counter = 2; /* two double speed steps */
11077 DOUBLE_PLAYER_SPEED(player);
11080 PlayLevelSoundAction(x, y, ACTION_PASSING);
11082 else if (player_can_move_or_snap && IS_DIGGABLE(element))
11086 if (mode != DF_SNAP)
11088 GfxElement[x][y] = GFX_ELEMENT(element);
11089 player->is_digging = TRUE;
11092 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
11094 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
11095 player->index_bit, dig_side);
11097 if (mode == DF_SNAP)
11099 #if USE_NEW_SNAP_DELAY
11100 if (level.block_snap_field)
11101 setFieldForSnapping(x, y, element, move_direction);
11103 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11105 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11108 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
11109 player->index_bit, dig_side);
11112 else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
11116 if (is_player && mode != DF_SNAP)
11118 GfxElement[x][y] = element;
11119 player->is_collecting = TRUE;
11122 if (element == EL_SPEED_PILL)
11124 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
11126 else if (element == EL_EXTRA_TIME && level.time > 0)
11128 TimeLeft += level.extra_time;
11129 DrawGameValue_Time(TimeLeft);
11131 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
11133 player->shield_normal_time_left += level.shield_normal_time;
11134 if (element == EL_SHIELD_DEADLY)
11135 player->shield_deadly_time_left += level.shield_deadly_time;
11137 else if (element == EL_DYNAMITE ||
11138 element == EL_EM_DYNAMITE ||
11139 element == EL_SP_DISK_RED)
11141 if (player->inventory_size < MAX_INVENTORY_SIZE)
11142 player->inventory_element[player->inventory_size++] = element;
11144 DrawGameValue_Dynamite(local_player->inventory_size);
11146 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
11148 player->dynabomb_count++;
11149 player->dynabombs_left++;
11151 else if (element == EL_DYNABOMB_INCREASE_SIZE)
11153 player->dynabomb_size++;
11155 else if (element == EL_DYNABOMB_INCREASE_POWER)
11157 player->dynabomb_xl = TRUE;
11159 else if (IS_KEY(element))
11161 player->key[KEY_NR(element)] = TRUE;
11163 DrawGameValue_Keys(player->key);
11165 redraw_mask |= REDRAW_DOOR_1;
11167 else if (IS_ENVELOPE(element))
11169 player->show_envelope = element;
11171 else if (element == EL_EMC_LENSES)
11173 game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
11175 RedrawAllInvisibleElementsForLenses();
11177 else if (element == EL_EMC_MAGNIFIER)
11179 game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
11181 RedrawAllInvisibleElementsForMagnifier();
11183 else if (IS_DROPPABLE(element) ||
11184 IS_THROWABLE(element)) /* can be collected and dropped */
11188 if (collect_count == 0)
11189 player->inventory_infinite_element = element;
11191 for (i = 0; i < collect_count; i++)
11192 if (player->inventory_size < MAX_INVENTORY_SIZE)
11193 player->inventory_element[player->inventory_size++] = element;
11195 DrawGameValue_Dynamite(local_player->inventory_size);
11197 else if (collect_count > 0)
11199 local_player->gems_still_needed -= collect_count;
11200 if (local_player->gems_still_needed < 0)
11201 local_player->gems_still_needed = 0;
11203 DrawGameValue_Emeralds(local_player->gems_still_needed);
11206 RaiseScoreElement(element);
11207 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
11210 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
11211 player->index_bit, dig_side);
11213 if (mode == DF_SNAP)
11215 #if USE_NEW_SNAP_DELAY
11216 if (level.block_snap_field)
11217 setFieldForSnapping(x, y, element, move_direction);
11219 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11221 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11224 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
11225 player->index_bit, dig_side);
11228 else if (player_can_move_or_snap && IS_PUSHABLE(element))
11230 if (mode == DF_SNAP && element != EL_BD_ROCK)
11231 return MP_NO_ACTION;
11233 if (CAN_FALL(element) && dy)
11234 return MP_NO_ACTION;
11236 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
11237 !(element == EL_SPRING && level.use_spring_bug))
11238 return MP_NO_ACTION;
11240 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
11241 ((move_direction & MV_VERTICAL &&
11242 ((element_info[element].move_pattern & MV_LEFT &&
11243 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
11244 (element_info[element].move_pattern & MV_RIGHT &&
11245 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
11246 (move_direction & MV_HORIZONTAL &&
11247 ((element_info[element].move_pattern & MV_UP &&
11248 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
11249 (element_info[element].move_pattern & MV_DOWN &&
11250 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
11251 return MP_NO_ACTION;
11253 /* do not push elements already moving away faster than player */
11254 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
11255 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
11256 return MP_NO_ACTION;
11258 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
11260 if (player->push_delay_value == -1 || !player_was_pushing)
11261 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11263 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11265 if (player->push_delay_value == -1)
11266 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11268 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
11270 if (!player->is_pushing)
11271 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11274 player->is_pushing = TRUE;
11276 if (!(IN_LEV_FIELD(nextx, nexty) &&
11277 (IS_FREE(nextx, nexty) ||
11278 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
11279 IS_SB_ELEMENT(element)))))
11280 return MP_NO_ACTION;
11282 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
11283 return MP_NO_ACTION;
11285 if (player->push_delay == -1) /* new pushing; restart delay */
11286 player->push_delay = 0;
11288 if (player->push_delay < player->push_delay_value &&
11289 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
11290 element != EL_SPRING && element != EL_BALLOON)
11292 /* make sure that there is no move delay before next try to push */
11293 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11294 player->move_delay = 0;
11296 return MP_NO_ACTION;
11299 if (IS_SB_ELEMENT(element))
11301 if (element == EL_SOKOBAN_FIELD_FULL)
11303 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
11304 local_player->sokobanfields_still_needed++;
11307 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
11309 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
11310 local_player->sokobanfields_still_needed--;
11313 Feld[x][y] = EL_SOKOBAN_OBJECT;
11315 if (Back[x][y] == Back[nextx][nexty])
11316 PlayLevelSoundAction(x, y, ACTION_PUSHING);
11317 else if (Back[x][y] != 0)
11318 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
11321 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
11324 if (local_player->sokobanfields_still_needed == 0 &&
11325 game.emulation == EMU_SOKOBAN)
11327 player->LevelSolved = player->GameOver = TRUE;
11328 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
11332 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
11334 InitMovingField(x, y, move_direction);
11335 GfxAction[x][y] = ACTION_PUSHING;
11337 if (mode == DF_SNAP)
11338 ContinueMoving(x, y);
11340 MovPos[x][y] = (dx != 0 ? dx : dy);
11342 Pushed[x][y] = TRUE;
11343 Pushed[nextx][nexty] = TRUE;
11345 if (game.engine_version < VERSION_IDENT(2,2,0,7))
11346 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11348 player->push_delay_value = -1; /* get new value later */
11350 /* check for element change _after_ element has been pushed */
11351 if (game.use_change_when_pushing_bug)
11353 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
11354 player->index_bit, dig_side);
11355 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
11356 player->index_bit, dig_side);
11359 else if (IS_SWITCHABLE(element))
11361 if (PLAYER_SWITCHING(player, x, y))
11363 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
11364 player->index_bit, dig_side);
11369 player->is_switching = TRUE;
11370 player->switch_x = x;
11371 player->switch_y = y;
11373 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
11375 if (element == EL_ROBOT_WHEEL)
11377 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
11381 DrawLevelField(x, y);
11383 else if (element == EL_SP_TERMINAL)
11388 SCAN_PLAYFIELD(xx, yy)
11390 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
11393 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
11395 else if (Feld[xx][yy] == EL_SP_TERMINAL)
11396 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
11399 else if (IS_BELT_SWITCH(element))
11401 ToggleBeltSwitch(x, y);
11403 else if (element == EL_SWITCHGATE_SWITCH_UP ||
11404 element == EL_SWITCHGATE_SWITCH_DOWN)
11406 ToggleSwitchgateSwitch(x, y);
11408 else if (element == EL_LIGHT_SWITCH ||
11409 element == EL_LIGHT_SWITCH_ACTIVE)
11411 ToggleLightSwitch(x, y);
11413 else if (element == EL_TIMEGATE_SWITCH)
11415 ActivateTimegateSwitch(x, y);
11417 else if (element == EL_BALLOON_SWITCH_LEFT ||
11418 element == EL_BALLOON_SWITCH_RIGHT ||
11419 element == EL_BALLOON_SWITCH_UP ||
11420 element == EL_BALLOON_SWITCH_DOWN ||
11421 element == EL_BALLOON_SWITCH_NONE ||
11422 element == EL_BALLOON_SWITCH_ANY)
11424 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
11425 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
11426 element == EL_BALLOON_SWITCH_UP ? MV_UP :
11427 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
11428 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
11431 else if (element == EL_LAMP)
11433 Feld[x][y] = EL_LAMP_ACTIVE;
11434 local_player->lights_still_needed--;
11436 ResetGfxAnimation(x, y);
11437 DrawLevelField(x, y);
11439 else if (element == EL_TIME_ORB_FULL)
11441 Feld[x][y] = EL_TIME_ORB_EMPTY;
11443 if (level.time > 0 || level.use_time_orb_bug)
11445 TimeLeft += level.time_orb_time;
11446 DrawGameValue_Time(TimeLeft);
11449 ResetGfxAnimation(x, y);
11450 DrawLevelField(x, y);
11452 else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
11453 element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
11457 game.ball_state = !game.ball_state;
11460 SCAN_PLAYFIELD(xx, yy)
11462 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
11465 int e = Feld[xx][yy];
11467 if (game.ball_state)
11469 if (e == EL_EMC_MAGIC_BALL)
11470 CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
11471 else if (e == EL_EMC_MAGIC_BALL_SWITCH)
11472 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
11476 if (e == EL_EMC_MAGIC_BALL_ACTIVE)
11477 CreateField(xx, yy, EL_EMC_MAGIC_BALL);
11478 else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
11479 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
11484 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
11485 player->index_bit, dig_side);
11487 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
11488 player->index_bit, dig_side);
11490 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
11491 player->index_bit, dig_side);
11497 if (!PLAYER_SWITCHING(player, x, y))
11499 player->is_switching = TRUE;
11500 player->switch_x = x;
11501 player->switch_y = y;
11503 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
11504 player->index_bit, dig_side);
11505 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
11506 player->index_bit, dig_side);
11508 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
11509 player->index_bit, dig_side);
11510 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
11511 player->index_bit, dig_side);
11514 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
11515 player->index_bit, dig_side);
11516 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
11517 player->index_bit, dig_side);
11519 return MP_NO_ACTION;
11522 player->push_delay = -1;
11524 if (is_player) /* function can also be called by EL_PENGUIN */
11526 if (Feld[x][y] != element) /* really digged/collected something */
11527 player->is_collecting = !player->is_digging;
11533 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
11535 int jx = player->jx, jy = player->jy;
11536 int x = jx + dx, y = jy + dy;
11537 int snap_direction = (dx == -1 ? MV_LEFT :
11538 dx == +1 ? MV_RIGHT :
11540 dy == +1 ? MV_DOWN : MV_NONE);
11542 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
11545 if (!player->active || !IN_LEV_FIELD(x, y))
11553 if (player->MovPos == 0)
11554 player->is_pushing = FALSE;
11556 player->is_snapping = FALSE;
11558 if (player->MovPos == 0)
11560 player->is_moving = FALSE;
11561 player->is_digging = FALSE;
11562 player->is_collecting = FALSE;
11568 if (player->is_snapping)
11571 player->MovDir = snap_direction;
11573 if (player->MovPos == 0)
11575 player->is_moving = FALSE;
11576 player->is_digging = FALSE;
11577 player->is_collecting = FALSE;
11580 player->is_dropping = FALSE;
11582 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
11585 player->is_snapping = TRUE;
11587 if (player->MovPos == 0)
11589 player->is_moving = FALSE;
11590 player->is_digging = FALSE;
11591 player->is_collecting = FALSE;
11594 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
11595 DrawLevelField(player->last_jx, player->last_jy);
11597 DrawLevelField(x, y);
11602 boolean DropElement(struct PlayerInfo *player)
11604 int old_element, new_element;
11605 int dropx = player->jx, dropy = player->jy;
11606 int drop_direction = player->MovDir;
11607 int drop_side = drop_direction;
11608 int drop_element = (player->inventory_size > 0 ?
11609 player->inventory_element[player->inventory_size - 1] :
11610 player->inventory_infinite_element != EL_UNDEFINED ?
11611 player->inventory_infinite_element :
11612 player->dynabombs_left > 0 ?
11613 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
11616 /* do not drop an element on top of another element; when holding drop key
11617 pressed without moving, dropped element must move away before the next
11618 element can be dropped (this is especially important if the next element
11619 is dynamite, which can be placed on background for historical reasons) */
11620 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
11623 if (IS_THROWABLE(drop_element))
11625 dropx += GET_DX_FROM_DIR(drop_direction);
11626 dropy += GET_DY_FROM_DIR(drop_direction);
11628 if (!IN_LEV_FIELD(dropx, dropy))
11632 old_element = Feld[dropx][dropy]; /* old element at dropping position */
11633 new_element = drop_element; /* default: no change when dropping */
11635 /* check if player is active, not moving and ready to drop */
11636 if (!player->active || player->MovPos || player->drop_delay > 0)
11639 /* check if player has anything that can be dropped */
11640 if (new_element == EL_UNDEFINED)
11643 /* check if anything can be dropped at the current position */
11644 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
11647 /* collected custom elements can only be dropped on empty fields */
11648 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
11651 if (old_element != EL_EMPTY)
11652 Back[dropx][dropy] = old_element; /* store old element on this field */
11654 ResetGfxAnimation(dropx, dropy);
11655 ResetRandomAnimationValue(dropx, dropy);
11657 if (player->inventory_size > 0 ||
11658 player->inventory_infinite_element != EL_UNDEFINED)
11660 if (player->inventory_size > 0)
11662 player->inventory_size--;
11664 DrawGameValue_Dynamite(local_player->inventory_size);
11666 if (new_element == EL_DYNAMITE)
11667 new_element = EL_DYNAMITE_ACTIVE;
11668 else if (new_element == EL_EM_DYNAMITE)
11669 new_element = EL_EM_DYNAMITE_ACTIVE;
11670 else if (new_element == EL_SP_DISK_RED)
11671 new_element = EL_SP_DISK_RED_ACTIVE;
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);
11682 /* needed if previous element just changed to "empty" in the last frame */
11683 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
11685 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
11686 player->index_bit, drop_side);
11687 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
11689 player->index_bit, drop_side);
11691 TestIfElementTouchesCustomElement(dropx, dropy);
11693 else /* player is dropping a dyna bomb */
11695 player->dynabombs_left--;
11697 Feld[dropx][dropy] = new_element;
11699 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
11700 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
11701 el2img(Feld[dropx][dropy]), 0);
11703 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
11706 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
11707 InitField_WithBug1(dropx, dropy, FALSE);
11709 new_element = Feld[dropx][dropy]; /* element might have changed */
11711 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
11712 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
11714 int move_direction, nextx, nexty;
11716 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
11717 MovDir[dropx][dropy] = drop_direction;
11719 move_direction = MovDir[dropx][dropy];
11720 nextx = dropx + GET_DX_FROM_DIR(move_direction);
11721 nexty = dropy + GET_DY_FROM_DIR(move_direction);
11723 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
11724 CheckCollision[dropx][dropy] = 2;
11727 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
11728 player->is_dropping = TRUE;
11730 player->drop_x = dropx;
11731 player->drop_y = dropy;
11736 /* ------------------------------------------------------------------------- */
11737 /* game sound playing functions */
11738 /* ------------------------------------------------------------------------- */
11740 static int *loop_sound_frame = NULL;
11741 static int *loop_sound_volume = NULL;
11743 void InitPlayLevelSound()
11745 int num_sounds = getSoundListSize();
11747 checked_free(loop_sound_frame);
11748 checked_free(loop_sound_volume);
11750 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
11751 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
11754 static void PlayLevelSound(int x, int y, int nr)
11756 int sx = SCREENX(x), sy = SCREENY(y);
11757 int volume, stereo_position;
11758 int max_distance = 8;
11759 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
11761 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
11762 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
11765 if (!IN_LEV_FIELD(x, y) ||
11766 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
11767 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
11770 volume = SOUND_MAX_VOLUME;
11772 if (!IN_SCR_FIELD(sx, sy))
11774 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
11775 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
11777 volume -= volume * (dx > dy ? dx : dy) / max_distance;
11780 stereo_position = (SOUND_MAX_LEFT +
11781 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
11782 (SCR_FIELDX + 2 * max_distance));
11784 if (IS_LOOP_SOUND(nr))
11786 /* This assures that quieter loop sounds do not overwrite louder ones,
11787 while restarting sound volume comparison with each new game frame. */
11789 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
11792 loop_sound_volume[nr] = volume;
11793 loop_sound_frame[nr] = FrameCounter;
11796 PlaySoundExt(nr, volume, stereo_position, type);
11799 static void PlayLevelSoundNearest(int x, int y, int sound_action)
11801 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
11802 x > LEVELX(BX2) ? LEVELX(BX2) : x,
11803 y < LEVELY(BY1) ? LEVELY(BY1) :
11804 y > LEVELY(BY2) ? LEVELY(BY2) : y,
11808 static void PlayLevelSoundAction(int x, int y, int action)
11810 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
11813 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
11815 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
11817 if (sound_effect != SND_UNDEFINED)
11818 PlayLevelSound(x, y, sound_effect);
11821 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
11824 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
11826 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
11827 PlayLevelSound(x, y, sound_effect);
11830 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
11832 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
11834 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
11835 PlayLevelSound(x, y, sound_effect);
11838 static void StopLevelSoundActionIfLoop(int x, int y, int action)
11840 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
11842 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
11843 StopSound(sound_effect);
11846 static void PlayLevelMusic()
11848 if (levelset.music[level_nr] != MUS_UNDEFINED)
11849 PlayMusic(levelset.music[level_nr]); /* from config file */
11851 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
11854 void PlayLevelSound_EM(int x, int y, int element_em, int sample)
11856 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
11861 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
11865 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
11869 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
11873 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
11877 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
11881 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
11885 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
11888 case SAMPLE_android_clone:
11889 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
11892 case SAMPLE_android_move:
11893 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
11896 case SAMPLE_spring:
11897 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
11901 PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
11905 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
11908 case SAMPLE_eater_eat:
11909 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
11913 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
11916 case SAMPLE_collect:
11917 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
11920 case SAMPLE_diamond:
11921 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
11924 case SAMPLE_squash:
11925 /* !!! CHECK THIS !!! */
11927 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
11929 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
11933 case SAMPLE_wonderfall:
11934 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
11938 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
11942 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
11946 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
11950 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
11954 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
11958 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
11961 case SAMPLE_wonder:
11962 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
11966 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
11969 case SAMPLE_exit_open:
11970 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
11973 case SAMPLE_exit_leave:
11974 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
11977 case SAMPLE_dynamite:
11978 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
11982 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
11986 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
11990 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
11994 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
11998 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
12002 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
12006 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
12011 void RaiseScore(int value)
12013 local_player->score += value;
12015 DrawGameValue_Score(local_player->score);
12018 void RaiseScoreElement(int element)
12023 case EL_BD_DIAMOND:
12024 case EL_EMERALD_YELLOW:
12025 case EL_EMERALD_RED:
12026 case EL_EMERALD_PURPLE:
12027 case EL_SP_INFOTRON:
12028 RaiseScore(level.score[SC_EMERALD]);
12031 RaiseScore(level.score[SC_DIAMOND]);
12034 RaiseScore(level.score[SC_CRYSTAL]);
12037 RaiseScore(level.score[SC_PEARL]);
12040 case EL_BD_BUTTERFLY:
12041 case EL_SP_ELECTRON:
12042 RaiseScore(level.score[SC_BUG]);
12045 case EL_BD_FIREFLY:
12046 case EL_SP_SNIKSNAK:
12047 RaiseScore(level.score[SC_SPACESHIP]);
12050 case EL_DARK_YAMYAM:
12051 RaiseScore(level.score[SC_YAMYAM]);
12054 RaiseScore(level.score[SC_ROBOT]);
12057 RaiseScore(level.score[SC_PACMAN]);
12060 RaiseScore(level.score[SC_NUT]);
12063 case EL_EM_DYNAMITE:
12064 case EL_SP_DISK_RED:
12065 case EL_DYNABOMB_INCREASE_NUMBER:
12066 case EL_DYNABOMB_INCREASE_SIZE:
12067 case EL_DYNABOMB_INCREASE_POWER:
12068 RaiseScore(level.score[SC_DYNAMITE]);
12070 case EL_SHIELD_NORMAL:
12071 case EL_SHIELD_DEADLY:
12072 RaiseScore(level.score[SC_SHIELD]);
12074 case EL_EXTRA_TIME:
12075 RaiseScore(level.extra_time_score);
12089 RaiseScore(level.score[SC_KEY]);
12092 RaiseScore(element_info[element].collect_score);
12097 void RequestQuitGame(boolean ask_if_really_quit)
12099 if (AllPlayersGone ||
12100 !ask_if_really_quit ||
12101 level_editor_test_game ||
12102 Request("Do you really want to quit the game ?",
12103 REQ_ASK | REQ_STAY_CLOSED))
12105 #if defined(NETWORK_AVALIABLE)
12106 if (options.network)
12107 SendToServer_StopPlaying();
12111 game_status = GAME_MODE_MAIN;
12117 if (tape.playing && tape.deactivate_display)
12118 TapeDeactivateDisplayOff(TRUE);
12120 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
12122 if (tape.playing && tape.deactivate_display)
12123 TapeDeactivateDisplayOn();
12128 /* ---------- new game button stuff ---------------------------------------- */
12130 /* graphic position values for game buttons */
12131 #define GAME_BUTTON_XSIZE 30
12132 #define GAME_BUTTON_YSIZE 30
12133 #define GAME_BUTTON_XPOS 5
12134 #define GAME_BUTTON_YPOS 215
12135 #define SOUND_BUTTON_XPOS 5
12136 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
12138 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
12139 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
12140 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
12141 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
12142 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
12143 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
12150 } gamebutton_info[NUM_GAME_BUTTONS] =
12153 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
12158 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
12159 GAME_CTRL_ID_PAUSE,
12163 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
12168 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
12169 SOUND_CTRL_ID_MUSIC,
12170 "background music on/off"
12173 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
12174 SOUND_CTRL_ID_LOOPS,
12175 "sound loops on/off"
12178 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
12179 SOUND_CTRL_ID_SIMPLE,
12180 "normal sounds on/off"
12184 void CreateGameButtons()
12188 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12190 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
12191 struct GadgetInfo *gi;
12194 unsigned long event_mask;
12195 int gd_xoffset, gd_yoffset;
12196 int gd_x1, gd_x2, gd_y1, gd_y2;
12199 gd_xoffset = gamebutton_info[i].x;
12200 gd_yoffset = gamebutton_info[i].y;
12201 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
12202 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
12204 if (id == GAME_CTRL_ID_STOP ||
12205 id == GAME_CTRL_ID_PAUSE ||
12206 id == GAME_CTRL_ID_PLAY)
12208 button_type = GD_TYPE_NORMAL_BUTTON;
12210 event_mask = GD_EVENT_RELEASED;
12211 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12212 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12216 button_type = GD_TYPE_CHECK_BUTTON;
12218 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
12219 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
12220 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
12221 event_mask = GD_EVENT_PRESSED;
12222 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
12223 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12226 gi = CreateGadget(GDI_CUSTOM_ID, id,
12227 GDI_INFO_TEXT, gamebutton_info[i].infotext,
12228 GDI_X, DX + gd_xoffset,
12229 GDI_Y, DY + gd_yoffset,
12230 GDI_WIDTH, GAME_BUTTON_XSIZE,
12231 GDI_HEIGHT, GAME_BUTTON_YSIZE,
12232 GDI_TYPE, button_type,
12233 GDI_STATE, GD_BUTTON_UNPRESSED,
12234 GDI_CHECKED, checked,
12235 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
12236 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
12237 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
12238 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
12239 GDI_EVENT_MASK, event_mask,
12240 GDI_CALLBACK_ACTION, HandleGameButtons,
12244 Error(ERR_EXIT, "cannot create gadget");
12246 game_gadget[id] = gi;
12250 void FreeGameButtons()
12254 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12255 FreeGadget(game_gadget[i]);
12258 static void MapGameButtons()
12262 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12263 MapGadget(game_gadget[i]);
12266 void UnmapGameButtons()
12270 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12271 UnmapGadget(game_gadget[i]);
12274 static void HandleGameButtons(struct GadgetInfo *gi)
12276 int id = gi->custom_id;
12278 if (game_status != GAME_MODE_PLAYING)
12283 case GAME_CTRL_ID_STOP:
12284 RequestQuitGame(TRUE);
12287 case GAME_CTRL_ID_PAUSE:
12288 if (options.network)
12290 #if defined(NETWORK_AVALIABLE)
12292 SendToServer_ContinuePlaying();
12294 SendToServer_PausePlaying();
12298 TapeTogglePause(TAPE_TOGGLE_MANUAL);
12301 case GAME_CTRL_ID_PLAY:
12304 #if defined(NETWORK_AVALIABLE)
12305 if (options.network)
12306 SendToServer_ContinuePlaying();
12310 tape.pausing = FALSE;
12311 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
12316 case SOUND_CTRL_ID_MUSIC:
12317 if (setup.sound_music)
12319 setup.sound_music = FALSE;
12322 else if (audio.music_available)
12324 setup.sound = setup.sound_music = TRUE;
12326 SetAudioMode(setup.sound);
12332 case SOUND_CTRL_ID_LOOPS:
12333 if (setup.sound_loops)
12334 setup.sound_loops = FALSE;
12335 else if (audio.loops_available)
12337 setup.sound = setup.sound_loops = TRUE;
12338 SetAudioMode(setup.sound);
12342 case SOUND_CTRL_ID_SIMPLE:
12343 if (setup.sound_simple)
12344 setup.sound_simple = FALSE;
12345 else if (audio.sound_available)
12347 setup.sound = setup.sound_simple = TRUE;
12348 SetAudioMode(setup.sound);