1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back! *
3 *----------------------------------------------------------*
4 * (c) 1995-2002 Artsoft Entertainment *
6 * Detmolder Strasse 189 *
9 * e-mail: info@artsoft.org *
10 *----------------------------------------------------------*
12 ***********************************************************/
14 #include "libgame/libgame.h"
24 /* EXPERIMENTAL STUFF */
25 #define USE_NEW_AMOEBA_CODE FALSE
27 /* EXPERIMENTAL STUFF */
28 #define USE_NEW_STUFF ( 1)
30 #define USE_NEW_SP_SLIPPERY (USE_NEW_STUFF * 1)
31 #define USE_NEW_CUSTOM_VALUE (USE_NEW_STUFF * 1)
32 #define USE_NEW_PLAYER_ANIM (USE_NEW_STUFF * 1)
33 #define USE_NEW_ALL_SLIPPERY (USE_NEW_STUFF * 1)
34 #define USE_NEW_PLAYER_SPEED (USE_NEW_STUFF * 1)
35 #define USE_NEW_DELAYED_ACTION (USE_NEW_STUFF * 1)
36 #define USE_NEW_SNAP_DELAY (USE_NEW_STUFF * 1)
37 #define USE_ONLY_ONE_CHANGE_PER_FRAME (USE_NEW_STUFF * 1)
38 #define USE_ONE_MORE_CHANGE_PER_FRAME (USE_NEW_STUFF * 1)
39 #define USE_FIXED_DONT_RUN_INTO (USE_NEW_STUFF * 1)
40 #define USE_NEW_SPRING_BUMPER (USE_NEW_STUFF * 1)
41 #define USE_STOP_CHANGED_ELEMENTS (USE_NEW_STUFF * 1)
42 #define USE_ELEMENT_TOUCHING_BUGFIX (USE_NEW_STUFF * 1)
43 #define USE_NEW_CONTINUOUS_SNAPPING (USE_NEW_STUFF * 1)
45 #define USE_QUICKSAND_IMPACT_BUGFIX (USE_NEW_STUFF * 0)
52 /* for MovePlayer() */
53 #define MP_NO_ACTION 0
56 #define MP_DONT_RUN_INTO (MP_MOVING | MP_ACTION)
58 /* for ScrollPlayer() */
60 #define SCROLL_GO_ON 1
62 /* for Bang()/Explode() */
63 #define EX_PHASE_START 0
64 #define EX_TYPE_NONE 0
65 #define EX_TYPE_NORMAL (1 << 0)
66 #define EX_TYPE_CENTER (1 << 1)
67 #define EX_TYPE_BORDER (1 << 2)
68 #define EX_TYPE_CROSS (1 << 3)
69 #define EX_TYPE_DYNA (1 << 4)
70 #define EX_TYPE_SINGLE_TILE (EX_TYPE_CENTER | EX_TYPE_BORDER)
72 /* special positions in the game control window (relative to control window) */
75 #define XX_EMERALDS 29
76 #define YY_EMERALDS 54
77 #define XX_DYNAMITE 29
78 #define YY_DYNAMITE 89
87 /* special positions in the game control window (relative to main window) */
88 #define DX_LEVEL (DX + XX_LEVEL)
89 #define DY_LEVEL (DY + YY_LEVEL)
90 #define DX_EMERALDS (DX + XX_EMERALDS)
91 #define DY_EMERALDS (DY + YY_EMERALDS)
92 #define DX_DYNAMITE (DX + XX_DYNAMITE)
93 #define DY_DYNAMITE (DY + YY_DYNAMITE)
94 #define DX_KEYS (DX + XX_KEYS)
95 #define DY_KEYS (DY + YY_KEYS)
96 #define DX_SCORE (DX + XX_SCORE)
97 #define DY_SCORE (DY + YY_SCORE)
98 #define DX_TIME1 (DX + XX_TIME1)
99 #define DX_TIME2 (DX + XX_TIME2)
100 #define DY_TIME (DY + YY_TIME)
102 /* values for delayed check of falling and moving elements and for collision */
103 #define CHECK_DELAY_MOVING 3
104 #define CHECK_DELAY_FALLING 3
105 #define CHECK_DELAY_COLLISION 2
107 /* values for initial player move delay (initial delay counter value) */
108 #define INITIAL_MOVE_DELAY_OFF -1
109 #define INITIAL_MOVE_DELAY_ON 0
111 /* values for player movement speed (which is in fact a delay value) */
112 #define MOVE_DELAY_MIN_SPEED 32
113 #define MOVE_DELAY_NORMAL_SPEED 8
114 #define MOVE_DELAY_HIGH_SPEED 4
115 #define MOVE_DELAY_MAX_SPEED 1
118 #define DOUBLE_MOVE_DELAY(x) (x = (x <= MOVE_DELAY_HIGH_SPEED ? x * 2 : x))
119 #define HALVE_MOVE_DELAY(x) (x = (x >= MOVE_DELAY_HIGH_SPEED ? x / 2 : x))
121 #define DOUBLE_MOVE_DELAY(x) (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
122 #define HALVE_MOVE_DELAY(x) (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
124 #define DOUBLE_PLAYER_SPEED(p) (HALVE_MOVE_DELAY((p)->move_delay_value))
125 #define HALVE_PLAYER_SPEED(p) (DOUBLE_MOVE_DELAY((p)->move_delay_value))
127 /* values for other actions */
128 #define MOVE_STEPSIZE_NORMAL (TILEX / MOVE_DELAY_NORMAL_SPEED)
129 #define MOVE_STEPSIZE_MIN (1)
130 #define MOVE_STEPSIZE_MAX (TILEX)
132 #define GET_DX_FROM_DIR(d) ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
133 #define GET_DY_FROM_DIR(d) ((d) == MV_UP ? -1 : (d) == MV_DOWN ? 1 : 0)
135 #define INIT_GFX_RANDOM() (SimpleRND(1000000))
137 #define GET_NEW_PUSH_DELAY(e) ( (element_info[e].push_delay_fixed) + \
138 RND(element_info[e].push_delay_random))
139 #define GET_NEW_DROP_DELAY(e) ( (element_info[e].drop_delay_fixed) + \
140 RND(element_info[e].drop_delay_random))
141 #define GET_NEW_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
142 RND(element_info[e].move_delay_random))
143 #define GET_MAX_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
144 (element_info[e].move_delay_random))
145 #define GET_NEW_CUSTOM_VALUE(e) ( (element_info[e].ce_value_fixed_initial) +\
146 RND(element_info[e].ce_value_random_initial))
147 #define GET_CHANGE_DELAY(c) ( ((c)->delay_fixed * (c)->delay_frames) + \
148 RND((c)->delay_random * (c)->delay_frames))
149 #define GET_CE_DELAY_VALUE(c) ( ((c)->delay_fixed) + \
150 RND((c)->delay_random))
153 #define GET_VALID_RUNTIME_ELEMENT(e) \
154 ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
156 #define GET_VALID_FILE_ELEMENT(e) \
157 ((e) >= NUM_FILE_ELEMENTS ? EL_UNKNOWN : (e))
160 #define GET_TARGET_ELEMENT(e, ch) \
161 ((e) == EL_TRIGGER_PLAYER ? (ch)->actual_trigger_player : \
162 (e) == EL_TRIGGER_ELEMENT ? (ch)->actual_trigger_element : \
163 (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value : (e))
165 #define CAN_GROW_INTO(e) \
166 ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
168 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition) \
169 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
172 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition) \
173 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
174 (CAN_MOVE_INTO_ACID(e) && \
175 Feld[x][y] == EL_ACID) || \
178 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition) \
179 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
180 (CAN_MOVE_INTO_ACID(e) && \
181 Feld[x][y] == EL_ACID) || \
184 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition) \
185 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
187 (CAN_MOVE_INTO_ACID(e) && \
188 Feld[x][y] == EL_ACID) || \
189 (DONT_COLLIDE_WITH(e) && \
191 !PLAYER_ENEMY_PROTECTED(x, y))))
193 #define ELEMENT_CAN_ENTER_FIELD(e, x, y) \
194 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
196 #define SATELLITE_CAN_ENTER_FIELD(x, y) \
197 ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
199 #define ANDROID_CAN_ENTER_FIELD(e, x, y) \
200 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
202 #define ANDROID_CAN_CLONE_FIELD(x, y) \
203 (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
204 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
206 #define ENEMY_CAN_ENTER_FIELD(e, x, y) \
207 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
209 #define YAMYAM_CAN_ENTER_FIELD(e, x, y) \
210 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
212 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y) \
213 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
215 #define PACMAN_CAN_ENTER_FIELD(e, x, y) \
216 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
218 #define PIG_CAN_ENTER_FIELD(e, x, y) \
219 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
221 #define PENGUIN_CAN_ENTER_FIELD(e, x, y) \
222 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN ||\
223 IS_FOOD_PENGUIN(Feld[x][y])))
224 #define DRAGON_CAN_ENTER_FIELD(e, x, y) \
225 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
227 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition) \
228 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
230 #define SPRING_CAN_ENTER_FIELD(e, x, y) \
231 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
233 #define SPRING_CAN_BUMP_FROM_FIELD(x, y) \
234 (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER || \
235 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
238 #define GROUP_NR(e) ((e) - EL_GROUP_START)
239 #define IS_IN_GROUP(e, nr) (element_info[e].in_group[nr] == TRUE)
240 #define IS_IN_GROUP_EL(e, ge) (IS_IN_GROUP(e, (ge) - EL_GROUP_START))
242 #define IS_EQUAL_OR_IN_GROUP(e, ge) \
243 (IS_GROUP_ELEMENT(ge) ? IS_IN_GROUP(e, GROUP_NR(ge)) : (e) == (ge))
246 #define MOVE_ENTER_EL(e) (element_info[e].move_enter_element)
248 #define CE_ENTER_FIELD_COND(e, x, y) \
249 (!IS_PLAYER(x, y) && \
250 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
252 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y) \
253 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
255 #define IN_LEV_FIELD_AND_IS_FREE(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
256 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
258 #define ACCESS_FROM(e, d) (element_info[e].access_direction &(d))
259 #define IS_WALKABLE_FROM(e, d) (IS_WALKABLE(e) && ACCESS_FROM(e, d))
260 #define IS_PASSABLE_FROM(e, d) (IS_PASSABLE(e) && ACCESS_FROM(e, d))
261 #define IS_ACCESSIBLE_FROM(e, d) (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
263 /* game button identifiers */
264 #define GAME_CTRL_ID_STOP 0
265 #define GAME_CTRL_ID_PAUSE 1
266 #define GAME_CTRL_ID_PLAY 2
267 #define SOUND_CTRL_ID_MUSIC 3
268 #define SOUND_CTRL_ID_LOOPS 4
269 #define SOUND_CTRL_ID_SIMPLE 5
271 #define NUM_GAME_BUTTONS 6
274 /* forward declaration for internal use */
276 static void CreateField(int, int, int);
278 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
279 static void AdvanceFrameAndPlayerCounters(int);
281 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
282 static boolean MovePlayer(struct PlayerInfo *, int, int);
283 static void ScrollPlayer(struct PlayerInfo *, int);
284 static void ScrollScreen(struct PlayerInfo *, int);
286 int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
288 static void InitBeltMovement(void);
289 static void CloseAllOpenTimegates(void);
290 static void CheckGravityMovement(struct PlayerInfo *);
291 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
292 static void KillPlayerUnlessEnemyProtected(int, int);
293 static void KillPlayerUnlessExplosionProtected(int, int);
295 static void TestIfPlayerTouchesCustomElement(int, int);
296 static void TestIfElementTouchesCustomElement(int, int);
297 static void TestIfElementHitsCustomElement(int, int, int);
299 static void TestIfElementSmashesCustomElement(int, int, int);
302 static void HandleElementChange(int, int, int);
303 static void ExecuteCustomElementAction(int, int, int, int);
304 static boolean ChangeElement(int, int, int, int);
306 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
307 #define CheckTriggeredElementChange(x, y, e, ev) \
308 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
309 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s) \
310 CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
311 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s) \
312 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
313 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p) \
314 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
316 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
317 #define CheckElementChange(x, y, e, te, ev) \
318 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
319 #define CheckElementChangeByPlayer(x, y, e, ev, p, s) \
320 CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
321 #define CheckElementChangeBySide(x, y, e, te, ev, s) \
322 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
324 static void PlayLevelSound(int, int, int);
325 static void PlayLevelSoundNearest(int, int, int);
326 static void PlayLevelSoundAction(int, int, int);
327 static void PlayLevelSoundElementAction(int, int, int, int);
328 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
329 static void PlayLevelSoundActionIfLoop(int, int, int);
330 static void StopLevelSoundActionIfLoop(int, int, int);
331 static void PlayLevelMusic();
333 static void MapGameButtons();
334 static void HandleGameButtons(struct GadgetInfo *);
336 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
339 /* ------------------------------------------------------------------------- */
340 /* definition of elements that automatically change to other elements after */
341 /* a specified time, eventually calling a function when changing */
342 /* ------------------------------------------------------------------------- */
344 /* forward declaration for changer functions */
345 static void InitBuggyBase(int, int);
346 static void WarnBuggyBase(int, int);
348 static void InitTrap(int, int);
349 static void ActivateTrap(int, int);
350 static void ChangeActiveTrap(int, int);
352 static void InitRobotWheel(int, int);
353 static void RunRobotWheel(int, int);
354 static void StopRobotWheel(int, int);
356 static void InitTimegateWheel(int, int);
357 static void RunTimegateWheel(int, int);
359 static void InitMagicBallDelay(int, int);
360 static void ActivateMagicBall(int, int);
362 static void InitDiagonalMovingElement(int, int);
364 struct ChangingElementInfo
369 void (*pre_change_function)(int x, int y);
370 void (*change_function)(int x, int y);
371 void (*post_change_function)(int x, int y);
374 static struct ChangingElementInfo change_delay_list[] =
425 EL_SWITCHGATE_OPENING,
433 EL_SWITCHGATE_CLOSING,
434 EL_SWITCHGATE_CLOSED,
466 EL_ACID_SPLASH_RIGHT,
475 EL_SP_BUGGY_BASE_ACTIVATING,
482 EL_SP_BUGGY_BASE_ACTIVATING,
483 EL_SP_BUGGY_BASE_ACTIVE,
490 EL_SP_BUGGY_BASE_ACTIVE,
514 EL_ROBOT_WHEEL_ACTIVE,
522 EL_TIMEGATE_SWITCH_ACTIVE,
530 EL_EMC_MAGIC_BALL_ACTIVE,
531 EL_EMC_MAGIC_BALL_ACTIVE,
538 EL_EMC_SPRING_BUMPER_ACTIVE,
539 EL_EMC_SPRING_BUMPER,
546 EL_DIAGONAL_SHRINKING,
559 InitDiagonalMovingElement
575 int push_delay_fixed, push_delay_random;
580 { EL_BALLOON, 0, 0 },
582 { EL_SOKOBAN_OBJECT, 2, 0 },
583 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
584 { EL_SATELLITE, 2, 0 },
585 { EL_SP_DISK_YELLOW, 2, 0 },
587 { EL_UNDEFINED, 0, 0 },
595 move_stepsize_list[] =
597 { EL_AMOEBA_DROP, 2 },
598 { EL_AMOEBA_DROPPING, 2 },
599 { EL_QUICKSAND_FILLING, 1 },
600 { EL_QUICKSAND_EMPTYING, 1 },
601 { EL_MAGIC_WALL_FILLING, 2 },
602 { EL_BD_MAGIC_WALL_FILLING, 2 },
603 { EL_MAGIC_WALL_EMPTYING, 2 },
604 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
614 collect_count_list[] =
617 { EL_BD_DIAMOND, 1 },
618 { EL_EMERALD_YELLOW, 1 },
619 { EL_EMERALD_RED, 1 },
620 { EL_EMERALD_PURPLE, 1 },
622 { EL_SP_INFOTRON, 1 },
634 access_direction_list[] =
636 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
637 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
638 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
639 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
640 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
641 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
642 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
643 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
644 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
645 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
646 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
648 { EL_SP_PORT_LEFT, MV_RIGHT },
649 { EL_SP_PORT_RIGHT, MV_LEFT },
650 { EL_SP_PORT_UP, MV_DOWN },
651 { EL_SP_PORT_DOWN, MV_UP },
652 { EL_SP_PORT_HORIZONTAL, MV_LEFT | MV_RIGHT },
653 { EL_SP_PORT_VERTICAL, MV_UP | MV_DOWN },
654 { EL_SP_PORT_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
655 { EL_SP_GRAVITY_PORT_LEFT, MV_RIGHT },
656 { EL_SP_GRAVITY_PORT_RIGHT, MV_LEFT },
657 { EL_SP_GRAVITY_PORT_UP, MV_DOWN },
658 { EL_SP_GRAVITY_PORT_DOWN, MV_UP },
659 { EL_SP_GRAVITY_ON_PORT_LEFT, MV_RIGHT },
660 { EL_SP_GRAVITY_ON_PORT_RIGHT, MV_LEFT },
661 { EL_SP_GRAVITY_ON_PORT_UP, MV_DOWN },
662 { EL_SP_GRAVITY_ON_PORT_DOWN, MV_UP },
663 { EL_SP_GRAVITY_OFF_PORT_LEFT, MV_RIGHT },
664 { EL_SP_GRAVITY_OFF_PORT_RIGHT, MV_LEFT },
665 { EL_SP_GRAVITY_OFF_PORT_UP, MV_DOWN },
666 { EL_SP_GRAVITY_OFF_PORT_DOWN, MV_UP },
668 { EL_UNDEFINED, MV_NONE }
671 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
673 #define IS_AUTO_CHANGING(e) (element_info[e].has_change_event[CE_DELAY])
674 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
675 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
676 IS_JUST_CHANGING(x, y))
678 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
680 /* static variables for playfield scan mode (scanning forward or backward) */
681 static int playfield_scan_start_x = 0;
682 static int playfield_scan_start_y = 0;
683 static int playfield_scan_delta_x = 1;
684 static int playfield_scan_delta_y = 1;
686 #define SCAN_PLAYFIELD(x, y) for ((y) = playfield_scan_start_y; \
687 (y) >= 0 && (y) <= lev_fieldy - 1; \
688 (y) += playfield_scan_delta_y) \
689 for ((x) = playfield_scan_start_x; \
690 (x) >= 0 && (x) <= lev_fieldx - 1; \
691 (x) += playfield_scan_delta_x) \
693 static void InitPlayfieldScanModeVars()
695 if (game.use_reverse_scan_direction)
697 playfield_scan_start_x = lev_fieldx - 1;
698 playfield_scan_start_y = lev_fieldy - 1;
700 playfield_scan_delta_x = -1;
701 playfield_scan_delta_y = -1;
705 playfield_scan_start_x = 0;
706 playfield_scan_start_y = 0;
708 playfield_scan_delta_x = 1;
709 playfield_scan_delta_y = 1;
713 static void InitPlayfieldScanMode(int mode)
715 game.use_reverse_scan_direction =
716 (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
718 InitPlayfieldScanModeVars();
721 static int get_move_delay_from_stepsize(int move_stepsize)
724 MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
726 /* make sure that stepsize value is always a power of 2 */
727 move_stepsize = (1 << log_2(move_stepsize));
729 return TILEX / move_stepsize;
732 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
735 int move_delay = get_move_delay_from_stepsize(move_stepsize);
736 boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
738 /* do no immediately change move delay -- the player might just be moving */
739 player->move_delay_value_next = move_delay;
741 /* information if player can move must be set separately */
742 player->cannot_move = cannot_move;
746 player->move_delay = game.initial_move_delay;
747 player->move_delay_value = game.initial_move_delay_value;
749 player->move_delay_value_next = -1;
751 player->move_delay_reset_counter = 0;
755 void GetPlayerConfig()
757 if (!audio.sound_available)
758 setup.sound_simple = FALSE;
760 if (!audio.loops_available)
761 setup.sound_loops = FALSE;
763 if (!audio.music_available)
764 setup.sound_music = FALSE;
766 if (!video.fullscreen_available)
767 setup.fullscreen = FALSE;
769 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
771 SetAudioMode(setup.sound);
775 static int getBeltNrFromBeltElement(int element)
777 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
778 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
779 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
782 static int getBeltNrFromBeltActiveElement(int element)
784 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
785 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
786 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
789 static int getBeltNrFromBeltSwitchElement(int element)
791 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
792 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
793 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
796 static int getBeltDirNrFromBeltSwitchElement(int element)
798 static int belt_base_element[4] =
800 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
801 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
802 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
803 EL_CONVEYOR_BELT_4_SWITCH_LEFT
806 int belt_nr = getBeltNrFromBeltSwitchElement(element);
807 int belt_dir_nr = element - belt_base_element[belt_nr];
809 return (belt_dir_nr % 3);
812 static int getBeltDirFromBeltSwitchElement(int element)
814 static int belt_move_dir[3] =
821 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
823 return belt_move_dir[belt_dir_nr];
826 static void InitPlayerField(int x, int y, int element, boolean init_game)
828 if (element == EL_SP_MURPHY)
832 if (stored_player[0].present)
834 Feld[x][y] = EL_SP_MURPHY_CLONE;
840 stored_player[0].use_murphy = TRUE;
842 if (!level.use_artwork_element[0])
843 stored_player[0].artwork_element = EL_SP_MURPHY;
846 Feld[x][y] = EL_PLAYER_1;
852 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
853 int jx = player->jx, jy = player->jy;
855 player->present = TRUE;
857 player->block_last_field = (element == EL_SP_MURPHY ?
858 level.sp_block_last_field :
859 level.block_last_field);
861 /* ---------- initialize player's last field block delay --------------- */
863 /* always start with reliable default value (no adjustment needed) */
864 player->block_delay_adjustment = 0;
866 /* special case 1: in Supaplex, Murphy blocks last field one more frame */
867 if (player->block_last_field && element == EL_SP_MURPHY)
868 player->block_delay_adjustment = 1;
870 /* special case 2: in game engines before 3.1.1, blocking was different */
871 if (game.use_block_last_field_bug)
872 player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
874 if (!options.network || player->connected)
876 player->active = TRUE;
878 /* remove potentially duplicate players */
879 if (StorePlayer[jx][jy] == Feld[x][y])
880 StorePlayer[jx][jy] = 0;
882 StorePlayer[x][y] = Feld[x][y];
886 printf("Player %d activated.\n", player->element_nr);
887 printf("[Local player is %d and currently %s.]\n",
888 local_player->element_nr,
889 local_player->active ? "active" : "not active");
893 Feld[x][y] = EL_EMPTY;
895 player->jx = player->last_jx = x;
896 player->jy = player->last_jy = y;
900 static void InitField(int x, int y, boolean init_game)
902 int element = Feld[x][y];
911 InitPlayerField(x, y, element, init_game);
914 case EL_SOKOBAN_FIELD_PLAYER:
915 element = Feld[x][y] = EL_PLAYER_1;
916 InitField(x, y, init_game);
918 element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
919 InitField(x, y, init_game);
922 case EL_SOKOBAN_FIELD_EMPTY:
923 local_player->sokobanfields_still_needed++;
927 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
928 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
929 else if (x > 0 && Feld[x-1][y] == EL_ACID)
930 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
931 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
932 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
933 else if (y > 0 && Feld[x][y-1] == EL_ACID)
934 Feld[x][y] = EL_ACID_POOL_BOTTOM;
935 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
936 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
945 case EL_SPACESHIP_RIGHT:
946 case EL_SPACESHIP_UP:
947 case EL_SPACESHIP_LEFT:
948 case EL_SPACESHIP_DOWN:
949 case EL_BD_BUTTERFLY:
950 case EL_BD_BUTTERFLY_RIGHT:
951 case EL_BD_BUTTERFLY_UP:
952 case EL_BD_BUTTERFLY_LEFT:
953 case EL_BD_BUTTERFLY_DOWN:
955 case EL_BD_FIREFLY_RIGHT:
956 case EL_BD_FIREFLY_UP:
957 case EL_BD_FIREFLY_LEFT:
958 case EL_BD_FIREFLY_DOWN:
959 case EL_PACMAN_RIGHT:
965 case EL_YAMYAM_RIGHT:
987 if (y == lev_fieldy - 1)
989 Feld[x][y] = EL_AMOEBA_GROWING;
990 Store[x][y] = EL_AMOEBA_WET;
994 case EL_DYNAMITE_ACTIVE:
995 case EL_SP_DISK_RED_ACTIVE:
996 case EL_DYNABOMB_PLAYER_1_ACTIVE:
997 case EL_DYNABOMB_PLAYER_2_ACTIVE:
998 case EL_DYNABOMB_PLAYER_3_ACTIVE:
999 case EL_DYNABOMB_PLAYER_4_ACTIVE:
1000 MovDelay[x][y] = 96;
1003 case EL_EM_DYNAMITE_ACTIVE:
1004 MovDelay[x][y] = 32;
1008 local_player->lights_still_needed++;
1012 local_player->friends_still_needed++;
1017 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1020 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1021 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1022 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1023 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1024 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1025 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1026 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1027 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1028 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1029 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1030 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1031 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1034 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1035 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1036 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1038 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
1040 game.belt_dir[belt_nr] = belt_dir;
1041 game.belt_dir_nr[belt_nr] = belt_dir_nr;
1043 else /* more than one switch -- set it like the first switch */
1045 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1050 case EL_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
1052 Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
1055 case EL_LIGHT_SWITCH_ACTIVE:
1057 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1060 case EL_EMC_MAGIC_BALL:
1061 if (game.ball_state)
1062 Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1065 case EL_EMC_MAGIC_BALL_SWITCH:
1066 if (game.ball_state)
1067 Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1072 if (IS_CUSTOM_ELEMENT(element))
1074 if (CAN_MOVE(element))
1077 #if USE_NEW_CUSTOM_VALUE
1078 if (!element_info[element].use_last_ce_value || init_game)
1079 CustomValue[x][y] = GET_NEW_CUSTOM_VALUE(Feld[x][y]);
1083 if (IS_CUSTOM_ELEMENT(element) && CAN_MOVE(element))
1086 else if (IS_GROUP_ELEMENT(element))
1088 struct ElementGroupInfo *group = element_info[element].group;
1089 int last_anim_random_frame = gfx.anim_random_frame;
1092 if (group->choice_mode == ANIM_RANDOM)
1093 gfx.anim_random_frame = RND(group->num_elements_resolved);
1095 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1096 group->choice_mode, 0,
1099 if (group->choice_mode == ANIM_RANDOM)
1100 gfx.anim_random_frame = last_anim_random_frame;
1102 group->choice_pos++;
1104 Feld[x][y] = group->element_resolved[element_pos];
1106 InitField(x, y, init_game);
1113 CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1118 #if USE_NEW_CUSTOM_VALUE
1121 CustomValue[x][y] = GET_NEW_CUSTOM_VALUE(Feld[x][y]);
1123 CustomValue[x][y] = element_info[Feld[x][y]].custom_value_initial;
1131 static inline void InitField_WithBug1(int x, int y, boolean init_game)
1133 InitField(x, y, init_game);
1135 /* not needed to call InitMovDir() -- already done by InitField()! */
1136 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1137 CAN_MOVE(Feld[x][y]))
1141 static inline void InitField_WithBug2(int x, int y, boolean init_game)
1143 int old_element = Feld[x][y];
1145 InitField(x, y, init_game);
1147 /* not needed to call InitMovDir() -- already done by InitField()! */
1148 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1149 CAN_MOVE(old_element) &&
1150 (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1153 /* this case is in fact a combination of not less than three bugs:
1154 first, it calls InitMovDir() for elements that can move, although this is
1155 already done by InitField(); then, it checks the element that was at this
1156 field _before_ the call to InitField() (which can change it); lastly, it
1157 was not called for "mole with direction" elements, which were treated as
1158 "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1162 inline void DrawGameValue_Emeralds(int value)
1164 DrawText(DX_EMERALDS, DY_EMERALDS, int2str(value, 3), FONT_TEXT_2);
1167 inline void DrawGameValue_Dynamite(int value)
1169 DrawText(DX_DYNAMITE, DY_DYNAMITE, int2str(value, 3), FONT_TEXT_2);
1172 inline void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
1176 /* currently only 4 of 8 possible keys are displayed */
1177 for (i = 0; i < STD_NUM_KEYS; i++)
1180 DrawMiniGraphicExt(drawto, DX_KEYS + i * MINI_TILEX, DY_KEYS,
1181 el2edimg(EL_KEY_1 + i));
1183 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
1184 DOOR_GFX_PAGEX5 + XX_KEYS + i * MINI_TILEX, YY_KEYS,
1185 MINI_TILEX, MINI_TILEY, DX_KEYS + i * MINI_TILEX, DY_KEYS);
1189 inline void DrawGameValue_Score(int value)
1191 DrawText(DX_SCORE, DY_SCORE, int2str(value, 5), FONT_TEXT_2);
1194 inline void DrawGameValue_Time(int value)
1197 DrawText(DX_TIME1, DY_TIME, int2str(value, 3), FONT_TEXT_2);
1199 DrawText(DX_TIME2, DY_TIME, int2str(value, 4), FONT_LEVEL_NUMBER);
1202 inline void DrawGameValue_Level(int value)
1205 DrawText(DX_LEVEL, DY_LEVEL, int2str(value, 2), FONT_TEXT_2);
1208 /* misuse area for displaying emeralds to draw bigger level number */
1209 DrawTextExt(drawto, DX_EMERALDS, DY_EMERALDS,
1210 int2str(value, 3), FONT_LEVEL_NUMBER, BLIT_OPAQUE);
1212 /* now copy it to the area for displaying level number */
1213 BlitBitmap(drawto, drawto,
1214 DX_EMERALDS, DY_EMERALDS + 1,
1215 getFontWidth(FONT_LEVEL_NUMBER) * 3,
1216 getFontHeight(FONT_LEVEL_NUMBER) - 1,
1217 DX_LEVEL - 1, DY_LEVEL + 1);
1219 /* restore the area for displaying emeralds */
1220 DrawGameValue_Emeralds(local_player->gems_still_needed);
1222 /* yes, this is all really ugly :-) */
1226 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
1229 int key[MAX_NUM_KEYS];
1232 for (i = 0; i < MAX_NUM_KEYS; i++)
1233 key[i] = key_bits & (1 << i);
1235 DrawGameValue_Level(level_nr);
1237 DrawGameValue_Emeralds(emeralds);
1238 DrawGameValue_Dynamite(dynamite);
1239 DrawGameValue_Score(score);
1240 DrawGameValue_Time(time);
1242 DrawGameValue_Keys(key);
1245 void DrawGameDoorValues()
1249 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
1251 DrawGameDoorValues_EM();
1256 DrawGameValue_Level(level_nr);
1258 DrawGameValue_Emeralds(local_player->gems_still_needed);
1259 DrawGameValue_Dynamite(local_player->inventory_size);
1260 DrawGameValue_Score(local_player->score);
1261 DrawGameValue_Time(TimeLeft);
1263 for (i = 0; i < MAX_PLAYERS; i++)
1264 DrawGameValue_Keys(stored_player[i].key);
1268 static void resolve_group_element(int group_element, int recursion_depth)
1270 static int group_nr;
1271 static struct ElementGroupInfo *group;
1272 struct ElementGroupInfo *actual_group = element_info[group_element].group;
1275 if (recursion_depth > NUM_GROUP_ELEMENTS) /* recursion too deep */
1277 Error(ERR_WARN, "recursion too deep when resolving group element %d",
1278 group_element - EL_GROUP_START + 1);
1280 /* replace element which caused too deep recursion by question mark */
1281 group->element_resolved[group->num_elements_resolved++] = EL_UNKNOWN;
1286 if (recursion_depth == 0) /* initialization */
1288 group = element_info[group_element].group;
1289 group_nr = group_element - EL_GROUP_START;
1291 group->num_elements_resolved = 0;
1292 group->choice_pos = 0;
1295 for (i = 0; i < actual_group->num_elements; i++)
1297 int element = actual_group->element[i];
1299 if (group->num_elements_resolved == NUM_FILE_ELEMENTS)
1302 if (IS_GROUP_ELEMENT(element))
1303 resolve_group_element(element, recursion_depth + 1);
1306 group->element_resolved[group->num_elements_resolved++] = element;
1307 element_info[element].in_group[group_nr] = TRUE;
1314 =============================================================================
1316 -----------------------------------------------------------------------------
1317 initialize game engine due to level / tape version number
1318 =============================================================================
1321 static void InitGameEngine()
1323 int i, j, k, l, x, y;
1325 /* set game engine from tape file when re-playing, else from level file */
1326 game.engine_version = (tape.playing ? tape.engine_version :
1327 level.game_version);
1329 /* ---------------------------------------------------------------------- */
1330 /* set flags for bugs and changes according to active game engine version */
1331 /* ---------------------------------------------------------------------- */
1334 Summary of bugfix/change:
1335 Fixed handling for custom elements that change when pushed by the player.
1337 Fixed/changed in version:
1341 Before 3.1.0, custom elements that "change when pushing" changed directly
1342 after the player started pushing them (until then handled in "DigField()").
1343 Since 3.1.0, these custom elements are not changed until the "pushing"
1344 move of the element is finished (now handled in "ContinueMoving()").
1346 Affected levels/tapes:
1347 The first condition is generally needed for all levels/tapes before version
1348 3.1.0, which might use the old behaviour before it was changed; known tapes
1349 that are affected are some tapes from the level set "Walpurgis Gardens" by
1351 The second condition is an exception from the above case and is needed for
1352 the special case of tapes recorded with game (not engine!) version 3.1.0 or
1353 above (including some development versions of 3.1.0), but before it was
1354 known that this change would break tapes like the above and was fixed in
1355 3.1.1, so that the changed behaviour was active although the engine version
1356 while recording maybe was before 3.1.0. There is at least one tape that is
1357 affected by this exception, which is the tape for the one-level set "Bug
1358 Machine" by Juergen Bonhagen.
1361 game.use_change_when_pushing_bug =
1362 (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1364 tape.game_version >= VERSION_IDENT(3,1,0,0) &&
1365 tape.game_version < VERSION_IDENT(3,1,1,0)));
1368 Summary of bugfix/change:
1369 Fixed handling for blocking the field the player leaves when moving.
1371 Fixed/changed in version:
1375 Before 3.1.1, when "block last field when moving" was enabled, the field
1376 the player is leaving when moving was blocked for the time of the move,
1377 and was directly unblocked afterwards. This resulted in the last field
1378 being blocked for exactly one less than the number of frames of one player
1379 move. Additionally, even when blocking was disabled, the last field was
1380 blocked for exactly one frame.
1381 Since 3.1.1, due to changes in player movement handling, the last field
1382 is not blocked at all when blocking is disabled. When blocking is enabled,
1383 the last field is blocked for exactly the number of frames of one player
1384 move. Additionally, if the player is Murphy, the hero of Supaplex, the
1385 last field is blocked for exactly one more than the number of frames of
1388 Affected levels/tapes:
1389 (!!! yet to be determined -- probably many !!!)
1392 game.use_block_last_field_bug =
1393 (game.engine_version < VERSION_IDENT(3,1,1,0));
1396 Summary of bugfix/change:
1397 Changed behaviour of CE changes with multiple changes per single frame.
1399 Fixed/changed in version:
1403 Before 3.2.0-6, only one single CE change was allowed in each engine frame.
1404 This resulted in race conditions where CEs seem to behave strange in some
1405 situations (where triggered CE changes were just skipped because there was
1406 already a CE change on that tile in the playfield in that engine frame).
1407 Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
1408 (The number of changes per frame must be limited in any case, because else
1409 it is easily possible to define CE changes that would result in an infinite
1410 loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
1411 should be set large enough so that it would only be reached in cases where
1412 the corresponding CE change conditions run into a loop. Therefore, it seems
1413 to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
1414 maximal number of change pages for custom elements.)
1416 Affected levels/tapes:
1420 #if USE_ONLY_ONE_CHANGE_PER_FRAME
1421 game.max_num_changes_per_frame = 1;
1423 game.max_num_changes_per_frame =
1424 (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
1427 /* ---------------------------------------------------------------------- */
1429 /* default scan direction: scan playfield from top/left to bottom/right */
1430 InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
1432 /* dynamically adjust element properties according to game engine version */
1433 InitElementPropertiesEngine(game.engine_version);
1436 printf("level %d: level version == %06d\n", level_nr, level.game_version);
1437 printf(" tape version == %06d [%s] [file: %06d]\n",
1438 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
1440 printf(" => game.engine_version == %06d\n", game.engine_version);
1444 /* ---------- recursively resolve group elements ------------------------- */
1446 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1447 for (j = 0; j < NUM_GROUP_ELEMENTS; j++)
1448 element_info[i].in_group[j] = FALSE;
1450 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
1451 resolve_group_element(EL_GROUP_START + i, 0);
1454 /* ---------- initialize player's initial move delay --------------------- */
1457 /* dynamically adjust player properties according to level information */
1458 game.initial_move_delay_value =
1459 get_move_delay_from_stepsize(level.initial_player_stepsize);
1461 /* dynamically adjust player properties according to level information */
1462 game.initial_move_delay_value =
1463 (level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED);
1466 /* dynamically adjust player properties according to game engine version */
1467 game.initial_move_delay = (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
1468 game.initial_move_delay_value : 0);
1470 /* ---------- initialize player's initial push delay --------------------- */
1472 /* dynamically adjust player properties according to game engine version */
1473 game.initial_push_delay_value =
1474 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
1476 /* ---------- initialize changing elements ------------------------------- */
1478 /* initialize changing elements information */
1479 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1481 struct ElementInfo *ei = &element_info[i];
1483 /* this pointer might have been changed in the level editor */
1484 ei->change = &ei->change_page[0];
1486 if (!IS_CUSTOM_ELEMENT(i))
1488 ei->change->target_element = EL_EMPTY_SPACE;
1489 ei->change->delay_fixed = 0;
1490 ei->change->delay_random = 0;
1491 ei->change->delay_frames = 1;
1494 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1496 ei->has_change_event[j] = FALSE;
1498 ei->event_page_nr[j] = 0;
1499 ei->event_page[j] = &ei->change_page[0];
1503 /* add changing elements from pre-defined list */
1504 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
1506 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
1507 struct ElementInfo *ei = &element_info[ch_delay->element];
1509 ei->change->target_element = ch_delay->target_element;
1510 ei->change->delay_fixed = ch_delay->change_delay;
1512 ei->change->pre_change_function = ch_delay->pre_change_function;
1513 ei->change->change_function = ch_delay->change_function;
1514 ei->change->post_change_function = ch_delay->post_change_function;
1516 ei->change->can_change = TRUE;
1517 ei->change->can_change_or_has_action = TRUE;
1519 ei->has_change_event[CE_DELAY] = TRUE;
1521 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
1522 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
1525 /* ---------- initialize internal run-time variables ------------- */
1527 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1529 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1531 for (j = 0; j < ei->num_change_pages; j++)
1533 ei->change_page[j].can_change_or_has_action =
1534 (ei->change_page[j].can_change |
1535 ei->change_page[j].has_action);
1539 /* add change events from custom element configuration */
1540 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1542 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1544 for (j = 0; j < ei->num_change_pages; j++)
1546 if (!ei->change_page[j].can_change_or_has_action)
1549 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1551 /* only add event page for the first page found with this event */
1552 if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
1554 ei->has_change_event[k] = TRUE;
1556 ei->event_page_nr[k] = j;
1557 ei->event_page[k] = &ei->change_page[j];
1563 /* ---------- initialize run-time trigger player and element ------------- */
1565 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1567 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1569 for (j = 0; j < ei->num_change_pages; j++)
1571 ei->change_page[j].actual_trigger_element = EL_EMPTY;
1572 ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
1573 ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
1574 ei->change_page[j].actual_trigger_ce_value = 0;
1578 /* ---------- initialize trigger events ---------------------------------- */
1580 /* initialize trigger events information */
1581 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1582 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1583 trigger_events[i][j] = FALSE;
1585 /* add trigger events from element change event properties */
1586 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1588 struct ElementInfo *ei = &element_info[i];
1590 for (j = 0; j < ei->num_change_pages; j++)
1592 if (!ei->change_page[j].can_change_or_has_action)
1595 if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
1597 int trigger_element = ei->change_page[j].trigger_element;
1599 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1601 if (ei->change_page[j].has_event[k])
1603 if (IS_GROUP_ELEMENT(trigger_element))
1605 struct ElementGroupInfo *group =
1606 element_info[trigger_element].group;
1608 for (l = 0; l < group->num_elements_resolved; l++)
1609 trigger_events[group->element_resolved[l]][k] = TRUE;
1612 trigger_events[trigger_element][k] = TRUE;
1619 /* ---------- initialize push delay -------------------------------------- */
1621 /* initialize push delay values to default */
1622 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1624 if (!IS_CUSTOM_ELEMENT(i))
1626 element_info[i].push_delay_fixed = game.default_push_delay_fixed;
1627 element_info[i].push_delay_random = game.default_push_delay_random;
1631 /* set push delay value for certain elements from pre-defined list */
1632 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
1634 int e = push_delay_list[i].element;
1636 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
1637 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
1640 /* set push delay value for Supaplex elements for newer engine versions */
1641 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
1643 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1645 if (IS_SP_ELEMENT(i))
1647 /* set SP push delay to just enough to push under a falling zonk */
1648 int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
1650 element_info[i].push_delay_fixed = delay;
1651 element_info[i].push_delay_random = 0;
1656 /* ---------- initialize move stepsize ----------------------------------- */
1658 /* initialize move stepsize values to default */
1659 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1660 if (!IS_CUSTOM_ELEMENT(i))
1661 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
1663 /* set move stepsize value for certain elements from pre-defined list */
1664 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
1666 int e = move_stepsize_list[i].element;
1668 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
1671 /* ---------- initialize collect score ----------------------------------- */
1673 /* initialize collect score values for custom elements from initial value */
1674 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1675 if (IS_CUSTOM_ELEMENT(i))
1676 element_info[i].collect_score = element_info[i].collect_score_initial;
1678 /* ---------- initialize collect count ----------------------------------- */
1680 /* initialize collect count values for non-custom elements */
1681 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1682 if (!IS_CUSTOM_ELEMENT(i))
1683 element_info[i].collect_count_initial = 0;
1685 /* add collect count values for all elements from pre-defined list */
1686 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
1687 element_info[collect_count_list[i].element].collect_count_initial =
1688 collect_count_list[i].count;
1690 /* ---------- initialize access direction -------------------------------- */
1692 /* initialize access direction values to default (access from every side) */
1693 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1694 if (!IS_CUSTOM_ELEMENT(i))
1695 element_info[i].access_direction = MV_ALL_DIRECTIONS;
1697 /* set access direction value for certain elements from pre-defined list */
1698 for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
1699 element_info[access_direction_list[i].element].access_direction =
1700 access_direction_list[i].direction;
1702 /* ---------- initialize explosion content ------------------------------- */
1703 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1705 if (IS_CUSTOM_ELEMENT(i))
1708 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
1710 /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
1712 element_info[i].content.e[x][y] =
1713 (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
1714 i == EL_PLAYER_2 ? EL_EMERALD_RED :
1715 i == EL_PLAYER_3 ? EL_EMERALD :
1716 i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
1717 i == EL_MOLE ? EL_EMERALD_RED :
1718 i == EL_PENGUIN ? EL_EMERALD_PURPLE :
1719 i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
1720 i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
1721 i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
1722 i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
1723 i == EL_WALL_EMERALD ? EL_EMERALD :
1724 i == EL_WALL_DIAMOND ? EL_DIAMOND :
1725 i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
1726 i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
1727 i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
1728 i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
1729 i == EL_WALL_PEARL ? EL_PEARL :
1730 i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
1736 int get_num_special_action(int element, int action_first, int action_last)
1738 int num_special_action = 0;
1741 for (i = action_first; i <= action_last; i++)
1743 boolean found = FALSE;
1745 for (j = 0; j < NUM_DIRECTIONS; j++)
1746 if (el_act_dir2img(element, i, j) !=
1747 el_act_dir2img(element, ACTION_DEFAULT, j))
1751 num_special_action++;
1757 printf("::: %d->%d: %d\n", action_first, action_last, num_special_action);
1760 return num_special_action;
1764 =============================================================================
1766 -----------------------------------------------------------------------------
1767 initialize and start new game
1768 =============================================================================
1773 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
1774 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
1775 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
1780 /* don't play tapes over network */
1781 network_playing = (options.network && !tape.playing);
1783 for (i = 0; i < MAX_PLAYERS; i++)
1785 struct PlayerInfo *player = &stored_player[i];
1787 player->index_nr = i;
1788 player->index_bit = (1 << i);
1789 player->element_nr = EL_PLAYER_1 + i;
1791 player->present = FALSE;
1792 player->active = FALSE;
1795 player->effective_action = 0;
1796 player->programmed_action = 0;
1799 player->gems_still_needed = level.gems_needed;
1800 player->sokobanfields_still_needed = 0;
1801 player->lights_still_needed = 0;
1802 player->friends_still_needed = 0;
1804 for (j = 0; j < MAX_NUM_KEYS; j++)
1805 player->key[j] = FALSE;
1807 player->dynabomb_count = 0;
1808 player->dynabomb_size = 1;
1809 player->dynabombs_left = 0;
1810 player->dynabomb_xl = FALSE;
1812 player->MovDir = MV_NONE;
1815 player->GfxDir = MV_NONE;
1816 player->GfxAction = ACTION_DEFAULT;
1818 player->StepFrame = 0;
1820 player->use_murphy = FALSE;
1821 player->artwork_element =
1822 (level.use_artwork_element[i] ? level.artwork_element[i] :
1823 player->element_nr);
1825 player->block_last_field = FALSE; /* initialized in InitPlayerField() */
1826 player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
1828 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
1830 player->actual_frame_counter = 0;
1832 player->step_counter = 0;
1834 player->last_move_dir = MV_NONE;
1836 player->is_waiting = FALSE;
1837 player->is_moving = FALSE;
1838 player->is_auto_moving = FALSE;
1839 player->is_digging = FALSE;
1840 player->is_snapping = FALSE;
1841 player->is_collecting = FALSE;
1842 player->is_pushing = FALSE;
1843 player->is_switching = FALSE;
1844 player->is_dropping = FALSE;
1845 player->is_dropping_pressed = FALSE;
1847 player->is_bored = FALSE;
1848 player->is_sleeping = FALSE;
1850 player->frame_counter_bored = -1;
1851 player->frame_counter_sleeping = -1;
1853 player->anim_delay_counter = 0;
1854 player->post_delay_counter = 0;
1856 player->dir_waiting = MV_NONE;
1857 player->action_waiting = ACTION_DEFAULT;
1858 player->last_action_waiting = ACTION_DEFAULT;
1859 player->special_action_bored = ACTION_DEFAULT;
1860 player->special_action_sleeping = ACTION_DEFAULT;
1863 /* cannot be set here -- could be modified in Init[Player]Field() below */
1865 /* set number of special actions for bored and sleeping animation */
1866 player->num_special_action_bored =
1867 get_num_special_action(player->artwork_element,
1868 ACTION_BORING_1, ACTION_BORING_LAST);
1869 player->num_special_action_sleeping =
1870 get_num_special_action(player->artwork_element,
1871 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
1874 player->switch_x = -1;
1875 player->switch_y = -1;
1877 player->drop_x = -1;
1878 player->drop_y = -1;
1880 player->show_envelope = 0;
1883 SetPlayerMoveSpeed(player, level.initial_player_stepsize, TRUE);
1885 player->move_delay = game.initial_move_delay;
1886 player->move_delay_value = game.initial_move_delay_value;
1888 player->move_delay_value_next = -1;
1890 player->move_delay_reset_counter = 0;
1892 player->cannot_move = FALSE;
1895 player->push_delay = -1; /* initialized when pushing starts */
1896 player->push_delay_value = game.initial_push_delay_value;
1898 player->drop_delay = 0;
1899 player->drop_pressed_delay = 0;
1901 player->last_jx = player->last_jy = 0;
1902 player->jx = player->jy = 0;
1904 player->shield_normal_time_left = 0;
1905 player->shield_deadly_time_left = 0;
1907 player->inventory_infinite_element = EL_UNDEFINED;
1908 player->inventory_size = 0;
1910 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
1911 SnapField(player, 0, 0);
1913 player->LevelSolved = FALSE;
1914 player->GameOver = FALSE;
1917 network_player_action_received = FALSE;
1919 #if defined(NETWORK_AVALIABLE)
1920 /* initial null action */
1921 if (network_playing)
1922 SendToServer_MovePlayer(MV_NONE);
1931 TimeLeft = level.time;
1934 ScreenMovDir = MV_NONE;
1938 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
1940 AllPlayersGone = FALSE;
1942 game.yamyam_content_nr = 0;
1943 game.magic_wall_active = FALSE;
1944 game.magic_wall_time_left = 0;
1945 game.light_time_left = 0;
1946 game.timegate_time_left = 0;
1947 game.switchgate_pos = 0;
1948 game.wind_direction = level.wind_direction_initial;
1949 game.gravity = level.initial_gravity;
1950 game.explosions_delayed = TRUE;
1952 game.lenses_time_left = 0;
1953 game.magnify_time_left = 0;
1955 game.ball_state = level.ball_state_initial;
1956 game.ball_content_nr = 0;
1958 game.envelope_active = FALSE;
1960 game.centered_player_nr = game.centered_player_nr_next = -1; /* focus all */
1962 for (i = 0; i < NUM_BELTS; i++)
1964 game.belt_dir[i] = MV_NONE;
1965 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
1968 for (i = 0; i < MAX_NUM_AMOEBA; i++)
1969 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
1972 SCAN_PLAYFIELD(x, y)
1974 for (x = 0; x < lev_fieldx; x++) for (y = 0; y < lev_fieldy; y++)
1977 Feld[x][y] = level.field[x][y];
1978 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
1979 ChangeDelay[x][y] = 0;
1980 ChangePage[x][y] = -1;
1981 #if USE_NEW_CUSTOM_VALUE
1982 CustomValue[x][y] = 0; /* initialized in InitField() */
1984 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
1986 WasJustMoving[x][y] = 0;
1987 WasJustFalling[x][y] = 0;
1988 CheckCollision[x][y] = 0;
1990 Pushed[x][y] = FALSE;
1992 ChangeCount[x][y] = 0;
1993 ChangeEvent[x][y] = -1;
1995 ExplodePhase[x][y] = 0;
1996 ExplodeDelay[x][y] = 0;
1997 ExplodeField[x][y] = EX_TYPE_NONE;
1999 RunnerVisit[x][y] = 0;
2000 PlayerVisit[x][y] = 0;
2003 GfxRandom[x][y] = INIT_GFX_RANDOM();
2004 GfxElement[x][y] = EL_UNDEFINED;
2005 GfxAction[x][y] = ACTION_DEFAULT;
2006 GfxDir[x][y] = MV_NONE;
2010 SCAN_PLAYFIELD(x, y)
2012 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
2015 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
2017 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
2019 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
2022 InitField(x, y, TRUE);
2027 for (i = 0; i < MAX_PLAYERS; i++)
2029 struct PlayerInfo *player = &stored_player[i];
2032 /* set number of special actions for bored and sleeping animation */
2033 player->num_special_action_bored =
2034 get_num_special_action(player->artwork_element,
2035 ACTION_BORING_1, ACTION_BORING_LAST);
2036 player->num_special_action_sleeping =
2037 get_num_special_action(player->artwork_element,
2038 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
2043 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
2044 emulate_sb ? EMU_SOKOBAN :
2045 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
2047 #if USE_NEW_ALL_SLIPPERY
2048 /* initialize type of slippery elements */
2049 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2051 if (!IS_CUSTOM_ELEMENT(i))
2053 /* default: elements slip down either to the left or right randomly */
2054 element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
2056 /* SP style elements prefer to slip down on the left side */
2057 if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
2058 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
2060 /* BD style elements prefer to slip down on the left side */
2061 if (game.emulation == EMU_BOULDERDASH)
2062 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
2067 /* initialize explosion and ignition delay */
2068 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2070 if (!IS_CUSTOM_ELEMENT(i))
2073 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
2074 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
2075 game.emulation == EMU_SUPAPLEX ? 3 : 2);
2076 int last_phase = (num_phase + 1) * delay;
2077 int half_phase = (num_phase / 2) * delay;
2079 element_info[i].explosion_delay = last_phase - 1;
2080 element_info[i].ignition_delay = half_phase;
2082 if (i == EL_BLACK_ORB)
2083 element_info[i].ignition_delay = 1;
2087 if (element_info[i].explosion_delay < 1) /* !!! check again !!! */
2088 element_info[i].explosion_delay = 1;
2090 if (element_info[i].ignition_delay < 1) /* !!! check again !!! */
2091 element_info[i].ignition_delay = 1;
2095 /* correct non-moving belts to start moving left */
2096 for (i = 0; i < NUM_BELTS; i++)
2097 if (game.belt_dir[i] == MV_NONE)
2098 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
2100 /* check if any connected player was not found in playfield */
2101 for (i = 0; i < MAX_PLAYERS; i++)
2103 struct PlayerInfo *player = &stored_player[i];
2105 if (player->connected && !player->present)
2107 for (j = 0; j < MAX_PLAYERS; j++)
2109 struct PlayerInfo *some_player = &stored_player[j];
2110 int jx = some_player->jx, jy = some_player->jy;
2112 /* assign first free player found that is present in the playfield */
2113 if (some_player->present && !some_player->connected)
2115 player->present = TRUE;
2116 player->active = TRUE;
2118 some_player->present = FALSE;
2119 some_player->active = FALSE;
2122 player->element_nr = some_player->element_nr;
2125 player->artwork_element = some_player->artwork_element;
2127 player->block_last_field = some_player->block_last_field;
2128 player->block_delay_adjustment = some_player->block_delay_adjustment;
2130 StorePlayer[jx][jy] = player->element_nr;
2131 player->jx = player->last_jx = jx;
2132 player->jy = player->last_jy = jy;
2142 /* when playing a tape, eliminate all players who do not participate */
2144 for (i = 0; i < MAX_PLAYERS; i++)
2146 if (stored_player[i].active && !tape.player_participates[i])
2148 struct PlayerInfo *player = &stored_player[i];
2149 int jx = player->jx, jy = player->jy;
2151 player->active = FALSE;
2152 StorePlayer[jx][jy] = 0;
2153 Feld[jx][jy] = EL_EMPTY;
2157 else if (!options.network && !setup.team_mode) /* && !tape.playing */
2159 /* when in single player mode, eliminate all but the first active player */
2161 for (i = 0; i < MAX_PLAYERS; i++)
2163 if (stored_player[i].active)
2165 for (j = i + 1; j < MAX_PLAYERS; j++)
2167 if (stored_player[j].active)
2169 struct PlayerInfo *player = &stored_player[j];
2170 int jx = player->jx, jy = player->jy;
2172 player->active = FALSE;
2173 player->present = FALSE;
2175 StorePlayer[jx][jy] = 0;
2176 Feld[jx][jy] = EL_EMPTY;
2183 /* when recording the game, store which players take part in the game */
2186 for (i = 0; i < MAX_PLAYERS; i++)
2187 if (stored_player[i].active)
2188 tape.player_participates[i] = TRUE;
2193 for (i = 0; i < MAX_PLAYERS; i++)
2195 struct PlayerInfo *player = &stored_player[i];
2197 printf("Player %d: present == %d, connected == %d, active == %d.\n",
2202 if (local_player == player)
2203 printf("Player %d is local player.\n", i+1);
2207 if (BorderElement == EL_EMPTY)
2210 SBX_Right = lev_fieldx - SCR_FIELDX;
2212 SBY_Lower = lev_fieldy - SCR_FIELDY;
2217 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
2219 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
2222 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
2223 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
2225 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
2226 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
2228 /* if local player not found, look for custom element that might create
2229 the player (make some assumptions about the right custom element) */
2230 if (!local_player->present)
2232 int start_x = 0, start_y = 0;
2233 int found_rating = 0;
2234 int found_element = EL_UNDEFINED;
2235 int player_nr = local_player->index_nr;
2238 SCAN_PLAYFIELD(x, y)
2240 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
2243 int element = Feld[x][y];
2248 if (level.use_start_element[player_nr] &&
2249 level.start_element[player_nr] == element &&
2256 found_element = element;
2259 if (!IS_CUSTOM_ELEMENT(element))
2262 if (CAN_CHANGE(element))
2264 for (i = 0; i < element_info[element].num_change_pages; i++)
2266 /* check for player created from custom element as single target */
2267 content = element_info[element].change_page[i].target_element;
2268 is_player = ELEM_IS_PLAYER(content);
2270 if (is_player && (found_rating < 3 || element < found_element))
2276 found_element = element;
2281 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
2283 /* check for player created from custom element as explosion content */
2284 content = element_info[element].content.e[xx][yy];
2285 is_player = ELEM_IS_PLAYER(content);
2287 if (is_player && (found_rating < 2 || element < found_element))
2289 start_x = x + xx - 1;
2290 start_y = y + yy - 1;
2293 found_element = element;
2296 if (!CAN_CHANGE(element))
2299 for (i = 0; i < element_info[element].num_change_pages; i++)
2301 /* check for player created from custom element as extended target */
2303 element_info[element].change_page[i].target_content.e[xx][yy];
2305 is_player = ELEM_IS_PLAYER(content);
2307 if (is_player && (found_rating < 1 || element < found_element))
2309 start_x = x + xx - 1;
2310 start_y = y + yy - 1;
2313 found_element = element;
2319 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
2320 start_x > SBX_Right + MIDPOSX ? SBX_Right :
2323 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
2324 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
2329 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
2330 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2331 local_player->jx - MIDPOSX);
2333 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2334 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2335 local_player->jy - MIDPOSY);
2338 if (!game.restart_level)
2339 CloseDoor(DOOR_CLOSE_1);
2341 /* !!! FIX THIS (START) !!! */
2342 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2344 InitGameEngine_EM();
2351 /* after drawing the level, correct some elements */
2352 if (game.timegate_time_left == 0)
2353 CloseAllOpenTimegates();
2355 if (setup.soft_scrolling)
2356 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
2358 redraw_mask |= REDRAW_FROM_BACKBUFFER;
2361 /* !!! FIX THIS (END) !!! */
2363 if (!game.restart_level)
2365 /* copy default game door content to main double buffer */
2366 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2367 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2370 DrawGameDoorValues();
2372 if (!game.restart_level)
2376 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
2377 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
2378 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
2382 /* copy actual game door content to door double buffer for OpenDoor() */
2383 BlitBitmap(drawto, bitmap_db_door,
2384 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
2386 OpenDoor(DOOR_OPEN_ALL);
2388 PlaySoundStereo(SND_GAME_STARTING, SOUND_MIDDLE);
2390 if (setup.sound_music)
2393 KeyboardAutoRepeatOffUnlessAutoplay();
2397 for (i = 0; i < MAX_PLAYERS; i++)
2398 printf("Player %d %sactive.\n",
2399 i + 1, (stored_player[i].active ? "" : "not "));
2403 game.restart_level = FALSE;
2406 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
2408 /* this is used for non-R'n'D game engines to update certain engine values */
2410 /* needed to determine if sounds are played within the visible screen area */
2411 scroll_x = actual_scroll_x;
2412 scroll_y = actual_scroll_y;
2415 void InitMovDir(int x, int y)
2417 int i, element = Feld[x][y];
2418 static int xy[4][2] =
2425 static int direction[3][4] =
2427 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
2428 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
2429 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
2438 Feld[x][y] = EL_BUG;
2439 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
2442 case EL_SPACESHIP_RIGHT:
2443 case EL_SPACESHIP_UP:
2444 case EL_SPACESHIP_LEFT:
2445 case EL_SPACESHIP_DOWN:
2446 Feld[x][y] = EL_SPACESHIP;
2447 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
2450 case EL_BD_BUTTERFLY_RIGHT:
2451 case EL_BD_BUTTERFLY_UP:
2452 case EL_BD_BUTTERFLY_LEFT:
2453 case EL_BD_BUTTERFLY_DOWN:
2454 Feld[x][y] = EL_BD_BUTTERFLY;
2455 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
2458 case EL_BD_FIREFLY_RIGHT:
2459 case EL_BD_FIREFLY_UP:
2460 case EL_BD_FIREFLY_LEFT:
2461 case EL_BD_FIREFLY_DOWN:
2462 Feld[x][y] = EL_BD_FIREFLY;
2463 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
2466 case EL_PACMAN_RIGHT:
2468 case EL_PACMAN_LEFT:
2469 case EL_PACMAN_DOWN:
2470 Feld[x][y] = EL_PACMAN;
2471 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
2474 case EL_YAMYAM_LEFT:
2475 case EL_YAMYAM_RIGHT:
2477 case EL_YAMYAM_DOWN:
2478 Feld[x][y] = EL_YAMYAM;
2479 MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
2482 case EL_SP_SNIKSNAK:
2483 MovDir[x][y] = MV_UP;
2486 case EL_SP_ELECTRON:
2487 MovDir[x][y] = MV_LEFT;
2494 Feld[x][y] = EL_MOLE;
2495 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
2499 if (IS_CUSTOM_ELEMENT(element))
2501 struct ElementInfo *ei = &element_info[element];
2502 int move_direction_initial = ei->move_direction_initial;
2503 int move_pattern = ei->move_pattern;
2505 if (move_direction_initial == MV_START_PREVIOUS)
2507 if (MovDir[x][y] != MV_NONE)
2510 move_direction_initial = MV_START_AUTOMATIC;
2513 if (move_direction_initial == MV_START_RANDOM)
2514 MovDir[x][y] = 1 << RND(4);
2515 else if (move_direction_initial & MV_ANY_DIRECTION)
2516 MovDir[x][y] = move_direction_initial;
2517 else if (move_pattern == MV_ALL_DIRECTIONS ||
2518 move_pattern == MV_TURNING_LEFT ||
2519 move_pattern == MV_TURNING_RIGHT ||
2520 move_pattern == MV_TURNING_LEFT_RIGHT ||
2521 move_pattern == MV_TURNING_RIGHT_LEFT ||
2522 move_pattern == MV_TURNING_RANDOM)
2523 MovDir[x][y] = 1 << RND(4);
2524 else if (move_pattern == MV_HORIZONTAL)
2525 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
2526 else if (move_pattern == MV_VERTICAL)
2527 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
2528 else if (move_pattern & MV_ANY_DIRECTION)
2529 MovDir[x][y] = element_info[element].move_pattern;
2530 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
2531 move_pattern == MV_ALONG_RIGHT_SIDE)
2533 /* use random direction as default start direction */
2534 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2535 MovDir[x][y] = 1 << RND(4);
2537 for (i = 0; i < NUM_DIRECTIONS; i++)
2539 int x1 = x + xy[i][0];
2540 int y1 = y + xy[i][1];
2542 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2544 if (move_pattern == MV_ALONG_RIGHT_SIDE)
2545 MovDir[x][y] = direction[0][i];
2547 MovDir[x][y] = direction[1][i];
2556 MovDir[x][y] = 1 << RND(4);
2558 if (element != EL_BUG &&
2559 element != EL_SPACESHIP &&
2560 element != EL_BD_BUTTERFLY &&
2561 element != EL_BD_FIREFLY)
2564 for (i = 0; i < NUM_DIRECTIONS; i++)
2566 int x1 = x + xy[i][0];
2567 int y1 = y + xy[i][1];
2569 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2571 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
2573 MovDir[x][y] = direction[0][i];
2576 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
2577 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
2579 MovDir[x][y] = direction[1][i];
2588 GfxDir[x][y] = MovDir[x][y];
2591 void InitAmoebaNr(int x, int y)
2594 int group_nr = AmoebeNachbarNr(x, y);
2598 for (i = 1; i < MAX_NUM_AMOEBA; i++)
2600 if (AmoebaCnt[i] == 0)
2608 AmoebaNr[x][y] = group_nr;
2609 AmoebaCnt[group_nr]++;
2610 AmoebaCnt2[group_nr]++;
2616 boolean raise_level = FALSE;
2618 if (local_player->MovPos)
2621 if (tape.auto_play) /* tape might already be stopped here */
2622 tape.auto_play_level_solved = TRUE;
2624 local_player->LevelSolved = FALSE;
2626 PlaySoundStereo(SND_GAME_WINNING, SOUND_MIDDLE);
2630 if (!tape.playing && setup.sound_loops)
2631 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2632 SND_CTRL_PLAY_LOOP);
2634 while (TimeLeft > 0)
2636 if (!tape.playing && !setup.sound_loops)
2637 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2639 if (TimeLeft > 100 && TimeLeft % 10 == 0)
2642 RaiseScore(level.score[SC_TIME_BONUS] * 10);
2647 RaiseScore(level.score[SC_TIME_BONUS]);
2650 DrawGameValue_Time(TimeLeft);
2658 if (!tape.playing && setup.sound_loops)
2659 StopSound(SND_GAME_LEVELTIME_BONUS);
2661 else if (level.time == 0) /* level without time limit */
2663 if (!tape.playing && setup.sound_loops)
2664 PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2665 SND_CTRL_PLAY_LOOP);
2667 while (TimePlayed < 999)
2669 if (!tape.playing && !setup.sound_loops)
2670 PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2672 if (TimePlayed < 900 && TimePlayed % 10 == 0)
2675 RaiseScore(level.score[SC_TIME_BONUS] * 10);
2680 RaiseScore(level.score[SC_TIME_BONUS]);
2683 DrawGameValue_Time(TimePlayed);
2691 if (!tape.playing && setup.sound_loops)
2692 StopSound(SND_GAME_LEVELTIME_BONUS);
2695 /* close exit door after last player */
2696 if (AllPlayersGone && ExitX >= 0 && ExitY >= 0 &&
2697 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
2698 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN))
2700 int element = Feld[ExitX][ExitY];
2702 Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
2703 EL_SP_EXIT_CLOSING);
2705 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
2708 /* player disappears */
2709 if (ExitX >= 0 && ExitY >= 0)
2710 DrawLevelField(ExitX, ExitY);
2716 printf("::: TAPE PLAYING -> DO NOT SAVE SCORE\n");
2718 printf("::: NO TAPE PLAYING -> SAVING SCORE\n");
2724 CloseDoor(DOOR_CLOSE_1);
2729 SaveTape(tape.level_nr); /* Ask to save tape */
2732 if (level_nr == leveldir_current->handicap_level)
2734 leveldir_current->handicap_level++;
2735 SaveLevelSetup_SeriesInfo();
2738 if (level_editor_test_game)
2739 local_player->score = -1; /* no highscore when playing from editor */
2740 else if (level_nr < leveldir_current->last_level)
2741 raise_level = TRUE; /* advance to next level */
2743 if ((hi_pos = NewHiScore()) >= 0)
2745 game_status = GAME_MODE_SCORES;
2746 DrawHallOfFame(hi_pos);
2755 game_status = GAME_MODE_MAIN;
2772 LoadScore(level_nr);
2774 if (strcmp(setup.player_name, EMPTY_PLAYER_NAME) == 0 ||
2775 local_player->score < highscore[MAX_SCORE_ENTRIES - 1].Score)
2778 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
2780 if (local_player->score > highscore[k].Score)
2782 /* player has made it to the hall of fame */
2784 if (k < MAX_SCORE_ENTRIES - 1)
2786 int m = MAX_SCORE_ENTRIES - 1;
2789 for (l = k; l < MAX_SCORE_ENTRIES; l++)
2790 if (!strcmp(setup.player_name, highscore[l].Name))
2792 if (m == k) /* player's new highscore overwrites his old one */
2796 for (l = m; l > k; l--)
2798 strcpy(highscore[l].Name, highscore[l - 1].Name);
2799 highscore[l].Score = highscore[l - 1].Score;
2806 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
2807 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
2808 highscore[k].Score = local_player->score;
2814 else if (!strncmp(setup.player_name, highscore[k].Name,
2815 MAX_PLAYER_NAME_LEN))
2816 break; /* player already there with a higher score */
2822 SaveScore(level_nr);
2827 inline static int getElementMoveStepsize(int x, int y)
2829 int element = Feld[x][y];
2830 int direction = MovDir[x][y];
2831 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2832 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2833 int horiz_move = (dx != 0);
2834 int sign = (horiz_move ? dx : dy);
2835 int step = sign * element_info[element].move_stepsize;
2837 /* special values for move stepsize for spring and things on conveyor belt */
2841 if (element == EL_SPRING)
2842 step = sign * MOVE_STEPSIZE_NORMAL * 2;
2843 else if (CAN_FALL(element) && !CAN_MOVE(element) &&
2844 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
2845 step = sign * MOVE_STEPSIZE_NORMAL / 2;
2847 if (CAN_FALL(element) &&
2848 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
2849 step = sign * MOVE_STEPSIZE_NORMAL / 2;
2850 else if (element == EL_SPRING)
2851 step = sign * MOVE_STEPSIZE_NORMAL * 2;
2858 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
2860 if (player->GfxAction != action || player->GfxDir != dir)
2863 printf("Player frame reset! (%d => %d, %d => %d)\n",
2864 player->GfxAction, action, player->GfxDir, dir);
2867 player->GfxAction = action;
2868 player->GfxDir = dir;
2870 player->StepFrame = 0;
2874 static void ResetRandomAnimationValue(int x, int y)
2876 GfxRandom[x][y] = INIT_GFX_RANDOM();
2879 static void ResetGfxAnimation(int x, int y)
2882 int element, graphic;
2886 GfxAction[x][y] = ACTION_DEFAULT;
2887 GfxDir[x][y] = MovDir[x][y];
2890 element = Feld[x][y];
2891 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
2893 if (graphic_info[graphic].anim_global_sync)
2894 GfxFrame[x][y] = FrameCounter;
2895 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
2896 GfxFrame[x][y] = CustomValue[x][y];
2897 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2898 GfxFrame[x][y] = element_info[element].collect_score;
2902 void InitMovingField(int x, int y, int direction)
2904 int element = Feld[x][y];
2908 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2909 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2913 if (!WasJustMoving[x][y] || direction != MovDir[x][y])
2914 ResetGfxAnimation(x, y);
2916 MovDir[x][y] = direction;
2917 GfxDir[x][y] = direction;
2918 GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
2919 ACTION_FALLING : ACTION_MOVING);
2922 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
2924 if (graphic_info[graphic].anim_global_sync)
2925 GfxFrame[x][y] = FrameCounter;
2926 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
2927 GfxFrame[x][y] = CustomValue[x][y];
2928 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2929 GfxFrame[x][y] = element_info[element].collect_score;
2932 /* this is needed for CEs with property "can move" / "not moving" */
2934 if (getElementMoveStepsize(x, y) != 0) /* moving or being moved */
2936 if (Feld[newx][newy] == EL_EMPTY)
2937 Feld[newx][newy] = EL_BLOCKED;
2939 MovDir[newx][newy] = MovDir[x][y];
2941 #if USE_NEW_CUSTOM_VALUE
2942 CustomValue[newx][newy] = CustomValue[x][y];
2945 GfxFrame[newx][newy] = GfxFrame[x][y];
2946 GfxRandom[newx][newy] = GfxRandom[x][y];
2947 GfxAction[newx][newy] = GfxAction[x][y];
2948 GfxDir[newx][newy] = GfxDir[x][y];
2952 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
2954 int direction = MovDir[x][y];
2956 int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
2957 int newy = y + (direction & MV_UP ? -1 : direction & MV_DOWN ? +1 : 0);
2959 int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2960 int newy = y + (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
2967 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
2969 int oldx = x, oldy = y;
2970 int direction = MovDir[x][y];
2972 if (direction == MV_LEFT)
2974 else if (direction == MV_RIGHT)
2976 else if (direction == MV_UP)
2978 else if (direction == MV_DOWN)
2981 *comes_from_x = oldx;
2982 *comes_from_y = oldy;
2985 int MovingOrBlocked2Element(int x, int y)
2987 int element = Feld[x][y];
2989 if (element == EL_BLOCKED)
2993 Blocked2Moving(x, y, &oldx, &oldy);
2994 return Feld[oldx][oldy];
3000 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
3002 /* like MovingOrBlocked2Element(), but if element is moving
3003 and (x,y) is the field the moving element is just leaving,
3004 return EL_BLOCKED instead of the element value */
3005 int element = Feld[x][y];
3007 if (IS_MOVING(x, y))
3009 if (element == EL_BLOCKED)
3013 Blocked2Moving(x, y, &oldx, &oldy);
3014 return Feld[oldx][oldy];
3023 static void RemoveField(int x, int y)
3025 Feld[x][y] = EL_EMPTY;
3031 #if USE_NEW_CUSTOM_VALUE
3032 CustomValue[x][y] = 0;
3036 ChangeDelay[x][y] = 0;
3037 ChangePage[x][y] = -1;
3038 Pushed[x][y] = FALSE;
3041 ExplodeField[x][y] = EX_TYPE_NONE;
3044 GfxElement[x][y] = EL_UNDEFINED;
3045 GfxAction[x][y] = ACTION_DEFAULT;
3046 GfxDir[x][y] = MV_NONE;
3049 void RemoveMovingField(int x, int y)
3051 int oldx = x, oldy = y, newx = x, newy = y;
3052 int element = Feld[x][y];
3053 int next_element = EL_UNDEFINED;
3055 if (element != EL_BLOCKED && !IS_MOVING(x, y))
3058 if (IS_MOVING(x, y))
3060 Moving2Blocked(x, y, &newx, &newy);
3062 if (Feld[newx][newy] != EL_BLOCKED)
3064 /* element is moving, but target field is not free (blocked), but
3065 already occupied by something different (example: acid pool);
3066 in this case, only remove the moving field, but not the target */
3068 RemoveField(oldx, oldy);
3070 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
3072 DrawLevelField(oldx, oldy);
3077 else if (element == EL_BLOCKED)
3079 Blocked2Moving(x, y, &oldx, &oldy);
3080 if (!IS_MOVING(oldx, oldy))
3084 if (element == EL_BLOCKED &&
3085 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
3086 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
3087 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
3088 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
3089 next_element = get_next_element(Feld[oldx][oldy]);
3091 RemoveField(oldx, oldy);
3092 RemoveField(newx, newy);
3094 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
3096 if (next_element != EL_UNDEFINED)
3097 Feld[oldx][oldy] = next_element;
3099 DrawLevelField(oldx, oldy);
3100 DrawLevelField(newx, newy);
3103 void DrawDynamite(int x, int y)
3105 int sx = SCREENX(x), sy = SCREENY(y);
3106 int graphic = el2img(Feld[x][y]);
3109 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
3112 if (IS_WALKABLE_INSIDE(Back[x][y]))
3116 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
3117 else if (Store[x][y])
3118 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
3120 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
3122 if (Back[x][y] || Store[x][y])
3123 DrawGraphicThruMask(sx, sy, graphic, frame);
3125 DrawGraphic(sx, sy, graphic, frame);
3128 void CheckDynamite(int x, int y)
3130 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
3134 if (MovDelay[x][y] != 0)
3137 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
3143 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
3148 void DrawRelocatePlayer(struct PlayerInfo *player, boolean quick_relocation)
3150 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3151 boolean no_delay = (tape.warp_forward);
3152 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
3153 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
3154 int jx = player->jx;
3155 int jy = player->jy;
3157 if (quick_relocation)
3159 int offset = (setup.scroll_delay ? 3 : 0);
3161 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
3163 scroll_x = (player->jx < SBX_Left + MIDPOSX ? SBX_Left :
3164 player->jx > SBX_Right + MIDPOSX ? SBX_Right :
3165 player->jx - MIDPOSX);
3167 scroll_y = (player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
3168 player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
3169 player->jy - MIDPOSY);
3173 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
3174 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
3175 scroll_x = jx - MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
3177 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
3178 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
3179 scroll_y = jy - MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
3181 /* don't scroll over playfield boundaries */
3182 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
3183 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
3185 /* don't scroll over playfield boundaries */
3186 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
3187 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
3190 RedrawPlayfield(TRUE, 0,0,0,0);
3194 int scroll_xx = (player->jx < SBX_Left + MIDPOSX ? SBX_Left :
3195 player->jx > SBX_Right + MIDPOSX ? SBX_Right :
3196 player->jx - MIDPOSX);
3198 int scroll_yy = (player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
3199 player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
3200 player->jy - MIDPOSY);
3202 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
3204 while (scroll_x != scroll_xx || scroll_y != scroll_yy)
3207 int fx = FX, fy = FY;
3209 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
3210 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
3212 if (dx == 0 && dy == 0) /* no scrolling needed at all */
3218 fx += dx * TILEX / 2;
3219 fy += dy * TILEY / 2;
3221 ScrollLevel(dx, dy);
3224 /* scroll in two steps of half tile size to make things smoother */
3225 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
3227 Delay(wait_delay_value);
3229 /* scroll second step to align at full tile size */
3231 Delay(wait_delay_value);
3236 Delay(wait_delay_value);
3240 void RelocatePlayer(int jx, int jy, int el_player_raw)
3242 int el_player = GET_PLAYER_ELEMENT(el_player_raw);
3243 int player_nr = GET_PLAYER_NR(el_player);
3244 struct PlayerInfo *player = &stored_player[player_nr];
3245 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3246 boolean no_delay = (tape.warp_forward);
3247 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
3248 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
3249 int old_jx = player->jx;
3250 int old_jy = player->jy;
3251 int old_element = Feld[old_jx][old_jy];
3252 int element = Feld[jx][jy];
3253 boolean player_relocated = (old_jx != jx || old_jy != jy);
3255 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
3256 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
3257 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
3258 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
3259 int leave_side_horiz = move_dir_horiz;
3260 int leave_side_vert = move_dir_vert;
3261 int enter_side = enter_side_horiz | enter_side_vert;
3262 int leave_side = leave_side_horiz | leave_side_vert;
3264 if (player->GameOver) /* do not reanimate dead player */
3267 if (!player_relocated) /* no need to relocate the player */
3270 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
3272 RemoveField(jx, jy); /* temporarily remove newly placed player */
3273 DrawLevelField(jx, jy);
3276 if (player->present)
3278 while (player->MovPos)
3280 ScrollPlayer(player, SCROLL_GO_ON);
3281 ScrollScreen(NULL, SCROLL_GO_ON);
3283 AdvanceFrameAndPlayerCounters(player->index_nr);
3288 Delay(wait_delay_value);
3291 DrawPlayer(player); /* needed here only to cleanup last field */
3292 DrawLevelField(player->jx, player->jy); /* remove player graphic */
3294 player->is_moving = FALSE;
3297 if (IS_CUSTOM_ELEMENT(old_element))
3298 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
3300 player->index_bit, leave_side);
3302 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
3304 player->index_bit, leave_side);
3306 Feld[jx][jy] = el_player;
3307 InitPlayerField(jx, jy, el_player, TRUE);
3309 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
3311 Feld[jx][jy] = element;
3312 InitField(jx, jy, FALSE);
3316 /* only visually relocate centered player */
3317 if (player->index_nr == game.centered_player_nr)
3318 DrawRelocatePlayer(player, level.instant_relocation);
3320 if (player == local_player) /* only visually relocate local player */
3321 DrawRelocatePlayer(player, level.instant_relocation);
3324 TestIfPlayerTouchesBadThing(jx, jy);
3325 TestIfPlayerTouchesCustomElement(jx, jy);
3327 if (IS_CUSTOM_ELEMENT(element))
3328 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
3329 player->index_bit, enter_side);
3331 CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
3332 player->index_bit, enter_side);
3335 void Explode(int ex, int ey, int phase, int mode)
3341 /* !!! eliminate this variable !!! */
3342 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
3344 if (game.explosions_delayed)
3346 ExplodeField[ex][ey] = mode;
3350 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
3352 int center_element = Feld[ex][ey];
3353 int artwork_element, explosion_element; /* set these values later */
3356 /* --- This is only really needed (and now handled) in "Impact()". --- */
3357 /* do not explode moving elements that left the explode field in time */
3358 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
3359 center_element == EL_EMPTY &&
3360 (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
3365 /* !!! at this place, the center element may be EL_BLOCKED !!! */
3366 if (mode == EX_TYPE_NORMAL ||
3367 mode == EX_TYPE_CENTER ||
3368 mode == EX_TYPE_CROSS)
3369 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
3372 /* remove things displayed in background while burning dynamite */
3373 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
3376 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
3378 /* put moving element to center field (and let it explode there) */
3379 center_element = MovingOrBlocked2Element(ex, ey);
3380 RemoveMovingField(ex, ey);
3381 Feld[ex][ey] = center_element;
3384 /* now "center_element" is finally determined -- set related values now */
3385 artwork_element = center_element; /* for custom player artwork */
3386 explosion_element = center_element; /* for custom player artwork */
3388 if (IS_PLAYER(ex, ey))
3390 int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
3392 artwork_element = stored_player[player_nr].artwork_element;
3394 if (level.use_explosion_element[player_nr])
3396 explosion_element = level.explosion_element[player_nr];
3397 artwork_element = explosion_element;
3402 if (mode == EX_TYPE_NORMAL ||
3403 mode == EX_TYPE_CENTER ||
3404 mode == EX_TYPE_CROSS)
3405 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
3409 last_phase = element_info[explosion_element].explosion_delay + 1;
3411 last_phase = element_info[center_element].explosion_delay + 1;
3414 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
3416 int xx = x - ex + 1;
3417 int yy = y - ey + 1;
3420 if (!IN_LEV_FIELD(x, y) ||
3421 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
3422 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
3425 element = Feld[x][y];
3427 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
3429 element = MovingOrBlocked2Element(x, y);
3431 if (!IS_EXPLOSION_PROOF(element))
3432 RemoveMovingField(x, y);
3435 /* indestructible elements can only explode in center (but not flames) */
3436 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
3437 mode == EX_TYPE_BORDER)) ||
3438 element == EL_FLAMES)
3441 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
3442 behaviour, for example when touching a yamyam that explodes to rocks
3443 with active deadly shield, a rock is created under the player !!! */
3444 /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
3446 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
3447 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
3448 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
3450 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
3453 if (IS_ACTIVE_BOMB(element))
3455 /* re-activate things under the bomb like gate or penguin */
3456 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
3463 /* save walkable background elements while explosion on same tile */
3464 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
3465 (x != ex || y != ey || mode == EX_TYPE_BORDER))
3466 Back[x][y] = element;
3468 /* ignite explodable elements reached by other explosion */
3469 if (element == EL_EXPLOSION)
3470 element = Store2[x][y];
3472 if (AmoebaNr[x][y] &&
3473 (element == EL_AMOEBA_FULL ||
3474 element == EL_BD_AMOEBA ||
3475 element == EL_AMOEBA_GROWING))
3477 AmoebaCnt[AmoebaNr[x][y]]--;
3478 AmoebaCnt2[AmoebaNr[x][y]]--;
3483 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
3486 int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
3488 Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
3490 switch(StorePlayer[ex][ey])
3493 Store[x][y] = EL_PLAYER_IS_EXPLODING_2;
3496 Store[x][y] = EL_PLAYER_IS_EXPLODING_3;
3499 Store[x][y] = EL_PLAYER_IS_EXPLODING_4;
3503 Store[x][y] = EL_PLAYER_IS_EXPLODING_1;
3508 if (PLAYERINFO(ex, ey)->use_murphy)
3509 Store[x][y] = EL_EMPTY;
3512 /* !!! check this case -- currently needed for rnd_rado_negundo_v,
3513 !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
3514 else if (ELEM_IS_PLAYER(center_element))
3515 Store[x][y] = EL_EMPTY;
3516 else if (center_element == EL_YAMYAM)
3517 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
3518 else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
3519 Store[x][y] = element_info[center_element].content.e[xx][yy];
3521 /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
3522 (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
3523 otherwise) -- FIX THIS !!! */
3524 else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
3525 Store[x][y] = element_info[element].content.e[1][1];
3527 else if (!CAN_EXPLODE(element))
3528 Store[x][y] = element_info[element].content.e[1][1];
3531 Store[x][y] = EL_EMPTY;
3533 else if (center_element == EL_MOLE)
3534 Store[x][y] = EL_EMERALD_RED;
3535 else if (center_element == EL_PENGUIN)
3536 Store[x][y] = EL_EMERALD_PURPLE;
3537 else if (center_element == EL_BUG)
3538 Store[x][y] = ((x == ex && y == ey) ? EL_DIAMOND : EL_EMERALD);
3539 else if (center_element == EL_BD_BUTTERFLY)
3540 Store[x][y] = EL_BD_DIAMOND;
3541 else if (center_element == EL_SP_ELECTRON)
3542 Store[x][y] = EL_SP_INFOTRON;
3543 else if (center_element == EL_AMOEBA_TO_DIAMOND)
3544 Store[x][y] = level.amoeba_content;
3545 else if (center_element == EL_YAMYAM)
3546 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
3547 else if (IS_CUSTOM_ELEMENT(center_element) &&
3548 element_info[center_element].content.e[xx][yy] != EL_EMPTY)
3549 Store[x][y] = element_info[center_element].content.e[xx][yy];
3550 else if (element == EL_WALL_EMERALD)
3551 Store[x][y] = EL_EMERALD;
3552 else if (element == EL_WALL_DIAMOND)
3553 Store[x][y] = EL_DIAMOND;
3554 else if (element == EL_WALL_BD_DIAMOND)
3555 Store[x][y] = EL_BD_DIAMOND;
3556 else if (element == EL_WALL_EMERALD_YELLOW)
3557 Store[x][y] = EL_EMERALD_YELLOW;
3558 else if (element == EL_WALL_EMERALD_RED)
3559 Store[x][y] = EL_EMERALD_RED;
3560 else if (element == EL_WALL_EMERALD_PURPLE)
3561 Store[x][y] = EL_EMERALD_PURPLE;
3562 else if (element == EL_WALL_PEARL)
3563 Store[x][y] = EL_PEARL;
3564 else if (element == EL_WALL_CRYSTAL)
3565 Store[x][y] = EL_CRYSTAL;
3566 else if (IS_CUSTOM_ELEMENT(element) && !CAN_EXPLODE(element))
3567 Store[x][y] = element_info[element].content.e[1][1];
3569 Store[x][y] = EL_EMPTY;
3572 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
3573 center_element == EL_AMOEBA_TO_DIAMOND)
3574 Store2[x][y] = element;
3576 Feld[x][y] = EL_EXPLOSION;
3577 GfxElement[x][y] = artwork_element;
3580 printf(":: setting gfx(%d,%d) to %d ['%s']\n",
3581 x, y, artwork_element, EL_NAME(artwork_element));
3584 ExplodePhase[x][y] = 1;
3585 ExplodeDelay[x][y] = last_phase;
3590 if (center_element == EL_YAMYAM)
3591 game.yamyam_content_nr =
3592 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
3604 GfxFrame[x][y] = 0; /* restart explosion animation */
3606 last_phase = ExplodeDelay[x][y];
3608 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
3612 /* activate this even in non-DEBUG version until cause for crash in
3613 getGraphicAnimationFrame() (see below) is found and eliminated */
3619 /* this can happen if the player leaves an explosion just in time */
3620 if (GfxElement[x][y] == EL_UNDEFINED)
3621 GfxElement[x][y] = EL_EMPTY;
3623 if (GfxElement[x][y] == EL_UNDEFINED)
3626 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
3627 printf("Explode(): This should never happen!\n");
3630 GfxElement[x][y] = EL_EMPTY;
3636 border_element = Store2[x][y];
3637 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3638 border_element = StorePlayer[x][y];
3640 if (phase == element_info[border_element].ignition_delay ||
3641 phase == last_phase)
3643 boolean border_explosion = FALSE;
3645 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
3646 !PLAYER_EXPLOSION_PROTECTED(x, y))
3648 KillPlayerUnlessExplosionProtected(x, y);
3649 border_explosion = TRUE;
3651 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
3653 Feld[x][y] = Store2[x][y];
3656 border_explosion = TRUE;
3658 else if (border_element == EL_AMOEBA_TO_DIAMOND)
3660 AmoebeUmwandeln(x, y);
3662 border_explosion = TRUE;
3665 /* if an element just explodes due to another explosion (chain-reaction),
3666 do not immediately end the new explosion when it was the last frame of
3667 the explosion (as it would be done in the following "if"-statement!) */
3668 if (border_explosion && phase == last_phase)
3672 if (phase == last_phase)
3676 element = Feld[x][y] = Store[x][y];
3677 Store[x][y] = Store2[x][y] = 0;
3678 GfxElement[x][y] = EL_UNDEFINED;
3680 /* player can escape from explosions and might therefore be still alive */
3681 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
3682 element <= EL_PLAYER_IS_EXPLODING_4)
3684 int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
3685 int explosion_element = EL_PLAYER_1 + player_nr;
3686 int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
3687 int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
3689 if (level.use_explosion_element[player_nr])
3690 explosion_element = level.explosion_element[player_nr];
3692 Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
3693 element_info[explosion_element].content.e[xx][yy]);
3696 /* restore probably existing indestructible background element */
3697 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
3698 element = Feld[x][y] = Back[x][y];
3701 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
3702 GfxDir[x][y] = MV_NONE;
3703 ChangeDelay[x][y] = 0;
3704 ChangePage[x][y] = -1;
3706 #if USE_NEW_CUSTOM_VALUE
3707 CustomValue[x][y] = 0;
3710 InitField_WithBug2(x, y, FALSE);
3712 DrawLevelField(x, y);
3714 TestIfElementTouchesCustomElement(x, y);
3716 if (GFX_CRUMBLED(element))
3717 DrawLevelFieldCrumbledSandNeighbours(x, y);
3719 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
3720 StorePlayer[x][y] = 0;
3722 if (ELEM_IS_PLAYER(element))
3723 RelocatePlayer(x, y, element);
3725 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3727 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
3728 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
3731 DrawLevelFieldCrumbledSand(x, y);
3733 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
3735 DrawLevelElement(x, y, Back[x][y]);
3736 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
3738 else if (IS_WALKABLE_UNDER(Back[x][y]))
3740 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3741 DrawLevelElementThruMask(x, y, Back[x][y]);
3743 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
3744 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3748 void DynaExplode(int ex, int ey)
3751 int dynabomb_element = Feld[ex][ey];
3752 int dynabomb_size = 1;
3753 boolean dynabomb_xl = FALSE;
3754 struct PlayerInfo *player;
3755 static int xy[4][2] =
3763 if (IS_ACTIVE_BOMB(dynabomb_element))
3765 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
3766 dynabomb_size = player->dynabomb_size;
3767 dynabomb_xl = player->dynabomb_xl;
3768 player->dynabombs_left++;
3771 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
3773 for (i = 0; i < NUM_DIRECTIONS; i++)
3775 for (j = 1; j <= dynabomb_size; j++)
3777 int x = ex + j * xy[i][0];
3778 int y = ey + j * xy[i][1];
3781 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
3784 element = Feld[x][y];
3786 /* do not restart explosions of fields with active bombs */
3787 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
3790 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
3792 if (element != EL_EMPTY && element != EL_EXPLOSION &&
3793 !IS_DIGGABLE(element) && !dynabomb_xl)
3799 void Bang(int x, int y)
3801 int element = MovingOrBlocked2Element(x, y);
3802 int explosion_type = EX_TYPE_NORMAL;
3804 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3806 struct PlayerInfo *player = PLAYERINFO(x, y);
3808 element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
3809 player->element_nr);
3811 if (level.use_explosion_element[player->index_nr])
3813 int explosion_element = level.explosion_element[player->index_nr];
3815 if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
3816 explosion_type = EX_TYPE_CROSS;
3817 else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
3818 explosion_type = EX_TYPE_CENTER;
3826 case EL_BD_BUTTERFLY:
3829 case EL_DARK_YAMYAM:
3833 RaiseScoreElement(element);
3836 case EL_DYNABOMB_PLAYER_1_ACTIVE:
3837 case EL_DYNABOMB_PLAYER_2_ACTIVE:
3838 case EL_DYNABOMB_PLAYER_3_ACTIVE:
3839 case EL_DYNABOMB_PLAYER_4_ACTIVE:
3840 case EL_DYNABOMB_INCREASE_NUMBER:
3841 case EL_DYNABOMB_INCREASE_SIZE:
3842 case EL_DYNABOMB_INCREASE_POWER:
3843 explosion_type = EX_TYPE_DYNA;
3848 case EL_LAMP_ACTIVE:
3849 case EL_AMOEBA_TO_DIAMOND:
3850 if (!IS_PLAYER(x, y)) /* penguin and player may be at same field */
3851 explosion_type = EX_TYPE_CENTER;
3855 if (element_info[element].explosion_type == EXPLODES_CROSS)
3856 explosion_type = EX_TYPE_CROSS;
3857 else if (element_info[element].explosion_type == EXPLODES_1X1)
3858 explosion_type = EX_TYPE_CENTER;
3862 if (explosion_type == EX_TYPE_DYNA)
3865 Explode(x, y, EX_PHASE_START, explosion_type);
3867 CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
3870 void SplashAcid(int x, int y)
3872 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
3873 (!IN_LEV_FIELD(x - 1, y - 2) ||
3874 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
3875 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
3877 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
3878 (!IN_LEV_FIELD(x + 1, y - 2) ||
3879 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
3880 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
3882 PlayLevelSound(x, y, SND_ACID_SPLASHING);
3885 static void InitBeltMovement()
3887 static int belt_base_element[4] =
3889 EL_CONVEYOR_BELT_1_LEFT,
3890 EL_CONVEYOR_BELT_2_LEFT,
3891 EL_CONVEYOR_BELT_3_LEFT,
3892 EL_CONVEYOR_BELT_4_LEFT
3894 static int belt_base_active_element[4] =
3896 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
3897 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
3898 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
3899 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
3904 /* set frame order for belt animation graphic according to belt direction */
3905 for (i = 0; i < NUM_BELTS; i++)
3909 for (j = 0; j < NUM_BELT_PARTS; j++)
3911 int element = belt_base_active_element[belt_nr] + j;
3912 int graphic = el2img(element);
3914 if (game.belt_dir[i] == MV_LEFT)
3915 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
3917 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
3922 SCAN_PLAYFIELD(x, y)
3924 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
3927 int element = Feld[x][y];
3929 for (i = 0; i < NUM_BELTS; i++)
3931 if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
3933 int e_belt_nr = getBeltNrFromBeltElement(element);
3936 if (e_belt_nr == belt_nr)
3938 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
3940 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
3947 static void ToggleBeltSwitch(int x, int y)
3949 static int belt_base_element[4] =
3951 EL_CONVEYOR_BELT_1_LEFT,
3952 EL_CONVEYOR_BELT_2_LEFT,
3953 EL_CONVEYOR_BELT_3_LEFT,
3954 EL_CONVEYOR_BELT_4_LEFT
3956 static int belt_base_active_element[4] =
3958 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
3959 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
3960 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
3961 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
3963 static int belt_base_switch_element[4] =
3965 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
3966 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
3967 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
3968 EL_CONVEYOR_BELT_4_SWITCH_LEFT
3970 static int belt_move_dir[4] =
3978 int element = Feld[x][y];
3979 int belt_nr = getBeltNrFromBeltSwitchElement(element);
3980 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
3981 int belt_dir = belt_move_dir[belt_dir_nr];
3984 if (!IS_BELT_SWITCH(element))
3987 game.belt_dir_nr[belt_nr] = belt_dir_nr;
3988 game.belt_dir[belt_nr] = belt_dir;
3990 if (belt_dir_nr == 3)
3993 /* set frame order for belt animation graphic according to belt direction */
3994 for (i = 0; i < NUM_BELT_PARTS; i++)
3996 int element = belt_base_active_element[belt_nr] + i;
3997 int graphic = el2img(element);
3999 if (belt_dir == MV_LEFT)
4000 graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
4002 graphic_info[graphic].anim_mode |= ANIM_REVERSE;
4006 SCAN_PLAYFIELD(xx, yy)
4008 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
4011 int element = Feld[xx][yy];
4013 if (IS_BELT_SWITCH(element))
4015 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
4017 if (e_belt_nr == belt_nr)
4019 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
4020 DrawLevelField(xx, yy);
4023 else if (IS_BELT(element) && belt_dir != MV_NONE)
4025 int e_belt_nr = getBeltNrFromBeltElement(element);
4027 if (e_belt_nr == belt_nr)
4029 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
4031 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
4032 DrawLevelField(xx, yy);
4035 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
4037 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
4039 if (e_belt_nr == belt_nr)
4041 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
4043 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
4044 DrawLevelField(xx, yy);
4050 static void ToggleSwitchgateSwitch(int x, int y)
4054 game.switchgate_pos = !game.switchgate_pos;
4057 SCAN_PLAYFIELD(xx, yy)
4059 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
4062 int element = Feld[xx][yy];
4064 if (element == EL_SWITCHGATE_SWITCH_UP ||
4065 element == EL_SWITCHGATE_SWITCH_DOWN)
4067 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
4068 DrawLevelField(xx, yy);
4070 else if (element == EL_SWITCHGATE_OPEN ||
4071 element == EL_SWITCHGATE_OPENING)
4073 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
4075 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
4077 else if (element == EL_SWITCHGATE_CLOSED ||
4078 element == EL_SWITCHGATE_CLOSING)
4080 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
4082 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
4087 static int getInvisibleActiveFromInvisibleElement(int element)
4089 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
4090 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
4091 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
4095 static int getInvisibleFromInvisibleActiveElement(int element)
4097 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
4098 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
4099 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
4103 static void RedrawAllLightSwitchesAndInvisibleElements()
4108 SCAN_PLAYFIELD(x, y)
4110 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
4113 int element = Feld[x][y];
4115 if (element == EL_LIGHT_SWITCH &&
4116 game.light_time_left > 0)
4118 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
4119 DrawLevelField(x, y);
4121 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
4122 game.light_time_left == 0)
4124 Feld[x][y] = EL_LIGHT_SWITCH;
4125 DrawLevelField(x, y);
4127 else if (element == EL_EMC_DRIPPER &&
4128 game.light_time_left > 0)
4130 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
4131 DrawLevelField(x, y);
4133 else if (element == EL_EMC_DRIPPER_ACTIVE &&
4134 game.light_time_left == 0)
4136 Feld[x][y] = EL_EMC_DRIPPER;
4137 DrawLevelField(x, y);
4139 else if (element == EL_INVISIBLE_STEELWALL ||
4140 element == EL_INVISIBLE_WALL ||
4141 element == EL_INVISIBLE_SAND)
4143 if (game.light_time_left > 0)
4144 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
4146 DrawLevelField(x, y);
4148 /* uncrumble neighbour fields, if needed */
4149 if (element == EL_INVISIBLE_SAND)
4150 DrawLevelFieldCrumbledSandNeighbours(x, y);
4152 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
4153 element == EL_INVISIBLE_WALL_ACTIVE ||
4154 element == EL_INVISIBLE_SAND_ACTIVE)
4156 if (game.light_time_left == 0)
4157 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
4159 DrawLevelField(x, y);
4161 /* re-crumble neighbour fields, if needed */
4162 if (element == EL_INVISIBLE_SAND)
4163 DrawLevelFieldCrumbledSandNeighbours(x, y);
4168 static void RedrawAllInvisibleElementsForLenses()
4173 SCAN_PLAYFIELD(x, y)
4175 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
4178 int element = Feld[x][y];
4180 if (element == EL_EMC_DRIPPER &&
4181 game.lenses_time_left > 0)
4183 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
4184 DrawLevelField(x, y);
4186 else if (element == EL_EMC_DRIPPER_ACTIVE &&
4187 game.lenses_time_left == 0)
4189 Feld[x][y] = EL_EMC_DRIPPER;
4190 DrawLevelField(x, y);
4192 else if (element == EL_INVISIBLE_STEELWALL ||
4193 element == EL_INVISIBLE_WALL ||
4194 element == EL_INVISIBLE_SAND)
4196 if (game.lenses_time_left > 0)
4197 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
4199 DrawLevelField(x, y);
4201 /* uncrumble neighbour fields, if needed */
4202 if (element == EL_INVISIBLE_SAND)
4203 DrawLevelFieldCrumbledSandNeighbours(x, y);
4205 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
4206 element == EL_INVISIBLE_WALL_ACTIVE ||
4207 element == EL_INVISIBLE_SAND_ACTIVE)
4209 if (game.lenses_time_left == 0)
4210 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
4212 DrawLevelField(x, y);
4214 /* re-crumble neighbour fields, if needed */
4215 if (element == EL_INVISIBLE_SAND)
4216 DrawLevelFieldCrumbledSandNeighbours(x, y);
4221 static void RedrawAllInvisibleElementsForMagnifier()
4226 SCAN_PLAYFIELD(x, y)
4228 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
4231 int element = Feld[x][y];
4233 if (element == EL_EMC_FAKE_GRASS &&
4234 game.magnify_time_left > 0)
4236 Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
4237 DrawLevelField(x, y);
4239 else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
4240 game.magnify_time_left == 0)
4242 Feld[x][y] = EL_EMC_FAKE_GRASS;
4243 DrawLevelField(x, y);
4245 else if (IS_GATE_GRAY(element) &&
4246 game.magnify_time_left > 0)
4248 Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
4249 element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
4250 IS_EM_GATE_GRAY(element) ?
4251 element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
4252 IS_EMC_GATE_GRAY(element) ?
4253 element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
4255 DrawLevelField(x, y);
4257 else if (IS_GATE_GRAY_ACTIVE(element) &&
4258 game.magnify_time_left == 0)
4260 Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
4261 element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
4262 IS_EM_GATE_GRAY_ACTIVE(element) ?
4263 element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
4264 IS_EMC_GATE_GRAY_ACTIVE(element) ?
4265 element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
4267 DrawLevelField(x, y);
4272 static void ToggleLightSwitch(int x, int y)
4274 int element = Feld[x][y];
4276 game.light_time_left =
4277 (element == EL_LIGHT_SWITCH ?
4278 level.time_light * FRAMES_PER_SECOND : 0);
4280 RedrawAllLightSwitchesAndInvisibleElements();
4283 static void ActivateTimegateSwitch(int x, int y)
4287 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
4290 SCAN_PLAYFIELD(xx, yy)
4292 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
4295 int element = Feld[xx][yy];
4297 if (element == EL_TIMEGATE_CLOSED ||
4298 element == EL_TIMEGATE_CLOSING)
4300 Feld[xx][yy] = EL_TIMEGATE_OPENING;
4301 PlayLevelSound(xx, yy, SND_TIMEGATE_OPENING);
4305 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
4307 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
4308 DrawLevelField(xx, yy);
4314 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
4317 void Impact(int x, int y)
4319 boolean last_line = (y == lev_fieldy - 1);
4320 boolean object_hit = FALSE;
4321 boolean impact = (last_line || object_hit);
4322 int element = Feld[x][y];
4323 int smashed = EL_STEELWALL;
4325 if (!last_line) /* check if element below was hit */
4327 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
4330 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
4331 MovDir[x][y + 1] != MV_DOWN ||
4332 MovPos[x][y + 1] <= TILEY / 2));
4334 /* do not smash moving elements that left the smashed field in time */
4335 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
4336 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
4339 #if USE_QUICKSAND_IMPACT_BUGFIX
4340 if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
4342 RemoveMovingField(x, y + 1);
4343 Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
4344 Feld[x][y + 2] = EL_ROCK;
4345 DrawLevelField(x, y + 2);
4352 smashed = MovingOrBlocked2Element(x, y + 1);
4354 impact = (last_line || object_hit);
4357 if (!last_line && smashed == EL_ACID) /* element falls into acid */
4359 SplashAcid(x, y + 1);
4363 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
4364 /* only reset graphic animation if graphic really changes after impact */
4366 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
4368 ResetGfxAnimation(x, y);
4369 DrawLevelField(x, y);
4372 if (impact && CAN_EXPLODE_IMPACT(element))
4377 else if (impact && element == EL_PEARL)
4379 ResetGfxAnimation(x, y);
4381 Feld[x][y] = EL_PEARL_BREAKING;
4382 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4385 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
4387 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4392 if (impact && element == EL_AMOEBA_DROP)
4394 if (object_hit && IS_PLAYER(x, y + 1))
4395 KillPlayerUnlessEnemyProtected(x, y + 1);
4396 else if (object_hit && smashed == EL_PENGUIN)
4400 Feld[x][y] = EL_AMOEBA_GROWING;
4401 Store[x][y] = EL_AMOEBA_WET;
4403 ResetRandomAnimationValue(x, y);
4408 if (object_hit) /* check which object was hit */
4410 if (CAN_PASS_MAGIC_WALL(element) &&
4411 (smashed == EL_MAGIC_WALL ||
4412 smashed == EL_BD_MAGIC_WALL))
4415 int activated_magic_wall =
4416 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
4417 EL_BD_MAGIC_WALL_ACTIVE);
4419 /* activate magic wall / mill */
4421 SCAN_PLAYFIELD(xx, yy)
4423 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
4425 if (Feld[xx][yy] == smashed)
4426 Feld[xx][yy] = activated_magic_wall;
4428 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
4429 game.magic_wall_active = TRUE;
4431 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
4432 SND_MAGIC_WALL_ACTIVATING :
4433 SND_BD_MAGIC_WALL_ACTIVATING));
4436 if (IS_PLAYER(x, y + 1))
4438 if (CAN_SMASH_PLAYER(element))
4440 KillPlayerUnlessEnemyProtected(x, y + 1);
4444 else if (smashed == EL_PENGUIN)
4446 if (CAN_SMASH_PLAYER(element))
4452 else if (element == EL_BD_DIAMOND)
4454 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
4460 else if (((element == EL_SP_INFOTRON ||
4461 element == EL_SP_ZONK) &&
4462 (smashed == EL_SP_SNIKSNAK ||
4463 smashed == EL_SP_ELECTRON ||
4464 smashed == EL_SP_DISK_ORANGE)) ||
4465 (element == EL_SP_INFOTRON &&
4466 smashed == EL_SP_DISK_YELLOW))
4471 else if (CAN_SMASH_EVERYTHING(element))
4473 if (IS_CLASSIC_ENEMY(smashed) ||
4474 CAN_EXPLODE_SMASHED(smashed))
4479 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
4481 if (smashed == EL_LAMP ||
4482 smashed == EL_LAMP_ACTIVE)
4487 else if (smashed == EL_NUT)
4489 Feld[x][y + 1] = EL_NUT_BREAKING;
4490 PlayLevelSound(x, y, SND_NUT_BREAKING);
4491 RaiseScoreElement(EL_NUT);
4494 else if (smashed == EL_PEARL)
4496 ResetGfxAnimation(x, y);
4498 Feld[x][y + 1] = EL_PEARL_BREAKING;
4499 PlayLevelSound(x, y, SND_PEARL_BREAKING);
4502 else if (smashed == EL_DIAMOND)
4504 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
4505 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
4508 else if (IS_BELT_SWITCH(smashed))
4510 ToggleBeltSwitch(x, y + 1);
4512 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
4513 smashed == EL_SWITCHGATE_SWITCH_DOWN)
4515 ToggleSwitchgateSwitch(x, y + 1);
4517 else if (smashed == EL_LIGHT_SWITCH ||
4518 smashed == EL_LIGHT_SWITCH_ACTIVE)
4520 ToggleLightSwitch(x, y + 1);
4525 TestIfElementSmashesCustomElement(x, y, MV_DOWN);
4528 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4530 CheckElementChangeBySide(x, y + 1, smashed, element,
4531 CE_SWITCHED, CH_SIDE_TOP);
4532 CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
4538 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4543 /* play sound of magic wall / mill */
4545 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
4546 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
4548 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
4549 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
4550 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
4551 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
4556 /* play sound of object that hits the ground */
4557 if (last_line || object_hit)
4558 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4561 inline static void TurnRoundExt(int x, int y)
4573 { 0, 0 }, { 0, 0 }, { 0, 0 },
4578 int left, right, back;
4582 { MV_DOWN, MV_UP, MV_RIGHT },
4583 { MV_UP, MV_DOWN, MV_LEFT },
4585 { MV_LEFT, MV_RIGHT, MV_DOWN },
4589 { MV_RIGHT, MV_LEFT, MV_UP }
4592 int element = Feld[x][y];
4593 int move_pattern = element_info[element].move_pattern;
4595 int old_move_dir = MovDir[x][y];
4596 int left_dir = turn[old_move_dir].left;
4597 int right_dir = turn[old_move_dir].right;
4598 int back_dir = turn[old_move_dir].back;
4600 int left_dx = move_xy[left_dir].dx, left_dy = move_xy[left_dir].dy;
4601 int right_dx = move_xy[right_dir].dx, right_dy = move_xy[right_dir].dy;
4602 int move_dx = move_xy[old_move_dir].dx, move_dy = move_xy[old_move_dir].dy;
4603 int back_dx = move_xy[back_dir].dx, back_dy = move_xy[back_dir].dy;
4605 int left_x = x + left_dx, left_y = y + left_dy;
4606 int right_x = x + right_dx, right_y = y + right_dy;
4607 int move_x = x + move_dx, move_y = y + move_dy;
4611 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4613 TestIfBadThingTouchesOtherBadThing(x, y);
4615 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
4616 MovDir[x][y] = right_dir;
4617 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4618 MovDir[x][y] = left_dir;
4620 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
4622 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
4625 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
4627 TestIfBadThingTouchesOtherBadThing(x, y);
4629 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
4630 MovDir[x][y] = left_dir;
4631 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4632 MovDir[x][y] = right_dir;
4634 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
4636 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
4639 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4641 TestIfBadThingTouchesOtherBadThing(x, y);
4643 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
4644 MovDir[x][y] = left_dir;
4645 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
4646 MovDir[x][y] = right_dir;
4648 if (MovDir[x][y] != old_move_dir)
4651 else if (element == EL_YAMYAM)
4653 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
4654 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
4656 if (can_turn_left && can_turn_right)
4657 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4658 else if (can_turn_left)
4659 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4660 else if (can_turn_right)
4661 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4663 MovDir[x][y] = back_dir;
4665 MovDelay[x][y] = 16 + 16 * RND(3);
4667 else if (element == EL_DARK_YAMYAM)
4669 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4671 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4674 if (can_turn_left && can_turn_right)
4675 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4676 else if (can_turn_left)
4677 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4678 else if (can_turn_right)
4679 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4681 MovDir[x][y] = back_dir;
4683 MovDelay[x][y] = 16 + 16 * RND(3);
4685 else if (element == EL_PACMAN)
4687 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
4688 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
4690 if (can_turn_left && can_turn_right)
4691 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4692 else if (can_turn_left)
4693 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4694 else if (can_turn_right)
4695 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4697 MovDir[x][y] = back_dir;
4699 MovDelay[x][y] = 6 + RND(40);
4701 else if (element == EL_PIG)
4703 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
4704 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
4705 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
4706 boolean should_turn_left, should_turn_right, should_move_on;
4708 int rnd = RND(rnd_value);
4710 should_turn_left = (can_turn_left &&
4712 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
4713 y + back_dy + left_dy)));
4714 should_turn_right = (can_turn_right &&
4716 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
4717 y + back_dy + right_dy)));
4718 should_move_on = (can_move_on &&
4721 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
4722 y + move_dy + left_dy) ||
4723 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
4724 y + move_dy + right_dy)));
4726 if (should_turn_left || should_turn_right || should_move_on)
4728 if (should_turn_left && should_turn_right && should_move_on)
4729 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
4730 rnd < 2 * rnd_value / 3 ? right_dir :
4732 else if (should_turn_left && should_turn_right)
4733 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4734 else if (should_turn_left && should_move_on)
4735 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
4736 else if (should_turn_right && should_move_on)
4737 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
4738 else if (should_turn_left)
4739 MovDir[x][y] = left_dir;
4740 else if (should_turn_right)
4741 MovDir[x][y] = right_dir;
4742 else if (should_move_on)
4743 MovDir[x][y] = old_move_dir;
4745 else if (can_move_on && rnd > rnd_value / 8)
4746 MovDir[x][y] = old_move_dir;
4747 else if (can_turn_left && can_turn_right)
4748 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4749 else if (can_turn_left && rnd > rnd_value / 8)
4750 MovDir[x][y] = left_dir;
4751 else if (can_turn_right && rnd > rnd_value/8)
4752 MovDir[x][y] = right_dir;
4754 MovDir[x][y] = back_dir;
4756 xx = x + move_xy[MovDir[x][y]].dx;
4757 yy = y + move_xy[MovDir[x][y]].dy;
4759 if (!IN_LEV_FIELD(xx, yy) ||
4760 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
4761 MovDir[x][y] = old_move_dir;
4765 else if (element == EL_DRAGON)
4767 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
4768 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
4769 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
4771 int rnd = RND(rnd_value);
4773 if (can_move_on && rnd > rnd_value / 8)
4774 MovDir[x][y] = old_move_dir;
4775 else if (can_turn_left && can_turn_right)
4776 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4777 else if (can_turn_left && rnd > rnd_value / 8)
4778 MovDir[x][y] = left_dir;
4779 else if (can_turn_right && rnd > rnd_value / 8)
4780 MovDir[x][y] = right_dir;
4782 MovDir[x][y] = back_dir;
4784 xx = x + move_xy[MovDir[x][y]].dx;
4785 yy = y + move_xy[MovDir[x][y]].dy;
4787 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
4788 MovDir[x][y] = old_move_dir;
4792 else if (element == EL_MOLE)
4794 boolean can_move_on =
4795 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
4796 IS_AMOEBOID(Feld[move_x][move_y]) ||
4797 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
4800 boolean can_turn_left =
4801 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
4802 IS_AMOEBOID(Feld[left_x][left_y])));
4804 boolean can_turn_right =
4805 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
4806 IS_AMOEBOID(Feld[right_x][right_y])));
4808 if (can_turn_left && can_turn_right)
4809 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
4810 else if (can_turn_left)
4811 MovDir[x][y] = left_dir;
4813 MovDir[x][y] = right_dir;
4816 if (MovDir[x][y] != old_move_dir)
4819 else if (element == EL_BALLOON)
4821 MovDir[x][y] = game.wind_direction;
4824 else if (element == EL_SPRING)
4826 #if USE_NEW_SPRING_BUMPER
4827 if (MovDir[x][y] & MV_HORIZONTAL)
4829 if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
4830 !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
4832 Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
4833 ResetGfxAnimation(move_x, move_y);
4834 DrawLevelField(move_x, move_y);
4836 MovDir[x][y] = back_dir;
4838 else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
4839 SPRING_CAN_ENTER_FIELD(element, x, y + 1))
4840 MovDir[x][y] = MV_NONE;
4843 if (MovDir[x][y] & MV_HORIZONTAL &&
4844 (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
4845 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
4846 MovDir[x][y] = MV_NONE;
4851 else if (element == EL_ROBOT ||
4852 element == EL_SATELLITE ||
4853 element == EL_PENGUIN ||
4854 element == EL_EMC_ANDROID)
4856 int attr_x = -1, attr_y = -1;
4867 for (i = 0; i < MAX_PLAYERS; i++)
4869 struct PlayerInfo *player = &stored_player[i];
4870 int jx = player->jx, jy = player->jy;
4872 if (!player->active)
4876 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
4884 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
4885 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
4886 game.engine_version < VERSION_IDENT(3,1,0,0)))
4892 if (element == EL_PENGUIN)
4895 static int xy[4][2] =
4903 for (i = 0; i < NUM_DIRECTIONS; i++)
4905 int ex = x + xy[i][0];
4906 int ey = y + xy[i][1];
4908 if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
4917 MovDir[x][y] = MV_NONE;
4919 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
4920 else if (attr_x > x)
4921 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
4923 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
4924 else if (attr_y > y)
4925 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
4927 if (element == EL_ROBOT)
4931 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4932 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
4933 Moving2Blocked(x, y, &newx, &newy);
4935 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
4936 MovDelay[x][y] = 8 + 8 * !RND(3);
4938 MovDelay[x][y] = 16;
4940 else if (element == EL_PENGUIN)
4946 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4948 boolean first_horiz = RND(2);
4949 int new_move_dir = MovDir[x][y];
4952 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4953 Moving2Blocked(x, y, &newx, &newy);
4955 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
4959 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4960 Moving2Blocked(x, y, &newx, &newy);
4962 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
4965 MovDir[x][y] = old_move_dir;
4969 else if (element == EL_SATELLITE)
4975 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4977 boolean first_horiz = RND(2);
4978 int new_move_dir = MovDir[x][y];
4981 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4982 Moving2Blocked(x, y, &newx, &newy);
4984 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
4988 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4989 Moving2Blocked(x, y, &newx, &newy);
4991 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
4994 MovDir[x][y] = old_move_dir;
4998 else if (element == EL_EMC_ANDROID)
5000 static int check_pos[16] =
5002 -1, /* 0 => (invalid) */
5003 7, /* 1 => MV_LEFT */
5004 3, /* 2 => MV_RIGHT */
5005 -1, /* 3 => (invalid) */
5007 0, /* 5 => MV_LEFT | MV_UP */
5008 2, /* 6 => MV_RIGHT | MV_UP */
5009 -1, /* 7 => (invalid) */
5010 5, /* 8 => MV_DOWN */
5011 6, /* 9 => MV_LEFT | MV_DOWN */
5012 4, /* 10 => MV_RIGHT | MV_DOWN */
5013 -1, /* 11 => (invalid) */
5014 -1, /* 12 => (invalid) */
5015 -1, /* 13 => (invalid) */
5016 -1, /* 14 => (invalid) */
5017 -1, /* 15 => (invalid) */
5025 { -1, -1, MV_LEFT | MV_UP },
5027 { +1, -1, MV_RIGHT | MV_UP },
5028 { +1, 0, MV_RIGHT },
5029 { +1, +1, MV_RIGHT | MV_DOWN },
5031 { -1, +1, MV_LEFT | MV_DOWN },
5034 int start_pos, check_order;
5035 boolean can_clone = FALSE;
5038 /* check if there is any free field around current position */
5039 for (i = 0; i < 8; i++)
5041 int newx = x + check_xy[i].dx;
5042 int newy = y + check_xy[i].dy;
5044 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5052 if (can_clone) /* randomly find an element to clone */
5056 start_pos = check_pos[RND(8)];
5057 check_order = (RND(2) ? -1 : +1);
5059 for (i = 0; i < 8; i++)
5061 int pos_raw = start_pos + i * check_order;
5062 int pos = (pos_raw + 8) % 8;
5063 int newx = x + check_xy[pos].dx;
5064 int newy = y + check_xy[pos].dy;
5066 if (ANDROID_CAN_CLONE_FIELD(newx, newy))
5068 element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
5069 element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
5071 Store[x][y] = Feld[newx][newy];
5080 if (can_clone) /* randomly find a direction to move */
5084 start_pos = check_pos[RND(8)];
5085 check_order = (RND(2) ? -1 : +1);
5087 for (i = 0; i < 8; i++)
5089 int pos_raw = start_pos + i * check_order;
5090 int pos = (pos_raw + 8) % 8;
5091 int newx = x + check_xy[pos].dx;
5092 int newy = y + check_xy[pos].dy;
5093 int new_move_dir = check_xy[pos].dir;
5095 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5097 MovDir[x][y] = new_move_dir;
5098 MovDelay[x][y] = level.android_clone_time * 8 + 1;
5107 if (can_clone) /* cloning and moving successful */
5110 /* cannot clone -- try to move towards player */
5112 start_pos = check_pos[MovDir[x][y] & 0x0f];
5113 check_order = (RND(2) ? -1 : +1);
5115 for (i = 0; i < 3; i++)
5117 /* first check start_pos, then previous/next or (next/previous) pos */
5118 int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
5119 int pos = (pos_raw + 8) % 8;
5120 int newx = x + check_xy[pos].dx;
5121 int newy = y + check_xy[pos].dy;
5122 int new_move_dir = check_xy[pos].dir;
5124 if (IS_PLAYER(newx, newy))
5127 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
5129 MovDir[x][y] = new_move_dir;
5130 MovDelay[x][y] = level.android_move_time * 8 + 1;
5137 else if (move_pattern == MV_TURNING_LEFT ||
5138 move_pattern == MV_TURNING_RIGHT ||
5139 move_pattern == MV_TURNING_LEFT_RIGHT ||
5140 move_pattern == MV_TURNING_RIGHT_LEFT ||
5141 move_pattern == MV_TURNING_RANDOM ||
5142 move_pattern == MV_ALL_DIRECTIONS)
5144 boolean can_turn_left =
5145 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
5146 boolean can_turn_right =
5147 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
5149 if (element_info[element].move_stepsize == 0) /* "not moving" */
5152 if (move_pattern == MV_TURNING_LEFT)
5153 MovDir[x][y] = left_dir;
5154 else if (move_pattern == MV_TURNING_RIGHT)
5155 MovDir[x][y] = right_dir;
5156 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
5157 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
5158 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
5159 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
5160 else if (move_pattern == MV_TURNING_RANDOM)
5161 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
5162 can_turn_right && !can_turn_left ? right_dir :
5163 RND(2) ? left_dir : right_dir);
5164 else if (can_turn_left && can_turn_right)
5165 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5166 else if (can_turn_left)
5167 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5168 else if (can_turn_right)
5169 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5171 MovDir[x][y] = back_dir;
5173 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5175 else if (move_pattern == MV_HORIZONTAL ||
5176 move_pattern == MV_VERTICAL)
5178 if (move_pattern & old_move_dir)
5179 MovDir[x][y] = back_dir;
5180 else if (move_pattern == MV_HORIZONTAL)
5181 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
5182 else if (move_pattern == MV_VERTICAL)
5183 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
5185 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5187 else if (move_pattern & MV_ANY_DIRECTION)
5189 MovDir[x][y] = move_pattern;
5190 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5192 else if (move_pattern & MV_WIND_DIRECTION)
5194 MovDir[x][y] = game.wind_direction;
5195 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5197 else if (move_pattern == MV_ALONG_LEFT_SIDE)
5199 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
5200 MovDir[x][y] = left_dir;
5201 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5202 MovDir[x][y] = right_dir;
5204 if (MovDir[x][y] != old_move_dir)
5205 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5207 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
5209 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
5210 MovDir[x][y] = right_dir;
5211 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5212 MovDir[x][y] = left_dir;
5214 if (MovDir[x][y] != old_move_dir)
5215 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5217 else if (move_pattern == MV_TOWARDS_PLAYER ||
5218 move_pattern == MV_AWAY_FROM_PLAYER)
5220 int attr_x = -1, attr_y = -1;
5222 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
5233 for (i = 0; i < MAX_PLAYERS; i++)
5235 struct PlayerInfo *player = &stored_player[i];
5236 int jx = player->jx, jy = player->jy;
5238 if (!player->active)
5242 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
5250 MovDir[x][y] = MV_NONE;
5252 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
5253 else if (attr_x > x)
5254 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
5256 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
5257 else if (attr_y > y)
5258 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
5260 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5262 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5264 boolean first_horiz = RND(2);
5265 int new_move_dir = MovDir[x][y];
5267 if (element_info[element].move_stepsize == 0) /* "not moving" */
5269 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
5270 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5276 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5277 Moving2Blocked(x, y, &newx, &newy);
5279 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5283 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5284 Moving2Blocked(x, y, &newx, &newy);
5286 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5289 MovDir[x][y] = old_move_dir;
5292 else if (move_pattern == MV_WHEN_PUSHED ||
5293 move_pattern == MV_WHEN_DROPPED)
5295 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5296 MovDir[x][y] = MV_NONE;
5300 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
5302 static int test_xy[7][2] =
5312 static int test_dir[7] =
5322 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
5323 int move_preference = -1000000; /* start with very low preference */
5324 int new_move_dir = MV_NONE;
5325 int start_test = RND(4);
5328 for (i = 0; i < NUM_DIRECTIONS; i++)
5330 int move_dir = test_dir[start_test + i];
5331 int move_dir_preference;
5333 xx = x + test_xy[start_test + i][0];
5334 yy = y + test_xy[start_test + i][1];
5336 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
5337 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
5339 new_move_dir = move_dir;
5344 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
5347 move_dir_preference = -1 * RunnerVisit[xx][yy];
5348 if (hunter_mode && PlayerVisit[xx][yy] > 0)
5349 move_dir_preference = PlayerVisit[xx][yy];
5351 if (move_dir_preference > move_preference)
5353 /* prefer field that has not been visited for the longest time */
5354 move_preference = move_dir_preference;
5355 new_move_dir = move_dir;
5357 else if (move_dir_preference == move_preference &&
5358 move_dir == old_move_dir)
5360 /* prefer last direction when all directions are preferred equally */
5361 move_preference = move_dir_preference;
5362 new_move_dir = move_dir;
5366 MovDir[x][y] = new_move_dir;
5367 if (old_move_dir != new_move_dir)
5368 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5372 static void TurnRound(int x, int y)
5374 int direction = MovDir[x][y];
5376 int element, graphic;
5381 GfxDir[x][y] = MovDir[x][y];
5383 if (direction != MovDir[x][y])
5387 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
5390 element = Feld[x][y];
5391 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5393 if (graphic_info[graphic].anim_global_sync)
5394 GfxFrame[x][y] = FrameCounter;
5395 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5396 GfxFrame[x][y] = CustomValue[x][y];
5397 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5398 GfxFrame[x][y] = element_info[element].collect_score;
5402 static boolean JustBeingPushed(int x, int y)
5406 for (i = 0; i < MAX_PLAYERS; i++)
5408 struct PlayerInfo *player = &stored_player[i];
5410 if (player->active && player->is_pushing && player->MovPos)
5412 int next_jx = player->jx + (player->jx - player->last_jx);
5413 int next_jy = player->jy + (player->jy - player->last_jy);
5415 if (x == next_jx && y == next_jy)
5423 void StartMoving(int x, int y)
5425 boolean started_moving = FALSE; /* some elements can fall _and_ move */
5426 int element = Feld[x][y];
5431 if (MovDelay[x][y] == 0)
5432 GfxAction[x][y] = ACTION_DEFAULT;
5434 if (CAN_FALL(element) && y < lev_fieldy - 1)
5436 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
5437 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
5438 if (JustBeingPushed(x, y))
5441 if (element == EL_QUICKSAND_FULL)
5443 if (IS_FREE(x, y + 1))
5445 InitMovingField(x, y, MV_DOWN);
5446 started_moving = TRUE;
5448 Feld[x][y] = EL_QUICKSAND_EMPTYING;
5449 Store[x][y] = EL_ROCK;
5451 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
5453 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5455 if (!MovDelay[x][y])
5456 MovDelay[x][y] = TILEY + 1;
5465 Feld[x][y] = EL_QUICKSAND_EMPTY;
5466 Feld[x][y + 1] = EL_QUICKSAND_FULL;
5467 Store[x][y + 1] = Store[x][y];
5470 PlayLevelSoundAction(x, y, ACTION_FILLING);
5473 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
5474 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5476 InitMovingField(x, y, MV_DOWN);
5477 started_moving = TRUE;
5479 Feld[x][y] = EL_QUICKSAND_FILLING;
5480 Store[x][y] = element;
5482 PlayLevelSoundAction(x, y, ACTION_FILLING);
5484 else if (element == EL_MAGIC_WALL_FULL)
5486 if (IS_FREE(x, y + 1))
5488 InitMovingField(x, y, MV_DOWN);
5489 started_moving = TRUE;
5491 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
5492 Store[x][y] = EL_CHANGED(Store[x][y]);
5494 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
5496 if (!MovDelay[x][y])
5497 MovDelay[x][y] = TILEY/4 + 1;
5506 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
5507 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
5508 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
5512 else if (element == EL_BD_MAGIC_WALL_FULL)
5514 if (IS_FREE(x, y + 1))
5516 InitMovingField(x, y, MV_DOWN);
5517 started_moving = TRUE;
5519 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
5520 Store[x][y] = EL_CHANGED2(Store[x][y]);
5522 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
5524 if (!MovDelay[x][y])
5525 MovDelay[x][y] = TILEY/4 + 1;
5534 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
5535 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
5536 Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
5540 else if (CAN_PASS_MAGIC_WALL(element) &&
5541 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
5542 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
5544 InitMovingField(x, y, MV_DOWN);
5545 started_moving = TRUE;
5548 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
5549 EL_BD_MAGIC_WALL_FILLING);
5550 Store[x][y] = element;
5552 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
5554 SplashAcid(x, y + 1);
5556 InitMovingField(x, y, MV_DOWN);
5557 started_moving = TRUE;
5559 Store[x][y] = EL_ACID;
5561 else if ((game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5562 CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
5564 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
5565 CAN_FALL(element) && WasJustFalling[x][y] &&
5566 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
5568 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
5569 CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
5570 (Feld[x][y + 1] == EL_BLOCKED)))
5572 /* this is needed for a special case not covered by calling "Impact()"
5573 from "ContinueMoving()": if an element moves to a tile directly below
5574 another element which was just falling on that tile (which was empty
5575 in the previous frame), the falling element above would just stop
5576 instead of smashing the element below (in previous version, the above
5577 element was just checked for "moving" instead of "falling", resulting
5578 in incorrect smashes caused by horizontal movement of the above
5579 element; also, the case of the player being the element to smash was
5580 simply not covered here... :-/ ) */
5582 CheckCollision[x][y] = 0;
5586 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
5588 if (MovDir[x][y] == MV_NONE)
5590 InitMovingField(x, y, MV_DOWN);
5591 started_moving = TRUE;
5594 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
5596 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
5597 MovDir[x][y] = MV_DOWN;
5599 InitMovingField(x, y, MV_DOWN);
5600 started_moving = TRUE;
5602 else if (element == EL_AMOEBA_DROP)
5604 Feld[x][y] = EL_AMOEBA_GROWING;
5605 Store[x][y] = EL_AMOEBA_WET;
5607 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
5608 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
5609 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
5610 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
5612 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
5613 (IS_FREE(x - 1, y + 1) ||
5614 Feld[x - 1][y + 1] == EL_ACID));
5615 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
5616 (IS_FREE(x + 1, y + 1) ||
5617 Feld[x + 1][y + 1] == EL_ACID));
5618 boolean can_fall_any = (can_fall_left || can_fall_right);
5619 boolean can_fall_both = (can_fall_left && can_fall_right);
5620 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
5622 #if USE_NEW_ALL_SLIPPERY
5623 if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
5625 if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
5626 can_fall_right = FALSE;
5627 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
5628 can_fall_left = FALSE;
5629 else if (slippery_type == SLIPPERY_ONLY_LEFT)
5630 can_fall_right = FALSE;
5631 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
5632 can_fall_left = FALSE;
5634 can_fall_any = (can_fall_left || can_fall_right);
5635 can_fall_both = FALSE;
5638 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
5640 if (slippery_type == SLIPPERY_ONLY_LEFT)
5641 can_fall_right = FALSE;
5642 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
5643 can_fall_left = FALSE;
5644 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
5645 can_fall_right = FALSE;
5646 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
5647 can_fall_left = FALSE;
5649 can_fall_any = (can_fall_left || can_fall_right);
5650 can_fall_both = (can_fall_left && can_fall_right);
5654 #if USE_NEW_ALL_SLIPPERY
5656 #if USE_NEW_SP_SLIPPERY
5657 /* !!! better use the same properties as for custom elements here !!! */
5658 else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
5659 can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
5661 can_fall_right = FALSE; /* slip down on left side */
5662 can_fall_both = FALSE;
5667 #if USE_NEW_ALL_SLIPPERY
5670 if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
5671 can_fall_right = FALSE; /* slip down on left side */
5673 can_fall_left = !(can_fall_right = RND(2));
5675 can_fall_both = FALSE;
5680 if (game.emulation == EMU_BOULDERDASH ||
5681 element == EL_BD_ROCK || element == EL_BD_DIAMOND)
5682 can_fall_right = FALSE; /* slip down on left side */
5684 can_fall_left = !(can_fall_right = RND(2));
5686 can_fall_both = FALSE;
5692 /* if not determined otherwise, prefer left side for slipping down */
5693 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
5694 started_moving = TRUE;
5698 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
5700 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
5703 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
5704 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
5705 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
5706 int belt_dir = game.belt_dir[belt_nr];
5708 if ((belt_dir == MV_LEFT && left_is_free) ||
5709 (belt_dir == MV_RIGHT && right_is_free))
5711 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
5713 InitMovingField(x, y, belt_dir);
5714 started_moving = TRUE;
5716 Pushed[x][y] = TRUE;
5717 Pushed[nextx][y] = TRUE;
5719 GfxAction[x][y] = ACTION_DEFAULT;
5723 MovDir[x][y] = 0; /* if element was moving, stop it */
5728 /* not "else if" because of elements that can fall and move (EL_SPRING) */
5730 if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
5732 if (CAN_MOVE(element) && !started_moving)
5735 int move_pattern = element_info[element].move_pattern;
5740 if (MovDir[x][y] == MV_NONE)
5742 printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
5743 x, y, element, element_info[element].token_name);
5744 printf("StartMoving(): This should never happen!\n");
5749 Moving2Blocked(x, y, &newx, &newy);
5751 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
5754 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5755 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5757 WasJustMoving[x][y] = 0;
5758 CheckCollision[x][y] = 0;
5760 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
5762 if (Feld[x][y] != element) /* element has changed */
5766 if (!MovDelay[x][y]) /* start new movement phase */
5768 /* all objects that can change their move direction after each step
5769 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
5771 if (element != EL_YAMYAM &&
5772 element != EL_DARK_YAMYAM &&
5773 element != EL_PACMAN &&
5774 !(move_pattern & MV_ANY_DIRECTION) &&
5775 move_pattern != MV_TURNING_LEFT &&
5776 move_pattern != MV_TURNING_RIGHT &&
5777 move_pattern != MV_TURNING_LEFT_RIGHT &&
5778 move_pattern != MV_TURNING_RIGHT_LEFT &&
5779 move_pattern != MV_TURNING_RANDOM)
5783 if (MovDelay[x][y] && (element == EL_BUG ||
5784 element == EL_SPACESHIP ||
5785 element == EL_SP_SNIKSNAK ||
5786 element == EL_SP_ELECTRON ||
5787 element == EL_MOLE))
5788 DrawLevelField(x, y);
5792 if (MovDelay[x][y]) /* wait some time before next movement */
5796 if (element == EL_ROBOT ||
5797 element == EL_YAMYAM ||
5798 element == EL_DARK_YAMYAM)
5800 DrawLevelElementAnimationIfNeeded(x, y, element);
5801 PlayLevelSoundAction(x, y, ACTION_WAITING);
5803 else if (element == EL_SP_ELECTRON)
5804 DrawLevelElementAnimationIfNeeded(x, y, element);
5805 else if (element == EL_DRAGON)
5808 int dir = MovDir[x][y];
5809 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
5810 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
5811 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
5812 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
5813 dir == MV_UP ? IMG_FLAMES_1_UP :
5814 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
5815 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5817 GfxAction[x][y] = ACTION_ATTACKING;
5819 if (IS_PLAYER(x, y))
5820 DrawPlayerField(x, y);
5822 DrawLevelField(x, y);
5824 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
5826 for (i = 1; i <= 3; i++)
5828 int xx = x + i * dx;
5829 int yy = y + i * dy;
5830 int sx = SCREENX(xx);
5831 int sy = SCREENY(yy);
5832 int flame_graphic = graphic + (i - 1);
5834 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
5839 int flamed = MovingOrBlocked2Element(xx, yy);
5843 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
5845 else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
5846 RemoveMovingField(xx, yy);
5848 RemoveField(xx, yy);
5850 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
5853 RemoveMovingField(xx, yy);
5856 ChangeDelay[xx][yy] = 0;
5858 Feld[xx][yy] = EL_FLAMES;
5860 if (IN_SCR_FIELD(sx, sy))
5862 DrawLevelFieldCrumbledSand(xx, yy);
5863 DrawGraphic(sx, sy, flame_graphic, frame);
5868 if (Feld[xx][yy] == EL_FLAMES)
5869 Feld[xx][yy] = EL_EMPTY;
5870 DrawLevelField(xx, yy);
5875 if (MovDelay[x][y]) /* element still has to wait some time */
5877 PlayLevelSoundAction(x, y, ACTION_WAITING);
5883 /* now make next step */
5885 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
5887 if (DONT_COLLIDE_WITH(element) &&
5888 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
5889 !PLAYER_ENEMY_PROTECTED(newx, newy))
5891 TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
5896 else if (CAN_MOVE_INTO_ACID(element) &&
5897 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
5898 !IS_MV_DIAGONAL(MovDir[x][y]) &&
5899 (MovDir[x][y] == MV_DOWN ||
5900 game.engine_version >= VERSION_IDENT(3,1,0,0)))
5902 SplashAcid(newx, newy);
5903 Store[x][y] = EL_ACID;
5905 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
5907 if (Feld[newx][newy] == EL_EXIT_OPEN)
5910 DrawLevelField(x, y);
5912 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
5913 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
5914 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
5916 local_player->friends_still_needed--;
5917 if (!local_player->friends_still_needed &&
5918 !local_player->GameOver && AllPlayersGone)
5919 local_player->LevelSolved = local_player->GameOver = TRUE;
5923 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
5925 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
5926 DrawLevelField(newx, newy);
5928 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
5930 else if (!IS_FREE(newx, newy))
5932 GfxAction[x][y] = ACTION_WAITING;
5934 if (IS_PLAYER(x, y))
5935 DrawPlayerField(x, y);
5937 DrawLevelField(x, y);
5942 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
5944 if (IS_FOOD_PIG(Feld[newx][newy]))
5946 if (IS_MOVING(newx, newy))
5947 RemoveMovingField(newx, newy);
5950 Feld[newx][newy] = EL_EMPTY;
5951 DrawLevelField(newx, newy);
5954 PlayLevelSound(x, y, SND_PIG_DIGGING);
5956 else if (!IS_FREE(newx, newy))
5958 if (IS_PLAYER(x, y))
5959 DrawPlayerField(x, y);
5961 DrawLevelField(x, y);
5966 else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
5968 if (Store[x][y] != EL_EMPTY)
5970 boolean can_clone = FALSE;
5973 /* check if element to clone is still there */
5974 for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
5976 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
5984 /* cannot clone or target field not free anymore -- do not clone */
5985 if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
5986 Store[x][y] = EL_EMPTY;
5989 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
5991 if (IS_MV_DIAGONAL(MovDir[x][y]))
5993 int diagonal_move_dir = MovDir[x][y];
5994 int stored = Store[x][y];
5995 int change_delay = 8;
5998 /* android is moving diagonally */
6000 CreateField(x, y, EL_DIAGONAL_SHRINKING);
6002 Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
6003 GfxElement[x][y] = EL_EMC_ANDROID;
6004 GfxAction[x][y] = ACTION_SHRINKING;
6005 GfxDir[x][y] = diagonal_move_dir;
6006 ChangeDelay[x][y] = change_delay;
6008 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
6011 DrawLevelGraphicAnimation(x, y, graphic);
6012 PlayLevelSoundAction(x, y, ACTION_SHRINKING);
6014 if (Feld[newx][newy] == EL_ACID)
6016 SplashAcid(newx, newy);
6021 CreateField(newx, newy, EL_DIAGONAL_GROWING);
6023 Store[newx][newy] = EL_EMC_ANDROID;
6024 GfxElement[newx][newy] = EL_EMC_ANDROID;
6025 GfxAction[newx][newy] = ACTION_GROWING;
6026 GfxDir[newx][newy] = diagonal_move_dir;
6027 ChangeDelay[newx][newy] = change_delay;
6029 graphic = el_act_dir2img(GfxElement[newx][newy],
6030 GfxAction[newx][newy], GfxDir[newx][newy]);
6032 DrawLevelGraphicAnimation(newx, newy, graphic);
6033 PlayLevelSoundAction(newx, newy, ACTION_GROWING);
6039 Feld[newx][newy] = EL_EMPTY;
6040 DrawLevelField(newx, newy);
6042 PlayLevelSoundAction(x, y, ACTION_DIGGING);
6045 else if (!IS_FREE(newx, newy))
6048 if (IS_PLAYER(x, y))
6049 DrawPlayerField(x, y);
6051 DrawLevelField(x, y);
6057 else if (IS_CUSTOM_ELEMENT(element) &&
6058 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6060 int new_element = Feld[newx][newy];
6062 if (!IS_FREE(newx, newy))
6064 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
6065 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
6068 /* no element can dig solid indestructible elements */
6069 if (IS_INDESTRUCTIBLE(new_element) &&
6070 !IS_DIGGABLE(new_element) &&
6071 !IS_COLLECTIBLE(new_element))
6074 if (AmoebaNr[newx][newy] &&
6075 (new_element == EL_AMOEBA_FULL ||
6076 new_element == EL_BD_AMOEBA ||
6077 new_element == EL_AMOEBA_GROWING))
6079 AmoebaCnt[AmoebaNr[newx][newy]]--;
6080 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6083 if (IS_MOVING(newx, newy))
6084 RemoveMovingField(newx, newy);
6087 RemoveField(newx, newy);
6088 DrawLevelField(newx, newy);
6091 /* if digged element was about to explode, prevent the explosion */
6092 ExplodeField[newx][newy] = EX_TYPE_NONE;
6094 PlayLevelSoundAction(x, y, action);
6097 Store[newx][newy] = EL_EMPTY;
6099 /* this makes it possible to leave the removed element again */
6100 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
6101 Store[newx][newy] = new_element;
6103 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
6105 int move_leave_element = element_info[element].move_leave_element;
6107 /* this makes it possible to leave the removed element again */
6108 Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
6109 new_element : move_leave_element);
6113 if (move_pattern & MV_MAZE_RUNNER_STYLE)
6115 RunnerVisit[x][y] = FrameCounter;
6116 PlayerVisit[x][y] /= 8; /* expire player visit path */
6119 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
6121 if (!IS_FREE(newx, newy))
6123 if (IS_PLAYER(x, y))
6124 DrawPlayerField(x, y);
6126 DrawLevelField(x, y);
6132 boolean wanna_flame = !RND(10);
6133 int dx = newx - x, dy = newy - y;
6134 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
6135 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
6136 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
6137 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
6138 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
6139 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
6142 IS_CLASSIC_ENEMY(element1) ||
6143 IS_CLASSIC_ENEMY(element2)) &&
6144 element1 != EL_DRAGON && element2 != EL_DRAGON &&
6145 element1 != EL_FLAMES && element2 != EL_FLAMES)
6147 ResetGfxAnimation(x, y);
6148 GfxAction[x][y] = ACTION_ATTACKING;
6150 if (IS_PLAYER(x, y))
6151 DrawPlayerField(x, y);
6153 DrawLevelField(x, y);
6155 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
6157 MovDelay[x][y] = 50;
6161 RemoveField(newx, newy);
6163 Feld[newx][newy] = EL_FLAMES;
6164 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
6167 RemoveField(newx1, newy1);
6169 Feld[newx1][newy1] = EL_FLAMES;
6171 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
6174 RemoveField(newx2, newy2);
6176 Feld[newx2][newy2] = EL_FLAMES;
6183 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6184 Feld[newx][newy] == EL_DIAMOND)
6186 if (IS_MOVING(newx, newy))
6187 RemoveMovingField(newx, newy);
6190 Feld[newx][newy] = EL_EMPTY;
6191 DrawLevelField(newx, newy);
6194 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
6196 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6197 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
6199 if (AmoebaNr[newx][newy])
6201 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6202 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
6203 Feld[newx][newy] == EL_BD_AMOEBA)
6204 AmoebaCnt[AmoebaNr[newx][newy]]--;
6209 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
6211 RemoveMovingField(newx, newy);
6214 if (IS_MOVING(newx, newy))
6216 RemoveMovingField(newx, newy);
6221 Feld[newx][newy] = EL_EMPTY;
6222 DrawLevelField(newx, newy);
6225 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
6227 else if ((element == EL_PACMAN || element == EL_MOLE)
6228 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
6230 if (AmoebaNr[newx][newy])
6232 AmoebaCnt2[AmoebaNr[newx][newy]]--;
6233 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
6234 Feld[newx][newy] == EL_BD_AMOEBA)
6235 AmoebaCnt[AmoebaNr[newx][newy]]--;
6238 if (element == EL_MOLE)
6240 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
6241 PlayLevelSound(x, y, SND_MOLE_DIGGING);
6243 ResetGfxAnimation(x, y);
6244 GfxAction[x][y] = ACTION_DIGGING;
6245 DrawLevelField(x, y);
6247 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
6249 return; /* wait for shrinking amoeba */
6251 else /* element == EL_PACMAN */
6253 Feld[newx][newy] = EL_EMPTY;
6254 DrawLevelField(newx, newy);
6255 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
6258 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
6259 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
6260 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
6262 /* wait for shrinking amoeba to completely disappear */
6265 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
6267 /* object was running against a wall */
6272 /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
6273 if (move_pattern & MV_ANY_DIRECTION &&
6274 move_pattern == MovDir[x][y])
6276 int blocking_element =
6277 (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
6279 CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
6282 element = Feld[x][y]; /* element might have changed */
6286 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
6287 DrawLevelElementAnimation(x, y, element);
6289 if (DONT_TOUCH(element))
6290 TestIfBadThingTouchesPlayer(x, y);
6295 InitMovingField(x, y, MovDir[x][y]);
6297 PlayLevelSoundAction(x, y, ACTION_MOVING);
6301 ContinueMoving(x, y);
6304 void ContinueMoving(int x, int y)
6306 int element = Feld[x][y];
6307 struct ElementInfo *ei = &element_info[element];
6308 int direction = MovDir[x][y];
6309 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
6310 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
6311 int newx = x + dx, newy = y + dy;
6312 int stored = Store[x][y];
6313 int stored_new = Store[newx][newy];
6314 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
6315 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
6316 boolean last_line = (newy == lev_fieldy - 1);
6318 MovPos[x][y] += getElementMoveStepsize(x, y);
6320 if (pushed_by_player) /* special case: moving object pushed by player */
6321 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
6323 if (ABS(MovPos[x][y]) < TILEX)
6325 DrawLevelField(x, y);
6327 return; /* element is still moving */
6330 /* element reached destination field */
6332 Feld[x][y] = EL_EMPTY;
6333 Feld[newx][newy] = element;
6334 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
6336 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
6338 element = Feld[newx][newy] = EL_ACID;
6340 else if (element == EL_MOLE)
6342 Feld[x][y] = EL_SAND;
6344 DrawLevelFieldCrumbledSandNeighbours(x, y);
6346 else if (element == EL_QUICKSAND_FILLING)
6348 element = Feld[newx][newy] = get_next_element(element);
6349 Store[newx][newy] = Store[x][y];
6351 else if (element == EL_QUICKSAND_EMPTYING)
6353 Feld[x][y] = get_next_element(element);
6354 element = Feld[newx][newy] = Store[x][y];
6356 else if (element == EL_MAGIC_WALL_FILLING)
6358 element = Feld[newx][newy] = get_next_element(element);
6359 if (!game.magic_wall_active)
6360 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
6361 Store[newx][newy] = Store[x][y];
6363 else if (element == EL_MAGIC_WALL_EMPTYING)
6365 Feld[x][y] = get_next_element(element);
6366 if (!game.magic_wall_active)
6367 Feld[x][y] = EL_MAGIC_WALL_DEAD;
6368 element = Feld[newx][newy] = Store[x][y];
6370 #if USE_NEW_CUSTOM_VALUE
6371 InitField(newx, newy, FALSE);
6374 else if (element == EL_BD_MAGIC_WALL_FILLING)
6376 element = Feld[newx][newy] = get_next_element(element);
6377 if (!game.magic_wall_active)
6378 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
6379 Store[newx][newy] = Store[x][y];
6381 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
6383 Feld[x][y] = get_next_element(element);
6384 if (!game.magic_wall_active)
6385 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
6386 element = Feld[newx][newy] = Store[x][y];
6388 #if USE_NEW_CUSTOM_VALUE
6389 InitField(newx, newy, FALSE);
6392 else if (element == EL_AMOEBA_DROPPING)
6394 Feld[x][y] = get_next_element(element);
6395 element = Feld[newx][newy] = Store[x][y];
6397 else if (element == EL_SOKOBAN_OBJECT)
6400 Feld[x][y] = Back[x][y];
6402 if (Back[newx][newy])
6403 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
6405 Back[x][y] = Back[newx][newy] = 0;
6408 Store[x][y] = EL_EMPTY;
6413 MovDelay[newx][newy] = 0;
6416 if (CAN_CHANGE_OR_HAS_ACTION(element))
6418 if (CAN_CHANGE(element))
6421 /* copy element change control values to new field */
6422 ChangeDelay[newx][newy] = ChangeDelay[x][y];
6423 ChangePage[newx][newy] = ChangePage[x][y];
6424 ChangeCount[newx][newy] = ChangeCount[x][y];
6425 ChangeEvent[newx][newy] = ChangeEvent[x][y];
6428 #if USE_NEW_CUSTOM_VALUE
6429 CustomValue[newx][newy] = CustomValue[x][y];
6435 #if USE_NEW_CUSTOM_VALUE
6436 CustomValue[newx][newy] = CustomValue[x][y];
6440 ChangeDelay[x][y] = 0;
6441 ChangePage[x][y] = -1;
6442 ChangeCount[x][y] = 0;
6443 ChangeEvent[x][y] = -1;
6445 #if USE_NEW_CUSTOM_VALUE
6446 CustomValue[x][y] = 0;
6449 /* copy animation control values to new field */
6450 GfxFrame[newx][newy] = GfxFrame[x][y];
6451 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
6452 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
6453 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
6455 Pushed[x][y] = Pushed[newx][newy] = FALSE;
6457 /* some elements can leave other elements behind after moving */
6459 if (ei->move_leave_element != EL_EMPTY &&
6460 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
6461 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
6463 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
6464 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
6465 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
6468 int move_leave_element = ei->move_leave_element;
6472 /* this makes it possible to leave the removed element again */
6473 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
6474 move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
6476 /* this makes it possible to leave the removed element again */
6477 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
6478 move_leave_element = stored;
6481 /* this makes it possible to leave the removed element again */
6482 if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
6483 ei->move_leave_element == EL_TRIGGER_ELEMENT)
6484 move_leave_element = stored;
6487 Feld[x][y] = move_leave_element;
6489 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
6490 MovDir[x][y] = direction;
6492 InitField(x, y, FALSE);
6494 if (GFX_CRUMBLED(Feld[x][y]))
6495 DrawLevelFieldCrumbledSandNeighbours(x, y);
6497 if (ELEM_IS_PLAYER(move_leave_element))
6498 RelocatePlayer(x, y, move_leave_element);
6501 /* do this after checking for left-behind element */
6502 ResetGfxAnimation(x, y); /* reset animation values for old field */
6504 if (!CAN_MOVE(element) ||
6505 (CAN_FALL(element) && direction == MV_DOWN &&
6506 (element == EL_SPRING ||
6507 element_info[element].move_pattern == MV_WHEN_PUSHED ||
6508 element_info[element].move_pattern == MV_WHEN_DROPPED)))
6509 GfxDir[x][y] = MovDir[newx][newy] = 0;
6511 DrawLevelField(x, y);
6512 DrawLevelField(newx, newy);
6514 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
6516 /* prevent pushed element from moving on in pushed direction */
6517 if (pushed_by_player && CAN_MOVE(element) &&
6518 element_info[element].move_pattern & MV_ANY_DIRECTION &&
6519 !(element_info[element].move_pattern & direction))
6520 TurnRound(newx, newy);
6522 /* prevent elements on conveyor belt from moving on in last direction */
6523 if (pushed_by_conveyor && CAN_FALL(element) &&
6524 direction & MV_HORIZONTAL)
6525 MovDir[newx][newy] = 0;
6527 if (!pushed_by_player)
6529 int nextx = newx + dx, nexty = newy + dy;
6530 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
6532 WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
6534 if (CAN_FALL(element) && direction == MV_DOWN)
6535 WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
6537 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
6538 CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
6541 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
6543 TestIfBadThingTouchesPlayer(newx, newy);
6544 TestIfBadThingTouchesFriend(newx, newy);
6546 if (!IS_CUSTOM_ELEMENT(element))
6547 TestIfBadThingTouchesOtherBadThing(newx, newy);
6549 else if (element == EL_PENGUIN)
6550 TestIfFriendTouchesBadThing(newx, newy);
6552 /* give the player one last chance (one more frame) to move away */
6553 if (CAN_FALL(element) && direction == MV_DOWN &&
6554 (last_line || (!IS_FREE(x, newy + 1) &&
6555 (!IS_PLAYER(x, newy + 1) ||
6556 game.engine_version < VERSION_IDENT(3,1,1,0)))))
6559 if (pushed_by_player && !game.use_change_when_pushing_bug)
6561 int push_side = MV_DIR_OPPOSITE(direction);
6562 struct PlayerInfo *player = PLAYERINFO(x, y);
6564 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
6565 player->index_bit, push_side);
6566 CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
6567 player->index_bit, push_side);
6570 if (element == EL_EMC_ANDROID && pushed_by_player) /* make another move */
6571 MovDelay[newx][newy] = 1;
6573 CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
6575 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
6578 if (ChangePage[newx][newy] != -1) /* delayed change */
6580 int page = ChangePage[newx][newy];
6581 struct ElementChangeInfo *change = &ei->change_page[page];
6583 ChangePage[newx][newy] = -1;
6585 if (change->can_change)
6587 if (ChangeElement(newx, newy, element, page))
6589 if (change->post_change_function)
6590 change->post_change_function(newx, newy);
6594 if (change->has_action)
6595 ExecuteCustomElementAction(newx, newy, element, page);
6599 TestIfElementHitsCustomElement(newx, newy, direction);
6600 TestIfPlayerTouchesCustomElement(newx, newy);
6601 TestIfElementTouchesCustomElement(newx, newy);
6604 if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
6605 IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
6606 CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
6607 MV_DIR_OPPOSITE(direction));
6611 int AmoebeNachbarNr(int ax, int ay)
6614 int element = Feld[ax][ay];
6616 static int xy[4][2] =
6624 for (i = 0; i < NUM_DIRECTIONS; i++)
6626 int x = ax + xy[i][0];
6627 int y = ay + xy[i][1];
6629 if (!IN_LEV_FIELD(x, y))
6632 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
6633 group_nr = AmoebaNr[x][y];
6639 void AmoebenVereinigen(int ax, int ay)
6641 int i, x, y, xx, yy;
6642 int new_group_nr = AmoebaNr[ax][ay];
6643 static int xy[4][2] =
6651 if (new_group_nr == 0)
6654 for (i = 0; i < NUM_DIRECTIONS; i++)
6659 if (!IN_LEV_FIELD(x, y))
6662 if ((Feld[x][y] == EL_AMOEBA_FULL ||
6663 Feld[x][y] == EL_BD_AMOEBA ||
6664 Feld[x][y] == EL_AMOEBA_DEAD) &&
6665 AmoebaNr[x][y] != new_group_nr)
6667 int old_group_nr = AmoebaNr[x][y];
6669 if (old_group_nr == 0)
6672 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
6673 AmoebaCnt[old_group_nr] = 0;
6674 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
6675 AmoebaCnt2[old_group_nr] = 0;
6678 SCAN_PLAYFIELD(xx, yy)
6680 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
6683 if (AmoebaNr[xx][yy] == old_group_nr)
6684 AmoebaNr[xx][yy] = new_group_nr;
6690 void AmoebeUmwandeln(int ax, int ay)
6694 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
6696 int group_nr = AmoebaNr[ax][ay];
6701 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
6702 printf("AmoebeUmwandeln(): This should never happen!\n");
6708 SCAN_PLAYFIELD(x, y)
6710 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
6713 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
6716 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
6720 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
6721 SND_AMOEBA_TURNING_TO_GEM :
6722 SND_AMOEBA_TURNING_TO_ROCK));
6727 static int xy[4][2] =
6735 for (i = 0; i < NUM_DIRECTIONS; i++)
6740 if (!IN_LEV_FIELD(x, y))
6743 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
6745 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
6746 SND_AMOEBA_TURNING_TO_GEM :
6747 SND_AMOEBA_TURNING_TO_ROCK));
6754 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
6757 int group_nr = AmoebaNr[ax][ay];
6758 boolean done = FALSE;
6763 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
6764 printf("AmoebeUmwandelnBD(): This should never happen!\n");
6770 SCAN_PLAYFIELD(x, y)
6772 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
6775 if (AmoebaNr[x][y] == group_nr &&
6776 (Feld[x][y] == EL_AMOEBA_DEAD ||
6777 Feld[x][y] == EL_BD_AMOEBA ||
6778 Feld[x][y] == EL_AMOEBA_GROWING))
6781 Feld[x][y] = new_element;
6782 InitField(x, y, FALSE);
6783 DrawLevelField(x, y);
6789 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
6790 SND_BD_AMOEBA_TURNING_TO_ROCK :
6791 SND_BD_AMOEBA_TURNING_TO_GEM));
6794 void AmoebeWaechst(int x, int y)
6796 static unsigned long sound_delay = 0;
6797 static unsigned long sound_delay_value = 0;
6799 if (!MovDelay[x][y]) /* start new growing cycle */
6803 if (DelayReached(&sound_delay, sound_delay_value))
6805 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
6806 sound_delay_value = 30;
6810 if (MovDelay[x][y]) /* wait some time before growing bigger */
6813 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6815 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
6816 6 - MovDelay[x][y]);
6818 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
6821 if (!MovDelay[x][y])
6823 Feld[x][y] = Store[x][y];
6825 DrawLevelField(x, y);
6830 void AmoebaDisappearing(int x, int y)
6832 static unsigned long sound_delay = 0;
6833 static unsigned long sound_delay_value = 0;
6835 if (!MovDelay[x][y]) /* start new shrinking cycle */
6839 if (DelayReached(&sound_delay, sound_delay_value))
6840 sound_delay_value = 30;
6843 if (MovDelay[x][y]) /* wait some time before shrinking */
6846 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6848 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
6849 6 - MovDelay[x][y]);
6851 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
6854 if (!MovDelay[x][y])
6856 Feld[x][y] = EL_EMPTY;
6857 DrawLevelField(x, y);
6859 /* don't let mole enter this field in this cycle;
6860 (give priority to objects falling to this field from above) */
6866 void AmoebeAbleger(int ax, int ay)
6869 int element = Feld[ax][ay];
6870 int graphic = el2img(element);
6871 int newax = ax, neway = ay;
6872 boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
6873 static int xy[4][2] =
6881 if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
6883 Feld[ax][ay] = EL_AMOEBA_DEAD;
6884 DrawLevelField(ax, ay);
6888 if (IS_ANIMATED(graphic))
6889 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6891 if (!MovDelay[ax][ay]) /* start making new amoeba field */
6892 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
6894 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
6897 if (MovDelay[ax][ay])
6901 if (can_drop) /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
6904 int x = ax + xy[start][0];
6905 int y = ay + xy[start][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 if (newax == ax && neway == ay)
6921 else /* normal or "filled" (BD style) amoeba */
6924 boolean waiting_for_player = FALSE;
6926 for (i = 0; i < NUM_DIRECTIONS; i++)
6928 int j = (start + i) % 4;
6929 int x = ax + xy[j][0];
6930 int y = ay + xy[j][1];
6932 if (!IN_LEV_FIELD(x, y))
6935 if (IS_FREE(x, y) ||
6936 CAN_GROW_INTO(Feld[x][y]) ||
6937 Feld[x][y] == EL_QUICKSAND_EMPTY)
6943 else if (IS_PLAYER(x, y))
6944 waiting_for_player = TRUE;
6947 if (newax == ax && neway == ay) /* amoeba cannot grow */
6949 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
6951 Feld[ax][ay] = EL_AMOEBA_DEAD;
6952 DrawLevelField(ax, ay);
6953 AmoebaCnt[AmoebaNr[ax][ay]]--;
6955 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
6957 if (element == EL_AMOEBA_FULL)
6958 AmoebeUmwandeln(ax, ay);
6959 else if (element == EL_BD_AMOEBA)
6960 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
6965 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
6967 /* amoeba gets larger by growing in some direction */
6969 int new_group_nr = AmoebaNr[ax][ay];
6972 if (new_group_nr == 0)
6974 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
6975 printf("AmoebeAbleger(): This should never happen!\n");
6980 AmoebaNr[newax][neway] = new_group_nr;
6981 AmoebaCnt[new_group_nr]++;
6982 AmoebaCnt2[new_group_nr]++;
6984 /* if amoeba touches other amoeba(s) after growing, unify them */
6985 AmoebenVereinigen(newax, neway);
6987 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
6989 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
6995 if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
6996 (neway == lev_fieldy - 1 && newax != ax))
6998 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
6999 Store[newax][neway] = element;
7001 else if (neway == ay || element == EL_EMC_DRIPPER)
7003 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
7005 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
7009 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
7010 Feld[ax][ay] = EL_AMOEBA_DROPPING;
7011 Store[ax][ay] = EL_AMOEBA_DROP;
7012 ContinueMoving(ax, ay);
7016 DrawLevelField(newax, neway);
7019 void Life(int ax, int ay)
7023 static int life[4] = { 2, 3, 3, 3 }; /* parameters for "game of life" */
7026 int element = Feld[ax][ay];
7027 int graphic = el2img(element);
7028 int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
7030 boolean changed = FALSE;
7032 if (IS_ANIMATED(graphic))
7033 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7038 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
7039 MovDelay[ax][ay] = life_time;
7041 if (MovDelay[ax][ay]) /* wait some time before next cycle */
7044 if (MovDelay[ax][ay])
7048 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
7050 int xx = ax+x1, yy = ay+y1;
7053 if (!IN_LEV_FIELD(xx, yy))
7056 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
7058 int x = xx+x2, y = yy+y2;
7060 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
7063 if (((Feld[x][y] == element ||
7064 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
7066 (IS_FREE(x, y) && Stop[x][y]))
7070 if (xx == ax && yy == ay) /* field in the middle */
7072 if (nachbarn < life_parameter[0] ||
7073 nachbarn > life_parameter[1])
7075 Feld[xx][yy] = EL_EMPTY;
7077 DrawLevelField(xx, yy);
7078 Stop[xx][yy] = TRUE;
7082 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
7083 { /* free border field */
7084 if (nachbarn >= life_parameter[2] &&
7085 nachbarn <= life_parameter[3])
7087 Feld[xx][yy] = element;
7088 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
7090 DrawLevelField(xx, yy);
7091 Stop[xx][yy] = TRUE;
7098 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
7099 SND_GAME_OF_LIFE_GROWING);
7102 static void InitRobotWheel(int x, int y)
7104 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
7107 static void RunRobotWheel(int x, int y)
7109 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
7112 static void StopRobotWheel(int x, int y)
7114 if (ZX == x && ZY == y)
7118 static void InitTimegateWheel(int x, int y)
7120 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
7123 static void RunTimegateWheel(int x, int y)
7125 PlayLevelSound(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
7128 static void InitMagicBallDelay(int x, int y)
7131 ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
7133 ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
7137 static void ActivateMagicBall(int bx, int by)
7141 if (level.ball_random)
7143 int pos_border = RND(8); /* select one of the eight border elements */
7144 int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
7145 int xx = pos_content % 3;
7146 int yy = pos_content / 3;
7151 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
7152 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
7156 for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
7158 int xx = x - bx + 1;
7159 int yy = y - by + 1;
7161 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
7162 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
7166 game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
7169 static void InitDiagonalMovingElement(int x, int y)
7172 MovDelay[x][y] = level.android_move_time;
7176 void CheckExit(int x, int y)
7178 if (local_player->gems_still_needed > 0 ||
7179 local_player->sokobanfields_still_needed > 0 ||
7180 local_player->lights_still_needed > 0)
7182 int element = Feld[x][y];
7183 int graphic = el2img(element);
7185 if (IS_ANIMATED(graphic))
7186 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7191 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7194 Feld[x][y] = EL_EXIT_OPENING;
7196 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
7199 void CheckExitSP(int x, int y)
7201 if (local_player->gems_still_needed > 0)
7203 int element = Feld[x][y];
7204 int graphic = el2img(element);
7206 if (IS_ANIMATED(graphic))
7207 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7212 if (AllPlayersGone) /* do not re-open exit door closed after last player */
7215 Feld[x][y] = EL_SP_EXIT_OPENING;
7217 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
7220 static void CloseAllOpenTimegates()
7225 SCAN_PLAYFIELD(x, y)
7227 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7230 int element = Feld[x][y];
7232 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
7234 Feld[x][y] = EL_TIMEGATE_CLOSING;
7236 PlayLevelSoundAction(x, y, ACTION_CLOSING);
7241 void EdelsteinFunkeln(int x, int y)
7243 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
7246 if (Feld[x][y] == EL_BD_DIAMOND)
7249 if (MovDelay[x][y] == 0) /* next animation frame */
7250 MovDelay[x][y] = 11 * !SimpleRND(500);
7252 if (MovDelay[x][y] != 0) /* wait some time before next frame */
7256 if (setup.direct_draw && MovDelay[x][y])
7257 SetDrawtoField(DRAW_BUFFERED);
7259 DrawLevelElementAnimation(x, y, Feld[x][y]);
7261 if (MovDelay[x][y] != 0)
7263 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
7264 10 - MovDelay[x][y]);
7266 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
7268 if (setup.direct_draw)
7272 dest_x = FX + SCREENX(x) * TILEX;
7273 dest_y = FY + SCREENY(y) * TILEY;
7275 BlitBitmap(drawto_field, window,
7276 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
7277 SetDrawtoField(DRAW_DIRECT);
7283 void MauerWaechst(int x, int y)
7287 if (!MovDelay[x][y]) /* next animation frame */
7288 MovDelay[x][y] = 3 * delay;
7290 if (MovDelay[x][y]) /* wait some time before next frame */
7294 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7296 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
7297 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
7299 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
7302 if (!MovDelay[x][y])
7304 if (MovDir[x][y] == MV_LEFT)
7306 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
7307 DrawLevelField(x - 1, y);
7309 else if (MovDir[x][y] == MV_RIGHT)
7311 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
7312 DrawLevelField(x + 1, y);
7314 else if (MovDir[x][y] == MV_UP)
7316 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
7317 DrawLevelField(x, y - 1);
7321 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
7322 DrawLevelField(x, y + 1);
7325 Feld[x][y] = Store[x][y];
7327 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7328 DrawLevelField(x, y);
7333 void MauerAbleger(int ax, int ay)
7335 int element = Feld[ax][ay];
7336 int graphic = el2img(element);
7337 boolean oben_frei = FALSE, unten_frei = FALSE;
7338 boolean links_frei = FALSE, rechts_frei = FALSE;
7339 boolean oben_massiv = FALSE, unten_massiv = FALSE;
7340 boolean links_massiv = FALSE, rechts_massiv = FALSE;
7341 boolean new_wall = FALSE;
7343 if (IS_ANIMATED(graphic))
7344 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7346 if (!MovDelay[ax][ay]) /* start building new wall */
7347 MovDelay[ax][ay] = 6;
7349 if (MovDelay[ax][ay]) /* wait some time before building new wall */
7352 if (MovDelay[ax][ay])
7356 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
7358 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
7360 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
7362 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
7365 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
7366 element == EL_EXPANDABLE_WALL_ANY)
7370 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
7371 Store[ax][ay-1] = element;
7372 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
7373 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
7374 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
7375 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
7380 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
7381 Store[ax][ay+1] = element;
7382 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
7383 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
7384 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
7385 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
7390 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7391 element == EL_EXPANDABLE_WALL_ANY ||
7392 element == EL_EXPANDABLE_WALL)
7396 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
7397 Store[ax-1][ay] = element;
7398 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
7399 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
7400 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
7401 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
7407 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
7408 Store[ax+1][ay] = element;
7409 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
7410 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
7411 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
7412 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
7417 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
7418 DrawLevelField(ax, ay);
7420 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
7422 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
7423 unten_massiv = TRUE;
7424 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
7425 links_massiv = TRUE;
7426 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
7427 rechts_massiv = TRUE;
7429 if (((oben_massiv && unten_massiv) ||
7430 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7431 element == EL_EXPANDABLE_WALL) &&
7432 ((links_massiv && rechts_massiv) ||
7433 element == EL_EXPANDABLE_WALL_VERTICAL))
7434 Feld[ax][ay] = EL_WALL;
7437 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
7440 void CheckForDragon(int x, int y)
7443 boolean dragon_found = FALSE;
7444 static int xy[4][2] =
7452 for (i = 0; i < NUM_DIRECTIONS; i++)
7454 for (j = 0; j < 4; j++)
7456 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7458 if (IN_LEV_FIELD(xx, yy) &&
7459 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
7461 if (Feld[xx][yy] == EL_DRAGON)
7462 dragon_found = TRUE;
7471 for (i = 0; i < NUM_DIRECTIONS; i++)
7473 for (j = 0; j < 3; j++)
7475 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7477 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
7479 Feld[xx][yy] = EL_EMPTY;
7480 DrawLevelField(xx, yy);
7489 static void InitBuggyBase(int x, int y)
7491 int element = Feld[x][y];
7492 int activating_delay = FRAMES_PER_SECOND / 4;
7495 (element == EL_SP_BUGGY_BASE ?
7496 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
7497 element == EL_SP_BUGGY_BASE_ACTIVATING ?
7499 element == EL_SP_BUGGY_BASE_ACTIVE ?
7500 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
7503 static void WarnBuggyBase(int x, int y)
7506 static int xy[4][2] =
7514 for (i = 0; i < NUM_DIRECTIONS; i++)
7516 int xx = x + xy[i][0];
7517 int yy = y + xy[i][1];
7519 if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
7521 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
7528 static void InitTrap(int x, int y)
7530 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
7533 static void ActivateTrap(int x, int y)
7535 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
7538 static void ChangeActiveTrap(int x, int y)
7540 int graphic = IMG_TRAP_ACTIVE;
7542 /* if new animation frame was drawn, correct crumbled sand border */
7543 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
7544 DrawLevelFieldCrumbledSand(x, y);
7547 static int getSpecialActionElement(int element, int number, int base_element)
7549 return (element != EL_EMPTY ? element :
7550 number != -1 ? base_element + number - 1 :
7554 static int getModifiedActionNumber(int value_old, int operator, int operand,
7555 int value_min, int value_max)
7557 int value_new = (operator == CA_MODE_SET ? operand :
7558 operator == CA_MODE_ADD ? value_old + operand :
7559 operator == CA_MODE_SUBTRACT ? value_old - operand :
7560 operator == CA_MODE_MULTIPLY ? value_old * operand :
7561 operator == CA_MODE_DIVIDE ? value_old / MAX(1, operand) :
7562 operator == CA_MODE_MODULO ? value_old % MAX(1, operand) :
7565 return (value_new < value_min ? value_min :
7566 value_new > value_max ? value_max :
7570 static void ExecuteCustomElementAction(int x, int y, int element, int page)
7572 struct ElementInfo *ei = &element_info[element];
7573 struct ElementChangeInfo *change = &ei->change_page[page];
7574 int action_type = change->action_type;
7575 int action_mode = change->action_mode;
7576 int action_arg = change->action_arg;
7579 if (!change->has_action)
7582 /* ---------- determine action paramater values -------------------------- */
7584 int level_time_value =
7585 (level.time > 0 ? TimeLeft :
7588 int action_arg_element =
7589 (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player :
7590 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
7591 action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element :
7594 int action_arg_direction =
7595 (action_arg >= CA_ARG_DIRECTION_LEFT &&
7596 action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
7597 action_arg == CA_ARG_DIRECTION_TRIGGER ?
7598 change->actual_trigger_side :
7599 action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
7600 MV_DIR_OPPOSITE(change->actual_trigger_side) :
7603 int action_arg_number_min =
7604 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
7607 int action_arg_number_max =
7608 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
7609 action_type == CA_SET_LEVEL_GEMS ? 999 :
7610 action_type == CA_SET_LEVEL_TIME ? 9999 :
7611 action_type == CA_SET_LEVEL_SCORE ? 99999 :
7612 action_type == CA_SET_CE_SCORE ? 9999 :
7613 action_type == CA_SET_CE_VALUE ? 9999 :
7616 int action_arg_number_reset =
7617 (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize :
7618 action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
7619 action_type == CA_SET_LEVEL_TIME ? level.time :
7620 action_type == CA_SET_LEVEL_SCORE ? 0 :
7621 action_type == CA_SET_CE_SCORE ? 0 :
7623 action_type == CA_SET_CE_VALUE ? GET_NEW_CUSTOM_VALUE(element) :
7625 action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
7629 int action_arg_number =
7630 (action_arg <= CA_ARG_MAX ? action_arg :
7631 action_arg >= CA_ARG_SPEED_NOT_MOVING &&
7632 action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
7633 action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
7634 action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
7635 action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
7636 action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
7637 action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
7638 #if USE_NEW_CUSTOM_VALUE
7639 action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
7641 action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
7643 action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
7644 action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
7645 action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
7646 action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
7647 action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CUSTOM_VALUE(change->target_element) :
7648 action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
7649 action_arg == CA_ARG_ELEMENT_NR_TARGET ? change->target_element :
7650 action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
7653 int action_arg_number_old =
7654 (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
7655 action_type == CA_SET_LEVEL_TIME ? TimeLeft :
7656 action_type == CA_SET_LEVEL_SCORE ? local_player->score :
7657 action_type == CA_SET_CE_SCORE ? ei->collect_score :
7658 action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
7661 int action_arg_number_new =
7662 getModifiedActionNumber(action_arg_number_old,
7663 action_mode, action_arg_number,
7664 action_arg_number_min, action_arg_number_max);
7666 int trigger_player_bits =
7667 (change->actual_trigger_player >= EL_PLAYER_1 &&
7668 change->actual_trigger_player <= EL_PLAYER_4 ?
7669 (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
7672 int action_arg_player_bits =
7673 (action_arg >= CA_ARG_PLAYER_1 &&
7674 action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
7675 action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
7678 /* ---------- execute action -------------------------------------------- */
7687 /* ---------- level actions ------------------------------------------- */
7689 case CA_RESTART_LEVEL:
7691 game.restart_level = TRUE;
7696 case CA_SHOW_ENVELOPE:
7698 int element = getSpecialActionElement(action_arg_element,
7699 action_arg_number, EL_ENVELOPE_1);
7701 if (IS_ENVELOPE(element))
7702 local_player->show_envelope = element;
7707 case CA_SET_LEVEL_TIME:
7709 if (level.time > 0) /* only modify limited time value */
7711 TimeLeft = action_arg_number_new;
7713 DrawGameValue_Time(TimeLeft);
7715 if (!TimeLeft && setup.time_limit)
7716 for (i = 0; i < MAX_PLAYERS; i++)
7717 KillPlayer(&stored_player[i]);
7723 case CA_SET_LEVEL_SCORE:
7725 local_player->score = action_arg_number_new;
7727 DrawGameValue_Score(local_player->score);
7732 case CA_SET_LEVEL_GEMS:
7734 local_player->gems_still_needed = action_arg_number_new;
7736 DrawGameValue_Emeralds(local_player->gems_still_needed);
7741 case CA_SET_LEVEL_GRAVITY:
7743 game.gravity = (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
7744 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
7745 action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
7750 case CA_SET_LEVEL_WIND:
7752 game.wind_direction = action_arg_direction;
7757 /* ---------- player actions ------------------------------------------ */
7759 case CA_MOVE_PLAYER:
7761 /* automatically move to the next field in specified direction */
7762 for (i = 0; i < MAX_PLAYERS; i++)
7763 if (trigger_player_bits & (1 << i))
7764 stored_player[i].programmed_action = action_arg_direction;
7769 case CA_EXIT_PLAYER:
7771 for (i = 0; i < MAX_PLAYERS; i++)
7772 if (action_arg_player_bits & (1 << i))
7773 stored_player[i].LevelSolved = stored_player[i].GameOver = TRUE;
7778 case CA_KILL_PLAYER:
7780 for (i = 0; i < MAX_PLAYERS; i++)
7781 if (action_arg_player_bits & (1 << i))
7782 KillPlayer(&stored_player[i]);
7787 case CA_SET_PLAYER_KEYS:
7789 int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
7790 int element = getSpecialActionElement(action_arg_element,
7791 action_arg_number, EL_KEY_1);
7793 if (IS_KEY(element))
7795 for (i = 0; i < MAX_PLAYERS; i++)
7797 if (trigger_player_bits & (1 << i))
7799 stored_player[i].key[KEY_NR(element)] = key_state;
7801 DrawGameValue_Keys(stored_player[i].key);
7803 redraw_mask |= REDRAW_DOOR_1;
7811 case CA_SET_PLAYER_SPEED:
7813 for (i = 0; i < MAX_PLAYERS; i++)
7815 if (trigger_player_bits & (1 << i))
7817 int move_stepsize = TILEX / stored_player[i].move_delay_value;
7819 if (action_arg == CA_ARG_SPEED_FASTER &&
7820 stored_player[i].cannot_move)
7822 action_arg_number = STEPSIZE_VERY_SLOW;
7824 else if (action_arg == CA_ARG_SPEED_SLOWER ||
7825 action_arg == CA_ARG_SPEED_FASTER)
7827 action_arg_number = 2;
7828 action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
7833 getModifiedActionNumber(move_stepsize,
7836 action_arg_number_min,
7837 action_arg_number_max);
7840 SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
7842 /* make sure that value is power of 2 */
7843 move_stepsize = (1 << log_2(move_stepsize));
7845 /* do no immediately change -- the player might just be moving */
7846 stored_player[i].move_delay_value_next = TILEX / move_stepsize;
7848 stored_player[i].cannot_move =
7849 (action_arg == CA_ARG_SPEED_NOT_MOVING ? TRUE : FALSE);
7857 case CA_SET_PLAYER_SHIELD:
7859 for (i = 0; i < MAX_PLAYERS; i++)
7861 if (trigger_player_bits & (1 << i))
7863 if (action_arg == CA_ARG_SHIELD_OFF)
7865 stored_player[i].shield_normal_time_left = 0;
7866 stored_player[i].shield_deadly_time_left = 0;
7868 else if (action_arg == CA_ARG_SHIELD_NORMAL)
7870 stored_player[i].shield_normal_time_left = 999999;
7872 else if (action_arg == CA_ARG_SHIELD_DEADLY)
7874 stored_player[i].shield_normal_time_left = 999999;
7875 stored_player[i].shield_deadly_time_left = 999999;
7883 case CA_SET_PLAYER_ARTWORK:
7885 for (i = 0; i < MAX_PLAYERS; i++)
7887 if (trigger_player_bits & (1 << i))
7889 int artwork_element = action_arg_element;
7891 if (action_arg == CA_ARG_ELEMENT_RESET)
7893 (level.use_artwork_element[i] ? level.artwork_element[i] :
7894 stored_player[i].element_nr);
7896 stored_player[i].artwork_element = artwork_element;
7898 SetPlayerWaiting(&stored_player[i], FALSE);
7900 /* set number of special actions for bored and sleeping animation */
7901 stored_player[i].num_special_action_bored =
7902 get_num_special_action(artwork_element,
7903 ACTION_BORING_1, ACTION_BORING_LAST);
7904 stored_player[i].num_special_action_sleeping =
7905 get_num_special_action(artwork_element,
7906 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
7913 /* ---------- CE actions ---------------------------------------------- */
7915 case CA_SET_CE_SCORE:
7917 ei->collect_score = action_arg_number_new;
7922 case CA_SET_CE_VALUE:
7924 #if USE_NEW_CUSTOM_VALUE
7925 int last_custom_value = CustomValue[x][y];
7927 CustomValue[x][y] = action_arg_number_new;
7930 printf("::: Count == %d\n", CustomValue[x][y]);
7933 if (CustomValue[x][y] == 0 && last_custom_value > 0)
7936 printf("::: CE_VALUE_GETS_ZERO\n");
7939 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
7940 CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
7943 printf("::: RESULT: %d, %d\n", Feld[x][y], ChangePage[x][y]);
7951 /* ---------- engine actions ------------------------------------------ */
7953 case CA_SET_ENGINE_SCAN_MODE:
7955 InitPlayfieldScanMode(action_arg);
7965 static void CreateFieldExt(int x, int y, int element, boolean is_change)
7967 int previous_move_direction = MovDir[x][y];
7968 #if USE_NEW_CUSTOM_VALUE
7969 int last_ce_value = CustomValue[x][y];
7971 boolean add_player = (ELEM_IS_PLAYER(element) &&
7972 IS_WALKABLE(Feld[x][y]));
7974 /* check if element under player changes from accessible to unaccessible
7975 (needed for special case of dropping element which then changes) */
7976 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
7977 IS_ACCESSIBLE(Feld[x][y]) && !IS_ACCESSIBLE(element))
7986 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
7987 RemoveMovingField(x, y);
7991 Feld[x][y] = element;
7993 ResetGfxAnimation(x, y);
7994 ResetRandomAnimationValue(x, y);
7996 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
7997 MovDir[x][y] = previous_move_direction;
7999 #if USE_NEW_CUSTOM_VALUE
8000 if (element_info[Feld[x][y]].use_last_ce_value)
8001 CustomValue[x][y] = last_ce_value;
8004 InitField_WithBug1(x, y, FALSE);
8006 DrawLevelField(x, y);
8008 if (GFX_CRUMBLED(Feld[x][y]))
8009 DrawLevelFieldCrumbledSandNeighbours(x, y);
8012 /* "ChangeCount" not set yet to allow "entered by player" change one time */
8013 if (ELEM_IS_PLAYER(element))
8014 RelocatePlayer(x, y, element);
8017 ChangeCount[x][y]++; /* count number of changes in the same frame */
8019 TestIfBadThingTouchesPlayer(x, y);
8020 TestIfPlayerTouchesCustomElement(x, y);
8021 TestIfElementTouchesCustomElement(x, y);
8024 static void CreateField(int x, int y, int element)
8026 CreateFieldExt(x, y, element, FALSE);
8029 static void CreateElementFromChange(int x, int y, int element)
8031 element = GET_VALID_RUNTIME_ELEMENT(element);
8033 #if USE_STOP_CHANGED_ELEMENTS
8034 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
8036 int old_element = Feld[x][y];
8038 /* prevent changed element from moving in same engine frame
8039 unless both old and new element can either fall or move */
8040 if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
8041 (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
8046 CreateFieldExt(x, y, element, TRUE);
8049 static boolean ChangeElement(int x, int y, int element, int page)
8051 struct ElementChangeInfo *change = &element_info[element].change_page[page];
8053 int old_element = Feld[x][y];
8055 /* always use default change event to prevent running into a loop */
8056 if (ChangeEvent[x][y] == -1)
8057 ChangeEvent[x][y] = CE_DELAY;
8059 if (ChangeEvent[x][y] == CE_DELAY)
8061 /* reset actual trigger element, trigger player and action element */
8062 change->actual_trigger_element = EL_EMPTY;
8063 change->actual_trigger_player = EL_PLAYER_1;
8064 change->actual_trigger_side = CH_SIDE_NONE;
8065 change->actual_trigger_ce_value = 0;
8068 /* do not change elements more than a specified maximum number of changes */
8069 if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
8072 ChangeCount[x][y]++; /* count number of changes in the same frame */
8074 if (change->explode)
8081 if (change->use_target_content)
8083 boolean complete_replace = TRUE;
8084 boolean can_replace[3][3];
8087 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
8090 boolean is_walkable;
8091 boolean is_diggable;
8092 boolean is_collectible;
8093 boolean is_removable;
8094 boolean is_destructible;
8095 int ex = x + xx - 1;
8096 int ey = y + yy - 1;
8097 int content_element = change->target_content.e[xx][yy];
8100 can_replace[xx][yy] = TRUE;
8102 if (ex == x && ey == y) /* do not check changing element itself */
8105 if (content_element == EL_EMPTY_SPACE)
8107 can_replace[xx][yy] = FALSE; /* do not replace border with space */
8112 if (!IN_LEV_FIELD(ex, ey))
8114 can_replace[xx][yy] = FALSE;
8115 complete_replace = FALSE;
8122 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
8123 e = MovingOrBlocked2Element(ex, ey);
8125 is_empty = (IS_FREE(ex, ey) ||
8126 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
8128 is_walkable = (is_empty || IS_WALKABLE(e));
8129 is_diggable = (is_empty || IS_DIGGABLE(e));
8130 is_collectible = (is_empty || IS_COLLECTIBLE(e));
8131 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
8132 is_removable = (is_diggable || is_collectible);
8134 can_replace[xx][yy] =
8135 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
8136 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
8137 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
8138 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
8139 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
8140 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
8141 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
8143 if (!can_replace[xx][yy])
8144 complete_replace = FALSE;
8147 if (!change->only_if_complete || complete_replace)
8149 boolean something_has_changed = FALSE;
8151 if (change->only_if_complete && change->use_random_replace &&
8152 RND(100) < change->random_percentage)
8155 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
8157 int ex = x + xx - 1;
8158 int ey = y + yy - 1;
8159 int content_element;
8161 if (can_replace[xx][yy] && (!change->use_random_replace ||
8162 RND(100) < change->random_percentage))
8164 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
8165 RemoveMovingField(ex, ey);
8167 ChangeEvent[ex][ey] = ChangeEvent[x][y];
8169 content_element = change->target_content.e[xx][yy];
8170 target_element = GET_TARGET_ELEMENT(content_element, change);
8172 CreateElementFromChange(ex, ey, target_element);
8174 something_has_changed = TRUE;
8176 /* for symmetry reasons, freeze newly created border elements */
8177 if (ex != x || ey != y)
8178 Stop[ex][ey] = TRUE; /* no more moving in this frame */
8182 if (something_has_changed)
8184 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
8185 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
8191 target_element = GET_TARGET_ELEMENT(change->target_element, change);
8193 if (element == EL_DIAGONAL_GROWING ||
8194 element == EL_DIAGONAL_SHRINKING)
8196 target_element = Store[x][y];
8198 Store[x][y] = EL_EMPTY;
8201 CreateElementFromChange(x, y, target_element);
8203 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
8204 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
8207 /* this uses direct change before indirect change */
8208 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
8213 #if USE_NEW_DELAYED_ACTION
8215 static void HandleElementChange(int x, int y, int page)
8217 int element = MovingOrBlocked2Element(x, y);
8218 struct ElementInfo *ei = &element_info[element];
8219 struct ElementChangeInfo *change = &ei->change_page[page];
8222 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
8223 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
8226 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
8227 x, y, element, element_info[element].token_name);
8228 printf("HandleElementChange(): This should never happen!\n");
8233 /* this can happen with classic bombs on walkable, changing elements */
8234 if (!CAN_CHANGE_OR_HAS_ACTION(element))
8237 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
8238 ChangeDelay[x][y] = 0;
8244 if (ChangeDelay[x][y] == 0) /* initialize element change */
8246 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
8248 if (change->can_change)
8250 ResetGfxAnimation(x, y);
8251 ResetRandomAnimationValue(x, y);
8253 if (change->pre_change_function)
8254 change->pre_change_function(x, y);
8258 ChangeDelay[x][y]--;
8260 if (ChangeDelay[x][y] != 0) /* continue element change */
8262 if (change->can_change)
8264 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8266 if (IS_ANIMATED(graphic))
8267 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8269 if (change->change_function)
8270 change->change_function(x, y);
8273 else /* finish element change */
8275 if (ChangePage[x][y] != -1) /* remember page from delayed change */
8277 page = ChangePage[x][y];
8278 ChangePage[x][y] = -1;
8280 change = &ei->change_page[page];
8283 if (IS_MOVING(x, y)) /* never change a running system ;-) */
8285 ChangeDelay[x][y] = 1; /* try change after next move step */
8286 ChangePage[x][y] = page; /* remember page to use for change */
8291 if (change->can_change)
8293 if (ChangeElement(x, y, element, page))
8295 if (change->post_change_function)
8296 change->post_change_function(x, y);
8300 if (change->has_action)
8301 ExecuteCustomElementAction(x, y, element, page);
8307 static void HandleElementChange(int x, int y, int page)
8309 int element = MovingOrBlocked2Element(x, y);
8310 struct ElementInfo *ei = &element_info[element];
8311 struct ElementChangeInfo *change = &ei->change_page[page];
8314 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
8317 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
8318 x, y, element, element_info[element].token_name);
8319 printf("HandleElementChange(): This should never happen!\n");
8324 /* this can happen with classic bombs on walkable, changing elements */
8325 if (!CAN_CHANGE(element))
8328 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
8329 ChangeDelay[x][y] = 0;
8335 if (ChangeDelay[x][y] == 0) /* initialize element change */
8337 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
8339 ResetGfxAnimation(x, y);
8340 ResetRandomAnimationValue(x, y);
8342 if (change->pre_change_function)
8343 change->pre_change_function(x, y);
8346 ChangeDelay[x][y]--;
8348 if (ChangeDelay[x][y] != 0) /* continue element change */
8350 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8352 if (IS_ANIMATED(graphic))
8353 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8355 if (change->change_function)
8356 change->change_function(x, y);
8358 else /* finish element change */
8360 if (ChangePage[x][y] != -1) /* remember page from delayed change */
8362 page = ChangePage[x][y];
8363 ChangePage[x][y] = -1;
8365 change = &ei->change_page[page];
8368 if (IS_MOVING(x, y)) /* never change a running system ;-) */
8370 ChangeDelay[x][y] = 1; /* try change after next move step */
8371 ChangePage[x][y] = page; /* remember page to use for change */
8376 if (ChangeElement(x, y, element, page))
8378 if (change->post_change_function)
8379 change->post_change_function(x, y);
8386 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
8387 int trigger_element,
8393 boolean change_done_any = FALSE;
8394 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
8397 if (!(trigger_events[trigger_element][trigger_event]))
8400 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8402 int element = EL_CUSTOM_START + i;
8403 boolean change_done = FALSE;
8406 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
8407 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
8410 for (p = 0; p < element_info[element].num_change_pages; p++)
8412 struct ElementChangeInfo *change = &element_info[element].change_page[p];
8414 if (change->can_change_or_has_action &&
8415 change->has_event[trigger_event] &&
8416 change->trigger_side & trigger_side &&
8417 change->trigger_player & trigger_player &&
8418 change->trigger_page & trigger_page_bits &&
8419 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
8421 change->actual_trigger_element = trigger_element;
8422 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
8423 change->actual_trigger_side = trigger_side;
8424 change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
8426 if ((change->can_change && !change_done) || change->has_action)
8431 SCAN_PLAYFIELD(x, y)
8433 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8436 if (Feld[x][y] == element)
8438 if (change->can_change && !change_done)
8440 ChangeDelay[x][y] = 1;
8441 ChangeEvent[x][y] = trigger_event;
8443 HandleElementChange(x, y, p);
8445 #if USE_NEW_DELAYED_ACTION
8446 else if (change->has_action)
8448 ExecuteCustomElementAction(x, y, element, p);
8449 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8452 if (change->has_action)
8454 ExecuteCustomElementAction(x, y, element, p);
8455 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8461 if (change->can_change)
8464 change_done_any = TRUE;
8471 return change_done_any;
8474 static boolean CheckElementChangeExt(int x, int y,
8476 int trigger_element,
8481 boolean change_done = FALSE;
8484 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
8485 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
8488 if (Feld[x][y] == EL_BLOCKED)
8490 Blocked2Moving(x, y, &x, &y);
8491 element = Feld[x][y];
8495 /* check if element has already changed */
8496 if (Feld[x][y] != element)
8499 /* check if element has already changed or is about to change after moving */
8500 if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
8501 Feld[x][y] != element) ||
8503 (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
8504 (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
8505 ChangePage[x][y] != -1)))
8509 for (p = 0; p < element_info[element].num_change_pages; p++)
8511 struct ElementChangeInfo *change = &element_info[element].change_page[p];
8513 boolean check_trigger_element =
8514 (trigger_event == CE_TOUCHING_X ||
8515 trigger_event == CE_HITTING_X ||
8516 trigger_event == CE_HIT_BY_X);
8518 if (change->can_change_or_has_action &&
8519 change->has_event[trigger_event] &&
8520 change->trigger_side & trigger_side &&
8521 change->trigger_player & trigger_player &&
8522 (!check_trigger_element ||
8523 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
8525 change->actual_trigger_element = trigger_element;
8526 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
8527 change->actual_trigger_side = trigger_side;
8528 change->actual_trigger_ce_value = CustomValue[x][y];
8530 /* special case: trigger element not at (x,y) position for some events */
8531 if (check_trigger_element)
8543 { 0, 0 }, { 0, 0 }, { 0, 0 },
8547 int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
8548 int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
8550 change->actual_trigger_ce_value = CustomValue[xx][yy];
8553 if (change->can_change && !change_done)
8555 ChangeDelay[x][y] = 1;
8556 ChangeEvent[x][y] = trigger_event;
8558 HandleElementChange(x, y, p);
8562 #if USE_NEW_DELAYED_ACTION
8563 else if (change->has_action)
8565 ExecuteCustomElementAction(x, y, element, p);
8566 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8569 if (change->has_action)
8571 ExecuteCustomElementAction(x, y, element, p);
8572 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8581 static void PlayPlayerSound(struct PlayerInfo *player)
8583 int jx = player->jx, jy = player->jy;
8584 int sound_element = player->artwork_element;
8585 int last_action = player->last_action_waiting;
8586 int action = player->action_waiting;
8588 if (player->is_waiting)
8590 if (action != last_action)
8591 PlayLevelSoundElementAction(jx, jy, sound_element, action);
8593 PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
8597 if (action != last_action)
8598 StopSound(element_info[sound_element].sound[last_action]);
8600 if (last_action == ACTION_SLEEPING)
8601 PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
8605 static void PlayAllPlayersSound()
8609 for (i = 0; i < MAX_PLAYERS; i++)
8610 if (stored_player[i].active)
8611 PlayPlayerSound(&stored_player[i]);
8614 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
8616 boolean last_waiting = player->is_waiting;
8617 int move_dir = player->MovDir;
8619 player->dir_waiting = move_dir;
8620 player->last_action_waiting = player->action_waiting;
8624 if (!last_waiting) /* not waiting -> waiting */
8626 player->is_waiting = TRUE;
8628 player->frame_counter_bored =
8630 game.player_boring_delay_fixed +
8631 SimpleRND(game.player_boring_delay_random);
8632 player->frame_counter_sleeping =
8634 game.player_sleeping_delay_fixed +
8635 SimpleRND(game.player_sleeping_delay_random);
8638 InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
8640 InitPlayerGfxAnimation(player, ACTION_WAITING, player->MovDir);
8644 if (game.player_sleeping_delay_fixed +
8645 game.player_sleeping_delay_random > 0 &&
8646 player->anim_delay_counter == 0 &&
8647 player->post_delay_counter == 0 &&
8648 FrameCounter >= player->frame_counter_sleeping)
8649 player->is_sleeping = TRUE;
8650 else if (game.player_boring_delay_fixed +
8651 game.player_boring_delay_random > 0 &&
8652 FrameCounter >= player->frame_counter_bored)
8653 player->is_bored = TRUE;
8655 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
8656 player->is_bored ? ACTION_BORING :
8660 if (player->is_sleeping && player->use_murphy)
8662 /* special for Murphy: leaning against solid objects when sleeping */
8664 if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
8665 Feld[player->jx - 1][player->jy] != EL_EMPTY)
8667 else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
8668 Feld[player->jx + 1][player->jy] != EL_EMPTY)
8669 move_dir = MV_RIGHT;
8671 player->is_sleeping = FALSE;
8673 player->dir_waiting = move_dir;
8677 if (player->is_sleeping)
8679 if (player->num_special_action_sleeping > 0)
8681 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
8683 int last_special_action = player->special_action_sleeping;
8684 int num_special_action = player->num_special_action_sleeping;
8685 int special_action =
8686 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
8687 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
8688 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
8689 last_special_action + 1 : ACTION_SLEEPING);
8690 int special_graphic =
8691 el_act_dir2img(player->artwork_element, special_action, move_dir);
8693 player->anim_delay_counter =
8694 graphic_info[special_graphic].anim_delay_fixed +
8695 SimpleRND(graphic_info[special_graphic].anim_delay_random);
8696 player->post_delay_counter =
8697 graphic_info[special_graphic].post_delay_fixed +
8698 SimpleRND(graphic_info[special_graphic].post_delay_random);
8700 player->special_action_sleeping = special_action;
8703 if (player->anim_delay_counter > 0)
8705 player->action_waiting = player->special_action_sleeping;
8706 player->anim_delay_counter--;
8708 else if (player->post_delay_counter > 0)
8710 player->post_delay_counter--;
8714 else if (player->is_bored)
8716 if (player->num_special_action_bored > 0)
8718 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
8720 int special_action =
8721 ACTION_BORING_1 + SimpleRND(player->num_special_action_bored);
8722 int special_graphic =
8723 el_act_dir2img(player->artwork_element, special_action, move_dir);
8725 player->anim_delay_counter =
8726 graphic_info[special_graphic].anim_delay_fixed +
8727 SimpleRND(graphic_info[special_graphic].anim_delay_random);
8728 player->post_delay_counter =
8729 graphic_info[special_graphic].post_delay_fixed +
8730 SimpleRND(graphic_info[special_graphic].post_delay_random);
8732 player->special_action_bored = special_action;
8735 if (player->anim_delay_counter > 0)
8737 player->action_waiting = player->special_action_bored;
8738 player->anim_delay_counter--;
8740 else if (player->post_delay_counter > 0)
8742 player->post_delay_counter--;
8747 else if (last_waiting) /* waiting -> not waiting */
8749 player->is_waiting = FALSE;
8750 player->is_bored = FALSE;
8751 player->is_sleeping = FALSE;
8753 player->frame_counter_bored = -1;
8754 player->frame_counter_sleeping = -1;
8756 player->anim_delay_counter = 0;
8757 player->post_delay_counter = 0;
8759 player->dir_waiting = player->MovDir;
8760 player->action_waiting = ACTION_DEFAULT;
8762 player->special_action_bored = ACTION_DEFAULT;
8763 player->special_action_sleeping = ACTION_DEFAULT;
8767 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
8769 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
8770 int left = player_action & JOY_LEFT;
8771 int right = player_action & JOY_RIGHT;
8772 int up = player_action & JOY_UP;
8773 int down = player_action & JOY_DOWN;
8774 int button1 = player_action & JOY_BUTTON_1;
8775 int button2 = player_action & JOY_BUTTON_2;
8776 int dx = (left ? -1 : right ? 1 : 0);
8777 int dy = (up ? -1 : down ? 1 : 0);
8779 if (!player->active || tape.pausing)
8785 snapped = SnapField(player, dx, dy);
8789 dropped = DropElement(player);
8791 moved = MovePlayer(player, dx, dy);
8794 if (tape.single_step && tape.recording && !tape.pausing)
8796 if (button1 || (dropped && !moved))
8798 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8799 SnapField(player, 0, 0); /* stop snapping */
8803 SetPlayerWaiting(player, FALSE);
8805 return player_action;
8809 /* no actions for this player (no input at player's configured device) */
8811 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
8812 SnapField(player, 0, 0);
8813 CheckGravityMovementWhenNotMoving(player);
8815 if (player->MovPos == 0)
8816 SetPlayerWaiting(player, TRUE);
8818 if (player->MovPos == 0) /* needed for tape.playing */
8819 player->is_moving = FALSE;
8821 player->is_dropping = FALSE;
8822 player->is_dropping_pressed = FALSE;
8823 player->drop_pressed_delay = 0;
8829 void AdvanceFrameAndPlayerCounters(int player_nr)
8833 /* advance frame counters (global frame counter and time frame counter) */
8837 /* advance player counters (counters for move delay, move animation etc.) */
8838 for (i = 0; i < MAX_PLAYERS; i++)
8840 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
8841 int move_delay_value = stored_player[i].move_delay_value;
8842 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
8844 if (!advance_player_counters) /* not all players may be affected */
8847 #if USE_NEW_PLAYER_ANIM
8848 if (move_frames == 0) /* less than one move per game frame */
8850 int stepsize = TILEX / move_delay_value;
8851 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
8852 int count = (stored_player[i].is_moving ?
8853 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
8855 if (count % delay == 0)
8860 stored_player[i].Frame += move_frames;
8862 if (stored_player[i].MovPos != 0)
8863 stored_player[i].StepFrame += move_frames;
8865 if (stored_player[i].move_delay > 0)
8866 stored_player[i].move_delay--;
8868 /* due to bugs in previous versions, counter must count up, not down */
8869 if (stored_player[i].push_delay != -1)
8870 stored_player[i].push_delay++;
8872 if (stored_player[i].drop_delay > 0)
8873 stored_player[i].drop_delay--;
8875 if (stored_player[i].is_dropping_pressed)
8876 stored_player[i].drop_pressed_delay++;
8882 static unsigned long game_frame_delay = 0;
8883 unsigned long game_frame_delay_value;
8884 int magic_wall_x = 0, magic_wall_y = 0;
8885 int i, x, y, element, graphic;
8886 byte *recorded_player_action;
8887 byte summarized_player_action = 0;
8888 byte tape_action[MAX_PLAYERS];
8890 if (game_status != GAME_MODE_PLAYING)
8893 game_frame_delay_value =
8894 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
8896 if (tape.playing && tape.warp_forward && !tape.pausing)
8897 game_frame_delay_value = 0;
8899 /* ---------- main game synchronization point ---------- */
8901 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
8903 InitPlayfieldScanModeVars();
8905 if (ScreenMovPos == 0) /* screen currently aligned at tile position */
8907 struct PlayerInfo *player;
8908 int player_nr = game.centered_player_nr_next;
8910 if (game.centered_player_nr_next == -1)
8911 player_nr = local_player->index_nr;
8913 player = &stored_player[player_nr];
8915 if (!player->active)
8916 game.centered_player_nr_next = game.centered_player_nr;
8918 if (game.centered_player_nr != game.centered_player_nr_next)
8920 DrawRelocatePlayer(player, setup.quick_switch);
8922 game.centered_player_nr = game.centered_player_nr_next;
8926 #if USE_ONE_MORE_CHANGE_PER_FRAME
8927 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
8929 SCAN_PLAYFIELD(x, y)
8931 ChangeCount[x][y] = 0;
8932 ChangeEvent[x][y] = -1;
8937 if (network_playing && !network_player_action_received)
8939 /* try to get network player actions in time */
8941 #if defined(NETWORK_AVALIABLE)
8942 /* last chance to get network player actions without main loop delay */
8946 /* game was quit by network peer */
8947 if (game_status != GAME_MODE_PLAYING)
8950 if (!network_player_action_received)
8951 return; /* failed to get network player actions in time */
8957 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
8960 /* !!! CHECK THIS (tape.pausing is always FALSE here!) !!! */
8961 if (recorded_player_action == NULL && tape.pausing)
8965 for (i = 0; i < MAX_PLAYERS; i++)
8967 summarized_player_action |= stored_player[i].action;
8969 if (!network_playing)
8970 stored_player[i].effective_action = stored_player[i].action;
8973 #if defined(NETWORK_AVALIABLE)
8974 if (network_playing)
8975 SendToServer_MovePlayer(summarized_player_action);
8978 if (!options.network && !setup.team_mode)
8979 local_player->effective_action = summarized_player_action;
8981 if (recorded_player_action != NULL)
8982 for (i = 0; i < MAX_PLAYERS; i++)
8983 stored_player[i].effective_action = recorded_player_action[i];
8985 for (i = 0; i < MAX_PLAYERS; i++)
8987 tape_action[i] = stored_player[i].effective_action;
8989 if (tape.recording && tape_action[i] && !tape.player_participates[i])
8990 tape.player_participates[i] = TRUE; /* player just appeared from CE */
8993 /* only save actions from input devices, but not programmed actions */
8995 TapeRecordAction(tape_action);
8997 for (i = 0; i < MAX_PLAYERS; i++)
8999 int actual_player_action = stored_player[i].effective_action;
9002 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
9003 - rnd_equinox_tetrachloride 048
9004 - rnd_equinox_tetrachloride_ii 096
9005 - rnd_emanuel_schmieg 002
9006 - doctor_sloan_ww 001, 020
9008 if (stored_player[i].MovPos == 0)
9009 CheckGravityMovement(&stored_player[i]);
9012 /* overwrite programmed action with tape action */
9013 if (stored_player[i].programmed_action)
9014 actual_player_action = stored_player[i].programmed_action;
9017 PlayerActions(&stored_player[i], actual_player_action);
9019 tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
9021 if (tape.recording && tape_action[i] && !tape.player_participates[i])
9022 tape.player_participates[i] = TRUE; /* player just appeared from CE */
9025 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
9028 network_player_action_received = FALSE;
9030 ScrollScreen(NULL, SCROLL_GO_ON);
9032 /* for backwards compatibility, the following code emulates a fixed bug that
9033 occured when pushing elements (causing elements that just made their last
9034 pushing step to already (if possible) make their first falling step in the
9035 same game frame, which is bad); this code is also needed to use the famous
9036 "spring push bug" which is used in older levels and might be wanted to be
9037 used also in newer levels, but in this case the buggy pushing code is only
9038 affecting the "spring" element and no other elements */
9040 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
9042 for (i = 0; i < MAX_PLAYERS; i++)
9044 struct PlayerInfo *player = &stored_player[i];
9048 if (player->active && player->is_pushing && player->is_moving &&
9050 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
9051 Feld[x][y] == EL_SPRING))
9053 ContinueMoving(x, y);
9055 /* continue moving after pushing (this is actually a bug) */
9056 if (!IS_MOVING(x, y))
9065 SCAN_PLAYFIELD(x, y)
9067 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9070 ChangeCount[x][y] = 0;
9071 ChangeEvent[x][y] = -1;
9073 /* this must be handled before main playfield loop */
9074 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
9077 if (MovDelay[x][y] <= 0)
9081 #if USE_NEW_SNAP_DELAY
9082 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
9085 if (MovDelay[x][y] <= 0)
9088 DrawLevelField(x, y);
9090 TestIfElementTouchesCustomElement(x, y); /* for empty space */
9096 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
9098 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
9099 printf("GameActions(): This should never happen!\n");
9101 ChangePage[x][y] = -1;
9106 if (WasJustMoving[x][y] > 0)
9107 WasJustMoving[x][y]--;
9108 if (WasJustFalling[x][y] > 0)
9109 WasJustFalling[x][y]--;
9110 if (CheckCollision[x][y] > 0)
9111 CheckCollision[x][y]--;
9115 /* reset finished pushing action (not done in ContinueMoving() to allow
9116 continuous pushing animation for elements with zero push delay) */
9117 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
9119 ResetGfxAnimation(x, y);
9120 DrawLevelField(x, y);
9124 if (IS_BLOCKED(x, y))
9128 Blocked2Moving(x, y, &oldx, &oldy);
9129 if (!IS_MOVING(oldx, oldy))
9131 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
9132 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
9133 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
9134 printf("GameActions(): This should never happen!\n");
9141 SCAN_PLAYFIELD(x, y)
9143 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9146 element = Feld[x][y];
9147 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9150 printf("::: %d,%d\n", x, y);
9152 if (element == EL_ROCK)
9153 printf("::: Yo man! Rocks can fall!\n");
9156 if (graphic_info[graphic].anim_global_sync)
9157 GfxFrame[x][y] = FrameCounter;
9158 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
9160 int old_gfx_frame = GfxFrame[x][y];
9162 GfxFrame[x][y] = CustomValue[x][y];
9165 if (GfxFrame[x][y] != old_gfx_frame)
9167 DrawLevelGraphicAnimation(x, y, graphic);
9169 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
9171 int old_gfx_frame = GfxFrame[x][y];
9173 GfxFrame[x][y] = element_info[element].collect_score;
9176 if (GfxFrame[x][y] != old_gfx_frame)
9178 DrawLevelGraphicAnimation(x, y, graphic);
9181 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
9182 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
9183 ResetRandomAnimationValue(x, y);
9185 SetRandomAnimationValue(x, y);
9187 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
9189 if (IS_INACTIVE(element))
9191 if (IS_ANIMATED(graphic))
9192 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9197 /* this may take place after moving, so 'element' may have changed */
9198 if (IS_CHANGING(x, y) &&
9199 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
9201 int page = element_info[element].event_page_nr[CE_DELAY];
9203 HandleElementChange(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] : page);
9207 printf("::: ChangeDelay == %d\n", ChangeDelay[x][y]);
9211 if (element == EL_CUSTOM_255)
9212 printf("::: ChangeDelay == %d\n", ChangeDelay[x][y]);
9216 HandleElementChange(x, y, page);
9218 if (CAN_CHANGE(element))
9219 HandleElementChange(x, y, page);
9221 if (HAS_ACTION(element))
9222 ExecuteCustomElementAction(x, y, element, page);
9227 element = Feld[x][y];
9228 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9231 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
9235 element = Feld[x][y];
9236 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9238 if (IS_ANIMATED(graphic) &&
9241 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9243 if (IS_GEM(element) || element == EL_SP_INFOTRON)
9244 EdelsteinFunkeln(x, y);
9246 else if ((element == EL_ACID ||
9247 element == EL_EXIT_OPEN ||
9248 element == EL_SP_EXIT_OPEN ||
9249 element == EL_SP_TERMINAL ||
9250 element == EL_SP_TERMINAL_ACTIVE ||
9251 element == EL_EXTRA_TIME ||
9252 element == EL_SHIELD_NORMAL ||
9253 element == EL_SHIELD_DEADLY) &&
9254 IS_ANIMATED(graphic))
9255 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9256 else if (IS_MOVING(x, y))
9257 ContinueMoving(x, y);
9258 else if (IS_ACTIVE_BOMB(element))
9259 CheckDynamite(x, y);
9260 else if (element == EL_AMOEBA_GROWING)
9261 AmoebeWaechst(x, y);
9262 else if (element == EL_AMOEBA_SHRINKING)
9263 AmoebaDisappearing(x, y);
9265 #if !USE_NEW_AMOEBA_CODE
9266 else if (IS_AMOEBALIVE(element))
9267 AmoebeAbleger(x, y);
9270 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
9272 else if (element == EL_EXIT_CLOSED)
9274 else if (element == EL_SP_EXIT_CLOSED)
9276 else if (element == EL_EXPANDABLE_WALL_GROWING)
9278 else if (element == EL_EXPANDABLE_WALL ||
9279 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9280 element == EL_EXPANDABLE_WALL_VERTICAL ||
9281 element == EL_EXPANDABLE_WALL_ANY)
9283 else if (element == EL_FLAMES)
9284 CheckForDragon(x, y);
9285 else if (element == EL_EXPLOSION)
9286 ; /* drawing of correct explosion animation is handled separately */
9287 else if (element == EL_ELEMENT_SNAPPING ||
9288 element == EL_DIAGONAL_SHRINKING ||
9289 element == EL_DIAGONAL_GROWING)
9292 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
9294 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9297 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
9298 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9301 if (element == EL_CUSTOM_255 ||
9302 element == EL_CUSTOM_256)
9303 DrawLevelGraphicAnimation(x, y, graphic);
9306 if (IS_BELT_ACTIVE(element))
9307 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
9309 if (game.magic_wall_active)
9311 int jx = local_player->jx, jy = local_player->jy;
9313 /* play the element sound at the position nearest to the player */
9314 if ((element == EL_MAGIC_WALL_FULL ||
9315 element == EL_MAGIC_WALL_ACTIVE ||
9316 element == EL_MAGIC_WALL_EMPTYING ||
9317 element == EL_BD_MAGIC_WALL_FULL ||
9318 element == EL_BD_MAGIC_WALL_ACTIVE ||
9319 element == EL_BD_MAGIC_WALL_EMPTYING) &&
9320 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
9328 #if USE_NEW_AMOEBA_CODE
9329 /* new experimental amoeba growth stuff */
9330 if (!(FrameCounter % 8))
9332 static unsigned long random = 1684108901;
9334 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
9336 x = RND(lev_fieldx);
9337 y = RND(lev_fieldy);
9338 element = Feld[x][y];
9340 if (!IS_PLAYER(x,y) &&
9341 (element == EL_EMPTY ||
9342 CAN_GROW_INTO(element) ||
9343 element == EL_QUICKSAND_EMPTY ||
9344 element == EL_ACID_SPLASH_LEFT ||
9345 element == EL_ACID_SPLASH_RIGHT))
9347 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
9348 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
9349 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
9350 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
9351 Feld[x][y] = EL_AMOEBA_DROP;
9354 random = random * 129 + 1;
9360 if (game.explosions_delayed)
9363 game.explosions_delayed = FALSE;
9366 SCAN_PLAYFIELD(x, y)
9368 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9371 element = Feld[x][y];
9373 if (ExplodeField[x][y])
9374 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
9375 else if (element == EL_EXPLOSION)
9376 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
9378 ExplodeField[x][y] = EX_TYPE_NONE;
9381 game.explosions_delayed = TRUE;
9384 if (game.magic_wall_active)
9386 if (!(game.magic_wall_time_left % 4))
9388 int element = Feld[magic_wall_x][magic_wall_y];
9390 if (element == EL_BD_MAGIC_WALL_FULL ||
9391 element == EL_BD_MAGIC_WALL_ACTIVE ||
9392 element == EL_BD_MAGIC_WALL_EMPTYING)
9393 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
9395 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
9398 if (game.magic_wall_time_left > 0)
9400 game.magic_wall_time_left--;
9401 if (!game.magic_wall_time_left)
9404 SCAN_PLAYFIELD(x, y)
9406 for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9409 element = Feld[x][y];
9411 if (element == EL_MAGIC_WALL_ACTIVE ||
9412 element == EL_MAGIC_WALL_FULL)
9414 Feld[x][y] = EL_MAGIC_WALL_DEAD;
9415 DrawLevelField(x, y);
9417 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
9418 element == EL_BD_MAGIC_WALL_FULL)
9420 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
9421 DrawLevelField(x, y);
9425 game.magic_wall_active = FALSE;
9430 if (game.light_time_left > 0)
9432 game.light_time_left--;
9434 if (game.light_time_left == 0)
9435 RedrawAllLightSwitchesAndInvisibleElements();
9438 if (game.timegate_time_left > 0)
9440 game.timegate_time_left--;
9442 if (game.timegate_time_left == 0)
9443 CloseAllOpenTimegates();
9446 if (game.lenses_time_left > 0)
9448 game.lenses_time_left--;
9450 if (game.lenses_time_left == 0)
9451 RedrawAllInvisibleElementsForLenses();
9454 if (game.magnify_time_left > 0)
9456 game.magnify_time_left--;
9458 if (game.magnify_time_left == 0)
9459 RedrawAllInvisibleElementsForMagnifier();
9462 for (i = 0; i < MAX_PLAYERS; i++)
9464 struct PlayerInfo *player = &stored_player[i];
9466 if (SHIELD_ON(player))
9468 if (player->shield_deadly_time_left)
9469 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
9470 else if (player->shield_normal_time_left)
9471 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
9475 if (TimeFrames >= FRAMES_PER_SECOND)
9480 for (i = 0; i < MAX_PLAYERS; i++)
9482 struct PlayerInfo *player = &stored_player[i];
9484 if (SHIELD_ON(player))
9486 player->shield_normal_time_left--;
9488 if (player->shield_deadly_time_left > 0)
9489 player->shield_deadly_time_left--;
9493 if (!level.use_step_counter)
9501 if (TimeLeft <= 10 && setup.time_limit)
9502 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
9504 DrawGameValue_Time(TimeLeft);
9506 if (!TimeLeft && setup.time_limit)
9507 for (i = 0; i < MAX_PLAYERS; i++)
9508 KillPlayer(&stored_player[i]);
9510 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
9511 DrawGameValue_Time(TimePlayed);
9514 if (tape.recording || tape.playing)
9515 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
9519 PlayAllPlayersSound();
9521 if (options.debug) /* calculate frames per second */
9523 static unsigned long fps_counter = 0;
9524 static int fps_frames = 0;
9525 unsigned long fps_delay_ms = Counter() - fps_counter;
9529 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
9531 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
9534 fps_counter = Counter();
9537 redraw_mask |= REDRAW_FPS;
9540 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
9542 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
9544 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
9546 local_player->show_envelope = 0;
9549 /* use random number generator in every frame to make it less predictable */
9550 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
9554 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
9556 int min_x = x, min_y = y, max_x = x, max_y = y;
9559 for (i = 0; i < MAX_PLAYERS; i++)
9561 int jx = stored_player[i].jx, jy = stored_player[i].jy;
9563 if (!stored_player[i].active || &stored_player[i] == player)
9566 min_x = MIN(min_x, jx);
9567 min_y = MIN(min_y, jy);
9568 max_x = MAX(max_x, jx);
9569 max_y = MAX(max_y, jy);
9572 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
9575 static boolean AllPlayersInVisibleScreen()
9579 for (i = 0; i < MAX_PLAYERS; i++)
9581 int jx = stored_player[i].jx, jy = stored_player[i].jy;
9583 if (!stored_player[i].active)
9586 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
9593 void ScrollLevel(int dx, int dy)
9595 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
9598 BlitBitmap(drawto_field, drawto_field,
9599 FX + TILEX * (dx == -1) - softscroll_offset,
9600 FY + TILEY * (dy == -1) - softscroll_offset,
9601 SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
9602 SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
9603 FX + TILEX * (dx == 1) - softscroll_offset,
9604 FY + TILEY * (dy == 1) - softscroll_offset);
9608 x = (dx == 1 ? BX1 : BX2);
9609 for (y = BY1; y <= BY2; y++)
9610 DrawScreenField(x, y);
9615 y = (dy == 1 ? BY1 : BY2);
9616 for (x = BX1; x <= BX2; x++)
9617 DrawScreenField(x, y);
9620 redraw_mask |= REDRAW_FIELD;
9623 static boolean canFallDown(struct PlayerInfo *player)
9625 int jx = player->jx, jy = player->jy;
9627 return (IN_LEV_FIELD(jx, jy + 1) &&
9628 (IS_FREE(jx, jy + 1) ||
9629 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
9630 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
9631 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
9634 static boolean canPassField(int x, int y, int move_dir)
9636 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9637 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9638 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
9641 int element = Feld[x][y];
9643 return (IS_PASSABLE_FROM(element, opposite_dir) &&
9644 !CAN_MOVE(element) &&
9645 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
9646 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
9647 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
9650 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
9652 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9653 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9654 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
9658 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9659 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
9660 (IS_DIGGABLE(Feld[newx][newy]) ||
9661 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9662 canPassField(newx, newy, move_dir)));
9665 static void CheckGravityMovement(struct PlayerInfo *player)
9667 if (game.gravity && !player->programmed_action)
9669 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
9670 int move_dir_vertical = player->effective_action & MV_VERTICAL;
9671 boolean player_is_snapping = player->effective_action & JOY_BUTTON_1;
9672 int jx = player->jx, jy = player->jy;
9673 boolean player_is_moving_to_valid_field =
9674 (!player_is_snapping &&
9675 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
9676 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
9677 boolean player_can_fall_down = canFallDown(player);
9679 if (player_can_fall_down &&
9680 !player_is_moving_to_valid_field)
9681 player->programmed_action = MV_DOWN;
9685 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
9687 return CheckGravityMovement(player);
9689 if (game.gravity && !player->programmed_action)
9691 int jx = player->jx, jy = player->jy;
9692 boolean field_under_player_is_free =
9693 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
9694 boolean player_is_standing_on_valid_field =
9695 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
9696 (IS_WALKABLE(Feld[jx][jy]) &&
9697 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
9699 if (field_under_player_is_free && !player_is_standing_on_valid_field)
9700 player->programmed_action = MV_DOWN;
9706 -----------------------------------------------------------------------------
9707 dx, dy: direction (non-diagonal) to try to move the player to
9708 real_dx, real_dy: direction as read from input device (can be diagonal)
9711 boolean MovePlayerOneStep(struct PlayerInfo *player,
9712 int dx, int dy, int real_dx, int real_dy)
9714 int jx = player->jx, jy = player->jy;
9715 int new_jx = jx + dx, new_jy = jy + dy;
9716 #if !USE_FIXED_DONT_RUN_INTO
9720 boolean player_can_move = !player->cannot_move;
9722 if (!player->active || (!dx && !dy))
9723 return MP_NO_ACTION;
9725 player->MovDir = (dx < 0 ? MV_LEFT :
9728 dy > 0 ? MV_DOWN : MV_NONE);
9730 if (!IN_LEV_FIELD(new_jx, new_jy))
9731 return MP_NO_ACTION;
9733 if (!player_can_move)
9736 if (player->MovPos == 0)
9738 player->is_moving = FALSE;
9739 player->is_digging = FALSE;
9740 player->is_collecting = FALSE;
9741 player->is_snapping = FALSE;
9742 player->is_pushing = FALSE;
9745 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
9746 SnapField(player, 0, 0);
9750 return MP_NO_ACTION;
9755 if (!options.network && game.centered_player_nr == -1 &&
9756 !AllPlayersInSight(player, new_jx, new_jy))
9757 return MP_NO_ACTION;
9759 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
9760 return MP_NO_ACTION;
9763 #if !USE_FIXED_DONT_RUN_INTO
9764 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
9766 /* (moved to DigField()) */
9767 if (player_can_move && DONT_RUN_INTO(element))
9769 if (element == EL_ACID && dx == 0 && dy == 1)
9771 SplashAcid(new_jx, new_jy);
9772 Feld[jx][jy] = EL_PLAYER_1;
9773 InitMovingField(jx, jy, MV_DOWN);
9774 Store[jx][jy] = EL_ACID;
9775 ContinueMoving(jx, jy);
9779 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
9785 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
9787 #if USE_FIXED_DONT_RUN_INTO
9788 if (can_move == MP_DONT_RUN_INTO)
9792 if (can_move != MP_MOVING)
9795 #if USE_FIXED_DONT_RUN_INTO
9798 /* check if DigField() has caused relocation of the player */
9799 if (player->jx != jx || player->jy != jy)
9800 return MP_NO_ACTION; /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
9802 StorePlayer[jx][jy] = 0;
9803 player->last_jx = jx;
9804 player->last_jy = jy;
9805 player->jx = new_jx;
9806 player->jy = new_jy;
9807 StorePlayer[new_jx][new_jy] = player->element_nr;
9809 if (player->move_delay_value_next != -1)
9811 player->move_delay_value = player->move_delay_value_next;
9812 player->move_delay_value_next = -1;
9816 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
9818 player->step_counter++;
9820 PlayerVisit[jx][jy] = FrameCounter;
9822 ScrollPlayer(player, SCROLL_INIT);
9827 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
9829 int jx = player->jx, jy = player->jy;
9830 int old_jx = jx, old_jy = jy;
9831 int moved = MP_NO_ACTION;
9833 if (!player->active)
9838 if (player->MovPos == 0)
9840 player->is_moving = FALSE;
9841 player->is_digging = FALSE;
9842 player->is_collecting = FALSE;
9843 player->is_snapping = FALSE;
9844 player->is_pushing = FALSE;
9850 if (player->move_delay > 0)
9853 player->move_delay = -1; /* set to "uninitialized" value */
9855 /* store if player is automatically moved to next field */
9856 player->is_auto_moving = (player->programmed_action != MV_NONE);
9858 /* remove the last programmed player action */
9859 player->programmed_action = 0;
9863 /* should only happen if pre-1.2 tape recordings are played */
9864 /* this is only for backward compatibility */
9866 int original_move_delay_value = player->move_delay_value;
9869 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
9873 /* scroll remaining steps with finest movement resolution */
9874 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
9876 while (player->MovPos)
9878 ScrollPlayer(player, SCROLL_GO_ON);
9879 ScrollScreen(NULL, SCROLL_GO_ON);
9881 AdvanceFrameAndPlayerCounters(player->index_nr);
9887 player->move_delay_value = original_move_delay_value;
9890 if (player->last_move_dir & MV_HORIZONTAL)
9892 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
9893 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
9897 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
9898 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
9905 if (moved & MP_MOVING && !ScreenMovPos &&
9906 (player->index_nr == game.centered_player_nr ||
9907 game.centered_player_nr == -1))
9909 if (moved & MP_MOVING && !ScreenMovPos &&
9910 (player == local_player || !options.network))
9913 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
9914 int offset = (setup.scroll_delay ? 3 : 0);
9916 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
9918 /* actual player has left the screen -- scroll in that direction */
9919 if (jx != old_jx) /* player has moved horizontally */
9920 scroll_x += (jx - old_jx);
9921 else /* player has moved vertically */
9922 scroll_y += (jy - old_jy);
9926 if (jx != old_jx) /* player has moved horizontally */
9928 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
9929 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
9930 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
9932 /* don't scroll over playfield boundaries */
9933 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
9934 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
9936 /* don't scroll more than one field at a time */
9937 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
9939 /* don't scroll against the player's moving direction */
9940 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
9941 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
9942 scroll_x = old_scroll_x;
9944 else /* player has moved vertically */
9946 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
9947 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
9948 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
9950 /* don't scroll over playfield boundaries */
9951 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
9952 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
9954 /* don't scroll more than one field at a time */
9955 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
9957 /* don't scroll against the player's moving direction */
9958 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
9959 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
9960 scroll_y = old_scroll_y;
9964 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
9967 if (!options.network && game.centered_player_nr == -1 &&
9968 !AllPlayersInVisibleScreen())
9970 scroll_x = old_scroll_x;
9971 scroll_y = old_scroll_y;
9975 if (!options.network && !AllPlayersInVisibleScreen())
9977 scroll_x = old_scroll_x;
9978 scroll_y = old_scroll_y;
9983 ScrollScreen(player, SCROLL_INIT);
9984 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
9989 player->StepFrame = 0;
9991 if (moved & MP_MOVING)
9993 if (old_jx != jx && old_jy == jy)
9994 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
9995 else if (old_jx == jx && old_jy != jy)
9996 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
9998 DrawLevelField(jx, jy); /* for "crumbled sand" */
10000 player->last_move_dir = player->MovDir;
10001 player->is_moving = TRUE;
10002 player->is_snapping = FALSE;
10003 player->is_switching = FALSE;
10004 player->is_dropping = FALSE;
10005 player->is_dropping_pressed = FALSE;
10006 player->drop_pressed_delay = 0;
10010 CheckGravityMovementWhenNotMoving(player);
10012 player->is_moving = FALSE;
10014 /* at this point, the player is allowed to move, but cannot move right now
10015 (e.g. because of something blocking the way) -- ensure that the player
10016 is also allowed to move in the next frame (in old versions before 3.1.1,
10017 the player was forced to wait again for eight frames before next try) */
10019 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
10020 player->move_delay = 0; /* allow direct movement in the next frame */
10023 if (player->move_delay == -1) /* not yet initialized by DigField() */
10024 player->move_delay = player->move_delay_value;
10026 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10028 TestIfPlayerTouchesBadThing(jx, jy);
10029 TestIfPlayerTouchesCustomElement(jx, jy);
10032 if (!player->active)
10033 RemovePlayer(player);
10038 void ScrollPlayer(struct PlayerInfo *player, int mode)
10040 int jx = player->jx, jy = player->jy;
10041 int last_jx = player->last_jx, last_jy = player->last_jy;
10042 int move_stepsize = TILEX / player->move_delay_value;
10044 #if USE_NEW_PLAYER_SPEED
10045 if (!player->active)
10048 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
10051 if (!player->active || player->MovPos == 0)
10055 if (mode == SCROLL_INIT)
10057 player->actual_frame_counter = FrameCounter;
10058 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10060 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
10061 Feld[last_jx][last_jy] == EL_EMPTY)
10063 int last_field_block_delay = 0; /* start with no blocking at all */
10064 int block_delay_adjustment = player->block_delay_adjustment;
10066 /* if player blocks last field, add delay for exactly one move */
10067 if (player->block_last_field)
10069 last_field_block_delay += player->move_delay_value;
10071 /* when blocking enabled, prevent moving up despite gravity */
10072 if (game.gravity && player->MovDir == MV_UP)
10073 block_delay_adjustment = -1;
10076 /* add block delay adjustment (also possible when not blocking) */
10077 last_field_block_delay += block_delay_adjustment;
10079 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
10080 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
10083 #if USE_NEW_PLAYER_SPEED
10084 if (player->MovPos != 0) /* player has not yet reached destination */
10090 else if (!FrameReached(&player->actual_frame_counter, 1))
10094 printf("::: player->MovPos: %d -> %d\n",
10096 player->MovPos + (player->MovPos > 0 ? -1 : 1) * move_stepsize);
10099 #if USE_NEW_PLAYER_SPEED
10100 if (player->MovPos != 0)
10102 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
10103 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10105 /* before DrawPlayer() to draw correct player graphic for this case */
10106 if (player->MovPos == 0)
10107 CheckGravityMovement(player);
10110 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
10111 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10113 /* before DrawPlayer() to draw correct player graphic for this case */
10114 if (player->MovPos == 0)
10115 CheckGravityMovement(player);
10118 if (player->MovPos == 0) /* player reached destination field */
10121 printf("::: player reached destination field\n");
10124 if (player->move_delay_reset_counter > 0)
10126 player->move_delay_reset_counter--;
10128 if (player->move_delay_reset_counter == 0)
10130 /* continue with normal speed after quickly moving through gate */
10131 HALVE_PLAYER_SPEED(player);
10133 /* be able to make the next move without delay */
10134 player->move_delay = 0;
10138 player->last_jx = jx;
10139 player->last_jy = jy;
10141 if (Feld[jx][jy] == EL_EXIT_OPEN ||
10142 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
10143 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
10145 DrawPlayer(player); /* needed here only to cleanup last field */
10146 RemovePlayer(player);
10148 if (local_player->friends_still_needed == 0 ||
10149 IS_SP_ELEMENT(Feld[jx][jy]))
10150 player->LevelSolved = player->GameOver = TRUE;
10153 /* this breaks one level: "machine", level 000 */
10155 int move_direction = player->MovDir;
10156 int enter_side = MV_DIR_OPPOSITE(move_direction);
10157 int leave_side = move_direction;
10158 int old_jx = last_jx;
10159 int old_jy = last_jy;
10160 int old_element = Feld[old_jx][old_jy];
10161 int new_element = Feld[jx][jy];
10163 if (IS_CUSTOM_ELEMENT(old_element))
10164 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
10166 player->index_bit, leave_side);
10168 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
10169 CE_PLAYER_LEAVES_X,
10170 player->index_bit, leave_side);
10172 if (IS_CUSTOM_ELEMENT(new_element))
10173 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
10174 player->index_bit, enter_side);
10176 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
10177 CE_PLAYER_ENTERS_X,
10178 player->index_bit, enter_side);
10180 CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
10181 CE_MOVE_OF_X, move_direction);
10184 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10186 TestIfPlayerTouchesBadThing(jx, jy);
10187 TestIfPlayerTouchesCustomElement(jx, jy);
10189 /* needed because pushed element has not yet reached its destination,
10190 so it would trigger a change event at its previous field location */
10191 if (!player->is_pushing)
10192 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
10194 if (!player->active)
10195 RemovePlayer(player);
10198 if (level.use_step_counter)
10208 if (TimeLeft <= 10 && setup.time_limit)
10209 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
10211 DrawGameValue_Time(TimeLeft);
10213 if (!TimeLeft && setup.time_limit)
10214 for (i = 0; i < MAX_PLAYERS; i++)
10215 KillPlayer(&stored_player[i]);
10217 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
10218 DrawGameValue_Time(TimePlayed);
10221 if (tape.single_step && tape.recording && !tape.pausing &&
10222 !player->programmed_action)
10223 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10227 void ScrollScreen(struct PlayerInfo *player, int mode)
10229 static unsigned long screen_frame_counter = 0;
10231 if (mode == SCROLL_INIT)
10233 /* set scrolling step size according to actual player's moving speed */
10234 ScrollStepSize = TILEX / player->move_delay_value;
10236 screen_frame_counter = FrameCounter;
10237 ScreenMovDir = player->MovDir;
10238 ScreenMovPos = player->MovPos;
10239 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
10242 else if (!FrameReached(&screen_frame_counter, 1))
10247 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
10248 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
10249 redraw_mask |= REDRAW_FIELD;
10252 ScreenMovDir = MV_NONE;
10255 void TestIfPlayerTouchesCustomElement(int x, int y)
10257 static int xy[4][2] =
10264 static int trigger_sides[4][2] =
10266 /* center side border side */
10267 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
10268 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
10269 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
10270 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
10272 static int touch_dir[4] =
10274 MV_LEFT | MV_RIGHT,
10279 int center_element = Feld[x][y]; /* should always be non-moving! */
10282 for (i = 0; i < NUM_DIRECTIONS; i++)
10284 int xx = x + xy[i][0];
10285 int yy = y + xy[i][1];
10286 int center_side = trigger_sides[i][0];
10287 int border_side = trigger_sides[i][1];
10288 int border_element;
10290 if (!IN_LEV_FIELD(xx, yy))
10293 if (IS_PLAYER(x, y))
10295 struct PlayerInfo *player = PLAYERINFO(x, y);
10297 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10298 border_element = Feld[xx][yy]; /* may be moving! */
10299 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
10300 border_element = Feld[xx][yy];
10301 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
10302 border_element = MovingOrBlocked2Element(xx, yy);
10304 continue; /* center and border element do not touch */
10306 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
10307 player->index_bit, border_side);
10308 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
10309 CE_PLAYER_TOUCHES_X,
10310 player->index_bit, border_side);
10312 else if (IS_PLAYER(xx, yy))
10314 struct PlayerInfo *player = PLAYERINFO(xx, yy);
10316 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10318 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
10319 continue; /* center and border element do not touch */
10322 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
10323 player->index_bit, center_side);
10324 CheckTriggeredElementChangeByPlayer(x, y, center_element,
10325 CE_PLAYER_TOUCHES_X,
10326 player->index_bit, center_side);
10332 #if USE_ELEMENT_TOUCHING_BUGFIX
10334 void TestIfElementTouchesCustomElement(int x, int y)
10336 static int xy[4][2] =
10343 static int trigger_sides[4][2] =
10345 /* center side border side */
10346 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
10347 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
10348 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
10349 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
10351 static int touch_dir[4] =
10353 MV_LEFT | MV_RIGHT,
10358 boolean change_center_element = FALSE;
10359 int center_element = Feld[x][y]; /* should always be non-moving! */
10360 int border_element_old[NUM_DIRECTIONS];
10363 for (i = 0; i < NUM_DIRECTIONS; i++)
10365 int xx = x + xy[i][0];
10366 int yy = y + xy[i][1];
10367 int border_element;
10369 border_element_old[i] = -1;
10371 if (!IN_LEV_FIELD(xx, yy))
10374 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10375 border_element = Feld[xx][yy]; /* may be moving! */
10376 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
10377 border_element = Feld[xx][yy];
10378 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
10379 border_element = MovingOrBlocked2Element(xx, yy);
10381 continue; /* center and border element do not touch */
10383 border_element_old[i] = border_element;
10386 for (i = 0; i < NUM_DIRECTIONS; i++)
10388 int xx = x + xy[i][0];
10389 int yy = y + xy[i][1];
10390 int center_side = trigger_sides[i][0];
10391 int border_element = border_element_old[i];
10393 if (border_element == -1)
10396 /* check for change of border element */
10397 CheckElementChangeBySide(xx, yy, border_element, center_element,
10398 CE_TOUCHING_X, center_side);
10401 for (i = 0; i < NUM_DIRECTIONS; i++)
10403 int border_side = trigger_sides[i][1];
10404 int border_element = border_element_old[i];
10406 if (border_element == -1)
10409 /* check for change of center element (but change it only once) */
10410 if (!change_center_element)
10411 change_center_element =
10412 CheckElementChangeBySide(x, y, center_element, border_element,
10413 CE_TOUCHING_X, border_side);
10419 void TestIfElementTouchesCustomElement_OLD(int x, int y)
10421 static int xy[4][2] =
10428 static int trigger_sides[4][2] =
10430 /* center side border side */
10431 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
10432 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
10433 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
10434 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
10436 static int touch_dir[4] =
10438 MV_LEFT | MV_RIGHT,
10443 boolean change_center_element = FALSE;
10444 int center_element = Feld[x][y]; /* should always be non-moving! */
10447 for (i = 0; i < NUM_DIRECTIONS; i++)
10449 int xx = x + xy[i][0];
10450 int yy = y + xy[i][1];
10451 int center_side = trigger_sides[i][0];
10452 int border_side = trigger_sides[i][1];
10453 int border_element;
10455 if (!IN_LEV_FIELD(xx, yy))
10458 if (game.engine_version < VERSION_IDENT(3,0,7,0))
10459 border_element = Feld[xx][yy]; /* may be moving! */
10460 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
10461 border_element = Feld[xx][yy];
10462 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
10463 border_element = MovingOrBlocked2Element(xx, yy);
10465 continue; /* center and border element do not touch */
10467 /* check for change of center element (but change it only once) */
10468 if (!change_center_element)
10469 change_center_element =
10470 CheckElementChangeBySide(x, y, center_element, border_element,
10471 CE_TOUCHING_X, border_side);
10473 /* check for change of border element */
10474 CheckElementChangeBySide(xx, yy, border_element, center_element,
10475 CE_TOUCHING_X, center_side);
10481 void TestIfElementHitsCustomElement(int x, int y, int direction)
10483 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
10484 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
10485 int hitx = x + dx, hity = y + dy;
10486 int hitting_element = Feld[x][y];
10487 int touched_element;
10489 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
10492 touched_element = (IN_LEV_FIELD(hitx, hity) ?
10493 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
10495 if (IN_LEV_FIELD(hitx, hity))
10497 int opposite_direction = MV_DIR_OPPOSITE(direction);
10498 int hitting_side = direction;
10499 int touched_side = opposite_direction;
10500 boolean object_hit = (!IS_MOVING(hitx, hity) ||
10501 MovDir[hitx][hity] != direction ||
10502 ABS(MovPos[hitx][hity]) <= TILEY / 2);
10508 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10509 CE_HITTING_X, touched_side);
10511 CheckElementChangeBySide(hitx, hity, touched_element,
10512 hitting_element, CE_HIT_BY_X, hitting_side);
10514 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10515 CE_HIT_BY_SOMETHING, opposite_direction);
10519 /* "hitting something" is also true when hitting the playfield border */
10520 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10521 CE_HITTING_SOMETHING, direction);
10525 void TestIfElementSmashesCustomElement(int x, int y, int direction)
10527 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
10528 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
10529 int hitx = x + dx, hity = y + dy;
10530 int hitting_element = Feld[x][y];
10531 int touched_element;
10533 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
10534 !IS_FREE(hitx, hity) &&
10535 (!IS_MOVING(hitx, hity) ||
10536 MovDir[hitx][hity] != direction ||
10537 ABS(MovPos[hitx][hity]) <= TILEY / 2));
10540 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
10544 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
10548 touched_element = (IN_LEV_FIELD(hitx, hity) ?
10549 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
10551 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10552 EP_CAN_SMASH_EVERYTHING, direction);
10554 if (IN_LEV_FIELD(hitx, hity))
10556 int opposite_direction = MV_DIR_OPPOSITE(direction);
10557 int hitting_side = direction;
10558 int touched_side = opposite_direction;
10560 int touched_element = MovingOrBlocked2Element(hitx, hity);
10563 boolean object_hit = (!IS_MOVING(hitx, hity) ||
10564 MovDir[hitx][hity] != direction ||
10565 ABS(MovPos[hitx][hity]) <= TILEY / 2);
10574 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10575 CE_SMASHED_BY_SOMETHING, opposite_direction);
10577 CheckElementChangeBySide(x, y, hitting_element, touched_element,
10578 CE_OTHER_IS_SMASHING, touched_side);
10580 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10581 CE_OTHER_GETS_SMASHED, hitting_side);
10587 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
10589 int i, kill_x = -1, kill_y = -1;
10591 int bad_element = -1;
10592 static int test_xy[4][2] =
10599 static int test_dir[4] =
10607 for (i = 0; i < NUM_DIRECTIONS; i++)
10609 int test_x, test_y, test_move_dir, test_element;
10611 test_x = good_x + test_xy[i][0];
10612 test_y = good_y + test_xy[i][1];
10614 if (!IN_LEV_FIELD(test_x, test_y))
10618 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
10620 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
10622 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
10623 2nd case: DONT_TOUCH style bad thing does not move away from good thing
10625 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
10626 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
10630 bad_element = test_element;
10636 if (kill_x != -1 || kill_y != -1)
10638 if (IS_PLAYER(good_x, good_y))
10640 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
10642 if (player->shield_deadly_time_left > 0 &&
10643 !IS_INDESTRUCTIBLE(bad_element))
10644 Bang(kill_x, kill_y);
10645 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
10646 KillPlayer(player);
10649 Bang(good_x, good_y);
10653 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
10655 int i, kill_x = -1, kill_y = -1;
10656 int bad_element = Feld[bad_x][bad_y];
10657 static int test_xy[4][2] =
10664 static int touch_dir[4] =
10666 MV_LEFT | MV_RIGHT,
10671 static int test_dir[4] =
10679 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
10682 for (i = 0; i < NUM_DIRECTIONS; i++)
10684 int test_x, test_y, test_move_dir, test_element;
10686 test_x = bad_x + test_xy[i][0];
10687 test_y = bad_y + test_xy[i][1];
10688 if (!IN_LEV_FIELD(test_x, test_y))
10692 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
10694 test_element = Feld[test_x][test_y];
10696 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
10697 2nd case: DONT_TOUCH style bad thing does not move away from good thing
10699 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
10700 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
10702 /* good thing is player or penguin that does not move away */
10703 if (IS_PLAYER(test_x, test_y))
10705 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
10707 if (bad_element == EL_ROBOT && player->is_moving)
10708 continue; /* robot does not kill player if he is moving */
10710 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10712 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
10713 continue; /* center and border element do not touch */
10720 else if (test_element == EL_PENGUIN)
10729 if (kill_x != -1 || kill_y != -1)
10731 if (IS_PLAYER(kill_x, kill_y))
10733 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
10735 if (player->shield_deadly_time_left > 0 &&
10736 !IS_INDESTRUCTIBLE(bad_element))
10737 Bang(bad_x, bad_y);
10738 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
10739 KillPlayer(player);
10742 Bang(kill_x, kill_y);
10746 void TestIfPlayerTouchesBadThing(int x, int y)
10748 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
10751 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
10753 TestIfGoodThingHitsBadThing(x, y, move_dir);
10756 void TestIfBadThingTouchesPlayer(int x, int y)
10758 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
10761 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
10763 TestIfBadThingHitsGoodThing(x, y, move_dir);
10766 void TestIfFriendTouchesBadThing(int x, int y)
10768 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
10771 void TestIfBadThingTouchesFriend(int x, int y)
10773 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
10776 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
10778 int i, kill_x = bad_x, kill_y = bad_y;
10779 static int xy[4][2] =
10787 for (i = 0; i < NUM_DIRECTIONS; i++)
10791 x = bad_x + xy[i][0];
10792 y = bad_y + xy[i][1];
10793 if (!IN_LEV_FIELD(x, y))
10796 element = Feld[x][y];
10797 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
10798 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
10806 if (kill_x != bad_x || kill_y != bad_y)
10807 Bang(bad_x, bad_y);
10810 void KillPlayer(struct PlayerInfo *player)
10812 int jx = player->jx, jy = player->jy;
10814 if (!player->active)
10817 /* remove accessible field at the player's position */
10818 Feld[jx][jy] = EL_EMPTY;
10820 /* deactivate shield (else Bang()/Explode() would not work right) */
10821 player->shield_normal_time_left = 0;
10822 player->shield_deadly_time_left = 0;
10825 BuryPlayer(player);
10828 static void KillPlayerUnlessEnemyProtected(int x, int y)
10830 if (!PLAYER_ENEMY_PROTECTED(x, y))
10831 KillPlayer(PLAYERINFO(x, y));
10834 static void KillPlayerUnlessExplosionProtected(int x, int y)
10836 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
10837 KillPlayer(PLAYERINFO(x, y));
10840 void BuryPlayer(struct PlayerInfo *player)
10842 int jx = player->jx, jy = player->jy;
10844 if (!player->active)
10847 PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
10848 PlayLevelSound(jx, jy, SND_GAME_LOSING);
10850 player->GameOver = TRUE;
10851 RemovePlayer(player);
10854 void RemovePlayer(struct PlayerInfo *player)
10856 int jx = player->jx, jy = player->jy;
10857 int i, found = FALSE;
10859 player->present = FALSE;
10860 player->active = FALSE;
10862 if (!ExplodeField[jx][jy])
10863 StorePlayer[jx][jy] = 0;
10865 if (player->is_moving)
10866 DrawLevelField(player->last_jx, player->last_jy);
10868 for (i = 0; i < MAX_PLAYERS; i++)
10869 if (stored_player[i].active)
10873 AllPlayersGone = TRUE;
10879 #if USE_NEW_SNAP_DELAY
10880 static void setFieldForSnapping(int x, int y, int element, int direction)
10882 struct ElementInfo *ei = &element_info[element];
10883 int direction_bit = MV_DIR_TO_BIT(direction);
10884 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
10885 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
10886 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
10888 Feld[x][y] = EL_ELEMENT_SNAPPING;
10889 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
10891 ResetGfxAnimation(x, y);
10893 GfxElement[x][y] = element;
10894 GfxAction[x][y] = action;
10895 GfxDir[x][y] = direction;
10896 GfxFrame[x][y] = -1;
10901 =============================================================================
10902 checkDiagonalPushing()
10903 -----------------------------------------------------------------------------
10904 check if diagonal input device direction results in pushing of object
10905 (by checking if the alternative direction is walkable, diggable, ...)
10906 =============================================================================
10909 static boolean checkDiagonalPushing(struct PlayerInfo *player,
10910 int x, int y, int real_dx, int real_dy)
10912 int jx, jy, dx, dy, xx, yy;
10914 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
10917 /* diagonal direction: check alternative direction */
10922 xx = jx + (dx == 0 ? real_dx : 0);
10923 yy = jy + (dy == 0 ? real_dy : 0);
10925 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
10929 =============================================================================
10931 -----------------------------------------------------------------------------
10932 x, y: field next to player (non-diagonal) to try to dig to
10933 real_dx, real_dy: direction as read from input device (can be diagonal)
10934 =============================================================================
10937 int DigField(struct PlayerInfo *player,
10938 int oldx, int oldy, int x, int y,
10939 int real_dx, int real_dy, int mode)
10941 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
10942 boolean player_was_pushing = player->is_pushing;
10943 boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
10944 boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
10945 int jx = oldx, jy = oldy;
10946 int dx = x - jx, dy = y - jy;
10947 int nextx = x + dx, nexty = y + dy;
10948 int move_direction = (dx == -1 ? MV_LEFT :
10949 dx == +1 ? MV_RIGHT :
10951 dy == +1 ? MV_DOWN : MV_NONE);
10952 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
10953 int dig_side = MV_DIR_OPPOSITE(move_direction);
10954 int old_element = Feld[jx][jy];
10955 #if USE_FIXED_DONT_RUN_INTO
10956 int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
10962 if (is_player) /* function can also be called by EL_PENGUIN */
10964 if (player->MovPos == 0)
10966 player->is_digging = FALSE;
10967 player->is_collecting = FALSE;
10970 if (player->MovPos == 0) /* last pushing move finished */
10971 player->is_pushing = FALSE;
10973 if (mode == DF_NO_PUSH) /* player just stopped pushing */
10975 player->is_switching = FALSE;
10976 player->push_delay = -1;
10978 return MP_NO_ACTION;
10982 #if !USE_FIXED_DONT_RUN_INTO
10983 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
10984 return MP_NO_ACTION;
10987 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
10988 old_element = Back[jx][jy];
10990 /* in case of element dropped at player position, check background */
10991 else if (Back[jx][jy] != EL_EMPTY &&
10992 game.engine_version >= VERSION_IDENT(2,2,0,0))
10993 old_element = Back[jx][jy];
10996 #if USE_FIXED_DONT_RUN_INTO
10997 if (player_can_move && DONT_RUN_INTO(element))
10999 if (element == EL_ACID && dx == 0 && dy == 1)
11002 Feld[jx][jy] = EL_PLAYER_1;
11003 InitMovingField(jx, jy, MV_DOWN);
11004 Store[jx][jy] = EL_ACID;
11005 ContinueMoving(jx, jy);
11006 BuryPlayer(player);
11009 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
11011 return MP_DONT_RUN_INTO;
11017 #if USE_FIXED_DONT_RUN_INTO
11018 if (player_can_move && DONT_RUN_INTO(element))
11020 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
11022 return MP_DONT_RUN_INTO;
11027 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
11028 return MP_NO_ACTION; /* field has no opening in this direction */
11030 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
11031 return MP_NO_ACTION; /* field has no opening in this direction */
11034 #if USE_FIXED_DONT_RUN_INTO
11035 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
11038 Feld[jx][jy] = EL_PLAYER_1;
11039 InitMovingField(jx, jy, MV_DOWN);
11040 Store[jx][jy] = EL_ACID;
11041 ContinueMoving(jx, jy);
11042 BuryPlayer(player);
11044 return MP_DONT_RUN_INTO;
11050 #if USE_FIXED_DONT_RUN_INTO
11051 if (player_can_move && DONT_RUN_INTO(element))
11053 if (element == EL_ACID && dx == 0 && dy == 1)
11056 Feld[jx][jy] = EL_PLAYER_1;
11057 InitMovingField(jx, jy, MV_DOWN);
11058 Store[jx][jy] = EL_ACID;
11059 ContinueMoving(jx, jy);
11060 BuryPlayer(player);
11063 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
11065 return MP_DONT_RUN_INTO;
11070 #if USE_FIXED_DONT_RUN_INTO
11071 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
11072 return MP_NO_ACTION;
11075 #if !USE_FIXED_DONT_RUN_INTO
11076 element = Feld[x][y];
11079 collect_count = element_info[element].collect_count_initial;
11081 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
11082 return MP_NO_ACTION;
11084 if (game.engine_version < VERSION_IDENT(2,2,0,0))
11085 player_can_move = player_can_move_or_snap;
11087 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
11088 game.engine_version >= VERSION_IDENT(2,2,0,0))
11090 CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
11091 player->index_bit, dig_side);
11092 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
11093 player->index_bit, dig_side);
11095 if (Feld[x][y] != element) /* field changed by snapping */
11098 return MP_NO_ACTION;
11101 if (game.gravity && is_player && !player->is_auto_moving &&
11102 canFallDown(player) && move_direction != MV_DOWN &&
11103 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
11104 return MP_NO_ACTION; /* player cannot walk here due to gravity */
11106 if (player_can_move &&
11107 IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
11109 int sound_element = SND_ELEMENT(element);
11110 int sound_action = ACTION_WALKING;
11112 if (IS_RND_GATE(element))
11114 if (!player->key[RND_GATE_NR(element)])
11115 return MP_NO_ACTION;
11117 else if (IS_RND_GATE_GRAY(element))
11119 if (!player->key[RND_GATE_GRAY_NR(element)])
11120 return MP_NO_ACTION;
11122 else if (IS_RND_GATE_GRAY_ACTIVE(element))
11124 if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
11125 return MP_NO_ACTION;
11127 else if (element == EL_EXIT_OPEN ||
11128 element == EL_SP_EXIT_OPEN ||
11129 element == EL_SP_EXIT_OPENING)
11131 sound_action = ACTION_PASSING; /* player is passing exit */
11133 else if (element == EL_EMPTY)
11135 sound_action = ACTION_MOVING; /* nothing to walk on */
11138 /* play sound from background or player, whatever is available */
11139 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
11140 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
11142 PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
11144 else if (player_can_move &&
11145 IS_PASSABLE(element) && canPassField(x, y, move_direction))
11147 if (!ACCESS_FROM(element, opposite_direction))
11148 return MP_NO_ACTION; /* field not accessible from this direction */
11150 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
11151 return MP_NO_ACTION;
11153 if (IS_EM_GATE(element))
11155 if (!player->key[EM_GATE_NR(element)])
11156 return MP_NO_ACTION;
11158 else if (IS_EM_GATE_GRAY(element))
11160 if (!player->key[EM_GATE_GRAY_NR(element)])
11161 return MP_NO_ACTION;
11163 else if (IS_EM_GATE_GRAY_ACTIVE(element))
11165 if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
11166 return MP_NO_ACTION;
11168 else if (IS_SP_PORT(element))
11170 if (element == EL_SP_GRAVITY_PORT_LEFT ||
11171 element == EL_SP_GRAVITY_PORT_RIGHT ||
11172 element == EL_SP_GRAVITY_PORT_UP ||
11173 element == EL_SP_GRAVITY_PORT_DOWN)
11174 game.gravity = !game.gravity;
11175 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
11176 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
11177 element == EL_SP_GRAVITY_ON_PORT_UP ||
11178 element == EL_SP_GRAVITY_ON_PORT_DOWN)
11179 game.gravity = TRUE;
11180 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
11181 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
11182 element == EL_SP_GRAVITY_OFF_PORT_UP ||
11183 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
11184 game.gravity = FALSE;
11187 /* automatically move to the next field with double speed */
11188 player->programmed_action = move_direction;
11190 if (player->move_delay_reset_counter == 0)
11192 player->move_delay_reset_counter = 2; /* two double speed steps */
11194 DOUBLE_PLAYER_SPEED(player);
11197 PlayLevelSoundAction(x, y, ACTION_PASSING);
11199 else if (player_can_move_or_snap && IS_DIGGABLE(element))
11203 if (mode != DF_SNAP)
11205 GfxElement[x][y] = GFX_ELEMENT(element);
11206 player->is_digging = TRUE;
11209 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
11211 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
11212 player->index_bit, dig_side);
11214 if (mode == DF_SNAP)
11216 #if USE_NEW_SNAP_DELAY
11217 if (level.block_snap_field)
11218 setFieldForSnapping(x, y, element, move_direction);
11220 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11222 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11225 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
11226 player->index_bit, dig_side);
11229 else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
11233 if (is_player && mode != DF_SNAP)
11235 GfxElement[x][y] = element;
11236 player->is_collecting = TRUE;
11239 if (element == EL_SPEED_PILL)
11241 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
11243 else if (element == EL_EXTRA_TIME && level.time > 0)
11245 TimeLeft += level.extra_time;
11246 DrawGameValue_Time(TimeLeft);
11248 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
11250 player->shield_normal_time_left += level.shield_normal_time;
11251 if (element == EL_SHIELD_DEADLY)
11252 player->shield_deadly_time_left += level.shield_deadly_time;
11254 else if (element == EL_DYNAMITE ||
11255 element == EL_EM_DYNAMITE ||
11256 element == EL_SP_DISK_RED)
11258 if (player->inventory_size < MAX_INVENTORY_SIZE)
11259 player->inventory_element[player->inventory_size++] = element;
11261 DrawGameValue_Dynamite(local_player->inventory_size);
11263 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
11265 player->dynabomb_count++;
11266 player->dynabombs_left++;
11268 else if (element == EL_DYNABOMB_INCREASE_SIZE)
11270 player->dynabomb_size++;
11272 else if (element == EL_DYNABOMB_INCREASE_POWER)
11274 player->dynabomb_xl = TRUE;
11276 else if (IS_KEY(element))
11278 player->key[KEY_NR(element)] = TRUE;
11280 DrawGameValue_Keys(player->key);
11282 redraw_mask |= REDRAW_DOOR_1;
11284 else if (IS_ENVELOPE(element))
11286 player->show_envelope = element;
11288 else if (element == EL_EMC_LENSES)
11290 game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
11292 RedrawAllInvisibleElementsForLenses();
11294 else if (element == EL_EMC_MAGNIFIER)
11296 game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
11298 RedrawAllInvisibleElementsForMagnifier();
11300 else if (IS_DROPPABLE(element) ||
11301 IS_THROWABLE(element)) /* can be collected and dropped */
11305 if (collect_count == 0)
11306 player->inventory_infinite_element = element;
11308 for (i = 0; i < collect_count; i++)
11309 if (player->inventory_size < MAX_INVENTORY_SIZE)
11310 player->inventory_element[player->inventory_size++] = element;
11312 DrawGameValue_Dynamite(local_player->inventory_size);
11314 else if (collect_count > 0)
11316 local_player->gems_still_needed -= collect_count;
11317 if (local_player->gems_still_needed < 0)
11318 local_player->gems_still_needed = 0;
11320 DrawGameValue_Emeralds(local_player->gems_still_needed);
11323 RaiseScoreElement(element);
11324 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
11327 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
11328 player->index_bit, dig_side);
11330 if (mode == DF_SNAP)
11332 #if USE_NEW_SNAP_DELAY
11333 if (level.block_snap_field)
11334 setFieldForSnapping(x, y, element, move_direction);
11336 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11338 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11341 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
11342 player->index_bit, dig_side);
11345 else if (player_can_move_or_snap && IS_PUSHABLE(element))
11347 if (mode == DF_SNAP && element != EL_BD_ROCK)
11348 return MP_NO_ACTION;
11350 if (CAN_FALL(element) && dy)
11351 return MP_NO_ACTION;
11353 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
11354 !(element == EL_SPRING && level.use_spring_bug))
11355 return MP_NO_ACTION;
11357 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
11358 ((move_direction & MV_VERTICAL &&
11359 ((element_info[element].move_pattern & MV_LEFT &&
11360 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
11361 (element_info[element].move_pattern & MV_RIGHT &&
11362 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
11363 (move_direction & MV_HORIZONTAL &&
11364 ((element_info[element].move_pattern & MV_UP &&
11365 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
11366 (element_info[element].move_pattern & MV_DOWN &&
11367 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
11368 return MP_NO_ACTION;
11370 /* do not push elements already moving away faster than player */
11371 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
11372 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
11373 return MP_NO_ACTION;
11375 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
11377 if (player->push_delay_value == -1 || !player_was_pushing)
11378 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11380 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11382 if (player->push_delay_value == -1)
11383 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11385 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
11387 if (!player->is_pushing)
11388 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11391 player->is_pushing = TRUE;
11393 if (!(IN_LEV_FIELD(nextx, nexty) &&
11394 (IS_FREE(nextx, nexty) ||
11395 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
11396 IS_SB_ELEMENT(element)))))
11397 return MP_NO_ACTION;
11399 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
11400 return MP_NO_ACTION;
11402 if (player->push_delay == -1) /* new pushing; restart delay */
11403 player->push_delay = 0;
11405 if (player->push_delay < player->push_delay_value &&
11406 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
11407 element != EL_SPRING && element != EL_BALLOON)
11409 /* make sure that there is no move delay before next try to push */
11410 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11411 player->move_delay = 0;
11413 return MP_NO_ACTION;
11416 if (IS_SB_ELEMENT(element))
11418 if (element == EL_SOKOBAN_FIELD_FULL)
11420 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
11421 local_player->sokobanfields_still_needed++;
11424 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
11426 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
11427 local_player->sokobanfields_still_needed--;
11430 Feld[x][y] = EL_SOKOBAN_OBJECT;
11432 if (Back[x][y] == Back[nextx][nexty])
11433 PlayLevelSoundAction(x, y, ACTION_PUSHING);
11434 else if (Back[x][y] != 0)
11435 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
11438 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
11441 if (local_player->sokobanfields_still_needed == 0 &&
11442 game.emulation == EMU_SOKOBAN)
11444 player->LevelSolved = player->GameOver = TRUE;
11445 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
11449 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
11451 InitMovingField(x, y, move_direction);
11452 GfxAction[x][y] = ACTION_PUSHING;
11454 if (mode == DF_SNAP)
11455 ContinueMoving(x, y);
11457 MovPos[x][y] = (dx != 0 ? dx : dy);
11459 Pushed[x][y] = TRUE;
11460 Pushed[nextx][nexty] = TRUE;
11462 if (game.engine_version < VERSION_IDENT(2,2,0,7))
11463 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11465 player->push_delay_value = -1; /* get new value later */
11467 /* check for element change _after_ element has been pushed */
11468 if (game.use_change_when_pushing_bug)
11470 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
11471 player->index_bit, dig_side);
11472 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
11473 player->index_bit, dig_side);
11476 else if (IS_SWITCHABLE(element))
11478 if (PLAYER_SWITCHING(player, x, y))
11480 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
11481 player->index_bit, dig_side);
11486 player->is_switching = TRUE;
11487 player->switch_x = x;
11488 player->switch_y = y;
11490 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
11492 if (element == EL_ROBOT_WHEEL)
11494 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
11498 DrawLevelField(x, y);
11500 else if (element == EL_SP_TERMINAL)
11505 SCAN_PLAYFIELD(xx, yy)
11507 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
11510 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
11512 else if (Feld[xx][yy] == EL_SP_TERMINAL)
11513 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
11516 else if (IS_BELT_SWITCH(element))
11518 ToggleBeltSwitch(x, y);
11520 else if (element == EL_SWITCHGATE_SWITCH_UP ||
11521 element == EL_SWITCHGATE_SWITCH_DOWN)
11523 ToggleSwitchgateSwitch(x, y);
11525 else if (element == EL_LIGHT_SWITCH ||
11526 element == EL_LIGHT_SWITCH_ACTIVE)
11528 ToggleLightSwitch(x, y);
11530 else if (element == EL_TIMEGATE_SWITCH)
11532 ActivateTimegateSwitch(x, y);
11534 else if (element == EL_BALLOON_SWITCH_LEFT ||
11535 element == EL_BALLOON_SWITCH_RIGHT ||
11536 element == EL_BALLOON_SWITCH_UP ||
11537 element == EL_BALLOON_SWITCH_DOWN ||
11538 element == EL_BALLOON_SWITCH_NONE ||
11539 element == EL_BALLOON_SWITCH_ANY)
11541 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
11542 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
11543 element == EL_BALLOON_SWITCH_UP ? MV_UP :
11544 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
11545 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
11548 else if (element == EL_LAMP)
11550 Feld[x][y] = EL_LAMP_ACTIVE;
11551 local_player->lights_still_needed--;
11553 ResetGfxAnimation(x, y);
11554 DrawLevelField(x, y);
11556 else if (element == EL_TIME_ORB_FULL)
11558 Feld[x][y] = EL_TIME_ORB_EMPTY;
11560 if (level.time > 0 || level.use_time_orb_bug)
11562 TimeLeft += level.time_orb_time;
11563 DrawGameValue_Time(TimeLeft);
11566 ResetGfxAnimation(x, y);
11567 DrawLevelField(x, y);
11569 else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
11570 element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
11574 game.ball_state = !game.ball_state;
11577 SCAN_PLAYFIELD(xx, yy)
11579 for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
11582 int e = Feld[xx][yy];
11584 if (game.ball_state)
11586 if (e == EL_EMC_MAGIC_BALL)
11587 CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
11588 else if (e == EL_EMC_MAGIC_BALL_SWITCH)
11589 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
11593 if (e == EL_EMC_MAGIC_BALL_ACTIVE)
11594 CreateField(xx, yy, EL_EMC_MAGIC_BALL);
11595 else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
11596 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
11601 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
11602 player->index_bit, dig_side);
11604 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
11605 player->index_bit, dig_side);
11607 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
11608 player->index_bit, dig_side);
11614 if (!PLAYER_SWITCHING(player, x, y))
11616 player->is_switching = TRUE;
11617 player->switch_x = x;
11618 player->switch_y = y;
11620 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
11621 player->index_bit, dig_side);
11622 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
11623 player->index_bit, dig_side);
11625 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
11626 player->index_bit, dig_side);
11627 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
11628 player->index_bit, dig_side);
11631 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
11632 player->index_bit, dig_side);
11633 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
11634 player->index_bit, dig_side);
11636 return MP_NO_ACTION;
11639 player->push_delay = -1;
11641 if (is_player) /* function can also be called by EL_PENGUIN */
11643 if (Feld[x][y] != element) /* really digged/collected something */
11644 player->is_collecting = !player->is_digging;
11650 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
11652 int jx = player->jx, jy = player->jy;
11653 int x = jx + dx, y = jy + dy;
11654 int snap_direction = (dx == -1 ? MV_LEFT :
11655 dx == +1 ? MV_RIGHT :
11657 dy == +1 ? MV_DOWN : MV_NONE);
11658 boolean can_continue_snapping = (level.continuous_snapping &&
11659 WasJustFalling[x][y] < CHECK_DELAY_FALLING);
11661 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
11664 if (!player->active || !IN_LEV_FIELD(x, y))
11672 if (player->MovPos == 0)
11673 player->is_pushing = FALSE;
11675 player->is_snapping = FALSE;
11677 if (player->MovPos == 0)
11679 player->is_moving = FALSE;
11680 player->is_digging = FALSE;
11681 player->is_collecting = FALSE;
11687 #if USE_NEW_CONTINUOUS_SNAPPING
11688 /* prevent snapping with already pressed snap key when not allowed */
11689 if (player->is_snapping && !can_continue_snapping)
11692 if (player->is_snapping)
11696 player->MovDir = snap_direction;
11698 if (player->MovPos == 0)
11700 player->is_moving = FALSE;
11701 player->is_digging = FALSE;
11702 player->is_collecting = FALSE;
11705 player->is_dropping = FALSE;
11706 player->is_dropping_pressed = FALSE;
11707 player->drop_pressed_delay = 0;
11709 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
11712 player->is_snapping = TRUE;
11714 if (player->MovPos == 0)
11716 player->is_moving = FALSE;
11717 player->is_digging = FALSE;
11718 player->is_collecting = FALSE;
11721 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
11722 DrawLevelField(player->last_jx, player->last_jy);
11724 DrawLevelField(x, y);
11729 boolean DropElement(struct PlayerInfo *player)
11731 int old_element, new_element;
11732 int dropx = player->jx, dropy = player->jy;
11733 int drop_direction = player->MovDir;
11734 int drop_side = drop_direction;
11735 int drop_element = (player->inventory_size > 0 ?
11736 player->inventory_element[player->inventory_size - 1] :
11737 player->inventory_infinite_element != EL_UNDEFINED ?
11738 player->inventory_infinite_element :
11739 player->dynabombs_left > 0 ?
11740 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
11743 player->is_dropping_pressed = TRUE;
11745 /* do not drop an element on top of another element; when holding drop key
11746 pressed without moving, dropped element must move away before the next
11747 element can be dropped (this is especially important if the next element
11748 is dynamite, which can be placed on background for historical reasons) */
11749 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
11752 if (IS_THROWABLE(drop_element))
11754 dropx += GET_DX_FROM_DIR(drop_direction);
11755 dropy += GET_DY_FROM_DIR(drop_direction);
11757 if (!IN_LEV_FIELD(dropx, dropy))
11761 old_element = Feld[dropx][dropy]; /* old element at dropping position */
11762 new_element = drop_element; /* default: no change when dropping */
11764 /* check if player is active, not moving and ready to drop */
11765 if (!player->active || player->MovPos || player->drop_delay > 0)
11768 /* check if player has anything that can be dropped */
11769 if (new_element == EL_UNDEFINED)
11772 /* check if drop key was pressed long enough for EM style dynamite */
11773 if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
11776 /* check if anything can be dropped at the current position */
11777 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
11780 /* collected custom elements can only be dropped on empty fields */
11781 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
11784 if (old_element != EL_EMPTY)
11785 Back[dropx][dropy] = old_element; /* store old element on this field */
11787 ResetGfxAnimation(dropx, dropy);
11788 ResetRandomAnimationValue(dropx, dropy);
11790 if (player->inventory_size > 0 ||
11791 player->inventory_infinite_element != EL_UNDEFINED)
11793 if (player->inventory_size > 0)
11795 player->inventory_size--;
11797 DrawGameValue_Dynamite(local_player->inventory_size);
11799 if (new_element == EL_DYNAMITE)
11800 new_element = EL_DYNAMITE_ACTIVE;
11801 else if (new_element == EL_EM_DYNAMITE)
11802 new_element = EL_EM_DYNAMITE_ACTIVE;
11803 else if (new_element == EL_SP_DISK_RED)
11804 new_element = EL_SP_DISK_RED_ACTIVE;
11807 Feld[dropx][dropy] = new_element;
11809 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
11810 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
11811 el2img(Feld[dropx][dropy]), 0);
11813 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
11815 /* needed if previous element just changed to "empty" in the last frame */
11816 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
11818 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
11819 player->index_bit, drop_side);
11820 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
11822 player->index_bit, drop_side);
11824 TestIfElementTouchesCustomElement(dropx, dropy);
11826 else /* player is dropping a dyna bomb */
11828 player->dynabombs_left--;
11830 Feld[dropx][dropy] = new_element;
11832 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
11833 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
11834 el2img(Feld[dropx][dropy]), 0);
11836 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
11839 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
11840 InitField_WithBug1(dropx, dropy, FALSE);
11842 new_element = Feld[dropx][dropy]; /* element might have changed */
11844 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
11845 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
11847 int move_direction, nextx, nexty;
11849 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
11850 MovDir[dropx][dropy] = drop_direction;
11852 move_direction = MovDir[dropx][dropy];
11853 nextx = dropx + GET_DX_FROM_DIR(move_direction);
11854 nexty = dropy + GET_DY_FROM_DIR(move_direction);
11856 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
11857 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
11860 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
11861 player->is_dropping = TRUE;
11863 player->drop_pressed_delay = 0;
11864 player->is_dropping_pressed = FALSE;
11866 player->drop_x = dropx;
11867 player->drop_y = dropy;
11872 /* ------------------------------------------------------------------------- */
11873 /* game sound playing functions */
11874 /* ------------------------------------------------------------------------- */
11876 static int *loop_sound_frame = NULL;
11877 static int *loop_sound_volume = NULL;
11879 void InitPlayLevelSound()
11881 int num_sounds = getSoundListSize();
11883 checked_free(loop_sound_frame);
11884 checked_free(loop_sound_volume);
11886 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
11887 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
11890 static void PlayLevelSound(int x, int y, int nr)
11892 int sx = SCREENX(x), sy = SCREENY(y);
11893 int volume, stereo_position;
11894 int max_distance = 8;
11895 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
11897 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
11898 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
11901 if (!IN_LEV_FIELD(x, y) ||
11902 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
11903 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
11906 volume = SOUND_MAX_VOLUME;
11908 if (!IN_SCR_FIELD(sx, sy))
11910 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
11911 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
11913 volume -= volume * (dx > dy ? dx : dy) / max_distance;
11916 stereo_position = (SOUND_MAX_LEFT +
11917 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
11918 (SCR_FIELDX + 2 * max_distance));
11920 if (IS_LOOP_SOUND(nr))
11922 /* This assures that quieter loop sounds do not overwrite louder ones,
11923 while restarting sound volume comparison with each new game frame. */
11925 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
11928 loop_sound_volume[nr] = volume;
11929 loop_sound_frame[nr] = FrameCounter;
11932 PlaySoundExt(nr, volume, stereo_position, type);
11935 static void PlayLevelSoundNearest(int x, int y, int sound_action)
11937 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
11938 x > LEVELX(BX2) ? LEVELX(BX2) : x,
11939 y < LEVELY(BY1) ? LEVELY(BY1) :
11940 y > LEVELY(BY2) ? LEVELY(BY2) : y,
11944 static void PlayLevelSoundAction(int x, int y, int action)
11946 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
11949 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
11951 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
11953 if (sound_effect != SND_UNDEFINED)
11954 PlayLevelSound(x, y, sound_effect);
11957 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
11960 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
11962 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
11963 PlayLevelSound(x, y, sound_effect);
11966 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
11968 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
11970 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
11971 PlayLevelSound(x, y, sound_effect);
11974 static void StopLevelSoundActionIfLoop(int x, int y, int action)
11976 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
11978 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
11979 StopSound(sound_effect);
11982 static void PlayLevelMusic()
11984 if (levelset.music[level_nr] != MUS_UNDEFINED)
11985 PlayMusic(levelset.music[level_nr]); /* from config file */
11987 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
11990 void PlayLevelSound_EM(int x, int y, int element_em, int sample)
11992 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
11997 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
12001 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12005 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12009 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12013 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
12017 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12021 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12024 case SAMPLE_android_clone:
12025 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12028 case SAMPLE_android_move:
12029 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12032 case SAMPLE_spring:
12033 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12037 PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
12041 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
12044 case SAMPLE_eater_eat:
12045 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
12049 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12052 case SAMPLE_collect:
12053 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
12056 case SAMPLE_diamond:
12057 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12060 case SAMPLE_squash:
12061 /* !!! CHECK THIS !!! */
12063 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
12065 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
12069 case SAMPLE_wonderfall:
12070 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
12074 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12078 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12082 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
12086 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
12090 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12094 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
12097 case SAMPLE_wonder:
12098 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12102 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
12105 case SAMPLE_exit_open:
12106 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
12109 case SAMPLE_exit_leave:
12110 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
12113 case SAMPLE_dynamite:
12114 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12118 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12122 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
12126 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12130 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
12134 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
12138 PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
12142 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
12147 void RaiseScore(int value)
12149 local_player->score += value;
12151 DrawGameValue_Score(local_player->score);
12154 void RaiseScoreElement(int element)
12159 case EL_BD_DIAMOND:
12160 case EL_EMERALD_YELLOW:
12161 case EL_EMERALD_RED:
12162 case EL_EMERALD_PURPLE:
12163 case EL_SP_INFOTRON:
12164 RaiseScore(level.score[SC_EMERALD]);
12167 RaiseScore(level.score[SC_DIAMOND]);
12170 RaiseScore(level.score[SC_CRYSTAL]);
12173 RaiseScore(level.score[SC_PEARL]);
12176 case EL_BD_BUTTERFLY:
12177 case EL_SP_ELECTRON:
12178 RaiseScore(level.score[SC_BUG]);
12181 case EL_BD_FIREFLY:
12182 case EL_SP_SNIKSNAK:
12183 RaiseScore(level.score[SC_SPACESHIP]);
12186 case EL_DARK_YAMYAM:
12187 RaiseScore(level.score[SC_YAMYAM]);
12190 RaiseScore(level.score[SC_ROBOT]);
12193 RaiseScore(level.score[SC_PACMAN]);
12196 RaiseScore(level.score[SC_NUT]);
12199 case EL_EM_DYNAMITE:
12200 case EL_SP_DISK_RED:
12201 case EL_DYNABOMB_INCREASE_NUMBER:
12202 case EL_DYNABOMB_INCREASE_SIZE:
12203 case EL_DYNABOMB_INCREASE_POWER:
12204 RaiseScore(level.score[SC_DYNAMITE]);
12206 case EL_SHIELD_NORMAL:
12207 case EL_SHIELD_DEADLY:
12208 RaiseScore(level.score[SC_SHIELD]);
12210 case EL_EXTRA_TIME:
12211 RaiseScore(level.extra_time_score);
12225 RaiseScore(level.score[SC_KEY]);
12228 RaiseScore(element_info[element].collect_score);
12233 void RequestQuitGame(boolean ask_if_really_quit)
12235 if (AllPlayersGone ||
12236 !ask_if_really_quit ||
12237 level_editor_test_game ||
12238 Request("Do you really want to quit the game ?",
12239 REQ_ASK | REQ_STAY_CLOSED))
12241 #if defined(NETWORK_AVALIABLE)
12242 if (options.network)
12243 SendToServer_StopPlaying();
12247 game_status = GAME_MODE_MAIN;
12253 if (tape.playing && tape.deactivate_display)
12254 TapeDeactivateDisplayOff(TRUE);
12256 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
12258 if (tape.playing && tape.deactivate_display)
12259 TapeDeactivateDisplayOn();
12264 /* ---------- new game button stuff ---------------------------------------- */
12266 /* graphic position values for game buttons */
12267 #define GAME_BUTTON_XSIZE 30
12268 #define GAME_BUTTON_YSIZE 30
12269 #define GAME_BUTTON_XPOS 5
12270 #define GAME_BUTTON_YPOS 215
12271 #define SOUND_BUTTON_XPOS 5
12272 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
12274 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
12275 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
12276 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
12277 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
12278 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
12279 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
12286 } gamebutton_info[NUM_GAME_BUTTONS] =
12289 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
12294 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
12295 GAME_CTRL_ID_PAUSE,
12299 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
12304 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
12305 SOUND_CTRL_ID_MUSIC,
12306 "background music on/off"
12309 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
12310 SOUND_CTRL_ID_LOOPS,
12311 "sound loops on/off"
12314 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
12315 SOUND_CTRL_ID_SIMPLE,
12316 "normal sounds on/off"
12320 void CreateGameButtons()
12324 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12326 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
12327 struct GadgetInfo *gi;
12330 unsigned long event_mask;
12331 int gd_xoffset, gd_yoffset;
12332 int gd_x1, gd_x2, gd_y1, gd_y2;
12335 gd_xoffset = gamebutton_info[i].x;
12336 gd_yoffset = gamebutton_info[i].y;
12337 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
12338 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
12340 if (id == GAME_CTRL_ID_STOP ||
12341 id == GAME_CTRL_ID_PAUSE ||
12342 id == GAME_CTRL_ID_PLAY)
12344 button_type = GD_TYPE_NORMAL_BUTTON;
12346 event_mask = GD_EVENT_RELEASED;
12347 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12348 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12352 button_type = GD_TYPE_CHECK_BUTTON;
12354 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
12355 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
12356 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
12357 event_mask = GD_EVENT_PRESSED;
12358 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
12359 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12362 gi = CreateGadget(GDI_CUSTOM_ID, id,
12363 GDI_INFO_TEXT, gamebutton_info[i].infotext,
12364 GDI_X, DX + gd_xoffset,
12365 GDI_Y, DY + gd_yoffset,
12366 GDI_WIDTH, GAME_BUTTON_XSIZE,
12367 GDI_HEIGHT, GAME_BUTTON_YSIZE,
12368 GDI_TYPE, button_type,
12369 GDI_STATE, GD_BUTTON_UNPRESSED,
12370 GDI_CHECKED, checked,
12371 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
12372 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
12373 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
12374 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
12375 GDI_EVENT_MASK, event_mask,
12376 GDI_CALLBACK_ACTION, HandleGameButtons,
12380 Error(ERR_EXIT, "cannot create gadget");
12382 game_gadget[id] = gi;
12386 void FreeGameButtons()
12390 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12391 FreeGadget(game_gadget[i]);
12394 static void MapGameButtons()
12398 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12399 MapGadget(game_gadget[i]);
12402 void UnmapGameButtons()
12406 for (i = 0; i < NUM_GAME_BUTTONS; i++)
12407 UnmapGadget(game_gadget[i]);
12410 static void HandleGameButtons(struct GadgetInfo *gi)
12412 int id = gi->custom_id;
12414 if (game_status != GAME_MODE_PLAYING)
12419 case GAME_CTRL_ID_STOP:
12420 RequestQuitGame(TRUE);
12423 case GAME_CTRL_ID_PAUSE:
12424 if (options.network)
12426 #if defined(NETWORK_AVALIABLE)
12428 SendToServer_ContinuePlaying();
12430 SendToServer_PausePlaying();
12434 TapeTogglePause(TAPE_TOGGLE_MANUAL);
12437 case GAME_CTRL_ID_PLAY:
12440 #if defined(NETWORK_AVALIABLE)
12441 if (options.network)
12442 SendToServer_ContinuePlaying();
12446 tape.pausing = FALSE;
12447 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
12452 case SOUND_CTRL_ID_MUSIC:
12453 if (setup.sound_music)
12455 setup.sound_music = FALSE;
12458 else if (audio.music_available)
12460 setup.sound = setup.sound_music = TRUE;
12462 SetAudioMode(setup.sound);
12468 case SOUND_CTRL_ID_LOOPS:
12469 if (setup.sound_loops)
12470 setup.sound_loops = FALSE;
12471 else if (audio.loops_available)
12473 setup.sound = setup.sound_loops = TRUE;
12474 SetAudioMode(setup.sound);
12478 case SOUND_CTRL_ID_SIMPLE:
12479 if (setup.sound_simple)
12480 setup.sound_simple = FALSE;
12481 else if (audio.sound_available)
12483 setup.sound = setup.sound_simple = TRUE;
12484 SetAudioMode(setup.sound);