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_player_nr = game.centered_player_nr_next = -1; /* focus all */
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);
3270 /* only visually relocate centered player */
3271 if (player->index_nr == game.centered_player_nr)
3272 DrawRelocatePlayer(player, level.instant_relocation);
3274 if (player == local_player) /* only visually relocate local player */
3275 DrawRelocatePlayer(player, level.instant_relocation);
3278 TestIfPlayerTouchesBadThing(jx, jy);
3279 TestIfPlayerTouchesCustomElement(jx, jy);
3281 if (IS_CUSTOM_ELEMENT(element))
3282 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
3283 player->index_bit, enter_side);
3285 CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
3286 player->index_bit, enter_side);
3289 void Explode(int ex, int ey, int phase, int mode)
3295 /* !!! eliminate this variable !!! */
3296 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
3298 if (game.explosions_delayed)
3300 ExplodeField[ex][ey] = mode;
3304 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
3306 int center_element = Feld[ex][ey];
3307 int artwork_element, explosion_element; /* set these values later */
3310 /* --- This is only really needed (and now handled) in "Impact()". --- */
3311 /* do not explode moving elements that left the explode field in time */
3312 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
3313 center_element == EL_EMPTY &&
3314 (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
3319 /* !!! at this place, the center element may be EL_BLOCKED !!! */
3320 if (mode == EX_TYPE_NORMAL ||
3321 mode == EX_TYPE_CENTER ||
3322 mode == EX_TYPE_CROSS)
3323 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
3326 /* remove things displayed in background while burning dynamite */
3327 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
3330 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
3332 /* put moving element to center field (and let it explode there) */
3333 center_element = MovingOrBlocked2Element(ex, ey);
3334 RemoveMovingField(ex, ey);
3335 Feld[ex][ey] = center_element;
3338 /* now "center_element" is finally determined -- set related values now */
3339 artwork_element = center_element; /* for custom player artwork */
3340 explosion_element = center_element; /* for custom player artwork */
3342 if (IS_PLAYER(ex, ey))
3344 int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
3346 artwork_element = stored_player[player_nr].artwork_element;
3348 if (level.use_explosion_element[player_nr])
3350 explosion_element = level.explosion_element[player_nr];
3351 artwork_element = explosion_element;
3356 if (mode == EX_TYPE_NORMAL ||
3357 mode == EX_TYPE_CENTER ||
3358 mode == EX_TYPE_CROSS)
3359 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
3363 last_phase = element_info[explosion_element].explosion_delay + 1;
3365 last_phase = element_info[center_element].explosion_delay + 1;
3368 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
3370 int xx = x - ex + 1;
3371 int yy = y - ey + 1;
3374 if (!IN_LEV_FIELD(x, y) ||
3375 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
3376 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
3379 element = Feld[x][y];
3381 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
3383 element = MovingOrBlocked2Element(x, y);
3385 if (!IS_EXPLOSION_PROOF(element))
3386 RemoveMovingField(x, y);
3389 /* indestructible elements can only explode in center (but not flames) */
3390 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
3391 mode == EX_TYPE_BORDER)) ||
3392 element == EL_FLAMES)
3395 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
3396 behaviour, for example when touching a yamyam that explodes to rocks
3397 with active deadly shield, a rock is created under the player !!! */
3398 /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
3400 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
3401 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
3402 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
3404 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
3407 if (IS_ACTIVE_BOMB(element))
3409 /* re-activate things under the bomb like gate or penguin */
3410 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
3417 /* save walkable background elements while explosion on same tile */
3418 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
3419 (x != ex || y != ey || mode == EX_TYPE_BORDER))
3420 Back[x][y] = element;
3422 /* ignite explodable elements reached by other explosion */
3423 if (element == EL_EXPLOSION)
3424 element = Store2[x][y];
3426 if (AmoebaNr[x][y] &&
3427 (element == EL_AMOEBA_FULL ||
3428 element == EL_BD_AMOEBA ||
3429 element == EL_AMOEBA_GROWING))
3431 AmoebaCnt[AmoebaNr[x][y]]--;
3432 AmoebaCnt2[AmoebaNr[x][y]]--;
3437 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
3440 int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
3442 Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
3444 switch(StorePlayer[ex][ey])
3447 Store[x][y] = EL_PLAYER_IS_EXPLODING_2;
3450 Store[x][y] = EL_PLAYER_IS_EXPLODING_3;
3453 Store[x][y] = EL_PLAYER_IS_EXPLODING_4;
3457 Store[x][y] = EL_PLAYER_IS_EXPLODING_1;
3462 if (PLAYERINFO(ex, ey)->use_murphy)
3463 Store[x][y] = EL_EMPTY;
3466 /* !!! check this case -- currently needed for rnd_rado_negundo_v,
3467 !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
3468 else if (ELEM_IS_PLAYER(center_element))
3469 Store[x][y] = EL_EMPTY;
3470 else if (center_element == EL_YAMYAM)
3471 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
3472 else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
3473 Store[x][y] = element_info[center_element].content.e[xx][yy];
3475 /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
3476 (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
3477 otherwise) -- FIX THIS !!! */
3478 else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
3479 Store[x][y] = element_info[element].content.e[1][1];
3481 else if (!CAN_EXPLODE(element))
3482 Store[x][y] = element_info[element].content.e[1][1];
3485 Store[x][y] = EL_EMPTY;
3487 else if (center_element == EL_MOLE)
3488 Store[x][y] = EL_EMERALD_RED;
3489 else if (center_element == EL_PENGUIN)
3490 Store[x][y] = EL_EMERALD_PURPLE;
3491 else if (center_element == EL_BUG)
3492 Store[x][y] = ((x == ex && y == ey) ? EL_DIAMOND : EL_EMERALD);
3493 else if (center_element == EL_BD_BUTTERFLY)
3494 Store[x][y] = EL_BD_DIAMOND;
3495 else if (center_element == EL_SP_ELECTRON)
3496 Store[x][y] = EL_SP_INFOTRON;
3497 else if (center_element == EL_AMOEBA_TO_DIAMOND)
3498 Store[x][y] = level.amoeba_content;
3499 else if (center_element == EL_YAMYAM)
3500 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
3501 else if (IS_CUSTOM_ELEMENT(center_element) &&
3502 element_info[center_element].content.e[xx][yy] != EL_EMPTY)
3503 Store[x][y] = element_info[center_element].content.e[xx][yy];
3504 else if (element == EL_WALL_EMERALD)
3505 Store[x][y] = EL_EMERALD;
3506 else if (element == EL_WALL_DIAMOND)
3507 Store[x][y] = EL_DIAMOND;
3508 else if (element == EL_WALL_BD_DIAMOND)
3509 Store[x][y] = EL_BD_DIAMOND;
3510 else if (element == EL_WALL_EMERALD_YELLOW)
3511 Store[x][y] = EL_EMERALD_YELLOW;
3512 else if (element == EL_WALL_EMERALD_RED)
3513 Store[x][y] = EL_EMERALD_RED;
3514 else if (element == EL_WALL_EMERALD_PURPLE)
3515 Store[x][y] = EL_EMERALD_PURPLE;
3516 else if (element == EL_WALL_PEARL)
3517 Store[x][y] = EL_PEARL;
3518 else if (element == EL_WALL_CRYSTAL)
3519 Store[x][y] = EL_CRYSTAL;
3520 else if (IS_CUSTOM_ELEMENT(element) && !CAN_EXPLODE(element))
3521 Store[x][y] = element_info[element].content.e[1][1];
3523 Store[x][y] = EL_EMPTY;
3526 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
3527 center_element == EL_AMOEBA_TO_DIAMOND)
3528 Store2[x][y] = element;
3530 Feld[x][y] = EL_EXPLOSION;
3531 GfxElement[x][y] = artwork_element;
3533 ExplodePhase[x][y] = 1;
3534 ExplodeDelay[x][y] = last_phase;
3539 if (center_element == EL_YAMYAM)
3540 game.yamyam_content_nr =
3541 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
3553 GfxFrame[x][y] = 0; /* restart explosion animation */
3555 last_phase = ExplodeDelay[x][y];
3557 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
3561 /* activate this even in non-DEBUG version until cause for crash in
3562 getGraphicAnimationFrame() (see below) is found and eliminated */
3567 if (GfxElement[x][y] == EL_UNDEFINED)
3570 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
3571 printf("Explode(): This should never happen!\n");
3574 GfxElement[x][y] = EL_EMPTY;
3578 border_element = Store2[x][y];
3579 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3580 border_element = StorePlayer[x][y];
3582 if (phase == element_info[border_element].ignition_delay ||
3583 phase == last_phase)
3585 boolean border_explosion = FALSE;
3587 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
3588 !PLAYER_EXPLOSION_PROTECTED(x, y))
3590 KillPlayerUnlessExplosionProtected(x, y);
3591 border_explosion = TRUE;
3593 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
3595 Feld[x][y] = Store2[x][y];
3598 border_explosion = TRUE;
3600 else if (border_element == EL_AMOEBA_TO_DIAMOND)
3602 AmoebeUmwandeln(x, y);
3604 border_explosion = TRUE;
3607 /* if an element just explodes due to another explosion (chain-reaction),
3608 do not immediately end the new explosion when it was the last frame of
3609 the explosion (as it would be done in the following "if"-statement!) */
3610 if (border_explosion && phase == last_phase)
3614 if (phase == last_phase)
3618 element = Feld[x][y] = Store[x][y];
3619 Store[x][y] = Store2[x][y] = 0;
3620 GfxElement[x][y] = EL_UNDEFINED;
3622 /* player can escape from explosions and might therefore be still alive */
3623 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
3624 element <= EL_PLAYER_IS_EXPLODING_4)
3626 int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
3627 int explosion_element = EL_PLAYER_1 + player_nr;
3628 int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
3629 int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
3631 if (level.use_explosion_element[player_nr])
3632 explosion_element = level.explosion_element[player_nr];
3634 Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
3635 element_info[explosion_element].content.e[xx][yy]);
3638 /* restore probably existing indestructible background element */
3639 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
3640 element = Feld[x][y] = Back[x][y];
3643 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
3644 GfxDir[x][y] = MV_NONE;
3645 ChangeDelay[x][y] = 0;
3646 ChangePage[x][y] = -1;
3648 #if USE_NEW_CUSTOM_VALUE
3649 CustomValue[x][y] = 0;
3652 InitField_WithBug2(x, y, FALSE);
3654 DrawLevelField(x, y);
3656 TestIfElementTouchesCustomElement(x, y);
3658 if (GFX_CRUMBLED(element))
3659 DrawLevelFieldCrumbledSandNeighbours(x, y);
3661 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
3662 StorePlayer[x][y] = 0;
3664 if (ELEM_IS_PLAYER(element))
3665 RelocatePlayer(x, y, element);
3667 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3669 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
3670 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
3673 DrawLevelFieldCrumbledSand(x, y);
3675 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
3677 DrawLevelElement(x, y, Back[x][y]);
3678 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
3680 else if (IS_WALKABLE_UNDER(Back[x][y]))
3682 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3683 DrawLevelElementThruMask(x, y, Back[x][y]);
3685 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
3686 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3690 void DynaExplode(int ex, int ey)
3693 int dynabomb_element = Feld[ex][ey];
3694 int dynabomb_size = 1;
3695 boolean dynabomb_xl = FALSE;
3696 struct PlayerInfo *player;
3697 static int xy[4][2] =
3705 if (IS_ACTIVE_BOMB(dynabomb_element))
3707 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
3708 dynabomb_size = player->dynabomb_size;
3709 dynabomb_xl = player->dynabomb_xl;
3710 player->dynabombs_left++;
3713 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
3715 for (i = 0; i < NUM_DIRECTIONS; i++)
3717 for (j = 1; j <= dynabomb_size; j++)
3719 int x = ex + j * xy[i][0];
3720 int y = ey + j * xy[i][1];
3723 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
3726 element = Feld[x][y];
3728 /* do not restart explosions of fields with active bombs */
3729 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
3732 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
3734 if (element != EL_EMPTY && element != EL_EXPLOSION &&
3735 !IS_DIGGABLE(element) && !dynabomb_xl)
3741 void Bang(int x, int y)
3743 int element = MovingOrBlocked2Element(x, y);
3744 int explosion_type = EX_TYPE_NORMAL;
3746 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3748 struct PlayerInfo *player = PLAYERINFO(x, y);
3750 element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
3751 player->element_nr);
3753 if (level.use_explosion_element[player->index_nr])
3755 int explosion_element = level.explosion_element[player->index_nr];
3757 if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
3758 explosion_type = EX_TYPE_CROSS;
3759 else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
3760 explosion_type = EX_TYPE_CENTER;
3768 case EL_BD_BUTTERFLY:
3771 case EL_DARK_YAMYAM:
3775 RaiseScoreElement(element);
3778 case EL_DYNABOMB_PLAYER_1_ACTIVE:
3779 case EL_DYNABOMB_PLAYER_2_ACTIVE:
3780 case EL_DYNABOMB_PLAYER_3_ACTIVE:
3781 case EL_DYNABOMB_PLAYER_4_ACTIVE:
3782 case EL_DYNABOMB_INCREASE_NUMBER:
3783 case EL_DYNABOMB_INCREASE_SIZE:
3784 case EL_DYNABOMB_INCREASE_POWER:
3785 explosion_type = EX_TYPE_DYNA;
3790 case EL_LAMP_ACTIVE:
3791 case EL_AMOEBA_TO_DIAMOND:
3792 if (!IS_PLAYER(x, y)) /* penguin and player may be at same field */
3793 explosion_type = EX_TYPE_CENTER;
3797 if (element_info[element].explosion_type == EXPLODES_CROSS)
3798 explosion_type = EX_TYPE_CROSS;
3799 else if (element_info[element].explosion_type == EXPLODES_1X1)
3800 explosion_type = EX_TYPE_CENTER;
3804 if (explosion_type == EX_TYPE_DYNA)
3807 Explode(x, y, EX_PHASE_START, explosion_type);
3809 CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
3812 void SplashAcid(int x, int y)
3814 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
3815 (!IN_LEV_FIELD(x - 1, y - 2) ||
3816 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
3817 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
3819 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
3820 (!IN_LEV_FIELD(x + 1, y - 2) ||
3821 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
3822 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
3824 PlayLevelSound(x, y, SND_ACID_SPLASHING);
3827 static void InitBeltMovement()
3829 static int belt_base_element[4] =
3831 EL_CONVEYOR_BELT_1_LEFT,
3832 EL_CONVEYOR_BELT_2_LEFT,
3833 EL_CONVEYOR_BELT_3_LEFT,
3834 EL_CONVEYOR_BELT_4_LEFT
3836 static int belt_base_active_element[4] =
3838 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
3839 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
3840 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
3841 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
3846 /* set frame order for belt animation graphic according to belt direction */
3847 for (i = 0; i < NUM_BELTS; i++)
3851 for (j = 0; j < NUM_BELT_PARTS; j++)
3853 int element = belt_base_active_element[belt_nr] + j;
3854 int graphic = el2img(element);
3856 if (game.belt_dir[i] == MV_LEFT)
3857 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
3859 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
3864 SCAN_PLAYFIELD(x, y)
3866 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
3869 int element = Feld[x][y];
3871 for (i = 0; i < NUM_BELTS; i++)
3873 if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
3875 int e_belt_nr = getBeltNrFromBeltElement(element);
3878 if (e_belt_nr == belt_nr)
3880 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
3882 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
3889 static void ToggleBeltSwitch(int x, int y)
3891 static int belt_base_element[4] =
3893 EL_CONVEYOR_BELT_1_LEFT,
3894 EL_CONVEYOR_BELT_2_LEFT,
3895 EL_CONVEYOR_BELT_3_LEFT,
3896 EL_CONVEYOR_BELT_4_LEFT
3898 static int belt_base_active_element[4] =
3900 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
3901 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
3902 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
3903 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
3905 static int belt_base_switch_element[4] =
3907 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
3908 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
3909 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
3910 EL_CONVEYOR_BELT_4_SWITCH_LEFT
3912 static int belt_move_dir[4] =
3920 int element = Feld[x][y];
3921 int belt_nr = getBeltNrFromBeltSwitchElement(element);
3922 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
3923 int belt_dir = belt_move_dir[belt_dir_nr];
3926 if (!IS_BELT_SWITCH(element))
3929 game.belt_dir_nr[belt_nr] = belt_dir_nr;
3930 game.belt_dir[belt_nr] = belt_dir;
3932 if (belt_dir_nr == 3)
3935 /* set frame order for belt animation graphic according to belt direction */
3936 for (i = 0; i < NUM_BELT_PARTS; i++)
3938 int element = belt_base_active_element[belt_nr] + i;
3939 int graphic = el2img(element);
3941 if (belt_dir == MV_LEFT)
3942 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
3944 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
3948 SCAN_PLAYFIELD(xx, yy)
3950 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
3953 int element = Feld[xx][yy];
3955 if (IS_BELT_SWITCH(element))
3957 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
3959 if (e_belt_nr == belt_nr)
3961 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
3962 DrawLevelField(xx, yy);
3965 else if (IS_BELT(element) && belt_dir != MV_NONE)
3967 int e_belt_nr = getBeltNrFromBeltElement(element);
3969 if (e_belt_nr == belt_nr)
3971 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
3973 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
3974 DrawLevelField(xx, yy);
3977 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
3979 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
3981 if (e_belt_nr == belt_nr)
3983 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
3985 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
3986 DrawLevelField(xx, yy);
3992 static void ToggleSwitchgateSwitch(int x, int y)
3996 game.switchgate_pos = !game.switchgate_pos;
3999 SCAN_PLAYFIELD(xx, yy)
4001 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
4004 int element = Feld[xx][yy];
4006 if (element == EL_SWITCHGATE_SWITCH_UP ||
4007 element == EL_SWITCHGATE_SWITCH_DOWN)
4009 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
4010 DrawLevelField(xx, yy);
4012 else if (element == EL_SWITCHGATE_OPEN ||
4013 element == EL_SWITCHGATE_OPENING)
4015 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
4017 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
4019 else if (element == EL_SWITCHGATE_CLOSED ||
4020 element == EL_SWITCHGATE_CLOSING)
4022 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
4024 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
4029 static int getInvisibleActiveFromInvisibleElement(int element)
4031 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
4032 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
4033 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
4037 static int getInvisibleFromInvisibleActiveElement(int element)
4039 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
4040 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
4041 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
4045 static void RedrawAllLightSwitchesAndInvisibleElements()
4050 SCAN_PLAYFIELD(x, y)
4052 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
4055 int element = Feld[x][y];
4057 if (element == EL_LIGHT_SWITCH &&
4058 game.light_time_left > 0)
4060 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
4061 DrawLevelField(x, y);
4063 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
4064 game.light_time_left == 0)
4066 Feld[x][y] = EL_LIGHT_SWITCH;
4067 DrawLevelField(x, y);
4069 else if (element == EL_EMC_DRIPPER &&
4070 game.light_time_left > 0)
4072 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
4073 DrawLevelField(x, y);
4075 else if (element == EL_EMC_DRIPPER_ACTIVE &&
4076 game.light_time_left == 0)
4078 Feld[x][y] = EL_EMC_DRIPPER;
4079 DrawLevelField(x, y);
4081 else if (element == EL_INVISIBLE_STEELWALL ||
4082 element == EL_INVISIBLE_WALL ||
4083 element == EL_INVISIBLE_SAND)
4085 if (game.light_time_left > 0)
4086 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
4088 DrawLevelField(x, y);
4090 /* uncrumble neighbour fields, if needed */
4091 if (element == EL_INVISIBLE_SAND)
4092 DrawLevelFieldCrumbledSandNeighbours(x, y);
4094 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
4095 element == EL_INVISIBLE_WALL_ACTIVE ||
4096 element == EL_INVISIBLE_SAND_ACTIVE)
4098 if (game.light_time_left == 0)
4099 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
4101 DrawLevelField(x, y);
4103 /* re-crumble neighbour fields, if needed */
4104 if (element == EL_INVISIBLE_SAND)
4105 DrawLevelFieldCrumbledSandNeighbours(x, y);
4110 static void RedrawAllInvisibleElementsForLenses()
4115 SCAN_PLAYFIELD(x, y)
4117 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
4120 int element = Feld[x][y];
4122 if (element == EL_EMC_DRIPPER &&
4123 game.lenses_time_left > 0)
4125 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
4126 DrawLevelField(x, y);
4128 else if (element == EL_EMC_DRIPPER_ACTIVE &&
4129 game.lenses_time_left == 0)
4131 Feld[x][y] = EL_EMC_DRIPPER;
4132 DrawLevelField(x, y);
4134 else if (element == EL_INVISIBLE_STEELWALL ||
4135 element == EL_INVISIBLE_WALL ||
4136 element == EL_INVISIBLE_SAND)
4138 if (game.lenses_time_left > 0)
4139 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
4141 DrawLevelField(x, y);
4143 /* uncrumble neighbour fields, if needed */
4144 if (element == EL_INVISIBLE_SAND)
4145 DrawLevelFieldCrumbledSandNeighbours(x, y);
4147 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
4148 element == EL_INVISIBLE_WALL_ACTIVE ||
4149 element == EL_INVISIBLE_SAND_ACTIVE)
4151 if (game.lenses_time_left == 0)
4152 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
4154 DrawLevelField(x, y);
4156 /* re-crumble neighbour fields, if needed */
4157 if (element == EL_INVISIBLE_SAND)
4158 DrawLevelFieldCrumbledSandNeighbours(x, y);
4163 static void RedrawAllInvisibleElementsForMagnifier()
4168 SCAN_PLAYFIELD(x, y)
4170 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
4173 int element = Feld[x][y];
4175 if (element == EL_EMC_FAKE_GRASS &&
4176 game.magnify_time_left > 0)
4178 Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
4179 DrawLevelField(x, y);
4181 else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
4182 game.magnify_time_left == 0)
4184 Feld[x][y] = EL_EMC_FAKE_GRASS;
4185 DrawLevelField(x, y);
4187 else if (IS_GATE_GRAY(element) &&
4188 game.magnify_time_left > 0)
4190 Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
4191 element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
4192 IS_EM_GATE_GRAY(element) ?
4193 element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
4194 IS_EMC_GATE_GRAY(element) ?
4195 element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
4197 DrawLevelField(x, y);
4199 else if (IS_GATE_GRAY_ACTIVE(element) &&
4200 game.magnify_time_left == 0)
4202 Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
4203 element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
4204 IS_EM_GATE_GRAY_ACTIVE(element) ?
4205 element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
4206 IS_EMC_GATE_GRAY_ACTIVE(element) ?
4207 element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
4209 DrawLevelField(x, y);
4214 static void ToggleLightSwitch(int x, int y)
4216 int element = Feld[x][y];
4218 game.light_time_left =
4219 (element == EL_LIGHT_SWITCH ?
4220 level.time_light * FRAMES_PER_SECOND : 0);
4222 RedrawAllLightSwitchesAndInvisibleElements();
4225 static void ActivateTimegateSwitch(int x, int y)
4229 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
4232 SCAN_PLAYFIELD(xx, yy)
4234 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
4237 int element = Feld[xx][yy];
4239 if (element == EL_TIMEGATE_CLOSED ||
4240 element == EL_TIMEGATE_CLOSING)
4242 Feld[xx][yy] = EL_TIMEGATE_OPENING;
4243 PlayLevelSound(xx, yy, SND_TIMEGATE_OPENING);
4247 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
4249 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
4250 DrawLevelField(xx, yy);
4256 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
4259 void Impact(int x, int y)
4261 boolean last_line = (y == lev_fieldy - 1);
4262 boolean object_hit = FALSE;
4263 boolean impact = (last_line || object_hit);
4264 int element = Feld[x][y];
4265 int smashed = EL_STEELWALL;
4267 if (!last_line) /* check if element below was hit */
4269 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
4272 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
4273 MovDir[x][y + 1] != MV_DOWN ||
4274 MovPos[x][y + 1] <= TILEY / 2));
4276 /* do not smash moving elements that left the smashed field in time */
4277 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
4278 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
4281 #if USE_QUICKSAND_IMPACT_BUGFIX
4282 if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
4284 RemoveMovingField(x, y + 1);
4285 Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
4286 Feld[x][y + 2] = EL_ROCK;
4287 DrawLevelField(x, y + 2);
4294 smashed = MovingOrBlocked2Element(x, y + 1);
4296 impact = (last_line || object_hit);
4299 if (!last_line && smashed == EL_ACID) /* element falls into acid */
4301 SplashAcid(x, y + 1);
4305 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
4306 /* only reset graphic animation if graphic really changes after impact */
4308 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
4310 ResetGfxAnimation(x, y);
4311 DrawLevelField(x, y);
4314 if (impact && CAN_EXPLODE_IMPACT(element))
4319 else if (impact && element == EL_PEARL)
4321 ResetGfxAnimation(x, y);
4323 Feld[x][y] = EL_PEARL_BREAKING;
4324 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4327 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
4329 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4334 if (impact && element == EL_AMOEBA_DROP)
4336 if (object_hit && IS_PLAYER(x, y + 1))
4337 KillPlayerUnlessEnemyProtected(x, y + 1);
4338 else if (object_hit && smashed == EL_PENGUIN)
4342 Feld[x][y] = EL_AMOEBA_GROWING;
4343 Store[x][y] = EL_AMOEBA_WET;
4345 ResetRandomAnimationValue(x, y);
4350 if (object_hit) /* check which object was hit */
4352 if (CAN_PASS_MAGIC_WALL(element) &&
4353 (smashed == EL_MAGIC_WALL ||
4354 smashed == EL_BD_MAGIC_WALL))
4357 int activated_magic_wall =
4358 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
4359 EL_BD_MAGIC_WALL_ACTIVE);
4361 /* activate magic wall / mill */
4363 SCAN_PLAYFIELD(xx, yy)
4365 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
4367 if (Feld[xx][yy] == smashed)
4368 Feld[xx][yy] = activated_magic_wall;
4370 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
4371 game.magic_wall_active = TRUE;
4373 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
4374 SND_MAGIC_WALL_ACTIVATING :
4375 SND_BD_MAGIC_WALL_ACTIVATING));
4378 if (IS_PLAYER(x, y + 1))
4380 if (CAN_SMASH_PLAYER(element))
4382 KillPlayerUnlessEnemyProtected(x, y + 1);
4386 else if (smashed == EL_PENGUIN)
4388 if (CAN_SMASH_PLAYER(element))
4394 else if (element == EL_BD_DIAMOND)
4396 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
4402 else if (((element == EL_SP_INFOTRON ||
4403 element == EL_SP_ZONK) &&
4404 (smashed == EL_SP_SNIKSNAK ||
4405 smashed == EL_SP_ELECTRON ||
4406 smashed == EL_SP_DISK_ORANGE)) ||
4407 (element == EL_SP_INFOTRON &&
4408 smashed == EL_SP_DISK_YELLOW))
4413 else if (CAN_SMASH_EVERYTHING(element))
4415 if (IS_CLASSIC_ENEMY(smashed) ||
4416 CAN_EXPLODE_SMASHED(smashed))
4421 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
4423 if (smashed == EL_LAMP ||
4424 smashed == EL_LAMP_ACTIVE)
4429 else if (smashed == EL_NUT)
4431 Feld[x][y + 1] = EL_NUT_BREAKING;
4432 PlayLevelSound(x, y, SND_NUT_BREAKING);
4433 RaiseScoreElement(EL_NUT);
4436 else if (smashed == EL_PEARL)
4438 ResetGfxAnimation(x, y);
4440 Feld[x][y + 1] = EL_PEARL_BREAKING;
4441 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4444 else if (smashed == EL_DIAMOND)
4446 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
4447 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
4450 else if (IS_BELT_SWITCH(smashed))
4452 ToggleBeltSwitch(x, y + 1);
4454 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
4455 smashed == EL_SWITCHGATE_SWITCH_DOWN)
4457 ToggleSwitchgateSwitch(x, y + 1);
4459 else if (smashed == EL_LIGHT_SWITCH ||
4460 smashed == EL_LIGHT_SWITCH_ACTIVE)
4462 ToggleLightSwitch(x, y + 1);
4467 TestIfElementSmashesCustomElement(x, y, MV_DOWN);
4470 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4472 CheckElementChangeBySide(x, y + 1, smashed, element,
4473 CE_SWITCHED, CH_SIDE_TOP);
4474 CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
4480 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4485 /* play sound of magic wall / mill */
4487 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
4488 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
4490 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
4491 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
4492 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
4493 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
4498 /* play sound of object that hits the ground */
4499 if (last_line || object_hit)
4500 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4503 inline static void TurnRoundExt(int x, int y)
4515 { 0, 0 }, { 0, 0 }, { 0, 0 },
4520 int left, right, back;
4524 { MV_DOWN, MV_UP, MV_RIGHT },
4525 { MV_UP, MV_DOWN, MV_LEFT },
4527 { MV_LEFT, MV_RIGHT, MV_DOWN },
4531 { MV_RIGHT, MV_LEFT, MV_UP }
4534 int element = Feld[x][y];
4535 int move_pattern = element_info[element].move_pattern;
4537 int old_move_dir = MovDir[x][y];
4538 int left_dir = turn[old_move_dir].left;
4539 int right_dir = turn[old_move_dir].right;
4540 int back_dir = turn[old_move_dir].back;
4542 int left_dx = move_xy[left_dir].dx, left_dy = move_xy[left_dir].dy;
4543 int right_dx = move_xy[right_dir].dx, right_dy = move_xy[right_dir].dy;
4544 int move_dx = move_xy[old_move_dir].dx, move_dy = move_xy[old_move_dir].dy;
4545 int back_dx = move_xy[back_dir].dx, back_dy = move_xy[back_dir].dy;
4547 int left_x = x + left_dx, left_y = y + left_dy;
4548 int right_x = x + right_dx, right_y = y + right_dy;
4549 int move_x = x + move_dx, move_y = y + move_dy;
4553 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4555 TestIfBadThingTouchesOtherBadThing(x, y);
4557 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
4558 MovDir[x][y] = right_dir;
4559 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4560 MovDir[x][y] = left_dir;
4562 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
4564 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
4567 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
4569 TestIfBadThingTouchesOtherBadThing(x, y);
4571 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
4572 MovDir[x][y] = left_dir;
4573 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4574 MovDir[x][y] = right_dir;
4576 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
4578 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
4581 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4583 TestIfBadThingTouchesOtherBadThing(x, y);
4585 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
4586 MovDir[x][y] = left_dir;
4587 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
4588 MovDir[x][y] = right_dir;
4590 if (MovDir[x][y] != old_move_dir)
4593 else if (element == EL_YAMYAM)
4595 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
4596 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
4598 if (can_turn_left && can_turn_right)
4599 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4600 else if (can_turn_left)
4601 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4602 else if (can_turn_right)
4603 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4605 MovDir[x][y] = back_dir;
4607 MovDelay[x][y] = 16 + 16 * RND(3);
4609 else if (element == EL_DARK_YAMYAM)
4611 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4613 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4616 if (can_turn_left && can_turn_right)
4617 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4618 else if (can_turn_left)
4619 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4620 else if (can_turn_right)
4621 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4623 MovDir[x][y] = back_dir;
4625 MovDelay[x][y] = 16 + 16 * RND(3);
4627 else if (element == EL_PACMAN)
4629 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
4630 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
4632 if (can_turn_left && can_turn_right)
4633 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4634 else if (can_turn_left)
4635 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4636 else if (can_turn_right)
4637 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4639 MovDir[x][y] = back_dir;
4641 MovDelay[x][y] = 6 + RND(40);
4643 else if (element == EL_PIG)
4645 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
4646 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
4647 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
4648 boolean should_turn_left, should_turn_right, should_move_on;
4650 int rnd = RND(rnd_value);
4652 should_turn_left = (can_turn_left &&
4654 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
4655 y + back_dy + left_dy)));
4656 should_turn_right = (can_turn_right &&
4658 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
4659 y + back_dy + right_dy)));
4660 should_move_on = (can_move_on &&
4663 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
4664 y + move_dy + left_dy) ||
4665 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
4666 y + move_dy + right_dy)));
4668 if (should_turn_left || should_turn_right || should_move_on)
4670 if (should_turn_left && should_turn_right && should_move_on)
4671 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
4672 rnd < 2 * rnd_value / 3 ? right_dir :
4674 else if (should_turn_left && should_turn_right)
4675 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4676 else if (should_turn_left && should_move_on)
4677 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
4678 else if (should_turn_right && should_move_on)
4679 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
4680 else if (should_turn_left)
4681 MovDir[x][y] = left_dir;
4682 else if (should_turn_right)
4683 MovDir[x][y] = right_dir;
4684 else if (should_move_on)
4685 MovDir[x][y] = old_move_dir;
4687 else if (can_move_on && rnd > rnd_value / 8)
4688 MovDir[x][y] = old_move_dir;
4689 else if (can_turn_left && can_turn_right)
4690 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4691 else if (can_turn_left && rnd > rnd_value / 8)
4692 MovDir[x][y] = left_dir;
4693 else if (can_turn_right && rnd > rnd_value/8)
4694 MovDir[x][y] = right_dir;
4696 MovDir[x][y] = back_dir;
4698 xx = x + move_xy[MovDir[x][y]].dx;
4699 yy = y + move_xy[MovDir[x][y]].dy;
4701 if (!IN_LEV_FIELD(xx, yy) ||
4702 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
4703 MovDir[x][y] = old_move_dir;
4707 else if (element == EL_DRAGON)
4709 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
4710 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
4711 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
4713 int rnd = RND(rnd_value);
4715 if (can_move_on && rnd > rnd_value / 8)
4716 MovDir[x][y] = old_move_dir;
4717 else if (can_turn_left && can_turn_right)
4718 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4719 else if (can_turn_left && rnd > rnd_value / 8)
4720 MovDir[x][y] = left_dir;
4721 else if (can_turn_right && rnd > rnd_value / 8)
4722 MovDir[x][y] = right_dir;
4724 MovDir[x][y] = back_dir;
4726 xx = x + move_xy[MovDir[x][y]].dx;
4727 yy = y + move_xy[MovDir[x][y]].dy;
4729 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
4730 MovDir[x][y] = old_move_dir;
4734 else if (element == EL_MOLE)
4736 boolean can_move_on =
4737 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
4738 IS_AMOEBOID(Feld[move_x][move_y]) ||
4739 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
4742 boolean can_turn_left =
4743 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
4744 IS_AMOEBOID(Feld[left_x][left_y])));
4746 boolean can_turn_right =
4747 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
4748 IS_AMOEBOID(Feld[right_x][right_y])));
4750 if (can_turn_left && can_turn_right)
4751 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
4752 else if (can_turn_left)
4753 MovDir[x][y] = left_dir;
4755 MovDir[x][y] = right_dir;
4758 if (MovDir[x][y] != old_move_dir)
4761 else if (element == EL_BALLOON)
4763 MovDir[x][y] = game.wind_direction;
4766 else if (element == EL_SPRING)
4768 #if USE_NEW_SPRING_BUMPER
4769 if (MovDir[x][y] & MV_HORIZONTAL)
4771 if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
4772 !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
4774 Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
4775 ResetGfxAnimation(move_x, move_y);
4776 DrawLevelField(move_x, move_y);
4778 MovDir[x][y] = back_dir;
4780 else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
4781 SPRING_CAN_ENTER_FIELD(element, x, y + 1))
4782 MovDir[x][y] = MV_NONE;
4785 if (MovDir[x][y] & MV_HORIZONTAL &&
4786 (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
4787 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
4788 MovDir[x][y] = MV_NONE;
4793 else if (element == EL_ROBOT ||
4794 element == EL_SATELLITE ||
4795 element == EL_PENGUIN ||
4796 element == EL_EMC_ANDROID)
4798 int attr_x = -1, attr_y = -1;
4809 for (i = 0; i < MAX_PLAYERS; i++)
4811 struct PlayerInfo *player = &stored_player[i];
4812 int jx = player->jx, jy = player->jy;
4814 if (!player->active)
4818 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
4826 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
4827 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
4828 game.engine_version < VERSION_IDENT(3,1,0,0)))
4834 if (element == EL_PENGUIN)
4837 static int xy[4][2] =
4845 for (i = 0; i < NUM_DIRECTIONS; i++)
4847 int ex = x + xy[i][0];
4848 int ey = y + xy[i][1];
4850 if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
4859 MovDir[x][y] = MV_NONE;
4861 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
4862 else if (attr_x > x)
4863 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
4865 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
4866 else if (attr_y > y)
4867 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
4869 if (element == EL_ROBOT)
4873 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4874 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
4875 Moving2Blocked(x, y, &newx, &newy);
4877 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
4878 MovDelay[x][y] = 8 + 8 * !RND(3);
4880 MovDelay[x][y] = 16;
4882 else if (element == EL_PENGUIN)
4888 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4890 boolean first_horiz = RND(2);
4891 int new_move_dir = MovDir[x][y];
4894 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4895 Moving2Blocked(x, y, &newx, &newy);
4897 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
4901 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4902 Moving2Blocked(x, y, &newx, &newy);
4904 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
4907 MovDir[x][y] = old_move_dir;
4911 else if (element == EL_SATELLITE)
4917 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4919 boolean first_horiz = RND(2);
4920 int new_move_dir = MovDir[x][y];
4923 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4924 Moving2Blocked(x, y, &newx, &newy);
4926 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
4930 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4931 Moving2Blocked(x, y, &newx, &newy);
4933 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
4936 MovDir[x][y] = old_move_dir;
4940 else if (element == EL_EMC_ANDROID)
4942 static int check_pos[16] =
4944 -1, /* 0 => (invalid) */
4945 7, /* 1 => MV_LEFT */
4946 3, /* 2 => MV_RIGHT */
4947 -1, /* 3 => (invalid) */
4949 0, /* 5 => MV_LEFT | MV_UP */
4950 2, /* 6 => MV_RIGHT | MV_UP */
4951 -1, /* 7 => (invalid) */
4952 5, /* 8 => MV_DOWN */
4953 6, /* 9 => MV_LEFT | MV_DOWN */
4954 4, /* 10 => MV_RIGHT | MV_DOWN */
4955 -1, /* 11 => (invalid) */
4956 -1, /* 12 => (invalid) */
4957 -1, /* 13 => (invalid) */
4958 -1, /* 14 => (invalid) */
4959 -1, /* 15 => (invalid) */
4967 { -1, -1, MV_LEFT | MV_UP },
4969 { +1, -1, MV_RIGHT | MV_UP },
4970 { +1, 0, MV_RIGHT },
4971 { +1, +1, MV_RIGHT | MV_DOWN },
4973 { -1, +1, MV_LEFT | MV_DOWN },
4976 int start_pos, check_order;
4977 boolean can_clone = FALSE;
4980 /* check if there is any free field around current position */
4981 for (i = 0; i < 8; i++)
4983 int newx = x + check_xy[i].dx;
4984 int newy = y + check_xy[i].dy;
4986 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
4994 if (can_clone) /* randomly find an element to clone */
4998 start_pos = check_pos[RND(8)];
4999 check_order = (RND(2) ? -1 : +1);
5001 for (i = 0; i < 8; i++)
5003 int pos_raw = start_pos + i * check_order;
5004 int pos = (pos_raw + 8) % 8;
5005 int newx = x + check_xy[pos].dx;
5006 int newy = y + check_xy[pos].dy;
5008 if (ANDROID_CAN_CLONE_FIELD(newx, newy))
5010 element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
5011 element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
5013 Store[x][y] = Feld[newx][newy];
5022 if (can_clone) /* randomly find a direction to move */
5026 start_pos = check_pos[RND(8)];
5027 check_order = (RND(2) ? -1 : +1);
5029 for (i = 0; i < 8; i++)
5031 int pos_raw = start_pos + i * check_order;
5032 int pos = (pos_raw + 8) % 8;
5033 int newx = x + check_xy[pos].dx;
5034 int newy = y + check_xy[pos].dy;
5035 int new_move_dir = check_xy[pos].dir;
5037 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5039 MovDir[x][y] = new_move_dir;
5040 MovDelay[x][y] = level.android_clone_time * 8 + 1;
5049 if (can_clone) /* cloning and moving successful */
5052 /* cannot clone -- try to move towards player */
5054 start_pos = check_pos[MovDir[x][y] & 0x0f];
5055 check_order = (RND(2) ? -1 : +1);
5057 for (i = 0; i < 3; i++)
5059 /* first check start_pos, then previous/next or (next/previous) pos */
5060 int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
5061 int pos = (pos_raw + 8) % 8;
5062 int newx = x + check_xy[pos].dx;
5063 int newy = y + check_xy[pos].dy;
5064 int new_move_dir = check_xy[pos].dir;
5066 if (IS_PLAYER(newx, newy))
5069 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
5071 MovDir[x][y] = new_move_dir;
5072 MovDelay[x][y] = level.android_move_time * 8 + 1;
5079 else if (move_pattern == MV_TURNING_LEFT ||
5080 move_pattern == MV_TURNING_RIGHT ||
5081 move_pattern == MV_TURNING_LEFT_RIGHT ||
5082 move_pattern == MV_TURNING_RIGHT_LEFT ||
5083 move_pattern == MV_TURNING_RANDOM ||
5084 move_pattern == MV_ALL_DIRECTIONS)
5086 boolean can_turn_left =
5087 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
5088 boolean can_turn_right =
5089 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
5091 if (element_info[element].move_stepsize == 0) /* "not moving" */
5094 if (move_pattern == MV_TURNING_LEFT)
5095 MovDir[x][y] = left_dir;
5096 else if (move_pattern == MV_TURNING_RIGHT)
5097 MovDir[x][y] = right_dir;
5098 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
5099 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
5100 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
5101 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
5102 else if (move_pattern == MV_TURNING_RANDOM)
5103 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
5104 can_turn_right && !can_turn_left ? right_dir :
5105 RND(2) ? left_dir : right_dir);
5106 else if (can_turn_left && can_turn_right)
5107 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5108 else if (can_turn_left)
5109 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5110 else if (can_turn_right)
5111 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5113 MovDir[x][y] = back_dir;
5115 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5117 else if (move_pattern == MV_HORIZONTAL ||
5118 move_pattern == MV_VERTICAL)
5120 if (move_pattern & old_move_dir)
5121 MovDir[x][y] = back_dir;
5122 else if (move_pattern == MV_HORIZONTAL)
5123 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
5124 else if (move_pattern == MV_VERTICAL)
5125 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
5127 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5129 else if (move_pattern & MV_ANY_DIRECTION)
5131 MovDir[x][y] = move_pattern;
5132 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5134 else if (move_pattern & MV_WIND_DIRECTION)
5136 MovDir[x][y] = game.wind_direction;
5137 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5139 else if (move_pattern == MV_ALONG_LEFT_SIDE)
5141 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
5142 MovDir[x][y] = left_dir;
5143 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5144 MovDir[x][y] = right_dir;
5146 if (MovDir[x][y] != old_move_dir)
5147 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5149 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
5151 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
5152 MovDir[x][y] = right_dir;
5153 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5154 MovDir[x][y] = left_dir;
5156 if (MovDir[x][y] != old_move_dir)
5157 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5159 else if (move_pattern == MV_TOWARDS_PLAYER ||
5160 move_pattern == MV_AWAY_FROM_PLAYER)
5162 int attr_x = -1, attr_y = -1;
5164 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
5175 for (i = 0; i < MAX_PLAYERS; i++)
5177 struct PlayerInfo *player = &stored_player[i];
5178 int jx = player->jx, jy = player->jy;
5180 if (!player->active)
5184 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
5192 MovDir[x][y] = MV_NONE;
5194 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
5195 else if (attr_x > x)
5196 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
5198 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
5199 else if (attr_y > y)
5200 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
5202 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5204 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5206 boolean first_horiz = RND(2);
5207 int new_move_dir = MovDir[x][y];
5209 if (element_info[element].move_stepsize == 0) /* "not moving" */
5211 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
5212 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5218 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5219 Moving2Blocked(x, y, &newx, &newy);
5221 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5225 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5226 Moving2Blocked(x, y, &newx, &newy);
5228 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5231 MovDir[x][y] = old_move_dir;
5234 else if (move_pattern == MV_WHEN_PUSHED ||
5235 move_pattern == MV_WHEN_DROPPED)
5237 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5238 MovDir[x][y] = MV_NONE;
5242 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
5244 static int test_xy[7][2] =
5254 static int test_dir[7] =
5264 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
5265 int move_preference = -1000000; /* start with very low preference */
5266 int new_move_dir = MV_NONE;
5267 int start_test = RND(4);
5270 for (i = 0; i < NUM_DIRECTIONS; i++)
5272 int move_dir = test_dir[start_test + i];
5273 int move_dir_preference;
5275 xx = x + test_xy[start_test + i][0];
5276 yy = y + test_xy[start_test + i][1];
5278 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
5279 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
5281 new_move_dir = move_dir;
5286 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
5289 move_dir_preference = -1 * RunnerVisit[xx][yy];
5290 if (hunter_mode && PlayerVisit[xx][yy] > 0)
5291 move_dir_preference = PlayerVisit[xx][yy];
5293 if (move_dir_preference > move_preference)
5295 /* prefer field that has not been visited for the longest time */
5296 move_preference = move_dir_preference;
5297 new_move_dir = move_dir;
5299 else if (move_dir_preference == move_preference &&
5300 move_dir == old_move_dir)
5302 /* prefer last direction when all directions are preferred equally */
5303 move_preference = move_dir_preference;
5304 new_move_dir = move_dir;
5308 MovDir[x][y] = new_move_dir;
5309 if (old_move_dir != new_move_dir)
5310 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5314 static void TurnRound(int x, int y)
5316 int direction = MovDir[x][y];
5318 int element, graphic;
5323 GfxDir[x][y] = MovDir[x][y];
5325 if (direction != MovDir[x][y])
5329 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
5332 element = Feld[x][y];
5333 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5335 if (graphic_info[graphic].anim_global_sync)
5336 GfxFrame[x][y] = FrameCounter;
5337 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5338 GfxFrame[x][y] = CustomValue[x][y];
5339 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5340 GfxFrame[x][y] = element_info[element].collect_score;
5344 static boolean JustBeingPushed(int x, int y)
5348 for (i = 0; i < MAX_PLAYERS; i++)
5350 struct PlayerInfo *player = &stored_player[i];
5352 if (player->active && player->is_pushing && player->MovPos)
5354 int next_jx = player->jx + (player->jx - player->last_jx);
5355 int next_jy = player->jy + (player->jy - player->last_jy);
5357 if (x == next_jx && y == next_jy)
5365 void StartMoving(int x, int y)
5367 boolean started_moving = FALSE; /* some elements can fall _and_ move */
5368 int element = Feld[x][y];
5373 if (MovDelay[x][y] == 0)
5374 GfxAction[x][y] = ACTION_DEFAULT;
5376 if (CAN_FALL(element) && y < lev_fieldy - 1)
5378 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
5379 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
5380 if (JustBeingPushed(x, y))
5383 if (element == EL_QUICKSAND_FULL)
5385 if (IS_FREE(x, y + 1))
5387 InitMovingField(x, y, MV_DOWN);
5388 started_moving = TRUE;
5390 Feld[x][y] = EL_QUICKSAND_EMPTYING;
5391 Store[x][y] = EL_ROCK;
5393 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
5395 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5397 if (!MovDelay[x][y])
5398 MovDelay[x][y] = TILEY + 1;
5407 Feld[x][y] = EL_QUICKSAND_EMPTY;
5408 Feld[x][y + 1] = EL_QUICKSAND_FULL;
5409 Store[x][y + 1] = Store[x][y];
5412 PlayLevelSoundAction(x, y, ACTION_FILLING);
5415 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
5416 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5418 InitMovingField(x, y, MV_DOWN);
5419 started_moving = TRUE;
5421 Feld[x][y] = EL_QUICKSAND_FILLING;
5422 Store[x][y] = element;
5424 PlayLevelSoundAction(x, y, ACTION_FILLING);
5426 else if (element == EL_MAGIC_WALL_FULL)
5428 if (IS_FREE(x, y + 1))
5430 InitMovingField(x, y, MV_DOWN);
5431 started_moving = TRUE;
5433 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
5434 Store[x][y] = EL_CHANGED(Store[x][y]);
5436 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
5438 if (!MovDelay[x][y])
5439 MovDelay[x][y] = TILEY/4 + 1;
5448 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
5449 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
5450 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
5454 else if (element == EL_BD_MAGIC_WALL_FULL)
5456 if (IS_FREE(x, y + 1))
5458 InitMovingField(x, y, MV_DOWN);
5459 started_moving = TRUE;
5461 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
5462 Store[x][y] = EL_CHANGED2(Store[x][y]);
5464 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
5466 if (!MovDelay[x][y])
5467 MovDelay[x][y] = TILEY/4 + 1;
5476 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
5477 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
5478 Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
5482 else if (CAN_PASS_MAGIC_WALL(element) &&
5483 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
5484 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
5486 InitMovingField(x, y, MV_DOWN);
5487 started_moving = TRUE;
5490 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
5491 EL_BD_MAGIC_WALL_FILLING);
5492 Store[x][y] = element;
5494 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
5496 SplashAcid(x, y + 1);
5498 InitMovingField(x, y, MV_DOWN);
5499 started_moving = TRUE;
5501 Store[x][y] = EL_ACID;
5503 else if ((game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5504 CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
5506 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
5507 CAN_FALL(element) && WasJustFalling[x][y] &&
5508 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
5510 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
5511 CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
5512 (Feld[x][y + 1] == EL_BLOCKED)))
5514 /* this is needed for a special case not covered by calling "Impact()"
5515 from "ContinueMoving()": if an element moves to a tile directly below
5516 another element which was just falling on that tile (which was empty
5517 in the previous frame), the falling element above would just stop
5518 instead of smashing the element below (in previous version, the above
5519 element was just checked for "moving" instead of "falling", resulting
5520 in incorrect smashes caused by horizontal movement of the above
5521 element; also, the case of the player being the element to smash was
5522 simply not covered here... :-/ ) */
5524 CheckCollision[x][y] = 0;
5528 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
5530 if (MovDir[x][y] == MV_NONE)
5532 InitMovingField(x, y, MV_DOWN);
5533 started_moving = TRUE;
5536 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
5538 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
5539 MovDir[x][y] = MV_DOWN;
5541 InitMovingField(x, y, MV_DOWN);
5542 started_moving = TRUE;
5544 else if (element == EL_AMOEBA_DROP)
5546 Feld[x][y] = EL_AMOEBA_GROWING;
5547 Store[x][y] = EL_AMOEBA_WET;
5549 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
5550 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
5551 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
5552 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
5554 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
5555 (IS_FREE(x - 1, y + 1) ||
5556 Feld[x - 1][y + 1] == EL_ACID));
5557 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
5558 (IS_FREE(x + 1, y + 1) ||
5559 Feld[x + 1][y + 1] == EL_ACID));
5560 boolean can_fall_any = (can_fall_left || can_fall_right);
5561 boolean can_fall_both = (can_fall_left && can_fall_right);
5562 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
5564 #if USE_NEW_ALL_SLIPPERY
5565 if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
5567 if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
5568 can_fall_right = FALSE;
5569 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
5570 can_fall_left = FALSE;
5571 else if (slippery_type == SLIPPERY_ONLY_LEFT)
5572 can_fall_right = FALSE;
5573 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
5574 can_fall_left = FALSE;
5576 can_fall_any = (can_fall_left || can_fall_right);
5577 can_fall_both = FALSE;
5580 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
5582 if (slippery_type == SLIPPERY_ONLY_LEFT)
5583 can_fall_right = FALSE;
5584 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
5585 can_fall_left = FALSE;
5586 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
5587 can_fall_right = FALSE;
5588 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
5589 can_fall_left = FALSE;
5591 can_fall_any = (can_fall_left || can_fall_right);
5592 can_fall_both = (can_fall_left && can_fall_right);
5596 #if USE_NEW_ALL_SLIPPERY
5598 #if USE_NEW_SP_SLIPPERY
5599 /* !!! better use the same properties as for custom elements here !!! */
5600 else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
5601 can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
5603 can_fall_right = FALSE; /* slip down on left side */
5604 can_fall_both = FALSE;
5609 #if USE_NEW_ALL_SLIPPERY
5612 if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
5613 can_fall_right = FALSE; /* slip down on left side */
5615 can_fall_left = !(can_fall_right = RND(2));
5617 can_fall_both = FALSE;
5622 if (game.emulation == EMU_BOULDERDASH ||
5623 element == EL_BD_ROCK || element == EL_BD_DIAMOND)
5624 can_fall_right = FALSE; /* slip down on left side */
5626 can_fall_left = !(can_fall_right = RND(2));
5628 can_fall_both = FALSE;
5634 /* if not determined otherwise, prefer left side for slipping down */
5635 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
5636 started_moving = TRUE;
5640 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
5642 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
5645 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
5646 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
5647 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
5648 int belt_dir = game.belt_dir[belt_nr];
5650 if ((belt_dir == MV_LEFT && left_is_free) ||
5651 (belt_dir == MV_RIGHT && right_is_free))
5653 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
5655 InitMovingField(x, y, belt_dir);
5656 started_moving = TRUE;
5658 Pushed[x][y] = TRUE;
5659 Pushed[nextx][y] = TRUE;
5661 GfxAction[x][y] = ACTION_DEFAULT;
5665 MovDir[x][y] = 0; /* if element was moving, stop it */
5670 /* not "else if" because of elements that can fall and move (EL_SPRING) */
5672 if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
5674 if (CAN_MOVE(element) && !started_moving)
5677 int move_pattern = element_info[element].move_pattern;
5682 if (MovDir[x][y] == MV_NONE)
5684 printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
5685 x, y, element, element_info[element].token_name);
5686 printf("StartMoving(): This should never happen!\n");
5691 Moving2Blocked(x, y, &newx, &newy);
5693 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
5696 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5697 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5699 WasJustMoving[x][y] = 0;
5700 CheckCollision[x][y] = 0;
5702 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
5704 if (Feld[x][y] != element) /* element has changed */
5708 if (!MovDelay[x][y]) /* start new movement phase */
5710 /* all objects that can change their move direction after each step
5711 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
5713 if (element != EL_YAMYAM &&
5714 element != EL_DARK_YAMYAM &&
5715 element != EL_PACMAN &&
5716 !(move_pattern & MV_ANY_DIRECTION) &&
5717 move_pattern != MV_TURNING_LEFT &&
5718 move_pattern != MV_TURNING_RIGHT &&
5719 move_pattern != MV_TURNING_LEFT_RIGHT &&
5720 move_pattern != MV_TURNING_RIGHT_LEFT &&
5721 move_pattern != MV_TURNING_RANDOM)
5725 if (MovDelay[x][y] && (element == EL_BUG ||
5726 element == EL_SPACESHIP ||
5727 element == EL_SP_SNIKSNAK ||
5728 element == EL_SP_ELECTRON ||
5729 element == EL_MOLE))
5730 DrawLevelField(x, y);
5734 if (MovDelay[x][y]) /* wait some time before next movement */
5738 if (element == EL_ROBOT ||
5739 element == EL_YAMYAM ||
5740 element == EL_DARK_YAMYAM)
5742 DrawLevelElementAnimationIfNeeded(x, y, element);
5743 PlayLevelSoundAction(x, y, ACTION_WAITING);
5745 else if (element == EL_SP_ELECTRON)
5746 DrawLevelElementAnimationIfNeeded(x, y, element);
5747 else if (element == EL_DRAGON)
5750 int dir = MovDir[x][y];
5751 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
5752 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
5753 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
5754 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
5755 dir == MV_UP ? IMG_FLAMES_1_UP :
5756 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
5757 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5759 GfxAction[x][y] = ACTION_ATTACKING;
5761 if (IS_PLAYER(x, y))
5762 DrawPlayerField(x, y);
5764 DrawLevelField(x, y);
5766 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
5768 for (i = 1; i <= 3; i++)
5770 int xx = x + i * dx;
5771 int yy = y + i * dy;
5772 int sx = SCREENX(xx);
5773 int sy = SCREENY(yy);
5774 int flame_graphic = graphic + (i - 1);
5776 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
5781 int flamed = MovingOrBlocked2Element(xx, yy);
5785 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
5787 else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
5788 RemoveMovingField(xx, yy);
5790 RemoveField(xx, yy);
5792 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
5795 RemoveMovingField(xx, yy);
5798 ChangeDelay[xx][yy] = 0;
5800 Feld[xx][yy] = EL_FLAMES;
5802 if (IN_SCR_FIELD(sx, sy))
5804 DrawLevelFieldCrumbledSand(xx, yy);
5805 DrawGraphic(sx, sy, flame_graphic, frame);
5810 if (Feld[xx][yy] == EL_FLAMES)
5811 Feld[xx][yy] = EL_EMPTY;
5812 DrawLevelField(xx, yy);
5817 if (MovDelay[x][y]) /* element still has to wait some time */
5819 PlayLevelSoundAction(x, y, ACTION_WAITING);
5825 /* now make next step */
5827 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
5829 if (DONT_COLLIDE_WITH(element) &&
5830 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
5831 !PLAYER_ENEMY_PROTECTED(newx, newy))
5833 TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
5838 else if (CAN_MOVE_INTO_ACID(element) &&
5839 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
5840 !IS_MV_DIAGONAL(MovDir[x][y]) &&
5841 (MovDir[x][y] == MV_DOWN ||
5842 game.engine_version >= VERSION_IDENT(3,1,0,0)))
5844 SplashAcid(newx, newy);
5845 Store[x][y] = EL_ACID;
5847 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
5849 if (Feld[newx][newy] == EL_EXIT_OPEN)
5852 DrawLevelField(x, y);
5854 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
5855 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
5856 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
5858 local_player->friends_still_needed--;
5859 if (!local_player->friends_still_needed &&
5860 !local_player->GameOver && AllPlayersGone)
5861 local_player->LevelSolved = local_player->GameOver = TRUE;
5865 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
5867 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
5868 DrawLevelField(newx, newy);
5870 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
5872 else if (!IS_FREE(newx, newy))
5874 GfxAction[x][y] = ACTION_WAITING;
5876 if (IS_PLAYER(x, y))
5877 DrawPlayerField(x, y);
5879 DrawLevelField(x, y);
5884 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
5886 if (IS_FOOD_PIG(Feld[newx][newy]))
5888 if (IS_MOVING(newx, newy))
5889 RemoveMovingField(newx, newy);
5892 Feld[newx][newy] = EL_EMPTY;
5893 DrawLevelField(newx, newy);
5896 PlayLevelSound(x, y, SND_PIG_DIGGING);
5898 else if (!IS_FREE(newx, newy))
5900 if (IS_PLAYER(x, y))
5901 DrawPlayerField(x, y);
5903 DrawLevelField(x, y);
5908 else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
5910 if (Store[x][y] != EL_EMPTY)
5912 boolean can_clone = FALSE;
5915 /* check if element to clone is still there */
5916 for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
5918 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
5926 /* cannot clone or target field not free anymore -- do not clone */
5927 if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
5928 Store[x][y] = EL_EMPTY;
5931 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
5933 if (IS_MV_DIAGONAL(MovDir[x][y]))
5935 int diagonal_move_dir = MovDir[x][y];
5936 int stored = Store[x][y];
5937 int change_delay = 8;
5940 /* android is moving diagonally */
5942 CreateField(x, y, EL_DIAGONAL_SHRINKING);
5944 Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
5945 GfxElement[x][y] = EL_EMC_ANDROID;
5946 GfxAction[x][y] = ACTION_SHRINKING;
5947 GfxDir[x][y] = diagonal_move_dir;
5948 ChangeDelay[x][y] = change_delay;
5950 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
5953 DrawLevelGraphicAnimation(x, y, graphic);
5954 PlayLevelSoundAction(x, y, ACTION_SHRINKING);
5956 if (Feld[newx][newy] == EL_ACID)
5958 SplashAcid(newx, newy);
5963 CreateField(newx, newy, EL_DIAGONAL_GROWING);
5965 Store[newx][newy] = EL_EMC_ANDROID;
5966 GfxElement[newx][newy] = EL_EMC_ANDROID;
5967 GfxAction[newx][newy] = ACTION_GROWING;
5968 GfxDir[newx][newy] = diagonal_move_dir;
5969 ChangeDelay[newx][newy] = change_delay;
5971 graphic = el_act_dir2img(GfxElement[newx][newy],
5972 GfxAction[newx][newy], GfxDir[newx][newy]);
5974 DrawLevelGraphicAnimation(newx, newy, graphic);
5975 PlayLevelSoundAction(newx, newy, ACTION_GROWING);
5981 Feld[newx][newy] = EL_EMPTY;
5982 DrawLevelField(newx, newy);
5984 PlayLevelSoundAction(x, y, ACTION_DIGGING);
5987 else if (!IS_FREE(newx, newy))
5990 if (IS_PLAYER(x, y))
5991 DrawPlayerField(x, y);
5993 DrawLevelField(x, y);
5999 else if (IS_CUSTOM_ELEMENT(element) &&
6000 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6002 int new_element = Feld[newx][newy];
6004 if (!IS_FREE(newx, newy))
6006 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
6007 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
6010 /* no element can dig solid indestructible elements */
6011 if (IS_INDESTRUCTIBLE(new_element) &&
6012 !IS_DIGGABLE(new_element) &&
6013 !IS_COLLECTIBLE(new_element))
6016 if (AmoebaNr[newx][newy] &&
6017 (new_element == EL_AMOEBA_FULL ||
6018 new_element == EL_BD_AMOEBA ||
6019 new_element == EL_AMOEBA_GROWING))
6021 AmoebaCnt[AmoebaNr[newx][newy]]--;
6022 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6025 if (IS_MOVING(newx, newy))
6026 RemoveMovingField(newx, newy);
6029 RemoveField(newx, newy);
6030 DrawLevelField(newx, newy);
6033 /* if digged element was about to explode, prevent the explosion */
6034 ExplodeField[newx][newy] = EX_TYPE_NONE;
6036 PlayLevelSoundAction(x, y, action);
6039 Store[newx][newy] = EL_EMPTY;
6041 /* this makes it possible to leave the removed element again */
6042 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
6043 Store[newx][newy] = new_element;
6045 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
6047 int move_leave_element = element_info[element].move_leave_element;
6049 /* this makes it possible to leave the removed element again */
6050 Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
6051 new_element : move_leave_element);
6055 if (move_pattern & MV_MAZE_RUNNER_STYLE)
6057 RunnerVisit[x][y] = FrameCounter;
6058 PlayerVisit[x][y] /= 8; /* expire player visit path */
6061 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
6063 if (!IS_FREE(newx, newy))
6065 if (IS_PLAYER(x, y))
6066 DrawPlayerField(x, y);
6068 DrawLevelField(x, y);
6074 boolean wanna_flame = !RND(10);
6075 int dx = newx - x, dy = newy - y;
6076 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
6077 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
6078 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
6079 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
6080 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
6081 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
6084 IS_CLASSIC_ENEMY(element1) ||
6085 IS_CLASSIC_ENEMY(element2)) &&
6086 element1 != EL_DRAGON && element2 != EL_DRAGON &&
6087 element1 != EL_FLAMES && element2 != EL_FLAMES)
6089 ResetGfxAnimation(x, y);
6090 GfxAction[x][y] = ACTION_ATTACKING;
6092 if (IS_PLAYER(x, y))
6093 DrawPlayerField(x, y);
6095 DrawLevelField(x, y);
6097 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
6099 MovDelay[x][y] = 50;
6103 RemoveField(newx, newy);
6105 Feld[newx][newy] = EL_FLAMES;
6106 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
6109 RemoveField(newx1, newy1);
6111 Feld[newx1][newy1] = EL_FLAMES;
6113 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
6116 RemoveField(newx2, newy2);
6118 Feld[newx2][newy2] = EL_FLAMES;
6125 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6126 Feld[newx][newy] == EL_DIAMOND)
6128 if (IS_MOVING(newx, newy))
6129 RemoveMovingField(newx, newy);
6132 Feld[newx][newy] = EL_EMPTY;
6133 DrawLevelField(newx, newy);
6136 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
6138 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6139 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
6141 if (AmoebaNr[newx][newy])
6143 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6144 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
6145 Feld[newx][newy] == EL_BD_AMOEBA)
6146 AmoebaCnt[AmoebaNr[newx][newy]]--;
6151 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
6153 RemoveMovingField(newx, newy);
6156 if (IS_MOVING(newx, newy))
6158 RemoveMovingField(newx, newy);
6163 Feld[newx][newy] = EL_EMPTY;
6164 DrawLevelField(newx, newy);
6167 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
6169 else if ((element == EL_PACMAN || element == EL_MOLE)
6170 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
6172 if (AmoebaNr[newx][newy])
6174 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6175 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
6176 Feld[newx][newy] == EL_BD_AMOEBA)
6177 AmoebaCnt[AmoebaNr[newx][newy]]--;
6180 if (element == EL_MOLE)
6182 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
6183 PlayLevelSound(x, y, SND_MOLE_DIGGING);
6185 ResetGfxAnimation(x, y);
6186 GfxAction[x][y] = ACTION_DIGGING;
6187 DrawLevelField(x, y);
6189 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
6191 return; /* wait for shrinking amoeba */
6193 else /* element == EL_PACMAN */
6195 Feld[newx][newy] = EL_EMPTY;
6196 DrawLevelField(newx, newy);
6197 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
6200 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
6201 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
6202 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
6204 /* wait for shrinking amoeba to completely disappear */
6207 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
6209 /* object was running against a wall */
6214 /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
6215 if (move_pattern & MV_ANY_DIRECTION &&
6216 move_pattern == MovDir[x][y])
6218 int blocking_element =
6219 (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
6221 CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
6224 element = Feld[x][y]; /* element might have changed */
6228 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
6229 DrawLevelElementAnimation(x, y, element);
6231 if (DONT_TOUCH(element))
6232 TestIfBadThingTouchesPlayer(x, y);
6237 InitMovingField(x, y, MovDir[x][y]);
6239 PlayLevelSoundAction(x, y, ACTION_MOVING);
6243 ContinueMoving(x, y);
6246 void ContinueMoving(int x, int y)
6248 int element = Feld[x][y];
6249 struct ElementInfo *ei = &element_info[element];
6250 int direction = MovDir[x][y];
6251 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
6252 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
6253 int newx = x + dx, newy = y + dy;
6254 int stored = Store[x][y];
6255 int stored_new = Store[newx][newy];
6256 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
6257 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
6258 boolean last_line = (newy == lev_fieldy - 1);
6260 MovPos[x][y] += getElementMoveStepsize(x, y);
6262 if (pushed_by_player) /* special case: moving object pushed by player */
6263 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
6265 if (ABS(MovPos[x][y]) < TILEX)
6267 DrawLevelField(x, y);
6269 return; /* element is still moving */
6272 /* element reached destination field */
6274 Feld[x][y] = EL_EMPTY;
6275 Feld[newx][newy] = element;
6276 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
6278 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
6280 element = Feld[newx][newy] = EL_ACID;
6282 else if (element == EL_MOLE)
6284 Feld[x][y] = EL_SAND;
6286 DrawLevelFieldCrumbledSandNeighbours(x, y);
6288 else if (element == EL_QUICKSAND_FILLING)
6290 element = Feld[newx][newy] = get_next_element(element);
6291 Store[newx][newy] = Store[x][y];
6293 else if (element == EL_QUICKSAND_EMPTYING)
6295 Feld[x][y] = get_next_element(element);
6296 element = Feld[newx][newy] = Store[x][y];
6298 else if (element == EL_MAGIC_WALL_FILLING)
6300 element = Feld[newx][newy] = get_next_element(element);
6301 if (!game.magic_wall_active)
6302 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
6303 Store[newx][newy] = Store[x][y];
6305 else if (element == EL_MAGIC_WALL_EMPTYING)
6307 Feld[x][y] = get_next_element(element);
6308 if (!game.magic_wall_active)
6309 Feld[x][y] = EL_MAGIC_WALL_DEAD;
6310 element = Feld[newx][newy] = Store[x][y];
6312 #if USE_NEW_CUSTOM_VALUE
6313 InitField(newx, newy, FALSE);
6316 else if (element == EL_BD_MAGIC_WALL_FILLING)
6318 element = Feld[newx][newy] = get_next_element(element);
6319 if (!game.magic_wall_active)
6320 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
6321 Store[newx][newy] = Store[x][y];
6323 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
6325 Feld[x][y] = get_next_element(element);
6326 if (!game.magic_wall_active)
6327 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
6328 element = Feld[newx][newy] = Store[x][y];
6330 #if USE_NEW_CUSTOM_VALUE
6331 InitField(newx, newy, FALSE);
6334 else if (element == EL_AMOEBA_DROPPING)
6336 Feld[x][y] = get_next_element(element);
6337 element = Feld[newx][newy] = Store[x][y];
6339 else if (element == EL_SOKOBAN_OBJECT)
6342 Feld[x][y] = Back[x][y];
6344 if (Back[newx][newy])
6345 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
6347 Back[x][y] = Back[newx][newy] = 0;
6350 Store[x][y] = EL_EMPTY;
6355 MovDelay[newx][newy] = 0;
6358 if (CAN_CHANGE_OR_HAS_ACTION(element))
6360 if (CAN_CHANGE(element))
6363 /* copy element change control values to new field */
6364 ChangeDelay[newx][newy] = ChangeDelay[x][y];
6365 ChangePage[newx][newy] = ChangePage[x][y];
6366 ChangeCount[newx][newy] = ChangeCount[x][y];
6367 ChangeEvent[newx][newy] = ChangeEvent[x][y];
6370 #if USE_NEW_CUSTOM_VALUE
6371 CustomValue[newx][newy] = CustomValue[x][y];
6377 #if USE_NEW_CUSTOM_VALUE
6378 CustomValue[newx][newy] = CustomValue[x][y];
6382 ChangeDelay[x][y] = 0;
6383 ChangePage[x][y] = -1;
6384 ChangeCount[x][y] = 0;
6385 ChangeEvent[x][y] = -1;
6387 #if USE_NEW_CUSTOM_VALUE
6388 CustomValue[x][y] = 0;
6391 /* copy animation control values to new field */
6392 GfxFrame[newx][newy] = GfxFrame[x][y];
6393 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
6394 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
6395 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
6397 Pushed[x][y] = Pushed[newx][newy] = FALSE;
6399 /* some elements can leave other elements behind after moving */
6401 if (ei->move_leave_element != EL_EMPTY &&
6402 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
6403 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
6405 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
6406 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
6407 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
6410 int move_leave_element = ei->move_leave_element;
6414 /* this makes it possible to leave the removed element again */
6415 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
6416 move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
6418 /* this makes it possible to leave the removed element again */
6419 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
6420 move_leave_element = stored;
6423 /* this makes it possible to leave the removed element again */
6424 if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
6425 ei->move_leave_element == EL_TRIGGER_ELEMENT)
6426 move_leave_element = stored;
6429 Feld[x][y] = move_leave_element;
6431 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
6432 MovDir[x][y] = direction;
6434 InitField(x, y, FALSE);
6436 if (GFX_CRUMBLED(Feld[x][y]))
6437 DrawLevelFieldCrumbledSandNeighbours(x, y);
6439 if (ELEM_IS_PLAYER(move_leave_element))
6440 RelocatePlayer(x, y, move_leave_element);
6443 /* do this after checking for left-behind element */
6444 ResetGfxAnimation(x, y); /* reset animation values for old field */
6446 if (!CAN_MOVE(element) ||
6447 (CAN_FALL(element) && direction == MV_DOWN &&
6448 (element == EL_SPRING ||
6449 element_info[element].move_pattern == MV_WHEN_PUSHED ||
6450 element_info[element].move_pattern == MV_WHEN_DROPPED)))
6451 GfxDir[x][y] = MovDir[newx][newy] = 0;
6453 DrawLevelField(x, y);
6454 DrawLevelField(newx, newy);
6456 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
6458 /* prevent pushed element from moving on in pushed direction */
6459 if (pushed_by_player && CAN_MOVE(element) &&
6460 element_info[element].move_pattern & MV_ANY_DIRECTION &&
6461 !(element_info[element].move_pattern & direction))
6462 TurnRound(newx, newy);
6464 /* prevent elements on conveyor belt from moving on in last direction */
6465 if (pushed_by_conveyor && CAN_FALL(element) &&
6466 direction & MV_HORIZONTAL)
6467 MovDir[newx][newy] = 0;
6469 if (!pushed_by_player)
6471 int nextx = newx + dx, nexty = newy + dy;
6472 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
6474 WasJustMoving[newx][newy] = 3;
6476 if (CAN_FALL(element) && direction == MV_DOWN)
6477 WasJustFalling[newx][newy] = 3;
6479 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
6480 CheckCollision[newx][newy] = 2;
6483 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
6485 TestIfBadThingTouchesPlayer(newx, newy);
6486 TestIfBadThingTouchesFriend(newx, newy);
6488 if (!IS_CUSTOM_ELEMENT(element))
6489 TestIfBadThingTouchesOtherBadThing(newx, newy);
6491 else if (element == EL_PENGUIN)
6492 TestIfFriendTouchesBadThing(newx, newy);
6494 /* give the player one last chance (one more frame) to move away */
6495 if (CAN_FALL(element) && direction == MV_DOWN &&
6496 (last_line || (!IS_FREE(x, newy + 1) &&
6497 (!IS_PLAYER(x, newy + 1) ||
6498 game.engine_version < VERSION_IDENT(3,1,1,0)))))
6501 if (pushed_by_player && !game.use_change_when_pushing_bug)
6503 int push_side = MV_DIR_OPPOSITE(direction);
6504 struct PlayerInfo *player = PLAYERINFO(x, y);
6506 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
6507 player->index_bit, push_side);
6508 CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
6509 player->index_bit, push_side);
6512 if (element == EL_EMC_ANDROID && pushed_by_player) /* make another move */
6513 MovDelay[newx][newy] = 1;
6515 CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
6517 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
6520 if (ChangePage[newx][newy] != -1) /* delayed change */
6522 int page = ChangePage[newx][newy];
6523 struct ElementChangeInfo *change = &ei->change_page[page];
6525 ChangePage[newx][newy] = -1;
6527 if (change->can_change)
6529 if (ChangeElement(newx, newy, element, page))
6531 if (change->post_change_function)
6532 change->post_change_function(newx, newy);
6536 if (change->has_action)
6537 ExecuteCustomElementAction(newx, newy, element, page);
6541 TestIfElementHitsCustomElement(newx, newy, direction);
6542 TestIfPlayerTouchesCustomElement(newx, newy);
6543 TestIfElementTouchesCustomElement(newx, newy);
6546 if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
6547 IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
6548 CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
6549 MV_DIR_OPPOSITE(direction));
6553 int AmoebeNachbarNr(int ax, int ay)
6556 int element = Feld[ax][ay];
6558 static int xy[4][2] =
6566 for (i = 0; i < NUM_DIRECTIONS; i++)
6568 int x = ax + xy[i][0];
6569 int y = ay + xy[i][1];
6571 if (!IN_LEV_FIELD(x, y))
6574 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
6575 group_nr = AmoebaNr[x][y];
6581 void AmoebenVereinigen(int ax, int ay)
6583 int i, x, y, xx, yy;
6584 int new_group_nr = AmoebaNr[ax][ay];
6585 static int xy[4][2] =
6593 if (new_group_nr == 0)
6596 for (i = 0; i < NUM_DIRECTIONS; i++)
6601 if (!IN_LEV_FIELD(x, y))
6604 if ((Feld[x][y] == EL_AMOEBA_FULL ||
6605 Feld[x][y] == EL_BD_AMOEBA ||
6606 Feld[x][y] == EL_AMOEBA_DEAD) &&
6607 AmoebaNr[x][y] != new_group_nr)
6609 int old_group_nr = AmoebaNr[x][y];
6611 if (old_group_nr == 0)
6614 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
6615 AmoebaCnt[old_group_nr] = 0;
6616 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
6617 AmoebaCnt2[old_group_nr] = 0;
6620 SCAN_PLAYFIELD(xx, yy)
6622 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
6625 if (AmoebaNr[xx][yy] == old_group_nr)
6626 AmoebaNr[xx][yy] = new_group_nr;
6632 void AmoebeUmwandeln(int ax, int ay)
6636 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
6638 int group_nr = AmoebaNr[ax][ay];
6643 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
6644 printf("AmoebeUmwandeln(): This should never happen!\n");
6650 SCAN_PLAYFIELD(x, y)
6652 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
6655 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
6658 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
6662 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
6663 SND_AMOEBA_TURNING_TO_GEM :
6664 SND_AMOEBA_TURNING_TO_ROCK));
6669 static int xy[4][2] =
6677 for (i = 0; i < NUM_DIRECTIONS; i++)
6682 if (!IN_LEV_FIELD(x, y))
6685 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
6687 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
6688 SND_AMOEBA_TURNING_TO_GEM :
6689 SND_AMOEBA_TURNING_TO_ROCK));
6696 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
6699 int group_nr = AmoebaNr[ax][ay];
6700 boolean done = FALSE;
6705 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
6706 printf("AmoebeUmwandelnBD(): This should never happen!\n");
6712 SCAN_PLAYFIELD(x, y)
6714 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
6717 if (AmoebaNr[x][y] == group_nr &&
6718 (Feld[x][y] == EL_AMOEBA_DEAD ||
6719 Feld[x][y] == EL_BD_AMOEBA ||
6720 Feld[x][y] == EL_AMOEBA_GROWING))
6723 Feld[x][y] = new_element;
6724 InitField(x, y, FALSE);
6725 DrawLevelField(x, y);
6731 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
6732 SND_BD_AMOEBA_TURNING_TO_ROCK :
6733 SND_BD_AMOEBA_TURNING_TO_GEM));
6736 void AmoebeWaechst(int x, int y)
6738 static unsigned long sound_delay = 0;
6739 static unsigned long sound_delay_value = 0;
6741 if (!MovDelay[x][y]) /* start new growing cycle */
6745 if (DelayReached(&sound_delay, sound_delay_value))
6747 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
6748 sound_delay_value = 30;
6752 if (MovDelay[x][y]) /* wait some time before growing bigger */
6755 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6757 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
6758 6 - MovDelay[x][y]);
6760 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
6763 if (!MovDelay[x][y])
6765 Feld[x][y] = Store[x][y];
6767 DrawLevelField(x, y);
6772 void AmoebaDisappearing(int x, int y)
6774 static unsigned long sound_delay = 0;
6775 static unsigned long sound_delay_value = 0;
6777 if (!MovDelay[x][y]) /* start new shrinking cycle */
6781 if (DelayReached(&sound_delay, sound_delay_value))
6782 sound_delay_value = 30;
6785 if (MovDelay[x][y]) /* wait some time before shrinking */
6788 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6790 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
6791 6 - MovDelay[x][y]);
6793 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
6796 if (!MovDelay[x][y])
6798 Feld[x][y] = EL_EMPTY;
6799 DrawLevelField(x, y);
6801 /* don't let mole enter this field in this cycle;
6802 (give priority to objects falling to this field from above) */
6808 void AmoebeAbleger(int ax, int ay)
6811 int element = Feld[ax][ay];
6812 int graphic = el2img(element);
6813 int newax = ax, neway = ay;
6814 boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
6815 static int xy[4][2] =
6823 if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
6825 Feld[ax][ay] = EL_AMOEBA_DEAD;
6826 DrawLevelField(ax, ay);
6830 if (IS_ANIMATED(graphic))
6831 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6833 if (!MovDelay[ax][ay]) /* start making new amoeba field */
6834 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
6836 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
6839 if (MovDelay[ax][ay])
6843 if (can_drop) /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
6846 int x = ax + xy[start][0];
6847 int y = ay + xy[start][1];
6849 if (!IN_LEV_FIELD(x, y))
6852 if (IS_FREE(x, y) ||
6853 CAN_GROW_INTO(Feld[x][y]) ||
6854 Feld[x][y] == EL_QUICKSAND_EMPTY)
6860 if (newax == ax && neway == ay)
6863 else /* normal or "filled" (BD style) amoeba */
6866 boolean waiting_for_player = FALSE;
6868 for (i = 0; i < NUM_DIRECTIONS; i++)
6870 int j = (start + i) % 4;
6871 int x = ax + xy[j][0];
6872 int y = ay + xy[j][1];
6874 if (!IN_LEV_FIELD(x, y))
6877 if (IS_FREE(x, y) ||
6878 CAN_GROW_INTO(Feld[x][y]) ||
6879 Feld[x][y] == EL_QUICKSAND_EMPTY)
6885 else if (IS_PLAYER(x, y))
6886 waiting_for_player = TRUE;
6889 if (newax == ax && neway == ay) /* amoeba cannot grow */
6891 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
6893 Feld[ax][ay] = EL_AMOEBA_DEAD;
6894 DrawLevelField(ax, ay);
6895 AmoebaCnt[AmoebaNr[ax][ay]]--;
6897 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
6899 if (element == EL_AMOEBA_FULL)
6900 AmoebeUmwandeln(ax, ay);
6901 else if (element == EL_BD_AMOEBA)
6902 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
6907 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
6909 /* amoeba gets larger by growing in some direction */
6911 int new_group_nr = AmoebaNr[ax][ay];
6914 if (new_group_nr == 0)
6916 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
6917 printf("AmoebeAbleger(): This should never happen!\n");
6922 AmoebaNr[newax][neway] = new_group_nr;
6923 AmoebaCnt[new_group_nr]++;
6924 AmoebaCnt2[new_group_nr]++;
6926 /* if amoeba touches other amoeba(s) after growing, unify them */
6927 AmoebenVereinigen(newax, neway);
6929 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
6931 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
6937 if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
6938 (neway == lev_fieldy - 1 && newax != ax))
6940 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
6941 Store[newax][neway] = element;
6943 else if (neway == ay || element == EL_EMC_DRIPPER)
6945 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
6947 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
6951 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
6952 Feld[ax][ay] = EL_AMOEBA_DROPPING;
6953 Store[ax][ay] = EL_AMOEBA_DROP;
6954 ContinueMoving(ax, ay);
6958 DrawLevelField(newax, neway);
6961 void Life(int ax, int ay)
6965 static int life[4] = { 2, 3, 3, 3 }; /* parameters for "game of life" */
6968 int element = Feld[ax][ay];
6969 int graphic = el2img(element);
6970 int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
6972 boolean changed = FALSE;
6974 if (IS_ANIMATED(graphic))
6975 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6980 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
6981 MovDelay[ax][ay] = life_time;
6983 if (MovDelay[ax][ay]) /* wait some time before next cycle */
6986 if (MovDelay[ax][ay])
6990 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
6992 int xx = ax+x1, yy = ay+y1;
6995 if (!IN_LEV_FIELD(xx, yy))
6998 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
7000 int x = xx+x2, y = yy+y2;
7002 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
7005 if (((Feld[x][y] == element ||
7006 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
7008 (IS_FREE(x, y) && Stop[x][y]))
7012 if (xx == ax && yy == ay) /* field in the middle */
7014 if (nachbarn < life_parameter[0] ||
7015 nachbarn > life_parameter[1])
7017 Feld[xx][yy] = EL_EMPTY;
7019 DrawLevelField(xx, yy);
7020 Stop[xx][yy] = TRUE;
7024 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
7025 { /* free border field */
7026 if (nachbarn >= life_parameter[2] &&
7027 nachbarn <= life_parameter[3])
7029 Feld[xx][yy] = element;
7030 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
7032 DrawLevelField(xx, yy);
7033 Stop[xx][yy] = TRUE;
7040 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
7041 SND_GAME_OF_LIFE_GROWING);
7044 static void InitRobotWheel(int x, int y)
7046 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
7049 static void RunRobotWheel(int x, int y)
7051 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
7054 static void StopRobotWheel(int x, int y)
7056 if (ZX == x && ZY == y)
7060 static void InitTimegateWheel(int x, int y)
7062 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
7065 static void RunTimegateWheel(int x, int y)
7067 PlayLevelSound(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
7070 static void InitMagicBallDelay(int x, int y)
7073 ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
7075 ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
7079 static void ActivateMagicBall(int bx, int by)
7083 if (level.ball_random)
7085 int pos_border = RND(8); /* select one of the eight border elements */
7086 int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
7087 int xx = pos_content % 3;
7088 int yy = pos_content / 3;
7093 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
7094 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
7098 for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
7100 int xx = x - bx + 1;
7101 int yy = y - by + 1;
7103 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
7104 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
7108 game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
7111 static void InitDiagonalMovingElement(int x, int y)
7114 MovDelay[x][y] = level.android_move_time;
7118 void CheckExit(int x, int y)
7120 if (local_player->gems_still_needed > 0 ||
7121 local_player->sokobanfields_still_needed > 0 ||
7122 local_player->lights_still_needed > 0)
7124 int element = Feld[x][y];
7125 int graphic = el2img(element);
7127 if (IS_ANIMATED(graphic))
7128 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7133 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7136 Feld[x][y] = EL_EXIT_OPENING;
7138 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
7141 void CheckExitSP(int x, int y)
7143 if (local_player->gems_still_needed > 0)
7145 int element = Feld[x][y];
7146 int graphic = el2img(element);
7148 if (IS_ANIMATED(graphic))
7149 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7154 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7157 Feld[x][y] = EL_SP_EXIT_OPENING;
7159 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
7162 static void CloseAllOpenTimegates()
7167 SCAN_PLAYFIELD(x, y)
7169 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7172 int element = Feld[x][y];
7174 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
7176 Feld[x][y] = EL_TIMEGATE_CLOSING;
7178 PlayLevelSoundAction(x, y, ACTION_CLOSING);
7183 void EdelsteinFunkeln(int x, int y)
7185 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
7188 if (Feld[x][y] == EL_BD_DIAMOND)
7191 if (MovDelay[x][y] == 0) /* next animation frame */
7192 MovDelay[x][y] = 11 * !SimpleRND(500);
7194 if (MovDelay[x][y] != 0) /* wait some time before next frame */
7198 if (setup.direct_draw && MovDelay[x][y])
7199 SetDrawtoField(DRAW_BUFFERED);
7201 DrawLevelElementAnimation(x, y, Feld[x][y]);
7203 if (MovDelay[x][y] != 0)
7205 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
7206 10 - MovDelay[x][y]);
7208 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
7210 if (setup.direct_draw)
7214 dest_x = FX + SCREENX(x) * TILEX;
7215 dest_y = FY + SCREENY(y) * TILEY;
7217 BlitBitmap(drawto_field, window,
7218 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
7219 SetDrawtoField(DRAW_DIRECT);
7225 void MauerWaechst(int x, int y)
7229 if (!MovDelay[x][y]) /* next animation frame */
7230 MovDelay[x][y] = 3 * delay;
7232 if (MovDelay[x][y]) /* wait some time before next frame */
7236 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7238 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
7239 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
7241 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
7244 if (!MovDelay[x][y])
7246 if (MovDir[x][y] == MV_LEFT)
7248 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
7249 DrawLevelField(x - 1, y);
7251 else if (MovDir[x][y] == MV_RIGHT)
7253 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
7254 DrawLevelField(x + 1, y);
7256 else if (MovDir[x][y] == MV_UP)
7258 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
7259 DrawLevelField(x, y - 1);
7263 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
7264 DrawLevelField(x, y + 1);
7267 Feld[x][y] = Store[x][y];
7269 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7270 DrawLevelField(x, y);
7275 void MauerAbleger(int ax, int ay)
7277 int element = Feld[ax][ay];
7278 int graphic = el2img(element);
7279 boolean oben_frei = FALSE, unten_frei = FALSE;
7280 boolean links_frei = FALSE, rechts_frei = FALSE;
7281 boolean oben_massiv = FALSE, unten_massiv = FALSE;
7282 boolean links_massiv = FALSE, rechts_massiv = FALSE;
7283 boolean new_wall = FALSE;
7285 if (IS_ANIMATED(graphic))
7286 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7288 if (!MovDelay[ax][ay]) /* start building new wall */
7289 MovDelay[ax][ay] = 6;
7291 if (MovDelay[ax][ay]) /* wait some time before building new wall */
7294 if (MovDelay[ax][ay])
7298 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
7300 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
7302 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
7304 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
7307 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
7308 element == EL_EXPANDABLE_WALL_ANY)
7312 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
7313 Store[ax][ay-1] = element;
7314 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
7315 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
7316 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
7317 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
7322 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
7323 Store[ax][ay+1] = element;
7324 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
7325 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
7326 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
7327 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
7332 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7333 element == EL_EXPANDABLE_WALL_ANY ||
7334 element == EL_EXPANDABLE_WALL)
7338 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
7339 Store[ax-1][ay] = element;
7340 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
7341 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
7342 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
7343 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
7349 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
7350 Store[ax+1][ay] = element;
7351 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
7352 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
7353 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
7354 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
7359 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
7360 DrawLevelField(ax, ay);
7362 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
7364 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
7365 unten_massiv = TRUE;
7366 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
7367 links_massiv = TRUE;
7368 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
7369 rechts_massiv = TRUE;
7371 if (((oben_massiv && unten_massiv) ||
7372 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7373 element == EL_EXPANDABLE_WALL) &&
7374 ((links_massiv && rechts_massiv) ||
7375 element == EL_EXPANDABLE_WALL_VERTICAL))
7376 Feld[ax][ay] = EL_WALL;
7379 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
7382 void CheckForDragon(int x, int y)
7385 boolean dragon_found = FALSE;
7386 static int xy[4][2] =
7394 for (i = 0; i < NUM_DIRECTIONS; i++)
7396 for (j = 0; j < 4; j++)
7398 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7400 if (IN_LEV_FIELD(xx, yy) &&
7401 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
7403 if (Feld[xx][yy] == EL_DRAGON)
7404 dragon_found = TRUE;
7413 for (i = 0; i < NUM_DIRECTIONS; i++)
7415 for (j = 0; j < 3; j++)
7417 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7419 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
7421 Feld[xx][yy] = EL_EMPTY;
7422 DrawLevelField(xx, yy);
7431 static void InitBuggyBase(int x, int y)
7433 int element = Feld[x][y];
7434 int activating_delay = FRAMES_PER_SECOND / 4;
7437 (element == EL_SP_BUGGY_BASE ?
7438 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
7439 element == EL_SP_BUGGY_BASE_ACTIVATING ?
7441 element == EL_SP_BUGGY_BASE_ACTIVE ?
7442 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
7445 static void WarnBuggyBase(int x, int y)
7448 static int xy[4][2] =
7456 for (i = 0; i < NUM_DIRECTIONS; i++)
7458 int xx = x + xy[i][0];
7459 int yy = y + xy[i][1];
7461 if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
7463 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
7470 static void InitTrap(int x, int y)
7472 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
7475 static void ActivateTrap(int x, int y)
7477 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
7480 static void ChangeActiveTrap(int x, int y)
7482 int graphic = IMG_TRAP_ACTIVE;
7484 /* if new animation frame was drawn, correct crumbled sand border */
7485 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
7486 DrawLevelFieldCrumbledSand(x, y);
7489 static int getSpecialActionElement(int element, int number, int base_element)
7491 return (element != EL_EMPTY ? element :
7492 number != -1 ? base_element + number - 1 :
7496 static int getModifiedActionNumber(int value_old, int operator, int operand,
7497 int value_min, int value_max)
7499 int value_new = (operator == CA_MODE_SET ? operand :
7500 operator == CA_MODE_ADD ? value_old + operand :
7501 operator == CA_MODE_SUBTRACT ? value_old - operand :
7502 operator == CA_MODE_MULTIPLY ? value_old * operand :
7503 operator == CA_MODE_DIVIDE ? value_old / MAX(1, operand) :
7504 operator == CA_MODE_MODULO ? value_old % MAX(1, operand) :
7507 return (value_new < value_min ? value_min :
7508 value_new > value_max ? value_max :
7512 static void ExecuteCustomElementAction(int x, int y, int element, int page)
7514 struct ElementInfo *ei = &element_info[element];
7515 struct ElementChangeInfo *change = &ei->change_page[page];
7516 int action_type = change->action_type;
7517 int action_mode = change->action_mode;
7518 int action_arg = change->action_arg;
7521 if (!change->has_action)
7524 /* ---------- determine action paramater values -------------------------- */
7526 int level_time_value =
7527 (level.time > 0 ? TimeLeft :
7530 int action_arg_element =
7531 (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player :
7532 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
7533 action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element :
7536 int action_arg_direction =
7537 (action_arg >= CA_ARG_DIRECTION_LEFT &&
7538 action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
7539 action_arg == CA_ARG_DIRECTION_TRIGGER ?
7540 change->actual_trigger_side :
7541 action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
7542 MV_DIR_OPPOSITE(change->actual_trigger_side) :
7545 int action_arg_number_min =
7546 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
7549 int action_arg_number_max =
7550 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
7551 action_type == CA_SET_LEVEL_GEMS ? 999 :
7552 action_type == CA_SET_LEVEL_TIME ? 9999 :
7553 action_type == CA_SET_LEVEL_SCORE ? 99999 :
7554 action_type == CA_SET_CE_SCORE ? 9999 :
7555 action_type == CA_SET_CE_VALUE ? 9999 :
7558 int action_arg_number_reset =
7559 (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize :
7560 action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
7561 action_type == CA_SET_LEVEL_TIME ? level.time :
7562 action_type == CA_SET_LEVEL_SCORE ? 0 :
7563 action_type == CA_SET_CE_SCORE ? 0 :
7565 action_type == CA_SET_CE_VALUE ? GET_NEW_CUSTOM_VALUE(element) :
7567 action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
7571 int action_arg_number =
7572 (action_arg <= CA_ARG_MAX ? action_arg :
7573 action_arg >= CA_ARG_SPEED_NOT_MOVING &&
7574 action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
7575 action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
7576 action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
7577 action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
7578 action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
7579 action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
7580 #if USE_NEW_CUSTOM_VALUE
7581 action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
7583 action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
7585 action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
7586 action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
7587 action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
7588 action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
7589 action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CUSTOM_VALUE(change->target_element) :
7590 action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
7591 action_arg == CA_ARG_ELEMENT_NR_TARGET ? change->target_element :
7592 action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
7595 int action_arg_number_old =
7596 (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
7597 action_type == CA_SET_LEVEL_TIME ? TimeLeft :
7598 action_type == CA_SET_LEVEL_SCORE ? local_player->score :
7599 action_type == CA_SET_CE_SCORE ? ei->collect_score :
7600 action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
7603 int action_arg_number_new =
7604 getModifiedActionNumber(action_arg_number_old,
7605 action_mode, action_arg_number,
7606 action_arg_number_min, action_arg_number_max);
7608 int trigger_player_bits =
7609 (change->actual_trigger_player >= EL_PLAYER_1 &&
7610 change->actual_trigger_player <= EL_PLAYER_4 ?
7611 (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
7614 int action_arg_player_bits =
7615 (action_arg >= CA_ARG_PLAYER_1 &&
7616 action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
7617 action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
7620 /* ---------- execute action -------------------------------------------- */
7629 /* ---------- level actions ------------------------------------------- */
7631 case CA_RESTART_LEVEL:
7633 game.restart_level = TRUE;
7638 case CA_SHOW_ENVELOPE:
7640 int element = getSpecialActionElement(action_arg_element,
7641 action_arg_number, EL_ENVELOPE_1);
7643 if (IS_ENVELOPE(element))
7644 local_player->show_envelope = element;
7649 case CA_SET_LEVEL_TIME:
7651 if (level.time > 0) /* only modify limited time value */
7653 TimeLeft = action_arg_number_new;
7655 DrawGameValue_Time(TimeLeft);
7657 if (!TimeLeft && setup.time_limit)
7658 for (i = 0; i < MAX_PLAYERS; i++)
7659 KillPlayer(&stored_player[i]);
7665 case CA_SET_LEVEL_SCORE:
7667 local_player->score = action_arg_number_new;
7669 DrawGameValue_Score(local_player->score);
7674 case CA_SET_LEVEL_GEMS:
7676 local_player->gems_still_needed = action_arg_number_new;
7678 DrawGameValue_Emeralds(local_player->gems_still_needed);
7683 case CA_SET_LEVEL_GRAVITY:
7685 game.gravity = (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
7686 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
7687 action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
7692 case CA_SET_LEVEL_WIND:
7694 game.wind_direction = action_arg_direction;
7699 /* ---------- player actions ------------------------------------------ */
7701 case CA_MOVE_PLAYER:
7703 /* automatically move to the next field in specified direction */
7704 for (i = 0; i < MAX_PLAYERS; i++)
7705 if (trigger_player_bits & (1 << i))
7706 stored_player[i].programmed_action = action_arg_direction;
7711 case CA_EXIT_PLAYER:
7713 for (i = 0; i < MAX_PLAYERS; i++)
7714 if (action_arg_player_bits & (1 << i))
7715 stored_player[i].LevelSolved = stored_player[i].GameOver = TRUE;
7720 case CA_KILL_PLAYER:
7722 for (i = 0; i < MAX_PLAYERS; i++)
7723 if (action_arg_player_bits & (1 << i))
7724 KillPlayer(&stored_player[i]);
7729 case CA_SET_PLAYER_KEYS:
7731 int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
7732 int element = getSpecialActionElement(action_arg_element,
7733 action_arg_number, EL_KEY_1);
7735 if (IS_KEY(element))
7737 for (i = 0; i < MAX_PLAYERS; i++)
7739 if (trigger_player_bits & (1 << i))
7741 stored_player[i].key[KEY_NR(element)] = key_state;
7743 DrawGameValue_Keys(stored_player[i].key);
7745 redraw_mask |= REDRAW_DOOR_1;
7753 case CA_SET_PLAYER_SPEED:
7755 for (i = 0; i < MAX_PLAYERS; i++)
7757 if (trigger_player_bits & (1 << i))
7759 int move_stepsize = TILEX / stored_player[i].move_delay_value;
7761 if (action_arg == CA_ARG_SPEED_FASTER &&
7762 stored_player[i].cannot_move)
7764 action_arg_number = STEPSIZE_VERY_SLOW;
7766 else if (action_arg == CA_ARG_SPEED_SLOWER ||
7767 action_arg == CA_ARG_SPEED_FASTER)
7769 action_arg_number = 2;
7770 action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
7775 getModifiedActionNumber(move_stepsize,
7778 action_arg_number_min,
7779 action_arg_number_max);
7782 SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
7784 /* make sure that value is power of 2 */
7785 move_stepsize = (1 << log_2(move_stepsize));
7787 /* do no immediately change -- the player might just be moving */
7788 stored_player[i].move_delay_value_next = TILEX / move_stepsize;
7790 stored_player[i].cannot_move =
7791 (action_arg == CA_ARG_SPEED_NOT_MOVING ? TRUE : FALSE);
7799 case CA_SET_PLAYER_SHIELD:
7801 for (i = 0; i < MAX_PLAYERS; i++)
7803 if (trigger_player_bits & (1 << i))
7805 if (action_arg == CA_ARG_SHIELD_OFF)
7807 stored_player[i].shield_normal_time_left = 0;
7808 stored_player[i].shield_deadly_time_left = 0;
7810 else if (action_arg == CA_ARG_SHIELD_NORMAL)
7812 stored_player[i].shield_normal_time_left = 999999;
7814 else if (action_arg == CA_ARG_SHIELD_DEADLY)
7816 stored_player[i].shield_normal_time_left = 999999;
7817 stored_player[i].shield_deadly_time_left = 999999;
7825 case CA_SET_PLAYER_ARTWORK:
7827 for (i = 0; i < MAX_PLAYERS; i++)
7829 if (trigger_player_bits & (1 << i))
7831 int artwork_element = action_arg_element;
7833 if (action_arg == CA_ARG_ELEMENT_RESET)
7835 (level.use_artwork_element[i] ? level.artwork_element[i] :
7836 stored_player[i].element_nr);
7838 stored_player[i].artwork_element = artwork_element;
7840 SetPlayerWaiting(&stored_player[i], FALSE);
7842 /* set number of special actions for bored and sleeping animation */
7843 stored_player[i].num_special_action_bored =
7844 get_num_special_action(artwork_element,
7845 ACTION_BORING_1, ACTION_BORING_LAST);
7846 stored_player[i].num_special_action_sleeping =
7847 get_num_special_action(artwork_element,
7848 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
7855 /* ---------- CE actions ---------------------------------------------- */
7857 case CA_SET_CE_SCORE:
7859 ei->collect_score = action_arg_number_new;
7864 case CA_SET_CE_VALUE:
7866 #if USE_NEW_CUSTOM_VALUE
7867 int last_custom_value = CustomValue[x][y];
7869 CustomValue[x][y] = action_arg_number_new;
7872 printf("::: Count == %d\n", CustomValue[x][y]);
7875 if (CustomValue[x][y] == 0 && last_custom_value > 0)
7878 printf("::: CE_VALUE_GETS_ZERO\n");
7881 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
7882 CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
7885 printf("::: RESULT: %d, %d\n", Feld[x][y], ChangePage[x][y]);
7893 /* ---------- engine actions ------------------------------------------ */
7895 case CA_SET_ENGINE_SCAN_MODE:
7897 InitPlayfieldScanMode(action_arg);
7907 static void CreateFieldExt(int x, int y, int element, boolean is_change)
7909 int previous_move_direction = MovDir[x][y];
7910 #if USE_NEW_CUSTOM_VALUE
7911 int last_ce_value = CustomValue[x][y];
7913 boolean add_player = (ELEM_IS_PLAYER(element) &&
7914 IS_WALKABLE(Feld[x][y]));
7916 /* check if element under player changes from accessible to unaccessible
7917 (needed for special case of dropping element which then changes) */
7918 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
7919 IS_ACCESSIBLE(Feld[x][y]) && !IS_ACCESSIBLE(element))
7928 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
7929 RemoveMovingField(x, y);
7933 Feld[x][y] = element;
7935 ResetGfxAnimation(x, y);
7936 ResetRandomAnimationValue(x, y);
7938 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
7939 MovDir[x][y] = previous_move_direction;
7941 #if USE_NEW_CUSTOM_VALUE
7942 if (element_info[Feld[x][y]].use_last_ce_value)
7943 CustomValue[x][y] = last_ce_value;
7946 InitField_WithBug1(x, y, FALSE);
7948 DrawLevelField(x, y);
7950 if (GFX_CRUMBLED(Feld[x][y]))
7951 DrawLevelFieldCrumbledSandNeighbours(x, y);
7954 /* "ChangeCount" not set yet to allow "entered by player" change one time */
7955 if (ELEM_IS_PLAYER(element))
7956 RelocatePlayer(x, y, element);
7959 ChangeCount[x][y]++; /* count number of changes in the same frame */
7961 TestIfBadThingTouchesPlayer(x, y);
7962 TestIfPlayerTouchesCustomElement(x, y);
7963 TestIfElementTouchesCustomElement(x, y);
7966 static void CreateField(int x, int y, int element)
7968 CreateFieldExt(x, y, element, FALSE);
7971 static void CreateElementFromChange(int x, int y, int element)
7973 element = GET_VALID_RUNTIME_ELEMENT(element);
7975 #if USE_STOP_CHANGED_ELEMENTS
7976 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
7978 int old_element = Feld[x][y];
7980 /* prevent changed element from moving in same engine frame
7981 unless both old and new element can either fall or move */
7982 if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
7983 (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
7988 CreateFieldExt(x, y, element, TRUE);
7991 static boolean ChangeElement(int x, int y, int element, int page)
7993 struct ElementChangeInfo *change = &element_info[element].change_page[page];
7995 int old_element = Feld[x][y];
7997 /* always use default change event to prevent running into a loop */
7998 if (ChangeEvent[x][y] == -1)
7999 ChangeEvent[x][y] = CE_DELAY;
8001 if (ChangeEvent[x][y] == CE_DELAY)
8003 /* reset actual trigger element, trigger player and action element */
8004 change->actual_trigger_element = EL_EMPTY;
8005 change->actual_trigger_player = EL_PLAYER_1;
8006 change->actual_trigger_side = CH_SIDE_NONE;
8007 change->actual_trigger_ce_value = 0;
8010 /* do not change elements more than a specified maximum number of changes */
8011 if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
8014 ChangeCount[x][y]++; /* count number of changes in the same frame */
8016 if (change->explode)
8023 if (change->use_target_content)
8025 boolean complete_replace = TRUE;
8026 boolean can_replace[3][3];
8029 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
8032 boolean is_walkable;
8033 boolean is_diggable;
8034 boolean is_collectible;
8035 boolean is_removable;
8036 boolean is_destructible;
8037 int ex = x + xx - 1;
8038 int ey = y + yy - 1;
8039 int content_element = change->target_content.e[xx][yy];
8042 can_replace[xx][yy] = TRUE;
8044 if (ex == x && ey == y) /* do not check changing element itself */
8047 if (content_element == EL_EMPTY_SPACE)
8049 can_replace[xx][yy] = FALSE; /* do not replace border with space */
8054 if (!IN_LEV_FIELD(ex, ey))
8056 can_replace[xx][yy] = FALSE;
8057 complete_replace = FALSE;
8064 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
8065 e = MovingOrBlocked2Element(ex, ey);
8067 is_empty = (IS_FREE(ex, ey) ||
8068 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
8070 is_walkable = (is_empty || IS_WALKABLE(e));
8071 is_diggable = (is_empty || IS_DIGGABLE(e));
8072 is_collectible = (is_empty || IS_COLLECTIBLE(e));
8073 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
8074 is_removable = (is_diggable || is_collectible);
8076 can_replace[xx][yy] =
8077 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
8078 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
8079 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
8080 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
8081 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
8082 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
8083 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
8085 if (!can_replace[xx][yy])
8086 complete_replace = FALSE;
8089 if (!change->only_if_complete || complete_replace)
8091 boolean something_has_changed = FALSE;
8093 if (change->only_if_complete && change->use_random_replace &&
8094 RND(100) < change->random_percentage)
8097 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
8099 int ex = x + xx - 1;
8100 int ey = y + yy - 1;
8101 int content_element;
8103 if (can_replace[xx][yy] && (!change->use_random_replace ||
8104 RND(100) < change->random_percentage))
8106 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
8107 RemoveMovingField(ex, ey);
8109 ChangeEvent[ex][ey] = ChangeEvent[x][y];
8111 content_element = change->target_content.e[xx][yy];
8112 target_element = GET_TARGET_ELEMENT(content_element, change);
8114 CreateElementFromChange(ex, ey, target_element);
8116 something_has_changed = TRUE;
8118 /* for symmetry reasons, freeze newly created border elements */
8119 if (ex != x || ey != y)
8120 Stop[ex][ey] = TRUE; /* no more moving in this frame */
8124 if (something_has_changed)
8126 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
8127 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
8133 target_element = GET_TARGET_ELEMENT(change->target_element, change);
8135 if (element == EL_DIAGONAL_GROWING ||
8136 element == EL_DIAGONAL_SHRINKING)
8138 target_element = Store[x][y];
8140 Store[x][y] = EL_EMPTY;
8143 CreateElementFromChange(x, y, target_element);
8145 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
8146 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
8149 /* this uses direct change before indirect change */
8150 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
8155 #if USE_NEW_DELAYED_ACTION
8157 static void HandleElementChange(int x, int y, int page)
8159 int element = MovingOrBlocked2Element(x, y);
8160 struct ElementInfo *ei = &element_info[element];
8161 struct ElementChangeInfo *change = &ei->change_page[page];
8164 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
8165 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
8168 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
8169 x, y, element, element_info[element].token_name);
8170 printf("HandleElementChange(): This should never happen!\n");
8175 /* this can happen with classic bombs on walkable, changing elements */
8176 if (!CAN_CHANGE_OR_HAS_ACTION(element))
8179 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
8180 ChangeDelay[x][y] = 0;
8186 if (ChangeDelay[x][y] == 0) /* initialize element change */
8188 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
8190 if (change->can_change)
8192 ResetGfxAnimation(x, y);
8193 ResetRandomAnimationValue(x, y);
8195 if (change->pre_change_function)
8196 change->pre_change_function(x, y);
8200 ChangeDelay[x][y]--;
8202 if (ChangeDelay[x][y] != 0) /* continue element change */
8204 if (change->can_change)
8206 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8208 if (IS_ANIMATED(graphic))
8209 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8211 if (change->change_function)
8212 change->change_function(x, y);
8215 else /* finish element change */
8217 if (ChangePage[x][y] != -1) /* remember page from delayed change */
8219 page = ChangePage[x][y];
8220 ChangePage[x][y] = -1;
8222 change = &ei->change_page[page];
8225 if (IS_MOVING(x, y)) /* never change a running system ;-) */
8227 ChangeDelay[x][y] = 1; /* try change after next move step */
8228 ChangePage[x][y] = page; /* remember page to use for change */
8233 if (change->can_change)
8235 if (ChangeElement(x, y, element, page))
8237 if (change->post_change_function)
8238 change->post_change_function(x, y);
8242 if (change->has_action)
8243 ExecuteCustomElementAction(x, y, element, page);
8249 static void HandleElementChange(int x, int y, int page)
8251 int element = MovingOrBlocked2Element(x, y);
8252 struct ElementInfo *ei = &element_info[element];
8253 struct ElementChangeInfo *change = &ei->change_page[page];
8256 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
8259 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
8260 x, y, element, element_info[element].token_name);
8261 printf("HandleElementChange(): This should never happen!\n");
8266 /* this can happen with classic bombs on walkable, changing elements */
8267 if (!CAN_CHANGE(element))
8270 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
8271 ChangeDelay[x][y] = 0;
8277 if (ChangeDelay[x][y] == 0) /* initialize element change */
8279 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
8281 ResetGfxAnimation(x, y);
8282 ResetRandomAnimationValue(x, y);
8284 if (change->pre_change_function)
8285 change->pre_change_function(x, y);
8288 ChangeDelay[x][y]--;
8290 if (ChangeDelay[x][y] != 0) /* continue element change */
8292 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8294 if (IS_ANIMATED(graphic))
8295 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8297 if (change->change_function)
8298 change->change_function(x, y);
8300 else /* finish element change */
8302 if (ChangePage[x][y] != -1) /* remember page from delayed change */
8304 page = ChangePage[x][y];
8305 ChangePage[x][y] = -1;
8307 change = &ei->change_page[page];
8310 if (IS_MOVING(x, y)) /* never change a running system ;-) */
8312 ChangeDelay[x][y] = 1; /* try change after next move step */
8313 ChangePage[x][y] = page; /* remember page to use for change */
8318 if (ChangeElement(x, y, element, page))
8320 if (change->post_change_function)
8321 change->post_change_function(x, y);
8328 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
8329 int trigger_element,
8335 boolean change_done_any = FALSE;
8336 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
8339 if (!(trigger_events[trigger_element][trigger_event]))
8342 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8344 int element = EL_CUSTOM_START + i;
8345 boolean change_done = FALSE;
8348 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
8349 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
8352 for (p = 0; p < element_info[element].num_change_pages; p++)
8354 struct ElementChangeInfo *change = &element_info[element].change_page[p];
8356 if (change->can_change_or_has_action &&
8357 change->has_event[trigger_event] &&
8358 change->trigger_side & trigger_side &&
8359 change->trigger_player & trigger_player &&
8360 change->trigger_page & trigger_page_bits &&
8361 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
8363 change->actual_trigger_element = trigger_element;
8364 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
8365 change->actual_trigger_side = trigger_side;
8366 change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
8368 if ((change->can_change && !change_done) || change->has_action)
8373 SCAN_PLAYFIELD(x, y)
8375 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8378 if (Feld[x][y] == element)
8380 if (change->can_change && !change_done)
8382 ChangeDelay[x][y] = 1;
8383 ChangeEvent[x][y] = trigger_event;
8385 HandleElementChange(x, y, p);
8387 #if USE_NEW_DELAYED_ACTION
8388 else if (change->has_action)
8390 ExecuteCustomElementAction(x, y, element, p);
8391 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8394 if (change->has_action)
8396 ExecuteCustomElementAction(x, y, element, p);
8397 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8403 if (change->can_change)
8406 change_done_any = TRUE;
8413 return change_done_any;
8416 static boolean CheckElementChangeExt(int x, int y,
8418 int trigger_element,
8423 boolean change_done = FALSE;
8426 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
8427 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
8430 if (Feld[x][y] == EL_BLOCKED)
8432 Blocked2Moving(x, y, &x, &y);
8433 element = Feld[x][y];
8437 /* check if element has already changed */
8438 if (Feld[x][y] != element)
8441 /* check if element has already changed or is about to change after moving */
8442 if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
8443 Feld[x][y] != element) ||
8445 (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
8446 (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
8447 ChangePage[x][y] != -1)))
8451 for (p = 0; p < element_info[element].num_change_pages; p++)
8453 struct ElementChangeInfo *change = &element_info[element].change_page[p];
8455 boolean check_trigger_element =
8456 (trigger_event == CE_TOUCHING_X ||
8457 trigger_event == CE_HITTING_X ||
8458 trigger_event == CE_HIT_BY_X);
8460 if (change->can_change_or_has_action &&
8461 change->has_event[trigger_event] &&
8462 change->trigger_side & trigger_side &&
8463 change->trigger_player & trigger_player &&
8464 (!check_trigger_element ||
8465 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
8467 change->actual_trigger_element = trigger_element;
8468 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
8469 change->actual_trigger_side = trigger_side;
8470 change->actual_trigger_ce_value = CustomValue[x][y];
8472 /* special case: trigger element not at (x,y) position for some events */
8473 if (check_trigger_element)
8485 { 0, 0 }, { 0, 0 }, { 0, 0 },
8489 int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
8490 int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
8492 change->actual_trigger_ce_value = CustomValue[xx][yy];
8495 if (change->can_change && !change_done)
8497 ChangeDelay[x][y] = 1;
8498 ChangeEvent[x][y] = trigger_event;
8500 HandleElementChange(x, y, p);
8504 #if USE_NEW_DELAYED_ACTION
8505 else if (change->has_action)
8507 ExecuteCustomElementAction(x, y, element, p);
8508 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8511 if (change->has_action)
8513 ExecuteCustomElementAction(x, y, element, p);
8514 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8523 static void PlayPlayerSound(struct PlayerInfo *player)
8525 int jx = player->jx, jy = player->jy;
8526 int sound_element = player->artwork_element;
8527 int last_action = player->last_action_waiting;
8528 int action = player->action_waiting;
8530 if (player->is_waiting)
8532 if (action != last_action)
8533 PlayLevelSoundElementAction(jx, jy, sound_element, action);
8535 PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
8539 if (action != last_action)
8540 StopSound(element_info[sound_element].sound[last_action]);
8542 if (last_action == ACTION_SLEEPING)
8543 PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
8547 static void PlayAllPlayersSound()
8551 for (i = 0; i < MAX_PLAYERS; i++)
8552 if (stored_player[i].active)
8553 PlayPlayerSound(&stored_player[i]);
8556 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
8558 boolean last_waiting = player->is_waiting;
8559 int move_dir = player->MovDir;
8561 player->last_action_waiting = player->action_waiting;
8565 if (!last_waiting) /* not waiting -> waiting */
8567 player->is_waiting = TRUE;
8569 player->frame_counter_bored =
8571 game.player_boring_delay_fixed +
8572 SimpleRND(game.player_boring_delay_random);
8573 player->frame_counter_sleeping =
8575 game.player_sleeping_delay_fixed +
8576 SimpleRND(game.player_sleeping_delay_random);
8578 InitPlayerGfxAnimation(player, ACTION_WAITING, player->MovDir);
8581 if (game.player_sleeping_delay_fixed +
8582 game.player_sleeping_delay_random > 0 &&
8583 player->anim_delay_counter == 0 &&
8584 player->post_delay_counter == 0 &&
8585 FrameCounter >= player->frame_counter_sleeping)
8586 player->is_sleeping = TRUE;
8587 else if (game.player_boring_delay_fixed +
8588 game.player_boring_delay_random > 0 &&
8589 FrameCounter >= player->frame_counter_bored)
8590 player->is_bored = TRUE;
8592 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
8593 player->is_bored ? ACTION_BORING :
8596 if (player->is_sleeping)
8598 if (player->num_special_action_sleeping > 0)
8600 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
8602 int last_special_action = player->special_action_sleeping;
8603 int num_special_action = player->num_special_action_sleeping;
8604 int special_action =
8605 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
8606 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
8607 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
8608 last_special_action + 1 : ACTION_SLEEPING);
8609 int special_graphic =
8610 el_act_dir2img(player->artwork_element, special_action, move_dir);
8612 player->anim_delay_counter =
8613 graphic_info[special_graphic].anim_delay_fixed +
8614 SimpleRND(graphic_info[special_graphic].anim_delay_random);
8615 player->post_delay_counter =
8616 graphic_info[special_graphic].post_delay_fixed +
8617 SimpleRND(graphic_info[special_graphic].post_delay_random);
8619 player->special_action_sleeping = special_action;
8622 if (player->anim_delay_counter > 0)
8624 player->action_waiting = player->special_action_sleeping;
8625 player->anim_delay_counter--;
8627 else if (player->post_delay_counter > 0)
8629 player->post_delay_counter--;
8633 else if (player->is_bored)
8635 if (player->num_special_action_bored > 0)
8637 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
8639 int special_action =
8640 ACTION_BORING_1 + SimpleRND(player->num_special_action_bored);
8641 int special_graphic =
8642 el_act_dir2img(player->artwork_element, special_action, move_dir);
8644 player->anim_delay_counter =
8645 graphic_info[special_graphic].anim_delay_fixed +
8646 SimpleRND(graphic_info[special_graphic].anim_delay_random);
8647 player->post_delay_counter =
8648 graphic_info[special_graphic].post_delay_fixed +
8649 SimpleRND(graphic_info[special_graphic].post_delay_random);
8651 player->special_action_bored = special_action;
8654 if (player->anim_delay_counter > 0)
8656 player->action_waiting = player->special_action_bored;
8657 player->anim_delay_counter--;
8659 else if (player->post_delay_counter > 0)
8661 player->post_delay_counter--;
8666 else if (last_waiting) /* waiting -> not waiting */
8668 player->is_waiting = FALSE;
8669 player->is_bored = FALSE;
8670 player->is_sleeping = FALSE;
8672 player->frame_counter_bored = -1;
8673 player->frame_counter_sleeping = -1;
8675 player->anim_delay_counter = 0;
8676 player->post_delay_counter = 0;
8678 player->action_waiting = ACTION_DEFAULT;
8680 player->special_action_bored = ACTION_DEFAULT;
8681 player->special_action_sleeping = ACTION_DEFAULT;
8685 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
8687 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
8688 int left = player_action & JOY_LEFT;
8689 int right = player_action & JOY_RIGHT;
8690 int up = player_action & JOY_UP;
8691 int down = player_action & JOY_DOWN;
8692 int button1 = player_action & JOY_BUTTON_1;
8693 int button2 = player_action & JOY_BUTTON_2;
8694 int dx = (left ? -1 : right ? 1 : 0);
8695 int dy = (up ? -1 : down ? 1 : 0);
8697 if (!player->active || tape.pausing)
8703 snapped = SnapField(player, dx, dy);
8707 dropped = DropElement(player);
8709 moved = MovePlayer(player, dx, dy);
8712 if (tape.single_step && tape.recording && !tape.pausing)
8714 if (button1 || (dropped && !moved))
8716 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8717 SnapField(player, 0, 0); /* stop snapping */
8721 SetPlayerWaiting(player, FALSE);
8723 return player_action;
8727 /* no actions for this player (no input at player's configured device) */
8729 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
8730 SnapField(player, 0, 0);
8731 CheckGravityMovementWhenNotMoving(player);
8733 if (player->MovPos == 0)
8734 SetPlayerWaiting(player, TRUE);
8736 if (player->MovPos == 0) /* needed for tape.playing */
8737 player->is_moving = FALSE;
8739 player->is_dropping = FALSE;
8745 void AdvanceFrameAndPlayerCounters(int player_nr)
8749 /* advance frame counters (global frame counter and time frame counter) */
8753 /* advance player counters (counters for move delay, move animation etc.) */
8754 for (i = 0; i < MAX_PLAYERS; i++)
8756 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
8757 int move_delay_value = stored_player[i].move_delay_value;
8758 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
8760 if (!advance_player_counters) /* not all players may be affected */
8763 #if USE_NEW_PLAYER_ANIM
8764 if (move_frames == 0) /* less than one move per game frame */
8766 int stepsize = TILEX / move_delay_value;
8767 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
8768 int count = (stored_player[i].is_moving ?
8769 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
8771 if (count % delay == 0)
8776 stored_player[i].Frame += move_frames;
8778 if (stored_player[i].MovPos != 0)
8779 stored_player[i].StepFrame += move_frames;
8781 if (stored_player[i].move_delay > 0)
8782 stored_player[i].move_delay--;
8784 /* due to bugs in previous versions, counter must count up, not down */
8785 if (stored_player[i].push_delay != -1)
8786 stored_player[i].push_delay++;
8788 if (stored_player[i].drop_delay > 0)
8789 stored_player[i].drop_delay--;
8795 static unsigned long game_frame_delay = 0;
8796 unsigned long game_frame_delay_value;
8797 int magic_wall_x = 0, magic_wall_y = 0;
8798 int i, x, y, element, graphic;
8799 byte *recorded_player_action;
8800 byte summarized_player_action = 0;
8801 byte tape_action[MAX_PLAYERS];
8803 if (game_status != GAME_MODE_PLAYING)
8806 game_frame_delay_value =
8807 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
8809 if (tape.playing && tape.warp_forward && !tape.pausing)
8810 game_frame_delay_value = 0;
8812 /* ---------- main game synchronization point ---------- */
8814 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
8816 InitPlayfieldScanModeVars();
8818 if (ScreenMovPos == 0) /* screen currently aligned at tile position */
8820 struct PlayerInfo *player;
8821 int player_nr = game.centered_player_nr_next;
8823 if (game.centered_player_nr_next == -1)
8824 player_nr = local_player->index_nr;
8826 player = &stored_player[player_nr];
8828 if (!player->active)
8829 game.centered_player_nr_next = game.centered_player_nr;
8831 if (game.centered_player_nr != game.centered_player_nr_next)
8833 DrawRelocatePlayer(player, setup.quick_switch);
8835 game.centered_player_nr = game.centered_player_nr_next;
8839 #if USE_ONE_MORE_CHANGE_PER_FRAME
8840 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
8842 SCAN_PLAYFIELD(x, y)
8844 ChangeCount[x][y] = 0;
8845 ChangeEvent[x][y] = -1;
8850 if (network_playing && !network_player_action_received)
8852 /* try to get network player actions in time */
8854 #if defined(NETWORK_AVALIABLE)
8855 /* last chance to get network player actions without main loop delay */
8859 /* game was quit by network peer */
8860 if (game_status != GAME_MODE_PLAYING)
8863 if (!network_player_action_received)
8864 return; /* failed to get network player actions in time */
8870 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
8873 /* !!! CHECK THIS (tape.pausing is always FALSE here!) !!! */
8874 if (recorded_player_action == NULL && tape.pausing)
8878 for (i = 0; i < MAX_PLAYERS; i++)
8880 summarized_player_action |= stored_player[i].action;
8882 if (!network_playing)
8883 stored_player[i].effective_action = stored_player[i].action;
8886 #if defined(NETWORK_AVALIABLE)
8887 if (network_playing)
8888 SendToServer_MovePlayer(summarized_player_action);
8891 if (!options.network && !setup.team_mode)
8892 local_player->effective_action = summarized_player_action;
8894 if (recorded_player_action != NULL)
8895 for (i = 0; i < MAX_PLAYERS; i++)
8896 stored_player[i].effective_action = recorded_player_action[i];
8898 for (i = 0; i < MAX_PLAYERS; i++)
8900 tape_action[i] = stored_player[i].effective_action;
8902 if (tape.recording && tape_action[i] && !tape.player_participates[i])
8903 tape.player_participates[i] = TRUE; /* player just appeared from CE */
8906 /* only save actions from input devices, but not programmed actions */
8908 TapeRecordAction(tape_action);
8910 for (i = 0; i < MAX_PLAYERS; i++)
8912 int actual_player_action = stored_player[i].effective_action;
8915 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
8916 - rnd_equinox_tetrachloride 048
8917 - rnd_equinox_tetrachloride_ii 096
8918 - rnd_emanuel_schmieg 002
8919 - doctor_sloan_ww 001, 020
8921 if (stored_player[i].MovPos == 0)
8922 CheckGravityMovement(&stored_player[i]);
8925 /* overwrite programmed action with tape action */
8926 if (stored_player[i].programmed_action)
8927 actual_player_action = stored_player[i].programmed_action;
8930 PlayerActions(&stored_player[i], actual_player_action);
8932 tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
8934 if (tape.recording && tape_action[i] && !tape.player_participates[i])
8935 tape.player_participates[i] = TRUE; /* player just appeared from CE */
8938 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
8941 network_player_action_received = FALSE;
8943 ScrollScreen(NULL, SCROLL_GO_ON);
8945 /* for backwards compatibility, the following code emulates a fixed bug that
8946 occured when pushing elements (causing elements that just made their last
8947 pushing step to already (if possible) make their first falling step in the
8948 same game frame, which is bad); this code is also needed to use the famous
8949 "spring push bug" which is used in older levels and might be wanted to be
8950 used also in newer levels, but in this case the buggy pushing code is only
8951 affecting the "spring" element and no other elements */
8953 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
8955 for (i = 0; i < MAX_PLAYERS; i++)
8957 struct PlayerInfo *player = &stored_player[i];
8961 if (player->active && player->is_pushing && player->is_moving &&
8963 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
8964 Feld[x][y] == EL_SPRING))
8966 ContinueMoving(x, y);
8968 /* continue moving after pushing (this is actually a bug) */
8969 if (!IS_MOVING(x, y))
8978 SCAN_PLAYFIELD(x, y)
8980 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8983 ChangeCount[x][y] = 0;
8984 ChangeEvent[x][y] = -1;
8986 /* this must be handled before main playfield loop */
8987 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
8990 if (MovDelay[x][y] <= 0)
8994 #if USE_NEW_SNAP_DELAY
8995 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
8998 if (MovDelay[x][y] <= 0)
9001 DrawLevelField(x, y);
9003 TestIfElementTouchesCustomElement(x, y); /* for empty space */
9009 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
9011 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
9012 printf("GameActions(): This should never happen!\n");
9014 ChangePage[x][y] = -1;
9019 if (WasJustMoving[x][y] > 0)
9020 WasJustMoving[x][y]--;
9021 if (WasJustFalling[x][y] > 0)
9022 WasJustFalling[x][y]--;
9023 if (CheckCollision[x][y] > 0)
9024 CheckCollision[x][y]--;
9028 /* reset finished pushing action (not done in ContinueMoving() to allow
9029 continuous pushing animation for elements with zero push delay) */
9030 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
9032 ResetGfxAnimation(x, y);
9033 DrawLevelField(x, y);
9037 if (IS_BLOCKED(x, y))
9041 Blocked2Moving(x, y, &oldx, &oldy);
9042 if (!IS_MOVING(oldx, oldy))
9044 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
9045 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
9046 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
9047 printf("GameActions(): This should never happen!\n");
9054 SCAN_PLAYFIELD(x, y)
9056 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9059 element = Feld[x][y];
9060 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9063 printf("::: %d,%d\n", x, y);
9065 if (element == EL_ROCK)
9066 printf("::: Yo man! Rocks can fall!\n");
9069 if (graphic_info[graphic].anim_global_sync)
9070 GfxFrame[x][y] = FrameCounter;
9071 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
9073 int old_gfx_frame = GfxFrame[x][y];
9075 GfxFrame[x][y] = CustomValue[x][y];
9078 if (GfxFrame[x][y] != old_gfx_frame)
9080 DrawLevelGraphicAnimation(x, y, graphic);
9082 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
9084 int old_gfx_frame = GfxFrame[x][y];
9086 GfxFrame[x][y] = element_info[element].collect_score;
9089 if (GfxFrame[x][y] != old_gfx_frame)
9091 DrawLevelGraphicAnimation(x, y, graphic);
9094 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
9095 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
9096 ResetRandomAnimationValue(x, y);
9098 SetRandomAnimationValue(x, y);
9100 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
9102 if (IS_INACTIVE(element))
9104 if (IS_ANIMATED(graphic))
9105 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9110 /* this may take place after moving, so 'element' may have changed */
9111 if (IS_CHANGING(x, y) &&
9112 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
9114 int page = element_info[element].event_page_nr[CE_DELAY];
9116 HandleElementChange(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] : page);
9120 printf("::: ChangeDelay == %d\n", ChangeDelay[x][y]);
9124 if (element == EL_CUSTOM_255)
9125 printf("::: ChangeDelay == %d\n", ChangeDelay[x][y]);
9129 HandleElementChange(x, y, page);
9131 if (CAN_CHANGE(element))
9132 HandleElementChange(x, y, page);
9134 if (HAS_ACTION(element))
9135 ExecuteCustomElementAction(x, y, element, page);
9140 element = Feld[x][y];
9141 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9144 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
9148 element = Feld[x][y];
9149 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9151 if (IS_ANIMATED(graphic) &&
9154 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9156 if (IS_GEM(element) || element == EL_SP_INFOTRON)
9157 EdelsteinFunkeln(x, y);
9159 else if ((element == EL_ACID ||
9160 element == EL_EXIT_OPEN ||
9161 element == EL_SP_EXIT_OPEN ||
9162 element == EL_SP_TERMINAL ||
9163 element == EL_SP_TERMINAL_ACTIVE ||
9164 element == EL_EXTRA_TIME ||
9165 element == EL_SHIELD_NORMAL ||
9166 element == EL_SHIELD_DEADLY) &&
9167 IS_ANIMATED(graphic))
9168 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9169 else if (IS_MOVING(x, y))
9170 ContinueMoving(x, y);
9171 else if (IS_ACTIVE_BOMB(element))
9172 CheckDynamite(x, y);
9173 else if (element == EL_AMOEBA_GROWING)
9174 AmoebeWaechst(x, y);
9175 else if (element == EL_AMOEBA_SHRINKING)
9176 AmoebaDisappearing(x, y);
9178 #if !USE_NEW_AMOEBA_CODE
9179 else if (IS_AMOEBALIVE(element))
9180 AmoebeAbleger(x, y);
9183 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
9185 else if (element == EL_EXIT_CLOSED)
9187 else if (element == EL_SP_EXIT_CLOSED)
9189 else if (element == EL_EXPANDABLE_WALL_GROWING)
9191 else if (element == EL_EXPANDABLE_WALL ||
9192 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9193 element == EL_EXPANDABLE_WALL_VERTICAL ||
9194 element == EL_EXPANDABLE_WALL_ANY)
9196 else if (element == EL_FLAMES)
9197 CheckForDragon(x, y);
9198 else if (element == EL_EXPLOSION)
9199 ; /* drawing of correct explosion animation is handled separately */
9200 else if (element == EL_ELEMENT_SNAPPING ||
9201 element == EL_DIAGONAL_SHRINKING ||
9202 element == EL_DIAGONAL_GROWING)
9205 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
9207 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9210 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
9211 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9214 if (element == EL_CUSTOM_255 ||
9215 element == EL_CUSTOM_256)
9216 DrawLevelGraphicAnimation(x, y, graphic);
9219 if (IS_BELT_ACTIVE(element))
9220 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
9222 if (game.magic_wall_active)
9224 int jx = local_player->jx, jy = local_player->jy;
9226 /* play the element sound at the position nearest to the player */
9227 if ((element == EL_MAGIC_WALL_FULL ||
9228 element == EL_MAGIC_WALL_ACTIVE ||
9229 element == EL_MAGIC_WALL_EMPTYING ||
9230 element == EL_BD_MAGIC_WALL_FULL ||
9231 element == EL_BD_MAGIC_WALL_ACTIVE ||
9232 element == EL_BD_MAGIC_WALL_EMPTYING) &&
9233 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
9241 #if USE_NEW_AMOEBA_CODE
9242 /* new experimental amoeba growth stuff */
9243 if (!(FrameCounter % 8))
9245 static unsigned long random = 1684108901;
9247 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
9249 x = RND(lev_fieldx);
9250 y = RND(lev_fieldy);
9251 element = Feld[x][y];
9253 if (!IS_PLAYER(x,y) &&
9254 (element == EL_EMPTY ||
9255 CAN_GROW_INTO(element) ||
9256 element == EL_QUICKSAND_EMPTY ||
9257 element == EL_ACID_SPLASH_LEFT ||
9258 element == EL_ACID_SPLASH_RIGHT))
9260 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
9261 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
9262 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
9263 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
9264 Feld[x][y] = EL_AMOEBA_DROP;
9267 random = random * 129 + 1;
9273 if (game.explosions_delayed)
9276 game.explosions_delayed = FALSE;
9279 SCAN_PLAYFIELD(x, y)
9281 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9284 element = Feld[x][y];
9286 if (ExplodeField[x][y])
9287 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
9288 else if (element == EL_EXPLOSION)
9289 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
9291 ExplodeField[x][y] = EX_TYPE_NONE;
9294 game.explosions_delayed = TRUE;
9297 if (game.magic_wall_active)
9299 if (!(game.magic_wall_time_left % 4))
9301 int element = Feld[magic_wall_x][magic_wall_y];
9303 if (element == EL_BD_MAGIC_WALL_FULL ||
9304 element == EL_BD_MAGIC_WALL_ACTIVE ||
9305 element == EL_BD_MAGIC_WALL_EMPTYING)
9306 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
9308 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
9311 if (game.magic_wall_time_left > 0)
9313 game.magic_wall_time_left--;
9314 if (!game.magic_wall_time_left)
9317 SCAN_PLAYFIELD(x, y)
9319 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9322 element = Feld[x][y];
9324 if (element == EL_MAGIC_WALL_ACTIVE ||
9325 element == EL_MAGIC_WALL_FULL)
9327 Feld[x][y] = EL_MAGIC_WALL_DEAD;
9328 DrawLevelField(x, y);
9330 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
9331 element == EL_BD_MAGIC_WALL_FULL)
9333 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
9334 DrawLevelField(x, y);
9338 game.magic_wall_active = FALSE;
9343 if (game.light_time_left > 0)
9345 game.light_time_left--;
9347 if (game.light_time_left == 0)
9348 RedrawAllLightSwitchesAndInvisibleElements();
9351 if (game.timegate_time_left > 0)
9353 game.timegate_time_left--;
9355 if (game.timegate_time_left == 0)
9356 CloseAllOpenTimegates();
9359 if (game.lenses_time_left > 0)
9361 game.lenses_time_left--;
9363 if (game.lenses_time_left == 0)
9364 RedrawAllInvisibleElementsForLenses();
9367 if (game.magnify_time_left > 0)
9369 game.magnify_time_left--;
9371 if (game.magnify_time_left == 0)
9372 RedrawAllInvisibleElementsForMagnifier();
9375 for (i = 0; i < MAX_PLAYERS; i++)
9377 struct PlayerInfo *player = &stored_player[i];
9379 if (SHIELD_ON(player))
9381 if (player->shield_deadly_time_left)
9382 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
9383 else if (player->shield_normal_time_left)
9384 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
9388 if (TimeFrames >= FRAMES_PER_SECOND)
9393 for (i = 0; i < MAX_PLAYERS; i++)
9395 struct PlayerInfo *player = &stored_player[i];
9397 if (SHIELD_ON(player))
9399 player->shield_normal_time_left--;
9401 if (player->shield_deadly_time_left > 0)
9402 player->shield_deadly_time_left--;
9406 if (!level.use_step_counter)
9414 if (TimeLeft <= 10 && setup.time_limit)
9415 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
9417 DrawGameValue_Time(TimeLeft);
9419 if (!TimeLeft && setup.time_limit)
9420 for (i = 0; i < MAX_PLAYERS; i++)
9421 KillPlayer(&stored_player[i]);
9423 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
9424 DrawGameValue_Time(TimePlayed);
9427 if (tape.recording || tape.playing)
9428 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
9432 PlayAllPlayersSound();
9434 if (options.debug) /* calculate frames per second */
9436 static unsigned long fps_counter = 0;
9437 static int fps_frames = 0;
9438 unsigned long fps_delay_ms = Counter() - fps_counter;
9442 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
9444 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
9447 fps_counter = Counter();
9450 redraw_mask |= REDRAW_FPS;
9453 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
9455 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
9457 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
9459 local_player->show_envelope = 0;
9462 /* use random number generator in every frame to make it less predictable */
9463 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
9467 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
9469 int min_x = x, min_y = y, max_x = x, max_y = y;
9472 for (i = 0; i < MAX_PLAYERS; i++)
9474 int jx = stored_player[i].jx, jy = stored_player[i].jy;
9476 if (!stored_player[i].active || &stored_player[i] == player)
9479 min_x = MIN(min_x, jx);
9480 min_y = MIN(min_y, jy);
9481 max_x = MAX(max_x, jx);
9482 max_y = MAX(max_y, jy);
9485 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
9488 static boolean AllPlayersInVisibleScreen()
9492 for (i = 0; i < MAX_PLAYERS; i++)
9494 int jx = stored_player[i].jx, jy = stored_player[i].jy;
9496 if (!stored_player[i].active)
9499 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
9506 void ScrollLevel(int dx, int dy)
9508 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
9511 BlitBitmap(drawto_field, drawto_field,
9512 FX + TILEX * (dx == -1) - softscroll_offset,
9513 FY + TILEY * (dy == -1) - softscroll_offset,
9514 SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
9515 SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
9516 FX + TILEX * (dx == 1) - softscroll_offset,
9517 FY + TILEY * (dy == 1) - softscroll_offset);
9521 x = (dx == 1 ? BX1 : BX2);
9522 for (y = BY1; y <= BY2; y++)
9523 DrawScreenField(x, y);
9528 y = (dy == 1 ? BY1 : BY2);
9529 for (x = BX1; x <= BX2; x++)
9530 DrawScreenField(x, y);
9533 redraw_mask |= REDRAW_FIELD;
9536 static boolean canFallDown(struct PlayerInfo *player)
9538 int jx = player->jx, jy = player->jy;
9540 return (IN_LEV_FIELD(jx, jy + 1) &&
9541 (IS_FREE(jx, jy + 1) ||
9542 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
9543 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
9544 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
9547 static boolean canPassField(int x, int y, int move_dir)
9549 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9550 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9551 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
9554 int element = Feld[x][y];
9556 return (IS_PASSABLE_FROM(element, opposite_dir) &&
9557 !CAN_MOVE(element) &&
9558 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
9559 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
9560 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
9563 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
9565 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9566 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9567 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
9571 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9572 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
9573 (IS_DIGGABLE(Feld[newx][newy]) ||
9574 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9575 canPassField(newx, newy, move_dir)));
9578 static void CheckGravityMovement(struct PlayerInfo *player)
9580 if (game.gravity && !player->programmed_action)
9582 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
9583 int move_dir_vertical = player->effective_action & MV_VERTICAL;
9584 boolean player_is_snapping = player->effective_action & JOY_BUTTON_1;
9585 int jx = player->jx, jy = player->jy;
9586 boolean player_is_moving_to_valid_field =
9587 (!player_is_snapping &&
9588 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
9589 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
9590 boolean player_can_fall_down = canFallDown(player);
9592 if (player_can_fall_down &&
9593 !player_is_moving_to_valid_field)
9594 player->programmed_action = MV_DOWN;
9598 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
9600 return CheckGravityMovement(player);
9602 if (game.gravity && !player->programmed_action)
9604 int jx = player->jx, jy = player->jy;
9605 boolean field_under_player_is_free =
9606 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
9607 boolean player_is_standing_on_valid_field =
9608 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
9609 (IS_WALKABLE(Feld[jx][jy]) &&
9610 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
9612 if (field_under_player_is_free && !player_is_standing_on_valid_field)
9613 player->programmed_action = MV_DOWN;
9619 -----------------------------------------------------------------------------
9620 dx, dy: direction (non-diagonal) to try to move the player to
9621 real_dx, real_dy: direction as read from input device (can be diagonal)
9624 boolean MovePlayerOneStep(struct PlayerInfo *player,
9625 int dx, int dy, int real_dx, int real_dy)
9627 int jx = player->jx, jy = player->jy;
9628 int new_jx = jx + dx, new_jy = jy + dy;
9629 #if !USE_FIXED_DONT_RUN_INTO
9633 boolean player_can_move = !player->cannot_move;
9635 if (!player->active || (!dx && !dy))
9636 return MP_NO_ACTION;
9638 player->MovDir = (dx < 0 ? MV_LEFT :
9641 dy > 0 ? MV_DOWN : MV_NONE);
9643 if (!IN_LEV_FIELD(new_jx, new_jy))
9644 return MP_NO_ACTION;
9646 if (!player_can_move)
9649 if (player->MovPos == 0)
9651 player->is_moving = FALSE;
9652 player->is_digging = FALSE;
9653 player->is_collecting = FALSE;
9654 player->is_snapping = FALSE;
9655 player->is_pushing = FALSE;
9658 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
9659 SnapField(player, 0, 0);
9663 return MP_NO_ACTION;
9668 if (!options.network && game.centered_player_nr == -1 &&
9669 !AllPlayersInSight(player, new_jx, new_jy))
9670 return MP_NO_ACTION;
9672 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
9673 return MP_NO_ACTION;
9676 #if !USE_FIXED_DONT_RUN_INTO
9677 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
9679 /* (moved to DigField()) */
9680 if (player_can_move && DONT_RUN_INTO(element))
9682 if (element == EL_ACID && dx == 0 && dy == 1)
9684 SplashAcid(new_jx, new_jy);
9685 Feld[jx][jy] = EL_PLAYER_1;
9686 InitMovingField(jx, jy, MV_DOWN);
9687 Store[jx][jy] = EL_ACID;
9688 ContinueMoving(jx, jy);
9692 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
9698 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
9700 #if USE_FIXED_DONT_RUN_INTO
9701 if (can_move == MP_DONT_RUN_INTO)
9705 if (can_move != MP_MOVING)
9708 #if USE_FIXED_DONT_RUN_INTO
9711 /* check if DigField() has caused relocation of the player */
9712 if (player->jx != jx || player->jy != jy)
9713 return MP_NO_ACTION; /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
9715 StorePlayer[jx][jy] = 0;
9716 player->last_jx = jx;
9717 player->last_jy = jy;
9718 player->jx = new_jx;
9719 player->jy = new_jy;
9720 StorePlayer[new_jx][new_jy] = player->element_nr;
9722 if (player->move_delay_value_next != -1)
9724 player->move_delay_value = player->move_delay_value_next;
9725 player->move_delay_value_next = -1;
9729 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
9731 player->step_counter++;
9733 PlayerVisit[jx][jy] = FrameCounter;
9735 ScrollPlayer(player, SCROLL_INIT);
9740 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
9742 int jx = player->jx, jy = player->jy;
9743 int old_jx = jx, old_jy = jy;
9744 int moved = MP_NO_ACTION;
9746 if (!player->active)
9751 if (player->MovPos == 0)
9753 player->is_moving = FALSE;
9754 player->is_digging = FALSE;
9755 player->is_collecting = FALSE;
9756 player->is_snapping = FALSE;
9757 player->is_pushing = FALSE;
9763 if (player->move_delay > 0)
9766 player->move_delay = -1; /* set to "uninitialized" value */
9768 /* store if player is automatically moved to next field */
9769 player->is_auto_moving = (player->programmed_action != MV_NONE);
9771 /* remove the last programmed player action */
9772 player->programmed_action = 0;
9776 /* should only happen if pre-1.2 tape recordings are played */
9777 /* this is only for backward compatibility */
9779 int original_move_delay_value = player->move_delay_value;
9782 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
9786 /* scroll remaining steps with finest movement resolution */
9787 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
9789 while (player->MovPos)
9791 ScrollPlayer(player, SCROLL_GO_ON);
9792 ScrollScreen(NULL, SCROLL_GO_ON);
9794 AdvanceFrameAndPlayerCounters(player->index_nr);
9800 player->move_delay_value = original_move_delay_value;
9803 if (player->last_move_dir & MV_HORIZONTAL)
9805 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
9806 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
9810 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
9811 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
9818 if (moved & MP_MOVING && !ScreenMovPos &&
9819 (player->index_nr == game.centered_player_nr ||
9820 game.centered_player_nr == -1))
9822 if (moved & MP_MOVING && !ScreenMovPos &&
9823 (player == local_player || !options.network))
9826 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
9827 int offset = (setup.scroll_delay ? 3 : 0);
9829 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
9831 /* actual player has left the screen -- scroll in that direction */
9832 if (jx != old_jx) /* player has moved horizontally */
9833 scroll_x += (jx - old_jx);
9834 else /* player has moved vertically */
9835 scroll_y += (jy - old_jy);
9839 if (jx != old_jx) /* player has moved horizontally */
9841 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
9842 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
9843 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
9845 /* don't scroll over playfield boundaries */
9846 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
9847 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
9849 /* don't scroll more than one field at a time */
9850 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
9852 /* don't scroll against the player's moving direction */
9853 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
9854 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
9855 scroll_x = old_scroll_x;
9857 else /* player has moved vertically */
9859 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
9860 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
9861 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
9863 /* don't scroll over playfield boundaries */
9864 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
9865 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
9867 /* don't scroll more than one field at a time */
9868 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
9870 /* don't scroll against the player's moving direction */
9871 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
9872 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
9873 scroll_y = old_scroll_y;
9877 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
9880 if (!options.network && game.centered_player_nr == -1 &&
9881 !AllPlayersInVisibleScreen())
9883 scroll_x = old_scroll_x;
9884 scroll_y = old_scroll_y;
9888 if (!options.network && !AllPlayersInVisibleScreen())
9890 scroll_x = old_scroll_x;
9891 scroll_y = old_scroll_y;
9896 ScrollScreen(player, SCROLL_INIT);
9897 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
9902 player->StepFrame = 0;
9904 if (moved & MP_MOVING)
9906 if (old_jx != jx && old_jy == jy)
9907 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
9908 else if (old_jx == jx && old_jy != jy)
9909 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
9911 DrawLevelField(jx, jy); /* for "crumbled sand" */
9913 player->last_move_dir = player->MovDir;
9914 player->is_moving = TRUE;
9915 player->is_snapping = FALSE;
9916 player->is_switching = FALSE;
9917 player->is_dropping = FALSE;
9921 CheckGravityMovementWhenNotMoving(player);
9923 player->is_moving = FALSE;
9925 /* at this point, the player is allowed to move, but cannot move right now
9926 (e.g. because of something blocking the way) -- ensure that the player
9927 is also allowed to move in the next frame (in old versions before 3.1.1,
9928 the player was forced to wait again for eight frames before next try) */
9930 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
9931 player->move_delay = 0; /* allow direct movement in the next frame */
9934 if (player->move_delay == -1) /* not yet initialized by DigField() */
9935 player->move_delay = player->move_delay_value;
9937 if (game.engine_version < VERSION_IDENT(3,0,7,0))
9939 TestIfPlayerTouchesBadThing(jx, jy);
9940 TestIfPlayerTouchesCustomElement(jx, jy);
9943 if (!player->active)
9944 RemovePlayer(player);
9949 void ScrollPlayer(struct PlayerInfo *player, int mode)
9951 int jx = player->jx, jy = player->jy;
9952 int last_jx = player->last_jx, last_jy = player->last_jy;
9953 int move_stepsize = TILEX / player->move_delay_value;
9955 #if USE_NEW_PLAYER_SPEED
9956 if (!player->active)
9959 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
9962 if (!player->active || player->MovPos == 0)
9966 if (mode == SCROLL_INIT)
9968 player->actual_frame_counter = FrameCounter;
9969 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
9971 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
9972 Feld[last_jx][last_jy] == EL_EMPTY)
9974 int last_field_block_delay = 0; /* start with no blocking at all */
9975 int block_delay_adjustment = player->block_delay_adjustment;
9977 /* if player blocks last field, add delay for exactly one move */
9978 if (player->block_last_field)
9980 last_field_block_delay += player->move_delay_value;
9982 /* when blocking enabled, prevent moving up despite gravity */
9983 if (game.gravity && player->MovDir == MV_UP)
9984 block_delay_adjustment = -1;
9987 /* add block delay adjustment (also possible when not blocking) */
9988 last_field_block_delay += block_delay_adjustment;
9990 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
9991 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
9994 #if USE_NEW_PLAYER_SPEED
9995 if (player->MovPos != 0) /* player has not yet reached destination */
10001 else if (!FrameReached(&player->actual_frame_counter, 1))
10005 printf("::: player->MovPos: %d -> %d\n",
10007 player->MovPos + (player->MovPos > 0 ? -1 : 1) * move_stepsize);
10010 #if USE_NEW_PLAYER_SPEED
10011 if (player->MovPos != 0)
10013 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
10014 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10016 /* before DrawPlayer() to draw correct player graphic for this case */
10017 if (player->MovPos == 0)
10018 CheckGravityMovement(player);
10021 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
10022 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10024 /* before DrawPlayer() to draw correct player graphic for this case */
10025 if (player->MovPos == 0)
10026 CheckGravityMovement(player);
10029 if (player->MovPos == 0) /* player reached destination field */
10032 printf("::: player reached destination field\n");
10035 if (player->move_delay_reset_counter > 0)
10037 player->move_delay_reset_counter--;
10039 if (player->move_delay_reset_counter == 0)
10041 /* continue with normal speed after quickly moving through gate */
10042 HALVE_PLAYER_SPEED(player);
10044 /* be able to make the next move without delay */
10045 player->move_delay = 0;
10049 player->last_jx = jx;
10050 player->last_jy = jy;
10052 if (Feld[jx][jy] == EL_EXIT_OPEN ||
10053 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
10054 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
10056 DrawPlayer(player); /* needed here only to cleanup last field */
10057 RemovePlayer(player);
10059 if (local_player->friends_still_needed == 0 ||
10060 IS_SP_ELEMENT(Feld[jx][jy]))
10061 player->LevelSolved = player->GameOver = TRUE;
10064 /* this breaks one level: "machine", level 000 */
10066 int move_direction = player->MovDir;
10067 int enter_side = MV_DIR_OPPOSITE(move_direction);
10068 int leave_side = move_direction;
10069 int old_jx = last_jx;
10070 int old_jy = last_jy;
10071 int old_element = Feld[old_jx][old_jy];
10072 int new_element = Feld[jx][jy];
10074 if (IS_CUSTOM_ELEMENT(old_element))
10075 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
10077 player->index_bit, leave_side);
10079 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
10080 CE_PLAYER_LEAVES_X,
10081 player->index_bit, leave_side);
10083 if (IS_CUSTOM_ELEMENT(new_element))
10084 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
10085 player->index_bit, enter_side);
10087 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
10088 CE_PLAYER_ENTERS_X,
10089 player->index_bit, enter_side);
10091 CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
10092 CE_MOVE_OF_X, move_direction);
10095 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10097 TestIfPlayerTouchesBadThing(jx, jy);
10098 TestIfPlayerTouchesCustomElement(jx, jy);
10100 /* needed because pushed element has not yet reached its destination,
10101 so it would trigger a change event at its previous field location */
10102 if (!player->is_pushing)
10103 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
10105 if (!player->active)
10106 RemovePlayer(player);
10109 if (level.use_step_counter)
10119 if (TimeLeft <= 10 && setup.time_limit)
10120 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
10122 DrawGameValue_Time(TimeLeft);
10124 if (!TimeLeft && setup.time_limit)
10125 for (i = 0; i < MAX_PLAYERS; i++)
10126 KillPlayer(&stored_player[i]);
10128 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
10129 DrawGameValue_Time(TimePlayed);
10132 if (tape.single_step && tape.recording && !tape.pausing &&
10133 !player->programmed_action)
10134 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10138 void ScrollScreen(struct PlayerInfo *player, int mode)
10140 static unsigned long screen_frame_counter = 0;
10142 if (mode == SCROLL_INIT)
10144 /* set scrolling step size according to actual player's moving speed */
10145 ScrollStepSize = TILEX / player->move_delay_value;
10147 screen_frame_counter = FrameCounter;
10148 ScreenMovDir = player->MovDir;
10149 ScreenMovPos = player->MovPos;
10150 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
10153 else if (!FrameReached(&screen_frame_counter, 1))
10158 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
10159 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
10160 redraw_mask |= REDRAW_FIELD;
10163 ScreenMovDir = MV_NONE;
10166 void TestIfPlayerTouchesCustomElement(int x, int y)
10168 static int xy[4][2] =
10175 static int trigger_sides[4][2] =
10177 /* center side border side */
10178 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
10179 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
10180 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
10181 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
10183 static int touch_dir[4] =
10185 MV_LEFT | MV_RIGHT,
10190 int center_element = Feld[x][y]; /* should always be non-moving! */
10193 for (i = 0; i < NUM_DIRECTIONS; i++)
10195 int xx = x + xy[i][0];
10196 int yy = y + xy[i][1];
10197 int center_side = trigger_sides[i][0];
10198 int border_side = trigger_sides[i][1];
10199 int border_element;
10201 if (!IN_LEV_FIELD(xx, yy))
10204 if (IS_PLAYER(x, y))
10206 struct PlayerInfo *player = PLAYERINFO(x, y);
10208 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10209 border_element = Feld[xx][yy]; /* may be moving! */
10210 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
10211 border_element = Feld[xx][yy];
10212 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
10213 border_element = MovingOrBlocked2Element(xx, yy);
10215 continue; /* center and border element do not touch */
10217 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
10218 player->index_bit, border_side);
10219 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
10220 CE_PLAYER_TOUCHES_X,
10221 player->index_bit, border_side);
10223 else if (IS_PLAYER(xx, yy))
10225 struct PlayerInfo *player = PLAYERINFO(xx, yy);
10227 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10229 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
10230 continue; /* center and border element do not touch */
10233 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
10234 player->index_bit, center_side);
10235 CheckTriggeredElementChangeByPlayer(x, y, center_element,
10236 CE_PLAYER_TOUCHES_X,
10237 player->index_bit, center_side);
10243 #if USE_ELEMENT_TOUCHING_BUGFIX
10245 void TestIfElementTouchesCustomElement(int x, int y)
10247 static int xy[4][2] =
10254 static int trigger_sides[4][2] =
10256 /* center side border side */
10257 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
10258 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
10259 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
10260 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
10262 static int touch_dir[4] =
10264 MV_LEFT | MV_RIGHT,
10269 boolean change_center_element = FALSE;
10270 int center_element = Feld[x][y]; /* should always be non-moving! */
10271 int border_element_old[NUM_DIRECTIONS];
10274 for (i = 0; i < NUM_DIRECTIONS; i++)
10276 int xx = x + xy[i][0];
10277 int yy = y + xy[i][1];
10278 int border_element;
10280 border_element_old[i] = -1;
10282 if (!IN_LEV_FIELD(xx, yy))
10285 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10286 border_element = Feld[xx][yy]; /* may be moving! */
10287 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
10288 border_element = Feld[xx][yy];
10289 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
10290 border_element = MovingOrBlocked2Element(xx, yy);
10292 continue; /* center and border element do not touch */
10294 border_element_old[i] = border_element;
10297 for (i = 0; i < NUM_DIRECTIONS; i++)
10299 int xx = x + xy[i][0];
10300 int yy = y + xy[i][1];
10301 int center_side = trigger_sides[i][0];
10302 int border_element = border_element_old[i];
10304 if (border_element == -1)
10307 /* check for change of border element */
10308 CheckElementChangeBySide(xx, yy, border_element, center_element,
10309 CE_TOUCHING_X, center_side);
10312 for (i = 0; i < NUM_DIRECTIONS; i++)
10314 int border_side = trigger_sides[i][1];
10315 int border_element = border_element_old[i];
10317 if (border_element == -1)
10320 /* check for change of center element (but change it only once) */
10321 if (!change_center_element)
10322 change_center_element =
10323 CheckElementChangeBySide(x, y, center_element, border_element,
10324 CE_TOUCHING_X, border_side);
10330 void TestIfElementTouchesCustomElement_OLD(int x, int y)
10332 static int xy[4][2] =
10339 static int trigger_sides[4][2] =
10341 /* center side border side */
10342 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
10343 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
10344 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
10345 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
10347 static int touch_dir[4] =
10349 MV_LEFT | MV_RIGHT,
10354 boolean change_center_element = FALSE;
10355 int center_element = Feld[x][y]; /* should always be non-moving! */
10358 for (i = 0; i < NUM_DIRECTIONS; i++)
10360 int xx = x + xy[i][0];
10361 int yy = y + xy[i][1];
10362 int center_side = trigger_sides[i][0];
10363 int border_side = trigger_sides[i][1];
10364 int border_element;
10366 if (!IN_LEV_FIELD(xx, yy))
10369 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10370 border_element = Feld[xx][yy]; /* may be moving! */
10371 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
10372 border_element = Feld[xx][yy];
10373 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
10374 border_element = MovingOrBlocked2Element(xx, yy);
10376 continue; /* center and border element do not touch */
10378 /* check for change of center element (but change it only once) */
10379 if (!change_center_element)
10380 change_center_element =
10381 CheckElementChangeBySide(x, y, center_element, border_element,
10382 CE_TOUCHING_X, border_side);
10384 /* check for change of border element */
10385 CheckElementChangeBySide(xx, yy, border_element, center_element,
10386 CE_TOUCHING_X, center_side);
10392 void TestIfElementHitsCustomElement(int x, int y, int direction)
10394 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
10395 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
10396 int hitx = x + dx, hity = y + dy;
10397 int hitting_element = Feld[x][y];
10398 int touched_element;
10400 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
10403 touched_element = (IN_LEV_FIELD(hitx, hity) ?
10404 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
10406 if (IN_LEV_FIELD(hitx, hity))
10408 int opposite_direction = MV_DIR_OPPOSITE(direction);
10409 int hitting_side = direction;
10410 int touched_side = opposite_direction;
10411 boolean object_hit = (!IS_MOVING(hitx, hity) ||
10412 MovDir[hitx][hity] != direction ||
10413 ABS(MovPos[hitx][hity]) <= TILEY / 2);
10419 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10420 CE_HITTING_X, touched_side);
10422 CheckElementChangeBySide(hitx, hity, touched_element,
10423 hitting_element, CE_HIT_BY_X, hitting_side);
10425 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10426 CE_HIT_BY_SOMETHING, opposite_direction);
10430 /* "hitting something" is also true when hitting the playfield border */
10431 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10432 CE_HITTING_SOMETHING, direction);
10436 void TestIfElementSmashesCustomElement(int x, int y, int direction)
10438 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
10439 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
10440 int hitx = x + dx, hity = y + dy;
10441 int hitting_element = Feld[x][y];
10442 int touched_element;
10444 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
10445 !IS_FREE(hitx, hity) &&
10446 (!IS_MOVING(hitx, hity) ||
10447 MovDir[hitx][hity] != direction ||
10448 ABS(MovPos[hitx][hity]) <= TILEY / 2));
10451 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
10455 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
10459 touched_element = (IN_LEV_FIELD(hitx, hity) ?
10460 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
10462 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10463 EP_CAN_SMASH_EVERYTHING, direction);
10465 if (IN_LEV_FIELD(hitx, hity))
10467 int opposite_direction = MV_DIR_OPPOSITE(direction);
10468 int hitting_side = direction;
10469 int touched_side = opposite_direction;
10471 int touched_element = MovingOrBlocked2Element(hitx, hity);
10474 boolean object_hit = (!IS_MOVING(hitx, hity) ||
10475 MovDir[hitx][hity] != direction ||
10476 ABS(MovPos[hitx][hity]) <= TILEY / 2);
10485 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10486 CE_SMASHED_BY_SOMETHING, opposite_direction);
10488 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10489 CE_OTHER_IS_SMASHING, touched_side);
10491 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10492 CE_OTHER_GETS_SMASHED, hitting_side);
10498 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
10500 int i, kill_x = -1, kill_y = -1;
10502 int bad_element = -1;
10503 static int test_xy[4][2] =
10510 static int test_dir[4] =
10518 for (i = 0; i < NUM_DIRECTIONS; i++)
10520 int test_x, test_y, test_move_dir, test_element;
10522 test_x = good_x + test_xy[i][0];
10523 test_y = good_y + test_xy[i][1];
10525 if (!IN_LEV_FIELD(test_x, test_y))
10529 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
10531 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
10533 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
10534 2nd case: DONT_TOUCH style bad thing does not move away from good thing
10536 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
10537 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
10541 bad_element = test_element;
10547 if (kill_x != -1 || kill_y != -1)
10549 if (IS_PLAYER(good_x, good_y))
10551 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
10553 if (player->shield_deadly_time_left > 0 &&
10554 !IS_INDESTRUCTIBLE(bad_element))
10555 Bang(kill_x, kill_y);
10556 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
10557 KillPlayer(player);
10560 Bang(good_x, good_y);
10564 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
10566 int i, kill_x = -1, kill_y = -1;
10567 int bad_element = Feld[bad_x][bad_y];
10568 static int test_xy[4][2] =
10575 static int touch_dir[4] =
10577 MV_LEFT | MV_RIGHT,
10582 static int test_dir[4] =
10590 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
10593 for (i = 0; i < NUM_DIRECTIONS; i++)
10595 int test_x, test_y, test_move_dir, test_element;
10597 test_x = bad_x + test_xy[i][0];
10598 test_y = bad_y + test_xy[i][1];
10599 if (!IN_LEV_FIELD(test_x, test_y))
10603 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
10605 test_element = Feld[test_x][test_y];
10607 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
10608 2nd case: DONT_TOUCH style bad thing does not move away from good thing
10610 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
10611 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
10613 /* good thing is player or penguin that does not move away */
10614 if (IS_PLAYER(test_x, test_y))
10616 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
10618 if (bad_element == EL_ROBOT && player->is_moving)
10619 continue; /* robot does not kill player if he is moving */
10621 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10623 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
10624 continue; /* center and border element do not touch */
10631 else if (test_element == EL_PENGUIN)
10640 if (kill_x != -1 || kill_y != -1)
10642 if (IS_PLAYER(kill_x, kill_y))
10644 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
10646 if (player->shield_deadly_time_left > 0 &&
10647 !IS_INDESTRUCTIBLE(bad_element))
10648 Bang(bad_x, bad_y);
10649 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
10650 KillPlayer(player);
10653 Bang(kill_x, kill_y);
10657 void TestIfPlayerTouchesBadThing(int x, int y)
10659 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
10662 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
10664 TestIfGoodThingHitsBadThing(x, y, move_dir);
10667 void TestIfBadThingTouchesPlayer(int x, int y)
10669 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
10672 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
10674 TestIfBadThingHitsGoodThing(x, y, move_dir);
10677 void TestIfFriendTouchesBadThing(int x, int y)
10679 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
10682 void TestIfBadThingTouchesFriend(int x, int y)
10684 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
10687 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
10689 int i, kill_x = bad_x, kill_y = bad_y;
10690 static int xy[4][2] =
10698 for (i = 0; i < NUM_DIRECTIONS; i++)
10702 x = bad_x + xy[i][0];
10703 y = bad_y + xy[i][1];
10704 if (!IN_LEV_FIELD(x, y))
10707 element = Feld[x][y];
10708 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
10709 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
10717 if (kill_x != bad_x || kill_y != bad_y)
10718 Bang(bad_x, bad_y);
10721 void KillPlayer(struct PlayerInfo *player)
10723 int jx = player->jx, jy = player->jy;
10725 if (!player->active)
10728 /* remove accessible field at the player's position */
10729 Feld[jx][jy] = EL_EMPTY;
10731 /* deactivate shield (else Bang()/Explode() would not work right) */
10732 player->shield_normal_time_left = 0;
10733 player->shield_deadly_time_left = 0;
10736 BuryPlayer(player);
10739 static void KillPlayerUnlessEnemyProtected(int x, int y)
10741 if (!PLAYER_ENEMY_PROTECTED(x, y))
10742 KillPlayer(PLAYERINFO(x, y));
10745 static void KillPlayerUnlessExplosionProtected(int x, int y)
10747 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
10748 KillPlayer(PLAYERINFO(x, y));
10751 void BuryPlayer(struct PlayerInfo *player)
10753 int jx = player->jx, jy = player->jy;
10755 if (!player->active)
10758 PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
10759 PlayLevelSound(jx, jy, SND_GAME_LOSING);
10761 player->GameOver = TRUE;
10762 RemovePlayer(player);
10765 void RemovePlayer(struct PlayerInfo *player)
10767 int jx = player->jx, jy = player->jy;
10768 int i, found = FALSE;
10770 player->present = FALSE;
10771 player->active = FALSE;
10773 if (!ExplodeField[jx][jy])
10774 StorePlayer[jx][jy] = 0;
10776 if (player->is_moving)
10777 DrawLevelField(player->last_jx, player->last_jy);
10779 for (i = 0; i < MAX_PLAYERS; i++)
10780 if (stored_player[i].active)
10784 AllPlayersGone = TRUE;
10790 #if USE_NEW_SNAP_DELAY
10791 static void setFieldForSnapping(int x, int y, int element, int direction)
10793 struct ElementInfo *ei = &element_info[element];
10794 int direction_bit = MV_DIR_TO_BIT(direction);
10795 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
10796 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
10797 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
10799 Feld[x][y] = EL_ELEMENT_SNAPPING;
10800 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
10802 ResetGfxAnimation(x, y);
10804 GfxElement[x][y] = element;
10805 GfxAction[x][y] = action;
10806 GfxDir[x][y] = direction;
10807 GfxFrame[x][y] = -1;
10812 =============================================================================
10813 checkDiagonalPushing()
10814 -----------------------------------------------------------------------------
10815 check if diagonal input device direction results in pushing of object
10816 (by checking if the alternative direction is walkable, diggable, ...)
10817 =============================================================================
10820 static boolean checkDiagonalPushing(struct PlayerInfo *player,
10821 int x, int y, int real_dx, int real_dy)
10823 int jx, jy, dx, dy, xx, yy;
10825 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
10828 /* diagonal direction: check alternative direction */
10833 xx = jx + (dx == 0 ? real_dx : 0);
10834 yy = jy + (dy == 0 ? real_dy : 0);
10836 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
10840 =============================================================================
10842 -----------------------------------------------------------------------------
10843 x, y: field next to player (non-diagonal) to try to dig to
10844 real_dx, real_dy: direction as read from input device (can be diagonal)
10845 =============================================================================
10848 int DigField(struct PlayerInfo *player,
10849 int oldx, int oldy, int x, int y,
10850 int real_dx, int real_dy, int mode)
10852 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
10853 boolean player_was_pushing = player->is_pushing;
10854 boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
10855 boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
10856 int jx = oldx, jy = oldy;
10857 int dx = x - jx, dy = y - jy;
10858 int nextx = x + dx, nexty = y + dy;
10859 int move_direction = (dx == -1 ? MV_LEFT :
10860 dx == +1 ? MV_RIGHT :
10862 dy == +1 ? MV_DOWN : MV_NONE);
10863 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
10864 int dig_side = MV_DIR_OPPOSITE(move_direction);
10865 int old_element = Feld[jx][jy];
10866 #if USE_FIXED_DONT_RUN_INTO
10867 int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
10873 if (is_player) /* function can also be called by EL_PENGUIN */
10875 if (player->MovPos == 0)
10877 player->is_digging = FALSE;
10878 player->is_collecting = FALSE;
10881 if (player->MovPos == 0) /* last pushing move finished */
10882 player->is_pushing = FALSE;
10884 if (mode == DF_NO_PUSH) /* player just stopped pushing */
10886 player->is_switching = FALSE;
10887 player->push_delay = -1;
10889 return MP_NO_ACTION;
10893 #if !USE_FIXED_DONT_RUN_INTO
10894 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
10895 return MP_NO_ACTION;
10898 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
10899 old_element = Back[jx][jy];
10901 /* in case of element dropped at player position, check background */
10902 else if (Back[jx][jy] != EL_EMPTY &&
10903 game.engine_version >= VERSION_IDENT(2,2,0,0))
10904 old_element = Back[jx][jy];
10907 #if USE_FIXED_DONT_RUN_INTO
10908 if (player_can_move && DONT_RUN_INTO(element))
10910 if (element == EL_ACID && dx == 0 && dy == 1)
10913 Feld[jx][jy] = EL_PLAYER_1;
10914 InitMovingField(jx, jy, MV_DOWN);
10915 Store[jx][jy] = EL_ACID;
10916 ContinueMoving(jx, jy);
10917 BuryPlayer(player);
10920 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
10922 return MP_DONT_RUN_INTO;
10928 #if USE_FIXED_DONT_RUN_INTO
10929 if (player_can_move && DONT_RUN_INTO(element))
10931 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
10933 return MP_DONT_RUN_INTO;
10938 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
10939 return MP_NO_ACTION; /* field has no opening in this direction */
10941 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
10942 return MP_NO_ACTION; /* field has no opening in this direction */
10945 #if USE_FIXED_DONT_RUN_INTO
10946 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
10949 Feld[jx][jy] = EL_PLAYER_1;
10950 InitMovingField(jx, jy, MV_DOWN);
10951 Store[jx][jy] = EL_ACID;
10952 ContinueMoving(jx, jy);
10953 BuryPlayer(player);
10955 return MP_DONT_RUN_INTO;
10961 #if USE_FIXED_DONT_RUN_INTO
10962 if (player_can_move && DONT_RUN_INTO(element))
10964 if (element == EL_ACID && dx == 0 && dy == 1)
10967 Feld[jx][jy] = EL_PLAYER_1;
10968 InitMovingField(jx, jy, MV_DOWN);
10969 Store[jx][jy] = EL_ACID;
10970 ContinueMoving(jx, jy);
10971 BuryPlayer(player);
10974 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
10976 return MP_DONT_RUN_INTO;
10981 #if USE_FIXED_DONT_RUN_INTO
10982 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
10983 return MP_NO_ACTION;
10986 #if !USE_FIXED_DONT_RUN_INTO
10987 element = Feld[x][y];
10990 collect_count = element_info[element].collect_count_initial;
10992 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
10993 return MP_NO_ACTION;
10995 if (game.engine_version < VERSION_IDENT(2,2,0,0))
10996 player_can_move = player_can_move_or_snap;
10998 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
10999 game.engine_version >= VERSION_IDENT(2,2,0,0))
11001 CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
11002 player->index_bit, dig_side);
11003 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
11004 player->index_bit, dig_side);
11006 if (Feld[x][y] != element) /* field changed by snapping */
11009 return MP_NO_ACTION;
11012 if (game.gravity && is_player && !player->is_auto_moving &&
11013 canFallDown(player) && move_direction != MV_DOWN &&
11014 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
11015 return MP_NO_ACTION; /* player cannot walk here due to gravity */
11017 if (player_can_move &&
11018 IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
11020 int sound_element = SND_ELEMENT(element);
11021 int sound_action = ACTION_WALKING;
11023 if (IS_RND_GATE(element))
11025 if (!player->key[RND_GATE_NR(element)])
11026 return MP_NO_ACTION;
11028 else if (IS_RND_GATE_GRAY(element))
11030 if (!player->key[RND_GATE_GRAY_NR(element)])
11031 return MP_NO_ACTION;
11033 else if (IS_RND_GATE_GRAY_ACTIVE(element))
11035 if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
11036 return MP_NO_ACTION;
11038 else if (element == EL_EXIT_OPEN ||
11039 element == EL_SP_EXIT_OPEN ||
11040 element == EL_SP_EXIT_OPENING)
11042 sound_action = ACTION_PASSING; /* player is passing exit */
11044 else if (element == EL_EMPTY)
11046 sound_action = ACTION_MOVING; /* nothing to walk on */
11049 /* play sound from background or player, whatever is available */
11050 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
11051 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
11053 PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
11055 else if (player_can_move &&
11056 IS_PASSABLE(element) && canPassField(x, y, move_direction))
11058 if (!ACCESS_FROM(element, opposite_direction))
11059 return MP_NO_ACTION; /* field not accessible from this direction */
11061 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
11062 return MP_NO_ACTION;
11064 if (IS_EM_GATE(element))
11066 if (!player->key[EM_GATE_NR(element)])
11067 return MP_NO_ACTION;
11069 else if (IS_EM_GATE_GRAY(element))
11071 if (!player->key[EM_GATE_GRAY_NR(element)])
11072 return MP_NO_ACTION;
11074 else if (IS_EM_GATE_GRAY_ACTIVE(element))
11076 if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
11077 return MP_NO_ACTION;
11079 else if (IS_SP_PORT(element))
11081 if (element == EL_SP_GRAVITY_PORT_LEFT ||
11082 element == EL_SP_GRAVITY_PORT_RIGHT ||
11083 element == EL_SP_GRAVITY_PORT_UP ||
11084 element == EL_SP_GRAVITY_PORT_DOWN)
11085 game.gravity = !game.gravity;
11086 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
11087 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
11088 element == EL_SP_GRAVITY_ON_PORT_UP ||
11089 element == EL_SP_GRAVITY_ON_PORT_DOWN)
11090 game.gravity = TRUE;
11091 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
11092 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
11093 element == EL_SP_GRAVITY_OFF_PORT_UP ||
11094 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
11095 game.gravity = FALSE;
11098 /* automatically move to the next field with double speed */
11099 player->programmed_action = move_direction;
11101 if (player->move_delay_reset_counter == 0)
11103 player->move_delay_reset_counter = 2; /* two double speed steps */
11105 DOUBLE_PLAYER_SPEED(player);
11108 PlayLevelSoundAction(x, y, ACTION_PASSING);
11110 else if (player_can_move_or_snap && IS_DIGGABLE(element))
11114 if (mode != DF_SNAP)
11116 GfxElement[x][y] = GFX_ELEMENT(element);
11117 player->is_digging = TRUE;
11120 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
11122 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
11123 player->index_bit, dig_side);
11125 if (mode == DF_SNAP)
11127 #if USE_NEW_SNAP_DELAY
11128 if (level.block_snap_field)
11129 setFieldForSnapping(x, y, element, move_direction);
11131 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11133 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11136 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
11137 player->index_bit, dig_side);
11140 else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
11144 if (is_player && mode != DF_SNAP)
11146 GfxElement[x][y] = element;
11147 player->is_collecting = TRUE;
11150 if (element == EL_SPEED_PILL)
11152 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
11154 else if (element == EL_EXTRA_TIME && level.time > 0)
11156 TimeLeft += level.extra_time;
11157 DrawGameValue_Time(TimeLeft);
11159 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
11161 player->shield_normal_time_left += level.shield_normal_time;
11162 if (element == EL_SHIELD_DEADLY)
11163 player->shield_deadly_time_left += level.shield_deadly_time;
11165 else if (element == EL_DYNAMITE ||
11166 element == EL_EM_DYNAMITE ||
11167 element == EL_SP_DISK_RED)
11169 if (player->inventory_size < MAX_INVENTORY_SIZE)
11170 player->inventory_element[player->inventory_size++] = element;
11172 DrawGameValue_Dynamite(local_player->inventory_size);
11174 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
11176 player->dynabomb_count++;
11177 player->dynabombs_left++;
11179 else if (element == EL_DYNABOMB_INCREASE_SIZE)
11181 player->dynabomb_size++;
11183 else if (element == EL_DYNABOMB_INCREASE_POWER)
11185 player->dynabomb_xl = TRUE;
11187 else if (IS_KEY(element))
11189 player->key[KEY_NR(element)] = TRUE;
11191 DrawGameValue_Keys(player->key);
11193 redraw_mask |= REDRAW_DOOR_1;
11195 else if (IS_ENVELOPE(element))
11197 player->show_envelope = element;
11199 else if (element == EL_EMC_LENSES)
11201 game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
11203 RedrawAllInvisibleElementsForLenses();
11205 else if (element == EL_EMC_MAGNIFIER)
11207 game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
11209 RedrawAllInvisibleElementsForMagnifier();
11211 else if (IS_DROPPABLE(element) ||
11212 IS_THROWABLE(element)) /* can be collected and dropped */
11216 if (collect_count == 0)
11217 player->inventory_infinite_element = element;
11219 for (i = 0; i < collect_count; i++)
11220 if (player->inventory_size < MAX_INVENTORY_SIZE)
11221 player->inventory_element[player->inventory_size++] = element;
11223 DrawGameValue_Dynamite(local_player->inventory_size);
11225 else if (collect_count > 0)
11227 local_player->gems_still_needed -= collect_count;
11228 if (local_player->gems_still_needed < 0)
11229 local_player->gems_still_needed = 0;
11231 DrawGameValue_Emeralds(local_player->gems_still_needed);
11234 RaiseScoreElement(element);
11235 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
11238 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
11239 player->index_bit, dig_side);
11241 if (mode == DF_SNAP)
11243 #if USE_NEW_SNAP_DELAY
11244 if (level.block_snap_field)
11245 setFieldForSnapping(x, y, element, move_direction);
11247 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11249 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11252 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
11253 player->index_bit, dig_side);
11256 else if (player_can_move_or_snap && IS_PUSHABLE(element))
11258 if (mode == DF_SNAP && element != EL_BD_ROCK)
11259 return MP_NO_ACTION;
11261 if (CAN_FALL(element) && dy)
11262 return MP_NO_ACTION;
11264 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
11265 !(element == EL_SPRING && level.use_spring_bug))
11266 return MP_NO_ACTION;
11268 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
11269 ((move_direction & MV_VERTICAL &&
11270 ((element_info[element].move_pattern & MV_LEFT &&
11271 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
11272 (element_info[element].move_pattern & MV_RIGHT &&
11273 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
11274 (move_direction & MV_HORIZONTAL &&
11275 ((element_info[element].move_pattern & MV_UP &&
11276 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
11277 (element_info[element].move_pattern & MV_DOWN &&
11278 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
11279 return MP_NO_ACTION;
11281 /* do not push elements already moving away faster than player */
11282 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
11283 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
11284 return MP_NO_ACTION;
11286 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
11288 if (player->push_delay_value == -1 || !player_was_pushing)
11289 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11291 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11293 if (player->push_delay_value == -1)
11294 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11296 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
11298 if (!player->is_pushing)
11299 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11302 player->is_pushing = TRUE;
11304 if (!(IN_LEV_FIELD(nextx, nexty) &&
11305 (IS_FREE(nextx, nexty) ||
11306 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
11307 IS_SB_ELEMENT(element)))))
11308 return MP_NO_ACTION;
11310 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
11311 return MP_NO_ACTION;
11313 if (player->push_delay == -1) /* new pushing; restart delay */
11314 player->push_delay = 0;
11316 if (player->push_delay < player->push_delay_value &&
11317 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
11318 element != EL_SPRING && element != EL_BALLOON)
11320 /* make sure that there is no move delay before next try to push */
11321 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11322 player->move_delay = 0;
11324 return MP_NO_ACTION;
11327 if (IS_SB_ELEMENT(element))
11329 if (element == EL_SOKOBAN_FIELD_FULL)
11331 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
11332 local_player->sokobanfields_still_needed++;
11335 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
11337 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
11338 local_player->sokobanfields_still_needed--;
11341 Feld[x][y] = EL_SOKOBAN_OBJECT;
11343 if (Back[x][y] == Back[nextx][nexty])
11344 PlayLevelSoundAction(x, y, ACTION_PUSHING);
11345 else if (Back[x][y] != 0)
11346 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
11349 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
11352 if (local_player->sokobanfields_still_needed == 0 &&
11353 game.emulation == EMU_SOKOBAN)
11355 player->LevelSolved = player->GameOver = TRUE;
11356 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
11360 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
11362 InitMovingField(x, y, move_direction);
11363 GfxAction[x][y] = ACTION_PUSHING;
11365 if (mode == DF_SNAP)
11366 ContinueMoving(x, y);
11368 MovPos[x][y] = (dx != 0 ? dx : dy);
11370 Pushed[x][y] = TRUE;
11371 Pushed[nextx][nexty] = TRUE;
11373 if (game.engine_version < VERSION_IDENT(2,2,0,7))
11374 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11376 player->push_delay_value = -1; /* get new value later */
11378 /* check for element change _after_ element has been pushed */
11379 if (game.use_change_when_pushing_bug)
11381 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
11382 player->index_bit, dig_side);
11383 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
11384 player->index_bit, dig_side);
11387 else if (IS_SWITCHABLE(element))
11389 if (PLAYER_SWITCHING(player, x, y))
11391 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
11392 player->index_bit, dig_side);
11397 player->is_switching = TRUE;
11398 player->switch_x = x;
11399 player->switch_y = y;
11401 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
11403 if (element == EL_ROBOT_WHEEL)
11405 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
11409 DrawLevelField(x, y);
11411 else if (element == EL_SP_TERMINAL)
11416 SCAN_PLAYFIELD(xx, yy)
11418 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
11421 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
11423 else if (Feld[xx][yy] == EL_SP_TERMINAL)
11424 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
11427 else if (IS_BELT_SWITCH(element))
11429 ToggleBeltSwitch(x, y);
11431 else if (element == EL_SWITCHGATE_SWITCH_UP ||
11432 element == EL_SWITCHGATE_SWITCH_DOWN)
11434 ToggleSwitchgateSwitch(x, y);
11436 else if (element == EL_LIGHT_SWITCH ||
11437 element == EL_LIGHT_SWITCH_ACTIVE)
11439 ToggleLightSwitch(x, y);
11441 else if (element == EL_TIMEGATE_SWITCH)
11443 ActivateTimegateSwitch(x, y);
11445 else if (element == EL_BALLOON_SWITCH_LEFT ||
11446 element == EL_BALLOON_SWITCH_RIGHT ||
11447 element == EL_BALLOON_SWITCH_UP ||
11448 element == EL_BALLOON_SWITCH_DOWN ||
11449 element == EL_BALLOON_SWITCH_NONE ||
11450 element == EL_BALLOON_SWITCH_ANY)
11452 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
11453 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
11454 element == EL_BALLOON_SWITCH_UP ? MV_UP :
11455 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
11456 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
11459 else if (element == EL_LAMP)
11461 Feld[x][y] = EL_LAMP_ACTIVE;
11462 local_player->lights_still_needed--;
11464 ResetGfxAnimation(x, y);
11465 DrawLevelField(x, y);
11467 else if (element == EL_TIME_ORB_FULL)
11469 Feld[x][y] = EL_TIME_ORB_EMPTY;
11471 if (level.time > 0 || level.use_time_orb_bug)
11473 TimeLeft += level.time_orb_time;
11474 DrawGameValue_Time(TimeLeft);
11477 ResetGfxAnimation(x, y);
11478 DrawLevelField(x, y);
11480 else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
11481 element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
11485 game.ball_state = !game.ball_state;
11488 SCAN_PLAYFIELD(xx, yy)
11490 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
11493 int e = Feld[xx][yy];
11495 if (game.ball_state)
11497 if (e == EL_EMC_MAGIC_BALL)
11498 CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
11499 else if (e == EL_EMC_MAGIC_BALL_SWITCH)
11500 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
11504 if (e == EL_EMC_MAGIC_BALL_ACTIVE)
11505 CreateField(xx, yy, EL_EMC_MAGIC_BALL);
11506 else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
11507 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
11512 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
11513 player->index_bit, dig_side);
11515 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
11516 player->index_bit, dig_side);
11518 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
11519 player->index_bit, dig_side);
11525 if (!PLAYER_SWITCHING(player, x, y))
11527 player->is_switching = TRUE;
11528 player->switch_x = x;
11529 player->switch_y = y;
11531 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
11532 player->index_bit, dig_side);
11533 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
11534 player->index_bit, dig_side);
11536 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
11537 player->index_bit, dig_side);
11538 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
11539 player->index_bit, dig_side);
11542 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
11543 player->index_bit, dig_side);
11544 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
11545 player->index_bit, dig_side);
11547 return MP_NO_ACTION;
11550 player->push_delay = -1;
11552 if (is_player) /* function can also be called by EL_PENGUIN */
11554 if (Feld[x][y] != element) /* really digged/collected something */
11555 player->is_collecting = !player->is_digging;
11561 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
11563 int jx = player->jx, jy = player->jy;
11564 int x = jx + dx, y = jy + dy;
11565 int snap_direction = (dx == -1 ? MV_LEFT :
11566 dx == +1 ? MV_RIGHT :
11568 dy == +1 ? MV_DOWN : MV_NONE);
11570 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
11573 if (!player->active || !IN_LEV_FIELD(x, y))
11581 if (player->MovPos == 0)
11582 player->is_pushing = FALSE;
11584 player->is_snapping = FALSE;
11586 if (player->MovPos == 0)
11588 player->is_moving = FALSE;
11589 player->is_digging = FALSE;
11590 player->is_collecting = FALSE;
11596 if (player->is_snapping)
11599 player->MovDir = snap_direction;
11601 if (player->MovPos == 0)
11603 player->is_moving = FALSE;
11604 player->is_digging = FALSE;
11605 player->is_collecting = FALSE;
11608 player->is_dropping = FALSE;
11610 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
11613 player->is_snapping = TRUE;
11615 if (player->MovPos == 0)
11617 player->is_moving = FALSE;
11618 player->is_digging = FALSE;
11619 player->is_collecting = FALSE;
11622 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
11623 DrawLevelField(player->last_jx, player->last_jy);
11625 DrawLevelField(x, y);
11630 boolean DropElement(struct PlayerInfo *player)
11632 int old_element, new_element;
11633 int dropx = player->jx, dropy = player->jy;
11634 int drop_direction = player->MovDir;
11635 int drop_side = drop_direction;
11636 int drop_element = (player->inventory_size > 0 ?
11637 player->inventory_element[player->inventory_size - 1] :
11638 player->inventory_infinite_element != EL_UNDEFINED ?
11639 player->inventory_infinite_element :
11640 player->dynabombs_left > 0 ?
11641 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
11644 /* do not drop an element on top of another element; when holding drop key
11645 pressed without moving, dropped element must move away before the next
11646 element can be dropped (this is especially important if the next element
11647 is dynamite, which can be placed on background for historical reasons) */
11648 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
11651 if (IS_THROWABLE(drop_element))
11653 dropx += GET_DX_FROM_DIR(drop_direction);
11654 dropy += GET_DY_FROM_DIR(drop_direction);
11656 if (!IN_LEV_FIELD(dropx, dropy))
11660 old_element = Feld[dropx][dropy]; /* old element at dropping position */
11661 new_element = drop_element; /* default: no change when dropping */
11663 /* check if player is active, not moving and ready to drop */
11664 if (!player->active || player->MovPos || player->drop_delay > 0)
11667 /* check if player has anything that can be dropped */
11668 if (new_element == EL_UNDEFINED)
11671 /* check if anything can be dropped at the current position */
11672 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
11675 /* collected custom elements can only be dropped on empty fields */
11676 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
11679 if (old_element != EL_EMPTY)
11680 Back[dropx][dropy] = old_element; /* store old element on this field */
11682 ResetGfxAnimation(dropx, dropy);
11683 ResetRandomAnimationValue(dropx, dropy);
11685 if (player->inventory_size > 0 ||
11686 player->inventory_infinite_element != EL_UNDEFINED)
11688 if (player->inventory_size > 0)
11690 player->inventory_size--;
11692 DrawGameValue_Dynamite(local_player->inventory_size);
11694 if (new_element == EL_DYNAMITE)
11695 new_element = EL_DYNAMITE_ACTIVE;
11696 else if (new_element == EL_EM_DYNAMITE)
11697 new_element = EL_EM_DYNAMITE_ACTIVE;
11698 else if (new_element == EL_SP_DISK_RED)
11699 new_element = EL_SP_DISK_RED_ACTIVE;
11702 Feld[dropx][dropy] = new_element;
11704 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
11705 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
11706 el2img(Feld[dropx][dropy]), 0);
11708 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
11710 /* needed if previous element just changed to "empty" in the last frame */
11711 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
11713 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
11714 player->index_bit, drop_side);
11715 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
11717 player->index_bit, drop_side);
11719 TestIfElementTouchesCustomElement(dropx, dropy);
11721 else /* player is dropping a dyna bomb */
11723 player->dynabombs_left--;
11725 Feld[dropx][dropy] = new_element;
11727 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
11728 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
11729 el2img(Feld[dropx][dropy]), 0);
11731 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
11734 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
11735 InitField_WithBug1(dropx, dropy, FALSE);
11737 new_element = Feld[dropx][dropy]; /* element might have changed */
11739 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
11740 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
11742 int move_direction, nextx, nexty;
11744 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
11745 MovDir[dropx][dropy] = drop_direction;
11747 move_direction = MovDir[dropx][dropy];
11748 nextx = dropx + GET_DX_FROM_DIR(move_direction);
11749 nexty = dropy + GET_DY_FROM_DIR(move_direction);
11751 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
11752 CheckCollision[dropx][dropy] = 2;
11755 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
11756 player->is_dropping = TRUE;
11758 player->drop_x = dropx;
11759 player->drop_y = dropy;
11764 /* ------------------------------------------------------------------------- */
11765 /* game sound playing functions */
11766 /* ------------------------------------------------------------------------- */
11768 static int *loop_sound_frame = NULL;
11769 static int *loop_sound_volume = NULL;
11771 void InitPlayLevelSound()
11773 int num_sounds = getSoundListSize();
11775 checked_free(loop_sound_frame);
11776 checked_free(loop_sound_volume);
11778 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
11779 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
11782 static void PlayLevelSound(int x, int y, int nr)
11784 int sx = SCREENX(x), sy = SCREENY(y);
11785 int volume, stereo_position;
11786 int max_distance = 8;
11787 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
11789 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
11790 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
11793 if (!IN_LEV_FIELD(x, y) ||
11794 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
11795 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
11798 volume = SOUND_MAX_VOLUME;
11800 if (!IN_SCR_FIELD(sx, sy))
11802 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
11803 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
11805 volume -= volume * (dx > dy ? dx : dy) / max_distance;
11808 stereo_position = (SOUND_MAX_LEFT +
11809 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
11810 (SCR_FIELDX + 2 * max_distance));
11812 if (IS_LOOP_SOUND(nr))
11814 /* This assures that quieter loop sounds do not overwrite louder ones,
11815 while restarting sound volume comparison with each new game frame. */
11817 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
11820 loop_sound_volume[nr] = volume;
11821 loop_sound_frame[nr] = FrameCounter;
11824 PlaySoundExt(nr, volume, stereo_position, type);
11827 static void PlayLevelSoundNearest(int x, int y, int sound_action)
11829 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
11830 x > LEVELX(BX2) ? LEVELX(BX2) : x,
11831 y < LEVELY(BY1) ? LEVELY(BY1) :
11832 y > LEVELY(BY2) ? LEVELY(BY2) : y,
11836 static void PlayLevelSoundAction(int x, int y, int action)
11838 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
11841 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
11843 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
11845 if (sound_effect != SND_UNDEFINED)
11846 PlayLevelSound(x, y, sound_effect);
11849 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
11852 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
11854 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
11855 PlayLevelSound(x, y, sound_effect);
11858 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
11860 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
11862 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
11863 PlayLevelSound(x, y, sound_effect);
11866 static void StopLevelSoundActionIfLoop(int x, int y, int action)
11868 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
11870 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
11871 StopSound(sound_effect);
11874 static void PlayLevelMusic()
11876 if (levelset.music[level_nr] != MUS_UNDEFINED)
11877 PlayMusic(levelset.music[level_nr]); /* from config file */
11879 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
11882 void PlayLevelSound_EM(int x, int y, int element_em, int sample)
11884 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
11889 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
11893 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
11897 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
11901 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
11905 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
11909 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
11913 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
11916 case SAMPLE_android_clone:
11917 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
11920 case SAMPLE_android_move:
11921 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
11924 case SAMPLE_spring:
11925 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
11929 PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
11933 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
11936 case SAMPLE_eater_eat:
11937 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
11941 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
11944 case SAMPLE_collect:
11945 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
11948 case SAMPLE_diamond:
11949 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
11952 case SAMPLE_squash:
11953 /* !!! CHECK THIS !!! */
11955 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
11957 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
11961 case SAMPLE_wonderfall:
11962 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
11966 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
11970 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
11974 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
11978 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
11982 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
11986 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
11989 case SAMPLE_wonder:
11990 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
11994 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
11997 case SAMPLE_exit_open:
11998 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
12001 case SAMPLE_exit_leave:
12002 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
12005 case SAMPLE_dynamite:
12006 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12010 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12014 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
12018 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12022 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
12026 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
12030 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
12034 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
12039 void RaiseScore(int value)
12041 local_player->score += value;
12043 DrawGameValue_Score(local_player->score);
12046 void RaiseScoreElement(int element)
12051 case EL_BD_DIAMOND:
12052 case EL_EMERALD_YELLOW:
12053 case EL_EMERALD_RED:
12054 case EL_EMERALD_PURPLE:
12055 case EL_SP_INFOTRON:
12056 RaiseScore(level.score[SC_EMERALD]);
12059 RaiseScore(level.score[SC_DIAMOND]);
12062 RaiseScore(level.score[SC_CRYSTAL]);
12065 RaiseScore(level.score[SC_PEARL]);
12068 case EL_BD_BUTTERFLY:
12069 case EL_SP_ELECTRON:
12070 RaiseScore(level.score[SC_BUG]);
12073 case EL_BD_FIREFLY:
12074 case EL_SP_SNIKSNAK:
12075 RaiseScore(level.score[SC_SPACESHIP]);
12078 case EL_DARK_YAMYAM:
12079 RaiseScore(level.score[SC_YAMYAM]);
12082 RaiseScore(level.score[SC_ROBOT]);
12085 RaiseScore(level.score[SC_PACMAN]);
12088 RaiseScore(level.score[SC_NUT]);
12091 case EL_EM_DYNAMITE:
12092 case EL_SP_DISK_RED:
12093 case EL_DYNABOMB_INCREASE_NUMBER:
12094 case EL_DYNABOMB_INCREASE_SIZE:
12095 case EL_DYNABOMB_INCREASE_POWER:
12096 RaiseScore(level.score[SC_DYNAMITE]);
12098 case EL_SHIELD_NORMAL:
12099 case EL_SHIELD_DEADLY:
12100 RaiseScore(level.score[SC_SHIELD]);
12102 case EL_EXTRA_TIME:
12103 RaiseScore(level.extra_time_score);
12117 RaiseScore(level.score[SC_KEY]);
12120 RaiseScore(element_info[element].collect_score);
12125 void RequestQuitGame(boolean ask_if_really_quit)
12127 if (AllPlayersGone ||
12128 !ask_if_really_quit ||
12129 level_editor_test_game ||
12130 Request("Do you really want to quit the game ?",
12131 REQ_ASK | REQ_STAY_CLOSED))
12133 #if defined(NETWORK_AVALIABLE)
12134 if (options.network)
12135 SendToServer_StopPlaying();
12139 game_status = GAME_MODE_MAIN;
12145 if (tape.playing && tape.deactivate_display)
12146 TapeDeactivateDisplayOff(TRUE);
12148 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
12150 if (tape.playing && tape.deactivate_display)
12151 TapeDeactivateDisplayOn();
12156 /* ---------- new game button stuff ---------------------------------------- */
12158 /* graphic position values for game buttons */
12159 #define GAME_BUTTON_XSIZE 30
12160 #define GAME_BUTTON_YSIZE 30
12161 #define GAME_BUTTON_XPOS 5
12162 #define GAME_BUTTON_YPOS 215
12163 #define SOUND_BUTTON_XPOS 5
12164 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
12166 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
12167 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
12168 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
12169 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
12170 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
12171 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
12178 } gamebutton_info[NUM_GAME_BUTTONS] =
12181 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
12186 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
12187 GAME_CTRL_ID_PAUSE,
12191 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
12196 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
12197 SOUND_CTRL_ID_MUSIC,
12198 "background music on/off"
12201 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
12202 SOUND_CTRL_ID_LOOPS,
12203 "sound loops on/off"
12206 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
12207 SOUND_CTRL_ID_SIMPLE,
12208 "normal sounds on/off"
12212 void CreateGameButtons()
12216 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12218 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
12219 struct GadgetInfo *gi;
12222 unsigned long event_mask;
12223 int gd_xoffset, gd_yoffset;
12224 int gd_x1, gd_x2, gd_y1, gd_y2;
12227 gd_xoffset = gamebutton_info[i].x;
12228 gd_yoffset = gamebutton_info[i].y;
12229 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
12230 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
12232 if (id == GAME_CTRL_ID_STOP ||
12233 id == GAME_CTRL_ID_PAUSE ||
12234 id == GAME_CTRL_ID_PLAY)
12236 button_type = GD_TYPE_NORMAL_BUTTON;
12238 event_mask = GD_EVENT_RELEASED;
12239 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12240 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12244 button_type = GD_TYPE_CHECK_BUTTON;
12246 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
12247 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
12248 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
12249 event_mask = GD_EVENT_PRESSED;
12250 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
12251 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12254 gi = CreateGadget(GDI_CUSTOM_ID, id,
12255 GDI_INFO_TEXT, gamebutton_info[i].infotext,
12256 GDI_X, DX + gd_xoffset,
12257 GDI_Y, DY + gd_yoffset,
12258 GDI_WIDTH, GAME_BUTTON_XSIZE,
12259 GDI_HEIGHT, GAME_BUTTON_YSIZE,
12260 GDI_TYPE, button_type,
12261 GDI_STATE, GD_BUTTON_UNPRESSED,
12262 GDI_CHECKED, checked,
12263 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
12264 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
12265 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
12266 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
12267 GDI_EVENT_MASK, event_mask,
12268 GDI_CALLBACK_ACTION, HandleGameButtons,
12272 Error(ERR_EXIT, "cannot create gadget");
12274 game_gadget[id] = gi;
12278 void FreeGameButtons()
12282 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12283 FreeGadget(game_gadget[i]);
12286 static void MapGameButtons()
12290 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12291 MapGadget(game_gadget[i]);
12294 void UnmapGameButtons()
12298 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12299 UnmapGadget(game_gadget[i]);
12302 static void HandleGameButtons(struct GadgetInfo *gi)
12304 int id = gi->custom_id;
12306 if (game_status != GAME_MODE_PLAYING)
12311 case GAME_CTRL_ID_STOP:
12312 RequestQuitGame(TRUE);
12315 case GAME_CTRL_ID_PAUSE:
12316 if (options.network)
12318 #if defined(NETWORK_AVALIABLE)
12320 SendToServer_ContinuePlaying();
12322 SendToServer_PausePlaying();
12326 TapeTogglePause(TAPE_TOGGLE_MANUAL);
12329 case GAME_CTRL_ID_PLAY:
12332 #if defined(NETWORK_AVALIABLE)
12333 if (options.network)
12334 SendToServer_ContinuePlaying();
12338 tape.pausing = FALSE;
12339 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
12344 case SOUND_CTRL_ID_MUSIC:
12345 if (setup.sound_music)
12347 setup.sound_music = FALSE;
12350 else if (audio.music_available)
12352 setup.sound = setup.sound_music = TRUE;
12354 SetAudioMode(setup.sound);
12360 case SOUND_CTRL_ID_LOOPS:
12361 if (setup.sound_loops)
12362 setup.sound_loops = FALSE;
12363 else if (audio.loops_available)
12365 setup.sound = setup.sound_loops = TRUE;
12366 SetAudioMode(setup.sound);
12370 case SOUND_CTRL_ID_SIMPLE:
12371 if (setup.sound_simple)
12372 setup.sound_simple = FALSE;
12373 else if (audio.sound_available)
12375 setup.sound = setup.sound_simple = TRUE;
12376 SetAudioMode(setup.sound);