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++;
1756 return num_special_action;
1760 =============================================================================
1762 -----------------------------------------------------------------------------
1763 initialize and start new game
1764 =============================================================================
1769 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
1770 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
1771 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
1776 /* don't play tapes over network */
1777 network_playing = (options.network && !tape.playing);
1779 for (i = 0; i < MAX_PLAYERS; i++)
1781 struct PlayerInfo *player = &stored_player[i];
1783 player->index_nr = i;
1784 player->index_bit = (1 << i);
1785 player->element_nr = EL_PLAYER_1 + i;
1787 player->present = FALSE;
1788 player->active = FALSE;
1791 player->effective_action = 0;
1792 player->programmed_action = 0;
1795 player->gems_still_needed = level.gems_needed;
1796 player->sokobanfields_still_needed = 0;
1797 player->lights_still_needed = 0;
1798 player->friends_still_needed = 0;
1800 for (j = 0; j < MAX_NUM_KEYS; j++)
1801 player->key[j] = FALSE;
1803 player->dynabomb_count = 0;
1804 player->dynabomb_size = 1;
1805 player->dynabombs_left = 0;
1806 player->dynabomb_xl = FALSE;
1808 player->MovDir = MV_NONE;
1811 player->GfxDir = MV_NONE;
1812 player->GfxAction = ACTION_DEFAULT;
1814 player->StepFrame = 0;
1816 player->use_murphy = FALSE;
1817 player->artwork_element =
1818 (level.use_artwork_element[i] ? level.artwork_element[i] :
1819 player->element_nr);
1821 player->block_last_field = FALSE; /* initialized in InitPlayerField() */
1822 player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
1824 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
1826 player->actual_frame_counter = 0;
1828 player->step_counter = 0;
1830 player->last_move_dir = MV_NONE;
1832 player->is_waiting = FALSE;
1833 player->is_moving = FALSE;
1834 player->is_auto_moving = FALSE;
1835 player->is_digging = FALSE;
1836 player->is_snapping = FALSE;
1837 player->is_collecting = FALSE;
1838 player->is_pushing = FALSE;
1839 player->is_switching = FALSE;
1840 player->is_dropping = FALSE;
1841 player->is_dropping_pressed = FALSE;
1843 player->is_bored = FALSE;
1844 player->is_sleeping = FALSE;
1846 player->frame_counter_bored = -1;
1847 player->frame_counter_sleeping = -1;
1849 player->anim_delay_counter = 0;
1850 player->post_delay_counter = 0;
1852 player->action_waiting = ACTION_DEFAULT;
1853 player->last_action_waiting = ACTION_DEFAULT;
1854 player->special_action_bored = ACTION_DEFAULT;
1855 player->special_action_sleeping = ACTION_DEFAULT;
1857 /* set number of special actions for bored and sleeping animation */
1858 player->num_special_action_bored =
1859 get_num_special_action(player->artwork_element,
1860 ACTION_BORING_1, ACTION_BORING_LAST);
1861 player->num_special_action_sleeping =
1862 get_num_special_action(player->artwork_element,
1863 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
1865 player->switch_x = -1;
1866 player->switch_y = -1;
1868 player->drop_x = -1;
1869 player->drop_y = -1;
1871 player->show_envelope = 0;
1874 SetPlayerMoveSpeed(player, level.initial_player_stepsize, TRUE);
1876 player->move_delay = game.initial_move_delay;
1877 player->move_delay_value = game.initial_move_delay_value;
1879 player->move_delay_value_next = -1;
1881 player->move_delay_reset_counter = 0;
1883 player->cannot_move = FALSE;
1886 player->push_delay = -1; /* initialized when pushing starts */
1887 player->push_delay_value = game.initial_push_delay_value;
1889 player->drop_delay = 0;
1890 player->drop_pressed_delay = 0;
1892 player->last_jx = player->last_jy = 0;
1893 player->jx = player->jy = 0;
1895 player->shield_normal_time_left = 0;
1896 player->shield_deadly_time_left = 0;
1898 player->inventory_infinite_element = EL_UNDEFINED;
1899 player->inventory_size = 0;
1901 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
1902 SnapField(player, 0, 0);
1904 player->LevelSolved = FALSE;
1905 player->GameOver = FALSE;
1908 network_player_action_received = FALSE;
1910 #if defined(NETWORK_AVALIABLE)
1911 /* initial null action */
1912 if (network_playing)
1913 SendToServer_MovePlayer(MV_NONE);
1922 TimeLeft = level.time;
1925 ScreenMovDir = MV_NONE;
1929 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
1931 AllPlayersGone = FALSE;
1933 game.yamyam_content_nr = 0;
1934 game.magic_wall_active = FALSE;
1935 game.magic_wall_time_left = 0;
1936 game.light_time_left = 0;
1937 game.timegate_time_left = 0;
1938 game.switchgate_pos = 0;
1939 game.wind_direction = level.wind_direction_initial;
1940 game.gravity = level.initial_gravity;
1941 game.explosions_delayed = TRUE;
1943 game.lenses_time_left = 0;
1944 game.magnify_time_left = 0;
1946 game.ball_state = level.ball_state_initial;
1947 game.ball_content_nr = 0;
1949 game.envelope_active = FALSE;
1951 game.centered_player_nr = game.centered_player_nr_next = -1; /* focus all */
1953 for (i = 0; i < NUM_BELTS; i++)
1955 game.belt_dir[i] = MV_NONE;
1956 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1959 for (i = 0; i < MAX_NUM_AMOEBA; i++)
1960 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
1963 SCAN_PLAYFIELD(x, y)
1965 for (x = 0; x < lev_fieldx; x++) for (y = 0; y < lev_fieldy; y++)
1968 Feld[x][y] = level.field[x][y];
1969 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
1970 ChangeDelay[x][y] = 0;
1971 ChangePage[x][y] = -1;
1972 #if USE_NEW_CUSTOM_VALUE
1973 CustomValue[x][y] = 0; /* initialized in InitField() */
1975 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
1977 WasJustMoving[x][y] = 0;
1978 WasJustFalling[x][y] = 0;
1979 CheckCollision[x][y] = 0;
1981 Pushed[x][y] = FALSE;
1983 ChangeCount[x][y] = 0;
1984 ChangeEvent[x][y] = -1;
1986 ExplodePhase[x][y] = 0;
1987 ExplodeDelay[x][y] = 0;
1988 ExplodeField[x][y] = EX_TYPE_NONE;
1990 RunnerVisit[x][y] = 0;
1991 PlayerVisit[x][y] = 0;
1994 GfxRandom[x][y] = INIT_GFX_RANDOM();
1995 GfxElement[x][y] = EL_UNDEFINED;
1996 GfxAction[x][y] = ACTION_DEFAULT;
1997 GfxDir[x][y] = MV_NONE;
2001 SCAN_PLAYFIELD(x, y)
2003 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
2006 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
2008 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
2010 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
2013 InitField(x, y, TRUE);
2018 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
2019 emulate_sb ? EMU_SOKOBAN :
2020 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
2022 #if USE_NEW_ALL_SLIPPERY
2023 /* initialize type of slippery elements */
2024 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2026 if (!IS_CUSTOM_ELEMENT(i))
2028 /* default: elements slip down either to the left or right randomly */
2029 element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
2031 /* SP style elements prefer to slip down on the left side */
2032 if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
2033 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
2035 /* BD style elements prefer to slip down on the left side */
2036 if (game.emulation == EMU_BOULDERDASH)
2037 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
2042 /* initialize explosion and ignition delay */
2043 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2045 if (!IS_CUSTOM_ELEMENT(i))
2048 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
2049 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
2050 game.emulation == EMU_SUPAPLEX ? 3 : 2);
2051 int last_phase = (num_phase + 1) * delay;
2052 int half_phase = (num_phase / 2) * delay;
2054 element_info[i].explosion_delay = last_phase - 1;
2055 element_info[i].ignition_delay = half_phase;
2057 if (i == EL_BLACK_ORB)
2058 element_info[i].ignition_delay = 1;
2062 if (element_info[i].explosion_delay < 1) /* !!! check again !!! */
2063 element_info[i].explosion_delay = 1;
2065 if (element_info[i].ignition_delay < 1) /* !!! check again !!! */
2066 element_info[i].ignition_delay = 1;
2070 /* correct non-moving belts to start moving left */
2071 for (i = 0; i < NUM_BELTS; i++)
2072 if (game.belt_dir[i] == MV_NONE)
2073 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
2075 /* check if any connected player was not found in playfield */
2076 for (i = 0; i < MAX_PLAYERS; i++)
2078 struct PlayerInfo *player = &stored_player[i];
2080 if (player->connected && !player->present)
2082 for (j = 0; j < MAX_PLAYERS; j++)
2084 struct PlayerInfo *some_player = &stored_player[j];
2085 int jx = some_player->jx, jy = some_player->jy;
2087 /* assign first free player found that is present in the playfield */
2088 if (some_player->present && !some_player->connected)
2090 player->present = TRUE;
2091 player->active = TRUE;
2093 some_player->present = FALSE;
2094 some_player->active = FALSE;
2097 player->element_nr = some_player->element_nr;
2100 player->artwork_element = some_player->artwork_element;
2102 player->block_last_field = some_player->block_last_field;
2103 player->block_delay_adjustment = some_player->block_delay_adjustment;
2105 StorePlayer[jx][jy] = player->element_nr;
2106 player->jx = player->last_jx = jx;
2107 player->jy = player->last_jy = jy;
2117 /* when playing a tape, eliminate all players who do not participate */
2119 for (i = 0; i < MAX_PLAYERS; i++)
2121 if (stored_player[i].active && !tape.player_participates[i])
2123 struct PlayerInfo *player = &stored_player[i];
2124 int jx = player->jx, jy = player->jy;
2126 player->active = FALSE;
2127 StorePlayer[jx][jy] = 0;
2128 Feld[jx][jy] = EL_EMPTY;
2132 else if (!options.network && !setup.team_mode) /* && !tape.playing */
2134 /* when in single player mode, eliminate all but the first active player */
2136 for (i = 0; i < MAX_PLAYERS; i++)
2138 if (stored_player[i].active)
2140 for (j = i + 1; j < MAX_PLAYERS; j++)
2142 if (stored_player[j].active)
2144 struct PlayerInfo *player = &stored_player[j];
2145 int jx = player->jx, jy = player->jy;
2147 player->active = FALSE;
2148 player->present = FALSE;
2150 StorePlayer[jx][jy] = 0;
2151 Feld[jx][jy] = EL_EMPTY;
2158 /* when recording the game, store which players take part in the game */
2161 for (i = 0; i < MAX_PLAYERS; i++)
2162 if (stored_player[i].active)
2163 tape.player_participates[i] = TRUE;
2168 for (i = 0; i < MAX_PLAYERS; i++)
2170 struct PlayerInfo *player = &stored_player[i];
2172 printf("Player %d: present == %d, connected == %d, active == %d.\n",
2177 if (local_player == player)
2178 printf("Player %d is local player.\n", i+1);
2182 if (BorderElement == EL_EMPTY)
2185 SBX_Right = lev_fieldx - SCR_FIELDX;
2187 SBY_Lower = lev_fieldy - SCR_FIELDY;
2192 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
2194 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
2197 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
2198 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
2200 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
2201 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
2203 /* if local player not found, look for custom element that might create
2204 the player (make some assumptions about the right custom element) */
2205 if (!local_player->present)
2207 int start_x = 0, start_y = 0;
2208 int found_rating = 0;
2209 int found_element = EL_UNDEFINED;
2210 int player_nr = local_player->index_nr;
2213 SCAN_PLAYFIELD(x, y)
2215 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
2218 int element = Feld[x][y];
2223 if (level.use_start_element[player_nr] &&
2224 level.start_element[player_nr] == element &&
2231 found_element = element;
2234 if (!IS_CUSTOM_ELEMENT(element))
2237 if (CAN_CHANGE(element))
2239 for (i = 0; i < element_info[element].num_change_pages; i++)
2241 /* check for player created from custom element as single target */
2242 content = element_info[element].change_page[i].target_element;
2243 is_player = ELEM_IS_PLAYER(content);
2245 if (is_player && (found_rating < 3 || element < found_element))
2251 found_element = element;
2256 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
2258 /* check for player created from custom element as explosion content */
2259 content = element_info[element].content.e[xx][yy];
2260 is_player = ELEM_IS_PLAYER(content);
2262 if (is_player && (found_rating < 2 || element < found_element))
2264 start_x = x + xx - 1;
2265 start_y = y + yy - 1;
2268 found_element = element;
2271 if (!CAN_CHANGE(element))
2274 for (i = 0; i < element_info[element].num_change_pages; i++)
2276 /* check for player created from custom element as extended target */
2278 element_info[element].change_page[i].target_content.e[xx][yy];
2280 is_player = ELEM_IS_PLAYER(content);
2282 if (is_player && (found_rating < 1 || element < found_element))
2284 start_x = x + xx - 1;
2285 start_y = y + yy - 1;
2288 found_element = element;
2294 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
2295 start_x > SBX_Right + MIDPOSX ? SBX_Right :
2298 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
2299 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
2304 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2305 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2306 local_player->jx - MIDPOSX);
2308 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2309 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2310 local_player->jy - MIDPOSY);
2313 if (!game.restart_level)
2314 CloseDoor(DOOR_CLOSE_1);
2316 /* !!! FIX THIS (START) !!! */
2317 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2319 InitGameEngine_EM();
2326 /* after drawing the level, correct some elements */
2327 if (game.timegate_time_left == 0)
2328 CloseAllOpenTimegates();
2330 if (setup.soft_scrolling)
2331 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
2333 redraw_mask |= REDRAW_FROM_BACKBUFFER;
2336 /* !!! FIX THIS (END) !!! */
2338 if (!game.restart_level)
2340 /* copy default game door content to main double buffer */
2341 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2342 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2345 DrawGameDoorValues();
2347 if (!game.restart_level)
2351 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
2352 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
2353 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
2357 /* copy actual game door content to door double buffer for OpenDoor() */
2358 BlitBitmap(drawto, bitmap_db_door,
2359 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
2361 OpenDoor(DOOR_OPEN_ALL);
2363 PlaySoundStereo(SND_GAME_STARTING, SOUND_MIDDLE);
2365 if (setup.sound_music)
2368 KeyboardAutoRepeatOffUnlessAutoplay();
2372 for (i = 0; i < MAX_PLAYERS; i++)
2373 printf("Player %d %sactive.\n",
2374 i + 1, (stored_player[i].active ? "" : "not "));
2378 game.restart_level = FALSE;
2381 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
2383 /* this is used for non-R'n'D game engines to update certain engine values */
2385 /* needed to determine if sounds are played within the visible screen area */
2386 scroll_x = actual_scroll_x;
2387 scroll_y = actual_scroll_y;
2390 void InitMovDir(int x, int y)
2392 int i, element = Feld[x][y];
2393 static int xy[4][2] =
2400 static int direction[3][4] =
2402 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
2403 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
2404 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
2413 Feld[x][y] = EL_BUG;
2414 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
2417 case EL_SPACESHIP_RIGHT:
2418 case EL_SPACESHIP_UP:
2419 case EL_SPACESHIP_LEFT:
2420 case EL_SPACESHIP_DOWN:
2421 Feld[x][y] = EL_SPACESHIP;
2422 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
2425 case EL_BD_BUTTERFLY_RIGHT:
2426 case EL_BD_BUTTERFLY_UP:
2427 case EL_BD_BUTTERFLY_LEFT:
2428 case EL_BD_BUTTERFLY_DOWN:
2429 Feld[x][y] = EL_BD_BUTTERFLY;
2430 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
2433 case EL_BD_FIREFLY_RIGHT:
2434 case EL_BD_FIREFLY_UP:
2435 case EL_BD_FIREFLY_LEFT:
2436 case EL_BD_FIREFLY_DOWN:
2437 Feld[x][y] = EL_BD_FIREFLY;
2438 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
2441 case EL_PACMAN_RIGHT:
2443 case EL_PACMAN_LEFT:
2444 case EL_PACMAN_DOWN:
2445 Feld[x][y] = EL_PACMAN;
2446 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
2449 case EL_YAMYAM_LEFT:
2450 case EL_YAMYAM_RIGHT:
2452 case EL_YAMYAM_DOWN:
2453 Feld[x][y] = EL_YAMYAM;
2454 MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
2457 case EL_SP_SNIKSNAK:
2458 MovDir[x][y] = MV_UP;
2461 case EL_SP_ELECTRON:
2462 MovDir[x][y] = MV_LEFT;
2469 Feld[x][y] = EL_MOLE;
2470 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
2474 if (IS_CUSTOM_ELEMENT(element))
2476 struct ElementInfo *ei = &element_info[element];
2477 int move_direction_initial = ei->move_direction_initial;
2478 int move_pattern = ei->move_pattern;
2480 if (move_direction_initial == MV_START_PREVIOUS)
2482 if (MovDir[x][y] != MV_NONE)
2485 move_direction_initial = MV_START_AUTOMATIC;
2488 if (move_direction_initial == MV_START_RANDOM)
2489 MovDir[x][y] = 1 << RND(4);
2490 else if (move_direction_initial & MV_ANY_DIRECTION)
2491 MovDir[x][y] = move_direction_initial;
2492 else if (move_pattern == MV_ALL_DIRECTIONS ||
2493 move_pattern == MV_TURNING_LEFT ||
2494 move_pattern == MV_TURNING_RIGHT ||
2495 move_pattern == MV_TURNING_LEFT_RIGHT ||
2496 move_pattern == MV_TURNING_RIGHT_LEFT ||
2497 move_pattern == MV_TURNING_RANDOM)
2498 MovDir[x][y] = 1 << RND(4);
2499 else if (move_pattern == MV_HORIZONTAL)
2500 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
2501 else if (move_pattern == MV_VERTICAL)
2502 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
2503 else if (move_pattern & MV_ANY_DIRECTION)
2504 MovDir[x][y] = element_info[element].move_pattern;
2505 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
2506 move_pattern == MV_ALONG_RIGHT_SIDE)
2508 /* use random direction as default start direction */
2509 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2510 MovDir[x][y] = 1 << RND(4);
2512 for (i = 0; i < NUM_DIRECTIONS; i++)
2514 int x1 = x + xy[i][0];
2515 int y1 = y + xy[i][1];
2517 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2519 if (move_pattern == MV_ALONG_RIGHT_SIDE)
2520 MovDir[x][y] = direction[0][i];
2522 MovDir[x][y] = direction[1][i];
2531 MovDir[x][y] = 1 << RND(4);
2533 if (element != EL_BUG &&
2534 element != EL_SPACESHIP &&
2535 element != EL_BD_BUTTERFLY &&
2536 element != EL_BD_FIREFLY)
2539 for (i = 0; i < NUM_DIRECTIONS; i++)
2541 int x1 = x + xy[i][0];
2542 int y1 = y + xy[i][1];
2544 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2546 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
2548 MovDir[x][y] = direction[0][i];
2551 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
2552 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
2554 MovDir[x][y] = direction[1][i];
2563 GfxDir[x][y] = MovDir[x][y];
2566 void InitAmoebaNr(int x, int y)
2569 int group_nr = AmoebeNachbarNr(x, y);
2573 for (i = 1; i < MAX_NUM_AMOEBA; i++)
2575 if (AmoebaCnt[i] == 0)
2583 AmoebaNr[x][y] = group_nr;
2584 AmoebaCnt[group_nr]++;
2585 AmoebaCnt2[group_nr]++;
2591 boolean raise_level = FALSE;
2593 if (local_player->MovPos)
2596 if (tape.auto_play) /* tape might already be stopped here */
2597 tape.auto_play_level_solved = TRUE;
2599 local_player->LevelSolved = FALSE;
2601 PlaySoundStereo(SND_GAME_WINNING, SOUND_MIDDLE);
2605 if (!tape.playing && setup.sound_loops)
2606 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2607 SND_CTRL_PLAY_LOOP);
2609 while (TimeLeft > 0)
2611 if (!tape.playing && !setup.sound_loops)
2612 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2614 if (TimeLeft > 100 && TimeLeft % 10 == 0)
2617 RaiseScore(level.score[SC_TIME_BONUS] * 10);
2622 RaiseScore(level.score[SC_TIME_BONUS]);
2625 DrawGameValue_Time(TimeLeft);
2633 if (!tape.playing && setup.sound_loops)
2634 StopSound(SND_GAME_LEVELTIME_BONUS);
2636 else if (level.time == 0) /* level without time limit */
2638 if (!tape.playing && setup.sound_loops)
2639 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2640 SND_CTRL_PLAY_LOOP);
2642 while (TimePlayed < 999)
2644 if (!tape.playing && !setup.sound_loops)
2645 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2647 if (TimePlayed < 900 && TimePlayed % 10 == 0)
2650 RaiseScore(level.score[SC_TIME_BONUS] * 10);
2655 RaiseScore(level.score[SC_TIME_BONUS]);
2658 DrawGameValue_Time(TimePlayed);
2666 if (!tape.playing && setup.sound_loops)
2667 StopSound(SND_GAME_LEVELTIME_BONUS);
2670 /* close exit door after last player */
2671 if (AllPlayersGone && ExitX >= 0 && ExitY >= 0 &&
2672 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
2673 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN))
2675 int element = Feld[ExitX][ExitY];
2677 Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
2678 EL_SP_EXIT_CLOSING);
2680 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
2683 /* player disappears */
2684 if (ExitX >= 0 && ExitY >= 0)
2685 DrawLevelField(ExitX, ExitY);
2691 printf("::: TAPE PLAYING -> DO NOT SAVE SCORE\n");
2693 printf("::: NO TAPE PLAYING -> SAVING SCORE\n");
2699 CloseDoor(DOOR_CLOSE_1);
2704 SaveTape(tape.level_nr); /* Ask to save tape */
2707 if (level_nr == leveldir_current->handicap_level)
2709 leveldir_current->handicap_level++;
2710 SaveLevelSetup_SeriesInfo();
2713 if (level_editor_test_game)
2714 local_player->score = -1; /* no highscore when playing from editor */
2715 else if (level_nr < leveldir_current->last_level)
2716 raise_level = TRUE; /* advance to next level */
2718 if ((hi_pos = NewHiScore()) >= 0)
2720 game_status = GAME_MODE_SCORES;
2721 DrawHallOfFame(hi_pos);
2730 game_status = GAME_MODE_MAIN;
2747 LoadScore(level_nr);
2749 if (strcmp(setup.player_name, EMPTY_PLAYER_NAME) == 0 ||
2750 local_player->score < highscore[MAX_SCORE_ENTRIES - 1].Score)
2753 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
2755 if (local_player->score > highscore[k].Score)
2757 /* player has made it to the hall of fame */
2759 if (k < MAX_SCORE_ENTRIES - 1)
2761 int m = MAX_SCORE_ENTRIES - 1;
2764 for (l = k; l < MAX_SCORE_ENTRIES; l++)
2765 if (!strcmp(setup.player_name, highscore[l].Name))
2767 if (m == k) /* player's new highscore overwrites his old one */
2771 for (l = m; l > k; l--)
2773 strcpy(highscore[l].Name, highscore[l - 1].Name);
2774 highscore[l].Score = highscore[l - 1].Score;
2781 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
2782 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
2783 highscore[k].Score = local_player->score;
2789 else if (!strncmp(setup.player_name, highscore[k].Name,
2790 MAX_PLAYER_NAME_LEN))
2791 break; /* player already there with a higher score */
2797 SaveScore(level_nr);
2802 inline static int getElementMoveStepsize(int x, int y)
2804 int element = Feld[x][y];
2805 int direction = MovDir[x][y];
2806 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2807 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2808 int horiz_move = (dx != 0);
2809 int sign = (horiz_move ? dx : dy);
2810 int step = sign * element_info[element].move_stepsize;
2812 /* special values for move stepsize for spring and things on conveyor belt */
2816 if (element == EL_SPRING)
2817 step = sign * MOVE_STEPSIZE_NORMAL * 2;
2818 else if (CAN_FALL(element) && !CAN_MOVE(element) &&
2819 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
2820 step = sign * MOVE_STEPSIZE_NORMAL / 2;
2822 if (CAN_FALL(element) &&
2823 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
2824 step = sign * MOVE_STEPSIZE_NORMAL / 2;
2825 else if (element == EL_SPRING)
2826 step = sign * MOVE_STEPSIZE_NORMAL * 2;
2833 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
2835 if (player->GfxAction != action || player->GfxDir != dir)
2838 printf("Player frame reset! (%d => %d, %d => %d)\n",
2839 player->GfxAction, action, player->GfxDir, dir);
2842 player->GfxAction = action;
2843 player->GfxDir = dir;
2845 player->StepFrame = 0;
2849 static void ResetRandomAnimationValue(int x, int y)
2851 GfxRandom[x][y] = INIT_GFX_RANDOM();
2854 static void ResetGfxAnimation(int x, int y)
2857 int element, graphic;
2861 GfxAction[x][y] = ACTION_DEFAULT;
2862 GfxDir[x][y] = MovDir[x][y];
2865 element = Feld[x][y];
2866 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
2868 if (graphic_info[graphic].anim_global_sync)
2869 GfxFrame[x][y] = FrameCounter;
2870 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
2871 GfxFrame[x][y] = CustomValue[x][y];
2872 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2873 GfxFrame[x][y] = element_info[element].collect_score;
2877 void InitMovingField(int x, int y, int direction)
2879 int element = Feld[x][y];
2883 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2884 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2888 if (!WasJustMoving[x][y] || direction != MovDir[x][y])
2889 ResetGfxAnimation(x, y);
2891 MovDir[x][y] = direction;
2892 GfxDir[x][y] = direction;
2893 GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
2894 ACTION_FALLING : ACTION_MOVING);
2897 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
2899 if (graphic_info[graphic].anim_global_sync)
2900 GfxFrame[x][y] = FrameCounter;
2901 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
2902 GfxFrame[x][y] = CustomValue[x][y];
2903 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2904 GfxFrame[x][y] = element_info[element].collect_score;
2907 /* this is needed for CEs with property "can move" / "not moving" */
2909 if (getElementMoveStepsize(x, y) != 0) /* moving or being moved */
2911 if (Feld[newx][newy] == EL_EMPTY)
2912 Feld[newx][newy] = EL_BLOCKED;
2914 MovDir[newx][newy] = MovDir[x][y];
2916 #if USE_NEW_CUSTOM_VALUE
2917 CustomValue[newx][newy] = CustomValue[x][y];
2920 GfxFrame[newx][newy] = GfxFrame[x][y];
2921 GfxRandom[newx][newy] = GfxRandom[x][y];
2922 GfxAction[newx][newy] = GfxAction[x][y];
2923 GfxDir[newx][newy] = GfxDir[x][y];
2927 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
2929 int direction = MovDir[x][y];
2931 int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
2932 int newy = y + (direction & MV_UP ? -1 : direction & MV_DOWN ? +1 : 0);
2934 int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2935 int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2942 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
2944 int oldx = x, oldy = y;
2945 int direction = MovDir[x][y];
2947 if (direction == MV_LEFT)
2949 else if (direction == MV_RIGHT)
2951 else if (direction == MV_UP)
2953 else if (direction == MV_DOWN)
2956 *comes_from_x = oldx;
2957 *comes_from_y = oldy;
2960 int MovingOrBlocked2Element(int x, int y)
2962 int element = Feld[x][y];
2964 if (element == EL_BLOCKED)
2968 Blocked2Moving(x, y, &oldx, &oldy);
2969 return Feld[oldx][oldy];
2975 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
2977 /* like MovingOrBlocked2Element(), but if element is moving
2978 and (x,y) is the field the moving element is just leaving,
2979 return EL_BLOCKED instead of the element value */
2980 int element = Feld[x][y];
2982 if (IS_MOVING(x, y))
2984 if (element == EL_BLOCKED)
2988 Blocked2Moving(x, y, &oldx, &oldy);
2989 return Feld[oldx][oldy];
2998 static void RemoveField(int x, int y)
3000 Feld[x][y] = EL_EMPTY;
3006 #if USE_NEW_CUSTOM_VALUE
3007 CustomValue[x][y] = 0;
3011 ChangeDelay[x][y] = 0;
3012 ChangePage[x][y] = -1;
3013 Pushed[x][y] = FALSE;
3016 ExplodeField[x][y] = EX_TYPE_NONE;
3019 GfxElement[x][y] = EL_UNDEFINED;
3020 GfxAction[x][y] = ACTION_DEFAULT;
3021 GfxDir[x][y] = MV_NONE;
3024 void RemoveMovingField(int x, int y)
3026 int oldx = x, oldy = y, newx = x, newy = y;
3027 int element = Feld[x][y];
3028 int next_element = EL_UNDEFINED;
3030 if (element != EL_BLOCKED && !IS_MOVING(x, y))
3033 if (IS_MOVING(x, y))
3035 Moving2Blocked(x, y, &newx, &newy);
3037 if (Feld[newx][newy] != EL_BLOCKED)
3039 /* element is moving, but target field is not free (blocked), but
3040 already occupied by something different (example: acid pool);
3041 in this case, only remove the moving field, but not the target */
3043 RemoveField(oldx, oldy);
3045 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
3047 DrawLevelField(oldx, oldy);
3052 else if (element == EL_BLOCKED)
3054 Blocked2Moving(x, y, &oldx, &oldy);
3055 if (!IS_MOVING(oldx, oldy))
3059 if (element == EL_BLOCKED &&
3060 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
3061 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
3062 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
3063 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
3064 next_element = get_next_element(Feld[oldx][oldy]);
3066 RemoveField(oldx, oldy);
3067 RemoveField(newx, newy);
3069 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
3071 if (next_element != EL_UNDEFINED)
3072 Feld[oldx][oldy] = next_element;
3074 DrawLevelField(oldx, oldy);
3075 DrawLevelField(newx, newy);
3078 void DrawDynamite(int x, int y)
3080 int sx = SCREENX(x), sy = SCREENY(y);
3081 int graphic = el2img(Feld[x][y]);
3084 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
3087 if (IS_WALKABLE_INSIDE(Back[x][y]))
3091 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
3092 else if (Store[x][y])
3093 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
3095 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
3097 if (Back[x][y] || Store[x][y])
3098 DrawGraphicThruMask(sx, sy, graphic, frame);
3100 DrawGraphic(sx, sy, graphic, frame);
3103 void CheckDynamite(int x, int y)
3105 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
3109 if (MovDelay[x][y] != 0)
3112 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
3118 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
3123 void DrawRelocatePlayer(struct PlayerInfo *player, boolean quick_relocation)
3125 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3126 boolean no_delay = (tape.warp_forward);
3127 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
3128 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
3129 int jx = player->jx;
3130 int jy = player->jy;
3132 if (quick_relocation)
3134 int offset = (setup.scroll_delay ? 3 : 0);
3136 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
3138 scroll_x = (player->jx < SBX_Left + MIDPOSX ? SBX_Left :
3139 player->jx > SBX_Right + MIDPOSX ? SBX_Right :
3140 player->jx - MIDPOSX);
3142 scroll_y = (player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
3143 player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
3144 player->jy - MIDPOSY);
3148 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
3149 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
3150 scroll_x = jx - MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
3152 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
3153 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
3154 scroll_y = jy - MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
3156 /* don't scroll over playfield boundaries */
3157 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
3158 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
3160 /* don't scroll over playfield boundaries */
3161 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
3162 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
3165 RedrawPlayfield(TRUE, 0,0,0,0);
3169 int scroll_xx = (player->jx < SBX_Left + MIDPOSX ? SBX_Left :
3170 player->jx > SBX_Right + MIDPOSX ? SBX_Right :
3171 player->jx - MIDPOSX);
3173 int scroll_yy = (player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
3174 player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
3175 player->jy - MIDPOSY);
3177 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
3179 while (scroll_x != scroll_xx || scroll_y != scroll_yy)
3182 int fx = FX, fy = FY;
3184 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
3185 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
3187 if (dx == 0 && dy == 0) /* no scrolling needed at all */
3193 fx += dx * TILEX / 2;
3194 fy += dy * TILEY / 2;
3196 ScrollLevel(dx, dy);
3199 /* scroll in two steps of half tile size to make things smoother */
3200 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
3202 Delay(wait_delay_value);
3204 /* scroll second step to align at full tile size */
3206 Delay(wait_delay_value);
3211 Delay(wait_delay_value);
3215 void RelocatePlayer(int jx, int jy, int el_player_raw)
3217 int el_player = GET_PLAYER_ELEMENT(el_player_raw);
3218 int player_nr = GET_PLAYER_NR(el_player);
3219 struct PlayerInfo *player = &stored_player[player_nr];
3220 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3221 boolean no_delay = (tape.warp_forward);
3222 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
3223 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
3224 int old_jx = player->jx;
3225 int old_jy = player->jy;
3226 int old_element = Feld[old_jx][old_jy];
3227 int element = Feld[jx][jy];
3228 boolean player_relocated = (old_jx != jx || old_jy != jy);
3230 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
3231 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
3232 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
3233 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
3234 int leave_side_horiz = move_dir_horiz;
3235 int leave_side_vert = move_dir_vert;
3236 int enter_side = enter_side_horiz | enter_side_vert;
3237 int leave_side = leave_side_horiz | leave_side_vert;
3239 if (player->GameOver) /* do not reanimate dead player */
3242 if (!player_relocated) /* no need to relocate the player */
3245 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
3247 RemoveField(jx, jy); /* temporarily remove newly placed player */
3248 DrawLevelField(jx, jy);
3251 if (player->present)
3253 while (player->MovPos)
3255 ScrollPlayer(player, SCROLL_GO_ON);
3256 ScrollScreen(NULL, SCROLL_GO_ON);
3258 AdvanceFrameAndPlayerCounters(player->index_nr);
3263 Delay(wait_delay_value);
3266 DrawPlayer(player); /* needed here only to cleanup last field */
3267 DrawLevelField(player->jx, player->jy); /* remove player graphic */
3269 player->is_moving = FALSE;
3272 if (IS_CUSTOM_ELEMENT(old_element))
3273 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
3275 player->index_bit, leave_side);
3277 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
3279 player->index_bit, leave_side);
3281 Feld[jx][jy] = el_player;
3282 InitPlayerField(jx, jy, el_player, TRUE);
3284 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
3286 Feld[jx][jy] = element;
3287 InitField(jx, jy, FALSE);
3291 /* only visually relocate centered player */
3292 if (player->index_nr == game.centered_player_nr)
3293 DrawRelocatePlayer(player, level.instant_relocation);
3295 if (player == local_player) /* only visually relocate local player */
3296 DrawRelocatePlayer(player, level.instant_relocation);
3299 TestIfPlayerTouchesBadThing(jx, jy);
3300 TestIfPlayerTouchesCustomElement(jx, jy);
3302 if (IS_CUSTOM_ELEMENT(element))
3303 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
3304 player->index_bit, enter_side);
3306 CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
3307 player->index_bit, enter_side);
3310 void Explode(int ex, int ey, int phase, int mode)
3316 /* !!! eliminate this variable !!! */
3317 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
3319 if (game.explosions_delayed)
3321 ExplodeField[ex][ey] = mode;
3325 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
3327 int center_element = Feld[ex][ey];
3328 int artwork_element, explosion_element; /* set these values later */
3331 /* --- This is only really needed (and now handled) in "Impact()". --- */
3332 /* do not explode moving elements that left the explode field in time */
3333 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
3334 center_element == EL_EMPTY &&
3335 (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
3340 /* !!! at this place, the center element may be EL_BLOCKED !!! */
3341 if (mode == EX_TYPE_NORMAL ||
3342 mode == EX_TYPE_CENTER ||
3343 mode == EX_TYPE_CROSS)
3344 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
3347 /* remove things displayed in background while burning dynamite */
3348 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
3351 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
3353 /* put moving element to center field (and let it explode there) */
3354 center_element = MovingOrBlocked2Element(ex, ey);
3355 RemoveMovingField(ex, ey);
3356 Feld[ex][ey] = center_element;
3359 /* now "center_element" is finally determined -- set related values now */
3360 artwork_element = center_element; /* for custom player artwork */
3361 explosion_element = center_element; /* for custom player artwork */
3363 if (IS_PLAYER(ex, ey))
3365 int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
3367 artwork_element = stored_player[player_nr].artwork_element;
3369 if (level.use_explosion_element[player_nr])
3371 explosion_element = level.explosion_element[player_nr];
3372 artwork_element = explosion_element;
3377 if (mode == EX_TYPE_NORMAL ||
3378 mode == EX_TYPE_CENTER ||
3379 mode == EX_TYPE_CROSS)
3380 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
3384 last_phase = element_info[explosion_element].explosion_delay + 1;
3386 last_phase = element_info[center_element].explosion_delay + 1;
3389 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
3391 int xx = x - ex + 1;
3392 int yy = y - ey + 1;
3395 if (!IN_LEV_FIELD(x, y) ||
3396 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
3397 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
3400 element = Feld[x][y];
3402 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
3404 element = MovingOrBlocked2Element(x, y);
3406 if (!IS_EXPLOSION_PROOF(element))
3407 RemoveMovingField(x, y);
3410 /* indestructible elements can only explode in center (but not flames) */
3411 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
3412 mode == EX_TYPE_BORDER)) ||
3413 element == EL_FLAMES)
3416 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
3417 behaviour, for example when touching a yamyam that explodes to rocks
3418 with active deadly shield, a rock is created under the player !!! */
3419 /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
3421 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
3422 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
3423 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
3425 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
3428 if (IS_ACTIVE_BOMB(element))
3430 /* re-activate things under the bomb like gate or penguin */
3431 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
3438 /* save walkable background elements while explosion on same tile */
3439 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
3440 (x != ex || y != ey || mode == EX_TYPE_BORDER))
3441 Back[x][y] = element;
3443 /* ignite explodable elements reached by other explosion */
3444 if (element == EL_EXPLOSION)
3445 element = Store2[x][y];
3447 if (AmoebaNr[x][y] &&
3448 (element == EL_AMOEBA_FULL ||
3449 element == EL_BD_AMOEBA ||
3450 element == EL_AMOEBA_GROWING))
3452 AmoebaCnt[AmoebaNr[x][y]]--;
3453 AmoebaCnt2[AmoebaNr[x][y]]--;
3458 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
3461 int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
3463 Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
3465 switch(StorePlayer[ex][ey])
3468 Store[x][y] = EL_PLAYER_IS_EXPLODING_2;
3471 Store[x][y] = EL_PLAYER_IS_EXPLODING_3;
3474 Store[x][y] = EL_PLAYER_IS_EXPLODING_4;
3478 Store[x][y] = EL_PLAYER_IS_EXPLODING_1;
3483 if (PLAYERINFO(ex, ey)->use_murphy)
3484 Store[x][y] = EL_EMPTY;
3487 /* !!! check this case -- currently needed for rnd_rado_negundo_v,
3488 !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
3489 else if (ELEM_IS_PLAYER(center_element))
3490 Store[x][y] = EL_EMPTY;
3491 else if (center_element == EL_YAMYAM)
3492 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
3493 else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
3494 Store[x][y] = element_info[center_element].content.e[xx][yy];
3496 /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
3497 (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
3498 otherwise) -- FIX THIS !!! */
3499 else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
3500 Store[x][y] = element_info[element].content.e[1][1];
3502 else if (!CAN_EXPLODE(element))
3503 Store[x][y] = element_info[element].content.e[1][1];
3506 Store[x][y] = EL_EMPTY;
3508 else if (center_element == EL_MOLE)
3509 Store[x][y] = EL_EMERALD_RED;
3510 else if (center_element == EL_PENGUIN)
3511 Store[x][y] = EL_EMERALD_PURPLE;
3512 else if (center_element == EL_BUG)
3513 Store[x][y] = ((x == ex && y == ey) ? EL_DIAMOND : EL_EMERALD);
3514 else if (center_element == EL_BD_BUTTERFLY)
3515 Store[x][y] = EL_BD_DIAMOND;
3516 else if (center_element == EL_SP_ELECTRON)
3517 Store[x][y] = EL_SP_INFOTRON;
3518 else if (center_element == EL_AMOEBA_TO_DIAMOND)
3519 Store[x][y] = level.amoeba_content;
3520 else if (center_element == EL_YAMYAM)
3521 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
3522 else if (IS_CUSTOM_ELEMENT(center_element) &&
3523 element_info[center_element].content.e[xx][yy] != EL_EMPTY)
3524 Store[x][y] = element_info[center_element].content.e[xx][yy];
3525 else if (element == EL_WALL_EMERALD)
3526 Store[x][y] = EL_EMERALD;
3527 else if (element == EL_WALL_DIAMOND)
3528 Store[x][y] = EL_DIAMOND;
3529 else if (element == EL_WALL_BD_DIAMOND)
3530 Store[x][y] = EL_BD_DIAMOND;
3531 else if (element == EL_WALL_EMERALD_YELLOW)
3532 Store[x][y] = EL_EMERALD_YELLOW;
3533 else if (element == EL_WALL_EMERALD_RED)
3534 Store[x][y] = EL_EMERALD_RED;
3535 else if (element == EL_WALL_EMERALD_PURPLE)
3536 Store[x][y] = EL_EMERALD_PURPLE;
3537 else if (element == EL_WALL_PEARL)
3538 Store[x][y] = EL_PEARL;
3539 else if (element == EL_WALL_CRYSTAL)
3540 Store[x][y] = EL_CRYSTAL;
3541 else if (IS_CUSTOM_ELEMENT(element) && !CAN_EXPLODE(element))
3542 Store[x][y] = element_info[element].content.e[1][1];
3544 Store[x][y] = EL_EMPTY;
3547 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
3548 center_element == EL_AMOEBA_TO_DIAMOND)
3549 Store2[x][y] = element;
3551 Feld[x][y] = EL_EXPLOSION;
3552 GfxElement[x][y] = artwork_element;
3555 printf(":: setting gfx(%d,%d) to %d ['%s']\n",
3556 x, y, artwork_element, EL_NAME(artwork_element));
3559 ExplodePhase[x][y] = 1;
3560 ExplodeDelay[x][y] = last_phase;
3565 if (center_element == EL_YAMYAM)
3566 game.yamyam_content_nr =
3567 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
3579 GfxFrame[x][y] = 0; /* restart explosion animation */
3581 last_phase = ExplodeDelay[x][y];
3583 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
3587 /* activate this even in non-DEBUG version until cause for crash in
3588 getGraphicAnimationFrame() (see below) is found and eliminated */
3594 /* this can happen if the player leaves an explosion just in time */
3595 if (GfxElement[x][y] == EL_UNDEFINED)
3596 GfxElement[x][y] = EL_EMPTY;
3598 if (GfxElement[x][y] == EL_UNDEFINED)
3601 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
3602 printf("Explode(): This should never happen!\n");
3605 GfxElement[x][y] = EL_EMPTY;
3611 border_element = Store2[x][y];
3612 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3613 border_element = StorePlayer[x][y];
3615 if (phase == element_info[border_element].ignition_delay ||
3616 phase == last_phase)
3618 boolean border_explosion = FALSE;
3620 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
3621 !PLAYER_EXPLOSION_PROTECTED(x, y))
3623 KillPlayerUnlessExplosionProtected(x, y);
3624 border_explosion = TRUE;
3626 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
3628 Feld[x][y] = Store2[x][y];
3631 border_explosion = TRUE;
3633 else if (border_element == EL_AMOEBA_TO_DIAMOND)
3635 AmoebeUmwandeln(x, y);
3637 border_explosion = TRUE;
3640 /* if an element just explodes due to another explosion (chain-reaction),
3641 do not immediately end the new explosion when it was the last frame of
3642 the explosion (as it would be done in the following "if"-statement!) */
3643 if (border_explosion && phase == last_phase)
3647 if (phase == last_phase)
3651 element = Feld[x][y] = Store[x][y];
3652 Store[x][y] = Store2[x][y] = 0;
3653 GfxElement[x][y] = EL_UNDEFINED;
3655 /* player can escape from explosions and might therefore be still alive */
3656 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
3657 element <= EL_PLAYER_IS_EXPLODING_4)
3659 int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
3660 int explosion_element = EL_PLAYER_1 + player_nr;
3661 int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
3662 int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
3664 if (level.use_explosion_element[player_nr])
3665 explosion_element = level.explosion_element[player_nr];
3667 Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
3668 element_info[explosion_element].content.e[xx][yy]);
3671 /* restore probably existing indestructible background element */
3672 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
3673 element = Feld[x][y] = Back[x][y];
3676 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
3677 GfxDir[x][y] = MV_NONE;
3678 ChangeDelay[x][y] = 0;
3679 ChangePage[x][y] = -1;
3681 #if USE_NEW_CUSTOM_VALUE
3682 CustomValue[x][y] = 0;
3685 InitField_WithBug2(x, y, FALSE);
3687 DrawLevelField(x, y);
3689 TestIfElementTouchesCustomElement(x, y);
3691 if (GFX_CRUMBLED(element))
3692 DrawLevelFieldCrumbledSandNeighbours(x, y);
3694 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
3695 StorePlayer[x][y] = 0;
3697 if (ELEM_IS_PLAYER(element))
3698 RelocatePlayer(x, y, element);
3700 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3702 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
3703 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
3706 DrawLevelFieldCrumbledSand(x, y);
3708 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
3710 DrawLevelElement(x, y, Back[x][y]);
3711 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
3713 else if (IS_WALKABLE_UNDER(Back[x][y]))
3715 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3716 DrawLevelElementThruMask(x, y, Back[x][y]);
3718 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
3719 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3723 void DynaExplode(int ex, int ey)
3726 int dynabomb_element = Feld[ex][ey];
3727 int dynabomb_size = 1;
3728 boolean dynabomb_xl = FALSE;
3729 struct PlayerInfo *player;
3730 static int xy[4][2] =
3738 if (IS_ACTIVE_BOMB(dynabomb_element))
3740 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
3741 dynabomb_size = player->dynabomb_size;
3742 dynabomb_xl = player->dynabomb_xl;
3743 player->dynabombs_left++;
3746 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
3748 for (i = 0; i < NUM_DIRECTIONS; i++)
3750 for (j = 1; j <= dynabomb_size; j++)
3752 int x = ex + j * xy[i][0];
3753 int y = ey + j * xy[i][1];
3756 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
3759 element = Feld[x][y];
3761 /* do not restart explosions of fields with active bombs */
3762 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
3765 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
3767 if (element != EL_EMPTY && element != EL_EXPLOSION &&
3768 !IS_DIGGABLE(element) && !dynabomb_xl)
3774 void Bang(int x, int y)
3776 int element = MovingOrBlocked2Element(x, y);
3777 int explosion_type = EX_TYPE_NORMAL;
3779 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3781 struct PlayerInfo *player = PLAYERINFO(x, y);
3783 element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
3784 player->element_nr);
3786 if (level.use_explosion_element[player->index_nr])
3788 int explosion_element = level.explosion_element[player->index_nr];
3790 if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
3791 explosion_type = EX_TYPE_CROSS;
3792 else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
3793 explosion_type = EX_TYPE_CENTER;
3801 case EL_BD_BUTTERFLY:
3804 case EL_DARK_YAMYAM:
3808 RaiseScoreElement(element);
3811 case EL_DYNABOMB_PLAYER_1_ACTIVE:
3812 case EL_DYNABOMB_PLAYER_2_ACTIVE:
3813 case EL_DYNABOMB_PLAYER_3_ACTIVE:
3814 case EL_DYNABOMB_PLAYER_4_ACTIVE:
3815 case EL_DYNABOMB_INCREASE_NUMBER:
3816 case EL_DYNABOMB_INCREASE_SIZE:
3817 case EL_DYNABOMB_INCREASE_POWER:
3818 explosion_type = EX_TYPE_DYNA;
3823 case EL_LAMP_ACTIVE:
3824 case EL_AMOEBA_TO_DIAMOND:
3825 if (!IS_PLAYER(x, y)) /* penguin and player may be at same field */
3826 explosion_type = EX_TYPE_CENTER;
3830 if (element_info[element].explosion_type == EXPLODES_CROSS)
3831 explosion_type = EX_TYPE_CROSS;
3832 else if (element_info[element].explosion_type == EXPLODES_1X1)
3833 explosion_type = EX_TYPE_CENTER;
3837 if (explosion_type == EX_TYPE_DYNA)
3840 Explode(x, y, EX_PHASE_START, explosion_type);
3842 CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
3845 void SplashAcid(int x, int y)
3847 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
3848 (!IN_LEV_FIELD(x - 1, y - 2) ||
3849 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
3850 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
3852 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
3853 (!IN_LEV_FIELD(x + 1, y - 2) ||
3854 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
3855 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
3857 PlayLevelSound(x, y, SND_ACID_SPLASHING);
3860 static void InitBeltMovement()
3862 static int belt_base_element[4] =
3864 EL_CONVEYOR_BELT_1_LEFT,
3865 EL_CONVEYOR_BELT_2_LEFT,
3866 EL_CONVEYOR_BELT_3_LEFT,
3867 EL_CONVEYOR_BELT_4_LEFT
3869 static int belt_base_active_element[4] =
3871 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
3872 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
3873 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
3874 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
3879 /* set frame order for belt animation graphic according to belt direction */
3880 for (i = 0; i < NUM_BELTS; i++)
3884 for (j = 0; j < NUM_BELT_PARTS; j++)
3886 int element = belt_base_active_element[belt_nr] + j;
3887 int graphic = el2img(element);
3889 if (game.belt_dir[i] == MV_LEFT)
3890 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
3892 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
3897 SCAN_PLAYFIELD(x, y)
3899 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
3902 int element = Feld[x][y];
3904 for (i = 0; i < NUM_BELTS; i++)
3906 if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
3908 int e_belt_nr = getBeltNrFromBeltElement(element);
3911 if (e_belt_nr == belt_nr)
3913 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
3915 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
3922 static void ToggleBeltSwitch(int x, int y)
3924 static int belt_base_element[4] =
3926 EL_CONVEYOR_BELT_1_LEFT,
3927 EL_CONVEYOR_BELT_2_LEFT,
3928 EL_CONVEYOR_BELT_3_LEFT,
3929 EL_CONVEYOR_BELT_4_LEFT
3931 static int belt_base_active_element[4] =
3933 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
3934 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
3935 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
3936 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
3938 static int belt_base_switch_element[4] =
3940 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
3941 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
3942 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
3943 EL_CONVEYOR_BELT_4_SWITCH_LEFT
3945 static int belt_move_dir[4] =
3953 int element = Feld[x][y];
3954 int belt_nr = getBeltNrFromBeltSwitchElement(element);
3955 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
3956 int belt_dir = belt_move_dir[belt_dir_nr];
3959 if (!IS_BELT_SWITCH(element))
3962 game.belt_dir_nr[belt_nr] = belt_dir_nr;
3963 game.belt_dir[belt_nr] = belt_dir;
3965 if (belt_dir_nr == 3)
3968 /* set frame order for belt animation graphic according to belt direction */
3969 for (i = 0; i < NUM_BELT_PARTS; i++)
3971 int element = belt_base_active_element[belt_nr] + i;
3972 int graphic = el2img(element);
3974 if (belt_dir == MV_LEFT)
3975 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
3977 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
3981 SCAN_PLAYFIELD(xx, yy)
3983 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
3986 int element = Feld[xx][yy];
3988 if (IS_BELT_SWITCH(element))
3990 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
3992 if (e_belt_nr == belt_nr)
3994 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
3995 DrawLevelField(xx, yy);
3998 else if (IS_BELT(element) && belt_dir != MV_NONE)
4000 int e_belt_nr = getBeltNrFromBeltElement(element);
4002 if (e_belt_nr == belt_nr)
4004 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
4006 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
4007 DrawLevelField(xx, yy);
4010 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
4012 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
4014 if (e_belt_nr == belt_nr)
4016 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
4018 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
4019 DrawLevelField(xx, yy);
4025 static void ToggleSwitchgateSwitch(int x, int y)
4029 game.switchgate_pos = !game.switchgate_pos;
4032 SCAN_PLAYFIELD(xx, yy)
4034 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
4037 int element = Feld[xx][yy];
4039 if (element == EL_SWITCHGATE_SWITCH_UP ||
4040 element == EL_SWITCHGATE_SWITCH_DOWN)
4042 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
4043 DrawLevelField(xx, yy);
4045 else if (element == EL_SWITCHGATE_OPEN ||
4046 element == EL_SWITCHGATE_OPENING)
4048 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
4050 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
4052 else if (element == EL_SWITCHGATE_CLOSED ||
4053 element == EL_SWITCHGATE_CLOSING)
4055 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
4057 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
4062 static int getInvisibleActiveFromInvisibleElement(int element)
4064 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
4065 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
4066 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
4070 static int getInvisibleFromInvisibleActiveElement(int element)
4072 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
4073 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
4074 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
4078 static void RedrawAllLightSwitchesAndInvisibleElements()
4083 SCAN_PLAYFIELD(x, y)
4085 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
4088 int element = Feld[x][y];
4090 if (element == EL_LIGHT_SWITCH &&
4091 game.light_time_left > 0)
4093 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
4094 DrawLevelField(x, y);
4096 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
4097 game.light_time_left == 0)
4099 Feld[x][y] = EL_LIGHT_SWITCH;
4100 DrawLevelField(x, y);
4102 else if (element == EL_EMC_DRIPPER &&
4103 game.light_time_left > 0)
4105 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
4106 DrawLevelField(x, y);
4108 else if (element == EL_EMC_DRIPPER_ACTIVE &&
4109 game.light_time_left == 0)
4111 Feld[x][y] = EL_EMC_DRIPPER;
4112 DrawLevelField(x, y);
4114 else if (element == EL_INVISIBLE_STEELWALL ||
4115 element == EL_INVISIBLE_WALL ||
4116 element == EL_INVISIBLE_SAND)
4118 if (game.light_time_left > 0)
4119 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
4121 DrawLevelField(x, y);
4123 /* uncrumble neighbour fields, if needed */
4124 if (element == EL_INVISIBLE_SAND)
4125 DrawLevelFieldCrumbledSandNeighbours(x, y);
4127 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
4128 element == EL_INVISIBLE_WALL_ACTIVE ||
4129 element == EL_INVISIBLE_SAND_ACTIVE)
4131 if (game.light_time_left == 0)
4132 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
4134 DrawLevelField(x, y);
4136 /* re-crumble neighbour fields, if needed */
4137 if (element == EL_INVISIBLE_SAND)
4138 DrawLevelFieldCrumbledSandNeighbours(x, y);
4143 static void RedrawAllInvisibleElementsForLenses()
4148 SCAN_PLAYFIELD(x, y)
4150 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
4153 int element = Feld[x][y];
4155 if (element == EL_EMC_DRIPPER &&
4156 game.lenses_time_left > 0)
4158 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
4159 DrawLevelField(x, y);
4161 else if (element == EL_EMC_DRIPPER_ACTIVE &&
4162 game.lenses_time_left == 0)
4164 Feld[x][y] = EL_EMC_DRIPPER;
4165 DrawLevelField(x, y);
4167 else if (element == EL_INVISIBLE_STEELWALL ||
4168 element == EL_INVISIBLE_WALL ||
4169 element == EL_INVISIBLE_SAND)
4171 if (game.lenses_time_left > 0)
4172 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
4174 DrawLevelField(x, y);
4176 /* uncrumble neighbour fields, if needed */
4177 if (element == EL_INVISIBLE_SAND)
4178 DrawLevelFieldCrumbledSandNeighbours(x, y);
4180 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
4181 element == EL_INVISIBLE_WALL_ACTIVE ||
4182 element == EL_INVISIBLE_SAND_ACTIVE)
4184 if (game.lenses_time_left == 0)
4185 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
4187 DrawLevelField(x, y);
4189 /* re-crumble neighbour fields, if needed */
4190 if (element == EL_INVISIBLE_SAND)
4191 DrawLevelFieldCrumbledSandNeighbours(x, y);
4196 static void RedrawAllInvisibleElementsForMagnifier()
4201 SCAN_PLAYFIELD(x, y)
4203 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
4206 int element = Feld[x][y];
4208 if (element == EL_EMC_FAKE_GRASS &&
4209 game.magnify_time_left > 0)
4211 Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
4212 DrawLevelField(x, y);
4214 else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
4215 game.magnify_time_left == 0)
4217 Feld[x][y] = EL_EMC_FAKE_GRASS;
4218 DrawLevelField(x, y);
4220 else if (IS_GATE_GRAY(element) &&
4221 game.magnify_time_left > 0)
4223 Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
4224 element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
4225 IS_EM_GATE_GRAY(element) ?
4226 element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
4227 IS_EMC_GATE_GRAY(element) ?
4228 element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
4230 DrawLevelField(x, y);
4232 else if (IS_GATE_GRAY_ACTIVE(element) &&
4233 game.magnify_time_left == 0)
4235 Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
4236 element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
4237 IS_EM_GATE_GRAY_ACTIVE(element) ?
4238 element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
4239 IS_EMC_GATE_GRAY_ACTIVE(element) ?
4240 element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
4242 DrawLevelField(x, y);
4247 static void ToggleLightSwitch(int x, int y)
4249 int element = Feld[x][y];
4251 game.light_time_left =
4252 (element == EL_LIGHT_SWITCH ?
4253 level.time_light * FRAMES_PER_SECOND : 0);
4255 RedrawAllLightSwitchesAndInvisibleElements();
4258 static void ActivateTimegateSwitch(int x, int y)
4262 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
4265 SCAN_PLAYFIELD(xx, yy)
4267 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
4270 int element = Feld[xx][yy];
4272 if (element == EL_TIMEGATE_CLOSED ||
4273 element == EL_TIMEGATE_CLOSING)
4275 Feld[xx][yy] = EL_TIMEGATE_OPENING;
4276 PlayLevelSound(xx, yy, SND_TIMEGATE_OPENING);
4280 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
4282 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
4283 DrawLevelField(xx, yy);
4289 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
4292 void Impact(int x, int y)
4294 boolean last_line = (y == lev_fieldy - 1);
4295 boolean object_hit = FALSE;
4296 boolean impact = (last_line || object_hit);
4297 int element = Feld[x][y];
4298 int smashed = EL_STEELWALL;
4300 if (!last_line) /* check if element below was hit */
4302 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
4305 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
4306 MovDir[x][y + 1] != MV_DOWN ||
4307 MovPos[x][y + 1] <= TILEY / 2));
4309 /* do not smash moving elements that left the smashed field in time */
4310 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
4311 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
4314 #if USE_QUICKSAND_IMPACT_BUGFIX
4315 if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
4317 RemoveMovingField(x, y + 1);
4318 Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
4319 Feld[x][y + 2] = EL_ROCK;
4320 DrawLevelField(x, y + 2);
4327 smashed = MovingOrBlocked2Element(x, y + 1);
4329 impact = (last_line || object_hit);
4332 if (!last_line && smashed == EL_ACID) /* element falls into acid */
4334 SplashAcid(x, y + 1);
4338 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
4339 /* only reset graphic animation if graphic really changes after impact */
4341 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
4343 ResetGfxAnimation(x, y);
4344 DrawLevelField(x, y);
4347 if (impact && CAN_EXPLODE_IMPACT(element))
4352 else if (impact && element == EL_PEARL)
4354 ResetGfxAnimation(x, y);
4356 Feld[x][y] = EL_PEARL_BREAKING;
4357 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4360 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
4362 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4367 if (impact && element == EL_AMOEBA_DROP)
4369 if (object_hit && IS_PLAYER(x, y + 1))
4370 KillPlayerUnlessEnemyProtected(x, y + 1);
4371 else if (object_hit && smashed == EL_PENGUIN)
4375 Feld[x][y] = EL_AMOEBA_GROWING;
4376 Store[x][y] = EL_AMOEBA_WET;
4378 ResetRandomAnimationValue(x, y);
4383 if (object_hit) /* check which object was hit */
4385 if (CAN_PASS_MAGIC_WALL(element) &&
4386 (smashed == EL_MAGIC_WALL ||
4387 smashed == EL_BD_MAGIC_WALL))
4390 int activated_magic_wall =
4391 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
4392 EL_BD_MAGIC_WALL_ACTIVE);
4394 /* activate magic wall / mill */
4396 SCAN_PLAYFIELD(xx, yy)
4398 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
4400 if (Feld[xx][yy] == smashed)
4401 Feld[xx][yy] = activated_magic_wall;
4403 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
4404 game.magic_wall_active = TRUE;
4406 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
4407 SND_MAGIC_WALL_ACTIVATING :
4408 SND_BD_MAGIC_WALL_ACTIVATING));
4411 if (IS_PLAYER(x, y + 1))
4413 if (CAN_SMASH_PLAYER(element))
4415 KillPlayerUnlessEnemyProtected(x, y + 1);
4419 else if (smashed == EL_PENGUIN)
4421 if (CAN_SMASH_PLAYER(element))
4427 else if (element == EL_BD_DIAMOND)
4429 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
4435 else if (((element == EL_SP_INFOTRON ||
4436 element == EL_SP_ZONK) &&
4437 (smashed == EL_SP_SNIKSNAK ||
4438 smashed == EL_SP_ELECTRON ||
4439 smashed == EL_SP_DISK_ORANGE)) ||
4440 (element == EL_SP_INFOTRON &&
4441 smashed == EL_SP_DISK_YELLOW))
4446 else if (CAN_SMASH_EVERYTHING(element))
4448 if (IS_CLASSIC_ENEMY(smashed) ||
4449 CAN_EXPLODE_SMASHED(smashed))
4454 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
4456 if (smashed == EL_LAMP ||
4457 smashed == EL_LAMP_ACTIVE)
4462 else if (smashed == EL_NUT)
4464 Feld[x][y + 1] = EL_NUT_BREAKING;
4465 PlayLevelSound(x, y, SND_NUT_BREAKING);
4466 RaiseScoreElement(EL_NUT);
4469 else if (smashed == EL_PEARL)
4471 ResetGfxAnimation(x, y);
4473 Feld[x][y + 1] = EL_PEARL_BREAKING;
4474 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4477 else if (smashed == EL_DIAMOND)
4479 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
4480 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
4483 else if (IS_BELT_SWITCH(smashed))
4485 ToggleBeltSwitch(x, y + 1);
4487 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
4488 smashed == EL_SWITCHGATE_SWITCH_DOWN)
4490 ToggleSwitchgateSwitch(x, y + 1);
4492 else if (smashed == EL_LIGHT_SWITCH ||
4493 smashed == EL_LIGHT_SWITCH_ACTIVE)
4495 ToggleLightSwitch(x, y + 1);
4500 TestIfElementSmashesCustomElement(x, y, MV_DOWN);
4503 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4505 CheckElementChangeBySide(x, y + 1, smashed, element,
4506 CE_SWITCHED, CH_SIDE_TOP);
4507 CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
4513 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4518 /* play sound of magic wall / mill */
4520 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
4521 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
4523 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
4524 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
4525 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
4526 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
4531 /* play sound of object that hits the ground */
4532 if (last_line || object_hit)
4533 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4536 inline static void TurnRoundExt(int x, int y)
4548 { 0, 0 }, { 0, 0 }, { 0, 0 },
4553 int left, right, back;
4557 { MV_DOWN, MV_UP, MV_RIGHT },
4558 { MV_UP, MV_DOWN, MV_LEFT },
4560 { MV_LEFT, MV_RIGHT, MV_DOWN },
4564 { MV_RIGHT, MV_LEFT, MV_UP }
4567 int element = Feld[x][y];
4568 int move_pattern = element_info[element].move_pattern;
4570 int old_move_dir = MovDir[x][y];
4571 int left_dir = turn[old_move_dir].left;
4572 int right_dir = turn[old_move_dir].right;
4573 int back_dir = turn[old_move_dir].back;
4575 int left_dx = move_xy[left_dir].dx, left_dy = move_xy[left_dir].dy;
4576 int right_dx = move_xy[right_dir].dx, right_dy = move_xy[right_dir].dy;
4577 int move_dx = move_xy[old_move_dir].dx, move_dy = move_xy[old_move_dir].dy;
4578 int back_dx = move_xy[back_dir].dx, back_dy = move_xy[back_dir].dy;
4580 int left_x = x + left_dx, left_y = y + left_dy;
4581 int right_x = x + right_dx, right_y = y + right_dy;
4582 int move_x = x + move_dx, move_y = y + move_dy;
4586 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4588 TestIfBadThingTouchesOtherBadThing(x, y);
4590 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
4591 MovDir[x][y] = right_dir;
4592 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4593 MovDir[x][y] = left_dir;
4595 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
4597 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
4600 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
4602 TestIfBadThingTouchesOtherBadThing(x, y);
4604 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
4605 MovDir[x][y] = left_dir;
4606 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4607 MovDir[x][y] = right_dir;
4609 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
4611 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
4614 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4616 TestIfBadThingTouchesOtherBadThing(x, y);
4618 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
4619 MovDir[x][y] = left_dir;
4620 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
4621 MovDir[x][y] = right_dir;
4623 if (MovDir[x][y] != old_move_dir)
4626 else if (element == EL_YAMYAM)
4628 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
4629 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
4631 if (can_turn_left && can_turn_right)
4632 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4633 else if (can_turn_left)
4634 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4635 else if (can_turn_right)
4636 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4638 MovDir[x][y] = back_dir;
4640 MovDelay[x][y] = 16 + 16 * RND(3);
4642 else if (element == EL_DARK_YAMYAM)
4644 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4646 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4649 if (can_turn_left && can_turn_right)
4650 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4651 else if (can_turn_left)
4652 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4653 else if (can_turn_right)
4654 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4656 MovDir[x][y] = back_dir;
4658 MovDelay[x][y] = 16 + 16 * RND(3);
4660 else if (element == EL_PACMAN)
4662 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
4663 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
4665 if (can_turn_left && can_turn_right)
4666 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4667 else if (can_turn_left)
4668 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4669 else if (can_turn_right)
4670 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4672 MovDir[x][y] = back_dir;
4674 MovDelay[x][y] = 6 + RND(40);
4676 else if (element == EL_PIG)
4678 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
4679 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
4680 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
4681 boolean should_turn_left, should_turn_right, should_move_on;
4683 int rnd = RND(rnd_value);
4685 should_turn_left = (can_turn_left &&
4687 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
4688 y + back_dy + left_dy)));
4689 should_turn_right = (can_turn_right &&
4691 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
4692 y + back_dy + right_dy)));
4693 should_move_on = (can_move_on &&
4696 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
4697 y + move_dy + left_dy) ||
4698 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
4699 y + move_dy + right_dy)));
4701 if (should_turn_left || should_turn_right || should_move_on)
4703 if (should_turn_left && should_turn_right && should_move_on)
4704 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
4705 rnd < 2 * rnd_value / 3 ? right_dir :
4707 else if (should_turn_left && should_turn_right)
4708 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4709 else if (should_turn_left && should_move_on)
4710 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
4711 else if (should_turn_right && should_move_on)
4712 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
4713 else if (should_turn_left)
4714 MovDir[x][y] = left_dir;
4715 else if (should_turn_right)
4716 MovDir[x][y] = right_dir;
4717 else if (should_move_on)
4718 MovDir[x][y] = old_move_dir;
4720 else if (can_move_on && rnd > rnd_value / 8)
4721 MovDir[x][y] = old_move_dir;
4722 else if (can_turn_left && can_turn_right)
4723 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4724 else if (can_turn_left && rnd > rnd_value / 8)
4725 MovDir[x][y] = left_dir;
4726 else if (can_turn_right && rnd > rnd_value/8)
4727 MovDir[x][y] = right_dir;
4729 MovDir[x][y] = back_dir;
4731 xx = x + move_xy[MovDir[x][y]].dx;
4732 yy = y + move_xy[MovDir[x][y]].dy;
4734 if (!IN_LEV_FIELD(xx, yy) ||
4735 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
4736 MovDir[x][y] = old_move_dir;
4740 else if (element == EL_DRAGON)
4742 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
4743 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
4744 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
4746 int rnd = RND(rnd_value);
4748 if (can_move_on && rnd > rnd_value / 8)
4749 MovDir[x][y] = old_move_dir;
4750 else if (can_turn_left && can_turn_right)
4751 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4752 else if (can_turn_left && rnd > rnd_value / 8)
4753 MovDir[x][y] = left_dir;
4754 else if (can_turn_right && rnd > rnd_value / 8)
4755 MovDir[x][y] = right_dir;
4757 MovDir[x][y] = back_dir;
4759 xx = x + move_xy[MovDir[x][y]].dx;
4760 yy = y + move_xy[MovDir[x][y]].dy;
4762 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
4763 MovDir[x][y] = old_move_dir;
4767 else if (element == EL_MOLE)
4769 boolean can_move_on =
4770 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
4771 IS_AMOEBOID(Feld[move_x][move_y]) ||
4772 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
4775 boolean can_turn_left =
4776 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
4777 IS_AMOEBOID(Feld[left_x][left_y])));
4779 boolean can_turn_right =
4780 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
4781 IS_AMOEBOID(Feld[right_x][right_y])));
4783 if (can_turn_left && can_turn_right)
4784 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
4785 else if (can_turn_left)
4786 MovDir[x][y] = left_dir;
4788 MovDir[x][y] = right_dir;
4791 if (MovDir[x][y] != old_move_dir)
4794 else if (element == EL_BALLOON)
4796 MovDir[x][y] = game.wind_direction;
4799 else if (element == EL_SPRING)
4801 #if USE_NEW_SPRING_BUMPER
4802 if (MovDir[x][y] & MV_HORIZONTAL)
4804 if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
4805 !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
4807 Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
4808 ResetGfxAnimation(move_x, move_y);
4809 DrawLevelField(move_x, move_y);
4811 MovDir[x][y] = back_dir;
4813 else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
4814 SPRING_CAN_ENTER_FIELD(element, x, y + 1))
4815 MovDir[x][y] = MV_NONE;
4818 if (MovDir[x][y] & MV_HORIZONTAL &&
4819 (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
4820 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
4821 MovDir[x][y] = MV_NONE;
4826 else if (element == EL_ROBOT ||
4827 element == EL_SATELLITE ||
4828 element == EL_PENGUIN ||
4829 element == EL_EMC_ANDROID)
4831 int attr_x = -1, attr_y = -1;
4842 for (i = 0; i < MAX_PLAYERS; i++)
4844 struct PlayerInfo *player = &stored_player[i];
4845 int jx = player->jx, jy = player->jy;
4847 if (!player->active)
4851 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
4859 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
4860 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
4861 game.engine_version < VERSION_IDENT(3,1,0,0)))
4867 if (element == EL_PENGUIN)
4870 static int xy[4][2] =
4878 for (i = 0; i < NUM_DIRECTIONS; i++)
4880 int ex = x + xy[i][0];
4881 int ey = y + xy[i][1];
4883 if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
4892 MovDir[x][y] = MV_NONE;
4894 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
4895 else if (attr_x > x)
4896 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
4898 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
4899 else if (attr_y > y)
4900 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
4902 if (element == EL_ROBOT)
4906 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4907 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
4908 Moving2Blocked(x, y, &newx, &newy);
4910 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
4911 MovDelay[x][y] = 8 + 8 * !RND(3);
4913 MovDelay[x][y] = 16;
4915 else if (element == EL_PENGUIN)
4921 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4923 boolean first_horiz = RND(2);
4924 int new_move_dir = MovDir[x][y];
4927 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4928 Moving2Blocked(x, y, &newx, &newy);
4930 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
4934 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4935 Moving2Blocked(x, y, &newx, &newy);
4937 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
4940 MovDir[x][y] = old_move_dir;
4944 else if (element == EL_SATELLITE)
4950 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4952 boolean first_horiz = RND(2);
4953 int new_move_dir = MovDir[x][y];
4956 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4957 Moving2Blocked(x, y, &newx, &newy);
4959 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
4963 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4964 Moving2Blocked(x, y, &newx, &newy);
4966 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
4969 MovDir[x][y] = old_move_dir;
4973 else if (element == EL_EMC_ANDROID)
4975 static int check_pos[16] =
4977 -1, /* 0 => (invalid) */
4978 7, /* 1 => MV_LEFT */
4979 3, /* 2 => MV_RIGHT */
4980 -1, /* 3 => (invalid) */
4982 0, /* 5 => MV_LEFT | MV_UP */
4983 2, /* 6 => MV_RIGHT | MV_UP */
4984 -1, /* 7 => (invalid) */
4985 5, /* 8 => MV_DOWN */
4986 6, /* 9 => MV_LEFT | MV_DOWN */
4987 4, /* 10 => MV_RIGHT | MV_DOWN */
4988 -1, /* 11 => (invalid) */
4989 -1, /* 12 => (invalid) */
4990 -1, /* 13 => (invalid) */
4991 -1, /* 14 => (invalid) */
4992 -1, /* 15 => (invalid) */
5000 { -1, -1, MV_LEFT | MV_UP },
5002 { +1, -1, MV_RIGHT | MV_UP },
5003 { +1, 0, MV_RIGHT },
5004 { +1, +1, MV_RIGHT | MV_DOWN },
5006 { -1, +1, MV_LEFT | MV_DOWN },
5009 int start_pos, check_order;
5010 boolean can_clone = FALSE;
5013 /* check if there is any free field around current position */
5014 for (i = 0; i < 8; i++)
5016 int newx = x + check_xy[i].dx;
5017 int newy = y + check_xy[i].dy;
5019 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5027 if (can_clone) /* randomly find an element to clone */
5031 start_pos = check_pos[RND(8)];
5032 check_order = (RND(2) ? -1 : +1);
5034 for (i = 0; i < 8; i++)
5036 int pos_raw = start_pos + i * check_order;
5037 int pos = (pos_raw + 8) % 8;
5038 int newx = x + check_xy[pos].dx;
5039 int newy = y + check_xy[pos].dy;
5041 if (ANDROID_CAN_CLONE_FIELD(newx, newy))
5043 element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
5044 element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
5046 Store[x][y] = Feld[newx][newy];
5055 if (can_clone) /* randomly find a direction to move */
5059 start_pos = check_pos[RND(8)];
5060 check_order = (RND(2) ? -1 : +1);
5062 for (i = 0; i < 8; i++)
5064 int pos_raw = start_pos + i * check_order;
5065 int pos = (pos_raw + 8) % 8;
5066 int newx = x + check_xy[pos].dx;
5067 int newy = y + check_xy[pos].dy;
5068 int new_move_dir = check_xy[pos].dir;
5070 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5072 MovDir[x][y] = new_move_dir;
5073 MovDelay[x][y] = level.android_clone_time * 8 + 1;
5082 if (can_clone) /* cloning and moving successful */
5085 /* cannot clone -- try to move towards player */
5087 start_pos = check_pos[MovDir[x][y] & 0x0f];
5088 check_order = (RND(2) ? -1 : +1);
5090 for (i = 0; i < 3; i++)
5092 /* first check start_pos, then previous/next or (next/previous) pos */
5093 int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
5094 int pos = (pos_raw + 8) % 8;
5095 int newx = x + check_xy[pos].dx;
5096 int newy = y + check_xy[pos].dy;
5097 int new_move_dir = check_xy[pos].dir;
5099 if (IS_PLAYER(newx, newy))
5102 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
5104 MovDir[x][y] = new_move_dir;
5105 MovDelay[x][y] = level.android_move_time * 8 + 1;
5112 else if (move_pattern == MV_TURNING_LEFT ||
5113 move_pattern == MV_TURNING_RIGHT ||
5114 move_pattern == MV_TURNING_LEFT_RIGHT ||
5115 move_pattern == MV_TURNING_RIGHT_LEFT ||
5116 move_pattern == MV_TURNING_RANDOM ||
5117 move_pattern == MV_ALL_DIRECTIONS)
5119 boolean can_turn_left =
5120 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
5121 boolean can_turn_right =
5122 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
5124 if (element_info[element].move_stepsize == 0) /* "not moving" */
5127 if (move_pattern == MV_TURNING_LEFT)
5128 MovDir[x][y] = left_dir;
5129 else if (move_pattern == MV_TURNING_RIGHT)
5130 MovDir[x][y] = right_dir;
5131 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
5132 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
5133 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
5134 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
5135 else if (move_pattern == MV_TURNING_RANDOM)
5136 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
5137 can_turn_right && !can_turn_left ? right_dir :
5138 RND(2) ? left_dir : right_dir);
5139 else if (can_turn_left && can_turn_right)
5140 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5141 else if (can_turn_left)
5142 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5143 else if (can_turn_right)
5144 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5146 MovDir[x][y] = back_dir;
5148 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5150 else if (move_pattern == MV_HORIZONTAL ||
5151 move_pattern == MV_VERTICAL)
5153 if (move_pattern & old_move_dir)
5154 MovDir[x][y] = back_dir;
5155 else if (move_pattern == MV_HORIZONTAL)
5156 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
5157 else if (move_pattern == MV_VERTICAL)
5158 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
5160 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5162 else if (move_pattern & MV_ANY_DIRECTION)
5164 MovDir[x][y] = move_pattern;
5165 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5167 else if (move_pattern & MV_WIND_DIRECTION)
5169 MovDir[x][y] = game.wind_direction;
5170 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5172 else if (move_pattern == MV_ALONG_LEFT_SIDE)
5174 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
5175 MovDir[x][y] = left_dir;
5176 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5177 MovDir[x][y] = right_dir;
5179 if (MovDir[x][y] != old_move_dir)
5180 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5182 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
5184 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
5185 MovDir[x][y] = right_dir;
5186 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5187 MovDir[x][y] = left_dir;
5189 if (MovDir[x][y] != old_move_dir)
5190 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5192 else if (move_pattern == MV_TOWARDS_PLAYER ||
5193 move_pattern == MV_AWAY_FROM_PLAYER)
5195 int attr_x = -1, attr_y = -1;
5197 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
5208 for (i = 0; i < MAX_PLAYERS; i++)
5210 struct PlayerInfo *player = &stored_player[i];
5211 int jx = player->jx, jy = player->jy;
5213 if (!player->active)
5217 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
5225 MovDir[x][y] = MV_NONE;
5227 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
5228 else if (attr_x > x)
5229 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
5231 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
5232 else if (attr_y > y)
5233 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
5235 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5237 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5239 boolean first_horiz = RND(2);
5240 int new_move_dir = MovDir[x][y];
5242 if (element_info[element].move_stepsize == 0) /* "not moving" */
5244 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
5245 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5251 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5252 Moving2Blocked(x, y, &newx, &newy);
5254 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5258 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5259 Moving2Blocked(x, y, &newx, &newy);
5261 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5264 MovDir[x][y] = old_move_dir;
5267 else if (move_pattern == MV_WHEN_PUSHED ||
5268 move_pattern == MV_WHEN_DROPPED)
5270 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5271 MovDir[x][y] = MV_NONE;
5275 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
5277 static int test_xy[7][2] =
5287 static int test_dir[7] =
5297 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
5298 int move_preference = -1000000; /* start with very low preference */
5299 int new_move_dir = MV_NONE;
5300 int start_test = RND(4);
5303 for (i = 0; i < NUM_DIRECTIONS; i++)
5305 int move_dir = test_dir[start_test + i];
5306 int move_dir_preference;
5308 xx = x + test_xy[start_test + i][0];
5309 yy = y + test_xy[start_test + i][1];
5311 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
5312 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
5314 new_move_dir = move_dir;
5319 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
5322 move_dir_preference = -1 * RunnerVisit[xx][yy];
5323 if (hunter_mode && PlayerVisit[xx][yy] > 0)
5324 move_dir_preference = PlayerVisit[xx][yy];
5326 if (move_dir_preference > move_preference)
5328 /* prefer field that has not been visited for the longest time */
5329 move_preference = move_dir_preference;
5330 new_move_dir = move_dir;
5332 else if (move_dir_preference == move_preference &&
5333 move_dir == old_move_dir)
5335 /* prefer last direction when all directions are preferred equally */
5336 move_preference = move_dir_preference;
5337 new_move_dir = move_dir;
5341 MovDir[x][y] = new_move_dir;
5342 if (old_move_dir != new_move_dir)
5343 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5347 static void TurnRound(int x, int y)
5349 int direction = MovDir[x][y];
5351 int element, graphic;
5356 GfxDir[x][y] = MovDir[x][y];
5358 if (direction != MovDir[x][y])
5362 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
5365 element = Feld[x][y];
5366 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5368 if (graphic_info[graphic].anim_global_sync)
5369 GfxFrame[x][y] = FrameCounter;
5370 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5371 GfxFrame[x][y] = CustomValue[x][y];
5372 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5373 GfxFrame[x][y] = element_info[element].collect_score;
5377 static boolean JustBeingPushed(int x, int y)
5381 for (i = 0; i < MAX_PLAYERS; i++)
5383 struct PlayerInfo *player = &stored_player[i];
5385 if (player->active && player->is_pushing && player->MovPos)
5387 int next_jx = player->jx + (player->jx - player->last_jx);
5388 int next_jy = player->jy + (player->jy - player->last_jy);
5390 if (x == next_jx && y == next_jy)
5398 void StartMoving(int x, int y)
5400 boolean started_moving = FALSE; /* some elements can fall _and_ move */
5401 int element = Feld[x][y];
5406 if (MovDelay[x][y] == 0)
5407 GfxAction[x][y] = ACTION_DEFAULT;
5409 if (CAN_FALL(element) && y < lev_fieldy - 1)
5411 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
5412 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
5413 if (JustBeingPushed(x, y))
5416 if (element == EL_QUICKSAND_FULL)
5418 if (IS_FREE(x, y + 1))
5420 InitMovingField(x, y, MV_DOWN);
5421 started_moving = TRUE;
5423 Feld[x][y] = EL_QUICKSAND_EMPTYING;
5424 Store[x][y] = EL_ROCK;
5426 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
5428 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5430 if (!MovDelay[x][y])
5431 MovDelay[x][y] = TILEY + 1;
5440 Feld[x][y] = EL_QUICKSAND_EMPTY;
5441 Feld[x][y + 1] = EL_QUICKSAND_FULL;
5442 Store[x][y + 1] = Store[x][y];
5445 PlayLevelSoundAction(x, y, ACTION_FILLING);
5448 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
5449 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5451 InitMovingField(x, y, MV_DOWN);
5452 started_moving = TRUE;
5454 Feld[x][y] = EL_QUICKSAND_FILLING;
5455 Store[x][y] = element;
5457 PlayLevelSoundAction(x, y, ACTION_FILLING);
5459 else if (element == EL_MAGIC_WALL_FULL)
5461 if (IS_FREE(x, y + 1))
5463 InitMovingField(x, y, MV_DOWN);
5464 started_moving = TRUE;
5466 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
5467 Store[x][y] = EL_CHANGED(Store[x][y]);
5469 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
5471 if (!MovDelay[x][y])
5472 MovDelay[x][y] = TILEY/4 + 1;
5481 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
5482 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
5483 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
5487 else if (element == EL_BD_MAGIC_WALL_FULL)
5489 if (IS_FREE(x, y + 1))
5491 InitMovingField(x, y, MV_DOWN);
5492 started_moving = TRUE;
5494 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
5495 Store[x][y] = EL_CHANGED2(Store[x][y]);
5497 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
5499 if (!MovDelay[x][y])
5500 MovDelay[x][y] = TILEY/4 + 1;
5509 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
5510 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
5511 Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
5515 else if (CAN_PASS_MAGIC_WALL(element) &&
5516 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
5517 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
5519 InitMovingField(x, y, MV_DOWN);
5520 started_moving = TRUE;
5523 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
5524 EL_BD_MAGIC_WALL_FILLING);
5525 Store[x][y] = element;
5527 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
5529 SplashAcid(x, y + 1);
5531 InitMovingField(x, y, MV_DOWN);
5532 started_moving = TRUE;
5534 Store[x][y] = EL_ACID;
5536 else if ((game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5537 CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
5539 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
5540 CAN_FALL(element) && WasJustFalling[x][y] &&
5541 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
5543 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
5544 CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
5545 (Feld[x][y + 1] == EL_BLOCKED)))
5547 /* this is needed for a special case not covered by calling "Impact()"
5548 from "ContinueMoving()": if an element moves to a tile directly below
5549 another element which was just falling on that tile (which was empty
5550 in the previous frame), the falling element above would just stop
5551 instead of smashing the element below (in previous version, the above
5552 element was just checked for "moving" instead of "falling", resulting
5553 in incorrect smashes caused by horizontal movement of the above
5554 element; also, the case of the player being the element to smash was
5555 simply not covered here... :-/ ) */
5557 CheckCollision[x][y] = 0;
5561 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
5563 if (MovDir[x][y] == MV_NONE)
5565 InitMovingField(x, y, MV_DOWN);
5566 started_moving = TRUE;
5569 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
5571 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
5572 MovDir[x][y] = MV_DOWN;
5574 InitMovingField(x, y, MV_DOWN);
5575 started_moving = TRUE;
5577 else if (element == EL_AMOEBA_DROP)
5579 Feld[x][y] = EL_AMOEBA_GROWING;
5580 Store[x][y] = EL_AMOEBA_WET;
5582 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
5583 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
5584 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
5585 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
5587 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
5588 (IS_FREE(x - 1, y + 1) ||
5589 Feld[x - 1][y + 1] == EL_ACID));
5590 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
5591 (IS_FREE(x + 1, y + 1) ||
5592 Feld[x + 1][y + 1] == EL_ACID));
5593 boolean can_fall_any = (can_fall_left || can_fall_right);
5594 boolean can_fall_both = (can_fall_left && can_fall_right);
5595 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
5597 #if USE_NEW_ALL_SLIPPERY
5598 if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
5600 if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
5601 can_fall_right = FALSE;
5602 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
5603 can_fall_left = FALSE;
5604 else if (slippery_type == SLIPPERY_ONLY_LEFT)
5605 can_fall_right = FALSE;
5606 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
5607 can_fall_left = FALSE;
5609 can_fall_any = (can_fall_left || can_fall_right);
5610 can_fall_both = FALSE;
5613 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
5615 if (slippery_type == SLIPPERY_ONLY_LEFT)
5616 can_fall_right = FALSE;
5617 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
5618 can_fall_left = FALSE;
5619 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
5620 can_fall_right = FALSE;
5621 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
5622 can_fall_left = FALSE;
5624 can_fall_any = (can_fall_left || can_fall_right);
5625 can_fall_both = (can_fall_left && can_fall_right);
5629 #if USE_NEW_ALL_SLIPPERY
5631 #if USE_NEW_SP_SLIPPERY
5632 /* !!! better use the same properties as for custom elements here !!! */
5633 else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
5634 can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
5636 can_fall_right = FALSE; /* slip down on left side */
5637 can_fall_both = FALSE;
5642 #if USE_NEW_ALL_SLIPPERY
5645 if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
5646 can_fall_right = FALSE; /* slip down on left side */
5648 can_fall_left = !(can_fall_right = RND(2));
5650 can_fall_both = FALSE;
5655 if (game.emulation == EMU_BOULDERDASH ||
5656 element == EL_BD_ROCK || element == EL_BD_DIAMOND)
5657 can_fall_right = FALSE; /* slip down on left side */
5659 can_fall_left = !(can_fall_right = RND(2));
5661 can_fall_both = FALSE;
5667 /* if not determined otherwise, prefer left side for slipping down */
5668 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
5669 started_moving = TRUE;
5673 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
5675 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
5678 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
5679 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
5680 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
5681 int belt_dir = game.belt_dir[belt_nr];
5683 if ((belt_dir == MV_LEFT && left_is_free) ||
5684 (belt_dir == MV_RIGHT && right_is_free))
5686 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
5688 InitMovingField(x, y, belt_dir);
5689 started_moving = TRUE;
5691 Pushed[x][y] = TRUE;
5692 Pushed[nextx][y] = TRUE;
5694 GfxAction[x][y] = ACTION_DEFAULT;
5698 MovDir[x][y] = 0; /* if element was moving, stop it */
5703 /* not "else if" because of elements that can fall and move (EL_SPRING) */
5705 if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
5707 if (CAN_MOVE(element) && !started_moving)
5710 int move_pattern = element_info[element].move_pattern;
5715 if (MovDir[x][y] == MV_NONE)
5717 printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
5718 x, y, element, element_info[element].token_name);
5719 printf("StartMoving(): This should never happen!\n");
5724 Moving2Blocked(x, y, &newx, &newy);
5726 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
5729 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5730 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5732 WasJustMoving[x][y] = 0;
5733 CheckCollision[x][y] = 0;
5735 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
5737 if (Feld[x][y] != element) /* element has changed */
5741 if (!MovDelay[x][y]) /* start new movement phase */
5743 /* all objects that can change their move direction after each step
5744 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
5746 if (element != EL_YAMYAM &&
5747 element != EL_DARK_YAMYAM &&
5748 element != EL_PACMAN &&
5749 !(move_pattern & MV_ANY_DIRECTION) &&
5750 move_pattern != MV_TURNING_LEFT &&
5751 move_pattern != MV_TURNING_RIGHT &&
5752 move_pattern != MV_TURNING_LEFT_RIGHT &&
5753 move_pattern != MV_TURNING_RIGHT_LEFT &&
5754 move_pattern != MV_TURNING_RANDOM)
5758 if (MovDelay[x][y] && (element == EL_BUG ||
5759 element == EL_SPACESHIP ||
5760 element == EL_SP_SNIKSNAK ||
5761 element == EL_SP_ELECTRON ||
5762 element == EL_MOLE))
5763 DrawLevelField(x, y);
5767 if (MovDelay[x][y]) /* wait some time before next movement */
5771 if (element == EL_ROBOT ||
5772 element == EL_YAMYAM ||
5773 element == EL_DARK_YAMYAM)
5775 DrawLevelElementAnimationIfNeeded(x, y, element);
5776 PlayLevelSoundAction(x, y, ACTION_WAITING);
5778 else if (element == EL_SP_ELECTRON)
5779 DrawLevelElementAnimationIfNeeded(x, y, element);
5780 else if (element == EL_DRAGON)
5783 int dir = MovDir[x][y];
5784 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
5785 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
5786 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
5787 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
5788 dir == MV_UP ? IMG_FLAMES_1_UP :
5789 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
5790 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5792 GfxAction[x][y] = ACTION_ATTACKING;
5794 if (IS_PLAYER(x, y))
5795 DrawPlayerField(x, y);
5797 DrawLevelField(x, y);
5799 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
5801 for (i = 1; i <= 3; i++)
5803 int xx = x + i * dx;
5804 int yy = y + i * dy;
5805 int sx = SCREENX(xx);
5806 int sy = SCREENY(yy);
5807 int flame_graphic = graphic + (i - 1);
5809 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
5814 int flamed = MovingOrBlocked2Element(xx, yy);
5818 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
5820 else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
5821 RemoveMovingField(xx, yy);
5823 RemoveField(xx, yy);
5825 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
5828 RemoveMovingField(xx, yy);
5831 ChangeDelay[xx][yy] = 0;
5833 Feld[xx][yy] = EL_FLAMES;
5835 if (IN_SCR_FIELD(sx, sy))
5837 DrawLevelFieldCrumbledSand(xx, yy);
5838 DrawGraphic(sx, sy, flame_graphic, frame);
5843 if (Feld[xx][yy] == EL_FLAMES)
5844 Feld[xx][yy] = EL_EMPTY;
5845 DrawLevelField(xx, yy);
5850 if (MovDelay[x][y]) /* element still has to wait some time */
5852 PlayLevelSoundAction(x, y, ACTION_WAITING);
5858 /* now make next step */
5860 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
5862 if (DONT_COLLIDE_WITH(element) &&
5863 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
5864 !PLAYER_ENEMY_PROTECTED(newx, newy))
5866 TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
5871 else if (CAN_MOVE_INTO_ACID(element) &&
5872 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
5873 !IS_MV_DIAGONAL(MovDir[x][y]) &&
5874 (MovDir[x][y] == MV_DOWN ||
5875 game.engine_version >= VERSION_IDENT(3,1,0,0)))
5877 SplashAcid(newx, newy);
5878 Store[x][y] = EL_ACID;
5880 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
5882 if (Feld[newx][newy] == EL_EXIT_OPEN)
5885 DrawLevelField(x, y);
5887 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
5888 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
5889 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
5891 local_player->friends_still_needed--;
5892 if (!local_player->friends_still_needed &&
5893 !local_player->GameOver && AllPlayersGone)
5894 local_player->LevelSolved = local_player->GameOver = TRUE;
5898 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
5900 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
5901 DrawLevelField(newx, newy);
5903 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
5905 else if (!IS_FREE(newx, newy))
5907 GfxAction[x][y] = ACTION_WAITING;
5909 if (IS_PLAYER(x, y))
5910 DrawPlayerField(x, y);
5912 DrawLevelField(x, y);
5917 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
5919 if (IS_FOOD_PIG(Feld[newx][newy]))
5921 if (IS_MOVING(newx, newy))
5922 RemoveMovingField(newx, newy);
5925 Feld[newx][newy] = EL_EMPTY;
5926 DrawLevelField(newx, newy);
5929 PlayLevelSound(x, y, SND_PIG_DIGGING);
5931 else if (!IS_FREE(newx, newy))
5933 if (IS_PLAYER(x, y))
5934 DrawPlayerField(x, y);
5936 DrawLevelField(x, y);
5941 else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
5943 if (Store[x][y] != EL_EMPTY)
5945 boolean can_clone = FALSE;
5948 /* check if element to clone is still there */
5949 for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
5951 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
5959 /* cannot clone or target field not free anymore -- do not clone */
5960 if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
5961 Store[x][y] = EL_EMPTY;
5964 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
5966 if (IS_MV_DIAGONAL(MovDir[x][y]))
5968 int diagonal_move_dir = MovDir[x][y];
5969 int stored = Store[x][y];
5970 int change_delay = 8;
5973 /* android is moving diagonally */
5975 CreateField(x, y, EL_DIAGONAL_SHRINKING);
5977 Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
5978 GfxElement[x][y] = EL_EMC_ANDROID;
5979 GfxAction[x][y] = ACTION_SHRINKING;
5980 GfxDir[x][y] = diagonal_move_dir;
5981 ChangeDelay[x][y] = change_delay;
5983 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
5986 DrawLevelGraphicAnimation(x, y, graphic);
5987 PlayLevelSoundAction(x, y, ACTION_SHRINKING);
5989 if (Feld[newx][newy] == EL_ACID)
5991 SplashAcid(newx, newy);
5996 CreateField(newx, newy, EL_DIAGONAL_GROWING);
5998 Store[newx][newy] = EL_EMC_ANDROID;
5999 GfxElement[newx][newy] = EL_EMC_ANDROID;
6000 GfxAction[newx][newy] = ACTION_GROWING;
6001 GfxDir[newx][newy] = diagonal_move_dir;
6002 ChangeDelay[newx][newy] = change_delay;
6004 graphic = el_act_dir2img(GfxElement[newx][newy],
6005 GfxAction[newx][newy], GfxDir[newx][newy]);
6007 DrawLevelGraphicAnimation(newx, newy, graphic);
6008 PlayLevelSoundAction(newx, newy, ACTION_GROWING);
6014 Feld[newx][newy] = EL_EMPTY;
6015 DrawLevelField(newx, newy);
6017 PlayLevelSoundAction(x, y, ACTION_DIGGING);
6020 else if (!IS_FREE(newx, newy))
6023 if (IS_PLAYER(x, y))
6024 DrawPlayerField(x, y);
6026 DrawLevelField(x, y);
6032 else if (IS_CUSTOM_ELEMENT(element) &&
6033 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6035 int new_element = Feld[newx][newy];
6037 if (!IS_FREE(newx, newy))
6039 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
6040 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
6043 /* no element can dig solid indestructible elements */
6044 if (IS_INDESTRUCTIBLE(new_element) &&
6045 !IS_DIGGABLE(new_element) &&
6046 !IS_COLLECTIBLE(new_element))
6049 if (AmoebaNr[newx][newy] &&
6050 (new_element == EL_AMOEBA_FULL ||
6051 new_element == EL_BD_AMOEBA ||
6052 new_element == EL_AMOEBA_GROWING))
6054 AmoebaCnt[AmoebaNr[newx][newy]]--;
6055 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6058 if (IS_MOVING(newx, newy))
6059 RemoveMovingField(newx, newy);
6062 RemoveField(newx, newy);
6063 DrawLevelField(newx, newy);
6066 /* if digged element was about to explode, prevent the explosion */
6067 ExplodeField[newx][newy] = EX_TYPE_NONE;
6069 PlayLevelSoundAction(x, y, action);
6072 Store[newx][newy] = EL_EMPTY;
6074 /* this makes it possible to leave the removed element again */
6075 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
6076 Store[newx][newy] = new_element;
6078 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
6080 int move_leave_element = element_info[element].move_leave_element;
6082 /* this makes it possible to leave the removed element again */
6083 Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
6084 new_element : move_leave_element);
6088 if (move_pattern & MV_MAZE_RUNNER_STYLE)
6090 RunnerVisit[x][y] = FrameCounter;
6091 PlayerVisit[x][y] /= 8; /* expire player visit path */
6094 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
6096 if (!IS_FREE(newx, newy))
6098 if (IS_PLAYER(x, y))
6099 DrawPlayerField(x, y);
6101 DrawLevelField(x, y);
6107 boolean wanna_flame = !RND(10);
6108 int dx = newx - x, dy = newy - y;
6109 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
6110 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
6111 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
6112 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
6113 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
6114 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
6117 IS_CLASSIC_ENEMY(element1) ||
6118 IS_CLASSIC_ENEMY(element2)) &&
6119 element1 != EL_DRAGON && element2 != EL_DRAGON &&
6120 element1 != EL_FLAMES && element2 != EL_FLAMES)
6122 ResetGfxAnimation(x, y);
6123 GfxAction[x][y] = ACTION_ATTACKING;
6125 if (IS_PLAYER(x, y))
6126 DrawPlayerField(x, y);
6128 DrawLevelField(x, y);
6130 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
6132 MovDelay[x][y] = 50;
6136 RemoveField(newx, newy);
6138 Feld[newx][newy] = EL_FLAMES;
6139 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
6142 RemoveField(newx1, newy1);
6144 Feld[newx1][newy1] = EL_FLAMES;
6146 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
6149 RemoveField(newx2, newy2);
6151 Feld[newx2][newy2] = EL_FLAMES;
6158 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6159 Feld[newx][newy] == EL_DIAMOND)
6161 if (IS_MOVING(newx, newy))
6162 RemoveMovingField(newx, newy);
6165 Feld[newx][newy] = EL_EMPTY;
6166 DrawLevelField(newx, newy);
6169 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
6171 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6172 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
6174 if (AmoebaNr[newx][newy])
6176 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6177 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
6178 Feld[newx][newy] == EL_BD_AMOEBA)
6179 AmoebaCnt[AmoebaNr[newx][newy]]--;
6184 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
6186 RemoveMovingField(newx, newy);
6189 if (IS_MOVING(newx, newy))
6191 RemoveMovingField(newx, newy);
6196 Feld[newx][newy] = EL_EMPTY;
6197 DrawLevelField(newx, newy);
6200 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
6202 else if ((element == EL_PACMAN || element == EL_MOLE)
6203 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
6205 if (AmoebaNr[newx][newy])
6207 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6208 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
6209 Feld[newx][newy] == EL_BD_AMOEBA)
6210 AmoebaCnt[AmoebaNr[newx][newy]]--;
6213 if (element == EL_MOLE)
6215 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
6216 PlayLevelSound(x, y, SND_MOLE_DIGGING);
6218 ResetGfxAnimation(x, y);
6219 GfxAction[x][y] = ACTION_DIGGING;
6220 DrawLevelField(x, y);
6222 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
6224 return; /* wait for shrinking amoeba */
6226 else /* element == EL_PACMAN */
6228 Feld[newx][newy] = EL_EMPTY;
6229 DrawLevelField(newx, newy);
6230 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
6233 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
6234 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
6235 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
6237 /* wait for shrinking amoeba to completely disappear */
6240 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
6242 /* object was running against a wall */
6247 /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
6248 if (move_pattern & MV_ANY_DIRECTION &&
6249 move_pattern == MovDir[x][y])
6251 int blocking_element =
6252 (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
6254 CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
6257 element = Feld[x][y]; /* element might have changed */
6261 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
6262 DrawLevelElementAnimation(x, y, element);
6264 if (DONT_TOUCH(element))
6265 TestIfBadThingTouchesPlayer(x, y);
6270 InitMovingField(x, y, MovDir[x][y]);
6272 PlayLevelSoundAction(x, y, ACTION_MOVING);
6276 ContinueMoving(x, y);
6279 void ContinueMoving(int x, int y)
6281 int element = Feld[x][y];
6282 struct ElementInfo *ei = &element_info[element];
6283 int direction = MovDir[x][y];
6284 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
6285 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
6286 int newx = x + dx, newy = y + dy;
6287 int stored = Store[x][y];
6288 int stored_new = Store[newx][newy];
6289 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
6290 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
6291 boolean last_line = (newy == lev_fieldy - 1);
6293 MovPos[x][y] += getElementMoveStepsize(x, y);
6295 if (pushed_by_player) /* special case: moving object pushed by player */
6296 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
6298 if (ABS(MovPos[x][y]) < TILEX)
6300 DrawLevelField(x, y);
6302 return; /* element is still moving */
6305 /* element reached destination field */
6307 Feld[x][y] = EL_EMPTY;
6308 Feld[newx][newy] = element;
6309 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
6311 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
6313 element = Feld[newx][newy] = EL_ACID;
6315 else if (element == EL_MOLE)
6317 Feld[x][y] = EL_SAND;
6319 DrawLevelFieldCrumbledSandNeighbours(x, y);
6321 else if (element == EL_QUICKSAND_FILLING)
6323 element = Feld[newx][newy] = get_next_element(element);
6324 Store[newx][newy] = Store[x][y];
6326 else if (element == EL_QUICKSAND_EMPTYING)
6328 Feld[x][y] = get_next_element(element);
6329 element = Feld[newx][newy] = Store[x][y];
6331 else if (element == EL_MAGIC_WALL_FILLING)
6333 element = Feld[newx][newy] = get_next_element(element);
6334 if (!game.magic_wall_active)
6335 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
6336 Store[newx][newy] = Store[x][y];
6338 else if (element == EL_MAGIC_WALL_EMPTYING)
6340 Feld[x][y] = get_next_element(element);
6341 if (!game.magic_wall_active)
6342 Feld[x][y] = EL_MAGIC_WALL_DEAD;
6343 element = Feld[newx][newy] = Store[x][y];
6345 #if USE_NEW_CUSTOM_VALUE
6346 InitField(newx, newy, FALSE);
6349 else if (element == EL_BD_MAGIC_WALL_FILLING)
6351 element = Feld[newx][newy] = get_next_element(element);
6352 if (!game.magic_wall_active)
6353 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
6354 Store[newx][newy] = Store[x][y];
6356 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
6358 Feld[x][y] = get_next_element(element);
6359 if (!game.magic_wall_active)
6360 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
6361 element = Feld[newx][newy] = Store[x][y];
6363 #if USE_NEW_CUSTOM_VALUE
6364 InitField(newx, newy, FALSE);
6367 else if (element == EL_AMOEBA_DROPPING)
6369 Feld[x][y] = get_next_element(element);
6370 element = Feld[newx][newy] = Store[x][y];
6372 else if (element == EL_SOKOBAN_OBJECT)
6375 Feld[x][y] = Back[x][y];
6377 if (Back[newx][newy])
6378 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
6380 Back[x][y] = Back[newx][newy] = 0;
6383 Store[x][y] = EL_EMPTY;
6388 MovDelay[newx][newy] = 0;
6391 if (CAN_CHANGE_OR_HAS_ACTION(element))
6393 if (CAN_CHANGE(element))
6396 /* copy element change control values to new field */
6397 ChangeDelay[newx][newy] = ChangeDelay[x][y];
6398 ChangePage[newx][newy] = ChangePage[x][y];
6399 ChangeCount[newx][newy] = ChangeCount[x][y];
6400 ChangeEvent[newx][newy] = ChangeEvent[x][y];
6403 #if USE_NEW_CUSTOM_VALUE
6404 CustomValue[newx][newy] = CustomValue[x][y];
6410 #if USE_NEW_CUSTOM_VALUE
6411 CustomValue[newx][newy] = CustomValue[x][y];
6415 ChangeDelay[x][y] = 0;
6416 ChangePage[x][y] = -1;
6417 ChangeCount[x][y] = 0;
6418 ChangeEvent[x][y] = -1;
6420 #if USE_NEW_CUSTOM_VALUE
6421 CustomValue[x][y] = 0;
6424 /* copy animation control values to new field */
6425 GfxFrame[newx][newy] = GfxFrame[x][y];
6426 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
6427 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
6428 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
6430 Pushed[x][y] = Pushed[newx][newy] = FALSE;
6432 /* some elements can leave other elements behind after moving */
6434 if (ei->move_leave_element != EL_EMPTY &&
6435 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
6436 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
6438 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
6439 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
6440 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
6443 int move_leave_element = ei->move_leave_element;
6447 /* this makes it possible to leave the removed element again */
6448 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
6449 move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
6451 /* this makes it possible to leave the removed element again */
6452 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
6453 move_leave_element = stored;
6456 /* this makes it possible to leave the removed element again */
6457 if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
6458 ei->move_leave_element == EL_TRIGGER_ELEMENT)
6459 move_leave_element = stored;
6462 Feld[x][y] = move_leave_element;
6464 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
6465 MovDir[x][y] = direction;
6467 InitField(x, y, FALSE);
6469 if (GFX_CRUMBLED(Feld[x][y]))
6470 DrawLevelFieldCrumbledSandNeighbours(x, y);
6472 if (ELEM_IS_PLAYER(move_leave_element))
6473 RelocatePlayer(x, y, move_leave_element);
6476 /* do this after checking for left-behind element */
6477 ResetGfxAnimation(x, y); /* reset animation values for old field */
6479 if (!CAN_MOVE(element) ||
6480 (CAN_FALL(element) && direction == MV_DOWN &&
6481 (element == EL_SPRING ||
6482 element_info[element].move_pattern == MV_WHEN_PUSHED ||
6483 element_info[element].move_pattern == MV_WHEN_DROPPED)))
6484 GfxDir[x][y] = MovDir[newx][newy] = 0;
6486 DrawLevelField(x, y);
6487 DrawLevelField(newx, newy);
6489 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
6491 /* prevent pushed element from moving on in pushed direction */
6492 if (pushed_by_player && CAN_MOVE(element) &&
6493 element_info[element].move_pattern & MV_ANY_DIRECTION &&
6494 !(element_info[element].move_pattern & direction))
6495 TurnRound(newx, newy);
6497 /* prevent elements on conveyor belt from moving on in last direction */
6498 if (pushed_by_conveyor && CAN_FALL(element) &&
6499 direction & MV_HORIZONTAL)
6500 MovDir[newx][newy] = 0;
6502 if (!pushed_by_player)
6504 int nextx = newx + dx, nexty = newy + dy;
6505 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
6507 WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
6509 if (CAN_FALL(element) && direction == MV_DOWN)
6510 WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
6512 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
6513 CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
6516 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
6518 TestIfBadThingTouchesPlayer(newx, newy);
6519 TestIfBadThingTouchesFriend(newx, newy);
6521 if (!IS_CUSTOM_ELEMENT(element))
6522 TestIfBadThingTouchesOtherBadThing(newx, newy);
6524 else if (element == EL_PENGUIN)
6525 TestIfFriendTouchesBadThing(newx, newy);
6527 /* give the player one last chance (one more frame) to move away */
6528 if (CAN_FALL(element) && direction == MV_DOWN &&
6529 (last_line || (!IS_FREE(x, newy + 1) &&
6530 (!IS_PLAYER(x, newy + 1) ||
6531 game.engine_version < VERSION_IDENT(3,1,1,0)))))
6534 if (pushed_by_player && !game.use_change_when_pushing_bug)
6536 int push_side = MV_DIR_OPPOSITE(direction);
6537 struct PlayerInfo *player = PLAYERINFO(x, y);
6539 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
6540 player->index_bit, push_side);
6541 CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
6542 player->index_bit, push_side);
6545 if (element == EL_EMC_ANDROID && pushed_by_player) /* make another move */
6546 MovDelay[newx][newy] = 1;
6548 CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
6550 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
6553 if (ChangePage[newx][newy] != -1) /* delayed change */
6555 int page = ChangePage[newx][newy];
6556 struct ElementChangeInfo *change = &ei->change_page[page];
6558 ChangePage[newx][newy] = -1;
6560 if (change->can_change)
6562 if (ChangeElement(newx, newy, element, page))
6564 if (change->post_change_function)
6565 change->post_change_function(newx, newy);
6569 if (change->has_action)
6570 ExecuteCustomElementAction(newx, newy, element, page);
6574 TestIfElementHitsCustomElement(newx, newy, direction);
6575 TestIfPlayerTouchesCustomElement(newx, newy);
6576 TestIfElementTouchesCustomElement(newx, newy);
6579 if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
6580 IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
6581 CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
6582 MV_DIR_OPPOSITE(direction));
6586 int AmoebeNachbarNr(int ax, int ay)
6589 int element = Feld[ax][ay];
6591 static int xy[4][2] =
6599 for (i = 0; i < NUM_DIRECTIONS; i++)
6601 int x = ax + xy[i][0];
6602 int y = ay + xy[i][1];
6604 if (!IN_LEV_FIELD(x, y))
6607 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
6608 group_nr = AmoebaNr[x][y];
6614 void AmoebenVereinigen(int ax, int ay)
6616 int i, x, y, xx, yy;
6617 int new_group_nr = AmoebaNr[ax][ay];
6618 static int xy[4][2] =
6626 if (new_group_nr == 0)
6629 for (i = 0; i < NUM_DIRECTIONS; i++)
6634 if (!IN_LEV_FIELD(x, y))
6637 if ((Feld[x][y] == EL_AMOEBA_FULL ||
6638 Feld[x][y] == EL_BD_AMOEBA ||
6639 Feld[x][y] == EL_AMOEBA_DEAD) &&
6640 AmoebaNr[x][y] != new_group_nr)
6642 int old_group_nr = AmoebaNr[x][y];
6644 if (old_group_nr == 0)
6647 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
6648 AmoebaCnt[old_group_nr] = 0;
6649 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
6650 AmoebaCnt2[old_group_nr] = 0;
6653 SCAN_PLAYFIELD(xx, yy)
6655 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
6658 if (AmoebaNr[xx][yy] == old_group_nr)
6659 AmoebaNr[xx][yy] = new_group_nr;
6665 void AmoebeUmwandeln(int ax, int ay)
6669 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
6671 int group_nr = AmoebaNr[ax][ay];
6676 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
6677 printf("AmoebeUmwandeln(): This should never happen!\n");
6683 SCAN_PLAYFIELD(x, y)
6685 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
6688 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
6691 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
6695 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
6696 SND_AMOEBA_TURNING_TO_GEM :
6697 SND_AMOEBA_TURNING_TO_ROCK));
6702 static int xy[4][2] =
6710 for (i = 0; i < NUM_DIRECTIONS; i++)
6715 if (!IN_LEV_FIELD(x, y))
6718 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
6720 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
6721 SND_AMOEBA_TURNING_TO_GEM :
6722 SND_AMOEBA_TURNING_TO_ROCK));
6729 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
6732 int group_nr = AmoebaNr[ax][ay];
6733 boolean done = FALSE;
6738 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
6739 printf("AmoebeUmwandelnBD(): This should never happen!\n");
6745 SCAN_PLAYFIELD(x, y)
6747 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
6750 if (AmoebaNr[x][y] == group_nr &&
6751 (Feld[x][y] == EL_AMOEBA_DEAD ||
6752 Feld[x][y] == EL_BD_AMOEBA ||
6753 Feld[x][y] == EL_AMOEBA_GROWING))
6756 Feld[x][y] = new_element;
6757 InitField(x, y, FALSE);
6758 DrawLevelField(x, y);
6764 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
6765 SND_BD_AMOEBA_TURNING_TO_ROCK :
6766 SND_BD_AMOEBA_TURNING_TO_GEM));
6769 void AmoebeWaechst(int x, int y)
6771 static unsigned long sound_delay = 0;
6772 static unsigned long sound_delay_value = 0;
6774 if (!MovDelay[x][y]) /* start new growing cycle */
6778 if (DelayReached(&sound_delay, sound_delay_value))
6780 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
6781 sound_delay_value = 30;
6785 if (MovDelay[x][y]) /* wait some time before growing bigger */
6788 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6790 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
6791 6 - MovDelay[x][y]);
6793 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
6796 if (!MovDelay[x][y])
6798 Feld[x][y] = Store[x][y];
6800 DrawLevelField(x, y);
6805 void AmoebaDisappearing(int x, int y)
6807 static unsigned long sound_delay = 0;
6808 static unsigned long sound_delay_value = 0;
6810 if (!MovDelay[x][y]) /* start new shrinking cycle */
6814 if (DelayReached(&sound_delay, sound_delay_value))
6815 sound_delay_value = 30;
6818 if (MovDelay[x][y]) /* wait some time before shrinking */
6821 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6823 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
6824 6 - MovDelay[x][y]);
6826 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
6829 if (!MovDelay[x][y])
6831 Feld[x][y] = EL_EMPTY;
6832 DrawLevelField(x, y);
6834 /* don't let mole enter this field in this cycle;
6835 (give priority to objects falling to this field from above) */
6841 void AmoebeAbleger(int ax, int ay)
6844 int element = Feld[ax][ay];
6845 int graphic = el2img(element);
6846 int newax = ax, neway = ay;
6847 boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
6848 static int xy[4][2] =
6856 if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
6858 Feld[ax][ay] = EL_AMOEBA_DEAD;
6859 DrawLevelField(ax, ay);
6863 if (IS_ANIMATED(graphic))
6864 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6866 if (!MovDelay[ax][ay]) /* start making new amoeba field */
6867 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
6869 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
6872 if (MovDelay[ax][ay])
6876 if (can_drop) /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
6879 int x = ax + xy[start][0];
6880 int y = ay + xy[start][1];
6882 if (!IN_LEV_FIELD(x, y))
6885 if (IS_FREE(x, y) ||
6886 CAN_GROW_INTO(Feld[x][y]) ||
6887 Feld[x][y] == EL_QUICKSAND_EMPTY)
6893 if (newax == ax && neway == ay)
6896 else /* normal or "filled" (BD style) amoeba */
6899 boolean waiting_for_player = FALSE;
6901 for (i = 0; i < NUM_DIRECTIONS; i++)
6903 int j = (start + i) % 4;
6904 int x = ax + xy[j][0];
6905 int y = ay + xy[j][1];
6907 if (!IN_LEV_FIELD(x, y))
6910 if (IS_FREE(x, y) ||
6911 CAN_GROW_INTO(Feld[x][y]) ||
6912 Feld[x][y] == EL_QUICKSAND_EMPTY)
6918 else if (IS_PLAYER(x, y))
6919 waiting_for_player = TRUE;
6922 if (newax == ax && neway == ay) /* amoeba cannot grow */
6924 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
6926 Feld[ax][ay] = EL_AMOEBA_DEAD;
6927 DrawLevelField(ax, ay);
6928 AmoebaCnt[AmoebaNr[ax][ay]]--;
6930 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
6932 if (element == EL_AMOEBA_FULL)
6933 AmoebeUmwandeln(ax, ay);
6934 else if (element == EL_BD_AMOEBA)
6935 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
6940 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
6942 /* amoeba gets larger by growing in some direction */
6944 int new_group_nr = AmoebaNr[ax][ay];
6947 if (new_group_nr == 0)
6949 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
6950 printf("AmoebeAbleger(): This should never happen!\n");
6955 AmoebaNr[newax][neway] = new_group_nr;
6956 AmoebaCnt[new_group_nr]++;
6957 AmoebaCnt2[new_group_nr]++;
6959 /* if amoeba touches other amoeba(s) after growing, unify them */
6960 AmoebenVereinigen(newax, neway);
6962 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
6964 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
6970 if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
6971 (neway == lev_fieldy - 1 && newax != ax))
6973 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
6974 Store[newax][neway] = element;
6976 else if (neway == ay || element == EL_EMC_DRIPPER)
6978 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
6980 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
6984 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
6985 Feld[ax][ay] = EL_AMOEBA_DROPPING;
6986 Store[ax][ay] = EL_AMOEBA_DROP;
6987 ContinueMoving(ax, ay);
6991 DrawLevelField(newax, neway);
6994 void Life(int ax, int ay)
6998 static int life[4] = { 2, 3, 3, 3 }; /* parameters for "game of life" */
7001 int element = Feld[ax][ay];
7002 int graphic = el2img(element);
7003 int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
7005 boolean changed = FALSE;
7007 if (IS_ANIMATED(graphic))
7008 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7013 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
7014 MovDelay[ax][ay] = life_time;
7016 if (MovDelay[ax][ay]) /* wait some time before next cycle */
7019 if (MovDelay[ax][ay])
7023 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
7025 int xx = ax+x1, yy = ay+y1;
7028 if (!IN_LEV_FIELD(xx, yy))
7031 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
7033 int x = xx+x2, y = yy+y2;
7035 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
7038 if (((Feld[x][y] == element ||
7039 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
7041 (IS_FREE(x, y) && Stop[x][y]))
7045 if (xx == ax && yy == ay) /* field in the middle */
7047 if (nachbarn < life_parameter[0] ||
7048 nachbarn > life_parameter[1])
7050 Feld[xx][yy] = EL_EMPTY;
7052 DrawLevelField(xx, yy);
7053 Stop[xx][yy] = TRUE;
7057 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
7058 { /* free border field */
7059 if (nachbarn >= life_parameter[2] &&
7060 nachbarn <= life_parameter[3])
7062 Feld[xx][yy] = element;
7063 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
7065 DrawLevelField(xx, yy);
7066 Stop[xx][yy] = TRUE;
7073 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
7074 SND_GAME_OF_LIFE_GROWING);
7077 static void InitRobotWheel(int x, int y)
7079 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
7082 static void RunRobotWheel(int x, int y)
7084 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
7087 static void StopRobotWheel(int x, int y)
7089 if (ZX == x && ZY == y)
7093 static void InitTimegateWheel(int x, int y)
7095 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
7098 static void RunTimegateWheel(int x, int y)
7100 PlayLevelSound(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
7103 static void InitMagicBallDelay(int x, int y)
7106 ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
7108 ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
7112 static void ActivateMagicBall(int bx, int by)
7116 if (level.ball_random)
7118 int pos_border = RND(8); /* select one of the eight border elements */
7119 int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
7120 int xx = pos_content % 3;
7121 int yy = pos_content / 3;
7126 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
7127 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
7131 for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
7133 int xx = x - bx + 1;
7134 int yy = y - by + 1;
7136 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
7137 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
7141 game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
7144 static void InitDiagonalMovingElement(int x, int y)
7147 MovDelay[x][y] = level.android_move_time;
7151 void CheckExit(int x, int y)
7153 if (local_player->gems_still_needed > 0 ||
7154 local_player->sokobanfields_still_needed > 0 ||
7155 local_player->lights_still_needed > 0)
7157 int element = Feld[x][y];
7158 int graphic = el2img(element);
7160 if (IS_ANIMATED(graphic))
7161 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7166 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7169 Feld[x][y] = EL_EXIT_OPENING;
7171 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
7174 void CheckExitSP(int x, int y)
7176 if (local_player->gems_still_needed > 0)
7178 int element = Feld[x][y];
7179 int graphic = el2img(element);
7181 if (IS_ANIMATED(graphic))
7182 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7187 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7190 Feld[x][y] = EL_SP_EXIT_OPENING;
7192 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
7195 static void CloseAllOpenTimegates()
7200 SCAN_PLAYFIELD(x, y)
7202 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7205 int element = Feld[x][y];
7207 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
7209 Feld[x][y] = EL_TIMEGATE_CLOSING;
7211 PlayLevelSoundAction(x, y, ACTION_CLOSING);
7216 void EdelsteinFunkeln(int x, int y)
7218 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
7221 if (Feld[x][y] == EL_BD_DIAMOND)
7224 if (MovDelay[x][y] == 0) /* next animation frame */
7225 MovDelay[x][y] = 11 * !SimpleRND(500);
7227 if (MovDelay[x][y] != 0) /* wait some time before next frame */
7231 if (setup.direct_draw && MovDelay[x][y])
7232 SetDrawtoField(DRAW_BUFFERED);
7234 DrawLevelElementAnimation(x, y, Feld[x][y]);
7236 if (MovDelay[x][y] != 0)
7238 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
7239 10 - MovDelay[x][y]);
7241 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
7243 if (setup.direct_draw)
7247 dest_x = FX + SCREENX(x) * TILEX;
7248 dest_y = FY + SCREENY(y) * TILEY;
7250 BlitBitmap(drawto_field, window,
7251 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
7252 SetDrawtoField(DRAW_DIRECT);
7258 void MauerWaechst(int x, int y)
7262 if (!MovDelay[x][y]) /* next animation frame */
7263 MovDelay[x][y] = 3 * delay;
7265 if (MovDelay[x][y]) /* wait some time before next frame */
7269 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7271 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
7272 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
7274 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
7277 if (!MovDelay[x][y])
7279 if (MovDir[x][y] == MV_LEFT)
7281 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
7282 DrawLevelField(x - 1, y);
7284 else if (MovDir[x][y] == MV_RIGHT)
7286 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
7287 DrawLevelField(x + 1, y);
7289 else if (MovDir[x][y] == MV_UP)
7291 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
7292 DrawLevelField(x, y - 1);
7296 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
7297 DrawLevelField(x, y + 1);
7300 Feld[x][y] = Store[x][y];
7302 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7303 DrawLevelField(x, y);
7308 void MauerAbleger(int ax, int ay)
7310 int element = Feld[ax][ay];
7311 int graphic = el2img(element);
7312 boolean oben_frei = FALSE, unten_frei = FALSE;
7313 boolean links_frei = FALSE, rechts_frei = FALSE;
7314 boolean oben_massiv = FALSE, unten_massiv = FALSE;
7315 boolean links_massiv = FALSE, rechts_massiv = FALSE;
7316 boolean new_wall = FALSE;
7318 if (IS_ANIMATED(graphic))
7319 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7321 if (!MovDelay[ax][ay]) /* start building new wall */
7322 MovDelay[ax][ay] = 6;
7324 if (MovDelay[ax][ay]) /* wait some time before building new wall */
7327 if (MovDelay[ax][ay])
7331 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
7333 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
7335 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
7337 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
7340 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
7341 element == EL_EXPANDABLE_WALL_ANY)
7345 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
7346 Store[ax][ay-1] = element;
7347 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
7348 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
7349 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
7350 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
7355 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
7356 Store[ax][ay+1] = element;
7357 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
7358 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
7359 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
7360 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
7365 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7366 element == EL_EXPANDABLE_WALL_ANY ||
7367 element == EL_EXPANDABLE_WALL)
7371 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
7372 Store[ax-1][ay] = element;
7373 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
7374 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
7375 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
7376 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
7382 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
7383 Store[ax+1][ay] = element;
7384 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
7385 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
7386 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
7387 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
7392 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
7393 DrawLevelField(ax, ay);
7395 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
7397 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
7398 unten_massiv = TRUE;
7399 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
7400 links_massiv = TRUE;
7401 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
7402 rechts_massiv = TRUE;
7404 if (((oben_massiv && unten_massiv) ||
7405 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7406 element == EL_EXPANDABLE_WALL) &&
7407 ((links_massiv && rechts_massiv) ||
7408 element == EL_EXPANDABLE_WALL_VERTICAL))
7409 Feld[ax][ay] = EL_WALL;
7412 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
7415 void CheckForDragon(int x, int y)
7418 boolean dragon_found = FALSE;
7419 static int xy[4][2] =
7427 for (i = 0; i < NUM_DIRECTIONS; i++)
7429 for (j = 0; j < 4; j++)
7431 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7433 if (IN_LEV_FIELD(xx, yy) &&
7434 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
7436 if (Feld[xx][yy] == EL_DRAGON)
7437 dragon_found = TRUE;
7446 for (i = 0; i < NUM_DIRECTIONS; i++)
7448 for (j = 0; j < 3; j++)
7450 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7452 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
7454 Feld[xx][yy] = EL_EMPTY;
7455 DrawLevelField(xx, yy);
7464 static void InitBuggyBase(int x, int y)
7466 int element = Feld[x][y];
7467 int activating_delay = FRAMES_PER_SECOND / 4;
7470 (element == EL_SP_BUGGY_BASE ?
7471 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
7472 element == EL_SP_BUGGY_BASE_ACTIVATING ?
7474 element == EL_SP_BUGGY_BASE_ACTIVE ?
7475 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
7478 static void WarnBuggyBase(int x, int y)
7481 static int xy[4][2] =
7489 for (i = 0; i < NUM_DIRECTIONS; i++)
7491 int xx = x + xy[i][0];
7492 int yy = y + xy[i][1];
7494 if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
7496 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
7503 static void InitTrap(int x, int y)
7505 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
7508 static void ActivateTrap(int x, int y)
7510 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
7513 static void ChangeActiveTrap(int x, int y)
7515 int graphic = IMG_TRAP_ACTIVE;
7517 /* if new animation frame was drawn, correct crumbled sand border */
7518 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
7519 DrawLevelFieldCrumbledSand(x, y);
7522 static int getSpecialActionElement(int element, int number, int base_element)
7524 return (element != EL_EMPTY ? element :
7525 number != -1 ? base_element + number - 1 :
7529 static int getModifiedActionNumber(int value_old, int operator, int operand,
7530 int value_min, int value_max)
7532 int value_new = (operator == CA_MODE_SET ? operand :
7533 operator == CA_MODE_ADD ? value_old + operand :
7534 operator == CA_MODE_SUBTRACT ? value_old - operand :
7535 operator == CA_MODE_MULTIPLY ? value_old * operand :
7536 operator == CA_MODE_DIVIDE ? value_old / MAX(1, operand) :
7537 operator == CA_MODE_MODULO ? value_old % MAX(1, operand) :
7540 return (value_new < value_min ? value_min :
7541 value_new > value_max ? value_max :
7545 static void ExecuteCustomElementAction(int x, int y, int element, int page)
7547 struct ElementInfo *ei = &element_info[element];
7548 struct ElementChangeInfo *change = &ei->change_page[page];
7549 int action_type = change->action_type;
7550 int action_mode = change->action_mode;
7551 int action_arg = change->action_arg;
7554 if (!change->has_action)
7557 /* ---------- determine action paramater values -------------------------- */
7559 int level_time_value =
7560 (level.time > 0 ? TimeLeft :
7563 int action_arg_element =
7564 (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player :
7565 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
7566 action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element :
7569 int action_arg_direction =
7570 (action_arg >= CA_ARG_DIRECTION_LEFT &&
7571 action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
7572 action_arg == CA_ARG_DIRECTION_TRIGGER ?
7573 change->actual_trigger_side :
7574 action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
7575 MV_DIR_OPPOSITE(change->actual_trigger_side) :
7578 int action_arg_number_min =
7579 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
7582 int action_arg_number_max =
7583 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
7584 action_type == CA_SET_LEVEL_GEMS ? 999 :
7585 action_type == CA_SET_LEVEL_TIME ? 9999 :
7586 action_type == CA_SET_LEVEL_SCORE ? 99999 :
7587 action_type == CA_SET_CE_SCORE ? 9999 :
7588 action_type == CA_SET_CE_VALUE ? 9999 :
7591 int action_arg_number_reset =
7592 (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize :
7593 action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
7594 action_type == CA_SET_LEVEL_TIME ? level.time :
7595 action_type == CA_SET_LEVEL_SCORE ? 0 :
7596 action_type == CA_SET_CE_SCORE ? 0 :
7598 action_type == CA_SET_CE_VALUE ? GET_NEW_CUSTOM_VALUE(element) :
7600 action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
7604 int action_arg_number =
7605 (action_arg <= CA_ARG_MAX ? action_arg :
7606 action_arg >= CA_ARG_SPEED_NOT_MOVING &&
7607 action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
7608 action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
7609 action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
7610 action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
7611 action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
7612 action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
7613 #if USE_NEW_CUSTOM_VALUE
7614 action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
7616 action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
7618 action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
7619 action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
7620 action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
7621 action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
7622 action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CUSTOM_VALUE(change->target_element) :
7623 action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
7624 action_arg == CA_ARG_ELEMENT_NR_TARGET ? change->target_element :
7625 action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
7628 int action_arg_number_old =
7629 (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
7630 action_type == CA_SET_LEVEL_TIME ? TimeLeft :
7631 action_type == CA_SET_LEVEL_SCORE ? local_player->score :
7632 action_type == CA_SET_CE_SCORE ? ei->collect_score :
7633 action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
7636 int action_arg_number_new =
7637 getModifiedActionNumber(action_arg_number_old,
7638 action_mode, action_arg_number,
7639 action_arg_number_min, action_arg_number_max);
7641 int trigger_player_bits =
7642 (change->actual_trigger_player >= EL_PLAYER_1 &&
7643 change->actual_trigger_player <= EL_PLAYER_4 ?
7644 (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
7647 int action_arg_player_bits =
7648 (action_arg >= CA_ARG_PLAYER_1 &&
7649 action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
7650 action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
7653 /* ---------- execute action -------------------------------------------- */
7662 /* ---------- level actions ------------------------------------------- */
7664 case CA_RESTART_LEVEL:
7666 game.restart_level = TRUE;
7671 case CA_SHOW_ENVELOPE:
7673 int element = getSpecialActionElement(action_arg_element,
7674 action_arg_number, EL_ENVELOPE_1);
7676 if (IS_ENVELOPE(element))
7677 local_player->show_envelope = element;
7682 case CA_SET_LEVEL_TIME:
7684 if (level.time > 0) /* only modify limited time value */
7686 TimeLeft = action_arg_number_new;
7688 DrawGameValue_Time(TimeLeft);
7690 if (!TimeLeft && setup.time_limit)
7691 for (i = 0; i < MAX_PLAYERS; i++)
7692 KillPlayer(&stored_player[i]);
7698 case CA_SET_LEVEL_SCORE:
7700 local_player->score = action_arg_number_new;
7702 DrawGameValue_Score(local_player->score);
7707 case CA_SET_LEVEL_GEMS:
7709 local_player->gems_still_needed = action_arg_number_new;
7711 DrawGameValue_Emeralds(local_player->gems_still_needed);
7716 case CA_SET_LEVEL_GRAVITY:
7718 game.gravity = (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
7719 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
7720 action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
7725 case CA_SET_LEVEL_WIND:
7727 game.wind_direction = action_arg_direction;
7732 /* ---------- player actions ------------------------------------------ */
7734 case CA_MOVE_PLAYER:
7736 /* automatically move to the next field in specified direction */
7737 for (i = 0; i < MAX_PLAYERS; i++)
7738 if (trigger_player_bits & (1 << i))
7739 stored_player[i].programmed_action = action_arg_direction;
7744 case CA_EXIT_PLAYER:
7746 for (i = 0; i < MAX_PLAYERS; i++)
7747 if (action_arg_player_bits & (1 << i))
7748 stored_player[i].LevelSolved = stored_player[i].GameOver = TRUE;
7753 case CA_KILL_PLAYER:
7755 for (i = 0; i < MAX_PLAYERS; i++)
7756 if (action_arg_player_bits & (1 << i))
7757 KillPlayer(&stored_player[i]);
7762 case CA_SET_PLAYER_KEYS:
7764 int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
7765 int element = getSpecialActionElement(action_arg_element,
7766 action_arg_number, EL_KEY_1);
7768 if (IS_KEY(element))
7770 for (i = 0; i < MAX_PLAYERS; i++)
7772 if (trigger_player_bits & (1 << i))
7774 stored_player[i].key[KEY_NR(element)] = key_state;
7776 DrawGameValue_Keys(stored_player[i].key);
7778 redraw_mask |= REDRAW_DOOR_1;
7786 case CA_SET_PLAYER_SPEED:
7788 for (i = 0; i < MAX_PLAYERS; i++)
7790 if (trigger_player_bits & (1 << i))
7792 int move_stepsize = TILEX / stored_player[i].move_delay_value;
7794 if (action_arg == CA_ARG_SPEED_FASTER &&
7795 stored_player[i].cannot_move)
7797 action_arg_number = STEPSIZE_VERY_SLOW;
7799 else if (action_arg == CA_ARG_SPEED_SLOWER ||
7800 action_arg == CA_ARG_SPEED_FASTER)
7802 action_arg_number = 2;
7803 action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
7808 getModifiedActionNumber(move_stepsize,
7811 action_arg_number_min,
7812 action_arg_number_max);
7815 SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
7817 /* make sure that value is power of 2 */
7818 move_stepsize = (1 << log_2(move_stepsize));
7820 /* do no immediately change -- the player might just be moving */
7821 stored_player[i].move_delay_value_next = TILEX / move_stepsize;
7823 stored_player[i].cannot_move =
7824 (action_arg == CA_ARG_SPEED_NOT_MOVING ? TRUE : FALSE);
7832 case CA_SET_PLAYER_SHIELD:
7834 for (i = 0; i < MAX_PLAYERS; i++)
7836 if (trigger_player_bits & (1 << i))
7838 if (action_arg == CA_ARG_SHIELD_OFF)
7840 stored_player[i].shield_normal_time_left = 0;
7841 stored_player[i].shield_deadly_time_left = 0;
7843 else if (action_arg == CA_ARG_SHIELD_NORMAL)
7845 stored_player[i].shield_normal_time_left = 999999;
7847 else if (action_arg == CA_ARG_SHIELD_DEADLY)
7849 stored_player[i].shield_normal_time_left = 999999;
7850 stored_player[i].shield_deadly_time_left = 999999;
7858 case CA_SET_PLAYER_ARTWORK:
7860 for (i = 0; i < MAX_PLAYERS; i++)
7862 if (trigger_player_bits & (1 << i))
7864 int artwork_element = action_arg_element;
7866 if (action_arg == CA_ARG_ELEMENT_RESET)
7868 (level.use_artwork_element[i] ? level.artwork_element[i] :
7869 stored_player[i].element_nr);
7871 stored_player[i].artwork_element = artwork_element;
7873 SetPlayerWaiting(&stored_player[i], FALSE);
7875 /* set number of special actions for bored and sleeping animation */
7876 stored_player[i].num_special_action_bored =
7877 get_num_special_action(artwork_element,
7878 ACTION_BORING_1, ACTION_BORING_LAST);
7879 stored_player[i].num_special_action_sleeping =
7880 get_num_special_action(artwork_element,
7881 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
7888 /* ---------- CE actions ---------------------------------------------- */
7890 case CA_SET_CE_SCORE:
7892 ei->collect_score = action_arg_number_new;
7897 case CA_SET_CE_VALUE:
7899 #if USE_NEW_CUSTOM_VALUE
7900 int last_custom_value = CustomValue[x][y];
7902 CustomValue[x][y] = action_arg_number_new;
7905 printf("::: Count == %d\n", CustomValue[x][y]);
7908 if (CustomValue[x][y] == 0 && last_custom_value > 0)
7911 printf("::: CE_VALUE_GETS_ZERO\n");
7914 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
7915 CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
7918 printf("::: RESULT: %d, %d\n", Feld[x][y], ChangePage[x][y]);
7926 /* ---------- engine actions ------------------------------------------ */
7928 case CA_SET_ENGINE_SCAN_MODE:
7930 InitPlayfieldScanMode(action_arg);
7940 static void CreateFieldExt(int x, int y, int element, boolean is_change)
7942 int previous_move_direction = MovDir[x][y];
7943 #if USE_NEW_CUSTOM_VALUE
7944 int last_ce_value = CustomValue[x][y];
7946 boolean add_player = (ELEM_IS_PLAYER(element) &&
7947 IS_WALKABLE(Feld[x][y]));
7949 /* check if element under player changes from accessible to unaccessible
7950 (needed for special case of dropping element which then changes) */
7951 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
7952 IS_ACCESSIBLE(Feld[x][y]) && !IS_ACCESSIBLE(element))
7961 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
7962 RemoveMovingField(x, y);
7966 Feld[x][y] = element;
7968 ResetGfxAnimation(x, y);
7969 ResetRandomAnimationValue(x, y);
7971 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
7972 MovDir[x][y] = previous_move_direction;
7974 #if USE_NEW_CUSTOM_VALUE
7975 if (element_info[Feld[x][y]].use_last_ce_value)
7976 CustomValue[x][y] = last_ce_value;
7979 InitField_WithBug1(x, y, FALSE);
7981 DrawLevelField(x, y);
7983 if (GFX_CRUMBLED(Feld[x][y]))
7984 DrawLevelFieldCrumbledSandNeighbours(x, y);
7987 /* "ChangeCount" not set yet to allow "entered by player" change one time */
7988 if (ELEM_IS_PLAYER(element))
7989 RelocatePlayer(x, y, element);
7992 ChangeCount[x][y]++; /* count number of changes in the same frame */
7994 TestIfBadThingTouchesPlayer(x, y);
7995 TestIfPlayerTouchesCustomElement(x, y);
7996 TestIfElementTouchesCustomElement(x, y);
7999 static void CreateField(int x, int y, int element)
8001 CreateFieldExt(x, y, element, FALSE);
8004 static void CreateElementFromChange(int x, int y, int element)
8006 element = GET_VALID_RUNTIME_ELEMENT(element);
8008 #if USE_STOP_CHANGED_ELEMENTS
8009 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
8011 int old_element = Feld[x][y];
8013 /* prevent changed element from moving in same engine frame
8014 unless both old and new element can either fall or move */
8015 if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
8016 (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
8021 CreateFieldExt(x, y, element, TRUE);
8024 static boolean ChangeElement(int x, int y, int element, int page)
8026 struct ElementChangeInfo *change = &element_info[element].change_page[page];
8028 int old_element = Feld[x][y];
8030 /* always use default change event to prevent running into a loop */
8031 if (ChangeEvent[x][y] == -1)
8032 ChangeEvent[x][y] = CE_DELAY;
8034 if (ChangeEvent[x][y] == CE_DELAY)
8036 /* reset actual trigger element, trigger player and action element */
8037 change->actual_trigger_element = EL_EMPTY;
8038 change->actual_trigger_player = EL_PLAYER_1;
8039 change->actual_trigger_side = CH_SIDE_NONE;
8040 change->actual_trigger_ce_value = 0;
8043 /* do not change elements more than a specified maximum number of changes */
8044 if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
8047 ChangeCount[x][y]++; /* count number of changes in the same frame */
8049 if (change->explode)
8056 if (change->use_target_content)
8058 boolean complete_replace = TRUE;
8059 boolean can_replace[3][3];
8062 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
8065 boolean is_walkable;
8066 boolean is_diggable;
8067 boolean is_collectible;
8068 boolean is_removable;
8069 boolean is_destructible;
8070 int ex = x + xx - 1;
8071 int ey = y + yy - 1;
8072 int content_element = change->target_content.e[xx][yy];
8075 can_replace[xx][yy] = TRUE;
8077 if (ex == x && ey == y) /* do not check changing element itself */
8080 if (content_element == EL_EMPTY_SPACE)
8082 can_replace[xx][yy] = FALSE; /* do not replace border with space */
8087 if (!IN_LEV_FIELD(ex, ey))
8089 can_replace[xx][yy] = FALSE;
8090 complete_replace = FALSE;
8097 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
8098 e = MovingOrBlocked2Element(ex, ey);
8100 is_empty = (IS_FREE(ex, ey) ||
8101 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
8103 is_walkable = (is_empty || IS_WALKABLE(e));
8104 is_diggable = (is_empty || IS_DIGGABLE(e));
8105 is_collectible = (is_empty || IS_COLLECTIBLE(e));
8106 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
8107 is_removable = (is_diggable || is_collectible);
8109 can_replace[xx][yy] =
8110 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
8111 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
8112 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
8113 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
8114 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
8115 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
8116 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
8118 if (!can_replace[xx][yy])
8119 complete_replace = FALSE;
8122 if (!change->only_if_complete || complete_replace)
8124 boolean something_has_changed = FALSE;
8126 if (change->only_if_complete && change->use_random_replace &&
8127 RND(100) < change->random_percentage)
8130 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
8132 int ex = x + xx - 1;
8133 int ey = y + yy - 1;
8134 int content_element;
8136 if (can_replace[xx][yy] && (!change->use_random_replace ||
8137 RND(100) < change->random_percentage))
8139 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
8140 RemoveMovingField(ex, ey);
8142 ChangeEvent[ex][ey] = ChangeEvent[x][y];
8144 content_element = change->target_content.e[xx][yy];
8145 target_element = GET_TARGET_ELEMENT(content_element, change);
8147 CreateElementFromChange(ex, ey, target_element);
8149 something_has_changed = TRUE;
8151 /* for symmetry reasons, freeze newly created border elements */
8152 if (ex != x || ey != y)
8153 Stop[ex][ey] = TRUE; /* no more moving in this frame */
8157 if (something_has_changed)
8159 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
8160 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
8166 target_element = GET_TARGET_ELEMENT(change->target_element, change);
8168 if (element == EL_DIAGONAL_GROWING ||
8169 element == EL_DIAGONAL_SHRINKING)
8171 target_element = Store[x][y];
8173 Store[x][y] = EL_EMPTY;
8176 CreateElementFromChange(x, y, target_element);
8178 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
8179 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
8182 /* this uses direct change before indirect change */
8183 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
8188 #if USE_NEW_DELAYED_ACTION
8190 static void HandleElementChange(int x, int y, int page)
8192 int element = MovingOrBlocked2Element(x, y);
8193 struct ElementInfo *ei = &element_info[element];
8194 struct ElementChangeInfo *change = &ei->change_page[page];
8197 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
8198 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
8201 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
8202 x, y, element, element_info[element].token_name);
8203 printf("HandleElementChange(): This should never happen!\n");
8208 /* this can happen with classic bombs on walkable, changing elements */
8209 if (!CAN_CHANGE_OR_HAS_ACTION(element))
8212 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
8213 ChangeDelay[x][y] = 0;
8219 if (ChangeDelay[x][y] == 0) /* initialize element change */
8221 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
8223 if (change->can_change)
8225 ResetGfxAnimation(x, y);
8226 ResetRandomAnimationValue(x, y);
8228 if (change->pre_change_function)
8229 change->pre_change_function(x, y);
8233 ChangeDelay[x][y]--;
8235 if (ChangeDelay[x][y] != 0) /* continue element change */
8237 if (change->can_change)
8239 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8241 if (IS_ANIMATED(graphic))
8242 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8244 if (change->change_function)
8245 change->change_function(x, y);
8248 else /* finish element change */
8250 if (ChangePage[x][y] != -1) /* remember page from delayed change */
8252 page = ChangePage[x][y];
8253 ChangePage[x][y] = -1;
8255 change = &ei->change_page[page];
8258 if (IS_MOVING(x, y)) /* never change a running system ;-) */
8260 ChangeDelay[x][y] = 1; /* try change after next move step */
8261 ChangePage[x][y] = page; /* remember page to use for change */
8266 if (change->can_change)
8268 if (ChangeElement(x, y, element, page))
8270 if (change->post_change_function)
8271 change->post_change_function(x, y);
8275 if (change->has_action)
8276 ExecuteCustomElementAction(x, y, element, page);
8282 static void HandleElementChange(int x, int y, int page)
8284 int element = MovingOrBlocked2Element(x, y);
8285 struct ElementInfo *ei = &element_info[element];
8286 struct ElementChangeInfo *change = &ei->change_page[page];
8289 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
8292 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
8293 x, y, element, element_info[element].token_name);
8294 printf("HandleElementChange(): This should never happen!\n");
8299 /* this can happen with classic bombs on walkable, changing elements */
8300 if (!CAN_CHANGE(element))
8303 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
8304 ChangeDelay[x][y] = 0;
8310 if (ChangeDelay[x][y] == 0) /* initialize element change */
8312 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
8314 ResetGfxAnimation(x, y);
8315 ResetRandomAnimationValue(x, y);
8317 if (change->pre_change_function)
8318 change->pre_change_function(x, y);
8321 ChangeDelay[x][y]--;
8323 if (ChangeDelay[x][y] != 0) /* continue element change */
8325 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8327 if (IS_ANIMATED(graphic))
8328 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8330 if (change->change_function)
8331 change->change_function(x, y);
8333 else /* finish element change */
8335 if (ChangePage[x][y] != -1) /* remember page from delayed change */
8337 page = ChangePage[x][y];
8338 ChangePage[x][y] = -1;
8340 change = &ei->change_page[page];
8343 if (IS_MOVING(x, y)) /* never change a running system ;-) */
8345 ChangeDelay[x][y] = 1; /* try change after next move step */
8346 ChangePage[x][y] = page; /* remember page to use for change */
8351 if (ChangeElement(x, y, element, page))
8353 if (change->post_change_function)
8354 change->post_change_function(x, y);
8361 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
8362 int trigger_element,
8368 boolean change_done_any = FALSE;
8369 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
8372 if (!(trigger_events[trigger_element][trigger_event]))
8375 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8377 int element = EL_CUSTOM_START + i;
8378 boolean change_done = FALSE;
8381 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
8382 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
8385 for (p = 0; p < element_info[element].num_change_pages; p++)
8387 struct ElementChangeInfo *change = &element_info[element].change_page[p];
8389 if (change->can_change_or_has_action &&
8390 change->has_event[trigger_event] &&
8391 change->trigger_side & trigger_side &&
8392 change->trigger_player & trigger_player &&
8393 change->trigger_page & trigger_page_bits &&
8394 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
8396 change->actual_trigger_element = trigger_element;
8397 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
8398 change->actual_trigger_side = trigger_side;
8399 change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
8401 if ((change->can_change && !change_done) || change->has_action)
8406 SCAN_PLAYFIELD(x, y)
8408 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8411 if (Feld[x][y] == element)
8413 if (change->can_change && !change_done)
8415 ChangeDelay[x][y] = 1;
8416 ChangeEvent[x][y] = trigger_event;
8418 HandleElementChange(x, y, p);
8420 #if USE_NEW_DELAYED_ACTION
8421 else if (change->has_action)
8423 ExecuteCustomElementAction(x, y, element, p);
8424 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8427 if (change->has_action)
8429 ExecuteCustomElementAction(x, y, element, p);
8430 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8436 if (change->can_change)
8439 change_done_any = TRUE;
8446 return change_done_any;
8449 static boolean CheckElementChangeExt(int x, int y,
8451 int trigger_element,
8456 boolean change_done = FALSE;
8459 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
8460 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
8463 if (Feld[x][y] == EL_BLOCKED)
8465 Blocked2Moving(x, y, &x, &y);
8466 element = Feld[x][y];
8470 /* check if element has already changed */
8471 if (Feld[x][y] != element)
8474 /* check if element has already changed or is about to change after moving */
8475 if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
8476 Feld[x][y] != element) ||
8478 (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
8479 (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
8480 ChangePage[x][y] != -1)))
8484 for (p = 0; p < element_info[element].num_change_pages; p++)
8486 struct ElementChangeInfo *change = &element_info[element].change_page[p];
8488 boolean check_trigger_element =
8489 (trigger_event == CE_TOUCHING_X ||
8490 trigger_event == CE_HITTING_X ||
8491 trigger_event == CE_HIT_BY_X);
8493 if (change->can_change_or_has_action &&
8494 change->has_event[trigger_event] &&
8495 change->trigger_side & trigger_side &&
8496 change->trigger_player & trigger_player &&
8497 (!check_trigger_element ||
8498 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
8500 change->actual_trigger_element = trigger_element;
8501 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
8502 change->actual_trigger_side = trigger_side;
8503 change->actual_trigger_ce_value = CustomValue[x][y];
8505 /* special case: trigger element not at (x,y) position for some events */
8506 if (check_trigger_element)
8518 { 0, 0 }, { 0, 0 }, { 0, 0 },
8522 int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
8523 int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
8525 change->actual_trigger_ce_value = CustomValue[xx][yy];
8528 if (change->can_change && !change_done)
8530 ChangeDelay[x][y] = 1;
8531 ChangeEvent[x][y] = trigger_event;
8533 HandleElementChange(x, y, p);
8537 #if USE_NEW_DELAYED_ACTION
8538 else if (change->has_action)
8540 ExecuteCustomElementAction(x, y, element, p);
8541 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8544 if (change->has_action)
8546 ExecuteCustomElementAction(x, y, element, p);
8547 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8556 static void PlayPlayerSound(struct PlayerInfo *player)
8558 int jx = player->jx, jy = player->jy;
8559 int sound_element = player->artwork_element;
8560 int last_action = player->last_action_waiting;
8561 int action = player->action_waiting;
8563 if (player->is_waiting)
8565 if (action != last_action)
8566 PlayLevelSoundElementAction(jx, jy, sound_element, action);
8568 PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
8572 if (action != last_action)
8573 StopSound(element_info[sound_element].sound[last_action]);
8575 if (last_action == ACTION_SLEEPING)
8576 PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
8580 static void PlayAllPlayersSound()
8584 for (i = 0; i < MAX_PLAYERS; i++)
8585 if (stored_player[i].active)
8586 PlayPlayerSound(&stored_player[i]);
8589 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
8591 boolean last_waiting = player->is_waiting;
8592 int move_dir = player->MovDir;
8594 player->last_action_waiting = player->action_waiting;
8598 if (!last_waiting) /* not waiting -> waiting */
8600 player->is_waiting = TRUE;
8602 player->frame_counter_bored =
8604 game.player_boring_delay_fixed +
8605 SimpleRND(game.player_boring_delay_random);
8606 player->frame_counter_sleeping =
8608 game.player_sleeping_delay_fixed +
8609 SimpleRND(game.player_sleeping_delay_random);
8611 InitPlayerGfxAnimation(player, ACTION_WAITING, player->MovDir);
8614 if (game.player_sleeping_delay_fixed +
8615 game.player_sleeping_delay_random > 0 &&
8616 player->anim_delay_counter == 0 &&
8617 player->post_delay_counter == 0 &&
8618 FrameCounter >= player->frame_counter_sleeping)
8619 player->is_sleeping = TRUE;
8620 else if (game.player_boring_delay_fixed +
8621 game.player_boring_delay_random > 0 &&
8622 FrameCounter >= player->frame_counter_bored)
8623 player->is_bored = TRUE;
8625 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
8626 player->is_bored ? ACTION_BORING :
8629 if (player->is_sleeping)
8631 if (player->num_special_action_sleeping > 0)
8633 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
8635 int last_special_action = player->special_action_sleeping;
8636 int num_special_action = player->num_special_action_sleeping;
8637 int special_action =
8638 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
8639 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
8640 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
8641 last_special_action + 1 : ACTION_SLEEPING);
8642 int special_graphic =
8643 el_act_dir2img(player->artwork_element, special_action, move_dir);
8645 player->anim_delay_counter =
8646 graphic_info[special_graphic].anim_delay_fixed +
8647 SimpleRND(graphic_info[special_graphic].anim_delay_random);
8648 player->post_delay_counter =
8649 graphic_info[special_graphic].post_delay_fixed +
8650 SimpleRND(graphic_info[special_graphic].post_delay_random);
8652 player->special_action_sleeping = special_action;
8655 if (player->anim_delay_counter > 0)
8657 player->action_waiting = player->special_action_sleeping;
8658 player->anim_delay_counter--;
8660 else if (player->post_delay_counter > 0)
8662 player->post_delay_counter--;
8666 else if (player->is_bored)
8668 if (player->num_special_action_bored > 0)
8670 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
8672 int special_action =
8673 ACTION_BORING_1 + SimpleRND(player->num_special_action_bored);
8674 int special_graphic =
8675 el_act_dir2img(player->artwork_element, special_action, move_dir);
8677 player->anim_delay_counter =
8678 graphic_info[special_graphic].anim_delay_fixed +
8679 SimpleRND(graphic_info[special_graphic].anim_delay_random);
8680 player->post_delay_counter =
8681 graphic_info[special_graphic].post_delay_fixed +
8682 SimpleRND(graphic_info[special_graphic].post_delay_random);
8684 player->special_action_bored = special_action;
8687 if (player->anim_delay_counter > 0)
8689 player->action_waiting = player->special_action_bored;
8690 player->anim_delay_counter--;
8692 else if (player->post_delay_counter > 0)
8694 player->post_delay_counter--;
8699 else if (last_waiting) /* waiting -> not waiting */
8701 player->is_waiting = FALSE;
8702 player->is_bored = FALSE;
8703 player->is_sleeping = FALSE;
8705 player->frame_counter_bored = -1;
8706 player->frame_counter_sleeping = -1;
8708 player->anim_delay_counter = 0;
8709 player->post_delay_counter = 0;
8711 player->action_waiting = ACTION_DEFAULT;
8713 player->special_action_bored = ACTION_DEFAULT;
8714 player->special_action_sleeping = ACTION_DEFAULT;
8718 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
8720 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
8721 int left = player_action & JOY_LEFT;
8722 int right = player_action & JOY_RIGHT;
8723 int up = player_action & JOY_UP;
8724 int down = player_action & JOY_DOWN;
8725 int button1 = player_action & JOY_BUTTON_1;
8726 int button2 = player_action & JOY_BUTTON_2;
8727 int dx = (left ? -1 : right ? 1 : 0);
8728 int dy = (up ? -1 : down ? 1 : 0);
8730 if (!player->active || tape.pausing)
8736 snapped = SnapField(player, dx, dy);
8740 dropped = DropElement(player);
8742 moved = MovePlayer(player, dx, dy);
8745 if (tape.single_step && tape.recording && !tape.pausing)
8747 if (button1 || (dropped && !moved))
8749 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8750 SnapField(player, 0, 0); /* stop snapping */
8754 SetPlayerWaiting(player, FALSE);
8756 return player_action;
8760 /* no actions for this player (no input at player's configured device) */
8762 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
8763 SnapField(player, 0, 0);
8764 CheckGravityMovementWhenNotMoving(player);
8766 if (player->MovPos == 0)
8767 SetPlayerWaiting(player, TRUE);
8769 if (player->MovPos == 0) /* needed for tape.playing */
8770 player->is_moving = FALSE;
8772 player->is_dropping = FALSE;
8773 player->is_dropping_pressed = FALSE;
8774 player->drop_pressed_delay = 0;
8780 void AdvanceFrameAndPlayerCounters(int player_nr)
8784 /* advance frame counters (global frame counter and time frame counter) */
8788 /* advance player counters (counters for move delay, move animation etc.) */
8789 for (i = 0; i < MAX_PLAYERS; i++)
8791 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
8792 int move_delay_value = stored_player[i].move_delay_value;
8793 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
8795 if (!advance_player_counters) /* not all players may be affected */
8798 #if USE_NEW_PLAYER_ANIM
8799 if (move_frames == 0) /* less than one move per game frame */
8801 int stepsize = TILEX / move_delay_value;
8802 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
8803 int count = (stored_player[i].is_moving ?
8804 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
8806 if (count % delay == 0)
8811 stored_player[i].Frame += move_frames;
8813 if (stored_player[i].MovPos != 0)
8814 stored_player[i].StepFrame += move_frames;
8816 if (stored_player[i].move_delay > 0)
8817 stored_player[i].move_delay--;
8819 /* due to bugs in previous versions, counter must count up, not down */
8820 if (stored_player[i].push_delay != -1)
8821 stored_player[i].push_delay++;
8823 if (stored_player[i].drop_delay > 0)
8824 stored_player[i].drop_delay--;
8826 if (stored_player[i].is_dropping_pressed)
8827 stored_player[i].drop_pressed_delay++;
8833 static unsigned long game_frame_delay = 0;
8834 unsigned long game_frame_delay_value;
8835 int magic_wall_x = 0, magic_wall_y = 0;
8836 int i, x, y, element, graphic;
8837 byte *recorded_player_action;
8838 byte summarized_player_action = 0;
8839 byte tape_action[MAX_PLAYERS];
8841 if (game_status != GAME_MODE_PLAYING)
8844 game_frame_delay_value =
8845 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
8847 if (tape.playing && tape.warp_forward && !tape.pausing)
8848 game_frame_delay_value = 0;
8850 /* ---------- main game synchronization point ---------- */
8852 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
8854 InitPlayfieldScanModeVars();
8856 if (ScreenMovPos == 0) /* screen currently aligned at tile position */
8858 struct PlayerInfo *player;
8859 int player_nr = game.centered_player_nr_next;
8861 if (game.centered_player_nr_next == -1)
8862 player_nr = local_player->index_nr;
8864 player = &stored_player[player_nr];
8866 if (!player->active)
8867 game.centered_player_nr_next = game.centered_player_nr;
8869 if (game.centered_player_nr != game.centered_player_nr_next)
8871 DrawRelocatePlayer(player, setup.quick_switch);
8873 game.centered_player_nr = game.centered_player_nr_next;
8877 #if USE_ONE_MORE_CHANGE_PER_FRAME
8878 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
8880 SCAN_PLAYFIELD(x, y)
8882 ChangeCount[x][y] = 0;
8883 ChangeEvent[x][y] = -1;
8888 if (network_playing && !network_player_action_received)
8890 /* try to get network player actions in time */
8892 #if defined(NETWORK_AVALIABLE)
8893 /* last chance to get network player actions without main loop delay */
8897 /* game was quit by network peer */
8898 if (game_status != GAME_MODE_PLAYING)
8901 if (!network_player_action_received)
8902 return; /* failed to get network player actions in time */
8908 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
8911 /* !!! CHECK THIS (tape.pausing is always FALSE here!) !!! */
8912 if (recorded_player_action == NULL && tape.pausing)
8916 for (i = 0; i < MAX_PLAYERS; i++)
8918 summarized_player_action |= stored_player[i].action;
8920 if (!network_playing)
8921 stored_player[i].effective_action = stored_player[i].action;
8924 #if defined(NETWORK_AVALIABLE)
8925 if (network_playing)
8926 SendToServer_MovePlayer(summarized_player_action);
8929 if (!options.network && !setup.team_mode)
8930 local_player->effective_action = summarized_player_action;
8932 if (recorded_player_action != NULL)
8933 for (i = 0; i < MAX_PLAYERS; i++)
8934 stored_player[i].effective_action = recorded_player_action[i];
8936 for (i = 0; i < MAX_PLAYERS; i++)
8938 tape_action[i] = stored_player[i].effective_action;
8940 if (tape.recording && tape_action[i] && !tape.player_participates[i])
8941 tape.player_participates[i] = TRUE; /* player just appeared from CE */
8944 /* only save actions from input devices, but not programmed actions */
8946 TapeRecordAction(tape_action);
8948 for (i = 0; i < MAX_PLAYERS; i++)
8950 int actual_player_action = stored_player[i].effective_action;
8953 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
8954 - rnd_equinox_tetrachloride 048
8955 - rnd_equinox_tetrachloride_ii 096
8956 - rnd_emanuel_schmieg 002
8957 - doctor_sloan_ww 001, 020
8959 if (stored_player[i].MovPos == 0)
8960 CheckGravityMovement(&stored_player[i]);
8963 /* overwrite programmed action with tape action */
8964 if (stored_player[i].programmed_action)
8965 actual_player_action = stored_player[i].programmed_action;
8968 PlayerActions(&stored_player[i], actual_player_action);
8970 tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
8972 if (tape.recording && tape_action[i] && !tape.player_participates[i])
8973 tape.player_participates[i] = TRUE; /* player just appeared from CE */
8976 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
8979 network_player_action_received = FALSE;
8981 ScrollScreen(NULL, SCROLL_GO_ON);
8983 /* for backwards compatibility, the following code emulates a fixed bug that
8984 occured when pushing elements (causing elements that just made their last
8985 pushing step to already (if possible) make their first falling step in the
8986 same game frame, which is bad); this code is also needed to use the famous
8987 "spring push bug" which is used in older levels and might be wanted to be
8988 used also in newer levels, but in this case the buggy pushing code is only
8989 affecting the "spring" element and no other elements */
8991 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
8993 for (i = 0; i < MAX_PLAYERS; i++)
8995 struct PlayerInfo *player = &stored_player[i];
8999 if (player->active && player->is_pushing && player->is_moving &&
9001 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
9002 Feld[x][y] == EL_SPRING))
9004 ContinueMoving(x, y);
9006 /* continue moving after pushing (this is actually a bug) */
9007 if (!IS_MOVING(x, y))
9016 SCAN_PLAYFIELD(x, y)
9018 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9021 ChangeCount[x][y] = 0;
9022 ChangeEvent[x][y] = -1;
9024 /* this must be handled before main playfield loop */
9025 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
9028 if (MovDelay[x][y] <= 0)
9032 #if USE_NEW_SNAP_DELAY
9033 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
9036 if (MovDelay[x][y] <= 0)
9039 DrawLevelField(x, y);
9041 TestIfElementTouchesCustomElement(x, y); /* for empty space */
9047 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
9049 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
9050 printf("GameActions(): This should never happen!\n");
9052 ChangePage[x][y] = -1;
9057 if (WasJustMoving[x][y] > 0)
9058 WasJustMoving[x][y]--;
9059 if (WasJustFalling[x][y] > 0)
9060 WasJustFalling[x][y]--;
9061 if (CheckCollision[x][y] > 0)
9062 CheckCollision[x][y]--;
9066 /* reset finished pushing action (not done in ContinueMoving() to allow
9067 continuous pushing animation for elements with zero push delay) */
9068 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
9070 ResetGfxAnimation(x, y);
9071 DrawLevelField(x, y);
9075 if (IS_BLOCKED(x, y))
9079 Blocked2Moving(x, y, &oldx, &oldy);
9080 if (!IS_MOVING(oldx, oldy))
9082 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
9083 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
9084 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
9085 printf("GameActions(): This should never happen!\n");
9092 SCAN_PLAYFIELD(x, y)
9094 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9097 element = Feld[x][y];
9098 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9101 printf("::: %d,%d\n", x, y);
9103 if (element == EL_ROCK)
9104 printf("::: Yo man! Rocks can fall!\n");
9107 if (graphic_info[graphic].anim_global_sync)
9108 GfxFrame[x][y] = FrameCounter;
9109 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
9111 int old_gfx_frame = GfxFrame[x][y];
9113 GfxFrame[x][y] = CustomValue[x][y];
9116 if (GfxFrame[x][y] != old_gfx_frame)
9118 DrawLevelGraphicAnimation(x, y, graphic);
9120 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
9122 int old_gfx_frame = GfxFrame[x][y];
9124 GfxFrame[x][y] = element_info[element].collect_score;
9127 if (GfxFrame[x][y] != old_gfx_frame)
9129 DrawLevelGraphicAnimation(x, y, graphic);
9132 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
9133 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
9134 ResetRandomAnimationValue(x, y);
9136 SetRandomAnimationValue(x, y);
9138 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
9140 if (IS_INACTIVE(element))
9142 if (IS_ANIMATED(graphic))
9143 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9148 /* this may take place after moving, so 'element' may have changed */
9149 if (IS_CHANGING(x, y) &&
9150 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
9152 int page = element_info[element].event_page_nr[CE_DELAY];
9154 HandleElementChange(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] : page);
9158 printf("::: ChangeDelay == %d\n", ChangeDelay[x][y]);
9162 if (element == EL_CUSTOM_255)
9163 printf("::: ChangeDelay == %d\n", ChangeDelay[x][y]);
9167 HandleElementChange(x, y, page);
9169 if (CAN_CHANGE(element))
9170 HandleElementChange(x, y, page);
9172 if (HAS_ACTION(element))
9173 ExecuteCustomElementAction(x, y, element, page);
9178 element = Feld[x][y];
9179 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9182 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
9186 element = Feld[x][y];
9187 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9189 if (IS_ANIMATED(graphic) &&
9192 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9194 if (IS_GEM(element) || element == EL_SP_INFOTRON)
9195 EdelsteinFunkeln(x, y);
9197 else if ((element == EL_ACID ||
9198 element == EL_EXIT_OPEN ||
9199 element == EL_SP_EXIT_OPEN ||
9200 element == EL_SP_TERMINAL ||
9201 element == EL_SP_TERMINAL_ACTIVE ||
9202 element == EL_EXTRA_TIME ||
9203 element == EL_SHIELD_NORMAL ||
9204 element == EL_SHIELD_DEADLY) &&
9205 IS_ANIMATED(graphic))
9206 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9207 else if (IS_MOVING(x, y))
9208 ContinueMoving(x, y);
9209 else if (IS_ACTIVE_BOMB(element))
9210 CheckDynamite(x, y);
9211 else if (element == EL_AMOEBA_GROWING)
9212 AmoebeWaechst(x, y);
9213 else if (element == EL_AMOEBA_SHRINKING)
9214 AmoebaDisappearing(x, y);
9216 #if !USE_NEW_AMOEBA_CODE
9217 else if (IS_AMOEBALIVE(element))
9218 AmoebeAbleger(x, y);
9221 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
9223 else if (element == EL_EXIT_CLOSED)
9225 else if (element == EL_SP_EXIT_CLOSED)
9227 else if (element == EL_EXPANDABLE_WALL_GROWING)
9229 else if (element == EL_EXPANDABLE_WALL ||
9230 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9231 element == EL_EXPANDABLE_WALL_VERTICAL ||
9232 element == EL_EXPANDABLE_WALL_ANY)
9234 else if (element == EL_FLAMES)
9235 CheckForDragon(x, y);
9236 else if (element == EL_EXPLOSION)
9237 ; /* drawing of correct explosion animation is handled separately */
9238 else if (element == EL_ELEMENT_SNAPPING ||
9239 element == EL_DIAGONAL_SHRINKING ||
9240 element == EL_DIAGONAL_GROWING)
9243 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
9245 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9248 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
9249 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9252 if (element == EL_CUSTOM_255 ||
9253 element == EL_CUSTOM_256)
9254 DrawLevelGraphicAnimation(x, y, graphic);
9257 if (IS_BELT_ACTIVE(element))
9258 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
9260 if (game.magic_wall_active)
9262 int jx = local_player->jx, jy = local_player->jy;
9264 /* play the element sound at the position nearest to the player */
9265 if ((element == EL_MAGIC_WALL_FULL ||
9266 element == EL_MAGIC_WALL_ACTIVE ||
9267 element == EL_MAGIC_WALL_EMPTYING ||
9268 element == EL_BD_MAGIC_WALL_FULL ||
9269 element == EL_BD_MAGIC_WALL_ACTIVE ||
9270 element == EL_BD_MAGIC_WALL_EMPTYING) &&
9271 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
9279 #if USE_NEW_AMOEBA_CODE
9280 /* new experimental amoeba growth stuff */
9281 if (!(FrameCounter % 8))
9283 static unsigned long random = 1684108901;
9285 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
9287 x = RND(lev_fieldx);
9288 y = RND(lev_fieldy);
9289 element = Feld[x][y];
9291 if (!IS_PLAYER(x,y) &&
9292 (element == EL_EMPTY ||
9293 CAN_GROW_INTO(element) ||
9294 element == EL_QUICKSAND_EMPTY ||
9295 element == EL_ACID_SPLASH_LEFT ||
9296 element == EL_ACID_SPLASH_RIGHT))
9298 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
9299 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
9300 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
9301 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
9302 Feld[x][y] = EL_AMOEBA_DROP;
9305 random = random * 129 + 1;
9311 if (game.explosions_delayed)
9314 game.explosions_delayed = FALSE;
9317 SCAN_PLAYFIELD(x, y)
9319 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9322 element = Feld[x][y];
9324 if (ExplodeField[x][y])
9325 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
9326 else if (element == EL_EXPLOSION)
9327 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
9329 ExplodeField[x][y] = EX_TYPE_NONE;
9332 game.explosions_delayed = TRUE;
9335 if (game.magic_wall_active)
9337 if (!(game.magic_wall_time_left % 4))
9339 int element = Feld[magic_wall_x][magic_wall_y];
9341 if (element == EL_BD_MAGIC_WALL_FULL ||
9342 element == EL_BD_MAGIC_WALL_ACTIVE ||
9343 element == EL_BD_MAGIC_WALL_EMPTYING)
9344 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
9346 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
9349 if (game.magic_wall_time_left > 0)
9351 game.magic_wall_time_left--;
9352 if (!game.magic_wall_time_left)
9355 SCAN_PLAYFIELD(x, y)
9357 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9360 element = Feld[x][y];
9362 if (element == EL_MAGIC_WALL_ACTIVE ||
9363 element == EL_MAGIC_WALL_FULL)
9365 Feld[x][y] = EL_MAGIC_WALL_DEAD;
9366 DrawLevelField(x, y);
9368 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
9369 element == EL_BD_MAGIC_WALL_FULL)
9371 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
9372 DrawLevelField(x, y);
9376 game.magic_wall_active = FALSE;
9381 if (game.light_time_left > 0)
9383 game.light_time_left--;
9385 if (game.light_time_left == 0)
9386 RedrawAllLightSwitchesAndInvisibleElements();
9389 if (game.timegate_time_left > 0)
9391 game.timegate_time_left--;
9393 if (game.timegate_time_left == 0)
9394 CloseAllOpenTimegates();
9397 if (game.lenses_time_left > 0)
9399 game.lenses_time_left--;
9401 if (game.lenses_time_left == 0)
9402 RedrawAllInvisibleElementsForLenses();
9405 if (game.magnify_time_left > 0)
9407 game.magnify_time_left--;
9409 if (game.magnify_time_left == 0)
9410 RedrawAllInvisibleElementsForMagnifier();
9413 for (i = 0; i < MAX_PLAYERS; i++)
9415 struct PlayerInfo *player = &stored_player[i];
9417 if (SHIELD_ON(player))
9419 if (player->shield_deadly_time_left)
9420 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
9421 else if (player->shield_normal_time_left)
9422 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
9426 if (TimeFrames >= FRAMES_PER_SECOND)
9431 for (i = 0; i < MAX_PLAYERS; i++)
9433 struct PlayerInfo *player = &stored_player[i];
9435 if (SHIELD_ON(player))
9437 player->shield_normal_time_left--;
9439 if (player->shield_deadly_time_left > 0)
9440 player->shield_deadly_time_left--;
9444 if (!level.use_step_counter)
9452 if (TimeLeft <= 10 && setup.time_limit)
9453 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
9455 DrawGameValue_Time(TimeLeft);
9457 if (!TimeLeft && setup.time_limit)
9458 for (i = 0; i < MAX_PLAYERS; i++)
9459 KillPlayer(&stored_player[i]);
9461 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
9462 DrawGameValue_Time(TimePlayed);
9465 if (tape.recording || tape.playing)
9466 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
9470 PlayAllPlayersSound();
9472 if (options.debug) /* calculate frames per second */
9474 static unsigned long fps_counter = 0;
9475 static int fps_frames = 0;
9476 unsigned long fps_delay_ms = Counter() - fps_counter;
9480 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
9482 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
9485 fps_counter = Counter();
9488 redraw_mask |= REDRAW_FPS;
9491 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
9493 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
9495 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
9497 local_player->show_envelope = 0;
9500 /* use random number generator in every frame to make it less predictable */
9501 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
9505 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
9507 int min_x = x, min_y = y, max_x = x, max_y = y;
9510 for (i = 0; i < MAX_PLAYERS; i++)
9512 int jx = stored_player[i].jx, jy = stored_player[i].jy;
9514 if (!stored_player[i].active || &stored_player[i] == player)
9517 min_x = MIN(min_x, jx);
9518 min_y = MIN(min_y, jy);
9519 max_x = MAX(max_x, jx);
9520 max_y = MAX(max_y, jy);
9523 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
9526 static boolean AllPlayersInVisibleScreen()
9530 for (i = 0; i < MAX_PLAYERS; i++)
9532 int jx = stored_player[i].jx, jy = stored_player[i].jy;
9534 if (!stored_player[i].active)
9537 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
9544 void ScrollLevel(int dx, int dy)
9546 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
9549 BlitBitmap(drawto_field, drawto_field,
9550 FX + TILEX * (dx == -1) - softscroll_offset,
9551 FY + TILEY * (dy == -1) - softscroll_offset,
9552 SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
9553 SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
9554 FX + TILEX * (dx == 1) - softscroll_offset,
9555 FY + TILEY * (dy == 1) - softscroll_offset);
9559 x = (dx == 1 ? BX1 : BX2);
9560 for (y = BY1; y <= BY2; y++)
9561 DrawScreenField(x, y);
9566 y = (dy == 1 ? BY1 : BY2);
9567 for (x = BX1; x <= BX2; x++)
9568 DrawScreenField(x, y);
9571 redraw_mask |= REDRAW_FIELD;
9574 static boolean canFallDown(struct PlayerInfo *player)
9576 int jx = player->jx, jy = player->jy;
9578 return (IN_LEV_FIELD(jx, jy + 1) &&
9579 (IS_FREE(jx, jy + 1) ||
9580 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
9581 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
9582 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
9585 static boolean canPassField(int x, int y, int move_dir)
9587 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9588 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9589 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
9592 int element = Feld[x][y];
9594 return (IS_PASSABLE_FROM(element, opposite_dir) &&
9595 !CAN_MOVE(element) &&
9596 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
9597 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
9598 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
9601 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
9603 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9604 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9605 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
9609 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9610 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
9611 (IS_DIGGABLE(Feld[newx][newy]) ||
9612 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9613 canPassField(newx, newy, move_dir)));
9616 static void CheckGravityMovement(struct PlayerInfo *player)
9618 if (game.gravity && !player->programmed_action)
9620 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
9621 int move_dir_vertical = player->effective_action & MV_VERTICAL;
9622 boolean player_is_snapping = player->effective_action & JOY_BUTTON_1;
9623 int jx = player->jx, jy = player->jy;
9624 boolean player_is_moving_to_valid_field =
9625 (!player_is_snapping &&
9626 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
9627 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
9628 boolean player_can_fall_down = canFallDown(player);
9630 if (player_can_fall_down &&
9631 !player_is_moving_to_valid_field)
9632 player->programmed_action = MV_DOWN;
9636 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
9638 return CheckGravityMovement(player);
9640 if (game.gravity && !player->programmed_action)
9642 int jx = player->jx, jy = player->jy;
9643 boolean field_under_player_is_free =
9644 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
9645 boolean player_is_standing_on_valid_field =
9646 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
9647 (IS_WALKABLE(Feld[jx][jy]) &&
9648 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
9650 if (field_under_player_is_free && !player_is_standing_on_valid_field)
9651 player->programmed_action = MV_DOWN;
9657 -----------------------------------------------------------------------------
9658 dx, dy: direction (non-diagonal) to try to move the player to
9659 real_dx, real_dy: direction as read from input device (can be diagonal)
9662 boolean MovePlayerOneStep(struct PlayerInfo *player,
9663 int dx, int dy, int real_dx, int real_dy)
9665 int jx = player->jx, jy = player->jy;
9666 int new_jx = jx + dx, new_jy = jy + dy;
9667 #if !USE_FIXED_DONT_RUN_INTO
9671 boolean player_can_move = !player->cannot_move;
9673 if (!player->active || (!dx && !dy))
9674 return MP_NO_ACTION;
9676 player->MovDir = (dx < 0 ? MV_LEFT :
9679 dy > 0 ? MV_DOWN : MV_NONE);
9681 if (!IN_LEV_FIELD(new_jx, new_jy))
9682 return MP_NO_ACTION;
9684 if (!player_can_move)
9687 if (player->MovPos == 0)
9689 player->is_moving = FALSE;
9690 player->is_digging = FALSE;
9691 player->is_collecting = FALSE;
9692 player->is_snapping = FALSE;
9693 player->is_pushing = FALSE;
9696 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
9697 SnapField(player, 0, 0);
9701 return MP_NO_ACTION;
9706 if (!options.network && game.centered_player_nr == -1 &&
9707 !AllPlayersInSight(player, new_jx, new_jy))
9708 return MP_NO_ACTION;
9710 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
9711 return MP_NO_ACTION;
9714 #if !USE_FIXED_DONT_RUN_INTO
9715 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
9717 /* (moved to DigField()) */
9718 if (player_can_move && DONT_RUN_INTO(element))
9720 if (element == EL_ACID && dx == 0 && dy == 1)
9722 SplashAcid(new_jx, new_jy);
9723 Feld[jx][jy] = EL_PLAYER_1;
9724 InitMovingField(jx, jy, MV_DOWN);
9725 Store[jx][jy] = EL_ACID;
9726 ContinueMoving(jx, jy);
9730 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
9736 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
9738 #if USE_FIXED_DONT_RUN_INTO
9739 if (can_move == MP_DONT_RUN_INTO)
9743 if (can_move != MP_MOVING)
9746 #if USE_FIXED_DONT_RUN_INTO
9749 /* check if DigField() has caused relocation of the player */
9750 if (player->jx != jx || player->jy != jy)
9751 return MP_NO_ACTION; /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
9753 StorePlayer[jx][jy] = 0;
9754 player->last_jx = jx;
9755 player->last_jy = jy;
9756 player->jx = new_jx;
9757 player->jy = new_jy;
9758 StorePlayer[new_jx][new_jy] = player->element_nr;
9760 if (player->move_delay_value_next != -1)
9762 player->move_delay_value = player->move_delay_value_next;
9763 player->move_delay_value_next = -1;
9767 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
9769 player->step_counter++;
9771 PlayerVisit[jx][jy] = FrameCounter;
9773 ScrollPlayer(player, SCROLL_INIT);
9778 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
9780 int jx = player->jx, jy = player->jy;
9781 int old_jx = jx, old_jy = jy;
9782 int moved = MP_NO_ACTION;
9784 if (!player->active)
9789 if (player->MovPos == 0)
9791 player->is_moving = FALSE;
9792 player->is_digging = FALSE;
9793 player->is_collecting = FALSE;
9794 player->is_snapping = FALSE;
9795 player->is_pushing = FALSE;
9801 if (player->move_delay > 0)
9804 player->move_delay = -1; /* set to "uninitialized" value */
9806 /* store if player is automatically moved to next field */
9807 player->is_auto_moving = (player->programmed_action != MV_NONE);
9809 /* remove the last programmed player action */
9810 player->programmed_action = 0;
9814 /* should only happen if pre-1.2 tape recordings are played */
9815 /* this is only for backward compatibility */
9817 int original_move_delay_value = player->move_delay_value;
9820 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
9824 /* scroll remaining steps with finest movement resolution */
9825 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
9827 while (player->MovPos)
9829 ScrollPlayer(player, SCROLL_GO_ON);
9830 ScrollScreen(NULL, SCROLL_GO_ON);
9832 AdvanceFrameAndPlayerCounters(player->index_nr);
9838 player->move_delay_value = original_move_delay_value;
9841 if (player->last_move_dir & MV_HORIZONTAL)
9843 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
9844 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
9848 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
9849 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
9856 if (moved & MP_MOVING && !ScreenMovPos &&
9857 (player->index_nr == game.centered_player_nr ||
9858 game.centered_player_nr == -1))
9860 if (moved & MP_MOVING && !ScreenMovPos &&
9861 (player == local_player || !options.network))
9864 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
9865 int offset = (setup.scroll_delay ? 3 : 0);
9867 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
9869 /* actual player has left the screen -- scroll in that direction */
9870 if (jx != old_jx) /* player has moved horizontally */
9871 scroll_x += (jx - old_jx);
9872 else /* player has moved vertically */
9873 scroll_y += (jy - old_jy);
9877 if (jx != old_jx) /* player has moved horizontally */
9879 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
9880 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
9881 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
9883 /* don't scroll over playfield boundaries */
9884 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
9885 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
9887 /* don't scroll more than one field at a time */
9888 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
9890 /* don't scroll against the player's moving direction */
9891 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
9892 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
9893 scroll_x = old_scroll_x;
9895 else /* player has moved vertically */
9897 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
9898 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
9899 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
9901 /* don't scroll over playfield boundaries */
9902 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
9903 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
9905 /* don't scroll more than one field at a time */
9906 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
9908 /* don't scroll against the player's moving direction */
9909 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
9910 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
9911 scroll_y = old_scroll_y;
9915 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
9918 if (!options.network && game.centered_player_nr == -1 &&
9919 !AllPlayersInVisibleScreen())
9921 scroll_x = old_scroll_x;
9922 scroll_y = old_scroll_y;
9926 if (!options.network && !AllPlayersInVisibleScreen())
9928 scroll_x = old_scroll_x;
9929 scroll_y = old_scroll_y;
9934 ScrollScreen(player, SCROLL_INIT);
9935 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
9940 player->StepFrame = 0;
9942 if (moved & MP_MOVING)
9944 if (old_jx != jx && old_jy == jy)
9945 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
9946 else if (old_jx == jx && old_jy != jy)
9947 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
9949 DrawLevelField(jx, jy); /* for "crumbled sand" */
9951 player->last_move_dir = player->MovDir;
9952 player->is_moving = TRUE;
9953 player->is_snapping = FALSE;
9954 player->is_switching = FALSE;
9955 player->is_dropping = FALSE;
9956 player->is_dropping_pressed = FALSE;
9957 player->drop_pressed_delay = 0;
9961 CheckGravityMovementWhenNotMoving(player);
9963 player->is_moving = FALSE;
9965 /* at this point, the player is allowed to move, but cannot move right now
9966 (e.g. because of something blocking the way) -- ensure that the player
9967 is also allowed to move in the next frame (in old versions before 3.1.1,
9968 the player was forced to wait again for eight frames before next try) */
9970 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
9971 player->move_delay = 0; /* allow direct movement in the next frame */
9974 if (player->move_delay == -1) /* not yet initialized by DigField() */
9975 player->move_delay = player->move_delay_value;
9977 if (game.engine_version < VERSION_IDENT(3,0,7,0))
9979 TestIfPlayerTouchesBadThing(jx, jy);
9980 TestIfPlayerTouchesCustomElement(jx, jy);
9983 if (!player->active)
9984 RemovePlayer(player);
9989 void ScrollPlayer(struct PlayerInfo *player, int mode)
9991 int jx = player->jx, jy = player->jy;
9992 int last_jx = player->last_jx, last_jy = player->last_jy;
9993 int move_stepsize = TILEX / player->move_delay_value;
9995 #if USE_NEW_PLAYER_SPEED
9996 if (!player->active)
9999 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
10002 if (!player->active || player->MovPos == 0)
10006 if (mode == SCROLL_INIT)
10008 player->actual_frame_counter = FrameCounter;
10009 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10011 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
10012 Feld[last_jx][last_jy] == EL_EMPTY)
10014 int last_field_block_delay = 0; /* start with no blocking at all */
10015 int block_delay_adjustment = player->block_delay_adjustment;
10017 /* if player blocks last field, add delay for exactly one move */
10018 if (player->block_last_field)
10020 last_field_block_delay += player->move_delay_value;
10022 /* when blocking enabled, prevent moving up despite gravity */
10023 if (game.gravity && player->MovDir == MV_UP)
10024 block_delay_adjustment = -1;
10027 /* add block delay adjustment (also possible when not blocking) */
10028 last_field_block_delay += block_delay_adjustment;
10030 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
10031 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
10034 #if USE_NEW_PLAYER_SPEED
10035 if (player->MovPos != 0) /* player has not yet reached destination */
10041 else if (!FrameReached(&player->actual_frame_counter, 1))
10045 printf("::: player->MovPos: %d -> %d\n",
10047 player->MovPos + (player->MovPos > 0 ? -1 : 1) * move_stepsize);
10050 #if USE_NEW_PLAYER_SPEED
10051 if (player->MovPos != 0)
10053 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
10054 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10056 /* before DrawPlayer() to draw correct player graphic for this case */
10057 if (player->MovPos == 0)
10058 CheckGravityMovement(player);
10061 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
10062 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10064 /* before DrawPlayer() to draw correct player graphic for this case */
10065 if (player->MovPos == 0)
10066 CheckGravityMovement(player);
10069 if (player->MovPos == 0) /* player reached destination field */
10072 printf("::: player reached destination field\n");
10075 if (player->move_delay_reset_counter > 0)
10077 player->move_delay_reset_counter--;
10079 if (player->move_delay_reset_counter == 0)
10081 /* continue with normal speed after quickly moving through gate */
10082 HALVE_PLAYER_SPEED(player);
10084 /* be able to make the next move without delay */
10085 player->move_delay = 0;
10089 player->last_jx = jx;
10090 player->last_jy = jy;
10092 if (Feld[jx][jy] == EL_EXIT_OPEN ||
10093 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
10094 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
10096 DrawPlayer(player); /* needed here only to cleanup last field */
10097 RemovePlayer(player);
10099 if (local_player->friends_still_needed == 0 ||
10100 IS_SP_ELEMENT(Feld[jx][jy]))
10101 player->LevelSolved = player->GameOver = TRUE;
10104 /* this breaks one level: "machine", level 000 */
10106 int move_direction = player->MovDir;
10107 int enter_side = MV_DIR_OPPOSITE(move_direction);
10108 int leave_side = move_direction;
10109 int old_jx = last_jx;
10110 int old_jy = last_jy;
10111 int old_element = Feld[old_jx][old_jy];
10112 int new_element = Feld[jx][jy];
10114 if (IS_CUSTOM_ELEMENT(old_element))
10115 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
10117 player->index_bit, leave_side);
10119 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
10120 CE_PLAYER_LEAVES_X,
10121 player->index_bit, leave_side);
10123 if (IS_CUSTOM_ELEMENT(new_element))
10124 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
10125 player->index_bit, enter_side);
10127 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
10128 CE_PLAYER_ENTERS_X,
10129 player->index_bit, enter_side);
10131 CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
10132 CE_MOVE_OF_X, move_direction);
10135 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10137 TestIfPlayerTouchesBadThing(jx, jy);
10138 TestIfPlayerTouchesCustomElement(jx, jy);
10140 /* needed because pushed element has not yet reached its destination,
10141 so it would trigger a change event at its previous field location */
10142 if (!player->is_pushing)
10143 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
10145 if (!player->active)
10146 RemovePlayer(player);
10149 if (level.use_step_counter)
10159 if (TimeLeft <= 10 && setup.time_limit)
10160 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
10162 DrawGameValue_Time(TimeLeft);
10164 if (!TimeLeft && setup.time_limit)
10165 for (i = 0; i < MAX_PLAYERS; i++)
10166 KillPlayer(&stored_player[i]);
10168 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
10169 DrawGameValue_Time(TimePlayed);
10172 if (tape.single_step && tape.recording && !tape.pausing &&
10173 !player->programmed_action)
10174 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10178 void ScrollScreen(struct PlayerInfo *player, int mode)
10180 static unsigned long screen_frame_counter = 0;
10182 if (mode == SCROLL_INIT)
10184 /* set scrolling step size according to actual player's moving speed */
10185 ScrollStepSize = TILEX / player->move_delay_value;
10187 screen_frame_counter = FrameCounter;
10188 ScreenMovDir = player->MovDir;
10189 ScreenMovPos = player->MovPos;
10190 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
10193 else if (!FrameReached(&screen_frame_counter, 1))
10198 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
10199 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
10200 redraw_mask |= REDRAW_FIELD;
10203 ScreenMovDir = MV_NONE;
10206 void TestIfPlayerTouchesCustomElement(int x, int y)
10208 static int xy[4][2] =
10215 static int trigger_sides[4][2] =
10217 /* center side border side */
10218 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
10219 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
10220 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
10221 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
10223 static int touch_dir[4] =
10225 MV_LEFT | MV_RIGHT,
10230 int center_element = Feld[x][y]; /* should always be non-moving! */
10233 for (i = 0; i < NUM_DIRECTIONS; i++)
10235 int xx = x + xy[i][0];
10236 int yy = y + xy[i][1];
10237 int center_side = trigger_sides[i][0];
10238 int border_side = trigger_sides[i][1];
10239 int border_element;
10241 if (!IN_LEV_FIELD(xx, yy))
10244 if (IS_PLAYER(x, y))
10246 struct PlayerInfo *player = PLAYERINFO(x, y);
10248 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10249 border_element = Feld[xx][yy]; /* may be moving! */
10250 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
10251 border_element = Feld[xx][yy];
10252 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
10253 border_element = MovingOrBlocked2Element(xx, yy);
10255 continue; /* center and border element do not touch */
10257 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
10258 player->index_bit, border_side);
10259 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
10260 CE_PLAYER_TOUCHES_X,
10261 player->index_bit, border_side);
10263 else if (IS_PLAYER(xx, yy))
10265 struct PlayerInfo *player = PLAYERINFO(xx, yy);
10267 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10269 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
10270 continue; /* center and border element do not touch */
10273 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
10274 player->index_bit, center_side);
10275 CheckTriggeredElementChangeByPlayer(x, y, center_element,
10276 CE_PLAYER_TOUCHES_X,
10277 player->index_bit, center_side);
10283 #if USE_ELEMENT_TOUCHING_BUGFIX
10285 void TestIfElementTouchesCustomElement(int x, int y)
10287 static int xy[4][2] =
10294 static int trigger_sides[4][2] =
10296 /* center side border side */
10297 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
10298 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
10299 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
10300 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
10302 static int touch_dir[4] =
10304 MV_LEFT | MV_RIGHT,
10309 boolean change_center_element = FALSE;
10310 int center_element = Feld[x][y]; /* should always be non-moving! */
10311 int border_element_old[NUM_DIRECTIONS];
10314 for (i = 0; i < NUM_DIRECTIONS; i++)
10316 int xx = x + xy[i][0];
10317 int yy = y + xy[i][1];
10318 int border_element;
10320 border_element_old[i] = -1;
10322 if (!IN_LEV_FIELD(xx, yy))
10325 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10326 border_element = Feld[xx][yy]; /* may be moving! */
10327 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
10328 border_element = Feld[xx][yy];
10329 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
10330 border_element = MovingOrBlocked2Element(xx, yy);
10332 continue; /* center and border element do not touch */
10334 border_element_old[i] = border_element;
10337 for (i = 0; i < NUM_DIRECTIONS; i++)
10339 int xx = x + xy[i][0];
10340 int yy = y + xy[i][1];
10341 int center_side = trigger_sides[i][0];
10342 int border_element = border_element_old[i];
10344 if (border_element == -1)
10347 /* check for change of border element */
10348 CheckElementChangeBySide(xx, yy, border_element, center_element,
10349 CE_TOUCHING_X, center_side);
10352 for (i = 0; i < NUM_DIRECTIONS; i++)
10354 int border_side = trigger_sides[i][1];
10355 int border_element = border_element_old[i];
10357 if (border_element == -1)
10360 /* check for change of center element (but change it only once) */
10361 if (!change_center_element)
10362 change_center_element =
10363 CheckElementChangeBySide(x, y, center_element, border_element,
10364 CE_TOUCHING_X, border_side);
10370 void TestIfElementTouchesCustomElement_OLD(int x, int y)
10372 static int xy[4][2] =
10379 static int trigger_sides[4][2] =
10381 /* center side border side */
10382 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
10383 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
10384 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
10385 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
10387 static int touch_dir[4] =
10389 MV_LEFT | MV_RIGHT,
10394 boolean change_center_element = FALSE;
10395 int center_element = Feld[x][y]; /* should always be non-moving! */
10398 for (i = 0; i < NUM_DIRECTIONS; i++)
10400 int xx = x + xy[i][0];
10401 int yy = y + xy[i][1];
10402 int center_side = trigger_sides[i][0];
10403 int border_side = trigger_sides[i][1];
10404 int border_element;
10406 if (!IN_LEV_FIELD(xx, yy))
10409 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10410 border_element = Feld[xx][yy]; /* may be moving! */
10411 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
10412 border_element = Feld[xx][yy];
10413 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
10414 border_element = MovingOrBlocked2Element(xx, yy);
10416 continue; /* center and border element do not touch */
10418 /* check for change of center element (but change it only once) */
10419 if (!change_center_element)
10420 change_center_element =
10421 CheckElementChangeBySide(x, y, center_element, border_element,
10422 CE_TOUCHING_X, border_side);
10424 /* check for change of border element */
10425 CheckElementChangeBySide(xx, yy, border_element, center_element,
10426 CE_TOUCHING_X, center_side);
10432 void TestIfElementHitsCustomElement(int x, int y, int direction)
10434 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
10435 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
10436 int hitx = x + dx, hity = y + dy;
10437 int hitting_element = Feld[x][y];
10438 int touched_element;
10440 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
10443 touched_element = (IN_LEV_FIELD(hitx, hity) ?
10444 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
10446 if (IN_LEV_FIELD(hitx, hity))
10448 int opposite_direction = MV_DIR_OPPOSITE(direction);
10449 int hitting_side = direction;
10450 int touched_side = opposite_direction;
10451 boolean object_hit = (!IS_MOVING(hitx, hity) ||
10452 MovDir[hitx][hity] != direction ||
10453 ABS(MovPos[hitx][hity]) <= TILEY / 2);
10459 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10460 CE_HITTING_X, touched_side);
10462 CheckElementChangeBySide(hitx, hity, touched_element,
10463 hitting_element, CE_HIT_BY_X, hitting_side);
10465 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10466 CE_HIT_BY_SOMETHING, opposite_direction);
10470 /* "hitting something" is also true when hitting the playfield border */
10471 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10472 CE_HITTING_SOMETHING, direction);
10476 void TestIfElementSmashesCustomElement(int x, int y, int direction)
10478 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
10479 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
10480 int hitx = x + dx, hity = y + dy;
10481 int hitting_element = Feld[x][y];
10482 int touched_element;
10484 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
10485 !IS_FREE(hitx, hity) &&
10486 (!IS_MOVING(hitx, hity) ||
10487 MovDir[hitx][hity] != direction ||
10488 ABS(MovPos[hitx][hity]) <= TILEY / 2));
10491 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
10495 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
10499 touched_element = (IN_LEV_FIELD(hitx, hity) ?
10500 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
10502 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10503 EP_CAN_SMASH_EVERYTHING, direction);
10505 if (IN_LEV_FIELD(hitx, hity))
10507 int opposite_direction = MV_DIR_OPPOSITE(direction);
10508 int hitting_side = direction;
10509 int touched_side = opposite_direction;
10511 int touched_element = MovingOrBlocked2Element(hitx, hity);
10514 boolean object_hit = (!IS_MOVING(hitx, hity) ||
10515 MovDir[hitx][hity] != direction ||
10516 ABS(MovPos[hitx][hity]) <= TILEY / 2);
10525 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10526 CE_SMASHED_BY_SOMETHING, opposite_direction);
10528 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10529 CE_OTHER_IS_SMASHING, touched_side);
10531 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10532 CE_OTHER_GETS_SMASHED, hitting_side);
10538 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
10540 int i, kill_x = -1, kill_y = -1;
10542 int bad_element = -1;
10543 static int test_xy[4][2] =
10550 static int test_dir[4] =
10558 for (i = 0; i < NUM_DIRECTIONS; i++)
10560 int test_x, test_y, test_move_dir, test_element;
10562 test_x = good_x + test_xy[i][0];
10563 test_y = good_y + test_xy[i][1];
10565 if (!IN_LEV_FIELD(test_x, test_y))
10569 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
10571 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
10573 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
10574 2nd case: DONT_TOUCH style bad thing does not move away from good thing
10576 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
10577 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
10581 bad_element = test_element;
10587 if (kill_x != -1 || kill_y != -1)
10589 if (IS_PLAYER(good_x, good_y))
10591 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
10593 if (player->shield_deadly_time_left > 0 &&
10594 !IS_INDESTRUCTIBLE(bad_element))
10595 Bang(kill_x, kill_y);
10596 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
10597 KillPlayer(player);
10600 Bang(good_x, good_y);
10604 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
10606 int i, kill_x = -1, kill_y = -1;
10607 int bad_element = Feld[bad_x][bad_y];
10608 static int test_xy[4][2] =
10615 static int touch_dir[4] =
10617 MV_LEFT | MV_RIGHT,
10622 static int test_dir[4] =
10630 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
10633 for (i = 0; i < NUM_DIRECTIONS; i++)
10635 int test_x, test_y, test_move_dir, test_element;
10637 test_x = bad_x + test_xy[i][0];
10638 test_y = bad_y + test_xy[i][1];
10639 if (!IN_LEV_FIELD(test_x, test_y))
10643 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
10645 test_element = Feld[test_x][test_y];
10647 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
10648 2nd case: DONT_TOUCH style bad thing does not move away from good thing
10650 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
10651 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
10653 /* good thing is player or penguin that does not move away */
10654 if (IS_PLAYER(test_x, test_y))
10656 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
10658 if (bad_element == EL_ROBOT && player->is_moving)
10659 continue; /* robot does not kill player if he is moving */
10661 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10663 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
10664 continue; /* center and border element do not touch */
10671 else if (test_element == EL_PENGUIN)
10680 if (kill_x != -1 || kill_y != -1)
10682 if (IS_PLAYER(kill_x, kill_y))
10684 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
10686 if (player->shield_deadly_time_left > 0 &&
10687 !IS_INDESTRUCTIBLE(bad_element))
10688 Bang(bad_x, bad_y);
10689 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
10690 KillPlayer(player);
10693 Bang(kill_x, kill_y);
10697 void TestIfPlayerTouchesBadThing(int x, int y)
10699 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
10702 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
10704 TestIfGoodThingHitsBadThing(x, y, move_dir);
10707 void TestIfBadThingTouchesPlayer(int x, int y)
10709 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
10712 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
10714 TestIfBadThingHitsGoodThing(x, y, move_dir);
10717 void TestIfFriendTouchesBadThing(int x, int y)
10719 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
10722 void TestIfBadThingTouchesFriend(int x, int y)
10724 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
10727 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
10729 int i, kill_x = bad_x, kill_y = bad_y;
10730 static int xy[4][2] =
10738 for (i = 0; i < NUM_DIRECTIONS; i++)
10742 x = bad_x + xy[i][0];
10743 y = bad_y + xy[i][1];
10744 if (!IN_LEV_FIELD(x, y))
10747 element = Feld[x][y];
10748 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
10749 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
10757 if (kill_x != bad_x || kill_y != bad_y)
10758 Bang(bad_x, bad_y);
10761 void KillPlayer(struct PlayerInfo *player)
10763 int jx = player->jx, jy = player->jy;
10765 if (!player->active)
10768 /* remove accessible field at the player's position */
10769 Feld[jx][jy] = EL_EMPTY;
10771 /* deactivate shield (else Bang()/Explode() would not work right) */
10772 player->shield_normal_time_left = 0;
10773 player->shield_deadly_time_left = 0;
10776 BuryPlayer(player);
10779 static void KillPlayerUnlessEnemyProtected(int x, int y)
10781 if (!PLAYER_ENEMY_PROTECTED(x, y))
10782 KillPlayer(PLAYERINFO(x, y));
10785 static void KillPlayerUnlessExplosionProtected(int x, int y)
10787 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
10788 KillPlayer(PLAYERINFO(x, y));
10791 void BuryPlayer(struct PlayerInfo *player)
10793 int jx = player->jx, jy = player->jy;
10795 if (!player->active)
10798 PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
10799 PlayLevelSound(jx, jy, SND_GAME_LOSING);
10801 player->GameOver = TRUE;
10802 RemovePlayer(player);
10805 void RemovePlayer(struct PlayerInfo *player)
10807 int jx = player->jx, jy = player->jy;
10808 int i, found = FALSE;
10810 player->present = FALSE;
10811 player->active = FALSE;
10813 if (!ExplodeField[jx][jy])
10814 StorePlayer[jx][jy] = 0;
10816 if (player->is_moving)
10817 DrawLevelField(player->last_jx, player->last_jy);
10819 for (i = 0; i < MAX_PLAYERS; i++)
10820 if (stored_player[i].active)
10824 AllPlayersGone = TRUE;
10830 #if USE_NEW_SNAP_DELAY
10831 static void setFieldForSnapping(int x, int y, int element, int direction)
10833 struct ElementInfo *ei = &element_info[element];
10834 int direction_bit = MV_DIR_TO_BIT(direction);
10835 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
10836 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
10837 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
10839 Feld[x][y] = EL_ELEMENT_SNAPPING;
10840 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
10842 ResetGfxAnimation(x, y);
10844 GfxElement[x][y] = element;
10845 GfxAction[x][y] = action;
10846 GfxDir[x][y] = direction;
10847 GfxFrame[x][y] = -1;
10852 =============================================================================
10853 checkDiagonalPushing()
10854 -----------------------------------------------------------------------------
10855 check if diagonal input device direction results in pushing of object
10856 (by checking if the alternative direction is walkable, diggable, ...)
10857 =============================================================================
10860 static boolean checkDiagonalPushing(struct PlayerInfo *player,
10861 int x, int y, int real_dx, int real_dy)
10863 int jx, jy, dx, dy, xx, yy;
10865 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
10868 /* diagonal direction: check alternative direction */
10873 xx = jx + (dx == 0 ? real_dx : 0);
10874 yy = jy + (dy == 0 ? real_dy : 0);
10876 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
10880 =============================================================================
10882 -----------------------------------------------------------------------------
10883 x, y: field next to player (non-diagonal) to try to dig to
10884 real_dx, real_dy: direction as read from input device (can be diagonal)
10885 =============================================================================
10888 int DigField(struct PlayerInfo *player,
10889 int oldx, int oldy, int x, int y,
10890 int real_dx, int real_dy, int mode)
10892 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
10893 boolean player_was_pushing = player->is_pushing;
10894 boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
10895 boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
10896 int jx = oldx, jy = oldy;
10897 int dx = x - jx, dy = y - jy;
10898 int nextx = x + dx, nexty = y + dy;
10899 int move_direction = (dx == -1 ? MV_LEFT :
10900 dx == +1 ? MV_RIGHT :
10902 dy == +1 ? MV_DOWN : MV_NONE);
10903 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
10904 int dig_side = MV_DIR_OPPOSITE(move_direction);
10905 int old_element = Feld[jx][jy];
10906 #if USE_FIXED_DONT_RUN_INTO
10907 int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
10913 if (is_player) /* function can also be called by EL_PENGUIN */
10915 if (player->MovPos == 0)
10917 player->is_digging = FALSE;
10918 player->is_collecting = FALSE;
10921 if (player->MovPos == 0) /* last pushing move finished */
10922 player->is_pushing = FALSE;
10924 if (mode == DF_NO_PUSH) /* player just stopped pushing */
10926 player->is_switching = FALSE;
10927 player->push_delay = -1;
10929 return MP_NO_ACTION;
10933 #if !USE_FIXED_DONT_RUN_INTO
10934 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
10935 return MP_NO_ACTION;
10938 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
10939 old_element = Back[jx][jy];
10941 /* in case of element dropped at player position, check background */
10942 else if (Back[jx][jy] != EL_EMPTY &&
10943 game.engine_version >= VERSION_IDENT(2,2,0,0))
10944 old_element = Back[jx][jy];
10947 #if USE_FIXED_DONT_RUN_INTO
10948 if (player_can_move && DONT_RUN_INTO(element))
10950 if (element == EL_ACID && dx == 0 && dy == 1)
10953 Feld[jx][jy] = EL_PLAYER_1;
10954 InitMovingField(jx, jy, MV_DOWN);
10955 Store[jx][jy] = EL_ACID;
10956 ContinueMoving(jx, jy);
10957 BuryPlayer(player);
10960 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
10962 return MP_DONT_RUN_INTO;
10968 #if USE_FIXED_DONT_RUN_INTO
10969 if (player_can_move && DONT_RUN_INTO(element))
10971 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
10973 return MP_DONT_RUN_INTO;
10978 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
10979 return MP_NO_ACTION; /* field has no opening in this direction */
10981 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
10982 return MP_NO_ACTION; /* field has no opening in this direction */
10985 #if USE_FIXED_DONT_RUN_INTO
10986 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
10989 Feld[jx][jy] = EL_PLAYER_1;
10990 InitMovingField(jx, jy, MV_DOWN);
10991 Store[jx][jy] = EL_ACID;
10992 ContinueMoving(jx, jy);
10993 BuryPlayer(player);
10995 return MP_DONT_RUN_INTO;
11001 #if USE_FIXED_DONT_RUN_INTO
11002 if (player_can_move && DONT_RUN_INTO(element))
11004 if (element == EL_ACID && dx == 0 && dy == 1)
11007 Feld[jx][jy] = EL_PLAYER_1;
11008 InitMovingField(jx, jy, MV_DOWN);
11009 Store[jx][jy] = EL_ACID;
11010 ContinueMoving(jx, jy);
11011 BuryPlayer(player);
11014 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
11016 return MP_DONT_RUN_INTO;
11021 #if USE_FIXED_DONT_RUN_INTO
11022 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
11023 return MP_NO_ACTION;
11026 #if !USE_FIXED_DONT_RUN_INTO
11027 element = Feld[x][y];
11030 collect_count = element_info[element].collect_count_initial;
11032 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
11033 return MP_NO_ACTION;
11035 if (game.engine_version < VERSION_IDENT(2,2,0,0))
11036 player_can_move = player_can_move_or_snap;
11038 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
11039 game.engine_version >= VERSION_IDENT(2,2,0,0))
11041 CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
11042 player->index_bit, dig_side);
11043 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
11044 player->index_bit, dig_side);
11046 if (Feld[x][y] != element) /* field changed by snapping */
11049 return MP_NO_ACTION;
11052 if (game.gravity && is_player && !player->is_auto_moving &&
11053 canFallDown(player) && move_direction != MV_DOWN &&
11054 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
11055 return MP_NO_ACTION; /* player cannot walk here due to gravity */
11057 if (player_can_move &&
11058 IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
11060 int sound_element = SND_ELEMENT(element);
11061 int sound_action = ACTION_WALKING;
11063 if (IS_RND_GATE(element))
11065 if (!player->key[RND_GATE_NR(element)])
11066 return MP_NO_ACTION;
11068 else if (IS_RND_GATE_GRAY(element))
11070 if (!player->key[RND_GATE_GRAY_NR(element)])
11071 return MP_NO_ACTION;
11073 else if (IS_RND_GATE_GRAY_ACTIVE(element))
11075 if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
11076 return MP_NO_ACTION;
11078 else if (element == EL_EXIT_OPEN ||
11079 element == EL_SP_EXIT_OPEN ||
11080 element == EL_SP_EXIT_OPENING)
11082 sound_action = ACTION_PASSING; /* player is passing exit */
11084 else if (element == EL_EMPTY)
11086 sound_action = ACTION_MOVING; /* nothing to walk on */
11089 /* play sound from background or player, whatever is available */
11090 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
11091 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
11093 PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
11095 else if (player_can_move &&
11096 IS_PASSABLE(element) && canPassField(x, y, move_direction))
11098 if (!ACCESS_FROM(element, opposite_direction))
11099 return MP_NO_ACTION; /* field not accessible from this direction */
11101 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
11102 return MP_NO_ACTION;
11104 if (IS_EM_GATE(element))
11106 if (!player->key[EM_GATE_NR(element)])
11107 return MP_NO_ACTION;
11109 else if (IS_EM_GATE_GRAY(element))
11111 if (!player->key[EM_GATE_GRAY_NR(element)])
11112 return MP_NO_ACTION;
11114 else if (IS_EM_GATE_GRAY_ACTIVE(element))
11116 if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
11117 return MP_NO_ACTION;
11119 else if (IS_SP_PORT(element))
11121 if (element == EL_SP_GRAVITY_PORT_LEFT ||
11122 element == EL_SP_GRAVITY_PORT_RIGHT ||
11123 element == EL_SP_GRAVITY_PORT_UP ||
11124 element == EL_SP_GRAVITY_PORT_DOWN)
11125 game.gravity = !game.gravity;
11126 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
11127 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
11128 element == EL_SP_GRAVITY_ON_PORT_UP ||
11129 element == EL_SP_GRAVITY_ON_PORT_DOWN)
11130 game.gravity = TRUE;
11131 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
11132 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
11133 element == EL_SP_GRAVITY_OFF_PORT_UP ||
11134 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
11135 game.gravity = FALSE;
11138 /* automatically move to the next field with double speed */
11139 player->programmed_action = move_direction;
11141 if (player->move_delay_reset_counter == 0)
11143 player->move_delay_reset_counter = 2; /* two double speed steps */
11145 DOUBLE_PLAYER_SPEED(player);
11148 PlayLevelSoundAction(x, y, ACTION_PASSING);
11150 else if (player_can_move_or_snap && IS_DIGGABLE(element))
11154 if (mode != DF_SNAP)
11156 GfxElement[x][y] = GFX_ELEMENT(element);
11157 player->is_digging = TRUE;
11160 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
11162 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
11163 player->index_bit, dig_side);
11165 if (mode == DF_SNAP)
11167 #if USE_NEW_SNAP_DELAY
11168 if (level.block_snap_field)
11169 setFieldForSnapping(x, y, element, move_direction);
11171 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11173 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11176 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
11177 player->index_bit, dig_side);
11180 else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
11184 if (is_player && mode != DF_SNAP)
11186 GfxElement[x][y] = element;
11187 player->is_collecting = TRUE;
11190 if (element == EL_SPEED_PILL)
11192 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
11194 else if (element == EL_EXTRA_TIME && level.time > 0)
11196 TimeLeft += level.extra_time;
11197 DrawGameValue_Time(TimeLeft);
11199 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
11201 player->shield_normal_time_left += level.shield_normal_time;
11202 if (element == EL_SHIELD_DEADLY)
11203 player->shield_deadly_time_left += level.shield_deadly_time;
11205 else if (element == EL_DYNAMITE ||
11206 element == EL_EM_DYNAMITE ||
11207 element == EL_SP_DISK_RED)
11209 if (player->inventory_size < MAX_INVENTORY_SIZE)
11210 player->inventory_element[player->inventory_size++] = element;
11212 DrawGameValue_Dynamite(local_player->inventory_size);
11214 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
11216 player->dynabomb_count++;
11217 player->dynabombs_left++;
11219 else if (element == EL_DYNABOMB_INCREASE_SIZE)
11221 player->dynabomb_size++;
11223 else if (element == EL_DYNABOMB_INCREASE_POWER)
11225 player->dynabomb_xl = TRUE;
11227 else if (IS_KEY(element))
11229 player->key[KEY_NR(element)] = TRUE;
11231 DrawGameValue_Keys(player->key);
11233 redraw_mask |= REDRAW_DOOR_1;
11235 else if (IS_ENVELOPE(element))
11237 player->show_envelope = element;
11239 else if (element == EL_EMC_LENSES)
11241 game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
11243 RedrawAllInvisibleElementsForLenses();
11245 else if (element == EL_EMC_MAGNIFIER)
11247 game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
11249 RedrawAllInvisibleElementsForMagnifier();
11251 else if (IS_DROPPABLE(element) ||
11252 IS_THROWABLE(element)) /* can be collected and dropped */
11256 if (collect_count == 0)
11257 player->inventory_infinite_element = element;
11259 for (i = 0; i < collect_count; i++)
11260 if (player->inventory_size < MAX_INVENTORY_SIZE)
11261 player->inventory_element[player->inventory_size++] = element;
11263 DrawGameValue_Dynamite(local_player->inventory_size);
11265 else if (collect_count > 0)
11267 local_player->gems_still_needed -= collect_count;
11268 if (local_player->gems_still_needed < 0)
11269 local_player->gems_still_needed = 0;
11271 DrawGameValue_Emeralds(local_player->gems_still_needed);
11274 RaiseScoreElement(element);
11275 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
11278 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
11279 player->index_bit, dig_side);
11281 if (mode == DF_SNAP)
11283 #if USE_NEW_SNAP_DELAY
11284 if (level.block_snap_field)
11285 setFieldForSnapping(x, y, element, move_direction);
11287 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11289 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11292 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
11293 player->index_bit, dig_side);
11296 else if (player_can_move_or_snap && IS_PUSHABLE(element))
11298 if (mode == DF_SNAP && element != EL_BD_ROCK)
11299 return MP_NO_ACTION;
11301 if (CAN_FALL(element) && dy)
11302 return MP_NO_ACTION;
11304 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
11305 !(element == EL_SPRING && level.use_spring_bug))
11306 return MP_NO_ACTION;
11308 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
11309 ((move_direction & MV_VERTICAL &&
11310 ((element_info[element].move_pattern & MV_LEFT &&
11311 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
11312 (element_info[element].move_pattern & MV_RIGHT &&
11313 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
11314 (move_direction & MV_HORIZONTAL &&
11315 ((element_info[element].move_pattern & MV_UP &&
11316 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
11317 (element_info[element].move_pattern & MV_DOWN &&
11318 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
11319 return MP_NO_ACTION;
11321 /* do not push elements already moving away faster than player */
11322 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
11323 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
11324 return MP_NO_ACTION;
11326 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
11328 if (player->push_delay_value == -1 || !player_was_pushing)
11329 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11331 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11333 if (player->push_delay_value == -1)
11334 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11336 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
11338 if (!player->is_pushing)
11339 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11342 player->is_pushing = TRUE;
11344 if (!(IN_LEV_FIELD(nextx, nexty) &&
11345 (IS_FREE(nextx, nexty) ||
11346 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
11347 IS_SB_ELEMENT(element)))))
11348 return MP_NO_ACTION;
11350 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
11351 return MP_NO_ACTION;
11353 if (player->push_delay == -1) /* new pushing; restart delay */
11354 player->push_delay = 0;
11356 if (player->push_delay < player->push_delay_value &&
11357 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
11358 element != EL_SPRING && element != EL_BALLOON)
11360 /* make sure that there is no move delay before next try to push */
11361 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11362 player->move_delay = 0;
11364 return MP_NO_ACTION;
11367 if (IS_SB_ELEMENT(element))
11369 if (element == EL_SOKOBAN_FIELD_FULL)
11371 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
11372 local_player->sokobanfields_still_needed++;
11375 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
11377 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
11378 local_player->sokobanfields_still_needed--;
11381 Feld[x][y] = EL_SOKOBAN_OBJECT;
11383 if (Back[x][y] == Back[nextx][nexty])
11384 PlayLevelSoundAction(x, y, ACTION_PUSHING);
11385 else if (Back[x][y] != 0)
11386 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
11389 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
11392 if (local_player->sokobanfields_still_needed == 0 &&
11393 game.emulation == EMU_SOKOBAN)
11395 player->LevelSolved = player->GameOver = TRUE;
11396 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
11400 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
11402 InitMovingField(x, y, move_direction);
11403 GfxAction[x][y] = ACTION_PUSHING;
11405 if (mode == DF_SNAP)
11406 ContinueMoving(x, y);
11408 MovPos[x][y] = (dx != 0 ? dx : dy);
11410 Pushed[x][y] = TRUE;
11411 Pushed[nextx][nexty] = TRUE;
11413 if (game.engine_version < VERSION_IDENT(2,2,0,7))
11414 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11416 player->push_delay_value = -1; /* get new value later */
11418 /* check for element change _after_ element has been pushed */
11419 if (game.use_change_when_pushing_bug)
11421 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
11422 player->index_bit, dig_side);
11423 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
11424 player->index_bit, dig_side);
11427 else if (IS_SWITCHABLE(element))
11429 if (PLAYER_SWITCHING(player, x, y))
11431 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
11432 player->index_bit, dig_side);
11437 player->is_switching = TRUE;
11438 player->switch_x = x;
11439 player->switch_y = y;
11441 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
11443 if (element == EL_ROBOT_WHEEL)
11445 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
11449 DrawLevelField(x, y);
11451 else if (element == EL_SP_TERMINAL)
11456 SCAN_PLAYFIELD(xx, yy)
11458 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
11461 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
11463 else if (Feld[xx][yy] == EL_SP_TERMINAL)
11464 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
11467 else if (IS_BELT_SWITCH(element))
11469 ToggleBeltSwitch(x, y);
11471 else if (element == EL_SWITCHGATE_SWITCH_UP ||
11472 element == EL_SWITCHGATE_SWITCH_DOWN)
11474 ToggleSwitchgateSwitch(x, y);
11476 else if (element == EL_LIGHT_SWITCH ||
11477 element == EL_LIGHT_SWITCH_ACTIVE)
11479 ToggleLightSwitch(x, y);
11481 else if (element == EL_TIMEGATE_SWITCH)
11483 ActivateTimegateSwitch(x, y);
11485 else if (element == EL_BALLOON_SWITCH_LEFT ||
11486 element == EL_BALLOON_SWITCH_RIGHT ||
11487 element == EL_BALLOON_SWITCH_UP ||
11488 element == EL_BALLOON_SWITCH_DOWN ||
11489 element == EL_BALLOON_SWITCH_NONE ||
11490 element == EL_BALLOON_SWITCH_ANY)
11492 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
11493 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
11494 element == EL_BALLOON_SWITCH_UP ? MV_UP :
11495 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
11496 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
11499 else if (element == EL_LAMP)
11501 Feld[x][y] = EL_LAMP_ACTIVE;
11502 local_player->lights_still_needed--;
11504 ResetGfxAnimation(x, y);
11505 DrawLevelField(x, y);
11507 else if (element == EL_TIME_ORB_FULL)
11509 Feld[x][y] = EL_TIME_ORB_EMPTY;
11511 if (level.time > 0 || level.use_time_orb_bug)
11513 TimeLeft += level.time_orb_time;
11514 DrawGameValue_Time(TimeLeft);
11517 ResetGfxAnimation(x, y);
11518 DrawLevelField(x, y);
11520 else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
11521 element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
11525 game.ball_state = !game.ball_state;
11528 SCAN_PLAYFIELD(xx, yy)
11530 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
11533 int e = Feld[xx][yy];
11535 if (game.ball_state)
11537 if (e == EL_EMC_MAGIC_BALL)
11538 CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
11539 else if (e == EL_EMC_MAGIC_BALL_SWITCH)
11540 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
11544 if (e == EL_EMC_MAGIC_BALL_ACTIVE)
11545 CreateField(xx, yy, EL_EMC_MAGIC_BALL);
11546 else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
11547 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
11552 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
11553 player->index_bit, dig_side);
11555 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
11556 player->index_bit, dig_side);
11558 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
11559 player->index_bit, dig_side);
11565 if (!PLAYER_SWITCHING(player, x, y))
11567 player->is_switching = TRUE;
11568 player->switch_x = x;
11569 player->switch_y = y;
11571 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
11572 player->index_bit, dig_side);
11573 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
11574 player->index_bit, dig_side);
11576 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
11577 player->index_bit, dig_side);
11578 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
11579 player->index_bit, dig_side);
11582 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
11583 player->index_bit, dig_side);
11584 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
11585 player->index_bit, dig_side);
11587 return MP_NO_ACTION;
11590 player->push_delay = -1;
11592 if (is_player) /* function can also be called by EL_PENGUIN */
11594 if (Feld[x][y] != element) /* really digged/collected something */
11595 player->is_collecting = !player->is_digging;
11601 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
11603 int jx = player->jx, jy = player->jy;
11604 int x = jx + dx, y = jy + dy;
11605 int snap_direction = (dx == -1 ? MV_LEFT :
11606 dx == +1 ? MV_RIGHT :
11608 dy == +1 ? MV_DOWN : MV_NONE);
11609 boolean can_continue_snapping = (level.continuous_snapping &&
11610 WasJustFalling[x][y] < CHECK_DELAY_FALLING);
11612 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
11615 if (!player->active || !IN_LEV_FIELD(x, y))
11623 if (player->MovPos == 0)
11624 player->is_pushing = FALSE;
11626 player->is_snapping = FALSE;
11628 if (player->MovPos == 0)
11630 player->is_moving = FALSE;
11631 player->is_digging = FALSE;
11632 player->is_collecting = FALSE;
11638 #if USE_NEW_CONTINUOUS_SNAPPING
11639 /* prevent snapping with already pressed snap key when not allowed */
11640 if (player->is_snapping && !can_continue_snapping)
11643 if (player->is_snapping)
11647 player->MovDir = snap_direction;
11649 if (player->MovPos == 0)
11651 player->is_moving = FALSE;
11652 player->is_digging = FALSE;
11653 player->is_collecting = FALSE;
11656 player->is_dropping = FALSE;
11657 player->is_dropping_pressed = FALSE;
11658 player->drop_pressed_delay = 0;
11660 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
11663 player->is_snapping = TRUE;
11665 if (player->MovPos == 0)
11667 player->is_moving = FALSE;
11668 player->is_digging = FALSE;
11669 player->is_collecting = FALSE;
11672 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
11673 DrawLevelField(player->last_jx, player->last_jy);
11675 DrawLevelField(x, y);
11680 boolean DropElement(struct PlayerInfo *player)
11682 int old_element, new_element;
11683 int dropx = player->jx, dropy = player->jy;
11684 int drop_direction = player->MovDir;
11685 int drop_side = drop_direction;
11686 int drop_element = (player->inventory_size > 0 ?
11687 player->inventory_element[player->inventory_size - 1] :
11688 player->inventory_infinite_element != EL_UNDEFINED ?
11689 player->inventory_infinite_element :
11690 player->dynabombs_left > 0 ?
11691 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
11694 player->is_dropping_pressed = TRUE;
11696 /* do not drop an element on top of another element; when holding drop key
11697 pressed without moving, dropped element must move away before the next
11698 element can be dropped (this is especially important if the next element
11699 is dynamite, which can be placed on background for historical reasons) */
11700 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
11703 if (IS_THROWABLE(drop_element))
11705 dropx += GET_DX_FROM_DIR(drop_direction);
11706 dropy += GET_DY_FROM_DIR(drop_direction);
11708 if (!IN_LEV_FIELD(dropx, dropy))
11712 old_element = Feld[dropx][dropy]; /* old element at dropping position */
11713 new_element = drop_element; /* default: no change when dropping */
11715 /* check if player is active, not moving and ready to drop */
11716 if (!player->active || player->MovPos || player->drop_delay > 0)
11719 /* check if player has anything that can be dropped */
11720 if (new_element == EL_UNDEFINED)
11723 /* check if drop key was pressed long enough for EM style dynamite */
11724 if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
11727 /* check if anything can be dropped at the current position */
11728 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
11731 /* collected custom elements can only be dropped on empty fields */
11732 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
11735 if (old_element != EL_EMPTY)
11736 Back[dropx][dropy] = old_element; /* store old element on this field */
11738 ResetGfxAnimation(dropx, dropy);
11739 ResetRandomAnimationValue(dropx, dropy);
11741 if (player->inventory_size > 0 ||
11742 player->inventory_infinite_element != EL_UNDEFINED)
11744 if (player->inventory_size > 0)
11746 player->inventory_size--;
11748 DrawGameValue_Dynamite(local_player->inventory_size);
11750 if (new_element == EL_DYNAMITE)
11751 new_element = EL_DYNAMITE_ACTIVE;
11752 else if (new_element == EL_EM_DYNAMITE)
11753 new_element = EL_EM_DYNAMITE_ACTIVE;
11754 else if (new_element == EL_SP_DISK_RED)
11755 new_element = EL_SP_DISK_RED_ACTIVE;
11758 Feld[dropx][dropy] = new_element;
11760 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
11761 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
11762 el2img(Feld[dropx][dropy]), 0);
11764 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
11766 /* needed if previous element just changed to "empty" in the last frame */
11767 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
11769 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
11770 player->index_bit, drop_side);
11771 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
11773 player->index_bit, drop_side);
11775 TestIfElementTouchesCustomElement(dropx, dropy);
11777 else /* player is dropping a dyna bomb */
11779 player->dynabombs_left--;
11781 Feld[dropx][dropy] = new_element;
11783 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
11784 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
11785 el2img(Feld[dropx][dropy]), 0);
11787 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
11790 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
11791 InitField_WithBug1(dropx, dropy, FALSE);
11793 new_element = Feld[dropx][dropy]; /* element might have changed */
11795 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
11796 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
11798 int move_direction, nextx, nexty;
11800 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
11801 MovDir[dropx][dropy] = drop_direction;
11803 move_direction = MovDir[dropx][dropy];
11804 nextx = dropx + GET_DX_FROM_DIR(move_direction);
11805 nexty = dropy + GET_DY_FROM_DIR(move_direction);
11807 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
11808 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
11811 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
11812 player->is_dropping = TRUE;
11814 player->drop_pressed_delay = 0;
11815 player->is_dropping_pressed = FALSE;
11817 player->drop_x = dropx;
11818 player->drop_y = dropy;
11823 /* ------------------------------------------------------------------------- */
11824 /* game sound playing functions */
11825 /* ------------------------------------------------------------------------- */
11827 static int *loop_sound_frame = NULL;
11828 static int *loop_sound_volume = NULL;
11830 void InitPlayLevelSound()
11832 int num_sounds = getSoundListSize();
11834 checked_free(loop_sound_frame);
11835 checked_free(loop_sound_volume);
11837 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
11838 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
11841 static void PlayLevelSound(int x, int y, int nr)
11843 int sx = SCREENX(x), sy = SCREENY(y);
11844 int volume, stereo_position;
11845 int max_distance = 8;
11846 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
11848 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
11849 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
11852 if (!IN_LEV_FIELD(x, y) ||
11853 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
11854 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
11857 volume = SOUND_MAX_VOLUME;
11859 if (!IN_SCR_FIELD(sx, sy))
11861 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
11862 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
11864 volume -= volume * (dx > dy ? dx : dy) / max_distance;
11867 stereo_position = (SOUND_MAX_LEFT +
11868 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
11869 (SCR_FIELDX + 2 * max_distance));
11871 if (IS_LOOP_SOUND(nr))
11873 /* This assures that quieter loop sounds do not overwrite louder ones,
11874 while restarting sound volume comparison with each new game frame. */
11876 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
11879 loop_sound_volume[nr] = volume;
11880 loop_sound_frame[nr] = FrameCounter;
11883 PlaySoundExt(nr, volume, stereo_position, type);
11886 static void PlayLevelSoundNearest(int x, int y, int sound_action)
11888 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
11889 x > LEVELX(BX2) ? LEVELX(BX2) : x,
11890 y < LEVELY(BY1) ? LEVELY(BY1) :
11891 y > LEVELY(BY2) ? LEVELY(BY2) : y,
11895 static void PlayLevelSoundAction(int x, int y, int action)
11897 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
11900 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
11902 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
11904 if (sound_effect != SND_UNDEFINED)
11905 PlayLevelSound(x, y, sound_effect);
11908 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
11911 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
11913 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
11914 PlayLevelSound(x, y, sound_effect);
11917 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
11919 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
11921 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
11922 PlayLevelSound(x, y, sound_effect);
11925 static void StopLevelSoundActionIfLoop(int x, int y, int action)
11927 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
11929 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
11930 StopSound(sound_effect);
11933 static void PlayLevelMusic()
11935 if (levelset.music[level_nr] != MUS_UNDEFINED)
11936 PlayMusic(levelset.music[level_nr]); /* from config file */
11938 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
11941 void PlayLevelSound_EM(int x, int y, int element_em, int sample)
11943 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
11948 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
11952 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
11956 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
11960 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
11964 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
11968 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
11972 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
11975 case SAMPLE_android_clone:
11976 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
11979 case SAMPLE_android_move:
11980 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
11983 case SAMPLE_spring:
11984 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
11988 PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
11992 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
11995 case SAMPLE_eater_eat:
11996 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
12000 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12003 case SAMPLE_collect:
12004 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
12007 case SAMPLE_diamond:
12008 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12011 case SAMPLE_squash:
12012 /* !!! CHECK THIS !!! */
12014 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
12016 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
12020 case SAMPLE_wonderfall:
12021 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
12025 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12029 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12033 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
12037 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
12041 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12045 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
12048 case SAMPLE_wonder:
12049 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12053 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
12056 case SAMPLE_exit_open:
12057 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
12060 case SAMPLE_exit_leave:
12061 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
12064 case SAMPLE_dynamite:
12065 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12069 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12073 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
12077 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12081 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
12085 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
12089 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
12093 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
12098 void RaiseScore(int value)
12100 local_player->score += value;
12102 DrawGameValue_Score(local_player->score);
12105 void RaiseScoreElement(int element)
12110 case EL_BD_DIAMOND:
12111 case EL_EMERALD_YELLOW:
12112 case EL_EMERALD_RED:
12113 case EL_EMERALD_PURPLE:
12114 case EL_SP_INFOTRON:
12115 RaiseScore(level.score[SC_EMERALD]);
12118 RaiseScore(level.score[SC_DIAMOND]);
12121 RaiseScore(level.score[SC_CRYSTAL]);
12124 RaiseScore(level.score[SC_PEARL]);
12127 case EL_BD_BUTTERFLY:
12128 case EL_SP_ELECTRON:
12129 RaiseScore(level.score[SC_BUG]);
12132 case EL_BD_FIREFLY:
12133 case EL_SP_SNIKSNAK:
12134 RaiseScore(level.score[SC_SPACESHIP]);
12137 case EL_DARK_YAMYAM:
12138 RaiseScore(level.score[SC_YAMYAM]);
12141 RaiseScore(level.score[SC_ROBOT]);
12144 RaiseScore(level.score[SC_PACMAN]);
12147 RaiseScore(level.score[SC_NUT]);
12150 case EL_EM_DYNAMITE:
12151 case EL_SP_DISK_RED:
12152 case EL_DYNABOMB_INCREASE_NUMBER:
12153 case EL_DYNABOMB_INCREASE_SIZE:
12154 case EL_DYNABOMB_INCREASE_POWER:
12155 RaiseScore(level.score[SC_DYNAMITE]);
12157 case EL_SHIELD_NORMAL:
12158 case EL_SHIELD_DEADLY:
12159 RaiseScore(level.score[SC_SHIELD]);
12161 case EL_EXTRA_TIME:
12162 RaiseScore(level.extra_time_score);
12176 RaiseScore(level.score[SC_KEY]);
12179 RaiseScore(element_info[element].collect_score);
12184 void RequestQuitGame(boolean ask_if_really_quit)
12186 if (AllPlayersGone ||
12187 !ask_if_really_quit ||
12188 level_editor_test_game ||
12189 Request("Do you really want to quit the game ?",
12190 REQ_ASK | REQ_STAY_CLOSED))
12192 #if defined(NETWORK_AVALIABLE)
12193 if (options.network)
12194 SendToServer_StopPlaying();
12198 game_status = GAME_MODE_MAIN;
12204 if (tape.playing && tape.deactivate_display)
12205 TapeDeactivateDisplayOff(TRUE);
12207 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
12209 if (tape.playing && tape.deactivate_display)
12210 TapeDeactivateDisplayOn();
12215 /* ---------- new game button stuff ---------------------------------------- */
12217 /* graphic position values for game buttons */
12218 #define GAME_BUTTON_XSIZE 30
12219 #define GAME_BUTTON_YSIZE 30
12220 #define GAME_BUTTON_XPOS 5
12221 #define GAME_BUTTON_YPOS 215
12222 #define SOUND_BUTTON_XPOS 5
12223 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
12225 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
12226 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
12227 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
12228 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
12229 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
12230 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
12237 } gamebutton_info[NUM_GAME_BUTTONS] =
12240 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
12245 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
12246 GAME_CTRL_ID_PAUSE,
12250 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
12255 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
12256 SOUND_CTRL_ID_MUSIC,
12257 "background music on/off"
12260 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
12261 SOUND_CTRL_ID_LOOPS,
12262 "sound loops on/off"
12265 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
12266 SOUND_CTRL_ID_SIMPLE,
12267 "normal sounds on/off"
12271 void CreateGameButtons()
12275 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12277 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
12278 struct GadgetInfo *gi;
12281 unsigned long event_mask;
12282 int gd_xoffset, gd_yoffset;
12283 int gd_x1, gd_x2, gd_y1, gd_y2;
12286 gd_xoffset = gamebutton_info[i].x;
12287 gd_yoffset = gamebutton_info[i].y;
12288 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
12289 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
12291 if (id == GAME_CTRL_ID_STOP ||
12292 id == GAME_CTRL_ID_PAUSE ||
12293 id == GAME_CTRL_ID_PLAY)
12295 button_type = GD_TYPE_NORMAL_BUTTON;
12297 event_mask = GD_EVENT_RELEASED;
12298 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12299 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12303 button_type = GD_TYPE_CHECK_BUTTON;
12305 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
12306 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
12307 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
12308 event_mask = GD_EVENT_PRESSED;
12309 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
12310 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12313 gi = CreateGadget(GDI_CUSTOM_ID, id,
12314 GDI_INFO_TEXT, gamebutton_info[i].infotext,
12315 GDI_X, DX + gd_xoffset,
12316 GDI_Y, DY + gd_yoffset,
12317 GDI_WIDTH, GAME_BUTTON_XSIZE,
12318 GDI_HEIGHT, GAME_BUTTON_YSIZE,
12319 GDI_TYPE, button_type,
12320 GDI_STATE, GD_BUTTON_UNPRESSED,
12321 GDI_CHECKED, checked,
12322 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
12323 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
12324 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
12325 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
12326 GDI_EVENT_MASK, event_mask,
12327 GDI_CALLBACK_ACTION, HandleGameButtons,
12331 Error(ERR_EXIT, "cannot create gadget");
12333 game_gadget[id] = gi;
12337 void FreeGameButtons()
12341 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12342 FreeGadget(game_gadget[i]);
12345 static void MapGameButtons()
12349 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12350 MapGadget(game_gadget[i]);
12353 void UnmapGameButtons()
12357 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12358 UnmapGadget(game_gadget[i]);
12361 static void HandleGameButtons(struct GadgetInfo *gi)
12363 int id = gi->custom_id;
12365 if (game_status != GAME_MODE_PLAYING)
12370 case GAME_CTRL_ID_STOP:
12371 RequestQuitGame(TRUE);
12374 case GAME_CTRL_ID_PAUSE:
12375 if (options.network)
12377 #if defined(NETWORK_AVALIABLE)
12379 SendToServer_ContinuePlaying();
12381 SendToServer_PausePlaying();
12385 TapeTogglePause(TAPE_TOGGLE_MANUAL);
12388 case GAME_CTRL_ID_PLAY:
12391 #if defined(NETWORK_AVALIABLE)
12392 if (options.network)
12393 SendToServer_ContinuePlaying();
12397 tape.pausing = FALSE;
12398 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
12403 case SOUND_CTRL_ID_MUSIC:
12404 if (setup.sound_music)
12406 setup.sound_music = FALSE;
12409 else if (audio.music_available)
12411 setup.sound = setup.sound_music = TRUE;
12413 SetAudioMode(setup.sound);
12419 case SOUND_CTRL_ID_LOOPS:
12420 if (setup.sound_loops)
12421 setup.sound_loops = FALSE;
12422 else if (audio.loops_available)
12424 setup.sound = setup.sound_loops = TRUE;
12425 SetAudioMode(setup.sound);
12429 case SOUND_CTRL_ID_SIMPLE:
12430 if (setup.sound_simple)
12431 setup.sound_simple = FALSE;
12432 else if (audio.sound_available)
12434 setup.sound = setup.sound_simple = TRUE;
12435 SetAudioMode(setup.sound);