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)
43 #define USE_NEW_CONTINUOUS_SNAPPING (USE_NEW_STUFF * 1)
45 #define USE_QUICKSAND_IMPACT_BUGFIX (USE_NEW_STUFF * 0)
52 /* for MovePlayer() */
53 #define MP_NO_ACTION 0
56 #define MP_DONT_RUN_INTO (MP_MOVING | MP_ACTION)
58 /* for ScrollPlayer() */
60 #define SCROLL_GO_ON 1
62 /* for Bang()/Explode() */
63 #define EX_PHASE_START 0
64 #define EX_TYPE_NONE 0
65 #define EX_TYPE_NORMAL (1 << 0)
66 #define EX_TYPE_CENTER (1 << 1)
67 #define EX_TYPE_BORDER (1 << 2)
68 #define EX_TYPE_CROSS (1 << 3)
69 #define EX_TYPE_DYNA (1 << 4)
70 #define EX_TYPE_SINGLE_TILE (EX_TYPE_CENTER | EX_TYPE_BORDER)
72 /* special positions in the game control window (relative to control window) */
75 #define XX_EMERALDS 29
76 #define YY_EMERALDS 54
77 #define XX_DYNAMITE 29
78 #define YY_DYNAMITE 89
87 /* special positions in the game control window (relative to main window) */
88 #define DX_LEVEL (DX + XX_LEVEL)
89 #define DY_LEVEL (DY + YY_LEVEL)
90 #define DX_EMERALDS (DX + XX_EMERALDS)
91 #define DY_EMERALDS (DY + YY_EMERALDS)
92 #define DX_DYNAMITE (DX + XX_DYNAMITE)
93 #define DY_DYNAMITE (DY + YY_DYNAMITE)
94 #define DX_KEYS (DX + XX_KEYS)
95 #define DY_KEYS (DY + YY_KEYS)
96 #define DX_SCORE (DX + XX_SCORE)
97 #define DY_SCORE (DY + YY_SCORE)
98 #define DX_TIME1 (DX + XX_TIME1)
99 #define DX_TIME2 (DX + XX_TIME2)
100 #define DY_TIME (DY + YY_TIME)
102 /* values for delayed check of falling and moving elements and for collision */
103 #define CHECK_DELAY_MOVING 3
104 #define CHECK_DELAY_FALLING 3
105 #define CHECK_DELAY_COLLISION 2
107 /* values for initial player move delay (initial delay counter value) */
108 #define INITIAL_MOVE_DELAY_OFF -1
109 #define INITIAL_MOVE_DELAY_ON 0
111 /* values for player movement speed (which is in fact a delay value) */
112 #define MOVE_DELAY_MIN_SPEED 32
113 #define MOVE_DELAY_NORMAL_SPEED 8
114 #define MOVE_DELAY_HIGH_SPEED 4
115 #define MOVE_DELAY_MAX_SPEED 1
118 #define DOUBLE_MOVE_DELAY(x) (x = (x <= MOVE_DELAY_HIGH_SPEED ? x * 2 : x))
119 #define HALVE_MOVE_DELAY(x) (x = (x >= MOVE_DELAY_HIGH_SPEED ? x / 2 : x))
121 #define DOUBLE_MOVE_DELAY(x) (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
122 #define HALVE_MOVE_DELAY(x) (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
124 #define DOUBLE_PLAYER_SPEED(p) (HALVE_MOVE_DELAY((p)->move_delay_value))
125 #define HALVE_PLAYER_SPEED(p) (DOUBLE_MOVE_DELAY((p)->move_delay_value))
127 /* values for other actions */
128 #define MOVE_STEPSIZE_NORMAL (TILEX / MOVE_DELAY_NORMAL_SPEED)
129 #define MOVE_STEPSIZE_MIN (1)
130 #define MOVE_STEPSIZE_MAX (TILEX)
132 #define GET_DX_FROM_DIR(d) ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
133 #define GET_DY_FROM_DIR(d) ((d) == MV_UP ? -1 : (d) == MV_DOWN ? 1 : 0)
135 #define INIT_GFX_RANDOM() (SimpleRND(1000000))
137 #define GET_NEW_PUSH_DELAY(e) ( (element_info[e].push_delay_fixed) + \
138 RND(element_info[e].push_delay_random))
139 #define GET_NEW_DROP_DELAY(e) ( (element_info[e].drop_delay_fixed) + \
140 RND(element_info[e].drop_delay_random))
141 #define GET_NEW_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
142 RND(element_info[e].move_delay_random))
143 #define GET_MAX_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
144 (element_info[e].move_delay_random))
145 #define GET_NEW_CUSTOM_VALUE(e) ( (element_info[e].ce_value_fixed_initial) +\
146 RND(element_info[e].ce_value_random_initial))
147 #define GET_CHANGE_DELAY(c) ( ((c)->delay_fixed * (c)->delay_frames) + \
148 RND((c)->delay_random * (c)->delay_frames))
149 #define GET_CE_DELAY_VALUE(c) ( ((c)->delay_fixed) + \
150 RND((c)->delay_random))
153 #define GET_VALID_RUNTIME_ELEMENT(e) \
154 ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
156 #define GET_VALID_FILE_ELEMENT(e) \
157 ((e) >= NUM_FILE_ELEMENTS ? EL_UNKNOWN : (e))
160 #define GET_TARGET_ELEMENT(e, ch) \
161 ((e) == EL_TRIGGER_PLAYER ? (ch)->actual_trigger_player : \
162 (e) == EL_TRIGGER_ELEMENT ? (ch)->actual_trigger_element : \
163 (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value : (e))
165 #define CAN_GROW_INTO(e) \
166 ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
168 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition) \
169 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
172 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition) \
173 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
174 (CAN_MOVE_INTO_ACID(e) && \
175 Feld[x][y] == EL_ACID) || \
178 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition) \
179 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
180 (CAN_MOVE_INTO_ACID(e) && \
181 Feld[x][y] == EL_ACID) || \
184 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition) \
185 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
187 (CAN_MOVE_INTO_ACID(e) && \
188 Feld[x][y] == EL_ACID) || \
189 (DONT_COLLIDE_WITH(e) && \
191 !PLAYER_ENEMY_PROTECTED(x, y))))
193 #define ELEMENT_CAN_ENTER_FIELD(e, x, y) \
194 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
196 #define SATELLITE_CAN_ENTER_FIELD(x, y) \
197 ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
199 #define ANDROID_CAN_ENTER_FIELD(e, x, y) \
200 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
202 #define ANDROID_CAN_CLONE_FIELD(x, y) \
203 (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
204 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
206 #define ENEMY_CAN_ENTER_FIELD(e, x, y) \
207 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
209 #define YAMYAM_CAN_ENTER_FIELD(e, x, y) \
210 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
212 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y) \
213 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
215 #define PACMAN_CAN_ENTER_FIELD(e, x, y) \
216 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
218 #define PIG_CAN_ENTER_FIELD(e, x, y) \
219 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
221 #define PENGUIN_CAN_ENTER_FIELD(e, x, y) \
222 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN ||\
223 IS_FOOD_PENGUIN(Feld[x][y])))
224 #define DRAGON_CAN_ENTER_FIELD(e, x, y) \
225 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
227 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition) \
228 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
230 #define SPRING_CAN_ENTER_FIELD(e, x, y) \
231 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
233 #define SPRING_CAN_BUMP_FROM_FIELD(x, y) \
234 (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER || \
235 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
238 #define GROUP_NR(e) ((e) - EL_GROUP_START)
239 #define IS_IN_GROUP(e, nr) (element_info[e].in_group[nr] == TRUE)
240 #define IS_IN_GROUP_EL(e, ge) (IS_IN_GROUP(e, (ge) - EL_GROUP_START))
242 #define IS_EQUAL_OR_IN_GROUP(e, ge) \
243 (IS_GROUP_ELEMENT(ge) ? IS_IN_GROUP(e, GROUP_NR(ge)) : (e) == (ge))
246 #define MOVE_ENTER_EL(e) (element_info[e].move_enter_element)
248 #define CE_ENTER_FIELD_COND(e, x, y) \
249 (!IS_PLAYER(x, y) && \
250 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
252 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y) \
253 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
255 #define IN_LEV_FIELD_AND_IS_FREE(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
256 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
258 #define ACCESS_FROM(e, d) (element_info[e].access_direction &(d))
259 #define IS_WALKABLE_FROM(e, d) (IS_WALKABLE(e) && ACCESS_FROM(e, d))
260 #define IS_PASSABLE_FROM(e, d) (IS_PASSABLE(e) && ACCESS_FROM(e, d))
261 #define IS_ACCESSIBLE_FROM(e, d) (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
263 /* game button identifiers */
264 #define GAME_CTRL_ID_STOP 0
265 #define GAME_CTRL_ID_PAUSE 1
266 #define GAME_CTRL_ID_PLAY 2
267 #define SOUND_CTRL_ID_MUSIC 3
268 #define SOUND_CTRL_ID_LOOPS 4
269 #define SOUND_CTRL_ID_SIMPLE 5
271 #define NUM_GAME_BUTTONS 6
274 /* forward declaration for internal use */
276 static void CreateField(int, int, int);
278 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
279 static void AdvanceFrameAndPlayerCounters(int);
281 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
282 static boolean MovePlayer(struct PlayerInfo *, int, int);
283 static void ScrollPlayer(struct PlayerInfo *, int);
284 static void ScrollScreen(struct PlayerInfo *, int);
286 int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
288 static void InitBeltMovement(void);
289 static void CloseAllOpenTimegates(void);
290 static void CheckGravityMovement(struct PlayerInfo *);
291 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
292 static void KillPlayerUnlessEnemyProtected(int, int);
293 static void KillPlayerUnlessExplosionProtected(int, int);
295 static void TestIfPlayerTouchesCustomElement(int, int);
296 static void TestIfElementTouchesCustomElement(int, int);
297 static void TestIfElementHitsCustomElement(int, int, int);
299 static void TestIfElementSmashesCustomElement(int, int, int);
302 static void HandleElementChange(int, int, int);
303 static void ExecuteCustomElementAction(int, int, int, int);
304 static boolean ChangeElement(int, int, int, int);
306 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
307 #define CheckTriggeredElementChange(x, y, e, ev) \
308 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
309 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s) \
310 CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
311 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s) \
312 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
313 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p) \
314 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
316 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
317 #define CheckElementChange(x, y, e, te, ev) \
318 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
319 #define CheckElementChangeByPlayer(x, y, e, ev, p, s) \
320 CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
321 #define CheckElementChangeBySide(x, y, e, te, ev, s) \
322 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
324 static void PlayLevelSound(int, int, int);
325 static void PlayLevelSoundNearest(int, int, int);
326 static void PlayLevelSoundAction(int, int, int);
327 static void PlayLevelSoundElementAction(int, int, int, int);
328 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
329 static void PlayLevelSoundActionIfLoop(int, int, int);
330 static void StopLevelSoundActionIfLoop(int, int, int);
331 static void PlayLevelMusic();
333 static void MapGameButtons();
334 static void HandleGameButtons(struct GadgetInfo *);
336 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
339 /* ------------------------------------------------------------------------- */
340 /* definition of elements that automatically change to other elements after */
341 /* a specified time, eventually calling a function when changing */
342 /* ------------------------------------------------------------------------- */
344 /* forward declaration for changer functions */
345 static void InitBuggyBase(int, int);
346 static void WarnBuggyBase(int, int);
348 static void InitTrap(int, int);
349 static void ActivateTrap(int, int);
350 static void ChangeActiveTrap(int, int);
352 static void InitRobotWheel(int, int);
353 static void RunRobotWheel(int, int);
354 static void StopRobotWheel(int, int);
356 static void InitTimegateWheel(int, int);
357 static void RunTimegateWheel(int, int);
359 static void InitMagicBallDelay(int, int);
360 static void ActivateMagicBall(int, int);
362 static void InitDiagonalMovingElement(int, int);
364 struct ChangingElementInfo
369 void (*pre_change_function)(int x, int y);
370 void (*change_function)(int x, int y);
371 void (*post_change_function)(int x, int y);
374 static struct ChangingElementInfo change_delay_list[] =
425 EL_SWITCHGATE_OPENING,
433 EL_SWITCHGATE_CLOSING,
434 EL_SWITCHGATE_CLOSED,
466 EL_ACID_SPLASH_RIGHT,
475 EL_SP_BUGGY_BASE_ACTIVATING,
482 EL_SP_BUGGY_BASE_ACTIVATING,
483 EL_SP_BUGGY_BASE_ACTIVE,
490 EL_SP_BUGGY_BASE_ACTIVE,
514 EL_ROBOT_WHEEL_ACTIVE,
522 EL_TIMEGATE_SWITCH_ACTIVE,
530 EL_EMC_MAGIC_BALL_ACTIVE,
531 EL_EMC_MAGIC_BALL_ACTIVE,
538 EL_EMC_SPRING_BUMPER_ACTIVE,
539 EL_EMC_SPRING_BUMPER,
546 EL_DIAGONAL_SHRINKING,
559 InitDiagonalMovingElement
575 int push_delay_fixed, push_delay_random;
580 { EL_BALLOON, 0, 0 },
582 { EL_SOKOBAN_OBJECT, 2, 0 },
583 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
584 { EL_SATELLITE, 2, 0 },
585 { EL_SP_DISK_YELLOW, 2, 0 },
587 { EL_UNDEFINED, 0, 0 },
595 move_stepsize_list[] =
597 { EL_AMOEBA_DROP, 2 },
598 { EL_AMOEBA_DROPPING, 2 },
599 { EL_QUICKSAND_FILLING, 1 },
600 { EL_QUICKSAND_EMPTYING, 1 },
601 { EL_MAGIC_WALL_FILLING, 2 },
602 { EL_BD_MAGIC_WALL_FILLING, 2 },
603 { EL_MAGIC_WALL_EMPTYING, 2 },
604 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
614 collect_count_list[] =
617 { EL_BD_DIAMOND, 1 },
618 { EL_EMERALD_YELLOW, 1 },
619 { EL_EMERALD_RED, 1 },
620 { EL_EMERALD_PURPLE, 1 },
622 { EL_SP_INFOTRON, 1 },
634 access_direction_list[] =
636 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
637 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
638 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
639 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
640 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
641 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
642 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
643 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
644 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
645 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
646 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
648 { EL_SP_PORT_LEFT, MV_RIGHT },
649 { EL_SP_PORT_RIGHT, MV_LEFT },
650 { EL_SP_PORT_UP, MV_DOWN },
651 { EL_SP_PORT_DOWN, MV_UP },
652 { EL_SP_PORT_HORIZONTAL, MV_LEFT | MV_RIGHT },
653 { EL_SP_PORT_VERTICAL, MV_UP | MV_DOWN },
654 { EL_SP_PORT_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
655 { EL_SP_GRAVITY_PORT_LEFT, MV_RIGHT },
656 { EL_SP_GRAVITY_PORT_RIGHT, MV_LEFT },
657 { EL_SP_GRAVITY_PORT_UP, MV_DOWN },
658 { EL_SP_GRAVITY_PORT_DOWN, MV_UP },
659 { EL_SP_GRAVITY_ON_PORT_LEFT, MV_RIGHT },
660 { EL_SP_GRAVITY_ON_PORT_RIGHT, MV_LEFT },
661 { EL_SP_GRAVITY_ON_PORT_UP, MV_DOWN },
662 { EL_SP_GRAVITY_ON_PORT_DOWN, MV_UP },
663 { EL_SP_GRAVITY_OFF_PORT_LEFT, MV_RIGHT },
664 { EL_SP_GRAVITY_OFF_PORT_RIGHT, MV_LEFT },
665 { EL_SP_GRAVITY_OFF_PORT_UP, MV_DOWN },
666 { EL_SP_GRAVITY_OFF_PORT_DOWN, MV_UP },
668 { EL_UNDEFINED, MV_NONE }
671 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
673 #define IS_AUTO_CHANGING(e) (element_info[e].has_change_event[CE_DELAY])
674 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
675 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
676 IS_JUST_CHANGING(x, y))
678 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
680 /* static variables for playfield scan mode (scanning forward or backward) */
681 static int playfield_scan_start_x = 0;
682 static int playfield_scan_start_y = 0;
683 static int playfield_scan_delta_x = 1;
684 static int playfield_scan_delta_y = 1;
686 #define SCAN_PLAYFIELD(x, y) for ((y) = playfield_scan_start_y; \
687 (y) >= 0 && (y) <= lev_fieldy - 1; \
688 (y) += playfield_scan_delta_y) \
689 for ((x) = playfield_scan_start_x; \
690 (x) >= 0 && (x) <= lev_fieldx - 1; \
691 (x) += playfield_scan_delta_x) \
693 static void InitPlayfieldScanModeVars()
695 if (game.use_reverse_scan_direction)
697 playfield_scan_start_x = lev_fieldx - 1;
698 playfield_scan_start_y = lev_fieldy - 1;
700 playfield_scan_delta_x = -1;
701 playfield_scan_delta_y = -1;
705 playfield_scan_start_x = 0;
706 playfield_scan_start_y = 0;
708 playfield_scan_delta_x = 1;
709 playfield_scan_delta_y = 1;
713 static void InitPlayfieldScanMode(int mode)
715 game.use_reverse_scan_direction =
716 (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
718 InitPlayfieldScanModeVars();
721 static int get_move_delay_from_stepsize(int move_stepsize)
724 MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
726 /* make sure that stepsize value is always a power of 2 */
727 move_stepsize = (1 << log_2(move_stepsize));
729 return TILEX / move_stepsize;
732 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
735 int move_delay = get_move_delay_from_stepsize(move_stepsize);
736 boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
738 /* do no immediately change move delay -- the player might just be moving */
739 player->move_delay_value_next = move_delay;
741 /* information if player can move must be set separately */
742 player->cannot_move = cannot_move;
746 player->move_delay = game.initial_move_delay;
747 player->move_delay_value = game.initial_move_delay_value;
749 player->move_delay_value_next = -1;
751 player->move_delay_reset_counter = 0;
755 void GetPlayerConfig()
757 if (!audio.sound_available)
758 setup.sound_simple = FALSE;
760 if (!audio.loops_available)
761 setup.sound_loops = FALSE;
763 if (!audio.music_available)
764 setup.sound_music = FALSE;
766 if (!video.fullscreen_available)
767 setup.fullscreen = FALSE;
769 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
771 SetAudioMode(setup.sound);
775 static int getBeltNrFromBeltElement(int element)
777 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
778 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
779 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
782 static int getBeltNrFromBeltActiveElement(int element)
784 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
785 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
786 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
789 static int getBeltNrFromBeltSwitchElement(int element)
791 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
792 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
793 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
796 static int getBeltDirNrFromBeltSwitchElement(int element)
798 static int belt_base_element[4] =
800 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
801 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
802 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
803 EL_CONVEYOR_BELT_4_SWITCH_LEFT
806 int belt_nr = getBeltNrFromBeltSwitchElement(element);
807 int belt_dir_nr = element - belt_base_element[belt_nr];
809 return (belt_dir_nr % 3);
812 static int getBeltDirFromBeltSwitchElement(int element)
814 static int belt_move_dir[3] =
821 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
823 return belt_move_dir[belt_dir_nr];
826 static void InitPlayerField(int x, int y, int element, boolean init_game)
828 if (element == EL_SP_MURPHY)
832 if (stored_player[0].present)
834 Feld[x][y] = EL_SP_MURPHY_CLONE;
840 stored_player[0].use_murphy = TRUE;
842 if (!level.use_artwork_element[0])
843 stored_player[0].artwork_element = EL_SP_MURPHY;
846 Feld[x][y] = EL_PLAYER_1;
852 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
853 int jx = player->jx, jy = player->jy;
855 player->present = TRUE;
857 player->block_last_field = (element == EL_SP_MURPHY ?
858 level.sp_block_last_field :
859 level.block_last_field);
861 /* ---------- initialize player's last field block delay --------------- */
863 /* always start with reliable default value (no adjustment needed) */
864 player->block_delay_adjustment = 0;
866 /* special case 1: in Supaplex, Murphy blocks last field one more frame */
867 if (player->block_last_field && element == EL_SP_MURPHY)
868 player->block_delay_adjustment = 1;
870 /* special case 2: in game engines before 3.1.1, blocking was different */
871 if (game.use_block_last_field_bug)
872 player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
874 if (!options.network || player->connected)
876 player->active = TRUE;
878 /* remove potentially duplicate players */
879 if (StorePlayer[jx][jy] == Feld[x][y])
880 StorePlayer[jx][jy] = 0;
882 StorePlayer[x][y] = Feld[x][y];
886 printf("Player %d activated.\n", player->element_nr);
887 printf("[Local player is %d and currently %s.]\n",
888 local_player->element_nr,
889 local_player->active ? "active" : "not active");
893 Feld[x][y] = EL_EMPTY;
895 player->jx = player->last_jx = x;
896 player->jy = player->last_jy = y;
900 static void InitField(int x, int y, boolean init_game)
902 int element = Feld[x][y];
911 InitPlayerField(x, y, element, init_game);
914 case EL_SOKOBAN_FIELD_PLAYER:
915 element = Feld[x][y] = EL_PLAYER_1;
916 InitField(x, y, init_game);
918 element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
919 InitField(x, y, init_game);
922 case EL_SOKOBAN_FIELD_EMPTY:
923 local_player->sokobanfields_still_needed++;
927 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
928 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
929 else if (x > 0 && Feld[x-1][y] == EL_ACID)
930 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
931 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
932 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
933 else if (y > 0 && Feld[x][y-1] == EL_ACID)
934 Feld[x][y] = EL_ACID_POOL_BOTTOM;
935 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
936 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
945 case EL_SPACESHIP_RIGHT:
946 case EL_SPACESHIP_UP:
947 case EL_SPACESHIP_LEFT:
948 case EL_SPACESHIP_DOWN:
949 case EL_BD_BUTTERFLY:
950 case EL_BD_BUTTERFLY_RIGHT:
951 case EL_BD_BUTTERFLY_UP:
952 case EL_BD_BUTTERFLY_LEFT:
953 case EL_BD_BUTTERFLY_DOWN:
955 case EL_BD_FIREFLY_RIGHT:
956 case EL_BD_FIREFLY_UP:
957 case EL_BD_FIREFLY_LEFT:
958 case EL_BD_FIREFLY_DOWN:
959 case EL_PACMAN_RIGHT:
965 case EL_YAMYAM_RIGHT:
987 if (y == lev_fieldy - 1)
989 Feld[x][y] = EL_AMOEBA_GROWING;
990 Store[x][y] = EL_AMOEBA_WET;
994 case EL_DYNAMITE_ACTIVE:
995 case EL_SP_DISK_RED_ACTIVE:
996 case EL_DYNABOMB_PLAYER_1_ACTIVE:
997 case EL_DYNABOMB_PLAYER_2_ACTIVE:
998 case EL_DYNABOMB_PLAYER_3_ACTIVE:
999 case EL_DYNABOMB_PLAYER_4_ACTIVE:
1000 MovDelay[x][y] = 96;
1003 case EL_EM_DYNAMITE_ACTIVE:
1004 MovDelay[x][y] = 32;
1008 local_player->lights_still_needed++;
1012 local_player->friends_still_needed++;
1017 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1020 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1021 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1022 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1023 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1024 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1025 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1026 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1027 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1028 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1029 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1030 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1031 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1034 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1035 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1036 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1038 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
1040 game.belt_dir[belt_nr] = belt_dir;
1041 game.belt_dir_nr[belt_nr] = belt_dir_nr;
1043 else /* more than one switch -- set it like the first switch */
1045 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1050 case EL_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
1052 Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
1055 case EL_LIGHT_SWITCH_ACTIVE:
1057 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1060 case EL_EMC_MAGIC_BALL:
1061 if (game.ball_state)
1062 Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1065 case EL_EMC_MAGIC_BALL_SWITCH:
1066 if (game.ball_state)
1067 Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1072 if (IS_CUSTOM_ELEMENT(element))
1074 if (CAN_MOVE(element))
1077 #if USE_NEW_CUSTOM_VALUE
1078 if (!element_info[element].use_last_ce_value || init_game)
1079 CustomValue[x][y] = GET_NEW_CUSTOM_VALUE(Feld[x][y]);
1083 if (IS_CUSTOM_ELEMENT(element) && CAN_MOVE(element))
1086 else if (IS_GROUP_ELEMENT(element))
1088 struct ElementGroupInfo *group = element_info[element].group;
1089 int last_anim_random_frame = gfx.anim_random_frame;
1092 if (group->choice_mode == ANIM_RANDOM)
1093 gfx.anim_random_frame = RND(group->num_elements_resolved);
1095 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1096 group->choice_mode, 0,
1099 if (group->choice_mode == ANIM_RANDOM)
1100 gfx.anim_random_frame = last_anim_random_frame;
1102 group->choice_pos++;
1104 Feld[x][y] = group->element_resolved[element_pos];
1106 InitField(x, y, init_game);
1113 CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1118 #if USE_NEW_CUSTOM_VALUE
1121 CustomValue[x][y] = GET_NEW_CUSTOM_VALUE(Feld[x][y]);
1123 CustomValue[x][y] = element_info[Feld[x][y]].custom_value_initial;
1131 static inline void InitField_WithBug1(int x, int y, boolean init_game)
1133 InitField(x, y, init_game);
1135 /* not needed to call InitMovDir() -- already done by InitField()! */
1136 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1137 CAN_MOVE(Feld[x][y]))
1141 static inline void InitField_WithBug2(int x, int y, boolean init_game)
1143 int old_element = Feld[x][y];
1145 InitField(x, y, init_game);
1147 /* not needed to call InitMovDir() -- already done by InitField()! */
1148 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1149 CAN_MOVE(old_element) &&
1150 (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1153 /* this case is in fact a combination of not less than three bugs:
1154 first, it calls InitMovDir() for elements that can move, although this is
1155 already done by InitField(); then, it checks the element that was at this
1156 field _before_ the call to InitField() (which can change it); lastly, it
1157 was not called for "mole with direction" elements, which were treated as
1158 "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1162 inline void DrawGameValue_Emeralds(int value)
1164 DrawText(DX_EMERALDS, DY_EMERALDS, int2str(value, 3), FONT_TEXT_2);
1167 inline void DrawGameValue_Dynamite(int value)
1169 DrawText(DX_DYNAMITE, DY_DYNAMITE, int2str(value, 3), FONT_TEXT_2);
1172 inline void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
1176 /* currently only 4 of 8 possible keys are displayed */
1177 for (i = 0; i < STD_NUM_KEYS; i++)
1180 DrawMiniGraphicExt(drawto, DX_KEYS + i * MINI_TILEX, DY_KEYS,
1181 el2edimg(EL_KEY_1 + i));
1183 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
1184 DOOR_GFX_PAGEX5 + XX_KEYS + i * MINI_TILEX, YY_KEYS,
1185 MINI_TILEX, MINI_TILEY, DX_KEYS + i * MINI_TILEX, DY_KEYS);
1189 inline void DrawGameValue_Score(int value)
1191 DrawText(DX_SCORE, DY_SCORE, int2str(value, 5), FONT_TEXT_2);
1194 inline void DrawGameValue_Time(int value)
1197 DrawText(DX_TIME1, DY_TIME, int2str(value, 3), FONT_TEXT_2);
1199 DrawText(DX_TIME2, DY_TIME, int2str(value, 4), FONT_LEVEL_NUMBER);
1202 inline void DrawGameValue_Level(int value)
1205 DrawText(DX_LEVEL, DY_LEVEL, int2str(value, 2), FONT_TEXT_2);
1208 /* misuse area for displaying emeralds to draw bigger level number */
1209 DrawTextExt(drawto, DX_EMERALDS, DY_EMERALDS,
1210 int2str(value, 3), FONT_LEVEL_NUMBER, BLIT_OPAQUE);
1212 /* now copy it to the area for displaying level number */
1213 BlitBitmap(drawto, drawto,
1214 DX_EMERALDS, DY_EMERALDS + 1,
1215 getFontWidth(FONT_LEVEL_NUMBER) * 3,
1216 getFontHeight(FONT_LEVEL_NUMBER) - 1,
1217 DX_LEVEL - 1, DY_LEVEL + 1);
1219 /* restore the area for displaying emeralds */
1220 DrawGameValue_Emeralds(local_player->gems_still_needed);
1222 /* yes, this is all really ugly :-) */
1226 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
1229 int key[MAX_NUM_KEYS];
1232 for (i = 0; i < MAX_NUM_KEYS; i++)
1233 key[i] = key_bits & (1 << i);
1235 DrawGameValue_Level(level_nr);
1237 DrawGameValue_Emeralds(emeralds);
1238 DrawGameValue_Dynamite(dynamite);
1239 DrawGameValue_Score(score);
1240 DrawGameValue_Time(time);
1242 DrawGameValue_Keys(key);
1245 void DrawGameDoorValues()
1249 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
1251 DrawGameDoorValues_EM();
1256 DrawGameValue_Level(level_nr);
1258 DrawGameValue_Emeralds(local_player->gems_still_needed);
1259 DrawGameValue_Dynamite(local_player->inventory_size);
1260 DrawGameValue_Score(local_player->score);
1261 DrawGameValue_Time(TimeLeft);
1263 for (i = 0; i < MAX_PLAYERS; i++)
1264 DrawGameValue_Keys(stored_player[i].key);
1268 static void resolve_group_element(int group_element, int recursion_depth)
1270 static int group_nr;
1271 static struct ElementGroupInfo *group;
1272 struct ElementGroupInfo *actual_group = element_info[group_element].group;
1275 if (recursion_depth > NUM_GROUP_ELEMENTS) /* recursion too deep */
1277 Error(ERR_WARN, "recursion too deep when resolving group element %d",
1278 group_element - EL_GROUP_START + 1);
1280 /* replace element which caused too deep recursion by question mark */
1281 group->element_resolved[group->num_elements_resolved++] = EL_UNKNOWN;
1286 if (recursion_depth == 0) /* initialization */
1288 group = element_info[group_element].group;
1289 group_nr = group_element - EL_GROUP_START;
1291 group->num_elements_resolved = 0;
1292 group->choice_pos = 0;
1295 for (i = 0; i < actual_group->num_elements; i++)
1297 int element = actual_group->element[i];
1299 if (group->num_elements_resolved == NUM_FILE_ELEMENTS)
1302 if (IS_GROUP_ELEMENT(element))
1303 resolve_group_element(element, recursion_depth + 1);
1306 group->element_resolved[group->num_elements_resolved++] = element;
1307 element_info[element].in_group[group_nr] = TRUE;
1314 =============================================================================
1316 -----------------------------------------------------------------------------
1317 initialize game engine due to level / tape version number
1318 =============================================================================
1321 static void InitGameEngine()
1323 int i, j, k, l, x, y;
1325 /* set game engine from tape file when re-playing, else from level file */
1326 game.engine_version = (tape.playing ? tape.engine_version :
1327 level.game_version);
1329 /* ---------------------------------------------------------------------- */
1330 /* set flags for bugs and changes according to active game engine version */
1331 /* ---------------------------------------------------------------------- */
1334 Summary of bugfix/change:
1335 Fixed handling for custom elements that change when pushed by the player.
1337 Fixed/changed in version:
1341 Before 3.1.0, custom elements that "change when pushing" changed directly
1342 after the player started pushing them (until then handled in "DigField()").
1343 Since 3.1.0, these custom elements are not changed until the "pushing"
1344 move of the element is finished (now handled in "ContinueMoving()").
1346 Affected levels/tapes:
1347 The first condition is generally needed for all levels/tapes before version
1348 3.1.0, which might use the old behaviour before it was changed; known tapes
1349 that are affected are some tapes from the level set "Walpurgis Gardens" by
1351 The second condition is an exception from the above case and is needed for
1352 the special case of tapes recorded with game (not engine!) version 3.1.0 or
1353 above (including some development versions of 3.1.0), but before it was
1354 known that this change would break tapes like the above and was fixed in
1355 3.1.1, so that the changed behaviour was active although the engine version
1356 while recording maybe was before 3.1.0. There is at least one tape that is
1357 affected by this exception, which is the tape for the one-level set "Bug
1358 Machine" by Juergen Bonhagen.
1361 game.use_change_when_pushing_bug =
1362 (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1364 tape.game_version >= VERSION_IDENT(3,1,0,0) &&
1365 tape.game_version < VERSION_IDENT(3,1,1,0)));
1368 Summary of bugfix/change:
1369 Fixed handling for blocking the field the player leaves when moving.
1371 Fixed/changed in version:
1375 Before 3.1.1, when "block last field when moving" was enabled, the field
1376 the player is leaving when moving was blocked for the time of the move,
1377 and was directly unblocked afterwards. This resulted in the last field
1378 being blocked for exactly one less than the number of frames of one player
1379 move. Additionally, even when blocking was disabled, the last field was
1380 blocked for exactly one frame.
1381 Since 3.1.1, due to changes in player movement handling, the last field
1382 is not blocked at all when blocking is disabled. When blocking is enabled,
1383 the last field is blocked for exactly the number of frames of one player
1384 move. Additionally, if the player is Murphy, the hero of Supaplex, the
1385 last field is blocked for exactly one more than the number of frames of
1388 Affected levels/tapes:
1389 (!!! yet to be determined -- probably many !!!)
1392 game.use_block_last_field_bug =
1393 (game.engine_version < VERSION_IDENT(3,1,1,0));
1396 Summary of bugfix/change:
1397 Changed behaviour of CE changes with multiple changes per single frame.
1399 Fixed/changed in version:
1403 Before 3.2.0-6, only one single CE change was allowed in each engine frame.
1404 This resulted in race conditions where CEs seem to behave strange in some
1405 situations (where triggered CE changes were just skipped because there was
1406 already a CE change on that tile in the playfield in that engine frame).
1407 Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
1408 (The number of changes per frame must be limited in any case, because else
1409 it is easily possible to define CE changes that would result in an infinite
1410 loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
1411 should be set large enough so that it would only be reached in cases where
1412 the corresponding CE change conditions run into a loop. Therefore, it seems
1413 to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
1414 maximal number of change pages for custom elements.)
1416 Affected levels/tapes:
1420 #if USE_ONLY_ONE_CHANGE_PER_FRAME
1421 game.max_num_changes_per_frame = 1;
1423 game.max_num_changes_per_frame =
1424 (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
1427 /* ---------------------------------------------------------------------- */
1429 /* default scan direction: scan playfield from top/left to bottom/right */
1430 InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
1432 /* dynamically adjust element properties according to game engine version */
1433 InitElementPropertiesEngine(game.engine_version);
1436 printf("level %d: level version == %06d\n", level_nr, level.game_version);
1437 printf(" tape version == %06d [%s] [file: %06d]\n",
1438 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
1440 printf(" => game.engine_version == %06d\n", game.engine_version);
1444 /* ---------- recursively resolve group elements ------------------------- */
1446 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1447 for (j = 0; j < NUM_GROUP_ELEMENTS; j++)
1448 element_info[i].in_group[j] = FALSE;
1450 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
1451 resolve_group_element(EL_GROUP_START + i, 0);
1454 /* ---------- initialize player's initial move delay --------------------- */
1457 /* dynamically adjust player properties according to level information */
1458 game.initial_move_delay_value =
1459 get_move_delay_from_stepsize(level.initial_player_stepsize);
1461 /* dynamically adjust player properties according to level information */
1462 game.initial_move_delay_value =
1463 (level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED);
1466 /* dynamically adjust player properties according to game engine version */
1467 game.initial_move_delay = (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
1468 game.initial_move_delay_value : 0);
1470 /* ---------- initialize player's initial push delay --------------------- */
1472 /* dynamically adjust player properties according to game engine version */
1473 game.initial_push_delay_value =
1474 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
1476 /* ---------- initialize changing elements ------------------------------- */
1478 /* initialize changing elements information */
1479 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1481 struct ElementInfo *ei = &element_info[i];
1483 /* this pointer might have been changed in the level editor */
1484 ei->change = &ei->change_page[0];
1486 if (!IS_CUSTOM_ELEMENT(i))
1488 ei->change->target_element = EL_EMPTY_SPACE;
1489 ei->change->delay_fixed = 0;
1490 ei->change->delay_random = 0;
1491 ei->change->delay_frames = 1;
1494 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1496 ei->has_change_event[j] = FALSE;
1498 ei->event_page_nr[j] = 0;
1499 ei->event_page[j] = &ei->change_page[0];
1503 /* add changing elements from pre-defined list */
1504 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
1506 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
1507 struct ElementInfo *ei = &element_info[ch_delay->element];
1509 ei->change->target_element = ch_delay->target_element;
1510 ei->change->delay_fixed = ch_delay->change_delay;
1512 ei->change->pre_change_function = ch_delay->pre_change_function;
1513 ei->change->change_function = ch_delay->change_function;
1514 ei->change->post_change_function = ch_delay->post_change_function;
1516 ei->change->can_change = TRUE;
1517 ei->change->can_change_or_has_action = TRUE;
1519 ei->has_change_event[CE_DELAY] = TRUE;
1521 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
1522 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
1525 /* ---------- initialize internal run-time variables ------------- */
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 ei->change_page[j].can_change_or_has_action =
1534 (ei->change_page[j].can_change |
1535 ei->change_page[j].has_action);
1539 /* add change events from custom element configuration */
1540 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1542 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1544 for (j = 0; j < ei->num_change_pages; j++)
1546 if (!ei->change_page[j].can_change_or_has_action)
1549 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1551 /* only add event page for the first page found with this event */
1552 if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
1554 ei->has_change_event[k] = TRUE;
1556 ei->event_page_nr[k] = j;
1557 ei->event_page[k] = &ei->change_page[j];
1563 /* ---------- initialize run-time trigger player and element ------------- */
1565 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1567 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1569 for (j = 0; j < ei->num_change_pages; j++)
1571 ei->change_page[j].actual_trigger_element = EL_EMPTY;
1572 ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
1573 ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
1574 ei->change_page[j].actual_trigger_ce_value = 0;
1578 /* ---------- initialize trigger events ---------------------------------- */
1580 /* initialize trigger events information */
1581 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1582 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1583 trigger_events[i][j] = FALSE;
1585 /* add trigger events from element change event properties */
1586 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1588 struct ElementInfo *ei = &element_info[i];
1590 for (j = 0; j < ei->num_change_pages; j++)
1592 if (!ei->change_page[j].can_change_or_has_action)
1595 if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
1597 int trigger_element = ei->change_page[j].trigger_element;
1599 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1601 if (ei->change_page[j].has_event[k])
1603 if (IS_GROUP_ELEMENT(trigger_element))
1605 struct ElementGroupInfo *group =
1606 element_info[trigger_element].group;
1608 for (l = 0; l < group->num_elements_resolved; l++)
1609 trigger_events[group->element_resolved[l]][k] = TRUE;
1612 trigger_events[trigger_element][k] = TRUE;
1619 /* ---------- initialize push delay -------------------------------------- */
1621 /* initialize push delay values to default */
1622 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1624 if (!IS_CUSTOM_ELEMENT(i))
1626 element_info[i].push_delay_fixed = game.default_push_delay_fixed;
1627 element_info[i].push_delay_random = game.default_push_delay_random;
1631 /* set push delay value for certain elements from pre-defined list */
1632 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
1634 int e = push_delay_list[i].element;
1636 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
1637 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
1640 /* set push delay value for Supaplex elements for newer engine versions */
1641 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
1643 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1645 if (IS_SP_ELEMENT(i))
1647 /* set SP push delay to just enough to push under a falling zonk */
1648 int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
1650 element_info[i].push_delay_fixed = delay;
1651 element_info[i].push_delay_random = 0;
1656 /* ---------- initialize move stepsize ----------------------------------- */
1658 /* initialize move stepsize values to default */
1659 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1660 if (!IS_CUSTOM_ELEMENT(i))
1661 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
1663 /* set move stepsize value for certain elements from pre-defined list */
1664 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
1666 int e = move_stepsize_list[i].element;
1668 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
1671 /* ---------- initialize collect score ----------------------------------- */
1673 /* initialize collect score values for custom elements from initial value */
1674 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1675 if (IS_CUSTOM_ELEMENT(i))
1676 element_info[i].collect_score = element_info[i].collect_score_initial;
1678 /* ---------- initialize collect count ----------------------------------- */
1680 /* initialize collect count values for non-custom elements */
1681 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1682 if (!IS_CUSTOM_ELEMENT(i))
1683 element_info[i].collect_count_initial = 0;
1685 /* add collect count values for all elements from pre-defined list */
1686 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
1687 element_info[collect_count_list[i].element].collect_count_initial =
1688 collect_count_list[i].count;
1690 /* ---------- initialize access direction -------------------------------- */
1692 /* initialize access direction values to default (access from every side) */
1693 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1694 if (!IS_CUSTOM_ELEMENT(i))
1695 element_info[i].access_direction = MV_ALL_DIRECTIONS;
1697 /* set access direction value for certain elements from pre-defined list */
1698 for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
1699 element_info[access_direction_list[i].element].access_direction =
1700 access_direction_list[i].direction;
1702 /* ---------- initialize explosion content ------------------------------- */
1703 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1705 if (IS_CUSTOM_ELEMENT(i))
1708 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
1710 /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
1712 element_info[i].content.e[x][y] =
1713 (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
1714 i == EL_PLAYER_2 ? EL_EMERALD_RED :
1715 i == EL_PLAYER_3 ? EL_EMERALD :
1716 i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
1717 i == EL_MOLE ? EL_EMERALD_RED :
1718 i == EL_PENGUIN ? EL_EMERALD_PURPLE :
1719 i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
1720 i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
1721 i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
1722 i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
1723 i == EL_WALL_EMERALD ? EL_EMERALD :
1724 i == EL_WALL_DIAMOND ? EL_DIAMOND :
1725 i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
1726 i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
1727 i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
1728 i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
1729 i == EL_WALL_PEARL ? EL_PEARL :
1730 i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
1736 int get_num_special_action(int element, int action_first, int action_last)
1738 int num_special_action = 0;
1741 for (i = action_first; i <= action_last; i++)
1743 boolean found = FALSE;
1745 for (j = 0; j < NUM_DIRECTIONS; j++)
1746 if (el_act_dir2img(element, i, j) !=
1747 el_act_dir2img(element, ACTION_DEFAULT, j))
1751 num_special_action++;
1757 printf("::: %d->%d: %d\n", action_first, action_last, num_special_action);
1760 return num_special_action;
1764 =============================================================================
1766 -----------------------------------------------------------------------------
1767 initialize and start new game
1768 =============================================================================
1773 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
1774 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
1775 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
1780 /* don't play tapes over network */
1781 network_playing = (options.network && !tape.playing);
1783 for (i = 0; i < MAX_PLAYERS; i++)
1785 struct PlayerInfo *player = &stored_player[i];
1787 player->index_nr = i;
1788 player->index_bit = (1 << i);
1789 player->element_nr = EL_PLAYER_1 + i;
1791 player->present = FALSE;
1792 player->active = FALSE;
1795 player->effective_action = 0;
1796 player->programmed_action = 0;
1799 player->gems_still_needed = level.gems_needed;
1800 player->sokobanfields_still_needed = 0;
1801 player->lights_still_needed = 0;
1802 player->friends_still_needed = 0;
1804 for (j = 0; j < MAX_NUM_KEYS; j++)
1805 player->key[j] = FALSE;
1807 player->dynabomb_count = 0;
1808 player->dynabomb_size = 1;
1809 player->dynabombs_left = 0;
1810 player->dynabomb_xl = FALSE;
1812 player->MovDir = MV_NONE;
1815 player->GfxDir = MV_NONE;
1816 player->GfxAction = ACTION_DEFAULT;
1818 player->StepFrame = 0;
1820 player->use_murphy = FALSE;
1821 player->artwork_element =
1822 (level.use_artwork_element[i] ? level.artwork_element[i] :
1823 player->element_nr);
1825 player->block_last_field = FALSE; /* initialized in InitPlayerField() */
1826 player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
1828 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
1830 player->actual_frame_counter = 0;
1832 player->step_counter = 0;
1834 player->last_move_dir = MV_NONE;
1836 player->is_waiting = FALSE;
1837 player->is_moving = FALSE;
1838 player->is_auto_moving = FALSE;
1839 player->is_digging = FALSE;
1840 player->is_snapping = FALSE;
1841 player->is_collecting = FALSE;
1842 player->is_pushing = FALSE;
1843 player->is_switching = FALSE;
1844 player->is_dropping = FALSE;
1845 player->is_dropping_pressed = FALSE;
1847 player->is_bored = FALSE;
1848 player->is_sleeping = FALSE;
1850 player->frame_counter_bored = -1;
1851 player->frame_counter_sleeping = -1;
1853 player->anim_delay_counter = 0;
1854 player->post_delay_counter = 0;
1856 player->dir_waiting = MV_NONE;
1857 player->action_waiting = ACTION_DEFAULT;
1858 player->last_action_waiting = ACTION_DEFAULT;
1859 player->special_action_bored = ACTION_DEFAULT;
1860 player->special_action_sleeping = ACTION_DEFAULT;
1863 /* cannot be set here -- could be modified in Init[Player]Field() below */
1865 /* set number of special actions for bored and sleeping animation */
1866 player->num_special_action_bored =
1867 get_num_special_action(player->artwork_element,
1868 ACTION_BORING_1, ACTION_BORING_LAST);
1869 player->num_special_action_sleeping =
1870 get_num_special_action(player->artwork_element,
1871 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
1874 player->switch_x = -1;
1875 player->switch_y = -1;
1877 player->drop_x = -1;
1878 player->drop_y = -1;
1880 player->show_envelope = 0;
1883 SetPlayerMoveSpeed(player, level.initial_player_stepsize, TRUE);
1885 player->move_delay = game.initial_move_delay;
1886 player->move_delay_value = game.initial_move_delay_value;
1888 player->move_delay_value_next = -1;
1890 player->move_delay_reset_counter = 0;
1892 player->cannot_move = FALSE;
1895 player->push_delay = -1; /* initialized when pushing starts */
1896 player->push_delay_value = game.initial_push_delay_value;
1898 player->drop_delay = 0;
1899 player->drop_pressed_delay = 0;
1901 player->last_jx = player->last_jy = 0;
1902 player->jx = player->jy = 0;
1904 player->shield_normal_time_left = 0;
1905 player->shield_deadly_time_left = 0;
1907 player->inventory_infinite_element = EL_UNDEFINED;
1908 player->inventory_size = 0;
1910 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
1911 SnapField(player, 0, 0);
1913 player->LevelSolved = FALSE;
1914 player->GameOver = FALSE;
1917 network_player_action_received = FALSE;
1919 #if defined(NETWORK_AVALIABLE)
1920 /* initial null action */
1921 if (network_playing)
1922 SendToServer_MovePlayer(MV_NONE);
1931 TimeLeft = level.time;
1934 ScreenMovDir = MV_NONE;
1938 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
1940 AllPlayersGone = FALSE;
1942 game.yamyam_content_nr = 0;
1943 game.magic_wall_active = FALSE;
1944 game.magic_wall_time_left = 0;
1945 game.light_time_left = 0;
1946 game.timegate_time_left = 0;
1947 game.switchgate_pos = 0;
1948 game.wind_direction = level.wind_direction_initial;
1949 game.gravity = level.initial_gravity;
1950 game.explosions_delayed = TRUE;
1952 game.lenses_time_left = 0;
1953 game.magnify_time_left = 0;
1955 game.ball_state = level.ball_state_initial;
1956 game.ball_content_nr = 0;
1958 game.envelope_active = FALSE;
1960 game.centered_player_nr = game.centered_player_nr_next = -1; /* focus all */
1961 game.set_centered_player = FALSE;
1963 for (i = 0; i < NUM_BELTS; i++)
1965 game.belt_dir[i] = MV_NONE;
1966 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1969 for (i = 0; i < MAX_NUM_AMOEBA; i++)
1970 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
1973 SCAN_PLAYFIELD(x, y)
1975 for (x = 0; x < lev_fieldx; x++) for (y = 0; y < lev_fieldy; y++)
1978 Feld[x][y] = level.field[x][y];
1979 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
1980 ChangeDelay[x][y] = 0;
1981 ChangePage[x][y] = -1;
1982 #if USE_NEW_CUSTOM_VALUE
1983 CustomValue[x][y] = 0; /* initialized in InitField() */
1985 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
1987 WasJustMoving[x][y] = 0;
1988 WasJustFalling[x][y] = 0;
1989 CheckCollision[x][y] = 0;
1991 Pushed[x][y] = FALSE;
1993 ChangeCount[x][y] = 0;
1994 ChangeEvent[x][y] = -1;
1996 ExplodePhase[x][y] = 0;
1997 ExplodeDelay[x][y] = 0;
1998 ExplodeField[x][y] = EX_TYPE_NONE;
2000 RunnerVisit[x][y] = 0;
2001 PlayerVisit[x][y] = 0;
2004 GfxRandom[x][y] = INIT_GFX_RANDOM();
2005 GfxElement[x][y] = EL_UNDEFINED;
2006 GfxAction[x][y] = ACTION_DEFAULT;
2007 GfxDir[x][y] = MV_NONE;
2011 SCAN_PLAYFIELD(x, y)
2013 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
2016 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
2018 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
2020 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
2023 InitField(x, y, TRUE);
2028 for (i = 0; i < MAX_PLAYERS; i++)
2030 struct PlayerInfo *player = &stored_player[i];
2033 /* set number of special actions for bored and sleeping animation */
2034 player->num_special_action_bored =
2035 get_num_special_action(player->artwork_element,
2036 ACTION_BORING_1, ACTION_BORING_LAST);
2037 player->num_special_action_sleeping =
2038 get_num_special_action(player->artwork_element,
2039 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
2044 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
2045 emulate_sb ? EMU_SOKOBAN :
2046 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
2048 #if USE_NEW_ALL_SLIPPERY
2049 /* initialize type of slippery elements */
2050 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2052 if (!IS_CUSTOM_ELEMENT(i))
2054 /* default: elements slip down either to the left or right randomly */
2055 element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
2057 /* SP style elements prefer to slip down on the left side */
2058 if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
2059 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
2061 /* BD style elements prefer to slip down on the left side */
2062 if (game.emulation == EMU_BOULDERDASH)
2063 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
2068 /* initialize explosion and ignition delay */
2069 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2071 if (!IS_CUSTOM_ELEMENT(i))
2074 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
2075 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
2076 game.emulation == EMU_SUPAPLEX ? 3 : 2);
2077 int last_phase = (num_phase + 1) * delay;
2078 int half_phase = (num_phase / 2) * delay;
2080 element_info[i].explosion_delay = last_phase - 1;
2081 element_info[i].ignition_delay = half_phase;
2083 if (i == EL_BLACK_ORB)
2084 element_info[i].ignition_delay = 1;
2088 if (element_info[i].explosion_delay < 1) /* !!! check again !!! */
2089 element_info[i].explosion_delay = 1;
2091 if (element_info[i].ignition_delay < 1) /* !!! check again !!! */
2092 element_info[i].ignition_delay = 1;
2096 /* correct non-moving belts to start moving left */
2097 for (i = 0; i < NUM_BELTS; i++)
2098 if (game.belt_dir[i] == MV_NONE)
2099 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
2101 /* check if any connected player was not found in playfield */
2102 for (i = 0; i < MAX_PLAYERS; i++)
2104 struct PlayerInfo *player = &stored_player[i];
2106 if (player->connected && !player->present)
2108 for (j = 0; j < MAX_PLAYERS; j++)
2110 struct PlayerInfo *some_player = &stored_player[j];
2111 int jx = some_player->jx, jy = some_player->jy;
2113 /* assign first free player found that is present in the playfield */
2114 if (some_player->present && !some_player->connected)
2116 player->present = TRUE;
2117 player->active = TRUE;
2119 some_player->present = FALSE;
2120 some_player->active = FALSE;
2123 player->element_nr = some_player->element_nr;
2126 player->artwork_element = some_player->artwork_element;
2128 player->block_last_field = some_player->block_last_field;
2129 player->block_delay_adjustment = some_player->block_delay_adjustment;
2131 StorePlayer[jx][jy] = player->element_nr;
2132 player->jx = player->last_jx = jx;
2133 player->jy = player->last_jy = jy;
2143 /* when playing a tape, eliminate all players who do not participate */
2145 for (i = 0; i < MAX_PLAYERS; i++)
2147 if (stored_player[i].active && !tape.player_participates[i])
2149 struct PlayerInfo *player = &stored_player[i];
2150 int jx = player->jx, jy = player->jy;
2152 player->active = FALSE;
2153 StorePlayer[jx][jy] = 0;
2154 Feld[jx][jy] = EL_EMPTY;
2158 else if (!options.network && !setup.team_mode) /* && !tape.playing */
2160 /* when in single player mode, eliminate all but the first active player */
2162 for (i = 0; i < MAX_PLAYERS; i++)
2164 if (stored_player[i].active)
2166 for (j = i + 1; j < MAX_PLAYERS; j++)
2168 if (stored_player[j].active)
2170 struct PlayerInfo *player = &stored_player[j];
2171 int jx = player->jx, jy = player->jy;
2173 player->active = FALSE;
2174 player->present = FALSE;
2176 StorePlayer[jx][jy] = 0;
2177 Feld[jx][jy] = EL_EMPTY;
2184 /* when recording the game, store which players take part in the game */
2187 for (i = 0; i < MAX_PLAYERS; i++)
2188 if (stored_player[i].active)
2189 tape.player_participates[i] = TRUE;
2194 for (i = 0; i < MAX_PLAYERS; i++)
2196 struct PlayerInfo *player = &stored_player[i];
2198 printf("Player %d: present == %d, connected == %d, active == %d.\n",
2203 if (local_player == player)
2204 printf("Player %d is local player.\n", i+1);
2208 if (BorderElement == EL_EMPTY)
2211 SBX_Right = lev_fieldx - SCR_FIELDX;
2213 SBY_Lower = lev_fieldy - SCR_FIELDY;
2218 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
2220 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
2223 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
2224 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
2226 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
2227 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
2229 /* if local player not found, look for custom element that might create
2230 the player (make some assumptions about the right custom element) */
2231 if (!local_player->present)
2233 int start_x = 0, start_y = 0;
2234 int found_rating = 0;
2235 int found_element = EL_UNDEFINED;
2236 int player_nr = local_player->index_nr;
2239 SCAN_PLAYFIELD(x, y)
2241 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
2244 int element = Feld[x][y];
2249 if (level.use_start_element[player_nr] &&
2250 level.start_element[player_nr] == element &&
2257 found_element = element;
2260 if (!IS_CUSTOM_ELEMENT(element))
2263 if (CAN_CHANGE(element))
2265 for (i = 0; i < element_info[element].num_change_pages; i++)
2267 /* check for player created from custom element as single target */
2268 content = element_info[element].change_page[i].target_element;
2269 is_player = ELEM_IS_PLAYER(content);
2271 if (is_player && (found_rating < 3 || element < found_element))
2277 found_element = element;
2282 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
2284 /* check for player created from custom element as explosion content */
2285 content = element_info[element].content.e[xx][yy];
2286 is_player = ELEM_IS_PLAYER(content);
2288 if (is_player && (found_rating < 2 || element < found_element))
2290 start_x = x + xx - 1;
2291 start_y = y + yy - 1;
2294 found_element = element;
2297 if (!CAN_CHANGE(element))
2300 for (i = 0; i < element_info[element].num_change_pages; i++)
2302 /* check for player created from custom element as extended target */
2304 element_info[element].change_page[i].target_content.e[xx][yy];
2306 is_player = ELEM_IS_PLAYER(content);
2308 if (is_player && (found_rating < 1 || element < found_element))
2310 start_x = x + xx - 1;
2311 start_y = y + yy - 1;
2314 found_element = element;
2320 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
2321 start_x > SBX_Right + MIDPOSX ? SBX_Right :
2324 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
2325 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
2330 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2331 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2332 local_player->jx - MIDPOSX);
2334 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2335 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2336 local_player->jy - MIDPOSY);
2339 if (!game.restart_level)
2340 CloseDoor(DOOR_CLOSE_1);
2342 /* !!! FIX THIS (START) !!! */
2343 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2345 InitGameEngine_EM();
2352 /* after drawing the level, correct some elements */
2353 if (game.timegate_time_left == 0)
2354 CloseAllOpenTimegates();
2356 if (setup.soft_scrolling)
2357 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
2359 redraw_mask |= REDRAW_FROM_BACKBUFFER;
2362 /* !!! FIX THIS (END) !!! */
2364 if (!game.restart_level)
2366 /* copy default game door content to main double buffer */
2367 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2368 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2371 DrawGameDoorValues();
2373 if (!game.restart_level)
2377 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
2378 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
2379 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
2383 /* copy actual game door content to door double buffer for OpenDoor() */
2384 BlitBitmap(drawto, bitmap_db_door,
2385 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
2387 OpenDoor(DOOR_OPEN_ALL);
2389 PlaySoundStereo(SND_GAME_STARTING, SOUND_MIDDLE);
2391 if (setup.sound_music)
2394 KeyboardAutoRepeatOffUnlessAutoplay();
2398 for (i = 0; i < MAX_PLAYERS; i++)
2399 printf("Player %d %sactive.\n",
2400 i + 1, (stored_player[i].active ? "" : "not "));
2404 game.restart_level = FALSE;
2407 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
2409 /* this is used for non-R'n'D game engines to update certain engine values */
2411 /* needed to determine if sounds are played within the visible screen area */
2412 scroll_x = actual_scroll_x;
2413 scroll_y = actual_scroll_y;
2416 void InitMovDir(int x, int y)
2418 int i, element = Feld[x][y];
2419 static int xy[4][2] =
2426 static int direction[3][4] =
2428 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
2429 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
2430 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
2439 Feld[x][y] = EL_BUG;
2440 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
2443 case EL_SPACESHIP_RIGHT:
2444 case EL_SPACESHIP_UP:
2445 case EL_SPACESHIP_LEFT:
2446 case EL_SPACESHIP_DOWN:
2447 Feld[x][y] = EL_SPACESHIP;
2448 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
2451 case EL_BD_BUTTERFLY_RIGHT:
2452 case EL_BD_BUTTERFLY_UP:
2453 case EL_BD_BUTTERFLY_LEFT:
2454 case EL_BD_BUTTERFLY_DOWN:
2455 Feld[x][y] = EL_BD_BUTTERFLY;
2456 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
2459 case EL_BD_FIREFLY_RIGHT:
2460 case EL_BD_FIREFLY_UP:
2461 case EL_BD_FIREFLY_LEFT:
2462 case EL_BD_FIREFLY_DOWN:
2463 Feld[x][y] = EL_BD_FIREFLY;
2464 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
2467 case EL_PACMAN_RIGHT:
2469 case EL_PACMAN_LEFT:
2470 case EL_PACMAN_DOWN:
2471 Feld[x][y] = EL_PACMAN;
2472 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
2475 case EL_YAMYAM_LEFT:
2476 case EL_YAMYAM_RIGHT:
2478 case EL_YAMYAM_DOWN:
2479 Feld[x][y] = EL_YAMYAM;
2480 MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
2483 case EL_SP_SNIKSNAK:
2484 MovDir[x][y] = MV_UP;
2487 case EL_SP_ELECTRON:
2488 MovDir[x][y] = MV_LEFT;
2495 Feld[x][y] = EL_MOLE;
2496 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
2500 if (IS_CUSTOM_ELEMENT(element))
2502 struct ElementInfo *ei = &element_info[element];
2503 int move_direction_initial = ei->move_direction_initial;
2504 int move_pattern = ei->move_pattern;
2506 if (move_direction_initial == MV_START_PREVIOUS)
2508 if (MovDir[x][y] != MV_NONE)
2511 move_direction_initial = MV_START_AUTOMATIC;
2514 if (move_direction_initial == MV_START_RANDOM)
2515 MovDir[x][y] = 1 << RND(4);
2516 else if (move_direction_initial & MV_ANY_DIRECTION)
2517 MovDir[x][y] = move_direction_initial;
2518 else if (move_pattern == MV_ALL_DIRECTIONS ||
2519 move_pattern == MV_TURNING_LEFT ||
2520 move_pattern == MV_TURNING_RIGHT ||
2521 move_pattern == MV_TURNING_LEFT_RIGHT ||
2522 move_pattern == MV_TURNING_RIGHT_LEFT ||
2523 move_pattern == MV_TURNING_RANDOM)
2524 MovDir[x][y] = 1 << RND(4);
2525 else if (move_pattern == MV_HORIZONTAL)
2526 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
2527 else if (move_pattern == MV_VERTICAL)
2528 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
2529 else if (move_pattern & MV_ANY_DIRECTION)
2530 MovDir[x][y] = element_info[element].move_pattern;
2531 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
2532 move_pattern == MV_ALONG_RIGHT_SIDE)
2534 /* use random direction as default start direction */
2535 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2536 MovDir[x][y] = 1 << RND(4);
2538 for (i = 0; i < NUM_DIRECTIONS; i++)
2540 int x1 = x + xy[i][0];
2541 int y1 = y + xy[i][1];
2543 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2545 if (move_pattern == MV_ALONG_RIGHT_SIDE)
2546 MovDir[x][y] = direction[0][i];
2548 MovDir[x][y] = direction[1][i];
2557 MovDir[x][y] = 1 << RND(4);
2559 if (element != EL_BUG &&
2560 element != EL_SPACESHIP &&
2561 element != EL_BD_BUTTERFLY &&
2562 element != EL_BD_FIREFLY)
2565 for (i = 0; i < NUM_DIRECTIONS; i++)
2567 int x1 = x + xy[i][0];
2568 int y1 = y + xy[i][1];
2570 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2572 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
2574 MovDir[x][y] = direction[0][i];
2577 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
2578 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
2580 MovDir[x][y] = direction[1][i];
2589 GfxDir[x][y] = MovDir[x][y];
2592 void InitAmoebaNr(int x, int y)
2595 int group_nr = AmoebeNachbarNr(x, y);
2599 for (i = 1; i < MAX_NUM_AMOEBA; i++)
2601 if (AmoebaCnt[i] == 0)
2609 AmoebaNr[x][y] = group_nr;
2610 AmoebaCnt[group_nr]++;
2611 AmoebaCnt2[group_nr]++;
2617 boolean raise_level = FALSE;
2619 if (local_player->MovPos)
2622 if (tape.auto_play) /* tape might already be stopped here */
2623 tape.auto_play_level_solved = TRUE;
2625 local_player->LevelSolved = FALSE;
2627 PlaySoundStereo(SND_GAME_WINNING, SOUND_MIDDLE);
2631 if (!tape.playing && setup.sound_loops)
2632 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2633 SND_CTRL_PLAY_LOOP);
2635 while (TimeLeft > 0)
2637 if (!tape.playing && !setup.sound_loops)
2638 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2640 if (TimeLeft > 100 && TimeLeft % 10 == 0)
2643 RaiseScore(level.score[SC_TIME_BONUS] * 10);
2648 RaiseScore(level.score[SC_TIME_BONUS]);
2651 DrawGameValue_Time(TimeLeft);
2659 if (!tape.playing && setup.sound_loops)
2660 StopSound(SND_GAME_LEVELTIME_BONUS);
2662 else if (level.time == 0) /* level without time limit */
2664 if (!tape.playing && setup.sound_loops)
2665 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2666 SND_CTRL_PLAY_LOOP);
2668 while (TimePlayed < 999)
2670 if (!tape.playing && !setup.sound_loops)
2671 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2673 if (TimePlayed < 900 && TimePlayed % 10 == 0)
2676 RaiseScore(level.score[SC_TIME_BONUS] * 10);
2681 RaiseScore(level.score[SC_TIME_BONUS]);
2684 DrawGameValue_Time(TimePlayed);
2692 if (!tape.playing && setup.sound_loops)
2693 StopSound(SND_GAME_LEVELTIME_BONUS);
2696 /* close exit door after last player */
2697 if (AllPlayersGone && ExitX >= 0 && ExitY >= 0 &&
2698 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
2699 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN))
2701 int element = Feld[ExitX][ExitY];
2703 Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
2704 EL_SP_EXIT_CLOSING);
2706 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
2709 /* player disappears */
2710 if (ExitX >= 0 && ExitY >= 0)
2711 DrawLevelField(ExitX, ExitY);
2717 printf("::: TAPE PLAYING -> DO NOT SAVE SCORE\n");
2719 printf("::: NO TAPE PLAYING -> SAVING SCORE\n");
2725 CloseDoor(DOOR_CLOSE_1);
2730 SaveTape(tape.level_nr); /* Ask to save tape */
2733 if (level_nr == leveldir_current->handicap_level)
2735 leveldir_current->handicap_level++;
2736 SaveLevelSetup_SeriesInfo();
2739 if (level_editor_test_game)
2740 local_player->score = -1; /* no highscore when playing from editor */
2741 else if (level_nr < leveldir_current->last_level)
2742 raise_level = TRUE; /* advance to next level */
2744 if ((hi_pos = NewHiScore()) >= 0)
2746 game_status = GAME_MODE_SCORES;
2747 DrawHallOfFame(hi_pos);
2756 game_status = GAME_MODE_MAIN;
2773 LoadScore(level_nr);
2775 if (strcmp(setup.player_name, EMPTY_PLAYER_NAME) == 0 ||
2776 local_player->score < highscore[MAX_SCORE_ENTRIES - 1].Score)
2779 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
2781 if (local_player->score > highscore[k].Score)
2783 /* player has made it to the hall of fame */
2785 if (k < MAX_SCORE_ENTRIES - 1)
2787 int m = MAX_SCORE_ENTRIES - 1;
2790 for (l = k; l < MAX_SCORE_ENTRIES; l++)
2791 if (!strcmp(setup.player_name, highscore[l].Name))
2793 if (m == k) /* player's new highscore overwrites his old one */
2797 for (l = m; l > k; l--)
2799 strcpy(highscore[l].Name, highscore[l - 1].Name);
2800 highscore[l].Score = highscore[l - 1].Score;
2807 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
2808 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
2809 highscore[k].Score = local_player->score;
2815 else if (!strncmp(setup.player_name, highscore[k].Name,
2816 MAX_PLAYER_NAME_LEN))
2817 break; /* player already there with a higher score */
2823 SaveScore(level_nr);
2828 inline static int getElementMoveStepsize(int x, int y)
2830 int element = Feld[x][y];
2831 int direction = MovDir[x][y];
2832 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2833 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2834 int horiz_move = (dx != 0);
2835 int sign = (horiz_move ? dx : dy);
2836 int step = sign * element_info[element].move_stepsize;
2838 /* special values for move stepsize for spring and things on conveyor belt */
2842 if (element == EL_SPRING)
2843 step = sign * MOVE_STEPSIZE_NORMAL * 2;
2844 else if (CAN_FALL(element) && !CAN_MOVE(element) &&
2845 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
2846 step = sign * MOVE_STEPSIZE_NORMAL / 2;
2848 if (CAN_FALL(element) &&
2849 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
2850 step = sign * MOVE_STEPSIZE_NORMAL / 2;
2851 else if (element == EL_SPRING)
2852 step = sign * MOVE_STEPSIZE_NORMAL * 2;
2859 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
2861 if (player->GfxAction != action || player->GfxDir != dir)
2864 printf("Player frame reset! (%d => %d, %d => %d)\n",
2865 player->GfxAction, action, player->GfxDir, dir);
2868 player->GfxAction = action;
2869 player->GfxDir = dir;
2871 player->StepFrame = 0;
2875 static void ResetRandomAnimationValue(int x, int y)
2877 GfxRandom[x][y] = INIT_GFX_RANDOM();
2880 static void ResetGfxAnimation(int x, int y)
2883 int element, graphic;
2887 GfxAction[x][y] = ACTION_DEFAULT;
2888 GfxDir[x][y] = MovDir[x][y];
2891 element = Feld[x][y];
2892 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
2894 if (graphic_info[graphic].anim_global_sync)
2895 GfxFrame[x][y] = FrameCounter;
2896 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
2897 GfxFrame[x][y] = CustomValue[x][y];
2898 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2899 GfxFrame[x][y] = element_info[element].collect_score;
2903 void InitMovingField(int x, int y, int direction)
2905 int element = Feld[x][y];
2909 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2910 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2914 if (!WasJustMoving[x][y] || direction != MovDir[x][y])
2915 ResetGfxAnimation(x, y);
2917 MovDir[x][y] = direction;
2918 GfxDir[x][y] = direction;
2919 GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
2920 ACTION_FALLING : ACTION_MOVING);
2923 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
2925 if (graphic_info[graphic].anim_global_sync)
2926 GfxFrame[x][y] = FrameCounter;
2927 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
2928 GfxFrame[x][y] = CustomValue[x][y];
2929 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2930 GfxFrame[x][y] = element_info[element].collect_score;
2933 /* this is needed for CEs with property "can move" / "not moving" */
2935 if (getElementMoveStepsize(x, y) != 0) /* moving or being moved */
2937 if (Feld[newx][newy] == EL_EMPTY)
2938 Feld[newx][newy] = EL_BLOCKED;
2940 MovDir[newx][newy] = MovDir[x][y];
2942 #if USE_NEW_CUSTOM_VALUE
2943 CustomValue[newx][newy] = CustomValue[x][y];
2946 GfxFrame[newx][newy] = GfxFrame[x][y];
2947 GfxRandom[newx][newy] = GfxRandom[x][y];
2948 GfxAction[newx][newy] = GfxAction[x][y];
2949 GfxDir[newx][newy] = GfxDir[x][y];
2953 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
2955 int direction = MovDir[x][y];
2957 int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
2958 int newy = y + (direction & MV_UP ? -1 : direction & MV_DOWN ? +1 : 0);
2960 int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2961 int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2968 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
2970 int oldx = x, oldy = y;
2971 int direction = MovDir[x][y];
2973 if (direction == MV_LEFT)
2975 else if (direction == MV_RIGHT)
2977 else if (direction == MV_UP)
2979 else if (direction == MV_DOWN)
2982 *comes_from_x = oldx;
2983 *comes_from_y = oldy;
2986 int MovingOrBlocked2Element(int x, int y)
2988 int element = Feld[x][y];
2990 if (element == EL_BLOCKED)
2994 Blocked2Moving(x, y, &oldx, &oldy);
2995 return Feld[oldx][oldy];
3001 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
3003 /* like MovingOrBlocked2Element(), but if element is moving
3004 and (x,y) is the field the moving element is just leaving,
3005 return EL_BLOCKED instead of the element value */
3006 int element = Feld[x][y];
3008 if (IS_MOVING(x, y))
3010 if (element == EL_BLOCKED)
3014 Blocked2Moving(x, y, &oldx, &oldy);
3015 return Feld[oldx][oldy];
3024 static void RemoveField(int x, int y)
3026 Feld[x][y] = EL_EMPTY;
3032 #if USE_NEW_CUSTOM_VALUE
3033 CustomValue[x][y] = 0;
3037 ChangeDelay[x][y] = 0;
3038 ChangePage[x][y] = -1;
3039 Pushed[x][y] = FALSE;
3042 ExplodeField[x][y] = EX_TYPE_NONE;
3045 GfxElement[x][y] = EL_UNDEFINED;
3046 GfxAction[x][y] = ACTION_DEFAULT;
3047 GfxDir[x][y] = MV_NONE;
3050 void RemoveMovingField(int x, int y)
3052 int oldx = x, oldy = y, newx = x, newy = y;
3053 int element = Feld[x][y];
3054 int next_element = EL_UNDEFINED;
3056 if (element != EL_BLOCKED && !IS_MOVING(x, y))
3059 if (IS_MOVING(x, y))
3061 Moving2Blocked(x, y, &newx, &newy);
3063 if (Feld[newx][newy] != EL_BLOCKED)
3065 /* element is moving, but target field is not free (blocked), but
3066 already occupied by something different (example: acid pool);
3067 in this case, only remove the moving field, but not the target */
3069 RemoveField(oldx, oldy);
3071 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
3073 DrawLevelField(oldx, oldy);
3078 else if (element == EL_BLOCKED)
3080 Blocked2Moving(x, y, &oldx, &oldy);
3081 if (!IS_MOVING(oldx, oldy))
3085 if (element == EL_BLOCKED &&
3086 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
3087 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
3088 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
3089 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
3090 next_element = get_next_element(Feld[oldx][oldy]);
3092 RemoveField(oldx, oldy);
3093 RemoveField(newx, newy);
3095 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
3097 if (next_element != EL_UNDEFINED)
3098 Feld[oldx][oldy] = next_element;
3100 DrawLevelField(oldx, oldy);
3101 DrawLevelField(newx, newy);
3104 void DrawDynamite(int x, int y)
3106 int sx = SCREENX(x), sy = SCREENY(y);
3107 int graphic = el2img(Feld[x][y]);
3110 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
3113 if (IS_WALKABLE_INSIDE(Back[x][y]))
3117 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
3118 else if (Store[x][y])
3119 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
3121 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
3123 if (Back[x][y] || Store[x][y])
3124 DrawGraphicThruMask(sx, sy, graphic, frame);
3126 DrawGraphic(sx, sy, graphic, frame);
3129 void CheckDynamite(int x, int y)
3131 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
3135 if (MovDelay[x][y] != 0)
3138 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
3144 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
3151 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
3153 boolean num_checked_players = 0;
3156 for (i = 0; i < MAX_PLAYERS; i++)
3158 if (stored_player[i].active)
3160 int sx = stored_player[i].jx;
3161 int sy = stored_player[i].jy;
3163 if (num_checked_players == 0)
3170 *sx1 = MIN(*sx1, sx);
3171 *sy1 = MIN(*sy1, sy);
3172 *sx2 = MAX(*sx2, sx);
3173 *sy2 = MAX(*sy2, sy);
3176 num_checked_players++;
3181 static boolean checkIfAllPlayersFitToScreen_RND()
3183 int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
3185 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
3187 return (sx2 - sx1 < SCR_FIELDX &&
3188 sy2 - sy1 < SCR_FIELDY);
3191 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
3193 int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
3195 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
3197 *sx = (sx1 + sx2) / 2;
3198 *sy = (sy1 + sy2) / 2;
3202 static void setMaxCenterDistanceForAllPlayers(int *max_dx, int *max_dy,
3203 int center_x, int center_y)
3205 int sx1 = center_x, sy1 = center_y, sx2 = center_x, sy2 = center_y;
3207 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
3209 *max_dx = MAX(ABS(sx1 - center_x), ABS(sx2 - center_x));
3210 *max_dy = MAX(ABS(sy1 - center_y), ABS(sy2 - center_y));
3213 static boolean checkIfAllPlayersAreVisible(int center_x, int center_y)
3217 setMaxCenterDistanceForAllPlayers(&max_dx, &max_dy, center_x, center_y);
3219 return (max_dx <= SCR_FIELDX / 2 &&
3220 max_dy <= SCR_FIELDY / 2);
3228 void DrawRelocateScreen(int x, int y, int move_dir, boolean center_screen,
3229 boolean quick_relocation)
3231 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3232 boolean no_delay = (tape.warp_forward);
3233 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
3234 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
3236 if (quick_relocation)
3238 int offset = (setup.scroll_delay ? 3 : 0);
3245 if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
3247 scroll_x = (x < SBX_Left + MIDPOSX ? SBX_Left :
3248 x > SBX_Right + MIDPOSX ? SBX_Right :
3251 scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
3252 y > SBY_Lower + MIDPOSY ? SBY_Lower :
3257 if ((move_dir == MV_LEFT && scroll_x > x - MIDPOSX + offset) ||
3258 (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
3259 scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
3261 if ((move_dir == MV_UP && scroll_y > y - MIDPOSY + offset) ||
3262 (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
3263 scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
3265 /* don't scroll over playfield boundaries */
3266 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
3267 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
3269 /* don't scroll over playfield boundaries */
3270 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
3271 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
3274 RedrawPlayfield(TRUE, 0,0,0,0);
3278 int scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left :
3279 x > SBX_Right + MIDPOSX ? SBX_Right :
3282 int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
3283 y > SBY_Lower + MIDPOSY ? SBY_Lower :
3286 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
3288 while (scroll_x != scroll_xx || scroll_y != scroll_yy)
3291 int fx = FX, fy = FY;
3293 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
3294 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
3296 if (dx == 0 && dy == 0) /* no scrolling needed at all */
3302 fx += dx * TILEX / 2;
3303 fy += dy * TILEY / 2;
3305 ScrollLevel(dx, dy);
3308 /* scroll in two steps of half tile size to make things smoother */
3309 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
3311 Delay(wait_delay_value);
3313 /* scroll second step to align at full tile size */
3315 Delay(wait_delay_value);
3320 Delay(wait_delay_value);
3326 void DrawRelocatePlayer(struct PlayerInfo *player, boolean quick_relocation)
3328 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3329 boolean no_delay = (tape.warp_forward);
3330 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
3331 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
3332 int jx = player->jx;
3333 int jy = player->jy;
3335 if (quick_relocation)
3337 int offset = (setup.scroll_delay ? 3 : 0);
3339 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
3341 scroll_x = (player->jx < SBX_Left + MIDPOSX ? SBX_Left :
3342 player->jx > SBX_Right + MIDPOSX ? SBX_Right :
3343 player->jx - MIDPOSX);
3345 scroll_y = (player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
3346 player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
3347 player->jy - MIDPOSY);
3351 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
3352 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
3353 scroll_x = jx - MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
3355 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
3356 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
3357 scroll_y = jy - MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
3359 /* don't scroll over playfield boundaries */
3360 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
3361 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
3363 /* don't scroll over playfield boundaries */
3364 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
3365 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
3368 RedrawPlayfield(TRUE, 0,0,0,0);
3372 int scroll_xx = (player->jx < SBX_Left + MIDPOSX ? SBX_Left :
3373 player->jx > SBX_Right + MIDPOSX ? SBX_Right :
3374 player->jx - MIDPOSX);
3376 int scroll_yy = (player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
3377 player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
3378 player->jy - MIDPOSY);
3380 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
3382 while (scroll_x != scroll_xx || scroll_y != scroll_yy)
3385 int fx = FX, fy = FY;
3387 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
3388 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
3390 if (dx == 0 && dy == 0) /* no scrolling needed at all */
3396 fx += dx * TILEX / 2;
3397 fy += dy * TILEY / 2;
3399 ScrollLevel(dx, dy);
3402 /* scroll in two steps of half tile size to make things smoother */
3403 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
3405 Delay(wait_delay_value);
3407 /* scroll second step to align at full tile size */
3409 Delay(wait_delay_value);
3414 Delay(wait_delay_value);
3420 void RelocatePlayer(int jx, int jy, int el_player_raw)
3422 int el_player = GET_PLAYER_ELEMENT(el_player_raw);
3423 int player_nr = GET_PLAYER_NR(el_player);
3424 struct PlayerInfo *player = &stored_player[player_nr];
3425 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3426 boolean no_delay = (tape.warp_forward);
3427 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
3428 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
3429 int old_jx = player->jx;
3430 int old_jy = player->jy;
3431 int old_element = Feld[old_jx][old_jy];
3432 int element = Feld[jx][jy];
3433 boolean player_relocated = (old_jx != jx || old_jy != jy);
3435 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
3436 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
3437 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
3438 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
3439 int leave_side_horiz = move_dir_horiz;
3440 int leave_side_vert = move_dir_vert;
3441 int enter_side = enter_side_horiz | enter_side_vert;
3442 int leave_side = leave_side_horiz | leave_side_vert;
3444 if (player->GameOver) /* do not reanimate dead player */
3447 if (!player_relocated) /* no need to relocate the player */
3450 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
3452 RemoveField(jx, jy); /* temporarily remove newly placed player */
3453 DrawLevelField(jx, jy);
3456 if (player->present)
3458 while (player->MovPos)
3460 ScrollPlayer(player, SCROLL_GO_ON);
3461 ScrollScreen(NULL, SCROLL_GO_ON);
3463 AdvanceFrameAndPlayerCounters(player->index_nr);
3468 Delay(wait_delay_value);
3471 DrawPlayer(player); /* needed here only to cleanup last field */
3472 DrawLevelField(player->jx, player->jy); /* remove player graphic */
3474 player->is_moving = FALSE;
3477 if (IS_CUSTOM_ELEMENT(old_element))
3478 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
3480 player->index_bit, leave_side);
3482 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
3484 player->index_bit, leave_side);
3486 Feld[jx][jy] = el_player;
3487 InitPlayerField(jx, jy, el_player, TRUE);
3489 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
3491 Feld[jx][jy] = element;
3492 InitField(jx, jy, FALSE);
3496 /* only visually relocate centered player */
3498 DrawRelocateScreen(player->jx, player->jy, player->MovDir, FALSE,
3499 level.instant_relocation);
3501 if (player->index_nr == game.centered_player_nr)
3502 DrawRelocatePlayer(player, level.instant_relocation);
3505 if (player == local_player) /* only visually relocate local player */
3506 DrawRelocatePlayer(player, level.instant_relocation);
3509 TestIfPlayerTouchesBadThing(jx, jy);
3510 TestIfPlayerTouchesCustomElement(jx, jy);
3512 if (IS_CUSTOM_ELEMENT(element))
3513 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
3514 player->index_bit, enter_side);
3516 CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
3517 player->index_bit, enter_side);
3520 void Explode(int ex, int ey, int phase, int mode)
3526 /* !!! eliminate this variable !!! */
3527 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
3529 if (game.explosions_delayed)
3531 ExplodeField[ex][ey] = mode;
3535 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
3537 int center_element = Feld[ex][ey];
3538 int artwork_element, explosion_element; /* set these values later */
3541 /* --- This is only really needed (and now handled) in "Impact()". --- */
3542 /* do not explode moving elements that left the explode field in time */
3543 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
3544 center_element == EL_EMPTY &&
3545 (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
3550 /* !!! at this place, the center element may be EL_BLOCKED !!! */
3551 if (mode == EX_TYPE_NORMAL ||
3552 mode == EX_TYPE_CENTER ||
3553 mode == EX_TYPE_CROSS)
3554 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
3557 /* remove things displayed in background while burning dynamite */
3558 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
3561 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
3563 /* put moving element to center field (and let it explode there) */
3564 center_element = MovingOrBlocked2Element(ex, ey);
3565 RemoveMovingField(ex, ey);
3566 Feld[ex][ey] = center_element;
3569 /* now "center_element" is finally determined -- set related values now */
3570 artwork_element = center_element; /* for custom player artwork */
3571 explosion_element = center_element; /* for custom player artwork */
3573 if (IS_PLAYER(ex, ey))
3575 int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
3577 artwork_element = stored_player[player_nr].artwork_element;
3579 if (level.use_explosion_element[player_nr])
3581 explosion_element = level.explosion_element[player_nr];
3582 artwork_element = explosion_element;
3587 if (mode == EX_TYPE_NORMAL ||
3588 mode == EX_TYPE_CENTER ||
3589 mode == EX_TYPE_CROSS)
3590 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
3594 last_phase = element_info[explosion_element].explosion_delay + 1;
3596 last_phase = element_info[center_element].explosion_delay + 1;
3599 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
3601 int xx = x - ex + 1;
3602 int yy = y - ey + 1;
3605 if (!IN_LEV_FIELD(x, y) ||
3606 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
3607 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
3610 element = Feld[x][y];
3612 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
3614 element = MovingOrBlocked2Element(x, y);
3616 if (!IS_EXPLOSION_PROOF(element))
3617 RemoveMovingField(x, y);
3620 /* indestructible elements can only explode in center (but not flames) */
3621 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
3622 mode == EX_TYPE_BORDER)) ||
3623 element == EL_FLAMES)
3626 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
3627 behaviour, for example when touching a yamyam that explodes to rocks
3628 with active deadly shield, a rock is created under the player !!! */
3629 /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
3631 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
3632 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
3633 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
3635 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
3638 if (IS_ACTIVE_BOMB(element))
3640 /* re-activate things under the bomb like gate or penguin */
3641 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
3648 /* save walkable background elements while explosion on same tile */
3649 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
3650 (x != ex || y != ey || mode == EX_TYPE_BORDER))
3651 Back[x][y] = element;
3653 /* ignite explodable elements reached by other explosion */
3654 if (element == EL_EXPLOSION)
3655 element = Store2[x][y];
3657 if (AmoebaNr[x][y] &&
3658 (element == EL_AMOEBA_FULL ||
3659 element == EL_BD_AMOEBA ||
3660 element == EL_AMOEBA_GROWING))
3662 AmoebaCnt[AmoebaNr[x][y]]--;
3663 AmoebaCnt2[AmoebaNr[x][y]]--;
3668 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
3671 int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
3673 Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
3675 switch(StorePlayer[ex][ey])
3678 Store[x][y] = EL_PLAYER_IS_EXPLODING_2;
3681 Store[x][y] = EL_PLAYER_IS_EXPLODING_3;
3684 Store[x][y] = EL_PLAYER_IS_EXPLODING_4;
3688 Store[x][y] = EL_PLAYER_IS_EXPLODING_1;
3693 if (PLAYERINFO(ex, ey)->use_murphy)
3694 Store[x][y] = EL_EMPTY;
3697 /* !!! check this case -- currently needed for rnd_rado_negundo_v,
3698 !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
3699 else if (ELEM_IS_PLAYER(center_element))
3700 Store[x][y] = EL_EMPTY;
3701 else if (center_element == EL_YAMYAM)
3702 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
3703 else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
3704 Store[x][y] = element_info[center_element].content.e[xx][yy];
3706 /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
3707 (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
3708 otherwise) -- FIX THIS !!! */
3709 else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
3710 Store[x][y] = element_info[element].content.e[1][1];
3712 else if (!CAN_EXPLODE(element))
3713 Store[x][y] = element_info[element].content.e[1][1];
3716 Store[x][y] = EL_EMPTY;
3718 else if (center_element == EL_MOLE)
3719 Store[x][y] = EL_EMERALD_RED;
3720 else if (center_element == EL_PENGUIN)
3721 Store[x][y] = EL_EMERALD_PURPLE;
3722 else if (center_element == EL_BUG)
3723 Store[x][y] = ((x == ex && y == ey) ? EL_DIAMOND : EL_EMERALD);
3724 else if (center_element == EL_BD_BUTTERFLY)
3725 Store[x][y] = EL_BD_DIAMOND;
3726 else if (center_element == EL_SP_ELECTRON)
3727 Store[x][y] = EL_SP_INFOTRON;
3728 else if (center_element == EL_AMOEBA_TO_DIAMOND)
3729 Store[x][y] = level.amoeba_content;
3730 else if (center_element == EL_YAMYAM)
3731 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
3732 else if (IS_CUSTOM_ELEMENT(center_element) &&
3733 element_info[center_element].content.e[xx][yy] != EL_EMPTY)
3734 Store[x][y] = element_info[center_element].content.e[xx][yy];
3735 else if (element == EL_WALL_EMERALD)
3736 Store[x][y] = EL_EMERALD;
3737 else if (element == EL_WALL_DIAMOND)
3738 Store[x][y] = EL_DIAMOND;
3739 else if (element == EL_WALL_BD_DIAMOND)
3740 Store[x][y] = EL_BD_DIAMOND;
3741 else if (element == EL_WALL_EMERALD_YELLOW)
3742 Store[x][y] = EL_EMERALD_YELLOW;
3743 else if (element == EL_WALL_EMERALD_RED)
3744 Store[x][y] = EL_EMERALD_RED;
3745 else if (element == EL_WALL_EMERALD_PURPLE)
3746 Store[x][y] = EL_EMERALD_PURPLE;
3747 else if (element == EL_WALL_PEARL)
3748 Store[x][y] = EL_PEARL;
3749 else if (element == EL_WALL_CRYSTAL)
3750 Store[x][y] = EL_CRYSTAL;
3751 else if (IS_CUSTOM_ELEMENT(element) && !CAN_EXPLODE(element))
3752 Store[x][y] = element_info[element].content.e[1][1];
3754 Store[x][y] = EL_EMPTY;
3757 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
3758 center_element == EL_AMOEBA_TO_DIAMOND)
3759 Store2[x][y] = element;
3761 Feld[x][y] = EL_EXPLOSION;
3762 GfxElement[x][y] = artwork_element;
3765 printf(":: setting gfx(%d,%d) to %d ['%s']\n",
3766 x, y, artwork_element, EL_NAME(artwork_element));
3769 ExplodePhase[x][y] = 1;
3770 ExplodeDelay[x][y] = last_phase;
3775 if (center_element == EL_YAMYAM)
3776 game.yamyam_content_nr =
3777 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
3789 GfxFrame[x][y] = 0; /* restart explosion animation */
3791 last_phase = ExplodeDelay[x][y];
3793 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
3797 /* activate this even in non-DEBUG version until cause for crash in
3798 getGraphicAnimationFrame() (see below) is found and eliminated */
3804 /* this can happen if the player leaves an explosion just in time */
3805 if (GfxElement[x][y] == EL_UNDEFINED)
3806 GfxElement[x][y] = EL_EMPTY;
3808 if (GfxElement[x][y] == EL_UNDEFINED)
3811 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
3812 printf("Explode(): This should never happen!\n");
3815 GfxElement[x][y] = EL_EMPTY;
3821 border_element = Store2[x][y];
3822 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3823 border_element = StorePlayer[x][y];
3825 if (phase == element_info[border_element].ignition_delay ||
3826 phase == last_phase)
3828 boolean border_explosion = FALSE;
3830 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
3831 !PLAYER_EXPLOSION_PROTECTED(x, y))
3833 KillPlayerUnlessExplosionProtected(x, y);
3834 border_explosion = TRUE;
3836 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
3838 Feld[x][y] = Store2[x][y];
3841 border_explosion = TRUE;
3843 else if (border_element == EL_AMOEBA_TO_DIAMOND)
3845 AmoebeUmwandeln(x, y);
3847 border_explosion = TRUE;
3850 /* if an element just explodes due to another explosion (chain-reaction),
3851 do not immediately end the new explosion when it was the last frame of
3852 the explosion (as it would be done in the following "if"-statement!) */
3853 if (border_explosion && phase == last_phase)
3857 if (phase == last_phase)
3861 element = Feld[x][y] = Store[x][y];
3862 Store[x][y] = Store2[x][y] = 0;
3863 GfxElement[x][y] = EL_UNDEFINED;
3865 /* player can escape from explosions and might therefore be still alive */
3866 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
3867 element <= EL_PLAYER_IS_EXPLODING_4)
3869 int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
3870 int explosion_element = EL_PLAYER_1 + player_nr;
3871 int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
3872 int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
3874 if (level.use_explosion_element[player_nr])
3875 explosion_element = level.explosion_element[player_nr];
3877 Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
3878 element_info[explosion_element].content.e[xx][yy]);
3881 /* restore probably existing indestructible background element */
3882 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
3883 element = Feld[x][y] = Back[x][y];
3886 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
3887 GfxDir[x][y] = MV_NONE;
3888 ChangeDelay[x][y] = 0;
3889 ChangePage[x][y] = -1;
3891 #if USE_NEW_CUSTOM_VALUE
3892 CustomValue[x][y] = 0;
3895 InitField_WithBug2(x, y, FALSE);
3897 DrawLevelField(x, y);
3899 TestIfElementTouchesCustomElement(x, y);
3901 if (GFX_CRUMBLED(element))
3902 DrawLevelFieldCrumbledSandNeighbours(x, y);
3904 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
3905 StorePlayer[x][y] = 0;
3907 if (ELEM_IS_PLAYER(element))
3908 RelocatePlayer(x, y, element);
3910 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3912 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
3913 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
3916 DrawLevelFieldCrumbledSand(x, y);
3918 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
3920 DrawLevelElement(x, y, Back[x][y]);
3921 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
3923 else if (IS_WALKABLE_UNDER(Back[x][y]))
3925 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3926 DrawLevelElementThruMask(x, y, Back[x][y]);
3928 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
3929 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3933 void DynaExplode(int ex, int ey)
3936 int dynabomb_element = Feld[ex][ey];
3937 int dynabomb_size = 1;
3938 boolean dynabomb_xl = FALSE;
3939 struct PlayerInfo *player;
3940 static int xy[4][2] =
3948 if (IS_ACTIVE_BOMB(dynabomb_element))
3950 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
3951 dynabomb_size = player->dynabomb_size;
3952 dynabomb_xl = player->dynabomb_xl;
3953 player->dynabombs_left++;
3956 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
3958 for (i = 0; i < NUM_DIRECTIONS; i++)
3960 for (j = 1; j <= dynabomb_size; j++)
3962 int x = ex + j * xy[i][0];
3963 int y = ey + j * xy[i][1];
3966 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
3969 element = Feld[x][y];
3971 /* do not restart explosions of fields with active bombs */
3972 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
3975 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
3977 if (element != EL_EMPTY && element != EL_EXPLOSION &&
3978 !IS_DIGGABLE(element) && !dynabomb_xl)
3984 void Bang(int x, int y)
3986 int element = MovingOrBlocked2Element(x, y);
3987 int explosion_type = EX_TYPE_NORMAL;
3989 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3991 struct PlayerInfo *player = PLAYERINFO(x, y);
3993 element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
3994 player->element_nr);
3996 if (level.use_explosion_element[player->index_nr])
3998 int explosion_element = level.explosion_element[player->index_nr];
4000 if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
4001 explosion_type = EX_TYPE_CROSS;
4002 else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
4003 explosion_type = EX_TYPE_CENTER;
4011 case EL_BD_BUTTERFLY:
4014 case EL_DARK_YAMYAM:
4018 RaiseScoreElement(element);
4021 case EL_DYNABOMB_PLAYER_1_ACTIVE:
4022 case EL_DYNABOMB_PLAYER_2_ACTIVE:
4023 case EL_DYNABOMB_PLAYER_3_ACTIVE:
4024 case EL_DYNABOMB_PLAYER_4_ACTIVE:
4025 case EL_DYNABOMB_INCREASE_NUMBER:
4026 case EL_DYNABOMB_INCREASE_SIZE:
4027 case EL_DYNABOMB_INCREASE_POWER:
4028 explosion_type = EX_TYPE_DYNA;
4033 case EL_LAMP_ACTIVE:
4034 case EL_AMOEBA_TO_DIAMOND:
4035 if (!IS_PLAYER(x, y)) /* penguin and player may be at same field */
4036 explosion_type = EX_TYPE_CENTER;
4040 if (element_info[element].explosion_type == EXPLODES_CROSS)
4041 explosion_type = EX_TYPE_CROSS;
4042 else if (element_info[element].explosion_type == EXPLODES_1X1)
4043 explosion_type = EX_TYPE_CENTER;
4047 if (explosion_type == EX_TYPE_DYNA)
4050 Explode(x, y, EX_PHASE_START, explosion_type);
4052 CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
4055 void SplashAcid(int x, int y)
4057 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
4058 (!IN_LEV_FIELD(x - 1, y - 2) ||
4059 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
4060 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
4062 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
4063 (!IN_LEV_FIELD(x + 1, y - 2) ||
4064 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
4065 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
4067 PlayLevelSound(x, y, SND_ACID_SPLASHING);
4070 static void InitBeltMovement()
4072 static int belt_base_element[4] =
4074 EL_CONVEYOR_BELT_1_LEFT,
4075 EL_CONVEYOR_BELT_2_LEFT,
4076 EL_CONVEYOR_BELT_3_LEFT,
4077 EL_CONVEYOR_BELT_4_LEFT
4079 static int belt_base_active_element[4] =
4081 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
4082 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
4083 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
4084 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
4089 /* set frame order for belt animation graphic according to belt direction */
4090 for (i = 0; i < NUM_BELTS; i++)
4094 for (j = 0; j < NUM_BELT_PARTS; j++)
4096 int element = belt_base_active_element[belt_nr] + j;
4097 int graphic = el2img(element);
4099 if (game.belt_dir[i] == MV_LEFT)
4100 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
4102 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
4107 SCAN_PLAYFIELD(x, y)
4109 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
4112 int element = Feld[x][y];
4114 for (i = 0; i < NUM_BELTS; i++)
4116 if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
4118 int e_belt_nr = getBeltNrFromBeltElement(element);
4121 if (e_belt_nr == belt_nr)
4123 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
4125 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
4132 static void ToggleBeltSwitch(int x, int y)
4134 static int belt_base_element[4] =
4136 EL_CONVEYOR_BELT_1_LEFT,
4137 EL_CONVEYOR_BELT_2_LEFT,
4138 EL_CONVEYOR_BELT_3_LEFT,
4139 EL_CONVEYOR_BELT_4_LEFT
4141 static int belt_base_active_element[4] =
4143 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
4144 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
4145 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
4146 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
4148 static int belt_base_switch_element[4] =
4150 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
4151 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
4152 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
4153 EL_CONVEYOR_BELT_4_SWITCH_LEFT
4155 static int belt_move_dir[4] =
4163 int element = Feld[x][y];
4164 int belt_nr = getBeltNrFromBeltSwitchElement(element);
4165 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
4166 int belt_dir = belt_move_dir[belt_dir_nr];
4169 if (!IS_BELT_SWITCH(element))
4172 game.belt_dir_nr[belt_nr] = belt_dir_nr;
4173 game.belt_dir[belt_nr] = belt_dir;
4175 if (belt_dir_nr == 3)
4178 /* set frame order for belt animation graphic according to belt direction */
4179 for (i = 0; i < NUM_BELT_PARTS; i++)
4181 int element = belt_base_active_element[belt_nr] + i;
4182 int graphic = el2img(element);
4184 if (belt_dir == MV_LEFT)
4185 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
4187 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
4191 SCAN_PLAYFIELD(xx, yy)
4193 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
4196 int element = Feld[xx][yy];
4198 if (IS_BELT_SWITCH(element))
4200 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
4202 if (e_belt_nr == belt_nr)
4204 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
4205 DrawLevelField(xx, yy);
4208 else if (IS_BELT(element) && belt_dir != MV_NONE)
4210 int e_belt_nr = getBeltNrFromBeltElement(element);
4212 if (e_belt_nr == belt_nr)
4214 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
4216 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
4217 DrawLevelField(xx, yy);
4220 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
4222 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
4224 if (e_belt_nr == belt_nr)
4226 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
4228 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
4229 DrawLevelField(xx, yy);
4235 static void ToggleSwitchgateSwitch(int x, int y)
4239 game.switchgate_pos = !game.switchgate_pos;
4242 SCAN_PLAYFIELD(xx, yy)
4244 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
4247 int element = Feld[xx][yy];
4249 if (element == EL_SWITCHGATE_SWITCH_UP ||
4250 element == EL_SWITCHGATE_SWITCH_DOWN)
4252 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
4253 DrawLevelField(xx, yy);
4255 else if (element == EL_SWITCHGATE_OPEN ||
4256 element == EL_SWITCHGATE_OPENING)
4258 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
4260 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
4262 else if (element == EL_SWITCHGATE_CLOSED ||
4263 element == EL_SWITCHGATE_CLOSING)
4265 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
4267 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
4272 static int getInvisibleActiveFromInvisibleElement(int element)
4274 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
4275 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
4276 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
4280 static int getInvisibleFromInvisibleActiveElement(int element)
4282 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
4283 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
4284 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
4288 static void RedrawAllLightSwitchesAndInvisibleElements()
4293 SCAN_PLAYFIELD(x, y)
4295 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
4298 int element = Feld[x][y];
4300 if (element == EL_LIGHT_SWITCH &&
4301 game.light_time_left > 0)
4303 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
4304 DrawLevelField(x, y);
4306 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
4307 game.light_time_left == 0)
4309 Feld[x][y] = EL_LIGHT_SWITCH;
4310 DrawLevelField(x, y);
4312 else if (element == EL_EMC_DRIPPER &&
4313 game.light_time_left > 0)
4315 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
4316 DrawLevelField(x, y);
4318 else if (element == EL_EMC_DRIPPER_ACTIVE &&
4319 game.light_time_left == 0)
4321 Feld[x][y] = EL_EMC_DRIPPER;
4322 DrawLevelField(x, y);
4324 else if (element == EL_INVISIBLE_STEELWALL ||
4325 element == EL_INVISIBLE_WALL ||
4326 element == EL_INVISIBLE_SAND)
4328 if (game.light_time_left > 0)
4329 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
4331 DrawLevelField(x, y);
4333 /* uncrumble neighbour fields, if needed */
4334 if (element == EL_INVISIBLE_SAND)
4335 DrawLevelFieldCrumbledSandNeighbours(x, y);
4337 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
4338 element == EL_INVISIBLE_WALL_ACTIVE ||
4339 element == EL_INVISIBLE_SAND_ACTIVE)
4341 if (game.light_time_left == 0)
4342 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
4344 DrawLevelField(x, y);
4346 /* re-crumble neighbour fields, if needed */
4347 if (element == EL_INVISIBLE_SAND)
4348 DrawLevelFieldCrumbledSandNeighbours(x, y);
4353 static void RedrawAllInvisibleElementsForLenses()
4358 SCAN_PLAYFIELD(x, y)
4360 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
4363 int element = Feld[x][y];
4365 if (element == EL_EMC_DRIPPER &&
4366 game.lenses_time_left > 0)
4368 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
4369 DrawLevelField(x, y);
4371 else if (element == EL_EMC_DRIPPER_ACTIVE &&
4372 game.lenses_time_left == 0)
4374 Feld[x][y] = EL_EMC_DRIPPER;
4375 DrawLevelField(x, y);
4377 else if (element == EL_INVISIBLE_STEELWALL ||
4378 element == EL_INVISIBLE_WALL ||
4379 element == EL_INVISIBLE_SAND)
4381 if (game.lenses_time_left > 0)
4382 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
4384 DrawLevelField(x, y);
4386 /* uncrumble neighbour fields, if needed */
4387 if (element == EL_INVISIBLE_SAND)
4388 DrawLevelFieldCrumbledSandNeighbours(x, y);
4390 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
4391 element == EL_INVISIBLE_WALL_ACTIVE ||
4392 element == EL_INVISIBLE_SAND_ACTIVE)
4394 if (game.lenses_time_left == 0)
4395 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
4397 DrawLevelField(x, y);
4399 /* re-crumble neighbour fields, if needed */
4400 if (element == EL_INVISIBLE_SAND)
4401 DrawLevelFieldCrumbledSandNeighbours(x, y);
4406 static void RedrawAllInvisibleElementsForMagnifier()
4411 SCAN_PLAYFIELD(x, y)
4413 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
4416 int element = Feld[x][y];
4418 if (element == EL_EMC_FAKE_GRASS &&
4419 game.magnify_time_left > 0)
4421 Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
4422 DrawLevelField(x, y);
4424 else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
4425 game.magnify_time_left == 0)
4427 Feld[x][y] = EL_EMC_FAKE_GRASS;
4428 DrawLevelField(x, y);
4430 else if (IS_GATE_GRAY(element) &&
4431 game.magnify_time_left > 0)
4433 Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
4434 element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
4435 IS_EM_GATE_GRAY(element) ?
4436 element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
4437 IS_EMC_GATE_GRAY(element) ?
4438 element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
4440 DrawLevelField(x, y);
4442 else if (IS_GATE_GRAY_ACTIVE(element) &&
4443 game.magnify_time_left == 0)
4445 Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
4446 element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
4447 IS_EM_GATE_GRAY_ACTIVE(element) ?
4448 element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
4449 IS_EMC_GATE_GRAY_ACTIVE(element) ?
4450 element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
4452 DrawLevelField(x, y);
4457 static void ToggleLightSwitch(int x, int y)
4459 int element = Feld[x][y];
4461 game.light_time_left =
4462 (element == EL_LIGHT_SWITCH ?
4463 level.time_light * FRAMES_PER_SECOND : 0);
4465 RedrawAllLightSwitchesAndInvisibleElements();
4468 static void ActivateTimegateSwitch(int x, int y)
4472 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
4475 SCAN_PLAYFIELD(xx, yy)
4477 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
4480 int element = Feld[xx][yy];
4482 if (element == EL_TIMEGATE_CLOSED ||
4483 element == EL_TIMEGATE_CLOSING)
4485 Feld[xx][yy] = EL_TIMEGATE_OPENING;
4486 PlayLevelSound(xx, yy, SND_TIMEGATE_OPENING);
4490 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
4492 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
4493 DrawLevelField(xx, yy);
4499 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
4502 void Impact(int x, int y)
4504 boolean last_line = (y == lev_fieldy - 1);
4505 boolean object_hit = FALSE;
4506 boolean impact = (last_line || object_hit);
4507 int element = Feld[x][y];
4508 int smashed = EL_STEELWALL;
4510 if (!last_line) /* check if element below was hit */
4512 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
4515 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
4516 MovDir[x][y + 1] != MV_DOWN ||
4517 MovPos[x][y + 1] <= TILEY / 2));
4519 /* do not smash moving elements that left the smashed field in time */
4520 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
4521 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
4524 #if USE_QUICKSAND_IMPACT_BUGFIX
4525 if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
4527 RemoveMovingField(x, y + 1);
4528 Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
4529 Feld[x][y + 2] = EL_ROCK;
4530 DrawLevelField(x, y + 2);
4537 smashed = MovingOrBlocked2Element(x, y + 1);
4539 impact = (last_line || object_hit);
4542 if (!last_line && smashed == EL_ACID) /* element falls into acid */
4544 SplashAcid(x, y + 1);
4548 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
4549 /* only reset graphic animation if graphic really changes after impact */
4551 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
4553 ResetGfxAnimation(x, y);
4554 DrawLevelField(x, y);
4557 if (impact && CAN_EXPLODE_IMPACT(element))
4562 else if (impact && element == EL_PEARL)
4564 ResetGfxAnimation(x, y);
4566 Feld[x][y] = EL_PEARL_BREAKING;
4567 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4570 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
4572 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4577 if (impact && element == EL_AMOEBA_DROP)
4579 if (object_hit && IS_PLAYER(x, y + 1))
4580 KillPlayerUnlessEnemyProtected(x, y + 1);
4581 else if (object_hit && smashed == EL_PENGUIN)
4585 Feld[x][y] = EL_AMOEBA_GROWING;
4586 Store[x][y] = EL_AMOEBA_WET;
4588 ResetRandomAnimationValue(x, y);
4593 if (object_hit) /* check which object was hit */
4595 if (CAN_PASS_MAGIC_WALL(element) &&
4596 (smashed == EL_MAGIC_WALL ||
4597 smashed == EL_BD_MAGIC_WALL))
4600 int activated_magic_wall =
4601 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
4602 EL_BD_MAGIC_WALL_ACTIVE);
4604 /* activate magic wall / mill */
4606 SCAN_PLAYFIELD(xx, yy)
4608 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
4610 if (Feld[xx][yy] == smashed)
4611 Feld[xx][yy] = activated_magic_wall;
4613 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
4614 game.magic_wall_active = TRUE;
4616 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
4617 SND_MAGIC_WALL_ACTIVATING :
4618 SND_BD_MAGIC_WALL_ACTIVATING));
4621 if (IS_PLAYER(x, y + 1))
4623 if (CAN_SMASH_PLAYER(element))
4625 KillPlayerUnlessEnemyProtected(x, y + 1);
4629 else if (smashed == EL_PENGUIN)
4631 if (CAN_SMASH_PLAYER(element))
4637 else if (element == EL_BD_DIAMOND)
4639 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
4645 else if (((element == EL_SP_INFOTRON ||
4646 element == EL_SP_ZONK) &&
4647 (smashed == EL_SP_SNIKSNAK ||
4648 smashed == EL_SP_ELECTRON ||
4649 smashed == EL_SP_DISK_ORANGE)) ||
4650 (element == EL_SP_INFOTRON &&
4651 smashed == EL_SP_DISK_YELLOW))
4656 else if (CAN_SMASH_EVERYTHING(element))
4658 if (IS_CLASSIC_ENEMY(smashed) ||
4659 CAN_EXPLODE_SMASHED(smashed))
4664 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
4666 if (smashed == EL_LAMP ||
4667 smashed == EL_LAMP_ACTIVE)
4672 else if (smashed == EL_NUT)
4674 Feld[x][y + 1] = EL_NUT_BREAKING;
4675 PlayLevelSound(x, y, SND_NUT_BREAKING);
4676 RaiseScoreElement(EL_NUT);
4679 else if (smashed == EL_PEARL)
4681 ResetGfxAnimation(x, y);
4683 Feld[x][y + 1] = EL_PEARL_BREAKING;
4684 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4687 else if (smashed == EL_DIAMOND)
4689 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
4690 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
4693 else if (IS_BELT_SWITCH(smashed))
4695 ToggleBeltSwitch(x, y + 1);
4697 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
4698 smashed == EL_SWITCHGATE_SWITCH_DOWN)
4700 ToggleSwitchgateSwitch(x, y + 1);
4702 else if (smashed == EL_LIGHT_SWITCH ||
4703 smashed == EL_LIGHT_SWITCH_ACTIVE)
4705 ToggleLightSwitch(x, y + 1);
4710 TestIfElementSmashesCustomElement(x, y, MV_DOWN);
4713 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4715 CheckElementChangeBySide(x, y + 1, smashed, element,
4716 CE_SWITCHED, CH_SIDE_TOP);
4717 CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
4723 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4728 /* play sound of magic wall / mill */
4730 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
4731 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
4733 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
4734 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
4735 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
4736 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
4741 /* play sound of object that hits the ground */
4742 if (last_line || object_hit)
4743 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4746 inline static void TurnRoundExt(int x, int y)
4758 { 0, 0 }, { 0, 0 }, { 0, 0 },
4763 int left, right, back;
4767 { MV_DOWN, MV_UP, MV_RIGHT },
4768 { MV_UP, MV_DOWN, MV_LEFT },
4770 { MV_LEFT, MV_RIGHT, MV_DOWN },
4774 { MV_RIGHT, MV_LEFT, MV_UP }
4777 int element = Feld[x][y];
4778 int move_pattern = element_info[element].move_pattern;
4780 int old_move_dir = MovDir[x][y];
4781 int left_dir = turn[old_move_dir].left;
4782 int right_dir = turn[old_move_dir].right;
4783 int back_dir = turn[old_move_dir].back;
4785 int left_dx = move_xy[left_dir].dx, left_dy = move_xy[left_dir].dy;
4786 int right_dx = move_xy[right_dir].dx, right_dy = move_xy[right_dir].dy;
4787 int move_dx = move_xy[old_move_dir].dx, move_dy = move_xy[old_move_dir].dy;
4788 int back_dx = move_xy[back_dir].dx, back_dy = move_xy[back_dir].dy;
4790 int left_x = x + left_dx, left_y = y + left_dy;
4791 int right_x = x + right_dx, right_y = y + right_dy;
4792 int move_x = x + move_dx, move_y = y + move_dy;
4796 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4798 TestIfBadThingTouchesOtherBadThing(x, y);
4800 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
4801 MovDir[x][y] = right_dir;
4802 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4803 MovDir[x][y] = left_dir;
4805 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
4807 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
4810 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
4812 TestIfBadThingTouchesOtherBadThing(x, y);
4814 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
4815 MovDir[x][y] = left_dir;
4816 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4817 MovDir[x][y] = right_dir;
4819 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
4821 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
4824 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4826 TestIfBadThingTouchesOtherBadThing(x, y);
4828 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
4829 MovDir[x][y] = left_dir;
4830 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
4831 MovDir[x][y] = right_dir;
4833 if (MovDir[x][y] != old_move_dir)
4836 else if (element == EL_YAMYAM)
4838 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
4839 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
4841 if (can_turn_left && can_turn_right)
4842 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4843 else if (can_turn_left)
4844 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4845 else if (can_turn_right)
4846 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4848 MovDir[x][y] = back_dir;
4850 MovDelay[x][y] = 16 + 16 * RND(3);
4852 else if (element == EL_DARK_YAMYAM)
4854 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4856 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4859 if (can_turn_left && can_turn_right)
4860 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4861 else if (can_turn_left)
4862 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4863 else if (can_turn_right)
4864 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4866 MovDir[x][y] = back_dir;
4868 MovDelay[x][y] = 16 + 16 * RND(3);
4870 else if (element == EL_PACMAN)
4872 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
4873 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
4875 if (can_turn_left && can_turn_right)
4876 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4877 else if (can_turn_left)
4878 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4879 else if (can_turn_right)
4880 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4882 MovDir[x][y] = back_dir;
4884 MovDelay[x][y] = 6 + RND(40);
4886 else if (element == EL_PIG)
4888 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
4889 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
4890 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
4891 boolean should_turn_left, should_turn_right, should_move_on;
4893 int rnd = RND(rnd_value);
4895 should_turn_left = (can_turn_left &&
4897 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
4898 y + back_dy + left_dy)));
4899 should_turn_right = (can_turn_right &&
4901 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
4902 y + back_dy + right_dy)));
4903 should_move_on = (can_move_on &&
4906 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
4907 y + move_dy + left_dy) ||
4908 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
4909 y + move_dy + right_dy)));
4911 if (should_turn_left || should_turn_right || should_move_on)
4913 if (should_turn_left && should_turn_right && should_move_on)
4914 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
4915 rnd < 2 * rnd_value / 3 ? right_dir :
4917 else if (should_turn_left && should_turn_right)
4918 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4919 else if (should_turn_left && should_move_on)
4920 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
4921 else if (should_turn_right && should_move_on)
4922 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
4923 else if (should_turn_left)
4924 MovDir[x][y] = left_dir;
4925 else if (should_turn_right)
4926 MovDir[x][y] = right_dir;
4927 else if (should_move_on)
4928 MovDir[x][y] = old_move_dir;
4930 else if (can_move_on && rnd > rnd_value / 8)
4931 MovDir[x][y] = old_move_dir;
4932 else if (can_turn_left && can_turn_right)
4933 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4934 else if (can_turn_left && rnd > rnd_value / 8)
4935 MovDir[x][y] = left_dir;
4936 else if (can_turn_right && rnd > rnd_value/8)
4937 MovDir[x][y] = right_dir;
4939 MovDir[x][y] = back_dir;
4941 xx = x + move_xy[MovDir[x][y]].dx;
4942 yy = y + move_xy[MovDir[x][y]].dy;
4944 if (!IN_LEV_FIELD(xx, yy) ||
4945 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
4946 MovDir[x][y] = old_move_dir;
4950 else if (element == EL_DRAGON)
4952 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
4953 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
4954 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
4956 int rnd = RND(rnd_value);
4958 if (can_move_on && rnd > rnd_value / 8)
4959 MovDir[x][y] = old_move_dir;
4960 else if (can_turn_left && can_turn_right)
4961 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4962 else if (can_turn_left && rnd > rnd_value / 8)
4963 MovDir[x][y] = left_dir;
4964 else if (can_turn_right && rnd > rnd_value / 8)
4965 MovDir[x][y] = right_dir;
4967 MovDir[x][y] = back_dir;
4969 xx = x + move_xy[MovDir[x][y]].dx;
4970 yy = y + move_xy[MovDir[x][y]].dy;
4972 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
4973 MovDir[x][y] = old_move_dir;
4977 else if (element == EL_MOLE)
4979 boolean can_move_on =
4980 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
4981 IS_AMOEBOID(Feld[move_x][move_y]) ||
4982 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
4985 boolean can_turn_left =
4986 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
4987 IS_AMOEBOID(Feld[left_x][left_y])));
4989 boolean can_turn_right =
4990 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
4991 IS_AMOEBOID(Feld[right_x][right_y])));
4993 if (can_turn_left && can_turn_right)
4994 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
4995 else if (can_turn_left)
4996 MovDir[x][y] = left_dir;
4998 MovDir[x][y] = right_dir;
5001 if (MovDir[x][y] != old_move_dir)
5004 else if (element == EL_BALLOON)
5006 MovDir[x][y] = game.wind_direction;
5009 else if (element == EL_SPRING)
5011 #if USE_NEW_SPRING_BUMPER
5012 if (MovDir[x][y] & MV_HORIZONTAL)
5014 if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
5015 !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
5017 Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
5018 ResetGfxAnimation(move_x, move_y);
5019 DrawLevelField(move_x, move_y);
5021 MovDir[x][y] = back_dir;
5023 else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
5024 SPRING_CAN_ENTER_FIELD(element, x, y + 1))
5025 MovDir[x][y] = MV_NONE;
5028 if (MovDir[x][y] & MV_HORIZONTAL &&
5029 (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
5030 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
5031 MovDir[x][y] = MV_NONE;
5036 else if (element == EL_ROBOT ||
5037 element == EL_SATELLITE ||
5038 element == EL_PENGUIN ||
5039 element == EL_EMC_ANDROID)
5041 int attr_x = -1, attr_y = -1;
5052 for (i = 0; i < MAX_PLAYERS; i++)
5054 struct PlayerInfo *player = &stored_player[i];
5055 int jx = player->jx, jy = player->jy;
5057 if (!player->active)
5061 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
5069 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
5070 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
5071 game.engine_version < VERSION_IDENT(3,1,0,0)))
5077 if (element == EL_PENGUIN)
5080 static int xy[4][2] =
5088 for (i = 0; i < NUM_DIRECTIONS; i++)
5090 int ex = x + xy[i][0];
5091 int ey = y + xy[i][1];
5093 if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
5102 MovDir[x][y] = MV_NONE;
5104 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
5105 else if (attr_x > x)
5106 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
5108 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
5109 else if (attr_y > y)
5110 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
5112 if (element == EL_ROBOT)
5116 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5117 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
5118 Moving2Blocked(x, y, &newx, &newy);
5120 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
5121 MovDelay[x][y] = 8 + 8 * !RND(3);
5123 MovDelay[x][y] = 16;
5125 else if (element == EL_PENGUIN)
5131 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5133 boolean first_horiz = RND(2);
5134 int new_move_dir = MovDir[x][y];
5137 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5138 Moving2Blocked(x, y, &newx, &newy);
5140 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
5144 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5145 Moving2Blocked(x, y, &newx, &newy);
5147 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
5150 MovDir[x][y] = old_move_dir;
5154 else if (element == EL_SATELLITE)
5160 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5162 boolean first_horiz = RND(2);
5163 int new_move_dir = MovDir[x][y];
5166 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5167 Moving2Blocked(x, y, &newx, &newy);
5169 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
5173 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5174 Moving2Blocked(x, y, &newx, &newy);
5176 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
5179 MovDir[x][y] = old_move_dir;
5183 else if (element == EL_EMC_ANDROID)
5185 static int check_pos[16] =
5187 -1, /* 0 => (invalid) */
5188 7, /* 1 => MV_LEFT */
5189 3, /* 2 => MV_RIGHT */
5190 -1, /* 3 => (invalid) */
5192 0, /* 5 => MV_LEFT | MV_UP */
5193 2, /* 6 => MV_RIGHT | MV_UP */
5194 -1, /* 7 => (invalid) */
5195 5, /* 8 => MV_DOWN */
5196 6, /* 9 => MV_LEFT | MV_DOWN */
5197 4, /* 10 => MV_RIGHT | MV_DOWN */
5198 -1, /* 11 => (invalid) */
5199 -1, /* 12 => (invalid) */
5200 -1, /* 13 => (invalid) */
5201 -1, /* 14 => (invalid) */
5202 -1, /* 15 => (invalid) */
5210 { -1, -1, MV_LEFT | MV_UP },
5212 { +1, -1, MV_RIGHT | MV_UP },
5213 { +1, 0, MV_RIGHT },
5214 { +1, +1, MV_RIGHT | MV_DOWN },
5216 { -1, +1, MV_LEFT | MV_DOWN },
5219 int start_pos, check_order;
5220 boolean can_clone = FALSE;
5223 /* check if there is any free field around current position */
5224 for (i = 0; i < 8; i++)
5226 int newx = x + check_xy[i].dx;
5227 int newy = y + check_xy[i].dy;
5229 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5237 if (can_clone) /* randomly find an element to clone */
5241 start_pos = check_pos[RND(8)];
5242 check_order = (RND(2) ? -1 : +1);
5244 for (i = 0; i < 8; i++)
5246 int pos_raw = start_pos + i * check_order;
5247 int pos = (pos_raw + 8) % 8;
5248 int newx = x + check_xy[pos].dx;
5249 int newy = y + check_xy[pos].dy;
5251 if (ANDROID_CAN_CLONE_FIELD(newx, newy))
5253 element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
5254 element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
5256 Store[x][y] = Feld[newx][newy];
5265 if (can_clone) /* randomly find a direction to move */
5269 start_pos = check_pos[RND(8)];
5270 check_order = (RND(2) ? -1 : +1);
5272 for (i = 0; i < 8; i++)
5274 int pos_raw = start_pos + i * check_order;
5275 int pos = (pos_raw + 8) % 8;
5276 int newx = x + check_xy[pos].dx;
5277 int newy = y + check_xy[pos].dy;
5278 int new_move_dir = check_xy[pos].dir;
5280 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5282 MovDir[x][y] = new_move_dir;
5283 MovDelay[x][y] = level.android_clone_time * 8 + 1;
5292 if (can_clone) /* cloning and moving successful */
5295 /* cannot clone -- try to move towards player */
5297 start_pos = check_pos[MovDir[x][y] & 0x0f];
5298 check_order = (RND(2) ? -1 : +1);
5300 for (i = 0; i < 3; i++)
5302 /* first check start_pos, then previous/next or (next/previous) pos */
5303 int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
5304 int pos = (pos_raw + 8) % 8;
5305 int newx = x + check_xy[pos].dx;
5306 int newy = y + check_xy[pos].dy;
5307 int new_move_dir = check_xy[pos].dir;
5309 if (IS_PLAYER(newx, newy))
5312 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
5314 MovDir[x][y] = new_move_dir;
5315 MovDelay[x][y] = level.android_move_time * 8 + 1;
5322 else if (move_pattern == MV_TURNING_LEFT ||
5323 move_pattern == MV_TURNING_RIGHT ||
5324 move_pattern == MV_TURNING_LEFT_RIGHT ||
5325 move_pattern == MV_TURNING_RIGHT_LEFT ||
5326 move_pattern == MV_TURNING_RANDOM ||
5327 move_pattern == MV_ALL_DIRECTIONS)
5329 boolean can_turn_left =
5330 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
5331 boolean can_turn_right =
5332 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
5334 if (element_info[element].move_stepsize == 0) /* "not moving" */
5337 if (move_pattern == MV_TURNING_LEFT)
5338 MovDir[x][y] = left_dir;
5339 else if (move_pattern == MV_TURNING_RIGHT)
5340 MovDir[x][y] = right_dir;
5341 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
5342 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
5343 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
5344 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
5345 else if (move_pattern == MV_TURNING_RANDOM)
5346 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
5347 can_turn_right && !can_turn_left ? right_dir :
5348 RND(2) ? left_dir : right_dir);
5349 else if (can_turn_left && can_turn_right)
5350 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5351 else if (can_turn_left)
5352 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5353 else if (can_turn_right)
5354 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5356 MovDir[x][y] = back_dir;
5358 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5360 else if (move_pattern == MV_HORIZONTAL ||
5361 move_pattern == MV_VERTICAL)
5363 if (move_pattern & old_move_dir)
5364 MovDir[x][y] = back_dir;
5365 else if (move_pattern == MV_HORIZONTAL)
5366 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
5367 else if (move_pattern == MV_VERTICAL)
5368 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
5370 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5372 else if (move_pattern & MV_ANY_DIRECTION)
5374 MovDir[x][y] = move_pattern;
5375 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5377 else if (move_pattern & MV_WIND_DIRECTION)
5379 MovDir[x][y] = game.wind_direction;
5380 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5382 else if (move_pattern == MV_ALONG_LEFT_SIDE)
5384 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
5385 MovDir[x][y] = left_dir;
5386 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5387 MovDir[x][y] = right_dir;
5389 if (MovDir[x][y] != old_move_dir)
5390 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5392 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
5394 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
5395 MovDir[x][y] = right_dir;
5396 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5397 MovDir[x][y] = left_dir;
5399 if (MovDir[x][y] != old_move_dir)
5400 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5402 else if (move_pattern == MV_TOWARDS_PLAYER ||
5403 move_pattern == MV_AWAY_FROM_PLAYER)
5405 int attr_x = -1, attr_y = -1;
5407 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
5418 for (i = 0; i < MAX_PLAYERS; i++)
5420 struct PlayerInfo *player = &stored_player[i];
5421 int jx = player->jx, jy = player->jy;
5423 if (!player->active)
5427 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
5435 MovDir[x][y] = MV_NONE;
5437 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
5438 else if (attr_x > x)
5439 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
5441 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
5442 else if (attr_y > y)
5443 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
5445 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5447 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5449 boolean first_horiz = RND(2);
5450 int new_move_dir = MovDir[x][y];
5452 if (element_info[element].move_stepsize == 0) /* "not moving" */
5454 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
5455 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5461 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5462 Moving2Blocked(x, y, &newx, &newy);
5464 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5468 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5469 Moving2Blocked(x, y, &newx, &newy);
5471 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5474 MovDir[x][y] = old_move_dir;
5477 else if (move_pattern == MV_WHEN_PUSHED ||
5478 move_pattern == MV_WHEN_DROPPED)
5480 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5481 MovDir[x][y] = MV_NONE;
5485 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
5487 static int test_xy[7][2] =
5497 static int test_dir[7] =
5507 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
5508 int move_preference = -1000000; /* start with very low preference */
5509 int new_move_dir = MV_NONE;
5510 int start_test = RND(4);
5513 for (i = 0; i < NUM_DIRECTIONS; i++)
5515 int move_dir = test_dir[start_test + i];
5516 int move_dir_preference;
5518 xx = x + test_xy[start_test + i][0];
5519 yy = y + test_xy[start_test + i][1];
5521 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
5522 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
5524 new_move_dir = move_dir;
5529 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
5532 move_dir_preference = -1 * RunnerVisit[xx][yy];
5533 if (hunter_mode && PlayerVisit[xx][yy] > 0)
5534 move_dir_preference = PlayerVisit[xx][yy];
5536 if (move_dir_preference > move_preference)
5538 /* prefer field that has not been visited for the longest time */
5539 move_preference = move_dir_preference;
5540 new_move_dir = move_dir;
5542 else if (move_dir_preference == move_preference &&
5543 move_dir == old_move_dir)
5545 /* prefer last direction when all directions are preferred equally */
5546 move_preference = move_dir_preference;
5547 new_move_dir = move_dir;
5551 MovDir[x][y] = new_move_dir;
5552 if (old_move_dir != new_move_dir)
5553 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5557 static void TurnRound(int x, int y)
5559 int direction = MovDir[x][y];
5561 int element, graphic;
5566 GfxDir[x][y] = MovDir[x][y];
5568 if (direction != MovDir[x][y])
5572 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
5575 element = Feld[x][y];
5576 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5578 if (graphic_info[graphic].anim_global_sync)
5579 GfxFrame[x][y] = FrameCounter;
5580 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5581 GfxFrame[x][y] = CustomValue[x][y];
5582 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5583 GfxFrame[x][y] = element_info[element].collect_score;
5587 static boolean JustBeingPushed(int x, int y)
5591 for (i = 0; i < MAX_PLAYERS; i++)
5593 struct PlayerInfo *player = &stored_player[i];
5595 if (player->active && player->is_pushing && player->MovPos)
5597 int next_jx = player->jx + (player->jx - player->last_jx);
5598 int next_jy = player->jy + (player->jy - player->last_jy);
5600 if (x == next_jx && y == next_jy)
5608 void StartMoving(int x, int y)
5610 boolean started_moving = FALSE; /* some elements can fall _and_ move */
5611 int element = Feld[x][y];
5616 if (MovDelay[x][y] == 0)
5617 GfxAction[x][y] = ACTION_DEFAULT;
5619 if (CAN_FALL(element) && y < lev_fieldy - 1)
5621 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
5622 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
5623 if (JustBeingPushed(x, y))
5626 if (element == EL_QUICKSAND_FULL)
5628 if (IS_FREE(x, y + 1))
5630 InitMovingField(x, y, MV_DOWN);
5631 started_moving = TRUE;
5633 Feld[x][y] = EL_QUICKSAND_EMPTYING;
5634 Store[x][y] = EL_ROCK;
5636 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
5638 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5640 if (!MovDelay[x][y])
5641 MovDelay[x][y] = TILEY + 1;
5650 Feld[x][y] = EL_QUICKSAND_EMPTY;
5651 Feld[x][y + 1] = EL_QUICKSAND_FULL;
5652 Store[x][y + 1] = Store[x][y];
5655 PlayLevelSoundAction(x, y, ACTION_FILLING);
5658 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
5659 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5661 InitMovingField(x, y, MV_DOWN);
5662 started_moving = TRUE;
5664 Feld[x][y] = EL_QUICKSAND_FILLING;
5665 Store[x][y] = element;
5667 PlayLevelSoundAction(x, y, ACTION_FILLING);
5669 else if (element == EL_MAGIC_WALL_FULL)
5671 if (IS_FREE(x, y + 1))
5673 InitMovingField(x, y, MV_DOWN);
5674 started_moving = TRUE;
5676 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
5677 Store[x][y] = EL_CHANGED(Store[x][y]);
5679 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
5681 if (!MovDelay[x][y])
5682 MovDelay[x][y] = TILEY/4 + 1;
5691 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
5692 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
5693 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
5697 else if (element == EL_BD_MAGIC_WALL_FULL)
5699 if (IS_FREE(x, y + 1))
5701 InitMovingField(x, y, MV_DOWN);
5702 started_moving = TRUE;
5704 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
5705 Store[x][y] = EL_CHANGED2(Store[x][y]);
5707 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
5709 if (!MovDelay[x][y])
5710 MovDelay[x][y] = TILEY/4 + 1;
5719 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
5720 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
5721 Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
5725 else if (CAN_PASS_MAGIC_WALL(element) &&
5726 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
5727 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
5729 InitMovingField(x, y, MV_DOWN);
5730 started_moving = TRUE;
5733 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
5734 EL_BD_MAGIC_WALL_FILLING);
5735 Store[x][y] = element;
5737 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
5739 SplashAcid(x, y + 1);
5741 InitMovingField(x, y, MV_DOWN);
5742 started_moving = TRUE;
5744 Store[x][y] = EL_ACID;
5746 else if ((game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5747 CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
5749 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
5750 CAN_FALL(element) && WasJustFalling[x][y] &&
5751 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
5753 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
5754 CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
5755 (Feld[x][y + 1] == EL_BLOCKED)))
5757 /* this is needed for a special case not covered by calling "Impact()"
5758 from "ContinueMoving()": if an element moves to a tile directly below
5759 another element which was just falling on that tile (which was empty
5760 in the previous frame), the falling element above would just stop
5761 instead of smashing the element below (in previous version, the above
5762 element was just checked for "moving" instead of "falling", resulting
5763 in incorrect smashes caused by horizontal movement of the above
5764 element; also, the case of the player being the element to smash was
5765 simply not covered here... :-/ ) */
5767 CheckCollision[x][y] = 0;
5771 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
5773 if (MovDir[x][y] == MV_NONE)
5775 InitMovingField(x, y, MV_DOWN);
5776 started_moving = TRUE;
5779 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
5781 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
5782 MovDir[x][y] = MV_DOWN;
5784 InitMovingField(x, y, MV_DOWN);
5785 started_moving = TRUE;
5787 else if (element == EL_AMOEBA_DROP)
5789 Feld[x][y] = EL_AMOEBA_GROWING;
5790 Store[x][y] = EL_AMOEBA_WET;
5792 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
5793 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
5794 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
5795 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
5797 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
5798 (IS_FREE(x - 1, y + 1) ||
5799 Feld[x - 1][y + 1] == EL_ACID));
5800 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
5801 (IS_FREE(x + 1, y + 1) ||
5802 Feld[x + 1][y + 1] == EL_ACID));
5803 boolean can_fall_any = (can_fall_left || can_fall_right);
5804 boolean can_fall_both = (can_fall_left && can_fall_right);
5805 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
5807 #if USE_NEW_ALL_SLIPPERY
5808 if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
5810 if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
5811 can_fall_right = FALSE;
5812 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
5813 can_fall_left = FALSE;
5814 else if (slippery_type == SLIPPERY_ONLY_LEFT)
5815 can_fall_right = FALSE;
5816 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
5817 can_fall_left = FALSE;
5819 can_fall_any = (can_fall_left || can_fall_right);
5820 can_fall_both = FALSE;
5823 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
5825 if (slippery_type == SLIPPERY_ONLY_LEFT)
5826 can_fall_right = FALSE;
5827 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
5828 can_fall_left = FALSE;
5829 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
5830 can_fall_right = FALSE;
5831 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
5832 can_fall_left = FALSE;
5834 can_fall_any = (can_fall_left || can_fall_right);
5835 can_fall_both = (can_fall_left && can_fall_right);
5839 #if USE_NEW_ALL_SLIPPERY
5841 #if USE_NEW_SP_SLIPPERY
5842 /* !!! better use the same properties as for custom elements here !!! */
5843 else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
5844 can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
5846 can_fall_right = FALSE; /* slip down on left side */
5847 can_fall_both = FALSE;
5852 #if USE_NEW_ALL_SLIPPERY
5855 if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
5856 can_fall_right = FALSE; /* slip down on left side */
5858 can_fall_left = !(can_fall_right = RND(2));
5860 can_fall_both = FALSE;
5865 if (game.emulation == EMU_BOULDERDASH ||
5866 element == EL_BD_ROCK || element == EL_BD_DIAMOND)
5867 can_fall_right = FALSE; /* slip down on left side */
5869 can_fall_left = !(can_fall_right = RND(2));
5871 can_fall_both = FALSE;
5877 /* if not determined otherwise, prefer left side for slipping down */
5878 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
5879 started_moving = TRUE;
5883 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
5885 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
5888 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
5889 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
5890 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
5891 int belt_dir = game.belt_dir[belt_nr];
5893 if ((belt_dir == MV_LEFT && left_is_free) ||
5894 (belt_dir == MV_RIGHT && right_is_free))
5896 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
5898 InitMovingField(x, y, belt_dir);
5899 started_moving = TRUE;
5901 Pushed[x][y] = TRUE;
5902 Pushed[nextx][y] = TRUE;
5904 GfxAction[x][y] = ACTION_DEFAULT;
5908 MovDir[x][y] = 0; /* if element was moving, stop it */
5913 /* not "else if" because of elements that can fall and move (EL_SPRING) */
5915 if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
5917 if (CAN_MOVE(element) && !started_moving)
5920 int move_pattern = element_info[element].move_pattern;
5925 if (MovDir[x][y] == MV_NONE)
5927 printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
5928 x, y, element, element_info[element].token_name);
5929 printf("StartMoving(): This should never happen!\n");
5934 Moving2Blocked(x, y, &newx, &newy);
5936 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
5939 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5940 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5942 WasJustMoving[x][y] = 0;
5943 CheckCollision[x][y] = 0;
5945 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
5947 if (Feld[x][y] != element) /* element has changed */
5951 if (!MovDelay[x][y]) /* start new movement phase */
5953 /* all objects that can change their move direction after each step
5954 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
5956 if (element != EL_YAMYAM &&
5957 element != EL_DARK_YAMYAM &&
5958 element != EL_PACMAN &&
5959 !(move_pattern & MV_ANY_DIRECTION) &&
5960 move_pattern != MV_TURNING_LEFT &&
5961 move_pattern != MV_TURNING_RIGHT &&
5962 move_pattern != MV_TURNING_LEFT_RIGHT &&
5963 move_pattern != MV_TURNING_RIGHT_LEFT &&
5964 move_pattern != MV_TURNING_RANDOM)
5968 if (MovDelay[x][y] && (element == EL_BUG ||
5969 element == EL_SPACESHIP ||
5970 element == EL_SP_SNIKSNAK ||
5971 element == EL_SP_ELECTRON ||
5972 element == EL_MOLE))
5973 DrawLevelField(x, y);
5977 if (MovDelay[x][y]) /* wait some time before next movement */
5981 if (element == EL_ROBOT ||
5982 element == EL_YAMYAM ||
5983 element == EL_DARK_YAMYAM)
5985 DrawLevelElementAnimationIfNeeded(x, y, element);
5986 PlayLevelSoundAction(x, y, ACTION_WAITING);
5988 else if (element == EL_SP_ELECTRON)
5989 DrawLevelElementAnimationIfNeeded(x, y, element);
5990 else if (element == EL_DRAGON)
5993 int dir = MovDir[x][y];
5994 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
5995 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
5996 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
5997 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
5998 dir == MV_UP ? IMG_FLAMES_1_UP :
5999 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
6000 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
6002 GfxAction[x][y] = ACTION_ATTACKING;
6004 if (IS_PLAYER(x, y))
6005 DrawPlayerField(x, y);
6007 DrawLevelField(x, y);
6009 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
6011 for (i = 1; i <= 3; i++)
6013 int xx = x + i * dx;
6014 int yy = y + i * dy;
6015 int sx = SCREENX(xx);
6016 int sy = SCREENY(yy);
6017 int flame_graphic = graphic + (i - 1);
6019 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
6024 int flamed = MovingOrBlocked2Element(xx, yy);
6028 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
6030 else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
6031 RemoveMovingField(xx, yy);
6033 RemoveField(xx, yy);
6035 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
6038 RemoveMovingField(xx, yy);
6041 ChangeDelay[xx][yy] = 0;
6043 Feld[xx][yy] = EL_FLAMES;
6045 if (IN_SCR_FIELD(sx, sy))
6047 DrawLevelFieldCrumbledSand(xx, yy);
6048 DrawGraphic(sx, sy, flame_graphic, frame);
6053 if (Feld[xx][yy] == EL_FLAMES)
6054 Feld[xx][yy] = EL_EMPTY;
6055 DrawLevelField(xx, yy);
6060 if (MovDelay[x][y]) /* element still has to wait some time */
6062 PlayLevelSoundAction(x, y, ACTION_WAITING);
6068 /* now make next step */
6070 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
6072 if (DONT_COLLIDE_WITH(element) &&
6073 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
6074 !PLAYER_ENEMY_PROTECTED(newx, newy))
6076 TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
6081 else if (CAN_MOVE_INTO_ACID(element) &&
6082 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
6083 !IS_MV_DIAGONAL(MovDir[x][y]) &&
6084 (MovDir[x][y] == MV_DOWN ||
6085 game.engine_version >= VERSION_IDENT(3,1,0,0)))
6087 SplashAcid(newx, newy);
6088 Store[x][y] = EL_ACID;
6090 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
6092 if (Feld[newx][newy] == EL_EXIT_OPEN)
6095 DrawLevelField(x, y);
6097 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
6098 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
6099 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
6101 local_player->friends_still_needed--;
6102 if (!local_player->friends_still_needed &&
6103 !local_player->GameOver && AllPlayersGone)
6104 local_player->LevelSolved = local_player->GameOver = TRUE;
6108 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
6110 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
6111 DrawLevelField(newx, newy);
6113 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
6115 else if (!IS_FREE(newx, newy))
6117 GfxAction[x][y] = ACTION_WAITING;
6119 if (IS_PLAYER(x, y))
6120 DrawPlayerField(x, y);
6122 DrawLevelField(x, y);
6127 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
6129 if (IS_FOOD_PIG(Feld[newx][newy]))
6131 if (IS_MOVING(newx, newy))
6132 RemoveMovingField(newx, newy);
6135 Feld[newx][newy] = EL_EMPTY;
6136 DrawLevelField(newx, newy);
6139 PlayLevelSound(x, y, SND_PIG_DIGGING);
6141 else if (!IS_FREE(newx, newy))
6143 if (IS_PLAYER(x, y))
6144 DrawPlayerField(x, y);
6146 DrawLevelField(x, y);
6151 else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
6153 if (Store[x][y] != EL_EMPTY)
6155 boolean can_clone = FALSE;
6158 /* check if element to clone is still there */
6159 for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
6161 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
6169 /* cannot clone or target field not free anymore -- do not clone */
6170 if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6171 Store[x][y] = EL_EMPTY;
6174 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6176 if (IS_MV_DIAGONAL(MovDir[x][y]))
6178 int diagonal_move_dir = MovDir[x][y];
6179 int stored = Store[x][y];
6180 int change_delay = 8;
6183 /* android is moving diagonally */
6185 CreateField(x, y, EL_DIAGONAL_SHRINKING);
6187 Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
6188 GfxElement[x][y] = EL_EMC_ANDROID;
6189 GfxAction[x][y] = ACTION_SHRINKING;
6190 GfxDir[x][y] = diagonal_move_dir;
6191 ChangeDelay[x][y] = change_delay;
6193 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
6196 DrawLevelGraphicAnimation(x, y, graphic);
6197 PlayLevelSoundAction(x, y, ACTION_SHRINKING);
6199 if (Feld[newx][newy] == EL_ACID)
6201 SplashAcid(newx, newy);
6206 CreateField(newx, newy, EL_DIAGONAL_GROWING);
6208 Store[newx][newy] = EL_EMC_ANDROID;
6209 GfxElement[newx][newy] = EL_EMC_ANDROID;
6210 GfxAction[newx][newy] = ACTION_GROWING;
6211 GfxDir[newx][newy] = diagonal_move_dir;
6212 ChangeDelay[newx][newy] = change_delay;
6214 graphic = el_act_dir2img(GfxElement[newx][newy],
6215 GfxAction[newx][newy], GfxDir[newx][newy]);
6217 DrawLevelGraphicAnimation(newx, newy, graphic);
6218 PlayLevelSoundAction(newx, newy, ACTION_GROWING);
6224 Feld[newx][newy] = EL_EMPTY;
6225 DrawLevelField(newx, newy);
6227 PlayLevelSoundAction(x, y, ACTION_DIGGING);
6230 else if (!IS_FREE(newx, newy))
6233 if (IS_PLAYER(x, y))
6234 DrawPlayerField(x, y);
6236 DrawLevelField(x, y);
6242 else if (IS_CUSTOM_ELEMENT(element) &&
6243 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6245 int new_element = Feld[newx][newy];
6247 if (!IS_FREE(newx, newy))
6249 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
6250 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
6253 /* no element can dig solid indestructible elements */
6254 if (IS_INDESTRUCTIBLE(new_element) &&
6255 !IS_DIGGABLE(new_element) &&
6256 !IS_COLLECTIBLE(new_element))
6259 if (AmoebaNr[newx][newy] &&
6260 (new_element == EL_AMOEBA_FULL ||
6261 new_element == EL_BD_AMOEBA ||
6262 new_element == EL_AMOEBA_GROWING))
6264 AmoebaCnt[AmoebaNr[newx][newy]]--;
6265 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6268 if (IS_MOVING(newx, newy))
6269 RemoveMovingField(newx, newy);
6272 RemoveField(newx, newy);
6273 DrawLevelField(newx, newy);
6276 /* if digged element was about to explode, prevent the explosion */
6277 ExplodeField[newx][newy] = EX_TYPE_NONE;
6279 PlayLevelSoundAction(x, y, action);
6282 Store[newx][newy] = EL_EMPTY;
6284 /* this makes it possible to leave the removed element again */
6285 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
6286 Store[newx][newy] = new_element;
6288 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
6290 int move_leave_element = element_info[element].move_leave_element;
6292 /* this makes it possible to leave the removed element again */
6293 Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
6294 new_element : move_leave_element);
6298 if (move_pattern & MV_MAZE_RUNNER_STYLE)
6300 RunnerVisit[x][y] = FrameCounter;
6301 PlayerVisit[x][y] /= 8; /* expire player visit path */
6304 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
6306 if (!IS_FREE(newx, newy))
6308 if (IS_PLAYER(x, y))
6309 DrawPlayerField(x, y);
6311 DrawLevelField(x, y);
6317 boolean wanna_flame = !RND(10);
6318 int dx = newx - x, dy = newy - y;
6319 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
6320 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
6321 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
6322 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
6323 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
6324 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
6327 IS_CLASSIC_ENEMY(element1) ||
6328 IS_CLASSIC_ENEMY(element2)) &&
6329 element1 != EL_DRAGON && element2 != EL_DRAGON &&
6330 element1 != EL_FLAMES && element2 != EL_FLAMES)
6332 ResetGfxAnimation(x, y);
6333 GfxAction[x][y] = ACTION_ATTACKING;
6335 if (IS_PLAYER(x, y))
6336 DrawPlayerField(x, y);
6338 DrawLevelField(x, y);
6340 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
6342 MovDelay[x][y] = 50;
6346 RemoveField(newx, newy);
6348 Feld[newx][newy] = EL_FLAMES;
6349 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
6352 RemoveField(newx1, newy1);
6354 Feld[newx1][newy1] = EL_FLAMES;
6356 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
6359 RemoveField(newx2, newy2);
6361 Feld[newx2][newy2] = EL_FLAMES;
6368 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6369 Feld[newx][newy] == EL_DIAMOND)
6371 if (IS_MOVING(newx, newy))
6372 RemoveMovingField(newx, newy);
6375 Feld[newx][newy] = EL_EMPTY;
6376 DrawLevelField(newx, newy);
6379 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
6381 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6382 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
6384 if (AmoebaNr[newx][newy])
6386 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6387 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
6388 Feld[newx][newy] == EL_BD_AMOEBA)
6389 AmoebaCnt[AmoebaNr[newx][newy]]--;
6394 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
6396 RemoveMovingField(newx, newy);
6399 if (IS_MOVING(newx, newy))
6401 RemoveMovingField(newx, newy);
6406 Feld[newx][newy] = EL_EMPTY;
6407 DrawLevelField(newx, newy);
6410 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
6412 else if ((element == EL_PACMAN || element == EL_MOLE)
6413 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
6415 if (AmoebaNr[newx][newy])
6417 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6418 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
6419 Feld[newx][newy] == EL_BD_AMOEBA)
6420 AmoebaCnt[AmoebaNr[newx][newy]]--;
6423 if (element == EL_MOLE)
6425 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
6426 PlayLevelSound(x, y, SND_MOLE_DIGGING);
6428 ResetGfxAnimation(x, y);
6429 GfxAction[x][y] = ACTION_DIGGING;
6430 DrawLevelField(x, y);
6432 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
6434 return; /* wait for shrinking amoeba */
6436 else /* element == EL_PACMAN */
6438 Feld[newx][newy] = EL_EMPTY;
6439 DrawLevelField(newx, newy);
6440 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
6443 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
6444 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
6445 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
6447 /* wait for shrinking amoeba to completely disappear */
6450 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
6452 /* object was running against a wall */
6457 /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
6458 if (move_pattern & MV_ANY_DIRECTION &&
6459 move_pattern == MovDir[x][y])
6461 int blocking_element =
6462 (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
6464 CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
6467 element = Feld[x][y]; /* element might have changed */
6471 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
6472 DrawLevelElementAnimation(x, y, element);
6474 if (DONT_TOUCH(element))
6475 TestIfBadThingTouchesPlayer(x, y);
6480 InitMovingField(x, y, MovDir[x][y]);
6482 PlayLevelSoundAction(x, y, ACTION_MOVING);
6486 ContinueMoving(x, y);
6489 void ContinueMoving(int x, int y)
6491 int element = Feld[x][y];
6492 struct ElementInfo *ei = &element_info[element];
6493 int direction = MovDir[x][y];
6494 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
6495 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
6496 int newx = x + dx, newy = y + dy;
6497 int stored = Store[x][y];
6498 int stored_new = Store[newx][newy];
6499 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
6500 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
6501 boolean last_line = (newy == lev_fieldy - 1);
6503 MovPos[x][y] += getElementMoveStepsize(x, y);
6505 if (pushed_by_player) /* special case: moving object pushed by player */
6506 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
6508 if (ABS(MovPos[x][y]) < TILEX)
6510 DrawLevelField(x, y);
6512 return; /* element is still moving */
6515 /* element reached destination field */
6517 Feld[x][y] = EL_EMPTY;
6518 Feld[newx][newy] = element;
6519 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
6521 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
6523 element = Feld[newx][newy] = EL_ACID;
6525 else if (element == EL_MOLE)
6527 Feld[x][y] = EL_SAND;
6529 DrawLevelFieldCrumbledSandNeighbours(x, y);
6531 else if (element == EL_QUICKSAND_FILLING)
6533 element = Feld[newx][newy] = get_next_element(element);
6534 Store[newx][newy] = Store[x][y];
6536 else if (element == EL_QUICKSAND_EMPTYING)
6538 Feld[x][y] = get_next_element(element);
6539 element = Feld[newx][newy] = Store[x][y];
6541 else if (element == EL_MAGIC_WALL_FILLING)
6543 element = Feld[newx][newy] = get_next_element(element);
6544 if (!game.magic_wall_active)
6545 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
6546 Store[newx][newy] = Store[x][y];
6548 else if (element == EL_MAGIC_WALL_EMPTYING)
6550 Feld[x][y] = get_next_element(element);
6551 if (!game.magic_wall_active)
6552 Feld[x][y] = EL_MAGIC_WALL_DEAD;
6553 element = Feld[newx][newy] = Store[x][y];
6555 #if USE_NEW_CUSTOM_VALUE
6556 InitField(newx, newy, FALSE);
6559 else if (element == EL_BD_MAGIC_WALL_FILLING)
6561 element = Feld[newx][newy] = get_next_element(element);
6562 if (!game.magic_wall_active)
6563 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
6564 Store[newx][newy] = Store[x][y];
6566 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
6568 Feld[x][y] = get_next_element(element);
6569 if (!game.magic_wall_active)
6570 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
6571 element = Feld[newx][newy] = Store[x][y];
6573 #if USE_NEW_CUSTOM_VALUE
6574 InitField(newx, newy, FALSE);
6577 else if (element == EL_AMOEBA_DROPPING)
6579 Feld[x][y] = get_next_element(element);
6580 element = Feld[newx][newy] = Store[x][y];
6582 else if (element == EL_SOKOBAN_OBJECT)
6585 Feld[x][y] = Back[x][y];
6587 if (Back[newx][newy])
6588 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
6590 Back[x][y] = Back[newx][newy] = 0;
6593 Store[x][y] = EL_EMPTY;
6598 MovDelay[newx][newy] = 0;
6601 if (CAN_CHANGE_OR_HAS_ACTION(element))
6603 if (CAN_CHANGE(element))
6606 /* copy element change control values to new field */
6607 ChangeDelay[newx][newy] = ChangeDelay[x][y];
6608 ChangePage[newx][newy] = ChangePage[x][y];
6609 ChangeCount[newx][newy] = ChangeCount[x][y];
6610 ChangeEvent[newx][newy] = ChangeEvent[x][y];
6613 #if USE_NEW_CUSTOM_VALUE
6614 CustomValue[newx][newy] = CustomValue[x][y];
6620 #if USE_NEW_CUSTOM_VALUE
6621 CustomValue[newx][newy] = CustomValue[x][y];
6625 ChangeDelay[x][y] = 0;
6626 ChangePage[x][y] = -1;
6627 ChangeCount[x][y] = 0;
6628 ChangeEvent[x][y] = -1;
6630 #if USE_NEW_CUSTOM_VALUE
6631 CustomValue[x][y] = 0;
6634 /* copy animation control values to new field */
6635 GfxFrame[newx][newy] = GfxFrame[x][y];
6636 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
6637 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
6638 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
6640 Pushed[x][y] = Pushed[newx][newy] = FALSE;
6642 /* some elements can leave other elements behind after moving */
6644 if (ei->move_leave_element != EL_EMPTY &&
6645 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
6646 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
6648 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
6649 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
6650 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
6653 int move_leave_element = ei->move_leave_element;
6657 /* this makes it possible to leave the removed element again */
6658 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
6659 move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
6661 /* this makes it possible to leave the removed element again */
6662 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
6663 move_leave_element = stored;
6666 /* this makes it possible to leave the removed element again */
6667 if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
6668 ei->move_leave_element == EL_TRIGGER_ELEMENT)
6669 move_leave_element = stored;
6672 Feld[x][y] = move_leave_element;
6674 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
6675 MovDir[x][y] = direction;
6677 InitField(x, y, FALSE);
6679 if (GFX_CRUMBLED(Feld[x][y]))
6680 DrawLevelFieldCrumbledSandNeighbours(x, y);
6682 if (ELEM_IS_PLAYER(move_leave_element))
6683 RelocatePlayer(x, y, move_leave_element);
6686 /* do this after checking for left-behind element */
6687 ResetGfxAnimation(x, y); /* reset animation values for old field */
6689 if (!CAN_MOVE(element) ||
6690 (CAN_FALL(element) && direction == MV_DOWN &&
6691 (element == EL_SPRING ||
6692 element_info[element].move_pattern == MV_WHEN_PUSHED ||
6693 element_info[element].move_pattern == MV_WHEN_DROPPED)))
6694 GfxDir[x][y] = MovDir[newx][newy] = 0;
6696 DrawLevelField(x, y);
6697 DrawLevelField(newx, newy);
6699 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
6701 /* prevent pushed element from moving on in pushed direction */
6702 if (pushed_by_player && CAN_MOVE(element) &&
6703 element_info[element].move_pattern & MV_ANY_DIRECTION &&
6704 !(element_info[element].move_pattern & direction))
6705 TurnRound(newx, newy);
6707 /* prevent elements on conveyor belt from moving on in last direction */
6708 if (pushed_by_conveyor && CAN_FALL(element) &&
6709 direction & MV_HORIZONTAL)
6710 MovDir[newx][newy] = 0;
6712 if (!pushed_by_player)
6714 int nextx = newx + dx, nexty = newy + dy;
6715 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
6717 WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
6719 if (CAN_FALL(element) && direction == MV_DOWN)
6720 WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
6722 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
6723 CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
6726 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
6728 TestIfBadThingTouchesPlayer(newx, newy);
6729 TestIfBadThingTouchesFriend(newx, newy);
6731 if (!IS_CUSTOM_ELEMENT(element))
6732 TestIfBadThingTouchesOtherBadThing(newx, newy);
6734 else if (element == EL_PENGUIN)
6735 TestIfFriendTouchesBadThing(newx, newy);
6737 /* give the player one last chance (one more frame) to move away */
6738 if (CAN_FALL(element) && direction == MV_DOWN &&
6739 (last_line || (!IS_FREE(x, newy + 1) &&
6740 (!IS_PLAYER(x, newy + 1) ||
6741 game.engine_version < VERSION_IDENT(3,1,1,0)))))
6744 if (pushed_by_player && !game.use_change_when_pushing_bug)
6746 int push_side = MV_DIR_OPPOSITE(direction);
6747 struct PlayerInfo *player = PLAYERINFO(x, y);
6749 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
6750 player->index_bit, push_side);
6751 CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
6752 player->index_bit, push_side);
6755 if (element == EL_EMC_ANDROID && pushed_by_player) /* make another move */
6756 MovDelay[newx][newy] = 1;
6758 CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
6760 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
6763 if (ChangePage[newx][newy] != -1) /* delayed change */
6765 int page = ChangePage[newx][newy];
6766 struct ElementChangeInfo *change = &ei->change_page[page];
6768 ChangePage[newx][newy] = -1;
6770 if (change->can_change)
6772 if (ChangeElement(newx, newy, element, page))
6774 if (change->post_change_function)
6775 change->post_change_function(newx, newy);
6779 if (change->has_action)
6780 ExecuteCustomElementAction(newx, newy, element, page);
6784 TestIfElementHitsCustomElement(newx, newy, direction);
6785 TestIfPlayerTouchesCustomElement(newx, newy);
6786 TestIfElementTouchesCustomElement(newx, newy);
6789 if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
6790 IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
6791 CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
6792 MV_DIR_OPPOSITE(direction));
6796 int AmoebeNachbarNr(int ax, int ay)
6799 int element = Feld[ax][ay];
6801 static int xy[4][2] =
6809 for (i = 0; i < NUM_DIRECTIONS; i++)
6811 int x = ax + xy[i][0];
6812 int y = ay + xy[i][1];
6814 if (!IN_LEV_FIELD(x, y))
6817 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
6818 group_nr = AmoebaNr[x][y];
6824 void AmoebenVereinigen(int ax, int ay)
6826 int i, x, y, xx, yy;
6827 int new_group_nr = AmoebaNr[ax][ay];
6828 static int xy[4][2] =
6836 if (new_group_nr == 0)
6839 for (i = 0; i < NUM_DIRECTIONS; i++)
6844 if (!IN_LEV_FIELD(x, y))
6847 if ((Feld[x][y] == EL_AMOEBA_FULL ||
6848 Feld[x][y] == EL_BD_AMOEBA ||
6849 Feld[x][y] == EL_AMOEBA_DEAD) &&
6850 AmoebaNr[x][y] != new_group_nr)
6852 int old_group_nr = AmoebaNr[x][y];
6854 if (old_group_nr == 0)
6857 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
6858 AmoebaCnt[old_group_nr] = 0;
6859 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
6860 AmoebaCnt2[old_group_nr] = 0;
6863 SCAN_PLAYFIELD(xx, yy)
6865 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
6868 if (AmoebaNr[xx][yy] == old_group_nr)
6869 AmoebaNr[xx][yy] = new_group_nr;
6875 void AmoebeUmwandeln(int ax, int ay)
6879 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
6881 int group_nr = AmoebaNr[ax][ay];
6886 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
6887 printf("AmoebeUmwandeln(): This should never happen!\n");
6893 SCAN_PLAYFIELD(x, y)
6895 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
6898 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
6901 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
6905 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
6906 SND_AMOEBA_TURNING_TO_GEM :
6907 SND_AMOEBA_TURNING_TO_ROCK));
6912 static int xy[4][2] =
6920 for (i = 0; i < NUM_DIRECTIONS; i++)
6925 if (!IN_LEV_FIELD(x, y))
6928 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
6930 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
6931 SND_AMOEBA_TURNING_TO_GEM :
6932 SND_AMOEBA_TURNING_TO_ROCK));
6939 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
6942 int group_nr = AmoebaNr[ax][ay];
6943 boolean done = FALSE;
6948 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
6949 printf("AmoebeUmwandelnBD(): This should never happen!\n");
6955 SCAN_PLAYFIELD(x, y)
6957 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
6960 if (AmoebaNr[x][y] == group_nr &&
6961 (Feld[x][y] == EL_AMOEBA_DEAD ||
6962 Feld[x][y] == EL_BD_AMOEBA ||
6963 Feld[x][y] == EL_AMOEBA_GROWING))
6966 Feld[x][y] = new_element;
6967 InitField(x, y, FALSE);
6968 DrawLevelField(x, y);
6974 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
6975 SND_BD_AMOEBA_TURNING_TO_ROCK :
6976 SND_BD_AMOEBA_TURNING_TO_GEM));
6979 void AmoebeWaechst(int x, int y)
6981 static unsigned long sound_delay = 0;
6982 static unsigned long sound_delay_value = 0;
6984 if (!MovDelay[x][y]) /* start new growing cycle */
6988 if (DelayReached(&sound_delay, sound_delay_value))
6990 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
6991 sound_delay_value = 30;
6995 if (MovDelay[x][y]) /* wait some time before growing bigger */
6998 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7000 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
7001 6 - MovDelay[x][y]);
7003 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
7006 if (!MovDelay[x][y])
7008 Feld[x][y] = Store[x][y];
7010 DrawLevelField(x, y);
7015 void AmoebaDisappearing(int x, int y)
7017 static unsigned long sound_delay = 0;
7018 static unsigned long sound_delay_value = 0;
7020 if (!MovDelay[x][y]) /* start new shrinking cycle */
7024 if (DelayReached(&sound_delay, sound_delay_value))
7025 sound_delay_value = 30;
7028 if (MovDelay[x][y]) /* wait some time before shrinking */
7031 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7033 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
7034 6 - MovDelay[x][y]);
7036 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
7039 if (!MovDelay[x][y])
7041 Feld[x][y] = EL_EMPTY;
7042 DrawLevelField(x, y);
7044 /* don't let mole enter this field in this cycle;
7045 (give priority to objects falling to this field from above) */
7051 void AmoebeAbleger(int ax, int ay)
7054 int element = Feld[ax][ay];
7055 int graphic = el2img(element);
7056 int newax = ax, neway = ay;
7057 boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
7058 static int xy[4][2] =
7066 if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
7068 Feld[ax][ay] = EL_AMOEBA_DEAD;
7069 DrawLevelField(ax, ay);
7073 if (IS_ANIMATED(graphic))
7074 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7076 if (!MovDelay[ax][ay]) /* start making new amoeba field */
7077 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
7079 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
7082 if (MovDelay[ax][ay])
7086 if (can_drop) /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
7089 int x = ax + xy[start][0];
7090 int y = ay + xy[start][1];
7092 if (!IN_LEV_FIELD(x, y))
7095 if (IS_FREE(x, y) ||
7096 CAN_GROW_INTO(Feld[x][y]) ||
7097 Feld[x][y] == EL_QUICKSAND_EMPTY)
7103 if (newax == ax && neway == ay)
7106 else /* normal or "filled" (BD style) amoeba */
7109 boolean waiting_for_player = FALSE;
7111 for (i = 0; i < NUM_DIRECTIONS; i++)
7113 int j = (start + i) % 4;
7114 int x = ax + xy[j][0];
7115 int y = ay + xy[j][1];
7117 if (!IN_LEV_FIELD(x, y))
7120 if (IS_FREE(x, y) ||
7121 CAN_GROW_INTO(Feld[x][y]) ||
7122 Feld[x][y] == EL_QUICKSAND_EMPTY)
7128 else if (IS_PLAYER(x, y))
7129 waiting_for_player = TRUE;
7132 if (newax == ax && neway == ay) /* amoeba cannot grow */
7134 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
7136 Feld[ax][ay] = EL_AMOEBA_DEAD;
7137 DrawLevelField(ax, ay);
7138 AmoebaCnt[AmoebaNr[ax][ay]]--;
7140 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
7142 if (element == EL_AMOEBA_FULL)
7143 AmoebeUmwandeln(ax, ay);
7144 else if (element == EL_BD_AMOEBA)
7145 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
7150 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
7152 /* amoeba gets larger by growing in some direction */
7154 int new_group_nr = AmoebaNr[ax][ay];
7157 if (new_group_nr == 0)
7159 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
7160 printf("AmoebeAbleger(): This should never happen!\n");
7165 AmoebaNr[newax][neway] = new_group_nr;
7166 AmoebaCnt[new_group_nr]++;
7167 AmoebaCnt2[new_group_nr]++;
7169 /* if amoeba touches other amoeba(s) after growing, unify them */
7170 AmoebenVereinigen(newax, neway);
7172 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
7174 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
7180 if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
7181 (neway == lev_fieldy - 1 && newax != ax))
7183 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
7184 Store[newax][neway] = element;
7186 else if (neway == ay || element == EL_EMC_DRIPPER)
7188 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
7190 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
7194 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
7195 Feld[ax][ay] = EL_AMOEBA_DROPPING;
7196 Store[ax][ay] = EL_AMOEBA_DROP;
7197 ContinueMoving(ax, ay);
7201 DrawLevelField(newax, neway);
7204 void Life(int ax, int ay)
7208 static int life[4] = { 2, 3, 3, 3 }; /* parameters for "game of life" */
7211 int element = Feld[ax][ay];
7212 int graphic = el2img(element);
7213 int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
7215 boolean changed = FALSE;
7217 if (IS_ANIMATED(graphic))
7218 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7223 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
7224 MovDelay[ax][ay] = life_time;
7226 if (MovDelay[ax][ay]) /* wait some time before next cycle */
7229 if (MovDelay[ax][ay])
7233 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
7235 int xx = ax+x1, yy = ay+y1;
7238 if (!IN_LEV_FIELD(xx, yy))
7241 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
7243 int x = xx+x2, y = yy+y2;
7245 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
7248 if (((Feld[x][y] == element ||
7249 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
7251 (IS_FREE(x, y) && Stop[x][y]))
7255 if (xx == ax && yy == ay) /* field in the middle */
7257 if (nachbarn < life_parameter[0] ||
7258 nachbarn > life_parameter[1])
7260 Feld[xx][yy] = EL_EMPTY;
7262 DrawLevelField(xx, yy);
7263 Stop[xx][yy] = TRUE;
7267 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
7268 { /* free border field */
7269 if (nachbarn >= life_parameter[2] &&
7270 nachbarn <= life_parameter[3])
7272 Feld[xx][yy] = element;
7273 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
7275 DrawLevelField(xx, yy);
7276 Stop[xx][yy] = TRUE;
7283 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
7284 SND_GAME_OF_LIFE_GROWING);
7287 static void InitRobotWheel(int x, int y)
7289 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
7292 static void RunRobotWheel(int x, int y)
7294 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
7297 static void StopRobotWheel(int x, int y)
7299 if (ZX == x && ZY == y)
7303 static void InitTimegateWheel(int x, int y)
7305 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
7308 static void RunTimegateWheel(int x, int y)
7310 PlayLevelSound(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
7313 static void InitMagicBallDelay(int x, int y)
7316 ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
7318 ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
7322 static void ActivateMagicBall(int bx, int by)
7326 if (level.ball_random)
7328 int pos_border = RND(8); /* select one of the eight border elements */
7329 int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
7330 int xx = pos_content % 3;
7331 int yy = pos_content / 3;
7336 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
7337 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
7341 for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
7343 int xx = x - bx + 1;
7344 int yy = y - by + 1;
7346 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
7347 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
7351 game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
7354 static void InitDiagonalMovingElement(int x, int y)
7357 MovDelay[x][y] = level.android_move_time;
7361 void CheckExit(int x, int y)
7363 if (local_player->gems_still_needed > 0 ||
7364 local_player->sokobanfields_still_needed > 0 ||
7365 local_player->lights_still_needed > 0)
7367 int element = Feld[x][y];
7368 int graphic = el2img(element);
7370 if (IS_ANIMATED(graphic))
7371 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7376 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7379 Feld[x][y] = EL_EXIT_OPENING;
7381 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
7384 void CheckExitSP(int x, int y)
7386 if (local_player->gems_still_needed > 0)
7388 int element = Feld[x][y];
7389 int graphic = el2img(element);
7391 if (IS_ANIMATED(graphic))
7392 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7397 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7400 Feld[x][y] = EL_SP_EXIT_OPENING;
7402 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
7405 static void CloseAllOpenTimegates()
7410 SCAN_PLAYFIELD(x, y)
7412 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7415 int element = Feld[x][y];
7417 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
7419 Feld[x][y] = EL_TIMEGATE_CLOSING;
7421 PlayLevelSoundAction(x, y, ACTION_CLOSING);
7426 void EdelsteinFunkeln(int x, int y)
7428 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
7431 if (Feld[x][y] == EL_BD_DIAMOND)
7434 if (MovDelay[x][y] == 0) /* next animation frame */
7435 MovDelay[x][y] = 11 * !SimpleRND(500);
7437 if (MovDelay[x][y] != 0) /* wait some time before next frame */
7441 if (setup.direct_draw && MovDelay[x][y])
7442 SetDrawtoField(DRAW_BUFFERED);
7444 DrawLevelElementAnimation(x, y, Feld[x][y]);
7446 if (MovDelay[x][y] != 0)
7448 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
7449 10 - MovDelay[x][y]);
7451 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
7453 if (setup.direct_draw)
7457 dest_x = FX + SCREENX(x) * TILEX;
7458 dest_y = FY + SCREENY(y) * TILEY;
7460 BlitBitmap(drawto_field, window,
7461 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
7462 SetDrawtoField(DRAW_DIRECT);
7468 void MauerWaechst(int x, int y)
7472 if (!MovDelay[x][y]) /* next animation frame */
7473 MovDelay[x][y] = 3 * delay;
7475 if (MovDelay[x][y]) /* wait some time before next frame */
7479 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7481 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
7482 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
7484 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
7487 if (!MovDelay[x][y])
7489 if (MovDir[x][y] == MV_LEFT)
7491 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
7492 DrawLevelField(x - 1, y);
7494 else if (MovDir[x][y] == MV_RIGHT)
7496 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
7497 DrawLevelField(x + 1, y);
7499 else if (MovDir[x][y] == MV_UP)
7501 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
7502 DrawLevelField(x, y - 1);
7506 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
7507 DrawLevelField(x, y + 1);
7510 Feld[x][y] = Store[x][y];
7512 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7513 DrawLevelField(x, y);
7518 void MauerAbleger(int ax, int ay)
7520 int element = Feld[ax][ay];
7521 int graphic = el2img(element);
7522 boolean oben_frei = FALSE, unten_frei = FALSE;
7523 boolean links_frei = FALSE, rechts_frei = FALSE;
7524 boolean oben_massiv = FALSE, unten_massiv = FALSE;
7525 boolean links_massiv = FALSE, rechts_massiv = FALSE;
7526 boolean new_wall = FALSE;
7528 if (IS_ANIMATED(graphic))
7529 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7531 if (!MovDelay[ax][ay]) /* start building new wall */
7532 MovDelay[ax][ay] = 6;
7534 if (MovDelay[ax][ay]) /* wait some time before building new wall */
7537 if (MovDelay[ax][ay])
7541 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
7543 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
7545 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
7547 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
7550 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
7551 element == EL_EXPANDABLE_WALL_ANY)
7555 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
7556 Store[ax][ay-1] = element;
7557 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
7558 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
7559 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
7560 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
7565 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
7566 Store[ax][ay+1] = element;
7567 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
7568 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
7569 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
7570 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
7575 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7576 element == EL_EXPANDABLE_WALL_ANY ||
7577 element == EL_EXPANDABLE_WALL)
7581 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
7582 Store[ax-1][ay] = element;
7583 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
7584 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
7585 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
7586 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
7592 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
7593 Store[ax+1][ay] = element;
7594 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
7595 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
7596 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
7597 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
7602 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
7603 DrawLevelField(ax, ay);
7605 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
7607 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
7608 unten_massiv = TRUE;
7609 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
7610 links_massiv = TRUE;
7611 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
7612 rechts_massiv = TRUE;
7614 if (((oben_massiv && unten_massiv) ||
7615 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7616 element == EL_EXPANDABLE_WALL) &&
7617 ((links_massiv && rechts_massiv) ||
7618 element == EL_EXPANDABLE_WALL_VERTICAL))
7619 Feld[ax][ay] = EL_WALL;
7622 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
7625 void CheckForDragon(int x, int y)
7628 boolean dragon_found = FALSE;
7629 static int xy[4][2] =
7637 for (i = 0; i < NUM_DIRECTIONS; i++)
7639 for (j = 0; j < 4; j++)
7641 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7643 if (IN_LEV_FIELD(xx, yy) &&
7644 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
7646 if (Feld[xx][yy] == EL_DRAGON)
7647 dragon_found = TRUE;
7656 for (i = 0; i < NUM_DIRECTIONS; i++)
7658 for (j = 0; j < 3; j++)
7660 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7662 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
7664 Feld[xx][yy] = EL_EMPTY;
7665 DrawLevelField(xx, yy);
7674 static void InitBuggyBase(int x, int y)
7676 int element = Feld[x][y];
7677 int activating_delay = FRAMES_PER_SECOND / 4;
7680 (element == EL_SP_BUGGY_BASE ?
7681 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
7682 element == EL_SP_BUGGY_BASE_ACTIVATING ?
7684 element == EL_SP_BUGGY_BASE_ACTIVE ?
7685 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
7688 static void WarnBuggyBase(int x, int y)
7691 static int xy[4][2] =
7699 for (i = 0; i < NUM_DIRECTIONS; i++)
7701 int xx = x + xy[i][0];
7702 int yy = y + xy[i][1];
7704 if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
7706 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
7713 static void InitTrap(int x, int y)
7715 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
7718 static void ActivateTrap(int x, int y)
7720 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
7723 static void ChangeActiveTrap(int x, int y)
7725 int graphic = IMG_TRAP_ACTIVE;
7727 /* if new animation frame was drawn, correct crumbled sand border */
7728 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
7729 DrawLevelFieldCrumbledSand(x, y);
7732 static int getSpecialActionElement(int element, int number, int base_element)
7734 return (element != EL_EMPTY ? element :
7735 number != -1 ? base_element + number - 1 :
7739 static int getModifiedActionNumber(int value_old, int operator, int operand,
7740 int value_min, int value_max)
7742 int value_new = (operator == CA_MODE_SET ? operand :
7743 operator == CA_MODE_ADD ? value_old + operand :
7744 operator == CA_MODE_SUBTRACT ? value_old - operand :
7745 operator == CA_MODE_MULTIPLY ? value_old * operand :
7746 operator == CA_MODE_DIVIDE ? value_old / MAX(1, operand) :
7747 operator == CA_MODE_MODULO ? value_old % MAX(1, operand) :
7750 return (value_new < value_min ? value_min :
7751 value_new > value_max ? value_max :
7755 static void ExecuteCustomElementAction(int x, int y, int element, int page)
7757 struct ElementInfo *ei = &element_info[element];
7758 struct ElementChangeInfo *change = &ei->change_page[page];
7759 int action_type = change->action_type;
7760 int action_mode = change->action_mode;
7761 int action_arg = change->action_arg;
7764 if (!change->has_action)
7767 /* ---------- determine action paramater values -------------------------- */
7769 int level_time_value =
7770 (level.time > 0 ? TimeLeft :
7773 int action_arg_element =
7774 (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player :
7775 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
7776 action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element :
7779 int action_arg_direction =
7780 (action_arg >= CA_ARG_DIRECTION_LEFT &&
7781 action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
7782 action_arg == CA_ARG_DIRECTION_TRIGGER ?
7783 change->actual_trigger_side :
7784 action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
7785 MV_DIR_OPPOSITE(change->actual_trigger_side) :
7788 int action_arg_number_min =
7789 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
7792 int action_arg_number_max =
7793 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
7794 action_type == CA_SET_LEVEL_GEMS ? 999 :
7795 action_type == CA_SET_LEVEL_TIME ? 9999 :
7796 action_type == CA_SET_LEVEL_SCORE ? 99999 :
7797 action_type == CA_SET_CE_SCORE ? 9999 :
7798 action_type == CA_SET_CE_VALUE ? 9999 :
7801 int action_arg_number_reset =
7802 (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize :
7803 action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
7804 action_type == CA_SET_LEVEL_TIME ? level.time :
7805 action_type == CA_SET_LEVEL_SCORE ? 0 :
7806 action_type == CA_SET_CE_SCORE ? 0 :
7808 action_type == CA_SET_CE_VALUE ? GET_NEW_CUSTOM_VALUE(element) :
7810 action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
7814 int action_arg_number =
7815 (action_arg <= CA_ARG_MAX ? action_arg :
7816 action_arg >= CA_ARG_SPEED_NOT_MOVING &&
7817 action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
7818 action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
7819 action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
7820 action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
7821 action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
7822 action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
7823 #if USE_NEW_CUSTOM_VALUE
7824 action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
7826 action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
7828 action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
7829 action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
7830 action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
7831 action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
7832 action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CUSTOM_VALUE(change->target_element) :
7833 action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
7834 action_arg == CA_ARG_ELEMENT_NR_TARGET ? change->target_element :
7835 action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
7838 int action_arg_number_old =
7839 (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
7840 action_type == CA_SET_LEVEL_TIME ? TimeLeft :
7841 action_type == CA_SET_LEVEL_SCORE ? local_player->score :
7842 action_type == CA_SET_CE_SCORE ? ei->collect_score :
7843 action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
7846 int action_arg_number_new =
7847 getModifiedActionNumber(action_arg_number_old,
7848 action_mode, action_arg_number,
7849 action_arg_number_min, action_arg_number_max);
7851 int trigger_player_bits =
7852 (change->actual_trigger_player >= EL_PLAYER_1 &&
7853 change->actual_trigger_player <= EL_PLAYER_4 ?
7854 (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
7857 int action_arg_player_bits =
7858 (action_arg >= CA_ARG_PLAYER_1 &&
7859 action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
7860 action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
7863 /* ---------- execute action -------------------------------------------- */
7872 /* ---------- level actions ------------------------------------------- */
7874 case CA_RESTART_LEVEL:
7876 game.restart_level = TRUE;
7881 case CA_SHOW_ENVELOPE:
7883 int element = getSpecialActionElement(action_arg_element,
7884 action_arg_number, EL_ENVELOPE_1);
7886 if (IS_ENVELOPE(element))
7887 local_player->show_envelope = element;
7892 case CA_SET_LEVEL_TIME:
7894 if (level.time > 0) /* only modify limited time value */
7896 TimeLeft = action_arg_number_new;
7898 DrawGameValue_Time(TimeLeft);
7900 if (!TimeLeft && setup.time_limit)
7901 for (i = 0; i < MAX_PLAYERS; i++)
7902 KillPlayer(&stored_player[i]);
7908 case CA_SET_LEVEL_SCORE:
7910 local_player->score = action_arg_number_new;
7912 DrawGameValue_Score(local_player->score);
7917 case CA_SET_LEVEL_GEMS:
7919 local_player->gems_still_needed = action_arg_number_new;
7921 DrawGameValue_Emeralds(local_player->gems_still_needed);
7926 case CA_SET_LEVEL_GRAVITY:
7928 game.gravity = (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
7929 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
7930 action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
7935 case CA_SET_LEVEL_WIND:
7937 game.wind_direction = action_arg_direction;
7942 /* ---------- player actions ------------------------------------------ */
7944 case CA_MOVE_PLAYER:
7946 /* automatically move to the next field in specified direction */
7947 for (i = 0; i < MAX_PLAYERS; i++)
7948 if (trigger_player_bits & (1 << i))
7949 stored_player[i].programmed_action = action_arg_direction;
7954 case CA_EXIT_PLAYER:
7956 for (i = 0; i < MAX_PLAYERS; i++)
7957 if (action_arg_player_bits & (1 << i))
7958 stored_player[i].LevelSolved = stored_player[i].GameOver = TRUE;
7963 case CA_KILL_PLAYER:
7965 for (i = 0; i < MAX_PLAYERS; i++)
7966 if (action_arg_player_bits & (1 << i))
7967 KillPlayer(&stored_player[i]);
7972 case CA_SET_PLAYER_KEYS:
7974 int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
7975 int element = getSpecialActionElement(action_arg_element,
7976 action_arg_number, EL_KEY_1);
7978 if (IS_KEY(element))
7980 for (i = 0; i < MAX_PLAYERS; i++)
7982 if (trigger_player_bits & (1 << i))
7984 stored_player[i].key[KEY_NR(element)] = key_state;
7986 DrawGameValue_Keys(stored_player[i].key);
7988 redraw_mask |= REDRAW_DOOR_1;
7996 case CA_SET_PLAYER_SPEED:
7998 for (i = 0; i < MAX_PLAYERS; i++)
8000 if (trigger_player_bits & (1 << i))
8002 int move_stepsize = TILEX / stored_player[i].move_delay_value;
8004 if (action_arg == CA_ARG_SPEED_FASTER &&
8005 stored_player[i].cannot_move)
8007 action_arg_number = STEPSIZE_VERY_SLOW;
8009 else if (action_arg == CA_ARG_SPEED_SLOWER ||
8010 action_arg == CA_ARG_SPEED_FASTER)
8012 action_arg_number = 2;
8013 action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
8018 getModifiedActionNumber(move_stepsize,
8021 action_arg_number_min,
8022 action_arg_number_max);
8025 SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
8027 /* make sure that value is power of 2 */
8028 move_stepsize = (1 << log_2(move_stepsize));
8030 /* do no immediately change -- the player might just be moving */
8031 stored_player[i].move_delay_value_next = TILEX / move_stepsize;
8033 stored_player[i].cannot_move =
8034 (action_arg == CA_ARG_SPEED_NOT_MOVING ? TRUE : FALSE);
8042 case CA_SET_PLAYER_SHIELD:
8044 for (i = 0; i < MAX_PLAYERS; i++)
8046 if (trigger_player_bits & (1 << i))
8048 if (action_arg == CA_ARG_SHIELD_OFF)
8050 stored_player[i].shield_normal_time_left = 0;
8051 stored_player[i].shield_deadly_time_left = 0;
8053 else if (action_arg == CA_ARG_SHIELD_NORMAL)
8055 stored_player[i].shield_normal_time_left = 999999;
8057 else if (action_arg == CA_ARG_SHIELD_DEADLY)
8059 stored_player[i].shield_normal_time_left = 999999;
8060 stored_player[i].shield_deadly_time_left = 999999;
8068 case CA_SET_PLAYER_ARTWORK:
8070 for (i = 0; i < MAX_PLAYERS; i++)
8072 if (trigger_player_bits & (1 << i))
8074 int artwork_element = action_arg_element;
8076 if (action_arg == CA_ARG_ELEMENT_RESET)
8078 (level.use_artwork_element[i] ? level.artwork_element[i] :
8079 stored_player[i].element_nr);
8081 stored_player[i].artwork_element = artwork_element;
8083 SetPlayerWaiting(&stored_player[i], FALSE);
8085 /* set number of special actions for bored and sleeping animation */
8086 stored_player[i].num_special_action_bored =
8087 get_num_special_action(artwork_element,
8088 ACTION_BORING_1, ACTION_BORING_LAST);
8089 stored_player[i].num_special_action_sleeping =
8090 get_num_special_action(artwork_element,
8091 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
8098 /* ---------- CE actions ---------------------------------------------- */
8100 case CA_SET_CE_SCORE:
8102 ei->collect_score = action_arg_number_new;
8107 case CA_SET_CE_VALUE:
8109 #if USE_NEW_CUSTOM_VALUE
8110 int last_custom_value = CustomValue[x][y];
8112 CustomValue[x][y] = action_arg_number_new;
8115 printf("::: Count == %d\n", CustomValue[x][y]);
8118 if (CustomValue[x][y] == 0 && last_custom_value > 0)
8121 printf("::: CE_VALUE_GETS_ZERO\n");
8124 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
8125 CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
8128 printf("::: RESULT: %d, %d\n", Feld[x][y], ChangePage[x][y]);
8136 /* ---------- engine actions ------------------------------------------ */
8138 case CA_SET_ENGINE_SCAN_MODE:
8140 InitPlayfieldScanMode(action_arg);
8150 static void CreateFieldExt(int x, int y, int element, boolean is_change)
8152 int previous_move_direction = MovDir[x][y];
8153 #if USE_NEW_CUSTOM_VALUE
8154 int last_ce_value = CustomValue[x][y];
8156 boolean add_player = (ELEM_IS_PLAYER(element) &&
8157 IS_WALKABLE(Feld[x][y]));
8159 /* check if element under player changes from accessible to unaccessible
8160 (needed for special case of dropping element which then changes) */
8161 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
8162 IS_ACCESSIBLE(Feld[x][y]) && !IS_ACCESSIBLE(element))
8171 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
8172 RemoveMovingField(x, y);
8176 Feld[x][y] = element;
8178 ResetGfxAnimation(x, y);
8179 ResetRandomAnimationValue(x, y);
8181 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8182 MovDir[x][y] = previous_move_direction;
8184 #if USE_NEW_CUSTOM_VALUE
8185 if (element_info[Feld[x][y]].use_last_ce_value)
8186 CustomValue[x][y] = last_ce_value;
8189 InitField_WithBug1(x, y, FALSE);
8191 DrawLevelField(x, y);
8193 if (GFX_CRUMBLED(Feld[x][y]))
8194 DrawLevelFieldCrumbledSandNeighbours(x, y);
8197 /* "ChangeCount" not set yet to allow "entered by player" change one time */
8198 if (ELEM_IS_PLAYER(element))
8199 RelocatePlayer(x, y, element);
8202 ChangeCount[x][y]++; /* count number of changes in the same frame */
8204 TestIfBadThingTouchesPlayer(x, y);
8205 TestIfPlayerTouchesCustomElement(x, y);
8206 TestIfElementTouchesCustomElement(x, y);
8209 static void CreateField(int x, int y, int element)
8211 CreateFieldExt(x, y, element, FALSE);
8214 static void CreateElementFromChange(int x, int y, int element)
8216 element = GET_VALID_RUNTIME_ELEMENT(element);
8218 #if USE_STOP_CHANGED_ELEMENTS
8219 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
8221 int old_element = Feld[x][y];
8223 /* prevent changed element from moving in same engine frame
8224 unless both old and new element can either fall or move */
8225 if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
8226 (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
8231 CreateFieldExt(x, y, element, TRUE);
8234 static boolean ChangeElement(int x, int y, int element, int page)
8236 struct ElementChangeInfo *change = &element_info[element].change_page[page];
8238 int old_element = Feld[x][y];
8240 /* always use default change event to prevent running into a loop */
8241 if (ChangeEvent[x][y] == -1)
8242 ChangeEvent[x][y] = CE_DELAY;
8244 if (ChangeEvent[x][y] == CE_DELAY)
8246 /* reset actual trigger element, trigger player and action element */
8247 change->actual_trigger_element = EL_EMPTY;
8248 change->actual_trigger_player = EL_PLAYER_1;
8249 change->actual_trigger_side = CH_SIDE_NONE;
8250 change->actual_trigger_ce_value = 0;
8253 /* do not change elements more than a specified maximum number of changes */
8254 if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
8257 ChangeCount[x][y]++; /* count number of changes in the same frame */
8259 if (change->explode)
8266 if (change->use_target_content)
8268 boolean complete_replace = TRUE;
8269 boolean can_replace[3][3];
8272 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
8275 boolean is_walkable;
8276 boolean is_diggable;
8277 boolean is_collectible;
8278 boolean is_removable;
8279 boolean is_destructible;
8280 int ex = x + xx - 1;
8281 int ey = y + yy - 1;
8282 int content_element = change->target_content.e[xx][yy];
8285 can_replace[xx][yy] = TRUE;
8287 if (ex == x && ey == y) /* do not check changing element itself */
8290 if (content_element == EL_EMPTY_SPACE)
8292 can_replace[xx][yy] = FALSE; /* do not replace border with space */
8297 if (!IN_LEV_FIELD(ex, ey))
8299 can_replace[xx][yy] = FALSE;
8300 complete_replace = FALSE;
8307 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
8308 e = MovingOrBlocked2Element(ex, ey);
8310 is_empty = (IS_FREE(ex, ey) ||
8311 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
8313 is_walkable = (is_empty || IS_WALKABLE(e));
8314 is_diggable = (is_empty || IS_DIGGABLE(e));
8315 is_collectible = (is_empty || IS_COLLECTIBLE(e));
8316 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
8317 is_removable = (is_diggable || is_collectible);
8319 can_replace[xx][yy] =
8320 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
8321 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
8322 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
8323 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
8324 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
8325 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
8326 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
8328 if (!can_replace[xx][yy])
8329 complete_replace = FALSE;
8332 if (!change->only_if_complete || complete_replace)
8334 boolean something_has_changed = FALSE;
8336 if (change->only_if_complete && change->use_random_replace &&
8337 RND(100) < change->random_percentage)
8340 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
8342 int ex = x + xx - 1;
8343 int ey = y + yy - 1;
8344 int content_element;
8346 if (can_replace[xx][yy] && (!change->use_random_replace ||
8347 RND(100) < change->random_percentage))
8349 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
8350 RemoveMovingField(ex, ey);
8352 ChangeEvent[ex][ey] = ChangeEvent[x][y];
8354 content_element = change->target_content.e[xx][yy];
8355 target_element = GET_TARGET_ELEMENT(content_element, change);
8357 CreateElementFromChange(ex, ey, target_element);
8359 something_has_changed = TRUE;
8361 /* for symmetry reasons, freeze newly created border elements */
8362 if (ex != x || ey != y)
8363 Stop[ex][ey] = TRUE; /* no more moving in this frame */
8367 if (something_has_changed)
8369 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
8370 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
8376 target_element = GET_TARGET_ELEMENT(change->target_element, change);
8378 if (element == EL_DIAGONAL_GROWING ||
8379 element == EL_DIAGONAL_SHRINKING)
8381 target_element = Store[x][y];
8383 Store[x][y] = EL_EMPTY;
8386 CreateElementFromChange(x, y, target_element);
8388 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
8389 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
8392 /* this uses direct change before indirect change */
8393 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
8398 #if USE_NEW_DELAYED_ACTION
8400 static void HandleElementChange(int x, int y, int page)
8402 int element = MovingOrBlocked2Element(x, y);
8403 struct ElementInfo *ei = &element_info[element];
8404 struct ElementChangeInfo *change = &ei->change_page[page];
8407 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
8408 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
8411 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
8412 x, y, element, element_info[element].token_name);
8413 printf("HandleElementChange(): This should never happen!\n");
8418 /* this can happen with classic bombs on walkable, changing elements */
8419 if (!CAN_CHANGE_OR_HAS_ACTION(element))
8422 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
8423 ChangeDelay[x][y] = 0;
8429 if (ChangeDelay[x][y] == 0) /* initialize element change */
8431 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
8433 if (change->can_change)
8435 ResetGfxAnimation(x, y);
8436 ResetRandomAnimationValue(x, y);
8438 if (change->pre_change_function)
8439 change->pre_change_function(x, y);
8443 ChangeDelay[x][y]--;
8445 if (ChangeDelay[x][y] != 0) /* continue element change */
8447 if (change->can_change)
8449 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8451 if (IS_ANIMATED(graphic))
8452 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8454 if (change->change_function)
8455 change->change_function(x, y);
8458 else /* finish element change */
8460 if (ChangePage[x][y] != -1) /* remember page from delayed change */
8462 page = ChangePage[x][y];
8463 ChangePage[x][y] = -1;
8465 change = &ei->change_page[page];
8468 if (IS_MOVING(x, y)) /* never change a running system ;-) */
8470 ChangeDelay[x][y] = 1; /* try change after next move step */
8471 ChangePage[x][y] = page; /* remember page to use for change */
8476 if (change->can_change)
8478 if (ChangeElement(x, y, element, page))
8480 if (change->post_change_function)
8481 change->post_change_function(x, y);
8485 if (change->has_action)
8486 ExecuteCustomElementAction(x, y, element, page);
8492 static void HandleElementChange(int x, int y, int page)
8494 int element = MovingOrBlocked2Element(x, y);
8495 struct ElementInfo *ei = &element_info[element];
8496 struct ElementChangeInfo *change = &ei->change_page[page];
8499 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
8502 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
8503 x, y, element, element_info[element].token_name);
8504 printf("HandleElementChange(): This should never happen!\n");
8509 /* this can happen with classic bombs on walkable, changing elements */
8510 if (!CAN_CHANGE(element))
8513 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
8514 ChangeDelay[x][y] = 0;
8520 if (ChangeDelay[x][y] == 0) /* initialize element change */
8522 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
8524 ResetGfxAnimation(x, y);
8525 ResetRandomAnimationValue(x, y);
8527 if (change->pre_change_function)
8528 change->pre_change_function(x, y);
8531 ChangeDelay[x][y]--;
8533 if (ChangeDelay[x][y] != 0) /* continue element change */
8535 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8537 if (IS_ANIMATED(graphic))
8538 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8540 if (change->change_function)
8541 change->change_function(x, y);
8543 else /* finish element change */
8545 if (ChangePage[x][y] != -1) /* remember page from delayed change */
8547 page = ChangePage[x][y];
8548 ChangePage[x][y] = -1;
8550 change = &ei->change_page[page];
8553 if (IS_MOVING(x, y)) /* never change a running system ;-) */
8555 ChangeDelay[x][y] = 1; /* try change after next move step */
8556 ChangePage[x][y] = page; /* remember page to use for change */
8561 if (ChangeElement(x, y, element, page))
8563 if (change->post_change_function)
8564 change->post_change_function(x, y);
8571 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
8572 int trigger_element,
8578 boolean change_done_any = FALSE;
8579 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
8582 if (!(trigger_events[trigger_element][trigger_event]))
8585 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8587 int element = EL_CUSTOM_START + i;
8588 boolean change_done = FALSE;
8591 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
8592 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
8595 for (p = 0; p < element_info[element].num_change_pages; p++)
8597 struct ElementChangeInfo *change = &element_info[element].change_page[p];
8599 if (change->can_change_or_has_action &&
8600 change->has_event[trigger_event] &&
8601 change->trigger_side & trigger_side &&
8602 change->trigger_player & trigger_player &&
8603 change->trigger_page & trigger_page_bits &&
8604 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
8606 change->actual_trigger_element = trigger_element;
8607 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
8608 change->actual_trigger_side = trigger_side;
8609 change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
8611 if ((change->can_change && !change_done) || change->has_action)
8616 SCAN_PLAYFIELD(x, y)
8618 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8621 if (Feld[x][y] == element)
8623 if (change->can_change && !change_done)
8625 ChangeDelay[x][y] = 1;
8626 ChangeEvent[x][y] = trigger_event;
8628 HandleElementChange(x, y, p);
8630 #if USE_NEW_DELAYED_ACTION
8631 else if (change->has_action)
8633 ExecuteCustomElementAction(x, y, element, p);
8634 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8637 if (change->has_action)
8639 ExecuteCustomElementAction(x, y, element, p);
8640 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8646 if (change->can_change)
8649 change_done_any = TRUE;
8656 return change_done_any;
8659 static boolean CheckElementChangeExt(int x, int y,
8661 int trigger_element,
8666 boolean change_done = FALSE;
8669 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
8670 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
8673 if (Feld[x][y] == EL_BLOCKED)
8675 Blocked2Moving(x, y, &x, &y);
8676 element = Feld[x][y];
8680 /* check if element has already changed */
8681 if (Feld[x][y] != element)
8684 /* check if element has already changed or is about to change after moving */
8685 if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
8686 Feld[x][y] != element) ||
8688 (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
8689 (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
8690 ChangePage[x][y] != -1)))
8694 for (p = 0; p < element_info[element].num_change_pages; p++)
8696 struct ElementChangeInfo *change = &element_info[element].change_page[p];
8698 boolean check_trigger_element =
8699 (trigger_event == CE_TOUCHING_X ||
8700 trigger_event == CE_HITTING_X ||
8701 trigger_event == CE_HIT_BY_X);
8703 if (change->can_change_or_has_action &&
8704 change->has_event[trigger_event] &&
8705 change->trigger_side & trigger_side &&
8706 change->trigger_player & trigger_player &&
8707 (!check_trigger_element ||
8708 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
8710 change->actual_trigger_element = trigger_element;
8711 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
8712 change->actual_trigger_side = trigger_side;
8713 change->actual_trigger_ce_value = CustomValue[x][y];
8715 /* special case: trigger element not at (x,y) position for some events */
8716 if (check_trigger_element)
8728 { 0, 0 }, { 0, 0 }, { 0, 0 },
8732 int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
8733 int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
8735 change->actual_trigger_ce_value = CustomValue[xx][yy];
8738 if (change->can_change && !change_done)
8740 ChangeDelay[x][y] = 1;
8741 ChangeEvent[x][y] = trigger_event;
8743 HandleElementChange(x, y, p);
8747 #if USE_NEW_DELAYED_ACTION
8748 else if (change->has_action)
8750 ExecuteCustomElementAction(x, y, element, p);
8751 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8754 if (change->has_action)
8756 ExecuteCustomElementAction(x, y, element, p);
8757 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8766 static void PlayPlayerSound(struct PlayerInfo *player)
8768 int jx = player->jx, jy = player->jy;
8769 int sound_element = player->artwork_element;
8770 int last_action = player->last_action_waiting;
8771 int action = player->action_waiting;
8773 if (player->is_waiting)
8775 if (action != last_action)
8776 PlayLevelSoundElementAction(jx, jy, sound_element, action);
8778 PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
8782 if (action != last_action)
8783 StopSound(element_info[sound_element].sound[last_action]);
8785 if (last_action == ACTION_SLEEPING)
8786 PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
8790 static void PlayAllPlayersSound()
8794 for (i = 0; i < MAX_PLAYERS; i++)
8795 if (stored_player[i].active)
8796 PlayPlayerSound(&stored_player[i]);
8799 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
8801 boolean last_waiting = player->is_waiting;
8802 int move_dir = player->MovDir;
8804 player->dir_waiting = move_dir;
8805 player->last_action_waiting = player->action_waiting;
8809 if (!last_waiting) /* not waiting -> waiting */
8811 player->is_waiting = TRUE;
8813 player->frame_counter_bored =
8815 game.player_boring_delay_fixed +
8816 SimpleRND(game.player_boring_delay_random);
8817 player->frame_counter_sleeping =
8819 game.player_sleeping_delay_fixed +
8820 SimpleRND(game.player_sleeping_delay_random);
8823 InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
8825 InitPlayerGfxAnimation(player, ACTION_WAITING, player->MovDir);
8829 if (game.player_sleeping_delay_fixed +
8830 game.player_sleeping_delay_random > 0 &&
8831 player->anim_delay_counter == 0 &&
8832 player->post_delay_counter == 0 &&
8833 FrameCounter >= player->frame_counter_sleeping)
8834 player->is_sleeping = TRUE;
8835 else if (game.player_boring_delay_fixed +
8836 game.player_boring_delay_random > 0 &&
8837 FrameCounter >= player->frame_counter_bored)
8838 player->is_bored = TRUE;
8840 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
8841 player->is_bored ? ACTION_BORING :
8845 if (player->is_sleeping && player->use_murphy)
8847 /* special case for sleeping Murphy when leaning against non-free tile */
8849 if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
8850 Feld[player->jx - 1][player->jy] != EL_EMPTY)
8852 else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
8853 Feld[player->jx + 1][player->jy] != EL_EMPTY)
8854 move_dir = MV_RIGHT;
8856 player->is_sleeping = FALSE;
8858 player->dir_waiting = move_dir;
8862 if (player->is_sleeping)
8864 if (player->num_special_action_sleeping > 0)
8866 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
8868 int last_special_action = player->special_action_sleeping;
8869 int num_special_action = player->num_special_action_sleeping;
8870 int special_action =
8871 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
8872 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
8873 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
8874 last_special_action + 1 : ACTION_SLEEPING);
8875 int special_graphic =
8876 el_act_dir2img(player->artwork_element, special_action, move_dir);
8878 player->anim_delay_counter =
8879 graphic_info[special_graphic].anim_delay_fixed +
8880 SimpleRND(graphic_info[special_graphic].anim_delay_random);
8881 player->post_delay_counter =
8882 graphic_info[special_graphic].post_delay_fixed +
8883 SimpleRND(graphic_info[special_graphic].post_delay_random);
8885 player->special_action_sleeping = special_action;
8888 if (player->anim_delay_counter > 0)
8890 player->action_waiting = player->special_action_sleeping;
8891 player->anim_delay_counter--;
8893 else if (player->post_delay_counter > 0)
8895 player->post_delay_counter--;
8899 else if (player->is_bored)
8901 if (player->num_special_action_bored > 0)
8903 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
8905 int special_action =
8906 ACTION_BORING_1 + SimpleRND(player->num_special_action_bored);
8907 int special_graphic =
8908 el_act_dir2img(player->artwork_element, special_action, move_dir);
8910 player->anim_delay_counter =
8911 graphic_info[special_graphic].anim_delay_fixed +
8912 SimpleRND(graphic_info[special_graphic].anim_delay_random);
8913 player->post_delay_counter =
8914 graphic_info[special_graphic].post_delay_fixed +
8915 SimpleRND(graphic_info[special_graphic].post_delay_random);
8917 player->special_action_bored = special_action;
8920 if (player->anim_delay_counter > 0)
8922 player->action_waiting = player->special_action_bored;
8923 player->anim_delay_counter--;
8925 else if (player->post_delay_counter > 0)
8927 player->post_delay_counter--;
8932 else if (last_waiting) /* waiting -> not waiting */
8934 player->is_waiting = FALSE;
8935 player->is_bored = FALSE;
8936 player->is_sleeping = FALSE;
8938 player->frame_counter_bored = -1;
8939 player->frame_counter_sleeping = -1;
8941 player->anim_delay_counter = 0;
8942 player->post_delay_counter = 0;
8944 player->dir_waiting = player->MovDir;
8945 player->action_waiting = ACTION_DEFAULT;
8947 player->special_action_bored = ACTION_DEFAULT;
8948 player->special_action_sleeping = ACTION_DEFAULT;
8952 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
8954 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
8955 int left = player_action & JOY_LEFT;
8956 int right = player_action & JOY_RIGHT;
8957 int up = player_action & JOY_UP;
8958 int down = player_action & JOY_DOWN;
8959 int button1 = player_action & JOY_BUTTON_1;
8960 int button2 = player_action & JOY_BUTTON_2;
8961 int dx = (left ? -1 : right ? 1 : 0);
8962 int dy = (up ? -1 : down ? 1 : 0);
8964 if (!player->active || tape.pausing)
8970 snapped = SnapField(player, dx, dy);
8974 dropped = DropElement(player);
8976 moved = MovePlayer(player, dx, dy);
8979 if (tape.single_step && tape.recording && !tape.pausing)
8981 if (button1 || (dropped && !moved))
8983 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8984 SnapField(player, 0, 0); /* stop snapping */
8988 SetPlayerWaiting(player, FALSE);
8990 return player_action;
8994 /* no actions for this player (no input at player's configured device) */
8996 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
8997 SnapField(player, 0, 0);
8998 CheckGravityMovementWhenNotMoving(player);
9000 if (player->MovPos == 0)
9001 SetPlayerWaiting(player, TRUE);
9003 if (player->MovPos == 0) /* needed for tape.playing */
9004 player->is_moving = FALSE;
9006 player->is_dropping = FALSE;
9007 player->is_dropping_pressed = FALSE;
9008 player->drop_pressed_delay = 0;
9014 void AdvanceFrameAndPlayerCounters(int player_nr)
9018 /* advance frame counters (global frame counter and time frame counter) */
9022 /* advance player counters (counters for move delay, move animation etc.) */
9023 for (i = 0; i < MAX_PLAYERS; i++)
9025 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
9026 int move_delay_value = stored_player[i].move_delay_value;
9027 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
9029 if (!advance_player_counters) /* not all players may be affected */
9032 #if USE_NEW_PLAYER_ANIM
9033 if (move_frames == 0) /* less than one move per game frame */
9035 int stepsize = TILEX / move_delay_value;
9036 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
9037 int count = (stored_player[i].is_moving ?
9038 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
9040 if (count % delay == 0)
9045 stored_player[i].Frame += move_frames;
9047 if (stored_player[i].MovPos != 0)
9048 stored_player[i].StepFrame += move_frames;
9050 if (stored_player[i].move_delay > 0)
9051 stored_player[i].move_delay--;
9053 /* due to bugs in previous versions, counter must count up, not down */
9054 if (stored_player[i].push_delay != -1)
9055 stored_player[i].push_delay++;
9057 if (stored_player[i].drop_delay > 0)
9058 stored_player[i].drop_delay--;
9060 if (stored_player[i].is_dropping_pressed)
9061 stored_player[i].drop_pressed_delay++;
9067 static unsigned long game_frame_delay = 0;
9068 unsigned long game_frame_delay_value;
9069 int magic_wall_x = 0, magic_wall_y = 0;
9070 int i, x, y, element, graphic;
9071 byte *recorded_player_action;
9072 byte summarized_player_action = 0;
9073 byte tape_action[MAX_PLAYERS];
9075 if (game_status != GAME_MODE_PLAYING)
9078 game_frame_delay_value =
9079 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
9081 if (tape.playing && tape.warp_forward && !tape.pausing)
9082 game_frame_delay_value = 0;
9084 /* ---------- main game synchronization point ---------- */
9086 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
9088 InitPlayfieldScanModeVars();
9090 if (game.set_centered_player)
9092 boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
9094 /* switching to "all players" only possible if all players fit to screen */
9095 if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
9097 game.centered_player_nr_next = game.centered_player_nr;
9098 game.set_centered_player = FALSE;
9101 /* do not switch focus to non-existing (or non-active) player */
9102 if (game.centered_player_nr_next >= 0 &&
9103 !stored_player[game.centered_player_nr_next].active)
9105 game.centered_player_nr_next = game.centered_player_nr;
9106 game.set_centered_player = FALSE;
9110 if (game.set_centered_player &&
9111 ScreenMovPos == 0) /* screen currently aligned at tile position */
9114 struct PlayerInfo *player;
9115 int player_nr = game.centered_player_nr_next;
9119 if (game.centered_player_nr_next == -1)
9121 setScreenCenteredToAllPlayers(&sx, &sy);
9125 sx = stored_player[game.centered_player_nr_next].jx;
9126 sy = stored_player[game.centered_player_nr_next].jy;
9130 player = &stored_player[player_nr];
9132 if (!player->active)
9133 game.centered_player_nr_next = game.centered_player_nr;
9140 if (game.centered_player_nr != game.centered_player_nr_next)
9144 DrawRelocateScreen(sx, sy, MV_NONE, TRUE, setup.quick_switch);
9146 DrawRelocatePlayer(player, setup.quick_switch);
9149 game.centered_player_nr = game.centered_player_nr_next;
9152 game.set_centered_player = FALSE;
9155 #if USE_ONE_MORE_CHANGE_PER_FRAME
9156 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
9158 SCAN_PLAYFIELD(x, y)
9160 ChangeCount[x][y] = 0;
9161 ChangeEvent[x][y] = -1;
9166 if (network_playing && !network_player_action_received)
9168 /* try to get network player actions in time */
9170 #if defined(NETWORK_AVALIABLE)
9171 /* last chance to get network player actions without main loop delay */
9175 /* game was quit by network peer */
9176 if (game_status != GAME_MODE_PLAYING)
9179 if (!network_player_action_received)
9180 return; /* failed to get network player actions in time */
9186 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
9189 /* !!! CHECK THIS (tape.pausing is always FALSE here!) !!! */
9190 if (recorded_player_action == NULL && tape.pausing)
9194 for (i = 0; i < MAX_PLAYERS; i++)
9196 summarized_player_action |= stored_player[i].action;
9198 if (!network_playing)
9199 stored_player[i].effective_action = stored_player[i].action;
9202 #if defined(NETWORK_AVALIABLE)
9203 if (network_playing)
9204 SendToServer_MovePlayer(summarized_player_action);
9207 if (!options.network && !setup.team_mode)
9208 local_player->effective_action = summarized_player_action;
9210 if (recorded_player_action != NULL)
9211 for (i = 0; i < MAX_PLAYERS; i++)
9212 stored_player[i].effective_action = recorded_player_action[i];
9214 for (i = 0; i < MAX_PLAYERS; i++)
9216 tape_action[i] = stored_player[i].effective_action;
9218 if (tape.recording && tape_action[i] && !tape.player_participates[i])
9219 tape.player_participates[i] = TRUE; /* player just appeared from CE */
9222 /* only save actions from input devices, but not programmed actions */
9224 TapeRecordAction(tape_action);
9226 for (i = 0; i < MAX_PLAYERS; i++)
9228 int actual_player_action = stored_player[i].effective_action;
9231 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
9232 - rnd_equinox_tetrachloride 048
9233 - rnd_equinox_tetrachloride_ii 096
9234 - rnd_emanuel_schmieg 002
9235 - doctor_sloan_ww 001, 020
9237 if (stored_player[i].MovPos == 0)
9238 CheckGravityMovement(&stored_player[i]);
9241 /* overwrite programmed action with tape action */
9242 if (stored_player[i].programmed_action)
9243 actual_player_action = stored_player[i].programmed_action;
9246 PlayerActions(&stored_player[i], actual_player_action);
9248 tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
9250 if (tape.recording && tape_action[i] && !tape.player_participates[i])
9251 tape.player_participates[i] = TRUE; /* player just appeared from CE */
9254 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
9257 network_player_action_received = FALSE;
9259 ScrollScreen(NULL, SCROLL_GO_ON);
9261 /* for backwards compatibility, the following code emulates a fixed bug that
9262 occured when pushing elements (causing elements that just made their last
9263 pushing step to already (if possible) make their first falling step in the
9264 same game frame, which is bad); this code is also needed to use the famous
9265 "spring push bug" which is used in older levels and might be wanted to be
9266 used also in newer levels, but in this case the buggy pushing code is only
9267 affecting the "spring" element and no other elements */
9269 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
9271 for (i = 0; i < MAX_PLAYERS; i++)
9273 struct PlayerInfo *player = &stored_player[i];
9277 if (player->active && player->is_pushing && player->is_moving &&
9279 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
9280 Feld[x][y] == EL_SPRING))
9282 ContinueMoving(x, y);
9284 /* continue moving after pushing (this is actually a bug) */
9285 if (!IS_MOVING(x, y))
9294 SCAN_PLAYFIELD(x, y)
9296 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9299 ChangeCount[x][y] = 0;
9300 ChangeEvent[x][y] = -1;
9302 /* this must be handled before main playfield loop */
9303 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
9306 if (MovDelay[x][y] <= 0)
9310 #if USE_NEW_SNAP_DELAY
9311 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
9314 if (MovDelay[x][y] <= 0)
9317 DrawLevelField(x, y);
9319 TestIfElementTouchesCustomElement(x, y); /* for empty space */
9325 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
9327 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
9328 printf("GameActions(): This should never happen!\n");
9330 ChangePage[x][y] = -1;
9335 if (WasJustMoving[x][y] > 0)
9336 WasJustMoving[x][y]--;
9337 if (WasJustFalling[x][y] > 0)
9338 WasJustFalling[x][y]--;
9339 if (CheckCollision[x][y] > 0)
9340 CheckCollision[x][y]--;
9344 /* reset finished pushing action (not done in ContinueMoving() to allow
9345 continuous pushing animation for elements with zero push delay) */
9346 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
9348 ResetGfxAnimation(x, y);
9349 DrawLevelField(x, y);
9353 if (IS_BLOCKED(x, y))
9357 Blocked2Moving(x, y, &oldx, &oldy);
9358 if (!IS_MOVING(oldx, oldy))
9360 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
9361 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
9362 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
9363 printf("GameActions(): This should never happen!\n");
9370 SCAN_PLAYFIELD(x, y)
9372 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9375 element = Feld[x][y];
9376 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9379 printf("::: %d,%d\n", x, y);
9381 if (element == EL_ROCK)
9382 printf("::: Yo man! Rocks can fall!\n");
9385 if (graphic_info[graphic].anim_global_sync)
9386 GfxFrame[x][y] = FrameCounter;
9387 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
9389 int old_gfx_frame = GfxFrame[x][y];
9391 GfxFrame[x][y] = CustomValue[x][y];
9394 if (GfxFrame[x][y] != old_gfx_frame)
9396 DrawLevelGraphicAnimation(x, y, graphic);
9398 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
9400 int old_gfx_frame = GfxFrame[x][y];
9402 GfxFrame[x][y] = element_info[element].collect_score;
9405 if (GfxFrame[x][y] != old_gfx_frame)
9407 DrawLevelGraphicAnimation(x, y, graphic);
9410 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
9411 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
9412 ResetRandomAnimationValue(x, y);
9414 SetRandomAnimationValue(x, y);
9416 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
9418 if (IS_INACTIVE(element))
9420 if (IS_ANIMATED(graphic))
9421 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9426 /* this may take place after moving, so 'element' may have changed */
9427 if (IS_CHANGING(x, y) &&
9428 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
9430 int page = element_info[element].event_page_nr[CE_DELAY];
9432 HandleElementChange(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] : page);
9436 printf("::: ChangeDelay == %d\n", ChangeDelay[x][y]);
9440 if (element == EL_CUSTOM_255)
9441 printf("::: ChangeDelay == %d\n", ChangeDelay[x][y]);
9445 HandleElementChange(x, y, page);
9447 if (CAN_CHANGE(element))
9448 HandleElementChange(x, y, page);
9450 if (HAS_ACTION(element))
9451 ExecuteCustomElementAction(x, y, element, page);
9456 element = Feld[x][y];
9457 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9460 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
9464 element = Feld[x][y];
9465 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9467 if (IS_ANIMATED(graphic) &&
9470 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9472 if (IS_GEM(element) || element == EL_SP_INFOTRON)
9473 EdelsteinFunkeln(x, y);
9475 else if ((element == EL_ACID ||
9476 element == EL_EXIT_OPEN ||
9477 element == EL_SP_EXIT_OPEN ||
9478 element == EL_SP_TERMINAL ||
9479 element == EL_SP_TERMINAL_ACTIVE ||
9480 element == EL_EXTRA_TIME ||
9481 element == EL_SHIELD_NORMAL ||
9482 element == EL_SHIELD_DEADLY) &&
9483 IS_ANIMATED(graphic))
9484 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9485 else if (IS_MOVING(x, y))
9486 ContinueMoving(x, y);
9487 else if (IS_ACTIVE_BOMB(element))
9488 CheckDynamite(x, y);
9489 else if (element == EL_AMOEBA_GROWING)
9490 AmoebeWaechst(x, y);
9491 else if (element == EL_AMOEBA_SHRINKING)
9492 AmoebaDisappearing(x, y);
9494 #if !USE_NEW_AMOEBA_CODE
9495 else if (IS_AMOEBALIVE(element))
9496 AmoebeAbleger(x, y);
9499 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
9501 else if (element == EL_EXIT_CLOSED)
9503 else if (element == EL_SP_EXIT_CLOSED)
9505 else if (element == EL_EXPANDABLE_WALL_GROWING)
9507 else if (element == EL_EXPANDABLE_WALL ||
9508 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9509 element == EL_EXPANDABLE_WALL_VERTICAL ||
9510 element == EL_EXPANDABLE_WALL_ANY)
9512 else if (element == EL_FLAMES)
9513 CheckForDragon(x, y);
9514 else if (element == EL_EXPLOSION)
9515 ; /* drawing of correct explosion animation is handled separately */
9516 else if (element == EL_ELEMENT_SNAPPING ||
9517 element == EL_DIAGONAL_SHRINKING ||
9518 element == EL_DIAGONAL_GROWING)
9521 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
9523 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9526 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
9527 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9530 if (element == EL_CUSTOM_255 ||
9531 element == EL_CUSTOM_256)
9532 DrawLevelGraphicAnimation(x, y, graphic);
9535 if (IS_BELT_ACTIVE(element))
9536 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
9538 if (game.magic_wall_active)
9540 int jx = local_player->jx, jy = local_player->jy;
9542 /* play the element sound at the position nearest to the player */
9543 if ((element == EL_MAGIC_WALL_FULL ||
9544 element == EL_MAGIC_WALL_ACTIVE ||
9545 element == EL_MAGIC_WALL_EMPTYING ||
9546 element == EL_BD_MAGIC_WALL_FULL ||
9547 element == EL_BD_MAGIC_WALL_ACTIVE ||
9548 element == EL_BD_MAGIC_WALL_EMPTYING) &&
9549 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
9557 #if USE_NEW_AMOEBA_CODE
9558 /* new experimental amoeba growth stuff */
9559 if (!(FrameCounter % 8))
9561 static unsigned long random = 1684108901;
9563 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
9565 x = RND(lev_fieldx);
9566 y = RND(lev_fieldy);
9567 element = Feld[x][y];
9569 if (!IS_PLAYER(x,y) &&
9570 (element == EL_EMPTY ||
9571 CAN_GROW_INTO(element) ||
9572 element == EL_QUICKSAND_EMPTY ||
9573 element == EL_ACID_SPLASH_LEFT ||
9574 element == EL_ACID_SPLASH_RIGHT))
9576 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
9577 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
9578 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
9579 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
9580 Feld[x][y] = EL_AMOEBA_DROP;
9583 random = random * 129 + 1;
9589 if (game.explosions_delayed)
9592 game.explosions_delayed = FALSE;
9595 SCAN_PLAYFIELD(x, y)
9597 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9600 element = Feld[x][y];
9602 if (ExplodeField[x][y])
9603 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
9604 else if (element == EL_EXPLOSION)
9605 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
9607 ExplodeField[x][y] = EX_TYPE_NONE;
9610 game.explosions_delayed = TRUE;
9613 if (game.magic_wall_active)
9615 if (!(game.magic_wall_time_left % 4))
9617 int element = Feld[magic_wall_x][magic_wall_y];
9619 if (element == EL_BD_MAGIC_WALL_FULL ||
9620 element == EL_BD_MAGIC_WALL_ACTIVE ||
9621 element == EL_BD_MAGIC_WALL_EMPTYING)
9622 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
9624 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
9627 if (game.magic_wall_time_left > 0)
9629 game.magic_wall_time_left--;
9630 if (!game.magic_wall_time_left)
9633 SCAN_PLAYFIELD(x, y)
9635 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9638 element = Feld[x][y];
9640 if (element == EL_MAGIC_WALL_ACTIVE ||
9641 element == EL_MAGIC_WALL_FULL)
9643 Feld[x][y] = EL_MAGIC_WALL_DEAD;
9644 DrawLevelField(x, y);
9646 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
9647 element == EL_BD_MAGIC_WALL_FULL)
9649 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
9650 DrawLevelField(x, y);
9654 game.magic_wall_active = FALSE;
9659 if (game.light_time_left > 0)
9661 game.light_time_left--;
9663 if (game.light_time_left == 0)
9664 RedrawAllLightSwitchesAndInvisibleElements();
9667 if (game.timegate_time_left > 0)
9669 game.timegate_time_left--;
9671 if (game.timegate_time_left == 0)
9672 CloseAllOpenTimegates();
9675 if (game.lenses_time_left > 0)
9677 game.lenses_time_left--;
9679 if (game.lenses_time_left == 0)
9680 RedrawAllInvisibleElementsForLenses();
9683 if (game.magnify_time_left > 0)
9685 game.magnify_time_left--;
9687 if (game.magnify_time_left == 0)
9688 RedrawAllInvisibleElementsForMagnifier();
9691 for (i = 0; i < MAX_PLAYERS; i++)
9693 struct PlayerInfo *player = &stored_player[i];
9695 if (SHIELD_ON(player))
9697 if (player->shield_deadly_time_left)
9698 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
9699 else if (player->shield_normal_time_left)
9700 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
9704 if (TimeFrames >= FRAMES_PER_SECOND)
9709 for (i = 0; i < MAX_PLAYERS; i++)
9711 struct PlayerInfo *player = &stored_player[i];
9713 if (SHIELD_ON(player))
9715 player->shield_normal_time_left--;
9717 if (player->shield_deadly_time_left > 0)
9718 player->shield_deadly_time_left--;
9722 if (!level.use_step_counter)
9730 if (TimeLeft <= 10 && setup.time_limit)
9731 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
9733 DrawGameValue_Time(TimeLeft);
9735 if (!TimeLeft && setup.time_limit)
9736 for (i = 0; i < MAX_PLAYERS; i++)
9737 KillPlayer(&stored_player[i]);
9739 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
9740 DrawGameValue_Time(TimePlayed);
9743 if (tape.recording || tape.playing)
9744 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
9748 PlayAllPlayersSound();
9750 if (options.debug) /* calculate frames per second */
9752 static unsigned long fps_counter = 0;
9753 static int fps_frames = 0;
9754 unsigned long fps_delay_ms = Counter() - fps_counter;
9758 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
9760 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
9763 fps_counter = Counter();
9766 redraw_mask |= REDRAW_FPS;
9769 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
9771 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
9773 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
9775 local_player->show_envelope = 0;
9778 /* use random number generator in every frame to make it less predictable */
9779 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
9783 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
9785 int min_x = x, min_y = y, max_x = x, max_y = y;
9788 for (i = 0; i < MAX_PLAYERS; i++)
9790 int jx = stored_player[i].jx, jy = stored_player[i].jy;
9792 if (!stored_player[i].active || &stored_player[i] == player)
9795 min_x = MIN(min_x, jx);
9796 min_y = MIN(min_y, jy);
9797 max_x = MAX(max_x, jx);
9798 max_y = MAX(max_y, jy);
9801 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
9804 static boolean AllPlayersInVisibleScreen()
9808 for (i = 0; i < MAX_PLAYERS; i++)
9810 int jx = stored_player[i].jx, jy = stored_player[i].jy;
9812 if (!stored_player[i].active)
9815 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
9822 void ScrollLevel(int dx, int dy)
9824 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
9827 BlitBitmap(drawto_field, drawto_field,
9828 FX + TILEX * (dx == -1) - softscroll_offset,
9829 FY + TILEY * (dy == -1) - softscroll_offset,
9830 SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
9831 SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
9832 FX + TILEX * (dx == 1) - softscroll_offset,
9833 FY + TILEY * (dy == 1) - softscroll_offset);
9837 x = (dx == 1 ? BX1 : BX2);
9838 for (y = BY1; y <= BY2; y++)
9839 DrawScreenField(x, y);
9844 y = (dy == 1 ? BY1 : BY2);
9845 for (x = BX1; x <= BX2; x++)
9846 DrawScreenField(x, y);
9849 redraw_mask |= REDRAW_FIELD;
9852 static boolean canFallDown(struct PlayerInfo *player)
9854 int jx = player->jx, jy = player->jy;
9856 return (IN_LEV_FIELD(jx, jy + 1) &&
9857 (IS_FREE(jx, jy + 1) ||
9858 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
9859 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
9860 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
9863 static boolean canPassField(int x, int y, int move_dir)
9865 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9866 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9867 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
9870 int element = Feld[x][y];
9872 return (IS_PASSABLE_FROM(element, opposite_dir) &&
9873 !CAN_MOVE(element) &&
9874 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
9875 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
9876 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
9879 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
9881 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9882 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9883 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
9887 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9888 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
9889 (IS_DIGGABLE(Feld[newx][newy]) ||
9890 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9891 canPassField(newx, newy, move_dir)));
9894 static void CheckGravityMovement(struct PlayerInfo *player)
9896 if (game.gravity && !player->programmed_action)
9898 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
9899 int move_dir_vertical = player->effective_action & MV_VERTICAL;
9900 boolean player_is_snapping = player->effective_action & JOY_BUTTON_1;
9901 int jx = player->jx, jy = player->jy;
9902 boolean player_is_moving_to_valid_field =
9903 (!player_is_snapping &&
9904 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
9905 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
9906 boolean player_can_fall_down = canFallDown(player);
9908 if (player_can_fall_down &&
9909 !player_is_moving_to_valid_field)
9910 player->programmed_action = MV_DOWN;
9914 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
9916 return CheckGravityMovement(player);
9918 if (game.gravity && !player->programmed_action)
9920 int jx = player->jx, jy = player->jy;
9921 boolean field_under_player_is_free =
9922 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
9923 boolean player_is_standing_on_valid_field =
9924 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
9925 (IS_WALKABLE(Feld[jx][jy]) &&
9926 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
9928 if (field_under_player_is_free && !player_is_standing_on_valid_field)
9929 player->programmed_action = MV_DOWN;
9935 -----------------------------------------------------------------------------
9936 dx, dy: direction (non-diagonal) to try to move the player to
9937 real_dx, real_dy: direction as read from input device (can be diagonal)
9940 boolean MovePlayerOneStep(struct PlayerInfo *player,
9941 int dx, int dy, int real_dx, int real_dy)
9943 int jx = player->jx, jy = player->jy;
9944 int new_jx = jx + dx, new_jy = jy + dy;
9945 #if !USE_FIXED_DONT_RUN_INTO
9949 boolean player_can_move = !player->cannot_move;
9951 if (!player->active || (!dx && !dy))
9952 return MP_NO_ACTION;
9954 player->MovDir = (dx < 0 ? MV_LEFT :
9957 dy > 0 ? MV_DOWN : MV_NONE);
9959 if (!IN_LEV_FIELD(new_jx, new_jy))
9960 return MP_NO_ACTION;
9962 if (!player_can_move)
9965 if (player->MovPos == 0)
9967 player->is_moving = FALSE;
9968 player->is_digging = FALSE;
9969 player->is_collecting = FALSE;
9970 player->is_snapping = FALSE;
9971 player->is_pushing = FALSE;
9974 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
9975 SnapField(player, 0, 0);
9979 return MP_NO_ACTION;
9984 if (!options.network && game.centered_player_nr == -1 &&
9985 !AllPlayersInSight(player, new_jx, new_jy))
9986 return MP_NO_ACTION;
9988 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
9989 return MP_NO_ACTION;
9992 #if !USE_FIXED_DONT_RUN_INTO
9993 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
9995 /* (moved to DigField()) */
9996 if (player_can_move && DONT_RUN_INTO(element))
9998 if (element == EL_ACID && dx == 0 && dy == 1)
10000 SplashAcid(new_jx, new_jy);
10001 Feld[jx][jy] = EL_PLAYER_1;
10002 InitMovingField(jx, jy, MV_DOWN);
10003 Store[jx][jy] = EL_ACID;
10004 ContinueMoving(jx, jy);
10005 BuryPlayer(player);
10008 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
10014 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
10016 #if USE_FIXED_DONT_RUN_INTO
10017 if (can_move == MP_DONT_RUN_INTO)
10021 if (can_move != MP_MOVING)
10024 #if USE_FIXED_DONT_RUN_INTO
10027 /* check if DigField() has caused relocation of the player */
10028 if (player->jx != jx || player->jy != jy)
10029 return MP_NO_ACTION; /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
10031 StorePlayer[jx][jy] = 0;
10032 player->last_jx = jx;
10033 player->last_jy = jy;
10034 player->jx = new_jx;
10035 player->jy = new_jy;
10036 StorePlayer[new_jx][new_jy] = player->element_nr;
10038 if (player->move_delay_value_next != -1)
10040 player->move_delay_value = player->move_delay_value_next;
10041 player->move_delay_value_next = -1;
10045 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
10047 player->step_counter++;
10049 PlayerVisit[jx][jy] = FrameCounter;
10051 ScrollPlayer(player, SCROLL_INIT);
10056 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
10058 int jx = player->jx, jy = player->jy;
10059 int old_jx = jx, old_jy = jy;
10060 int moved = MP_NO_ACTION;
10062 if (!player->active)
10067 if (player->MovPos == 0)
10069 player->is_moving = FALSE;
10070 player->is_digging = FALSE;
10071 player->is_collecting = FALSE;
10072 player->is_snapping = FALSE;
10073 player->is_pushing = FALSE;
10079 if (player->move_delay > 0)
10082 player->move_delay = -1; /* set to "uninitialized" value */
10084 /* store if player is automatically moved to next field */
10085 player->is_auto_moving = (player->programmed_action != MV_NONE);
10087 /* remove the last programmed player action */
10088 player->programmed_action = 0;
10090 if (player->MovPos)
10092 /* should only happen if pre-1.2 tape recordings are played */
10093 /* this is only for backward compatibility */
10095 int original_move_delay_value = player->move_delay_value;
10098 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
10102 /* scroll remaining steps with finest movement resolution */
10103 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
10105 while (player->MovPos)
10107 ScrollPlayer(player, SCROLL_GO_ON);
10108 ScrollScreen(NULL, SCROLL_GO_ON);
10110 AdvanceFrameAndPlayerCounters(player->index_nr);
10116 player->move_delay_value = original_move_delay_value;
10119 if (player->last_move_dir & MV_HORIZONTAL)
10121 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
10122 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
10126 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
10127 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
10134 if (moved & MP_MOVING && !ScreenMovPos &&
10135 (player->index_nr == game.centered_player_nr ||
10136 game.centered_player_nr == -1))
10138 if (moved & MP_MOVING && !ScreenMovPos &&
10139 (player == local_player || !options.network))
10142 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
10143 int offset = (setup.scroll_delay ? 3 : 0);
10145 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
10147 /* actual player has left the screen -- scroll in that direction */
10148 if (jx != old_jx) /* player has moved horizontally */
10149 scroll_x += (jx - old_jx);
10150 else /* player has moved vertically */
10151 scroll_y += (jy - old_jy);
10155 if (jx != old_jx) /* player has moved horizontally */
10157 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
10158 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
10159 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
10161 /* don't scroll over playfield boundaries */
10162 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
10163 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
10165 /* don't scroll more than one field at a time */
10166 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
10168 /* don't scroll against the player's moving direction */
10169 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
10170 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
10171 scroll_x = old_scroll_x;
10173 else /* player has moved vertically */
10175 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
10176 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
10177 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
10179 /* don't scroll over playfield boundaries */
10180 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
10181 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
10183 /* don't scroll more than one field at a time */
10184 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
10186 /* don't scroll against the player's moving direction */
10187 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
10188 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
10189 scroll_y = old_scroll_y;
10193 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
10196 if (!options.network && game.centered_player_nr == -1 &&
10197 !AllPlayersInVisibleScreen())
10199 scroll_x = old_scroll_x;
10200 scroll_y = old_scroll_y;
10204 if (!options.network && !AllPlayersInVisibleScreen())
10206 scroll_x = old_scroll_x;
10207 scroll_y = old_scroll_y;
10212 ScrollScreen(player, SCROLL_INIT);
10213 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
10218 player->StepFrame = 0;
10220 if (moved & MP_MOVING)
10222 if (old_jx != jx && old_jy == jy)
10223 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
10224 else if (old_jx == jx && old_jy != jy)
10225 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
10227 DrawLevelField(jx, jy); /* for "crumbled sand" */
10229 player->last_move_dir = player->MovDir;
10230 player->is_moving = TRUE;
10231 player->is_snapping = FALSE;
10232 player->is_switching = FALSE;
10233 player->is_dropping = FALSE;
10234 player->is_dropping_pressed = FALSE;
10235 player->drop_pressed_delay = 0;
10239 CheckGravityMovementWhenNotMoving(player);
10241 player->is_moving = FALSE;
10243 /* at this point, the player is allowed to move, but cannot move right now
10244 (e.g. because of something blocking the way) -- ensure that the player
10245 is also allowed to move in the next frame (in old versions before 3.1.1,
10246 the player was forced to wait again for eight frames before next try) */
10248 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
10249 player->move_delay = 0; /* allow direct movement in the next frame */
10252 if (player->move_delay == -1) /* not yet initialized by DigField() */
10253 player->move_delay = player->move_delay_value;
10255 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10257 TestIfPlayerTouchesBadThing(jx, jy);
10258 TestIfPlayerTouchesCustomElement(jx, jy);
10261 if (!player->active)
10262 RemovePlayer(player);
10267 void ScrollPlayer(struct PlayerInfo *player, int mode)
10269 int jx = player->jx, jy = player->jy;
10270 int last_jx = player->last_jx, last_jy = player->last_jy;
10271 int move_stepsize = TILEX / player->move_delay_value;
10273 #if USE_NEW_PLAYER_SPEED
10274 if (!player->active)
10277 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
10280 if (!player->active || player->MovPos == 0)
10284 if (mode == SCROLL_INIT)
10286 player->actual_frame_counter = FrameCounter;
10287 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10289 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
10290 Feld[last_jx][last_jy] == EL_EMPTY)
10292 int last_field_block_delay = 0; /* start with no blocking at all */
10293 int block_delay_adjustment = player->block_delay_adjustment;
10295 /* if player blocks last field, add delay for exactly one move */
10296 if (player->block_last_field)
10298 last_field_block_delay += player->move_delay_value;
10300 /* when blocking enabled, prevent moving up despite gravity */
10301 if (game.gravity && player->MovDir == MV_UP)
10302 block_delay_adjustment = -1;
10305 /* add block delay adjustment (also possible when not blocking) */
10306 last_field_block_delay += block_delay_adjustment;
10308 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
10309 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
10312 #if USE_NEW_PLAYER_SPEED
10313 if (player->MovPos != 0) /* player has not yet reached destination */
10319 else if (!FrameReached(&player->actual_frame_counter, 1))
10323 printf("::: player->MovPos: %d -> %d\n",
10325 player->MovPos + (player->MovPos > 0 ? -1 : 1) * move_stepsize);
10328 #if USE_NEW_PLAYER_SPEED
10329 if (player->MovPos != 0)
10331 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
10332 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10334 /* before DrawPlayer() to draw correct player graphic for this case */
10335 if (player->MovPos == 0)
10336 CheckGravityMovement(player);
10339 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
10340 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10342 /* before DrawPlayer() to draw correct player graphic for this case */
10343 if (player->MovPos == 0)
10344 CheckGravityMovement(player);
10347 if (player->MovPos == 0) /* player reached destination field */
10350 printf("::: player reached destination field\n");
10353 if (player->move_delay_reset_counter > 0)
10355 player->move_delay_reset_counter--;
10357 if (player->move_delay_reset_counter == 0)
10359 /* continue with normal speed after quickly moving through gate */
10360 HALVE_PLAYER_SPEED(player);
10362 /* be able to make the next move without delay */
10363 player->move_delay = 0;
10367 player->last_jx = jx;
10368 player->last_jy = jy;
10370 if (Feld[jx][jy] == EL_EXIT_OPEN ||
10371 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
10372 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
10374 DrawPlayer(player); /* needed here only to cleanup last field */
10375 RemovePlayer(player);
10377 if (local_player->friends_still_needed == 0 ||
10378 IS_SP_ELEMENT(Feld[jx][jy]))
10379 player->LevelSolved = player->GameOver = TRUE;
10382 /* this breaks one level: "machine", level 000 */
10384 int move_direction = player->MovDir;
10385 int enter_side = MV_DIR_OPPOSITE(move_direction);
10386 int leave_side = move_direction;
10387 int old_jx = last_jx;
10388 int old_jy = last_jy;
10389 int old_element = Feld[old_jx][old_jy];
10390 int new_element = Feld[jx][jy];
10392 if (IS_CUSTOM_ELEMENT(old_element))
10393 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
10395 player->index_bit, leave_side);
10397 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
10398 CE_PLAYER_LEAVES_X,
10399 player->index_bit, leave_side);
10401 if (IS_CUSTOM_ELEMENT(new_element))
10402 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
10403 player->index_bit, enter_side);
10405 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
10406 CE_PLAYER_ENTERS_X,
10407 player->index_bit, enter_side);
10409 CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
10410 CE_MOVE_OF_X, move_direction);
10413 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10415 TestIfPlayerTouchesBadThing(jx, jy);
10416 TestIfPlayerTouchesCustomElement(jx, jy);
10418 /* needed because pushed element has not yet reached its destination,
10419 so it would trigger a change event at its previous field location */
10420 if (!player->is_pushing)
10421 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
10423 if (!player->active)
10424 RemovePlayer(player);
10427 if (level.use_step_counter)
10437 if (TimeLeft <= 10 && setup.time_limit)
10438 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
10440 DrawGameValue_Time(TimeLeft);
10442 if (!TimeLeft && setup.time_limit)
10443 for (i = 0; i < MAX_PLAYERS; i++)
10444 KillPlayer(&stored_player[i]);
10446 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
10447 DrawGameValue_Time(TimePlayed);
10450 if (tape.single_step && tape.recording && !tape.pausing &&
10451 !player->programmed_action)
10452 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10456 void ScrollScreen(struct PlayerInfo *player, int mode)
10458 static unsigned long screen_frame_counter = 0;
10460 if (mode == SCROLL_INIT)
10462 /* set scrolling step size according to actual player's moving speed */
10463 ScrollStepSize = TILEX / player->move_delay_value;
10465 screen_frame_counter = FrameCounter;
10466 ScreenMovDir = player->MovDir;
10467 ScreenMovPos = player->MovPos;
10468 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
10471 else if (!FrameReached(&screen_frame_counter, 1))
10476 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
10477 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
10478 redraw_mask |= REDRAW_FIELD;
10481 ScreenMovDir = MV_NONE;
10484 void TestIfPlayerTouchesCustomElement(int x, int y)
10486 static int xy[4][2] =
10493 static int trigger_sides[4][2] =
10495 /* center side border side */
10496 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
10497 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
10498 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
10499 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
10501 static int touch_dir[4] =
10503 MV_LEFT | MV_RIGHT,
10508 int center_element = Feld[x][y]; /* should always be non-moving! */
10511 for (i = 0; i < NUM_DIRECTIONS; i++)
10513 int xx = x + xy[i][0];
10514 int yy = y + xy[i][1];
10515 int center_side = trigger_sides[i][0];
10516 int border_side = trigger_sides[i][1];
10517 int border_element;
10519 if (!IN_LEV_FIELD(xx, yy))
10522 if (IS_PLAYER(x, y))
10524 struct PlayerInfo *player = PLAYERINFO(x, y);
10526 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10527 border_element = Feld[xx][yy]; /* may be moving! */
10528 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
10529 border_element = Feld[xx][yy];
10530 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
10531 border_element = MovingOrBlocked2Element(xx, yy);
10533 continue; /* center and border element do not touch */
10535 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
10536 player->index_bit, border_side);
10537 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
10538 CE_PLAYER_TOUCHES_X,
10539 player->index_bit, border_side);
10541 else if (IS_PLAYER(xx, yy))
10543 struct PlayerInfo *player = PLAYERINFO(xx, yy);
10545 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10547 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
10548 continue; /* center and border element do not touch */
10551 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
10552 player->index_bit, center_side);
10553 CheckTriggeredElementChangeByPlayer(x, y, center_element,
10554 CE_PLAYER_TOUCHES_X,
10555 player->index_bit, center_side);
10561 #if USE_ELEMENT_TOUCHING_BUGFIX
10563 void TestIfElementTouchesCustomElement(int x, int y)
10565 static int xy[4][2] =
10572 static int trigger_sides[4][2] =
10574 /* center side border side */
10575 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
10576 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
10577 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
10578 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
10580 static int touch_dir[4] =
10582 MV_LEFT | MV_RIGHT,
10587 boolean change_center_element = FALSE;
10588 int center_element = Feld[x][y]; /* should always be non-moving! */
10589 int border_element_old[NUM_DIRECTIONS];
10592 for (i = 0; i < NUM_DIRECTIONS; i++)
10594 int xx = x + xy[i][0];
10595 int yy = y + xy[i][1];
10596 int border_element;
10598 border_element_old[i] = -1;
10600 if (!IN_LEV_FIELD(xx, yy))
10603 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10604 border_element = Feld[xx][yy]; /* may be moving! */
10605 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
10606 border_element = Feld[xx][yy];
10607 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
10608 border_element = MovingOrBlocked2Element(xx, yy);
10610 continue; /* center and border element do not touch */
10612 border_element_old[i] = border_element;
10615 for (i = 0; i < NUM_DIRECTIONS; i++)
10617 int xx = x + xy[i][0];
10618 int yy = y + xy[i][1];
10619 int center_side = trigger_sides[i][0];
10620 int border_element = border_element_old[i];
10622 if (border_element == -1)
10625 /* check for change of border element */
10626 CheckElementChangeBySide(xx, yy, border_element, center_element,
10627 CE_TOUCHING_X, center_side);
10630 for (i = 0; i < NUM_DIRECTIONS; i++)
10632 int border_side = trigger_sides[i][1];
10633 int border_element = border_element_old[i];
10635 if (border_element == -1)
10638 /* check for change of center element (but change it only once) */
10639 if (!change_center_element)
10640 change_center_element =
10641 CheckElementChangeBySide(x, y, center_element, border_element,
10642 CE_TOUCHING_X, border_side);
10648 void TestIfElementTouchesCustomElement_OLD(int x, int y)
10650 static int xy[4][2] =
10657 static int trigger_sides[4][2] =
10659 /* center side border side */
10660 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
10661 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
10662 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
10663 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
10665 static int touch_dir[4] =
10667 MV_LEFT | MV_RIGHT,
10672 boolean change_center_element = FALSE;
10673 int center_element = Feld[x][y]; /* should always be non-moving! */
10676 for (i = 0; i < NUM_DIRECTIONS; i++)
10678 int xx = x + xy[i][0];
10679 int yy = y + xy[i][1];
10680 int center_side = trigger_sides[i][0];
10681 int border_side = trigger_sides[i][1];
10682 int border_element;
10684 if (!IN_LEV_FIELD(xx, yy))
10687 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10688 border_element = Feld[xx][yy]; /* may be moving! */
10689 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
10690 border_element = Feld[xx][yy];
10691 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
10692 border_element = MovingOrBlocked2Element(xx, yy);
10694 continue; /* center and border element do not touch */
10696 /* check for change of center element (but change it only once) */
10697 if (!change_center_element)
10698 change_center_element =
10699 CheckElementChangeBySide(x, y, center_element, border_element,
10700 CE_TOUCHING_X, border_side);
10702 /* check for change of border element */
10703 CheckElementChangeBySide(xx, yy, border_element, center_element,
10704 CE_TOUCHING_X, center_side);
10710 void TestIfElementHitsCustomElement(int x, int y, int direction)
10712 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
10713 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
10714 int hitx = x + dx, hity = y + dy;
10715 int hitting_element = Feld[x][y];
10716 int touched_element;
10718 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
10721 touched_element = (IN_LEV_FIELD(hitx, hity) ?
10722 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
10724 if (IN_LEV_FIELD(hitx, hity))
10726 int opposite_direction = MV_DIR_OPPOSITE(direction);
10727 int hitting_side = direction;
10728 int touched_side = opposite_direction;
10729 boolean object_hit = (!IS_MOVING(hitx, hity) ||
10730 MovDir[hitx][hity] != direction ||
10731 ABS(MovPos[hitx][hity]) <= TILEY / 2);
10737 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10738 CE_HITTING_X, touched_side);
10740 CheckElementChangeBySide(hitx, hity, touched_element,
10741 hitting_element, CE_HIT_BY_X, hitting_side);
10743 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10744 CE_HIT_BY_SOMETHING, opposite_direction);
10748 /* "hitting something" is also true when hitting the playfield border */
10749 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10750 CE_HITTING_SOMETHING, direction);
10754 void TestIfElementSmashesCustomElement(int x, int y, int direction)
10756 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
10757 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
10758 int hitx = x + dx, hity = y + dy;
10759 int hitting_element = Feld[x][y];
10760 int touched_element;
10762 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
10763 !IS_FREE(hitx, hity) &&
10764 (!IS_MOVING(hitx, hity) ||
10765 MovDir[hitx][hity] != direction ||
10766 ABS(MovPos[hitx][hity]) <= TILEY / 2));
10769 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
10773 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
10777 touched_element = (IN_LEV_FIELD(hitx, hity) ?
10778 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
10780 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10781 EP_CAN_SMASH_EVERYTHING, direction);
10783 if (IN_LEV_FIELD(hitx, hity))
10785 int opposite_direction = MV_DIR_OPPOSITE(direction);
10786 int hitting_side = direction;
10787 int touched_side = opposite_direction;
10789 int touched_element = MovingOrBlocked2Element(hitx, hity);
10792 boolean object_hit = (!IS_MOVING(hitx, hity) ||
10793 MovDir[hitx][hity] != direction ||
10794 ABS(MovPos[hitx][hity]) <= TILEY / 2);
10803 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10804 CE_SMASHED_BY_SOMETHING, opposite_direction);
10806 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10807 CE_OTHER_IS_SMASHING, touched_side);
10809 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10810 CE_OTHER_GETS_SMASHED, hitting_side);
10816 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
10818 int i, kill_x = -1, kill_y = -1;
10820 int bad_element = -1;
10821 static int test_xy[4][2] =
10828 static int test_dir[4] =
10836 for (i = 0; i < NUM_DIRECTIONS; i++)
10838 int test_x, test_y, test_move_dir, test_element;
10840 test_x = good_x + test_xy[i][0];
10841 test_y = good_y + test_xy[i][1];
10843 if (!IN_LEV_FIELD(test_x, test_y))
10847 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
10849 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
10851 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
10852 2nd case: DONT_TOUCH style bad thing does not move away from good thing
10854 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
10855 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
10859 bad_element = test_element;
10865 if (kill_x != -1 || kill_y != -1)
10867 if (IS_PLAYER(good_x, good_y))
10869 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
10871 if (player->shield_deadly_time_left > 0 &&
10872 !IS_INDESTRUCTIBLE(bad_element))
10873 Bang(kill_x, kill_y);
10874 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
10875 KillPlayer(player);
10878 Bang(good_x, good_y);
10882 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
10884 int i, kill_x = -1, kill_y = -1;
10885 int bad_element = Feld[bad_x][bad_y];
10886 static int test_xy[4][2] =
10893 static int touch_dir[4] =
10895 MV_LEFT | MV_RIGHT,
10900 static int test_dir[4] =
10908 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
10911 for (i = 0; i < NUM_DIRECTIONS; i++)
10913 int test_x, test_y, test_move_dir, test_element;
10915 test_x = bad_x + test_xy[i][0];
10916 test_y = bad_y + test_xy[i][1];
10917 if (!IN_LEV_FIELD(test_x, test_y))
10921 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
10923 test_element = Feld[test_x][test_y];
10925 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
10926 2nd case: DONT_TOUCH style bad thing does not move away from good thing
10928 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
10929 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
10931 /* good thing is player or penguin that does not move away */
10932 if (IS_PLAYER(test_x, test_y))
10934 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
10936 if (bad_element == EL_ROBOT && player->is_moving)
10937 continue; /* robot does not kill player if he is moving */
10939 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10941 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
10942 continue; /* center and border element do not touch */
10949 else if (test_element == EL_PENGUIN)
10958 if (kill_x != -1 || kill_y != -1)
10960 if (IS_PLAYER(kill_x, kill_y))
10962 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
10964 if (player->shield_deadly_time_left > 0 &&
10965 !IS_INDESTRUCTIBLE(bad_element))
10966 Bang(bad_x, bad_y);
10967 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
10968 KillPlayer(player);
10971 Bang(kill_x, kill_y);
10975 void TestIfPlayerTouchesBadThing(int x, int y)
10977 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
10980 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
10982 TestIfGoodThingHitsBadThing(x, y, move_dir);
10985 void TestIfBadThingTouchesPlayer(int x, int y)
10987 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
10990 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
10992 TestIfBadThingHitsGoodThing(x, y, move_dir);
10995 void TestIfFriendTouchesBadThing(int x, int y)
10997 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
11000 void TestIfBadThingTouchesFriend(int x, int y)
11002 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
11005 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
11007 int i, kill_x = bad_x, kill_y = bad_y;
11008 static int xy[4][2] =
11016 for (i = 0; i < NUM_DIRECTIONS; i++)
11020 x = bad_x + xy[i][0];
11021 y = bad_y + xy[i][1];
11022 if (!IN_LEV_FIELD(x, y))
11025 element = Feld[x][y];
11026 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
11027 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
11035 if (kill_x != bad_x || kill_y != bad_y)
11036 Bang(bad_x, bad_y);
11039 void KillPlayer(struct PlayerInfo *player)
11041 int jx = player->jx, jy = player->jy;
11043 if (!player->active)
11046 /* remove accessible field at the player's position */
11047 Feld[jx][jy] = EL_EMPTY;
11049 /* deactivate shield (else Bang()/Explode() would not work right) */
11050 player->shield_normal_time_left = 0;
11051 player->shield_deadly_time_left = 0;
11054 BuryPlayer(player);
11057 static void KillPlayerUnlessEnemyProtected(int x, int y)
11059 if (!PLAYER_ENEMY_PROTECTED(x, y))
11060 KillPlayer(PLAYERINFO(x, y));
11063 static void KillPlayerUnlessExplosionProtected(int x, int y)
11065 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
11066 KillPlayer(PLAYERINFO(x, y));
11069 void BuryPlayer(struct PlayerInfo *player)
11071 int jx = player->jx, jy = player->jy;
11073 if (!player->active)
11076 PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
11077 PlayLevelSound(jx, jy, SND_GAME_LOSING);
11079 player->GameOver = TRUE;
11080 RemovePlayer(player);
11083 void RemovePlayer(struct PlayerInfo *player)
11085 int jx = player->jx, jy = player->jy;
11086 int i, found = FALSE;
11088 player->present = FALSE;
11089 player->active = FALSE;
11091 if (!ExplodeField[jx][jy])
11092 StorePlayer[jx][jy] = 0;
11094 if (player->is_moving)
11095 DrawLevelField(player->last_jx, player->last_jy);
11097 for (i = 0; i < MAX_PLAYERS; i++)
11098 if (stored_player[i].active)
11102 AllPlayersGone = TRUE;
11108 #if USE_NEW_SNAP_DELAY
11109 static void setFieldForSnapping(int x, int y, int element, int direction)
11111 struct ElementInfo *ei = &element_info[element];
11112 int direction_bit = MV_DIR_TO_BIT(direction);
11113 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
11114 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
11115 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
11117 Feld[x][y] = EL_ELEMENT_SNAPPING;
11118 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
11120 ResetGfxAnimation(x, y);
11122 GfxElement[x][y] = element;
11123 GfxAction[x][y] = action;
11124 GfxDir[x][y] = direction;
11125 GfxFrame[x][y] = -1;
11130 =============================================================================
11131 checkDiagonalPushing()
11132 -----------------------------------------------------------------------------
11133 check if diagonal input device direction results in pushing of object
11134 (by checking if the alternative direction is walkable, diggable, ...)
11135 =============================================================================
11138 static boolean checkDiagonalPushing(struct PlayerInfo *player,
11139 int x, int y, int real_dx, int real_dy)
11141 int jx, jy, dx, dy, xx, yy;
11143 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
11146 /* diagonal direction: check alternative direction */
11151 xx = jx + (dx == 0 ? real_dx : 0);
11152 yy = jy + (dy == 0 ? real_dy : 0);
11154 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
11158 =============================================================================
11160 -----------------------------------------------------------------------------
11161 x, y: field next to player (non-diagonal) to try to dig to
11162 real_dx, real_dy: direction as read from input device (can be diagonal)
11163 =============================================================================
11166 int DigField(struct PlayerInfo *player,
11167 int oldx, int oldy, int x, int y,
11168 int real_dx, int real_dy, int mode)
11170 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
11171 boolean player_was_pushing = player->is_pushing;
11172 boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
11173 boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
11174 int jx = oldx, jy = oldy;
11175 int dx = x - jx, dy = y - jy;
11176 int nextx = x + dx, nexty = y + dy;
11177 int move_direction = (dx == -1 ? MV_LEFT :
11178 dx == +1 ? MV_RIGHT :
11180 dy == +1 ? MV_DOWN : MV_NONE);
11181 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
11182 int dig_side = MV_DIR_OPPOSITE(move_direction);
11183 int old_element = Feld[jx][jy];
11184 #if USE_FIXED_DONT_RUN_INTO
11185 int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
11191 if (is_player) /* function can also be called by EL_PENGUIN */
11193 if (player->MovPos == 0)
11195 player->is_digging = FALSE;
11196 player->is_collecting = FALSE;
11199 if (player->MovPos == 0) /* last pushing move finished */
11200 player->is_pushing = FALSE;
11202 if (mode == DF_NO_PUSH) /* player just stopped pushing */
11204 player->is_switching = FALSE;
11205 player->push_delay = -1;
11207 return MP_NO_ACTION;
11211 #if !USE_FIXED_DONT_RUN_INTO
11212 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
11213 return MP_NO_ACTION;
11216 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
11217 old_element = Back[jx][jy];
11219 /* in case of element dropped at player position, check background */
11220 else if (Back[jx][jy] != EL_EMPTY &&
11221 game.engine_version >= VERSION_IDENT(2,2,0,0))
11222 old_element = Back[jx][jy];
11225 #if USE_FIXED_DONT_RUN_INTO
11226 if (player_can_move && DONT_RUN_INTO(element))
11228 if (element == EL_ACID && dx == 0 && dy == 1)
11231 Feld[jx][jy] = EL_PLAYER_1;
11232 InitMovingField(jx, jy, MV_DOWN);
11233 Store[jx][jy] = EL_ACID;
11234 ContinueMoving(jx, jy);
11235 BuryPlayer(player);
11238 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
11240 return MP_DONT_RUN_INTO;
11246 #if USE_FIXED_DONT_RUN_INTO
11247 if (player_can_move && DONT_RUN_INTO(element))
11249 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
11251 return MP_DONT_RUN_INTO;
11256 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
11257 return MP_NO_ACTION; /* field has no opening in this direction */
11259 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
11260 return MP_NO_ACTION; /* field has no opening in this direction */
11263 #if USE_FIXED_DONT_RUN_INTO
11264 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
11267 Feld[jx][jy] = EL_PLAYER_1;
11268 InitMovingField(jx, jy, MV_DOWN);
11269 Store[jx][jy] = EL_ACID;
11270 ContinueMoving(jx, jy);
11271 BuryPlayer(player);
11273 return MP_DONT_RUN_INTO;
11279 #if USE_FIXED_DONT_RUN_INTO
11280 if (player_can_move && DONT_RUN_INTO(element))
11282 if (element == EL_ACID && dx == 0 && dy == 1)
11285 Feld[jx][jy] = EL_PLAYER_1;
11286 InitMovingField(jx, jy, MV_DOWN);
11287 Store[jx][jy] = EL_ACID;
11288 ContinueMoving(jx, jy);
11289 BuryPlayer(player);
11292 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
11294 return MP_DONT_RUN_INTO;
11299 #if USE_FIXED_DONT_RUN_INTO
11300 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
11301 return MP_NO_ACTION;
11304 #if !USE_FIXED_DONT_RUN_INTO
11305 element = Feld[x][y];
11308 collect_count = element_info[element].collect_count_initial;
11310 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
11311 return MP_NO_ACTION;
11313 if (game.engine_version < VERSION_IDENT(2,2,0,0))
11314 player_can_move = player_can_move_or_snap;
11316 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
11317 game.engine_version >= VERSION_IDENT(2,2,0,0))
11319 CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
11320 player->index_bit, dig_side);
11321 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
11322 player->index_bit, dig_side);
11324 if (Feld[x][y] != element) /* field changed by snapping */
11327 return MP_NO_ACTION;
11330 if (game.gravity && is_player && !player->is_auto_moving &&
11331 canFallDown(player) && move_direction != MV_DOWN &&
11332 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
11333 return MP_NO_ACTION; /* player cannot walk here due to gravity */
11335 if (player_can_move &&
11336 IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
11338 int sound_element = SND_ELEMENT(element);
11339 int sound_action = ACTION_WALKING;
11341 if (IS_RND_GATE(element))
11343 if (!player->key[RND_GATE_NR(element)])
11344 return MP_NO_ACTION;
11346 else if (IS_RND_GATE_GRAY(element))
11348 if (!player->key[RND_GATE_GRAY_NR(element)])
11349 return MP_NO_ACTION;
11351 else if (IS_RND_GATE_GRAY_ACTIVE(element))
11353 if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
11354 return MP_NO_ACTION;
11356 else if (element == EL_EXIT_OPEN ||
11357 element == EL_SP_EXIT_OPEN ||
11358 element == EL_SP_EXIT_OPENING)
11360 sound_action = ACTION_PASSING; /* player is passing exit */
11362 else if (element == EL_EMPTY)
11364 sound_action = ACTION_MOVING; /* nothing to walk on */
11367 /* play sound from background or player, whatever is available */
11368 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
11369 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
11371 PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
11373 else if (player_can_move &&
11374 IS_PASSABLE(element) && canPassField(x, y, move_direction))
11376 if (!ACCESS_FROM(element, opposite_direction))
11377 return MP_NO_ACTION; /* field not accessible from this direction */
11379 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
11380 return MP_NO_ACTION;
11382 if (IS_EM_GATE(element))
11384 if (!player->key[EM_GATE_NR(element)])
11385 return MP_NO_ACTION;
11387 else if (IS_EM_GATE_GRAY(element))
11389 if (!player->key[EM_GATE_GRAY_NR(element)])
11390 return MP_NO_ACTION;
11392 else if (IS_EM_GATE_GRAY_ACTIVE(element))
11394 if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
11395 return MP_NO_ACTION;
11397 else if (IS_SP_PORT(element))
11399 if (element == EL_SP_GRAVITY_PORT_LEFT ||
11400 element == EL_SP_GRAVITY_PORT_RIGHT ||
11401 element == EL_SP_GRAVITY_PORT_UP ||
11402 element == EL_SP_GRAVITY_PORT_DOWN)
11403 game.gravity = !game.gravity;
11404 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
11405 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
11406 element == EL_SP_GRAVITY_ON_PORT_UP ||
11407 element == EL_SP_GRAVITY_ON_PORT_DOWN)
11408 game.gravity = TRUE;
11409 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
11410 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
11411 element == EL_SP_GRAVITY_OFF_PORT_UP ||
11412 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
11413 game.gravity = FALSE;
11416 /* automatically move to the next field with double speed */
11417 player->programmed_action = move_direction;
11419 if (player->move_delay_reset_counter == 0)
11421 player->move_delay_reset_counter = 2; /* two double speed steps */
11423 DOUBLE_PLAYER_SPEED(player);
11426 PlayLevelSoundAction(x, y, ACTION_PASSING);
11428 else if (player_can_move_or_snap && IS_DIGGABLE(element))
11432 if (mode != DF_SNAP)
11434 GfxElement[x][y] = GFX_ELEMENT(element);
11435 player->is_digging = TRUE;
11438 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
11440 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
11441 player->index_bit, dig_side);
11443 if (mode == DF_SNAP)
11445 #if USE_NEW_SNAP_DELAY
11446 if (level.block_snap_field)
11447 setFieldForSnapping(x, y, element, move_direction);
11449 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11451 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11454 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
11455 player->index_bit, dig_side);
11458 else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
11462 if (is_player && mode != DF_SNAP)
11464 GfxElement[x][y] = element;
11465 player->is_collecting = TRUE;
11468 if (element == EL_SPEED_PILL)
11470 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
11472 else if (element == EL_EXTRA_TIME && level.time > 0)
11474 TimeLeft += level.extra_time;
11475 DrawGameValue_Time(TimeLeft);
11477 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
11479 player->shield_normal_time_left += level.shield_normal_time;
11480 if (element == EL_SHIELD_DEADLY)
11481 player->shield_deadly_time_left += level.shield_deadly_time;
11483 else if (element == EL_DYNAMITE ||
11484 element == EL_EM_DYNAMITE ||
11485 element == EL_SP_DISK_RED)
11487 if (player->inventory_size < MAX_INVENTORY_SIZE)
11488 player->inventory_element[player->inventory_size++] = element;
11490 DrawGameValue_Dynamite(local_player->inventory_size);
11492 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
11494 player->dynabomb_count++;
11495 player->dynabombs_left++;
11497 else if (element == EL_DYNABOMB_INCREASE_SIZE)
11499 player->dynabomb_size++;
11501 else if (element == EL_DYNABOMB_INCREASE_POWER)
11503 player->dynabomb_xl = TRUE;
11505 else if (IS_KEY(element))
11507 player->key[KEY_NR(element)] = TRUE;
11509 DrawGameValue_Keys(player->key);
11511 redraw_mask |= REDRAW_DOOR_1;
11513 else if (IS_ENVELOPE(element))
11515 player->show_envelope = element;
11517 else if (element == EL_EMC_LENSES)
11519 game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
11521 RedrawAllInvisibleElementsForLenses();
11523 else if (element == EL_EMC_MAGNIFIER)
11525 game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
11527 RedrawAllInvisibleElementsForMagnifier();
11529 else if (IS_DROPPABLE(element) ||
11530 IS_THROWABLE(element)) /* can be collected and dropped */
11534 if (collect_count == 0)
11535 player->inventory_infinite_element = element;
11537 for (i = 0; i < collect_count; i++)
11538 if (player->inventory_size < MAX_INVENTORY_SIZE)
11539 player->inventory_element[player->inventory_size++] = element;
11541 DrawGameValue_Dynamite(local_player->inventory_size);
11543 else if (collect_count > 0)
11545 local_player->gems_still_needed -= collect_count;
11546 if (local_player->gems_still_needed < 0)
11547 local_player->gems_still_needed = 0;
11549 DrawGameValue_Emeralds(local_player->gems_still_needed);
11552 RaiseScoreElement(element);
11553 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
11556 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
11557 player->index_bit, dig_side);
11559 if (mode == DF_SNAP)
11561 #if USE_NEW_SNAP_DELAY
11562 if (level.block_snap_field)
11563 setFieldForSnapping(x, y, element, move_direction);
11565 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11567 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11570 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
11571 player->index_bit, dig_side);
11574 else if (player_can_move_or_snap && IS_PUSHABLE(element))
11576 if (mode == DF_SNAP && element != EL_BD_ROCK)
11577 return MP_NO_ACTION;
11579 if (CAN_FALL(element) && dy)
11580 return MP_NO_ACTION;
11582 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
11583 !(element == EL_SPRING && level.use_spring_bug))
11584 return MP_NO_ACTION;
11586 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
11587 ((move_direction & MV_VERTICAL &&
11588 ((element_info[element].move_pattern & MV_LEFT &&
11589 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
11590 (element_info[element].move_pattern & MV_RIGHT &&
11591 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
11592 (move_direction & MV_HORIZONTAL &&
11593 ((element_info[element].move_pattern & MV_UP &&
11594 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
11595 (element_info[element].move_pattern & MV_DOWN &&
11596 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
11597 return MP_NO_ACTION;
11599 /* do not push elements already moving away faster than player */
11600 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
11601 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
11602 return MP_NO_ACTION;
11604 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
11606 if (player->push_delay_value == -1 || !player_was_pushing)
11607 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11609 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11611 if (player->push_delay_value == -1)
11612 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11614 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
11616 if (!player->is_pushing)
11617 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11620 player->is_pushing = TRUE;
11622 if (!(IN_LEV_FIELD(nextx, nexty) &&
11623 (IS_FREE(nextx, nexty) ||
11624 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
11625 IS_SB_ELEMENT(element)))))
11626 return MP_NO_ACTION;
11628 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
11629 return MP_NO_ACTION;
11631 if (player->push_delay == -1) /* new pushing; restart delay */
11632 player->push_delay = 0;
11634 if (player->push_delay < player->push_delay_value &&
11635 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
11636 element != EL_SPRING && element != EL_BALLOON)
11638 /* make sure that there is no move delay before next try to push */
11639 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11640 player->move_delay = 0;
11642 return MP_NO_ACTION;
11645 if (IS_SB_ELEMENT(element))
11647 if (element == EL_SOKOBAN_FIELD_FULL)
11649 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
11650 local_player->sokobanfields_still_needed++;
11653 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
11655 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
11656 local_player->sokobanfields_still_needed--;
11659 Feld[x][y] = EL_SOKOBAN_OBJECT;
11661 if (Back[x][y] == Back[nextx][nexty])
11662 PlayLevelSoundAction(x, y, ACTION_PUSHING);
11663 else if (Back[x][y] != 0)
11664 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
11667 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
11670 if (local_player->sokobanfields_still_needed == 0 &&
11671 game.emulation == EMU_SOKOBAN)
11673 player->LevelSolved = player->GameOver = TRUE;
11674 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
11678 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
11680 InitMovingField(x, y, move_direction);
11681 GfxAction[x][y] = ACTION_PUSHING;
11683 if (mode == DF_SNAP)
11684 ContinueMoving(x, y);
11686 MovPos[x][y] = (dx != 0 ? dx : dy);
11688 Pushed[x][y] = TRUE;
11689 Pushed[nextx][nexty] = TRUE;
11691 if (game.engine_version < VERSION_IDENT(2,2,0,7))
11692 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11694 player->push_delay_value = -1; /* get new value later */
11696 /* check for element change _after_ element has been pushed */
11697 if (game.use_change_when_pushing_bug)
11699 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
11700 player->index_bit, dig_side);
11701 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
11702 player->index_bit, dig_side);
11705 else if (IS_SWITCHABLE(element))
11707 if (PLAYER_SWITCHING(player, x, y))
11709 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
11710 player->index_bit, dig_side);
11715 player->is_switching = TRUE;
11716 player->switch_x = x;
11717 player->switch_y = y;
11719 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
11721 if (element == EL_ROBOT_WHEEL)
11723 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
11727 DrawLevelField(x, y);
11729 else if (element == EL_SP_TERMINAL)
11734 SCAN_PLAYFIELD(xx, yy)
11736 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
11739 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
11741 else if (Feld[xx][yy] == EL_SP_TERMINAL)
11742 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
11745 else if (IS_BELT_SWITCH(element))
11747 ToggleBeltSwitch(x, y);
11749 else if (element == EL_SWITCHGATE_SWITCH_UP ||
11750 element == EL_SWITCHGATE_SWITCH_DOWN)
11752 ToggleSwitchgateSwitch(x, y);
11754 else if (element == EL_LIGHT_SWITCH ||
11755 element == EL_LIGHT_SWITCH_ACTIVE)
11757 ToggleLightSwitch(x, y);
11759 else if (element == EL_TIMEGATE_SWITCH)
11761 ActivateTimegateSwitch(x, y);
11763 else if (element == EL_BALLOON_SWITCH_LEFT ||
11764 element == EL_BALLOON_SWITCH_RIGHT ||
11765 element == EL_BALLOON_SWITCH_UP ||
11766 element == EL_BALLOON_SWITCH_DOWN ||
11767 element == EL_BALLOON_SWITCH_NONE ||
11768 element == EL_BALLOON_SWITCH_ANY)
11770 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
11771 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
11772 element == EL_BALLOON_SWITCH_UP ? MV_UP :
11773 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
11774 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
11777 else if (element == EL_LAMP)
11779 Feld[x][y] = EL_LAMP_ACTIVE;
11780 local_player->lights_still_needed--;
11782 ResetGfxAnimation(x, y);
11783 DrawLevelField(x, y);
11785 else if (element == EL_TIME_ORB_FULL)
11787 Feld[x][y] = EL_TIME_ORB_EMPTY;
11789 if (level.time > 0 || level.use_time_orb_bug)
11791 TimeLeft += level.time_orb_time;
11792 DrawGameValue_Time(TimeLeft);
11795 ResetGfxAnimation(x, y);
11796 DrawLevelField(x, y);
11798 else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
11799 element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
11803 game.ball_state = !game.ball_state;
11806 SCAN_PLAYFIELD(xx, yy)
11808 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
11811 int e = Feld[xx][yy];
11813 if (game.ball_state)
11815 if (e == EL_EMC_MAGIC_BALL)
11816 CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
11817 else if (e == EL_EMC_MAGIC_BALL_SWITCH)
11818 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
11822 if (e == EL_EMC_MAGIC_BALL_ACTIVE)
11823 CreateField(xx, yy, EL_EMC_MAGIC_BALL);
11824 else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
11825 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
11830 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
11831 player->index_bit, dig_side);
11833 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
11834 player->index_bit, dig_side);
11836 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
11837 player->index_bit, dig_side);
11843 if (!PLAYER_SWITCHING(player, x, y))
11845 player->is_switching = TRUE;
11846 player->switch_x = x;
11847 player->switch_y = y;
11849 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
11850 player->index_bit, dig_side);
11851 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
11852 player->index_bit, dig_side);
11854 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
11855 player->index_bit, dig_side);
11856 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
11857 player->index_bit, dig_side);
11860 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
11861 player->index_bit, dig_side);
11862 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
11863 player->index_bit, dig_side);
11865 return MP_NO_ACTION;
11868 player->push_delay = -1;
11870 if (is_player) /* function can also be called by EL_PENGUIN */
11872 if (Feld[x][y] != element) /* really digged/collected something */
11873 player->is_collecting = !player->is_digging;
11879 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
11881 int jx = player->jx, jy = player->jy;
11882 int x = jx + dx, y = jy + dy;
11883 int snap_direction = (dx == -1 ? MV_LEFT :
11884 dx == +1 ? MV_RIGHT :
11886 dy == +1 ? MV_DOWN : MV_NONE);
11887 boolean can_continue_snapping = (level.continuous_snapping &&
11888 WasJustFalling[x][y] < CHECK_DELAY_FALLING);
11890 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
11893 if (!player->active || !IN_LEV_FIELD(x, y))
11901 if (player->MovPos == 0)
11902 player->is_pushing = FALSE;
11904 player->is_snapping = FALSE;
11906 if (player->MovPos == 0)
11908 player->is_moving = FALSE;
11909 player->is_digging = FALSE;
11910 player->is_collecting = FALSE;
11916 #if USE_NEW_CONTINUOUS_SNAPPING
11917 /* prevent snapping with already pressed snap key when not allowed */
11918 if (player->is_snapping && !can_continue_snapping)
11921 if (player->is_snapping)
11925 player->MovDir = snap_direction;
11927 if (player->MovPos == 0)
11929 player->is_moving = FALSE;
11930 player->is_digging = FALSE;
11931 player->is_collecting = FALSE;
11934 player->is_dropping = FALSE;
11935 player->is_dropping_pressed = FALSE;
11936 player->drop_pressed_delay = 0;
11938 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
11941 player->is_snapping = TRUE;
11943 if (player->MovPos == 0)
11945 player->is_moving = FALSE;
11946 player->is_digging = FALSE;
11947 player->is_collecting = FALSE;
11950 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
11951 DrawLevelField(player->last_jx, player->last_jy);
11953 DrawLevelField(x, y);
11958 boolean DropElement(struct PlayerInfo *player)
11960 int old_element, new_element;
11961 int dropx = player->jx, dropy = player->jy;
11962 int drop_direction = player->MovDir;
11963 int drop_side = drop_direction;
11964 int drop_element = (player->inventory_size > 0 ?
11965 player->inventory_element[player->inventory_size - 1] :
11966 player->inventory_infinite_element != EL_UNDEFINED ?
11967 player->inventory_infinite_element :
11968 player->dynabombs_left > 0 ?
11969 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
11972 player->is_dropping_pressed = TRUE;
11974 /* do not drop an element on top of another element; when holding drop key
11975 pressed without moving, dropped element must move away before the next
11976 element can be dropped (this is especially important if the next element
11977 is dynamite, which can be placed on background for historical reasons) */
11978 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
11981 if (IS_THROWABLE(drop_element))
11983 dropx += GET_DX_FROM_DIR(drop_direction);
11984 dropy += GET_DY_FROM_DIR(drop_direction);
11986 if (!IN_LEV_FIELD(dropx, dropy))
11990 old_element = Feld[dropx][dropy]; /* old element at dropping position */
11991 new_element = drop_element; /* default: no change when dropping */
11993 /* check if player is active, not moving and ready to drop */
11994 if (!player->active || player->MovPos || player->drop_delay > 0)
11997 /* check if player has anything that can be dropped */
11998 if (new_element == EL_UNDEFINED)
12001 /* check if drop key was pressed long enough for EM style dynamite */
12002 if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
12005 /* check if anything can be dropped at the current position */
12006 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
12009 /* collected custom elements can only be dropped on empty fields */
12010 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
12013 if (old_element != EL_EMPTY)
12014 Back[dropx][dropy] = old_element; /* store old element on this field */
12016 ResetGfxAnimation(dropx, dropy);
12017 ResetRandomAnimationValue(dropx, dropy);
12019 if (player->inventory_size > 0 ||
12020 player->inventory_infinite_element != EL_UNDEFINED)
12022 if (player->inventory_size > 0)
12024 player->inventory_size--;
12026 DrawGameValue_Dynamite(local_player->inventory_size);
12028 if (new_element == EL_DYNAMITE)
12029 new_element = EL_DYNAMITE_ACTIVE;
12030 else if (new_element == EL_EM_DYNAMITE)
12031 new_element = EL_EM_DYNAMITE_ACTIVE;
12032 else if (new_element == EL_SP_DISK_RED)
12033 new_element = EL_SP_DISK_RED_ACTIVE;
12036 Feld[dropx][dropy] = new_element;
12038 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
12039 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
12040 el2img(Feld[dropx][dropy]), 0);
12042 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
12044 /* needed if previous element just changed to "empty" in the last frame */
12045 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
12047 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
12048 player->index_bit, drop_side);
12049 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
12051 player->index_bit, drop_side);
12053 TestIfElementTouchesCustomElement(dropx, dropy);
12055 else /* player is dropping a dyna bomb */
12057 player->dynabombs_left--;
12059 Feld[dropx][dropy] = new_element;
12061 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
12062 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
12063 el2img(Feld[dropx][dropy]), 0);
12065 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
12068 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
12069 InitField_WithBug1(dropx, dropy, FALSE);
12071 new_element = Feld[dropx][dropy]; /* element might have changed */
12073 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
12074 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
12076 int move_direction, nextx, nexty;
12078 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
12079 MovDir[dropx][dropy] = drop_direction;
12081 move_direction = MovDir[dropx][dropy];
12082 nextx = dropx + GET_DX_FROM_DIR(move_direction);
12083 nexty = dropy + GET_DY_FROM_DIR(move_direction);
12085 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
12086 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
12089 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
12090 player->is_dropping = TRUE;
12092 player->drop_pressed_delay = 0;
12093 player->is_dropping_pressed = FALSE;
12095 player->drop_x = dropx;
12096 player->drop_y = dropy;
12101 /* ------------------------------------------------------------------------- */
12102 /* game sound playing functions */
12103 /* ------------------------------------------------------------------------- */
12105 static int *loop_sound_frame = NULL;
12106 static int *loop_sound_volume = NULL;
12108 void InitPlayLevelSound()
12110 int num_sounds = getSoundListSize();
12112 checked_free(loop_sound_frame);
12113 checked_free(loop_sound_volume);
12115 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
12116 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
12119 static void PlayLevelSound(int x, int y, int nr)
12121 int sx = SCREENX(x), sy = SCREENY(y);
12122 int volume, stereo_position;
12123 int max_distance = 8;
12124 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
12126 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
12127 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
12130 if (!IN_LEV_FIELD(x, y) ||
12131 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
12132 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
12135 volume = SOUND_MAX_VOLUME;
12137 if (!IN_SCR_FIELD(sx, sy))
12139 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
12140 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
12142 volume -= volume * (dx > dy ? dx : dy) / max_distance;
12145 stereo_position = (SOUND_MAX_LEFT +
12146 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
12147 (SCR_FIELDX + 2 * max_distance));
12149 if (IS_LOOP_SOUND(nr))
12151 /* This assures that quieter loop sounds do not overwrite louder ones,
12152 while restarting sound volume comparison with each new game frame. */
12154 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
12157 loop_sound_volume[nr] = volume;
12158 loop_sound_frame[nr] = FrameCounter;
12161 PlaySoundExt(nr, volume, stereo_position, type);
12164 static void PlayLevelSoundNearest(int x, int y, int sound_action)
12166 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
12167 x > LEVELX(BX2) ? LEVELX(BX2) : x,
12168 y < LEVELY(BY1) ? LEVELY(BY1) :
12169 y > LEVELY(BY2) ? LEVELY(BY2) : y,
12173 static void PlayLevelSoundAction(int x, int y, int action)
12175 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
12178 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
12180 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
12182 if (sound_effect != SND_UNDEFINED)
12183 PlayLevelSound(x, y, sound_effect);
12186 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
12189 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
12191 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12192 PlayLevelSound(x, y, sound_effect);
12195 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
12197 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
12199 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12200 PlayLevelSound(x, y, sound_effect);
12203 static void StopLevelSoundActionIfLoop(int x, int y, int action)
12205 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
12207 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12208 StopSound(sound_effect);
12211 static void PlayLevelMusic()
12213 if (levelset.music[level_nr] != MUS_UNDEFINED)
12214 PlayMusic(levelset.music[level_nr]); /* from config file */
12216 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
12219 void PlayLevelSound_EM(int x, int y, int element_em, int sample)
12221 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
12226 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
12230 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12234 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12238 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12242 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
12246 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12250 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12253 case SAMPLE_android_clone:
12254 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12257 case SAMPLE_android_move:
12258 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12261 case SAMPLE_spring:
12262 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12266 PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
12270 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
12273 case SAMPLE_eater_eat:
12274 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
12278 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12281 case SAMPLE_collect:
12282 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
12285 case SAMPLE_diamond:
12286 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12289 case SAMPLE_squash:
12290 /* !!! CHECK THIS !!! */
12292 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
12294 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
12298 case SAMPLE_wonderfall:
12299 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
12303 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12307 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12311 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
12315 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
12319 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12323 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
12326 case SAMPLE_wonder:
12327 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12331 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
12334 case SAMPLE_exit_open:
12335 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
12338 case SAMPLE_exit_leave:
12339 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
12342 case SAMPLE_dynamite:
12343 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12347 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12351 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
12355 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12359 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
12363 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
12367 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
12371 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
12376 void RaiseScore(int value)
12378 local_player->score += value;
12380 DrawGameValue_Score(local_player->score);
12383 void RaiseScoreElement(int element)
12388 case EL_BD_DIAMOND:
12389 case EL_EMERALD_YELLOW:
12390 case EL_EMERALD_RED:
12391 case EL_EMERALD_PURPLE:
12392 case EL_SP_INFOTRON:
12393 RaiseScore(level.score[SC_EMERALD]);
12396 RaiseScore(level.score[SC_DIAMOND]);
12399 RaiseScore(level.score[SC_CRYSTAL]);
12402 RaiseScore(level.score[SC_PEARL]);
12405 case EL_BD_BUTTERFLY:
12406 case EL_SP_ELECTRON:
12407 RaiseScore(level.score[SC_BUG]);
12410 case EL_BD_FIREFLY:
12411 case EL_SP_SNIKSNAK:
12412 RaiseScore(level.score[SC_SPACESHIP]);
12415 case EL_DARK_YAMYAM:
12416 RaiseScore(level.score[SC_YAMYAM]);
12419 RaiseScore(level.score[SC_ROBOT]);
12422 RaiseScore(level.score[SC_PACMAN]);
12425 RaiseScore(level.score[SC_NUT]);
12428 case EL_EM_DYNAMITE:
12429 case EL_SP_DISK_RED:
12430 case EL_DYNABOMB_INCREASE_NUMBER:
12431 case EL_DYNABOMB_INCREASE_SIZE:
12432 case EL_DYNABOMB_INCREASE_POWER:
12433 RaiseScore(level.score[SC_DYNAMITE]);
12435 case EL_SHIELD_NORMAL:
12436 case EL_SHIELD_DEADLY:
12437 RaiseScore(level.score[SC_SHIELD]);
12439 case EL_EXTRA_TIME:
12440 RaiseScore(level.extra_time_score);
12454 RaiseScore(level.score[SC_KEY]);
12457 RaiseScore(element_info[element].collect_score);
12462 void RequestQuitGame(boolean ask_if_really_quit)
12464 if (AllPlayersGone ||
12465 !ask_if_really_quit ||
12466 level_editor_test_game ||
12467 Request("Do you really want to quit the game ?",
12468 REQ_ASK | REQ_STAY_CLOSED))
12470 #if defined(NETWORK_AVALIABLE)
12471 if (options.network)
12472 SendToServer_StopPlaying();
12476 game_status = GAME_MODE_MAIN;
12482 if (tape.playing && tape.deactivate_display)
12483 TapeDeactivateDisplayOff(TRUE);
12485 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
12487 if (tape.playing && tape.deactivate_display)
12488 TapeDeactivateDisplayOn();
12493 /* ---------- new game button stuff ---------------------------------------- */
12495 /* graphic position values for game buttons */
12496 #define GAME_BUTTON_XSIZE 30
12497 #define GAME_BUTTON_YSIZE 30
12498 #define GAME_BUTTON_XPOS 5
12499 #define GAME_BUTTON_YPOS 215
12500 #define SOUND_BUTTON_XPOS 5
12501 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
12503 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
12504 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
12505 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
12506 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
12507 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
12508 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
12515 } gamebutton_info[NUM_GAME_BUTTONS] =
12518 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
12523 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
12524 GAME_CTRL_ID_PAUSE,
12528 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
12533 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
12534 SOUND_CTRL_ID_MUSIC,
12535 "background music on/off"
12538 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
12539 SOUND_CTRL_ID_LOOPS,
12540 "sound loops on/off"
12543 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
12544 SOUND_CTRL_ID_SIMPLE,
12545 "normal sounds on/off"
12549 void CreateGameButtons()
12553 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12555 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
12556 struct GadgetInfo *gi;
12559 unsigned long event_mask;
12560 int gd_xoffset, gd_yoffset;
12561 int gd_x1, gd_x2, gd_y1, gd_y2;
12564 gd_xoffset = gamebutton_info[i].x;
12565 gd_yoffset = gamebutton_info[i].y;
12566 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
12567 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
12569 if (id == GAME_CTRL_ID_STOP ||
12570 id == GAME_CTRL_ID_PAUSE ||
12571 id == GAME_CTRL_ID_PLAY)
12573 button_type = GD_TYPE_NORMAL_BUTTON;
12575 event_mask = GD_EVENT_RELEASED;
12576 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12577 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12581 button_type = GD_TYPE_CHECK_BUTTON;
12583 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
12584 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
12585 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
12586 event_mask = GD_EVENT_PRESSED;
12587 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
12588 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12591 gi = CreateGadget(GDI_CUSTOM_ID, id,
12592 GDI_INFO_TEXT, gamebutton_info[i].infotext,
12593 GDI_X, DX + gd_xoffset,
12594 GDI_Y, DY + gd_yoffset,
12595 GDI_WIDTH, GAME_BUTTON_XSIZE,
12596 GDI_HEIGHT, GAME_BUTTON_YSIZE,
12597 GDI_TYPE, button_type,
12598 GDI_STATE, GD_BUTTON_UNPRESSED,
12599 GDI_CHECKED, checked,
12600 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
12601 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
12602 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
12603 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
12604 GDI_EVENT_MASK, event_mask,
12605 GDI_CALLBACK_ACTION, HandleGameButtons,
12609 Error(ERR_EXIT, "cannot create gadget");
12611 game_gadget[id] = gi;
12615 void FreeGameButtons()
12619 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12620 FreeGadget(game_gadget[i]);
12623 static void MapGameButtons()
12627 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12628 MapGadget(game_gadget[i]);
12631 void UnmapGameButtons()
12635 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12636 UnmapGadget(game_gadget[i]);
12639 static void HandleGameButtons(struct GadgetInfo *gi)
12641 int id = gi->custom_id;
12643 if (game_status != GAME_MODE_PLAYING)
12648 case GAME_CTRL_ID_STOP:
12649 RequestQuitGame(TRUE);
12652 case GAME_CTRL_ID_PAUSE:
12653 if (options.network)
12655 #if defined(NETWORK_AVALIABLE)
12657 SendToServer_ContinuePlaying();
12659 SendToServer_PausePlaying();
12663 TapeTogglePause(TAPE_TOGGLE_MANUAL);
12666 case GAME_CTRL_ID_PLAY:
12669 #if defined(NETWORK_AVALIABLE)
12670 if (options.network)
12671 SendToServer_ContinuePlaying();
12675 tape.pausing = FALSE;
12676 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
12681 case SOUND_CTRL_ID_MUSIC:
12682 if (setup.sound_music)
12684 setup.sound_music = FALSE;
12687 else if (audio.music_available)
12689 setup.sound = setup.sound_music = TRUE;
12691 SetAudioMode(setup.sound);
12697 case SOUND_CTRL_ID_LOOPS:
12698 if (setup.sound_loops)
12699 setup.sound_loops = FALSE;
12700 else if (audio.loops_available)
12702 setup.sound = setup.sound_loops = TRUE;
12703 SetAudioMode(setup.sound);
12707 case SOUND_CTRL_ID_SIMPLE:
12708 if (setup.sound_simple)
12709 setup.sound_simple = FALSE;
12710 else if (audio.sound_available)
12712 setup.sound = setup.sound_simple = TRUE;
12713 SetAudioMode(setup.sound);